automation_model 1.0.451-dev → 1.0.451-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 +132 -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 +21 -17
- package/lib/stable_browser.js +661 -815
- 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",
|
|
@@ -42,20 +44,26 @@ const Types = {
|
|
|
42
44
|
VERIFY_VISUAL: "verify_visual",
|
|
43
45
|
LOAD_DATA: "load_data",
|
|
44
46
|
SET_INPUT: "set_input",
|
|
47
|
+
WAIT_FOR_TEXT_TO_DISAPPEAR: "wait_for_text_to_disappear",
|
|
45
48
|
};
|
|
46
49
|
export const apps = {};
|
|
47
50
|
class StableBrowser {
|
|
51
|
+
browser;
|
|
52
|
+
page;
|
|
53
|
+
logger;
|
|
54
|
+
context;
|
|
55
|
+
world;
|
|
56
|
+
project_path = null;
|
|
57
|
+
webLogFile = null;
|
|
58
|
+
networkLogger = null;
|
|
59
|
+
configuration = null;
|
|
60
|
+
appName = "main";
|
|
48
61
|
constructor(browser, page, logger = null, context = null, world = null) {
|
|
49
62
|
this.browser = browser;
|
|
50
63
|
this.page = page;
|
|
51
64
|
this.logger = logger;
|
|
52
65
|
this.context = context;
|
|
53
66
|
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
67
|
if (!this.logger) {
|
|
60
68
|
this.logger = console;
|
|
61
69
|
}
|
|
@@ -80,11 +88,13 @@ class StableBrowser {
|
|
|
80
88
|
catch (e) {
|
|
81
89
|
this.logger.error("unable to read ai_config.json");
|
|
82
90
|
}
|
|
83
|
-
context.pageLoading = { status: false };
|
|
84
|
-
context.pages = [this.page];
|
|
85
91
|
const logFolder = path.join(this.project_path, "logs", "web");
|
|
86
92
|
this.world = world;
|
|
93
|
+
context.pages = [this.page];
|
|
94
|
+
context.pageLoading = { status: false };
|
|
87
95
|
this.registerEventListeners(this.context);
|
|
96
|
+
registerNetworkEvents(this.world, this, this.context, this.page);
|
|
97
|
+
registerDownloadEvent(this.page, this.world, this.context);
|
|
88
98
|
}
|
|
89
99
|
registerEventListeners(context) {
|
|
90
100
|
this.registerConsoleLogListener(this.page, context);
|
|
@@ -93,10 +103,17 @@ class StableBrowser {
|
|
|
93
103
|
context.pageLoading = { status: false };
|
|
94
104
|
}
|
|
95
105
|
context.playContext.on("page", async function (page) {
|
|
106
|
+
if (this.configuration && this.configuration.closePopups === true) {
|
|
107
|
+
console.log("close unexpected popups");
|
|
108
|
+
await page.close();
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
96
111
|
context.pageLoading.status = true;
|
|
97
112
|
this.page = page;
|
|
98
113
|
context.page = page;
|
|
99
114
|
context.pages.push(page);
|
|
115
|
+
registerNetworkEvents(this.world, this, context, this.page);
|
|
116
|
+
registerDownloadEvent(this.page, this.world, context);
|
|
100
117
|
page.on("close", async () => {
|
|
101
118
|
if (this.context && this.context.pages && this.context.pages.length > 1) {
|
|
102
119
|
this.context.pages.pop();
|
|
@@ -126,10 +143,10 @@ class StableBrowser {
|
|
|
126
143
|
if (this.appName === appName) {
|
|
127
144
|
return;
|
|
128
145
|
}
|
|
129
|
-
let
|
|
146
|
+
let navigate = false;
|
|
130
147
|
if (!apps[appName]) {
|
|
131
148
|
let newContext = await getContext(null, false, this.logger, appName, false, this);
|
|
132
|
-
|
|
149
|
+
navigate = true;
|
|
133
150
|
apps[appName] = {
|
|
134
151
|
context: newContext,
|
|
135
152
|
browser: newContext.browser,
|
|
@@ -141,8 +158,7 @@ class StableBrowser {
|
|
|
141
158
|
this._copyContext(apps[appName], this);
|
|
142
159
|
apps[this.appName] = tempContext;
|
|
143
160
|
this.appName = appName;
|
|
144
|
-
if (
|
|
145
|
-
this.registerEventListeners(this.context);
|
|
161
|
+
if (navigate) {
|
|
146
162
|
await this.goto(this.context.environment.baseUrl);
|
|
147
163
|
await this.waitForPageLoad();
|
|
148
164
|
}
|
|
@@ -168,7 +184,6 @@ class StableBrowser {
|
|
|
168
184
|
this.context.webLogger = [];
|
|
169
185
|
}
|
|
170
186
|
page.on("console", async (msg) => {
|
|
171
|
-
var _a;
|
|
172
187
|
const obj = {
|
|
173
188
|
type: msg.type(),
|
|
174
189
|
text: msg.text(),
|
|
@@ -176,7 +191,9 @@ class StableBrowser {
|
|
|
176
191
|
time: new Date().toISOString(),
|
|
177
192
|
};
|
|
178
193
|
this.context.webLogger.push(obj);
|
|
179
|
-
|
|
194
|
+
if (msg.type() === "error") {
|
|
195
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
|
|
196
|
+
}
|
|
180
197
|
});
|
|
181
198
|
}
|
|
182
199
|
registerRequestListener(page, context, logFile) {
|
|
@@ -184,7 +201,6 @@ class StableBrowser {
|
|
|
184
201
|
this.context.networkLogger = [];
|
|
185
202
|
}
|
|
186
203
|
page.on("request", async (data) => {
|
|
187
|
-
var _a;
|
|
188
204
|
const startTime = new Date().getTime();
|
|
189
205
|
try {
|
|
190
206
|
const pageUrl = new URL(page.url());
|
|
@@ -209,7 +225,7 @@ class StableBrowser {
|
|
|
209
225
|
startTime,
|
|
210
226
|
};
|
|
211
227
|
context.networkLogger.push(obj);
|
|
212
|
-
|
|
228
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
|
|
213
229
|
}
|
|
214
230
|
catch (error) {
|
|
215
231
|
console.error("Error in request listener", error);
|
|
@@ -234,20 +250,6 @@ class StableBrowser {
|
|
|
234
250
|
timeout: 60000,
|
|
235
251
|
});
|
|
236
252
|
}
|
|
237
|
-
_validateSelectors(selectors) {
|
|
238
|
-
if (!selectors) {
|
|
239
|
-
throw new Error("selectors is null");
|
|
240
|
-
}
|
|
241
|
-
if (!selectors.locators) {
|
|
242
|
-
throw new Error("selectors.locators is null");
|
|
243
|
-
}
|
|
244
|
-
if (!Array.isArray(selectors.locators)) {
|
|
245
|
-
throw new Error("selectors.locators expected to be array");
|
|
246
|
-
}
|
|
247
|
-
if (selectors.locators.length === 0) {
|
|
248
|
-
throw new Error("selectors.locators expected to be non empty array");
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
253
|
_fixUsingParams(text, _params) {
|
|
252
254
|
if (!_params || typeof text !== "string") {
|
|
253
255
|
return text;
|
|
@@ -315,7 +317,7 @@ class StableBrowser {
|
|
|
315
317
|
locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
|
|
316
318
|
}
|
|
317
319
|
}
|
|
318
|
-
if (locator
|
|
320
|
+
if (locator?.engine) {
|
|
319
321
|
if (locator.engine === "css") {
|
|
320
322
|
locatorReturn = scope.locator(locator.selector);
|
|
321
323
|
}
|
|
@@ -336,7 +338,10 @@ class StableBrowser {
|
|
|
336
338
|
return locatorReturn;
|
|
337
339
|
}
|
|
338
340
|
async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
|
|
339
|
-
|
|
341
|
+
if (css && css.locator) {
|
|
342
|
+
css = css.locator;
|
|
343
|
+
}
|
|
344
|
+
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*:not(script, style, head)", false, false, _params);
|
|
340
345
|
if (result.elementCount === 0) {
|
|
341
346
|
return;
|
|
342
347
|
}
|
|
@@ -351,7 +356,7 @@ class StableBrowser {
|
|
|
351
356
|
}
|
|
352
357
|
async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
|
|
353
358
|
//const stringifyText = JSON.stringify(text);
|
|
354
|
-
return await scope.evaluate(([text, tag, regex, partial]) => {
|
|
359
|
+
return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
|
|
355
360
|
function isParent(parent, child) {
|
|
356
361
|
let currentNode = child.parentNode;
|
|
357
362
|
while (currentNode !== null) {
|
|
@@ -363,6 +368,15 @@ class StableBrowser {
|
|
|
363
368
|
return false;
|
|
364
369
|
}
|
|
365
370
|
document.isParent = isParent;
|
|
371
|
+
function getRegex(str) {
|
|
372
|
+
const match = str.match(/^\/(.*?)\/([gimuy]*)$/);
|
|
373
|
+
if (!match) {
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
let [_, pattern, flags] = match;
|
|
377
|
+
return new RegExp(pattern, flags);
|
|
378
|
+
}
|
|
379
|
+
document.getRegex = getRegex;
|
|
366
380
|
function collectAllShadowDomElements(element, result = []) {
|
|
367
381
|
// Check and add the element if it has a shadow root
|
|
368
382
|
if (element.shadowRoot) {
|
|
@@ -379,7 +393,11 @@ class StableBrowser {
|
|
|
379
393
|
}
|
|
380
394
|
document.collectAllShadowDomElements = collectAllShadowDomElements;
|
|
381
395
|
if (!tag) {
|
|
382
|
-
tag = "
|
|
396
|
+
tag = "*:not(script, style, head)";
|
|
397
|
+
}
|
|
398
|
+
let regexpSearch = document.getRegex(text);
|
|
399
|
+
if (regexpSearch) {
|
|
400
|
+
regex = true;
|
|
383
401
|
}
|
|
384
402
|
let elements = Array.from(document.querySelectorAll(tag));
|
|
385
403
|
let shadowHosts = [];
|
|
@@ -396,7 +414,9 @@ class StableBrowser {
|
|
|
396
414
|
let randomToken = null;
|
|
397
415
|
const foundElements = [];
|
|
398
416
|
if (regex) {
|
|
399
|
-
|
|
417
|
+
if (!regexpSearch) {
|
|
418
|
+
regexpSearch = new RegExp(text, "im");
|
|
419
|
+
}
|
|
400
420
|
for (let i = 0; i < elements.length; i++) {
|
|
401
421
|
const element = elements[i];
|
|
402
422
|
if ((element.innerText && regexpSearch.test(element.innerText)) ||
|
|
@@ -410,8 +430,8 @@ class StableBrowser {
|
|
|
410
430
|
for (let i = 0; i < elements.length; i++) {
|
|
411
431
|
const element = elements[i];
|
|
412
432
|
if (partial) {
|
|
413
|
-
if ((element.innerText && element.innerText.trim().includes(text)) ||
|
|
414
|
-
(element.value && element.value.includes(text))) {
|
|
433
|
+
if ((element.innerText && element.innerText.toLowerCase().trim().includes(text.toLowerCase())) ||
|
|
434
|
+
(element.value && element.value.toLowerCase().includes(text.toLowerCase()))) {
|
|
415
435
|
foundElements.push(element);
|
|
416
436
|
}
|
|
417
437
|
}
|
|
@@ -456,18 +476,29 @@ class StableBrowser {
|
|
|
456
476
|
}
|
|
457
477
|
async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
|
|
458
478
|
let locatorSearch = selectorHierarchy[index];
|
|
479
|
+
try {
|
|
480
|
+
locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
|
|
481
|
+
}
|
|
482
|
+
catch (e) {
|
|
483
|
+
console.error(e);
|
|
484
|
+
}
|
|
459
485
|
//info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
|
|
460
486
|
let locator = null;
|
|
461
487
|
if (locatorSearch.climb && locatorSearch.climb >= 0) {
|
|
462
488
|
let locatorString = await this._locateElmentByTextClimbCss(scope, locatorSearch.text, locatorSearch.climb, locatorSearch.css, _params);
|
|
463
489
|
if (!locatorString) {
|
|
490
|
+
info.failCause.textNotFound = true;
|
|
491
|
+
info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
|
|
464
492
|
return;
|
|
465
493
|
}
|
|
466
494
|
locator = this._getLocator({ css: locatorString }, scope, _params);
|
|
467
495
|
}
|
|
468
496
|
else if (locatorSearch.text) {
|
|
469
|
-
let
|
|
497
|
+
let text = this._fixUsingParams(locatorSearch.text, _params);
|
|
498
|
+
let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
|
|
470
499
|
if (result.elementCount === 0) {
|
|
500
|
+
info.failCause.textNotFound = true;
|
|
501
|
+
info.failCause.lastError = "failed to locate element by text: " + text;
|
|
471
502
|
return;
|
|
472
503
|
}
|
|
473
504
|
locatorSearch.css = "[data-blinq-id='blinq-id-" + result.randomToken + "']";
|
|
@@ -484,6 +515,9 @@ class StableBrowser {
|
|
|
484
515
|
// cssHref = true;
|
|
485
516
|
// }
|
|
486
517
|
let count = await locator.count();
|
|
518
|
+
if (count > 0 && !info.failCause.count) {
|
|
519
|
+
info.failCause.count = count;
|
|
520
|
+
}
|
|
487
521
|
//info.log += "total elements found " + count + "\n";
|
|
488
522
|
//let visibleCount = 0;
|
|
489
523
|
let visibleLocator = null;
|
|
@@ -501,6 +535,8 @@ class StableBrowser {
|
|
|
501
535
|
foundLocators.push(locator.nth(j));
|
|
502
536
|
}
|
|
503
537
|
else {
|
|
538
|
+
info.failCause.visible = visible;
|
|
539
|
+
info.failCause.enabled = enabled;
|
|
504
540
|
if (!info.printMessages) {
|
|
505
541
|
info.printMessages = {};
|
|
506
542
|
}
|
|
@@ -512,6 +548,11 @@ class StableBrowser {
|
|
|
512
548
|
}
|
|
513
549
|
}
|
|
514
550
|
async closeUnexpectedPopups(info, _params) {
|
|
551
|
+
if (!info) {
|
|
552
|
+
info = {};
|
|
553
|
+
info.failCause = {};
|
|
554
|
+
info.log = "";
|
|
555
|
+
}
|
|
515
556
|
if (this.configuration.popupHandlers && this.configuration.popupHandlers.length > 0) {
|
|
516
557
|
if (!info) {
|
|
517
558
|
info = {};
|
|
@@ -552,7 +593,10 @@ class StableBrowser {
|
|
|
552
593
|
}
|
|
553
594
|
return { rerun: false };
|
|
554
595
|
}
|
|
555
|
-
async _locate(selectors, info, _params, timeout
|
|
596
|
+
async _locate(selectors, info, _params, timeout) {
|
|
597
|
+
if (!timeout) {
|
|
598
|
+
timeout = 30000;
|
|
599
|
+
}
|
|
556
600
|
for (let i = 0; i < 3; i++) {
|
|
557
601
|
info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
|
|
558
602
|
for (let j = 0; j < selectors.locators.length; j++) {
|
|
@@ -566,32 +610,46 @@ class StableBrowser {
|
|
|
566
610
|
}
|
|
567
611
|
throw new Error("unable to locate element " + JSON.stringify(selectors));
|
|
568
612
|
}
|
|
569
|
-
async
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
613
|
+
async _findFrameScope(selectors, timeout = 30000, info) {
|
|
614
|
+
if (!info) {
|
|
615
|
+
info = {};
|
|
616
|
+
info.failCause = {};
|
|
617
|
+
info.log = "";
|
|
618
|
+
}
|
|
575
619
|
let scope = this.page;
|
|
620
|
+
if (selectors.frame) {
|
|
621
|
+
return selectors.frame;
|
|
622
|
+
}
|
|
576
623
|
if (selectors.iframe_src || selectors.frameLocators) {
|
|
577
|
-
const findFrame = (frame, framescope) => {
|
|
624
|
+
const findFrame = async (frame, framescope) => {
|
|
578
625
|
for (let i = 0; i < frame.selectors.length; i++) {
|
|
579
626
|
let frameLocator = frame.selectors[i];
|
|
580
627
|
if (frameLocator.css) {
|
|
581
|
-
|
|
582
|
-
|
|
628
|
+
let testframescope = framescope.frameLocator(frameLocator.css);
|
|
629
|
+
if (frameLocator.index) {
|
|
630
|
+
testframescope = framescope.nth(frameLocator.index);
|
|
631
|
+
}
|
|
632
|
+
try {
|
|
633
|
+
await testframescope.owner().evaluateHandle(() => true, null, {
|
|
634
|
+
timeout: 5000,
|
|
635
|
+
});
|
|
636
|
+
framescope = testframescope;
|
|
637
|
+
break;
|
|
638
|
+
}
|
|
639
|
+
catch (error) {
|
|
640
|
+
console.error("frame not found " + frameLocator.css);
|
|
641
|
+
}
|
|
583
642
|
}
|
|
584
643
|
}
|
|
585
644
|
if (frame.children) {
|
|
586
|
-
return findFrame(frame.children, framescope);
|
|
645
|
+
return await findFrame(frame.children, framescope);
|
|
587
646
|
}
|
|
588
647
|
return framescope;
|
|
589
648
|
};
|
|
590
|
-
info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
|
|
591
649
|
while (true) {
|
|
592
650
|
let frameFound = false;
|
|
593
651
|
if (selectors.nestFrmLoc) {
|
|
594
|
-
scope = findFrame(selectors.nestFrmLoc, scope);
|
|
652
|
+
scope = await findFrame(selectors.nestFrmLoc, scope);
|
|
595
653
|
frameFound = true;
|
|
596
654
|
break;
|
|
597
655
|
}
|
|
@@ -611,6 +669,8 @@ class StableBrowser {
|
|
|
611
669
|
if (!scope) {
|
|
612
670
|
info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
|
|
613
671
|
if (performance.now() - startTime > timeout) {
|
|
672
|
+
info.failCause.iframeNotFound = true;
|
|
673
|
+
info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
|
|
614
674
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
615
675
|
}
|
|
616
676
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -620,6 +680,30 @@ class StableBrowser {
|
|
|
620
680
|
}
|
|
621
681
|
}
|
|
622
682
|
}
|
|
683
|
+
if (!scope) {
|
|
684
|
+
scope = this.page;
|
|
685
|
+
}
|
|
686
|
+
return scope;
|
|
687
|
+
}
|
|
688
|
+
async _getDocumentBody(selectors, timeout = 30000, info) {
|
|
689
|
+
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
690
|
+
return scope.evaluate(() => {
|
|
691
|
+
var bodyContent = document.body.innerHTML;
|
|
692
|
+
return bodyContent;
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
696
|
+
if (!info) {
|
|
697
|
+
info = {};
|
|
698
|
+
info.failCause = {};
|
|
699
|
+
info.log = "";
|
|
700
|
+
}
|
|
701
|
+
let highPriorityTimeout = 5000;
|
|
702
|
+
let visibleOnlyTimeout = 6000;
|
|
703
|
+
let startTime = performance.now();
|
|
704
|
+
let locatorsCount = 0;
|
|
705
|
+
//let arrayMode = Array.isArray(selectors);
|
|
706
|
+
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
623
707
|
let selectorsLocators = null;
|
|
624
708
|
selectorsLocators = selectors.locators;
|
|
625
709
|
// group selectors by priority
|
|
@@ -721,6 +805,8 @@ class StableBrowser {
|
|
|
721
805
|
}
|
|
722
806
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
723
807
|
info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
|
|
808
|
+
info.failCause.locatorNotFound = true;
|
|
809
|
+
info.failCause.lastError = "failed to locate unique element";
|
|
724
810
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
725
811
|
}
|
|
726
812
|
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
@@ -752,89 +838,129 @@ class StableBrowser {
|
|
|
752
838
|
});
|
|
753
839
|
result.locatorIndex = i;
|
|
754
840
|
}
|
|
841
|
+
if (foundLocators.length > 1) {
|
|
842
|
+
info.failCause.foundMultiple = true;
|
|
843
|
+
}
|
|
755
844
|
}
|
|
756
845
|
return result;
|
|
757
846
|
}
|
|
758
|
-
async
|
|
759
|
-
this._validateSelectors(selectors);
|
|
847
|
+
async simpleClick(elementDescription, _params, options = {}, world = null) {
|
|
760
848
|
const startTime = Date.now();
|
|
761
|
-
|
|
762
|
-
|
|
849
|
+
let timeout = 30000;
|
|
850
|
+
if (options && options.timeout) {
|
|
851
|
+
timeout = options.timeout;
|
|
763
852
|
}
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
853
|
+
while (true) {
|
|
854
|
+
try {
|
|
855
|
+
const result = await locate_element(this.context, elementDescription, "click");
|
|
856
|
+
if (result?.elementNumber >= 0) {
|
|
857
|
+
const selectors = {
|
|
858
|
+
frame: result?.frame,
|
|
859
|
+
locators: [
|
|
860
|
+
{
|
|
861
|
+
css: result?.css,
|
|
862
|
+
},
|
|
863
|
+
],
|
|
864
|
+
};
|
|
865
|
+
await this.click(selectors, _params, options, world);
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
catch (e) {
|
|
870
|
+
if (performance.now() - startTime > timeout) {
|
|
871
|
+
// throw e;
|
|
872
|
+
await _commandError({ text: "simpleClick", operation: "simpleClick", elementDescription, info: {} }, e, this);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
|
|
879
|
+
const startTime = Date.now();
|
|
880
|
+
let timeout = 30000;
|
|
881
|
+
if (options && options.timeout) {
|
|
882
|
+
timeout = options.timeout;
|
|
883
|
+
}
|
|
884
|
+
while (true) {
|
|
885
|
+
try {
|
|
886
|
+
const result = await locate_element(this.context, elementDescription, "fill", value);
|
|
887
|
+
if (result?.elementNumber >= 0) {
|
|
888
|
+
const selectors = {
|
|
889
|
+
frame: result?.frame,
|
|
890
|
+
locators: [
|
|
891
|
+
{
|
|
892
|
+
css: result?.css,
|
|
893
|
+
},
|
|
894
|
+
],
|
|
895
|
+
};
|
|
896
|
+
await this.clickType(selectors, value, false, _params, options, world);
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
catch (e) {
|
|
901
|
+
if (performance.now() - startTime > timeout) {
|
|
902
|
+
// throw e;
|
|
903
|
+
await _commandError({ text: "simpleClickType", operation: "simpleClickType", value, elementDescription, info: {} }, e, this);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
async click(selectors, _params, options = {}, world = null) {
|
|
910
|
+
const state = {
|
|
911
|
+
selectors,
|
|
912
|
+
_params,
|
|
913
|
+
options,
|
|
914
|
+
world,
|
|
915
|
+
text: "Click element",
|
|
916
|
+
type: Types.CLICK,
|
|
917
|
+
operation: "click",
|
|
918
|
+
log: "***** click on " + selectors.element_name + " *****\n",
|
|
919
|
+
};
|
|
771
920
|
try {
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
921
|
+
await _preCommand(state, this);
|
|
922
|
+
if (state.options && state.options.context) {
|
|
923
|
+
state.selectors.locators[0].text = state.options.context;
|
|
924
|
+
}
|
|
775
925
|
try {
|
|
776
|
-
await
|
|
777
|
-
await element.click();
|
|
926
|
+
await state.element.click();
|
|
778
927
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
779
928
|
}
|
|
780
929
|
catch (e) {
|
|
781
930
|
// await this.closeUnexpectedPopups();
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
await element.dispatchEvent("click");
|
|
931
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
932
|
+
await state.element.dispatchEvent("click");
|
|
785
933
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
786
934
|
}
|
|
787
935
|
await this.waitForPageLoad();
|
|
788
|
-
return info;
|
|
936
|
+
return state.info;
|
|
789
937
|
}
|
|
790
938
|
catch (e) {
|
|
791
|
-
|
|
792
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
793
|
-
info.screenshotPath = screenshotPath;
|
|
794
|
-
Object.assign(e, { info: info });
|
|
795
|
-
error = e;
|
|
796
|
-
throw e;
|
|
939
|
+
await _commandError(state, e, this);
|
|
797
940
|
}
|
|
798
941
|
finally {
|
|
799
|
-
|
|
800
|
-
this._reportToWorld(world, {
|
|
801
|
-
element_name: selectors.element_name,
|
|
802
|
-
type: Types.CLICK,
|
|
803
|
-
text: `Click element`,
|
|
804
|
-
screenshotId,
|
|
805
|
-
result: error
|
|
806
|
-
? {
|
|
807
|
-
status: "FAILED",
|
|
808
|
-
startTime,
|
|
809
|
-
endTime,
|
|
810
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
811
|
-
}
|
|
812
|
-
: {
|
|
813
|
-
status: "PASSED",
|
|
814
|
-
startTime,
|
|
815
|
-
endTime,
|
|
816
|
-
},
|
|
817
|
-
info: info,
|
|
818
|
-
});
|
|
942
|
+
_commandFinally(state, this);
|
|
819
943
|
}
|
|
820
944
|
}
|
|
821
945
|
async setCheck(selectors, checked = true, _params, options = {}, world = null) {
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
946
|
+
const state = {
|
|
947
|
+
selectors,
|
|
948
|
+
_params,
|
|
949
|
+
options,
|
|
950
|
+
world,
|
|
951
|
+
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
952
|
+
text: checked ? `Check element` : `Uncheck element`,
|
|
953
|
+
operation: "setCheck",
|
|
954
|
+
log: "***** check " + selectors.element_name + " *****\n",
|
|
955
|
+
};
|
|
832
956
|
try {
|
|
833
|
-
|
|
834
|
-
|
|
957
|
+
await _preCommand(state, this);
|
|
958
|
+
state.info.checked = checked;
|
|
959
|
+
// let element = await this._locate(selectors, info, _params);
|
|
960
|
+
// ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
835
961
|
try {
|
|
836
|
-
await this._highlightElements(element);
|
|
837
|
-
await element.setChecked(checked);
|
|
962
|
+
// await this._highlightElements(element);
|
|
963
|
+
await state.element.setChecked(checked);
|
|
838
964
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
839
965
|
}
|
|
840
966
|
catch (e) {
|
|
@@ -843,179 +969,108 @@ class StableBrowser {
|
|
|
843
969
|
}
|
|
844
970
|
else {
|
|
845
971
|
//await this.closeUnexpectedPopups();
|
|
846
|
-
info.log += "setCheck failed, will try again" + "\n";
|
|
847
|
-
element = await this._locate(selectors, info, _params);
|
|
848
|
-
await element.setChecked(checked, { timeout: 5000, force: true });
|
|
972
|
+
state.info.log += "setCheck failed, will try again" + "\n";
|
|
973
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
974
|
+
await state.element.setChecked(checked, { timeout: 5000, force: true });
|
|
849
975
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
850
976
|
}
|
|
851
977
|
}
|
|
852
978
|
await this.waitForPageLoad();
|
|
853
|
-
return info;
|
|
979
|
+
return state.info;
|
|
854
980
|
}
|
|
855
981
|
catch (e) {
|
|
856
|
-
|
|
857
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
858
|
-
info.screenshotPath = screenshotPath;
|
|
859
|
-
Object.assign(e, { info: info });
|
|
860
|
-
error = e;
|
|
861
|
-
throw e;
|
|
982
|
+
await _commandError(state, e, this);
|
|
862
983
|
}
|
|
863
984
|
finally {
|
|
864
|
-
|
|
865
|
-
this._reportToWorld(world, {
|
|
866
|
-
element_name: selectors.element_name,
|
|
867
|
-
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
868
|
-
text: checked ? `Check element` : `Uncheck element`,
|
|
869
|
-
screenshotId,
|
|
870
|
-
result: error
|
|
871
|
-
? {
|
|
872
|
-
status: "FAILED",
|
|
873
|
-
startTime,
|
|
874
|
-
endTime,
|
|
875
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
876
|
-
}
|
|
877
|
-
: {
|
|
878
|
-
status: "PASSED",
|
|
879
|
-
startTime,
|
|
880
|
-
endTime,
|
|
881
|
-
},
|
|
882
|
-
info: info,
|
|
883
|
-
});
|
|
985
|
+
_commandFinally(state, this);
|
|
884
986
|
}
|
|
885
987
|
}
|
|
886
988
|
async hover(selectors, _params, options = {}, world = null) {
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
989
|
+
const state = {
|
|
990
|
+
selectors,
|
|
991
|
+
_params,
|
|
992
|
+
options,
|
|
993
|
+
world,
|
|
994
|
+
type: Types.HOVER,
|
|
995
|
+
text: `Hover element`,
|
|
996
|
+
operation: "hover",
|
|
997
|
+
log: "***** hover " + selectors.element_name + " *****\n",
|
|
998
|
+
};
|
|
896
999
|
try {
|
|
897
|
-
|
|
898
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1000
|
+
await _preCommand(state, this);
|
|
899
1001
|
try {
|
|
900
|
-
await
|
|
901
|
-
await element.hover();
|
|
1002
|
+
await state.element.hover();
|
|
902
1003
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
903
1004
|
}
|
|
904
1005
|
catch (e) {
|
|
905
1006
|
//await this.closeUnexpectedPopups();
|
|
906
|
-
info.log += "hover failed, will try again" + "\n";
|
|
907
|
-
element = await this._locate(selectors, info, _params);
|
|
908
|
-
await element.hover({ timeout: 10000 });
|
|
1007
|
+
state.info.log += "hover failed, will try again" + "\n";
|
|
1008
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
1009
|
+
await state.element.hover({ timeout: 10000 });
|
|
909
1010
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
910
1011
|
}
|
|
911
1012
|
await this.waitForPageLoad();
|
|
912
|
-
return info;
|
|
1013
|
+
return state.info;
|
|
913
1014
|
}
|
|
914
1015
|
catch (e) {
|
|
915
|
-
|
|
916
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
917
|
-
info.screenshotPath = screenshotPath;
|
|
918
|
-
Object.assign(e, { info: info });
|
|
919
|
-
error = e;
|
|
920
|
-
throw e;
|
|
1016
|
+
await _commandError(state, e, this);
|
|
921
1017
|
}
|
|
922
1018
|
finally {
|
|
923
|
-
|
|
924
|
-
this._reportToWorld(world, {
|
|
925
|
-
element_name: selectors.element_name,
|
|
926
|
-
type: Types.HOVER,
|
|
927
|
-
text: `Hover element`,
|
|
928
|
-
screenshotId,
|
|
929
|
-
result: error
|
|
930
|
-
? {
|
|
931
|
-
status: "FAILED",
|
|
932
|
-
startTime,
|
|
933
|
-
endTime,
|
|
934
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
935
|
-
}
|
|
936
|
-
: {
|
|
937
|
-
status: "PASSED",
|
|
938
|
-
startTime,
|
|
939
|
-
endTime,
|
|
940
|
-
},
|
|
941
|
-
info: info,
|
|
942
|
-
});
|
|
1019
|
+
_commandFinally(state, this);
|
|
943
1020
|
}
|
|
944
1021
|
}
|
|
945
1022
|
async selectOption(selectors, values, _params = null, options = {}, world = null) {
|
|
946
|
-
this._validateSelectors(selectors);
|
|
947
1023
|
if (!values) {
|
|
948
1024
|
throw new Error("values is null");
|
|
949
1025
|
}
|
|
950
|
-
const
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1026
|
+
const state = {
|
|
1027
|
+
selectors,
|
|
1028
|
+
_params,
|
|
1029
|
+
options,
|
|
1030
|
+
world,
|
|
1031
|
+
value: values.toString(),
|
|
1032
|
+
type: Types.SELECT,
|
|
1033
|
+
text: `Select option: ${values}`,
|
|
1034
|
+
operation: "selectOption",
|
|
1035
|
+
log: "***** select option " + selectors.element_name + " *****\n",
|
|
1036
|
+
};
|
|
958
1037
|
try {
|
|
959
|
-
|
|
960
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1038
|
+
await _preCommand(state, this);
|
|
961
1039
|
try {
|
|
962
|
-
await
|
|
963
|
-
await element.selectOption(values);
|
|
1040
|
+
await state.element.selectOption(values);
|
|
964
1041
|
}
|
|
965
1042
|
catch (e) {
|
|
966
1043
|
//await this.closeUnexpectedPopups();
|
|
967
|
-
info.log += "selectOption failed, will try force" + "\n";
|
|
968
|
-
await element.selectOption(values, { timeout: 10000, force: true });
|
|
1044
|
+
state.info.log += "selectOption failed, will try force" + "\n";
|
|
1045
|
+
await state.element.selectOption(values, { timeout: 10000, force: true });
|
|
969
1046
|
}
|
|
970
1047
|
await this.waitForPageLoad();
|
|
971
|
-
return info;
|
|
1048
|
+
return state.info;
|
|
972
1049
|
}
|
|
973
1050
|
catch (e) {
|
|
974
|
-
|
|
975
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
976
|
-
info.screenshotPath = screenshotPath;
|
|
977
|
-
Object.assign(e, { info: info });
|
|
978
|
-
this.logger.info("click failed, will try next selector");
|
|
979
|
-
error = e;
|
|
980
|
-
throw e;
|
|
1051
|
+
await _commandError(state, e, this);
|
|
981
1052
|
}
|
|
982
1053
|
finally {
|
|
983
|
-
|
|
984
|
-
this._reportToWorld(world, {
|
|
985
|
-
element_name: selectors.element_name,
|
|
986
|
-
type: Types.SELECT,
|
|
987
|
-
text: `Select option: ${values}`,
|
|
988
|
-
value: values.toString(),
|
|
989
|
-
screenshotId,
|
|
990
|
-
result: error
|
|
991
|
-
? {
|
|
992
|
-
status: "FAILED",
|
|
993
|
-
startTime,
|
|
994
|
-
endTime,
|
|
995
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
996
|
-
}
|
|
997
|
-
: {
|
|
998
|
-
status: "PASSED",
|
|
999
|
-
startTime,
|
|
1000
|
-
endTime,
|
|
1001
|
-
},
|
|
1002
|
-
info: info,
|
|
1003
|
-
});
|
|
1054
|
+
_commandFinally(state, this);
|
|
1004
1055
|
}
|
|
1005
1056
|
}
|
|
1006
1057
|
async type(_value, _params = null, options = {}, world = null) {
|
|
1007
|
-
const
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1058
|
+
const state = {
|
|
1059
|
+
value: _value,
|
|
1060
|
+
_params,
|
|
1061
|
+
options,
|
|
1062
|
+
world,
|
|
1063
|
+
locate: false,
|
|
1064
|
+
scroll: false,
|
|
1065
|
+
highlight: false,
|
|
1066
|
+
type: Types.TYPE_PRESS,
|
|
1067
|
+
text: `Type value: ${_value}`,
|
|
1068
|
+
operation: "type",
|
|
1069
|
+
log: "",
|
|
1070
|
+
};
|
|
1016
1071
|
try {
|
|
1017
|
-
|
|
1018
|
-
const valueSegment =
|
|
1072
|
+
await _preCommand(state, this);
|
|
1073
|
+
const valueSegment = state.value.split("&&");
|
|
1019
1074
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1020
1075
|
if (i > 0) {
|
|
1021
1076
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1035,134 +1090,76 @@ class StableBrowser {
|
|
|
1035
1090
|
await this.page.keyboard.type(value);
|
|
1036
1091
|
}
|
|
1037
1092
|
}
|
|
1038
|
-
return info;
|
|
1093
|
+
return state.info;
|
|
1039
1094
|
}
|
|
1040
1095
|
catch (e) {
|
|
1041
|
-
|
|
1042
|
-
this.logger.error("type failed " + info.log);
|
|
1043
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1044
|
-
info.screenshotPath = screenshotPath;
|
|
1045
|
-
Object.assign(e, { info: info });
|
|
1046
|
-
error = e;
|
|
1047
|
-
throw e;
|
|
1096
|
+
await _commandError(state, e, this);
|
|
1048
1097
|
}
|
|
1049
1098
|
finally {
|
|
1050
|
-
|
|
1051
|
-
this._reportToWorld(world, {
|
|
1052
|
-
type: Types.TYPE_PRESS,
|
|
1053
|
-
screenshotId,
|
|
1054
|
-
value: _value,
|
|
1055
|
-
text: `type value: ${_value}`,
|
|
1056
|
-
result: error
|
|
1057
|
-
? {
|
|
1058
|
-
status: "FAILED",
|
|
1059
|
-
startTime,
|
|
1060
|
-
endTime,
|
|
1061
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1062
|
-
}
|
|
1063
|
-
: {
|
|
1064
|
-
status: "PASSED",
|
|
1065
|
-
startTime,
|
|
1066
|
-
endTime,
|
|
1067
|
-
},
|
|
1068
|
-
info: info,
|
|
1069
|
-
});
|
|
1099
|
+
_commandFinally(state, this);
|
|
1070
1100
|
}
|
|
1071
1101
|
}
|
|
1072
1102
|
async setInputValue(selectors, value, _params = null, options = {}, world = null) {
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
let screenshotPath = null;
|
|
1103
|
+
const state = {
|
|
1104
|
+
selectors,
|
|
1105
|
+
_params,
|
|
1106
|
+
value,
|
|
1107
|
+
options,
|
|
1108
|
+
world,
|
|
1109
|
+
type: Types.SET_INPUT,
|
|
1110
|
+
text: `Set input value`,
|
|
1111
|
+
operation: "setInputValue",
|
|
1112
|
+
log: "***** set input value " + selectors.element_name + " *****\n",
|
|
1113
|
+
};
|
|
1085
1114
|
try {
|
|
1086
|
-
|
|
1087
|
-
let
|
|
1088
|
-
await this.scrollIfNeeded(element, info);
|
|
1089
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1090
|
-
await this._highlightElements(element);
|
|
1115
|
+
await _preCommand(state, this);
|
|
1116
|
+
let value = await this._replaceWithLocalData(state.value, this);
|
|
1091
1117
|
try {
|
|
1092
|
-
await element.evaluateHandle((el, value) => {
|
|
1118
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1093
1119
|
el.value = value;
|
|
1094
1120
|
}, value);
|
|
1095
1121
|
}
|
|
1096
1122
|
catch (error) {
|
|
1097
1123
|
this.logger.error("setInputValue failed, will try again");
|
|
1098
|
-
|
|
1099
|
-
info.
|
|
1100
|
-
|
|
1101
|
-
await element.evaluateHandle((el, value) => {
|
|
1124
|
+
await _screenshot(state, this);
|
|
1125
|
+
Object.assign(error, { info: state.info });
|
|
1126
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1102
1127
|
el.value = value;
|
|
1103
1128
|
});
|
|
1104
1129
|
}
|
|
1105
1130
|
}
|
|
1106
1131
|
catch (e) {
|
|
1107
|
-
|
|
1108
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1109
|
-
info.screenshotPath = screenshotPath;
|
|
1110
|
-
Object.assign(e, { info: info });
|
|
1111
|
-
error = e;
|
|
1112
|
-
throw e;
|
|
1132
|
+
await _commandError(state, e, this);
|
|
1113
1133
|
}
|
|
1114
1134
|
finally {
|
|
1115
|
-
|
|
1116
|
-
this._reportToWorld(world, {
|
|
1117
|
-
element_name: selectors.element_name,
|
|
1118
|
-
type: Types.SET_INPUT,
|
|
1119
|
-
text: `Set input value`,
|
|
1120
|
-
value: value,
|
|
1121
|
-
screenshotId,
|
|
1122
|
-
result: error
|
|
1123
|
-
? {
|
|
1124
|
-
status: "FAILED",
|
|
1125
|
-
startTime,
|
|
1126
|
-
endTime,
|
|
1127
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1128
|
-
}
|
|
1129
|
-
: {
|
|
1130
|
-
status: "PASSED",
|
|
1131
|
-
startTime,
|
|
1132
|
-
endTime,
|
|
1133
|
-
},
|
|
1134
|
-
info: info,
|
|
1135
|
-
});
|
|
1135
|
+
_commandFinally(state, this);
|
|
1136
1136
|
}
|
|
1137
1137
|
}
|
|
1138
1138
|
async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1139
|
+
const state = {
|
|
1140
|
+
selectors,
|
|
1141
|
+
_params,
|
|
1142
|
+
value: await this._replaceWithLocalData(value, this),
|
|
1143
|
+
options,
|
|
1144
|
+
world,
|
|
1145
|
+
type: Types.SET_DATE_TIME,
|
|
1146
|
+
text: `Set date time value: ${value}`,
|
|
1147
|
+
operation: "setDateTime",
|
|
1148
|
+
log: "***** set date time value " + selectors.element_name + " *****\n",
|
|
1149
|
+
throwError: false,
|
|
1150
|
+
};
|
|
1149
1151
|
try {
|
|
1150
|
-
|
|
1151
|
-
let element = await this._locate(selectors, info, _params);
|
|
1152
|
-
//insert red border around the element
|
|
1153
|
-
await this.scrollIfNeeded(element, info);
|
|
1154
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1155
|
-
await this._highlightElements(element);
|
|
1152
|
+
await _preCommand(state, this);
|
|
1156
1153
|
try {
|
|
1157
|
-
await element.click();
|
|
1154
|
+
await state.element.click();
|
|
1158
1155
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1159
1156
|
if (format) {
|
|
1160
|
-
value = dayjs(value).format(format);
|
|
1161
|
-
await element.fill(value);
|
|
1157
|
+
state.value = dayjs(state.value).format(format);
|
|
1158
|
+
await state.element.fill(state.value);
|
|
1162
1159
|
}
|
|
1163
1160
|
else {
|
|
1164
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1165
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1161
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1162
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1166
1163
|
el.value = ""; // clear input
|
|
1167
1164
|
el.value = dateTimeValue;
|
|
1168
1165
|
}, dateTimeValue);
|
|
@@ -1175,20 +1172,19 @@ class StableBrowser {
|
|
|
1175
1172
|
}
|
|
1176
1173
|
catch (err) {
|
|
1177
1174
|
//await this.closeUnexpectedPopups();
|
|
1178
|
-
this.logger.error("setting date time input failed " + JSON.stringify(info));
|
|
1175
|
+
this.logger.error("setting date time input failed " + JSON.stringify(state.info));
|
|
1179
1176
|
this.logger.info("Trying again");
|
|
1180
|
-
|
|
1181
|
-
info.
|
|
1182
|
-
Object.assign(err, { info: info });
|
|
1177
|
+
await _screenshot(state, this);
|
|
1178
|
+
Object.assign(err, { info: state.info });
|
|
1183
1179
|
await element.click();
|
|
1184
1180
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1185
1181
|
if (format) {
|
|
1186
|
-
value = dayjs(value).format(format);
|
|
1187
|
-
await element.fill(value);
|
|
1182
|
+
state.value = dayjs(state.value).format(format);
|
|
1183
|
+
await state.element.fill(state.value);
|
|
1188
1184
|
}
|
|
1189
1185
|
else {
|
|
1190
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1191
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1186
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1187
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1192
1188
|
el.value = ""; // clear input
|
|
1193
1189
|
el.value = dateTimeValue;
|
|
1194
1190
|
}, dateTimeValue);
|
|
@@ -1201,61 +1197,39 @@ class StableBrowser {
|
|
|
1201
1197
|
}
|
|
1202
1198
|
}
|
|
1203
1199
|
catch (e) {
|
|
1204
|
-
|
|
1205
|
-
throw e;
|
|
1200
|
+
await _commandError(state, e, this);
|
|
1206
1201
|
}
|
|
1207
1202
|
finally {
|
|
1208
|
-
|
|
1209
|
-
this._reportToWorld(world, {
|
|
1210
|
-
element_name: selectors.element_name,
|
|
1211
|
-
type: Types.SET_DATE_TIME,
|
|
1212
|
-
screenshotId,
|
|
1213
|
-
value: value,
|
|
1214
|
-
text: `setDateTime input with value: ${value}`,
|
|
1215
|
-
result: error
|
|
1216
|
-
? {
|
|
1217
|
-
status: "FAILED",
|
|
1218
|
-
startTime,
|
|
1219
|
-
endTime,
|
|
1220
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1221
|
-
}
|
|
1222
|
-
: {
|
|
1223
|
-
status: "PASSED",
|
|
1224
|
-
startTime,
|
|
1225
|
-
endTime,
|
|
1226
|
-
},
|
|
1227
|
-
info: info,
|
|
1228
|
-
});
|
|
1203
|
+
_commandFinally(state, this);
|
|
1229
1204
|
}
|
|
1230
1205
|
}
|
|
1231
1206
|
async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
|
|
1232
1207
|
_value = unEscapeString(_value);
|
|
1233
|
-
this._validateSelectors(selectors);
|
|
1234
|
-
const startTime = Date.now();
|
|
1235
|
-
let error = null;
|
|
1236
|
-
let screenshotId = null;
|
|
1237
|
-
let screenshotPath = null;
|
|
1238
|
-
const info = {};
|
|
1239
|
-
info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
|
|
1240
|
-
info.operation = "clickType";
|
|
1241
|
-
info.selectors = selectors;
|
|
1242
1208
|
const newValue = await this._replaceWithLocalData(_value, world);
|
|
1209
|
+
const state = {
|
|
1210
|
+
selectors,
|
|
1211
|
+
_params,
|
|
1212
|
+
value: newValue,
|
|
1213
|
+
originalValue: _value,
|
|
1214
|
+
options,
|
|
1215
|
+
world,
|
|
1216
|
+
type: Types.FILL,
|
|
1217
|
+
text: `Click type input with value: ${_value}`,
|
|
1218
|
+
operation: "clickType",
|
|
1219
|
+
log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
|
|
1220
|
+
};
|
|
1243
1221
|
if (newValue !== _value) {
|
|
1244
1222
|
//this.logger.info(_value + "=" + newValue);
|
|
1245
1223
|
_value = newValue;
|
|
1246
1224
|
}
|
|
1247
|
-
info.value = _value;
|
|
1248
1225
|
try {
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
await this.scrollIfNeeded(element, info);
|
|
1252
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1253
|
-
await this._highlightElements(element);
|
|
1226
|
+
await _preCommand(state, this);
|
|
1227
|
+
state.info.value = _value;
|
|
1254
1228
|
if (options === null || options === undefined || !options.press) {
|
|
1255
1229
|
try {
|
|
1256
|
-
let currentValue = await element.inputValue();
|
|
1230
|
+
let currentValue = await state.element.inputValue();
|
|
1257
1231
|
if (currentValue) {
|
|
1258
|
-
await element.fill("");
|
|
1232
|
+
await state.element.fill("");
|
|
1259
1233
|
}
|
|
1260
1234
|
}
|
|
1261
1235
|
catch (e) {
|
|
@@ -1264,22 +1238,22 @@ class StableBrowser {
|
|
|
1264
1238
|
}
|
|
1265
1239
|
if (options === null || options === undefined || options.press) {
|
|
1266
1240
|
try {
|
|
1267
|
-
await element.click({ timeout: 5000 });
|
|
1241
|
+
await state.element.click({ timeout: 5000 });
|
|
1268
1242
|
}
|
|
1269
1243
|
catch (e) {
|
|
1270
|
-
await element.dispatchEvent("click");
|
|
1244
|
+
await state.element.dispatchEvent("click");
|
|
1271
1245
|
}
|
|
1272
1246
|
}
|
|
1273
1247
|
else {
|
|
1274
1248
|
try {
|
|
1275
|
-
await element.focus();
|
|
1249
|
+
await state.element.focus();
|
|
1276
1250
|
}
|
|
1277
1251
|
catch (e) {
|
|
1278
|
-
await element.dispatchEvent("focus");
|
|
1252
|
+
await state.element.dispatchEvent("focus");
|
|
1279
1253
|
}
|
|
1280
1254
|
}
|
|
1281
1255
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1282
|
-
const valueSegment =
|
|
1256
|
+
const valueSegment = state.value.split("&&");
|
|
1283
1257
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1284
1258
|
if (i > 0) {
|
|
1285
1259
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1299,13 +1273,14 @@ class StableBrowser {
|
|
|
1299
1273
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1300
1274
|
}
|
|
1301
1275
|
}
|
|
1276
|
+
await _screenshot(state, this);
|
|
1302
1277
|
if (enter === true) {
|
|
1303
1278
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1304
1279
|
await this.page.keyboard.press("Enter");
|
|
1305
1280
|
await this.waitForPageLoad();
|
|
1306
1281
|
}
|
|
1307
1282
|
else if (enter === false) {
|
|
1308
|
-
await element.dispatchEvent("change");
|
|
1283
|
+
await state.element.dispatchEvent("change");
|
|
1309
1284
|
//await this.page.keyboard.press("Tab");
|
|
1310
1285
|
}
|
|
1311
1286
|
else {
|
|
@@ -1314,104 +1289,50 @@ class StableBrowser {
|
|
|
1314
1289
|
await this.waitForPageLoad();
|
|
1315
1290
|
}
|
|
1316
1291
|
}
|
|
1317
|
-
return info;
|
|
1292
|
+
return state.info;
|
|
1318
1293
|
}
|
|
1319
1294
|
catch (e) {
|
|
1320
|
-
|
|
1321
|
-
this.logger.error("fill failed " + JSON.stringify(info));
|
|
1322
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1323
|
-
info.screenshotPath = screenshotPath;
|
|
1324
|
-
Object.assign(e, { info: info });
|
|
1325
|
-
error = e;
|
|
1326
|
-
throw e;
|
|
1295
|
+
await _commandError(state, e, this);
|
|
1327
1296
|
}
|
|
1328
1297
|
finally {
|
|
1329
|
-
|
|
1330
|
-
this._reportToWorld(world, {
|
|
1331
|
-
element_name: selectors.element_name,
|
|
1332
|
-
type: Types.FILL,
|
|
1333
|
-
screenshotId,
|
|
1334
|
-
value: _value,
|
|
1335
|
-
text: `clickType input with value: ${_value}`,
|
|
1336
|
-
result: error
|
|
1337
|
-
? {
|
|
1338
|
-
status: "FAILED",
|
|
1339
|
-
startTime,
|
|
1340
|
-
endTime,
|
|
1341
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1342
|
-
}
|
|
1343
|
-
: {
|
|
1344
|
-
status: "PASSED",
|
|
1345
|
-
startTime,
|
|
1346
|
-
endTime,
|
|
1347
|
-
},
|
|
1348
|
-
info: info,
|
|
1349
|
-
});
|
|
1350
|
-
}
|
|
1351
|
-
}
|
|
1352
|
-
async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
|
|
1353
|
-
this._validateSelectors(selectors);
|
|
1354
|
-
value = unEscapeString(value);
|
|
1355
|
-
const startTime = Date.now();
|
|
1356
|
-
let error = null;
|
|
1357
|
-
let screenshotId = null;
|
|
1358
|
-
let screenshotPath = null;
|
|
1359
|
-
const info = {};
|
|
1360
|
-
info.log = "***** fill on " + selectors.element_name + " with value " + value + "*****\n";
|
|
1361
|
-
info.operation = "fill";
|
|
1362
|
-
info.selectors = selectors;
|
|
1363
|
-
info.value = value;
|
|
1364
|
-
try {
|
|
1365
|
-
let element = await this._locate(selectors, info, _params);
|
|
1366
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1367
|
-
await this._highlightElements(element);
|
|
1368
|
-
await element.fill(value);
|
|
1369
|
-
await element.dispatchEvent("change");
|
|
1370
|
-
if (enter) {
|
|
1371
|
-
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1372
|
-
await this.page.keyboard.press("Enter");
|
|
1373
|
-
}
|
|
1374
|
-
await this.waitForPageLoad();
|
|
1375
|
-
return info;
|
|
1298
|
+
_commandFinally(state, this);
|
|
1376
1299
|
}
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
info: info,
|
|
1407
|
-
});
|
|
1300
|
+
}
|
|
1301
|
+
async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
|
|
1302
|
+
const state = {
|
|
1303
|
+
selectors,
|
|
1304
|
+
_params,
|
|
1305
|
+
value: unEscapeString(value),
|
|
1306
|
+
options,
|
|
1307
|
+
world,
|
|
1308
|
+
type: Types.FILL,
|
|
1309
|
+
text: `Fill input with value: ${value}`,
|
|
1310
|
+
operation: "fill",
|
|
1311
|
+
log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
|
|
1312
|
+
};
|
|
1313
|
+
try {
|
|
1314
|
+
await _preCommand(state, this);
|
|
1315
|
+
await state.element.fill(value);
|
|
1316
|
+
await state.element.dispatchEvent("change");
|
|
1317
|
+
if (enter) {
|
|
1318
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1319
|
+
await this.page.keyboard.press("Enter");
|
|
1320
|
+
}
|
|
1321
|
+
await this.waitForPageLoad();
|
|
1322
|
+
return state.info;
|
|
1323
|
+
}
|
|
1324
|
+
catch (e) {
|
|
1325
|
+
await _commandError(state, e, this);
|
|
1326
|
+
}
|
|
1327
|
+
finally {
|
|
1328
|
+
_commandFinally(state, this);
|
|
1408
1329
|
}
|
|
1409
1330
|
}
|
|
1410
1331
|
async getText(selectors, _params = null, options = {}, info = {}, world = null) {
|
|
1411
1332
|
return await this._getText(selectors, 0, _params, options, info, world);
|
|
1412
1333
|
}
|
|
1413
1334
|
async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
|
|
1414
|
-
|
|
1335
|
+
_validateSelectors(selectors);
|
|
1415
1336
|
let screenshotId = null;
|
|
1416
1337
|
let screenshotPath = null;
|
|
1417
1338
|
if (!info.log) {
|
|
@@ -1455,166 +1376,124 @@ class StableBrowser {
|
|
|
1455
1376
|
}
|
|
1456
1377
|
}
|
|
1457
1378
|
async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
|
|
1458
|
-
var _a;
|
|
1459
|
-
this._validateSelectors(selectors);
|
|
1460
1379
|
if (!pattern) {
|
|
1461
1380
|
throw new Error("pattern is null");
|
|
1462
1381
|
}
|
|
1463
1382
|
if (!text) {
|
|
1464
1383
|
throw new Error("text is null");
|
|
1465
1384
|
}
|
|
1385
|
+
const state = {
|
|
1386
|
+
selectors,
|
|
1387
|
+
_params,
|
|
1388
|
+
pattern,
|
|
1389
|
+
value: pattern,
|
|
1390
|
+
options,
|
|
1391
|
+
world,
|
|
1392
|
+
locate: false,
|
|
1393
|
+
scroll: false,
|
|
1394
|
+
screenshot: false,
|
|
1395
|
+
highlight: false,
|
|
1396
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1397
|
+
text: `Verify element contains pattern: ${pattern}`,
|
|
1398
|
+
operation: "containsPattern",
|
|
1399
|
+
log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
|
|
1400
|
+
};
|
|
1466
1401
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1467
1402
|
if (newValue !== text) {
|
|
1468
1403
|
this.logger.info(text + "=" + newValue);
|
|
1469
1404
|
text = newValue;
|
|
1470
1405
|
}
|
|
1471
|
-
const startTime = Date.now();
|
|
1472
|
-
let error = null;
|
|
1473
|
-
let screenshotId = null;
|
|
1474
|
-
let screenshotPath = null;
|
|
1475
|
-
const info = {};
|
|
1476
|
-
info.log =
|
|
1477
|
-
"***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
|
|
1478
|
-
info.operation = "containsPattern";
|
|
1479
|
-
info.selectors = selectors;
|
|
1480
|
-
info.value = text;
|
|
1481
|
-
info.pattern = pattern;
|
|
1482
1406
|
let foundObj = null;
|
|
1483
1407
|
try {
|
|
1484
|
-
|
|
1408
|
+
await _preCommand(state, this);
|
|
1409
|
+
state.info.pattern = pattern;
|
|
1410
|
+
foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
|
|
1485
1411
|
if (foundObj && foundObj.element) {
|
|
1486
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1412
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1487
1413
|
}
|
|
1488
|
-
|
|
1414
|
+
await _screenshot(state, this);
|
|
1489
1415
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1490
1416
|
pattern = pattern.replace("{text}", escapedText);
|
|
1491
1417
|
let regex = new RegExp(pattern, "im");
|
|
1492
|
-
if (!regex.test(foundObj
|
|
1493
|
-
info.foundText = foundObj
|
|
1418
|
+
if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
|
|
1419
|
+
state.info.foundText = foundObj?.text;
|
|
1494
1420
|
throw new Error("element doesn't contain text " + text);
|
|
1495
1421
|
}
|
|
1496
|
-
return info;
|
|
1422
|
+
return state.info;
|
|
1497
1423
|
}
|
|
1498
1424
|
catch (e) {
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
|
|
1502
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1503
|
-
info.screenshotPath = screenshotPath;
|
|
1504
|
-
Object.assign(e, { info: info });
|
|
1505
|
-
error = e;
|
|
1506
|
-
throw e;
|
|
1425
|
+
this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
|
|
1426
|
+
await _commandError(state, e, this);
|
|
1507
1427
|
}
|
|
1508
1428
|
finally {
|
|
1509
|
-
|
|
1510
|
-
this._reportToWorld(world, {
|
|
1511
|
-
element_name: selectors.element_name,
|
|
1512
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1513
|
-
value: pattern,
|
|
1514
|
-
text: `Verify element contains pattern: ${pattern}`,
|
|
1515
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1516
|
-
result: error
|
|
1517
|
-
? {
|
|
1518
|
-
status: "FAILED",
|
|
1519
|
-
startTime,
|
|
1520
|
-
endTime,
|
|
1521
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1522
|
-
}
|
|
1523
|
-
: {
|
|
1524
|
-
status: "PASSED",
|
|
1525
|
-
startTime,
|
|
1526
|
-
endTime,
|
|
1527
|
-
},
|
|
1528
|
-
info: info,
|
|
1529
|
-
});
|
|
1429
|
+
_commandFinally(state, this);
|
|
1530
1430
|
}
|
|
1531
1431
|
}
|
|
1532
1432
|
async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1433
|
+
const state = {
|
|
1434
|
+
selectors,
|
|
1435
|
+
_params,
|
|
1436
|
+
value: text,
|
|
1437
|
+
options,
|
|
1438
|
+
world,
|
|
1439
|
+
locate: false,
|
|
1440
|
+
scroll: false,
|
|
1441
|
+
screenshot: false,
|
|
1442
|
+
highlight: false,
|
|
1443
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1444
|
+
text: `Verify element contains text: ${text}`,
|
|
1445
|
+
operation: "containsText",
|
|
1446
|
+
log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
|
|
1447
|
+
};
|
|
1536
1448
|
if (!text) {
|
|
1537
1449
|
throw new Error("text is null");
|
|
1538
1450
|
}
|
|
1539
|
-
|
|
1540
|
-
let error = null;
|
|
1541
|
-
let screenshotId = null;
|
|
1542
|
-
let screenshotPath = null;
|
|
1543
|
-
const info = {};
|
|
1544
|
-
info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
|
|
1545
|
-
info.operation = "containsText";
|
|
1546
|
-
info.selectors = selectors;
|
|
1451
|
+
text = unEscapeString(text);
|
|
1547
1452
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1548
1453
|
if (newValue !== text) {
|
|
1549
1454
|
this.logger.info(text + "=" + newValue);
|
|
1550
1455
|
text = newValue;
|
|
1551
1456
|
}
|
|
1552
|
-
info.value = text;
|
|
1553
1457
|
let foundObj = null;
|
|
1554
1458
|
try {
|
|
1555
|
-
|
|
1459
|
+
await _preCommand(state, this);
|
|
1460
|
+
foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
|
|
1556
1461
|
if (foundObj && foundObj.element) {
|
|
1557
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1462
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1558
1463
|
}
|
|
1559
|
-
|
|
1464
|
+
await _screenshot(state, this);
|
|
1560
1465
|
const dateAlternatives = findDateAlternatives(text);
|
|
1561
1466
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1562
1467
|
if (dateAlternatives.date) {
|
|
1563
1468
|
for (let i = 0; i < dateAlternatives.dates.length; i++) {
|
|
1564
|
-
if (
|
|
1565
|
-
|
|
1566
|
-
return info;
|
|
1469
|
+
if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
|
|
1470
|
+
foundObj?.value?.includes(dateAlternatives.dates[i])) {
|
|
1471
|
+
return state.info;
|
|
1567
1472
|
}
|
|
1568
1473
|
}
|
|
1569
1474
|
throw new Error("element doesn't contain text " + text);
|
|
1570
1475
|
}
|
|
1571
1476
|
else if (numberAlternatives.number) {
|
|
1572
1477
|
for (let i = 0; i < numberAlternatives.numbers.length; i++) {
|
|
1573
|
-
if (
|
|
1574
|
-
|
|
1575
|
-
return info;
|
|
1478
|
+
if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
|
|
1479
|
+
foundObj?.value?.includes(numberAlternatives.numbers[i])) {
|
|
1480
|
+
return state.info;
|
|
1576
1481
|
}
|
|
1577
1482
|
}
|
|
1578
1483
|
throw new Error("element doesn't contain text " + text);
|
|
1579
1484
|
}
|
|
1580
|
-
else if (!
|
|
1581
|
-
info.foundText = foundObj
|
|
1582
|
-
info.value = foundObj
|
|
1485
|
+
else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
|
|
1486
|
+
state.info.foundText = foundObj?.text;
|
|
1487
|
+
state.info.value = foundObj?.value;
|
|
1583
1488
|
throw new Error("element doesn't contain text " + text);
|
|
1584
1489
|
}
|
|
1585
|
-
return info;
|
|
1490
|
+
return state.info;
|
|
1586
1491
|
}
|
|
1587
1492
|
catch (e) {
|
|
1588
|
-
|
|
1589
|
-
this.logger.error("verify element contains text failed " + info.log);
|
|
1590
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1591
|
-
info.screenshotPath = screenshotPath;
|
|
1592
|
-
Object.assign(e, { info: info });
|
|
1593
|
-
error = e;
|
|
1594
|
-
throw e;
|
|
1493
|
+
await _commandError(state, e, this);
|
|
1595
1494
|
}
|
|
1596
1495
|
finally {
|
|
1597
|
-
|
|
1598
|
-
this._reportToWorld(world, {
|
|
1599
|
-
element_name: selectors.element_name,
|
|
1600
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1601
|
-
text: `Verify element contains text: ${text}`,
|
|
1602
|
-
value: text,
|
|
1603
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1604
|
-
result: error
|
|
1605
|
-
? {
|
|
1606
|
-
status: "FAILED",
|
|
1607
|
-
startTime,
|
|
1608
|
-
endTime,
|
|
1609
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1610
|
-
}
|
|
1611
|
-
: {
|
|
1612
|
-
status: "PASSED",
|
|
1613
|
-
startTime,
|
|
1614
|
-
endTime,
|
|
1615
|
-
},
|
|
1616
|
-
info: info,
|
|
1617
|
-
});
|
|
1496
|
+
_commandFinally(state, this);
|
|
1618
1497
|
}
|
|
1619
1498
|
}
|
|
1620
1499
|
_getDataFile(world = null) {
|
|
@@ -1843,7 +1722,6 @@ class StableBrowser {
|
|
|
1843
1722
|
}
|
|
1844
1723
|
async takeScreenshot(screenshotPath) {
|
|
1845
1724
|
const playContext = this.context.playContext;
|
|
1846
|
-
const client = await playContext.newCDPSession(this.page);
|
|
1847
1725
|
// Using CDP to capture the screenshot
|
|
1848
1726
|
const viewportWidth = Math.max(...(await this.page.evaluate(() => [
|
|
1849
1727
|
document.body.scrollWidth,
|
|
@@ -1853,164 +1731,105 @@ class StableBrowser {
|
|
|
1853
1731
|
document.body.clientWidth,
|
|
1854
1732
|
document.documentElement.clientWidth,
|
|
1855
1733
|
])));
|
|
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
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
await client.detach();
|
|
1734
|
+
let screenshotBuffer = null;
|
|
1735
|
+
if (this.context.browserName === "chromium") {
|
|
1736
|
+
const client = await playContext.newCDPSession(this.page);
|
|
1737
|
+
const { data } = await client.send("Page.captureScreenshot", {
|
|
1738
|
+
format: "png",
|
|
1739
|
+
// clip: {
|
|
1740
|
+
// x: 0,
|
|
1741
|
+
// y: 0,
|
|
1742
|
+
// width: viewportWidth,
|
|
1743
|
+
// height: viewportHeight,
|
|
1744
|
+
// scale: 1,
|
|
1745
|
+
// },
|
|
1746
|
+
});
|
|
1747
|
+
await client.detach();
|
|
1748
|
+
if (!screenshotPath) {
|
|
1749
|
+
return data;
|
|
1750
|
+
}
|
|
1751
|
+
screenshotBuffer = Buffer.from(data, "base64");
|
|
1752
|
+
}
|
|
1753
|
+
else {
|
|
1754
|
+
screenshotBuffer = await this.page.screenshot();
|
|
1755
|
+
}
|
|
1756
|
+
let image = await Jimp.read(screenshotBuffer);
|
|
1757
|
+
// Get the image dimensions
|
|
1758
|
+
const { width, height } = image.bitmap;
|
|
1759
|
+
const resizeRatio = viewportWidth / width;
|
|
1760
|
+
// Resize the image to fit within the viewport dimensions without enlarging
|
|
1761
|
+
if (width > viewportWidth) {
|
|
1762
|
+
image = image.resize({ w: viewportWidth, h: height * resizeRatio }); // Resize the image while maintaining aspect ratio
|
|
1763
|
+
await image.write(screenshotPath);
|
|
1764
|
+
}
|
|
1765
|
+
else {
|
|
1766
|
+
fs.writeFileSync(screenshotPath, screenshotBuffer);
|
|
1767
|
+
}
|
|
1891
1768
|
}
|
|
1892
1769
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1770
|
+
const state = {
|
|
1771
|
+
selectors,
|
|
1772
|
+
_params,
|
|
1773
|
+
options,
|
|
1774
|
+
world,
|
|
1775
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1776
|
+
text: `Verify element exists in page`,
|
|
1777
|
+
operation: "verifyElementExistInPage",
|
|
1778
|
+
log: "***** verify element " + selectors.element_name + " exists in page *****\n",
|
|
1779
|
+
};
|
|
1898
1780
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1899
|
-
const info = {};
|
|
1900
|
-
info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
|
|
1901
|
-
info.operation = "verify";
|
|
1902
|
-
info.selectors = selectors;
|
|
1903
1781
|
try {
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
}
|
|
1908
|
-
await this._highlightElements(element);
|
|
1909
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1910
|
-
await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
1911
|
-
return info;
|
|
1782
|
+
await _preCommand(state, this);
|
|
1783
|
+
await expect(state.element).toHaveCount(1, { timeout: 10000 });
|
|
1784
|
+
return state.info;
|
|
1912
1785
|
}
|
|
1913
1786
|
catch (e) {
|
|
1914
|
-
|
|
1915
|
-
this.logger.error("verify failed " + info.log);
|
|
1916
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1917
|
-
info.screenshotPath = screenshotPath;
|
|
1918
|
-
Object.assign(e, { info: info });
|
|
1919
|
-
error = e;
|
|
1920
|
-
throw e;
|
|
1787
|
+
await _commandError(state, e, this);
|
|
1921
1788
|
}
|
|
1922
1789
|
finally {
|
|
1923
|
-
|
|
1924
|
-
this._reportToWorld(world, {
|
|
1925
|
-
element_name: selectors.element_name,
|
|
1926
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1927
|
-
text: "Verify element exists in page",
|
|
1928
|
-
screenshotId,
|
|
1929
|
-
result: error
|
|
1930
|
-
? {
|
|
1931
|
-
status: "FAILED",
|
|
1932
|
-
startTime,
|
|
1933
|
-
endTime,
|
|
1934
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1935
|
-
}
|
|
1936
|
-
: {
|
|
1937
|
-
status: "PASSED",
|
|
1938
|
-
startTime,
|
|
1939
|
-
endTime,
|
|
1940
|
-
},
|
|
1941
|
-
info: info,
|
|
1942
|
-
});
|
|
1790
|
+
_commandFinally(state, this);
|
|
1943
1791
|
}
|
|
1944
1792
|
}
|
|
1945
1793
|
async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1794
|
+
const state = {
|
|
1795
|
+
selectors,
|
|
1796
|
+
_params,
|
|
1797
|
+
attribute,
|
|
1798
|
+
variable,
|
|
1799
|
+
options,
|
|
1800
|
+
world,
|
|
1801
|
+
type: Types.EXTRACT,
|
|
1802
|
+
text: `Extract attribute from element`,
|
|
1803
|
+
operation: "extractAttribute",
|
|
1804
|
+
log: "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1805
|
+
};
|
|
1951
1806
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1952
|
-
const info = {};
|
|
1953
|
-
info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
|
|
1954
|
-
info.operation = "extract";
|
|
1955
|
-
info.selectors = selectors;
|
|
1956
1807
|
try {
|
|
1957
|
-
|
|
1958
|
-
await this._highlightElements(element);
|
|
1959
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1808
|
+
await _preCommand(state, this);
|
|
1960
1809
|
switch (attribute) {
|
|
1961
1810
|
case "inner_text":
|
|
1962
|
-
|
|
1811
|
+
state.value = await state.element.innerText();
|
|
1963
1812
|
break;
|
|
1964
1813
|
case "href":
|
|
1965
|
-
|
|
1814
|
+
state.value = await state.element.getAttribute("href");
|
|
1966
1815
|
break;
|
|
1967
1816
|
case "value":
|
|
1968
|
-
|
|
1817
|
+
state.value = await state.element.inputValue();
|
|
1969
1818
|
break;
|
|
1970
1819
|
default:
|
|
1971
|
-
|
|
1820
|
+
state.value = await state.element.getAttribute(attribute);
|
|
1972
1821
|
break;
|
|
1973
1822
|
}
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
this.setTestData({ [variable]: info.value }, world);
|
|
1979
|
-
this.logger.info("set test data: " + variable + "=" + info.value);
|
|
1980
|
-
return info;
|
|
1823
|
+
state.info.value = state.value;
|
|
1824
|
+
this.setTestData({ [variable]: state.value }, world);
|
|
1825
|
+
this.logger.info("set test data: " + variable + "=" + state.value);
|
|
1826
|
+
return state.info;
|
|
1981
1827
|
}
|
|
1982
1828
|
catch (e) {
|
|
1983
|
-
|
|
1984
|
-
this.logger.error("extract failed " + info.log);
|
|
1985
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1986
|
-
info.screenshotPath = screenshotPath;
|
|
1987
|
-
Object.assign(e, { info: info });
|
|
1988
|
-
error = e;
|
|
1989
|
-
throw e;
|
|
1829
|
+
await _commandError(state, e, this);
|
|
1990
1830
|
}
|
|
1991
1831
|
finally {
|
|
1992
|
-
|
|
1993
|
-
this._reportToWorld(world, {
|
|
1994
|
-
element_name: selectors.element_name,
|
|
1995
|
-
type: Types.EXTRACT_ATTRIBUTE,
|
|
1996
|
-
variable: variable,
|
|
1997
|
-
value: info.value,
|
|
1998
|
-
text: "Extract attribute from element",
|
|
1999
|
-
screenshotId,
|
|
2000
|
-
result: error
|
|
2001
|
-
? {
|
|
2002
|
-
status: "FAILED",
|
|
2003
|
-
startTime,
|
|
2004
|
-
endTime,
|
|
2005
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2006
|
-
}
|
|
2007
|
-
: {
|
|
2008
|
-
status: "PASSED",
|
|
2009
|
-
startTime,
|
|
2010
|
-
endTime,
|
|
2011
|
-
},
|
|
2012
|
-
info: info,
|
|
2013
|
-
});
|
|
1832
|
+
_commandFinally(state, this);
|
|
2014
1833
|
}
|
|
2015
1834
|
}
|
|
2016
1835
|
async extractEmailData(emailAddress, options, world) {
|
|
@@ -2087,7 +1906,8 @@ class StableBrowser {
|
|
|
2087
1906
|
catch (e) {
|
|
2088
1907
|
errorCount++;
|
|
2089
1908
|
if (errorCount > 3) {
|
|
2090
|
-
throw e;
|
|
1909
|
+
// throw e;
|
|
1910
|
+
await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
|
|
2091
1911
|
}
|
|
2092
1912
|
// ignore
|
|
2093
1913
|
}
|
|
@@ -2198,7 +2018,8 @@ class StableBrowser {
|
|
|
2198
2018
|
info.screenshotPath = screenshotPath;
|
|
2199
2019
|
Object.assign(e, { info: info });
|
|
2200
2020
|
error = e;
|
|
2201
|
-
throw e;
|
|
2021
|
+
// throw e;
|
|
2022
|
+
await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
|
|
2202
2023
|
}
|
|
2203
2024
|
finally {
|
|
2204
2025
|
const endTime = Date.now();
|
|
@@ -2211,7 +2032,7 @@ class StableBrowser {
|
|
|
2211
2032
|
status: "FAILED",
|
|
2212
2033
|
startTime,
|
|
2213
2034
|
endTime,
|
|
2214
|
-
message: error
|
|
2035
|
+
message: error?.message,
|
|
2215
2036
|
}
|
|
2216
2037
|
: {
|
|
2217
2038
|
status: "PASSED",
|
|
@@ -2224,52 +2045,58 @@ class StableBrowser {
|
|
|
2224
2045
|
}
|
|
2225
2046
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2226
2047
|
text = unEscapeString(text);
|
|
2227
|
-
const
|
|
2048
|
+
const state = {
|
|
2049
|
+
text_search: text,
|
|
2050
|
+
options,
|
|
2051
|
+
world,
|
|
2052
|
+
locate: false,
|
|
2053
|
+
scroll: false,
|
|
2054
|
+
highlight: false,
|
|
2055
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
2056
|
+
text: `Verify text exists in page`,
|
|
2057
|
+
operation: "verifyTextExistInPage",
|
|
2058
|
+
log: "***** verify text " + text + " exists in page *****\n",
|
|
2059
|
+
};
|
|
2228
2060
|
const timeout = this._getLoadTimeout(options);
|
|
2229
|
-
let error = null;
|
|
2230
|
-
let screenshotId = null;
|
|
2231
|
-
let screenshotPath = null;
|
|
2232
2061
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2233
|
-
const info = {};
|
|
2234
|
-
info.log = "***** verify text " + text + " exists in page *****\n";
|
|
2235
|
-
info.operation = "verifyTextExistInPage";
|
|
2236
2062
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
2237
2063
|
if (newValue !== text) {
|
|
2238
2064
|
this.logger.info(text + "=" + newValue);
|
|
2239
2065
|
text = newValue;
|
|
2240
2066
|
}
|
|
2241
|
-
info.text = text;
|
|
2242
2067
|
let dateAlternatives = findDateAlternatives(text);
|
|
2243
2068
|
let numberAlternatives = findNumberAlternatives(text);
|
|
2244
2069
|
try {
|
|
2070
|
+
await _preCommand(state, this);
|
|
2071
|
+
state.info.text = text;
|
|
2245
2072
|
while (true) {
|
|
2246
2073
|
const frames = this.page.frames();
|
|
2247
2074
|
let results = [];
|
|
2248
2075
|
for (let i = 0; i < frames.length; i++) {
|
|
2249
2076
|
if (dateAlternatives.date) {
|
|
2250
2077
|
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2251
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "
|
|
2078
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2252
2079
|
result.frame = frames[i];
|
|
2253
2080
|
results.push(result);
|
|
2254
2081
|
}
|
|
2255
2082
|
}
|
|
2256
2083
|
else if (numberAlternatives.number) {
|
|
2257
2084
|
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2258
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "
|
|
2085
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2259
2086
|
result.frame = frames[i];
|
|
2260
2087
|
results.push(result);
|
|
2261
2088
|
}
|
|
2262
2089
|
}
|
|
2263
2090
|
else {
|
|
2264
|
-
const result = await this._locateElementByText(frames[i], text, "
|
|
2091
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2265
2092
|
result.frame = frames[i];
|
|
2266
2093
|
results.push(result);
|
|
2267
2094
|
}
|
|
2268
2095
|
}
|
|
2269
|
-
info.results = results;
|
|
2096
|
+
state.info.results = results;
|
|
2270
2097
|
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2271
2098
|
if (resultWithElementsFound.length === 0) {
|
|
2272
|
-
if (Date.now() - startTime > timeout) {
|
|
2099
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2273
2100
|
throw new Error(`Text ${text} not found in page`);
|
|
2274
2101
|
}
|
|
2275
2102
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -2281,44 +2108,89 @@ class StableBrowser {
|
|
|
2281
2108
|
await this._highlightElements(frame, dataAttribute);
|
|
2282
2109
|
const element = await frame.$(dataAttribute);
|
|
2283
2110
|
if (element) {
|
|
2284
|
-
await this.scrollIfNeeded(element, info);
|
|
2111
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2285
2112
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2286
2113
|
}
|
|
2287
2114
|
}
|
|
2288
|
-
|
|
2289
|
-
return info;
|
|
2115
|
+
await _screenshot(state, this);
|
|
2116
|
+
return state.info;
|
|
2290
2117
|
}
|
|
2291
2118
|
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2292
2119
|
}
|
|
2293
2120
|
catch (e) {
|
|
2294
|
-
|
|
2295
|
-
this.logger.error("verify text exist in page failed " + info.log);
|
|
2296
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2297
|
-
info.screenshotPath = screenshotPath;
|
|
2298
|
-
Object.assign(e, { info: info });
|
|
2299
|
-
error = e;
|
|
2300
|
-
throw e;
|
|
2121
|
+
await _commandError(state, e, this);
|
|
2301
2122
|
}
|
|
2302
2123
|
finally {
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2124
|
+
_commandFinally(state, this);
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
async waitForTextToDisappear(text, options = {}, world = null) {
|
|
2128
|
+
text = unEscapeString(text);
|
|
2129
|
+
const state = {
|
|
2130
|
+
text_search: text,
|
|
2131
|
+
options,
|
|
2132
|
+
world,
|
|
2133
|
+
locate: false,
|
|
2134
|
+
scroll: false,
|
|
2135
|
+
highlight: false,
|
|
2136
|
+
type: Types.WAIT_FOR_TEXT_TO_DISAPPEAR,
|
|
2137
|
+
text: `Verify text does not exist in page`,
|
|
2138
|
+
operation: "verifyTextNotExistInPage",
|
|
2139
|
+
log: "***** verify text " + text + " does not exist in page *****\n",
|
|
2140
|
+
};
|
|
2141
|
+
const timeout = this._getLoadTimeout(options);
|
|
2142
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2143
|
+
const newValue = await this._replaceWithLocalData(text, world);
|
|
2144
|
+
if (newValue !== text) {
|
|
2145
|
+
this.logger.info(text + "=" + newValue);
|
|
2146
|
+
text = newValue;
|
|
2147
|
+
}
|
|
2148
|
+
let dateAlternatives = findDateAlternatives(text);
|
|
2149
|
+
let numberAlternatives = findNumberAlternatives(text);
|
|
2150
|
+
try {
|
|
2151
|
+
await _preCommand(state, this);
|
|
2152
|
+
state.info.text = text;
|
|
2153
|
+
while (true) {
|
|
2154
|
+
const frames = this.page.frames();
|
|
2155
|
+
let results = [];
|
|
2156
|
+
for (let i = 0; i < frames.length; i++) {
|
|
2157
|
+
if (dateAlternatives.date) {
|
|
2158
|
+
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2159
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2160
|
+
result.frame = frames[i];
|
|
2161
|
+
results.push(result);
|
|
2162
|
+
}
|
|
2314
2163
|
}
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2164
|
+
else if (numberAlternatives.number) {
|
|
2165
|
+
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2166
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2167
|
+
result.frame = frames[i];
|
|
2168
|
+
results.push(result);
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
else {
|
|
2172
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2173
|
+
result.frame = frames[i];
|
|
2174
|
+
results.push(result);
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
state.info.results = results;
|
|
2178
|
+
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2179
|
+
if (resultWithElementsFound.length === 0) {
|
|
2180
|
+
await _screenshot(state, this);
|
|
2181
|
+
return state.info;
|
|
2182
|
+
}
|
|
2183
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2184
|
+
throw new Error(`Text ${text} found in page`);
|
|
2185
|
+
}
|
|
2186
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
catch (e) {
|
|
2190
|
+
await _commandError(state, e, this);
|
|
2191
|
+
}
|
|
2192
|
+
finally {
|
|
2193
|
+
_commandFinally(state, this);
|
|
2322
2194
|
}
|
|
2323
2195
|
}
|
|
2324
2196
|
_getServerUrl() {
|
|
@@ -2381,7 +2253,8 @@ class StableBrowser {
|
|
|
2381
2253
|
info.screenshotPath = screenshotPath;
|
|
2382
2254
|
Object.assign(e, { info: info });
|
|
2383
2255
|
error = e;
|
|
2384
|
-
throw e;
|
|
2256
|
+
// throw e;
|
|
2257
|
+
await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
|
|
2385
2258
|
}
|
|
2386
2259
|
finally {
|
|
2387
2260
|
const endTime = Date.now();
|
|
@@ -2394,7 +2267,7 @@ class StableBrowser {
|
|
|
2394
2267
|
status: "FAILED",
|
|
2395
2268
|
startTime,
|
|
2396
2269
|
endTime,
|
|
2397
|
-
message: error
|
|
2270
|
+
message: error?.message,
|
|
2398
2271
|
}
|
|
2399
2272
|
: {
|
|
2400
2273
|
status: "PASSED",
|
|
@@ -2426,7 +2299,7 @@ class StableBrowser {
|
|
|
2426
2299
|
this.logger.info("Table data verified");
|
|
2427
2300
|
}
|
|
2428
2301
|
async getTableData(selectors, _params = null, options = {}, world = null) {
|
|
2429
|
-
|
|
2302
|
+
_validateSelectors(selectors);
|
|
2430
2303
|
const startTime = Date.now();
|
|
2431
2304
|
let error = null;
|
|
2432
2305
|
let screenshotId = null;
|
|
@@ -2448,7 +2321,8 @@ class StableBrowser {
|
|
|
2448
2321
|
info.screenshotPath = screenshotPath;
|
|
2449
2322
|
Object.assign(e, { info: info });
|
|
2450
2323
|
error = e;
|
|
2451
|
-
throw e;
|
|
2324
|
+
// throw e;
|
|
2325
|
+
await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
|
|
2452
2326
|
}
|
|
2453
2327
|
finally {
|
|
2454
2328
|
const endTime = Date.now();
|
|
@@ -2462,7 +2336,7 @@ class StableBrowser {
|
|
|
2462
2336
|
status: "FAILED",
|
|
2463
2337
|
startTime,
|
|
2464
2338
|
endTime,
|
|
2465
|
-
message: error
|
|
2339
|
+
message: error?.message,
|
|
2466
2340
|
}
|
|
2467
2341
|
: {
|
|
2468
2342
|
status: "PASSED",
|
|
@@ -2474,7 +2348,7 @@ class StableBrowser {
|
|
|
2474
2348
|
}
|
|
2475
2349
|
}
|
|
2476
2350
|
async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
|
|
2477
|
-
|
|
2351
|
+
_validateSelectors(selectors);
|
|
2478
2352
|
if (!query) {
|
|
2479
2353
|
throw new Error("query is null");
|
|
2480
2354
|
}
|
|
@@ -2613,7 +2487,8 @@ class StableBrowser {
|
|
|
2613
2487
|
info.screenshotPath = screenshotPath;
|
|
2614
2488
|
Object.assign(e, { info: info });
|
|
2615
2489
|
error = e;
|
|
2616
|
-
throw e;
|
|
2490
|
+
// throw e;
|
|
2491
|
+
await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
|
|
2617
2492
|
}
|
|
2618
2493
|
finally {
|
|
2619
2494
|
const endTime = Date.now();
|
|
@@ -2627,7 +2502,7 @@ class StableBrowser {
|
|
|
2627
2502
|
status: "FAILED",
|
|
2628
2503
|
startTime,
|
|
2629
2504
|
endTime,
|
|
2630
|
-
message: error
|
|
2505
|
+
message: error?.message,
|
|
2631
2506
|
}
|
|
2632
2507
|
: {
|
|
2633
2508
|
status: "PASSED",
|
|
@@ -2639,27 +2514,7 @@ class StableBrowser {
|
|
|
2639
2514
|
}
|
|
2640
2515
|
}
|
|
2641
2516
|
async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
|
|
2642
|
-
|
|
2643
|
-
return value;
|
|
2644
|
-
}
|
|
2645
|
-
// find all the accurance of {{(.*?)}} and replace with the value
|
|
2646
|
-
let regex = /{{(.*?)}}/g;
|
|
2647
|
-
let matches = value.match(regex);
|
|
2648
|
-
if (matches) {
|
|
2649
|
-
const testData = this.getTestData(world);
|
|
2650
|
-
for (let i = 0; i < matches.length; i++) {
|
|
2651
|
-
let match = matches[i];
|
|
2652
|
-
let key = match.substring(2, match.length - 2);
|
|
2653
|
-
let newValue = objectPath.get(testData, key, null);
|
|
2654
|
-
if (newValue !== null) {
|
|
2655
|
-
value = value.replace(match, newValue);
|
|
2656
|
-
}
|
|
2657
|
-
}
|
|
2658
|
-
}
|
|
2659
|
-
if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
|
|
2660
|
-
return await decrypt(value, null, totpWait);
|
|
2661
|
-
}
|
|
2662
|
-
return value;
|
|
2517
|
+
return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
|
|
2663
2518
|
}
|
|
2664
2519
|
_getLoadTimeout(options) {
|
|
2665
2520
|
let timeout = 15000;
|
|
@@ -2719,7 +2574,7 @@ class StableBrowser {
|
|
|
2719
2574
|
status: "FAILED",
|
|
2720
2575
|
startTime,
|
|
2721
2576
|
endTime,
|
|
2722
|
-
message: error
|
|
2577
|
+
message: error?.message,
|
|
2723
2578
|
}
|
|
2724
2579
|
: {
|
|
2725
2580
|
status: "PASSED",
|
|
@@ -2730,39 +2585,28 @@ class StableBrowser {
|
|
|
2730
2585
|
}
|
|
2731
2586
|
}
|
|
2732
2587
|
async closePage(options = {}, world = null) {
|
|
2733
|
-
const
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2588
|
+
const state = {
|
|
2589
|
+
options,
|
|
2590
|
+
world,
|
|
2591
|
+
locate: false,
|
|
2592
|
+
scroll: false,
|
|
2593
|
+
highlight: false,
|
|
2594
|
+
type: Types.CLOSE_PAGE,
|
|
2595
|
+
text: `Close page`,
|
|
2596
|
+
operation: "closePage",
|
|
2597
|
+
log: "***** close page *****\n",
|
|
2598
|
+
throwError: false,
|
|
2599
|
+
};
|
|
2738
2600
|
try {
|
|
2601
|
+
await _preCommand(state, this);
|
|
2739
2602
|
await this.page.close();
|
|
2740
2603
|
}
|
|
2741
2604
|
catch (e) {
|
|
2742
2605
|
console.log(".");
|
|
2606
|
+
await _commandError(state, e, this);
|
|
2743
2607
|
}
|
|
2744
2608
|
finally {
|
|
2745
|
-
|
|
2746
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2747
|
-
const endTime = Date.now();
|
|
2748
|
-
this._reportToWorld(world, {
|
|
2749
|
-
type: Types.CLOSE_PAGE,
|
|
2750
|
-
text: "close page",
|
|
2751
|
-
screenshotId,
|
|
2752
|
-
result: error
|
|
2753
|
-
? {
|
|
2754
|
-
status: "FAILED",
|
|
2755
|
-
startTime,
|
|
2756
|
-
endTime,
|
|
2757
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2758
|
-
}
|
|
2759
|
-
: {
|
|
2760
|
-
status: "PASSED",
|
|
2761
|
-
startTime,
|
|
2762
|
-
endTime,
|
|
2763
|
-
},
|
|
2764
|
-
info: info,
|
|
2765
|
-
});
|
|
2609
|
+
_commandFinally(state, this);
|
|
2766
2610
|
}
|
|
2767
2611
|
}
|
|
2768
2612
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
@@ -2782,6 +2626,7 @@ class StableBrowser {
|
|
|
2782
2626
|
}
|
|
2783
2627
|
catch (e) {
|
|
2784
2628
|
console.log(".");
|
|
2629
|
+
await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
|
|
2785
2630
|
}
|
|
2786
2631
|
finally {
|
|
2787
2632
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
@@ -2796,7 +2641,7 @@ class StableBrowser {
|
|
|
2796
2641
|
status: "FAILED",
|
|
2797
2642
|
startTime,
|
|
2798
2643
|
endTime,
|
|
2799
|
-
message: error
|
|
2644
|
+
message: error?.message,
|
|
2800
2645
|
}
|
|
2801
2646
|
: {
|
|
2802
2647
|
status: "PASSED",
|
|
@@ -2818,6 +2663,7 @@ class StableBrowser {
|
|
|
2818
2663
|
}
|
|
2819
2664
|
catch (e) {
|
|
2820
2665
|
console.log(".");
|
|
2666
|
+
await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
|
|
2821
2667
|
}
|
|
2822
2668
|
finally {
|
|
2823
2669
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
@@ -2832,7 +2678,7 @@ class StableBrowser {
|
|
|
2832
2678
|
status: "FAILED",
|
|
2833
2679
|
startTime,
|
|
2834
2680
|
endTime,
|
|
2835
|
-
message: error
|
|
2681
|
+
message: error?.message,
|
|
2836
2682
|
}
|
|
2837
2683
|
: {
|
|
2838
2684
|
status: "PASSED",
|