@step-forge/step-forge 0.0.20 → 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.
Files changed (47) hide show
  1. package/README.md +2 -0
  2. package/RUNTIME.md +242 -0
  3. package/dist/{analyzer-DJyJbU_V.js → analyzer-byS8yRrY.js} +202 -34
  4. package/dist/analyzer-byS8yRrY.js.map +1 -0
  5. package/dist/analyzer-cli.js +1 -1
  6. package/dist/analyzer.d.ts +2 -0
  7. package/dist/analyzer.js +1 -2
  8. package/dist/cli.cjs +525 -0
  9. package/dist/cli.cjs.map +1 -0
  10. package/dist/cli.d.cts +1 -0
  11. package/dist/cli.d.ts +1 -0
  12. package/dist/cli.js +526 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/{hooks-Dar49TtT.d.ts → config-C7PCYgYy.d.cts} +65 -16
  15. package/dist/{hooks-Dar49TtT.d.cts → config-C7PCYgYy.d.ts} +65 -16
  16. package/dist/engine-DPVLEHBi.js +163 -0
  17. package/dist/engine-DPVLEHBi.js.map +1 -0
  18. package/dist/engine-vqA-eL_T.cjs +186 -0
  19. package/dist/engine-vqA-eL_T.cjs.map +1 -0
  20. package/dist/gherkinParser-BT40q_i3.cjs +338 -0
  21. package/dist/gherkinParser-BT40q_i3.cjs.map +1 -0
  22. package/dist/gherkinParser-NcttZgN4.js +259 -0
  23. package/dist/gherkinParser-NcttZgN4.js.map +1 -0
  24. package/dist/hooks-BDCMKeNq.js +71 -0
  25. package/dist/{hooks-CywugMQQ.js.map → hooks-BDCMKeNq.js.map} +1 -1
  26. package/dist/{hooks-CGYzwDOv.cjs → hooks-Be0cjULN.cjs} +20 -31
  27. package/dist/{hooks-CGYzwDOv.cjs.map → hooks-Be0cjULN.cjs.map} +1 -1
  28. package/dist/runtime.cjs +7 -162
  29. package/dist/runtime.d.cts +44 -8
  30. package/dist/runtime.d.ts +44 -8
  31. package/dist/runtime.js +3 -159
  32. package/dist/step-forge.cjs +73 -216
  33. package/dist/step-forge.cjs.map +1 -1
  34. package/dist/step-forge.d.cts +19 -10
  35. package/dist/step-forge.d.ts +19 -10
  36. package/dist/step-forge.js +67 -185
  37. package/dist/step-forge.js.map +1 -1
  38. package/package.json +12 -18
  39. package/dist/analyzer-DJyJbU_V.js.map +0 -1
  40. package/dist/gherkinParser-Dp2d7JNr.js +0 -116
  41. package/dist/gherkinParser-Dp2d7JNr.js.map +0 -1
  42. package/dist/hooks-CywugMQQ.js +0 -82
  43. package/dist/runtime.cjs.map +0 -1
  44. package/dist/runtime.js.map +0 -1
  45. package/dist/vitest.d.ts +0 -74
  46. package/dist/vitest.js +0 -136
  47. package/dist/vitest.js.map +0 -1
package/dist/runtime.cjs CHANGED
@@ -1,168 +1,13 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_hooks = require("./hooks-CGYzwDOv.cjs");
3
- let _cucumber_cucumber_expressions = require("@cucumber/cucumber-expressions");
4
- //#region src/runtime/engine.ts
5
- const keywordToStepType = {
6
- Given: "given",
7
- When: "when",
8
- Then: "then"
9
- };
10
- var UndefinedStepError = class extends Error {
11
- step;
12
- constructor(step) {
13
- super(`Undefined step: ${step.effectiveKeyword} ${step.text}`);
14
- this.step = step;
15
- this.name = "UndefinedStepError";
16
- }
17
- };
18
- var AmbiguousStepError = class extends Error {
19
- step;
20
- matches;
21
- constructor(step, matches) {
22
- super(`Ambiguous step: "${step.text}" matched ${matches.length} definitions:\n` + matches.map((m) => ` - ${m.expression}`).join("\n"));
23
- this.step = step;
24
- this.matches = matches;
25
- this.name = "AmbiguousStepError";
26
- }
27
- };
28
- function compile(registry) {
29
- return registry.all().map((step) => {
30
- const paramRegistry = new _cucumber_cucumber_expressions.ParameterTypeRegistry();
31
- for (const parser of step.parsers) {
32
- if (paramRegistry.lookupByTypeName(parser.name)) continue;
33
- const regexps = Array.isArray(parser.regexp) ? parser.regexp : [parser.regexp];
34
- paramRegistry.defineParameterType(new _cucumber_cucumber_expressions.ParameterType(parser.name, regexps, null, (value) => parser.parse(value)));
35
- }
36
- return {
37
- step,
38
- expression: new _cucumber_cucumber_expressions.CucumberExpression(step.expression, paramRegistry)
39
- };
40
- });
41
- }
42
- /**
43
- * Find the single step definition matching a Gherkin step. Matching is
44
- * opinionated and strict: the keyword must line up with the step type, exactly
45
- * one definition must match, and undefined/ambiguous both throw rather than
46
- * silently skipping (unlike Cucumber's pending/undefined dance).
47
- */
48
- function matchStep(step, compiled) {
49
- const expectedType = keywordToStepType[step.effectiveKeyword];
50
- const matches = [];
51
- for (const { step: def, expression } of compiled) {
52
- if (def.stepType !== expectedType) continue;
53
- const result = expression.match(step.text);
54
- if (result) matches.push({
55
- step: def,
56
- args: result.map((a) => a.getValue(null))
57
- });
58
- }
59
- if (matches.length === 0) throw new UndefinedStepError(step);
60
- if (matches.length > 1) throw new AmbiguousStepError(step, matches.map((m) => m.step));
61
- return matches[0];
62
- }
63
- /**
64
- * Run one scenario against a registry. A fresh world is created per scenario
65
- * (state never leaks between scenarios). On the first failing step the
66
- * remaining steps are marked skipped, matching Cucumber's execution semantics.
67
- *
68
- * Throws on failure so it maps cleanly onto a test runner's `test()` body, but
69
- * the returned/attached `ScenarioResult` carries the per-step breakdown.
70
- */
71
- async function runScenario(scenario, registry, makeWorld, hooks = require_hooks.globalHookRegistry) {
72
- const compiled = compile(registry);
73
- const world = makeWorld();
74
- const scenarioInfo = {
75
- name: scenario.name,
76
- file: scenario.file
77
- };
78
- const steps = [];
79
- let failed = false;
80
- let firstError;
81
- const fail = (err) => {
82
- if (failed) return;
83
- failed = true;
84
- firstError = err instanceof Error ? err : new Error(String(err));
85
- };
86
- try {
87
- for (const hook of hooks.for("scenario", "before")) await hook.fn({
88
- world,
89
- scenario: scenarioInfo
90
- });
91
- } catch (err) {
92
- fail(err);
93
- }
94
- for (const step of scenario.steps) {
95
- if (failed) {
96
- steps.push({
97
- step,
98
- status: "skipped"
99
- });
100
- continue;
101
- }
102
- try {
103
- const { step: def, args } = matchStep(step, compiled);
104
- await def.execute(world, args);
105
- steps.push({
106
- step,
107
- status: "passed"
108
- });
109
- } catch (err) {
110
- const error = err instanceof Error ? err : new Error(String(err));
111
- steps.push({
112
- step,
113
- status: "failed",
114
- error
115
- });
116
- fail(error);
117
- }
118
- }
119
- for (const hook of hooks.for("scenario", "after")) try {
120
- await hook.fn({
121
- world,
122
- scenario: scenarioInfo
123
- });
124
- } catch (err) {
125
- fail(err);
126
- }
127
- const result = {
128
- scenario,
129
- status: failed ? "failed" : "passed",
130
- steps
131
- };
132
- if (failed && firstError) {
133
- const failing = steps.find((s) => s.status === "failed");
134
- if (failing) attachFeatureFrame(firstError, scenario.file, failing.step);
135
- throw firstError;
136
- }
137
- return result;
138
- }
139
- /**
140
- * Prepend a synthetic stack frame pointing at the failing Gherkin step. Because
141
- * the frame's file is the real `.feature` on disk, the test runner treats it as
142
- * a source location and shows a code frame at the step — instead of us jamming
143
- * `file:line` into the error message. The frame goes *above* the real stack, so
144
- * the step-definition frames (the actual throw site) are preserved below it.
145
- */
146
- function attachFeatureFrame(error, file, step) {
147
- const frame = ` at ${`${step.effectiveKeyword} ${step.text}`} (${file}:${step.line}:${step.column})`;
148
- const stack = error.stack;
149
- if (!stack) {
150
- error.stack = `${error.name}: ${error.message}\n${frame}`;
151
- return;
152
- }
153
- const firstFrame = stack.indexOf("\n at ");
154
- if (firstFrame === -1) error.stack = `${stack}\n${frame}`;
155
- else error.stack = stack.slice(0, firstFrame) + `\n${frame}` + stack.slice(firstFrame);
156
- }
157
- //#endregion
158
- exports.AmbiguousStepError = AmbiguousStepError;
2
+ const require_hooks = require("./hooks-Be0cjULN.cjs");
3
+ const require_engine = require("./engine-vqA-eL_T.cjs");
4
+ exports.AmbiguousStepError = require_engine.AmbiguousStepError;
159
5
  exports.HookRegistry = require_hooks.HookRegistry;
160
6
  exports.StepRegistry = require_hooks.StepRegistry;
161
- exports.UndefinedStepError = UndefinedStepError;
162
- exports.ensureGlobalHooks = require_hooks.ensureGlobalHooks;
7
+ exports.UndefinedStepError = require_engine.UndefinedStepError;
8
+ exports.compileRegistry = require_engine.compileRegistry;
163
9
  exports.globalHookRegistry = require_hooks.globalHookRegistry;
164
10
  exports.globalRegistry = require_hooks.globalRegistry;
165
11
  exports.runHooks = require_hooks.runHooks;
166
- exports.runScenario = runScenario;
167
-
168
- //# sourceMappingURL=runtime.cjs.map
12
+ exports.runHooksParallel = require_hooks.runHooksParallel;
13
+ exports.runScenario = require_engine.runScenario;
@@ -1,4 +1,5 @@
1
- import { a as RegisteredHook, b as Parser, c as ensureGlobalHooks, g as ParsedStep, h as ParsedScenario, i as PlainHookFn, l as globalHookRegistry, n as HookScope, o as ScenarioHookFn, r as HookTiming, s as ScenarioInfo, t as HookRegistry, u as runHooks, y as MergeableWorld } from "./hooks-Dar49TtT.cjs";
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
+ import { CucumberExpression } from "@cucumber/cucumber-expressions";
2
3
 
3
4
  //#region src/runtime/registry.d.ts
4
5
  type StepType = "given" | "when" | "then";
@@ -35,6 +36,11 @@ declare class StepRegistry {
35
36
  declare const globalRegistry: StepRegistry;
36
37
  //#endregion
37
38
  //#region src/runtime/engine.d.ts
39
+ /** A registered step paired with its compiled Cucumber expression. */
40
+ interface CompiledStep {
41
+ step: RegisteredStep;
42
+ expression: CucumberExpression;
43
+ }
38
44
  declare class UndefinedStepError extends Error {
39
45
  readonly step: ParsedStep;
40
46
  constructor(step: ParsedStep);
@@ -44,26 +50,56 @@ declare class AmbiguousStepError extends Error {
44
50
  readonly matches: RegisteredStep[];
45
51
  constructor(step: ParsedStep, matches: RegisteredStep[]);
46
52
  }
53
+ /**
54
+ * Compile a registry's steps into matchable Cucumber expressions **once** per
55
+ * run. The result is reused for every scenario — compilation is pure and depends
56
+ * only on the registry, so recompiling per scenario (as an earlier version did)
57
+ * was wasted work proportional to scenarios × steps.
58
+ */
59
+ declare function compileRegistry(registry: StepRegistry): CompiledStep[];
47
60
  interface StepResult {
48
61
  step: ParsedStep;
49
62
  status: "passed" | "failed" | "skipped";
50
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;
51
70
  durationMs?: number;
52
71
  }
53
72
  interface ScenarioResult {
54
73
  scenario: ParsedScenario;
55
74
  status: "passed" | "failed";
56
75
  steps: StepResult[];
76
+ /**
77
+ * The scenario's first error, if it failed. Usually the same object as the
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.
81
+ */
82
+ error?: Error;
83
+ /** Wall-clock duration of the whole scenario, in milliseconds. */
84
+ durationMs?: number;
57
85
  }
58
86
  /**
59
- * Run one scenario against a registry. A fresh world is created per scenario
60
- * (state never leaks between scenarios). On the first failing step the
61
- * remaining steps are marked skipped, matching Cucumber's execution semantics.
87
+ * Run one scenario against a pre-compiled step table. A fresh world is created
88
+ * per scenario (state never leaks between scenarios). On the first failing step
89
+ * the remaining steps are marked skipped, matching Cucumber's execution
90
+ * semantics.
91
+ *
92
+ * Never throws for step or hook failures: it always resolves to a
93
+ * `ScenarioResult` carrying the per-step breakdown and (on failure) the first
94
+ * `error` with a synthetic `.feature` stack frame attached. Callers decide what
95
+ * to do with a failure — the CLI runner reports it, a test-runner adapter can
96
+ * re-throw `result.error`. It still rejects for truly exceptional conditions
97
+ * (e.g. a bug in the engine itself), never for a normal test failure.
62
98
  *
63
- * Throws on failure so it maps cleanly onto a test runner's `test()` body, but
64
- * the returned/attached `ScenarioResult` carries the per-step breakdown.
99
+ * Pass the compiled table from {@link compileRegistry} once and reuse it across
100
+ * every scenario in the run.
65
101
  */
66
- declare function runScenario(scenario: ParsedScenario, registry: StepRegistry, makeWorld: () => MergeableWorld<any, any, any>, hooks?: HookRegistry): Promise<ScenarioResult>;
102
+ declare function runScenario(scenario: ParsedScenario, compiled: CompiledStep[], makeWorld: () => MergeableWorld<any, any, any>, hooks?: HookRegistry): Promise<ScenarioResult>;
67
103
  //#endregion
68
- export { AmbiguousStepError, HookRegistry, type HookScope, type HookTiming, type PlainHookFn, type RegisteredHook, type RegisteredStep, type ScenarioHookFn, type ScenarioInfo, type ScenarioResult, StepRegistry, type StepResult, type StepType, UndefinedStepError, 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 };
69
105
  //# sourceMappingURL=runtime.d.cts.map
package/dist/runtime.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { a as RegisteredHook, b as Parser, c as ensureGlobalHooks, g as ParsedStep, h as ParsedScenario, i as PlainHookFn, l as globalHookRegistry, n as HookScope, o as ScenarioHookFn, r as HookTiming, s as ScenarioInfo, t as HookRegistry, u as runHooks, y as MergeableWorld } from "./hooks-Dar49TtT.js";
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
+ import { CucumberExpression } from "@cucumber/cucumber-expressions";
2
3
 
3
4
  //#region src/runtime/registry.d.ts
4
5
  type StepType = "given" | "when" | "then";
@@ -35,6 +36,11 @@ declare class StepRegistry {
35
36
  declare const globalRegistry: StepRegistry;
36
37
  //#endregion
37
38
  //#region src/runtime/engine.d.ts
39
+ /** A registered step paired with its compiled Cucumber expression. */
40
+ interface CompiledStep {
41
+ step: RegisteredStep;
42
+ expression: CucumberExpression;
43
+ }
38
44
  declare class UndefinedStepError extends Error {
39
45
  readonly step: ParsedStep;
40
46
  constructor(step: ParsedStep);
@@ -44,26 +50,56 @@ declare class AmbiguousStepError extends Error {
44
50
  readonly matches: RegisteredStep[];
45
51
  constructor(step: ParsedStep, matches: RegisteredStep[]);
46
52
  }
53
+ /**
54
+ * Compile a registry's steps into matchable Cucumber expressions **once** per
55
+ * run. The result is reused for every scenario — compilation is pure and depends
56
+ * only on the registry, so recompiling per scenario (as an earlier version did)
57
+ * was wasted work proportional to scenarios × steps.
58
+ */
59
+ declare function compileRegistry(registry: StepRegistry): CompiledStep[];
47
60
  interface StepResult {
48
61
  step: ParsedStep;
49
62
  status: "passed" | "failed" | "skipped";
50
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;
51
70
  durationMs?: number;
52
71
  }
53
72
  interface ScenarioResult {
54
73
  scenario: ParsedScenario;
55
74
  status: "passed" | "failed";
56
75
  steps: StepResult[];
76
+ /**
77
+ * The scenario's first error, if it failed. Usually the same object as the
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.
81
+ */
82
+ error?: Error;
83
+ /** Wall-clock duration of the whole scenario, in milliseconds. */
84
+ durationMs?: number;
57
85
  }
58
86
  /**
59
- * Run one scenario against a registry. A fresh world is created per scenario
60
- * (state never leaks between scenarios). On the first failing step the
61
- * remaining steps are marked skipped, matching Cucumber's execution semantics.
87
+ * Run one scenario against a pre-compiled step table. A fresh world is created
88
+ * per scenario (state never leaks between scenarios). On the first failing step
89
+ * the remaining steps are marked skipped, matching Cucumber's execution
90
+ * semantics.
91
+ *
92
+ * Never throws for step or hook failures: it always resolves to a
93
+ * `ScenarioResult` carrying the per-step breakdown and (on failure) the first
94
+ * `error` with a synthetic `.feature` stack frame attached. Callers decide what
95
+ * to do with a failure — the CLI runner reports it, a test-runner adapter can
96
+ * re-throw `result.error`. It still rejects for truly exceptional conditions
97
+ * (e.g. a bug in the engine itself), never for a normal test failure.
62
98
  *
63
- * Throws on failure so it maps cleanly onto a test runner's `test()` body, but
64
- * the returned/attached `ScenarioResult` carries the per-step breakdown.
99
+ * Pass the compiled table from {@link compileRegistry} once and reuse it across
100
+ * every scenario in the run.
65
101
  */
66
- declare function runScenario(scenario: ParsedScenario, registry: StepRegistry, makeWorld: () => MergeableWorld<any, any, any>, hooks?: HookRegistry): Promise<ScenarioResult>;
102
+ declare function runScenario(scenario: ParsedScenario, compiled: CompiledStep[], makeWorld: () => MergeableWorld<any, any, any>, hooks?: HookRegistry): Promise<ScenarioResult>;
67
103
  //#endregion
68
- export { AmbiguousStepError, HookRegistry, type HookScope, type HookTiming, type PlainHookFn, type RegisteredHook, type RegisteredStep, type ScenarioHookFn, type ScenarioInfo, type ScenarioResult, StepRegistry, type StepResult, type StepType, UndefinedStepError, 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 };
69
105
  //# sourceMappingURL=runtime.d.ts.map
package/dist/runtime.js CHANGED
@@ -1,159 +1,3 @@
1
- import { a as StepRegistry, i as runHooks, n as ensureGlobalHooks, o as globalRegistry, r as globalHookRegistry, t as HookRegistry } from "./hooks-CywugMQQ.js";
2
- import { CucumberExpression, ParameterType, ParameterTypeRegistry } from "@cucumber/cucumber-expressions";
3
- //#region src/runtime/engine.ts
4
- const keywordToStepType = {
5
- Given: "given",
6
- When: "when",
7
- Then: "then"
8
- };
9
- var UndefinedStepError = class extends Error {
10
- step;
11
- constructor(step) {
12
- super(`Undefined step: ${step.effectiveKeyword} ${step.text}`);
13
- this.step = step;
14
- this.name = "UndefinedStepError";
15
- }
16
- };
17
- var AmbiguousStepError = class extends Error {
18
- step;
19
- matches;
20
- constructor(step, matches) {
21
- super(`Ambiguous step: "${step.text}" matched ${matches.length} definitions:\n` + matches.map((m) => ` - ${m.expression}`).join("\n"));
22
- this.step = step;
23
- this.matches = matches;
24
- this.name = "AmbiguousStepError";
25
- }
26
- };
27
- function compile(registry) {
28
- return registry.all().map((step) => {
29
- const paramRegistry = new ParameterTypeRegistry();
30
- for (const parser of step.parsers) {
31
- if (paramRegistry.lookupByTypeName(parser.name)) continue;
32
- const regexps = Array.isArray(parser.regexp) ? parser.regexp : [parser.regexp];
33
- paramRegistry.defineParameterType(new ParameterType(parser.name, regexps, null, (value) => parser.parse(value)));
34
- }
35
- return {
36
- step,
37
- expression: new CucumberExpression(step.expression, paramRegistry)
38
- };
39
- });
40
- }
41
- /**
42
- * Find the single step definition matching a Gherkin step. Matching is
43
- * opinionated and strict: the keyword must line up with the step type, exactly
44
- * one definition must match, and undefined/ambiguous both throw rather than
45
- * silently skipping (unlike Cucumber's pending/undefined dance).
46
- */
47
- function matchStep(step, compiled) {
48
- const expectedType = keywordToStepType[step.effectiveKeyword];
49
- const matches = [];
50
- for (const { step: def, expression } of compiled) {
51
- if (def.stepType !== expectedType) continue;
52
- const result = expression.match(step.text);
53
- if (result) matches.push({
54
- step: def,
55
- args: result.map((a) => a.getValue(null))
56
- });
57
- }
58
- if (matches.length === 0) throw new UndefinedStepError(step);
59
- if (matches.length > 1) throw new AmbiguousStepError(step, matches.map((m) => m.step));
60
- return matches[0];
61
- }
62
- /**
63
- * Run one scenario against a registry. A fresh world is created per scenario
64
- * (state never leaks between scenarios). On the first failing step the
65
- * remaining steps are marked skipped, matching Cucumber's execution semantics.
66
- *
67
- * Throws on failure so it maps cleanly onto a test runner's `test()` body, but
68
- * the returned/attached `ScenarioResult` carries the per-step breakdown.
69
- */
70
- async function runScenario(scenario, registry, makeWorld, hooks = globalHookRegistry) {
71
- const compiled = compile(registry);
72
- const world = makeWorld();
73
- const scenarioInfo = {
74
- name: scenario.name,
75
- file: scenario.file
76
- };
77
- const steps = [];
78
- let failed = false;
79
- let firstError;
80
- const fail = (err) => {
81
- if (failed) return;
82
- failed = true;
83
- firstError = err instanceof Error ? err : new Error(String(err));
84
- };
85
- try {
86
- for (const hook of hooks.for("scenario", "before")) await hook.fn({
87
- world,
88
- scenario: scenarioInfo
89
- });
90
- } catch (err) {
91
- fail(err);
92
- }
93
- for (const step of scenario.steps) {
94
- if (failed) {
95
- steps.push({
96
- step,
97
- status: "skipped"
98
- });
99
- continue;
100
- }
101
- try {
102
- const { step: def, args } = matchStep(step, compiled);
103
- await def.execute(world, args);
104
- steps.push({
105
- step,
106
- status: "passed"
107
- });
108
- } catch (err) {
109
- const error = err instanceof Error ? err : new Error(String(err));
110
- steps.push({
111
- step,
112
- status: "failed",
113
- error
114
- });
115
- fail(error);
116
- }
117
- }
118
- for (const hook of hooks.for("scenario", "after")) try {
119
- await hook.fn({
120
- world,
121
- scenario: scenarioInfo
122
- });
123
- } catch (err) {
124
- fail(err);
125
- }
126
- const result = {
127
- scenario,
128
- status: failed ? "failed" : "passed",
129
- steps
130
- };
131
- if (failed && firstError) {
132
- const failing = steps.find((s) => s.status === "failed");
133
- if (failing) attachFeatureFrame(firstError, scenario.file, failing.step);
134
- throw firstError;
135
- }
136
- return result;
137
- }
138
- /**
139
- * Prepend a synthetic stack frame pointing at the failing Gherkin step. Because
140
- * the frame's file is the real `.feature` on disk, the test runner treats it as
141
- * a source location and shows a code frame at the step — instead of us jamming
142
- * `file:line` into the error message. The frame goes *above* the real stack, so
143
- * the step-definition frames (the actual throw site) are preserved below it.
144
- */
145
- function attachFeatureFrame(error, file, step) {
146
- const frame = ` at ${`${step.effectiveKeyword} ${step.text}`} (${file}:${step.line}:${step.column})`;
147
- const stack = error.stack;
148
- if (!stack) {
149
- error.stack = `${error.name}: ${error.message}\n${frame}`;
150
- return;
151
- }
152
- const firstFrame = stack.indexOf("\n at ");
153
- if (firstFrame === -1) error.stack = `${stack}\n${frame}`;
154
- else error.stack = stack.slice(0, firstFrame) + `\n${frame}` + stack.slice(firstFrame);
155
- }
156
- //#endregion
157
- export { AmbiguousStepError, HookRegistry, StepRegistry, UndefinedStepError, ensureGlobalHooks, globalHookRegistry, globalRegistry, runHooks, runScenario };
158
-
159
- //# sourceMappingURL=runtime.js.map
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 };