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.
@@ -0,0 +1,490 @@
1
+ 'use strict';
2
+
3
+ // Load modules
4
+
5
+ const Lab = require('lab');
6
+ const Code = require('code');
7
+ const Joi = require('..');
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('alternatives', () => {
25
+
26
+ it('fails when no alternatives are provided', (done) => {
27
+
28
+ Joi.alternatives().validate('a', (err, value) => {
29
+
30
+ expect(err).to.exist();
31
+ expect(err.message).to.equal('"value" not matching any of the allowed alternatives');
32
+ done();
33
+ });
34
+ });
35
+
36
+ it('allows undefined when no alternatives are provided', (done) => {
37
+
38
+ Joi.alternatives().validate(undefined, (err, value) => {
39
+
40
+ expect(err).to.not.exist();
41
+ done();
42
+ });
43
+ });
44
+
45
+ it('applies modifiers when higher priority converts', (done) => {
46
+
47
+ const schema = Joi.object({
48
+ a: [
49
+ Joi.number(),
50
+ Joi.string()
51
+ ]
52
+ });
53
+
54
+ schema.validate({ a: '5' }, (err, value) => {
55
+
56
+ expect(err).to.not.exist();
57
+ expect(value.a).to.equal(5);
58
+ done();
59
+ });
60
+ });
61
+
62
+ it('applies modifiers when lower priority valid is a match', (done) => {
63
+
64
+ const schema = Joi.object({
65
+ a: [
66
+ Joi.number(),
67
+ Joi.valid('5')
68
+ ]
69
+ });
70
+
71
+ schema.validate({ a: '5' }, (err, value) => {
72
+
73
+ expect(err).to.not.exist();
74
+ expect(value.a).to.equal(5);
75
+ done();
76
+ });
77
+ });
78
+
79
+ it('does not apply modifier if alternative fails', (done) => {
80
+
81
+ const schema = Joi.object({
82
+ a: [
83
+ Joi.object({ c: Joi.any(), d: Joi.number() }).rename('b', 'c'),
84
+ { b: Joi.any(), d: Joi.string() }
85
+ ]
86
+ });
87
+
88
+ const input = { a: { b: 'any', d: 'string' } };
89
+ schema.validate(input, (err, value) => {
90
+
91
+ expect(err).to.not.exist();
92
+ expect(value.a.b).to.equal('any');
93
+ done();
94
+ });
95
+ });
96
+
97
+ describe('try()', () => {
98
+
99
+ it('throws when missing alternatives', (done) => {
100
+
101
+ expect(() => {
102
+
103
+ Joi.alternatives().try();
104
+ }).to.throw('Cannot add other alternatives without at least one schema');
105
+ done();
106
+ });
107
+ });
108
+
109
+ describe('when()', () => {
110
+
111
+ it('throws on invalid ref (not string)', (done) => {
112
+
113
+ expect(() => {
114
+
115
+ Joi.alternatives().when(5, { is: 6, then: Joi.number() });
116
+ }).to.throw('Invalid reference: 5');
117
+ done();
118
+ });
119
+
120
+ it('validates conditional alternatives', (done) => {
121
+
122
+ const schema = {
123
+ a: Joi.alternatives().when('b', { is: 5, then: 'x', otherwise: 'y' })
124
+ .try('z'),
125
+ b: Joi.any()
126
+ };
127
+
128
+ Helper.validate(schema, [
129
+ [{ a: 'x', b: 5 }, true],
130
+ [{ a: 'x', b: 6 }, false],
131
+ [{ a: 'y', b: 5 }, false],
132
+ [{ a: 'y', b: 6 }, true],
133
+ [{ a: 'z', b: 5 }, false],
134
+ [{ a: 'z', b: 6 }, false]
135
+ ], done);
136
+ });
137
+
138
+ it('validates conditional alternatives (empty key)', (done) => {
139
+
140
+ const schema = {
141
+ a: Joi.alternatives().when('', { is: 5, then: 'x', otherwise: 'y' })
142
+ .try('z'),
143
+ '': Joi.any()
144
+ };
145
+
146
+ Helper.validate(schema, [
147
+ [{ a: 'x', '': 5 }, true],
148
+ [{ a: 'x', '': 6 }, false],
149
+ [{ a: 'y', '': 5 }, false],
150
+ [{ a: 'y', '': 6 }, true],
151
+ [{ a: 'z', '': 5 }, false],
152
+ [{ a: 'z', '': 6 }, false]
153
+ ], done);
154
+ });
155
+
156
+ it('validates only then', (done) => {
157
+
158
+ const schema = {
159
+ a: Joi.alternatives().when(Joi.ref('b'), { is: 5, then: 'x' })
160
+ .try('z'),
161
+ b: Joi.any()
162
+ };
163
+
164
+ Helper.validate(schema, [
165
+ [{ a: 'x', b: 5 }, true],
166
+ [{ a: 'x', b: 6 }, false],
167
+ [{ a: 'y', b: 5 }, false],
168
+ [{ a: 'y', b: 6 }, false],
169
+ [{ a: 'z', b: 5 }, false],
170
+ [{ a: 'z', b: 6 }, true]
171
+ ], done);
172
+ });
173
+
174
+ it('validates only otherwise', (done) => {
175
+
176
+ const schema = {
177
+ a: Joi.alternatives().when('b', { is: 5, otherwise: 'y' })
178
+ .try('z'),
179
+ b: Joi.any()
180
+ };
181
+
182
+ Helper.validate(schema, [
183
+ [{ a: 'x', b: 5 }, false],
184
+ [{ a: 'x', b: 6 }, false],
185
+ [{ a: 'y', b: 5 }, false],
186
+ [{ a: 'y', b: 6 }, true],
187
+ [{ a: 'z', b: 5 }, true],
188
+ [{ a: 'z', b: 6 }, false]
189
+ ], done);
190
+ });
191
+
192
+ it('validates when is is null', (done) => {
193
+
194
+ const schema = {
195
+ a: Joi.alternatives().when('b', { is: null, then: 'x', otherwise: Joi.number() }),
196
+ b: Joi.any()
197
+ };
198
+
199
+ Helper.validate(schema, [
200
+ [{ a: 1 }, true],
201
+ [{ a: 'y' }, false],
202
+ [{ a: 'x', b: null }, true],
203
+ [{ a: 'y', b: null }, false],
204
+ [{ a: 1, b: null }, false]
205
+ ], done);
206
+ });
207
+
208
+ it('validates when is has ref', (done) => {
209
+
210
+ const schema = {
211
+ a: Joi.alternatives().when('b', { is: Joi.ref('c'), then: 'x' }),
212
+ b: Joi.any(),
213
+ c: Joi.number()
214
+ };
215
+
216
+ Helper.validate(schema, [
217
+ [{ a: 'x', b: 5, c: '5' }, true],
218
+ [{ a: 'x', b: 5, c: '1' }, false],
219
+ [{ a: 'x', b: '5', c: '5' }, false],
220
+ [{ a: 'y', b: 5, c: 5 }, false],
221
+ [{ a: 'y' }, false]
222
+ ], done);
223
+ });
224
+
225
+ it('validates when then has ref', (done) => {
226
+
227
+ const schema = {
228
+ a: Joi.alternatives().when('b', { is: 5, then: Joi.ref('c') }),
229
+ b: Joi.any(),
230
+ c: Joi.number()
231
+ };
232
+
233
+ Helper.validate(schema, [
234
+ [{ a: 'x', b: 5, c: '1' }, false],
235
+ [{ a: 1, b: 5, c: '1' }, true],
236
+ [{ a: '1', b: 5, c: '1' }, false]
237
+ ], done);
238
+ });
239
+
240
+ it('validates when otherwise has ref', (done) => {
241
+
242
+ const schema = {
243
+ a: Joi.alternatives().when('b', { is: 6, otherwise: Joi.ref('c') }),
244
+ b: Joi.any(),
245
+ c: Joi.number()
246
+ };
247
+
248
+ Helper.validate(schema, [
249
+ [{ a: 'x', b: 5, c: '1' }, false],
250
+ [{ a: 1, b: 5, c: '1' }, true],
251
+ [{ a: '1', b: 5, c: '1' }, false]
252
+ ], done);
253
+ });
254
+
255
+ it('validates when empty value', (done) => {
256
+
257
+ const schema = {
258
+ a: Joi.alternatives().when('b', { is: true, then: Joi.required() }),
259
+ b: Joi.boolean().default(false)
260
+ };
261
+
262
+ Helper.validate(schema, [
263
+ [{ b: false }, true],
264
+ [{ b: true }, true] // true because required() only applies to the one alternative
265
+ ], done);
266
+ });
267
+
268
+ it('validates when missing value', (done) => {
269
+
270
+ const schema = Joi.object({
271
+ a: Joi.alternatives().when('b', { is: 5, then: Joi.optional(), otherwise: Joi.required() }).required(),
272
+ b: Joi.number()
273
+ });
274
+
275
+ Helper.validate(schema, [
276
+ [{ a: 1 }, true],
277
+ [{}, false],
278
+ [{ b: 1 }, false],
279
+ [{ a: 1, b: 1 }, true],
280
+ [{ a: 1, b: 5 }, true],
281
+ [{ b: 5 }, false]
282
+ ], done);
283
+ });
284
+ });
285
+
286
+ describe('describe()', () => {
287
+
288
+ it('describes when', (done) => {
289
+
290
+ const schema = {
291
+ a: Joi.alternatives().when('b', { is: 5, then: 'x', otherwise: 'y' })
292
+ .try('z'),
293
+ b: Joi.any()
294
+ };
295
+
296
+ const outcome = {
297
+ type: 'object',
298
+ children: {
299
+ b: {
300
+ type: 'any'
301
+ },
302
+ a: {
303
+ type: 'alternatives',
304
+ alternatives: [
305
+ {
306
+ ref: 'ref:b',
307
+ is: {
308
+ type: 'number',
309
+ flags: {
310
+ allowOnly: true,
311
+ presence: 'required'
312
+ },
313
+ valids: [5],
314
+ invalids: [Infinity, -Infinity]
315
+ },
316
+ then: {
317
+ type: 'string',
318
+ flags: {
319
+ allowOnly: true
320
+ },
321
+ valids: ['x'],
322
+ invalids: ['']
323
+ },
324
+ otherwise: {
325
+ type: 'string',
326
+ flags: {
327
+ allowOnly: true
328
+ },
329
+ valids: ['y'],
330
+ invalids: ['']
331
+ }
332
+ },
333
+ {
334
+ type: 'string',
335
+ flags: {
336
+ allowOnly: true
337
+ },
338
+ valids: ['z'],
339
+ invalids: ['']
340
+ }
341
+ ]
342
+ }
343
+ }
344
+ };
345
+
346
+ expect(Joi.describe(schema)).to.deep.equal(outcome);
347
+ done();
348
+ });
349
+
350
+ it('describes when (only then)', (done) => {
351
+
352
+ const schema = {
353
+ a: Joi.alternatives().when('b', { is: 5, then: 'x' })
354
+ .try('z'),
355
+ b: Joi.any()
356
+ };
357
+
358
+ const outcome = {
359
+ type: 'object',
360
+ children: {
361
+ b: {
362
+ type: 'any'
363
+ },
364
+ a: {
365
+ type: 'alternatives',
366
+ alternatives: [
367
+ {
368
+ ref: 'ref:b',
369
+ is: {
370
+ type: 'number',
371
+ flags: {
372
+ allowOnly: true,
373
+ presence: 'required'
374
+ },
375
+ valids: [5],
376
+ invalids: [Infinity, -Infinity]
377
+ },
378
+ then: {
379
+ type: 'string',
380
+ flags: {
381
+ allowOnly: true
382
+ },
383
+ valids: ['x'],
384
+ invalids: ['']
385
+ }
386
+ },
387
+ {
388
+ type: 'string',
389
+ flags: {
390
+ allowOnly: true
391
+ },
392
+ valids: ['z'],
393
+ invalids: ['']
394
+ }
395
+ ]
396
+ }
397
+ }
398
+ };
399
+
400
+ expect(Joi.describe(schema)).to.deep.equal(outcome);
401
+ done();
402
+ });
403
+
404
+ it('describes when (only otherwise)', (done) => {
405
+
406
+ const schema = {
407
+ a: Joi.alternatives().when('b', { is: 5, otherwise: 'y' })
408
+ .try('z'),
409
+ b: Joi.any()
410
+ };
411
+
412
+ const outcome = {
413
+ type: 'object',
414
+ children: {
415
+ b: {
416
+ type: 'any'
417
+ },
418
+ a: {
419
+ type: 'alternatives',
420
+ alternatives: [
421
+ {
422
+ ref: 'ref:b',
423
+ is: {
424
+ type: 'number',
425
+ flags: {
426
+ allowOnly: true,
427
+ presence: 'required'
428
+ },
429
+ valids: [5],
430
+ invalids: [Infinity, -Infinity]
431
+ },
432
+ otherwise: {
433
+ type: 'string',
434
+ flags: {
435
+ allowOnly: true
436
+ },
437
+ valids: ['y'],
438
+ invalids: ['']
439
+ }
440
+ },
441
+ {
442
+ type: 'string',
443
+ flags: {
444
+ allowOnly: true
445
+ },
446
+ valids: ['z'],
447
+ invalids: ['']
448
+ }
449
+ ]
450
+ }
451
+ }
452
+ };
453
+
454
+ expect(Joi.describe(schema)).to.deep.equal(outcome);
455
+ done();
456
+ });
457
+
458
+ it('describes inherited fields (from any)', (done) => {
459
+
460
+ const schema = Joi.alternatives()
461
+ .try('a')
462
+ .description('d')
463
+ .example('a')
464
+ .meta('b')
465
+ .meta('c')
466
+ .notes('f')
467
+ .tags('g');
468
+
469
+ const outcome = {
470
+ type: 'alternatives',
471
+ description: 'd',
472
+ notes: ['f'],
473
+ tags: ['g'],
474
+ meta: ['b', 'c'],
475
+ examples: ['a'],
476
+ alternatives: [{
477
+ type: 'string',
478
+ flags: {
479
+ allowOnly: true
480
+ },
481
+ valids: ['a'],
482
+ invalids: ['']
483
+ }]
484
+ };
485
+
486
+ expect(Joi.describe(schema)).to.deep.equal(outcome);
487
+ done();
488
+ });
489
+ });
490
+ });