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