automation_model 1.0.478-dev → 1.0.478-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 +520 -290
- 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,10 +922,29 @@ 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
|
}
|
|
803
934
|
async simpleClick(elementDescription, _params, options = {}, world = null) {
|
|
935
|
+
const state = {
|
|
936
|
+
locate: false,
|
|
937
|
+
scroll: false,
|
|
938
|
+
highlight: false,
|
|
939
|
+
_params,
|
|
940
|
+
options,
|
|
941
|
+
world,
|
|
942
|
+
type: Types.CLICK,
|
|
943
|
+
text: "Click element",
|
|
944
|
+
operation: "simpleClick",
|
|
945
|
+
log: "***** click on " + elementDescription + " *****\n",
|
|
946
|
+
};
|
|
947
|
+
_preCommand(state, this);
|
|
804
948
|
const startTime = Date.now();
|
|
805
949
|
let timeout = 30000;
|
|
806
950
|
if (options && options.timeout) {
|
|
@@ -809,12 +953,12 @@ class StableBrowser {
|
|
|
809
953
|
while (true) {
|
|
810
954
|
try {
|
|
811
955
|
const result = await locate_element(this.context, elementDescription, "click");
|
|
812
|
-
if (
|
|
956
|
+
if (result?.elementNumber >= 0) {
|
|
813
957
|
const selectors = {
|
|
814
|
-
frame: result
|
|
958
|
+
frame: result?.frame,
|
|
815
959
|
locators: [
|
|
816
960
|
{
|
|
817
|
-
css: result
|
|
961
|
+
css: result?.css,
|
|
818
962
|
},
|
|
819
963
|
],
|
|
820
964
|
};
|
|
@@ -824,13 +968,32 @@ class StableBrowser {
|
|
|
824
968
|
}
|
|
825
969
|
catch (e) {
|
|
826
970
|
if (performance.now() - startTime > timeout) {
|
|
827
|
-
throw e;
|
|
971
|
+
// throw e;
|
|
972
|
+
try {
|
|
973
|
+
await _commandError(state, "timeout looking for " + elementDescription, this);
|
|
974
|
+
}
|
|
975
|
+
finally {
|
|
976
|
+
_commandFinally(state, this);
|
|
977
|
+
}
|
|
828
978
|
}
|
|
829
979
|
}
|
|
830
980
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
831
981
|
}
|
|
832
982
|
}
|
|
833
983
|
async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
|
|
984
|
+
const state = {
|
|
985
|
+
locate: false,
|
|
986
|
+
scroll: false,
|
|
987
|
+
highlight: false,
|
|
988
|
+
_params,
|
|
989
|
+
options,
|
|
990
|
+
world,
|
|
991
|
+
type: Types.FILL,
|
|
992
|
+
text: "Fill element",
|
|
993
|
+
operation: "simpleClickType",
|
|
994
|
+
log: "***** click type on " + elementDescription + " *****\n",
|
|
995
|
+
};
|
|
996
|
+
_preCommand(state, this);
|
|
834
997
|
const startTime = Date.now();
|
|
835
998
|
let timeout = 30000;
|
|
836
999
|
if (options && options.timeout) {
|
|
@@ -839,12 +1002,12 @@ class StableBrowser {
|
|
|
839
1002
|
while (true) {
|
|
840
1003
|
try {
|
|
841
1004
|
const result = await locate_element(this.context, elementDescription, "fill", value);
|
|
842
|
-
if (
|
|
1005
|
+
if (result?.elementNumber >= 0) {
|
|
843
1006
|
const selectors = {
|
|
844
|
-
frame: result
|
|
1007
|
+
frame: result?.frame,
|
|
845
1008
|
locators: [
|
|
846
1009
|
{
|
|
847
|
-
css: result
|
|
1010
|
+
css: result?.css,
|
|
848
1011
|
},
|
|
849
1012
|
],
|
|
850
1013
|
};
|
|
@@ -854,7 +1017,13 @@ class StableBrowser {
|
|
|
854
1017
|
}
|
|
855
1018
|
catch (e) {
|
|
856
1019
|
if (performance.now() - startTime > timeout) {
|
|
857
|
-
throw e;
|
|
1020
|
+
// throw e;
|
|
1021
|
+
try {
|
|
1022
|
+
await _commandError(state, "timeout looking for " + elementDescription, this);
|
|
1023
|
+
}
|
|
1024
|
+
finally {
|
|
1025
|
+
_commandFinally(state, this);
|
|
1026
|
+
}
|
|
858
1027
|
}
|
|
859
1028
|
}
|
|
860
1029
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
@@ -878,13 +1047,13 @@ class StableBrowser {
|
|
|
878
1047
|
}
|
|
879
1048
|
try {
|
|
880
1049
|
await state.element.click();
|
|
881
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1050
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
882
1051
|
}
|
|
883
1052
|
catch (e) {
|
|
884
1053
|
// await this.closeUnexpectedPopups();
|
|
885
1054
|
state.element = await this._locate(selectors, state.info, _params);
|
|
886
1055
|
await state.element.dispatchEvent("click");
|
|
887
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1056
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
888
1057
|
}
|
|
889
1058
|
await this.waitForPageLoad();
|
|
890
1059
|
return state.info;
|
|
@@ -982,6 +1151,7 @@ class StableBrowser {
|
|
|
982
1151
|
_params,
|
|
983
1152
|
options,
|
|
984
1153
|
world,
|
|
1154
|
+
value: values.toString(),
|
|
985
1155
|
type: Types.SELECT,
|
|
986
1156
|
text: `Select option: ${values}`,
|
|
987
1157
|
operation: "selectOption",
|
|
@@ -1089,33 +1259,30 @@ class StableBrowser {
|
|
|
1089
1259
|
}
|
|
1090
1260
|
}
|
|
1091
1261
|
async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1262
|
+
const state = {
|
|
1263
|
+
selectors,
|
|
1264
|
+
_params,
|
|
1265
|
+
value: await this._replaceWithLocalData(value, this),
|
|
1266
|
+
options,
|
|
1267
|
+
world,
|
|
1268
|
+
type: Types.SET_DATE_TIME,
|
|
1269
|
+
text: `Set date time value: ${value}`,
|
|
1270
|
+
operation: "setDateTime",
|
|
1271
|
+
log: "***** set date time value " + selectors.element_name + " *****\n",
|
|
1272
|
+
throwError: false,
|
|
1273
|
+
};
|
|
1102
1274
|
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);
|
|
1275
|
+
await _preCommand(state, this);
|
|
1109
1276
|
try {
|
|
1110
|
-
await element.click();
|
|
1277
|
+
await state.element.click();
|
|
1111
1278
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1112
1279
|
if (format) {
|
|
1113
|
-
value = dayjs(value).format(format);
|
|
1114
|
-
await element.fill(value);
|
|
1280
|
+
state.value = dayjs(state.value).format(format);
|
|
1281
|
+
await state.element.fill(state.value);
|
|
1115
1282
|
}
|
|
1116
1283
|
else {
|
|
1117
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1118
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1284
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1285
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1119
1286
|
el.value = ""; // clear input
|
|
1120
1287
|
el.value = dateTimeValue;
|
|
1121
1288
|
}, dateTimeValue);
|
|
@@ -1128,20 +1295,19 @@ class StableBrowser {
|
|
|
1128
1295
|
}
|
|
1129
1296
|
catch (err) {
|
|
1130
1297
|
//await this.closeUnexpectedPopups();
|
|
1131
|
-
this.logger.error("setting date time input failed " + JSON.stringify(info));
|
|
1298
|
+
this.logger.error("setting date time input failed " + JSON.stringify(state.info));
|
|
1132
1299
|
this.logger.info("Trying again");
|
|
1133
|
-
|
|
1134
|
-
info.
|
|
1135
|
-
Object.assign(err, { info: info });
|
|
1300
|
+
await _screenshot(state, this);
|
|
1301
|
+
Object.assign(err, { info: state.info });
|
|
1136
1302
|
await element.click();
|
|
1137
1303
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1138
1304
|
if (format) {
|
|
1139
|
-
value = dayjs(value).format(format);
|
|
1140
|
-
await element.fill(value);
|
|
1305
|
+
state.value = dayjs(state.value).format(format);
|
|
1306
|
+
await state.element.fill(state.value);
|
|
1141
1307
|
}
|
|
1142
1308
|
else {
|
|
1143
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1144
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1309
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1310
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1145
1311
|
el.value = ""; // clear input
|
|
1146
1312
|
el.value = dateTimeValue;
|
|
1147
1313
|
}, dateTimeValue);
|
|
@@ -1154,46 +1320,27 @@ class StableBrowser {
|
|
|
1154
1320
|
}
|
|
1155
1321
|
}
|
|
1156
1322
|
catch (e) {
|
|
1157
|
-
|
|
1158
|
-
throw e;
|
|
1323
|
+
await _commandError(state, e, this);
|
|
1159
1324
|
}
|
|
1160
1325
|
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
|
-
});
|
|
1326
|
+
_commandFinally(state, this);
|
|
1182
1327
|
}
|
|
1183
1328
|
}
|
|
1184
1329
|
async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
|
|
1330
|
+
_value = unEscapeString(_value);
|
|
1331
|
+
const newValue = await this._replaceWithLocalData(_value, world);
|
|
1185
1332
|
const state = {
|
|
1186
1333
|
selectors,
|
|
1187
1334
|
_params,
|
|
1188
|
-
value:
|
|
1335
|
+
value: newValue,
|
|
1336
|
+
originalValue: _value,
|
|
1189
1337
|
options,
|
|
1190
1338
|
world,
|
|
1191
1339
|
type: Types.FILL,
|
|
1192
1340
|
text: `Click type input with value: ${_value}`,
|
|
1193
1341
|
operation: "clickType",
|
|
1194
|
-
log: "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n",
|
|
1342
|
+
log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
|
|
1195
1343
|
};
|
|
1196
|
-
const newValue = await this._replaceWithLocalData(state.value, world);
|
|
1197
1344
|
if (newValue !== _value) {
|
|
1198
1345
|
//this.logger.info(_value + "=" + newValue);
|
|
1199
1346
|
_value = newValue;
|
|
@@ -1205,7 +1352,7 @@ class StableBrowser {
|
|
|
1205
1352
|
try {
|
|
1206
1353
|
let currentValue = await state.element.inputValue();
|
|
1207
1354
|
if (currentValue) {
|
|
1208
|
-
await element.fill("");
|
|
1355
|
+
await state.element.fill("");
|
|
1209
1356
|
}
|
|
1210
1357
|
}
|
|
1211
1358
|
catch (e) {
|
|
@@ -1313,6 +1460,7 @@ class StableBrowser {
|
|
|
1313
1460
|
let screenshotPath = null;
|
|
1314
1461
|
if (!info.log) {
|
|
1315
1462
|
info.log = "";
|
|
1463
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
1316
1464
|
}
|
|
1317
1465
|
info.operation = "getText";
|
|
1318
1466
|
info.selectors = selectors;
|
|
@@ -1352,7 +1500,6 @@ class StableBrowser {
|
|
|
1352
1500
|
}
|
|
1353
1501
|
}
|
|
1354
1502
|
async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
|
|
1355
|
-
var _a;
|
|
1356
1503
|
if (!pattern) {
|
|
1357
1504
|
throw new Error("pattern is null");
|
|
1358
1505
|
}
|
|
@@ -1363,7 +1510,7 @@ class StableBrowser {
|
|
|
1363
1510
|
selectors,
|
|
1364
1511
|
_params,
|
|
1365
1512
|
pattern,
|
|
1366
|
-
value:
|
|
1513
|
+
value: pattern,
|
|
1367
1514
|
options,
|
|
1368
1515
|
world,
|
|
1369
1516
|
locate: false,
|
|
@@ -1392,14 +1539,14 @@ class StableBrowser {
|
|
|
1392
1539
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1393
1540
|
pattern = pattern.replace("{text}", escapedText);
|
|
1394
1541
|
let regex = new RegExp(pattern, "im");
|
|
1395
|
-
if (!regex.test(foundObj
|
|
1396
|
-
state.info.foundText = foundObj
|
|
1542
|
+
if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
|
|
1543
|
+
state.info.foundText = foundObj?.text;
|
|
1397
1544
|
throw new Error("element doesn't contain text " + text);
|
|
1398
1545
|
}
|
|
1399
1546
|
return state.info;
|
|
1400
1547
|
}
|
|
1401
1548
|
catch (e) {
|
|
1402
|
-
this.logger.error("found text " +
|
|
1549
|
+
this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
|
|
1403
1550
|
await _commandError(state, e, this);
|
|
1404
1551
|
}
|
|
1405
1552
|
finally {
|
|
@@ -1407,7 +1554,6 @@ class StableBrowser {
|
|
|
1407
1554
|
}
|
|
1408
1555
|
}
|
|
1409
1556
|
async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
|
|
1410
|
-
var _a, _b, _c;
|
|
1411
1557
|
const state = {
|
|
1412
1558
|
selectors,
|
|
1413
1559
|
_params,
|
|
@@ -1444,8 +1590,8 @@ class StableBrowser {
|
|
|
1444
1590
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1445
1591
|
if (dateAlternatives.date) {
|
|
1446
1592
|
for (let i = 0; i < dateAlternatives.dates.length; i++) {
|
|
1447
|
-
if (
|
|
1448
|
-
|
|
1593
|
+
if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
|
|
1594
|
+
foundObj?.value?.includes(dateAlternatives.dates[i])) {
|
|
1449
1595
|
return state.info;
|
|
1450
1596
|
}
|
|
1451
1597
|
}
|
|
@@ -1453,16 +1599,16 @@ class StableBrowser {
|
|
|
1453
1599
|
}
|
|
1454
1600
|
else if (numberAlternatives.number) {
|
|
1455
1601
|
for (let i = 0; i < numberAlternatives.numbers.length; i++) {
|
|
1456
|
-
if (
|
|
1457
|
-
|
|
1602
|
+
if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
|
|
1603
|
+
foundObj?.value?.includes(numberAlternatives.numbers[i])) {
|
|
1458
1604
|
return state.info;
|
|
1459
1605
|
}
|
|
1460
1606
|
}
|
|
1461
1607
|
throw new Error("element doesn't contain text " + text);
|
|
1462
1608
|
}
|
|
1463
|
-
else if (!
|
|
1464
|
-
state.info.foundText = foundObj
|
|
1465
|
-
state.info.value = foundObj
|
|
1609
|
+
else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
|
|
1610
|
+
state.info.foundText = foundObj?.text;
|
|
1611
|
+
state.info.value = foundObj?.value;
|
|
1466
1612
|
throw new Error("element doesn't contain text " + text);
|
|
1467
1613
|
}
|
|
1468
1614
|
return state.info;
|
|
@@ -1653,11 +1799,9 @@ class StableBrowser {
|
|
|
1653
1799
|
if (!fs.existsSync(world.screenshotPath)) {
|
|
1654
1800
|
fs.mkdirSync(world.screenshotPath, { recursive: true });
|
|
1655
1801
|
}
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
}
|
|
1660
|
-
const screenshotPath = path.join(world.screenshotPath, nextIndex + ".png");
|
|
1802
|
+
// to make sure the path doesn't start with -
|
|
1803
|
+
const uuidStr = "id_" + randomUUID();
|
|
1804
|
+
const screenshotPath = path.join(world.screenshotPath, uuidStr + ".png");
|
|
1661
1805
|
try {
|
|
1662
1806
|
await this.takeScreenshot(screenshotPath);
|
|
1663
1807
|
// let buffer = await this.page.screenshot({ timeout: 4000 });
|
|
@@ -1671,7 +1815,7 @@ class StableBrowser {
|
|
|
1671
1815
|
catch (e) {
|
|
1672
1816
|
this.logger.info("unable to take screenshot, ignored");
|
|
1673
1817
|
}
|
|
1674
|
-
result.screenshotId =
|
|
1818
|
+
result.screenshotId = uuidStr;
|
|
1675
1819
|
result.screenshotPath = screenshotPath;
|
|
1676
1820
|
if (info && info.box) {
|
|
1677
1821
|
await drawRectangle(screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
|
|
@@ -1769,74 +1913,101 @@ class StableBrowser {
|
|
|
1769
1913
|
}
|
|
1770
1914
|
}
|
|
1771
1915
|
async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1916
|
+
const state = {
|
|
1917
|
+
selectors,
|
|
1918
|
+
_params,
|
|
1919
|
+
attribute,
|
|
1920
|
+
variable,
|
|
1921
|
+
options,
|
|
1922
|
+
world,
|
|
1923
|
+
type: Types.EXTRACT,
|
|
1924
|
+
text: `Extract attribute from element`,
|
|
1925
|
+
operation: "extractAttribute",
|
|
1926
|
+
log: "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1927
|
+
};
|
|
1777
1928
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1778
|
-
const info = {};
|
|
1779
|
-
info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
|
|
1780
|
-
info.operation = "extract";
|
|
1781
|
-
info.selectors = selectors;
|
|
1782
1929
|
try {
|
|
1783
|
-
|
|
1784
|
-
await this._highlightElements(element);
|
|
1785
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1930
|
+
await _preCommand(state, this);
|
|
1786
1931
|
switch (attribute) {
|
|
1787
1932
|
case "inner_text":
|
|
1788
|
-
|
|
1933
|
+
state.value = await state.element.innerText();
|
|
1789
1934
|
break;
|
|
1790
1935
|
case "href":
|
|
1791
|
-
|
|
1936
|
+
state.value = await state.element.getAttribute("href");
|
|
1792
1937
|
break;
|
|
1793
1938
|
case "value":
|
|
1794
|
-
|
|
1939
|
+
state.value = await state.element.inputValue();
|
|
1795
1940
|
break;
|
|
1796
1941
|
default:
|
|
1797
|
-
|
|
1942
|
+
state.value = await state.element.getAttribute(attribute);
|
|
1798
1943
|
break;
|
|
1799
1944
|
}
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1945
|
+
state.info.value = state.value;
|
|
1946
|
+
this.setTestData({ [variable]: state.value }, world);
|
|
1947
|
+
this.logger.info("set test data: " + variable + "=" + state.value);
|
|
1948
|
+
return state.info;
|
|
1949
|
+
}
|
|
1950
|
+
catch (e) {
|
|
1951
|
+
await _commandError(state, e, this);
|
|
1952
|
+
}
|
|
1953
|
+
finally {
|
|
1954
|
+
_commandFinally(state, this);
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
async verifyAttribute(selectors, attribute, value, _params = null, options = {}, world = null) {
|
|
1958
|
+
const state = {
|
|
1959
|
+
selectors,
|
|
1960
|
+
_params,
|
|
1961
|
+
attribute,
|
|
1962
|
+
value,
|
|
1963
|
+
options,
|
|
1964
|
+
world,
|
|
1965
|
+
type: Types.VERIFY_ATTRIBUTE,
|
|
1966
|
+
text: `Verify element attribute`,
|
|
1967
|
+
operation: "verifyAttribute",
|
|
1968
|
+
log: "***** verify attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1969
|
+
};
|
|
1970
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1971
|
+
let val;
|
|
1972
|
+
try {
|
|
1973
|
+
await _preCommand(state, this);
|
|
1974
|
+
switch (attribute) {
|
|
1975
|
+
case "innerText":
|
|
1976
|
+
val = String(await state.element.innerText());
|
|
1977
|
+
break;
|
|
1978
|
+
case "value":
|
|
1979
|
+
val = String(await state.element.inputValue());
|
|
1980
|
+
break;
|
|
1981
|
+
case "checked":
|
|
1982
|
+
val = String(await state.element.isChecked());
|
|
1983
|
+
break;
|
|
1984
|
+
case "disabled":
|
|
1985
|
+
val = String(await state.element.isDisabled());
|
|
1986
|
+
break;
|
|
1987
|
+
default:
|
|
1988
|
+
val = String(await state.element.getAttribute(attribute));
|
|
1989
|
+
break;
|
|
1803
1990
|
}
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1991
|
+
let regex;
|
|
1992
|
+
if (value.startsWith("/") && value.endsWith("/")) {
|
|
1993
|
+
const patternBody = value.slice(1, -1);
|
|
1994
|
+
regex = new RegExp(patternBody, "g");
|
|
1995
|
+
}
|
|
1996
|
+
else {
|
|
1997
|
+
const escapedPattern = value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1998
|
+
regex = new RegExp(escapedPattern, "g");
|
|
1999
|
+
}
|
|
2000
|
+
if (!val.match(regex)) {
|
|
2001
|
+
throw new Error(`The ${attribute} attribute has a value of "${val}", but the expected value is "${value}"`);
|
|
2002
|
+
}
|
|
2003
|
+
state.info.value = val;
|
|
2004
|
+
return state.info;
|
|
1807
2005
|
}
|
|
1808
2006
|
catch (e) {
|
|
1809
|
-
|
|
1810
|
-
this.logger.error("extract failed " + info.log);
|
|
1811
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1812
|
-
info.screenshotPath = screenshotPath;
|
|
1813
|
-
Object.assign(e, { info: info });
|
|
1814
|
-
error = e;
|
|
1815
|
-
throw e;
|
|
2007
|
+
await _commandError(state, e, this);
|
|
1816
2008
|
}
|
|
1817
2009
|
finally {
|
|
1818
|
-
|
|
1819
|
-
this._reportToWorld(world, {
|
|
1820
|
-
element_name: selectors.element_name,
|
|
1821
|
-
type: Types.EXTRACT_ATTRIBUTE,
|
|
1822
|
-
variable: variable,
|
|
1823
|
-
value: info.value,
|
|
1824
|
-
text: "Extract attribute from element",
|
|
1825
|
-
screenshotId,
|
|
1826
|
-
result: error
|
|
1827
|
-
? {
|
|
1828
|
-
status: "FAILED",
|
|
1829
|
-
startTime,
|
|
1830
|
-
endTime,
|
|
1831
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1832
|
-
}
|
|
1833
|
-
: {
|
|
1834
|
-
status: "PASSED",
|
|
1835
|
-
startTime,
|
|
1836
|
-
endTime,
|
|
1837
|
-
},
|
|
1838
|
-
info: info,
|
|
1839
|
-
});
|
|
2010
|
+
_commandFinally(state, this);
|
|
1840
2011
|
}
|
|
1841
2012
|
}
|
|
1842
2013
|
async extractEmailData(emailAddress, options, world) {
|
|
@@ -1913,7 +2084,8 @@ class StableBrowser {
|
|
|
1913
2084
|
catch (e) {
|
|
1914
2085
|
errorCount++;
|
|
1915
2086
|
if (errorCount > 3) {
|
|
1916
|
-
throw e;
|
|
2087
|
+
// throw e;
|
|
2088
|
+
await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
|
|
1917
2089
|
}
|
|
1918
2090
|
// ignore
|
|
1919
2091
|
}
|
|
@@ -2024,11 +2196,12 @@ class StableBrowser {
|
|
|
2024
2196
|
info.screenshotPath = screenshotPath;
|
|
2025
2197
|
Object.assign(e, { info: info });
|
|
2026
2198
|
error = e;
|
|
2027
|
-
throw e;
|
|
2199
|
+
// throw e;
|
|
2200
|
+
await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
|
|
2028
2201
|
}
|
|
2029
2202
|
finally {
|
|
2030
2203
|
const endTime = Date.now();
|
|
2031
|
-
|
|
2204
|
+
_reportToWorld(world, {
|
|
2032
2205
|
type: Types.VERIFY_PAGE_PATH,
|
|
2033
2206
|
text: "Verify page path",
|
|
2034
2207
|
screenshotId,
|
|
@@ -2037,7 +2210,7 @@ class StableBrowser {
|
|
|
2037
2210
|
status: "FAILED",
|
|
2038
2211
|
startTime,
|
|
2039
2212
|
endTime,
|
|
2040
|
-
message: error
|
|
2213
|
+
message: error?.message,
|
|
2041
2214
|
}
|
|
2042
2215
|
: {
|
|
2043
2216
|
status: "PASSED",
|
|
@@ -2050,52 +2223,58 @@ class StableBrowser {
|
|
|
2050
2223
|
}
|
|
2051
2224
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2052
2225
|
text = unEscapeString(text);
|
|
2053
|
-
const
|
|
2226
|
+
const state = {
|
|
2227
|
+
text_search: text,
|
|
2228
|
+
options,
|
|
2229
|
+
world,
|
|
2230
|
+
locate: false,
|
|
2231
|
+
scroll: false,
|
|
2232
|
+
highlight: false,
|
|
2233
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
2234
|
+
text: `Verify text exists in page`,
|
|
2235
|
+
operation: "verifyTextExistInPage",
|
|
2236
|
+
log: "***** verify text " + text + " exists in page *****\n",
|
|
2237
|
+
};
|
|
2054
2238
|
const timeout = this._getLoadTimeout(options);
|
|
2055
|
-
let error = null;
|
|
2056
|
-
let screenshotId = null;
|
|
2057
|
-
let screenshotPath = null;
|
|
2058
2239
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2059
|
-
const info = {};
|
|
2060
|
-
info.log = "***** verify text " + text + " exists in page *****\n";
|
|
2061
|
-
info.operation = "verifyTextExistInPage";
|
|
2062
2240
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
2063
2241
|
if (newValue !== text) {
|
|
2064
2242
|
this.logger.info(text + "=" + newValue);
|
|
2065
2243
|
text = newValue;
|
|
2066
2244
|
}
|
|
2067
|
-
info.text = text;
|
|
2068
2245
|
let dateAlternatives = findDateAlternatives(text);
|
|
2069
2246
|
let numberAlternatives = findNumberAlternatives(text);
|
|
2070
2247
|
try {
|
|
2248
|
+
await _preCommand(state, this);
|
|
2249
|
+
state.info.text = text;
|
|
2071
2250
|
while (true) {
|
|
2072
2251
|
const frames = this.page.frames();
|
|
2073
2252
|
let results = [];
|
|
2074
2253
|
for (let i = 0; i < frames.length; i++) {
|
|
2075
2254
|
if (dateAlternatives.date) {
|
|
2076
2255
|
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2077
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "
|
|
2256
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2078
2257
|
result.frame = frames[i];
|
|
2079
2258
|
results.push(result);
|
|
2080
2259
|
}
|
|
2081
2260
|
}
|
|
2082
2261
|
else if (numberAlternatives.number) {
|
|
2083
2262
|
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2084
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "
|
|
2263
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2085
2264
|
result.frame = frames[i];
|
|
2086
2265
|
results.push(result);
|
|
2087
2266
|
}
|
|
2088
2267
|
}
|
|
2089
2268
|
else {
|
|
2090
|
-
const result = await this._locateElementByText(frames[i], text, "
|
|
2269
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2091
2270
|
result.frame = frames[i];
|
|
2092
2271
|
results.push(result);
|
|
2093
2272
|
}
|
|
2094
2273
|
}
|
|
2095
|
-
info.results = results;
|
|
2274
|
+
state.info.results = results;
|
|
2096
2275
|
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2097
2276
|
if (resultWithElementsFound.length === 0) {
|
|
2098
|
-
if (Date.now() - startTime > timeout) {
|
|
2277
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2099
2278
|
throw new Error(`Text ${text} not found in page`);
|
|
2100
2279
|
}
|
|
2101
2280
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -2107,44 +2286,89 @@ class StableBrowser {
|
|
|
2107
2286
|
await this._highlightElements(frame, dataAttribute);
|
|
2108
2287
|
const element = await frame.$(dataAttribute);
|
|
2109
2288
|
if (element) {
|
|
2110
|
-
await this.scrollIfNeeded(element, info);
|
|
2289
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2111
2290
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2112
2291
|
}
|
|
2113
2292
|
}
|
|
2114
|
-
|
|
2115
|
-
return info;
|
|
2293
|
+
await _screenshot(state, this);
|
|
2294
|
+
return state.info;
|
|
2116
2295
|
}
|
|
2117
2296
|
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2118
2297
|
}
|
|
2119
2298
|
catch (e) {
|
|
2120
|
-
|
|
2121
|
-
this.logger.error("verify text exist in page failed " + info.log);
|
|
2122
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2123
|
-
info.screenshotPath = screenshotPath;
|
|
2124
|
-
Object.assign(e, { info: info });
|
|
2125
|
-
error = e;
|
|
2126
|
-
throw e;
|
|
2299
|
+
await _commandError(state, e, this);
|
|
2127
2300
|
}
|
|
2128
2301
|
finally {
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2302
|
+
_commandFinally(state, this);
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
async waitForTextToDisappear(text, options = {}, world = null) {
|
|
2306
|
+
text = unEscapeString(text);
|
|
2307
|
+
const state = {
|
|
2308
|
+
text_search: text,
|
|
2309
|
+
options,
|
|
2310
|
+
world,
|
|
2311
|
+
locate: false,
|
|
2312
|
+
scroll: false,
|
|
2313
|
+
highlight: false,
|
|
2314
|
+
type: Types.WAIT_FOR_TEXT_TO_DISAPPEAR,
|
|
2315
|
+
text: `Verify text does not exist in page`,
|
|
2316
|
+
operation: "verifyTextNotExistInPage",
|
|
2317
|
+
log: "***** verify text " + text + " does not exist in page *****\n",
|
|
2318
|
+
};
|
|
2319
|
+
const timeout = this._getLoadTimeout(options);
|
|
2320
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2321
|
+
const newValue = await this._replaceWithLocalData(text, world);
|
|
2322
|
+
if (newValue !== text) {
|
|
2323
|
+
this.logger.info(text + "=" + newValue);
|
|
2324
|
+
text = newValue;
|
|
2325
|
+
}
|
|
2326
|
+
let dateAlternatives = findDateAlternatives(text);
|
|
2327
|
+
let numberAlternatives = findNumberAlternatives(text);
|
|
2328
|
+
try {
|
|
2329
|
+
await _preCommand(state, this);
|
|
2330
|
+
state.info.text = text;
|
|
2331
|
+
while (true) {
|
|
2332
|
+
const frames = this.page.frames();
|
|
2333
|
+
let results = [];
|
|
2334
|
+
for (let i = 0; i < frames.length; i++) {
|
|
2335
|
+
if (dateAlternatives.date) {
|
|
2336
|
+
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2337
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2338
|
+
result.frame = frames[i];
|
|
2339
|
+
results.push(result);
|
|
2340
|
+
}
|
|
2140
2341
|
}
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2342
|
+
else if (numberAlternatives.number) {
|
|
2343
|
+
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2344
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2345
|
+
result.frame = frames[i];
|
|
2346
|
+
results.push(result);
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
else {
|
|
2350
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2351
|
+
result.frame = frames[i];
|
|
2352
|
+
results.push(result);
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
state.info.results = results;
|
|
2356
|
+
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2357
|
+
if (resultWithElementsFound.length === 0) {
|
|
2358
|
+
await _screenshot(state, this);
|
|
2359
|
+
return state.info;
|
|
2360
|
+
}
|
|
2361
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2362
|
+
throw new Error(`Text ${text} found in page`);
|
|
2363
|
+
}
|
|
2364
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
catch (e) {
|
|
2368
|
+
await _commandError(state, e, this);
|
|
2369
|
+
}
|
|
2370
|
+
finally {
|
|
2371
|
+
_commandFinally(state, this);
|
|
2148
2372
|
}
|
|
2149
2373
|
}
|
|
2150
2374
|
_getServerUrl() {
|
|
@@ -2207,11 +2431,12 @@ class StableBrowser {
|
|
|
2207
2431
|
info.screenshotPath = screenshotPath;
|
|
2208
2432
|
Object.assign(e, { info: info });
|
|
2209
2433
|
error = e;
|
|
2210
|
-
throw e;
|
|
2434
|
+
// throw e;
|
|
2435
|
+
await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
|
|
2211
2436
|
}
|
|
2212
2437
|
finally {
|
|
2213
2438
|
const endTime = Date.now();
|
|
2214
|
-
|
|
2439
|
+
_reportToWorld(world, {
|
|
2215
2440
|
type: Types.VERIFY_VISUAL,
|
|
2216
2441
|
text: "Visual verification",
|
|
2217
2442
|
screenshotId,
|
|
@@ -2220,7 +2445,7 @@ class StableBrowser {
|
|
|
2220
2445
|
status: "FAILED",
|
|
2221
2446
|
startTime,
|
|
2222
2447
|
endTime,
|
|
2223
|
-
message: error
|
|
2448
|
+
message: error?.message,
|
|
2224
2449
|
}
|
|
2225
2450
|
: {
|
|
2226
2451
|
status: "PASSED",
|
|
@@ -2259,6 +2484,7 @@ class StableBrowser {
|
|
|
2259
2484
|
let screenshotPath = null;
|
|
2260
2485
|
const info = {};
|
|
2261
2486
|
info.log = "";
|
|
2487
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
2262
2488
|
info.operation = "getTableData";
|
|
2263
2489
|
info.selectors = selectors;
|
|
2264
2490
|
try {
|
|
@@ -2274,11 +2500,12 @@ class StableBrowser {
|
|
|
2274
2500
|
info.screenshotPath = screenshotPath;
|
|
2275
2501
|
Object.assign(e, { info: info });
|
|
2276
2502
|
error = e;
|
|
2277
|
-
throw e;
|
|
2503
|
+
// throw e;
|
|
2504
|
+
await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
|
|
2278
2505
|
}
|
|
2279
2506
|
finally {
|
|
2280
2507
|
const endTime = Date.now();
|
|
2281
|
-
|
|
2508
|
+
_reportToWorld(world, {
|
|
2282
2509
|
element_name: selectors.element_name,
|
|
2283
2510
|
type: Types.GET_TABLE_DATA,
|
|
2284
2511
|
text: "Get table data",
|
|
@@ -2288,7 +2515,7 @@ class StableBrowser {
|
|
|
2288
2515
|
status: "FAILED",
|
|
2289
2516
|
startTime,
|
|
2290
2517
|
endTime,
|
|
2291
|
-
message: error
|
|
2518
|
+
message: error?.message,
|
|
2292
2519
|
}
|
|
2293
2520
|
: {
|
|
2294
2521
|
status: "PASSED",
|
|
@@ -2439,11 +2666,12 @@ class StableBrowser {
|
|
|
2439
2666
|
info.screenshotPath = screenshotPath;
|
|
2440
2667
|
Object.assign(e, { info: info });
|
|
2441
2668
|
error = e;
|
|
2442
|
-
throw e;
|
|
2669
|
+
// throw e;
|
|
2670
|
+
await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
|
|
2443
2671
|
}
|
|
2444
2672
|
finally {
|
|
2445
2673
|
const endTime = Date.now();
|
|
2446
|
-
|
|
2674
|
+
_reportToWorld(world, {
|
|
2447
2675
|
element_name: selectors.element_name,
|
|
2448
2676
|
type: Types.ANALYZE_TABLE,
|
|
2449
2677
|
text: "Analyze table",
|
|
@@ -2453,7 +2681,7 @@ class StableBrowser {
|
|
|
2453
2681
|
status: "FAILED",
|
|
2454
2682
|
startTime,
|
|
2455
2683
|
endTime,
|
|
2456
|
-
message: error
|
|
2684
|
+
message: error?.message,
|
|
2457
2685
|
}
|
|
2458
2686
|
: {
|
|
2459
2687
|
status: "PASSED",
|
|
@@ -2465,27 +2693,7 @@ class StableBrowser {
|
|
|
2465
2693
|
}
|
|
2466
2694
|
}
|
|
2467
2695
|
async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
|
|
2468
|
-
|
|
2469
|
-
return value;
|
|
2470
|
-
}
|
|
2471
|
-
// find all the accurance of {{(.*?)}} and replace with the value
|
|
2472
|
-
let regex = /{{(.*?)}}/g;
|
|
2473
|
-
let matches = value.match(regex);
|
|
2474
|
-
if (matches) {
|
|
2475
|
-
const testData = this.getTestData(world);
|
|
2476
|
-
for (let i = 0; i < matches.length; i++) {
|
|
2477
|
-
let match = matches[i];
|
|
2478
|
-
let key = match.substring(2, match.length - 2);
|
|
2479
|
-
let newValue = objectPath.get(testData, key, null);
|
|
2480
|
-
if (newValue !== null) {
|
|
2481
|
-
value = value.replace(match, newValue);
|
|
2482
|
-
}
|
|
2483
|
-
}
|
|
2484
|
-
}
|
|
2485
|
-
if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
|
|
2486
|
-
return await decrypt(value, null, totpWait);
|
|
2487
|
-
}
|
|
2488
|
-
return value;
|
|
2696
|
+
return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
|
|
2489
2697
|
}
|
|
2490
2698
|
_getLoadTimeout(options) {
|
|
2491
2699
|
let timeout = 15000;
|
|
@@ -2536,7 +2744,7 @@ class StableBrowser {
|
|
|
2536
2744
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2537
2745
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2538
2746
|
const endTime = Date.now();
|
|
2539
|
-
|
|
2747
|
+
_reportToWorld(world, {
|
|
2540
2748
|
type: Types.GET_PAGE_STATUS,
|
|
2541
2749
|
text: "Wait for page load",
|
|
2542
2750
|
screenshotId,
|
|
@@ -2545,7 +2753,7 @@ class StableBrowser {
|
|
|
2545
2753
|
status: "FAILED",
|
|
2546
2754
|
startTime,
|
|
2547
2755
|
endTime,
|
|
2548
|
-
message: error
|
|
2756
|
+
message: error?.message,
|
|
2549
2757
|
}
|
|
2550
2758
|
: {
|
|
2551
2759
|
status: "PASSED",
|
|
@@ -2556,41 +2764,35 @@ class StableBrowser {
|
|
|
2556
2764
|
}
|
|
2557
2765
|
}
|
|
2558
2766
|
async closePage(options = {}, world = null) {
|
|
2559
|
-
const
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2767
|
+
const state = {
|
|
2768
|
+
options,
|
|
2769
|
+
world,
|
|
2770
|
+
locate: false,
|
|
2771
|
+
scroll: false,
|
|
2772
|
+
highlight: false,
|
|
2773
|
+
type: Types.CLOSE_PAGE,
|
|
2774
|
+
text: `Close page`,
|
|
2775
|
+
operation: "closePage",
|
|
2776
|
+
log: "***** close page *****\n",
|
|
2777
|
+
throwError: false,
|
|
2778
|
+
};
|
|
2564
2779
|
try {
|
|
2780
|
+
await _preCommand(state, this);
|
|
2565
2781
|
await this.page.close();
|
|
2566
2782
|
}
|
|
2567
2783
|
catch (e) {
|
|
2568
2784
|
console.log(".");
|
|
2785
|
+
await _commandError(state, e, this);
|
|
2569
2786
|
}
|
|
2570
2787
|
finally {
|
|
2571
|
-
|
|
2572
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2573
|
-
const endTime = Date.now();
|
|
2574
|
-
this._reportToWorld(world, {
|
|
2575
|
-
type: Types.CLOSE_PAGE,
|
|
2576
|
-
text: "close page",
|
|
2577
|
-
screenshotId,
|
|
2578
|
-
result: error
|
|
2579
|
-
? {
|
|
2580
|
-
status: "FAILED",
|
|
2581
|
-
startTime,
|
|
2582
|
-
endTime,
|
|
2583
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2584
|
-
}
|
|
2585
|
-
: {
|
|
2586
|
-
status: "PASSED",
|
|
2587
|
-
startTime,
|
|
2588
|
-
endTime,
|
|
2589
|
-
},
|
|
2590
|
-
info: info,
|
|
2591
|
-
});
|
|
2788
|
+
_commandFinally(state, this);
|
|
2592
2789
|
}
|
|
2593
2790
|
}
|
|
2791
|
+
saveTestDataAsGlobal(options, world) {
|
|
2792
|
+
const dataFile = this._getDataFile(world);
|
|
2793
|
+
process.env.GLOBAL_TEST_DATA_FILE = dataFile;
|
|
2794
|
+
this.logger.info("Save the scenario test data as global for the following scenarios.");
|
|
2795
|
+
}
|
|
2594
2796
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
2595
2797
|
const startTime = Date.now();
|
|
2596
2798
|
let error = null;
|
|
@@ -2608,12 +2810,13 @@ class StableBrowser {
|
|
|
2608
2810
|
}
|
|
2609
2811
|
catch (e) {
|
|
2610
2812
|
console.log(".");
|
|
2813
|
+
await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
|
|
2611
2814
|
}
|
|
2612
2815
|
finally {
|
|
2613
2816
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2614
2817
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2615
2818
|
const endTime = Date.now();
|
|
2616
|
-
|
|
2819
|
+
_reportToWorld(world, {
|
|
2617
2820
|
type: Types.SET_VIEWPORT,
|
|
2618
2821
|
text: "set viewport size to " + width + "x" + hight,
|
|
2619
2822
|
screenshotId,
|
|
@@ -2622,7 +2825,7 @@ class StableBrowser {
|
|
|
2622
2825
|
status: "FAILED",
|
|
2623
2826
|
startTime,
|
|
2624
2827
|
endTime,
|
|
2625
|
-
message: error
|
|
2828
|
+
message: error?.message,
|
|
2626
2829
|
}
|
|
2627
2830
|
: {
|
|
2628
2831
|
status: "PASSED",
|
|
@@ -2644,12 +2847,13 @@ class StableBrowser {
|
|
|
2644
2847
|
}
|
|
2645
2848
|
catch (e) {
|
|
2646
2849
|
console.log(".");
|
|
2850
|
+
await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
|
|
2647
2851
|
}
|
|
2648
2852
|
finally {
|
|
2649
2853
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2650
2854
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2651
2855
|
const endTime = Date.now();
|
|
2652
|
-
|
|
2856
|
+
_reportToWorld(world, {
|
|
2653
2857
|
type: Types.GET_PAGE_STATUS,
|
|
2654
2858
|
text: "page relaod",
|
|
2655
2859
|
screenshotId,
|
|
@@ -2658,7 +2862,7 @@ class StableBrowser {
|
|
|
2658
2862
|
status: "FAILED",
|
|
2659
2863
|
startTime,
|
|
2660
2864
|
endTime,
|
|
2661
|
-
message: error
|
|
2865
|
+
message: error?.message,
|
|
2662
2866
|
}
|
|
2663
2867
|
: {
|
|
2664
2868
|
status: "PASSED",
|
|
@@ -2685,11 +2889,37 @@ class StableBrowser {
|
|
|
2685
2889
|
console.log("#-#");
|
|
2686
2890
|
}
|
|
2687
2891
|
}
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2892
|
+
async beforeStep(world, step) {
|
|
2893
|
+
this.stepName = step.pickleStep.text;
|
|
2894
|
+
this.logger.info("step: " + this.stepName);
|
|
2895
|
+
if (this.stepIndex === undefined) {
|
|
2896
|
+
this.stepIndex = 0;
|
|
2897
|
+
}
|
|
2898
|
+
else {
|
|
2899
|
+
this.stepIndex++;
|
|
2900
|
+
}
|
|
2901
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2902
|
+
if (this.context.browserObject.context) {
|
|
2903
|
+
await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
if (this.tags === null && step && step.pickle && step.pickle.tags) {
|
|
2907
|
+
this.tags = step.pickle.tags.map((tag) => tag.name);
|
|
2908
|
+
// check if @global_test_data tag is present
|
|
2909
|
+
if (this.tags.includes("@global_test_data")) {
|
|
2910
|
+
this.saveTestDataAsGlobal({}, world);
|
|
2911
|
+
}
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
async afterStep(world, step) {
|
|
2915
|
+
this.stepName = null;
|
|
2916
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2917
|
+
if (this.context.browserObject.context) {
|
|
2918
|
+
await this.context.browserObject.context.tracing.stopChunk({
|
|
2919
|
+
path: path.join(this.context.browserObject.traceFolder, `trace-${this.stepIndex}.zip`),
|
|
2920
|
+
});
|
|
2921
|
+
}
|
|
2691
2922
|
}
|
|
2692
|
-
world.attach(JSON.stringify(properties), { mediaType: "application/json" });
|
|
2693
2923
|
}
|
|
2694
2924
|
}
|
|
2695
2925
|
function createTimedPromise(promise, label) {
|