@step-forge/step-forge 0.0.21 → 0.0.22
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/RUNTIME.md +42 -12
- package/dist/{analyzer-DYfdoSIS.js → analyzer-byS8yRrY.js} +52 -24
- package/dist/analyzer-byS8yRrY.js.map +1 -0
- package/dist/analyzer-cli.js +1 -1
- package/dist/analyzer.d.ts +2 -0
- package/dist/analyzer.js +1 -1
- package/dist/cli.cjs +129 -74
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +129 -74
- package/dist/cli.js.map +1 -1
- package/dist/{hooks-Dar49TtT.d.ts → config-C7PCYgYy.d.cts} +65 -16
- package/dist/{hooks-Dar49TtT.d.cts → config-C7PCYgYy.d.ts} +65 -16
- package/dist/{engine-DPRxs6eC.js → engine-DPVLEHBi.js} +8 -26
- package/dist/engine-DPVLEHBi.js.map +1 -0
- package/dist/{engine-DWAIlwWp.cjs → engine-vqA-eL_T.cjs} +8 -26
- package/dist/engine-vqA-eL_T.cjs.map +1 -0
- package/dist/{gherkinParser-CHpkEwii.cjs → gherkinParser-BT40q_i3.cjs} +97 -2
- package/dist/gherkinParser-BT40q_i3.cjs.map +1 -0
- package/dist/{gherkinParser-CKARHgt4.js → gherkinParser-NcttZgN4.js} +74 -3
- package/dist/gherkinParser-NcttZgN4.js.map +1 -0
- package/dist/hooks-BDCMKeNq.js +71 -0
- package/dist/{hooks-CywugMQQ.js.map → hooks-BDCMKeNq.js.map} +1 -1
- package/dist/{hooks-CGYzwDOv.cjs → hooks-Be0cjULN.cjs} +20 -31
- package/dist/{hooks-CGYzwDOv.cjs.map → hooks-Be0cjULN.cjs.map} +1 -1
- package/dist/runtime.cjs +3 -3
- package/dist/runtime.d.cts +11 -48
- package/dist/runtime.d.ts +11 -48
- package/dist/runtime.js +3 -3
- package/dist/step-forge.cjs +65 -28
- package/dist/step-forge.cjs.map +1 -1
- package/dist/step-forge.d.cts +13 -4
- package/dist/step-forge.d.ts +13 -4
- package/dist/step-forge.js +65 -28
- package/dist/step-forge.js.map +1 -1
- package/package.json +2 -2
- package/dist/analyzer-DYfdoSIS.js.map +0 -1
- package/dist/engine-DPRxs6eC.js.map +0 -1
- package/dist/engine-DWAIlwWp.cjs.map +0 -1
- package/dist/gherkinParser-CHpkEwii.cjs.map +0 -1
- package/dist/gherkinParser-CKARHgt4.js.map +0 -1
- package/dist/hooks-CywugMQQ.js +0 -82
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
//#region src/runtime/registry.ts
|
|
2
|
+
/**
|
|
3
|
+
* A collection of registered steps. Deliberately a plain instance (not a hidden
|
|
4
|
+
* module global) so tests and the Vitest plugin can create isolated registries.
|
|
5
|
+
* `globalRegistry` is the default sink that `.step()` writes to.
|
|
6
|
+
*/
|
|
7
|
+
var StepRegistry = class {
|
|
8
|
+
steps = [];
|
|
9
|
+
add(step) {
|
|
10
|
+
this.steps.push(step);
|
|
11
|
+
}
|
|
12
|
+
all() {
|
|
13
|
+
return this.steps;
|
|
14
|
+
}
|
|
15
|
+
clear() {
|
|
16
|
+
this.steps = [];
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const globalRegistry = new StepRegistry();
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/runtime/hooks.ts
|
|
22
|
+
/**
|
|
23
|
+
* Collection of registered hooks. Like {@link StepRegistry}, a plain instance
|
|
24
|
+
* (not a hidden global) so tests can isolate; `globalHookRegistry` is the
|
|
25
|
+
* default sink the public `beforeScenario`/`afterAll`/etc helpers write to.
|
|
26
|
+
*/
|
|
27
|
+
var HookRegistry = class {
|
|
28
|
+
hooks = [];
|
|
29
|
+
add(hook) {
|
|
30
|
+
this.hooks.push(hook);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Hooks for a scope+timing. `before` hooks run in registration order;
|
|
34
|
+
* `after` hooks run in reverse (LIFO), so teardown unwinds setup.
|
|
35
|
+
*/
|
|
36
|
+
for(scope, timing) {
|
|
37
|
+
const matching = this.hooks.filter((h) => h.scope === scope && h.timing === timing);
|
|
38
|
+
return timing === "after" ? matching.reverse() : matching;
|
|
39
|
+
}
|
|
40
|
+
clear() {
|
|
41
|
+
this.hooks = [];
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const globalHookRegistry = new HookRegistry();
|
|
45
|
+
/**
|
|
46
|
+
* Run every registered hook of a scope+timing **in registration order** (after
|
|
47
|
+
* hooks reversed by {@link HookRegistry.for}, so teardown unwinds setup). Used
|
|
48
|
+
* for feature hooks, where ordering matters. Throws if a hook throws, so the
|
|
49
|
+
* runner reports it against the enclosing boundary.
|
|
50
|
+
*/
|
|
51
|
+
async function runHooks(scope, timing, registry = globalHookRegistry) {
|
|
52
|
+
for (const hook of registry.for(scope, timing)) await hook.fn();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Run every registered hook of a scope+timing **concurrently**, resolving once
|
|
56
|
+
* all of them settle. This is how global `beforeAll`/`afterAll` run: independent
|
|
57
|
+
* setup/teardown steps fire in parallel with no ordering between them. A hook
|
|
58
|
+
* with a sequential requirement should sequence that work inside a single hook.
|
|
59
|
+
*
|
|
60
|
+
* The runner calls this exactly once for `beforeAll` (before any scenario
|
|
61
|
+
* starts) and once for `afterAll` (after every scenario is done), so global
|
|
62
|
+
* setup/teardown brackets the whole run deterministically. Rejects if any hook
|
|
63
|
+
* rejects (via `Promise.all`), surfacing the first failure to the caller.
|
|
64
|
+
*/
|
|
65
|
+
async function runHooksParallel(scope, timing, registry = globalHookRegistry) {
|
|
66
|
+
await Promise.all(registry.for(scope, timing).map((hook) => hook.fn()));
|
|
67
|
+
}
|
|
68
|
+
//#endregion
|
|
69
|
+
export { StepRegistry as a, runHooksParallel as i, globalHookRegistry as n, globalRegistry as o, runHooks as r, HookRegistry as t };
|
|
70
|
+
|
|
71
|
+
//# sourceMappingURL=hooks-BDCMKeNq.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks-
|
|
1
|
+
{"version":3,"file":"hooks-BDCMKeNq.js","names":[],"sources":["../../src/runtime/registry.ts","../../src/runtime/hooks.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { Parser } from \"../parsers\";\nimport { MergeableWorld } from \"../world\";\n\nexport type StepType = \"given\" | \"when\" | \"then\";\n\n/**\n * A step definition as it exists at *runtime*, independent of any test runner.\n *\n * `execute` is the fully-wired step body: given a world and the raw values\n * captured from a Gherkin step, it applies parsers, validates + narrows\n * dependencies, runs the user's step function, and merges the result back into\n * the world. The engine never needs to know how any of that works — it just\n * matches text to a step and calls `execute`.\n */\nexport interface RegisteredStep {\n stepType: StepType;\n /** The Cucumber-expression source, e.g. `a user named {string}`. */\n expression: string;\n /** Parsers, one per captured variable, applied after expression capture. */\n parsers: Parser<any>[];\n execute: (\n world: MergeableWorld<any, any, any>,\n capturedArgs: unknown[]\n ) => Promise<void>;\n /** Where the step was defined, for ambiguous-match diagnostics. */\n source?: string;\n}\n\n/**\n * A collection of registered steps. Deliberately a plain instance (not a hidden\n * module global) so tests and the Vitest plugin can create isolated registries.\n * `globalRegistry` is the default sink that `.step()` writes to.\n */\nexport class StepRegistry {\n private steps: RegisteredStep[] = [];\n\n add(step: RegisteredStep): void {\n this.steps.push(step);\n }\n\n all(): readonly RegisteredStep[] {\n return this.steps;\n }\n\n clear(): void {\n this.steps = [];\n }\n}\n\nexport const globalRegistry = new StepRegistry();\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { MergeableWorld } from \"../world\";\n\n/**\n * Hooks are side-effect callbacks that run around scenarios, feature files, or\n * the whole run. Unlike steps they never seed state: state is owned end to end\n * by the typed dependency graph (given/when/then), and hooks exist for setup and\n * teardown (opening a DB, starting a server, resetting mocks). A scenario hook\n * may *read* the world, but its return value is ignored.\n */\nexport type HookScope = \"scenario\" | \"feature\" | \"global\";\nexport type HookTiming = \"before\" | \"after\";\n\n/** Lightweight scenario identity handed to per-scenario hooks. */\nexport interface ScenarioInfo {\n name: string;\n file: string;\n}\n\n/** A per-scenario hook: gets the scenario's world (read-only in spirit). */\nexport type ScenarioHookFn = (context: {\n world: MergeableWorld<any, any, any>;\n scenario: ScenarioInfo;\n}) => void | Promise<void>;\n\n/** A per-feature-file or global hook: no world exists at these boundaries. */\nexport type PlainHookFn = () => void | Promise<void>;\n\nexport interface RegisteredHook {\n scope: HookScope;\n timing: HookTiming;\n fn: ScenarioHookFn | PlainHookFn;\n}\n\n/**\n * Collection of registered hooks. Like {@link StepRegistry}, a plain instance\n * (not a hidden global) so tests can isolate; `globalHookRegistry` is the\n * default sink the public `beforeScenario`/`afterAll`/etc helpers write to.\n */\nexport class HookRegistry {\n private hooks: RegisteredHook[] = [];\n\n add(hook: RegisteredHook): void {\n this.hooks.push(hook);\n }\n\n /**\n * Hooks for a scope+timing. `before` hooks run in registration order;\n * `after` hooks run in reverse (LIFO), so teardown unwinds setup.\n */\n for(scope: HookScope, timing: HookTiming): RegisteredHook[] {\n const matching = this.hooks.filter(\n h => h.scope === scope && h.timing === timing\n );\n return timing === \"after\" ? matching.reverse() : matching;\n }\n\n clear(): void {\n this.hooks = [];\n }\n}\n\nexport const globalHookRegistry = new HookRegistry();\n\n/**\n * Run every registered hook of a scope+timing **in registration order** (after\n * hooks reversed by {@link HookRegistry.for}, so teardown unwinds setup). Used\n * for feature hooks, where ordering matters. Throws if a hook throws, so the\n * runner reports it against the enclosing boundary.\n */\nexport async function runHooks(\n scope: \"feature\" | \"global\",\n timing: HookTiming,\n registry: HookRegistry = globalHookRegistry\n): Promise<void> {\n for (const hook of registry.for(scope, timing)) {\n await (hook.fn as PlainHookFn)();\n }\n}\n\n/**\n * Run every registered hook of a scope+timing **concurrently**, resolving once\n * all of them settle. This is how global `beforeAll`/`afterAll` run: independent\n * setup/teardown steps fire in parallel with no ordering between them. A hook\n * with a sequential requirement should sequence that work inside a single hook.\n *\n * The runner calls this exactly once for `beforeAll` (before any scenario\n * starts) and once for `afterAll` (after every scenario is done), so global\n * setup/teardown brackets the whole run deterministically. Rejects if any hook\n * rejects (via `Promise.all`), surfacing the first failure to the caller.\n */\nexport async function runHooksParallel(\n scope: \"feature\" | \"global\",\n timing: HookTiming,\n registry: HookRegistry = globalHookRegistry\n): Promise<void> {\n await Promise.all(\n registry.for(scope, timing).map(hook => (hook.fn as PlainHookFn)())\n );\n}\n"],"mappings":";;;;;;AAkCA,IAAa,eAAb,MAA0B;CACxB,QAAkC,CAAC;CAEnC,IAAI,MAA4B;EAC9B,KAAK,MAAM,KAAK,IAAI;CACtB;CAEA,MAAiC;EAC/B,OAAO,KAAK;CACd;CAEA,QAAc;EACZ,KAAK,QAAQ,CAAC;CAChB;AACF;AAEA,MAAa,iBAAiB,IAAI,aAAa;;;;;;;;ACX/C,IAAa,eAAb,MAA0B;CACxB,QAAkC,CAAC;CAEnC,IAAI,MAA4B;EAC9B,KAAK,MAAM,KAAK,IAAI;CACtB;;;;;CAMA,IAAI,OAAkB,QAAsC;EAC1D,MAAM,WAAW,KAAK,MAAM,QAC1B,MAAK,EAAE,UAAU,SAAS,EAAE,WAAW,MACzC;EACA,OAAO,WAAW,UAAU,SAAS,QAAQ,IAAI;CACnD;CAEA,QAAc;EACZ,KAAK,QAAQ,CAAC;CAChB;AACF;AAEA,MAAa,qBAAqB,IAAI,aAAa;;;;;;;AAQnD,eAAsB,SACpB,OACA,QACA,WAAyB,oBACV;CACf,KAAK,MAAM,QAAQ,SAAS,IAAI,OAAO,MAAM,GAC3C,MAAO,KAAK,GAAmB;AAEnC;;;;;;;;;;;;AAaA,eAAsB,iBACpB,OACA,QACA,WAAyB,oBACV;CACf,MAAM,QAAQ,IACZ,SAAS,IAAI,OAAO,MAAM,CAAC,CAAC,KAAI,SAAS,KAAK,GAAmB,CAAC,CACpE;AACF"}
|
|
@@ -43,38 +43,27 @@ var HookRegistry = class {
|
|
|
43
43
|
};
|
|
44
44
|
const globalHookRegistry = new HookRegistry();
|
|
45
45
|
/**
|
|
46
|
-
* Run every registered
|
|
47
|
-
*
|
|
46
|
+
* Run every registered hook of a scope+timing **in registration order** (after
|
|
47
|
+
* hooks reversed by {@link HookRegistry.for}, so teardown unwinds setup). Used
|
|
48
|
+
* for feature hooks, where ordering matters. Throws if a hook throws, so the
|
|
48
49
|
* runner reports it against the enclosing boundary.
|
|
49
50
|
*/
|
|
50
51
|
async function runHooks(scope, timing, registry = globalHookRegistry) {
|
|
51
52
|
for (const hook of registry.for(scope, timing)) await hook.fn();
|
|
52
53
|
}
|
|
53
|
-
const GLOBAL_GUARD = Symbol.for("step-forge.globalHooksStarted");
|
|
54
54
|
/**
|
|
55
|
-
* Run
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
55
|
+
* Run every registered hook of a scope+timing **concurrently**, resolving once
|
|
56
|
+
* all of them settle. This is how global `beforeAll`/`afterAll` run: independent
|
|
57
|
+
* setup/teardown steps fire in parallel with no ordering between them. A hook
|
|
58
|
+
* with a sequential requirement should sequence that work inside a single hook.
|
|
59
59
|
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* in each. Size global setup to be worker-safe (e.g. a server per worker).
|
|
65
|
-
* - Teardown is best-effort: after-hooks start on the worker's `beforeExit` and
|
|
66
|
-
* are not awaited by the runner, so keep them fast/synchronous.
|
|
60
|
+
* The runner calls this exactly once for `beforeAll` (before any scenario
|
|
61
|
+
* starts) and once for `afterAll` (after every scenario is done), so global
|
|
62
|
+
* setup/teardown brackets the whole run deterministically. Rejects if any hook
|
|
63
|
+
* rejects (via `Promise.all`), surfacing the first failure to the caller.
|
|
67
64
|
*/
|
|
68
|
-
async function
|
|
69
|
-
|
|
70
|
-
if (store[GLOBAL_GUARD]) return;
|
|
71
|
-
store[GLOBAL_GUARD] = true;
|
|
72
|
-
for (const hook of registry.for("global", "before")) await hook.fn();
|
|
73
|
-
process.once("beforeExit", () => {
|
|
74
|
-
(async () => {
|
|
75
|
-
for (const hook of registry.for("global", "after")) await hook.fn();
|
|
76
|
-
})();
|
|
77
|
-
});
|
|
65
|
+
async function runHooksParallel(scope, timing, registry = globalHookRegistry) {
|
|
66
|
+
await Promise.all(registry.for(scope, timing).map((hook) => hook.fn()));
|
|
78
67
|
}
|
|
79
68
|
//#endregion
|
|
80
69
|
Object.defineProperty(exports, "HookRegistry", {
|
|
@@ -89,12 +78,6 @@ Object.defineProperty(exports, "StepRegistry", {
|
|
|
89
78
|
return StepRegistry;
|
|
90
79
|
}
|
|
91
80
|
});
|
|
92
|
-
Object.defineProperty(exports, "ensureGlobalHooks", {
|
|
93
|
-
enumerable: true,
|
|
94
|
-
get: function() {
|
|
95
|
-
return ensureGlobalHooks;
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
81
|
Object.defineProperty(exports, "globalHookRegistry", {
|
|
99
82
|
enumerable: true,
|
|
100
83
|
get: function() {
|
|
@@ -113,5 +96,11 @@ Object.defineProperty(exports, "runHooks", {
|
|
|
113
96
|
return runHooks;
|
|
114
97
|
}
|
|
115
98
|
});
|
|
99
|
+
Object.defineProperty(exports, "runHooksParallel", {
|
|
100
|
+
enumerable: true,
|
|
101
|
+
get: function() {
|
|
102
|
+
return runHooksParallel;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
116
105
|
|
|
117
|
-
//# sourceMappingURL=hooks-
|
|
106
|
+
//# sourceMappingURL=hooks-Be0cjULN.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks-
|
|
1
|
+
{"version":3,"file":"hooks-Be0cjULN.cjs","names":[],"sources":["../../src/runtime/registry.ts","../../src/runtime/hooks.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { Parser } from \"../parsers\";\nimport { MergeableWorld } from \"../world\";\n\nexport type StepType = \"given\" | \"when\" | \"then\";\n\n/**\n * A step definition as it exists at *runtime*, independent of any test runner.\n *\n * `execute` is the fully-wired step body: given a world and the raw values\n * captured from a Gherkin step, it applies parsers, validates + narrows\n * dependencies, runs the user's step function, and merges the result back into\n * the world. The engine never needs to know how any of that works — it just\n * matches text to a step and calls `execute`.\n */\nexport interface RegisteredStep {\n stepType: StepType;\n /** The Cucumber-expression source, e.g. `a user named {string}`. */\n expression: string;\n /** Parsers, one per captured variable, applied after expression capture. */\n parsers: Parser<any>[];\n execute: (\n world: MergeableWorld<any, any, any>,\n capturedArgs: unknown[]\n ) => Promise<void>;\n /** Where the step was defined, for ambiguous-match diagnostics. */\n source?: string;\n}\n\n/**\n * A collection of registered steps. Deliberately a plain instance (not a hidden\n * module global) so tests and the Vitest plugin can create isolated registries.\n * `globalRegistry` is the default sink that `.step()` writes to.\n */\nexport class StepRegistry {\n private steps: RegisteredStep[] = [];\n\n add(step: RegisteredStep): void {\n this.steps.push(step);\n }\n\n all(): readonly RegisteredStep[] {\n return this.steps;\n }\n\n clear(): void {\n this.steps = [];\n }\n}\n\nexport const globalRegistry = new StepRegistry();\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { MergeableWorld } from \"../world\";\n\n/**\n * Hooks are side-effect callbacks that run around scenarios, feature files, or\n * the whole run. Unlike steps they never seed state: state is owned end to end\n * by the typed dependency graph (given/when/then), and hooks exist for setup and\n * teardown (opening a DB, starting a server, resetting mocks). A scenario hook\n * may *read* the world, but its return value is ignored.\n */\nexport type HookScope = \"scenario\" | \"feature\" | \"global\";\nexport type HookTiming = \"before\" | \"after\";\n\n/** Lightweight scenario identity handed to per-scenario hooks. */\nexport interface ScenarioInfo {\n name: string;\n file: string;\n}\n\n/** A per-scenario hook: gets the scenario's world (read-only in spirit). */\nexport type ScenarioHookFn = (context: {\n world: MergeableWorld<any, any, any>;\n scenario: ScenarioInfo;\n}) => void | Promise<void>;\n\n/** A per-feature-file or global hook: no world exists at these boundaries. */\nexport type PlainHookFn = () => void | Promise<void>;\n\nexport interface RegisteredHook {\n scope: HookScope;\n timing: HookTiming;\n fn: ScenarioHookFn | PlainHookFn;\n}\n\n/**\n * Collection of registered hooks. Like {@link StepRegistry}, a plain instance\n * (not a hidden global) so tests can isolate; `globalHookRegistry` is the\n * default sink the public `beforeScenario`/`afterAll`/etc helpers write to.\n */\nexport class HookRegistry {\n private hooks: RegisteredHook[] = [];\n\n add(hook: RegisteredHook): void {\n this.hooks.push(hook);\n }\n\n /**\n * Hooks for a scope+timing. `before` hooks run in registration order;\n * `after` hooks run in reverse (LIFO), so teardown unwinds setup.\n */\n for(scope: HookScope, timing: HookTiming): RegisteredHook[] {\n const matching = this.hooks.filter(\n h => h.scope === scope && h.timing === timing\n );\n return timing === \"after\" ? matching.reverse() : matching;\n }\n\n clear(): void {\n this.hooks = [];\n }\n}\n\nexport const globalHookRegistry = new HookRegistry();\n\n/**\n * Run every registered hook of a scope+timing **in registration order** (after\n * hooks reversed by {@link HookRegistry.for}, so teardown unwinds setup). Used\n * for feature hooks, where ordering matters. Throws if a hook throws, so the\n * runner reports it against the enclosing boundary.\n */\nexport async function runHooks(\n scope: \"feature\" | \"global\",\n timing: HookTiming,\n registry: HookRegistry = globalHookRegistry\n): Promise<void> {\n for (const hook of registry.for(scope, timing)) {\n await (hook.fn as PlainHookFn)();\n }\n}\n\n/**\n * Run every registered hook of a scope+timing **concurrently**, resolving once\n * all of them settle. This is how global `beforeAll`/`afterAll` run: independent\n * setup/teardown steps fire in parallel with no ordering between them. A hook\n * with a sequential requirement should sequence that work inside a single hook.\n *\n * The runner calls this exactly once for `beforeAll` (before any scenario\n * starts) and once for `afterAll` (after every scenario is done), so global\n * setup/teardown brackets the whole run deterministically. Rejects if any hook\n * rejects (via `Promise.all`), surfacing the first failure to the caller.\n */\nexport async function runHooksParallel(\n scope: \"feature\" | \"global\",\n timing: HookTiming,\n registry: HookRegistry = globalHookRegistry\n): Promise<void> {\n await Promise.all(\n registry.for(scope, timing).map(hook => (hook.fn as PlainHookFn)())\n );\n}\n"],"mappings":";;;;;;AAkCA,IAAa,eAAb,MAA0B;CACxB,QAAkC,CAAC;CAEnC,IAAI,MAA4B;EAC9B,KAAK,MAAM,KAAK,IAAI;CACtB;CAEA,MAAiC;EAC/B,OAAO,KAAK;CACd;CAEA,QAAc;EACZ,KAAK,QAAQ,CAAC;CAChB;AACF;AAEA,MAAa,iBAAiB,IAAI,aAAa;;;;;;;;ACX/C,IAAa,eAAb,MAA0B;CACxB,QAAkC,CAAC;CAEnC,IAAI,MAA4B;EAC9B,KAAK,MAAM,KAAK,IAAI;CACtB;;;;;CAMA,IAAI,OAAkB,QAAsC;EAC1D,MAAM,WAAW,KAAK,MAAM,QAC1B,MAAK,EAAE,UAAU,SAAS,EAAE,WAAW,MACzC;EACA,OAAO,WAAW,UAAU,SAAS,QAAQ,IAAI;CACnD;CAEA,QAAc;EACZ,KAAK,QAAQ,CAAC;CAChB;AACF;AAEA,MAAa,qBAAqB,IAAI,aAAa;;;;;;;AAQnD,eAAsB,SACpB,OACA,QACA,WAAyB,oBACV;CACf,KAAK,MAAM,QAAQ,SAAS,IAAI,OAAO,MAAM,GAC3C,MAAO,KAAK,GAAmB;AAEnC;;;;;;;;;;;;AAaA,eAAsB,iBACpB,OACA,QACA,WAAyB,oBACV;CACf,MAAM,QAAQ,IACZ,SAAS,IAAI,OAAO,MAAM,CAAC,CAAC,KAAI,SAAS,KAAK,GAAmB,CAAC,CACpE;AACF"}
|
package/dist/runtime.cjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_hooks = require("./hooks-
|
|
3
|
-
const require_engine = require("./engine-
|
|
2
|
+
const require_hooks = require("./hooks-Be0cjULN.cjs");
|
|
3
|
+
const require_engine = require("./engine-vqA-eL_T.cjs");
|
|
4
4
|
exports.AmbiguousStepError = require_engine.AmbiguousStepError;
|
|
5
5
|
exports.HookRegistry = require_hooks.HookRegistry;
|
|
6
6
|
exports.StepRegistry = require_hooks.StepRegistry;
|
|
7
7
|
exports.UndefinedStepError = require_engine.UndefinedStepError;
|
|
8
8
|
exports.compileRegistry = require_engine.compileRegistry;
|
|
9
|
-
exports.ensureGlobalHooks = require_hooks.ensureGlobalHooks;
|
|
10
9
|
exports.globalHookRegistry = require_hooks.globalHookRegistry;
|
|
11
10
|
exports.globalRegistry = require_hooks.globalRegistry;
|
|
12
11
|
exports.runHooks = require_hooks.runHooks;
|
|
12
|
+
exports.runHooksParallel = require_hooks.runHooksParallel;
|
|
13
13
|
exports.runScenario = require_engine.runScenario;
|
package/dist/runtime.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { S as Parser, _ as ParsedScenario, a as HookTiming, c as ScenarioHookFn, d as runHooks, f as runHooksParallel, i as HookScope, l as ScenarioInfo, n as RunnerOptions, o as PlainHookFn, r as HookRegistry, s as RegisteredHook, t as ResolvedConfig, u as globalHookRegistry, v as ParsedStep, x as MergeableWorld } from "./config-C7PCYgYy.cjs";
|
|
2
2
|
import { CucumberExpression } from "@cucumber/cucumber-expressions";
|
|
3
3
|
|
|
4
4
|
//#region src/runtime/registry.d.ts
|
|
@@ -61,6 +61,12 @@ interface StepResult {
|
|
|
61
61
|
step: ParsedStep;
|
|
62
62
|
status: "passed" | "failed" | "skipped";
|
|
63
63
|
error?: Error;
|
|
64
|
+
/**
|
|
65
|
+
* Absolute `file:line:column` where the matched step is *defined* (its
|
|
66
|
+
* `.step(...)` call site), for Cucumber-style reporting. Absent when no step
|
|
67
|
+
* matched (undefined/ambiguous) or the step was skipped.
|
|
68
|
+
*/
|
|
69
|
+
source?: string;
|
|
64
70
|
durationMs?: number;
|
|
65
71
|
}
|
|
66
72
|
interface ScenarioResult {
|
|
@@ -69,9 +75,9 @@ interface ScenarioResult {
|
|
|
69
75
|
steps: StepResult[];
|
|
70
76
|
/**
|
|
71
77
|
* The scenario's first error, if it failed. Usually the same object as the
|
|
72
|
-
* failing step's `error
|
|
73
|
-
*
|
|
74
|
-
*
|
|
78
|
+
* failing step's `error`; for a hook failure there's no step to point at, so
|
|
79
|
+
* this is the only place it surfaces. Reporters read this; the runner never
|
|
80
|
+
* throws it.
|
|
75
81
|
*/
|
|
76
82
|
error?: Error;
|
|
77
83
|
/** Wall-clock duration of the whole scenario, in milliseconds. */
|
|
@@ -95,48 +101,5 @@ interface ScenarioResult {
|
|
|
95
101
|
*/
|
|
96
102
|
declare function runScenario(scenario: ParsedScenario, compiled: CompiledStep[], makeWorld: () => MergeableWorld<any, any, any>, hooks?: HookRegistry): Promise<ScenarioResult>;
|
|
97
103
|
//#endregion
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* User-facing runner configuration. The same shape is accepted from a
|
|
101
|
-
* `step-forge.config.ts` file (default export) and from CLI flags, with flags
|
|
102
|
-
* taking precedence. Everything is optional; {@link resolveConfig} fills in
|
|
103
|
-
* defaults.
|
|
104
|
-
*/
|
|
105
|
-
interface RunnerOptions {
|
|
106
|
-
/** Feature-file glob(s), relative to `cwd`. Defaults to every `.feature` file. */
|
|
107
|
-
features?: string | string[];
|
|
108
|
-
/** Step-module glob(s), relative to `cwd`. Defaults to every `.steps.ts` file. */
|
|
109
|
-
steps?: string | string[];
|
|
110
|
-
/**
|
|
111
|
-
* Module that default-exports a world factory `() => world`, relative to
|
|
112
|
-
* `cwd`. When omitted each scenario gets a fresh `BasicWorld`.
|
|
113
|
-
*/
|
|
114
|
-
world?: string;
|
|
115
|
-
/**
|
|
116
|
-
* Max scenarios in flight at once. Defaults to `1` (serial), matching
|
|
117
|
-
* Cucumber's default execution model — scenarios often share module-level
|
|
118
|
-
* state via hooks, which only holds under serial execution. Raise it to opt
|
|
119
|
-
* into parallelism for isolated, I/O-bound suites.
|
|
120
|
-
*/
|
|
121
|
-
concurrency?: number;
|
|
122
|
-
/** Reporter name. Default `pretty`. */
|
|
123
|
-
reporter?: "pretty" | "progress";
|
|
124
|
-
/** Only run scenarios whose name matches this (string → substring/regex). */
|
|
125
|
-
name?: string;
|
|
126
|
-
/** Cucumber tag expression, e.g. `@smoke and not @wip`. */
|
|
127
|
-
tags?: string;
|
|
128
|
-
}
|
|
129
|
-
/** Fully-resolved config: no optionals, globs kept as arrays, paths absolute. */
|
|
130
|
-
interface ResolvedConfig {
|
|
131
|
-
cwd: string;
|
|
132
|
-
features: string[];
|
|
133
|
-
steps: string[];
|
|
134
|
-
world?: string;
|
|
135
|
-
concurrency: number;
|
|
136
|
-
reporter: "pretty" | "progress";
|
|
137
|
-
name?: string;
|
|
138
|
-
tags?: string;
|
|
139
|
-
}
|
|
140
|
-
//#endregion
|
|
141
|
-
export { AmbiguousStepError, type CompiledStep, HookRegistry, type HookScope, type HookTiming, type PlainHookFn, type RegisteredHook, type RegisteredStep, type ResolvedConfig, type RunnerOptions, type ScenarioHookFn, type ScenarioInfo, type ScenarioResult, StepRegistry, type StepResult, type StepType, UndefinedStepError, compileRegistry, ensureGlobalHooks, globalHookRegistry, globalRegistry, runHooks, runScenario };
|
|
104
|
+
export { AmbiguousStepError, type CompiledStep, HookRegistry, type HookScope, type HookTiming, type PlainHookFn, type RegisteredHook, type RegisteredStep, type ResolvedConfig, type RunnerOptions, type ScenarioHookFn, type ScenarioInfo, type ScenarioResult, StepRegistry, type StepResult, type StepType, UndefinedStepError, compileRegistry, globalHookRegistry, globalRegistry, runHooks, runHooksParallel, runScenario };
|
|
142
105
|
//# sourceMappingURL=runtime.d.cts.map
|
package/dist/runtime.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { S as Parser, _ as ParsedScenario, a as HookTiming, c as ScenarioHookFn, d as runHooks, f as runHooksParallel, i as HookScope, l as ScenarioInfo, n as RunnerOptions, o as PlainHookFn, r as HookRegistry, s as RegisteredHook, t as ResolvedConfig, u as globalHookRegistry, v as ParsedStep, x as MergeableWorld } from "./config-C7PCYgYy.js";
|
|
2
2
|
import { CucumberExpression } from "@cucumber/cucumber-expressions";
|
|
3
3
|
|
|
4
4
|
//#region src/runtime/registry.d.ts
|
|
@@ -61,6 +61,12 @@ interface StepResult {
|
|
|
61
61
|
step: ParsedStep;
|
|
62
62
|
status: "passed" | "failed" | "skipped";
|
|
63
63
|
error?: Error;
|
|
64
|
+
/**
|
|
65
|
+
* Absolute `file:line:column` where the matched step is *defined* (its
|
|
66
|
+
* `.step(...)` call site), for Cucumber-style reporting. Absent when no step
|
|
67
|
+
* matched (undefined/ambiguous) or the step was skipped.
|
|
68
|
+
*/
|
|
69
|
+
source?: string;
|
|
64
70
|
durationMs?: number;
|
|
65
71
|
}
|
|
66
72
|
interface ScenarioResult {
|
|
@@ -69,9 +75,9 @@ interface ScenarioResult {
|
|
|
69
75
|
steps: StepResult[];
|
|
70
76
|
/**
|
|
71
77
|
* The scenario's first error, if it failed. Usually the same object as the
|
|
72
|
-
* failing step's `error
|
|
73
|
-
*
|
|
74
|
-
*
|
|
78
|
+
* failing step's `error`; for a hook failure there's no step to point at, so
|
|
79
|
+
* this is the only place it surfaces. Reporters read this; the runner never
|
|
80
|
+
* throws it.
|
|
75
81
|
*/
|
|
76
82
|
error?: Error;
|
|
77
83
|
/** Wall-clock duration of the whole scenario, in milliseconds. */
|
|
@@ -95,48 +101,5 @@ interface ScenarioResult {
|
|
|
95
101
|
*/
|
|
96
102
|
declare function runScenario(scenario: ParsedScenario, compiled: CompiledStep[], makeWorld: () => MergeableWorld<any, any, any>, hooks?: HookRegistry): Promise<ScenarioResult>;
|
|
97
103
|
//#endregion
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* User-facing runner configuration. The same shape is accepted from a
|
|
101
|
-
* `step-forge.config.ts` file (default export) and from CLI flags, with flags
|
|
102
|
-
* taking precedence. Everything is optional; {@link resolveConfig} fills in
|
|
103
|
-
* defaults.
|
|
104
|
-
*/
|
|
105
|
-
interface RunnerOptions {
|
|
106
|
-
/** Feature-file glob(s), relative to `cwd`. Defaults to every `.feature` file. */
|
|
107
|
-
features?: string | string[];
|
|
108
|
-
/** Step-module glob(s), relative to `cwd`. Defaults to every `.steps.ts` file. */
|
|
109
|
-
steps?: string | string[];
|
|
110
|
-
/**
|
|
111
|
-
* Module that default-exports a world factory `() => world`, relative to
|
|
112
|
-
* `cwd`. When omitted each scenario gets a fresh `BasicWorld`.
|
|
113
|
-
*/
|
|
114
|
-
world?: string;
|
|
115
|
-
/**
|
|
116
|
-
* Max scenarios in flight at once. Defaults to `1` (serial), matching
|
|
117
|
-
* Cucumber's default execution model — scenarios often share module-level
|
|
118
|
-
* state via hooks, which only holds under serial execution. Raise it to opt
|
|
119
|
-
* into parallelism for isolated, I/O-bound suites.
|
|
120
|
-
*/
|
|
121
|
-
concurrency?: number;
|
|
122
|
-
/** Reporter name. Default `pretty`. */
|
|
123
|
-
reporter?: "pretty" | "progress";
|
|
124
|
-
/** Only run scenarios whose name matches this (string → substring/regex). */
|
|
125
|
-
name?: string;
|
|
126
|
-
/** Cucumber tag expression, e.g. `@smoke and not @wip`. */
|
|
127
|
-
tags?: string;
|
|
128
|
-
}
|
|
129
|
-
/** Fully-resolved config: no optionals, globs kept as arrays, paths absolute. */
|
|
130
|
-
interface ResolvedConfig {
|
|
131
|
-
cwd: string;
|
|
132
|
-
features: string[];
|
|
133
|
-
steps: string[];
|
|
134
|
-
world?: string;
|
|
135
|
-
concurrency: number;
|
|
136
|
-
reporter: "pretty" | "progress";
|
|
137
|
-
name?: string;
|
|
138
|
-
tags?: string;
|
|
139
|
-
}
|
|
140
|
-
//#endregion
|
|
141
|
-
export { AmbiguousStepError, type CompiledStep, HookRegistry, type HookScope, type HookTiming, type PlainHookFn, type RegisteredHook, type RegisteredStep, type ResolvedConfig, type RunnerOptions, type ScenarioHookFn, type ScenarioInfo, type ScenarioResult, StepRegistry, type StepResult, type StepType, UndefinedStepError, compileRegistry, ensureGlobalHooks, globalHookRegistry, globalRegistry, runHooks, runScenario };
|
|
104
|
+
export { AmbiguousStepError, type CompiledStep, HookRegistry, type HookScope, type HookTiming, type PlainHookFn, type RegisteredHook, type RegisteredStep, type ResolvedConfig, type RunnerOptions, type ScenarioHookFn, type ScenarioInfo, type ScenarioResult, StepRegistry, type StepResult, type StepType, UndefinedStepError, compileRegistry, globalHookRegistry, globalRegistry, runHooks, runHooksParallel, runScenario };
|
|
142
105
|
//# sourceMappingURL=runtime.d.ts.map
|
package/dist/runtime.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as StepRegistry, i as
|
|
2
|
-
import { i as runScenario, n as UndefinedStepError, r as compileRegistry, t as AmbiguousStepError } from "./engine-
|
|
3
|
-
export { AmbiguousStepError, HookRegistry, StepRegistry, UndefinedStepError, compileRegistry,
|
|
1
|
+
import { a as StepRegistry, i as runHooksParallel, n as globalHookRegistry, o as globalRegistry, r as runHooks, t as HookRegistry } from "./hooks-BDCMKeNq.js";
|
|
2
|
+
import { i as runScenario, n as UndefinedStepError, r as compileRegistry, t as AmbiguousStepError } from "./engine-DPVLEHBi.js";
|
|
3
|
+
export { AmbiguousStepError, HookRegistry, StepRegistry, UndefinedStepError, compileRegistry, globalHookRegistry, globalRegistry, runHooks, runHooksParallel, runScenario };
|
package/dist/step-forge.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_gherkinParser = require("./gherkinParser-
|
|
3
|
-
const require_hooks = require("./hooks-
|
|
2
|
+
const require_gherkinParser = require("./gherkinParser-BT40q_i3.cjs");
|
|
3
|
+
const require_hooks = require("./hooks-Be0cjULN.cjs");
|
|
4
4
|
let lodash = require("lodash");
|
|
5
5
|
lodash = require_gherkinParser.__toESM(lodash, 1);
|
|
6
6
|
let typescript = require("typescript");
|
|
@@ -117,11 +117,13 @@ const addStep = (statement, stepType, dependencies = {
|
|
|
117
117
|
});
|
|
118
118
|
world[stepType].merge({ ...result });
|
|
119
119
|
};
|
|
120
|
+
const source = require_gherkinParser.captureDefinitionSite();
|
|
120
121
|
require_hooks.globalRegistry.add({
|
|
121
122
|
stepType,
|
|
122
123
|
expression,
|
|
123
124
|
parsers,
|
|
124
|
-
execute
|
|
125
|
+
execute,
|
|
126
|
+
source
|
|
125
127
|
});
|
|
126
128
|
return {
|
|
127
129
|
statement,
|
|
@@ -202,6 +204,21 @@ const BUILDER_NAMES = {
|
|
|
202
204
|
thenBuilder: "then"
|
|
203
205
|
};
|
|
204
206
|
function extractStepDefinitions(filePaths, tsConfigPath) {
|
|
207
|
+
const fast = extractWithSources(filePaths.map(parseSourceFile).filter((s) => s !== null), null);
|
|
208
|
+
if (!fast.needsChecker) return fast.results;
|
|
209
|
+
const program = typescript.default.createProgram(filePaths, resolveCompilerOptions(tsConfigPath));
|
|
210
|
+
const checker = program.getTypeChecker();
|
|
211
|
+
return extractWithSources(filePaths.map((fp) => program.getSourceFile(fp)).filter((s) => s !== void 0), checker).results;
|
|
212
|
+
}
|
|
213
|
+
/** Parse a single file into an AST with no type resolution. */
|
|
214
|
+
function parseSourceFile(filePath) {
|
|
215
|
+
const text = typescript.default.sys.readFile(filePath);
|
|
216
|
+
if (text === void 0) return null;
|
|
217
|
+
const scriptKind = filePath.endsWith(".tsx") ? typescript.default.ScriptKind.TSX : typescript.default.ScriptKind.TS;
|
|
218
|
+
return typescript.default.createSourceFile(filePath, text, typescript.default.ScriptTarget.ESNext, true, scriptKind);
|
|
219
|
+
}
|
|
220
|
+
/** Resolve compiler options from tsconfig (fallback to sane defaults). */
|
|
221
|
+
function resolveCompilerOptions(tsConfigPath) {
|
|
205
222
|
const configPath = tsConfigPath ?? typescript.default.findConfigFile(process.cwd(), typescript.default.sys.fileExists);
|
|
206
223
|
let compilerOptions = {
|
|
207
224
|
target: typescript.default.ScriptTarget.ESNext,
|
|
@@ -215,22 +232,25 @@ function extractStepDefinitions(filePaths, tsConfigPath) {
|
|
|
215
232
|
if (!configFile.error) compilerOptions = typescript.default.parseJsonConfigFileContent(configFile.config, typescript.default.sys, configPath.replace(/[/\\][^/\\]+$/, "")).options;
|
|
216
233
|
}
|
|
217
234
|
compilerOptions.noEmit = true;
|
|
218
|
-
|
|
219
|
-
|
|
235
|
+
return compilerOptions;
|
|
236
|
+
}
|
|
237
|
+
function extractWithSources(sources, checker) {
|
|
238
|
+
const ctx = {
|
|
239
|
+
checker,
|
|
240
|
+
needsChecker: false
|
|
241
|
+
};
|
|
220
242
|
const results = [];
|
|
221
|
-
for (const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
227
|
-
return results;
|
|
243
|
+
for (const sourceFile of sources) results.push(...extractFromSourceFile(sourceFile, ctx));
|
|
244
|
+
return {
|
|
245
|
+
results,
|
|
246
|
+
needsChecker: ctx.needsChecker
|
|
247
|
+
};
|
|
228
248
|
}
|
|
229
|
-
function extractFromSourceFile(sourceFile,
|
|
249
|
+
function extractFromSourceFile(sourceFile, ctx) {
|
|
230
250
|
const results = [];
|
|
231
251
|
function visit(node) {
|
|
232
252
|
if (typescript.default.isCallExpression(node) && typescript.default.isPropertyAccessExpression(node.expression) && node.expression.name.text === "step") {
|
|
233
|
-
const meta = extractFromRegisterCall(node, sourceFile,
|
|
253
|
+
const meta = extractFromRegisterCall(node, sourceFile, ctx);
|
|
234
254
|
if (meta) results.push(meta);
|
|
235
255
|
}
|
|
236
256
|
typescript.default.forEachChild(node, visit);
|
|
@@ -238,7 +258,7 @@ function extractFromSourceFile(sourceFile, checker) {
|
|
|
238
258
|
visit(sourceFile);
|
|
239
259
|
return results;
|
|
240
260
|
}
|
|
241
|
-
function extractFromRegisterCall(registerCall, sourceFile,
|
|
261
|
+
function extractFromRegisterCall(registerCall, sourceFile, ctx) {
|
|
242
262
|
const chain = collectCallChain(registerCall);
|
|
243
263
|
let stepType = null;
|
|
244
264
|
let expression = null;
|
|
@@ -253,7 +273,7 @@ function extractFromRegisterCall(registerCall, sourceFile, checker) {
|
|
|
253
273
|
if (!name) continue;
|
|
254
274
|
if (name === "register") continue;
|
|
255
275
|
if (name === "step") {
|
|
256
|
-
produces = extractProducedKeys(link,
|
|
276
|
+
produces = extractProducedKeys(link, ctx);
|
|
257
277
|
continue;
|
|
258
278
|
}
|
|
259
279
|
if (name === "dependencies") {
|
|
@@ -270,7 +290,7 @@ function extractFromRegisterCall(registerCall, sourceFile, checker) {
|
|
|
270
290
|
}
|
|
271
291
|
}
|
|
272
292
|
if (!stepType || !expression) {
|
|
273
|
-
const reExport = resolveReExportedCall(chain,
|
|
293
|
+
const reExport = resolveReExportedCall(chain, ctx);
|
|
274
294
|
if (reExport) {
|
|
275
295
|
if (!stepType) stepType = reExport.stepType;
|
|
276
296
|
if (!expression) expression = reExport.expression;
|
|
@@ -357,33 +377,37 @@ function extractDependencies(depsCall) {
|
|
|
357
377
|
}
|
|
358
378
|
return deps;
|
|
359
379
|
}
|
|
360
|
-
function extractProducedKeys(stepCall,
|
|
380
|
+
function extractProducedKeys(stepCall, ctx) {
|
|
361
381
|
const callback = stepCall.arguments[0];
|
|
362
382
|
if (!callback) return [];
|
|
363
|
-
if (typescript.default.isArrowFunction(callback) || typescript.default.isFunctionExpression(callback)) return extractProducedKeysFromCallback(callback,
|
|
383
|
+
if (typescript.default.isArrowFunction(callback) || typescript.default.isFunctionExpression(callback)) return extractProducedKeysFromCallback(callback, ctx);
|
|
364
384
|
return [];
|
|
365
385
|
}
|
|
366
|
-
function extractProducedKeysFromCallback(callback,
|
|
386
|
+
function extractProducedKeysFromCallback(callback, ctx) {
|
|
367
387
|
const body = callback.body;
|
|
368
|
-
if (!typescript.default.isBlock(body)) return extractKeysFromExpression(body,
|
|
388
|
+
if (!typescript.default.isBlock(body)) return extractKeysFromExpression(body, ctx);
|
|
369
389
|
const keys = /* @__PURE__ */ new Set();
|
|
370
390
|
function visitReturn(node) {
|
|
371
|
-
if (typescript.default.isReturnStatement(node) && node.expression) for (const key of extractKeysFromExpression(node.expression,
|
|
391
|
+
if (typescript.default.isReturnStatement(node) && node.expression) for (const key of extractKeysFromExpression(node.expression, ctx)) keys.add(key);
|
|
372
392
|
typescript.default.forEachChild(node, visitReturn);
|
|
373
393
|
}
|
|
374
394
|
visitReturn(body);
|
|
375
395
|
return [...keys];
|
|
376
396
|
}
|
|
377
|
-
function extractKeysFromExpression(expr,
|
|
397
|
+
function extractKeysFromExpression(expr, ctx) {
|
|
378
398
|
while (typescript.default.isParenthesizedExpression(expr)) expr = expr.expression;
|
|
379
399
|
if (typescript.default.isObjectLiteralExpression(expr)) return expr.properties.filter((p) => typescript.default.isPropertyAssignment(p) || typescript.default.isShorthandPropertyAssignment(p)).map((p) => p.name.getText()).filter(Boolean);
|
|
400
|
+
if (!ctx.checker) {
|
|
401
|
+
ctx.needsChecker = true;
|
|
402
|
+
return [];
|
|
403
|
+
}
|
|
380
404
|
try {
|
|
381
|
-
return checker.getTypeAtLocation(expr).getProperties().map((p) => p.name).filter((n) => n !== "merge");
|
|
405
|
+
return ctx.checker.getTypeAtLocation(expr).getProperties().map((p) => p.name).filter((n) => n !== "merge");
|
|
382
406
|
} catch {
|
|
383
407
|
return [];
|
|
384
408
|
}
|
|
385
409
|
}
|
|
386
|
-
function resolveReExportedCall(chain,
|
|
410
|
+
function resolveReExportedCall(chain, ctx) {
|
|
387
411
|
const lastCall = chain[chain.length - 1];
|
|
388
412
|
if (!lastCall) return null;
|
|
389
413
|
const expr = lastCall.expression;
|
|
@@ -391,7 +415,11 @@ function resolveReExportedCall(chain, checker) {
|
|
|
391
415
|
if (typescript.default.isIdentifier(expr)) identifier = expr;
|
|
392
416
|
else if (typescript.default.isPropertyAccessExpression(expr) && typescript.default.isIdentifier(expr.expression)) identifier = expr.expression;
|
|
393
417
|
if (!identifier) return null;
|
|
394
|
-
|
|
418
|
+
if (!ctx.checker) {
|
|
419
|
+
ctx.needsChecker = true;
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
const symbol = ctx.checker.getSymbolAtLocation(identifier);
|
|
395
423
|
if (!symbol) return null;
|
|
396
424
|
const decl = symbol.valueDeclaration;
|
|
397
425
|
if (!decl || !typescript.default.isVariableDeclaration(decl) || !decl.initializer) return null;
|
|
@@ -611,7 +639,12 @@ function afterFeature(fn) {
|
|
|
611
639
|
fn
|
|
612
640
|
});
|
|
613
641
|
}
|
|
614
|
-
/**
|
|
642
|
+
/**
|
|
643
|
+
* Run once before the entire run, ahead of any scenario (and before
|
|
644
|
+
* concurrency starts). Multiple `beforeAll` hooks run **in parallel** with no
|
|
645
|
+
* ordering between them — if a step of setup must precede another, sequence both
|
|
646
|
+
* inside a single hook.
|
|
647
|
+
*/
|
|
615
648
|
function beforeAll(fn) {
|
|
616
649
|
require_hooks.globalHookRegistry.add({
|
|
617
650
|
scope: "global",
|
|
@@ -619,7 +652,11 @@ function beforeAll(fn) {
|
|
|
619
652
|
fn
|
|
620
653
|
});
|
|
621
654
|
}
|
|
622
|
-
/**
|
|
655
|
+
/**
|
|
656
|
+
* Run once after the entire run, once every scenario is done. Multiple
|
|
657
|
+
* `afterAll` hooks run **in parallel** with no ordering between them — sequence
|
|
658
|
+
* dependent teardown inside a single hook.
|
|
659
|
+
*/
|
|
623
660
|
function afterAll(fn) {
|
|
624
661
|
require_hooks.globalHookRegistry.add({
|
|
625
662
|
scope: "global",
|