schema-path.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. var tape = require('tape')
  2. var validator = require('../')
  3. var get = require('jsonpointer').get;
  4. function toPointer( path ) {
  5. if ( ! ( path && path.length && path.join ) ){
  6. return '';
  7. }
  8. return '/' + path.join('/');
  9. }
  10. function lookup(schema, err){
  11. return get(schema, toPointer(err.schemaPath));
  12. }
  13. tape('schemaPath', function(t) {
  14. var schema = {
  15. type: 'object',
  16. target: 'top level',
  17. properties: {
  18. target: 'inside properties',
  19. hello: {
  20. target: 'inside hello',
  21. type:'string'
  22. },
  23. someItems: {
  24. target: 'in someItems',
  25. type: 'array',
  26. items: [
  27. {
  28. type: 'string'
  29. },
  30. {
  31. type: 'array'
  32. },
  33. ],
  34. additionalItems: {
  35. target: 'inside additionalItems',
  36. type: 'boolean',
  37. }
  38. },
  39. nestedOuter: {
  40. type: 'object',
  41. target: 'in nestedOuter',
  42. properties: {
  43. nestedInner: {
  44. type: 'object',
  45. target: 'in nestedInner',
  46. properties: {
  47. deeplyNestedProperty: {
  48. target: 'in deeplyNestedProperty',
  49. type: "boolean"
  50. }
  51. }
  52. },
  53. },
  54. required: ['nestedInner']
  55. },
  56. aggregate: {
  57. allOf: [
  58. { pattern: 'z$' },
  59. { pattern: '^a' },
  60. { pattern: '-' },
  61. { pattern: '^...$' }
  62. ]
  63. },
  64. negate: {
  65. target: "in negate",
  66. not: {
  67. type: "boolean"
  68. }
  69. },
  70. selection: {
  71. target: 'in selection',
  72. anyOf: [
  73. { 'pattern': '^[a-z]{3}$' },
  74. { 'pattern': '^[0-9]$' }
  75. ],
  76. },
  77. exclusiveSelection: {
  78. target: 'There can be only one',
  79. oneOf: [
  80. { pattern: 'a' },
  81. { pattern: 'e' },
  82. { pattern: 'i' },
  83. { pattern: 'o' },
  84. { pattern: 'u' }
  85. ]
  86. }
  87. },
  88. patternProperties: {
  89. ".*String": { type: 'string' },
  90. '^[01]+$': { type: 'number' }
  91. },
  92. additionalProperties: false
  93. }
  94. var validate = validator(schema, { verbose: true, greedy: true } );
  95. function notOkAt(data, path, message) {
  96. if(validate(data)) {
  97. return t.fail('should have failed: ' + message)
  98. }
  99. t.deepEqual(validate.errors[0].schemaPath, path, message)
  100. }
  101. function notOkWithTarget(data, target, message) {
  102. if(validate(data)) {
  103. return t.fail('should have failed: ' + message)
  104. }
  105. t.deepEqual(lookup(schema, validate.errors[0]).target, target, message)
  106. }
  107. // Top level errors
  108. notOkAt(null, [], 'should target parent of failed type error')
  109. notOkAt(undefined, [], 'should target parent of failed type error')
  110. notOkWithTarget({invalidAdditionalProp: '*whistles innocently*'}, 'top level', 'additionalProperties should be associated with containing schema')
  111. // Errors in properties
  112. notOkAt({hello: 42}, ['properties', 'hello'], 'should target property with type error')
  113. notOkAt({someItems: [42]}, ['properties','someItems','0'], 'should target specific someItems rule(0)')
  114. notOkAt({someItems: ['astring', 42]}, ['properties','someItems','1'], 'should target specific someItems rule(1)')
  115. notOkAt({someItems: ['astring', 42, 'not a boolean']}, ['properties','someItems', 'additionalItems'], 'should target additionalItems')
  116. notOkWithTarget({someItems: ['astring', 42, true, false, 42]}, 'inside additionalItems', 'should sitll target additionalProperties after valid additional items')
  117. notOkWithTarget({nestedOuter: {}}, 'in nestedOuter', 'should target container of missing required property')
  118. notOkWithTarget({nestedOuter: {nestedInner: 'not an object'}}, 'in nestedInner', 'should target property with type error (inner)')
  119. notOkWithTarget({nestedOuter: {nestedInner: {deeplyNestedProperty: 'not a boolean'}}}, 'in deeplyNestedProperty', 'should target property with type error (deep)')
  120. notOkAt({aggregate: 'a-a'}, ['properties', 'aggregate', 'allOf', 0], 'should target specific rule in allOf (0)')
  121. notOkAt({aggregate: 'z-z'}, ['properties', 'aggregate', 'allOf', 1], 'should target specific rule in allOf (1)')
  122. notOkAt({aggregate: 'a:z'}, ['properties', 'aggregate', 'allOf', 2], 'should target specific rule in allOf (2)')
  123. notOkAt({aggregate: 'a--z'}, ['properties', 'aggregate', 'allOf', 3], 'should target specific rule in allOf (3)')
  124. notOkAt({'notAString': 42}, ['patternProperties', '.*String'], 'should target the specific pattern in patternProperties (wildcards)')
  125. notOkAt({
  126. 'I am a String': 'I really am',
  127. '001100111011000111100': "Don't stand around jabbering when you're in mortal danger"
  128. }, ['patternProperties', '^[01]+$'], 'should target the specific pattern in patternProperties ("binary" keys)')
  129. notOkWithTarget({negate: false}, 'in negate', 'should target container of not')
  130. notOkWithTarget(({selection: 'grit'}), 'in selection', 'should target container for anyOf (no matches)');
  131. notOkWithTarget(({exclusiveSelection: 'fly'}), 'There can be only one', 'should target container for oneOf (no match)');
  132. notOkWithTarget(({exclusiveSelection: 'ice'}), 'There can be only one', 'should target container for oneOf (multiple matches)');
  133. t.end()
  134. })
  135. tape('schemaPath - nested selectors', function(t) {
  136. var schema = {
  137. anyOf: [
  138. { oneOf:[
  139. { allOf: [
  140. {
  141. properties: {
  142. nestedSelectors: {type: "integer"}
  143. }
  144. }
  145. ]}
  146. ]}
  147. ]
  148. }
  149. var validate = validator(schema, { verbose: true, greedy: true } );
  150. t.notOk(validate({nestedSelectors: "nope"}), 'should not crash on visit inside *Of');
  151. t.end()
  152. })