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