1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96 |
x1
x1
x1
x1
x1
x1
x55
x55
x55
x55
x55
x55
x55
x55
x55
x55
x15
x15
x55
x55
x55
x114
x114
x55
x55
x54
x12
x12
x54
x55
x114
x114
x55
x55
x55
x15
x55
x55
x30
x55
x55
x17
x55
x55
x12
x12
x55
x55
x55
x55 |
I
I
|
import { describe, beforeAll, afterAll } from "jsr:@std/testing/bdd";
import type Assert from '../shared/assert.ts';
import type { HooksObject } from '../types.ts';
import ModuleContext from '../shared/module-context.ts';
export type { Assert };
export type { HookFn, HooksObject, PushResultInfo } from '../types.ts';
/**
* Defines a test module (suite) for Deno's BDD test runner.
*
* Wraps `describe()` from `@std/testing/bdd` and sets up the QUnit lifecycle
* (before/beforeEach/afterEach/after hooks, assertion counting, steps tracking).
*
* @param {string} moduleName - Name of the test suite
* @param {object} [runtimeOptions] - Optional Deno BDD options forwarded to `describe()`
* (e.g. `{ concurrency: false }`, `{ permissions: { read: true } }`)
* @param {function} moduleContent - Callback that defines tests and hooks via `hooks.before`,
* `hooks.beforeEach`, `hooks.afterEach`, `hooks.after`
* @returns {void}
* @example
* ```js ignore
* import { module, test } from "qunitx";
*
* module("Math", (hooks) => {
* hooks.before((assert) => {
* assert.step("before hook ran");
* });
*
* test("addition", (assert) => {
* assert.equal(2 + 2, 4);
* });
* });
* ```
*/
export default function module(moduleName: string, moduleContent: (hooks: HooksObject<Assert>, meta: { moduleName: string; options: unknown }) => void): void;
/** Defines a test module (suite) with optional Deno BDD runtime options forwarded to `describe()`. */
export default function module(moduleName: string, runtimeOptions: object, moduleContent: (hooks: HooksObject<Assert>, meta: { moduleName: string; options: unknown }) => void): void;
export default function module(
moduleName: string,
runtimeOptions: object | ((hooks: HooksObject<Assert>) => void),
moduleContent?: (hooks: HooksObject<Assert>, meta: { moduleName: string; options: unknown }) => void,
): void {
const targetRuntimeOptions = moduleContent ? runtimeOptions as object : {};
const targetModuleContent = (moduleContent ? moduleContent : runtimeOptions) as (hooks: HooksObject<Assert>, meta: { moduleName: string; options: unknown }) => void;
const moduleContext = new ModuleContext(moduleName);
describe(moduleName, { ...targetRuntimeOptions }, function () {
const beforeHooks: ((assert: Assert) => void | Promise<void>)[] = [];
const afterHooks: ((assert: Assert) => void | Promise<void>)[] = [];
beforeAll(async function () {
// before() assertions are attributed to the first direct test only (matching QUnit's model).
// Tests inherit parent context via prototype chain, so no Object.assign needed.
const firstTest = moduleContext.tests[0];
const beforeAssert = firstTest ? firstTest.assert! : moduleContext.assert!;
for (const hook of beforeHooks) {
await hook.call(moduleContext.userContext, beforeAssert);
}
});
afterAll(async () => {
for (const testContext of moduleContext.tests) {
await testContext.assert!.waitForAsyncOps();
}
const lastTest = moduleContext.tests[moduleContext.tests.length - 1];
if (lastTest) {
for (let j = afterHooks.length - 1; j >= 0; j--) {
await afterHooks[j]!.call(lastTest.userContext, lastTest.assert!);
}
}
for (let i = 0, len = moduleContext.tests.length; i < len; i++) {
moduleContext.tests[i].finish();
}
});
targetModuleContent.call(moduleContext.userContext, {
before(beforeFn) {
beforeHooks.push(beforeFn);
},
beforeEach(beforeEachFn) {
moduleContext.beforeEachHooks.push(beforeEachFn);
},
afterEach(afterEachFn) {
moduleContext.afterEachHooks.push(afterEachFn);
},
after(afterFn) {
afterHooks.push(afterFn);
}
}, { moduleName, options: runtimeOptions });
ModuleContext.currentModuleChain.pop();
});
}
|