Skip to content

Commit badd6d1

Browse files
committed
Added CasperJS Functional Test to Build
Fixes TryGhost#363 - Added new grunt task to run casperjs tests. - Added prerequisites (sass/bourbon/casperjs) to travis config. - Updated failing functional tests to use more robust `waitFor` statements. - Updated capserjs `base.js` file to use a password which conforms to our 8 character minimum. - Added necessary logout to first test and also registration step to ensure a user is present in the system.
1 parent 1efd16d commit badd6d1

File tree

8 files changed

+190
-46
lines changed

8 files changed

+190
-46
lines changed

Diff for: .travis.yml

+12-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,16 @@ node_js:
44
- "0.10"
55
git:
66
submodules: false
7+
before_install:
8+
- gem update --system
9+
- gem install sass bourbon
10+
- npm install -g grunt-cli
11+
- git clone git://github.com/n1k0/casperjs.git ~/casperjs
12+
- cd ~/casperjs
13+
- git checkout tags/1.1-beta1
14+
- export PATH=$PATH:`pwd`/bin
15+
- cd -
716
before_script:
8-
- npm install -g grunt-cli
17+
- phantomjs --version
18+
- casperjs --version
19+
- grunt init

Diff for: Gruntfile.js

+34-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ var path = require('path'),
66
spawn = require("child_process").spawn,
77
buildDirectory = path.resolve(process.cwd(), '.build'),
88
distDirectory = path.resolve(process.cwd(), '.dist'),
9+
config = require('./config'),
10+
_ = require('underscore'),
911
configureGrunt = function (grunt) {
1012

1113
// load all grunt tasks
@@ -343,6 +345,34 @@ var path = require('path'),
343345
cfg.buildType = type;
344346
});
345347

348+
grunt.registerTask('spawn-casperjs', function () {
349+
var done = this.async(),
350+
options = ['host', 'noPort', 'port', 'email', 'password'],
351+
args = ['test', 'admin/', '--includes=base.js', '--direct', '--log-level=debug'];
352+
353+
// Forward parameters from grunt to casperjs
354+
_.each(options, function processOption(option) {
355+
if (grunt.option(option)) {
356+
args.push('--' + option + '=' + grunt.option(option));
357+
}
358+
});
359+
360+
grunt.util.spawn({
361+
cmd: 'casperjs',
362+
args: args,
363+
opts: {
364+
cwd: path.resolve('core/test/functional'),
365+
stdio: 'inherit'
366+
}
367+
}, function (error, result, code) {
368+
if (error) {
369+
grunt.fail.fatal(result.stdout);
370+
}
371+
grunt.log.writeln(result.stdout);
372+
done();
373+
});
374+
});
375+
346376
// Prepare the project for development
347377
// TODO: Git submodule init/update (https://github.com/jaubourg/grunt-update-submodules)?
348378
grunt.registerTask("init", ["shell:bourbon", "sass:admin", 'handlebars']);
@@ -356,8 +386,11 @@ var path = require('path'),
356386
// Run migrations tests only
357387
grunt.registerTask("test-m", ["mochacli:migrate"]);
358388

389+
// Run casperjs tests only
390+
grunt.registerTask('test-functional', ['express', 'spawn-casperjs']);
391+
359392
// Run tests and lint code
360-
grunt.registerTask("validate", ["jslint", "mochacli:all"]);
393+
grunt.registerTask("validate", ["jslint", "mochacli:all", "test-functional"]);
361394

362395
// Generate Docs
363396
grunt.registerTask("docs", ["groc"]);

Diff for: core/test/functional/admin/01_login_test.js

+70-17
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,59 @@
11
/*globals casper, __utils__, url, user, falseUser */
2+
3+
casper.test.begin('Ensure Session is Killed', 1, function suite(test) {
4+
casper.test.filename = 'login_logout_test.png';
5+
6+
casper.start(url + 'logout/', function (response) {
7+
test.assert(/\/ghost\//.test(response.url), response.url);
8+
});
9+
10+
casper.run(function () {
11+
test.done();
12+
});
13+
});
14+
15+
casper.test.begin('Ensure a User is Registered', 2, function suite(test) {
16+
casper.test.filename = 'login_user_registered_test.png';
17+
18+
casper.start(url + 'ghost/signup/');
19+
20+
casper.waitFor(function checkOpaque() {
21+
return this.evaluate(function () {
22+
var loginBox = document.querySelector('.login-box');
23+
return window.getComputedStyle(loginBox).getPropertyValue('display') === "block"
24+
&& window.getComputedStyle(loginBox).getPropertyValue('opacity') === "1";
25+
});
26+
}, function then() {
27+
checkUrl = true;
28+
this.fill("#register", user, true);
29+
});
30+
31+
casper.waitForSelectorTextChange('.notification-error', function (text) {
32+
test.assertSelectorHasText('.notification-error', 'already registered');
33+
// If the previous assert succeeds, then we should skip the next check and just pass.
34+
test.pass('Already registered!');
35+
}, function () {
36+
test.assertUrlMatch(/\/ghost\/$/, 'If we\'re not already registered, we should be logged in.');
37+
test.pass('Successfully registered.')
38+
}, 2000);
39+
40+
casper.run(function () {
41+
test.done();
42+
});
43+
});
44+
45+
casper.test.begin('Ensure Session is Killed after Registration', 1, function suite(test) {
46+
casper.test.filename = 'login_logout2_test.png';
47+
48+
casper.start(url + 'logout/', function (response) {
49+
test.assert(/\/ghost\//.test(response.url), response.url);
50+
});
51+
52+
casper.run(function () {
53+
test.done();
54+
});
55+
});
56+
257
casper.test.begin("Ghost admin will load login page", 2, function suite(test) {
358

459
casper.test.filename = "admin_test.png";
@@ -26,9 +81,9 @@ casper.test.begin('Redirects to signin', 2, function suite(test) {
2681
});
2782
});
2883

29-
casper.test.begin("Can login to Ghost", 3, function suite(test) {
84+
casper.test.begin("Can't spam it", 2, function suite(test) {
3085

31-
casper.test.filename = "login_test.png";
86+
casper.test.filename = "login_spam_test.png";
3287

3388
casper.start(url + "ghost/signin/", function testTitle() {
3489
test.assertTitle("", "Ghost admin has no title");
@@ -42,24 +97,25 @@ casper.test.begin("Can login to Ghost", 3, function suite(test) {
4297
&& window.getComputedStyle(loginBox).getPropertyValue('opacity') === "1";
4398
});
4499
}, function then() {
45-
this.fill("#login", user, true);
100+
this.fill("#login", falseUser, true);
101+
casper.wait(200, function doneWait() {
102+
this.fill("#login", falseUser, true);
103+
});
46104
});
47-
48105
casper.wait(1000, function doneWait() {
49106
this.echo("I've waited for 1 seconds.");
50107
});
51108

52-
casper.then(function testForDashboard() {
53-
this.test.assertExists("#global-header", "Global admin header is present");
54-
this.test.assertExists(".dashboard", "We're now on the dashboard");
109+
casper.then(function testForErrorMessage() {
110+
test.assertSelectorHasText('.notification-error', 'Slow down, there are way too many login attempts!');
55111
});
56112

57113
casper.run(function () {
58114
test.done();
59115
});
60116
});
61117

62-
casper.test.begin("Can't spam it", 2, function suite(test) {
118+
casper.test.begin("Can login to Ghost", 3, function suite(test) {
63119

64120
casper.test.filename = "login_test.png";
65121

@@ -74,22 +130,19 @@ casper.test.begin("Can't spam it", 2, function suite(test) {
74130
&& window.getComputedStyle(loginBox).getPropertyValue('opacity') === "1";
75131
});
76132
}, function then() {
77-
this.fill("#login", falseUser, true);
78-
casper.wait(200, function doneWait() {
79-
this.fill("#login", falseUser, true);
80-
});
81-
133+
this.fill("#login", user, true);
82134
});
83-
casper.wait(200, function doneWait() {
135+
136+
casper.wait(1000, function doneWait() {
84137
this.echo("I've waited for 1 seconds.");
85138
});
86139

87-
casper.then(function testForErrorMessage() {
88-
test.assertSelectorHasText('.notification-error', 'Slow down, there are way too many login attempts!');
140+
casper.then(function testForDashboard() {
141+
this.test.assertExists("#global-header", "Global admin header is present");
142+
this.test.assertExists(".dashboard", "We're now on the dashboard");
89143
});
90144

91145
casper.run(function () {
92146
test.done();
93147
});
94148
});
95-

Diff for: core/test/functional/admin/03_editor_test.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ casper.test.begin("Haunted markdown in editor works", 3, function suite(test) {
6464
}).viewport(1280, 1024);
6565

6666
casper.then(function testImage() {
67-
casper.writeContentToCodeMirror("![some text]()");
67+
casper.writeContentToCodeMirror("![sometext]()");
6868
});
6969

7070
// We must wait after sending keys to CodeMirror
@@ -77,9 +77,9 @@ casper.test.begin("Haunted markdown in editor works", 3, function suite(test) {
7777

7878
test.assertEvalEquals(function () {
7979
return document.querySelector('.CodeMirror-wrap textarea').value;
80-
}, "![some text]()", 'Editor value is correct');
80+
}, "![sometext]()", 'Editor value is correct');
8181

82-
test.assertSelectorHasText('.entry-preview .rendered-markdown', 'Add image of some text', 'Editor value is correct');
82+
test.assertSelectorHasText('.entry-preview .rendered-markdown', 'Add image of sometext', 'Editor value is correct');
8383
});
8484

8585
casper.run(function () {

Diff for: core/test/functional/admin/05_settings_test.js

+30-8
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,12 @@ casper.test.begin("Settings screen is correct", 19, function suite(test) {
8282

8383
casper.then(function checkUserWasSaved() {
8484
casper.removeListener('resource.requested', handleUserRequest);
85-
test.assertExists('.notification-success', 'got success notification');
85+
});
86+
87+
casper.waitForSelector('.notification-success', function onSuccess() {
88+
test.assert(true, 'Got success notification');
89+
}, function onTimeout() {
90+
test.assert(false, 'No success notification :(');
8691
});
8792

8893
casper.thenClick('#main-menu .settings a').then(function testOpeningSettingsTwice() {
@@ -105,7 +110,12 @@ casper.test.begin("Settings screen is correct", 19, function suite(test) {
105110

106111
casper.then(function checkSettingsWereSaved() {
107112
casper.removeListener('resource.requested', handleSettingsRequest);
108-
test.assertExists('.notification-success', 'got success notification');
113+
});
114+
115+
casper.waitForSelector('.notification-success', function onSuccess() {
116+
test.assert(true, 'Got success notification');
117+
}, function onTimeout() {
118+
test.assert(false, 'No success notification :(');
109119
});
110120

111121
casper.run(function () {
@@ -130,24 +140,36 @@ casper.test.begin("User settings screen validates email", 6, function suite(test
130140
brokenEmail = email.replace('.', '-');
131141

132142
casper.fillSelectors('.user-details-container', {
133-
'#user-email': brokenEmail
143+
'#user-email': brokenEmail
134144
}, false);
135145
});
136146

137-
casper.thenClick('#user .button-save').waitForResource('/users/', function () {
138-
test.assertExists('.notification-error', 'got error notification');
147+
casper.thenClick('#user .button-save');
148+
149+
casper.waitForResource('/users/');
150+
151+
casper.waitForSelector('.notification-error', function onSuccess() {
152+
test.assert(true, 'Got error notification');
139153
test.assertSelectorDoesntHaveText('.notification-error', '[object Object]');
154+
}, function onTimeout() {
155+
test.assert(false, 'No error notification :(');
140156
});
141157

142158
casper.then(function resetEmailToValid() {
143159
casper.fillSelectors('.user-details-container', {
144-
'#user-email': email
160+
'#user-email': email
145161
}, false);
146162
});
147163

148-
casper.thenClick('#user .button-save').waitForResource('/users/', function () {
149-
test.assertExists('.notification-success', 'got success notification');
164+
casper.thenClick('#user .button-save');
165+
166+
casper.waitForResource('/users/');
167+
168+
casper.waitForSelector('.notification-success', function onSuccess() {
169+
test.assert(true, 'Got success notification');
150170
test.assertSelectorDoesntHaveText('.notification-success', '[object Object]');
171+
}, function onTimeout() {
172+
test.assert(false, 'No success notification :(');
151173
});
152174

153175
casper.run(function () {

Diff for: core/test/functional/admin/06_flow_test.js

+16-4
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,14 @@ casper.test.begin("Ghost edit draft flow works correctly", 7, function suite(tes
2222
this.echo("I've waited for 1 seconds.");
2323
});
2424

25-
casper.thenClick('.button-save').waitForResource(/posts/, function then() {
26-
test.assertExists('.notification-success', 'got success notification');
25+
casper.thenClick('.button-save');
26+
27+
casper.waitForResource(/posts/);
28+
29+
casper.waitForSelector('.notification-success', function onSuccess() {
30+
test.assert(true, 'Got success notification');
31+
}, function onTimeout() {
32+
test.assert(false, 'No success notification :(');
2733
});
2834

2935
casper.thenOpen(url + 'ghost/content/', function then() {
@@ -42,8 +48,14 @@ casper.test.begin("Ghost edit draft flow works correctly", 7, function suite(tes
4248
test.assertUrlMatch(/editor/, "Ghost doesn't require login this time");
4349
});
4450

45-
casper.thenClick('.button-save').waitForResource(/posts/, function then() {
46-
test.assertExists('.notification-success', 'got success notification');
51+
casper.thenClick('.button-save');
52+
53+
casper.waitForResource(/posts/);
54+
55+
casper.waitForSelector('.notification-success', function onSuccess() {
56+
test.assert(true, 'Got success notification');
57+
}, function onTimeout() {
58+
test.assert(false, 'No success notification :(');
4759
});
4860

4961
casper.run(function () {

0 commit comments

Comments
 (0)