448 lines
10 KiB
JavaScript
448 lines
10 KiB
JavaScript
|
var tape = require('tape')
|
||
|
var cosmic = require('./fixtures/cosmic')
|
||
|
var validator = require('../')
|
||
|
var validatorRequire = require('../require')
|
||
|
|
||
|
tape('simple', function(t) {
|
||
|
var schema = {
|
||
|
required: true,
|
||
|
type: 'object',
|
||
|
properties: {
|
||
|
hello: {type:'string', required:true}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var validate = validator(schema)
|
||
|
|
||
|
t.ok(validate({hello: 'world'}), 'should be valid')
|
||
|
t.notOk(validate(), 'should be invalid')
|
||
|
t.notOk(validate({}), 'should be invalid')
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
tape('advanced', function(t) {
|
||
|
var validate = validator(cosmic.schema)
|
||
|
|
||
|
t.ok(validate(cosmic.valid), 'should be valid')
|
||
|
t.notOk(validate(cosmic.invalid), 'should be invalid')
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
tape('greedy/false', function(t) {
|
||
|
var validate = validator({
|
||
|
type: 'object',
|
||
|
properties: {
|
||
|
x: {
|
||
|
type: 'number'
|
||
|
}
|
||
|
},
|
||
|
required: ['x', 'y']
|
||
|
});
|
||
|
t.notOk(validate({}), 'should be invalid')
|
||
|
t.strictEqual(validate.errors.length, 2);
|
||
|
t.strictEqual(validate.errors[0].field, 'data.x')
|
||
|
t.strictEqual(validate.errors[0].message, 'is required')
|
||
|
t.strictEqual(validate.errors[1].field, 'data.y')
|
||
|
t.strictEqual(validate.errors[1].message, 'is required')
|
||
|
t.notOk(validate({x: 'string'}), 'should be invalid')
|
||
|
t.strictEqual(validate.errors.length, 1);
|
||
|
t.strictEqual(validate.errors[0].field, 'data.y')
|
||
|
t.strictEqual(validate.errors[0].message, 'is required')
|
||
|
t.notOk(validate({x: 'string', y: 'value'}), 'should be invalid')
|
||
|
t.strictEqual(validate.errors.length, 1);
|
||
|
t.strictEqual(validate.errors[0].field, 'data.x')
|
||
|
t.strictEqual(validate.errors[0].message, 'is the wrong type')
|
||
|
t.end();
|
||
|
});
|
||
|
|
||
|
tape('greedy/true', function(t) {
|
||
|
var validate = validator({
|
||
|
type: 'object',
|
||
|
properties: {
|
||
|
x: {
|
||
|
type: 'number'
|
||
|
}
|
||
|
},
|
||
|
required: ['x', 'y']
|
||
|
}, {
|
||
|
greedy: true
|
||
|
});
|
||
|
t.notOk(validate({}), 'should be invalid')
|
||
|
t.strictEqual(validate.errors.length, 2);
|
||
|
t.strictEqual(validate.errors[0].field, 'data.x')
|
||
|
t.strictEqual(validate.errors[0].message, 'is required')
|
||
|
t.strictEqual(validate.errors[1].field, 'data.y')
|
||
|
t.strictEqual(validate.errors[1].message, 'is required')
|
||
|
t.notOk(validate({x: 'string'}), 'should be invalid')
|
||
|
t.strictEqual(validate.errors.length, 2);
|
||
|
t.strictEqual(validate.errors[0].field, 'data.y')
|
||
|
t.strictEqual(validate.errors[0].message, 'is required')
|
||
|
t.strictEqual(validate.errors[1].field, 'data.x')
|
||
|
t.strictEqual(validate.errors[1].message, 'is the wrong type')
|
||
|
t.notOk(validate({x: 'string', y: 'value'}), 'should be invalid')
|
||
|
t.strictEqual(validate.errors.length, 1);
|
||
|
t.strictEqual(validate.errors[0].field, 'data.x')
|
||
|
t.strictEqual(validate.errors[0].message, 'is the wrong type')
|
||
|
t.ok(validate({x: 1, y: 'value'}), 'should be invalid')
|
||
|
t.end();
|
||
|
});
|
||
|
|
||
|
tape('additional props', function(t) {
|
||
|
var validate = validator({
|
||
|
type: 'object',
|
||
|
additionalProperties: false
|
||
|
}, {
|
||
|
verbose: true
|
||
|
})
|
||
|
|
||
|
t.ok(validate({}))
|
||
|
t.notOk(validate({foo:'bar'}))
|
||
|
t.ok(validate.errors[0].value === 'data.foo', 'should output the property not allowed in verbose mode')
|
||
|
t.strictEqual(validate.errors[0].type, 'object', 'error object should contain the type')
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
tape('array', function(t) {
|
||
|
var validate = validator({
|
||
|
type: 'array',
|
||
|
required: true,
|
||
|
items: {
|
||
|
type: 'string'
|
||
|
}
|
||
|
})
|
||
|
|
||
|
t.notOk(validate({}), 'wrong type')
|
||
|
t.notOk(validate(), 'is required')
|
||
|
t.ok(validate(['test']))
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
tape('nested array', function(t) {
|
||
|
var validate = validator({
|
||
|
type: 'object',
|
||
|
properties: {
|
||
|
list: {
|
||
|
type: 'array',
|
||
|
required: true,
|
||
|
items: {
|
||
|
type: 'string'
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
|
||
|
t.notOk(validate({}), 'is required')
|
||
|
t.ok(validate({list:['test']}))
|
||
|
t.notOk(validate({list:[1]}))
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
tape('enum', function(t) {
|
||
|
var validate = validator({
|
||
|
type: 'object',
|
||
|
properties: {
|
||
|
foo: {
|
||
|
type: 'number',
|
||
|
required: true,
|
||
|
enum: [42]
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
|
||
|
t.notOk(validate({}), 'is required')
|
||
|
t.ok(validate({foo:42}))
|
||
|
t.notOk(validate({foo:43}))
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
tape('minimum/maximum', function(t) {
|
||
|
var validate = validator({
|
||
|
type: 'object',
|
||
|
properties: {
|
||
|
foo: {
|
||
|
type: 'number',
|
||
|
minimum: 0,
|
||
|
maximum: 0
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
|
||
|
t.notOk(validate({foo:-42}))
|
||
|
t.ok(validate({foo:0}))
|
||
|
t.notOk(validate({foo:42}))
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
tape('exclusiveMinimum/exclusiveMaximum', function(t) {
|
||
|
var validate = validator({
|
||
|
type: 'object',
|
||
|
properties: {
|
||
|
foo: {
|
||
|
type: 'number',
|
||
|
minimum: 10,
|
||
|
maximum: 20,
|
||
|
exclusiveMinimum: true,
|
||
|
exclusiveMaximum: true
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
|
||
|
t.notOk(validate({foo:10}))
|
||
|
t.ok(validate({foo:11}))
|
||
|
t.notOk(validate({foo:20}))
|
||
|
t.ok(validate({foo:19}))
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
tape('custom format', function(t) {
|
||
|
var validate = validator({
|
||
|
type: 'object',
|
||
|
properties: {
|
||
|
foo: {
|
||
|
type: 'string',
|
||
|
format: 'as'
|
||
|
}
|
||
|
}
|
||
|
}, {formats: {as:/^a+$/}})
|
||
|
|
||
|
t.notOk(validate({foo:''}), 'not as')
|
||
|
t.notOk(validate({foo:'b'}), 'not as')
|
||
|
t.notOk(validate({foo:'aaab'}), 'not as')
|
||
|
t.ok(validate({foo:'a'}), 'as')
|
||
|
t.ok(validate({foo:'aaaaaa'}), 'as')
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
tape('custom format function', function(t) {
|
||
|
var validate = validator({
|
||
|
type: 'object',
|
||
|
properties: {
|
||
|
foo: {
|
||
|
type: 'string',
|
||
|
format: 'as'
|
||
|
}
|
||
|
}
|
||
|
}, {formats: {as:function(s) { return /^a+$/.test(s) } }})
|
||
|
|
||
|
t.notOk(validate({foo:''}), 'not as')
|
||
|
t.notOk(validate({foo:'b'}), 'not as')
|
||
|
t.notOk(validate({foo:'aaab'}), 'not as')
|
||
|
t.ok(validate({foo:'a'}), 'as')
|
||
|
t.ok(validate({foo:'aaaaaa'}), 'as')
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
tape('do not mutate schema', function(t) {
|
||
|
var sch = {
|
||
|
items: [
|
||
|
{}
|
||
|
],
|
||
|
additionalItems: {
|
||
|
type: 'integer'
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var copy = JSON.parse(JSON.stringify(sch))
|
||
|
|
||
|
validator(sch)
|
||
|
|
||
|
t.same(sch, copy, 'did not mutate')
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
tape('#toJSON()', function(t) {
|
||
|
var schema = {
|
||
|
required: true,
|
||
|
type: 'object',
|
||
|
properties: {
|
||
|
hello: {type:'string', required:true}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var validate = validator(schema)
|
||
|
|
||
|
t.deepEqual(validate.toJSON(), schema, 'should return original schema')
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
tape('external schemas', function(t) {
|
||
|
var ext = {type: 'string'}
|
||
|
var schema = {
|
||
|
required: true,
|
||
|
$ref: '#ext'
|
||
|
}
|
||
|
|
||
|
var validate = validator(schema, {schemas: {ext:ext}})
|
||
|
|
||
|
t.ok(validate('hello string'), 'is a string')
|
||
|
t.notOk(validate(42), 'not a string')
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
tape('external schema URIs', function(t) {
|
||
|
var ext = {type: 'string'}
|
||
|
var schema = {
|
||
|
required: true,
|
||
|
$ref: 'http://example.com/schemas/schemaURIs'
|
||
|
}
|
||
|
|
||
|
var opts = {schemas:{}};
|
||
|
opts.schemas['http://example.com/schemas/schemaURIs'] = ext;
|
||
|
var validate = validator(schema, opts)
|
||
|
|
||
|
t.ok(validate('hello string'), 'is a string')
|
||
|
t.notOk(validate(42), 'not a string')
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
tape('top-level external schema', function(t) {
|
||
|
var defs = {
|
||
|
"string": {
|
||
|
type: "string"
|
||
|
},
|
||
|
"sex": {
|
||
|
type: "string",
|
||
|
enum: ["male", "female", "other"]
|
||
|
}
|
||
|
}
|
||
|
var schema = {
|
||
|
type: "object",
|
||
|
properties: {
|
||
|
"name": { $ref: "definitions.json#/string" },
|
||
|
"sex": { $ref: "definitions.json#/sex" }
|
||
|
},
|
||
|
required: ["name", "sex"]
|
||
|
}
|
||
|
|
||
|
var validate = validator(schema, {
|
||
|
schemas: {
|
||
|
"definitions.json": defs
|
||
|
}
|
||
|
})
|
||
|
t.ok(validate({name:"alice", sex:"female"}), 'is an object')
|
||
|
t.notOk(validate({name:"alice", sex: "bob"}), 'recognizes external schema')
|
||
|
t.notOk(validate({name:2, sex: "female"}), 'recognizes external schema')
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
tape('nested required array decl', function(t) {
|
||
|
var schema = {
|
||
|
properties: {
|
||
|
x: {
|
||
|
type: 'object',
|
||
|
properties: {
|
||
|
y: {
|
||
|
type: 'object',
|
||
|
properties: {
|
||
|
z: {
|
||
|
type: 'string'
|
||
|
}
|
||
|
},
|
||
|
required: ['z']
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
required: ['x']
|
||
|
}
|
||
|
|
||
|
var validate = validator(schema)
|
||
|
|
||
|
t.ok(validate({x: {}}), 'should be valid')
|
||
|
t.notOk(validate({}), 'should not be valid')
|
||
|
t.strictEqual(validate.errors[0].field, 'data.x', 'should output the missing field')
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
tape('verbose mode', function(t) {
|
||
|
var schema = {
|
||
|
required: true,
|
||
|
type: 'object',
|
||
|
properties: {
|
||
|
hello: {
|
||
|
required: true,
|
||
|
type: 'string'
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var validate = validator(schema, {verbose: true})
|
||
|
|
||
|
t.ok(validate({hello: 'string'}), 'should be valid')
|
||
|
t.notOk(validate({hello: 100}), 'should not be valid')
|
||
|
t.strictEqual(validate.errors[0].value, 100, 'error object should contain the invalid value')
|
||
|
t.strictEqual(validate.errors[0].type, 'string', 'error object should contain the type')
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
tape('additional props in verbose mode', function(t) {
|
||
|
var schema = {
|
||
|
type: 'object',
|
||
|
required: true,
|
||
|
additionalProperties: false,
|
||
|
properties: {
|
||
|
foo: {
|
||
|
type: 'string'
|
||
|
},
|
||
|
'hello world': {
|
||
|
type: 'object',
|
||
|
required: true,
|
||
|
additionalProperties: false,
|
||
|
properties: {
|
||
|
foo: {
|
||
|
type: 'string'
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var validate = validator(schema, {verbose: true})
|
||
|
|
||
|
validate({'hello world': {bar: 'string'}});
|
||
|
|
||
|
t.strictEqual(validate.errors[0].value, 'data["hello world"].bar', 'should output the path to the additional prop in the error')
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
tape('Date.now() is an integer', function(t) {
|
||
|
var schema = {type: 'integer'}
|
||
|
var validate = validator(schema)
|
||
|
|
||
|
t.ok(validate(Date.now()), 'is integer')
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
tape('field shows item index in arrays', function(t) {
|
||
|
var schema = {
|
||
|
type: 'array',
|
||
|
items: {
|
||
|
type: 'array',
|
||
|
items: {
|
||
|
properties: {
|
||
|
foo: {
|
||
|
type: 'string',
|
||
|
required: true
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var validate = validator(schema)
|
||
|
|
||
|
validate([
|
||
|
[
|
||
|
{ foo: 'test' },
|
||
|
{ foo: 'test' }
|
||
|
],
|
||
|
[
|
||
|
{ foo: 'test' },
|
||
|
{ baz: 'test' }
|
||
|
]
|
||
|
])
|
||
|
|
||
|
t.strictEqual(validate.errors[0].field, 'data.1.1.foo', 'should output the field with specific index of failing item in the error')
|
||
|
t.end()
|
||
|
})
|