@augment-vir/test
Version:
A universal testing suite that works with Mocha style test runners _and_ Node.js's built-in test runner.
104 lines (103 loc) • 3.71 kB
JavaScript
import { assert, check, } from '@augment-vir/assert';
import { ensureErrorAndPrependMessage, } from '@augment-vir/core';
import { it } from './universal-it.js';
const unsetError = Symbol('unset-error');
/**
* Succinctly run many input / output tests for a pure function without repeating `it` boilerplate.
* Compatible with both [Node.js's test runner](https://nodejs.org/api/test.html) and
* [web-test-runner](https://modern-web.dev/docs/test-runner/overview/) or other Mocha-style test
* runners.
*
* @category Test
* @category Package : @augment-vir/test
* @example
*
* ```ts
* import {itCases, describe} from '@augment-vir/test';
*
* function myFunctionToTest(a: number, b: number) {
* return a + b;
* }
*
* describe(myFunctionToTest.name, () => {
* itCases(myFunctionToTest, [
* {
* it: 'handles negative numbers',
* inputs: [
* -1,
* -2,
* ],
* expect: -3,
* },
* {
* it: 'handles 0',
* inputs: [
* 0,
* 0,
* ],
* expect: 0,
* },
* {
* it: 'adds',
* inputs: [
* 3,
* 5,
* ],
* expect: 8,
* },
* ]);
* });
* ```
*
* @package [`@augment-vir/test`](https://www.npmjs.com/package/@augment-vir/test)
*/
export function itCases(functionToTest, testCasesOrCustomAsserter, maybeTestCases) {
const testCases = (maybeTestCases ||
testCasesOrCustomAsserter);
if (!check.isArray(testCases)) {
throw new TypeError('expected an array of test cases');
}
const asserter = maybeTestCases ? testCasesOrCustomAsserter : assert.deepEquals;
if (typeof asserter !== 'function') {
throw new TypeError('expected a function for the custom asserter');
}
return testCases.map((testCase) => {
const itFunction = testCase.only ? it.only : testCase.skip ? it.skip : it;
return itFunction(testCase.it, async () => {
const functionInputs = 'input' in testCase
? [testCase.input]
: 'inputs' in testCase
? testCase.inputs
: // as cast here to cover the case where the input has NO inputs
[];
if ('expect' in testCase) {
await assert.output(asserter, functionToTest, functionInputs, testCase.expect, testCase.it);
}
else {
let caughtError = unsetError;
try {
await functionToTest(...functionInputs);
}
catch (thrownError) {
caughtError = thrownError;
}
const errorThrower = () => {
if (caughtError !== unsetError) {
throw caughtError;
}
};
// give a better name if possible
Object.defineProperty(errorThrower, 'name', {
value: functionToTest.name,
});
const expectsAnError = !!(testCase.throws?.matchConstructor || testCase.throws?.matchMessage);
if (caughtError !== unsetError && !expectsAnError) {
throw ensureErrorAndPrependMessage(caughtError, `${functionToTest.name} threw an unexpected error`);
}
else if (expectsAnError) {
assert.throws(errorThrower, testCase.throws, 'Caught error did not match expectations');
}
}
});
});
}