automation_model 1.0.408-dev → 1.0.408-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 +37 -3
- package/lib/init_browser.js.map +1 -1
- package/lib/stable_browser.d.ts +12 -4
- package/lib/stable_browser.js +211 -82
- 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 +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
|
}
|
|
@@ -215,9 +288,7 @@ class StableBrowser {
|
|
|
215
288
|
}
|
|
216
289
|
_getLocator(locator, scope, _params) {
|
|
217
290
|
locator = this._fixLocatorUsingParams(locator, _params);
|
|
218
|
-
|
|
219
|
-
return scope.locator(locator.selector);
|
|
220
|
-
}
|
|
291
|
+
let locatorReturn;
|
|
221
292
|
if (locator.role) {
|
|
222
293
|
if (locator.role[1].nameReg) {
|
|
223
294
|
locator.role[1].name = reg_parser(locator.role[1].nameReg);
|
|
@@ -226,22 +297,47 @@ class StableBrowser {
|
|
|
226
297
|
// if (locator.role[1].name) {
|
|
227
298
|
// locator.role[1].name = this._fixUsingParams(locator.role[1].name, _params);
|
|
228
299
|
// }
|
|
229
|
-
|
|
300
|
+
locatorReturn = scope.getByRole(locator.role[0], locator.role[1]);
|
|
230
301
|
}
|
|
231
302
|
if (locator.css) {
|
|
232
|
-
|
|
303
|
+
locatorReturn = scope.locator(locator.css);
|
|
233
304
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
305
|
+
// handle role/name locators
|
|
306
|
+
// locator.selector will be something like: textbox[name="Username"i]
|
|
307
|
+
if (locator.engine === "internal:role") {
|
|
308
|
+
// extract the role, name and the i/s flags using regex
|
|
309
|
+
const match = locator.selector.match(/(.*)\[(.*)="(.*)"(.*)\]/);
|
|
310
|
+
if (match) {
|
|
311
|
+
const role = match[1];
|
|
312
|
+
const name = match[3];
|
|
313
|
+
const flags = match[4];
|
|
314
|
+
locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (locator === null || locator === void 0 ? void 0 : locator.engine) {
|
|
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}`);
|
|
238
329
|
}
|
|
239
|
-
const locator = scope.locator(`${locator.engine}="${selector}"`);
|
|
240
|
-
return locator;
|
|
241
330
|
}
|
|
242
|
-
|
|
331
|
+
if (!locatorReturn) {
|
|
332
|
+
console.error(locator);
|
|
333
|
+
throw new Error("Locator undefined");
|
|
334
|
+
}
|
|
335
|
+
return locatorReturn;
|
|
243
336
|
}
|
|
244
337
|
async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
|
|
338
|
+
if (css && css.locator) {
|
|
339
|
+
css = css.locator;
|
|
340
|
+
}
|
|
245
341
|
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, true, _params);
|
|
246
342
|
if (result.elementCount === 0) {
|
|
247
343
|
return;
|
|
@@ -450,6 +546,8 @@ class StableBrowser {
|
|
|
450
546
|
if (result.foundElements.length > 0) {
|
|
451
547
|
let dialogCloseLocator = result.foundElements[0].locator;
|
|
452
548
|
await dialogCloseLocator.click();
|
|
549
|
+
// wait for the dialog to close
|
|
550
|
+
await dialogCloseLocator.waitFor({ state: "hidden" });
|
|
453
551
|
return { rerun: true };
|
|
454
552
|
}
|
|
455
553
|
}
|
|
@@ -458,7 +556,7 @@ class StableBrowser {
|
|
|
458
556
|
}
|
|
459
557
|
async _locate(selectors, info, _params, timeout = 30000) {
|
|
460
558
|
for (let i = 0; i < 3; i++) {
|
|
461
|
-
info.log += "attempt " + i + ":
|
|
559
|
+
info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
|
|
462
560
|
for (let j = 0; j < selectors.locators.length; j++) {
|
|
463
561
|
let selector = selectors.locators[j];
|
|
464
562
|
info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
|
|
@@ -478,9 +576,30 @@ class StableBrowser {
|
|
|
478
576
|
//let arrayMode = Array.isArray(selectors);
|
|
479
577
|
let scope = this.page;
|
|
480
578
|
if (selectors.iframe_src || selectors.frameLocators) {
|
|
579
|
+
const findFrame = (frame, framescope) => {
|
|
580
|
+
for (let i = 0; i < frame.selectors.length; i++) {
|
|
581
|
+
let frameLocator = frame.selectors[i];
|
|
582
|
+
if (frameLocator.css) {
|
|
583
|
+
framescope = framescope.frameLocator(frameLocator.css);
|
|
584
|
+
if (frameLocator.index) {
|
|
585
|
+
framescope = framescope.nth(frameLocator.index);
|
|
586
|
+
}
|
|
587
|
+
break;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
if (frame.children) {
|
|
591
|
+
return findFrame(frame.children, framescope);
|
|
592
|
+
}
|
|
593
|
+
return framescope;
|
|
594
|
+
};
|
|
481
595
|
info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
|
|
482
596
|
while (true) {
|
|
483
597
|
let frameFound = false;
|
|
598
|
+
if (selectors.nestFrmLoc) {
|
|
599
|
+
scope = findFrame(selectors.nestFrmLoc, scope);
|
|
600
|
+
frameFound = true;
|
|
601
|
+
break;
|
|
602
|
+
}
|
|
484
603
|
if (selectors.frameLocators) {
|
|
485
604
|
for (let i = 0; i < selectors.frameLocators.length; i++) {
|
|
486
605
|
let frameLocator = selectors.frameLocators[i];
|
|
@@ -644,6 +763,9 @@ class StableBrowser {
|
|
|
644
763
|
async click(selectors, _params, options = {}, world = null) {
|
|
645
764
|
this._validateSelectors(selectors);
|
|
646
765
|
const startTime = Date.now();
|
|
766
|
+
if (options && options.context) {
|
|
767
|
+
selectors.locators[0].text = options.context;
|
|
768
|
+
}
|
|
647
769
|
const info = {};
|
|
648
770
|
info.log = "***** click on " + selectors.element_name + " *****\n";
|
|
649
771
|
info.operation = "click";
|
|
@@ -657,14 +779,14 @@ class StableBrowser {
|
|
|
657
779
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
658
780
|
try {
|
|
659
781
|
await this._highlightElements(element);
|
|
660
|
-
await element.click(
|
|
782
|
+
await element.click();
|
|
661
783
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
662
784
|
}
|
|
663
785
|
catch (e) {
|
|
664
786
|
// await this.closeUnexpectedPopups();
|
|
665
787
|
info.log += "click failed, will try again" + "\n";
|
|
666
788
|
element = await this._locate(selectors, info, _params);
|
|
667
|
-
await element.click
|
|
789
|
+
await element.dispatchEvent("click");
|
|
668
790
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
669
791
|
}
|
|
670
792
|
await this.waitForPageLoad();
|
|
@@ -717,7 +839,7 @@ class StableBrowser {
|
|
|
717
839
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
718
840
|
try {
|
|
719
841
|
await this._highlightElements(element);
|
|
720
|
-
await element.setChecked(checked
|
|
842
|
+
await element.setChecked(checked);
|
|
721
843
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
722
844
|
}
|
|
723
845
|
catch (e) {
|
|
@@ -781,7 +903,7 @@ class StableBrowser {
|
|
|
781
903
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
782
904
|
try {
|
|
783
905
|
await this._highlightElements(element);
|
|
784
|
-
await element.hover(
|
|
906
|
+
await element.hover();
|
|
785
907
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
786
908
|
}
|
|
787
909
|
catch (e) {
|
|
@@ -843,7 +965,7 @@ class StableBrowser {
|
|
|
843
965
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
844
966
|
try {
|
|
845
967
|
await this._highlightElements(element);
|
|
846
|
-
await element.selectOption(values
|
|
968
|
+
await element.selectOption(values);
|
|
847
969
|
}
|
|
848
970
|
catch (e) {
|
|
849
971
|
//await this.closeUnexpectedPopups();
|
|
@@ -1246,7 +1368,7 @@ class StableBrowser {
|
|
|
1246
1368
|
let element = await this._locate(selectors, info, _params);
|
|
1247
1369
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1248
1370
|
await this._highlightElements(element);
|
|
1249
|
-
await element.fill(value
|
|
1371
|
+
await element.fill(value);
|
|
1250
1372
|
await element.dispatchEvent("change");
|
|
1251
1373
|
if (enter) {
|
|
1252
1374
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
@@ -1465,7 +1587,7 @@ class StableBrowser {
|
|
|
1465
1587
|
return info;
|
|
1466
1588
|
}
|
|
1467
1589
|
catch (e) {
|
|
1468
|
-
|
|
1590
|
+
await this.closeUnexpectedPopups();
|
|
1469
1591
|
this.logger.error("verify element contains text failed " + info.log);
|
|
1470
1592
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1471
1593
|
info.screenshotPath = screenshotPath;
|
|
@@ -1513,6 +1635,29 @@ class StableBrowser {
|
|
|
1513
1635
|
}
|
|
1514
1636
|
return dataFile;
|
|
1515
1637
|
}
|
|
1638
|
+
async waitForUserInput(message, world = null) {
|
|
1639
|
+
if (!message) {
|
|
1640
|
+
message = "# Wait for user input. Press any key to continue";
|
|
1641
|
+
}
|
|
1642
|
+
else {
|
|
1643
|
+
message = "# Wait for user input. " + message;
|
|
1644
|
+
}
|
|
1645
|
+
message += "\n";
|
|
1646
|
+
const value = await new Promise((resolve) => {
|
|
1647
|
+
const rl = readline.createInterface({
|
|
1648
|
+
input: process.stdin,
|
|
1649
|
+
output: process.stdout,
|
|
1650
|
+
});
|
|
1651
|
+
rl.question(message, (answer) => {
|
|
1652
|
+
rl.close();
|
|
1653
|
+
resolve(answer);
|
|
1654
|
+
});
|
|
1655
|
+
});
|
|
1656
|
+
if (value) {
|
|
1657
|
+
this.logger.info(`{{userInput}} was set to: ${value}`);
|
|
1658
|
+
}
|
|
1659
|
+
this.setTestData({ userInput: value }, world);
|
|
1660
|
+
}
|
|
1516
1661
|
setTestData(testData, world = null) {
|
|
1517
1662
|
if (!testData) {
|
|
1518
1663
|
return;
|
|
@@ -1720,30 +1865,29 @@ class StableBrowser {
|
|
|
1720
1865
|
])));
|
|
1721
1866
|
const { data } = await client.send("Page.captureScreenshot", {
|
|
1722
1867
|
format: "png",
|
|
1723
|
-
clip: {
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
},
|
|
1868
|
+
// clip: {
|
|
1869
|
+
// x: 0,
|
|
1870
|
+
// y: 0,
|
|
1871
|
+
// width: viewportWidth,
|
|
1872
|
+
// height: viewportHeight,
|
|
1873
|
+
// scale: 1,
|
|
1874
|
+
// },
|
|
1730
1875
|
});
|
|
1731
1876
|
if (!screenshotPath) {
|
|
1732
1877
|
return data;
|
|
1733
1878
|
}
|
|
1734
1879
|
let screenshotBuffer = Buffer.from(data, "base64");
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
}
|
|
1746
|
-
fs.writeFileSync(screenshotPath, screenshotBuffer);
|
|
1880
|
+
let image = await Jimp.read(screenshotBuffer);
|
|
1881
|
+
// Get the image dimensions
|
|
1882
|
+
const { width, height } = image.bitmap;
|
|
1883
|
+
// Resize the image to fit within the viewport dimensions without enlarging
|
|
1884
|
+
if (width > viewportWidth || height > viewportHeight) {
|
|
1885
|
+
image = image.resize({ w: viewportWidth, h: viewportHeight }); // Resize the image while maintaining aspect ratio
|
|
1886
|
+
await image.write(screenshotPath);
|
|
1887
|
+
}
|
|
1888
|
+
else {
|
|
1889
|
+
fs.writeFileSync(screenshotPath, screenshotBuffer);
|
|
1890
|
+
}
|
|
1747
1891
|
await client.detach();
|
|
1748
1892
|
}
|
|
1749
1893
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
@@ -2552,13 +2696,13 @@ class StableBrowser {
|
|
|
2552
2696
|
}
|
|
2553
2697
|
catch (e) {
|
|
2554
2698
|
if (e.label === "networkidle") {
|
|
2555
|
-
console.log("
|
|
2699
|
+
console.log("waited for the network to be idle timeout");
|
|
2556
2700
|
}
|
|
2557
2701
|
else if (e.label === "load") {
|
|
2558
|
-
console.log("
|
|
2702
|
+
console.log("waited for the load timeout");
|
|
2559
2703
|
}
|
|
2560
2704
|
else if (e.label === "domcontentloaded") {
|
|
2561
|
-
console.log("
|
|
2705
|
+
console.log("waited for the domcontent loaded timeout");
|
|
2562
2706
|
}
|
|
2563
2707
|
console.log(".");
|
|
2564
2708
|
}
|
|
@@ -2701,33 +2845,18 @@ class StableBrowser {
|
|
|
2701
2845
|
}
|
|
2702
2846
|
async scrollIfNeeded(element, info) {
|
|
2703
2847
|
try {
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
if (rect &&
|
|
2707
|
-
rect.top >= 0 &&
|
|
2708
|
-
rect.left >= 0 &&
|
|
2709
|
-
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
|
|
2710
|
-
rect.right <= (window.innerWidth || document.documentElement.clientWidth)) {
|
|
2711
|
-
return false;
|
|
2712
|
-
}
|
|
2713
|
-
else {
|
|
2714
|
-
node.scrollIntoView({
|
|
2715
|
-
behavior: "smooth",
|
|
2716
|
-
block: "center",
|
|
2717
|
-
inline: "center",
|
|
2718
|
-
});
|
|
2719
|
-
return true;
|
|
2720
|
-
}
|
|
2848
|
+
await element.scrollIntoViewIfNeeded({
|
|
2849
|
+
timeout: 2000,
|
|
2721
2850
|
});
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
}
|
|
2851
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
2852
|
+
if (info) {
|
|
2853
|
+
info.box = await element.boundingBox({
|
|
2854
|
+
timeout: 1000,
|
|
2855
|
+
});
|
|
2727
2856
|
}
|
|
2728
2857
|
}
|
|
2729
2858
|
catch (e) {
|
|
2730
|
-
console.log("
|
|
2859
|
+
console.log("#-#");
|
|
2731
2860
|
}
|
|
2732
2861
|
}
|
|
2733
2862
|
_reportToWorld(world, properties) {
|