From d0a307a28a224df96620f33f1de73c8d42ad8d5e Mon Sep 17 00:00:00 2001 From: Bryce Osterhaus Date: Wed, 3 May 2017 16:56:24 -0700 Subject: [PATCH 1/2] Failing test case for issue #227 --- packages/metal-state/test/Config.js | 80 ++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/packages/metal-state/test/Config.js b/packages/metal-state/test/Config.js index 70f042d5..3e78a621 100644 --- a/packages/metal-state/test/Config.js +++ b/packages/metal-state/test/Config.js @@ -80,7 +80,29 @@ describe('Config', function() { }, config.config); }); - it('should return config with specific validator from "validators"', function() { + it('should return config with "arrayOf" validator from "validators"', function() { + var config = Config.arrayOf(Config.number()); + assert.ok(core.isObject(config)); + assert.ok(core.isFunction(config.config.validator)); + assert.ok(config.config.validator([1, 2])); + assert.ok(config.config.validator([1, 'one']) instanceof Error); + assert.ok(config.config.validator(['one']) instanceof Error); + }); + + it('should return config with "instanceOf" validator from "validators"', function() { + class TestClass { + } + class TestClass2 { + } + + var config = Config.instanceOf(TestClass); + assert.ok(core.isObject(config)); + assert.ok(core.isFunction(config.config.validator)); + assert.ok(config.config.validator(new TestClass())); + assert.ok(config.config.validator(new TestClass2()) instanceof Error); + }); + + it('should return config with "number" validator from "validators"', function() { var config = Config.number(); assert.ok(core.isObject(config)); assert.ok(core.isFunction(config.config.validator)); @@ -88,6 +110,62 @@ describe('Config', function() { assert.ok(config.config.validator('test') instanceof Error); }); + it('should return config with "oneOf" validator from "validators"', function() { + var config = Config.oneOf([1, 'one']); + assert.ok(core.isObject(config)); + assert.ok(core.isFunction(config.config.validator)); + assert.ok(config.config.validator(1)); + assert.ok(config.config.validator('one')); + assert.ok(config.config.validator(2) instanceof Error); + assert.ok(config.config.validator(false) instanceof Error); + }); + + it('should return config with "shapeOf" validator from "validators"', function() { + var shape = { + one: Config.string(), + two: { + three: { + four: Config.number() + } + }, + five: Config.arrayOf(Config.string()) + }; + + var pass = { + one: 'one', + two: { + three: { + four: 4 + } + }, + five: ['five'] + }; + + var fail = { + one: 'one', + two: { + three: { + four: 'four' + } + }, + five: 5 + }; + + var config = Config.arrayOf(Config.shapeOf(shape)); + assert.ok(core.isObject(config)); + assert.ok(core.isFunction(config.config.validator)); + assert.ok(config.config.validator(pass)); + assert.ok(config.config.validator(fail) instanceof Error); + }); + + it('should return config with "string" validator from "validators"', function() { + var config = Config.string(); + assert.ok(core.isObject(config)); + assert.ok(core.isFunction(config.config.validator)); + assert.ok(config.config.validator('test')); + assert.ok(config.config.validator(10) instanceof Error); + }); + it('should return config with data from multiple calls', function() { var setter = () => { }; From 44eeda44c10df8ee6341b80d6277d324dcc2da5f Mon Sep 17 00:00:00 2001 From: Bryce Osterhaus Date: Thu, 4 May 2017 11:21:02 -0700 Subject: [PATCH 2/2] Fixes #227 We need to explicity handle use case for each validator since they all work differently. Validators that accept arguments all do something a little different. --- packages/metal-state/src/Config.js | 82 +++++++++++++++++++++++++++--- 1 file changed, 76 insertions(+), 6 deletions(-) diff --git a/packages/metal-state/src/Config.js b/packages/metal-state/src/Config.js index a86bd970..4c82addc 100644 --- a/packages/metal-state/src/Config.js +++ b/packages/metal-state/src/Config.js @@ -110,12 +110,82 @@ function mergeConfig(context, config) { return obj; } -// Add all validators to `Config`. -const fnNames = Object.keys(validators); -fnNames.forEach( - name => Config[name] = function() { +/** + * Adds primitive type validators to the config object. + * @param {string} name The name of the validator. + */ +function setPrimitiveValidators(name) { + Config[name] = function() { return this.validator(validators[name]); - } -); + }; +} + +setPrimitiveValidators('any'); +setPrimitiveValidators('array'); +setPrimitiveValidators('bool'); +setPrimitiveValidators('func'); +setPrimitiveValidators('number'); +setPrimitiveValidators('object'); +setPrimitiveValidators('string'); + +/** + * Calls validators with a single nested config. + * @param {string} name The name of the validator. + */ +function setNestedValidators(name) { + Config[name] = function(arg) { + return this.validator(validators[name](arg.config.validator)); + }; +} + +setNestedValidators('arrayOf'); +setNestedValidators('objectOf'); + +/** + * Calls validators with provided argument. + * @param {string} name The name of the validator. + */ +function setExplicitValueValidators(name) { + Config[name] = function(arg) { + return this.validator(validators[name](arg)); + }; +} + +setExplicitValueValidators('instanceOf'); +setExplicitValueValidators('oneOf'); + +Config.oneOfType = function(validatorArray) { + validatorArray = validatorArray.map( + item => item.config.validator + ); + + return this.validator(validators.oneOfType(validatorArray)); +}; + +/** + * Recursively sets validators for shapeOf. + * @param {!Object} shape The shape of specific types. + */ +function destructShapeOfConfigs(shape) { + const keys = Object.keys(shape); + + const retShape = {}; + + keys.forEach( + key => { + const value = shape[key]; + + retShape[key] = value.config && value.config.validator ? value.config.validator : destructShapeOfConfigs(value); + } + ); + + return retShape; +} + +Config.shapeOf = function(shapeObj) { + shapeObj = destructShapeOfConfigs(shapeObj); + + return this.validator(validators.shapeOf(shapeObj)); +}; export default Config;