automation_model 1.0.396-dev → 1.0.396-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/lib/api.js +12 -9
- package/lib/api.js.map +1 -1
- package/lib/auto_page.d.ts +1 -1
- package/lib/auto_page.js +28 -1
- package/lib/auto_page.js.map +1 -1
- package/lib/browser_manager.js +3 -2
- package/lib/browser_manager.js.map +1 -1
- package/lib/stable_browser.d.ts +6 -1
- package/lib/stable_browser.js +245 -137
- package/lib/stable_browser.js.map +1 -1
- package/lib/test_context.d.ts +1 -0
- package/lib/test_context.js +1 -0
- package/lib/test_context.js.map +1 -1
- package/lib/utils.js +2 -2
- package/lib/utils.js.map +1 -1
- package/package.json +10 -6
package/lib/stable_browser.js
CHANGED
|
@@ -13,6 +13,8 @@ import { getTableCells, getTableData } from "./table_analyze.js";
|
|
|
13
13
|
import objectPath from "object-path";
|
|
14
14
|
import { decrypt } from "./utils.js";
|
|
15
15
|
import csv from "csv-parser";
|
|
16
|
+
import { Readable } from "node:stream";
|
|
17
|
+
import readline from "readline";
|
|
16
18
|
const Types = {
|
|
17
19
|
CLICK: "click_element",
|
|
18
20
|
NAVIGATE: "navigate",
|
|
@@ -28,6 +30,7 @@ const Types = {
|
|
|
28
30
|
SELECT: "select_combobox",
|
|
29
31
|
VERIFY_PAGE_PATH: "verify_page_path",
|
|
30
32
|
TYPE_PRESS: "type_press",
|
|
33
|
+
PRESS: "press_key",
|
|
31
34
|
HOVER: "hover_element",
|
|
32
35
|
CHECK: "check_element",
|
|
33
36
|
UNCHECK: "uncheck_element",
|
|
@@ -36,6 +39,8 @@ const Types = {
|
|
|
36
39
|
SET_DATE_TIME: "set_date_time",
|
|
37
40
|
SET_VIEWPORT: "set_viewport",
|
|
38
41
|
VERIFY_VISUAL: "verify_visual",
|
|
42
|
+
LOAD_DATA: "load_data",
|
|
43
|
+
SET_INPUT: "set_input",
|
|
39
44
|
};
|
|
40
45
|
class StableBrowser {
|
|
41
46
|
constructor(browser, page, logger = null, context = null) {
|
|
@@ -81,9 +86,20 @@ class StableBrowser {
|
|
|
81
86
|
this.page = page;
|
|
82
87
|
context.page = page;
|
|
83
88
|
context.pages.push(page);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
89
|
+
page.on("close", async () => {
|
|
90
|
+
if (this.context && this.context.pages && this.context.pages.length > 1) {
|
|
91
|
+
this.context.pages.pop();
|
|
92
|
+
this.page = this.context.pages[this.context.pages.length - 1];
|
|
93
|
+
this.context.page = this.page;
|
|
94
|
+
try {
|
|
95
|
+
let title = await this.page.title();
|
|
96
|
+
console.log("Switched to page " + title);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
console.error("Error on page close", error);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
});
|
|
87
103
|
try {
|
|
88
104
|
await this.waitForPageLoad();
|
|
89
105
|
console.log("Switch page: " + (await page.title()));
|
|
@@ -178,24 +194,78 @@ class StableBrowser {
|
|
|
178
194
|
}
|
|
179
195
|
return text;
|
|
180
196
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
197
|
+
_fixLocatorUsingParams(locator, _params) {
|
|
198
|
+
// check if not null
|
|
199
|
+
if (!locator) {
|
|
200
|
+
return locator;
|
|
201
|
+
}
|
|
202
|
+
// clone the locator
|
|
203
|
+
locator = JSON.parse(JSON.stringify(locator));
|
|
204
|
+
this.scanAndManipulate(locator, _params);
|
|
205
|
+
return locator;
|
|
206
|
+
}
|
|
207
|
+
_isObject(value) {
|
|
208
|
+
return value && typeof value === "object" && value.constructor === Object;
|
|
209
|
+
}
|
|
210
|
+
scanAndManipulate(currentObj, _params) {
|
|
211
|
+
for (const key in currentObj) {
|
|
212
|
+
if (typeof currentObj[key] === "string") {
|
|
213
|
+
// Perform string manipulation
|
|
214
|
+
currentObj[key] = this._fixUsingParams(currentObj[key], _params);
|
|
215
|
+
}
|
|
216
|
+
else if (this._isObject(currentObj[key])) {
|
|
217
|
+
// Recursively scan nested objects
|
|
218
|
+
this.scanAndManipulate(currentObj[key], _params);
|
|
219
|
+
}
|
|
184
220
|
}
|
|
221
|
+
}
|
|
222
|
+
_getLocator(locator, scope, _params) {
|
|
223
|
+
locator = this._fixLocatorUsingParams(locator, _params);
|
|
224
|
+
let locatorReturn;
|
|
185
225
|
if (locator.role) {
|
|
186
226
|
if (locator.role[1].nameReg) {
|
|
187
227
|
locator.role[1].name = reg_parser(locator.role[1].nameReg);
|
|
188
228
|
delete locator.role[1].nameReg;
|
|
189
229
|
}
|
|
190
|
-
if (locator.role[1].name) {
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
|
|
230
|
+
// if (locator.role[1].name) {
|
|
231
|
+
// locator.role[1].name = this._fixUsingParams(locator.role[1].name, _params);
|
|
232
|
+
// }
|
|
233
|
+
locatorReturn = scope.getByRole(locator.role[0], locator.role[1]);
|
|
194
234
|
}
|
|
195
235
|
if (locator.css) {
|
|
196
|
-
|
|
236
|
+
locatorReturn = scope.locator(locator.css);
|
|
237
|
+
}
|
|
238
|
+
// handle role/name locators
|
|
239
|
+
// locator.selector will be something like: textbox[name="Username"i]
|
|
240
|
+
if (locator.engine === "internal:role") {
|
|
241
|
+
// extract the role, name and the i/s flags using regex
|
|
242
|
+
const match = locator.selector.match(/(.*)\[(.*)="(.*)"(.*)\]/);
|
|
243
|
+
if (match) {
|
|
244
|
+
const role = match[1];
|
|
245
|
+
const name = match[3];
|
|
246
|
+
const flags = match[4];
|
|
247
|
+
locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (locator === null || locator === void 0 ? void 0 : locator.engine) {
|
|
251
|
+
if (locator.engine === "css") {
|
|
252
|
+
locatorReturn = scope.locator(locator.selector);
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
let selector = locator.selector;
|
|
256
|
+
if (locator.engine === "internal:attr") {
|
|
257
|
+
if (!selector.startsWith("[")) {
|
|
258
|
+
selector = `[${selector}]`;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
locatorReturn = scope.locator(`${locator.engine}=${selector}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (!locatorReturn) {
|
|
265
|
+
console.error(locator);
|
|
266
|
+
throw new Error("Locator undefined");
|
|
197
267
|
}
|
|
198
|
-
|
|
268
|
+
return locatorReturn;
|
|
199
269
|
}
|
|
200
270
|
async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
|
|
201
271
|
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, true, _params);
|
|
@@ -406,6 +476,8 @@ class StableBrowser {
|
|
|
406
476
|
if (result.foundElements.length > 0) {
|
|
407
477
|
let dialogCloseLocator = result.foundElements[0].locator;
|
|
408
478
|
await dialogCloseLocator.click();
|
|
479
|
+
// wait for the dialog to close
|
|
480
|
+
await dialogCloseLocator.waitFor({ state: "hidden" });
|
|
409
481
|
return { rerun: true };
|
|
410
482
|
}
|
|
411
483
|
}
|
|
@@ -414,7 +486,7 @@ class StableBrowser {
|
|
|
414
486
|
}
|
|
415
487
|
async _locate(selectors, info, _params, timeout = 30000) {
|
|
416
488
|
for (let i = 0; i < 3; i++) {
|
|
417
|
-
info.log += "attempt " + i + ":
|
|
489
|
+
info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
|
|
418
490
|
for (let j = 0; j < selectors.locators.length; j++) {
|
|
419
491
|
let selector = selectors.locators[j];
|
|
420
492
|
info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
|
|
@@ -434,9 +506,27 @@ class StableBrowser {
|
|
|
434
506
|
//let arrayMode = Array.isArray(selectors);
|
|
435
507
|
let scope = this.page;
|
|
436
508
|
if (selectors.iframe_src || selectors.frameLocators) {
|
|
509
|
+
const findFrame = (frame, framescope) => {
|
|
510
|
+
for (let i = 0; i < frame.selectors.length; i++) {
|
|
511
|
+
let frameLocator = frame.selectors[i];
|
|
512
|
+
if (frameLocator.css) {
|
|
513
|
+
framescope = framescope.frameLocator(frameLocator.css);
|
|
514
|
+
break;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
if (frame.children) {
|
|
518
|
+
return findFrame(frame.children, framescope);
|
|
519
|
+
}
|
|
520
|
+
return framescope;
|
|
521
|
+
};
|
|
437
522
|
info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
|
|
438
523
|
while (true) {
|
|
439
524
|
let frameFound = false;
|
|
525
|
+
if (selectors.nestFrmLoc) {
|
|
526
|
+
scope = findFrame(selectors.nestFrmLoc, scope);
|
|
527
|
+
frameFound = true;
|
|
528
|
+
break;
|
|
529
|
+
}
|
|
440
530
|
if (selectors.frameLocators) {
|
|
441
531
|
for (let i = 0; i < selectors.frameLocators.length; i++) {
|
|
442
532
|
let frameLocator = selectors.frameLocators[i];
|
|
@@ -600,6 +690,9 @@ class StableBrowser {
|
|
|
600
690
|
async click(selectors, _params, options = {}, world = null) {
|
|
601
691
|
this._validateSelectors(selectors);
|
|
602
692
|
const startTime = Date.now();
|
|
693
|
+
if (options && options.context) {
|
|
694
|
+
selectors.locators[0].text = options.context;
|
|
695
|
+
}
|
|
603
696
|
const info = {};
|
|
604
697
|
info.log = "***** click on " + selectors.element_name + " *****\n";
|
|
605
698
|
info.operation = "click";
|
|
@@ -613,14 +706,14 @@ class StableBrowser {
|
|
|
613
706
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
614
707
|
try {
|
|
615
708
|
await this._highlightElements(element);
|
|
616
|
-
await element.click(
|
|
709
|
+
await element.click();
|
|
617
710
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
618
711
|
}
|
|
619
712
|
catch (e) {
|
|
620
713
|
// await this.closeUnexpectedPopups();
|
|
621
714
|
info.log += "click failed, will try again" + "\n";
|
|
622
715
|
element = await this._locate(selectors, info, _params);
|
|
623
|
-
await element.click
|
|
716
|
+
await element.dispatchEvent("click");
|
|
624
717
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
625
718
|
}
|
|
626
719
|
await this.waitForPageLoad();
|
|
@@ -673,7 +766,7 @@ class StableBrowser {
|
|
|
673
766
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
674
767
|
try {
|
|
675
768
|
await this._highlightElements(element);
|
|
676
|
-
await element.setChecked(checked
|
|
769
|
+
await element.setChecked(checked);
|
|
677
770
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
678
771
|
}
|
|
679
772
|
catch (e) {
|
|
@@ -737,7 +830,7 @@ class StableBrowser {
|
|
|
737
830
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
738
831
|
try {
|
|
739
832
|
await this._highlightElements(element);
|
|
740
|
-
await element.hover(
|
|
833
|
+
await element.hover();
|
|
741
834
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
742
835
|
}
|
|
743
836
|
catch (e) {
|
|
@@ -799,7 +892,7 @@ class StableBrowser {
|
|
|
799
892
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
800
893
|
try {
|
|
801
894
|
await this._highlightElements(element);
|
|
802
|
-
await element.selectOption(values
|
|
895
|
+
await element.selectOption(values);
|
|
803
896
|
}
|
|
804
897
|
catch (e) {
|
|
805
898
|
//await this.closeUnexpectedPopups();
|
|
@@ -908,71 +1001,45 @@ class StableBrowser {
|
|
|
908
1001
|
});
|
|
909
1002
|
}
|
|
910
1003
|
}
|
|
911
|
-
async
|
|
1004
|
+
async setInputValue(selectors, value, _params = null, options = {}, world = null) {
|
|
1005
|
+
// set input value for non fillable inputs like date, time, range, color, etc.
|
|
912
1006
|
this._validateSelectors(selectors);
|
|
913
1007
|
const startTime = Date.now();
|
|
914
|
-
let error = null;
|
|
915
|
-
let screenshotId = null;
|
|
916
|
-
let screenshotPath = null;
|
|
917
1008
|
const info = {};
|
|
918
|
-
info.log = "";
|
|
919
|
-
info.operation =
|
|
1009
|
+
info.log = "***** set input value " + selectors.element_name + " *****\n";
|
|
1010
|
+
info.operation = "setInputValue";
|
|
920
1011
|
info.selectors = selectors;
|
|
1012
|
+
value = this._fixUsingParams(value, _params);
|
|
921
1013
|
info.value = value;
|
|
1014
|
+
let error = null;
|
|
1015
|
+
let screenshotId = null;
|
|
1016
|
+
let screenshotPath = null;
|
|
922
1017
|
try {
|
|
923
1018
|
value = await this._replaceWithLocalData(value, this);
|
|
924
1019
|
let element = await this._locate(selectors, info, _params);
|
|
925
|
-
//insert red border around the element
|
|
926
1020
|
await this.scrollIfNeeded(element, info);
|
|
927
1021
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
928
1022
|
await this._highlightElements(element);
|
|
929
1023
|
try {
|
|
930
|
-
await element.
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
value = dayjs(value).format(format);
|
|
934
|
-
await element.fill(value);
|
|
935
|
-
}
|
|
936
|
-
else {
|
|
937
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
938
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
939
|
-
el.value = ""; // clear input
|
|
940
|
-
el.value = dateTimeValue;
|
|
941
|
-
}, dateTimeValue);
|
|
942
|
-
}
|
|
943
|
-
if (enter) {
|
|
944
|
-
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
945
|
-
await this.page.keyboard.press("Enter");
|
|
946
|
-
await this.waitForPageLoad();
|
|
947
|
-
}
|
|
1024
|
+
await element.evaluateHandle((el, value) => {
|
|
1025
|
+
el.value = value;
|
|
1026
|
+
}, value);
|
|
948
1027
|
}
|
|
949
1028
|
catch (error) {
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
this.logger.info("Trying again")(({ screenshotId, screenshotPath } = await this._screenShot(options, world, info)));
|
|
1029
|
+
this.logger.error("setInputValue failed, will try again");
|
|
1030
|
+
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
953
1031
|
info.screenshotPath = screenshotPath;
|
|
954
1032
|
Object.assign(error, { info: info });
|
|
955
|
-
await element.
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
value = dayjs(value).format(format);
|
|
959
|
-
await element.fill(value);
|
|
960
|
-
}
|
|
961
|
-
else {
|
|
962
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
963
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
964
|
-
el.value = ""; // clear input
|
|
965
|
-
el.value = dateTimeValue;
|
|
966
|
-
}, dateTimeValue);
|
|
967
|
-
}
|
|
968
|
-
if (enter) {
|
|
969
|
-
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
970
|
-
await this.page.keyboard.press("Enter");
|
|
971
|
-
await this.waitForPageLoad();
|
|
972
|
-
}
|
|
1033
|
+
await element.evaluateHandle((el, value) => {
|
|
1034
|
+
el.value = value;
|
|
1035
|
+
});
|
|
973
1036
|
}
|
|
974
1037
|
}
|
|
975
|
-
catch (
|
|
1038
|
+
catch (e) {
|
|
1039
|
+
this.logger.error("setInputValue failed " + info.log);
|
|
1040
|
+
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1041
|
+
info.screenshotPath = screenshotPath;
|
|
1042
|
+
Object.assign(e, { info: info });
|
|
976
1043
|
error = e;
|
|
977
1044
|
throw e;
|
|
978
1045
|
}
|
|
@@ -980,10 +1047,10 @@ class StableBrowser {
|
|
|
980
1047
|
const endTime = Date.now();
|
|
981
1048
|
this._reportToWorld(world, {
|
|
982
1049
|
element_name: selectors.element_name,
|
|
983
|
-
type: Types.
|
|
984
|
-
|
|
1050
|
+
type: Types.SET_INPUT,
|
|
1051
|
+
text: `Set input value`,
|
|
985
1052
|
value: value,
|
|
986
|
-
|
|
1053
|
+
screenshotId,
|
|
987
1054
|
result: error
|
|
988
1055
|
? {
|
|
989
1056
|
status: "FAILED",
|
|
@@ -1000,7 +1067,7 @@ class StableBrowser {
|
|
|
1000
1067
|
});
|
|
1001
1068
|
}
|
|
1002
1069
|
}
|
|
1003
|
-
async setDateTime(selectors, value, enter = false, _params = null, options = {}, world = null) {
|
|
1070
|
+
async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
|
|
1004
1071
|
this._validateSelectors(selectors);
|
|
1005
1072
|
const startTime = Date.now();
|
|
1006
1073
|
let error = null;
|
|
@@ -1012,6 +1079,7 @@ class StableBrowser {
|
|
|
1012
1079
|
info.selectors = selectors;
|
|
1013
1080
|
info.value = value;
|
|
1014
1081
|
try {
|
|
1082
|
+
value = await this._replaceWithLocalData(value, this);
|
|
1015
1083
|
let element = await this._locate(selectors, info, _params);
|
|
1016
1084
|
//insert red border around the element
|
|
1017
1085
|
await this.scrollIfNeeded(element, info);
|
|
@@ -1020,28 +1088,51 @@ class StableBrowser {
|
|
|
1020
1088
|
try {
|
|
1021
1089
|
await element.click();
|
|
1022
1090
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1091
|
+
if (format) {
|
|
1092
|
+
value = dayjs(value).format(format);
|
|
1093
|
+
await element.fill(value);
|
|
1094
|
+
}
|
|
1095
|
+
else {
|
|
1096
|
+
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1097
|
+
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1098
|
+
el.value = ""; // clear input
|
|
1099
|
+
el.value = dateTimeValue;
|
|
1100
|
+
}, dateTimeValue);
|
|
1101
|
+
}
|
|
1102
|
+
if (enter) {
|
|
1103
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1104
|
+
await this.page.keyboard.press("Enter");
|
|
1105
|
+
await this.waitForPageLoad();
|
|
1106
|
+
}
|
|
1028
1107
|
}
|
|
1029
|
-
catch (
|
|
1108
|
+
catch (err) {
|
|
1030
1109
|
//await this.closeUnexpectedPopups();
|
|
1031
1110
|
this.logger.error("setting date time input failed " + JSON.stringify(info));
|
|
1032
|
-
this.logger.info("Trying again")
|
|
1111
|
+
this.logger.info("Trying again");
|
|
1112
|
+
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1033
1113
|
info.screenshotPath = screenshotPath;
|
|
1034
|
-
Object.assign(
|
|
1114
|
+
Object.assign(err, { info: info });
|
|
1035
1115
|
await element.click();
|
|
1036
1116
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1117
|
+
if (format) {
|
|
1118
|
+
value = dayjs(value).format(format);
|
|
1119
|
+
await element.fill(value);
|
|
1120
|
+
}
|
|
1121
|
+
else {
|
|
1122
|
+
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1123
|
+
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1124
|
+
el.value = ""; // clear input
|
|
1125
|
+
el.value = dateTimeValue;
|
|
1126
|
+
}, dateTimeValue);
|
|
1127
|
+
}
|
|
1128
|
+
if (enter) {
|
|
1129
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1130
|
+
await this.page.keyboard.press("Enter");
|
|
1131
|
+
await this.waitForPageLoad();
|
|
1132
|
+
}
|
|
1042
1133
|
}
|
|
1043
1134
|
}
|
|
1044
|
-
catch (
|
|
1135
|
+
catch (e) {
|
|
1045
1136
|
error = e;
|
|
1046
1137
|
throw e;
|
|
1047
1138
|
}
|
|
@@ -1091,20 +1182,32 @@ class StableBrowser {
|
|
|
1091
1182
|
await this.scrollIfNeeded(element, info);
|
|
1092
1183
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1093
1184
|
await this._highlightElements(element);
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1185
|
+
if (options === null || options === undefined || !options.press) {
|
|
1186
|
+
try {
|
|
1187
|
+
let currentValue = await element.inputValue();
|
|
1188
|
+
if (currentValue) {
|
|
1189
|
+
await element.fill("");
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
catch (e) {
|
|
1193
|
+
this.logger.info("unable to clear input value");
|
|
1098
1194
|
}
|
|
1099
1195
|
}
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1196
|
+
if (options === null || options === undefined || options.press) {
|
|
1197
|
+
try {
|
|
1198
|
+
await element.click({ timeout: 5000 });
|
|
1199
|
+
}
|
|
1200
|
+
catch (e) {
|
|
1201
|
+
await element.dispatchEvent("click");
|
|
1202
|
+
}
|
|
1105
1203
|
}
|
|
1106
|
-
|
|
1107
|
-
|
|
1204
|
+
else {
|
|
1205
|
+
try {
|
|
1206
|
+
await element.focus();
|
|
1207
|
+
}
|
|
1208
|
+
catch (e) {
|
|
1209
|
+
await element.dispatchEvent("focus");
|
|
1210
|
+
}
|
|
1108
1211
|
}
|
|
1109
1212
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1110
1213
|
const valueSegment = _value.split("&&");
|
|
@@ -1192,7 +1295,7 @@ class StableBrowser {
|
|
|
1192
1295
|
let element = await this._locate(selectors, info, _params);
|
|
1193
1296
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1194
1297
|
await this._highlightElements(element);
|
|
1195
|
-
await element.fill(value
|
|
1298
|
+
await element.fill(value);
|
|
1196
1299
|
await element.dispatchEvent("change");
|
|
1197
1300
|
if (enter) {
|
|
1198
1301
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
@@ -1411,7 +1514,7 @@ class StableBrowser {
|
|
|
1411
1514
|
return info;
|
|
1412
1515
|
}
|
|
1413
1516
|
catch (e) {
|
|
1414
|
-
|
|
1517
|
+
await this.closeUnexpectedPopups();
|
|
1415
1518
|
this.logger.error("verify element contains text failed " + info.log);
|
|
1416
1519
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1417
1520
|
info.screenshotPath = screenshotPath;
|
|
@@ -1459,6 +1562,29 @@ class StableBrowser {
|
|
|
1459
1562
|
}
|
|
1460
1563
|
return dataFile;
|
|
1461
1564
|
}
|
|
1565
|
+
async waitForUserInput(message, world = null) {
|
|
1566
|
+
if (!message) {
|
|
1567
|
+
message = "# Wait for user input. Press any key to continue";
|
|
1568
|
+
}
|
|
1569
|
+
else {
|
|
1570
|
+
message = "# Wait for user input. " + message;
|
|
1571
|
+
}
|
|
1572
|
+
message += "\n";
|
|
1573
|
+
const value = await new Promise((resolve) => {
|
|
1574
|
+
const rl = readline.createInterface({
|
|
1575
|
+
input: process.stdin,
|
|
1576
|
+
output: process.stdout,
|
|
1577
|
+
});
|
|
1578
|
+
rl.question(message, (answer) => {
|
|
1579
|
+
rl.close();
|
|
1580
|
+
resolve(answer);
|
|
1581
|
+
});
|
|
1582
|
+
});
|
|
1583
|
+
if (value) {
|
|
1584
|
+
this.logger.info(`{{userInput}} was set to: ${value}`);
|
|
1585
|
+
}
|
|
1586
|
+
this.setTestData({ userInput: value }, world);
|
|
1587
|
+
}
|
|
1462
1588
|
setTestData(testData, world = null) {
|
|
1463
1589
|
if (!testData) {
|
|
1464
1590
|
return;
|
|
@@ -1486,7 +1612,7 @@ class StableBrowser {
|
|
|
1486
1612
|
const data = fs.readFileSync(filePath, "utf8");
|
|
1487
1613
|
const results = [];
|
|
1488
1614
|
return new Promise((resolve, reject) => {
|
|
1489
|
-
const readableStream = new
|
|
1615
|
+
const readableStream = new Readable();
|
|
1490
1616
|
readableStream._read = () => { }; // _read is required but you can noop it
|
|
1491
1617
|
readableStream.push(data);
|
|
1492
1618
|
readableStream.push(null);
|
|
@@ -1666,13 +1792,13 @@ class StableBrowser {
|
|
|
1666
1792
|
])));
|
|
1667
1793
|
const { data } = await client.send("Page.captureScreenshot", {
|
|
1668
1794
|
format: "png",
|
|
1669
|
-
clip: {
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
},
|
|
1795
|
+
// clip: {
|
|
1796
|
+
// x: 0,
|
|
1797
|
+
// y: 0,
|
|
1798
|
+
// width: viewportWidth,
|
|
1799
|
+
// height: viewportHeight,
|
|
1800
|
+
// scale: 1,
|
|
1801
|
+
// },
|
|
1676
1802
|
});
|
|
1677
1803
|
if (!screenshotPath) {
|
|
1678
1804
|
return data;
|
|
@@ -1861,11 +1987,14 @@ class StableBrowser {
|
|
|
1861
1987
|
//}
|
|
1862
1988
|
if ((result && result.data, result.data.status === true)) {
|
|
1863
1989
|
let codeOrUrlFound = false;
|
|
1990
|
+
let emailCode = null;
|
|
1991
|
+
let emailUrl = null;
|
|
1864
1992
|
// check if a code is returned
|
|
1865
1993
|
if (result.data.content && result.data.content.code) {
|
|
1866
1994
|
let code = result.data.content.code;
|
|
1867
1995
|
this.setTestData({ emailCode: code }, world);
|
|
1868
1996
|
this.logger.info("set test data: emailCode = " + code);
|
|
1997
|
+
emailCode = code;
|
|
1869
1998
|
codeOrUrlFound = true;
|
|
1870
1999
|
}
|
|
1871
2000
|
// check if a url is returned
|
|
@@ -1873,6 +2002,7 @@ class StableBrowser {
|
|
|
1873
2002
|
let url = result.data.content.url;
|
|
1874
2003
|
this.setTestData({ emailUrl: url }, world);
|
|
1875
2004
|
this.logger.info("set test data: emailUrl = " + url);
|
|
2005
|
+
emailUrl = url;
|
|
1876
2006
|
codeOrUrlFound = true;
|
|
1877
2007
|
}
|
|
1878
2008
|
if (codeOrUrlFound) {
|
|
@@ -2494,13 +2624,13 @@ class StableBrowser {
|
|
|
2494
2624
|
}
|
|
2495
2625
|
catch (e) {
|
|
2496
2626
|
if (e.label === "networkidle") {
|
|
2497
|
-
console.log("
|
|
2627
|
+
console.log("waited for the network to be idle timeout");
|
|
2498
2628
|
}
|
|
2499
2629
|
else if (e.label === "load") {
|
|
2500
|
-
console.log("
|
|
2630
|
+
console.log("waited for the load timeout");
|
|
2501
2631
|
}
|
|
2502
2632
|
else if (e.label === "domcontentloaded") {
|
|
2503
|
-
console.log("
|
|
2633
|
+
console.log("waited for the domcontent loaded timeout");
|
|
2504
2634
|
}
|
|
2505
2635
|
console.log(".");
|
|
2506
2636
|
}
|
|
@@ -2535,13 +2665,6 @@ class StableBrowser {
|
|
|
2535
2665
|
const info = {};
|
|
2536
2666
|
try {
|
|
2537
2667
|
await this.page.close();
|
|
2538
|
-
if (this.context && this.context.pages && this.context.pages.length > 0) {
|
|
2539
|
-
this.context.pages.pop();
|
|
2540
|
-
this.page = this.context.pages[this.context.pages.length - 1];
|
|
2541
|
-
this.context.page = this.page;
|
|
2542
|
-
let title = await this.page.title();
|
|
2543
|
-
console.log("Switched to page " + title);
|
|
2544
|
-
}
|
|
2545
2668
|
}
|
|
2546
2669
|
catch (e) {
|
|
2547
2670
|
console.log(".");
|
|
@@ -2650,33 +2773,18 @@ class StableBrowser {
|
|
|
2650
2773
|
}
|
|
2651
2774
|
async scrollIfNeeded(element, info) {
|
|
2652
2775
|
try {
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
if (rect &&
|
|
2656
|
-
rect.top >= 0 &&
|
|
2657
|
-
rect.left >= 0 &&
|
|
2658
|
-
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
|
|
2659
|
-
rect.right <= (window.innerWidth || document.documentElement.clientWidth)) {
|
|
2660
|
-
return false;
|
|
2661
|
-
}
|
|
2662
|
-
else {
|
|
2663
|
-
node.scrollIntoView({
|
|
2664
|
-
behavior: "smooth",
|
|
2665
|
-
block: "center",
|
|
2666
|
-
inline: "center",
|
|
2667
|
-
});
|
|
2668
|
-
return true;
|
|
2669
|
-
}
|
|
2776
|
+
await element.scrollIntoViewIfNeeded({
|
|
2777
|
+
timeout: 2000,
|
|
2670
2778
|
});
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
}
|
|
2779
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
2780
|
+
if (info) {
|
|
2781
|
+
info.box = await element.boundingBox({
|
|
2782
|
+
timeout: 1000,
|
|
2783
|
+
});
|
|
2676
2784
|
}
|
|
2677
2785
|
}
|
|
2678
2786
|
catch (e) {
|
|
2679
|
-
console.log("
|
|
2787
|
+
console.log("#-#");
|
|
2680
2788
|
}
|
|
2681
2789
|
}
|
|
2682
2790
|
_reportToWorld(world, properties) {
|