@dev-blinq/cucumber_client 1.0.1275-dev → 1.0.1275-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 (37) hide show
  1. package/bin/assets/bundled_scripts/recorder.js +120 -120
  2. package/bin/assets/preload/recorderv3.js +3 -1
  3. package/bin/assets/scripts/dom_parent.js +4 -0
  4. package/bin/assets/scripts/recorder.js +11 -4
  5. package/bin/assets/scripts/unique_locators.js +847 -687
  6. package/bin/assets/templates/_hooks_template.txt +37 -0
  7. package/bin/assets/templates/page_template.txt +2 -16
  8. package/bin/assets/templates/utils_template.txt +1 -46
  9. package/bin/client/apiTest/apiTest.js +6 -0
  10. package/bin/client/cli_helpers.js +11 -13
  11. package/bin/client/code_cleanup/utils.js +5 -1
  12. package/bin/client/code_gen/code_inversion.js +53 -4
  13. package/bin/client/code_gen/page_reflection.js +838 -902
  14. package/bin/client/code_gen/playwright_codeget.js +43 -12
  15. package/bin/client/cucumber/feature.js +89 -27
  16. package/bin/client/cucumber/project_to_document.js +1 -1
  17. package/bin/client/cucumber/steps_definitions.js +84 -81
  18. package/bin/client/cucumber_selector.js +17 -1
  19. package/bin/client/local_agent.js +7 -6
  20. package/bin/client/project.js +186 -196
  21. package/bin/client/recorderv3/bvt_recorder.js +170 -60
  22. package/bin/client/recorderv3/implemented_steps.js +74 -16
  23. package/bin/client/recorderv3/index.js +50 -25
  24. package/bin/client/recorderv3/network.js +299 -0
  25. package/bin/client/recorderv3/services.js +4 -16
  26. package/bin/client/recorderv3/step_runner.js +332 -69
  27. package/bin/client/recorderv3/step_utils.js +575 -7
  28. package/bin/client/recorderv3/update_feature.js +32 -30
  29. package/bin/client/run_cucumber.js +5 -1
  30. package/bin/client/scenario_report.js +0 -5
  31. package/bin/client/test_scenario.js +0 -1
  32. package/bin/client/utils/socket_logger.js +132 -0
  33. package/bin/index.js +1 -0
  34. package/bin/logger.js +3 -2
  35. package/bin/min/consoleApi.min.cjs +2 -3
  36. package/bin/min/injectedScript.min.cjs +16 -16
  37. package/package.json +24 -14
@@ -1,4 +1,4 @@
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";
@@ -9,9 +9,428 @@ import { Step } from "../cucumber/feature.js";
9
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
 
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 = [
313
+ "custom",
314
+ "context",
315
+ "basic",
316
+ "text_with_index",
317
+ "ignore_digit",
318
+ "no_text",
319
+ ];
320
+ for (const strategy of orderedPriorities) {
321
+ if (allStrategyLocators[strategy] && allStrategyLocators[strategy].length > 0) {
322
+ return strategy;
323
+ }
324
+ }
325
+ return null;
326
+ }
327
+
328
+
329
+ const _parameterizeLocators = (locators, replacementFromValue, replacementToValue) => {
330
+ for (const loc of locators) {
331
+ if (loc?.css?.includes(replacementFromValue)) {
332
+ loc.css = loc.css.replaceAll(replacementFromValue, replacementToValue);
333
+ }
334
+ if (loc?.text?.includes(replacementFromValue)) {
335
+ loc.text = loc.text.replaceAll(replacementFromValue, replacementToValue);
336
+ }
337
+ if (loc?.climb?.includes(replacementFromValue)) {
338
+ loc.climb = loc.climb.replaceAll(replacementFromValue, replacementToValue);
339
+ }
340
+ }
341
+ return locators;
342
+ }
343
+ const parameterizeLocators = ({
344
+ cmd, locs, isValueVariable, isTextVariable,
345
+ parametersMap
346
+ }) => {
347
+ if (isValueVariable) {
348
+ const variable = cmd.value.slice(1, -1);
349
+ const val = parametersMap[variable];
350
+ const replacementFromValue = val.trim();
351
+ const replacementToValue = `{${variable}}`
352
+ locs = _parameterizeLocators(locs, replacementFromValue, replacementToValue);
353
+ }
354
+ if (isTextVariable) {
355
+ const variable = cmd.text.slice(1, -1);
356
+ const val = parametersMap[variable];
357
+ const replacementFromValue = val.trim();
358
+ const replacementToValue = `{${variable}}`
359
+ locs = _parameterizeLocators(locs, replacementFromValue, replacementToValue);
360
+ }
361
+ return locs
362
+ }
363
+
364
+ //TODO: IMPORTAN
365
+ export const toRecordingStep = (cmd, parametersMap) => {
366
+ if (cmd.type === "api") {
367
+ return {
368
+ type: "api",
369
+ value: cmd.value,
370
+ };
371
+ }
372
+ const step = _toRecordingStep(cmd);
373
+ const cmdID = {
374
+ cmdId: cmd.id,
375
+ };
376
+ Object.assign(step, cmdID);
377
+
378
+ const locatorsObject = JSON.parse(JSON.stringify(cmd.locators ?? null));
379
+
380
+ if (!locatorsObject) return step;
381
+ const isValueVariable = isVariable(cmd.value);
382
+ const isTextVariable = isVariable(cmd.text);
383
+
384
+ if (!isValueVariable && !isTextVariable) {
385
+ return step;
386
+ }
387
+
388
+ if (isValueVariable) {
389
+ step.dataSource = "parameters";
390
+ step.dataKey = convertToIdentifier(cmd.value.slice(1, -1))
391
+ }
392
+ const allStrategyLocators = JSON.parse(JSON.stringify(cmd?.allStrategyLocators ?? null));
393
+
394
+ if (!allStrategyLocators) {
395
+ let locs = locatorsObject.locators;
396
+ locs = parameterizeLocators({
397
+ cmd,
398
+ locs,
399
+ isValueVariable,
400
+ isTextVariable,
401
+ parametersMap
402
+ });
403
+ locatorsObject.locators = locs;
404
+ return {
405
+ ...step,
406
+ locators: locatorsObject
407
+ };
408
+ }
409
+
410
+ for (const key in allStrategyLocators) {
411
+ if (key === "strategy") continue;
412
+ if (key === "no_text" || key === "custom") continue;
413
+ const locators = allStrategyLocators[key];
414
+ if (locators.length === 0) continue;
415
+ parameterizeLocators({
416
+ cmd,
417
+ locs: locators,
418
+ isValueVariable,
419
+ isTextVariable,
420
+ parametersMap
421
+ });
422
+ }
423
+
424
+ locatorsObject.locators = allStrategyLocators[allStrategyLocators.strategy] ?? locatorsObject.locators;
425
+
426
+ return {
427
+ ...step,
428
+ locators: locatorsObject,
429
+ allStrategyLocators,
430
+ isLocatorsAssigned: true
431
+ };
432
+ }
433
+
15
434
  export const toMethodName = (str) => {
16
435
  // Remove any non-word characters (excluding underscore) and trim spaces
17
436
  let cleanStr = str.trim().replace(/[^\w\s]/gi, "");
@@ -68,22 +487,79 @@ function makeStepTextUnique(step, stepsDefinitions) {
68
487
  step.text = stepText;
69
488
  }
70
489
 
71
- export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions }) {
72
- // console.log("saveRecording", step.text);
490
+ export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions, parametersMap }) {
491
+ let routesPath = path.join(tmpdir(), "blinq_temp_routes");
492
+
493
+ if (process.env.TEMP_RUN === "true") {
494
+ if (existsSync(routesPath)) {
495
+ rmSync(routesPath, { recursive: true });
496
+ }
497
+ mkdirSync(routesPath, { recursive: true });
498
+ saveRoutes({ step, folderPath: routesPath });
499
+ } else {
500
+ if (existsSync(routesPath)) {
501
+ // remove the folder
502
+ try {
503
+ rmSync(routesPath, { recursive: true });
504
+ console.log("Removed temp_routes_folder:", routesPath);
505
+ } catch (error) {
506
+ console.error("Error removing temp_routes folder", error);
507
+ }
508
+ routesPath = path.join(projectDir, "data", "routes");
509
+ if (!existsSync(routesPath)) {
510
+ mkdirSync(routesPath, { recursive: true });
511
+ }
512
+ saveRoutes({ step, folderPath: routesPath });
513
+ }
514
+ }
515
+
73
516
  if (step.isImplementedWhileRecording && !process.env.TEMP_RUN) {
74
517
  return;
75
518
  }
519
+
76
520
  if (step.isImplemented && step.shouldOverride) {
77
521
  let stepDef = stepsDefinitions.findMatchingStep(step.text);
78
522
  codePage = getCodePage(stepDef.file);
79
523
  } else {
80
524
  const isUtilStep = makeStepTextUnique(step, stepsDefinitions);
525
+
81
526
  if (isUtilStep) {
82
527
  return;
83
528
  }
84
529
  }
530
+
531
+ routesPath = path.join(tmpdir(), "blinq_temp_routes");
532
+ if (process.env.TEMP_RUN === "true") {
533
+ console.log("Save routes in temp folder for running:", routesPath);
534
+ if (existsSync(routesPath)) {
535
+ console.log("Removing existing temp_routes_folder:", routesPath);
536
+ rmSync(routesPath, { recursive: true });
537
+ }
538
+ mkdirSync(routesPath, { recursive: true });
539
+ console.log("Created temp_routes_folder:", routesPath);
540
+ saveRoutes({ step, folderPath: routesPath });
541
+ } else {
542
+ console.log("Saving routes in project directory:", projectDir);
543
+ if (existsSync(routesPath)) {
544
+ // remove the folder
545
+ try {
546
+ rmSync(routesPath, { recursive: true });
547
+ console.log("Removed temp_routes_folder:", routesPath);
548
+ } catch (error) {
549
+ console.error("Error removing temp_routes folder", error);
550
+ }
551
+ }
552
+ routesPath = path.join(projectDir, "data", "routes");
553
+ console.log("Saving routes to:", routesPath);
554
+ if (!existsSync(routesPath)) {
555
+ mkdirSync(routesPath, { recursive: true });
556
+ }
557
+ saveRoutes({ step, folderPath: routesPath });
558
+ }
559
+
85
560
  cucumberStep.text = step.text;
86
561
  const recording = new Recording();
562
+ step.commands = step.commands.map((cmd) => toRecordingStep(cmd, parametersMap));
87
563
  const steps = step.commands;
88
564
 
89
565
  recording.loadFromObject({ steps, step: cucumberStep });
@@ -108,6 +584,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
108
584
  isStaticToken,
109
585
  status,
110
586
  } = step.commands[0].value;
587
+
111
588
  const result = await generateApiCode(
112
589
  {
113
590
  url,
@@ -132,6 +609,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
132
609
  step.keyword,
133
610
  stepsDefinitions
134
611
  );
612
+
135
613
  if (!step.isImplemented) {
136
614
  stepsDefinitions.addStep({
137
615
  name: step.text,
@@ -139,6 +617,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
139
617
  source: "recorder",
140
618
  });
141
619
  }
620
+
142
621
  cucumberStep.methodName = result.methodName;
143
622
  return result.codePage;
144
623
  } else {
@@ -156,17 +635,29 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
156
635
  if (step.commands && step.commands.length > 0 && step.commands[0]) {
157
636
  path = step.commands[0].lastKnownUrlPath;
158
637
  }
638
+ let protect = false;
639
+ if (step.commands && step.commands.length > 0 && step.commands[0].type) {
640
+ if (step.commands[0].type === "verify_element_property" || step.commands[0].type === "conditional_wait") {
641
+ protect = true;
642
+ }
643
+ }
159
644
  const infraResult = codePage.addInfraCommand(
160
645
  methodName,
161
646
  description,
162
647
  cucumberStep.getVariablesList(),
163
648
  generateCodeResult.codeLines,
164
- false,
649
+ protect,
165
650
  "recorder",
166
651
  path
167
652
  );
168
653
  const keyword = (cucumberStep.keywordAlias ?? cucumberStep.keyword).trim();
169
- const stepResult = codePage.addCucumberStep(keyword, cucumberStep.getTemplate(), methodName, steps.length);
654
+ const stepResult = codePage.addCucumberStep(
655
+ keyword,
656
+ cucumberStep.getTemplate(),
657
+ methodName,
658
+ steps.length,
659
+ step.finalTimeout
660
+ );
170
661
 
171
662
  if (!step.isImplemented) {
172
663
  stepsDefinitions.addStep({
@@ -177,6 +668,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
177
668
  }
178
669
 
179
670
  codePage.removeUnusedElements();
671
+ codePage.mergeSimilarElements();
180
672
  cucumberStep.methodName = methodName;
181
673
  if (generateCodeResult.locatorsMetadata) {
182
674
  codePage.addLocatorsMetadata(generateCodeResult.locatorsMetadata);
@@ -306,6 +798,12 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
306
798
  const utilsTemplateFilePath = path.join(__dirname, "../../assets", "templates", "utils_template.txt");
307
799
  const utilsContent = readFileSync(utilsTemplateFilePath, "utf8");
308
800
  writeFileSync(utilsFilePath, utilsContent, "utf8");
801
+ const hooksTemplateFilePath = path.join(__dirname, "../../assets", "templates", "_hooks_template.txt");
802
+ if (existsSync(hooksTemplateFilePath)) {
803
+ const hooksFilePath = path.join(stepDefinitionFolderPath, "_hooks.mjs");
804
+ const hooksContent = readFileSync(hooksTemplateFilePath, "utf8");
805
+ writeFileSync(hooksFilePath, hooksContent, "utf8");
806
+ }
309
807
  const steps = scenario.steps;
310
808
 
311
809
  const stepsDefinitions = new StepsDefinitions(projectDir);
@@ -321,6 +819,35 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
321
819
  }
322
820
  }
323
821
  if ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0) {
822
+ let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
823
+ if (process.env.TEMP_RUN === "true") {
824
+ console.log("Save routes in temp folder for running:", routesPath);
825
+ if (existsSync(routesPath)) {
826
+ console.log("Removing existing temp_routes_folder:", routesPath);
827
+ routesPath = path.join(tmpdir(), `blinq_temp_routes`);
828
+ rmSync(routesPath, { recursive: true });
829
+ }
830
+ mkdirSync(routesPath, { recursive: true });
831
+ console.log("Created temp_routes_folder:", routesPath);
832
+ saveRoutes({ step, folderPath: routesPath });
833
+ } else {
834
+ console.log("Saving routes in project directory:", projectDir);
835
+ if (existsSync(routesPath)) {
836
+ // remove the folder
837
+ try {
838
+ rmSync(routesPath, { recursive: true });
839
+ console.log("Removed temp_routes_folder:", routesPath);
840
+ } catch (error) {
841
+ console.error("Error removing temp_routes folder", error);
842
+ }
843
+ }
844
+ routesPath = path.join(projectDir, "data", "routes");
845
+ console.log("Saving routes to:", routesPath);
846
+ if (!existsSync(routesPath)) {
847
+ mkdirSync(routesPath, { recursive: true });
848
+ }
849
+ saveRoutes({ step, folderPath: routesPath });
850
+ }
324
851
  continue;
325
852
  }
326
853
  const cucumberStep = getCucumberStep({ step });
@@ -328,8 +855,7 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
328
855
  const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
329
856
  // path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
330
857
  let codePage = getCodePage(stepDefsFilePath);
331
-
332
- codePage = await saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions });
858
+ codePage = await saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions, parametersMap: scenario.parametersMap });
333
859
  if (!codePage) {
334
860
  continue;
335
861
  }
@@ -340,3 +866,45 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
340
866
  }
341
867
  writeFileSync(utilsFilePath, utilsContent, "utf8");
342
868
  }
869
+
870
+ export function saveRoutes({ step, folderPath }) {
871
+ const routeItems = step.routeItems;
872
+ if (!routeItems || routeItems.length === 0) {
873
+ return;
874
+ }
875
+ const cucumberStep = getCucumberStep({ step });
876
+ const template = cucumberStep.getTemplate();
877
+ const stepNameHash = createHash("sha256").update(template).digest("hex");
878
+ console.log("Saving routes for step:", step.text, "with hash:", stepNameHash);
879
+
880
+ const routeItemsWithFilters = routeItems.map((routeItem) => {
881
+ const oldFilters = routeItem.filters;
882
+ const queryParamsObject = {};
883
+ oldFilters.queryParams.forEach((queryParam) => {
884
+ queryParamsObject[queryParam.key] = queryParam.value;
885
+ });
886
+ const newFilters = { path: oldFilters.path, method: oldFilters.method, queryParams: queryParamsObject };
887
+ return {
888
+ ...routeItem,
889
+ filters: newFilters,
890
+ };
891
+ });
892
+
893
+ const routesFilePath = path.join(folderPath, stepNameHash + ".json");
894
+ console.log("Routes file path:", routesFilePath);
895
+ const routesData = {
896
+ template,
897
+ routes: routeItemsWithFilters,
898
+ };
899
+ console.log("Routes data to save:", routesData);
900
+
901
+ if (!existsSync(folderPath)) {
902
+ mkdirSync(folderPath, { recursive: true });
903
+ }
904
+ try {
905
+ writeFileSync(routesFilePath, JSON.stringify(routesData, null, 2), "utf8");
906
+ console.log("Saved routes to", routesFilePath);
907
+ } catch (error) {
908
+ console.error("Failed to save routes to", routesFilePath, "Error:", error);
909
+ }
910
+ }