mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-11 02:12:21 -05:00
Prevent loading setup screen if already setup
Closes #3145 - Prevent navigation to the setup screen if Ghost setup has previously been completed. - Fix templates that were incorrectly using foreach instead of each. - Add validation for minimum password length. - Fix up functional tests and split out tests for setup to a separate instance of casper because setup requires a new database. - Add a cleanDatabase task to grunt which resets the database to new.
This commit is contained in:
parent
5095c6f0dd
commit
979c3f237c
9 changed files with 78 additions and 31 deletions
31
Gruntfile.js
31
Gruntfile.js
|
@ -449,7 +449,7 @@ var path = require('path'),
|
|||
// This really ought to be refactored into a separate grunt task module
|
||||
grunt.registerTask('spawnCasperJS', function (target) {
|
||||
|
||||
target = _.contains(['client', 'frontend'], target) ? target + '/' : undefined;
|
||||
target = _.contains(['client', 'frontend', 'setup'], target) ? target + '/' : undefined;
|
||||
|
||||
var done = this.async(),
|
||||
options = ['host', 'noPort', 'port', 'email', 'password'],
|
||||
|
@ -550,6 +550,22 @@ var path = require('path'),
|
|||
});
|
||||
});
|
||||
|
||||
// #### Reset Database to "New" state *(Utility Task)*
|
||||
// Drops all database tables and then runs the migration process to put the database
|
||||
// in a "new" state.
|
||||
grunt.registerTask('cleanDatabase', function () {
|
||||
var done = this.async(),
|
||||
migration = require('./core/server/data/migration');
|
||||
|
||||
migration.reset().then(function () {
|
||||
return migration.init();
|
||||
}).then(function () {
|
||||
done();
|
||||
}).catch(function (err) {
|
||||
grunt.fail.fatal(err.stack);
|
||||
});
|
||||
});
|
||||
|
||||
// ### Validate
|
||||
// **Main testing task**
|
||||
//
|
||||
|
@ -637,6 +653,16 @@ var path = require('path'),
|
|||
grunt.registerTask('test-routes', 'Run functional route tests (mocha)',
|
||||
['clean:test', 'setTestEnv', 'loadConfig', 'mochacli:routes']);
|
||||
|
||||
// ### 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',
|
||||
['clean:test', 'setTestEnv', 'loadConfig', 'cleanDatabase', 'express:test',
|
||||
'spawnCasperJS:setup', 'express:test:stop']
|
||||
);
|
||||
|
||||
// ### Functional tests *(sub task)*
|
||||
// `grunt test-functional` will run just the functional tests
|
||||
//
|
||||
|
@ -656,7 +682,8 @@ var path = require('path'),
|
|||
// 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)',
|
||||
['clean:test', 'setTestEnv', 'loadConfig', 'express:test', 'spawnCasperJS', 'express:test:stop']
|
||||
['clean:test', 'setTestEnv', 'loadConfig', 'cleanDatabase', 'express:test', 'spawnCasperJS', 'express:test:stop',
|
||||
'test-functional-setup']
|
||||
);
|
||||
|
||||
// ### Coverage
|
||||
|
|
|
@ -3,10 +3,29 @@ import loadingIndicator from 'ghost/mixins/loading-indicator';
|
|||
|
||||
var SetupRoute = Ember.Route.extend(styleBody, loadingIndicator, {
|
||||
classNames: ['ghost-setup'],
|
||||
|
||||
// use the beforeModel hook to check to see whether or not setup has been
|
||||
// previously completed. If it has, stop the transition into the setup page.
|
||||
|
||||
beforeModel: function () {
|
||||
var self = this;
|
||||
|
||||
// If user is logged in, setup has already been completed.
|
||||
if (this.get('session').isAuthenticated) {
|
||||
this.transitionTo(Ember.SimpleAuth.routeAfterAuthentication);
|
||||
return;
|
||||
}
|
||||
|
||||
// If user is not logged in, check the state of the setup process via the API
|
||||
return ic.ajax.request(this.get('ghostPaths.url').api('authentication/setup'), {
|
||||
type: 'GET'
|
||||
}).then(function (result) {
|
||||
var setup = result.setup[0].status;
|
||||
|
||||
if (setup) {
|
||||
return self.transitionTo('signin');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -16,13 +16,13 @@
|
|||
<h3>Stack Trace</h3>
|
||||
<p><strong>{{message}}</strong></p>
|
||||
<ul class="error-stack-list">
|
||||
{{#foreach stack}}
|
||||
{{#each stack}}
|
||||
<li>
|
||||
at
|
||||
{{#if function}}<em class="error-stack-function">{{function}}</em>{{/if}}
|
||||
<span class="error-stack-file">({{at}})</span>
|
||||
</li>
|
||||
{{/foreach}}
|
||||
{{/each}}
|
||||
</ul>
|
||||
</section>
|
||||
{{/if}}
|
||||
|
|
|
@ -3,13 +3,13 @@ var SetupValidator = Ember.Object.create({
|
|||
var data = model.getProperties('blogTitle', 'name', 'email', 'password'),
|
||||
validationErrors = [];
|
||||
|
||||
if (!validator.isLength(data.blogTitle || '', 1)) {
|
||||
if (!validator.isLength(data.blogTitle, 1)) {
|
||||
validationErrors.push({
|
||||
message: 'Please enter a blog title.'
|
||||
});
|
||||
}
|
||||
|
||||
if (!validator.isLength(data.name || '', 1)) {
|
||||
if (!validator.isLength(data.name, 1)) {
|
||||
validationErrors.push({
|
||||
message: 'Please enter a name.'
|
||||
});
|
||||
|
@ -21,9 +21,9 @@ var SetupValidator = Ember.Object.create({
|
|||
});
|
||||
}
|
||||
|
||||
if (!validator.isLength(data.password || '', 1)) {
|
||||
if (!validator.isLength(data.password, 8)) {
|
||||
validationErrors.push({
|
||||
message: 'Please enter a password.'
|
||||
message: 'Password must be at least 8 characters long.'
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ var SignupValidator = Ember.Object.create({
|
|||
var data = model.getProperties('name', 'email', 'password'),
|
||||
validationErrors = [];
|
||||
|
||||
if (!validator.isLength(data.name || '', 1)) {
|
||||
if (!validator.isLength(data.name, 1)) {
|
||||
validationErrors.push({
|
||||
message: 'Please enter a name.'
|
||||
});
|
||||
|
@ -15,9 +15,9 @@ var SignupValidator = Ember.Object.create({
|
|||
});
|
||||
}
|
||||
|
||||
if (!validator.isLength(data.password || '', 1)) {
|
||||
if (!validator.isLength(data.password, 8)) {
|
||||
validationErrors.push({
|
||||
message: 'Please enter a password.'
|
||||
message: 'Password must be at least 8 characters long.'
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -41,13 +41,13 @@
|
|||
<h3>Stack Trace</h3>
|
||||
<p><strong>{{message}}</strong></p>
|
||||
<ul class="error-stack-list">
|
||||
{{#foreach stack}}
|
||||
{{#each stack}}
|
||||
<li>
|
||||
at
|
||||
{{#if function}}<em class="error-stack-function">{{function}}</em>{{/if}}
|
||||
<span class="error-stack-file">({{at}})</span>
|
||||
</li>
|
||||
{{/foreach}}
|
||||
{{/each}}
|
||||
</ul>
|
||||
</section>
|
||||
{{/if}}
|
||||
|
|
|
@ -91,7 +91,7 @@ screens = {
|
|||
'signin-authenticated': {
|
||||
url: 'ghost/signin/',
|
||||
//signin with authenticated user redirects to posts
|
||||
selector: '#main-menu .content.active'
|
||||
selector: '#main-menu .content .active'
|
||||
},
|
||||
'signout': {
|
||||
url: 'ghost/signout/',
|
||||
|
@ -109,7 +109,7 @@ screens = {
|
|||
},
|
||||
'setup-authenticated': {
|
||||
url: 'ghost/setup/',
|
||||
selector: '#main-menu .content.active'
|
||||
selector: '#main-menu .content a.active'
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -386,9 +386,9 @@ CasperTest.Routines = (function () {
|
|||
var errorText = casper.evaluate(function () {
|
||||
return document.querySelector('.notification-error').innerText;
|
||||
});
|
||||
casper.echoConcise('It appears as though a user is already registered. Error text: ' + errorText);
|
||||
casper.echoConcise('Setup failed. Error text: ' + errorText);
|
||||
}, function onTimeout() {
|
||||
casper.echoConcise('It appears as though a user was not already registered.');
|
||||
casper.echoConcise('Setup completed.');
|
||||
}, 2000);
|
||||
|
||||
casper.captureScreenshot('setting_up3.png');
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
/*globals CasperTest, casper */
|
||||
CasperTest.begin('Ghost signout works correctly', 3, function suite(test) {
|
||||
CasperTest.Routines.setup.run(test);
|
||||
CasperTest.Routines.signout.run(test);
|
||||
CasperTest.Routines.signin.run(test);
|
||||
|
||||
|
@ -31,4 +30,4 @@ CasperTest.begin('Ghost signout works correctly', 3, function suite(test) {
|
|||
|
||||
casper.captureScreenshot('user-menu-logout-clicked.png');
|
||||
|
||||
}, true);
|
||||
}, true);
|
||||
|
|
|
@ -3,33 +3,35 @@
|
|||
|
||||
/*global CasperTest, casper, email */
|
||||
|
||||
CasperTest.begin('Ghost setup fails properly', 5, function suite(test) {
|
||||
CasperTest.begin('Ghost setup fails properly', 6, function suite(test) {
|
||||
casper.thenOpenAndWaitForPageLoad('setup', function then() {
|
||||
test.assertUrlMatch(/ghost\/setup\/$/, 'Landed on the correct URL');
|
||||
});
|
||||
|
||||
casper.then(function setupWithShortPassword() {
|
||||
casper.fillAndAdd('#setup', {email: email, password: 'test'});
|
||||
casper.fillAndAdd('#setup', { 'blog-title': 'ghost', name: 'slimer', email: email, password: 'short' });
|
||||
});
|
||||
|
||||
// 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]');
|
||||
test.assertSelectorHasText('.notification-error', 'Password must be at least 8 characters long');
|
||||
}, function onTimeout() {
|
||||
test.assert(false, 'No error notification :(');
|
||||
});
|
||||
|
||||
casper.then(function setupWithLongPassword() {
|
||||
casper.fillAndAdd('#setup', {email: email, password: 'testing1234'});
|
||||
casper.fillAndAdd('#setup', { 'blog-title': 'ghost', name: 'slimer', email: email, password: password });
|
||||
});
|
||||
|
||||
// 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 :(');
|
||||
casper.wait(2000);
|
||||
|
||||
casper.waitForResource(/\d+/, function testForDashboard() {
|
||||
test.assertUrlMatch(/ghost\/\d+\/$/, 'Landed on the correct URL');
|
||||
test.assertExists('#global-header', 'Global admin header is present');
|
||||
test.assertExists('.manage', 'We\'re now on content');
|
||||
}, function onTimeOut() {
|
||||
test.fail('Failed to signin');
|
||||
});
|
||||
}, true);
|
||||
|
||||
|
@ -39,13 +41,13 @@ CasperTest.begin('Authenticated user is redirected', 8, function suite(test) {
|
|||
test.assertUrlMatch(/ghost\/signin\/$/, 'Landed on the correct URL');
|
||||
});
|
||||
|
||||
casper.waitForOpaque('.login-box', function then() {
|
||||
casper.waitForOpaque('.login-box', function then() {
|
||||
this.fillAndSave('#login', user);
|
||||
});
|
||||
|
||||
casper.wait(2000);
|
||||
|
||||
casper.waitForResource(/posts/, function testForDashboard() {
|
||||
casper.waitForResource(/\d+/, function testForDashboard() {
|
||||
test.assertUrlMatch(/ghost\/\d+\/$/, 'Landed on the correct URL');
|
||||
test.assertExists('#global-header', 'Global admin header is present');
|
||||
test.assertExists('.manage', 'We\'re now on content');
|
Loading…
Add table
Reference in a new issue