@step-forge/step-forge 0.0.20 → 0.0.21

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/dist/runtime.js CHANGED
@@ -1,159 +1,3 @@
1
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
2
+ import { i as runScenario, n as UndefinedStepError, r as compileRegistry, t as AmbiguousStepError } from "./engine-DPRxs6eC.js";
3
+ export { AmbiguousStepError, HookRegistry, StepRegistry, UndefinedStepError, compileRegistry, ensureGlobalHooks, globalHookRegistry, globalRegistry, runHooks, runScenario };
@@ -1,39 +1,10 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- //#region \0rolldown/runtime.js
3
- var __create = Object.create;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
- var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
- key = keys[i];
12
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
- get: ((k) => from[k]).bind(null, key),
14
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
- });
16
- }
17
- return to;
18
- };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
- value: mod,
21
- enumerable: true
22
- }) : target, mod));
23
- //#endregion
2
+ const require_gherkinParser = require("./gherkinParser-CHpkEwii.cjs");
24
3
  const require_hooks = require("./hooks-CGYzwDOv.cjs");
25
4
  let lodash = require("lodash");
26
- lodash = __toESM(lodash, 1);
27
- let node_fs_promises = require("node:fs/promises");
28
- let node_path = require("node:path");
29
- node_path = __toESM(node_path, 1);
5
+ lodash = require_gherkinParser.__toESM(lodash, 1);
30
6
  let typescript = require("typescript");
31
- typescript = __toESM(typescript, 1);
32
- let node_fs = require("node:fs");
33
- node_fs = __toESM(node_fs, 1);
34
- let _cucumber_gherkin = require("@cucumber/gherkin");
35
- let _cucumber_messages = require("@cucumber/messages");
36
- _cucumber_messages = __toESM(_cucumber_messages, 1);
7
+ typescript = require_gherkinParser.__toESM(typescript, 1);
37
8
  //#region src/builderTypeUtils.ts
38
9
  const isString = (statement) => typeof statement === "string";
39
10
  //#endregion
@@ -224,42 +195,6 @@ const thenStatement = () => (statement) => {
224
195
  };
225
196
  const thenBuilder = () => ({ statement: thenStatement() });
226
197
  //#endregion
227
- //#region src/world.ts
228
- function mergeCustomizer(objValue, srcValue) {
229
- if (lodash.default.isArray(objValue)) return objValue.concat(srcValue);
230
- else if (objValue && !lodash.default.isPlainObject(objValue) && objValue !== srcValue) throw new Error(`Merge would have destroyed previous value ${objValue} with ${srcValue}`);
231
- return objValue;
232
- }
233
- var BasicWorld = class {
234
- givenState = {};
235
- whenState = {};
236
- thenState = {};
237
- get given() {
238
- return {
239
- ...this.givenState,
240
- merge: (newState) => {
241
- this.givenState = lodash.default.merge({ ...this.givenState }, newState, mergeCustomizer);
242
- }
243
- };
244
- }
245
- get when() {
246
- return {
247
- ...this.whenState,
248
- merge: (newState) => {
249
- this.whenState = lodash.default.merge({ ...this.whenState }, newState, mergeCustomizer);
250
- }
251
- };
252
- }
253
- get then() {
254
- return {
255
- ...this.thenState,
256
- merge: (newState) => {
257
- this.thenState = lodash.default.merge({ ...this.thenState }, newState, mergeCustomizer);
258
- }
259
- };
260
- }
261
- };
262
- //#endregion
263
198
  //#region src/analyzer/stepExtractor.ts
264
199
  const BUILDER_NAMES = {
265
200
  givenBuilder: "given",
@@ -477,116 +412,6 @@ function resolveReExportedCall(chain, checker) {
477
412
  return null;
478
413
  }
479
414
  //#endregion
480
- //#region src/analyzer/gherkinParser.ts
481
- function parseFeatureFiles(filePaths) {
482
- const scenarios = [];
483
- for (const filePath of filePaths) {
484
- const parsed = parseFeatureContent(node_fs.readFileSync(filePath, "utf-8"), filePath);
485
- scenarios.push(...parsed);
486
- }
487
- return scenarios;
488
- }
489
- function parseFeatureContent(content, filePath) {
490
- const feature = new _cucumber_gherkin.Parser(new _cucumber_gherkin.AstBuilder(_cucumber_messages.IdGenerator.uuid()), new _cucumber_gherkin.GherkinClassicTokenMatcher()).parse(content).feature;
491
- if (!feature) return [];
492
- const featureBackground = [];
493
- const scenarios = [];
494
- const featureTags = tagNames(feature.tags);
495
- for (const child of feature.children) {
496
- if (child.background) featureBackground.push(...child.background.steps);
497
- if (child.scenario) scenarios.push(...expandScenario(child.scenario, featureBackground, filePath, featureTags));
498
- if (child.rule) {
499
- const ruleBackground = [...featureBackground];
500
- const ruleTags = [...featureTags, ...tagNames(child.rule.tags)];
501
- for (const ruleChild of child.rule.children) {
502
- if (ruleChild.background) ruleBackground.push(...ruleChild.background.steps);
503
- if (ruleChild.scenario) scenarios.push(...expandScenario(ruleChild.scenario, ruleBackground, filePath, ruleTags));
504
- }
505
- }
506
- }
507
- return scenarios;
508
- }
509
- /** Extract tag names (each keeping its leading `@`), deduped in order. */
510
- function tagNames(tags) {
511
- return [...new Set((tags ?? []).map((t) => t.name))];
512
- }
513
- function expandScenario(scenario, backgroundSteps, filePath, inheritedTags) {
514
- const scenarioTags = [...inheritedTags, ...tagNames(scenario.tags)];
515
- if (!(scenario.examples.length > 0 && scenario.examples.some((e) => e.tableBody.length > 0))) {
516
- const bgParsed = convertSteps(backgroundSteps);
517
- const scenarioParsed = convertSteps(scenario.steps);
518
- const allSteps = resolveEffectiveKeywords([...bgParsed, ...scenarioParsed]);
519
- return [{
520
- name: scenario.name,
521
- file: filePath,
522
- steps: allSteps,
523
- tags: scenarioTags
524
- }];
525
- }
526
- const results = [];
527
- for (const example of scenario.examples) {
528
- if (!example.tableHeader || example.tableBody.length === 0) continue;
529
- const headers = example.tableHeader.cells.map((c) => c.value);
530
- const exampleTags = [...scenarioTags, ...tagNames(example.tags)];
531
- for (const row of example.tableBody) {
532
- const values = row.cells.map((c) => c.value);
533
- const substitution = {};
534
- headers.forEach((h, i) => {
535
- substitution[h] = values[i];
536
- });
537
- const bgParsed = convertSteps(backgroundSteps);
538
- const scenarioSteps = convertSteps(scenario.steps).map((step) => ({
539
- ...step,
540
- text: substituteExampleValues(step.text, substitution)
541
- }));
542
- const allSteps = resolveEffectiveKeywords([...bgParsed, ...scenarioSteps]);
543
- results.push({
544
- name: headers.map((h, i) => `${h}=${values[i]}`).join(", "),
545
- file: filePath,
546
- steps: allSteps,
547
- tags: exampleTags,
548
- outline: { name: scenario.name }
549
- });
550
- }
551
- }
552
- return results;
553
- }
554
- function convertSteps(steps) {
555
- return steps.map((step) => ({
556
- keyword: normalizeKeyword(step.keyword),
557
- text: step.text,
558
- line: step.location.line,
559
- column: (step.location.column ?? 1) + step.keyword.length
560
- }));
561
- }
562
- function normalizeKeyword(keyword) {
563
- const trimmed = keyword.trim();
564
- if (trimmed === "Given") return "Given";
565
- if (trimmed === "When") return "When";
566
- if (trimmed === "Then") return "Then";
567
- if (trimmed === "And") return "And";
568
- if (trimmed === "But") return "But";
569
- return "Given";
570
- }
571
- function resolveEffectiveKeywords(steps) {
572
- let lastEffective = "Given";
573
- return steps.map((step) => {
574
- let effectiveKeyword;
575
- if (step.keyword === "And" || step.keyword === "But") effectiveKeyword = lastEffective;
576
- else effectiveKeyword = step.keyword;
577
- lastEffective = effectiveKeyword;
578
- return {
579
- ...step,
580
- effectiveKeyword
581
- };
582
- });
583
- }
584
- function substituteExampleValues(text, substitution) {
585
- let result = text;
586
- for (const [key, value] of Object.entries(substitution)) result = result.replace(new RegExp(`<${key}>`, "g"), value);
587
- return result;
588
- }
589
- //#endregion
590
415
  //#region src/analyzer/stepMatcher.ts
591
416
  function compileDefinitions(definitions) {
592
417
  const compiled = [];
@@ -721,12 +546,12 @@ function runRules(rules, scenario, matchedSteps) {
721
546
  //#region src/analyzer/index.ts
722
547
  async function analyze(config, options) {
723
548
  const rules = options?.rules ?? defaultRules;
724
- const stepFilePaths = await resolveGlobs(config.stepFiles);
725
- const featureFilePaths = await resolveGlobs(config.featureFiles);
549
+ const stepFilePaths = await require_gherkinParser.globFiles(config.stepFiles);
550
+ const featureFilePaths = await require_gherkinParser.globFiles(config.featureFiles);
726
551
  if (stepFilePaths.length === 0) return [];
727
552
  if (featureFilePaths.length === 0) return [];
728
553
  const stepDefinitions = extractStepDefinitions(stepFilePaths, config.tsConfigPath);
729
- const scenarios = parseFeatureFiles(featureFilePaths);
554
+ const scenarios = require_gherkinParser.parseFeatureFiles(featureFilePaths);
730
555
  const diagnostics = [];
731
556
  for (const scenario of scenarios) {
732
557
  const scenarioDiags = runRules(rules, scenario, matchScenarioSteps(scenario, stepDefinitions));
@@ -734,11 +559,6 @@ async function analyze(config, options) {
734
559
  }
735
560
  return diagnostics;
736
561
  }
737
- async function resolveGlobs(patterns) {
738
- const files = [];
739
- for (const pattern of patterns) for await (const file of (0, node_fs_promises.glob)(pattern)) files.push(node_path.resolve(file));
740
- return [...new Set(files)];
741
- }
742
562
  //#endregion
743
563
  //#region src/init.ts
744
564
  const createBuilders = () => {
@@ -812,14 +632,14 @@ function afterAll(fn) {
812
632
  const analyzer = {
813
633
  analyze,
814
634
  extractStepDefinitions,
815
- parseFeatureFiles,
816
- parseFeatureContent,
635
+ parseFeatureFiles: require_gherkinParser.parseFeatureFiles,
636
+ parseFeatureContent: require_gherkinParser.parseFeatureContent,
817
637
  matchScenarioSteps,
818
638
  findMatchingDefinitions,
819
639
  defaultRules
820
640
  };
821
641
  //#endregion
822
- exports.BasicWorld = BasicWorld;
642
+ exports.BasicWorld = require_gherkinParser.BasicWorld;
823
643
  exports.afterAll = afterAll;
824
644
  exports.afterFeature = afterFeature;
825
645
  exports.afterScenario = afterScenario;