automation_model 1.0.407-dev → 1.0.407-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 +210 -83
- 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,20 +297,42 @@ 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) {
|
|
245
338
|
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, true, _params);
|
|
@@ -450,6 +543,8 @@ class StableBrowser {
|
|
|
450
543
|
if (result.foundElements.length > 0) {
|
|
451
544
|
let dialogCloseLocator = result.foundElements[0].locator;
|
|
452
545
|
await dialogCloseLocator.click();
|
|
546
|
+
// wait for the dialog to close
|
|
547
|
+
await dialogCloseLocator.waitFor({ state: "hidden" });
|
|
453
548
|
return { rerun: true };
|
|
454
549
|
}
|
|
455
550
|
}
|
|
@@ -458,7 +553,7 @@ class StableBrowser {
|
|
|
458
553
|
}
|
|
459
554
|
async _locate(selectors, info, _params, timeout = 30000) {
|
|
460
555
|
for (let i = 0; i < 3; i++) {
|
|
461
|
-
info.log += "attempt " + i + ":
|
|
556
|
+
info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
|
|
462
557
|
for (let j = 0; j < selectors.locators.length; j++) {
|
|
463
558
|
let selector = selectors.locators[j];
|
|
464
559
|
info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
|
|
@@ -478,9 +573,30 @@ class StableBrowser {
|
|
|
478
573
|
//let arrayMode = Array.isArray(selectors);
|
|
479
574
|
let scope = this.page;
|
|
480
575
|
if (selectors.iframe_src || selectors.frameLocators) {
|
|
576
|
+
const findFrame = (frame, framescope) => {
|
|
577
|
+
for (let i = 0; i < frame.selectors.length; i++) {
|
|
578
|
+
let frameLocator = frame.selectors[i];
|
|
579
|
+
if (frameLocator.css) {
|
|
580
|
+
framescope = framescope.frameLocator(frameLocator.css);
|
|
581
|
+
if (frameLocator.index) {
|
|
582
|
+
framescope = framescope.nth(frameLocator.index);
|
|
583
|
+
}
|
|
584
|
+
break;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
if (frame.children) {
|
|
588
|
+
return findFrame(frame.children, framescope);
|
|
589
|
+
}
|
|
590
|
+
return framescope;
|
|
591
|
+
};
|
|
481
592
|
info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
|
|
482
593
|
while (true) {
|
|
483
594
|
let frameFound = false;
|
|
595
|
+
if (selectors.nestFrmLoc) {
|
|
596
|
+
scope = findFrame(selectors.nestFrmLoc, scope);
|
|
597
|
+
frameFound = true;
|
|
598
|
+
break;
|
|
599
|
+
}
|
|
484
600
|
if (selectors.frameLocators) {
|
|
485
601
|
for (let i = 0; i < selectors.frameLocators.length; i++) {
|
|
486
602
|
let frameLocator = selectors.frameLocators[i];
|
|
@@ -644,6 +760,9 @@ class StableBrowser {
|
|
|
644
760
|
async click(selectors, _params, options = {}, world = null) {
|
|
645
761
|
this._validateSelectors(selectors);
|
|
646
762
|
const startTime = Date.now();
|
|
763
|
+
if (options && options.context) {
|
|
764
|
+
selectors.locators[0].text = options.context;
|
|
765
|
+
}
|
|
647
766
|
const info = {};
|
|
648
767
|
info.log = "***** click on " + selectors.element_name + " *****\n";
|
|
649
768
|
info.operation = "click";
|
|
@@ -657,14 +776,14 @@ class StableBrowser {
|
|
|
657
776
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
658
777
|
try {
|
|
659
778
|
await this._highlightElements(element);
|
|
660
|
-
await element.click(
|
|
779
|
+
await element.click();
|
|
661
780
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
662
781
|
}
|
|
663
782
|
catch (e) {
|
|
664
783
|
// await this.closeUnexpectedPopups();
|
|
665
784
|
info.log += "click failed, will try again" + "\n";
|
|
666
785
|
element = await this._locate(selectors, info, _params);
|
|
667
|
-
await element.click
|
|
786
|
+
await element.dispatchEvent("click");
|
|
668
787
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
669
788
|
}
|
|
670
789
|
await this.waitForPageLoad();
|
|
@@ -717,7 +836,7 @@ class StableBrowser {
|
|
|
717
836
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
718
837
|
try {
|
|
719
838
|
await this._highlightElements(element);
|
|
720
|
-
await element.setChecked(checked
|
|
839
|
+
await element.setChecked(checked);
|
|
721
840
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
722
841
|
}
|
|
723
842
|
catch (e) {
|
|
@@ -781,7 +900,7 @@ class StableBrowser {
|
|
|
781
900
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
782
901
|
try {
|
|
783
902
|
await this._highlightElements(element);
|
|
784
|
-
await element.hover(
|
|
903
|
+
await element.hover();
|
|
785
904
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
786
905
|
}
|
|
787
906
|
catch (e) {
|
|
@@ -843,7 +962,7 @@ class StableBrowser {
|
|
|
843
962
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
844
963
|
try {
|
|
845
964
|
await this._highlightElements(element);
|
|
846
|
-
await element.selectOption(values
|
|
965
|
+
await element.selectOption(values);
|
|
847
966
|
}
|
|
848
967
|
catch (e) {
|
|
849
968
|
//await this.closeUnexpectedPopups();
|
|
@@ -1059,7 +1178,8 @@ class StableBrowser {
|
|
|
1059
1178
|
catch (err) {
|
|
1060
1179
|
//await this.closeUnexpectedPopups();
|
|
1061
1180
|
this.logger.error("setting date time input failed " + JSON.stringify(info));
|
|
1062
|
-
this.logger.info("Trying again")
|
|
1181
|
+
this.logger.info("Trying again");
|
|
1182
|
+
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1063
1183
|
info.screenshotPath = screenshotPath;
|
|
1064
1184
|
Object.assign(err, { info: info });
|
|
1065
1185
|
await element.click();
|
|
@@ -1245,7 +1365,7 @@ class StableBrowser {
|
|
|
1245
1365
|
let element = await this._locate(selectors, info, _params);
|
|
1246
1366
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1247
1367
|
await this._highlightElements(element);
|
|
1248
|
-
await element.fill(value
|
|
1368
|
+
await element.fill(value);
|
|
1249
1369
|
await element.dispatchEvent("change");
|
|
1250
1370
|
if (enter) {
|
|
1251
1371
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
@@ -1464,7 +1584,7 @@ class StableBrowser {
|
|
|
1464
1584
|
return info;
|
|
1465
1585
|
}
|
|
1466
1586
|
catch (e) {
|
|
1467
|
-
|
|
1587
|
+
await this.closeUnexpectedPopups();
|
|
1468
1588
|
this.logger.error("verify element contains text failed " + info.log);
|
|
1469
1589
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1470
1590
|
info.screenshotPath = screenshotPath;
|
|
@@ -1512,6 +1632,29 @@ class StableBrowser {
|
|
|
1512
1632
|
}
|
|
1513
1633
|
return dataFile;
|
|
1514
1634
|
}
|
|
1635
|
+
async waitForUserInput(message, world = null) {
|
|
1636
|
+
if (!message) {
|
|
1637
|
+
message = "# Wait for user input. Press any key to continue";
|
|
1638
|
+
}
|
|
1639
|
+
else {
|
|
1640
|
+
message = "# Wait for user input. " + message;
|
|
1641
|
+
}
|
|
1642
|
+
message += "\n";
|
|
1643
|
+
const value = await new Promise((resolve) => {
|
|
1644
|
+
const rl = readline.createInterface({
|
|
1645
|
+
input: process.stdin,
|
|
1646
|
+
output: process.stdout,
|
|
1647
|
+
});
|
|
1648
|
+
rl.question(message, (answer) => {
|
|
1649
|
+
rl.close();
|
|
1650
|
+
resolve(answer);
|
|
1651
|
+
});
|
|
1652
|
+
});
|
|
1653
|
+
if (value) {
|
|
1654
|
+
this.logger.info(`{{userInput}} was set to: ${value}`);
|
|
1655
|
+
}
|
|
1656
|
+
this.setTestData({ userInput: value }, world);
|
|
1657
|
+
}
|
|
1515
1658
|
setTestData(testData, world = null) {
|
|
1516
1659
|
if (!testData) {
|
|
1517
1660
|
return;
|
|
@@ -1719,30 +1862,29 @@ class StableBrowser {
|
|
|
1719
1862
|
])));
|
|
1720
1863
|
const { data } = await client.send("Page.captureScreenshot", {
|
|
1721
1864
|
format: "png",
|
|
1722
|
-
clip: {
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
},
|
|
1865
|
+
// clip: {
|
|
1866
|
+
// x: 0,
|
|
1867
|
+
// y: 0,
|
|
1868
|
+
// width: viewportWidth,
|
|
1869
|
+
// height: viewportHeight,
|
|
1870
|
+
// scale: 1,
|
|
1871
|
+
// },
|
|
1729
1872
|
});
|
|
1730
1873
|
if (!screenshotPath) {
|
|
1731
1874
|
return data;
|
|
1732
1875
|
}
|
|
1733
1876
|
let screenshotBuffer = Buffer.from(data, "base64");
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
}
|
|
1745
|
-
fs.writeFileSync(screenshotPath, screenshotBuffer);
|
|
1877
|
+
let image = await Jimp.read(screenshotBuffer);
|
|
1878
|
+
// Get the image dimensions
|
|
1879
|
+
const { width, height } = image.bitmap;
|
|
1880
|
+
// Resize the image to fit within the viewport dimensions without enlarging
|
|
1881
|
+
if (width > viewportWidth || height > viewportHeight) {
|
|
1882
|
+
image = image.resize({ w: viewportWidth, h: viewportHeight }); // Resize the image while maintaining aspect ratio
|
|
1883
|
+
await image.write(screenshotPath);
|
|
1884
|
+
}
|
|
1885
|
+
else {
|
|
1886
|
+
fs.writeFileSync(screenshotPath, screenshotBuffer);
|
|
1887
|
+
}
|
|
1746
1888
|
await client.detach();
|
|
1747
1889
|
}
|
|
1748
1890
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
@@ -2551,13 +2693,13 @@ class StableBrowser {
|
|
|
2551
2693
|
}
|
|
2552
2694
|
catch (e) {
|
|
2553
2695
|
if (e.label === "networkidle") {
|
|
2554
|
-
console.log("
|
|
2696
|
+
console.log("waited for the network to be idle timeout");
|
|
2555
2697
|
}
|
|
2556
2698
|
else if (e.label === "load") {
|
|
2557
|
-
console.log("
|
|
2699
|
+
console.log("waited for the load timeout");
|
|
2558
2700
|
}
|
|
2559
2701
|
else if (e.label === "domcontentloaded") {
|
|
2560
|
-
console.log("
|
|
2702
|
+
console.log("waited for the domcontent loaded timeout");
|
|
2561
2703
|
}
|
|
2562
2704
|
console.log(".");
|
|
2563
2705
|
}
|
|
@@ -2700,33 +2842,18 @@ class StableBrowser {
|
|
|
2700
2842
|
}
|
|
2701
2843
|
async scrollIfNeeded(element, info) {
|
|
2702
2844
|
try {
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
if (rect &&
|
|
2706
|
-
rect.top >= 0 &&
|
|
2707
|
-
rect.left >= 0 &&
|
|
2708
|
-
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
|
|
2709
|
-
rect.right <= (window.innerWidth || document.documentElement.clientWidth)) {
|
|
2710
|
-
return false;
|
|
2711
|
-
}
|
|
2712
|
-
else {
|
|
2713
|
-
node.scrollIntoView({
|
|
2714
|
-
behavior: "smooth",
|
|
2715
|
-
block: "center",
|
|
2716
|
-
inline: "center",
|
|
2717
|
-
});
|
|
2718
|
-
return true;
|
|
2719
|
-
}
|
|
2845
|
+
await element.scrollIntoViewIfNeeded({
|
|
2846
|
+
timeout: 2000,
|
|
2720
2847
|
});
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
}
|
|
2848
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
2849
|
+
if (info) {
|
|
2850
|
+
info.box = await element.boundingBox({
|
|
2851
|
+
timeout: 1000,
|
|
2852
|
+
});
|
|
2726
2853
|
}
|
|
2727
2854
|
}
|
|
2728
2855
|
catch (e) {
|
|
2729
|
-
console.log("
|
|
2856
|
+
console.log("#-#");
|
|
2730
2857
|
}
|
|
2731
2858
|
}
|
|
2732
2859
|
_reportToWorld(world, properties) {
|