From 1b7a110c56a5e13a2134784bda2267d5f672a9f3 Mon Sep 17 00:00:00 2001 From: Hem Brahmbhatt Date: Wed, 30 Mar 2016 17:43:29 +0100 Subject: [PATCH 1/2] Support for built in formats in schema files --- lib/convict.js | 21 ++++++++++++++++----- test/cases/schema-built-in-formats.json | 22 ++++++++++++++++++++++ test/schema-tests.js | 10 +++++++--- 3 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 test/cases/schema-built-in-formats.json diff --git a/lib/convict.js b/lib/convict.js index 900433dc..c69e87eb 100644 --- a/lib/convict.js +++ b/lib/convict.js @@ -92,7 +92,17 @@ function contains (options, x) { JSON.stringify(options)); } -var BUILT_INS = [Object, Array, String, Number, Boolean]; +var BUILT_INS_BY_NAME = { + 'Object': Object, + 'Array': Array, + 'String': String, + 'Number': Number, + 'Boolean': Boolean +}; +var BUILT_IN_NAMES = Object.keys(BUILT_INS_BY_NAME); +var BUILT_INS = BUILT_IN_NAMES.map(function (name) { + return BUILT_INS_BY_NAME[name]; +}); function normalizeSchema (name, node, props, fullName, env, argv) { // If the current schema node is not a config property (has no "default"), recursively normalize it. @@ -137,15 +147,16 @@ function normalizeSchema (name, node, props, fullName, env, argv) { var format = o.format; var newFormat; - if (BUILT_INS.indexOf(format) >= 0) { + if (BUILT_INS.indexOf(format) >= 0 || BUILT_IN_NAMES.indexOf(format) >= 0) { // if the format property is a built-in JavaScript constructor, // assert that the value is of that type + var Format = typeof format === 'string' ? BUILT_INS_BY_NAME[format] : format; newFormat = function(x) { assert(Object.prototype.toString.call(x) == - Object.prototype.toString.call(new format()), - 'must be of type ' + format.name); + Object.prototype.toString.call(new Format()), + 'must be of type ' + Format.name); }; - o.format = format.name.toLowerCase(); + o.format = Format.name.toLowerCase(); } else if (typeof format === 'string') { // store declared type diff --git a/test/cases/schema-built-in-formats.json b/test/cases/schema-built-in-formats.json new file mode 100644 index 00000000..4afed2e7 --- /dev/null +++ b/test/cases/schema-built-in-formats.json @@ -0,0 +1,22 @@ +{ + "someNumber": { + "format": "Number", + "default": 1 + }, + "someObject": { + "format": "Object", + "default": {} + }, + "someArray": { + "format": "Array", + "default": [] + }, + "someBoolean": { + "format": "Boolean", + "default": true + }, + "someString": { + "format": "String", + "default": "foo" + } +} diff --git a/test/schema-tests.js b/test/schema-tests.js index 8fb35ed3..d30faf42 100644 --- a/test/schema-tests.js +++ b/test/schema-tests.js @@ -13,10 +13,14 @@ describe('convict schema file', function() { } }); - it('must parse a config specification from a file', function() { + beforeEach(function() { conf = convict(path.join(__dirname, 'schema.json')); }); + it('must parse a specification with built-in formats', function() { + conf = convict(path.join(__dirname, 'cases/schema-built-in-formats.json')); + }); + it('must be valid', function() { (function() { conf.validate(); }).must.not.throw(); }); @@ -172,8 +176,8 @@ describe('convict schema file', function() { describe('.default()', function() { // Temporarily modify a property while testing default() - before(function() { conf.set('foo.bar', 8); }); - after(function() { conf.set('foo.bar', 7); }); + beforeEach(function() { conf.set('foo.bar', 8); }); + afterEach(function() { conf.set('foo.bar', 7); }); it('must report the default value of a property', function() { conf.get('foo.bar').must.be(8); // Modified From f571669cc62e60a23ab3962612eeab587b78e787 Mon Sep 17 00:00:00 2001 From: Hem Brahmbhatt Date: Thu, 31 Mar 2016 11:24:46 +0100 Subject: [PATCH 2/2] Made schema file tests more robust --- test/schema-tests.js | 249 ++++++++++++++++++++++--------------------- 1 file changed, 128 insertions(+), 121 deletions(-) diff --git a/test/schema-tests.js b/test/schema-tests.js index d30faf42..653fdc78 100644 --- a/test/schema-tests.js +++ b/test/schema-tests.js @@ -13,7 +13,7 @@ describe('convict schema file', function() { } }); - beforeEach(function() { + it('must parse a config specification from a file', function() { conf = convict(path.join(__dirname, 'schema.json')); }); @@ -21,171 +21,178 @@ describe('convict schema file', function() { conf = convict(path.join(__dirname, 'cases/schema-built-in-formats.json')); }); - it('must be valid', function() { - (function() { conf.validate(); }).must.not.throw(); - }); + describe('after being parsed', function() { - it('must be valid again', function() { - (function() { conf2.validate(); }).must.not.throw(); - }); + beforeEach(function() { + conf = convict(path.join(__dirname, 'schema.json')); + }); + + it('must be valid', function() { + (function() { conf.validate(); }).must.not.throw(); + }); - it('must export all its properties as JSON', function() { - var res = conf.getProperties(); - res.must.eql({ - 'foo': { - 'bar': 7, - 'baz': { - 'bing': 'foo', - 'name with spaces': { - 'name_with_underscores': true + it('must be valid again', function() { + (function() { conf2.validate(); }).must.not.throw(); + }); + + it('must export all its properties as JSON', function() { + var res = conf.getProperties(); + res.must.eql({ + 'foo': { + 'bar': 7, + 'baz': { + 'bing': 'foo', + 'name with spaces': { + 'name_with_underscores': true + } } } - } + }); }); - }); - it('must export all its properties as JSON (deprecated method)', function() { - var res = conf.root(); - res.must.eql({ - 'foo': { - 'bar': 7, - 'baz': { - 'bing': 'foo', - 'name with spaces': { - 'name_with_underscores': true + it('must export all its properties as JSON (deprecated method)', function() { + var res = conf.root(); + res.must.eql({ + 'foo': { + 'bar': 7, + 'baz': { + 'bing': 'foo', + 'name with spaces': { + 'name_with_underscores': true + } } } - } + }); }); - }); - it('must export all its properties as a string', function() { - var res = conf.toString(); - res.must.eql(JSON.stringify({ - 'foo': { - 'bar': 7, - 'baz': { - 'bing': 'foo', - 'name with spaces': { - 'name_with_underscores': true + it('must export all its properties as a string', function() { + var res = conf.toString(); + res.must.eql(JSON.stringify({ + 'foo': { + 'bar': 7, + 'baz': { + 'bing': 'foo', + 'name with spaces': { + 'name_with_underscores': true + } } } - } - }, null, 2)); - }); + }, null, 2)); + }); - it('must export the schema as JSON', function() { - var res = conf.getSchema(); - res.must.eql({ - 'properties': { - 'foo': { - 'properties': { - 'bar': {}, - 'baz': { - 'properties': { - 'bing': {}, - 'name with spaces': { - 'properties': { - 'name_with_underscores': {} + it('must export the schema as JSON', function() { + var res = conf.getSchema(); + res.must.eql({ + 'properties': { + 'foo': { + 'properties': { + 'bar': {}, + 'baz': { + 'properties': { + 'bing': {}, + 'name with spaces': { + 'properties': { + 'name_with_underscores': {} + } } } } } } } - } + }); }); - }); - it('must export the schema as a JSON string', function() { - var res = conf.getSchemaString(); - res.must.eql(JSON.stringify({ - 'properties': { - 'foo': { - 'properties': { - 'bar': {}, - 'baz': { - 'properties': { - 'bing': {}, - 'name with spaces': { - 'properties': { - 'name_with_underscores': {} + it('must export the schema as a JSON string', function() { + var res = conf.getSchemaString(); + res.must.eql(JSON.stringify({ + 'properties': { + 'foo': { + 'properties': { + 'bar': {}, + 'baz': { + 'properties': { + 'bing': {}, + 'name with spaces': { + 'properties': { + 'name_with_underscores': {} + } } } } } } } - } - }, null, 2)); - }); + }, null, 2)); + }); - it('must export the schema as a JSON string (deprecated method)', function() { - var res = conf.toSchemaString(); - res.must.eql(JSON.stringify({ - 'properties': { - 'foo': { - 'properties': { - 'bar': {}, - 'baz': { - 'properties': { - 'bing': {}, - 'name with spaces': { - 'properties': { - 'name_with_underscores': {} + it('must export the schema as a JSON string (deprecated method)', function() { + var res = conf.toSchemaString(); + res.must.eql(JSON.stringify({ + 'properties': { + 'foo': { + 'properties': { + 'bar': {}, + 'baz': { + 'properties': { + 'bing': {}, + 'name with spaces': { + 'properties': { + 'name_with_underscores': {} + } } } } } } } - } - }, null, 2)); - }); - - describe('.has()', function() { - it('must not have undefined properties', function() { - var val = conf.has('foo.bar.madeup'); - val.must.be(false); + }, null, 2)); }); - it('must not have properties specified with a default of undefined', function() { - var val = conf2.has('foo.none'); - val.must.be(false); - }); - }); + describe('.has()', function() { + it('must not have undefined properties', function() { + var val = conf.has('foo.bar.madeup'); + val.must.be(false); + }); - describe('.get()', function() { - it('must find a nested value', function() { - var val = conf.get('foo.bar'); - val.must.be(7); + it('must not have properties specified with a default of undefined', function() { + var val = conf2.has('foo.none'); + val.must.be(false); + }); }); - it('must handle three levels of nesting', function() { - conf.get('foo.baz.bing').must.be('foo'); - }); + describe('.get()', function() { + it('must find a nested value', function() { + var val = conf.get('foo.bar'); + val.must.be(7); + }); - it('must handle names with spaces and underscores', function() { - conf.get('foo.baz.name with spaces.name_with_underscores').must.be(true); - }); + it('must handle three levels of nesting', function() { + conf.get('foo.baz.bing').must.be('foo'); + }); + + it('must handle names with spaces and underscores', function() { + conf.get('foo.baz.name with spaces.name_with_underscores').must.be(true); + }); - it('must throw if conf doesn\'t exist', function() { - (function() { conf.get('foo.no'); }).must.throw(); + it('must throw if conf doesn\'t exist', function() { + (function() { conf.get('foo.no'); }).must.throw(); + }); }); - }); - describe('.default()', function() { - // Temporarily modify a property while testing default() - beforeEach(function() { conf.set('foo.bar', 8); }); - afterEach(function() { conf.set('foo.bar', 7); }); + describe('.default()', function() { + // Temporarily modify a property while testing default() + beforeEach(function() { conf.set('foo.bar', 8); }); + afterEach(function() { conf.set('foo.bar', 7); }); - it('must report the default value of a property', function() { - conf.get('foo.bar').must.be(8); // Modified - conf.default('foo.bar').must.be(7); - }); + it('must report the default value of a property', function() { + conf.get('foo.bar').must.be(8); // Modified + conf.default('foo.bar').must.be(7); + }); - it('must throw if key doesn\'t exist', function() { - (function() { conf.default('foo.no'); }).must.throw(); + it('must throw if key doesn\'t exist', function() { + (function() { conf.default('foo.no'); }).must.throw(); + }); }); }); });