automation_model 1.0.394-dev → 1.0.394-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 +9 -6
- 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 +10 -2
- package/lib/stable_browser.js +246 -140
- 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);
|
|
@@ -414,7 +484,7 @@ class StableBrowser {
|
|
|
414
484
|
}
|
|
415
485
|
async _locate(selectors, info, _params, timeout = 30000) {
|
|
416
486
|
for (let i = 0; i < 3; i++) {
|
|
417
|
-
info.log += "attempt " + i + ":
|
|
487
|
+
info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
|
|
418
488
|
for (let j = 0; j < selectors.locators.length; j++) {
|
|
419
489
|
let selector = selectors.locators[j];
|
|
420
490
|
info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
|
|
@@ -434,9 +504,27 @@ class StableBrowser {
|
|
|
434
504
|
//let arrayMode = Array.isArray(selectors);
|
|
435
505
|
let scope = this.page;
|
|
436
506
|
if (selectors.iframe_src || selectors.frameLocators) {
|
|
507
|
+
const findFrame = (frame, framescope) => {
|
|
508
|
+
for (let i = 0; i < frame.selectors.length; i++) {
|
|
509
|
+
let frameLocator = frame.selectors[i];
|
|
510
|
+
if (frameLocator.css) {
|
|
511
|
+
framescope = framescope.frameLocator(frameLocator.css);
|
|
512
|
+
break;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
if (frame.children) {
|
|
516
|
+
return findFrame(frame.children, framescope);
|
|
517
|
+
}
|
|
518
|
+
return framescope;
|
|
519
|
+
};
|
|
437
520
|
info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
|
|
438
521
|
while (true) {
|
|
439
522
|
let frameFound = false;
|
|
523
|
+
if (selectors.nestFrmLoc) {
|
|
524
|
+
scope = findFrame(selectors.nestFrmLoc, scope);
|
|
525
|
+
frameFound = true;
|
|
526
|
+
break;
|
|
527
|
+
}
|
|
440
528
|
if (selectors.frameLocators) {
|
|
441
529
|
for (let i = 0; i < selectors.frameLocators.length; i++) {
|
|
442
530
|
let frameLocator = selectors.frameLocators[i];
|
|
@@ -600,6 +688,9 @@ class StableBrowser {
|
|
|
600
688
|
async click(selectors, _params, options = {}, world = null) {
|
|
601
689
|
this._validateSelectors(selectors);
|
|
602
690
|
const startTime = Date.now();
|
|
691
|
+
if (options && options.context) {
|
|
692
|
+
selectors.locators[0].text = options.context;
|
|
693
|
+
}
|
|
603
694
|
const info = {};
|
|
604
695
|
info.log = "***** click on " + selectors.element_name + " *****\n";
|
|
605
696
|
info.operation = "click";
|
|
@@ -613,14 +704,14 @@ class StableBrowser {
|
|
|
613
704
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
614
705
|
try {
|
|
615
706
|
await this._highlightElements(element);
|
|
616
|
-
await element.click(
|
|
707
|
+
await element.click();
|
|
617
708
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
618
709
|
}
|
|
619
710
|
catch (e) {
|
|
620
711
|
// await this.closeUnexpectedPopups();
|
|
621
712
|
info.log += "click failed, will try again" + "\n";
|
|
622
713
|
element = await this._locate(selectors, info, _params);
|
|
623
|
-
await element.click
|
|
714
|
+
await element.dispatchEvent("click");
|
|
624
715
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
625
716
|
}
|
|
626
717
|
await this.waitForPageLoad();
|
|
@@ -673,7 +764,7 @@ class StableBrowser {
|
|
|
673
764
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
674
765
|
try {
|
|
675
766
|
await this._highlightElements(element);
|
|
676
|
-
await element.setChecked(checked
|
|
767
|
+
await element.setChecked(checked);
|
|
677
768
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
678
769
|
}
|
|
679
770
|
catch (e) {
|
|
@@ -737,7 +828,7 @@ class StableBrowser {
|
|
|
737
828
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
738
829
|
try {
|
|
739
830
|
await this._highlightElements(element);
|
|
740
|
-
await element.hover(
|
|
831
|
+
await element.hover();
|
|
741
832
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
742
833
|
}
|
|
743
834
|
catch (e) {
|
|
@@ -799,7 +890,7 @@ class StableBrowser {
|
|
|
799
890
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
800
891
|
try {
|
|
801
892
|
await this._highlightElements(element);
|
|
802
|
-
await element.selectOption(values
|
|
893
|
+
await element.selectOption(values);
|
|
803
894
|
}
|
|
804
895
|
catch (e) {
|
|
805
896
|
//await this.closeUnexpectedPopups();
|
|
@@ -908,71 +999,45 @@ class StableBrowser {
|
|
|
908
999
|
});
|
|
909
1000
|
}
|
|
910
1001
|
}
|
|
911
|
-
async
|
|
1002
|
+
async setInputValue(selectors, value, _params = null, options = {}, world = null) {
|
|
1003
|
+
// set input value for non fillable inputs like date, time, range, color, etc.
|
|
912
1004
|
this._validateSelectors(selectors);
|
|
913
1005
|
const startTime = Date.now();
|
|
914
|
-
let error = null;
|
|
915
|
-
let screenshotId = null;
|
|
916
|
-
let screenshotPath = null;
|
|
917
1006
|
const info = {};
|
|
918
|
-
info.log = "";
|
|
919
|
-
info.operation =
|
|
1007
|
+
info.log = "***** set input value " + selectors.element_name + " *****\n";
|
|
1008
|
+
info.operation = "setInputValue";
|
|
920
1009
|
info.selectors = selectors;
|
|
1010
|
+
value = this._fixUsingParams(value, _params);
|
|
921
1011
|
info.value = value;
|
|
1012
|
+
let error = null;
|
|
1013
|
+
let screenshotId = null;
|
|
1014
|
+
let screenshotPath = null;
|
|
922
1015
|
try {
|
|
923
1016
|
value = await this._replaceWithLocalData(value, this);
|
|
924
1017
|
let element = await this._locate(selectors, info, _params);
|
|
925
|
-
//insert red border around the element
|
|
926
1018
|
await this.scrollIfNeeded(element, info);
|
|
927
1019
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
928
1020
|
await this._highlightElements(element);
|
|
929
1021
|
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
|
-
}
|
|
1022
|
+
await element.evaluateHandle((el, value) => {
|
|
1023
|
+
el.value = value;
|
|
1024
|
+
}, value);
|
|
948
1025
|
}
|
|
949
1026
|
catch (error) {
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
this.logger.info("Trying again")(({ screenshotId, screenshotPath } = await this._screenShot(options, world, info)));
|
|
1027
|
+
this.logger.error("setInputValue failed, will try again");
|
|
1028
|
+
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
953
1029
|
info.screenshotPath = screenshotPath;
|
|
954
1030
|
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
|
-
}
|
|
1031
|
+
await element.evaluateHandle((el, value) => {
|
|
1032
|
+
el.value = value;
|
|
1033
|
+
});
|
|
973
1034
|
}
|
|
974
1035
|
}
|
|
975
|
-
catch (
|
|
1036
|
+
catch (e) {
|
|
1037
|
+
this.logger.error("setInputValue failed " + info.log);
|
|
1038
|
+
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1039
|
+
info.screenshotPath = screenshotPath;
|
|
1040
|
+
Object.assign(e, { info: info });
|
|
976
1041
|
error = e;
|
|
977
1042
|
throw e;
|
|
978
1043
|
}
|
|
@@ -980,10 +1045,10 @@ class StableBrowser {
|
|
|
980
1045
|
const endTime = Date.now();
|
|
981
1046
|
this._reportToWorld(world, {
|
|
982
1047
|
element_name: selectors.element_name,
|
|
983
|
-
type: Types.
|
|
984
|
-
|
|
1048
|
+
type: Types.SET_INPUT,
|
|
1049
|
+
text: `Set input value`,
|
|
985
1050
|
value: value,
|
|
986
|
-
|
|
1051
|
+
screenshotId,
|
|
987
1052
|
result: error
|
|
988
1053
|
? {
|
|
989
1054
|
status: "FAILED",
|
|
@@ -1000,7 +1065,7 @@ class StableBrowser {
|
|
|
1000
1065
|
});
|
|
1001
1066
|
}
|
|
1002
1067
|
}
|
|
1003
|
-
async setDateTime(selectors, value, enter = false, _params = null, options = {}, world = null) {
|
|
1068
|
+
async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
|
|
1004
1069
|
this._validateSelectors(selectors);
|
|
1005
1070
|
const startTime = Date.now();
|
|
1006
1071
|
let error = null;
|
|
@@ -1012,6 +1077,7 @@ class StableBrowser {
|
|
|
1012
1077
|
info.selectors = selectors;
|
|
1013
1078
|
info.value = value;
|
|
1014
1079
|
try {
|
|
1080
|
+
value = await this._replaceWithLocalData(value, this);
|
|
1015
1081
|
let element = await this._locate(selectors, info, _params);
|
|
1016
1082
|
//insert red border around the element
|
|
1017
1083
|
await this.scrollIfNeeded(element, info);
|
|
@@ -1020,28 +1086,51 @@ class StableBrowser {
|
|
|
1020
1086
|
try {
|
|
1021
1087
|
await element.click();
|
|
1022
1088
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1089
|
+
if (format) {
|
|
1090
|
+
value = dayjs(value).format(format);
|
|
1091
|
+
await element.fill(value);
|
|
1092
|
+
}
|
|
1093
|
+
else {
|
|
1094
|
+
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1095
|
+
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1096
|
+
el.value = ""; // clear input
|
|
1097
|
+
el.value = dateTimeValue;
|
|
1098
|
+
}, dateTimeValue);
|
|
1099
|
+
}
|
|
1100
|
+
if (enter) {
|
|
1101
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1102
|
+
await this.page.keyboard.press("Enter");
|
|
1103
|
+
await this.waitForPageLoad();
|
|
1104
|
+
}
|
|
1028
1105
|
}
|
|
1029
|
-
catch (
|
|
1106
|
+
catch (err) {
|
|
1030
1107
|
//await this.closeUnexpectedPopups();
|
|
1031
1108
|
this.logger.error("setting date time input failed " + JSON.stringify(info));
|
|
1032
|
-
this.logger.info("Trying again")
|
|
1109
|
+
this.logger.info("Trying again");
|
|
1110
|
+
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1033
1111
|
info.screenshotPath = screenshotPath;
|
|
1034
|
-
Object.assign(
|
|
1112
|
+
Object.assign(err, { info: info });
|
|
1035
1113
|
await element.click();
|
|
1036
1114
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1115
|
+
if (format) {
|
|
1116
|
+
value = dayjs(value).format(format);
|
|
1117
|
+
await element.fill(value);
|
|
1118
|
+
}
|
|
1119
|
+
else {
|
|
1120
|
+
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1121
|
+
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1122
|
+
el.value = ""; // clear input
|
|
1123
|
+
el.value = dateTimeValue;
|
|
1124
|
+
}, dateTimeValue);
|
|
1125
|
+
}
|
|
1126
|
+
if (enter) {
|
|
1127
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1128
|
+
await this.page.keyboard.press("Enter");
|
|
1129
|
+
await this.waitForPageLoad();
|
|
1130
|
+
}
|
|
1042
1131
|
}
|
|
1043
1132
|
}
|
|
1044
|
-
catch (
|
|
1133
|
+
catch (e) {
|
|
1045
1134
|
error = e;
|
|
1046
1135
|
throw e;
|
|
1047
1136
|
}
|
|
@@ -1091,20 +1180,32 @@ class StableBrowser {
|
|
|
1091
1180
|
await this.scrollIfNeeded(element, info);
|
|
1092
1181
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1093
1182
|
await this._highlightElements(element);
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1183
|
+
if (options === null || options === undefined || !options.press) {
|
|
1184
|
+
try {
|
|
1185
|
+
let currentValue = await element.inputValue();
|
|
1186
|
+
if (currentValue) {
|
|
1187
|
+
await element.fill("");
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
catch (e) {
|
|
1191
|
+
this.logger.info("unable to clear input value");
|
|
1098
1192
|
}
|
|
1099
1193
|
}
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1194
|
+
if (options === null || options === undefined || options.press) {
|
|
1195
|
+
try {
|
|
1196
|
+
await element.click({ timeout: 5000 });
|
|
1197
|
+
}
|
|
1198
|
+
catch (e) {
|
|
1199
|
+
await element.dispatchEvent("click");
|
|
1200
|
+
}
|
|
1105
1201
|
}
|
|
1106
|
-
|
|
1107
|
-
|
|
1202
|
+
else {
|
|
1203
|
+
try {
|
|
1204
|
+
await element.focus();
|
|
1205
|
+
}
|
|
1206
|
+
catch (e) {
|
|
1207
|
+
await element.dispatchEvent("focus");
|
|
1208
|
+
}
|
|
1108
1209
|
}
|
|
1109
1210
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1110
1211
|
const valueSegment = _value.split("&&");
|
|
@@ -1192,7 +1293,7 @@ class StableBrowser {
|
|
|
1192
1293
|
let element = await this._locate(selectors, info, _params);
|
|
1193
1294
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1194
1295
|
await this._highlightElements(element);
|
|
1195
|
-
await element.fill(value
|
|
1296
|
+
await element.fill(value);
|
|
1196
1297
|
await element.dispatchEvent("change");
|
|
1197
1298
|
if (enter) {
|
|
1198
1299
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
@@ -1411,7 +1512,7 @@ class StableBrowser {
|
|
|
1411
1512
|
return info;
|
|
1412
1513
|
}
|
|
1413
1514
|
catch (e) {
|
|
1414
|
-
|
|
1515
|
+
await this.closeUnexpectedPopups();
|
|
1415
1516
|
this.logger.error("verify element contains text failed " + info.log);
|
|
1416
1517
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1417
1518
|
info.screenshotPath = screenshotPath;
|
|
@@ -1459,6 +1560,29 @@ class StableBrowser {
|
|
|
1459
1560
|
}
|
|
1460
1561
|
return dataFile;
|
|
1461
1562
|
}
|
|
1563
|
+
async waitForUserInput(message, world = null) {
|
|
1564
|
+
if (!message) {
|
|
1565
|
+
message = "# Wait for user input. Press any key to continue";
|
|
1566
|
+
}
|
|
1567
|
+
else {
|
|
1568
|
+
message = "# Wait for user input. " + message;
|
|
1569
|
+
}
|
|
1570
|
+
message += "\n";
|
|
1571
|
+
const value = await new Promise((resolve) => {
|
|
1572
|
+
const rl = readline.createInterface({
|
|
1573
|
+
input: process.stdin,
|
|
1574
|
+
output: process.stdout,
|
|
1575
|
+
});
|
|
1576
|
+
rl.question(message, (answer) => {
|
|
1577
|
+
rl.close();
|
|
1578
|
+
resolve(answer);
|
|
1579
|
+
});
|
|
1580
|
+
});
|
|
1581
|
+
if (value) {
|
|
1582
|
+
this.logger.info(`{{userInput}} was set to: ${value}`);
|
|
1583
|
+
}
|
|
1584
|
+
this.setTestData({ userInput: value }, world);
|
|
1585
|
+
}
|
|
1462
1586
|
setTestData(testData, world = null) {
|
|
1463
1587
|
if (!testData) {
|
|
1464
1588
|
return;
|
|
@@ -1486,7 +1610,7 @@ class StableBrowser {
|
|
|
1486
1610
|
const data = fs.readFileSync(filePath, "utf8");
|
|
1487
1611
|
const results = [];
|
|
1488
1612
|
return new Promise((resolve, reject) => {
|
|
1489
|
-
const readableStream = new
|
|
1613
|
+
const readableStream = new Readable();
|
|
1490
1614
|
readableStream._read = () => { }; // _read is required but you can noop it
|
|
1491
1615
|
readableStream.push(data);
|
|
1492
1616
|
readableStream.push(null);
|
|
@@ -1666,13 +1790,13 @@ class StableBrowser {
|
|
|
1666
1790
|
])));
|
|
1667
1791
|
const { data } = await client.send("Page.captureScreenshot", {
|
|
1668
1792
|
format: "png",
|
|
1669
|
-
clip: {
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
},
|
|
1793
|
+
// clip: {
|
|
1794
|
+
// x: 0,
|
|
1795
|
+
// y: 0,
|
|
1796
|
+
// width: viewportWidth,
|
|
1797
|
+
// height: viewportHeight,
|
|
1798
|
+
// scale: 1,
|
|
1799
|
+
// },
|
|
1676
1800
|
});
|
|
1677
1801
|
if (!screenshotPath) {
|
|
1678
1802
|
return data;
|
|
@@ -1861,22 +1985,26 @@ class StableBrowser {
|
|
|
1861
1985
|
//}
|
|
1862
1986
|
if ((result && result.data, result.data.status === true)) {
|
|
1863
1987
|
let codeOrUrlFound = false;
|
|
1988
|
+
let emailCode = null;
|
|
1989
|
+
let emailUrl = null;
|
|
1864
1990
|
// check if a code is returned
|
|
1865
1991
|
if (result.data.content && result.data.content.code) {
|
|
1866
1992
|
let code = result.data.content.code;
|
|
1867
1993
|
this.setTestData({ emailCode: code }, world);
|
|
1868
|
-
this.logger.info("set test data: emailCode=" + code);
|
|
1994
|
+
this.logger.info("set test data: emailCode = " + code);
|
|
1995
|
+
emailCode = code;
|
|
1869
1996
|
codeOrUrlFound = true;
|
|
1870
1997
|
}
|
|
1871
1998
|
// check if a url is returned
|
|
1872
1999
|
if (result.data.content && result.data.content.url) {
|
|
1873
2000
|
let url = result.data.content.url;
|
|
1874
2001
|
this.setTestData({ emailUrl: url }, world);
|
|
1875
|
-
this.logger.info("set test data: emailUrl=" + url);
|
|
2002
|
+
this.logger.info("set test data: emailUrl = " + url);
|
|
2003
|
+
emailUrl = url;
|
|
1876
2004
|
codeOrUrlFound = true;
|
|
1877
2005
|
}
|
|
1878
2006
|
if (codeOrUrlFound) {
|
|
1879
|
-
return;
|
|
2007
|
+
return { emailUrl, emailCode };
|
|
1880
2008
|
}
|
|
1881
2009
|
else {
|
|
1882
2010
|
this.logger.info("an email received but no code or url found");
|
|
@@ -2494,13 +2622,13 @@ class StableBrowser {
|
|
|
2494
2622
|
}
|
|
2495
2623
|
catch (e) {
|
|
2496
2624
|
if (e.label === "networkidle") {
|
|
2497
|
-
console.log("
|
|
2625
|
+
console.log("waited for the network to be idle timeout");
|
|
2498
2626
|
}
|
|
2499
2627
|
else if (e.label === "load") {
|
|
2500
|
-
console.log("
|
|
2628
|
+
console.log("waited for the load timeout");
|
|
2501
2629
|
}
|
|
2502
2630
|
else if (e.label === "domcontentloaded") {
|
|
2503
|
-
console.log("
|
|
2631
|
+
console.log("waited for the domcontent loaded timeout");
|
|
2504
2632
|
}
|
|
2505
2633
|
console.log(".");
|
|
2506
2634
|
}
|
|
@@ -2535,13 +2663,6 @@ class StableBrowser {
|
|
|
2535
2663
|
const info = {};
|
|
2536
2664
|
try {
|
|
2537
2665
|
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
2666
|
}
|
|
2546
2667
|
catch (e) {
|
|
2547
2668
|
console.log(".");
|
|
@@ -2650,33 +2771,18 @@ class StableBrowser {
|
|
|
2650
2771
|
}
|
|
2651
2772
|
async scrollIfNeeded(element, info) {
|
|
2652
2773
|
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
|
-
}
|
|
2774
|
+
await element.scrollIntoViewIfNeeded({
|
|
2775
|
+
timeout: 2000,
|
|
2670
2776
|
});
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
}
|
|
2777
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
2778
|
+
if (info) {
|
|
2779
|
+
info.box = await element.boundingBox({
|
|
2780
|
+
timeout: 1000,
|
|
2781
|
+
});
|
|
2676
2782
|
}
|
|
2677
2783
|
}
|
|
2678
2784
|
catch (e) {
|
|
2679
|
-
console.log("
|
|
2785
|
+
console.log("#-#");
|
|
2680
2786
|
}
|
|
2681
2787
|
}
|
|
2682
2788
|
_reportToWorld(world, properties) {
|