Skip to content

Commit

Permalink
Refurbished validationmessages
Browse files Browse the repository at this point in the history
Number fields got "Required" insted of "Value is not a number" since the
input[type="number"] stopped the value being written to the model and we
had higher precedence on tv4 error messages. This is no longer the case
we show some ordinary angular validation messages as well.

This fixes json-schema-form#322

Also fixed some small bugs regarding the error messages. Often
`viewValue` is what you want in the error message.

Plus a start with protractor.
  • Loading branch information
davidlgj committed Apr 17, 2015
1 parent dbb7d17 commit 5700261
Show file tree
Hide file tree
Showing 12 changed files with 152 additions and 31 deletions.
3 changes: 2 additions & 1 deletion .jscs.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"preset": "google",
"maximumLineLength": 100
"maximumLineLength": 100,
"disallowMultipleLineBreaks": false
}
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ The context variables available to you are:
| error | The error code |
| title | Title of the field |
| value | The model value |
| viewValue | The view value (probably the one you want) |
| form | form definition object for this field |
| schema | schema for this field |
Expand Down
5 changes: 3 additions & 2 deletions examples/bootstrap-example.html
Original file line number Diff line number Diff line change
Expand Up @@ -273,11 +273,12 @@ <h3>Schema</h3>
}).error(function() {
$scope.loadedData = 'dummy';
$scope.error = 'Failed to load gist.';
$scope.selectedTest = $scope.tests[0];
});
} else {
$scope.selectedTest = $scope.tests[0];
}

$scope.selectedTest = $scope.tests[0];

$scope.$watch('selectedTest',function(val){
if (val) {
$http.get(val.data).then(function(res) {setNewData(res.data);});
Expand Down
38 changes: 38 additions & 0 deletions gulp/tasks/protractor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
var gulp = require('gulp');

// The protractor task
var protractor = require('gulp-protractor');

// Start a standalone server
var webdriver_standalone = protractor.webdriver_standalone;

// Download and update the selenium driver
var webdriver_update = protractor.webdriver_update;

// Downloads the selenium webdriver
gulp.task('webdriver-update', webdriver_update);

// Start the standalone selenium server
// NOTE: This is not needed if you reference the
// seleniumServerJar in your protractor.conf.js
gulp.task('webdriver-standalone', webdriver_standalone);


// Setting up the test task
gulp.task('protractor', ['webdriver-update'], function(cb) {
gulp.src(['test/protractor/specs/**/*.js']).pipe(protractor.protractor({
configFile: 'test/protractor/conf.js',
})).on('error', function(e) {
console.log(e);
}).on('end', cb);
});

['validation-messages', 'custom-validation'].forEach(function(name) {
gulp.task('protractor:' + name, ['webdriver-update'], function(cb) {
gulp.src(['test/protractor/specs/' + name + '.js']).pipe(protractor.protractor({
configFile: 'test/protractor/conf.js',
})).on('error', function(e) {
console.log(e);
}).on('end', cb);
});
});
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"gulp-concat": "^2.2.0",
"gulp-jscs": "^1.1.0",
"gulp-minify-html": "^0.1.1",
"gulp-protractor": "^1.0.0",
"gulp-rename": "^1.2.0",
"gulp-uglify": "^0.2.1",
"karma": "^0.12.0",
Expand All @@ -49,6 +50,7 @@
"karma-phantomjs-launcher": "^0.1.4",
"mocha": "^1.18.0",
"mocha-lcov-reporter": "0.0.1",
"protractor": "^2.0.0",
"sinon": "^1.9.0",
"sinon-chai": "^2.5.0",
"streamqueue": "0.0.5"
Expand Down
13 changes: 1 addition & 12 deletions src/directives/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,13 @@ angular.module('schemaForm').directive('sfMessage',

// We only show one error.
// TODO: Make that optional
// tv4- errors take precedence
var error = errors[0];
if (errors.length > 1) {

error = errors.reduce(function(prev, value) {
if (prev && prev.indexOf('tv4-') === 0) {
return prev;
}
return value;
});
console.log('reduced',errors, error)

}

if (error) {
element.html(sfErrorMessage.interpolate(
error,
scope.ngModel.$modelValue,
scope.ngModel.$viewValue,
scope.form,
scope.options && scope.options.validationMessage
));
Expand Down
1 change: 1 addition & 0 deletions src/services/decorators.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ angular.module('schemaForm').provider('schemaFormDecorators',
return sfErrorMessage.interpolate(
(schemaError && schemaError.code + '') || 'default',
(scope.ngModel && scope.ngModel.$modelValue) || '',
(scope.ngModel && scope.ngModel.$viewValue) || '',
scope.form,
scope.options && scope.options.validationMessage
);
Expand Down
38 changes: 25 additions & 13 deletions src/services/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,25 @@ angular.module('schemaForm').provider('sfErrorMessage', function() {
12: 'Data is valid against more than one schema from "oneOf"',
13: 'Data matches schema from "not"',
// Numeric errors
100: 'Value {{value}} is not a multiple of {{schema.multipleOf}}',
101: 'Value {{value}} is less than minimum {{schema.minimum}}',
102: 'Value {{value}} is equal to exclusive minimum {{schema.minimum}}',
103: 'Value {{value}} is greater than maximum {{schema.maximum}}',
104: 'Value {{value}} is equal to exclusive maximum {{schema.maximum}}',
105: 'Value {{value}} is not a valid number',
100: 'Value is not a multiple of {{schema.divisibleBy}}',
101: '{{viewValue}} is less than the allowed minimum of {{schema.minimum}}',
102: '{{viewValue}} is equal to the exclusive minimum {{schema.minimum}}',
103: '{{viewValue}} is greater than the allowed maximum of {{schema.maximum}}',
104: '{{viewValue}} is equal to the exclusive maximum {{schema.maximum}}',
105: 'Value is not a valid number',
// String errors
200: 'String is too short ({{value.length}} chars), minimum {{schema.minimum}}',
201: 'String is too long ({{value.length}} chars), maximum {{schema.maximum}}',
200: 'String is too short ({{viewValue.length}} chars), minimum {{schema.minLength}}',
201: 'String is too long ({{viewValue.length}} chars), maximum {{schema.maxLength}}',
202: 'String does not match pattern: {{schema.pattern}}',
// Object errors
300: 'Too few properties defined, minimum {{schema.minimum}}',
301: 'Too many properties defined, maximum {{schema.maximum}}',
300: 'Too few properties defined, minimum {{schema.minProperties}}',
301: 'Too many properties defined, maximum {{schema.maxProperties}}',
302: 'Required',
303: 'Additional properties not allowed',
304: 'Dependency failed - key must exist',
// Array errors
400: 'Array is too short ({{value.length}}), minimum {{schema.minimum}}',
401: 'Array is too long ({{value.length}}), maximum {{schema.maximum}}',
400: 'Array is too short ({{value.length}}), minimum {{schema.maxItems}}',
401: 'Array is too long ({{value.length}}), maximum {{schema.minItems}}',
402: 'Array items are not unique',
403: 'Additional items not allowed',
// Format errors
Expand All @@ -44,6 +44,15 @@ angular.module('schemaForm').provider('sfErrorMessage', function() {
1000: 'Unknown property (not in schema)'
};

// In some cases we get hit with an angular validation error
defaultMessages.number = defaultMessages[105];
defaultMessages.required = defaultMessages[302];
defaultMessages.min = defaultMessages[101];
defaultMessages.max = defaultMessages[103];
defaultMessages.maxlength = defaultMessages[201];
defaultMessages.minlength = defaultMessages[200];
defaultMessages.pattern = defaultMessages[202];

this.setDefaultMessages = function(messages) {
defaultMessages = messages;
};
Expand All @@ -68,12 +77,14 @@ angular.module('schemaForm').provider('sfErrorMessage', function() {
* @param {string} error the error code, i.e. tv4-xxx for tv4 errors, otherwise it's whats on
* ngModel.$error for custom errors.
* @param {Any} value the actual model value.
* @param {Any} viewValue the viewValue
* @param {Object} form a form definition object for this field
* @param {Object} global the global validation messages object (even though its called global
* its actually just shared in one instance of sf-schema)
* @return {string} The error message.
*/
service.interpolate = function(error, value, form, global) {
service.interpolate = function(error, value, viewValue, form, global) {
console.log(error, value, viewValue)
global = global || {};
var validationMessage = form.validationMessage || {};

Expand All @@ -99,6 +110,7 @@ angular.module('schemaForm').provider('sfErrorMessage', function() {
var context = {
error: error,
value: value,
viewValue: viewValue,
form: form,
schema: form.schema,
title: form.title || (form.schema && form.schema.title)
Expand Down
3 changes: 1 addition & 2 deletions test/protractor/conf.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
exports.config = {
seleniumAddress: 'http://localhost:4444/wd/hub',
specs: ['custom-validation.js']
seleniumAddress: 'http://localhost:4444/wd/hub'
}
File renamed without changes.
66 changes: 66 additions & 0 deletions test/protractor/specs/validation-messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/* global browser, it, describe, element, by */

describe('Schema Form validation messages', function() {

describe('#string', function() {
var URL = 'http://localhost:8080/examples/bootstrap-example.html#/86fb7505a8ab6a43bc70';

it('should not complain if it gets a normal string', function() {
browser.get(URL);
var input = element.all(by.css('form[name=ngform] input')).first();
input.sendKeys('string');

expect(input.getAttribute('value')).toEqual('string');
expect(input.evaluate('ngModel.$valid')).toEqual(true);

});


var validationMessageTestBuider = function(nr, value, validationMessage) {
it('should say "' + validationMessage + '" when input is ' + value, function() {
browser.get(URL);
var input = element.all(by.css('form[name=ngform] input')).get(nr);
input.sendKeys(value);

var message = element.all(by.css('form[name=ngform] div[sf-message]')).get(nr);
expect(input.evaluate('ngModel.$valid')).toEqual(false);
expect(message.getText()).toEqual(validationMessage);

});
};

var stringTests = {
's': 'String is too short (1 chars), minimum 3',
'tooo long string': 'String is too long (11 chars), maximum 10',
'foo 66': 'String does not match pattern: ^[a-zA-Z ]+$'
};

Object.keys(stringTests).forEach(function(value) {
validationMessageTestBuider(0, value, stringTests[value]);
});


var integerTests = {
'3': '3 is less than the allowed minimum of 6',
'66': '66 is greater than the allowed maximum of 50',
'11': 'Value is not a multiple of 3',
'aaa': 'Value is not a valid number'
};

Object.keys(integerTests).forEach(function(value) {
validationMessageTestBuider(1, value, integerTests[value]);
});


it('should say "Required" when fields are required', function() {
browser.get(URL);
element.all(by.css('form[name=ngform]')).submit();
var input = element.all(by.css('form[name=ngform] input')).get(1);

var message = element.all(by.css('form[name=ngform] div[sf-message]')).get(1);
expect(input.evaluate('ngModel.$valid')).toEqual(false);
expect(message.getText()).toEqual('Required');

});
});
});
13 changes: 12 additions & 1 deletion test/services/messages-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ describe('schemaFormServices', function() {
var result = sfErrorMessage.interpolate(
'foobar-error', //error
'foobar', //value
'foobar', //view value
{schema: {title: 'Foo'}}, //form
{'default': 'Oh noes!'}
);
Expand All @@ -25,6 +26,7 @@ describe('schemaFormServices', function() {
var result = sfErrorMessage.interpolate(
'foobar-error', //error
'foobar', //value
'foobar', //view value
{validationMessage: {'default': 'Oh yes!'}, schema: {title: 'Foo'}}, //form
{'default': 'Oh noes!'}
);
Expand All @@ -40,6 +42,7 @@ describe('schemaFormServices', function() {
var result = sfErrorMessage.interpolate(
'foobar-error', //error
'foobar', //value
'foobar', //view value
{schema: {title: 'Foo'}}, //form
{'default': 'Oh noes!', 'foobar-error': 'Aw chucks!'}
);
Expand All @@ -54,6 +57,7 @@ describe('schemaFormServices', function() {
var result = sfErrorMessage.interpolate(
'foobar-error', //error
'foobar', //value
'foobar', //view value
{schema: {title: 'Foo'}, validationMessage: {'foobar-error': 'Noooooo!'}}, //form
{'default': 'Oh noes!', 'foobar-error': 'Aw chucks!'}
);
Expand All @@ -68,6 +72,7 @@ describe('schemaFormServices', function() {
var result = sfErrorMessage.interpolate(
'foobar-error', //error
'foobar', //value
'foobar', //view value
{
schema: {title: 'Foo'},
validationMessage: {
Expand All @@ -87,6 +92,7 @@ describe('schemaFormServices', function() {
var result = sfErrorMessage.interpolate(
'foobar-error', //error
'foobar', //value
'foobar', //view value
{
schema: {title: 'Foo'},
validationMessage: {
Expand All @@ -101,6 +107,7 @@ describe('schemaFormServices', function() {
result = sfErrorMessage.interpolate(
'foobar-error', //error
'foobar', //value
'foobar', //view value
{
title: 'Bar',
schema: {title: 'Foo'},
Expand All @@ -121,6 +128,7 @@ describe('schemaFormServices', function() {
var result = sfErrorMessage.interpolate(
'foobar-error', //error
'foobar', //value
'foobar', //view value
{
schema: {title: 'Foo'},
validationMessage: 'Huh?'
Expand All @@ -140,6 +148,7 @@ describe('schemaFormServices', function() {
var result = sfErrorMessage.interpolate(
'foobar-error', //error
'foobar', //value
'foobar', //view value
{
schema: {title: 'Foo'},
validationMessage: {
Expand Down Expand Up @@ -174,6 +183,7 @@ describe('schemaFormServices', function() {
var result = sfErrorMessage.interpolate(
'foobar-error', //error
'foobar', //value
'foobar', //view value
{
schema: {title: 'Foo'},
validationMessage: msgFn
Expand Down Expand Up @@ -201,7 +211,8 @@ describe('schemaFormServices', function() {

var result = sfErrorMessage.interpolate(
'tv4-302', //error
'foobar', //value
'foobar', //value
'foobar', //view value
{
schema: {title: 'Foo'},
validationMessage: {302: 'tv4 error!'}
Expand Down

0 comments on commit 5700261

Please sign in to comment.