@ricsam/quickjs-test-environment 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/cjs/describe.cjs +180 -0
  2. package/dist/cjs/describe.cjs.map +10 -0
  3. package/dist/cjs/expect.cjs +622 -0
  4. package/dist/cjs/expect.cjs.map +10 -0
  5. package/dist/cjs/handle.cjs +79 -0
  6. package/dist/cjs/handle.cjs.map +10 -0
  7. package/dist/cjs/hooks.cjs +83 -0
  8. package/dist/cjs/hooks.cjs.map +10 -0
  9. package/dist/cjs/index.cjs +2 -2
  10. package/dist/cjs/index.cjs.map +2 -2
  11. package/dist/cjs/package.json +1 -1
  12. package/dist/cjs/runner.cjs +346 -0
  13. package/dist/cjs/runner.cjs.map +10 -0
  14. package/dist/cjs/setup.cjs +70 -0
  15. package/dist/cjs/setup.cjs.map +10 -0
  16. package/dist/cjs/types.cjs +26 -0
  17. package/dist/cjs/types.cjs.map +9 -0
  18. package/dist/mjs/describe.mjs +149 -0
  19. package/dist/mjs/describe.mjs.map +10 -0
  20. package/dist/mjs/expect.mjs +591 -0
  21. package/dist/mjs/expect.mjs.map +10 -0
  22. package/dist/mjs/handle.mjs +48 -0
  23. package/dist/mjs/handle.mjs.map +10 -0
  24. package/dist/mjs/hooks.mjs +52 -0
  25. package/dist/mjs/hooks.mjs.map +10 -0
  26. package/dist/mjs/index.mjs +2 -2
  27. package/dist/mjs/index.mjs.map +2 -2
  28. package/dist/mjs/package.json +1 -1
  29. package/dist/mjs/runner.mjs +315 -0
  30. package/dist/mjs/runner.mjs.map +10 -0
  31. package/dist/mjs/setup.mjs +39 -0
  32. package/dist/mjs/setup.mjs.map +10 -0
  33. package/dist/mjs/types.mjs +3 -0
  34. package/dist/mjs/types.mjs.map +9 -0
  35. package/dist/types/globals/describe.d.ts +5 -0
  36. package/dist/types/globals/expect.d.ts +3 -0
  37. package/dist/types/globals/hooks.d.ts +3 -0
  38. package/dist/types/runner.d.ts +5 -0
  39. package/dist/types/types.d.ts +2 -0
  40. package/package.json +2 -2
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/handle.ts"],
4
+ "sourcesContent": [
5
+ "import type { TestEnvironmentHandle, TestState, RunResults } from \"./types.mjs\";\nimport { runTests } from \"./runner.mjs\";\n\nexport function createTestEnvironmentHandle(state: TestState): TestEnvironmentHandle {\n return {\n get stateMap() {\n return state.stateMap;\n },\n\n async run(): Promise<RunResults> {\n return runTests(state);\n },\n\n hasTests(): boolean {\n return state.suites.some(suite =>\n suite.tests.length > 0 || suite.children.length > 0\n );\n },\n\n getTestCount(): number {\n let count = 0;\n const countSuite = (suite: typeof state.suites[0]) => {\n count += suite.tests.length;\n suite.children.forEach(countSuite);\n };\n state.suites.forEach(countSuite);\n return count;\n },\n\n reset(): void {\n // Dispose test function handles\n const disposeSuite = (suite: typeof state.suites[0]) => {\n suite.tests.forEach(test => test.fn.dispose());\n suite.beforeAll.forEach(fn => fn.dispose());\n suite.afterAll.forEach(fn => fn.dispose());\n suite.beforeEach.forEach(fn => fn.dispose());\n suite.afterEach.forEach(fn => fn.dispose());\n suite.children.forEach(disposeSuite);\n };\n state.suites.forEach(disposeSuite);\n\n state.suites = [];\n state.currentSuite = null;\n },\n\n dispose(): void {\n this.reset();\n // Dispose global function handles\n state.handles.forEach(handle => handle.dispose());\n state.handles = [];\n },\n };\n}\n"
6
+ ],
7
+ "mappings": ";;AACA;AAEO,SAAS,2BAA2B,CAAC,OAAyC;AAAA,EACnF,OAAO;AAAA,QACD,QAAQ,GAAG;AAAA,MACb,OAAO,MAAM;AAAA;AAAA,SAGT,IAAG,GAAwB;AAAA,MAC/B,OAAO,SAAS,KAAK;AAAA;AAAA,IAGvB,QAAQ,GAAY;AAAA,MAClB,OAAO,MAAM,OAAO,KAAK,WACvB,MAAM,MAAM,SAAS,KAAK,MAAM,SAAS,SAAS,CACpD;AAAA;AAAA,IAGF,YAAY,GAAW;AAAA,MACrB,IAAI,QAAQ;AAAA,MACZ,MAAM,aAAa,CAAC,UAAkC;AAAA,QACpD,SAAS,MAAM,MAAM;AAAA,QACrB,MAAM,SAAS,QAAQ,UAAU;AAAA;AAAA,MAEnC,MAAM,OAAO,QAAQ,UAAU;AAAA,MAC/B,OAAO;AAAA;AAAA,IAGT,KAAK,GAAS;AAAA,MAEZ,MAAM,eAAe,CAAC,UAAkC;AAAA,QACtD,MAAM,MAAM,QAAQ,UAAQ,KAAK,GAAG,QAAQ,CAAC;AAAA,QAC7C,MAAM,UAAU,QAAQ,QAAM,GAAG,QAAQ,CAAC;AAAA,QAC1C,MAAM,SAAS,QAAQ,QAAM,GAAG,QAAQ,CAAC;AAAA,QACzC,MAAM,WAAW,QAAQ,QAAM,GAAG,QAAQ,CAAC;AAAA,QAC3C,MAAM,UAAU,QAAQ,QAAM,GAAG,QAAQ,CAAC;AAAA,QAC1C,MAAM,SAAS,QAAQ,YAAY;AAAA;AAAA,MAErC,MAAM,OAAO,QAAQ,YAAY;AAAA,MAEjC,MAAM,SAAS,CAAC;AAAA,MAChB,MAAM,eAAe;AAAA;AAAA,IAGvB,OAAO,GAAS;AAAA,MACd,KAAK,MAAM;AAAA,MAEX,MAAM,QAAQ,QAAQ,YAAU,OAAO,QAAQ,CAAC;AAAA,MAChD,MAAM,UAAU,CAAC;AAAA;AAAA,EAErB;AAAA;",
8
+ "debugId": "1B6E6A16C6B25D0B64756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,52 @@
1
+ // @bun
2
+ // packages/test-environment/src/globals/hooks.ts
3
+ import { createSuite } from "./describe.mjs";
4
+ function ensureSuite(state) {
5
+ if (!state.currentSuite) {
6
+ const rootSuite = createSuite("(root)", null);
7
+ state.suites.push(rootSuite);
8
+ state.currentSuite = rootSuite;
9
+ }
10
+ return state.currentSuite;
11
+ }
12
+ function setupHooks(context, state) {
13
+ const handles = [];
14
+ const beforeAllFn = context.newFunction("beforeAll", (fnHandle) => {
15
+ const suite = ensureSuite(state);
16
+ const fnDup = fnHandle.dup();
17
+ suite.beforeAll.push(fnDup);
18
+ return context.undefined;
19
+ });
20
+ context.setProp(context.global, "beforeAll", beforeAllFn);
21
+ handles.push(beforeAllFn);
22
+ const afterAllFn = context.newFunction("afterAll", (fnHandle) => {
23
+ const suite = ensureSuite(state);
24
+ const fnDup = fnHandle.dup();
25
+ suite.afterAll.push(fnDup);
26
+ return context.undefined;
27
+ });
28
+ context.setProp(context.global, "afterAll", afterAllFn);
29
+ handles.push(afterAllFn);
30
+ const beforeEachFn = context.newFunction("beforeEach", (fnHandle) => {
31
+ const suite = ensureSuite(state);
32
+ const fnDup = fnHandle.dup();
33
+ suite.beforeEach.push(fnDup);
34
+ return context.undefined;
35
+ });
36
+ context.setProp(context.global, "beforeEach", beforeEachFn);
37
+ handles.push(beforeEachFn);
38
+ const afterEachFn = context.newFunction("afterEach", (fnHandle) => {
39
+ const suite = ensureSuite(state);
40
+ const fnDup = fnHandle.dup();
41
+ suite.afterEach.push(fnDup);
42
+ return context.undefined;
43
+ });
44
+ context.setProp(context.global, "afterEach", afterEachFn);
45
+ handles.push(afterEachFn);
46
+ return handles;
47
+ }
48
+ export {
49
+ setupHooks
50
+ };
51
+
52
+ //# debugId=337AAC81CA448F0564756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/globals/hooks.ts"],
4
+ "sourcesContent": [
5
+ "import type { QuickJSContext, QuickJSHandle } from \"quickjs-emscripten\";\nimport type { TestState, RegisteredSuite } from \"../types.mjs\";\nimport { createSuite } from \"./describe.mjs\";\n\n/**\n * Helper to ensure we have a current suite for hook registration.\n * Creates an implicit root suite if called at top level.\n */\nfunction ensureSuite(state: TestState): RegisteredSuite {\n if (!state.currentSuite) {\n // Create implicit root suite for top-level hooks\n const rootSuite = createSuite(\"(root)\", null);\n state.suites.push(rootSuite);\n state.currentSuite = rootSuite;\n }\n return state.currentSuite;\n}\n\nexport function setupHooks(\n context: QuickJSContext,\n state: TestState\n): QuickJSHandle[] {\n const handles: QuickJSHandle[] = [];\n\n // beforeAll - runs once before all tests in the suite\n const beforeAllFn = context.newFunction(\"beforeAll\", (fnHandle) => {\n const suite = ensureSuite(state);\n const fnDup = fnHandle.dup();\n suite.beforeAll.push(fnDup);\n return context.undefined;\n });\n context.setProp(context.global, \"beforeAll\", beforeAllFn);\n handles.push(beforeAllFn);\n\n // afterAll - runs once after all tests in the suite\n const afterAllFn = context.newFunction(\"afterAll\", (fnHandle) => {\n const suite = ensureSuite(state);\n const fnDup = fnHandle.dup();\n suite.afterAll.push(fnDup);\n return context.undefined;\n });\n context.setProp(context.global, \"afterAll\", afterAllFn);\n handles.push(afterAllFn);\n\n // beforeEach - runs before each test in the suite and nested suites\n const beforeEachFn = context.newFunction(\"beforeEach\", (fnHandle) => {\n const suite = ensureSuite(state);\n const fnDup = fnHandle.dup();\n suite.beforeEach.push(fnDup);\n return context.undefined;\n });\n context.setProp(context.global, \"beforeEach\", beforeEachFn);\n handles.push(beforeEachFn);\n\n // afterEach - runs after each test in the suite and nested suites\n const afterEachFn = context.newFunction(\"afterEach\", (fnHandle) => {\n const suite = ensureSuite(state);\n const fnDup = fnHandle.dup();\n suite.afterEach.push(fnDup);\n return context.undefined;\n });\n context.setProp(context.global, \"afterEach\", afterEachFn);\n handles.push(afterEachFn);\n\n return handles;\n}\n"
6
+ ],
7
+ "mappings": ";;AAEA;AAMA,SAAS,WAAW,CAAC,OAAmC;AAAA,EACtD,IAAI,CAAC,MAAM,cAAc;AAAA,IAEvB,MAAM,YAAY,YAAY,UAAU,IAAI;AAAA,IAC5C,MAAM,OAAO,KAAK,SAAS;AAAA,IAC3B,MAAM,eAAe;AAAA,EACvB;AAAA,EACA,OAAO,MAAM;AAAA;AAGR,SAAS,UAAU,CACxB,SACA,OACiB;AAAA,EACjB,MAAM,UAA2B,CAAC;AAAA,EAGlC,MAAM,cAAc,QAAQ,YAAY,aAAa,CAAC,aAAa;AAAA,IACjE,MAAM,QAAQ,YAAY,KAAK;AAAA,IAC/B,MAAM,QAAQ,SAAS,IAAI;AAAA,IAC3B,MAAM,UAAU,KAAK,KAAK;AAAA,IAC1B,OAAO,QAAQ;AAAA,GAChB;AAAA,EACD,QAAQ,QAAQ,QAAQ,QAAQ,aAAa,WAAW;AAAA,EACxD,QAAQ,KAAK,WAAW;AAAA,EAGxB,MAAM,aAAa,QAAQ,YAAY,YAAY,CAAC,aAAa;AAAA,IAC/D,MAAM,QAAQ,YAAY,KAAK;AAAA,IAC/B,MAAM,QAAQ,SAAS,IAAI;AAAA,IAC3B,MAAM,SAAS,KAAK,KAAK;AAAA,IACzB,OAAO,QAAQ;AAAA,GAChB;AAAA,EACD,QAAQ,QAAQ,QAAQ,QAAQ,YAAY,UAAU;AAAA,EACtD,QAAQ,KAAK,UAAU;AAAA,EAGvB,MAAM,eAAe,QAAQ,YAAY,cAAc,CAAC,aAAa;AAAA,IACnE,MAAM,QAAQ,YAAY,KAAK;AAAA,IAC/B,MAAM,QAAQ,SAAS,IAAI;AAAA,IAC3B,MAAM,WAAW,KAAK,KAAK;AAAA,IAC3B,OAAO,QAAQ;AAAA,GAChB;AAAA,EACD,QAAQ,QAAQ,QAAQ,QAAQ,cAAc,YAAY;AAAA,EAC1D,QAAQ,KAAK,YAAY;AAAA,EAGzB,MAAM,cAAc,QAAQ,YAAY,aAAa,CAAC,aAAa;AAAA,IACjE,MAAM,QAAQ,YAAY,KAAK;AAAA,IAC/B,MAAM,QAAQ,SAAS,IAAI;AAAA,IAC3B,MAAM,UAAU,KAAK,KAAK;AAAA,IAC1B,OAAO,QAAQ;AAAA,GAChB;AAAA,EACD,QAAQ,QAAQ,QAAQ,QAAQ,aAAa,WAAW;AAAA,EACxD,QAAQ,KAAK,WAAW;AAAA,EAExB,OAAO;AAAA;",
8
+ "debugId": "337AAC81CA448F0564756E2164756E21",
9
+ "names": []
10
+ }
@@ -1,8 +1,8 @@
1
1
  // @bun
2
2
  // packages/test-environment/src/index.ts
3
- import { setupTestEnvironment } from "./setup.ts";
3
+ import { setupTestEnvironment } from "./setup.mjs";
4
4
  export {
5
5
  setupTestEnvironment
6
6
  };
7
7
 
8
- //# debugId=BFD175E53A6D950064756E2164756E21
8
+ //# debugId=FCD44CE91F43579C64756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.ts"],
4
4
  "sourcesContent": [
5
- "export { setupTestEnvironment } from \"./setup.ts\";\n\nexport type {\n SetupTestOptions,\n TestEnvironmentHandle,\n SuiteInfo,\n SuiteResult,\n TestInfo,\n TestResult,\n TestError,\n RunResults,\n} from \"./types.ts\";\n"
5
+ "export { setupTestEnvironment } from \"./setup.mjs\";\n\nexport type {\n SetupTestOptions,\n TestEnvironmentHandle,\n SuiteInfo,\n SuiteResult,\n TestInfo,\n TestResult,\n TestError,\n RunResults,\n} from \"./types.mjs\";\n"
6
6
  ],
7
7
  "mappings": ";;AAAA;",
8
- "debugId": "BFD175E53A6D950064756E2164756E21",
8
+ "debugId": "FCD44CE91F43579C64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/quickjs-test-environment",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "type": "module"
5
5
  }
@@ -0,0 +1,315 @@
1
+ // @bun
2
+ // packages/test-environment/src/runner.ts
3
+ function checkForOnly(suites) {
4
+ for (const suite of suites) {
5
+ if (suite.modifier === "only")
6
+ return true;
7
+ for (const test of suite.tests) {
8
+ if (test.modifier === "only")
9
+ return true;
10
+ }
11
+ if (checkForOnly(suite.children))
12
+ return true;
13
+ }
14
+ return false;
15
+ }
16
+ async function executeFunction(context, fn) {
17
+ const result = context.callFunction(fn, context.undefined);
18
+ if (result.error) {
19
+ const errorDump = context.dump(result.error);
20
+ result.error.dispose();
21
+ return {
22
+ success: false,
23
+ error: {
24
+ message: errorDump?.message || String(errorDump),
25
+ stack: errorDump?.stack,
26
+ expected: errorDump?.expected,
27
+ actual: errorDump?.actual,
28
+ matcherName: errorDump?.matcherName
29
+ }
30
+ };
31
+ }
32
+ const isPromiseCode = context.evalCode(`
33
+ (function(val) { return val && typeof val.then === 'function'; })
34
+ `);
35
+ if (isPromiseCode.error) {
36
+ isPromiseCode.error.dispose();
37
+ result.value.dispose();
38
+ return { success: true };
39
+ }
40
+ const checkResult = context.callFunction(isPromiseCode.value, context.undefined, result.value);
41
+ isPromiseCode.value.dispose();
42
+ if (checkResult.error) {
43
+ checkResult.error.dispose();
44
+ result.value.dispose();
45
+ return { success: true };
46
+ }
47
+ const isPromise = context.dump(checkResult.value);
48
+ checkResult.value.dispose();
49
+ if (!isPromise) {
50
+ result.value.dispose();
51
+ return { success: true };
52
+ }
53
+ return new Promise((resolve) => {
54
+ const promiseHandle = result.value;
55
+ const thenFn = context.newFunction("then", () => {
56
+ resolve({ success: true });
57
+ return context.undefined;
58
+ });
59
+ const catchFn = context.newFunction("catch", (errorHandle) => {
60
+ const errorDump = context.dump(errorHandle);
61
+ resolve({
62
+ success: false,
63
+ error: {
64
+ message: errorDump?.message || String(errorDump),
65
+ stack: errorDump?.stack,
66
+ expected: errorDump?.expected,
67
+ actual: errorDump?.actual,
68
+ matcherName: errorDump?.matcherName
69
+ }
70
+ });
71
+ return context.undefined;
72
+ });
73
+ const thenProp = context.getProp(promiseHandle, "then");
74
+ const thenResult = context.callFunction(thenProp, promiseHandle, thenFn);
75
+ thenProp.dispose();
76
+ if (thenResult.error) {
77
+ thenResult.error.dispose();
78
+ promiseHandle.dispose();
79
+ thenFn.dispose();
80
+ catchFn.dispose();
81
+ resolve({ success: true });
82
+ return;
83
+ }
84
+ const catchProp = context.getProp(thenResult.value, "catch");
85
+ const catchResult = context.callFunction(catchProp, thenResult.value, catchFn);
86
+ catchProp.dispose();
87
+ thenResult.value.dispose();
88
+ if (catchResult.error) {
89
+ catchResult.error.dispose();
90
+ } else {
91
+ catchResult.value.dispose();
92
+ }
93
+ promiseHandle.dispose();
94
+ thenFn.dispose();
95
+ catchFn.dispose();
96
+ context.runtime.executePendingJobs();
97
+ });
98
+ }
99
+ async function executeHooks(context, hooks) {
100
+ for (const hook of hooks) {
101
+ const result = await executeFunction(context, hook);
102
+ if (!result.success) {
103
+ return result;
104
+ }
105
+ }
106
+ return { success: true };
107
+ }
108
+ function collectBeforeEachHooks(suite) {
109
+ const hooks = [];
110
+ const path = [];
111
+ let current = suite;
112
+ while (current) {
113
+ path.unshift(current);
114
+ current = current.parent;
115
+ }
116
+ for (const s of path) {
117
+ hooks.push(...s.beforeEach);
118
+ }
119
+ return hooks;
120
+ }
121
+ function collectAfterEachHooks(suite) {
122
+ const hooks = [];
123
+ const path = [];
124
+ let current = suite;
125
+ while (current) {
126
+ path.unshift(current);
127
+ current = current.parent;
128
+ }
129
+ for (const s of path.reverse()) {
130
+ hooks.push(...s.afterEach);
131
+ }
132
+ return hooks;
133
+ }
134
+ function hasOnlyAncestor(suite) {
135
+ let current = suite;
136
+ while (current) {
137
+ if (current.modifier === "only")
138
+ return true;
139
+ current = current.parent;
140
+ }
141
+ return false;
142
+ }
143
+ function hasOnlyDescendant(suite) {
144
+ for (const test of suite.tests) {
145
+ if (test.modifier === "only")
146
+ return true;
147
+ }
148
+ for (const child of suite.children) {
149
+ if (child.modifier === "only")
150
+ return true;
151
+ if (hasOnlyDescendant(child))
152
+ return true;
153
+ }
154
+ return false;
155
+ }
156
+ async function runTest(runCtx, test) {
157
+ const { state, context } = runCtx;
158
+ const testInfo = {
159
+ name: test.name,
160
+ suite: test.suite.path,
161
+ fullName: test.suite.path.length > 0 ? [...test.suite.path, test.name].join(" > ") : test.name
162
+ };
163
+ const shouldSkip = test.modifier === "skip" || test.modifier === "todo" || test.suite.modifier === "skip" || test.suite.modifier === "todo" || runCtx.hasOnly && test.modifier !== "only" && !hasOnlyAncestor(test.suite);
164
+ if (shouldSkip) {
165
+ const result2 = {
166
+ ...testInfo,
167
+ status: test.modifier === "todo" ? "todo" : "skip",
168
+ duration: 0
169
+ };
170
+ runCtx.results.tests.push(result2);
171
+ if (result2.status === "skip")
172
+ runCtx.results.skipped++;
173
+ if (result2.status === "todo")
174
+ runCtx.results.todo++;
175
+ return result2;
176
+ }
177
+ state.handlers.onTestStart?.(testInfo);
178
+ const startTime = performance.now();
179
+ const beforeEachHooks = collectBeforeEachHooks(test.suite);
180
+ const beforeResult = await executeHooks(context, beforeEachHooks);
181
+ let testError;
182
+ if (!beforeResult.success) {
183
+ testError = beforeResult.error;
184
+ } else {
185
+ const testResult = await executeFunction(context, test.fn);
186
+ if (!testResult.success) {
187
+ testError = testResult.error;
188
+ }
189
+ }
190
+ const afterEachHooks = collectAfterEachHooks(test.suite);
191
+ await executeHooks(context, afterEachHooks);
192
+ const duration = performance.now() - startTime;
193
+ const result = {
194
+ ...testInfo,
195
+ status: testError ? "fail" : "pass",
196
+ duration,
197
+ error: testError
198
+ };
199
+ runCtx.results.tests.push(result);
200
+ if (result.status === "pass") {
201
+ runCtx.results.passed++;
202
+ state.handlers.onTestPass?.(result);
203
+ } else {
204
+ runCtx.results.failed++;
205
+ state.handlers.onTestFail?.(result);
206
+ }
207
+ return result;
208
+ }
209
+ async function runSuite(runCtx, suite) {
210
+ const { state, context } = runCtx;
211
+ const suiteInfo = {
212
+ name: suite.name,
213
+ path: suite.path,
214
+ fullName: suite.path.join(" > ") || suite.name
215
+ };
216
+ state.handlers.onSuiteStart?.(suiteInfo);
217
+ const startTime = performance.now();
218
+ let passed = 0;
219
+ let failed = 0;
220
+ let skipped = 0;
221
+ const shouldSkipSuite = suite.modifier === "skip" || suite.modifier === "todo" || runCtx.hasOnly && suite.modifier !== "only" && !hasOnlyDescendant(suite) && !hasOnlyAncestor(suite);
222
+ if (!shouldSkipSuite) {
223
+ const beforeAllResult = await executeHooks(context, suite.beforeAll);
224
+ if (beforeAllResult.success) {
225
+ for (const test of suite.tests) {
226
+ const result = await runTest(runCtx, test);
227
+ if (result.status === "pass")
228
+ passed++;
229
+ else if (result.status === "fail")
230
+ failed++;
231
+ else
232
+ skipped++;
233
+ }
234
+ for (const child of suite.children) {
235
+ await runSuite(runCtx, child);
236
+ }
237
+ } else {
238
+ for (const test of suite.tests) {
239
+ const testResult = {
240
+ name: test.name,
241
+ suite: test.suite.path,
242
+ fullName: test.suite.path.length > 0 ? [...test.suite.path, test.name].join(" > ") : test.name,
243
+ status: "fail",
244
+ duration: 0,
245
+ error: beforeAllResult.error
246
+ };
247
+ runCtx.results.tests.push(testResult);
248
+ runCtx.results.failed++;
249
+ failed++;
250
+ state.handlers.onTestFail?.(testResult);
251
+ }
252
+ }
253
+ await executeHooks(context, suite.afterAll);
254
+ } else {
255
+ for (const test of suite.tests) {
256
+ skipped++;
257
+ runCtx.results.skipped++;
258
+ runCtx.results.tests.push({
259
+ name: test.name,
260
+ suite: test.suite.path,
261
+ fullName: test.suite.path.length > 0 ? [...test.suite.path, test.name].join(" > ") : test.name,
262
+ status: "skip",
263
+ duration: 0
264
+ });
265
+ }
266
+ for (const child of suite.children) {
267
+ await runSuite(runCtx, child);
268
+ }
269
+ }
270
+ const duration = performance.now() - startTime;
271
+ const suiteResult = {
272
+ ...suiteInfo,
273
+ passed,
274
+ failed,
275
+ skipped,
276
+ duration
277
+ };
278
+ runCtx.results.suites.push(suiteResult);
279
+ state.handlers.onSuiteEnd?.(suiteResult);
280
+ return suiteResult;
281
+ }
282
+ async function runTests(state) {
283
+ const startTime = performance.now();
284
+ const results = {
285
+ passed: 0,
286
+ failed: 0,
287
+ skipped: 0,
288
+ todo: 0,
289
+ total: 0,
290
+ duration: 0,
291
+ suites: [],
292
+ tests: [],
293
+ success: true
294
+ };
295
+ const hasOnly = checkForOnly(state.suites);
296
+ const runCtx = {
297
+ state,
298
+ context: state.context,
299
+ results,
300
+ hasOnly
301
+ };
302
+ for (const suite of state.suites) {
303
+ await runSuite(runCtx, suite);
304
+ }
305
+ results.duration = performance.now() - startTime;
306
+ results.total = results.passed + results.failed + results.skipped + results.todo;
307
+ results.success = results.failed === 0;
308
+ state.handlers.onRunComplete?.(results);
309
+ return results;
310
+ }
311
+ export {
312
+ runTests
313
+ };
314
+
315
+ //# debugId=846C154A81717D9264756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/runner.ts"],
4
+ "sourcesContent": [
5
+ "import type { QuickJSContext, QuickJSHandle } from \"quickjs-emscripten\";\nimport type {\n TestState,\n RegisteredSuite,\n RegisteredTest,\n RunResults,\n SuiteInfo,\n SuiteResult,\n TestInfo,\n TestResult,\n TestError,\n} from \"./types.mjs\";\n\ninterface RunContext {\n state: TestState;\n context: QuickJSContext;\n results: RunResults;\n hasOnly: boolean;\n}\n\n/**\n * Check if any test or suite has .only modifier\n */\nfunction checkForOnly(suites: RegisteredSuite[]): boolean {\n for (const suite of suites) {\n if (suite.modifier === \"only\") return true;\n for (const test of suite.tests) {\n if (test.modifier === \"only\") return true;\n }\n if (checkForOnly(suite.children)) return true;\n }\n return false;\n}\n\n/**\n * Execute a function handle (sync or async)\n * Captures AssertionError properties from expect() matchers\n */\nasync function executeFunction(\n context: QuickJSContext,\n fn: QuickJSHandle\n): Promise<{ success: boolean; error?: TestError }> {\n const result = context.callFunction(fn, context.undefined);\n\n if (result.error) {\n const errorDump = context.dump(result.error);\n result.error.dispose();\n\n // Extract error properties (including expect() matcher metadata)\n return {\n success: false,\n error: {\n message: errorDump?.message || String(errorDump),\n stack: errorDump?.stack,\n expected: errorDump?.expected,\n actual: errorDump?.actual,\n matcherName: errorDump?.matcherName,\n },\n };\n }\n\n // Check if result is a promise\n const isPromiseCode = context.evalCode(`\n (function(val) { return val && typeof val.then === 'function'; })\n `);\n\n if (isPromiseCode.error) {\n isPromiseCode.error.dispose();\n result.value.dispose();\n return { success: true };\n }\n\n const checkResult = context.callFunction(\n isPromiseCode.value,\n context.undefined,\n result.value\n );\n isPromiseCode.value.dispose();\n\n if (checkResult.error) {\n checkResult.error.dispose();\n result.value.dispose();\n return { success: true };\n }\n\n const isPromise = context.dump(checkResult.value);\n checkResult.value.dispose();\n\n if (!isPromise) {\n result.value.dispose();\n return { success: true };\n }\n\n // Handle async - wrap in promise resolution tracking\n return new Promise((resolve) => {\n const promiseHandle = result.value;\n\n // Create resolve/reject callbacks\n const thenFn = context.newFunction(\"then\", () => {\n resolve({ success: true });\n return context.undefined;\n });\n\n const catchFn = context.newFunction(\"catch\", (errorHandle) => {\n const errorDump = context.dump(errorHandle);\n resolve({\n success: false,\n error: {\n message: errorDump?.message || String(errorDump),\n stack: errorDump?.stack,\n expected: errorDump?.expected,\n actual: errorDump?.actual,\n matcherName: errorDump?.matcherName,\n },\n });\n return context.undefined;\n });\n\n // Get the 'then' property from promise\n const thenProp = context.getProp(promiseHandle, \"then\");\n\n // Call promise.then().catch()\n const thenResult = context.callFunction(thenProp, promiseHandle, thenFn);\n thenProp.dispose();\n\n if (thenResult.error) {\n thenResult.error.dispose();\n promiseHandle.dispose();\n thenFn.dispose();\n catchFn.dispose();\n resolve({ success: true });\n return;\n }\n\n // Get 'catch' property from thenResult\n const catchProp = context.getProp(thenResult.value, \"catch\");\n const catchResult = context.callFunction(\n catchProp,\n thenResult.value,\n catchFn\n );\n catchProp.dispose();\n thenResult.value.dispose();\n\n if (catchResult.error) {\n catchResult.error.dispose();\n } else {\n catchResult.value.dispose();\n }\n\n promiseHandle.dispose();\n thenFn.dispose();\n catchFn.dispose();\n\n // Execute pending jobs to process the promise\n context.runtime.executePendingJobs();\n });\n}\n\n/**\n * Execute all hooks in an array\n */\nasync function executeHooks(\n context: QuickJSContext,\n hooks: QuickJSHandle[]\n): Promise<{ success: boolean; error?: TestError }> {\n for (const hook of hooks) {\n const result = await executeFunction(context, hook);\n if (!result.success) {\n return result;\n }\n }\n return { success: true };\n}\n\n/**\n * Collect beforeEach hooks from outer to inner\n */\nfunction collectBeforeEachHooks(suite: RegisteredSuite): QuickJSHandle[] {\n const hooks: QuickJSHandle[] = [];\n const path: RegisteredSuite[] = [];\n\n let current: RegisteredSuite | null = suite;\n while (current) {\n path.unshift(current);\n current = current.parent;\n }\n\n for (const s of path) {\n hooks.push(...s.beforeEach);\n }\n\n return hooks;\n}\n\n/**\n * Collect afterEach hooks from inner to outer\n */\nfunction collectAfterEachHooks(suite: RegisteredSuite): QuickJSHandle[] {\n const hooks: QuickJSHandle[] = [];\n const path: RegisteredSuite[] = [];\n\n let current: RegisteredSuite | null = suite;\n while (current) {\n path.unshift(current);\n current = current.parent;\n }\n\n // Reverse for inner to outer\n for (const s of path.reverse()) {\n hooks.push(...s.afterEach);\n }\n\n return hooks;\n}\n\n/**\n * Check if any ancestor suite has .only modifier\n */\nfunction hasOnlyAncestor(suite: RegisteredSuite): boolean {\n let current: RegisteredSuite | null = suite;\n while (current) {\n if (current.modifier === \"only\") return true;\n current = current.parent;\n }\n return false;\n}\n\n/**\n * Check if suite has any .only descendants\n */\nfunction hasOnlyDescendant(suite: RegisteredSuite): boolean {\n for (const test of suite.tests) {\n if (test.modifier === \"only\") return true;\n }\n for (const child of suite.children) {\n if (child.modifier === \"only\") return true;\n if (hasOnlyDescendant(child)) return true;\n }\n return false;\n}\n\n/**\n * Run a single test\n */\nasync function runTest(\n runCtx: RunContext,\n test: RegisteredTest\n): Promise<TestResult> {\n const { state, context } = runCtx;\n\n const testInfo: TestInfo = {\n name: test.name,\n suite: test.suite.path,\n fullName:\n test.suite.path.length > 0\n ? [...test.suite.path, test.name].join(\" > \")\n : test.name,\n };\n\n // Check if should skip\n const shouldSkip =\n test.modifier === \"skip\" ||\n test.modifier === \"todo\" ||\n test.suite.modifier === \"skip\" ||\n test.suite.modifier === \"todo\" ||\n (runCtx.hasOnly &&\n test.modifier !== \"only\" &&\n !hasOnlyAncestor(test.suite));\n\n if (shouldSkip) {\n const result: TestResult = {\n ...testInfo,\n status: test.modifier === \"todo\" ? \"todo\" : \"skip\",\n duration: 0,\n };\n\n runCtx.results.tests.push(result);\n if (result.status === \"skip\") runCtx.results.skipped++;\n if (result.status === \"todo\") runCtx.results.todo++;\n\n return result;\n }\n\n // Call onTestStart\n state.handlers.onTestStart?.(testInfo);\n\n const startTime = performance.now();\n\n // Run beforeEach hooks\n const beforeEachHooks = collectBeforeEachHooks(test.suite);\n const beforeResult = await executeHooks(context, beforeEachHooks);\n\n let testError: TestError | undefined;\n\n if (!beforeResult.success) {\n testError = beforeResult.error;\n } else {\n // Run the test\n const testResult = await executeFunction(context, test.fn);\n if (!testResult.success) {\n testError = testResult.error;\n }\n }\n\n // Run afterEach hooks (even if test failed)\n const afterEachHooks = collectAfterEachHooks(test.suite);\n await executeHooks(context, afterEachHooks);\n\n const duration = performance.now() - startTime;\n\n const result: TestResult = {\n ...testInfo,\n status: testError ? \"fail\" : \"pass\",\n duration,\n error: testError,\n };\n\n runCtx.results.tests.push(result);\n\n if (result.status === \"pass\") {\n runCtx.results.passed++;\n state.handlers.onTestPass?.(result);\n } else {\n runCtx.results.failed++;\n state.handlers.onTestFail?.(result);\n }\n\n return result;\n}\n\n/**\n * Run a suite and its children\n */\nasync function runSuite(\n runCtx: RunContext,\n suite: RegisteredSuite\n): Promise<SuiteResult> {\n const { state, context } = runCtx;\n\n const suiteInfo: SuiteInfo = {\n name: suite.name,\n path: suite.path,\n fullName: suite.path.join(\" > \") || suite.name,\n };\n\n // Call onSuiteStart\n state.handlers.onSuiteStart?.(suiteInfo);\n\n const startTime = performance.now();\n let passed = 0;\n let failed = 0;\n let skipped = 0;\n\n // Check if entire suite should skip\n const shouldSkipSuite =\n suite.modifier === \"skip\" ||\n suite.modifier === \"todo\" ||\n (runCtx.hasOnly &&\n suite.modifier !== \"only\" &&\n !hasOnlyDescendant(suite) &&\n !hasOnlyAncestor(suite));\n\n if (!shouldSkipSuite) {\n // Run beforeAll hooks\n const beforeAllResult = await executeHooks(context, suite.beforeAll);\n\n if (beforeAllResult.success) {\n // Run tests\n for (const test of suite.tests) {\n const result = await runTest(runCtx, test);\n if (result.status === \"pass\") passed++;\n else if (result.status === \"fail\") failed++;\n else skipped++;\n }\n\n // Run child suites\n for (const child of suite.children) {\n await runSuite(runCtx, child);\n }\n } else {\n // beforeAll failed - skip all tests in this suite\n for (const test of suite.tests) {\n const testResult: TestResult = {\n name: test.name,\n suite: test.suite.path,\n fullName:\n test.suite.path.length > 0\n ? [...test.suite.path, test.name].join(\" > \")\n : test.name,\n status: \"fail\",\n duration: 0,\n error: beforeAllResult.error,\n };\n runCtx.results.tests.push(testResult);\n runCtx.results.failed++;\n failed++;\n state.handlers.onTestFail?.(testResult);\n }\n }\n\n // Run afterAll hooks\n await executeHooks(context, suite.afterAll);\n } else {\n // Skip all tests\n for (const test of suite.tests) {\n skipped++;\n runCtx.results.skipped++;\n runCtx.results.tests.push({\n name: test.name,\n suite: test.suite.path,\n fullName:\n test.suite.path.length > 0\n ? [...test.suite.path, test.name].join(\" > \")\n : test.name,\n status: \"skip\",\n duration: 0,\n });\n }\n\n // Still process children (they might have .only)\n for (const child of suite.children) {\n await runSuite(runCtx, child);\n }\n }\n\n const duration = performance.now() - startTime;\n\n const suiteResult: SuiteResult = {\n ...suiteInfo,\n passed,\n failed,\n skipped,\n duration,\n };\n\n runCtx.results.suites.push(suiteResult);\n\n // Call onSuiteEnd\n state.handlers.onSuiteEnd?.(suiteResult);\n\n return suiteResult;\n}\n\n/**\n * Main run function\n */\nexport async function runTests(state: TestState): Promise<RunResults> {\n const startTime = performance.now();\n\n const results: RunResults = {\n passed: 0,\n failed: 0,\n skipped: 0,\n todo: 0,\n total: 0,\n duration: 0,\n suites: [],\n tests: [],\n success: true,\n };\n\n const hasOnly = checkForOnly(state.suites);\n\n const runCtx: RunContext = {\n state,\n context: state.context,\n results,\n hasOnly,\n };\n\n // Run all suites\n for (const suite of state.suites) {\n await runSuite(runCtx, suite);\n }\n\n results.duration = performance.now() - startTime;\n results.total = results.passed + results.failed + results.skipped + results.todo;\n results.success = results.failed === 0;\n\n // Call onRunComplete\n state.handlers.onRunComplete?.(results);\n\n return results;\n}\n"
6
+ ],
7
+ "mappings": ";;AAuBA,SAAS,YAAY,CAAC,QAAoC;AAAA,EACxD,WAAW,SAAS,QAAQ;AAAA,IAC1B,IAAI,MAAM,aAAa;AAAA,MAAQ,OAAO;AAAA,IACtC,WAAW,QAAQ,MAAM,OAAO;AAAA,MAC9B,IAAI,KAAK,aAAa;AAAA,QAAQ,OAAO;AAAA,IACvC;AAAA,IACA,IAAI,aAAa,MAAM,QAAQ;AAAA,MAAG,OAAO;AAAA,EAC3C;AAAA,EACA,OAAO;AAAA;AAOT,eAAe,eAAe,CAC5B,SACA,IACkD;AAAA,EAClD,MAAM,SAAS,QAAQ,aAAa,IAAI,QAAQ,SAAS;AAAA,EAEzD,IAAI,OAAO,OAAO;AAAA,IAChB,MAAM,YAAY,QAAQ,KAAK,OAAO,KAAK;AAAA,IAC3C,OAAO,MAAM,QAAQ;AAAA,IAGrB,OAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO;AAAA,QACL,SAAS,WAAW,WAAW,OAAO,SAAS;AAAA,QAC/C,OAAO,WAAW;AAAA,QAClB,UAAU,WAAW;AAAA,QACrB,QAAQ,WAAW;AAAA,QACnB,aAAa,WAAW;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AAAA,EAGA,MAAM,gBAAgB,QAAQ,SAAS;AAAA;AAAA,GAEtC;AAAA,EAED,IAAI,cAAc,OAAO;AAAA,IACvB,cAAc,MAAM,QAAQ;AAAA,IAC5B,OAAO,MAAM,QAAQ;AAAA,IACrB,OAAO,EAAE,SAAS,KAAK;AAAA,EACzB;AAAA,EAEA,MAAM,cAAc,QAAQ,aAC1B,cAAc,OACd,QAAQ,WACR,OAAO,KACT;AAAA,EACA,cAAc,MAAM,QAAQ;AAAA,EAE5B,IAAI,YAAY,OAAO;AAAA,IACrB,YAAY,MAAM,QAAQ;AAAA,IAC1B,OAAO,MAAM,QAAQ;AAAA,IACrB,OAAO,EAAE,SAAS,KAAK;AAAA,EACzB;AAAA,EAEA,MAAM,YAAY,QAAQ,KAAK,YAAY,KAAK;AAAA,EAChD,YAAY,MAAM,QAAQ;AAAA,EAE1B,IAAI,CAAC,WAAW;AAAA,IACd,OAAO,MAAM,QAAQ;AAAA,IACrB,OAAO,EAAE,SAAS,KAAK;AAAA,EACzB;AAAA,EAGA,OAAO,IAAI,QAAQ,CAAC,YAAY;AAAA,IAC9B,MAAM,gBAAgB,OAAO;AAAA,IAG7B,MAAM,SAAS,QAAQ,YAAY,QAAQ,MAAM;AAAA,MAC/C,QAAQ,EAAE,SAAS,KAAK,CAAC;AAAA,MACzB,OAAO,QAAQ;AAAA,KAChB;AAAA,IAED,MAAM,UAAU,QAAQ,YAAY,SAAS,CAAC,gBAAgB;AAAA,MAC5D,MAAM,YAAY,QAAQ,KAAK,WAAW;AAAA,MAC1C,QAAQ;AAAA,QACN,SAAS;AAAA,QACT,OAAO;AAAA,UACL,SAAS,WAAW,WAAW,OAAO,SAAS;AAAA,UAC/C,OAAO,WAAW;AAAA,UAClB,UAAU,WAAW;AAAA,UACrB,QAAQ,WAAW;AAAA,UACnB,aAAa,WAAW;AAAA,QAC1B;AAAA,MACF,CAAC;AAAA,MACD,OAAO,QAAQ;AAAA,KAChB;AAAA,IAGD,MAAM,WAAW,QAAQ,QAAQ,eAAe,MAAM;AAAA,IAGtD,MAAM,aAAa,QAAQ,aAAa,UAAU,eAAe,MAAM;AAAA,IACvE,SAAS,QAAQ;AAAA,IAEjB,IAAI,WAAW,OAAO;AAAA,MACpB,WAAW,MAAM,QAAQ;AAAA,MACzB,cAAc,QAAQ;AAAA,MACtB,OAAO,QAAQ;AAAA,MACf,QAAQ,QAAQ;AAAA,MAChB,QAAQ,EAAE,SAAS,KAAK,CAAC;AAAA,MACzB;AAAA,IACF;AAAA,IAGA,MAAM,YAAY,QAAQ,QAAQ,WAAW,OAAO,OAAO;AAAA,IAC3D,MAAM,cAAc,QAAQ,aAC1B,WACA,WAAW,OACX,OACF;AAAA,IACA,UAAU,QAAQ;AAAA,IAClB,WAAW,MAAM,QAAQ;AAAA,IAEzB,IAAI,YAAY,OAAO;AAAA,MACrB,YAAY,MAAM,QAAQ;AAAA,IAC5B,EAAO;AAAA,MACL,YAAY,MAAM,QAAQ;AAAA;AAAA,IAG5B,cAAc,QAAQ;AAAA,IACtB,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ;AAAA,IAGhB,QAAQ,QAAQ,mBAAmB;AAAA,GACpC;AAAA;AAMH,eAAe,YAAY,CACzB,SACA,OACkD;AAAA,EAClD,WAAW,QAAQ,OAAO;AAAA,IACxB,MAAM,SAAS,MAAM,gBAAgB,SAAS,IAAI;AAAA,IAClD,IAAI,CAAC,OAAO,SAAS;AAAA,MACnB,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,OAAO,EAAE,SAAS,KAAK;AAAA;AAMzB,SAAS,sBAAsB,CAAC,OAAyC;AAAA,EACvE,MAAM,QAAyB,CAAC;AAAA,EAChC,MAAM,OAA0B,CAAC;AAAA,EAEjC,IAAI,UAAkC;AAAA,EACtC,OAAO,SAAS;AAAA,IACd,KAAK,QAAQ,OAAO;AAAA,IACpB,UAAU,QAAQ;AAAA,EACpB;AAAA,EAEA,WAAW,KAAK,MAAM;AAAA,IACpB,MAAM,KAAK,GAAG,EAAE,UAAU;AAAA,EAC5B;AAAA,EAEA,OAAO;AAAA;AAMT,SAAS,qBAAqB,CAAC,OAAyC;AAAA,EACtE,MAAM,QAAyB,CAAC;AAAA,EAChC,MAAM,OAA0B,CAAC;AAAA,EAEjC,IAAI,UAAkC;AAAA,EACtC,OAAO,SAAS;AAAA,IACd,KAAK,QAAQ,OAAO;AAAA,IACpB,UAAU,QAAQ;AAAA,EACpB;AAAA,EAGA,WAAW,KAAK,KAAK,QAAQ,GAAG;AAAA,IAC9B,MAAM,KAAK,GAAG,EAAE,SAAS;AAAA,EAC3B;AAAA,EAEA,OAAO;AAAA;AAMT,SAAS,eAAe,CAAC,OAAiC;AAAA,EACxD,IAAI,UAAkC;AAAA,EACtC,OAAO,SAAS;AAAA,IACd,IAAI,QAAQ,aAAa;AAAA,MAAQ,OAAO;AAAA,IACxC,UAAU,QAAQ;AAAA,EACpB;AAAA,EACA,OAAO;AAAA;AAMT,SAAS,iBAAiB,CAAC,OAAiC;AAAA,EAC1D,WAAW,QAAQ,MAAM,OAAO;AAAA,IAC9B,IAAI,KAAK,aAAa;AAAA,MAAQ,OAAO;AAAA,EACvC;AAAA,EACA,WAAW,SAAS,MAAM,UAAU;AAAA,IAClC,IAAI,MAAM,aAAa;AAAA,MAAQ,OAAO;AAAA,IACtC,IAAI,kBAAkB,KAAK;AAAA,MAAG,OAAO;AAAA,EACvC;AAAA,EACA,OAAO;AAAA;AAMT,eAAe,OAAO,CACpB,QACA,MACqB;AAAA,EACrB,QAAQ,OAAO,YAAY;AAAA,EAE3B,MAAM,WAAqB;AAAA,IACzB,MAAM,KAAK;AAAA,IACX,OAAO,KAAK,MAAM;AAAA,IAClB,UACE,KAAK,MAAM,KAAK,SAAS,IACrB,CAAC,GAAG,KAAK,MAAM,MAAM,KAAK,IAAI,EAAE,KAAK,KAAK,IAC1C,KAAK;AAAA,EACb;AAAA,EAGA,MAAM,aACJ,KAAK,aAAa,UAClB,KAAK,aAAa,UAClB,KAAK,MAAM,aAAa,UACxB,KAAK,MAAM,aAAa,UACvB,OAAO,WACN,KAAK,aAAa,UAClB,CAAC,gBAAgB,KAAK,KAAK;AAAA,EAE/B,IAAI,YAAY;AAAA,IACd,MAAM,UAAqB;AAAA,SACtB;AAAA,MACH,QAAQ,KAAK,aAAa,SAAS,SAAS;AAAA,MAC5C,UAAU;AAAA,IACZ;AAAA,IAEA,OAAO,QAAQ,MAAM,KAAK,OAAM;AAAA,IAChC,IAAI,QAAO,WAAW;AAAA,MAAQ,OAAO,QAAQ;AAAA,IAC7C,IAAI,QAAO,WAAW;AAAA,MAAQ,OAAO,QAAQ;AAAA,IAE7C,OAAO;AAAA,EACT;AAAA,EAGA,MAAM,SAAS,cAAc,QAAQ;AAAA,EAErC,MAAM,YAAY,YAAY,IAAI;AAAA,EAGlC,MAAM,kBAAkB,uBAAuB,KAAK,KAAK;AAAA,EACzD,MAAM,eAAe,MAAM,aAAa,SAAS,eAAe;AAAA,EAEhE,IAAI;AAAA,EAEJ,IAAI,CAAC,aAAa,SAAS;AAAA,IACzB,YAAY,aAAa;AAAA,EAC3B,EAAO;AAAA,IAEL,MAAM,aAAa,MAAM,gBAAgB,SAAS,KAAK,EAAE;AAAA,IACzD,IAAI,CAAC,WAAW,SAAS;AAAA,MACvB,YAAY,WAAW;AAAA,IACzB;AAAA;AAAA,EAIF,MAAM,iBAAiB,sBAAsB,KAAK,KAAK;AAAA,EACvD,MAAM,aAAa,SAAS,cAAc;AAAA,EAE1C,MAAM,WAAW,YAAY,IAAI,IAAI;AAAA,EAErC,MAAM,SAAqB;AAAA,OACtB;AAAA,IACH,QAAQ,YAAY,SAAS;AAAA,IAC7B;AAAA,IACA,OAAO;AAAA,EACT;AAAA,EAEA,OAAO,QAAQ,MAAM,KAAK,MAAM;AAAA,EAEhC,IAAI,OAAO,WAAW,QAAQ;AAAA,IAC5B,OAAO,QAAQ;AAAA,IACf,MAAM,SAAS,aAAa,MAAM;AAAA,EACpC,EAAO;AAAA,IACL,OAAO,QAAQ;AAAA,IACf,MAAM,SAAS,aAAa,MAAM;AAAA;AAAA,EAGpC,OAAO;AAAA;AAMT,eAAe,QAAQ,CACrB,QACA,OACsB;AAAA,EACtB,QAAQ,OAAO,YAAY;AAAA,EAE3B,MAAM,YAAuB;AAAA,IAC3B,MAAM,MAAM;AAAA,IACZ,MAAM,MAAM;AAAA,IACZ,UAAU,MAAM,KAAK,KAAK,KAAK,KAAK,MAAM;AAAA,EAC5C;AAAA,EAGA,MAAM,SAAS,eAAe,SAAS;AAAA,EAEvC,MAAM,YAAY,YAAY,IAAI;AAAA,EAClC,IAAI,SAAS;AAAA,EACb,IAAI,SAAS;AAAA,EACb,IAAI,UAAU;AAAA,EAGd,MAAM,kBACJ,MAAM,aAAa,UACnB,MAAM,aAAa,UAClB,OAAO,WACN,MAAM,aAAa,UACnB,CAAC,kBAAkB,KAAK,KACxB,CAAC,gBAAgB,KAAK;AAAA,EAE1B,IAAI,CAAC,iBAAiB;AAAA,IAEpB,MAAM,kBAAkB,MAAM,aAAa,SAAS,MAAM,SAAS;AAAA,IAEnE,IAAI,gBAAgB,SAAS;AAAA,MAE3B,WAAW,QAAQ,MAAM,OAAO;AAAA,QAC9B,MAAM,SAAS,MAAM,QAAQ,QAAQ,IAAI;AAAA,QACzC,IAAI,OAAO,WAAW;AAAA,UAAQ;AAAA,QACzB,SAAI,OAAO,WAAW;AAAA,UAAQ;AAAA,QAC9B;AAAA;AAAA,MACP;AAAA,MAGA,WAAW,SAAS,MAAM,UAAU;AAAA,QAClC,MAAM,SAAS,QAAQ,KAAK;AAAA,MAC9B;AAAA,IACF,EAAO;AAAA,MAEL,WAAW,QAAQ,MAAM,OAAO;AAAA,QAC9B,MAAM,aAAyB;AAAA,UAC7B,MAAM,KAAK;AAAA,UACX,OAAO,KAAK,MAAM;AAAA,UAClB,UACE,KAAK,MAAM,KAAK,SAAS,IACrB,CAAC,GAAG,KAAK,MAAM,MAAM,KAAK,IAAI,EAAE,KAAK,KAAK,IAC1C,KAAK;AAAA,UACX,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,OAAO,gBAAgB;AAAA,QACzB;AAAA,QACA,OAAO,QAAQ,MAAM,KAAK,UAAU;AAAA,QACpC,OAAO,QAAQ;AAAA,QACf;AAAA,QACA,MAAM,SAAS,aAAa,UAAU;AAAA,MACxC;AAAA;AAAA,IAIF,MAAM,aAAa,SAAS,MAAM,QAAQ;AAAA,EAC5C,EAAO;AAAA,IAEL,WAAW,QAAQ,MAAM,OAAO;AAAA,MAC9B;AAAA,MACA,OAAO,QAAQ;AAAA,MACf,OAAO,QAAQ,MAAM,KAAK;AAAA,QACxB,MAAM,KAAK;AAAA,QACX,OAAO,KAAK,MAAM;AAAA,QAClB,UACE,KAAK,MAAM,KAAK,SAAS,IACrB,CAAC,GAAG,KAAK,MAAM,MAAM,KAAK,IAAI,EAAE,KAAK,KAAK,IAC1C,KAAK;AAAA,QACX,QAAQ;AAAA,QACR,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAAA,IAGA,WAAW,SAAS,MAAM,UAAU;AAAA,MAClC,MAAM,SAAS,QAAQ,KAAK;AAAA,IAC9B;AAAA;AAAA,EAGF,MAAM,WAAW,YAAY,IAAI,IAAI;AAAA,EAErC,MAAM,cAA2B;AAAA,OAC5B;AAAA,IACH;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EAEA,OAAO,QAAQ,OAAO,KAAK,WAAW;AAAA,EAGtC,MAAM,SAAS,aAAa,WAAW;AAAA,EAEvC,OAAO;AAAA;AAMT,eAAsB,QAAQ,CAAC,OAAuC;AAAA,EACpE,MAAM,YAAY,YAAY,IAAI;AAAA,EAElC,MAAM,UAAsB;AAAA,IAC1B,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,UAAU;AAAA,IACV,QAAQ,CAAC;AAAA,IACT,OAAO,CAAC;AAAA,IACR,SAAS;AAAA,EACX;AAAA,EAEA,MAAM,UAAU,aAAa,MAAM,MAAM;AAAA,EAEzC,MAAM,SAAqB;AAAA,IACzB;AAAA,IACA,SAAS,MAAM;AAAA,IACf;AAAA,IACA;AAAA,EACF;AAAA,EAGA,WAAW,SAAS,MAAM,QAAQ;AAAA,IAChC,MAAM,SAAS,QAAQ,KAAK;AAAA,EAC9B;AAAA,EAEA,QAAQ,WAAW,YAAY,IAAI,IAAI;AAAA,EACvC,QAAQ,QAAQ,QAAQ,SAAS,QAAQ,SAAS,QAAQ,UAAU,QAAQ;AAAA,EAC5E,QAAQ,UAAU,QAAQ,WAAW;AAAA,EAGrC,MAAM,SAAS,gBAAgB,OAAO;AAAA,EAEtC,OAAO;AAAA;",
8
+ "debugId": "846C154A81717D9264756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,39 @@
1
+ // @bun
2
+ // packages/test-environment/src/setup.ts
3
+ import { setupCore } from "@ricsam/quickjs-core";
4
+ import { createTestEnvironmentHandle } from "./handle.mjs";
5
+ import { setupDescribe, setupIt } from "./globals/describe.mjs";
6
+ import { setupHooks } from "./globals/hooks.mjs";
7
+ import { setupExpect } from "./globals/expect.mjs";
8
+ function setupTestEnvironment(context, options = {}) {
9
+ const coreHandle = options.coreHandle ?? setupCore(context);
10
+ const stateMap = options.stateMap ?? coreHandle.stateMap;
11
+ const state = {
12
+ context,
13
+ stateMap,
14
+ handlers: {
15
+ onSuiteStart: options.onSuiteStart,
16
+ onSuiteEnd: options.onSuiteEnd,
17
+ onTestStart: options.onTestStart,
18
+ onTestPass: options.onTestPass,
19
+ onTestFail: options.onTestFail,
20
+ onRunComplete: options.onRunComplete
21
+ },
22
+ suites: [],
23
+ currentSuite: null,
24
+ handles: []
25
+ };
26
+ const describeHandles = setupDescribe(context, state);
27
+ const itHandles = setupIt(context, state);
28
+ state.handles.push(...describeHandles, ...itHandles);
29
+ const hookHandles = setupHooks(context, state);
30
+ state.handles.push(...hookHandles);
31
+ const expectHandles = setupExpect(context, state);
32
+ state.handles.push(...expectHandles);
33
+ return createTestEnvironmentHandle(state);
34
+ }
35
+ export {
36
+ setupTestEnvironment
37
+ };
38
+
39
+ //# debugId=F80D6B4A8EEE5DFF64756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/setup.ts"],
4
+ "sourcesContent": [
5
+ "import type { QuickJSContext } from \"quickjs-emscripten\";\nimport { setupCore } from \"@ricsam/quickjs-core\";\nimport type { SetupTestOptions, TestEnvironmentHandle, TestState } from \"./types.mjs\";\nimport { createTestEnvironmentHandle } from \"./handle.mjs\";\nimport { setupDescribe, setupIt } from \"./globals/describe.mjs\";\nimport { setupHooks } from \"./globals/hooks.mjs\";\nimport { setupExpect } from \"./globals/expect.mjs\";\n\nexport function setupTestEnvironment(\n context: QuickJSContext,\n options: SetupTestOptions = {}\n): TestEnvironmentHandle {\n // Setup core if not provided\n const coreHandle = options.coreHandle ?? setupCore(context);\n const stateMap = options.stateMap ?? coreHandle.stateMap;\n\n // Initialize internal state\n const state: TestState = {\n context,\n stateMap,\n handlers: {\n onSuiteStart: options.onSuiteStart,\n onSuiteEnd: options.onSuiteEnd,\n onTestStart: options.onTestStart,\n onTestPass: options.onTestPass,\n onTestFail: options.onTestFail,\n onRunComplete: options.onRunComplete,\n },\n suites: [],\n currentSuite: null,\n handles: [],\n };\n\n // Register describe/it/test globals\n const describeHandles = setupDescribe(context, state);\n const itHandles = setupIt(context, state);\n state.handles.push(...describeHandles, ...itHandles);\n\n // Register lifecycle hook globals\n const hookHandles = setupHooks(context, state);\n state.handles.push(...hookHandles);\n\n // Register expect global\n const expectHandles = setupExpect(context, state);\n state.handles.push(...expectHandles);\n\n return createTestEnvironmentHandle(state);\n}\n"
6
+ ],
7
+ "mappings": ";;AACA;AAEA;AACA;AACA;AACA;AAEO,SAAS,oBAAoB,CAClC,SACA,UAA4B,CAAC,GACN;AAAA,EAEvB,MAAM,aAAa,QAAQ,cAAc,UAAU,OAAO;AAAA,EAC1D,MAAM,WAAW,QAAQ,YAAY,WAAW;AAAA,EAGhD,MAAM,QAAmB;AAAA,IACvB;AAAA,IACA;AAAA,IACA,UAAU;AAAA,MACR,cAAc,QAAQ;AAAA,MACtB,YAAY,QAAQ;AAAA,MACpB,aAAa,QAAQ;AAAA,MACrB,YAAY,QAAQ;AAAA,MACpB,YAAY,QAAQ;AAAA,MACpB,eAAe,QAAQ;AAAA,IACzB;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,cAAc;AAAA,IACd,SAAS,CAAC;AAAA,EACZ;AAAA,EAGA,MAAM,kBAAkB,cAAc,SAAS,KAAK;AAAA,EACpD,MAAM,YAAY,QAAQ,SAAS,KAAK;AAAA,EACxC,MAAM,QAAQ,KAAK,GAAG,iBAAiB,GAAG,SAAS;AAAA,EAGnD,MAAM,cAAc,WAAW,SAAS,KAAK;AAAA,EAC7C,MAAM,QAAQ,KAAK,GAAG,WAAW;AAAA,EAGjC,MAAM,gBAAgB,YAAY,SAAS,KAAK;AAAA,EAChD,MAAM,QAAQ,KAAK,GAAG,aAAa;AAAA,EAEnC,OAAO,4BAA4B,KAAK;AAAA;",
8
+ "debugId": "F80D6B4A8EEE5DFF64756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,3 @@
1
+ // @bun
2
+
3
+ //# debugId=8F67D6BE051B863664756E2164756E21
@@ -0,0 +1,9 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [
5
+ ],
6
+ "mappings": "",
7
+ "debugId": "8F67D6BE051B863664756E2164756E21",
8
+ "names": []
9
+ }
@@ -0,0 +1,5 @@
1
+ import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
2
+ import type { TestState, RegisteredSuite } from "../types.ts";
3
+ export declare function createSuite(name: string, parent: RegisteredSuite | null, modifier?: RegisteredSuite["modifier"]): RegisteredSuite;
4
+ export declare function setupDescribe(context: QuickJSContext, state: TestState): QuickJSHandle[];
5
+ export declare function setupIt(context: QuickJSContext, state: TestState): QuickJSHandle[];
@@ -0,0 +1,3 @@
1
+ import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
2
+ import type { TestState } from "../types.ts";
3
+ export declare function setupExpect(context: QuickJSContext, state: TestState): QuickJSHandle[];
@@ -0,0 +1,3 @@
1
+ import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
2
+ import type { TestState } from "../types.ts";
3
+ export declare function setupHooks(context: QuickJSContext, state: TestState): QuickJSHandle[];
@@ -0,0 +1,5 @@
1
+ import type { TestState, RunResults } from "./types.ts";
2
+ /**
3
+ * Main run function
4
+ */
5
+ export declare function runTests(state: TestState): Promise<RunResults>;
@@ -70,6 +70,8 @@ export interface TestState {
70
70
  };
71
71
  suites: RegisteredSuite[];
72
72
  currentSuite: RegisteredSuite | null;
73
+ /** Handles to dispose when environment is disposed */
74
+ handles: QuickJSHandle[];
73
75
  }
74
76
  export interface RegisteredSuite {
75
77
  id: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/quickjs-test-environment",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Test environment for QuickJS with Bun/Jest/Vitest-compatible primitives",
5
5
  "main": "./dist/cjs/index.cjs",
6
6
  "types": "./dist/types/index.d.ts",
@@ -19,7 +19,7 @@
19
19
  "quickjs-emscripten": ">=0.31.0"
20
20
  },
21
21
  "dependencies": {
22
- "@ricsam/quickjs-core": "^0.2.0"
22
+ "@ricsam/quickjs-core": "^0.2.2"
23
23
  },
24
24
  "author": "Richard Samuelsson",
25
25
  "license": "MIT",