From 740233d5bf17681f9b685b8b5f4578e3dd98287c Mon Sep 17 00:00:00 2001 From: charlie Benger-stevenson Date: Tue, 10 Oct 2023 12:45:00 +0100 Subject: [PATCH] Keyword option overrride, unit test and readme update --- README.md | 2 + spec/options.spec.ts | 114 ++++++++++++++++++++++++++++++++++--------- src/index.ts | 7 ++- 3 files changed, 98 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index b1271b9..8f6749c 100644 --- a/README.md +++ b/README.md @@ -313,6 +313,7 @@ Defaults: { keepErrors: false, singleError: false, + keyword: 'errorMessage' } ``` @@ -321,6 +322,7 @@ Defaults: - `false` (default): create multiple errors, one for each message - `true`: create single error, messages are concatenated using `"; "` - non-empty string: this string is used as a separator to concatenate messages +- _keyword_: Override the keyword for error messages, for example x-error-message for use with swagger extensions ## Supporters diff --git a/spec/options.spec.ts b/spec/options.spec.ts index 8771ae0..ddf4348 100644 --- a/spec/options.spec.ts +++ b/spec/options.spec.ts @@ -2,10 +2,31 @@ import ajvErrors from ".." import Ajv, {ErrorObject, SchemaObject, ValidateFunction} from "ajv" import assert = require("assert") +function assertErrors( + validate: ValidateFunction, + expectedErrors: Partial[] + ): void { + const {errors} = validate + assert.strictEqual(errors?.length, expectedErrors.length) + + expectedErrors.forEach((expectedErr, i) => { + const err = errors[i] as ErrorObject & {emUsed: boolean} + assert.strictEqual(err.keyword, expectedErr.keyword) + assert.strictEqual(err.instancePath, expectedErr.instancePath) + assert.strictEqual(err.emUsed, expectedErr.emUsed) + if (expectedErr.keyword === "errorMessage") { + assert.strictEqual(err.params.errors.length, expectedErr.errors?.length) + expectedErr.errors?.forEach((matchedKeyword: string, j: number) => + assert.strictEqual(err.params.errors[j].keyword, matchedKeyword) + ) + } + }) + } + describe("options", () => { - let ajv: Ajv + let ajv: Ajv - beforeEach(() => { + beforeEach(() => { ajv = new Ajv({allErrors: true}) }) @@ -180,6 +201,74 @@ describe("options", () => { }) }) + describe("keyword option", () => { + it("should set the keyword", () => { + ajvErrors(ajv, {keyword: 'x-errorMessage'}) + testKeywordErrors() + }) + function testKeywordErrors(): void { + const errorMessageKeyword = "x-errorMessage" + const schema: SchemaObject = { + type: "number", + minimum: 2, + maximum: 10, + multipleOf: 2, + [errorMessageKeyword]: { + type: "should be number", + minimum: "should be >= 2", + maximum: "should be <= 10", + multipleOf: "should be multipleOf 2", + }, + } + const validate = ajv.compile(schema) + assert.strictEqual(validate(11), false) + + assertErrors(validate, [ + { + "instancePath": "", + "schemaPath": "#/x-errorMessage", + "keyword": "x-errorMessage", + "params": { + "errors": [ + { + "instancePath": "", + "schemaPath": "#/maximum", + "keyword": "maximum", + "params": { + "comparison": "<=", + "limit": 10 + }, + "message": "must be <= 10", + "emUsed": true + } + ] + }, + "message": "should be <= 10" + }, + { + "instancePath": "", + "schemaPath": "#/x-errorMessage", + "keyword": "x-errorMessage", + "params": { + "errors": [ + { + "instancePath": "", + "schemaPath": "#/multipleOf", + "keyword": "multipleOf", + "params": { + "multipleOf": 2 + }, + "message": "must be multiple of 2", + "emUsed": true + } + ] + }, + "message": "should be multipleOf 2" + } + ]) + } + }) + describe("singleError", () => { describe("= true", () => { it("should generate a single error for all keywords", () => { @@ -228,25 +317,4 @@ describe("options", () => { ]) } }) - - function assertErrors( - validate: ValidateFunction, - expectedErrors: Partial[] - ): void { - const {errors} = validate - assert.strictEqual(errors?.length, expectedErrors.length) - - expectedErrors.forEach((expectedErr, i) => { - const err = errors[i] as ErrorObject & {emUsed: boolean} - assert.strictEqual(err.keyword, expectedErr.keyword) - assert.strictEqual(err.instancePath, expectedErr.instancePath) - assert.strictEqual(err.emUsed, expectedErr.emUsed) - if (expectedErr.keyword === "errorMessage") { - assert.strictEqual(err.params.errors.length, expectedErr.errors?.length) - expectedErr.errors?.forEach((matchedKeyword: string, j: number) => - assert.strictEqual(err.params.errors[j].keyword, matchedKeyword) - ) - } - }) - } }) diff --git a/src/index.ts b/src/index.ts index 1013551..82e00f3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,8 +23,6 @@ interface ChildErrors { items?: ErrorsMap } -const keyword = "errorMessage" - const used: Name = new Name("emUsed") const KEYWORD_PROPERTY_PARAMS = { @@ -36,13 +34,18 @@ const KEYWORD_PROPERTY_PARAMS = { export interface ErrorMessageOptions { keepErrors?: boolean singleError?: boolean | string + keyword?: string } const INTERPOLATION = /\$\{[^}]+\}/ const INTERPOLATION_REPLACE = /\$\{([^}]+)\}/g const EMPTY_STR = /^""\s*\+\s*|\s*\+\s*""$/g +let keyword = 'errorMessage' function errorMessage(options: ErrorMessageOptions): CodeKeywordDefinition { + if(options.keyword){ + keyword = options.keyword; + } return { keyword, schemaType: ["string", "object"],