asynchronous custom keywords can define custom errors by returning the promise that rejects with Ajv.ValidationError, closes #118
parent
3aaeaf6ec0
commit
53a6c70138
16
CUSTOM.md
16
CUSTOM.md
|
@ -34,7 +34,7 @@ ajv.addKeyword('constant', { validate: function (schema, data) {
|
||||||
return typeof schema == 'object && schema !== null'
|
return typeof schema == 'object && schema !== null'
|
||||||
? deepEqual(schema, data)
|
? deepEqual(schema, data)
|
||||||
: schema === data;
|
: schema === data;
|
||||||
} });
|
}, errors: false });
|
||||||
|
|
||||||
var schema = { "constant": 2 };
|
var schema = { "constant": 2 };
|
||||||
var validate = ajv.compile(schema);
|
var validate = ajv.compile(schema);
|
||||||
|
@ -49,6 +49,10 @@ console.log(validate({foo: 'baz'})); // false
|
||||||
|
|
||||||
`constant` keyword is already available in Ajv with option `v5: true`.
|
`constant` keyword is already available in Ajv with option `v5: true`.
|
||||||
|
|
||||||
|
__Please note:__ If the keyword does not define custom errors (see [Reporting errors in custom keywords](#reporting-errors-in-custom-keywords)) pass `errors: false` in its definition; it will make generated code more efficient.
|
||||||
|
|
||||||
|
To add asynchronous keyword pass `async: true` in its definition.
|
||||||
|
|
||||||
|
|
||||||
### Define keyword with "compilation" function
|
### Define keyword with "compilation" function
|
||||||
|
|
||||||
|
@ -66,7 +70,7 @@ ajv.addKeyword('range', { type: 'number', compile: function (sch, parentSchema)
|
||||||
return parentSchema.exclusiveRange === true
|
return parentSchema.exclusiveRange === true
|
||||||
? function (data) { return data > min && data < max; }
|
? function (data) { return data > min && data < max; }
|
||||||
: function (data) { return data >= min && data <= max; }
|
: function (data) { return data >= min && data <= max; }
|
||||||
} });
|
}, errors: false });
|
||||||
|
|
||||||
var schema = { "range": [2, 4], "exclusiveRange": true };
|
var schema = { "range": [2, 4], "exclusiveRange": true };
|
||||||
var validate = ajv.compile(schema);
|
var validate = ajv.compile(schema);
|
||||||
|
@ -76,6 +80,8 @@ console.log(validate(2)); // false
|
||||||
console.log(validate(4)); // false
|
console.log(validate(4)); // false
|
||||||
```
|
```
|
||||||
|
|
||||||
|
See note on custom errors and asynchronous keywords in the previous section.
|
||||||
|
|
||||||
|
|
||||||
### Define keyword with "macro" function
|
### Define keyword with "macro" function
|
||||||
|
|
||||||
|
@ -310,9 +316,9 @@ Converts the JSON-Pointer fragment from URI to the property name.
|
||||||
|
|
||||||
## Reporting errors in custom keywords
|
## Reporting errors in custom keywords
|
||||||
|
|
||||||
All custom keywords but macro keywords can create custom error messages.
|
All custom keywords but macro keywords can optionally create custom error messages.
|
||||||
|
|
||||||
Validating and compiled keywords should define errors by assigning them to `.errors` property of the validation function. It should not be done for asynchronous keywords (see #118).
|
Synchronous validating and compiled keywords should define errors by assigning them to `.errors` property of the validation function. Asynchronous keywords can return promise that rejects with `new Ajv.ValidationError(errors)`, where `errors` is an array of custom validation errors (if you don't want to define custom errors in asynchronous keyword, its validation function can return the promise that resolves with `false`).
|
||||||
|
|
||||||
Inline custom keyword should increase error counter `errors` and add error to `vErrors` array (it can be null). This can be done for both synchronous and asynchronous keywords. See [example range keyword](https://github.com/epoberezkin/ajv/blob/master/spec/custom_rules/range_with_errors.jst).
|
Inline custom keyword should increase error counter `errors` and add error to `vErrors` array (it can be null). This can be done for both synchronous and asynchronous keywords. See [example range keyword](https://github.com/epoberezkin/ajv/blob/master/spec/custom_rules/range_with_errors.jst).
|
||||||
|
|
||||||
|
@ -331,7 +337,7 @@ ajv.addKeyword('range', {
|
||||||
|
|
||||||
Each error object should at least have properties `keyword`, `message` and `params`, other properties will be added.
|
Each error object should at least have properties `keyword`, `message` and `params`, other properties will be added.
|
||||||
|
|
||||||
Inlined keywords can optionally define `dataPath` property in error objects, that will be added by ajv unless `errors` option of the keyword is `"full"`.
|
Inlined keywords can optionally define `dataPath` and `schemaPath` properties in error objects, that will be assigned by Ajv unless `errors` option of the keyword is `"full"`.
|
||||||
|
|
||||||
If custom keyword doesn't create errors, the default error will be created in case the keyword fails validation (see [Validation errors](#validation-errors)).
|
If custom keyword doesn't create errors, the default error will be created in case the keyword fails validation (see [Validation errors](#validation-errors)).
|
||||||
|
|
||||||
|
|
|
@ -314,13 +314,13 @@ __Please note__: [Option](#options) `missingRefs` should NOT be set to `"ignore"
|
||||||
|
|
||||||
Example in node REPL: https://tonicdev.com/esp/ajv-asynchronous-validation
|
Example in node REPL: https://tonicdev.com/esp/ajv-asynchronous-validation
|
||||||
|
|
||||||
Starting from version 3.5.0 you can define custom formats and keywords that perform validation asyncronously by accessing database or some service. You should add `async: true` in the keyword or format defnition (see [addFormat](#api-addformat) and [addKeyword](#api-addkeyword)).
|
Starting from version 3.5.0 you can define custom formats and keywords that perform validation asyncronously by accessing database or some service. You should add `async: true` in the keyword or format defnition (see [addFormat](#api-addformat), [addKeyword](#api-addkeyword) and [Defining custom keywords](#defining-custom-keywords)).
|
||||||
|
|
||||||
If your schema uses asynchronous formats/keywords or refers to some schema that contains them it should have `"$async": true` keyword so that Ajv can compile it correctly. If asynchronous format/keyword or reference to asynchronous schema is used in the schema without `$async` keyword Ajv will throw an exception during schema compilation.
|
If your schema uses asynchronous formats/keywords or refers to some schema that contains them it should have `"$async": true` keyword so that Ajv can compile it correctly. If asynchronous format/keyword or reference to asynchronous schema is used in the schema without `$async` keyword Ajv will throw an exception during schema compilation.
|
||||||
|
|
||||||
__Please note__: all asynchronous subschemas that are referenced from the current or other schemas should have `"$async": true` keyword as well, otherwise the schema compilation will fail.
|
__Please note__: all asynchronous subschemas that are referenced from the current or other schemas should have `"$async": true` keyword as well, otherwise the schema compilation will fail.
|
||||||
|
|
||||||
Validation function for an asynchronous custom format/keyword should return a promise that resolves to `true` or `false`. Ajv compiles asynchronous schemas to either [generator function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*) (default) that can be optionally transpiled with [regenerator](https://github.com/facebook/regenerator) or to [es7 async function](http://tc39.github.io/ecmascript-asyncawait/) that can be transpiled with [nodent](https://github.com/MatAtBread/nodent) or with regenerator as well. You can also supply any other transpiler as a function. See [Options](#options).
|
Validation function for an asynchronous custom format/keyword should return a promise that resolves to `true` or `false` (or rejects with `new Ajv.ValidationError(errors)` if you want to return custom errors from the keyword function). Ajv compiles asynchronous schemas to either [generator function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*) (default) that can be optionally transpiled with [regenerator](https://github.com/facebook/regenerator) or to [es7 async function](http://tc39.github.io/ecmascript-asyncawait/) that can be transpiled with [nodent](https://github.com/MatAtBread/nodent) or with regenerator as well. You can also supply any other transpiler as a function. See [Options](#options).
|
||||||
|
|
||||||
The compiled validation function has `async: true` property (if the schema is asynchronous), so you can differentiate these functions if you are using both syncronous and asynchronous schemas.
|
The compiled validation function has `async: true` property (if the schema is asynchronous), so you can differentiate these functions if you are using both syncronous and asynchronous schemas.
|
||||||
|
|
||||||
|
|
|
@ -18,18 +18,10 @@
|
||||||
|
|
||||||
{{? !($inline || $macro) }}{{=$ruleErrs}} = null;{{?}}
|
{{? !($inline || $macro) }}{{=$ruleErrs}} = null;{{?}}
|
||||||
var {{=$errs}} = errors;
|
var {{=$errs}} = errors;
|
||||||
|
var valid{{=$lvl}};
|
||||||
|
|
||||||
{{## def.callRuleValidate:
|
{{## def.callRuleValidate:
|
||||||
{{? $inline }}
|
{{=$ruleValidate.code}}.call(
|
||||||
{{? $rDef.statements }}
|
|
||||||
valid{{=$lvl}}
|
|
||||||
{{??}}
|
|
||||||
({{= $ruleValidate.validate }})
|
|
||||||
{{?}}
|
|
||||||
{{?? $macro }}
|
|
||||||
valid{{=$it.level}}
|
|
||||||
{{??}}
|
|
||||||
{{?$asyncKeyword}}{{=it.yieldAwait}} {{?}}{{=$ruleValidate.code}}.call(
|
|
||||||
{{? it.opts.passContext }}this{{??}}self{{?}}
|
{{? it.opts.passContext }}this{{??}}self{{?}}
|
||||||
{{ var $validateArgs = $ruleValidate.validate.length; }}
|
{{ var $validateArgs = $ruleValidate.validate.length; }}
|
||||||
{{? $rDef.compile || $rDef.schema === false }}
|
{{? $rDef.compile || $rDef.schema === false }}
|
||||||
|
@ -42,6 +34,27 @@ var {{=$errs}} = errors;
|
||||||
, {{# def.dataPath }}
|
, {{# def.dataPath }}
|
||||||
{{# def.passParentData }}
|
{{# def.passParentData }}
|
||||||
)
|
)
|
||||||
|
#}}
|
||||||
|
|
||||||
|
{{## def.ruleValidationResult:
|
||||||
|
{{? $inline }}
|
||||||
|
{{? $rDef.statements }}
|
||||||
|
valid{{=$lvl}}
|
||||||
|
{{??}}
|
||||||
|
({{= $ruleValidate.validate }})
|
||||||
|
{{?}}
|
||||||
|
{{?? $macro }}
|
||||||
|
valid{{=$it.level}}
|
||||||
|
{{??}}
|
||||||
|
{{? $asyncKeyword }}
|
||||||
|
{{? $rDef.errors === false }}
|
||||||
|
({{=it.yieldAwait}}{{= def_callRuleValidate }})
|
||||||
|
{{??}}
|
||||||
|
valid{{=$lvl}}
|
||||||
|
{{?}}
|
||||||
|
{{??}}
|
||||||
|
{{= def_callRuleValidate }}
|
||||||
|
{{?}}
|
||||||
{{?}}
|
{{?}}
|
||||||
#}}
|
#}}
|
||||||
|
|
||||||
|
@ -51,6 +64,9 @@ var {{=$errs}} = errors;
|
||||||
{{# _inline ? 'if (\{\{=$ruleErr\}\}.dataPath === undefined) {' : '' }}
|
{{# _inline ? 'if (\{\{=$ruleErr\}\}.dataPath === undefined) {' : '' }}
|
||||||
{{=$ruleErr}}.dataPath = (dataPath || '') + {{= it.errorPath }};
|
{{=$ruleErr}}.dataPath = (dataPath || '') + {{= it.errorPath }};
|
||||||
{{# _inline ? '}' : '' }}
|
{{# _inline ? '}' : '' }}
|
||||||
|
{{# _inline ? 'if (\{\{=$ruleErr\}\}.schemaPath === undefined) {' : '' }}
|
||||||
|
{{=$ruleErr}}.schemaPath = "{{=$errSchemaPath}}";
|
||||||
|
{{# _inline ? '}' : '' }}
|
||||||
{{? it.opts.verbose }}
|
{{? it.opts.verbose }}
|
||||||
{{=$ruleErr}}.schema = validate.schema{{=$schemaPath}};
|
{{=$ruleErr}}.schema = validate.schema{{=$schemaPath}};
|
||||||
{{=$ruleErr}}.data = {{=$data}};
|
{{=$ruleErr}}.data = {{=$data}};
|
||||||
|
@ -70,9 +86,30 @@ var {{=$errs}} = errors;
|
||||||
{{ var $code = it.validate($it).replace(/validate\.schema/g, $ruleValidate.code); }}
|
{{ var $code = it.validate($it).replace(/validate\.schema/g, $ruleValidate.code); }}
|
||||||
{{# def.resetCompositeRule }}
|
{{# def.resetCompositeRule }}
|
||||||
{{= $code }}
|
{{= $code }}
|
||||||
|
{{?? $rDef.compile || $rDef.validate }}
|
||||||
|
{{# def.beginDefOut}}
|
||||||
|
{{# def.callRuleValidate }}
|
||||||
|
{{# def.storeDefOut:def_callRuleValidate }}
|
||||||
|
|
||||||
|
{{? $rDef.errors !== false }}
|
||||||
|
{{? $asyncKeyword }}
|
||||||
|
{{ $ruleErrs = 'customErrors' + $lvl; }}
|
||||||
|
var {{=$ruleErrs}} = null;
|
||||||
|
try {
|
||||||
|
valid{{=$lvl}} = {{=it.yieldAwait}}{{= def_callRuleValidate }};
|
||||||
|
} catch (e) {
|
||||||
|
valid{{=$lvl}} = false;
|
||||||
|
if (e instanceof ValidationError) {{=$ruleErrs}} = e.errors;
|
||||||
|
else throw e;
|
||||||
|
}
|
||||||
|
{{??}}
|
||||||
|
{{=$ruleValidate.code}}.errors = null;
|
||||||
|
{{?}}
|
||||||
|
{{?}}
|
||||||
{{?}}
|
{{?}}
|
||||||
|
|
||||||
if (!({{# def.callRuleValidate }})) {
|
|
||||||
|
if (!{{# def.ruleValidationResult }}) {
|
||||||
{{ $errorKeyword = $rule.keyword; }}
|
{{ $errorKeyword = $rule.keyword; }}
|
||||||
{{# def.beginDefOut}}
|
{{# def.beginDefOut}}
|
||||||
{{# def.error:'custom' }}
|
{{# def.error:'custom' }}
|
||||||
|
@ -96,6 +133,9 @@ if (!({{# def.callRuleValidate }})) {
|
||||||
{{?}}
|
{{?}}
|
||||||
{{?? $macro}}
|
{{?? $macro}}
|
||||||
{{# def.extraError:'custom' }}
|
{{# def.extraError:'custom' }}
|
||||||
|
{{??}}
|
||||||
|
{{? $rDef.errors === false}}
|
||||||
|
{{= def_customError }}
|
||||||
{{??}}
|
{{??}}
|
||||||
if (Array.isArray({{=$ruleErrs}})) {
|
if (Array.isArray({{=$ruleErrs}})) {
|
||||||
if (vErrors === null) vErrors = {{=$ruleErrs}};
|
if (vErrors === null) vErrors = {{=$ruleErrs}};
|
||||||
|
@ -106,6 +146,7 @@ if (!({{# def.callRuleValidate }})) {
|
||||||
{{= def_customError }}
|
{{= def_customError }}
|
||||||
}
|
}
|
||||||
{{?}}
|
{{?}}
|
||||||
|
{{?}}
|
||||||
{{ $errorKeyword = undefined; }}
|
{{ $errorKeyword = undefined; }}
|
||||||
|
|
||||||
} {{? $breakOnError }} else { {{?}}
|
} {{? $breakOnError }} else { {{?}}
|
||||||
|
|
|
@ -47,6 +47,54 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "async custom keywords (validated with errors)",
|
||||||
|
"schema": {
|
||||||
|
"$async": true,
|
||||||
|
"properties": {
|
||||||
|
"userId": {
|
||||||
|
"type": "integer",
|
||||||
|
"idExistsWithError": { "table": "users" }
|
||||||
|
},
|
||||||
|
"postId": {
|
||||||
|
"type": "integer",
|
||||||
|
"idExistsWithError": { "table": "posts" }
|
||||||
|
},
|
||||||
|
"categoryId": {
|
||||||
|
"description": "will throw if present, no such table",
|
||||||
|
"type": "integer",
|
||||||
|
"idExistsWithError": { "table": "categories" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"description": "valid object",
|
||||||
|
"data": { "userId": 1, "postId": 21 },
|
||||||
|
"valid": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "another valid object",
|
||||||
|
"data": { "userId": 5, "postId": 25 },
|
||||||
|
"valid": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "invalid - no such post",
|
||||||
|
"data": { "userId": 5, "postId": 10 },
|
||||||
|
"valid": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "invalid - no such user",
|
||||||
|
"data": { "userId": 9, "postId": 25 },
|
||||||
|
"valid": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "should throw exception during validation - no such table",
|
||||||
|
"data": { "postId": 25, "categoryId": 1 },
|
||||||
|
"error": "no such table"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "async custom keywords (compiled)",
|
"description": "async custom keywords (compiled)",
|
||||||
"schema": {
|
"schema": {
|
||||||
|
|
|
@ -3,11 +3,13 @@
|
||||||
var jsonSchemaTest = require('json-schema-test')
|
var jsonSchemaTest = require('json-schema-test')
|
||||||
, Promise = require('./promise')
|
, Promise = require('./promise')
|
||||||
, getAjvInstances = require('./ajv_async_instances')
|
, getAjvInstances = require('./ajv_async_instances')
|
||||||
, assert = require('./chai').assert;
|
, assert = require('./chai').assert
|
||||||
|
, Ajv = require('./ajv');
|
||||||
|
|
||||||
|
|
||||||
var instances = getAjvInstances({ v5: true });
|
var instances = getAjvInstances({ v5: true });
|
||||||
|
|
||||||
|
|
||||||
instances.forEach(addAsyncFormatsAndKeywords);
|
instances.forEach(addAsyncFormatsAndKeywords);
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,7 +59,15 @@ function addAsyncFormatsAndKeywords (ajv) {
|
||||||
ajv.addKeyword('idExists', {
|
ajv.addKeyword('idExists', {
|
||||||
async: true,
|
async: true,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
validate: checkIdExists
|
validate: checkIdExists,
|
||||||
|
errors: false
|
||||||
|
});
|
||||||
|
|
||||||
|
ajv.addKeyword('idExistsWithError', {
|
||||||
|
async: true,
|
||||||
|
type: 'number',
|
||||||
|
validate: checkIdExistsWithError,
|
||||||
|
errors: true
|
||||||
});
|
});
|
||||||
|
|
||||||
ajv.addKeyword('idExistsCompiled', {
|
ajv.addKeyword('idExistsCompiled', {
|
||||||
|
@ -88,6 +98,28 @@ function checkIdExists(schema, data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function checkIdExistsWithError(schema, data) {
|
||||||
|
var table = schema.table;
|
||||||
|
switch (table) {
|
||||||
|
case 'users': return check(table, [1, 5, 8]);
|
||||||
|
case 'posts': return check(table, [21, 25, 28]);
|
||||||
|
default: throw new Error('no such table');
|
||||||
|
}
|
||||||
|
|
||||||
|
function check(table, IDs) {
|
||||||
|
if (IDs.indexOf(data) >= 0) {
|
||||||
|
return Promise.resolve(true);
|
||||||
|
} else {
|
||||||
|
var error = {
|
||||||
|
keyword: 'idExistsWithError',
|
||||||
|
message: 'id not found in table ' + table
|
||||||
|
};
|
||||||
|
return Promise.reject(new Ajv.ValidationError([error]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function compileCheckIdExists(schema) {
|
function compileCheckIdExists(schema) {
|
||||||
switch (schema.table) {
|
switch (schema.table) {
|
||||||
case 'users': return compileCheck([1, 5, 8]);
|
case 'users': return compileCheck([1, 5, 8]);
|
||||||
|
|
|
@ -94,7 +94,15 @@ describe('async schemas, formats and keywords', function() {
|
||||||
ajv.addKeyword('idExists', {
|
ajv.addKeyword('idExists', {
|
||||||
async: true,
|
async: true,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
validate: checkIdExists
|
validate: checkIdExists,
|
||||||
|
errors: false
|
||||||
|
});
|
||||||
|
|
||||||
|
ajv.addKeyword('idExistsWithError', {
|
||||||
|
async: true,
|
||||||
|
type: 'number',
|
||||||
|
validate: checkIdExistsWithError,
|
||||||
|
errors: true
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -122,6 +130,34 @@ describe('async schemas, formats and keywords', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should return custom error', function() {
|
||||||
|
return Promise.all(instances.map(function (ajv) {
|
||||||
|
var schema = {
|
||||||
|
$async: true,
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
userId: {
|
||||||
|
type: 'integer',
|
||||||
|
idExistsWithError: { table: 'users' }
|
||||||
|
},
|
||||||
|
postId: {
|
||||||
|
type: 'integer',
|
||||||
|
idExistsWithError: { table: 'posts' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var validate = ajv.compile(schema);
|
||||||
|
var _co = useCo(ajv);
|
||||||
|
|
||||||
|
return Promise.all([
|
||||||
|
shouldBeInvalid(_co(validate({ userId: 5, postId: 10 })), [ 'id not found in table posts' ]),
|
||||||
|
shouldBeInvalid(_co(validate({ userId: 9, postId: 25 })), [ 'id not found in table users' ])
|
||||||
|
]);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
function checkIdExists(schema, data) {
|
function checkIdExists(schema, data) {
|
||||||
switch (schema.table) {
|
switch (schema.table) {
|
||||||
case 'users': return check([1, 5, 8]);
|
case 'users': return check([1, 5, 8]);
|
||||||
|
@ -133,6 +169,27 @@ describe('async schemas, formats and keywords', function() {
|
||||||
return Promise.resolve(IDs.indexOf(data) >= 0);
|
return Promise.resolve(IDs.indexOf(data) >= 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkIdExistsWithError(schema, data) {
|
||||||
|
var table = schema.table;
|
||||||
|
switch (table) {
|
||||||
|
case 'users': return check(table, [1, 5, 8]);
|
||||||
|
case 'posts': return check(table, [21, 25, 28]);
|
||||||
|
default: throw new Error('no such table');
|
||||||
|
}
|
||||||
|
|
||||||
|
function check(table, IDs) {
|
||||||
|
if (IDs.indexOf(data) >= 0) {
|
||||||
|
return Promise.resolve(true);
|
||||||
|
} else {
|
||||||
|
var error = {
|
||||||
|
keyword: 'idExistsWithError',
|
||||||
|
message: 'id not found in table ' + table
|
||||||
|
};
|
||||||
|
return Promise.reject(new Ajv.ValidationError([error]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -366,12 +423,18 @@ function shouldBeValid(p) {
|
||||||
|
|
||||||
|
|
||||||
var SHOULD_BE_INVALID = 'test: should be invalid';
|
var SHOULD_BE_INVALID = 'test: should be invalid';
|
||||||
function shouldBeInvalid(p) {
|
function shouldBeInvalid(p, expectedMessages) {
|
||||||
return checkNotValid(p)
|
return checkNotValid(p)
|
||||||
.then(function (err) {
|
.then(function (err) {
|
||||||
err .should.be.instanceof(Ajv.ValidationError);
|
err .should.be.instanceof(Ajv.ValidationError);
|
||||||
err.errors .should.be.an('array');
|
err.errors .should.be.an('array');
|
||||||
err.validation .should.equal(true);
|
err.validation .should.equal(true);
|
||||||
|
if (expectedMessages) {
|
||||||
|
var messages = err.errors.map(function (e) {
|
||||||
|
return e.message;
|
||||||
|
});
|
||||||
|
messages .should.eql(expectedMessages);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -512,9 +512,9 @@ describe('Custom keywords', function () {
|
||||||
shouldBeValid(validate, 'abc');
|
shouldBeValid(validate, 'abc');
|
||||||
|
|
||||||
shouldBeInvalid(validate, 1.99, numErrors);
|
shouldBeInvalid(validate, 1.99, numErrors);
|
||||||
if (customErrors) shouldBeRangeError(validate.errors[0], '', '>=', 2);
|
if (customErrors) shouldBeRangeError(validate.errors[0], '', '#/range', '>=', 2);
|
||||||
shouldBeInvalid(validate, 4.01, numErrors);
|
shouldBeInvalid(validate, 4.01, numErrors);
|
||||||
if (customErrors) shouldBeRangeError(validate.errors[0], '', '<=', 4);
|
if (customErrors) shouldBeRangeError(validate.errors[0], '', '#/range','<=', 4);
|
||||||
|
|
||||||
var schema = {
|
var schema = {
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -531,9 +531,9 @@ describe('Custom keywords', function () {
|
||||||
shouldBeValid(validate, { foo: 3.99 });
|
shouldBeValid(validate, { foo: 3.99 });
|
||||||
|
|
||||||
shouldBeInvalid(validate, { foo: 2 }, numErrors);
|
shouldBeInvalid(validate, { foo: 2 }, numErrors);
|
||||||
if (customErrors) shouldBeRangeError(validate.errors[0], '.foo', '>', 2, true);
|
if (customErrors) shouldBeRangeError(validate.errors[0], '.foo', '#/properties/foo/range', '>', 2, true);
|
||||||
shouldBeInvalid(validate, { foo: 4 }, numErrors);
|
shouldBeInvalid(validate, { foo: 4 }, numErrors);
|
||||||
if (customErrors) shouldBeRangeError(validate.errors[0], '.foo', '<', 4, true);
|
if (customErrors) shouldBeRangeError(validate.errors[0], '.foo', '#/properties/foo/range', '<', 4, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -562,12 +562,13 @@ describe('Custom keywords', function () {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldBeRangeError(error, dataPath, comparison, limit, exclusive) {
|
function shouldBeRangeError(error, dataPath, schemaPath, comparison, limit, exclusive) {
|
||||||
delete error.schema;
|
delete error.schema;
|
||||||
delete error.data;
|
delete error.data;
|
||||||
error .should.eql({
|
error .should.eql({
|
||||||
keyword: 'range',
|
keyword: 'range',
|
||||||
dataPath: dataPath,
|
dataPath: dataPath,
|
||||||
|
schemaPath: schemaPath,
|
||||||
message: 'should be ' + comparison + ' ' + limit,
|
message: 'should be ' + comparison + ' ' + limit,
|
||||||
params: {
|
params: {
|
||||||
comparison: comparison,
|
comparison: comparison,
|
||||||
|
|
Loading…
Reference in New Issue