@dev-blinq/cucumber_client 1.0.1289-dev → 1.0.1289-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 (53) 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 +906 -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/page_template.txt +2 -16
  18. package/bin/assets/templates/utils_template.txt +1 -46
  19. package/bin/client/apiTest/apiTest.js +6 -0
  20. package/bin/client/cli_helpers.js +11 -13
  21. package/bin/client/code_cleanup/utils.js +5 -1
  22. package/bin/client/code_gen/api_codegen.js +2 -2
  23. package/bin/client/code_gen/code_inversion.js +53 -4
  24. package/bin/client/code_gen/page_reflection.js +839 -902
  25. package/bin/client/code_gen/playwright_codeget.js +43 -12
  26. package/bin/client/cucumber/feature.js +89 -27
  27. package/bin/client/cucumber/feature_data.js +2 -2
  28. package/bin/client/cucumber/project_to_document.js +9 -3
  29. package/bin/client/cucumber/steps_definitions.js +90 -84
  30. package/bin/client/cucumber_selector.js +17 -1
  31. package/bin/client/local_agent.js +6 -5
  32. package/bin/client/parse_feature_file.js +23 -26
  33. package/bin/client/playground/projects/env.json +2 -2
  34. package/bin/client/project.js +186 -196
  35. package/bin/client/recorderv3/bvt_recorder.js +170 -60
  36. package/bin/client/recorderv3/implemented_steps.js +74 -16
  37. package/bin/client/recorderv3/index.js +57 -25
  38. package/bin/client/recorderv3/network.js +299 -0
  39. package/bin/client/recorderv3/scriptTest.js +1 -1
  40. package/bin/client/recorderv3/services.js +4 -16
  41. package/bin/client/recorderv3/step_runner.js +331 -68
  42. package/bin/client/recorderv3/step_utils.js +577 -7
  43. package/bin/client/recorderv3/update_feature.js +32 -30
  44. package/bin/client/run_cucumber.js +5 -1
  45. package/bin/client/scenario_report.js +0 -5
  46. package/bin/client/test_scenario.js +0 -1
  47. package/bin/client/upload-service.js +2 -2
  48. package/bin/client/utils/socket_logger.js +132 -0
  49. package/bin/index.js +1 -0
  50. package/bin/logger.js +3 -2
  51. package/bin/min/consoleApi.min.cjs +2 -3
  52. package/bin/min/injectedScript.min.cjs +16 -16
  53. package/package.json +21 -12
@@ -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,420 @@ 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
+ const isValueVariable = isVariable(cmd.value);
371
+ const isTextVariable = isVariable(cmd.text);
372
+ const allStrategyLocators = JSON.parse(JSON.stringify(cmd?.allStrategyLocators ?? null));
373
+ step.locators = locatorsObject;
374
+ step.allStrategyLocators = allStrategyLocators;
375
+ step.isLocatorsAssigned = true;
376
+
377
+ if (!isValueVariable && !isTextVariable) {
378
+ return step;
379
+ }
380
+
381
+ if (isValueVariable) {
382
+ step.dataSource = "parameters";
383
+ step.dataKey = convertToIdentifier(cmd.value.slice(1, -1));
384
+ }
385
+
386
+ if (!allStrategyLocators) {
387
+ let locs = locatorsObject.locators;
388
+ locs = parameterizeLocators({
389
+ cmd,
390
+ locs,
391
+ isValueVariable,
392
+ isTextVariable,
393
+ parametersMap,
394
+ });
395
+ locatorsObject.locators = locs;
396
+ return {
397
+ ...step,
398
+ locators: locatorsObject,
399
+ };
400
+ }
401
+
402
+ for (const key in allStrategyLocators) {
403
+ if (key === "strategy") continue;
404
+ if (key === "no_text" || key === "custom") continue;
405
+ const locators = allStrategyLocators[key];
406
+ if (locators.length === 0) continue;
407
+ parameterizeLocators({
408
+ cmd,
409
+ locs: locators,
410
+ isValueVariable,
411
+ isTextVariable,
412
+ parametersMap,
413
+ });
414
+ }
415
+
416
+ locatorsObject.locators = allStrategyLocators[allStrategyLocators.strategy] ?? locatorsObject.locators;
417
+
418
+ return {
419
+ ...step,
420
+ locators: locatorsObject,
421
+ allStrategyLocators,
422
+ isLocatorsAssigned: true,
423
+ };
424
+ };
425
+
15
426
  export const toMethodName = (str) => {
16
427
  // Remove any non-word characters (excluding underscore) and trim spaces
17
428
  let cleanStr = str.trim().replace(/[^\w\s]/gi, "");
@@ -68,22 +479,82 @@ function makeStepTextUnique(step, stepsDefinitions) {
68
479
  step.text = stepText;
69
480
  }
70
481
 
71
- export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions }) {
72
- // console.log("saveRecording", step.text);
482
+ export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions, parametersMap }) {
483
+ if (step.commands && Array.isArray(step.commands)) {
484
+ step.commands = step.commands.map((cmd) => toRecordingStep(cmd, parametersMap));
485
+ }
486
+ let routesPath = path.join(tmpdir(), "blinq_temp_routes");
487
+
488
+ if (process.env.TEMP_RUN === "true") {
489
+ if (existsSync(routesPath)) {
490
+ rmSync(routesPath, { recursive: true });
491
+ }
492
+ mkdirSync(routesPath, { recursive: true });
493
+ saveRoutes({ step, folderPath: routesPath });
494
+ } else {
495
+ if (existsSync(routesPath)) {
496
+ // remove the folder
497
+ try {
498
+ rmSync(routesPath, { recursive: true });
499
+ console.log("Removed temp_routes_folder:", routesPath);
500
+ } catch (error) {
501
+ console.error("Error removing temp_routes folder", error);
502
+ }
503
+ routesPath = path.join(projectDir, "data", "routes");
504
+ if (!existsSync(routesPath)) {
505
+ mkdirSync(routesPath, { recursive: true });
506
+ }
507
+ saveRoutes({ step, folderPath: routesPath });
508
+ }
509
+ }
510
+
73
511
  if (step.isImplementedWhileRecording && !process.env.TEMP_RUN) {
74
512
  return;
75
513
  }
514
+
76
515
  if (step.isImplemented && step.shouldOverride) {
77
516
  let stepDef = stepsDefinitions.findMatchingStep(step.text);
78
517
  codePage = getCodePage(stepDef.file);
79
518
  } else {
80
519
  const isUtilStep = makeStepTextUnique(step, stepsDefinitions);
520
+
81
521
  if (isUtilStep) {
82
522
  return;
83
523
  }
84
524
  }
525
+
526
+ routesPath = path.join(tmpdir(), "blinq_temp_routes");
527
+ if (process.env.TEMP_RUN === "true") {
528
+ console.log("Save routes in temp folder for running:", routesPath);
529
+ if (existsSync(routesPath)) {
530
+ console.log("Removing existing temp_routes_folder:", routesPath);
531
+ rmSync(routesPath, { recursive: true });
532
+ }
533
+ mkdirSync(routesPath, { recursive: true });
534
+ console.log("Created temp_routes_folder:", routesPath);
535
+ saveRoutes({ step, folderPath: routesPath });
536
+ } else {
537
+ console.log("Saving routes in project directory:", projectDir);
538
+ if (existsSync(routesPath)) {
539
+ // remove the folder
540
+ try {
541
+ rmSync(routesPath, { recursive: true });
542
+ console.log("Removed temp_routes_folder:", routesPath);
543
+ } catch (error) {
544
+ console.error("Error removing temp_routes folder", error);
545
+ }
546
+ }
547
+ routesPath = path.join(projectDir, "data", "routes");
548
+ console.log("Saving routes to:", routesPath);
549
+ if (!existsSync(routesPath)) {
550
+ mkdirSync(routesPath, { recursive: true });
551
+ }
552
+ saveRoutes({ step, folderPath: routesPath });
553
+ }
554
+
85
555
  cucumberStep.text = step.text;
86
556
  const recording = new Recording();
557
+
87
558
  const steps = step.commands;
88
559
 
89
560
  recording.loadFromObject({ steps, step: cucumberStep });
@@ -108,6 +579,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
108
579
  isStaticToken,
109
580
  status,
110
581
  } = step.commands[0].value;
582
+
111
583
  const result = await generateApiCode(
112
584
  {
113
585
  url,
@@ -132,6 +604,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
132
604
  step.keyword,
133
605
  stepsDefinitions
134
606
  );
607
+
135
608
  if (!step.isImplemented) {
136
609
  stepsDefinitions.addStep({
137
610
  name: step.text,
@@ -139,6 +612,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
139
612
  source: "recorder",
140
613
  });
141
614
  }
615
+
142
616
  cucumberStep.methodName = result.methodName;
143
617
  return result.codePage;
144
618
  } else {
@@ -156,17 +630,29 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
156
630
  if (step.commands && step.commands.length > 0 && step.commands[0]) {
157
631
  path = step.commands[0].lastKnownUrlPath;
158
632
  }
633
+ let protect = false;
634
+ if (step.commands && step.commands.length > 0 && step.commands[0].type) {
635
+ if (step.commands[0].type === "verify_element_property" || step.commands[0].type === "conditional_wait") {
636
+ protect = true;
637
+ }
638
+ }
159
639
  const infraResult = codePage.addInfraCommand(
160
640
  methodName,
161
641
  description,
162
642
  cucumberStep.getVariablesList(),
163
643
  generateCodeResult.codeLines,
164
- false,
644
+ protect,
165
645
  "recorder",
166
646
  path
167
647
  );
168
648
  const keyword = (cucumberStep.keywordAlias ?? cucumberStep.keyword).trim();
169
- const stepResult = codePage.addCucumberStep(keyword, cucumberStep.getTemplate(), methodName, steps.length);
649
+ const stepResult = codePage.addCucumberStep(
650
+ keyword,
651
+ cucumberStep.getTemplate(),
652
+ methodName,
653
+ steps.length,
654
+ step.finalTimeout
655
+ );
170
656
 
171
657
  if (!step.isImplemented) {
172
658
  stepsDefinitions.addStep({
@@ -177,6 +663,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
177
663
  }
178
664
 
179
665
  codePage.removeUnusedElements();
666
+ codePage.mergeSimilarElements();
180
667
  cucumberStep.methodName = methodName;
181
668
  if (generateCodeResult.locatorsMetadata) {
182
669
  codePage.addLocatorsMetadata(generateCodeResult.locatorsMetadata);
@@ -306,6 +793,12 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
306
793
  const utilsTemplateFilePath = path.join(__dirname, "../../assets", "templates", "utils_template.txt");
307
794
  const utilsContent = readFileSync(utilsTemplateFilePath, "utf8");
308
795
  writeFileSync(utilsFilePath, utilsContent, "utf8");
796
+ const hooksTemplateFilePath = path.join(__dirname, "../../assets", "templates", "_hooks_template.txt");
797
+ if (existsSync(hooksTemplateFilePath)) {
798
+ const hooksFilePath = path.join(stepDefinitionFolderPath, "_hooks.mjs");
799
+ const hooksContent = readFileSync(hooksTemplateFilePath, "utf8");
800
+ writeFileSync(hooksFilePath, hooksContent, "utf8");
801
+ }
309
802
  const steps = scenario.steps;
310
803
 
311
804
  const stepsDefinitions = new StepsDefinitions(projectDir);
@@ -321,6 +814,35 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
321
814
  }
322
815
  }
323
816
  if ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0) {
817
+ let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
818
+ if (process.env.TEMP_RUN === "true") {
819
+ console.log("Save routes in temp folder for running:", routesPath);
820
+ if (existsSync(routesPath)) {
821
+ console.log("Removing existing temp_routes_folder:", routesPath);
822
+ routesPath = path.join(tmpdir(), `blinq_temp_routes`);
823
+ rmSync(routesPath, { recursive: true });
824
+ }
825
+ mkdirSync(routesPath, { recursive: true });
826
+ console.log("Created temp_routes_folder:", routesPath);
827
+ saveRoutes({ step, folderPath: routesPath });
828
+ } else {
829
+ console.log("Saving routes in project directory:", projectDir);
830
+ if (existsSync(routesPath)) {
831
+ // remove the folder
832
+ try {
833
+ rmSync(routesPath, { recursive: true });
834
+ console.log("Removed temp_routes_folder:", routesPath);
835
+ } catch (error) {
836
+ console.error("Error removing temp_routes folder", error);
837
+ }
838
+ }
839
+ routesPath = path.join(projectDir, "data", "routes");
840
+ console.log("Saving routes to:", routesPath);
841
+ if (!existsSync(routesPath)) {
842
+ mkdirSync(routesPath, { recursive: true });
843
+ }
844
+ saveRoutes({ step, folderPath: routesPath });
845
+ }
324
846
  continue;
325
847
  }
326
848
  const cucumberStep = getCucumberStep({ step });
@@ -328,8 +850,14 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
328
850
  const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
329
851
  // path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
330
852
  let codePage = getCodePage(stepDefsFilePath);
331
-
332
- codePage = await saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions });
853
+ codePage = await saveRecording({
854
+ step,
855
+ cucumberStep,
856
+ codePage,
857
+ projectDir,
858
+ stepsDefinitions,
859
+ parametersMap: scenario.parametersMap,
860
+ });
333
861
  if (!codePage) {
334
862
  continue;
335
863
  }
@@ -340,3 +868,45 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
340
868
  }
341
869
  writeFileSync(utilsFilePath, utilsContent, "utf8");
342
870
  }
871
+
872
+ export function saveRoutes({ step, folderPath }) {
873
+ const routeItems = step.routeItems;
874
+ if (!routeItems || routeItems.length === 0) {
875
+ return;
876
+ }
877
+ const cucumberStep = getCucumberStep({ step });
878
+ const template = cucumberStep.getTemplate();
879
+ const stepNameHash = createHash("sha256").update(template).digest("hex");
880
+ console.log("Saving routes for step:", step.text, "with hash:", stepNameHash);
881
+
882
+ const routeItemsWithFilters = routeItems.map((routeItem) => {
883
+ const oldFilters = routeItem.filters;
884
+ const queryParamsObject = {};
885
+ oldFilters.queryParams.forEach((queryParam) => {
886
+ queryParamsObject[queryParam.key] = queryParam.value;
887
+ });
888
+ const newFilters = { path: oldFilters.path, method: oldFilters.method, queryParams: queryParamsObject };
889
+ return {
890
+ ...routeItem,
891
+ filters: newFilters,
892
+ };
893
+ });
894
+
895
+ const routesFilePath = path.join(folderPath, stepNameHash + ".json");
896
+ console.log("Routes file path:", routesFilePath);
897
+ const routesData = {
898
+ template,
899
+ routes: routeItemsWithFilters,
900
+ };
901
+ console.log("Routes data to save:", routesData);
902
+
903
+ if (!existsSync(folderPath)) {
904
+ mkdirSync(folderPath, { recursive: true });
905
+ }
906
+ try {
907
+ writeFileSync(routesFilePath, JSON.stringify(routesData, null, 2), "utf8");
908
+ console.log("Saved routes to", routesFilePath);
909
+ } catch (error) {
910
+ console.error("Failed to save routes to", routesFilePath, "Error:", error);
911
+ }
912
+ }