misc.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. var tape = require('tape')
  2. var cosmic = require('./fixtures/cosmic')
  3. var validator = require('../')
  4. var validatorRequire = require('../require')
  5. tape('simple', function(t) {
  6. var schema = {
  7. required: true,
  8. type: 'object',
  9. properties: {
  10. hello: {type:'string', required:true}
  11. }
  12. }
  13. var validate = validator(schema)
  14. t.ok(validate({hello: 'world'}), 'should be valid')
  15. t.notOk(validate(), 'should be invalid')
  16. t.notOk(validate({}), 'should be invalid')
  17. t.end()
  18. })
  19. tape('advanced', function(t) {
  20. var validate = validator(cosmic.schema)
  21. t.ok(validate(cosmic.valid), 'should be valid')
  22. t.notOk(validate(cosmic.invalid), 'should be invalid')
  23. t.end()
  24. })
  25. tape('greedy/false', function(t) {
  26. var validate = validator({
  27. type: 'object',
  28. properties: {
  29. x: {
  30. type: 'number'
  31. }
  32. },
  33. required: ['x', 'y']
  34. });
  35. t.notOk(validate({}), 'should be invalid')
  36. t.strictEqual(validate.errors.length, 2);
  37. t.strictEqual(validate.errors[0].field, 'data.x')
  38. t.strictEqual(validate.errors[0].message, 'is required')
  39. t.strictEqual(validate.errors[1].field, 'data.y')
  40. t.strictEqual(validate.errors[1].message, 'is required')
  41. t.notOk(validate({x: 'string'}), 'should be invalid')
  42. t.strictEqual(validate.errors.length, 1);
  43. t.strictEqual(validate.errors[0].field, 'data.y')
  44. t.strictEqual(validate.errors[0].message, 'is required')
  45. t.notOk(validate({x: 'string', y: 'value'}), 'should be invalid')
  46. t.strictEqual(validate.errors.length, 1);
  47. t.strictEqual(validate.errors[0].field, 'data.x')
  48. t.strictEqual(validate.errors[0].message, 'is the wrong type')
  49. t.end();
  50. });
  51. tape('greedy/true', function(t) {
  52. var validate = validator({
  53. type: 'object',
  54. properties: {
  55. x: {
  56. type: 'number'
  57. }
  58. },
  59. required: ['x', 'y']
  60. }, {
  61. greedy: true
  62. });
  63. t.notOk(validate({}), 'should be invalid')
  64. t.strictEqual(validate.errors.length, 2);
  65. t.strictEqual(validate.errors[0].field, 'data.x')
  66. t.strictEqual(validate.errors[0].message, 'is required')
  67. t.strictEqual(validate.errors[1].field, 'data.y')
  68. t.strictEqual(validate.errors[1].message, 'is required')
  69. t.notOk(validate({x: 'string'}), 'should be invalid')
  70. t.strictEqual(validate.errors.length, 2);
  71. t.strictEqual(validate.errors[0].field, 'data.y')
  72. t.strictEqual(validate.errors[0].message, 'is required')
  73. t.strictEqual(validate.errors[1].field, 'data.x')
  74. t.strictEqual(validate.errors[1].message, 'is the wrong type')
  75. t.notOk(validate({x: 'string', y: 'value'}), 'should be invalid')
  76. t.strictEqual(validate.errors.length, 1);
  77. t.strictEqual(validate.errors[0].field, 'data.x')
  78. t.strictEqual(validate.errors[0].message, 'is the wrong type')
  79. t.ok(validate({x: 1, y: 'value'}), 'should be invalid')
  80. t.end();
  81. });
  82. tape('additional props', function(t) {
  83. var validate = validator({
  84. type: 'object',
  85. additionalProperties: false
  86. }, {
  87. verbose: true
  88. })
  89. t.ok(validate({}))
  90. t.notOk(validate({foo:'bar'}))
  91. t.ok(validate.errors[0].value === 'data.foo', 'should output the property not allowed in verbose mode')
  92. t.strictEqual(validate.errors[0].type, 'object', 'error object should contain the type')
  93. t.end()
  94. })
  95. tape('array', function(t) {
  96. var validate = validator({
  97. type: 'array',
  98. required: true,
  99. items: {
  100. type: 'string'
  101. }
  102. })
  103. t.notOk(validate({}), 'wrong type')
  104. t.notOk(validate(), 'is required')
  105. t.ok(validate(['test']))
  106. t.end()
  107. })
  108. tape('nested array', function(t) {
  109. var validate = validator({
  110. type: 'object',
  111. properties: {
  112. list: {
  113. type: 'array',
  114. required: true,
  115. items: {
  116. type: 'string'
  117. }
  118. }
  119. }
  120. })
  121. t.notOk(validate({}), 'is required')
  122. t.ok(validate({list:['test']}))
  123. t.notOk(validate({list:[1]}))
  124. t.end()
  125. })
  126. tape('enum', function(t) {
  127. var validate = validator({
  128. type: 'object',
  129. properties: {
  130. foo: {
  131. type: 'number',
  132. required: true,
  133. enum: [42]
  134. }
  135. }
  136. })
  137. t.notOk(validate({}), 'is required')
  138. t.ok(validate({foo:42}))
  139. t.notOk(validate({foo:43}))
  140. t.end()
  141. })
  142. tape('minimum/maximum', function(t) {
  143. var validate = validator({
  144. type: 'object',
  145. properties: {
  146. foo: {
  147. type: 'number',
  148. minimum: 0,
  149. maximum: 0
  150. }
  151. }
  152. })
  153. t.notOk(validate({foo:-42}))
  154. t.ok(validate({foo:0}))
  155. t.notOk(validate({foo:42}))
  156. t.end()
  157. })
  158. tape('exclusiveMinimum/exclusiveMaximum', function(t) {
  159. var validate = validator({
  160. type: 'object',
  161. properties: {
  162. foo: {
  163. type: 'number',
  164. minimum: 10,
  165. maximum: 20,
  166. exclusiveMinimum: true,
  167. exclusiveMaximum: true
  168. }
  169. }
  170. })
  171. t.notOk(validate({foo:10}))
  172. t.ok(validate({foo:11}))
  173. t.notOk(validate({foo:20}))
  174. t.ok(validate({foo:19}))
  175. t.end()
  176. })
  177. tape('custom format', function(t) {
  178. var validate = validator({
  179. type: 'object',
  180. properties: {
  181. foo: {
  182. type: 'string',
  183. format: 'as'
  184. }
  185. }
  186. }, {formats: {as:/^a+$/}})
  187. t.notOk(validate({foo:''}), 'not as')
  188. t.notOk(validate({foo:'b'}), 'not as')
  189. t.notOk(validate({foo:'aaab'}), 'not as')
  190. t.ok(validate({foo:'a'}), 'as')
  191. t.ok(validate({foo:'aaaaaa'}), 'as')
  192. t.end()
  193. })
  194. tape('custom format function', function(t) {
  195. var validate = validator({
  196. type: 'object',
  197. properties: {
  198. foo: {
  199. type: 'string',
  200. format: 'as'
  201. }
  202. }
  203. }, {formats: {as:function(s) { return /^a+$/.test(s) } }})
  204. t.notOk(validate({foo:''}), 'not as')
  205. t.notOk(validate({foo:'b'}), 'not as')
  206. t.notOk(validate({foo:'aaab'}), 'not as')
  207. t.ok(validate({foo:'a'}), 'as')
  208. t.ok(validate({foo:'aaaaaa'}), 'as')
  209. t.end()
  210. })
  211. tape('do not mutate schema', function(t) {
  212. var sch = {
  213. items: [
  214. {}
  215. ],
  216. additionalItems: {
  217. type: 'integer'
  218. }
  219. }
  220. var copy = JSON.parse(JSON.stringify(sch))
  221. validator(sch)
  222. t.same(sch, copy, 'did not mutate')
  223. t.end()
  224. })
  225. tape('#toJSON()', function(t) {
  226. var schema = {
  227. required: true,
  228. type: 'object',
  229. properties: {
  230. hello: {type:'string', required:true}
  231. }
  232. }
  233. var validate = validator(schema)
  234. t.deepEqual(validate.toJSON(), schema, 'should return original schema')
  235. t.end()
  236. })
  237. tape('external schemas', function(t) {
  238. var ext = {type: 'string'}
  239. var schema = {
  240. required: true,
  241. $ref: '#ext'
  242. }
  243. var validate = validator(schema, {schemas: {ext:ext}})
  244. t.ok(validate('hello string'), 'is a string')
  245. t.notOk(validate(42), 'not a string')
  246. t.end()
  247. })
  248. tape('external schema URIs', function(t) {
  249. var ext = {type: 'string'}
  250. var schema = {
  251. required: true,
  252. $ref: 'http://example.com/schemas/schemaURIs'
  253. }
  254. var opts = {schemas:{}};
  255. opts.schemas['http://example.com/schemas/schemaURIs'] = ext;
  256. var validate = validator(schema, opts)
  257. t.ok(validate('hello string'), 'is a string')
  258. t.notOk(validate(42), 'not a string')
  259. t.end()
  260. })
  261. tape('top-level external schema', function(t) {
  262. var defs = {
  263. "string": {
  264. type: "string"
  265. },
  266. "sex": {
  267. type: "string",
  268. enum: ["male", "female", "other"]
  269. }
  270. }
  271. var schema = {
  272. type: "object",
  273. properties: {
  274. "name": { $ref: "definitions.json#/string" },
  275. "sex": { $ref: "definitions.json#/sex" }
  276. },
  277. required: ["name", "sex"]
  278. }
  279. var validate = validator(schema, {
  280. schemas: {
  281. "definitions.json": defs
  282. }
  283. })
  284. t.ok(validate({name:"alice", sex:"female"}), 'is an object')
  285. t.notOk(validate({name:"alice", sex: "bob"}), 'recognizes external schema')
  286. t.notOk(validate({name:2, sex: "female"}), 'recognizes external schema')
  287. t.end()
  288. })
  289. tape('nested required array decl', function(t) {
  290. var schema = {
  291. properties: {
  292. x: {
  293. type: 'object',
  294. properties: {
  295. y: {
  296. type: 'object',
  297. properties: {
  298. z: {
  299. type: 'string'
  300. }
  301. },
  302. required: ['z']
  303. }
  304. }
  305. }
  306. },
  307. required: ['x']
  308. }
  309. var validate = validator(schema)
  310. t.ok(validate({x: {}}), 'should be valid')
  311. t.notOk(validate({}), 'should not be valid')
  312. t.strictEqual(validate.errors[0].field, 'data.x', 'should output the missing field')
  313. t.end()
  314. })
  315. tape('verbose mode', function(t) {
  316. var schema = {
  317. required: true,
  318. type: 'object',
  319. properties: {
  320. hello: {
  321. required: true,
  322. type: 'string'
  323. }
  324. }
  325. };
  326. var validate = validator(schema, {verbose: true})
  327. t.ok(validate({hello: 'string'}), 'should be valid')
  328. t.notOk(validate({hello: 100}), 'should not be valid')
  329. t.strictEqual(validate.errors[0].value, 100, 'error object should contain the invalid value')
  330. t.strictEqual(validate.errors[0].type, 'string', 'error object should contain the type')
  331. t.end()
  332. })
  333. tape('additional props in verbose mode', function(t) {
  334. var schema = {
  335. type: 'object',
  336. required: true,
  337. additionalProperties: false,
  338. properties: {
  339. foo: {
  340. type: 'string'
  341. },
  342. 'hello world': {
  343. type: 'object',
  344. required: true,
  345. additionalProperties: false,
  346. properties: {
  347. foo: {
  348. type: 'string'
  349. }
  350. }
  351. }
  352. }
  353. };
  354. var validate = validator(schema, {verbose: true})
  355. validate({'hello world': {bar: 'string'}});
  356. t.strictEqual(validate.errors[0].value, 'data["hello world"].bar', 'should output the path to the additional prop in the error')
  357. t.end()
  358. })
  359. tape('Date.now() is an integer', function(t) {
  360. var schema = {type: 'integer'}
  361. var validate = validator(schema)
  362. t.ok(validate(Date.now()), 'is integer')
  363. t.end()
  364. })
  365. tape('field shows item index in arrays', function(t) {
  366. var schema = {
  367. type: 'array',
  368. items: {
  369. type: 'array',
  370. items: {
  371. properties: {
  372. foo: {
  373. type: 'string',
  374. required: true
  375. }
  376. }
  377. }
  378. }
  379. }
  380. var validate = validator(schema)
  381. validate([
  382. [
  383. { foo: 'test' },
  384. { foo: 'test' }
  385. ],
  386. [
  387. { foo: 'test' },
  388. { baz: 'test' }
  389. ]
  390. ])
  391. t.strictEqual(validate.errors[0].field, 'data.1.1.foo', 'should output the field with specific index of failing item in the error')
  392. t.end()
  393. })