automation_model 1.0.446-dev → 1.0.446-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.d.ts +42 -1
- package/lib/api.js +183 -7
- package/lib/api.js.map +1 -1
- package/lib/auto_page.js +21 -39
- package/lib/auto_page.js.map +1 -1
- package/lib/axe/axe.mini.js +12 -0
- package/lib/browser_manager.js +19 -9
- package/lib/browser_manager.js.map +1 -1
- package/lib/command_common.d.ts +5 -0
- package/lib/command_common.js +126 -0
- package/lib/command_common.js.map +1 -0
- package/lib/environment.js +5 -3
- package/lib/environment.js.map +1 -1
- package/lib/error-messages.d.ts +6 -0
- package/lib/error-messages.js +182 -0
- package/lib/error-messages.js.map +1 -0
- package/lib/init_browser.d.ts +1 -1
- package/lib/init_browser.js +39 -2
- package/lib/init_browser.js.map +1 -1
- package/lib/locate_element.d.ts +7 -0
- package/lib/locate_element.js +213 -0
- package/lib/locate_element.js.map +1 -0
- package/lib/network.d.ts +3 -0
- package/lib/network.js +144 -0
- package/lib/network.js.map +1 -0
- package/lib/stable_browser.d.ts +25 -20
- package/lib/stable_browser.js +573 -697
- package/lib/stable_browser.js.map +1 -1
- package/lib/test_context.d.ts +2 -0
- package/lib/test_context.js +11 -10
- package/lib/test_context.js.map +1 -1
- package/lib/utils.d.ts +3 -1
- package/lib/utils.js +68 -1
- package/lib/utils.js.map +1 -1
- package/package.json +6 -6
package/lib/stable_browser.js
CHANGED
|
@@ -2,20 +2,22 @@
|
|
|
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";
|
|
11
11
|
//import { closeUnexpectedPopups } from "./popups.js";
|
|
12
12
|
import { getTableCells, getTableData } from "./table_analyze.js";
|
|
13
|
-
import
|
|
14
|
-
import { decrypt } from "./utils.js";
|
|
13
|
+
import { maskValue, replaceWithLocalTestData } from "./utils.js";
|
|
15
14
|
import csv from "csv-parser";
|
|
16
15
|
import { Readable } from "node:stream";
|
|
17
16
|
import readline from "readline";
|
|
18
17
|
import { getContext } from "./init_browser.js";
|
|
18
|
+
import { locate_element } from "./locate_element.js";
|
|
19
|
+
import { _commandError, _commandFinally, _preCommand, _validateSelectors, _screenshot } from "./command_common.js";
|
|
20
|
+
import { registerDownloadEvent, registerNetworkEvents } from "./network.js";
|
|
19
21
|
const Types = {
|
|
20
22
|
CLICK: "click_element",
|
|
21
23
|
NAVIGATE: "navigate",
|
|
@@ -45,15 +47,22 @@ const Types = {
|
|
|
45
47
|
};
|
|
46
48
|
export const apps = {};
|
|
47
49
|
class StableBrowser {
|
|
48
|
-
|
|
50
|
+
browser;
|
|
51
|
+
page;
|
|
52
|
+
logger;
|
|
53
|
+
context;
|
|
54
|
+
world;
|
|
55
|
+
project_path = null;
|
|
56
|
+
webLogFile = null;
|
|
57
|
+
networkLogger = null;
|
|
58
|
+
configuration = null;
|
|
59
|
+
appName = "main";
|
|
60
|
+
constructor(browser, page, logger = null, context = null, world = null) {
|
|
49
61
|
this.browser = browser;
|
|
50
62
|
this.page = page;
|
|
51
63
|
this.logger = logger;
|
|
52
64
|
this.context = context;
|
|
53
|
-
this.
|
|
54
|
-
this.webLogFile = null;
|
|
55
|
-
this.configuration = null;
|
|
56
|
-
this.appName = "main";
|
|
65
|
+
this.world = world;
|
|
57
66
|
if (!this.logger) {
|
|
58
67
|
this.logger = console;
|
|
59
68
|
}
|
|
@@ -78,20 +87,32 @@ class StableBrowser {
|
|
|
78
87
|
catch (e) {
|
|
79
88
|
this.logger.error("unable to read ai_config.json");
|
|
80
89
|
}
|
|
81
|
-
context.pageLoading = { status: false };
|
|
82
|
-
context.pages = [this.page];
|
|
83
90
|
const logFolder = path.join(this.project_path, "logs", "web");
|
|
84
|
-
this.
|
|
85
|
-
this.
|
|
91
|
+
this.world = world;
|
|
92
|
+
context.pages = [this.page];
|
|
93
|
+
context.pageLoading = { status: false };
|
|
94
|
+
this.registerEventListeners(this.context);
|
|
95
|
+
registerNetworkEvents(this.world, this, this.context, this.page);
|
|
96
|
+
registerDownloadEvent(this.page, this.world, this.context);
|
|
86
97
|
}
|
|
87
98
|
registerEventListeners(context) {
|
|
88
|
-
this.registerConsoleLogListener(this.page, context
|
|
89
|
-
this.registerRequestListener();
|
|
99
|
+
this.registerConsoleLogListener(this.page, context);
|
|
100
|
+
this.registerRequestListener(this.page, context, this.webLogFile);
|
|
101
|
+
if (!context.pageLoading) {
|
|
102
|
+
context.pageLoading = { status: false };
|
|
103
|
+
}
|
|
90
104
|
context.playContext.on("page", async function (page) {
|
|
105
|
+
if (this.configuration && this.configuration.closePopups === true) {
|
|
106
|
+
console.log("close unexpected popups");
|
|
107
|
+
await page.close();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
91
110
|
context.pageLoading.status = true;
|
|
92
111
|
this.page = page;
|
|
93
112
|
context.page = page;
|
|
94
113
|
context.pages.push(page);
|
|
114
|
+
registerNetworkEvents(this.world, this, context, this.page);
|
|
115
|
+
registerDownloadEvent(this.page, this.world, context);
|
|
95
116
|
page.on("close", async () => {
|
|
96
117
|
if (this.context && this.context.pages && this.context.pages.length > 1) {
|
|
97
118
|
this.context.pages.pop();
|
|
@@ -121,10 +142,10 @@ class StableBrowser {
|
|
|
121
142
|
if (this.appName === appName) {
|
|
122
143
|
return;
|
|
123
144
|
}
|
|
124
|
-
let
|
|
145
|
+
let navigate = false;
|
|
125
146
|
if (!apps[appName]) {
|
|
126
147
|
let newContext = await getContext(null, false, this.logger, appName, false, this);
|
|
127
|
-
|
|
148
|
+
navigate = true;
|
|
128
149
|
apps[appName] = {
|
|
129
150
|
context: newContext,
|
|
130
151
|
browser: newContext.browser,
|
|
@@ -136,8 +157,7 @@ class StableBrowser {
|
|
|
136
157
|
this._copyContext(apps[appName], this);
|
|
137
158
|
apps[this.appName] = tempContext;
|
|
138
159
|
this.appName = appName;
|
|
139
|
-
if (
|
|
140
|
-
this.registerEventListeners(this.context);
|
|
160
|
+
if (navigate) {
|
|
141
161
|
await this.goto(this.context.environment.baseUrl);
|
|
142
162
|
await this.waitForPageLoad();
|
|
143
163
|
}
|
|
@@ -158,37 +178,63 @@ class StableBrowser {
|
|
|
158
178
|
const fileName = nextIndex + ".json";
|
|
159
179
|
return path.join(logFolder, fileName);
|
|
160
180
|
}
|
|
161
|
-
registerConsoleLogListener(page, context
|
|
181
|
+
registerConsoleLogListener(page, context) {
|
|
162
182
|
if (!this.context.webLogger) {
|
|
163
183
|
this.context.webLogger = [];
|
|
164
184
|
}
|
|
165
185
|
page.on("console", async (msg) => {
|
|
166
|
-
|
|
186
|
+
const obj = {
|
|
167
187
|
type: msg.type(),
|
|
168
188
|
text: msg.text(),
|
|
169
189
|
location: msg.location(),
|
|
170
190
|
time: new Date().toISOString(),
|
|
171
|
-
}
|
|
172
|
-
|
|
191
|
+
};
|
|
192
|
+
this.context.webLogger.push(obj);
|
|
193
|
+
if (msg.type() === "error") {
|
|
194
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
|
|
195
|
+
}
|
|
173
196
|
});
|
|
174
197
|
}
|
|
175
|
-
registerRequestListener() {
|
|
176
|
-
this.
|
|
198
|
+
registerRequestListener(page, context, logFile) {
|
|
199
|
+
if (!this.context.networkLogger) {
|
|
200
|
+
this.context.networkLogger = [];
|
|
201
|
+
}
|
|
202
|
+
page.on("request", async (data) => {
|
|
203
|
+
const startTime = new Date().getTime();
|
|
177
204
|
try {
|
|
178
|
-
const pageUrl = new URL(
|
|
205
|
+
const pageUrl = new URL(page.url());
|
|
179
206
|
const requestUrl = new URL(data.url());
|
|
180
207
|
if (pageUrl.hostname === requestUrl.hostname) {
|
|
181
208
|
const method = data.method();
|
|
182
|
-
if (
|
|
209
|
+
if (["POST", "GET", "PUT", "DELETE", "PATCH"].includes(method)) {
|
|
183
210
|
const token = await data.headerValue("Authorization");
|
|
184
211
|
if (token) {
|
|
185
|
-
|
|
212
|
+
context.authtoken = token;
|
|
186
213
|
}
|
|
187
214
|
}
|
|
188
215
|
}
|
|
216
|
+
const response = await data.response();
|
|
217
|
+
const endTime = new Date().getTime();
|
|
218
|
+
const obj = {
|
|
219
|
+
url: data.url(),
|
|
220
|
+
method: data.method(),
|
|
221
|
+
postData: data.postData(),
|
|
222
|
+
error: data.failure() ? data.failure().errorText : null,
|
|
223
|
+
duration: endTime - startTime,
|
|
224
|
+
startTime,
|
|
225
|
+
};
|
|
226
|
+
context.networkLogger.push(obj);
|
|
227
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
|
|
189
228
|
}
|
|
190
229
|
catch (error) {
|
|
191
230
|
console.error("Error in request listener", error);
|
|
231
|
+
context.networkLogger.push({
|
|
232
|
+
error: "not able to listen",
|
|
233
|
+
message: error.message,
|
|
234
|
+
stack: error.stack,
|
|
235
|
+
time: new Date().toISOString(),
|
|
236
|
+
});
|
|
237
|
+
// await fs.promises.writeFile(logFile, JSON.stringify(context.networkLogger, null, 2));
|
|
192
238
|
}
|
|
193
239
|
});
|
|
194
240
|
}
|
|
@@ -203,20 +249,6 @@ class StableBrowser {
|
|
|
203
249
|
timeout: 60000,
|
|
204
250
|
});
|
|
205
251
|
}
|
|
206
|
-
_validateSelectors(selectors) {
|
|
207
|
-
if (!selectors) {
|
|
208
|
-
throw new Error("selectors is null");
|
|
209
|
-
}
|
|
210
|
-
if (!selectors.locators) {
|
|
211
|
-
throw new Error("selectors.locators is null");
|
|
212
|
-
}
|
|
213
|
-
if (!Array.isArray(selectors.locators)) {
|
|
214
|
-
throw new Error("selectors.locators expected to be array");
|
|
215
|
-
}
|
|
216
|
-
if (selectors.locators.length === 0) {
|
|
217
|
-
throw new Error("selectors.locators expected to be non empty array");
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
252
|
_fixUsingParams(text, _params) {
|
|
221
253
|
if (!_params || typeof text !== "string") {
|
|
222
254
|
return text;
|
|
@@ -284,7 +316,7 @@ class StableBrowser {
|
|
|
284
316
|
locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
|
|
285
317
|
}
|
|
286
318
|
}
|
|
287
|
-
if (locator
|
|
319
|
+
if (locator?.engine) {
|
|
288
320
|
if (locator.engine === "css") {
|
|
289
321
|
locatorReturn = scope.locator(locator.selector);
|
|
290
322
|
}
|
|
@@ -305,7 +337,10 @@ class StableBrowser {
|
|
|
305
337
|
return locatorReturn;
|
|
306
338
|
}
|
|
307
339
|
async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
|
|
308
|
-
|
|
340
|
+
if (css && css.locator) {
|
|
341
|
+
css = css.locator;
|
|
342
|
+
}
|
|
343
|
+
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*:not(script, style, head)", false, false, _params);
|
|
309
344
|
if (result.elementCount === 0) {
|
|
310
345
|
return;
|
|
311
346
|
}
|
|
@@ -320,7 +355,7 @@ class StableBrowser {
|
|
|
320
355
|
}
|
|
321
356
|
async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
|
|
322
357
|
//const stringifyText = JSON.stringify(text);
|
|
323
|
-
return await scope.evaluate(([text, tag, regex, partial]) => {
|
|
358
|
+
return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
|
|
324
359
|
function isParent(parent, child) {
|
|
325
360
|
let currentNode = child.parentNode;
|
|
326
361
|
while (currentNode !== null) {
|
|
@@ -332,6 +367,15 @@ class StableBrowser {
|
|
|
332
367
|
return false;
|
|
333
368
|
}
|
|
334
369
|
document.isParent = isParent;
|
|
370
|
+
function getRegex(str) {
|
|
371
|
+
const match = str.match(/^\/(.*?)\/([gimuy]*)$/);
|
|
372
|
+
if (!match) {
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
let [_, pattern, flags] = match;
|
|
376
|
+
return new RegExp(pattern, flags);
|
|
377
|
+
}
|
|
378
|
+
document.getRegex = getRegex;
|
|
335
379
|
function collectAllShadowDomElements(element, result = []) {
|
|
336
380
|
// Check and add the element if it has a shadow root
|
|
337
381
|
if (element.shadowRoot) {
|
|
@@ -348,7 +392,11 @@ class StableBrowser {
|
|
|
348
392
|
}
|
|
349
393
|
document.collectAllShadowDomElements = collectAllShadowDomElements;
|
|
350
394
|
if (!tag) {
|
|
351
|
-
tag = "
|
|
395
|
+
tag = "*:not(script, style, head)";
|
|
396
|
+
}
|
|
397
|
+
let regexpSearch = document.getRegex(text);
|
|
398
|
+
if (regexpSearch) {
|
|
399
|
+
regex = true;
|
|
352
400
|
}
|
|
353
401
|
let elements = Array.from(document.querySelectorAll(tag));
|
|
354
402
|
let shadowHosts = [];
|
|
@@ -365,7 +413,9 @@ class StableBrowser {
|
|
|
365
413
|
let randomToken = null;
|
|
366
414
|
const foundElements = [];
|
|
367
415
|
if (regex) {
|
|
368
|
-
|
|
416
|
+
if (!regexpSearch) {
|
|
417
|
+
regexpSearch = new RegExp(text, "im");
|
|
418
|
+
}
|
|
369
419
|
for (let i = 0; i < elements.length; i++) {
|
|
370
420
|
const element = elements[i];
|
|
371
421
|
if ((element.innerText && regexpSearch.test(element.innerText)) ||
|
|
@@ -379,8 +429,8 @@ class StableBrowser {
|
|
|
379
429
|
for (let i = 0; i < elements.length; i++) {
|
|
380
430
|
const element = elements[i];
|
|
381
431
|
if (partial) {
|
|
382
|
-
if ((element.innerText && element.innerText.trim().includes(text)) ||
|
|
383
|
-
(element.value && element.value.includes(text))) {
|
|
432
|
+
if ((element.innerText && element.innerText.toLowerCase().trim().includes(text.toLowerCase())) ||
|
|
433
|
+
(element.value && element.value.toLowerCase().includes(text.toLowerCase()))) {
|
|
384
434
|
foundElements.push(element);
|
|
385
435
|
}
|
|
386
436
|
}
|
|
@@ -425,18 +475,29 @@ class StableBrowser {
|
|
|
425
475
|
}
|
|
426
476
|
async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
|
|
427
477
|
let locatorSearch = selectorHierarchy[index];
|
|
478
|
+
try {
|
|
479
|
+
locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
|
|
480
|
+
}
|
|
481
|
+
catch (e) {
|
|
482
|
+
console.error(e);
|
|
483
|
+
}
|
|
428
484
|
//info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
|
|
429
485
|
let locator = null;
|
|
430
486
|
if (locatorSearch.climb && locatorSearch.climb >= 0) {
|
|
431
487
|
let locatorString = await this._locateElmentByTextClimbCss(scope, locatorSearch.text, locatorSearch.climb, locatorSearch.css, _params);
|
|
432
488
|
if (!locatorString) {
|
|
489
|
+
info.failCause.textNotFound = true;
|
|
490
|
+
info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
|
|
433
491
|
return;
|
|
434
492
|
}
|
|
435
493
|
locator = this._getLocator({ css: locatorString }, scope, _params);
|
|
436
494
|
}
|
|
437
495
|
else if (locatorSearch.text) {
|
|
438
|
-
let
|
|
496
|
+
let text = this._fixUsingParams(locatorSearch.text, _params);
|
|
497
|
+
let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
|
|
439
498
|
if (result.elementCount === 0) {
|
|
499
|
+
info.failCause.textNotFound = true;
|
|
500
|
+
info.failCause.lastError = "failed to locate element by text: " + text;
|
|
440
501
|
return;
|
|
441
502
|
}
|
|
442
503
|
locatorSearch.css = "[data-blinq-id='blinq-id-" + result.randomToken + "']";
|
|
@@ -453,6 +514,9 @@ class StableBrowser {
|
|
|
453
514
|
// cssHref = true;
|
|
454
515
|
// }
|
|
455
516
|
let count = await locator.count();
|
|
517
|
+
if (count > 0 && !info.failCause.count) {
|
|
518
|
+
info.failCause.count = count;
|
|
519
|
+
}
|
|
456
520
|
//info.log += "total elements found " + count + "\n";
|
|
457
521
|
//let visibleCount = 0;
|
|
458
522
|
let visibleLocator = null;
|
|
@@ -470,6 +534,8 @@ class StableBrowser {
|
|
|
470
534
|
foundLocators.push(locator.nth(j));
|
|
471
535
|
}
|
|
472
536
|
else {
|
|
537
|
+
info.failCause.visible = visible;
|
|
538
|
+
info.failCause.enabled = enabled;
|
|
473
539
|
if (!info.printMessages) {
|
|
474
540
|
info.printMessages = {};
|
|
475
541
|
}
|
|
@@ -481,6 +547,11 @@ class StableBrowser {
|
|
|
481
547
|
}
|
|
482
548
|
}
|
|
483
549
|
async closeUnexpectedPopups(info, _params) {
|
|
550
|
+
if (!info) {
|
|
551
|
+
info = {};
|
|
552
|
+
info.failCause = {};
|
|
553
|
+
info.log = "";
|
|
554
|
+
}
|
|
484
555
|
if (this.configuration.popupHandlers && this.configuration.popupHandlers.length > 0) {
|
|
485
556
|
if (!info) {
|
|
486
557
|
info = {};
|
|
@@ -521,7 +592,10 @@ class StableBrowser {
|
|
|
521
592
|
}
|
|
522
593
|
return { rerun: false };
|
|
523
594
|
}
|
|
524
|
-
async _locate(selectors, info, _params, timeout
|
|
595
|
+
async _locate(selectors, info, _params, timeout) {
|
|
596
|
+
if (!timeout) {
|
|
597
|
+
timeout = 30000;
|
|
598
|
+
}
|
|
525
599
|
for (let i = 0; i < 3; i++) {
|
|
526
600
|
info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
|
|
527
601
|
for (let j = 0; j < selectors.locators.length; j++) {
|
|
@@ -535,32 +609,46 @@ class StableBrowser {
|
|
|
535
609
|
}
|
|
536
610
|
throw new Error("unable to locate element " + JSON.stringify(selectors));
|
|
537
611
|
}
|
|
538
|
-
async
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
612
|
+
async _findFrameScope(selectors, timeout = 30000, info) {
|
|
613
|
+
if (!info) {
|
|
614
|
+
info = {};
|
|
615
|
+
info.failCause = {};
|
|
616
|
+
info.log = "";
|
|
617
|
+
}
|
|
544
618
|
let scope = this.page;
|
|
619
|
+
if (selectors.frame) {
|
|
620
|
+
return selectors.frame;
|
|
621
|
+
}
|
|
545
622
|
if (selectors.iframe_src || selectors.frameLocators) {
|
|
546
|
-
const findFrame = (frame, framescope) => {
|
|
623
|
+
const findFrame = async (frame, framescope) => {
|
|
547
624
|
for (let i = 0; i < frame.selectors.length; i++) {
|
|
548
625
|
let frameLocator = frame.selectors[i];
|
|
549
626
|
if (frameLocator.css) {
|
|
550
|
-
|
|
551
|
-
|
|
627
|
+
let testframescope = framescope.frameLocator(frameLocator.css);
|
|
628
|
+
if (frameLocator.index) {
|
|
629
|
+
testframescope = framescope.nth(frameLocator.index);
|
|
630
|
+
}
|
|
631
|
+
try {
|
|
632
|
+
await testframescope.owner().evaluateHandle(() => true, null, {
|
|
633
|
+
timeout: 5000,
|
|
634
|
+
});
|
|
635
|
+
framescope = testframescope;
|
|
636
|
+
break;
|
|
637
|
+
}
|
|
638
|
+
catch (error) {
|
|
639
|
+
console.error("frame not found " + frameLocator.css);
|
|
640
|
+
}
|
|
552
641
|
}
|
|
553
642
|
}
|
|
554
643
|
if (frame.children) {
|
|
555
|
-
return findFrame(frame.children, framescope);
|
|
644
|
+
return await findFrame(frame.children, framescope);
|
|
556
645
|
}
|
|
557
646
|
return framescope;
|
|
558
647
|
};
|
|
559
|
-
info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
|
|
560
648
|
while (true) {
|
|
561
649
|
let frameFound = false;
|
|
562
650
|
if (selectors.nestFrmLoc) {
|
|
563
|
-
scope = findFrame(selectors.nestFrmLoc, scope);
|
|
651
|
+
scope = await findFrame(selectors.nestFrmLoc, scope);
|
|
564
652
|
frameFound = true;
|
|
565
653
|
break;
|
|
566
654
|
}
|
|
@@ -580,6 +668,8 @@ class StableBrowser {
|
|
|
580
668
|
if (!scope) {
|
|
581
669
|
info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
|
|
582
670
|
if (performance.now() - startTime > timeout) {
|
|
671
|
+
info.failCause.iframeNotFound = true;
|
|
672
|
+
info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
|
|
583
673
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
584
674
|
}
|
|
585
675
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -589,6 +679,30 @@ class StableBrowser {
|
|
|
589
679
|
}
|
|
590
680
|
}
|
|
591
681
|
}
|
|
682
|
+
if (!scope) {
|
|
683
|
+
scope = this.page;
|
|
684
|
+
}
|
|
685
|
+
return scope;
|
|
686
|
+
}
|
|
687
|
+
async _getDocumentBody(selectors, timeout = 30000, info) {
|
|
688
|
+
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
689
|
+
return scope.evaluate(() => {
|
|
690
|
+
var bodyContent = document.body.innerHTML;
|
|
691
|
+
return bodyContent;
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
695
|
+
if (!info) {
|
|
696
|
+
info = {};
|
|
697
|
+
info.failCause = {};
|
|
698
|
+
info.log = "";
|
|
699
|
+
}
|
|
700
|
+
let highPriorityTimeout = 5000;
|
|
701
|
+
let visibleOnlyTimeout = 6000;
|
|
702
|
+
let startTime = performance.now();
|
|
703
|
+
let locatorsCount = 0;
|
|
704
|
+
//let arrayMode = Array.isArray(selectors);
|
|
705
|
+
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
592
706
|
let selectorsLocators = null;
|
|
593
707
|
selectorsLocators = selectors.locators;
|
|
594
708
|
// group selectors by priority
|
|
@@ -690,6 +804,8 @@ class StableBrowser {
|
|
|
690
804
|
}
|
|
691
805
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
692
806
|
info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
|
|
807
|
+
info.failCause.locatorNotFound = true;
|
|
808
|
+
info.failCause.lastError = "failed to locate unique element";
|
|
693
809
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
694
810
|
}
|
|
695
811
|
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
@@ -721,89 +837,129 @@ class StableBrowser {
|
|
|
721
837
|
});
|
|
722
838
|
result.locatorIndex = i;
|
|
723
839
|
}
|
|
840
|
+
if (foundLocators.length > 1) {
|
|
841
|
+
info.failCause.foundMultiple = true;
|
|
842
|
+
}
|
|
724
843
|
}
|
|
725
844
|
return result;
|
|
726
845
|
}
|
|
727
|
-
async
|
|
728
|
-
this._validateSelectors(selectors);
|
|
846
|
+
async simpleClick(elementDescription, _params, options = {}, world = null) {
|
|
729
847
|
const startTime = Date.now();
|
|
730
|
-
|
|
731
|
-
|
|
848
|
+
let timeout = 30000;
|
|
849
|
+
if (options && options.timeout) {
|
|
850
|
+
timeout = options.timeout;
|
|
732
851
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
852
|
+
while (true) {
|
|
853
|
+
try {
|
|
854
|
+
const result = await locate_element(this.context, elementDescription, "click");
|
|
855
|
+
if (result?.elementNumber >= 0) {
|
|
856
|
+
const selectors = {
|
|
857
|
+
frame: result?.frame,
|
|
858
|
+
locators: [
|
|
859
|
+
{
|
|
860
|
+
css: result?.css,
|
|
861
|
+
},
|
|
862
|
+
],
|
|
863
|
+
};
|
|
864
|
+
await this.click(selectors, _params, options, world);
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
catch (e) {
|
|
869
|
+
if (performance.now() - startTime > timeout) {
|
|
870
|
+
// throw e;
|
|
871
|
+
await _commandError({ text: "simpleClick", operation: "simpleClick", elementDescription, info: {} }, e, this);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
|
|
878
|
+
const startTime = Date.now();
|
|
879
|
+
let timeout = 30000;
|
|
880
|
+
if (options && options.timeout) {
|
|
881
|
+
timeout = options.timeout;
|
|
882
|
+
}
|
|
883
|
+
while (true) {
|
|
884
|
+
try {
|
|
885
|
+
const result = await locate_element(this.context, elementDescription, "fill", value);
|
|
886
|
+
if (result?.elementNumber >= 0) {
|
|
887
|
+
const selectors = {
|
|
888
|
+
frame: result?.frame,
|
|
889
|
+
locators: [
|
|
890
|
+
{
|
|
891
|
+
css: result?.css,
|
|
892
|
+
},
|
|
893
|
+
],
|
|
894
|
+
};
|
|
895
|
+
await this.clickType(selectors, value, false, _params, options, world);
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
catch (e) {
|
|
900
|
+
if (performance.now() - startTime > timeout) {
|
|
901
|
+
// throw e;
|
|
902
|
+
await _commandError({ text: "simpleClickType", operation: "simpleClickType", value, elementDescription, info: {} }, e, this);
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
async click(selectors, _params, options = {}, world = null) {
|
|
909
|
+
const state = {
|
|
910
|
+
selectors,
|
|
911
|
+
_params,
|
|
912
|
+
options,
|
|
913
|
+
world,
|
|
914
|
+
text: "Click element",
|
|
915
|
+
type: Types.CLICK,
|
|
916
|
+
operation: "click",
|
|
917
|
+
log: "***** click on " + selectors.element_name + " *****\n",
|
|
918
|
+
};
|
|
740
919
|
try {
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
920
|
+
await _preCommand(state, this);
|
|
921
|
+
if (state.options && state.options.context) {
|
|
922
|
+
state.selectors.locators[0].text = state.options.context;
|
|
923
|
+
}
|
|
744
924
|
try {
|
|
745
|
-
await
|
|
746
|
-
await element.click();
|
|
925
|
+
await state.element.click();
|
|
747
926
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
748
927
|
}
|
|
749
928
|
catch (e) {
|
|
750
929
|
// await this.closeUnexpectedPopups();
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
await element.dispatchEvent("click");
|
|
930
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
931
|
+
await state.element.dispatchEvent("click");
|
|
754
932
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
755
933
|
}
|
|
756
934
|
await this.waitForPageLoad();
|
|
757
|
-
return info;
|
|
935
|
+
return state.info;
|
|
758
936
|
}
|
|
759
937
|
catch (e) {
|
|
760
|
-
|
|
761
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
762
|
-
info.screenshotPath = screenshotPath;
|
|
763
|
-
Object.assign(e, { info: info });
|
|
764
|
-
error = e;
|
|
765
|
-
throw e;
|
|
938
|
+
await _commandError(state, e, this);
|
|
766
939
|
}
|
|
767
940
|
finally {
|
|
768
|
-
|
|
769
|
-
this._reportToWorld(world, {
|
|
770
|
-
element_name: selectors.element_name,
|
|
771
|
-
type: Types.CLICK,
|
|
772
|
-
text: `Click element`,
|
|
773
|
-
screenshotId,
|
|
774
|
-
result: error
|
|
775
|
-
? {
|
|
776
|
-
status: "FAILED",
|
|
777
|
-
startTime,
|
|
778
|
-
endTime,
|
|
779
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
780
|
-
}
|
|
781
|
-
: {
|
|
782
|
-
status: "PASSED",
|
|
783
|
-
startTime,
|
|
784
|
-
endTime,
|
|
785
|
-
},
|
|
786
|
-
info: info,
|
|
787
|
-
});
|
|
941
|
+
_commandFinally(state, this);
|
|
788
942
|
}
|
|
789
943
|
}
|
|
790
944
|
async setCheck(selectors, checked = true, _params, options = {}, world = null) {
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
945
|
+
const state = {
|
|
946
|
+
selectors,
|
|
947
|
+
_params,
|
|
948
|
+
options,
|
|
949
|
+
world,
|
|
950
|
+
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
951
|
+
text: checked ? `Check element` : `Uncheck element`,
|
|
952
|
+
operation: "setCheck",
|
|
953
|
+
log: "***** check " + selectors.element_name + " *****\n",
|
|
954
|
+
};
|
|
801
955
|
try {
|
|
802
|
-
|
|
803
|
-
|
|
956
|
+
await _preCommand(state, this);
|
|
957
|
+
state.info.checked = checked;
|
|
958
|
+
// let element = await this._locate(selectors, info, _params);
|
|
959
|
+
// ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
804
960
|
try {
|
|
805
|
-
await this._highlightElements(element);
|
|
806
|
-
await element.setChecked(checked);
|
|
961
|
+
// await this._highlightElements(element);
|
|
962
|
+
await state.element.setChecked(checked);
|
|
807
963
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
808
964
|
}
|
|
809
965
|
catch (e) {
|
|
@@ -812,179 +968,108 @@ class StableBrowser {
|
|
|
812
968
|
}
|
|
813
969
|
else {
|
|
814
970
|
//await this.closeUnexpectedPopups();
|
|
815
|
-
info.log += "setCheck failed, will try again" + "\n";
|
|
816
|
-
element = await this._locate(selectors, info, _params);
|
|
817
|
-
await element.setChecked(checked, { timeout: 5000, force: true });
|
|
971
|
+
state.info.log += "setCheck failed, will try again" + "\n";
|
|
972
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
973
|
+
await state.element.setChecked(checked, { timeout: 5000, force: true });
|
|
818
974
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
819
975
|
}
|
|
820
976
|
}
|
|
821
977
|
await this.waitForPageLoad();
|
|
822
|
-
return info;
|
|
978
|
+
return state.info;
|
|
823
979
|
}
|
|
824
980
|
catch (e) {
|
|
825
|
-
|
|
826
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
827
|
-
info.screenshotPath = screenshotPath;
|
|
828
|
-
Object.assign(e, { info: info });
|
|
829
|
-
error = e;
|
|
830
|
-
throw e;
|
|
981
|
+
await _commandError(state, e, this);
|
|
831
982
|
}
|
|
832
983
|
finally {
|
|
833
|
-
|
|
834
|
-
this._reportToWorld(world, {
|
|
835
|
-
element_name: selectors.element_name,
|
|
836
|
-
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
837
|
-
text: checked ? `Check element` : `Uncheck element`,
|
|
838
|
-
screenshotId,
|
|
839
|
-
result: error
|
|
840
|
-
? {
|
|
841
|
-
status: "FAILED",
|
|
842
|
-
startTime,
|
|
843
|
-
endTime,
|
|
844
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
845
|
-
}
|
|
846
|
-
: {
|
|
847
|
-
status: "PASSED",
|
|
848
|
-
startTime,
|
|
849
|
-
endTime,
|
|
850
|
-
},
|
|
851
|
-
info: info,
|
|
852
|
-
});
|
|
984
|
+
_commandFinally(state, this);
|
|
853
985
|
}
|
|
854
986
|
}
|
|
855
987
|
async hover(selectors, _params, options = {}, world = null) {
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
988
|
+
const state = {
|
|
989
|
+
selectors,
|
|
990
|
+
_params,
|
|
991
|
+
options,
|
|
992
|
+
world,
|
|
993
|
+
type: Types.HOVER,
|
|
994
|
+
text: `Hover element`,
|
|
995
|
+
operation: "hover",
|
|
996
|
+
log: "***** hover " + selectors.element_name + " *****\n",
|
|
997
|
+
};
|
|
865
998
|
try {
|
|
866
|
-
|
|
867
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
999
|
+
await _preCommand(state, this);
|
|
868
1000
|
try {
|
|
869
|
-
await
|
|
870
|
-
await element.hover();
|
|
1001
|
+
await state.element.hover();
|
|
871
1002
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
872
1003
|
}
|
|
873
1004
|
catch (e) {
|
|
874
1005
|
//await this.closeUnexpectedPopups();
|
|
875
|
-
info.log += "hover failed, will try again" + "\n";
|
|
876
|
-
element = await this._locate(selectors, info, _params);
|
|
877
|
-
await element.hover({ timeout: 10000 });
|
|
1006
|
+
state.info.log += "hover failed, will try again" + "\n";
|
|
1007
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
1008
|
+
await state.element.hover({ timeout: 10000 });
|
|
878
1009
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
879
1010
|
}
|
|
880
1011
|
await this.waitForPageLoad();
|
|
881
|
-
return info;
|
|
1012
|
+
return state.info;
|
|
882
1013
|
}
|
|
883
1014
|
catch (e) {
|
|
884
|
-
|
|
885
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
886
|
-
info.screenshotPath = screenshotPath;
|
|
887
|
-
Object.assign(e, { info: info });
|
|
888
|
-
error = e;
|
|
889
|
-
throw e;
|
|
1015
|
+
await _commandError(state, e, this);
|
|
890
1016
|
}
|
|
891
1017
|
finally {
|
|
892
|
-
|
|
893
|
-
this._reportToWorld(world, {
|
|
894
|
-
element_name: selectors.element_name,
|
|
895
|
-
type: Types.HOVER,
|
|
896
|
-
text: `Hover element`,
|
|
897
|
-
screenshotId,
|
|
898
|
-
result: error
|
|
899
|
-
? {
|
|
900
|
-
status: "FAILED",
|
|
901
|
-
startTime,
|
|
902
|
-
endTime,
|
|
903
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
904
|
-
}
|
|
905
|
-
: {
|
|
906
|
-
status: "PASSED",
|
|
907
|
-
startTime,
|
|
908
|
-
endTime,
|
|
909
|
-
},
|
|
910
|
-
info: info,
|
|
911
|
-
});
|
|
1018
|
+
_commandFinally(state, this);
|
|
912
1019
|
}
|
|
913
1020
|
}
|
|
914
1021
|
async selectOption(selectors, values, _params = null, options = {}, world = null) {
|
|
915
|
-
this._validateSelectors(selectors);
|
|
916
1022
|
if (!values) {
|
|
917
1023
|
throw new Error("values is null");
|
|
918
1024
|
}
|
|
919
|
-
const
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
1025
|
+
const state = {
|
|
1026
|
+
selectors,
|
|
1027
|
+
_params,
|
|
1028
|
+
options,
|
|
1029
|
+
world,
|
|
1030
|
+
value: values.toString(),
|
|
1031
|
+
type: Types.SELECT,
|
|
1032
|
+
text: `Select option: ${values}`,
|
|
1033
|
+
operation: "selectOption",
|
|
1034
|
+
log: "***** select option " + selectors.element_name + " *****\n",
|
|
1035
|
+
};
|
|
927
1036
|
try {
|
|
928
|
-
|
|
929
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1037
|
+
await _preCommand(state, this);
|
|
930
1038
|
try {
|
|
931
|
-
await
|
|
932
|
-
await element.selectOption(values);
|
|
1039
|
+
await state.element.selectOption(values);
|
|
933
1040
|
}
|
|
934
1041
|
catch (e) {
|
|
935
1042
|
//await this.closeUnexpectedPopups();
|
|
936
|
-
info.log += "selectOption failed, will try force" + "\n";
|
|
937
|
-
await element.selectOption(values, { timeout: 10000, force: true });
|
|
1043
|
+
state.info.log += "selectOption failed, will try force" + "\n";
|
|
1044
|
+
await state.element.selectOption(values, { timeout: 10000, force: true });
|
|
938
1045
|
}
|
|
939
1046
|
await this.waitForPageLoad();
|
|
940
|
-
return info;
|
|
1047
|
+
return state.info;
|
|
941
1048
|
}
|
|
942
1049
|
catch (e) {
|
|
943
|
-
|
|
944
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
945
|
-
info.screenshotPath = screenshotPath;
|
|
946
|
-
Object.assign(e, { info: info });
|
|
947
|
-
this.logger.info("click failed, will try next selector");
|
|
948
|
-
error = e;
|
|
949
|
-
throw e;
|
|
1050
|
+
await _commandError(state, e, this);
|
|
950
1051
|
}
|
|
951
1052
|
finally {
|
|
952
|
-
|
|
953
|
-
this._reportToWorld(world, {
|
|
954
|
-
element_name: selectors.element_name,
|
|
955
|
-
type: Types.SELECT,
|
|
956
|
-
text: `Select option: ${values}`,
|
|
957
|
-
value: values.toString(),
|
|
958
|
-
screenshotId,
|
|
959
|
-
result: error
|
|
960
|
-
? {
|
|
961
|
-
status: "FAILED",
|
|
962
|
-
startTime,
|
|
963
|
-
endTime,
|
|
964
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
965
|
-
}
|
|
966
|
-
: {
|
|
967
|
-
status: "PASSED",
|
|
968
|
-
startTime,
|
|
969
|
-
endTime,
|
|
970
|
-
},
|
|
971
|
-
info: info,
|
|
972
|
-
});
|
|
1053
|
+
_commandFinally(state, this);
|
|
973
1054
|
}
|
|
974
1055
|
}
|
|
975
1056
|
async type(_value, _params = null, options = {}, world = null) {
|
|
976
|
-
const
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1057
|
+
const state = {
|
|
1058
|
+
value: _value,
|
|
1059
|
+
_params,
|
|
1060
|
+
options,
|
|
1061
|
+
world,
|
|
1062
|
+
locate: false,
|
|
1063
|
+
scroll: false,
|
|
1064
|
+
highlight: false,
|
|
1065
|
+
type: Types.TYPE_PRESS,
|
|
1066
|
+
text: `Type value: ${_value}`,
|
|
1067
|
+
operation: "type",
|
|
1068
|
+
log: "",
|
|
1069
|
+
};
|
|
985
1070
|
try {
|
|
986
|
-
|
|
987
|
-
const valueSegment =
|
|
1071
|
+
await _preCommand(state, this);
|
|
1072
|
+
const valueSegment = state.value.split("&&");
|
|
988
1073
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
989
1074
|
if (i > 0) {
|
|
990
1075
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1004,108 +1089,53 @@ class StableBrowser {
|
|
|
1004
1089
|
await this.page.keyboard.type(value);
|
|
1005
1090
|
}
|
|
1006
1091
|
}
|
|
1007
|
-
return info;
|
|
1092
|
+
return state.info;
|
|
1008
1093
|
}
|
|
1009
1094
|
catch (e) {
|
|
1010
|
-
|
|
1011
|
-
this.logger.error("type failed " + info.log);
|
|
1012
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1013
|
-
info.screenshotPath = screenshotPath;
|
|
1014
|
-
Object.assign(e, { info: info });
|
|
1015
|
-
error = e;
|
|
1016
|
-
throw e;
|
|
1095
|
+
await _commandError(state, e, this);
|
|
1017
1096
|
}
|
|
1018
1097
|
finally {
|
|
1019
|
-
|
|
1020
|
-
this._reportToWorld(world, {
|
|
1021
|
-
type: Types.TYPE_PRESS,
|
|
1022
|
-
screenshotId,
|
|
1023
|
-
value: _value,
|
|
1024
|
-
text: `type value: ${_value}`,
|
|
1025
|
-
result: error
|
|
1026
|
-
? {
|
|
1027
|
-
status: "FAILED",
|
|
1028
|
-
startTime,
|
|
1029
|
-
endTime,
|
|
1030
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1031
|
-
}
|
|
1032
|
-
: {
|
|
1033
|
-
status: "PASSED",
|
|
1034
|
-
startTime,
|
|
1035
|
-
endTime,
|
|
1036
|
-
},
|
|
1037
|
-
info: info,
|
|
1038
|
-
});
|
|
1098
|
+
_commandFinally(state, this);
|
|
1039
1099
|
}
|
|
1040
1100
|
}
|
|
1041
1101
|
async setInputValue(selectors, value, _params = null, options = {}, world = null) {
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
let screenshotPath = null;
|
|
1102
|
+
const state = {
|
|
1103
|
+
selectors,
|
|
1104
|
+
_params,
|
|
1105
|
+
value,
|
|
1106
|
+
options,
|
|
1107
|
+
world,
|
|
1108
|
+
type: Types.SET_INPUT,
|
|
1109
|
+
text: `Set input value`,
|
|
1110
|
+
operation: "setInputValue",
|
|
1111
|
+
log: "***** set input value " + selectors.element_name + " *****\n",
|
|
1112
|
+
};
|
|
1054
1113
|
try {
|
|
1055
|
-
|
|
1056
|
-
let
|
|
1057
|
-
await this.scrollIfNeeded(element, info);
|
|
1058
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1059
|
-
await this._highlightElements(element);
|
|
1114
|
+
await _preCommand(state, this);
|
|
1115
|
+
let value = await this._replaceWithLocalData(state.value, this);
|
|
1060
1116
|
try {
|
|
1061
|
-
await element.evaluateHandle((el, value) => {
|
|
1117
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1062
1118
|
el.value = value;
|
|
1063
1119
|
}, value);
|
|
1064
1120
|
}
|
|
1065
1121
|
catch (error) {
|
|
1066
1122
|
this.logger.error("setInputValue failed, will try again");
|
|
1067
|
-
|
|
1068
|
-
info.
|
|
1069
|
-
|
|
1070
|
-
await element.evaluateHandle((el, value) => {
|
|
1123
|
+
await _screenshot(state, this);
|
|
1124
|
+
Object.assign(error, { info: state.info });
|
|
1125
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1071
1126
|
el.value = value;
|
|
1072
1127
|
});
|
|
1073
1128
|
}
|
|
1074
1129
|
}
|
|
1075
1130
|
catch (e) {
|
|
1076
|
-
|
|
1077
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1078
|
-
info.screenshotPath = screenshotPath;
|
|
1079
|
-
Object.assign(e, { info: info });
|
|
1080
|
-
error = e;
|
|
1081
|
-
throw e;
|
|
1131
|
+
await _commandError(state, e, this);
|
|
1082
1132
|
}
|
|
1083
1133
|
finally {
|
|
1084
|
-
|
|
1085
|
-
this._reportToWorld(world, {
|
|
1086
|
-
element_name: selectors.element_name,
|
|
1087
|
-
type: Types.SET_INPUT,
|
|
1088
|
-
text: `Set input value`,
|
|
1089
|
-
value: value,
|
|
1090
|
-
screenshotId,
|
|
1091
|
-
result: error
|
|
1092
|
-
? {
|
|
1093
|
-
status: "FAILED",
|
|
1094
|
-
startTime,
|
|
1095
|
-
endTime,
|
|
1096
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1097
|
-
}
|
|
1098
|
-
: {
|
|
1099
|
-
status: "PASSED",
|
|
1100
|
-
startTime,
|
|
1101
|
-
endTime,
|
|
1102
|
-
},
|
|
1103
|
-
info: info,
|
|
1104
|
-
});
|
|
1134
|
+
_commandFinally(state, this);
|
|
1105
1135
|
}
|
|
1106
1136
|
}
|
|
1107
1137
|
async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
|
|
1108
|
-
|
|
1138
|
+
_validateSelectors(selectors);
|
|
1109
1139
|
const startTime = Date.now();
|
|
1110
1140
|
let error = null;
|
|
1111
1141
|
let screenshotId = null;
|
|
@@ -1171,7 +1201,8 @@ class StableBrowser {
|
|
|
1171
1201
|
}
|
|
1172
1202
|
catch (e) {
|
|
1173
1203
|
error = e;
|
|
1174
|
-
throw e;
|
|
1204
|
+
// throw e;
|
|
1205
|
+
await _commandError({ text: "setDateTime", operation: "setDateTime", selectors, value, info }, e, this);
|
|
1175
1206
|
}
|
|
1176
1207
|
finally {
|
|
1177
1208
|
const endTime = Date.now();
|
|
@@ -1198,32 +1229,32 @@ class StableBrowser {
|
|
|
1198
1229
|
}
|
|
1199
1230
|
}
|
|
1200
1231
|
async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
|
|
1201
|
-
|
|
1202
|
-
const startTime = Date.now();
|
|
1203
|
-
let error = null;
|
|
1204
|
-
let screenshotId = null;
|
|
1205
|
-
let screenshotPath = null;
|
|
1206
|
-
const info = {};
|
|
1207
|
-
info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
|
|
1208
|
-
info.operation = "clickType";
|
|
1209
|
-
info.selectors = selectors;
|
|
1232
|
+
_value = unEscapeString(_value);
|
|
1210
1233
|
const newValue = await this._replaceWithLocalData(_value, world);
|
|
1234
|
+
const state = {
|
|
1235
|
+
selectors,
|
|
1236
|
+
_params,
|
|
1237
|
+
value: newValue,
|
|
1238
|
+
originalValue: _value,
|
|
1239
|
+
options,
|
|
1240
|
+
world,
|
|
1241
|
+
type: Types.FILL,
|
|
1242
|
+
text: `Click type input with value: ${_value}`,
|
|
1243
|
+
operation: "clickType",
|
|
1244
|
+
log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
|
|
1245
|
+
};
|
|
1211
1246
|
if (newValue !== _value) {
|
|
1212
1247
|
//this.logger.info(_value + "=" + newValue);
|
|
1213
1248
|
_value = newValue;
|
|
1214
1249
|
}
|
|
1215
|
-
info.value = _value;
|
|
1216
1250
|
try {
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
await this.scrollIfNeeded(element, info);
|
|
1220
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1221
|
-
await this._highlightElements(element);
|
|
1251
|
+
await _preCommand(state, this);
|
|
1252
|
+
state.info.value = _value;
|
|
1222
1253
|
if (options === null || options === undefined || !options.press) {
|
|
1223
1254
|
try {
|
|
1224
|
-
let currentValue = await element.inputValue();
|
|
1255
|
+
let currentValue = await state.element.inputValue();
|
|
1225
1256
|
if (currentValue) {
|
|
1226
|
-
await element.fill("");
|
|
1257
|
+
await state.element.fill("");
|
|
1227
1258
|
}
|
|
1228
1259
|
}
|
|
1229
1260
|
catch (e) {
|
|
@@ -1232,22 +1263,22 @@ class StableBrowser {
|
|
|
1232
1263
|
}
|
|
1233
1264
|
if (options === null || options === undefined || options.press) {
|
|
1234
1265
|
try {
|
|
1235
|
-
await element.click({ timeout: 5000 });
|
|
1266
|
+
await state.element.click({ timeout: 5000 });
|
|
1236
1267
|
}
|
|
1237
1268
|
catch (e) {
|
|
1238
|
-
await element.dispatchEvent("click");
|
|
1269
|
+
await state.element.dispatchEvent("click");
|
|
1239
1270
|
}
|
|
1240
1271
|
}
|
|
1241
1272
|
else {
|
|
1242
1273
|
try {
|
|
1243
|
-
await element.focus();
|
|
1274
|
+
await state.element.focus();
|
|
1244
1275
|
}
|
|
1245
1276
|
catch (e) {
|
|
1246
|
-
await element.dispatchEvent("focus");
|
|
1277
|
+
await state.element.dispatchEvent("focus");
|
|
1247
1278
|
}
|
|
1248
1279
|
}
|
|
1249
1280
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1250
|
-
const valueSegment =
|
|
1281
|
+
const valueSegment = state.value.split("&&");
|
|
1251
1282
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1252
1283
|
if (i > 0) {
|
|
1253
1284
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1267,13 +1298,14 @@ class StableBrowser {
|
|
|
1267
1298
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1268
1299
|
}
|
|
1269
1300
|
}
|
|
1301
|
+
await _screenshot(state, this);
|
|
1270
1302
|
if (enter === true) {
|
|
1271
1303
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1272
1304
|
await this.page.keyboard.press("Enter");
|
|
1273
1305
|
await this.waitForPageLoad();
|
|
1274
1306
|
}
|
|
1275
1307
|
else if (enter === false) {
|
|
1276
|
-
await element.dispatchEvent("change");
|
|
1308
|
+
await state.element.dispatchEvent("change");
|
|
1277
1309
|
//await this.page.keyboard.press("Tab");
|
|
1278
1310
|
}
|
|
1279
1311
|
else {
|
|
@@ -1282,103 +1314,50 @@ class StableBrowser {
|
|
|
1282
1314
|
await this.waitForPageLoad();
|
|
1283
1315
|
}
|
|
1284
1316
|
}
|
|
1285
|
-
return info;
|
|
1317
|
+
return state.info;
|
|
1286
1318
|
}
|
|
1287
1319
|
catch (e) {
|
|
1288
|
-
|
|
1289
|
-
this.logger.error("fill failed " + JSON.stringify(info));
|
|
1290
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1291
|
-
info.screenshotPath = screenshotPath;
|
|
1292
|
-
Object.assign(e, { info: info });
|
|
1293
|
-
error = e;
|
|
1294
|
-
throw e;
|
|
1320
|
+
await _commandError(state, e, this);
|
|
1295
1321
|
}
|
|
1296
1322
|
finally {
|
|
1297
|
-
|
|
1298
|
-
this._reportToWorld(world, {
|
|
1299
|
-
element_name: selectors.element_name,
|
|
1300
|
-
type: Types.FILL,
|
|
1301
|
-
screenshotId,
|
|
1302
|
-
value: _value,
|
|
1303
|
-
text: `clickType input with value: ${_value}`,
|
|
1304
|
-
result: error
|
|
1305
|
-
? {
|
|
1306
|
-
status: "FAILED",
|
|
1307
|
-
startTime,
|
|
1308
|
-
endTime,
|
|
1309
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1310
|
-
}
|
|
1311
|
-
: {
|
|
1312
|
-
status: "PASSED",
|
|
1313
|
-
startTime,
|
|
1314
|
-
endTime,
|
|
1315
|
-
},
|
|
1316
|
-
info: info,
|
|
1317
|
-
});
|
|
1323
|
+
_commandFinally(state, this);
|
|
1318
1324
|
}
|
|
1319
1325
|
}
|
|
1320
1326
|
async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1327
|
+
const state = {
|
|
1328
|
+
selectors,
|
|
1329
|
+
_params,
|
|
1330
|
+
value: unEscapeString(value),
|
|
1331
|
+
options,
|
|
1332
|
+
world,
|
|
1333
|
+
type: Types.FILL,
|
|
1334
|
+
text: `Fill input with value: ${value}`,
|
|
1335
|
+
operation: "fill",
|
|
1336
|
+
log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
|
|
1337
|
+
};
|
|
1331
1338
|
try {
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
await
|
|
1335
|
-
await element.fill(value);
|
|
1336
|
-
await element.dispatchEvent("change");
|
|
1339
|
+
await _preCommand(state, this);
|
|
1340
|
+
await state.element.fill(value);
|
|
1341
|
+
await state.element.dispatchEvent("change");
|
|
1337
1342
|
if (enter) {
|
|
1338
1343
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1339
1344
|
await this.page.keyboard.press("Enter");
|
|
1340
1345
|
}
|
|
1341
1346
|
await this.waitForPageLoad();
|
|
1342
|
-
return info;
|
|
1347
|
+
return state.info;
|
|
1343
1348
|
}
|
|
1344
1349
|
catch (e) {
|
|
1345
|
-
|
|
1346
|
-
this.logger.error("fill failed " + info.log);
|
|
1347
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1348
|
-
info.screenshotPath = screenshotPath;
|
|
1349
|
-
Object.assign(e, { info: info });
|
|
1350
|
-
error = e;
|
|
1351
|
-
throw e;
|
|
1350
|
+
await _commandError(state, e, this);
|
|
1352
1351
|
}
|
|
1353
1352
|
finally {
|
|
1354
|
-
|
|
1355
|
-
this._reportToWorld(world, {
|
|
1356
|
-
element_name: selectors.element_name,
|
|
1357
|
-
type: Types.FILL,
|
|
1358
|
-
screenshotId,
|
|
1359
|
-
value,
|
|
1360
|
-
text: `Fill input with value: ${value}`,
|
|
1361
|
-
result: error
|
|
1362
|
-
? {
|
|
1363
|
-
status: "FAILED",
|
|
1364
|
-
startTime,
|
|
1365
|
-
endTime,
|
|
1366
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1367
|
-
}
|
|
1368
|
-
: {
|
|
1369
|
-
status: "PASSED",
|
|
1370
|
-
startTime,
|
|
1371
|
-
endTime,
|
|
1372
|
-
},
|
|
1373
|
-
info: info,
|
|
1374
|
-
});
|
|
1353
|
+
_commandFinally(state, this);
|
|
1375
1354
|
}
|
|
1376
1355
|
}
|
|
1377
1356
|
async getText(selectors, _params = null, options = {}, info = {}, world = null) {
|
|
1378
1357
|
return await this._getText(selectors, 0, _params, options, info, world);
|
|
1379
1358
|
}
|
|
1380
1359
|
async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
|
|
1381
|
-
|
|
1360
|
+
_validateSelectors(selectors);
|
|
1382
1361
|
let screenshotId = null;
|
|
1383
1362
|
let screenshotPath = null;
|
|
1384
1363
|
if (!info.log) {
|
|
@@ -1422,165 +1401,124 @@ class StableBrowser {
|
|
|
1422
1401
|
}
|
|
1423
1402
|
}
|
|
1424
1403
|
async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
|
|
1425
|
-
var _a;
|
|
1426
|
-
this._validateSelectors(selectors);
|
|
1427
1404
|
if (!pattern) {
|
|
1428
1405
|
throw new Error("pattern is null");
|
|
1429
1406
|
}
|
|
1430
1407
|
if (!text) {
|
|
1431
1408
|
throw new Error("text is null");
|
|
1432
1409
|
}
|
|
1410
|
+
const state = {
|
|
1411
|
+
selectors,
|
|
1412
|
+
_params,
|
|
1413
|
+
pattern,
|
|
1414
|
+
value: pattern,
|
|
1415
|
+
options,
|
|
1416
|
+
world,
|
|
1417
|
+
locate: false,
|
|
1418
|
+
scroll: false,
|
|
1419
|
+
screenshot: false,
|
|
1420
|
+
highlight: false,
|
|
1421
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1422
|
+
text: `Verify element contains pattern: ${pattern}`,
|
|
1423
|
+
operation: "containsPattern",
|
|
1424
|
+
log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
|
|
1425
|
+
};
|
|
1433
1426
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1434
1427
|
if (newValue !== text) {
|
|
1435
1428
|
this.logger.info(text + "=" + newValue);
|
|
1436
1429
|
text = newValue;
|
|
1437
1430
|
}
|
|
1438
|
-
const startTime = Date.now();
|
|
1439
|
-
let error = null;
|
|
1440
|
-
let screenshotId = null;
|
|
1441
|
-
let screenshotPath = null;
|
|
1442
|
-
const info = {};
|
|
1443
|
-
info.log =
|
|
1444
|
-
"***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
|
|
1445
|
-
info.operation = "containsPattern";
|
|
1446
|
-
info.selectors = selectors;
|
|
1447
|
-
info.value = text;
|
|
1448
|
-
info.pattern = pattern;
|
|
1449
1431
|
let foundObj = null;
|
|
1450
1432
|
try {
|
|
1451
|
-
|
|
1433
|
+
await _preCommand(state, this);
|
|
1434
|
+
state.info.pattern = pattern;
|
|
1435
|
+
foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
|
|
1452
1436
|
if (foundObj && foundObj.element) {
|
|
1453
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1437
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1454
1438
|
}
|
|
1455
|
-
|
|
1439
|
+
await _screenshot(state, this);
|
|
1456
1440
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1457
1441
|
pattern = pattern.replace("{text}", escapedText);
|
|
1458
1442
|
let regex = new RegExp(pattern, "im");
|
|
1459
|
-
if (!regex.test(foundObj
|
|
1460
|
-
info.foundText = foundObj
|
|
1443
|
+
if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
|
|
1444
|
+
state.info.foundText = foundObj?.text;
|
|
1461
1445
|
throw new Error("element doesn't contain text " + text);
|
|
1462
1446
|
}
|
|
1463
|
-
return info;
|
|
1447
|
+
return state.info;
|
|
1464
1448
|
}
|
|
1465
1449
|
catch (e) {
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
|
|
1469
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1470
|
-
info.screenshotPath = screenshotPath;
|
|
1471
|
-
Object.assign(e, { info: info });
|
|
1472
|
-
error = e;
|
|
1473
|
-
throw e;
|
|
1450
|
+
this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
|
|
1451
|
+
await _commandError(state, e, this);
|
|
1474
1452
|
}
|
|
1475
1453
|
finally {
|
|
1476
|
-
|
|
1477
|
-
this._reportToWorld(world, {
|
|
1478
|
-
element_name: selectors.element_name,
|
|
1479
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1480
|
-
value: pattern,
|
|
1481
|
-
text: `Verify element contains pattern: ${pattern}`,
|
|
1482
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1483
|
-
result: error
|
|
1484
|
-
? {
|
|
1485
|
-
status: "FAILED",
|
|
1486
|
-
startTime,
|
|
1487
|
-
endTime,
|
|
1488
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1489
|
-
}
|
|
1490
|
-
: {
|
|
1491
|
-
status: "PASSED",
|
|
1492
|
-
startTime,
|
|
1493
|
-
endTime,
|
|
1494
|
-
},
|
|
1495
|
-
info: info,
|
|
1496
|
-
});
|
|
1454
|
+
_commandFinally(state, this);
|
|
1497
1455
|
}
|
|
1498
1456
|
}
|
|
1499
1457
|
async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
|
|
1500
|
-
|
|
1501
|
-
|
|
1458
|
+
const state = {
|
|
1459
|
+
selectors,
|
|
1460
|
+
_params,
|
|
1461
|
+
value: text,
|
|
1462
|
+
options,
|
|
1463
|
+
world,
|
|
1464
|
+
locate: false,
|
|
1465
|
+
scroll: false,
|
|
1466
|
+
screenshot: false,
|
|
1467
|
+
highlight: false,
|
|
1468
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1469
|
+
text: `Verify element contains text: ${text}`,
|
|
1470
|
+
operation: "containsText",
|
|
1471
|
+
log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
|
|
1472
|
+
};
|
|
1502
1473
|
if (!text) {
|
|
1503
1474
|
throw new Error("text is null");
|
|
1504
1475
|
}
|
|
1505
|
-
|
|
1506
|
-
let error = null;
|
|
1507
|
-
let screenshotId = null;
|
|
1508
|
-
let screenshotPath = null;
|
|
1509
|
-
const info = {};
|
|
1510
|
-
info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
|
|
1511
|
-
info.operation = "containsText";
|
|
1512
|
-
info.selectors = selectors;
|
|
1476
|
+
text = unEscapeString(text);
|
|
1513
1477
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1514
1478
|
if (newValue !== text) {
|
|
1515
1479
|
this.logger.info(text + "=" + newValue);
|
|
1516
1480
|
text = newValue;
|
|
1517
1481
|
}
|
|
1518
|
-
info.value = text;
|
|
1519
1482
|
let foundObj = null;
|
|
1520
1483
|
try {
|
|
1521
|
-
|
|
1484
|
+
await _preCommand(state, this);
|
|
1485
|
+
foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
|
|
1522
1486
|
if (foundObj && foundObj.element) {
|
|
1523
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1487
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1524
1488
|
}
|
|
1525
|
-
|
|
1489
|
+
await _screenshot(state, this);
|
|
1526
1490
|
const dateAlternatives = findDateAlternatives(text);
|
|
1527
1491
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1528
1492
|
if (dateAlternatives.date) {
|
|
1529
1493
|
for (let i = 0; i < dateAlternatives.dates.length; i++) {
|
|
1530
|
-
if (
|
|
1531
|
-
|
|
1532
|
-
return info;
|
|
1494
|
+
if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
|
|
1495
|
+
foundObj?.value?.includes(dateAlternatives.dates[i])) {
|
|
1496
|
+
return state.info;
|
|
1533
1497
|
}
|
|
1534
1498
|
}
|
|
1535
1499
|
throw new Error("element doesn't contain text " + text);
|
|
1536
1500
|
}
|
|
1537
1501
|
else if (numberAlternatives.number) {
|
|
1538
1502
|
for (let i = 0; i < numberAlternatives.numbers.length; i++) {
|
|
1539
|
-
if (
|
|
1540
|
-
|
|
1541
|
-
return info;
|
|
1503
|
+
if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
|
|
1504
|
+
foundObj?.value?.includes(numberAlternatives.numbers[i])) {
|
|
1505
|
+
return state.info;
|
|
1542
1506
|
}
|
|
1543
1507
|
}
|
|
1544
1508
|
throw new Error("element doesn't contain text " + text);
|
|
1545
1509
|
}
|
|
1546
|
-
else if (!
|
|
1547
|
-
info.foundText = foundObj
|
|
1548
|
-
info.value = foundObj
|
|
1510
|
+
else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
|
|
1511
|
+
state.info.foundText = foundObj?.text;
|
|
1512
|
+
state.info.value = foundObj?.value;
|
|
1549
1513
|
throw new Error("element doesn't contain text " + text);
|
|
1550
1514
|
}
|
|
1551
|
-
return info;
|
|
1515
|
+
return state.info;
|
|
1552
1516
|
}
|
|
1553
1517
|
catch (e) {
|
|
1554
|
-
|
|
1555
|
-
this.logger.error("verify element contains text failed " + info.log);
|
|
1556
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1557
|
-
info.screenshotPath = screenshotPath;
|
|
1558
|
-
Object.assign(e, { info: info });
|
|
1559
|
-
error = e;
|
|
1560
|
-
throw e;
|
|
1518
|
+
await _commandError(state, e, this);
|
|
1561
1519
|
}
|
|
1562
1520
|
finally {
|
|
1563
|
-
|
|
1564
|
-
this._reportToWorld(world, {
|
|
1565
|
-
element_name: selectors.element_name,
|
|
1566
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1567
|
-
text: `Verify element contains text: ${text}`,
|
|
1568
|
-
value: text,
|
|
1569
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1570
|
-
result: error
|
|
1571
|
-
? {
|
|
1572
|
-
status: "FAILED",
|
|
1573
|
-
startTime,
|
|
1574
|
-
endTime,
|
|
1575
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1576
|
-
}
|
|
1577
|
-
: {
|
|
1578
|
-
status: "PASSED",
|
|
1579
|
-
startTime,
|
|
1580
|
-
endTime,
|
|
1581
|
-
},
|
|
1582
|
-
info: info,
|
|
1583
|
-
});
|
|
1521
|
+
_commandFinally(state, this);
|
|
1584
1522
|
}
|
|
1585
1523
|
}
|
|
1586
1524
|
_getDataFile(world = null) {
|
|
@@ -1809,7 +1747,6 @@ class StableBrowser {
|
|
|
1809
1747
|
}
|
|
1810
1748
|
async takeScreenshot(screenshotPath) {
|
|
1811
1749
|
const playContext = this.context.playContext;
|
|
1812
|
-
const client = await playContext.newCDPSession(this.page);
|
|
1813
1750
|
// Using CDP to capture the screenshot
|
|
1814
1751
|
const viewportWidth = Math.max(...(await this.page.evaluate(() => [
|
|
1815
1752
|
document.body.scrollWidth,
|
|
@@ -1819,164 +1756,109 @@ class StableBrowser {
|
|
|
1819
1756
|
document.body.clientWidth,
|
|
1820
1757
|
document.documentElement.clientWidth,
|
|
1821
1758
|
])));
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
await client.detach();
|
|
1759
|
+
let screenshotBuffer = null;
|
|
1760
|
+
if (this.context.browserName === "chromium") {
|
|
1761
|
+
const client = await playContext.newCDPSession(this.page);
|
|
1762
|
+
const { data } = await client.send("Page.captureScreenshot", {
|
|
1763
|
+
format: "png",
|
|
1764
|
+
// clip: {
|
|
1765
|
+
// x: 0,
|
|
1766
|
+
// y: 0,
|
|
1767
|
+
// width: viewportWidth,
|
|
1768
|
+
// height: viewportHeight,
|
|
1769
|
+
// scale: 1,
|
|
1770
|
+
// },
|
|
1771
|
+
});
|
|
1772
|
+
await client.detach();
|
|
1773
|
+
if (!screenshotPath) {
|
|
1774
|
+
return data;
|
|
1775
|
+
}
|
|
1776
|
+
screenshotBuffer = Buffer.from(data, "base64");
|
|
1777
|
+
}
|
|
1778
|
+
else {
|
|
1779
|
+
screenshotBuffer = await this.page.screenshot();
|
|
1780
|
+
}
|
|
1781
|
+
let image = await Jimp.read(screenshotBuffer);
|
|
1782
|
+
// Get the image dimensions
|
|
1783
|
+
const { width, height } = image.bitmap;
|
|
1784
|
+
const resizeRatio = viewportWidth / width;
|
|
1785
|
+
// Resize the image to fit within the viewport dimensions without enlarging
|
|
1786
|
+
if (width > viewportWidth) {
|
|
1787
|
+
image = image.resize({ w: viewportWidth, h: height * resizeRatio }); // Resize the image while maintaining aspect ratio
|
|
1788
|
+
await image.write(screenshotPath);
|
|
1789
|
+
}
|
|
1790
|
+
else {
|
|
1791
|
+
fs.writeFileSync(screenshotPath, screenshotBuffer);
|
|
1792
|
+
}
|
|
1857
1793
|
}
|
|
1858
1794
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1795
|
+
const state = {
|
|
1796
|
+
selectors,
|
|
1797
|
+
_params,
|
|
1798
|
+
options,
|
|
1799
|
+
world,
|
|
1800
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1801
|
+
text: `Verify element exists in page`,
|
|
1802
|
+
operation: "verifyElementExistInPage",
|
|
1803
|
+
log: "***** verify element " + selectors.element_name + " exists in page *****\n",
|
|
1804
|
+
};
|
|
1864
1805
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1865
|
-
const info = {};
|
|
1866
|
-
info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
|
|
1867
|
-
info.operation = "verify";
|
|
1868
|
-
info.selectors = selectors;
|
|
1869
1806
|
try {
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
}
|
|
1874
|
-
await this._highlightElements(element);
|
|
1875
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1876
|
-
await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
1877
|
-
return info;
|
|
1807
|
+
await _preCommand(state, this);
|
|
1808
|
+
await expect(state.element).toHaveCount(1, { timeout: 10000 });
|
|
1809
|
+
return state.info;
|
|
1878
1810
|
}
|
|
1879
1811
|
catch (e) {
|
|
1880
|
-
|
|
1881
|
-
this.logger.error("verify failed " + info.log);
|
|
1882
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1883
|
-
info.screenshotPath = screenshotPath;
|
|
1884
|
-
Object.assign(e, { info: info });
|
|
1885
|
-
error = e;
|
|
1886
|
-
throw e;
|
|
1812
|
+
await _commandError(state, e, this);
|
|
1887
1813
|
}
|
|
1888
1814
|
finally {
|
|
1889
|
-
|
|
1890
|
-
this._reportToWorld(world, {
|
|
1891
|
-
element_name: selectors.element_name,
|
|
1892
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1893
|
-
text: "Verify element exists in page",
|
|
1894
|
-
screenshotId,
|
|
1895
|
-
result: error
|
|
1896
|
-
? {
|
|
1897
|
-
status: "FAILED",
|
|
1898
|
-
startTime,
|
|
1899
|
-
endTime,
|
|
1900
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1901
|
-
}
|
|
1902
|
-
: {
|
|
1903
|
-
status: "PASSED",
|
|
1904
|
-
startTime,
|
|
1905
|
-
endTime,
|
|
1906
|
-
},
|
|
1907
|
-
info: info,
|
|
1908
|
-
});
|
|
1815
|
+
_commandFinally(state, this);
|
|
1909
1816
|
}
|
|
1910
1817
|
}
|
|
1911
1818
|
async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1819
|
+
const state = {
|
|
1820
|
+
selectors,
|
|
1821
|
+
_params,
|
|
1822
|
+
attribute,
|
|
1823
|
+
variable,
|
|
1824
|
+
options,
|
|
1825
|
+
world,
|
|
1826
|
+
type: Types.EXTRACT,
|
|
1827
|
+
text: `Extract attribute from element`,
|
|
1828
|
+
operation: "extractAttribute",
|
|
1829
|
+
log: "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1830
|
+
};
|
|
1917
1831
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1918
|
-
const info = {};
|
|
1919
|
-
info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
|
|
1920
|
-
info.operation = "extract";
|
|
1921
|
-
info.selectors = selectors;
|
|
1922
1832
|
try {
|
|
1923
|
-
|
|
1924
|
-
await this._highlightElements(element);
|
|
1925
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1833
|
+
await _preCommand(state, this);
|
|
1926
1834
|
switch (attribute) {
|
|
1927
1835
|
case "inner_text":
|
|
1928
|
-
|
|
1836
|
+
state.value = await state.element.innerText();
|
|
1929
1837
|
break;
|
|
1930
1838
|
case "href":
|
|
1931
|
-
|
|
1839
|
+
state.value = await state.element.getAttribute("href");
|
|
1932
1840
|
break;
|
|
1933
1841
|
case "value":
|
|
1934
|
-
|
|
1842
|
+
state.value = await state.element.inputValue();
|
|
1935
1843
|
break;
|
|
1936
1844
|
default:
|
|
1937
|
-
|
|
1845
|
+
state.value = await state.element.getAttribute(attribute);
|
|
1938
1846
|
break;
|
|
1939
1847
|
}
|
|
1940
|
-
|
|
1848
|
+
state.info.value = state.value;
|
|
1849
|
+
this[variable] = state.value;
|
|
1941
1850
|
if (world) {
|
|
1942
|
-
world[variable] =
|
|
1851
|
+
world[variable] = state.value;
|
|
1943
1852
|
}
|
|
1944
|
-
this.setTestData({ [variable]:
|
|
1945
|
-
this.logger.info("set test data: " + variable + "=" +
|
|
1946
|
-
return info;
|
|
1853
|
+
this.setTestData({ [variable]: state.value }, world);
|
|
1854
|
+
this.logger.info("set test data: " + variable + "=" + state.value);
|
|
1855
|
+
return state.info;
|
|
1947
1856
|
}
|
|
1948
1857
|
catch (e) {
|
|
1949
|
-
|
|
1950
|
-
this.logger.error("extract failed " + info.log);
|
|
1951
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1952
|
-
info.screenshotPath = screenshotPath;
|
|
1953
|
-
Object.assign(e, { info: info });
|
|
1954
|
-
error = e;
|
|
1955
|
-
throw e;
|
|
1858
|
+
await _commandError(state, e, this);
|
|
1956
1859
|
}
|
|
1957
1860
|
finally {
|
|
1958
|
-
|
|
1959
|
-
this._reportToWorld(world, {
|
|
1960
|
-
element_name: selectors.element_name,
|
|
1961
|
-
type: Types.EXTRACT_ATTRIBUTE,
|
|
1962
|
-
variable: variable,
|
|
1963
|
-
value: info.value,
|
|
1964
|
-
text: "Extract attribute from element",
|
|
1965
|
-
screenshotId,
|
|
1966
|
-
result: error
|
|
1967
|
-
? {
|
|
1968
|
-
status: "FAILED",
|
|
1969
|
-
startTime,
|
|
1970
|
-
endTime,
|
|
1971
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1972
|
-
}
|
|
1973
|
-
: {
|
|
1974
|
-
status: "PASSED",
|
|
1975
|
-
startTime,
|
|
1976
|
-
endTime,
|
|
1977
|
-
},
|
|
1978
|
-
info: info,
|
|
1979
|
-
});
|
|
1861
|
+
_commandFinally(state, this);
|
|
1980
1862
|
}
|
|
1981
1863
|
}
|
|
1982
1864
|
async extractEmailData(emailAddress, options, world) {
|
|
@@ -2053,7 +1935,8 @@ class StableBrowser {
|
|
|
2053
1935
|
catch (e) {
|
|
2054
1936
|
errorCount++;
|
|
2055
1937
|
if (errorCount > 3) {
|
|
2056
|
-
throw e;
|
|
1938
|
+
// throw e;
|
|
1939
|
+
await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
|
|
2057
1940
|
}
|
|
2058
1941
|
// ignore
|
|
2059
1942
|
}
|
|
@@ -2164,7 +2047,8 @@ class StableBrowser {
|
|
|
2164
2047
|
info.screenshotPath = screenshotPath;
|
|
2165
2048
|
Object.assign(e, { info: info });
|
|
2166
2049
|
error = e;
|
|
2167
|
-
throw e;
|
|
2050
|
+
// throw e;
|
|
2051
|
+
await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
|
|
2168
2052
|
}
|
|
2169
2053
|
finally {
|
|
2170
2054
|
const endTime = Date.now();
|
|
@@ -2177,7 +2061,7 @@ class StableBrowser {
|
|
|
2177
2061
|
status: "FAILED",
|
|
2178
2062
|
startTime,
|
|
2179
2063
|
endTime,
|
|
2180
|
-
message: error
|
|
2064
|
+
message: error?.message,
|
|
2181
2065
|
}
|
|
2182
2066
|
: {
|
|
2183
2067
|
status: "PASSED",
|
|
@@ -2213,20 +2097,20 @@ class StableBrowser {
|
|
|
2213
2097
|
for (let i = 0; i < frames.length; i++) {
|
|
2214
2098
|
if (dateAlternatives.date) {
|
|
2215
2099
|
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2216
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "
|
|
2100
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2217
2101
|
result.frame = frames[i];
|
|
2218
2102
|
results.push(result);
|
|
2219
2103
|
}
|
|
2220
2104
|
}
|
|
2221
2105
|
else if (numberAlternatives.number) {
|
|
2222
2106
|
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2223
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "
|
|
2107
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2224
2108
|
result.frame = frames[i];
|
|
2225
2109
|
results.push(result);
|
|
2226
2110
|
}
|
|
2227
2111
|
}
|
|
2228
2112
|
else {
|
|
2229
|
-
const result = await this._locateElementByText(frames[i], text, "
|
|
2113
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2230
2114
|
result.frame = frames[i];
|
|
2231
2115
|
results.push(result);
|
|
2232
2116
|
}
|
|
@@ -2262,7 +2146,8 @@ class StableBrowser {
|
|
|
2262
2146
|
info.screenshotPath = screenshotPath;
|
|
2263
2147
|
Object.assign(e, { info: info });
|
|
2264
2148
|
error = e;
|
|
2265
|
-
throw e;
|
|
2149
|
+
// throw e;
|
|
2150
|
+
await _commandError({ text: "verifyTextExistInPage", operation: "verifyTextExistInPage", text, info }, e, this);
|
|
2266
2151
|
}
|
|
2267
2152
|
finally {
|
|
2268
2153
|
const endTime = Date.now();
|
|
@@ -2275,7 +2160,7 @@ class StableBrowser {
|
|
|
2275
2160
|
status: "FAILED",
|
|
2276
2161
|
startTime,
|
|
2277
2162
|
endTime,
|
|
2278
|
-
message: error
|
|
2163
|
+
message: error?.message,
|
|
2279
2164
|
}
|
|
2280
2165
|
: {
|
|
2281
2166
|
status: "PASSED",
|
|
@@ -2346,7 +2231,8 @@ class StableBrowser {
|
|
|
2346
2231
|
info.screenshotPath = screenshotPath;
|
|
2347
2232
|
Object.assign(e, { info: info });
|
|
2348
2233
|
error = e;
|
|
2349
|
-
throw e;
|
|
2234
|
+
// throw e;
|
|
2235
|
+
await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
|
|
2350
2236
|
}
|
|
2351
2237
|
finally {
|
|
2352
2238
|
const endTime = Date.now();
|
|
@@ -2359,7 +2245,7 @@ class StableBrowser {
|
|
|
2359
2245
|
status: "FAILED",
|
|
2360
2246
|
startTime,
|
|
2361
2247
|
endTime,
|
|
2362
|
-
message: error
|
|
2248
|
+
message: error?.message,
|
|
2363
2249
|
}
|
|
2364
2250
|
: {
|
|
2365
2251
|
status: "PASSED",
|
|
@@ -2391,7 +2277,7 @@ class StableBrowser {
|
|
|
2391
2277
|
this.logger.info("Table data verified");
|
|
2392
2278
|
}
|
|
2393
2279
|
async getTableData(selectors, _params = null, options = {}, world = null) {
|
|
2394
|
-
|
|
2280
|
+
_validateSelectors(selectors);
|
|
2395
2281
|
const startTime = Date.now();
|
|
2396
2282
|
let error = null;
|
|
2397
2283
|
let screenshotId = null;
|
|
@@ -2413,7 +2299,8 @@ class StableBrowser {
|
|
|
2413
2299
|
info.screenshotPath = screenshotPath;
|
|
2414
2300
|
Object.assign(e, { info: info });
|
|
2415
2301
|
error = e;
|
|
2416
|
-
throw e;
|
|
2302
|
+
// throw e;
|
|
2303
|
+
await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
|
|
2417
2304
|
}
|
|
2418
2305
|
finally {
|
|
2419
2306
|
const endTime = Date.now();
|
|
@@ -2427,7 +2314,7 @@ class StableBrowser {
|
|
|
2427
2314
|
status: "FAILED",
|
|
2428
2315
|
startTime,
|
|
2429
2316
|
endTime,
|
|
2430
|
-
message: error
|
|
2317
|
+
message: error?.message,
|
|
2431
2318
|
}
|
|
2432
2319
|
: {
|
|
2433
2320
|
status: "PASSED",
|
|
@@ -2439,7 +2326,7 @@ class StableBrowser {
|
|
|
2439
2326
|
}
|
|
2440
2327
|
}
|
|
2441
2328
|
async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
|
|
2442
|
-
|
|
2329
|
+
_validateSelectors(selectors);
|
|
2443
2330
|
if (!query) {
|
|
2444
2331
|
throw new Error("query is null");
|
|
2445
2332
|
}
|
|
@@ -2578,7 +2465,8 @@ class StableBrowser {
|
|
|
2578
2465
|
info.screenshotPath = screenshotPath;
|
|
2579
2466
|
Object.assign(e, { info: info });
|
|
2580
2467
|
error = e;
|
|
2581
|
-
throw e;
|
|
2468
|
+
// throw e;
|
|
2469
|
+
await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
|
|
2582
2470
|
}
|
|
2583
2471
|
finally {
|
|
2584
2472
|
const endTime = Date.now();
|
|
@@ -2592,7 +2480,7 @@ class StableBrowser {
|
|
|
2592
2480
|
status: "FAILED",
|
|
2593
2481
|
startTime,
|
|
2594
2482
|
endTime,
|
|
2595
|
-
message: error
|
|
2483
|
+
message: error?.message,
|
|
2596
2484
|
}
|
|
2597
2485
|
: {
|
|
2598
2486
|
status: "PASSED",
|
|
@@ -2604,27 +2492,7 @@ class StableBrowser {
|
|
|
2604
2492
|
}
|
|
2605
2493
|
}
|
|
2606
2494
|
async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
|
|
2607
|
-
|
|
2608
|
-
return value;
|
|
2609
|
-
}
|
|
2610
|
-
// find all the accurance of {{(.*?)}} and replace with the value
|
|
2611
|
-
let regex = /{{(.*?)}}/g;
|
|
2612
|
-
let matches = value.match(regex);
|
|
2613
|
-
if (matches) {
|
|
2614
|
-
const testData = this.getTestData(world);
|
|
2615
|
-
for (let i = 0; i < matches.length; i++) {
|
|
2616
|
-
let match = matches[i];
|
|
2617
|
-
let key = match.substring(2, match.length - 2);
|
|
2618
|
-
let newValue = objectPath.get(testData, key, null);
|
|
2619
|
-
if (newValue !== null) {
|
|
2620
|
-
value = value.replace(match, newValue);
|
|
2621
|
-
}
|
|
2622
|
-
}
|
|
2623
|
-
}
|
|
2624
|
-
if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
|
|
2625
|
-
return await decrypt(value, null, totpWait);
|
|
2626
|
-
}
|
|
2627
|
-
return value;
|
|
2495
|
+
return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
|
|
2628
2496
|
}
|
|
2629
2497
|
_getLoadTimeout(options) {
|
|
2630
2498
|
let timeout = 15000;
|
|
@@ -2684,7 +2552,7 @@ class StableBrowser {
|
|
|
2684
2552
|
status: "FAILED",
|
|
2685
2553
|
startTime,
|
|
2686
2554
|
endTime,
|
|
2687
|
-
message: error
|
|
2555
|
+
message: error?.message,
|
|
2688
2556
|
}
|
|
2689
2557
|
: {
|
|
2690
2558
|
status: "PASSED",
|
|
@@ -2705,6 +2573,7 @@ class StableBrowser {
|
|
|
2705
2573
|
}
|
|
2706
2574
|
catch (e) {
|
|
2707
2575
|
console.log(".");
|
|
2576
|
+
await _commandError({ text: "closePage", operation: "closePage", info }, e, this);
|
|
2708
2577
|
}
|
|
2709
2578
|
finally {
|
|
2710
2579
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
@@ -2719,7 +2588,7 @@ class StableBrowser {
|
|
|
2719
2588
|
status: "FAILED",
|
|
2720
2589
|
startTime,
|
|
2721
2590
|
endTime,
|
|
2722
|
-
message: error
|
|
2591
|
+
message: error?.message,
|
|
2723
2592
|
}
|
|
2724
2593
|
: {
|
|
2725
2594
|
status: "PASSED",
|
|
@@ -2747,6 +2616,7 @@ class StableBrowser {
|
|
|
2747
2616
|
}
|
|
2748
2617
|
catch (e) {
|
|
2749
2618
|
console.log(".");
|
|
2619
|
+
await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
|
|
2750
2620
|
}
|
|
2751
2621
|
finally {
|
|
2752
2622
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
@@ -2761,7 +2631,7 @@ class StableBrowser {
|
|
|
2761
2631
|
status: "FAILED",
|
|
2762
2632
|
startTime,
|
|
2763
2633
|
endTime,
|
|
2764
|
-
message: error
|
|
2634
|
+
message: error?.message,
|
|
2765
2635
|
}
|
|
2766
2636
|
: {
|
|
2767
2637
|
status: "PASSED",
|
|
@@ -2783,6 +2653,7 @@ class StableBrowser {
|
|
|
2783
2653
|
}
|
|
2784
2654
|
catch (e) {
|
|
2785
2655
|
console.log(".");
|
|
2656
|
+
await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
|
|
2786
2657
|
}
|
|
2787
2658
|
finally {
|
|
2788
2659
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
@@ -2797,7 +2668,7 @@ class StableBrowser {
|
|
|
2797
2668
|
status: "FAILED",
|
|
2798
2669
|
startTime,
|
|
2799
2670
|
endTime,
|
|
2800
|
-
message: error
|
|
2671
|
+
message: error?.message,
|
|
2801
2672
|
}
|
|
2802
2673
|
: {
|
|
2803
2674
|
status: "PASSED",
|
|
@@ -2982,5 +2853,10 @@ const KEYBOARD_EVENTS = [
|
|
|
2982
2853
|
"TVAntennaCable",
|
|
2983
2854
|
"TVAudioDescription",
|
|
2984
2855
|
];
|
|
2856
|
+
function unEscapeString(str) {
|
|
2857
|
+
const placeholder = "__NEWLINE__";
|
|
2858
|
+
str = str.replace(new RegExp(placeholder, "g"), "\n");
|
|
2859
|
+
return str;
|
|
2860
|
+
}
|
|
2985
2861
|
export { StableBrowser };
|
|
2986
2862
|
//# sourceMappingURL=stable_browser.js.map
|