mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-23 19:02:29 +03:00
Update validator to 3.4.0
Closes #1379 - Convert to new api usage for both server-side and client-side - Provide way require a negative response for boolean methods in default-settings.json - Add field validation functional tests - Settings (General) - Title length validation - Description length validation - postsPerPage, numeric, min, max - Settings (User) - Bio Length validation - Location length validation - Url validation - Login - Email validation - Editor - Title required validation
This commit is contained in:
parent
8d3a54527b
commit
e4bb6d08cc
@ -15,6 +15,6 @@
|
||||
"nprogress": "0.1.2",
|
||||
"fastclick": "1.0.0",
|
||||
"Countable": "2.0.2",
|
||||
"validator-js": "1.5.1"
|
||||
"validator-js": "3.4.0"
|
||||
}
|
||||
}
|
||||
|
1233
core/client/assets/vendor/validator-client.js
vendored
1233
core/client/assets/vendor/validator-client.js
vendored
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
/*globals window, $, _, Backbone, Validator */
|
||||
/*globals window, $, _, Backbone, validator */
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
@ -17,7 +17,6 @@
|
||||
Views : {},
|
||||
Collections : {},
|
||||
Models : {},
|
||||
Validate : new Validator(),
|
||||
|
||||
paths: ghostPaths(),
|
||||
|
||||
@ -62,21 +61,16 @@
|
||||
});
|
||||
};
|
||||
|
||||
Ghost.Validate.error = function (object) {
|
||||
this._errors.push(object);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Ghost.Validate.handleErrors = function () {
|
||||
validator.handleErrors = function (errors) {
|
||||
Ghost.notifications.clearEverything();
|
||||
_.each(Ghost.Validate._errors, function (errorObj) {
|
||||
_.each(errors, function (errorObj) {
|
||||
|
||||
Ghost.notifications.addItem({
|
||||
type: 'error',
|
||||
message: errorObj.message || errorObj,
|
||||
status: 'passive'
|
||||
});
|
||||
|
||||
if (errorObj.hasOwnProperty('el')) {
|
||||
errorObj.el.addClass('input-error');
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/*global window, Ghost, $ */
|
||||
/*global window, Ghost, $, validator */
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
@ -25,14 +25,19 @@
|
||||
event.preventDefault();
|
||||
var email = this.$el.find('.email').val(),
|
||||
password = this.$el.find('.password').val(),
|
||||
redirect = Ghost.Views.Utils.getUrlVariables().r;
|
||||
redirect = Ghost.Views.Utils.getUrlVariables().r,
|
||||
validationErrors = [];
|
||||
|
||||
Ghost.Validate._errors = [];
|
||||
Ghost.Validate.check(email).isEmail();
|
||||
Ghost.Validate.check(password, "Please enter a password").len(0);
|
||||
if (!validator.isEmail(email)) {
|
||||
validationErrors.push("Invalid Email");
|
||||
}
|
||||
|
||||
if (Ghost.Validate._errors.length > 0) {
|
||||
Ghost.Validate.handleErrors();
|
||||
if (!validator.isLength(password, 0)) {
|
||||
validationErrors.push("Please enter a password");
|
||||
}
|
||||
|
||||
if (validationErrors.length) {
|
||||
validator.handleErrors(validationErrors);
|
||||
} else {
|
||||
$.ajax({
|
||||
url: Ghost.paths.subdir + '/ghost/signin/',
|
||||
@ -88,18 +93,27 @@
|
||||
event.preventDefault();
|
||||
var name = this.$('.name').val(),
|
||||
email = this.$('.email').val(),
|
||||
password = this.$('.password').val();
|
||||
password = this.$('.password').val(),
|
||||
validationErrors = [];
|
||||
|
||||
// This is needed due to how error handling is done. If this is not here, there will not be a time
|
||||
// when there is no error.
|
||||
Ghost.Validate._errors = [];
|
||||
Ghost.Validate.check(name, "Please enter a name").len(1);
|
||||
Ghost.Validate.check(email, "Please enter a correct email address").isEmail();
|
||||
Ghost.Validate.check(password, "Your password is not long enough. It must be at least 8 characters long.").len(8);
|
||||
Ghost.Validate.check(this.submitted, "Ghost is signing you up. Please wait...").equals("no");
|
||||
if (!validator.isLength(name, 1)) {
|
||||
validationErrors.push("Please enter a name.");
|
||||
}
|
||||
|
||||
if (Ghost.Validate._errors.length > 0) {
|
||||
Ghost.Validate.handleErrors();
|
||||
if (!validator.isEmail(email)) {
|
||||
validationErrors.push("Please enter a correct email address.");
|
||||
}
|
||||
|
||||
if (!validator.isLength(password, 0)) {
|
||||
validationErrors.push("Please enter a password");
|
||||
}
|
||||
|
||||
if (!validator.equals(this.submitted, "no")) {
|
||||
validationErrors.push("Ghost is signing you up. Please wait...");
|
||||
}
|
||||
|
||||
if (validationErrors.length) {
|
||||
validator.handleErrors(validationErrors);
|
||||
} else {
|
||||
this.submitted = "yes";
|
||||
$.ajax({
|
||||
@ -152,13 +166,15 @@
|
||||
submitHandler: function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
var email = this.$el.find('.email').val();
|
||||
var email = this.$el.find('.email').val(),
|
||||
validationErrors = [];
|
||||
|
||||
Ghost.Validate._errors = [];
|
||||
Ghost.Validate.check(email).isEmail();
|
||||
if (!validator.isEmail(email)) {
|
||||
validationErrors.push("Please enter a correct email address.");
|
||||
}
|
||||
|
||||
if (Ghost.Validate._errors.length > 0) {
|
||||
Ghost.Validate.handleErrors();
|
||||
if (validationErrors.length) {
|
||||
validator.handleErrors(validationErrors);
|
||||
} else {
|
||||
$.ajax({
|
||||
url: Ghost.paths.subdir + '/ghost/forgotten/',
|
||||
|
@ -1,4 +1,4 @@
|
||||
/*global document, Ghost, $, _, Countable */
|
||||
/*global document, Ghost, $, _, Countable, validator */
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
@ -160,28 +160,32 @@
|
||||
description = this.$('#blog-description').val(),
|
||||
email = this.$('#email-address').val(),
|
||||
postsPerPage = this.$('#postsPerPage').val(),
|
||||
permalinks = this.$('#permalinks').is(':checked') ? '/:year/:month/:day/:slug/' : '/:slug/';
|
||||
permalinks = this.$('#permalinks').is(':checked') ? '/:year/:month/:day/:slug/' : '/:slug/',
|
||||
validationErrors = [];
|
||||
|
||||
Ghost.Validate._errors = [];
|
||||
Ghost.Validate
|
||||
.check(title, {message: "Title is too long", el: $('#blog-title')})
|
||||
.len(0, 150);
|
||||
Ghost.Validate
|
||||
.check(description, {message: "Description is too long", el: $('#blog-description')})
|
||||
.len(0, 200);
|
||||
Ghost.Validate
|
||||
.check(email, {message: "Please supply a valid email address", el: $('#email-address')})
|
||||
.isEmail().len(0, 254);
|
||||
Ghost.Validate
|
||||
.check(postsPerPage, {message: "Please use a number less than 1000", el: $('postsPerPage')})
|
||||
.isInt().max(1000);
|
||||
Ghost.Validate
|
||||
.check(postsPerPage, {message: "Please use a number greater than 0", el: $('postsPerPage')})
|
||||
.isInt().min(0);
|
||||
if (!validator.isLength(title, 0, 150)) {
|
||||
validationErrors.push({message: "Title is too long", el: $('#blog-title')});
|
||||
}
|
||||
|
||||
if (!validator.isLength(description, 0, 200)) {
|
||||
validationErrors.push({message: "Description is too long", el: $('#blog-description')});
|
||||
}
|
||||
|
||||
if (!validator.isEmail(email) || !validator.isLength(email, 0, 254)) {
|
||||
validationErrors.push({message: "Please supply a valid email address", el: $('#email-address')});
|
||||
}
|
||||
|
||||
if (!validator.isInt(postsPerPage) || postsPerPage > 1000) {
|
||||
validationErrors.push({message: "Please use a number less than 1000", el: $('postsPerPage')});
|
||||
}
|
||||
|
||||
if (!validator.isInt(postsPerPage) || postsPerPage < 0) {
|
||||
validationErrors.push({message: "Please use a number greater than 0", el: $('postsPerPage')});
|
||||
}
|
||||
|
||||
|
||||
if (Ghost.Validate._errors.length > 0) {
|
||||
Ghost.Validate.handleErrors();
|
||||
if (validationErrors.length) {
|
||||
validator.handleErrors(validationErrors);
|
||||
} else {
|
||||
this.model.save({
|
||||
title: title,
|
||||
@ -343,30 +347,33 @@
|
||||
userEmail = this.$('#user-email').val(),
|
||||
userLocation = this.$('#user-location').val(),
|
||||
userWebsite = this.$('#user-website').val(),
|
||||
userBio = this.$('#user-bio').val();
|
||||
userBio = this.$('#user-bio').val(),
|
||||
validationErrors = [];
|
||||
|
||||
Ghost.Validate._errors = [];
|
||||
Ghost.Validate
|
||||
.check(userName, {message: "Name is too long", el: $('#user-name')})
|
||||
.len(0, 150);
|
||||
Ghost.Validate
|
||||
.check(userBio, {message: "Bio is too long", el: $('#user-bio')})
|
||||
.len(0, 200);
|
||||
Ghost.Validate
|
||||
.check(userEmail, {message: "Please supply a valid email address", el: $('#user-email')})
|
||||
.isEmail();
|
||||
Ghost.Validate
|
||||
.check(userLocation, {message: "Location is too long", el: $('#user-location')})
|
||||
.len(0, 150);
|
||||
if (userWebsite.length > 0) {
|
||||
Ghost.Validate
|
||||
.check(userWebsite, {message: "Please use a valid url", el: $('#user-website')})
|
||||
.isUrl()
|
||||
.len(0, 2000);
|
||||
if (!validator.isLength(userName, 0, 150)) {
|
||||
validationErrors.push({message: "Name is too long", el: $('#user-name')});
|
||||
}
|
||||
|
||||
if (Ghost.Validate._errors.length > 0) {
|
||||
Ghost.Validate.handleErrors();
|
||||
if (!validator.isLength(userBio, 0, 200)) {
|
||||
validationErrors.push({message: "Bio is too long", el: $('#user-bio')});
|
||||
}
|
||||
|
||||
if (!validator.isEmail(userEmail)) {
|
||||
validationErrors.push({message: "Please supply a valid email address", el: $('#user-email')});
|
||||
}
|
||||
|
||||
if (!validator.isLength(userLocation, 0, 150)) {
|
||||
validationErrors.push({message: "Location is too long", el: $('#user-location')});
|
||||
}
|
||||
|
||||
if (userWebsite.length) {
|
||||
if (!validator.isURL(userWebsite) || !validator.isLength(userWebsite, 0, 2000)) {
|
||||
validationErrors.push({message: "Please use a valid url", el: $('#user-website')});
|
||||
}
|
||||
}
|
||||
|
||||
if (validationErrors.length) {
|
||||
validator.handleErrors(validationErrors);
|
||||
} else {
|
||||
|
||||
this.model.save({
|
||||
@ -389,16 +396,20 @@
|
||||
var self = this,
|
||||
oldPassword = this.$('#user-password-old').val(),
|
||||
newPassword = this.$('#user-password-new').val(),
|
||||
ne2Password = this.$('#user-new-password-verification').val();
|
||||
ne2Password = this.$('#user-new-password-verification').val(),
|
||||
validationErrors = [];
|
||||
|
||||
Ghost.Validate._errors = [];
|
||||
Ghost.Validate.check(newPassword, {message: 'Your new passwords do not match'}).equals(ne2Password);
|
||||
Ghost.Validate.check(newPassword, {message: 'Your password is not long enough. It must be at least 8 characters long.'}).len(8);
|
||||
if (!validator.equals(newPassword, ne2Password)) {
|
||||
validationErrors.push("Your new passwords do not match");
|
||||
}
|
||||
|
||||
if (Ghost.Validate._errors.length > 0) {
|
||||
Ghost.Validate.handleErrors();
|
||||
if (!validator.isLength(newPassword, 8)) {
|
||||
validationErrors.push("Your password is not long enough. It must be at least 8 characters long.");
|
||||
}
|
||||
|
||||
if (validationErrors.length) {
|
||||
validator.handleErrors(validationErrors);
|
||||
} else {
|
||||
|
||||
$.ajax({
|
||||
url: Ghost.paths.subdir + '/ghost/changepw/',
|
||||
type: 'POST',
|
||||
|
@ -23,7 +23,7 @@
|
||||
"email": {
|
||||
"defaultValue": "ghost@example.com",
|
||||
"validations": {
|
||||
"notNull": true,
|
||||
"isNull": false,
|
||||
"isEmail": true
|
||||
}
|
||||
},
|
||||
@ -36,29 +36,29 @@
|
||||
"defaultLang": {
|
||||
"defaultValue": "en_US",
|
||||
"validations": {
|
||||
"notNull": true
|
||||
"isNull": false
|
||||
}
|
||||
},
|
||||
"postsPerPage": {
|
||||
"defaultValue": "6",
|
||||
"validations": {
|
||||
"notNull": true,
|
||||
"isNull": false,
|
||||
"isInt": true,
|
||||
"max": 1000
|
||||
"isLength": [0, 1000]
|
||||
}
|
||||
},
|
||||
"forceI18n": {
|
||||
"defaultValue": "true",
|
||||
"validations": {
|
||||
"notNull": true,
|
||||
"isIn": ["true", "false"]
|
||||
"isNull": false,
|
||||
"isIn": [["true", "false"]]
|
||||
}
|
||||
},
|
||||
"permalinks": {
|
||||
"defaultValue": "/:slug/",
|
||||
"validations": {
|
||||
"is": "^(\/:?[a-z0-9_-]+){1,5}\/$",
|
||||
"regex": "(:id|:slug|:year|:month|:day)",
|
||||
"matches": "^(\/:?[a-z0-9_-]+){1,5}\/$",
|
||||
"matches": "(:id|:slug|:year|:month|:day)",
|
||||
"notContains": "/ghost/"
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ var db = {
|
||||
html: {type: 'text', maxlength: 16777215, fieldtype: 'medium', nullable: true},
|
||||
image: {type: 'text', maxlength: 2000, nullable: true},
|
||||
featured: {type: 'bool', nullable: false, defaultTo: false},
|
||||
page: {type: 'bool', nullable: false, defaultTo: false, validations: {'isIn': ['true', 'false']}},
|
||||
page: {type: 'bool', nullable: false, defaultTo: false, validations: {'isIn': [['true', 'false']]}},
|
||||
status: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'draft'},
|
||||
language: {type: 'string', maxlength: 6, nullable: false, defaultTo: 'en_US'},
|
||||
meta_title: {type: 'string', maxlength: 150, nullable: true},
|
||||
@ -31,7 +31,7 @@ var db = {
|
||||
image: {type: 'text', maxlength: 2000, nullable: true},
|
||||
cover: {type: 'text', maxlength: 2000, nullable: true},
|
||||
bio: {type: 'string', maxlength: 200, nullable: true},
|
||||
website: {type: 'text', maxlength: 2000, nullable: true, validations: {'isUrl': true}},
|
||||
website: {type: 'text', maxlength: 2000, nullable: true, validations: {'isURL': true}},
|
||||
location: {type: 'text', maxlength: 65535, nullable: true},
|
||||
accessibility: {type: 'text', maxlength: 65535, nullable: true},
|
||||
status: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'active'},
|
||||
@ -91,7 +91,7 @@ var db = {
|
||||
uuid: {type: 'string', maxlength: 36, nullable: false, validations: {'isUUID': true}},
|
||||
key: {type: 'string', maxlength: 150, nullable: false, unique: true},
|
||||
value: {type: 'text', maxlength: 65535, nullable: true},
|
||||
type: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'core', validations: {'isIn': ['core', 'blog', 'theme', 'app', 'plugin']}},
|
||||
type: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'core', validations: {'isIn': [['core', 'blog', 'theme', 'app', 'plugin']]}},
|
||||
created_at: {type: 'dateTime', nullable: false},
|
||||
created_by: {type: 'integer', nullable: false},
|
||||
updated_at: {type: 'dateTime', nullable: true},
|
||||
|
@ -6,6 +6,16 @@ var schema = require('../schema').tables,
|
||||
validateSettings,
|
||||
validate;
|
||||
|
||||
// Provide a few custom validators
|
||||
//
|
||||
validator.extend('empty', function (str) {
|
||||
return _.isEmpty(str);
|
||||
});
|
||||
|
||||
validator.extend('notContains', function (str, badString) {
|
||||
return !_.contains(str, badString);
|
||||
});
|
||||
|
||||
// Validation validation against schema attributes
|
||||
// values are checked against the validation objects
|
||||
// form schema.js
|
||||
@ -16,17 +26,18 @@ validateSchema = function (tableName, model) {
|
||||
// check nullable
|
||||
if (model.hasOwnProperty(columnKey) && schema[tableName][columnKey].hasOwnProperty('nullable')
|
||||
&& schema[tableName][columnKey].nullable !== true) {
|
||||
validator.check(model[columnKey], 'Value in [' + tableName + '.' + columnKey +
|
||||
'] cannot be blank.').notNull();
|
||||
validator.check(model[columnKey], 'Value in [' + tableName + '.' + columnKey +
|
||||
'] cannot be blank.').notEmpty();
|
||||
if (validator.isNull(model[columnKey]) || validator.empty(model[columnKey])) {
|
||||
throw new Error('Value in [' + tableName + '.' + columnKey + '] cannot be blank.');
|
||||
}
|
||||
}
|
||||
// TODO: check if mandatory values should be enforced
|
||||
if (model[columnKey]) {
|
||||
// check length
|
||||
if (schema[tableName][columnKey].hasOwnProperty('maxlength')) {
|
||||
validator.check(model[columnKey], 'Value in [' + tableName + '.' + columnKey +
|
||||
'] exceeds maximum length of %2 characters.').len(0, schema[tableName][columnKey].maxlength);
|
||||
if (!validator.isLength(model[columnKey], 0, schema[tableName][columnKey].maxlength)) {
|
||||
throw new Error('Value in [' + tableName + '.' + columnKey +
|
||||
'] exceeds maximum length of ' + schema[tableName][columnKey].maxlength + ' characters.');
|
||||
}
|
||||
}
|
||||
|
||||
//check validations objects
|
||||
@ -36,9 +47,8 @@ validateSchema = function (tableName, model) {
|
||||
|
||||
//check type
|
||||
if (schema[tableName][columnKey].hasOwnProperty('type')) {
|
||||
if (schema[tableName][columnKey].type === 'integer') {
|
||||
validator.check(model[columnKey], 'Value in [' + tableName + '.' + columnKey +
|
||||
'] is no valid integer.' + model[columnKey]).isInt();
|
||||
if (schema[tableName][columnKey].type === 'integer' && !validator.isInt(model[columnKey])) {
|
||||
throw new Error('Value in [' + tableName + '.' + columnKey + '] is no valid integer.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -57,29 +67,42 @@ validateSettings = function (defaultSettings, model) {
|
||||
}
|
||||
};
|
||||
|
||||
// Validate using the validation module.
|
||||
// Each validation's key is a name and its value is an array of options
|
||||
// Use true (boolean) if options aren't applicable
|
||||
// Validate default settings using the validator module.
|
||||
// Each validation's key is a method name and its value is an array of options
|
||||
//
|
||||
// eg:
|
||||
// validations: { isUrl: true, len: [20, 40] }
|
||||
// validations: { isUrl: true, isLength: [20, 40] }
|
||||
//
|
||||
// will validate that a values's length is a URL between 20 and 40 chars,
|
||||
// available validators: https://github.com/chriso/node-validator#list-of-validation-methods
|
||||
// will validate that a setting's length is a URL between 20 and 40 chars.
|
||||
//
|
||||
// If you pass a boolean as the value, it will specify the "good" result. By default
|
||||
// the "good" result is assumed to be true.
|
||||
//
|
||||
// eg:
|
||||
// validations: { isNull: false } // means the "good" result would
|
||||
// // fail the `isNull` check, so
|
||||
// // not null.
|
||||
//
|
||||
// available validators: https://github.com/chriso/validator.js#validators
|
||||
validate = function (value, key, validations) {
|
||||
_.each(validations, function (validationOptions, validationName) {
|
||||
var validation = validator.check(value, 'Validation [' + validationName + '] of field [' + key + '] failed.');
|
||||
var goodResult = true;
|
||||
|
||||
if (validationOptions === true) {
|
||||
validationOptions = null;
|
||||
}
|
||||
/* jshint ignore:start */
|
||||
if (typeof validationOptions !== 'array') {
|
||||
if (_.isBoolean(validationOptions)) {
|
||||
goodResult = validationOptions;
|
||||
validationOptions = [];
|
||||
} else if (!_.isArray(validationOptions)) {
|
||||
validationOptions = [validationOptions];
|
||||
}
|
||||
/* jshint ignore:end */
|
||||
// equivalent of validation.isSomething(option1, option2)
|
||||
validation[validationName].apply(validation, validationOptions);
|
||||
|
||||
validationOptions.unshift(value);
|
||||
|
||||
// equivalent of validator.isSomething(option1, option2)
|
||||
if (validator[validationName].apply(validator, validationOptions) !== goodResult) {
|
||||
throw new Error('Settings validation (' + validationName + ') failed for ' + key);
|
||||
}
|
||||
|
||||
validationOptions.shift();
|
||||
}, this);
|
||||
};
|
||||
|
||||
|
@ -17,7 +17,9 @@ var _ = require('lodash'),
|
||||
|
||||
function validatePasswordLength(password) {
|
||||
try {
|
||||
validator.check(password, "Your password must be at least 8 characters long.").len(8);
|
||||
if (!validator.isLength(password, 8)) {
|
||||
throw new Error('Your password must be at least 8 characters long.');
|
||||
}
|
||||
} catch (error) {
|
||||
return when.reject(error);
|
||||
}
|
||||
|
@ -91,6 +91,27 @@ CasperTest.begin("Word count and plurality", 4, function suite(test) {
|
||||
});
|
||||
});
|
||||
|
||||
CasperTest.begin('Required Title', 4, function suite(test) {
|
||||
casper.thenOpen(url + "ghost/editor/", function testTitleAndUrl() {
|
||||
test.assertTitle("Ghost Admin", "Ghost admin has no title");
|
||||
test.assertUrlMatch(/ghost\/editor\/$/, "Ghost doesn't require login this time");
|
||||
});
|
||||
|
||||
casper.waitForSelector('#entry-title', function then() {
|
||||
test.assertEvalEquals(function() {
|
||||
return document.getElementById('entry-title').value;
|
||||
}, '', 'Title is empty');
|
||||
});
|
||||
|
||||
casper.thenClick('.js-publish-button'); // Safe to assume draft mode?
|
||||
|
||||
casper.waitForSelectorTextChange('.notification-error', function onSuccess() {
|
||||
test.assertSelectorHasText('.notification-error', 'must specify a title');
|
||||
}, function onTimeout() {
|
||||
test.fail('Title required error did not appear');
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
CasperTest.begin('Title Trimming', 2, function suite(test) {
|
||||
var untrimmedTitle = ' test title ',
|
||||
trimmedTitle = 'test title';
|
||||
|
@ -130,3 +130,24 @@ CasperTest.begin("Can login to Ghost", 4, function suite(test) {
|
||||
test.fail('Failed to load ghost/ resource');
|
||||
});
|
||||
}, true);
|
||||
|
||||
CasperTest.begin('Ensure email field form validation', 1, function suite(test) {
|
||||
casper.thenOpen(url + 'ghost/signin/');
|
||||
|
||||
casper.waitForOpaque(".js-login-box",
|
||||
function then() {
|
||||
this.fill("form.login-form", {
|
||||
'email': 'notanemail'
|
||||
}, true);
|
||||
},
|
||||
function onTimeout() {
|
||||
test.fail('Login form didn\'t fade in.');
|
||||
});
|
||||
|
||||
casper.waitForSelectorTextChange('.notification-error', function onSuccess() {
|
||||
test.assertSelectorHasText('.notification-error', 'Invalid Email');
|
||||
}, function onTimeout() {
|
||||
test.fail('Email validation error did not appear');
|
||||
}, 2000);
|
||||
|
||||
}, true);
|
||||
|
@ -112,6 +112,111 @@ CasperTest.begin("Settings screen is correct", 18, function suite(test) {
|
||||
});
|
||||
});
|
||||
|
||||
CasperTest.begin('Ensure general blog title field length validation', 3, function suite(test) {
|
||||
casper.thenOpen(url + "ghost/settings/general/", function testTitleAndUrl() {
|
||||
test.assertTitle("Ghost Admin", "Ghost admin has no title");
|
||||
test.assertUrlMatch(/ghost\/settings\/general\/$/, "Ghost doesn't require login this time");
|
||||
});
|
||||
|
||||
casper.waitForSelector('#general', function then() {
|
||||
this.fill("form#settings-general", {
|
||||
'general[title]': new Array(152).join('a')
|
||||
});
|
||||
});
|
||||
|
||||
casper.thenClick('#general .button-save');
|
||||
|
||||
casper.waitForSelectorTextChange('.notification-error', function onSuccess() {
|
||||
test.assertSelectorHasText('.notification-error', 'too long');
|
||||
}, function onTimeout() {
|
||||
test.fail('Blog title length error did not appear');
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
CasperTest.begin('Ensure general blog description field length validation', 3, function suite(test) {
|
||||
casper.thenOpen(url + "ghost/settings/general/", function testTitleAndUrl() {
|
||||
test.assertTitle("Ghost Admin", "Ghost admin has no title");
|
||||
test.assertUrlMatch(/ghost\/settings\/general\/$/, "Ghost doesn't require login this time");
|
||||
});
|
||||
|
||||
casper.waitForSelector('#general', function then() {
|
||||
this.fillSelectors("form#settings-general", {
|
||||
'#blog-description': new Array(202).join('a')
|
||||
});
|
||||
});
|
||||
|
||||
casper.thenClick('#general .button-save');
|
||||
|
||||
casper.waitForSelectorTextChange('.notification-error', function onSuccess() {
|
||||
test.assertSelectorHasText('.notification-error', 'too long');
|
||||
}, function onTimeout() {
|
||||
test.fail('Blog description length error did not appear');
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
CasperTest.begin('Ensure postsPerPage number field form validation', 3, function suite(test) {
|
||||
casper.thenOpen(url + "ghost/settings/general/", function testTitleAndUrl() {
|
||||
test.assertTitle("Ghost Admin", "Ghost admin has no title");
|
||||
test.assertUrlMatch(/ghost\/settings\/general\/$/, "Ghost doesn't require login this time");
|
||||
});
|
||||
|
||||
casper.waitForSelector('#general', function then() {
|
||||
this.fill("form#settings-general", {
|
||||
'general[postsPerPage]': 'notaninteger'
|
||||
});
|
||||
});
|
||||
|
||||
casper.thenClick('#general .button-save');
|
||||
|
||||
casper.waitForSelectorTextChange('.notification-error', function onSuccess() {
|
||||
test.assertSelectorHasText('.notification-error', 'use a number');
|
||||
}, function onTimeout() {
|
||||
test.fail('postsPerPage error did not appear');
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
CasperTest.begin('Ensure postsPerPage max of 1000', 3, function suite(test) {
|
||||
casper.thenOpen(url + "ghost/settings/general/", function testTitleAndUrl() {
|
||||
test.assertTitle("Ghost Admin", "Ghost admin has no title");
|
||||
test.assertUrlMatch(/ghost\/settings\/general\/$/, "Ghost doesn't require login this time");
|
||||
});
|
||||
|
||||
casper.waitForSelector('#general', function then() {
|
||||
this.fill("form#settings-general", {
|
||||
'general[postsPerPage]': '1001'
|
||||
});
|
||||
});
|
||||
|
||||
casper.thenClick('#general .button-save');
|
||||
|
||||
casper.waitForSelectorTextChange('.notification-error', function onSuccess() {
|
||||
test.assertSelectorHasText('.notification-error', 'use a number less than 1000');
|
||||
}, function onTimeout() {
|
||||
test.fail('postsPerPage max error did not appear');
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
CasperTest.begin('Ensure postsPerPage min of 0', 3, function suite(test) {
|
||||
casper.thenOpen(url + "ghost/settings/general/", function testTitleAndUrl() {
|
||||
test.assertTitle("Ghost Admin", "Ghost admin has no title");
|
||||
test.assertUrlMatch(/ghost\/settings\/general\/$/, "Ghost doesn't require login this time");
|
||||
});
|
||||
|
||||
casper.waitForSelector('#general', function then() {
|
||||
this.fill("form#settings-general", {
|
||||
'general[postsPerPage]': '-1'
|
||||
});
|
||||
});
|
||||
|
||||
casper.thenClick('#general .button-save');
|
||||
|
||||
casper.waitForSelectorTextChange('.notification-error', function onSuccess() {
|
||||
test.assertSelectorHasText('.notification-error', 'use a number greater than 0');
|
||||
}, function onTimeout() {
|
||||
test.fail('postsPerPage min error did not appear');
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
CasperTest.begin("User settings screen validates email", 6, function suite(test) {
|
||||
var email, brokenEmail;
|
||||
|
||||
@ -183,3 +288,66 @@ CasperTest.begin("User settings screen shows remaining characters for Bio proper
|
||||
test.assert(getRemainingBioCharacterCount() === '195', 'Bio remaining characters is 195');
|
||||
});
|
||||
});
|
||||
|
||||
CasperTest.begin('Ensure user bio field length validation', 3, function suite(test) {
|
||||
casper.thenOpen(url + "ghost/settings/user/", function testTitleAndUrl() {
|
||||
test.assertTitle("Ghost Admin", "Ghost admin has no title");
|
||||
test.assertUrlMatch(/ghost\/settings\/user\/$/, "Ghost doesn't require login this time");
|
||||
});
|
||||
|
||||
casper.waitForSelector('#user', function then() {
|
||||
this.fillSelectors("form.user-profile", {
|
||||
'#user-bio': new Array(202).join('a')
|
||||
});
|
||||
});
|
||||
|
||||
casper.thenClick('#user .button-save');
|
||||
|
||||
casper.waitForSelectorTextChange('.notification-error', function onSuccess() {
|
||||
test.assertSelectorHasText('.notification-error', 'is too long');
|
||||
}, function onTimeout() {
|
||||
test.fail('Bio field length error did not appear');
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
CasperTest.begin('Ensure user url field validation', 3, function suite(test) {
|
||||
casper.thenOpen(url + "ghost/settings/user/", function testTitleAndUrl() {
|
||||
test.assertTitle("Ghost Admin", "Ghost admin has no title");
|
||||
test.assertUrlMatch(/ghost\/settings\/user\/$/, "Ghost doesn't require login this time");
|
||||
});
|
||||
|
||||
casper.waitForSelector('#user', function then() {
|
||||
this.fillSelectors("form.user-profile", {
|
||||
'#user-website': 'notaurl'
|
||||
});
|
||||
});
|
||||
|
||||
casper.thenClick('#user .button-save');
|
||||
|
||||
casper.waitForSelectorTextChange('.notification-error', function onSuccess() {
|
||||
test.assertSelectorHasText('.notification-error', 'use a valid url');
|
||||
}, function onTimeout() {
|
||||
test.fail('Url validation error did not appear');
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
CasperTest.begin('Ensure user location field length validation', 3, function suite(test) {
|
||||
casper.thenOpen(url + "ghost/settings/user/", function testTitleAndUrl() {
|
||||
test.assertTitle("Ghost Admin", "Ghost admin has no title");
|
||||
test.assertUrlMatch(/ghost\/settings\/user\/$/, "Ghost doesn't require login this time");
|
||||
});
|
||||
|
||||
casper.waitForSelector('#user', function then() {
|
||||
this.fillSelectors("form.user-profile", {
|
||||
'#user-location': new Array(1002).join('a')
|
||||
});
|
||||
});
|
||||
|
||||
casper.thenClick('#user .button-save');
|
||||
|
||||
casper.waitForSelectorTextChange('.notification-error', function onSuccess() {
|
||||
test.assertSelectorHasText('.notification-error', 'is too long');
|
||||
}, function onTimeout() {
|
||||
test.fail('Location field length error did not appear');
|
||||
}, 2000);
|
||||
});
|
||||
|
@ -52,7 +52,7 @@
|
||||
"showdown": "0.3.1",
|
||||
"sqlite3": "2.2.0",
|
||||
"unidecode": "0.1.3",
|
||||
"validator": "1.4.0",
|
||||
"validator": "3.4.0",
|
||||
"when": "2.7.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
|
Loading…
Reference in New Issue
Block a user