diff --git a/.changeset/tidy-pointers-smile.md b/.changeset/tidy-pointers-smile.md new file mode 100644 index 00000000..80ab610d --- /dev/null +++ b/.changeset/tidy-pointers-smile.md @@ -0,0 +1,5 @@ +--- +'better-ajv-errors': patch +--- + +Preserve validation errors for Unicode JSON Pointer paths. diff --git a/src/__tests__/helpers/make-tree.js b/src/__tests__/helpers/make-tree.js index 5211d037..32e0a14f 100644 --- a/src/__tests__/helpers/make-tree.js +++ b/src/__tests__/helpers/make-tree.js @@ -49,6 +49,17 @@ describe('makeTree', () => { `); }); + it('preserves escaped and Unicode JSON pointer segments', async () => { + const error = { instancePath: '/root/~0tilde/~1slash/名前' }; + const tree = makeTree([error]); + + expect( + tree.children['/root'].children['/~0tilde'].children['/~1slash'].children[ + '/名前' + ].errors + ).toEqual([error]); + }); + it('works on array dataPath', async () => { expect( makeTree([{ dataPath: '/root/child/0' }, { dataPath: '/root/child/1' }]) diff --git a/src/__tests__/index.js b/src/__tests__/index.js index 5ad28e5b..66ae211d 100644 --- a/src/__tests__/index.js +++ b/src/__tests__/index.js @@ -33,7 +33,10 @@ describe('Main', () => { }); it('should output errors for multiple required values', async () => { - const [schema, data, json] = await getSchemaAndData('multiple-required', __dirname); + const [schema, data, json] = await getSchemaAndData( + 'multiple-required', + __dirname + ); const ajv = new Ajv({ allErrors: true }); const validate = ajv.compile(schema); const valid = validate(data); @@ -45,5 +48,30 @@ describe('Main', () => { }); expect(res).toMatchSnapshot(); - }); + }); + + it('should output errors for Unicode property paths', async () => { + const schema = { + type: 'object', + properties: { 名前: { type: 'string', minLength: 2 } }, + }; + const data = { 名前: '' }; + const ajv = new Ajv(); + const validate = ajv.compile(schema); + const valid = validate(data); + expect(valid).toBeFalsy(); + + const res = betterAjvErrors(schema, data, validate.errors, { + format: 'js', + }); + + expect(validate.errors).toEqual([ + expect.objectContaining({ instancePath: '/名前' }), + ]); + expect(res).toEqual([ + expect.objectContaining({ + error: '/名前: minLength must NOT have fewer than 2 characters', + }), + ]); + }); }); diff --git a/src/helpers.js b/src/helpers.js index 13e30fad..4302b221 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -15,7 +15,7 @@ import { DefaultValidationError, } from './validation-errors/index'; -const JSON_POINTERS_REGEX = /\/[\w_-]+(\/\d+)?/g; +const JSON_POINTERS_REGEX = /\/[^/]*(\/\d+)?/g; // Make a tree of errors from ajv errors array export function makeTree(ajvErrors = []) { @@ -43,8 +43,8 @@ export function makeTree(ajvErrors = []) { export function filterRedundantErrors(root, parent, key) { /** - * Partition out the errors by kind for ease of proceessing - */ + * Partition out the errors by kind for ease of proceessing + */ const { anyOfErrors, enumErrors, requiredErrors } = getErrors(root).reduce( (acc, error) => { if (isRequiredError(error)) { @@ -63,14 +63,14 @@ export function filterRedundantErrors(root, parent, key) { return acc; }, - { anyOfErrors: [], enumErrors: [], requiredErrors: [] }, + { anyOfErrors: [], enumErrors: [], requiredErrors: [] } ); /** * If there is are `required` errors then we can just drop every non-required error. * And, also `required` should have more priority than `anyOf`. @see #8 */ - + if (requiredErrors.length > 0) { root.errors = requiredErrors; root.children = {}; @@ -93,7 +93,7 @@ export function filterRedundantErrors(root, parent, key) { /** * If all errors are `enum` and siblings have any error then we can safely * ignore the node. As we return early if there's required errors, we only - * need to check anyofErrors length + * need to check anyofErrors length * * **CAUTION** * Need explicit `root.errors` check because `[].every(fn) === true`