@dev-blinq/cucumber_client 1.0.1301-dev → 1.0.1301-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 +37 -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 +575 -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,425 @@ 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
+
423
+ return {
424
+ ...step,
425
+ locators: locatorsObject,
426
+ allStrategyLocators,
427
+ isLocatorsAssigned: true,
428
+ };
429
+ };
430
+
15
431
  export const toMethodName = (str) => {
16
432
  // Remove any non-word characters (excluding underscore) and trim spaces
17
433
  let cleanStr = str.trim().replace(/[^\w\s]/gi, "");
@@ -68,22 +484,82 @@ function makeStepTextUnique(step, stepsDefinitions) {
68
484
  step.text = stepText;
69
485
  }
70
486
 
71
- export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions }) {
72
- // console.log("saveRecording", step.text);
487
+ export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions, parametersMap }) {
488
+ if (step.commands && Array.isArray(step.commands)) {
489
+ step.commands = step.commands.map((cmd) => toRecordingStep(cmd, parametersMap));
490
+ }
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
+
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,12 +635,18 @@ 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
  );
@@ -183,6 +668,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
183
668
  }
184
669
 
185
670
  codePage.removeUnusedElements();
671
+ codePage.mergeSimilarElements();
186
672
  cucumberStep.methodName = methodName;
187
673
  if (generateCodeResult.locatorsMetadata) {
188
674
  codePage.addLocatorsMetadata(generateCodeResult.locatorsMetadata);
@@ -312,6 +798,12 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
312
798
  const utilsTemplateFilePath = path.join(__dirname, "../../assets", "templates", "utils_template.txt");
313
799
  const utilsContent = readFileSync(utilsTemplateFilePath, "utf8");
314
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
+ }
315
807
  const steps = scenario.steps;
316
808
 
317
809
  const stepsDefinitions = new StepsDefinitions(projectDir);
@@ -327,6 +819,35 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
327
819
  }
328
820
  }
329
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
+ }
330
851
  continue;
331
852
  }
332
853
  const cucumberStep = getCucumberStep({ step });
@@ -334,7 +855,14 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
334
855
  const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
335
856
  // path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
336
857
  let codePage = getCodePage(stepDefsFilePath);
337
- codePage = await saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions });
858
+ codePage = await saveRecording({
859
+ step,
860
+ cucumberStep,
861
+ codePage,
862
+ projectDir,
863
+ stepsDefinitions,
864
+ parametersMap: scenario.parametersMap,
865
+ });
338
866
  if (!codePage) {
339
867
  continue;
340
868
  }
@@ -345,3 +873,45 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
345
873
  }
346
874
  writeFileSync(utilsFilePath, utilsContent, "utf8");
347
875
  }
876
+
877
+ export function saveRoutes({ step, folderPath }) {
878
+ const routeItems = step.routeItems;
879
+ if (!routeItems || routeItems.length === 0) {
880
+ return;
881
+ }
882
+ const cucumberStep = getCucumberStep({ step });
883
+ const template = cucumberStep.getTemplate();
884
+ const stepNameHash = createHash("sha256").update(template).digest("hex");
885
+ console.log("Saving routes for step:", step.text, "with hash:", stepNameHash);
886
+
887
+ const routeItemsWithFilters = routeItems.map((routeItem) => {
888
+ const oldFilters = routeItem.filters;
889
+ const queryParamsObject = {};
890
+ oldFilters.queryParams.forEach((queryParam) => {
891
+ queryParamsObject[queryParam.key] = queryParam.value;
892
+ });
893
+ const newFilters = { path: oldFilters.path, method: oldFilters.method, queryParams: queryParamsObject };
894
+ return {
895
+ ...routeItem,
896
+ filters: newFilters,
897
+ };
898
+ });
899
+
900
+ const routesFilePath = path.join(folderPath, stepNameHash + ".json");
901
+ console.log("Routes file path:", routesFilePath);
902
+ const routesData = {
903
+ template,
904
+ routes: routeItemsWithFilters,
905
+ };
906
+ console.log("Routes data to save:", routesData);
907
+
908
+ if (!existsSync(folderPath)) {
909
+ mkdirSync(folderPath, { recursive: true });
910
+ }
911
+ try {
912
+ writeFileSync(routesFilePath, JSON.stringify(routesData, null, 2), "utf8");
913
+ console.log("Saved routes to", routesFilePath);
914
+ } catch (error) {
915
+ console.error("Failed to save routes to", routesFilePath, "Error:", error);
916
+ }
917
+ }