Skip to content

Commit 1cbe4ef

Browse files
feat(src): add support for custom error messages (#33)
1 parent ebc09b7 commit 1cbe4ef

File tree

7 files changed

+219
-19
lines changed

7 files changed

+219
-19
lines changed

README.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ npm i schema-utils
2727

2828
### `validateOptions`
2929

30-
**schema.json**
30+
**`schema.json`**
3131
```js
3232
{
3333
"type": "object",
@@ -38,6 +38,25 @@ npm i schema-utils
3838
}
3939
```
4040

41+
#### Error Messages (Custom)
42+
43+
**`schema.json`**
44+
```js
45+
{
46+
"type": "object",
47+
"properties": {
48+
"option": {
49+
"type": [ "boolean" ]
50+
}
51+
},
52+
// Overrides the default err.message for option
53+
"errorMessage": {
54+
"option": "should be {Boolean} (https:/github.com/org/repo#anchor)"
55+
}
56+
"additionalProperties": false
57+
}
58+
```
59+
4160
```js
4261
import schema from 'path/to/schema.json'
4362
import validateOptions from 'schema-utils'

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
},
1919
"dependencies": {
2020
"ajv": "^6.1.0",
21+
"ajv-errors": "^1.0.0",
2122
"ajv-keywords": "^3.1.0"
2223
},
2324
"devDependencies": {

src/ValidationError.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* eslint-disable
2-
strict
2+
strict,
3+
no-param-reassign
34
*/
45

56
'use strict';
@@ -12,11 +13,15 @@ class ValidationError extends Error {
1213

1314
this.message = `${name || ''} Invalid Options\n\n`;
1415

15-
errors.forEach((err) => {
16-
this.message += `options${err.dataPath} ${err.message}\n`;
16+
this.errors = errors.map((err) => {
17+
err.dataPath = err.dataPath.replace(/\//g, '.');
18+
19+
return err;
1720
});
1821

19-
this.errors = errors;
22+
this.errors.forEach((err) => {
23+
this.message += `options${err.dataPath} ${err.message}\n`;
24+
});
2025

2126
Error.captureStackTrace(this, this.constructor);
2227
}

src/validateOptions.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,18 @@ const fs = require('fs');
99
const path = require('path');
1010

1111
const Ajv = require('ajv');
12-
const ajvKeywords = require('ajv-keywords');
12+
const errors = require('ajv-errors');
13+
const keywords = require('ajv-keywords');
1314

1415
const ValidationError = require('./ValidationError');
1516

1617
const ajv = new Ajv({
1718
allErrors: true,
18-
useDefaults: true,
19-
errorDataPath: 'property',
19+
jsonPointers: true,
2020
});
2121

22-
ajvKeywords(ajv, ['instanceof', 'typeof']);
22+
errors(ajv);
23+
keywords(ajv, ['instanceof', 'typeof']);
2324

2425
const validateOptions = (schema, options, name) => {
2526
if (typeof schema === 'string') {

test/__snapshots__/index.test.js.snap

Lines changed: 50 additions & 2 deletions
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 option key 1`] = `
3+
exports[`Error Errors 1`] = `
44
Array [
55
Object {
66
"dataPath": ".string",
@@ -68,7 +68,55 @@ Array [
6868
]
6969
`;
7070

71-
exports[`Error should throw error 1`] = `
71+
exports[`Error Messages Customized 1`] = `
72+
Object {
73+
"dataPath": ".string",
74+
"keyword": "errorMessage",
75+
"message": "should be {String} (https://github.com/org/repo#anchor)",
76+
"params": Object {
77+
"errors": Array [
78+
Object {
79+
"dataPath": "/string",
80+
"keyword": "type",
81+
"message": "should be string",
82+
"params": Object {
83+
"type": "string",
84+
},
85+
"schemaPath": "#/properties/string/type",
86+
},
87+
],
88+
},
89+
"schemaPath": "#/errorMessage",
90+
}
91+
`;
92+
93+
exports[`Error Messages Customized 2`] = `
94+
"{Name} Invalid Options
95+
96+
options.string should be {String} (https://github.com/org/repo#anchor)
97+
"
98+
`;
99+
100+
exports[`Error Messages Default 1`] = `
101+
Object {
102+
"dataPath": ".string",
103+
"keyword": "type",
104+
"message": "should be string",
105+
"params": Object {
106+
"type": "string",
107+
},
108+
"schemaPath": "#/properties/string/type",
109+
}
110+
`;
111+
112+
exports[`Error Messages Default 2`] = `
113+
"{Name} Invalid Options
114+
115+
options.string should be string
116+
"
117+
`;
118+
119+
exports[`Error Throws 1`] = `
72120
"{Name} Invalid Options
73121
74122
options.string should be string

test/fixtures/errors/schema.json

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{
2+
"type": "object",
3+
"properties": {
4+
"string": {
5+
"type": "string"
6+
},
7+
"array": {
8+
"type": "array",
9+
"items": {
10+
"type": "string"
11+
}
12+
},
13+
"object": {
14+
"type": "object",
15+
"properties": {
16+
"prop": {
17+
"type": "boolean"
18+
},
19+
"object": {
20+
"type": "object",
21+
"properties": {
22+
"prop": {
23+
"type": "boolean"
24+
}
25+
},
26+
"errorMessage": {
27+
"properties": {
28+
"prop": "should be {Boolean} (https://github.com/org/repo#anchor)"
29+
}
30+
}
31+
}
32+
},
33+
"errorMessage": {
34+
"properties": {
35+
"prop": "should be {Boolean} (https://github.com/org/repo#anchor)",
36+
"object": "should be {Object} (https://github.com/org/repo#anchor)"
37+
}
38+
}
39+
},
40+
"boolean": {
41+
"type": "boolean"
42+
},
43+
"type": {
44+
"typeof": "function"
45+
},
46+
"instance": {
47+
"instanceof": "RegExp"
48+
}
49+
},
50+
"errorMessage": {
51+
"properties": {
52+
"type": "should be {Function} (https://github.com/org/repo#anchor)",
53+
"array": "should be {Array} (https://github.com/org/repo#anchor)",
54+
"string": "should be {String} (https://github.com/org/repo#anchor)",
55+
"object": "should be {Object} (https://github.com/org/repo#anchor)",
56+
"boolean": "should be {Boolean} (https://github.com/org/repo#anchor)",
57+
"instance": "should be {RegExp} (https://github.com/org/repo#anchor)"
58+
}
59+
},
60+
"additionalProperties": false
61+
}

test/index.test.js

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* eslint-disable
22
strict,
3-
no-shadow
3+
no-shadow,
4+
arrow-body-style
45
*/
56

67
'use strict';
@@ -21,10 +22,11 @@ test('Valid', () => {
2122
boolean: true,
2223
instance: new RegExp(''),
2324
};
25+
const validate = () => {
26+
return validateOptions('test/fixtures/schema.json', options, '{Name}');
27+
};
2428

25-
expect(validateOptions('test/fixtures/schema.json', options, 'Loader')).toBe(
26-
true
27-
);
29+
expect(validate()).toBe(true);
2830
});
2931

3032
describe('Error', () => {
@@ -42,15 +44,16 @@ describe('Error', () => {
4244
instance() {},
4345
};
4446

45-
const validate = () =>
46-
validateOptions('test/fixtures/schema.json', options, '{Name}');
47+
const validate = () => {
48+
return validateOptions('test/fixtures/schema.json', options, '{Name}');
49+
};
4750

48-
test('should throw error', () => {
51+
test('Throws', () => {
4952
expect(validate).toThrowError();
5053
expect(validate).toThrowErrorMatchingSnapshot();
5154
});
5255

53-
test('should have errors for every option key', () => {
56+
test('Errors', () => {
5457
try {
5558
validate();
5659
} catch (err) {
@@ -70,4 +73,66 @@ describe('Error', () => {
7073
expect(err.errors).toMatchSnapshot();
7174
}
7275
});
76+
77+
describe('Messages', () => {
78+
test('Default', () => {
79+
const options = {
80+
type() {},
81+
array: [''],
82+
string: 1,
83+
object: {
84+
prop: false,
85+
object: {
86+
prop: false,
87+
},
88+
},
89+
boolean: true,
90+
instance: new RegExp(''),
91+
};
92+
93+
const validate = () => {
94+
return validateOptions('test/fixtures/schema.json', options, '{Name}');
95+
};
96+
97+
try {
98+
validate();
99+
} catch (err) {
100+
err.errors.forEach((err) => expect(err).toMatchSnapshot());
101+
102+
expect(err.message).toMatchSnapshot();
103+
}
104+
});
105+
106+
test('Customized', () => {
107+
const options = {
108+
type() {},
109+
array: [''],
110+
string: 1,
111+
object: {
112+
prop: false,
113+
object: {
114+
prop: false,
115+
},
116+
},
117+
boolean: true,
118+
instance: new RegExp(''),
119+
};
120+
121+
const validate = () => {
122+
return validateOptions(
123+
'test/fixtures/errors/schema.json',
124+
options,
125+
'{Name}'
126+
);
127+
};
128+
129+
try {
130+
validate();
131+
} catch (err) {
132+
err.errors.forEach((err) => expect(err).toMatchSnapshot());
133+
134+
expect(err.message).toMatchSnapshot();
135+
}
136+
});
137+
});
73138
});

0 commit comments

Comments
 (0)