@simplysf/simply-utam-core 0.0.0 → 0.1.0
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/LICENSE.txt +206 -0
- package/README.md +98 -12
- package/lib/discovery/index.d.ts +1 -0
- package/lib/discovery/index.js +17 -0
- package/lib/discovery/index.js.map +1 -0
- package/lib/discovery/project-discovery.d.ts +55 -0
- package/lib/discovery/project-discovery.js +180 -0
- package/lib/discovery/project-discovery.js.map +1 -0
- package/lib/index.d.ts +8 -2
- package/lib/index.js +8 -2
- package/lib/index.js.map +1 -1
- package/lib/namespaces/index.d.ts +1 -0
- package/lib/namespaces/index.js +17 -0
- package/lib/namespaces/index.js.map +1 -0
- package/lib/namespaces/namespace-rewriter.d.ts +50 -0
- package/lib/namespaces/namespace-rewriter.js +223 -0
- package/lib/namespaces/namespace-rewriter.js.map +1 -0
- package/lib/navigator.d.ts +133 -5
- package/lib/navigator.js +224 -12
- package/lib/navigator.js.map +1 -1
- package/lib/overrides/index.d.ts +1 -0
- package/lib/overrides/index.js +17 -0
- package/lib/overrides/index.js.map +1 -0
- package/lib/overrides/overrides-merger.d.ts +63 -0
- package/lib/overrides/overrides-merger.js +280 -0
- package/lib/overrides/overrides-merger.js.map +1 -0
- package/lib/rules/index.d.ts +1 -0
- package/lib/rules/index.js +17 -0
- package/lib/rules/index.js.map +1 -0
- package/lib/rules/rules-generator.d.ts +49 -0
- package/lib/rules/rules-generator.js +199 -0
- package/lib/rules/rules-generator.js.map +1 -0
- package/lib/scaffold/index.d.ts +1 -0
- package/lib/scaffold/index.js +17 -0
- package/lib/scaffold/index.js.map +1 -0
- package/lib/scaffold/project-scaffolder.d.ts +148 -0
- package/lib/scaffold/project-scaffolder.js +313 -0
- package/lib/scaffold/project-scaffolder.js.map +1 -0
- package/lib/steps/expression-matcher.d.ts +42 -0
- package/lib/steps/expression-matcher.js +106 -0
- package/lib/steps/expression-matcher.js.map +1 -0
- package/lib/steps/gherkin-parser.d.ts +27 -0
- package/lib/steps/gherkin-parser.js +98 -0
- package/lib/steps/gherkin-parser.js.map +1 -0
- package/lib/steps/index.d.ts +4 -0
- package/lib/steps/index.js +20 -0
- package/lib/steps/index.js.map +1 -0
- package/lib/steps/snippet-generator.d.ts +34 -0
- package/lib/steps/snippet-generator.js +101 -0
- package/lib/steps/snippet-generator.js.map +1 -0
- package/lib/steps/step-scaffolder.d.ts +49 -0
- package/lib/steps/step-scaffolder.js +225 -0
- package/lib/steps/step-scaffolder.js.map +1 -0
- package/lib/test-environment.d.ts +99 -4
- package/lib/test-environment.js +302 -4
- package/lib/test-environment.js.map +1 -1
- package/package.json +34 -4
- package/templates/generator.config.json +13 -0
- package/templates/hello.feature +4 -0
- package/templates/hello.steps.mjs +7 -0
- package/templates/namespace-map.json +3 -0
- package/templates/utam.config.json +9 -0
- package/templates/wdio.conf.mjs +68 -0
- package/templates/wireit.json +37 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ParameterTypeRegistry } from '@cucumber/cucumber-expressions';
|
|
2
|
+
import { ParsedGherkinStep, StepSemanticKeyword } from './gherkin-parser.js';
|
|
3
|
+
export interface GeneratedStepSnippet {
|
|
4
|
+
keyword: StepSemanticKeyword;
|
|
5
|
+
expression: string;
|
|
6
|
+
parameters: string[];
|
|
7
|
+
fullText: string;
|
|
8
|
+
featureFile: string;
|
|
9
|
+
stepText: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Formats parameter names into clean, non-conflicting JavaScript identifier names.
|
|
13
|
+
*
|
|
14
|
+
* @param names - Raw parameter names from Cucumber expression generator.
|
|
15
|
+
* @param hasDataTable - Whether the step includes a Gherkin data table.
|
|
16
|
+
* @param hasDocString - Whether the step includes a Gherkin doc string.
|
|
17
|
+
* @returns Array of sanitized unique identifier names.
|
|
18
|
+
*/
|
|
19
|
+
export declare function formatParameterNames(names: readonly string[], hasDataTable?: boolean, hasDocString?: boolean): string[];
|
|
20
|
+
/**
|
|
21
|
+
* Generates an idiomatic Cucumber step definition snippet using CucumberExpressionGenerator.
|
|
22
|
+
*
|
|
23
|
+
* @param step - Parsed Gherkin step.
|
|
24
|
+
* @param registry - Optional Cucumber parameter type registry.
|
|
25
|
+
* @returns Generated snippet object with expression and implementation template.
|
|
26
|
+
*/
|
|
27
|
+
export declare function generateStepSnippet(step: ParsedGherkinStep, registry?: ParameterTypeRegistry): GeneratedStepSnippet;
|
|
28
|
+
/**
|
|
29
|
+
* Formats generated JavaScript/TypeScript code using Prettier.
|
|
30
|
+
*
|
|
31
|
+
* @param code - Raw code string to format.
|
|
32
|
+
* @returns Formatted code string.
|
|
33
|
+
*/
|
|
34
|
+
export declare function formatCode(code: string): Promise<string>;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2026, SimplySF.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { ParameterTypeRegistry, CucumberExpressionGenerator } from '@cucumber/cucumber-expressions';
|
|
17
|
+
import prettier from 'prettier';
|
|
18
|
+
/**
|
|
19
|
+
* Formats parameter names into clean, non-conflicting JavaScript identifier names.
|
|
20
|
+
*
|
|
21
|
+
* @param names - Raw parameter names from Cucumber expression generator.
|
|
22
|
+
* @param hasDataTable - Whether the step includes a Gherkin data table.
|
|
23
|
+
* @param hasDocString - Whether the step includes a Gherkin doc string.
|
|
24
|
+
* @returns Array of sanitized unique identifier names.
|
|
25
|
+
*/
|
|
26
|
+
export function formatParameterNames(names, hasDataTable = false, hasDocString = false) {
|
|
27
|
+
const result = [];
|
|
28
|
+
const counts = {};
|
|
29
|
+
for (const name of names) {
|
|
30
|
+
const sanitized = name.replace(/[^a-zA-Z0-9_$]/g, '') || 'arg';
|
|
31
|
+
counts[sanitized] = (counts[sanitized] || 0) + 1;
|
|
32
|
+
}
|
|
33
|
+
const seen = {};
|
|
34
|
+
for (const name of names) {
|
|
35
|
+
const sanitized = name.replace(/[^a-zA-Z0-9_$]/g, '') || 'arg';
|
|
36
|
+
if (counts[sanitized] > 1) {
|
|
37
|
+
seen[sanitized] = (seen[sanitized] || 0) + 1;
|
|
38
|
+
result.push(`${sanitized}${seen[sanitized]}`);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
result.push(sanitized);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (hasDataTable) {
|
|
45
|
+
result.push('dataTable');
|
|
46
|
+
}
|
|
47
|
+
if (hasDocString) {
|
|
48
|
+
result.push('docString');
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Generates an idiomatic Cucumber step definition snippet using CucumberExpressionGenerator.
|
|
54
|
+
*
|
|
55
|
+
* @param step - Parsed Gherkin step.
|
|
56
|
+
* @param registry - Optional Cucumber parameter type registry.
|
|
57
|
+
* @returns Generated snippet object with expression and implementation template.
|
|
58
|
+
*/
|
|
59
|
+
export function generateStepSnippet(step, registry = new ParameterTypeRegistry()) {
|
|
60
|
+
const generator = new CucumberExpressionGenerator(() => registry.parameterTypes);
|
|
61
|
+
const generatedList = generator.generateExpressions(step.text);
|
|
62
|
+
let expressionSource = step.text;
|
|
63
|
+
let rawParamNames = [];
|
|
64
|
+
if (generatedList.length > 0) {
|
|
65
|
+
const bestMatch = generatedList[0];
|
|
66
|
+
expressionSource = bestMatch.source;
|
|
67
|
+
rawParamNames = bestMatch.parameterNames;
|
|
68
|
+
}
|
|
69
|
+
const parameters = formatParameterNames(rawParamNames, step.hasDataTable, step.hasDocString);
|
|
70
|
+
const escapedExpression = expressionSource.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
71
|
+
const fullText = `${step.keyword}('${escapedExpression}', async (${parameters.join(', ')}) => {\n // TODO: Implement step logic\n console.warn('[STEP NOT IMPLEMENTED]: ${escapedExpression}');\n});`;
|
|
72
|
+
return {
|
|
73
|
+
keyword: step.keyword,
|
|
74
|
+
expression: expressionSource,
|
|
75
|
+
parameters,
|
|
76
|
+
fullText,
|
|
77
|
+
featureFile: step.filePath,
|
|
78
|
+
stepText: step.text,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Formats generated JavaScript/TypeScript code using Prettier.
|
|
83
|
+
*
|
|
84
|
+
* @param code - Raw code string to format.
|
|
85
|
+
* @returns Formatted code string.
|
|
86
|
+
*/
|
|
87
|
+
export async function formatCode(code) {
|
|
88
|
+
try {
|
|
89
|
+
return await prettier.format(code, {
|
|
90
|
+
parser: 'typescript',
|
|
91
|
+
singleQuote: true,
|
|
92
|
+
semi: true,
|
|
93
|
+
trailingComma: 'all',
|
|
94
|
+
printWidth: 100,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return code;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=snippet-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snippet-generator.js","sourceRoot":"","sources":["../../src/steps/snippet-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { GeneratedStepSnippet } from './snippet-generator.js';
|
|
2
|
+
export interface GenerateStepsOptions {
|
|
3
|
+
rootDir?: string;
|
|
4
|
+
features?: string | string[];
|
|
5
|
+
steps?: string | string[];
|
|
6
|
+
outputFile?: string;
|
|
7
|
+
dryRun?: boolean;
|
|
8
|
+
verbose?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface GenerateStepsResult {
|
|
11
|
+
featureFilesScanned: number;
|
|
12
|
+
stepFilesScanned: number;
|
|
13
|
+
totalFeatureSteps: number;
|
|
14
|
+
matchedStepsCount: number;
|
|
15
|
+
undefinedStepsCount: number;
|
|
16
|
+
uniqueSnippetsGenerated: number;
|
|
17
|
+
dryRun: boolean;
|
|
18
|
+
outputFile: string;
|
|
19
|
+
snippets: GeneratedStepSnippet[];
|
|
20
|
+
generatedCode?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Resolves one or more glob patterns or file paths into absolute file paths.
|
|
24
|
+
*
|
|
25
|
+
* @param patterns - Pattern or list of glob patterns to resolve.
|
|
26
|
+
* @param rootDir - Root directory to resolve relative paths against.
|
|
27
|
+
* @returns Array of unique resolved absolute file paths.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveGlobs(patterns: string | string[], rootDir: string): string[];
|
|
30
|
+
/**
|
|
31
|
+
* Determines the default output target file path for scaffolded step definitions.
|
|
32
|
+
*
|
|
33
|
+
* If existing step definition files are found, places generated.steps.mjs in the same directory.
|
|
34
|
+
* Otherwise, inspects project discovery for package directories (e.g. sfdx-source, force-app)
|
|
35
|
+
* and checks candidate test/utam directory locations.
|
|
36
|
+
*
|
|
37
|
+
* @param rootDir - Root directory of the project.
|
|
38
|
+
* @param stepFiles - List of resolved existing step definition file paths.
|
|
39
|
+
* @returns Absolute path to target generated.steps.mjs file.
|
|
40
|
+
*/
|
|
41
|
+
export declare function resolveDefaultTargetOutput(rootDir: string, stepFiles?: readonly string[]): string;
|
|
42
|
+
/**
|
|
43
|
+
* Scans feature files, discovers undefined Gherkin steps against existing step definition files,
|
|
44
|
+
* deduplicates snippets by expression, and non-destructively scaffolds new step definitions.
|
|
45
|
+
*
|
|
46
|
+
* @param options - Step generation configuration options.
|
|
47
|
+
* @returns Summary of scanned features, matched steps, and generated snippet definitions.
|
|
48
|
+
*/
|
|
49
|
+
export declare function generateCucumberSteps(options?: GenerateStepsOptions): Promise<GenerateStepsResult>;
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2026, SimplySF.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import fs from 'node:fs';
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
import { ParameterTypeRegistry } from '@cucumber/cucumber-expressions';
|
|
19
|
+
import { discoverProject } from '../discovery/index.js';
|
|
20
|
+
import { extractStepsFromGherkin } from './gherkin-parser.js';
|
|
21
|
+
import { extractStepExpressionsFromContent, compileStepExpressions, matchStepAgainstExpressions, normalizeExpression, } from './expression-matcher.js';
|
|
22
|
+
import { generateStepSnippet, formatCode } from './snippet-generator.js';
|
|
23
|
+
/**
|
|
24
|
+
* Resolves one or more glob patterns or file paths into absolute file paths.
|
|
25
|
+
*
|
|
26
|
+
* @param patterns - Pattern or list of glob patterns to resolve.
|
|
27
|
+
* @param rootDir - Root directory to resolve relative paths against.
|
|
28
|
+
* @returns Array of unique resolved absolute file paths.
|
|
29
|
+
*/
|
|
30
|
+
export function resolveGlobs(patterns, rootDir) {
|
|
31
|
+
const files = [];
|
|
32
|
+
const list = Array.isArray(patterns) ? patterns : [patterns];
|
|
33
|
+
for (const pattern of list) {
|
|
34
|
+
if (!pattern)
|
|
35
|
+
continue;
|
|
36
|
+
const normalized = pattern.replace(/\\/g, '/');
|
|
37
|
+
try {
|
|
38
|
+
const matched = fs.globSync(normalized, { cwd: rootDir });
|
|
39
|
+
for (const m of matched) {
|
|
40
|
+
files.push(path.resolve(rootDir, m));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// If globSync fails, check if pattern is a direct file path
|
|
45
|
+
const direct = path.resolve(rootDir, pattern);
|
|
46
|
+
if (fs.existsSync(direct)) {
|
|
47
|
+
files.push(direct);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return Array.from(new Set(files));
|
|
52
|
+
}
|
|
53
|
+
function collectExistingExpressions(stepFiles, targetOutput) {
|
|
54
|
+
const rawExpressions = [];
|
|
55
|
+
for (const stepFile of stepFiles) {
|
|
56
|
+
try {
|
|
57
|
+
if (fs.existsSync(stepFile)) {
|
|
58
|
+
const content = fs.readFileSync(stepFile, 'utf-8');
|
|
59
|
+
rawExpressions.push(...extractStepExpressionsFromContent(content));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Ignore unreadable step files
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (fs.existsSync(targetOutput)) {
|
|
67
|
+
try {
|
|
68
|
+
const content = fs.readFileSync(targetOutput, 'utf-8');
|
|
69
|
+
rawExpressions.push(...extractStepExpressionsFromContent(content));
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Ignore read errors
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const registry = new ParameterTypeRegistry();
|
|
76
|
+
const compiledExpressions = compileStepExpressions(rawExpressions, registry);
|
|
77
|
+
const existingNormalizedExpressions = new Set();
|
|
78
|
+
for (const raw of rawExpressions) {
|
|
79
|
+
existingNormalizedExpressions.add(normalizeExpression(raw.pattern));
|
|
80
|
+
}
|
|
81
|
+
return { compiledExpressions, existingNormalizedExpressions, registry };
|
|
82
|
+
}
|
|
83
|
+
function collectStepsFromFeatures(featureFiles, rootDir) {
|
|
84
|
+
const allParsedSteps = [];
|
|
85
|
+
for (const featureFile of featureFiles) {
|
|
86
|
+
try {
|
|
87
|
+
const content = fs.readFileSync(featureFile, 'utf-8');
|
|
88
|
+
const relativePath = path.relative(rootDir, featureFile);
|
|
89
|
+
const steps = extractStepsFromGherkin(content, relativePath);
|
|
90
|
+
allParsedSteps.push(...steps);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// Ignore unreadable feature files
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return allParsedSteps;
|
|
97
|
+
}
|
|
98
|
+
async function writeScaffoldedSnippets(uniqueSnippets, targetOutput, dryRun) {
|
|
99
|
+
if (uniqueSnippets.length === 0) {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
let fileContent = fs.existsSync(targetOutput)
|
|
103
|
+
? fs.readFileSync(targetOutput, 'utf-8').trim() + '\n\n'
|
|
104
|
+
: "import { Given, When, Then } from '@wdio/cucumber-framework';\n\n";
|
|
105
|
+
for (const snippet of uniqueSnippets) {
|
|
106
|
+
fileContent += snippet.fullText + '\n\n';
|
|
107
|
+
}
|
|
108
|
+
const generatedCode = await formatCode(fileContent);
|
|
109
|
+
if (!dryRun) {
|
|
110
|
+
const parentDir = path.dirname(targetOutput);
|
|
111
|
+
if (!fs.existsSync(parentDir)) {
|
|
112
|
+
fs.mkdirSync(parentDir, { recursive: true });
|
|
113
|
+
}
|
|
114
|
+
fs.writeFileSync(targetOutput, generatedCode, 'utf-8');
|
|
115
|
+
}
|
|
116
|
+
return generatedCode;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Determines the default output target file path for scaffolded step definitions.
|
|
120
|
+
*
|
|
121
|
+
* If existing step definition files are found, places generated.steps.mjs in the same directory.
|
|
122
|
+
* Otherwise, inspects project discovery for package directories (e.g. sfdx-source, force-app)
|
|
123
|
+
* and checks candidate test/utam directory locations.
|
|
124
|
+
*
|
|
125
|
+
* @param rootDir - Root directory of the project.
|
|
126
|
+
* @param stepFiles - List of resolved existing step definition file paths.
|
|
127
|
+
* @returns Absolute path to target generated.steps.mjs file.
|
|
128
|
+
*/
|
|
129
|
+
export function resolveDefaultTargetOutput(rootDir, stepFiles = []) {
|
|
130
|
+
if (stepFiles.length > 0) {
|
|
131
|
+
const primaryDir = path.dirname(stepFiles[0]);
|
|
132
|
+
return path.join(primaryDir, 'generated.steps.mjs');
|
|
133
|
+
}
|
|
134
|
+
const discovery = discoverProject(rootDir);
|
|
135
|
+
const sourceDir = discovery.defaultPackageDirectory ?? discovery.packageDirectories[0] ?? 'force-app';
|
|
136
|
+
const candidateDirs = [
|
|
137
|
+
path.join(rootDir, sourceDir, 'test', 'utam', 'step_definitions'),
|
|
138
|
+
path.join(rootDir, sourceDir, 'tests', 'utam', 'step_definitions'),
|
|
139
|
+
path.join(rootDir, sourceDir, 'test', 'utam'),
|
|
140
|
+
path.join(rootDir, sourceDir, 'tests', 'utam'),
|
|
141
|
+
path.join(rootDir, 'test', 'utam', 'step_definitions'),
|
|
142
|
+
path.join(rootDir, 'tests', 'utam', 'step_definitions'),
|
|
143
|
+
path.join(rootDir, 'test', 'utam'),
|
|
144
|
+
path.join(rootDir, 'tests', 'utam'),
|
|
145
|
+
];
|
|
146
|
+
for (const dir of candidateDirs) {
|
|
147
|
+
if (fs.existsSync(dir)) {
|
|
148
|
+
if (dir.endsWith('step_definitions')) {
|
|
149
|
+
return path.join(dir, 'generated.steps.mjs');
|
|
150
|
+
}
|
|
151
|
+
return path.join(dir, 'step_definitions', 'generated.steps.mjs');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return path.resolve(rootDir, sourceDir, 'test/utam/step_definitions/generated.steps.mjs');
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Scans feature files, discovers undefined Gherkin steps against existing step definition files,
|
|
158
|
+
* deduplicates snippets by expression, and non-destructively scaffolds new step definitions.
|
|
159
|
+
*
|
|
160
|
+
* @param options - Step generation configuration options.
|
|
161
|
+
* @returns Summary of scanned features, matched steps, and generated snippet definitions.
|
|
162
|
+
*/
|
|
163
|
+
export async function generateCucumberSteps(options = {}) {
|
|
164
|
+
const rootDir = path.resolve(options.rootDir ?? process.cwd());
|
|
165
|
+
const dryRun = Boolean(options.dryRun);
|
|
166
|
+
const featurePatterns = options.features ?? [
|
|
167
|
+
'sfdx-source/**/*.feature',
|
|
168
|
+
'force-app/**/*.feature',
|
|
169
|
+
'test/**/*.feature',
|
|
170
|
+
'**/*.feature',
|
|
171
|
+
];
|
|
172
|
+
const stepPatterns = options.steps ?? [
|
|
173
|
+
'sfdx-source/**/*.steps.{js,mjs,ts}',
|
|
174
|
+
'force-app/**/*.steps.{js,mjs,ts}',
|
|
175
|
+
'test/**/*.steps.{js,mjs,ts}',
|
|
176
|
+
];
|
|
177
|
+
const featureFiles = resolveGlobs(featurePatterns, rootDir).filter((f) => f.endsWith('.feature'));
|
|
178
|
+
const stepFiles = resolveGlobs(stepPatterns, rootDir);
|
|
179
|
+
const targetOutput = options.outputFile
|
|
180
|
+
? path.isAbsolute(options.outputFile)
|
|
181
|
+
? options.outputFile
|
|
182
|
+
: path.resolve(rootDir, options.outputFile)
|
|
183
|
+
: resolveDefaultTargetOutput(rootDir, stepFiles);
|
|
184
|
+
const { compiledExpressions, existingNormalizedExpressions, registry } = collectExistingExpressions(stepFiles, targetOutput);
|
|
185
|
+
const allParsedSteps = collectStepsFromFeatures(featureFiles, rootDir);
|
|
186
|
+
let matchedStepsCount = 0;
|
|
187
|
+
const undefinedSteps = [];
|
|
188
|
+
for (const step of allParsedSteps) {
|
|
189
|
+
if (matchStepAgainstExpressions(step.text, compiledExpressions)) {
|
|
190
|
+
matchedStepsCount++;
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
undefinedSteps.push(step);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const uniqueSnippets = [];
|
|
197
|
+
const seenExpressionsInRun = new Set();
|
|
198
|
+
for (const step of undefinedSteps) {
|
|
199
|
+
const snippet = generateStepSnippet(step, registry);
|
|
200
|
+
const normalized = normalizeExpression(snippet.expression);
|
|
201
|
+
if (existingNormalizedExpressions.has(normalized)) {
|
|
202
|
+
matchedStepsCount++;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (seenExpressionsInRun.has(normalized)) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
seenExpressionsInRun.add(normalized);
|
|
209
|
+
uniqueSnippets.push(snippet);
|
|
210
|
+
}
|
|
211
|
+
const generatedCode = await writeScaffoldedSnippets(uniqueSnippets, targetOutput, dryRun);
|
|
212
|
+
return {
|
|
213
|
+
featureFilesScanned: featureFiles.length,
|
|
214
|
+
stepFilesScanned: stepFiles.length,
|
|
215
|
+
totalFeatureSteps: allParsedSteps.length,
|
|
216
|
+
matchedStepsCount,
|
|
217
|
+
undefinedStepsCount: undefinedSteps.length,
|
|
218
|
+
uniqueSnippetsGenerated: uniqueSnippets.length,
|
|
219
|
+
dryRun,
|
|
220
|
+
outputFile: targetOutput,
|
|
221
|
+
snippets: uniqueSnippets,
|
|
222
|
+
generatedCode,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=step-scaffolder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"step-scaffolder.js","sourceRoot":"","sources":["../../src/steps/step-scaffolder.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,37 @@
|
|
|
1
|
-
import { Org } from '@salesforce/core';
|
|
1
|
+
import { Connection, Org } from '@salesforce/core';
|
|
2
2
|
/** The environment variable naming the org a UTAM run authenticates to. */
|
|
3
3
|
export declare const USERNAME_ENV = "UTAM_USERNAME";
|
|
4
4
|
/** An environment-shaped map; `process.env` by default. */
|
|
5
5
|
export type EnvLike = Record<string, string | undefined>;
|
|
6
|
+
/**
|
|
7
|
+
* Default trusted Salesforce host regular expression patterns for Experience Cloud and Salesforce domains.
|
|
8
|
+
*/
|
|
9
|
+
export declare const DEFAULT_ALLOWED_HOST_PATTERNS: RegExp[];
|
|
10
|
+
/**
|
|
11
|
+
* Checks whether a given hostname is an authorized Salesforce or custom domain.
|
|
12
|
+
*
|
|
13
|
+
* @param hostname - The hostname to validate.
|
|
14
|
+
* @param instanceUrl - Optional base instance URL of the target org.
|
|
15
|
+
* @param envAllowedDomains - Comma-delimited custom allowed domains or wildcards.
|
|
16
|
+
* @returns True if the hostname is authorized, false otherwise.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isHostAllowed(hostname: string, instanceUrl?: string, envAllowedDomains?: string | undefined): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Validates and parses a raw secure URL retrieved from SiteDetail.
|
|
21
|
+
*
|
|
22
|
+
* @param secureUrl - The raw secure URL string.
|
|
23
|
+
* @param siteId - Identifier of the site for error reporting.
|
|
24
|
+
* @param hostValidator - Optional callback function validating the hostname.
|
|
25
|
+
* @returns The parsed and validated WHATWG URL object.
|
|
26
|
+
*/
|
|
27
|
+
export declare function validateAndParseSecureUrl(secureUrl: string, siteId: string, hostValidator?: (host: string) => boolean): URL;
|
|
6
28
|
export type TestEnvironmentOptions = {
|
|
7
29
|
/** Username or alias of an org already authorized with the Salesforce CLI. Wins over `UTAM_USERNAME`. */
|
|
8
30
|
username?: string;
|
|
9
31
|
/** Where `UTAM_USERNAME` is read from. Defaults to `process.env`. */
|
|
10
32
|
env?: EnvLike;
|
|
33
|
+
/** Optional custom allowed domains or wildcards overriding UTAM_ALLOWED_DOMAINS. */
|
|
34
|
+
allowedDomains?: string;
|
|
11
35
|
};
|
|
12
36
|
/**
|
|
13
37
|
* The org a UTAM run tests against, resolved from the Salesforce CLI's local auth store.
|
|
@@ -21,6 +45,10 @@ export declare class TestEnvironment {
|
|
|
21
45
|
* @param options Explicit username and environment overrides.
|
|
22
46
|
*/
|
|
23
47
|
constructor(options?: TestEnvironmentOptions);
|
|
48
|
+
/**
|
|
49
|
+
* Indicates whether authentication has been initialized.
|
|
50
|
+
*/
|
|
51
|
+
get isInitialized(): boolean;
|
|
24
52
|
/**
|
|
25
53
|
* Resolve the authenticated org. The first call authenticates and every later call shares its
|
|
26
54
|
* result, so callers need not coordinate who goes first; a failed attempt is not cached.
|
|
@@ -31,13 +59,80 @@ export declare class TestEnvironment {
|
|
|
31
59
|
* starts. Optional: every other method authenticates on first use.
|
|
32
60
|
*/
|
|
33
61
|
init(): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Returns the underlying `@salesforce/core` Connection instance.
|
|
64
|
+
*/
|
|
65
|
+
getConnection(): Promise<Connection>;
|
|
66
|
+
/**
|
|
67
|
+
* Returns the cleaned instance URL (My Domain) of the target org.
|
|
68
|
+
*/
|
|
69
|
+
getInstanceUrl(): Promise<string>;
|
|
34
70
|
/**
|
|
35
71
|
* Build a single-use frontdoor URL that logs the browser in and lands on `returnUrl`.
|
|
36
72
|
*
|
|
37
|
-
* The URL comes from Salesforce's single-access endpoint rather than a `sid=` query parameter,
|
|
38
|
-
* so the session's access token never appears in the browser's address bar or history.
|
|
39
|
-
*
|
|
40
73
|
* @param returnUrl Path to open after login, e.g. `/lightning/page/home`. Omit for the org's default landing page.
|
|
41
74
|
*/
|
|
42
75
|
buildFrontdoorUrl(returnUrl?: string): Promise<string>;
|
|
76
|
+
/**
|
|
77
|
+
* Checks whether a hostname is authorized using the current target org context.
|
|
78
|
+
*
|
|
79
|
+
* @param hostname - The hostname to validate.
|
|
80
|
+
*/
|
|
81
|
+
isHostAllowedInternal(hostname: string): Promise<boolean>;
|
|
82
|
+
/**
|
|
83
|
+
* Queries the Experience Cloud site and validates its SecureUrl.
|
|
84
|
+
*/
|
|
85
|
+
getValidatedExperienceSiteUrl(sitePrefix?: string): Promise<URL>;
|
|
86
|
+
/**
|
|
87
|
+
* Generates a Frontdoor URL for an Experience Cloud Site using the singleaccess API.
|
|
88
|
+
*
|
|
89
|
+
* @param sitePrefix - Optional URL path prefix of the site.
|
|
90
|
+
* @param returnUrl - Optional landing page path after login.
|
|
91
|
+
* @returns One-time Experience Cloud frontdoor login URL.
|
|
92
|
+
*/
|
|
93
|
+
buildExperienceFrontdoorUrl(sitePrefix?: string, returnUrl?: string): Promise<string>;
|
|
94
|
+
/**
|
|
95
|
+
* Retrieves the Salesforce Organization ID dynamically via SOQL query.
|
|
96
|
+
*/
|
|
97
|
+
getOrgId(): Promise<string>;
|
|
98
|
+
/**
|
|
99
|
+
* Resolves a given Salesforce username to its corresponding User ID via SOQL query.
|
|
100
|
+
*
|
|
101
|
+
* @param username - Target Salesforce username to resolve.
|
|
102
|
+
*/
|
|
103
|
+
getUserIdByUsername(username: string): Promise<string>;
|
|
104
|
+
/**
|
|
105
|
+
* Resolves an Experience Cloud Site's prefix to its corresponding 15-character Network ID.
|
|
106
|
+
*
|
|
107
|
+
* @param sitePrefix - The URL path prefix of the Experience site.
|
|
108
|
+
*/
|
|
109
|
+
getNetworkIdByPrefix(sitePrefix?: string): Promise<string>;
|
|
110
|
+
/**
|
|
111
|
+
* Generates a fully qualified secure URL for a given Experience Cloud Site (Community).
|
|
112
|
+
*
|
|
113
|
+
* @param sitePrefix - The URL path prefix of the Experience site.
|
|
114
|
+
* @param path - Optional relative subpath to append.
|
|
115
|
+
*/
|
|
116
|
+
buildExperienceUrl(sitePrefix?: string, path?: string): Promise<string>;
|
|
117
|
+
/**
|
|
118
|
+
* Resets active connection state, requiring re-authentication on subsequent calls.
|
|
119
|
+
*/
|
|
120
|
+
reset(): void;
|
|
43
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* Returns the global singleton TestEnvironment instance, lazily creating it if necessary.
|
|
124
|
+
*
|
|
125
|
+
* @param options - Optional configuration options to initialize or overwrite the singleton.
|
|
126
|
+
* @returns The active TestEnvironment instance.
|
|
127
|
+
*/
|
|
128
|
+
export declare function getTestEnvironment(options?: TestEnvironmentOptions): TestEnvironment;
|
|
129
|
+
/**
|
|
130
|
+
* Overrides the active global singleton TestEnvironment instance.
|
|
131
|
+
*
|
|
132
|
+
* @param instance - The TestEnvironment instance to set, or null to clear.
|
|
133
|
+
*/
|
|
134
|
+
export declare function setTestEnvironment(instance: TestEnvironment | null): void;
|
|
135
|
+
/**
|
|
136
|
+
* Clears and resets the global singleton TestEnvironment instance.
|
|
137
|
+
*/
|
|
138
|
+
export declare function resetTestEnvironment(): void;
|