From 29319c5d08bae069865f2707157f32c271e9a4c1 Mon Sep 17 00:00:00 2001 From: Fulvio Gentile Date: Fri, 1 May 2020 21:37:33 +0200 Subject: [PATCH 1/6] Add a way to provide custom messages - Fixes #122 --- readme.md | 25 +++++++++++++++++++++++ source/predicates/predicate.ts | 21 +++++++++++++++++++ test/custom-message.ts | 37 ++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 test/custom-message.ts diff --git a/readme.md b/readme.md index a04a404..3930200 100644 --- a/readme.md +++ b/readme.md @@ -236,6 +236,31 @@ ow(1, 'input', ow.number.validate(value => ({ //=> ArgumentError: Expected number `input` to be greater than 10, got 1 ``` +#### message(fn | string) + +Provide a custom message + +```ts +ow('🌈', 'unicorn', ow.string.equals('🦄').message('Expected unicorn, got rainbow')); +//=> ArgumentError: Expected unicorn, got rainbow +``` + +You can also pass in a function as `message` value which accepts the label as argument. + +```ts +ow('🌈', ow.string.minLength(5).message((value, label) => `Expected ${label}, to be have a minimum length of 5, got \`${value}\``)); +//=> ArgumentError: Expected string, to be have a minimum length of 5, got `🌈` +``` + +If you want to add a different message per validation it is possible + +```ts +ow('1234', ow.string.minLength(5).message((value, label) => `Expected ${label}, to be have a minimum length of 5, got \`${value}\``).url.message('This is no url')); +//=> ArgumentError: Expected string, to be have a minimum length of 5, got `1234` +ow('12345', ow.string.minLength(5).message((value, label) => `Expected ${label}, to be have a minimum length of 5, got \`${value}\``).url.message('This is no url')); +//=> ArgumentError: This is no url +``` + This can be useful for creating your own reusable validators which can be extracted to a separate npm package. ## Maintainers diff --git a/source/predicates/predicate.ts b/source/predicates/predicate.ts index 3902eae..15ad2a1 100644 --- a/source/predicates/predicate.ts +++ b/source/predicates/predicate.ts @@ -4,6 +4,8 @@ import {not} from '../operators/not'; import {BasePredicate, testSymbol} from './base-predicate'; import {Main} from '..'; +export type ValidatorMessage = (value: T, label?: string) => string; + /** @hidden */ @@ -164,6 +166,25 @@ export class Predicate implements BasePredicate { }); } + /** + Override the message thrown + + @param newMessage | Either a string containing the new message or a function returning the new message + */ + message(newMessage: string | ValidatorMessage) { + const {validators} = this.context; + + validators[validators.length - 1].message = (value, label) => { + if (typeof newMessage === 'function') { + return newMessage(value, label); + } + + return newMessage; + }; + + return this; + } + /** Register a new validator. diff --git a/test/custom-message.ts b/test/custom-message.ts new file mode 100644 index 0000000..aa0414e --- /dev/null +++ b/test/custom-message.ts @@ -0,0 +1,37 @@ +import test from 'ava'; +import {default as ow, Predicate} from '../source'; + +class CustomPredicate extends Predicate { + constructor() { + super('string'); + } + + get unicorn() { + return this.addValidator({ + message: (value, label) => `Expected ${label} to be \`🦄\`, got \`${value}\``, + validator: value => value === '🦄' + }); + } +} + +test('custom validate message', t => { + t.throws(() => { + ow('🌈', 'unicorn', new CustomPredicate().unicorn.message('Expect unicorn, got rainbow')); + }, 'Expect unicorn, got rainbow'); + + t.throws(() => { + ow('🌈', 'unicorn', new CustomPredicate().unicorn.message((value, label) => `Expected ${label}, to be \`🦄\` got \`${value}\``)); + }, 'Expected string `unicorn`, to be `🦄` got `🌈`'); + + t.throws(() => { + ow('🌈', ow.string.minLength(5).message((value, label) => `Expected ${label}, to be have a minimum length of 5, got \`${value}\``)); + }, 'Expected string, to be have a minimum length of 5, got `🌈`'); + + t.throws(() => { + ow('1234', ow.string.minLength(5).message((value, label) => `Expected ${label}, to be have a minimum length of 5, got \`${value}\``).url.message('This is no url')); + }, 'Expected string, to be have a minimum length of 5, got `1234`'); + + t.throws(() => { + ow('12345', ow.string.minLength(5).message((value, label) => `Expected ${label}, to be have a minimum length of 5, got \`${value}\``).url.message('This is no url')); + }, 'This is no url'); +}); From b8e3231d493a7259728aec2697d813638f28b642 Mon Sep 17 00:00:00 2001 From: Fulvio Gentile Date: Thu, 7 May 2020 22:19:37 +0200 Subject: [PATCH 2/6] Fix pr --- readme.md | 26 ++++++++++++++++++-------- source/predicates/predicate.ts | 7 +++++-- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/readme.md b/readme.md index 3930200..f9ab8ca 100644 --- a/readme.md +++ b/readme.md @@ -236,28 +236,39 @@ ow(1, 'input', ow.number.validate(value => ({ //=> ArgumentError: Expected number `input` to be greater than 10, got 1 ``` -#### message(fn | string) +#### message(string | fn) -Provide a custom message +Provide a custom message: ```ts ow('🌈', 'unicorn', ow.string.equals('🦄').message('Expected unicorn, got rainbow')); //=> ArgumentError: Expected unicorn, got rainbow ``` -You can also pass in a function as `message` value which accepts the label as argument. +You can also pass in a function which receives the value as the first parameter and the label as the second parameter and is expected to return the message. ```ts -ow('🌈', ow.string.minLength(5).message((value, label) => `Expected ${label}, to be have a minimum length of 5, got \`${value}\``)); +ow('🌈', ow.string.minLength(5).message((value, label) => `Expected ${label}, to have a minimum length of 5, got \`${value}\``)); //=> ArgumentError: Expected string, to be have a minimum length of 5, got `🌈` ``` -If you want to add a different message per validation it is possible +It's also possible to add a separate message per validation: ```ts -ow('1234', ow.string.minLength(5).message((value, label) => `Expected ${label}, to be have a minimum length of 5, got \`${value}\``).url.message('This is no url')); +ow( + '1234', + ow.string + .minLength(5).message((value, label) => `Expected ${label}, to be have a minimum length of 5, got \`${value}\``) + .url.message('This is no url') +); //=> ArgumentError: Expected string, to be have a minimum length of 5, got `1234` -ow('12345', ow.string.minLength(5).message((value, label) => `Expected ${label}, to be have a minimum length of 5, got \`${value}\``).url.message('This is no url')); + +ow( + '12345', + ow.string + .minLength(5).message((value, label) => `Expected ${label}, to be have a minimum length of 5, got \`${value}\``) + .url.message('This is no url') +); //=> ArgumentError: This is no url ``` @@ -272,4 +283,3 @@ This can be useful for creating your own reusable validators which can be extrac - [@sindresorhus/is](https://github.com/sindresorhus/is) - Type check values - [ngx-ow](https://github.com/SamVerschueren/ngx-ow) - Angular form validation on steroids - diff --git a/source/predicates/predicate.ts b/source/predicates/predicate.ts index 15ad2a1..45ea5da 100644 --- a/source/predicates/predicate.ts +++ b/source/predicates/predicate.ts @@ -4,6 +4,9 @@ import {not} from '../operators/not'; import {BasePredicate, testSymbol} from './base-predicate'; import {Main} from '..'; +/** +@hidden + */ export type ValidatorMessage = (value: T, label?: string) => string; /** @@ -167,9 +170,9 @@ export class Predicate implements BasePredicate { } /** - Override the message thrown + Provide an new error message to be thrown when the validation fails. - @param newMessage | Either a string containing the new message or a function returning the new message + @param newMessage - Either a string containing the new message or a function returning the new message */ message(newMessage: string | ValidatorMessage) { const {validators} = this.context; From 3b2498848323658dc78b8444540ca736786d4f57 Mon Sep 17 00:00:00 2001 From: Fulvio Gentile Date: Sat, 23 May 2020 17:37:54 +0200 Subject: [PATCH 3/6] Add docs for `ValidatorMessage` --- source/predicates/predicate.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/predicates/predicate.ts b/source/predicates/predicate.ts index 45ea5da..2057f78 100644 --- a/source/predicates/predicate.ts +++ b/source/predicates/predicate.ts @@ -5,7 +5,10 @@ import {BasePredicate, testSymbol} from './base-predicate'; import {Main} from '..'; /** -@hidden +Function executed when the provided validation fails. +The first argument provided to the function is the provided `value` for the property, +the second argument is the optional `label` for the property. +The returned value will be the error message. */ export type ValidatorMessage = (value: T, label?: string) => string; From d94b4f0b983a786bd558083f9095504c228128c8 Mon Sep 17 00:00:00 2001 From: Fulvio Gentile Date: Sat, 6 Jun 2020 13:56:35 +0200 Subject: [PATCH 4/6] Apply suggestions from code review Co-authored-by: Sindre Sorhus --- source/predicates/predicate.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/predicates/predicate.ts b/source/predicates/predicate.ts index 2057f78..89827e8 100644 --- a/source/predicates/predicate.ts +++ b/source/predicates/predicate.ts @@ -9,7 +9,7 @@ Function executed when the provided validation fails. The first argument provided to the function is the provided `value` for the property, the second argument is the optional `label` for the property. The returned value will be the error message. - */ +*/ export type ValidatorMessage = (value: T, label?: string) => string; /** @@ -175,7 +175,7 @@ export class Predicate implements BasePredicate { /** Provide an new error message to be thrown when the validation fails. - @param newMessage - Either a string containing the new message or a function returning the new message + @param newMessage - Either a string containing the new message or a function returning the new message. */ message(newMessage: string | ValidatorMessage) { const {validators} = this.context; From 7c3cef5624e81cd5f1e1077007cb8500391fab31 Mon Sep 17 00:00:00 2001 From: Fulvio Gentile Date: Sat, 6 Jun 2020 14:06:11 +0200 Subject: [PATCH 5/6] chore: Fix pr --- source/predicates/predicate.ts | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/source/predicates/predicate.ts b/source/predicates/predicate.ts index 89827e8..2cf3ba3 100644 --- a/source/predicates/predicate.ts +++ b/source/predicates/predicate.ts @@ -6,11 +6,13 @@ import {Main} from '..'; /** Function executed when the provided validation fails. -The first argument provided to the function is the provided `value` for the property, -the second argument is the optional `label` for the property. -The returned value will be the error message. + +@param value - Tested value +@param label - Label of the tested value + +@returns {string} - The actual error message */ -export type ValidatorMessage = (value: T, label?: string) => string; +export type ValidatorMessageBuilder = (value: T, label?: string) => string; /** @hidden @@ -173,11 +175,22 @@ export class Predicate implements BasePredicate { } /** - Provide an new error message to be thrown when the validation fails. + Provide a new error message to be thrown when the validation fails. @param newMessage - Either a string containing the new message or a function returning the new message. + + @example + ``` + ow('🌈', 'unicorn', ow.string.equals('🦄').message('Expected unicorn, got rainbow')); + //=> ArgumentError: Expected unicorn, got rainbow + ``` + @example + ``` + ow('🌈', ow.string.minLength(5).message((value, label) => `Expected ${label}, to have a minimum length of 5, got \`${value}\``)); + //=> ArgumentError: Expected string, to be have a minimum length of 5, got `🌈` + ``` */ - message(newMessage: string | ValidatorMessage) { + message(newMessage: string | ValidatorMessageBuilder) { const {validators} = this.context; validators[validators.length - 1].message = (value, label) => { From 48b38ccbf83bf8dbdc0c23732b306018ee49dcf3 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Mon, 20 Jul 2020 09:38:32 +0800 Subject: [PATCH 6/6] Update predicate.ts --- source/predicates/predicate.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/predicates/predicate.ts b/source/predicates/predicate.ts index 2cf3ba3..baa9b39 100644 --- a/source/predicates/predicate.ts +++ b/source/predicates/predicate.ts @@ -7,10 +7,10 @@ import {Main} from '..'; /** Function executed when the provided validation fails. -@param value - Tested value -@param label - Label of the tested value +@param value - The tested value. +@param label - Label of the tested value. -@returns {string} - The actual error message +@returns {string} - The actual error message. */ export type ValidatorMessageBuilder = (value: T, label?: string) => string; @@ -25,8 +25,7 @@ export interface Validator { /** Provide custom message used by `not` operator. - When absent, the return value of `message()` is used and 'not' is inserted after the first 'to', - e.g. `Expected 'smth' to be empty` -> `Expected 'smth' to not be empty`. + When absent, the return value of `message()` is used and 'not' is inserted after the first 'to', e.g. `Expected 'smth' to be empty` -> `Expected 'smth' to not be empty`. */ negatedMessage?(value: T, label: string): string; } @@ -184,6 +183,7 @@ export class Predicate implements BasePredicate { ow('🌈', 'unicorn', ow.string.equals('🦄').message('Expected unicorn, got rainbow')); //=> ArgumentError: Expected unicorn, got rainbow ``` + @example ``` ow('🌈', ow.string.minLength(5).message((value, label) => `Expected ${label}, to have a minimum length of 5, got \`${value}\``));