automation_model 1.0.413-dev → 1.0.413-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 +21 -10
- package/lib/api.js.map +1 -1
- package/lib/auto_page.d.ts +1 -1
- package/lib/auto_page.js +6 -2
- 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/environment.d.ts +3 -0
- package/lib/environment.js +1 -0
- package/lib/environment.js.map +1 -1
- package/lib/init_browser.d.ts +2 -1
- package/lib/init_browser.js +38 -3
- package/lib/init_browser.js.map +1 -1
- package/lib/stable_browser.d.ts +12 -4
- package/lib/stable_browser.js +223 -102
- package/lib/stable_browser.js.map +1 -1
- package/lib/test_context.d.ts +3 -0
- package/lib/test_context.js +3 -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 +8 -6
package/lib/stable_browser.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import { expect } from "@playwright/test";
|
|
3
3
|
import dayjs from "dayjs";
|
|
4
4
|
import fs from "fs";
|
|
5
|
+
import { Jimp } from "jimp";
|
|
5
6
|
import path from "path";
|
|
6
7
|
import reg_parser from "regex-parser";
|
|
7
|
-
import sharp from "sharp";
|
|
8
8
|
import { findDateAlternatives, findNumberAlternatives } from "./analyze_helper.js";
|
|
9
9
|
import { getDateTimeValue } from "./date_time.js";
|
|
10
10
|
import drawRectangle from "./drawRect.js";
|
|
@@ -14,6 +14,8 @@ import objectPath from "object-path";
|
|
|
14
14
|
import { decrypt } from "./utils.js";
|
|
15
15
|
import csv from "csv-parser";
|
|
16
16
|
import { Readable } from "node:stream";
|
|
17
|
+
import readline from "readline";
|
|
18
|
+
import { getContext } from "./init_browser.js";
|
|
17
19
|
const Types = {
|
|
18
20
|
CLICK: "click_element",
|
|
19
21
|
NAVIGATE: "navigate",
|
|
@@ -41,15 +43,19 @@ const Types = {
|
|
|
41
43
|
LOAD_DATA: "load_data",
|
|
42
44
|
SET_INPUT: "set_input",
|
|
43
45
|
};
|
|
46
|
+
export const apps = {};
|
|
44
47
|
class StableBrowser {
|
|
45
|
-
constructor(browser, page, logger = null, context = null) {
|
|
48
|
+
constructor(browser, page, logger = null, context = null, world = null) {
|
|
46
49
|
this.browser = browser;
|
|
47
50
|
this.page = page;
|
|
48
51
|
this.logger = logger;
|
|
49
52
|
this.context = context;
|
|
53
|
+
this.world = world;
|
|
50
54
|
this.project_path = null;
|
|
51
55
|
this.webLogFile = null;
|
|
56
|
+
this.networkLogger = null;
|
|
52
57
|
this.configuration = null;
|
|
58
|
+
this.appName = "main";
|
|
53
59
|
if (!this.logger) {
|
|
54
60
|
this.logger = console;
|
|
55
61
|
}
|
|
@@ -75,23 +81,34 @@ class StableBrowser {
|
|
|
75
81
|
this.logger.error("unable to read ai_config.json");
|
|
76
82
|
}
|
|
77
83
|
const logFolder = path.join(this.project_path, "logs", "web");
|
|
78
|
-
this.
|
|
79
|
-
this.registerConsoleLogListener(page, context, this.webLogFile);
|
|
80
|
-
this.registerRequestListener();
|
|
84
|
+
this.world = world;
|
|
81
85
|
context.pages = [this.page];
|
|
82
86
|
context.pageLoading = { status: false };
|
|
87
|
+
this.registerEventListeners(this.context);
|
|
88
|
+
}
|
|
89
|
+
registerEventListeners(context) {
|
|
90
|
+
this.registerConsoleLogListener(this.page, context);
|
|
91
|
+
this.registerRequestListener(this.page, context, this.webLogFile);
|
|
92
|
+
if (!context.pageLoading) {
|
|
93
|
+
context.pageLoading = { status: false };
|
|
94
|
+
}
|
|
83
95
|
context.playContext.on("page", async function (page) {
|
|
84
96
|
context.pageLoading.status = true;
|
|
85
97
|
this.page = page;
|
|
86
98
|
context.page = page;
|
|
87
99
|
context.pages.push(page);
|
|
88
100
|
page.on("close", async () => {
|
|
89
|
-
if (this.context && this.context.pages && this.context.pages.length >
|
|
101
|
+
if (this.context && this.context.pages && this.context.pages.length > 1) {
|
|
90
102
|
this.context.pages.pop();
|
|
91
103
|
this.page = this.context.pages[this.context.pages.length - 1];
|
|
92
104
|
this.context.page = this.page;
|
|
93
|
-
|
|
94
|
-
|
|
105
|
+
try {
|
|
106
|
+
let title = await this.page.title();
|
|
107
|
+
console.log("Switched to page " + title);
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
console.error("Error on page close", error);
|
|
111
|
+
}
|
|
95
112
|
}
|
|
96
113
|
});
|
|
97
114
|
try {
|
|
@@ -104,6 +121,36 @@ class StableBrowser {
|
|
|
104
121
|
context.pageLoading.status = false;
|
|
105
122
|
}.bind(this));
|
|
106
123
|
}
|
|
124
|
+
async switchApp(appName) {
|
|
125
|
+
// check if the current app (this.appName) is the same as the new app
|
|
126
|
+
if (this.appName === appName) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
let navigate = false;
|
|
130
|
+
if (!apps[appName]) {
|
|
131
|
+
let newContext = await getContext(null, false, this.logger, appName, false, this);
|
|
132
|
+
navigate = true;
|
|
133
|
+
apps[appName] = {
|
|
134
|
+
context: newContext,
|
|
135
|
+
browser: newContext.browser,
|
|
136
|
+
page: newContext.page,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const tempContext = {};
|
|
140
|
+
this._copyContext(this, tempContext);
|
|
141
|
+
this._copyContext(apps[appName], this);
|
|
142
|
+
apps[this.appName] = tempContext;
|
|
143
|
+
this.appName = appName;
|
|
144
|
+
if (navigate) {
|
|
145
|
+
await this.goto(this.context.environment.baseUrl);
|
|
146
|
+
await this.waitForPageLoad();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
_copyContext(from, to) {
|
|
150
|
+
to.browser = from.browser;
|
|
151
|
+
to.page = from.page;
|
|
152
|
+
to.context = from.context;
|
|
153
|
+
}
|
|
107
154
|
getWebLogFile(logFolder) {
|
|
108
155
|
if (!fs.existsSync(logFolder)) {
|
|
109
156
|
fs.mkdirSync(logFolder, { recursive: true });
|
|
@@ -115,37 +162,63 @@ class StableBrowser {
|
|
|
115
162
|
const fileName = nextIndex + ".json";
|
|
116
163
|
return path.join(logFolder, fileName);
|
|
117
164
|
}
|
|
118
|
-
registerConsoleLogListener(page, context
|
|
165
|
+
registerConsoleLogListener(page, context) {
|
|
119
166
|
if (!this.context.webLogger) {
|
|
120
167
|
this.context.webLogger = [];
|
|
121
168
|
}
|
|
122
169
|
page.on("console", async (msg) => {
|
|
123
|
-
|
|
170
|
+
var _a;
|
|
171
|
+
const obj = {
|
|
124
172
|
type: msg.type(),
|
|
125
173
|
text: msg.text(),
|
|
126
174
|
location: msg.location(),
|
|
127
175
|
time: new Date().toISOString(),
|
|
128
|
-
}
|
|
129
|
-
|
|
176
|
+
};
|
|
177
|
+
this.context.webLogger.push(obj);
|
|
178
|
+
(_a = this.world) === null || _a === void 0 ? void 0 : _a.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
|
|
130
179
|
});
|
|
131
180
|
}
|
|
132
|
-
registerRequestListener() {
|
|
133
|
-
this.
|
|
181
|
+
registerRequestListener(page, context, logFile) {
|
|
182
|
+
if (!this.context.networkLogger) {
|
|
183
|
+
this.context.networkLogger = [];
|
|
184
|
+
}
|
|
185
|
+
page.on("request", async (data) => {
|
|
186
|
+
var _a;
|
|
187
|
+
const startTime = new Date().getTime();
|
|
134
188
|
try {
|
|
135
|
-
const pageUrl = new URL(
|
|
189
|
+
const pageUrl = new URL(page.url());
|
|
136
190
|
const requestUrl = new URL(data.url());
|
|
137
191
|
if (pageUrl.hostname === requestUrl.hostname) {
|
|
138
192
|
const method = data.method();
|
|
139
|
-
if (
|
|
193
|
+
if (["POST", "GET", "PUT", "DELETE", "PATCH"].includes(method)) {
|
|
140
194
|
const token = await data.headerValue("Authorization");
|
|
141
195
|
if (token) {
|
|
142
|
-
|
|
196
|
+
context.authtoken = token;
|
|
143
197
|
}
|
|
144
198
|
}
|
|
145
199
|
}
|
|
200
|
+
const response = await data.response();
|
|
201
|
+
const endTime = new Date().getTime();
|
|
202
|
+
const obj = {
|
|
203
|
+
url: data.url(),
|
|
204
|
+
method: data.method(),
|
|
205
|
+
postData: data.postData(),
|
|
206
|
+
error: data.failure() ? data.failure().errorText : null,
|
|
207
|
+
duration: endTime - startTime,
|
|
208
|
+
startTime,
|
|
209
|
+
};
|
|
210
|
+
context.networkLogger.push(obj);
|
|
211
|
+
(_a = this.world) === null || _a === void 0 ? void 0 : _a.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
|
|
146
212
|
}
|
|
147
213
|
catch (error) {
|
|
148
214
|
console.error("Error in request listener", error);
|
|
215
|
+
context.networkLogger.push({
|
|
216
|
+
error: "not able to listen",
|
|
217
|
+
message: error.message,
|
|
218
|
+
stack: error.stack,
|
|
219
|
+
time: new Date().toISOString(),
|
|
220
|
+
});
|
|
221
|
+
// await fs.promises.writeFile(logFile, JSON.stringify(context.networkLogger, null, 2));
|
|
149
222
|
}
|
|
150
223
|
});
|
|
151
224
|
}
|
|
@@ -226,7 +299,7 @@ class StableBrowser {
|
|
|
226
299
|
// }
|
|
227
300
|
locatorReturn = scope.getByRole(locator.role[0], locator.role[1]);
|
|
228
301
|
}
|
|
229
|
-
if (locator.css
|
|
302
|
+
if (locator.css) {
|
|
230
303
|
locatorReturn = scope.locator(locator.css);
|
|
231
304
|
}
|
|
232
305
|
// handle role/name locators
|
|
@@ -242,25 +315,29 @@ class StableBrowser {
|
|
|
242
315
|
}
|
|
243
316
|
}
|
|
244
317
|
if (locator === null || locator === void 0 ? void 0 : locator.engine) {
|
|
245
|
-
if (locator.engine === "
|
|
246
|
-
|
|
318
|
+
if (locator.engine === "css") {
|
|
319
|
+
locatorReturn = scope.locator(locator.selector);
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
let selector = locator.selector;
|
|
323
|
+
if (locator.engine === "internal:attr") {
|
|
324
|
+
if (!selector.startsWith("[")) {
|
|
325
|
+
selector = `[${selector}]`;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
locatorReturn = scope.locator(`${locator.engine}=${selector}`);
|
|
247
329
|
}
|
|
248
|
-
locatorReturn = scope.locator(`${locator.engine}=${selector}`);
|
|
249
330
|
}
|
|
250
331
|
if (!locatorReturn) {
|
|
251
332
|
console.error(locator);
|
|
252
333
|
throw new Error("Locator undefined");
|
|
253
|
-
// } else {
|
|
254
|
-
// const count = locatorReturn.count();
|
|
255
|
-
// if (count === 0) {
|
|
256
|
-
// throw new Error("Elements not found");
|
|
257
|
-
// } else if (count > 1) {
|
|
258
|
-
// throw new Error("Multiple elements found");
|
|
259
|
-
// }
|
|
260
334
|
}
|
|
261
335
|
return locatorReturn;
|
|
262
336
|
}
|
|
263
337
|
async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
|
|
338
|
+
if (css && css.locator) {
|
|
339
|
+
css = css.locator;
|
|
340
|
+
}
|
|
264
341
|
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, true, _params);
|
|
265
342
|
if (result.elementCount === 0) {
|
|
266
343
|
return;
|
|
@@ -288,6 +365,15 @@ class StableBrowser {
|
|
|
288
365
|
return false;
|
|
289
366
|
}
|
|
290
367
|
document.isParent = isParent;
|
|
368
|
+
function getRegex(str) {
|
|
369
|
+
const match = str.match(/^\/(.*?)\/([gimuy]*)$/);
|
|
370
|
+
if (!match) {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
let [_, pattern, flags] = match;
|
|
374
|
+
return new RegExp(pattern, flags);
|
|
375
|
+
}
|
|
376
|
+
document.getRegex = getRegex;
|
|
291
377
|
function collectAllShadowDomElements(element, result = []) {
|
|
292
378
|
// Check and add the element if it has a shadow root
|
|
293
379
|
if (element.shadowRoot) {
|
|
@@ -306,6 +392,10 @@ class StableBrowser {
|
|
|
306
392
|
if (!tag) {
|
|
307
393
|
tag = "*";
|
|
308
394
|
}
|
|
395
|
+
let regexpSearch = document.getRegex(text);
|
|
396
|
+
if (regexpSearch) {
|
|
397
|
+
regex = true;
|
|
398
|
+
}
|
|
309
399
|
let elements = Array.from(document.querySelectorAll(tag));
|
|
310
400
|
let shadowHosts = [];
|
|
311
401
|
document.collectAllShadowDomElements(document, shadowHosts);
|
|
@@ -321,7 +411,9 @@ class StableBrowser {
|
|
|
321
411
|
let randomToken = null;
|
|
322
412
|
const foundElements = [];
|
|
323
413
|
if (regex) {
|
|
324
|
-
|
|
414
|
+
if (!regexpSearch) {
|
|
415
|
+
regexpSearch = new RegExp(text, "im");
|
|
416
|
+
}
|
|
325
417
|
for (let i = 0; i < elements.length; i++) {
|
|
326
418
|
const element = elements[i];
|
|
327
419
|
if ((element.innerText && regexpSearch.test(element.innerText)) ||
|
|
@@ -335,8 +427,8 @@ class StableBrowser {
|
|
|
335
427
|
for (let i = 0; i < elements.length; i++) {
|
|
336
428
|
const element = elements[i];
|
|
337
429
|
if (partial) {
|
|
338
|
-
if ((element.innerText && element.innerText.trim().includes(text)) ||
|
|
339
|
-
(element.value && element.value.includes(text))) {
|
|
430
|
+
if ((element.innerText && element.innerText.toLowerCase().trim().includes(text.toLowerCase())) ||
|
|
431
|
+
(element.value && element.value.toLowerCase().includes(text.toLowerCase()))) {
|
|
340
432
|
foundElements.push(element);
|
|
341
433
|
}
|
|
342
434
|
}
|
|
@@ -469,6 +561,8 @@ class StableBrowser {
|
|
|
469
561
|
if (result.foundElements.length > 0) {
|
|
470
562
|
let dialogCloseLocator = result.foundElements[0].locator;
|
|
471
563
|
await dialogCloseLocator.click();
|
|
564
|
+
// wait for the dialog to close
|
|
565
|
+
await dialogCloseLocator.waitFor({ state: "hidden" });
|
|
472
566
|
return { rerun: true };
|
|
473
567
|
}
|
|
474
568
|
}
|
|
@@ -477,7 +571,7 @@ class StableBrowser {
|
|
|
477
571
|
}
|
|
478
572
|
async _locate(selectors, info, _params, timeout = 30000) {
|
|
479
573
|
for (let i = 0; i < 3; i++) {
|
|
480
|
-
info.log += "attempt " + i + ":
|
|
574
|
+
info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
|
|
481
575
|
for (let j = 0; j < selectors.locators.length; j++) {
|
|
482
576
|
let selector = selectors.locators[j];
|
|
483
577
|
info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
|
|
@@ -497,9 +591,30 @@ class StableBrowser {
|
|
|
497
591
|
//let arrayMode = Array.isArray(selectors);
|
|
498
592
|
let scope = this.page;
|
|
499
593
|
if (selectors.iframe_src || selectors.frameLocators) {
|
|
594
|
+
const findFrame = (frame, framescope) => {
|
|
595
|
+
for (let i = 0; i < frame.selectors.length; i++) {
|
|
596
|
+
let frameLocator = frame.selectors[i];
|
|
597
|
+
if (frameLocator.css) {
|
|
598
|
+
framescope = framescope.frameLocator(frameLocator.css);
|
|
599
|
+
if (frameLocator.index) {
|
|
600
|
+
framescope = framescope.nth(frameLocator.index);
|
|
601
|
+
}
|
|
602
|
+
break;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
if (frame.children) {
|
|
606
|
+
return findFrame(frame.children, framescope);
|
|
607
|
+
}
|
|
608
|
+
return framescope;
|
|
609
|
+
};
|
|
500
610
|
info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
|
|
501
611
|
while (true) {
|
|
502
612
|
let frameFound = false;
|
|
613
|
+
if (selectors.nestFrmLoc) {
|
|
614
|
+
scope = findFrame(selectors.nestFrmLoc, scope);
|
|
615
|
+
frameFound = true;
|
|
616
|
+
break;
|
|
617
|
+
}
|
|
503
618
|
if (selectors.frameLocators) {
|
|
504
619
|
for (let i = 0; i < selectors.frameLocators.length; i++) {
|
|
505
620
|
let frameLocator = selectors.frameLocators[i];
|
|
@@ -679,14 +794,14 @@ class StableBrowser {
|
|
|
679
794
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
680
795
|
try {
|
|
681
796
|
await this._highlightElements(element);
|
|
682
|
-
await element.click(
|
|
797
|
+
await element.click();
|
|
683
798
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
684
799
|
}
|
|
685
800
|
catch (e) {
|
|
686
801
|
// await this.closeUnexpectedPopups();
|
|
687
802
|
info.log += "click failed, will try again" + "\n";
|
|
688
803
|
element = await this._locate(selectors, info, _params);
|
|
689
|
-
await element.click
|
|
804
|
+
await element.dispatchEvent("click");
|
|
690
805
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
691
806
|
}
|
|
692
807
|
await this.waitForPageLoad();
|
|
@@ -739,7 +854,7 @@ class StableBrowser {
|
|
|
739
854
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
740
855
|
try {
|
|
741
856
|
await this._highlightElements(element);
|
|
742
|
-
await element.setChecked(checked
|
|
857
|
+
await element.setChecked(checked);
|
|
743
858
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
744
859
|
}
|
|
745
860
|
catch (e) {
|
|
@@ -803,7 +918,7 @@ class StableBrowser {
|
|
|
803
918
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
804
919
|
try {
|
|
805
920
|
await this._highlightElements(element);
|
|
806
|
-
await element.hover(
|
|
921
|
+
await element.hover();
|
|
807
922
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
808
923
|
}
|
|
809
924
|
catch (e) {
|
|
@@ -865,7 +980,7 @@ class StableBrowser {
|
|
|
865
980
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
866
981
|
try {
|
|
867
982
|
await this._highlightElements(element);
|
|
868
|
-
await element.selectOption(values
|
|
983
|
+
await element.selectOption(values);
|
|
869
984
|
}
|
|
870
985
|
catch (e) {
|
|
871
986
|
//await this.closeUnexpectedPopups();
|
|
@@ -1153,7 +1268,6 @@ class StableBrowser {
|
|
|
1153
1268
|
let element = await this._locate(selectors, info, _params);
|
|
1154
1269
|
//insert red border around the element
|
|
1155
1270
|
await this.scrollIfNeeded(element, info);
|
|
1156
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1157
1271
|
await this._highlightElements(element);
|
|
1158
1272
|
if (options === null || options === undefined || !options.press) {
|
|
1159
1273
|
try {
|
|
@@ -1203,6 +1317,7 @@ class StableBrowser {
|
|
|
1203
1317
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1204
1318
|
}
|
|
1205
1319
|
}
|
|
1320
|
+
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1206
1321
|
if (enter === true) {
|
|
1207
1322
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1208
1323
|
await this.page.keyboard.press("Enter");
|
|
@@ -1268,7 +1383,7 @@ class StableBrowser {
|
|
|
1268
1383
|
let element = await this._locate(selectors, info, _params);
|
|
1269
1384
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1270
1385
|
await this._highlightElements(element);
|
|
1271
|
-
await element.fill(value
|
|
1386
|
+
await element.fill(value);
|
|
1272
1387
|
await element.dispatchEvent("change");
|
|
1273
1388
|
if (enter) {
|
|
1274
1389
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
@@ -1487,7 +1602,7 @@ class StableBrowser {
|
|
|
1487
1602
|
return info;
|
|
1488
1603
|
}
|
|
1489
1604
|
catch (e) {
|
|
1490
|
-
|
|
1605
|
+
await this.closeUnexpectedPopups();
|
|
1491
1606
|
this.logger.error("verify element contains text failed " + info.log);
|
|
1492
1607
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1493
1608
|
info.screenshotPath = screenshotPath;
|
|
@@ -1535,6 +1650,29 @@ class StableBrowser {
|
|
|
1535
1650
|
}
|
|
1536
1651
|
return dataFile;
|
|
1537
1652
|
}
|
|
1653
|
+
async waitForUserInput(message, world = null) {
|
|
1654
|
+
if (!message) {
|
|
1655
|
+
message = "# Wait for user input. Press any key to continue";
|
|
1656
|
+
}
|
|
1657
|
+
else {
|
|
1658
|
+
message = "# Wait for user input. " + message;
|
|
1659
|
+
}
|
|
1660
|
+
message += "\n";
|
|
1661
|
+
const value = await new Promise((resolve) => {
|
|
1662
|
+
const rl = readline.createInterface({
|
|
1663
|
+
input: process.stdin,
|
|
1664
|
+
output: process.stdout,
|
|
1665
|
+
});
|
|
1666
|
+
rl.question(message, (answer) => {
|
|
1667
|
+
rl.close();
|
|
1668
|
+
resolve(answer);
|
|
1669
|
+
});
|
|
1670
|
+
});
|
|
1671
|
+
if (value) {
|
|
1672
|
+
this.logger.info(`{{userInput}} was set to: ${value}`);
|
|
1673
|
+
}
|
|
1674
|
+
this.setTestData({ userInput: value }, world);
|
|
1675
|
+
}
|
|
1538
1676
|
setTestData(testData, world = null) {
|
|
1539
1677
|
if (!testData) {
|
|
1540
1678
|
return;
|
|
@@ -1722,7 +1860,6 @@ class StableBrowser {
|
|
|
1722
1860
|
}
|
|
1723
1861
|
async takeScreenshot(screenshotPath) {
|
|
1724
1862
|
const playContext = this.context.playContext;
|
|
1725
|
-
const client = await playContext.newCDPSession(this.page);
|
|
1726
1863
|
// Using CDP to capture the screenshot
|
|
1727
1864
|
const viewportWidth = Math.max(...(await this.page.evaluate(() => [
|
|
1728
1865
|
document.body.scrollWidth,
|
|
@@ -1732,41 +1869,40 @@ class StableBrowser {
|
|
|
1732
1869
|
document.body.clientWidth,
|
|
1733
1870
|
document.documentElement.clientWidth,
|
|
1734
1871
|
])));
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
}
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
await client.detach();
|
|
1872
|
+
let screenshotBuffer = null;
|
|
1873
|
+
if (this.context.browserName === "chromium") {
|
|
1874
|
+
const client = await playContext.newCDPSession(this.page);
|
|
1875
|
+
const { data } = await client.send("Page.captureScreenshot", {
|
|
1876
|
+
format: "png",
|
|
1877
|
+
// clip: {
|
|
1878
|
+
// x: 0,
|
|
1879
|
+
// y: 0,
|
|
1880
|
+
// width: viewportWidth,
|
|
1881
|
+
// height: viewportHeight,
|
|
1882
|
+
// scale: 1,
|
|
1883
|
+
// },
|
|
1884
|
+
});
|
|
1885
|
+
await client.detach();
|
|
1886
|
+
if (!screenshotPath) {
|
|
1887
|
+
return data;
|
|
1888
|
+
}
|
|
1889
|
+
screenshotBuffer = Buffer.from(data, "base64");
|
|
1890
|
+
}
|
|
1891
|
+
else {
|
|
1892
|
+
screenshotBuffer = await this.page.screenshot();
|
|
1893
|
+
}
|
|
1894
|
+
let image = await Jimp.read(screenshotBuffer);
|
|
1895
|
+
// Get the image dimensions
|
|
1896
|
+
const { width, height } = image.bitmap;
|
|
1897
|
+
const resizeRatio = viewportWidth / width;
|
|
1898
|
+
// Resize the image to fit within the viewport dimensions without enlarging
|
|
1899
|
+
if (width > viewportWidth) {
|
|
1900
|
+
image = image.resize({ w: viewportWidth, h: height * resizeRatio }); // Resize the image while maintaining aspect ratio
|
|
1901
|
+
await image.write(screenshotPath);
|
|
1902
|
+
}
|
|
1903
|
+
else {
|
|
1904
|
+
fs.writeFileSync(screenshotPath, screenshotBuffer);
|
|
1905
|
+
}
|
|
1770
1906
|
}
|
|
1771
1907
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
1772
1908
|
this._validateSelectors(selectors);
|
|
@@ -2574,13 +2710,13 @@ class StableBrowser {
|
|
|
2574
2710
|
}
|
|
2575
2711
|
catch (e) {
|
|
2576
2712
|
if (e.label === "networkidle") {
|
|
2577
|
-
console.log("
|
|
2713
|
+
console.log("waited for the network to be idle timeout");
|
|
2578
2714
|
}
|
|
2579
2715
|
else if (e.label === "load") {
|
|
2580
|
-
console.log("
|
|
2716
|
+
console.log("waited for the load timeout");
|
|
2581
2717
|
}
|
|
2582
2718
|
else if (e.label === "domcontentloaded") {
|
|
2583
|
-
console.log("
|
|
2719
|
+
console.log("waited for the domcontent loaded timeout");
|
|
2584
2720
|
}
|
|
2585
2721
|
console.log(".");
|
|
2586
2722
|
}
|
|
@@ -2723,33 +2859,18 @@ class StableBrowser {
|
|
|
2723
2859
|
}
|
|
2724
2860
|
async scrollIfNeeded(element, info) {
|
|
2725
2861
|
try {
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
if (rect &&
|
|
2729
|
-
rect.top >= 0 &&
|
|
2730
|
-
rect.left >= 0 &&
|
|
2731
|
-
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
|
|
2732
|
-
rect.right <= (window.innerWidth || document.documentElement.clientWidth)) {
|
|
2733
|
-
return false;
|
|
2734
|
-
}
|
|
2735
|
-
else {
|
|
2736
|
-
node.scrollIntoView({
|
|
2737
|
-
behavior: "smooth",
|
|
2738
|
-
block: "center",
|
|
2739
|
-
inline: "center",
|
|
2740
|
-
});
|
|
2741
|
-
return true;
|
|
2742
|
-
}
|
|
2862
|
+
await element.scrollIntoViewIfNeeded({
|
|
2863
|
+
timeout: 2000,
|
|
2743
2864
|
});
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
}
|
|
2865
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
2866
|
+
if (info) {
|
|
2867
|
+
info.box = await element.boundingBox({
|
|
2868
|
+
timeout: 1000,
|
|
2869
|
+
});
|
|
2749
2870
|
}
|
|
2750
2871
|
}
|
|
2751
2872
|
catch (e) {
|
|
2752
|
-
console.log("
|
|
2873
|
+
console.log("#-#");
|
|
2753
2874
|
}
|
|
2754
2875
|
}
|
|
2755
2876
|
_reportToWorld(world, properties) {
|