automation_model 1.0.477-dev → 1.0.477-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 +221 -47
- package/lib/api.js.map +1 -1
- package/lib/auto_page.d.ts +2 -1
- package/lib/auto_page.js +39 -17
- package/lib/auto_page.js.map +1 -1
- package/lib/browser_manager.d.ts +6 -3
- package/lib/browser_manager.js +92 -18
- package/lib/browser_manager.js.map +1 -1
- package/lib/command_common.d.ts +1 -0
- package/lib/command_common.js +61 -7
- package/lib/command_common.js.map +1 -1
- 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 +188 -0
- package/lib/error-messages.js.map +1 -0
- package/lib/generation_scripts.d.ts +4 -0
- package/lib/generation_scripts.js +2 -0
- package/lib/generation_scripts.js.map +1 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/init_browser.d.ts +2 -1
- package/lib/init_browser.js +31 -4
- package/lib/init_browser.js.map +1 -1
- package/lib/locate_element.js +5 -3
- package/lib/locate_element.js.map +1 -1
- package/lib/locator.d.ts +36 -0
- package/lib/locator.js +165 -0
- package/lib/locator.js.map +1 -1
- package/lib/locator_log.d.ts +26 -0
- package/lib/locator_log.js +69 -0
- package/lib/locator_log.js.map +1 -0
- package/lib/network.d.ts +3 -0
- package/lib/network.js +155 -0
- package/lib/network.js.map +1 -0
- package/lib/stable_browser.d.ts +44 -9
- package/lib/stable_browser.js +490 -295
- package/lib/stable_browser.js.map +1 -1
- package/lib/table.d.ts +13 -0
- package/lib/table.js +187 -0
- package/lib/table.js.map +1 -0
- 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 +5 -4
package/lib/stable_browser.js
CHANGED
|
@@ -10,15 +10,17 @@ 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";
|
|
19
18
|
import { locate_element } from "./locate_element.js";
|
|
20
|
-
import {
|
|
21
|
-
|
|
19
|
+
import { randomUUID } from "crypto";
|
|
20
|
+
import { _commandError, _commandFinally, _preCommand, _validateSelectors, _screenshot, _reportToWorld, } from "./command_common.js";
|
|
21
|
+
import { registerDownloadEvent, registerNetworkEvents } from "./network.js";
|
|
22
|
+
import { LocatorLog } from "./locator_log.js";
|
|
23
|
+
export const Types = {
|
|
22
24
|
CLICK: "click_element",
|
|
23
25
|
NAVIGATE: "navigate",
|
|
24
26
|
FILL: "fill_element",
|
|
@@ -44,20 +46,28 @@ const Types = {
|
|
|
44
46
|
VERIFY_VISUAL: "verify_visual",
|
|
45
47
|
LOAD_DATA: "load_data",
|
|
46
48
|
SET_INPUT: "set_input",
|
|
49
|
+
WAIT_FOR_TEXT_TO_DISAPPEAR: "wait_for_text_to_disappear",
|
|
50
|
+
VERIFY_ATTRIBUTE: "verify_element_attribute",
|
|
47
51
|
};
|
|
48
52
|
export const apps = {};
|
|
49
53
|
class StableBrowser {
|
|
54
|
+
browser;
|
|
55
|
+
page;
|
|
56
|
+
logger;
|
|
57
|
+
context;
|
|
58
|
+
world;
|
|
59
|
+
project_path = null;
|
|
60
|
+
webLogFile = null;
|
|
61
|
+
networkLogger = null;
|
|
62
|
+
configuration = null;
|
|
63
|
+
appName = "main";
|
|
64
|
+
tags = null;
|
|
50
65
|
constructor(browser, page, logger = null, context = null, world = null) {
|
|
51
66
|
this.browser = browser;
|
|
52
67
|
this.page = page;
|
|
53
68
|
this.logger = logger;
|
|
54
69
|
this.context = context;
|
|
55
70
|
this.world = world;
|
|
56
|
-
this.project_path = null;
|
|
57
|
-
this.webLogFile = null;
|
|
58
|
-
this.networkLogger = null;
|
|
59
|
-
this.configuration = null;
|
|
60
|
-
this.appName = "main";
|
|
61
71
|
if (!this.logger) {
|
|
62
72
|
this.logger = console;
|
|
63
73
|
}
|
|
@@ -82,23 +92,50 @@ class StableBrowser {
|
|
|
82
92
|
catch (e) {
|
|
83
93
|
this.logger.error("unable to read ai_config.json");
|
|
84
94
|
}
|
|
85
|
-
context.pageLoading = { status: false };
|
|
86
|
-
context.pages = [this.page];
|
|
87
95
|
const logFolder = path.join(this.project_path, "logs", "web");
|
|
88
96
|
this.world = world;
|
|
97
|
+
context.pages = [this.page];
|
|
98
|
+
context.pageLoading = { status: false };
|
|
89
99
|
this.registerEventListeners(this.context);
|
|
100
|
+
registerNetworkEvents(this.world, this, this.context, this.page);
|
|
101
|
+
registerDownloadEvent(this.page, this.world, this.context);
|
|
102
|
+
}
|
|
103
|
+
async scrollPageToLoadLazyElements() {
|
|
104
|
+
let lastHeight = await this.page.evaluate(() => document.body.scrollHeight);
|
|
105
|
+
let retry = 0;
|
|
106
|
+
while (true) {
|
|
107
|
+
await this.page.evaluate(() => window.scrollBy(0, window.innerHeight));
|
|
108
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
109
|
+
let newHeight = await this.page.evaluate(() => document.body.scrollHeight);
|
|
110
|
+
if (newHeight === lastHeight) {
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
lastHeight = newHeight;
|
|
114
|
+
retry++;
|
|
115
|
+
if (retry > 10) {
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
await this.page.evaluate(() => window.scrollTo(0, 0));
|
|
90
120
|
}
|
|
91
121
|
registerEventListeners(context) {
|
|
92
122
|
this.registerConsoleLogListener(this.page, context);
|
|
93
|
-
this.registerRequestListener(this.page, context, this.webLogFile);
|
|
123
|
+
// this.registerRequestListener(this.page, context, this.webLogFile);
|
|
94
124
|
if (!context.pageLoading) {
|
|
95
125
|
context.pageLoading = { status: false };
|
|
96
126
|
}
|
|
97
127
|
context.playContext.on("page", async function (page) {
|
|
128
|
+
if (this.configuration && this.configuration.closePopups === true) {
|
|
129
|
+
console.log("close unexpected popups");
|
|
130
|
+
await page.close();
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
98
133
|
context.pageLoading.status = true;
|
|
99
134
|
this.page = page;
|
|
100
135
|
context.page = page;
|
|
101
136
|
context.pages.push(page);
|
|
137
|
+
registerNetworkEvents(this.world, this, context, this.page);
|
|
138
|
+
registerDownloadEvent(this.page, this.world, context);
|
|
102
139
|
page.on("close", async () => {
|
|
103
140
|
if (this.context && this.context.pages && this.context.pages.length > 1) {
|
|
104
141
|
this.context.pages.pop();
|
|
@@ -128,9 +165,9 @@ class StableBrowser {
|
|
|
128
165
|
if (this.appName === appName) {
|
|
129
166
|
return;
|
|
130
167
|
}
|
|
131
|
-
let
|
|
168
|
+
let navigate = false;
|
|
132
169
|
if (!apps[appName]) {
|
|
133
|
-
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this);
|
|
170
|
+
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this, -1, this.context.reportFolder);
|
|
134
171
|
newContextCreated = true;
|
|
135
172
|
apps[appName] = {
|
|
136
173
|
context: newContext,
|
|
@@ -143,8 +180,7 @@ class StableBrowser {
|
|
|
143
180
|
this._copyContext(apps[appName], this);
|
|
144
181
|
apps[this.appName] = tempContext;
|
|
145
182
|
this.appName = appName;
|
|
146
|
-
if (
|
|
147
|
-
this.registerEventListeners(this.context);
|
|
183
|
+
if (navigate) {
|
|
148
184
|
await this.goto(this.context.environment.baseUrl);
|
|
149
185
|
await this.waitForPageLoad();
|
|
150
186
|
}
|
|
@@ -170,7 +206,6 @@ class StableBrowser {
|
|
|
170
206
|
this.context.webLogger = [];
|
|
171
207
|
}
|
|
172
208
|
page.on("console", async (msg) => {
|
|
173
|
-
var _a;
|
|
174
209
|
const obj = {
|
|
175
210
|
type: msg.type(),
|
|
176
211
|
text: msg.text(),
|
|
@@ -179,7 +214,7 @@ class StableBrowser {
|
|
|
179
214
|
};
|
|
180
215
|
this.context.webLogger.push(obj);
|
|
181
216
|
if (msg.type() === "error") {
|
|
182
|
-
|
|
217
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
|
|
183
218
|
}
|
|
184
219
|
});
|
|
185
220
|
}
|
|
@@ -188,7 +223,6 @@ class StableBrowser {
|
|
|
188
223
|
this.context.networkLogger = [];
|
|
189
224
|
}
|
|
190
225
|
page.on("request", async (data) => {
|
|
191
|
-
var _a;
|
|
192
226
|
const startTime = new Date().getTime();
|
|
193
227
|
try {
|
|
194
228
|
const pageUrl = new URL(page.url());
|
|
@@ -213,10 +247,10 @@ class StableBrowser {
|
|
|
213
247
|
startTime,
|
|
214
248
|
};
|
|
215
249
|
context.networkLogger.push(obj);
|
|
216
|
-
|
|
250
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
|
|
217
251
|
}
|
|
218
252
|
catch (error) {
|
|
219
|
-
console.error("Error in request listener", error);
|
|
253
|
+
// console.error("Error in request listener", error);
|
|
220
254
|
context.networkLogger.push({
|
|
221
255
|
error: "not able to listen",
|
|
222
256
|
message: error.message,
|
|
@@ -305,7 +339,7 @@ class StableBrowser {
|
|
|
305
339
|
locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
|
|
306
340
|
}
|
|
307
341
|
}
|
|
308
|
-
if (locator
|
|
342
|
+
if (locator?.engine) {
|
|
309
343
|
if (locator.engine === "css") {
|
|
310
344
|
locatorReturn = scope.locator(locator.selector);
|
|
311
345
|
}
|
|
@@ -329,7 +363,7 @@ class StableBrowser {
|
|
|
329
363
|
if (css && css.locator) {
|
|
330
364
|
css = css.locator;
|
|
331
365
|
}
|
|
332
|
-
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "
|
|
366
|
+
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*:not(script, style, head)", false, false, _params);
|
|
333
367
|
if (result.elementCount === 0) {
|
|
334
368
|
return;
|
|
335
369
|
}
|
|
@@ -381,7 +415,7 @@ class StableBrowser {
|
|
|
381
415
|
}
|
|
382
416
|
document.collectAllShadowDomElements = collectAllShadowDomElements;
|
|
383
417
|
if (!tag) {
|
|
384
|
-
tag = "
|
|
418
|
+
tag = "*:not(script, style, head)";
|
|
385
419
|
}
|
|
386
420
|
let regexpSearch = document.getRegex(text);
|
|
387
421
|
if (regexpSearch) {
|
|
@@ -463,9 +497,21 @@ class StableBrowser {
|
|
|
463
497
|
}, [text1, tag1, regex1, partial1]);
|
|
464
498
|
}
|
|
465
499
|
async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
|
|
500
|
+
if (!info) {
|
|
501
|
+
info = {};
|
|
502
|
+
}
|
|
503
|
+
if (!info.failCause) {
|
|
504
|
+
info.failCause = {};
|
|
505
|
+
}
|
|
506
|
+
if (!info.log) {
|
|
507
|
+
info.log = "";
|
|
508
|
+
info.locatorLog = new LocatorLog(selectorHierarchy);
|
|
509
|
+
}
|
|
466
510
|
let locatorSearch = selectorHierarchy[index];
|
|
511
|
+
let originalLocatorSearch = "";
|
|
467
512
|
try {
|
|
468
|
-
|
|
513
|
+
originalLocatorSearch = this._fixUsingParams(JSON.stringify(locatorSearch), _params);
|
|
514
|
+
locatorSearch = JSON.parse(originalLocatorSearch);
|
|
469
515
|
}
|
|
470
516
|
catch (e) {
|
|
471
517
|
console.error(e);
|
|
@@ -475,13 +521,18 @@ class StableBrowser {
|
|
|
475
521
|
if (locatorSearch.climb && locatorSearch.climb >= 0) {
|
|
476
522
|
let locatorString = await this._locateElmentByTextClimbCss(scope, locatorSearch.text, locatorSearch.climb, locatorSearch.css, _params);
|
|
477
523
|
if (!locatorString) {
|
|
524
|
+
info.failCause.textNotFound = true;
|
|
525
|
+
info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
|
|
478
526
|
return;
|
|
479
527
|
}
|
|
480
528
|
locator = this._getLocator({ css: locatorString }, scope, _params);
|
|
481
529
|
}
|
|
482
530
|
else if (locatorSearch.text) {
|
|
483
|
-
let
|
|
531
|
+
let text = this._fixUsingParams(locatorSearch.text, _params);
|
|
532
|
+
let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
|
|
484
533
|
if (result.elementCount === 0) {
|
|
534
|
+
info.failCause.textNotFound = true;
|
|
535
|
+
info.failCause.lastError = "failed to locate element by text: " + text;
|
|
485
536
|
return;
|
|
486
537
|
}
|
|
487
538
|
locatorSearch.css = "[data-blinq-id='blinq-id-" + result.randomToken + "']";
|
|
@@ -498,13 +549,22 @@ class StableBrowser {
|
|
|
498
549
|
// cssHref = true;
|
|
499
550
|
// }
|
|
500
551
|
let count = await locator.count();
|
|
552
|
+
if (count > 0 && !info.failCause.count) {
|
|
553
|
+
info.failCause.count = count;
|
|
554
|
+
}
|
|
501
555
|
//info.log += "total elements found " + count + "\n";
|
|
502
556
|
//let visibleCount = 0;
|
|
503
557
|
let visibleLocator = null;
|
|
504
|
-
if (locatorSearch.index && locatorSearch.index < count) {
|
|
558
|
+
if (typeof locatorSearch.index === "number" && locatorSearch.index < count) {
|
|
505
559
|
foundLocators.push(locator.nth(locatorSearch.index));
|
|
560
|
+
if (info.locatorLog) {
|
|
561
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND");
|
|
562
|
+
}
|
|
506
563
|
return;
|
|
507
564
|
}
|
|
565
|
+
if (info.locatorLog && count === 0) {
|
|
566
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "NOT_FOUND");
|
|
567
|
+
}
|
|
508
568
|
for (let j = 0; j < count; j++) {
|
|
509
569
|
let visible = await locator.nth(j).isVisible();
|
|
510
570
|
const enabled = await locator.nth(j).isEnabled();
|
|
@@ -513,24 +573,40 @@ class StableBrowser {
|
|
|
513
573
|
}
|
|
514
574
|
if (visible && enabled) {
|
|
515
575
|
foundLocators.push(locator.nth(j));
|
|
576
|
+
if (info.locatorLog) {
|
|
577
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND");
|
|
578
|
+
}
|
|
516
579
|
}
|
|
517
580
|
else {
|
|
581
|
+
info.failCause.visible = visible;
|
|
582
|
+
info.failCause.enabled = enabled;
|
|
518
583
|
if (!info.printMessages) {
|
|
519
584
|
info.printMessages = {};
|
|
520
585
|
}
|
|
586
|
+
if (info.locatorLog && !visible) {
|
|
587
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND_NOT_VISIBLE");
|
|
588
|
+
}
|
|
589
|
+
if (info.locatorLog && !enabled) {
|
|
590
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND_NOT_ENABLED");
|
|
591
|
+
}
|
|
521
592
|
if (!info.printMessages[j.toString()]) {
|
|
522
|
-
info.log += "element " + locator + " visible " + visible + " enabled " + enabled + "\n";
|
|
593
|
+
//info.log += "element " + locator + " visible " + visible + " enabled " + enabled + "\n";
|
|
523
594
|
info.printMessages[j.toString()] = true;
|
|
524
595
|
}
|
|
525
596
|
}
|
|
526
597
|
}
|
|
527
598
|
}
|
|
528
599
|
async closeUnexpectedPopups(info, _params) {
|
|
600
|
+
if (!info) {
|
|
601
|
+
info = {};
|
|
602
|
+
info.failCause = {};
|
|
603
|
+
info.log = "";
|
|
604
|
+
}
|
|
529
605
|
if (this.configuration.popupHandlers && this.configuration.popupHandlers.length > 0) {
|
|
530
606
|
if (!info) {
|
|
531
607
|
info = {};
|
|
532
608
|
}
|
|
533
|
-
info.log += "scan for popup handlers" + "\n";
|
|
609
|
+
//info.log += "scan for popup handlers" + "\n";
|
|
534
610
|
const handlerGroup = [];
|
|
535
611
|
for (let i = 0; i < this.configuration.popupHandlers.length; i++) {
|
|
536
612
|
handlerGroup.push(this.configuration.popupHandlers[i].locator);
|
|
@@ -557,9 +633,21 @@ class StableBrowser {
|
|
|
557
633
|
}
|
|
558
634
|
if (result.foundElements.length > 0) {
|
|
559
635
|
let dialogCloseLocator = result.foundElements[0].locator;
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
636
|
+
try {
|
|
637
|
+
await scope?.evaluate(() => {
|
|
638
|
+
window.__isClosingPopups = true;
|
|
639
|
+
});
|
|
640
|
+
await dialogCloseLocator.click();
|
|
641
|
+
// wait for the dialog to close
|
|
642
|
+
await dialogCloseLocator.waitFor({ state: "hidden" });
|
|
643
|
+
}
|
|
644
|
+
catch (e) {
|
|
645
|
+
}
|
|
646
|
+
finally {
|
|
647
|
+
await scope?.evaluate(() => {
|
|
648
|
+
window.__isClosingPopups = false;
|
|
649
|
+
});
|
|
650
|
+
}
|
|
563
651
|
return { rerun: true };
|
|
564
652
|
}
|
|
565
653
|
}
|
|
@@ -583,7 +671,13 @@ class StableBrowser {
|
|
|
583
671
|
}
|
|
584
672
|
throw new Error("unable to locate element " + JSON.stringify(selectors));
|
|
585
673
|
}
|
|
586
|
-
async _findFrameScope(selectors, timeout = 30000) {
|
|
674
|
+
async _findFrameScope(selectors, timeout = 30000, info) {
|
|
675
|
+
if (!info) {
|
|
676
|
+
info = {};
|
|
677
|
+
info.failCause = {};
|
|
678
|
+
info.log = "";
|
|
679
|
+
}
|
|
680
|
+
let startTime = Date.now();
|
|
587
681
|
let scope = this.page;
|
|
588
682
|
if (selectors.frame) {
|
|
589
683
|
return selectors.frame;
|
|
@@ -614,9 +708,11 @@ class StableBrowser {
|
|
|
614
708
|
}
|
|
615
709
|
return framescope;
|
|
616
710
|
};
|
|
711
|
+
let fLocator = null;
|
|
617
712
|
while (true) {
|
|
618
713
|
let frameFound = false;
|
|
619
714
|
if (selectors.nestFrmLoc) {
|
|
715
|
+
fLocator = selectors.nestFrmLoc;
|
|
620
716
|
scope = await findFrame(selectors.nestFrmLoc, scope);
|
|
621
717
|
frameFound = true;
|
|
622
718
|
break;
|
|
@@ -625,6 +721,7 @@ class StableBrowser {
|
|
|
625
721
|
for (let i = 0; i < selectors.frameLocators.length; i++) {
|
|
626
722
|
let frameLocator = selectors.frameLocators[i];
|
|
627
723
|
if (frameLocator.css) {
|
|
724
|
+
fLocator = frameLocator.css;
|
|
628
725
|
scope = scope.frameLocator(frameLocator.css);
|
|
629
726
|
frameFound = true;
|
|
630
727
|
break;
|
|
@@ -632,16 +729,25 @@ class StableBrowser {
|
|
|
632
729
|
}
|
|
633
730
|
}
|
|
634
731
|
if (!frameFound && selectors.iframe_src) {
|
|
732
|
+
fLocator = selectors.iframe_src;
|
|
635
733
|
scope = this.page.frame({ url: selectors.iframe_src });
|
|
636
734
|
}
|
|
637
735
|
if (!scope) {
|
|
638
|
-
info
|
|
639
|
-
|
|
736
|
+
if (info && info.locatorLog) {
|
|
737
|
+
info.locatorLog.setFrameSearchStatus("frame-" + fLocator, "NOT_FOUND");
|
|
738
|
+
}
|
|
739
|
+
//info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
|
|
740
|
+
if (Date.now() - startTime > timeout) {
|
|
741
|
+
info.failCause.iframeNotFound = true;
|
|
742
|
+
info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
|
|
640
743
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
641
744
|
}
|
|
642
745
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
643
746
|
}
|
|
644
747
|
else {
|
|
748
|
+
if (info && info.locatorLog) {
|
|
749
|
+
info.locatorLog.setFrameSearchStatus("frame-" + fLocator, "FOUND");
|
|
750
|
+
}
|
|
645
751
|
break;
|
|
646
752
|
}
|
|
647
753
|
}
|
|
@@ -651,20 +757,27 @@ class StableBrowser {
|
|
|
651
757
|
}
|
|
652
758
|
return scope;
|
|
653
759
|
}
|
|
654
|
-
async _getDocumentBody(selectors, timeout = 30000) {
|
|
655
|
-
let scope = await this._findFrameScope(selectors, timeout);
|
|
760
|
+
async _getDocumentBody(selectors, timeout = 30000, info) {
|
|
761
|
+
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
656
762
|
return scope.evaluate(() => {
|
|
657
763
|
var bodyContent = document.body.innerHTML;
|
|
658
764
|
return bodyContent;
|
|
659
765
|
});
|
|
660
766
|
}
|
|
661
767
|
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
768
|
+
if (!info) {
|
|
769
|
+
info = {};
|
|
770
|
+
info.failCause = {};
|
|
771
|
+
info.log = "";
|
|
772
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
773
|
+
}
|
|
662
774
|
let highPriorityTimeout = 5000;
|
|
663
775
|
let visibleOnlyTimeout = 6000;
|
|
664
|
-
let startTime =
|
|
776
|
+
let startTime = Date.now();
|
|
665
777
|
let locatorsCount = 0;
|
|
778
|
+
let lazy_scroll = false;
|
|
666
779
|
//let arrayMode = Array.isArray(selectors);
|
|
667
|
-
let scope = await this._findFrameScope(selectors, timeout);
|
|
780
|
+
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
668
781
|
let selectorsLocators = null;
|
|
669
782
|
selectorsLocators = selectors.locators;
|
|
670
783
|
// group selectors by priority
|
|
@@ -751,21 +864,33 @@ class StableBrowser {
|
|
|
751
864
|
return maxCountElement.locator;
|
|
752
865
|
}
|
|
753
866
|
}
|
|
754
|
-
if (
|
|
867
|
+
if (Date.now() - startTime > timeout) {
|
|
755
868
|
break;
|
|
756
869
|
}
|
|
757
|
-
if (
|
|
870
|
+
if (Date.now() - startTime > highPriorityTimeout) {
|
|
758
871
|
info.log += "high priority timeout, will try all elements" + "\n";
|
|
759
872
|
highPriorityOnly = false;
|
|
873
|
+
if (this.configuration && this.configuration.load_all_lazy === true && !lazy_scroll) {
|
|
874
|
+
lazy_scroll = true;
|
|
875
|
+
await this.scrollPageToLoadLazyElements();
|
|
876
|
+
}
|
|
760
877
|
}
|
|
761
|
-
if (
|
|
878
|
+
if (Date.now() - startTime > visibleOnlyTimeout) {
|
|
762
879
|
info.log += "visible only timeout, will try all elements" + "\n";
|
|
763
880
|
visibleOnly = false;
|
|
764
881
|
}
|
|
765
882
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
766
883
|
}
|
|
767
884
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
768
|
-
info.
|
|
885
|
+
// if (info.locatorLog) {
|
|
886
|
+
// const lines = info.locatorLog.toString().split("\n");
|
|
887
|
+
// for (let line of lines) {
|
|
888
|
+
// this.logger.debug(line);
|
|
889
|
+
// }
|
|
890
|
+
// }
|
|
891
|
+
//info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
|
|
892
|
+
info.failCause.locatorNotFound = true;
|
|
893
|
+
info.failCause.lastError = "failed to locate unique element";
|
|
769
894
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
770
895
|
}
|
|
771
896
|
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
@@ -797,6 +922,12 @@ class StableBrowser {
|
|
|
797
922
|
});
|
|
798
923
|
result.locatorIndex = i;
|
|
799
924
|
}
|
|
925
|
+
if (foundLocators.length > 1) {
|
|
926
|
+
info.failCause.foundMultiple = true;
|
|
927
|
+
if (info.locatorLog) {
|
|
928
|
+
info.locatorLog.setLocatorSearchStatus(locatorsGroup[i], "FOUND_NOT_UNIQUE");
|
|
929
|
+
}
|
|
930
|
+
}
|
|
800
931
|
}
|
|
801
932
|
return result;
|
|
802
933
|
}
|
|
@@ -809,12 +940,12 @@ class StableBrowser {
|
|
|
809
940
|
while (true) {
|
|
810
941
|
try {
|
|
811
942
|
const result = await locate_element(this.context, elementDescription, "click");
|
|
812
|
-
if (
|
|
943
|
+
if (result?.elementNumber >= 0) {
|
|
813
944
|
const selectors = {
|
|
814
|
-
frame: result
|
|
945
|
+
frame: result?.frame,
|
|
815
946
|
locators: [
|
|
816
947
|
{
|
|
817
|
-
css: result
|
|
948
|
+
css: result?.css,
|
|
818
949
|
},
|
|
819
950
|
],
|
|
820
951
|
};
|
|
@@ -823,8 +954,9 @@ class StableBrowser {
|
|
|
823
954
|
}
|
|
824
955
|
}
|
|
825
956
|
catch (e) {
|
|
826
|
-
if (
|
|
827
|
-
throw e;
|
|
957
|
+
if (Date.now() - startTime > timeout) {
|
|
958
|
+
// throw e;
|
|
959
|
+
await _commandError({ text: "simpleClick", operation: "simpleClick", elementDescription, info: {} }, e, this);
|
|
828
960
|
}
|
|
829
961
|
}
|
|
830
962
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
@@ -839,12 +971,12 @@ class StableBrowser {
|
|
|
839
971
|
while (true) {
|
|
840
972
|
try {
|
|
841
973
|
const result = await locate_element(this.context, elementDescription, "fill", value);
|
|
842
|
-
if (
|
|
974
|
+
if (result?.elementNumber >= 0) {
|
|
843
975
|
const selectors = {
|
|
844
|
-
frame: result
|
|
976
|
+
frame: result?.frame,
|
|
845
977
|
locators: [
|
|
846
978
|
{
|
|
847
|
-
css: result
|
|
979
|
+
css: result?.css,
|
|
848
980
|
},
|
|
849
981
|
],
|
|
850
982
|
};
|
|
@@ -853,8 +985,9 @@ class StableBrowser {
|
|
|
853
985
|
}
|
|
854
986
|
}
|
|
855
987
|
catch (e) {
|
|
856
|
-
if (
|
|
857
|
-
throw e;
|
|
988
|
+
if (Date.now() - startTime > timeout) {
|
|
989
|
+
// throw e;
|
|
990
|
+
await _commandError({ text: "simpleClickType", operation: "simpleClickType", value, elementDescription, info: {} }, e, this);
|
|
858
991
|
}
|
|
859
992
|
}
|
|
860
993
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
@@ -878,13 +1011,13 @@ class StableBrowser {
|
|
|
878
1011
|
}
|
|
879
1012
|
try {
|
|
880
1013
|
await state.element.click();
|
|
881
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1014
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
882
1015
|
}
|
|
883
1016
|
catch (e) {
|
|
884
1017
|
// await this.closeUnexpectedPopups();
|
|
885
1018
|
state.element = await this._locate(selectors, state.info, _params);
|
|
886
1019
|
await state.element.dispatchEvent("click");
|
|
887
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1020
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
888
1021
|
}
|
|
889
1022
|
await this.waitForPageLoad();
|
|
890
1023
|
return state.info;
|
|
@@ -908,7 +1041,7 @@ class StableBrowser {
|
|
|
908
1041
|
log: "***** check " + selectors.element_name + " *****\n",
|
|
909
1042
|
};
|
|
910
1043
|
try {
|
|
911
|
-
_preCommand(state, this);
|
|
1044
|
+
await _preCommand(state, this);
|
|
912
1045
|
state.info.checked = checked;
|
|
913
1046
|
// let element = await this._locate(selectors, info, _params);
|
|
914
1047
|
// ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
@@ -923,7 +1056,7 @@ class StableBrowser {
|
|
|
923
1056
|
}
|
|
924
1057
|
else {
|
|
925
1058
|
//await this.closeUnexpectedPopups();
|
|
926
|
-
info.log += "setCheck failed, will try again" + "\n";
|
|
1059
|
+
state.info.log += "setCheck failed, will try again" + "\n";
|
|
927
1060
|
state.element = await this._locate(selectors, state.info, _params);
|
|
928
1061
|
await state.element.setChecked(checked, { timeout: 5000, force: true });
|
|
929
1062
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -951,7 +1084,7 @@ class StableBrowser {
|
|
|
951
1084
|
log: "***** hover " + selectors.element_name + " *****\n",
|
|
952
1085
|
};
|
|
953
1086
|
try {
|
|
954
|
-
_preCommand(state, this);
|
|
1087
|
+
await _preCommand(state, this);
|
|
955
1088
|
try {
|
|
956
1089
|
await state.element.hover();
|
|
957
1090
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -982,6 +1115,7 @@ class StableBrowser {
|
|
|
982
1115
|
_params,
|
|
983
1116
|
options,
|
|
984
1117
|
world,
|
|
1118
|
+
value: values.toString(),
|
|
985
1119
|
type: Types.SELECT,
|
|
986
1120
|
text: `Select option: ${values}`,
|
|
987
1121
|
operation: "selectOption",
|
|
@@ -1089,33 +1223,30 @@ class StableBrowser {
|
|
|
1089
1223
|
}
|
|
1090
1224
|
}
|
|
1091
1225
|
async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1226
|
+
const state = {
|
|
1227
|
+
selectors,
|
|
1228
|
+
_params,
|
|
1229
|
+
value: await this._replaceWithLocalData(value, this),
|
|
1230
|
+
options,
|
|
1231
|
+
world,
|
|
1232
|
+
type: Types.SET_DATE_TIME,
|
|
1233
|
+
text: `Set date time value: ${value}`,
|
|
1234
|
+
operation: "setDateTime",
|
|
1235
|
+
log: "***** set date time value " + selectors.element_name + " *****\n",
|
|
1236
|
+
throwError: false,
|
|
1237
|
+
};
|
|
1102
1238
|
try {
|
|
1103
|
-
|
|
1104
|
-
let element = await this._locate(selectors, info, _params);
|
|
1105
|
-
//insert red border around the element
|
|
1106
|
-
await this.scrollIfNeeded(element, info);
|
|
1107
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1108
|
-
await this._highlightElements(element);
|
|
1239
|
+
await _preCommand(state, this);
|
|
1109
1240
|
try {
|
|
1110
|
-
await element.click();
|
|
1241
|
+
await state.element.click();
|
|
1111
1242
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1112
1243
|
if (format) {
|
|
1113
|
-
value = dayjs(value).format(format);
|
|
1114
|
-
await element.fill(value);
|
|
1244
|
+
state.value = dayjs(state.value).format(format);
|
|
1245
|
+
await state.element.fill(state.value);
|
|
1115
1246
|
}
|
|
1116
1247
|
else {
|
|
1117
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1118
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1248
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1249
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1119
1250
|
el.value = ""; // clear input
|
|
1120
1251
|
el.value = dateTimeValue;
|
|
1121
1252
|
}, dateTimeValue);
|
|
@@ -1128,20 +1259,19 @@ class StableBrowser {
|
|
|
1128
1259
|
}
|
|
1129
1260
|
catch (err) {
|
|
1130
1261
|
//await this.closeUnexpectedPopups();
|
|
1131
|
-
this.logger.error("setting date time input failed " + JSON.stringify(info));
|
|
1262
|
+
this.logger.error("setting date time input failed " + JSON.stringify(state.info));
|
|
1132
1263
|
this.logger.info("Trying again");
|
|
1133
|
-
|
|
1134
|
-
info.
|
|
1135
|
-
Object.assign(err, { info: info });
|
|
1264
|
+
await _screenshot(state, this);
|
|
1265
|
+
Object.assign(err, { info: state.info });
|
|
1136
1266
|
await element.click();
|
|
1137
1267
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1138
1268
|
if (format) {
|
|
1139
|
-
value = dayjs(value).format(format);
|
|
1140
|
-
await element.fill(value);
|
|
1269
|
+
state.value = dayjs(state.value).format(format);
|
|
1270
|
+
await state.element.fill(state.value);
|
|
1141
1271
|
}
|
|
1142
1272
|
else {
|
|
1143
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1144
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1273
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1274
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1145
1275
|
el.value = ""; // clear input
|
|
1146
1276
|
el.value = dateTimeValue;
|
|
1147
1277
|
}, dateTimeValue);
|
|
@@ -1154,46 +1284,27 @@ class StableBrowser {
|
|
|
1154
1284
|
}
|
|
1155
1285
|
}
|
|
1156
1286
|
catch (e) {
|
|
1157
|
-
|
|
1158
|
-
throw e;
|
|
1287
|
+
await _commandError(state, e, this);
|
|
1159
1288
|
}
|
|
1160
1289
|
finally {
|
|
1161
|
-
|
|
1162
|
-
this._reportToWorld(world, {
|
|
1163
|
-
element_name: selectors.element_name,
|
|
1164
|
-
type: Types.SET_DATE_TIME,
|
|
1165
|
-
screenshotId,
|
|
1166
|
-
value: value,
|
|
1167
|
-
text: `setDateTime input with value: ${value}`,
|
|
1168
|
-
result: error
|
|
1169
|
-
? {
|
|
1170
|
-
status: "FAILED",
|
|
1171
|
-
startTime,
|
|
1172
|
-
endTime,
|
|
1173
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1174
|
-
}
|
|
1175
|
-
: {
|
|
1176
|
-
status: "PASSED",
|
|
1177
|
-
startTime,
|
|
1178
|
-
endTime,
|
|
1179
|
-
},
|
|
1180
|
-
info: info,
|
|
1181
|
-
});
|
|
1290
|
+
_commandFinally(state, this);
|
|
1182
1291
|
}
|
|
1183
1292
|
}
|
|
1184
1293
|
async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
|
|
1294
|
+
_value = unEscapeString(_value);
|
|
1295
|
+
const newValue = await this._replaceWithLocalData(_value, world);
|
|
1185
1296
|
const state = {
|
|
1186
1297
|
selectors,
|
|
1187
1298
|
_params,
|
|
1188
|
-
value:
|
|
1299
|
+
value: newValue,
|
|
1300
|
+
originalValue: _value,
|
|
1189
1301
|
options,
|
|
1190
1302
|
world,
|
|
1191
1303
|
type: Types.FILL,
|
|
1192
1304
|
text: `Click type input with value: ${_value}`,
|
|
1193
1305
|
operation: "clickType",
|
|
1194
|
-
log: "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n",
|
|
1306
|
+
log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
|
|
1195
1307
|
};
|
|
1196
|
-
const newValue = await this._replaceWithLocalData(state.value, world);
|
|
1197
1308
|
if (newValue !== _value) {
|
|
1198
1309
|
//this.logger.info(_value + "=" + newValue);
|
|
1199
1310
|
_value = newValue;
|
|
@@ -1205,7 +1316,7 @@ class StableBrowser {
|
|
|
1205
1316
|
try {
|
|
1206
1317
|
let currentValue = await state.element.inputValue();
|
|
1207
1318
|
if (currentValue) {
|
|
1208
|
-
await element.fill("");
|
|
1319
|
+
await state.element.fill("");
|
|
1209
1320
|
}
|
|
1210
1321
|
}
|
|
1211
1322
|
catch (e) {
|
|
@@ -1313,6 +1424,7 @@ class StableBrowser {
|
|
|
1313
1424
|
let screenshotPath = null;
|
|
1314
1425
|
if (!info.log) {
|
|
1315
1426
|
info.log = "";
|
|
1427
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
1316
1428
|
}
|
|
1317
1429
|
info.operation = "getText";
|
|
1318
1430
|
info.selectors = selectors;
|
|
@@ -1352,7 +1464,6 @@ class StableBrowser {
|
|
|
1352
1464
|
}
|
|
1353
1465
|
}
|
|
1354
1466
|
async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
|
|
1355
|
-
var _a;
|
|
1356
1467
|
if (!pattern) {
|
|
1357
1468
|
throw new Error("pattern is null");
|
|
1358
1469
|
}
|
|
@@ -1363,7 +1474,7 @@ class StableBrowser {
|
|
|
1363
1474
|
selectors,
|
|
1364
1475
|
_params,
|
|
1365
1476
|
pattern,
|
|
1366
|
-
value:
|
|
1477
|
+
value: pattern,
|
|
1367
1478
|
options,
|
|
1368
1479
|
world,
|
|
1369
1480
|
locate: false,
|
|
@@ -1392,14 +1503,14 @@ class StableBrowser {
|
|
|
1392
1503
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1393
1504
|
pattern = pattern.replace("{text}", escapedText);
|
|
1394
1505
|
let regex = new RegExp(pattern, "im");
|
|
1395
|
-
if (!regex.test(foundObj
|
|
1396
|
-
state.info.foundText = foundObj
|
|
1506
|
+
if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
|
|
1507
|
+
state.info.foundText = foundObj?.text;
|
|
1397
1508
|
throw new Error("element doesn't contain text " + text);
|
|
1398
1509
|
}
|
|
1399
1510
|
return state.info;
|
|
1400
1511
|
}
|
|
1401
1512
|
catch (e) {
|
|
1402
|
-
this.logger.error("found text " +
|
|
1513
|
+
this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
|
|
1403
1514
|
await _commandError(state, e, this);
|
|
1404
1515
|
}
|
|
1405
1516
|
finally {
|
|
@@ -1407,7 +1518,6 @@ class StableBrowser {
|
|
|
1407
1518
|
}
|
|
1408
1519
|
}
|
|
1409
1520
|
async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
|
|
1410
|
-
var _a, _b, _c;
|
|
1411
1521
|
const state = {
|
|
1412
1522
|
selectors,
|
|
1413
1523
|
_params,
|
|
@@ -1434,6 +1544,7 @@ class StableBrowser {
|
|
|
1434
1544
|
}
|
|
1435
1545
|
let foundObj = null;
|
|
1436
1546
|
try {
|
|
1547
|
+
await _preCommand(state, this);
|
|
1437
1548
|
foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
|
|
1438
1549
|
if (foundObj && foundObj.element) {
|
|
1439
1550
|
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
@@ -1443,8 +1554,8 @@ class StableBrowser {
|
|
|
1443
1554
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1444
1555
|
if (dateAlternatives.date) {
|
|
1445
1556
|
for (let i = 0; i < dateAlternatives.dates.length; i++) {
|
|
1446
|
-
if (
|
|
1447
|
-
|
|
1557
|
+
if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
|
|
1558
|
+
foundObj?.value?.includes(dateAlternatives.dates[i])) {
|
|
1448
1559
|
return state.info;
|
|
1449
1560
|
}
|
|
1450
1561
|
}
|
|
@@ -1452,16 +1563,16 @@ class StableBrowser {
|
|
|
1452
1563
|
}
|
|
1453
1564
|
else if (numberAlternatives.number) {
|
|
1454
1565
|
for (let i = 0; i < numberAlternatives.numbers.length; i++) {
|
|
1455
|
-
if (
|
|
1456
|
-
|
|
1566
|
+
if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
|
|
1567
|
+
foundObj?.value?.includes(numberAlternatives.numbers[i])) {
|
|
1457
1568
|
return state.info;
|
|
1458
1569
|
}
|
|
1459
1570
|
}
|
|
1460
1571
|
throw new Error("element doesn't contain text " + text);
|
|
1461
1572
|
}
|
|
1462
|
-
else if (!
|
|
1463
|
-
state.info.foundText = foundObj
|
|
1464
|
-
state.info.value = foundObj
|
|
1573
|
+
else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
|
|
1574
|
+
state.info.foundText = foundObj?.text;
|
|
1575
|
+
state.info.value = foundObj?.value;
|
|
1465
1576
|
throw new Error("element doesn't contain text " + text);
|
|
1466
1577
|
}
|
|
1467
1578
|
return state.info;
|
|
@@ -1652,11 +1763,9 @@ class StableBrowser {
|
|
|
1652
1763
|
if (!fs.existsSync(world.screenshotPath)) {
|
|
1653
1764
|
fs.mkdirSync(world.screenshotPath, { recursive: true });
|
|
1654
1765
|
}
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
}
|
|
1659
|
-
const screenshotPath = path.join(world.screenshotPath, nextIndex + ".png");
|
|
1766
|
+
// to make sure the path doesn't start with -
|
|
1767
|
+
const uuidStr = "id_" + randomUUID();
|
|
1768
|
+
const screenshotPath = path.join(world.screenshotPath, uuidStr + ".png");
|
|
1660
1769
|
try {
|
|
1661
1770
|
await this.takeScreenshot(screenshotPath);
|
|
1662
1771
|
// let buffer = await this.page.screenshot({ timeout: 4000 });
|
|
@@ -1670,7 +1779,7 @@ class StableBrowser {
|
|
|
1670
1779
|
catch (e) {
|
|
1671
1780
|
this.logger.info("unable to take screenshot, ignored");
|
|
1672
1781
|
}
|
|
1673
|
-
result.screenshotId =
|
|
1782
|
+
result.screenshotId = uuidStr;
|
|
1674
1783
|
result.screenshotPath = screenshotPath;
|
|
1675
1784
|
if (info && info.box) {
|
|
1676
1785
|
await drawRectangle(screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
|
|
@@ -1768,74 +1877,101 @@ class StableBrowser {
|
|
|
1768
1877
|
}
|
|
1769
1878
|
}
|
|
1770
1879
|
async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1880
|
+
const state = {
|
|
1881
|
+
selectors,
|
|
1882
|
+
_params,
|
|
1883
|
+
attribute,
|
|
1884
|
+
variable,
|
|
1885
|
+
options,
|
|
1886
|
+
world,
|
|
1887
|
+
type: Types.EXTRACT,
|
|
1888
|
+
text: `Extract attribute from element`,
|
|
1889
|
+
operation: "extractAttribute",
|
|
1890
|
+
log: "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1891
|
+
};
|
|
1776
1892
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1777
|
-
const info = {};
|
|
1778
|
-
info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
|
|
1779
|
-
info.operation = "extract";
|
|
1780
|
-
info.selectors = selectors;
|
|
1781
1893
|
try {
|
|
1782
|
-
|
|
1783
|
-
await this._highlightElements(element);
|
|
1784
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1894
|
+
await _preCommand(state, this);
|
|
1785
1895
|
switch (attribute) {
|
|
1786
1896
|
case "inner_text":
|
|
1787
|
-
|
|
1897
|
+
state.value = await state.element.innerText();
|
|
1788
1898
|
break;
|
|
1789
1899
|
case "href":
|
|
1790
|
-
|
|
1900
|
+
state.value = await state.element.getAttribute("href");
|
|
1791
1901
|
break;
|
|
1792
1902
|
case "value":
|
|
1793
|
-
|
|
1903
|
+
state.value = await state.element.inputValue();
|
|
1794
1904
|
break;
|
|
1795
1905
|
default:
|
|
1796
|
-
|
|
1906
|
+
state.value = await state.element.getAttribute(attribute);
|
|
1797
1907
|
break;
|
|
1798
1908
|
}
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1909
|
+
state.info.value = state.value;
|
|
1910
|
+
this.setTestData({ [variable]: state.value }, world);
|
|
1911
|
+
this.logger.info("set test data: " + variable + "=" + state.value);
|
|
1912
|
+
return state.info;
|
|
1913
|
+
}
|
|
1914
|
+
catch (e) {
|
|
1915
|
+
await _commandError(state, e, this);
|
|
1916
|
+
}
|
|
1917
|
+
finally {
|
|
1918
|
+
_commandFinally(state, this);
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
async verifyAttribute(selectors, attribute, value, _params = null, options = {}, world = null) {
|
|
1922
|
+
const state = {
|
|
1923
|
+
selectors,
|
|
1924
|
+
_params,
|
|
1925
|
+
attribute,
|
|
1926
|
+
value,
|
|
1927
|
+
options,
|
|
1928
|
+
world,
|
|
1929
|
+
type: Types.VERIFY_ATTRIBUTE,
|
|
1930
|
+
text: `Verify element attribute`,
|
|
1931
|
+
operation: "verifyAttribute",
|
|
1932
|
+
log: "***** verify attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1933
|
+
};
|
|
1934
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1935
|
+
let val;
|
|
1936
|
+
try {
|
|
1937
|
+
await _preCommand(state, this);
|
|
1938
|
+
switch (attribute) {
|
|
1939
|
+
case "innerText":
|
|
1940
|
+
val = String(await state.element.innerText());
|
|
1941
|
+
break;
|
|
1942
|
+
case "value":
|
|
1943
|
+
val = String(await state.element.inputValue());
|
|
1944
|
+
break;
|
|
1945
|
+
case "checked":
|
|
1946
|
+
val = String(await state.element.isChecked());
|
|
1947
|
+
break;
|
|
1948
|
+
case "disabled":
|
|
1949
|
+
val = String(await state.element.isDisabled());
|
|
1950
|
+
break;
|
|
1951
|
+
default:
|
|
1952
|
+
val = String(await state.element.getAttribute(attribute));
|
|
1953
|
+
break;
|
|
1802
1954
|
}
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1955
|
+
let regex;
|
|
1956
|
+
if (value.startsWith("/") && value.endsWith("/")) {
|
|
1957
|
+
const patternBody = value.slice(1, -1);
|
|
1958
|
+
regex = new RegExp(patternBody, "g");
|
|
1959
|
+
}
|
|
1960
|
+
else {
|
|
1961
|
+
const escapedPattern = value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1962
|
+
regex = new RegExp(escapedPattern, "g");
|
|
1963
|
+
}
|
|
1964
|
+
if (!val.match(regex)) {
|
|
1965
|
+
throw new Error(`The ${attribute} attribute has a value of "${val}", but the expected value is "${value}"`);
|
|
1966
|
+
}
|
|
1967
|
+
state.info.value = val;
|
|
1968
|
+
return state.info;
|
|
1806
1969
|
}
|
|
1807
1970
|
catch (e) {
|
|
1808
|
-
|
|
1809
|
-
this.logger.error("extract failed " + info.log);
|
|
1810
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1811
|
-
info.screenshotPath = screenshotPath;
|
|
1812
|
-
Object.assign(e, { info: info });
|
|
1813
|
-
error = e;
|
|
1814
|
-
throw e;
|
|
1971
|
+
await _commandError(state, e, this);
|
|
1815
1972
|
}
|
|
1816
1973
|
finally {
|
|
1817
|
-
|
|
1818
|
-
this._reportToWorld(world, {
|
|
1819
|
-
element_name: selectors.element_name,
|
|
1820
|
-
type: Types.EXTRACT_ATTRIBUTE,
|
|
1821
|
-
variable: variable,
|
|
1822
|
-
value: info.value,
|
|
1823
|
-
text: "Extract attribute from element",
|
|
1824
|
-
screenshotId,
|
|
1825
|
-
result: error
|
|
1826
|
-
? {
|
|
1827
|
-
status: "FAILED",
|
|
1828
|
-
startTime,
|
|
1829
|
-
endTime,
|
|
1830
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1831
|
-
}
|
|
1832
|
-
: {
|
|
1833
|
-
status: "PASSED",
|
|
1834
|
-
startTime,
|
|
1835
|
-
endTime,
|
|
1836
|
-
},
|
|
1837
|
-
info: info,
|
|
1838
|
-
});
|
|
1974
|
+
_commandFinally(state, this);
|
|
1839
1975
|
}
|
|
1840
1976
|
}
|
|
1841
1977
|
async extractEmailData(emailAddress, options, world) {
|
|
@@ -1912,7 +2048,8 @@ class StableBrowser {
|
|
|
1912
2048
|
catch (e) {
|
|
1913
2049
|
errorCount++;
|
|
1914
2050
|
if (errorCount > 3) {
|
|
1915
|
-
throw e;
|
|
2051
|
+
// throw e;
|
|
2052
|
+
await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
|
|
1916
2053
|
}
|
|
1917
2054
|
// ignore
|
|
1918
2055
|
}
|
|
@@ -2023,11 +2160,12 @@ class StableBrowser {
|
|
|
2023
2160
|
info.screenshotPath = screenshotPath;
|
|
2024
2161
|
Object.assign(e, { info: info });
|
|
2025
2162
|
error = e;
|
|
2026
|
-
throw e;
|
|
2163
|
+
// throw e;
|
|
2164
|
+
await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
|
|
2027
2165
|
}
|
|
2028
2166
|
finally {
|
|
2029
2167
|
const endTime = Date.now();
|
|
2030
|
-
|
|
2168
|
+
_reportToWorld(world, {
|
|
2031
2169
|
type: Types.VERIFY_PAGE_PATH,
|
|
2032
2170
|
text: "Verify page path",
|
|
2033
2171
|
screenshotId,
|
|
@@ -2036,7 +2174,7 @@ class StableBrowser {
|
|
|
2036
2174
|
status: "FAILED",
|
|
2037
2175
|
startTime,
|
|
2038
2176
|
endTime,
|
|
2039
|
-
message: error
|
|
2177
|
+
message: error?.message,
|
|
2040
2178
|
}
|
|
2041
2179
|
: {
|
|
2042
2180
|
status: "PASSED",
|
|
@@ -2049,52 +2187,58 @@ class StableBrowser {
|
|
|
2049
2187
|
}
|
|
2050
2188
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2051
2189
|
text = unEscapeString(text);
|
|
2052
|
-
const
|
|
2190
|
+
const state = {
|
|
2191
|
+
text_search: text,
|
|
2192
|
+
options,
|
|
2193
|
+
world,
|
|
2194
|
+
locate: false,
|
|
2195
|
+
scroll: false,
|
|
2196
|
+
highlight: false,
|
|
2197
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
2198
|
+
text: `Verify text exists in page`,
|
|
2199
|
+
operation: "verifyTextExistInPage",
|
|
2200
|
+
log: "***** verify text " + text + " exists in page *****\n",
|
|
2201
|
+
};
|
|
2053
2202
|
const timeout = this._getLoadTimeout(options);
|
|
2054
|
-
let error = null;
|
|
2055
|
-
let screenshotId = null;
|
|
2056
|
-
let screenshotPath = null;
|
|
2057
2203
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2058
|
-
const info = {};
|
|
2059
|
-
info.log = "***** verify text " + text + " exists in page *****\n";
|
|
2060
|
-
info.operation = "verifyTextExistInPage";
|
|
2061
2204
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
2062
2205
|
if (newValue !== text) {
|
|
2063
2206
|
this.logger.info(text + "=" + newValue);
|
|
2064
2207
|
text = newValue;
|
|
2065
2208
|
}
|
|
2066
|
-
info.text = text;
|
|
2067
2209
|
let dateAlternatives = findDateAlternatives(text);
|
|
2068
2210
|
let numberAlternatives = findNumberAlternatives(text);
|
|
2069
2211
|
try {
|
|
2212
|
+
await _preCommand(state, this);
|
|
2213
|
+
state.info.text = text;
|
|
2070
2214
|
while (true) {
|
|
2071
2215
|
const frames = this.page.frames();
|
|
2072
2216
|
let results = [];
|
|
2073
2217
|
for (let i = 0; i < frames.length; i++) {
|
|
2074
2218
|
if (dateAlternatives.date) {
|
|
2075
2219
|
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2076
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "
|
|
2220
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2077
2221
|
result.frame = frames[i];
|
|
2078
2222
|
results.push(result);
|
|
2079
2223
|
}
|
|
2080
2224
|
}
|
|
2081
2225
|
else if (numberAlternatives.number) {
|
|
2082
2226
|
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2083
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "
|
|
2227
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2084
2228
|
result.frame = frames[i];
|
|
2085
2229
|
results.push(result);
|
|
2086
2230
|
}
|
|
2087
2231
|
}
|
|
2088
2232
|
else {
|
|
2089
|
-
const result = await this._locateElementByText(frames[i], text, "
|
|
2233
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2090
2234
|
result.frame = frames[i];
|
|
2091
2235
|
results.push(result);
|
|
2092
2236
|
}
|
|
2093
2237
|
}
|
|
2094
|
-
info.results = results;
|
|
2238
|
+
state.info.results = results;
|
|
2095
2239
|
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2096
2240
|
if (resultWithElementsFound.length === 0) {
|
|
2097
|
-
if (Date.now() - startTime > timeout) {
|
|
2241
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2098
2242
|
throw new Error(`Text ${text} not found in page`);
|
|
2099
2243
|
}
|
|
2100
2244
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -2106,44 +2250,89 @@ class StableBrowser {
|
|
|
2106
2250
|
await this._highlightElements(frame, dataAttribute);
|
|
2107
2251
|
const element = await frame.$(dataAttribute);
|
|
2108
2252
|
if (element) {
|
|
2109
|
-
await this.scrollIfNeeded(element, info);
|
|
2253
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2110
2254
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2111
2255
|
}
|
|
2112
2256
|
}
|
|
2113
|
-
|
|
2114
|
-
return info;
|
|
2257
|
+
await _screenshot(state, this);
|
|
2258
|
+
return state.info;
|
|
2115
2259
|
}
|
|
2116
2260
|
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2117
2261
|
}
|
|
2118
2262
|
catch (e) {
|
|
2119
|
-
|
|
2120
|
-
this.logger.error("verify text exist in page failed " + info.log);
|
|
2121
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2122
|
-
info.screenshotPath = screenshotPath;
|
|
2123
|
-
Object.assign(e, { info: info });
|
|
2124
|
-
error = e;
|
|
2125
|
-
throw e;
|
|
2263
|
+
await _commandError(state, e, this);
|
|
2126
2264
|
}
|
|
2127
2265
|
finally {
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2266
|
+
_commandFinally(state, this);
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
async waitForTextToDisappear(text, options = {}, world = null) {
|
|
2270
|
+
text = unEscapeString(text);
|
|
2271
|
+
const state = {
|
|
2272
|
+
text_search: text,
|
|
2273
|
+
options,
|
|
2274
|
+
world,
|
|
2275
|
+
locate: false,
|
|
2276
|
+
scroll: false,
|
|
2277
|
+
highlight: false,
|
|
2278
|
+
type: Types.WAIT_FOR_TEXT_TO_DISAPPEAR,
|
|
2279
|
+
text: `Verify text does not exist in page`,
|
|
2280
|
+
operation: "verifyTextNotExistInPage",
|
|
2281
|
+
log: "***** verify text " + text + " does not exist in page *****\n",
|
|
2282
|
+
};
|
|
2283
|
+
const timeout = this._getLoadTimeout(options);
|
|
2284
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2285
|
+
const newValue = await this._replaceWithLocalData(text, world);
|
|
2286
|
+
if (newValue !== text) {
|
|
2287
|
+
this.logger.info(text + "=" + newValue);
|
|
2288
|
+
text = newValue;
|
|
2289
|
+
}
|
|
2290
|
+
let dateAlternatives = findDateAlternatives(text);
|
|
2291
|
+
let numberAlternatives = findNumberAlternatives(text);
|
|
2292
|
+
try {
|
|
2293
|
+
await _preCommand(state, this);
|
|
2294
|
+
state.info.text = text;
|
|
2295
|
+
while (true) {
|
|
2296
|
+
const frames = this.page.frames();
|
|
2297
|
+
let results = [];
|
|
2298
|
+
for (let i = 0; i < frames.length; i++) {
|
|
2299
|
+
if (dateAlternatives.date) {
|
|
2300
|
+
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2301
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2302
|
+
result.frame = frames[i];
|
|
2303
|
+
results.push(result);
|
|
2304
|
+
}
|
|
2139
2305
|
}
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2306
|
+
else if (numberAlternatives.number) {
|
|
2307
|
+
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2308
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2309
|
+
result.frame = frames[i];
|
|
2310
|
+
results.push(result);
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
else {
|
|
2314
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2315
|
+
result.frame = frames[i];
|
|
2316
|
+
results.push(result);
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
state.info.results = results;
|
|
2320
|
+
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2321
|
+
if (resultWithElementsFound.length === 0) {
|
|
2322
|
+
await _screenshot(state, this);
|
|
2323
|
+
return state.info;
|
|
2324
|
+
}
|
|
2325
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2326
|
+
throw new Error(`Text ${text} found in page`);
|
|
2327
|
+
}
|
|
2328
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
catch (e) {
|
|
2332
|
+
await _commandError(state, e, this);
|
|
2333
|
+
}
|
|
2334
|
+
finally {
|
|
2335
|
+
_commandFinally(state, this);
|
|
2147
2336
|
}
|
|
2148
2337
|
}
|
|
2149
2338
|
_getServerUrl() {
|
|
@@ -2206,11 +2395,12 @@ class StableBrowser {
|
|
|
2206
2395
|
info.screenshotPath = screenshotPath;
|
|
2207
2396
|
Object.assign(e, { info: info });
|
|
2208
2397
|
error = e;
|
|
2209
|
-
throw e;
|
|
2398
|
+
// throw e;
|
|
2399
|
+
await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
|
|
2210
2400
|
}
|
|
2211
2401
|
finally {
|
|
2212
2402
|
const endTime = Date.now();
|
|
2213
|
-
|
|
2403
|
+
_reportToWorld(world, {
|
|
2214
2404
|
type: Types.VERIFY_VISUAL,
|
|
2215
2405
|
text: "Visual verification",
|
|
2216
2406
|
screenshotId,
|
|
@@ -2219,7 +2409,7 @@ class StableBrowser {
|
|
|
2219
2409
|
status: "FAILED",
|
|
2220
2410
|
startTime,
|
|
2221
2411
|
endTime,
|
|
2222
|
-
message: error
|
|
2412
|
+
message: error?.message,
|
|
2223
2413
|
}
|
|
2224
2414
|
: {
|
|
2225
2415
|
status: "PASSED",
|
|
@@ -2258,6 +2448,7 @@ class StableBrowser {
|
|
|
2258
2448
|
let screenshotPath = null;
|
|
2259
2449
|
const info = {};
|
|
2260
2450
|
info.log = "";
|
|
2451
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
2261
2452
|
info.operation = "getTableData";
|
|
2262
2453
|
info.selectors = selectors;
|
|
2263
2454
|
try {
|
|
@@ -2273,11 +2464,12 @@ class StableBrowser {
|
|
|
2273
2464
|
info.screenshotPath = screenshotPath;
|
|
2274
2465
|
Object.assign(e, { info: info });
|
|
2275
2466
|
error = e;
|
|
2276
|
-
throw e;
|
|
2467
|
+
// throw e;
|
|
2468
|
+
await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
|
|
2277
2469
|
}
|
|
2278
2470
|
finally {
|
|
2279
2471
|
const endTime = Date.now();
|
|
2280
|
-
|
|
2472
|
+
_reportToWorld(world, {
|
|
2281
2473
|
element_name: selectors.element_name,
|
|
2282
2474
|
type: Types.GET_TABLE_DATA,
|
|
2283
2475
|
text: "Get table data",
|
|
@@ -2287,7 +2479,7 @@ class StableBrowser {
|
|
|
2287
2479
|
status: "FAILED",
|
|
2288
2480
|
startTime,
|
|
2289
2481
|
endTime,
|
|
2290
|
-
message: error
|
|
2482
|
+
message: error?.message,
|
|
2291
2483
|
}
|
|
2292
2484
|
: {
|
|
2293
2485
|
status: "PASSED",
|
|
@@ -2438,11 +2630,12 @@ class StableBrowser {
|
|
|
2438
2630
|
info.screenshotPath = screenshotPath;
|
|
2439
2631
|
Object.assign(e, { info: info });
|
|
2440
2632
|
error = e;
|
|
2441
|
-
throw e;
|
|
2633
|
+
// throw e;
|
|
2634
|
+
await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
|
|
2442
2635
|
}
|
|
2443
2636
|
finally {
|
|
2444
2637
|
const endTime = Date.now();
|
|
2445
|
-
|
|
2638
|
+
_reportToWorld(world, {
|
|
2446
2639
|
element_name: selectors.element_name,
|
|
2447
2640
|
type: Types.ANALYZE_TABLE,
|
|
2448
2641
|
text: "Analyze table",
|
|
@@ -2452,7 +2645,7 @@ class StableBrowser {
|
|
|
2452
2645
|
status: "FAILED",
|
|
2453
2646
|
startTime,
|
|
2454
2647
|
endTime,
|
|
2455
|
-
message: error
|
|
2648
|
+
message: error?.message,
|
|
2456
2649
|
}
|
|
2457
2650
|
: {
|
|
2458
2651
|
status: "PASSED",
|
|
@@ -2464,27 +2657,7 @@ class StableBrowser {
|
|
|
2464
2657
|
}
|
|
2465
2658
|
}
|
|
2466
2659
|
async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
|
|
2467
|
-
|
|
2468
|
-
return value;
|
|
2469
|
-
}
|
|
2470
|
-
// find all the accurance of {{(.*?)}} and replace with the value
|
|
2471
|
-
let regex = /{{(.*?)}}/g;
|
|
2472
|
-
let matches = value.match(regex);
|
|
2473
|
-
if (matches) {
|
|
2474
|
-
const testData = this.getTestData(world);
|
|
2475
|
-
for (let i = 0; i < matches.length; i++) {
|
|
2476
|
-
let match = matches[i];
|
|
2477
|
-
let key = match.substring(2, match.length - 2);
|
|
2478
|
-
let newValue = objectPath.get(testData, key, null);
|
|
2479
|
-
if (newValue !== null) {
|
|
2480
|
-
value = value.replace(match, newValue);
|
|
2481
|
-
}
|
|
2482
|
-
}
|
|
2483
|
-
}
|
|
2484
|
-
if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
|
|
2485
|
-
return await decrypt(value, null, totpWait);
|
|
2486
|
-
}
|
|
2487
|
-
return value;
|
|
2660
|
+
return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
|
|
2488
2661
|
}
|
|
2489
2662
|
_getLoadTimeout(options) {
|
|
2490
2663
|
let timeout = 15000;
|
|
@@ -2535,7 +2708,7 @@ class StableBrowser {
|
|
|
2535
2708
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2536
2709
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2537
2710
|
const endTime = Date.now();
|
|
2538
|
-
|
|
2711
|
+
_reportToWorld(world, {
|
|
2539
2712
|
type: Types.GET_PAGE_STATUS,
|
|
2540
2713
|
text: "Wait for page load",
|
|
2541
2714
|
screenshotId,
|
|
@@ -2544,7 +2717,7 @@ class StableBrowser {
|
|
|
2544
2717
|
status: "FAILED",
|
|
2545
2718
|
startTime,
|
|
2546
2719
|
endTime,
|
|
2547
|
-
message: error
|
|
2720
|
+
message: error?.message,
|
|
2548
2721
|
}
|
|
2549
2722
|
: {
|
|
2550
2723
|
status: "PASSED",
|
|
@@ -2555,41 +2728,35 @@ class StableBrowser {
|
|
|
2555
2728
|
}
|
|
2556
2729
|
}
|
|
2557
2730
|
async closePage(options = {}, world = null) {
|
|
2558
|
-
const
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2731
|
+
const state = {
|
|
2732
|
+
options,
|
|
2733
|
+
world,
|
|
2734
|
+
locate: false,
|
|
2735
|
+
scroll: false,
|
|
2736
|
+
highlight: false,
|
|
2737
|
+
type: Types.CLOSE_PAGE,
|
|
2738
|
+
text: `Close page`,
|
|
2739
|
+
operation: "closePage",
|
|
2740
|
+
log: "***** close page *****\n",
|
|
2741
|
+
throwError: false,
|
|
2742
|
+
};
|
|
2563
2743
|
try {
|
|
2744
|
+
await _preCommand(state, this);
|
|
2564
2745
|
await this.page.close();
|
|
2565
2746
|
}
|
|
2566
2747
|
catch (e) {
|
|
2567
2748
|
console.log(".");
|
|
2749
|
+
await _commandError(state, e, this);
|
|
2568
2750
|
}
|
|
2569
2751
|
finally {
|
|
2570
|
-
|
|
2571
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2572
|
-
const endTime = Date.now();
|
|
2573
|
-
this._reportToWorld(world, {
|
|
2574
|
-
type: Types.CLOSE_PAGE,
|
|
2575
|
-
text: "close page",
|
|
2576
|
-
screenshotId,
|
|
2577
|
-
result: error
|
|
2578
|
-
? {
|
|
2579
|
-
status: "FAILED",
|
|
2580
|
-
startTime,
|
|
2581
|
-
endTime,
|
|
2582
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2583
|
-
}
|
|
2584
|
-
: {
|
|
2585
|
-
status: "PASSED",
|
|
2586
|
-
startTime,
|
|
2587
|
-
endTime,
|
|
2588
|
-
},
|
|
2589
|
-
info: info,
|
|
2590
|
-
});
|
|
2752
|
+
_commandFinally(state, this);
|
|
2591
2753
|
}
|
|
2592
2754
|
}
|
|
2755
|
+
saveTestDataAsGlobal(options, world) {
|
|
2756
|
+
const dataFile = this._getDataFile(world);
|
|
2757
|
+
process.env.GLOBAL_TEST_DATA_FILE = dataFile;
|
|
2758
|
+
this.logger.info("Save the scenario test data as global for the following scenarios.");
|
|
2759
|
+
}
|
|
2593
2760
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
2594
2761
|
const startTime = Date.now();
|
|
2595
2762
|
let error = null;
|
|
@@ -2607,12 +2774,13 @@ class StableBrowser {
|
|
|
2607
2774
|
}
|
|
2608
2775
|
catch (e) {
|
|
2609
2776
|
console.log(".");
|
|
2777
|
+
await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
|
|
2610
2778
|
}
|
|
2611
2779
|
finally {
|
|
2612
2780
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2613
2781
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2614
2782
|
const endTime = Date.now();
|
|
2615
|
-
|
|
2783
|
+
_reportToWorld(world, {
|
|
2616
2784
|
type: Types.SET_VIEWPORT,
|
|
2617
2785
|
text: "set viewport size to " + width + "x" + hight,
|
|
2618
2786
|
screenshotId,
|
|
@@ -2621,7 +2789,7 @@ class StableBrowser {
|
|
|
2621
2789
|
status: "FAILED",
|
|
2622
2790
|
startTime,
|
|
2623
2791
|
endTime,
|
|
2624
|
-
message: error
|
|
2792
|
+
message: error?.message,
|
|
2625
2793
|
}
|
|
2626
2794
|
: {
|
|
2627
2795
|
status: "PASSED",
|
|
@@ -2643,12 +2811,13 @@ class StableBrowser {
|
|
|
2643
2811
|
}
|
|
2644
2812
|
catch (e) {
|
|
2645
2813
|
console.log(".");
|
|
2814
|
+
await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
|
|
2646
2815
|
}
|
|
2647
2816
|
finally {
|
|
2648
2817
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2649
2818
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2650
2819
|
const endTime = Date.now();
|
|
2651
|
-
|
|
2820
|
+
_reportToWorld(world, {
|
|
2652
2821
|
type: Types.GET_PAGE_STATUS,
|
|
2653
2822
|
text: "page relaod",
|
|
2654
2823
|
screenshotId,
|
|
@@ -2657,7 +2826,7 @@ class StableBrowser {
|
|
|
2657
2826
|
status: "FAILED",
|
|
2658
2827
|
startTime,
|
|
2659
2828
|
endTime,
|
|
2660
|
-
message: error
|
|
2829
|
+
message: error?.message,
|
|
2661
2830
|
}
|
|
2662
2831
|
: {
|
|
2663
2832
|
status: "PASSED",
|
|
@@ -2684,11 +2853,37 @@ class StableBrowser {
|
|
|
2684
2853
|
console.log("#-#");
|
|
2685
2854
|
}
|
|
2686
2855
|
}
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2856
|
+
async beforeStep(world, step) {
|
|
2857
|
+
this.stepName = step.pickleStep.text;
|
|
2858
|
+
this.logger.info("step: " + this.stepName);
|
|
2859
|
+
if (this.stepIndex === undefined) {
|
|
2860
|
+
this.stepIndex = 0;
|
|
2861
|
+
}
|
|
2862
|
+
else {
|
|
2863
|
+
this.stepIndex++;
|
|
2864
|
+
}
|
|
2865
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2866
|
+
if (this.context.browserObject.context) {
|
|
2867
|
+
await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
if (this.tags === null && step && step.pickle && step.pickle.tags) {
|
|
2871
|
+
this.tags = step.pickle.tags.map((tag) => tag.name);
|
|
2872
|
+
// check if @global_test_data tag is present
|
|
2873
|
+
if (this.tags.includes("@global_test_data")) {
|
|
2874
|
+
this.saveTestDataAsGlobal({}, world);
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
async afterStep(world, step) {
|
|
2879
|
+
this.stepName = null;
|
|
2880
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2881
|
+
if (this.context.browserObject.context) {
|
|
2882
|
+
await this.context.browserObject.context.tracing.stopChunk({
|
|
2883
|
+
path: path.join(this.context.browserObject.traceFolder, `trace-${this.stepIndex}.zip`),
|
|
2884
|
+
});
|
|
2885
|
+
}
|
|
2690
2886
|
}
|
|
2691
|
-
world.attach(JSON.stringify(properties), { mediaType: "application/json" });
|
|
2692
2887
|
}
|
|
2693
2888
|
}
|
|
2694
2889
|
function createTimedPromise(promise, label) {
|