@step-forge/step-forge 0.0.11 → 0.0.13
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/analyzer-QH03uejK.js +478 -0
- package/dist/analyzer-QH03uejK.js.map +1 -0
- package/dist/analyzer-cli.d.ts +1 -0
- package/dist/analyzer-cli.js +69 -0
- package/dist/analyzer-cli.js.map +1 -0
- package/dist/analyzer.d.ts +73 -0
- package/dist/analyzer.js +2 -0
- package/dist/step-forge.cjs +727 -0
- package/dist/step-forge.cjs.map +1 -0
- package/dist/step-forge.d.cts +326 -0
- package/dist/step-forge.d.ts +326 -0
- package/dist/step-forge.js +695 -0
- package/dist/step-forge.js.map +1 -0
- package/package.json +3 -2
- package/.claude/settings.local.json +0 -18
- package/.eslintignore +0 -6
- package/.eslintrc +0 -18
- package/.prettierignore +0 -6
- package/.prettierrc +0 -15
- package/CLAUDE.md +0 -115
- package/cucumber.mjs +0 -39
- package/docs/assets/state_deps.gif +0 -0
- package/features/TESTING.md +0 -100
- package/features/analyzer/analyzer.feature +0 -110
- package/features/analyzer/fixtures/ambiguous-step.feature +0 -5
- package/features/analyzer/fixtures/missing-given-dep.feature +0 -5
- package/features/analyzer/fixtures/missing-multiple-deps.feature +0 -6
- package/features/analyzer/fixtures/missing-when-dep.feature +0 -5
- package/features/analyzer/fixtures/steps.ts +0 -113
- package/features/analyzer/fixtures/undefined-step.feature +0 -5
- package/features/analyzer/fixtures/undefined-with-missing-dep.feature +0 -5
- package/features/analyzer/fixtures/valid-and-but-keywords.feature +0 -8
- package/features/analyzer/fixtures/valid-deps.feature +0 -6
- package/features/analyzer/fixtures/valid-no-deps.feature +0 -6
- package/features/analyzer/fixtures/valid-optional-deps.feature +0 -6
- package/features/analyzer/fixtures/valid-variables.feature +0 -6
- package/features/basic.feature +0 -21
- package/features/exported.feature +0 -6
- package/features/steps/analyzerSteps.ts +0 -67
- package/features/steps/commonSteps.ts +0 -93
- package/features/steps/exportedSteps.ts +0 -42
- package/features/steps/world.ts +0 -29
- package/src/analyzer/cli.ts +0 -96
- package/src/analyzer/gherkinParser.ts +0 -179
- package/src/analyzer/index.ts +0 -89
- package/src/analyzer/rules/ambiguousStepRule.ts +0 -36
- package/src/analyzer/rules/dependencyRule.ts +0 -71
- package/src/analyzer/rules/index.ts +0 -23
- package/src/analyzer/rules/undefinedStepRule.ts +0 -31
- package/src/analyzer/stepExtractor.ts +0 -432
- package/src/analyzer/stepMatcher.ts +0 -85
- package/src/analyzer/types.ts +0 -55
- package/src/builderTypeUtils.ts +0 -27
- package/src/common.ts +0 -138
- package/src/given.ts +0 -102
- package/src/index.ts +0 -46
- package/src/then.ts +0 -128
- package/src/utils.ts +0 -74
- package/src/when.ts +0 -118
- package/src/world.ts +0 -90
- package/test/givenCompilationTests.ts +0 -130
- package/test/testUtils.ts +0 -30
- package/test/thenCompilationTests.ts +0 -207
- package/test/whenCompilationTests.ts +0 -157
- package/ts-node-esm-register.js +0 -8
- package/tsconfig.cucumber.json +0 -14
- package/tsconfig.json +0 -29
- package/tsdown.config.ts +0 -35
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import { GherkinClassicTokenMatcher, Parser, AstBuilder } from "@cucumber/gherkin";
|
|
3
|
-
import * as messages from "@cucumber/messages";
|
|
4
|
-
import { ParsedScenario, ParsedStep } from "./types.js";
|
|
5
|
-
|
|
6
|
-
type GherkinKeyword = "Given" | "When" | "Then" | "And" | "But";
|
|
7
|
-
|
|
8
|
-
export function parseFeatureFiles(filePaths: string[]): ParsedScenario[] {
|
|
9
|
-
const scenarios: ParsedScenario[] = [];
|
|
10
|
-
|
|
11
|
-
for (const filePath of filePaths) {
|
|
12
|
-
const content = fs.readFileSync(filePath, "utf-8");
|
|
13
|
-
const parsed = parseFeatureContent(content, filePath);
|
|
14
|
-
scenarios.push(...parsed);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return scenarios;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function parseFeatureContent(
|
|
21
|
-
content: string,
|
|
22
|
-
filePath: string
|
|
23
|
-
): ParsedScenario[] {
|
|
24
|
-
const newId = messages.IdGenerator.uuid();
|
|
25
|
-
const builder = new AstBuilder(newId);
|
|
26
|
-
const matcher = new GherkinClassicTokenMatcher();
|
|
27
|
-
const parser = new Parser(builder, matcher);
|
|
28
|
-
|
|
29
|
-
const gherkinDocument: messages.GherkinDocument = parser.parse(content);
|
|
30
|
-
const feature = gherkinDocument.feature;
|
|
31
|
-
if (!feature) return [];
|
|
32
|
-
|
|
33
|
-
// Collect background steps at the feature level
|
|
34
|
-
const featureBackground: messages.Step[] = [];
|
|
35
|
-
const scenarios: ParsedScenario[] = [];
|
|
36
|
-
|
|
37
|
-
for (const child of feature.children) {
|
|
38
|
-
if (child.background) {
|
|
39
|
-
featureBackground.push(...child.background.steps);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (child.scenario) {
|
|
43
|
-
scenarios.push(
|
|
44
|
-
...expandScenario(child.scenario, featureBackground, filePath)
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (child.rule) {
|
|
49
|
-
// Rules can have their own backgrounds
|
|
50
|
-
const ruleBackground: messages.Step[] = [...featureBackground];
|
|
51
|
-
for (const ruleChild of child.rule.children) {
|
|
52
|
-
if (ruleChild.background) {
|
|
53
|
-
ruleBackground.push(...ruleChild.background.steps);
|
|
54
|
-
}
|
|
55
|
-
if (ruleChild.scenario) {
|
|
56
|
-
scenarios.push(
|
|
57
|
-
...expandScenario(ruleChild.scenario, ruleBackground, filePath)
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return scenarios;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function expandScenario(
|
|
68
|
-
scenario: messages.Scenario,
|
|
69
|
-
backgroundSteps: messages.Step[],
|
|
70
|
-
filePath: string
|
|
71
|
-
): ParsedScenario[] {
|
|
72
|
-
const hasExamples =
|
|
73
|
-
scenario.examples.length > 0 &&
|
|
74
|
-
scenario.examples.some((e) => e.tableBody.length > 0);
|
|
75
|
-
|
|
76
|
-
if (!hasExamples) {
|
|
77
|
-
// Regular scenario
|
|
78
|
-
const bgParsed = convertSteps(backgroundSteps);
|
|
79
|
-
const scenarioParsed = convertSteps(scenario.steps);
|
|
80
|
-
const allSteps = resolveEffectiveKeywords([...bgParsed, ...scenarioParsed]);
|
|
81
|
-
|
|
82
|
-
return [
|
|
83
|
-
{
|
|
84
|
-
name: scenario.name,
|
|
85
|
-
file: filePath,
|
|
86
|
-
steps: allSteps,
|
|
87
|
-
},
|
|
88
|
-
];
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Scenario Outline — expand with each example row
|
|
92
|
-
const results: ParsedScenario[] = [];
|
|
93
|
-
for (const example of scenario.examples) {
|
|
94
|
-
if (!example.tableHeader || example.tableBody.length === 0) continue;
|
|
95
|
-
const headers = example.tableHeader.cells.map((c) => c.value);
|
|
96
|
-
|
|
97
|
-
for (const row of example.tableBody) {
|
|
98
|
-
const values = row.cells.map((c) => c.value);
|
|
99
|
-
const substitution: Record<string, string> = {};
|
|
100
|
-
headers.forEach((h, i) => {
|
|
101
|
-
substitution[h] = values[i];
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
const bgParsed = convertSteps(backgroundSteps);
|
|
105
|
-
const scenarioSteps = convertSteps(scenario.steps).map((step) => ({
|
|
106
|
-
...step,
|
|
107
|
-
text: substituteExampleValues(step.text, substitution),
|
|
108
|
-
}));
|
|
109
|
-
const allSteps = resolveEffectiveKeywords([...bgParsed, ...scenarioSteps]);
|
|
110
|
-
|
|
111
|
-
results.push({
|
|
112
|
-
name: `${scenario.name} (${values.join(", ")})`,
|
|
113
|
-
file: filePath,
|
|
114
|
-
steps: allSteps,
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return results;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function convertSteps(
|
|
123
|
-
steps: readonly messages.Step[]
|
|
124
|
-
): Omit<ParsedStep, "effectiveKeyword">[] {
|
|
125
|
-
return steps.map((step) => ({
|
|
126
|
-
keyword: normalizeKeyword(step.keyword),
|
|
127
|
-
text: step.text,
|
|
128
|
-
line: step.location.line,
|
|
129
|
-
// step.location.column points to the keyword start; shift past the
|
|
130
|
-
// keyword (which includes a trailing space) so column points to the
|
|
131
|
-
// start of the step text. This makes `column + text.length` produce
|
|
132
|
-
// the correct end position for diagnostic ranges.
|
|
133
|
-
column: (step.location.column ?? 1) + step.keyword.length,
|
|
134
|
-
}));
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function normalizeKeyword(keyword: string): GherkinKeyword {
|
|
138
|
-
const trimmed = keyword.trim();
|
|
139
|
-
// Gherkin keywords may include trailing space, e.g. "Given "
|
|
140
|
-
if (trimmed === "Given") return "Given";
|
|
141
|
-
if (trimmed === "When") return "When";
|
|
142
|
-
if (trimmed === "Then") return "Then";
|
|
143
|
-
if (trimmed === "And") return "And";
|
|
144
|
-
if (trimmed === "But") return "But";
|
|
145
|
-
// Fallback: treat as Given (shouldn't happen with valid Gherkin)
|
|
146
|
-
return "Given";
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function resolveEffectiveKeywords(
|
|
150
|
-
steps: Omit<ParsedStep, "effectiveKeyword">[]
|
|
151
|
-
): ParsedStep[] {
|
|
152
|
-
let lastEffective: "Given" | "When" | "Then" = "Given";
|
|
153
|
-
|
|
154
|
-
return steps.map((step) => {
|
|
155
|
-
let effectiveKeyword: "Given" | "When" | "Then";
|
|
156
|
-
if (step.keyword === "And" || step.keyword === "But") {
|
|
157
|
-
effectiveKeyword = lastEffective;
|
|
158
|
-
} else {
|
|
159
|
-
effectiveKeyword = step.keyword as "Given" | "When" | "Then";
|
|
160
|
-
}
|
|
161
|
-
lastEffective = effectiveKeyword;
|
|
162
|
-
|
|
163
|
-
return {
|
|
164
|
-
...step,
|
|
165
|
-
effectiveKeyword,
|
|
166
|
-
};
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function substituteExampleValues(
|
|
171
|
-
text: string,
|
|
172
|
-
substitution: Record<string, string>
|
|
173
|
-
): string {
|
|
174
|
-
let result = text;
|
|
175
|
-
for (const [key, value] of Object.entries(substitution)) {
|
|
176
|
-
result = result.replace(new RegExp(`<${key}>`, "g"), value);
|
|
177
|
-
}
|
|
178
|
-
return result;
|
|
179
|
-
}
|
package/src/analyzer/index.ts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { glob } from "node:fs/promises";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import { extractStepDefinitions } from "./stepExtractor.js";
|
|
4
|
-
import { parseFeatureFiles, parseFeatureContent } from "./gherkinParser.js";
|
|
5
|
-
import {
|
|
6
|
-
matchScenarioSteps,
|
|
7
|
-
findMatchingDefinitions,
|
|
8
|
-
} from "./stepMatcher.js";
|
|
9
|
-
import { defaultRules, runRules } from "./rules/index.js";
|
|
10
|
-
import type {
|
|
11
|
-
AnalyzerConfig,
|
|
12
|
-
AnalysisRule,
|
|
13
|
-
Diagnostic,
|
|
14
|
-
StepDefinitionMeta,
|
|
15
|
-
ParsedScenario,
|
|
16
|
-
MatchedStep,
|
|
17
|
-
ParsedStep,
|
|
18
|
-
} from "./types.js";
|
|
19
|
-
|
|
20
|
-
export type {
|
|
21
|
-
AnalyzerConfig,
|
|
22
|
-
AnalysisRule,
|
|
23
|
-
Diagnostic,
|
|
24
|
-
StepDefinitionMeta,
|
|
25
|
-
ParsedScenario,
|
|
26
|
-
MatchedStep,
|
|
27
|
-
ParsedStep,
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export {
|
|
31
|
-
extractStepDefinitions,
|
|
32
|
-
parseFeatureFiles,
|
|
33
|
-
parseFeatureContent,
|
|
34
|
-
matchScenarioSteps,
|
|
35
|
-
findMatchingDefinitions,
|
|
36
|
-
defaultRules,
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export interface AnalyzeOptions {
|
|
40
|
-
rules?: AnalysisRule[];
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export async function analyze(
|
|
44
|
-
config: AnalyzerConfig,
|
|
45
|
-
options?: AnalyzeOptions
|
|
46
|
-
): Promise<Diagnostic[]> {
|
|
47
|
-
const rules = options?.rules ?? defaultRules;
|
|
48
|
-
|
|
49
|
-
// 1. Resolve file globs to paths
|
|
50
|
-
const stepFilePaths = await resolveGlobs(config.stepFiles);
|
|
51
|
-
const featureFilePaths = await resolveGlobs(config.featureFiles);
|
|
52
|
-
|
|
53
|
-
if (stepFilePaths.length === 0) {
|
|
54
|
-
return [];
|
|
55
|
-
}
|
|
56
|
-
if (featureFilePaths.length === 0) {
|
|
57
|
-
return [];
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// 2. Extract step metadata from step files
|
|
61
|
-
const stepDefinitions = extractStepDefinitions(
|
|
62
|
-
stepFilePaths,
|
|
63
|
-
config.tsConfigPath
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
// 3. Parse feature files
|
|
67
|
-
const scenarios = parseFeatureFiles(featureFilePaths);
|
|
68
|
-
|
|
69
|
-
// 4. For each scenario, match steps and run rules
|
|
70
|
-
const diagnostics: Diagnostic[] = [];
|
|
71
|
-
for (const scenario of scenarios) {
|
|
72
|
-
const matchedSteps = matchScenarioSteps(scenario, stepDefinitions);
|
|
73
|
-
const scenarioDiags = runRules(rules, scenario, matchedSteps);
|
|
74
|
-
diagnostics.push(...scenarioDiags);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return diagnostics;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
async function resolveGlobs(patterns: string[]): Promise<string[]> {
|
|
81
|
-
const files: string[] = [];
|
|
82
|
-
for (const pattern of patterns) {
|
|
83
|
-
for await (const file of glob(pattern)) {
|
|
84
|
-
files.push(path.resolve(file));
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
// Deduplicate
|
|
88
|
-
return [...new Set(files)];
|
|
89
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AnalysisRule,
|
|
3
|
-
Diagnostic,
|
|
4
|
-
MatchedStep,
|
|
5
|
-
ParsedScenario,
|
|
6
|
-
} from "../types.js";
|
|
7
|
-
|
|
8
|
-
export const ambiguousStepRule: AnalysisRule = {
|
|
9
|
-
name: "ambiguous-step",
|
|
10
|
-
|
|
11
|
-
check(
|
|
12
|
-
scenario: ParsedScenario,
|
|
13
|
-
matchedSteps: MatchedStep[]
|
|
14
|
-
): Diagnostic[] {
|
|
15
|
-
return matchedSteps
|
|
16
|
-
.filter((step) => step.definitions.length > 1)
|
|
17
|
-
.map((step) => {
|
|
18
|
-
const locations = step.definitions
|
|
19
|
-
.map((d) => `${d.sourceFile}:${d.line}`)
|
|
20
|
-
.join(", ");
|
|
21
|
-
return {
|
|
22
|
-
file: scenario.file,
|
|
23
|
-
range: {
|
|
24
|
-
startLine: step.line,
|
|
25
|
-
startColumn: step.column,
|
|
26
|
-
endLine: step.line,
|
|
27
|
-
endColumn: step.column + step.text.length,
|
|
28
|
-
},
|
|
29
|
-
severity: "error" as const,
|
|
30
|
-
message: `Step "${step.text}" matches multiple step definitions: ${locations}`,
|
|
31
|
-
rule: "ambiguous-step",
|
|
32
|
-
source: "step-forge" as const,
|
|
33
|
-
};
|
|
34
|
-
});
|
|
35
|
-
},
|
|
36
|
-
};
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AnalysisRule,
|
|
3
|
-
Diagnostic,
|
|
4
|
-
MatchedStep,
|
|
5
|
-
ParsedScenario,
|
|
6
|
-
} from "../types.js";
|
|
7
|
-
|
|
8
|
-
export const dependencyRule: AnalysisRule = {
|
|
9
|
-
name: "dependency-check",
|
|
10
|
-
|
|
11
|
-
check(
|
|
12
|
-
scenario: ParsedScenario,
|
|
13
|
-
matchedSteps: MatchedStep[]
|
|
14
|
-
): Diagnostic[] {
|
|
15
|
-
const diagnostics: Diagnostic[] = [];
|
|
16
|
-
const produced = {
|
|
17
|
-
given: new Set<string>(),
|
|
18
|
-
when: new Set<string>(),
|
|
19
|
-
then: new Set<string>(),
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
for (const step of matchedSteps) {
|
|
23
|
-
if (step.definitions.length !== 1) continue;
|
|
24
|
-
|
|
25
|
-
const { dependencies, produces, stepType } = step.definitions[0];
|
|
26
|
-
|
|
27
|
-
// Collect all missing required dependencies for this step
|
|
28
|
-
const missing: Record<string, string[]> = {};
|
|
29
|
-
for (const phase of ["given", "when", "then"] as const) {
|
|
30
|
-
for (const [key, requirement] of Object.entries(dependencies[phase])) {
|
|
31
|
-
if (requirement === "required" && !produced[phase].has(key)) {
|
|
32
|
-
if (!missing[phase]) {
|
|
33
|
-
missing[phase] = [];
|
|
34
|
-
}
|
|
35
|
-
missing[phase].push(key);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (Object.keys(missing).length > 0) {
|
|
41
|
-
const lines = Object.entries(missing).map(
|
|
42
|
-
([phase, keys]) =>
|
|
43
|
-
`${phase.charAt(0).toUpperCase() + phase.slice(1)}: ${keys.map((k) => `${phase}.${k}`).join(", ")}`
|
|
44
|
-
);
|
|
45
|
-
const message =
|
|
46
|
-
"Missing required dependencies:\n" + lines.join("\n");
|
|
47
|
-
|
|
48
|
-
diagnostics.push({
|
|
49
|
-
file: scenario.file,
|
|
50
|
-
range: {
|
|
51
|
-
startLine: step.line,
|
|
52
|
-
startColumn: step.column,
|
|
53
|
-
endLine: step.line,
|
|
54
|
-
endColumn: step.column + step.text.length,
|
|
55
|
-
},
|
|
56
|
-
severity: "error",
|
|
57
|
-
message,
|
|
58
|
-
rule: "dependency-check",
|
|
59
|
-
source: "step-forge",
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Add produced keys for this step
|
|
64
|
-
for (const key of produces) {
|
|
65
|
-
produced[stepType].add(key);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return diagnostics;
|
|
70
|
-
},
|
|
71
|
-
};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AnalysisRule,
|
|
3
|
-
Diagnostic,
|
|
4
|
-
MatchedStep,
|
|
5
|
-
ParsedScenario,
|
|
6
|
-
} from "../types.js";
|
|
7
|
-
import { ambiguousStepRule } from "./ambiguousStepRule.js";
|
|
8
|
-
import { dependencyRule } from "./dependencyRule.js";
|
|
9
|
-
import { undefinedStepRule } from "./undefinedStepRule.js";
|
|
10
|
-
|
|
11
|
-
export const defaultRules: AnalysisRule[] = [
|
|
12
|
-
undefinedStepRule,
|
|
13
|
-
ambiguousStepRule,
|
|
14
|
-
dependencyRule,
|
|
15
|
-
];
|
|
16
|
-
|
|
17
|
-
export function runRules(
|
|
18
|
-
rules: AnalysisRule[],
|
|
19
|
-
scenario: ParsedScenario,
|
|
20
|
-
matchedSteps: MatchedStep[]
|
|
21
|
-
): Diagnostic[] {
|
|
22
|
-
return rules.flatMap((rule) => rule.check(scenario, matchedSteps));
|
|
23
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AnalysisRule,
|
|
3
|
-
Diagnostic,
|
|
4
|
-
MatchedStep,
|
|
5
|
-
ParsedScenario,
|
|
6
|
-
} from "../types.js";
|
|
7
|
-
|
|
8
|
-
export const undefinedStepRule: AnalysisRule = {
|
|
9
|
-
name: "undefined-step",
|
|
10
|
-
|
|
11
|
-
check(
|
|
12
|
-
scenario: ParsedScenario,
|
|
13
|
-
matchedSteps: MatchedStep[]
|
|
14
|
-
): Diagnostic[] {
|
|
15
|
-
return matchedSteps
|
|
16
|
-
.filter((step) => step.definitions.length === 0)
|
|
17
|
-
.map((step) => ({
|
|
18
|
-
file: scenario.file,
|
|
19
|
-
range: {
|
|
20
|
-
startLine: step.line,
|
|
21
|
-
startColumn: step.column,
|
|
22
|
-
endLine: step.line,
|
|
23
|
-
endColumn: step.column + step.text.length,
|
|
24
|
-
},
|
|
25
|
-
severity: "error" as const,
|
|
26
|
-
message: `Step "${step.text}" does not match any step definition`,
|
|
27
|
-
rule: "undefined-step",
|
|
28
|
-
source: "step-forge" as const,
|
|
29
|
-
}));
|
|
30
|
-
},
|
|
31
|
-
};
|