fix(node/test): support describe + it

This commit is contained in:
Marvin Hagemeister 2024-04-01 20:50:08 +02:00
parent 240b362c00
commit eb1fa37289

View file

@ -11,6 +11,23 @@ export function run() {
function noop() {}
type SuiteFn = () => unknown;
class NodeSuiteContext {
before: SuiteFn | null = null;
beforeEach: SuiteFn | null = null;
afterEach: SuiteFn | null = null;
after: SuiteFn | null = null;
didRunBefore = false;
firstTestId = -1;
lastTestId = -1;
constructor(public parent: NodeSuiteContext | null, public name: string) {}
}
let TEST_ID = 0;
const ROOT_SUITE = new NodeSuiteContext(null, "");
let CURRENT_SUITE = ROOT_SUITE;
class NodeTestContext {
#denoContext: Deno.TestContext;
@ -113,13 +130,39 @@ function prepareOptions(name, options, fn, overrides) {
return { fn, options: finalOptions, name };
}
function wrapTestFn(fn, resolve) {
function wrapTestFn(
fn: (ctx: NodeTestContext) => unknown,
ancestors: NodeSuiteContext[],
id: number,
resolve: () => void,
) {
return async function (t) {
let i = ancestors.length;
while (i--) {
const ancestor = ancestors[i];
if (ancestor.firstTestId === id) {
await ancestor.before?.();
}
await ancestor.beforeEach?.();
}
const nodeTestContext = new NodeTestContext(t);
try {
await fn(nodeTestContext);
} finally {
resolve();
try {
for (let i = 0; i < ancestors.length; i++) {
const ancestor = ancestors[i];
await ancestor.afterEach?.();
if (ancestor.lastTestId === id) {
await ancestor.after?.();
}
}
} finally {
resolve();
}
}
};
}
@ -127,13 +170,37 @@ function wrapTestFn(fn, resolve) {
function prepareDenoTest(name, options, fn, overrides) {
const prepared = prepareOptions(name, options, fn, overrides);
const { promise, resolve } = Promise.withResolvers();
const { promise, resolve } = Promise.withResolvers<void>();
const ignore = prepared.options.todo || prepared.options.skip;
let id = -1;
if (!ignore) {
id = TEST_ID++;
if (CURRENT_SUITE.firstTestId === -1) {
CURRENT_SUITE.firstTestId = id;
}
CURRENT_SUITE.lastTestId = id;
}
let testName = prepared.name;
const ancestors: NodeSuiteContext[] = [];
let parent: NodeSuiteContext | null = CURRENT_SUITE;
while (parent !== null && parent.parent !== null) {
ancestors.push(parent);
testName = parent.name + " > " + testName;
if (!ignore && parent.firstTestId === -1) {
parent.firstTestId = id;
}
parent = parent.parent;
}
const denoTestOptions = {
name: prepared.name,
fn: wrapTestFn(prepared.fn, resolve),
name: testName,
fn: wrapTestFn(prepared.fn, ancestors, id, resolve),
only: prepared.options.only,
ignore: prepared.options.todo || prepared.options.skip,
ignore,
sanitizeExit: false,
sanitizeOps: false,
sanitizeResources: false,
@ -158,28 +225,37 @@ test.only = function only(name, options, fn) {
return prepareDenoTest(name, options, fn, { only: true });
};
export function describe() {
notImplemented("test.describe");
export function describe(
name: string,
fn: () => unknown,
): void | Promise<void> {
const ctx = new NodeSuiteContext(CURRENT_SUITE, name);
const prev = CURRENT_SUITE;
CURRENT_SUITE = ctx;
try {
fn();
} finally {
CURRENT_SUITE = prev;
prev.lastTestId = TEST_ID;
}
}
export function it() {
notImplemented("test.it");
export const it = test;
export function before(fn: () => unknown) {
CURRENT_SUITE.before = fn;
}
export function before() {
notImplemented("test.before");
export function after(fn: () => unknown) {
CURRENT_SUITE.after = fn;
}
export function after() {
notImplemented("test.after");
export function beforeEach(fn: () => unknown) {
CURRENT_SUITE.beforeEach = fn;
}
export function beforeEach() {
notImplemented("test.beforeEach");
}
export function afterEach() {
notImplemented("test.afterEach");
export function afterEach(fn: () => unknown) {
CURRENT_SUITE.afterEach = fn;
}
export const mock = {