@dev-blinq/cucumber_client 1.0.1276-dev → 1.0.1276-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 +121 -121
  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 +837 -815
  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 +579 -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,432 @@ 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
+ const allStrategyLocators = JSON.parse(JSON.stringify(cmd?.allStrategyLocators ?? null));
384
+
385
+ step.locators = locatorsObject;
386
+ step.allStrategyLocators = allStrategyLocators;
387
+ step.isLocatorsAssigned = true;
388
+
389
+ if (!isValueVariable && !isTextVariable) {
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
+ isTextVariable,
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
+ isTextVariable,
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
+
15
438
  export const toMethodName = (str) => {
16
439
  // Remove any non-word characters (excluding underscore) and trim spaces
17
440
  let cleanStr = str.trim().replace(/[^\w\s]/gi, "");
@@ -68,22 +491,79 @@ function makeStepTextUnique(step, stepsDefinitions) {
68
491
  step.text = stepText;
69
492
  }
70
493
 
71
- export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions }) {
72
- // console.log("saveRecording", step.text);
494
+ export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions, parametersMap }) {
495
+ let routesPath = path.join(tmpdir(), "blinq_temp_routes");
496
+
497
+ if (process.env.TEMP_RUN === "true") {
498
+ if (existsSync(routesPath)) {
499
+ rmSync(routesPath, { recursive: true });
500
+ }
501
+ mkdirSync(routesPath, { recursive: true });
502
+ saveRoutes({ step, folderPath: routesPath });
503
+ } else {
504
+ if (existsSync(routesPath)) {
505
+ // remove the folder
506
+ try {
507
+ rmSync(routesPath, { recursive: true });
508
+ console.log("Removed temp_routes_folder:", routesPath);
509
+ } catch (error) {
510
+ console.error("Error removing temp_routes folder", error);
511
+ }
512
+ routesPath = path.join(projectDir, "data", "routes");
513
+ if (!existsSync(routesPath)) {
514
+ mkdirSync(routesPath, { recursive: true });
515
+ }
516
+ saveRoutes({ step, folderPath: routesPath });
517
+ }
518
+ }
519
+
73
520
  if (step.isImplementedWhileRecording && !process.env.TEMP_RUN) {
74
521
  return;
75
522
  }
523
+
76
524
  if (step.isImplemented && step.shouldOverride) {
77
525
  let stepDef = stepsDefinitions.findMatchingStep(step.text);
78
526
  codePage = getCodePage(stepDef.file);
79
527
  } else {
80
528
  const isUtilStep = makeStepTextUnique(step, stepsDefinitions);
529
+
81
530
  if (isUtilStep) {
82
531
  return;
83
532
  }
84
533
  }
534
+
535
+ routesPath = path.join(tmpdir(), "blinq_temp_routes");
536
+ if (process.env.TEMP_RUN === "true") {
537
+ console.log("Save routes in temp folder for running:", routesPath);
538
+ if (existsSync(routesPath)) {
539
+ console.log("Removing existing temp_routes_folder:", routesPath);
540
+ rmSync(routesPath, { recursive: true });
541
+ }
542
+ mkdirSync(routesPath, { recursive: true });
543
+ console.log("Created temp_routes_folder:", routesPath);
544
+ saveRoutes({ step, folderPath: routesPath });
545
+ } else {
546
+ console.log("Saving routes in project directory:", projectDir);
547
+ if (existsSync(routesPath)) {
548
+ // remove the folder
549
+ try {
550
+ rmSync(routesPath, { recursive: true });
551
+ console.log("Removed temp_routes_folder:", routesPath);
552
+ } catch (error) {
553
+ console.error("Error removing temp_routes folder", error);
554
+ }
555
+ }
556
+ routesPath = path.join(projectDir, "data", "routes");
557
+ console.log("Saving routes to:", routesPath);
558
+ if (!existsSync(routesPath)) {
559
+ mkdirSync(routesPath, { recursive: true });
560
+ }
561
+ saveRoutes({ step, folderPath: routesPath });
562
+ }
563
+
85
564
  cucumberStep.text = step.text;
86
565
  const recording = new Recording();
566
+ step.commands = step.commands.map((cmd) => toRecordingStep(cmd, parametersMap));
87
567
  const steps = step.commands;
88
568
 
89
569
  recording.loadFromObject({ steps, step: cucumberStep });
@@ -108,6 +588,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
108
588
  isStaticToken,
109
589
  status,
110
590
  } = step.commands[0].value;
591
+
111
592
  const result = await generateApiCode(
112
593
  {
113
594
  url,
@@ -132,6 +613,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
132
613
  step.keyword,
133
614
  stepsDefinitions
134
615
  );
616
+
135
617
  if (!step.isImplemented) {
136
618
  stepsDefinitions.addStep({
137
619
  name: step.text,
@@ -139,6 +621,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
139
621
  source: "recorder",
140
622
  });
141
623
  }
624
+
142
625
  cucumberStep.methodName = result.methodName;
143
626
  return result.codePage;
144
627
  } else {
@@ -156,17 +639,29 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
156
639
  if (step.commands && step.commands.length > 0 && step.commands[0]) {
157
640
  path = step.commands[0].lastKnownUrlPath;
158
641
  }
642
+ let protect = false;
643
+ if (step.commands && step.commands.length > 0 && step.commands[0].type) {
644
+ if (step.commands[0].type === "verify_element_property" || step.commands[0].type === "conditional_wait") {
645
+ protect = true;
646
+ }
647
+ }
159
648
  const infraResult = codePage.addInfraCommand(
160
649
  methodName,
161
650
  description,
162
651
  cucumberStep.getVariablesList(),
163
652
  generateCodeResult.codeLines,
164
- false,
653
+ protect,
165
654
  "recorder",
166
655
  path
167
656
  );
168
657
  const keyword = (cucumberStep.keywordAlias ?? cucumberStep.keyword).trim();
169
- const stepResult = codePage.addCucumberStep(keyword, cucumberStep.getTemplate(), methodName, steps.length);
658
+ const stepResult = codePage.addCucumberStep(
659
+ keyword,
660
+ cucumberStep.getTemplate(),
661
+ methodName,
662
+ steps.length,
663
+ step.finalTimeout
664
+ );
170
665
 
171
666
  if (!step.isImplemented) {
172
667
  stepsDefinitions.addStep({
@@ -177,6 +672,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
177
672
  }
178
673
 
179
674
  codePage.removeUnusedElements();
675
+ codePage.mergeSimilarElements();
180
676
  cucumberStep.methodName = methodName;
181
677
  if (generateCodeResult.locatorsMetadata) {
182
678
  codePage.addLocatorsMetadata(generateCodeResult.locatorsMetadata);
@@ -306,6 +802,12 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
306
802
  const utilsTemplateFilePath = path.join(__dirname, "../../assets", "templates", "utils_template.txt");
307
803
  const utilsContent = readFileSync(utilsTemplateFilePath, "utf8");
308
804
  writeFileSync(utilsFilePath, utilsContent, "utf8");
805
+ const hooksTemplateFilePath = path.join(__dirname, "../../assets", "templates", "_hooks_template.txt");
806
+ if (existsSync(hooksTemplateFilePath)) {
807
+ const hooksFilePath = path.join(stepDefinitionFolderPath, "_hooks.mjs");
808
+ const hooksContent = readFileSync(hooksTemplateFilePath, "utf8");
809
+ writeFileSync(hooksFilePath, hooksContent, "utf8");
810
+ }
309
811
  const steps = scenario.steps;
310
812
 
311
813
  const stepsDefinitions = new StepsDefinitions(projectDir);
@@ -321,6 +823,35 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
321
823
  }
322
824
  }
323
825
  if ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0) {
826
+ let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
827
+ if (process.env.TEMP_RUN === "true") {
828
+ console.log("Save routes in temp folder for running:", routesPath);
829
+ if (existsSync(routesPath)) {
830
+ console.log("Removing existing temp_routes_folder:", routesPath);
831
+ routesPath = path.join(tmpdir(), `blinq_temp_routes`);
832
+ rmSync(routesPath, { recursive: true });
833
+ }
834
+ mkdirSync(routesPath, { recursive: true });
835
+ console.log("Created temp_routes_folder:", routesPath);
836
+ saveRoutes({ step, folderPath: routesPath });
837
+ } else {
838
+ console.log("Saving routes in project directory:", projectDir);
839
+ if (existsSync(routesPath)) {
840
+ // remove the folder
841
+ try {
842
+ rmSync(routesPath, { recursive: true });
843
+ console.log("Removed temp_routes_folder:", routesPath);
844
+ } catch (error) {
845
+ console.error("Error removing temp_routes folder", error);
846
+ }
847
+ }
848
+ routesPath = path.join(projectDir, "data", "routes");
849
+ console.log("Saving routes to:", routesPath);
850
+ if (!existsSync(routesPath)) {
851
+ mkdirSync(routesPath, { recursive: true });
852
+ }
853
+ saveRoutes({ step, folderPath: routesPath });
854
+ }
324
855
  continue;
325
856
  }
326
857
  const cucumberStep = getCucumberStep({ step });
@@ -328,8 +859,7 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
328
859
  const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
329
860
  // path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
330
861
  let codePage = getCodePage(stepDefsFilePath);
331
-
332
- codePage = await saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions });
862
+ codePage = await saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions, parametersMap: scenario.parametersMap });
333
863
  if (!codePage) {
334
864
  continue;
335
865
  }
@@ -340,3 +870,45 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
340
870
  }
341
871
  writeFileSync(utilsFilePath, utilsContent, "utf8");
342
872
  }
873
+
874
+ export function saveRoutes({ step, folderPath }) {
875
+ const routeItems = step.routeItems;
876
+ if (!routeItems || routeItems.length === 0) {
877
+ return;
878
+ }
879
+ const cucumberStep = getCucumberStep({ step });
880
+ const template = cucumberStep.getTemplate();
881
+ const stepNameHash = createHash("sha256").update(template).digest("hex");
882
+ console.log("Saving routes for step:", step.text, "with hash:", stepNameHash);
883
+
884
+ const routeItemsWithFilters = routeItems.map((routeItem) => {
885
+ const oldFilters = routeItem.filters;
886
+ const queryParamsObject = {};
887
+ oldFilters.queryParams.forEach((queryParam) => {
888
+ queryParamsObject[queryParam.key] = queryParam.value;
889
+ });
890
+ const newFilters = { path: oldFilters.path, method: oldFilters.method, queryParams: queryParamsObject };
891
+ return {
892
+ ...routeItem,
893
+ filters: newFilters,
894
+ };
895
+ });
896
+
897
+ const routesFilePath = path.join(folderPath, stepNameHash + ".json");
898
+ console.log("Routes file path:", routesFilePath);
899
+ const routesData = {
900
+ template,
901
+ routes: routeItemsWithFilters,
902
+ };
903
+ console.log("Routes data to save:", routesData);
904
+
905
+ if (!existsSync(folderPath)) {
906
+ mkdirSync(folderPath, { recursive: true });
907
+ }
908
+ try {
909
+ writeFileSync(routesFilePath, JSON.stringify(routesData, null, 2), "utf8");
910
+ console.log("Saved routes to", routesFilePath);
911
+ } catch (error) {
912
+ console.error("Failed to save routes to", routesFilePath, "Error:", error);
913
+ }
914
+ }