Skip to content

Commit

Permalink
Cleanup for #1659.
Browse files Browse the repository at this point in the history
  • Loading branch information
Marsup committed Nov 24, 2018
1 parent 354f6fa commit 028d6fe
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 51 deletions.
26 changes: 13 additions & 13 deletions API.md
Expand Up @@ -57,7 +57,7 @@
- [`array.max(limit)`](#arraymaxlimit)
- [`array.length(limit)`](#arraylengthlimit)
- [`array.unique([comparator], [options])`](#arrayuniquecomparator-options)
- [`array.assertItem(schema)`](#arrayassertitemschema)
- [`array.has(schema)`](#arrayhasschema)
- [`boolean` - inherits from `Any`](#boolean---inherits-from-any)
- [`boolean.truthy(value)`](#booleantruthyvalue)
- [`boolean.falsy(value)`](#booleanfalsyvalue)
Expand Down Expand Up @@ -171,8 +171,8 @@
- [`array.ref`](#arrayref)
- [`array.sparse`](#arraysparse)
- [`array.unique`](#arrayunique)
- [`array.assertItemKnown`](#arrayassertitemknown)
- [`array.assertItemUnknown`](#arrayassertitemunknown)
- [`array.hasKnown`](#arrayhasknown)
- [`array.hasUnknown`](#arrayhasunknown)
- [`binary.base`](#binarybase)
- [`binary.length`](#binarylength)
- [`binary.max`](#binarymax)
Expand Down Expand Up @@ -1242,10 +1242,10 @@ schema.validate([{}, {}]);

💥 Possible validation errors:[`array.unique`](#arrayunique)

#### `array.assertItem(schema)`
#### `array.has(schema)`

Verifies that an assertion passes for at least one item in the array, where:
- `schema` - the validation rules required to satisfy the assertion. If the `schema` includes references, they are resolved against
Verifies that a schema validates at least one of the values in the array, where:
- `schema` - the validation rules required to satisfy the check. If the `schema` includes references, they are resolved against
the array item being tested, not the value of the `ref` target.

```js
Expand All @@ -1254,10 +1254,10 @@ const schema = Joi.array().items(
a: Joi.string(),
b: Joi.number()
})
).assertItem(Joi.object({ a: Joi.string().valid('a'), b: Joi.number() }))
).has(Joi.object({ a: Joi.string().valid('a'), b: Joi.number() }))
```

💥 Possible validation errors:[`array.assertItemKnown`](#arrayassertitemknown), [`array.assertitemUnknown`](#arrayassertitemunknown)
💥 Possible validation errors:[`array.hasKnown`](#arrayhasknown), [`array.hasUnknown`](#arrayhasunknown)


### `boolean` - inherits from `Any`
Expand Down Expand Up @@ -3029,26 +3029,26 @@ A duplicate value was found in an array.
}
```

#### `array.assertItemKnown`
#### `array.hasKnown`

**Description**

The schema on an [`array.assertItem()`](#arrayassertitem) failed to validate. This error happens when the schema is labelled.
The schema on an [`array.has()`](#arrayhas) was not found in the array. This error happens when the schema is labelled.

**Context**
```ts
{
key: string, // Last element of the path accessing the value, `undefined` if at the root
label: string, // Label if defined, otherwise it's the key
assertionLabel: string // Label of assertion schema
patternLabel: string // Label of assertion schema
}
```

#### `array.assertItemUnknown`
#### `array.hasUnknown`

**Description**

The schema on an [`array.assertItem()`](#arrayassertitem) failed to validate. This error happens when the schema is unlabelled.
The schema on an [`array.has()`](#arrayhas) was not found in the array. This error happens when the schema is unlabelled.

**Context**
```ts
Expand Down
4 changes: 2 additions & 2 deletions lib/language.js
Expand Up @@ -37,8 +37,8 @@ exports.errors = {
includesRequiredBoth: 'does not contain {{knownMisses}} and {{unknownMisses}} other required value(s)',
excludes: 'at position {{pos}} contains an excluded value',
excludesSingle: 'single value of "{{!label}}" contains an excluded value',
assertItemKnown: 'does not contain a match for type "{{!assertionLabel}}"',
assertItemUnknown: 'failed an assertion test',
hasKnown: 'does not contain at least one required match for type "{{!patternLabel}}"',
hasUnknown: 'does not contain at least one required match',
min: 'must contain at least {{limit}} items',
max: 'must contain less than or equal to {{limit}} items',
length: 'must contain {{limit}} items',
Expand Down
26 changes: 17 additions & 9 deletions lib/types/array/index.js
Expand Up @@ -332,6 +332,15 @@ internals.Array = class extends Any {
}
}

if (description.rules) {
for (let i = 0; i < description.rules.length; ++i) {
const rule = description.rules[i];
if (rule.name === 'has') {
rule.arg = rule.arg.describe();
}
}
}

return description;
}

Expand Down Expand Up @@ -483,38 +492,37 @@ internals.Array = class extends Any {
});
}

assertItem(schema) {
has(schema) {

try {
schema = Cast.schema(this._currentJoi, schema);
}
catch (castErr) {
if (castErr.hasOwnProperty('path')) {
castErr.message = castErr.message + '(' + castErr.path + ')';
castErr.message = `${castErr.message}(${castErr.path})`;
}

throw castErr;
}

return this._test('assertItem', schema, function (value, state, options) {
return this._test('has', schema, function (value, state, options) {

const isValid = value.some((item, idx) => {

const localState = new State(idx, [...state.path, idx], state.key, state.reference);
const result = schema._validate(item, localState, options);
return !result.errors;
return !schema._validate(item, localState, options).errors;
});

if (isValid) {
return value;
}

const assertionLabel = schema._getLabel();
if (assertionLabel) {
return this.createError('array.assertItemKnown', { assertionLabel }, state, options);
const patternLabel = schema._getLabel();
if (patternLabel) {
return this.createError('array.hasKnown', { patternLabel }, state, options);
}

return this.createError('array.assertItemUnknown', null, state, options);
return this.createError('array.hasUnknown', null, state, options);
});
}

Expand Down
67 changes: 40 additions & 27 deletions test/types/array.js
Expand Up @@ -762,13 +762,13 @@ describe('array', () => {
});
});

describe('assertItem()', () => {
describe('has()', () => {

it('shows path to errors in schema', () => {

expect(() => {

Joi.array().assertItem({
Joi.array().has({
a: {
b: {
c: {
Expand All @@ -784,7 +784,7 @@ describe('array', () => {

expect(() => {

Joi.array().assertItem(undefined);
Joi.array().has(undefined);
}).to.throw(Error, 'Invalid schema content: ');
});

Expand All @@ -800,7 +800,7 @@ describe('array', () => {
e: Joi.any()
}
})
).assertItem(Joi.object().assert('d.e', Joi.ref('a.c'), 'equal to a.c'));
).has(Joi.object().assert('d.e', Joi.ref('a.c'), 'equal to a.c'));

Helper.validate(schema, [
[[{ a: { b: 'x', c: 5 }, d: { e: 5 } }], true]
Expand All @@ -810,22 +810,22 @@ describe('array', () => {

it('does not throw if assertion passes', () => {

const schema = Joi.array().assertItem(Joi.string());
const schema = Joi.array().has(Joi.string());
Helper.validate(schema, [
[['foo'], true]
]);
});

it('throws with proper message if assertion fails on unknown schema', () => {

const schema = Joi.array().assertItem(Joi.string());
const schema = Joi.array().has(Joi.string());
Helper.validate(schema, [
[[0], false, null, {
message: '"value" failed an assertion test',
message: '"value" does not contain at least one required match',
details: [{
message: '"value" failed an assertion test',
message: '"value" does not contain at least one required match',
path: [],
type: 'array.assertItemUnknown',
type: 'array.hasUnknown',
context: { label: 'value', key: undefined }
}]
}]
Expand All @@ -834,15 +834,15 @@ describe('array', () => {

it('throws with proper message if assertion fails on known schema', () => {

const schema = Joi.array().assertItem(Joi.string().label('foo'));
const schema = Joi.array().has(Joi.string().label('foo'));
Helper.validate(schema, [
[[0], false, null, {
message: '"value" does not contain a match for type "foo"',
message: '"value" does not contain at least one required match for type "foo"',
details: [{
message: '"value" does not contain a match for type "foo"',
message: '"value" does not contain at least one required match for type "foo"',
path: [],
type: 'array.assertItemKnown',
context: { label: 'value', key: undefined, assertionLabel: 'foo' }
type: 'array.hasKnown',
context: { label: 'value', key: undefined, patternLabel: 'foo' }
}]
}]
]);
Expand All @@ -851,15 +851,15 @@ describe('array', () => {
it('shows correct path for error', () => {

const schema = Joi.object({
arr: Joi.array().assertItem(Joi.string())
arr: Joi.array().has(Joi.string())
});
Helper.validate(schema, [
[{ arr: [0] }, false, null, {
message: 'child "arr" fails because ["arr" failed an assertion test]',
message: 'child "arr" fails because ["arr" does not contain at least one required match]',
details: [{
message: '"arr" failed an assertion test',
message: '"arr" does not contain at least one required match',
path: ['arr'],
type: 'array.assertItemUnknown',
type: 'array.hasUnknown',
context: { label: 'arr', key: 'arr' }
}]
}]
Expand All @@ -870,7 +870,7 @@ describe('array', () => {

const schema = Joi.object({
arr: Joi.array().items(
Joi.object({ foo: Joi.array().assertItem(Joi.string()) })
Joi.object({ foo: Joi.array().has(Joi.string()) })
)
});
Helper.validate(schema, [
Expand All @@ -882,16 +882,16 @@ describe('array', () => {

const schema = Joi.object({
arr: Joi.array().items(
Joi.object({ foo: Joi.array().assertItem(Joi.string()) })
Joi.object({ foo: Joi.array().has(Joi.string()) })
)
});
Helper.validate(schema, [
[{ arr: [{ foo: [0] }] }, false, null, {
message: 'child "arr" fails because ["arr" at position 0 fails because [child "foo" fails because ["foo" failed an assertion test]]]',
message: 'child "arr" fails because ["arr" at position 0 fails because [child "foo" fails because ["foo" does not contain at least one required match]]]',
details: [{
message: '"foo" failed an assertion test',
message: '"foo" does not contain at least one required match',
path: ['arr', 0, 'foo'],
type: 'array.assertItemUnknown',
type: 'array.hasUnknown',
context: { label: 'foo', key: 'foo' }
}]
}]
Expand All @@ -900,23 +900,36 @@ describe('array', () => {

it('handles multiple assertions', () => {

const schema = Joi.array().assertItem(Joi.string()).assertItem(Joi.number());
const schema = Joi.array().has(Joi.string()).has(Joi.number());
Helper.validate(schema, [
[['foo', 0], true]
]);

Helper.validate(schema, [
[['foo'], false, null, {
message: '"value" failed an assertion test',
message: '"value" does not contain at least one required match',
details: [{
message: '"value" failed an assertion test',
message: '"value" does not contain at least one required match',
path: [],
type: 'array.assertItemUnknown',
type: 'array.hasUnknown',
context: { label: 'value', key: undefined }
}]
}]
]);
});

it('describes the pattern schema', () => {

const schema = Joi.array().has(Joi.string()).has(Joi.number());
expect(schema.describe()).to.equal({
type: 'array',
flags: { sparse: false },
rules: [
{ name: 'has', arg: { type: 'string', invalids: [''] } },
{ name: 'has', arg: { type: 'number', flags: { unsafe: false }, invalids: [Infinity, -Infinity] } }
]
});
});
});


Expand Down

0 comments on commit 028d6fe

Please sign in to comment.