Merge branch 'master' into beta

master
Evgeny Poberezkin 2017-11-24 21:18:06 +00:00
commit 04d62ae67e
15 changed files with 332 additions and 86 deletions

129
CUSTOM.md
View File

@ -42,18 +42,27 @@ __Please note__: In cases when validation flow is different depending on the sch
Example. `constant` keyword (a synonym for draft6 keyword `const`, it is equivalent to `enum` keyword with one item):
```javascript
ajv.addKeyword('constant', { validate: function (schema, data) {
return typeof schema == 'object' && schema !== null
? deepEqual(schema, data)
: schema === data;
}, errors: false });
ajv.addKeyword('constant', {
validate: function (schema, data) {
return typeof schema == 'object' && schema !== null
? deepEqual(schema, data)
: schema === data;
},
errors: false
});
var schema = { "constant": 2 };
var schema = {
"constant": 2
};
var validate = ajv.compile(schema);
console.log(validate(2)); // true
console.log(validate(3)); // false
var schema = { "constant": { "foo": "bar" } };
var schema = {
"constant": {
"foo": "bar"
}
};
var validate = ajv.compile(schema);
console.log(validate({foo: 'bar'})); // true
console.log(validate({foo: 'baz'})); // false
@ -86,20 +95,31 @@ All custom keywords types can have an optional `metaSchema` property in their de
Example. `range` and `exclusiveRange` keywords using compiled schema:
```javascript
ajv.addKeyword('range', { type: 'number', compile: function (sch, parentSchema) {
var min = sch[0];
var max = sch[1];
ajv.addKeyword('range', {
type: 'number',
compile: function (sch, parentSchema) {
var min = sch[0];
var max = sch[1];
return parentSchema.exclusiveRange === true
? function (data) { return data > min && data < max; }
: function (data) { return data >= min && data <= max; }
}, errors: false, metaSchema: {
type: 'array',
items: [ { type: 'number' }, { type: 'number' } ],
additionalItems: false
} });
return parentSchema.exclusiveRange === true
? function (data) { return data > min && data < max; }
: function (data) { return data >= min && data <= max; }
},
errors: false,
metaSchema: {
type: 'array',
items: [
{ type: 'number' },
{ type: 'number' }
],
additionalItems: false
}
});
var schema = { "range": [2, 4], "exclusiveRange": true };
var schema = {
"range": [2, 4],
"exclusiveRange": true
};
var validate = ajv.compile(schema);
console.log(validate(2.01)); // true
console.log(validate(3.99)); // true
@ -122,27 +142,30 @@ In addition to the errors from the expanded schema macro keyword will add its ow
Example. `range` and `exclusiveRange` keywords from the previous example defined with macro:
```javascript
ajv.addKeyword('range', { type: 'number', macro: function (schema, parentSchema) {
return {
minimum: schema[0],
maximum: schema[1],
exclusiveMinimum: !!parentSchema.exclusiveRange,
exclusiveMaximum: !!parentSchema.exclusiveRange
};
}, metaSchema: {
type: 'array',
items: [ { type: 'number' }, { type: 'number' } ],
additionalItems: false
} });
ajv.addKeyword('range', {
type: 'number',
macro: function (schema, parentSchema) {
return {
minimum: schema[0],
maximum: schema[1],
exclusiveMinimum: !!parentSchema.exclusiveRange,
exclusiveMaximum: !!parentSchema.exclusiveRange
};
},
metaSchema: {
type: 'array',
items: [
{ type: 'number' },
{ type: 'number' }
],
additionalItems: false
}
});
```
Example. `contains` keyword from version 5 proposals that requires that the array has at least one item matching schema (see https://github.com/json-schema/json-schema/wiki/contains-(v5-proposal)):
```javascript
ajv.addKeyword('contains', { type: 'array', macro: function (schema) {
return { "not": { "items": { "not": schema } } };
} });
var schema = {
"contains": {
"type": "number",
@ -151,7 +174,20 @@ var schema = {
}
};
var validate = ajv.compile(schema);
var validate = ajv.addKeyword('contains', {
type: 'array',
macro: function (schema) {
return {
"not": {
"items": {
"not": schema
}
}
};
}
})
.compile(schema);
console.log(validate([1,2,3])); // false
console.log(validate([2,3,4])); // false
console.log(validate([3,4,5])); // true, number 5 matches schema inside "contains"
@ -177,14 +213,18 @@ While it can be more challenging to define keywords with "inline" functions, it
Example `even` keyword:
```javascript
ajv.addKeyword('even', { type: 'number', inline: function (it, keyword, schema) {
var op = schema ? '===' : '!==';
return 'data' + (it.dataLevel || '') + ' % 2 ' + op + ' 0';
}, metaSchema: { type: 'boolean' } });
var schema = { "even": true };
var validate = ajv.compile(schema);
var validate = ajv.addKeyword('even', {
type: 'number',
inline: function (it, keyword, schema) {
var op = schema ? '===' : '!==';
return 'data' + (it.dataLevel || '') + ' % 2 ' + op + ' 0';
},
metaSchema: { type: 'boolean' }
})
.compile(schema);
console.log(validate(2)); // true
console.log(validate(3)); // false
```
@ -214,7 +254,10 @@ ajv.addKeyword('range', {
statements: true,
metaSchema: {
type: 'array',
items: [ { type: 'number' }, { type: 'number' } ],
items: [
{ type: 'number' },
{ type: 'number' }
],
additionalItems: false
}
});

View File

@ -49,7 +49,7 @@ The keywords and their values define what rules the data should satisfy to be va
`type` keyword requires that the data is of certain type (or some of types). Its value can be a string (the allowed type) or an array of strings (multiple allowed types).
Type can be: number, integer, string, boolean, array, object or null.
Type can be: `number`, `integer`, `string`, `boolean`, `array`, `object` or `null`.
__Examples__

View File

@ -7,8 +7,8 @@ The fastest JSON Schema validator for Node.js and browser with draft-07 support.
[![Build Status](https://travis-ci.org/epoberezkin/ajv.svg?branch=master)](https://travis-ci.org/epoberezkin/ajv)
[![npm version](https://badge.fury.io/js/ajv.svg)](https://www.npmjs.com/package/ajv)
[![npm@beta](https://img.shields.io/npm/v/ajv/beta.svg)](https://github.com/epoberezkin/ajv/tree/beta)
[![npm downloads](https://img.shields.io/npm/dm/ajv.svg)](https://www.npmjs.com/package/ajv)
[![Code Climate](https://codeclimate.com/github/epoberezkin/ajv/badges/gpa.svg)](https://codeclimate.com/github/epoberezkin/ajv)
[![Coverage Status](https://coveralls.io/repos/epoberezkin/ajv/badge.svg?branch=master&service=github)](https://coveralls.io/github/epoberezkin/ajv?branch=master)
[![Greenkeeper badge](https://badges.greenkeeper.io/epoberezkin/ajv.svg)](https://greenkeeper.io/)
[![Gitter](https://img.shields.io/gitter/room/ajv-validator/ajv.svg)](https://gitter.im/ajv-validator/ajv)
@ -110,6 +110,12 @@ Currently Ajv is the only validator that passes all the tests from [JSON Schema
npm install ajv
```
or to install [version 6](https://github.com/epoberezkin/ajv/tree/beta):
```
npm install ajv@beta
```
## <a name="usage"></a>Getting started
@ -139,8 +145,8 @@ or
```javascript
// ...
ajv.addSchema(schema, 'mySchema');
var valid = ajv.validate('mySchema', data);
var valid = ajv.addSchema(schema, 'mySchema')
.validate('mySchema', data);
if (!valid) console.log(ajv.errorsText());
// ...
```
@ -293,8 +299,8 @@ or use `addSchema` method:
```javascript
var ajv = new Ajv;
ajv.addSchema(defsSchema);
var validate = ajv.compile(schema);
var validate = ajv.addSchema(defsSchema)
.compile(schema);
```
See [Options](#options) and [addSchema](#api) method.
@ -450,14 +456,17 @@ Ajv allows defining keywords with:
Example. `range` and `exclusiveRange` keywords using compiled schema:
```javascript
ajv.addKeyword('range', { type: 'number', compile: function (sch, parentSchema) {
var min = sch[0];
var max = sch[1];
ajv.addKeyword('range', {
type: 'number',
compile: function (sch, parentSchema) {
var min = sch[0];
var max = sch[1];
return parentSchema.exclusiveRange === true
? function (data) { return data > min && data < max; }
: function (data) { return data >= min && data <= max; }
} });
return parentSchema.exclusiveRange === true
? function (data) { return data > min && data < max; }
: function (data) { return data >= min && data <= max; }
}
});
var schema = { "range": [2, 4], "exclusiveRange": true };
var validate = ajv.compile(schema);
@ -869,7 +878,7 @@ __Please note__: every time this method is called the errors are overwritten so
If the schema is asynchronous (has `$async` keyword on the top level) this method returns a Promise. See [Asynchronous validation](#asynchronous-validation).
##### .addSchema(Array&lt;Object&gt;|Object schema [, String key])
##### .addSchema(Array&lt;Object&gt;|Object schema [, String key]) -&gt; Ajv
Add schema(s) to validator instance. This method does not compile schemas (but it still validates them). Because of that dependencies can be added in any order and circular dependencies are supported. It also prevents unnecessary compilation of schemas that are containers for other schemas but not used as a whole.
@ -884,8 +893,14 @@ Although `addSchema` does not compile schemas, explicit compilation is not requi
By default the schema is validated against meta-schema before it is added, and if the schema does not pass validation the exception is thrown. This behaviour is controlled by `validateSchema` option.
__Please note__: Ajv uses the [method chaining syntax](https://en.wikipedia.org/wiki/Method_chaining) for all methods with the prefix `add*` and `remove*`.
This allows you to do nice things like the following.
##### .addMetaSchema(Array&lt;Object&gt;|Object schema [, String key])
```javascript
var validate = new Ajv().addSchema(schema).addFormat(name, regex).getSchema(uri);
```
##### .addMetaSchema(Array&lt;Object&gt;|Object schema [, String key]) -&gt; Ajv
Adds meta schema(s) that can be used to validate other schemas. That function should be used instead of `addSchema` because there may be instance options that would compile a meta schema incorrectly (at the moment it is `removeAdditional` option).
@ -910,7 +925,7 @@ Errors will be available at `ajv.errors`.
Retrieve compiled schema previously added with `addSchema` by the key passed to `addSchema` or by its full reference (id). The returned validating function has `schema` property with the reference to the original schema.
##### .removeSchema([Object schema|String key|String ref|RegExp pattern])
##### .removeSchema([Object schema|String key|String ref|RegExp pattern]) -&gt; Ajv
Remove added/cached schema. Even if schema is referenced by other schemas it can be safely removed as dependent schemas have local references.
@ -923,7 +938,7 @@ Schema can be removed using:
If no parameter is passed all schemas but meta-schemas will be removed and the cache will be cleared.
##### <a name="api-addformat"></a>.addFormat(String name, String|RegExp|Function|Object format)
##### <a name="api-addformat"></a>.addFormat(String name, String|RegExp|Function|Object format) -&gt; Ajv
Add custom format to validate strings or numbers. It can also be used to replace pre-defined formats for Ajv instance.
@ -941,7 +956,7 @@ If object is passed it should have properties `validate`, `compare` and `async`:
Custom formats can be also added via `formats` option.
##### <a name="api-addkeyword"></a>.addKeyword(String keyword, Object definition)
##### <a name="api-addkeyword"></a>.addKeyword(String keyword, Object definition) -&gt; Ajv
Add custom validation keyword to Ajv instance.
@ -982,7 +997,7 @@ See [Defining custom keywords](#defining-custom-keywords) for more details.
Returns custom keyword definition, `true` for pre-defined keywords and `false` if the keyword is unknown.
##### .removeKeyword(String keyword)
##### .removeKeyword(String keyword) -&gt; Ajv
Removes custom or pre-defined keyword so you can redefine them.
@ -1016,6 +1031,7 @@ Defaults:
formats: {},
unknownFormats: true,
schemas: {},
logger: undefined,
// referenced schema options:
schemaId: undefined // recommended '$id'
missingRefs: true,
@ -1064,6 +1080,9 @@ Defaults:
- `[String]` - an array of unknown format names that will be ignored. This option can be used to allow usage of third party schemas with format(s) for which you don't have definitions, but still fail if another unknown format is used. If `format` keyword value is [$data reference](#data-reference) and it is not in this array the validation will fail.
- `"ignore"` - to log warning during schema compilation and always pass validation (the default behaviour in versions before 5.0.0). This option is not recommended, as it allows to mistype format name and it won't be validated without any error message. This behaviour is required by JSON-schema specification.
- _schemas_: an array or object of schemas that will be added to the instance. In case you pass the array the schemas must have IDs in them. When the object is passed the method `addSchema(value, key)` will be called for each schema in this object.
- _logger_: sets the logging method. Default is the global `console` object that should have methods `log`, `warn` and `error`. Option values:
- custom logger - it should have methods `log`, `warn` and `error`. If any of these methods is missing an exception will be thrown.
- `false` - logging is disabled.
##### Referenced schema options

18
lib/ajv.d.ts vendored
View File

@ -36,15 +36,17 @@ declare namespace ajv {
* Adds schema to the instance.
* @param {Object|Array} schema schema or array of schemas. If array is passed, `key` and other parameters will be ignored.
* @param {String} key Optional schema key. Can be passed to `validate` method instead of schema object or id/ref. One schema per instance can have empty `id` and `key`.
* @return {Ajv} this for method chaining
*/
addSchema(schema: Array<Object> | Object, key?: string): void;
addSchema(schema: Array<Object> | Object, key?: string): Ajv;
/**
* Add schema that will be used to validate other schemas
* options in META_IGNORE_OPTIONS are alway set to false
* @param {Object} schema schema object
* @param {String} key optional schema key
* @return {Ajv} this for method chaining
*/
addMetaSchema(schema: Object, key?: string): void;
addMetaSchema(schema: Object, key?: string): Ajv;
/**
* Validate schema
* @param {Object|Boolean} schema schema to validate
@ -63,21 +65,24 @@ declare namespace ajv {
* If RegExp is passed all schemas with key/id matching pattern but meta-schemas are removed.
* Even if schema is referenced by other schemas it still can be removed as other schemas have local references.
* @param {String|Object|RegExp|Boolean} schemaKeyRef key, ref, pattern to match key/ref or schema object
* @return {Ajv} this for method chaining
*/
removeSchema(schemaKeyRef?: Object | string | RegExp | boolean): void;
removeSchema(schemaKeyRef?: Object | string | RegExp | boolean): Ajv;
/**
* Add custom format
* @param {String} name format name
* @param {String|RegExp|Function} format string is converted to RegExp; function should return boolean (true when valid)
* @return {Ajv} this for method chaining
*/
addFormat(name: string, format: FormatValidator | FormatDefinition): void;
addFormat(name: string, format: FormatValidator | FormatDefinition): Ajv;
/**
* Define custom keyword
* @this Ajv
* @param {String} keyword custom keyword, should be a valid identifier, should be different from all standard, custom and macro keywords.
* @param {Object} definition keyword definition object with properties `type` (type(s) which the keyword applies to), `validate` or `compile`.
* @return {Ajv} this for method chaining
*/
addKeyword(keyword: string, definition: KeywordDefinition): void;
addKeyword(keyword: string, definition: KeywordDefinition): Ajv;
/**
* Get keyword definition
* @this Ajv
@ -89,8 +94,9 @@ declare namespace ajv {
* Remove keyword
* @this Ajv
* @param {String} keyword pre-defined or custom keyword.
* @return {Ajv} this for method chaining
*/
removeKeyword(keyword: string): void;
removeKeyword(keyword: string): Ajv;
/**
* Convert array of error message objects to string
* @param {Array<Object>} errors optional array of validation errors, if not passed errors from the instance are used.

View File

@ -51,6 +51,7 @@ var META_SUPPORT_DATA = ['/properties'];
function Ajv(opts) {
if (!(this instanceof Ajv)) return new Ajv(opts);
opts = this._opts = util.copy(opts) || {};
setLogger(this);
this._schemas = {};
this._refs = {};
this._fragments = {};
@ -122,6 +123,7 @@ function compile(schema, _meta) {
* @param {String} key Optional schema key. Can be passed to `validate` method instead of schema object or id/ref. One schema per instance can have empty `id` and `key`.
* @param {Boolean} _skipValidation true to skip schema validation. Used internally, option validateSchema should be used instead.
* @param {Boolean} _meta true if schema is a meta-schema. Used internally, addMetaSchema should be used instead.
* @return {Ajv} this for method chaining
*/
function addSchema(schema, key, _skipValidation, _meta) {
if (Array.isArray(schema)){
@ -134,6 +136,7 @@ function addSchema(schema, key, _skipValidation, _meta) {
key = resolve.normalizeId(key || id);
checkUnique(this, key);
this._schemas[key] = this._addSchema(schema, _skipValidation, _meta, true);
return this;
}
@ -144,9 +147,11 @@ function addSchema(schema, key, _skipValidation, _meta) {
* @param {Object} schema schema object
* @param {String} key optional schema key
* @param {Boolean} skipValidation true to skip schema validation, can be used to override validateSchema option for meta-schema
* @return {Ajv} this for method chaining
*/
function addMetaSchema(schema, key, skipValidation) {
this.addSchema(schema, key, skipValidation, true);
return this;
}
@ -163,7 +168,7 @@ function validateSchema(schema, throwOrLogError) {
throw new Error('$schema must be a string');
$schema = $schema || this._opts.defaultMeta || defaultMeta(this);
if (!$schema) {
console.warn('meta-schema not available');
this.logger.warn('meta-schema not available');
this.errors = null;
return true;
}
@ -176,7 +181,7 @@ function validateSchema(schema, throwOrLogError) {
finally { this._formats.uri = currentUriFormat; }
if (!valid && throwOrLogError) {
var message = 'schema is invalid: ' + this.errorsText();
if (this._opts.validateSchema == 'log') console.error(message);
if (this._opts.validateSchema == 'log') this.logger.error(message);
else throw new Error(message);
}
return valid;
@ -243,25 +248,26 @@ function _getSchemaObj(self, keyRef) {
* Even if schema is referenced by other schemas it still can be removed as other schemas have local references.
* @this Ajv
* @param {String|Object|RegExp} schemaKeyRef key, ref, pattern to match key/ref or schema object
* @return {Ajv} this for method chaining
*/
function removeSchema(schemaKeyRef) {
if (schemaKeyRef instanceof RegExp) {
_removeAllSchemas(this, this._schemas, schemaKeyRef);
_removeAllSchemas(this, this._refs, schemaKeyRef);
return;
return this;
}
switch (typeof schemaKeyRef) {
case 'undefined':
_removeAllSchemas(this, this._schemas);
_removeAllSchemas(this, this._refs);
this._cache.clear();
return;
return this;
case 'string':
var schemaObj = _getSchemaObj(this, schemaKeyRef);
if (schemaObj) this._cache.del(schemaObj.cacheKey);
delete this._schemas[schemaKeyRef];
delete this._refs[schemaKeyRef];
return;
return this;
case 'object':
var serialize = this._opts.serialize;
var cacheKey = serialize ? serialize(schemaKeyRef) : schemaKeyRef;
@ -273,6 +279,7 @@ function removeSchema(schemaKeyRef) {
delete this._refs[id];
}
}
return this;
}
@ -375,15 +382,15 @@ function chooseGetId(opts) {
}
}
/* @this Ajv */
function _getId(schema) {
if (schema.$id) console.warn('schema $id ignored', schema.$id);
if (schema.$id) this.logger.warn('schema $id ignored', schema.$id);
return schema.id;
}
/* @this Ajv */
function _get$Id(schema) {
if (schema.id) console.warn('schema id ignored', schema.id);
if (schema.id) this.logger.warn('schema id ignored', schema.id);
return schema.$id;
}
@ -423,10 +430,12 @@ function errorsText(errors, options) {
* @this Ajv
* @param {String} name format name
* @param {String|RegExp|Function} format string is converted to RegExp; function should return boolean (true when valid)
* @return {Ajv} this for method chaining
*/
function addFormat(name, format) {
if (typeof format == 'string') format = new RegExp(format);
this._formats[name] = format;
return this;
}
@ -472,3 +481,19 @@ function getMetaSchemaOptions(self) {
delete metaOpts[META_IGNORE_OPTIONS[i]];
return metaOpts;
}
function setLogger(self) {
var logger = self._opts.logger;
if (logger === false) {
self.logger = {log: noop, warn: noop, error: noop};
} else {
if (logger === undefined) logger = console;
if (!(typeof logger == 'object' && logger.log && logger.warn && logger.error))
throw new Error('logger must implement log, warn and error methods');
self.logger = logger;
}
}
function noop() {}

View File

@ -103,6 +103,7 @@ function compile(schema, root, localRefs, baseId) {
useCustomRule: useCustomRule,
opts: opts,
formats: formats,
logger: self.logger,
self: self
});
@ -143,7 +144,7 @@ function compile(schema, root, localRefs, baseId) {
refVal[0] = validate;
} catch(e) {
console.error('Error compiling schema, function code:', sourceCode);
self.logger.error('Error compiling schema, function code:', sourceCode);
throw e;
}
@ -257,7 +258,7 @@ function compile(schema, root, localRefs, baseId) {
var valid = validateSchema(schema);
if (!valid) {
var message = 'keyword schema is invalid: ' + self.errorsText(validateSchema.errors);
if (self._opts.validateSchema == 'log') console.error(message);
if (self._opts.validateSchema == 'log') self.logger.error(message);
else throw new Error(message);
}
}

View File

@ -71,7 +71,7 @@
{{ var $format = it.formats[$schema]; }}
{{? !$format }}
{{? $unknownFormats == 'ignore' }}
{{ console.warn('unknown format "' + $schema + '" ignored in schema at path "' + it.errSchemaPath + '"'); }}
{{ it.logger.warn('unknown format "' + $schema + '" ignored in schema at path "' + it.errSchemaPath + '"'); }}
{{# def.skipFormat }}
{{?? $allowUnknown && $unknownFormats.indexOf($schema) >= 0 }}
{{# def.skipFormat }}

View File

@ -27,11 +27,11 @@
{{? $refVal === undefined }}
{{ var $message = it.MissingRefError.message(it.baseId, $schema); }}
{{? it.opts.missingRefs == 'fail' }}
{{ console.error($message); }}
{{ it.logger.error($message); }}
{{# def.error:'$ref' }}
{{? $breakOnError }} if (false) { {{?}}
{{?? it.opts.missingRefs == 'ignore' }}
{{ console.warn($message); }}
{{ it.logger.warn($message); }}
{{? $breakOnError }} if (true) { {{?}}
{{??}}
{{ throw new it.MissingRefError(it.baseId, $schema, $message); }}

View File

@ -122,7 +122,7 @@
{{?? it.opts.extendRefs !== true }}
{{
$refKeywords = false;
console.warn('$ref: keywords ignored in schema at path "' + it.errSchemaPath + '"');
it.logger.warn('$ref: keywords ignored in schema at path "' + it.errSchemaPath + '"');
}}
{{?}}
{{?}}
@ -163,7 +163,7 @@
{{?}}
{{??}}
{{? it.opts.v5 && it.schema.patternGroups }}
{{ console.warn('keyword "patternGroups" is deprecated and disabled. Use option patternGroups: true to enable.'); }}
{{ it.logger.warn('keyword "patternGroups" is deprecated and disabled. Use option patternGroups: true to enable.'); }}
{{?}}
{{~ it.RULES:$rulesGroup }}
{{? $shouldUseGroup($rulesGroup) }}

View File

@ -14,6 +14,7 @@ module.exports = {
* @this Ajv
* @param {String} keyword custom keyword, should be unique (including different from all standard, custom and macro keywords).
* @param {Object} definition keyword definition object with properties `type` (type(s) which the keyword applies to), `validate` or `compile`.
* @return {Ajv} this for method chaining
*/
function addKeyword(keyword, definition) {
/* jshint validthis: true */
@ -91,6 +92,8 @@ function addKeyword(keyword, definition) {
function checkDataType(dataType) {
if (!RULES.types[dataType]) throw new Error('Unknown type ' + dataType);
}
return this;
}
@ -111,6 +114,7 @@ function getKeyword(keyword) {
* Remove keyword
* @this Ajv
* @param {String} keyword pre-defined or custom keyword.
* @return {Ajv} this for method chaining
*/
function removeKeyword(keyword) {
/* jshint validthis: true */
@ -127,4 +131,5 @@ function removeKeyword(keyword) {
}
}
}
return this;
}

View File

@ -57,6 +57,10 @@
"type": "string"
},
"default": {},
"examples": {
"type": "array",
"items": {}
},
"multipleOf": {
"type": "number",
"exclusiveMinimum": 0

View File

@ -119,8 +119,7 @@ describe('Ajv', function () {
describe('addSchema method', function() {
it('should add and compile schema with key', function() {
var res = ajv.addSchema({ type: 'integer' }, 'int');
should.not.exist(res);
ajv.addSchema({ type: 'integer' }, 'int');
var validate = ajv.getSchema('int');
validate .should.be.a('function');
@ -217,6 +216,11 @@ describe('Ajv', function () {
e.message .should.equal('schema id must be string');
}
});
it('should return instance of itself', function() {
var res = ajv.addSchema({ type: 'integer' }, 'int');
res.should.equal(ajv);
});
});
@ -382,6 +386,13 @@ describe('Ajv', function () {
should.not.exist(ajv._cache.get(str2));
ajv._cache.get(str3) .should.be.an('object');
});
it('should return instance of itself', function() {
var res = ajv
.addSchema({ type: 'integer' }, 'int')
.removeSchema('int');
res.should.equal(ajv);
});
});
@ -408,6 +419,11 @@ describe('Ajv', function () {
testFormat();
});
it('should return instance of itself', function() {
var res = ajv.addFormat('identifier', /^[a-z_$][a-z0-9_$]*$/i);
res.should.equal(ajv);
});
function testFormat() {
var validate = ajv.compile({ format: 'identifier' });
validate('Abc1') .should.equal(true);

View File

@ -239,6 +239,32 @@ describe('async schemas, formats and keywords', function() {
return recursiveTest(schema);
});
it.skip('should validate recursive ref to async sub-schema, issue #612', function() {
var schema = {
$async: true,
type: 'object',
properties: {
foo: {
$async: true,
anyOf: [
{
type: 'string',
format: 'english_word'
},
{
type: 'object',
properties: {
foo: { $ref: '#/properties/foo' }
}
}
]
}
}
};
return recursiveTest(schema);
});
it('should validate ref from referenced async schema to root schema', function() {
var schema = {
$async: true,

View File

@ -939,6 +939,13 @@ describe('Custom keywords', function () {
});
});
it('should return instance of itself', function() {
var res = ajv.addKeyword('any', {
validate: function() { return true; }
});
res.should.equal(ajv);
});
it('should throw if unknown type is passed', function() {
should.throw(function() {
addKeyword('custom1', 'wrongtype');
@ -1041,6 +1048,15 @@ describe('Custom keywords', function () {
validate(1) .should.equal(false);
validate(2) .should.equal(true);
});
it('should return instance of itself', function() {
var res = ajv
.addKeyword('any', {
validate: function() { return true; }
})
.removeKeyword('any');
res.should.equal(ajv);
});
});

View File

@ -1307,4 +1307,89 @@ describe('Ajv Options', function () {
});
});
});
describe('logger', function() {
/**
* The logger option tests are based on the meta scenario which writes into the logger.warn
*/
var origConsoleWarn = console.warn;
var consoleCalled;
beforeEach(function() {
consoleCalled = false;
console.warn = function() {
consoleCalled = true;
};
});
afterEach(function() {
console.warn = origConsoleWarn;
});
it('no custom logger is given - global console should be used', function() {
var ajv = new Ajv({
meta: false
});
ajv.compile({
type: 'number',
minimum: 1
});
should.equal(consoleCalled, true);
});
it('custom logger is an object - logs should only report to it', function() {
var loggerCalled = false;
var logger = {
warn: log,
log: log,
error: log
};
var ajv = new Ajv({
meta: false,
logger: logger
});
ajv.compile({
type: 'number',
minimum: 1
});
should.equal(loggerCalled, true);
should.equal(consoleCalled, false);
function log() {
loggerCalled = true;
}
});
it('logger option is false - no logs should be reported', function() {
var ajv = new Ajv({
meta: false,
logger: false
});
ajv.compile({
type: 'number',
minimum: 1
});
should.equal(consoleCalled, false);
});
it('logger option is an object without required methods - an error should be thrown', function() {
(function(){
new Ajv({
meta: false,
logger: {}
});
}).should.throw(Error, /logger must implement log, warn and error methods/);
});
});
});