@dev-blinq/cucumber_client 1.0.1732-dev → 1.0.1733-dev

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.
@@ -100,18 +100,10 @@ class StepsDefinitions {
100
100
  this.initPage(codePage, mjsFile);
101
101
  }
102
102
  let stepCount = Object.keys(this.steps).length;
103
- if (this.steps["Before"]) {
104
- stepCount--;
105
- }
106
- if (this.steps["After"]) {
107
- stepCount--;
108
- }
109
- if (this.steps["BeforeAll"]) {
110
- stepCount--;
111
- }
112
- if (this.steps["AfterAll"]) {
113
- stepCount--;
114
- }
103
+ if (this.steps["Before"]) stepCount--;
104
+ if (this.steps["After"]) stepCount--;
105
+ if (this.steps["BeforeAll"]) stepCount--;
106
+ if (this.steps["AfterAll"]) stepCount--;
115
107
  if (print) {
116
108
  logger.info("total steps definitions found", stepCount);
117
109
  }
@@ -0,0 +1,45 @@
1
+ export const FIXED_FOLDER_NAMES = {
2
+ STEP_DEFINITIONS: "step_definitions",
3
+ FEATURES: "features",
4
+ ASSETS: "assets",
5
+ TEMPLATES: "templates",
6
+ BLINQ_TEMP_ROUTES: "blinq_temp_routes",
7
+ DATA: "data",
8
+ ROUTES: "routes",
9
+ };
10
+
11
+ export const FIXED_FILE_NAMES = {
12
+ UTILS: "utils.mjs",
13
+ UTILS_TEMPLATE: "utils_template.txt",
14
+ HOOKS_TEMPLATE: "_hooks_template.txt",
15
+ HOOKS: "_hooks.mjs",
16
+ };
17
+
18
+ export const UTF8_ENCODING = "utf8";
19
+
20
+ export class UpdateStepDefinitionsError extends Error {
21
+ constructor({ type, message, statusCode = 500, meta } = {}) {
22
+ super(message);
23
+
24
+ this.name = "UpdateStepDefinitionsError";
25
+ this.type = type;
26
+ this.statusCode = statusCode;
27
+ this.meta = meta;
28
+
29
+ if (typeof Object.setPrototypeOf === "function") {
30
+ Object.setPrototypeOf(this, new.target.prototype);
31
+ }
32
+ }
33
+ }
34
+ export const SaveJobErrorType = {
35
+ VALIDATION_ERROR: "VALIDATION_ERROR",
36
+ STEP_ERROR: "STEP_ERROR",
37
+ GIT_ERROR: "GIT_ERROR",
38
+ UNKNOWN: "UNKNOWN",
39
+ INTERNAL_ERROR: "INTERNAL_ERROR",
40
+ STEP_DEFINITION_UPDATE_FAILED: "STEP_DEFINITION_UPDATE_FAILED",
41
+ FILE_SYSTEM_ERROR: "FILE_SYSTEM_ERROR",
42
+ STEP_DEFINITIONS_LOADING_FAILED: "STEP_DEFINITIONS_LOADING_FAILED",
43
+ STEP_COMMANDS_PROCESSING_FAILED: "STEP_COMMANDS_PROCESSING_FAILED",
44
+ STEP_INITIALIZATION_FAILED: "STEP_INITIALIZATION_FAILED",
45
+ };
@@ -11,6 +11,14 @@ import { generateApiCode } from "../code_gen/api_codegen.js";
11
11
  import { tmpdir } from "node:os";
12
12
  import { createHash } from "node:crypto";
13
13
  import { getErrorMessage } from "../utils/socket_logger.js";
14
+ import {
15
+ FIXED_FILE_NAMES,
16
+ FIXED_FOLDER_NAMES,
17
+ SaveJobErrorType,
18
+ UpdateStepDefinitionsError,
19
+ UTF8_ENCODING,
20
+ } from "./constants.js";
21
+ import { handleFileInitOps, potentialErrorWrapper, processScenarioSteps, writeTemplateFiles } from "./utils.js";
14
22
 
15
23
  const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
16
24
 
@@ -454,7 +462,7 @@ export function getCodePage(stepDefsFilePath) {
454
462
  return codePage;
455
463
  }
456
464
  // const templateFile = path.join(__dirname, "../../assets", "templates", "page_template.txt");
457
- // const initialCode = readFileSync(templateFile, "utf8");
465
+ // const initialCode = readFileSync(templateFile, UTF8_ENCODING);
458
466
  const codePage = new CodePage();
459
467
  codePage.generateModel();
460
468
  return codePage;
@@ -508,6 +516,7 @@ export async function saveRecording({
508
516
  stepsDefinitions,
509
517
  parametersMap,
510
518
  logger,
519
+ bvtSnapshotsDir,
511
520
  }) {
512
521
  if (step.commands && Array.isArray(step.commands)) {
513
522
  step.commands = step.commands.map((cmd) => toRecordingStep(cmd, parametersMap, step.text));
@@ -762,7 +771,7 @@ const getLocatorsJson = (file) => {
762
771
  return {};
763
772
  }
764
773
  try {
765
- const locatorsJson = readFileSync(locatorsFilePath, "utf8");
774
+ const locatorsJson = readFileSync(locatorsFilePath, UTF8_ENCODING);
766
775
  return JSON.parse(locatorsJson);
767
776
  } catch (error) {
768
777
  console.error(error);
@@ -863,40 +872,101 @@ export async function executeStep({ stepsDefinitions, cucumberStep, context, cod
863
872
  throw new Error(`Step definition not found for "${cucumberStep.text}"`);
864
873
  }
865
874
  }
866
- class DomainError extends Error {
867
- constructor(message) {
868
- super(message);
869
- this.name = this.constructor.name;
870
- this.statusCode = 500; // default, can override in subclasses
871
- if (typeof Object.setPrototypeOf === "function") {
872
- Object.setPrototypeOf(this, new.target.prototype);
873
- }
874
- }
875
- }
876
- export async function updateStepDefinitions({ scenario, featureName, projectDir, logger }) {
877
- try {
878
- const utilsFilePath = path.join(projectDir, "features", "step_definitions", "utils.mjs");
879
- const stepDefinitionFolderPath = path.join(projectDir, "features", "step_definitions");
880
- if (!existsSync(stepDefinitionFolderPath)) {
881
- mkdirSync(stepDefinitionFolderPath, { recursive: true });
882
- }
883
875
 
884
- const utilsTemplateFilePath = path.join(__dirname, "../../assets", "templates", "utils_template.txt");
885
- const utilsContent = readFileSync(utilsTemplateFilePath, "utf8");
886
- writeFileSync(utilsFilePath, utilsContent, "utf8");
876
+ export async function updateStepDefinitionsOld({ scenario, featureName, projectDir, logger }) {
877
+ // set the candidate step definition file name
878
+ // set the utils file path
879
+ const utilsFilePath = path.join(projectDir, "features", "step_definitions", "utils.mjs");
880
+ const stepDefinitionFolderPath = path.join(projectDir, "features", "step_definitions");
881
+ if (!existsSync(stepDefinitionFolderPath)) {
882
+ mkdirSync(stepDefinitionFolderPath, { recursive: true });
883
+ }
884
+ const utilsTemplateFilePath = path.join(__dirname, "../../assets", "templates", "utils_template.txt");
885
+ const utilsContent = readFileSync(utilsTemplateFilePath, "utf8");
886
+ writeFileSync(utilsFilePath, utilsContent, "utf8");
887
+ const hooksTemplateFilePath = path.join(__dirname, "../../assets", "templates", "_hooks_template.txt");
888
+ if (existsSync(hooksTemplateFilePath)) {
889
+ const hooksFilePath = path.join(stepDefinitionFolderPath, "_hooks.mjs");
890
+ const hooksContent = readFileSync(hooksTemplateFilePath, "utf8");
891
+ writeFileSync(hooksFilePath, hooksContent, "utf8");
892
+ }
893
+ const steps = scenario.steps;
887
894
 
888
- const hooksTemplateFilePath = path.join(__dirname, "../../assets", "templates", "_hooks_template.txt");
889
- if (existsSync(hooksTemplateFilePath)) {
890
- const hooksFilePath = path.join(stepDefinitionFolderPath, "_hooks.mjs");
891
- const hooksContent = readFileSync(hooksTemplateFilePath, "utf8");
892
- writeFileSync(hooksFilePath, hooksContent, "utf8");
895
+ const stepsDefinitions = new StepsDefinitions(projectDir);
896
+ const featureFolder = path.join(projectDir, "features");
897
+ stepsDefinitions.load(false);
898
+ // const parameters = scenario.parameters;
899
+ // await saveRecordings({ steps, parameters, codePage, projectDir });
900
+ for (const step of steps) {
901
+ if (step.internalImplementedStepId) {
902
+ const si = steps.findIndex((s) => s.id === step.internalImplementedStepId);
903
+ if (si !== -1) {
904
+ step.isImplementedWhileRecording = true;
905
+ }
906
+ }
907
+ if (!step.isUtilStep && ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0)) {
908
+ let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
909
+ if (process.env.TEMP_RUN === "true") {
910
+ if (existsSync(routesPath)) {
911
+ routesPath = path.join(tmpdir(), `blinq_temp_routes`);
912
+ rmSync(routesPath, { recursive: true });
913
+ }
914
+ mkdirSync(routesPath, { recursive: true });
915
+ saveRoutes({ step, folderPath: routesPath }, logger);
916
+ } else {
917
+ if (existsSync(routesPath)) {
918
+ try {
919
+ rmSync(routesPath, { recursive: true });
920
+ } catch (error) {
921
+ logger.error(`Error removing temp routes folder: ${getErrorMessage(error)}`);
922
+ }
923
+ }
924
+ routesPath = path.join(projectDir, "data", "routes");
925
+ if (!existsSync(routesPath)) {
926
+ mkdirSync(routesPath, { recursive: true });
927
+ }
928
+ saveRoutes({ step, folderPath: routesPath }, logger);
929
+ }
930
+ if (step.commands && Array.isArray(step.commands)) {
931
+ step.commands = step.commands.map((cmd) => toRecordingStep(cmd, scenario.parametersMap));
932
+ }
933
+ continue;
934
+ }
935
+ const cucumberStep = getCucumberStep({ step });
936
+ const pageName = generatePageName(step.startFrame?.url ?? "default", step.isUtilStep);
937
+ const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
938
+ let codePage = getCodePage(stepDefsFilePath);
939
+ codePage = await saveRecording({
940
+ step,
941
+ cucumberStep,
942
+ codePage,
943
+ projectDir,
944
+ stepsDefinitions,
945
+ parametersMap: scenario.parametersMap,
946
+ });
947
+ if (!codePage) {
948
+ continue;
893
949
  }
950
+ const res = await codePage.save();
951
+ if (!res) {
952
+ throw new Error(`Failed to save step definition for "${cucumberStep.text}" in "${codePage.sourceFileName}"`);
953
+ }
954
+ }
955
+ writeFileSync(utilsFilePath, utilsContent, "utf8");
956
+ }
894
957
 
958
+ export async function updateStepDefinitions({ scenario, featureName, projectDir, logger, bvtSnapshotsDir }) {
959
+ try {
960
+ const { featureFolder, utilsFilePath, utilsContent } = handleFileInitOps(projectDir);
895
961
  const steps = scenario.steps;
896
- const stepsDefinitions = new StepsDefinitions(projectDir);
897
- const featureFolder = path.join(projectDir, "features");
898
962
 
899
- stepsDefinitions.load(false);
963
+ const stepsDefinitions = new StepsDefinitions(projectDir);
964
+ await potentialErrorWrapper(
965
+ () => stepsDefinitions.load(false),
966
+ "Failed to load step definitions",
967
+ SaveJobErrorType.STEP_DEFINITIONS_LOADING_FAILED,
968
+ logger
969
+ );
900
970
 
901
971
  for (let index = 0; index < steps.length; index++) {
902
972
  const step = steps[index];
@@ -908,66 +978,104 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir,
908
978
  }
909
979
  }
910
980
 
911
- if (!step.isUtilStep && ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0)) {
912
- let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
913
- if (process.env.TEMP_RUN === "true") {
914
- if (existsSync(routesPath)) rmSync(routesPath, { recursive: true });
915
- mkdirSync(routesPath, { recursive: true });
916
- saveRoutes({ step, folderPath: routesPath }, logger);
917
- } else {
918
- if (existsSync(routesPath)) {
919
- try {
920
- rmSync(routesPath, { recursive: true });
921
- } catch (error) {
922
- logger.error(`❌ Error removing temp routes folder: ${getErrorMessage(error)}`);
981
+ const processed = await potentialErrorWrapper(
982
+ () => {
983
+ if (!step.isUtilStep && ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0)) {
984
+ let routesPath = path.join(tmpdir(), FIXED_FOLDER_NAMES.BLINQ_TEMP_ROUTES);
985
+ if (process.env.TEMP_RUN === "true") {
986
+ if (existsSync(routesPath)) rmSync(routesPath, { recursive: true });
987
+ mkdirSync(routesPath, { recursive: true });
988
+ saveRoutes({ step, folderPath: routesPath }, logger);
989
+ } else {
990
+ if (existsSync(routesPath)) {
991
+ try {
992
+ rmSync(routesPath, { recursive: true });
993
+ } catch (error) {
994
+ logger.error(`❌ Error removing temp routes folder: ${getErrorMessage(error)}`);
995
+ }
996
+ }
997
+ routesPath = path.join(projectDir, FIXED_FOLDER_NAMES.DATA, FIXED_FOLDER_NAMES.ROUTES);
998
+ if (!existsSync(routesPath)) mkdirSync(routesPath, { recursive: true });
999
+ saveRoutes({ step, folderPath: routesPath }, logger);
923
1000
  }
924
- }
925
- routesPath = path.join(projectDir, "data", "routes");
926
- if (!existsSync(routesPath)) mkdirSync(routesPath, { recursive: true });
927
- saveRoutes({ step, folderPath: routesPath }, logger);
928
- }
929
1001
 
930
- if (step.commands && Array.isArray(step.commands)) {
931
- step.commands = step.commands.map((cmd) => toRecordingStep(cmd, scenario.parametersMap));
932
- }
1002
+ if (step.commands && Array.isArray(step.commands)) {
1003
+ step.commands = step.commands.map((cmd) => toRecordingStep(cmd, scenario.parametersMap));
1004
+ }
933
1005
 
934
- return;
935
- }
1006
+ return true;
1007
+ } else {
1008
+ return false;
1009
+ }
1010
+ },
1011
+ "Failed to process step commands and routes",
1012
+ SaveJobErrorType.STEP_COMMANDS_PROCESSING_FAILED,
1013
+ logger
1014
+ );
1015
+ if (processed) continue;
1016
+
1017
+ const { cucumberStep, stepDefsFilePath } = await potentialErrorWrapper(
1018
+ () => {
1019
+ const cucumberStep = getCucumberStep({ step });
1020
+ const pageName = generatePageName(step.startFrame?.url ?? "default", step.isUtilStep);
1021
+ const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
1022
+ return { cucumberStep, stepDefsFilePath };
1023
+ },
1024
+ "Failed to initialize cucumber step and locate definition path",
1025
+ SaveJobErrorType.STEP_INITIALIZATION_FAILED,
1026
+ logger
1027
+ );
936
1028
 
937
- const cucumberStep = getCucumberStep({ step });
938
- const pageName = generatePageName(step.startFrame?.url ?? "default", step.isUtilStep);
939
- const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
940
- let codePage = getCodePage(stepDefsFilePath);
941
-
942
- codePage = await saveRecording({
943
- step,
944
- cucumberStep,
945
- codePage,
946
- projectDir,
947
- stepsDefinitions,
948
- parametersMap: scenario.parametersMap,
949
- });
1029
+ const codePage = await potentialErrorWrapper(
1030
+ async () => {
1031
+ let codePage = getCodePage(stepDefsFilePath);
1032
+ codePage = await saveRecording({
1033
+ step,
1034
+ cucumberStep,
1035
+ codePage,
1036
+ projectDir,
1037
+ stepsDefinitions,
1038
+ parametersMap: scenario.parametersMap,
1039
+ bvtSnapshotsDir,
1040
+ });
1041
+ return codePage;
1042
+ },
1043
+ "Failed to generate and save step definition",
1044
+ SaveJobErrorType.STEP_DEFINITION_UPDATE_FAILED,
1045
+ logger
1046
+ );
950
1047
 
951
- if (!codePage) return;
1048
+ if (!codePage) continue;
952
1049
 
953
1050
  const res = await codePage.save();
954
1051
  if (!res) {
955
- throw new DomainError(
956
- `Failed to save step definition for "${cucumberStep.text}" in "${codePage.sourceFileName}"`
957
- );
1052
+ throw new UpdateStepDefinitionsError({
1053
+ type: SaveJobErrorType.STEP_DEFINITION_UPDATE_FAILED,
1054
+ message: `Failed to save step definition for "${cucumberStep.text}" in "${codePage.sourceFileName}"`,
1055
+ });
958
1056
  }
959
1057
  } catch (error) {
960
- logger.error(`❌ Failed to update step definition for step "${step.text}": ${getErrorMessage(error)}`);
961
- throw new DomainError(
962
- `Failed to update step definition for step "${step.text}" at index ${index}: ${getErrorMessage(error)}`
963
- );
1058
+ throw new UpdateStepDefinitionsError({
1059
+ type: SaveJobErrorType.STEP_DEFINITION_UPDATE_FAILED,
1060
+ message: "Failed to update step definition",
1061
+ meta: {
1062
+ stepIndex: index,
1063
+ stepText: step.text,
1064
+ reason: getErrorMessage(error),
1065
+ },
1066
+ });
964
1067
  }
965
1068
  }
966
1069
 
967
- writeFileSync(utilsFilePath, utilsContent, "utf8");
1070
+ writeFileSync(utilsFilePath, utilsContent, UTF8_ENCODING);
968
1071
  } catch (error) {
969
1072
  logger.error(`❌ updateStepDefinitions() failed: ${error?.message ?? String(error)}`);
970
- throw error instanceof DomainError ? error : new DomainError(error?.message ?? "Unknown error");
1073
+ throw error instanceof UpdateStepDefinitionsError
1074
+ ? error
1075
+ : new UpdateStepDefinitionsError({
1076
+ type: SaveJobErrorType.INTERNAL_ERROR,
1077
+ message: error?.message ?? "Unknown error",
1078
+ });
971
1079
  }
972
1080
  }
973
1081
 
@@ -1004,8 +1112,8 @@ export function saveRoutes({ step, folderPath }, logger) {
1004
1112
  mkdirSync(folderPath, { recursive: true });
1005
1113
  }
1006
1114
  try {
1007
- writeFileSync(routesFilePath, JSON.stringify(routesData, null, 2), "utf8");
1115
+ writeFileSync(routesFilePath, JSON.stringify(routesData, null, 2), UTF8_ENCODING);
1008
1116
  } catch (error) {
1009
- logger.error(`Error saving routes to ${routesFilePath}: ${getErrorMessage(error)}`);
1117
+ logger.error(`❌ Error saving routes to ${routesFilePath}: ${getErrorMessage(error)}`);
1010
1118
  }
1011
1119
  }
@@ -0,0 +1,74 @@
1
+ import { getErrorMessage } from "../utils/socket_logger.js";
2
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
+ import path from "node:path";
4
+ import {
5
+ FIXED_FILE_NAMES,
6
+ FIXED_FOLDER_NAMES,
7
+ SaveJobErrorType,
8
+ UpdateStepDefinitionsError,
9
+ UTF8_ENCODING,
10
+ } from "./constants";
11
+
12
+ export function handleFileInitOps(projectDir) {
13
+ try {
14
+ const stepDefinitionFolderPath = path.join(
15
+ projectDir,
16
+ FIXED_FOLDER_NAMES.FEATURES,
17
+ FIXED_FOLDER_NAMES.STEP_DEFINITIONS
18
+ );
19
+ if (!existsSync(stepDefinitionFolderPath)) {
20
+ mkdirSync(stepDefinitionFolderPath, { recursive: true });
21
+ }
22
+
23
+ const utilsFilePath = path.join(
24
+ projectDir,
25
+ FIXED_FOLDER_NAMES.FEATURES,
26
+ FIXED_FOLDER_NAMES.STEP_DEFINITIONS,
27
+ FIXED_FILE_NAMES.UTILS
28
+ );
29
+
30
+ const utilsTemplateFilePath = path.join(
31
+ __dirname,
32
+ `../../${FIXED_FOLDER_NAMES.ASSETS}`,
33
+ FIXED_FOLDER_NAMES.TEMPLATES,
34
+ FIXED_FILE_NAMES.UTILS_TEMPLATE
35
+ );
36
+ const utilsContent = readFileSync(utilsTemplateFilePath, UTF8_ENCODING);
37
+ writeFileSync(utilsFilePath, utilsContent, UTF8_ENCODING);
38
+
39
+ const hooksTemplateFilePath = path.join(
40
+ __dirname,
41
+ `../../${FIXED_FOLDER_NAMES.ASSETS}`,
42
+ FIXED_FOLDER_NAMES.TEMPLATES,
43
+ FIXED_FILE_NAMES.HOOKS_TEMPLATE
44
+ );
45
+ if (existsSync(hooksTemplateFilePath)) {
46
+ const hooksFilePath = path.join(stepDefinitionFolderPath, FIXED_FILE_NAMES.HOOKS);
47
+ const hooksContent = readFileSync(hooksTemplateFilePath, UTF8_ENCODING);
48
+ writeFileSync(hooksFilePath, hooksContent, UTF8_ENCODING);
49
+ }
50
+
51
+ const featureFolder = path.join(projectDir, FIXED_FOLDER_NAMES.FEATURES);
52
+
53
+ return { featureFolder, utilsFilePath, utilsContent };
54
+ } catch (error) {
55
+ throw new UpdateStepDefinitionsError({
56
+ type: SaveJobErrorType.FILE_SYSTEM_ERROR,
57
+ message: "Failed to initialize step definition folder structure",
58
+ meta: { reason: getErrorMessage(error) },
59
+ });
60
+ }
61
+ }
62
+
63
+ export async function potentialErrorWrapper(fn, errorMessage, type, logger) {
64
+ try {
65
+ return await fn();
66
+ } catch (error) {
67
+ logger.error(`❌ ${errorMessage}: ${getErrorMessage(error)}`);
68
+ throw new UpdateStepDefinitionsError({
69
+ type: type,
70
+ message: errorMessage,
71
+ meta: { reason: getErrorMessage(error) },
72
+ });
73
+ }
74
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dev-blinq/cucumber_client",
3
- "version": "1.0.1732-dev",
3
+ "version": "1.0.1733-dev",
4
4
  "description": " ",
5
5
  "main": "bin/index.js",
6
6
  "types": "bin/index.d.ts",