diff --git a/.prettierrc.json b/.prettierrc.json index 0c4bb70..3204bbf 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -2,5 +2,15 @@ "tabWidth": 4, "singleQuote": true, "printWidth": 100, - "semi": true + "semi": true, + "overrides": [ + { + "files": [ + "src/types.ts", + "src/validate.ts", + "src/validators/bail.ts" + ], + "options": { "printWidth": 1000 } + } + ] } diff --git a/README.md b/README.md index 5704a6a..450cd32 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ const isUniqueEmail = () => async function (email, conf) { const exists = await db.users.find({ email }); // User | null if (!exists) { - return; + return email; // You have to return the original value } throw 'This email already in use. Try your alternate address.'; }; @@ -106,26 +106,6 @@ const validateRegistration = validate({ What you might notice is the extra parentheses before the async part: in Dvali it is customary to wrap the validator in an extra function. This allows to be consistent with other validation functions that need parameters (`minLength` for example). -To make this `isUniqueEmail` validation function more expressive, we can use the `Success`, `Failure` and `Ignore` macros, exported from the library: - -```js -import validate, { Success, Failure, Ignore } from 'dvali'; - -const isUniqueEmail = () => - async function (email, conf) { - if (typeof email !== 'string') { - return Ignore(); - } - const exists = await db.users.find({ email }); - if (!exists) { - return Success(); - } - return Failure('This email already in use. Try your alternate address.'); - }; -``` - -Why use this `Ignore` call? Composability is very important in Dvali, one function should only do one thing (but do that thing well). In this case, we let the `isString` function handle whether the email is a string, and our function only proceeds if it's valid. This is useful in cases like optional fields, when we want to the validation to pass, even if the tested value is undefined. - To make writing validation functions easier, there are several helpers in the core library for example `validateCondition` for testing simple conditions, or `validateRegex` for testing against a regular expression. Before writing your own validation function for everything, check these out! ### Sanitize and transform @@ -136,15 +116,12 @@ To go forth with our registration theme, check this feature out with a sanitizat ```js import bcrypt from 'bcrypt'; -import validate, { isString, minLength, Ignore, Success } from 'dvali'; +import validate, { isString, minLength } from 'dvali'; const hash = () => async function (password, conf) { - if (typeof password !== 'string') { - return Ignore(); - } const hashedPassword = await bcrypt.hash(password, 8); - return Success(hashedPassword); + return hashedPassword; // ... or just return the Promise: // return bcrypt.hash(password, 8); }; @@ -176,10 +153,10 @@ See the `confirmPassword` higher-order validator in action: const confirmPassword = (validators) => async function (user, conf) { if (user?.password_confirm === undefined) { - return Failure('You should confirm your password.'); + throw 'You should confirm your password.' } if (user.password_confirm !== user.password) { - return Failure('The two passwords do not match.'); + throw 'The two passwords do not match.' } return validate(validators)(user); }; diff --git a/package-lock.json b/package-lock.json index 09c7ee4..c9ef753 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "typescript": "^4.5.4" }, "engines": { - "node": "14<" + "node": ">=14" } }, "node_modules/@ampproject/remapping": { diff --git a/package.json b/package.json index 09b91da..b4d8c06 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "module": "./es/index.js", "types": "./dist/index.d.ts", "engines": { - "node": "14<" + "node": ">=14" }, "exports": { "require": "./dist/index.js", diff --git a/src/index.ts b/src/index.ts index f9418c1..139eb1c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -68,11 +68,8 @@ import truncate from './transformers/string/truncate.js'; import upper from './transformers/string/upper.js'; import { - Failure, - Ignore, FailureFunction, - Success, - Validator, + ValidatorInner, ValidatorConfiguration, ValidatorFunction, ValidatorObject, @@ -81,7 +78,7 @@ import { export type { FailureFunction, - Validator, + ValidatorInner as Validator, ValidatorConfiguration, ValidatorFunction, ValidatorObject, @@ -89,9 +86,6 @@ export type { }; export { - Failure, - Ignore, - Success, validate, arrayOf, bail, diff --git a/src/transformers/number/ceil.ts b/src/transformers/number/ceil.ts index e46e721..d816cdb 100644 --- a/src/transformers/number/ceil.ts +++ b/src/transformers/number/ceil.ts @@ -1,10 +1,7 @@ -import { Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; -const ceil = (): ValidatorFunction => (value, conf) => { - if (typeof value !== 'number' || Number.isNaN(value)) { - return Ignore(); - } - return Success(Math.ceil(value)); +const ceil = (): SyncValidatorFunction => (value, conf) => { + return Math.ceil(value); }; export default ceil; diff --git a/src/transformers/number/clamp.ts b/src/transformers/number/clamp.ts index e5d23b3..0e1703f 100644 --- a/src/transformers/number/clamp.ts +++ b/src/transformers/number/clamp.ts @@ -1,18 +1,15 @@ -import { Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; const clamp = - (min: number, max: number): ValidatorFunction => + (min: number, max: number): SyncValidatorFunction => (value, conf) => { - if (typeof value !== 'number' || Number.isNaN(value)) { - return Ignore(); - } if (min >= value) { - return Success(min); + return min; } if (max <= value) { - return Success(max); + return max; } - return Success(value); + return value; }; export default clamp; diff --git a/src/transformers/number/floor.ts b/src/transformers/number/floor.ts index 5e3b625..7e95410 100644 --- a/src/transformers/number/floor.ts +++ b/src/transformers/number/floor.ts @@ -1,10 +1,7 @@ -import { Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; -const floor = (): ValidatorFunction => (value, conf) => { - if (typeof value !== 'number' || Number.isNaN(value)) { - return Ignore(); - } - return Success(Math.floor(value)); +const floor = (): SyncValidatorFunction => (value, conf) => { + return Math.floor(value); }; export default floor; diff --git a/src/transformers/number/round.ts b/src/transformers/number/round.ts index d86f745..78e072c 100644 --- a/src/transformers/number/round.ts +++ b/src/transformers/number/round.ts @@ -1,10 +1,7 @@ -import { Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; -const round = (): ValidatorFunction => (value, conf) => { - if (typeof value !== 'number' || Number.isNaN(value)) { - return Ignore(); - } - return Success(Math.round(value)); +const round = ():SyncValidatorFunction => (value, conf) => { + return Math.round(value); }; export default round; diff --git a/src/transformers/number/toMultipleOf.ts b/src/transformers/number/toMultipleOf.ts index c7ea2b1..fae3bfc 100644 --- a/src/transformers/number/toMultipleOf.ts +++ b/src/transformers/number/toMultipleOf.ts @@ -1,12 +1,9 @@ -import { Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; const toMultipleOf = - (n: number, round = Math.round): ValidatorFunction => + (n: number, round = Math.round): SyncValidatorFunction => (value, conf) => { - if (typeof value !== 'number' || Number.isNaN(value)) { - return Ignore(); - } - return Success(round(value / n) * n); + return round(value / n) * n; }; export default toMultipleOf; diff --git a/src/transformers/string/lower.ts b/src/transformers/string/lower.ts index 7c8b01a..bb0a894 100644 --- a/src/transformers/string/lower.ts +++ b/src/transformers/string/lower.ts @@ -1,10 +1,7 @@ -import { Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; -const lower = (): ValidatorFunction => (value, conf) => { - if (typeof value !== 'string') { - return Ignore(); - } - return Success(value.toLocaleLowerCase()); +const lower = (): SyncValidatorFunction => (value, conf) => { + return value.toLocaleLowerCase(); }; export default lower; diff --git a/src/transformers/string/normalize.ts b/src/transformers/string/normalize.ts index 9064df8..f2d4a7a 100644 --- a/src/transformers/string/normalize.ts +++ b/src/transformers/string/normalize.ts @@ -1,12 +1,9 @@ -import { Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; const normalize = - (form?: 'NFC' | 'NFD' | 'NFKC' | 'NFKD'): ValidatorFunction => + (form?: 'NFC' | 'NFD' | 'NFKC' | 'NFKD'): SyncValidatorFunction => (value, conf) => { - if (typeof value !== 'string') { - return Ignore(); - } - return Success(value.normalize(form)); + return value.normalize(form); }; export default normalize; diff --git a/src/transformers/string/trim.ts b/src/transformers/string/trim.ts index 6cd08d8..d01da36 100644 --- a/src/transformers/string/trim.ts +++ b/src/transformers/string/trim.ts @@ -1,10 +1,7 @@ -import { Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; -const trim = (): ValidatorFunction => (value, conf) => { - if (typeof value !== 'string') { - return Ignore(); - } - return Success(value.trim()); +const trim = ():SyncValidatorFunction => (value, conf) => { + return value.trim(); }; export default trim; diff --git a/src/transformers/string/truncate.ts b/src/transformers/string/truncate.ts index 37b5b91..b7fddbb 100644 --- a/src/transformers/string/truncate.ts +++ b/src/transformers/string/truncate.ts @@ -1,12 +1,9 @@ -import { Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; const truncate = - (n: number): ValidatorFunction => + (n: number): SyncValidatorFunction => (value, conf) => { - if (typeof value !== 'string') { - return Ignore(); - } - return Success(n >= 0 ? value.substr(0, n) : value.substr(n)); + return n >= 0 ? value.substr(0, n) : value.substr(n); }; export default truncate; diff --git a/src/transformers/string/upper.ts b/src/transformers/string/upper.ts index 5c0095b..d7c1687 100644 --- a/src/transformers/string/upper.ts +++ b/src/transformers/string/upper.ts @@ -1,10 +1,7 @@ -import { Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; -const upper = (): ValidatorFunction => (value, conf) => { - if (typeof value !== 'string') { - return Ignore(); - } - return Success(value.toLocaleUpperCase()); +const upper = ():SyncValidatorFunction => (value, conf) => { + return value.toLocaleUpperCase(); }; export default upper; diff --git a/src/types.ts b/src/types.ts index 273a4f0..fd85e37 100644 --- a/src/types.ts +++ b/src/types.ts @@ -10,30 +10,188 @@ export interface ValidatorConfiguration { parent: any; } -export interface ValidatorFunction { - (value: U, conf: ValidatorConfiguration): Promise; -} +export type SyncValidatingFunction = O extends Promise ? never : (val: I, c?: Partial) => O; +export type SyncValidatingFunctionInner = O extends Promise ? never : SyncValidatingFunction; +export type AsyncValidatingFunction = (val: I, c?: Partial) => Promise; +export type ValidatingFunction = SyncValidatingFunctionInner | AsyncValidatingFunction; + +export type SyncValidatorFunction = (value: I, conf: ValidatorConfiguration) => O; +// This is necessary so sync validators don't eat async validators (SyncValidatorFunction> instead of ValidatorFunction) +export type SyncValidatorFunctionInner = O extends Promise ? never : SyncValidatorFunction; +export type AsyncValidatorFunction = (value: I, conf: ValidatorConfiguration) => Promise; +export type ValidatorFunction = SyncValidatorFunctionInner | AsyncValidatorFunction; + +export type SyncValidatorFunctionList1 = [SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList2 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList3 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList4 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList5 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList6 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList7 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList8 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList9 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList10 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList11 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList12 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList13 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList14 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList15 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList16 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList17 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList18 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList19 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList20 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList21 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList22 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList23 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList24 = [SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionInner]; +export type SyncValidatorFunctionList = + | SyncValidatorFunctionList1 + | SyncValidatorFunctionList2 + | SyncValidatorFunctionList3 + | SyncValidatorFunctionList4 + | SyncValidatorFunctionList5 + | SyncValidatorFunctionList6 + | SyncValidatorFunctionList7 + | SyncValidatorFunctionList8 + | SyncValidatorFunctionList9 + | SyncValidatorFunctionList10 + | SyncValidatorFunctionList11 + | SyncValidatorFunctionList12 + | SyncValidatorFunctionList13 + | SyncValidatorFunctionList14 + | SyncValidatorFunctionList15 + | SyncValidatorFunctionList16 + | SyncValidatorFunctionList17 + | SyncValidatorFunctionList18 + | SyncValidatorFunctionList19 + | SyncValidatorFunctionList20 + | SyncValidatorFunctionList21 + | SyncValidatorFunctionList22 + | SyncValidatorFunctionList23 + | SyncValidatorFunctionList24; -export type ValidatorObject = { - [key in keyof T]: Validator; +export type ValidatorFunctionList1 = [ValidatorFunction]; +export type ValidatorFunctionList2 = [ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList3 = [ValidatorFunction, ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList4 = [ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList5 = [ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList6 = [ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList7 = [ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList8 = [ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList9 = [ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList10 = [ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList11 = [ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList12 = [ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList13 = [ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList14 = [ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList15 = [ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList16 = [ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList17 = [ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList18 = [ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList19 = [ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList20 = [ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList21 = [ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList22 = [ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList23 = [ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList24 = [ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction, ValidatorFunction]; +export type ValidatorFunctionList = + | ValidatorFunctionList1 + | ValidatorFunctionList2 + | ValidatorFunctionList3 + | ValidatorFunctionList4 + | ValidatorFunctionList5 + | ValidatorFunctionList6 + | ValidatorFunctionList7 + | ValidatorFunctionList8 + | ValidatorFunctionList9 + | ValidatorFunctionList10 + | ValidatorFunctionList11 + | ValidatorFunctionList12 + | ValidatorFunctionList13 + | ValidatorFunctionList14 + | ValidatorFunctionList15 + | ValidatorFunctionList16 + | ValidatorFunctionList17 + | ValidatorFunctionList18 + | ValidatorFunctionList19 + | ValidatorFunctionList20 + | ValidatorFunctionList21 + | ValidatorFunctionList22 + | ValidatorFunctionList23 + | ValidatorFunctionList24; + +// Value validators are necessary, because we don't have generics in mapped types. +// Validator object values only support the return type of last validator function. +export type SyncValueValidator = SyncValidatorFunctionList1 | SyncValidatorFunctionList2 | SyncValidatorFunctionList3 | SyncValidatorFunctionInner | SyncValidatingFunction | SyncValidatorObject; +export type SyncValidatorObject = { + [key in keyof O]: SyncValueValidator; +}; +export type ValueValidator = ValidatorFunctionList1 | ValidatorFunctionList2 | ValidatorFunctionList3 | ValidatorFunction | ValidatingFunction | ValidatorObject; +export type ValidatorObject = { + [key in keyof O]: ValueValidator; }; -export type Validator = ValidatorObject | ValidatorFunction[] | ValidatorFunction; +export type SyncValidatorInner = + | SyncValidatorFunctionList1 + | SyncValidatorFunctionList2 + | SyncValidatorFunctionList3 + | SyncValidatorFunctionList4 + | SyncValidatorFunctionList5 + | SyncValidatorFunctionList6 + | SyncValidatorFunctionList7 + | SyncValidatorFunctionList8 + | SyncValidatorFunctionList9 + | SyncValidatorFunctionList10 + | SyncValidatorFunctionList11 + | SyncValidatorFunctionList12 + | SyncValidatorFunctionList13 + | SyncValidatorFunctionList14 + | SyncValidatorFunctionList15 + | SyncValidatorFunctionList16 + | SyncValidatorFunctionList17 + | SyncValidatorFunctionList18 + | SyncValidatorFunctionList19 + | SyncValidatorFunctionList20 + | SyncValidatorFunctionList21 + | SyncValidatorFunctionList22 + | SyncValidatorFunctionList23 + | SyncValidatorFunctionList24 + | SyncValidatorFunctionInner + | SyncValidatorObject; +export type ValidatorInner = + | ValidatorFunctionList1 + | ValidatorFunctionList2 + | ValidatorFunctionList3 + | ValidatorFunctionList4 + | ValidatorFunctionList5 + | ValidatorFunctionList6 + | ValidatorFunctionList7 + | ValidatorFunctionList8 + | ValidatorFunctionList9 + | ValidatorFunctionList10 + | ValidatorFunctionList11 + | ValidatorFunctionList12 + | ValidatorFunctionList13 + | ValidatorFunctionList14 + | ValidatorFunctionList15 + | ValidatorFunctionList16 + | ValidatorFunctionList17 + | ValidatorFunctionList18 + | ValidatorFunctionList19 + | ValidatorFunctionList20 + | ValidatorFunctionList21 + | ValidatorFunctionList22 + | ValidatorFunctionList23 + | ValidatorFunctionList24 + | ValidatorFunction + | ValidatorObject; + +export type SyncValidator = SyncValidatorInner; +export type Validator = ValidatorInner; export interface FailureFunction { (v: T, conf: ValidatorConfiguration): string; } -export type InferValidator = T extends Validator ? U : never; - -export const Success = function (t?: T): Promise { - return Promise.resolve(t); -}; - -export const Ignore = function (): Promise { - return Promise.resolve(undefined); -}; - -export const Failure = function (t: string): never { - throw t; -}; +export type InferValidator = T extends ValidatorInner ? O : never; diff --git a/src/validate.ts b/src/validate.ts index d6e7910..1a20c0d 100644 --- a/src/validate.ts +++ b/src/validate.ts @@ -1,97 +1,195 @@ -import { - Validator, - ValidatorConfiguration, - ValidatorFunction, - ValidatorObject, - ValidatorState, -} from './types.js'; - -const resolveValidatorList = function ( - validators: ValidatorFunction[], - value: any, - conf: ValidatorConfiguration -): Promise { - return validators - .reduce>>((previousPromise, validator) => { - return previousPromise.then(({ value, failures }) => - validator(value, conf).then( - (newValue) => ({ - value: typeof newValue !== 'undefined' ? newValue : value, - failures, - }), +import { AsyncValidatingFunction, SyncValidatingFunction, SyncValidatingFunctionInner, SyncValidatorFunctionInner, SyncValidatorFunctionList, SyncValidatorFunctionList1, SyncValidatorFunctionList10, SyncValidatorFunctionList11, SyncValidatorFunctionList12, SyncValidatorFunctionList13, SyncValidatorFunctionList14, SyncValidatorFunctionList15, SyncValidatorFunctionList16, SyncValidatorFunctionList17, SyncValidatorFunctionList18, SyncValidatorFunctionList19, SyncValidatorFunctionList2, SyncValidatorFunctionList20, SyncValidatorFunctionList21, SyncValidatorFunctionList22, SyncValidatorFunctionList23, SyncValidatorFunctionList24, SyncValidatorFunctionList3, SyncValidatorFunctionList4, SyncValidatorFunctionList5, SyncValidatorFunctionList6, SyncValidatorFunctionList7, SyncValidatorFunctionList8, SyncValidatorFunctionList9, SyncValidatorObject, ValidatingFunction, ValidatorInner, ValidatorConfiguration, ValidatorFunction, ValidatorFunctionList, ValidatorFunctionList1, ValidatorFunctionList10, ValidatorFunctionList11, ValidatorFunctionList12, ValidatorFunctionList13, ValidatorFunctionList14, ValidatorFunctionList15, ValidatorFunctionList16, ValidatorFunctionList17, ValidatorFunctionList18, ValidatorFunctionList19, ValidatorFunctionList2, ValidatorFunctionList20, ValidatorFunctionList21, ValidatorFunctionList22, ValidatorFunctionList23, ValidatorFunctionList24, ValidatorFunctionList3, ValidatorFunctionList4, ValidatorFunctionList5, ValidatorFunctionList6, ValidatorFunctionList7, ValidatorFunctionList8, ValidatorFunctionList9, ValidatorObject, ValidatorState } from './types.js'; + +function resolveValidatorList(validators: SyncValidatorFunctionList, value: any, conf: ValidatorConfiguration): O; +function resolveValidatorList(validators: ValidatorFunctionList, value: any, conf: ValidatorConfiguration): Promise; +function resolveValidatorList(validators: ValidatorFunctionList, value: any, conf: ValidatorConfiguration): O | Promise { + // TODO: we should bail by default + const result = (validators as ValidatorFunction[]).reduce | Promise>>( + (previousState, validator) => { + // TODO: refactor this to a unified function + if (isPromise(previousState)) { + return previousState.then(({ value, failures }) => { + const result: unknown = validator(value, conf); + if (isPromise(result)) { + return result.then( + (newValue) => { + return { + value: newValue, + failures, + }; + }, + (failure) => ({ + value, + failures: failures.concat(failure), + }) + ); + } + return { + value: result, + failures: failures, + }; + }); + } + const result: unknown = validator(value, conf); + if (isPromise(result)) { + return result.then( + (newValue) => { + return { + value: newValue, + failures: previousState.failures, + }; + }, (failure) => ({ value, - failures: failures.concat(failure), + failures: previousState.failures.concat(failure), }) - ) - ); - }, Promise.resolve({ value, failures: [] })) - .then(({ value, failures }) => { + ); + } + return { + value: result, + failures: previousState.failures, + }; + }, + { value, failures: [] } as ValidatorState + ); + + if (isPromise(result)) { + return result.then(({ value, failures }) => { if (failures.length > 0) { throw failures; } - return value; + return value as O; }); -}; + } -const resolveValidatorObject = function ( - validator: ValidatorObject, - testValue: any, - conf: ValidatorConfiguration -): Promise { - const keys = Object.keys(validator) as (keyof T)[]; - return Promise.all( - keys.map>>((i) => - validate(validator[i], { - ...conf, - name: i as string, - path: conf.path.concat(i as string), - parent: testValue, - })(testValue[i]).then( - (value) => ({ value: [i, value], failures: [] }), - (failures) => ({ value: [i, testValue[i]], failures }) - ) - ) - ).then((results) => { - let sanitizedObject: { [key in keyof T]?: T[key] } = {}; - let validationFailures: string[] = []; - - results.forEach((result) => { - if (result.failures.length > 0) { - validationFailures = validationFailures.concat(result.failures); - } else { - if (typeof result.value[1] !== 'undefined') { - sanitizedObject[result.value[0]] = result.value[1]; - } - } - }); + // TODO: handles multiple failures + if (result.failures.length > 0) { + throw result.failures; + } + return result.value as O; +} - if (validationFailures.length > 0) { - throw validationFailures; +function resolveValidatorObject(validator: SyncValidatorObject, testValue: any, conf: ValidatorConfiguration): O; +function resolveValidatorObject(validator: ValidatorObject, testValue: any, conf: ValidatorConfiguration): Promise; +function resolveValidatorObject(validator: ValidatorObject, testValue: any, conf: ValidatorConfiguration): O | Promise { + const keys = Object.keys(validator) as (keyof O)[]; + const results = keys.map((key) => { + const result = validate(validator[key as keyof O] as any, { + ...conf, + name: key as string, + path: conf.path.concat(key as string), + parent: testValue, + })(testValue[key]); + if (isPromise(result)) { + return result.then( + (value) => + ({ + value: [key, value], + failures: [], + } as ValidatorState<[keyof O, O[keyof O]]>), + (failure) => ({ value: [key, testValue[key]], failures: Array.isArray(failure) ? failure : [failure] } as ValidatorState<[keyof O, O[keyof O]]>) + ); } - return sanitizedObject as T; + return { + value: [key, result] as [keyof O, O[keyof O]], + failures: [], + } as ValidatorState<[keyof O, O[keyof O]]>; }); + + if (!hasNoPromise(results)) { + return Promise.all(results).then((results) => { + const failures = results.flatMap((result) => result.failures); + if (failures.length > 0) { + throw failures; + } + return Object.fromEntries(results.map(({ value }) => value)) as O; + }); + } + + const failures = results.flatMap((result) => result.failures); + if (failures.length > 0) { + throw failures; + } + return Object.fromEntries(results.map(({ value }) => value)) as O; +} + +export const isPromise = (value: any): value is Promise => { + return typeof value === 'object' && value !== null && typeof value.then === 'function'; }; -const isValidatorFunctionList = ( - validator: Validator -): validator is ValidatorFunction[] => { +export const hasNoPromise = (value: (T | Promise)[]): value is T[] => { + return !value.some(isPromise); +}; + +const isValidatorFunctionList = (validator: ValidatorInner): validator is ValidatorFunctionList => { return typeof validator === 'object' && Array.isArray(validator); }; -const isValidatorObject = (validator: Validator): validator is ValidatorObject => { +const isValidatorObject = (validator: ValidatorInner): validator is ValidatorObject => { return typeof validator === 'object' && !Array.isArray(validator); }; -const isValidatorFunction = (validator: Validator): validator is ValidatorFunction => { +const isValidatorFunction = (validator: ValidatorInner): validator is ValidatorFunction => { return typeof validator === 'function'; }; -const validate = function ( - validator: Validator, - validateConf?: Partial -) { - return function (testValue: any, testConf?: Partial): Promise { +export function validate(v: SyncValidatorFunctionInner, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatingFunctionInner, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList1, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList2, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList3, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList4, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList5, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList6, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList7, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList8, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList9, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList10, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList11, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList12, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList13, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList14, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList15, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList16, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList17, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList18, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList19, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList20, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList21, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList22, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList23, c?: Partial): SyncValidatingFunction; +export function validate(v: SyncValidatorFunctionList24, c?: Partial): SyncValidatingFunction +export function validate(v: ValidatorFunction, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatingFunction, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList1, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList2, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList3, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList4, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList5, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList6, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList7, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList8, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList9, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList10, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList11, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList12, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList13, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList14, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList15, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList16, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList17, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList18, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList19, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList20, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList21, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList22, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList23, c?: Partial): AsyncValidatingFunction; +export function validate(v: ValidatorFunctionList24, c?: Partial): AsyncValidatingFunction +export function validate(v: SyncValidatorObject, c?: Partial): SyncValidatingFunction; +export function validate(v: ValidatorObject, c?: Partial): AsyncValidatingFunction; +export function validate(validator: ValidatorInner, validateConf?: Partial): ValidatingFunction; +export function validate(validator: ValidatorInner, validateConf?: Partial): ValidatingFunction { + return function (testValue: any, testConf) { // Set defaults to configuration const conf: ValidatorConfiguration = { name: 'object', @@ -116,25 +214,31 @@ const validate = function ( } else if (isValidatorFunction(validator)) { // It is a function, validate with it try { - return validator(testValue, conf) - .then((newValue) => (typeof newValue !== 'undefined' ? newValue : testValue)) - .catch((failures) => { + const result = validator(testValue, conf); + if (isPromise(result)) { + return result.catch((failures) => { if (Array.isArray(failures)) { return Promise.reject(failures); } return Promise.reject([failures]); }); + } + return result; + // TODO: For some reason, without this it won't compile ??? + return result !== 'undefined' ? result : testValue; } catch (failures) { if (Array.isArray(failures)) { - return Promise.reject(failures); + throw failures; } - return Promise.reject([failures]); + throw [failures]; } } else { // Shouldn't go on here throw new Error('Validator should be an array, object or function.'); } }; -}; +} + +// TODO: add identity transform function export default validate; diff --git a/src/validators/arrayOf.ts b/src/validators/arrayOf.ts index f0ca83f..92c3e84 100644 --- a/src/validators/arrayOf.ts +++ b/src/validators/arrayOf.ts @@ -1,37 +1,65 @@ -import validate from '../validate.js'; -import { Validator, ValidatorFunction, ValidatorState } from '../types.js'; +import validate, { hasNoPromise, isPromise } from '../validate.js'; +import { + ValidatorState, + ValidatorConfiguration, + SyncValidatingFunction, + AsyncValidatingFunction, + SyncValidator, + Validator, +} from '../types.js'; -const arrayOf = - (validator: Validator): ValidatorFunction => - (testValues, conf) => { +function arrayOf(validator: SyncValidator): SyncValidatingFunction; +function arrayOf(validator: Validator): AsyncValidatingFunction; +function arrayOf( + validator: Validator +): (val: I[], c: ValidatorConfiguration) => O[] | Promise { + return (testValues, conf) => { // Array of one item should use that validation to every item in the array if (typeof testValues !== 'object' || !Array.isArray(testValues)) { throw `Field ${conf.name} should be an array.`; } - return Promise.all( - testValues.map>>((testValue, i) => - validate(validator, { - ...conf, - name: `${conf.name}[${i}]`, - path: conf.path.concat(i.toString()), - parent: testValues, - })(testValue).then( - (value) => ({ value, failures: [] }), - (failures) => ({ value: testValue, failures }) - ) - ) - ).then((results) => { - let validationFailures = results.reduce( - (previousFailures, { failures }) => previousFailures.concat(failures), - [] - ); - if (validationFailures.length > 0) { - throw validationFailures; + const results = testValues.map | Promise>>( + (testValue, i) => { + try { + const result = validate(validator, { + ...conf, + name: `${conf.name}[${i}]`, + path: conf.path.concat(i.toString()), + parent: testValues, + })(testValue); + + if (isPromise(result)) { + return result.then( + (value) => ({ value, failures: [] }), + (failures) => ({ value: testValue as any, failures }) + ); + } + + // TODO: handles multiple failures + return { value: result, failures: [] }; + } catch (failure) { + return { value: testValue as any, failures: failure as string[] }; + } } + ); - return results.map(({ value }) => value) as T[]; - }); + if (!hasNoPromise(results)) { + return Promise.all(results).then((results) => { + const failures = results.flatMap((result) => result.failures); + if (failures.length > 0) { + throw failures; + } + return results.map(({ value }) => value); + }); + } + + const failures = results.flatMap((result) => result.failures); + if (failures.length > 0) { + throw failures; + } + return results.map(({ value }) => value); }; +} export default arrayOf; diff --git a/src/validators/bail.ts b/src/validators/bail.ts index f977632..6cae227 100644 --- a/src/validators/bail.ts +++ b/src/validators/bail.ts @@ -1,35 +1,92 @@ -import { ValidatorFunction, ValidatorState } from '../types.js'; -import validate from '../validate.js'; +import { SyncValidatorFunctionList, ValidatorConfiguration, ValidatorFunction, ValidatorFunctionList, ValidatorState } from '../types.js'; +import validate, { isPromise } from '../validate.js'; -const bail = (validators: ValidatorFunction[]): ValidatorFunction => { +// TODO: replace this with parallel validation +function bail(validators: SyncValidatorFunctionList): (val: I, c: ValidatorConfiguration) => O; +function bail(validators: ValidatorFunctionList): (val: I, c: ValidatorConfiguration) => Promise; +function bail(validators: ValidatorFunctionList): (val: I, c: ValidatorConfiguration) => O | Promise { if (!Array.isArray(validators)) { // If not an array is passed, simply continue validation return validate(validators); } return (value, conf) => { - return validators - .reduce>>((previousPromise, validator) => { - return previousPromise.then(({ value, failures }) => - failures.length > 0 - ? { value, failures } // Short-circuit for any error - : validator(value, conf).then( - (newValue) => ({ - value: typeof newValue !== 'undefined' ? newValue : value, - failures, - }), - (failure) => ({ - value, - failures: failures.concat(failure), - }) - ) - ); - }, Promise.resolve({ value, failures: [] })) - .then(({ value, failures }) => { + const result = (validators as ValidatorFunction[]).reduce | Promise>>( + (previousState, validator) => { + if (isPromise(previousState)) { + return previousState.then(({ value, failures }) => { + if (failures.length > 0) { + return previousState; + } + try { + const result = validator(value, conf); + if (isPromise(result)) { + return result.then( + (newValue) => ({ + value: newValue, + failures, + }), + (failure) => ({ + value, + failures: failures.concat(failure), + }) + ); + } + return { + value: result, + failures: failures, + }; + } catch (err) { + return { + value, + failures: failures.concat(err as string[]), + }; + } + }); + } + if (previousState.failures.length > 0) { + return previousState; + } + try { + const result = validator(value, conf); + if (isPromise(result)) { + return result.then( + (newValue) => ({ + value: newValue, + failures: previousState.failures, + }), + (failure) => ({ + value, + failures: previousState.failures.concat(failure), + }) + ); + } + return { + value: result, + failures: previousState.failures, + }; + } catch (err) { + return { + value, + failures: previousState.failures.concat(err as string[]), + }; + } + }, + { value, failures: [] } as ValidatorState + ); + + if (isPromise(result)) { + return result.then(({ value, failures }) => { if (failures.length > 0) { throw failures; } - return value; + return value as O; }); + } + + if (result.failures.length > 0) { + throw result.failures; + } + return result.value as O; }; -}; +} export default bail; diff --git a/src/validators/boolean/falsey.ts b/src/validators/boolean/falsey.ts index d9ab96d..f01c854 100644 --- a/src/validators/boolean/falsey.ts +++ b/src/validators/boolean/falsey.ts @@ -1,7 +1,7 @@ -import { ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; import validateCondition from '../validateCondition.js'; -const falsey = (): ValidatorFunction => +const falsey = (): SyncValidatorFunction => validateCondition( (value) => !value, (_, conf) => `Field ${conf.name} should be falsey.` diff --git a/src/validators/boolean/isBool.ts b/src/validators/boolean/isBool.ts index 3b96144..1243c6f 100644 --- a/src/validators/boolean/isBool.ts +++ b/src/validators/boolean/isBool.ts @@ -1,7 +1,7 @@ -import { ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; import validateCondition from '../validateCondition.js'; -const isBool = (): ValidatorFunction => +const isBool = (): SyncValidatorFunction => validateCondition( (v) => v === true || v === false, (_, conf) => `Field ${conf.name} should be a boolean.` diff --git a/src/validators/boolean/toBool.ts b/src/validators/boolean/toBool.ts index 2204266..9e7130e 100644 --- a/src/validators/boolean/toBool.ts +++ b/src/validators/boolean/toBool.ts @@ -1,7 +1,7 @@ -import { Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; -const toBool = (): ValidatorFunction => (value) => { - return Success(!!value); +const toBool = (): SyncValidatorFunction => (value) => { + return !!value; }; export default toBool; diff --git a/src/validators/boolean/truthy.ts b/src/validators/boolean/truthy.ts index aa5e38d..ea2e64e 100644 --- a/src/validators/boolean/truthy.ts +++ b/src/validators/boolean/truthy.ts @@ -1,7 +1,7 @@ -import { ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; import validateCondition from '../validateCondition.js'; -const truthy = (): ValidatorFunction => +const truthy = (): SyncValidatorFunction => validateCondition( (val) => !!val, (_, conf) => `Field ${conf.name} should be truthy.` diff --git a/src/validators/boolean/tryBool.ts b/src/validators/boolean/tryBool.ts index d4f0adf..bfdec05 100644 --- a/src/validators/boolean/tryBool.ts +++ b/src/validators/boolean/tryBool.ts @@ -1,15 +1,15 @@ -import { Failure, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; -const tryBool: (t?: any[], f?: any[]) => ValidatorFunction = +const tryBool: (t?: any[], f?: any[]) => SyncValidatorFunction = (truthy = [true, 1], falsey = [false, 0]) => (value, conf) => { if (truthy.indexOf(value) !== -1) { - return Success(true); + return true; } if (falsey.indexOf(value) !== -1) { - return Success(false); + return false; } - throw Failure(`Field ${conf.name} cannot be converted to boolean.`); + throw `Field ${conf.name} cannot be converted to boolean.`; }; export default tryBool; diff --git a/src/validators/date/after.ts b/src/validators/date/after.ts index ecc9f6b..ff4f133 100644 --- a/src/validators/date/after.ts +++ b/src/validators/date/after.ts @@ -1,21 +1,18 @@ -import { Failure, Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; const after: ( date: string | number | Date, opts?: { inclusive: boolean } -) => ValidatorFunction = +) => SyncValidatorFunction = (date, opts = { inclusive: false }) => (value, conf) => { - if (!(value instanceof Date)) { - return Ignore(); - } - const min = date instanceof Date ? date : new Date(date); + if ((opts.inclusive && value >= min) || (!opts.inclusive && value > min)) { - return Success(); + return value; } - return Failure(`Field ${conf.name} should be after ${min.toString()}.`); + throw `Field ${conf.name} should be after ${min.toString()}.` }; export default after; diff --git a/src/validators/date/before.ts b/src/validators/date/before.ts index 9bc986f..4d740b5 100644 --- a/src/validators/date/before.ts +++ b/src/validators/date/before.ts @@ -1,22 +1,18 @@ -import { Failure, Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; const before: ( date: string | number | Date, opts?: { inclusive?: boolean } -) => ValidatorFunction = - (date, opts = { inclusive: false }): ValidatorFunction => +) => SyncValidatorFunction = + (date, opts = { inclusive: false }) => (value, conf) => { - if (!(value instanceof Date)) { - return Ignore(); - } - const max = date instanceof Date ? date : new Date(date); if ((opts.inclusive && value <= max) || (!opts.inclusive && value < max)) { - return Success(); + return value; } - return Failure(`Field ${conf.name} should be before ${max.toString()}.`); + throw `Field ${conf.name} should be before ${max.toString()}.` }; export default before; diff --git a/src/validators/date/inRange.ts b/src/validators/date/inRange.ts index 7af2742..54401d4 100644 --- a/src/validators/date/inRange.ts +++ b/src/validators/date/inRange.ts @@ -1,20 +1,12 @@ -import { Failure, Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; const inRange: ( minDate: string | number | Date, maxDate: string | number | Date, opts?: { minInclusive?: boolean; maxInclusive?: boolean } -) => ValidatorFunction = - ( - minDate, - maxDate, - opts = { minInclusive: false, maxInclusive: false } - ): ValidatorFunction => +) => SyncValidatorFunction = + (minDate, maxDate, opts = { minInclusive: false, maxInclusive: false }) => (value, conf) => { - if (!(value instanceof Date)) { - return Ignore(); - } - const min = minDate instanceof Date ? minDate : new Date(minDate); const max = maxDate instanceof Date ? maxDate : new Date(maxDate); @@ -22,12 +14,10 @@ const inRange: ( ((opts.minInclusive && value >= min) || (!opts.minInclusive && value > min)) && ((opts.maxInclusive && value <= max) || (!opts.maxInclusive && value < max)) ) { - return Success(); + return value; } - return Failure( - `Field ${conf.name} should be between ${min.toString()} and ${max.toString()}.` - ); + throw `Field ${conf.name} should be between ${min.toString()} and ${max.toString()}.`; }; export default inRange; diff --git a/src/validators/date/isDate.ts b/src/validators/date/isDate.ts index 2b87515..74ff74a 100644 --- a/src/validators/date/isDate.ts +++ b/src/validators/date/isDate.ts @@ -1,7 +1,7 @@ -import { ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; import validateCondition from '../validateCondition.js'; -const isDate = (): ValidatorFunction => +const isDate = (): SyncValidatorFunction => validateCondition( (value) => value instanceof Date && !Number.isNaN(value.getTime()), (_, conf) => `Field ${conf.name} should be a valid date.` diff --git a/src/validators/date/toDate.ts b/src/validators/date/toDate.ts index f6ed307..f146c67 100644 --- a/src/validators/date/toDate.ts +++ b/src/validators/date/toDate.ts @@ -1,8 +1,8 @@ -import { Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; -const toDate = (): ValidatorFunction => (value, conf) => { +const toDate = (): SyncValidatorFunction => (value, conf) => { const parsed = value instanceof Date ? value : new Date(value); - return Success(parsed); + return parsed; }; export default toDate; diff --git a/src/validators/date/tryDate.ts b/src/validators/date/tryDate.ts index 2c66409..6194f45 100644 --- a/src/validators/date/tryDate.ts +++ b/src/validators/date/tryDate.ts @@ -1,11 +1,11 @@ -import { Failure, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; -const tryDate = (): ValidatorFunction => (value, conf) => { +const tryDate = (): SyncValidatorFunction => (value, conf) => { const parsed = value instanceof Date ? value : new Date(value); if (!Number.isNaN(parsed.getTime())) { - return Success(parsed); + return parsed; } - return Failure(`Field ${conf.name} should be a valid date.`); + throw `Field ${conf.name} should be a valid date.` }; export default tryDate; diff --git a/src/validators/either.ts b/src/validators/either.ts index 9c6e840..18e4b57 100644 --- a/src/validators/either.ts +++ b/src/validators/either.ts @@ -1,27 +1,71 @@ -import { InferValidator, Success, Validator, ValidatorFunction } from '../types.js'; -import validate from '../validate.js'; +import { + SyncValidatorFunctionInner, + SyncValidator, + ValidatorConfiguration, + ValidatorFunction, + ValidatorState, + Validator, +} from '../types.js'; +import validate, { hasNoPromise, isPromise } from '../validate.js'; -const either = - >(validators: V[]): ValidatorFunction> => - (value, conf) => { - return Promise.allSettled( - validators.map( - (validator) => validate(validator, conf)(value, conf) as InferValidator - ) - ).then((results) => { - const fulfilledPromise = results.find( - (result: PromiseSettledResult): result is PromiseFulfilledResult => - result.status === 'fulfilled' - ); - if (fulfilledPromise) { - return Success(fulfilledPromise.value); +function either( + validators: [SyncValidator, SyncValidator] +): SyncValidatorFunctionInner; +function either( + validators: [Validator, Validator] +): ValidatorFunction; +function either( + validators: [Validator, Validator] +): (val: unknown, c: ValidatorConfiguration) => O1 | O2 | Promise { + return (value, conf) => { + const results = validators.map((validator) => { + try { + const result = validate(validator as Validator, conf)( + value, + conf + ) as O1 | O2 | Promise; + if (isPromise(result)) { + return result.then( + (value) => + ({ + value, + failures: [], + } as ValidatorState), + (failure) => + ({ + value, + failures: failure, + } as ValidatorState) + ); } - const rejectedPromises = results.filter( - (result: PromiseSettledResult): result is PromiseRejectedResult => - result.status === 'rejected' - ); - throw rejectedPromises.flatMap((result) => result.reason); + return { + value: result, + failures: [], + } as ValidatorState; + } catch (err) { + return { + value, + failures: err, + } as ValidatorState; + } + }); + + if (!hasNoPromise(results)) { + return Promise.all(results).then((results) => { + const successfulResult = results.find((result) => result.failures.length === 0); + if (typeof successfulResult === 'undefined') { + throw results.flatMap((result) => result.failures); + } + return successfulResult.value; }); - }; + } + + const successfulResult = results.find((result) => result.failures.length === 0); + if (typeof successfulResult === 'undefined') { + throw results.flatMap((result) => result.failures); + } + return successfulResult.value; + }; +} export default either; diff --git a/src/validators/equals.ts b/src/validators/equals.ts index b23c409..9ffbaa3 100644 --- a/src/validators/equals.ts +++ b/src/validators/equals.ts @@ -1,7 +1,7 @@ -import { ValidatorFunction } from '../types.js'; +import { SyncValidatorFunction } from '../types.js'; import validateCondition from './validateCondition.js'; -const equals = (to: T): ValidatorFunction => +const equals = (to: T): SyncValidatorFunction => validateCondition( (value) => value === to, (_, conf) => `Field ${conf.name} should be equal to ${to}.` diff --git a/src/validators/number/between.ts b/src/validators/number/between.ts index 50335c6..1d26d1b 100644 --- a/src/validators/number/between.ts +++ b/src/validators/number/between.ts @@ -1,24 +1,20 @@ -import { Failure, Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; const between: ( min: number, max: number, opts?: { minInclusive?: boolean; maxInclusive?: boolean } -) => ValidatorFunction = +) => SyncValidatorFunction = (min, max, opts = { minInclusive: false, maxInclusive: false }) => (value, conf) => { - if (typeof value !== 'number' || Number.isNaN(value)) { - return Ignore(); - } - if ( ((opts.minInclusive && value >= min) || (!opts.minInclusive && value > min)) && ((opts.maxInclusive && value <= max) || (!opts.maxInclusive && value < max)) ) { - return Success(); + return value; } - return Failure(`Field ${conf.name} should be between ${min} and ${max}.`); + throw `Field ${conf.name} should be between ${min} and ${max}.` }; export default between; diff --git a/src/validators/number/closeTo.ts b/src/validators/number/closeTo.ts index 2e3592d..7eee39d 100644 --- a/src/validators/number/closeTo.ts +++ b/src/validators/number/closeTo.ts @@ -1,15 +1,12 @@ -import { Failure, Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; -const closeTo: (v: number, e?: number) => ValidatorFunction = +const closeTo: (v: number, e?: number) => SyncValidatorFunction = (exactValue, epsilon = Number.EPSILON) => (value, conf) => { - if (typeof value !== 'number') { - return Ignore(); - } if (Math.abs(value - exactValue) < epsilon) { - return Success(exactValue); + return exactValue; } - return Failure(`Field ${conf.name} should be approximately ${exactValue}.`); + throw `Field ${conf.name} should be approximately ${exactValue}.` }; export default closeTo; diff --git a/src/validators/number/gt.ts b/src/validators/number/gt.ts index 7255c7f..78c9e27 100644 --- a/src/validators/number/gt.ts +++ b/src/validators/number/gt.ts @@ -1,17 +1,13 @@ -import { Failure, Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; const gt = - (min: number): ValidatorFunction => + (min: number): SyncValidatorFunction => (value, conf) => { - if (typeof value !== 'number' || Number.isNaN(value)) { - return Ignore(); - } - if (value > min) { - return Success(); + return value; } - return Failure(`Field ${conf.name} should be greater than ${min}.`); + throw `Field ${conf.name} should be greater than ${min}.` }; export default gt; diff --git a/src/validators/number/gte.ts b/src/validators/number/gte.ts index d658c3b..f8cc86c 100644 --- a/src/validators/number/gte.ts +++ b/src/validators/number/gte.ts @@ -1,17 +1,13 @@ -import { Failure, Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; const gte = - (min: number): ValidatorFunction => + (min: number): SyncValidatorFunction => (value, conf) => { - if (typeof value !== 'number' || Number.isNaN(value)) { - return Ignore(); - } - if (value >= min) { - return Success(); + return value; } - return Failure(`Field ${conf.name} should be greater than or equal to ${min}.`); + throw `Field ${conf.name} should be greater than or equal to ${min}.` }; export default gte; diff --git a/src/validators/number/isInt.ts b/src/validators/number/isInt.ts index 889bc52..b89625b 100644 --- a/src/validators/number/isInt.ts +++ b/src/validators/number/isInt.ts @@ -1,7 +1,7 @@ -import { Failure, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; import validateCondition from '../validateCondition.js'; -const isInt = (): ValidatorFunction => +const isInt = (): SyncValidatorFunction => validateCondition( (value) => Number.isInteger(value), (_, conf) => `Field ${conf.name} should be an integer.` diff --git a/src/validators/number/isMultipleOf.ts b/src/validators/number/isMultipleOf.ts index 3e34f4b..82e71ee 100644 --- a/src/validators/number/isMultipleOf.ts +++ b/src/validators/number/isMultipleOf.ts @@ -1,17 +1,13 @@ -import { Failure, Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; const isMultipleOf = - (n: number): ValidatorFunction => + (n: number): SyncValidatorFunction => (value, conf) => { - if (typeof value !== 'number' || Number.isNaN(value)) { - return Ignore(); - } - if (value % n === 0) { - return Success(); + return value; } - return Failure(`Field ${conf.name} should be the multiple of ${n}.`); + throw `Field ${conf.name} should be the multiple of ${n}.` }; export default isMultipleOf; diff --git a/src/validators/number/isNumber.ts b/src/validators/number/isNumber.ts index 98378ab..1d7f7f6 100644 --- a/src/validators/number/isNumber.ts +++ b/src/validators/number/isNumber.ts @@ -1,7 +1,7 @@ -import { ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; import validateCondition from '../validateCondition.js'; -const isNumber = (): ValidatorFunction => +const isNumber = (): SyncValidatorFunction => validateCondition( (value) => typeof value === 'number' && !Number.isNaN(value), (_, conf) => `Field ${conf.name} should be a number.` diff --git a/src/validators/number/lt.ts b/src/validators/number/lt.ts index 4cc5f05..d0e4c32 100644 --- a/src/validators/number/lt.ts +++ b/src/validators/number/lt.ts @@ -1,17 +1,13 @@ -import { Failure, Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; const lt = - (max: number): ValidatorFunction => + (max: number): SyncValidatorFunction => (value, conf) => { - if (typeof value !== 'number' || Number.isNaN(value)) { - return Ignore(); - } - if (value < max) { - return Success(); + return value; } - return Failure(`Field ${conf.name} should be less than ${max}.`); + throw `Field ${conf.name} should be less than ${max}.` }; export default lt; diff --git a/src/validators/number/lte.ts b/src/validators/number/lte.ts index 28defd4..64e15ce 100644 --- a/src/validators/number/lte.ts +++ b/src/validators/number/lte.ts @@ -1,17 +1,13 @@ -import { Failure, Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; const lte = - (max: number): ValidatorFunction => + (max: number): SyncValidatorFunction => (value, conf) => { - if (typeof value !== 'number' || Number.isNaN(value)) { - return Ignore(); - } - if (value <= max) { - return Success(); + return value; } - return Failure(`Field ${conf.name} should be less than or equal to ${max}.`); + throw `Field ${conf.name} should be less than or equal to ${max}.` }; export default lte; diff --git a/src/validators/number/toInt.ts b/src/validators/number/toInt.ts index b70dc94..925a66a 100644 --- a/src/validators/number/toInt.ts +++ b/src/validators/number/toInt.ts @@ -1,7 +1,7 @@ -import { Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; -const toInt = (): ValidatorFunction => (value) => { - return Success(Number.parseInt(value as any)); +const toInt = (): SyncValidatorFunction => (value) => { + return Number.parseInt(value as string); }; export default toInt; diff --git a/src/validators/number/toNumber.ts b/src/validators/number/toNumber.ts index 625cd6c..00210a9 100644 --- a/src/validators/number/toNumber.ts +++ b/src/validators/number/toNumber.ts @@ -1,7 +1,7 @@ -import { Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; -const toNumber = (): ValidatorFunction => (value, conf) => { - return Success(Number.parseFloat(value as any)); +const toNumber = (): SyncValidatorFunction => (value, conf) => { + return Number.parseFloat(value as string); }; export default toNumber; diff --git a/src/validators/number/tryInt.ts b/src/validators/number/tryInt.ts index b511887..3cb320c 100644 --- a/src/validators/number/tryInt.ts +++ b/src/validators/number/tryInt.ts @@ -1,11 +1,11 @@ -import { Failure, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; -const tryInt = (): ValidatorFunction => (value, conf) => { - const parsed = Number.parseInt(value as any); +const tryInt = (): SyncValidatorFunction => (value, conf) => { + const parsed = Number.parseInt(value as string); if (!Number.isNaN(parsed)) { - return Success(parsed); + return parsed; } - return Failure(`Field ${conf.name} should be an integer.`); + throw `Field ${conf.name} should be an integer.` }; export default tryInt; diff --git a/src/validators/number/tryNumber.ts b/src/validators/number/tryNumber.ts index c7db4c7..c38af70 100644 --- a/src/validators/number/tryNumber.ts +++ b/src/validators/number/tryNumber.ts @@ -1,11 +1,11 @@ -import { Failure, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; -const tryNumber = (): ValidatorFunction => (value, conf) => { - const parsed = Number.parseFloat(value as any); +const tryNumber = (): SyncValidatorFunction => (value, conf) => { + const parsed = Number.parseFloat(value as string); if (!Number.isNaN(parsed)) { - return Success(parsed); + return parsed; } - return Failure(`Field ${conf.name} should be numeric.`); + throw `Field ${conf.name} should be numeric.` }; export default tryNumber; diff --git a/src/validators/oneOf.ts b/src/validators/oneOf.ts index aef6a16..c0652d9 100644 --- a/src/validators/oneOf.ts +++ b/src/validators/oneOf.ts @@ -1,9 +1,9 @@ -import { ValidatorFunction } from '../types.js'; +import { SyncValidatorFunction } from '../types.js'; import validateCondition from './validateCondition.js'; -const oneOf = (items: T[]): ValidatorFunction => +const oneOf = (items: T[]): SyncValidatorFunction => validateCondition( - (value) => items.indexOf(value) !== -1, + (value) => items.indexOf(value as T) !== -1, (_, conf) => `Field ${conf.name} should be one of ${items.join(', ')}.` ); diff --git a/src/validators/optional.ts b/src/validators/optional.ts index 386620d..24492da 100644 --- a/src/validators/optional.ts +++ b/src/validators/optional.ts @@ -1,13 +1,19 @@ -import validate from '../validate.js'; -import { ValidatorFunction, Validator, Success } from '../types.js'; +import { AsyncValidatingFunction, SyncValidatingFunction, SyncValidator, ValidatorConfiguration, Validator } from '../types.js'; +import validate, { isPromise } from '../validate.js'; -const optional = - (validators: Validator): ValidatorFunction => - (value, conf) => { +function optional(validator: SyncValidator): SyncValidatingFunction; +function optional(validator: Validator): AsyncValidatingFunction; +function optional(validator: Validator): (value: unknown, conf: ValidatorConfiguration) => T | undefined | Promise { + return (value, conf) => { if (typeof value === 'undefined') { - return Success(); + return value; } - return validate(validators, conf)(value); + const result = validate(validator)(value); + if (isPromise(result)) { + return result; + } + return result; }; +} export default optional; diff --git a/src/validators/string/betweenLength.ts b/src/validators/string/betweenLength.ts index 88ce1ba..1110c94 100644 --- a/src/validators/string/betweenLength.ts +++ b/src/validators/string/betweenLength.ts @@ -1,20 +1,14 @@ -import { Failure, Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; const betweenLength = - (min: number, max: number): ValidatorFunction => + (min: number, max: number): SyncValidatorFunction => (value, conf) => { - if (typeof value !== 'string') { - return Ignore(); - } - const minLength = Math.min(min, max); const maxLength = Math.max(min, max); if (minLength <= value.length && value.length <= maxLength) { - return Success(); + return value; } - return Failure( - `Field ${conf.name} length should be between ${minLength} and ${maxLength} characters.` - ); + throw `Field ${conf.name} length should be between ${minLength} and ${maxLength} characters.`; }; export default betweenLength; diff --git a/src/validators/string/insensitiveEquals.ts b/src/validators/string/insensitiveEquals.ts index d56de03..15e8f2f 100644 --- a/src/validators/string/insensitiveEquals.ts +++ b/src/validators/string/insensitiveEquals.ts @@ -1,15 +1,12 @@ -import { Failure, Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; const insensitiveEquals = - (to: string): ValidatorFunction => + (to: string): SyncValidatorFunction => (value, conf) => { - if (typeof value !== 'string') { - return Ignore(); - } if (value.toLocaleLowerCase() === to.toLocaleLowerCase()) { - return Success(); + return value; } - return Failure(`Field ${conf.name} should be equal to ${to}.`); + throw `Field ${conf.name} should be equal to ${to}.` }; export default insensitiveEquals; diff --git a/src/validators/string/isEmail.ts b/src/validators/string/isEmail.ts index be327e2..80e2348 100644 --- a/src/validators/string/isEmail.ts +++ b/src/validators/string/isEmail.ts @@ -1,7 +1,7 @@ -import { ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; import validateRegex from '../validateRegex.js'; -const isEmail = (): ValidatorFunction => +const isEmail = (): SyncValidatorFunction => validateRegex( /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/, (_, { name }) => `Field ${name} should be an email.` diff --git a/src/validators/string/isString.ts b/src/validators/string/isString.ts index c2c57ad..a875ef7 100644 --- a/src/validators/string/isString.ts +++ b/src/validators/string/isString.ts @@ -1,7 +1,7 @@ -import { ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; import validateCondition from '../validateCondition.js'; -const isString = (): ValidatorFunction => +const isString = (): SyncValidatorFunction => validateCondition( (value) => typeof value === 'string', (_, conf) => `Field ${conf.name} should be string.` diff --git a/src/validators/string/isUrl.ts b/src/validators/string/isUrl.ts index 7576239..5e89837 100644 --- a/src/validators/string/isUrl.ts +++ b/src/validators/string/isUrl.ts @@ -1,16 +1,11 @@ -import { Failure, Ignore, Success, ValidatorFunction } from '../../types.js'; -import validateRegex from '../validateRegex.js'; - -const isUrl = (): ValidatorFunction => (value, conf) => { - if (typeof value !== 'string') { - return Ignore(); - } +import { SyncValidatorFunction } from '../../types.js'; +const isUrl = (): SyncValidatorFunction => (value, conf) => { try { - const url = new URL(value); - return Success(); + new URL(value); + return value; } catch { - return Failure(`Field ${conf.name} should be an url.`); + throw `Field ${conf.name} should be an url.` } }; export default isUrl; diff --git a/src/validators/string/length.ts b/src/validators/string/length.ts index 77a7380..43fa217 100644 --- a/src/validators/string/length.ts +++ b/src/validators/string/length.ts @@ -1,16 +1,12 @@ -import { Failure, Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; const length = - (n: number): ValidatorFunction => + (n: number): SyncValidatorFunction => (value, conf) => { - if (typeof value !== 'string') { - return Ignore(); - } - if (value.length === n) { - return Success(); + return value; } - return Failure(`Field ${conf.name} length should be exactly ${n} characters.`); + throw `Field ${conf.name} length should be exactly ${n} characters.` }; export default length; diff --git a/src/validators/string/maxLength.ts b/src/validators/string/maxLength.ts index 6afedc0..e39e041 100644 --- a/src/validators/string/maxLength.ts +++ b/src/validators/string/maxLength.ts @@ -1,16 +1,12 @@ -import { Failure, Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; const maxLength = - (max: number): ValidatorFunction => + (max: number): SyncValidatorFunction => (value, conf) => { - if (typeof value !== 'string') { - return Ignore(); - } - if (value.length <= max) { - return Success(); + return value; } - return Failure(`Field ${conf.name} length should be at most ${max} characters.`); + throw `Field ${conf.name} length should be at most ${max} characters.` }; export default maxLength; diff --git a/src/validators/string/minLength.ts b/src/validators/string/minLength.ts index 859dd04..e665980 100644 --- a/src/validators/string/minLength.ts +++ b/src/validators/string/minLength.ts @@ -1,16 +1,12 @@ -import { Failure, Ignore, Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; const minLength = - (min: number): ValidatorFunction => + (min: number): SyncValidatorFunction => (value, conf) => { - if (typeof value !== 'string') { - return Ignore(); - } - if (value.length >= min) { - return Success(); + return value; } - return Failure(`Field ${conf.name} length should be at least ${min} characters.`); + throw `Field ${conf.name} length should be at least ${min} characters.` }; export default minLength; diff --git a/src/validators/string/toString.ts b/src/validators/string/toString.ts index 5cb10c2..d33cb6e 100644 --- a/src/validators/string/toString.ts +++ b/src/validators/string/toString.ts @@ -1,7 +1,7 @@ -import { Success, ValidatorFunction } from '../../types.js'; +import { SyncValidatorFunction } from '../../types.js'; -const toString = (): ValidatorFunction => (value, conf) => { - return Success('' + value); +const toString = (): SyncValidatorFunction => (value, conf) => { + return '' + value; }; export default toString; diff --git a/src/validators/validateCondition.ts b/src/validators/validateCondition.ts index 943b499..37747dd 100644 --- a/src/validators/validateCondition.ts +++ b/src/validators/validateCondition.ts @@ -1,26 +1,48 @@ import { - Failure, FailureFunction, - Success, ValidatorConfiguration, - ValidatorFunction, + AsyncValidatorFunction, + SyncValidatorFunctionInner, } from '../types.js'; +import { isPromise } from '../validate.js'; -interface ConditionFunction { - (v: T, conf: ValidatorConfiguration): boolean; +interface SyncConditionFunction { + (v: unknown, conf: ValidatorConfiguration): boolean; } +interface AsyncConditionFunction { + (v: unknown, conf: ValidatorConfiguration): Promise; +} +type ConditionFunction = SyncConditionFunction | AsyncConditionFunction; -const validateCondition = ( +function validateCondition( + condition: SyncConditionFunction, + errorMsg?: FailureFunction +): SyncValidatorFunctionInner; +function validateCondition( + condition: AsyncConditionFunction, + errorMsg?: FailureFunction +): AsyncValidatorFunction; +function validateCondition( condition: ConditionFunction, - errorMsg: FailureFunction = (_, { name }) => `Field ${name} format is invalid.` -): ValidatorFunction => { + errorMsg: FailureFunction = (_, { name }) => `Field ${name} format is invalid.` +): (value: unknown, conf: ValidatorConfiguration) => T | Promise { return function (field, conf) { - if (condition(field, conf)) { - return Success(); + const result = condition(field, conf); + if (isPromise(result)) { + return result.then((result) => { + if (result) { + return field as T; + } else { + throw errorMsg(field, conf); + } + }); + } + if (result) { + return field as T; } else { - return Failure(errorMsg(field, conf)); + throw errorMsg(field, conf); } }; -}; +} export default validateCondition; diff --git a/src/validators/validateRegex.ts b/src/validators/validateRegex.ts index f9f846e..47c928e 100644 --- a/src/validators/validateRegex.ts +++ b/src/validators/validateRegex.ts @@ -1,17 +1,14 @@ -import { Failure, FailureFunction, Ignore, Success, ValidatorFunction } from '../types.js'; +import { FailureFunction, SyncValidatorFunction } from '../types.js'; const validateRegex = ( regex: RegExp, errorMsg: FailureFunction = (_, { name }) => `Field ${name} format is invalid.` -): ValidatorFunction => { - return function (field, conf) { - if (typeof field !== 'string') { - return Ignore(); - } - if (regex.test(field)) { - return Success(); +): SyncValidatorFunction => { + return function (value, conf) { + if (regex.test(value)) { + return value; } else { - return Failure(errorMsg(field, conf)); + throw errorMsg(value, conf) } }; }; diff --git a/tests/README.ts b/tests/README.ts index 818bb03..3caec04 100644 --- a/tests/README.ts +++ b/tests/README.ts @@ -4,10 +4,7 @@ import validate, { isEmail, minLength, ValidatorFunction, - Success, - Failure, - Ignore, - Validator, + ValidatorObject, } from '../src/index'; test('README: Validation functions', async () => { @@ -22,7 +19,12 @@ test('README: Validation functions', async () => { }, }); - const validatedUser = await validateUser({ + type ExpectedType = { + email: string; + password: string; + address: { city: string; street: string }; + }; + const validatedUser: ExpectedType = validateUser({ email: 'jdoe@example.net', password: 'asdasd69', address: { @@ -37,16 +39,6 @@ test('README: Validation functions', async () => { password: 'asdasd69', address: { city: 'Washington DC', street: 'Pennsylvania Avenue' }, }); - - // Let's check for the expected type with some TypeScript magic - type ExpectedType = { - email: string; - password: string; - address: { city: string; street: string }; - }; - type AssertExpectedType = T extends ExpectedType ? true : never; - // This line shouldn't compile if the type is wrong - const cond1: AssertExpectedType = true; }); test('README: Bring your own validator - isUniqueEmail starting', async () => { @@ -59,11 +51,11 @@ test('README: Bring your own validator - isUniqueEmail starting', async () => { }, }; - const isUniqueEmail = (): ValidatorFunction => + const isUniqueEmail = (): ValidatorFunction => async function (email, conf) { const exists = await db.users.find({ email }); // User | null if (!exists) { - return; + return email; } throw 'This email already in use. Try your alternate address.'; }; @@ -90,37 +82,29 @@ test('README: Bring your own validator - isUniqueEmail full', async () => { }, }; - const isUniqueEmail = (): ValidatorFunction => + const isUniqueEmail = (): ValidatorFunction => async function (email, conf) { - if (typeof email !== 'string') { - return Ignore(); - } const exists = await db.users.find({ email }); if (!exists) { - return Success(); + return email; } - return Failure('This email already in use. Try your alternate address.'); + throw 'This email already in use. Try your alternate address.' }; const validateRegistration = validate({ email: [isString(), isUniqueEmail()], }); - const validatedRegistration = await validateRegistration({ email: 'dsa@asd.asd' }); + type ExpectedType = { + email: string; + }; + const validatedRegistration: ExpectedType = await validateRegistration({ email: 'dsa@asd.asd' }); expect(validatedRegistration).toEqual({ email: 'dsa@asd.asd' }); try { await validateRegistration({ email: 'asd@asd.asd' }); } catch (err) { expect(err).toEqual(['This email already in use. Try your alternate address.']); } - - // Let's check for the expected type with some TypeScript magic - type ExpectedType = { - email: string; - }; - type AssertExpectedType = T extends ExpectedType ? true : never; - // This line shouldn't compile if the type is wrong - const cond1: AssertExpectedType = true; }); test('README: Sanitize and transform - hash', async () => { @@ -133,16 +117,13 @@ test('README: Sanitize and transform - hash', async () => { }, }; - const isUniqueEmail = (): ValidatorFunction => + const isUniqueEmail = (): ValidatorFunction => async function (email, conf) { - if (typeof email !== 'string') { - return Ignore(); - } const exists = await db.users.find({ email }); if (!exists) { - return Success(); + return email; } - return Failure('This email already in use. Try your alternate address.'); + throw 'This email already in use. Try your alternate address.' }; // Mock bcrypt @@ -152,13 +133,10 @@ test('README: Sanitize and transform - hash', async () => { }, }; - const hash = (): ValidatorFunction => + const hash = (): ValidatorFunction => async function (password, conf) { - if (typeof password !== 'string') { - return Ignore(); - } const hashedPassword = await bcrypt.hash(password, 8); - return Success(hashedPassword); + return hashedPassword; }; const validateRegistration = validate({ @@ -166,7 +144,11 @@ test('README: Sanitize and transform - hash', async () => { password: [isString(), minLength(8), hash()], }); - const validatedRegistration = await validateRegistration({ + type ExpectedType = { + email: string; + password: string; + }; + const validatedRegistration: ExpectedType = await validateRegistration({ email: 'asd2@asd.asd', password: 'asdasd69', }); @@ -175,15 +157,6 @@ test('README: Sanitize and transform - hash', async () => { email: 'asd2@asd.asd', password: '$2b$08$4S0b.0ut...', }); - - // Let's check for the expected type with some TypeScript magic - type ExpectedType = { - email: string; - password: string; - }; - type AssertExpectedType = T extends ExpectedType ? true : never; - // This line shouldn't compile if the type is wrong - const cond1: AssertExpectedType = true; }); test('README: Higher-order validators - confirmPassword', async () => { @@ -196,16 +169,13 @@ test('README: Higher-order validators - confirmPassword', async () => { }, }; - const isUniqueEmail = (): ValidatorFunction => + const isUniqueEmail = (): ValidatorFunction => async function (email, conf) { - if (typeof email !== 'string') { - return Ignore(); - } const exists = await db.users.find({ email }); if (!exists) { - return Success(); + return email; } - return Failure('This email already in use. Try your alternate address.'); + throw 'This email already in use. Try your alternate address.' }; // Mock bcrypt @@ -215,22 +185,24 @@ test('README: Higher-order validators - confirmPassword', async () => { }, }; - const hash = (): ValidatorFunction => + const hash = (): ValidatorFunction => async function (password, conf) { - if (typeof password !== 'string') { - return Ignore(); - } const hashedPassword = await bcrypt.hash(password, 8); - return Success(hashedPassword); + return hashedPassword; }; - const confirmPassword = (validator: Validator): ValidatorFunction => + const confirmPassword = < + I extends { email: string; password: string; password_confirm: string }, + O extends { email: string; password: string } + >( + validator: ValidatorObject + ): ValidatorFunction => async function (user, conf) { if (!user?.password_confirm) { - return Failure('You should confirm your password.'); + throw 'You should confirm your password.' } if (user.password_confirm !== user.password) { - return Failure('The two passwords do not match.'); + throw 'The two passwords do not match.' } return validate(validator)(user); }; @@ -242,7 +214,11 @@ test('README: Higher-order validators - confirmPassword', async () => { }) ); - const validatedRegistration = await validateRegistration({ + type ExpectedType = { + email: string; + password: string; + }; + const validatedRegistration: ExpectedType = await validateRegistration({ email: 'asd2@asd.asd', password: 'asdasd69', password_confirm: 'asdasd69', @@ -252,13 +228,4 @@ test('README: Higher-order validators - confirmPassword', async () => { email: 'asd2@asd.asd', password: '$2b$08$4S0b.0ut...', }); - - // Let's check for the expected type with some TypeScript magic - type ExpectedType = { - email: string; - password: string; - }; - type AssertExpectedType = T extends ExpectedType ? true : never; - // This line shouldn't compile if the type is wrong - const cond1: AssertExpectedType = true; }); diff --git a/tests/transformers/number/ceil.ts b/tests/transformers/number/ceil.ts index 4d9ce70..4ca91c7 100644 --- a/tests/transformers/number/ceil.ts +++ b/tests/transformers/number/ceil.ts @@ -12,19 +12,9 @@ const conf: ValidatorConfiguration = { test('ceil validator rounds up the number to the nearest integer', async () => { const validateCeil = ceil(); - await expect(validateCeil(5, conf)).resolves.toBe(5); - await expect(validateCeil(6.1, conf)).resolves.toBe(7); - await expect(validateCeil(6.5, conf)).resolves.toBe(7); - await expect(validateCeil(6.9, conf)).resolves.toBe(7); - await expect(validateCeil(7.999999999999901, conf)).resolves.toBe(8); -}); - -test('ceil ignores non-number inputs', async () => { - const validateCeil = ceil(); - - expect(validateCeil('6' as any, conf)).resolves.toBe(undefined); - await expect(validateCeil(NaN as any, conf)).resolves.toBe(undefined); - await expect(validateCeil(undefined as any, conf)).resolves.toBe(undefined); - await expect(validateCeil(null as any, conf)).resolves.toBe(undefined); - await expect(validateCeil({} as any, conf)).resolves.toBe(undefined); + await expect(validateCeil(5, conf)).toBe(5); + await expect(validateCeil(6.1, conf)).toBe(7); + await expect(validateCeil(6.5, conf)).toBe(7); + await expect(validateCeil(6.9, conf)).toBe(7); + await expect(validateCeil(7.999999999999901, conf)).toBe(8); }); diff --git a/tests/transformers/number/clamp.ts b/tests/transformers/number/clamp.ts index c6ecdf4..067161d 100644 --- a/tests/transformers/number/clamp.ts +++ b/tests/transformers/number/clamp.ts @@ -12,37 +12,27 @@ const conf: ValidatorConfiguration = { test('clamp validator returns the number if it is between the two limits', async () => { const validateClamp = clamp(5, 7); - await expect(validateClamp(5, conf)).resolves.toBe(5); - await expect(validateClamp(5.5, conf)).resolves.toBe(5.5); - await expect(validateClamp(6, conf)).resolves.toBe(6); - await expect(validateClamp(7, conf)).resolves.toBe(7); + await expect(validateClamp(5, conf)).toBe(5); + await expect(validateClamp(5.5, conf)).toBe(5.5); + await expect(validateClamp(6, conf)).toBe(6); + await expect(validateClamp(7, conf)).toBe(7); }); test('clamp validator returns the closest limit if the value is outside the limit', async () => { const validateClamp = clamp(5, 7); - await expect(validateClamp(-Infinity, conf)).resolves.toBe(5); - await expect(validateClamp(-1000, conf)).resolves.toBe(5); - await expect(validateClamp(999, conf)).resolves.toBe(7); - await expect(validateClamp(Infinity, conf)).resolves.toBe(7); + await expect(validateClamp(-Infinity, conf)).toBe(5); + await expect(validateClamp(-1000, conf)).toBe(5); + await expect(validateClamp(999, conf)).toBe(7); + await expect(validateClamp(Infinity, conf)).toBe(7); }); test('clamp validator works with decimal limits too', async () => { const validateClamp = clamp(5.1, 6.9); - await expect(validateClamp(-1000, conf)).resolves.toBe(5.1); - await expect(validateClamp(5.1, conf)).resolves.toBe(5.1); - await expect(validateClamp(6, conf)).resolves.toBe(6); - await expect(validateClamp(6.3, conf)).resolves.toBe(6.3); - await expect(validateClamp(999, conf)).resolves.toBe(6.9); -}); - -test('clamp ignores non-number inputs', async () => { - const validateClamp = clamp(5, 7); - - await expect(validateClamp('6' as any, conf)).resolves.toBe(undefined); - await expect(validateClamp(NaN as any, conf)).resolves.toBe(undefined); - await expect(validateClamp(undefined as any, conf)).resolves.toBe(undefined); - await expect(validateClamp(null as any, conf)).resolves.toBe(undefined); - await expect(validateClamp({} as any, conf)).resolves.toBe(undefined); + await expect(validateClamp(-1000, conf)).toBe(5.1); + await expect(validateClamp(5.1, conf)).toBe(5.1); + await expect(validateClamp(6, conf)).toBe(6); + await expect(validateClamp(6.3, conf)).toBe(6.3); + await expect(validateClamp(999, conf)).toBe(6.9); }); diff --git a/tests/transformers/number/floor.ts b/tests/transformers/number/floor.ts index a8f352d..dc5f578 100644 --- a/tests/transformers/number/floor.ts +++ b/tests/transformers/number/floor.ts @@ -12,18 +12,8 @@ const conf: ValidatorConfiguration = { test('floor validator rounds down the number to the nearest integer', async () => { const validateFloor = floor(); - await expect(validateFloor(5, conf)).resolves.toBe(5); - await expect(validateFloor(6.1, conf)).resolves.toBe(6); - await expect(validateFloor(6.9, conf)).resolves.toBe(6); - await expect(validateFloor(7.00000000001, conf)).resolves.toBe(7); -}); - -test('floor ignores non-number inputs', async () => { - const validateFloor = floor(); - - expect(validateFloor('6' as any, conf)).resolves.toBe(undefined); - await expect(validateFloor(NaN as any, conf)).resolves.toBe(undefined); - await expect(validateFloor(undefined as any, conf)).resolves.toBe(undefined); - await expect(validateFloor(null as any, conf)).resolves.toBe(undefined); - await expect(validateFloor({} as any, conf)).resolves.toBe(undefined); + await expect(validateFloor(5, conf)).toBe(5); + await expect(validateFloor(6.1, conf)).toBe(6); + await expect(validateFloor(6.9, conf)).toBe(6); + await expect(validateFloor(7.00000000001, conf)).toBe(7); }); diff --git a/tests/transformers/number/round.ts b/tests/transformers/number/round.ts index ec3fbf4..87ccb62 100644 --- a/tests/transformers/number/round.ts +++ b/tests/transformers/number/round.ts @@ -12,19 +12,9 @@ const conf: ValidatorConfiguration = { test('round validator rounds the number to the nearest integer mathematically', async () => { const validateRound = round(); - await expect(validateRound(5, conf)).resolves.toBe(5); - await expect(validateRound(6.1, conf)).resolves.toBe(6); - await expect(validateRound(6.5, conf)).resolves.toBe(7); - await expect(validateRound(6.9, conf)).resolves.toBe(7); - await expect(validateRound(7.00000000001, conf)).resolves.toBe(7); -}); - -test('round ignores non-number inputs', async () => { - const validateRound = round(); - - expect(validateRound('6' as any, conf)).resolves.toBe(undefined); - await expect(validateRound(NaN as any, conf)).resolves.toBe(undefined); - await expect(validateRound(undefined as any, conf)).resolves.toBe(undefined); - await expect(validateRound(null as any, conf)).resolves.toBe(undefined); - await expect(validateRound({} as any, conf)).resolves.toBe(undefined); + await expect(validateRound(5, conf)).toBe(5); + await expect(validateRound(6.1, conf)).toBe(6); + await expect(validateRound(6.5, conf)).toBe(7); + await expect(validateRound(6.9, conf)).toBe(7); + await expect(validateRound(7.00000000001, conf)).toBe(7); }); diff --git a/tests/transformers/number/toMultipleOf.ts b/tests/transformers/number/toMultipleOf.ts index 2455a4c..0e9a5f5 100644 --- a/tests/transformers/number/toMultipleOf.ts +++ b/tests/transformers/number/toMultipleOf.ts @@ -12,29 +12,19 @@ const conf: ValidatorConfiguration = { test('toMultipleOf validator rounds to the nearest multiple of n mathematically', async () => { const validateMultipleOfFive = toMultipleOf(5); - await expect(validateMultipleOfFive(5, conf)).resolves.toBe(5); - await expect(validateMultipleOfFive(6.1, conf)).resolves.toBe(5); - await expect(validateMultipleOfFive(7.5, conf)).resolves.toBe(10); - await expect(validateMultipleOfFive(10.00000000001, conf)).resolves.toBe(10); - await expect(validateMultipleOfFive(-7.5, conf)).resolves.toBe(-5); + await expect(validateMultipleOfFive(5, conf)).toBe(5); + await expect(validateMultipleOfFive(6.1, conf)).toBe(5); + await expect(validateMultipleOfFive(7.5, conf)).toBe(10); + await expect(validateMultipleOfFive(10.00000000001, conf)).toBe(10); + await expect(validateMultipleOfFive(-7.5, conf)).toBe(-5); }); test('toMultipleOf validator rounds to the nearest multiple of n with the given function', async () => { const validateMultipleOfFive = toMultipleOf(5, Math.floor); - await expect(validateMultipleOfFive(5, conf)).resolves.toBe(5); - await expect(validateMultipleOfFive(6.1, conf)).resolves.toBe(5); - await expect(validateMultipleOfFive(7.5, conf)).resolves.toBe(5); - await expect(validateMultipleOfFive(10.00000000001, conf)).resolves.toBe(10); - await expect(validateMultipleOfFive(-7.5, conf)).resolves.toBe(-10); -}); - -test('toMultipleOf ignores non-number inputs', async () => { - const validateMultipleOfFive = toMultipleOf(5); - - await expect(validateMultipleOfFive('10' as any, conf)).resolves.toBe(undefined); - await expect(validateMultipleOfFive(NaN as any, conf)).resolves.toBe(undefined); - await expect(validateMultipleOfFive(undefined as any, conf)).resolves.toBe(undefined); - await expect(validateMultipleOfFive(null as any, conf)).resolves.toBe(undefined); - await expect(validateMultipleOfFive({} as any, conf)).resolves.toBe(undefined); + await expect(validateMultipleOfFive(5, conf)).toBe(5); + await expect(validateMultipleOfFive(6.1, conf)).toBe(5); + await expect(validateMultipleOfFive(7.5, conf)).toBe(5); + await expect(validateMultipleOfFive(10.00000000001, conf)).toBe(10); + await expect(validateMultipleOfFive(-7.5, conf)).toBe(-10); }); diff --git a/tests/transformers/string/lower.ts b/tests/transformers/string/lower.ts index b7a5323..fe609d2 100644 --- a/tests/transformers/string/lower.ts +++ b/tests/transformers/string/lower.ts @@ -12,27 +12,15 @@ const conf: ValidatorConfiguration = { test('lower validator changes every letter to the lowercase equivalent', async () => { const validateLower = lower(); - await expect(validateLower('asdasd', conf)).resolves.toBe('asdasd'); - await expect(validateLower('Asdasd', conf)).resolves.toBe('asdasd'); - await expect(validateLower('aSdaSD', conf)).resolves.toBe('asdasd'); + await expect(validateLower('asdasd', conf)).toBe('asdasd'); + await expect(validateLower('Asdasd', conf)).toBe('asdasd'); + await expect(validateLower('aSdaSD', conf)).toBe('asdasd'); }); test('lower validator works with locale-strings', async () => { const validateLower = lower(); - await expect(validateLower('mÄdchEn', conf)).resolves.toBe('mädchen'); - await expect(validateLower('cSütÖrtÖk', conf)).resolves.toBe('csütörtök'); + await expect(validateLower('mÄdchEn', conf)).toBe('mädchen'); + await expect(validateLower('cSütÖrtÖk', conf)).toBe('csütörtök'); }); -test('lower if passed word is not a string, ignores', async () => { - const validateLower = lower(); - - await expect(validateLower(8 as any, conf)).resolves.toBe(undefined); - await expect(validateLower(NaN as any, conf)).resolves.toBe(undefined); - await expect(validateLower(true as any, conf)).resolves.toBe(undefined); - await expect(validateLower([] as any, conf)).resolves.toBe(undefined); - await expect(validateLower(Array(8).fill('a') as any, conf)).resolves.toBe(undefined); - await expect(validateLower({} as any, conf)).resolves.toBe(undefined); - await expect(validateLower(undefined as any, conf)).resolves.toBe(undefined); - await expect(validateLower(null as any, conf)).resolves.toBe(undefined); -}); diff --git a/tests/transformers/string/normalize.ts b/tests/transformers/string/normalize.ts index 366cbe1..091777d 100644 --- a/tests/transformers/string/normalize.ts +++ b/tests/transformers/string/normalize.ts @@ -12,24 +12,11 @@ const conf: ValidatorConfiguration = { test('normalize validator normalizes the Unicode representation of a string', async () => { const validateNormalize = normalize(); - await expect(validateNormalize('\u006E\u0303', conf)).resolves.toBe('\u00F1'); // ñ + await expect(validateNormalize('\u006E\u0303', conf)).toBe('\u00F1'); // ñ }); test('normalize validator normalizes the string with another normalization form', async () => { const validateNormalize = normalize('NFD'); - await expect(validateNormalize('\u00F1', conf)).resolves.toBe('\u006E\u0303'); // ñ -}); - -test('normalize if passed word is not a string, ignores', async () => { - const validateNormalize = normalize(); - - await expect(validateNormalize(8 as any, conf)).resolves.toBe(undefined); - await expect(validateNormalize(NaN as any, conf)).resolves.toBe(undefined); - await expect(validateNormalize(true as any, conf)).resolves.toBe(undefined); - await expect(validateNormalize([] as any, conf)).resolves.toBe(undefined); - await expect(validateNormalize(Array(8).fill('a') as any, conf)).resolves.toBe(undefined); - await expect(validateNormalize({} as any, conf)).resolves.toBe(undefined); - await expect(validateNormalize(undefined as any, conf)).resolves.toBe(undefined); - await expect(validateNormalize(null as any, conf)).resolves.toBe(undefined); + await expect(validateNormalize('\u00F1', conf)).toBe('\u006E\u0303'); // ñ }); diff --git a/tests/transformers/string/trim.ts b/tests/transformers/string/trim.ts index 9e7ad57..6f8bd81 100644 --- a/tests/transformers/string/trim.ts +++ b/tests/transformers/string/trim.ts @@ -12,21 +12,8 @@ const conf: ValidatorConfiguration = { test('trim validator removes whitespace from the start and end', async () => { const validateTrim = trim(); - await expect(validateTrim(' asdasd ', conf)).resolves.toBe('asdasd'); - await expect(validateTrim(' asdasd\r\n\t', conf)).resolves.toBe('asdasd'); - await expect(validateTrim(' asd asd\t', conf)).resolves.toBe('asd asd'); - await expect(validateTrim(' asd\r\nasd\t', conf)).resolves.toBe('asd\r\nasd'); -}); - -test('trim if passed word is not a string, ignores', async () => { - const validateTrim = trim(); - - await expect(validateTrim(8 as any, conf)).resolves.toBe(undefined); - await expect(validateTrim(NaN as any, conf)).resolves.toBe(undefined); - await expect(validateTrim(true as any, conf)).resolves.toBe(undefined); - await expect(validateTrim([] as any, conf)).resolves.toBe(undefined); - await expect(validateTrim(Array(8).fill('a') as any, conf)).resolves.toBe(undefined); - await expect(validateTrim({} as any, conf)).resolves.toBe(undefined); - await expect(validateTrim(undefined as any, conf)).resolves.toBe(undefined); - await expect(validateTrim(null as any, conf)).resolves.toBe(undefined); + await expect(validateTrim(' asdasd ', conf)).toBe('asdasd'); + await expect(validateTrim(' asdasd\r\n\t', conf)).toBe('asdasd'); + await expect(validateTrim(' asd asd\t', conf)).toBe('asd asd'); + await expect(validateTrim(' asd\r\nasd\t', conf)).toBe('asd\r\nasd'); }); diff --git a/tests/transformers/string/truncate.ts b/tests/transformers/string/truncate.ts index 7fca89a..3be961f 100644 --- a/tests/transformers/string/truncate.ts +++ b/tests/transformers/string/truncate.ts @@ -12,34 +12,21 @@ const conf: ValidatorConfiguration = { test('truncate validator cuts the string to a maximum number of characters', async () => { const validateTruncate = truncate(6); - await expect(validateTruncate('123', conf)).resolves.toBe('123'); - await expect(validateTruncate('123456', conf)).resolves.toBe('123456'); - await expect(validateTruncate('123456789', conf)).resolves.toBe('123456'); + await expect(validateTruncate('123', conf)).toBe('123'); + await expect(validateTruncate('123456', conf)).toBe('123456'); + await expect(validateTruncate('123456789', conf)).toBe('123456'); }); test('truncate validator cuts from the end of the string if n is negative', async () => { const validateTruncate = truncate(-6); - await expect(validateTruncate('123', conf)).resolves.toBe('123'); - await expect(validateTruncate('123456', conf)).resolves.toBe('123456'); - await expect(validateTruncate('123456789', conf)).resolves.toBe('456789'); + await expect(validateTruncate('123', conf)).toBe('123'); + await expect(validateTruncate('123456', conf)).toBe('123456'); + await expect(validateTruncate('123456789', conf)).toBe('456789'); }); test('truncate validator returns empty if n is zero', async () => { const validateTruncate = truncate(0); - await expect(validateTruncate('123', conf)).resolves.toBe(''); -}); - -test('truncate if passed word is not a string, ignores', async () => { - const validateTruncate = truncate(6); - - await expect(validateTruncate(8 as any, conf)).resolves.toBe(undefined); - await expect(validateTruncate(NaN as any, conf)).resolves.toBe(undefined); - await expect(validateTruncate(true as any, conf)).resolves.toBe(undefined); - await expect(validateTruncate([] as any, conf)).resolves.toBe(undefined); - await expect(validateTruncate(Array(8).fill('a') as any, conf)).resolves.toBe(undefined); - await expect(validateTruncate({} as any, conf)).resolves.toBe(undefined); - await expect(validateTruncate(undefined as any, conf)).resolves.toBe(undefined); - await expect(validateTruncate(null as any, conf)).resolves.toBe(undefined); + await expect(validateTruncate('123', conf)).toBe(''); }); diff --git a/tests/transformers/string/upper.ts b/tests/transformers/string/upper.ts index 3f1aa62..62da5c0 100644 --- a/tests/transformers/string/upper.ts +++ b/tests/transformers/string/upper.ts @@ -12,27 +12,15 @@ const conf: ValidatorConfiguration = { test('upper validator changes every letter to the uppercase equivalent', async () => { const validateUpper = upper(); - await expect(validateUpper('asdasd', conf)).resolves.toBe('ASDASD'); - await expect(validateUpper('Asdasd', conf)).resolves.toBe('ASDASD'); - await expect(validateUpper('aSdaSD', conf)).resolves.toBe('ASDASD'); - await expect(validateUpper('ASDASD', conf)).resolves.toBe('ASDASD'); + await expect(validateUpper('asdasd', conf)).toBe('ASDASD'); + await expect(validateUpper('Asdasd', conf)).toBe('ASDASD'); + await expect(validateUpper('aSdaSD', conf)).toBe('ASDASD'); + await expect(validateUpper('ASDASD', conf)).toBe('ASDASD'); }); test('upper validator works with locale-strings', async () => { const validateUpper = upper(); - await expect(validateUpper('weiß', conf)).resolves.toBe('WEISS'); - await expect(validateUpper('cSütÖrtÖk', conf)).resolves.toBe('CSÜTÖRTÖK'); -}); -test('upper if passed word is not a string, ignores', async () => { - const validateUpper = upper(); - - await expect(validateUpper(8 as any, conf)).resolves.toBe(undefined); - await expect(validateUpper(NaN as any, conf)).resolves.toBe(undefined); - await expect(validateUpper(true as any, conf)).resolves.toBe(undefined); - await expect(validateUpper([] as any, conf)).resolves.toBe(undefined); - await expect(validateUpper(Array(8).fill('a') as any, conf)).resolves.toBe(undefined); - await expect(validateUpper({} as any, conf)).resolves.toBe(undefined); - await expect(validateUpper(undefined as any, conf)).resolves.toBe(undefined); - await expect(validateUpper(null as any, conf)).resolves.toBe(undefined); + await expect(validateUpper('weiß', conf)).toBe('WEISS'); + await expect(validateUpper('cSütÖrtÖk', conf)).toBe('CSÜTÖRTÖK'); }); diff --git a/tests/types.ts b/tests/types.ts new file mode 100644 index 0000000..5f8ea6f --- /dev/null +++ b/tests/types.ts @@ -0,0 +1,158 @@ +import { describe, expect, test } from '@jest/globals'; +import { SyncValidatorFunction, ValidatingFunction, ValidatorFunction } from '../src/types'; +import validate from '../src/validate'; + +const isString: SyncValidatorFunction = (val, c) => { + if (typeof val === 'string') { + return val; + } else { + throw new Error('Not a string.'); + } +}; + +const toNumber: SyncValidatorFunction = (val, c) => { + return Number.parseInt(val); +}; + +const intoString: SyncValidatorFunction = (val, c) => { + return val.toString(); +}; + +const isStringP: ValidatorFunction = (val, c) => { + if (typeof val === 'string') return Promise.resolve(val); + else return Promise.reject(); +}; + +const toNumberP: ValidatorFunction = (val, c) => { + return Promise.resolve(Number.parseInt(val)); +}; + +const intoStringP: ValidatorFunction = (val, c) => { + return Promise.resolve(val.toString()); +}; + +test('sync validator function returns type without promise', () => { + const validateWithSyncFunction = validate(isString); + const vfn: string = validateWithSyncFunction(''); +}); +test('sync validator function array returns transformed type without promise', () => { + const validateWithSyncFunction1 = validate([isString]); + const vlist1: string = validateWithSyncFunction1(''); + + const validateWithSyncFunction2 = validate([isString, toNumber]); + const vlist2: number = validateWithSyncFunction2(''); + + const validateWithSyncFunction3 = validate([isString, toNumber, intoString]); + const vlist3: string = validateWithSyncFunction3(''); + + const validateWithSyncFunction24 = validate([toNumber, intoString, toNumber, intoString, toNumber, intoString, toNumber, intoString, toNumber, intoString, toNumber, intoString, toNumber, intoString, toNumber, intoString, toNumber, intoString, toNumber, intoString, toNumber, intoString, toNumber, intoString]); + const vlist24: string = validateWithSyncFunction24(''); +}); +test('sync validator object returns object type without promise', () => { + const validateWithObject = validate({ a: [isString, toNumber, intoString], b: isString }); + const vobj: { a: string; b: string } = validateWithObject({ a: '', b: '' }); +}); +test('sync nested validator object return nested object without promise', () => { + const validateWithObject = validate({ a: { c: isString }, b: isString }); + const vdobj: { a: { c: string }; b: string } = validateWithObject({ a: { c: '' }, b: '' }); +}); + +test('async validator function returns type with promise', () => { + const validateWithAsyncFunction = validate(isStringP); + const vfnp: Promise = validateWithAsyncFunction(''); +}); +test('async validator function array returns type with promise', () => { + const validateWithAsyncFunction1 = validate([isStringP]); + const vlist1p: Promise = validateWithAsyncFunction1(''); + + const validateWithAsyncFunction2 = validate([isStringP, toNumberP]); + const vlist2p: Promise = validateWithAsyncFunction2(''); + + const validateWithAsyncFunction3 = validate([isStringP, toNumberP, intoStringP]); + const vlist3p: Promise = validateWithAsyncFunction3(''); + + const validateWithSyncFunction24 = validate([toNumberP, intoStringP, toNumberP, intoStringP, toNumberP, intoStringP, toNumberP, intoStringP, toNumberP, intoStringP, toNumberP, intoStringP, toNumberP, intoStringP, toNumberP, intoStringP, toNumberP, intoStringP, toNumberP, intoStringP, toNumberP, intoStringP, toNumberP, intoStringP]); + const vlist24p: Promise = validateWithSyncFunction24(''); +}); +test('async validator object returns object type with promise', () => { + const validateWithAsyncObject = validate({ a: [isStringP, toNumberP, intoStringP], b: isStringP }); + const vobjp: Promise<{ a: string; b: string }> = validateWithAsyncObject({ a: '', b: '' }); +}); +test('async nested validator object returns object type with promise', () => { + const validateWithAsyncNestedObject = validate({ a: { c: isStringP }, b: isStringP }); + const vdobjp: Promise<{ a: { c: string }; b: string }> = validateWithAsyncNestedObject({ a: { c: '' }, b: '' }); +}); + +test('mixed validator function array returns type with promise', () => { + const validateWithMixedFunction21 = validate([isString, toNumberP]); + const vlist2m1: Promise = validateWithMixedFunction21(''); + + const validateWithMixedFunction22 = validate([isStringP, toNumber]); + const vlist2m2: Promise = validateWithMixedFunction22(''); + + const validateWithMixedFunction31 = validate([isStringP, toNumber, intoStringP]); + const vlist3m1: Promise = validateWithMixedFunction31(''); + + const validateWithMixedFunction32 = validate([isStringP, toNumber, intoStringP]); + const vlist3m2: Promise = validateWithMixedFunction32(''); +}); +test('mixed validator object returns object type with promise', () => { + const validateWithMixedObject1 = validate({ a: [isString, toNumber, intoStringP], b: isStringP }); + const vobjm1: Promise<{ a: string; b: string }> = validateWithMixedObject1({ a: '', b: '' }); + + const validateWithMixedObject2 = validate({ a: [isStringP, toNumberP, intoString], b: isStringP }); + const vobjm2: Promise<{ a: string; b: string }> = validateWithMixedObject2({ a: '', b: '' }); + + const validateWithMixedObject3 = validate({ a: [isString, toNumber, intoString], b: isStringP }); + const vobjm3: Promise<{ a: string; b: string }> = validateWithMixedObject3({ a: '', b: '' }); + + const validateWithMixedObject4 = validate({ a: [isString, toNumber, intoStringP], b: isString }); + const vobjm4: Promise<{ a: string; b: string }> = validateWithMixedObject4({ a: '', b: '' }); +}); +test('mixed nested validator object returns object type with promise', () => { + const validateWithAsyncNestedObject1 = validate({ a: { c: isString }, b: isStringP }); + const vdobjm1: Promise<{ a: { c: string }; b: string }> = validateWithAsyncNestedObject1({ a: { c: '' }, b: '' }); + + const validateWithAsyncNestedObject2 = validate({ a: { c: isStringP }, b: isString }); + const vdobjm2: Promise<{ a: { c: string }; b: string }> = validateWithAsyncNestedObject2({ a: { c: '' }, b: '' }); +}); + + +test('inner sync validator function returns inner type', () => { + const validateWithSyncInnerFunction = validate(validate(isString)); + const vfnsi: string = validateWithSyncInnerFunction(''); +}); + +test('inner async validator function returns inner type', () => { + const validatingFn: ValidatingFunction = validate(isStringP); + const validateWithInnerFunction = validate(validatingFn); + const vfnai: Promise = validateWithInnerFunction(''); +}); +test('inner validator function array returns inner type', () => { + const validateWithInnerFunction11 = validate([validate(isString)]); + const vlist1i1: string = validateWithInnerFunction11(''); + + const validateWithInnerFunction13 = validate(validate([isStringP])); + const vlist1i3: Promise = validateWithInnerFunction13(''); +}); +test('inner validator object returns inner object type', () => { + const validateWithInnerObject1 = validate({ a: validate(isStringP), b: isStringP }); + const vobji1: Promise<{ a: string; b: string }> = validateWithInnerObject1({ a: '', b: '' }); + + const validateWithInnerObject2 = validate({ a: validate(isString), b: isStringP }); + const vobji2: Promise<{ a: string; b: string }> = validateWithInnerObject2({ a: '', b: '' }); + + const validateWithInnerObject3 = validate({ a: validate(isString), b: isString }); + const vobji3: { a: string; b: string } = validateWithInnerObject3({ a: '', b: '' }); +}); +test('inner nested validator object returns inner object type', () => { + const vf: ValidatingFunction = validate(isStringP); + const validateWithInnerNestedObject1 = validate({ a: { c: vf }, b: isStringP }); + const vdobji1: Promise<{ a: { c: string }; b: string }> = validateWithInnerNestedObject1({ a: { c: '' }, b: '' }); + + const validateWithInnerNestedObject2 = validate({ a: { c: validate(isString) }, b: isStringP }); + const vdobji2: Promise<{ a: { c: string }; b: string }> = validateWithInnerNestedObject2({ a: { c: '' }, b: '' }); + + const validateWithInnerNestedObject3 = validate({ a: { c: isStringP }, b: validate(isStringP) }); + const vdobji3: Promise<{ a: { c: string }; b: string }> = validateWithInnerNestedObject3({ a: { c: '' }, b: '' }); +}); \ No newline at end of file diff --git a/tests/validate.ts b/tests/validate.ts index 0f828c2..25d515f 100644 --- a/tests/validate.ts +++ b/tests/validate.ts @@ -1,20 +1,20 @@ import { describe, expect, test } from '@jest/globals'; import validate from '../src/validate'; -import { Failure, Success, ValidatorFunction } from '../src/types'; +import { ValidatorFunction } from '../src/types'; -const testSuccessfulValidation = (): ValidatorFunction => - async function (_value, _conf) { - return Success(); +const testSuccessfulValidation = (): ValidatorFunction => + async function (value, _conf) { + return value; }; -const testFailingValidation = (): ValidatorFunction => +const testFailingValidation = (): ValidatorFunction => async function (_value, _conf) { - return Failure('test failed'); + throw 'test failed' }; -const testTransformValidation = (): ValidatorFunction => +const testTransformValidation = (): ValidatorFunction => async function (value, _conf) { - return Success(value + value); + return value + value; }; test('validate with function returns the value on success', async () => { @@ -121,7 +121,7 @@ test('validate with object works with nested objects', async () => { test('validate with object throws array of error messages', async () => { const value = { key: 'asdasd', another: 'dsadas' }; const validateTest = validate({ - key: [testFailingValidation()], + key: testFailingValidation(), another: testFailingValidation(), }); diff --git a/tests/validators/arrayOf.ts b/tests/validators/arrayOf.ts index 455a9aa..6fd94ba 100644 --- a/tests/validators/arrayOf.ts +++ b/tests/validators/arrayOf.ts @@ -1,45 +1,84 @@ import { describe, expect, test } from '@jest/globals'; import arrayOf from '../../src/validators/arrayOf'; import validate from '../../src/validate'; -import { Failure, Success, ValidatorFunction } from '../../src/types'; +import { SyncValidatorFunction, ValidatorFunction } from '../../src/types'; -const testSuccessfulValidation = (): ValidatorFunction => - async function (_value, _conf) { - return Success(); +const testSuccessfulValidation = (): SyncValidatorFunction => + function (value, _conf) { + return value; + }; + +const testSuccessfulValidationP = (): ValidatorFunction => + async function (value, _conf) { + return value; + }; + +const testFailingValidation = (): SyncValidatorFunction => + function (_value, _conf) { + throw 'test failed'; }; -const testFailingValidation = (): ValidatorFunction => +const testFailingValidationP = (): ValidatorFunction => async function (_value, _conf) { - return Failure('test failed'); + throw 'test failed'; }; -const testMinimumValidation = (): ValidatorFunction => +const testMinimumValidation = (): ValidatorFunction => async function (value, _conf) { if (value > 2) { - return Success(); + return value; } else { - return Failure('test failed'); + throw 'test failed'; } }; -test('arrayOf function validates all items in an array', async () => { +test('arrayOf function validates all items in an array with sync validator', async () => { const value = [1, 2, 3]; const validateTest = validate(arrayOf(testSuccessfulValidation())); - await expect(validateTest(value)).resolves.toEqual(value); + const result: number[] = validateTest(value); + expect(result).toEqual(value); }); -test('arrayOf function validates objects', async () => { - const value = [{ key: 'asdasd' }, { key: 'dsadsa' }]; - const validateTest = validate(arrayOf(testSuccessfulValidation())); +test('arrayOf function validates all items in an array with async validator', async () => { + const value = [1, 2, 3]; + const validateTest = validate(arrayOf(testSuccessfulValidationP())); + + const result: Promise = validateTest(value); + await expect(result).resolves.toEqual(value); +}); + +test('arrayOf function validates objects with sync validator', async () => { + const value = [{ key: 1 }, { key: 2 }]; + const validateTest = validate(arrayOf({ key: testSuccessfulValidation() })); - await expect(validateTest(value)).resolves.toEqual(value); + const result: { key: number }[] = validateTest(value); + expect(result).toEqual(value); }); -test('arrayOf function throws error for every wrong item in array', async () => { +test('arrayOf function validates objects with async validator', async () => { + const value = [{ key: 1 }, { key: 2 }]; + const validateTest = validate(arrayOf({ key: testSuccessfulValidationP() })); + + const result: Promise<{ key: number }[]> = validateTest(value); + await expect(result).resolves.toEqual(value); +}); + +test('arrayOf function throws error for every wrong item in array with sync validator', async () => { const value = [1, 2, 3]; const validateTest = validate(arrayOf(testFailingValidation())); + try { + validateTest(value); + } catch (err) { + expect(err).toEqual(['test failed', 'test failed', 'test failed']); + } +}); + +test('arrayOf function throws error for every wrong item in array with async validator', async () => { + const value = [1, 2, 3]; + const validateTest = validate(arrayOf(testFailingValidationP())); + try { await validateTest(value); } catch (err) { @@ -63,7 +102,7 @@ test('arrayOf function throws error if given data is not an array', async () => const validateTest = validate(arrayOf(testSuccessfulValidation())); try { - await validateTest(value); + validateTest(value as any); } catch (err) { expect(err).not.toBeUndefined(); } diff --git a/tests/validators/bail.ts b/tests/validators/bail.ts index 92190da..72ae1e7 100644 --- a/tests/validators/bail.ts +++ b/tests/validators/bail.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from '@jest/globals'; -import { Failure, Success, ValidatorConfiguration, ValidatorFunction } from '../../src/types'; +import { SyncValidatorFunction, ValidatorConfiguration, ValidatorFunction } from '../../src/types'; import validate from '../../src/validate'; import bail from '../../src/validators/bail'; @@ -10,50 +10,161 @@ const conf: ValidatorConfiguration = { path: [], }; -test('bail runs over each test if they are not failing', async () => { +test('bail runs over each sync test if they are not failing', async () => { let i = 0; - const increment = (): ValidatorFunction => async () => { + const increment = (): SyncValidatorFunction => (value) => { i += 1; - return Success(); + return value; }; - // To test whether it works with returning value - const increment2 = (): ValidatorFunction => async (value) => { + const validateBail = validate(bail([increment(), increment(), increment()])); + + expect(validateBail(null)).toBe(null); + expect(i).toBe(3); +}); + +test('bail runs over each async test if they are not failing', async () => { + let i = 0; + + const increment = (): ValidatorFunction => async (value) => { i += 1; - return Success(value); + return value; }; - const validateBail = validate(bail([increment(), increment2(), increment()])); + const validateBail = validate(bail([increment(), increment(), increment()])); await expect(validateBail(null)).resolves.toBe(null); expect(i).toBe(3); }); -test('bail stops after first failing test', async () => { +test('bail runs over each mixed test if they are not failing', async () => { + let i = 0; + + const increment = (): SyncValidatorFunction => (value) => { + i += 1; + return value; + }; + + const incrementP = (): ValidatorFunction => async (value) => { + i += 1; + return value; + }; + + const validateBail = validate(bail([increment(), incrementP()])); + + await expect(validateBail(null)).resolves.toBe(null); + expect(i).toBe(2); + + i = 0 + const validateBail2 = validate(bail([incrementP(), increment()])); + + await expect(validateBail2(null)).resolves.toBe(null); + expect(i).toBe(2); +}); + +test('bail stops after first failing sync test', async () => { + let i = 0; + + const increment = (): SyncValidatorFunction => (value) => { + i += 1; + return value; + }; + + const failIncrement = (): SyncValidatorFunction => () => { + i += 1; + throw 'Incrementation failed.' + }; + + const validateBail = validate(bail([increment(), failIncrement(), failIncrement()])); + + try { + validateBail(null); + } catch (err) { + expect(err).toEqual(['Incrementation failed.']); + } + expect(i).toBe(2); +}); + +test('bail stops after first failing async test', async () => { let i = 0; - const failIncrement = (): ValidatorFunction => async () => { + const increment = (): ValidatorFunction => async (value) => { i += 1; - return Failure('Incrementation failed.'); + return value; }; - const validateBail = validate(bail([failIncrement(), failIncrement(), failIncrement()])); + const failIncrement = (): ValidatorFunction => async () => { + i += 1; + throw 'Incrementation failed.' + }; + + const validateBail = validate(bail([increment(), failIncrement(), failIncrement()])); try { await validateBail(null); } catch (err) { expect(err).toEqual(['Incrementation failed.']); } - expect(i).toBe(1); + expect(i).toBe(2); +}); + +test('bail stops after first failing test in mixed tests', async () => { + let i = 0; + + const increment = (): SyncValidatorFunction => (value) => { + i += 1; + return value; + }; + + const failIncrement = (): SyncValidatorFunction => () => { + i += 1; + throw 'Incrementation failed.' + }; + + const incrementP = (): ValidatorFunction => async (value) => { + i += 1; + return value; + }; + + const failIncrementP = (): ValidatorFunction => async () => { + i += 1; + throw 'Incrementation failed.' + }; + + const validateBail1 = validate(bail([increment(), failIncrementP(), failIncrement()])); + try { + await validateBail1(null); + } catch (err) { + expect(err).toEqual(['Incrementation failed.']); + } + expect(i).toBe(2); + + i = 0; + const validateBail2 = validate(bail([increment(), failIncrement(), failIncrementP()])); + try { + await validateBail2(null); + } catch (err) { + expect(err).toEqual(['Incrementation failed.']); + } + expect(i).toBe(2); + + i = 0; + const validateBail3 = validate(bail([incrementP(), failIncrement(), failIncrementP()])); + try { + await validateBail3(null); + } catch (err) { + expect(err).toEqual(['Incrementation failed.']); + } + expect(i).toBe(2); }); test('bail just passes to validate if the passed value is not an array', async () => { - const increment = (): ValidatorFunction => async (value) => { + const increment = (): ValidatorFunction => async (value) => { if (value) { - return Success(); + return value; } else { - return Failure("This shouldn't pass."); + throw "This shouldn't pass." } }; diff --git a/tests/validators/boolean/falsey.ts b/tests/validators/boolean/falsey.ts index 6438dd2..1875138 100644 --- a/tests/validators/boolean/falsey.ts +++ b/tests/validators/boolean/falsey.ts @@ -12,44 +12,44 @@ const conf: ValidatorConfiguration = { test('falsey with false, zero, empty string or nullish returns success', async () => { const validateFalsey = falsey(); - await expect(validateFalsey(false, conf)).resolves.toBeUndefined(); - await expect(validateFalsey(0 as any, conf)).resolves.toBeUndefined(); - await expect(validateFalsey(-0 as any, conf)).resolves.toBeUndefined(); - await expect(validateFalsey('' as any, conf)).resolves.toBeUndefined(); - await expect(validateFalsey(null as any, conf)).resolves.toBeUndefined(); - await expect(validateFalsey(undefined as any, conf)).resolves.toBeUndefined(); - await expect(validateFalsey(NaN as any, conf)).resolves.toBeUndefined(); + expect(validateFalsey(false, conf)).toEqual(false); + expect(validateFalsey(0, conf)).toEqual(0); + expect(validateFalsey(-0, conf)).toEqual(-0); + expect(validateFalsey('', conf)).toEqual(''); + expect(validateFalsey(null, conf)).toEqual(null); + expect(validateFalsey(undefined, conf)).toEqual(undefined); + expect(validateFalsey(NaN, conf)).toEqual(NaN); }); test('falsey with anything other fails', async () => { const validateFalsey = falsey(); try { - await validateFalsey(true as any, conf); + validateFalsey(true as any, conf); } catch (err) { expect(err).toBe('Field boolField should be falsey.'); } try { - await validateFalsey(-1 as any, conf); + validateFalsey(-1 as any, conf); } catch (err) { expect(err).toBe('Field boolField should be falsey.'); } try { - await validateFalsey('string' as any, conf); + validateFalsey('string' as any, conf); } catch (err) { expect(err).toBe('Field boolField should be falsey.'); } try { - await validateFalsey({} as any, conf); + validateFalsey({} as any, conf); } catch (err) { expect(err).toBe('Field boolField should be falsey.'); } try { - await validateFalsey([] as any, conf); + validateFalsey([] as any, conf); } catch (err) { expect(err).toBe('Field boolField should be falsey.'); } diff --git a/tests/validators/boolean/isBool.ts b/tests/validators/boolean/isBool.ts index 3dab677..0e2a0a8 100644 --- a/tests/validators/boolean/isBool.ts +++ b/tests/validators/boolean/isBool.ts @@ -12,39 +12,39 @@ const conf: ValidatorConfiguration = { test('isBool with true or false returns success', async () => { const validateBool = isBool(); - await expect(validateBool(true, conf)).resolves.toBeUndefined(); - await expect(validateBool(false, conf)).resolves.toBeUndefined(); + expect(validateBool(true, conf)).toEqual(true); + expect(validateBool(false, conf)).toEqual(false); }); test('isBool with anything other fails', async () => { const validateBool = isBool(); try { - await validateBool('string' as any, conf); + validateBool('string', conf); } catch (err) { expect(err).toBe('Field boolField should be a boolean.'); } try { - await validateBool(123 as any, conf); + validateBool(123, conf); } catch (err) { expect(err).toBe('Field boolField should be a boolean.'); } try { - await validateBool(null as any, conf); + validateBool(null, conf); } catch (err) { expect(err).toBe('Field boolField should be a boolean.'); } try { - await validateBool(undefined as any, conf); + validateBool(undefined, conf); } catch (err) { expect(err).toBe('Field boolField should be a boolean.'); } try { - await validateBool(NaN as any, conf); + validateBool(NaN, conf); } catch (err) { expect(err).toBe('Field boolField should be a boolean.'); } diff --git a/tests/validators/boolean/toBool.ts b/tests/validators/boolean/toBool.ts index 332313a..49e1fd4 100644 --- a/tests/validators/boolean/toBool.ts +++ b/tests/validators/boolean/toBool.ts @@ -12,17 +12,17 @@ const conf: ValidatorConfiguration = { test('toBool converts everything to boolean', async () => { const sanitizeBool = toBool(); - await expect(sanitizeBool(true, conf)).resolves.toBe(true); - await expect(sanitizeBool(false, conf)).resolves.toBe(false); - await expect(sanitizeBool(0 as any, conf)).resolves.toBe(false); - await expect(sanitizeBool(-0 as any, conf)).resolves.toBe(false); - await expect(sanitizeBool(1 as any, conf)).resolves.toBe(true); - await expect(sanitizeBool(123 as any, conf)).resolves.toBe(true); - await expect(sanitizeBool(NaN as any, conf)).resolves.toBe(false); - await expect(sanitizeBool('' as any, conf)).resolves.toBe(false); - await expect(sanitizeBool('asd' as any, conf)).resolves.toBe(true); - await expect(sanitizeBool({} as any, conf)).resolves.toBe(true); - await expect(sanitizeBool([] as any, conf)).resolves.toBe(true); - await expect(sanitizeBool(null as any, conf)).resolves.toBe(false); - await expect(sanitizeBool(undefined as any, conf)).resolves.toBe(false); + await expect(sanitizeBool(true, conf)).toBe(true); + await expect(sanitizeBool(false, conf)).toBe(false); + await expect(sanitizeBool(0 as any, conf)).toBe(false); + await expect(sanitizeBool(-0 as any, conf)).toBe(false); + await expect(sanitizeBool(1 as any, conf)).toBe(true); + await expect(sanitizeBool(123 as any, conf)).toBe(true); + await expect(sanitizeBool(NaN as any, conf)).toBe(false); + await expect(sanitizeBool('' as any, conf)).toBe(false); + await expect(sanitizeBool('asd' as any, conf)).toBe(true); + await expect(sanitizeBool({} as any, conf)).toBe(true); + await expect(sanitizeBool([] as any, conf)).toBe(true); + await expect(sanitizeBool(null as any, conf)).toBe(false); + await expect(sanitizeBool(undefined as any, conf)).toBe(false); }); diff --git a/tests/validators/boolean/truthy.ts b/tests/validators/boolean/truthy.ts index f9d16bf..ee54697 100644 --- a/tests/validators/boolean/truthy.ts +++ b/tests/validators/boolean/truthy.ts @@ -12,49 +12,49 @@ const conf: ValidatorConfiguration = { test('truthy with other values returns success', async () => { const validateTruthy = truthy(); - await expect(validateTruthy(true, conf)).resolves.toBeUndefined(); - await expect(validateTruthy(1 as any, conf)).resolves.toBeUndefined(); - await expect(validateTruthy(-1 as any, conf)).resolves.toBeUndefined(); - await expect(validateTruthy('string' as any, conf)).resolves.toBeUndefined(); - await expect(validateTruthy({} as any, conf)).resolves.toBeUndefined(); - await expect(validateTruthy([] as any, conf)).resolves.toBeUndefined(); + expect(validateTruthy(true, conf)).toEqual(true); + expect(validateTruthy(1, conf)).toEqual(1); + expect(validateTruthy(-1, conf)).toEqual(-1); + expect(validateTruthy('string', conf)).toEqual('string'); + expect(validateTruthy({}, conf)).toEqual({}); + expect(validateTruthy([], conf)).toEqual([]); }); test('truthy with false, zero, empty string or nullish other fails', async () => { const validateTruthy = truthy(); try { - await validateTruthy(false, conf); + validateTruthy(false, conf); } catch (err) { expect(err).toBe('Field boolField should be truthy.'); } try { - await validateTruthy(0 as any, conf); + validateTruthy(0, conf); } catch (err) { expect(err).toBe('Field boolField should be truthy.'); } try { - await validateTruthy(-0 as any, conf); + validateTruthy(-0, conf); } catch (err) { expect(err).toBe('Field boolField should be truthy.'); } try { - await validateTruthy('' as any, conf); + validateTruthy('', conf); } catch (err) { expect(err).toBe('Field boolField should be truthy.'); } try { - await validateTruthy(null as any, conf); + validateTruthy(null, conf); } catch (err) { expect(err).toBe('Field boolField should be truthy.'); } try { - await validateTruthy(undefined as any, conf); + validateTruthy(undefined, conf); } catch (err) { expect(err).toBe('Field boolField should be truthy.'); } try { - await validateTruthy(NaN as any, conf); + validateTruthy(NaN, conf); } catch (err) { expect(err).toBe('Field boolField should be truthy.'); } diff --git a/tests/validators/boolean/tryBool.ts b/tests/validators/boolean/tryBool.ts index 95f9eb0..e440d80 100644 --- a/tests/validators/boolean/tryBool.ts +++ b/tests/validators/boolean/tryBool.ts @@ -12,10 +12,10 @@ const conf: ValidatorConfiguration = { test('tryBool with with true, false, one and zero returns the correct value', async () => { const sanitizeBool = tryBool(); - await expect(sanitizeBool(true, conf)).resolves.toBe(true); - await expect(sanitizeBool(false, conf)).resolves.toBe(false); - await expect(sanitizeBool(1 as any, conf)).resolves.toBe(true); - await expect(sanitizeBool(0 as any, conf)).resolves.toBe(false); + await expect(sanitizeBool(true, conf)).toBe(true); + await expect(sanitizeBool(false, conf)).toBe(false); + await expect(sanitizeBool(1 as any, conf)).toBe(true); + await expect(sanitizeBool(0 as any, conf)).toBe(false); }); test('tryBool with other things fails', async () => { @@ -61,8 +61,8 @@ test('tryBool with other things fails', async () => { test('tryBool can be customized to accept other truthy or falsey values', async () => { const sanitizeBool = tryBool([true, 1, 'true', 600], [true, 1, 'false', null]); - await expect(sanitizeBool('true' as any, conf)).resolves.toBe(true); - await expect(sanitizeBool('false' as any, conf)).resolves.toBe(false); - await expect(sanitizeBool(600 as any, conf)).resolves.toBe(true); - await expect(sanitizeBool(null as any, conf)).resolves.toBe(false); + await expect(sanitizeBool('true' as any, conf)).toBe(true); + await expect(sanitizeBool('false' as any, conf)).toBe(false); + await expect(sanitizeBool(600 as any, conf)).toBe(true); + await expect(sanitizeBool(null as any, conf)).toBe(false); }); diff --git a/tests/validators/date/after.ts b/tests/validators/date/after.ts index fa226df..c508e8d 100644 --- a/tests/validators/date/after.ts +++ b/tests/validators/date/after.ts @@ -13,19 +13,19 @@ test('after when the date is after the date returns success', async () => { const begin = new Date('2000-01-01'); const validateInRange = after(begin); - await expect(validateInRange(new Date('2000-01-02'), conf)).resolves.toBeUndefined(); - await expect(validateInRange(new Date('2021-08-27'), conf)).resolves.toBeUndefined(); - await expect(validateInRange(new Date('2099-01-01'), conf)).resolves.toBeUndefined(); + expect(validateInRange(new Date('2000-01-02'), conf)).toEqual(new Date('2000-01-02')); + expect(validateInRange(new Date('2021-08-27'), conf)).toEqual(new Date('2021-08-27')); + expect(validateInRange(new Date('2099-01-01'), conf)).toEqual(new Date('2099-01-01')); }); test('after definition can convert from string or number', async () => { const validateInStringRange = after('2000-01-01'); - await expect(validateInStringRange(new Date('2021-08-27'), conf)).resolves.toBeUndefined(); + expect(validateInStringRange(new Date('2021-08-27'), conf)).toEqual(new Date('2021-08-27')); const validateInNumberRange = after(946684800000); - await expect(validateInNumberRange(new Date('2021-08-27'), conf)).resolves.toBeUndefined(); + expect(validateInNumberRange(new Date('2021-08-27'), conf)).toEqual(new Date('2021-08-27')); }); test('after when the date is before or equal to the date, fails', async () => { @@ -55,18 +55,5 @@ test('after, when both inclusives are set, returns success for and upper lower l const begin = new Date('2000-01-01'); const validateInRange = after(begin, { inclusive: true }); - await expect(validateInRange(new Date('2000-01-01'), conf)).resolves.toBeUndefined(); -}); - -test('after ignores non-date inputs', async () => { - const begin = new Date('2000-01-01'); - const validateInRange = after(begin); - - await expect(validateInRange('string' as any, conf)).resolves.toBeUndefined(); - await expect(validateInRange(123 as any, conf)).resolves.toBeUndefined(); - await expect(validateInRange(NaN as any, conf)).resolves.toBeUndefined(); - await expect(validateInRange({} as any, conf)).resolves.toBeUndefined(); - await expect(validateInRange([] as any, conf)).resolves.toBeUndefined(); - await expect(validateInRange(null as any, conf)).resolves.toBeUndefined(); - await expect(validateInRange(undefined as any, conf)).resolves.toBeUndefined(); + expect(validateInRange(new Date('2000-01-01'), conf)).toEqual(new Date('2000-01-01')); }); diff --git a/tests/validators/date/before.ts b/tests/validators/date/before.ts index f04ef0f..9f58cd4 100644 --- a/tests/validators/date/before.ts +++ b/tests/validators/date/before.ts @@ -13,19 +13,19 @@ test('before when the date is before the date returns success', async () => { const end = new Date('2099-12-31'); const validateInRange = before(end); - await expect(validateInRange(new Date('2000-01-02'), conf)).resolves.toBeUndefined(); - await expect(validateInRange(new Date('2021-08-27'), conf)).resolves.toBeUndefined(); - await expect(validateInRange(new Date('2099-01-01'), conf)).resolves.toBeUndefined(); + expect(validateInRange(new Date('2000-01-02'), conf)).toEqual(new Date('2000-01-02')); + expect(validateInRange(new Date('2021-08-27'), conf)).toEqual(new Date('2021-08-27')); + expect(validateInRange(new Date('2099-01-01'), conf)).toEqual(new Date('2099-01-01')); }); test('before definition can convert from string or number', async () => { const validateInStringRange = before('2099-12-31'); - await expect(validateInStringRange(new Date('2021-08-27'), conf)).resolves.toBeUndefined(); + expect(validateInStringRange(new Date('2021-08-27'), conf)).toEqual(new Date('2021-08-27')); const validateInNumberRange = before(4102358400000); - await expect(validateInNumberRange(new Date('2021-08-27'), conf)).resolves.toBeUndefined(); + expect(validateInNumberRange(new Date('2021-08-27'), conf)).toEqual(new Date('2021-08-27')); }); test('before when the date is after or equal to the date, fails', async () => { @@ -33,19 +33,19 @@ test('before when the date is after or equal to the date, fails', async () => { const validateInRange = before(end); try { - await validateInRange(new Date('2222-12-31'), conf); + validateInRange(new Date('2222-12-31'), conf); } catch (err) { expect(err).toBe(`Field dateField should be before ${end.toString()}.`); } try { - await validateInRange(new Date('2099-12-31'), conf); + validateInRange(new Date('2099-12-31'), conf); } catch (err) { expect(err).toBe(`Field dateField should be before ${end.toString()}.`); } try { - await validateInRange(new Date('invalid'), conf); + validateInRange(new Date('invalid'), conf); } catch (err) { expect(err).toBe(`Field dateField should be before ${end.toString()}.`); } @@ -55,18 +55,5 @@ test('before, when inclusive is set, returns success for limit', async () => { const end = new Date('2099-12-31'); const validateInRange = before(end, { inclusive: true }); - await expect(validateInRange(new Date('2099-12-31'), conf)).resolves.toBeUndefined(); -}); - -test('before ignores non-date inputs', async () => { - const end = new Date('2099-12-31'); - const validateInRange = before(end); - - await expect(validateInRange('string' as any, conf)).resolves.toBeUndefined(); - await expect(validateInRange(123 as any, conf)).resolves.toBeUndefined(); - await expect(validateInRange(NaN as any, conf)).resolves.toBeUndefined(); - await expect(validateInRange({} as any, conf)).resolves.toBeUndefined(); - await expect(validateInRange([] as any, conf)).resolves.toBeUndefined(); - await expect(validateInRange(null as any, conf)).resolves.toBeUndefined(); - await expect(validateInRange(undefined as any, conf)).resolves.toBeUndefined(); + expect(validateInRange(new Date('2099-12-31'), conf)).toEqual(new Date('2099-12-31')); }); diff --git a/tests/validators/date/inRange.ts b/tests/validators/date/inRange.ts index 6012061..b26e114 100644 --- a/tests/validators/date/inRange.ts +++ b/tests/validators/date/inRange.ts @@ -14,19 +14,19 @@ test('inRange when the date is between the two dates returns success', async () const end = new Date('2099-12-31'); const validateInRange = inRange(begin, end); - await expect(validateInRange(new Date('2000-01-02'), conf)).resolves.toBeUndefined(); - await expect(validateInRange(new Date('2021-08-27'), conf)).resolves.toBeUndefined(); - await expect(validateInRange(new Date('2099-01-01'), conf)).resolves.toBeUndefined(); + expect(validateInRange(new Date('2000-01-02'), conf)).toEqual(new Date('2000-01-02')); + expect(validateInRange(new Date('2021-08-27'), conf)).toEqual(new Date('2021-08-27')); + expect(validateInRange(new Date('2099-01-01'), conf)).toEqual(new Date('2099-01-01')); }); test('inRange definition can convert from string or number', async () => { const validateInStringRange = inRange('2000-01-01', '2099-12-31'); - await expect(validateInStringRange(new Date('2021-08-27'), conf)).resolves.toBeUndefined(); + expect(validateInStringRange(new Date('2021-08-27'), conf)).toEqual(new Date('2021-08-27')); const validateInNumberRange = inRange(946684800000, 4102358400000); - await expect(validateInNumberRange(new Date('2021-08-27'), conf)).resolves.toBeUndefined(); + expect(validateInNumberRange(new Date('2021-08-27'), conf)).toEqual(new Date('2021-08-27')); }); test('inRange when the date is before, after or equal to the two dates, fails', async () => { @@ -35,7 +35,7 @@ test('inRange when the date is before, after or equal to the two dates, fails', const validateInRange = inRange(begin, end); try { - await validateInRange(new Date('1999-12-31'), conf); + validateInRange(new Date('1999-12-31'), conf); } catch (err) { expect(err).toBe( `Field dateField should be between ${begin.toString()} and ${end.toString()}.` @@ -43,7 +43,7 @@ test('inRange when the date is before, after or equal to the two dates, fails', } try { - await validateInRange(new Date('2222-12-31'), conf); + validateInRange(new Date('2222-12-31'), conf); } catch (err) { expect(err).toBe( `Field dateField should be between ${begin.toString()} and ${end.toString()}.` @@ -51,7 +51,7 @@ test('inRange when the date is before, after or equal to the two dates, fails', } try { - await validateInRange(new Date('2000-01-01'), conf); + validateInRange(new Date('2000-01-01'), conf); } catch (err) { expect(err).toBe( `Field dateField should be between ${begin.toString()} and ${end.toString()}.` @@ -59,7 +59,7 @@ test('inRange when the date is before, after or equal to the two dates, fails', } try { - await validateInRange(new Date('2099-12-31'), conf); + validateInRange(new Date('2099-12-31'), conf); } catch (err) { expect(err).toBe( `Field dateField should be between ${begin.toString()} and ${end.toString()}.` @@ -67,7 +67,7 @@ test('inRange when the date is before, after or equal to the two dates, fails', } try { - await validateInRange(new Date('invalid'), conf); + validateInRange(new Date('invalid'), conf); } catch (err) { expect(err).toBe( `Field dateField should be between ${begin.toString()} and ${end.toString()}.` @@ -80,10 +80,10 @@ test('inRange, when minInclusive is set, returns success for lower limit and fai const end = new Date('2099-12-31'); const validateInRange = inRange(begin, end, { minInclusive: true }); - await expect(validateInRange(new Date('2000-01-01'), conf)).resolves.toBeUndefined(); + expect(validateInRange(new Date('2000-01-01'), conf)).toEqual(new Date('2000-01-01')); try { - await validateInRange(new Date('2099-12-31'), conf); + validateInRange(new Date('2099-12-31'), conf); } catch (err) { expect(err).toBe( `Field dateField should be between ${begin.toString()} and ${end.toString()}.` @@ -97,14 +97,14 @@ test('inRange, when maxInclusive is set, fails for lower limit and returns succe const validateInRange = inRange(begin, end, { maxInclusive: true }); try { - await validateInRange(new Date('2000-01-01'), conf); + validateInRange(new Date('2000-01-01'), conf); } catch (err) { expect(err).toBe( `Field dateField should be between ${begin.toString()} and ${end.toString()}.` ); } - await expect(validateInRange(new Date('2099-12-31'), conf)).resolves.toBeUndefined(); + expect(validateInRange(new Date('2099-12-31'), conf)).toEqual(new Date('2099-12-31')); }); test('inRange, when both inclusives are set, returns success for and upper lower limit', async () => { @@ -112,20 +112,6 @@ test('inRange, when both inclusives are set, returns success for and upper lower const end = new Date('2099-12-31'); const validateInRange = inRange(begin, end, { minInclusive: true, maxInclusive: true }); - await expect(validateInRange(new Date('2000-01-01'), conf)).resolves.toBeUndefined(); - await expect(validateInRange(new Date('2099-12-31'), conf)).resolves.toBeUndefined(); -}); - -test('inRange ignores non-date inputs', async () => { - const begin = new Date('2000-01-01'); - const end = new Date('2099-12-31'); - const validateInRange = inRange(begin, end); - - await expect(validateInRange('string' as any, conf)).resolves.toBeUndefined(); - await expect(validateInRange(123 as any, conf)).resolves.toBeUndefined(); - await expect(validateInRange(NaN as any, conf)).resolves.toBeUndefined(); - await expect(validateInRange({} as any, conf)).resolves.toBeUndefined(); - await expect(validateInRange([] as any, conf)).resolves.toBeUndefined(); - await expect(validateInRange(null as any, conf)).resolves.toBeUndefined(); - await expect(validateInRange(undefined as any, conf)).resolves.toBeUndefined(); + expect(validateInRange(new Date('2000-01-01'), conf)).toEqual(new Date('2000-01-01')); + expect(validateInRange(new Date('2099-12-31'), conf)).toEqual(new Date('2099-12-31')); }); diff --git a/tests/validators/date/isDate.ts b/tests/validators/date/isDate.ts index 48f903f..b90cf77 100644 --- a/tests/validators/date/isDate.ts +++ b/tests/validators/date/isDate.ts @@ -15,7 +15,7 @@ test('isDate when date is passed returns successfully', async () => { const dateString = '2021-08-27'; const date = new Date(dateString); - await expect(sanitizeDate(date, conf)).resolves.toBeUndefined(); + expect(sanitizeDate(date, conf)).toEqual(date); }); test('isDate when invalid date is passed fails', async () => { @@ -24,7 +24,7 @@ test('isDate when invalid date is passed fails', async () => { const dateString = 'invalid'; const date = new Date(dateString); try { - await sanitizeDate(date, conf); + sanitizeDate(date, conf); } catch (err) { expect(err).toBe('Field dateField should be a valid date.'); } @@ -34,13 +34,13 @@ test('isDate when other data is passed fails', async () => { const sanitizeDate = isDate(); try { - await sanitizeDate({} as any, conf); + sanitizeDate({} as any, conf); } catch (err) { expect(err).toBe('Field dateField should be a valid date.'); } try { - await sanitizeDate([] as any, conf); + sanitizeDate([] as any, conf); } catch (err) { expect(err).toBe('Field dateField should be a valid date.'); } diff --git a/tests/validators/either.ts b/tests/validators/either.ts index a6f06f5..2ff67ec 100644 --- a/tests/validators/either.ts +++ b/tests/validators/either.ts @@ -1,47 +1,93 @@ import { expect, test } from '@jest/globals'; import either from '../../src/validators/either'; import validate from '../../src/validate'; -import { Failure, Success, ValidatorFunction } from '../../src/types'; +import { SyncValidatorFunction, ValidatorFunction } from '../../src/types'; -const testSuccessfulValidation = (): ValidatorFunction => - async function (_value, _conf) { - return Success(); +const testSuccessfulStringValidation = (): SyncValidatorFunction => + function (value, _conf) { + return value; + }; + +const testSuccessfulNumberValidation = (): SyncValidatorFunction => + function (value, _conf) { + return value; + }; + +const testSuccessfulStringValidationP = (): ValidatorFunction => + async function (value, _conf) { + return value; + }; + +const testSuccessfulNumberValidationP = (): ValidatorFunction => + async function (value, _conf) { + return value; }; -const testFailingValidation = (i: number): ValidatorFunction => +const testFailingValidation = (i: number): SyncValidatorFunction => + function (_value, _conf) { + throw `test ${i} failed`; + }; + +const testFailingValidationP = (i: number): ValidatorFunction => async function (_value, _conf) { - return Failure(`test ${i} failed`); + throw `test ${i} failed`; }; -const testTransformingValidation = (): ValidatorFunction => +const testTransformingValidation = (): ValidatorFunction => async function (value, _conf) { - return Success(value + 1); + return value + 1; }; -test('either function passes if both of the two validation passes', async () => { +test('either function passes if both of the two sync validation passes', async () => { const value = 1; - const validateTest = validate(either([testSuccessfulValidation(), testSuccessfulValidation()])); + const validateTest = validate( + either([testSuccessfulNumberValidation(), testSuccessfulNumberValidation()]) + ); + const result: number = validateTest(value); + expect(result).toEqual(value); +}); - await expect(validateTest(value)).resolves.toEqual(value); +test('either function passes if both of the two async validation passes', async () => { + const value = 1; + const validateTest = validate( + either([testSuccessfulNumberValidationP(), testSuccessfulNumberValidationP()]) + ); + const result: Promise = validateTest(value); + await expect(result).resolves.toEqual(value); }); -test('either function passes if any of the two validation passes', async () => { +test('either function passes if either of the two sync validation passes', async () => { const value = 1; - const validateTest = validate(either([testSuccessfulValidation(), testFailingValidation(1)])); + const validateTest = validate(either([testSuccessfulNumberValidation(), testSuccessfulStringValidation()])); + const result = validateTest(value); + // TODO: this type doesn't work + // const result: string | number = validateTest(value); + expect(result).toEqual(value); +}); - await expect(validateTest(value)).resolves.toEqual(value); +test('either function passes if both of the two async validation passes', async () => { + const value = 1; + const validateTest = validate( + either([testSuccessfulNumberValidationP(), testSuccessfulStringValidationP()]) + ); + const result: Promise = validateTest(value); + await expect(result).resolves.toEqual(value); }); test('either function resolves to first if the first is transformation', async () => { const value = 1; - const validateTest = validate(either([testTransformingValidation(), testSuccessfulValidation()])); + const validateTest = validate( + either([testTransformingValidation(), testSuccessfulNumberValidationP()]) + ); await expect(validateTest(value)).resolves.toEqual(value + 1); }); test('either function resolves to itself if the second one is a transformation', async () => { const value = 1; - const validateTest = validate(either([testSuccessfulValidation(), testTransformingValidation()])); + const validateTest = validate( + either([testSuccessfulNumberValidationP(), testTransformingValidation()]) + ); await expect(validateTest(value)).resolves.toEqual(value); }); @@ -55,10 +101,21 @@ test('either function resolves the transformation only once if both are a transf await expect(validateTest(value)).resolves.toEqual(value + 1); }); -test('either function throws error if both of the two validation fails', async () => { +test('either function throws error if both of the two sync validation fails', async () => { const value = 1; const validateTest = validate(either([testFailingValidation(1), testFailingValidation(2)])); + try { + validateTest(value); + } catch (err) { + expect(err).toEqual(['test 1 failed', 'test 2 failed']); + } +}); + +test('either function throws error if both of the two async validation fails', async () => { + const value = 1; + const validateTest = validate(either([testFailingValidationP(1), testFailingValidationP(2)])); + try { await validateTest(value); } catch (err) { diff --git a/tests/validators/equals.ts b/tests/validators/equals.ts index 5f20ad9..e41a957 100644 --- a/tests/validators/equals.ts +++ b/tests/validators/equals.ts @@ -11,71 +11,71 @@ const conf: ValidatorConfiguration = { test('equals validator if the value is equals to the param, returns successfully', async () => { const validateStringEquality = equals('asdasd'); - await expect(validateStringEquality('asdasd', conf)).resolves.toBeUndefined(); + expect(validateStringEquality('asdasd', conf)).toEqual('asdasd'); const validateNumberEquality = equals(100); - await expect(validateNumberEquality(100, conf)).resolves.toBeUndefined(); + expect(validateNumberEquality(100, conf)).toEqual(100); const validateBoolEquality = equals(true); - await expect(validateBoolEquality(true, conf)).resolves.toBeUndefined(); + expect(validateBoolEquality(true, conf)).toEqual(true); const obj = {}; const validateObjectEquality = equals(obj); - await expect(validateObjectEquality(obj, conf)).resolves.toBeUndefined(); + expect(validateObjectEquality(obj, conf)).toEqual(obj); const validateNullEquality = equals(null); - await expect(validateNullEquality(null, conf)).resolves.toBeUndefined(); + expect(validateNullEquality(null, conf)).toEqual(null); const validateUndefinedEquality = equals(undefined); - await expect(validateUndefinedEquality(undefined, conf)).resolves.toBeUndefined(); + expect(validateUndefinedEquality(undefined, conf)).toEqual(undefined); }); test('equals validator if the value is not equals, fails', async () => { const validateStringEquality = equals('asdasd'); try { - await validateStringEquality('dsadsa', conf); + validateStringEquality('dsadsa', conf); } catch (err) { expect(err).toBe('Field strField should be equal to asdasd.'); } const validateCapitalizedStringEquality = equals('asdasd'); try { - await validateCapitalizedStringEquality('ASDASD', conf); + validateCapitalizedStringEquality('ASDASD', conf); } catch (err) { expect(err).toBe('Field strField should be equal to asdasd.'); } const validateNumberEquality = equals(100); try { - await validateNumberEquality(101, conf); + validateNumberEquality(101, conf); } catch (err) { expect(err).toBe('Field strField should be equal to 100.'); } const validateFloatEquality = equals(0.3); try { - await validateFloatEquality(0.1 + 0.2, conf); + validateFloatEquality(0.1 + 0.2, conf); } catch (err) { expect(err).toBe('Field strField should be equal to 0.3.'); } const validateBoolEquality = equals(true); try { - await validateBoolEquality(false, conf); + validateBoolEquality(false, conf); } catch (err) { expect(err).toBe('Field strField should be equal to true.'); } const validateNaNEquality = equals(NaN); try { - await validateNaNEquality(NaN, conf); + validateNaNEquality(NaN, conf); } catch (err) { expect(err).toBe('Field strField should be equal to NaN.'); } const validateObjectEquality = equals({}); try { - await validateObjectEquality({}, conf); + validateObjectEquality({}, conf); } catch (err) { expect(err).toBe('Field strField should be equal to [object Object].'); } diff --git a/tests/validators/number/between.ts b/tests/validators/number/between.ts index cfb7314..ecd173f 100644 --- a/tests/validators/number/between.ts +++ b/tests/validators/number/between.ts @@ -12,9 +12,9 @@ const conf: ValidatorConfiguration = { test('between, when the number is between the two values, return success', async () => { const validateBetween = between(1, 10); - await expect(validateBetween(2, conf)).resolves.toBeUndefined(); - await expect(validateBetween(5, conf)).resolves.toBeUndefined(); - await expect(validateBetween(8, conf)).resolves.toBeUndefined(); + await expect(validateBetween(2, conf)).toEqual(2); + await expect(validateBetween(5, conf)).toEqual(5); + await expect(validateBetween(8, conf)).toEqual(8); }); test('between, when the number is not between or equal the two values, throws', async () => { @@ -60,7 +60,7 @@ test('between, when the number is not between or equal the two values, throws', test('between, when min inclusive is set, returns success for min and failure for max', async () => { const validateBetween = between(1, 10, { minInclusive: true }); - await expect(validateBetween(1, conf)).resolves.toBeUndefined(); + await expect(validateBetween(1, conf)).toEqual(1); try { await validateBetween(10, conf); @@ -72,7 +72,7 @@ test('between, when min inclusive is set, returns success for min and failure fo test('between, when max inclusive is set, returns failure for min and success for max', async () => { const validateBetween = between(1, 10, { maxInclusive: true }); - await expect(validateBetween(10, conf)).resolves.toBeUndefined(); + await expect(validateBetween(10, conf)).toEqual(10); try { await validateBetween(1, conf); @@ -84,16 +84,6 @@ test('between, when max inclusive is set, returns failure for min and success fo test('between, when both inclusive is set, returns success for both limits', async () => { const validateBetween = between(1, 10, { minInclusive: true, maxInclusive: true }); - await expect(validateBetween(1, conf)).resolves.toBeUndefined(); - await expect(validateBetween(10, conf)).resolves.toBeUndefined(); -}); - -test('between ignores non-number inputs', async () => { - const validateBetween = between(1, 10); - - await expect(validateBetween('6' as any, conf)).resolves.toBeUndefined(); - await expect(validateBetween(NaN as any, conf)).resolves.toBeUndefined(); - await expect(validateBetween(undefined as any, conf)).resolves.toBeUndefined(); - await expect(validateBetween(null as any, conf)).resolves.toBeUndefined(); - await expect(validateBetween({} as any, conf)).resolves.toBeUndefined(); + await expect(validateBetween(1, conf)).toEqual(1); + await expect(validateBetween(10, conf)).toEqual(10); }); diff --git a/tests/validators/number/closeTo.ts b/tests/validators/number/closeTo.ts index 9057f98..ec92e2f 100644 --- a/tests/validators/number/closeTo.ts +++ b/tests/validators/number/closeTo.ts @@ -12,9 +12,9 @@ const conf: ValidatorConfiguration = { test('closeTo when the two values are closer than an epsilon, returns the exact value', async () => { const validateCloseTo = closeTo(0.3); - await expect(validateCloseTo(0.3, conf)).resolves.toBe(0.3); - await expect(validateCloseTo(0.3 + 1e-16, conf)).resolves.toBe(0.3); - await expect(validateCloseTo(0.2 + 0.1, conf)).resolves.toBe(0.3); + await expect(validateCloseTo(0.3, conf)).toBe(0.3); + await expect(validateCloseTo(0.3 + 1e-16, conf)).toBe(0.3); + await expect(validateCloseTo(0.2 + 0.1, conf)).toBe(0.3); }); test('closeTo when the two values are farther than an epsilon, fails', async () => { @@ -48,10 +48,10 @@ test('closeTo when the two values are farther than an epsilon, fails', async () test('closeTo when can configure the epsilon', async () => { const validateCloseTo = closeTo(0.3, 0.001); - await expect(validateCloseTo(0.3, conf)).resolves.toBe(0.3); - await expect(validateCloseTo(0.3 + 1e-10, conf)).resolves.toBe(0.3); - await expect(validateCloseTo(0.2 + 0.1, conf)).resolves.toBe(0.3); - await expect(validateCloseTo(0.3 - 0.0001, conf)).resolves.toBe(0.3); + await expect(validateCloseTo(0.3, conf)).toBe(0.3); + await expect(validateCloseTo(0.3 + 1e-10, conf)).toBe(0.3); + await expect(validateCloseTo(0.2 + 0.1, conf)).toBe(0.3); + await expect(validateCloseTo(0.3 - 0.0001, conf)).toBe(0.3); try { await validateCloseTo(0.4, conf); @@ -59,11 +59,3 @@ test('closeTo when can configure the epsilon', async () => { expect(err).toBe('Field numField should be approximately 0.3.'); } }); - -test('closeTo when the passed value is not a number, ignores', async () => { - const validateCloseTo = closeTo(0.3); - - await expect(validateCloseTo('string' as any, conf)).resolves.toBeUndefined(); - await expect(validateCloseTo(null as any, conf)).resolves.toBeUndefined(); - await expect(validateCloseTo({} as any, conf)).resolves.toBeUndefined(); -}); diff --git a/tests/validators/number/gt.ts b/tests/validators/number/gt.ts index 80f6659..cf399e0 100644 --- a/tests/validators/number/gt.ts +++ b/tests/validators/number/gt.ts @@ -12,10 +12,10 @@ const conf: ValidatorConfiguration = { test('gt when the number is greater returns success', async () => { const validateGreaterThanTen = gt(10); - await expect(validateGreaterThanTen(20, conf)).resolves.toBeUndefined(); - await expect(validateGreaterThanTen(500, conf)).resolves.toBeUndefined(); - await expect(validateGreaterThanTen(1000, conf)).resolves.toBeUndefined(); - await expect(validateGreaterThanTen(Infinity, conf)).resolves.toBeUndefined(); + await expect(validateGreaterThanTen(20, conf)).toEqual(20); + await expect(validateGreaterThanTen(500, conf)).toEqual(500); + await expect(validateGreaterThanTen(1000, conf)).toEqual(1000); + await expect(validateGreaterThanTen(Infinity, conf)).toEqual(Infinity); }); test('gt when the number is lower or equal fails', async () => { @@ -42,13 +42,3 @@ test('gt when the number is lower or equal fails', async () => { expect(err).toBe('Field numField should be greater than 10.'); } }); - -test('gt ignores non-number inputs', async () => { - const validateGreaterThanTen = gt(10); - - await expect(validateGreaterThanTen('6' as any, conf)).resolves.toBeUndefined(); - await expect(validateGreaterThanTen(NaN as any, conf)).resolves.toBeUndefined(); - await expect(validateGreaterThanTen(undefined as any, conf)).resolves.toBeUndefined(); - await expect(validateGreaterThanTen(null as any, conf)).resolves.toBeUndefined(); - await expect(validateGreaterThanTen({} as any, conf)).resolves.toBeUndefined(); -}); diff --git a/tests/validators/number/gte.ts b/tests/validators/number/gte.ts index 878ffdb..0aac2cf 100644 --- a/tests/validators/number/gte.ts +++ b/tests/validators/number/gte.ts @@ -12,11 +12,11 @@ const conf: ValidatorConfiguration = { test('gte when the number is greater or equal returns success', async () => { const validateGreaterThanOrEqualToTen = gte(10); - await expect(validateGreaterThanOrEqualToTen(10, conf)).resolves.toBeUndefined(); - await expect(validateGreaterThanOrEqualToTen(20, conf)).resolves.toBeUndefined(); - await expect(validateGreaterThanOrEqualToTen(500, conf)).resolves.toBeUndefined(); - await expect(validateGreaterThanOrEqualToTen(1000, conf)).resolves.toBeUndefined(); - await expect(validateGreaterThanOrEqualToTen(Infinity, conf)).resolves.toBeUndefined(); + await expect(validateGreaterThanOrEqualToTen(10, conf)).toEqual(10); + await expect(validateGreaterThanOrEqualToTen(20, conf)).toEqual(20); + await expect(validateGreaterThanOrEqualToTen(500, conf)).toEqual(500); + await expect(validateGreaterThanOrEqualToTen(1000, conf)).toEqual(1000); + await expect(validateGreaterThanOrEqualToTen(Infinity, conf)).toEqual(Infinity); }); test('gte when the number is lower fails', async () => { @@ -38,13 +38,3 @@ test('gte when the number is lower fails', async () => { expect(err).toBe('Field numField should be greater than or equal to 10.'); } }); - -test('gte ignores non-number inputs', async () => { - const validateGreaterThanOrEqualToTen = gte(10); - - await expect(validateGreaterThanOrEqualToTen('6' as any, conf)).resolves.toBeUndefined(); - await expect(validateGreaterThanOrEqualToTen(NaN as any, conf)).resolves.toBeUndefined(); - await expect(validateGreaterThanOrEqualToTen(undefined as any, conf)).resolves.toBeUndefined(); - await expect(validateGreaterThanOrEqualToTen(null as any, conf)).resolves.toBeUndefined(); - await expect(validateGreaterThanOrEqualToTen({} as any, conf)).resolves.toBeUndefined(); -}); diff --git a/tests/validators/number/isInt.ts b/tests/validators/number/isInt.ts index f84aac9..bc57141 100644 --- a/tests/validators/number/isInt.ts +++ b/tests/validators/number/isInt.ts @@ -12,10 +12,10 @@ const conf: ValidatorConfiguration = { test('isInt, when integer is passed, returns success', async () => { const validateInteger = isInt(); - await expect(validateInteger(-1, conf)).resolves.toBeUndefined(); - await expect(validateInteger(0, conf)).resolves.toBeUndefined(); - await expect(validateInteger(123, conf)).resolves.toBeUndefined(); - await expect(validateInteger(1e10, conf)).resolves.toBeUndefined(); + await expect(validateInteger(-1, conf)).toEqual(-1); + await expect(validateInteger(0, conf)).toEqual(0); + await expect(validateInteger(123, conf)).toEqual(123); + await expect(validateInteger(1e10, conf)).toEqual(1e10); }); test('isInt, when not an integer is passed, fails', async () => { diff --git a/tests/validators/number/isMultipleOf.ts b/tests/validators/number/isMultipleOf.ts index 4e2775c..622d325 100644 --- a/tests/validators/number/isMultipleOf.ts +++ b/tests/validators/number/isMultipleOf.ts @@ -12,9 +12,9 @@ const conf: ValidatorConfiguration = { test('isMultipleOf when the number is multiple of the given param returns success', async () => { const validateMultipleOfFive = isMultipleOf(5); - await expect(validateMultipleOfFive(10, conf)).resolves.toBeUndefined(); - await expect(validateMultipleOfFive(0, conf)).resolves.toBeUndefined(); - await expect(validateMultipleOfFive(-5, conf)).resolves.toBeUndefined(); + await expect(validateMultipleOfFive(10, conf)).toEqual(10); + await expect(validateMultipleOfFive(0, conf)).toEqual(0); + await expect(validateMultipleOfFive(-5, conf)).toEqual(-5); }); test('isMultipleOf when the number is not the multiple of the given param fails', async () => { @@ -42,12 +42,3 @@ test('isMultipleOf when the number is not the multiple of the given param fails' } }); -test('isMultipleOf ignores non-number inputs', async () => { - const validateMultipleOfFive = isMultipleOf(5); - - await expect(validateMultipleOfFive('10' as any, conf)).resolves.toBeUndefined(); - await expect(validateMultipleOfFive(NaN as any, conf)).resolves.toBeUndefined(); - await expect(validateMultipleOfFive(undefined as any, conf)).resolves.toBeUndefined(); - await expect(validateMultipleOfFive(null as any, conf)).resolves.toBeUndefined(); - await expect(validateMultipleOfFive({} as any, conf)).resolves.toBeUndefined(); -}); diff --git a/tests/validators/number/isNumber.ts b/tests/validators/number/isNumber.ts index dc6068d..c6d4533 100644 --- a/tests/validators/number/isNumber.ts +++ b/tests/validators/number/isNumber.ts @@ -12,14 +12,14 @@ const conf: ValidatorConfiguration = { test('isNumber, when number is passed, returns success', async () => { const validateNumber = isNumber(); - await expect(validateNumber(-1, conf)).resolves.toBeUndefined(); - await expect(validateNumber(0, conf)).resolves.toBeUndefined(); - await expect(validateNumber(123, conf)).resolves.toBeUndefined(); - await expect(validateNumber(1e10, conf)).resolves.toBeUndefined(); - await expect(validateNumber(123.45, conf)).resolves.toBeUndefined(); - await expect(validateNumber(1 / 10, conf)).resolves.toBeUndefined(); - await expect(validateNumber(Infinity, conf)).resolves.toBeUndefined(); - await expect(validateNumber(-Infinity, conf)).resolves.toBeUndefined(); + await expect(validateNumber(-1, conf)).toEqual(-1); + await expect(validateNumber(0, conf)).toEqual(0); + await expect(validateNumber(123, conf)).toEqual(123); + await expect(validateNumber(1e10, conf)).toEqual(1e10); + await expect(validateNumber(123.45, conf)).toEqual(123.45); + await expect(validateNumber(1 / 10, conf)).toEqual(1 / 10); + await expect(validateNumber(Infinity, conf)).toEqual(Infinity); + await expect(validateNumber(-Infinity, conf)).toEqual(-Infinity); }); test('isNumber, when not a number is passed, fails', async () => { diff --git a/tests/validators/number/lt.ts b/tests/validators/number/lt.ts index e5d3a8f..f8097d3 100644 --- a/tests/validators/number/lt.ts +++ b/tests/validators/number/lt.ts @@ -12,9 +12,9 @@ const conf: ValidatorConfiguration = { test('lt when the number is lower returns success', async () => { const validateLessThanTen = lt(10); - await expect(validateLessThanTen(0, conf)).resolves.toBeUndefined(); - await expect(validateLessThanTen(-5, conf)).resolves.toBeUndefined(); - await expect(validateLessThanTen(-Infinity, conf)).resolves.toBeUndefined(); + await expect(validateLessThanTen(0, conf)).toEqual(0); + await expect(validateLessThanTen(-5, conf)).toEqual(-5); + await expect(validateLessThanTen(-Infinity, conf)).toEqual(-Infinity); }); test('lt when the number is greater or equal fails', async () => { @@ -36,13 +36,3 @@ test('lt when the number is greater or equal fails', async () => { expect(err).toBe('Field numField should be less than 10.'); } }); - -test('lt ignores non-number inputs', async () => { - const validateLessThanTen = lt(10); - - await expect(validateLessThanTen('6' as any, conf)).resolves.toBeUndefined(); - await expect(validateLessThanTen(NaN as any, conf)).resolves.toBeUndefined(); - await expect(validateLessThanTen(undefined as any, conf)).resolves.toBeUndefined(); - await expect(validateLessThanTen(null as any, conf)).resolves.toBeUndefined(); - await expect(validateLessThanTen({} as any, conf)).resolves.toBeUndefined(); -}); diff --git a/tests/validators/number/lte.ts b/tests/validators/number/lte.ts index 05c574e..b83b69c 100644 --- a/tests/validators/number/lte.ts +++ b/tests/validators/number/lte.ts @@ -12,10 +12,10 @@ const conf: ValidatorConfiguration = { test('lte when the number is less or equal returns success', async () => { const validateLessThanOrEqualToTen = lte(10); - await expect(validateLessThanOrEqualToTen(10, conf)).resolves.toBeUndefined(); - await expect(validateLessThanOrEqualToTen(0, conf)).resolves.toBeUndefined(); - await expect(validateLessThanOrEqualToTen(-5, conf)).resolves.toBeUndefined(); - await expect(validateLessThanOrEqualToTen(-Infinity, conf)).resolves.toBeUndefined(); + await expect(validateLessThanOrEqualToTen(10, conf)).toEqual(10); + await expect(validateLessThanOrEqualToTen(0, conf)).toEqual(0); + await expect(validateLessThanOrEqualToTen(-5, conf)).toEqual(-5); + await expect(validateLessThanOrEqualToTen(-Infinity, conf)).toEqual(-Infinity); }); test('lte when the number is lower fails', async () => { @@ -32,13 +32,3 @@ test('lte when the number is lower fails', async () => { expect(err).toBe('Field numField should be less than or equal to 10.'); } }); - -test('lte ignores non-number inputs', async () => { - const validateLessThanOrEqualToTen = lte(10); - - await expect(validateLessThanOrEqualToTen('6' as any, conf)).resolves.toBeUndefined(); - await expect(validateLessThanOrEqualToTen(NaN as any, conf)).resolves.toBeUndefined(); - await expect(validateLessThanOrEqualToTen(undefined as any, conf)).resolves.toBeUndefined(); - await expect(validateLessThanOrEqualToTen(null as any, conf)).resolves.toBeUndefined(); - await expect(validateLessThanOrEqualToTen({} as any, conf)).resolves.toBeUndefined(); -}); diff --git a/tests/validators/number/toInt.ts b/tests/validators/number/toInt.ts index 40687d8..02d1665 100644 --- a/tests/validators/number/toInt.ts +++ b/tests/validators/number/toInt.ts @@ -12,36 +12,36 @@ const conf: ValidatorConfiguration = { test('toInt, when number is passed, returns the value', async () => { const validateInteger = toInt(); - await expect(validateInteger(-1, conf)).resolves.toBe(-1); - await expect(validateInteger(0, conf)).resolves.toBe(0); - await expect(validateInteger(123, conf)).resolves.toBe(123); - await expect(validateInteger(1e10, conf)).resolves.toBe(1e10); + await expect(validateInteger(-1, conf)).toBe(-1); + await expect(validateInteger(0, conf)).toBe(0); + await expect(validateInteger(123, conf)).toBe(123); + await expect(validateInteger(1e10, conf)).toBe(1e10); }); test('toInt, when numeric string is passed, returns the parsed value', async () => { const validateInteger = toInt(); - await expect(validateInteger('-1' as any, conf)).resolves.toBe(-1); - await expect(validateInteger('0' as any, conf)).resolves.toBe(0); - await expect(validateInteger('123' as any, conf)).resolves.toBe(123); + await expect(validateInteger('-1' as any, conf)).toBe(-1); + await expect(validateInteger('0' as any, conf)).toBe(0); + await expect(validateInteger('123' as any, conf)).toBe(123); }); test('tryInt, when decimal value is passed, returns the value floored to the integer part', async () => { const validateInteger = toInt(); - await expect(validateInteger(1.1 as any, conf)).resolves.toBe(1); - await expect(validateInteger('1.1' as any, conf)).resolves.toBe(1); - await expect(validateInteger('123.99' as any, conf)).resolves.toBe(123); + await expect(validateInteger(1.1 as any, conf)).toBe(1); + await expect(validateInteger('1.1' as any, conf)).toBe(1); + await expect(validateInteger('123.99' as any, conf)).toBe(123); }); test('toInt, when not convertible value is passed, returns NaN', async () => { const validateInteger = toInt(); - await expect(validateInteger('string' as any, conf)).resolves.toBe(NaN); - await expect(validateInteger(Infinity, conf)).resolves.toBe(NaN); - await expect(validateInteger(-Infinity, conf)).resolves.toBe(NaN); - await expect(validateInteger(NaN as any, conf)).resolves.toBe(NaN); - await expect(validateInteger([] as any, conf)).resolves.toBe(NaN); - await expect(validateInteger({} as any, conf)).resolves.toBe(NaN); - await expect(validateInteger(null as any, conf)).resolves.toBe(NaN); + await expect(validateInteger('string' as any, conf)).toBe(NaN); + await expect(validateInteger(Infinity, conf)).toBe(NaN); + await expect(validateInteger(-Infinity, conf)).toBe(NaN); + await expect(validateInteger(NaN as any, conf)).toBe(NaN); + await expect(validateInteger([] as any, conf)).toBe(NaN); + await expect(validateInteger({} as any, conf)).toBe(NaN); + await expect(validateInteger(null as any, conf)).toBe(NaN); }); diff --git a/tests/validators/number/toNumber.ts b/tests/validators/number/toNumber.ts index 08a51e5..df00af7 100644 --- a/tests/validators/number/toNumber.ts +++ b/tests/validators/number/toNumber.ts @@ -12,32 +12,32 @@ const conf: ValidatorConfiguration = { test('toNumber, when number is passed, returns the value', async () => { const validateNumber = toNumber(); - await expect(validateNumber(-1, conf)).resolves.toBe(-1); - await expect(validateNumber(0, conf)).resolves.toBe(0); - await expect(validateNumber(123, conf)).resolves.toBe(123); - await expect(validateNumber(1e10, conf)).resolves.toBe(1e10); - await expect(validateNumber(123.45, conf)).resolves.toBe(123.45); - await expect(validateNumber(1 / 10, conf)).resolves.toBe(1 / 10); - await expect(validateNumber(Infinity, conf)).resolves.toBe(Infinity); - await expect(validateNumber(-Infinity, conf)).resolves.toBe(-Infinity); + await expect(validateNumber(-1, conf)).toBe(-1); + await expect(validateNumber(0, conf)).toBe(0); + await expect(validateNumber(123, conf)).toBe(123); + await expect(validateNumber(1e10, conf)).toBe(1e10); + await expect(validateNumber(123.45, conf)).toBe(123.45); + await expect(validateNumber(1 / 10, conf)).toBe(1 / 10); + await expect(validateNumber(Infinity, conf)).toBe(Infinity); + await expect(validateNumber(-Infinity, conf)).toBe(-Infinity); }); test('toNumber, when numeric string is passed, returns the parsed value', async () => { const validateNumber = toNumber(); - await expect(validateNumber('-1' as any, conf)).resolves.toBe(-1); - await expect(validateNumber('0' as any, conf)).resolves.toBe(0); - await expect(validateNumber('123' as any, conf)).resolves.toBe(123); - await expect(validateNumber('1e10' as any, conf)).resolves.toBe(1e10); - await expect(validateNumber('123.45' as any, conf)).resolves.toBe(123.45); + await expect(validateNumber('-1' as any, conf)).toBe(-1); + await expect(validateNumber('0' as any, conf)).toBe(0); + await expect(validateNumber('123' as any, conf)).toBe(123); + await expect(validateNumber('1e10' as any, conf)).toBe(1e10); + await expect(validateNumber('123.45' as any, conf)).toBe(123.45); }); test('toNumber, when not convertible value is passed, returns NaN', async () => { const validateNumber = toNumber(); - await expect(validateNumber('string' as any, conf)).resolves.toBe(NaN); - await expect(validateNumber(NaN as any, conf)).resolves.toBe(NaN); - await expect(validateNumber([] as any, conf)).resolves.toBe(NaN); - await expect(validateNumber({} as any, conf)).resolves.toBe(NaN); - await expect(validateNumber(null as any, conf)).resolves.toBe(NaN); + await expect(validateNumber('string' as any, conf)).toBe(NaN); + await expect(validateNumber(NaN as any, conf)).toBe(NaN); + await expect(validateNumber([] as any, conf)).toBe(NaN); + await expect(validateNumber({} as any, conf)).toBe(NaN); + await expect(validateNumber(null as any, conf)).toBe(NaN); }); diff --git a/tests/validators/number/tryInt.ts b/tests/validators/number/tryInt.ts index 106eff0..905a1a0 100644 --- a/tests/validators/number/tryInt.ts +++ b/tests/validators/number/tryInt.ts @@ -12,27 +12,27 @@ const conf: ValidatorConfiguration = { test('tryInt, when integer is passed, returns the value', async () => { const validateInteger = tryInt(); - await expect(validateInteger(-1, conf)).resolves.toBe(-1); - await expect(validateInteger(0, conf)).resolves.toBe(0); - await expect(validateInteger(123, conf)).resolves.toBe(123); - await expect(validateInteger(1e10, conf)).resolves.toBe(1e10); + await expect(validateInteger(-1, conf)).toBe(-1); + await expect(validateInteger(0, conf)).toBe(0); + await expect(validateInteger(123, conf)).toBe(123); + await expect(validateInteger(1e10, conf)).toBe(1e10); }); test('tryInt, when numeric string is passed, returns the parsed value', async () => { const validateInteger = tryInt(); - await expect(validateInteger('-1' as any, conf)).resolves.toBe(-1); - await expect(validateInteger('0' as any, conf)).resolves.toBe(0); - await expect(validateInteger('123' as any, conf)).resolves.toBe(123); + await expect(validateInteger('-1' as any, conf)).toBe(-1); + await expect(validateInteger('0' as any, conf)).toBe(0); + await expect(validateInteger('123' as any, conf)).toBe(123); }); test('tryInt, when decimal value is passed, returns the value floored to the integer part', async () => { const validateInteger = tryInt(); - await expect(validateInteger(1.1 as any, conf)).resolves.toBe(1); - await expect(validateInteger('1.1' as any, conf)).resolves.toBe(1); - await expect(validateInteger('123.99' as any, conf)).resolves.toBe(123); - await expect(validateInteger('-123.99' as any, conf)).resolves.toBe(-123); + await expect(validateInteger(1.1 as any, conf)).toBe(1); + await expect(validateInteger('1.1' as any, conf)).toBe(1); + await expect(validateInteger('123.99' as any, conf)).toBe(123); + await expect(validateInteger('-123.99' as any, conf)).toBe(-123); }); test('tryInt, when not an integer is passed, fails', async () => { diff --git a/tests/validators/number/tryNumber.ts b/tests/validators/number/tryNumber.ts index d6d0e0b..e9a073e 100644 --- a/tests/validators/number/tryNumber.ts +++ b/tests/validators/number/tryNumber.ts @@ -12,24 +12,24 @@ const conf: ValidatorConfiguration = { test('tryNumber, when number is passed, returns the value', async () => { const validateNumber = tryNumber(); - await expect(validateNumber(-1, conf)).resolves.toBe(-1); - await expect(validateNumber(0, conf)).resolves.toBe(0); - await expect(validateNumber(123, conf)).resolves.toBe(123); - await expect(validateNumber(1e10, conf)).resolves.toBe(1e10); - await expect(validateNumber(123.45, conf)).resolves.toBe(123.45); - await expect(validateNumber(1 / 10, conf)).resolves.toBe(1 / 10); - await expect(validateNumber(Infinity, conf)).resolves.toBe(Infinity); - await expect(validateNumber(-Infinity, conf)).resolves.toBe(-Infinity); + await expect(validateNumber(-1, conf)).toBe(-1); + await expect(validateNumber(0, conf)).toBe(0); + await expect(validateNumber(123, conf)).toBe(123); + await expect(validateNumber(1e10, conf)).toBe(1e10); + await expect(validateNumber(123.45, conf)).toBe(123.45); + await expect(validateNumber(1 / 10, conf)).toBe(1 / 10); + await expect(validateNumber(Infinity, conf)).toBe(Infinity); + await expect(validateNumber(-Infinity, conf)).toBe(-Infinity); }); test('tryNumber, when numeric string is passed, returns the parsed value', async () => { const validateNumber = tryNumber(); - await expect(validateNumber('-1' as any, conf)).resolves.toBe(-1); - await expect(validateNumber('0' as any, conf)).resolves.toBe(0); - await expect(validateNumber('123' as any, conf)).resolves.toBe(123); - await expect(validateNumber('1e10' as any, conf)).resolves.toBe(1e10); - await expect(validateNumber('123.45' as any, conf)).resolves.toBe(123.45); + await expect(validateNumber('-1' as any, conf)).toBe(-1); + await expect(validateNumber('0' as any, conf)).toBe(0); + await expect(validateNumber('123' as any, conf)).toBe(123); + await expect(validateNumber('1e10' as any, conf)).toBe(1e10); + await expect(validateNumber('123.45' as any, conf)).toBe(123.45); }); test('tryNumber, when not a number is passed, fails', async () => { diff --git a/tests/validators/oneOf.ts b/tests/validators/oneOf.ts index a8a3338..9114141 100644 --- a/tests/validators/oneOf.ts +++ b/tests/validators/oneOf.ts @@ -11,81 +11,81 @@ const conf: ValidatorConfiguration = { test('oneOf validator if the value is one of the list, returns successfully', async () => { const validateStringEquality = oneOf(['one', 'two', 'three']); - await expect(validateStringEquality('one', conf)).resolves.toBeUndefined(); - await expect(validateStringEquality('two', conf)).resolves.toBeUndefined(); - await expect(validateStringEquality('three', conf)).resolves.toBeUndefined(); + expect(validateStringEquality('one', conf)).toEqual('one'); + expect(validateStringEquality('two', conf)).toEqual('two'); + expect(validateStringEquality('three', conf)).toEqual('three'); const validateNumberEquality = oneOf([100, 101, 102]); - await expect(validateNumberEquality(100, conf)).resolves.toBeUndefined(); - await expect(validateNumberEquality(101, conf)).resolves.toBeUndefined(); - await expect(validateNumberEquality(102, conf)).resolves.toBeUndefined(); + expect(validateNumberEquality(100, conf)).toEqual(100); + expect(validateNumberEquality(101, conf)).toEqual(101); + expect(validateNumberEquality(102, conf)).toEqual(102); const validateBoolEquality = oneOf([true, false]); - await expect(validateBoolEquality(true, conf)).resolves.toBeUndefined(); - await expect(validateBoolEquality(false, conf)).resolves.toBeUndefined(); + expect(validateBoolEquality(true, conf)).toEqual(true); + expect(validateBoolEquality(false, conf)).toEqual(false); const obj = {}; const validateObjectEquality = oneOf([obj]); - await expect(validateObjectEquality(obj, conf)).resolves.toBeUndefined(); + expect(validateObjectEquality(obj, conf)).toEqual(obj); const validateNullEquality = oneOf([null, undefined]); - await expect(validateNullEquality(null, conf)).resolves.toBeUndefined(); - await expect(validateNullEquality(undefined, conf)).resolves.toBeUndefined(); + expect(validateNullEquality(null, conf)).toEqual(null); + expect(validateNullEquality(undefined, conf)).toEqual(undefined); }); test('oneOf validator if the value is not in the list, fails', async () => { const validateStringEquality = oneOf(['one', 'two', 'three']); try { - await validateStringEquality('four', conf); + validateStringEquality('four', conf); } catch (err) { expect(err).toBe('Field strField should be one of one, two, three.'); } const validateCapitalizedStringEquality = oneOf(['one', 'two', 'three']); try { - await validateCapitalizedStringEquality('ONE', conf); + validateCapitalizedStringEquality('ONE', conf); } catch (err) { expect(err).toBe('Field strField should be one of one, two, three.'); } const validateEmptyStringEquality = oneOf(['one', 'two', 'three']); try { - await validateEmptyStringEquality('', conf); + validateEmptyStringEquality('', conf); } catch (err) { expect(err).toBe('Field strField should be one of one, two, three.'); } const validateNumberEquality = oneOf([100, 101, 102]); try { - await validateNumberEquality(99, conf); + validateNumberEquality(99, conf); } catch (err) { expect(err).toBe('Field strField should be one of 100, 101, 102.'); } const validateFloatNumberEquality = oneOf([0.1, 0.2, 0.3]); try { - await validateFloatNumberEquality(0.1 + 0.2, conf); + validateFloatNumberEquality(0.1 + 0.2, conf); } catch (err) { expect(err).toBe('Field strField should be one of 0.1, 0.2, 0.3.'); } const validateBoolEquality = oneOf([true]); try { - await validateBoolEquality(false, conf); + validateBoolEquality(false, conf); } catch (err) { expect(err).toBe('Field strField should be one of true.'); } const validateNaNEquality = oneOf([NaN]); try { - await validateNaNEquality(NaN, conf); + validateNaNEquality(NaN, conf); } catch (err) { expect(err).toBe('Field strField should be one of NaN.'); } const validateObjectEquality = oneOf([{}]); try { - await validateObjectEquality({}, conf); + validateObjectEquality({}, conf); } catch (err) { expect(err).toBe('Field strField should be one of [object Object].'); } diff --git a/tests/validators/optional.ts b/tests/validators/optional.ts index 7a5b9ec..b09b23e 100644 --- a/tests/validators/optional.ts +++ b/tests/validators/optional.ts @@ -1,32 +1,34 @@ import { describe, expect, test } from '@jest/globals'; -import { Failure, Success, ValidatorConfiguration, ValidatorFunction } from '../../src/types'; +import { SyncValidatorFunction, ValidatorConfiguration, ValidatorFunction } from '../../src/types'; import validate from '../../src/validate'; import optional from '../../src/validators/optional'; -const conf: ValidatorConfiguration = { - name: 'optionalField', - original: {}, - path: [], - parent: {}, -}; - -test('optional validator if value is undefined, returns successfully without calling the inner validator', async () => { +test('optional sync validator if value is undefined returns successfully', async () => { let i = 0; - const testValidation = (): ValidatorFunction => async (value) => { + const testValidation = (): SyncValidatorFunction => (value) => { i += 1; if (value) { - return Success(); + return value as string; } - return Failure('This field is required.'); + throw 'This field is required.'; }; - const validateFunctionOptional = validate({ field: optional(testValidation()) }); - expect(await validateFunctionOptional({})).toEqual({} as any); + const validateFunctionOptional = validate(optional([testValidation()])); + expect(validateFunctionOptional(undefined)).toEqual(undefined); expect(i).toEqual(0); - expect(await validateFunctionOptional({ field: 'string' })).toEqual({ field: 'string' }); + expect(validateFunctionOptional({ field: 'string' })).toEqual({ field: 'string' }); expect(i).toEqual(1); +}); - i = 0; +test('optional validator if value is undefined returns successfully', async () => { + let i = 0; + const testValidation = (): ValidatorFunction => async (value) => { + i += 1; + if (value) { + return value as string; + } + throw 'This field is required.'; + }; const validateListOptional = validate({ field: optional([testValidation(), testValidation()]), @@ -36,22 +38,19 @@ test('optional validator if value is undefined, returns successfully without cal expect(await validateListOptional({ field: 'string' })).toEqual({ field: 'string' }); expect(i).toEqual(2); - // Let's check for the expected type with some TypeScript magic - const validatedOptional = await validateFunctionOptional({ field: 'string' }); + // Let's check for the expected type type ExpectedType = { field: string | undefined; }; - type AssertExpectedType = T extends ExpectedType ? true : never; - // This line shouldn't compile if the type is wrong - const cond1: AssertExpectedType = true; + // const validatedType: ExpectedType = await validateFunctionOptional({ field: 'string' }); }); -test('optional validator without it, undefined fails', async () => { - const testValidation = (): ValidatorFunction => async (value) => { +test('optional validator without it undefined fails', async () => { + const testValidation = (): ValidatorFunction => async (value) => { if (value) { - return Success(); + return value as string; } - return Failure('This field is required.'); + throw 'This field is required.'; }; const validateNotOptional = validate({ field: testValidation() }); diff --git a/tests/validators/string/betweenLength.ts b/tests/validators/string/betweenLength.ts index 0398149..bf9bce0 100644 --- a/tests/validators/string/betweenLength.ts +++ b/tests/validators/string/betweenLength.ts @@ -12,17 +12,17 @@ const conf: ValidatorConfiguration = { test('betweenLength if passed words length is between its limits, returns success', async () => { const validateLength = betweenLength(5, 10); - await expect(validateLength('smart', conf)).resolves.toBeUndefined(); - await expect(validateLength('serious', conf)).resolves.toBeUndefined(); - await expect(validateLength('attractive', conf)).resolves.toBeUndefined(); + await expect(validateLength('smart', conf)).toEqual('smart'); + await expect(validateLength('serious', conf)).toEqual('serious'); + await expect(validateLength('attractive', conf)).toEqual('attractive'); }); test('betweenLength still works if the parameters are switched up', async () => { const validateLength = betweenLength(10, 5); - await expect(validateLength('smart', conf)).resolves.toBeUndefined(); - await expect(validateLength('serious', conf)).resolves.toBeUndefined(); - await expect(validateLength('attractive', conf)).resolves.toBeUndefined(); + await expect(validateLength('smart', conf)).toEqual('smart'); + await expect(validateLength('serious', conf)).toEqual('serious'); + await expect(validateLength('attractive', conf)).toEqual('attractive'); }); test('betweenLength if passed word is shorter or longer, fails', async () => { @@ -40,16 +40,3 @@ test('betweenLength if passed word is shorter or longer, fails', async () => { expect(err).toBe('Field strField length should be between 5 and 10 characters.'); } }); - -test('betweenLength if passed word is not a string, ignores', async () => { - const validateLength = betweenLength(5, 10); - - await expect(validateLength(8 as any, conf)).resolves.toBeUndefined(); - await expect(validateLength(NaN as any, conf)).resolves.toBeUndefined(); - await expect(validateLength(true as any, conf)).resolves.toBeUndefined(); - await expect(validateLength([] as any, conf)).resolves.toBeUndefined(); - await expect(validateLength(Array(8).fill('a') as any, conf)).resolves.toBeUndefined(); - await expect(validateLength({} as any, conf)).resolves.toBeUndefined(); - await expect(validateLength(undefined as any, conf)).resolves.toBeUndefined(); - await expect(validateLength(null as any, conf)).resolves.toBeUndefined(); -}); diff --git a/tests/validators/string/insensitiveEquals.ts b/tests/validators/string/insensitiveEquals.ts index 8cf8c3a..fb0ff46 100644 --- a/tests/validators/string/insensitiveEquals.ts +++ b/tests/validators/string/insensitiveEquals.ts @@ -12,10 +12,10 @@ const conf: ValidatorConfiguration = { test('insensitiveEquals, if the two strings are the same, returns successfully', async () => { const validateInsensitiveEquals = insensitiveEquals('AsdasD'); - await expect(validateInsensitiveEquals('asdasd', conf)).resolves.toBeUndefined(); - await expect(validateInsensitiveEquals('AsdasD', conf)).resolves.toBeUndefined(); - await expect(validateInsensitiveEquals('ASDASD', conf)).resolves.toBeUndefined(); - await expect(validateInsensitiveEquals('AsDaSd', conf)).resolves.toBeUndefined(); + await expect(validateInsensitiveEquals('asdasd', conf)).toEqual('asdasd'); + await expect(validateInsensitiveEquals('AsdasD', conf)).toEqual('AsdasD'); + await expect(validateInsensitiveEquals('ASDASD', conf)).toEqual('ASDASD'); + await expect(validateInsensitiveEquals('AsDaSd', conf)).toEqual('AsDaSd'); }); test('insensitiveEquals, if the two string are the same, fails', async () => { @@ -39,18 +39,3 @@ test('insensitiveEquals, if the two string are the same, fails', async () => { expect(err).toBe('Field strField should be equal to AsdasD.'); } }); - -test('insensitiveEquals, if not a string is given, ignores', async () => { - const validateInsensitiveEquals = insensitiveEquals('AsdasD'); - - await expect(validateInsensitiveEquals(6 as any, conf)).resolves.toBeUndefined(); - await expect(validateInsensitiveEquals(NaN as any, conf)).resolves.toBeUndefined(); - await expect(validateInsensitiveEquals(true as any, conf)).resolves.toBeUndefined(); - await expect(validateInsensitiveEquals([] as any, conf)).resolves.toBeUndefined(); - await expect( - validateInsensitiveEquals(['A', 's', 'd', 'a', 's', 'D'] as any, conf) - ).resolves.toBeUndefined(); - await expect(validateInsensitiveEquals({} as any, conf)).resolves.toBeUndefined(); - await expect(validateInsensitiveEquals(undefined as any, conf)).resolves.toBeUndefined(); - await expect(validateInsensitiveEquals(null as any, conf)).resolves.toBeUndefined(); -}); diff --git a/tests/validators/string/isEmail.ts b/tests/validators/string/isEmail.ts index e4069b0..5c00167 100644 --- a/tests/validators/string/isEmail.ts +++ b/tests/validators/string/isEmail.ts @@ -12,9 +12,9 @@ const conf: ValidatorConfiguration = { test('isEmail, if valid email is given, returns successfully', async () => { const validateEmail = isEmail(); - await expect(validateEmail('support@gmail.com', conf)).resolves.toBeUndefined(); - await expect(validateEmail('cat@dog.ninja', conf)).resolves.toBeUndefined(); - await expect(validateEmail('asd@asd.asd', conf)).resolves.toBeUndefined(); + await expect(validateEmail('support@gmail.com', conf)).toEqual('support@gmail.com'); + await expect(validateEmail('cat@dog.ninja', conf)).toEqual('cat@dog.ninja'); + await expect(validateEmail('asd@asd.asd', conf)).toEqual('asd@asd.asd'); }); test('isEmail, if invalid email is given, fails', async () => { @@ -57,17 +57,3 @@ test('isEmail, if invalid email is given, fails', async () => { } }); -test('isEmail, if not a string is given, ignores', async () => { - const validateEmail = isEmail(); - - await expect(validateEmail(8 as any, conf)).resolves.toBeUndefined(); - await expect(validateEmail(NaN as any, conf)).resolves.toBeUndefined(); - await expect(validateEmail(true as any, conf)).resolves.toBeUndefined(); - await expect(validateEmail([] as any, conf)).resolves.toBeUndefined(); - await expect( - validateEmail(['a', 's', 'd', '@', 'a', 's', 'd', '.', 'a', 's', 'd'] as any, conf) - ).resolves.toBeUndefined(); - await expect(validateEmail({} as any, conf)).resolves.toBeUndefined(); - await expect(validateEmail(undefined as any, conf)).resolves.toBeUndefined(); - await expect(validateEmail(null as any, conf)).resolves.toBeUndefined(); -}); diff --git a/tests/validators/string/isString.ts b/tests/validators/string/isString.ts index b5b5244..0ae0309 100644 --- a/tests/validators/string/isString.ts +++ b/tests/validators/string/isString.ts @@ -12,8 +12,8 @@ const conf: ValidatorConfiguration = { test('isString, when passed value is string returns success', async () => { const sanitizeString = isString(); - await expect(sanitizeString('string', conf)).resolves.toBeUndefined(); - await expect(sanitizeString('0', conf)).resolves.toBeUndefined(); + await expect(sanitizeString('string', conf)).toEqual('string'); + await expect(sanitizeString('0', conf)).toEqual('0'); }); test('isString, when passed value is not string, fails', async () => { diff --git a/tests/validators/string/isUrl.ts b/tests/validators/string/isUrl.ts index ae5371e..08b40a6 100644 --- a/tests/validators/string/isUrl.ts +++ b/tests/validators/string/isUrl.ts @@ -12,16 +12,20 @@ const conf: ValidatorConfiguration = { test('isUrl, if valid url is given, returns successfully', async () => { const validateUrl = isUrl(); - await expect(validateUrl('https://google.com', conf)).resolves.toBeUndefined(); - await expect(validateUrl('https://google.com/', conf)).resolves.toBeUndefined(); - await expect(validateUrl('https://google.com:8443', conf)).resolves.toBeUndefined(); - await expect(validateUrl('https://google.com/search?q=name', conf)).resolves.toBeUndefined(); - await expect(validateUrl('https://google.com/#asdasd', conf)).resolves.toBeUndefined(); - await expect(validateUrl('mailto:asd@asd.asd', conf)).resolves.toBeUndefined(); - await expect(validateUrl('file:///etc/passwd', conf)).resolves.toBeUndefined(); - await expect( - validateUrl('ftp://user:password@ftphost.com/directory', conf) - ).resolves.toBeUndefined(); + await expect(validateUrl('https://google.com', conf)).toEqual('https://google.com'); + await expect(validateUrl('https://google.com/', conf)).toEqual('https://google.com/'); + await expect(validateUrl('https://google.com:8443', conf)).toEqual('https://google.com:8443'); + await expect(validateUrl('https://google.com/search?q=name', conf)).toEqual( + 'https://google.com/search?q=name' + ); + await expect(validateUrl('https://google.com/#asdasd', conf)).toEqual( + 'https://google.com/#asdasd' + ); + await expect(validateUrl('mailto:asd@asd.asd', conf)).toEqual('mailto:asd@asd.asd'); + await expect(validateUrl('file:///etc/passwd', conf)).toEqual('file:///etc/passwd'); + await expect(validateUrl('ftp://user:password@ftphost.com/directory', conf)).toEqual( + 'ftp://user:password@ftphost.com/directory' + ); }); test('isUrl, if invalid url is given, fails', async () => { @@ -45,18 +49,3 @@ test('isUrl, if invalid url is given, fails', async () => { expect(err).toBe('Field strField should be an url.'); } }); - -test('isUrl, if not a string is given, ignores', async () => { - const validateUrl = isUrl(); - - await expect(validateUrl(8 as any, conf)).resolves.toBeUndefined(); - await expect(validateUrl(NaN as any, conf)).resolves.toBeUndefined(); - await expect(validateUrl(true as any, conf)).resolves.toBeUndefined(); - await expect(validateUrl([] as any, conf)).resolves.toBeUndefined(); - await expect( - validateUrl(['a', 's', 'd', '@', 'a', 's', 'd', '.', 'a', 's', 'd'] as any, conf) - ).resolves.toBeUndefined(); - await expect(validateUrl({} as any, conf)).resolves.toBeUndefined(); - await expect(validateUrl(undefined as any, conf)).resolves.toBeUndefined(); - await expect(validateUrl(null as any, conf)).resolves.toBeUndefined(); -}); diff --git a/tests/validators/string/length.ts b/tests/validators/string/length.ts index 2833ca6..7679394 100644 --- a/tests/validators/string/length.ts +++ b/tests/validators/string/length.ts @@ -12,9 +12,9 @@ const conf: ValidatorConfiguration = { test('length if passed words length is the same, returns success', async () => { const validateLength = length(5); - await expect(validateLength('smart', conf)).resolves.toBeUndefined(); - await expect(validateLength('young', conf)).resolves.toBeUndefined(); - await expect(validateLength('brave', conf)).resolves.toBeUndefined(); + await expect(validateLength('smart', conf)).toEqual('smart'); + await expect(validateLength('young', conf)).toEqual('young'); + await expect(validateLength('brave', conf)).toEqual('brave'); }); test('length if passed word is shorter or longer, fails', async () => { @@ -32,16 +32,3 @@ test('length if passed word is shorter or longer, fails', async () => { expect(err).toBe('Field strField length should be exactly 5 characters.'); } }); - -test('length if passed word is not a string, ignores', async () => { - const validateLength = length(5); - - await expect(validateLength(8 as any, conf)).resolves.toBeUndefined(); - await expect(validateLength(NaN as any, conf)).resolves.toBeUndefined(); - await expect(validateLength(true as any, conf)).resolves.toBeUndefined(); - await expect(validateLength([] as any, conf)).resolves.toBeUndefined(); - await expect(validateLength(Array(8).fill('a') as any, conf)).resolves.toBeUndefined(); - await expect(validateLength({} as any, conf)).resolves.toBeUndefined(); - await expect(validateLength(undefined as any, conf)).resolves.toBeUndefined(); - await expect(validateLength(null as any, conf)).resolves.toBeUndefined(); -}); diff --git a/tests/validators/string/maxLength.ts b/tests/validators/string/maxLength.ts index 4139e6b..3868888 100644 --- a/tests/validators/string/maxLength.ts +++ b/tests/validators/string/maxLength.ts @@ -12,9 +12,9 @@ const conf: ValidatorConfiguration = { test('maxLength if passed words length is between its limits, returns success', async () => { const validateLength = maxLength(10); - await expect(validateLength('smart', conf)).resolves.toBeUndefined(); - await expect(validateLength('serious', conf)).resolves.toBeUndefined(); - await expect(validateLength('attractive', conf)).resolves.toBeUndefined(); + await expect(validateLength('smart', conf)).toEqual('smart'); + await expect(validateLength('serious', conf)).toEqual('serious'); + await expect(validateLength('attractive', conf)).toEqual('attractive'); }); test('maxLength if passed word is longer, fails', async () => { @@ -32,16 +32,3 @@ test('maxLength if passed word is longer, fails', async () => { expect(err).toBe('Field strField length should be at most 10 characters.'); } }); - -test('maxLength if passed word is not a string, ignores', async () => { - const validateLength = maxLength(10); - - await expect(validateLength(8 as any, conf)).resolves.toBeUndefined(); - await expect(validateLength(NaN as any, conf)).resolves.toBeUndefined(); - await expect(validateLength(true as any, conf)).resolves.toBeUndefined(); - await expect(validateLength([] as any, conf)).resolves.toBeUndefined(); - await expect(validateLength(Array(8).fill('a') as any, conf)).resolves.toBeUndefined(); - await expect(validateLength({} as any, conf)).resolves.toBeUndefined(); - await expect(validateLength(undefined as any, conf)).resolves.toBeUndefined(); - await expect(validateLength(null as any, conf)).resolves.toBeUndefined(); -}); diff --git a/tests/validators/string/minLength.ts b/tests/validators/string/minLength.ts index dca6e6b..bfb0bff 100644 --- a/tests/validators/string/minLength.ts +++ b/tests/validators/string/minLength.ts @@ -12,9 +12,9 @@ const conf: ValidatorConfiguration = { test('minLength if passed words length is as at least long as the limit, returns success', async () => { const validateLength = minLength(5); - await expect(validateLength('smart', conf)).resolves.toBeUndefined(); - await expect(validateLength('serious', conf)).resolves.toBeUndefined(); - await expect(validateLength('attractive', conf)).resolves.toBeUndefined(); + await expect(validateLength('smart', conf)).toEqual('smart'); + await expect(validateLength('serious', conf)).toEqual('serious'); + await expect(validateLength('attractive', conf)).toEqual('attractive'); }); test('minLength if passed word is shorter, fails', async () => { @@ -32,16 +32,3 @@ test('minLength if passed word is shorter, fails', async () => { expect(err).toBe('Field strField length should be at least 5 characters.'); } }); - -test('minLength if passed word is not a string, ignores', async () => { - const validateLength = minLength(5); - - await expect(validateLength(8 as any, conf)).resolves.toBeUndefined(); - await expect(validateLength(NaN as any, conf)).resolves.toBeUndefined(); - await expect(validateLength(true as any, conf)).resolves.toBeUndefined(); - await expect(validateLength([] as any, conf)).resolves.toBeUndefined(); - await expect(validateLength(Array(8).fill('a') as any, conf)).resolves.toBeUndefined(); - await expect(validateLength({} as any, conf)).resolves.toBeUndefined(); - await expect(validateLength(undefined as any, conf)).resolves.toBeUndefined(); - await expect(validateLength(null as any, conf)).resolves.toBeUndefined(); -}); diff --git a/tests/validators/validateCondition.ts b/tests/validators/validateCondition.ts index 3b5ae04..5a3cf68 100644 --- a/tests/validators/validateCondition.ts +++ b/tests/validators/validateCondition.ts @@ -1,19 +1,44 @@ -import { describe, expect, test } from '@jest/globals'; +import { expect, test } from '@jest/globals'; import { ValidatorConfiguration } from '../../src/types'; import validateCondition from '../../src/validators/validateCondition'; const conf: ValidatorConfiguration = { name: 'object', original: {}, parent: {}, path: [] }; -test('validateCondition function returns undefined if regex applies', async () => { +test('validateCondition function returns value if condition applies', async () => { const validateTest = validateCondition(() => true); - await expect(validateTest('', conf)).resolves.toBeUndefined(); + expect(validateTest('', conf)).toEqual(''); }); -test('validateCondition function throws error if regex fails', async () => { +test('validateCondition function returns value if async condition applies', async () => { + const validateTest = validateCondition(() => Promise.resolve(true)); + await expect(validateTest('', conf)).resolves.toEqual(''); +}); + +test('validateCondition function throws error if condition fails', async () => { const validateTest = validateCondition(() => false); + try { + validateTest('', conf); + } catch (err) { + expect(err).not.toBeUndefined(); + } +}); + +test('validateCondition function throws error if async condition fails', async () => { + const validateTest = validateCondition(() => Promise.resolve(false)); try { await validateTest('', conf); } catch (err) { expect(err).not.toBeUndefined(); } }); + +test('validateCondition function throws error if async condition fails', async () => { + const validateTest = validateCondition(() => { + throw 'Oh no.'; + }); + try { + validateTest('', conf); + } catch (err) { + expect(err).not.toBeUndefined(); + } +}); diff --git a/tests/validators/validateRegex.ts b/tests/validators/validateRegex.ts index 1d67fd6..eb50ed4 100644 --- a/tests/validators/validateRegex.ts +++ b/tests/validators/validateRegex.ts @@ -6,19 +6,14 @@ const conf: ValidatorConfiguration = { name: 'object', original: {}, parent: {}, test('validateRegex function returns undefined if regex applies', async () => { const validateTest = validateRegex(/[a-z].*/); - await expect(validateTest('asdasd', conf)).resolves.toBeUndefined(); + expect(validateTest('asdasd', conf)).toEqual('asdasd'); }); test('validateRegex function throws error if regex fails', async () => { const validateTest = validateRegex(/[a-z].*/); try { - await validateTest('123', conf); + validateTest('123', conf); } catch (err) { expect(err).not.toBeUndefined(); } }); - -test('validateRegex function ignores if passed value is not a string', async () => { - const validateTest = validateRegex(/[a-z].*/); - await expect(validateTest(123 as any, conf)).resolves.toBeUndefined(); -});