From e2ebb911d6ec6e547cbe1dcbab8b76b4a06fb239 Mon Sep 17 00:00:00 2001 From: parbez Date: Sat, 3 Dec 2022 19:12:30 +0530 Subject: [PATCH] fix: if this makes sense --- README.md | 8 ++--- src/constraints/ObjectConstrains.ts | 14 ++++---- tests/validators/object.test.ts | 51 ++++++++++++++++++++++++++++- 3 files changed, 59 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 94b6e218..f970cb15 100644 --- a/README.md +++ b/README.md @@ -809,12 +809,10 @@ provides the schema when `is` resolves truthy, and `otherwise` provides the sche ##### Available options for providing `is` When `is` is not provided (`=== undefined`) it is strictly resolved as `Boolean(value)` wherein `value` is the current -value of the referenced sibling. Note that if the refeferenced sibling is an array that this will mean the result is -always `true` because `Boolean([...])` is true. +value of the referenced sibling. Note that if multiple siblings are referenced then all the values of the array need to +resolve truthy for the `is` to resolve truthy. -When `is` is a primitive literal it is strictly compared (`===`) to the current value of the referenced sibling, or -through [`Array#some`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some) with -a strict negative comparison (`!==`) for each element if multiple siblings are referenced. +When `is` is a primitive literal it is strictly compared (`===`) to the current value. If you want to use a different form of equality you can provide a function like: `is: (value) => value === true`. diff --git a/src/constraints/ObjectConstrains.ts b/src/constraints/ObjectConstrains.ts index b2ec6c0b..8b5aabbd 100644 --- a/src/constraints/ObjectConstrains.ts +++ b/src/constraints/ObjectConstrains.ts @@ -25,9 +25,11 @@ export function whenConstraint, I, Key extends When return Result.err(new ExpectedConstraintError('s.object(T.when)', 'Validator has no parent', parent, 'Validator to have a parent')); } - const value = Array.isArray(key) ? key.map((k) => get(parent, k)) : get(parent, key); + const isKeyArray = Array.isArray(key); - const predicate = resolveBooleanIs(options, value) ? options.then : options.otherwise; + const value = isKeyArray ? key.map((k) => get(parent, k)) : get(parent, key); + + const predicate = resolveBooleanIs(options, value, isKeyArray) ? options.then : options.otherwise; if (predicate) { return predicate(validator).run(input) as Result>; @@ -38,18 +40,14 @@ export function whenConstraint, I, Key extends When }; } -function resolveBooleanIs, Key extends WhenKey>(options: WhenOptions, value: any) { +function resolveBooleanIs, Key extends WhenKey>(options: WhenOptions, value: any, isKeyArray: boolean) { if (options.is === undefined) { - return Boolean(value); + return isKeyArray ? !value.some((val: any) => !val) : Boolean(value); } if (typeof options.is === 'function') { return options.is(value); } - if (Array.isArray(value)) { - return !value.some((val) => val !== options.is); - } - return value === options.is; } diff --git a/tests/validators/object.test.ts b/tests/validators/object.test.ts index abe84ad7..71ad1a96 100644 --- a/tests/validators/object.test.ts +++ b/tests/validators/object.test.ts @@ -379,7 +379,7 @@ describe('ObjectValidator', () => { ); }); - test('Given a key WITH is boolean THEN return value based on the value at key position', () => { + test('Given a key WITH is primitive literal THEN return value based on the value strictly equal to the primitive literal', () => { const whenPredicate = s.object({ booleanLike: s.boolean, numberLike: s.number.when('booleanLike', { @@ -434,6 +434,55 @@ describe('ObjectValidator', () => { ); }); + test('Given an array of keys WITHOUT is THEN check truly of each values', () => { + const whenPredicate = s.object({ + booleanLike: s.boolean, + stringLike: s.string, + numberLike: s.number.when(['booleanLike', 'stringLike'], { + then: (schema) => schema.greaterThanOrEqual(5), + otherwise: (schema) => schema.lessThanOrEqual(5) + }) + }); + + expect(whenPredicate.parse({ booleanLike: true, stringLike: 'foo', numberLike: 6 })).toStrictEqual({ + booleanLike: true, + stringLike: 'foo', + numberLike: 6 + }); + expectError( + () => whenPredicate.parse({ booleanLike: true, stringLike: 'foo', numberLike: 4 }), + new CombinedPropertyError([ + ['numberLike', new ExpectedConstraintError('s.number.greaterThanOrEqual', 'Invalid number value', 4, 'expected >= 5')] + ]) + ); + + expect(whenPredicate.parse({ booleanLike: false, stringLike: 'foo', numberLike: 4 })).toStrictEqual({ + booleanLike: false, + stringLike: 'foo', + numberLike: 4 + }); + + expect(whenPredicate.parse({ booleanLike: true, stringLike: '', numberLike: 4 })).toStrictEqual({ + booleanLike: true, + stringLike: '', + numberLike: 4 + }); + + expectError( + () => whenPredicate.parse({ booleanLike: false, stringLike: 'foo', numberLike: 6 }), + new CombinedPropertyError([ + ['numberLike', new ExpectedConstraintError('s.number.lessThanOrEqual', 'Invalid number value', 6, 'expected <= 5')] + ]) + ); + + expectError( + () => whenPredicate.parse({ booleanLike: true, stringLike: '', numberLike: 6 }), + new CombinedPropertyError([ + ['numberLike', new ExpectedConstraintError('s.number.lessThanOrEqual', 'Invalid number value', 6, 'expected <= 5')] + ]) + ); + }); + test('Given a number key THEN return return value based on the key', () => { const whenPredicate = s.object({ 1: s.boolean,