automation_model 1.0.449-dev → 1.0.449-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 +38 -1
- 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 +20 -17
- package/lib/stable_browser.js +584 -805
- 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,17 +47,22 @@ const Types = {
|
|
|
45
47
|
};
|
|
46
48
|
export const apps = {};
|
|
47
49
|
class StableBrowser {
|
|
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";
|
|
48
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
65
|
this.world = world;
|
|
54
|
-
this.project_path = null;
|
|
55
|
-
this.webLogFile = null;
|
|
56
|
-
this.networkLogger = null;
|
|
57
|
-
this.configuration = null;
|
|
58
|
-
this.appName = "main";
|
|
59
66
|
if (!this.logger) {
|
|
60
67
|
this.logger = console;
|
|
61
68
|
}
|
|
@@ -80,20 +87,32 @@ class StableBrowser {
|
|
|
80
87
|
catch (e) {
|
|
81
88
|
this.logger.error("unable to read ai_config.json");
|
|
82
89
|
}
|
|
83
|
-
context.pageLoading = { status: false };
|
|
84
|
-
context.pages = [this.page];
|
|
85
90
|
const logFolder = path.join(this.project_path, "logs", "web");
|
|
86
91
|
this.world = world;
|
|
87
|
-
this.
|
|
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);
|
|
88
97
|
}
|
|
89
98
|
registerEventListeners(context) {
|
|
90
99
|
this.registerConsoleLogListener(this.page, context);
|
|
91
100
|
this.registerRequestListener(this.page, context, this.webLogFile);
|
|
101
|
+
if (!context.pageLoading) {
|
|
102
|
+
context.pageLoading = { status: false };
|
|
103
|
+
}
|
|
92
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
|
+
}
|
|
93
110
|
context.pageLoading.status = true;
|
|
94
111
|
this.page = page;
|
|
95
112
|
context.page = page;
|
|
96
113
|
context.pages.push(page);
|
|
114
|
+
registerNetworkEvents(this.world, this, context, this.page);
|
|
115
|
+
registerDownloadEvent(this.page, this.world, context);
|
|
97
116
|
page.on("close", async () => {
|
|
98
117
|
if (this.context && this.context.pages && this.context.pages.length > 1) {
|
|
99
118
|
this.context.pages.pop();
|
|
@@ -123,10 +142,10 @@ class StableBrowser {
|
|
|
123
142
|
if (this.appName === appName) {
|
|
124
143
|
return;
|
|
125
144
|
}
|
|
126
|
-
let
|
|
145
|
+
let navigate = false;
|
|
127
146
|
if (!apps[appName]) {
|
|
128
147
|
let newContext = await getContext(null, false, this.logger, appName, false, this);
|
|
129
|
-
|
|
148
|
+
navigate = true;
|
|
130
149
|
apps[appName] = {
|
|
131
150
|
context: newContext,
|
|
132
151
|
browser: newContext.browser,
|
|
@@ -138,8 +157,7 @@ class StableBrowser {
|
|
|
138
157
|
this._copyContext(apps[appName], this);
|
|
139
158
|
apps[this.appName] = tempContext;
|
|
140
159
|
this.appName = appName;
|
|
141
|
-
if (
|
|
142
|
-
this.registerEventListeners(this.context);
|
|
160
|
+
if (navigate) {
|
|
143
161
|
await this.goto(this.context.environment.baseUrl);
|
|
144
162
|
await this.waitForPageLoad();
|
|
145
163
|
}
|
|
@@ -165,7 +183,6 @@ class StableBrowser {
|
|
|
165
183
|
this.context.webLogger = [];
|
|
166
184
|
}
|
|
167
185
|
page.on("console", async (msg) => {
|
|
168
|
-
var _a;
|
|
169
186
|
const obj = {
|
|
170
187
|
type: msg.type(),
|
|
171
188
|
text: msg.text(),
|
|
@@ -173,7 +190,9 @@ class StableBrowser {
|
|
|
173
190
|
time: new Date().toISOString(),
|
|
174
191
|
};
|
|
175
192
|
this.context.webLogger.push(obj);
|
|
176
|
-
|
|
193
|
+
if (msg.type() === "error") {
|
|
194
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
|
|
195
|
+
}
|
|
177
196
|
});
|
|
178
197
|
}
|
|
179
198
|
registerRequestListener(page, context, logFile) {
|
|
@@ -181,7 +200,6 @@ class StableBrowser {
|
|
|
181
200
|
this.context.networkLogger = [];
|
|
182
201
|
}
|
|
183
202
|
page.on("request", async (data) => {
|
|
184
|
-
var _a;
|
|
185
203
|
const startTime = new Date().getTime();
|
|
186
204
|
try {
|
|
187
205
|
const pageUrl = new URL(page.url());
|
|
@@ -206,7 +224,7 @@ class StableBrowser {
|
|
|
206
224
|
startTime,
|
|
207
225
|
};
|
|
208
226
|
context.networkLogger.push(obj);
|
|
209
|
-
|
|
227
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
|
|
210
228
|
}
|
|
211
229
|
catch (error) {
|
|
212
230
|
console.error("Error in request listener", error);
|
|
@@ -231,20 +249,6 @@ class StableBrowser {
|
|
|
231
249
|
timeout: 60000,
|
|
232
250
|
});
|
|
233
251
|
}
|
|
234
|
-
_validateSelectors(selectors) {
|
|
235
|
-
if (!selectors) {
|
|
236
|
-
throw new Error("selectors is null");
|
|
237
|
-
}
|
|
238
|
-
if (!selectors.locators) {
|
|
239
|
-
throw new Error("selectors.locators is null");
|
|
240
|
-
}
|
|
241
|
-
if (!Array.isArray(selectors.locators)) {
|
|
242
|
-
throw new Error("selectors.locators expected to be array");
|
|
243
|
-
}
|
|
244
|
-
if (selectors.locators.length === 0) {
|
|
245
|
-
throw new Error("selectors.locators expected to be non empty array");
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
252
|
_fixUsingParams(text, _params) {
|
|
249
253
|
if (!_params || typeof text !== "string") {
|
|
250
254
|
return text;
|
|
@@ -312,7 +316,7 @@ class StableBrowser {
|
|
|
312
316
|
locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
|
|
313
317
|
}
|
|
314
318
|
}
|
|
315
|
-
if (locator
|
|
319
|
+
if (locator?.engine) {
|
|
316
320
|
if (locator.engine === "css") {
|
|
317
321
|
locatorReturn = scope.locator(locator.selector);
|
|
318
322
|
}
|
|
@@ -333,7 +337,10 @@ class StableBrowser {
|
|
|
333
337
|
return locatorReturn;
|
|
334
338
|
}
|
|
335
339
|
async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
|
|
336
|
-
|
|
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);
|
|
337
344
|
if (result.elementCount === 0) {
|
|
338
345
|
return;
|
|
339
346
|
}
|
|
@@ -348,7 +355,7 @@ class StableBrowser {
|
|
|
348
355
|
}
|
|
349
356
|
async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
|
|
350
357
|
//const stringifyText = JSON.stringify(text);
|
|
351
|
-
return await scope.evaluate(([text, tag, regex, partial]) => {
|
|
358
|
+
return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
|
|
352
359
|
function isParent(parent, child) {
|
|
353
360
|
let currentNode = child.parentNode;
|
|
354
361
|
while (currentNode !== null) {
|
|
@@ -360,6 +367,15 @@ class StableBrowser {
|
|
|
360
367
|
return false;
|
|
361
368
|
}
|
|
362
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;
|
|
363
379
|
function collectAllShadowDomElements(element, result = []) {
|
|
364
380
|
// Check and add the element if it has a shadow root
|
|
365
381
|
if (element.shadowRoot) {
|
|
@@ -376,7 +392,11 @@ class StableBrowser {
|
|
|
376
392
|
}
|
|
377
393
|
document.collectAllShadowDomElements = collectAllShadowDomElements;
|
|
378
394
|
if (!tag) {
|
|
379
|
-
tag = "
|
|
395
|
+
tag = "*:not(script, style, head)";
|
|
396
|
+
}
|
|
397
|
+
let regexpSearch = document.getRegex(text);
|
|
398
|
+
if (regexpSearch) {
|
|
399
|
+
regex = true;
|
|
380
400
|
}
|
|
381
401
|
let elements = Array.from(document.querySelectorAll(tag));
|
|
382
402
|
let shadowHosts = [];
|
|
@@ -393,7 +413,9 @@ class StableBrowser {
|
|
|
393
413
|
let randomToken = null;
|
|
394
414
|
const foundElements = [];
|
|
395
415
|
if (regex) {
|
|
396
|
-
|
|
416
|
+
if (!regexpSearch) {
|
|
417
|
+
regexpSearch = new RegExp(text, "im");
|
|
418
|
+
}
|
|
397
419
|
for (let i = 0; i < elements.length; i++) {
|
|
398
420
|
const element = elements[i];
|
|
399
421
|
if ((element.innerText && regexpSearch.test(element.innerText)) ||
|
|
@@ -407,8 +429,8 @@ class StableBrowser {
|
|
|
407
429
|
for (let i = 0; i < elements.length; i++) {
|
|
408
430
|
const element = elements[i];
|
|
409
431
|
if (partial) {
|
|
410
|
-
if ((element.innerText && element.innerText.trim().includes(text)) ||
|
|
411
|
-
(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()))) {
|
|
412
434
|
foundElements.push(element);
|
|
413
435
|
}
|
|
414
436
|
}
|
|
@@ -453,18 +475,29 @@ class StableBrowser {
|
|
|
453
475
|
}
|
|
454
476
|
async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
|
|
455
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
|
+
}
|
|
456
484
|
//info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
|
|
457
485
|
let locator = null;
|
|
458
486
|
if (locatorSearch.climb && locatorSearch.climb >= 0) {
|
|
459
487
|
let locatorString = await this._locateElmentByTextClimbCss(scope, locatorSearch.text, locatorSearch.climb, locatorSearch.css, _params);
|
|
460
488
|
if (!locatorString) {
|
|
489
|
+
info.failCause.textNotFound = true;
|
|
490
|
+
info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
|
|
461
491
|
return;
|
|
462
492
|
}
|
|
463
493
|
locator = this._getLocator({ css: locatorString }, scope, _params);
|
|
464
494
|
}
|
|
465
495
|
else if (locatorSearch.text) {
|
|
466
|
-
let
|
|
496
|
+
let text = this._fixUsingParams(locatorSearch.text, _params);
|
|
497
|
+
let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
|
|
467
498
|
if (result.elementCount === 0) {
|
|
499
|
+
info.failCause.textNotFound = true;
|
|
500
|
+
info.failCause.lastError = "failed to locate element by text: " + text;
|
|
468
501
|
return;
|
|
469
502
|
}
|
|
470
503
|
locatorSearch.css = "[data-blinq-id='blinq-id-" + result.randomToken + "']";
|
|
@@ -481,6 +514,9 @@ class StableBrowser {
|
|
|
481
514
|
// cssHref = true;
|
|
482
515
|
// }
|
|
483
516
|
let count = await locator.count();
|
|
517
|
+
if (count > 0 && !info.failCause.count) {
|
|
518
|
+
info.failCause.count = count;
|
|
519
|
+
}
|
|
484
520
|
//info.log += "total elements found " + count + "\n";
|
|
485
521
|
//let visibleCount = 0;
|
|
486
522
|
let visibleLocator = null;
|
|
@@ -498,6 +534,8 @@ class StableBrowser {
|
|
|
498
534
|
foundLocators.push(locator.nth(j));
|
|
499
535
|
}
|
|
500
536
|
else {
|
|
537
|
+
info.failCause.visible = visible;
|
|
538
|
+
info.failCause.enabled = enabled;
|
|
501
539
|
if (!info.printMessages) {
|
|
502
540
|
info.printMessages = {};
|
|
503
541
|
}
|
|
@@ -509,6 +547,11 @@ class StableBrowser {
|
|
|
509
547
|
}
|
|
510
548
|
}
|
|
511
549
|
async closeUnexpectedPopups(info, _params) {
|
|
550
|
+
if (!info) {
|
|
551
|
+
info = {};
|
|
552
|
+
info.failCause = {};
|
|
553
|
+
info.log = "";
|
|
554
|
+
}
|
|
512
555
|
if (this.configuration.popupHandlers && this.configuration.popupHandlers.length > 0) {
|
|
513
556
|
if (!info) {
|
|
514
557
|
info = {};
|
|
@@ -549,7 +592,10 @@ class StableBrowser {
|
|
|
549
592
|
}
|
|
550
593
|
return { rerun: false };
|
|
551
594
|
}
|
|
552
|
-
async _locate(selectors, info, _params, timeout
|
|
595
|
+
async _locate(selectors, info, _params, timeout) {
|
|
596
|
+
if (!timeout) {
|
|
597
|
+
timeout = 30000;
|
|
598
|
+
}
|
|
553
599
|
for (let i = 0; i < 3; i++) {
|
|
554
600
|
info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
|
|
555
601
|
for (let j = 0; j < selectors.locators.length; j++) {
|
|
@@ -563,32 +609,46 @@ class StableBrowser {
|
|
|
563
609
|
}
|
|
564
610
|
throw new Error("unable to locate element " + JSON.stringify(selectors));
|
|
565
611
|
}
|
|
566
|
-
async
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
612
|
+
async _findFrameScope(selectors, timeout = 30000, info) {
|
|
613
|
+
if (!info) {
|
|
614
|
+
info = {};
|
|
615
|
+
info.failCause = {};
|
|
616
|
+
info.log = "";
|
|
617
|
+
}
|
|
572
618
|
let scope = this.page;
|
|
619
|
+
if (selectors.frame) {
|
|
620
|
+
return selectors.frame;
|
|
621
|
+
}
|
|
573
622
|
if (selectors.iframe_src || selectors.frameLocators) {
|
|
574
|
-
const findFrame = (frame, framescope) => {
|
|
623
|
+
const findFrame = async (frame, framescope) => {
|
|
575
624
|
for (let i = 0; i < frame.selectors.length; i++) {
|
|
576
625
|
let frameLocator = frame.selectors[i];
|
|
577
626
|
if (frameLocator.css) {
|
|
578
|
-
|
|
579
|
-
|
|
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
|
+
}
|
|
580
641
|
}
|
|
581
642
|
}
|
|
582
643
|
if (frame.children) {
|
|
583
|
-
return findFrame(frame.children, framescope);
|
|
644
|
+
return await findFrame(frame.children, framescope);
|
|
584
645
|
}
|
|
585
646
|
return framescope;
|
|
586
647
|
};
|
|
587
|
-
info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
|
|
588
648
|
while (true) {
|
|
589
649
|
let frameFound = false;
|
|
590
650
|
if (selectors.nestFrmLoc) {
|
|
591
|
-
scope = findFrame(selectors.nestFrmLoc, scope);
|
|
651
|
+
scope = await findFrame(selectors.nestFrmLoc, scope);
|
|
592
652
|
frameFound = true;
|
|
593
653
|
break;
|
|
594
654
|
}
|
|
@@ -608,6 +668,8 @@ class StableBrowser {
|
|
|
608
668
|
if (!scope) {
|
|
609
669
|
info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
|
|
610
670
|
if (performance.now() - startTime > timeout) {
|
|
671
|
+
info.failCause.iframeNotFound = true;
|
|
672
|
+
info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
|
|
611
673
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
612
674
|
}
|
|
613
675
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -617,6 +679,30 @@ class StableBrowser {
|
|
|
617
679
|
}
|
|
618
680
|
}
|
|
619
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);
|
|
620
706
|
let selectorsLocators = null;
|
|
621
707
|
selectorsLocators = selectors.locators;
|
|
622
708
|
// group selectors by priority
|
|
@@ -718,6 +804,8 @@ class StableBrowser {
|
|
|
718
804
|
}
|
|
719
805
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
720
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";
|
|
721
809
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
722
810
|
}
|
|
723
811
|
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
@@ -749,89 +837,129 @@ class StableBrowser {
|
|
|
749
837
|
});
|
|
750
838
|
result.locatorIndex = i;
|
|
751
839
|
}
|
|
840
|
+
if (foundLocators.length > 1) {
|
|
841
|
+
info.failCause.foundMultiple = true;
|
|
842
|
+
}
|
|
752
843
|
}
|
|
753
844
|
return result;
|
|
754
845
|
}
|
|
755
|
-
async
|
|
756
|
-
this._validateSelectors(selectors);
|
|
846
|
+
async simpleClick(elementDescription, _params, options = {}, world = null) {
|
|
757
847
|
const startTime = Date.now();
|
|
758
|
-
|
|
759
|
-
|
|
848
|
+
let timeout = 30000;
|
|
849
|
+
if (options && options.timeout) {
|
|
850
|
+
timeout = options.timeout;
|
|
760
851
|
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
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
|
+
};
|
|
768
919
|
try {
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
920
|
+
await _preCommand(state, this);
|
|
921
|
+
if (state.options && state.options.context) {
|
|
922
|
+
state.selectors.locators[0].text = state.options.context;
|
|
923
|
+
}
|
|
772
924
|
try {
|
|
773
|
-
await
|
|
774
|
-
await element.click();
|
|
925
|
+
await state.element.click();
|
|
775
926
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
776
927
|
}
|
|
777
928
|
catch (e) {
|
|
778
929
|
// await this.closeUnexpectedPopups();
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
await element.dispatchEvent("click");
|
|
930
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
931
|
+
await state.element.dispatchEvent("click");
|
|
782
932
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
783
933
|
}
|
|
784
934
|
await this.waitForPageLoad();
|
|
785
|
-
return info;
|
|
935
|
+
return state.info;
|
|
786
936
|
}
|
|
787
937
|
catch (e) {
|
|
788
|
-
|
|
789
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
790
|
-
info.screenshotPath = screenshotPath;
|
|
791
|
-
Object.assign(e, { info: info });
|
|
792
|
-
error = e;
|
|
793
|
-
throw e;
|
|
938
|
+
await _commandError(state, e, this);
|
|
794
939
|
}
|
|
795
940
|
finally {
|
|
796
|
-
|
|
797
|
-
this._reportToWorld(world, {
|
|
798
|
-
element_name: selectors.element_name,
|
|
799
|
-
type: Types.CLICK,
|
|
800
|
-
text: `Click element`,
|
|
801
|
-
screenshotId,
|
|
802
|
-
result: error
|
|
803
|
-
? {
|
|
804
|
-
status: "FAILED",
|
|
805
|
-
startTime,
|
|
806
|
-
endTime,
|
|
807
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
808
|
-
}
|
|
809
|
-
: {
|
|
810
|
-
status: "PASSED",
|
|
811
|
-
startTime,
|
|
812
|
-
endTime,
|
|
813
|
-
},
|
|
814
|
-
info: info,
|
|
815
|
-
});
|
|
941
|
+
_commandFinally(state, this);
|
|
816
942
|
}
|
|
817
943
|
}
|
|
818
944
|
async setCheck(selectors, checked = true, _params, options = {}, world = null) {
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
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
|
+
};
|
|
829
955
|
try {
|
|
830
|
-
|
|
831
|
-
|
|
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));
|
|
832
960
|
try {
|
|
833
|
-
await this._highlightElements(element);
|
|
834
|
-
await element.setChecked(checked);
|
|
961
|
+
// await this._highlightElements(element);
|
|
962
|
+
await state.element.setChecked(checked);
|
|
835
963
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
836
964
|
}
|
|
837
965
|
catch (e) {
|
|
@@ -840,179 +968,108 @@ class StableBrowser {
|
|
|
840
968
|
}
|
|
841
969
|
else {
|
|
842
970
|
//await this.closeUnexpectedPopups();
|
|
843
|
-
info.log += "setCheck failed, will try again" + "\n";
|
|
844
|
-
element = await this._locate(selectors, info, _params);
|
|
845
|
-
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 });
|
|
846
974
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
847
975
|
}
|
|
848
976
|
}
|
|
849
977
|
await this.waitForPageLoad();
|
|
850
|
-
return info;
|
|
978
|
+
return state.info;
|
|
851
979
|
}
|
|
852
980
|
catch (e) {
|
|
853
|
-
|
|
854
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
855
|
-
info.screenshotPath = screenshotPath;
|
|
856
|
-
Object.assign(e, { info: info });
|
|
857
|
-
error = e;
|
|
858
|
-
throw e;
|
|
981
|
+
await _commandError(state, e, this);
|
|
859
982
|
}
|
|
860
983
|
finally {
|
|
861
|
-
|
|
862
|
-
this._reportToWorld(world, {
|
|
863
|
-
element_name: selectors.element_name,
|
|
864
|
-
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
865
|
-
text: checked ? `Check element` : `Uncheck element`,
|
|
866
|
-
screenshotId,
|
|
867
|
-
result: error
|
|
868
|
-
? {
|
|
869
|
-
status: "FAILED",
|
|
870
|
-
startTime,
|
|
871
|
-
endTime,
|
|
872
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
873
|
-
}
|
|
874
|
-
: {
|
|
875
|
-
status: "PASSED",
|
|
876
|
-
startTime,
|
|
877
|
-
endTime,
|
|
878
|
-
},
|
|
879
|
-
info: info,
|
|
880
|
-
});
|
|
984
|
+
_commandFinally(state, this);
|
|
881
985
|
}
|
|
882
986
|
}
|
|
883
987
|
async hover(selectors, _params, options = {}, world = null) {
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
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
|
+
};
|
|
893
998
|
try {
|
|
894
|
-
|
|
895
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
999
|
+
await _preCommand(state, this);
|
|
896
1000
|
try {
|
|
897
|
-
await
|
|
898
|
-
await element.hover();
|
|
1001
|
+
await state.element.hover();
|
|
899
1002
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
900
1003
|
}
|
|
901
1004
|
catch (e) {
|
|
902
1005
|
//await this.closeUnexpectedPopups();
|
|
903
|
-
info.log += "hover failed, will try again" + "\n";
|
|
904
|
-
element = await this._locate(selectors, info, _params);
|
|
905
|
-
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 });
|
|
906
1009
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
907
1010
|
}
|
|
908
1011
|
await this.waitForPageLoad();
|
|
909
|
-
return info;
|
|
1012
|
+
return state.info;
|
|
910
1013
|
}
|
|
911
1014
|
catch (e) {
|
|
912
|
-
|
|
913
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
914
|
-
info.screenshotPath = screenshotPath;
|
|
915
|
-
Object.assign(e, { info: info });
|
|
916
|
-
error = e;
|
|
917
|
-
throw e;
|
|
1015
|
+
await _commandError(state, e, this);
|
|
918
1016
|
}
|
|
919
1017
|
finally {
|
|
920
|
-
|
|
921
|
-
this._reportToWorld(world, {
|
|
922
|
-
element_name: selectors.element_name,
|
|
923
|
-
type: Types.HOVER,
|
|
924
|
-
text: `Hover element`,
|
|
925
|
-
screenshotId,
|
|
926
|
-
result: error
|
|
927
|
-
? {
|
|
928
|
-
status: "FAILED",
|
|
929
|
-
startTime,
|
|
930
|
-
endTime,
|
|
931
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
932
|
-
}
|
|
933
|
-
: {
|
|
934
|
-
status: "PASSED",
|
|
935
|
-
startTime,
|
|
936
|
-
endTime,
|
|
937
|
-
},
|
|
938
|
-
info: info,
|
|
939
|
-
});
|
|
1018
|
+
_commandFinally(state, this);
|
|
940
1019
|
}
|
|
941
1020
|
}
|
|
942
1021
|
async selectOption(selectors, values, _params = null, options = {}, world = null) {
|
|
943
|
-
this._validateSelectors(selectors);
|
|
944
1022
|
if (!values) {
|
|
945
1023
|
throw new Error("values is null");
|
|
946
1024
|
}
|
|
947
|
-
const
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
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
|
+
};
|
|
955
1036
|
try {
|
|
956
|
-
|
|
957
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1037
|
+
await _preCommand(state, this);
|
|
958
1038
|
try {
|
|
959
|
-
await
|
|
960
|
-
await element.selectOption(values);
|
|
1039
|
+
await state.element.selectOption(values);
|
|
961
1040
|
}
|
|
962
1041
|
catch (e) {
|
|
963
1042
|
//await this.closeUnexpectedPopups();
|
|
964
|
-
info.log += "selectOption failed, will try force" + "\n";
|
|
965
|
-
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 });
|
|
966
1045
|
}
|
|
967
1046
|
await this.waitForPageLoad();
|
|
968
|
-
return info;
|
|
1047
|
+
return state.info;
|
|
969
1048
|
}
|
|
970
1049
|
catch (e) {
|
|
971
|
-
|
|
972
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
973
|
-
info.screenshotPath = screenshotPath;
|
|
974
|
-
Object.assign(e, { info: info });
|
|
975
|
-
this.logger.info("click failed, will try next selector");
|
|
976
|
-
error = e;
|
|
977
|
-
throw e;
|
|
1050
|
+
await _commandError(state, e, this);
|
|
978
1051
|
}
|
|
979
1052
|
finally {
|
|
980
|
-
|
|
981
|
-
this._reportToWorld(world, {
|
|
982
|
-
element_name: selectors.element_name,
|
|
983
|
-
type: Types.SELECT,
|
|
984
|
-
text: `Select option: ${values}`,
|
|
985
|
-
value: values.toString(),
|
|
986
|
-
screenshotId,
|
|
987
|
-
result: error
|
|
988
|
-
? {
|
|
989
|
-
status: "FAILED",
|
|
990
|
-
startTime,
|
|
991
|
-
endTime,
|
|
992
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
993
|
-
}
|
|
994
|
-
: {
|
|
995
|
-
status: "PASSED",
|
|
996
|
-
startTime,
|
|
997
|
-
endTime,
|
|
998
|
-
},
|
|
999
|
-
info: info,
|
|
1000
|
-
});
|
|
1053
|
+
_commandFinally(state, this);
|
|
1001
1054
|
}
|
|
1002
1055
|
}
|
|
1003
1056
|
async type(_value, _params = null, options = {}, world = null) {
|
|
1004
|
-
const
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
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
|
+
};
|
|
1013
1070
|
try {
|
|
1014
|
-
|
|
1015
|
-
const valueSegment =
|
|
1071
|
+
await _preCommand(state, this);
|
|
1072
|
+
const valueSegment = state.value.split("&&");
|
|
1016
1073
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1017
1074
|
if (i > 0) {
|
|
1018
1075
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1032,134 +1089,76 @@ class StableBrowser {
|
|
|
1032
1089
|
await this.page.keyboard.type(value);
|
|
1033
1090
|
}
|
|
1034
1091
|
}
|
|
1035
|
-
return info;
|
|
1092
|
+
return state.info;
|
|
1036
1093
|
}
|
|
1037
1094
|
catch (e) {
|
|
1038
|
-
|
|
1039
|
-
this.logger.error("type failed " + info.log);
|
|
1040
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1041
|
-
info.screenshotPath = screenshotPath;
|
|
1042
|
-
Object.assign(e, { info: info });
|
|
1043
|
-
error = e;
|
|
1044
|
-
throw e;
|
|
1095
|
+
await _commandError(state, e, this);
|
|
1045
1096
|
}
|
|
1046
1097
|
finally {
|
|
1047
|
-
|
|
1048
|
-
this._reportToWorld(world, {
|
|
1049
|
-
type: Types.TYPE_PRESS,
|
|
1050
|
-
screenshotId,
|
|
1051
|
-
value: _value,
|
|
1052
|
-
text: `type value: ${_value}`,
|
|
1053
|
-
result: error
|
|
1054
|
-
? {
|
|
1055
|
-
status: "FAILED",
|
|
1056
|
-
startTime,
|
|
1057
|
-
endTime,
|
|
1058
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1059
|
-
}
|
|
1060
|
-
: {
|
|
1061
|
-
status: "PASSED",
|
|
1062
|
-
startTime,
|
|
1063
|
-
endTime,
|
|
1064
|
-
},
|
|
1065
|
-
info: info,
|
|
1066
|
-
});
|
|
1098
|
+
_commandFinally(state, this);
|
|
1067
1099
|
}
|
|
1068
1100
|
}
|
|
1069
1101
|
async setInputValue(selectors, value, _params = null, options = {}, world = null) {
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
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
|
+
};
|
|
1082
1113
|
try {
|
|
1083
|
-
|
|
1084
|
-
let
|
|
1085
|
-
await this.scrollIfNeeded(element, info);
|
|
1086
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1087
|
-
await this._highlightElements(element);
|
|
1114
|
+
await _preCommand(state, this);
|
|
1115
|
+
let value = await this._replaceWithLocalData(state.value, this);
|
|
1088
1116
|
try {
|
|
1089
|
-
await element.evaluateHandle((el, value) => {
|
|
1117
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1090
1118
|
el.value = value;
|
|
1091
1119
|
}, value);
|
|
1092
1120
|
}
|
|
1093
1121
|
catch (error) {
|
|
1094
1122
|
this.logger.error("setInputValue failed, will try again");
|
|
1095
|
-
|
|
1096
|
-
info.
|
|
1097
|
-
|
|
1098
|
-
await element.evaluateHandle((el, value) => {
|
|
1123
|
+
await _screenshot(state, this);
|
|
1124
|
+
Object.assign(error, { info: state.info });
|
|
1125
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1099
1126
|
el.value = value;
|
|
1100
1127
|
});
|
|
1101
1128
|
}
|
|
1102
1129
|
}
|
|
1103
1130
|
catch (e) {
|
|
1104
|
-
|
|
1105
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1106
|
-
info.screenshotPath = screenshotPath;
|
|
1107
|
-
Object.assign(e, { info: info });
|
|
1108
|
-
error = e;
|
|
1109
|
-
throw e;
|
|
1131
|
+
await _commandError(state, e, this);
|
|
1110
1132
|
}
|
|
1111
1133
|
finally {
|
|
1112
|
-
|
|
1113
|
-
this._reportToWorld(world, {
|
|
1114
|
-
element_name: selectors.element_name,
|
|
1115
|
-
type: Types.SET_INPUT,
|
|
1116
|
-
text: `Set input value`,
|
|
1117
|
-
value: value,
|
|
1118
|
-
screenshotId,
|
|
1119
|
-
result: error
|
|
1120
|
-
? {
|
|
1121
|
-
status: "FAILED",
|
|
1122
|
-
startTime,
|
|
1123
|
-
endTime,
|
|
1124
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1125
|
-
}
|
|
1126
|
-
: {
|
|
1127
|
-
status: "PASSED",
|
|
1128
|
-
startTime,
|
|
1129
|
-
endTime,
|
|
1130
|
-
},
|
|
1131
|
-
info: info,
|
|
1132
|
-
});
|
|
1134
|
+
_commandFinally(state, this);
|
|
1133
1135
|
}
|
|
1134
1136
|
}
|
|
1135
1137
|
async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
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
|
+
};
|
|
1146
1150
|
try {
|
|
1147
|
-
|
|
1148
|
-
let element = await this._locate(selectors, info, _params);
|
|
1149
|
-
//insert red border around the element
|
|
1150
|
-
await this.scrollIfNeeded(element, info);
|
|
1151
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1152
|
-
await this._highlightElements(element);
|
|
1151
|
+
await _preCommand(state, this);
|
|
1153
1152
|
try {
|
|
1154
|
-
await element.click();
|
|
1153
|
+
await state.element.click();
|
|
1155
1154
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1156
1155
|
if (format) {
|
|
1157
|
-
value = dayjs(value).format(format);
|
|
1158
|
-
await element.fill(value);
|
|
1156
|
+
state.value = dayjs(state.value).format(format);
|
|
1157
|
+
await state.element.fill(state.value);
|
|
1159
1158
|
}
|
|
1160
1159
|
else {
|
|
1161
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1162
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1160
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1161
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1163
1162
|
el.value = ""; // clear input
|
|
1164
1163
|
el.value = dateTimeValue;
|
|
1165
1164
|
}, dateTimeValue);
|
|
@@ -1172,20 +1171,19 @@ class StableBrowser {
|
|
|
1172
1171
|
}
|
|
1173
1172
|
catch (err) {
|
|
1174
1173
|
//await this.closeUnexpectedPopups();
|
|
1175
|
-
this.logger.error("setting date time input failed " + JSON.stringify(info));
|
|
1174
|
+
this.logger.error("setting date time input failed " + JSON.stringify(state.info));
|
|
1176
1175
|
this.logger.info("Trying again");
|
|
1177
|
-
|
|
1178
|
-
info.
|
|
1179
|
-
Object.assign(err, { info: info });
|
|
1176
|
+
await _screenshot(state, this);
|
|
1177
|
+
Object.assign(err, { info: state.info });
|
|
1180
1178
|
await element.click();
|
|
1181
1179
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1182
1180
|
if (format) {
|
|
1183
|
-
value = dayjs(value).format(format);
|
|
1184
|
-
await element.fill(value);
|
|
1181
|
+
state.value = dayjs(state.value).format(format);
|
|
1182
|
+
await state.element.fill(state.value);
|
|
1185
1183
|
}
|
|
1186
1184
|
else {
|
|
1187
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1188
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1185
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1186
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1189
1187
|
el.value = ""; // clear input
|
|
1190
1188
|
el.value = dateTimeValue;
|
|
1191
1189
|
}, dateTimeValue);
|
|
@@ -1198,61 +1196,39 @@ class StableBrowser {
|
|
|
1198
1196
|
}
|
|
1199
1197
|
}
|
|
1200
1198
|
catch (e) {
|
|
1201
|
-
|
|
1202
|
-
throw e;
|
|
1199
|
+
await _commandError(state, e, this);
|
|
1203
1200
|
}
|
|
1204
1201
|
finally {
|
|
1205
|
-
|
|
1206
|
-
this._reportToWorld(world, {
|
|
1207
|
-
element_name: selectors.element_name,
|
|
1208
|
-
type: Types.SET_DATE_TIME,
|
|
1209
|
-
screenshotId,
|
|
1210
|
-
value: value,
|
|
1211
|
-
text: `setDateTime input with value: ${value}`,
|
|
1212
|
-
result: error
|
|
1213
|
-
? {
|
|
1214
|
-
status: "FAILED",
|
|
1215
|
-
startTime,
|
|
1216
|
-
endTime,
|
|
1217
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1218
|
-
}
|
|
1219
|
-
: {
|
|
1220
|
-
status: "PASSED",
|
|
1221
|
-
startTime,
|
|
1222
|
-
endTime,
|
|
1223
|
-
},
|
|
1224
|
-
info: info,
|
|
1225
|
-
});
|
|
1202
|
+
_commandFinally(state, this);
|
|
1226
1203
|
}
|
|
1227
1204
|
}
|
|
1228
1205
|
async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
|
|
1229
1206
|
_value = unEscapeString(_value);
|
|
1230
|
-
this._validateSelectors(selectors);
|
|
1231
|
-
const startTime = Date.now();
|
|
1232
|
-
let error = null;
|
|
1233
|
-
let screenshotId = null;
|
|
1234
|
-
let screenshotPath = null;
|
|
1235
|
-
const info = {};
|
|
1236
|
-
info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
|
|
1237
|
-
info.operation = "clickType";
|
|
1238
|
-
info.selectors = selectors;
|
|
1239
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
|
+
};
|
|
1240
1220
|
if (newValue !== _value) {
|
|
1241
1221
|
//this.logger.info(_value + "=" + newValue);
|
|
1242
1222
|
_value = newValue;
|
|
1243
1223
|
}
|
|
1244
|
-
info.value = _value;
|
|
1245
1224
|
try {
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
await this.scrollIfNeeded(element, info);
|
|
1249
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1250
|
-
await this._highlightElements(element);
|
|
1225
|
+
await _preCommand(state, this);
|
|
1226
|
+
state.info.value = _value;
|
|
1251
1227
|
if (options === null || options === undefined || !options.press) {
|
|
1252
1228
|
try {
|
|
1253
|
-
let currentValue = await element.inputValue();
|
|
1229
|
+
let currentValue = await state.element.inputValue();
|
|
1254
1230
|
if (currentValue) {
|
|
1255
|
-
await element.fill("");
|
|
1231
|
+
await state.element.fill("");
|
|
1256
1232
|
}
|
|
1257
1233
|
}
|
|
1258
1234
|
catch (e) {
|
|
@@ -1261,22 +1237,22 @@ class StableBrowser {
|
|
|
1261
1237
|
}
|
|
1262
1238
|
if (options === null || options === undefined || options.press) {
|
|
1263
1239
|
try {
|
|
1264
|
-
await element.click({ timeout: 5000 });
|
|
1240
|
+
await state.element.click({ timeout: 5000 });
|
|
1265
1241
|
}
|
|
1266
1242
|
catch (e) {
|
|
1267
|
-
await element.dispatchEvent("click");
|
|
1243
|
+
await state.element.dispatchEvent("click");
|
|
1268
1244
|
}
|
|
1269
1245
|
}
|
|
1270
1246
|
else {
|
|
1271
1247
|
try {
|
|
1272
|
-
await element.focus();
|
|
1248
|
+
await state.element.focus();
|
|
1273
1249
|
}
|
|
1274
1250
|
catch (e) {
|
|
1275
|
-
await element.dispatchEvent("focus");
|
|
1251
|
+
await state.element.dispatchEvent("focus");
|
|
1276
1252
|
}
|
|
1277
1253
|
}
|
|
1278
1254
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1279
|
-
const valueSegment =
|
|
1255
|
+
const valueSegment = state.value.split("&&");
|
|
1280
1256
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1281
1257
|
if (i > 0) {
|
|
1282
1258
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1296,13 +1272,14 @@ class StableBrowser {
|
|
|
1296
1272
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1297
1273
|
}
|
|
1298
1274
|
}
|
|
1275
|
+
await _screenshot(state, this);
|
|
1299
1276
|
if (enter === true) {
|
|
1300
1277
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1301
1278
|
await this.page.keyboard.press("Enter");
|
|
1302
1279
|
await this.waitForPageLoad();
|
|
1303
1280
|
}
|
|
1304
1281
|
else if (enter === false) {
|
|
1305
|
-
await element.dispatchEvent("change");
|
|
1282
|
+
await state.element.dispatchEvent("change");
|
|
1306
1283
|
//await this.page.keyboard.press("Tab");
|
|
1307
1284
|
}
|
|
1308
1285
|
else {
|
|
@@ -1311,104 +1288,50 @@ class StableBrowser {
|
|
|
1311
1288
|
await this.waitForPageLoad();
|
|
1312
1289
|
}
|
|
1313
1290
|
}
|
|
1314
|
-
return info;
|
|
1291
|
+
return state.info;
|
|
1315
1292
|
}
|
|
1316
1293
|
catch (e) {
|
|
1317
|
-
|
|
1318
|
-
this.logger.error("fill failed " + JSON.stringify(info));
|
|
1319
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1320
|
-
info.screenshotPath = screenshotPath;
|
|
1321
|
-
Object.assign(e, { info: info });
|
|
1322
|
-
error = e;
|
|
1323
|
-
throw e;
|
|
1294
|
+
await _commandError(state, e, this);
|
|
1324
1295
|
}
|
|
1325
1296
|
finally {
|
|
1326
|
-
|
|
1327
|
-
this._reportToWorld(world, {
|
|
1328
|
-
element_name: selectors.element_name,
|
|
1329
|
-
type: Types.FILL,
|
|
1330
|
-
screenshotId,
|
|
1331
|
-
value: _value,
|
|
1332
|
-
text: `clickType input with value: ${_value}`,
|
|
1333
|
-
result: error
|
|
1334
|
-
? {
|
|
1335
|
-
status: "FAILED",
|
|
1336
|
-
startTime,
|
|
1337
|
-
endTime,
|
|
1338
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1339
|
-
}
|
|
1340
|
-
: {
|
|
1341
|
-
status: "PASSED",
|
|
1342
|
-
startTime,
|
|
1343
|
-
endTime,
|
|
1344
|
-
},
|
|
1345
|
-
info: info,
|
|
1346
|
-
});
|
|
1297
|
+
_commandFinally(state, this);
|
|
1347
1298
|
}
|
|
1348
1299
|
}
|
|
1349
1300
|
async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
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
|
+
};
|
|
1361
1312
|
try {
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
await
|
|
1365
|
-
await element.fill(value);
|
|
1366
|
-
await element.dispatchEvent("change");
|
|
1313
|
+
await _preCommand(state, this);
|
|
1314
|
+
await state.element.fill(value);
|
|
1315
|
+
await state.element.dispatchEvent("change");
|
|
1367
1316
|
if (enter) {
|
|
1368
1317
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1369
1318
|
await this.page.keyboard.press("Enter");
|
|
1370
1319
|
}
|
|
1371
1320
|
await this.waitForPageLoad();
|
|
1372
|
-
return info;
|
|
1321
|
+
return state.info;
|
|
1373
1322
|
}
|
|
1374
1323
|
catch (e) {
|
|
1375
|
-
|
|
1376
|
-
this.logger.error("fill failed " + info.log);
|
|
1377
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1378
|
-
info.screenshotPath = screenshotPath;
|
|
1379
|
-
Object.assign(e, { info: info });
|
|
1380
|
-
error = e;
|
|
1381
|
-
throw e;
|
|
1324
|
+
await _commandError(state, e, this);
|
|
1382
1325
|
}
|
|
1383
1326
|
finally {
|
|
1384
|
-
|
|
1385
|
-
this._reportToWorld(world, {
|
|
1386
|
-
element_name: selectors.element_name,
|
|
1387
|
-
type: Types.FILL,
|
|
1388
|
-
screenshotId,
|
|
1389
|
-
value,
|
|
1390
|
-
text: `Fill input with value: ${value}`,
|
|
1391
|
-
result: error
|
|
1392
|
-
? {
|
|
1393
|
-
status: "FAILED",
|
|
1394
|
-
startTime,
|
|
1395
|
-
endTime,
|
|
1396
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1397
|
-
}
|
|
1398
|
-
: {
|
|
1399
|
-
status: "PASSED",
|
|
1400
|
-
startTime,
|
|
1401
|
-
endTime,
|
|
1402
|
-
},
|
|
1403
|
-
info: info,
|
|
1404
|
-
});
|
|
1327
|
+
_commandFinally(state, this);
|
|
1405
1328
|
}
|
|
1406
1329
|
}
|
|
1407
1330
|
async getText(selectors, _params = null, options = {}, info = {}, world = null) {
|
|
1408
1331
|
return await this._getText(selectors, 0, _params, options, info, world);
|
|
1409
1332
|
}
|
|
1410
1333
|
async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
|
|
1411
|
-
|
|
1334
|
+
_validateSelectors(selectors);
|
|
1412
1335
|
let screenshotId = null;
|
|
1413
1336
|
let screenshotPath = null;
|
|
1414
1337
|
if (!info.log) {
|
|
@@ -1452,166 +1375,124 @@ class StableBrowser {
|
|
|
1452
1375
|
}
|
|
1453
1376
|
}
|
|
1454
1377
|
async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
|
|
1455
|
-
var _a;
|
|
1456
|
-
this._validateSelectors(selectors);
|
|
1457
1378
|
if (!pattern) {
|
|
1458
1379
|
throw new Error("pattern is null");
|
|
1459
1380
|
}
|
|
1460
1381
|
if (!text) {
|
|
1461
1382
|
throw new Error("text is null");
|
|
1462
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
|
+
};
|
|
1463
1400
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1464
1401
|
if (newValue !== text) {
|
|
1465
1402
|
this.logger.info(text + "=" + newValue);
|
|
1466
1403
|
text = newValue;
|
|
1467
1404
|
}
|
|
1468
|
-
const startTime = Date.now();
|
|
1469
|
-
let error = null;
|
|
1470
|
-
let screenshotId = null;
|
|
1471
|
-
let screenshotPath = null;
|
|
1472
|
-
const info = {};
|
|
1473
|
-
info.log =
|
|
1474
|
-
"***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
|
|
1475
|
-
info.operation = "containsPattern";
|
|
1476
|
-
info.selectors = selectors;
|
|
1477
|
-
info.value = text;
|
|
1478
|
-
info.pattern = pattern;
|
|
1479
1405
|
let foundObj = null;
|
|
1480
1406
|
try {
|
|
1481
|
-
|
|
1407
|
+
await _preCommand(state, this);
|
|
1408
|
+
state.info.pattern = pattern;
|
|
1409
|
+
foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
|
|
1482
1410
|
if (foundObj && foundObj.element) {
|
|
1483
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1411
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1484
1412
|
}
|
|
1485
|
-
|
|
1413
|
+
await _screenshot(state, this);
|
|
1486
1414
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1487
1415
|
pattern = pattern.replace("{text}", escapedText);
|
|
1488
1416
|
let regex = new RegExp(pattern, "im");
|
|
1489
|
-
if (!regex.test(foundObj
|
|
1490
|
-
info.foundText = foundObj
|
|
1417
|
+
if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
|
|
1418
|
+
state.info.foundText = foundObj?.text;
|
|
1491
1419
|
throw new Error("element doesn't contain text " + text);
|
|
1492
1420
|
}
|
|
1493
|
-
return info;
|
|
1421
|
+
return state.info;
|
|
1494
1422
|
}
|
|
1495
1423
|
catch (e) {
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
|
|
1499
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1500
|
-
info.screenshotPath = screenshotPath;
|
|
1501
|
-
Object.assign(e, { info: info });
|
|
1502
|
-
error = e;
|
|
1503
|
-
throw e;
|
|
1424
|
+
this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
|
|
1425
|
+
await _commandError(state, e, this);
|
|
1504
1426
|
}
|
|
1505
1427
|
finally {
|
|
1506
|
-
|
|
1507
|
-
this._reportToWorld(world, {
|
|
1508
|
-
element_name: selectors.element_name,
|
|
1509
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1510
|
-
value: pattern,
|
|
1511
|
-
text: `Verify element contains pattern: ${pattern}`,
|
|
1512
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1513
|
-
result: error
|
|
1514
|
-
? {
|
|
1515
|
-
status: "FAILED",
|
|
1516
|
-
startTime,
|
|
1517
|
-
endTime,
|
|
1518
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1519
|
-
}
|
|
1520
|
-
: {
|
|
1521
|
-
status: "PASSED",
|
|
1522
|
-
startTime,
|
|
1523
|
-
endTime,
|
|
1524
|
-
},
|
|
1525
|
-
info: info,
|
|
1526
|
-
});
|
|
1428
|
+
_commandFinally(state, this);
|
|
1527
1429
|
}
|
|
1528
1430
|
}
|
|
1529
1431
|
async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
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
|
+
};
|
|
1533
1447
|
if (!text) {
|
|
1534
1448
|
throw new Error("text is null");
|
|
1535
1449
|
}
|
|
1536
|
-
|
|
1537
|
-
let error = null;
|
|
1538
|
-
let screenshotId = null;
|
|
1539
|
-
let screenshotPath = null;
|
|
1540
|
-
const info = {};
|
|
1541
|
-
info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
|
|
1542
|
-
info.operation = "containsText";
|
|
1543
|
-
info.selectors = selectors;
|
|
1450
|
+
text = unEscapeString(text);
|
|
1544
1451
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1545
1452
|
if (newValue !== text) {
|
|
1546
1453
|
this.logger.info(text + "=" + newValue);
|
|
1547
1454
|
text = newValue;
|
|
1548
1455
|
}
|
|
1549
|
-
info.value = text;
|
|
1550
1456
|
let foundObj = null;
|
|
1551
1457
|
try {
|
|
1552
|
-
|
|
1458
|
+
await _preCommand(state, this);
|
|
1459
|
+
foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
|
|
1553
1460
|
if (foundObj && foundObj.element) {
|
|
1554
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1461
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1555
1462
|
}
|
|
1556
|
-
|
|
1463
|
+
await _screenshot(state, this);
|
|
1557
1464
|
const dateAlternatives = findDateAlternatives(text);
|
|
1558
1465
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1559
1466
|
if (dateAlternatives.date) {
|
|
1560
1467
|
for (let i = 0; i < dateAlternatives.dates.length; i++) {
|
|
1561
|
-
if (
|
|
1562
|
-
|
|
1563
|
-
return info;
|
|
1468
|
+
if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
|
|
1469
|
+
foundObj?.value?.includes(dateAlternatives.dates[i])) {
|
|
1470
|
+
return state.info;
|
|
1564
1471
|
}
|
|
1565
1472
|
}
|
|
1566
1473
|
throw new Error("element doesn't contain text " + text);
|
|
1567
1474
|
}
|
|
1568
1475
|
else if (numberAlternatives.number) {
|
|
1569
1476
|
for (let i = 0; i < numberAlternatives.numbers.length; i++) {
|
|
1570
|
-
if (
|
|
1571
|
-
|
|
1572
|
-
return info;
|
|
1477
|
+
if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
|
|
1478
|
+
foundObj?.value?.includes(numberAlternatives.numbers[i])) {
|
|
1479
|
+
return state.info;
|
|
1573
1480
|
}
|
|
1574
1481
|
}
|
|
1575
1482
|
throw new Error("element doesn't contain text " + text);
|
|
1576
1483
|
}
|
|
1577
|
-
else if (!
|
|
1578
|
-
info.foundText = foundObj
|
|
1579
|
-
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;
|
|
1580
1487
|
throw new Error("element doesn't contain text " + text);
|
|
1581
1488
|
}
|
|
1582
|
-
return info;
|
|
1489
|
+
return state.info;
|
|
1583
1490
|
}
|
|
1584
1491
|
catch (e) {
|
|
1585
|
-
|
|
1586
|
-
this.logger.error("verify element contains text failed " + info.log);
|
|
1587
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1588
|
-
info.screenshotPath = screenshotPath;
|
|
1589
|
-
Object.assign(e, { info: info });
|
|
1590
|
-
error = e;
|
|
1591
|
-
throw e;
|
|
1492
|
+
await _commandError(state, e, this);
|
|
1592
1493
|
}
|
|
1593
1494
|
finally {
|
|
1594
|
-
|
|
1595
|
-
this._reportToWorld(world, {
|
|
1596
|
-
element_name: selectors.element_name,
|
|
1597
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1598
|
-
text: `Verify element contains text: ${text}`,
|
|
1599
|
-
value: text,
|
|
1600
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1601
|
-
result: error
|
|
1602
|
-
? {
|
|
1603
|
-
status: "FAILED",
|
|
1604
|
-
startTime,
|
|
1605
|
-
endTime,
|
|
1606
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1607
|
-
}
|
|
1608
|
-
: {
|
|
1609
|
-
status: "PASSED",
|
|
1610
|
-
startTime,
|
|
1611
|
-
endTime,
|
|
1612
|
-
},
|
|
1613
|
-
info: info,
|
|
1614
|
-
});
|
|
1495
|
+
_commandFinally(state, this);
|
|
1615
1496
|
}
|
|
1616
1497
|
}
|
|
1617
1498
|
_getDataFile(world = null) {
|
|
@@ -1840,7 +1721,6 @@ class StableBrowser {
|
|
|
1840
1721
|
}
|
|
1841
1722
|
async takeScreenshot(screenshotPath) {
|
|
1842
1723
|
const playContext = this.context.playContext;
|
|
1843
|
-
const client = await playContext.newCDPSession(this.page);
|
|
1844
1724
|
// Using CDP to capture the screenshot
|
|
1845
1725
|
const viewportWidth = Math.max(...(await this.page.evaluate(() => [
|
|
1846
1726
|
document.body.scrollWidth,
|
|
@@ -1850,164 +1730,105 @@ class StableBrowser {
|
|
|
1850
1730
|
document.body.clientWidth,
|
|
1851
1731
|
document.documentElement.clientWidth,
|
|
1852
1732
|
])));
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
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
|
+
}
|
|
1888
1767
|
}
|
|
1889
1768
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
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
|
+
};
|
|
1895
1779
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1896
|
-
const info = {};
|
|
1897
|
-
info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
|
|
1898
|
-
info.operation = "verify";
|
|
1899
|
-
info.selectors = selectors;
|
|
1900
1780
|
try {
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
}
|
|
1905
|
-
await this._highlightElements(element);
|
|
1906
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1907
|
-
await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
1908
|
-
return info;
|
|
1781
|
+
await _preCommand(state, this);
|
|
1782
|
+
await expect(state.element).toHaveCount(1, { timeout: 10000 });
|
|
1783
|
+
return state.info;
|
|
1909
1784
|
}
|
|
1910
1785
|
catch (e) {
|
|
1911
|
-
|
|
1912
|
-
this.logger.error("verify failed " + info.log);
|
|
1913
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1914
|
-
info.screenshotPath = screenshotPath;
|
|
1915
|
-
Object.assign(e, { info: info });
|
|
1916
|
-
error = e;
|
|
1917
|
-
throw e;
|
|
1786
|
+
await _commandError(state, e, this);
|
|
1918
1787
|
}
|
|
1919
1788
|
finally {
|
|
1920
|
-
|
|
1921
|
-
this._reportToWorld(world, {
|
|
1922
|
-
element_name: selectors.element_name,
|
|
1923
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1924
|
-
text: "Verify element exists in page",
|
|
1925
|
-
screenshotId,
|
|
1926
|
-
result: error
|
|
1927
|
-
? {
|
|
1928
|
-
status: "FAILED",
|
|
1929
|
-
startTime,
|
|
1930
|
-
endTime,
|
|
1931
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1932
|
-
}
|
|
1933
|
-
: {
|
|
1934
|
-
status: "PASSED",
|
|
1935
|
-
startTime,
|
|
1936
|
-
endTime,
|
|
1937
|
-
},
|
|
1938
|
-
info: info,
|
|
1939
|
-
});
|
|
1789
|
+
_commandFinally(state, this);
|
|
1940
1790
|
}
|
|
1941
1791
|
}
|
|
1942
1792
|
async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
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
|
+
};
|
|
1948
1805
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1949
|
-
const info = {};
|
|
1950
|
-
info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
|
|
1951
|
-
info.operation = "extract";
|
|
1952
|
-
info.selectors = selectors;
|
|
1953
1806
|
try {
|
|
1954
|
-
|
|
1955
|
-
await this._highlightElements(element);
|
|
1956
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1807
|
+
await _preCommand(state, this);
|
|
1957
1808
|
switch (attribute) {
|
|
1958
1809
|
case "inner_text":
|
|
1959
|
-
|
|
1810
|
+
state.value = await state.element.innerText();
|
|
1960
1811
|
break;
|
|
1961
1812
|
case "href":
|
|
1962
|
-
|
|
1813
|
+
state.value = await state.element.getAttribute("href");
|
|
1963
1814
|
break;
|
|
1964
1815
|
case "value":
|
|
1965
|
-
|
|
1816
|
+
state.value = await state.element.inputValue();
|
|
1966
1817
|
break;
|
|
1967
1818
|
default:
|
|
1968
|
-
|
|
1819
|
+
state.value = await state.element.getAttribute(attribute);
|
|
1969
1820
|
break;
|
|
1970
1821
|
}
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
this.setTestData({ [variable]: info.value }, world);
|
|
1976
|
-
this.logger.info("set test data: " + variable + "=" + info.value);
|
|
1977
|
-
return info;
|
|
1822
|
+
state.info.value = state.value;
|
|
1823
|
+
this.setTestData({ [variable]: state.value }, world);
|
|
1824
|
+
this.logger.info("set test data: " + variable + "=" + state.value);
|
|
1825
|
+
return state.info;
|
|
1978
1826
|
}
|
|
1979
1827
|
catch (e) {
|
|
1980
|
-
|
|
1981
|
-
this.logger.error("extract failed " + info.log);
|
|
1982
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1983
|
-
info.screenshotPath = screenshotPath;
|
|
1984
|
-
Object.assign(e, { info: info });
|
|
1985
|
-
error = e;
|
|
1986
|
-
throw e;
|
|
1828
|
+
await _commandError(state, e, this);
|
|
1987
1829
|
}
|
|
1988
1830
|
finally {
|
|
1989
|
-
|
|
1990
|
-
this._reportToWorld(world, {
|
|
1991
|
-
element_name: selectors.element_name,
|
|
1992
|
-
type: Types.EXTRACT_ATTRIBUTE,
|
|
1993
|
-
variable: variable,
|
|
1994
|
-
value: info.value,
|
|
1995
|
-
text: "Extract attribute from element",
|
|
1996
|
-
screenshotId,
|
|
1997
|
-
result: error
|
|
1998
|
-
? {
|
|
1999
|
-
status: "FAILED",
|
|
2000
|
-
startTime,
|
|
2001
|
-
endTime,
|
|
2002
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2003
|
-
}
|
|
2004
|
-
: {
|
|
2005
|
-
status: "PASSED",
|
|
2006
|
-
startTime,
|
|
2007
|
-
endTime,
|
|
2008
|
-
},
|
|
2009
|
-
info: info,
|
|
2010
|
-
});
|
|
1831
|
+
_commandFinally(state, this);
|
|
2011
1832
|
}
|
|
2012
1833
|
}
|
|
2013
1834
|
async extractEmailData(emailAddress, options, world) {
|
|
@@ -2084,7 +1905,8 @@ class StableBrowser {
|
|
|
2084
1905
|
catch (e) {
|
|
2085
1906
|
errorCount++;
|
|
2086
1907
|
if (errorCount > 3) {
|
|
2087
|
-
throw e;
|
|
1908
|
+
// throw e;
|
|
1909
|
+
await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
|
|
2088
1910
|
}
|
|
2089
1911
|
// ignore
|
|
2090
1912
|
}
|
|
@@ -2195,7 +2017,8 @@ class StableBrowser {
|
|
|
2195
2017
|
info.screenshotPath = screenshotPath;
|
|
2196
2018
|
Object.assign(e, { info: info });
|
|
2197
2019
|
error = e;
|
|
2198
|
-
throw e;
|
|
2020
|
+
// throw e;
|
|
2021
|
+
await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
|
|
2199
2022
|
}
|
|
2200
2023
|
finally {
|
|
2201
2024
|
const endTime = Date.now();
|
|
@@ -2208,7 +2031,7 @@ class StableBrowser {
|
|
|
2208
2031
|
status: "FAILED",
|
|
2209
2032
|
startTime,
|
|
2210
2033
|
endTime,
|
|
2211
|
-
message: error
|
|
2034
|
+
message: error?.message,
|
|
2212
2035
|
}
|
|
2213
2036
|
: {
|
|
2214
2037
|
status: "PASSED",
|
|
@@ -2221,52 +2044,58 @@ class StableBrowser {
|
|
|
2221
2044
|
}
|
|
2222
2045
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2223
2046
|
text = unEscapeString(text);
|
|
2224
|
-
const
|
|
2047
|
+
const state = {
|
|
2048
|
+
text_search: text,
|
|
2049
|
+
options,
|
|
2050
|
+
world,
|
|
2051
|
+
locate: false,
|
|
2052
|
+
scroll: false,
|
|
2053
|
+
highlight: false,
|
|
2054
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
2055
|
+
text: `Verify text exists in page`,
|
|
2056
|
+
operation: "verifyTextExistInPage",
|
|
2057
|
+
log: "***** verify text " + text + " exists in page *****\n",
|
|
2058
|
+
};
|
|
2225
2059
|
const timeout = this._getLoadTimeout(options);
|
|
2226
|
-
let error = null;
|
|
2227
|
-
let screenshotId = null;
|
|
2228
|
-
let screenshotPath = null;
|
|
2229
2060
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2230
|
-
const info = {};
|
|
2231
|
-
info.log = "***** verify text " + text + " exists in page *****\n";
|
|
2232
|
-
info.operation = "verifyTextExistInPage";
|
|
2233
2061
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
2234
2062
|
if (newValue !== text) {
|
|
2235
2063
|
this.logger.info(text + "=" + newValue);
|
|
2236
2064
|
text = newValue;
|
|
2237
2065
|
}
|
|
2238
|
-
info.text = text;
|
|
2239
2066
|
let dateAlternatives = findDateAlternatives(text);
|
|
2240
2067
|
let numberAlternatives = findNumberAlternatives(text);
|
|
2241
2068
|
try {
|
|
2069
|
+
await _preCommand(state, this);
|
|
2070
|
+
state.info.text = text;
|
|
2242
2071
|
while (true) {
|
|
2243
2072
|
const frames = this.page.frames();
|
|
2244
2073
|
let results = [];
|
|
2245
2074
|
for (let i = 0; i < frames.length; i++) {
|
|
2246
2075
|
if (dateAlternatives.date) {
|
|
2247
2076
|
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2248
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "
|
|
2077
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2249
2078
|
result.frame = frames[i];
|
|
2250
2079
|
results.push(result);
|
|
2251
2080
|
}
|
|
2252
2081
|
}
|
|
2253
2082
|
else if (numberAlternatives.number) {
|
|
2254
2083
|
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2255
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "
|
|
2084
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2256
2085
|
result.frame = frames[i];
|
|
2257
2086
|
results.push(result);
|
|
2258
2087
|
}
|
|
2259
2088
|
}
|
|
2260
2089
|
else {
|
|
2261
|
-
const result = await this._locateElementByText(frames[i], text, "
|
|
2090
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2262
2091
|
result.frame = frames[i];
|
|
2263
2092
|
results.push(result);
|
|
2264
2093
|
}
|
|
2265
2094
|
}
|
|
2266
|
-
info.results = results;
|
|
2095
|
+
state.info.results = results;
|
|
2267
2096
|
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2268
2097
|
if (resultWithElementsFound.length === 0) {
|
|
2269
|
-
if (Date.now() - startTime > timeout) {
|
|
2098
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2270
2099
|
throw new Error(`Text ${text} not found in page`);
|
|
2271
2100
|
}
|
|
2272
2101
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -2278,44 +2107,20 @@ class StableBrowser {
|
|
|
2278
2107
|
await this._highlightElements(frame, dataAttribute);
|
|
2279
2108
|
const element = await frame.$(dataAttribute);
|
|
2280
2109
|
if (element) {
|
|
2281
|
-
await this.scrollIfNeeded(element, info);
|
|
2110
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2282
2111
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2283
2112
|
}
|
|
2284
2113
|
}
|
|
2285
|
-
|
|
2286
|
-
return info;
|
|
2114
|
+
await _screenshot(state, this);
|
|
2115
|
+
return state.info;
|
|
2287
2116
|
}
|
|
2288
2117
|
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2289
2118
|
}
|
|
2290
2119
|
catch (e) {
|
|
2291
|
-
|
|
2292
|
-
this.logger.error("verify text exist in page failed " + info.log);
|
|
2293
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2294
|
-
info.screenshotPath = screenshotPath;
|
|
2295
|
-
Object.assign(e, { info: info });
|
|
2296
|
-
error = e;
|
|
2297
|
-
throw e;
|
|
2120
|
+
await _commandError(state, e, this);
|
|
2298
2121
|
}
|
|
2299
2122
|
finally {
|
|
2300
|
-
|
|
2301
|
-
this._reportToWorld(world, {
|
|
2302
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
2303
|
-
text: "Verify text exists in page",
|
|
2304
|
-
screenshotId,
|
|
2305
|
-
result: error
|
|
2306
|
-
? {
|
|
2307
|
-
status: "FAILED",
|
|
2308
|
-
startTime,
|
|
2309
|
-
endTime,
|
|
2310
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2311
|
-
}
|
|
2312
|
-
: {
|
|
2313
|
-
status: "PASSED",
|
|
2314
|
-
startTime,
|
|
2315
|
-
endTime,
|
|
2316
|
-
},
|
|
2317
|
-
info: info,
|
|
2318
|
-
});
|
|
2123
|
+
_commandFinally(state, this);
|
|
2319
2124
|
}
|
|
2320
2125
|
}
|
|
2321
2126
|
_getServerUrl() {
|
|
@@ -2378,7 +2183,8 @@ class StableBrowser {
|
|
|
2378
2183
|
info.screenshotPath = screenshotPath;
|
|
2379
2184
|
Object.assign(e, { info: info });
|
|
2380
2185
|
error = e;
|
|
2381
|
-
throw e;
|
|
2186
|
+
// throw e;
|
|
2187
|
+
await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
|
|
2382
2188
|
}
|
|
2383
2189
|
finally {
|
|
2384
2190
|
const endTime = Date.now();
|
|
@@ -2391,7 +2197,7 @@ class StableBrowser {
|
|
|
2391
2197
|
status: "FAILED",
|
|
2392
2198
|
startTime,
|
|
2393
2199
|
endTime,
|
|
2394
|
-
message: error
|
|
2200
|
+
message: error?.message,
|
|
2395
2201
|
}
|
|
2396
2202
|
: {
|
|
2397
2203
|
status: "PASSED",
|
|
@@ -2423,7 +2229,7 @@ class StableBrowser {
|
|
|
2423
2229
|
this.logger.info("Table data verified");
|
|
2424
2230
|
}
|
|
2425
2231
|
async getTableData(selectors, _params = null, options = {}, world = null) {
|
|
2426
|
-
|
|
2232
|
+
_validateSelectors(selectors);
|
|
2427
2233
|
const startTime = Date.now();
|
|
2428
2234
|
let error = null;
|
|
2429
2235
|
let screenshotId = null;
|
|
@@ -2445,7 +2251,8 @@ class StableBrowser {
|
|
|
2445
2251
|
info.screenshotPath = screenshotPath;
|
|
2446
2252
|
Object.assign(e, { info: info });
|
|
2447
2253
|
error = e;
|
|
2448
|
-
throw e;
|
|
2254
|
+
// throw e;
|
|
2255
|
+
await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
|
|
2449
2256
|
}
|
|
2450
2257
|
finally {
|
|
2451
2258
|
const endTime = Date.now();
|
|
@@ -2459,7 +2266,7 @@ class StableBrowser {
|
|
|
2459
2266
|
status: "FAILED",
|
|
2460
2267
|
startTime,
|
|
2461
2268
|
endTime,
|
|
2462
|
-
message: error
|
|
2269
|
+
message: error?.message,
|
|
2463
2270
|
}
|
|
2464
2271
|
: {
|
|
2465
2272
|
status: "PASSED",
|
|
@@ -2471,7 +2278,7 @@ class StableBrowser {
|
|
|
2471
2278
|
}
|
|
2472
2279
|
}
|
|
2473
2280
|
async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
|
|
2474
|
-
|
|
2281
|
+
_validateSelectors(selectors);
|
|
2475
2282
|
if (!query) {
|
|
2476
2283
|
throw new Error("query is null");
|
|
2477
2284
|
}
|
|
@@ -2610,7 +2417,8 @@ class StableBrowser {
|
|
|
2610
2417
|
info.screenshotPath = screenshotPath;
|
|
2611
2418
|
Object.assign(e, { info: info });
|
|
2612
2419
|
error = e;
|
|
2613
|
-
throw e;
|
|
2420
|
+
// throw e;
|
|
2421
|
+
await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
|
|
2614
2422
|
}
|
|
2615
2423
|
finally {
|
|
2616
2424
|
const endTime = Date.now();
|
|
@@ -2624,7 +2432,7 @@ class StableBrowser {
|
|
|
2624
2432
|
status: "FAILED",
|
|
2625
2433
|
startTime,
|
|
2626
2434
|
endTime,
|
|
2627
|
-
message: error
|
|
2435
|
+
message: error?.message,
|
|
2628
2436
|
}
|
|
2629
2437
|
: {
|
|
2630
2438
|
status: "PASSED",
|
|
@@ -2636,27 +2444,7 @@ class StableBrowser {
|
|
|
2636
2444
|
}
|
|
2637
2445
|
}
|
|
2638
2446
|
async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
|
|
2639
|
-
|
|
2640
|
-
return value;
|
|
2641
|
-
}
|
|
2642
|
-
// find all the accurance of {{(.*?)}} and replace with the value
|
|
2643
|
-
let regex = /{{(.*?)}}/g;
|
|
2644
|
-
let matches = value.match(regex);
|
|
2645
|
-
if (matches) {
|
|
2646
|
-
const testData = this.getTestData(world);
|
|
2647
|
-
for (let i = 0; i < matches.length; i++) {
|
|
2648
|
-
let match = matches[i];
|
|
2649
|
-
let key = match.substring(2, match.length - 2);
|
|
2650
|
-
let newValue = objectPath.get(testData, key, null);
|
|
2651
|
-
if (newValue !== null) {
|
|
2652
|
-
value = value.replace(match, newValue);
|
|
2653
|
-
}
|
|
2654
|
-
}
|
|
2655
|
-
}
|
|
2656
|
-
if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
|
|
2657
|
-
return await decrypt(value, null, totpWait);
|
|
2658
|
-
}
|
|
2659
|
-
return value;
|
|
2447
|
+
return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
|
|
2660
2448
|
}
|
|
2661
2449
|
_getLoadTimeout(options) {
|
|
2662
2450
|
let timeout = 15000;
|
|
@@ -2716,7 +2504,7 @@ class StableBrowser {
|
|
|
2716
2504
|
status: "FAILED",
|
|
2717
2505
|
startTime,
|
|
2718
2506
|
endTime,
|
|
2719
|
-
message: error
|
|
2507
|
+
message: error?.message,
|
|
2720
2508
|
}
|
|
2721
2509
|
: {
|
|
2722
2510
|
status: "PASSED",
|
|
@@ -2727,39 +2515,28 @@ class StableBrowser {
|
|
|
2727
2515
|
}
|
|
2728
2516
|
}
|
|
2729
2517
|
async closePage(options = {}, world = null) {
|
|
2730
|
-
const
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2518
|
+
const state = {
|
|
2519
|
+
options,
|
|
2520
|
+
world,
|
|
2521
|
+
locate: false,
|
|
2522
|
+
scroll: false,
|
|
2523
|
+
highlight: false,
|
|
2524
|
+
type: Types.CLOSE_PAGE,
|
|
2525
|
+
text: `Close page`,
|
|
2526
|
+
operation: "closePage",
|
|
2527
|
+
log: "***** close page *****\n",
|
|
2528
|
+
throwError: false,
|
|
2529
|
+
};
|
|
2735
2530
|
try {
|
|
2531
|
+
await _preCommand(state, this);
|
|
2736
2532
|
await this.page.close();
|
|
2737
2533
|
}
|
|
2738
2534
|
catch (e) {
|
|
2739
2535
|
console.log(".");
|
|
2536
|
+
await _commandError(state, e, this);
|
|
2740
2537
|
}
|
|
2741
2538
|
finally {
|
|
2742
|
-
|
|
2743
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2744
|
-
const endTime = Date.now();
|
|
2745
|
-
this._reportToWorld(world, {
|
|
2746
|
-
type: Types.CLOSE_PAGE,
|
|
2747
|
-
text: "close page",
|
|
2748
|
-
screenshotId,
|
|
2749
|
-
result: error
|
|
2750
|
-
? {
|
|
2751
|
-
status: "FAILED",
|
|
2752
|
-
startTime,
|
|
2753
|
-
endTime,
|
|
2754
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2755
|
-
}
|
|
2756
|
-
: {
|
|
2757
|
-
status: "PASSED",
|
|
2758
|
-
startTime,
|
|
2759
|
-
endTime,
|
|
2760
|
-
},
|
|
2761
|
-
info: info,
|
|
2762
|
-
});
|
|
2539
|
+
_commandFinally(state, this);
|
|
2763
2540
|
}
|
|
2764
2541
|
}
|
|
2765
2542
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
@@ -2779,6 +2556,7 @@ class StableBrowser {
|
|
|
2779
2556
|
}
|
|
2780
2557
|
catch (e) {
|
|
2781
2558
|
console.log(".");
|
|
2559
|
+
await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
|
|
2782
2560
|
}
|
|
2783
2561
|
finally {
|
|
2784
2562
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
@@ -2793,7 +2571,7 @@ class StableBrowser {
|
|
|
2793
2571
|
status: "FAILED",
|
|
2794
2572
|
startTime,
|
|
2795
2573
|
endTime,
|
|
2796
|
-
message: error
|
|
2574
|
+
message: error?.message,
|
|
2797
2575
|
}
|
|
2798
2576
|
: {
|
|
2799
2577
|
status: "PASSED",
|
|
@@ -2815,6 +2593,7 @@ class StableBrowser {
|
|
|
2815
2593
|
}
|
|
2816
2594
|
catch (e) {
|
|
2817
2595
|
console.log(".");
|
|
2596
|
+
await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
|
|
2818
2597
|
}
|
|
2819
2598
|
finally {
|
|
2820
2599
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
@@ -2829,7 +2608,7 @@ class StableBrowser {
|
|
|
2829
2608
|
status: "FAILED",
|
|
2830
2609
|
startTime,
|
|
2831
2610
|
endTime,
|
|
2832
|
-
message: error
|
|
2611
|
+
message: error?.message,
|
|
2833
2612
|
}
|
|
2834
2613
|
: {
|
|
2835
2614
|
status: "PASSED",
|