0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-03 23:00:14 -05:00

Added CasperJS Functional Test to Build

Fixes #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.
This commit is contained in:
William Dibbern 2013-08-26 16:28:41 -05:00
parent 1efd16dbcc
commit badd6d10d4
8 changed files with 210 additions and 66 deletions

View file

@ -4,5 +4,16 @@ node_js:
- "0.10"
git:
submodules: false
before_install:
- gem update --system
- gem install sass bourbon
- npm install -g grunt-cli
- git clone git://github.com/n1k0/casperjs.git ~/casperjs
- cd ~/casperjs
- git checkout tags/1.1-beta1
- export PATH=$PATH:`pwd`/bin
- cd -
before_script:
- npm install -g grunt-cli
- phantomjs --version
- casperjs --version
- grunt init

View file

@ -6,6 +6,8 @@ var path = require('path'),
spawn = require("child_process").spawn,
buildDirectory = path.resolve(process.cwd(), '.build'),
distDirectory = path.resolve(process.cwd(), '.dist'),
config = require('./config'),
_ = require('underscore'),
configureGrunt = function (grunt) {
// load all grunt tasks
@ -343,6 +345,34 @@ var path = require('path'),
cfg.buildType = type;
});
grunt.registerTask('spawn-casperjs', function () {
var done = this.async(),
options = ['host', 'noPort', 'port', 'email', 'password'],
args = ['test', 'admin/', '--includes=base.js', '--direct', '--log-level=debug'];
// Forward parameters from grunt to casperjs
_.each(options, function processOption(option) {
if (grunt.option(option)) {
args.push('--' + option + '=' + grunt.option(option));
}
});
grunt.util.spawn({
cmd: 'casperjs',
args: args,
opts: {
cwd: path.resolve('core/test/functional'),
stdio: 'inherit'
}
}, function (error, result, code) {
if (error) {
grunt.fail.fatal(result.stdout);
}
grunt.log.writeln(result.stdout);
done();
});
});
// Prepare the project for development
// TODO: Git submodule init/update (https://github.com/jaubourg/grunt-update-submodules)?
grunt.registerTask("init", ["shell:bourbon", "sass:admin", 'handlebars']);
@ -356,8 +386,11 @@ var path = require('path'),
// Run migrations tests only
grunt.registerTask("test-m", ["mochacli:migrate"]);
// Run casperjs tests only
grunt.registerTask('test-functional', ['express', 'spawn-casperjs']);
// Run tests and lint code
grunt.registerTask("validate", ["jslint", "mochacli:all"]);
grunt.registerTask("validate", ["jslint", "mochacli:all", "test-functional"]);
// Generate Docs
grunt.registerTask("docs", ["groc"]);

View file

@ -1,4 +1,59 @@
/*globals casper, __utils__, url, user, falseUser */
casper.test.begin('Ensure Session is Killed', 1, function suite(test) {
casper.test.filename = 'login_logout_test.png';
casper.start(url + 'logout/', function (response) {
test.assert(/\/ghost\//.test(response.url), response.url);
});
casper.run(function () {
test.done();
});
});
casper.test.begin('Ensure a User is Registered', 2, function suite(test) {
casper.test.filename = 'login_user_registered_test.png';
casper.start(url + 'ghost/signup/');
casper.waitFor(function checkOpaque() {
return this.evaluate(function () {
var loginBox = document.querySelector('.login-box');
return window.getComputedStyle(loginBox).getPropertyValue('display') === "block"
&& window.getComputedStyle(loginBox).getPropertyValue('opacity') === "1";
});
}, function then() {
checkUrl = true;
this.fill("#register", user, true);
});
casper.waitForSelectorTextChange('.notification-error', function (text) {
test.assertSelectorHasText('.notification-error', 'already registered');
// If the previous assert succeeds, then we should skip the next check and just pass.
test.pass('Already registered!');
}, function () {
test.assertUrlMatch(/\/ghost\/$/, 'If we\'re not already registered, we should be logged in.');
test.pass('Successfully registered.')
}, 2000);
casper.run(function () {
test.done();
});
});
casper.test.begin('Ensure Session is Killed after Registration', 1, function suite(test) {
casper.test.filename = 'login_logout2_test.png';
casper.start(url + 'logout/', function (response) {
test.assert(/\/ghost\//.test(response.url), response.url);
});
casper.run(function () {
test.done();
});
});
casper.test.begin("Ghost admin will load login page", 2, function suite(test) {
casper.test.filename = "admin_test.png";
@ -26,9 +81,9 @@ casper.test.begin('Redirects to signin', 2, function suite(test) {
});
});
casper.test.begin("Can login to Ghost", 3, function suite(test) {
casper.test.begin("Can't spam it", 2, function suite(test) {
casper.test.filename = "login_test.png";
casper.test.filename = "login_spam_test.png";
casper.start(url + "ghost/signin/", function testTitle() {
test.assertTitle("", "Ghost admin has no title");
@ -42,6 +97,39 @@ casper.test.begin("Can login to Ghost", 3, function suite(test) {
&& window.getComputedStyle(loginBox).getPropertyValue('opacity') === "1";
});
}, function then() {
this.fill("#login", falseUser, true);
casper.wait(200, function doneWait() {
this.fill("#login", falseUser, true);
});
});
casper.wait(1000, function doneWait() {
this.echo("I've waited for 1 seconds.");
});
casper.then(function testForErrorMessage() {
test.assertSelectorHasText('.notification-error', 'Slow down, there are way too many login attempts!');
});
casper.run(function () {
test.done();
});
});
casper.test.begin("Can login to Ghost", 3, function suite(test) {
casper.test.filename = "login_test.png";
casper.start(url + "ghost/login/", function testTitle() {
test.assertTitle("", "Ghost admin has no title");
}).viewport(1280, 1024);
casper.waitFor(function checkOpaque() {
return this.evaluate(function () {
var loginBox = document.querySelector('.login-box');
return window.getComputedStyle(loginBox).getPropertyValue('display') === "block"
&& window.getComputedStyle(loginBox).getPropertyValue('opacity') === "1";
});
}, function then() {
this.fill("#login", user, true);
});
@ -58,38 +146,3 @@ casper.test.begin("Can login to Ghost", 3, function suite(test) {
test.done();
});
});
casper.test.begin("Can't spam it", 2, function suite(test) {
casper.test.filename = "login_test.png";
casper.start(url + "ghost/login/", function testTitle() {
test.assertTitle("", "Ghost admin has no title");
}).viewport(1280, 1024);
casper.waitFor(function checkOpaque() {
return this.evaluate(function () {
var loginBox = document.querySelector('.login-box');
return window.getComputedStyle(loginBox).getPropertyValue('display') === "block"
&& window.getComputedStyle(loginBox).getPropertyValue('opacity') === "1";
});
}, function then() {
this.fill("#login", falseUser, true);
casper.wait(200, function doneWait() {
this.fill("#login", falseUser, true);
});
});
casper.wait(200, function doneWait() {
this.echo("I've waited for 1 seconds.");
});
casper.then(function testForErrorMessage() {
test.assertSelectorHasText('.notification-error', 'Slow down, there are way too many login attempts!');
});
casper.run(function () {
test.done();
});
});

View file

@ -64,7 +64,7 @@ casper.test.begin("Haunted markdown in editor works", 3, function suite(test) {
}).viewport(1280, 1024);
casper.then(function testImage() {
casper.writeContentToCodeMirror("![some text]()");
casper.writeContentToCodeMirror("![sometext]()");
});
// We must wait after sending keys to CodeMirror
@ -77,9 +77,9 @@ casper.test.begin("Haunted markdown in editor works", 3, function suite(test) {
test.assertEvalEquals(function () {
return document.querySelector('.CodeMirror-wrap textarea').value;
}, "![some text]()", 'Editor value is correct');
}, "![sometext]()", 'Editor value is correct');
test.assertSelectorHasText('.entry-preview .rendered-markdown', 'Add image of some text', 'Editor value is correct');
test.assertSelectorHasText('.entry-preview .rendered-markdown', 'Add image of sometext', 'Editor value is correct');
});
casper.run(function () {

View file

@ -82,7 +82,12 @@ casper.test.begin("Settings screen is correct", 19, function suite(test) {
casper.then(function checkUserWasSaved() {
casper.removeListener('resource.requested', handleUserRequest);
test.assertExists('.notification-success', 'got success notification');
});
casper.waitForSelector('.notification-success', function onSuccess() {
test.assert(true, 'Got success notification');
}, function onTimeout() {
test.assert(false, 'No success notification :(');
});
casper.thenClick('#main-menu .settings a').then(function testOpeningSettingsTwice() {
@ -105,7 +110,12 @@ casper.test.begin("Settings screen is correct", 19, function suite(test) {
casper.then(function checkSettingsWereSaved() {
casper.removeListener('resource.requested', handleSettingsRequest);
test.assertExists('.notification-success', 'got success notification');
});
casper.waitForSelector('.notification-success', function onSuccess() {
test.assert(true, 'Got success notification');
}, function onTimeout() {
test.assert(false, 'No success notification :(');
});
casper.run(function () {
@ -130,24 +140,36 @@ casper.test.begin("User settings screen validates email", 6, function suite(test
brokenEmail = email.replace('.', '-');
casper.fillSelectors('.user-details-container', {
'#user-email': brokenEmail
'#user-email': brokenEmail
}, false);
});
casper.thenClick('#user .button-save').waitForResource('/users/', function () {
test.assertExists('.notification-error', 'got error notification');
casper.thenClick('#user .button-save');
casper.waitForResource('/users/');
casper.waitForSelector('.notification-error', function onSuccess() {
test.assert(true, 'Got error notification');
test.assertSelectorDoesntHaveText('.notification-error', '[object Object]');
}, function onTimeout() {
test.assert(false, 'No error notification :(');
});
casper.then(function resetEmailToValid() {
casper.fillSelectors('.user-details-container', {
'#user-email': email
'#user-email': email
}, false);
});
casper.thenClick('#user .button-save').waitForResource('/users/', function () {
test.assertExists('.notification-success', 'got success notification');
casper.thenClick('#user .button-save');
casper.waitForResource('/users/');
casper.waitForSelector('.notification-success', function onSuccess() {
test.assert(true, 'Got success notification');
test.assertSelectorDoesntHaveText('.notification-success', '[object Object]');
}, function onTimeout() {
test.assert(false, 'No success notification :(');
});
casper.run(function () {

View file

@ -22,8 +22,14 @@ casper.test.begin("Ghost edit draft flow works correctly", 7, function suite(tes
this.echo("I've waited for 1 seconds.");
});
casper.thenClick('.button-save').waitForResource(/posts/, function then() {
test.assertExists('.notification-success', 'got success notification');
casper.thenClick('.button-save');
casper.waitForResource(/posts/);
casper.waitForSelector('.notification-success', function onSuccess() {
test.assert(true, 'Got success notification');
}, function onTimeout() {
test.assert(false, 'No success notification :(');
});
casper.thenOpen(url + 'ghost/content/', function then() {
@ -42,8 +48,14 @@ casper.test.begin("Ghost edit draft flow works correctly", 7, function suite(tes
test.assertUrlMatch(/editor/, "Ghost doesn't require login this time");
});
casper.thenClick('.button-save').waitForResource(/posts/, function then() {
test.assertExists('.notification-success', 'got success notification');
casper.thenClick('.button-save');
casper.waitForResource(/posts/);
casper.waitForSelector('.notification-success', function onSuccess() {
test.assert(true, 'Got success notification');
}, function onTimeout() {
test.assert(false, 'No success notification :(');
});
casper.run(function () {

View file

@ -19,8 +19,14 @@ casper.test.begin("Ghost logout works correctly", 2, function suite(test) {
});
});
casper.thenClick('.usermenu-signout a').waitForResource(/login/, function then() {
test.assertExists('.notification-success', 'got success notification');
casper.thenClick('.usermenu-signout a');
casper.waitForResource(/signin/);
casper.waitForSelector('.notification-success', function onSuccess() {
test.assert(true, 'Got success notification');
}, function onTimeout() {
test.assert(false, 'No success notification :(');
});
casper.run(function () {
@ -50,13 +56,12 @@ casper.test.begin("Can't spam signin", 3, function suite(test) {
});
});
casper.wait(200, function doneWait() {
this.echo("I've waited for 1 seconds.");
});
casper.then(function testForErrorMessage() {
test.assertExists('.notification-error', 'got error notification');
casper.waitForSelector('.notification-error', function onSuccess() {
test.assert(true, 'Got error notification');
test.assertSelectorDoesntHaveText('.notification-error', '[object Object]');
}, function onTimeout() {
test.assert(false, 'No error notification :(');
});
casper.run(function () {
@ -77,9 +82,13 @@ casper.test.begin("Ghost signup fails properly", 5, function suite(test) {
});
// should now throw a short password error
casper.waitForResource(/signup/, function () {
test.assertExists('.notification-error', 'got error notification');
casper.waitForResource(/signup/);
casper.waitForSelector('.notification-error', function onSuccess() {
test.assert(true, 'Got error notification');
test.assertSelectorDoesntHaveText('.notification-error', '[object Object]');
}, function onTimeout() {
test.assert(false, 'No error notification :(');
});
casper.then(function signupWithLongPassword() {
@ -87,9 +96,13 @@ casper.test.begin("Ghost signup fails properly", 5, function suite(test) {
});
// should now throw a 1 user only error
casper.waitForResource(/signup/, function () {
test.assertExists('.notification-error', 'got error notification');
casper.waitForResource(/signup/);
casper.waitForSelector('.notification-error', function onSuccess() {
test.assert(true, 'Got error notification');
test.assertSelectorDoesntHaveText('.notification-error', '[object Object]');
}, function onTimeout() {
test.assert(false, 'No error notification :(');
});
casper.run(function () {

View file

@ -25,7 +25,7 @@ var host = casper.cli.options.host || 'localhost',
noPort = casper.cli.options.noPort || false,
port = casper.cli.options.port || '2368',
email = casper.cli.options.email || 'ghost@tryghost.org',
password = casper.cli.options.password || 'Sl1m3r',
password = casper.cli.options.password || 'Sl1m3rson',
url = "http://" + host + (noPort ? '/' : ":" + port + "/"),
user = {
email: email,