@dev-blinq/cucumber_client 1.0.1307-dev → 1.0.1307-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 +106 -106
  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 +4 -16
  13. package/bin/assets/scripts/snapshot_capturer.js +153 -146
  14. package/bin/assets/scripts/unique_locators.js +925 -793
  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 -46
  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 +48 -14
  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 +90 -87
  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 +168 -55
  35. package/bin/client/recorderv3/implemented_steps.js +74 -16
  36. package/bin/client/recorderv3/index.js +69 -22
  37. package/bin/client/recorderv3/network.js +299 -0
  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 +329 -72
  41. package/bin/client/recorderv3/step_utils.js +574 -5
  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 +20 -11
@@ -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,424 @@ 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 = ["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, isTextVariable, parametersMap }) => {
336
+ if (isValueVariable) {
337
+ const variable = cmd.value.slice(1, -1);
338
+ const val = parametersMap[variable];
339
+ const replacementFromValue = val.trim();
340
+ const replacementToValue = `{${variable}}`;
341
+ locs = _parameterizeLocators(locs, replacementFromValue, replacementToValue);
342
+ }
343
+ if (isTextVariable) {
344
+ const variable = cmd.text.slice(1, -1);
345
+ const val = parametersMap[variable];
346
+ const replacementFromValue = val.trim();
347
+ const replacementToValue = `{${variable}}`;
348
+ locs = _parameterizeLocators(locs, replacementFromValue, replacementToValue);
349
+ }
350
+ return locs;
351
+ };
352
+
353
+ //TODO: IMPORTAN
354
+ export const toRecordingStep = (cmd, parametersMap) => {
355
+ if (cmd.type === "api") {
356
+ return {
357
+ type: "api",
358
+ value: cmd.value,
359
+ };
360
+ }
361
+ const step = _toRecordingStep(cmd);
362
+ const cmdID = {
363
+ cmdId: cmd.id,
364
+ };
365
+ Object.assign(step, cmdID);
366
+
367
+ const locatorsObject = JSON.parse(JSON.stringify(cmd.locators ?? null));
368
+
369
+ if (!locatorsObject) return step;
370
+
371
+ const element_name = cmd?.locators?.element_name ?? `${cmd.label} ${cmd.role ?? "Text"}`;
372
+ locatorsObject.element_name = element_name;
373
+
374
+ const isValueVariable = isVariable(cmd.value);
375
+ const isTextVariable = isVariable(cmd.text);
376
+ const allStrategyLocators = JSON.parse(JSON.stringify(cmd?.allStrategyLocators ?? null));
377
+ step.locators = locatorsObject;
378
+ step.allStrategyLocators = allStrategyLocators;
379
+ step.isLocatorsAssigned = true;
380
+
381
+ if (!isValueVariable && !isTextVariable) {
382
+ return step;
383
+ }
384
+
385
+ if (isValueVariable) {
386
+ step.dataSource = "parameters";
387
+ step.dataKey = convertToIdentifier(cmd.value.slice(1, -1));
388
+ }
389
+
390
+ if (!allStrategyLocators) {
391
+ let locs = locatorsObject.locators;
392
+ locs = parameterizeLocators({
393
+ cmd,
394
+ locs,
395
+ isValueVariable,
396
+ isTextVariable,
397
+ parametersMap,
398
+ });
399
+ locatorsObject.locators = locs;
400
+ return {
401
+ ...step,
402
+ locators: locatorsObject,
403
+ };
404
+ }
405
+
406
+ for (const key in allStrategyLocators) {
407
+ if (key === "strategy") continue;
408
+ if (key === "no_text" || key === "custom") continue;
409
+ const locators = allStrategyLocators[key];
410
+ if (locators.length === 0) continue;
411
+ parameterizeLocators({
412
+ cmd,
413
+ locs: locators,
414
+ isValueVariable,
415
+ isTextVariable,
416
+ parametersMap,
417
+ });
418
+ }
419
+
420
+ locatorsObject.locators = allStrategyLocators[allStrategyLocators.strategy] ?? locatorsObject.locators;
421
+
422
+ return {
423
+ ...step,
424
+ locators: locatorsObject,
425
+ allStrategyLocators,
426
+ isLocatorsAssigned: true,
427
+ };
428
+ };
429
+
15
430
  export const toMethodName = (str) => {
16
431
  // Remove any non-word characters (excluding underscore) and trim spaces
17
432
  let cleanStr = str.trim().replace(/[^\w\s]/gi, "");
@@ -68,22 +483,82 @@ function makeStepTextUnique(step, stepsDefinitions) {
68
483
  step.text = stepText;
69
484
  }
70
485
 
71
- export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions }) {
72
- // console.log("saveRecording", step.text);
486
+ export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions, parametersMap }) {
487
+ if (step.commands && Array.isArray(step.commands)) {
488
+ step.commands = step.commands.map((cmd) => toRecordingStep(cmd, parametersMap));
489
+ }
490
+ let routesPath = path.join(tmpdir(), "blinq_temp_routes");
491
+
492
+ if (process.env.TEMP_RUN === "true") {
493
+ if (existsSync(routesPath)) {
494
+ rmSync(routesPath, { recursive: true });
495
+ }
496
+ mkdirSync(routesPath, { recursive: true });
497
+ saveRoutes({ step, folderPath: routesPath });
498
+ } else {
499
+ if (existsSync(routesPath)) {
500
+ // remove the folder
501
+ try {
502
+ rmSync(routesPath, { recursive: true });
503
+ console.log("Removed temp_routes_folder:", routesPath);
504
+ } catch (error) {
505
+ console.error("Error removing temp_routes folder", error);
506
+ }
507
+ routesPath = path.join(projectDir, "data", "routes");
508
+ if (!existsSync(routesPath)) {
509
+ mkdirSync(routesPath, { recursive: true });
510
+ }
511
+ saveRoutes({ step, folderPath: routesPath });
512
+ }
513
+ }
514
+
73
515
  if (step.isImplementedWhileRecording && !process.env.TEMP_RUN) {
74
516
  return;
75
517
  }
518
+
76
519
  if (step.isImplemented && step.shouldOverride) {
77
520
  let stepDef = stepsDefinitions.findMatchingStep(step.text);
78
521
  codePage = getCodePage(stepDef.file);
79
522
  } else {
80
523
  const isUtilStep = makeStepTextUnique(step, stepsDefinitions);
524
+
81
525
  if (isUtilStep) {
82
526
  return;
83
527
  }
84
528
  }
529
+
530
+ routesPath = path.join(tmpdir(), "blinq_temp_routes");
531
+ if (process.env.TEMP_RUN === "true") {
532
+ console.log("Save routes in temp folder for running:", routesPath);
533
+ if (existsSync(routesPath)) {
534
+ console.log("Removing existing temp_routes_folder:", routesPath);
535
+ rmSync(routesPath, { recursive: true });
536
+ }
537
+ mkdirSync(routesPath, { recursive: true });
538
+ console.log("Created temp_routes_folder:", routesPath);
539
+ saveRoutes({ step, folderPath: routesPath });
540
+ } else {
541
+ console.log("Saving routes in project directory:", projectDir);
542
+ if (existsSync(routesPath)) {
543
+ // remove the folder
544
+ try {
545
+ rmSync(routesPath, { recursive: true });
546
+ console.log("Removed temp_routes_folder:", routesPath);
547
+ } catch (error) {
548
+ console.error("Error removing temp_routes folder", error);
549
+ }
550
+ }
551
+ routesPath = path.join(projectDir, "data", "routes");
552
+ console.log("Saving routes to:", routesPath);
553
+ if (!existsSync(routesPath)) {
554
+ mkdirSync(routesPath, { recursive: true });
555
+ }
556
+ saveRoutes({ step, folderPath: routesPath });
557
+ }
558
+
85
559
  cucumberStep.text = step.text;
86
560
  const recording = new Recording();
561
+
87
562
  const steps = step.commands;
88
563
 
89
564
  recording.loadFromObject({ steps, step: cucumberStep });
@@ -108,6 +583,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
108
583
  isStaticToken,
109
584
  status,
110
585
  } = step.commands[0].value;
586
+
111
587
  const result = await generateApiCode(
112
588
  {
113
589
  url,
@@ -132,6 +608,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
132
608
  step.keyword,
133
609
  stepsDefinitions
134
610
  );
611
+
135
612
  if (!step.isImplemented) {
136
613
  stepsDefinitions.addStep({
137
614
  name: step.text,
@@ -139,6 +616,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
139
616
  source: "recorder",
140
617
  });
141
618
  }
619
+
142
620
  cucumberStep.methodName = result.methodName;
143
621
  return result.codePage;
144
622
  } else {
@@ -156,12 +634,18 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
156
634
  if (step.commands && step.commands.length > 0 && step.commands[0]) {
157
635
  path = step.commands[0].lastKnownUrlPath;
158
636
  }
637
+ let protect = false;
638
+ if (step.commands && step.commands.length > 0 && step.commands[0].type) {
639
+ if (step.commands[0].type === "verify_element_property" || step.commands[0].type === "conditional_wait") {
640
+ protect = true;
641
+ }
642
+ }
159
643
  const infraResult = codePage.addInfraCommand(
160
644
  methodName,
161
645
  description,
162
646
  cucumberStep.getVariablesList(),
163
647
  generateCodeResult.codeLines,
164
- false,
648
+ protect,
165
649
  "recorder",
166
650
  path
167
651
  );
@@ -183,6 +667,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
183
667
  }
184
668
 
185
669
  codePage.removeUnusedElements();
670
+ codePage.mergeSimilarElements();
186
671
  cucumberStep.methodName = methodName;
187
672
  if (generateCodeResult.locatorsMetadata) {
188
673
  codePage.addLocatorsMetadata(generateCodeResult.locatorsMetadata);
@@ -312,6 +797,12 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
312
797
  const utilsTemplateFilePath = path.join(__dirname, "../../assets", "templates", "utils_template.txt");
313
798
  const utilsContent = readFileSync(utilsTemplateFilePath, "utf8");
314
799
  writeFileSync(utilsFilePath, utilsContent, "utf8");
800
+ const hooksTemplateFilePath = path.join(__dirname, "../../assets", "templates", "_hooks_template.txt");
801
+ if (existsSync(hooksTemplateFilePath)) {
802
+ const hooksFilePath = path.join(stepDefinitionFolderPath, "_hooks.mjs");
803
+ const hooksContent = readFileSync(hooksTemplateFilePath, "utf8");
804
+ writeFileSync(hooksFilePath, hooksContent, "utf8");
805
+ }
315
806
  const steps = scenario.steps;
316
807
 
317
808
  const stepsDefinitions = new StepsDefinitions(projectDir);
@@ -327,6 +818,35 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
327
818
  }
328
819
  }
329
820
  if ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0) {
821
+ let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
822
+ if (process.env.TEMP_RUN === "true") {
823
+ console.log("Save routes in temp folder for running:", routesPath);
824
+ if (existsSync(routesPath)) {
825
+ console.log("Removing existing temp_routes_folder:", routesPath);
826
+ routesPath = path.join(tmpdir(), `blinq_temp_routes`);
827
+ rmSync(routesPath, { recursive: true });
828
+ }
829
+ mkdirSync(routesPath, { recursive: true });
830
+ console.log("Created temp_routes_folder:", routesPath);
831
+ saveRoutes({ step, folderPath: routesPath });
832
+ } else {
833
+ console.log("Saving routes in project directory:", projectDir);
834
+ if (existsSync(routesPath)) {
835
+ // remove the folder
836
+ try {
837
+ rmSync(routesPath, { recursive: true });
838
+ console.log("Removed temp_routes_folder:", routesPath);
839
+ } catch (error) {
840
+ console.error("Error removing temp_routes folder", error);
841
+ }
842
+ }
843
+ routesPath = path.join(projectDir, "data", "routes");
844
+ console.log("Saving routes to:", routesPath);
845
+ if (!existsSync(routesPath)) {
846
+ mkdirSync(routesPath, { recursive: true });
847
+ }
848
+ saveRoutes({ step, folderPath: routesPath });
849
+ }
330
850
  continue;
331
851
  }
332
852
  const cucumberStep = getCucumberStep({ step });
@@ -334,7 +854,14 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
334
854
  const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
335
855
  // path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
336
856
  let codePage = getCodePage(stepDefsFilePath);
337
- codePage = await saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions });
857
+ codePage = await saveRecording({
858
+ step,
859
+ cucumberStep,
860
+ codePage,
861
+ projectDir,
862
+ stepsDefinitions,
863
+ parametersMap: scenario.parametersMap,
864
+ });
338
865
  if (!codePage) {
339
866
  continue;
340
867
  }
@@ -345,3 +872,45 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
345
872
  }
346
873
  writeFileSync(utilsFilePath, utilsContent, "utf8");
347
874
  }
875
+
876
+ export function saveRoutes({ step, folderPath }) {
877
+ const routeItems = step.routeItems;
878
+ if (!routeItems || routeItems.length === 0) {
879
+ return;
880
+ }
881
+ const cucumberStep = getCucumberStep({ step });
882
+ const template = cucumberStep.getTemplate();
883
+ const stepNameHash = createHash("sha256").update(template).digest("hex");
884
+ console.log("Saving routes for step:", step.text, "with hash:", stepNameHash);
885
+
886
+ const routeItemsWithFilters = routeItems.map((routeItem) => {
887
+ const oldFilters = routeItem.filters;
888
+ const queryParamsObject = {};
889
+ oldFilters.queryParams.forEach((queryParam) => {
890
+ queryParamsObject[queryParam.key] = queryParam.value;
891
+ });
892
+ const newFilters = { path: oldFilters.path, method: oldFilters.method, queryParams: queryParamsObject };
893
+ return {
894
+ ...routeItem,
895
+ filters: newFilters,
896
+ };
897
+ });
898
+
899
+ const routesFilePath = path.join(folderPath, stepNameHash + ".json");
900
+ console.log("Routes file path:", routesFilePath);
901
+ const routesData = {
902
+ template,
903
+ routes: routeItemsWithFilters,
904
+ };
905
+ console.log("Routes data to save:", routesData);
906
+
907
+ if (!existsSync(folderPath)) {
908
+ mkdirSync(folderPath, { recursive: true });
909
+ }
910
+ try {
911
+ writeFileSync(routesFilePath, JSON.stringify(routesData, null, 2), "utf8");
912
+ console.log("Saved routes to", routesFilePath);
913
+ } catch (error) {
914
+ console.error("Failed to save routes to", routesFilePath, "Error:", error);
915
+ }
916
+ }