@dev-blinq/cucumber_client 1.0.1421-dev → 1.0.1421-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.
- package/bin/assets/bundled_scripts/recorder.js +105 -105
- package/bin/assets/preload/css_gen.js +10 -10
- package/bin/assets/preload/toolbar.js +27 -29
- package/bin/assets/preload/unique_locators.js +1 -1
- package/bin/assets/preload/yaml.js +288 -275
- package/bin/assets/scripts/aria_snapshot.js +223 -220
- package/bin/assets/scripts/dom_attr.js +329 -329
- package/bin/assets/scripts/dom_parent.js +169 -174
- package/bin/assets/scripts/event_utils.js +94 -94
- package/bin/assets/scripts/pw.js +2050 -1949
- package/bin/assets/scripts/recorder.js +70 -45
- package/bin/assets/scripts/snapshot_capturer.js +147 -147
- package/bin/assets/scripts/unique_locators.js +163 -44
- package/bin/assets/scripts/yaml.js +796 -783
- package/bin/assets/templates/_hooks_template.txt +6 -2
- package/bin/assets/templates/utils_template.txt +16 -16
- package/bin/client/code_cleanup/find_step_definition_references.js +0 -1
- package/bin/client/code_gen/api_codegen.js +2 -2
- package/bin/client/code_gen/code_inversion.js +63 -2
- package/bin/client/code_gen/function_signature.js +4 -0
- package/bin/client/code_gen/page_reflection.js +823 -1003
- package/bin/client/code_gen/playwright_codeget.js +25 -3
- package/bin/client/cucumber/feature_data.js +2 -2
- package/bin/client/cucumber/project_to_document.js +8 -2
- package/bin/client/cucumber/steps_definitions.js +19 -3
- package/bin/client/cucumber_selector.js +4 -0
- package/bin/client/local_agent.js +3 -2
- package/bin/client/parse_feature_file.js +23 -26
- package/bin/client/playground/projects/env.json +2 -2
- package/bin/client/project.js +186 -202
- package/bin/client/recorderv3/bvt_init.js +363 -0
- package/bin/client/recorderv3/bvt_recorder.js +1008 -47
- package/bin/client/recorderv3/implemented_steps.js +2 -0
- package/bin/client/recorderv3/index.js +4 -283
- package/bin/client/recorderv3/scriptTest.js +1 -1
- package/bin/client/recorderv3/services.js +810 -142
- package/bin/client/recorderv3/step_runner.js +37 -11
- package/bin/client/recorderv3/step_utils.js +495 -39
- package/bin/client/recorderv3/update_feature.js +32 -13
- package/bin/client/recorderv3/wbr_entry.js +61 -0
- package/bin/client/recording.js +1 -0
- package/bin/client/upload-service.js +4 -2
- package/bin/client/utils/socket_logger.js +1 -1
- package/bin/index.js +4 -1
- package/bin/logger.js +3 -2
- package/bin/min/consoleApi.min.cjs +2 -3
- package/bin/min/injectedScript.min.cjs +16 -16
- package/package.json +19 -9
|
@@ -5,6 +5,8 @@ import path from "path";
|
|
|
5
5
|
import { CodePage } from "./page_reflection.js";
|
|
6
6
|
import { convertToIdentifier, escapeNonPrintables } from "./utils.js";
|
|
7
7
|
import socketLogger from "../utils/socket_logger.js";
|
|
8
|
+
import fs from "fs";
|
|
9
|
+
|
|
8
10
|
const findElementIdentifier = (node, step, userData, elements) => {
|
|
9
11
|
if (node.key) {
|
|
10
12
|
// incase of rerunning implemented steps
|
|
@@ -105,6 +107,11 @@ const _generateCodeFromCommand = (step, elements, userData) => {
|
|
|
105
107
|
// handle element
|
|
106
108
|
let element_name = null;
|
|
107
109
|
let allStrategyLocators = null;
|
|
110
|
+
const codeLines = [];
|
|
111
|
+
|
|
112
|
+
if (_isCodeGenerationStep(step) === false)
|
|
113
|
+
return { codeLines, elements: elementsChanged ? elements : null, elementIdentifier, allStrategyLocators };
|
|
114
|
+
|
|
108
115
|
if (_isCodeGenerationStep(step) && step.type !== Types.VERIFY_PAGE_SNAPSHOT) {
|
|
109
116
|
allStrategyLocators = step.allStrategyLocators ?? splitToLocatorsGroups(step.locators);
|
|
110
117
|
let node = null;
|
|
@@ -167,8 +174,6 @@ const _generateCodeFromCommand = (step, elements, userData) => {
|
|
|
167
174
|
}
|
|
168
175
|
}
|
|
169
176
|
|
|
170
|
-
const codeLines = [];
|
|
171
|
-
|
|
172
177
|
let line = null;
|
|
173
178
|
let comment = null;
|
|
174
179
|
let input = null;
|
|
@@ -363,7 +368,7 @@ const _generateCodeFromCommand = (step, elements, userData) => {
|
|
|
363
368
|
codeLines.push(line);
|
|
364
369
|
break;
|
|
365
370
|
}
|
|
366
|
-
case Types.VERIFY_PAGE_SNAPSHOT:
|
|
371
|
+
case Types.VERIFY_PAGE_SNAPSHOT: {
|
|
367
372
|
comment = step.nestFrmLoc
|
|
368
373
|
? `// Verify page snapshot ${step.parameters[0]} in the frame ${step.selectors.iframe_src}`
|
|
369
374
|
: `// Verify page snapshot ${step.parameters[0]}`;
|
|
@@ -372,7 +377,24 @@ const _generateCodeFromCommand = (step, elements, userData) => {
|
|
|
372
377
|
codeLines.push(line);
|
|
373
378
|
line = `await context.web.snapshotValidation(frameLocator, _param_0 , _params, ${JSON.stringify(options)}, this);`;
|
|
374
379
|
codeLines.push(line);
|
|
380
|
+
|
|
381
|
+
const data = step.data;
|
|
382
|
+
if (data) {
|
|
383
|
+
try {
|
|
384
|
+
const { snapshot, fileName, filePath } = data;
|
|
385
|
+
const folderPath = process.env.BVT_TEMP_SNAPSHOTS_FOLDER ?? filePath;
|
|
386
|
+
const filePathWithName = path.join(folderPath, fileName);
|
|
387
|
+
if (!fs.existsSync(folderPath)) {
|
|
388
|
+
fs.mkdirSync(folderPath, { recursive: true });
|
|
389
|
+
}
|
|
390
|
+
fs.writeFileSync(filePathWithName, snapshot, "utf-8");
|
|
391
|
+
} catch (e) {
|
|
392
|
+
console.log(`Error saving snapshot file: ${e}`);
|
|
393
|
+
throw e;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
375
396
|
break;
|
|
397
|
+
}
|
|
376
398
|
case Types.VERIFY_PAGE_CONTAINS_TEXT:
|
|
377
399
|
line = "await context.web.verifyTextExistInPage( ";
|
|
378
400
|
input = `${escapeNonPrintables(JSON.stringify(step.parameters[0]))}`;
|
|
@@ -4,7 +4,13 @@ import { AstBuilder, GherkinClassicTokenMatcher, Parser } from "@cucumber/gherki
|
|
|
4
4
|
import { Feature } from "./feature.js";
|
|
5
5
|
import { StepsDefinitions } from "./steps_definitions.js";
|
|
6
6
|
|
|
7
|
-
export const projectDocument = (
|
|
7
|
+
export const projectDocument = (
|
|
8
|
+
folder,
|
|
9
|
+
featureName = null,
|
|
10
|
+
scenarioName = null,
|
|
11
|
+
supressErrors = false,
|
|
12
|
+
errors = []
|
|
13
|
+
) => {
|
|
8
14
|
const uuidFn = () => (23212).toString(16);
|
|
9
15
|
const builder = new AstBuilder(uuidFn);
|
|
10
16
|
const matcher = new GherkinClassicTokenMatcher();
|
|
@@ -59,4 +65,4 @@ export const projectDocument = (folder, featureName = null, scenarioName = null,
|
|
|
59
65
|
// const documents = projectDocument(projectDir, featureName, scenarioName, true, errors);
|
|
60
66
|
// const args = process.argv.slice(2);
|
|
61
67
|
|
|
62
|
-
// console.log(JSON.stringify(documents));
|
|
68
|
+
// console.log(JSON.stringify(documents));
|
|
@@ -36,6 +36,19 @@ class StepsDefinitions {
|
|
|
36
36
|
// }
|
|
37
37
|
// });
|
|
38
38
|
const { expressions, methods } = codePage;
|
|
39
|
+
|
|
40
|
+
if (codePage.fileContent.includes('from "./utils.mjs"')) {
|
|
41
|
+
const filePath = path.join(
|
|
42
|
+
this.baseFolder,
|
|
43
|
+
this.isTemp ? (process.env.tempFeaturesFolderPath ?? "__temp_features") : "features",
|
|
44
|
+
"step_definitions",
|
|
45
|
+
"utils.mjs"
|
|
46
|
+
);
|
|
47
|
+
const utilsCodePage = new CodePage(filePath);
|
|
48
|
+
utilsCodePage.generateModel();
|
|
49
|
+
methods.push(...utilsCodePage.methods);
|
|
50
|
+
}
|
|
51
|
+
|
|
39
52
|
for (let i = 0; i < expressions.length; i++) {
|
|
40
53
|
const expression = expressions[i];
|
|
41
54
|
const pattern = expression.pattern;
|
|
@@ -45,7 +58,10 @@ class StepsDefinitions {
|
|
|
45
58
|
if (method && expression.keyword && pattern && expression.methodName) {
|
|
46
59
|
this.steps[pattern] = {
|
|
47
60
|
// file: path.join(this.isTemp ? "__temp_features" : "features", mjsFile),
|
|
48
|
-
file: path.join(
|
|
61
|
+
file: path.join(
|
|
62
|
+
this.isTemp ? (process.env.tempFeaturesFolderPath ?? "__temp_features") : "features",
|
|
63
|
+
mjsFile
|
|
64
|
+
),
|
|
49
65
|
functionName: expression.methodName,
|
|
50
66
|
name: pattern,
|
|
51
67
|
source: this.findKey(method.codePart, "source"),
|
|
@@ -56,7 +72,7 @@ class StepsDefinitions {
|
|
|
56
72
|
}
|
|
57
73
|
load(print = true, supressErrors = false, errors = []) {
|
|
58
74
|
const mjsFiles = findFilesWithExtension(
|
|
59
|
-
path.join(this.baseFolder, this.isTemp ? process.env.tempFeaturesFolderPath ?? "__temp_features" : "features"),
|
|
75
|
+
path.join(this.baseFolder, this.isTemp ? (process.env.tempFeaturesFolderPath ?? "__temp_features") : "features"),
|
|
60
76
|
"mjs"
|
|
61
77
|
);
|
|
62
78
|
// console.log({ mjsFiles });
|
|
@@ -64,7 +80,7 @@ class StepsDefinitions {
|
|
|
64
80
|
const mjsFile = mjsFiles[i];
|
|
65
81
|
const filePath = path.join(
|
|
66
82
|
this.baseFolder,
|
|
67
|
-
this.isTemp ? process.env.tempFeaturesFolderPath ?? "__temp_features" : "features",
|
|
83
|
+
this.isTemp ? (process.env.tempFeaturesFolderPath ?? "__temp_features") : "features",
|
|
68
84
|
mjsFile
|
|
69
85
|
);
|
|
70
86
|
const codePage = new CodePage(filePath);
|
|
@@ -117,6 +117,10 @@ if (deleteMjsFiles) {
|
|
|
117
117
|
const utilsPath = path.join(__dirname, "../assets/templates/utils_template.txt");
|
|
118
118
|
const utilsContent = fs.readFileSync(utilsPath, "utf8");
|
|
119
119
|
const utilsFileName = stepsPath + "/utils.mjs";
|
|
120
|
+
// if stepsPath directory doesn't exist create it
|
|
121
|
+
if (!fs.existsSync(stepsPath)) {
|
|
122
|
+
fs.mkdirSync(stepsPath, { recursive: true });
|
|
123
|
+
}
|
|
120
124
|
fs.writeFileSync(utilsFileName, utilsContent, "utf8");
|
|
121
125
|
// copy hooks file to the steps directory
|
|
122
126
|
const hooksTemplateFilePath = path.join(__dirname, "../assets/templates/_hooks_template.txt");
|
|
@@ -696,6 +696,7 @@ class LocalAgent {
|
|
|
696
696
|
stepDiff: stepResult,
|
|
697
697
|
});
|
|
698
698
|
page.removeUnusedElements();
|
|
699
|
+
page.mergeSimilarElements();
|
|
699
700
|
agent.scenarioReport.updateLastStep({
|
|
700
701
|
code: {
|
|
701
702
|
cleanFileNames: page.cleanFileNames,
|
|
@@ -809,12 +810,12 @@ class LocalAgent {
|
|
|
809
810
|
} catch (err) {
|
|
810
811
|
logger.error("Error reading memory file: " + err.message);
|
|
811
812
|
}
|
|
812
|
-
const screenshot = await agent.getScreenshot(agent.context);
|
|
813
|
+
//const screenshot = await agent.getScreenshot(agent.context);
|
|
813
814
|
// check if the popup is open
|
|
814
815
|
const popupResult = await agent.context.web.closeUnexpectedPopups(null, null);
|
|
815
816
|
if (nodes) {
|
|
816
817
|
nodes.memory = memory;
|
|
817
|
-
nodes.screenshot = screenshot;
|
|
818
|
+
//nodes.screenshot = screenshot;
|
|
818
819
|
if (popupResult.rerun === true) {
|
|
819
820
|
nodes.rerun = true;
|
|
820
821
|
}
|
|
@@ -1,37 +1,34 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
let id =0;
|
|
9
|
-
const uuidFn = () => (++id).toString(16)
|
|
10
|
-
const builder = new AstBuilder(uuidFn)
|
|
11
|
-
const matcher = new GherkinClassicTokenMatcher()
|
|
1
|
+
import { AstBuilder, GherkinClassicTokenMatcher, Parser } from "@cucumber/gherkin";
|
|
2
|
+
import { readFileSync, writeFileSync } from "fs";
|
|
3
|
+
import { loadArgs, showUsage, validateCLIArg } from "./cli_helpers.js";
|
|
4
|
+
let id = 0;
|
|
5
|
+
const uuidFn = () => (++id).toString(16);
|
|
6
|
+
const builder = new AstBuilder(uuidFn);
|
|
7
|
+
const matcher = new GherkinClassicTokenMatcher();
|
|
12
8
|
|
|
13
|
-
const parser = new Parser(builder, matcher)
|
|
9
|
+
const parser = new Parser(builder, matcher);
|
|
14
10
|
|
|
15
11
|
const parseFeatureFile = (featureFilePath) => {
|
|
16
|
-
const source = readFileSync(featureFilePath,
|
|
17
|
-
const gherkinDocument = parser.parse(source)
|
|
18
|
-
return gherkinDocument
|
|
19
|
-
}
|
|
12
|
+
const source = readFileSync(featureFilePath, "utf8");
|
|
13
|
+
const gherkinDocument = parser.parse(source);
|
|
14
|
+
return gherkinDocument;
|
|
15
|
+
};
|
|
20
16
|
|
|
21
|
-
const args = loadArgs()
|
|
22
|
-
const featureFilePath = args[0]
|
|
23
|
-
const outputFilePath = args[1]
|
|
24
|
-
const usage =
|
|
17
|
+
const args = loadArgs();
|
|
18
|
+
const featureFilePath = args[0];
|
|
19
|
+
const outputFilePath = args[1];
|
|
20
|
+
const usage =
|
|
21
|
+
"Usage: node parse_feature_file.js <featureFilePath> [<outputFilePath>] \n\nExample: node parseFeatureFile.js ./features/my.feature ./features/my.feature.json\n\n";
|
|
25
22
|
try {
|
|
26
|
-
validateCLIArg(featureFilePath,
|
|
23
|
+
validateCLIArg(featureFilePath, "featureFilePath");
|
|
27
24
|
} catch (error) {
|
|
28
|
-
showUsage(error, usage)
|
|
25
|
+
showUsage(error, usage);
|
|
29
26
|
}
|
|
30
|
-
const gherkinDocument = parseFeatureFile(featureFilePath)
|
|
27
|
+
const gherkinDocument = parseFeatureFile(featureFilePath);
|
|
31
28
|
if (outputFilePath) {
|
|
32
|
-
writeFileSync(outputFilePath, JSON.stringify(gherkinDocument, null, 2))
|
|
29
|
+
writeFileSync(outputFilePath, JSON.stringify(gherkinDocument, null, 2));
|
|
33
30
|
} else {
|
|
34
|
-
console.log(JSON.stringify(gherkinDocument, null, 2))
|
|
31
|
+
console.log(JSON.stringify(gherkinDocument, null, 2));
|
|
35
32
|
}
|
|
36
33
|
|
|
37
|
-
export { parseFeatureFile }
|
|
34
|
+
export { parseFeatureFile };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
}
|
|
2
|
+
"baseUrl": "https://google.com"
|
|
3
|
+
}
|
package/bin/client/project.js
CHANGED
|
@@ -1,220 +1,204 @@
|
|
|
1
|
+
// project.ts
|
|
1
2
|
import { writeFileSync, readFileSync, readdirSync, existsSync, mkdirSync } from "fs";
|
|
2
|
-
import { CodePage } from "./code_gen/page_reflection.js";
|
|
3
|
-
|
|
4
|
-
import logger from "../logger.js";
|
|
5
3
|
import path from "path";
|
|
6
4
|
import { fileURLToPath } from "url";
|
|
5
|
+
// Keep the .js extensions because the compiled runtime modules are ESM .js files.
|
|
6
|
+
import { CodePage } from "./code_gen/page_reflection.js";
|
|
7
|
+
import logger from "../logger.js";
|
|
7
8
|
import { findFilesWithExtension } from "./cucumber/steps_definitions.js";
|
|
8
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
10
|
const __dirname = path.dirname(__filename);
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
async load() {
|
|
20
|
-
this.pages = [];
|
|
21
|
-
// load env.json from the project root folder
|
|
22
|
-
let envFile = "";
|
|
23
|
-
const envArgVal = checkForEnvArg();
|
|
24
|
-
if (envArgVal) {
|
|
25
|
-
envFile = envArgVal;
|
|
26
|
-
} else if (process.env["BLINQ_ENV"]) {
|
|
27
|
-
envFile = process.env["BLINQ_ENV"];
|
|
28
|
-
} else if (existsSync(path.join(this.rootFolder, "env.json"))) {
|
|
29
|
-
envFile = path.join(this.rootFolder, "env.json");
|
|
30
|
-
} else {
|
|
31
|
-
//list all the files in the environments directory use the first json file
|
|
32
|
-
let files = readdirSync(path.join(this.rootFolder, "environments"));
|
|
33
|
-
for (let i = 0; i < files.length; i++) {
|
|
34
|
-
let file = files[i];
|
|
35
|
-
if (file.endsWith(".json")) {
|
|
36
|
-
envFile = path.join(this.rootFolder, "environments", file);
|
|
37
|
-
break;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
// const envFile = path.join(this.rootFolder, "env.json");
|
|
42
|
-
if (!existsSync(envFile) || envFile === "") {
|
|
43
|
-
throw new Error(
|
|
44
|
-
`
|
|
45
|
-
======================================================
|
|
46
|
-
env.json file not found in the project root folder
|
|
47
|
-
or in the environments folder
|
|
48
|
-
======================================================
|
|
49
|
-
`
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
let envFileContent = readFileSync(envFile, "utf8");
|
|
53
|
-
try {
|
|
54
|
-
this.environment = JSON.parse(envFileContent);
|
|
55
|
-
} catch (e) {
|
|
56
|
-
throw new Error(
|
|
57
|
-
`
|
|
58
|
-
======================================================
|
|
59
|
-
unable to parse environment file
|
|
60
|
-
${e}
|
|
61
|
-
======================================================
|
|
62
|
-
`
|
|
63
|
-
);
|
|
11
|
+
function checkForEnvArg() {
|
|
12
|
+
for (const arg of process.argv.slice(2)) {
|
|
13
|
+
if (arg.startsWith("--")) {
|
|
14
|
+
const [key, value] = arg.split("=");
|
|
15
|
+
if (key.slice(2) === "env") {
|
|
16
|
+
return value ?? null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
64
19
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
`
|
|
73
|
-
);
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
function generatePrompt(promptFile, parameters, chat = true) {
|
|
23
|
+
let promptFileContent = readFileSync(promptFile, "utf8");
|
|
24
|
+
for (const key in parameters) {
|
|
25
|
+
const value = parameters[key];
|
|
26
|
+
promptFileContent = promptFileContent.replaceAll(`{${key}}`, value);
|
|
74
27
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
28
|
+
if (!chat)
|
|
29
|
+
return { promptFileContent };
|
|
30
|
+
const messages = [];
|
|
31
|
+
const parts = promptFileContent.split("####");
|
|
32
|
+
for (let i = 0; i < parts.length; i++) {
|
|
33
|
+
if (!parts[i].trim())
|
|
34
|
+
continue;
|
|
35
|
+
const role = parts[i];
|
|
36
|
+
const prompt = parts[i + 1];
|
|
37
|
+
messages.push({ role, content: (prompt ?? "").trim() });
|
|
38
|
+
i++;
|
|
78
39
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
writeFileSync(hooksFilePath, hooksContent, "utf8");
|
|
90
|
-
}
|
|
91
|
-
} catch (e) {
|
|
92
|
-
logger.error("Error loading utils_template");
|
|
40
|
+
return { messages, promptFileContent };
|
|
41
|
+
}
|
|
42
|
+
class Project {
|
|
43
|
+
rootFolder;
|
|
44
|
+
stepsFolder;
|
|
45
|
+
environment = null;
|
|
46
|
+
pages = [];
|
|
47
|
+
constructor(rootFolder) {
|
|
48
|
+
this.rootFolder = rootFolder;
|
|
49
|
+
this.stepsFolder = path.join(rootFolder, "features", "step_definitions");
|
|
93
50
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
51
|
+
async load() {
|
|
52
|
+
this.pages = [];
|
|
53
|
+
// Resolve env file path
|
|
54
|
+
let envFile = "";
|
|
55
|
+
const envArgVal = checkForEnvArg();
|
|
56
|
+
if (envArgVal) {
|
|
57
|
+
envFile = envArgVal;
|
|
58
|
+
}
|
|
59
|
+
else if (process.env["BLINQ_ENV"]) {
|
|
60
|
+
envFile = process.env["BLINQ_ENV"];
|
|
61
|
+
}
|
|
62
|
+
else if (existsSync(path.join(this.rootFolder, "env.json"))) {
|
|
63
|
+
envFile = path.join(this.rootFolder, "env.json");
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// pick the first .json from environments/
|
|
67
|
+
const envDir = path.join(this.rootFolder, "environments");
|
|
68
|
+
const files = existsSync(envDir) ? readdirSync(envDir) : [];
|
|
69
|
+
for (let i = 0; i < files.length; i++) {
|
|
70
|
+
const file = files[i];
|
|
71
|
+
if (file.endsWith(".json")) {
|
|
72
|
+
envFile = path.join(envDir, file);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (!existsSync(envFile) || envFile === "") {
|
|
78
|
+
throw new Error(`
|
|
79
|
+
======================================================
|
|
80
|
+
env.json file not found in the project root folder
|
|
81
|
+
or in the environments folder
|
|
82
|
+
======================================================
|
|
83
|
+
`);
|
|
84
|
+
}
|
|
85
|
+
const envFileContent = readFileSync(envFile, "utf8");
|
|
86
|
+
try {
|
|
87
|
+
this.environment = JSON.parse(envFileContent);
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
throw new Error(`
|
|
91
|
+
======================================================
|
|
92
|
+
unable to parse environment file
|
|
93
|
+
${e}
|
|
94
|
+
======================================================
|
|
95
|
+
`);
|
|
96
|
+
}
|
|
97
|
+
if (!this.environment.baseUrl) {
|
|
98
|
+
throw new Error(`
|
|
99
|
+
======================================================
|
|
100
|
+
baseUrl not found in env.json file
|
|
101
|
+
or in the environments folder
|
|
102
|
+
======================================================
|
|
103
|
+
`);
|
|
104
|
+
}
|
|
105
|
+
// Prepare step_definitions utilities
|
|
106
|
+
if (!existsSync(this.stepsFolder)) {
|
|
107
|
+
return this.environment.name;
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
const utilsPath = path.join(__dirname, "../assets/templates/utils_template.txt");
|
|
111
|
+
const utilsContent = readFileSync(utilsPath, "utf8");
|
|
112
|
+
const utilsFileName = path.join(this.stepsFolder, "utils.mjs");
|
|
113
|
+
writeFileSync(utilsFileName, utilsContent, "utf8");
|
|
114
|
+
const hooksTemplateFilePath = path.join(__dirname, "../assets", "templates", "_hooks_template.txt");
|
|
115
|
+
if (existsSync(hooksTemplateFilePath)) {
|
|
116
|
+
const hooksFilePath = path.join(this.stepsFolder, "_hooks.mjs");
|
|
117
|
+
const hooksContent = readFileSync(hooksTemplateFilePath, "utf8");
|
|
118
|
+
writeFileSync(hooksFilePath, hooksContent, "utf8");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
logger.error("Error loading utils_template");
|
|
123
|
+
}
|
|
124
|
+
const files = findFilesWithExtension(this.stepsFolder, "mjs");
|
|
125
|
+
for (let i = 0; i < files.length; i++) {
|
|
126
|
+
const file = files[i];
|
|
127
|
+
if (file.startsWith("_"))
|
|
128
|
+
continue;
|
|
129
|
+
if (file.endsWith(".mjs")) {
|
|
130
|
+
const fullFileName = path.join(this.rootFolder, "features", "step_definitions", file);
|
|
131
|
+
const page = new CodePage(fullFileName);
|
|
132
|
+
page.generateModel();
|
|
133
|
+
this.pages.push(page);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return this.environment.name;
|
|
112
137
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
this.pages.forEach((page) => {
|
|
118
|
-
result.push(page.getName());
|
|
119
|
-
});
|
|
120
|
-
return result;
|
|
121
|
-
}
|
|
122
|
-
findMethodInPages(methodName) {
|
|
123
|
-
for (const page of this.pages) {
|
|
124
|
-
for (const method of page.methods) {
|
|
125
|
-
if (method.name === methodName) {
|
|
126
|
-
return { page, method };
|
|
127
|
-
}
|
|
128
|
-
}
|
|
138
|
+
getPagesNames() {
|
|
139
|
+
const result = [];
|
|
140
|
+
this.pages.forEach((page) => result.push(page.getName()));
|
|
141
|
+
return result;
|
|
129
142
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
});
|
|
140
|
-
return result;
|
|
141
|
-
}
|
|
142
|
-
getPage(pageName, createEmpty = false) {
|
|
143
|
-
for (let i = 0; i < this.pages.length; i++) {
|
|
144
|
-
if (this.pages[i].getName().toLowerCase() === pageName.toLowerCase()) {
|
|
145
|
-
return this.pages[i];
|
|
146
|
-
}
|
|
143
|
+
findMethodInPages(methodName) {
|
|
144
|
+
for (const page of this.pages) {
|
|
145
|
+
for (const method of page.methods) {
|
|
146
|
+
if (method.name === methodName) {
|
|
147
|
+
return { page, method };
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return { page: null, method: null };
|
|
147
152
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
153
|
+
getProjectSignature() {
|
|
154
|
+
const result = [];
|
|
155
|
+
this.pages.forEach((page) => {
|
|
156
|
+
page.getMethodsNames().forEach((methodName) => {
|
|
157
|
+
result.push(page.getMethodInCommandFormat(methodName));
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
return result;
|
|
154
161
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
+
getPage(pageName, createEmpty = false) {
|
|
163
|
+
for (let i = 0; i < this.pages.length; i++) {
|
|
164
|
+
if (this.pages[i].getName().toLowerCase() === pageName.toLowerCase()) {
|
|
165
|
+
return this.pages[i];
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (createEmpty) {
|
|
169
|
+
const distFileName = path.join(this.stepsFolder, `${pageName}_page.mjs`);
|
|
170
|
+
const page = new CodePage(distFileName);
|
|
171
|
+
page.generateModel();
|
|
172
|
+
this.setPage(pageName, page);
|
|
173
|
+
return page;
|
|
174
|
+
}
|
|
175
|
+
return null;
|
|
162
176
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
177
|
+
setPage(pageName, page) {
|
|
178
|
+
for (let i = 0; i < this.pages.length; i++) {
|
|
179
|
+
if (this.pages[i].getName().toLowerCase() === pageName.toLowerCase()) {
|
|
180
|
+
this.pages[i] = page;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
169
183
|
}
|
|
170
|
-
|
|
171
|
-
|
|
184
|
+
createNewPage(name, _path) {
|
|
185
|
+
const distFileName = path.join(this.stepsFolder, `${name}_page.mjs`);
|
|
186
|
+
if (existsSync(distFileName)) {
|
|
187
|
+
logger.error("Page already exists: " + distFileName);
|
|
188
|
+
throw new Error(`Page already exists: ${name} - ${name}_page.mjs`);
|
|
189
|
+
}
|
|
190
|
+
if (!existsSync(this.stepsFolder)) {
|
|
191
|
+
mkdirSync(this.stepsFolder, { recursive: true });
|
|
192
|
+
}
|
|
193
|
+
const map = {
|
|
194
|
+
name,
|
|
195
|
+
path: _path ? `"${_path}"` : "null",
|
|
196
|
+
name_upper: name.charAt(0).toUpperCase() + name.slice(1),
|
|
197
|
+
};
|
|
198
|
+
const { promptFileContent } = generatePrompt(path.join(__dirname, "../assets/templates/page_template.txt"), map, false);
|
|
199
|
+
writeFileSync(distFileName, promptFileContent, "utf8");
|
|
200
|
+
// refresh pages list
|
|
201
|
+
void this.load();
|
|
172
202
|
}
|
|
173
|
-
let map = {
|
|
174
|
-
name,
|
|
175
|
-
path: _path ? `"${_path}"` : "null",
|
|
176
|
-
name_upper: name.charAt(0).toUpperCase() + name.slice(1),
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
let content = generatePrompt(
|
|
180
|
-
path.join(__dirname, "../assets/templates/page_template.txt"),
|
|
181
|
-
map,
|
|
182
|
-
false
|
|
183
|
-
).promptFileContent;
|
|
184
|
-
writeFileSync(distFileName, content, "utf8");
|
|
185
|
-
this.load();
|
|
186
|
-
}
|
|
187
203
|
}
|
|
188
|
-
|
|
189
|
-
let promptFileContent = readFileSync(promptFile, "utf8");
|
|
190
|
-
for (const key in parameters) {
|
|
191
|
-
const value = parameters[key];
|
|
192
|
-
promptFileContent = promptFileContent.replaceAll(`{${key}}`, value);
|
|
193
|
-
}
|
|
194
|
-
if (!chat) return { promptFileContent };
|
|
195
|
-
const messages = [];
|
|
196
|
-
const parts = promptFileContent.split("####");
|
|
197
|
-
for (let i = 0; i < parts.length; i++) {
|
|
198
|
-
if (!parts[i].trim()) continue;
|
|
199
|
-
const role = parts[i];
|
|
200
|
-
const prompt = parts[i + 1];
|
|
201
|
-
messages.push({ role, content: prompt.trim() });
|
|
202
|
-
// console.error("####prompt####", `role ${role}ֿֿ`);
|
|
203
|
-
// console.error(prompt.trim());
|
|
204
|
-
i++;
|
|
205
|
-
}
|
|
206
|
-
return { messages, promptFileContent };
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
const checkForEnvArg = () => {
|
|
210
|
-
for (let arg of process.argv.slice(2)) {
|
|
211
|
-
if (arg.startsWith("--")) {
|
|
212
|
-
const [key, value] = arg.split("=");
|
|
213
|
-
if (key.slice(2) === "env") {
|
|
214
|
-
return value;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
return null;
|
|
219
|
-
};
|
|
220
|
-
export { Project };
|
|
204
|
+
export { Project, generatePrompt, checkForEnvArg };
|