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/.eslintignore +2 -0
- package/.github/CONTRIBUTING.md +14 -0
- package/.github/ISSUE_TEMPLATE.md +19 -0
- package/.npmignore +21 -0
- package/.travis.yml +8 -0
- package/API.md +1690 -0
- package/README.md +1 -1
- package/examples/conditionalRequire.js +52 -0
- package/examples/customMessage.js +31 -0
- package/examples/multipleWhen.js +26 -0
- package/examples/timestamps.js +22 -0
- package/generate-readme-toc.js +49 -0
- package/images/joi.png +0 -0
- package/images/validation.png +0 -0
- package/lib/any.js +74 -35
- package/lib/array.js +11 -44
- package/lib/boolean.js +9 -2
- package/lib/date.js +0 -3
- package/lib/errors.js +21 -22
- package/lib/index.js +142 -3
- package/lib/language.js +2 -6
- package/lib/object.js +36 -33
- package/lib/string.js +0 -17
- package/package.json +5 -5
- package/test/alternatives.js +490 -0
- package/test/any.js +1751 -0
- package/test/array.js +1049 -0
- package/test/binary.js +190 -0
- package/test/boolean.js +163 -0
- package/test/date.js +441 -0
- package/test/errors.js +512 -0
- package/test/function.js +224 -0
- package/test/helper.js +65 -0
- package/test/index.js +2450 -0
- package/test/number.js +836 -0
- package/test/object.js +1318 -0
- package/test/ref.js +470 -0
- package/test/string.js +3206 -0
- package/lib/lazy.js +0 -51
- package/lib/schemas.js +0 -25
|
@@ -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
|
+
});
|