@dev-blinq/cucumber_client 1.0.1409-dev → 1.0.1409-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.
Files changed (50) hide show
  1. package/bin/assets/bundled_scripts/recorder.js +105 -105
  2. package/bin/assets/preload/css_gen.js +10 -10
  3. package/bin/assets/preload/toolbar.js +27 -29
  4. package/bin/assets/preload/unique_locators.js +1 -1
  5. package/bin/assets/preload/yaml.js +288 -275
  6. package/bin/assets/scripts/aria_snapshot.js +223 -220
  7. package/bin/assets/scripts/dom_attr.js +329 -329
  8. package/bin/assets/scripts/dom_parent.js +169 -174
  9. package/bin/assets/scripts/event_utils.js +94 -94
  10. package/bin/assets/scripts/pw.js +2050 -1949
  11. package/bin/assets/scripts/recorder.js +70 -45
  12. package/bin/assets/scripts/snapshot_capturer.js +147 -147
  13. package/bin/assets/scripts/unique_locators.js +163 -44
  14. package/bin/assets/scripts/yaml.js +796 -783
  15. package/bin/assets/templates/_hooks_template.txt +6 -2
  16. package/bin/assets/templates/utils_template.txt +16 -16
  17. package/bin/client/code_cleanup/find_step_definition_references.js +0 -1
  18. package/bin/client/code_cleanup/utils.js +5 -1
  19. package/bin/client/code_gen/api_codegen.js +2 -2
  20. package/bin/client/code_gen/code_inversion.js +63 -2
  21. package/bin/client/code_gen/function_signature.js +4 -0
  22. package/bin/client/code_gen/page_reflection.js +846 -906
  23. package/bin/client/code_gen/playwright_codeget.js +27 -3
  24. package/bin/client/cucumber/feature.js +4 -0
  25. package/bin/client/cucumber/feature_data.js +2 -2
  26. package/bin/client/cucumber/project_to_document.js +8 -2
  27. package/bin/client/cucumber/steps_definitions.js +19 -3
  28. package/bin/client/cucumber_selector.js +4 -0
  29. package/bin/client/local_agent.js +3 -2
  30. package/bin/client/parse_feature_file.js +23 -26
  31. package/bin/client/playground/projects/env.json +2 -2
  32. package/bin/client/project.js +186 -202
  33. package/bin/client/recorderv3/bvt_init.js +363 -0
  34. package/bin/client/recorderv3/bvt_recorder.js +1038 -76
  35. package/bin/client/recorderv3/implemented_steps.js +2 -0
  36. package/bin/client/recorderv3/index.js +4 -311
  37. package/bin/client/recorderv3/scriptTest.js +1 -1
  38. package/bin/client/recorderv3/services.js +814 -154
  39. package/bin/client/recorderv3/step_runner.js +36 -10
  40. package/bin/client/recorderv3/step_utils.js +503 -51
  41. package/bin/client/recorderv3/update_feature.js +9 -5
  42. package/bin/client/recorderv3/wbr_entry.js +61 -0
  43. package/bin/client/recording.js +1 -0
  44. package/bin/client/upload-service.js +3 -2
  45. package/bin/client/utils/socket_logger.js +132 -0
  46. package/bin/index.js +4 -1
  47. package/bin/logger.js +3 -2
  48. package/bin/min/consoleApi.min.cjs +2 -3
  49. package/bin/min/injectedScript.min.cjs +16 -16
  50. package/package.json +19 -9
@@ -14,6 +14,428 @@ import { createHash } from "crypto";
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) => {
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
+ };
254
+ }
255
+ case "conditional_wait": {
256
+ return {
257
+ type: "conditional_wait",
258
+ element: {
259
+ role: cmd.role,
260
+ name: cmd.label,
261
+ },
262
+ parameters: [cmd.timeout, cmd.selectedField, cmd.value],
263
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
264
+ };
265
+ }
266
+ case "navigate": {
267
+ return {
268
+ type: "navigate",
269
+ parameters: [cmd.value],
270
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
271
+ };
272
+ }
273
+ case "browser_go_back": {
274
+ return {
275
+ type: "browser_go_back",
276
+ };
277
+ }
278
+ case "browser_go_forward": {
279
+ return {
280
+ type: "browser_go_forward",
281
+ };
282
+ }
283
+ case "set_input_files": {
284
+ return {
285
+ type: "set_input_files",
286
+ element: {
287
+ role: cmd.role,
288
+ name: cmd.label,
289
+ },
290
+ parameters: [cmd.files],
291
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
292
+ };
293
+ }
294
+ case "verify_page_snapshot": {
295
+ return {
296
+ type: "verify_page_snapshot",
297
+ parameters: [cmd.value],
298
+ selectors: cmd.selectors,
299
+ data: cmd.data,
300
+ };
301
+ }
302
+ default: {
303
+ return {
304
+ type: cmd.type,
305
+ parameters: [cmd.value],
306
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
307
+ };
308
+ }
309
+ }
310
+ };
311
+
312
+ function getBestStrategy(allStrategyLocators) {
313
+ const orderedPriorities = ["custom", "context", "basic", "text_with_index", "ignore_digit", "no_text"];
314
+ for (const strategy of orderedPriorities) {
315
+ if (allStrategyLocators[strategy] && allStrategyLocators[strategy].length > 0) {
316
+ return strategy;
317
+ }
318
+ }
319
+ return null;
320
+ }
321
+
322
+ const _parameterizeLocators = (locators, replacementFromValue, replacementToValue) => {
323
+ for (const loc of locators) {
324
+ if (loc?.css?.includes(replacementFromValue)) {
325
+ loc.css = loc.css.replaceAll(replacementFromValue, replacementToValue);
326
+ }
327
+ if (loc?.text?.includes(replacementFromValue)) {
328
+ loc.text = loc.text.replaceAll(replacementFromValue, replacementToValue);
329
+ }
330
+ if (loc?.climb && typeof loc.climb === "string" && loc.climb?.includes(replacementFromValue)) {
331
+ loc.climb = loc.climb.replaceAll(replacementFromValue, replacementToValue);
332
+ }
333
+ }
334
+ return locators;
335
+ };
336
+ const parameterizeLocators = ({ cmd, locs, isValueVariable, isTargetValueVariable, parametersMap }) => {
337
+ if (isValueVariable) {
338
+ const variable = cmd.value.slice(1, -1);
339
+ // const val = parametersMap[variable];
340
+ if (typeof cmd.text === "string") {
341
+ const replacementFromValue = cmd.text.trim().replace(/\s+/g, " ") ?? ""; // val.trim();
342
+ if (replacementFromValue.length > 0) {
343
+ const replacementToValue = `{${variable}}`;
344
+ locs = _parameterizeLocators(locs, replacementFromValue, replacementToValue);
345
+ }
346
+ }
347
+ }
348
+ if (isTargetValueVariable) {
349
+ const variable = cmd.targetValue.slice(1, -1);
350
+ // const val = parametersMap[variable];
351
+ if (typeof cmd.targetText === "string") {
352
+ const replacementFromValue = cmd.targetText.trim().replace(/\s+/g, " ") ?? ""; // val.trim();
353
+ if (replacementFromValue.length > 0) {
354
+ const replacementToValue = `{${variable}}`;
355
+ locs = _parameterizeLocators(locs, replacementFromValue, replacementToValue);
356
+ }
357
+ }
358
+ }
359
+ return locs;
360
+ };
361
+
362
+ //TODO: IMPORTAN
363
+ export const toRecordingStep = (cmd, parametersMap) => {
364
+ if (cmd.type === "api") {
365
+ return {
366
+ type: "api",
367
+ value: cmd.value,
368
+ };
369
+ }
370
+ const step = _toRecordingStep(cmd);
371
+ const cmdID = {
372
+ cmdId: cmd.id,
373
+ };
374
+ Object.assign(step, cmdID);
375
+
376
+ const locatorsObject = JSON.parse(JSON.stringify(cmd.locators ?? null));
377
+
378
+ if (!locatorsObject) return step;
379
+
380
+ const element_name = cmd?.locators?.element_name ?? `${cmd.label} ${cmd.role ?? "Text"}`;
381
+ locatorsObject.element_name = element_name;
382
+
383
+ const isValueVariable = isVariable(cmd.value);
384
+ const isTargetValueVariable = isVariable(cmd.targetValue);
385
+ const allStrategyLocators = JSON.parse(JSON.stringify(cmd?.allStrategyLocators ?? null));
386
+ step.locators = locatorsObject;
387
+ step.allStrategyLocators = allStrategyLocators;
388
+ step.isLocatorsAssigned = true;
389
+
390
+ if (!isValueVariable && !isTargetValueVariable) {
391
+ return step;
392
+ }
393
+
394
+ if (isValueVariable) {
395
+ step.dataSource = "parameters";
396
+ step.dataKey = convertToIdentifier(cmd.value.slice(1, -1));
397
+ }
398
+
399
+ if (!allStrategyLocators) {
400
+ let locs = locatorsObject.locators;
401
+ locs = parameterizeLocators({
402
+ cmd,
403
+ locs,
404
+ isValueVariable,
405
+ isTargetValueVariable,
406
+ parametersMap,
407
+ });
408
+ locatorsObject.locators = locs;
409
+ return {
410
+ ...step,
411
+ locators: locatorsObject,
412
+ };
413
+ }
414
+
415
+ for (const key in allStrategyLocators) {
416
+ if (key === "strategy") continue;
417
+ if (key === "no_text" || key === "custom") continue;
418
+ const locators = allStrategyLocators[key];
419
+ if (locators.length === 0) continue;
420
+ parameterizeLocators({
421
+ cmd,
422
+ locs: locators,
423
+ isValueVariable,
424
+ isTargetValueVariable,
425
+ parametersMap,
426
+ });
427
+ }
428
+
429
+ locatorsObject.locators = allStrategyLocators[allStrategyLocators.strategy] ?? locatorsObject.locators;
430
+
431
+ return {
432
+ ...step,
433
+ locators: locatorsObject,
434
+ allStrategyLocators,
435
+ isLocatorsAssigned: true,
436
+ };
437
+ };
438
+
17
439
  export const toMethodName = (str) => {
18
440
  // Remove any non-word characters (excluding underscore) and trim spaces
19
441
  let cleanStr = str.trim().replace(/[^\w\s]/gi, "");
@@ -54,7 +476,7 @@ export function getCucumberStep({ step }) {
54
476
 
55
477
  function makeStepTextUnique(step, stepsDefinitions) {
56
478
  // const utilsFilePath = path.join("features", "step_definitions", "utils.mjs");
57
- let stepText = step.text;
479
+ let stepText = step.renamedText ? step.renamedText : step.text;
58
480
  let stepIndex = 1;
59
481
  // console.log("makeStepTextUnique", step.text);
60
482
  let stepDef = stepsDefinitions.findMatchingStep(stepText);
@@ -70,10 +492,13 @@ function makeStepTextUnique(step, stepsDefinitions) {
70
492
  step.text = stepText;
71
493
  }
72
494
 
73
- export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions }) {
495
+ export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions, parametersMap }) {
496
+ if (step.commands && Array.isArray(step.commands)) {
497
+ step.commands = step.commands.map((cmd) => toRecordingStep(cmd, parametersMap));
498
+ }
74
499
  let routesPath = path.join(tmpdir(), "blinq_temp_routes");
75
500
 
76
- if (process.env.TEMP_RUN) {
501
+ if (process.env.TEMP_RUN === "true") {
77
502
  if (existsSync(routesPath)) {
78
503
  rmSync(routesPath, { recursive: true });
79
504
  }
@@ -84,9 +509,9 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
84
509
  // remove the folder
85
510
  try {
86
511
  rmSync(routesPath, { recursive: true });
87
- console.log("Removed temp_routes_folder:", routesPath);
512
+ //
88
513
  } catch (error) {
89
- console.error("Error removing temp_routes folder", error);
514
+ //
90
515
  }
91
516
  routesPath = path.join(projectDir, "data", "routes");
92
517
  if (!existsSync(routesPath)) {
@@ -107,32 +532,36 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
107
532
  const isUtilStep = makeStepTextUnique(step, stepsDefinitions);
108
533
 
109
534
  if (isUtilStep) {
110
- return;
535
+ if (!step.renamedText) {
536
+ return;
537
+ }
538
+ const { functionName } = stepsDefinitions.findMatchingStep(step.renamedText);
539
+ step.renamedText = functionName;
540
+ const newImportLine = `import { ${functionName} } from "./utils.mjs";\n`;
541
+
542
+ if (!codePage.fileContent.includes(newImportLine)) {
543
+ codePage.fileContent = newImportLine + codePage.fileContent;
544
+ }
111
545
  }
112
546
  }
113
547
 
548
+ routesPath = path.join(tmpdir(), "blinq_temp_routes");
114
549
  if (process.env.TEMP_RUN === "true") {
115
- console.log("Save routes in temp folder for running:", routesPath);
116
550
  if (existsSync(routesPath)) {
117
- console.log("Removing existing temp_routes_folder:", routesPath);
118
551
  rmSync(routesPath, { recursive: true });
119
552
  }
120
553
  mkdirSync(routesPath, { recursive: true });
121
- console.log("Created temp_routes_folder:", routesPath);
122
554
  saveRoutes({ step, folderPath: routesPath });
123
555
  } else {
124
- console.log("Saving routes in project directory:", projectDir);
125
556
  if (existsSync(routesPath)) {
126
557
  // remove the folder
127
558
  try {
128
559
  rmSync(routesPath, { recursive: true });
129
- console.log("Removed temp_routes_folder:", routesPath);
130
560
  } catch (error) {
131
- console.error("Error removing temp_routes folder", error);
561
+ //
132
562
  }
133
563
  }
134
564
  routesPath = path.join(projectDir, "data", "routes");
135
- console.log("Saving routes to:", routesPath);
136
565
  if (!existsSync(routesPath)) {
137
566
  mkdirSync(routesPath, { recursive: true });
138
567
  }
@@ -141,13 +570,14 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
141
570
 
142
571
  cucumberStep.text = step.text;
143
572
  const recording = new Recording();
573
+
144
574
  const steps = step.commands;
145
575
 
146
576
  recording.loadFromObject({ steps, step: cucumberStep });
147
577
 
148
578
  const userData = {}; // TODO: get user data
149
579
 
150
- let methodName = toMethodName(step.text);
580
+ let methodName = step.renamedText ? step.renamedText : toMethodName(step.text);
151
581
  if (step.isApiStep) {
152
582
  const {
153
583
  url,
@@ -191,13 +621,11 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
191
621
  stepsDefinitions
192
622
  );
193
623
 
194
- if (!step.isImplemented) {
195
- stepsDefinitions.addStep({
196
- name: step.text,
197
- file: result.codePage.sourceFileName,
198
- source: "recorder",
199
- });
200
- }
624
+ stepsDefinitions.addStep({
625
+ name: step.text,
626
+ file: result.codePage.sourceFileName,
627
+ source: "recorder",
628
+ });
201
629
 
202
630
  cucumberStep.methodName = result.methodName;
203
631
  return result.codePage;
@@ -209,7 +637,10 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
209
637
  }
210
638
  codePage = generateCodeResult.page;
211
639
  methodName = generateCodeResult.methodName;
212
- codePage.insertElements(generateCodeResult.elements);
640
+
641
+ if (!step.renamedText) {
642
+ codePage.insertElements(generateCodeResult.elements);
643
+ }
213
644
 
214
645
  const description = cucumberStep.text;
215
646
  let path = null;
@@ -222,15 +653,17 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
222
653
  protect = true;
223
654
  }
224
655
  }
225
- const infraResult = codePage.addInfraCommand(
226
- methodName,
227
- description,
228
- cucumberStep.getVariablesList(),
229
- generateCodeResult.codeLines,
230
- protect,
231
- "recorder",
232
- path
233
- );
656
+ if (!step.renamedText) {
657
+ const infraResult = codePage.addInfraCommand(
658
+ methodName,
659
+ description,
660
+ cucumberStep.getVariablesList(),
661
+ generateCodeResult.codeLines,
662
+ protect,
663
+ "recorder",
664
+ path
665
+ );
666
+ }
234
667
  const keyword = (cucumberStep.keywordAlias ?? cucumberStep.keyword).trim();
235
668
  const stepResult = codePage.addCucumberStep(
236
669
  keyword,
@@ -240,15 +673,14 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
240
673
  step.finalTimeout
241
674
  );
242
675
 
243
- if (!step.isImplemented) {
244
- stepsDefinitions.addStep({
245
- name: step.text,
246
- file: codePage.sourceFileName,
247
- source: "recorder",
248
- });
249
- }
676
+ stepsDefinitions.addStep({
677
+ name: step.text,
678
+ file: codePage.sourceFileName,
679
+ source: "recorder",
680
+ });
250
681
 
251
682
  codePage.removeUnusedElements();
683
+ codePage.mergeSimilarElements();
252
684
  cucumberStep.methodName = methodName;
253
685
  if (generateCodeResult.locatorsMetadata) {
254
686
  codePage.addLocatorsMetadata(generateCodeResult.locatorsMetadata);
@@ -300,26 +732,35 @@ export const getCommandsForImplementedStep = (stepName, stepsDefinitions, stepPa
300
732
  if (error) {
301
733
  throw new Error(error);
302
734
  }
735
+ isUtilStep = codePage.sourceFileName.endsWith("utils.mjs");
303
736
 
304
737
  if (parametersNames.length !== stepParams.length) {
305
738
  // console.log("Parameters mismatch", parametersNames, stepParams);
306
739
  throw new Error("Parameters mismatch");
307
740
  }
308
- for (let i = 0; i < parametersNames.length; i++) {
309
- stepParams[i].argumentName = parametersNames[i];
310
- }
311
741
 
312
- isUtilStep = codePage.sourceFileName.endsWith("utils.mjs");
313
- for (const { code } of codeCommands) {
314
- const command = invertCodeToCommand(code, elements, stepParams, stepsDefinitions, codePage, stepName)[0];
315
- if (command === undefined || command.type === null) continue;
316
- if (command.element) {
317
- const key = command.element.key;
318
- if (key && locatorsJson[key]) {
319
- command.allStrategyLocators = locatorsJson[key];
742
+ const pattern = step.name;
743
+ if (isUtilStep && pattern === "Verify the file {string} exists") {
744
+ commands.push({
745
+ type: "verify_file_exists",
746
+ parameters: [stepParams[0].text],
747
+ });
748
+ } else {
749
+ for (let i = 0; i < parametersNames.length; i++) {
750
+ stepParams[i].argumentName = parametersNames[i];
751
+ }
752
+
753
+ for (const { code } of codeCommands) {
754
+ const command = invertCodeToCommand(code, elements, stepParams, stepsDefinitions, codePage, stepName)[0];
755
+ if (command === undefined || command.type === null) continue;
756
+ if (command.element) {
757
+ const key = command.element.key;
758
+ if (key && locatorsJson[key]) {
759
+ command.allStrategyLocators = locatorsJson[key];
760
+ }
320
761
  }
762
+ commands.push(command);
321
763
  }
322
- commands.push(command);
323
764
  }
324
765
  } catch (error) {
325
766
  console.error(error);
@@ -404,6 +845,7 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
404
845
  console.log("Save routes in temp folder for running:", routesPath);
405
846
  if (existsSync(routesPath)) {
406
847
  console.log("Removing existing temp_routes_folder:", routesPath);
848
+ routesPath = path.join(tmpdir(), `blinq_temp_routes`);
407
849
  rmSync(routesPath, { recursive: true });
408
850
  }
409
851
  mkdirSync(routesPath, { recursive: true });
@@ -427,6 +869,9 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
427
869
  }
428
870
  saveRoutes({ step, folderPath: routesPath });
429
871
  }
872
+ if (step.commands && Array.isArray(step.commands)) {
873
+ step.commands = step.commands.map((cmd) => toRecordingStep(cmd, scenario.parametersMap));
874
+ }
430
875
  continue;
431
876
  }
432
877
  const cucumberStep = getCucumberStep({ step });
@@ -434,7 +879,14 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
434
879
  const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
435
880
  // path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
436
881
  let codePage = getCodePage(stepDefsFilePath);
437
- codePage = await saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions });
882
+ codePage = await saveRecording({
883
+ step,
884
+ cucumberStep,
885
+ codePage,
886
+ projectDir,
887
+ stepsDefinitions,
888
+ parametersMap: scenario.parametersMap,
889
+ });
438
890
  if (!codePage) {
439
891
  continue;
440
892
  }
@@ -129,10 +129,14 @@ export function getExamplesContent(parametersMap) {
129
129
  function getTagsContent(scenario, featureFileObject) {
130
130
  let oldTags = featureFileObject?.scenarios?.find((s) => s.name === scenario.name)?.tags ?? [];
131
131
  for (const tag of scenario.tags) {
132
- if (oldTags.includes(tag)) {
133
- oldTags = oldTags.filter((t) => t !== tag);
134
- } else {
135
- oldTags.push(tag);
132
+ if (tag === "global_test_data") {
133
+ if (!oldTags.includes("global_test_data")) {
134
+ oldTags.push("global_test_data");
135
+ }
136
+ }
137
+
138
+ if (tag === "remove_global_test_data") {
139
+ oldTags = oldTags.filter((t) => t !== "global_test_data");
136
140
  }
137
141
  }
138
142
 
@@ -231,7 +235,7 @@ const GherkinToObject = (gherkin) => {
231
235
  skipEmptyLines();
232
236
 
233
237
  if (idx >= lines.length) {
234
- return -1;
238
+ return scenario;
235
239
  }
236
240
 
237
241
  while (