Dev tooling clean up

- remove old casper.js tests
- cleanup mochacli groups that are less useful than `grunt test:path`
- improve inline Gruntfile.js docs
- fixup coverage for new internal apps
- add new coverage-all task which works for all our tests
- reverse order of master-warn messages to make this clearer
- remove bluebird from greenkeeper ignores
This commit is contained in:
Hannah Wolfe 2016-03-29 20:14:11 +01:00
parent b0ab3f0273
commit 6b74e1a34a
15 changed files with 42 additions and 3041 deletions

View File

@ -188,61 +188,14 @@ var _ = require('lodash'),
]
},
// ##### Groups of unit tests
server: {
src: ['core/test/unit/**/server*_spec.js']
},
helpers: {
src: ['core/test/unit/server_helpers/*_spec.js']
},
metadata: {
src: ['core/test/unit/metadata/*_spec.js']
},
middleware: {
src: ['core/test/unit/middleware/*_spec.js']
},
showdown: {
src: ['core/test/unit/**/showdown*_spec.js']
},
perm: {
src: ['core/test/unit/**/permissions_spec.js']
},
migrate: {
src: [
'core/test/unit/**/export_spec.js',
'core/test/unit/**/import_spec.js'
]
},
storage: {
src: ['core/test/unit/**/storage*_spec.js']
},
// #### All Integration tests
integration: {
src: [
'core/test/integration/**/*_spec.js',
'core/test/integration/**/*_spec.js',
'core/test/integration/*_spec.js'
]
},
// ##### Model integration tests
model: {
src: ['core/test/integration/**/model*_spec.js']
},
// ##### API integration tests
api: {
src: ['core/test/integration/**/api*_spec.js']
},
// #### All Route tests
routes: {
src: [
@ -263,23 +216,30 @@ var _ = require('lodash'),
// `grunt coverage`.
mocha_istanbul: {
coverage: {
// TODO fix the timing/async & cleanup issues with the route and integration tests so that
// they can also have coverage generated for them & the order doesn't matter
src: ['core/test/unit'],
src: [
'core/test/unit',
'core/server/apps'
],
options: {
mask: '**/*_spec.js',
coverageFolder: 'core/test/coverage/unit',
mochaOptions: ['--timeout=15000'],
excludes: ['core/client/**']
excludes: ['core/client/**', 'core/server/built']
}
},
coverage_integration: {
src: ['core/test/integration/api'],
coverage_all: {
src: [
'core/test/unit',
'core/server/apps',
'core/test/integration',
'core/test/functional'
],
options: {
coverageFolder: 'core/test/coverage/integration',
coverageFolder: 'core/test/coverage/all',
mask: '**/*_spec.js',
mochaOptions: ['--timeout=15000'],
excludes: ['core/client/**', 'core/server/built', 'core/server/apps', 'core/server/config', 'core/server/data']
excludes: ['core/client/**', 'core/server/built']
}
}
@ -440,55 +400,6 @@ var _ = require('lodash'),
// Load the configuration
grunt.initConfig(cfg);
// ## Utilities
//
// ### Spawn Casper.js
// Custom test runner for our Casper.js functional tests
// This really ought to be refactored into a separate grunt task module
grunt.registerTask('spawnCasperJS', function (target) {
target = _.contains(['client', 'setup'], target) ? target + '/' : undefined;
var done = this.async(),
options = ['host', 'noPort', 'port', 'email', 'password'],
args = ['test']
.concat(grunt.option('target') || target || ['client/'])
.concat(['--includes=base.js', '--log-level=debug', '--port=2369']);
// Forward parameters from grunt to casperjs
_.each(options, function processOption(option) {
if (grunt.option(option)) {
args.push('--' + option + '=' + grunt.option(option));
}
});
if (grunt.option('fail-fast')) {
args.push('--fail-fast');
}
// Show concise logs in Travis as ours are getting too long
if (grunt.option('concise') || process.env.TRAVIS) {
args.push('--concise');
} else {
args.push('--verbose');
}
grunt.util.spawn({
cmd: 'casperjs',
args: args,
opts: {
cwd: path.resolve('core/test/functional'),
stdio: 'inherit'
}
}, function (error, result, code) {
/*jshint unused:false*/
if (error) {
grunt.fail.fatal(result.stderr);
}
grunt.log.writeln(result.stdout);
done();
});
});
// # Custom Tasks
// Ghost has a number of useful tasks that we use every day in development. Tasks marked as *Utility* are used
@ -507,8 +418,7 @@ var _ = require('lodash'),
grunt.registerTask('help',
'Outputs help information if you type `grunt help` instead of `grunt --help`',
function () {
console.log('Type `grunt --help` to get the details of available grunt tasks, ' +
'or alternatively visit https://github.com/TryGhost/Ghost/wiki/Grunt-Toolkit');
console.log('Type `grunt --help` to get the details of available grunt tasks.');
});
// ### Documentation
@ -587,7 +497,16 @@ var _ = require('lodash'),
});
});
grunt.registerTask('test', function (test) {
// ### Test
// **Testing utility**
//
// `grunt test:unit/apps_spec.js` will run just the tests inside the apps_spec.js file
//
// It works for any path relative to the core/test folder. It will also run all the tests in a single directory
//
// `grunt test:integration/api` - runs the api integration tests
// `grunt test:integration` - runs the integration tests in the root folder and excludes all api & model tests
grunt.registerTask('test', 'Run a particular spec file from the core/test directory e.g. `grunt test:unit/apps_spec.js`', function (test) {
if (!test) {
grunt.fail.fatal('No test provided. `grunt test` expects a filename. e.g.: `grunt test:unit/apps_spec.js`. Did you mean `npm test` or `grunt validate`?');
}
@ -652,15 +571,13 @@ var _ = require('lodash'),
// ### Unit Tests *(sub task)*
// `grunt test-unit` will run just the unit tests
//
// Provided you already have a `config.js` file, you can run individual sections from
// [mochacli](#grunt-mocha-cli) by running:
// If you need to run an individual unit test file, you can use the `grunt test:<file_path>` task:
//
// `NODE_ENV=testing grunt mochacli:section`
// `grunt test:unit/config_spec.js`
//
// If you need to run an individual unit test file, you can do so, providing you have mocha installed globally
// by using a command in the form:
// This also works for folders (although it isn't recursive), E.g.
//
// `NODE_ENV=testing mocha --timeout=15000 --ui=bdd --reporter=spec core/test/unit/config_spec.js`
// `grunt test:unit/server_helpers`
//
// Unit tests are run with [mocha](http://mochajs.org/) using
// [should](https://github.com/visionmedia/should.js) to describe the tests in a highly readable style.
@ -675,21 +592,19 @@ var _ = require('lodash'),
//
// Provided you already have a `config.js` file, you can run just the model integration tests by running:
//
// `NODE_ENV=testing grunt mochacli:model`
// `grunt test:integration/model`
//
// Or just the api integration tests by running:
//
// `NODE_ENV=testing grunt mochacli:api`
// `grunt test:integration/api`
//
// Integration tests are run with [mocha](http://mochajs.org/) using
// [should](https://github.com/visionmedia/should.js) to describe the tests in a highly readable style.
// Integration tests are different to the unit tests because they make requests to the database.
//
// If you need to run an individual integration test file you can do so, providing you have mocha installed
// globally, by using a command in the form (replace path to api_tags_spec.js with the test file you want to
// run):
// If you need to run an individual integration test file you can use the `grunt test:<file_path>` task:
//
// `NODE_ENV=testing mocha --timeout=15000 --ui=bdd --reporter=spec core/test/integration/api/api_tags_spec.js`
// `grunt test:integration/api/api_tags_spec.js`
//
// Their purpose is to test that both the api and models behave as expected when the database layer is involved.
// These tests are run against sqlite3, mysql and pg on travis and ensure that differences between the databases
@ -703,10 +618,9 @@ var _ = require('lodash'),
// ### Route tests *(sub task)*
// `grunt test-routes` will run just the route tests
//
// If you need to run an individual route test file, you can do so, providing you have a `config.js` file and
// mocha installed globally by using a command in the form:
// If you need to run an individual route test file, you can use the `grunt test:<file_path>` task:
//
// `NODE_ENV=testing mocha --timeout=15000 --ui=bdd --reporter=spec core/test/functional/routes/admin_spec.js`
// `grunt test:functional/routes/admin_spec.js`
//
// Route tests are run with [mocha](http://mochajs.org/) using
// [should](https://github.com/visionmedia/should.js) and [supertest](https://github.com/visionmedia/supertest)
@ -737,37 +651,6 @@ var _ = require('lodash'),
['test-setup', 'shell:ember:test']
);
// ### Functional tests *(sub task)*
// `grunt test-functional` will run just the functional tests
//
// You can use the `--target` argument to run any individual test file, or the admin or frontend tests:
//
// `grunt test-functional --target=client/editor_test.js` - run just the editor tests
//
// `grunt test-functional --target=client/` - run all of the tests in the client directory
//
// Functional tests are run with [phantom.js](http://phantomjs.org/) and defined using the testing api from
// [casper.js](http://docs.casperjs.org/en/latest/testing.html).
//
// An express server is started with the testing environment set, and then a headless phantom.js browser is
// used to make requests to that server. The Casper.js API then allows us to describe the elements and
// interactions we expect to appear on the page.
//
// The purpose of the functional tests is to ensure that Ghost is working as is expected from a user perspective
// including buttons and other important interactions in the admin UI.
grunt.registerTask('test-functional', 'Run functional interface tests (CasperJS)',
['test-setup', 'shell:ember:dev', 'cleanDatabase', 'express:test', 'spawnCasperJS', 'express:test:stop', 'test-functional-setup']
);
// ### Functional tests for the setup process
// `grunt test-functional-setup will run just the functional tests for the setup page.
//
// Setup only works with a brand new database, so it needs to run isolated from the rest of
// the functional tests.
grunt.registerTask('test-functional-setup', 'Run functional tests for setup',
['test-setup', 'cleanDatabase', 'express:test', 'spawnCasperJS:setup', 'express:test:stop']
);
// ### Coverage
// `grunt coverage` will generate a report for the Unit Tests.
//
@ -782,8 +665,8 @@ var _ = require('lodash'),
['test-setup', 'mocha_istanbul:coverage']
);
grunt.registerTask('coverage-integration', 'Generate unit and integration tests coverage report',
['test-setup', 'mocha_istanbul:coverage_integration']
grunt.registerTask('coverage-all', 'Generate unit and integration tests coverage report',
['test-setup', 'mocha_istanbul:coverage_all']
);
// #### Master Warning *(Utility Task)*
@ -794,9 +677,11 @@ var _ = require('lodash'),
grunt.registerTask('master-warn',
'Outputs a warning to runners of grunt prod, that master shouldn\'t be used for live blogs',
function () {
console.log('>', chalk.red('Always two there are, no more, no less. A master and a'),
chalk.bold.red('stable') + chalk.red('.'));
console.log('Use the', chalk.bold('stable'), 'branch for live blogs.', chalk.bold('Never'), 'master!');
console.log(chalk.red(
'Use the ' + chalk.bold('stable') + ' branch for live blogs. '
+ chalk.bold.underline('Never') + ' master!'
));
console.log('>', 'Always two there are, no more, no less. A master and a ' + chalk.bold('stable') + '.');
});
// ### Build About Page *(Utility Task)*

View File

@ -1,541 +0,0 @@
/*globals casper */
/**
* Casper Tests
*
* Functional browser tests for checking that the Ghost Admin UI is working as expected
* The setup of these tests is a little hacky for now, which is why they are not wired in to grunt
* Requires that you are running Ghost locally and have already registered a single user
*
* Usage (from test/functional):
*
* casperjs test admin/ --includes=base.js [--host=localhost --port=2368 --noPort=false --email=ghost@tryghost.org --password=Sl1m3r]
*
* --host - your local host address e.g. localhost or local.tryghost.org
* --port - port number of your local Ghost
* --email - the email address your admin user is registered with
* --password - the password your admin user is registered with
* --noPort - don't include a port number
*
* Requirements:
* you must have phantomjs 1.9.1 and casperjs 1.1.0-DEV installed in order for these tests to work
*/
/*jshint unused:false */
var DEBUG = false, // TOGGLE THIS TO GET MORE SCREENSHOTS
host = casper.cli.options.host || 'localhost',
noPort = casper.cli.options.noPort || false,
port = casper.cli.options.port || '2368',
email = casper.cli.options.email || 'jbloggs@example.com',
password = casper.cli.options.password || 'Sl1m3rson',
url = 'http://' + host + (noPort ? '/' : ':' + port + '/'),
newUser = {
name: 'Test User',
slug: 'test',
email: email,
password: password
},
newSetup = {
'blog-title': 'Test Blog',
name: 'Test User',
email: email,
password: password
},
user = {
identification: email,
password: password
},
falseUser = {
identification: email,
password: 'letmethrough'
},
testPost = {
title: 'Bacon ipsum dolor sit amet',
html: 'I am a test post.\n#I have some small content'
},
screens,
CasperTest,
utils = require('utils'),
// ## Debugging
jsErrors = [],
pageErrors = [],
resourceErrors = [];
screens = {
root: {
url: 'ghost/',
linkSelector: '.gh-nav-main-content',
selector: '.gh-nav-main-content.active'
},
content: {
url: 'ghost/',
linkSelector: '.gh-nav-main-content',
selector: '.gh-nav-main-content.active'
},
editor: {
url: 'ghost/editor/',
linkSelector: '.gh-nav-main-editor',
selector: '.gh-nav-main-editor.active'
},
about: {
url: 'ghost/about',
linkSelector: '.gh-nav-menu-about',
selector: '.gh-about-header'
},
'editor.editing': {
url: 'ghost/editor/',
linkSelector: 'a.post-edit',
selector: '.entry-markdown-content .markdown-editor'
},
'settings.general': {
url: 'ghost/settings/general',
selector: '.gh-nav-settings-general.active'
},
'settings.tags': {
url: 'ghost/settings/tags',
selector: '.gh-nav-settings-tags.active'
},
team: {
url: 'ghost/team',
linkSelector: '.gh-nav-main-users',
selector: '.gh-nav-main-users.active'
},
'team.user': {
url: 'ghost/team/test',
linkSelector: '.user-menu-profile',
selector: '.user-profile'
},
signin: {
url: 'ghost/signin/',
selector: '.btn-blue'
},
'signin-authenticated': {
url: 'ghost/signin/',
// signin with authenticated user redirects to posts
selector: '.gh-nav-main-content.active'
},
signout: {
url: 'ghost/signout/',
linkSelector: '.user-menu-signout',
// When no user exists we get redirected to setup which has btn-green
selector: '.btn-blue, .btn-green'
},
signup: {
url: 'ghost/signup/',
selector: '.btn-blue'
},
setup: {
url: 'ghost/setup/one/',
selector: '.btn-green'
},
'setup.two': {
url: 'ghost/setup/two/',
linkSelector: '.btn-green',
selector: '.gh-flow-create'
},
'setup.three': {
url: 'ghost/setup/three/',
selector: '.gh-flow-invite'
},
'setup-authenticated': {
url: 'ghost/setup/',
selector: '.gh-nav-main-content.active'
}
};
casper.writeContentToEditor = function (content) {
// If we are on a new editor, the autosave is going to get triggered when we try to type, so we need to trigger
// that and wait for it to sort itself out
if (/ghost\/editor\/$/.test(casper.getCurrentUrl())) {
casper.waitForSelector('.entry-markdown-content textarea', function onSuccess() {
casper.click('.entry-markdown-content textarea');
}, function onTimeout() {
casper.test.fail('Editor was not found on initial load.');
}, 2000);
casper.waitForUrl(/\/ghost\/editor\/\d+\/$/, function onSuccess() {
// do nothing
}, function onTimeout() {
casper.test.fail('The url didn\'t change: ' + casper.getCurrentUrl());
}, 2000);
}
casper.waitForSelector('.entry-markdown-content textarea', function onSuccess() {
casper.sendKeys('.entry-markdown-content textarea', content, {keepFocus: true});
// Always end with a new line
casper.sendKeys('.entry-markdown-content textarea', '\n', {keepFocus: true});
casper.captureScreenshot('EditorText.png');
return this;
}, function onTimeout() {
casper.test.fail('Editor was not found on main load.');
}, 2000);
};
casper.waitForOpacity = function (classname, opacity, then, timeout) {
timeout = timeout || casper.failOnTimeout(casper.test, 'waitForOpacity failed on ' + classname + ' ' + opacity);
casper.waitForSelector(classname).then(function () {
casper.waitFor(function checkOpaque() {
var value = this.evaluate(function (element, opacity) {
var target = document.querySelector(element);
if (target === null) {
return null;
}
return window.getComputedStyle(target).getPropertyValue('opacity') === opacity;
}, classname, opacity);
if (value !== true && value !== false) {
casper.test.fail('Unable to find element: ' + classname);
}
return value;
}, then, timeout);
});
};
casper.waitForOpaque = function (classname, then, timeout) {
casper.waitForOpacity(classname, '1', then, timeout);
};
casper.waitForTransparent = function (classname, then, timeout) {
casper.waitForOpacity(classname, '0', then, timeout);
};
// ### Then Open And Wait For Page Load
// Always wait for the `.page-content` element as some indication that the ember app has loaded.
casper.thenOpenAndWaitForPageLoad = function (screen, then, timeout) {
then = then || function () {};
timeout = timeout || casper.failOnTimeout(casper.test, 'Unable to load ' + screen);
return casper.thenOpen(url + screens[screen].url).then(function () {
// HACK: phantomjs + flexbox = nope. Fix offending styles here.
casper.evaluate(function () {
var style = document.createElement('style');
style.innerHTML = '.gh-main > section { width: auto; }';
document.body.appendChild(style);
});
return casper.waitForScreenLoad(screen, then, timeout);
});
};
casper.waitForScreenLoad = function (screen, then, timeout) {
// Some screens fade in
return casper.waitForOpaque(screens[screen].selector, then, timeout, 10000);
};
casper.thenTransitionAndWaitForScreenLoad = function (screen, then, timeout) {
then = then || function () {};
timeout = timeout || casper.failOnTimeout(casper.test, 'Unable to load ' + screen);
return casper.thenClick(screens[screen].linkSelector).then(function () {
return casper.waitForScreenLoad(screen, then, timeout);
});
};
casper.failOnTimeout = function (test, message) {
return function onTimeout() {
test.fail(message);
};
};
// ### Fill And Save
// With Ember in place, we don't want to submit forms, rather press the button which always has a class of
// 'btn-blue'. This method handles that smoothly.
casper.fillAndSave = function (selector, data) {
casper.then(function doFill() {
casper.fill(selector, data, false);
casper.thenClick('.btn-blue');
});
};
// ### Fill And Add
// With Ember in place, we don't want to submit forms, rather press the green button which always has a class of
// 'btn-green'. This method handles that smoothly.
casper.fillAndAdd = function (selector, data) {
casper.then(function doFill() {
casper.fill(selector, data, false);
casper.thenClick('.btn-green');
});
};
// ## Echo Concise
// Does casper.echo but checks for the presence of the --concise flag
casper.echoConcise = function (message, style) {
if (!casper.cli.options.concise) {
casper.echo(message, style);
}
};
// ### Wait for Selector Text
// Does casper.waitForSelector but checks for the presence of specified text
// http://stackoverflow.com/questions/32104784/wait-for-an-element-to-have-a-specific-text-with-casperjs
casper.waitForSelectorText = function (selector, text, then, onTimeout, timeout) {
this.waitForSelector(selector, function _then() {
this.waitFor(function _check() {
var content = this.fetchText(selector);
if (utils.isRegExp(text)) {
return text.test(content);
}
return content.indexOf(text) !== -1;
}, then, onTimeout, timeout);
}, onTimeout, timeout);
return this;
};
// pass through all console.logs
casper.on('remote.message', function (msg) {
casper.echoConcise('CONSOLE LOG: ' + msg, 'INFO');
});
// output any errors
casper.on('error', function (msg, trace) {
casper.echoConcise('ERROR, ' + msg, 'ERROR');
if (trace && trace[0]) {
casper.echoConcise('file: ' + trace[0].file, 'WARNING');
casper.echoConcise('line: ' + trace[0].line, 'WARNING');
casper.echoConcise('function: ' + trace[0].function, 'WARNING');
}
jsErrors.push(msg);
});
// output any page errors
casper.on('page.error', function (msg, trace) {
casper.echoConcise('PAGE ERROR: ' + msg, 'ERROR');
if (trace && trace[0]) {
casper.echoConcise('file: ' + trace[0].file, 'WARNING');
casper.echoConcise('line: ' + trace[0].line, 'WARNING');
casper.echoConcise('function: ' + trace[0].function, 'WARNING');
}
pageErrors.push(msg);
});
casper.on('resource.received', function (resource) {
var status = resource.status;
if (status >= 400) {
casper.echoConcise('RESOURCE ERROR: ' + resource.url + ' failed to load (' + status + ')', 'ERROR');
resourceErrors.push({
url: resource.url,
status: resource.status
});
}
});
casper.captureScreenshot = function (filename, debugOnly) {
debugOnly = debugOnly !== false;
// If we are in debug mode, OR debugOnly is false
if (DEBUG || debugOnly === false) {
filename = filename || 'casper_test_fail.png';
casper.then(function () {
casper.capture(new Date().getTime() + '_' + filename);
});
}
};
// on failure, grab a screenshot
casper.test.on('fail', function captureFailure() {
casper.captureScreenshot(casper.test.filename || 'casper_test_fail.png', false);
casper.then(function () {
console.log(casper.getHTML());
casper.exit(1);
});
});
// on exit, output any errors
casper.test.on('exit', function () {
if (jsErrors.length > 0) {
casper.echo(jsErrors.length + ' Javascript errors found', 'WARNING');
} else {
casper.echo(jsErrors.length + ' Javascript errors found', 'INFO');
}
if (pageErrors.length > 0) {
casper.echo(pageErrors.length + ' Page errors found', 'WARNING');
} else {
casper.echo(pageErrors.length + ' Page errors found', 'INFO');
}
if (resourceErrors.length > 0) {
casper.echo(resourceErrors.length + ' Resource errors found', 'WARNING');
} else {
casper.echo(resourceErrors.length + ' Resource errors found', 'INFO');
}
});
CasperTest = (function () {
var _beforeDoneHandler,
_noop = function noop() { },
_isUserRegistered = false;
// Always log out at end of test.
casper.test.tearDown(function (done) {
casper.then(_beforeDoneHandler);
CasperTest.Routines.signout.run();
casper.run(done);
});
// Wrapper around `casper.test.begin`
function begin(testName, expect, suite, doNotAutoLogin, doNotRunSetup) {
_beforeDoneHandler = _noop;
var runTest = function (test) {
test.filename = testName.toLowerCase().replace(/ /g, '-').concat('.png');
casper.start('about:blank').viewport(1280, 1024);
// Only call register once for the lifetime of CasperTest
if (!_isUserRegistered && !doNotRunSetup) {
CasperTest.Routines.signout.run();
CasperTest.Routines.setup.run();
CasperTest.Routines.signout.run();
_isUserRegistered = true;
}
if (!doNotAutoLogin) {
/* Ensure we're logged out at the start of every test or we may get
unexpected failures. */
CasperTest.Routines.signout.run();
CasperTest.Routines.signin.run();
}
suite.call(casper, test);
casper.run(function () {
test.done();
});
};
if (typeof expect === 'function') {
doNotAutoLogin = suite;
suite = expect;
casper.test.begin(testName, runTest);
} else {
casper.test.begin(testName, expect, runTest);
}
}
// Sets a handler to be invoked right before `test.done` is invoked
function beforeDone(fn) {
if (fn) {
_beforeDoneHandler = fn;
} else {
_beforeDoneHandler = _noop;
}
}
return {
begin: begin,
beforeDone: beforeDone
};
}());
CasperTest.Routines = (function () {
function setup() {
casper.thenOpenAndWaitForPageLoad('setup.two', function then() {
casper.captureScreenshot('setting_up1.png');
casper.fillAndAdd('#setup', newSetup);
casper.captureScreenshot('setting_up2.png');
casper.waitForSelectorTextChange('.gh-alert-success', function onSuccess() {
var errorText = casper.evaluate(function () {
return document.querySelector('.gh-alert').innerText;
});
casper.echoConcise('Setup failed. Error text: ' + errorText);
}, function onTimeout() {
casper.echoConcise('Setup completed.');
}, 2000);
casper.captureScreenshot('setting_up3.png');
});
}
function signin() {
casper.thenOpenAndWaitForPageLoad('signin', function then() {
casper.waitForOpaque('.gh-signin', function then() {
casper.captureScreenshot('signing_in.png');
this.fillAndSave('#login', user);
casper.captureScreenshot('signing_in2.png');
});
casper.waitForResource(/posts\/\?(?=.*status=all)(?=.*staticPages=all)/, function then() {
casper.captureScreenshot('signing_in.png');
}, function timeout() {
casper.test.fail('Unable to signin and load admin panel');
});
});
}
function signout() {
casper.thenOpenAndWaitForPageLoad('signout', function then() {
casper.captureScreenshot('ember_signing_out.png');
});
}
// This will need switching over to ember once settings general is working properly.
function togglePermalinks(state) {
casper.thenOpenAndWaitForPageLoad('settings.general', function then() {
var currentState = this.evaluate(function () {
return document.querySelector('#permalinks') && document.querySelector('#permalinks').checked ? 'on' : 'off';
});
if (currentState !== state) {
casper.thenClick('#permalinks');
casper.thenClick('.btn-blue');
casper.captureScreenshot('saving.png');
casper.waitForSelector('.notification-success', function () {
casper.captureScreenshot('saved.png');
});
}
});
}
function createTestPost(publish) {
casper.thenOpenAndWaitForPageLoad('editor', function createTestPost() {
casper.sendKeys('#entry-title', testPost.title);
casper.writeContentToEditor(testPost.html);
});
casper.waitForSelectorTextChange('.entry-preview .rendered-markdown');
if (publish) {
// Open the publish options menu;
casper.thenClick('.js-publish-splitbutton .dropdown-toggle');
casper.waitForOpaque('.js-publish-splitbutton .open');
// Select the publish post button
casper.thenClick('.post-save-publish a');
casper.waitForSelectorTextChange('.js-publish-button', function onSuccess() {
casper.thenClick('.js-publish-button');
});
} else {
casper.thenClick('.js-publish-button');
}
casper.waitForResource(/posts\/\?include=tags$/);
}
function _createRunner(fn) {
fn.run = function run(test) {
var self = this;
casper.then(function () {
self.call(casper, test);
});
};
return fn;
}
return {
setup: _createRunner(setup),
signin: _createRunner(signin),
signout: _createRunner(signout),
createTestPost: _createRunner(createTestPost),
togglePermalinks: _createRunner(togglePermalinks)
};
}());

View File

@ -1,38 +0,0 @@
// # About Test
// Test the various parts of the About page
/*globals CasperTest, casper */
CasperTest.begin('About screen is correct', 9, function suite(test) {
casper.thenOpenAndWaitForPageLoad('about', function testTitleAndUrl() {
test.assertTitle('About - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/about\/$/, 'Redirected to the correct URL');
});
casper.then(function testVersionNumber() {
var versionNumber = casper.getHTML('.gh-env-list-version');
test.assertMatch(versionNumber, /\d+\.\d+\.\d+/, 'Version is a number'); // Tests for a pattern like 0.0.0 to v11111.3334534.2342453-beta
});
casper.then(function testDatabaseType() {
var databaseTypeText = casper.getHTML('.gh-env-list-database-type');
test.assertMatch(databaseTypeText, /sqlite3|mysql|pg/gi, 'Database is an allowed type');
});
casper.waitForSelector('.gh-contributors article', function testContributors() {
var firstContribImageSrc = casper.getElementAttribute('.gh-contributors article:nth-child(1) a img', 'src');
// Check first contributor image tag is on the page
test.assertExist('.gh-contributors article:nth-child(1) img', 'First contributor image is in place');
// Check first contributor image resource exists & alt tag isnt empty
test.assertResourceExists(firstContribImageSrc, 'First contributor image file exists');
test.assertDoesntExist('.gh-contributors article:nth-child(1) a img[alt=""]', 'First contributor image alt is not empty');
// Check first contributor links to GitHub
test.assertExists('.gh-contributors article:nth-child(1) a[href*="github.com"]', 'First contributor link to GitHub');
// Check first contributor links to GitHub
test.assertDoesntExist('.gh-contributors article:nth-child(1) a[title=""]', 'First contributor title is not empty');
});
});

View File

@ -1,230 +0,0 @@
// # App Test
// Tests that the general layout & functionality of global admin components is correct
/*globals CasperTest, casper, newUser */
CasperTest.begin('Admin navigation bar is correct', 65, function suite(test) {
casper.thenOpenAndWaitForPageLoad('root', function testTitleAndUrl() {
test.assertTitle('Content - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/\d+\/$/, 'Landed on the correct URL');
});
casper.then(function testNavItems() {
var logoHref = this.getElementAttribute('.gh-nav-footer-sitelink', 'href'),
contentHref = this.getElementAttribute('.gh-nav-main-content', 'href'),
editorHref = this.getElementAttribute('.gh-nav-main-editor', 'href'),
usersHref = this.getElementAttribute('.gh-nav-main-users', 'href'),
settingsGeneralHref = this.getElementAttribute('.gh-nav-settings-general', 'href'),
settingsNavigationHref = this.getElementAttribute('.gh-nav-settings-navigation', 'href'),
settingsTagsHref = this.getElementAttribute('.gh-nav-settings-tags', 'href'),
settingsCodeInjectionHref = this.getElementAttribute('.gh-nav-settings-code-injection', 'href'),
settingsLabsHref = this.getElementAttribute('.gh-nav-settings-labs', 'href');
// Logo
test.assertExists('.gh-nav-footer-sitelink', 'Ghost home page link exists in nav footer');
test.assertEquals(logoHref, 'http://127.0.0.1:2369/', 'Ghost logo link href is correct');
// Content
test.assertExists('.gh-nav-main-content', 'Content nav item exists');
test.assertSelectorHasText('.gh-nav-main-content', 'Content', 'Content nav item has correct text');
test.assertEquals(contentHref, '/ghost/', 'Content href is correct');
test.assertExists('.gh-nav-main-content.active', 'Content nav item is not marked active');
// Editor
test.assertExists('.gh-nav-main-editor', 'Editor nav item exists');
test.assertSelectorHasText('.gh-nav-main-editor', 'New Post', 'Editor nav item has correct text');
test.assertEquals(editorHref, '/ghost/editor/', 'Editor href is correct');
test.assertDoesntExist('.gh-nav-main-editor.active', 'Editor nav item is not marked active');
// Users
test.assertExists('.gh-nav-main-users', 'Users nav item exists');
test.assertSelectorHasText('.gh-nav-main-users', 'Team', 'Users nav item has correct text');
test.assertEquals(usersHref, '/ghost/team/', 'Users href is correct');
test.assertDoesntExist('.gh-nav-main-users.active', 'Users nav item is not marked active');
// Settings - General
test.assertExists('.gh-nav-settings-general', 'Settings - General nav exists');
test.assertSelectorHasText('.gh-nav-settings-general', 'General', 'Settings nav item has correct text');
test.assertEquals(settingsGeneralHref, '/ghost/settings/general/', 'Settings href is correct');
test.assertDoesntExist('.gh-nav-settings-general.active', 'Settings nav item is marked active');
// Settings - Navigation
test.assertExists('.gh-nav-settings-navigation', 'Settings - Navigation nav item exists');
test.assertSelectorHasText('.gh-nav-settings-navigation', 'Navigation', 'Settings nav item has correct text');
test.assertEquals(settingsNavigationHref, '/ghost/settings/navigation/', 'Settings Navigation href is correct');
test.assertDoesntExist('.gh-nav-settings-navigation.active', 'Settings - Navigation nav item is marked active');
// Settings - Tags
test.assertExists('.gh-nav-settings-tags', 'Settings - Tags nav item exists');
test.assertSelectorHasText('.gh-nav-settings-tags', 'Tags', 'Settings nav item has correct text');
test.assertEquals(settingsTagsHref, '/ghost/settings/tags/', 'Settings Navigation href is correct');
test.assertDoesntExist('.gh-nav-settings-tags.active', 'Settings - Navigation nav item is marked active');
// Settings - Code Injection
test.assertExists('.gh-nav-settings-code-injection', 'Settings - Code Injection nav item exists');
test.assertSelectorHasText('.gh-nav-settings-code-injection', 'Code Injection', 'Settings nav item has correct text');
test.assertEquals(settingsCodeInjectionHref, '/ghost/settings/code-injection/', 'Settings Navigation href is correct');
test.assertDoesntExist('.gh-nav-settings-code-injection.active', 'Settings - Code Injection nav item is marked active');
// Settings - Labs
test.assertExists('.gh-nav-settings-labs', 'Settings - Labs nav item exists');
test.assertSelectorHasText('.gh-nav-settings-labs', 'Labs', 'Settings nav item has correct text');
test.assertEquals(settingsLabsHref, '/ghost/settings/labs/', 'Settings Labs href is correct');
test.assertDoesntExist('.gh-nav-settings-labs.active', 'Settings - Labs nav item is marked active');
});
casper.then(function testHelpMenuNotVisible() {
test.assertExists('.gh-help-button', 'Help menu nav item exists');
test.assertNotExists('.gh-help-button .dropdown.open', 'Help menu should not be visible');
});
casper.thenClick('.gh-help-button');
casper.waitForSelector('.dropdown', function then() {
var supportHref = this.getElementAttribute('.help-menu-support', 'href'),
tweetHref = this.getElementAttribute('.help-menu-tweet', 'href'),
howtoHref = this.getElementAttribute('.help-menu-how-to', 'href'),
wishlistHref = this.getElementAttribute('.help-menu-wishlist', 'href');
test.assertVisible('.dropdown-menu', 'Help menu should be visible');
test.assertExists('.help-menu-support', 'Support menu item exists');
test.assertSelectorHasText('.help-menu-support', 'Support Center', 'Support menu item has correct text');
test.assertEquals(supportHref, 'http://support.ghost.org/', 'Support href is correct');
test.assertExists('.help-menu-tweet', 'Tweet menu item exists');
test.assertSelectorHasText('.help-menu-tweet', 'Tweet @TryGhost!', 'Tweet menu item has correct text');
test.assertEquals(tweetHref, 'https://twitter.com/intent/tweet?text=%40TryGhost+Hi%21+Can+you+help+me+with+&related=TryGhost', 'Tweet href is correct');
test.assertExists('.help-menu-how-to', 'How-to menu item exists');
test.assertSelectorHasText('.help-menu-how-to', 'How to Use Ghost', 'How-to menu item has correct text');
test.assertEquals(howtoHref, 'http://support.ghost.org/how-to-use-ghost/', 'How-to href is correct');
test.assertExists('.help-menu-wishlist', 'Wishlist menu item exists');
test.assertSelectorHasText('.help-menu-wishlist', 'Wishlist', 'Wishlist menu item has correct text');
test.assertEquals(wishlistHref, 'http://ideas.ghost.org/', 'Wishlist href is correct');
test.assertExists('.help-menu-markdown', 'Markdown menu item exists');
test.assertSelectorHasText('.help-menu-markdown', 'Markdown Help', 'Markdown menu item has correct text');
casper.thenClick('.help-menu-markdown');
casper.waitUntilVisible('.modal-container', function onSuccess() {
test.assertSelectorHasText(
'.modal-content .modal-header',
'Markdown Help',
'delete modal has correct text');
test.assertExists('.modal-content .close', '.modal-content .close exists');
});
casper.thenClick('.modal-content .close');
casper.waitWhileVisible('.modal-container', function onSuccess() {
test.assert(true, 'clicking close should remove the markdown help modal');
});
}, casper.failOnTimeout(test, 'WaitForSelector .gh-help-menu .ember-view.open failed'));
casper.then(function testUserMenuNotVisible() {
test.assertExists('.gh-nav-menu .gh-nav-menu-details-user', 'User menu nav item exists');
test.assertNotExists('.gh-nav-menu-details-user .user-menu-profile', 'User menu should not be visible');
});
casper.thenClick('.gh-nav-menu');
casper.waitForSelector('.dropdown', function then() {
var profileHref = this.getElementAttribute('.user-menu-profile', 'href'),
signoutHref = this.getElementAttribute('.user-menu-signout', 'href');
test.assertVisible('.dropdown-item.user-menu-profile', 'User menu should be visible');
test.assertExists('.dropdown-item.user-menu-profile', 'Profile menu item exists');
test.assertSelectorHasText('.dropdown-item.user-menu-profile', 'Your Profile',
'Profile menu item has correct text');
test.assertEquals(profileHref, '/ghost/team/' + newUser.slug + '/', 'Profile href is correct');
test.assertExists('.user-menu-signout', 'Sign Out menu item exists');
test.assertSelectorHasText('.user-menu-signout', 'Sign Out', 'Signout menu item has correct text');
test.assertEquals(signoutHref, '/ghost/signout/', 'Sign Out href is correct');
}, casper.failOnTimeout(test, 'WaitForSelector .user-menu .dropdown failed'));
// TODO Add tests to check each pane gets active class appropriately
});
CasperTest.begin('Can transition to the editor and back', 6, function suite(test) {
casper.thenOpenAndWaitForPageLoad('root', function testTitleAndUrl() {
test.assertTitle('Content - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/\d+\/$/, 'Landed on the correct URL');
});
casper.thenTransitionAndWaitForScreenLoad('editor', function testTransitionToEditor() {
test.assertUrlMatch(/ghost\/editor\//, 'Landed on the correct URL');
test.assertExists('.entry-markdown', 'Ghost editor is present');
test.assertExists('.entry-preview', 'Ghost preview is present');
});
casper.thenTransitionAndWaitForScreenLoad('content', function testTransitionToContent() {
test.assertUrlMatch(/ghost\/\d+\/$/, 'Landed on the correct URL');
});
});
CasperTest.begin('Can search for posts and users', 8, function suite(test) {
var searchControl = '.gh-nav-search-input',
searchInput = '.gh-nav-search-input .selectize-input',
mouse = require('mouse').create(casper);
casper.thenOpenAndWaitForPageLoad('root', function testTitleAndUrl() {
test.assertTitle('Content - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/\d+\/$/, 'Landed on the correct URL');
});
casper.thenClick('.gh-nav-search-button');
casper.waitForResource(/posts\/\?fields=id%2Ctitle%2Cpage&limit=all&status=all&staticPages=all/, function then() {
test.assert(true, 'Queried filtered posts list on search focus');
}, function timeout() {
casper.test.fail('Did not query filtered posts list on search focus');
});
casper.waitForResource(/users\/\?fields=name%2Cslug&limit=all/, function then() {
test.assert(true, 'Queried filtered users list on search focus');
}, function timeout() {
casper.test.fail('Did not query filtered users list on search focus');
});
casper.then(function testUserResults() {
casper.sendKeys(searchInput, 'Test', {keepFocus: true});
casper.waitForSelectorText(searchControl + ' .option.active', 'Test User', function success() {
test.assert(true, 'Queried user was displayed when searching');
}, function timeout() {
casper.test.fail('Queried user was not displayed when searching');
});
});
casper.then(function testUserNavigation() {
casper.sendKeys(searchInput, casper.page.event.key.Enter, {keepFocus: true});
casper.waitForSelector('.settings-user', function () {
test.assertUrlMatch(/ghost\/team\/test\//, 'Landed on correct URL');
});
});
// casper loses the focus somehow, click off/on the input to regain it
casper.thenClick('.gh-input.user-name');
casper.thenClick(searchControl + ' .selectize-input');
casper.wait(500);
casper.then(function testPostResultsAndClick() {
casper.sendKeys(searchInput, 'Welcome', {keepFocus: true});
casper.wait(500);
casper.then(function () {
casper.waitForSelectorText(searchControl + ' .option.active', 'Welcome to Ghost', function success() {
test.assert(true, 'Queried post was displayed when searching');
mouse.down(searchControl + ' .option.active');
casper.waitForSelector('.view-editor', function () {
test.assertUrlMatch(/ghost\/editor\/\d\//, 'Landed on correct URL');
});
}, function timeout() {
casper.test.fail('Queried post was not displayed when searching');
});
});
});
});

View File

@ -1,94 +0,0 @@
// # Content Test
// Test the content screen, uses the editor to create dummy content
/*globals CasperTest, casper, testPost */
CasperTest.begin('Content screen is correct', 15, function suite(test) {
// First, create a sample post for testing (this should probably be a routine)
CasperTest.Routines.createTestPost.run(false);
// Begin test
casper.thenOpenAndWaitForPageLoad('content', function testTitleAndUrl() {
test.assertTitle('Content - Test Blog', 'Title is "Content - Test Blog"');
test.assertUrlMatch(/ghost\/\d+\/$/, 'Landed on the correct URL');
});
casper.then(function testViews() {
test.assertExists('.gh-main .gh-view', 'Content main view is present');
test.assertExists('.content-list-content', 'Content list view is present');
test.assertExists('.gh-nav-main-editor', 'add new post button exists');
test.assertEquals(
this.getElementAttribute('.gh-nav-main-editor', 'href'),
'/ghost/editor/', 'add new post href is correct'
);
test.assertExists('.content-list-content li .entry-title', 'Content list view has at least one item');
test.assertSelectorHasText(
'.content-list-content li:first-of-type h3', testPost.title, 'title is present and has content'
);
test.assertSelectorHasText(
'.content-list-content li:first-of-type .entry-meta .status .draft', 'Draft', 'correct status is present'
);
test.assertExists('.content-preview', 'Content preview is present');
});
casper.then(function testEditPostButton() {
test.assertExists('.content-preview a.post-edit', 'edit post button exists');
});
casper.then(function testActiveItem() {
test.assertExists('.content-list-content li:first-of-type .active', 'first item is active');
test.assertDoesntExist('.content-list-content li:nth-of-type(2) .active', 'second item is not active');
// Ember adds script tags into the list so we need to use nth-of-type
}).thenClick('.content-list-content li:nth-of-type(2) a', function then() {
test.assertDoesntExist('.content-list-content li:first-of-type .active', 'first item is not active');
test.assertExists('.content-list-content li:nth-of-type(2) .active', 'second item is active');
});
});
CasperTest.begin('Content list shows correct post status', 3, function testStaticPageStatus(test) {
CasperTest.Routines.createTestPost.run(true);
// Begin test
casper.thenOpenAndWaitForPageLoad('content', function testTitleAndUrl() {
test.assertTitle('Content - Test Blog', 'Title is "Content - Test Blog"');
test.assertUrlMatch(/ghost\/\d+\/$/, 'Landed on the correct URL');
});
// Select first non-draft, non-static post. Should be second in the list at this stage of testing.
casper.thenClick('.content-list-content li:nth-of-type(3) a');
// Test for status of 'Published'
casper.then(function checkStatus() {
test.assertSelectorHasText('.content-list-content .active .published', 'Published',
'status is present and labeled as published');
});
casper.thenClick('.post-edit');
casper.waitForSelector('#entry-title');
// // TODO readd this test when #3811 is fixed
// // Change post to static page
// casper.thenClick('.post-settings');
// casper.waitForOpaque('.post-settings-menu.open');
// casper.thenClick('.post-setting-static-page');
// casper.thenTransitionAndWaitForScreenLoad('content', function onSuccess() {
// casper.waitForSelector('.content-list-content li .entry-meta .status .page', function waitForSuccess() {
// test.assertSelectorHasText('.content-list-content li .entry-meta .status .page', 'Page', 'status is Page');
// }, function onTimeout() {
// test.assert(false, 'status did not change');
// });
// });
});
// TODO: Implement this test... much needed!
// CasperTest.begin('Infinite scrolling', 2, function suite(test) {
// // Placeholder for infinite scrolling/pagination tests (will need to setup 16+ posts).
//
// casper.thenOpenAndWaitForPageLoad('content', function testTitleAndUrl() {
// test.assertTitle('Content - Test Blog', 'Title is "Content - Test Blog"');
// test.assertUrlMatch(/ghost\/\d+\/$/, 'Landed on the correct URL');
// });
// });

View File

@ -1,734 +0,0 @@
// # Editor Test
// Test the editor screen works as expected
/*globals CasperTest, casper, testPost, $ */
CasperTest.begin('Ghost editor functions correctly', 16, function suite(test) {
test.assertHTMLEquals = function (equals, message) {
test.assertEvalEquals(function () {
return document.querySelector('.entry-preview .rendered-markdown').innerHTML
.replace(/<script.*?><\/script>/g, '');
}, equals, message);
};
casper.thenOpenAndWaitForPageLoad('editor', function testTitleAndUrl() {
test.assertTitle('Editor - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/editor\/$/, 'Landed on the correct URL');
test.assertExists('.entry-markdown', 'Ghost editor is present');
test.assertExists('.entry-preview', 'Ghost preview is present');
});
// Part 1: Test saving with no data - title should default
casper.waitForSelector('#entry-title', function then() {
test.assertEvalEquals(function () {
return document.getElementById('entry-title').value;
}, '', 'Title is empty');
});
casper.thenClick('.js-publish-button');
casper.waitForSelector('.gh-notification', function onSuccess() {
test.assert(true, 'Can save with no title.');
test.assertEvalEquals(function () {
return document.getElementById('entry-title').value;
}, '(Untitled)', 'Title is "(Untitled)"');
}, function onTimeout() {
test.assert(false, 'Failed to save without a title.');
});
casper.thenClick('.gh-notification-close');
// Part 2: Test saving with data
casper.then(function createTestPost() {
casper.sendKeys('#entry-title', testPost.title, {reset: true});
casper.writeContentToEditor(testPost.html);
// TODO: Expand markdown tests to cover more markdown, and keyboard shortcuts
casper.waitForSelectorTextChange('.entry-preview .rendered-markdown', function onSuccess() {
test.assertSelectorHasText(
'.entry-preview .rendered-markdown',
'I am a test post. \n\nI have some small content\n',
'Editor HTML preview has correct text.'
);
test.assertHTMLEquals(
'<p>I am a test post. </p>\n\n<h1 id=\"ihavesomesmallcontent\">I have some small content</h1>\n',
'generated HTML is correct'
);
}, casper.failOnTimeout(test, 'markdown did not re-render'));
});
casper.thenClick('.js-publish-button');
casper.waitForSelector('.gh-notification', function onSuccess() {
test.assertUrlMatch(/ghost\/editor\/\d+\/$/, 'got an id on our URL');
test.assertEvalEquals(function () {
return document.querySelector('#entry-title').value;
}, testPost.title, 'Title is correct');
}, casper.failOnTimeout(test, 'Post was not successfully created'));
// Part 3: Test title trimming
var untrimmedTitle = ' test title ',
trimmedTitle = 'test title';
casper.then(function populateTitle() {
// Clear element
casper.evaluate(function () {
$('#entry-title').val('');
});
casper.sendKeys('#entry-title', untrimmedTitle);
casper.click('#entry-markdown-content');
test.assertEvalEquals(function () {
return $('#entry-title').val();
}, trimmedTitle, 'Entry title should match expected value.');
});
// Reset the editor
casper.thenOpenAndWaitForPageLoad('editor', function testWordCount() {
// Part 4: Word count and plurality
casper.then(function checkZeroPlural() {
test.assertSelectorHasText('.entry-word-count', '0 words', 'count of 0 produces plural "words".');
});
casper.then(function () {
casper.writeContentToEditor('test');
});
casper.waitForSelectorTextChange('.entry-word-count', function onSuccess() {
test.assertSelectorHasText('.entry-word-count', '1 word', 'count of 1 produces singular "word".');
});
casper.then(function () {
casper.writeContentToEditor('test');
});
casper.waitForSelectorTextChange('.entry-word-count', function onSuccess() {
test.assertSelectorHasText('.entry-word-count', '2 words', 'count of 2 produces plural "words".');
});
casper.then(function () {
casper.writeContentToEditor('even **more** words'); // append another word, assumes newline
});
casper.waitForSelectorTextChange('.entry-word-count', function onSuccess() {
test.assertSelectorHasText('.entry-word-count', '5 words', 'count of 5 produces plural "words".');
});
});
});
CasperTest.begin('Image Uploads', 23, function suite(test) {
test.assertHTMLEquals = function (equals, message) {
test.assertEvalEquals(function () {
return document.querySelector('.entry-preview .rendered-markdown').innerHTML
.replace(/<script.*?><\/script>/g, '');
}, equals, message);
};
casper.thenOpenAndWaitForPageLoad('editor', function testTitleAndUrl() {
test.assertTitle('Editor - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/editor\/$/, 'Landed on the correct URL');
});
//
// Test standard image upload modal
casper.then(function testImage() {
casper.writeContentToEditor('![some text]()');
});
casper.waitForSelectorTextChange('.entry-preview .rendered-markdown', function onSuccess() {
test.assertEvalEquals(function () {
return document.querySelector('.entry-markdown-content textarea').value;
}, '![some text]()\n', 'Editor value is correct');
test.assertHTMLEquals('<section class=\"js-drop-zone image-uploader\" data-uploaderui=\"true\"><span class=\"media\"><span class=\"hidden\">Image Upload</span></span><img class=\"js-upload-target\" style=\"display: none; \" src=\"\"><div class=\"description\">Add image of <strong>some text</strong></div><input class=\"js-fileupload main fileupload\" type=\"file\" name=\"uploadimage\"><div class=\"js-fail failed\" style=\"display: none\">Something went wrong :(</div><button class=\"js-fail btn btn-green\" style=\"display: none\">Try Again</button><a class=\"image-url\" title=\"Add image from URL\"><i class=\"icon-link\"><span class=\"hidden\">URL</span></i></a></section>\n', 'HTML is correct');
test.assertSelectorHasText(
'.entry-preview .rendered-markdown', 'Add image of some text', 'Alt value is correct'
);
}, function onTimeout() {
test.assert('false', 'markdown did not re-render');
});
function assertEmptyImageUploaderDisplaysCorrectly() {
test.assertExists('.entry-preview .js-upload-target', 'Upload target exists');
test.assertExists('.entry-preview .js-fileupload', 'File upload target exists');
test.assertExists('.entry-preview .icon-link', 'Image URL button exists');
}
casper.then(function waitForUploader() {
casper.waitForSelector('.entry-preview .js-drop-zone.image-uploader', assertEmptyImageUploaderDisplaysCorrectly);
});
// Test image URL upload modal
casper.thenClick('.entry-preview .image-uploader a.image-url');
casper.then(function checkUploader() {
casper.waitForSelector('.image-uploader-url', function onSuccess() {
test.assertExists('.image-uploader-url .url.js-upload-url', 'Image URL uploader exists');
test.assertExists('.image-uploader-url .btn-blue.js-button-accept', 'Image URL accept button exists');
test.assertExists('.image-uploader-url .image-upload', 'Back to normal image upload style button exists');
});
});
// Test image source location
casper.thenOpenAndWaitForPageLoad('editor', function testTitleAndUrl() {
test.assertTitle('Editor - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/editor\/$/, 'Landed on the correct URL');
});
var testFileLocation = '/test/file/location',
imageURL = 'http://www.random.url';
casper.then(function () {
var markdownImageString = '![](' + testFileLocation + ')';
casper.writeContentToEditor(markdownImageString);
});
casper.waitForSelector('img.js-upload-target', function () {
var imageJQuerySelector = '.entry-preview img.js-upload-target[src="' + testFileLocation + '"]';
test.assertExists(imageJQuerySelector, 'Uploaded image tag properly links to source location');
});
// Test cancel image button
casper.thenClick('.pre-image-uploader a.image-cancel.js-cancel');
casper.waitForSelector('.entry-preview .js-drop-zone.image-uploader', assertEmptyImageUploaderDisplaysCorrectly);
// Test image url source location
casper.thenOpenAndWaitForPageLoad('editor', function testTitleAndUrl() {
test.assertTitle('Editor - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/editor\/$/, 'Landed on the correct URL');
});
casper.then(function () {
casper.writeContentToEditor('![]()');
});
casper.then(function () {
casper.waitForSelector('.entry-preview .js-drop-zone.image-uploader', function onSuccess() {
casper.thenClick('.entry-preview .image-uploader .image-url');
});
});
casper.then(function () {
casper.waitForSelector('.image-uploader-url', function onSuccess() {
casper.sendKeys('.image-uploader-url input.url.js-upload-url', imageURL);
casper.thenClick('.js-button-accept.btn-blue');
});
});
casper.waitForSelector('img.js-upload-target', function onSuccess() {
var imageJQuerySelector = '.entry-preview img.js-upload-target[src="' + imageURL + '"]';
test.assertExists(imageJQuerySelector, 'Uploaded image tag properly links to inputted image URL');
});
// Save the post with the image
casper.thenClick('.js-publish-button');
casper.waitForSelector('.gh-notification', function onSuccess() {
test.assertUrlMatch(/ghost\/editor\/\d+\/$/, 'got an id on our URL');
}, casper.failOnTimeout(test, 'Post was not successfully created'));
casper.thenTransitionAndWaitForScreenLoad('content', function canTransition() {
test.assert(true, 'Can transition to content screen');
test.assertUrlMatch(/ghost\/\d+\/$/, 'content transitions to correct url');
});
// TODO fix this test
// Edit the draft post we just created
// casper.thenClick('a.post-edit');
//
// casper.waitForScreenLoad('editor.editing', function () {
// casper.writeContentToEditor('abcdefghijklmnopqrstuvwxyz');
// casper.waitForSelectorTextChange('.entry-preview .rendered-markdown', function onSuccess() {
// test.assertSelectorHasText(
// '.entry-preview .rendered-markdown',//
// 'abcdefghijklmnopqrstuvwxyz',
// 'Editor HTML preview has correct text after editing.'
// );
// }, casper.failOnTimeout(test, 'markdown did not re-render'));
//
// }, casper.failOnTimeout(test, 'Editor did not load'));
});
CasperTest.begin('Publish menu - new post', 10, function suite(test) {
casper.thenOpenAndWaitForPageLoad('editor', function testTitleAndUrl() {
test.assertTitle('Editor - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/editor\/$/, 'Landed on the correct URL');
});
// ... check default option status, label, class
casper.then(function () {
test.assertExists('.js-publish-splitbutton', '.js-publish-splitbutton exists');
test.assertExists('.js-publish-button', '.js-publish-button exists');
test.assertExists('.js-publish-button.btn-blue', '.js-publish-button.btn-blue exists');
test.assertSelectorHasText('.js-publish-button', 'Save Draft', '.js-publish-button says Save Draft');
});
// Fill headline and content
casper.then(function fillContent() {
casper.sendKeys('#entry-title', 'Headline');
casper.writeContentToEditor('Just a bit of test text');
});
casper.then(function switchMenuToPublish() {
// Open the publish options menu;
casper.thenClick('.js-publish-splitbutton .dropdown-toggle');
casper.waitForOpaque('.js-publish-splitbutton .dropdown-menu');
// Select the publish post button
casper.thenClick('.js-publish-splitbutton li:first-child a');
// ... check status, label, class
casper.waitForSelector('.js-publish-splitbutton .js-publish-button:not([disabled])', function onSuccess() {
test.assertExists('.js-publish-button.btn-red', 'Publish button should have .btn-red');
test.assertSelectorHasText('.js-publish-button', 'Publish Now', '.js-publish-button says Publish Now');
}, function onTimeout() {
test.assert(false, 'Publish split button works');
});
});
// Do publish
casper.thenClick('.js-publish-button');
// ... check status, label, class
casper.waitForSelector('.js-publish-splitbutton .js-publish-button:not([disabled])', function onSuccess() {
test.assertExists('.js-publish-button.btn-blue', 'Update button should have .btn-blue');
test.assertSelectorHasText('.js-publish-button', 'Update Post', '.js-publish-button says Update Post');
}, function onTimeout() {
test.assert(false, 'Publish split button works');
});
});
CasperTest.begin('Publish menu - new page', 10, function suite(test) {
casper.thenOpenAndWaitForPageLoad('editor', function testTitleAndUrl() {
test.assertTitle('Editor - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/editor\/$/, 'Landed on the correct URL');
});
// ... check default option status, label, class
casper.then(function () {
test.assertExists('.js-publish-splitbutton', '.js-publish-splitbutton exists');
test.assertExists('.js-publish-button', '.js-publish-button exists');
test.assertExists('.js-publish-button.btn-blue', '.js-publish-button.btn-blue exists');
test.assertSelectorHasText('.js-publish-button', 'Save Draft', '.js-publish-button says Save Draft');
});
// Fill headline and content
casper.then(function fillContent() {
casper.sendKeys('#entry-title', 'Page Headline');
casper.writeContentToEditor('There once was a page, this was it');
});
// Open post settings menu
casper.thenClick('.post-settings');
// Check the checkbox is checked
casper.thenClick('label[for=static-page]');
casper.then(function switchMenuToPublish() {
// Open the publish options menu;
casper.thenClick('.js-publish-splitbutton .dropdown-toggle');
casper.waitForOpaque('.js-publish-splitbutton .dropdown-menu');
// Select the publish post button
casper.thenClick('.js-publish-splitbutton li:first-child a');
// ... check status, label, class
casper.waitForSelector('.js-publish-splitbutton .js-publish-button:not([disabled])', function onSuccess() {
test.assertExists('.js-publish-button.btn-red', 'Publish button should have .btn-red');
test.assertSelectorHasText('.js-publish-button', 'Publish Now', '.js-publish-button says Publish Now');
}, function onTimeout() {
test.assert(false, 'Publish split button works');
});
});
// Do publish
casper.thenClick('.js-publish-button');
// ... check status, label, class
casper.waitForSelector('.js-publish-splitbutton .js-publish-button:not([disabled])', function onSuccess() {
test.assertExists('.js-publish-button.btn-blue', 'Update button should have .btn-blue');
test.assertSelectorHasText('.js-publish-button', 'Update Page', '.js-publish-button says Update Page');
}, function onTimeout() {
test.assert(false, 'Publish split button works');
});
});
CasperTest.begin('Publish menu - existing post', 23, function suite(test) {
// Create a post, save it and test refreshed editor
casper.thenOpenAndWaitForPageLoad('editor', function testTitleAndUrl() {
test.assertTitle('Editor - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/editor\/$/, 'Landed on the correct URL');
});
casper.thenClick('.js-publish-splitbutton .dropdown-toggle');
casper.waitForOpaque('.js-publish-splitbutton .dropdown-menu', function onSuccess() {
test.assert(true, 'popup menu should be visible after clicking post-settings icon');
test.assertNotVisible(
'.js-publish-splitbutton .delete', 'delete post button shouldn\'t be visible on unsaved drafts'
);
});
casper.then(function createTestPost() {
casper.sendKeys('#entry-title', testPost.title);
casper.writeContentToEditor(testPost.html);
});
casper.waitForSelectorTextChange('.entry-preview .rendered-markdown', function onSuccess() {
test.assertSelectorHasText('.entry-preview .rendered-markdown', 'test', 'Editor value is correct');
});
casper.thenClick('.js-publish-splitbutton .dropdown-toggle');
// Create a post in draft status
casper.thenClick('.js-publish-button');
casper.waitForSelector('.gh-notification', function checkPostWasCreated() {
test.assertUrlMatch(/ghost\/editor\/\d+\/$/, 'got an id on our URL');
});
// ... check option status, label, class now that we're *saved* as 'draft'
casper.waitForSelector('.js-publish-splitbutton .js-publish-button:not([disabled])', function () {
test.assertExists('.js-publish-splitbutton', '.js-publish-splitbutton exists');
test.assertExists('.js-publish-button', '.js-publish-button exists');
test.assertExists('.js-publish-button.btn-blue', '.js-publish-button.btn-blue exists');
test.assertSelectorHasText('.js-publish-button', 'Save Draft', '.js-publish-button says Save Draft');
});
casper.thenClick('.js-publish-splitbutton .dropdown-toggle');
casper.waitForOpaque('.js-publish-splitbutton .dropdown-menu', function onSuccess() {
test.assert(true, 'delete post button should be visible for saved drafts');
test.assertVisible(
'.js-publish-splitbutton .delete', 'delete post button should be visible on saved drafts'
);
});
casper.thenClick('.js-publish-splitbutton .dropdown-toggle');
casper.then(function switchMenuToPublish() {
// Open the publish options menu;
casper.thenClick('.js-publish-splitbutton .dropdown-toggle');
casper.waitForOpaque('.js-publish-splitbutton .dropdown-menu');
// Select the publish post button
casper.thenClick('.js-publish-splitbutton li:first-child a');
// ... check status, label, class
casper.waitForSelector('.js-publish-splitbutton', function onSuccess() {
test.assertExists('.js-publish-button.btn-red', 'Publish button should have .btn-red');
test.assertSelectorHasText('.js-publish-button', 'Publish Now', '.js-publish-button says Publish Now');
}, function onTimeout() {
test.assert(false, 'Publish split button works');
});
});
// Do publish
casper.thenClick('.js-publish-button');
casper.waitForSelector('.gh-notification', function checkPostWasCreated() {
test.assertUrlMatch(/ghost\/editor\/\d+\/$/, 'got an id on our URL');
});
// ... check option status, label, class for saved as 'published'
casper.waitForSelector('.js-publish-splitbutton .js-publish-button:not([disabled])', function () {
test.assertExists('.js-publish-splitbutton', '.js-publish-splitbutton exists');
test.assertExists('.js-publish-button', '.js-publish-button exists');
test.assertExists('.js-publish-button.btn-blue', '.js-publish-button.btn-blue exists');
test.assertSelectorHasText('.js-publish-button', 'Update Post', '.js-publish-button says Update Post');
});
casper.then(function switchMenuToUnpublish() {
// Open the publish options menu;
casper.thenClick('.js-publish-splitbutton .dropdown-toggle');
casper.waitForOpaque('.js-publish-splitbutton .dropdown-menu');
// Select the publish post button
casper.thenClick('.js-publish-splitbutton li:nth-child(2) a');
// ... check status, label, class
casper.waitForSelector('.js-publish-splitbutton', function onSuccess() {
test.assertExists('.js-publish-button.btn-red', 'Publish button should have .btn-red');
test.assertSelectorHasText('.js-publish-button', 'Unpublish', '.js-publish-button says Unpublish');
}, function onTimeout() {
test.assert(false, 'Publish split button works');
});
});
// Do unpublish
casper.thenClick('.js-publish-button');
casper.waitForSelector('.gh-notification', function checkPostWasCreated() {
// ... check status, label, class
casper.waitForSelector('.js-publish-splitbutton .js-publish-button:not([disabled])', function onSuccess() {
test.assertExists('.js-publish-button.btn-blue', 'Publish button should have .btn-blue');
test.assertSelectorHasText('.js-publish-button', 'Save Draft', '.js-publish-button says Save Draft');
}, function onTimeout() {
test.assert(false, 'Publish split button works');
});
});
});
CasperTest.begin('Publish menu - delete post', 6, function testDeleteModal(test) {
// Create a post that can be deleted
CasperTest.Routines.createTestPost.run(false);
// Begin test
casper.thenOpenAndWaitForPageLoad('content', function testTitleAndUrl() {
test.assertTitle('Content - Test Blog', 'Title is "Content - Test Blog"');
test.assertUrlMatch(/ghost\/\d+\/$/, 'Landed on the correct URL');
});
// Transition to the editor
casper.thenClick('.post-edit');
casper.waitForSelector('#entry-title');
// Open post settings menu
casper.thenClick('.js-publish-splitbutton .dropdown-toggle');
casper.waitForOpaque('.js-publish-splitbutton .dropdown-menu');
casper.thenClick('.js-publish-splitbutton li:nth-child(4) a');
casper.waitUntilVisible('.modal-container', function onSuccess() {
test.assertSelectorHasText(
'.modal-content .modal-header',
'Are you sure you want to delete this post?',
'delete modal has correct text');
});
casper.thenClick('.js-button-reject');
casper.waitWhileVisible('.modal-container', function onSuccess() {
test.assert(true, 'clicking cancel should close the delete post modal');
});
// Test delete
casper.thenClick('.js-publish-splitbutton .dropdown-toggle');
casper.waitForOpaque('.js-publish-splitbutton .dropdown-menu');
casper.thenClick('.js-publish-splitbutton li:nth-child(4) a');
casper.waitForSelector('.modal-container .modal-content', function onSuccess() {
test.assertExists('.modal-content .js-button-accept', 'delete button exists');
// Delete the post
this.click('.modal-content .js-button-accept');
casper.waitWhileVisible('.modal-container', function onSuccess() {
test.assert(true, 'clicking delete button should close the delete post modal');
});
});
});
CasperTest.begin('Publish menu - new post status is correct after failed save', 2, function suite(test) {
casper.thenOpenAndWaitForPageLoad('editor', function testTitleAndUrl() {
test.assertTitle('Editor - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/editor\/$/, 'Landed on the correct URL');
});
// Fill title and content
casper.then(function writePost() {
casper.sendKeys('#entry-title', new Array(160).join('x'));
});
casper.then(function switchMenuToPublish() {
// Open the publish options menu;
casper.thenClick('.js-publish-splitbutton .dropdown-toggle');
casper.waitForOpaque('.js-publish-splitbutton .dropdown-menu');
// Select the publish post button
casper.thenClick('.js-publish-splitbutton li:first-child a');
});
// attempt to save
casper.thenClick('.js-publish-button');
// Click on "Content" in the main nav
casper.thenClick('.gh-nav-main-content');
// The "Are you sure?" modal appears
casper.waitUntilVisible('.modal-content', function onSuccess() {
casper.thenClick('.btn-red');
}, function onTimeout() {
test.assert(false, 'Are you sure you want to leave modal did not appear.');
});
});
CasperTest.begin('Publish menu - existing post status is correct after failed save', 6, function suite(test) {
casper.thenOpenAndWaitForPageLoad('editor', function testTitleAndUrl() {
test.assertTitle('Editor - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/editor\/$/, 'Landed on the correct URL');
});
// Fill title and content
casper.then(function writePost() {
casper.sendKeys('#entry-title', 'a valid title');
casper.writeContentToEditor('body content');
});
// save
casper.thenClick('.js-publish-button');
casper.waitForSelector('.gh-notification');
casper.then(function updateTitle() {
casper.sendKeys('#entry-title', new Array(160).join('y'));
});
casper.then(function switchMenuToPublish() {
// Open the publish options menu;
casper.thenClick('.js-publish-splitbutton .dropdown-toggle');
casper.waitForOpaque('.js-publish-splitbutton .dropdown-menu');
// Select the publish post button
casper.thenClick('.js-publish-splitbutton li:first-child a');
// ... check status, label, class
casper.waitForSelector('.js-publish-splitbutton .js-publish-button:not([disabled])', function onSuccess() {
test.assertExists('.js-publish-button.btn-red', 'Publish button should have .btn-red');
test.assertSelectorHasText('.js-publish-button', 'Publish Now', '.js-publish-button says Publish Now');
}, function onTimeout() {
test.assert(false, 'Publish split button works');
});
});
// attempt to save
casper.thenClick('.js-publish-button');
// ... check status, label, class
casper.waitForSelector('.gh-alert-red', function onSuccess() {
test.assertExists('.js-publish-button.btn-blue', 'Update button should have .btn-blue');
// wait for button to settle
casper.wait(1100);
casper.then(function () {
test.assertSelectorHasText('.js-publish-button', 'Save Draft', '.js-publish-button says Save Draft');
});
}, function onTimeout() {
test.assert(false, 'Saving post with invalid title should trigger an error');
});
});
// test the markdown help modal
CasperTest.begin('Markdown help modal', 5, function suite(test) {
casper.thenOpenAndWaitForPageLoad('editor', function testTitleAndUrl() {
test.assertTitle('Editor - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/editor\/$/, 'Landed on the correct URL');
});
// open markdown help modal
casper.thenClick('a.markdown-help');
casper.waitUntilVisible('.modal-container', function onSuccess() {
test.assertSelectorHasText(
'.modal-content .modal-header',
'Markdown Help',
'delete modal has correct text');
test.assertExists('.modal-content .close', '.modal-content .close exists');
});
casper.thenClick('.modal-content .close');
casper.waitWhileVisible('.modal-container', function onSuccess() {
test.assert(true, 'clicking close should remove the markdown help modal');
});
});
// test editor title input is correct after changing a post attribute in the post-settings-menu
CasperTest.begin('Title input is set correctly after using the Post-Settings-Menu', function suite(test) {
casper.thenOpenAndWaitForPageLoad('editor', function testTitleAndUrl() {
test.assertTitle('Editor - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/editor\/$/, 'Landed on the correct URL');
});
// add a new post
casper.then(function fillContent() {
casper.sendKeys('#entry-title', 'post title', {reset: true});
casper.writeContentToEditor('Just a bit of test text');
});
// save draft
casper.thenClick('.js-publish-button');
casper.waitForSelector('.gh-notification');
// change the title
casper.then(function updateTitle() {
casper.sendKeys('#entry-title', 'changed post title', {reset: true});
casper.click('#entry-markdown-content');
});
// change a post attribute via the post-settings-menu
casper.thenClick('.post-settings');
casper.then(function () {
this.fillSelectors('.settings-menu form', {
'#url': 'changed-slug'
}, false);
this.click('.post-settings');
});
casper.waitForResource(/\/posts\/\d+\/\?include=tags/, function testGoodResponse(resource) {
test.assert(resource.status < 400);
});
casper.then(function checkTitleInput() {
test.assertEvalEquals(function () {
return $('#entry-title').val();
}, 'changed post title', 'Title input should match expected value.');
});
});
// test editor content input is correct after changing a post attribute in the post-settings-menu
CasperTest.begin('Editor content is set correctly after using the Post-Settings-Menu', function suite(test) {
casper.thenOpenAndWaitForPageLoad('editor', function testTitleAndUrl() {
test.assertTitle('Editor - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/editor\/$/, 'Landed on the correct URL');
});
// add a new post
casper.then(function fillContent() {
casper.sendKeys('#entry-title', 'post title');
casper.writeContentToEditor('Just a bit of test text');
});
// save draft
casper.thenClick('.js-publish-button');
casper.waitForSelector('.gh-notification');
// change the content
casper.then(function updateContent() {
casper.writeContentToEditor('updated content');
casper.click('#entry-title');
});
// change a post attribute via the post-settings-menu
casper.thenClick('.post-settings');
casper.then(function () {
this.fillSelectors('.settings-menu form', {
'#url': 'changed-slug-after-update'
}, false);
this.click('.post-settings');
});
casper.waitForResource(/\/posts\/\d+\/\?include=tags/, function testGoodResponse(resource) {
test.assert(resource.status < 400);
});
casper.waitForSelectorTextChange('.entry-preview .rendered-markdown', function onSuccess() {
test.assertSelectorHasText(
'.entry-preview .rendered-markdown',
'updated content',
'Editor has correct content.'
);
}, casper.failOnTimeout(test, 'markdown was not available'));
});

View File

@ -1,259 +0,0 @@
// # Post Settings Menu Tests
// Test the post settings menu on the editor screen works as expected
/*globals CasperTest, casper, __utils__ */
CasperTest.begin('Post settings menu', 8, function suite(test) {
casper.thenOpenAndWaitForPageLoad('editor', function testTitleAndUrl() {
test.assertTitle('Editor - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/editor\/$/, 'Landed on the correct URL');
});
casper.then(function () {
test.assertExists('.post-settings', 'icon toggle should exist');
test.assertExists('.settings-menu', 'popup menu should be rendered at startup');
test.assertExists('.settings-menu #url', 'url field exists');
test.assertExists('.settings-menu .post-setting-date', 'publication date field exists');
test.assertExists('.settings-menu .post-setting-static-page', 'static page checkbox field exists');
});
// Enter a title and save draft so converting to/from static post
// will result in notifications and 'Delete This Post' button appears
casper.then(function () {
casper.sendKeys('#entry-title', 'aTitle');
casper.thenClick('.js-publish-button');
});
casper.thenClick('.post-settings');
casper.waitForOpaque('.settings-menu', function onSuccess() {
test.assert(true, 'post settings menu should be visible after clicking post-settings icon');
});
});
CasperTest.begin('Post url can be changed', 4, function suite(test) {
// Create a sample post
CasperTest.Routines.createTestPost.run(false);
// Begin test
casper.thenOpenAndWaitForPageLoad('content', function testTitleAndUrl() {
test.assertTitle('Content - Test Blog', 'Title is "Content - Test Blog"');
test.assertUrlMatch(/ghost\/\d+\/$/, 'Landed on the correct URL');
});
// Transition to the editor
casper.thenClick('.post-edit');
casper.waitForSelector('#entry-title');
casper.thenClick('.post-settings');
// Test change permalink
casper.then(function () {
this.fillSelectors('.settings-menu form', {
'#url': 'new-url'
}, false);
this.click('.post-settings');
});
casper.waitForResource(/\/posts\/\d+\/\?include=tags/, function testGoodResponse(resource) {
test.assert(resource.status < 400, 'resource.status < 400');
});
casper.then(function checkValueMatches() {
// using assertField(name) checks the htmls initial "value" attribute, so have to hack around it.
var slugVal = this.evaluate(function () {
return __utils__.getFieldValue('post-setting-slug');
});
test.assertEquals(slugVal, 'new-url', 'slug has correct value');
});
});
CasperTest.begin('Post published date can be changed', 4, function suite(test) {
// Create a sample post
CasperTest.Routines.createTestPost.run(false);
// Begin test
casper.thenOpenAndWaitForPageLoad('content', function testTitleAndUrl() {
test.assertTitle('Content - Test Blog', 'Title is "Content - Test Blog"');
test.assertUrlMatch(/ghost\/\d+\/$/, 'Landed on the correct URL');
});
// Transition to the editor
casper.thenClick('.post-edit');
casper.waitForSelector('#entry-title');
casper.thenClick('.post-settings');
// Test change published date
casper.then(function () {
this.fillSelectors('.settings-menu form', {
'.post-setting-date': '22 May 14 @ 23:39'
}, false);
this.click('.post-settings');
});
casper.waitForResource(/\/posts\/\d+\/\?include=tags/, function testGoodResponse(resource) {
test.assert(resource.status < 400, 'resource.status < 400');
});
casper.then(function checkValueMatches() {
// using assertField(name) checks the htmls initial "value" attribute, so have to hack around it.
var dateVal = this.evaluate(function () {
return __utils__.getFieldValue('post-setting-date');
});
test.assertEquals(dateVal, '22 May 14 @ 23:39', 'date is correct');
});
});
CasperTest.begin('Post can be changed to static page', 2, function suite(test) {
// Create a sample post
CasperTest.Routines.createTestPost.run(false);
// Begin test
casper.thenOpenAndWaitForPageLoad('content', function testTitleAndUrl() {
test.assertTitle('Content - Test Blog', 'Title is "Content - Test Blog"');
test.assertUrlMatch(/ghost\/\d+\/$/, 'Landed on the correct URL');
});
// Transition to the editor
casper.thenClick('.post-edit');
casper.waitForSelector('#entry-title');
casper.thenClick('.post-settings');
casper.thenClick('label[for=static-page]');
casper.waitForSelector('.post-setting-static-page:checked', function onSuccess() {
casper.click('label[for=static-page]');
}, function onTimeout() {
casper.test.fail('Post was not changed to static page.');
}, 2000);
casper.waitForSelector('.post-setting-static-page:not(checked)', function onSuccess() {
return;
}, function onTimeout() {
casper.test.fail('Static page was not changed to post.');
}, 2000);
});
CasperTest.begin('Post url input is reset from all whitespace back to original value', 4, function suite(test) {
// Create a sample post
CasperTest.Routines.createTestPost.run(false);
// Begin test
casper.thenOpenAndWaitForPageLoad('content', function testTitleAndUrl() {
test.assertTitle('Content - Test Blog', 'Title is "Content - Test Blog"');
test.assertUrlMatch(/ghost\/\d+\/$/, 'Landed on the correct URL');
});
// Transition to the editor
casper.thenClick('.post-edit');
casper.waitForSelector('#entry-title');
casper.thenClick('.post-settings');
var originalSlug;
casper.then(function () {
originalSlug = casper.evaluate(function () {
return __utils__.getFieldValue('post-setting-slug');
});
});
// Test change permalink
casper.then(function () {
this.fillSelectors('.settings-menu form', {
'#url': ' '
}, false);
});
// Click in a different field
casper.thenClick('#post-setting-date');
casper.then(function checkValueMatches() {
// using assertField(name) checks the htmls initial "value" attribute, so have to hack around it.
var slugVal = this.evaluate(function () {
return __utils__.getFieldValue('post-setting-slug');
});
test.assertNotEquals(slugVal, ' ', 'slug is not just spaces');
test.assertEquals(slugVal, originalSlug, 'slug gets reset to original value');
});
});
CasperTest.begin('Tag Editor', 9, function suite(test) {
var testTag = 'Test1',
createdTag = '#tag-input + .selectize-control .item',
tagInput = '#tag-input + .selectize-control input[type="text"]';
casper.thenOpenAndWaitForPageLoad('editor', function testTitleAndUrl() {
test.assertTitle('Editor - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/editor\/$/, 'Landed on the correct URL');
});
casper.then(function () {
test.assertExists('#tag-input + .selectize-control', 'should have tag list area');
});
casper.thenClick(tagInput);
casper.then(function () {
casper.sendKeys(tagInput, testTag, {keepFocus: true});
});
casper.then(function () {
casper.sendKeys(tagInput, casper.page.event.key.Enter, {keepFocus: true});
});
casper.waitForSelector(createdTag, function onSuccess() {
test.assertSelectorHasText(createdTag, testTag, 'typing enter after tag name should create tag');
});
casper.thenClick(createdTag + ' a.remove');
casper.waitWhileSelector(createdTag, function onSuccess() {
test.assert(true, 'clicking the tag remove button should delete the tag');
});
casper.then(function () {
casper.sendKeys(tagInput, testTag, {keepFocus: true});
});
casper.then(function () {
casper.sendKeys(tagInput, casper.page.event.key.Tab, {keepFocus: true});
});
casper.waitForSelector(createdTag, function onSuccess() {
test.assertSelectorHasText(createdTag, testTag, 'typing tab after tag name should create tag');
});
casper.then(function () {
casper.sendKeys(tagInput, casper.page.event.key.Backspace, {keepFocus: true});
});
casper.waitWhileSelector(createdTag, function onSuccess() {
test.assert(true, 'hitting backspace should delete the last tag');
});
casper.then(function () {
casper.sendKeys(tagInput, testTag, {keepFocus: true});
});
casper.then(function () {
casper.sendKeys(tagInput, casper.page.event.key.Enter, {keepFocus: true});
});
casper.thenClick(createdTag);
casper.waitForSelector(createdTag + '.active', function onSuccess() {
test.assert(true, 'clicking a tag should highlight it');
});
casper.then(function () {
casper.sendKeys(createdTag + '.active', casper.page.event.key.Backspace);
});
casper.waitWhileSelector(createdTag + '.active', function onSuccess() {
test.assert(true, 'hitting backspace on a higlighted tag should delete it');
});
// TODO: add this back in if create-on-blur functionality is required
// casper.then(function () {
// casper.sendKeys(tagInput, testTag, {keepFocus: true});
// });
// // Click in a different field
// casper.thenClick('#post-setting-date');
// casper.waitForSelector(createdTag, function onSuccess() {
// test.assertSelectorHasText(createdTag, testTag, 'de-focusing from tag input should create tag with leftover text');
// });
});

View File

@ -1,134 +0,0 @@
// # Settings Test
// Test the various tabs on the settings page
/*globals CasperTest, casper */
// These classes relate to elements which only appear when a given tab is loaded.
// These are used to check that a switch to a tab is complete, or that we are on the right tab.
var generalTabDetector = '.gh-nav-settings-general.active';
CasperTest.begin('Settings screen is correct', 5, function suite(test) {
casper.thenOpenAndWaitForPageLoad('settings.general', function testTitleAndUrl() {
test.assertTitle('Settings - General - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/settings\/general\/$/, 'Landed on the correct URL');
});
casper.then(function testViews() {
test.assertExists('.gh-main .gh-view', 'Settings content view is present');
test.assertExists(generalTabDetector, 'Form is present');
test.assertSelectorHasText('.view-title', 'General', 'Title is "General"');
});
});
// ## General settings tests
CasperTest.begin('General settings pane is correct', 4, function suite(test) {
casper.thenOpenAndWaitForPageLoad('settings.general', function testTitleAndUrl() {
test.assertUrlMatch(/ghost\/settings\/general\/$/, 'Landed on the correct URL');
});
function assertImageUploaderModalThenClose() {
test.assertSelectorHasText('.description', 'Add image', '.description has the correct text');
casper.click('.modal-container .js-button-accept');
}
// Ensure image upload modals display correctly
// Test Blog Logo Upload Button
casper.waitForSelector('.js-modal-logo', function () {
casper.click('.js-modal-logo');
});
casper.waitForSelector('.modal-container .modal-content .js-drop-zone .description',
assertImageUploaderModalThenClose, casper.failOnTimeout(test, 'No upload logo modal container appeared'));
// Test Blog Cover Upload Button
casper.waitForSelector('.js-modal-cover', function () {
casper.click('.js-modal-cover');
});
casper.waitForSelector('.modal-container .modal-content .js-drop-zone .description',
assertImageUploaderModalThenClose, casper.failOnTimeout(test, 'No upload cover modal container appeared'));
function handleSettingsRequest(requestData) {
// make sure we only get requests from the user pane
if (requestData.url.indexOf('users/') !== -1) {
test.fail('Saving a settings pane triggered the user pane to save');
}
}
casper.then(function listenForRequests() {
casper.on('resource.requested', handleSettingsRequest);
});
// Ensure can save
casper.waitForSelector('header .btn-blue').then(function () {
casper.thenClick('header .btn-blue');
casper.waitForResource('settings/', function onSuccess() {
test.assert(true, 'Settings were saved');
}, function doneWaiting() {
test.fail('Settings were not saved');
});
});
casper.then(function stopListeningForRequests() {
casper.removeListener('resource.requested', handleSettingsRequest);
});
});
// ## General settings validations tests
CasperTest.begin('General settings validation is correct', 7, function suite(test) {
casper.thenOpenAndWaitForPageLoad('settings.general', function testTitleAndUrl() {
test.assertTitle('Settings - General - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/settings\/general\/$/, 'Landed on the correct URL');
});
// Ensure general blog title field length validation
casper.fillAndSave('form#settings-general', {
'general[title]': new Array(152).join('a')
});
casper.waitForText('Title is too long', function onSuccess() {
test.assert(true, 'Blog title length error was shown');
}, casper.failOnTimeout(test, 'Blog title length error did not appear'));
// Ensure general blog description field length validation
casper.fillAndSave('form#settings-general', {
'general[description]': new Array(202).join('a')
});
casper.waitForText('Description is too long', function onSuccess() {
test.assert(true, 'Blog description length error was shown');
}, casper.failOnTimeout(test, 'Blog description length error did not appear'));
// TODO move these to ember tests, note: async issues - field will be often be null without a casper.wait
// Check postsPerPage autocorrect
casper.fillAndSave('form#settings-general', {
'general[postsPerPage]': 'notaninteger'
});
casper.wait(2000);
casper.then(function checkSlugInputValue() {
test.assertField('general[postsPerPage]', '5', 'posts per page is set correctly');
});
casper.fillAndSave('form#settings-general', {
'general[postsPerPage]': '1001'
});
casper.wait(2000);
casper.then(function checkSlugInputValue() {
test.assertField('general[postsPerPage]', '5', 'posts per page is set correctly');
});
// Ensure private blog password validation
casper.fillAndSave('form#settings-general', {
'general[isPrivate]': '1',
'general[password]': ''
});
casper.waitForText('Password must be supplied', function onSuccess() {
test.assert(true, 'Password required error was shown');
}, casper.failOnTimeout(test, 'Password required error did not appear'));
});

View File

@ -1,246 +0,0 @@
// # Signin Test
// Test that signin works, including testing our spam prevention mechanisms
/*globals CasperTest, casper, url, user, falseUser */
CasperTest.begin('Ghost admin will load login page', 4, function suite(test) {
CasperTest.Routines.signout.run(test);
casper.thenOpenAndWaitForPageLoad('signin', function testTitleAndUrl() {
test.assertTitle('Sign In - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/signin\/$/, 'We should be presented with the signin page.');
casper.then(function testLink() {
var text = this.evaluate(function (selector) {
return document.querySelector(selector).innerText;
}, '.forgotten-link');
casper.echoConcise('Text' + text);
test.assertExists('.forgotten-link', '.forgotten-link exists');
test.assertEquals(text, 'Forgot?', 'Forgotten text is correct');
});
});
}, true);
// Note, this test applies to a global redirect, which sends us to the standard admin.
CasperTest.begin('Redirects login to signin', 2, function suite(test) {
CasperTest.Routines.signout.run(test);
casper.start(url + 'ghost/login/', function testRedirect(response) {
test.assertEquals(response.status, 200, 'Response status should be 200.');
test.assertUrlMatch(/ghost\/signin\//, 'Should be redirected to /signin/.');
});
}, true);
CasperTest.begin('Login limit is in place', 7, function suite(test) {
CasperTest.Routines.signout.run(test);
casper.thenOpenAndWaitForPageLoad('signin', function testTitleAndUrl() {
test.assertTitle('Sign In - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/signin\/$/, 'Landed on the correct URL');
});
casper.waitForOpaque('.gh-signin',
function then() {
this.fillAndSave('#login', falseUser);
},
function onTimeout() {
test.fail('Sign in form didn\'t fade in.');
});
casper.wait(2100, function doneWait() {
this.fillAndSave('#login', falseUser);
});
casper.waitForText('remaining', function onSuccess() {
test.assert(true, 'The login limit is in place.');
test.assertSelectorDoesntHaveText('.gh-alert', '[object Object]');
test.assertTextExists('password is incorrect');
test.assertDoesntExist('.form-group.error input[name="identification"]', 'email field was highlighted');
test.assertExists('.form-group.error input[name="password"]', 'password field was not highlighted');
}, function onTimeout() {
test.assert(false, 'We did not trip the login limit.');
});
}, true);
CasperTest.begin('Can login to Ghost', 4, function suite(test) {
CasperTest.Routines.signout.run(test);
casper.thenOpenAndWaitForPageLoad('signin', function testTitleAndUrl() {
test.assertTitle('Sign In - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/signin\/$/, 'Landed on the correct URL');
});
casper.waitForOpaque('.gh-signin', function then() {
this.fillAndSave('#login', user);
});
casper.wait(2000);
casper.waitForResource(/posts/, function testForDashboard() {
test.assertUrlMatch(/ghost\/\d+\/$/, 'Landed on the correct URL');
test.assertExists('.gh-nav-main-content.active', 'Now we are on Content');
}, function onTimeOut() {
test.fail('Failed to signin');
});
}, true);
CasperTest.begin('Authenticated user is redirected', 6, function suite(test) {
CasperTest.Routines.signout.run(test);
casper.thenOpenAndWaitForPageLoad('signin', function testTitleAndUrl() {
test.assertTitle('Sign In - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/signin\/$/, 'Landed on the correct URL');
});
casper.waitForOpaque('' +
'.gh-signin', function then() {
this.fillAndSave('#login', user);
});
casper.wait(2000);
casper.waitForResource(/posts/, function testForDashboard() {
test.assertUrlMatch(/ghost\/\d+\/$/, 'Landed on the correct URL');
test.assertExists('.gh-nav-main-content.active', 'Now we are on Content');
}, function onTimeOut() {
test.fail('Failed to signin');
});
casper.thenOpenAndWaitForPageLoad('signin-authenticated', function testTitleAndUrl() {
test.assertUrlMatch(/ghost\/\d+\/$/, 'Landed on the correct URL');
test.assertExists('.gh-nav-main-content.active', 'Now we are on Content');
}, function onTimeOut() {
test.fail('Failed to redirect');
});
}, true);
CasperTest.begin('Validates unknown email for sign-in', 5, function suite(test) {
CasperTest.Routines.signout.run(test);
casper.thenOpenAndWaitForPageLoad('signin', function testTitleAndUrl() {
test.assertTitle('Sign In - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/signin\/$/, 'Landed on the correct URL');
});
casper.waitForOpaque('.gh-signin',
function testUnknownEmail() {
this.fillAndSave('form.gh-signin', {
identification: 'unknown@ghost.org',
password: 'testing'
});
},
function onTimeout() {
test.fail('Login form didn\'t fade in.');
});
casper.waitForText('no user with that email address', function onSuccess() {
test.assert(true, 'Unknown email error was shown');
test.assertExists('.form-group.error input[name="identification"]', 'email field was not highlighted');
test.assertDoesntExist('.form-group.error input[name="password"]', 'password field was highlighted');
}, casper.failOnTimeout(test, 'Unknown email error was not shown'));
}, true);
CasperTest.begin('Validates missing details for sign-in', 11, function suite(test) {
CasperTest.Routines.signout.run(test);
casper.thenOpenAndWaitForPageLoad('signin', function testTitleAndUrl() {
test.assertTitle('Sign In - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/signin\/$/, 'Landed on the correct URL');
});
casper.waitForOpaque('.gh-signin',
function testMissingEverything() {
this.fillAndSave('form.gh-signin', {
identification: '',
password: ''
});
},
function onTimeout() {
test.fail('Login form didn\'t fade in.');
});
casper.waitForText('fill out the form', function onSuccess() {
test.assert(true, 'Missing details error was shown');
test.assertExists('.form-group.error input[name="identification"]', 'email field was not highlighted');
test.assertExists('.form-group.error input[name="password"]', 'password field was not highlighted');
}, casper.failOnTimeout(test, 'Missing details error was not shown'));
casper.then(function testMissingEmail() {
this.fillAndSave('form.gh-signin', {
identification: '',
password: 'testing'
});
});
casper.waitForText('fill out the form', function onSuccess() {
test.assert(true, 'Missing details error was shown');
test.assertExists('.form-group.error input[name="identification"]', 'email field was not highlighted');
test.assertDoesntExist('.form-group.error input[name="password"]', 'password field was still highlighted');
}, casper.failOnTimeout(test, 'Missing details error was not shown'));
casper.then(function testMissingPassword() {
this.fillAndSave('form.gh-signin', {
identification: 'test@test.com',
password: ''
});
});
casper.waitForText('fill out the form', function onSuccess() {
test.assert(true, 'Missing details error was shown');
test.assertDoesntExist('.form-group.error input[name="identification"]', 'email field was still highlighted');
test.assertExists('.form-group.error input[name="password"]', 'password field was not highlighted');
}, casper.failOnTimeout(test, 'Missing details error was not shown'));
}, true);
CasperTest.begin('Validates missing details for forgotten password', 5, function suite(test) {
CasperTest.Routines.signout.run(test);
casper.thenOpenAndWaitForPageLoad('signin', function testTitleAndUrl() {
test.assertTitle('Sign In - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/signin\/$/, 'Landed on the correct URL');
});
casper.waitForOpaque('.gh-signin',
function testMissingEmail() {
casper.fill('form.gh-signin', {
identification: '',
password: ''
});
casper.click('.forgotten-link');
},
function onTimeout() {
test.fail('Login form didn\'t fade in.');
});
casper.waitForText('enter an email address', function onSuccess() {
test.assert(true, 'Missing email error was shown');
test.assertExists('.form-group.error input[name="identification"]', 'email field was not highlighted');
test.assertDoesntExist('.form-group.error input[name="password"]', 'password field was highlighted');
}, casper.failOnTimeout(test, 'Missing email error was not shown'));
}, true);
CasperTest.begin('Validates unknown email for forgotten password', 5, function suite(test) {
CasperTest.Routines.signout.run(test);
casper.thenOpenAndWaitForPageLoad('signin', function testTitleAndUrl() {
test.assertTitle('Sign In - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/signin\/$/, 'Landed on the correct URL');
});
casper.waitForOpaque('.gh-signin',
function testMissingEmail() {
casper.fill('form.gh-signin', {
identification: 'unknown@ghost.org',
password: ''
});
casper.click('.forgotten-link');
},
function onTimeout() {
test.fail('Login form didn\'t fade in.');
});
casper.waitForText('no user with that email address', function onSuccess() {
test.assert(true, 'Unknown email error was shown');
test.assertExists('.form-group.error input[name="identification"]', 'email field was not highlighted');
test.assertDoesntExist('.form-group.error input[name="password"]', 'password field was highlighted');
}, casper.failOnTimeout(test, 'Unknown email error was not shown'));
}, true);

View File

@ -1,32 +0,0 @@
// # Signout Test
// Test that signout works correctly
/*globals CasperTest, casper */
CasperTest.begin('Ghost signout works correctly', 3, function suite(test) {
CasperTest.Routines.signout.run(test);
CasperTest.Routines.signin.run(test);
casper.thenOpenAndWaitForPageLoad('root', function then() {
test.assertTitle('Content - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/\d+\/$/, 'Landed on the correct URL without signing in');
});
casper.thenClick('.gh-nav-menu').waitFor(function checkOpaque() {
return this.evaluate(function () {
var menu = document.querySelector('.gh-nav-menu.open');
return window.getComputedStyle(menu).getPropertyValue('display') === 'block' &&
window.getComputedStyle(menu).getPropertyValue('opacity') === '1';
});
});
casper.captureScreenshot('user-menu-open.png');
casper.waitForSelector('.user-menu-signout');
casper.thenClick('.user-menu-signout');
casper.waitForSelector('#login').then(function assertSuccess() {
test.assert(true, 'Got login screen');
});
casper.captureScreenshot('user-menu-logout-clicked.png');
}, true);

View File

@ -1,36 +0,0 @@
// # Signup Test
// Test that signup works correctly
/*globals CasperTest */
CasperTest.begin('Ghost signup fails properly', 0, function suite(test) {
/*jshint unused:false */
// casper.thenOpenAndWaitForPageLoad('signup', function then() {
// test.assertUrlMatch(/ghost\/signup\/$/, 'Landed on the correct URL');
// });
// casper.then(function signupWithShortPassword() {
// casper.fillAndSave('#signup', {email: email, password: 'test'});
// });
// // should now throw a short password error
// 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() {
// casper.fillAndSave('#signup', {email: email, password: 'testing1234'});
// });
// // should now throw a 1 user only error
// 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 :(');
// });
}, true);

View File

@ -1,126 +0,0 @@
// # Settings Test
// Test the various tabs on the settings page
/*
NOTE: These tests have been replaced with:
core/client/tests/acceptance/settings/tags-test.js
core/client/tests/integration/components/gh-tag-settings-form-test.js
*/
// CasperTest.begin('Tags screen is correct', 6, function suite(test) {
// casper.thenOpenAndWaitForPageLoad('settings.tags', function testTitleAndUrl() {
// test.assertTitle('Settings - Tags - Test Blog', 'Ghost admin has incorrect title');
// test.assertUrlMatch(/ghost\/settings\/tags\/$/, 'Landed on the correct URL');
// });
//
// casper.then(function tagsScreenHasContent() {
// test.assertExists('.settings-tags .settings-tag', 'Has a tag');
// test.assertSelectorHasText('.settings-tag .tag-title', 'Getting Started', 'Tag title is displayed');
// test.assertSelectorHasText('.settings-tag .label', '/getting-started', 'Tag slug is displayed');
// test.assertSelectorHasText('.settings-tag .tags-count', '1', 'Number of posts using tag is displayed');
// });
// });
//
// CasperTest.begin('Tag creation', 16, function suite(test) {
// casper.thenOpenAndWaitForPageLoad('settings.tags');
//
// casper.thenClick('.view-actions .btn-green');
//
// casper.waitForOpaque('.tag-settings-pane', function onSuccess() {
// test.assert(true, 'tag settings menu is visible after clicking New Tag button');
// });
//
// casper.then(function enterName() {
// casper.sendKeys('#tag-name', 'Test Tag', {keepFocus: true});
// });
// casper.thenClick('#tag-description');
// casper.waitForResource(/\/tags\//, function onSuccess() {
// test.assert(true, 'Losing focus on the name field triggered a save request');
// }, function doneWaiting() {
// test.fail('Name field did not trigger a save request on blur');
// });
//
// casper.waitForText('/test-tag').then(function verifyUIUpdates() {
// test.assertField('url', 'test-tag');
// test.assertSelectorHasText('.ghost-url-preview', '127.0.0.1:2369/tag/test-tag');
// test.assertSelectorHasText('.settings-tags .tag-title', 'Test Tag');
// test.assertSelectorHasText('.settings-tags .label', '/test-tag');
// test.assertSelectorHasText('.settings-tags .tags-count', '0');
// });
//
// casper.then(function testMissingNameValidation() {
// casper.fill('.tag-settings-pane form', {
// name: ''
// });
// casper.waitForText('You must specify a name for the tag.', function onSuccess() {
// test.assertExists('.form-group.error input[name="name"]');
// test.assert(true, 'Error displayed for missing tag name');
// }, function doneWaiting() {
// test.fail('Error not displayed for missing tag name');
// });
// });
//
// casper.then(function testNameStartsWithCommaValidation() {
// casper.fill('.tag-settings-pane form', {
// name: ',, commas'
// });
// casper.waitForText('Tag names can\'t start with commas.', function onSuccess() {
// test.assertExists('.form-group.error input[name="name"]');
// test.assert(true, 'Error displayed for tag name starting with comma');
// }, function doneWaiting() {
// test.fail('Error not displayed for tag name starting with comma');
// });
// });
//
// casper.thenClick('.meta-data-button');
//
// casper.waitForOpaque('.tag-meta-settings-pane', function onSuccess() {
// test.assert(true, 'tags meta settings menu is visible after clicking Meta Data button');
// });
//
// casper.then(function testMetaTitleValidation() {
// casper.fill('.tag-meta-settings-pane form', {
// meta_title: new Array(152).join('a')
// });
// casper.waitForText('Meta Title cannot be longer than 150 characters.', function onSuccess() {
// test.assertExists('.form-group.error input[name="meta_title"]');
// test.assert(true, 'Error displayed when meta title is too long');
// }, function doneWaiting() {
// test.fail('Error not displayed when meta title is too long');
// });
// });
//
// casper.then(function testMetaDescriptionValidation() {
// casper.fill('.tag-meta-settings-pane form', {
// meta_description: new Array(202).join('a')
// });
// casper.waitForText('Meta Description cannot be longer than 200 characters.', function onSuccess() {
// test.assertExists('.form-group.error textarea[name="meta_description"]');
// test.assert(true, 'Error displayed when meta description is too long');
// }, function doneWaiting() {
// test.fail('Error not displayed when meta description is too long');
// });
// });
// });
//
// CasperTest.begin('Tag editing', 3, function suite(test) {
// casper.thenOpenAndWaitForPageLoad('settings.tags');
//
// casper.thenClick('.settings-tags .settings-tag .tag-edit-button');
//
// casper.waitForOpaque('.tag-settings-pane', function onSuccess() {
// test.assert(true, 'tag settings menu is visible after clicking tag');
// });
//
// casper.then(function testNameValidation() {
// casper.fill('.tag-settings-pane form', {
// name: ''
// });
// casper.waitForText('You must specify a name for the tag.', function onSuccess() {
// test.assertExists('.form-group.error input[name="name"]');
// test.assert(true, 'Error displayed for missing tag name');
// }, function doneWaiting() {
// test.fail('Error not displayed for missing tag name');
// });
// });
// });

View File

@ -1,316 +0,0 @@
// # Settings Test
/*globals CasperTest, casper */
// These classes relate to elements which only appear when a given tab is loaded.
// These are used to check that a switch to a tab is complete, or that we are on the right tab.
var generalTabDetector = '.gh-nav-settings-general.active',
usersTabDetector = '.gh-nav-main-users.active';
CasperTest.begin('Team tab is correct', 4, function suite(test) {
// TODO make sure settings nav tests are refactored into app_test.js
casper.then(function testSwitchingTabs() {
casper.thenClick('.gh-nav-main-users');
casper.waitForSelector(usersTabDetector, function then() {
// assert that the right menu item is active
test.assert(true, 'Users link is active');
test.assertDoesntExist('.gh-nav-settings-general.active', 'General link is not active');
}, casper.failOnTimeout(test, 'waitForSelector `usersTabDetector` timed out'));
casper.thenClick('.gh-nav-settings-general');
casper.waitForSelector(generalTabDetector, function then() {
// assert that the right menu item is active
test.assert(true, 'General link is active');
test.assertDoesntExist('.gh-nav-main-users.active', 'User link is not active');
}, casper.failOnTimeout(test, 'waitForSelector `generalTabDetector` timed out'));
});
});
CasperTest.begin('Users screen is correct', 9, function suite(test) {
casper.thenOpenAndWaitForPageLoad('settings.general');
casper.thenTransitionAndWaitForScreenLoad('team', function canTransition() {
test.assert(true, 'Can transition to users screen from settings.general');
test.assertUrlMatch(/ghost\/team\/$/, 'team transitions to correct url');
});
casper.then(function usersScreenHasContent() {
test.assertSelectorHasText('.team .user-list .user-list-title', 'Active users', 'active users text is correct');
test.assertExists('.team .user-list .user-list-item', 'Has an active user');
test.assertSelectorHasText('.team .user-list-item .name', 'Test User', 'test user text is correct');
test.assertExists('.team .user-list-item .role-label.owner', 'First user has owner role displayed');
test.assertExists('.view-actions .btn-green', 'Add user button is on page.');
});
casper.thenClick('.view-actions .btn-green');
casper.waitForOpaque('.invite-new-user .modal-content', function then() {
test.assertEval(function testOwnerRoleNotAnOption() {
var options = document.querySelectorAll('.invite-new-user #new-user-role select option'),
i = 0;
for (; i < options.length; i = i + 1) {
if (options[i].text === 'Owner') {
return false;
}
}
return true;
}, '"Owner" is not a role option for new users');
});
// role options get loaded asynchronously; give them a chance to come in
casper.waitForSelector('.invite-new-user #new-user-role select option', function then() {
test.assertEval(function authorIsSelectedByDefault() {
var options = document.querySelectorAll('.invite-new-user #new-user-role select option'),
i = 0;
for (; i < options.length; i = i + 1) {
if (options[i].selected) {
return options[i].text === 'Author';
}
}
return false;
}, 'The "Author" role is selected by default when adding a new user');
});
});
// ### User settings tests
CasperTest.begin('Can save settings', 5, function suite(test) {
casper.thenOpenAndWaitForPageLoad('team.user', function testTitleAndUrl() {
test.assertTitle('Team - User - Test Blog', 'Ghost Admin title is correct');
test.assertUrlMatch(/ghost\/team\/test\/$/, 'team.user has correct URL');
});
function handleUserRequest(requestData) {
// make sure we only get requests from the user pane
if (requestData.url.indexOf('settings/') !== -1) {
test.fail('Saving the user pane triggered another settings pane to save');
}
}
function handleSettingsRequest(requestData) {
// make sure we only get requests from the user pane
if (requestData.url.indexOf('team/') !== -1) {
test.fail('Saving a settings pane triggered the user pane to save');
}
}
casper.then(function listenForRequests() {
casper.on('resource.requested', handleUserRequest);
});
casper.thenClick('.btn-blue');
casper.waitForResource(/\/users\/\d\/\?include=roles/, function onSuccess() {
test.assert(true, 'Saving the user pane triggered a save request');
}, function doneWaiting() {
test.fail('Saving the user pane did not trigger a save request');
});
casper.then(function checkUserWasSaved() {
casper.removeListener('resource.requested', handleUserRequest);
});
casper.thenClick('.gh-nav-settings-general').then(function testTransitionToGeneral() {
casper.waitForSelector(generalTabDetector, function then() {
casper.on('resource.requested', handleSettingsRequest);
test.assertEval(function testGeneralIsActive() {
return document.querySelector('.gh-nav-settings-general').classList.contains('active');
}, 'general tab is marked active');
},
casper.failOnTimeout(test, 'waitForSelector `generalTabDetector` timed out'));
});
casper.thenClick('.btn-blue');
casper.waitForResource(/\/users\/\d\/\?include=roles/, function onSuccess() {
test.assert(true, 'Saving the user pane triggered a save request');
}, function doneWaiting() {
test.fail('Saving the user pane did not trigger a save request');
});
casper.then(function checkSettingsWereSaved() {
casper.removeListener('resource.requested', handleSettingsRequest);
});
CasperTest.beforeDone(function () {
casper.removeListener('resource.requested', handleUserRequest);
casper.removeListener('resource.requested', handleSettingsRequest);
});
});
CasperTest.begin('User settings screen resets all whitespace slug to original value', 3, function suite(test) {
var slug;
casper.thenOpenAndWaitForPageLoad('team.user', function testTitleAndUrl() {
test.assertTitle('Team - User - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/team\/test\/$/, 'Ghost doesn\'t require login this time');
});
casper.then(function getSlugValue() {
slug = this.evaluate(function () {
return document.querySelector('#user-slug').value;
});
});
casper.then(function changeSlugInput() {
casper.fillSelectors('.user-profile', {
'#user-slug': ' '
}, false);
});
casper.thenClick('body');
casper.then(function checkSlugInputValue() {
casper.wait(250);
test.assertField('user', slug, 'user slug is correct');
});
});
CasperTest.begin('User settings screen change slug handles duplicate slug', 4, function suite(test) {
var slug;
casper.thenOpenAndWaitForPageLoad('team.user', function testTitleAndUrl() {
test.assertTitle('Team - User - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/team\/test\/$/, 'Ghost doesn\'t require login this time');
});
casper.then(function getSlugValue() {
slug = this.evaluate(function () {
return document.querySelector('#user-slug').value;
});
});
casper.then(function changeSlug() {
casper.fillSelectors('.user-profile', {
'#user-slug': slug + '!'
}, false);
});
casper.thenClick('body');
casper.waitForResource(/\/slugs\/user\//, function testGoodResponse(resource) {
test.assert(resource.status < 400, 'resource.status < 400');
});
casper.then(function checkSlugInputValue() {
test.assertField('user', slug, 'user slug is correct');
});
});
CasperTest.begin('User settings screen validates email', 4, function suite(test) {
var email;
casper.thenOpenAndWaitForPageLoad('team.user', function testTitleAndUrl() {
test.assertTitle('Team - User - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/team\/test\/$/, 'Ghost doesn\'t require login this time');
});
casper.then(function getEmail() {
email = this.evaluate(function () {
return document.querySelector('#user-email').value;
});
});
casper.then(function setEmailToInvalid() {
var brokenEmail = email.replace('.', '-');
this.fillAndSave('.user-profile', {
email: brokenEmail
});
});
casper.waitForText('Please supply a valid email address', function onSuccess() {
test.assert(true, 'Invalid email error was shown');
}, casper.failOnTimeout(test, 'Invalid email error was not shown'));
casper.then(function resetEmailToValid() {
casper.fillSelectors('.user-profile', {
'#user-email': email
}, false);
});
casper.then(function checkEmailErrorWasCleared() {
test.assertTextDoesntExist('Please supply a valid email address', 'Invalid email error was not cleared');
});
casper.thenClick('.view-actions .btn-blue');
casper.waitForResource(/users/);
});
// TODO: user needs to be loaded whenever it is edited (multi user)
CasperTest.begin('User settings screen shows remaining characters for Bio properly', 4, function suite(test) {
casper.thenOpenAndWaitForPageLoad('team.user', function testTitleAndUrl() {
test.assertTitle('Team - User - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/team\/test\/$/, 'Ghost doesn\'t require login this time');
});
function getRemainingBioCharacterCount() {
return casper.getHTML('.word-count');
}
casper.then(function checkCharacterCount() {
test.assert(getRemainingBioCharacterCount() === '200', 'Bio remaining characters is 200');
});
casper.then(function setBioToValid() {
casper.fillSelectors('.user-profile', {
'#user-bio': 'asdf\n' // 5 characters
}, false);
});
casper.then(function checkCharacterCount() {
test.assert(getRemainingBioCharacterCount() === '195', 'Bio remaining characters is 195');
});
});
CasperTest.begin('Ensure user bio field length validation', 3, function suite(test) {
casper.thenOpenAndWaitForPageLoad('team.user', function testTitleAndUrl() {
test.assertTitle('Team - User - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/team\/test\/$/, 'Ghost doesn\'t require login this time');
});
casper.then(function setBioToInvalid() {
this.fillSelectors('form.user-profile', {
'#user-bio': new Array(202).join('a')
});
});
casper.thenClick('.view-actions .btn-blue');
casper.waitForText('Bio is too long', function onSuccess() {
test.assert(true, 'Bio too long error was shown');
}, casper.failOnTimeout(test, 'Bio too long error was not shown'));
});
CasperTest.begin('Ensure user url field validation', 3, function suite(test) {
casper.thenOpenAndWaitForPageLoad('team.user', function testTitleAndUrl() {
test.assertTitle('Team - User - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/team\/test\/$/, 'Ghost doesn\'t require login this time');
});
casper.then(function setWebsiteToInvalid() {
this.fillSelectors('form.user-profile', {
'#user-website': 'notaurl'
});
});
casper.thenClick('.view-actions .btn-blue');
casper.waitForText('Website is not a valid url', function onSuccess() {
test.assert(true, 'Website invalid error was shown');
}, casper.failOnTimeout(test, 'Website invalid error was not shown'));
});
CasperTest.begin('Ensure user location field length validation', 3, function suite(test) {
casper.thenOpenAndWaitForPageLoad('team.user', function testTitleAndUrl() {
test.assertTitle('Team - User - Test Blog', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/team\/test\/$/, 'Ghost doesn\'t require login this time');
});
casper.then(function setLocationToInvalid() {
this.fillSelectors('form.user-profile', {
'#user-location': new Array(1002).join('a')
});
});
casper.thenClick('.view-actions .btn-blue');
casper.waitForText('Location is too long', function onSuccess() {
test.assert(true, 'Location too long error was shown');
}, casper.failOnTimeout(test, 'Location too long error was not shown'));
});

View File

@ -1,97 +0,0 @@
// Test that setup works correctly
/*global CasperTest, casper, email, user, password */
CasperTest.begin('Ghost setup fails properly', 11, function suite(test) {
casper.thenOpenAndWaitForPageLoad('setup', function then() {
test.assertUrlMatch(/ghost\/setup\/one\/$/, 'Landed on the correct URL');
});
casper.thenTransitionAndWaitForScreenLoad('setup.two', function setupWithShortPassword() {
casper.fillAndAdd('#setup', {'blog-title': 'ghost', name: 'slimer', email: email, password: 'short'});
});
// should now show a short password error
casper.waitForText('Password must be at least 8 characters long', function onSuccess() {
test.assert(true, 'Short password error was shown');
}, casper.failOnTimeout(test, 'Short password error was not shown'));
casper.then(function setupWithLongPassword() {
casper.fillAndAdd('#setup', {'blog-title': 'ghost', name: 'slimer', email: email, password: password});
});
// This can take quite a long time
casper.wait(5000);
casper.waitForScreenLoad('setup.three', function inviteUsers() {
casper.thenClick('.gh-flow-content .btn');
});
casper.waitForText('No users to invite', function onSuccess() {
test.assert(true, 'Got error message');
test.assertExists('.gh-flow-content .btn-minor', 'Submit button is not minor');
test.assertSelectorHasText('.gh-flow-content .btn', 'No users to invite', 'Submit button has wrong text');
}, function onTimeout() {
test.assert(false, 'No error message for empty invitation list');
});
casper.then(function fillInvalidEmail() {
casper.fill('form.gh-flow-invite', {users: 'test'});
casper.thenClick('.gh-flow-content .btn');
});
casper.waitForText('1 invalid email address', function onSuccess() {
test.assert(true, 'Got invalid email error');
}, casper.failOnTimeout(test, 'Invalid email error not shown'));
casper.then(function fillInvitationForm() {
casper.fill('form.gh-flow-invite', {users: 'test@example.com'});
test.assertSelectorHasText('.gh-flow-content .btn', 'Invite 1 user', 'One invitation button text is incorrect');
test.assertExists('.gh-flow-content .btn-green', 'Submit button is not green');
casper.fill('form.gh-flow-invite', {users: 'test@example.com\ntest2@example.com'});
test.assertSelectorHasText('.gh-flow-content .btn', 'Invite 2 users', 'Two invitations button text is incorrect');
});
casper.thenClick('.gh-flow-content .btn');
// This might take awhile
casper.wait(5000);
// These invitations will fail, because Casper can't send emails
casper.waitForSelector('.gh-alert', function onSuccess() {
test.assert(true, 'Got error notification');
test.assertSelectorHasText('.gh-alert', 'Failed to send 2 invitations: test@example.com, test2@example.com');
}, function onTimeout() {
test.assert(false, 'No error notification after invite.');
});
}, true, true);
CasperTest.begin('Authenticated user is redirected', 6, function suite(test) {
casper.thenOpenAndWaitForPageLoad('signin', function testTitleAndUrl() {
test.assertTitle('Sign In - ghost', 'Ghost admin has incorrect title');
test.assertUrlMatch(/ghost\/signin\/$/, 'Landed on the correct URL');
});
casper.waitForOpaque('.gh-signin', function then() {
this.fillAndSave('#login', user);
});
casper.wait(2000);
casper.waitForResource(/\d+/, function testForDashboard() {
test.assertUrlMatch(/ghost\/\d+\/$/, 'Landed on the correct URL');
test.assertExists('.gh-nav-main-content.active', 'Now we are on Content');
}, function onTimeOut() {
test.fail('Failed to signin');
});
casper.thenOpenAndWaitForPageLoad('setup-authenticated', function testTitleAndUrl() {
test.assertUrlMatch(/ghost\/\d+\/$/, 'Landed on the correct URL');
test.assertExists('.gh-nav-main-content.active', 'Now we are on Content');
}, function onTimeOut() {
test.fail('Failed to redirect');
});
}, true, true);

View File

@ -106,7 +106,6 @@
"greenkeeper": {
"ignore": [
"bower",
"bluebird",
"glob",
"lodash",
"mysql",