var tape = require('tape') var validator = require('../') var get = require('jsonpointer').get; function toPointer( path ) { if ( ! ( path && path.length && path.join ) ){ return ''; } return '/' + path.join('/'); } function lookup(schema, err){ return get(schema, toPointer(err.schemaPath)); } tape('schemaPath', function(t) { var schema = { type: 'object', target: 'top level', properties: { target: 'inside properties', hello: { target: 'inside hello', type:'string' }, someItems: { target: 'in someItems', type: 'array', items: [ { type: 'string' }, { type: 'array' }, ], additionalItems: { target: 'inside additionalItems', type: 'boolean', } }, nestedOuter: { type: 'object', target: 'in nestedOuter', properties: { nestedInner: { type: 'object', target: 'in nestedInner', properties: { deeplyNestedProperty: { target: 'in deeplyNestedProperty', type: "boolean" } } }, }, required: ['nestedInner'] }, aggregate: { allOf: [ { pattern: 'z$' }, { pattern: '^a' }, { pattern: '-' }, { pattern: '^...$' } ] }, negate: { target: "in negate", not: { type: "boolean" } }, selection: { target: 'in selection', anyOf: [ { 'pattern': '^[a-z]{3}$' }, { 'pattern': '^[0-9]$' } ], }, exclusiveSelection: { target: 'There can be only one', oneOf: [ { pattern: 'a' }, { pattern: 'e' }, { pattern: 'i' }, { pattern: 'o' }, { pattern: 'u' } ] } }, patternProperties: { ".*String": { type: 'string' }, '^[01]+$': { type: 'number' } }, additionalProperties: false } var validate = validator(schema, { verbose: true, greedy: true } ); function notOkAt(data, path, message) { if(validate(data)) { return t.fail('should have failed: ' + message) } t.deepEqual(validate.errors[0].schemaPath, path, message) } function notOkWithTarget(data, target, message) { if(validate(data)) { return t.fail('should have failed: ' + message) } t.deepEqual(lookup(schema, validate.errors[0]).target, target, message) } // Top level errors notOkAt(null, [], 'should target parent of failed type error') notOkAt(undefined, [], 'should target parent of failed type error') notOkWithTarget({invalidAdditionalProp: '*whistles innocently*'}, 'top level', 'additionalProperties should be associated with containing schema') // Errors in properties notOkAt({hello: 42}, ['properties', 'hello'], 'should target property with type error') notOkAt({someItems: [42]}, ['properties','someItems','0'], 'should target specific someItems rule(0)') notOkAt({someItems: ['astring', 42]}, ['properties','someItems','1'], 'should target specific someItems rule(1)') notOkAt({someItems: ['astring', 42, 'not a boolean']}, ['properties','someItems', 'additionalItems'], 'should target additionalItems') notOkWithTarget({someItems: ['astring', 42, true, false, 42]}, 'inside additionalItems', 'should sitll target additionalProperties after valid additional items') notOkWithTarget({nestedOuter: {}}, 'in nestedOuter', 'should target container of missing required property') notOkWithTarget({nestedOuter: {nestedInner: 'not an object'}}, 'in nestedInner', 'should target property with type error (inner)') notOkWithTarget({nestedOuter: {nestedInner: {deeplyNestedProperty: 'not a boolean'}}}, 'in deeplyNestedProperty', 'should target property with type error (deep)') notOkAt({aggregate: 'a-a'}, ['properties', 'aggregate', 'allOf', 0], 'should target specific rule in allOf (0)') notOkAt({aggregate: 'z-z'}, ['properties', 'aggregate', 'allOf', 1], 'should target specific rule in allOf (1)') notOkAt({aggregate: 'a:z'}, ['properties', 'aggregate', 'allOf', 2], 'should target specific rule in allOf (2)') notOkAt({aggregate: 'a--z'}, ['properties', 'aggregate', 'allOf', 3], 'should target specific rule in allOf (3)') notOkAt({'notAString': 42}, ['patternProperties', '.*String'], 'should target the specific pattern in patternProperties (wildcards)') notOkAt({ 'I am a String': 'I really am', '001100111011000111100': "Don't stand around jabbering when you're in mortal danger" }, ['patternProperties', '^[01]+$'], 'should target the specific pattern in patternProperties ("binary" keys)') notOkWithTarget({negate: false}, 'in negate', 'should target container of not') notOkWithTarget(({selection: 'grit'}), 'in selection', 'should target container for anyOf (no matches)'); notOkWithTarget(({exclusiveSelection: 'fly'}), 'There can be only one', 'should target container for oneOf (no match)'); notOkWithTarget(({exclusiveSelection: 'ice'}), 'There can be only one', 'should target container for oneOf (multiple matches)'); t.end() }) tape('schemaPath - nested selectors', function(t) { var schema = { anyOf: [ { oneOf:[ { allOf: [ { properties: { nestedSelectors: {type: "integer"} } } ]} ]} ] } var validate = validator(schema, { verbose: true, greedy: true } ); t.notOk(validate({nestedSelectors: "nope"}), 'should not crash on visit inside *Of'); t.end() })