@dev-blinq/cucumber_client 1.0.1253-dev → 1.0.1253-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 +220 -0
- package/bin/assets/preload/recorderv3.js +4 -2
- package/bin/assets/scripts/aria_snapshot.js +235 -0
- package/bin/assets/scripts/dom_attr.js +372 -0
- package/bin/assets/scripts/dom_element.js +0 -0
- package/bin/assets/scripts/dom_parent.js +185 -0
- package/bin/assets/scripts/event_utils.js +105 -0
- package/bin/assets/scripts/pw.js +7886 -0
- package/bin/assets/scripts/recorder.js +1147 -0
- package/bin/assets/scripts/snapshot_capturer.js +155 -0
- package/bin/assets/scripts/unique_locators.js +852 -0
- package/bin/assets/scripts/yaml.js +4770 -0
- package/bin/assets/templates/_hooks_template.txt +37 -0
- package/bin/assets/templates/page_template.txt +2 -16
- package/bin/assets/templates/utils_template.txt +44 -71
- package/bin/client/apiTest/apiTest.js +6 -0
- package/bin/client/cli_helpers.js +11 -13
- package/bin/client/code_cleanup/utils.js +5 -1
- package/bin/client/code_gen/code_inversion.js +61 -4
- package/bin/client/code_gen/page_reflection.js +10 -3
- package/bin/client/code_gen/playwright_codeget.js +55 -16
- package/bin/client/cucumber/feature.js +89 -27
- package/bin/client/cucumber/project_to_document.js +1 -1
- package/bin/client/cucumber/steps_definitions.js +84 -76
- package/bin/client/cucumber_selector.js +13 -1
- package/bin/client/local_agent.js +3 -3
- package/bin/client/project.js +7 -1
- package/bin/client/recorderv3/bvt_recorder.js +298 -123
- package/bin/client/recorderv3/implemented_steps.js +74 -16
- package/bin/client/recorderv3/index.js +47 -25
- package/bin/client/recorderv3/network.js +299 -0
- package/bin/client/recorderv3/services.js +4 -16
- package/bin/client/recorderv3/step_runner.js +326 -67
- package/bin/client/recorderv3/step_utils.js +152 -5
- package/bin/client/recorderv3/update_feature.js +66 -34
- package/bin/client/recording.js +3 -0
- package/bin/client/run_cucumber.js +5 -1
- package/bin/client/scenario_report.js +0 -5
- package/bin/client/test_scenario.js +0 -1
- package/bin/client/utils/socket_logger.js +132 -0
- package/bin/index.js +1 -0
- package/package.json +17 -9
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {
|
|
2
|
+
After,
|
|
3
|
+
setDefaultTimeout,
|
|
4
|
+
Before,
|
|
5
|
+
AfterStep,
|
|
6
|
+
BeforeStep,
|
|
7
|
+
} from "@dev-blinq/cucumber-js";
|
|
8
|
+
import { closeContext, initContext, navigate, executeBrunoRequest, verifyFileExists } from "automation_model";
|
|
9
|
+
setDefaultTimeout(60 * 1000);
|
|
10
|
+
|
|
11
|
+
const url = null;
|
|
12
|
+
|
|
13
|
+
const elements = {};
|
|
14
|
+
|
|
15
|
+
let context = null;
|
|
16
|
+
Before(async function (scenario) {
|
|
17
|
+
context = await initContext(url, false, false, this);
|
|
18
|
+
await navigate(url);
|
|
19
|
+
await context.web.beforeScenario(this, scenario);
|
|
20
|
+
});
|
|
21
|
+
After(async function (scenario) {
|
|
22
|
+
await context.web.afterScenario(this, scenario);
|
|
23
|
+
await closeContext();
|
|
24
|
+
context = null;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
BeforeStep(async function (step) {
|
|
28
|
+
if (context) {
|
|
29
|
+
await context.web.beforeStep(this, step);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
AfterStep(async function (step) {
|
|
34
|
+
if (context) {
|
|
35
|
+
await context.web.afterStep(this, step);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
@@ -1,20 +1,6 @@
|
|
|
1
|
-
import { Given, When, Then, After, setDefaultTimeout, Before} from "@dev-blinq/cucumber-js";
|
|
2
|
-
import { closeContext, initContext, navigate } from "automation_model";
|
|
3
|
-
setDefaultTimeout(60 * 1000);
|
|
4
|
-
|
|
5
|
-
const path = null;
|
|
1
|
+
import { Given, When, Then, After, setDefaultTimeout, Before } from "@dev-blinq/cucumber-js";
|
|
2
|
+
import { closeContext, initContext, navigate, TestContext as context } from "automation_model";
|
|
6
3
|
|
|
7
4
|
const elements = {
|
|
8
5
|
};
|
|
9
6
|
|
|
10
|
-
let context = null;
|
|
11
|
-
Before(async function () {
|
|
12
|
-
if (!context) {
|
|
13
|
-
context = await initContext(path, false, false, this);
|
|
14
|
-
}
|
|
15
|
-
await navigate(path);
|
|
16
|
-
});
|
|
17
|
-
After(async function () {
|
|
18
|
-
await closeContext();
|
|
19
|
-
context = null;
|
|
20
|
-
});
|
|
@@ -2,55 +2,10 @@ import {
|
|
|
2
2
|
Given,
|
|
3
3
|
When,
|
|
4
4
|
Then,
|
|
5
|
-
After,
|
|
6
|
-
setDefaultTimeout,
|
|
7
|
-
Before,
|
|
8
5
|
defineStep,
|
|
9
|
-
AfterStep,
|
|
10
|
-
BeforeStep,
|
|
11
6
|
} from "@dev-blinq/cucumber-js";
|
|
12
|
-
import { closeContext, initContext, navigate, executeBrunoRequest, verifyFileExists } from "automation_model";
|
|
7
|
+
import { closeContext, initContext, navigate, executeBrunoRequest, verifyFileExists, TestContext as context } from "automation_model";
|
|
13
8
|
import path from "path";
|
|
14
|
-
setDefaultTimeout(60 * 1000);
|
|
15
|
-
|
|
16
|
-
const url = null;
|
|
17
|
-
|
|
18
|
-
const elements = {};
|
|
19
|
-
|
|
20
|
-
let context = null;
|
|
21
|
-
Before(async function (scenario) {
|
|
22
|
-
if (!context) {
|
|
23
|
-
context = await initContext(url, false, false, this);
|
|
24
|
-
}
|
|
25
|
-
await navigate(url);
|
|
26
|
-
await context.web.beforeScenario(this, scenario);
|
|
27
|
-
});
|
|
28
|
-
After(async function (scenario) {
|
|
29
|
-
await context.web.afterScenario(this, scenario);
|
|
30
|
-
await closeContext();
|
|
31
|
-
context = null;
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
BeforeStep(async function (step) {
|
|
35
|
-
if (context) {
|
|
36
|
-
await context.web.beforeStep(this, step);
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
AfterStep(async function (step) {
|
|
41
|
-
if (context) {
|
|
42
|
-
await context.web.afterStep(this, step);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Load test data for a user
|
|
48
|
-
* @param {string} user name of the user to load test data for
|
|
49
|
-
* @protect
|
|
50
|
-
*/
|
|
51
|
-
async function loadUserData(user) {
|
|
52
|
-
await context.web.loadTestDataAsync("users", user, this);
|
|
53
|
-
}
|
|
54
9
|
|
|
55
10
|
/**
|
|
56
11
|
* Verify text exsits in page
|
|
@@ -60,7 +15,6 @@ async function loadUserData(user) {
|
|
|
60
15
|
async function verifyTextExistsInPage(text) {
|
|
61
16
|
await context.web.verifyTextExistInPage(text, null, this);
|
|
62
17
|
}
|
|
63
|
-
|
|
64
18
|
Then("Verify the text {string} can be found in the page", verifyTextExistsInPage);
|
|
65
19
|
|
|
66
20
|
/**
|
|
@@ -87,6 +41,7 @@ async function fillElement(elementDescription, value) {
|
|
|
87
41
|
}
|
|
88
42
|
When("fill {string} with {string}", fillElement);
|
|
89
43
|
When("Fill {string} with {string}", fillElement);
|
|
44
|
+
|
|
90
45
|
/**
|
|
91
46
|
* Verify text does not exist in page
|
|
92
47
|
* @param {string} text the text to verify does not exist in page
|
|
@@ -107,6 +62,24 @@ async function navigateTo(url) {
|
|
|
107
62
|
}
|
|
108
63
|
When("Navigate to {string}", navigateTo);
|
|
109
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Navigate to the current page
|
|
67
|
+
* @protect
|
|
68
|
+
*/
|
|
69
|
+
async function browserNavigateBack() {
|
|
70
|
+
await context.web.goBack({}, this);
|
|
71
|
+
}
|
|
72
|
+
Then("Browser navigate back", browserNavigateBack);
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Navigate forward in browser history
|
|
76
|
+
* @protect
|
|
77
|
+
*/
|
|
78
|
+
async function browserNavigateForward() {
|
|
79
|
+
await context.web.goForward({}, this);
|
|
80
|
+
}
|
|
81
|
+
Then("Browser navigate forward", browserNavigateForward);
|
|
82
|
+
|
|
110
83
|
/**
|
|
111
84
|
* Store browser session "<path>"
|
|
112
85
|
* @param {string} filePath the file path or empty to store in the test data file
|
|
@@ -141,6 +114,7 @@ Then(
|
|
|
141
114
|
"Identify the text {string}, climb {string} levels in the page, validate text {string} can be found in the context",
|
|
142
115
|
verifyTextRelatedToText
|
|
143
116
|
);
|
|
117
|
+
|
|
144
118
|
/**
|
|
145
119
|
* execute bruno single request given the bruno project is placed in a folder called bruno under the root of the cucumber project
|
|
146
120
|
* @requestName the name of the bruno request file
|
|
@@ -149,7 +123,6 @@ Then(
|
|
|
149
123
|
async function runBrunoRequest(requestName) {
|
|
150
124
|
await executeBrunoRequest(requestName, {}, context, this);
|
|
151
125
|
}
|
|
152
|
-
|
|
153
126
|
When("Bruno - {string}", runBrunoRequest);
|
|
154
127
|
When("bruno - {string}", runBrunoRequest);
|
|
155
128
|
|
|
@@ -163,41 +136,41 @@ async function verify_the_downloaded_file_exists(fileName) {
|
|
|
163
136
|
const downloadFile = path.join(downloadFolder, fileName);
|
|
164
137
|
await verifyFileExists(downloadFile, {}, context, this);
|
|
165
138
|
}
|
|
166
|
-
|
|
167
139
|
Then("Verify the file {string} exists", { timeout: 60000 }, verify_the_downloaded_file_exists);
|
|
168
140
|
|
|
169
|
-
When("Noop", async function(){})
|
|
170
|
-
|
|
171
141
|
/**
|
|
172
|
-
*
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
*/
|
|
142
|
+
* Noop step for running only hooks
|
|
143
|
+
*/
|
|
144
|
+
When("Noop", async function () {});
|
|
176
145
|
|
|
177
|
-
|
|
178
|
-
|
|
146
|
+
/**
|
|
147
|
+
* Verify the page url is "<url>"
|
|
148
|
+
* @param {string} url URL to be verified against current URL
|
|
149
|
+
* @protect
|
|
150
|
+
*/
|
|
151
|
+
async function verify_page_url(url) {
|
|
152
|
+
await context.web.verifyPagePath(url, {}, this);
|
|
179
153
|
}
|
|
180
154
|
Then("Verify the page url is {string}", verify_page_url);
|
|
181
155
|
|
|
182
156
|
/**
|
|
183
|
-
* Verify the page title is "<title>"
|
|
184
|
-
* @param {string} title Title to be verified against current Title
|
|
185
|
-
* @protect
|
|
186
|
-
*/
|
|
187
|
-
async function verify_page_title(title){
|
|
157
|
+
* Verify the page title is "<title>"
|
|
158
|
+
* @param {string} title Title to be verified against current Title
|
|
159
|
+
* @protect
|
|
160
|
+
*/
|
|
161
|
+
async function verify_page_title(title) {
|
|
188
162
|
await context.web.verifyPageTitle(title, {}, this);
|
|
189
163
|
}
|
|
190
|
-
Then("Verify the page title is {string}",verify_page_title)
|
|
164
|
+
Then("Verify the page title is {string}", verify_page_title);
|
|
191
165
|
|
|
192
166
|
/**
|
|
193
|
-
* Explicit wait/sleep function that pauses execution for a specified duration
|
|
194
|
-
* @param {duration} - Duration to sleep in milliseconds (default: 1000ms)
|
|
195
|
-
* @param {options} - Optional configuration object
|
|
196
|
-
* @param {world} - Optional world context
|
|
197
|
-
* @returns Promise that resolves after the specified duration
|
|
198
|
-
*/
|
|
167
|
+
* Explicit wait/sleep function that pauses execution for a specified duration
|
|
168
|
+
* @param {duration} - Duration to sleep in milliseconds (default: 1000ms)
|
|
169
|
+
* @param {options} - Optional configuration object
|
|
170
|
+
* @param {world} - Optional world context
|
|
171
|
+
* @returns Promise that resolves after the specified duration
|
|
172
|
+
*/
|
|
199
173
|
async function sleep(duration) {
|
|
200
174
|
await context.web.sleep(duration, {}, null);
|
|
201
175
|
}
|
|
202
|
-
|
|
203
|
-
Then("Sleep for {string} ms", {timeout: -1}, sleep);
|
|
176
|
+
Then("Sleep for {string} ms", { timeout: -1 }, sleep);
|
|
@@ -75,6 +75,12 @@ class ApiTest {
|
|
|
75
75
|
const utilsTemplateFilePath = path.join(__dirname, "../../assets", "templates", "utils_template.txt");
|
|
76
76
|
const utilsContent = readFileSync(utilsTemplateFilePath, "utf8");
|
|
77
77
|
writeFileSync(utilsFilePath, utilsContent, "utf8");
|
|
78
|
+
const hooksTemplateFilePath = path.join(__dirname, "../../assets", "templates", "_hooks_template.txt");
|
|
79
|
+
if (existsSync(hooksTemplateFilePath)) {
|
|
80
|
+
const hooksFilePath = path.join(stepDefinitionFolderPath, "_hooks.mjs");
|
|
81
|
+
const hooksContent = readFileSync(hooksTemplateFilePath, "utf8");
|
|
82
|
+
writeFileSync(hooksFilePath, hooksContent, "utf8");
|
|
83
|
+
}
|
|
78
84
|
} catch (error) {
|
|
79
85
|
console.log("error", error);
|
|
80
86
|
}
|
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
|
|
2
1
|
const validateCLIArg = (arg, name) => {
|
|
3
2
|
if (!arg) {
|
|
4
|
-
throw new Error(`${name} is required`)
|
|
3
|
+
throw new Error(`${name} is required`);
|
|
5
4
|
}
|
|
6
|
-
}
|
|
5
|
+
};
|
|
7
6
|
const showUsage = (error, usage) => {
|
|
8
|
-
console.error(error.message)
|
|
9
|
-
console.info(usage)
|
|
10
|
-
process.exit(1)
|
|
11
|
-
}
|
|
12
|
-
const loadArgs = ()=>{
|
|
13
|
-
const args = process.argv.slice(2)
|
|
14
|
-
return args
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export { validateCLIArg, loadArgs, showUsage };
|
|
7
|
+
console.error(error.message);
|
|
8
|
+
console.info(usage);
|
|
9
|
+
process.exit(1);
|
|
10
|
+
};
|
|
11
|
+
const loadArgs = () => {
|
|
12
|
+
const args = process.argv.slice(2);
|
|
13
|
+
return args;
|
|
14
|
+
};
|
|
15
|
+
export { validateCLIArg, loadArgs, showUsage };
|
|
@@ -7,10 +7,11 @@ const { default: traverse } = pkg;
|
|
|
7
7
|
import pkg1 from "@babel/generator";
|
|
8
8
|
const { default: generate } = pkg1;
|
|
9
9
|
import * as t from "@babel/types";
|
|
10
|
-
|
|
11
10
|
import { CucumberExpression, ParameterTypeRegistry } from "@cucumber/cucumber-expressions";
|
|
12
11
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
13
12
|
import { getAiConfig } from "../code_gen/page_reflection.js";
|
|
13
|
+
import socketLogger from "../utils/socket_logger.js";
|
|
14
|
+
|
|
14
15
|
const STEP_KEYWORDS = new Set(["Given", "When", "Then"]);
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -307,14 +308,17 @@ export function getDefaultPrettierConfig() {
|
|
|
307
308
|
const configContent = readFileSync(prettierConfigPath, "utf-8");
|
|
308
309
|
prettierConfig = JSON.parse(configContent);
|
|
309
310
|
} catch (error) {
|
|
311
|
+
socketLogger.error("Error parsing Prettier config file", error);
|
|
310
312
|
console.error(`Error parsing Prettier config file: ${error}`);
|
|
311
313
|
}
|
|
312
314
|
} else {
|
|
313
315
|
// save the default config to .prettierrc
|
|
314
316
|
try {
|
|
315
317
|
writeFileSync(prettierConfigPath, JSON.stringify(prettierConfig, null, 2), "utf-8");
|
|
318
|
+
socketLogger.info(`Created default Prettier config at ${prettierConfigPath}`);
|
|
316
319
|
console.log(`Created default Prettier config at ${prettierConfigPath}`);
|
|
317
320
|
} catch (error) {
|
|
321
|
+
socketLogger.error(`Error writing Prettier config file: ${error}`);
|
|
318
322
|
console.error(`Error writing Prettier config file: ${error}`);
|
|
319
323
|
}
|
|
320
324
|
}
|
|
@@ -310,6 +310,14 @@ const invertStableCommand = (call, elements, stepParams) => {
|
|
|
310
310
|
break;
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
+
case "goBack":
|
|
314
|
+
step.type = Types.GO_BACK;
|
|
315
|
+
break;
|
|
316
|
+
|
|
317
|
+
case "goForward":
|
|
318
|
+
step.type = Types.GO_FORWARD;
|
|
319
|
+
break;
|
|
320
|
+
|
|
313
321
|
case "reloadPage":
|
|
314
322
|
step.type = Types.RELOAD;
|
|
315
323
|
break;
|
|
@@ -366,10 +374,15 @@ const invertStableCommand = (call, elements, stepParams) => {
|
|
|
366
374
|
}
|
|
367
375
|
case "verifyTextRelatedToText": {
|
|
368
376
|
step.type = Types.VERIFY_TEXT_RELATED_TO_TEXT;
|
|
369
|
-
const
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
377
|
+
const textAnchorParse = parseDataSource(call.arguments[0], stepParams);
|
|
378
|
+
const textAnchor =
|
|
379
|
+
textAnchorParse.type === "literal" ? textAnchorParse.value : toVariableName(textAnchorParse.dataKey);
|
|
380
|
+
const climbParse = parseDataSource(call.arguments[1], stepParams);
|
|
381
|
+
const climb = climbParse.type === "literal" ? climbParse.value : toVariableName(climbParse.dataKey);
|
|
382
|
+
const textToVerifyParse = parseDataSource(call.arguments[2], stepParams);
|
|
383
|
+
const textToVerify =
|
|
384
|
+
textToVerifyParse.type === "literal" ? textToVerifyParse.value : toVariableName(textToVerifyParse.dataKey);
|
|
385
|
+
step.parameters = [textAnchor, climb, textToVerify];
|
|
373
386
|
break;
|
|
374
387
|
}
|
|
375
388
|
case "setInputFiles": {
|
|
@@ -418,6 +431,50 @@ const invertStableCommand = (call, elements, stepParams) => {
|
|
|
418
431
|
}
|
|
419
432
|
break;
|
|
420
433
|
}
|
|
434
|
+
case "verifyProperty": {
|
|
435
|
+
step.type = Types.VERIFY_PROPERTY;
|
|
436
|
+
step.element = extractElement(call.arguments[0]);
|
|
437
|
+
const property = call.arguments[1].value;
|
|
438
|
+
const value = parseDataSource(call.arguments[2], stepParams);
|
|
439
|
+
if (value.type === "literal") {
|
|
440
|
+
step.parameters = [property, value.value];
|
|
441
|
+
} else {
|
|
442
|
+
step.dataSource = value.dataSource;
|
|
443
|
+
step.dataKey = value.dataKey;
|
|
444
|
+
step.parameters = [property, toVariableName(value.dataKey)];
|
|
445
|
+
}
|
|
446
|
+
break;
|
|
447
|
+
}
|
|
448
|
+
case "extractProperty": {
|
|
449
|
+
step.type = Types.EXTRACT_PROPERTY;
|
|
450
|
+
step.element = extractElement(call.arguments[0]);
|
|
451
|
+
const attribute = call.arguments[1].value;
|
|
452
|
+
const variable = parseDataSource(call.arguments[2], stepParams);
|
|
453
|
+
if (variable.type === "literal") {
|
|
454
|
+
step.parameters = [attribute, variable.value];
|
|
455
|
+
} else {
|
|
456
|
+
step.dataSource = variable.dataSource;
|
|
457
|
+
step.dataKey = variable.dataKey;
|
|
458
|
+
step.parameters = [attribute, toVariableName(variable.dataKey)];
|
|
459
|
+
}
|
|
460
|
+
if (call.arguments[4].type === "ObjectExpression") {
|
|
461
|
+
const properties = call.arguments[4].properties;
|
|
462
|
+
const regexProp = properties.findIndex((prop) => prop.key.name === "regex");
|
|
463
|
+
if (regexProp !== -1) {
|
|
464
|
+
const regex = properties[regexProp].value.value;
|
|
465
|
+
step.regex = regex;
|
|
466
|
+
}
|
|
467
|
+
const trimSpacesProp = properties.findIndex((prop) => prop.key.name === "trimSpaces");
|
|
468
|
+
if (trimSpacesProp !== -1) {
|
|
469
|
+
const trimSpaces = properties[trimSpacesProp].value.value;
|
|
470
|
+
step.trimSpaces = trimSpaces;
|
|
471
|
+
}
|
|
472
|
+
} else {
|
|
473
|
+
step.regex = "";
|
|
474
|
+
step.trimSpaces = false;
|
|
475
|
+
}
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
421
478
|
default:
|
|
422
479
|
return; // Skip if no matching method
|
|
423
480
|
}
|
|
@@ -33,6 +33,9 @@ export function getAiConfig() {
|
|
|
33
33
|
} catch (e) {
|
|
34
34
|
ai_config = {};
|
|
35
35
|
}
|
|
36
|
+
if (!ai_config.locatorsMetadataDir) {
|
|
37
|
+
ai_config.locatorsMetadataDir = "features/step_definitions/locators";
|
|
38
|
+
}
|
|
36
39
|
return ai_config;
|
|
37
40
|
}
|
|
38
41
|
class CodePage {
|
|
@@ -237,13 +240,17 @@ this.imports[2].node.source.value
|
|
|
237
240
|
}
|
|
238
241
|
return templates;
|
|
239
242
|
}
|
|
240
|
-
getExpectedTimeout(expectedNumofCmds) {
|
|
243
|
+
getExpectedTimeout(expectedNumofCmds, finalTimeout) {
|
|
244
|
+
const timeoutNum = parseFloat(finalTimeout);
|
|
245
|
+
if (finalTimeout && !isNaN(timeoutNum)) {
|
|
246
|
+
return -1;
|
|
247
|
+
}
|
|
241
248
|
return expectedNumofCmds * 60 * 1000;
|
|
242
249
|
}
|
|
243
|
-
addCucumberStep(type, cucumberLine, method, expectedNumofCmds) {
|
|
250
|
+
addCucumberStep(type, cucumberLine, method, expectedNumofCmds, finalTimeout) {
|
|
244
251
|
const result = {};
|
|
245
252
|
let code = "\n";
|
|
246
|
-
code += `${type}(${JSON.stringify(cucumberLine)}, ${expectedNumofCmds ? `{ timeout: ${this.getExpectedTimeout(expectedNumofCmds)}}, ` : ""}${method});\n`;
|
|
253
|
+
code += `${type}(${JSON.stringify(cucumberLine)}, ${expectedNumofCmds ? `{ timeout: ${this.getExpectedTimeout(expectedNumofCmds, finalTimeout)}}, ` : ""}${method});\n`;
|
|
247
254
|
let existCodePart = null;
|
|
248
255
|
for (let i = 0; i < this.cucumberCalls.length; i++) {
|
|
249
256
|
if (
|
|
@@ -4,6 +4,7 @@ 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";
|
|
7
8
|
const findElementIdentifier = (node, step, userData, elements) => {
|
|
8
9
|
if (node.key) {
|
|
9
10
|
// incase of rerunning implemented steps
|
|
@@ -12,6 +13,13 @@ const findElementIdentifier = (node, step, userData, elements) => {
|
|
|
12
13
|
|
|
13
14
|
let elementIdentifier = null;
|
|
14
15
|
const keys = Object.keys(elements);
|
|
16
|
+
|
|
17
|
+
for (let i = 0; i < keys.length; i++) {
|
|
18
|
+
const element_key = keys[i];
|
|
19
|
+
const element = elements[element_key];
|
|
20
|
+
element["element_key"] = element_key;
|
|
21
|
+
}
|
|
22
|
+
|
|
15
23
|
let name = node.name;
|
|
16
24
|
if (!name && step.dataKey) {
|
|
17
25
|
name = step.dataKey;
|
|
@@ -58,7 +66,8 @@ const _isCodeGenerationStep = (step) => {
|
|
|
58
66
|
step.type === Types.VERIFY_ATTRIBUTE ||
|
|
59
67
|
step.type === Types.VERIFY_PROPERTY ||
|
|
60
68
|
step.type === Types.SET_INPUT_FILES ||
|
|
61
|
-
step.type === Types.VERIFY_PAGE_SNAPSHOT
|
|
69
|
+
step.type === Types.VERIFY_PAGE_SNAPSHOT ||
|
|
70
|
+
step.type === Types.CONDITIONAL_WAIT
|
|
62
71
|
) {
|
|
63
72
|
return true;
|
|
64
73
|
}
|
|
@@ -91,7 +100,7 @@ const splitToLocatorsGroups = (locators) => {
|
|
|
91
100
|
const _generateCodeFromCommand = (step, elements, userData) => {
|
|
92
101
|
let elementsChanged = false;
|
|
93
102
|
|
|
94
|
-
let elementIdentifier =
|
|
103
|
+
let elementIdentifier = step.locators?.element_key;
|
|
95
104
|
let locatorObject = step.locators;
|
|
96
105
|
// handle element
|
|
97
106
|
let element_name = null;
|
|
@@ -129,18 +138,20 @@ const _generateCodeFromCommand = (step, elements, userData) => {
|
|
|
129
138
|
let locatorObjectClone = JSON.parse(JSON.stringify(locatorObject));
|
|
130
139
|
element_name = locatorObjectClone.element_name;
|
|
131
140
|
delete locatorObjectClone.element_name;
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
141
|
+
if (!elementIdentifier) {
|
|
142
|
+
keys.forEach((key) => {
|
|
143
|
+
let elementClone = JSON.parse(JSON.stringify(elements[key]));
|
|
144
|
+
delete elementClone.element_name;
|
|
145
|
+
if (JSON.stringify(elementClone) === JSON.stringify(locatorObjectClone)) {
|
|
146
|
+
elementIdentifier = key;
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
139
150
|
if (!elementIdentifier) {
|
|
140
151
|
elementIdentifier = findElementIdentifier(node, step, userData, elements);
|
|
141
152
|
}
|
|
142
153
|
const withoutAllStrategyLocators = excludeKey(locatorObject, "allStrategyLocators");
|
|
143
|
-
elements[elementIdentifier] = withoutAllStrategyLocators;
|
|
154
|
+
elements[elementIdentifier] = { ...withoutAllStrategyLocators, element_key: elementIdentifier };
|
|
144
155
|
elementsChanged = true;
|
|
145
156
|
}
|
|
146
157
|
let optionElement = null;
|
|
@@ -578,7 +589,7 @@ const _generateCodeFromCommand = (step, elements, userData) => {
|
|
|
578
589
|
case 1:
|
|
579
590
|
comment = `// Click on ${elementIdentifier ? elementIdentifier : step.label} in the context of ${escapeNonPrintables(step.value)}`;
|
|
580
591
|
codeLines.push(escapeNonPrintables(comment));
|
|
581
|
-
input = `"${escapeNonPrintables(step.value)}"`;
|
|
592
|
+
input = `"${escapeNonPrintables(step.value.replaceAll('"', '\\"'))}"`;
|
|
582
593
|
if (step.dataSource === "userData" || step.dataSource === "parameters") {
|
|
583
594
|
input = "_" + step.dataKey;
|
|
584
595
|
}
|
|
@@ -588,7 +599,7 @@ const _generateCodeFromCommand = (step, elements, userData) => {
|
|
|
588
599
|
case 2:
|
|
589
600
|
comment = `// Double click on ${elementIdentifier ? elementIdentifier : step.label} in the context of ${escapeNonPrintables(step.value)}`;
|
|
590
601
|
codeLines.push(escapeNonPrintables(comment));
|
|
591
|
-
input = `"${escapeNonPrintables(step.value)}"`;
|
|
602
|
+
input = `"${escapeNonPrintables(step.value.replaceAll('"', '\\"'))}"`;
|
|
592
603
|
if (step.dataSource === "userData" || step.dataSource === "parameters") {
|
|
593
604
|
input = "_" + step.dataKey;
|
|
594
605
|
}
|
|
@@ -598,7 +609,7 @@ const _generateCodeFromCommand = (step, elements, userData) => {
|
|
|
598
609
|
default:
|
|
599
610
|
comment = `// Click on ${elementIdentifier ? elementIdentifier : step.label} in the context of ${escapeNonPrintables(step.value)}`;
|
|
600
611
|
codeLines.push(escapeNonPrintables(comment));
|
|
601
|
-
input = `"${escapeNonPrintables(step.value)}"`;
|
|
612
|
+
input = `"${escapeNonPrintables(step.value.replaceAll('"', '\\"'))}"`;
|
|
602
613
|
if (step.dataSource === "userData" || step.dataSource === "parameters") {
|
|
603
614
|
input = "_" + step.dataKey;
|
|
604
615
|
}
|
|
@@ -641,6 +652,13 @@ const _generateCodeFromCommand = (step, elements, userData) => {
|
|
|
641
652
|
codeLines.push(line);
|
|
642
653
|
break;
|
|
643
654
|
}
|
|
655
|
+
case Types.CONDITIONAL_WAIT: {
|
|
656
|
+
line = `await context.web.conditionalWait(elements["${elementIdentifier}"], `;
|
|
657
|
+
input = "_param_0";
|
|
658
|
+
line += `"${step.parameters[1]}", ${input}, _params, null, this);`;
|
|
659
|
+
codeLines.push(line);
|
|
660
|
+
break;
|
|
661
|
+
}
|
|
644
662
|
case Types.SET_INPUT_FILES: {
|
|
645
663
|
line = `await context.web.setInputFiles(elements["${elementIdentifier}"], `;
|
|
646
664
|
let files = step.parameters[0];
|
|
@@ -666,6 +684,22 @@ const _generateCodeFromCommand = (step, elements, userData) => {
|
|
|
666
684
|
return { codeLines, elements: elementsChanged ? elements : null, elementIdentifier, allStrategyLocators };
|
|
667
685
|
};
|
|
668
686
|
|
|
687
|
+
/**
|
|
688
|
+
* Generates a report command based on the given position.
|
|
689
|
+
* @param {"start"|"end"} position
|
|
690
|
+
*/
|
|
691
|
+
const generateReportCommand = (position, cmdId) => {
|
|
692
|
+
const codeLines = [];
|
|
693
|
+
if (position === "start") {
|
|
694
|
+
const line = `await context.web.addCommandToReport("${cmdId}", "PASSED", '{"status":"start","cmdId":"${cmdId}"}', {type:"cmdReport"},this);`;
|
|
695
|
+
codeLines.push(line);
|
|
696
|
+
} else if (position === "end") {
|
|
697
|
+
const line = `await context.web.addCommandToReport("${cmdId}", "PASSED", '{"status":"end","cmdId":"${cmdId}"}', {type:"cmdReport"},this);`;
|
|
698
|
+
codeLines.push(line);
|
|
699
|
+
}
|
|
700
|
+
return codeLines;
|
|
701
|
+
};
|
|
702
|
+
|
|
669
703
|
const generateCode = (recording, codePage, userData, projectDir, methodName) => {
|
|
670
704
|
const stepsDefinitions = new StepsDefinitions(projectDir);
|
|
671
705
|
stepsDefinitions.load(false);
|
|
@@ -692,6 +726,7 @@ const generateCode = (recording, codePage, userData, projectDir, methodName) =>
|
|
|
692
726
|
}
|
|
693
727
|
let elements = {};
|
|
694
728
|
if (!codePage) {
|
|
729
|
+
socketLogger.info("CodePage is null");
|
|
695
730
|
console.log("codePage is null");
|
|
696
731
|
} else {
|
|
697
732
|
elements = codePage.getVariableDeclarationAsObject("elements");
|
|
@@ -706,12 +741,16 @@ const generateCode = (recording, codePage, userData, projectDir, methodName) =>
|
|
|
706
741
|
if (recordingStep.type === Types.COMPLETE) {
|
|
707
742
|
return;
|
|
708
743
|
}
|
|
709
|
-
|
|
710
|
-
//
|
|
711
|
-
//
|
|
744
|
+
|
|
745
|
+
// if (process.env.TEMP_RUN === "true") {
|
|
746
|
+
// codeLines.push(...generateReportCommand("start", recordingStep.cmdId));
|
|
712
747
|
// }
|
|
713
748
|
const result = _generateCodeFromCommand(recordingStep, elements, userData);
|
|
749
|
+
|
|
714
750
|
codeLines.push(...result.codeLines);
|
|
751
|
+
// if (process.env.TEMP_RUN === "true") {
|
|
752
|
+
// codeLines.push(...generateReportCommand("end", recordingStep.cmdId));
|
|
753
|
+
// }
|
|
715
754
|
if (result.elements) {
|
|
716
755
|
elements = result.elements;
|
|
717
756
|
}
|