From ae9b47bfa86ed7b2461a86a54b556ce2b6abce35 Mon Sep 17 00:00:00 2001 From: Joshua Wiens Date: Sat, 28 Jan 2017 22:45:50 -0600 Subject: [PATCH] feat(validations): add validateOptions module --- src/index.js | 3 + src/lib/validate-options.js | 17 ++++ src/lib/validate-options.test.js | 129 +++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+) create mode 100644 src/lib/validate-options.js create mode 100644 src/lib/validate-options.test.js diff --git a/src/index.js b/src/index.js index e69de29..d798f33 100644 --- a/src/index.js +++ b/src/index.js @@ -0,0 +1,3 @@ +import { validateOptions } from './lib/validate-options'; + +export default validateOptions; diff --git a/src/lib/validate-options.js b/src/lib/validate-options.js new file mode 100644 index 0000000..651ac0d --- /dev/null +++ b/src/lib/validate-options.js @@ -0,0 +1,17 @@ +import Ajv from 'ajv'; + +const validateOptions = (schema, data) => { + // This mutates the original data with defaults! + const ajv = new Ajv({ + useDefaults: true, + errorDataPath: 'property', + }); + const isValid = ajv.validate(schema, data); + + return { + isValid, + error: ajv.errors && ajv.errorsText(), + }; +}; + +export default validateOptions; diff --git a/src/lib/validate-options.test.js b/src/lib/validate-options.test.js new file mode 100644 index 0000000..39aecca --- /dev/null +++ b/src/lib/validate-options.test.js @@ -0,0 +1,129 @@ +import assert from 'assert'; +import validateOptions from './validate-options'; + +const generateProperties = (entry) => { + const ret = {}; + + Object.keys(entry).forEach((e) => { + ret[e] = { + type: ['array', 'string'], + items: { + type: 'string', + }, + }; + }); + + return ret; +}; + +const parsePaths = (entry) => { + const ret = { + type: ['array', 'object'], + }; + + if (entry instanceof Object) { + ret.additionalProperties = false; + ret.properties = generateProperties(entry); + } else { + ret.items = { + type: 'string', + }; + } + + return ret; +}; + +const testSchema = ({ entry } = {}) => { + return { + $schema: 'http://json-schema.org/draft-04/schema#', + additionalProperties: false, + type: 'object', + properties: { + styleExtensions: { + type: 'array', + items: { + type: 'string', + }, + default: ['.css'], + }, + minimize: { + type: 'boolean', + }, + moduleExtensions: { + type: 'array', + items: { + type: 'string', + }, + }, + paths: parsePaths(entry), + purifyOptions: { + type: 'object', + properties: {}, + }, + verbose: { + type: 'boolean', + }, + }, + required: [ + 'paths', + ], + }; +}; + +describe('Validate options', () => { + it('fails without a schema and data', () => { + assert.throws( + () => { + validateOptions(); + }, + Error, + ); + }); + + it('fails with empty data', () => { + const result = validateOptions(testSchema()); + + assert.ok(!result.isValid); + assert.ok(result.error); + }); + + it('does not fail if paths are provided', () => { + const result = validateOptions(testSchema(), { paths: ['./foo'] }); + + assert.ok(result.isValid); + assert.ok(!result.error); + }); + + it('does not allow arbitrary properties', () => { + const result = validateOptions(testSchema(), { paths: ['./foo'], foobar: ['./foo'] }); + + assert.ok(!result.isValid); + assert.ok(result.error); + }); + + it('styleExtensions have defaults', () => { + const paths = ['./foo']; + const data = { paths }; + + const result = validateOptions(testSchema(), data); + + assert.deepEqual(data, { paths, styleExtensions: ['.css'] }); + assert.ok(!result.error); + }); + + it('fails without matching path keys', () => { + const data = { + paths: { + a: './foo', + }, + }; + + const result = validateOptions(testSchema({ + entry: { + b: './bar', + }, + }), data); + + assert.ok(result.error); + }); +});