import { getRaw } from "https://esm.town/v/karfau/getRaw";
import { sleep } from "https://esm.town/v/stevekrouse/sleep?v=1";
export async function testRunner<
Input extends {
val?: Ref;
},
>(input: Input, ...tests: Test<Input>[]) {
let reportHeader = "";
try {
const [userHandle, valName] = input.val || [];
if (userHandle && valName) {
const version = await getRaw([
userHandle,
valName,
], "version");
if (typeof version === "number") {
reportHeader = `Testing @${userHandle}.${valName} v${version} ${new Date().toISOString()}:\n`;
}
}
}
catch {
}
let failedCount = 0;
let skipCount = 0;
let start = Date.now();
const result = (await Promise.all(tests.map(async (t, index) => {
const name = t.name.replaceAll(/_/g, " ");
if (name.startsWith("skip")) {
skipCount++;
return `${index + 1} [${name}]`;
}
const tstart = Date.now();
try {
const p = t(input);
if (p && p instanceof Promise) {
await Promise.race([
p,
sleep(2000).then(() => {
throw "async test did not complete in time";
}),
]);
}
return `${index + 1} ${name}: passed in ${Date.now() - tstart}ms`;
}
catch (error) {
failedCount++;
return `${index + 1} ${name}: failed after ${Date.now() - tstart}ms\n ${
/^assert/i.test(error.toString()) ? error : error.stack
}`;
}
}))).join("\n").replaceAll(/\033\[[\d;]+m/g, "");
if (failedCount) {
throw `${reportHeader}${failedCount} of ${tests.length} tests failed ${
skipCount ? `, ${skipCount} skipped` : ""
} (${Date.now() - start}ms):\n${result}`;
}
return `${reportHeader}${tests.length - skipCount} tests passed ${skipCount ? `, ${skipCount} skipped` : ""} (${
Date.now() - start
}ms):\n${result}`;
}
type Ref = [
userHandle: string,
valName: string,
];
type Test<
Input extends {
val?: Ref;
},
> = ((input: Input) => void) | ((input: Input) => Promise<void>);