@fast-check/jest
Version:
Property based testing for Jest based on fast-check
96 lines (95 loc) • 4.66 kB
JavaScript
import { readConfigureGlobal, record } from "fast-check";
//#region src/internals/TestWithPropRunnerBuilder.ts
function wrapProp(prop) {
return (...args) => Promise.resolve(prop(...args));
}
function buildTestWithPropRunner(testFn, label, arbitraries, prop, params, timeout, jest, fc) {
const customParams = { ...params };
if (customParams.seed === void 0) {
const seedFromGlobals = readConfigureGlobal().seed;
if (seedFromGlobals !== void 0) customParams.seed = seedFromGlobals;
else {
const seedFromJest = typeof jest.getSeed === "function" ? jest.getSeed() : void 0;
if (seedFromJest !== void 0) customParams.seed = seedFromJest;
else customParams.seed = Date.now() ^ Math.random() * 4294967296;
}
}
if (customParams.interruptAfterTimeLimit === void 0) customParams.interruptAfterTimeLimit = fc.readConfigureGlobal().interruptAfterTimeLimit;
const jestTimeout = timeout !== void 0 ? timeout : extractJestGlobalTimeout();
if (jestTimeout !== void 0) if (customParams.interruptAfterTimeLimit === void 0) customParams.interruptAfterTimeLimit = jestTimeout;
else customParams.interruptAfterTimeLimit = Math.min(customParams.interruptAfterTimeLimit, jestTimeout);
else console.warn("Unable to get back timeout of Jest, falling back onto Jest for global timeout handling");
const promiseProp = wrapProp(prop);
const propertyInstance = fc.asyncProperty(...arbitraries, promiseProp);
testFn(`${label} (with seed=${customParams.seed})`, async () => {
await fc.assert(propertyInstance, customParams);
}, jestTimeout !== void 0 ? 2147483647 : void 0);
}
function extractJestGlobalTimeout() {
const jestTimeout = globalThis[Symbol.for("TEST_TIMEOUT_SYMBOL")];
if (typeof jestTimeout === "number") return jestTimeout;
const stateSymbolStringValue = String(Symbol("JEST_STATE_SYMBOL"));
for (const key of Object.getOwnPropertySymbols(globalThis)) if (String(key) === stateSymbolStringValue) {
const jestState = globalThis[key];
if (jestState !== null && typeof jestState === "object" && typeof jestState.testTimeout === "number") return jestState.testTimeout;
}
if (typeof jasmine !== "undefined") return jasmine.DEFAULT_TIMEOUT_INTERVAL;
}
//#endregion
//#region src/internals/TestBuilder.ts
function adaptParametersForRecord(parameters, originalParamaters) {
return {
...parameters,
examples: parameters.examples !== void 0 ? parameters.examples.map((example) => example[0]) : void 0,
reporter: originalParamaters.reporter,
asyncReporter: originalParamaters.asyncReporter
};
}
function adaptExecutionTreeForRecord(executionSummary) {
return executionSummary.map((summary) => ({
...summary,
value: summary.value[0],
children: adaptExecutionTreeForRecord(summary.children)
}));
}
function adaptRunDetailsForRecord(runDetails, originalParamaters) {
return {
...runDetails,
counterexample: runDetails.counterexample !== null ? runDetails.counterexample[0] : null,
failures: runDetails.failures.map((failure) => failure[0]),
executionSummary: adaptExecutionTreeForRecord(runDetails.executionSummary),
runConfiguration: adaptParametersForRecord(runDetails.runConfiguration, originalParamaters)
};
}
function buildTestProp(testFn, jest, fc) {
return (arbitraries, params) => {
if (Array.isArray(arbitraries)) return (testName, prop, timeout) => buildTestWithPropRunner(testFn, testName, arbitraries, prop, params, timeout, jest, fc);
return (testName, prop, timeout) => {
const recordArb = record(arbitraries);
const recordParams = params !== void 0 ? {
...params,
examples: params.examples !== void 0 ? params.examples.map((example) => [example]) : void 0,
reporter: params.reporter !== void 0 ? (runDetails) => params.reporter(adaptRunDetailsForRecord(runDetails, params)) : void 0,
asyncReporter: params.asyncReporter !== void 0 ? (runDetails) => params.asyncReporter(adaptRunDetailsForRecord(runDetails, params)) : void 0
} : void 0;
buildTestWithPropRunner(testFn, testName, [recordArb], (value) => prop(value), recordParams, timeout, jest, fc);
};
};
}
/**
* Build the enriched version of {it,test}, the one with added `.prop`
*/
function buildTest(testFn, jest, fc) {
let atLeastOneExtra = false;
const extraKeys = {};
for (const key in testFn) if (typeof testFn[key] === "function") {
atLeastOneExtra = true;
extraKeys[key] = key !== "each" ? buildTest(testFn[key], jest, fc) : testFn[key];
}
if (!atLeastOneExtra) return testFn;
const enrichedTestFn = (...args) => testFn(...args);
if ("each" in testFn) extraKeys["prop"] = buildTestProp(testFn, jest, fc);
return Object.assign(enrichedTestFn, extraKeys);
}
//#endregion
export { buildTest as t };