automation_model 1.0.458-dev → 1.0.458-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 +185 -7
- package/lib/api.js.map +1 -1
- package/lib/auto_page.js +21 -16
- package/lib/auto_page.js.map +1 -1
- package/lib/axe/axe.mini.js +12 -0
- package/lib/browser_manager.d.ts +5 -3
- package/lib/browser_manager.js +44 -19
- 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 +20 -4
- 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 +23 -17
- package/lib/stable_browser.js +675 -793
- package/lib/stable_browser.js.map +1 -1
- package/lib/test_context.d.ts +2 -0
- package/lib/test_context.js +12 -11
- 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 +4 -5
package/lib/stable_browser.js
CHANGED
|
@@ -10,12 +10,14 @@ 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,9 +143,9 @@ 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
|
-
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this);
|
|
148
|
+
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this, -1, this.context.reportFolder);
|
|
132
149
|
newContextCreated = true;
|
|
133
150
|
apps[appName] = {
|
|
134
151
|
context: newContext,
|
|
@@ -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,10 +225,10 @@ 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
|
-
console.error("Error in request listener", error);
|
|
231
|
+
// console.error("Error in request listener", error);
|
|
216
232
|
context.networkLogger.push({
|
|
217
233
|
error: "not able to listen",
|
|
218
234
|
message: error.message,
|
|
@@ -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
|
}
|
|
@@ -339,7 +341,7 @@ class StableBrowser {
|
|
|
339
341
|
if (css && css.locator) {
|
|
340
342
|
css = css.locator;
|
|
341
343
|
}
|
|
342
|
-
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "
|
|
344
|
+
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*:not(script, style, head)", false, false, _params);
|
|
343
345
|
if (result.elementCount === 0) {
|
|
344
346
|
return;
|
|
345
347
|
}
|
|
@@ -354,7 +356,7 @@ class StableBrowser {
|
|
|
354
356
|
}
|
|
355
357
|
async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
|
|
356
358
|
//const stringifyText = JSON.stringify(text);
|
|
357
|
-
return await scope.evaluate(([text, tag, regex, partial]) => {
|
|
359
|
+
return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
|
|
358
360
|
function isParent(parent, child) {
|
|
359
361
|
let currentNode = child.parentNode;
|
|
360
362
|
while (currentNode !== null) {
|
|
@@ -366,6 +368,15 @@ class StableBrowser {
|
|
|
366
368
|
return false;
|
|
367
369
|
}
|
|
368
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;
|
|
369
380
|
function collectAllShadowDomElements(element, result = []) {
|
|
370
381
|
// Check and add the element if it has a shadow root
|
|
371
382
|
if (element.shadowRoot) {
|
|
@@ -382,7 +393,11 @@ class StableBrowser {
|
|
|
382
393
|
}
|
|
383
394
|
document.collectAllShadowDomElements = collectAllShadowDomElements;
|
|
384
395
|
if (!tag) {
|
|
385
|
-
tag = "
|
|
396
|
+
tag = "*:not(script, style, head)";
|
|
397
|
+
}
|
|
398
|
+
let regexpSearch = document.getRegex(text);
|
|
399
|
+
if (regexpSearch) {
|
|
400
|
+
regex = true;
|
|
386
401
|
}
|
|
387
402
|
let elements = Array.from(document.querySelectorAll(tag));
|
|
388
403
|
let shadowHosts = [];
|
|
@@ -399,7 +414,9 @@ class StableBrowser {
|
|
|
399
414
|
let randomToken = null;
|
|
400
415
|
const foundElements = [];
|
|
401
416
|
if (regex) {
|
|
402
|
-
|
|
417
|
+
if (!regexpSearch) {
|
|
418
|
+
regexpSearch = new RegExp(text, "im");
|
|
419
|
+
}
|
|
403
420
|
for (let i = 0; i < elements.length; i++) {
|
|
404
421
|
const element = elements[i];
|
|
405
422
|
if ((element.innerText && regexpSearch.test(element.innerText)) ||
|
|
@@ -413,8 +430,8 @@ class StableBrowser {
|
|
|
413
430
|
for (let i = 0; i < elements.length; i++) {
|
|
414
431
|
const element = elements[i];
|
|
415
432
|
if (partial) {
|
|
416
|
-
if ((element.innerText && element.innerText.trim().includes(text)) ||
|
|
417
|
-
(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()))) {
|
|
418
435
|
foundElements.push(element);
|
|
419
436
|
}
|
|
420
437
|
}
|
|
@@ -458,19 +475,39 @@ class StableBrowser {
|
|
|
458
475
|
}, [text1, tag1, regex1, partial1]);
|
|
459
476
|
}
|
|
460
477
|
async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
|
|
478
|
+
if (!info) {
|
|
479
|
+
info = {};
|
|
480
|
+
}
|
|
481
|
+
if (!info.failCause) {
|
|
482
|
+
info.failCause = {};
|
|
483
|
+
}
|
|
484
|
+
if (!info.log) {
|
|
485
|
+
info.log = "";
|
|
486
|
+
}
|
|
461
487
|
let locatorSearch = selectorHierarchy[index];
|
|
488
|
+
try {
|
|
489
|
+
locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
|
|
490
|
+
}
|
|
491
|
+
catch (e) {
|
|
492
|
+
console.error(e);
|
|
493
|
+
}
|
|
462
494
|
//info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
|
|
463
495
|
let locator = null;
|
|
464
496
|
if (locatorSearch.climb && locatorSearch.climb >= 0) {
|
|
465
497
|
let locatorString = await this._locateElmentByTextClimbCss(scope, locatorSearch.text, locatorSearch.climb, locatorSearch.css, _params);
|
|
466
498
|
if (!locatorString) {
|
|
499
|
+
info.failCause.textNotFound = true;
|
|
500
|
+
info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
|
|
467
501
|
return;
|
|
468
502
|
}
|
|
469
503
|
locator = this._getLocator({ css: locatorString }, scope, _params);
|
|
470
504
|
}
|
|
471
505
|
else if (locatorSearch.text) {
|
|
472
|
-
let
|
|
506
|
+
let text = this._fixUsingParams(locatorSearch.text, _params);
|
|
507
|
+
let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
|
|
473
508
|
if (result.elementCount === 0) {
|
|
509
|
+
info.failCause.textNotFound = true;
|
|
510
|
+
info.failCause.lastError = "failed to locate element by text: " + text;
|
|
474
511
|
return;
|
|
475
512
|
}
|
|
476
513
|
locatorSearch.css = "[data-blinq-id='blinq-id-" + result.randomToken + "']";
|
|
@@ -487,6 +524,9 @@ class StableBrowser {
|
|
|
487
524
|
// cssHref = true;
|
|
488
525
|
// }
|
|
489
526
|
let count = await locator.count();
|
|
527
|
+
if (count > 0 && !info.failCause.count) {
|
|
528
|
+
info.failCause.count = count;
|
|
529
|
+
}
|
|
490
530
|
//info.log += "total elements found " + count + "\n";
|
|
491
531
|
//let visibleCount = 0;
|
|
492
532
|
let visibleLocator = null;
|
|
@@ -504,6 +544,8 @@ class StableBrowser {
|
|
|
504
544
|
foundLocators.push(locator.nth(j));
|
|
505
545
|
}
|
|
506
546
|
else {
|
|
547
|
+
info.failCause.visible = visible;
|
|
548
|
+
info.failCause.enabled = enabled;
|
|
507
549
|
if (!info.printMessages) {
|
|
508
550
|
info.printMessages = {};
|
|
509
551
|
}
|
|
@@ -515,6 +557,11 @@ class StableBrowser {
|
|
|
515
557
|
}
|
|
516
558
|
}
|
|
517
559
|
async closeUnexpectedPopups(info, _params) {
|
|
560
|
+
if (!info) {
|
|
561
|
+
info = {};
|
|
562
|
+
info.failCause = {};
|
|
563
|
+
info.log = "";
|
|
564
|
+
}
|
|
518
565
|
if (this.configuration.popupHandlers && this.configuration.popupHandlers.length > 0) {
|
|
519
566
|
if (!info) {
|
|
520
567
|
info = {};
|
|
@@ -555,7 +602,10 @@ class StableBrowser {
|
|
|
555
602
|
}
|
|
556
603
|
return { rerun: false };
|
|
557
604
|
}
|
|
558
|
-
async _locate(selectors, info, _params, timeout
|
|
605
|
+
async _locate(selectors, info, _params, timeout) {
|
|
606
|
+
if (!timeout) {
|
|
607
|
+
timeout = 30000;
|
|
608
|
+
}
|
|
559
609
|
for (let i = 0; i < 3; i++) {
|
|
560
610
|
info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
|
|
561
611
|
for (let j = 0; j < selectors.locators.length; j++) {
|
|
@@ -569,35 +619,46 @@ class StableBrowser {
|
|
|
569
619
|
}
|
|
570
620
|
throw new Error("unable to locate element " + JSON.stringify(selectors));
|
|
571
621
|
}
|
|
572
|
-
async
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
622
|
+
async _findFrameScope(selectors, timeout = 30000, info) {
|
|
623
|
+
if (!info) {
|
|
624
|
+
info = {};
|
|
625
|
+
info.failCause = {};
|
|
626
|
+
info.log = "";
|
|
627
|
+
}
|
|
578
628
|
let scope = this.page;
|
|
629
|
+
if (selectors.frame) {
|
|
630
|
+
return selectors.frame;
|
|
631
|
+
}
|
|
579
632
|
if (selectors.iframe_src || selectors.frameLocators) {
|
|
580
|
-
const findFrame = (frame, framescope) => {
|
|
633
|
+
const findFrame = async (frame, framescope) => {
|
|
581
634
|
for (let i = 0; i < frame.selectors.length; i++) {
|
|
582
635
|
let frameLocator = frame.selectors[i];
|
|
583
636
|
if (frameLocator.css) {
|
|
584
|
-
|
|
637
|
+
let testframescope = framescope.frameLocator(frameLocator.css);
|
|
585
638
|
if (frameLocator.index) {
|
|
586
|
-
|
|
639
|
+
testframescope = framescope.nth(frameLocator.index);
|
|
640
|
+
}
|
|
641
|
+
try {
|
|
642
|
+
await testframescope.owner().evaluateHandle(() => true, null, {
|
|
643
|
+
timeout: 5000,
|
|
644
|
+
});
|
|
645
|
+
framescope = testframescope;
|
|
646
|
+
break;
|
|
647
|
+
}
|
|
648
|
+
catch (error) {
|
|
649
|
+
console.error("frame not found " + frameLocator.css);
|
|
587
650
|
}
|
|
588
|
-
break;
|
|
589
651
|
}
|
|
590
652
|
}
|
|
591
653
|
if (frame.children) {
|
|
592
|
-
return findFrame(frame.children, framescope);
|
|
654
|
+
return await findFrame(frame.children, framescope);
|
|
593
655
|
}
|
|
594
656
|
return framescope;
|
|
595
657
|
};
|
|
596
|
-
info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
|
|
597
658
|
while (true) {
|
|
598
659
|
let frameFound = false;
|
|
599
660
|
if (selectors.nestFrmLoc) {
|
|
600
|
-
scope = findFrame(selectors.nestFrmLoc, scope);
|
|
661
|
+
scope = await findFrame(selectors.nestFrmLoc, scope);
|
|
601
662
|
frameFound = true;
|
|
602
663
|
break;
|
|
603
664
|
}
|
|
@@ -617,6 +678,8 @@ class StableBrowser {
|
|
|
617
678
|
if (!scope) {
|
|
618
679
|
info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
|
|
619
680
|
if (performance.now() - startTime > timeout) {
|
|
681
|
+
info.failCause.iframeNotFound = true;
|
|
682
|
+
info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
|
|
620
683
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
621
684
|
}
|
|
622
685
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -626,6 +689,30 @@ class StableBrowser {
|
|
|
626
689
|
}
|
|
627
690
|
}
|
|
628
691
|
}
|
|
692
|
+
if (!scope) {
|
|
693
|
+
scope = this.page;
|
|
694
|
+
}
|
|
695
|
+
return scope;
|
|
696
|
+
}
|
|
697
|
+
async _getDocumentBody(selectors, timeout = 30000, info) {
|
|
698
|
+
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
699
|
+
return scope.evaluate(() => {
|
|
700
|
+
var bodyContent = document.body.innerHTML;
|
|
701
|
+
return bodyContent;
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
705
|
+
if (!info) {
|
|
706
|
+
info = {};
|
|
707
|
+
info.failCause = {};
|
|
708
|
+
info.log = "";
|
|
709
|
+
}
|
|
710
|
+
let highPriorityTimeout = 5000;
|
|
711
|
+
let visibleOnlyTimeout = 6000;
|
|
712
|
+
let startTime = performance.now();
|
|
713
|
+
let locatorsCount = 0;
|
|
714
|
+
//let arrayMode = Array.isArray(selectors);
|
|
715
|
+
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
629
716
|
let selectorsLocators = null;
|
|
630
717
|
selectorsLocators = selectors.locators;
|
|
631
718
|
// group selectors by priority
|
|
@@ -727,6 +814,8 @@ class StableBrowser {
|
|
|
727
814
|
}
|
|
728
815
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
729
816
|
info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
|
|
817
|
+
info.failCause.locatorNotFound = true;
|
|
818
|
+
info.failCause.lastError = "failed to locate unique element";
|
|
730
819
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
731
820
|
}
|
|
732
821
|
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
@@ -758,89 +847,129 @@ class StableBrowser {
|
|
|
758
847
|
});
|
|
759
848
|
result.locatorIndex = i;
|
|
760
849
|
}
|
|
850
|
+
if (foundLocators.length > 1) {
|
|
851
|
+
info.failCause.foundMultiple = true;
|
|
852
|
+
}
|
|
761
853
|
}
|
|
762
854
|
return result;
|
|
763
855
|
}
|
|
764
|
-
async
|
|
765
|
-
this._validateSelectors(selectors);
|
|
856
|
+
async simpleClick(elementDescription, _params, options = {}, world = null) {
|
|
766
857
|
const startTime = Date.now();
|
|
767
|
-
|
|
768
|
-
|
|
858
|
+
let timeout = 30000;
|
|
859
|
+
if (options && options.timeout) {
|
|
860
|
+
timeout = options.timeout;
|
|
769
861
|
}
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
862
|
+
while (true) {
|
|
863
|
+
try {
|
|
864
|
+
const result = await locate_element(this.context, elementDescription, "click");
|
|
865
|
+
if (result?.elementNumber >= 0) {
|
|
866
|
+
const selectors = {
|
|
867
|
+
frame: result?.frame,
|
|
868
|
+
locators: [
|
|
869
|
+
{
|
|
870
|
+
css: result?.css,
|
|
871
|
+
},
|
|
872
|
+
],
|
|
873
|
+
};
|
|
874
|
+
await this.click(selectors, _params, options, world);
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
catch (e) {
|
|
879
|
+
if (performance.now() - startTime > timeout) {
|
|
880
|
+
// throw e;
|
|
881
|
+
await _commandError({ text: "simpleClick", operation: "simpleClick", elementDescription, info: {} }, e, this);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
|
|
888
|
+
const startTime = Date.now();
|
|
889
|
+
let timeout = 30000;
|
|
890
|
+
if (options && options.timeout) {
|
|
891
|
+
timeout = options.timeout;
|
|
892
|
+
}
|
|
893
|
+
while (true) {
|
|
894
|
+
try {
|
|
895
|
+
const result = await locate_element(this.context, elementDescription, "fill", value);
|
|
896
|
+
if (result?.elementNumber >= 0) {
|
|
897
|
+
const selectors = {
|
|
898
|
+
frame: result?.frame,
|
|
899
|
+
locators: [
|
|
900
|
+
{
|
|
901
|
+
css: result?.css,
|
|
902
|
+
},
|
|
903
|
+
],
|
|
904
|
+
};
|
|
905
|
+
await this.clickType(selectors, value, false, _params, options, world);
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
catch (e) {
|
|
910
|
+
if (performance.now() - startTime > timeout) {
|
|
911
|
+
// throw e;
|
|
912
|
+
await _commandError({ text: "simpleClickType", operation: "simpleClickType", value, elementDescription, info: {} }, e, this);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
async click(selectors, _params, options = {}, world = null) {
|
|
919
|
+
const state = {
|
|
920
|
+
selectors,
|
|
921
|
+
_params,
|
|
922
|
+
options,
|
|
923
|
+
world,
|
|
924
|
+
text: "Click element",
|
|
925
|
+
type: Types.CLICK,
|
|
926
|
+
operation: "click",
|
|
927
|
+
log: "***** click on " + selectors.element_name + " *****\n",
|
|
928
|
+
};
|
|
777
929
|
try {
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
930
|
+
await _preCommand(state, this);
|
|
931
|
+
if (state.options && state.options.context) {
|
|
932
|
+
state.selectors.locators[0].text = state.options.context;
|
|
933
|
+
}
|
|
781
934
|
try {
|
|
782
|
-
await
|
|
783
|
-
await
|
|
784
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
935
|
+
await state.element.click();
|
|
936
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
785
937
|
}
|
|
786
938
|
catch (e) {
|
|
787
939
|
// await this.closeUnexpectedPopups();
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
await
|
|
791
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
940
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
941
|
+
await state.element.dispatchEvent("click");
|
|
942
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
792
943
|
}
|
|
793
944
|
await this.waitForPageLoad();
|
|
794
|
-
return info;
|
|
945
|
+
return state.info;
|
|
795
946
|
}
|
|
796
947
|
catch (e) {
|
|
797
|
-
|
|
798
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
799
|
-
info.screenshotPath = screenshotPath;
|
|
800
|
-
Object.assign(e, { info: info });
|
|
801
|
-
error = e;
|
|
802
|
-
throw e;
|
|
948
|
+
await _commandError(state, e, this);
|
|
803
949
|
}
|
|
804
950
|
finally {
|
|
805
|
-
|
|
806
|
-
this._reportToWorld(world, {
|
|
807
|
-
element_name: selectors.element_name,
|
|
808
|
-
type: Types.CLICK,
|
|
809
|
-
text: `Click element`,
|
|
810
|
-
screenshotId,
|
|
811
|
-
result: error
|
|
812
|
-
? {
|
|
813
|
-
status: "FAILED",
|
|
814
|
-
startTime,
|
|
815
|
-
endTime,
|
|
816
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
817
|
-
}
|
|
818
|
-
: {
|
|
819
|
-
status: "PASSED",
|
|
820
|
-
startTime,
|
|
821
|
-
endTime,
|
|
822
|
-
},
|
|
823
|
-
info: info,
|
|
824
|
-
});
|
|
951
|
+
_commandFinally(state, this);
|
|
825
952
|
}
|
|
826
953
|
}
|
|
827
954
|
async setCheck(selectors, checked = true, _params, options = {}, world = null) {
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
955
|
+
const state = {
|
|
956
|
+
selectors,
|
|
957
|
+
_params,
|
|
958
|
+
options,
|
|
959
|
+
world,
|
|
960
|
+
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
961
|
+
text: checked ? `Check element` : `Uncheck element`,
|
|
962
|
+
operation: "setCheck",
|
|
963
|
+
log: "***** check " + selectors.element_name + " *****\n",
|
|
964
|
+
};
|
|
838
965
|
try {
|
|
839
|
-
|
|
840
|
-
|
|
966
|
+
await _preCommand(state, this);
|
|
967
|
+
state.info.checked = checked;
|
|
968
|
+
// let element = await this._locate(selectors, info, _params);
|
|
969
|
+
// ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
841
970
|
try {
|
|
842
|
-
await this._highlightElements(element);
|
|
843
|
-
await element.setChecked(checked);
|
|
971
|
+
// await this._highlightElements(element);
|
|
972
|
+
await state.element.setChecked(checked);
|
|
844
973
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
845
974
|
}
|
|
846
975
|
catch (e) {
|
|
@@ -849,179 +978,108 @@ class StableBrowser {
|
|
|
849
978
|
}
|
|
850
979
|
else {
|
|
851
980
|
//await this.closeUnexpectedPopups();
|
|
852
|
-
info.log += "setCheck failed, will try again" + "\n";
|
|
853
|
-
element = await this._locate(selectors, info, _params);
|
|
854
|
-
await element.setChecked(checked, { timeout: 5000, force: true });
|
|
981
|
+
state.info.log += "setCheck failed, will try again" + "\n";
|
|
982
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
983
|
+
await state.element.setChecked(checked, { timeout: 5000, force: true });
|
|
855
984
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
856
985
|
}
|
|
857
986
|
}
|
|
858
987
|
await this.waitForPageLoad();
|
|
859
|
-
return info;
|
|
988
|
+
return state.info;
|
|
860
989
|
}
|
|
861
990
|
catch (e) {
|
|
862
|
-
|
|
863
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
864
|
-
info.screenshotPath = screenshotPath;
|
|
865
|
-
Object.assign(e, { info: info });
|
|
866
|
-
error = e;
|
|
867
|
-
throw e;
|
|
991
|
+
await _commandError(state, e, this);
|
|
868
992
|
}
|
|
869
993
|
finally {
|
|
870
|
-
|
|
871
|
-
this._reportToWorld(world, {
|
|
872
|
-
element_name: selectors.element_name,
|
|
873
|
-
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
874
|
-
text: checked ? `Check element` : `Uncheck element`,
|
|
875
|
-
screenshotId,
|
|
876
|
-
result: error
|
|
877
|
-
? {
|
|
878
|
-
status: "FAILED",
|
|
879
|
-
startTime,
|
|
880
|
-
endTime,
|
|
881
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
882
|
-
}
|
|
883
|
-
: {
|
|
884
|
-
status: "PASSED",
|
|
885
|
-
startTime,
|
|
886
|
-
endTime,
|
|
887
|
-
},
|
|
888
|
-
info: info,
|
|
889
|
-
});
|
|
994
|
+
_commandFinally(state, this);
|
|
890
995
|
}
|
|
891
996
|
}
|
|
892
997
|
async hover(selectors, _params, options = {}, world = null) {
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
998
|
+
const state = {
|
|
999
|
+
selectors,
|
|
1000
|
+
_params,
|
|
1001
|
+
options,
|
|
1002
|
+
world,
|
|
1003
|
+
type: Types.HOVER,
|
|
1004
|
+
text: `Hover element`,
|
|
1005
|
+
operation: "hover",
|
|
1006
|
+
log: "***** hover " + selectors.element_name + " *****\n",
|
|
1007
|
+
};
|
|
902
1008
|
try {
|
|
903
|
-
|
|
904
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1009
|
+
await _preCommand(state, this);
|
|
905
1010
|
try {
|
|
906
|
-
await
|
|
907
|
-
await element.hover();
|
|
1011
|
+
await state.element.hover();
|
|
908
1012
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
909
1013
|
}
|
|
910
1014
|
catch (e) {
|
|
911
1015
|
//await this.closeUnexpectedPopups();
|
|
912
|
-
info.log += "hover failed, will try again" + "\n";
|
|
913
|
-
element = await this._locate(selectors, info, _params);
|
|
914
|
-
await element.hover({ timeout: 10000 });
|
|
1016
|
+
state.info.log += "hover failed, will try again" + "\n";
|
|
1017
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
1018
|
+
await state.element.hover({ timeout: 10000 });
|
|
915
1019
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
916
1020
|
}
|
|
917
1021
|
await this.waitForPageLoad();
|
|
918
|
-
return info;
|
|
1022
|
+
return state.info;
|
|
919
1023
|
}
|
|
920
1024
|
catch (e) {
|
|
921
|
-
|
|
922
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
923
|
-
info.screenshotPath = screenshotPath;
|
|
924
|
-
Object.assign(e, { info: info });
|
|
925
|
-
error = e;
|
|
926
|
-
throw e;
|
|
1025
|
+
await _commandError(state, e, this);
|
|
927
1026
|
}
|
|
928
1027
|
finally {
|
|
929
|
-
|
|
930
|
-
this._reportToWorld(world, {
|
|
931
|
-
element_name: selectors.element_name,
|
|
932
|
-
type: Types.HOVER,
|
|
933
|
-
text: `Hover element`,
|
|
934
|
-
screenshotId,
|
|
935
|
-
result: error
|
|
936
|
-
? {
|
|
937
|
-
status: "FAILED",
|
|
938
|
-
startTime,
|
|
939
|
-
endTime,
|
|
940
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
941
|
-
}
|
|
942
|
-
: {
|
|
943
|
-
status: "PASSED",
|
|
944
|
-
startTime,
|
|
945
|
-
endTime,
|
|
946
|
-
},
|
|
947
|
-
info: info,
|
|
948
|
-
});
|
|
1028
|
+
_commandFinally(state, this);
|
|
949
1029
|
}
|
|
950
1030
|
}
|
|
951
1031
|
async selectOption(selectors, values, _params = null, options = {}, world = null) {
|
|
952
|
-
this._validateSelectors(selectors);
|
|
953
1032
|
if (!values) {
|
|
954
1033
|
throw new Error("values is null");
|
|
955
1034
|
}
|
|
956
|
-
const
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
1035
|
+
const state = {
|
|
1036
|
+
selectors,
|
|
1037
|
+
_params,
|
|
1038
|
+
options,
|
|
1039
|
+
world,
|
|
1040
|
+
value: values.toString(),
|
|
1041
|
+
type: Types.SELECT,
|
|
1042
|
+
text: `Select option: ${values}`,
|
|
1043
|
+
operation: "selectOption",
|
|
1044
|
+
log: "***** select option " + selectors.element_name + " *****\n",
|
|
1045
|
+
};
|
|
964
1046
|
try {
|
|
965
|
-
|
|
966
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1047
|
+
await _preCommand(state, this);
|
|
967
1048
|
try {
|
|
968
|
-
await
|
|
969
|
-
await element.selectOption(values);
|
|
1049
|
+
await state.element.selectOption(values);
|
|
970
1050
|
}
|
|
971
1051
|
catch (e) {
|
|
972
1052
|
//await this.closeUnexpectedPopups();
|
|
973
|
-
info.log += "selectOption failed, will try force" + "\n";
|
|
974
|
-
await element.selectOption(values, { timeout: 10000, force: true });
|
|
1053
|
+
state.info.log += "selectOption failed, will try force" + "\n";
|
|
1054
|
+
await state.element.selectOption(values, { timeout: 10000, force: true });
|
|
975
1055
|
}
|
|
976
1056
|
await this.waitForPageLoad();
|
|
977
|
-
return info;
|
|
1057
|
+
return state.info;
|
|
978
1058
|
}
|
|
979
1059
|
catch (e) {
|
|
980
|
-
|
|
981
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
982
|
-
info.screenshotPath = screenshotPath;
|
|
983
|
-
Object.assign(e, { info: info });
|
|
984
|
-
this.logger.info("click failed, will try next selector");
|
|
985
|
-
error = e;
|
|
986
|
-
throw e;
|
|
1060
|
+
await _commandError(state, e, this);
|
|
987
1061
|
}
|
|
988
1062
|
finally {
|
|
989
|
-
|
|
990
|
-
this._reportToWorld(world, {
|
|
991
|
-
element_name: selectors.element_name,
|
|
992
|
-
type: Types.SELECT,
|
|
993
|
-
text: `Select option: ${values}`,
|
|
994
|
-
value: values.toString(),
|
|
995
|
-
screenshotId,
|
|
996
|
-
result: error
|
|
997
|
-
? {
|
|
998
|
-
status: "FAILED",
|
|
999
|
-
startTime,
|
|
1000
|
-
endTime,
|
|
1001
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1002
|
-
}
|
|
1003
|
-
: {
|
|
1004
|
-
status: "PASSED",
|
|
1005
|
-
startTime,
|
|
1006
|
-
endTime,
|
|
1007
|
-
},
|
|
1008
|
-
info: info,
|
|
1009
|
-
});
|
|
1063
|
+
_commandFinally(state, this);
|
|
1010
1064
|
}
|
|
1011
1065
|
}
|
|
1012
1066
|
async type(_value, _params = null, options = {}, world = null) {
|
|
1013
|
-
const
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1067
|
+
const state = {
|
|
1068
|
+
value: _value,
|
|
1069
|
+
_params,
|
|
1070
|
+
options,
|
|
1071
|
+
world,
|
|
1072
|
+
locate: false,
|
|
1073
|
+
scroll: false,
|
|
1074
|
+
highlight: false,
|
|
1075
|
+
type: Types.TYPE_PRESS,
|
|
1076
|
+
text: `Type value: ${_value}`,
|
|
1077
|
+
operation: "type",
|
|
1078
|
+
log: "",
|
|
1079
|
+
};
|
|
1022
1080
|
try {
|
|
1023
|
-
|
|
1024
|
-
const valueSegment =
|
|
1081
|
+
await _preCommand(state, this);
|
|
1082
|
+
const valueSegment = state.value.split("&&");
|
|
1025
1083
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1026
1084
|
if (i > 0) {
|
|
1027
1085
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1041,134 +1099,76 @@ class StableBrowser {
|
|
|
1041
1099
|
await this.page.keyboard.type(value);
|
|
1042
1100
|
}
|
|
1043
1101
|
}
|
|
1044
|
-
return info;
|
|
1102
|
+
return state.info;
|
|
1045
1103
|
}
|
|
1046
1104
|
catch (e) {
|
|
1047
|
-
|
|
1048
|
-
this.logger.error("type failed " + info.log);
|
|
1049
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1050
|
-
info.screenshotPath = screenshotPath;
|
|
1051
|
-
Object.assign(e, { info: info });
|
|
1052
|
-
error = e;
|
|
1053
|
-
throw e;
|
|
1105
|
+
await _commandError(state, e, this);
|
|
1054
1106
|
}
|
|
1055
1107
|
finally {
|
|
1056
|
-
|
|
1057
|
-
this._reportToWorld(world, {
|
|
1058
|
-
type: Types.TYPE_PRESS,
|
|
1059
|
-
screenshotId,
|
|
1060
|
-
value: _value,
|
|
1061
|
-
text: `type value: ${_value}`,
|
|
1062
|
-
result: error
|
|
1063
|
-
? {
|
|
1064
|
-
status: "FAILED",
|
|
1065
|
-
startTime,
|
|
1066
|
-
endTime,
|
|
1067
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1068
|
-
}
|
|
1069
|
-
: {
|
|
1070
|
-
status: "PASSED",
|
|
1071
|
-
startTime,
|
|
1072
|
-
endTime,
|
|
1073
|
-
},
|
|
1074
|
-
info: info,
|
|
1075
|
-
});
|
|
1108
|
+
_commandFinally(state, this);
|
|
1076
1109
|
}
|
|
1077
1110
|
}
|
|
1078
1111
|
async setInputValue(selectors, value, _params = null, options = {}, world = null) {
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
let screenshotPath = null;
|
|
1112
|
+
const state = {
|
|
1113
|
+
selectors,
|
|
1114
|
+
_params,
|
|
1115
|
+
value,
|
|
1116
|
+
options,
|
|
1117
|
+
world,
|
|
1118
|
+
type: Types.SET_INPUT,
|
|
1119
|
+
text: `Set input value`,
|
|
1120
|
+
operation: "setInputValue",
|
|
1121
|
+
log: "***** set input value " + selectors.element_name + " *****\n",
|
|
1122
|
+
};
|
|
1091
1123
|
try {
|
|
1092
|
-
|
|
1093
|
-
let
|
|
1094
|
-
await this.scrollIfNeeded(element, info);
|
|
1095
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1096
|
-
await this._highlightElements(element);
|
|
1124
|
+
await _preCommand(state, this);
|
|
1125
|
+
let value = await this._replaceWithLocalData(state.value, this);
|
|
1097
1126
|
try {
|
|
1098
|
-
await element.evaluateHandle((el, value) => {
|
|
1127
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1099
1128
|
el.value = value;
|
|
1100
1129
|
}, value);
|
|
1101
1130
|
}
|
|
1102
1131
|
catch (error) {
|
|
1103
1132
|
this.logger.error("setInputValue failed, will try again");
|
|
1104
|
-
|
|
1105
|
-
info.
|
|
1106
|
-
|
|
1107
|
-
await element.evaluateHandle((el, value) => {
|
|
1133
|
+
await _screenshot(state, this);
|
|
1134
|
+
Object.assign(error, { info: state.info });
|
|
1135
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1108
1136
|
el.value = value;
|
|
1109
1137
|
});
|
|
1110
1138
|
}
|
|
1111
1139
|
}
|
|
1112
1140
|
catch (e) {
|
|
1113
|
-
|
|
1114
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1115
|
-
info.screenshotPath = screenshotPath;
|
|
1116
|
-
Object.assign(e, { info: info });
|
|
1117
|
-
error = e;
|
|
1118
|
-
throw e;
|
|
1141
|
+
await _commandError(state, e, this);
|
|
1119
1142
|
}
|
|
1120
1143
|
finally {
|
|
1121
|
-
|
|
1122
|
-
this._reportToWorld(world, {
|
|
1123
|
-
element_name: selectors.element_name,
|
|
1124
|
-
type: Types.SET_INPUT,
|
|
1125
|
-
text: `Set input value`,
|
|
1126
|
-
value: value,
|
|
1127
|
-
screenshotId,
|
|
1128
|
-
result: error
|
|
1129
|
-
? {
|
|
1130
|
-
status: "FAILED",
|
|
1131
|
-
startTime,
|
|
1132
|
-
endTime,
|
|
1133
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1134
|
-
}
|
|
1135
|
-
: {
|
|
1136
|
-
status: "PASSED",
|
|
1137
|
-
startTime,
|
|
1138
|
-
endTime,
|
|
1139
|
-
},
|
|
1140
|
-
info: info,
|
|
1141
|
-
});
|
|
1144
|
+
_commandFinally(state, this);
|
|
1142
1145
|
}
|
|
1143
1146
|
}
|
|
1144
1147
|
async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1148
|
+
const state = {
|
|
1149
|
+
selectors,
|
|
1150
|
+
_params,
|
|
1151
|
+
value: await this._replaceWithLocalData(value, this),
|
|
1152
|
+
options,
|
|
1153
|
+
world,
|
|
1154
|
+
type: Types.SET_DATE_TIME,
|
|
1155
|
+
text: `Set date time value: ${value}`,
|
|
1156
|
+
operation: "setDateTime",
|
|
1157
|
+
log: "***** set date time value " + selectors.element_name + " *****\n",
|
|
1158
|
+
throwError: false,
|
|
1159
|
+
};
|
|
1155
1160
|
try {
|
|
1156
|
-
|
|
1157
|
-
let element = await this._locate(selectors, info, _params);
|
|
1158
|
-
//insert red border around the element
|
|
1159
|
-
await this.scrollIfNeeded(element, info);
|
|
1160
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1161
|
-
await this._highlightElements(element);
|
|
1161
|
+
await _preCommand(state, this);
|
|
1162
1162
|
try {
|
|
1163
|
-
await element.click();
|
|
1163
|
+
await state.element.click();
|
|
1164
1164
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1165
1165
|
if (format) {
|
|
1166
|
-
value = dayjs(value).format(format);
|
|
1167
|
-
await element.fill(value);
|
|
1166
|
+
state.value = dayjs(state.value).format(format);
|
|
1167
|
+
await state.element.fill(state.value);
|
|
1168
1168
|
}
|
|
1169
1169
|
else {
|
|
1170
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1171
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1170
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1171
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1172
1172
|
el.value = ""; // clear input
|
|
1173
1173
|
el.value = dateTimeValue;
|
|
1174
1174
|
}, dateTimeValue);
|
|
@@ -1181,20 +1181,19 @@ class StableBrowser {
|
|
|
1181
1181
|
}
|
|
1182
1182
|
catch (err) {
|
|
1183
1183
|
//await this.closeUnexpectedPopups();
|
|
1184
|
-
this.logger.error("setting date time input failed " + JSON.stringify(info));
|
|
1184
|
+
this.logger.error("setting date time input failed " + JSON.stringify(state.info));
|
|
1185
1185
|
this.logger.info("Trying again");
|
|
1186
|
-
|
|
1187
|
-
info.
|
|
1188
|
-
Object.assign(err, { info: info });
|
|
1186
|
+
await _screenshot(state, this);
|
|
1187
|
+
Object.assign(err, { info: state.info });
|
|
1189
1188
|
await element.click();
|
|
1190
1189
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1191
1190
|
if (format) {
|
|
1192
|
-
value = dayjs(value).format(format);
|
|
1193
|
-
await element.fill(value);
|
|
1191
|
+
state.value = dayjs(state.value).format(format);
|
|
1192
|
+
await state.element.fill(state.value);
|
|
1194
1193
|
}
|
|
1195
1194
|
else {
|
|
1196
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1197
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1195
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1196
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1198
1197
|
el.value = ""; // clear input
|
|
1199
1198
|
el.value = dateTimeValue;
|
|
1200
1199
|
}, dateTimeValue);
|
|
@@ -1207,61 +1206,39 @@ class StableBrowser {
|
|
|
1207
1206
|
}
|
|
1208
1207
|
}
|
|
1209
1208
|
catch (e) {
|
|
1210
|
-
|
|
1211
|
-
throw e;
|
|
1209
|
+
await _commandError(state, e, this);
|
|
1212
1210
|
}
|
|
1213
1211
|
finally {
|
|
1214
|
-
|
|
1215
|
-
this._reportToWorld(world, {
|
|
1216
|
-
element_name: selectors.element_name,
|
|
1217
|
-
type: Types.SET_DATE_TIME,
|
|
1218
|
-
screenshotId,
|
|
1219
|
-
value: value,
|
|
1220
|
-
text: `setDateTime input with value: ${value}`,
|
|
1221
|
-
result: error
|
|
1222
|
-
? {
|
|
1223
|
-
status: "FAILED",
|
|
1224
|
-
startTime,
|
|
1225
|
-
endTime,
|
|
1226
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1227
|
-
}
|
|
1228
|
-
: {
|
|
1229
|
-
status: "PASSED",
|
|
1230
|
-
startTime,
|
|
1231
|
-
endTime,
|
|
1232
|
-
},
|
|
1233
|
-
info: info,
|
|
1234
|
-
});
|
|
1212
|
+
_commandFinally(state, this);
|
|
1235
1213
|
}
|
|
1236
1214
|
}
|
|
1237
1215
|
async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
|
|
1238
1216
|
_value = unEscapeString(_value);
|
|
1239
|
-
this._validateSelectors(selectors);
|
|
1240
|
-
const startTime = Date.now();
|
|
1241
|
-
let error = null;
|
|
1242
|
-
let screenshotId = null;
|
|
1243
|
-
let screenshotPath = null;
|
|
1244
|
-
const info = {};
|
|
1245
|
-
info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
|
|
1246
|
-
info.operation = "clickType";
|
|
1247
|
-
info.selectors = selectors;
|
|
1248
1217
|
const newValue = await this._replaceWithLocalData(_value, world);
|
|
1218
|
+
const state = {
|
|
1219
|
+
selectors,
|
|
1220
|
+
_params,
|
|
1221
|
+
value: newValue,
|
|
1222
|
+
originalValue: _value,
|
|
1223
|
+
options,
|
|
1224
|
+
world,
|
|
1225
|
+
type: Types.FILL,
|
|
1226
|
+
text: `Click type input with value: ${_value}`,
|
|
1227
|
+
operation: "clickType",
|
|
1228
|
+
log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
|
|
1229
|
+
};
|
|
1249
1230
|
if (newValue !== _value) {
|
|
1250
1231
|
//this.logger.info(_value + "=" + newValue);
|
|
1251
1232
|
_value = newValue;
|
|
1252
1233
|
}
|
|
1253
|
-
info.value = _value;
|
|
1254
1234
|
try {
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
await this.scrollIfNeeded(element, info);
|
|
1258
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1259
|
-
await this._highlightElements(element);
|
|
1235
|
+
await _preCommand(state, this);
|
|
1236
|
+
state.info.value = _value;
|
|
1260
1237
|
if (options === null || options === undefined || !options.press) {
|
|
1261
1238
|
try {
|
|
1262
|
-
let currentValue = await element.inputValue();
|
|
1239
|
+
let currentValue = await state.element.inputValue();
|
|
1263
1240
|
if (currentValue) {
|
|
1264
|
-
await element.fill("");
|
|
1241
|
+
await state.element.fill("");
|
|
1265
1242
|
}
|
|
1266
1243
|
}
|
|
1267
1244
|
catch (e) {
|
|
@@ -1270,22 +1247,22 @@ class StableBrowser {
|
|
|
1270
1247
|
}
|
|
1271
1248
|
if (options === null || options === undefined || options.press) {
|
|
1272
1249
|
try {
|
|
1273
|
-
await element.click({ timeout: 5000 });
|
|
1250
|
+
await state.element.click({ timeout: 5000 });
|
|
1274
1251
|
}
|
|
1275
1252
|
catch (e) {
|
|
1276
|
-
await element.dispatchEvent("click");
|
|
1253
|
+
await state.element.dispatchEvent("click");
|
|
1277
1254
|
}
|
|
1278
1255
|
}
|
|
1279
1256
|
else {
|
|
1280
1257
|
try {
|
|
1281
|
-
await element.focus();
|
|
1258
|
+
await state.element.focus();
|
|
1282
1259
|
}
|
|
1283
1260
|
catch (e) {
|
|
1284
|
-
await element.dispatchEvent("focus");
|
|
1261
|
+
await state.element.dispatchEvent("focus");
|
|
1285
1262
|
}
|
|
1286
1263
|
}
|
|
1287
1264
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1288
|
-
const valueSegment =
|
|
1265
|
+
const valueSegment = state.value.split("&&");
|
|
1289
1266
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1290
1267
|
if (i > 0) {
|
|
1291
1268
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1305,13 +1282,14 @@ class StableBrowser {
|
|
|
1305
1282
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1306
1283
|
}
|
|
1307
1284
|
}
|
|
1285
|
+
await _screenshot(state, this);
|
|
1308
1286
|
if (enter === true) {
|
|
1309
1287
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1310
1288
|
await this.page.keyboard.press("Enter");
|
|
1311
1289
|
await this.waitForPageLoad();
|
|
1312
1290
|
}
|
|
1313
1291
|
else if (enter === false) {
|
|
1314
|
-
await element.dispatchEvent("change");
|
|
1292
|
+
await state.element.dispatchEvent("change");
|
|
1315
1293
|
//await this.page.keyboard.press("Tab");
|
|
1316
1294
|
}
|
|
1317
1295
|
else {
|
|
@@ -1320,104 +1298,50 @@ class StableBrowser {
|
|
|
1320
1298
|
await this.waitForPageLoad();
|
|
1321
1299
|
}
|
|
1322
1300
|
}
|
|
1323
|
-
return info;
|
|
1301
|
+
return state.info;
|
|
1324
1302
|
}
|
|
1325
1303
|
catch (e) {
|
|
1326
|
-
|
|
1327
|
-
this.logger.error("fill failed " + JSON.stringify(info));
|
|
1328
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1329
|
-
info.screenshotPath = screenshotPath;
|
|
1330
|
-
Object.assign(e, { info: info });
|
|
1331
|
-
error = e;
|
|
1332
|
-
throw e;
|
|
1304
|
+
await _commandError(state, e, this);
|
|
1333
1305
|
}
|
|
1334
1306
|
finally {
|
|
1335
|
-
|
|
1336
|
-
this._reportToWorld(world, {
|
|
1337
|
-
element_name: selectors.element_name,
|
|
1338
|
-
type: Types.FILL,
|
|
1339
|
-
screenshotId,
|
|
1340
|
-
value: _value,
|
|
1341
|
-
text: `clickType input with value: ${_value}`,
|
|
1342
|
-
result: error
|
|
1343
|
-
? {
|
|
1344
|
-
status: "FAILED",
|
|
1345
|
-
startTime,
|
|
1346
|
-
endTime,
|
|
1347
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1348
|
-
}
|
|
1349
|
-
: {
|
|
1350
|
-
status: "PASSED",
|
|
1351
|
-
startTime,
|
|
1352
|
-
endTime,
|
|
1353
|
-
},
|
|
1354
|
-
info: info,
|
|
1355
|
-
});
|
|
1307
|
+
_commandFinally(state, this);
|
|
1356
1308
|
}
|
|
1357
1309
|
}
|
|
1358
1310
|
async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1311
|
+
const state = {
|
|
1312
|
+
selectors,
|
|
1313
|
+
_params,
|
|
1314
|
+
value: unEscapeString(value),
|
|
1315
|
+
options,
|
|
1316
|
+
world,
|
|
1317
|
+
type: Types.FILL,
|
|
1318
|
+
text: `Fill input with value: ${value}`,
|
|
1319
|
+
operation: "fill",
|
|
1320
|
+
log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
|
|
1321
|
+
};
|
|
1370
1322
|
try {
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
await
|
|
1374
|
-
await element.fill(value);
|
|
1375
|
-
await element.dispatchEvent("change");
|
|
1323
|
+
await _preCommand(state, this);
|
|
1324
|
+
await state.element.fill(value);
|
|
1325
|
+
await state.element.dispatchEvent("change");
|
|
1376
1326
|
if (enter) {
|
|
1377
|
-
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1378
|
-
await this.page.keyboard.press("Enter");
|
|
1379
|
-
}
|
|
1380
|
-
await this.waitForPageLoad();
|
|
1381
|
-
return info;
|
|
1382
|
-
}
|
|
1383
|
-
catch (e) {
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
Object.assign(e, { info: info });
|
|
1389
|
-
error = e;
|
|
1390
|
-
throw e;
|
|
1391
|
-
}
|
|
1392
|
-
finally {
|
|
1393
|
-
const endTime = Date.now();
|
|
1394
|
-
this._reportToWorld(world, {
|
|
1395
|
-
element_name: selectors.element_name,
|
|
1396
|
-
type: Types.FILL,
|
|
1397
|
-
screenshotId,
|
|
1398
|
-
value,
|
|
1399
|
-
text: `Fill input with value: ${value}`,
|
|
1400
|
-
result: error
|
|
1401
|
-
? {
|
|
1402
|
-
status: "FAILED",
|
|
1403
|
-
startTime,
|
|
1404
|
-
endTime,
|
|
1405
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1406
|
-
}
|
|
1407
|
-
: {
|
|
1408
|
-
status: "PASSED",
|
|
1409
|
-
startTime,
|
|
1410
|
-
endTime,
|
|
1411
|
-
},
|
|
1412
|
-
info: info,
|
|
1413
|
-
});
|
|
1327
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1328
|
+
await this.page.keyboard.press("Enter");
|
|
1329
|
+
}
|
|
1330
|
+
await this.waitForPageLoad();
|
|
1331
|
+
return state.info;
|
|
1332
|
+
}
|
|
1333
|
+
catch (e) {
|
|
1334
|
+
await _commandError(state, e, this);
|
|
1335
|
+
}
|
|
1336
|
+
finally {
|
|
1337
|
+
_commandFinally(state, this);
|
|
1414
1338
|
}
|
|
1415
1339
|
}
|
|
1416
1340
|
async getText(selectors, _params = null, options = {}, info = {}, world = null) {
|
|
1417
1341
|
return await this._getText(selectors, 0, _params, options, info, world);
|
|
1418
1342
|
}
|
|
1419
1343
|
async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
|
|
1420
|
-
|
|
1344
|
+
_validateSelectors(selectors);
|
|
1421
1345
|
let screenshotId = null;
|
|
1422
1346
|
let screenshotPath = null;
|
|
1423
1347
|
if (!info.log) {
|
|
@@ -1461,166 +1385,124 @@ class StableBrowser {
|
|
|
1461
1385
|
}
|
|
1462
1386
|
}
|
|
1463
1387
|
async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
|
|
1464
|
-
var _a;
|
|
1465
|
-
this._validateSelectors(selectors);
|
|
1466
1388
|
if (!pattern) {
|
|
1467
1389
|
throw new Error("pattern is null");
|
|
1468
1390
|
}
|
|
1469
1391
|
if (!text) {
|
|
1470
1392
|
throw new Error("text is null");
|
|
1471
1393
|
}
|
|
1394
|
+
const state = {
|
|
1395
|
+
selectors,
|
|
1396
|
+
_params,
|
|
1397
|
+
pattern,
|
|
1398
|
+
value: pattern,
|
|
1399
|
+
options,
|
|
1400
|
+
world,
|
|
1401
|
+
locate: false,
|
|
1402
|
+
scroll: false,
|
|
1403
|
+
screenshot: false,
|
|
1404
|
+
highlight: false,
|
|
1405
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1406
|
+
text: `Verify element contains pattern: ${pattern}`,
|
|
1407
|
+
operation: "containsPattern",
|
|
1408
|
+
log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
|
|
1409
|
+
};
|
|
1472
1410
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1473
1411
|
if (newValue !== text) {
|
|
1474
1412
|
this.logger.info(text + "=" + newValue);
|
|
1475
1413
|
text = newValue;
|
|
1476
1414
|
}
|
|
1477
|
-
const startTime = Date.now();
|
|
1478
|
-
let error = null;
|
|
1479
|
-
let screenshotId = null;
|
|
1480
|
-
let screenshotPath = null;
|
|
1481
|
-
const info = {};
|
|
1482
|
-
info.log =
|
|
1483
|
-
"***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
|
|
1484
|
-
info.operation = "containsPattern";
|
|
1485
|
-
info.selectors = selectors;
|
|
1486
|
-
info.value = text;
|
|
1487
|
-
info.pattern = pattern;
|
|
1488
1415
|
let foundObj = null;
|
|
1489
1416
|
try {
|
|
1490
|
-
|
|
1417
|
+
await _preCommand(state, this);
|
|
1418
|
+
state.info.pattern = pattern;
|
|
1419
|
+
foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
|
|
1491
1420
|
if (foundObj && foundObj.element) {
|
|
1492
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1421
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1493
1422
|
}
|
|
1494
|
-
|
|
1423
|
+
await _screenshot(state, this);
|
|
1495
1424
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1496
1425
|
pattern = pattern.replace("{text}", escapedText);
|
|
1497
1426
|
let regex = new RegExp(pattern, "im");
|
|
1498
|
-
if (!regex.test(foundObj
|
|
1499
|
-
info.foundText = foundObj
|
|
1427
|
+
if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
|
|
1428
|
+
state.info.foundText = foundObj?.text;
|
|
1500
1429
|
throw new Error("element doesn't contain text " + text);
|
|
1501
1430
|
}
|
|
1502
|
-
return info;
|
|
1431
|
+
return state.info;
|
|
1503
1432
|
}
|
|
1504
1433
|
catch (e) {
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
|
|
1508
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1509
|
-
info.screenshotPath = screenshotPath;
|
|
1510
|
-
Object.assign(e, { info: info });
|
|
1511
|
-
error = e;
|
|
1512
|
-
throw e;
|
|
1434
|
+
this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
|
|
1435
|
+
await _commandError(state, e, this);
|
|
1513
1436
|
}
|
|
1514
1437
|
finally {
|
|
1515
|
-
|
|
1516
|
-
this._reportToWorld(world, {
|
|
1517
|
-
element_name: selectors.element_name,
|
|
1518
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1519
|
-
value: pattern,
|
|
1520
|
-
text: `Verify element contains pattern: ${pattern}`,
|
|
1521
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1522
|
-
result: error
|
|
1523
|
-
? {
|
|
1524
|
-
status: "FAILED",
|
|
1525
|
-
startTime,
|
|
1526
|
-
endTime,
|
|
1527
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1528
|
-
}
|
|
1529
|
-
: {
|
|
1530
|
-
status: "PASSED",
|
|
1531
|
-
startTime,
|
|
1532
|
-
endTime,
|
|
1533
|
-
},
|
|
1534
|
-
info: info,
|
|
1535
|
-
});
|
|
1438
|
+
_commandFinally(state, this);
|
|
1536
1439
|
}
|
|
1537
1440
|
}
|
|
1538
1441
|
async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1442
|
+
const state = {
|
|
1443
|
+
selectors,
|
|
1444
|
+
_params,
|
|
1445
|
+
value: text,
|
|
1446
|
+
options,
|
|
1447
|
+
world,
|
|
1448
|
+
locate: false,
|
|
1449
|
+
scroll: false,
|
|
1450
|
+
screenshot: false,
|
|
1451
|
+
highlight: false,
|
|
1452
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1453
|
+
text: `Verify element contains text: ${text}`,
|
|
1454
|
+
operation: "containsText",
|
|
1455
|
+
log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
|
|
1456
|
+
};
|
|
1542
1457
|
if (!text) {
|
|
1543
1458
|
throw new Error("text is null");
|
|
1544
1459
|
}
|
|
1545
|
-
|
|
1546
|
-
let error = null;
|
|
1547
|
-
let screenshotId = null;
|
|
1548
|
-
let screenshotPath = null;
|
|
1549
|
-
const info = {};
|
|
1550
|
-
info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
|
|
1551
|
-
info.operation = "containsText";
|
|
1552
|
-
info.selectors = selectors;
|
|
1460
|
+
text = unEscapeString(text);
|
|
1553
1461
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1554
1462
|
if (newValue !== text) {
|
|
1555
1463
|
this.logger.info(text + "=" + newValue);
|
|
1556
1464
|
text = newValue;
|
|
1557
1465
|
}
|
|
1558
|
-
info.value = text;
|
|
1559
1466
|
let foundObj = null;
|
|
1560
1467
|
try {
|
|
1561
|
-
|
|
1468
|
+
await _preCommand(state, this);
|
|
1469
|
+
foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
|
|
1562
1470
|
if (foundObj && foundObj.element) {
|
|
1563
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1471
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1564
1472
|
}
|
|
1565
|
-
|
|
1473
|
+
await _screenshot(state, this);
|
|
1566
1474
|
const dateAlternatives = findDateAlternatives(text);
|
|
1567
1475
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1568
1476
|
if (dateAlternatives.date) {
|
|
1569
1477
|
for (let i = 0; i < dateAlternatives.dates.length; i++) {
|
|
1570
|
-
if (
|
|
1571
|
-
|
|
1572
|
-
return info;
|
|
1478
|
+
if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
|
|
1479
|
+
foundObj?.value?.includes(dateAlternatives.dates[i])) {
|
|
1480
|
+
return state.info;
|
|
1573
1481
|
}
|
|
1574
1482
|
}
|
|
1575
1483
|
throw new Error("element doesn't contain text " + text);
|
|
1576
1484
|
}
|
|
1577
1485
|
else if (numberAlternatives.number) {
|
|
1578
1486
|
for (let i = 0; i < numberAlternatives.numbers.length; i++) {
|
|
1579
|
-
if (
|
|
1580
|
-
|
|
1581
|
-
return info;
|
|
1487
|
+
if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
|
|
1488
|
+
foundObj?.value?.includes(numberAlternatives.numbers[i])) {
|
|
1489
|
+
return state.info;
|
|
1582
1490
|
}
|
|
1583
1491
|
}
|
|
1584
1492
|
throw new Error("element doesn't contain text " + text);
|
|
1585
1493
|
}
|
|
1586
|
-
else if (!
|
|
1587
|
-
info.foundText = foundObj
|
|
1588
|
-
info.value = foundObj
|
|
1494
|
+
else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
|
|
1495
|
+
state.info.foundText = foundObj?.text;
|
|
1496
|
+
state.info.value = foundObj?.value;
|
|
1589
1497
|
throw new Error("element doesn't contain text " + text);
|
|
1590
1498
|
}
|
|
1591
|
-
return info;
|
|
1499
|
+
return state.info;
|
|
1592
1500
|
}
|
|
1593
1501
|
catch (e) {
|
|
1594
|
-
|
|
1595
|
-
this.logger.error("verify element contains text failed " + info.log);
|
|
1596
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1597
|
-
info.screenshotPath = screenshotPath;
|
|
1598
|
-
Object.assign(e, { info: info });
|
|
1599
|
-
error = e;
|
|
1600
|
-
throw e;
|
|
1502
|
+
await _commandError(state, e, this);
|
|
1601
1503
|
}
|
|
1602
1504
|
finally {
|
|
1603
|
-
|
|
1604
|
-
this._reportToWorld(world, {
|
|
1605
|
-
element_name: selectors.element_name,
|
|
1606
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1607
|
-
text: `Verify element contains text: ${text}`,
|
|
1608
|
-
value: text,
|
|
1609
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1610
|
-
result: error
|
|
1611
|
-
? {
|
|
1612
|
-
status: "FAILED",
|
|
1613
|
-
startTime,
|
|
1614
|
-
endTime,
|
|
1615
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1616
|
-
}
|
|
1617
|
-
: {
|
|
1618
|
-
status: "PASSED",
|
|
1619
|
-
startTime,
|
|
1620
|
-
endTime,
|
|
1621
|
-
},
|
|
1622
|
-
info: info,
|
|
1623
|
-
});
|
|
1505
|
+
_commandFinally(state, this);
|
|
1624
1506
|
}
|
|
1625
1507
|
}
|
|
1626
1508
|
_getDataFile(world = null) {
|
|
@@ -1849,7 +1731,6 @@ class StableBrowser {
|
|
|
1849
1731
|
}
|
|
1850
1732
|
async takeScreenshot(screenshotPath) {
|
|
1851
1733
|
const playContext = this.context.playContext;
|
|
1852
|
-
const client = await playContext.newCDPSession(this.page);
|
|
1853
1734
|
// Using CDP to capture the screenshot
|
|
1854
1735
|
const viewportWidth = Math.max(...(await this.page.evaluate(() => [
|
|
1855
1736
|
document.body.scrollWidth,
|
|
@@ -1859,20 +1740,28 @@ class StableBrowser {
|
|
|
1859
1740
|
document.body.clientWidth,
|
|
1860
1741
|
document.documentElement.clientWidth,
|
|
1861
1742
|
])));
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1743
|
+
let screenshotBuffer = null;
|
|
1744
|
+
if (this.context.browserName === "chromium") {
|
|
1745
|
+
const client = await playContext.newCDPSession(this.page);
|
|
1746
|
+
const { data } = await client.send("Page.captureScreenshot", {
|
|
1747
|
+
format: "png",
|
|
1748
|
+
// clip: {
|
|
1749
|
+
// x: 0,
|
|
1750
|
+
// y: 0,
|
|
1751
|
+
// width: viewportWidth,
|
|
1752
|
+
// height: viewportHeight,
|
|
1753
|
+
// scale: 1,
|
|
1754
|
+
// },
|
|
1755
|
+
});
|
|
1756
|
+
await client.detach();
|
|
1757
|
+
if (!screenshotPath) {
|
|
1758
|
+
return data;
|
|
1759
|
+
}
|
|
1760
|
+
screenshotBuffer = Buffer.from(data, "base64");
|
|
1761
|
+
}
|
|
1762
|
+
else {
|
|
1763
|
+
screenshotBuffer = await this.page.screenshot();
|
|
1874
1764
|
}
|
|
1875
|
-
let screenshotBuffer = Buffer.from(data, "base64");
|
|
1876
1765
|
let image = await Jimp.read(screenshotBuffer);
|
|
1877
1766
|
// Get the image dimensions
|
|
1878
1767
|
const { width, height } = image.bitmap;
|
|
@@ -1885,130 +1774,71 @@ class StableBrowser {
|
|
|
1885
1774
|
else {
|
|
1886
1775
|
fs.writeFileSync(screenshotPath, screenshotBuffer);
|
|
1887
1776
|
}
|
|
1888
|
-
await client.detach();
|
|
1889
1777
|
}
|
|
1890
1778
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1779
|
+
const state = {
|
|
1780
|
+
selectors,
|
|
1781
|
+
_params,
|
|
1782
|
+
options,
|
|
1783
|
+
world,
|
|
1784
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1785
|
+
text: `Verify element exists in page`,
|
|
1786
|
+
operation: "verifyElementExistInPage",
|
|
1787
|
+
log: "***** verify element " + selectors.element_name + " exists in page *****\n",
|
|
1788
|
+
};
|
|
1896
1789
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1897
|
-
const info = {};
|
|
1898
|
-
info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
|
|
1899
|
-
info.operation = "verify";
|
|
1900
|
-
info.selectors = selectors;
|
|
1901
1790
|
try {
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
}
|
|
1906
|
-
await this._highlightElements(element);
|
|
1907
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1908
|
-
await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
1909
|
-
return info;
|
|
1791
|
+
await _preCommand(state, this);
|
|
1792
|
+
await expect(state.element).toHaveCount(1, { timeout: 10000 });
|
|
1793
|
+
return state.info;
|
|
1910
1794
|
}
|
|
1911
1795
|
catch (e) {
|
|
1912
|
-
|
|
1913
|
-
this.logger.error("verify failed " + info.log);
|
|
1914
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1915
|
-
info.screenshotPath = screenshotPath;
|
|
1916
|
-
Object.assign(e, { info: info });
|
|
1917
|
-
error = e;
|
|
1918
|
-
throw e;
|
|
1796
|
+
await _commandError(state, e, this);
|
|
1919
1797
|
}
|
|
1920
1798
|
finally {
|
|
1921
|
-
|
|
1922
|
-
this._reportToWorld(world, {
|
|
1923
|
-
element_name: selectors.element_name,
|
|
1924
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1925
|
-
text: "Verify element exists in page",
|
|
1926
|
-
screenshotId,
|
|
1927
|
-
result: error
|
|
1928
|
-
? {
|
|
1929
|
-
status: "FAILED",
|
|
1930
|
-
startTime,
|
|
1931
|
-
endTime,
|
|
1932
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1933
|
-
}
|
|
1934
|
-
: {
|
|
1935
|
-
status: "PASSED",
|
|
1936
|
-
startTime,
|
|
1937
|
-
endTime,
|
|
1938
|
-
},
|
|
1939
|
-
info: info,
|
|
1940
|
-
});
|
|
1799
|
+
_commandFinally(state, this);
|
|
1941
1800
|
}
|
|
1942
1801
|
}
|
|
1943
1802
|
async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1803
|
+
const state = {
|
|
1804
|
+
selectors,
|
|
1805
|
+
_params,
|
|
1806
|
+
attribute,
|
|
1807
|
+
variable,
|
|
1808
|
+
options,
|
|
1809
|
+
world,
|
|
1810
|
+
type: Types.EXTRACT,
|
|
1811
|
+
text: `Extract attribute from element`,
|
|
1812
|
+
operation: "extractAttribute",
|
|
1813
|
+
log: "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1814
|
+
};
|
|
1949
1815
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1950
|
-
const info = {};
|
|
1951
|
-
info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
|
|
1952
|
-
info.operation = "extract";
|
|
1953
|
-
info.selectors = selectors;
|
|
1954
1816
|
try {
|
|
1955
|
-
|
|
1956
|
-
await this._highlightElements(element);
|
|
1957
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1817
|
+
await _preCommand(state, this);
|
|
1958
1818
|
switch (attribute) {
|
|
1959
1819
|
case "inner_text":
|
|
1960
|
-
|
|
1820
|
+
state.value = await state.element.innerText();
|
|
1961
1821
|
break;
|
|
1962
1822
|
case "href":
|
|
1963
|
-
|
|
1823
|
+
state.value = await state.element.getAttribute("href");
|
|
1964
1824
|
break;
|
|
1965
1825
|
case "value":
|
|
1966
|
-
|
|
1826
|
+
state.value = await state.element.inputValue();
|
|
1967
1827
|
break;
|
|
1968
1828
|
default:
|
|
1969
|
-
|
|
1829
|
+
state.value = await state.element.getAttribute(attribute);
|
|
1970
1830
|
break;
|
|
1971
1831
|
}
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
this.setTestData({ [variable]: info.value }, world);
|
|
1977
|
-
this.logger.info("set test data: " + variable + "=" + info.value);
|
|
1978
|
-
return info;
|
|
1832
|
+
state.info.value = state.value;
|
|
1833
|
+
this.setTestData({ [variable]: state.value }, world);
|
|
1834
|
+
this.logger.info("set test data: " + variable + "=" + state.value);
|
|
1835
|
+
return state.info;
|
|
1979
1836
|
}
|
|
1980
1837
|
catch (e) {
|
|
1981
|
-
|
|
1982
|
-
this.logger.error("extract failed " + info.log);
|
|
1983
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1984
|
-
info.screenshotPath = screenshotPath;
|
|
1985
|
-
Object.assign(e, { info: info });
|
|
1986
|
-
error = e;
|
|
1987
|
-
throw e;
|
|
1838
|
+
await _commandError(state, e, this);
|
|
1988
1839
|
}
|
|
1989
1840
|
finally {
|
|
1990
|
-
|
|
1991
|
-
this._reportToWorld(world, {
|
|
1992
|
-
element_name: selectors.element_name,
|
|
1993
|
-
type: Types.EXTRACT_ATTRIBUTE,
|
|
1994
|
-
variable: variable,
|
|
1995
|
-
value: info.value,
|
|
1996
|
-
text: "Extract attribute from element",
|
|
1997
|
-
screenshotId,
|
|
1998
|
-
result: error
|
|
1999
|
-
? {
|
|
2000
|
-
status: "FAILED",
|
|
2001
|
-
startTime,
|
|
2002
|
-
endTime,
|
|
2003
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2004
|
-
}
|
|
2005
|
-
: {
|
|
2006
|
-
status: "PASSED",
|
|
2007
|
-
startTime,
|
|
2008
|
-
endTime,
|
|
2009
|
-
},
|
|
2010
|
-
info: info,
|
|
2011
|
-
});
|
|
1841
|
+
_commandFinally(state, this);
|
|
2012
1842
|
}
|
|
2013
1843
|
}
|
|
2014
1844
|
async extractEmailData(emailAddress, options, world) {
|
|
@@ -2085,7 +1915,8 @@ class StableBrowser {
|
|
|
2085
1915
|
catch (e) {
|
|
2086
1916
|
errorCount++;
|
|
2087
1917
|
if (errorCount > 3) {
|
|
2088
|
-
throw e;
|
|
1918
|
+
// throw e;
|
|
1919
|
+
await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
|
|
2089
1920
|
}
|
|
2090
1921
|
// ignore
|
|
2091
1922
|
}
|
|
@@ -2196,7 +2027,8 @@ class StableBrowser {
|
|
|
2196
2027
|
info.screenshotPath = screenshotPath;
|
|
2197
2028
|
Object.assign(e, { info: info });
|
|
2198
2029
|
error = e;
|
|
2199
|
-
throw e;
|
|
2030
|
+
// throw e;
|
|
2031
|
+
await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
|
|
2200
2032
|
}
|
|
2201
2033
|
finally {
|
|
2202
2034
|
const endTime = Date.now();
|
|
@@ -2209,7 +2041,7 @@ class StableBrowser {
|
|
|
2209
2041
|
status: "FAILED",
|
|
2210
2042
|
startTime,
|
|
2211
2043
|
endTime,
|
|
2212
|
-
message: error
|
|
2044
|
+
message: error?.message,
|
|
2213
2045
|
}
|
|
2214
2046
|
: {
|
|
2215
2047
|
status: "PASSED",
|
|
@@ -2222,52 +2054,58 @@ class StableBrowser {
|
|
|
2222
2054
|
}
|
|
2223
2055
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2224
2056
|
text = unEscapeString(text);
|
|
2225
|
-
const
|
|
2057
|
+
const state = {
|
|
2058
|
+
text_search: text,
|
|
2059
|
+
options,
|
|
2060
|
+
world,
|
|
2061
|
+
locate: false,
|
|
2062
|
+
scroll: false,
|
|
2063
|
+
highlight: false,
|
|
2064
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
2065
|
+
text: `Verify text exists in page`,
|
|
2066
|
+
operation: "verifyTextExistInPage",
|
|
2067
|
+
log: "***** verify text " + text + " exists in page *****\n",
|
|
2068
|
+
};
|
|
2226
2069
|
const timeout = this._getLoadTimeout(options);
|
|
2227
|
-
let error = null;
|
|
2228
|
-
let screenshotId = null;
|
|
2229
|
-
let screenshotPath = null;
|
|
2230
2070
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2231
|
-
const info = {};
|
|
2232
|
-
info.log = "***** verify text " + text + " exists in page *****\n";
|
|
2233
|
-
info.operation = "verifyTextExistInPage";
|
|
2234
2071
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
2235
2072
|
if (newValue !== text) {
|
|
2236
2073
|
this.logger.info(text + "=" + newValue);
|
|
2237
2074
|
text = newValue;
|
|
2238
2075
|
}
|
|
2239
|
-
info.text = text;
|
|
2240
2076
|
let dateAlternatives = findDateAlternatives(text);
|
|
2241
2077
|
let numberAlternatives = findNumberAlternatives(text);
|
|
2242
2078
|
try {
|
|
2079
|
+
await _preCommand(state, this);
|
|
2080
|
+
state.info.text = text;
|
|
2243
2081
|
while (true) {
|
|
2244
2082
|
const frames = this.page.frames();
|
|
2245
2083
|
let results = [];
|
|
2246
2084
|
for (let i = 0; i < frames.length; i++) {
|
|
2247
2085
|
if (dateAlternatives.date) {
|
|
2248
2086
|
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2249
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "
|
|
2087
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2250
2088
|
result.frame = frames[i];
|
|
2251
2089
|
results.push(result);
|
|
2252
2090
|
}
|
|
2253
2091
|
}
|
|
2254
2092
|
else if (numberAlternatives.number) {
|
|
2255
2093
|
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2256
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "
|
|
2094
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2257
2095
|
result.frame = frames[i];
|
|
2258
2096
|
results.push(result);
|
|
2259
2097
|
}
|
|
2260
2098
|
}
|
|
2261
2099
|
else {
|
|
2262
|
-
const result = await this._locateElementByText(frames[i], text, "
|
|
2100
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2263
2101
|
result.frame = frames[i];
|
|
2264
2102
|
results.push(result);
|
|
2265
2103
|
}
|
|
2266
2104
|
}
|
|
2267
|
-
info.results = results;
|
|
2105
|
+
state.info.results = results;
|
|
2268
2106
|
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2269
2107
|
if (resultWithElementsFound.length === 0) {
|
|
2270
|
-
if (Date.now() - startTime > timeout) {
|
|
2108
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2271
2109
|
throw new Error(`Text ${text} not found in page`);
|
|
2272
2110
|
}
|
|
2273
2111
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -2279,44 +2117,89 @@ class StableBrowser {
|
|
|
2279
2117
|
await this._highlightElements(frame, dataAttribute);
|
|
2280
2118
|
const element = await frame.$(dataAttribute);
|
|
2281
2119
|
if (element) {
|
|
2282
|
-
await this.scrollIfNeeded(element, info);
|
|
2120
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2283
2121
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2284
2122
|
}
|
|
2285
2123
|
}
|
|
2286
|
-
|
|
2287
|
-
return info;
|
|
2124
|
+
await _screenshot(state, this);
|
|
2125
|
+
return state.info;
|
|
2288
2126
|
}
|
|
2289
2127
|
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2290
2128
|
}
|
|
2291
2129
|
catch (e) {
|
|
2292
|
-
|
|
2293
|
-
this.logger.error("verify text exist in page failed " + info.log);
|
|
2294
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2295
|
-
info.screenshotPath = screenshotPath;
|
|
2296
|
-
Object.assign(e, { info: info });
|
|
2297
|
-
error = e;
|
|
2298
|
-
throw e;
|
|
2130
|
+
await _commandError(state, e, this);
|
|
2299
2131
|
}
|
|
2300
2132
|
finally {
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2133
|
+
_commandFinally(state, this);
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
async waitForTextToDisappear(text, options = {}, world = null) {
|
|
2137
|
+
text = unEscapeString(text);
|
|
2138
|
+
const state = {
|
|
2139
|
+
text_search: text,
|
|
2140
|
+
options,
|
|
2141
|
+
world,
|
|
2142
|
+
locate: false,
|
|
2143
|
+
scroll: false,
|
|
2144
|
+
highlight: false,
|
|
2145
|
+
type: Types.WAIT_FOR_TEXT_TO_DISAPPEAR,
|
|
2146
|
+
text: `Verify text does not exist in page`,
|
|
2147
|
+
operation: "verifyTextNotExistInPage",
|
|
2148
|
+
log: "***** verify text " + text + " does not exist in page *****\n",
|
|
2149
|
+
};
|
|
2150
|
+
const timeout = this._getLoadTimeout(options);
|
|
2151
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2152
|
+
const newValue = await this._replaceWithLocalData(text, world);
|
|
2153
|
+
if (newValue !== text) {
|
|
2154
|
+
this.logger.info(text + "=" + newValue);
|
|
2155
|
+
text = newValue;
|
|
2156
|
+
}
|
|
2157
|
+
let dateAlternatives = findDateAlternatives(text);
|
|
2158
|
+
let numberAlternatives = findNumberAlternatives(text);
|
|
2159
|
+
try {
|
|
2160
|
+
await _preCommand(state, this);
|
|
2161
|
+
state.info.text = text;
|
|
2162
|
+
while (true) {
|
|
2163
|
+
const frames = this.page.frames();
|
|
2164
|
+
let results = [];
|
|
2165
|
+
for (let i = 0; i < frames.length; i++) {
|
|
2166
|
+
if (dateAlternatives.date) {
|
|
2167
|
+
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2168
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2169
|
+
result.frame = frames[i];
|
|
2170
|
+
results.push(result);
|
|
2171
|
+
}
|
|
2312
2172
|
}
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2173
|
+
else if (numberAlternatives.number) {
|
|
2174
|
+
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2175
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2176
|
+
result.frame = frames[i];
|
|
2177
|
+
results.push(result);
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
else {
|
|
2181
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2182
|
+
result.frame = frames[i];
|
|
2183
|
+
results.push(result);
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
state.info.results = results;
|
|
2187
|
+
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2188
|
+
if (resultWithElementsFound.length === 0) {
|
|
2189
|
+
await _screenshot(state, this);
|
|
2190
|
+
return state.info;
|
|
2191
|
+
}
|
|
2192
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2193
|
+
throw new Error(`Text ${text} found in page`);
|
|
2194
|
+
}
|
|
2195
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
catch (e) {
|
|
2199
|
+
await _commandError(state, e, this);
|
|
2200
|
+
}
|
|
2201
|
+
finally {
|
|
2202
|
+
_commandFinally(state, this);
|
|
2320
2203
|
}
|
|
2321
2204
|
}
|
|
2322
2205
|
_getServerUrl() {
|
|
@@ -2379,7 +2262,8 @@ class StableBrowser {
|
|
|
2379
2262
|
info.screenshotPath = screenshotPath;
|
|
2380
2263
|
Object.assign(e, { info: info });
|
|
2381
2264
|
error = e;
|
|
2382
|
-
throw e;
|
|
2265
|
+
// throw e;
|
|
2266
|
+
await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
|
|
2383
2267
|
}
|
|
2384
2268
|
finally {
|
|
2385
2269
|
const endTime = Date.now();
|
|
@@ -2392,7 +2276,7 @@ class StableBrowser {
|
|
|
2392
2276
|
status: "FAILED",
|
|
2393
2277
|
startTime,
|
|
2394
2278
|
endTime,
|
|
2395
|
-
message: error
|
|
2279
|
+
message: error?.message,
|
|
2396
2280
|
}
|
|
2397
2281
|
: {
|
|
2398
2282
|
status: "PASSED",
|
|
@@ -2424,7 +2308,7 @@ class StableBrowser {
|
|
|
2424
2308
|
this.logger.info("Table data verified");
|
|
2425
2309
|
}
|
|
2426
2310
|
async getTableData(selectors, _params = null, options = {}, world = null) {
|
|
2427
|
-
|
|
2311
|
+
_validateSelectors(selectors);
|
|
2428
2312
|
const startTime = Date.now();
|
|
2429
2313
|
let error = null;
|
|
2430
2314
|
let screenshotId = null;
|
|
@@ -2446,7 +2330,8 @@ class StableBrowser {
|
|
|
2446
2330
|
info.screenshotPath = screenshotPath;
|
|
2447
2331
|
Object.assign(e, { info: info });
|
|
2448
2332
|
error = e;
|
|
2449
|
-
throw e;
|
|
2333
|
+
// throw e;
|
|
2334
|
+
await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
|
|
2450
2335
|
}
|
|
2451
2336
|
finally {
|
|
2452
2337
|
const endTime = Date.now();
|
|
@@ -2460,7 +2345,7 @@ class StableBrowser {
|
|
|
2460
2345
|
status: "FAILED",
|
|
2461
2346
|
startTime,
|
|
2462
2347
|
endTime,
|
|
2463
|
-
message: error
|
|
2348
|
+
message: error?.message,
|
|
2464
2349
|
}
|
|
2465
2350
|
: {
|
|
2466
2351
|
status: "PASSED",
|
|
@@ -2472,7 +2357,7 @@ class StableBrowser {
|
|
|
2472
2357
|
}
|
|
2473
2358
|
}
|
|
2474
2359
|
async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
|
|
2475
|
-
|
|
2360
|
+
_validateSelectors(selectors);
|
|
2476
2361
|
if (!query) {
|
|
2477
2362
|
throw new Error("query is null");
|
|
2478
2363
|
}
|
|
@@ -2611,7 +2496,8 @@ class StableBrowser {
|
|
|
2611
2496
|
info.screenshotPath = screenshotPath;
|
|
2612
2497
|
Object.assign(e, { info: info });
|
|
2613
2498
|
error = e;
|
|
2614
|
-
throw e;
|
|
2499
|
+
// throw e;
|
|
2500
|
+
await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
|
|
2615
2501
|
}
|
|
2616
2502
|
finally {
|
|
2617
2503
|
const endTime = Date.now();
|
|
@@ -2625,7 +2511,7 @@ class StableBrowser {
|
|
|
2625
2511
|
status: "FAILED",
|
|
2626
2512
|
startTime,
|
|
2627
2513
|
endTime,
|
|
2628
|
-
message: error
|
|
2514
|
+
message: error?.message,
|
|
2629
2515
|
}
|
|
2630
2516
|
: {
|
|
2631
2517
|
status: "PASSED",
|
|
@@ -2637,27 +2523,7 @@ class StableBrowser {
|
|
|
2637
2523
|
}
|
|
2638
2524
|
}
|
|
2639
2525
|
async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
|
|
2640
|
-
|
|
2641
|
-
return value;
|
|
2642
|
-
}
|
|
2643
|
-
// find all the accurance of {{(.*?)}} and replace with the value
|
|
2644
|
-
let regex = /{{(.*?)}}/g;
|
|
2645
|
-
let matches = value.match(regex);
|
|
2646
|
-
if (matches) {
|
|
2647
|
-
const testData = this.getTestData(world);
|
|
2648
|
-
for (let i = 0; i < matches.length; i++) {
|
|
2649
|
-
let match = matches[i];
|
|
2650
|
-
let key = match.substring(2, match.length - 2);
|
|
2651
|
-
let newValue = objectPath.get(testData, key, null);
|
|
2652
|
-
if (newValue !== null) {
|
|
2653
|
-
value = value.replace(match, newValue);
|
|
2654
|
-
}
|
|
2655
|
-
}
|
|
2656
|
-
}
|
|
2657
|
-
if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
|
|
2658
|
-
return await decrypt(value, null, totpWait);
|
|
2659
|
-
}
|
|
2660
|
-
return value;
|
|
2526
|
+
return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
|
|
2661
2527
|
}
|
|
2662
2528
|
_getLoadTimeout(options) {
|
|
2663
2529
|
let timeout = 15000;
|
|
@@ -2717,7 +2583,7 @@ class StableBrowser {
|
|
|
2717
2583
|
status: "FAILED",
|
|
2718
2584
|
startTime,
|
|
2719
2585
|
endTime,
|
|
2720
|
-
message: error
|
|
2586
|
+
message: error?.message,
|
|
2721
2587
|
}
|
|
2722
2588
|
: {
|
|
2723
2589
|
status: "PASSED",
|
|
@@ -2728,39 +2594,28 @@ class StableBrowser {
|
|
|
2728
2594
|
}
|
|
2729
2595
|
}
|
|
2730
2596
|
async closePage(options = {}, world = null) {
|
|
2731
|
-
const
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2597
|
+
const state = {
|
|
2598
|
+
options,
|
|
2599
|
+
world,
|
|
2600
|
+
locate: false,
|
|
2601
|
+
scroll: false,
|
|
2602
|
+
highlight: false,
|
|
2603
|
+
type: Types.CLOSE_PAGE,
|
|
2604
|
+
text: `Close page`,
|
|
2605
|
+
operation: "closePage",
|
|
2606
|
+
log: "***** close page *****\n",
|
|
2607
|
+
throwError: false,
|
|
2608
|
+
};
|
|
2736
2609
|
try {
|
|
2610
|
+
await _preCommand(state, this);
|
|
2737
2611
|
await this.page.close();
|
|
2738
2612
|
}
|
|
2739
2613
|
catch (e) {
|
|
2740
2614
|
console.log(".");
|
|
2615
|
+
await _commandError(state, e, this);
|
|
2741
2616
|
}
|
|
2742
2617
|
finally {
|
|
2743
|
-
|
|
2744
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2745
|
-
const endTime = Date.now();
|
|
2746
|
-
this._reportToWorld(world, {
|
|
2747
|
-
type: Types.CLOSE_PAGE,
|
|
2748
|
-
text: "close page",
|
|
2749
|
-
screenshotId,
|
|
2750
|
-
result: error
|
|
2751
|
-
? {
|
|
2752
|
-
status: "FAILED",
|
|
2753
|
-
startTime,
|
|
2754
|
-
endTime,
|
|
2755
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2756
|
-
}
|
|
2757
|
-
: {
|
|
2758
|
-
status: "PASSED",
|
|
2759
|
-
startTime,
|
|
2760
|
-
endTime,
|
|
2761
|
-
},
|
|
2762
|
-
info: info,
|
|
2763
|
-
});
|
|
2618
|
+
_commandFinally(state, this);
|
|
2764
2619
|
}
|
|
2765
2620
|
}
|
|
2766
2621
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
@@ -2780,6 +2635,7 @@ class StableBrowser {
|
|
|
2780
2635
|
}
|
|
2781
2636
|
catch (e) {
|
|
2782
2637
|
console.log(".");
|
|
2638
|
+
await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
|
|
2783
2639
|
}
|
|
2784
2640
|
finally {
|
|
2785
2641
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
@@ -2794,7 +2650,7 @@ class StableBrowser {
|
|
|
2794
2650
|
status: "FAILED",
|
|
2795
2651
|
startTime,
|
|
2796
2652
|
endTime,
|
|
2797
|
-
message: error
|
|
2653
|
+
message: error?.message,
|
|
2798
2654
|
}
|
|
2799
2655
|
: {
|
|
2800
2656
|
status: "PASSED",
|
|
@@ -2816,6 +2672,7 @@ class StableBrowser {
|
|
|
2816
2672
|
}
|
|
2817
2673
|
catch (e) {
|
|
2818
2674
|
console.log(".");
|
|
2675
|
+
await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
|
|
2819
2676
|
}
|
|
2820
2677
|
finally {
|
|
2821
2678
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
@@ -2830,7 +2687,7 @@ class StableBrowser {
|
|
|
2830
2687
|
status: "FAILED",
|
|
2831
2688
|
startTime,
|
|
2832
2689
|
endTime,
|
|
2833
|
-
message: error
|
|
2690
|
+
message: error?.message,
|
|
2834
2691
|
}
|
|
2835
2692
|
: {
|
|
2836
2693
|
status: "PASSED",
|
|
@@ -2863,6 +2720,31 @@ class StableBrowser {
|
|
|
2863
2720
|
}
|
|
2864
2721
|
world.attach(JSON.stringify(properties), { mediaType: "application/json" });
|
|
2865
2722
|
}
|
|
2723
|
+
async beforeStep(world, step) {
|
|
2724
|
+
this.stepName = step.pickleStep.text;
|
|
2725
|
+
this.logger.info("step: " + this.stepName);
|
|
2726
|
+
if (this.stepIndex === undefined) {
|
|
2727
|
+
this.stepIndex = 0;
|
|
2728
|
+
}
|
|
2729
|
+
else {
|
|
2730
|
+
this.stepIndex++;
|
|
2731
|
+
}
|
|
2732
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2733
|
+
if (this.context.browserObject.context) {
|
|
2734
|
+
await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
async afterStep(world, step) {
|
|
2739
|
+
this.stepName = null;
|
|
2740
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2741
|
+
if (this.context.browserObject.context) {
|
|
2742
|
+
await this.context.browserObject.context.tracing.stopChunk({
|
|
2743
|
+
path: path.join(this.context.browserObject.traceFolder, `trace-${this.stepIndex}.zip`),
|
|
2744
|
+
});
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2866
2748
|
}
|
|
2867
2749
|
function createTimedPromise(promise, label) {
|
|
2868
2750
|
return promise
|