| 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('data is undefined', function (t) { |
| var validate = validator({type: 'string'}) |
| |
| t.notOk(validate(null)) |
| t.notOk(validate(undefined)) |
| 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('minimum/maximum number type', function(t) { |
| var validate = validator({ |
| type: ['integer', 'null'], |
| minimum: 1, |
| maximum: 100 |
| }) |
| |
| t.notOk(validate(-1)) |
| t.notOk(validate(0)) |
| t.ok(validate(null)) |
| t.ok(validate(1)) |
| t.ok(validate(100)) |
| t.notOk(validate(101)) |
| 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() |
| }) |