automation_model 1.0.412-dev → 1.0.412-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 -5
- package/lib/stable_browser.js +225 -106
- 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];
|
|
@@ -660,14 +775,12 @@ class StableBrowser {
|
|
|
660
775
|
}
|
|
661
776
|
return result;
|
|
662
777
|
}
|
|
663
|
-
async contextClick(selectors, text, _params, options = {}, world = null) {
|
|
664
|
-
this._validateSelectors(selectors);
|
|
665
|
-
selectors.locators[0].text = text;
|
|
666
|
-
await this.click(selectors, _params, options, world);
|
|
667
|
-
}
|
|
668
778
|
async click(selectors, _params, options = {}, world = null) {
|
|
669
779
|
this._validateSelectors(selectors);
|
|
670
780
|
const startTime = Date.now();
|
|
781
|
+
if (options && options.context) {
|
|
782
|
+
selectors.locators[0].text = options.context;
|
|
783
|
+
}
|
|
671
784
|
const info = {};
|
|
672
785
|
info.log = "***** click on " + selectors.element_name + " *****\n";
|
|
673
786
|
info.operation = "click";
|
|
@@ -681,14 +794,14 @@ class StableBrowser {
|
|
|
681
794
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
682
795
|
try {
|
|
683
796
|
await this._highlightElements(element);
|
|
684
|
-
await element.click(
|
|
797
|
+
await element.click();
|
|
685
798
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
686
799
|
}
|
|
687
800
|
catch (e) {
|
|
688
801
|
// await this.closeUnexpectedPopups();
|
|
689
802
|
info.log += "click failed, will try again" + "\n";
|
|
690
803
|
element = await this._locate(selectors, info, _params);
|
|
691
|
-
await element.click
|
|
804
|
+
await element.dispatchEvent("click");
|
|
692
805
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
693
806
|
}
|
|
694
807
|
await this.waitForPageLoad();
|
|
@@ -741,7 +854,7 @@ class StableBrowser {
|
|
|
741
854
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
742
855
|
try {
|
|
743
856
|
await this._highlightElements(element);
|
|
744
|
-
await element.setChecked(checked
|
|
857
|
+
await element.setChecked(checked);
|
|
745
858
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
746
859
|
}
|
|
747
860
|
catch (e) {
|
|
@@ -805,7 +918,7 @@ class StableBrowser {
|
|
|
805
918
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
806
919
|
try {
|
|
807
920
|
await this._highlightElements(element);
|
|
808
|
-
await element.hover(
|
|
921
|
+
await element.hover();
|
|
809
922
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
810
923
|
}
|
|
811
924
|
catch (e) {
|
|
@@ -867,7 +980,7 @@ class StableBrowser {
|
|
|
867
980
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
868
981
|
try {
|
|
869
982
|
await this._highlightElements(element);
|
|
870
|
-
await element.selectOption(values
|
|
983
|
+
await element.selectOption(values);
|
|
871
984
|
}
|
|
872
985
|
catch (e) {
|
|
873
986
|
//await this.closeUnexpectedPopups();
|
|
@@ -1270,7 +1383,7 @@ class StableBrowser {
|
|
|
1270
1383
|
let element = await this._locate(selectors, info, _params);
|
|
1271
1384
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1272
1385
|
await this._highlightElements(element);
|
|
1273
|
-
await element.fill(value
|
|
1386
|
+
await element.fill(value);
|
|
1274
1387
|
await element.dispatchEvent("change");
|
|
1275
1388
|
if (enter) {
|
|
1276
1389
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
@@ -1489,7 +1602,7 @@ class StableBrowser {
|
|
|
1489
1602
|
return info;
|
|
1490
1603
|
}
|
|
1491
1604
|
catch (e) {
|
|
1492
|
-
|
|
1605
|
+
await this.closeUnexpectedPopups();
|
|
1493
1606
|
this.logger.error("verify element contains text failed " + info.log);
|
|
1494
1607
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1495
1608
|
info.screenshotPath = screenshotPath;
|
|
@@ -1537,6 +1650,29 @@ class StableBrowser {
|
|
|
1537
1650
|
}
|
|
1538
1651
|
return dataFile;
|
|
1539
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
|
+
}
|
|
1540
1676
|
setTestData(testData, world = null) {
|
|
1541
1677
|
if (!testData) {
|
|
1542
1678
|
return;
|
|
@@ -1724,7 +1860,6 @@ class StableBrowser {
|
|
|
1724
1860
|
}
|
|
1725
1861
|
async takeScreenshot(screenshotPath) {
|
|
1726
1862
|
const playContext = this.context.playContext;
|
|
1727
|
-
const client = await playContext.newCDPSession(this.page);
|
|
1728
1863
|
// Using CDP to capture the screenshot
|
|
1729
1864
|
const viewportWidth = Math.max(...(await this.page.evaluate(() => [
|
|
1730
1865
|
document.body.scrollWidth,
|
|
@@ -1734,41 +1869,40 @@ class StableBrowser {
|
|
|
1734
1869
|
document.body.clientWidth,
|
|
1735
1870
|
document.documentElement.clientWidth,
|
|
1736
1871
|
])));
|
|
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
|
-
|
|
1770
|
-
|
|
1771
|
-
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
|
+
}
|
|
1772
1906
|
}
|
|
1773
1907
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
1774
1908
|
this._validateSelectors(selectors);
|
|
@@ -2576,13 +2710,13 @@ class StableBrowser {
|
|
|
2576
2710
|
}
|
|
2577
2711
|
catch (e) {
|
|
2578
2712
|
if (e.label === "networkidle") {
|
|
2579
|
-
console.log("
|
|
2713
|
+
console.log("waited for the network to be idle timeout");
|
|
2580
2714
|
}
|
|
2581
2715
|
else if (e.label === "load") {
|
|
2582
|
-
console.log("
|
|
2716
|
+
console.log("waited for the load timeout");
|
|
2583
2717
|
}
|
|
2584
2718
|
else if (e.label === "domcontentloaded") {
|
|
2585
|
-
console.log("
|
|
2719
|
+
console.log("waited for the domcontent loaded timeout");
|
|
2586
2720
|
}
|
|
2587
2721
|
console.log(".");
|
|
2588
2722
|
}
|
|
@@ -2725,33 +2859,18 @@ class StableBrowser {
|
|
|
2725
2859
|
}
|
|
2726
2860
|
async scrollIfNeeded(element, info) {
|
|
2727
2861
|
try {
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
if (rect &&
|
|
2731
|
-
rect.top >= 0 &&
|
|
2732
|
-
rect.left >= 0 &&
|
|
2733
|
-
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
|
|
2734
|
-
rect.right <= (window.innerWidth || document.documentElement.clientWidth)) {
|
|
2735
|
-
return false;
|
|
2736
|
-
}
|
|
2737
|
-
else {
|
|
2738
|
-
node.scrollIntoView({
|
|
2739
|
-
behavior: "smooth",
|
|
2740
|
-
block: "center",
|
|
2741
|
-
inline: "center",
|
|
2742
|
-
});
|
|
2743
|
-
return true;
|
|
2744
|
-
}
|
|
2862
|
+
await element.scrollIntoViewIfNeeded({
|
|
2863
|
+
timeout: 2000,
|
|
2745
2864
|
});
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
}
|
|
2865
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
2866
|
+
if (info) {
|
|
2867
|
+
info.box = await element.boundingBox({
|
|
2868
|
+
timeout: 1000,
|
|
2869
|
+
});
|
|
2751
2870
|
}
|
|
2752
2871
|
}
|
|
2753
2872
|
catch (e) {
|
|
2754
|
-
console.log("
|
|
2873
|
+
console.log("#-#");
|
|
2755
2874
|
}
|
|
2756
2875
|
}
|
|
2757
2876
|
_reportToWorld(world, properties) {
|