automation_model 1.0.459-dev → 1.0.459-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 +181 -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 +6 -0
- package/lib/command_common.js +138 -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 +18 -3
- 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 +24 -19
- package/lib/stable_browser.js +651 -781
- package/lib/stable_browser.js.map +1 -1
- package/lib/test_context.d.ts +1 -0
- package/lib/test_context.js +12 -12
- 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, _reportToWorld, } 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
1327
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1378
1328
|
await this.page.keyboard.press("Enter");
|
|
1379
1329
|
}
|
|
1380
1330
|
await this.waitForPageLoad();
|
|
1381
|
-
return info;
|
|
1331
|
+
return state.info;
|
|
1382
1332
|
}
|
|
1383
1333
|
catch (e) {
|
|
1384
|
-
|
|
1385
|
-
this.logger.error("fill failed " + info.log);
|
|
1386
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1387
|
-
info.screenshotPath = screenshotPath;
|
|
1388
|
-
Object.assign(e, { info: info });
|
|
1389
|
-
error = e;
|
|
1390
|
-
throw e;
|
|
1334
|
+
await _commandError(state, e, this);
|
|
1391
1335
|
}
|
|
1392
1336
|
finally {
|
|
1393
|
-
|
|
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
|
-
});
|
|
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) {
|
|
@@ -1894,127 +1776,69 @@ class StableBrowser {
|
|
|
1894
1776
|
}
|
|
1895
1777
|
}
|
|
1896
1778
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
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
|
+
};
|
|
1902
1789
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1903
|
-
const info = {};
|
|
1904
|
-
info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
|
|
1905
|
-
info.operation = "verify";
|
|
1906
|
-
info.selectors = selectors;
|
|
1907
1790
|
try {
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
}
|
|
1912
|
-
await this._highlightElements(element);
|
|
1913
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1914
|
-
await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
1915
|
-
return info;
|
|
1791
|
+
await _preCommand(state, this);
|
|
1792
|
+
await expect(state.element).toHaveCount(1, { timeout: 10000 });
|
|
1793
|
+
return state.info;
|
|
1916
1794
|
}
|
|
1917
1795
|
catch (e) {
|
|
1918
|
-
|
|
1919
|
-
this.logger.error("verify failed " + info.log);
|
|
1920
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1921
|
-
info.screenshotPath = screenshotPath;
|
|
1922
|
-
Object.assign(e, { info: info });
|
|
1923
|
-
error = e;
|
|
1924
|
-
throw e;
|
|
1796
|
+
await _commandError(state, e, this);
|
|
1925
1797
|
}
|
|
1926
1798
|
finally {
|
|
1927
|
-
|
|
1928
|
-
this._reportToWorld(world, {
|
|
1929
|
-
element_name: selectors.element_name,
|
|
1930
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1931
|
-
text: "Verify element exists in page",
|
|
1932
|
-
screenshotId,
|
|
1933
|
-
result: error
|
|
1934
|
-
? {
|
|
1935
|
-
status: "FAILED",
|
|
1936
|
-
startTime,
|
|
1937
|
-
endTime,
|
|
1938
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1939
|
-
}
|
|
1940
|
-
: {
|
|
1941
|
-
status: "PASSED",
|
|
1942
|
-
startTime,
|
|
1943
|
-
endTime,
|
|
1944
|
-
},
|
|
1945
|
-
info: info,
|
|
1946
|
-
});
|
|
1799
|
+
_commandFinally(state, this);
|
|
1947
1800
|
}
|
|
1948
1801
|
}
|
|
1949
1802
|
async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
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
|
+
};
|
|
1955
1815
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1956
|
-
const info = {};
|
|
1957
|
-
info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
|
|
1958
|
-
info.operation = "extract";
|
|
1959
|
-
info.selectors = selectors;
|
|
1960
1816
|
try {
|
|
1961
|
-
|
|
1962
|
-
await this._highlightElements(element);
|
|
1963
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1817
|
+
await _preCommand(state, this);
|
|
1964
1818
|
switch (attribute) {
|
|
1965
1819
|
case "inner_text":
|
|
1966
|
-
|
|
1820
|
+
state.value = await state.element.innerText();
|
|
1967
1821
|
break;
|
|
1968
1822
|
case "href":
|
|
1969
|
-
|
|
1823
|
+
state.value = await state.element.getAttribute("href");
|
|
1970
1824
|
break;
|
|
1971
1825
|
case "value":
|
|
1972
|
-
|
|
1826
|
+
state.value = await state.element.inputValue();
|
|
1973
1827
|
break;
|
|
1974
1828
|
default:
|
|
1975
|
-
|
|
1829
|
+
state.value = await state.element.getAttribute(attribute);
|
|
1976
1830
|
break;
|
|
1977
1831
|
}
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
this.setTestData({ [variable]: info.value }, world);
|
|
1983
|
-
this.logger.info("set test data: " + variable + "=" + info.value);
|
|
1984
|
-
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;
|
|
1985
1836
|
}
|
|
1986
1837
|
catch (e) {
|
|
1987
|
-
|
|
1988
|
-
this.logger.error("extract failed " + info.log);
|
|
1989
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1990
|
-
info.screenshotPath = screenshotPath;
|
|
1991
|
-
Object.assign(e, { info: info });
|
|
1992
|
-
error = e;
|
|
1993
|
-
throw e;
|
|
1838
|
+
await _commandError(state, e, this);
|
|
1994
1839
|
}
|
|
1995
1840
|
finally {
|
|
1996
|
-
|
|
1997
|
-
this._reportToWorld(world, {
|
|
1998
|
-
element_name: selectors.element_name,
|
|
1999
|
-
type: Types.EXTRACT_ATTRIBUTE,
|
|
2000
|
-
variable: variable,
|
|
2001
|
-
value: info.value,
|
|
2002
|
-
text: "Extract attribute from element",
|
|
2003
|
-
screenshotId,
|
|
2004
|
-
result: error
|
|
2005
|
-
? {
|
|
2006
|
-
status: "FAILED",
|
|
2007
|
-
startTime,
|
|
2008
|
-
endTime,
|
|
2009
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2010
|
-
}
|
|
2011
|
-
: {
|
|
2012
|
-
status: "PASSED",
|
|
2013
|
-
startTime,
|
|
2014
|
-
endTime,
|
|
2015
|
-
},
|
|
2016
|
-
info: info,
|
|
2017
|
-
});
|
|
1841
|
+
_commandFinally(state, this);
|
|
2018
1842
|
}
|
|
2019
1843
|
}
|
|
2020
1844
|
async extractEmailData(emailAddress, options, world) {
|
|
@@ -2091,7 +1915,8 @@ class StableBrowser {
|
|
|
2091
1915
|
catch (e) {
|
|
2092
1916
|
errorCount++;
|
|
2093
1917
|
if (errorCount > 3) {
|
|
2094
|
-
throw e;
|
|
1918
|
+
// throw e;
|
|
1919
|
+
await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
|
|
2095
1920
|
}
|
|
2096
1921
|
// ignore
|
|
2097
1922
|
}
|
|
@@ -2202,11 +2027,12 @@ class StableBrowser {
|
|
|
2202
2027
|
info.screenshotPath = screenshotPath;
|
|
2203
2028
|
Object.assign(e, { info: info });
|
|
2204
2029
|
error = e;
|
|
2205
|
-
throw e;
|
|
2030
|
+
// throw e;
|
|
2031
|
+
await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
|
|
2206
2032
|
}
|
|
2207
2033
|
finally {
|
|
2208
2034
|
const endTime = Date.now();
|
|
2209
|
-
|
|
2035
|
+
_reportToWorld(world, {
|
|
2210
2036
|
type: Types.VERIFY_PAGE_PATH,
|
|
2211
2037
|
text: "Verify page path",
|
|
2212
2038
|
screenshotId,
|
|
@@ -2215,7 +2041,7 @@ class StableBrowser {
|
|
|
2215
2041
|
status: "FAILED",
|
|
2216
2042
|
startTime,
|
|
2217
2043
|
endTime,
|
|
2218
|
-
message: error
|
|
2044
|
+
message: error?.message,
|
|
2219
2045
|
}
|
|
2220
2046
|
: {
|
|
2221
2047
|
status: "PASSED",
|
|
@@ -2228,52 +2054,58 @@ class StableBrowser {
|
|
|
2228
2054
|
}
|
|
2229
2055
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2230
2056
|
text = unEscapeString(text);
|
|
2231
|
-
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
|
+
};
|
|
2232
2069
|
const timeout = this._getLoadTimeout(options);
|
|
2233
|
-
let error = null;
|
|
2234
|
-
let screenshotId = null;
|
|
2235
|
-
let screenshotPath = null;
|
|
2236
2070
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2237
|
-
const info = {};
|
|
2238
|
-
info.log = "***** verify text " + text + " exists in page *****\n";
|
|
2239
|
-
info.operation = "verifyTextExistInPage";
|
|
2240
2071
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
2241
2072
|
if (newValue !== text) {
|
|
2242
2073
|
this.logger.info(text + "=" + newValue);
|
|
2243
2074
|
text = newValue;
|
|
2244
2075
|
}
|
|
2245
|
-
info.text = text;
|
|
2246
2076
|
let dateAlternatives = findDateAlternatives(text);
|
|
2247
2077
|
let numberAlternatives = findNumberAlternatives(text);
|
|
2248
2078
|
try {
|
|
2079
|
+
await _preCommand(state, this);
|
|
2080
|
+
state.info.text = text;
|
|
2249
2081
|
while (true) {
|
|
2250
2082
|
const frames = this.page.frames();
|
|
2251
2083
|
let results = [];
|
|
2252
2084
|
for (let i = 0; i < frames.length; i++) {
|
|
2253
2085
|
if (dateAlternatives.date) {
|
|
2254
2086
|
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2255
|
-
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, {});
|
|
2256
2088
|
result.frame = frames[i];
|
|
2257
2089
|
results.push(result);
|
|
2258
2090
|
}
|
|
2259
2091
|
}
|
|
2260
2092
|
else if (numberAlternatives.number) {
|
|
2261
2093
|
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2262
|
-
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, {});
|
|
2263
2095
|
result.frame = frames[i];
|
|
2264
2096
|
results.push(result);
|
|
2265
2097
|
}
|
|
2266
2098
|
}
|
|
2267
2099
|
else {
|
|
2268
|
-
const result = await this._locateElementByText(frames[i], text, "
|
|
2100
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2269
2101
|
result.frame = frames[i];
|
|
2270
2102
|
results.push(result);
|
|
2271
2103
|
}
|
|
2272
2104
|
}
|
|
2273
|
-
info.results = results;
|
|
2105
|
+
state.info.results = results;
|
|
2274
2106
|
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2275
2107
|
if (resultWithElementsFound.length === 0) {
|
|
2276
|
-
if (Date.now() - startTime > timeout) {
|
|
2108
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2277
2109
|
throw new Error(`Text ${text} not found in page`);
|
|
2278
2110
|
}
|
|
2279
2111
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -2285,44 +2117,89 @@ class StableBrowser {
|
|
|
2285
2117
|
await this._highlightElements(frame, dataAttribute);
|
|
2286
2118
|
const element = await frame.$(dataAttribute);
|
|
2287
2119
|
if (element) {
|
|
2288
|
-
await this.scrollIfNeeded(element, info);
|
|
2120
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2289
2121
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2290
2122
|
}
|
|
2291
2123
|
}
|
|
2292
|
-
|
|
2293
|
-
return info;
|
|
2124
|
+
await _screenshot(state, this);
|
|
2125
|
+
return state.info;
|
|
2294
2126
|
}
|
|
2295
2127
|
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2296
2128
|
}
|
|
2297
2129
|
catch (e) {
|
|
2298
|
-
|
|
2299
|
-
this.logger.error("verify text exist in page failed " + info.log);
|
|
2300
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2301
|
-
info.screenshotPath = screenshotPath;
|
|
2302
|
-
Object.assign(e, { info: info });
|
|
2303
|
-
error = e;
|
|
2304
|
-
throw e;
|
|
2130
|
+
await _commandError(state, e, this);
|
|
2305
2131
|
}
|
|
2306
2132
|
finally {
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
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
|
+
}
|
|
2318
2172
|
}
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
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);
|
|
2326
2203
|
}
|
|
2327
2204
|
}
|
|
2328
2205
|
_getServerUrl() {
|
|
@@ -2385,11 +2262,12 @@ class StableBrowser {
|
|
|
2385
2262
|
info.screenshotPath = screenshotPath;
|
|
2386
2263
|
Object.assign(e, { info: info });
|
|
2387
2264
|
error = e;
|
|
2388
|
-
throw e;
|
|
2265
|
+
// throw e;
|
|
2266
|
+
await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
|
|
2389
2267
|
}
|
|
2390
2268
|
finally {
|
|
2391
2269
|
const endTime = Date.now();
|
|
2392
|
-
|
|
2270
|
+
_reportToWorld(world, {
|
|
2393
2271
|
type: Types.VERIFY_VISUAL,
|
|
2394
2272
|
text: "Visual verification",
|
|
2395
2273
|
screenshotId,
|
|
@@ -2398,7 +2276,7 @@ class StableBrowser {
|
|
|
2398
2276
|
status: "FAILED",
|
|
2399
2277
|
startTime,
|
|
2400
2278
|
endTime,
|
|
2401
|
-
message: error
|
|
2279
|
+
message: error?.message,
|
|
2402
2280
|
}
|
|
2403
2281
|
: {
|
|
2404
2282
|
status: "PASSED",
|
|
@@ -2430,7 +2308,7 @@ class StableBrowser {
|
|
|
2430
2308
|
this.logger.info("Table data verified");
|
|
2431
2309
|
}
|
|
2432
2310
|
async getTableData(selectors, _params = null, options = {}, world = null) {
|
|
2433
|
-
|
|
2311
|
+
_validateSelectors(selectors);
|
|
2434
2312
|
const startTime = Date.now();
|
|
2435
2313
|
let error = null;
|
|
2436
2314
|
let screenshotId = null;
|
|
@@ -2452,11 +2330,12 @@ class StableBrowser {
|
|
|
2452
2330
|
info.screenshotPath = screenshotPath;
|
|
2453
2331
|
Object.assign(e, { info: info });
|
|
2454
2332
|
error = e;
|
|
2455
|
-
throw e;
|
|
2333
|
+
// throw e;
|
|
2334
|
+
await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
|
|
2456
2335
|
}
|
|
2457
2336
|
finally {
|
|
2458
2337
|
const endTime = Date.now();
|
|
2459
|
-
|
|
2338
|
+
_reportToWorld(world, {
|
|
2460
2339
|
element_name: selectors.element_name,
|
|
2461
2340
|
type: Types.GET_TABLE_DATA,
|
|
2462
2341
|
text: "Get table data",
|
|
@@ -2466,7 +2345,7 @@ class StableBrowser {
|
|
|
2466
2345
|
status: "FAILED",
|
|
2467
2346
|
startTime,
|
|
2468
2347
|
endTime,
|
|
2469
|
-
message: error
|
|
2348
|
+
message: error?.message,
|
|
2470
2349
|
}
|
|
2471
2350
|
: {
|
|
2472
2351
|
status: "PASSED",
|
|
@@ -2478,7 +2357,7 @@ class StableBrowser {
|
|
|
2478
2357
|
}
|
|
2479
2358
|
}
|
|
2480
2359
|
async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
|
|
2481
|
-
|
|
2360
|
+
_validateSelectors(selectors);
|
|
2482
2361
|
if (!query) {
|
|
2483
2362
|
throw new Error("query is null");
|
|
2484
2363
|
}
|
|
@@ -2617,11 +2496,12 @@ class StableBrowser {
|
|
|
2617
2496
|
info.screenshotPath = screenshotPath;
|
|
2618
2497
|
Object.assign(e, { info: info });
|
|
2619
2498
|
error = e;
|
|
2620
|
-
throw e;
|
|
2499
|
+
// throw e;
|
|
2500
|
+
await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
|
|
2621
2501
|
}
|
|
2622
2502
|
finally {
|
|
2623
2503
|
const endTime = Date.now();
|
|
2624
|
-
|
|
2504
|
+
_reportToWorld(world, {
|
|
2625
2505
|
element_name: selectors.element_name,
|
|
2626
2506
|
type: Types.ANALYZE_TABLE,
|
|
2627
2507
|
text: "Analyze table",
|
|
@@ -2631,7 +2511,7 @@ class StableBrowser {
|
|
|
2631
2511
|
status: "FAILED",
|
|
2632
2512
|
startTime,
|
|
2633
2513
|
endTime,
|
|
2634
|
-
message: error
|
|
2514
|
+
message: error?.message,
|
|
2635
2515
|
}
|
|
2636
2516
|
: {
|
|
2637
2517
|
status: "PASSED",
|
|
@@ -2643,27 +2523,7 @@ class StableBrowser {
|
|
|
2643
2523
|
}
|
|
2644
2524
|
}
|
|
2645
2525
|
async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
|
|
2646
|
-
|
|
2647
|
-
return value;
|
|
2648
|
-
}
|
|
2649
|
-
// find all the accurance of {{(.*?)}} and replace with the value
|
|
2650
|
-
let regex = /{{(.*?)}}/g;
|
|
2651
|
-
let matches = value.match(regex);
|
|
2652
|
-
if (matches) {
|
|
2653
|
-
const testData = this.getTestData(world);
|
|
2654
|
-
for (let i = 0; i < matches.length; i++) {
|
|
2655
|
-
let match = matches[i];
|
|
2656
|
-
let key = match.substring(2, match.length - 2);
|
|
2657
|
-
let newValue = objectPath.get(testData, key, null);
|
|
2658
|
-
if (newValue !== null) {
|
|
2659
|
-
value = value.replace(match, newValue);
|
|
2660
|
-
}
|
|
2661
|
-
}
|
|
2662
|
-
}
|
|
2663
|
-
if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
|
|
2664
|
-
return await decrypt(value, null, totpWait);
|
|
2665
|
-
}
|
|
2666
|
-
return value;
|
|
2526
|
+
return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
|
|
2667
2527
|
}
|
|
2668
2528
|
_getLoadTimeout(options) {
|
|
2669
2529
|
let timeout = 15000;
|
|
@@ -2714,7 +2574,7 @@ class StableBrowser {
|
|
|
2714
2574
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2715
2575
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2716
2576
|
const endTime = Date.now();
|
|
2717
|
-
|
|
2577
|
+
_reportToWorld(world, {
|
|
2718
2578
|
type: Types.GET_PAGE_STATUS,
|
|
2719
2579
|
text: "Wait for page load",
|
|
2720
2580
|
screenshotId,
|
|
@@ -2723,7 +2583,7 @@ class StableBrowser {
|
|
|
2723
2583
|
status: "FAILED",
|
|
2724
2584
|
startTime,
|
|
2725
2585
|
endTime,
|
|
2726
|
-
message: error
|
|
2586
|
+
message: error?.message,
|
|
2727
2587
|
}
|
|
2728
2588
|
: {
|
|
2729
2589
|
status: "PASSED",
|
|
@@ -2734,39 +2594,28 @@ class StableBrowser {
|
|
|
2734
2594
|
}
|
|
2735
2595
|
}
|
|
2736
2596
|
async closePage(options = {}, world = null) {
|
|
2737
|
-
const
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
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
|
+
};
|
|
2742
2609
|
try {
|
|
2610
|
+
await _preCommand(state, this);
|
|
2743
2611
|
await this.page.close();
|
|
2744
2612
|
}
|
|
2745
2613
|
catch (e) {
|
|
2746
2614
|
console.log(".");
|
|
2615
|
+
await _commandError(state, e, this);
|
|
2747
2616
|
}
|
|
2748
2617
|
finally {
|
|
2749
|
-
|
|
2750
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2751
|
-
const endTime = Date.now();
|
|
2752
|
-
this._reportToWorld(world, {
|
|
2753
|
-
type: Types.CLOSE_PAGE,
|
|
2754
|
-
text: "close page",
|
|
2755
|
-
screenshotId,
|
|
2756
|
-
result: error
|
|
2757
|
-
? {
|
|
2758
|
-
status: "FAILED",
|
|
2759
|
-
startTime,
|
|
2760
|
-
endTime,
|
|
2761
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2762
|
-
}
|
|
2763
|
-
: {
|
|
2764
|
-
status: "PASSED",
|
|
2765
|
-
startTime,
|
|
2766
|
-
endTime,
|
|
2767
|
-
},
|
|
2768
|
-
info: info,
|
|
2769
|
-
});
|
|
2618
|
+
_commandFinally(state, this);
|
|
2770
2619
|
}
|
|
2771
2620
|
}
|
|
2772
2621
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
@@ -2786,12 +2635,13 @@ class StableBrowser {
|
|
|
2786
2635
|
}
|
|
2787
2636
|
catch (e) {
|
|
2788
2637
|
console.log(".");
|
|
2638
|
+
await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
|
|
2789
2639
|
}
|
|
2790
2640
|
finally {
|
|
2791
2641
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2792
2642
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2793
2643
|
const endTime = Date.now();
|
|
2794
|
-
|
|
2644
|
+
_reportToWorld(world, {
|
|
2795
2645
|
type: Types.SET_VIEWPORT,
|
|
2796
2646
|
text: "set viewport size to " + width + "x" + hight,
|
|
2797
2647
|
screenshotId,
|
|
@@ -2800,7 +2650,7 @@ class StableBrowser {
|
|
|
2800
2650
|
status: "FAILED",
|
|
2801
2651
|
startTime,
|
|
2802
2652
|
endTime,
|
|
2803
|
-
message: error
|
|
2653
|
+
message: error?.message,
|
|
2804
2654
|
}
|
|
2805
2655
|
: {
|
|
2806
2656
|
status: "PASSED",
|
|
@@ -2822,12 +2672,13 @@ class StableBrowser {
|
|
|
2822
2672
|
}
|
|
2823
2673
|
catch (e) {
|
|
2824
2674
|
console.log(".");
|
|
2675
|
+
await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
|
|
2825
2676
|
}
|
|
2826
2677
|
finally {
|
|
2827
2678
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2828
2679
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2829
2680
|
const endTime = Date.now();
|
|
2830
|
-
|
|
2681
|
+
_reportToWorld(world, {
|
|
2831
2682
|
type: Types.GET_PAGE_STATUS,
|
|
2832
2683
|
text: "page relaod",
|
|
2833
2684
|
screenshotId,
|
|
@@ -2836,7 +2687,7 @@ class StableBrowser {
|
|
|
2836
2687
|
status: "FAILED",
|
|
2837
2688
|
startTime,
|
|
2838
2689
|
endTime,
|
|
2839
|
-
message: error
|
|
2690
|
+
message: error?.message,
|
|
2840
2691
|
}
|
|
2841
2692
|
: {
|
|
2842
2693
|
status: "PASSED",
|
|
@@ -2863,11 +2714,30 @@ class StableBrowser {
|
|
|
2863
2714
|
console.log("#-#");
|
|
2864
2715
|
}
|
|
2865
2716
|
}
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2717
|
+
async beforeStep(world, step) {
|
|
2718
|
+
this.stepName = step.pickleStep.text;
|
|
2719
|
+
this.logger.info("step: " + this.stepName);
|
|
2720
|
+
if (this.stepIndex === undefined) {
|
|
2721
|
+
this.stepIndex = 0;
|
|
2722
|
+
}
|
|
2723
|
+
else {
|
|
2724
|
+
this.stepIndex++;
|
|
2725
|
+
}
|
|
2726
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2727
|
+
if (this.context.browserObject.context) {
|
|
2728
|
+
await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
async afterStep(world, step) {
|
|
2733
|
+
this.stepName = null;
|
|
2734
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2735
|
+
if (this.context.browserObject.context) {
|
|
2736
|
+
await this.context.browserObject.context.tracing.stopChunk({
|
|
2737
|
+
path: path.join(this.context.browserObject.traceFolder, `trace-${this.stepIndex}.zip`),
|
|
2738
|
+
});
|
|
2739
|
+
}
|
|
2869
2740
|
}
|
|
2870
|
-
world.attach(JSON.stringify(properties), { mediaType: "application/json" });
|
|
2871
2741
|
}
|
|
2872
2742
|
}
|
|
2873
2743
|
function createTimedPromise(promise, label) {
|