From aa19c5274c68fef87cee76432704179e44351d00 Mon Sep 17 00:00:00 2001 From: Dmitry Maganov Date: Sun, 3 Feb 2019 11:18:58 +0300 Subject: [PATCH] feat: schemes as conditionals and shortcuts for when() --- src/Condition.js | 17 ++++++++++--- src/mixed.js | 8 ++++++ test/mixed.js | 64 +++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 84 insertions(+), 5 deletions(-) diff --git a/src/Condition.js b/src/Condition.js index 514e1f82b..bd2799191 100644 --- a/src/Condition.js +++ b/src/Condition.js @@ -7,6 +7,10 @@ function callOrConcat(schema) { return base => base.concat(schema); } +function makeIsFn(refs, predicate) { + return refs.length < 2 ? predicate : (...values) => values.every(predicate); +} + class Conditional { constructor(refs, options) { let { is, then, otherwise } = options; @@ -26,10 +30,15 @@ class Conditional { 'either `then:` or `otherwise:` is required for `when()` conditions', ); - let isFn = - typeof is === 'function' - ? is - : (...values) => values.every(value => value === is); + let isFn; + + if (typeof is === 'function') { + isFn = is; + } else if (isSchema(is)) { + isFn = makeIsFn(this.refs, value => is.isValidSync(value)); + } else { + isFn = makeIsFn(this.refs, value => value === is); + } this.fn = function(...values) { let currentSchema = values.pop(); diff --git a/src/mixed.js b/src/mixed.js index bc2e0775c..472620645 100644 --- a/src/mixed.js +++ b/src/mixed.js @@ -380,6 +380,14 @@ const proto = (SchemaType.prototype = { }, when(keys, options) { + if (arguments.length === 1) { + options = keys; + keys = '.'; + } else if (isSchema(keys) || typeof keys === 'function') { + options = { ...options, is: keys }; + keys = '.'; + } + var next = this.clone(), deps = [].concat(keys).map(key => new Ref(key)); diff --git a/test/mixed.js b/test/mixed.js index 153f79ed3..96bb7a76d 100644 --- a/test/mixed.js +++ b/test/mixed.js @@ -1,4 +1,15 @@ -import { array, mixed, string, number, object, ref, reach, bool } from '../src'; +import { + array, + mixed, + string, + number, + object, + ref, + reach, + bool, + ValidationError, +} from '../src'; + let noop = () => {}; function ensureSync(fn) { @@ -701,6 +712,57 @@ describe('Mixed Types ', () => { inst.default().should.eql({ prop: undefined }); }); + it('should support self references and scheme conditions', async function() { + let inst = mixed() + .strict() + .when('.', { + is: number(), + then: number().min(5), + otherwise: string(), + }); + + await inst + .validate(4) + .should.be.rejectedWith(ValidationError, /must be greater/); + + await inst.validate(5).should.be.fulfilled(); + + await inst + .validate([]) + .should.be.rejectedWith(ValidationError, /must be a `string` type/); + + await inst.validate('hello').should.be.fulfilled(); + }); + + it('should support conditional first argument as `is` shortcut', async function() { + let inst = mixed().when(value => typeof value === 'number', { + then: number().min(5), + }); + + await inst + .validate(4) + .should.be.rejectedWith(ValidationError, /must be greater/); + + await inst.validate(5).should.be.fulfilled(); + + await inst.validate('hello').should.be.fulfilled(); + }); + + it('should support conditional signle argument as options shortcut', async function() { + let inst = mixed().when({ + is: value => typeof value === 'number', + then: number().min(5), + }); + + await inst + .validate(4) + .should.be.rejectedWith(ValidationError, /must be greater/); + + await inst.validate(5).should.be.fulfilled(); + + await inst.validate('hello').should.be.fulfilled(); + }); + it('should use label in error message', async function() { let label = 'Label'; let inst = object({