automation_model 1.0.409-dev → 1.0.409-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 +201 -95
- 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,10 +297,10 @@ 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
305
|
// handle role/name locators
|
|
235
306
|
// locator.selector will be something like: textbox[name="Username"i]
|
|
@@ -240,24 +311,33 @@ class StableBrowser {
|
|
|
240
311
|
const role = match[1];
|
|
241
312
|
const name = match[3];
|
|
242
313
|
const flags = match[4];
|
|
243
|
-
|
|
314
|
+
locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
|
|
244
315
|
}
|
|
245
316
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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}`);
|
|
254
329
|
}
|
|
255
|
-
const newLocator = scope.locator(`${locator.engine}="${selector}"`);
|
|
256
|
-
return newLocator;
|
|
257
330
|
}
|
|
258
|
-
|
|
331
|
+
if (!locatorReturn) {
|
|
332
|
+
console.error(locator);
|
|
333
|
+
throw new Error("Locator undefined");
|
|
334
|
+
}
|
|
335
|
+
return locatorReturn;
|
|
259
336
|
}
|
|
260
337
|
async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
|
|
338
|
+
if (css && css.locator) {
|
|
339
|
+
css = css.locator;
|
|
340
|
+
}
|
|
261
341
|
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, true, _params);
|
|
262
342
|
if (result.elementCount === 0) {
|
|
263
343
|
return;
|
|
@@ -466,6 +546,8 @@ class StableBrowser {
|
|
|
466
546
|
if (result.foundElements.length > 0) {
|
|
467
547
|
let dialogCloseLocator = result.foundElements[0].locator;
|
|
468
548
|
await dialogCloseLocator.click();
|
|
549
|
+
// wait for the dialog to close
|
|
550
|
+
await dialogCloseLocator.waitFor({ state: "hidden" });
|
|
469
551
|
return { rerun: true };
|
|
470
552
|
}
|
|
471
553
|
}
|
|
@@ -474,7 +556,7 @@ class StableBrowser {
|
|
|
474
556
|
}
|
|
475
557
|
async _locate(selectors, info, _params, timeout = 30000) {
|
|
476
558
|
for (let i = 0; i < 3; i++) {
|
|
477
|
-
info.log += "attempt " + i + ":
|
|
559
|
+
info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
|
|
478
560
|
for (let j = 0; j < selectors.locators.length; j++) {
|
|
479
561
|
let selector = selectors.locators[j];
|
|
480
562
|
info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
|
|
@@ -494,9 +576,30 @@ class StableBrowser {
|
|
|
494
576
|
//let arrayMode = Array.isArray(selectors);
|
|
495
577
|
let scope = this.page;
|
|
496
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
|
+
};
|
|
497
595
|
info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
|
|
498
596
|
while (true) {
|
|
499
597
|
let frameFound = false;
|
|
598
|
+
if (selectors.nestFrmLoc) {
|
|
599
|
+
scope = findFrame(selectors.nestFrmLoc, scope);
|
|
600
|
+
frameFound = true;
|
|
601
|
+
break;
|
|
602
|
+
}
|
|
500
603
|
if (selectors.frameLocators) {
|
|
501
604
|
for (let i = 0; i < selectors.frameLocators.length; i++) {
|
|
502
605
|
let frameLocator = selectors.frameLocators[i];
|
|
@@ -660,6 +763,9 @@ class StableBrowser {
|
|
|
660
763
|
async click(selectors, _params, options = {}, world = null) {
|
|
661
764
|
this._validateSelectors(selectors);
|
|
662
765
|
const startTime = Date.now();
|
|
766
|
+
if (options && options.context) {
|
|
767
|
+
selectors.locators[0].text = options.context;
|
|
768
|
+
}
|
|
663
769
|
const info = {};
|
|
664
770
|
info.log = "***** click on " + selectors.element_name + " *****\n";
|
|
665
771
|
info.operation = "click";
|
|
@@ -673,14 +779,14 @@ class StableBrowser {
|
|
|
673
779
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
674
780
|
try {
|
|
675
781
|
await this._highlightElements(element);
|
|
676
|
-
await element.click(
|
|
782
|
+
await element.click();
|
|
677
783
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
678
784
|
}
|
|
679
785
|
catch (e) {
|
|
680
786
|
// await this.closeUnexpectedPopups();
|
|
681
787
|
info.log += "click failed, will try again" + "\n";
|
|
682
788
|
element = await this._locate(selectors, info, _params);
|
|
683
|
-
await element.click
|
|
789
|
+
await element.dispatchEvent("click");
|
|
684
790
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
685
791
|
}
|
|
686
792
|
await this.waitForPageLoad();
|
|
@@ -733,7 +839,7 @@ class StableBrowser {
|
|
|
733
839
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
734
840
|
try {
|
|
735
841
|
await this._highlightElements(element);
|
|
736
|
-
await element.setChecked(checked
|
|
842
|
+
await element.setChecked(checked);
|
|
737
843
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
738
844
|
}
|
|
739
845
|
catch (e) {
|
|
@@ -797,7 +903,7 @@ class StableBrowser {
|
|
|
797
903
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
798
904
|
try {
|
|
799
905
|
await this._highlightElements(element);
|
|
800
|
-
await element.hover(
|
|
906
|
+
await element.hover();
|
|
801
907
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
802
908
|
}
|
|
803
909
|
catch (e) {
|
|
@@ -859,7 +965,7 @@ class StableBrowser {
|
|
|
859
965
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
860
966
|
try {
|
|
861
967
|
await this._highlightElements(element);
|
|
862
|
-
await element.selectOption(values
|
|
968
|
+
await element.selectOption(values);
|
|
863
969
|
}
|
|
864
970
|
catch (e) {
|
|
865
971
|
//await this.closeUnexpectedPopups();
|
|
@@ -1262,7 +1368,7 @@ class StableBrowser {
|
|
|
1262
1368
|
let element = await this._locate(selectors, info, _params);
|
|
1263
1369
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1264
1370
|
await this._highlightElements(element);
|
|
1265
|
-
await element.fill(value
|
|
1371
|
+
await element.fill(value);
|
|
1266
1372
|
await element.dispatchEvent("change");
|
|
1267
1373
|
if (enter) {
|
|
1268
1374
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
@@ -1481,7 +1587,7 @@ class StableBrowser {
|
|
|
1481
1587
|
return info;
|
|
1482
1588
|
}
|
|
1483
1589
|
catch (e) {
|
|
1484
|
-
|
|
1590
|
+
await this.closeUnexpectedPopups();
|
|
1485
1591
|
this.logger.error("verify element contains text failed " + info.log);
|
|
1486
1592
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1487
1593
|
info.screenshotPath = screenshotPath;
|
|
@@ -1529,6 +1635,29 @@ class StableBrowser {
|
|
|
1529
1635
|
}
|
|
1530
1636
|
return dataFile;
|
|
1531
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
|
+
}
|
|
1532
1661
|
setTestData(testData, world = null) {
|
|
1533
1662
|
if (!testData) {
|
|
1534
1663
|
return;
|
|
@@ -1726,40 +1855,32 @@ class StableBrowser {
|
|
|
1726
1855
|
document.body.clientWidth,
|
|
1727
1856
|
document.documentElement.clientWidth,
|
|
1728
1857
|
])));
|
|
1729
|
-
const viewportHeight = Math.max(...(await this.page.evaluate(() => [
|
|
1730
|
-
document.body.scrollHeight,
|
|
1731
|
-
document.documentElement.scrollHeight,
|
|
1732
|
-
document.body.offsetHeight,
|
|
1733
|
-
document.documentElement.offsetHeight,
|
|
1734
|
-
document.body.clientHeight,
|
|
1735
|
-
document.documentElement.clientHeight,
|
|
1736
|
-
])));
|
|
1737
1858
|
const { data } = await client.send("Page.captureScreenshot", {
|
|
1738
1859
|
format: "png",
|
|
1739
|
-
clip: {
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
},
|
|
1860
|
+
// clip: {
|
|
1861
|
+
// x: 0,
|
|
1862
|
+
// y: 0,
|
|
1863
|
+
// width: viewportWidth,
|
|
1864
|
+
// height: viewportHeight,
|
|
1865
|
+
// scale: 1,
|
|
1866
|
+
// },
|
|
1746
1867
|
});
|
|
1747
1868
|
if (!screenshotPath) {
|
|
1748
1869
|
return data;
|
|
1749
1870
|
}
|
|
1750
1871
|
let screenshotBuffer = Buffer.from(data, "base64");
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1872
|
+
let image = await Jimp.read(screenshotBuffer);
|
|
1873
|
+
// Get the image dimensions
|
|
1874
|
+
const { width, height } = image.bitmap;
|
|
1875
|
+
const resizeRatio = viewportWidth / width;
|
|
1876
|
+
// Resize the image to fit within the viewport dimensions without enlarging
|
|
1877
|
+
if (width > viewportWidth) {
|
|
1878
|
+
image = image.resize({ w: viewportWidth, h: height * resizeRatio }); // Resize the image while maintaining aspect ratio
|
|
1879
|
+
await image.write(screenshotPath);
|
|
1880
|
+
}
|
|
1881
|
+
else {
|
|
1882
|
+
fs.writeFileSync(screenshotPath, screenshotBuffer);
|
|
1883
|
+
}
|
|
1763
1884
|
await client.detach();
|
|
1764
1885
|
}
|
|
1765
1886
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
@@ -2568,13 +2689,13 @@ class StableBrowser {
|
|
|
2568
2689
|
}
|
|
2569
2690
|
catch (e) {
|
|
2570
2691
|
if (e.label === "networkidle") {
|
|
2571
|
-
console.log("
|
|
2692
|
+
console.log("waited for the network to be idle timeout");
|
|
2572
2693
|
}
|
|
2573
2694
|
else if (e.label === "load") {
|
|
2574
|
-
console.log("
|
|
2695
|
+
console.log("waited for the load timeout");
|
|
2575
2696
|
}
|
|
2576
2697
|
else if (e.label === "domcontentloaded") {
|
|
2577
|
-
console.log("
|
|
2698
|
+
console.log("waited for the domcontent loaded timeout");
|
|
2578
2699
|
}
|
|
2579
2700
|
console.log(".");
|
|
2580
2701
|
}
|
|
@@ -2717,33 +2838,18 @@ class StableBrowser {
|
|
|
2717
2838
|
}
|
|
2718
2839
|
async scrollIfNeeded(element, info) {
|
|
2719
2840
|
try {
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
if (rect &&
|
|
2723
|
-
rect.top >= 0 &&
|
|
2724
|
-
rect.left >= 0 &&
|
|
2725
|
-
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
|
|
2726
|
-
rect.right <= (window.innerWidth || document.documentElement.clientWidth)) {
|
|
2727
|
-
return false;
|
|
2728
|
-
}
|
|
2729
|
-
else {
|
|
2730
|
-
node.scrollIntoView({
|
|
2731
|
-
behavior: "smooth",
|
|
2732
|
-
block: "center",
|
|
2733
|
-
inline: "center",
|
|
2734
|
-
});
|
|
2735
|
-
return true;
|
|
2736
|
-
}
|
|
2841
|
+
await element.scrollIntoViewIfNeeded({
|
|
2842
|
+
timeout: 2000,
|
|
2737
2843
|
});
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
}
|
|
2844
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
2845
|
+
if (info) {
|
|
2846
|
+
info.box = await element.boundingBox({
|
|
2847
|
+
timeout: 1000,
|
|
2848
|
+
});
|
|
2743
2849
|
}
|
|
2744
2850
|
}
|
|
2745
2851
|
catch (e) {
|
|
2746
|
-
console.log("
|
|
2852
|
+
console.log("#-#");
|
|
2747
2853
|
}
|
|
2748
2854
|
}
|
|
2749
2855
|
_reportToWorld(world, properties) {
|