@dev-blinq/cucumber_client 1.0.1467-dev → 1.0.1467-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 (36) hide show
  1. package/bin/assets/bundled_scripts/recorder.js +73 -73
  2. package/bin/assets/scripts/recorder.js +87 -49
  3. package/bin/assets/scripts/snapshot_capturer.js +10 -17
  4. package/bin/assets/scripts/unique_locators.js +168 -40
  5. package/bin/assets/templates/_hooks_template.txt +6 -2
  6. package/bin/assets/templates/utils_template.txt +16 -16
  7. package/bin/client/code_cleanup/utils.js +16 -7
  8. package/bin/client/code_gen/code_inversion.js +127 -2
  9. package/bin/client/code_gen/duplication_analysis.js +2 -1
  10. package/bin/client/code_gen/function_signature.js +8 -0
  11. package/bin/client/code_gen/index.js +4 -0
  12. package/bin/client/code_gen/page_reflection.js +95 -10
  13. package/bin/client/code_gen/playwright_codeget.js +173 -77
  14. package/bin/client/cucumber/feature.js +4 -17
  15. package/bin/client/cucumber/steps_definitions.js +13 -0
  16. package/bin/client/recorderv3/bvt_init.js +310 -0
  17. package/bin/client/recorderv3/bvt_recorder.js +1560 -1183
  18. package/bin/client/recorderv3/implemented_steps.js +2 -0
  19. package/bin/client/recorderv3/index.js +3 -293
  20. package/bin/client/recorderv3/mixpanel.js +41 -0
  21. package/bin/client/recorderv3/services.js +838 -142
  22. package/bin/client/recorderv3/step_runner.js +36 -7
  23. package/bin/client/recorderv3/step_utils.js +171 -95
  24. package/bin/client/recorderv3/update_feature.js +87 -39
  25. package/bin/client/recorderv3/wbr_entry.js +61 -0
  26. package/bin/client/recording.js +1 -0
  27. package/bin/client/types/locators.js +2 -0
  28. package/bin/client/upload-service.js +2 -0
  29. package/bin/client/utils/app_dir.js +21 -0
  30. package/bin/client/utils/socket_logger.js +100 -125
  31. package/bin/index.js +4 -1
  32. package/package.json +12 -5
  33. package/bin/client/recorderv3/app_dir.js +0 -23
  34. package/bin/client/recorderv3/network.js +0 -299
  35. package/bin/client/recorderv3/scriptTest.js +0 -5
  36. package/bin/client/recorderv3/ws_server.js +0 -72
@@ -67,13 +67,12 @@ export class BVTStepRunner {
67
67
  resolve();
68
68
  }
69
69
  } else {
70
- console.warn(`No paused command found for cmdId: ${cmdId}`);
71
- socketLogger.error(`No paused command found for cmdId: ${cmdId}`);
70
+ socketLogger.error(`No paused command found for cmdId: ${cmdId}`, undefined, "BVTStepRunner.resumeExecution");
72
71
  }
73
72
  }
74
73
  }
75
74
 
76
- async copyCodetoTempFolder({ step, parametersMap, tempFolderPath }) {
75
+ async copyCodetoTempFolder({ step, parametersMap, tempFolderPath, AICode }) {
77
76
  if (!fs.existsSync(tempFolderPath)) {
78
77
  fs.mkdirSync(tempFolderPath);
79
78
  }
@@ -84,6 +83,18 @@ export class BVTStepRunner {
84
83
  overwrite: true,
85
84
  recursive: true,
86
85
  });
86
+
87
+ // If AICode is provided, save it as well
88
+ if (AICode) {
89
+ for (const { mjsFileContent, mjsFile } of AICode) {
90
+ const mjsPath = path
91
+ .normalize(mjsFile)
92
+ .split(path.sep)
93
+ .filter((part) => part !== "features")
94
+ .join(path.sep);
95
+ writeFileSync(path.join(tempFolderPath, mjsPath), mjsFileContent);
96
+ }
97
+ }
87
98
  }
88
99
 
89
100
  async writeTempFeatureFile({ step, parametersMap, tempFolderPath, tags }) {
@@ -250,7 +261,7 @@ export class BVTStepRunner {
250
261
  return { result, info };
251
262
  }
252
263
 
253
- async runStep({ step, parametersMap, envPath, tags, config }, bvtContext, options) {
264
+ async runStep({ step, parametersMap, envPath, tags, config, AICode }, bvtContext, options) {
254
265
  // Create a new AbortController for this specific step execution
255
266
  this.#currentStepController = new AbortController();
256
267
  const { signal } = this.#currentStepController;
@@ -284,13 +295,27 @@ export class BVTStepRunner {
284
295
  return cId;
285
296
  };
286
297
  }
298
+ if (bvtContext.api) {
299
+ bvtContext.api.getCmdId = () => {
300
+ if (cmdIDs.length === 0) {
301
+ cmdIDs = (step.commands || []).map((cmd) => cmd.cmdId ?? cmd.id);
302
+ }
303
+ const cId = cmdIDs.shift();
304
+ this.sendExecutionStatus({
305
+ type: "cmdExecutionStart",
306
+ cmdId: cId,
307
+ });
308
+ this.#lastAttemptedCmdId = cId;
309
+ return cId;
310
+ };
311
+ }
287
312
 
288
313
  const __temp_features_FolderName = "__temp_features" + Math.random().toString(36).substring(2, 7);
289
314
  const tempFolderPath = path.join(this.projectDir, __temp_features_FolderName);
290
315
  process.env.tempFeaturesFolderPath = __temp_features_FolderName;
291
316
  process.env.TESTCASE_REPORT_FOLDER_PATH = tempFolderPath;
292
317
 
293
- await this.copyCodetoTempFolder({ step, parametersMap, tempFolderPath });
318
+ await this.copyCodetoTempFolder({ step, parametersMap, tempFolderPath, AICode });
294
319
 
295
320
  // Write abort wrapper code with this step's signal
296
321
  await this.writeWrapperCode(tempFolderPath, signal);
@@ -306,8 +331,11 @@ export class BVTStepRunner {
306
331
  });
307
332
  }
308
333
 
309
- if (!step.isImplemented && step.commands.length > 0) {
310
- const pageName = generatePageName(step.startFrame?.url ?? "default");
334
+ if (
335
+ step.isUtilStep ||
336
+ ((!(step.isImplemented && !step.shouldOverride) || step.shouldMakeStepTextUnique) && step.commands.length > 0)
337
+ ) {
338
+ const pageName = generatePageName(step.startFrame?.url ?? "default", step.isUtilStep);
311
339
  const stepDefinitionFolderPath = path.join(tempFolderPath, "step_definitions");
312
340
  if (!existsSync(stepDefinitionFolderPath)) {
313
341
  mkdirSync(stepDefinitionFolderPath, { recursive: true });
@@ -321,6 +349,7 @@ export class BVTStepRunner {
321
349
  projectDir: this.projectDir,
322
350
  stepsDefinitions,
323
351
  parametersMap,
352
+ logger: socketLogger,
324
353
  });
325
354
  if (codePage) {
326
355
  await codePage.save(stepDefsFilePath);
@@ -1,7 +1,6 @@
1
- import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
2
- import path from "path";
3
- import url from "url";
4
- import logger from "../../logger.js";
1
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import url from "node:url";
5
4
  import { CodePage, getAiConfig } from "../code_gen/page_reflection.js";
6
5
  import { generateCode, generatePageName } from "../code_gen/playwright_codeget.js";
7
6
  import { invertCodeToCommand } from "../code_gen/code_inversion.js";
@@ -9,8 +8,9 @@ import { Step } from "../cucumber/feature.js";
9
8
  import { locateDefinitionPath, StepsDefinitions } from "../cucumber/steps_definitions.js";
10
9
  import { Recording } from "../recording.js";
11
10
  import { generateApiCode } from "../code_gen/api_codegen.js";
12
- import { tmpdir } from "os";
13
- import { createHash } from "crypto";
11
+ import { tmpdir } from "node:os";
12
+ import { createHash } from "node:crypto";
13
+ import { getErrorMessage } from "../utils/socket_logger.js";
14
14
 
15
15
  const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
16
16
 
@@ -48,7 +48,7 @@ const replaceLastOccurence = (str, search, replacement) => {
48
48
  return str.substring(0, lastIndex) + replacement + str.substring(lastIndex + search.length);
49
49
  };
50
50
 
51
- const _toRecordingStep = (cmd) => {
51
+ export const _toRecordingStep = (cmd, stepText) => {
52
52
  switch (cmd.type) {
53
53
  case "hover_element": {
54
54
  return {
@@ -250,6 +250,7 @@ const _toRecordingStep = (cmd) => {
250
250
  },
251
251
  parameters: [cmd.selectedField, cmd.value],
252
252
  lastKnownUrlPath: cmd.lastKnownUrlPath,
253
+ valueInStepText: stepText && typeof stepText === "string" ? stepText.includes(`"${cmd.value}"`) : false,
253
254
  };
254
255
  }
255
256
  case "conditional_wait": {
@@ -261,6 +262,7 @@ const _toRecordingStep = (cmd) => {
261
262
  },
262
263
  parameters: [cmd.timeout, cmd.selectedField, cmd.value],
263
264
  lastKnownUrlPath: cmd.lastKnownUrlPath,
265
+ valueInStepText: stepText && typeof stepText === "string" ? stepText.includes(`"${cmd.value}"`) : false,
264
266
  };
265
267
  }
266
268
  case "navigate": {
@@ -296,6 +298,8 @@ const _toRecordingStep = (cmd) => {
296
298
  type: "verify_page_snapshot",
297
299
  parameters: [cmd.value],
298
300
  selectors: cmd.selectors,
301
+ data: cmd.data,
302
+ valueInStepText: stepText && typeof stepText === "string" ? stepText.includes(`"${cmd.value}"`) : false,
299
303
  };
300
304
  }
301
305
  default: {
@@ -332,49 +336,62 @@ const _parameterizeLocators = (locators, replacementFromValue, replacementToValu
332
336
  }
333
337
  return locators;
334
338
  };
335
- const parameterizeLocators = ({ cmd, locs, isValueVariable, isTextVariable, parametersMap }) => {
339
+ const parameterizeLocators = ({ cmd, locs, isValueVariable, isTargetValueVariable, parametersMap }) => {
336
340
  if (isValueVariable) {
337
341
  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);
342
+ // const val = parametersMap[variable];
343
+ if (typeof cmd.text === "string") {
344
+ const replacementFromValue = cmd.text.trim().replace(/\s+/g, " ") ?? ""; // val.trim();
345
+ if (replacementFromValue.length > 0) {
346
+ const replacementToValue = `{${variable}}`;
347
+ locs = _parameterizeLocators(locs, replacementFromValue, replacementToValue);
348
+ }
349
+ }
350
+ }
351
+ if (isTargetValueVariable) {
352
+ const variable = cmd.targetValue.slice(1, -1);
353
+ // const val = parametersMap[variable];
354
+ if (typeof cmd.targetText === "string") {
355
+ const replacementFromValue = cmd.targetText.trim().replace(/\s+/g, " ") ?? ""; // val.trim();
356
+ if (replacementFromValue.length > 0) {
357
+ const replacementToValue = `{${variable}}`;
358
+ locs = _parameterizeLocators(locs, replacementFromValue, replacementToValue);
359
+ }
360
+ }
349
361
  }
350
362
  return locs;
351
363
  };
352
364
 
353
365
  //TODO: IMPORTAN
354
- export const toRecordingStep = (cmd, parametersMap) => {
366
+ export const toRecordingStep = (cmd, parametersMap, stepText) => {
355
367
  if (cmd.type === "api") {
356
368
  return {
357
369
  type: "api",
358
370
  value: cmd.value,
359
371
  };
360
372
  }
361
- const step = _toRecordingStep(cmd);
373
+ const step = _toRecordingStep(cmd, stepText);
362
374
  const cmdID = {
363
375
  cmdId: cmd.id,
376
+ options: cmd.options ?? null,
364
377
  };
365
378
  Object.assign(step, cmdID);
366
379
 
367
380
  const locatorsObject = JSON.parse(JSON.stringify(cmd.locators ?? null));
368
381
 
369
382
  if (!locatorsObject) return step;
383
+
384
+ const element_name = cmd?.locators?.element_name ?? `${cmd.label} ${cmd.role ?? "Text"}`;
385
+ locatorsObject.element_name = element_name;
386
+
370
387
  const isValueVariable = isVariable(cmd.value);
371
- const isTextVariable = isVariable(cmd.text);
388
+ const isTargetValueVariable = isVariable(cmd.targetValue);
372
389
  const allStrategyLocators = JSON.parse(JSON.stringify(cmd?.allStrategyLocators ?? null));
373
390
  step.locators = locatorsObject;
374
391
  step.allStrategyLocators = allStrategyLocators;
375
392
  step.isLocatorsAssigned = true;
376
393
 
377
- if (!isValueVariable && !isTextVariable) {
394
+ if (!isValueVariable && !isTargetValueVariable) {
378
395
  return step;
379
396
  }
380
397
 
@@ -389,7 +406,7 @@ export const toRecordingStep = (cmd, parametersMap) => {
389
406
  cmd,
390
407
  locs,
391
408
  isValueVariable,
392
- isTextVariable,
409
+ isTargetValueVariable,
393
410
  parametersMap,
394
411
  });
395
412
  locatorsObject.locators = locs;
@@ -408,7 +425,7 @@ export const toRecordingStep = (cmd, parametersMap) => {
408
425
  cmd,
409
426
  locs: locators,
410
427
  isValueVariable,
411
- isTextVariable,
428
+ isTargetValueVariable,
412
429
  parametersMap,
413
430
  });
414
431
  }
@@ -445,7 +462,7 @@ export function getCodePage(stepDefsFilePath) {
445
462
  export function getCucumberStep({ step }) {
446
463
  const cucumberStep = new Step();
447
464
  cucumberStep.loadFromJson({
448
- text: step.text,
465
+ text: step.renamedText ? step.renamedText : step.text,
449
466
  keyword: step.keyword,
450
467
  keywordType: step.keywordType,
451
468
  parameters: [],
@@ -463,12 +480,16 @@ export function getCucumberStep({ step }) {
463
480
 
464
481
  function makeStepTextUnique(step, stepsDefinitions) {
465
482
  // const utilsFilePath = path.join("features", "step_definitions", "utils.mjs");
466
- let stepText = step.text;
483
+ let stepText = step.renamedText ? step.renamedText : step.text;
467
484
  let stepIndex = 1;
468
485
  // console.log("makeStepTextUnique", step.text);
469
486
  let stepDef = stepsDefinitions.findMatchingStep(stepText);
470
487
  // console.log({ stepDef });
471
- if (stepDef && stepDef?.file.endsWith("utils.mjs")) {
488
+ if (
489
+ stepDef &&
490
+ (stepDef?.file.endsWith("utils.mjs") || stepDef?.file.endsWith("renamed_util.mjs")) &&
491
+ !step.shouldMakeStepTextUnique
492
+ ) {
472
493
  return true;
473
494
  }
474
495
  while (stepDef) {
@@ -479,7 +500,18 @@ function makeStepTextUnique(step, stepsDefinitions) {
479
500
  step.text = stepText;
480
501
  }
481
502
 
482
- export async function saveRecording({ step, cucumberStep, codePage, projectDir, stepsDefinitions, parametersMap }) {
503
+ export async function saveRecording({
504
+ step,
505
+ cucumberStep,
506
+ codePage,
507
+ projectDir,
508
+ stepsDefinitions,
509
+ parametersMap,
510
+ logger,
511
+ }) {
512
+ if (step.commands && Array.isArray(step.commands)) {
513
+ step.commands = step.commands.map((cmd) => toRecordingStep(cmd, parametersMap, step.text));
514
+ }
483
515
  let routesPath = path.join(tmpdir(), "blinq_temp_routes");
484
516
 
485
517
  if (process.env.TEMP_RUN === "true") {
@@ -487,21 +519,19 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
487
519
  rmSync(routesPath, { recursive: true });
488
520
  }
489
521
  mkdirSync(routesPath, { recursive: true });
490
- saveRoutes({ step, folderPath: routesPath });
522
+ saveRoutes({ step, folderPath: routesPath }, logger);
491
523
  } else {
492
524
  if (existsSync(routesPath)) {
493
- // remove the folder
494
525
  try {
495
526
  rmSync(routesPath, { recursive: true });
496
- console.log("Removed temp_routes_folder:", routesPath);
497
527
  } catch (error) {
498
- console.error("Error removing temp_routes folder", error);
528
+ logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
499
529
  }
500
530
  routesPath = path.join(projectDir, "data", "routes");
501
531
  if (!existsSync(routesPath)) {
502
532
  mkdirSync(routesPath, { recursive: true });
503
533
  }
504
- saveRoutes({ step, folderPath: routesPath });
534
+ saveRoutes({ step, folderPath: routesPath }, logger);
505
535
  }
506
536
  }
507
537
 
@@ -509,49 +539,63 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
509
539
  return;
510
540
  }
511
541
 
542
+ let isUtilStep = false;
543
+ let isChangedUtilStepName = false;
544
+
512
545
  if (step.isImplemented && step.shouldOverride) {
513
- let stepDef = stepsDefinitions.findMatchingStep(step.text);
546
+ const stepDef = stepsDefinitions.findMatchingStep(step.text);
514
547
  codePage = getCodePage(stepDef.file);
515
548
  } else {
516
- const isUtilStep = makeStepTextUnique(step, stepsDefinitions);
549
+ isUtilStep = makeStepTextUnique(step, stepsDefinitions);
517
550
 
518
551
  if (isUtilStep) {
519
- return;
552
+ isChangedUtilStepName =
553
+ step.renamedText && stepsDefinitions.findMatchingStep(step.renamedText)?.file.endsWith("renamed_util.mjs");
554
+
555
+ if (!isChangedUtilStepName) {
556
+ const isUtilStep = stepsDefinitions.findMatchingStep(step.text)?.file.endsWith("utils.mjs");
557
+ if (!step.renamedText || step.renamedText === step.text || isUtilStep) {
558
+ return;
559
+ }
560
+ step.text = step.text.trim();
561
+ const { functionName } = stepsDefinitions.findMatchingStep(step.renamedText);
562
+ step.renamedText = functionName;
563
+ const newImportLine = `import { ${functionName} } from "./utils.mjs";\n`;
564
+
565
+ if (!codePage.fileContent.includes(newImportLine)) {
566
+ codePage.fileContent = newImportLine + codePage.fileContent;
567
+ }
568
+ }
520
569
  }
521
570
  }
522
571
 
572
+ const renamedUtil = step.renamedText && isUtilStep;
573
+
523
574
  routesPath = path.join(tmpdir(), "blinq_temp_routes");
524
575
  if (process.env.TEMP_RUN === "true") {
525
- console.log("Save routes in temp folder for running:", routesPath);
526
576
  if (existsSync(routesPath)) {
527
- console.log("Removing existing temp_routes_folder:", routesPath);
528
577
  rmSync(routesPath, { recursive: true });
529
578
  }
530
579
  mkdirSync(routesPath, { recursive: true });
531
- console.log("Created temp_routes_folder:", routesPath);
532
- saveRoutes({ step, folderPath: routesPath });
580
+ saveRoutes({ step, folderPath: routesPath }, logger);
533
581
  } else {
534
- console.log("Saving routes in project directory:", projectDir);
535
582
  if (existsSync(routesPath)) {
536
- // remove the folder
537
583
  try {
538
584
  rmSync(routesPath, { recursive: true });
539
- console.log("Removed temp_routes_folder:", routesPath);
540
585
  } catch (error) {
541
- console.error("Error removing temp_routes folder", error);
586
+ logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
542
587
  }
543
588
  }
544
589
  routesPath = path.join(projectDir, "data", "routes");
545
- console.log("Saving routes to:", routesPath);
546
590
  if (!existsSync(routesPath)) {
547
591
  mkdirSync(routesPath, { recursive: true });
548
592
  }
549
- saveRoutes({ step, folderPath: routesPath });
593
+ saveRoutes({ step, folderPath: routesPath }, logger);
550
594
  }
551
595
 
552
596
  cucumberStep.text = step.text;
553
597
  const recording = new Recording();
554
- step.commands = step.commands.map((cmd) => toRecordingStep(cmd, parametersMap));
598
+
555
599
  const steps = step.commands;
556
600
 
557
601
  recording.loadFromObject({ steps, step: cucumberStep });
@@ -602,7 +646,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
602
646
  stepsDefinitions
603
647
  );
604
648
 
605
- if (!step.isImplemented) {
649
+ if (!renamedUtil && !(step.isImplemented && step.shouldOverride)) {
606
650
  stepsDefinitions.addStep({
607
651
  name: step.text,
608
652
  file: result.codePage.sourceFileName,
@@ -614,13 +658,16 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
614
658
  return result.codePage;
615
659
  } else {
616
660
  const generateCodeResult = generateCode(recording, codePage, userData, projectDir, methodName);
617
- if (generateCodeResult.noCode === true) {
618
- logger.log("No code generated for step: " + step.text);
661
+ console.log("Generated code for step:", step.text);
662
+ if (!isUtilStep && generateCodeResult.noCode === true) {
619
663
  return generateCodeResult.page;
620
664
  }
621
665
  codePage = generateCodeResult.page;
622
666
  methodName = generateCodeResult.methodName;
623
- codePage.insertElements(generateCodeResult.elements);
667
+
668
+ if (!renamedUtil) {
669
+ codePage.insertElements(generateCodeResult.elements);
670
+ }
624
671
 
625
672
  const description = cucumberStep.text;
626
673
  let path = null;
@@ -633,15 +680,43 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
633
680
  protect = true;
634
681
  }
635
682
  }
636
- const infraResult = codePage.addInfraCommand(
637
- methodName,
638
- description,
639
- cucumberStep.getVariablesList(),
640
- generateCodeResult.codeLines,
641
- protect,
642
- "recorder",
643
- path
644
- );
683
+
684
+ if (renamedUtil) {
685
+ if (isChangedUtilStepName) {
686
+ const newFileContent = codePage.fileContent
687
+ .replace(`async function ${step.renamedText}`, `async function ${methodName}`)
688
+ .replace(`Then("${step.renamedText}"`, `// Then("${step.renamedText}"`)
689
+ .replace(`When("${step.renamedText}"`, `// When("${step.renamedText}"`)
690
+ .replace(`Given("${step.renamedText}"`, `// Given("${step.renamedText}"`);
691
+
692
+ codePage._init();
693
+ codePage.generateModel(newFileContent);
694
+ } else {
695
+ codePage.addInfraCommandUtil(
696
+ methodName,
697
+ description,
698
+ cucumberStep.parameters,
699
+ generateCodeResult.codeLines,
700
+ step.renamedText,
701
+ step.text,
702
+ parametersMap,
703
+ protect,
704
+ "recorder",
705
+ path
706
+ );
707
+ }
708
+ } else {
709
+ codePage.addInfraCommand(
710
+ methodName,
711
+ description,
712
+ cucumberStep.getVariablesList(),
713
+ generateCodeResult.codeLines,
714
+ protect,
715
+ "recorder",
716
+ path
717
+ );
718
+ }
719
+
645
720
  const keyword = (cucumberStep.keywordAlias ?? cucumberStep.keyword).trim();
646
721
  const stepResult = codePage.addCucumberStep(
647
722
  keyword,
@@ -651,7 +726,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
651
726
  step.finalTimeout
652
727
  );
653
728
 
654
- if (!step.isImplemented) {
729
+ if (!renamedUtil && !(step.isImplemented && step.shouldOverride)) {
655
730
  stepsDefinitions.addStep({
656
731
  name: step.text,
657
732
  file: codePage.sourceFileName,
@@ -704,7 +779,7 @@ export const getCommandsForImplementedStep = (stepName, stepsDefinitions, stepPa
704
779
  const file = step?.file;
705
780
  const locatorsJson = getLocatorsJson(file);
706
781
  if (!step) {
707
- throw new Error("Step definition not found" + stepName);
782
+ throw new Error(`Step definition not found: ${stepName}`);
708
783
  }
709
784
  isImplemented = true;
710
785
  const { codeCommands, codePage, elements, parametersNames, error } =
@@ -712,26 +787,35 @@ export const getCommandsForImplementedStep = (stepName, stepsDefinitions, stepPa
712
787
  if (error) {
713
788
  throw new Error(error);
714
789
  }
790
+ isUtilStep = codePage.sourceFileName.endsWith("utils.mjs") || codePage.sourceFileName.endsWith("renamed_util.mjs");
715
791
 
716
792
  if (parametersNames.length !== stepParams.length) {
717
793
  // console.log("Parameters mismatch", parametersNames, stepParams);
718
794
  throw new Error("Parameters mismatch");
719
795
  }
720
- for (let i = 0; i < parametersNames.length; i++) {
721
- stepParams[i].argumentName = parametersNames[i];
722
- }
723
796
 
724
- isUtilStep = codePage.sourceFileName.endsWith("utils.mjs");
725
- for (const { code } of codeCommands) {
726
- const command = invertCodeToCommand(code, elements, stepParams, stepsDefinitions, codePage, stepName)[0];
727
- if (command === undefined || command.type === null) continue;
728
- if (command.element) {
729
- const key = command.element.key;
730
- if (key && locatorsJson[key]) {
731
- command.allStrategyLocators = locatorsJson[key];
797
+ const pattern = step.name;
798
+ if (isUtilStep && pattern === "Verify the file {string} exists") {
799
+ commands.push({
800
+ type: "verify_file_exists",
801
+ parameters: [stepParams[0].text],
802
+ });
803
+ } else {
804
+ for (let i = 0; i < parametersNames.length; i++) {
805
+ stepParams[i].argumentName = parametersNames[i];
806
+ }
807
+
808
+ for (const { code } of codeCommands) {
809
+ const command = invertCodeToCommand(code, elements, stepParams, stepsDefinitions, codePage, stepName)[0];
810
+ if (command === undefined || command.type === null) continue;
811
+ if (command.element) {
812
+ const key = command.element.key;
813
+ if (key && locatorsJson[key]) {
814
+ command.allStrategyLocators = locatorsJson[key];
815
+ }
732
816
  }
817
+ commands.push(command);
733
818
  }
734
- commands.push(command);
735
819
  }
736
820
  } catch (error) {
737
821
  console.error(error);
@@ -779,7 +863,7 @@ export async function executeStep({ stepsDefinitions, cucumberStep, context, cod
779
863
  }
780
864
  }
781
865
 
782
- export async function updateStepDefinitions({ scenario, featureName, projectDir }) {
866
+ export async function updateStepDefinitions({ scenario, featureName, projectDir, logger }) {
783
867
  // set the candidate step definition file name
784
868
  // set the utils file path
785
869
  const utilsFilePath = path.join(projectDir, "features", "step_definitions", "utils.mjs");
@@ -810,42 +894,37 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
810
894
  step.isImplementedWhileRecording = true;
811
895
  }
812
896
  }
813
- if ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0) {
897
+ if (!step.isUtilStep && ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0)) {
814
898
  let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
815
899
  if (process.env.TEMP_RUN === "true") {
816
- console.log("Save routes in temp folder for running:", routesPath);
817
900
  if (existsSync(routesPath)) {
818
- console.log("Removing existing temp_routes_folder:", routesPath);
819
901
  routesPath = path.join(tmpdir(), `blinq_temp_routes`);
820
902
  rmSync(routesPath, { recursive: true });
821
903
  }
822
904
  mkdirSync(routesPath, { recursive: true });
823
- console.log("Created temp_routes_folder:", routesPath);
824
- saveRoutes({ step, folderPath: routesPath });
905
+ saveRoutes({ step, folderPath: routesPath }, logger);
825
906
  } else {
826
- console.log("Saving routes in project directory:", projectDir);
827
907
  if (existsSync(routesPath)) {
828
- // remove the folder
829
908
  try {
830
909
  rmSync(routesPath, { recursive: true });
831
- console.log("Removed temp_routes_folder:", routesPath);
832
910
  } catch (error) {
833
- console.error("Error removing temp_routes folder", error);
911
+ logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
834
912
  }
835
913
  }
836
914
  routesPath = path.join(projectDir, "data", "routes");
837
- console.log("Saving routes to:", routesPath);
838
915
  if (!existsSync(routesPath)) {
839
916
  mkdirSync(routesPath, { recursive: true });
840
917
  }
841
- saveRoutes({ step, folderPath: routesPath });
918
+ saveRoutes({ step, folderPath: routesPath }, logger);
919
+ }
920
+ if (step.commands && Array.isArray(step.commands)) {
921
+ step.commands = step.commands.map((cmd) => toRecordingStep(cmd, scenario.parametersMap));
842
922
  }
843
923
  continue;
844
924
  }
845
925
  const cucumberStep = getCucumberStep({ step });
846
- const pageName = generatePageName(step.startFrame?.url ?? "default");
926
+ const pageName = generatePageName(step.startFrame?.url ?? "default", step.isUtilStep);
847
927
  const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
848
- // path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
849
928
  let codePage = getCodePage(stepDefsFilePath);
850
929
  codePage = await saveRecording({
851
930
  step,
@@ -866,7 +945,7 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
866
945
  writeFileSync(utilsFilePath, utilsContent, "utf8");
867
946
  }
868
947
 
869
- export function saveRoutes({ step, folderPath }) {
948
+ export function saveRoutes({ step, folderPath }, logger) {
870
949
  const routeItems = step.routeItems;
871
950
  if (!routeItems || routeItems.length === 0) {
872
951
  return;
@@ -889,21 +968,18 @@ export function saveRoutes({ step, folderPath }) {
889
968
  };
890
969
  });
891
970
 
892
- const routesFilePath = path.join(folderPath, stepNameHash + ".json");
893
- console.log("Routes file path:", routesFilePath);
971
+ const routesFilePath = path.join(folderPath, `${stepNameHash}.json`);
894
972
  const routesData = {
895
973
  template,
896
974
  routes: routeItemsWithFilters,
897
975
  };
898
- console.log("Routes data to save:", routesData);
899
976
 
900
977
  if (!existsSync(folderPath)) {
901
978
  mkdirSync(folderPath, { recursive: true });
902
979
  }
903
980
  try {
904
981
  writeFileSync(routesFilePath, JSON.stringify(routesData, null, 2), "utf8");
905
- console.log("Saved routes to", routesFilePath);
906
982
  } catch (error) {
907
- console.error("Failed to save routes to", routesFilePath, "Error:", error);
983
+ logger.error(`Error saving routes to ${routesFilePath}: ${getErrorMessage(error)}`);
908
984
  }
909
985
  }