@dev-blinq/cucumber_client 1.0.1469-stage → 1.0.1470-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.
|
@@ -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
|
-
|
|
105
|
-
|
|
106
|
-
if (this.steps["
|
|
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
|
+
};
|
|
@@ -147,7 +147,7 @@ export class NamesService {
|
|
|
147
147
|
}
|
|
148
148
|
return result.data;
|
|
149
149
|
}
|
|
150
|
-
async generateLocatorDescriptions({ locatorsObj }) {
|
|
150
|
+
async generateLocatorDescriptions({ locatorsObj, }) {
|
|
151
151
|
const url = `${getRunsServiceBaseURL()}/locate/generate_locator_summaries`;
|
|
152
152
|
const result = await axiosClient({
|
|
153
153
|
url,
|
|
@@ -172,6 +172,7 @@ export class PublishService {
|
|
|
172
172
|
constructor(TOKEN) {
|
|
173
173
|
this.TOKEN = TOKEN;
|
|
174
174
|
}
|
|
175
|
+
//! deprecated
|
|
175
176
|
async saveScenario({ scenario, featureName, override, branch, isEditing, projectId, env, AICode }) {
|
|
176
177
|
const runsURL = getRunsServiceBaseURL();
|
|
177
178
|
const workspaceURL = runsURL.replace("/runs", "/workspace");
|
|
@@ -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,
|
|
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));
|
|
@@ -761,7 +770,7 @@ const getLocatorsJson = (file) => {
|
|
|
761
770
|
return {};
|
|
762
771
|
}
|
|
763
772
|
try {
|
|
764
|
-
const locatorsJson = readFileSync(locatorsFilePath,
|
|
773
|
+
const locatorsJson = readFileSync(locatorsFilePath, UTF8_ENCODING);
|
|
765
774
|
return JSON.parse(locatorsJson);
|
|
766
775
|
} catch (error) {
|
|
767
776
|
console.error(error);
|
|
@@ -863,7 +872,7 @@ export async function executeStep({ stepsDefinitions, cucumberStep, context, cod
|
|
|
863
872
|
}
|
|
864
873
|
}
|
|
865
874
|
|
|
866
|
-
export async function
|
|
875
|
+
export async function updateStepDefinitionsOld({ scenario, featureName, projectDir, logger }) {
|
|
867
876
|
// set the candidate step definition file name
|
|
868
877
|
// set the utils file path
|
|
869
878
|
const utilsFilePath = path.join(projectDir, "features", "step_definitions", "utils.mjs");
|
|
@@ -945,6 +954,130 @@ export async function updateStepDefinitions({ scenario, featureName, projectDir,
|
|
|
945
954
|
writeFileSync(utilsFilePath, utilsContent, "utf8");
|
|
946
955
|
}
|
|
947
956
|
|
|
957
|
+
export async function updateStepDefinitions({ scenario, featureName, projectDir, logger, bvtSnapshotsDir }) {
|
|
958
|
+
try {
|
|
959
|
+
const { featureFolder, utilsFilePath, utilsContent } = handleFileInitOps(projectDir);
|
|
960
|
+
const steps = scenario.steps;
|
|
961
|
+
|
|
962
|
+
const stepsDefinitions = new StepsDefinitions(projectDir);
|
|
963
|
+
await potentialErrorWrapper(
|
|
964
|
+
() => stepsDefinitions.load(false),
|
|
965
|
+
"Failed to load step definitions",
|
|
966
|
+
SaveJobErrorType.STEP_DEFINITIONS_LOADING_FAILED,
|
|
967
|
+
logger
|
|
968
|
+
);
|
|
969
|
+
|
|
970
|
+
for (let index = 0; index < steps.length; index++) {
|
|
971
|
+
const step = steps[index];
|
|
972
|
+
try {
|
|
973
|
+
if (step.internalImplementedStepId) {
|
|
974
|
+
const si = steps.findIndex((s) => s.id === step.internalImplementedStepId);
|
|
975
|
+
if (si !== -1) {
|
|
976
|
+
step.isImplementedWhileRecording = true;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
const processed = await potentialErrorWrapper(
|
|
981
|
+
() => {
|
|
982
|
+
if (!step.isUtilStep && ((step.isImplemented && !step.shouldOverride) || step.commands.length === 0)) {
|
|
983
|
+
let routesPath = path.join(tmpdir(), FIXED_FOLDER_NAMES.BLINQ_TEMP_ROUTES);
|
|
984
|
+
if (process.env.TEMP_RUN === "true") {
|
|
985
|
+
if (existsSync(routesPath)) rmSync(routesPath, { recursive: true });
|
|
986
|
+
mkdirSync(routesPath, { recursive: true });
|
|
987
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
988
|
+
} else {
|
|
989
|
+
if (existsSync(routesPath)) {
|
|
990
|
+
try {
|
|
991
|
+
rmSync(routesPath, { recursive: true });
|
|
992
|
+
} catch (error) {
|
|
993
|
+
logger.error(`❌ Error removing temp routes folder: ${getErrorMessage(error)}`);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
routesPath = path.join(projectDir, FIXED_FOLDER_NAMES.DATA, FIXED_FOLDER_NAMES.ROUTES);
|
|
997
|
+
if (!existsSync(routesPath)) mkdirSync(routesPath, { recursive: true });
|
|
998
|
+
saveRoutes({ step, folderPath: routesPath }, logger);
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
if (step.commands && Array.isArray(step.commands)) {
|
|
1002
|
+
step.commands = step.commands.map((cmd) => toRecordingStep(cmd, scenario.parametersMap));
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
return true;
|
|
1006
|
+
} else {
|
|
1007
|
+
return false;
|
|
1008
|
+
}
|
|
1009
|
+
},
|
|
1010
|
+
"Failed to process step commands and routes",
|
|
1011
|
+
SaveJobErrorType.STEP_COMMANDS_PROCESSING_FAILED,
|
|
1012
|
+
logger
|
|
1013
|
+
);
|
|
1014
|
+
if (processed) continue;
|
|
1015
|
+
|
|
1016
|
+
const { cucumberStep, stepDefsFilePath } = await potentialErrorWrapper(
|
|
1017
|
+
() => {
|
|
1018
|
+
const cucumberStep = getCucumberStep({ step });
|
|
1019
|
+
const pageName = generatePageName(step.startFrame?.url ?? "default", step.isUtilStep);
|
|
1020
|
+
const stepDefsFilePath = locateDefinitionPath(featureFolder, pageName);
|
|
1021
|
+
return { cucumberStep, stepDefsFilePath };
|
|
1022
|
+
},
|
|
1023
|
+
"Failed to initialize cucumber step and locate definition path",
|
|
1024
|
+
SaveJobErrorType.STEP_INITIALIZATION_FAILED,
|
|
1025
|
+
logger
|
|
1026
|
+
);
|
|
1027
|
+
|
|
1028
|
+
const codePage = await potentialErrorWrapper(
|
|
1029
|
+
async () => {
|
|
1030
|
+
let codePage = getCodePage(stepDefsFilePath);
|
|
1031
|
+
codePage = await saveRecording({
|
|
1032
|
+
step,
|
|
1033
|
+
cucumberStep,
|
|
1034
|
+
codePage,
|
|
1035
|
+
projectDir,
|
|
1036
|
+
stepsDefinitions,
|
|
1037
|
+
parametersMap: scenario.parametersMap,
|
|
1038
|
+
bvtSnapshotsDir,
|
|
1039
|
+
});
|
|
1040
|
+
return codePage;
|
|
1041
|
+
},
|
|
1042
|
+
"Failed to generate and save step definition",
|
|
1043
|
+
SaveJobErrorType.STEP_DEFINITION_UPDATE_FAILED,
|
|
1044
|
+
logger
|
|
1045
|
+
);
|
|
1046
|
+
|
|
1047
|
+
if (!codePage) continue;
|
|
1048
|
+
|
|
1049
|
+
const res = await codePage.save();
|
|
1050
|
+
if (!res) {
|
|
1051
|
+
throw new UpdateStepDefinitionsError({
|
|
1052
|
+
type: SaveJobErrorType.STEP_DEFINITION_UPDATE_FAILED,
|
|
1053
|
+
message: `Failed to save step definition for "${cucumberStep.text}" in "${codePage.sourceFileName}"`,
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
} catch (error) {
|
|
1057
|
+
throw new UpdateStepDefinitionsError({
|
|
1058
|
+
type: SaveJobErrorType.STEP_DEFINITION_UPDATE_FAILED,
|
|
1059
|
+
message: "Failed to update step definition",
|
|
1060
|
+
meta: {
|
|
1061
|
+
stepIndex: index,
|
|
1062
|
+
stepText: step.text,
|
|
1063
|
+
reason: getErrorMessage(error),
|
|
1064
|
+
},
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
writeFileSync(utilsFilePath, utilsContent, UTF8_ENCODING);
|
|
1070
|
+
} catch (error) {
|
|
1071
|
+
logger.error(`❌ updateStepDefinitions() failed: ${error?.message ?? String(error)}`);
|
|
1072
|
+
throw error instanceof UpdateStepDefinitionsError
|
|
1073
|
+
? error
|
|
1074
|
+
: new UpdateStepDefinitionsError({
|
|
1075
|
+
type: SaveJobErrorType.INTERNAL_ERROR,
|
|
1076
|
+
message: error?.message ?? "Unknown error",
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
948
1081
|
export function saveRoutes({ step, folderPath }, logger) {
|
|
949
1082
|
const routeItems = step.routeItems;
|
|
950
1083
|
if (!routeItems || routeItems.length === 0) {
|
|
@@ -978,8 +1111,8 @@ export function saveRoutes({ step, folderPath }, logger) {
|
|
|
978
1111
|
mkdirSync(folderPath, { recursive: true });
|
|
979
1112
|
}
|
|
980
1113
|
try {
|
|
981
|
-
writeFileSync(routesFilePath, JSON.stringify(routesData, null, 2),
|
|
1114
|
+
writeFileSync(routesFilePath, JSON.stringify(routesData, null, 2), UTF8_ENCODING);
|
|
982
1115
|
} catch (error) {
|
|
983
|
-
logger.error(
|
|
1116
|
+
logger.error(`❌ Error saving routes to ${routesFilePath}: ${getErrorMessage(error)}`);
|
|
984
1117
|
}
|
|
985
1118
|
}
|
|
@@ -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.js";
|
|
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
|
+
}
|