Skip to content

Commit 9c5ef5e

Browse files
michael-ciniawskyjoshwiens
authored andcommitted
fix(validateOptions): catch ValidationError and handle it internally (#15)
1 parent 8e242c8 commit 9c5ef5e

File tree

6 files changed

+85
-20
lines changed

6 files changed

+85
-20
lines changed

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
"lint": "eslint --cache src test",
1818
"lint-staged": "lint-staged",
1919
"security": "nsp check",
20-
"test": "jest",
21-
"test:watch": "jest --watch",
22-
"test:coverage": "jest --collectCoverageFrom='src/**/*.js' --coverage",
20+
"test": "cross-env JEST=true jest",
21+
"test:watch": "cross-env JEST=true jest --watch",
22+
"test:coverage": "cross-env JEST=true jest --collectCoverageFrom='src/**/*.js' --coverage",
2323
"travis:lint": "npm run lint && npm run security",
2424
"travis:test": "npm run test -- --runInBand",
2525
"travis:coverage": "npm run test:coverage -- --runInBand",
@@ -30,7 +30,8 @@
3030
},
3131
"dependencies": {
3232
"ajv": "^5.0.0",
33-
"ajv-keywords": "^2.1.0"
33+
"ajv-keywords": "^2.1.0",
34+
"chalk": "^2.3.0"
3435
},
3536
"devDependencies": {
3637
"babel-cli": "^6.0.0",

src/ValidationError.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ class ValidationError extends Error {
44

55
this.name = 'ValidationError';
66

7-
this.message = `Validation Error\n\n${name || ''} Invalid Options\n\n`;
7+
this.message = `${name || ''} Invalid Options\n\n`;
88

99
errors.forEach((err) => {
1010
this.message += `options${err.dataPath} ${err.message}\n`;

src/validateOptions.js

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
11
/* eslint-disable
22
import/order,
3-
no-param-reassign
3+
no-param-reassign,
4+
array-bracket-spacing,
45
*/
56
import fs from 'fs';
67
import path from 'path';
78

9+
import chalk from 'chalk';
10+
811
import Ajv from 'ajv';
912
import ajvKeywords from 'ajv-keywords';
13+
1014
import ValidationError from './ValidationError';
1115

1216
const ajv = new Ajv({
13-
useDefaults: true,
1417
allErrors: true,
18+
useDefaults: true,
1519
errorDataPath: 'property',
1620
});
1721

18-
ajvKeywords(ajv, ['instanceof', 'typeof']);
22+
ajvKeywords(ajv, [ 'instanceof', 'typeof' ]);
1923

2024
const validateOptions = (schema, options, name) => {
2125
if (typeof schema === 'string') {
@@ -24,7 +28,18 @@ const validateOptions = (schema, options, name) => {
2428
}
2529

2630
if (!ajv.validate(schema, options)) {
27-
throw new ValidationError(ajv.errors, name);
31+
try {
32+
throw new ValidationError(ajv.errors, name);
33+
} catch (err) {
34+
console.error(chalk.bold.red(`\n${err.message}\n`));
35+
36+
// rethrow {Error} for testing only
37+
if (process.env.JEST) {
38+
throw err;
39+
}
40+
41+
process.exit(1);
42+
}
2843
}
2944

3045
return true;

test/__snapshots__/index.test.js.snap

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`Error should have errors for every key in options 1`] = `
3+
exports[`Error should have errors for every option key 1`] = `
44
Array [
55
Object {
66
"dataPath": ".string",
@@ -29,6 +29,15 @@ Array [
2929
},
3030
"schemaPath": "#/properties/object/properties/prop/type",
3131
},
32+
Object {
33+
"dataPath": ".object.object.prop",
34+
"keyword": "type",
35+
"message": "should be boolean",
36+
"params": Object {
37+
"type": "boolean",
38+
},
39+
"schemaPath": "#/properties/object/properties/object/properties/prop/type",
40+
},
3241
Object {
3342
"dataPath": ".boolean",
3443
"keyword": "type",
@@ -58,3 +67,16 @@ Array [
5867
},
5968
]
6069
`;
70+
71+
exports[`Error should throw error 1`] = `
72+
"{Name} Invalid Options
73+
74+
options.string should be string
75+
options.array should be array
76+
options.object.prop should be boolean
77+
options.object.object.prop should be boolean
78+
options.boolean should be boolean
79+
options.type should pass \\"typeof\\" keyword validation
80+
options.instance should pass \\"instanceof\\" keyword validation
81+
"
82+
`;

test/fixtures/schema.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@
1515
"properties": {
1616
"prop": {
1717
"type": "boolean"
18+
},
19+
"object": {
20+
"type": "object",
21+
"properties": {
22+
"prop": {
23+
"type": "boolean"
24+
}
25+
}
1826
}
1927
}
2028
},

test/index.test.js

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,16 @@ import validateOptions from '../src';
77

88
test('Valid', () => {
99
const options = {
10-
string: 'hello',
10+
type() {},
1111
array: [ 'a' ],
12-
object: { prop: false },
12+
string: 'hello',
13+
object: {
14+
prop: false,
15+
object: {
16+
prop: false
17+
}
18+
},
1319
boolean: true,
14-
type() {},
1520
instance: new RegExp('')
1621
};
1722

@@ -21,11 +26,16 @@ test('Valid', () => {
2126

2227
describe('Error', () => {
2328
const options = {
24-
string: false,
29+
type: null,
2530
array: {},
26-
object: { prop: 1 },
31+
string: false,
32+
object: {
33+
prop: 1,
34+
object: {
35+
prop: 1
36+
}
37+
},
2738
boolean: 'hello',
28-
type: null,
2939
instance() {}
3040
};
3141

@@ -34,16 +44,25 @@ describe('Error', () => {
3444
};
3545

3646
test('should throw error', () => {
37-
expect(validate).toThrowError(/Validation Error\n\n{Name} Invalid Options\n\n/);
47+
expect(validate).toThrowError();
48+
expect(validate).toThrowErrorMatchingSnapshot();
3849
});
3950

40-
test('should have errors for every key in options', () => {
51+
test('should have errors for every option key', () => {
4152
try {
4253
validate();
4354
} catch (err) {
44-
const errors = err.errors.map(e => e.dataPath);
55+
const errors = err.errors.map(err => err.dataPath);
4556

46-
const expected = ['.string', '.array', '.object.prop', '.boolean', '.type', '.instance'];
57+
const expected = [
58+
'.string',
59+
'.array',
60+
'.object.prop',
61+
'.object.object.prop',
62+
'.boolean',
63+
'.type',
64+
'.instance'
65+
];
4766

4867
expect(errors).toMatchObject(expected);
4968
expect(err.errors).toMatchSnapshot();

0 commit comments

Comments
 (0)