@dev-blinq/cucumber_client 1.0.1438-dev → 1.0.1438-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 +73 -73
- package/bin/assets/preload/css_gen.js +10 -10
- package/bin/assets/preload/toolbar.js +27 -29
- package/bin/assets/preload/unique_locators.js +1 -1
- package/bin/assets/preload/yaml.js +288 -275
- package/bin/assets/scripts/aria_snapshot.js +223 -220
- package/bin/assets/scripts/dom_attr.js +329 -329
- package/bin/assets/scripts/dom_parent.js +169 -174
- package/bin/assets/scripts/event_utils.js +94 -94
- package/bin/assets/scripts/pw.js +2050 -1949
- package/bin/assets/scripts/recorder.js +70 -45
- package/bin/assets/scripts/snapshot_capturer.js +147 -147
- package/bin/assets/scripts/unique_locators.js +170 -49
- package/bin/assets/scripts/yaml.js +796 -783
- package/bin/assets/templates/_hooks_template.txt +6 -2
- package/bin/assets/templates/utils_template.txt +16 -16
- package/bin/client/code_cleanup/find_step_definition_references.js +0 -1
- package/bin/client/code_cleanup/utils.js +16 -7
- package/bin/client/code_gen/api_codegen.js +2 -2
- package/bin/client/code_gen/code_inversion.js +63 -2
- package/bin/client/code_gen/duplication_analysis.js +2 -1
- package/bin/client/code_gen/function_signature.js +4 -0
- package/bin/client/code_gen/page_reflection.js +52 -11
- package/bin/client/code_gen/playwright_codeget.js +46 -28
- package/bin/client/cucumber/feature.js +4 -17
- package/bin/client/cucumber/feature_data.js +2 -2
- package/bin/client/cucumber/project_to_document.js +8 -2
- package/bin/client/cucumber/steps_definitions.js +19 -3
- package/bin/client/local_agent.js +3 -2
- package/bin/client/parse_feature_file.js +23 -26
- package/bin/client/playground/projects/env.json +2 -2
- package/bin/client/recorderv3/bvt_init.js +305 -0
- package/bin/client/recorderv3/bvt_recorder.js +1025 -57
- package/bin/client/recorderv3/implemented_steps.js +2 -0
- package/bin/client/recorderv3/index.js +3 -283
- package/bin/client/recorderv3/services.js +818 -142
- package/bin/client/recorderv3/step_runner.js +21 -7
- package/bin/client/recorderv3/step_utils.js +540 -72
- package/bin/client/recorderv3/update_feature.js +86 -39
- package/bin/client/recorderv3/wbr_entry.js +61 -0
- package/bin/client/recording.js +1 -0
- package/bin/client/upload-service.js +4 -2
- package/bin/client/utils/app_dir.js +21 -0
- package/bin/client/utils/socket_logger.js +87 -125
- package/bin/index.js +4 -1
- package/package.json +11 -5
- package/bin/client/recorderv3/app_dir.js +0 -23
- package/bin/client/recorderv3/network.js +0 -299
- package/bin/client/recorderv3/scriptTest.js +0 -5
- package/bin/client/recorderv3/ws_server.js +0 -72
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import url from "url";
|
|
4
|
-
import logger from "../../logger.js";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import url from "node:url";
|
|
5
4
|
import { CodePage, getAiConfig } from "../code_gen/page_reflection.js";
|
|
6
5
|
import { generateCode, generatePageName } from "../code_gen/playwright_codeget.js";
|
|
7
6
|
import { invertCodeToCommand } from "../code_gen/code_inversion.js";
|
|
@@ -9,11 +8,437 @@ import { Step } from "../cucumber/feature.js";
|
|
|
9
8
|
import { locateDefinitionPath, StepsDefinitions } from "../cucumber/steps_definitions.js";
|
|
10
9
|
import { Recording } from "../recording.js";
|
|
11
10
|
import { generateApiCode } from "../code_gen/api_codegen.js";
|
|
12
|
-
import { tmpdir } from "os";
|
|
13
|
-
import { createHash } from "crypto";
|
|
11
|
+
import { tmpdir } from "node:os";
|
|
12
|
+
import { createHash } from "node:crypto";
|
|
13
|
+
import { getErrorMessage } from "../utils/socket_logger.js";
|
|
14
14
|
|
|
15
15
|
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
|
|
16
16
|
|
|
17
|
+
const convertToIdentifier = (text) => {
|
|
18
|
+
// replace all invalid characters with _
|
|
19
|
+
return text.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const isVariable = (text) => {
|
|
23
|
+
if (typeof text !== "string") return false;
|
|
24
|
+
const isParametric = text.startsWith("<") && text.endsWith(">");
|
|
25
|
+
if (!isParametric) return false;
|
|
26
|
+
const l = text.length;
|
|
27
|
+
if (l < 2) return false;
|
|
28
|
+
const leftindex = text.indexOf("<");
|
|
29
|
+
const rightindex = text.indexOf(">");
|
|
30
|
+
return leftindex === 0 && rightindex === l - 1;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const extractQuotes = (text) => {
|
|
34
|
+
const stringRegex = /"([^"]*)"/g;
|
|
35
|
+
const matches = text.match(stringRegex);
|
|
36
|
+
if (!matches) return [];
|
|
37
|
+
const quotes = [];
|
|
38
|
+
for (const match of matches) {
|
|
39
|
+
const value = match.slice(1, -1);
|
|
40
|
+
quotes.push(value);
|
|
41
|
+
}
|
|
42
|
+
return quotes;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const replaceLastOccurence = (str, search, replacement) => {
|
|
46
|
+
const lastIndex = str.lastIndexOf(search);
|
|
47
|
+
if (lastIndex === -1) return str;
|
|
48
|
+
return str.substring(0, lastIndex) + replacement + str.substring(lastIndex + search.length);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const _toRecordingStep = (cmd, stepText) => {
|
|
52
|
+
switch (cmd.type) {
|
|
53
|
+
case "hover_element": {
|
|
54
|
+
return {
|
|
55
|
+
type: "hover_element",
|
|
56
|
+
element: {
|
|
57
|
+
role: cmd.role,
|
|
58
|
+
name: cmd.label,
|
|
59
|
+
},
|
|
60
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
case "click_element": {
|
|
64
|
+
return {
|
|
65
|
+
type: "click_element",
|
|
66
|
+
element: {
|
|
67
|
+
role: cmd.role,
|
|
68
|
+
name: cmd.label,
|
|
69
|
+
},
|
|
70
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
71
|
+
count: cmd.count ?? 1,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
case "context_click": {
|
|
75
|
+
return {
|
|
76
|
+
type: "context_click",
|
|
77
|
+
element: {
|
|
78
|
+
role: cmd.role,
|
|
79
|
+
name: cmd.label,
|
|
80
|
+
},
|
|
81
|
+
label: cmd.label,
|
|
82
|
+
value: cmd.value,
|
|
83
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
84
|
+
text: cmd.text,
|
|
85
|
+
count: cmd.count ?? 1,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
case "parameterized_click": {
|
|
89
|
+
return {
|
|
90
|
+
type: "parameterized_click",
|
|
91
|
+
element: {
|
|
92
|
+
role: cmd.role,
|
|
93
|
+
name: cmd.label,
|
|
94
|
+
},
|
|
95
|
+
label: cmd.label,
|
|
96
|
+
value: cmd.value,
|
|
97
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
98
|
+
count: cmd.count ?? 1,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
case "fill_element": {
|
|
102
|
+
return {
|
|
103
|
+
type: "fill_element",
|
|
104
|
+
element: {
|
|
105
|
+
role: cmd.role,
|
|
106
|
+
name: cmd.label,
|
|
107
|
+
},
|
|
108
|
+
parameters: [cmd.value, cmd.enter ?? false],
|
|
109
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
case "select_combobox": {
|
|
113
|
+
return {
|
|
114
|
+
type: "select_combobox",
|
|
115
|
+
element: {
|
|
116
|
+
role: "combobox",
|
|
117
|
+
name: cmd.label,
|
|
118
|
+
},
|
|
119
|
+
selectMode: "select",
|
|
120
|
+
parameters: [cmd.value],
|
|
121
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
case "verify_page_contains_text": {
|
|
125
|
+
return {
|
|
126
|
+
type: "verify_page_contains_text",
|
|
127
|
+
parameters: [cmd.value, cmd.isRegex],
|
|
128
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
case "verify_element_contains_text": {
|
|
132
|
+
return {
|
|
133
|
+
type: "verify_element_contains_text",
|
|
134
|
+
element: {
|
|
135
|
+
role: cmd.role,
|
|
136
|
+
name: cmd.label,
|
|
137
|
+
},
|
|
138
|
+
parameters: [cmd.value, cmd.climb],
|
|
139
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
case "close_page": {
|
|
143
|
+
return {
|
|
144
|
+
type: "close_page",
|
|
145
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
case "check_element": {
|
|
149
|
+
return {
|
|
150
|
+
type: "check_element",
|
|
151
|
+
element: {
|
|
152
|
+
role: cmd.role,
|
|
153
|
+
name: cmd.label,
|
|
154
|
+
},
|
|
155
|
+
check: cmd.check,
|
|
156
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
case "press_key": {
|
|
160
|
+
return {
|
|
161
|
+
type: "press_key",
|
|
162
|
+
element: {
|
|
163
|
+
role: cmd.role,
|
|
164
|
+
name: cmd.label,
|
|
165
|
+
},
|
|
166
|
+
key: cmd.value,
|
|
167
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
case "load_user": {
|
|
171
|
+
return {
|
|
172
|
+
type: "load_data",
|
|
173
|
+
parameters: ["users", cmd.value],
|
|
174
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
case "load_csv": {
|
|
178
|
+
return {
|
|
179
|
+
type: "load_data",
|
|
180
|
+
parameters: ["csv", `${cmd.label}:${cmd.value}`],
|
|
181
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
case "set_date_time": {
|
|
185
|
+
return {
|
|
186
|
+
type: "set_date_time",
|
|
187
|
+
element: {
|
|
188
|
+
role: cmd.role,
|
|
189
|
+
name: cmd.label,
|
|
190
|
+
},
|
|
191
|
+
parameters: [cmd.value],
|
|
192
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
case "set_input": {
|
|
196
|
+
return {
|
|
197
|
+
type: "set_input",
|
|
198
|
+
element: {
|
|
199
|
+
role: cmd.role,
|
|
200
|
+
name: cmd.label,
|
|
201
|
+
},
|
|
202
|
+
value: cmd.value,
|
|
203
|
+
parameters: [cmd.value],
|
|
204
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
case "extract_attribute": {
|
|
208
|
+
return {
|
|
209
|
+
type: "extract_attribute",
|
|
210
|
+
element: {
|
|
211
|
+
role: cmd.role,
|
|
212
|
+
name: cmd.label,
|
|
213
|
+
},
|
|
214
|
+
parameters: [cmd.selectedField, cmd.variableName],
|
|
215
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
216
|
+
regex: cmd.regex,
|
|
217
|
+
trimSpaces: cmd.trimSpaces,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
case "extract_property": {
|
|
221
|
+
return {
|
|
222
|
+
type: "extract_property",
|
|
223
|
+
element: {
|
|
224
|
+
role: cmd.role,
|
|
225
|
+
name: cmd.label,
|
|
226
|
+
},
|
|
227
|
+
parameters: [cmd.selectedField, cmd.variableName],
|
|
228
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
229
|
+
regex: cmd.regex,
|
|
230
|
+
trimSpaces: cmd.trimSpaces,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
case "verify_element_attribute": {
|
|
234
|
+
return {
|
|
235
|
+
type: "verify_element_attribute",
|
|
236
|
+
element: {
|
|
237
|
+
role: cmd.role,
|
|
238
|
+
name: cmd.label,
|
|
239
|
+
},
|
|
240
|
+
parameters: [cmd.selectedField, cmd.value],
|
|
241
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
case "verify_element_property": {
|
|
245
|
+
return {
|
|
246
|
+
type: "verify_element_property",
|
|
247
|
+
element: {
|
|
248
|
+
role: cmd.role,
|
|
249
|
+
name: cmd.label,
|
|
250
|
+
},
|
|
251
|
+
parameters: [cmd.selectedField, cmd.value],
|
|
252
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
253
|
+
valueInStepText: stepText && typeof stepText === "string" ? stepText.includes(`"${cmd.value}"`) : false,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
case "conditional_wait": {
|
|
257
|
+
return {
|
|
258
|
+
type: "conditional_wait",
|
|
259
|
+
element: {
|
|
260
|
+
role: cmd.role,
|
|
261
|
+
name: cmd.label,
|
|
262
|
+
},
|
|
263
|
+
parameters: [cmd.timeout, cmd.selectedField, cmd.value],
|
|
264
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
265
|
+
valueInStepText: stepText && typeof stepText === "string" ? stepText.includes(`"${cmd.value}"`) : false,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
case "navigate": {
|
|
269
|
+
return {
|
|
270
|
+
type: "navigate",
|
|
271
|
+
parameters: [cmd.value],
|
|
272
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
case "browser_go_back": {
|
|
276
|
+
return {
|
|
277
|
+
type: "browser_go_back",
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
case "browser_go_forward": {
|
|
281
|
+
return {
|
|
282
|
+
type: "browser_go_forward",
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
case "set_input_files": {
|
|
286
|
+
return {
|
|
287
|
+
type: "set_input_files",
|
|
288
|
+
element: {
|
|
289
|
+
role: cmd.role,
|
|
290
|
+
name: cmd.label,
|
|
291
|
+
},
|
|
292
|
+
parameters: [cmd.files],
|
|
293
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
case "verify_page_snapshot": {
|
|
297
|
+
return {
|
|
298
|
+
type: "verify_page_snapshot",
|
|
299
|
+
parameters: [cmd.value],
|
|
300
|
+
selectors: cmd.selectors,
|
|
301
|
+
data: cmd.data,
|
|
302
|
+
valueInStepText: stepText && typeof stepText === "string" ? stepText.includes(`"${cmd.value}"`) : false,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
default: {
|
|
306
|
+
return {
|
|
307
|
+
type: cmd.type,
|
|
308
|
+
parameters: [cmd.value],
|
|
309
|
+
lastKnownUrlPath: cmd.lastKnownUrlPath,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
function getBestStrategy(allStrategyLocators) {
|
|
316
|
+
const orderedPriorities = ["custom", "context", "basic", "text_with_index", "ignore_digit", "no_text"];
|
|
317
|
+
for (const strategy of orderedPriorities) {
|
|
318
|
+
if (allStrategyLocators[strategy] && allStrategyLocators[strategy].length > 0) {
|
|
319
|
+
return strategy;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const _parameterizeLocators = (locators, replacementFromValue, replacementToValue) => {
|
|
326
|
+
for (const loc of locators) {
|
|
327
|
+
if (loc?.css?.includes(replacementFromValue)) {
|
|
328
|
+
loc.css = loc.css.replaceAll(replacementFromValue, replacementToValue);
|
|
329
|
+
}
|
|
330
|
+
if (loc?.text?.includes(replacementFromValue)) {
|
|
331
|
+
loc.text = loc.text.replaceAll(replacementFromValue, replacementToValue);
|
|
332
|
+
}
|
|
333
|
+
if (loc?.climb && typeof loc.climb === "string" && loc.climb?.includes(replacementFromValue)) {
|
|
334
|
+
loc.climb = loc.climb.replaceAll(replacementFromValue, replacementToValue);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return locators;
|
|
338
|
+
};
|
|
339
|
+
const parameterizeLocators = ({ cmd, locs, isValueVariable, isTargetValueVariable, parametersMap }) => {
|
|
340
|
+
if (isValueVariable) {
|
|
341
|
+
const variable = cmd.value.slice(1, -1);
|
|
342
|
+
// const val = parametersMap[variable];
|
|
343
|
+
if (typeof cmd.text === "string") {
|
|
344
|
+
const replacementFromValue = cmd.text.trim().replace(/\s+/g, " ") ?? ""; // val.trim();
|
|
345
|
+
if (replacementFromValue.length > 0) {
|
|
346
|
+
const replacementToValue = `{${variable}}`;
|
|
347
|
+
locs = _parameterizeLocators(locs, replacementFromValue, replacementToValue);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (isTargetValueVariable) {
|
|
352
|
+
const variable = cmd.targetValue.slice(1, -1);
|
|
353
|
+
// const val = parametersMap[variable];
|
|
354
|
+
if (typeof cmd.targetText === "string") {
|
|
355
|
+
const replacementFromValue = cmd.targetText.trim().replace(/\s+/g, " ") ?? ""; // val.trim();
|
|
356
|
+
if (replacementFromValue.length > 0) {
|
|
357
|
+
const replacementToValue = `{${variable}}`;
|
|
358
|
+
locs = _parameterizeLocators(locs, replacementFromValue, replacementToValue);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return locs;
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
//TODO: IMPORTAN
|
|
366
|
+
export const toRecordingStep = (cmd, parametersMap, stepText) => {
|
|
367
|
+
if (cmd.type === "api") {
|
|
368
|
+
return {
|
|
369
|
+
type: "api",
|
|
370
|
+
value: cmd.value,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
const step = _toRecordingStep(cmd, stepText);
|
|
374
|
+
const cmdID = {
|
|
375
|
+
cmdId: cmd.id,
|
|
376
|
+
};
|
|
377
|
+
Object.assign(step, cmdID);
|
|
378
|
+
|
|
379
|
+
const locatorsObject = JSON.parse(JSON.stringify(cmd.locators ?? null));
|
|
380
|
+
|
|
381
|
+
if (!locatorsObject) return step;
|
|
382
|
+
|
|
383
|
+
const element_name = cmd?.locators?.element_name ?? `${cmd.label} ${cmd.role ?? "Text"}`;
|
|
384
|
+
locatorsObject.element_name = element_name;
|
|
385
|
+
|
|
386
|
+
const isValueVariable = isVariable(cmd.value);
|
|
387
|
+
const isTargetValueVariable = isVariable(cmd.targetValue);
|
|
388
|
+
const allStrategyLocators = JSON.parse(JSON.stringify(cmd?.allStrategyLocators ?? null));
|
|
389
|
+
step.locators = locatorsObject;
|
|
390
|
+
step.allStrategyLocators = allStrategyLocators;
|
|
391
|
+
step.isLocatorsAssigned = true;
|
|
392
|
+
|
|
393
|
+
if (!isValueVariable && !isTargetValueVariable) {
|
|
394
|
+
return step;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (isValueVariable) {
|
|
398
|
+
step.dataSource = "parameters";
|
|
399
|
+
step.dataKey = convertToIdentifier(cmd.value.slice(1, -1));
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (!allStrategyLocators) {
|
|
403
|
+
let locs = locatorsObject.locators;
|
|
404
|
+
locs = parameterizeLocators({
|
|
405
|
+
cmd,
|
|
406
|
+
locs,
|
|
407
|
+
isValueVariable,
|
|
408
|
+
isTargetValueVariable,
|
|
409
|
+
parametersMap,
|
|
410
|
+
});
|
|
411
|
+
locatorsObject.locators = locs;
|
|
412
|
+
return {
|
|
413
|
+
...step,
|
|
414
|
+
locators: locatorsObject,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
for (const key in allStrategyLocators) {
|
|
419
|
+
if (key === "strategy") continue;
|
|
420
|
+
if (key === "no_text" || key === "custom") continue;
|
|
421
|
+
const locators = allStrategyLocators[key];
|
|
422
|
+
if (locators.length === 0) continue;
|
|
423
|
+
parameterizeLocators({
|
|
424
|
+
cmd,
|
|
425
|
+
locs: locators,
|
|
426
|
+
isValueVariable,
|
|
427
|
+
isTargetValueVariable,
|
|
428
|
+
parametersMap,
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
locatorsObject.locators = allStrategyLocators[allStrategyLocators.strategy] ?? locatorsObject.locators;
|
|
433
|
+
|
|
434
|
+
return {
|
|
435
|
+
...step,
|
|
436
|
+
locators: locatorsObject,
|
|
437
|
+
allStrategyLocators,
|
|
438
|
+
isLocatorsAssigned: true,
|
|
439
|
+
};
|
|
440
|
+
};
|
|
441
|
+
|
|
17
442
|
export const toMethodName = (str) => {
|
|
18
443
|
// Remove any non-word characters (excluding underscore) and trim spaces
|
|
19
444
|
let cleanStr = str.trim().replace(/[^\w\s]/gi, "");
|
|
@@ -36,7 +461,7 @@ export function getCodePage(stepDefsFilePath) {
|
|
|
36
461
|
export function getCucumberStep({ step }) {
|
|
37
462
|
const cucumberStep = new Step();
|
|
38
463
|
cucumberStep.loadFromJson({
|
|
39
|
-
text: step.text,
|
|
464
|
+
text: step.renamedText ? step.renamedText : step.text,
|
|
40
465
|
keyword: step.keyword,
|
|
41
466
|
keywordType: step.keywordType,
|
|
42
467
|
parameters: [],
|
|
@@ -54,7 +479,7 @@ export function getCucumberStep({ step }) {
|
|
|
54
479
|
|
|
55
480
|
function makeStepTextUnique(step, stepsDefinitions) {
|
|
56
481
|
// const utilsFilePath = path.join("features", "step_definitions", "utils.mjs");
|
|
57
|
-
let stepText = step.text;
|
|
482
|
+
let stepText = step.renamedText ? step.renamedText : step.text;
|
|
58
483
|
let stepIndex = 1;
|
|
59
484
|
// console.log("makeStepTextUnique", step.text);
|
|
60
485
|
let stepDef = stepsDefinitions.findMatchingStep(stepText);
|
|
@@ -70,29 +495,38 @@ function makeStepTextUnique(step, stepsDefinitions) {
|
|
|
70
495
|
step.text = stepText;
|
|
71
496
|
}
|
|
72
497
|
|
|
73
|
-
export async function saveRecording({
|
|
498
|
+
export async function saveRecording({
|
|
499
|
+
step,
|
|
500
|
+
cucumberStep,
|
|
501
|
+
codePage,
|
|
502
|
+
projectDir,
|
|
503
|
+
stepsDefinitions,
|
|
504
|
+
parametersMap,
|
|
505
|
+
logger,
|
|
506
|
+
}) {
|
|
507
|
+
if (step.commands && Array.isArray(step.commands)) {
|
|
508
|
+
step.commands = step.commands.map((cmd) => toRecordingStep(cmd, parametersMap, step.text));
|
|
509
|
+
}
|
|
74
510
|
let routesPath = path.join(tmpdir(), "blinq_temp_routes");
|
|
75
511
|
|
|
76
|
-
if (process.env.TEMP_RUN) {
|
|
512
|
+
if (process.env.TEMP_RUN === "true") {
|
|
77
513
|
if (existsSync(routesPath)) {
|
|
78
514
|
rmSync(routesPath, { recursive: true });
|
|
79
515
|
}
|
|
80
516
|
mkdirSync(routesPath, { recursive: true });
|
|
81
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
517
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
82
518
|
} else {
|
|
83
519
|
if (existsSync(routesPath)) {
|
|
84
|
-
// remove the folder
|
|
85
520
|
try {
|
|
86
521
|
rmSync(routesPath, { recursive: true });
|
|
87
|
-
console.log("Removed temp_routes_folder:", routesPath);
|
|
88
522
|
} catch (error) {
|
|
89
|
-
|
|
523
|
+
logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
90
524
|
}
|
|
91
525
|
routesPath = path.join(projectDir, "data", "routes");
|
|
92
526
|
if (!existsSync(routesPath)) {
|
|
93
527
|
mkdirSync(routesPath, { recursive: true });
|
|
94
528
|
}
|
|
95
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
529
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
96
530
|
}
|
|
97
531
|
}
|
|
98
532
|
|
|
@@ -101,46 +535,51 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
101
535
|
}
|
|
102
536
|
|
|
103
537
|
if (step.isImplemented && step.shouldOverride) {
|
|
104
|
-
|
|
538
|
+
const stepDef = stepsDefinitions.findMatchingStep(step.text);
|
|
105
539
|
codePage = getCodePage(stepDef.file);
|
|
106
540
|
} else {
|
|
107
541
|
const isUtilStep = makeStepTextUnique(step, stepsDefinitions);
|
|
108
542
|
|
|
109
543
|
if (isUtilStep) {
|
|
110
|
-
|
|
544
|
+
if (!step.renamedText) {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
step.text = step.text.trim();
|
|
548
|
+
const { functionName } = stepsDefinitions.findMatchingStep(step.renamedText);
|
|
549
|
+
step.renamedText = functionName;
|
|
550
|
+
const newImportLine = `import { ${functionName} } from "./utils.mjs";\n`;
|
|
551
|
+
|
|
552
|
+
if (!codePage.fileContent.includes(newImportLine)) {
|
|
553
|
+
codePage.fileContent = newImportLine + codePage.fileContent;
|
|
554
|
+
}
|
|
111
555
|
}
|
|
112
556
|
}
|
|
113
557
|
|
|
558
|
+
routesPath = path.join(tmpdir(), "blinq_temp_routes");
|
|
114
559
|
if (process.env.TEMP_RUN === "true") {
|
|
115
|
-
console.log("Save routes in temp folder for running:", routesPath);
|
|
116
560
|
if (existsSync(routesPath)) {
|
|
117
|
-
console.log("Removing existing temp_routes_folder:", routesPath);
|
|
118
561
|
rmSync(routesPath, { recursive: true });
|
|
119
562
|
}
|
|
120
563
|
mkdirSync(routesPath, { recursive: true });
|
|
121
|
-
|
|
122
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
564
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
123
565
|
} else {
|
|
124
|
-
console.log("Saving routes in project directory:", projectDir);
|
|
125
566
|
if (existsSync(routesPath)) {
|
|
126
|
-
// remove the folder
|
|
127
567
|
try {
|
|
128
568
|
rmSync(routesPath, { recursive: true });
|
|
129
|
-
console.log("Removed temp_routes_folder:", routesPath);
|
|
130
569
|
} catch (error) {
|
|
131
|
-
|
|
570
|
+
logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
132
571
|
}
|
|
133
572
|
}
|
|
134
573
|
routesPath = path.join(projectDir, "data", "routes");
|
|
135
|
-
console.log("Saving routes to:", routesPath);
|
|
136
574
|
if (!existsSync(routesPath)) {
|
|
137
575
|
mkdirSync(routesPath, { recursive: true });
|
|
138
576
|
}
|
|
139
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
577
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
140
578
|
}
|
|
141
579
|
|
|
142
580
|
cucumberStep.text = step.text;
|
|
143
581
|
const recording = new Recording();
|
|
582
|
+
|
|
144
583
|
const steps = step.commands;
|
|
145
584
|
|
|
146
585
|
recording.loadFromObject({ steps, step: cucumberStep });
|
|
@@ -191,7 +630,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
191
630
|
stepsDefinitions
|
|
192
631
|
);
|
|
193
632
|
|
|
194
|
-
if (!step.isImplemented) {
|
|
633
|
+
if (!step.renamedText && !(step.isImplemented && step.shouldOverride)) {
|
|
195
634
|
stepsDefinitions.addStep({
|
|
196
635
|
name: step.text,
|
|
197
636
|
file: result.codePage.sourceFileName,
|
|
@@ -204,12 +643,14 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
204
643
|
} else {
|
|
205
644
|
const generateCodeResult = generateCode(recording, codePage, userData, projectDir, methodName);
|
|
206
645
|
if (generateCodeResult.noCode === true) {
|
|
207
|
-
logger.log("No code generated for step: " + step.text);
|
|
208
646
|
return generateCodeResult.page;
|
|
209
647
|
}
|
|
210
648
|
codePage = generateCodeResult.page;
|
|
211
649
|
methodName = generateCodeResult.methodName;
|
|
212
|
-
|
|
650
|
+
|
|
651
|
+
if (!step.renamedText) {
|
|
652
|
+
codePage.insertElements(generateCodeResult.elements);
|
|
653
|
+
}
|
|
213
654
|
|
|
214
655
|
const description = cucumberStep.text;
|
|
215
656
|
let path = null;
|
|
@@ -222,15 +663,32 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
222
663
|
protect = true;
|
|
223
664
|
}
|
|
224
665
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
666
|
+
|
|
667
|
+
if (step.renamedText) {
|
|
668
|
+
codePage.addInfraCommandUtil(
|
|
669
|
+
methodName,
|
|
670
|
+
description,
|
|
671
|
+
cucumberStep.parameters,
|
|
672
|
+
generateCodeResult.codeLines,
|
|
673
|
+
step.renamedText,
|
|
674
|
+
step.text,
|
|
675
|
+
parametersMap,
|
|
676
|
+
protect,
|
|
677
|
+
"recorder",
|
|
678
|
+
path
|
|
679
|
+
);
|
|
680
|
+
} else {
|
|
681
|
+
codePage.addInfraCommand(
|
|
682
|
+
methodName,
|
|
683
|
+
description,
|
|
684
|
+
cucumberStep.getVariablesList(),
|
|
685
|
+
generateCodeResult.codeLines,
|
|
686
|
+
protect,
|
|
687
|
+
"recorder",
|
|
688
|
+
path
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
|
|
234
692
|
const keyword = (cucumberStep.keywordAlias ?? cucumberStep.keyword).trim();
|
|
235
693
|
const stepResult = codePage.addCucumberStep(
|
|
236
694
|
keyword,
|
|
@@ -240,7 +698,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
240
698
|
step.finalTimeout
|
|
241
699
|
);
|
|
242
700
|
|
|
243
|
-
if (!step.isImplemented) {
|
|
701
|
+
if (!step.renamedText && !(step.isImplemented && step.shouldOverride)) {
|
|
244
702
|
stepsDefinitions.addStep({
|
|
245
703
|
name: step.text,
|
|
246
704
|
file: codePage.sourceFileName,
|
|
@@ -249,6 +707,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
|
|
|
249
707
|
}
|
|
250
708
|
|
|
251
709
|
codePage.removeUnusedElements();
|
|
710
|
+
codePage.mergeSimilarElements();
|
|
252
711
|
cucumberStep.methodName = methodName;
|
|
253
712
|
if (generateCodeResult.locatorsMetadata) {
|
|
254
713
|
codePage.addLocatorsMetadata(generateCodeResult.locatorsMetadata);
|
|
@@ -292,7 +751,7 @@ export const getCommandsForImplementedStep = (stepName, stepsDefinitions, stepPa
|
|
|
292
751
|
const file = step?.file;
|
|
293
752
|
const locatorsJson = getLocatorsJson(file);
|
|
294
753
|
if (!step) {
|
|
295
|
-
throw new Error(
|
|
754
|
+
throw new Error(`Step definition not found: ${stepName}`);
|
|
296
755
|
}
|
|
297
756
|
isImplemented = true;
|
|
298
757
|
const { codeCommands, codePage, elements, parametersNames, error } =
|
|
@@ -300,26 +759,35 @@ export const getCommandsForImplementedStep = (stepName, stepsDefinitions, stepPa
|
|
|
300
759
|
if (error) {
|
|
301
760
|
throw new Error(error);
|
|
302
761
|
}
|
|
762
|
+
isUtilStep = codePage.sourceFileName.endsWith("utils.mjs");
|
|
303
763
|
|
|
304
764
|
if (parametersNames.length !== stepParams.length) {
|
|
305
765
|
// console.log("Parameters mismatch", parametersNames, stepParams);
|
|
306
766
|
throw new Error("Parameters mismatch");
|
|
307
767
|
}
|
|
308
|
-
for (let i = 0; i < parametersNames.length; i++) {
|
|
309
|
-
stepParams[i].argumentName = parametersNames[i];
|
|
310
|
-
}
|
|
311
768
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
769
|
+
const pattern = step.name;
|
|
770
|
+
if (isUtilStep && pattern === "Verify the file {string} exists") {
|
|
771
|
+
commands.push({
|
|
772
|
+
type: "verify_file_exists",
|
|
773
|
+
parameters: [stepParams[0].text],
|
|
774
|
+
});
|
|
775
|
+
} else {
|
|
776
|
+
for (let i = 0; i < parametersNames.length; i++) {
|
|
777
|
+
stepParams[i].argumentName = parametersNames[i];
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
for (const { code } of codeCommands) {
|
|
781
|
+
const command = invertCodeToCommand(code, elements, stepParams, stepsDefinitions, codePage, stepName)[0];
|
|
782
|
+
if (command === undefined || command.type === null) continue;
|
|
783
|
+
if (command.element) {
|
|
784
|
+
const key = command.element.key;
|
|
785
|
+
if (key && locatorsJson[key]) {
|
|
786
|
+
command.allStrategyLocators = locatorsJson[key];
|
|
787
|
+
}
|
|
320
788
|
}
|
|
789
|
+
commands.push(command);
|
|
321
790
|
}
|
|
322
|
-
commands.push(command);
|
|
323
791
|
}
|
|
324
792
|
} catch (error) {
|
|
325
793
|
console.error(error);
|
|
@@ -367,7 +835,7 @@ export async function executeStep({ stepsDefinitions, cucumberStep, context, cod
|
|
|
367
835
|
}
|
|
368
836
|
}
|
|
369
837
|
|
|
370
|
-
export async function updateStepDefinitions({ scenario, featureName, projectDir }) {
|
|
838
|
+
export async function updateStepDefinitions({ scenario, featureName, projectDir, logger }) {
|
|
371
839
|
// set the candidate step definition file name
|
|
372
840
|
// set the utils file path
|
|
373
841
|
const utilsFilePath = path.join(projectDir, "features", "step_definitions", "utils.mjs");
|
|
@@ -401,40 +869,43 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
|
|
|
401
869
|
if ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0) {
|
|
402
870
|
let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
|
|
403
871
|
if (process.env.TEMP_RUN === "true") {
|
|
404
|
-
console.log("Save routes in temp folder for running:", routesPath);
|
|
405
872
|
if (existsSync(routesPath)) {
|
|
406
|
-
|
|
873
|
+
routesPath = path.join(tmpdir(), `blinq_temp_routes`);
|
|
407
874
|
rmSync(routesPath, { recursive: true });
|
|
408
875
|
}
|
|
409
876
|
mkdirSync(routesPath, { recursive: true });
|
|
410
|
-
|
|
411
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
877
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
412
878
|
} else {
|
|
413
|
-
console.log("Saving routes in project directory:", projectDir);
|
|
414
879
|
if (existsSync(routesPath)) {
|
|
415
|
-
// remove the folder
|
|
416
880
|
try {
|
|
417
881
|
rmSync(routesPath, { recursive: true });
|
|
418
|
-
console.log("Removed temp_routes_folder:", routesPath);
|
|
419
882
|
} catch (error) {
|
|
420
|
-
|
|
883
|
+
logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
421
884
|
}
|
|
422
885
|
}
|
|
423
886
|
routesPath = path.join(projectDir, "data", "routes");
|
|
424
|
-
console.log("Saving routes to:", routesPath);
|
|
425
887
|
if (!existsSync(routesPath)) {
|
|
426
888
|
mkdirSync(routesPath, { recursive: true });
|
|
427
889
|
}
|
|
428
|
-
saveRoutes({ step, folderPath: routesPath });
|
|
890
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
891
|
+
}
|
|
892
|
+
if (step.commands && Array.isArray(step.commands)) {
|
|
893
|
+
step.commands = step.commands.map((cmd) => toRecordingStep(cmd, scenario.parametersMap));
|
|
429
894
|
}
|
|
430
895
|
continue;
|
|
431
896
|
}
|
|
432
897
|
const cucumberStep = getCucumberStep({ step });
|
|
433
898
|
const pageName = generatePageName(step.startFrame?.url ?? "default");
|
|
434
899
|
const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
|
|
435
|
-
// path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
|
|
436
900
|
let codePage = getCodePage(stepDefsFilePath);
|
|
437
|
-
codePage = await saveRecording({
|
|
901
|
+
codePage = await saveRecording({
|
|
902
|
+
step,
|
|
903
|
+
cucumberStep,
|
|
904
|
+
codePage,
|
|
905
|
+
projectDir,
|
|
906
|
+
stepsDefinitions,
|
|
907
|
+
parametersMap: scenario.parametersMap,
|
|
908
|
+
});
|
|
438
909
|
if (!codePage) {
|
|
439
910
|
continue;
|
|
440
911
|
}
|
|
@@ -446,7 +917,7 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
|
|
|
446
917
|
writeFileSync(utilsFilePath, utilsContent, "utf8");
|
|
447
918
|
}
|
|
448
919
|
|
|
449
|
-
export function saveRoutes({ step, folderPath }) {
|
|
920
|
+
export function saveRoutes({ step, folderPath }, logger) {
|
|
450
921
|
const routeItems = step.routeItems;
|
|
451
922
|
if (!routeItems || routeItems.length === 0) {
|
|
452
923
|
return;
|
|
@@ -469,21 +940,18 @@ export function saveRoutes({ step, folderPath }) {
|
|
|
469
940
|
};
|
|
470
941
|
});
|
|
471
942
|
|
|
472
|
-
const routesFilePath = path.join(folderPath, stepNameHash
|
|
473
|
-
console.log("Routes file path:", routesFilePath);
|
|
943
|
+
const routesFilePath = path.join(folderPath, `${stepNameHash}.json`);
|
|
474
944
|
const routesData = {
|
|
475
945
|
template,
|
|
476
946
|
routes: routeItemsWithFilters,
|
|
477
947
|
};
|
|
478
|
-
console.log("Routes data to save:", routesData);
|
|
479
948
|
|
|
480
949
|
if (!existsSync(folderPath)) {
|
|
481
950
|
mkdirSync(folderPath, { recursive: true });
|
|
482
951
|
}
|
|
483
952
|
try {
|
|
484
953
|
writeFileSync(routesFilePath, JSON.stringify(routesData, null, 2), "utf8");
|
|
485
|
-
console.log("Saved routes to", routesFilePath);
|
|
486
954
|
} catch (error) {
|
|
487
|
-
|
|
955
|
+
logger.error(`Error saving routes to ${routesFilePath}: ${getErrorMessage(error)}`);
|
|
488
956
|
}
|
|
489
957
|
}
|