joi 8.4.2 → 9.0.0-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/test/array.js ADDED
@@ -0,0 +1,1049 @@
1
+ 'use strict';
2
+
3
+ // Load modules
4
+
5
+ const Lab = require('lab');
6
+ const Code = require('code');
7
+ const Joi = require('../lib');
8
+ const Helper = require('./helper');
9
+
10
+
11
+ // Declare internals
12
+
13
+ const internals = {};
14
+
15
+
16
+ // Test shortcuts
17
+
18
+ const lab = exports.lab = Lab.script();
19
+ const describe = lab.describe;
20
+ const it = lab.it;
21
+ const expect = Code.expect;
22
+
23
+
24
+ describe('array', () => {
25
+
26
+ it('converts a string to an array', (done) => {
27
+
28
+ Joi.array().validate('[1,2,3]', (err, value) => {
29
+
30
+ expect(err).to.not.exist();
31
+ expect(value.length).to.equal(3);
32
+ done();
33
+ });
34
+ });
35
+
36
+ it('errors on non-array string', (done) => {
37
+
38
+ Joi.array().validate('{ "something": false }', (err, value) => {
39
+
40
+ expect(err).to.exist();
41
+ expect(err.message).to.equal('"value" must be an array');
42
+ done();
43
+ });
44
+ });
45
+
46
+ it('errors on number', (done) => {
47
+
48
+ Joi.array().validate(3, (err, value) => {
49
+
50
+ expect(err).to.exist();
51
+ expect(value).to.equal(3);
52
+ done();
53
+ });
54
+ });
55
+
56
+ it('converts a non-array string with number type', (done) => {
57
+
58
+ Joi.array().validate('3', (err, value) => {
59
+
60
+ expect(err).to.exist();
61
+ expect(value).to.equal('3');
62
+ done();
63
+ });
64
+ });
65
+
66
+ it('errors on a non-array string', (done) => {
67
+
68
+ Joi.array().validate('asdf', (err, value) => {
69
+
70
+ expect(err).to.exist();
71
+ expect(value).to.equal('asdf');
72
+ done();
73
+ });
74
+ });
75
+
76
+ describe('items()', () => {
77
+
78
+ it('converts members', (done) => {
79
+
80
+ const schema = Joi.array().items(Joi.number());
81
+ const input = ['1', '2', '3'];
82
+ schema.validate(input, (err, value) => {
83
+
84
+ expect(err).to.not.exist();
85
+ expect(value).to.deep.equal([1, 2, 3]);
86
+ done();
87
+ });
88
+ });
89
+
90
+ it('shows path to errors in array items', (done) => {
91
+
92
+ expect(() => {
93
+
94
+ Joi.array().items({
95
+ a: {
96
+ b: {
97
+ c: {
98
+ d: undefined
99
+ }
100
+ }
101
+ }
102
+ });
103
+ }).to.throw(Error, 'Invalid schema content: (0.a.b.c.d)');
104
+
105
+ expect(() => {
106
+
107
+ Joi.array().items({ foo: 'bar' }, undefined);
108
+ }).to.throw(Error, 'Invalid schema content: (1)');
109
+
110
+ done();
111
+ });
112
+
113
+ it('allows zero size', (done) => {
114
+
115
+ const schema = Joi.object({
116
+ test: Joi.array().items(Joi.object({
117
+ foo: Joi.string().required()
118
+ }))
119
+ });
120
+ const input = { test: [] };
121
+
122
+ schema.validate(input, (err, value) => {
123
+
124
+ expect(err).to.not.exist();
125
+ done();
126
+ });
127
+ });
128
+
129
+ it('returns the first error when only one inclusion', (done) => {
130
+
131
+ const schema = Joi.object({
132
+ test: Joi.array().items(Joi.object({
133
+ foo: Joi.string().required()
134
+ }))
135
+ });
136
+ const input = { test: [{ foo: 'a' }, { bar: 2 }] };
137
+
138
+ schema.validate(input, (err, value) => {
139
+
140
+ expect(err.message).to.equal('child "test" fails because ["test" at position 1 fails because [child "foo" fails because ["foo" is required]]]');
141
+ done();
142
+ });
143
+ });
144
+
145
+ it('validates multiple types added in two calls', (done) => {
146
+
147
+ const schema = Joi.array()
148
+ .items(Joi.number())
149
+ .items(Joi.string());
150
+
151
+ Helper.validate(schema, [
152
+ [[1, 2, 3], true],
153
+ [[50, 100, 1000], true],
154
+ [[1, 'a', 5, 10], true],
155
+ [['joi', 'everydaylowprices', 5000], true]
156
+ ], done);
157
+ });
158
+
159
+ it('validates multiple types with stripUnknown', (done) => {
160
+
161
+ const schema = Joi.array().items(Joi.number(), Joi.string()).options({ stripUnknown: true });
162
+
163
+ Helper.validate(schema, [
164
+ [[1, 2, 'a'], true, null, [1, 2, 'a']],
165
+ [[1, { foo: 'bar' }, 'a', 2], true, null, [1, 'a', 2]]
166
+ ], done);
167
+ });
168
+
169
+ it('allows forbidden to restrict values', (done) => {
170
+
171
+ const schema = Joi.array().items(Joi.string().valid('four').forbidden(), Joi.string());
172
+ const input = ['one', 'two', 'three', 'four'];
173
+
174
+ schema.validate(input, (err, value) => {
175
+
176
+ expect(err).to.exist();
177
+ expect(err.message).to.equal('"value" at position 3 contains an excluded value');
178
+ done();
179
+ });
180
+ });
181
+
182
+ it('validates that a required value exists', (done) => {
183
+
184
+ const schema = Joi.array().items(Joi.string().valid('four').required(), Joi.string());
185
+ const input = ['one', 'two', 'three'];
186
+
187
+ schema.validate(input, (err, value) => {
188
+
189
+ expect(err).to.exist();
190
+ expect(err.message).to.equal('"value" does not contain 1 required value(s)');
191
+ done();
192
+ });
193
+ });
194
+
195
+ it('validates that a required value exists with abortEarly = false', (done) => {
196
+
197
+ const schema = Joi.array().items(Joi.string().valid('four').required(), Joi.string()).options({ abortEarly: false });
198
+ const input = ['one', 'two', 'three'];
199
+
200
+ schema.validate(input, (err, value) => {
201
+
202
+ expect(err).to.exist();
203
+ expect(err.message).to.equal('"value" does not contain 1 required value(s)');
204
+ done();
205
+ });
206
+ });
207
+
208
+ it('does not re-run required tests that have already been matched', (done) => {
209
+
210
+ const schema = Joi.array().items(Joi.string().valid('four').required(), Joi.string());
211
+ const input = ['one', 'two', 'three', 'four', 'four', 'four'];
212
+
213
+ schema.validate(input, (err, value) => {
214
+
215
+ expect(err).to.not.exist();
216
+ expect(value).to.deep.equal(input);
217
+ done();
218
+ });
219
+ });
220
+
221
+ it('does not re-run required tests that have already failed', (done) => {
222
+
223
+ const schema = Joi.array().items(Joi.string().valid('four').required(), Joi.boolean().required(), Joi.number());
224
+ const input = ['one', 'two', 'three', 'four', 'four', 'four'];
225
+
226
+ schema.validate(input, (err, value) => {
227
+
228
+ expect(err).to.exist();
229
+ expect(err.message).to.equal('"value" at position 0 does not match any of the allowed types');
230
+ done();
231
+ });
232
+ });
233
+
234
+ it('can require duplicates of the same schema and fail', (done) => {
235
+
236
+ const schema = Joi.array().items(Joi.string().valid('four').required(), Joi.string().valid('four').required(), Joi.string());
237
+ const input = ['one', 'two', 'three', 'four'];
238
+
239
+ schema.validate(input, (err, value) => {
240
+
241
+ expect(err).to.exist();
242
+ expect(err.message).to.equal('"value" does not contain 1 required value(s)');
243
+ done();
244
+ });
245
+ });
246
+
247
+ it('can require duplicates of the same schema and pass', (done) => {
248
+
249
+ const schema = Joi.array().items(Joi.string().valid('four').required(), Joi.string().valid('four').required(), Joi.string());
250
+ const input = ['one', 'two', 'three', 'four', 'four'];
251
+
252
+ schema.validate(input, (err, value) => {
253
+
254
+ expect(err).to.not.exist();
255
+ expect(value).to.deep.equal(input);
256
+ done();
257
+ });
258
+ });
259
+
260
+ it('continues to validate after a required match', (done) => {
261
+
262
+ const schema = Joi.array().items(Joi.string().required(), Joi.boolean());
263
+ const input = [true, 'one', false, 'two'];
264
+
265
+ schema.validate(input, (err, value) => {
266
+
267
+ expect(err).to.not.exist();
268
+ expect(value).to.deep.equal(input);
269
+ done();
270
+ });
271
+ });
272
+
273
+ it('can use a label on a required parameter', (done) => {
274
+
275
+ const schema = Joi.array().items(Joi.string().required().label('required string'), Joi.boolean());
276
+ const input = [true, false];
277
+
278
+ schema.validate(input, (err, value) => {
279
+
280
+ expect(err).to.exist();
281
+ expect(err.message).to.equal('"value" does not contain [required string]');
282
+ done();
283
+ });
284
+ });
285
+
286
+ it('can use a label on one required parameter, and no label on another', (done) => {
287
+
288
+ const schema = Joi.array().items(Joi.string().required().label('required string'), Joi.string().required(), Joi.boolean());
289
+ const input = [true, false];
290
+
291
+ schema.validate(input, (err, value) => {
292
+
293
+ expect(err).to.exist();
294
+ expect(err.message).to.equal('"value" does not contain [required string] and 1 other required value(s)');
295
+ done();
296
+ });
297
+ });
298
+
299
+ it('can strip matching items', (done) => {
300
+
301
+ const schema = Joi.array().items(Joi.string(), Joi.any().strip());
302
+ schema.validate(['one', 'two', 3, 4], (err, value) => {
303
+
304
+ expect(err).to.not.exist();
305
+ expect(value).to.deep.equal(['one', 'two']);
306
+ done();
307
+ });
308
+ });
309
+ });
310
+
311
+ describe('min()', () => {
312
+
313
+ it('validates array size', (done) => {
314
+
315
+ const schema = Joi.array().min(2);
316
+ Helper.validate(schema, [
317
+ [[1, 2], true],
318
+ [[1], false]
319
+ ], done);
320
+ });
321
+
322
+ it('throws when limit is not a number', (done) => {
323
+
324
+ expect(() => {
325
+
326
+ Joi.array().min('a');
327
+ }).to.throw('limit must be a positive integer');
328
+ done();
329
+ });
330
+
331
+ it('throws when limit is not an integer', (done) => {
332
+
333
+ expect(() => {
334
+
335
+ Joi.array().min(1.2);
336
+ }).to.throw('limit must be a positive integer');
337
+ done();
338
+ });
339
+ });
340
+
341
+ describe('max()', () => {
342
+
343
+ it('validates array size', (done) => {
344
+
345
+ const schema = Joi.array().max(1);
346
+ Helper.validate(schema, [
347
+ [[1, 2], false],
348
+ [[1], true]
349
+ ], done);
350
+ });
351
+
352
+ it('throws when limit is not a number', (done) => {
353
+
354
+ expect(() => {
355
+
356
+ Joi.array().max('a');
357
+ }).to.throw('limit must be a positive integer');
358
+ done();
359
+ });
360
+
361
+ it('throws when limit is not an integer', (done) => {
362
+
363
+ expect(() => {
364
+
365
+ Joi.array().max(1.2);
366
+ }).to.throw('limit must be a positive integer');
367
+ done();
368
+ });
369
+ });
370
+
371
+ describe('length()', () => {
372
+
373
+ it('validates array size', (done) => {
374
+
375
+ const schema = Joi.array().length(2);
376
+ Helper.validate(schema, [
377
+ [[1, 2], true],
378
+ [[1], false]
379
+ ], done);
380
+ });
381
+
382
+ it('throws when limit is not a number', (done) => {
383
+
384
+ expect(() => {
385
+
386
+ Joi.array().length('a');
387
+ }).to.throw('limit must be a positive integer');
388
+ done();
389
+ });
390
+
391
+ it('throws when limit is not an integer', (done) => {
392
+
393
+ expect(() => {
394
+
395
+ Joi.array().length(1.2);
396
+ }).to.throw('limit must be a positive integer');
397
+ done();
398
+ });
399
+ });
400
+
401
+ describe('validate()', () => {
402
+
403
+ it('should, by default, allow undefined, allow empty array', (done) => {
404
+
405
+ Helper.validate(Joi.array(), [
406
+ [undefined, true],
407
+ [[], true]
408
+ ], done);
409
+ });
410
+
411
+ it('should, when .required(), deny undefined', (done) => {
412
+
413
+ Helper.validate(Joi.array().required(), [
414
+ [undefined, false]
415
+ ], done);
416
+ });
417
+
418
+ it('allows empty arrays', (done) => {
419
+
420
+ Helper.validate(Joi.array(), [
421
+ [undefined, true],
422
+ [[], true]
423
+ ], done);
424
+ });
425
+
426
+ it('excludes values when items are forbidden', (done) => {
427
+
428
+ Helper.validate(Joi.array().items(Joi.string().forbidden()), [
429
+ [['2', '1'], false],
430
+ [['1'], false],
431
+ [[2], true]
432
+ ], done);
433
+ });
434
+
435
+ it('allows types to be forbidden', (done) => {
436
+
437
+ const schema = Joi.array().items(Joi.number().forbidden());
438
+
439
+ const n = [1, 2, 'hippo'];
440
+ schema.validate(n, (err, value) => {
441
+
442
+ expect(err).to.exist();
443
+
444
+ const m = ['x', 'y', 'z'];
445
+ schema.validate(m, (err2, value2) => {
446
+
447
+ expect(err2).to.not.exist();
448
+ done();
449
+ });
450
+ });
451
+ });
452
+
453
+ it('validates array of Numbers', (done) => {
454
+
455
+ Helper.validate(Joi.array().items(Joi.number()), [
456
+ [[1, 2, 3], true],
457
+ [[50, 100, 1000], true],
458
+ [['a', 1, 2], false],
459
+ [['1', '2', 4], true]
460
+ ], done);
461
+ });
462
+
463
+ it('validates array of mixed Numbers & Strings', (done) => {
464
+
465
+ Helper.validate(Joi.array().items(Joi.number(), Joi.string()), [
466
+ [[1, 2, 3], true],
467
+ [[50, 100, 1000], true],
468
+ [[1, 'a', 5, 10], true],
469
+ [['joi', 'everydaylowprices', 5000], true]
470
+ ], done);
471
+ });
472
+
473
+ it('validates array of objects with schema', (done) => {
474
+
475
+ Helper.validate(Joi.array().items(Joi.object({ h1: Joi.number().required() })), [
476
+ [[{ h1: 1 }, { h1: 2 }, { h1: 3 }], true],
477
+ [[{ h2: 1, h3: 'somestring' }, { h1: 2 }, { h1: 3 }], false],
478
+ [[1, 2, [1]], false]
479
+ ], done);
480
+ });
481
+
482
+ it('errors on array of unallowed mixed types (Array)', (done) => {
483
+
484
+ Helper.validate(Joi.array().items(Joi.number()), [
485
+ [[1, 2, 3], true],
486
+ [[1, 2, [1]], false]
487
+ ], done);
488
+ });
489
+
490
+ it('errors on invalid number rule using includes', (done) => {
491
+
492
+ const schema = Joi.object({
493
+ arr: Joi.array().items(Joi.number().integer())
494
+ });
495
+
496
+ const input = { arr: [1, 2, 2.1] };
497
+ schema.validate(input, (err, value) => {
498
+
499
+ expect(err).to.exist();
500
+ expect(err.message).to.equal('child "arr" fails because ["arr" at position 2 fails because ["2" must be an integer]]');
501
+ done();
502
+ });
503
+ });
504
+
505
+ it('validates an array within an object', (done) => {
506
+
507
+ const schema = Joi.object({
508
+ array: Joi.array().items(Joi.string().min(5), Joi.number().min(3))
509
+ }).options({ convert: false });
510
+
511
+ Helper.validate(schema, [
512
+ [{ array: ['12345'] }, true],
513
+ [{ array: ['1'] }, false],
514
+ [{ array: [3] }, true],
515
+ [{ array: ['12345', 3] }, true]
516
+ ], done);
517
+ });
518
+
519
+ it('should not change original value', (done) => {
520
+
521
+ const schema = Joi.array().items(Joi.number()).unique();
522
+ const input = ['1', '2'];
523
+
524
+ schema.validate(input, (err, value) => {
525
+
526
+ expect(err).to.not.exist();
527
+ expect(value).to.deep.equal([1, 2]);
528
+ expect(input).to.deep.equal(['1', '2']);
529
+ done();
530
+ });
531
+ });
532
+
533
+ it('should have multiple errors if abort early is false', (done) => {
534
+
535
+ const schema = Joi.array().items(Joi.number(), Joi.object()).items(Joi.boolean().forbidden());
536
+ const input = [1, undefined, true, 'a'];
537
+
538
+ Joi.validate(input, schema, { abortEarly: false }, (err, value) => {
539
+
540
+ expect(err).to.exist();
541
+ expect(err.details).to.deep.equal([{
542
+ message: '"value" must not be a sparse array',
543
+ path: '1',
544
+ type: 'array.sparse',
545
+ context: {
546
+ key: 'value'
547
+ }
548
+ }, {
549
+ message: '"value" at position 2 contains an excluded value',
550
+ path: '2',
551
+ type: 'array.excludes',
552
+ context: {
553
+ pos: 2,
554
+ key: 'value',
555
+ value: true
556
+ }
557
+ }, {
558
+ message: '"value" at position 3 does not match any of the allowed types',
559
+ path: '3',
560
+ type: 'array.includes',
561
+ context: {
562
+ pos: 3,
563
+ key: 'value',
564
+ value: 'a'
565
+ }
566
+ }]);
567
+ done();
568
+ });
569
+ });
570
+ });
571
+
572
+ describe('describe()', () => {
573
+
574
+ it('returns an empty description when no rules are applied', (done) => {
575
+
576
+ const schema = Joi.array();
577
+ const desc = schema.describe();
578
+ expect(desc).to.deep.equal({
579
+ type: 'array',
580
+ flags: { sparse: false }
581
+ });
582
+ done();
583
+ });
584
+
585
+ it('returns an updated description when sparse rule is applied', (done) => {
586
+
587
+ const schema = Joi.array().sparse();
588
+ const desc = schema.describe();
589
+ expect(desc).to.deep.equal({
590
+ type: 'array',
591
+ flags: { sparse: true }
592
+ });
593
+ done();
594
+ });
595
+
596
+ it('returns an items array only if items are specified', (done) => {
597
+
598
+ const schema = Joi.array().items().max(5);
599
+ const desc = schema.describe();
600
+ expect(desc.items).to.not.exist();
601
+ done();
602
+ });
603
+
604
+ it('returns a recursively defined array of items when specified', (done) => {
605
+
606
+ const schema = Joi.array()
607
+ .items(Joi.number(), Joi.string())
608
+ .items(Joi.boolean().forbidden())
609
+ .ordered(Joi.number(), Joi.string())
610
+ .ordered(Joi.string().required());
611
+ const desc = schema.describe();
612
+ expect(desc.items).to.have.length(3);
613
+ expect(desc).to.deep.equal({
614
+ type: 'array',
615
+ flags: { sparse: false },
616
+ orderedItems: [{ type: 'number', invalids: [Infinity, -Infinity] }, { type: 'string', invalids: [''] }, { type: 'string', invalids: [''], flags: { presence: 'required' } }],
617
+ items: [{ type: 'number', invalids: [Infinity, -Infinity] }, { type: 'string', invalids: [''] }, { type: 'boolean', flags: { presence: 'forbidden' } }]
618
+ });
619
+
620
+ done();
621
+ });
622
+ });
623
+
624
+ describe('unique()', () => {
625
+
626
+ it('errors if duplicate numbers, strings, objects, binaries, functions, dates and booleans', (done) => {
627
+
628
+ const buffer = new Buffer('hello world');
629
+ const func = function () {};
630
+ const now = new Date();
631
+ const schema = Joi.array().sparse().unique();
632
+
633
+ Helper.validate(schema, [
634
+ [[2, 2], false],
635
+ [[0x2, 2], false],
636
+ [['duplicate', 'duplicate'], false],
637
+ [[{ a: 'b' }, { a: 'b' }], false],
638
+ [[buffer, buffer], false],
639
+ [[func, func], false],
640
+ [[now, now], false],
641
+ [[true, true], false],
642
+ [[undefined, undefined], false]
643
+ ], done);
644
+ });
645
+
646
+ it('ignores duplicates if they are of different types', (done) => {
647
+
648
+ const schema = Joi.array().unique();
649
+
650
+ Helper.validate(schema, [
651
+ [[2, '2'], true]
652
+ ], done);
653
+ });
654
+
655
+ it('validates without duplicates', (done) => {
656
+
657
+ const buffer = new Buffer('hello world');
658
+ const buffer2 = new Buffer('Hello world');
659
+ const func = function () {};
660
+ const func2 = function () {};
661
+ const now = new Date();
662
+ const now2 = new Date(+now + 100);
663
+ const schema = Joi.array().unique();
664
+
665
+ Helper.validate(schema, [
666
+ [[1, 2], true],
667
+ [['s1', 's2'], true],
668
+ [[{ a: 'b' }, { a: 'c' }], true],
669
+ [[buffer, buffer2], true],
670
+ [[func, func2], true],
671
+ [[now, now2], true],
672
+ [[true, false], true]
673
+ ], done);
674
+ });
675
+ });
676
+
677
+ describe('sparse()', () => {
678
+
679
+ it('errors on undefined value', (done) => {
680
+
681
+ const schema = Joi.array().items(Joi.number());
682
+
683
+ Helper.validate(schema, [
684
+ [[undefined], false],
685
+ [[2, undefined], false]
686
+ ], done);
687
+ });
688
+
689
+ it('validates on undefined value with sparse', (done) => {
690
+
691
+ const schema = Joi.array().items(Joi.number()).sparse();
692
+
693
+ Helper.validate(schema, [
694
+ [[undefined], true],
695
+ [[2, undefined], true]
696
+ ], done);
697
+ });
698
+
699
+ it('switches the sparse flag', (done) => {
700
+
701
+ const schema = Joi.array().sparse();
702
+ const desc = schema.describe();
703
+ expect(desc).to.deep.equal({
704
+ type: 'array',
705
+ flags: { sparse: true }
706
+ });
707
+ done();
708
+ });
709
+
710
+ it('switches the sparse flag with explicit value', (done) => {
711
+
712
+ const schema = Joi.array().sparse(true);
713
+ const desc = schema.describe();
714
+ expect(desc).to.deep.equal({
715
+ type: 'array',
716
+ flags: { sparse: true }
717
+ });
718
+ done();
719
+ });
720
+
721
+ it('switches the sparse flag back', (done) => {
722
+
723
+ const schema = Joi.array().sparse().sparse(false);
724
+ const desc = schema.describe();
725
+ expect(desc).to.deep.equal({
726
+ type: 'array',
727
+ flags: { sparse: false }
728
+ });
729
+ done();
730
+ });
731
+ });
732
+
733
+ describe('single()', () => {
734
+
735
+ it('should allow a single element', (done) => {
736
+
737
+ const schema = Joi.array().items(Joi.number()).items(Joi.boolean().forbidden()).single();
738
+
739
+ Helper.validate(schema, [
740
+ [[1, 2, 3], true],
741
+ [1, true],
742
+ [['a'], false, null, '"value" at position 0 fails because ["0" must be a number]'],
743
+ ['a', false, null, 'single value of "value" fails because ["value" must be a number]'],
744
+ [true, false, null, 'single value of "value" contains an excluded value']
745
+ ], done);
746
+ });
747
+
748
+ it('should allow a single element with multiple types', (done) => {
749
+
750
+ const schema = Joi.array().items(Joi.number(), Joi.string()).single();
751
+
752
+ Helper.validate(schema, [
753
+ [[1, 2, 3], true],
754
+ [1, true],
755
+ [[1, 'a'], true],
756
+ ['a', true],
757
+ [true, false, null, 'single value of "value" does not match any of the allowed types']
758
+ ], done);
759
+ });
760
+
761
+ it('should allow nested arrays', (done) => {
762
+
763
+ const schema = Joi.array().items(Joi.array().items(Joi.number())).single();
764
+
765
+ Helper.validate(schema, [
766
+ [[[1], [2], [3]], true],
767
+ [[1, 2, 3], true],
768
+ [[['a']], false, null, '"value" at position 0 fails because ["0" at position 0 fails because ["0" must be a number]]'],
769
+ [['a'], false, null, '"value" at position 0 fails because ["0" must be an array]'],
770
+ ['a', false, null, 'single value of "value" fails because ["value" must be an array]'],
771
+ [1, false, null, 'single value of "value" fails because ["value" must be an array]'],
772
+ [true, false, null, 'single value of "value" fails because ["value" must be an array]']
773
+ ], done);
774
+ });
775
+
776
+ it('should allow nested arrays with multiple types', (done) => {
777
+
778
+ const schema = Joi.array().items(Joi.array().items(Joi.number(), Joi.boolean())).single();
779
+
780
+ Helper.validate(schema, [
781
+ [[[1, true]], true],
782
+ [[1, true], true],
783
+ [[[1, 'a']], false, null, '"value" at position 0 fails because ["0" at position 1 does not match any of the allowed types]'],
784
+ [[1, 'a'], false, null, '"value" at position 0 fails because ["0" must be an array]']
785
+ ], done);
786
+ });
787
+
788
+ it('switches the single flag with explicit value', (done) => {
789
+
790
+ const schema = Joi.array().single(true);
791
+ const desc = schema.describe();
792
+ expect(desc).to.deep.equal({
793
+ type: 'array',
794
+ flags: { sparse: false, single: true }
795
+ });
796
+ done();
797
+ });
798
+
799
+ it('switches the single flag back', (done) => {
800
+
801
+ const schema = Joi.array().single().single(false);
802
+ const desc = schema.describe();
803
+ expect(desc).to.deep.equal({
804
+ type: 'array',
805
+ flags: { sparse: false, single: false }
806
+ });
807
+ done();
808
+ });
809
+ });
810
+
811
+ describe('options()', () => {
812
+
813
+ it('respects stripUnknown', (done) => {
814
+
815
+ const schema = Joi.array().items(Joi.string()).options({ stripUnknown: true });
816
+ schema.validate(['one', 'two', 3, 4, true, false], (err, value) => {
817
+
818
+ expect(err).to.not.exist();
819
+ expect(value).to.deep.equal(['one', 'two']);
820
+ done();
821
+ });
822
+ });
823
+ });
824
+
825
+ describe('ordered()', () => {
826
+
827
+ it('shows path to errors in array ordered items', (done) => {
828
+
829
+ expect(() => {
830
+
831
+ Joi.array().ordered({
832
+ a: {
833
+ b: {
834
+ c: {
835
+ d: undefined
836
+ }
837
+ }
838
+ }
839
+ });
840
+ }).to.throw(Error, 'Invalid schema content: (0.a.b.c.d)');
841
+
842
+ expect(() => {
843
+
844
+ Joi.array().ordered({ foo: 'bar' }, undefined);
845
+ }).to.throw(Error, 'Invalid schema content: (1)');
846
+
847
+ done();
848
+ });
849
+
850
+ it('validates input against items in order', (done) => {
851
+
852
+ const schema = Joi.array().ordered([Joi.string().required(), Joi.number().required()]);
853
+ const input = ['s1', 2];
854
+ schema.validate(input, (err, value) => {
855
+
856
+ expect(err).to.not.exist();
857
+ expect(value).to.deep.equal(['s1', 2]);
858
+ done();
859
+ });
860
+ });
861
+
862
+ it('validates input with optional item', (done) => {
863
+
864
+ const schema = Joi.array().ordered([Joi.string().required(), Joi.number().required(), Joi.number()]);
865
+ const input = ['s1', 2, 3];
866
+
867
+ schema.validate(input, (err, value) => {
868
+
869
+ expect(err).to.not.exist();
870
+ expect(value).to.deep.equal(['s1', 2, 3]);
871
+ done();
872
+ });
873
+ });
874
+
875
+ it('validates input without optional item', (done) => {
876
+
877
+ const schema = Joi.array().ordered([Joi.string().required(), Joi.number().required(), Joi.number()]);
878
+ const input = ['s1', 2];
879
+
880
+ schema.validate(input, (err, value) => {
881
+
882
+ expect(err).to.not.exist();
883
+ expect(value).to.deep.equal(['s1', 2]);
884
+ done();
885
+ });
886
+ });
887
+
888
+ it('validates input without optional item', (done) => {
889
+
890
+ const schema = Joi.array().ordered([Joi.string().required(), Joi.number().required(), Joi.number()]).sparse(true);
891
+ const input = ['s1', 2, undefined];
892
+
893
+ schema.validate(input, (err, value) => {
894
+
895
+ expect(err).to.not.exist();
896
+ expect(value).to.deep.equal(['s1', 2, undefined]);
897
+ done();
898
+ });
899
+ });
900
+
901
+ it('validates input without optional item in a sparse array', (done) => {
902
+
903
+ const schema = Joi.array().ordered([Joi.string().required(), Joi.number(), Joi.number().required()]).sparse(true);
904
+ const input = ['s1', undefined, 3];
905
+
906
+ schema.validate(input, (err, value) => {
907
+
908
+ expect(err).to.not.exist();
909
+ expect(value).to.deep.equal(['s1', undefined, 3]);
910
+ done();
911
+ });
912
+ });
913
+
914
+ it('validates when input matches ordered items and matches regular items', (done) => {
915
+
916
+ const schema = Joi.array().ordered([Joi.string().required(), Joi.number().required()]).items(Joi.number());
917
+ const input = ['s1', 2, 3, 4, 5];
918
+ schema.validate(input, (err, value) => {
919
+
920
+ expect(err).to.not.exist();
921
+ expect(value).to.deep.equal(['s1', 2, 3, 4, 5]);
922
+ done();
923
+ });
924
+ });
925
+
926
+ it('errors when input does not match ordered items', (done) => {
927
+
928
+ const schema = Joi.array().ordered([Joi.number().required(), Joi.string().required()]);
929
+ const input = ['s1', 2];
930
+ schema.validate(input, (err, value) => {
931
+
932
+ expect(err).to.exist();
933
+ expect(err.message).to.equal('"value" at position 0 fails because ["0" must be a number]');
934
+ done();
935
+ });
936
+ });
937
+
938
+ it('errors when input has more items than ordered items', (done) => {
939
+
940
+ const schema = Joi.array().ordered([Joi.number().required(), Joi.string().required()]);
941
+ const input = [1, 's2', 3];
942
+ schema.validate(input, (err, value) => {
943
+
944
+ expect(err).to.exist();
945
+ expect(err.message).to.equal('"value" at position 2 fails because array must contain at most 2 items');
946
+ done();
947
+ });
948
+ });
949
+
950
+ it('errors when input has more items than ordered items with abortEarly = false', (done) => {
951
+
952
+ const schema = Joi.array().ordered([Joi.string(), Joi.number()]).options({ abortEarly: false });
953
+ const input = [1, 2, 3, 4, 5];
954
+ schema.validate(input, (err, value) => {
955
+
956
+ expect(err).to.exist();
957
+ expect(err.message).to.equal('"value" at position 0 fails because ["0" must be a string]. "value" at position 2 fails because array must contain at most 2 items. "value" at position 3 fails because array must contain at most 2 items. "value" at position 4 fails because array must contain at most 2 items');
958
+ expect(err.details).to.have.length(4);
959
+ done();
960
+ });
961
+ });
962
+
963
+ it('errors when input has less items than ordered items', (done) => {
964
+
965
+ const schema = Joi.array().ordered([Joi.number().required(), Joi.string().required()]);
966
+ const input = [1];
967
+ schema.validate(input, (err, value) => {
968
+
969
+ expect(err).to.exist();
970
+ expect(err.message).to.equal('"value" does not contain 1 required value(s)');
971
+ done();
972
+ });
973
+ });
974
+
975
+ it('errors when input matches ordered items but not matches regular items', (done) => {
976
+
977
+ const schema = Joi.array().ordered([Joi.string().required(), Joi.number().required()]).items(Joi.number()).options({ abortEarly: false });
978
+ const input = ['s1', 2, 3, 4, 's5'];
979
+ schema.validate(input, (err, value) => {
980
+
981
+ expect(err).to.exist();
982
+ expect(err.message).to.equal('"value" at position 4 fails because ["4" must be a number]');
983
+ done();
984
+ });
985
+ });
986
+
987
+ it('errors when input does not match ordered items but matches regular items', (done) => {
988
+
989
+ const schema = Joi.array().ordered([Joi.string(), Joi.number()]).items(Joi.number()).options({ abortEarly: false });
990
+ const input = [1, 2, 3, 4, 5];
991
+ schema.validate(input, (err, value) => {
992
+
993
+ expect(err).to.exist();
994
+ expect(err.message).to.equal('"value" at position 0 fails because ["0" must be a string]');
995
+ done();
996
+ });
997
+ });
998
+
999
+ it('errors when input does not match ordered items not matches regular items', (done) => {
1000
+
1001
+ const schema = Joi.array().ordered([Joi.string(), Joi.number()]).items(Joi.string()).options({ abortEarly: false });
1002
+ const input = [1, 2, 3, 4, 5];
1003
+ schema.validate(input, (err, value) => {
1004
+
1005
+ expect(err).to.exist();
1006
+ expect(err.message).to.equal('"value" at position 0 fails because ["0" must be a string]. "value" at position 2 fails because ["2" must be a string]. "value" at position 3 fails because ["3" must be a string]. "value" at position 4 fails because ["4" must be a string]');
1007
+ expect(err.details).to.have.length(4);
1008
+ done();
1009
+ });
1010
+ });
1011
+
1012
+ it('errors but continues when abortEarly is set to false', (done) => {
1013
+
1014
+ const schema = Joi.array().ordered([Joi.number().required(), Joi.string().required()]).options({ abortEarly: false });
1015
+ const input = ['s1', 2];
1016
+ schema.validate(input, (err, value) => {
1017
+
1018
+ expect(err).to.exist();
1019
+ expect(err.message).to.equal('"value" at position 0 fails because ["0" must be a number]. "value" at position 1 fails because ["1" must be a string]');
1020
+ expect(err.details).to.have.length(2);
1021
+ done();
1022
+ });
1023
+ });
1024
+
1025
+ it('strips item', (done) => {
1026
+
1027
+ const schema = Joi.array().ordered([Joi.string().required(), Joi.number().strip(), Joi.number().required()]);
1028
+ const input = ['s1', 2, 3];
1029
+ schema.validate(input, (err, value) => {
1030
+
1031
+ expect(err).to.not.exist();
1032
+ expect(value).to.deep.equal(['s1', 3]);
1033
+ done();
1034
+ });
1035
+ });
1036
+
1037
+ it('strips multiple items', (done) => {
1038
+
1039
+ const schema = Joi.array().ordered([Joi.string().strip(), Joi.number(), Joi.number().strip()]);
1040
+ const input = ['s1', 2, 3];
1041
+ schema.validate(input, (err, value) => {
1042
+
1043
+ expect(err).to.not.exist();
1044
+ expect(value).to.deep.equal([2]);
1045
+ done();
1046
+ });
1047
+ });
1048
+ });
1049
+ });