diff --git a/packages/constants/src/__fixtures__/boolean.ts b/packages/constants/src/__fixtures__/boolean.ts new file mode 100644 index 00000000..6d7988f7 --- /dev/null +++ b/packages/constants/src/__fixtures__/boolean.ts @@ -0,0 +1,45 @@ +/* + * 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..8716b922 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.each` + input | result + ${true} | ${1} + ${false} | ${0} + `('should convert $input to $result', ({ input, result }) => { + expect(booleanToNumber(input)).toEqual(result); + }); }); 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/is-noyes/index.test.ts b/packages/predicates/src/is-noyes/index.test.ts index 1fdf5f6b..87fe97e3 100644 --- a/packages/predicates/src/is-noyes/index.test.ts +++ b/packages/predicates/src/is-noyes/index.test.ts @@ -10,24 +10,40 @@ * governing permissions and limitations under the License. */ -import { describe, it, expect } from 'vitest'; -import { isTrue, isYes, isFalse, isNo } 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(isTrue(item)).toBeTruthy(); - expect(isYes(item)).toBeTruthy(); - }); - } +import { isFalse, isNo, isTrue, isYes } from './'; - for (const item of falsey) { - it(`should return false for ${item}`, () => { - expect(isFalse(item)).toBeTruthy(); - expect(isNo(item)).toBeTruthy(); +describe('boolean validators', () => { + describe.each([ + // positive cases + ...createFixture( + true, + [falsey, isFalse], + [falsey, isNo], + [truthy, isTrue], + [truthy, isYes], + ), + // negative cases + ...createFixture( + false, + [truthy, isFalse], + [truthy, isNo], + [falsey, isTrue], + [falsey, isYes], + ), + ])('%s', (_name, expected, fn, values) => { + it.each(values)(`should return "${expected}" for %j`, (value) => { + // TODO: remove condition once implementation is updated from another branch + if (!/^\[object (?:array|object)\]$/i.test({}.toString.call(value))) { + expect(fn(value)).toBe(expected); + } }); - } + }); }); 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); + }); });