joi 7.2.0 → 7.3.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/API.md +20 -5
- package/README.md +9 -1
- package/generate-readme-toc.js +22 -8
- package/lib/any.js +1 -8
- package/lib/index.js +25 -0
- package/lib/object.js +8 -3
- package/lib/string/rfc3986.js +12 -1
- package/lib/string/uri.js +9 -2
- package/lib/string.js +7 -2
- package/package.json +3 -2
- package/test/any.js +67 -22
- package/test/index.js +67 -0
- package/test/string.js +856 -761
package/API.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!-- version -->
|
|
2
|
-
# 7.
|
|
2
|
+
# 7.3.0 API Reference
|
|
3
3
|
<!-- versionstop -->
|
|
4
4
|
|
|
5
5
|
<img src="https://raw.github.com/hapijs/joi/master/images/validation.png" align="right" />
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
- [`assert(value, schema, [message])`](#assertvalue-schema-message)
|
|
13
13
|
- [`attempt(value, schema, [message])`](#attemptvalue-schema-message)
|
|
14
14
|
- [`isRef(ref)`](#isrefref)
|
|
15
|
+
- [`reach(schema, path)`](#reachschema-path)
|
|
15
16
|
- [`any`](#any)
|
|
16
17
|
- [`any.allow(value)`](#anyallowvalue)
|
|
17
18
|
- [`any.valid(value)` - aliases: `only`, `equal`](#anyvalidvalue---aliases-only-equal)
|
|
@@ -131,7 +132,11 @@ Validates a value using the given schema and options where:
|
|
|
131
132
|
- `allowUnknown` - when `true`, allows object to contain unknown keys which are ignored. Defaults to `false`.
|
|
132
133
|
- `skipFunctions` - when `true`, ignores unknown keys with a function value. Defaults to `false`.
|
|
133
134
|
- `stripUnknown` - when `true`, unknown keys are deleted (only when value is an object or an array). Defaults to `false`.
|
|
134
|
-
- `language` - overrides individual error messages
|
|
135
|
+
- `language` - overrides individual error messages. Defaults to no override (`{}`). Messages apply the following rules :
|
|
136
|
+
- variables are put between curly braces like `{{var}}`, if prefixed by a `!` like `{{!var}}`, it will be html escaped
|
|
137
|
+
- strings are always preceeded by the key name, unless a `{{key}}` is found elsewhere or if the string is prefixed by a `!!`
|
|
138
|
+
- when `'label'` is set, it overrides the key name in the error message
|
|
139
|
+
- to better understand the structure of the language, it's advised to have a look at the existing messages you want to override [here](lib/language.js)
|
|
135
140
|
- `presence` - sets the default presence requirements. Supported modes: `'optional'`, `'required'`, and `'forbidden'`.
|
|
136
141
|
Defaults to `'optional'`.
|
|
137
142
|
- `context` - provides an external data set to be used in [references](#refkey-options). Can only be set as an external option to
|
|
@@ -219,6 +224,15 @@ const ref = Joi.ref('a');
|
|
|
219
224
|
Joi.isRef(ref); // returns true
|
|
220
225
|
```
|
|
221
226
|
|
|
227
|
+
### `reach(schema, path)`
|
|
228
|
+
|
|
229
|
+
Get a sub-schema of an existing schema based on a path. Path separatar is a dot (`.`).
|
|
230
|
+
|
|
231
|
+
```js
|
|
232
|
+
const schema = Joi.object({ foo: Joi.object({ bar: Joi.number() }});
|
|
233
|
+
const number = Joi.reach(schema, 'foo.bar');
|
|
234
|
+
```
|
|
235
|
+
|
|
222
236
|
### `any`
|
|
223
237
|
|
|
224
238
|
Generates a schema object that matches any data type.
|
|
@@ -498,7 +512,7 @@ Considers anything that matches the schema to be empty (`undefined`).
|
|
|
498
512
|
- `schema` - any object or joi schema to match. An undefined schema unsets that rule.
|
|
499
513
|
|
|
500
514
|
```js
|
|
501
|
-
|
|
515
|
+
let schema = Joi.string().empty('');
|
|
502
516
|
schema.validate(''); // returns { error: null, value: undefined }
|
|
503
517
|
schema = schema.empty();
|
|
504
518
|
schema.validate(''); // returns { error: "value" is not allowed to be empty, value: '' }
|
|
@@ -520,7 +534,7 @@ array.validate(['a', 'b', 'a'], (err, value) => { });
|
|
|
520
534
|
Allow this array to be sparse. `enabled` can be used with a falsy value to go back to the default behavior.
|
|
521
535
|
|
|
522
536
|
```javascript
|
|
523
|
-
|
|
537
|
+
let schema = Joi.array().sparse(); // undefined values are now allowed
|
|
524
538
|
schema = schema.sparse(false); // undefined values are now denied
|
|
525
539
|
```
|
|
526
540
|
|
|
@@ -1260,7 +1274,7 @@ const schema = Joi.object({
|
|
|
1260
1274
|
|
|
1261
1275
|
Specifies the maximum number of string characters where:
|
|
1262
1276
|
- `limit` - the maximum number of string characters allowed.
|
|
1263
|
-
- `encoding` -
|
|
1277
|
+
- `encoding` - if specified, the string length is calculated in bytes using the provided encoding.
|
|
1264
1278
|
|
|
1265
1279
|
```javascript
|
|
1266
1280
|
const schema = Joi.string().max(10);
|
|
@@ -1384,6 +1398,7 @@ Requires the string value to be a valid [RFC 3986](http://tools.ietf.org/html/rf
|
|
|
1384
1398
|
|
|
1385
1399
|
- `options` - optional settings:
|
|
1386
1400
|
- `scheme` - Specifies one or more acceptable Schemes, should only include the scheme name. Can be an Array or String (strings are automatically escaped for use in a Regular Expression).
|
|
1401
|
+
- `allowRelative` - Allow relative URIs. Defaults to `false`.
|
|
1387
1402
|
|
|
1388
1403
|
```javascript
|
|
1389
1404
|
// Accept git or git http/https
|
package/README.md
CHANGED
|
@@ -6,11 +6,19 @@ Object schema description language and validator for JavaScript objects.
|
|
|
6
6
|
[](http://travis-ci.org/hapijs/joi)
|
|
7
7
|
[](https://david-dm.org/hapijs/joi)
|
|
8
8
|
[](https://david-dm.org/hapijs/joi#info=devDependencies)
|
|
9
|
+
[](https://snyk.io/test/npm/joi)
|
|
9
10
|
|
|
10
11
|
[](https://gitter.im/hapijs/joi?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
11
12
|
|
|
12
13
|
Lead Maintainer: [Nicolas Morel](https://github.com/marsup)
|
|
13
14
|
|
|
15
|
+
# Introduction
|
|
16
|
+
|
|
17
|
+
Imagine you run facebook and you want visitors to sign up on the website with real names and not something like `l337_p@nda` in the first name field. How would you define the limitations of what can be inputted and validate it against the set rules?
|
|
18
|
+
|
|
19
|
+
This is joi, joi allows you to create *blueprints* or *schemas* for JavaScript objects (an object that stores information) to ensure *validation* of key information.
|
|
20
|
+
|
|
21
|
+
|
|
14
22
|
# Example
|
|
15
23
|
|
|
16
24
|
```javascript
|
|
@@ -87,4 +95,4 @@ When validating a schema:
|
|
|
87
95
|
* Rules are defined in an additive fashion and evaluated in order after whitelist and blacklist checks.
|
|
88
96
|
|
|
89
97
|
# API
|
|
90
|
-
See the [API Reference](API.md).
|
|
98
|
+
See the [API Reference](https://github.com/hapijs/joi/blob/v7.3.0/API.md).
|
package/generate-readme-toc.js
CHANGED
|
@@ -9,13 +9,18 @@ const Package = require('./package.json');
|
|
|
9
9
|
// Declare internals
|
|
10
10
|
|
|
11
11
|
const internals = {
|
|
12
|
-
|
|
12
|
+
api: {
|
|
13
|
+
filename: './API.md',
|
|
14
|
+
contents: Fs.readFileSync('./API.md', 'utf8')
|
|
15
|
+
},
|
|
16
|
+
readme: {
|
|
17
|
+
filename: './README.md',
|
|
18
|
+
contents: Fs.readFileSync('./README.md', 'utf8')
|
|
19
|
+
}
|
|
13
20
|
};
|
|
14
21
|
|
|
22
|
+
internals.generateToc = function () {
|
|
15
23
|
|
|
16
|
-
internals.generate = function () {
|
|
17
|
-
|
|
18
|
-
const api = Fs.readFileSync(internals.filename, 'utf8');
|
|
19
24
|
const tocOptions = {
|
|
20
25
|
bullets: '-',
|
|
21
26
|
slugify: function (text) {
|
|
@@ -26,10 +31,19 @@ internals.generate = function () {
|
|
|
26
31
|
}
|
|
27
32
|
};
|
|
28
33
|
|
|
29
|
-
const
|
|
30
|
-
.replace(/<!-- version -->(.|\n)*<!-- versionstop -->/,
|
|
34
|
+
const api = Toc.insert(internals.api.contents, tocOptions)
|
|
35
|
+
.replace(/<!-- version -->(.|\n)*<!-- versionstop -->/, `<!-- version -->\n# ${Package.version} API Reference\n<!-- versionstop -->`);
|
|
36
|
+
|
|
37
|
+
Fs.writeFileSync(internals.api.filename, api);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
internals.generateLink = function () {
|
|
41
|
+
// create absolute URL for versioned docs
|
|
42
|
+
const readme = internals.readme.contents
|
|
43
|
+
.replace(/\[API Reference\]\(.*\)/gi, `[API Reference](${Package.homepage || ''}/blob/v${Package.version}/${internals.api.filename.substr(2)})`);
|
|
31
44
|
|
|
32
|
-
Fs.writeFileSync(internals.filename,
|
|
45
|
+
Fs.writeFileSync(internals.readme.filename, readme);
|
|
33
46
|
};
|
|
34
47
|
|
|
35
|
-
internals.
|
|
48
|
+
internals.generateToc();
|
|
49
|
+
internals.generateLink();
|
package/lib/any.js
CHANGED
|
@@ -219,8 +219,6 @@ internals.Any.prototype.concat = function (schema) {
|
|
|
219
219
|
|
|
220
220
|
internals.Any.prototype._test = function (name, arg, func) {
|
|
221
221
|
|
|
222
|
-
Hoek.assert(!this._flags.allowOnly, 'Cannot define rules when valid values specified');
|
|
223
|
-
|
|
224
222
|
const obj = this.clone();
|
|
225
223
|
obj._tests.push({ func, name, arg });
|
|
226
224
|
return obj;
|
|
@@ -279,8 +277,6 @@ internals.Any.prototype.allow = function () {
|
|
|
279
277
|
|
|
280
278
|
internals.Any.prototype.valid = internals.Any.prototype.only = internals.Any.prototype.equal = function () {
|
|
281
279
|
|
|
282
|
-
Hoek.assert(!this._tests.length, 'Cannot set valid values when rules specified');
|
|
283
|
-
|
|
284
280
|
const obj = this.allow.apply(this, arguments);
|
|
285
281
|
obj._flags.allowOnly = true;
|
|
286
282
|
return obj;
|
|
@@ -769,10 +765,7 @@ internals.Set = function () {
|
|
|
769
765
|
|
|
770
766
|
internals.Set.prototype.add = function (value, refs) {
|
|
771
767
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
if (typeof value !== 'function' &&
|
|
775
|
-
this.has(value, null, null, false)) {
|
|
768
|
+
if (!Ref.isRef(value) && this.has(value, null, null, false)) {
|
|
776
769
|
|
|
777
770
|
return;
|
|
778
771
|
}
|
package/lib/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// Load modules
|
|
4
4
|
|
|
5
|
+
const Hoek = require('hoek');
|
|
5
6
|
const Any = require('./any');
|
|
6
7
|
const Cast = require('./cast');
|
|
7
8
|
const Ref = require('./ref');
|
|
@@ -147,6 +148,30 @@ internals.root = function () {
|
|
|
147
148
|
return result.value;
|
|
148
149
|
};
|
|
149
150
|
|
|
151
|
+
root.reach = function (schema, path) {
|
|
152
|
+
|
|
153
|
+
Hoek.assert(schema && schema.isJoi, 'you must provide a joi schema');
|
|
154
|
+
Hoek.assert(typeof path === 'string', 'path must be a string');
|
|
155
|
+
|
|
156
|
+
if (path === '') {
|
|
157
|
+
return schema;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const parts = path.split('.');
|
|
161
|
+
const children = schema._inner.children;
|
|
162
|
+
if (!children) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const key = parts[0];
|
|
167
|
+
for (let i = 0; i < children.length; ++i) {
|
|
168
|
+
const child = children[i];
|
|
169
|
+
if (child.key === key) {
|
|
170
|
+
return this.reach(child.schema, path.substr(key.length + 1));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
150
175
|
return root;
|
|
151
176
|
};
|
|
152
177
|
|
package/lib/object.js
CHANGED
|
@@ -145,6 +145,11 @@ internals.Object.prototype._base = function (value, state, options) {
|
|
|
145
145
|
|
|
146
146
|
const unprocessed = Hoek.mapToObject(Object.keys(target));
|
|
147
147
|
|
|
148
|
+
// Children mustn't inherit the current label if it exists
|
|
149
|
+
const childOptions = options.language && options.language.label ?
|
|
150
|
+
Hoek.applyToDefaults(options, { language: { label: null } }, true) :
|
|
151
|
+
options;
|
|
152
|
+
|
|
148
153
|
if (this._inner.children) {
|
|
149
154
|
for (let i = 0; i < this._inner.children.length; ++i) {
|
|
150
155
|
const child = this._inner.children[i];
|
|
@@ -154,9 +159,9 @@ internals.Object.prototype._base = function (value, state, options) {
|
|
|
154
159
|
delete unprocessed[key];
|
|
155
160
|
|
|
156
161
|
const localState = { key, path: (state.path || '') + (state.path && key ? '.' : '') + key, parent: target, reference: state.reference };
|
|
157
|
-
const result = child.schema._validate(item, localState,
|
|
162
|
+
const result = child.schema._validate(item, localState, childOptions);
|
|
158
163
|
if (result.errors) {
|
|
159
|
-
errors.push(this.createError('object.child', { key, reason: result.errors }, localState,
|
|
164
|
+
errors.push(this.createError('object.child', { key, reason: result.errors }, localState, childOptions));
|
|
160
165
|
|
|
161
166
|
if (options.abortEarly) {
|
|
162
167
|
return finish();
|
|
@@ -231,7 +236,7 @@ internals.Object.prototype._base = function (value, state, options) {
|
|
|
231
236
|
(this._flags.allowUnknown !== undefined ? !this._flags.allowUnknown : !options.allowUnknown)) {
|
|
232
237
|
|
|
233
238
|
for (let i = 0; i < unprocessedKeys.length; ++i) {
|
|
234
|
-
errors.push(this.createError('object.allowUnknown', null, { key: unprocessedKeys[i], path: state.path + (state.path ? '.' : '') + unprocessedKeys[i] },
|
|
239
|
+
errors.push(this.createError('object.allowUnknown', null, { key: unprocessedKeys[i], path: state.path + (state.path ? '.' : '') + unprocessedKeys[i] }, childOptions));
|
|
235
240
|
}
|
|
236
241
|
}
|
|
237
242
|
}
|
package/lib/string/rfc3986.js
CHANGED
|
@@ -162,14 +162,25 @@ internals.generate = function () {
|
|
|
162
162
|
*/
|
|
163
163
|
const segment = pcharOnly + '*';
|
|
164
164
|
const segmentNz = pcharOnly + '+';
|
|
165
|
+
const segmentNzNc = '[' + unreserved + pctEncoded + subDelims + '@' + ']+';
|
|
166
|
+
const pathEmpty = '';
|
|
165
167
|
const pathAbEmpty = '(?:\\/' + segment + ')*';
|
|
166
168
|
const pathAbsolute = '\\/(?:' + segmentNz + pathAbEmpty + ')?';
|
|
167
169
|
const pathRootless = segmentNz + pathAbEmpty;
|
|
170
|
+
const pathNoScheme = segmentNzNc + pathAbEmpty;
|
|
168
171
|
|
|
169
172
|
/**
|
|
170
173
|
* hier-part = "//" authority path
|
|
171
174
|
*/
|
|
172
|
-
internals.rfc3986.hierPart = '(?:\\/\\/' + authority + pathAbEmpty + or + pathAbsolute + or + pathRootless + ')';
|
|
175
|
+
internals.rfc3986.hierPart = '(?:' + '(?:\\/\\/' + authority + pathAbEmpty + ')' + or + pathAbsolute + or + pathRootless + ')';
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* relative-part = "//" authority path-abempty
|
|
179
|
+
* / path-absolute
|
|
180
|
+
* / path-noscheme
|
|
181
|
+
* / path-empty
|
|
182
|
+
*/
|
|
183
|
+
internals.rfc3986.relativeRef = '(?:' + '(?:\\/\\/' + authority + pathAbEmpty + ')' + or + pathAbsolute + or + pathNoScheme + or + pathEmpty + ')';
|
|
173
184
|
|
|
174
185
|
/**
|
|
175
186
|
* query = *( pchar / "/" / "?" )
|
package/lib/string/uri.js
CHANGED
|
@@ -9,7 +9,7 @@ const RFC3986 = require('./rfc3986');
|
|
|
9
9
|
|
|
10
10
|
const internals = {
|
|
11
11
|
Uri: {
|
|
12
|
-
createUriRegex: function (optionalScheme) {
|
|
12
|
+
createUriRegex: function (optionalScheme, allowRelative) {
|
|
13
13
|
|
|
14
14
|
let scheme = RFC3986.scheme;
|
|
15
15
|
|
|
@@ -20,10 +20,17 @@ const internals = {
|
|
|
20
20
|
scheme = '(?:' + optionalScheme + ')';
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
const withScheme = '(?:' + scheme + ':' + RFC3986.hierPart + ')';
|
|
24
|
+
const prefix = allowRelative ? '(?:' + withScheme + '|' + RFC3986.relativeRef + ')' : withScheme;
|
|
25
|
+
|
|
23
26
|
/**
|
|
24
27
|
* URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
|
|
28
|
+
*
|
|
29
|
+
* OR
|
|
30
|
+
*
|
|
31
|
+
* relative-ref = relative-part [ "?" query ] [ "#" fragment ]
|
|
25
32
|
*/
|
|
26
|
-
return new RegExp('^' +
|
|
33
|
+
return new RegExp('^' + prefix + '(?:\\?' + RFC3986.query + ')?' + '(?:#' + RFC3986.fragment + ')?$');
|
|
27
34
|
}
|
|
28
35
|
}
|
|
29
36
|
};
|
package/lib/string.js
CHANGED
|
@@ -273,6 +273,7 @@ internals.String.prototype.ip = function (ipOptions) {
|
|
|
273
273
|
internals.String.prototype.uri = function (uriOptions) {
|
|
274
274
|
|
|
275
275
|
let customScheme = '';
|
|
276
|
+
let allowRelative = false;
|
|
276
277
|
let regex = internals.uriRegex;
|
|
277
278
|
|
|
278
279
|
if (uriOptions) {
|
|
@@ -305,10 +306,14 @@ internals.String.prototype.uri = function (uriOptions) {
|
|
|
305
306
|
}
|
|
306
307
|
}
|
|
307
308
|
}
|
|
309
|
+
|
|
310
|
+
if (uriOptions.allowRelative) {
|
|
311
|
+
allowRelative = true;
|
|
312
|
+
}
|
|
308
313
|
}
|
|
309
314
|
|
|
310
|
-
if (customScheme) {
|
|
311
|
-
regex = Uri.createUriRegex(customScheme);
|
|
315
|
+
if (customScheme || allowRelative) {
|
|
316
|
+
regex = Uri.createUriRegex(customScheme, allowRelative);
|
|
312
317
|
}
|
|
313
318
|
|
|
314
319
|
return this._test('uri', uriOptions, (value, state, options) => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "joi",
|
|
3
3
|
"description": "Object schema validation",
|
|
4
|
-
"version": "7.
|
|
4
|
+
"version": "7.3.0",
|
|
5
|
+
"homepage": "https://github.com/hapijs/joi",
|
|
5
6
|
"repository": "git://github.com/hapijs/joi",
|
|
6
7
|
"main": "lib/index.js",
|
|
7
8
|
"keywords": [
|
|
@@ -27,7 +28,7 @@
|
|
|
27
28
|
"test": "lab -t 100 -a code -L",
|
|
28
29
|
"test-cov-html": "lab -r html -o coverage.html -a code",
|
|
29
30
|
"toc": "node generate-readme-toc.js",
|
|
30
|
-
"version": "npm run toc && git add API.md"
|
|
31
|
+
"version": "npm run toc && git add API.md README.md"
|
|
31
32
|
},
|
|
32
33
|
"license": "BSD-3-Clause"
|
|
33
34
|
}
|
package/test/any.js
CHANGED
|
@@ -171,6 +171,39 @@ describe('any', () => {
|
|
|
171
171
|
expect(schema).to.deep.equal({ type: 'object', label: 'lbl' });
|
|
172
172
|
done();
|
|
173
173
|
});
|
|
174
|
+
|
|
175
|
+
it('does not leak into sub objects', (done) => {
|
|
176
|
+
|
|
177
|
+
const schema = Joi.object({ a: Joi.number() }).label('foo');
|
|
178
|
+
schema.validate({ a: 'a' }, (err, value) => {
|
|
179
|
+
|
|
180
|
+
expect(err).to.exist();
|
|
181
|
+
expect(err.message).to.equal('child "a" fails because ["a" must be a number]');
|
|
182
|
+
done();
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('does not leak into sub objects from an array', (done) => {
|
|
187
|
+
|
|
188
|
+
const schema = Joi.array().items(Joi.object({ a: Joi.number() }).label('foo')).label('bar');
|
|
189
|
+
schema.validate([{ a: 'a' }], (err, value) => {
|
|
190
|
+
|
|
191
|
+
expect(err).to.exist();
|
|
192
|
+
expect(err.message).to.equal('"bar" at position 0 fails because [child "a" fails because ["a" must be a number]]');
|
|
193
|
+
done();
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('does not leak into unknown keys', (done) => {
|
|
198
|
+
|
|
199
|
+
const schema = Joi.object({ a: Joi.number() }).label('foo');
|
|
200
|
+
schema.validate({ b: 'a' }, (err, value) => {
|
|
201
|
+
|
|
202
|
+
expect(err).to.exist();
|
|
203
|
+
expect(err.message).to.equal('"b" is not allowed');
|
|
204
|
+
done();
|
|
205
|
+
});
|
|
206
|
+
});
|
|
174
207
|
});
|
|
175
208
|
|
|
176
209
|
describe('strict()', () => {
|
|
@@ -1470,27 +1503,6 @@ describe('any', () => {
|
|
|
1470
1503
|
|
|
1471
1504
|
describe('Set', () => {
|
|
1472
1505
|
|
|
1473
|
-
describe('add()', () => {
|
|
1474
|
-
|
|
1475
|
-
it('throws when adding a non ref function', (done) => {
|
|
1476
|
-
|
|
1477
|
-
expect(() => {
|
|
1478
|
-
|
|
1479
|
-
Joi.valid(() => { });
|
|
1480
|
-
}).to.throw('Value cannot be an object or function');
|
|
1481
|
-
done();
|
|
1482
|
-
});
|
|
1483
|
-
|
|
1484
|
-
it('throws when adding an object function', (done) => {
|
|
1485
|
-
|
|
1486
|
-
expect(() => {
|
|
1487
|
-
|
|
1488
|
-
Joi.valid({});
|
|
1489
|
-
}).to.throw('Value cannot be an object or function');
|
|
1490
|
-
done();
|
|
1491
|
-
});
|
|
1492
|
-
});
|
|
1493
|
-
|
|
1494
1506
|
describe('has()', () => {
|
|
1495
1507
|
|
|
1496
1508
|
it('compares date to null', (done) => {
|
|
@@ -1508,6 +1520,7 @@ describe('any', () => {
|
|
|
1508
1520
|
expect(any._valids.has(new Buffer(''))).to.equal(false);
|
|
1509
1521
|
done();
|
|
1510
1522
|
});
|
|
1523
|
+
|
|
1511
1524
|
});
|
|
1512
1525
|
|
|
1513
1526
|
describe('values()', () => {
|
|
@@ -1559,7 +1572,7 @@ describe('any', () => {
|
|
|
1559
1572
|
|
|
1560
1573
|
expect(() => {
|
|
1561
1574
|
|
|
1562
|
-
Joi.any().valid(true, 1, 'hello', new Date());
|
|
1575
|
+
Joi.any().valid(true, 1, 'hello', new Date(), Symbol('foo'), () => {}, {});
|
|
1563
1576
|
}).not.to.throw();
|
|
1564
1577
|
done();
|
|
1565
1578
|
});
|
|
@@ -1572,6 +1585,38 @@ describe('any', () => {
|
|
|
1572
1585
|
}).to.throw(Error, 'Cannot call allow/valid/invalid with undefined');
|
|
1573
1586
|
done();
|
|
1574
1587
|
});
|
|
1588
|
+
|
|
1589
|
+
it('validates differents types of values', (done) => {
|
|
1590
|
+
|
|
1591
|
+
expect(Joi.valid(1).validate(1).error).to.be.null();
|
|
1592
|
+
expect(Joi.valid(1).validate(2).error).to.exist();
|
|
1593
|
+
|
|
1594
|
+
const d = new Date();
|
|
1595
|
+
expect(Joi.valid(d).validate(new Date(d.getTime())).error).to.be.null();
|
|
1596
|
+
expect(Joi.valid(d).validate(new Date(d.getTime() + 1)).error).to.exist();
|
|
1597
|
+
|
|
1598
|
+
const str = 'foo';
|
|
1599
|
+
expect(Joi.valid(str).validate(str).error).to.be.null();
|
|
1600
|
+
expect(Joi.valid(str).validate('foobar').error).to.exist();
|
|
1601
|
+
|
|
1602
|
+
const s = Symbol('foo');
|
|
1603
|
+
expect(Joi.valid(s).validate(s).error).to.be.null();
|
|
1604
|
+
expect(Joi.valid(s).validate(Symbol('foo')).error).to.exist();
|
|
1605
|
+
|
|
1606
|
+
const o = {};
|
|
1607
|
+
expect(Joi.valid(o).validate(o).error).to.be.null();
|
|
1608
|
+
expect(Joi.valid(o).validate({}).error).to.exist();
|
|
1609
|
+
|
|
1610
|
+
const f = () => {};
|
|
1611
|
+
expect(Joi.valid(f).validate(f).error).to.be.null();
|
|
1612
|
+
expect(Joi.valid(f).validate(() => {}).error).to.exist();
|
|
1613
|
+
|
|
1614
|
+
const b = new Buffer('foo');
|
|
1615
|
+
expect(Joi.valid(b).validate(b).error).to.be.null();
|
|
1616
|
+
expect(Joi.valid(b).validate(new Buffer('foobar')).error).to.exist();
|
|
1617
|
+
|
|
1618
|
+
done();
|
|
1619
|
+
});
|
|
1575
1620
|
});
|
|
1576
1621
|
|
|
1577
1622
|
describe('invalid()', () => {
|
package/test/index.js
CHANGED
|
@@ -1691,4 +1691,71 @@ describe('Joi', () => {
|
|
|
1691
1691
|
done();
|
|
1692
1692
|
});
|
|
1693
1693
|
});
|
|
1694
|
+
|
|
1695
|
+
describe('reach()', () => {
|
|
1696
|
+
|
|
1697
|
+
it('should fail without any parameter', (done) => {
|
|
1698
|
+
|
|
1699
|
+
expect(() => Joi.reach()).to.throw('you must provide a joi schema');
|
|
1700
|
+
done();
|
|
1701
|
+
});
|
|
1702
|
+
|
|
1703
|
+
it('should fail when schema is not a joi object', (done) => {
|
|
1704
|
+
|
|
1705
|
+
expect(() => Joi.reach({ foo: 'bar' }, 'foo')).to.throw('you must provide a joi schema');
|
|
1706
|
+
done();
|
|
1707
|
+
});
|
|
1708
|
+
|
|
1709
|
+
it('should fail without a proper path', (done) => {
|
|
1710
|
+
|
|
1711
|
+
const schema = Joi.object();
|
|
1712
|
+
expect(() => Joi.reach(schema)).to.throw('path must be a string');
|
|
1713
|
+
expect(() => Joi.reach(schema, true)).to.throw('path must be a string');
|
|
1714
|
+
done();
|
|
1715
|
+
});
|
|
1716
|
+
|
|
1717
|
+
it('should return undefined when no keys are defined', (done) => {
|
|
1718
|
+
|
|
1719
|
+
const schema = Joi.object();
|
|
1720
|
+
expect(Joi.reach(schema, 'a')).to.be.undefined();
|
|
1721
|
+
done();
|
|
1722
|
+
});
|
|
1723
|
+
|
|
1724
|
+
it('should return undefined when key is not found', (done) => {
|
|
1725
|
+
|
|
1726
|
+
const schema = Joi.object().keys({ a: Joi.number() });
|
|
1727
|
+
expect(Joi.reach(schema, 'foo')).to.be.undefined();
|
|
1728
|
+
done();
|
|
1729
|
+
});
|
|
1730
|
+
|
|
1731
|
+
it('should return a schema when key is found', (done) => {
|
|
1732
|
+
|
|
1733
|
+
const a = Joi.number();
|
|
1734
|
+
const schema = Joi.object().keys({ a });
|
|
1735
|
+
expect(Joi.reach(schema, 'a')).to.equal(a);
|
|
1736
|
+
done();
|
|
1737
|
+
});
|
|
1738
|
+
|
|
1739
|
+
it('should return undefined on a schema that does not support reach', (done) => {
|
|
1740
|
+
|
|
1741
|
+
const schema = Joi.number();
|
|
1742
|
+
expect(Joi.reach(schema, 'a')).to.be.undefined();
|
|
1743
|
+
done();
|
|
1744
|
+
});
|
|
1745
|
+
|
|
1746
|
+
it('should return a schema when deep key is found', (done) => {
|
|
1747
|
+
|
|
1748
|
+
const bar = Joi.number();
|
|
1749
|
+
const schema = Joi.object({ foo: Joi.object({ bar }) });
|
|
1750
|
+
expect(Joi.reach(schema, 'foo.bar')).to.equal(bar);
|
|
1751
|
+
done();
|
|
1752
|
+
});
|
|
1753
|
+
|
|
1754
|
+
it('should return undefined when deep key is not found', (done) => {
|
|
1755
|
+
|
|
1756
|
+
const schema = Joi.object({ foo: Joi.object({ bar: Joi.number() }) });
|
|
1757
|
+
expect(Joi.reach(schema, 'foo.baz')).to.be.undefined();
|
|
1758
|
+
done();
|
|
1759
|
+
});
|
|
1760
|
+
});
|
|
1694
1761
|
});
|