automation_model 1.0.447-dev → 1.0.447-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 +131 -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 +622 -811
- 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,134 +1089,76 @@ 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
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1138
|
+
const state = {
|
|
1139
|
+
selectors,
|
|
1140
|
+
_params,
|
|
1141
|
+
value: await this._replaceWithLocalData(value, this),
|
|
1142
|
+
options,
|
|
1143
|
+
world,
|
|
1144
|
+
type: Types.SET_DATE_TIME,
|
|
1145
|
+
text: `Set date time value: ${value}`,
|
|
1146
|
+
operation: "setDateTime",
|
|
1147
|
+
log: "***** set date time value " + selectors.element_name + " *****\n",
|
|
1148
|
+
throwError: false,
|
|
1149
|
+
};
|
|
1118
1150
|
try {
|
|
1119
|
-
|
|
1120
|
-
let element = await this._locate(selectors, info, _params);
|
|
1121
|
-
//insert red border around the element
|
|
1122
|
-
await this.scrollIfNeeded(element, info);
|
|
1123
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1124
|
-
await this._highlightElements(element);
|
|
1151
|
+
await _preCommand(state, this);
|
|
1125
1152
|
try {
|
|
1126
|
-
await element.click();
|
|
1153
|
+
await state.element.click();
|
|
1127
1154
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1128
1155
|
if (format) {
|
|
1129
|
-
value = dayjs(value).format(format);
|
|
1130
|
-
await element.fill(value);
|
|
1156
|
+
state.value = dayjs(state.value).format(format);
|
|
1157
|
+
await state.element.fill(state.value);
|
|
1131
1158
|
}
|
|
1132
1159
|
else {
|
|
1133
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1134
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1160
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1161
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1135
1162
|
el.value = ""; // clear input
|
|
1136
1163
|
el.value = dateTimeValue;
|
|
1137
1164
|
}, dateTimeValue);
|
|
@@ -1144,20 +1171,19 @@ class StableBrowser {
|
|
|
1144
1171
|
}
|
|
1145
1172
|
catch (err) {
|
|
1146
1173
|
//await this.closeUnexpectedPopups();
|
|
1147
|
-
this.logger.error("setting date time input failed " + JSON.stringify(info));
|
|
1174
|
+
this.logger.error("setting date time input failed " + JSON.stringify(state.info));
|
|
1148
1175
|
this.logger.info("Trying again");
|
|
1149
|
-
|
|
1150
|
-
info.
|
|
1151
|
-
Object.assign(err, { info: info });
|
|
1176
|
+
await _screenshot(state, this);
|
|
1177
|
+
Object.assign(err, { info: state.info });
|
|
1152
1178
|
await element.click();
|
|
1153
1179
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1154
1180
|
if (format) {
|
|
1155
|
-
value = dayjs(value).format(format);
|
|
1156
|
-
await element.fill(value);
|
|
1181
|
+
state.value = dayjs(state.value).format(format);
|
|
1182
|
+
await state.element.fill(state.value);
|
|
1157
1183
|
}
|
|
1158
1184
|
else {
|
|
1159
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1160
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1185
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1186
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1161
1187
|
el.value = ""; // clear input
|
|
1162
1188
|
el.value = dateTimeValue;
|
|
1163
1189
|
}, dateTimeValue);
|
|
@@ -1170,61 +1196,39 @@ class StableBrowser {
|
|
|
1170
1196
|
}
|
|
1171
1197
|
}
|
|
1172
1198
|
catch (e) {
|
|
1173
|
-
|
|
1174
|
-
throw e;
|
|
1199
|
+
await _commandError(state, e, this);
|
|
1175
1200
|
}
|
|
1176
1201
|
finally {
|
|
1177
|
-
|
|
1178
|
-
this._reportToWorld(world, {
|
|
1179
|
-
element_name: selectors.element_name,
|
|
1180
|
-
type: Types.SET_DATE_TIME,
|
|
1181
|
-
screenshotId,
|
|
1182
|
-
value: value,
|
|
1183
|
-
text: `setDateTime input with value: ${value}`,
|
|
1184
|
-
result: error
|
|
1185
|
-
? {
|
|
1186
|
-
status: "FAILED",
|
|
1187
|
-
startTime,
|
|
1188
|
-
endTime,
|
|
1189
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1190
|
-
}
|
|
1191
|
-
: {
|
|
1192
|
-
status: "PASSED",
|
|
1193
|
-
startTime,
|
|
1194
|
-
endTime,
|
|
1195
|
-
},
|
|
1196
|
-
info: info,
|
|
1197
|
-
});
|
|
1202
|
+
_commandFinally(state, this);
|
|
1198
1203
|
}
|
|
1199
1204
|
}
|
|
1200
1205
|
async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
|
|
1201
1206
|
_value = unEscapeString(_value);
|
|
1202
|
-
this._validateSelectors(selectors);
|
|
1203
|
-
const startTime = Date.now();
|
|
1204
|
-
let error = null;
|
|
1205
|
-
let screenshotId = null;
|
|
1206
|
-
let screenshotPath = null;
|
|
1207
|
-
const info = {};
|
|
1208
|
-
info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
|
|
1209
|
-
info.operation = "clickType";
|
|
1210
|
-
info.selectors = selectors;
|
|
1211
1207
|
const newValue = await this._replaceWithLocalData(_value, world);
|
|
1208
|
+
const state = {
|
|
1209
|
+
selectors,
|
|
1210
|
+
_params,
|
|
1211
|
+
value: newValue,
|
|
1212
|
+
originalValue: _value,
|
|
1213
|
+
options,
|
|
1214
|
+
world,
|
|
1215
|
+
type: Types.FILL,
|
|
1216
|
+
text: `Click type input with value: ${_value}`,
|
|
1217
|
+
operation: "clickType",
|
|
1218
|
+
log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
|
|
1219
|
+
};
|
|
1212
1220
|
if (newValue !== _value) {
|
|
1213
1221
|
//this.logger.info(_value + "=" + newValue);
|
|
1214
1222
|
_value = newValue;
|
|
1215
1223
|
}
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
//insert red border around the element
|
|
1220
|
-
await this.scrollIfNeeded(element, info);
|
|
1221
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1222
|
-
await this._highlightElements(element);
|
|
1224
|
+
try {
|
|
1225
|
+
await _preCommand(state, this);
|
|
1226
|
+
state.info.value = _value;
|
|
1223
1227
|
if (options === null || options === undefined || !options.press) {
|
|
1224
1228
|
try {
|
|
1225
|
-
let currentValue = await element.inputValue();
|
|
1229
|
+
let currentValue = await state.element.inputValue();
|
|
1226
1230
|
if (currentValue) {
|
|
1227
|
-
await element.fill("");
|
|
1231
|
+
await state.element.fill("");
|
|
1228
1232
|
}
|
|
1229
1233
|
}
|
|
1230
1234
|
catch (e) {
|
|
@@ -1233,22 +1237,22 @@ class StableBrowser {
|
|
|
1233
1237
|
}
|
|
1234
1238
|
if (options === null || options === undefined || options.press) {
|
|
1235
1239
|
try {
|
|
1236
|
-
await element.click({ timeout: 5000 });
|
|
1240
|
+
await state.element.click({ timeout: 5000 });
|
|
1237
1241
|
}
|
|
1238
1242
|
catch (e) {
|
|
1239
|
-
await element.dispatchEvent("click");
|
|
1243
|
+
await state.element.dispatchEvent("click");
|
|
1240
1244
|
}
|
|
1241
1245
|
}
|
|
1242
1246
|
else {
|
|
1243
1247
|
try {
|
|
1244
|
-
await element.focus();
|
|
1248
|
+
await state.element.focus();
|
|
1245
1249
|
}
|
|
1246
1250
|
catch (e) {
|
|
1247
|
-
await element.dispatchEvent("focus");
|
|
1251
|
+
await state.element.dispatchEvent("focus");
|
|
1248
1252
|
}
|
|
1249
1253
|
}
|
|
1250
1254
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1251
|
-
const valueSegment =
|
|
1255
|
+
const valueSegment = state.value.split("&&");
|
|
1252
1256
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1253
1257
|
if (i > 0) {
|
|
1254
1258
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1268,13 +1272,14 @@ class StableBrowser {
|
|
|
1268
1272
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1269
1273
|
}
|
|
1270
1274
|
}
|
|
1275
|
+
await _screenshot(state, this);
|
|
1271
1276
|
if (enter === true) {
|
|
1272
1277
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1273
1278
|
await this.page.keyboard.press("Enter");
|
|
1274
1279
|
await this.waitForPageLoad();
|
|
1275
1280
|
}
|
|
1276
1281
|
else if (enter === false) {
|
|
1277
|
-
await element.dispatchEvent("change");
|
|
1282
|
+
await state.element.dispatchEvent("change");
|
|
1278
1283
|
//await this.page.keyboard.press("Tab");
|
|
1279
1284
|
}
|
|
1280
1285
|
else {
|
|
@@ -1283,104 +1288,50 @@ class StableBrowser {
|
|
|
1283
1288
|
await this.waitForPageLoad();
|
|
1284
1289
|
}
|
|
1285
1290
|
}
|
|
1286
|
-
return info;
|
|
1291
|
+
return state.info;
|
|
1287
1292
|
}
|
|
1288
1293
|
catch (e) {
|
|
1289
|
-
|
|
1290
|
-
this.logger.error("fill failed " + JSON.stringify(info));
|
|
1291
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1292
|
-
info.screenshotPath = screenshotPath;
|
|
1293
|
-
Object.assign(e, { info: info });
|
|
1294
|
-
error = e;
|
|
1295
|
-
throw e;
|
|
1294
|
+
await _commandError(state, e, this);
|
|
1296
1295
|
}
|
|
1297
1296
|
finally {
|
|
1298
|
-
|
|
1299
|
-
this._reportToWorld(world, {
|
|
1300
|
-
element_name: selectors.element_name,
|
|
1301
|
-
type: Types.FILL,
|
|
1302
|
-
screenshotId,
|
|
1303
|
-
value: _value,
|
|
1304
|
-
text: `clickType input with value: ${_value}`,
|
|
1305
|
-
result: error
|
|
1306
|
-
? {
|
|
1307
|
-
status: "FAILED",
|
|
1308
|
-
startTime,
|
|
1309
|
-
endTime,
|
|
1310
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1311
|
-
}
|
|
1312
|
-
: {
|
|
1313
|
-
status: "PASSED",
|
|
1314
|
-
startTime,
|
|
1315
|
-
endTime,
|
|
1316
|
-
},
|
|
1317
|
-
info: info,
|
|
1318
|
-
});
|
|
1297
|
+
_commandFinally(state, this);
|
|
1319
1298
|
}
|
|
1320
1299
|
}
|
|
1321
1300
|
async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1301
|
+
const state = {
|
|
1302
|
+
selectors,
|
|
1303
|
+
_params,
|
|
1304
|
+
value: unEscapeString(value),
|
|
1305
|
+
options,
|
|
1306
|
+
world,
|
|
1307
|
+
type: Types.FILL,
|
|
1308
|
+
text: `Fill input with value: ${value}`,
|
|
1309
|
+
operation: "fill",
|
|
1310
|
+
log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
|
|
1311
|
+
};
|
|
1333
1312
|
try {
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
await
|
|
1337
|
-
await element.fill(value);
|
|
1338
|
-
await element.dispatchEvent("change");
|
|
1313
|
+
await _preCommand(state, this);
|
|
1314
|
+
await state.element.fill(value);
|
|
1315
|
+
await state.element.dispatchEvent("change");
|
|
1339
1316
|
if (enter) {
|
|
1340
1317
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1341
1318
|
await this.page.keyboard.press("Enter");
|
|
1342
1319
|
}
|
|
1343
1320
|
await this.waitForPageLoad();
|
|
1344
|
-
return info;
|
|
1321
|
+
return state.info;
|
|
1345
1322
|
}
|
|
1346
1323
|
catch (e) {
|
|
1347
|
-
|
|
1348
|
-
this.logger.error("fill failed " + info.log);
|
|
1349
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1350
|
-
info.screenshotPath = screenshotPath;
|
|
1351
|
-
Object.assign(e, { info: info });
|
|
1352
|
-
error = e;
|
|
1353
|
-
throw e;
|
|
1324
|
+
await _commandError(state, e, this);
|
|
1354
1325
|
}
|
|
1355
1326
|
finally {
|
|
1356
|
-
|
|
1357
|
-
this._reportToWorld(world, {
|
|
1358
|
-
element_name: selectors.element_name,
|
|
1359
|
-
type: Types.FILL,
|
|
1360
|
-
screenshotId,
|
|
1361
|
-
value,
|
|
1362
|
-
text: `Fill input with value: ${value}`,
|
|
1363
|
-
result: error
|
|
1364
|
-
? {
|
|
1365
|
-
status: "FAILED",
|
|
1366
|
-
startTime,
|
|
1367
|
-
endTime,
|
|
1368
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1369
|
-
}
|
|
1370
|
-
: {
|
|
1371
|
-
status: "PASSED",
|
|
1372
|
-
startTime,
|
|
1373
|
-
endTime,
|
|
1374
|
-
},
|
|
1375
|
-
info: info,
|
|
1376
|
-
});
|
|
1327
|
+
_commandFinally(state, this);
|
|
1377
1328
|
}
|
|
1378
1329
|
}
|
|
1379
1330
|
async getText(selectors, _params = null, options = {}, info = {}, world = null) {
|
|
1380
1331
|
return await this._getText(selectors, 0, _params, options, info, world);
|
|
1381
1332
|
}
|
|
1382
1333
|
async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
|
|
1383
|
-
|
|
1334
|
+
_validateSelectors(selectors);
|
|
1384
1335
|
let screenshotId = null;
|
|
1385
1336
|
let screenshotPath = null;
|
|
1386
1337
|
if (!info.log) {
|
|
@@ -1424,166 +1375,124 @@ class StableBrowser {
|
|
|
1424
1375
|
}
|
|
1425
1376
|
}
|
|
1426
1377
|
async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
|
|
1427
|
-
var _a;
|
|
1428
|
-
this._validateSelectors(selectors);
|
|
1429
1378
|
if (!pattern) {
|
|
1430
1379
|
throw new Error("pattern is null");
|
|
1431
1380
|
}
|
|
1432
1381
|
if (!text) {
|
|
1433
1382
|
throw new Error("text is null");
|
|
1434
1383
|
}
|
|
1384
|
+
const state = {
|
|
1385
|
+
selectors,
|
|
1386
|
+
_params,
|
|
1387
|
+
pattern,
|
|
1388
|
+
value: pattern,
|
|
1389
|
+
options,
|
|
1390
|
+
world,
|
|
1391
|
+
locate: false,
|
|
1392
|
+
scroll: false,
|
|
1393
|
+
screenshot: false,
|
|
1394
|
+
highlight: false,
|
|
1395
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1396
|
+
text: `Verify element contains pattern: ${pattern}`,
|
|
1397
|
+
operation: "containsPattern",
|
|
1398
|
+
log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
|
|
1399
|
+
};
|
|
1435
1400
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1436
1401
|
if (newValue !== text) {
|
|
1437
1402
|
this.logger.info(text + "=" + newValue);
|
|
1438
1403
|
text = newValue;
|
|
1439
1404
|
}
|
|
1440
|
-
const startTime = Date.now();
|
|
1441
|
-
let error = null;
|
|
1442
|
-
let screenshotId = null;
|
|
1443
|
-
let screenshotPath = null;
|
|
1444
|
-
const info = {};
|
|
1445
|
-
info.log =
|
|
1446
|
-
"***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
|
|
1447
|
-
info.operation = "containsPattern";
|
|
1448
|
-
info.selectors = selectors;
|
|
1449
|
-
info.value = text;
|
|
1450
|
-
info.pattern = pattern;
|
|
1451
1405
|
let foundObj = null;
|
|
1452
1406
|
try {
|
|
1453
|
-
|
|
1407
|
+
await _preCommand(state, this);
|
|
1408
|
+
state.info.pattern = pattern;
|
|
1409
|
+
foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
|
|
1454
1410
|
if (foundObj && foundObj.element) {
|
|
1455
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1411
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1456
1412
|
}
|
|
1457
|
-
|
|
1413
|
+
await _screenshot(state, this);
|
|
1458
1414
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1459
1415
|
pattern = pattern.replace("{text}", escapedText);
|
|
1460
1416
|
let regex = new RegExp(pattern, "im");
|
|
1461
|
-
if (!regex.test(foundObj
|
|
1462
|
-
info.foundText = foundObj
|
|
1417
|
+
if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
|
|
1418
|
+
state.info.foundText = foundObj?.text;
|
|
1463
1419
|
throw new Error("element doesn't contain text " + text);
|
|
1464
1420
|
}
|
|
1465
|
-
return info;
|
|
1421
|
+
return state.info;
|
|
1466
1422
|
}
|
|
1467
1423
|
catch (e) {
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
|
|
1471
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1472
|
-
info.screenshotPath = screenshotPath;
|
|
1473
|
-
Object.assign(e, { info: info });
|
|
1474
|
-
error = e;
|
|
1475
|
-
throw e;
|
|
1424
|
+
this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
|
|
1425
|
+
await _commandError(state, e, this);
|
|
1476
1426
|
}
|
|
1477
1427
|
finally {
|
|
1478
|
-
|
|
1479
|
-
this._reportToWorld(world, {
|
|
1480
|
-
element_name: selectors.element_name,
|
|
1481
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1482
|
-
value: pattern,
|
|
1483
|
-
text: `Verify element contains pattern: ${pattern}`,
|
|
1484
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1485
|
-
result: error
|
|
1486
|
-
? {
|
|
1487
|
-
status: "FAILED",
|
|
1488
|
-
startTime,
|
|
1489
|
-
endTime,
|
|
1490
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1491
|
-
}
|
|
1492
|
-
: {
|
|
1493
|
-
status: "PASSED",
|
|
1494
|
-
startTime,
|
|
1495
|
-
endTime,
|
|
1496
|
-
},
|
|
1497
|
-
info: info,
|
|
1498
|
-
});
|
|
1428
|
+
_commandFinally(state, this);
|
|
1499
1429
|
}
|
|
1500
1430
|
}
|
|
1501
1431
|
async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1432
|
+
const state = {
|
|
1433
|
+
selectors,
|
|
1434
|
+
_params,
|
|
1435
|
+
value: text,
|
|
1436
|
+
options,
|
|
1437
|
+
world,
|
|
1438
|
+
locate: false,
|
|
1439
|
+
scroll: false,
|
|
1440
|
+
screenshot: false,
|
|
1441
|
+
highlight: false,
|
|
1442
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1443
|
+
text: `Verify element contains text: ${text}`,
|
|
1444
|
+
operation: "containsText",
|
|
1445
|
+
log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
|
|
1446
|
+
};
|
|
1505
1447
|
if (!text) {
|
|
1506
1448
|
throw new Error("text is null");
|
|
1507
1449
|
}
|
|
1508
|
-
|
|
1509
|
-
let error = null;
|
|
1510
|
-
let screenshotId = null;
|
|
1511
|
-
let screenshotPath = null;
|
|
1512
|
-
const info = {};
|
|
1513
|
-
info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
|
|
1514
|
-
info.operation = "containsText";
|
|
1515
|
-
info.selectors = selectors;
|
|
1450
|
+
text = unEscapeString(text);
|
|
1516
1451
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1517
1452
|
if (newValue !== text) {
|
|
1518
1453
|
this.logger.info(text + "=" + newValue);
|
|
1519
1454
|
text = newValue;
|
|
1520
1455
|
}
|
|
1521
|
-
info.value = text;
|
|
1522
1456
|
let foundObj = null;
|
|
1523
1457
|
try {
|
|
1524
|
-
|
|
1458
|
+
await _preCommand(state, this);
|
|
1459
|
+
foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
|
|
1525
1460
|
if (foundObj && foundObj.element) {
|
|
1526
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1461
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1527
1462
|
}
|
|
1528
|
-
|
|
1463
|
+
await _screenshot(state, this);
|
|
1529
1464
|
const dateAlternatives = findDateAlternatives(text);
|
|
1530
1465
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1531
1466
|
if (dateAlternatives.date) {
|
|
1532
1467
|
for (let i = 0; i < dateAlternatives.dates.length; i++) {
|
|
1533
|
-
if (
|
|
1534
|
-
|
|
1535
|
-
return info;
|
|
1468
|
+
if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
|
|
1469
|
+
foundObj?.value?.includes(dateAlternatives.dates[i])) {
|
|
1470
|
+
return state.info;
|
|
1536
1471
|
}
|
|
1537
1472
|
}
|
|
1538
1473
|
throw new Error("element doesn't contain text " + text);
|
|
1539
1474
|
}
|
|
1540
1475
|
else if (numberAlternatives.number) {
|
|
1541
1476
|
for (let i = 0; i < numberAlternatives.numbers.length; i++) {
|
|
1542
|
-
if (
|
|
1543
|
-
|
|
1544
|
-
return info;
|
|
1477
|
+
if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
|
|
1478
|
+
foundObj?.value?.includes(numberAlternatives.numbers[i])) {
|
|
1479
|
+
return state.info;
|
|
1545
1480
|
}
|
|
1546
1481
|
}
|
|
1547
1482
|
throw new Error("element doesn't contain text " + text);
|
|
1548
1483
|
}
|
|
1549
|
-
else if (!
|
|
1550
|
-
info.foundText = foundObj
|
|
1551
|
-
info.value = foundObj
|
|
1484
|
+
else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
|
|
1485
|
+
state.info.foundText = foundObj?.text;
|
|
1486
|
+
state.info.value = foundObj?.value;
|
|
1552
1487
|
throw new Error("element doesn't contain text " + text);
|
|
1553
1488
|
}
|
|
1554
|
-
return info;
|
|
1489
|
+
return state.info;
|
|
1555
1490
|
}
|
|
1556
1491
|
catch (e) {
|
|
1557
|
-
|
|
1558
|
-
this.logger.error("verify element contains text failed " + info.log);
|
|
1559
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1560
|
-
info.screenshotPath = screenshotPath;
|
|
1561
|
-
Object.assign(e, { info: info });
|
|
1562
|
-
error = e;
|
|
1563
|
-
throw e;
|
|
1492
|
+
await _commandError(state, e, this);
|
|
1564
1493
|
}
|
|
1565
1494
|
finally {
|
|
1566
|
-
|
|
1567
|
-
this._reportToWorld(world, {
|
|
1568
|
-
element_name: selectors.element_name,
|
|
1569
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1570
|
-
text: `Verify element contains text: ${text}`,
|
|
1571
|
-
value: text,
|
|
1572
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1573
|
-
result: error
|
|
1574
|
-
? {
|
|
1575
|
-
status: "FAILED",
|
|
1576
|
-
startTime,
|
|
1577
|
-
endTime,
|
|
1578
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1579
|
-
}
|
|
1580
|
-
: {
|
|
1581
|
-
status: "PASSED",
|
|
1582
|
-
startTime,
|
|
1583
|
-
endTime,
|
|
1584
|
-
},
|
|
1585
|
-
info: info,
|
|
1586
|
-
});
|
|
1495
|
+
_commandFinally(state, this);
|
|
1587
1496
|
}
|
|
1588
1497
|
}
|
|
1589
1498
|
_getDataFile(world = null) {
|
|
@@ -1812,7 +1721,6 @@ class StableBrowser {
|
|
|
1812
1721
|
}
|
|
1813
1722
|
async takeScreenshot(screenshotPath) {
|
|
1814
1723
|
const playContext = this.context.playContext;
|
|
1815
|
-
const client = await playContext.newCDPSession(this.page);
|
|
1816
1724
|
// Using CDP to capture the screenshot
|
|
1817
1725
|
const viewportWidth = Math.max(...(await this.page.evaluate(() => [
|
|
1818
1726
|
document.body.scrollWidth,
|
|
@@ -1822,164 +1730,109 @@ class StableBrowser {
|
|
|
1822
1730
|
document.body.clientWidth,
|
|
1823
1731
|
document.documentElement.clientWidth,
|
|
1824
1732
|
])));
|
|
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
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
await client.detach();
|
|
1733
|
+
let screenshotBuffer = null;
|
|
1734
|
+
if (this.context.browserName === "chromium") {
|
|
1735
|
+
const client = await playContext.newCDPSession(this.page);
|
|
1736
|
+
const { data } = await client.send("Page.captureScreenshot", {
|
|
1737
|
+
format: "png",
|
|
1738
|
+
// clip: {
|
|
1739
|
+
// x: 0,
|
|
1740
|
+
// y: 0,
|
|
1741
|
+
// width: viewportWidth,
|
|
1742
|
+
// height: viewportHeight,
|
|
1743
|
+
// scale: 1,
|
|
1744
|
+
// },
|
|
1745
|
+
});
|
|
1746
|
+
await client.detach();
|
|
1747
|
+
if (!screenshotPath) {
|
|
1748
|
+
return data;
|
|
1749
|
+
}
|
|
1750
|
+
screenshotBuffer = Buffer.from(data, "base64");
|
|
1751
|
+
}
|
|
1752
|
+
else {
|
|
1753
|
+
screenshotBuffer = await this.page.screenshot();
|
|
1754
|
+
}
|
|
1755
|
+
let image = await Jimp.read(screenshotBuffer);
|
|
1756
|
+
// Get the image dimensions
|
|
1757
|
+
const { width, height } = image.bitmap;
|
|
1758
|
+
const resizeRatio = viewportWidth / width;
|
|
1759
|
+
// Resize the image to fit within the viewport dimensions without enlarging
|
|
1760
|
+
if (width > viewportWidth) {
|
|
1761
|
+
image = image.resize({ w: viewportWidth, h: height * resizeRatio }); // Resize the image while maintaining aspect ratio
|
|
1762
|
+
await image.write(screenshotPath);
|
|
1763
|
+
}
|
|
1764
|
+
else {
|
|
1765
|
+
fs.writeFileSync(screenshotPath, screenshotBuffer);
|
|
1766
|
+
}
|
|
1860
1767
|
}
|
|
1861
1768
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1769
|
+
const state = {
|
|
1770
|
+
selectors,
|
|
1771
|
+
_params,
|
|
1772
|
+
options,
|
|
1773
|
+
world,
|
|
1774
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1775
|
+
text: `Verify element exists in page`,
|
|
1776
|
+
operation: "verifyElementExistInPage",
|
|
1777
|
+
log: "***** verify element " + selectors.element_name + " exists in page *****\n",
|
|
1778
|
+
};
|
|
1867
1779
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1868
|
-
const info = {};
|
|
1869
|
-
info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
|
|
1870
|
-
info.operation = "verify";
|
|
1871
|
-
info.selectors = selectors;
|
|
1872
1780
|
try {
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
}
|
|
1877
|
-
await this._highlightElements(element);
|
|
1878
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1879
|
-
await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
1880
|
-
return info;
|
|
1781
|
+
await _preCommand(state, this);
|
|
1782
|
+
await expect(state.element).toHaveCount(1, { timeout: 10000 });
|
|
1783
|
+
return state.info;
|
|
1881
1784
|
}
|
|
1882
1785
|
catch (e) {
|
|
1883
|
-
|
|
1884
|
-
this.logger.error("verify failed " + info.log);
|
|
1885
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1886
|
-
info.screenshotPath = screenshotPath;
|
|
1887
|
-
Object.assign(e, { info: info });
|
|
1888
|
-
error = e;
|
|
1889
|
-
throw e;
|
|
1786
|
+
await _commandError(state, e, this);
|
|
1890
1787
|
}
|
|
1891
1788
|
finally {
|
|
1892
|
-
|
|
1893
|
-
this._reportToWorld(world, {
|
|
1894
|
-
element_name: selectors.element_name,
|
|
1895
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1896
|
-
text: "Verify element exists in page",
|
|
1897
|
-
screenshotId,
|
|
1898
|
-
result: error
|
|
1899
|
-
? {
|
|
1900
|
-
status: "FAILED",
|
|
1901
|
-
startTime,
|
|
1902
|
-
endTime,
|
|
1903
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1904
|
-
}
|
|
1905
|
-
: {
|
|
1906
|
-
status: "PASSED",
|
|
1907
|
-
startTime,
|
|
1908
|
-
endTime,
|
|
1909
|
-
},
|
|
1910
|
-
info: info,
|
|
1911
|
-
});
|
|
1789
|
+
_commandFinally(state, this);
|
|
1912
1790
|
}
|
|
1913
1791
|
}
|
|
1914
1792
|
async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1793
|
+
const state = {
|
|
1794
|
+
selectors,
|
|
1795
|
+
_params,
|
|
1796
|
+
attribute,
|
|
1797
|
+
variable,
|
|
1798
|
+
options,
|
|
1799
|
+
world,
|
|
1800
|
+
type: Types.EXTRACT,
|
|
1801
|
+
text: `Extract attribute from element`,
|
|
1802
|
+
operation: "extractAttribute",
|
|
1803
|
+
log: "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1804
|
+
};
|
|
1920
1805
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1921
|
-
const info = {};
|
|
1922
|
-
info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
|
|
1923
|
-
info.operation = "extract";
|
|
1924
|
-
info.selectors = selectors;
|
|
1925
1806
|
try {
|
|
1926
|
-
|
|
1927
|
-
await this._highlightElements(element);
|
|
1928
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1807
|
+
await _preCommand(state, this);
|
|
1929
1808
|
switch (attribute) {
|
|
1930
1809
|
case "inner_text":
|
|
1931
|
-
|
|
1810
|
+
state.value = await state.element.innerText();
|
|
1932
1811
|
break;
|
|
1933
1812
|
case "href":
|
|
1934
|
-
|
|
1813
|
+
state.value = await state.element.getAttribute("href");
|
|
1935
1814
|
break;
|
|
1936
1815
|
case "value":
|
|
1937
|
-
|
|
1816
|
+
state.value = await state.element.inputValue();
|
|
1938
1817
|
break;
|
|
1939
1818
|
default:
|
|
1940
|
-
|
|
1819
|
+
state.value = await state.element.getAttribute(attribute);
|
|
1941
1820
|
break;
|
|
1942
1821
|
}
|
|
1943
|
-
|
|
1822
|
+
state.info.value = state.value;
|
|
1823
|
+
this[variable] = state.value;
|
|
1944
1824
|
if (world) {
|
|
1945
|
-
world[variable] =
|
|
1825
|
+
world[variable] = state.value;
|
|
1946
1826
|
}
|
|
1947
|
-
this.setTestData({ [variable]:
|
|
1948
|
-
this.logger.info("set test data: " + variable + "=" +
|
|
1949
|
-
return info;
|
|
1827
|
+
this.setTestData({ [variable]: state.value }, world);
|
|
1828
|
+
this.logger.info("set test data: " + variable + "=" + state.value);
|
|
1829
|
+
return state.info;
|
|
1950
1830
|
}
|
|
1951
1831
|
catch (e) {
|
|
1952
|
-
|
|
1953
|
-
this.logger.error("extract failed " + info.log);
|
|
1954
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1955
|
-
info.screenshotPath = screenshotPath;
|
|
1956
|
-
Object.assign(e, { info: info });
|
|
1957
|
-
error = e;
|
|
1958
|
-
throw e;
|
|
1832
|
+
await _commandError(state, e, this);
|
|
1959
1833
|
}
|
|
1960
1834
|
finally {
|
|
1961
|
-
|
|
1962
|
-
this._reportToWorld(world, {
|
|
1963
|
-
element_name: selectors.element_name,
|
|
1964
|
-
type: Types.EXTRACT_ATTRIBUTE,
|
|
1965
|
-
variable: variable,
|
|
1966
|
-
value: info.value,
|
|
1967
|
-
text: "Extract attribute from element",
|
|
1968
|
-
screenshotId,
|
|
1969
|
-
result: error
|
|
1970
|
-
? {
|
|
1971
|
-
status: "FAILED",
|
|
1972
|
-
startTime,
|
|
1973
|
-
endTime,
|
|
1974
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1975
|
-
}
|
|
1976
|
-
: {
|
|
1977
|
-
status: "PASSED",
|
|
1978
|
-
startTime,
|
|
1979
|
-
endTime,
|
|
1980
|
-
},
|
|
1981
|
-
info: info,
|
|
1982
|
-
});
|
|
1835
|
+
_commandFinally(state, this);
|
|
1983
1836
|
}
|
|
1984
1837
|
}
|
|
1985
1838
|
async extractEmailData(emailAddress, options, world) {
|
|
@@ -2056,7 +1909,8 @@ class StableBrowser {
|
|
|
2056
1909
|
catch (e) {
|
|
2057
1910
|
errorCount++;
|
|
2058
1911
|
if (errorCount > 3) {
|
|
2059
|
-
throw e;
|
|
1912
|
+
// throw e;
|
|
1913
|
+
await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
|
|
2060
1914
|
}
|
|
2061
1915
|
// ignore
|
|
2062
1916
|
}
|
|
@@ -2167,7 +2021,8 @@ class StableBrowser {
|
|
|
2167
2021
|
info.screenshotPath = screenshotPath;
|
|
2168
2022
|
Object.assign(e, { info: info });
|
|
2169
2023
|
error = e;
|
|
2170
|
-
throw e;
|
|
2024
|
+
// throw e;
|
|
2025
|
+
await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
|
|
2171
2026
|
}
|
|
2172
2027
|
finally {
|
|
2173
2028
|
const endTime = Date.now();
|
|
@@ -2180,7 +2035,7 @@ class StableBrowser {
|
|
|
2180
2035
|
status: "FAILED",
|
|
2181
2036
|
startTime,
|
|
2182
2037
|
endTime,
|
|
2183
|
-
message: error
|
|
2038
|
+
message: error?.message,
|
|
2184
2039
|
}
|
|
2185
2040
|
: {
|
|
2186
2041
|
status: "PASSED",
|
|
@@ -2193,49 +2048,55 @@ class StableBrowser {
|
|
|
2193
2048
|
}
|
|
2194
2049
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2195
2050
|
text = unEscapeString(text);
|
|
2196
|
-
const
|
|
2051
|
+
const state = {
|
|
2052
|
+
text_search: text,
|
|
2053
|
+
options,
|
|
2054
|
+
world,
|
|
2055
|
+
locate: false,
|
|
2056
|
+
scroll: false,
|
|
2057
|
+
highlight: false,
|
|
2058
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
2059
|
+
text: `Verify text exists in page`,
|
|
2060
|
+
operation: "verifyTextExistInPage",
|
|
2061
|
+
log: "***** verify text " + text + " exists in page *****\n",
|
|
2062
|
+
};
|
|
2197
2063
|
const timeout = this._getLoadTimeout(options);
|
|
2198
|
-
let error = null;
|
|
2199
|
-
let screenshotId = null;
|
|
2200
|
-
let screenshotPath = null;
|
|
2201
2064
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2202
|
-
const info = {};
|
|
2203
|
-
info.log = "***** verify text " + text + " exists in page *****\n";
|
|
2204
|
-
info.operation = "verifyTextExistInPage";
|
|
2205
2065
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
2206
2066
|
if (newValue !== text) {
|
|
2207
2067
|
this.logger.info(text + "=" + newValue);
|
|
2208
2068
|
text = newValue;
|
|
2209
2069
|
}
|
|
2210
|
-
info.text = text;
|
|
2211
2070
|
let dateAlternatives = findDateAlternatives(text);
|
|
2212
2071
|
let numberAlternatives = findNumberAlternatives(text);
|
|
2213
2072
|
try {
|
|
2073
|
+
await _preCommand(state, this);
|
|
2074
|
+
state.info.text = text;
|
|
2214
2075
|
while (true) {
|
|
2215
2076
|
const frames = this.page.frames();
|
|
2216
2077
|
let results = [];
|
|
2217
2078
|
for (let i = 0; i < frames.length; i++) {
|
|
2218
2079
|
if (dateAlternatives.date) {
|
|
2219
2080
|
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2220
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "
|
|
2081
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2221
2082
|
result.frame = frames[i];
|
|
2222
2083
|
results.push(result);
|
|
2223
2084
|
}
|
|
2224
2085
|
}
|
|
2225
2086
|
else if (numberAlternatives.number) {
|
|
2226
2087
|
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2227
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "
|
|
2088
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2228
2089
|
result.frame = frames[i];
|
|
2229
2090
|
results.push(result);
|
|
2230
2091
|
}
|
|
2231
2092
|
}
|
|
2232
2093
|
else {
|
|
2233
|
-
const result = await this._locateElementByText(frames[i], text, "
|
|
2094
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2234
2095
|
result.frame = frames[i];
|
|
2235
2096
|
results.push(result);
|
|
2236
2097
|
}
|
|
2237
2098
|
}
|
|
2238
|
-
info.results = results;
|
|
2099
|
+
state.info.results = results;
|
|
2239
2100
|
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2240
2101
|
if (resultWithElementsFound.length === 0) {
|
|
2241
2102
|
if (Date.now() - startTime > timeout) {
|
|
@@ -2250,44 +2111,20 @@ class StableBrowser {
|
|
|
2250
2111
|
await this._highlightElements(frame, dataAttribute);
|
|
2251
2112
|
const element = await frame.$(dataAttribute);
|
|
2252
2113
|
if (element) {
|
|
2253
|
-
await this.scrollIfNeeded(element, info);
|
|
2114
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2254
2115
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2255
2116
|
}
|
|
2256
2117
|
}
|
|
2257
|
-
|
|
2258
|
-
return info;
|
|
2118
|
+
await _screenshot(state, this);
|
|
2119
|
+
return state.info;
|
|
2259
2120
|
}
|
|
2260
2121
|
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2261
2122
|
}
|
|
2262
2123
|
catch (e) {
|
|
2263
|
-
|
|
2264
|
-
this.logger.error("verify text exist in page failed " + info.log);
|
|
2265
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2266
|
-
info.screenshotPath = screenshotPath;
|
|
2267
|
-
Object.assign(e, { info: info });
|
|
2268
|
-
error = e;
|
|
2269
|
-
throw e;
|
|
2124
|
+
await _commandError(state, e, this);
|
|
2270
2125
|
}
|
|
2271
2126
|
finally {
|
|
2272
|
-
|
|
2273
|
-
this._reportToWorld(world, {
|
|
2274
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
2275
|
-
text: "Verify text exists in page",
|
|
2276
|
-
screenshotId,
|
|
2277
|
-
result: error
|
|
2278
|
-
? {
|
|
2279
|
-
status: "FAILED",
|
|
2280
|
-
startTime,
|
|
2281
|
-
endTime,
|
|
2282
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2283
|
-
}
|
|
2284
|
-
: {
|
|
2285
|
-
status: "PASSED",
|
|
2286
|
-
startTime,
|
|
2287
|
-
endTime,
|
|
2288
|
-
},
|
|
2289
|
-
info: info,
|
|
2290
|
-
});
|
|
2127
|
+
_commandFinally(state, this);
|
|
2291
2128
|
}
|
|
2292
2129
|
}
|
|
2293
2130
|
_getServerUrl() {
|
|
@@ -2350,7 +2187,8 @@ class StableBrowser {
|
|
|
2350
2187
|
info.screenshotPath = screenshotPath;
|
|
2351
2188
|
Object.assign(e, { info: info });
|
|
2352
2189
|
error = e;
|
|
2353
|
-
throw e;
|
|
2190
|
+
// throw e;
|
|
2191
|
+
await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
|
|
2354
2192
|
}
|
|
2355
2193
|
finally {
|
|
2356
2194
|
const endTime = Date.now();
|
|
@@ -2363,7 +2201,7 @@ class StableBrowser {
|
|
|
2363
2201
|
status: "FAILED",
|
|
2364
2202
|
startTime,
|
|
2365
2203
|
endTime,
|
|
2366
|
-
message: error
|
|
2204
|
+
message: error?.message,
|
|
2367
2205
|
}
|
|
2368
2206
|
: {
|
|
2369
2207
|
status: "PASSED",
|
|
@@ -2395,7 +2233,7 @@ class StableBrowser {
|
|
|
2395
2233
|
this.logger.info("Table data verified");
|
|
2396
2234
|
}
|
|
2397
2235
|
async getTableData(selectors, _params = null, options = {}, world = null) {
|
|
2398
|
-
|
|
2236
|
+
_validateSelectors(selectors);
|
|
2399
2237
|
const startTime = Date.now();
|
|
2400
2238
|
let error = null;
|
|
2401
2239
|
let screenshotId = null;
|
|
@@ -2417,7 +2255,8 @@ class StableBrowser {
|
|
|
2417
2255
|
info.screenshotPath = screenshotPath;
|
|
2418
2256
|
Object.assign(e, { info: info });
|
|
2419
2257
|
error = e;
|
|
2420
|
-
throw e;
|
|
2258
|
+
// throw e;
|
|
2259
|
+
await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
|
|
2421
2260
|
}
|
|
2422
2261
|
finally {
|
|
2423
2262
|
const endTime = Date.now();
|
|
@@ -2431,7 +2270,7 @@ class StableBrowser {
|
|
|
2431
2270
|
status: "FAILED",
|
|
2432
2271
|
startTime,
|
|
2433
2272
|
endTime,
|
|
2434
|
-
message: error
|
|
2273
|
+
message: error?.message,
|
|
2435
2274
|
}
|
|
2436
2275
|
: {
|
|
2437
2276
|
status: "PASSED",
|
|
@@ -2443,7 +2282,7 @@ class StableBrowser {
|
|
|
2443
2282
|
}
|
|
2444
2283
|
}
|
|
2445
2284
|
async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
|
|
2446
|
-
|
|
2285
|
+
_validateSelectors(selectors);
|
|
2447
2286
|
if (!query) {
|
|
2448
2287
|
throw new Error("query is null");
|
|
2449
2288
|
}
|
|
@@ -2582,7 +2421,8 @@ class StableBrowser {
|
|
|
2582
2421
|
info.screenshotPath = screenshotPath;
|
|
2583
2422
|
Object.assign(e, { info: info });
|
|
2584
2423
|
error = e;
|
|
2585
|
-
throw e;
|
|
2424
|
+
// throw e;
|
|
2425
|
+
await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
|
|
2586
2426
|
}
|
|
2587
2427
|
finally {
|
|
2588
2428
|
const endTime = Date.now();
|
|
@@ -2596,7 +2436,7 @@ class StableBrowser {
|
|
|
2596
2436
|
status: "FAILED",
|
|
2597
2437
|
startTime,
|
|
2598
2438
|
endTime,
|
|
2599
|
-
message: error
|
|
2439
|
+
message: error?.message,
|
|
2600
2440
|
}
|
|
2601
2441
|
: {
|
|
2602
2442
|
status: "PASSED",
|
|
@@ -2608,27 +2448,7 @@ class StableBrowser {
|
|
|
2608
2448
|
}
|
|
2609
2449
|
}
|
|
2610
2450
|
async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
|
|
2611
|
-
|
|
2612
|
-
return value;
|
|
2613
|
-
}
|
|
2614
|
-
// find all the accurance of {{(.*?)}} and replace with the value
|
|
2615
|
-
let regex = /{{(.*?)}}/g;
|
|
2616
|
-
let matches = value.match(regex);
|
|
2617
|
-
if (matches) {
|
|
2618
|
-
const testData = this.getTestData(world);
|
|
2619
|
-
for (let i = 0; i < matches.length; i++) {
|
|
2620
|
-
let match = matches[i];
|
|
2621
|
-
let key = match.substring(2, match.length - 2);
|
|
2622
|
-
let newValue = objectPath.get(testData, key, null);
|
|
2623
|
-
if (newValue !== null) {
|
|
2624
|
-
value = value.replace(match, newValue);
|
|
2625
|
-
}
|
|
2626
|
-
}
|
|
2627
|
-
}
|
|
2628
|
-
if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
|
|
2629
|
-
return await decrypt(value, null, totpWait);
|
|
2630
|
-
}
|
|
2631
|
-
return value;
|
|
2451
|
+
return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
|
|
2632
2452
|
}
|
|
2633
2453
|
_getLoadTimeout(options) {
|
|
2634
2454
|
let timeout = 15000;
|
|
@@ -2688,7 +2508,7 @@ class StableBrowser {
|
|
|
2688
2508
|
status: "FAILED",
|
|
2689
2509
|
startTime,
|
|
2690
2510
|
endTime,
|
|
2691
|
-
message: error
|
|
2511
|
+
message: error?.message,
|
|
2692
2512
|
}
|
|
2693
2513
|
: {
|
|
2694
2514
|
status: "PASSED",
|
|
@@ -2699,39 +2519,28 @@ class StableBrowser {
|
|
|
2699
2519
|
}
|
|
2700
2520
|
}
|
|
2701
2521
|
async closePage(options = {}, world = null) {
|
|
2702
|
-
const
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2522
|
+
const state = {
|
|
2523
|
+
options,
|
|
2524
|
+
world,
|
|
2525
|
+
locate: false,
|
|
2526
|
+
scroll: false,
|
|
2527
|
+
highlight: false,
|
|
2528
|
+
type: Types.CLOSE_PAGE,
|
|
2529
|
+
text: `Close page`,
|
|
2530
|
+
operation: "closePage",
|
|
2531
|
+
log: "***** close page *****\n",
|
|
2532
|
+
throwError: false,
|
|
2533
|
+
};
|
|
2707
2534
|
try {
|
|
2535
|
+
await _preCommand(state, this);
|
|
2708
2536
|
await this.page.close();
|
|
2709
2537
|
}
|
|
2710
2538
|
catch (e) {
|
|
2711
2539
|
console.log(".");
|
|
2540
|
+
await _commandError(state, e, this);
|
|
2712
2541
|
}
|
|
2713
2542
|
finally {
|
|
2714
|
-
|
|
2715
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2716
|
-
const endTime = Date.now();
|
|
2717
|
-
this._reportToWorld(world, {
|
|
2718
|
-
type: Types.CLOSE_PAGE,
|
|
2719
|
-
text: "close page",
|
|
2720
|
-
screenshotId,
|
|
2721
|
-
result: error
|
|
2722
|
-
? {
|
|
2723
|
-
status: "FAILED",
|
|
2724
|
-
startTime,
|
|
2725
|
-
endTime,
|
|
2726
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2727
|
-
}
|
|
2728
|
-
: {
|
|
2729
|
-
status: "PASSED",
|
|
2730
|
-
startTime,
|
|
2731
|
-
endTime,
|
|
2732
|
-
},
|
|
2733
|
-
info: info,
|
|
2734
|
-
});
|
|
2543
|
+
_commandFinally(state, this);
|
|
2735
2544
|
}
|
|
2736
2545
|
}
|
|
2737
2546
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
@@ -2751,6 +2560,7 @@ class StableBrowser {
|
|
|
2751
2560
|
}
|
|
2752
2561
|
catch (e) {
|
|
2753
2562
|
console.log(".");
|
|
2563
|
+
await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
|
|
2754
2564
|
}
|
|
2755
2565
|
finally {
|
|
2756
2566
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
@@ -2765,7 +2575,7 @@ class StableBrowser {
|
|
|
2765
2575
|
status: "FAILED",
|
|
2766
2576
|
startTime,
|
|
2767
2577
|
endTime,
|
|
2768
|
-
message: error
|
|
2578
|
+
message: error?.message,
|
|
2769
2579
|
}
|
|
2770
2580
|
: {
|
|
2771
2581
|
status: "PASSED",
|
|
@@ -2787,6 +2597,7 @@ class StableBrowser {
|
|
|
2787
2597
|
}
|
|
2788
2598
|
catch (e) {
|
|
2789
2599
|
console.log(".");
|
|
2600
|
+
await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
|
|
2790
2601
|
}
|
|
2791
2602
|
finally {
|
|
2792
2603
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
@@ -2801,7 +2612,7 @@ class StableBrowser {
|
|
|
2801
2612
|
status: "FAILED",
|
|
2802
2613
|
startTime,
|
|
2803
2614
|
endTime,
|
|
2804
|
-
message: error
|
|
2615
|
+
message: error?.message,
|
|
2805
2616
|
}
|
|
2806
2617
|
: {
|
|
2807
2618
|
status: "PASSED",
|