@dev-blinq/cucumber_client 1.0.1323-dev → 1.0.1323-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 (52) hide show
  1. package/bin/assets/bundled_scripts/recorder.js +108 -108
  2. package/bin/assets/preload/css_gen.js +10 -10
  3. package/bin/assets/preload/recorderv3.js +3 -1
  4. package/bin/assets/preload/toolbar.js +27 -29
  5. package/bin/assets/preload/unique_locators.js +1 -1
  6. package/bin/assets/preload/yaml.js +288 -275
  7. package/bin/assets/scripts/aria_snapshot.js +223 -220
  8. package/bin/assets/scripts/dom_attr.js +329 -329
  9. package/bin/assets/scripts/dom_parent.js +169 -174
  10. package/bin/assets/scripts/event_utils.js +94 -94
  11. package/bin/assets/scripts/pw.js +2050 -1949
  12. package/bin/assets/scripts/recorder.js +5 -17
  13. package/bin/assets/scripts/snapshot_capturer.js +153 -146
  14. package/bin/assets/scripts/unique_locators.js +156 -48
  15. package/bin/assets/scripts/yaml.js +796 -783
  16. package/bin/assets/templates/_hooks_template.txt +41 -0
  17. package/bin/assets/templates/utils_template.txt +1 -44
  18. package/bin/client/apiTest/apiTest.js +6 -0
  19. package/bin/client/cli_helpers.js +11 -13
  20. package/bin/client/code_cleanup/utils.js +5 -1
  21. package/bin/client/code_gen/api_codegen.js +2 -2
  22. package/bin/client/code_gen/code_inversion.js +53 -4
  23. package/bin/client/code_gen/page_reflection.js +839 -906
  24. package/bin/client/code_gen/playwright_codeget.js +26 -18
  25. package/bin/client/cucumber/feature.js +89 -27
  26. package/bin/client/cucumber/feature_data.js +2 -2
  27. package/bin/client/cucumber/project_to_document.js +9 -3
  28. package/bin/client/cucumber/steps_definitions.js +6 -3
  29. package/bin/client/cucumber_selector.js +17 -1
  30. package/bin/client/local_agent.js +6 -5
  31. package/bin/client/parse_feature_file.js +23 -26
  32. package/bin/client/playground/projects/env.json +2 -2
  33. package/bin/client/project.js +186 -196
  34. package/bin/client/recorderv3/bvt_recorder.js +190 -127
  35. package/bin/client/recorderv3/implemented_steps.js +74 -16
  36. package/bin/client/recorderv3/index.js +68 -54
  37. package/bin/client/recorderv3/network.js +22 -5
  38. package/bin/client/recorderv3/scriptTest.js +1 -1
  39. package/bin/client/recorderv3/services.js +4 -16
  40. package/bin/client/recorderv3/step_runner.js +303 -220
  41. package/bin/client/recorderv3/step_utils.js +484 -7
  42. package/bin/client/recorderv3/update_feature.js +32 -30
  43. package/bin/client/run_cucumber.js +5 -1
  44. package/bin/client/scenario_report.js +0 -5
  45. package/bin/client/test_scenario.js +0 -1
  46. package/bin/client/upload-service.js +2 -2
  47. package/bin/client/utils/socket_logger.js +132 -0
  48. package/bin/index.js +1 -0
  49. package/bin/logger.js +3 -2
  50. package/bin/min/consoleApi.min.cjs +2 -3
  51. package/bin/min/injectedScript.min.cjs +16 -16
  52. package/package.json +21 -12
@@ -14,6 +14,427 @@ 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
+ };
300
+ }
301
+ default: {
302
+ return {
303
+ type: cmd.type,
304
+ parameters: [cmd.value],
305
+ lastKnownUrlPath: cmd.lastKnownUrlPath,
306
+ };
307
+ }
308
+ }
309
+ };
310
+
311
+ function getBestStrategy(allStrategyLocators) {
312
+ const orderedPriorities = ["custom", "context", "basic", "text_with_index", "ignore_digit", "no_text"];
313
+ for (const strategy of orderedPriorities) {
314
+ if (allStrategyLocators[strategy] && allStrategyLocators[strategy].length > 0) {
315
+ return strategy;
316
+ }
317
+ }
318
+ return null;
319
+ }
320
+
321
+ const _parameterizeLocators = (locators, replacementFromValue, replacementToValue) => {
322
+ for (const loc of locators) {
323
+ if (loc?.css?.includes(replacementFromValue)) {
324
+ loc.css = loc.css.replaceAll(replacementFromValue, replacementToValue);
325
+ }
326
+ if (loc?.text?.includes(replacementFromValue)) {
327
+ loc.text = loc.text.replaceAll(replacementFromValue, replacementToValue);
328
+ }
329
+ if (loc?.climb && typeof loc.climb === "string" && loc.climb?.includes(replacementFromValue)) {
330
+ loc.climb = loc.climb.replaceAll(replacementFromValue, replacementToValue);
331
+ }
332
+ }
333
+ return locators;
334
+ };
335
+ const parameterizeLocators = ({ cmd, locs, isValueVariable, isTargetValueVariable, parametersMap }) => {
336
+ if (isValueVariable) {
337
+ const variable = cmd.value.slice(1, -1);
338
+ // const val = parametersMap[variable];
339
+ if (typeof cmd.text === "string") {
340
+ const replacementFromValue = cmd.text.trim().replace(/\s+/, " ") ?? ""; // val.trim();
341
+ if (replacementFromValue.length > 0) {
342
+ const replacementToValue = `{${variable}}`;
343
+ locs = _parameterizeLocators(locs, replacementFromValue, replacementToValue);
344
+ }
345
+ }
346
+ }
347
+ if (isTargetValueVariable) {
348
+ const variable = cmd.targetValue.slice(1, -1);
349
+ // const val = parametersMap[variable];
350
+ if (typeof cmd.targetText === "string") {
351
+ const replacementFromValue = cmd.targetText.trim().replace(/\s+/, " ") ?? ""; // val.trim();
352
+ if (replacementFromValue.length > 0) {
353
+ const replacementToValue = `{${variable}}`;
354
+ locs = _parameterizeLocators(locs, replacementFromValue, replacementToValue);
355
+ }
356
+ }
357
+ }
358
+ return locs;
359
+ };
360
+
361
+ //TODO: IMPORTAN
362
+ export const toRecordingStep = (cmd, parametersMap) => {
363
+ if (cmd.type === "api") {
364
+ return {
365
+ type: "api",
366
+ value: cmd.value,
367
+ };
368
+ }
369
+ const step = _toRecordingStep(cmd);
370
+ const cmdID = {
371
+ cmdId: cmd.id,
372
+ };
373
+ Object.assign(step, cmdID);
374
+
375
+ const locatorsObject = JSON.parse(JSON.stringify(cmd.locators ?? null));
376
+
377
+ if (!locatorsObject) return step;
378
+
379
+ const element_name = cmd?.locators?.element_name ?? `${cmd.label} ${cmd.role ?? "Text"}`;
380
+ locatorsObject.element_name = element_name;
381
+
382
+ const isValueVariable = isVariable(cmd.value);
383
+ const isTargetValueVariable = isVariable(cmd.targetValue);
384
+ const allStrategyLocators = JSON.parse(JSON.stringify(cmd?.allStrategyLocators ?? null));
385
+ step.locators = locatorsObject;
386
+ step.allStrategyLocators = allStrategyLocators;
387
+ step.isLocatorsAssigned = true;
388
+
389
+ if (!isValueVariable && !isTargetValueVariable) {
390
+ return step;
391
+ }
392
+
393
+ if (isValueVariable) {
394
+ step.dataSource = "parameters";
395
+ step.dataKey = convertToIdentifier(cmd.value.slice(1, -1));
396
+ }
397
+
398
+ if (!allStrategyLocators) {
399
+ let locs = locatorsObject.locators;
400
+ locs = parameterizeLocators({
401
+ cmd,
402
+ locs,
403
+ isValueVariable,
404
+ isTargetValueVariable,
405
+ parametersMap,
406
+ });
407
+ locatorsObject.locators = locs;
408
+ return {
409
+ ...step,
410
+ locators: locatorsObject,
411
+ };
412
+ }
413
+
414
+ for (const key in allStrategyLocators) {
415
+ if (key === "strategy") continue;
416
+ if (key === "no_text" || key === "custom") continue;
417
+ const locators = allStrategyLocators[key];
418
+ if (locators.length === 0) continue;
419
+ parameterizeLocators({
420
+ cmd,
421
+ locs: locators,
422
+ isValueVariable,
423
+ isTargetValueVariable,
424
+ parametersMap,
425
+ });
426
+ }
427
+
428
+ locatorsObject.locators = allStrategyLocators[allStrategyLocators.strategy] ?? locatorsObject.locators;
429
+
430
+ return {
431
+ ...step,
432
+ locators: locatorsObject,
433
+ allStrategyLocators,
434
+ isLocatorsAssigned: true,
435
+ };
436
+ };
437
+
17
438
  export const toMethodName = (str) => {
18
439
  // Remove any non-word characters (excluding underscore) and trim spaces
19
440
  let cleanStr = str.trim().replace(/[^\w\s]/gi, "");
@@ -70,11 +491,13 @@ function makeStepTextUnique(step, stepsDefinitions) {
70
491
  step.text = stepText;
71
492
  }
72
493
 
73
- export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions }) {
74
-
494
+ export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions, parametersMap }) {
495
+ if (step.commands && Array.isArray(step.commands)) {
496
+ step.commands = step.commands.map((cmd) => toRecordingStep(cmd, parametersMap));
497
+ }
75
498
  let routesPath = path.join(tmpdir(), "blinq_temp_routes");
76
499
 
77
- if (process.env.TEMP_RUN) {
500
+ if (process.env.TEMP_RUN === "true") {
78
501
  if (existsSync(routesPath)) {
79
502
  rmSync(routesPath, { recursive: true });
80
503
  }
@@ -111,7 +534,8 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
111
534
  return;
112
535
  }
113
536
  }
114
-
537
+
538
+ routesPath = path.join(tmpdir(), "blinq_temp_routes");
115
539
  if (process.env.TEMP_RUN === "true") {
116
540
  console.log("Save routes in temp folder for running:", routesPath);
117
541
  if (existsSync(routesPath)) {
@@ -142,6 +566,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
142
566
 
143
567
  cucumberStep.text = step.text;
144
568
  const recording = new Recording();
569
+
145
570
  const steps = step.commands;
146
571
 
147
572
  recording.loadFromObject({ steps, step: cucumberStep });
@@ -217,12 +642,18 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
217
642
  if (step.commands && step.commands.length > 0 && step.commands[0]) {
218
643
  path = step.commands[0].lastKnownUrlPath;
219
644
  }
645
+ let protect = false;
646
+ if (step.commands && step.commands.length > 0 && step.commands[0].type) {
647
+ if (step.commands[0].type === "verify_element_property" || step.commands[0].type === "conditional_wait") {
648
+ protect = true;
649
+ }
650
+ }
220
651
  const infraResult = codePage.addInfraCommand(
221
652
  methodName,
222
653
  description,
223
654
  cucumberStep.getVariablesList(),
224
655
  generateCodeResult.codeLines,
225
- false,
656
+ protect,
226
657
  "recorder",
227
658
  path
228
659
  );
@@ -244,6 +675,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
244
675
  }
245
676
 
246
677
  codePage.removeUnusedElements();
678
+ codePage.mergeSimilarElements();
247
679
  cucumberStep.methodName = methodName;
248
680
  if (generateCodeResult.locatorsMetadata) {
249
681
  codePage.addLocatorsMetadata(generateCodeResult.locatorsMetadata);
@@ -373,6 +805,12 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
373
805
  const utilsTemplateFilePath = path.join(__dirname, "../../assets", "templates", "utils_template.txt");
374
806
  const utilsContent = readFileSync(utilsTemplateFilePath, "utf8");
375
807
  writeFileSync(utilsFilePath, utilsContent, "utf8");
808
+ const hooksTemplateFilePath = path.join(__dirname, "../../assets", "templates", "_hooks_template.txt");
809
+ if (existsSync(hooksTemplateFilePath)) {
810
+ const hooksFilePath = path.join(stepDefinitionFolderPath, "_hooks.mjs");
811
+ const hooksContent = readFileSync(hooksTemplateFilePath, "utf8");
812
+ writeFileSync(hooksFilePath, hooksContent, "utf8");
813
+ }
376
814
  const steps = scenario.steps;
377
815
 
378
816
  const stepsDefinitions = new StepsDefinitions(projectDir);
@@ -388,6 +826,38 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
388
826
  }
389
827
  }
390
828
  if ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0) {
829
+ let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
830
+ if (process.env.TEMP_RUN === "true") {
831
+ console.log("Save routes in temp folder for running:", routesPath);
832
+ if (existsSync(routesPath)) {
833
+ console.log("Removing existing temp_routes_folder:", routesPath);
834
+ routesPath = path.join(tmpdir(), `blinq_temp_routes`);
835
+ rmSync(routesPath, { recursive: true });
836
+ }
837
+ mkdirSync(routesPath, { recursive: true });
838
+ console.log("Created temp_routes_folder:", routesPath);
839
+ saveRoutes({ step, folderPath: routesPath });
840
+ } else {
841
+ console.log("Saving routes in project directory:", projectDir);
842
+ if (existsSync(routesPath)) {
843
+ // remove the folder
844
+ try {
845
+ rmSync(routesPath, { recursive: true });
846
+ console.log("Removed temp_routes_folder:", routesPath);
847
+ } catch (error) {
848
+ console.error("Error removing temp_routes folder", error);
849
+ }
850
+ }
851
+ routesPath = path.join(projectDir, "data", "routes");
852
+ console.log("Saving routes to:", routesPath);
853
+ if (!existsSync(routesPath)) {
854
+ mkdirSync(routesPath, { recursive: true });
855
+ }
856
+ saveRoutes({ step, folderPath: routesPath });
857
+ }
858
+ if (step.commands && Array.isArray(step.commands)) {
859
+ step.commands = step.commands.map((cmd) => toRecordingStep(cmd, scenario.parametersMap));
860
+ }
391
861
  continue;
392
862
  }
393
863
  const cucumberStep = getCucumberStep({ step });
@@ -395,7 +865,14 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
395
865
  const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
396
866
  // path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
397
867
  let codePage = getCodePage(stepDefsFilePath);
398
- codePage = await saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions });
868
+ codePage = await saveRecording({
869
+ step,
870
+ cucumberStep,
871
+ codePage,
872
+ projectDir,
873
+ stepsDefinitions,
874
+ parametersMap: scenario.parametersMap,
875
+ });
399
876
  if (!codePage) {
400
877
  continue;
401
878
  }
@@ -421,7 +898,7 @@ export function saveRoutes({ step, folderPath }) {
421
898
  const oldFilters = routeItem.filters;
422
899
  const queryParamsObject = {};
423
900
  oldFilters.queryParams.forEach((queryParam) => {
424
- queryParamsObject[queryParam.paramKey] = queryParam.paramValue;
901
+ queryParamsObject[queryParam.key] = queryParam.value;
425
902
  });
426
903
  const newFilters = { path: oldFilters.path, method: oldFilters.method, queryParams: queryParamsObject };
427
904
  return {
@@ -64,7 +64,7 @@ const escapeNonPrintables = (text) => {
64
64
  export function getCommandContent(command) {
65
65
  switch (command.type) {
66
66
  case "click_element": {
67
- return `click on ${escapeNonPrintables(command.element.name)}`;
67
+ return `${command.count === 2 ? "Double click" : "Click"} on ${escapeNonPrintables(command.element.name)}`;
68
68
  }
69
69
  case "fill_element": {
70
70
  return `fill ${escapeNonPrintables(command.element.name)} with ${escapeNonPrintables(command.parameters[0])}${command.parameters[1] ? ` and press enter key` : ""}`;
@@ -82,7 +82,7 @@ export function getCommandContent(command) {
82
82
  return `verify the element ${escapeNonPrintables(command.element.name)} contains text ${escapeNonPrintables(command.parameters[0])}`;
83
83
  }
84
84
  case "context_click": {
85
- return `click on ${escapeNonPrintables(command.label)} in the context of ${escapeNonPrintables(command.value)}`;
85
+ return `${command.count === 2 ? "Double click" : "Click"} on ${escapeNonPrintables(command.label)} in the context of ${escapeNonPrintables(command.value)}`;
86
86
  }
87
87
  case "hover_element": {
88
88
  return `hover over ${escapeNonPrintables(command.element.name)}`;
@@ -99,6 +99,9 @@ export function getCommandContent(command) {
99
99
  case "verify_page_snapshot": {
100
100
  return `verify page snapshot stored in ${command.parameters[0]}`;
101
101
  }
102
+ case "parameterized_click": {
103
+ return `${command.count === 2 ? "Parameterized double click" : "Parameterized click"} on ${escapeNonPrintables(command.element.name)}`;
104
+ }
102
105
  default: {
103
106
  return "";
104
107
  }
@@ -126,10 +129,14 @@ export function getExamplesContent(parametersMap) {
126
129
  function getTagsContent(scenario, featureFileObject) {
127
130
  let oldTags = featureFileObject?.scenarios?.find((s) => s.name === scenario.name)?.tags ?? [];
128
131
  for (const tag of scenario.tags) {
129
- if (oldTags.includes(tag)) {
130
- oldTags = oldTags.filter((t) => t !== tag);
131
- } else {
132
- 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");
133
140
  }
134
141
  }
135
142
 
@@ -210,14 +217,12 @@ const GherkinToObject = (gherkin) => {
210
217
  steps: [],
211
218
  };
212
219
  while (idx < lines.length && lines[idx].startsWith("@")) {
213
- skipEmptyLines();
214
220
  const tags = [...lines[idx].matchAll(/@([^@]+)/g)].map((match) => match[1].trim());
215
221
  scenario.tags.push(...(tags ?? []));
216
222
  idx++;
223
+ skipEmptyLines();
217
224
  }
218
225
 
219
- skipEmptyLines();
220
-
221
226
  if (idx < lines.length && (lines[idx].startsWith("Scenario:") || lines[idx].startsWith("Scenario Outline:"))) {
222
227
  scenario.name = lines[idx].substring(lines[idx].indexOf(":") + 1).trim();
223
228
  idx++;
@@ -240,32 +245,32 @@ const GherkinToObject = (gherkin) => {
240
245
  !lines[idx].startsWith("@")
241
246
  ) {
242
247
  const line = lines[idx++];
243
- if (line.startsWith("#")) {
244
- const comment = line;
245
- if (comment) {
246
- const command = {
247
- type: "comment",
248
- text: comment,
249
- };
250
- scenario.steps.push(command);
251
- }
252
- } else if (line.startsWith("Examples:")) {
253
- obj.hasParams = true;
254
- const command = {
248
+ let command;
249
+ if (line.startsWith("Examples:")) {
250
+ scenario.hasParams = true;
251
+ command = {
255
252
  type: "examples",
256
253
  lines: [],
257
254
  };
258
-
259
255
  while (idx < lines.length && lines[idx].startsWith("|")) {
260
256
  const line = lines[idx++];
261
257
  command.lines.push(line);
262
258
  }
263
259
  } else {
264
- scenario.steps.push({
265
- type: "step",
266
- text: line,
267
- });
260
+ if (line.startsWith("#")) {
261
+ command = {
262
+ type: "comment",
263
+ text: line,
264
+ };
265
+ } else {
266
+ command = {
267
+ type: "step",
268
+ text: line,
269
+ };
270
+ }
268
271
  }
272
+ scenario.steps.push(command);
273
+ skipEmptyLines();
269
274
  }
270
275
 
271
276
  return scenario;
@@ -274,7 +279,6 @@ const GherkinToObject = (gherkin) => {
274
279
  while (idx < lines.length) {
275
280
  const scenario = getScenario();
276
281
  if (scenario === -1) break;
277
-
278
282
  if (scenario.error) {
279
283
  return {
280
284
  error: scenario.error,
@@ -300,8 +304,7 @@ function updateExistingScenario({ featureFileContent, scenarioName, scenarioCont
300
304
  skipScenarioIndex = i;
301
305
  continue;
302
306
  }
303
- let scenarioContent = `${featureFileObject.hasParams ? "Scenario Outline" : "Scenario"}: ${scenario.name}`;
304
-
307
+ let scenarioContent = `${scenario.hasParams ? "Scenario Outline" : "Scenario"}: ${scenario.name}`;
305
308
  let tagsLine;
306
309
  if (scenario.tags?.length > 0) {
307
310
  tagsLine = `${scenario.tags.map((t) => `@${t}`).join(" ")}`;
@@ -324,7 +327,6 @@ function updateExistingScenario({ featureFileContent, scenarioName, scenarioCont
324
327
  if (skipScenarioIndex !== -1) {
325
328
  finalContent = results.join("\n") + "\n" + scenarioContent;
326
329
  }
327
-
328
330
  return finalContent;
329
331
  }
330
332
  export async function updateFeatureFile({ featureName, scenario, override, projectDir }) {