@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.
- package/README.md +2 -0
- package/RUNTIME.md +242 -0
- package/dist/{analyzer-DJyJbU_V.js → analyzer-byS8yRrY.js} +202 -34
- 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 -2
- package/dist/cli.cjs +525 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +526 -0
- package/dist/cli.js.map +1 -0
- 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-DPVLEHBi.js +163 -0
- package/dist/engine-DPVLEHBi.js.map +1 -0
- package/dist/engine-vqA-eL_T.cjs +186 -0
- package/dist/engine-vqA-eL_T.cjs.map +1 -0
- package/dist/gherkinParser-BT40q_i3.cjs +338 -0
- package/dist/gherkinParser-BT40q_i3.cjs.map +1 -0
- package/dist/gherkinParser-NcttZgN4.js +259 -0
- 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 +7 -162
- package/dist/runtime.d.cts +44 -8
- package/dist/runtime.d.ts +44 -8
- package/dist/runtime.js +3 -159
- package/dist/step-forge.cjs +73 -216
- package/dist/step-forge.cjs.map +1 -1
- package/dist/step-forge.d.cts +19 -10
- package/dist/step-forge.d.ts +19 -10
- package/dist/step-forge.js +67 -185
- package/dist/step-forge.js.map +1 -1
- package/package.json +12 -18
- package/dist/analyzer-DJyJbU_V.js.map +0 -1
- package/dist/gherkinParser-Dp2d7JNr.js +0 -116
- package/dist/gherkinParser-Dp2d7JNr.js.map +0 -1
- package/dist/hooks-CywugMQQ.js +0 -82
- package/dist/runtime.cjs.map +0 -1
- package/dist/runtime.js.map +0 -1
- package/dist/vitest.d.ts +0 -74
- package/dist/vitest.js +0 -136
- 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-
|
|
3
|
-
|
|
4
|
-
|
|
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.
|
|
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.
|
|
167
|
-
|
|
168
|
-
//# sourceMappingURL=runtime.cjs.map
|
|
12
|
+
exports.runHooksParallel = require_hooks.runHooksParallel;
|
|
13
|
+
exports.runScenario = require_engine.runScenario;
|
package/dist/runtime.d.cts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
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
|
+
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
|
|
60
|
-
* (state never leaks between scenarios). On the first failing step
|
|
61
|
-
* remaining steps are marked skipped, matching Cucumber's execution
|
|
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
|
-
*
|
|
64
|
-
*
|
|
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,
|
|
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,
|
|
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 {
|
|
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
|
|
60
|
-
* (state never leaks between scenarios). On the first failing step
|
|
61
|
-
* remaining steps are marked skipped, matching Cucumber's execution
|
|
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
|
-
*
|
|
64
|
-
*
|
|
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,
|
|
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,
|
|
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
|
|
2
|
-
import {
|
|
3
|
-
|
|
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 };
|