From 12ea11597443819589763dde54617db563b02896 Mon Sep 17 00:00:00 2001 From: Joshua T Kalis Date: Wed, 20 Nov 2024 11:02:21 -0500 Subject: [PATCH] fix(tests): use .each convention for tests closes #11 --- .../constants/src/__fixtures__/boolean.ts | 46 ++++ .../src/boolean-to-number/index.test.ts | 17 +- .../converters/src/to-boolean/index.test.ts | 43 ++-- packages/predicates/src/index.ts | 2 +- .../predicates/src/is-noyes/index.test.ts | 51 +++-- packages/predicates/src/is-noyes/index.ts | 9 +- .../predicates/src/is-number/index.test.ts | 209 ++++++------------ .../predicates/src/is-string/index.test.ts | 11 +- .../predicates/src/is-worker/index.test.ts | 38 +--- 9 files changed, 187 insertions(+), 239 deletions(-) create mode 100644 packages/constants/src/__fixtures__/boolean.ts diff --git a/packages/constants/src/__fixtures__/boolean.ts b/packages/constants/src/__fixtures__/boolean.ts new file mode 100644 index 00000000..207ecb93 --- /dev/null +++ b/packages/constants/src/__fixtures__/boolean.ts @@ -0,0 +1,46 @@ +/* + * Copyright 2024 Hypergiant Galactic Systems Inc. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +// biome-ignore lint/suspicious/noExplicitAny: +type Callable = (...a: any[]) => any; +type Fixture = [string, boolean, Callable, Values][]; +// biome-ignore lint/suspicious/noExplicitAny: +type Values = any[]; + +export const truthy: Values = [ + 1, + '1', + 'on', + 'true', + 'yes', + true, + 'ON', + 'YES', + 'TRUE', +]; +export const falsey: Values = [ + '', + 0, + '0', + 'off', + 'false', + 'no', + false, + [], + {}, + 'OFF', + 'NO', + 'FALSE', +]; + +export const createFixture = (bool: boolean, ...rest: [Values, Callable][]) => + rest.map(([values, fn]) => [fn.name, bool, fn, values]) as Fixture; diff --git a/packages/converters/src/boolean-to-number/index.test.ts b/packages/converters/src/boolean-to-number/index.test.ts index 02a66a97..243eaadf 100644 --- a/packages/converters/src/boolean-to-number/index.test.ts +++ b/packages/converters/src/boolean-to-number/index.test.ts @@ -13,15 +13,12 @@ import { describe, expect, it } from 'vitest'; import { booleanToNumber } from './'; -const testPairs: [boolean, number][] = [ - [true, 1], - [false, 0], -]; - describe('bool to number', () => { - for (const [input, result] of testPairs) { - it(`should convert ${input} to ${result}`, () => { - expect(booleanToNumber(input)).toEqual(result); - }); - } + it('should convert true to 1', () => { + expect(booleanToNumber(true)).toEqual(1); + }); + + it('should convert false to 0', () => { + expect(booleanToNumber(false)).toEqual(0); + }); }); diff --git a/packages/converters/src/to-boolean/index.test.ts b/packages/converters/src/to-boolean/index.test.ts index 3b041ee7..2ac2044a 100644 --- a/packages/converters/src/to-boolean/index.test.ts +++ b/packages/converters/src/to-boolean/index.test.ts @@ -10,34 +10,25 @@ * governing permissions and limitations under the License. */ -import { expect, it, describe } from 'vitest'; -import { toBoolean } from './'; +import { describe, expect, it } from 'vitest'; -const truthy = [1, '1', 'on', 'true', 'yes', true, 'ON', 'YES', 'TRUE']; -const falsey = [ - 0, - '0', - 'off', - 'false', - 'no', - false, - [], - {}, - 'OFF', - 'NO', - 'FALSE', -]; +import { + createFixture, + falsey, + truthy, +} from '../../../constants/src/__fixtures__/boolean'; -describe('toBoolean', () => { - for (const item of truthy) { - it(`should return true for ${item}`, () => { - expect(toBoolean(item)).toBeTruthy(); - }); - } +import { toBoolean } from './'; - for (const item of falsey) { - it(`should return false for ${item}`, () => { - expect(toBoolean(item)).not.toBeTruthy(); +describe('toBoolean', () => { + describe.each([ + // positive cases + ...createFixture(true, [truthy, toBoolean]), + // negative cases + ...createFixture(false, [falsey, toBoolean]), + ])('%s', (_name, expected, fn, values) => { + it.each(values)(`should return "${expected}" for %j`, (value) => { + expect(fn(value)).toBe(expected); }); - } + }); }); diff --git a/packages/predicates/src/index.ts b/packages/predicates/src/index.ts index ca32bfaf..dbd01dc4 100644 --- a/packages/predicates/src/index.ts +++ b/packages/predicates/src/index.ts @@ -6,7 +6,7 @@ export { isBbox } from './is-bbox'; export { isLatitude } from './is-latitude'; export { isLongitude } from './is-longitude'; export { isNothing } from './is-nothing'; -export { isFalse, isNo, isTrue, isYes } from './is-noyes'; +export { isFalse, isNo, isOff, isOn, isTrue, isYes } from './is-noyes'; export { isFiniteNumber, isFiniteNumeric, diff --git a/packages/predicates/src/is-noyes/index.test.ts b/packages/predicates/src/is-noyes/index.test.ts index 4b584f10..55ffcd7c 100644 --- a/packages/predicates/src/is-noyes/index.test.ts +++ b/packages/predicates/src/is-noyes/index.test.ts @@ -10,26 +10,41 @@ * governing permissions and limitations under the License. */ -import { describe, it, expect } from 'vitest'; -import { isTrue, isYes, isFalse, isNo, isOn, isOff } from './'; +import { describe, expect, it } from 'vitest'; -const truthy = [1, '1', 'on', 'true', 'yes', true, 'ON', 'YES', 'TRUE']; -const falsey = [0, '0', 'off', 'false', 'no', false, 'OFF', 'NO', 'FALSE']; +import { + createFixture, + falsey, + truthy, +} from '../../../constants/src/__fixtures__/boolean'; -describe('boolean validators', () => { - for (const item of truthy) { - it(`should return true for ${item}`, () => { - expect(isOn(item)).toBeTruthy(); - expect(isTrue(item)).toBeTruthy(); - expect(isYes(item)).toBeTruthy(); - }); - } +import { isFalse, isNo, isOff, isOn, isTrue, isYes } from './'; - for (const item of falsey) { - it(`should return false for ${item}`, () => { - expect(isFalse(item)).toBeTruthy(); - expect(isOff(item)).toBeTruthy(); - expect(isNo(item)).toBeTruthy(); +describe('boolean validators', () => { + describe.each([ + // positive cases + ...createFixture( + true, + [falsey, isFalse], + [falsey, isNo], + [falsey, isOff], + [truthy, isOn], + [truthy, isTrue], + [truthy, isYes], + ), + // negative cases + ...createFixture( + false, + [truthy, isFalse], + [truthy, isNo], + [truthy, isOff], + [falsey, isOn], + [falsey, isTrue], + [falsey, isYes], + ), + ])('%s', (_name, expected, fn, values) => { + it.each(values)(`should return "${expected}" for %j`, (value) => { + expect(fn(value)).toBe(expected); }); - } + }); }); diff --git a/packages/predicates/src/is-noyes/index.ts b/packages/predicates/src/is-noyes/index.ts index ca059b68..6b8692ec 100644 --- a/packages/predicates/src/is-noyes/index.ts +++ b/packages/predicates/src/is-noyes/index.ts @@ -10,12 +10,9 @@ * governing permissions and limitations under the License. */ -// const trueRegex = /^(?:y|yes|true|1|on)$/i; -// const falseRegex = /^(?:n|no|false|0|off)$/i; - -// const test = (r: RegExp, val: unknown) => r.test(`${val}`.trim()); - -const falseValues = ['0', 'false', 'n', 'no', 'off']; +// NOTE: a stringify'd empty array will result in ''; a stringify'd object will +// result in '[object Object]' which would then need to be lower cased as below +const falseValues = ['', '0', 'false', 'n', 'no', 'off', '[object object]']; const trueValues = ['1', 'true', 'y', 'yes', 'on']; const test = (list: string[], val: unknown) => diff --git a/packages/predicates/src/is-number/index.test.ts b/packages/predicates/src/is-number/index.test.ts index 0408e757..6405e884 100644 --- a/packages/predicates/src/is-number/index.test.ts +++ b/packages/predicates/src/is-number/index.test.ts @@ -12,152 +12,69 @@ import { expect, it, describe } from 'vitest'; import { isFiniteNumber, isFiniteNumeric, isNumber, isNumeric } from './'; -import { - floatingPointPairs, - floatingPointStringPairs, - nonFinitePairs, - nonFiniteStringPairs, - nonNumericPairs, - numberLikeStringPairs, - numberLiteralPairs, -} from './__fixtures__'; -describe('number validators', () => { - for (const pairs of numberLiteralPairs) { - it(`isFiniteNumber: ${pairs[0]}`, () => { - expect(isFiniteNumber(pairs[1])).toBeTruthy(); - }); - - it(`isFiniteNumeric: ${pairs[0]}`, () => { - expect(isFiniteNumeric(pairs[1])).toBeTruthy(); - }); - - it(`isNumber: ${pairs[0]}`, () => { - expect(isNumber(pairs[1])).toBeTruthy(); - }); - - it(`isNumeric: ${pairs[0]}`, () => { - expect(isNumeric(pairs[1])).toBeTruthy(); - }); - } - - for (const pairs of floatingPointPairs) { - it(`isFiniteNumber: ${pairs[0]}`, () => { - expect(isFiniteNumber(pairs[1])).toBeTruthy(); - }); - - it(`isFiniteNumeric: ${pairs[0]}`, () => { - expect(isFiniteNumeric(pairs[1])).toBeTruthy(); - }); - - it(`isNumber: ${pairs[0]}`, () => { - expect(isNumber(pairs[1])).toBeTruthy(); - }); - - it(`isNumeric: ${pairs[0]}`, () => { - expect(isNumeric(pairs[1])).toBeTruthy(); - }); - } - - for (const pairs of numberLikeStringPairs) { - it(`isFiniteNumber: ${pairs[0]}`, () => { - expect(isFiniteNumber(pairs[1])).toBeFalsy(); - }); - - it(`isFiniteNumeric: ${pairs[0]}`, () => { - expect(isFiniteNumeric(pairs[1])).toBeTruthy(); - }); - - it(`isNumber: ${pairs[0]}`, () => { - expect(isNumber(pairs[1])).toBeFalsy(); - }); - - it(`isNumeric: ${pairs[0]}`, () => { - expect(isNumeric(pairs[1])).toBeTruthy(); - }); - } - - for (const pairs of floatingPointStringPairs) { - it(`isFiniteNumber: ${pairs[0]}`, () => { - expect(isFiniteNumber(pairs[1])).toBeFalsy(); - }); - - it(`isFiniteNumeric: ${pairs[0]}`, () => { - expect(isFiniteNumeric(pairs[1])).toBeTruthy(); - }); +// biome-ignore lint/style/useNumberNamespace: want to diff against Number.NEGATIVE_INFINITY +const INFINITY = Infinity; +const { POSITIVE_INFINITY, NEGATIVE_INFINITY } = Number; - it(`isNumber: ${pairs[0]}`, () => { - expect(isNumber(pairs[1])).toBeFalsy(); - }); - - it(`isNumeric: ${pairs[0]}`, () => { - expect(isNumeric(pairs[1])).toBeTruthy(); - }); - } - - for (const pairs of nonFinitePairs) { - it(`isFiniteNumber: ${pairs[0]}`, () => { - expect(isFiniteNumber(pairs[1])).toBeFalsy(); - }); - - it(`isFiniteNumeric: ${pairs[0]}`, () => { - expect(isFiniteNumeric(pairs[1])).toBeFalsy(); - }); - - it(`isNumber: ${pairs[0]}`, () => { - expect(isNumber(pairs[1])).toBeTruthy(); - }); - - it(`isNumeric: ${pairs[0]}`, () => { - expect(isNumeric(pairs[1])).toBeTruthy(); - }); - } - - for (const pairs of nonFiniteStringPairs) { - it(`isFiniteNumber: ${pairs[0]}`, () => { - expect(isFiniteNumber(pairs[1])).toBeFalsy(); - }); - - it(`isFiniteNumeric: ${pairs[0]}`, () => { - expect(isFiniteNumeric(pairs[1])).toBeFalsy(); - }); - - it(`isNumber: ${pairs[0]}`, () => { - expect(isNumber(pairs[1])).toBeFalsy(); - }); - - it(`isNumeric: ${pairs[0]}`, () => { - expect(isNumeric(pairs[1])).toBeTruthy(); - }); - } - - for (const pairs of nonNumericPairs) { - it(`isFiniteNumber: ${pairs[0]}`, () => { - expect(isFiniteNumber(pairs[1])).toBeFalsy(); - }); - - it(`isFiniteNumeric: ${pairs[0]}`, () => { - // TODO: find a cleaner way to do this - // NOTE: parseFloat will convert 7.2acdgs to 7.2 - if (pairs[0].includes('trailing non-numeric')) { - expect(isFiniteNumeric(pairs[1])).toBeTruthy(); - } else { - expect(isFiniteNumeric(pairs[1])).toBeFalsy(); - } - }); - - it(`isNumber: ${pairs[0]}`, () => { - expect(isNumber(pairs[1])).toBeFalsy(); - }); - - it(`isNumeric: ${pairs[0]}`, () => { - // TODO: find a cleaner way to do this - // NOTE: parseFloat will convert 7.2acdgs to 7.2 - if (pairs[0].includes('trailing non-numeric')) { - expect(isFiniteNumeric(pairs[1])).toBeTruthy(); - } else { - expect(isFiniteNumeric(pairs[1])).toBeFalsy(); - } - }); - } +describe('number validators', () => { + it.each` + label | value | isFiniteNumber | isFiniteNumeric | isNumber | isNumeric + ${'integer literal: negative'} | ${-1} | ${true} | ${true} | ${true} | ${true} + ${'integer literal: zero'} | ${0} | ${true} | ${true} | ${true} | ${true} + ${'integer literal: positive'} | ${1} | ${true} | ${true} | ${true} | ${true} + ${'integer literal: octal'} | ${0o0144} | ${true} | ${true} | ${true} | ${true} + ${'integer literal: hexadecimal'} | ${0xfff} | ${true} | ${true} | ${true} | ${true} + ${'floating point: nagative'} | ${-1.1234} | ${true} | ${true} | ${true} | ${true} + ${'floating point: positive'} | ${1.1234} | ${true} | ${true} | ${true} | ${true} + ${'floating point: exponential notation'} | ${8e5} | ${true} | ${true} | ${true} | ${true} + ${'number-like string: negative'} | ${'-1'} | ${false} | ${true} | ${false} | ${true} + ${'number-like string: zero'} | ${'0'} | ${false} | ${true} | ${false} | ${true} + ${'number-like string: positive'} | ${'1'} | ${false} | ${true} | ${false} | ${true} + ${'number-like string: octal'} | ${'040'} | ${false} | ${true} | ${false} | ${true} + ${'number-like string: hexadecimal'} | ${'0xFF'} | ${false} | ${true} | ${false} | ${true} + ${'number-like string: zero padded'} | ${'05'} | ${false} | ${true} | ${false} | ${true} + ${'float string: negative'} | ${'-1.1234'} | ${false} | ${true} | ${false} | ${true} + ${'float string: positive'} | ${'1.1234'} | ${false} | ${true} | ${false} | ${true} + ${'float string: exponential notation'} | ${'123e-2'} | ${false} | ${true} | ${false} | ${true} + ${'float string: zero padded'} | ${'01.1234'} | ${false} | ${true} | ${false} | ${true} + ${'non-finite number: NaN'} | ${Number.NaN} | ${false} | ${false} | ${true} | ${true} + ${'non-finite number: positive'} | ${POSITIVE_INFINITY} | ${false} | ${false} | ${true} | ${true} + ${'non-finite number: negative'} | ${NEGATIVE_INFINITY} | ${false} | ${false} | ${true} | ${true} + ${'non-finite number: positive primitive'} | ${INFINITY} | ${false} | ${false} | ${true} | ${true} + ${'non-finite number: negative primitive'} | ${-INFINITY} | ${false} | ${false} | ${true} | ${true} + ${'non-finite string: NaN string'} | ${'NaN'} | ${false} | ${false} | ${false} | ${true} + ${'non-finite string: positive'} | ${'Infinity'} | ${false} | ${false} | ${false} | ${true} + ${'non-finite string: negative'} | ${'-Infinity'} | ${false} | ${false} | ${false} | ${true} + ${'non-numeric: empty'} | ${''} | ${false} | ${false} | ${false} | ${false} + ${'non-numeric: whitespace character'} | ${' '} | ${false} | ${false} | ${false} | ${false} + ${'non-numeric: tab character'} | ${'\t\t'} | ${false} | ${false} | ${false} | ${false} + ${'non-numeric: alphanumeric '} | ${'abcdefghi123456'} | ${false} | ${false} | ${false} | ${false} + ${'non-numeric: non-numeric '} | ${'xabcdefx'} | ${false} | ${false} | ${false} | ${false} + ${'non-numeric: preceding non-numeric'} | ${'bcfed5.2'} | ${false} | ${false} | ${false} | ${false} + ${'non-numeric: literal true'} | ${true} | ${false} | ${false} | ${false} | ${false} + ${'non-numeric: literal false'} | ${false} | ${false} | ${false} | ${false} | ${false} + ${'non-numeric: undefined'} | ${undefined} | ${false} | ${false} | ${false} | ${false} + ${'non-numeric: null'} | ${null} | ${false} | ${false} | ${false} | ${false} + ${'non-numeric: date object'} | ${new Date()} | ${false} | ${false} | ${false} | ${false} + ${'non-numeric: object'} | ${{}} | ${false} | ${false} | ${false} | ${false} + ${'non-numeric: function instance'} | ${() => void 0} | ${false} | ${false} | ${false} | ${false} + `('$label', ({ value, ...expected }) => { + expect(isFiniteNumber(value)).toBe(expected.isFiniteNumber); + expect(isFiniteNumeric(value)).toBe(expected.isFiniteNumeric); + expect(isNumber(value)).toBe(expected.isNumber); + expect(isNumeric(value)).toBe(expected.isNumeric); + }); + + it('should be different for number with trailing non-numeric characters', () => { + const value = '7.2abcdef'; + + expect(isFiniteNumber(value)).toBe(false); + expect(isNumber(value)).toBe(false); + + // NOTE: these differ from it.each testing above + // parseFloat will convert value to 7.2 just fine + expect(isFiniteNumeric(value)).toBe(true); + expect(isNumeric(value)).toBe(true); + }); }); diff --git a/packages/predicates/src/is-string/index.test.ts b/packages/predicates/src/is-string/index.test.ts index 8829e73d..aafb03cc 100644 --- a/packages/predicates/src/is-string/index.test.ts +++ b/packages/predicates/src/is-string/index.test.ts @@ -15,9 +15,10 @@ import { isString } from './'; import { nonStringPairs } from './__fixtures__'; describe('string validators', () => { - for (const pair of nonStringPairs) { - it(`isString: ${pair[0]}`, () => { - expect(isString(pair[1])).toBeFalsy(); - }); - } + it.each(nonStringPairs)( + 'isString(%s) should be false', + (_expected, value) => { + expect(isString(value)).toBeFalsy(); + }, + ); }); diff --git a/packages/predicates/src/is-worker/index.test.ts b/packages/predicates/src/is-worker/index.test.ts index cf706453..bec9f670 100644 --- a/packages/predicates/src/is-worker/index.test.ts +++ b/packages/predicates/src/is-worker/index.test.ts @@ -14,34 +14,18 @@ import '@vitest/web-worker'; import { describe, it, expect } from 'vitest'; import { isSharedWorker, isWorker } from './'; -const nonWorkerPairs = [ - ['array', []], - ['object', {}], - [ - 'function', - () => { - // noop - }, - ], -] as const; - const workerUrl = new URL('./__fixtures__/worker.ts', import.meta.url); -const sharedWorker = new SharedWorker(workerUrl); -const worker = new Worker(workerUrl); describe('worker validators', () => { - expect(isSharedWorker(sharedWorker)).toBeTruthy(); - expect(isSharedWorker(worker)).toBeFalsy(); - expect(isWorker(worker)).toBeTruthy(); - expect(isWorker(sharedWorker)).toBeFalsy(); - - for (const pair of nonWorkerPairs) { - it(`isSharedWorker: ${pair[0]}`, () => { - expect(isSharedWorker(pair[1])).toBeFalsy(); - }); - - it(`isWorker: ${pair[0]}`, () => { - expect(isWorker(pair[1])).toBeFalsy(); - }); - } + it.each` + label | value | isShared | isWorker + ${'array'} | ${[]} | ${false} | ${false} + ${'object'} | ${{}} | ${false} | ${false} + ${'function'} | ${() => void 0} | ${false} | ${false} + ${'SharedWorker'} | ${new SharedWorker(workerUrl)} | ${true} | ${false} + ${'Worker'} | ${new Worker(workerUrl)} | ${false} | ${true} + `('$label', ({ value, ...expected }) => { + expect(isSharedWorker(value)).toBe(expected.isShared); + expect(isWorker(value)).toBe(expected.isWorker); + }); });