diff --git a/src/mixed.js b/src/mixed.js index 2efacba5e..2906a636e 100644 --- a/src/mixed.js +++ b/src/mixed.js @@ -38,10 +38,20 @@ SchemaType.prototype = { constructor: SchemaType, - clone(){ + clone() { + if (this._mutate) + return this; + return cloneDeep(this); }, + withMutation(fn) { + this._mutate = true + let result = fn(this) + this._mutate = false + return result + }, + concat(schema){ if (!schema) return this @@ -79,26 +89,28 @@ SchemaType.prototype = { : this.transforms.reduce( (value, transform) => transform.call(this, value, _value), _value) - - if( value === undefined && _.has(this, '_default') ) + if (value === undefined && _.has(this, '_default')) value = this.default() return value }, _resolve(context, parent){ - var schema = this; + if (this._deps.length) { + return this._deps.reduce((schema, match) => + match.resolve(schema, match.getValue(parent, context)), this) + } - return this._deps.reduce((schema, match) => - match.resolve(schema, match.getValue(parent, context)), schema) + return this }, //-- tests - _validate(value, options = {}, state = {}) { + _validate(_value, options = {}, state = {}) { let valids = this._whitelist , invalids = this._blacklist , context = options.context , parent = state.parent + , value = _value , schema, endEarly, isStrict; schema = this._resolve(context, parent) @@ -110,29 +122,29 @@ SchemaType.prototype = { let errors = []; let reject = () => Promise.reject(new ValidationError(errors, value)); - if ( !state.isCast && !isStrict ) + if (!state.isCast && !isStrict) value = schema._cast(value, options) // value is cast, we can check if it meets type requirements - if ( value !== undefined && !schema.isType(value) ){ + if (value !== undefined && !schema.isType(value)) { errors.push(schema._typeError({ value, path, type: schema._type })) if ( endEarly ) return reject() } // next check Whitelist for matching values - if ( valids.length && !valids.has(value) ) { + if (valids.length && !valids.has(value)) { errors.push(schema._whitelistError(valids.values(), path)) - if ( endEarly ) return reject() + if (endEarly) return reject() } // next check Blacklist for matching values - if ( invalids.has(value) ){ + if (invalids.has(value)) { errors.push(schema._blacklistError(invalids.values(), path)) - if ( endEarly ) return reject() + if (endEarly) return reject() } // It makes no sense to validate further at this point if their are errors - if ( errors.length ) + if (errors.length) return reject() let result = schema.tests.map(fn => fn({ value, path, state, schema, options })) diff --git a/test/mixed.js b/test/mixed.js index 98ed2afc8..63906c3dd 100644 --- a/test/mixed.js +++ b/test/mixed.js @@ -16,9 +16,13 @@ describe( 'Mixed Types ', function(){ it('should be immutable', function(){ var inst = mixed(), next; + var sub = inst.sub = mixed() inst.should.not.equal(next = inst.required()) + next.sub.should.equal(sub) + inst.sub.should.equal(next.sub) + inst.should.be.an.instanceOf(mixed) next.should.be.an.instanceOf(mixed) diff --git a/test/object.js b/test/object.js index cda828b45..a834762e4 100644 --- a/test/object.js +++ b/test/object.js @@ -107,6 +107,41 @@ describe('Object types', function(){ }) }) + it('should not clone during validating', function(){ + var inst = object().shape({ + num: number().max(4), + str: string(), + arr: array().of(number().max(6)), + dte: date(), + + nested: object() + .shape({ str: string().min(3) }) + .required(), + + arrNested: array().of( + object().shape({ num: number() }) + ) + }) + + let base = mixed.prototype.clone; + let replace = () => mixed.prototype.clone = base + mixed.prototype.clone = function(...args) { + if (!this._mutate) + throw new Error('should not call clone') + + return base.apply(this, args) + } + + return inst + .validate({ + nested: { str: 5 }, + arrNested: [{ num: 5 }, { num: '2' }] + }) + .then(replace) + .catch(replace) + }) + + it('should call shape with constructed with an arg', function(){ var inst = object({ prop: mixed()