@dev-blinq/cucumber_client 1.0.1731-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,42 +872,104 @@ 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;
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}"`);
893
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
- steps.forEach(async (step, index) => {
971
+ for (let index = 0; index < steps.length; index++) {
972
+ const step = steps[index];
902
973
  try {
903
974
  if (step.internalImplementedStepId) {
904
975
  const si = steps.findIndex((s) => s.id === step.internalImplementedStepId);
@@ -907,66 +978,104 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir,
907
978
  }
908
979
  }
909
980
 
910
- if (!step.isUtilStep && ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0)) {
911
- let routesPath = path.join(tmpdir(), `blinq_temp_routes`);
912
- if (process.env.TEMP_RUN === "true") {
913
- if (existsSync(routesPath)) rmSync(routesPath, { recursive: true });
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)}`);
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);
922
1000
  }
923
- }
924
- routesPath = path.join(projectDir, "data", "routes");
925
- if (!existsSync(routesPath)) mkdirSync(routesPath, { recursive: true });
926
- saveRoutes({ step, folderPath: routesPath }, logger);
927
- }
928
1001
 
929
- if (step.commands && Array.isArray(step.commands)) {
930
- step.commands = step.commands.map((cmd) => toRecordingStep(cmd, scenario.parametersMap));
931
- }
1002
+ if (step.commands && Array.isArray(step.commands)) {
1003
+ step.commands = step.commands.map((cmd) => toRecordingStep(cmd, scenario.parametersMap));
1004
+ }
932
1005
 
933
- return;
934
- }
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
+ );
935
1028
 
936
- const cucumberStep = getCucumberStep({ step });
937
- const pageName = generatePageName(step.startFrame?.url ?? "default", step.isUtilStep);
938
- const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
939
- let codePage = getCodePage(stepDefsFilePath);
940
-
941
- codePage = await saveRecording({
942
- step,
943
- cucumberStep,
944
- codePage,
945
- projectDir,
946
- stepsDefinitions,
947
- parametersMap: scenario.parametersMap,
948
- });
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
+ );
949
1047
 
950
- if (!codePage) return;
1048
+ if (!codePage) continue;
951
1049
 
952
1050
  const res = await codePage.save();
953
1051
  if (!res) {
954
- throw new DomainError(
955
- `Failed to save step definition for "${cucumberStep.text}" in "${codePage.sourceFileName}"`
956
- );
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
+ });
957
1056
  }
958
1057
  } catch (error) {
959
- logger.error(`❌ Failed to update step definition for step "${step.text}": ${getErrorMessage(error)}`);
960
- throw new DomainError(
961
- `Failed to update step definition for step "${step.text}" at index ${index}: ${getErrorMessage(error)}`
962
- );
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
+ });
963
1067
  }
964
- });
1068
+ }
965
1069
 
966
- writeFileSync(utilsFilePath, utilsContent, "utf8");
1070
+ writeFileSync(utilsFilePath, utilsContent, UTF8_ENCODING);
967
1071
  } catch (error) {
968
1072
  logger.error(`❌ updateStepDefinitions() failed: ${error?.message ?? String(error)}`);
969
- 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
+ });
970
1079
  }
971
1080
  }
972
1081
 
@@ -1003,8 +1112,8 @@ export function saveRoutes({ step, folderPath }, logger) {
1003
1112
  mkdirSync(folderPath, { recursive: true });
1004
1113
  }
1005
1114
  try {
1006
- writeFileSync(routesFilePath, JSON.stringify(routesData, null, 2), "utf8");
1115
+ writeFileSync(routesFilePath, JSON.stringify(routesData, null, 2), UTF8_ENCODING);
1007
1116
  } catch (error) {
1008
- logger.error(`Error saving routes to ${routesFilePath}: ${getErrorMessage(error)}`);
1117
+ logger.error(`❌ Error saving routes to ${routesFilePath}: ${getErrorMessage(error)}`);
1009
1118
  }
1010
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.1731-dev",
3
+ "version": "1.0.1733-dev",
4
4
  "description": " ",
5
5
  "main": "bin/index.js",
6
6
  "types": "bin/index.d.ts",