@dev-blinq/cucumber_client 1.0.1363-dev → 1.0.1363-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 +107 -107
- 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 +13 -23
- 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 +41 -0
- package/bin/assets/templates/utils_template.txt +2 -45
- package/bin/client/apiTest/apiTest.js +6 -0
- package/bin/client/code_cleanup/utils.js +5 -1
- package/bin/client/code_gen/api_codegen.js +2 -2
- package/bin/client/code_gen/code_inversion.js +107 -2
- package/bin/client/code_gen/function_signature.js +4 -0
- package/bin/client/code_gen/page_reflection.js +846 -906
- package/bin/client/code_gen/playwright_codeget.js +45 -12
- package/bin/client/cucumber/feature.js +4 -0
- package/bin/client/cucumber/feature_data.js +2 -2
- package/bin/client/cucumber/project_to_document.js +9 -3
- package/bin/client/cucumber/steps_definitions.js +6 -3
- package/bin/client/cucumber_selector.js +17 -1
- package/bin/client/local_agent.js +4 -3
- package/bin/client/parse_feature_file.js +23 -26
- package/bin/client/playground/projects/env.json +2 -2
- package/bin/client/project.js +186 -196
- package/bin/client/recorderv3/bvt_recorder.js +238 -95
- package/bin/client/recorderv3/implemented_steps.js +8 -0
- package/bin/client/recorderv3/index.js +59 -54
- package/bin/client/recorderv3/scriptTest.js +1 -1
- package/bin/client/recorderv3/services.js +66 -16
- package/bin/client/recorderv3/step_runner.js +315 -205
- package/bin/client/recorderv3/step_utils.js +476 -16
- package/bin/client/recorderv3/update_feature.js +9 -5
- package/bin/client/recording.js +1 -0
- package/bin/client/upload-service.js +3 -2
- package/bin/client/utils/socket_logger.js +132 -0
- package/bin/index.js +4 -0
- 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 +20 -10
|
@@ -4,6 +4,9 @@ import { StepsDefinitions } from "../cucumber/steps_definitions.js";
|
|
|
4
4
|
import path from "path";
|
|
5
5
|
import { CodePage } from "./page_reflection.js";
|
|
6
6
|
import { convertToIdentifier, escapeNonPrintables } from "./utils.js";
|
|
7
|
+
import socketLogger from "../utils/socket_logger.js";
|
|
8
|
+
import fs from "fs";
|
|
9
|
+
|
|
7
10
|
const findElementIdentifier = (node, step, userData, elements) => {
|
|
8
11
|
if (node.key) {
|
|
9
12
|
// incase of rerunning implemented steps
|
|
@@ -12,6 +15,13 @@ const findElementIdentifier = (node, step, userData, elements) => {
|
|
|
12
15
|
|
|
13
16
|
let elementIdentifier = null;
|
|
14
17
|
const keys = Object.keys(elements);
|
|
18
|
+
|
|
19
|
+
for (let i = 0; i < keys.length; i++) {
|
|
20
|
+
const element_key = keys[i];
|
|
21
|
+
const element = elements[element_key];
|
|
22
|
+
element["element_key"] = element_key;
|
|
23
|
+
}
|
|
24
|
+
|
|
15
25
|
let name = node.name;
|
|
16
26
|
if (!name && step.dataKey) {
|
|
17
27
|
name = step.dataKey;
|
|
@@ -92,11 +102,16 @@ const splitToLocatorsGroups = (locators) => {
|
|
|
92
102
|
const _generateCodeFromCommand = (step, elements, userData) => {
|
|
93
103
|
let elementsChanged = false;
|
|
94
104
|
|
|
95
|
-
let elementIdentifier =
|
|
105
|
+
let elementIdentifier = step.locators?.element_key;
|
|
96
106
|
let locatorObject = step.locators;
|
|
97
107
|
// handle element
|
|
98
108
|
let element_name = null;
|
|
99
109
|
let allStrategyLocators = null;
|
|
110
|
+
const codeLines = [];
|
|
111
|
+
|
|
112
|
+
if (_isCodeGenerationStep(step) === false)
|
|
113
|
+
return { codeLines, elements: elementsChanged ? elements : null, elementIdentifier, allStrategyLocators };
|
|
114
|
+
|
|
100
115
|
if (_isCodeGenerationStep(step) && step.type !== Types.VERIFY_PAGE_SNAPSHOT) {
|
|
101
116
|
allStrategyLocators = step.allStrategyLocators ?? splitToLocatorsGroups(step.locators);
|
|
102
117
|
let node = null;
|
|
@@ -130,18 +145,20 @@ const _generateCodeFromCommand = (step, elements, userData) => {
|
|
|
130
145
|
let locatorObjectClone = JSON.parse(JSON.stringify(locatorObject));
|
|
131
146
|
element_name = locatorObjectClone.element_name;
|
|
132
147
|
delete locatorObjectClone.element_name;
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
148
|
+
if (!elementIdentifier) {
|
|
149
|
+
keys.forEach((key) => {
|
|
150
|
+
let elementClone = JSON.parse(JSON.stringify(elements[key]));
|
|
151
|
+
delete elementClone.element_name;
|
|
152
|
+
if (JSON.stringify(elementClone) === JSON.stringify(locatorObjectClone)) {
|
|
153
|
+
elementIdentifier = key;
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
140
157
|
if (!elementIdentifier) {
|
|
141
158
|
elementIdentifier = findElementIdentifier(node, step, userData, elements);
|
|
142
159
|
}
|
|
143
160
|
const withoutAllStrategyLocators = excludeKey(locatorObject, "allStrategyLocators");
|
|
144
|
-
elements[elementIdentifier] = withoutAllStrategyLocators;
|
|
161
|
+
elements[elementIdentifier] = { ...withoutAllStrategyLocators, element_key: elementIdentifier };
|
|
145
162
|
elementsChanged = true;
|
|
146
163
|
}
|
|
147
164
|
let optionElement = null;
|
|
@@ -157,8 +174,6 @@ const _generateCodeFromCommand = (step, elements, userData) => {
|
|
|
157
174
|
}
|
|
158
175
|
}
|
|
159
176
|
|
|
160
|
-
const codeLines = [];
|
|
161
|
-
|
|
162
177
|
let line = null;
|
|
163
178
|
let comment = null;
|
|
164
179
|
let input = null;
|
|
@@ -353,7 +368,7 @@ const _generateCodeFromCommand = (step, elements, userData) => {
|
|
|
353
368
|
codeLines.push(line);
|
|
354
369
|
break;
|
|
355
370
|
}
|
|
356
|
-
case Types.VERIFY_PAGE_SNAPSHOT:
|
|
371
|
+
case Types.VERIFY_PAGE_SNAPSHOT: {
|
|
357
372
|
comment = step.nestFrmLoc
|
|
358
373
|
? `// Verify page snapshot ${step.parameters[0]} in the frame ${step.selectors.iframe_src}`
|
|
359
374
|
: `// Verify page snapshot ${step.parameters[0]}`;
|
|
@@ -362,7 +377,24 @@ const _generateCodeFromCommand = (step, elements, userData) => {
|
|
|
362
377
|
codeLines.push(line);
|
|
363
378
|
line = `await context.web.snapshotValidation(frameLocator, _param_0 , _params, ${JSON.stringify(options)}, this);`;
|
|
364
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
|
+
}
|
|
365
396
|
break;
|
|
397
|
+
}
|
|
366
398
|
case Types.VERIFY_PAGE_CONTAINS_TEXT:
|
|
367
399
|
line = "await context.web.verifyTextExistInPage( ";
|
|
368
400
|
input = `${escapeNonPrintables(JSON.stringify(step.parameters[0]))}`;
|
|
@@ -716,6 +748,7 @@ const generateCode = (recording, codePage, userData, projectDir, methodName) =>
|
|
|
716
748
|
}
|
|
717
749
|
let elements = {};
|
|
718
750
|
if (!codePage) {
|
|
751
|
+
socketLogger.info("CodePage is null");
|
|
719
752
|
console.log("codePage is null");
|
|
720
753
|
} else {
|
|
721
754
|
elements = codePage.getVariableDeclarationAsObject("elements");
|
|
@@ -8,6 +8,7 @@ import { parseStepTextParameters, toCucumberExpression, unEscapeNonPrintables }
|
|
|
8
8
|
import stream from "stream";
|
|
9
9
|
import { testStringForRegex } from "../recorderv3/update_feature.js";
|
|
10
10
|
import { generateTestData } from "./feature_data.js";
|
|
11
|
+
import socketLogger from "../utils/socket_logger.js";
|
|
11
12
|
class DataTable {
|
|
12
13
|
constructor(dataTableDocument) {
|
|
13
14
|
if (!dataTableDocument) {
|
|
@@ -544,6 +545,9 @@ const scenarioResolution = async (featureFilePath) => {
|
|
|
544
545
|
fs.rmSync(tmpDir, { recursive: true });
|
|
545
546
|
}
|
|
546
547
|
} catch (e) {
|
|
548
|
+
socketLogger.error(
|
|
549
|
+
`An error has occurred while removing the temp folder at ${tmpDir}. Please remove it manually. Error: ${e}`
|
|
550
|
+
);
|
|
547
551
|
console.error(
|
|
548
552
|
`An error has occurred while removing the temp folder at ${tmpDir}. Please remove it manually. Error: ${e}`
|
|
549
553
|
);
|
|
@@ -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();
|
|
@@ -15,7 +21,7 @@ export const projectDocument = (folder, featureName = null, scenarioName = null,
|
|
|
15
21
|
// read all the feature files in the project folder under features folder
|
|
16
22
|
let featureFiles = [];
|
|
17
23
|
const featuresDir = path.join(folder, "features");
|
|
18
|
-
featureFiles = fs.readdirSync(featuresDir).filter((file) => {
|
|
24
|
+
featureFiles = fs.readdirSync(featuresDir, { recursive: true }).filter((file) => {
|
|
19
25
|
return file.endsWith(".feature");
|
|
20
26
|
});
|
|
21
27
|
const documents = [];
|
|
@@ -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));
|
|
@@ -45,7 +45,10 @@ class StepsDefinitions {
|
|
|
45
45
|
if (method && expression.keyword && pattern && expression.methodName) {
|
|
46
46
|
this.steps[pattern] = {
|
|
47
47
|
// file: path.join(this.isTemp ? "__temp_features" : "features", mjsFile),
|
|
48
|
-
file: path.join(
|
|
48
|
+
file: path.join(
|
|
49
|
+
this.isTemp ? (process.env.tempFeaturesFolderPath ?? "__temp_features") : "features",
|
|
50
|
+
mjsFile
|
|
51
|
+
),
|
|
49
52
|
functionName: expression.methodName,
|
|
50
53
|
name: pattern,
|
|
51
54
|
source: this.findKey(method.codePart, "source"),
|
|
@@ -56,7 +59,7 @@ class StepsDefinitions {
|
|
|
56
59
|
}
|
|
57
60
|
load(print = true, supressErrors = false, errors = []) {
|
|
58
61
|
const mjsFiles = findFilesWithExtension(
|
|
59
|
-
path.join(this.baseFolder, this.isTemp ? process.env.tempFeaturesFolderPath ?? "__temp_features" : "features"),
|
|
62
|
+
path.join(this.baseFolder, this.isTemp ? (process.env.tempFeaturesFolderPath ?? "__temp_features") : "features"),
|
|
60
63
|
"mjs"
|
|
61
64
|
);
|
|
62
65
|
// console.log({ mjsFiles });
|
|
@@ -64,7 +67,7 @@ class StepsDefinitions {
|
|
|
64
67
|
const mjsFile = mjsFiles[i];
|
|
65
68
|
const filePath = path.join(
|
|
66
69
|
this.baseFolder,
|
|
67
|
-
this.isTemp ? process.env.tempFeaturesFolderPath ?? "__temp_features" : "features",
|
|
70
|
+
this.isTemp ? (process.env.tempFeaturesFolderPath ?? "__temp_features") : "features",
|
|
68
71
|
mjsFile
|
|
69
72
|
);
|
|
70
73
|
const codePage = new CodePage(filePath);
|
|
@@ -6,6 +6,7 @@ import crypto from "crypto";
|
|
|
6
6
|
import { findFilesWithExtension } from "./cucumber/steps_definitions.js";
|
|
7
7
|
import fs from "fs";
|
|
8
8
|
//import { spawn } from "child_process";
|
|
9
|
+
const __dirname = path.dirname(new URL(import.meta.url).pathname);
|
|
9
10
|
|
|
10
11
|
if (process.argv.length < 3) {
|
|
11
12
|
console.log("Usage: node cucumber_selector.js <projectsDir> --all");
|
|
@@ -100,8 +101,8 @@ if (process.argv.includes("--scenario-name")) {
|
|
|
100
101
|
}
|
|
101
102
|
const projectName = selectedScenario.featureFile.split(path.sep)[0];
|
|
102
103
|
let deleteMjsFiles = readlineSync.keyInYN("Delete mjs file?");
|
|
104
|
+
let stepsPath = path.join(selectedScenario.projectsDir, projectName, "features", "step_definitions");
|
|
103
105
|
if (deleteMjsFiles) {
|
|
104
|
-
let stepsPath = path.join(selectedScenario.projectsDir, projectName, "features", "step_definitions");
|
|
105
106
|
if (fs.existsSync(stepsPath)) {
|
|
106
107
|
// delete all the *.mjs files
|
|
107
108
|
let files = fs.readdirSync(stepsPath);
|
|
@@ -112,6 +113,21 @@ if (deleteMjsFiles) {
|
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
115
|
}
|
|
116
|
+
// copy utils file to the steps directory
|
|
117
|
+
const utilsPath = path.join(__dirname, "../assets/templates/utils_template.txt");
|
|
118
|
+
const utilsContent = fs.readFileSync(utilsPath, "utf8");
|
|
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
|
+
}
|
|
124
|
+
fs.writeFileSync(utilsFileName, utilsContent, "utf8");
|
|
125
|
+
// copy hooks file to the steps directory
|
|
126
|
+
const hooksTemplateFilePath = path.join(__dirname, "../assets/templates/_hooks_template.txt");
|
|
127
|
+
const hooksContent = fs.readFileSync(hooksTemplateFilePath, "utf8");
|
|
128
|
+
const hooksFilePath = path.join(stepsPath, "_hooks.mjs");
|
|
129
|
+
fs.writeFileSync(hooksFilePath, hooksContent, "utf8");
|
|
130
|
+
|
|
115
131
|
process.env.PROJECT_PATH = path.join(selectedScenario.projectsDir, projectName);
|
|
116
132
|
//chanks[currentChank][index];
|
|
117
133
|
console.log("Running scenario:");
|
|
@@ -267,7 +267,7 @@ class LocalAgent {
|
|
|
267
267
|
null,
|
|
268
268
|
true,
|
|
269
269
|
null,
|
|
270
|
-
|
|
270
|
+
-1,
|
|
271
271
|
null,
|
|
272
272
|
initScript
|
|
273
273
|
);
|
|
@@ -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
|
+
}
|