@ricsam/quickjs-test-environment 0.2.16 → 0.2.17
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.
- package/README.md +34 -17
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/runner.cjs +25 -8
- package/dist/cjs/runner.cjs.map +3 -3
- package/dist/cjs/setup.cjs +2 -9
- package/dist/cjs/setup.cjs.map +3 -3
- package/dist/mjs/index.mjs.map +1 -1
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/runner.mjs +25 -8
- package/dist/mjs/runner.mjs.map +3 -3
- package/dist/mjs/setup.mjs +2 -9
- package/dist/mjs/setup.mjs.map +3 -3
- package/dist/types/index.d.ts +1 -1
- package/dist/types/types.d.ts +23 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,25 +1,42 @@
|
|
|
1
1
|
# @ricsam/quickjs-test-environment
|
|
2
2
|
|
|
3
|
-
Test primitives for running tests in sandboxed QuickJS. Provides a Bun/Jest/Vitest-compatible API with
|
|
3
|
+
Test primitives for running tests in sandboxed QuickJS. Provides a Bun/Jest/Vitest-compatible API with event-based result streaming.
|
|
4
|
+
|
|
5
|
+
> **Note**: This is a low-level package. For most use cases, use [`@ricsam/quickjs-runtime`](../runtime) with `createRuntime({ testEnvironment: true })` instead.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bun add @ricsam/quickjs-test-environment
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Setup
|
|
4
14
|
|
|
5
15
|
```typescript
|
|
6
16
|
import { setupTestEnvironment } from "@ricsam/quickjs-test-environment";
|
|
7
17
|
|
|
8
18
|
const handle = setupTestEnvironment(context, {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
19
|
+
onEvent: (event) => {
|
|
20
|
+
if (event.type === "testEnd") {
|
|
21
|
+
const icon = event.test.status === "pass" ? "✓" : "✗";
|
|
22
|
+
console.log(`${icon} ${event.test.fullName}`);
|
|
23
|
+
if (event.test.error) {
|
|
24
|
+
console.log(` Error: ${event.test.error.message}`);
|
|
25
|
+
}
|
|
26
|
+
} else if (event.type === "runEnd") {
|
|
27
|
+
console.log(`\n${event.results.passed}/${event.results.total} tests passed`);
|
|
28
|
+
}
|
|
13
29
|
},
|
|
14
30
|
});
|
|
15
31
|
```
|
|
16
32
|
|
|
17
|
-
|
|
33
|
+
## Injected Globals
|
|
34
|
+
|
|
18
35
|
- `describe`, `it`, `test` (with `.skip`, `.only`, `.todo` modifiers)
|
|
19
36
|
- `beforeAll`, `afterAll`, `beforeEach`, `afterEach`
|
|
20
37
|
- `expect` with matchers (`toBe`, `toEqual`, `toThrow`, etc.) and modifiers (`.not`, `.resolves`, `.rejects`)
|
|
21
38
|
|
|
22
|
-
|
|
39
|
+
## Usage in QuickJS
|
|
23
40
|
|
|
24
41
|
```javascript
|
|
25
42
|
describe("Math operations", () => {
|
|
@@ -44,7 +61,7 @@ describe("Math operations", () => {
|
|
|
44
61
|
});
|
|
45
62
|
```
|
|
46
63
|
|
|
47
|
-
|
|
64
|
+
## Running Tests from Host
|
|
48
65
|
|
|
49
66
|
```typescript
|
|
50
67
|
// Load untrusted test code
|
|
@@ -63,13 +80,13 @@ handle.reset();
|
|
|
63
80
|
handle.dispose();
|
|
64
81
|
```
|
|
65
82
|
|
|
66
|
-
|
|
83
|
+
## TestEvent Types
|
|
67
84
|
|
|
68
|
-
|
|
|
69
|
-
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
85
|
+
| Event Type | Description |
|
|
86
|
+
|------------|-------------|
|
|
87
|
+
| `runStart` | Emitted when test run begins (includes `testCount`, `suiteCount`) |
|
|
88
|
+
| `suiteStart` | Emitted when a describe block begins |
|
|
89
|
+
| `suiteEnd` | Emitted when a describe block completes |
|
|
90
|
+
| `testStart` | Emitted before each test runs |
|
|
91
|
+
| `testEnd` | Emitted when a test completes (includes `status`: pass/fail/skip/todo) |
|
|
92
|
+
| `runEnd` | Emitted after all tests complete (includes full `results`) |
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"export { setupTestEnvironment } from \"./setup.cjs\";\n\nexport type {\n SetupTestOptions,\n TestEnvironmentHandle,\n SuiteInfo,\n SuiteResult,\n TestInfo,\n TestResult,\n TestError,\n RunResults,\n} from \"./types.cjs\";\n"
|
|
5
|
+
"export { setupTestEnvironment } from \"./setup.cjs\";\n\nexport type {\n SetupTestOptions,\n TestEnvironmentHandle,\n TestEvent,\n SuiteInfo,\n SuiteResult,\n TestInfo,\n TestResult,\n TestError,\n RunResults,\n} from \"./types.cjs\";\n"
|
|
6
6
|
],
|
|
7
7
|
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAqC,IAArC;",
|
|
8
8
|
"debugId": "3CED267B02A0F88164756E2164756E21",
|
package/dist/cjs/package.json
CHANGED
package/dist/cjs/runner.cjs
CHANGED
|
@@ -207,7 +207,7 @@ async function runTest(runCtx, test) {
|
|
|
207
207
|
runCtx.results.todo++;
|
|
208
208
|
return result2;
|
|
209
209
|
}
|
|
210
|
-
state.
|
|
210
|
+
state.onEvent?.({ type: "testStart", test: testInfo });
|
|
211
211
|
const startTime = performance.now();
|
|
212
212
|
const beforeEachHooks = collectBeforeEachHooks(test.suite);
|
|
213
213
|
const beforeResult = await executeHooks(context, beforeEachHooks);
|
|
@@ -232,11 +232,10 @@ async function runTest(runCtx, test) {
|
|
|
232
232
|
runCtx.results.tests.push(result);
|
|
233
233
|
if (result.status === "pass") {
|
|
234
234
|
runCtx.results.passed++;
|
|
235
|
-
state.handlers.onTestPass?.(result);
|
|
236
235
|
} else {
|
|
237
236
|
runCtx.results.failed++;
|
|
238
|
-
state.handlers.onTestFail?.(result);
|
|
239
237
|
}
|
|
238
|
+
state.onEvent?.({ type: "testEnd", test: result });
|
|
240
239
|
return result;
|
|
241
240
|
}
|
|
242
241
|
async function runSuite(runCtx, suite) {
|
|
@@ -246,7 +245,7 @@ async function runSuite(runCtx, suite) {
|
|
|
246
245
|
path: suite.path,
|
|
247
246
|
fullName: suite.path.join(" > ") || suite.name
|
|
248
247
|
};
|
|
249
|
-
state.
|
|
248
|
+
state.onEvent?.({ type: "suiteStart", suite: suiteInfo });
|
|
250
249
|
const startTime = performance.now();
|
|
251
250
|
let passed = 0;
|
|
252
251
|
let failed = 0;
|
|
@@ -280,7 +279,7 @@ async function runSuite(runCtx, suite) {
|
|
|
280
279
|
runCtx.results.tests.push(testResult);
|
|
281
280
|
runCtx.results.failed++;
|
|
282
281
|
failed++;
|
|
283
|
-
state.
|
|
282
|
+
state.onEvent?.({ type: "testEnd", test: testResult });
|
|
284
283
|
}
|
|
285
284
|
}
|
|
286
285
|
await executeHooks(context, suite.afterAll);
|
|
@@ -309,9 +308,24 @@ async function runSuite(runCtx, suite) {
|
|
|
309
308
|
duration
|
|
310
309
|
};
|
|
311
310
|
runCtx.results.suites.push(suiteResult);
|
|
312
|
-
state.
|
|
311
|
+
state.onEvent?.({ type: "suiteEnd", suite: suiteResult });
|
|
313
312
|
return suiteResult;
|
|
314
313
|
}
|
|
314
|
+
function countTests(suites) {
|
|
315
|
+
let count = 0;
|
|
316
|
+
for (const suite of suites) {
|
|
317
|
+
count += suite.tests.length;
|
|
318
|
+
count += countTests(suite.children);
|
|
319
|
+
}
|
|
320
|
+
return count;
|
|
321
|
+
}
|
|
322
|
+
function countSuites(suites) {
|
|
323
|
+
let count = suites.length;
|
|
324
|
+
for (const suite of suites) {
|
|
325
|
+
count += countSuites(suite.children);
|
|
326
|
+
}
|
|
327
|
+
return count;
|
|
328
|
+
}
|
|
315
329
|
async function runTests(state) {
|
|
316
330
|
const startTime = performance.now();
|
|
317
331
|
const results = {
|
|
@@ -332,15 +346,18 @@ async function runTests(state) {
|
|
|
332
346
|
results,
|
|
333
347
|
hasOnly
|
|
334
348
|
};
|
|
349
|
+
const testCount = countTests(state.suites);
|
|
350
|
+
const suiteCount = countSuites(state.suites);
|
|
351
|
+
state.onEvent?.({ type: "runStart", testCount, suiteCount });
|
|
335
352
|
for (const suite of state.suites) {
|
|
336
353
|
await runSuite(runCtx, suite);
|
|
337
354
|
}
|
|
338
355
|
results.duration = performance.now() - startTime;
|
|
339
356
|
results.total = results.passed + results.failed + results.skipped + results.todo;
|
|
340
357
|
results.success = results.failed === 0;
|
|
341
|
-
state.
|
|
358
|
+
state.onEvent?.({ type: "runEnd", results });
|
|
342
359
|
return results;
|
|
343
360
|
}
|
|
344
361
|
})
|
|
345
362
|
|
|
346
|
-
//# debugId=
|
|
363
|
+
//# debugId=7C44782526B7BEE164756E2164756E21
|
package/dist/cjs/runner.cjs.map
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/runner.ts"],
|
|
4
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.cjs\";\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"
|
|
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 TestEvent,\n} from \"./types.cjs\";\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 // Emit testStart event\n state.onEvent?.({ type: \"testStart\", test: 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 } else {\n runCtx.results.failed++;\n }\n\n // Emit testEnd event\n state.onEvent?.({ type: \"testEnd\", test: result });\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 // Emit suiteStart event\n state.onEvent?.({ type: \"suiteStart\", suite: 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 // Emit testEnd event for the failed test\n state.onEvent?.({ type: \"testEnd\", test: 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 // Emit suiteEnd event\n state.onEvent?.({ type: \"suiteEnd\", suite: suiteResult });\n\n return suiteResult;\n}\n\n/**\n * Count tests in suites\n */\nfunction countTests(suites: RegisteredSuite[]): number {\n let count = 0;\n for (const suite of suites) {\n count += suite.tests.length;\n count += countTests(suite.children);\n }\n return count;\n}\n\n/**\n * Count suites\n */\nfunction countSuites(suites: RegisteredSuite[]): number {\n let count = suites.length;\n for (const suite of suites) {\n count += countSuites(suite.children);\n }\n return count;\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 // Emit runStart event\n const testCount = countTests(state.suites);\n const suiteCount = countSuites(state.suites);\n state.onEvent?.({ type: \"runStart\", testCount, suiteCount });\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 // Emit runEnd event\n state.onEvent?.({ type: \"runEnd\", results });\n\n return results;\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "7C44782526B7BEE164756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/cjs/setup.cjs
CHANGED
|
@@ -44,14 +44,7 @@ function setupTestEnvironment(context, options = {}) {
|
|
|
44
44
|
const state = {
|
|
45
45
|
context,
|
|
46
46
|
stateMap,
|
|
47
|
-
|
|
48
|
-
onSuiteStart: options.onSuiteStart,
|
|
49
|
-
onSuiteEnd: options.onSuiteEnd,
|
|
50
|
-
onTestStart: options.onTestStart,
|
|
51
|
-
onTestPass: options.onTestPass,
|
|
52
|
-
onTestFail: options.onTestFail,
|
|
53
|
-
onRunComplete: options.onRunComplete
|
|
54
|
-
},
|
|
47
|
+
onEvent: options.onEvent,
|
|
55
48
|
suites: [],
|
|
56
49
|
currentSuite: null,
|
|
57
50
|
handles: []
|
|
@@ -67,4 +60,4 @@ function setupTestEnvironment(context, options = {}) {
|
|
|
67
60
|
}
|
|
68
61
|
})
|
|
69
62
|
|
|
70
|
-
//# debugId=
|
|
63
|
+
//# debugId=581D819CD4BDCD1964756E2164756E21
|
package/dist/cjs/setup.cjs.map
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/setup.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import type { QuickJSContext } from \"quickjs-emscripten\";\nimport { setupCore } from \"@ricsam/quickjs-core\";\nimport type { SetupTestOptions, TestEnvironmentHandle, TestState } from \"./types.cjs\";\nimport { createTestEnvironmentHandle } from \"./handle.cjs\";\nimport { setupDescribe, setupIt } from \"./globals/describe.cjs\";\nimport { setupHooks } from \"./globals/hooks.cjs\";\nimport { setupExpect } from \"./globals/expect.cjs\";\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
|
|
5
|
+
"import type { QuickJSContext } from \"quickjs-emscripten\";\nimport { setupCore } from \"@ricsam/quickjs-core\";\nimport type { SetupTestOptions, TestEnvironmentHandle, TestState } from \"./types.cjs\";\nimport { createTestEnvironmentHandle } from \"./handle.cjs\";\nimport { setupDescribe, setupIt } from \"./globals/describe.cjs\";\nimport { setupHooks } from \"./globals/hooks.cjs\";\nimport { setupExpect } from \"./globals/expect.cjs\";\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 onEvent: options.onEvent,\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
6
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAC0B,IAA1B;AAE4C,IAA5C;AACuC,IAAvC;AAC2B,IAA3B;AAC4B,IAA5B;AAEO,SAAS,oBAAoB,CAClC,SACA,UAA4B,CAAC,GACN;AAAA,EAEvB,MAAM,aAAa,QAAQ,cAAc,8BAAU,OAAO;AAAA,EAC1D,MAAM,WAAW,QAAQ,YAAY,WAAW;AAAA,EAGhD,MAAM,QAAmB;AAAA,IACvB;AAAA,IACA;AAAA,IACA,
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAC0B,IAA1B;AAE4C,IAA5C;AACuC,IAAvC;AAC2B,IAA3B;AAC4B,IAA5B;AAEO,SAAS,oBAAoB,CAClC,SACA,UAA4B,CAAC,GACN;AAAA,EAEvB,MAAM,aAAa,QAAQ,cAAc,8BAAU,OAAO;AAAA,EAC1D,MAAM,WAAW,QAAQ,YAAY,WAAW;AAAA,EAGhD,MAAM,QAAmB;AAAA,IACvB;AAAA,IACA;AAAA,IACA,SAAS,QAAQ;AAAA,IACjB,QAAQ,CAAC;AAAA,IACT,cAAc;AAAA,IACd,SAAS,CAAC;AAAA,EACZ;AAAA,EAGA,MAAM,kBAAkB,8BAAc,SAAS,KAAK;AAAA,EACpD,MAAM,YAAY,wBAAQ,SAAS,KAAK;AAAA,EACxC,MAAM,QAAQ,KAAK,GAAG,iBAAiB,GAAG,SAAS;AAAA,EAGnD,MAAM,cAAc,wBAAW,SAAS,KAAK;AAAA,EAC7C,MAAM,QAAQ,KAAK,GAAG,WAAW;AAAA,EAGjC,MAAM,gBAAgB,0BAAY,SAAS,KAAK;AAAA,EAChD,MAAM,QAAQ,KAAK,GAAG,aAAa;AAAA,EAEnC,OAAO,0CAA4B,KAAK;AAAA;",
|
|
8
|
+
"debugId": "581D819CD4BDCD1964756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/mjs/index.mjs.map
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
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"
|
|
5
|
+
"export { setupTestEnvironment } from \"./setup.mjs\";\n\nexport type {\n SetupTestOptions,\n TestEnvironmentHandle,\n TestEvent,\n SuiteInfo,\n SuiteResult,\n TestInfo,\n TestResult,\n TestError,\n RunResults,\n} from \"./types.mjs\";\n"
|
|
6
6
|
],
|
|
7
7
|
"mappings": ";;AAAA;",
|
|
8
8
|
"debugId": "FCD44CE91F43579C64756E2164756E21",
|
package/dist/mjs/package.json
CHANGED
package/dist/mjs/runner.mjs
CHANGED
|
@@ -174,7 +174,7 @@ async function runTest(runCtx, test) {
|
|
|
174
174
|
runCtx.results.todo++;
|
|
175
175
|
return result2;
|
|
176
176
|
}
|
|
177
|
-
state.
|
|
177
|
+
state.onEvent?.({ type: "testStart", test: testInfo });
|
|
178
178
|
const startTime = performance.now();
|
|
179
179
|
const beforeEachHooks = collectBeforeEachHooks(test.suite);
|
|
180
180
|
const beforeResult = await executeHooks(context, beforeEachHooks);
|
|
@@ -199,11 +199,10 @@ async function runTest(runCtx, test) {
|
|
|
199
199
|
runCtx.results.tests.push(result);
|
|
200
200
|
if (result.status === "pass") {
|
|
201
201
|
runCtx.results.passed++;
|
|
202
|
-
state.handlers.onTestPass?.(result);
|
|
203
202
|
} else {
|
|
204
203
|
runCtx.results.failed++;
|
|
205
|
-
state.handlers.onTestFail?.(result);
|
|
206
204
|
}
|
|
205
|
+
state.onEvent?.({ type: "testEnd", test: result });
|
|
207
206
|
return result;
|
|
208
207
|
}
|
|
209
208
|
async function runSuite(runCtx, suite) {
|
|
@@ -213,7 +212,7 @@ async function runSuite(runCtx, suite) {
|
|
|
213
212
|
path: suite.path,
|
|
214
213
|
fullName: suite.path.join(" > ") || suite.name
|
|
215
214
|
};
|
|
216
|
-
state.
|
|
215
|
+
state.onEvent?.({ type: "suiteStart", suite: suiteInfo });
|
|
217
216
|
const startTime = performance.now();
|
|
218
217
|
let passed = 0;
|
|
219
218
|
let failed = 0;
|
|
@@ -247,7 +246,7 @@ async function runSuite(runCtx, suite) {
|
|
|
247
246
|
runCtx.results.tests.push(testResult);
|
|
248
247
|
runCtx.results.failed++;
|
|
249
248
|
failed++;
|
|
250
|
-
state.
|
|
249
|
+
state.onEvent?.({ type: "testEnd", test: testResult });
|
|
251
250
|
}
|
|
252
251
|
}
|
|
253
252
|
await executeHooks(context, suite.afterAll);
|
|
@@ -276,9 +275,24 @@ async function runSuite(runCtx, suite) {
|
|
|
276
275
|
duration
|
|
277
276
|
};
|
|
278
277
|
runCtx.results.suites.push(suiteResult);
|
|
279
|
-
state.
|
|
278
|
+
state.onEvent?.({ type: "suiteEnd", suite: suiteResult });
|
|
280
279
|
return suiteResult;
|
|
281
280
|
}
|
|
281
|
+
function countTests(suites) {
|
|
282
|
+
let count = 0;
|
|
283
|
+
for (const suite of suites) {
|
|
284
|
+
count += suite.tests.length;
|
|
285
|
+
count += countTests(suite.children);
|
|
286
|
+
}
|
|
287
|
+
return count;
|
|
288
|
+
}
|
|
289
|
+
function countSuites(suites) {
|
|
290
|
+
let count = suites.length;
|
|
291
|
+
for (const suite of suites) {
|
|
292
|
+
count += countSuites(suite.children);
|
|
293
|
+
}
|
|
294
|
+
return count;
|
|
295
|
+
}
|
|
282
296
|
async function runTests(state) {
|
|
283
297
|
const startTime = performance.now();
|
|
284
298
|
const results = {
|
|
@@ -299,17 +313,20 @@ async function runTests(state) {
|
|
|
299
313
|
results,
|
|
300
314
|
hasOnly
|
|
301
315
|
};
|
|
316
|
+
const testCount = countTests(state.suites);
|
|
317
|
+
const suiteCount = countSuites(state.suites);
|
|
318
|
+
state.onEvent?.({ type: "runStart", testCount, suiteCount });
|
|
302
319
|
for (const suite of state.suites) {
|
|
303
320
|
await runSuite(runCtx, suite);
|
|
304
321
|
}
|
|
305
322
|
results.duration = performance.now() - startTime;
|
|
306
323
|
results.total = results.passed + results.failed + results.skipped + results.todo;
|
|
307
324
|
results.success = results.failed === 0;
|
|
308
|
-
state.
|
|
325
|
+
state.onEvent?.({ type: "runEnd", results });
|
|
309
326
|
return results;
|
|
310
327
|
}
|
|
311
328
|
export {
|
|
312
329
|
runTests
|
|
313
330
|
};
|
|
314
331
|
|
|
315
|
-
//# debugId=
|
|
332
|
+
//# debugId=0EFFC4F7FAB8133964756E2164756E21
|
package/dist/mjs/runner.mjs.map
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/runner.ts"],
|
|
4
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"
|
|
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 TestEvent,\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 // Emit testStart event\n state.onEvent?.({ type: \"testStart\", test: 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 } else {\n runCtx.results.failed++;\n }\n\n // Emit testEnd event\n state.onEvent?.({ type: \"testEnd\", test: result });\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 // Emit suiteStart event\n state.onEvent?.({ type: \"suiteStart\", suite: 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 // Emit testEnd event for the failed test\n state.onEvent?.({ type: \"testEnd\", test: 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 // Emit suiteEnd event\n state.onEvent?.({ type: \"suiteEnd\", suite: suiteResult });\n\n return suiteResult;\n}\n\n/**\n * Count tests in suites\n */\nfunction countTests(suites: RegisteredSuite[]): number {\n let count = 0;\n for (const suite of suites) {\n count += suite.tests.length;\n count += countTests(suite.children);\n }\n return count;\n}\n\n/**\n * Count suites\n */\nfunction countSuites(suites: RegisteredSuite[]): number {\n let count = suites.length;\n for (const suite of suites) {\n count += countSuites(suite.children);\n }\n return count;\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 // Emit runStart event\n const testCount = countTests(state.suites);\n const suiteCount = countSuites(state.suites);\n state.onEvent?.({ type: \"runStart\", testCount, suiteCount });\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 // Emit runEnd event\n state.onEvent?.({ type: \"runEnd\", results });\n\n return results;\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";;AAwBA,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,UAAU,EAAE,MAAM,aAAa,MAAM,SAAS,CAAC;AAAA,EAErD,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,EACjB,EAAO;AAAA,IACL,OAAO,QAAQ;AAAA;AAAA,EAIjB,MAAM,UAAU,EAAE,MAAM,WAAW,MAAM,OAAO,CAAC;AAAA,EAEjD,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,UAAU,EAAE,MAAM,cAAc,OAAO,UAAU,CAAC;AAAA,EAExD,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,QAEA,MAAM,UAAU,EAAE,MAAM,WAAW,MAAM,WAAW,CAAC;AAAA,MACvD;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,UAAU,EAAE,MAAM,YAAY,OAAO,YAAY,CAAC;AAAA,EAExD,OAAO;AAAA;AAMT,SAAS,UAAU,CAAC,QAAmC;AAAA,EACrD,IAAI,QAAQ;AAAA,EACZ,WAAW,SAAS,QAAQ;AAAA,IAC1B,SAAS,MAAM,MAAM;AAAA,IACrB,SAAS,WAAW,MAAM,QAAQ;AAAA,EACpC;AAAA,EACA,OAAO;AAAA;AAMT,SAAS,WAAW,CAAC,QAAmC;AAAA,EACtD,IAAI,QAAQ,OAAO;AAAA,EACnB,WAAW,SAAS,QAAQ;AAAA,IAC1B,SAAS,YAAY,MAAM,QAAQ;AAAA,EACrC;AAAA,EACA,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,MAAM,YAAY,WAAW,MAAM,MAAM;AAAA,EACzC,MAAM,aAAa,YAAY,MAAM,MAAM;AAAA,EAC3C,MAAM,UAAU,EAAE,MAAM,YAAY,WAAW,WAAW,CAAC;AAAA,EAG3D,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,UAAU,EAAE,MAAM,UAAU,QAAQ,CAAC;AAAA,EAE3C,OAAO;AAAA;",
|
|
8
|
+
"debugId": "0EFFC4F7FAB8133964756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/mjs/setup.mjs
CHANGED
|
@@ -11,14 +11,7 @@ function setupTestEnvironment(context, options = {}) {
|
|
|
11
11
|
const state = {
|
|
12
12
|
context,
|
|
13
13
|
stateMap,
|
|
14
|
-
|
|
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
|
-
},
|
|
14
|
+
onEvent: options.onEvent,
|
|
22
15
|
suites: [],
|
|
23
16
|
currentSuite: null,
|
|
24
17
|
handles: []
|
|
@@ -36,4 +29,4 @@ export {
|
|
|
36
29
|
setupTestEnvironment
|
|
37
30
|
};
|
|
38
31
|
|
|
39
|
-
//# debugId=
|
|
32
|
+
//# debugId=404E566BCE6A227164756E2164756E21
|
package/dist/mjs/setup.mjs.map
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/setup.ts"],
|
|
4
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
|
|
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 onEvent: options.onEvent,\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
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,
|
|
8
|
-
"debugId": "
|
|
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,SAAS,QAAQ;AAAA,IACjB,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": "404E566BCE6A227164756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { setupTestEnvironment } from "./setup.ts";
|
|
2
|
-
export type { SetupTestOptions, TestEnvironmentHandle, SuiteInfo, SuiteResult, TestInfo, TestResult, TestError, RunResults, } from "./types.ts";
|
|
2
|
+
export type { SetupTestOptions, TestEnvironmentHandle, TestEvent, SuiteInfo, SuiteResult, TestInfo, TestResult, TestError, RunResults, } from "./types.ts";
|
package/dist/types/types.d.ts
CHANGED
|
@@ -1,12 +1,28 @@
|
|
|
1
1
|
import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
|
|
2
2
|
import type { StateMap, CoreHandle } from "@ricsam/quickjs-core";
|
|
3
|
+
export type TestEvent = {
|
|
4
|
+
type: "runStart";
|
|
5
|
+
testCount: number;
|
|
6
|
+
suiteCount: number;
|
|
7
|
+
} | {
|
|
8
|
+
type: "suiteStart";
|
|
9
|
+
suite: SuiteInfo;
|
|
10
|
+
} | {
|
|
11
|
+
type: "suiteEnd";
|
|
12
|
+
suite: SuiteResult;
|
|
13
|
+
} | {
|
|
14
|
+
type: "testStart";
|
|
15
|
+
test: TestInfo;
|
|
16
|
+
} | {
|
|
17
|
+
type: "testEnd";
|
|
18
|
+
test: TestResult;
|
|
19
|
+
} | {
|
|
20
|
+
type: "runEnd";
|
|
21
|
+
results: RunResults;
|
|
22
|
+
};
|
|
3
23
|
export interface SetupTestOptions {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
onTestStart?: (test: TestInfo) => void;
|
|
7
|
-
onTestPass?: (test: TestResult) => void;
|
|
8
|
-
onTestFail?: (test: TestResult) => void;
|
|
9
|
-
onRunComplete?: (results: RunResults) => void;
|
|
24
|
+
onEvent?: (event: TestEvent) => void;
|
|
25
|
+
testTimeout?: number;
|
|
10
26
|
stateMap?: StateMap;
|
|
11
27
|
coreHandle?: CoreHandle;
|
|
12
28
|
}
|
|
@@ -60,14 +76,7 @@ export interface RunResults {
|
|
|
60
76
|
export interface TestState {
|
|
61
77
|
context: QuickJSContext;
|
|
62
78
|
stateMap: StateMap;
|
|
63
|
-
|
|
64
|
-
onSuiteStart?: (suite: SuiteInfo) => void;
|
|
65
|
-
onSuiteEnd?: (suite: SuiteResult) => void;
|
|
66
|
-
onTestStart?: (test: TestInfo) => void;
|
|
67
|
-
onTestPass?: (test: TestResult) => void;
|
|
68
|
-
onTestFail?: (test: TestResult) => void;
|
|
69
|
-
onRunComplete?: (results: RunResults) => void;
|
|
70
|
-
};
|
|
79
|
+
onEvent?: (event: TestEvent) => void;
|
|
71
80
|
suites: RegisteredSuite[];
|
|
72
81
|
currentSuite: RegisteredSuite | null;
|
|
73
82
|
/** Handles to dispose when environment is disposed */
|
package/package.json
CHANGED