@dev-blinq/cucumber_client 1.0.1190-dev → 1.0.1190-stage
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/bin/assets/bundled_scripts/recorder.js +220 -0
- package/bin/assets/preload/find_context.js +1 -1
- package/bin/assets/preload/locators.js +18 -0
- package/bin/assets/preload/recorderv3.js +77 -6
- package/bin/assets/preload/unique_locators.js +24 -3
- package/bin/assets/scripts/aria_snapshot.js +235 -0
- package/bin/assets/scripts/dom_attr.js +372 -0
- package/bin/assets/scripts/dom_element.js +0 -0
- package/bin/assets/scripts/dom_parent.js +185 -0
- package/bin/assets/scripts/event_utils.js +105 -0
- package/bin/assets/scripts/pw.js +7886 -0
- package/bin/assets/scripts/recorder.js +1147 -0
- package/bin/assets/scripts/snapshot_capturer.js +155 -0
- package/bin/assets/scripts/unique_locators.js +841 -0
- package/bin/assets/scripts/yaml.js +4770 -0
- package/bin/assets/templates/page_template.txt +2 -16
- package/bin/assets/templates/utils_template.txt +59 -7
- package/bin/client/cli_helpers.js +11 -13
- package/bin/client/code_cleanup/utils.js +42 -14
- package/bin/client/code_gen/code_inversion.js +51 -14
- package/bin/client/code_gen/index.js +3 -0
- package/bin/client/code_gen/page_reflection.js +37 -20
- package/bin/client/code_gen/playwright_codeget.js +153 -25
- package/bin/client/cucumber/feature.js +103 -28
- package/bin/client/cucumber/project_to_document.js +8 -7
- package/bin/client/cucumber/steps_definitions.js +118 -85
- package/bin/client/local_agent.js +6 -2
- package/bin/client/operations/dump_tree.js +157 -2
- package/bin/client/project.js +6 -2
- package/bin/client/recorderv3/bvt_recorder.js +275 -79
- package/bin/client/recorderv3/cli.js +1 -0
- package/bin/client/recorderv3/implemented_steps.js +111 -11
- package/bin/client/recorderv3/index.js +52 -7
- package/bin/client/recorderv3/network.js +299 -0
- package/bin/client/recorderv3/step_runner.js +183 -13
- package/bin/client/recorderv3/step_utils.js +163 -14
- package/bin/client/recorderv3/update_feature.js +58 -30
- package/bin/client/recording.js +8 -0
- package/bin/client/run_cucumber.js +16 -2
- package/bin/client/scenario_report.js +35 -8
- package/bin/client/test_scenario.js +0 -1
- package/bin/index.js +1 -0
- package/package.json +15 -8
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
1
|
+
import { existsSync, mkdirSync, rmSync, writeFileSync } from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import fs from "fs";
|
|
4
3
|
import { generatePageName } from "../code_gen/playwright_codeget.js";
|
|
5
4
|
import {
|
|
6
5
|
executeStep,
|
|
@@ -9,26 +8,36 @@ import {
|
|
|
9
8
|
getUtilsCodePage,
|
|
10
9
|
loadStepDefinitions,
|
|
11
10
|
saveRecording,
|
|
11
|
+
saveRoutes,
|
|
12
12
|
} from "./step_utils.js";
|
|
13
13
|
import { escapeString, getExamplesContent } from "./update_feature.js";
|
|
14
|
+
import fs from "fs";
|
|
15
|
+
import { locateDefinitionPath } from "../cucumber/steps_definitions.js";
|
|
16
|
+
import { tmpdir } from "os";
|
|
14
17
|
|
|
15
18
|
// let copiedCodeToTemp = false;
|
|
16
19
|
async function withAbort(fn, signal) {
|
|
17
20
|
if (!signal) {
|
|
18
21
|
return await fn();
|
|
19
22
|
}
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
const abortHandler = () => reject(new Error("Aborted"));
|
|
25
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
20
26
|
|
|
21
|
-
|
|
22
|
-
|
|
27
|
+
fn()
|
|
28
|
+
.then(resolve)
|
|
29
|
+
.catch(reject)
|
|
30
|
+
.finally(() => {
|
|
31
|
+
signal.removeEventListener("abort", abortHandler);
|
|
32
|
+
});
|
|
23
33
|
});
|
|
24
|
-
|
|
25
|
-
return await Promise.race([fn(), abortPromise]);
|
|
26
34
|
}
|
|
27
35
|
export class BVTStepRunner {
|
|
28
36
|
#currentStepController;
|
|
29
37
|
#port;
|
|
30
|
-
constructor({ projectDir }) {
|
|
38
|
+
constructor({ projectDir, sendExecutionStatus }) {
|
|
31
39
|
this.projectDir = projectDir;
|
|
40
|
+
this.sendExecutionStatus = sendExecutionStatus;
|
|
32
41
|
}
|
|
33
42
|
setRemoteDebugPort(port) {
|
|
34
43
|
this.#port = port;
|
|
@@ -55,11 +64,12 @@ export class BVTStepRunner {
|
|
|
55
64
|
// copiedCodeToTemp = true;
|
|
56
65
|
}
|
|
57
66
|
|
|
58
|
-
async writeTempFeatureFile({ step, parametersMap, tempFolderPath }) {
|
|
67
|
+
async writeTempFeatureFile({ step, parametersMap, tempFolderPath, tags }) {
|
|
59
68
|
const tFilePath = path.join(tempFolderPath, "__temp.feature");
|
|
60
69
|
// console.log(tFilePath);
|
|
61
70
|
let tFileContent = `# temp feature file
|
|
62
71
|
Feature: Temp feature
|
|
72
|
+
${tags ? tags.join(" ") : ""}
|
|
63
73
|
Scenario Outline: Temp Scenario
|
|
64
74
|
Given ${escapeString(step.text)}
|
|
65
75
|
`;
|
|
@@ -67,7 +77,135 @@ export class BVTStepRunner {
|
|
|
67
77
|
writeFileSync(tFilePath, tFileContent);
|
|
68
78
|
return tFilePath;
|
|
69
79
|
}
|
|
70
|
-
|
|
80
|
+
|
|
81
|
+
executeStepRemote = async ({ feature_file_path, scenario, tempFolderPath, stepText, config }, options) => {
|
|
82
|
+
const { skipAfter = true, skipBefore = true } = options || {};
|
|
83
|
+
const environment = {
|
|
84
|
+
...process.env,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const { loadConfiguration, loadSupport, runCucumber } = await import("@dev-blinq/cucumber-js/api");
|
|
89
|
+
const { runConfiguration } = await loadConfiguration(
|
|
90
|
+
{
|
|
91
|
+
provided: {
|
|
92
|
+
name: [scenario],
|
|
93
|
+
paths: [feature_file_path],
|
|
94
|
+
import: [path.join(tempFolderPath, "step_definitions", "**", "*.mjs")],
|
|
95
|
+
// format: ["bvt"],
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
{ cwd: process.cwd(), env: environment }
|
|
99
|
+
);
|
|
100
|
+
// const files = glob.sync(path.join(tempFolderPath, "step_definitions", "**", "*.mjs"));
|
|
101
|
+
// console.log("Files found:", files);
|
|
102
|
+
const support = await loadSupport(runConfiguration, { cwd: process.cwd(), env: environment });
|
|
103
|
+
// console.log("found ", support.stepDefinitions.length, "step definitions");
|
|
104
|
+
// support.stepDefinitions.map((step) => {
|
|
105
|
+
// console.log("step", step.pattern);
|
|
106
|
+
// });
|
|
107
|
+
|
|
108
|
+
support.afterTestRunHookDefinitions = [];
|
|
109
|
+
if (skipAfter) {
|
|
110
|
+
// ignore afterAll/after hooks
|
|
111
|
+
support.afterTestCaseHookDefinitions = [];
|
|
112
|
+
}
|
|
113
|
+
if (skipBefore && !config.legacySyntax) {
|
|
114
|
+
// ignore beforeAll/before hooks
|
|
115
|
+
support.beforeTestCaseHookDefinitions = support.beforeTestCaseHookDefinitions.filter((hook) => {
|
|
116
|
+
return hook.uri.endsWith("utils.mjs");
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
support.beforeTestRunHookDefinitions = [];
|
|
120
|
+
|
|
121
|
+
let errorMesssage = null;
|
|
122
|
+
let info = null;
|
|
123
|
+
let errInfo = null;
|
|
124
|
+
const result = await runCucumber({ ...runConfiguration, support }, environment, (message) => {
|
|
125
|
+
if (message.testStepFinished) {
|
|
126
|
+
const { testStepFinished } = message;
|
|
127
|
+
const { testStepResult } = testStepFinished;
|
|
128
|
+
if (testStepResult.status === "FAILED" || testStepResult.status === "AMBIGUOUS") {
|
|
129
|
+
if (!errorMesssage) {
|
|
130
|
+
errorMesssage = testStepResult.message;
|
|
131
|
+
if (info) {
|
|
132
|
+
errInfo = info;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (testStepResult.status === "UNDEFINED") {
|
|
137
|
+
if (!errorMesssage) {
|
|
138
|
+
errorMesssage = `step ${JSON.stringify(stepText)} is ${testStepResult.status}`;
|
|
139
|
+
if (info) {
|
|
140
|
+
errInfo = info;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (message.attachment) {
|
|
146
|
+
const attachment = message.attachment;
|
|
147
|
+
if (attachment.mediaType === "application/json" && attachment.body) {
|
|
148
|
+
const body = JSON.parse(attachment.body);
|
|
149
|
+
info = body.info;
|
|
150
|
+
const result = body.result;
|
|
151
|
+
|
|
152
|
+
if (result.status === "PASSED") {
|
|
153
|
+
this.sendExecutionStatus({
|
|
154
|
+
type: "cmdExecutionSuccess",
|
|
155
|
+
cmdId: body.cmdId,
|
|
156
|
+
});
|
|
157
|
+
} else {
|
|
158
|
+
this.sendExecutionStatus({
|
|
159
|
+
type: "cmdExecutionError",
|
|
160
|
+
cmdId: body.cmdId,
|
|
161
|
+
error: {
|
|
162
|
+
message: result.message,
|
|
163
|
+
info,
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
} else if (attachment.mediaType === "application/json+intercept-results" && attachment.body) {
|
|
168
|
+
const body = JSON.parse(attachment.body);
|
|
169
|
+
if (body) {
|
|
170
|
+
this.sendExecutionStatus({
|
|
171
|
+
type: "interceptResults",
|
|
172
|
+
interceptResults: body,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
if (errorMesssage) {
|
|
179
|
+
const bvtError = new Error(errorMesssage);
|
|
180
|
+
Object.assign(bvtError, { info: errInfo });
|
|
181
|
+
throw bvtError;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
result,
|
|
186
|
+
info,
|
|
187
|
+
};
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.error("Error running cucumber-js", error);
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
async runStep({ step, parametersMap, envPath, tags, config }, bvtContext, options) {
|
|
195
|
+
let cmdIDs = (step.commands || []).map((cmd) => cmd.cmdId);
|
|
196
|
+
if (bvtContext.web) {
|
|
197
|
+
bvtContext.web.getCmdId = () => {
|
|
198
|
+
if (cmdIDs.length === 0) {
|
|
199
|
+
cmdIDs = (step.commands || []).map((cmd) => cmd.cmdId);
|
|
200
|
+
}
|
|
201
|
+
const cId = cmdIDs.shift();
|
|
202
|
+
this.sendExecutionStatus({
|
|
203
|
+
type: "cmdExecutionStart",
|
|
204
|
+
cmdId: cId,
|
|
205
|
+
});
|
|
206
|
+
return cId;
|
|
207
|
+
};
|
|
208
|
+
}
|
|
71
209
|
let codePage; // = getCodePage();
|
|
72
210
|
// const tempFolderPath = process.env.tempFeaturesFolderPath;
|
|
73
211
|
const __temp_features_FolderName = "__temp_features" + Math.random().toString(36).substring(2, 7);
|
|
@@ -96,7 +234,8 @@ export class BVTStepRunner {
|
|
|
96
234
|
if (!existsSync(stepDefinitionFolderPath)) {
|
|
97
235
|
mkdirSync(stepDefinitionFolderPath, { recursive: true });
|
|
98
236
|
}
|
|
99
|
-
const stepDefsFilePath =
|
|
237
|
+
const stepDefsFilePath = locateDefinitionPath(tempFolderPath, pageName);
|
|
238
|
+
//path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
|
|
100
239
|
codePage = getCodePage(stepDefsFilePath);
|
|
101
240
|
codePage = await saveRecording({ step, cucumberStep, codePage, projectDir: this.projectDir, stepsDefinitions });
|
|
102
241
|
if (codePage) {
|
|
@@ -106,19 +245,49 @@ export class BVTStepRunner {
|
|
|
106
245
|
if (!codePage) {
|
|
107
246
|
codePage = getUtilsCodePage(this.projectDir);
|
|
108
247
|
}
|
|
248
|
+
} else {
|
|
249
|
+
let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
|
|
250
|
+
if (process.env.TEMP_RUN === "true") {
|
|
251
|
+
// console.log("Save routes in temp folder for running:", routesPath);
|
|
252
|
+
if (existsSync(routesPath)) {
|
|
253
|
+
console.log("Removing existing temp_routes_folder:", routesPath);
|
|
254
|
+
rmSync(routesPath, { recursive: true });
|
|
255
|
+
}
|
|
256
|
+
mkdirSync(routesPath, { recursive: true });
|
|
257
|
+
console.log("Created temp_routes_folder:", routesPath);
|
|
258
|
+
saveRoutes({ step, folderPath: routesPath });
|
|
259
|
+
} else {
|
|
260
|
+
console.log("Saving routes in project directory:", this.projectDir);
|
|
261
|
+
if (existsSync(routesPath)) {
|
|
262
|
+
// remove the folder
|
|
263
|
+
try {
|
|
264
|
+
rmSync(routesPath, { recursive: true });
|
|
265
|
+
console.log("Removed temp_routes_folder:", routesPath);
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.error("Error removing temp_routes folder", error);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
routesPath = path.join(this.projectDir, "data", "routes");
|
|
271
|
+
console.log("Saving routes to:", routesPath);
|
|
272
|
+
if (!existsSync(routesPath)) {
|
|
273
|
+
mkdirSync(routesPath, { recursive: true });
|
|
274
|
+
}
|
|
275
|
+
saveRoutes({ step, folderPath: routesPath });
|
|
276
|
+
}
|
|
109
277
|
}
|
|
110
|
-
const feature_file_path = await this.writeTempFeatureFile({ step, parametersMap, tempFolderPath });
|
|
278
|
+
const feature_file_path = await this.writeTempFeatureFile({ step, parametersMap, tempFolderPath, tags });
|
|
111
279
|
// console.log({ feature_file_path, step_text: step.text });
|
|
112
280
|
|
|
113
281
|
const stepExecController = new AbortController();
|
|
114
282
|
this.#currentStepController = stepExecController;
|
|
115
|
-
await withAbort(async () => {
|
|
116
|
-
await
|
|
283
|
+
const { result, info } = await withAbort(async () => {
|
|
284
|
+
return await this.executeStepRemote(
|
|
117
285
|
{
|
|
118
286
|
feature_file_path,
|
|
119
287
|
tempFolderPath,
|
|
120
288
|
stepText: step.text,
|
|
121
289
|
scenario: "Temp Scenario",
|
|
290
|
+
config,
|
|
122
291
|
},
|
|
123
292
|
options
|
|
124
293
|
);
|
|
@@ -128,5 +297,6 @@ export class BVTStepRunner {
|
|
|
128
297
|
fs.rmSync(tempFolderPath, { recursive: true });
|
|
129
298
|
}
|
|
130
299
|
});
|
|
300
|
+
return { result, info };
|
|
131
301
|
}
|
|
132
302
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import url from "url";
|
|
4
4
|
import logger from "../../logger.js";
|
|
5
|
-
import { CodePage } from "../code_gen/page_reflection.js";
|
|
5
|
+
import { CodePage, getAiConfig } from "../code_gen/page_reflection.js";
|
|
6
6
|
import { generateCode, generatePageName } from "../code_gen/playwright_codeget.js";
|
|
7
7
|
import { invertCodeToCommand } from "../code_gen/code_inversion.js";
|
|
8
8
|
import { Step } from "../cucumber/feature.js";
|
|
9
|
-
import { StepsDefinitions } from "../cucumber/steps_definitions.js";
|
|
9
|
+
import { locateDefinitionPath, StepsDefinitions } from "../cucumber/steps_definitions.js";
|
|
10
10
|
import { Recording } from "../recording.js";
|
|
11
11
|
import { generateApiCode } from "../code_gen/api_codegen.js";
|
|
12
|
+
import { tmpdir } from "os";
|
|
13
|
+
import { createHash } from "crypto";
|
|
12
14
|
|
|
13
15
|
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
|
|
14
16
|
|
|
@@ -69,19 +71,74 @@ function makeStepTextUnique(step, stepsDefinitions) {
|
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions }) {
|
|
72
|
-
|
|
74
|
+
let routesPath = path.join(tmpdir(), "blinq_temp_routes");
|
|
75
|
+
|
|
76
|
+
if (process.env.TEMP_RUN) {
|
|
77
|
+
if (existsSync(routesPath)) {
|
|
78
|
+
rmSync(routesPath, { recursive: true });
|
|
79
|
+
}
|
|
80
|
+
mkdirSync(routesPath, { recursive: true });
|
|
81
|
+
saveRoutes({ step, folderPath: routesPath });
|
|
82
|
+
} else {
|
|
83
|
+
if (existsSync(routesPath)) {
|
|
84
|
+
// remove the folder
|
|
85
|
+
try {
|
|
86
|
+
rmSync(routesPath, { recursive: true });
|
|
87
|
+
console.log("Removed temp_routes_folder:", routesPath);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.error("Error removing temp_routes folder", error);
|
|
90
|
+
}
|
|
91
|
+
routesPath = path.join(projectDir, "data", "routes");
|
|
92
|
+
if (!existsSync(routesPath)) {
|
|
93
|
+
mkdirSync(routesPath, { recursive: true });
|
|
94
|
+
}
|
|
95
|
+
saveRoutes({ step, folderPath: routesPath });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
73
99
|
if (step.isImplementedWhileRecording && !process.env.TEMP_RUN) {
|
|
74
100
|
return;
|
|
75
101
|
}
|
|
102
|
+
|
|
76
103
|
if (step.isImplemented && step.shouldOverride) {
|
|
77
104
|
let stepDef = stepsDefinitions.findMatchingStep(step.text);
|
|
78
105
|
codePage = getCodePage(stepDef.file);
|
|
79
106
|
} else {
|
|
80
107
|
const isUtilStep = makeStepTextUnique(step, stepsDefinitions);
|
|
108
|
+
|
|
81
109
|
if (isUtilStep) {
|
|
82
110
|
return;
|
|
83
111
|
}
|
|
84
112
|
}
|
|
113
|
+
|
|
114
|
+
if (process.env.TEMP_RUN === "true") {
|
|
115
|
+
console.log("Save routes in temp folder for running:", routesPath);
|
|
116
|
+
if (existsSync(routesPath)) {
|
|
117
|
+
console.log("Removing existing temp_routes_folder:", routesPath);
|
|
118
|
+
rmSync(routesPath, { recursive: true });
|
|
119
|
+
}
|
|
120
|
+
mkdirSync(routesPath, { recursive: true });
|
|
121
|
+
console.log("Created temp_routes_folder:", routesPath);
|
|
122
|
+
saveRoutes({ step, folderPath: routesPath });
|
|
123
|
+
} else {
|
|
124
|
+
console.log("Saving routes in project directory:", projectDir);
|
|
125
|
+
if (existsSync(routesPath)) {
|
|
126
|
+
// remove the folder
|
|
127
|
+
try {
|
|
128
|
+
rmSync(routesPath, { recursive: true });
|
|
129
|
+
console.log("Removed temp_routes_folder:", routesPath);
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.error("Error removing temp_routes folder", error);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
routesPath = path.join(projectDir, "data", "routes");
|
|
135
|
+
console.log("Saving routes to:", routesPath);
|
|
136
|
+
if (!existsSync(routesPath)) {
|
|
137
|
+
mkdirSync(routesPath, { recursive: true });
|
|
138
|
+
}
|
|
139
|
+
saveRoutes({ step, folderPath: routesPath });
|
|
140
|
+
}
|
|
141
|
+
|
|
85
142
|
cucumberStep.text = step.text;
|
|
86
143
|
const recording = new Recording();
|
|
87
144
|
const steps = step.commands;
|
|
@@ -108,6 +165,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
108
165
|
isStaticToken,
|
|
109
166
|
status,
|
|
110
167
|
} = step.commands[0].value;
|
|
168
|
+
|
|
111
169
|
const result = await generateApiCode(
|
|
112
170
|
{
|
|
113
171
|
url,
|
|
@@ -132,11 +190,15 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
132
190
|
step.keyword,
|
|
133
191
|
stepsDefinitions
|
|
134
192
|
);
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
193
|
+
|
|
194
|
+
if (!step.isImplemented) {
|
|
195
|
+
stepsDefinitions.addStep({
|
|
196
|
+
name: step.text,
|
|
197
|
+
file: result.codePage.sourceFileName,
|
|
198
|
+
source: "recorder",
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
140
202
|
cucumberStep.methodName = result.methodName;
|
|
141
203
|
return result.codePage;
|
|
142
204
|
} else {
|
|
@@ -164,7 +226,13 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
164
226
|
path
|
|
165
227
|
);
|
|
166
228
|
const keyword = (cucumberStep.keywordAlias ?? cucumberStep.keyword).trim();
|
|
167
|
-
const stepResult = codePage.addCucumberStep(
|
|
229
|
+
const stepResult = codePage.addCucumberStep(
|
|
230
|
+
keyword,
|
|
231
|
+
cucumberStep.getTemplate(),
|
|
232
|
+
methodName,
|
|
233
|
+
steps.length,
|
|
234
|
+
step.finalTimeout
|
|
235
|
+
);
|
|
168
236
|
|
|
169
237
|
if (!step.isImplemented) {
|
|
170
238
|
stepsDefinitions.addStep({
|
|
@@ -185,7 +253,17 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
185
253
|
|
|
186
254
|
const getLocatorsJson = (file) => {
|
|
187
255
|
if (!file) return {};
|
|
188
|
-
|
|
256
|
+
let locatorsFilePath = file.replace(".mjs", ".json");
|
|
257
|
+
const originLocatorsFilePath = locatorsFilePath;
|
|
258
|
+
const config = getAiConfig();
|
|
259
|
+
if (config && config.locatorsMetadataDir) {
|
|
260
|
+
// if config.locatorsMetadataDir is set, use it to create the file path
|
|
261
|
+
locatorsFilePath = path.join(config.locatorsMetadataDir, path.basename(locatorsFilePath));
|
|
262
|
+
if (!existsSync(locatorsFilePath)) {
|
|
263
|
+
// if the file does not exist in the config directory, use the original path
|
|
264
|
+
locatorsFilePath = originLocatorsFilePath;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
189
267
|
if (!existsSync(locatorsFilePath)) {
|
|
190
268
|
return {};
|
|
191
269
|
}
|
|
@@ -227,7 +305,7 @@ export const getCommandsForImplementedStep = (stepName, stepsDefinitions, stepPa
|
|
|
227
305
|
|
|
228
306
|
isUtilStep = codePage.sourceFileName.endsWith("utils.mjs");
|
|
229
307
|
for (const { code } of codeCommands) {
|
|
230
|
-
const command = invertCodeToCommand(code, elements, stepParams)[0];
|
|
308
|
+
const command = invertCodeToCommand(code, elements, stepParams, stepsDefinitions, codePage, stepName)[0];
|
|
231
309
|
if (command === undefined || command.type === null) continue;
|
|
232
310
|
if (command.element) {
|
|
233
311
|
const key = command.element.key;
|
|
@@ -297,6 +375,7 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
|
|
|
297
375
|
const steps = scenario.steps;
|
|
298
376
|
|
|
299
377
|
const stepsDefinitions = new StepsDefinitions(projectDir);
|
|
378
|
+
const featureFolder = path.join(projectDir, "features");
|
|
300
379
|
stepsDefinitions.load(false);
|
|
301
380
|
// const parameters = scenario.parameters;
|
|
302
381
|
// await saveRecordings({ steps, parameters, codePage, projectDir });
|
|
@@ -308,13 +387,41 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
|
|
|
308
387
|
}
|
|
309
388
|
}
|
|
310
389
|
if ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0) {
|
|
390
|
+
let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
|
|
391
|
+
if (process.env.TEMP_RUN === "true") {
|
|
392
|
+
console.log("Save routes in temp folder for running:", routesPath);
|
|
393
|
+
if (existsSync(routesPath)) {
|
|
394
|
+
console.log("Removing existing temp_routes_folder:", routesPath);
|
|
395
|
+
rmSync(routesPath, { recursive: true });
|
|
396
|
+
}
|
|
397
|
+
mkdirSync(routesPath, { recursive: true });
|
|
398
|
+
console.log("Created temp_routes_folder:", routesPath);
|
|
399
|
+
saveRoutes({ step, folderPath: routesPath });
|
|
400
|
+
} else {
|
|
401
|
+
console.log("Saving routes in project directory:", projectDir);
|
|
402
|
+
if (existsSync(routesPath)) {
|
|
403
|
+
// remove the folder
|
|
404
|
+
try {
|
|
405
|
+
rmSync(routesPath, { recursive: true });
|
|
406
|
+
console.log("Removed temp_routes_folder:", routesPath);
|
|
407
|
+
} catch (error) {
|
|
408
|
+
console.error("Error removing temp_routes folder", error);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
routesPath = path.join(projectDir, "data", "routes");
|
|
412
|
+
console.log("Saving routes to:", routesPath);
|
|
413
|
+
if (!existsSync(routesPath)) {
|
|
414
|
+
mkdirSync(routesPath, { recursive: true });
|
|
415
|
+
}
|
|
416
|
+
saveRoutes({ step, folderPath: routesPath });
|
|
417
|
+
}
|
|
311
418
|
continue;
|
|
312
419
|
}
|
|
313
420
|
const cucumberStep = getCucumberStep({ step });
|
|
314
421
|
const pageName = generatePageName(step.startFrame?.url ?? "default");
|
|
315
|
-
const stepDefsFilePath =
|
|
422
|
+
const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
|
|
423
|
+
// path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
|
|
316
424
|
let codePage = getCodePage(stepDefsFilePath);
|
|
317
|
-
|
|
318
425
|
codePage = await saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions });
|
|
319
426
|
if (!codePage) {
|
|
320
427
|
continue;
|
|
@@ -326,3 +433,45 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
|
|
|
326
433
|
}
|
|
327
434
|
writeFileSync(utilsFilePath, utilsContent, "utf8");
|
|
328
435
|
}
|
|
436
|
+
|
|
437
|
+
export function saveRoutes({ step, folderPath }) {
|
|
438
|
+
const routeItems = step.routeItems;
|
|
439
|
+
if (!routeItems || routeItems.length === 0) {
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
const cucumberStep = getCucumberStep({ step });
|
|
443
|
+
const template = cucumberStep.getTemplate();
|
|
444
|
+
const stepNameHash = createHash("sha256").update(template).digest("hex");
|
|
445
|
+
console.log("Saving routes for step:", step.text, "with hash:", stepNameHash);
|
|
446
|
+
|
|
447
|
+
const routeItemsWithFilters = routeItems.map((routeItem) => {
|
|
448
|
+
const oldFilters = routeItem.filters;
|
|
449
|
+
const queryParamsObject = {};
|
|
450
|
+
oldFilters.queryParams.forEach((queryParam) => {
|
|
451
|
+
queryParamsObject[queryParam.key] = queryParam.value;
|
|
452
|
+
});
|
|
453
|
+
const newFilters = { path: oldFilters.path, method: oldFilters.method, queryParams: queryParamsObject };
|
|
454
|
+
return {
|
|
455
|
+
...routeItem,
|
|
456
|
+
filters: newFilters,
|
|
457
|
+
};
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
const routesFilePath = path.join(folderPath, stepNameHash + ".json");
|
|
461
|
+
console.log("Routes file path:", routesFilePath);
|
|
462
|
+
const routesData = {
|
|
463
|
+
template,
|
|
464
|
+
routes: routeItemsWithFilters,
|
|
465
|
+
};
|
|
466
|
+
console.log("Routes data to save:", routesData);
|
|
467
|
+
|
|
468
|
+
if (!existsSync(folderPath)) {
|
|
469
|
+
mkdirSync(folderPath, { recursive: true });
|
|
470
|
+
}
|
|
471
|
+
try {
|
|
472
|
+
writeFileSync(routesFilePath, JSON.stringify(routesData, null, 2), "utf8");
|
|
473
|
+
console.log("Saved routes to", routesFilePath);
|
|
474
|
+
} catch (error) {
|
|
475
|
+
console.error("Failed to save routes to", routesFilePath, "Error:", error);
|
|
476
|
+
}
|
|
477
|
+
}
|