@dev-blinq/cucumber_client 1.0.1469-dev → 1.0.1469-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 (43) 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 +125 -1
  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 +90 -9
  13. package/bin/client/code_gen/playwright_codeget.js +173 -77
  14. package/bin/client/codemod/find_harcoded_locators.js +173 -0
  15. package/bin/client/codemod/fix_hardcoded_locators.js +247 -0
  16. package/bin/client/codemod/index.js +8 -0
  17. package/bin/client/codemod/locators_array/find_misstructured_elements.js +148 -0
  18. package/bin/client/codemod/locators_array/fix_misstructured_elements.js +144 -0
  19. package/bin/client/codemod/locators_array/index.js +114 -0
  20. package/bin/client/codemod/types.js +1 -0
  21. package/bin/client/cucumber/feature.js +4 -17
  22. package/bin/client/cucumber/steps_definitions.js +13 -0
  23. package/bin/client/recorderv3/bvt_init.js +310 -0
  24. package/bin/client/recorderv3/bvt_recorder.js +1560 -1183
  25. package/bin/client/recorderv3/implemented_steps.js +2 -0
  26. package/bin/client/recorderv3/index.js +3 -293
  27. package/bin/client/recorderv3/mixpanel.js +41 -0
  28. package/bin/client/recorderv3/services.js +838 -142
  29. package/bin/client/recorderv3/step_runner.js +36 -7
  30. package/bin/client/recorderv3/step_utils.js +168 -95
  31. package/bin/client/recorderv3/update_feature.js +87 -39
  32. package/bin/client/recorderv3/wbr_entry.js +61 -0
  33. package/bin/client/recording.js +1 -0
  34. package/bin/client/types/locators.js +2 -0
  35. package/bin/client/upload-service.js +2 -0
  36. package/bin/client/utils/app_dir.js +21 -0
  37. package/bin/client/utils/socket_logger.js +100 -125
  38. package/bin/index.js +5 -1
  39. package/package.json +13 -5
  40. package/bin/client/recorderv3/app_dir.js +0 -23
  41. package/bin/client/recorderv3/network.js +0 -299
  42. package/bin/client/recorderv3/scriptTest.js +0 -5
  43. 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,9 +500,17 @@ 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
+ }) {
483
512
  if (step.commands && Array.isArray(step.commands)) {
484
- step.commands = step.commands.map((cmd) => toRecordingStep(cmd, parametersMap));
513
+ step.commands = step.commands.map((cmd) => toRecordingStep(cmd, parametersMap, step.text));
485
514
  }
486
515
  let routesPath = path.join(tmpdir(), "blinq_temp_routes");
487
516
 
@@ -490,21 +519,19 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
490
519
  rmSync(routesPath, { recursive: true });
491
520
  }
492
521
  mkdirSync(routesPath, { recursive: true });
493
- saveRoutes({ step, folderPath: routesPath });
522
+ saveRoutes({ step, folderPath: routesPath }, logger);
494
523
  } else {
495
524
  if (existsSync(routesPath)) {
496
- // remove the folder
497
525
  try {
498
526
  rmSync(routesPath, { recursive: true });
499
- console.log("Removed temp_routes_folder:", routesPath);
500
527
  } catch (error) {
501
- console.error("Error removing temp_routes folder", error);
528
+ logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
502
529
  }
503
530
  routesPath = path.join(projectDir, "data", "routes");
504
531
  if (!existsSync(routesPath)) {
505
532
  mkdirSync(routesPath, { recursive: true });
506
533
  }
507
- saveRoutes({ step, folderPath: routesPath });
534
+ saveRoutes({ step, folderPath: routesPath }, logger);
508
535
  }
509
536
  }
510
537
 
@@ -512,44 +539,58 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
512
539
  return;
513
540
  }
514
541
 
542
+ let isUtilStep = false;
543
+ let isChangedUtilStepName = false;
544
+
515
545
  if (step.isImplemented && step.shouldOverride) {
516
- let stepDef = stepsDefinitions.findMatchingStep(step.text);
546
+ const stepDef = stepsDefinitions.findMatchingStep(step.text);
517
547
  codePage = getCodePage(stepDef.file);
518
548
  } else {
519
- const isUtilStep = makeStepTextUnique(step, stepsDefinitions);
549
+ isUtilStep = makeStepTextUnique(step, stepsDefinitions);
520
550
 
521
551
  if (isUtilStep) {
522
- 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
+ }
523
569
  }
524
570
  }
525
571
 
572
+ const renamedUtil = step.renamedText && isUtilStep;
573
+
526
574
  routesPath = path.join(tmpdir(), "blinq_temp_routes");
527
575
  if (process.env.TEMP_RUN === "true") {
528
- console.log("Save routes in temp folder for running:", routesPath);
529
576
  if (existsSync(routesPath)) {
530
- console.log("Removing existing temp_routes_folder:", routesPath);
531
577
  rmSync(routesPath, { recursive: true });
532
578
  }
533
579
  mkdirSync(routesPath, { recursive: true });
534
- console.log("Created temp_routes_folder:", routesPath);
535
- saveRoutes({ step, folderPath: routesPath });
580
+ saveRoutes({ step, folderPath: routesPath }, logger);
536
581
  } else {
537
- console.log("Saving routes in project directory:", projectDir);
538
582
  if (existsSync(routesPath)) {
539
- // remove the folder
540
583
  try {
541
584
  rmSync(routesPath, { recursive: true });
542
- console.log("Removed temp_routes_folder:", routesPath);
543
585
  } catch (error) {
544
- console.error("Error removing temp_routes folder", error);
586
+ logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
545
587
  }
546
588
  }
547
589
  routesPath = path.join(projectDir, "data", "routes");
548
- console.log("Saving routes to:", routesPath);
549
590
  if (!existsSync(routesPath)) {
550
591
  mkdirSync(routesPath, { recursive: true });
551
592
  }
552
- saveRoutes({ step, folderPath: routesPath });
593
+ saveRoutes({ step, folderPath: routesPath }, logger);
553
594
  }
554
595
 
555
596
  cucumberStep.text = step.text;
@@ -605,7 +646,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
605
646
  stepsDefinitions
606
647
  );
607
648
 
608
- if (!step.isImplemented) {
649
+ if (!renamedUtil && !(step.isImplemented && step.shouldOverride)) {
609
650
  stepsDefinitions.addStep({
610
651
  name: step.text,
611
652
  file: result.codePage.sourceFileName,
@@ -617,13 +658,16 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
617
658
  return result.codePage;
618
659
  } else {
619
660
  const generateCodeResult = generateCode(recording, codePage, userData, projectDir, methodName);
620
- if (generateCodeResult.noCode === true) {
621
- logger.log("No code generated for step: " + step.text);
661
+ console.log("Generated code for step:", step.text);
662
+ if (!isUtilStep && generateCodeResult.noCode === true) {
622
663
  return generateCodeResult.page;
623
664
  }
624
665
  codePage = generateCodeResult.page;
625
666
  methodName = generateCodeResult.methodName;
626
- codePage.insertElements(generateCodeResult.elements);
667
+
668
+ if (!renamedUtil) {
669
+ codePage.insertElements(generateCodeResult.elements);
670
+ }
627
671
 
628
672
  const description = cucumberStep.text;
629
673
  let path = null;
@@ -636,15 +680,43 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
636
680
  protect = true;
637
681
  }
638
682
  }
639
- const infraResult = codePage.addInfraCommand(
640
- methodName,
641
- description,
642
- cucumberStep.getVariablesList(),
643
- generateCodeResult.codeLines,
644
- protect,
645
- "recorder",
646
- path
647
- );
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
+
648
720
  const keyword = (cucumberStep.keywordAlias ?? cucumberStep.keyword).trim();
649
721
  const stepResult = codePage.addCucumberStep(
650
722
  keyword,
@@ -654,7 +726,7 @@ export async function saveRecording({ step, cucumberStep, codePage, projectDir,
654
726
  step.finalTimeout
655
727
  );
656
728
 
657
- if (!step.isImplemented) {
729
+ if (!renamedUtil && !(step.isImplemented && step.shouldOverride)) {
658
730
  stepsDefinitions.addStep({
659
731
  name: step.text,
660
732
  file: codePage.sourceFileName,
@@ -707,7 +779,7 @@ export const getCommandsForImplementedStep = (stepName, stepsDefinitions, stepPa
707
779
  const file = step?.file;
708
780
  const locatorsJson = getLocatorsJson(file);
709
781
  if (!step) {
710
- throw new Error("Step definition not found" + stepName);
782
+ throw new Error(`Step definition not found: ${stepName}`);
711
783
  }
712
784
  isImplemented = true;
713
785
  const { codeCommands, codePage, elements, parametersNames, error } =
@@ -715,26 +787,35 @@ export const getCommandsForImplementedStep = (stepName, stepsDefinitions, stepPa
715
787
  if (error) {
716
788
  throw new Error(error);
717
789
  }
790
+ isUtilStep = codePage.sourceFileName.endsWith("utils.mjs") || codePage.sourceFileName.endsWith("renamed_util.mjs");
718
791
 
719
792
  if (parametersNames.length !== stepParams.length) {
720
793
  // console.log("Parameters mismatch", parametersNames, stepParams);
721
794
  throw new Error("Parameters mismatch");
722
795
  }
723
- for (let i = 0; i < parametersNames.length; i++) {
724
- stepParams[i].argumentName = parametersNames[i];
725
- }
726
796
 
727
- isUtilStep = codePage.sourceFileName.endsWith("utils.mjs");
728
- for (const { code } of codeCommands) {
729
- const command = invertCodeToCommand(code, elements, stepParams, stepsDefinitions, codePage, stepName)[0];
730
- if (command === undefined || command.type === null) continue;
731
- if (command.element) {
732
- const key = command.element.key;
733
- if (key && locatorsJson[key]) {
734
- 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
+ }
735
816
  }
817
+ commands.push(command);
736
818
  }
737
- commands.push(command);
738
819
  }
739
820
  } catch (error) {
740
821
  console.error(error);
@@ -782,7 +863,7 @@ export async function executeStep({ stepsDefinitions, cucumberStep, context, cod
782
863
  }
783
864
  }
784
865
 
785
- export async function updateStepDefinitions({ scenario, featureName, projectDir }) {
866
+ export async function updateStepDefinitions({ scenario, featureName, projectDir, logger }) {
786
867
  // set the candidate step definition file name
787
868
  // set the utils file path
788
869
  const utilsFilePath = path.join(projectDir, "features", "step_definitions", "utils.mjs");
@@ -813,42 +894,37 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
813
894
  step.isImplementedWhileRecording = true;
814
895
  }
815
896
  }
816
- if ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0) {
897
+ if (!step.isUtilStep && ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0)) {
817
898
  let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
818
899
  if (process.env.TEMP_RUN === "true") {
819
- console.log("Save routes in temp folder for running:", routesPath);
820
900
  if (existsSync(routesPath)) {
821
- console.log("Removing existing temp_routes_folder:", routesPath);
822
901
  routesPath = path.join(tmpdir(), `blinq_temp_routes`);
823
902
  rmSync(routesPath, { recursive: true });
824
903
  }
825
904
  mkdirSync(routesPath, { recursive: true });
826
- console.log("Created temp_routes_folder:", routesPath);
827
- saveRoutes({ step, folderPath: routesPath });
905
+ saveRoutes({ step, folderPath: routesPath }, logger);
828
906
  } else {
829
- console.log("Saving routes in project directory:", projectDir);
830
907
  if (existsSync(routesPath)) {
831
- // remove the folder
832
908
  try {
833
909
  rmSync(routesPath, { recursive: true });
834
- console.log("Removed temp_routes_folder:", routesPath);
835
910
  } catch (error) {
836
- console.error("Error removing temp_routes folder", error);
911
+ logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
837
912
  }
838
913
  }
839
914
  routesPath = path.join(projectDir, "data", "routes");
840
- console.log("Saving routes to:", routesPath);
841
915
  if (!existsSync(routesPath)) {
842
916
  mkdirSync(routesPath, { recursive: true });
843
917
  }
844
- 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));
845
922
  }
846
923
  continue;
847
924
  }
848
925
  const cucumberStep = getCucumberStep({ step });
849
- const pageName = generatePageName(step.startFrame?.url ?? "default");
926
+ const pageName = generatePageName(step.startFrame?.url ?? "default", step.isUtilStep);
850
927
  const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
851
- // path.join(stepDefinitionFolderPath, pageName + "_page.mjs");
852
928
  let codePage = getCodePage(stepDefsFilePath);
853
929
  codePage = await saveRecording({
854
930
  step,
@@ -869,7 +945,7 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir
869
945
  writeFileSync(utilsFilePath, utilsContent, "utf8");
870
946
  }
871
947
 
872
- export function saveRoutes({ step, folderPath }) {
948
+ export function saveRoutes({ step, folderPath }, logger) {
873
949
  const routeItems = step.routeItems;
874
950
  if (!routeItems || routeItems.length === 0) {
875
951
  return;
@@ -892,21 +968,18 @@ export function saveRoutes({ step, folderPath }) {
892
968
  };
893
969
  });
894
970
 
895
- const routesFilePath = path.join(folderPath, stepNameHash + ".json");
896
- console.log("Routes file path:", routesFilePath);
971
+ const routesFilePath = path.join(folderPath, `${stepNameHash}.json`);
897
972
  const routesData = {
898
973
  template,
899
974
  routes: routeItemsWithFilters,
900
975
  };
901
- console.log("Routes data to save:", routesData);
902
976
 
903
977
  if (!existsSync(folderPath)) {
904
978
  mkdirSync(folderPath, { recursive: true });
905
979
  }
906
980
  try {
907
981
  writeFileSync(routesFilePath, JSON.stringify(routesData, null, 2), "utf8");
908
- console.log("Saved routes to", routesFilePath);
909
982
  } catch (error) {
910
- console.error("Failed to save routes to", routesFilePath, "Error:", error);
983
+ logger.error(`Error saving routes to ${routesFilePath}: ${getErrorMessage(error)}`);
911
984
  }
912
985
  }