From ac17f9516d0760ae1ae3e590786ca6cfedaaac97 Mon Sep 17 00:00:00 2001 From: Oleksandr_Halichenko Date: Tue, 11 Nov 2025 18:11:43 +0200 Subject: [PATCH] added `deeply strictly equal` matcher --- CHANGELOG.md | 3 + README.md | 1 + package-lock.json | 4 +- package.json | 2 +- src/matchers.ts | 8 + src/verify.ts | 210 ++++---- test/expect.spec.ts | 6 + test/validationTransformer.spec.ts | 811 +++++++++++++++-------------- 8 files changed, 537 insertions(+), 508 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 560b85c..959400f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how :pencil: - chore +## [1.6.0] +- :rocket: added `deeply strictly equal` matcher + ## [1.5.0] - :rocket: improved `AssertionError` to inherit it from nodejs `AssertionError` diff --git a/README.md b/README.md index 1c4743e..9317927 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ This library supports a variety of validation types, which can all be negated by - **`equal`**: Checks for non-strict equality (`==`). - **`strictly equal`**: Checks for strict equality (`===`). - **`deeply equal`**: Performs a deep comparison of object properties or array elements. + - **`deeply strictly equal`**: Performs a deep comparison of object properties or array elements with nodejs [util.isDeepStrictEqual](https://nodejs.org/api/util.html#utilisdeepstrictequalval1-val2-options) - **`case insensitive equal`**: Compares two values for non-strict equality after converting them to lowercase. - **`contain`**: Verifies if a string contains a specific substring. - **`include members`**: Checks if an array or object includes a specific set of members. diff --git a/package-lock.json b/package-lock.json index 932c1c5..8663834 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@qavajs/validation", - "version": "1.5.0", + "version": "1.6.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@qavajs/validation", - "version": "1.5.0", + "version": "1.6.0", "license": "MIT", "dependencies": { "ajv": "^8.17.1" diff --git a/package.json b/package.json index 1e71eb7..1249326 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@qavajs/validation", - "version": "1.5.0", + "version": "1.6.0", "description": "@qavajs library that transform plain english definition to validation functions", "main": "./lib/index.js", "scripts": { diff --git a/src/matchers.ts b/src/matchers.ts index 8c3d7a6..040a5ed 100644 --- a/src/matchers.ts +++ b/src/matchers.ts @@ -1,5 +1,6 @@ import { expect as base, type MatcherResult, type MatcherContext } from './expect'; import Ajv from 'ajv'; +import { isDeepStrictEqual } from 'node:util'; export type BaseMatchers = { toSimpleEqual(this: MatcherContext, expected: any): MatcherResult; @@ -16,6 +17,7 @@ export type BaseMatchers = { toBeTruthy(this: MatcherContext): MatcherResult; toContain(this: MatcherContext, expected: any): MatcherResult; toDeepEqual(this: MatcherContext, expected: any): MatcherResult; + toDeepStrictEqual(this: MatcherContext, expected: any): MatcherResult; toStrictEqual(this: MatcherContext, expected: any): MatcherResult; toHaveLength(this: MatcherContext, expected: number): MatcherResult; toHaveProperty(this: MatcherContext, key: string, value?: any): MatcherResult; @@ -134,6 +136,12 @@ export const expect = base.extend({ return { pass, message }; }, + toDeepStrictEqual(expected: any) { + const pass = isDeepStrictEqual(this.received, expected); + const message = this.formatMessage(this.received, expected, 'to deeply strictly equal', this.isNot); + return { pass, message }; + }, + toStrictEqual(expected: any) { const pass = this.received === expected; const message = this.formatMessage(this.received, expected, 'to strictly equal', this.isNot); diff --git a/src/verify.ts b/src/verify.ts index cf485a1..9fd2eae 100644 --- a/src/verify.ts +++ b/src/verify.ts @@ -1,22 +1,23 @@ import { expect } from './matchers'; export const validations = { - EQUAL: 'equal', - DEEPLY_EQUAL: 'deeply equal', - STRICTLY_EQUAL: 'strictly equal', - HAVE_MEMBERS: 'have member', - MATCH: 'match', - CONTAIN: 'contain', - ABOVE: 'above', - BELOW: 'below', - GREATER: 'greater than', - LESS: 'less than', - HAVE_TYPE: 'have type', - INCLUDE_MEMBERS: 'include member', - HAVE_PROPERTY: 'have property', - MATCH_SCHEMA: 'match schema', - CASE_INSENSITIVE_EQUAL: 'case insensitive equal', - SATISFY: 'satisfy' + EQUAL: 'equal', + DEEPLY_EQUAL: 'deeply equal', + STRICTLY_EQUAL: 'strictly equal', + DEEPLY_STRICTLY_EQUAL: 'deeply strictly equal', + HAVE_MEMBERS: 'have member', + MATCH: 'match', + CONTAIN: 'contain', + ABOVE: 'above', + BELOW: 'below', + GREATER: 'greater than', + LESS: 'less than', + HAVE_TYPE: 'have type', + INCLUDE_MEMBERS: 'include member', + HAVE_PROPERTY: 'have property', + MATCH_SCHEMA: 'match schema', + CASE_INSENSITIVE_EQUAL: 'case insensitive equal', + SATISFY: 'satisfy' }; const isClause = '(?:is |do |does |to )?'; @@ -29,123 +30,126 @@ export const validationExtractRegexp = new RegExp(`^${isClause}${notClause}${toB export const validationRegexp = new RegExp(`(${isClause}${notClause}${toBeClause}${softlyClause}${validationClause})`); type VerifyInput = { - received: any; - expected: any; - validation: string; - reverse: boolean; - soft: boolean; + received: any; + expected: any; + validation: string; + reverse: boolean; + soft: boolean; }; const aboveFn = (expectClause: any, expected: any) => expectClause.toBeGreaterThan(toNumber(expected)); const belowFn = (expectClause: any, expected: any) => expectClause.toBeLessThan(toNumber(expected)); const validationFns: Record void> = { - [validations.EQUAL]: (expectClause, expected: any) => expectClause.toSimpleEqual(expected), - [validations.STRICTLY_EQUAL]: (expectClause: any, expected: any) => expectClause.toEqual(expected), - [validations.DEEPLY_EQUAL]: (expectClause: any, expected: any) => expectClause.toDeepEqual(expected), - [validations.HAVE_MEMBERS]: (expectClause: any, expected: any) => expectClause.toHaveMembers(expected), - [validations.MATCH]: (expectClause: any, expected: any) => expectClause.toMatch(toRegexp(expected)), - [validations.CONTAIN]: (expectClause: any, expected: any) => expectClause.toContain(expected), - [validations.ABOVE]: aboveFn, - [validations.BELOW]: belowFn, - [validations.GREATER]: aboveFn, - [validations.LESS]: belowFn, - [validations.HAVE_TYPE]: (expectClause: any, expected: string) => expectClause.toHaveType(expected), - [validations.INCLUDE_MEMBERS]: (expectClause: any, expected: string) => expectClause.toIncludeMembers(expected), - [validations.HAVE_PROPERTY]: (expectClause: any, expected: string) => expectClause.toHaveProperty(expected), - [validations.MATCH_SCHEMA]: (expectClause: any, expected: string) => expectClause.toMatchSchema(expected), - [validations.CASE_INSENSITIVE_EQUAL]: (expectClause: any, expected: any) => expectClause.toCaseInsensitiveEqual(expected), - [validations.SATISFY]: (expectClause: any, expected: any) => expectClause.toSatisfy(expected), + [validations.EQUAL]: (expectClause, expected: any) => expectClause.toSimpleEqual(expected), + [validations.STRICTLY_EQUAL]: (expectClause: any, expected: any) => expectClause.toEqual(expected), + [validations.DEEPLY_EQUAL]: (expectClause: any, expected: any) => expectClause.toDeepEqual(expected), + [validations.DEEPLY_STRICTLY_EQUAL]: (expectClause: any, expected: any) => expectClause.toDeepStrictEqual(expected), + [validations.HAVE_MEMBERS]: (expectClause: any, expected: any) => expectClause.toHaveMembers(expected), + [validations.MATCH]: (expectClause: any, expected: any) => expectClause.toMatch(toRegexp(expected)), + [validations.CONTAIN]: (expectClause: any, expected: any) => expectClause.toContain(expected), + [validations.ABOVE]: aboveFn, + [validations.BELOW]: belowFn, + [validations.GREATER]: aboveFn, + [validations.LESS]: belowFn, + [validations.HAVE_TYPE]: (expectClause: any, expected: string) => expectClause.toHaveType(expected), + [validations.INCLUDE_MEMBERS]: (expectClause: any, expected: string) => expectClause.toIncludeMembers(expected), + [validations.HAVE_PROPERTY]: (expectClause: any, expected: string) => expectClause.toHaveProperty(expected), + [validations.MATCH_SCHEMA]: (expectClause: any, expected: string) => expectClause.toMatchSchema(expected), + [validations.CASE_INSENSITIVE_EQUAL]: (expectClause: any, expected: any) => expectClause.toCaseInsensitiveEqual(expected), + [validations.SATISFY]: (expectClause: any, expected: any) => expectClause.toSatisfy(expected), }; /** * Basic verification function * @param {VerifyInput} object with all needed data for validation */ -export function verify({ received, expected, validation, reverse, soft }: VerifyInput): void { - const expectClause = expect(received).configure({ not: reverse, soft }); - const validate = validationFns[validation]; - validate(expectClause, expected); +export function verify({received, expected, validation, reverse, soft}: VerifyInput): void { + const expectClause = expect(received).configure({not: reverse, soft}); + const validate = validationFns[validation]; + validate(expectClause, expected); } export function getValidation(validationType: string, options?: { soft: boolean }): (AR: any, expected: any) => void { - const match = validationExtractRegexp.exec(validationType); - if (!match) throw new Error(`Validation '${validationType}' is not supported`); - const { reverse, validation, soft } = match.groups as {[p: string]: string}; - const softProp = options?.soft || !!soft; - return function (received: any, expected: any) { - verify({ received, expected, validation, reverse: Boolean(reverse), soft: softProp }); - }; + const match = validationExtractRegexp.exec(validationType); + if (!match) throw new Error(`Validation '${validationType}' is not supported`); + const {reverse, validation, soft} = match.groups as { [p: string]: string }; + const softProp = options?.soft || !!soft; + return function (received: any, expected: any) { + verify({received, expected, validation, reverse: Boolean(reverse), soft: softProp}); + }; } -export function getPollValidation(validationType: string, options?: { soft: boolean }): (AR: any, expected: any, options?: { timeout?: number, interval?: number }) => Promise { - const match = validationExtractRegexp.exec(validationType); - if (!match) throw new Error(`Poll validation '${validationType}' is not supported`); - const { reverse, validation, soft } = match.groups as {[p: string]: string}; - const softProp = options?.soft || !!soft; - return async function (received: any, expected: any, options?: { timeout?: number, interval?: number }) { +export function getPollValidation(validationType: string, options?: { + soft: boolean +}): (AR: any, expected: any, options?: { timeout?: number, interval?: number }) => Promise { + const match = validationExtractRegexp.exec(validationType); + if (!match) throw new Error(`Poll validation '${validationType}' is not supported`); + const {reverse, validation, soft} = match.groups as { [p: string]: string }; + const softProp = options?.soft || !!soft; + return async function (received: any, expected: any, options?: { timeout?: number, interval?: number }) { + const timeout = options?.timeout ?? 5000; + const interval = options?.interval ?? 500; + let lastError: Error = new Error(`Promise was not settled before timeout`); + let intervalId: NodeJS.Timeout; + const evaluatePromise = new Promise(resolve => { + intervalId = setInterval(async () => { + try { + const actualValue = await received(); + verify({ + received: actualValue, + expected, + validation, + reverse: Boolean(reverse), + soft: softProp + }); + clearInterval(intervalId); + resolve(); + } catch (err: any) { + lastError = err; + } + }, interval); + }); + const timeoutPromise = new Promise((_, reject) => setTimeout(() => { + clearInterval(intervalId); + reject(lastError) + }, timeout)); + return Promise.race([evaluatePromise, timeoutPromise]); + }; +} + +export async function poll(fn: Function, options?: { timeout?: number, interval?: number }) { const timeout = options?.timeout ?? 5000; const interval = options?.interval ?? 500; - let lastError: Error = new Error(`Promise was not settled before timeout`); + let lastError: Error = new Error('Unexpected error'); let intervalId: NodeJS.Timeout; const evaluatePromise = new Promise(resolve => { - intervalId = setInterval(async () => { - try { - const actualValue = await received(); - verify({ - received: actualValue, - expected, - validation, - reverse: Boolean(reverse), - soft: softProp - }); - clearInterval(intervalId); - resolve(); - } catch (err: any) { - lastError = err; - } - }, interval); + intervalId = setInterval(async () => { + try { + await fn(); + clearInterval(intervalId); + resolve(); + } catch (err: any) { + lastError = err; + } + }, interval); }); const timeoutPromise = new Promise((_, reject) => setTimeout(() => { - clearInterval(intervalId); - reject(lastError) + clearInterval(intervalId); + reject(lastError); }, timeout)); return Promise.race([evaluatePromise, timeoutPromise]); - }; -} - -export async function poll(fn: Function, options?: { timeout?: number, interval?: number }) { - const timeout = options?.timeout ?? 5000; - const interval = options?.interval ?? 500; - let lastError: Error = new Error('Unexpected error'); - let intervalId: NodeJS.Timeout; - const evaluatePromise = new Promise(resolve => { - intervalId = setInterval(async () => { - try { - await fn(); - clearInterval(intervalId); - resolve(); - } catch (err: any) { - lastError = err; - } - }, interval); - }); - const timeoutPromise = new Promise((_, reject) => setTimeout(() => { - clearInterval(intervalId); - reject(lastError); - }, timeout)); - return Promise.race([evaluatePromise, timeoutPromise]); } export { expect }; function toNumber(n: any): number { - const parsedNumber = Number.parseFloat(n); - if (Number.isNaN(parsedNumber)) { - throw new TypeError(`${n} is not a number`); - } - return parsedNumber; + const parsedNumber = Number.parseFloat(n); + if (Number.isNaN(parsedNumber)) { + throw new TypeError(`${n} is not a number`); + } + return parsedNumber; } function toRegexp(r: string | RegExp): RegExp { - return r instanceof RegExp ? r : new RegExp(r); + return r instanceof RegExp ? r : new RegExp(r); } diff --git a/test/expect.spec.ts b/test/expect.spec.ts index 968113c..26598d2 100644 --- a/test/expect.spec.ts +++ b/test/expect.spec.ts @@ -127,6 +127,12 @@ describe('expect matchers', () => { vitestExpect(() => expect([{a: 42}, {b: 'text'}]).toDeepEqual([{b: 'text'}, {a: 42}])).not.toThrow(); }); + // toDeepStrictEqual + test('toDeepStrictEqual', () => { + vitestExpect(() => expect([[1, 2], [1, 2, 3]]).toDeepStrictEqual([[1, 2], [1, 2, 3]])).not.toThrow(); + vitestExpect(() => expect([{a: 42}, {b: 'text'}]).toDeepStrictEqual([{a: 42}, {b: 'text'}])).not.toThrow(); + }); + // toHaveLength test('toHaveLength', () => { vitestExpect(() => expect([1,2,3]).toHaveLength(3)).not.toThrow(); diff --git a/test/validationTransformer.spec.ts b/test/validationTransformer.spec.ts index b325573..ecdeb2f 100644 --- a/test/validationTransformer.spec.ts +++ b/test/validationTransformer.spec.ts @@ -1,421 +1,428 @@ -import { test, expect } from 'vitest'; -import { getValidation, AssertionError, SoftAssertionError } from '../src'; +import {test, expect} from 'vitest'; +import {getValidation, AssertionError, SoftAssertionError} from '../src'; type TestParams = { - testName: string; - validation: string; - positiveArgs: [any, any]; - negativeArgs: [any, any]; - expectedError: string; + testName: string; + validation: string; + positiveArgs: [any, any]; + negativeArgs: [any, any]; + expectedError: string; }; const tests: Array = [ - { - testName: 'equals', - validation: 'equals', - positiveArgs: [1, 1], - negativeArgs: [1, 2], - expectedError: 'expected 1 to equal 2', - }, - { - testName: 'equal to', - validation: 'equal to', - positiveArgs: [1, 1], - negativeArgs: [1, 2], - expectedError: 'expected 1 to equal 2', - }, - { - testName: 'equals with type cast', - validation: 'equals', - positiveArgs: [1, '1'], - negativeArgs: [1, '2'], - expectedError: 'expected 1 to equal \'2\'', - }, - { - testName: 'to equal', - validation: 'to equal', - positiveArgs: [1, 1], - negativeArgs: [1, 2], - expectedError: 'expected 1 to equal 2', - }, - { - testName: 'does not equal', - validation: 'does not equal', - positiveArgs: [1, 2], - negativeArgs: [1, 1], - expectedError: 'expected 1 not to equal 1', - }, - { - testName: 'not to equal', - validation: 'not to equal', - positiveArgs: [1, 2], - negativeArgs: [1, 1], - expectedError: 'expected 1 not to equal 1', - }, - { - testName: 'not to equal', - validation: 'not to equal', - positiveArgs: [1, 2], - negativeArgs: [1, 1], - expectedError: 'expected 1 not to equal 1', - }, - { - testName: 'strictly equals', - validation: 'strictly equals', - positiveArgs: [1, 1], - negativeArgs: [1, 2], - expectedError: 'expected 1 to equal 2', - }, - { - testName: 'strictly equals with type cast', - validation: 'strictly equals', - positiveArgs: [1, 1], - negativeArgs: [1, '1'], - expectedError: 'expected 1 to equal \'1\'', - }, - { - testName: 'to strictly equal', - validation: 'to strictly equal', - positiveArgs: [1, 1], - negativeArgs: [1, 2], - expectedError: 'expected 1 to equal 2', - }, - { - testName: 'does not strictly equal', - validation: 'does not strictly equal', - positiveArgs: [1, 2], - negativeArgs: [1, 1], - expectedError: 'expected 1 not to equal 1', - }, - { - testName: 'not to strictly equal', - validation: 'not to strictly equal', - positiveArgs: [1, 2], - negativeArgs: [1, 1], - expectedError: 'expected 1 not to equal 1', - }, - { - testName: 'not to strictly equal', - validation: 'not to strictly equal', - positiveArgs: [1, 2], - negativeArgs: [1, 1], - expectedError: 'expected 1 not to equal 1', - }, - { - testName: 'deeply equals', - validation: 'deeply equals', - positiveArgs: [{x: 1}, {x: 1}], - negativeArgs: [{x: 1}, {x: 2}], - expectedError: 'expected { x: 1 } to deeply equal { x: 2 }', - }, - { - testName: 'deeply equals array', - validation: 'deeply equals', - positiveArgs: [[1], [1]], - negativeArgs: [[1], [2]], - expectedError: 'expected [ 1 ] to deeply equal [ 2 ]', - }, - { - testName: 'to deeply equal', - validation: 'to deeply equal', - positiveArgs: [{x: 1}, {x: 1}], - negativeArgs: [{x: 1}, {x: 2}], - expectedError: 'expected { x: 1 } to deeply equal { x: 2 }', - }, - { - testName: 'does not deeply equal', - validation: 'does not deeply equal', - positiveArgs: [{x: 1}, {x: 2}], - negativeArgs: [{x: 1}, {x: 1}], - expectedError: 'expected { x: 1 } not to deeply equal { x: 1 }', - }, - { - testName: 'does not deeply equal array', - validation: 'does not deeply equal', - positiveArgs: [[1], [2]], - negativeArgs: [[], []], - expectedError: 'expected [] not to deeply equal []', - }, - { - testName: 'not to deeply equal', - validation: 'not to deeply equal', - positiveArgs: [{x: 1}, {x: 2}], - negativeArgs: [{x: 1}, {x: 1}], - expectedError: 'expected { x: 1 } not to deeply equal { x: 1 }', - }, - { - testName: 'not to deeply equal', - validation: 'not to deeply equal', - positiveArgs: [{x: 1}, {x: 2}], - negativeArgs: [{x: 1}, {x: 1}], - expectedError: 'expected { x: 1 } not to deeply equal { x: 1 }', - }, - { - testName: 'matches', - validation: 'matches', - positiveArgs: ['expression', '^expression$'], - negativeArgs: ['expression', '^espresso$'], - expectedError: "'expression' to match /^espresso$/", - }, - { - testName: 'to match', - validation: 'to match', - positiveArgs: ['expression', /^expression$/], - negativeArgs: ['expression', /^espresso$/], - expectedError: "'expression' to match /^espresso$/", - }, - { - testName: 'does not match', - validation: 'does not match', - positiveArgs: ['expression', '^espresso$'], - negativeArgs: ['expression', '^expression$'], - expectedError: "'expression' not to match /^expression$/", - }, - { - testName: 'contains', - validation: 'contains', - positiveArgs: ['expression', 'expr'], - negativeArgs: ['expression', 'esp'], - expectedError: "expected 'expression' to contain 'esp'", - }, - { - testName: 'contains with type cast', - validation: 'contains', - positiveArgs: ['111111', 1], - negativeArgs: ['1234', 5], - expectedError: "expected '1234' to contain 5", - }, - { - testName: 'does not contain', - validation: 'does not contain', - positiveArgs: ['expression', 'esp'], - negativeArgs: ['expression', 'expr'], - expectedError: "expected 'expression' not to contain 'expr'", - }, - { - testName: 'have members', - validation: 'have members', - positiveArgs: [ - [1, 2, 3], - [3, 2, 1], - ], - negativeArgs: [[1, 2, 3], [4]], - expectedError: 'expected [ 1, 2, 3 ] to have the same members as [ 4 ]', - }, - { - testName: 'does not have members', - validation: 'does not have members', - positiveArgs: [[1, 2, 3], [4]], - negativeArgs: [ - [1, 2, 3], - [3, 2, 1], - ], - expectedError: 'expected [ 1, 2, 3 ] not to have the same members as [ 3, 2, 1 ]', - }, - { - testName: 'to include members', - validation: 'include members', - positiveArgs: [ - [1, 2, 3], - [2, 1], - ], - negativeArgs: [[1, 2, 3], [4]], - expectedError: 'expected [ 1, 2, 3 ] to be a superset of [ 4 ]', - }, - { - testName: 'not to include members', - validation: 'not to include members', - positiveArgs: [[1, 2, 3], [4]], - negativeArgs: [ - [1, 2, 3], - [2, 1], - ], - expectedError: 'expected [ 1, 2, 3 ] not to be a superset of [ 2, 1 ]', - }, - { - testName: 'to be above', - validation: 'to be above', - positiveArgs: [2, 1], - negativeArgs: [1, 2], - expectedError: 'expected 1 to be greater than 2', - }, - { - testName: 'to be above with type cast', - validation: 'to be above', - positiveArgs: [2, '1'], - negativeArgs: [1, 2], - expectedError: 'expected 1 to be greater than 2', - }, - { - testName: 'to be above throw error if ER is not a number', - validation: 'to be above', - positiveArgs: [2, '1'], - negativeArgs: [1, 'two'], - expectedError: 'two is not a number', - }, - { - testName: 'not to be above', - validation: 'not to be above', - positiveArgs: [1, 1], - negativeArgs: [2, 1], - expectedError: 'expected 2 not to be greater than 1', - }, - { - testName: 'to be below', - validation: 'to be below', - positiveArgs: [1, 2], - negativeArgs: [2, 1], - expectedError: 'expected 2 to be less than 1', - }, - { - testName: 'to be below with type cast', - validation: 'to be below', - positiveArgs: [1, '2'], - negativeArgs: [2, 1], - expectedError: 'expected 2 to be less than 1', - }, - { - testName: 'to be below throw an error if ER is not a number', - validation: 'to be below', - positiveArgs: [1, '2'], - negativeArgs: [2, 'one'], - expectedError: 'one is not a number', - }, - { - testName: 'not to be below', - validation: 'not to be below', - positiveArgs: [1, 1], - negativeArgs: [1, 2], - expectedError: 'expected 1 not to be less than 2', - }, - { - testName: 'to be greater than', - validation: 'to be greater than', - positiveArgs: [2, 1], - negativeArgs: [1, 2], - expectedError: 'expected 1 to be greater than 2', - }, - { - testName: 'is not greater than', - validation: 'is not greater than', - positiveArgs: [2, 2], - negativeArgs: [2, 1], - expectedError: 'expected 2 not to be greater than 1', - }, - { - testName: 'to be less than', - validation: 'to be less than', - positiveArgs: [1, 2], - negativeArgs: [2, 1], - expectedError: 'expected 2 to be less than 1', - }, - { - testName: 'not to be less than', - validation: 'not to be less than', - positiveArgs: [1, 1], - negativeArgs: [1, 2], - expectedError: 'expected 1 not to be less than 2', - }, - { - testName: 'to have type', - validation: 'to have type', - positiveArgs: [1, 'number'], - negativeArgs: [1, 'string'], - expectedError: `expected 1 to have type 'string'`, - }, - { - testName: 'not to have type', - validation: 'not to have type', - positiveArgs: [{}, 'string'], - negativeArgs: [{}, 'object'], - expectedError: `expected {} not to have type 'object'`, - }, - { - testName: 'to have property', - validation: 'to have property', - positiveArgs: [{ prop: 42 }, 'prop'], - negativeArgs: [{ prop: 42 }, 'anotherProp'], - expectedError: 'expected { prop: 42 } to have property \'anotherProp\'', - }, - { - testName: 'not to have property', - validation: 'not to have property', - positiveArgs: [{ prop: 42 }, 'anotherProp'], - negativeArgs: [{ prop: 42 }, 'prop'], - expectedError: 'expected { prop: 42 } not to have property \'prop\'', - }, - { - testName: 'to match schema', - validation: 'to match schema', - positiveArgs: [{ prop: 42, str: 'abc' }, { - type: 'object', - properties: { - prop: {type: 'integer'}, - str: {type: 'string', pattern: '^abc$'} - }, - required: ['prop'], - additionalProperties: false, - }], - negativeArgs: [{ prop: 42, str: 'ab' }, { - type: 'object', - properties: { - prop: {type: 'integer'}, - str: {type: 'string', pattern: '^abc$'} - }, - required: ['prop'], - additionalProperties: false, - }], - expectedError: 'object does not match schema\n/str must match pattern "^abc$" (#/properties/str/pattern)', - }, - { - testName: 'case insensitive equals', - validation: 'case insensitive equals', - positiveArgs: ['some text', 'Some Text'], - negativeArgs: ['some text', 'Another Text'], - expectedError: 'expected \'some text\' to equal \'Another Text\'', - }, - { - testName: 'not to case insensitive equal', - validation: 'not to case insensitive equal', - positiveArgs: ['some text', 'Another Text'], - negativeArgs: ['some text', 'Some Text'], - expectedError: 'expected \'some text\' not to equal \'Some Text\'', - }, - { - testName: 'satisfy', - validation: 'satisfy', - positiveArgs: [1, (arg: number) => [1, 2].includes(arg)], - negativeArgs: [1, (arg: number) => [3, 4].includes(arg)], - expectedError: 'expected 1 to satisfy (arg) => [3, 4].includes(arg)', - }, + { + testName: 'equals', + validation: 'equals', + positiveArgs: [1, 1], + negativeArgs: [1, 2], + expectedError: 'expected 1 to equal 2', + }, + { + testName: 'equal to', + validation: 'equal to', + positiveArgs: [1, 1], + negativeArgs: [1, 2], + expectedError: 'expected 1 to equal 2', + }, + { + testName: 'equals with type cast', + validation: 'equals', + positiveArgs: [1, '1'], + negativeArgs: [1, '2'], + expectedError: 'expected 1 to equal \'2\'', + }, + { + testName: 'to equal', + validation: 'to equal', + positiveArgs: [1, 1], + negativeArgs: [1, 2], + expectedError: 'expected 1 to equal 2', + }, + { + testName: 'does not equal', + validation: 'does not equal', + positiveArgs: [1, 2], + negativeArgs: [1, 1], + expectedError: 'expected 1 not to equal 1', + }, + { + testName: 'not to equal', + validation: 'not to equal', + positiveArgs: [1, 2], + negativeArgs: [1, 1], + expectedError: 'expected 1 not to equal 1', + }, + { + testName: 'not to equal', + validation: 'not to equal', + positiveArgs: [1, 2], + negativeArgs: [1, 1], + expectedError: 'expected 1 not to equal 1', + }, + { + testName: 'strictly equals', + validation: 'strictly equals', + positiveArgs: [1, 1], + negativeArgs: [1, 2], + expectedError: 'expected 1 to equal 2', + }, + { + testName: 'strictly equals with type cast', + validation: 'strictly equals', + positiveArgs: [1, 1], + negativeArgs: [1, '1'], + expectedError: 'expected 1 to equal \'1\'', + }, + { + testName: 'to strictly equal', + validation: 'to strictly equal', + positiveArgs: [1, 1], + negativeArgs: [1, 2], + expectedError: 'expected 1 to equal 2', + }, + { + testName: 'does not strictly equal', + validation: 'does not strictly equal', + positiveArgs: [1, 2], + negativeArgs: [1, 1], + expectedError: 'expected 1 not to equal 1', + }, + { + testName: 'not to strictly equal', + validation: 'not to strictly equal', + positiveArgs: [1, 2], + negativeArgs: [1, 1], + expectedError: 'expected 1 not to equal 1', + }, + { + testName: 'not to strictly equal', + validation: 'not to strictly equal', + positiveArgs: [1, 2], + negativeArgs: [1, 1], + expectedError: 'expected 1 not to equal 1', + }, + { + testName: 'deeply equals', + validation: 'deeply equals', + positiveArgs: [{x: 1}, {x: 1}], + negativeArgs: [{x: 1}, {x: 2}], + expectedError: 'expected { x: 1 } to deeply equal { x: 2 }', + }, + { + testName: 'deeply equals array', + validation: 'deeply equals', + positiveArgs: [[1], [1]], + negativeArgs: [[1], [2]], + expectedError: 'expected [ 1 ] to deeply equal [ 2 ]', + }, + { + testName: 'to deeply equal', + validation: 'to deeply equal', + positiveArgs: [{x: 1}, {x: 1}], + negativeArgs: [{x: 1}, {x: 2}], + expectedError: 'expected { x: 1 } to deeply equal { x: 2 }', + }, + { + testName: 'to deeply strictly equal', + validation: 'to deeply strictly equal', + positiveArgs: [[{x: 1}, {x: 2}], [{x: 1}, {x: 2}]], + negativeArgs: [[{x: 1}, {x: 2}], [{x: 2}, {x: 1}]], + expectedError: 'expected [ { x: 1 }, { x: 2 } ] to deeply strictly equal [ { x: 2 }, { x: 1 } ]', + }, + { + testName: 'does not deeply equal', + validation: 'does not deeply equal', + positiveArgs: [{x: 1}, {x: 2}], + negativeArgs: [{x: 1}, {x: 1}], + expectedError: 'expected { x: 1 } not to deeply equal { x: 1 }', + }, + { + testName: 'does not deeply equal array', + validation: 'does not deeply equal', + positiveArgs: [[1], [2]], + negativeArgs: [[], []], + expectedError: 'expected [] not to deeply equal []', + }, + { + testName: 'not to deeply equal', + validation: 'not to deeply equal', + positiveArgs: [{x: 1}, {x: 2}], + negativeArgs: [{x: 1}, {x: 1}], + expectedError: 'expected { x: 1 } not to deeply equal { x: 1 }', + }, + { + testName: 'not to deeply equal', + validation: 'not to deeply equal', + positiveArgs: [{x: 1}, {x: 2}], + negativeArgs: [{x: 1}, {x: 1}], + expectedError: 'expected { x: 1 } not to deeply equal { x: 1 }', + }, + { + testName: 'matches', + validation: 'matches', + positiveArgs: ['expression', '^expression$'], + negativeArgs: ['expression', '^espresso$'], + expectedError: "'expression' to match /^espresso$/", + }, + { + testName: 'to match', + validation: 'to match', + positiveArgs: ['expression', /^expression$/], + negativeArgs: ['expression', /^espresso$/], + expectedError: "'expression' to match /^espresso$/", + }, + { + testName: 'does not match', + validation: 'does not match', + positiveArgs: ['expression', '^espresso$'], + negativeArgs: ['expression', '^expression$'], + expectedError: "'expression' not to match /^expression$/", + }, + { + testName: 'contains', + validation: 'contains', + positiveArgs: ['expression', 'expr'], + negativeArgs: ['expression', 'esp'], + expectedError: "expected 'expression' to contain 'esp'", + }, + { + testName: 'contains with type cast', + validation: 'contains', + positiveArgs: ['111111', 1], + negativeArgs: ['1234', 5], + expectedError: "expected '1234' to contain 5", + }, + { + testName: 'does not contain', + validation: 'does not contain', + positiveArgs: ['expression', 'esp'], + negativeArgs: ['expression', 'expr'], + expectedError: "expected 'expression' not to contain 'expr'", + }, + { + testName: 'have members', + validation: 'have members', + positiveArgs: [ + [1, 2, 3], + [3, 2, 1], + ], + negativeArgs: [[1, 2, 3], [4]], + expectedError: 'expected [ 1, 2, 3 ] to have the same members as [ 4 ]', + }, + { + testName: 'does not have members', + validation: 'does not have members', + positiveArgs: [[1, 2, 3], [4]], + negativeArgs: [ + [1, 2, 3], + [3, 2, 1], + ], + expectedError: 'expected [ 1, 2, 3 ] not to have the same members as [ 3, 2, 1 ]', + }, + { + testName: 'to include members', + validation: 'include members', + positiveArgs: [ + [1, 2, 3], + [2, 1], + ], + negativeArgs: [[1, 2, 3], [4]], + expectedError: 'expected [ 1, 2, 3 ] to be a superset of [ 4 ]', + }, + { + testName: 'not to include members', + validation: 'not to include members', + positiveArgs: [[1, 2, 3], [4]], + negativeArgs: [ + [1, 2, 3], + [2, 1], + ], + expectedError: 'expected [ 1, 2, 3 ] not to be a superset of [ 2, 1 ]', + }, + { + testName: 'to be above', + validation: 'to be above', + positiveArgs: [2, 1], + negativeArgs: [1, 2], + expectedError: 'expected 1 to be greater than 2', + }, + { + testName: 'to be above with type cast', + validation: 'to be above', + positiveArgs: [2, '1'], + negativeArgs: [1, 2], + expectedError: 'expected 1 to be greater than 2', + }, + { + testName: 'to be above throw error if ER is not a number', + validation: 'to be above', + positiveArgs: [2, '1'], + negativeArgs: [1, 'two'], + expectedError: 'two is not a number', + }, + { + testName: 'not to be above', + validation: 'not to be above', + positiveArgs: [1, 1], + negativeArgs: [2, 1], + expectedError: 'expected 2 not to be greater than 1', + }, + { + testName: 'to be below', + validation: 'to be below', + positiveArgs: [1, 2], + negativeArgs: [2, 1], + expectedError: 'expected 2 to be less than 1', + }, + { + testName: 'to be below with type cast', + validation: 'to be below', + positiveArgs: [1, '2'], + negativeArgs: [2, 1], + expectedError: 'expected 2 to be less than 1', + }, + { + testName: 'to be below throw an error if ER is not a number', + validation: 'to be below', + positiveArgs: [1, '2'], + negativeArgs: [2, 'one'], + expectedError: 'one is not a number', + }, + { + testName: 'not to be below', + validation: 'not to be below', + positiveArgs: [1, 1], + negativeArgs: [1, 2], + expectedError: 'expected 1 not to be less than 2', + }, + { + testName: 'to be greater than', + validation: 'to be greater than', + positiveArgs: [2, 1], + negativeArgs: [1, 2], + expectedError: 'expected 1 to be greater than 2', + }, + { + testName: 'is not greater than', + validation: 'is not greater than', + positiveArgs: [2, 2], + negativeArgs: [2, 1], + expectedError: 'expected 2 not to be greater than 1', + }, + { + testName: 'to be less than', + validation: 'to be less than', + positiveArgs: [1, 2], + negativeArgs: [2, 1], + expectedError: 'expected 2 to be less than 1', + }, + { + testName: 'not to be less than', + validation: 'not to be less than', + positiveArgs: [1, 1], + negativeArgs: [1, 2], + expectedError: 'expected 1 not to be less than 2', + }, + { + testName: 'to have type', + validation: 'to have type', + positiveArgs: [1, 'number'], + negativeArgs: [1, 'string'], + expectedError: `expected 1 to have type 'string'`, + }, + { + testName: 'not to have type', + validation: 'not to have type', + positiveArgs: [{}, 'string'], + negativeArgs: [{}, 'object'], + expectedError: `expected {} not to have type 'object'`, + }, + { + testName: 'to have property', + validation: 'to have property', + positiveArgs: [{prop: 42}, 'prop'], + negativeArgs: [{prop: 42}, 'anotherProp'], + expectedError: 'expected { prop: 42 } to have property \'anotherProp\'', + }, + { + testName: 'not to have property', + validation: 'not to have property', + positiveArgs: [{prop: 42}, 'anotherProp'], + negativeArgs: [{prop: 42}, 'prop'], + expectedError: 'expected { prop: 42 } not to have property \'prop\'', + }, + { + testName: 'to match schema', + validation: 'to match schema', + positiveArgs: [{prop: 42, str: 'abc'}, { + type: 'object', + properties: { + prop: {type: 'integer'}, + str: {type: 'string', pattern: '^abc$'} + }, + required: ['prop'], + additionalProperties: false, + }], + negativeArgs: [{prop: 42, str: 'ab'}, { + type: 'object', + properties: { + prop: {type: 'integer'}, + str: {type: 'string', pattern: '^abc$'} + }, + required: ['prop'], + additionalProperties: false, + }], + expectedError: 'object does not match schema\n/str must match pattern "^abc$" (#/properties/str/pattern)', + }, + { + testName: 'case insensitive equals', + validation: 'case insensitive equals', + positiveArgs: ['some text', 'Some Text'], + negativeArgs: ['some text', 'Another Text'], + expectedError: 'expected \'some text\' to equal \'Another Text\'', + }, + { + testName: 'not to case insensitive equal', + validation: 'not to case insensitive equal', + positiveArgs: ['some text', 'Another Text'], + negativeArgs: ['some text', 'Some Text'], + expectedError: 'expected \'some text\' not to equal \'Some Text\'', + }, + { + testName: 'satisfy', + validation: 'satisfy', + positiveArgs: [1, (arg: number) => [1, 2].includes(arg)], + negativeArgs: [1, (arg: number) => [3, 4].includes(arg)], + expectedError: 'expected 1 to satisfy (arg) => [3, 4].includes(arg)', + }, ]; -test.each(tests)('$testName', ({ validation, positiveArgs, negativeArgs, expectedError }: TestParams) => { - const verify = getValidation(validation); - const catcherPositive = () => verify(...positiveArgs); - const catcherNegative = () => verify(...negativeArgs); - expect(catcherPositive).to.not.throw(); - expect(catcherNegative).to.throw(expectedError); +test.each(tests)('$testName', ({validation, positiveArgs, negativeArgs, expectedError}: TestParams) => { + const verify = getValidation(validation); + const catcherPositive = () => verify(...positiveArgs); + const catcherNegative = () => verify(...negativeArgs); + expect(catcherPositive).to.not.throw(); + expect(catcherNegative).to.throw(expectedError); }); test('should throw an error if validation is not supported', () => { - const catcher = () => getValidation('to be cool'); - expect(catcher).to.throw("Validation 'to be cool' is not supported"); + const catcher = () => getValidation('to be cool'); + expect(catcher).to.throw("Validation 'to be cool' is not supported"); }); test('should throw AssertionError in case of hard error', () => { - const validation = getValidation('to equal'); - const catcher = () => validation(1, 2); - expect(catcher).to.throw(AssertionError, "expected 1 to equal 2"); + const validation = getValidation('to equal'); + const catcher = () => validation(1, 2); + expect(catcher).to.throw(AssertionError, "expected 1 to equal 2"); }); test('should throw SoftAssertionError in case of soft error', () => { - const validation = getValidation('to equal', { soft: true }); - const catcher = () => validation(1, 2); - expect(catcher).to.throw(SoftAssertionError, "expected 1 to equal 2"); + const validation = getValidation('to equal', {soft: true}); + const catcher = () => validation(1, 2); + expect(catcher).to.throw(SoftAssertionError, "expected 1 to equal 2"); }); test('should throw SoftAssertionError in case softly prefix', () => { - const validation = getValidation('to softly equal', { soft: true }); - const catcher = () => validation(1, 2); - expect(catcher).to.throw(SoftAssertionError, "expected 1 to equal 2"); + const validation = getValidation('to softly equal', {soft: true}); + const catcher = () => validation(1, 2); + expect(catcher).to.throw(SoftAssertionError, "expected 1 to equal 2"); }); \ No newline at end of file