Skip to content

Commit

Permalink
feat(src): add support for custom error messages (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
michael-ciniawsky committed Aug 7, 2018
1 parent ebc09b7 commit 1cbe4ef
Show file tree
Hide file tree
Showing 7 changed files with 219 additions and 19 deletions.
21 changes: 20 additions & 1 deletion README.md
Expand Up @@ -27,7 +27,7 @@ npm i schema-utils

### `validateOptions`

**schema.json**
**`schema.json`**
```js
{
"type": "object",
Expand All @@ -38,6 +38,25 @@ npm i schema-utils
}
```

#### Error Messages (Custom)

**`schema.json`**
```js
{
"type": "object",
"properties": {
"option": {
"type": [ "boolean" ]
}
},
// Overrides the default err.message for option
"errorMessage": {
"option": "should be {Boolean} (https:/github.com/org/repo#anchor)"
}
"additionalProperties": false
}
```

```js
import schema from 'path/to/schema.json'
import validateOptions from 'schema-utils'
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -18,6 +18,7 @@
},
"dependencies": {
"ajv": "^6.1.0",
"ajv-errors": "^1.0.0",
"ajv-keywords": "^3.1.0"
},
"devDependencies": {
Expand Down
13 changes: 9 additions & 4 deletions src/ValidationError.js
@@ -1,5 +1,6 @@
/* eslint-disable
strict
strict,
no-param-reassign
*/

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

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

errors.forEach((err) => {
this.message += `options${err.dataPath} ${err.message}\n`;
this.errors = errors.map((err) => {
err.dataPath = err.dataPath.replace(/\//g, '.');

return err;
});

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

Error.captureStackTrace(this, this.constructor);
}
Expand Down
9 changes: 5 additions & 4 deletions src/validateOptions.js
Expand Up @@ -9,17 +9,18 @@ const fs = require('fs');
const path = require('path');

const Ajv = require('ajv');
const ajvKeywords = require('ajv-keywords');
const errors = require('ajv-errors');
const keywords = require('ajv-keywords');

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

const ajv = new Ajv({
allErrors: true,
useDefaults: true,
errorDataPath: 'property',
jsonPointers: true,
});

ajvKeywords(ajv, ['instanceof', 'typeof']);
errors(ajv);
keywords(ajv, ['instanceof', 'typeof']);

const validateOptions = (schema, options, name) => {
if (typeof schema === 'string') {
Expand Down
52 changes: 50 additions & 2 deletions test/__snapshots__/index.test.js.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Error should have errors for every option key 1`] = `
exports[`Error Errors 1`] = `
Array [
Object {
"dataPath": ".string",
Expand Down Expand Up @@ -68,7 +68,55 @@ Array [
]
`;

exports[`Error should throw error 1`] = `
exports[`Error Messages Customized 1`] = `
Object {
"dataPath": ".string",
"keyword": "errorMessage",
"message": "should be {String} (https://github.com/org/repo#anchor)",
"params": Object {
"errors": Array [
Object {
"dataPath": "/string",
"keyword": "type",
"message": "should be string",
"params": Object {
"type": "string",
},
"schemaPath": "#/properties/string/type",
},
],
},
"schemaPath": "#/errorMessage",
}
`;

exports[`Error Messages Customized 2`] = `
"{Name} Invalid Options
options.string should be {String} (https://github.com/org/repo#anchor)
"
`;

exports[`Error Messages Default 1`] = `
Object {
"dataPath": ".string",
"keyword": "type",
"message": "should be string",
"params": Object {
"type": "string",
},
"schemaPath": "#/properties/string/type",
}
`;

exports[`Error Messages Default 2`] = `
"{Name} Invalid Options
options.string should be string
"
`;

exports[`Error Throws 1`] = `
"{Name} Invalid Options
options.string should be string
Expand Down
61 changes: 61 additions & 0 deletions test/fixtures/errors/schema.json
@@ -0,0 +1,61 @@
{
"type": "object",
"properties": {
"string": {
"type": "string"
},
"array": {
"type": "array",
"items": {
"type": "string"
}
},
"object": {
"type": "object",
"properties": {
"prop": {
"type": "boolean"
},
"object": {
"type": "object",
"properties": {
"prop": {
"type": "boolean"
}
},
"errorMessage": {
"properties": {
"prop": "should be {Boolean} (https://github.com/org/repo#anchor)"
}
}
}
},
"errorMessage": {
"properties": {
"prop": "should be {Boolean} (https://github.com/org/repo#anchor)",
"object": "should be {Object} (https://github.com/org/repo#anchor)"
}
}
},
"boolean": {
"type": "boolean"
},
"type": {
"typeof": "function"
},
"instance": {
"instanceof": "RegExp"
}
},
"errorMessage": {
"properties": {
"type": "should be {Function} (https://github.com/org/repo#anchor)",
"array": "should be {Array} (https://github.com/org/repo#anchor)",
"string": "should be {String} (https://github.com/org/repo#anchor)",
"object": "should be {Object} (https://github.com/org/repo#anchor)",
"boolean": "should be {Boolean} (https://github.com/org/repo#anchor)",
"instance": "should be {RegExp} (https://github.com/org/repo#anchor)"
}
},
"additionalProperties": false
}
81 changes: 73 additions & 8 deletions test/index.test.js
@@ -1,6 +1,7 @@
/* eslint-disable
strict,
no-shadow
no-shadow,
arrow-body-style
*/

'use strict';
Expand All @@ -21,10 +22,11 @@ test('Valid', () => {
boolean: true,
instance: new RegExp(''),
};
const validate = () => {
return validateOptions('test/fixtures/schema.json', options, '{Name}');
};

expect(validateOptions('test/fixtures/schema.json', options, 'Loader')).toBe(
true
);
expect(validate()).toBe(true);
});

describe('Error', () => {
Expand All @@ -42,15 +44,16 @@ describe('Error', () => {
instance() {},
};

const validate = () =>
validateOptions('test/fixtures/schema.json', options, '{Name}');
const validate = () => {
return validateOptions('test/fixtures/schema.json', options, '{Name}');
};

test('should throw error', () => {
test('Throws', () => {
expect(validate).toThrowError();
expect(validate).toThrowErrorMatchingSnapshot();
});

test('should have errors for every option key', () => {
test('Errors', () => {
try {
validate();
} catch (err) {
Expand All @@ -70,4 +73,66 @@ describe('Error', () => {
expect(err.errors).toMatchSnapshot();
}
});

describe('Messages', () => {
test('Default', () => {
const options = {
type() {},
array: [''],
string: 1,
object: {
prop: false,
object: {
prop: false,
},
},
boolean: true,
instance: new RegExp(''),
};

const validate = () => {
return validateOptions('test/fixtures/schema.json', options, '{Name}');
};

try {
validate();
} catch (err) {
err.errors.forEach((err) => expect(err).toMatchSnapshot());

expect(err.message).toMatchSnapshot();
}
});

test('Customized', () => {
const options = {
type() {},
array: [''],
string: 1,
object: {
prop: false,
object: {
prop: false,
},
},
boolean: true,
instance: new RegExp(''),
};

const validate = () => {
return validateOptions(
'test/fixtures/errors/schema.json',
options,
'{Name}'
);
};

try {
validate();
} catch (err) {
err.errors.forEach((err) => expect(err).toMatchSnapshot());

expect(err.message).toMatchSnapshot();
}
});
});
});

0 comments on commit 1cbe4ef

Please sign in to comment.