automation_model 1.0.474-dev → 1.0.474-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 +53 -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/network.d.ts +3 -0
- package/lib/network.js +150 -0
- package/lib/network.js.map +1 -0
- package/lib/stable_browser.d.ts +16 -10
- package/lib/stable_browser.js +448 -291
- 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,14 +10,15 @@ 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 {
|
|
19
|
+
import { randomUUID } from "crypto";
|
|
20
|
+
import { _commandError, _commandFinally, _preCommand, _validateSelectors, _screenshot, _reportToWorld, } from "./command_common.js";
|
|
21
|
+
import { registerDownloadEvent, registerNetworkEvents } from "./network.js";
|
|
21
22
|
const Types = {
|
|
22
23
|
CLICK: "click_element",
|
|
23
24
|
NAVIGATE: "navigate",
|
|
@@ -44,20 +45,28 @@ const Types = {
|
|
|
44
45
|
VERIFY_VISUAL: "verify_visual",
|
|
45
46
|
LOAD_DATA: "load_data",
|
|
46
47
|
SET_INPUT: "set_input",
|
|
48
|
+
WAIT_FOR_TEXT_TO_DISAPPEAR: "wait_for_text_to_disappear",
|
|
49
|
+
VERIFY_ATTRIBUTE: "verify_element_attribute",
|
|
47
50
|
};
|
|
48
51
|
export const apps = {};
|
|
49
52
|
class StableBrowser {
|
|
53
|
+
browser;
|
|
54
|
+
page;
|
|
55
|
+
logger;
|
|
56
|
+
context;
|
|
57
|
+
world;
|
|
58
|
+
project_path = null;
|
|
59
|
+
webLogFile = null;
|
|
60
|
+
networkLogger = null;
|
|
61
|
+
configuration = null;
|
|
62
|
+
appName = "main";
|
|
63
|
+
tags = null;
|
|
50
64
|
constructor(browser, page, logger = null, context = null, world = null) {
|
|
51
65
|
this.browser = browser;
|
|
52
66
|
this.page = page;
|
|
53
67
|
this.logger = logger;
|
|
54
68
|
this.context = context;
|
|
55
69
|
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
70
|
if (!this.logger) {
|
|
62
71
|
this.logger = console;
|
|
63
72
|
}
|
|
@@ -82,11 +91,31 @@ class StableBrowser {
|
|
|
82
91
|
catch (e) {
|
|
83
92
|
this.logger.error("unable to read ai_config.json");
|
|
84
93
|
}
|
|
85
|
-
context.pageLoading = { status: false };
|
|
86
|
-
context.pages = [this.page];
|
|
87
94
|
const logFolder = path.join(this.project_path, "logs", "web");
|
|
88
95
|
this.world = world;
|
|
96
|
+
context.pages = [this.page];
|
|
97
|
+
context.pageLoading = { status: false };
|
|
89
98
|
this.registerEventListeners(this.context);
|
|
99
|
+
registerNetworkEvents(this.world, this, this.context, this.page);
|
|
100
|
+
registerDownloadEvent(this.page, this.world, this.context);
|
|
101
|
+
}
|
|
102
|
+
async scrollPageToLoadLazyElements() {
|
|
103
|
+
let lastHeight = await this.page.evaluate(() => document.body.scrollHeight);
|
|
104
|
+
let retry = 0;
|
|
105
|
+
while (true) {
|
|
106
|
+
await this.page.evaluate(() => window.scrollBy(0, window.innerHeight));
|
|
107
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
108
|
+
let newHeight = await this.page.evaluate(() => document.body.scrollHeight);
|
|
109
|
+
if (newHeight === lastHeight) {
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
lastHeight = newHeight;
|
|
113
|
+
retry++;
|
|
114
|
+
if (retry > 10) {
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
await this.page.evaluate(() => window.scrollTo(0, 0));
|
|
90
119
|
}
|
|
91
120
|
registerEventListeners(context) {
|
|
92
121
|
this.registerConsoleLogListener(this.page, context);
|
|
@@ -95,10 +124,17 @@ class StableBrowser {
|
|
|
95
124
|
context.pageLoading = { status: false };
|
|
96
125
|
}
|
|
97
126
|
context.playContext.on("page", async function (page) {
|
|
127
|
+
if (this.configuration && this.configuration.closePopups === true) {
|
|
128
|
+
console.log("close unexpected popups");
|
|
129
|
+
await page.close();
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
98
132
|
context.pageLoading.status = true;
|
|
99
133
|
this.page = page;
|
|
100
134
|
context.page = page;
|
|
101
135
|
context.pages.push(page);
|
|
136
|
+
registerNetworkEvents(this.world, this, context, this.page);
|
|
137
|
+
registerDownloadEvent(this.page, this.world, context);
|
|
102
138
|
page.on("close", async () => {
|
|
103
139
|
if (this.context && this.context.pages && this.context.pages.length > 1) {
|
|
104
140
|
this.context.pages.pop();
|
|
@@ -128,9 +164,9 @@ class StableBrowser {
|
|
|
128
164
|
if (this.appName === appName) {
|
|
129
165
|
return;
|
|
130
166
|
}
|
|
131
|
-
let
|
|
167
|
+
let navigate = false;
|
|
132
168
|
if (!apps[appName]) {
|
|
133
|
-
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this);
|
|
169
|
+
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this, -1, this.context.reportFolder);
|
|
134
170
|
newContextCreated = true;
|
|
135
171
|
apps[appName] = {
|
|
136
172
|
context: newContext,
|
|
@@ -143,8 +179,7 @@ class StableBrowser {
|
|
|
143
179
|
this._copyContext(apps[appName], this);
|
|
144
180
|
apps[this.appName] = tempContext;
|
|
145
181
|
this.appName = appName;
|
|
146
|
-
if (
|
|
147
|
-
this.registerEventListeners(this.context);
|
|
182
|
+
if (navigate) {
|
|
148
183
|
await this.goto(this.context.environment.baseUrl);
|
|
149
184
|
await this.waitForPageLoad();
|
|
150
185
|
}
|
|
@@ -170,7 +205,6 @@ class StableBrowser {
|
|
|
170
205
|
this.context.webLogger = [];
|
|
171
206
|
}
|
|
172
207
|
page.on("console", async (msg) => {
|
|
173
|
-
var _a;
|
|
174
208
|
const obj = {
|
|
175
209
|
type: msg.type(),
|
|
176
210
|
text: msg.text(),
|
|
@@ -179,7 +213,7 @@ class StableBrowser {
|
|
|
179
213
|
};
|
|
180
214
|
this.context.webLogger.push(obj);
|
|
181
215
|
if (msg.type() === "error") {
|
|
182
|
-
|
|
216
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
|
|
183
217
|
}
|
|
184
218
|
});
|
|
185
219
|
}
|
|
@@ -188,7 +222,6 @@ class StableBrowser {
|
|
|
188
222
|
this.context.networkLogger = [];
|
|
189
223
|
}
|
|
190
224
|
page.on("request", async (data) => {
|
|
191
|
-
var _a;
|
|
192
225
|
const startTime = new Date().getTime();
|
|
193
226
|
try {
|
|
194
227
|
const pageUrl = new URL(page.url());
|
|
@@ -213,10 +246,10 @@ class StableBrowser {
|
|
|
213
246
|
startTime,
|
|
214
247
|
};
|
|
215
248
|
context.networkLogger.push(obj);
|
|
216
|
-
|
|
249
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
|
|
217
250
|
}
|
|
218
251
|
catch (error) {
|
|
219
|
-
console.error("Error in request listener", error);
|
|
252
|
+
// console.error("Error in request listener", error);
|
|
220
253
|
context.networkLogger.push({
|
|
221
254
|
error: "not able to listen",
|
|
222
255
|
message: error.message,
|
|
@@ -305,7 +338,7 @@ class StableBrowser {
|
|
|
305
338
|
locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
|
|
306
339
|
}
|
|
307
340
|
}
|
|
308
|
-
if (locator
|
|
341
|
+
if (locator?.engine) {
|
|
309
342
|
if (locator.engine === "css") {
|
|
310
343
|
locatorReturn = scope.locator(locator.selector);
|
|
311
344
|
}
|
|
@@ -329,7 +362,7 @@ class StableBrowser {
|
|
|
329
362
|
if (css && css.locator) {
|
|
330
363
|
css = css.locator;
|
|
331
364
|
}
|
|
332
|
-
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "
|
|
365
|
+
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*:not(script, style, head)", false, false, _params);
|
|
333
366
|
if (result.elementCount === 0) {
|
|
334
367
|
return;
|
|
335
368
|
}
|
|
@@ -381,7 +414,7 @@ class StableBrowser {
|
|
|
381
414
|
}
|
|
382
415
|
document.collectAllShadowDomElements = collectAllShadowDomElements;
|
|
383
416
|
if (!tag) {
|
|
384
|
-
tag = "
|
|
417
|
+
tag = "*:not(script, style, head)";
|
|
385
418
|
}
|
|
386
419
|
let regexpSearch = document.getRegex(text);
|
|
387
420
|
if (regexpSearch) {
|
|
@@ -463,6 +496,15 @@ class StableBrowser {
|
|
|
463
496
|
}, [text1, tag1, regex1, partial1]);
|
|
464
497
|
}
|
|
465
498
|
async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
|
|
499
|
+
if (!info) {
|
|
500
|
+
info = {};
|
|
501
|
+
}
|
|
502
|
+
if (!info.failCause) {
|
|
503
|
+
info.failCause = {};
|
|
504
|
+
}
|
|
505
|
+
if (!info.log) {
|
|
506
|
+
info.log = "";
|
|
507
|
+
}
|
|
466
508
|
let locatorSearch = selectorHierarchy[index];
|
|
467
509
|
try {
|
|
468
510
|
locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
|
|
@@ -475,13 +517,18 @@ class StableBrowser {
|
|
|
475
517
|
if (locatorSearch.climb && locatorSearch.climb >= 0) {
|
|
476
518
|
let locatorString = await this._locateElmentByTextClimbCss(scope, locatorSearch.text, locatorSearch.climb, locatorSearch.css, _params);
|
|
477
519
|
if (!locatorString) {
|
|
520
|
+
info.failCause.textNotFound = true;
|
|
521
|
+
info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
|
|
478
522
|
return;
|
|
479
523
|
}
|
|
480
524
|
locator = this._getLocator({ css: locatorString }, scope, _params);
|
|
481
525
|
}
|
|
482
526
|
else if (locatorSearch.text) {
|
|
483
|
-
let
|
|
527
|
+
let text = this._fixUsingParams(locatorSearch.text, _params);
|
|
528
|
+
let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
|
|
484
529
|
if (result.elementCount === 0) {
|
|
530
|
+
info.failCause.textNotFound = true;
|
|
531
|
+
info.failCause.lastError = "failed to locate element by text: " + text;
|
|
485
532
|
return;
|
|
486
533
|
}
|
|
487
534
|
locatorSearch.css = "[data-blinq-id='blinq-id-" + result.randomToken + "']";
|
|
@@ -498,10 +545,13 @@ class StableBrowser {
|
|
|
498
545
|
// cssHref = true;
|
|
499
546
|
// }
|
|
500
547
|
let count = await locator.count();
|
|
548
|
+
if (count > 0 && !info.failCause.count) {
|
|
549
|
+
info.failCause.count = count;
|
|
550
|
+
}
|
|
501
551
|
//info.log += "total elements found " + count + "\n";
|
|
502
552
|
//let visibleCount = 0;
|
|
503
553
|
let visibleLocator = null;
|
|
504
|
-
if (locatorSearch.index && locatorSearch.index < count) {
|
|
554
|
+
if (typeof locatorSearch.index === "number" && locatorSearch.index < count) {
|
|
505
555
|
foundLocators.push(locator.nth(locatorSearch.index));
|
|
506
556
|
return;
|
|
507
557
|
}
|
|
@@ -515,6 +565,8 @@ class StableBrowser {
|
|
|
515
565
|
foundLocators.push(locator.nth(j));
|
|
516
566
|
}
|
|
517
567
|
else {
|
|
568
|
+
info.failCause.visible = visible;
|
|
569
|
+
info.failCause.enabled = enabled;
|
|
518
570
|
if (!info.printMessages) {
|
|
519
571
|
info.printMessages = {};
|
|
520
572
|
}
|
|
@@ -526,6 +578,11 @@ class StableBrowser {
|
|
|
526
578
|
}
|
|
527
579
|
}
|
|
528
580
|
async closeUnexpectedPopups(info, _params) {
|
|
581
|
+
if (!info) {
|
|
582
|
+
info = {};
|
|
583
|
+
info.failCause = {};
|
|
584
|
+
info.log = "";
|
|
585
|
+
}
|
|
529
586
|
if (this.configuration.popupHandlers && this.configuration.popupHandlers.length > 0) {
|
|
530
587
|
if (!info) {
|
|
531
588
|
info = {};
|
|
@@ -557,16 +614,31 @@ class StableBrowser {
|
|
|
557
614
|
}
|
|
558
615
|
if (result.foundElements.length > 0) {
|
|
559
616
|
let dialogCloseLocator = result.foundElements[0].locator;
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
617
|
+
try {
|
|
618
|
+
await scope?.evaluate(() => {
|
|
619
|
+
window.__isClosingPopups = true;
|
|
620
|
+
});
|
|
621
|
+
await dialogCloseLocator.click();
|
|
622
|
+
// wait for the dialog to close
|
|
623
|
+
await dialogCloseLocator.waitFor({ state: "hidden" });
|
|
624
|
+
}
|
|
625
|
+
catch (e) {
|
|
626
|
+
}
|
|
627
|
+
finally {
|
|
628
|
+
await scope?.evaluate(() => {
|
|
629
|
+
window.__isClosingPopups = false;
|
|
630
|
+
});
|
|
631
|
+
}
|
|
563
632
|
return { rerun: true };
|
|
564
633
|
}
|
|
565
634
|
}
|
|
566
635
|
}
|
|
567
636
|
return { rerun: false };
|
|
568
637
|
}
|
|
569
|
-
async _locate(selectors, info, _params, timeout
|
|
638
|
+
async _locate(selectors, info, _params, timeout) {
|
|
639
|
+
if (!timeout) {
|
|
640
|
+
timeout = 30000;
|
|
641
|
+
}
|
|
570
642
|
for (let i = 0; i < 3; i++) {
|
|
571
643
|
info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
|
|
572
644
|
for (let j = 0; j < selectors.locators.length; j++) {
|
|
@@ -580,8 +652,14 @@ class StableBrowser {
|
|
|
580
652
|
}
|
|
581
653
|
throw new Error("unable to locate element " + JSON.stringify(selectors));
|
|
582
654
|
}
|
|
583
|
-
async _findFrameScope(selectors, timeout = 30000) {
|
|
584
|
-
|
|
655
|
+
async _findFrameScope(selectors, timeout = 30000, info) {
|
|
656
|
+
if (!info) {
|
|
657
|
+
info = {};
|
|
658
|
+
info.failCause = {};
|
|
659
|
+
info.log = "";
|
|
660
|
+
}
|
|
661
|
+
let startTime = Date.now();
|
|
662
|
+
let scope = this.page;
|
|
585
663
|
if (selectors.frame) {
|
|
586
664
|
return selectors.frame;
|
|
587
665
|
}
|
|
@@ -633,7 +711,9 @@ class StableBrowser {
|
|
|
633
711
|
}
|
|
634
712
|
if (!scope) {
|
|
635
713
|
info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
|
|
636
|
-
if (
|
|
714
|
+
if (Date.now() - startTime > timeout) {
|
|
715
|
+
info.failCause.iframeNotFound = true;
|
|
716
|
+
info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
|
|
637
717
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
638
718
|
}
|
|
639
719
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -648,20 +728,26 @@ class StableBrowser {
|
|
|
648
728
|
}
|
|
649
729
|
return scope;
|
|
650
730
|
}
|
|
651
|
-
async _getDocumentBody(selectors, timeout = 30000) {
|
|
652
|
-
let scope = await this._findFrameScope(selectors, timeout);
|
|
731
|
+
async _getDocumentBody(selectors, timeout = 30000, info) {
|
|
732
|
+
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
653
733
|
return scope.evaluate(() => {
|
|
654
734
|
var bodyContent = document.body.innerHTML;
|
|
655
735
|
return bodyContent;
|
|
656
736
|
});
|
|
657
737
|
}
|
|
658
738
|
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
739
|
+
if (!info) {
|
|
740
|
+
info = {};
|
|
741
|
+
info.failCause = {};
|
|
742
|
+
info.log = "";
|
|
743
|
+
}
|
|
659
744
|
let highPriorityTimeout = 5000;
|
|
660
745
|
let visibleOnlyTimeout = 6000;
|
|
661
|
-
let startTime =
|
|
746
|
+
let startTime = Date.now();
|
|
662
747
|
let locatorsCount = 0;
|
|
748
|
+
let lazy_scroll = false;
|
|
663
749
|
//let arrayMode = Array.isArray(selectors);
|
|
664
|
-
let scope = await this._findFrameScope(selectors, timeout);
|
|
750
|
+
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
665
751
|
let selectorsLocators = null;
|
|
666
752
|
selectorsLocators = selectors.locators;
|
|
667
753
|
// group selectors by priority
|
|
@@ -748,14 +834,18 @@ class StableBrowser {
|
|
|
748
834
|
return maxCountElement.locator;
|
|
749
835
|
}
|
|
750
836
|
}
|
|
751
|
-
if (
|
|
837
|
+
if (Date.now() - startTime > timeout) {
|
|
752
838
|
break;
|
|
753
839
|
}
|
|
754
|
-
if (
|
|
840
|
+
if (Date.now() - startTime > highPriorityTimeout) {
|
|
755
841
|
info.log += "high priority timeout, will try all elements" + "\n";
|
|
756
842
|
highPriorityOnly = false;
|
|
843
|
+
if (this.configuration && this.configuration.load_all_lazy === true && !lazy_scroll) {
|
|
844
|
+
lazy_scroll = true;
|
|
845
|
+
await this.scrollPageToLoadLazyElements();
|
|
846
|
+
}
|
|
757
847
|
}
|
|
758
|
-
if (
|
|
848
|
+
if (Date.now() - startTime > visibleOnlyTimeout) {
|
|
759
849
|
info.log += "visible only timeout, will try all elements" + "\n";
|
|
760
850
|
visibleOnly = false;
|
|
761
851
|
}
|
|
@@ -763,6 +853,8 @@ class StableBrowser {
|
|
|
763
853
|
}
|
|
764
854
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
765
855
|
info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
|
|
856
|
+
info.failCause.locatorNotFound = true;
|
|
857
|
+
info.failCause.lastError = "failed to locate unique element";
|
|
766
858
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
767
859
|
}
|
|
768
860
|
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
@@ -794,6 +886,9 @@ class StableBrowser {
|
|
|
794
886
|
});
|
|
795
887
|
result.locatorIndex = i;
|
|
796
888
|
}
|
|
889
|
+
if (foundLocators.length > 1) {
|
|
890
|
+
info.failCause.foundMultiple = true;
|
|
891
|
+
}
|
|
797
892
|
}
|
|
798
893
|
return result;
|
|
799
894
|
}
|
|
@@ -806,12 +901,12 @@ class StableBrowser {
|
|
|
806
901
|
while (true) {
|
|
807
902
|
try {
|
|
808
903
|
const result = await locate_element(this.context, elementDescription, "click");
|
|
809
|
-
if (
|
|
904
|
+
if (result?.elementNumber >= 0) {
|
|
810
905
|
const selectors = {
|
|
811
|
-
frame: result
|
|
906
|
+
frame: result?.frame,
|
|
812
907
|
locators: [
|
|
813
908
|
{
|
|
814
|
-
css: result
|
|
909
|
+
css: result?.css,
|
|
815
910
|
},
|
|
816
911
|
],
|
|
817
912
|
};
|
|
@@ -820,8 +915,9 @@ class StableBrowser {
|
|
|
820
915
|
}
|
|
821
916
|
}
|
|
822
917
|
catch (e) {
|
|
823
|
-
if (
|
|
824
|
-
throw e;
|
|
918
|
+
if (Date.now() - startTime > timeout) {
|
|
919
|
+
// throw e;
|
|
920
|
+
await _commandError({ text: "simpleClick", operation: "simpleClick", elementDescription, info: {} }, e, this);
|
|
825
921
|
}
|
|
826
922
|
}
|
|
827
923
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
@@ -836,12 +932,12 @@ class StableBrowser {
|
|
|
836
932
|
while (true) {
|
|
837
933
|
try {
|
|
838
934
|
const result = await locate_element(this.context, elementDescription, "fill", value);
|
|
839
|
-
if (
|
|
935
|
+
if (result?.elementNumber >= 0) {
|
|
840
936
|
const selectors = {
|
|
841
|
-
frame: result
|
|
937
|
+
frame: result?.frame,
|
|
842
938
|
locators: [
|
|
843
939
|
{
|
|
844
|
-
css: result
|
|
940
|
+
css: result?.css,
|
|
845
941
|
},
|
|
846
942
|
],
|
|
847
943
|
};
|
|
@@ -850,8 +946,9 @@ class StableBrowser {
|
|
|
850
946
|
}
|
|
851
947
|
}
|
|
852
948
|
catch (e) {
|
|
853
|
-
if (
|
|
854
|
-
throw e;
|
|
949
|
+
if (Date.now() - startTime > timeout) {
|
|
950
|
+
// throw e;
|
|
951
|
+
await _commandError({ text: "simpleClickType", operation: "simpleClickType", value, elementDescription, info: {} }, e, this);
|
|
855
952
|
}
|
|
856
953
|
}
|
|
857
954
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
@@ -875,13 +972,13 @@ class StableBrowser {
|
|
|
875
972
|
}
|
|
876
973
|
try {
|
|
877
974
|
await state.element.click();
|
|
878
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
975
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
879
976
|
}
|
|
880
977
|
catch (e) {
|
|
881
978
|
// await this.closeUnexpectedPopups();
|
|
882
979
|
state.element = await this._locate(selectors, state.info, _params);
|
|
883
980
|
await state.element.dispatchEvent("click");
|
|
884
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
981
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
885
982
|
}
|
|
886
983
|
await this.waitForPageLoad();
|
|
887
984
|
return state.info;
|
|
@@ -905,7 +1002,7 @@ class StableBrowser {
|
|
|
905
1002
|
log: "***** check " + selectors.element_name + " *****\n",
|
|
906
1003
|
};
|
|
907
1004
|
try {
|
|
908
|
-
_preCommand(state, this);
|
|
1005
|
+
await _preCommand(state, this);
|
|
909
1006
|
state.info.checked = checked;
|
|
910
1007
|
// let element = await this._locate(selectors, info, _params);
|
|
911
1008
|
// ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
@@ -920,8 +1017,8 @@ class StableBrowser {
|
|
|
920
1017
|
}
|
|
921
1018
|
else {
|
|
922
1019
|
//await this.closeUnexpectedPopups();
|
|
923
|
-
info.log += "setCheck failed, will try again" + "\n";
|
|
924
|
-
state.element = await this._locate(selectors, info, _params);
|
|
1020
|
+
state.info.log += "setCheck failed, will try again" + "\n";
|
|
1021
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
925
1022
|
await state.element.setChecked(checked, { timeout: 5000, force: true });
|
|
926
1023
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
927
1024
|
}
|
|
@@ -948,7 +1045,7 @@ class StableBrowser {
|
|
|
948
1045
|
log: "***** hover " + selectors.element_name + " *****\n",
|
|
949
1046
|
};
|
|
950
1047
|
try {
|
|
951
|
-
_preCommand(state, this);
|
|
1048
|
+
await _preCommand(state, this);
|
|
952
1049
|
try {
|
|
953
1050
|
await state.element.hover();
|
|
954
1051
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -979,6 +1076,7 @@ class StableBrowser {
|
|
|
979
1076
|
_params,
|
|
980
1077
|
options,
|
|
981
1078
|
world,
|
|
1079
|
+
value: values.toString(),
|
|
982
1080
|
type: Types.SELECT,
|
|
983
1081
|
text: `Select option: ${values}`,
|
|
984
1082
|
operation: "selectOption",
|
|
@@ -1086,33 +1184,30 @@ class StableBrowser {
|
|
|
1086
1184
|
}
|
|
1087
1185
|
}
|
|
1088
1186
|
async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1187
|
+
const state = {
|
|
1188
|
+
selectors,
|
|
1189
|
+
_params,
|
|
1190
|
+
value: await this._replaceWithLocalData(value, this),
|
|
1191
|
+
options,
|
|
1192
|
+
world,
|
|
1193
|
+
type: Types.SET_DATE_TIME,
|
|
1194
|
+
text: `Set date time value: ${value}`,
|
|
1195
|
+
operation: "setDateTime",
|
|
1196
|
+
log: "***** set date time value " + selectors.element_name + " *****\n",
|
|
1197
|
+
throwError: false,
|
|
1198
|
+
};
|
|
1099
1199
|
try {
|
|
1100
|
-
|
|
1101
|
-
let element = await this._locate(selectors, info, _params);
|
|
1102
|
-
//insert red border around the element
|
|
1103
|
-
await this.scrollIfNeeded(element, info);
|
|
1104
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1105
|
-
await this._highlightElements(element);
|
|
1200
|
+
await _preCommand(state, this);
|
|
1106
1201
|
try {
|
|
1107
|
-
await element.click();
|
|
1202
|
+
await state.element.click();
|
|
1108
1203
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1109
1204
|
if (format) {
|
|
1110
|
-
value = dayjs(value).format(format);
|
|
1111
|
-
await element.fill(value);
|
|
1205
|
+
state.value = dayjs(state.value).format(format);
|
|
1206
|
+
await state.element.fill(state.value);
|
|
1112
1207
|
}
|
|
1113
1208
|
else {
|
|
1114
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1115
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1209
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1210
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1116
1211
|
el.value = ""; // clear input
|
|
1117
1212
|
el.value = dateTimeValue;
|
|
1118
1213
|
}, dateTimeValue);
|
|
@@ -1125,20 +1220,19 @@ class StableBrowser {
|
|
|
1125
1220
|
}
|
|
1126
1221
|
catch (err) {
|
|
1127
1222
|
//await this.closeUnexpectedPopups();
|
|
1128
|
-
this.logger.error("setting date time input failed " + JSON.stringify(info));
|
|
1223
|
+
this.logger.error("setting date time input failed " + JSON.stringify(state.info));
|
|
1129
1224
|
this.logger.info("Trying again");
|
|
1130
|
-
|
|
1131
|
-
info.
|
|
1132
|
-
Object.assign(err, { info: info });
|
|
1225
|
+
await _screenshot(state, this);
|
|
1226
|
+
Object.assign(err, { info: state.info });
|
|
1133
1227
|
await element.click();
|
|
1134
1228
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1135
1229
|
if (format) {
|
|
1136
|
-
value = dayjs(value).format(format);
|
|
1137
|
-
await element.fill(value);
|
|
1230
|
+
state.value = dayjs(state.value).format(format);
|
|
1231
|
+
await state.element.fill(state.value);
|
|
1138
1232
|
}
|
|
1139
1233
|
else {
|
|
1140
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1141
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1234
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1235
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1142
1236
|
el.value = ""; // clear input
|
|
1143
1237
|
el.value = dateTimeValue;
|
|
1144
1238
|
}, dateTimeValue);
|
|
@@ -1151,46 +1245,27 @@ class StableBrowser {
|
|
|
1151
1245
|
}
|
|
1152
1246
|
}
|
|
1153
1247
|
catch (e) {
|
|
1154
|
-
|
|
1155
|
-
throw e;
|
|
1248
|
+
await _commandError(state, e, this);
|
|
1156
1249
|
}
|
|
1157
1250
|
finally {
|
|
1158
|
-
|
|
1159
|
-
this._reportToWorld(world, {
|
|
1160
|
-
element_name: selectors.element_name,
|
|
1161
|
-
type: Types.SET_DATE_TIME,
|
|
1162
|
-
screenshotId,
|
|
1163
|
-
value: value,
|
|
1164
|
-
text: `setDateTime input with value: ${value}`,
|
|
1165
|
-
result: error
|
|
1166
|
-
? {
|
|
1167
|
-
status: "FAILED",
|
|
1168
|
-
startTime,
|
|
1169
|
-
endTime,
|
|
1170
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1171
|
-
}
|
|
1172
|
-
: {
|
|
1173
|
-
status: "PASSED",
|
|
1174
|
-
startTime,
|
|
1175
|
-
endTime,
|
|
1176
|
-
},
|
|
1177
|
-
info: info,
|
|
1178
|
-
});
|
|
1251
|
+
_commandFinally(state, this);
|
|
1179
1252
|
}
|
|
1180
1253
|
}
|
|
1181
1254
|
async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
|
|
1255
|
+
_value = unEscapeString(_value);
|
|
1256
|
+
const newValue = await this._replaceWithLocalData(_value, world);
|
|
1182
1257
|
const state = {
|
|
1183
1258
|
selectors,
|
|
1184
1259
|
_params,
|
|
1185
|
-
value:
|
|
1260
|
+
value: newValue,
|
|
1261
|
+
originalValue: _value,
|
|
1186
1262
|
options,
|
|
1187
1263
|
world,
|
|
1188
1264
|
type: Types.FILL,
|
|
1189
1265
|
text: `Click type input with value: ${_value}`,
|
|
1190
1266
|
operation: "clickType",
|
|
1191
|
-
log: "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n",
|
|
1267
|
+
log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
|
|
1192
1268
|
};
|
|
1193
|
-
const newValue = await this._replaceWithLocalData(state.value, world);
|
|
1194
1269
|
if (newValue !== _value) {
|
|
1195
1270
|
//this.logger.info(_value + "=" + newValue);
|
|
1196
1271
|
_value = newValue;
|
|
@@ -1202,7 +1277,7 @@ class StableBrowser {
|
|
|
1202
1277
|
try {
|
|
1203
1278
|
let currentValue = await state.element.inputValue();
|
|
1204
1279
|
if (currentValue) {
|
|
1205
|
-
await element.fill("");
|
|
1280
|
+
await state.element.fill("");
|
|
1206
1281
|
}
|
|
1207
1282
|
}
|
|
1208
1283
|
catch (e) {
|
|
@@ -1349,7 +1424,6 @@ class StableBrowser {
|
|
|
1349
1424
|
}
|
|
1350
1425
|
}
|
|
1351
1426
|
async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
|
|
1352
|
-
var _a;
|
|
1353
1427
|
if (!pattern) {
|
|
1354
1428
|
throw new Error("pattern is null");
|
|
1355
1429
|
}
|
|
@@ -1360,7 +1434,7 @@ class StableBrowser {
|
|
|
1360
1434
|
selectors,
|
|
1361
1435
|
_params,
|
|
1362
1436
|
pattern,
|
|
1363
|
-
value:
|
|
1437
|
+
value: pattern,
|
|
1364
1438
|
options,
|
|
1365
1439
|
world,
|
|
1366
1440
|
locate: false,
|
|
@@ -1389,14 +1463,14 @@ class StableBrowser {
|
|
|
1389
1463
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1390
1464
|
pattern = pattern.replace("{text}", escapedText);
|
|
1391
1465
|
let regex = new RegExp(pattern, "im");
|
|
1392
|
-
if (!regex.test(foundObj
|
|
1393
|
-
state.info.foundText = foundObj
|
|
1466
|
+
if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
|
|
1467
|
+
state.info.foundText = foundObj?.text;
|
|
1394
1468
|
throw new Error("element doesn't contain text " + text);
|
|
1395
1469
|
}
|
|
1396
1470
|
return state.info;
|
|
1397
1471
|
}
|
|
1398
1472
|
catch (e) {
|
|
1399
|
-
this.logger.error("found text " +
|
|
1473
|
+
this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
|
|
1400
1474
|
await _commandError(state, e, this);
|
|
1401
1475
|
}
|
|
1402
1476
|
finally {
|
|
@@ -1404,7 +1478,6 @@ class StableBrowser {
|
|
|
1404
1478
|
}
|
|
1405
1479
|
}
|
|
1406
1480
|
async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
|
|
1407
|
-
var _a, _b, _c;
|
|
1408
1481
|
const state = {
|
|
1409
1482
|
selectors,
|
|
1410
1483
|
_params,
|
|
@@ -1431,6 +1504,7 @@ class StableBrowser {
|
|
|
1431
1504
|
}
|
|
1432
1505
|
let foundObj = null;
|
|
1433
1506
|
try {
|
|
1507
|
+
await _preCommand(state, this);
|
|
1434
1508
|
foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
|
|
1435
1509
|
if (foundObj && foundObj.element) {
|
|
1436
1510
|
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
@@ -1440,8 +1514,8 @@ class StableBrowser {
|
|
|
1440
1514
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1441
1515
|
if (dateAlternatives.date) {
|
|
1442
1516
|
for (let i = 0; i < dateAlternatives.dates.length; i++) {
|
|
1443
|
-
if (
|
|
1444
|
-
|
|
1517
|
+
if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
|
|
1518
|
+
foundObj?.value?.includes(dateAlternatives.dates[i])) {
|
|
1445
1519
|
return state.info;
|
|
1446
1520
|
}
|
|
1447
1521
|
}
|
|
@@ -1449,16 +1523,16 @@ class StableBrowser {
|
|
|
1449
1523
|
}
|
|
1450
1524
|
else if (numberAlternatives.number) {
|
|
1451
1525
|
for (let i = 0; i < numberAlternatives.numbers.length; i++) {
|
|
1452
|
-
if (
|
|
1453
|
-
|
|
1526
|
+
if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
|
|
1527
|
+
foundObj?.value?.includes(numberAlternatives.numbers[i])) {
|
|
1454
1528
|
return state.info;
|
|
1455
1529
|
}
|
|
1456
1530
|
}
|
|
1457
1531
|
throw new Error("element doesn't contain text " + text);
|
|
1458
1532
|
}
|
|
1459
|
-
else if (!
|
|
1460
|
-
state.info.foundText = foundObj
|
|
1461
|
-
state.info.value = foundObj
|
|
1533
|
+
else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
|
|
1534
|
+
state.info.foundText = foundObj?.text;
|
|
1535
|
+
state.info.value = foundObj?.value;
|
|
1462
1536
|
throw new Error("element doesn't contain text " + text);
|
|
1463
1537
|
}
|
|
1464
1538
|
return state.info;
|
|
@@ -1649,11 +1723,9 @@ class StableBrowser {
|
|
|
1649
1723
|
if (!fs.existsSync(world.screenshotPath)) {
|
|
1650
1724
|
fs.mkdirSync(world.screenshotPath, { recursive: true });
|
|
1651
1725
|
}
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
}
|
|
1656
|
-
const screenshotPath = path.join(world.screenshotPath, nextIndex + ".png");
|
|
1726
|
+
// to make sure the path doesn't start with -
|
|
1727
|
+
const uuidStr = "id_" + randomUUID();
|
|
1728
|
+
const screenshotPath = path.join(world.screenshotPath, uuidStr + ".png");
|
|
1657
1729
|
try {
|
|
1658
1730
|
await this.takeScreenshot(screenshotPath);
|
|
1659
1731
|
// let buffer = await this.page.screenshot({ timeout: 4000 });
|
|
@@ -1667,7 +1739,7 @@ class StableBrowser {
|
|
|
1667
1739
|
catch (e) {
|
|
1668
1740
|
this.logger.info("unable to take screenshot, ignored");
|
|
1669
1741
|
}
|
|
1670
|
-
result.screenshotId =
|
|
1742
|
+
result.screenshotId = uuidStr;
|
|
1671
1743
|
result.screenshotPath = screenshotPath;
|
|
1672
1744
|
if (info && info.box) {
|
|
1673
1745
|
await drawRectangle(screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
|
|
@@ -1765,74 +1837,101 @@ class StableBrowser {
|
|
|
1765
1837
|
}
|
|
1766
1838
|
}
|
|
1767
1839
|
async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1840
|
+
const state = {
|
|
1841
|
+
selectors,
|
|
1842
|
+
_params,
|
|
1843
|
+
attribute,
|
|
1844
|
+
variable,
|
|
1845
|
+
options,
|
|
1846
|
+
world,
|
|
1847
|
+
type: Types.EXTRACT,
|
|
1848
|
+
text: `Extract attribute from element`,
|
|
1849
|
+
operation: "extractAttribute",
|
|
1850
|
+
log: "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1851
|
+
};
|
|
1773
1852
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1774
|
-
const info = {};
|
|
1775
|
-
info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
|
|
1776
|
-
info.operation = "extract";
|
|
1777
|
-
info.selectors = selectors;
|
|
1778
1853
|
try {
|
|
1779
|
-
|
|
1780
|
-
await this._highlightElements(element);
|
|
1781
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1854
|
+
await _preCommand(state, this);
|
|
1782
1855
|
switch (attribute) {
|
|
1783
1856
|
case "inner_text":
|
|
1784
|
-
|
|
1857
|
+
state.value = await state.element.innerText();
|
|
1785
1858
|
break;
|
|
1786
1859
|
case "href":
|
|
1787
|
-
|
|
1860
|
+
state.value = await state.element.getAttribute("href");
|
|
1788
1861
|
break;
|
|
1789
1862
|
case "value":
|
|
1790
|
-
|
|
1863
|
+
state.value = await state.element.inputValue();
|
|
1791
1864
|
break;
|
|
1792
1865
|
default:
|
|
1793
|
-
|
|
1866
|
+
state.value = await state.element.getAttribute(attribute);
|
|
1794
1867
|
break;
|
|
1795
1868
|
}
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1869
|
+
state.info.value = state.value;
|
|
1870
|
+
this.setTestData({ [variable]: state.value }, world);
|
|
1871
|
+
this.logger.info("set test data: " + variable + "=" + state.value);
|
|
1872
|
+
return state.info;
|
|
1873
|
+
}
|
|
1874
|
+
catch (e) {
|
|
1875
|
+
await _commandError(state, e, this);
|
|
1876
|
+
}
|
|
1877
|
+
finally {
|
|
1878
|
+
_commandFinally(state, this);
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
async verifyAttribute(selectors, attribute, value, _params = null, options = {}, world = null) {
|
|
1882
|
+
const state = {
|
|
1883
|
+
selectors,
|
|
1884
|
+
_params,
|
|
1885
|
+
attribute,
|
|
1886
|
+
value,
|
|
1887
|
+
options,
|
|
1888
|
+
world,
|
|
1889
|
+
type: Types.VERIFY_ATTRIBUTE,
|
|
1890
|
+
text: `Verify element attribute`,
|
|
1891
|
+
operation: "verifyAttribute",
|
|
1892
|
+
log: "***** verify attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1893
|
+
};
|
|
1894
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1895
|
+
let val;
|
|
1896
|
+
try {
|
|
1897
|
+
await _preCommand(state, this);
|
|
1898
|
+
switch (attribute) {
|
|
1899
|
+
case "innerText":
|
|
1900
|
+
val = String(await state.element.innerText());
|
|
1901
|
+
break;
|
|
1902
|
+
case "value":
|
|
1903
|
+
val = String(await state.element.inputValue());
|
|
1904
|
+
break;
|
|
1905
|
+
case "checked":
|
|
1906
|
+
val = String(await state.element.isChecked());
|
|
1907
|
+
break;
|
|
1908
|
+
case "disabled":
|
|
1909
|
+
val = String(await state.element.isDisabled());
|
|
1910
|
+
break;
|
|
1911
|
+
default:
|
|
1912
|
+
val = String(await state.element.getAttribute(attribute));
|
|
1913
|
+
break;
|
|
1799
1914
|
}
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1915
|
+
let regex;
|
|
1916
|
+
if (value.startsWith("/") && value.endsWith("/")) {
|
|
1917
|
+
const patternBody = value.slice(1, -1);
|
|
1918
|
+
regex = new RegExp(patternBody, "g");
|
|
1919
|
+
}
|
|
1920
|
+
else {
|
|
1921
|
+
const escapedPattern = value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1922
|
+
regex = new RegExp(escapedPattern, "g");
|
|
1923
|
+
}
|
|
1924
|
+
if (!val.match(regex)) {
|
|
1925
|
+
throw new Error(`The ${attribute} attribute has a value of "${val}", but the expected value is "${value}"`);
|
|
1926
|
+
}
|
|
1927
|
+
state.info.value = val;
|
|
1928
|
+
return state.info;
|
|
1803
1929
|
}
|
|
1804
1930
|
catch (e) {
|
|
1805
|
-
|
|
1806
|
-
this.logger.error("extract failed " + info.log);
|
|
1807
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1808
|
-
info.screenshotPath = screenshotPath;
|
|
1809
|
-
Object.assign(e, { info: info });
|
|
1810
|
-
error = e;
|
|
1811
|
-
throw e;
|
|
1931
|
+
await _commandError(state, e, this);
|
|
1812
1932
|
}
|
|
1813
1933
|
finally {
|
|
1814
|
-
|
|
1815
|
-
this._reportToWorld(world, {
|
|
1816
|
-
element_name: selectors.element_name,
|
|
1817
|
-
type: Types.EXTRACT_ATTRIBUTE,
|
|
1818
|
-
variable: variable,
|
|
1819
|
-
value: info.value,
|
|
1820
|
-
text: "Extract attribute from element",
|
|
1821
|
-
screenshotId,
|
|
1822
|
-
result: error
|
|
1823
|
-
? {
|
|
1824
|
-
status: "FAILED",
|
|
1825
|
-
startTime,
|
|
1826
|
-
endTime,
|
|
1827
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1828
|
-
}
|
|
1829
|
-
: {
|
|
1830
|
-
status: "PASSED",
|
|
1831
|
-
startTime,
|
|
1832
|
-
endTime,
|
|
1833
|
-
},
|
|
1834
|
-
info: info,
|
|
1835
|
-
});
|
|
1934
|
+
_commandFinally(state, this);
|
|
1836
1935
|
}
|
|
1837
1936
|
}
|
|
1838
1937
|
async extractEmailData(emailAddress, options, world) {
|
|
@@ -1909,7 +2008,8 @@ class StableBrowser {
|
|
|
1909
2008
|
catch (e) {
|
|
1910
2009
|
errorCount++;
|
|
1911
2010
|
if (errorCount > 3) {
|
|
1912
|
-
throw e;
|
|
2011
|
+
// throw e;
|
|
2012
|
+
await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
|
|
1913
2013
|
}
|
|
1914
2014
|
// ignore
|
|
1915
2015
|
}
|
|
@@ -2020,11 +2120,12 @@ class StableBrowser {
|
|
|
2020
2120
|
info.screenshotPath = screenshotPath;
|
|
2021
2121
|
Object.assign(e, { info: info });
|
|
2022
2122
|
error = e;
|
|
2023
|
-
throw e;
|
|
2123
|
+
// throw e;
|
|
2124
|
+
await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
|
|
2024
2125
|
}
|
|
2025
2126
|
finally {
|
|
2026
2127
|
const endTime = Date.now();
|
|
2027
|
-
|
|
2128
|
+
_reportToWorld(world, {
|
|
2028
2129
|
type: Types.VERIFY_PAGE_PATH,
|
|
2029
2130
|
text: "Verify page path",
|
|
2030
2131
|
screenshotId,
|
|
@@ -2033,7 +2134,7 @@ class StableBrowser {
|
|
|
2033
2134
|
status: "FAILED",
|
|
2034
2135
|
startTime,
|
|
2035
2136
|
endTime,
|
|
2036
|
-
message: error
|
|
2137
|
+
message: error?.message,
|
|
2037
2138
|
}
|
|
2038
2139
|
: {
|
|
2039
2140
|
status: "PASSED",
|
|
@@ -2046,52 +2147,58 @@ class StableBrowser {
|
|
|
2046
2147
|
}
|
|
2047
2148
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2048
2149
|
text = unEscapeString(text);
|
|
2049
|
-
const
|
|
2150
|
+
const state = {
|
|
2151
|
+
text_search: text,
|
|
2152
|
+
options,
|
|
2153
|
+
world,
|
|
2154
|
+
locate: false,
|
|
2155
|
+
scroll: false,
|
|
2156
|
+
highlight: false,
|
|
2157
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
2158
|
+
text: `Verify text exists in page`,
|
|
2159
|
+
operation: "verifyTextExistInPage",
|
|
2160
|
+
log: "***** verify text " + text + " exists in page *****\n",
|
|
2161
|
+
};
|
|
2050
2162
|
const timeout = this._getLoadTimeout(options);
|
|
2051
|
-
let error = null;
|
|
2052
|
-
let screenshotId = null;
|
|
2053
|
-
let screenshotPath = null;
|
|
2054
2163
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2055
|
-
const info = {};
|
|
2056
|
-
info.log = "***** verify text " + text + " exists in page *****\n";
|
|
2057
|
-
info.operation = "verifyTextExistInPage";
|
|
2058
2164
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
2059
2165
|
if (newValue !== text) {
|
|
2060
2166
|
this.logger.info(text + "=" + newValue);
|
|
2061
2167
|
text = newValue;
|
|
2062
2168
|
}
|
|
2063
|
-
info.text = text;
|
|
2064
2169
|
let dateAlternatives = findDateAlternatives(text);
|
|
2065
2170
|
let numberAlternatives = findNumberAlternatives(text);
|
|
2066
2171
|
try {
|
|
2172
|
+
await _preCommand(state, this);
|
|
2173
|
+
state.info.text = text;
|
|
2067
2174
|
while (true) {
|
|
2068
2175
|
const frames = this.page.frames();
|
|
2069
2176
|
let results = [];
|
|
2070
2177
|
for (let i = 0; i < frames.length; i++) {
|
|
2071
2178
|
if (dateAlternatives.date) {
|
|
2072
2179
|
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2073
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "
|
|
2180
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2074
2181
|
result.frame = frames[i];
|
|
2075
2182
|
results.push(result);
|
|
2076
2183
|
}
|
|
2077
2184
|
}
|
|
2078
2185
|
else if (numberAlternatives.number) {
|
|
2079
2186
|
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2080
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "
|
|
2187
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2081
2188
|
result.frame = frames[i];
|
|
2082
2189
|
results.push(result);
|
|
2083
2190
|
}
|
|
2084
2191
|
}
|
|
2085
2192
|
else {
|
|
2086
|
-
const result = await this._locateElementByText(frames[i], text, "
|
|
2193
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2087
2194
|
result.frame = frames[i];
|
|
2088
2195
|
results.push(result);
|
|
2089
2196
|
}
|
|
2090
2197
|
}
|
|
2091
|
-
info.results = results;
|
|
2198
|
+
state.info.results = results;
|
|
2092
2199
|
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2093
2200
|
if (resultWithElementsFound.length === 0) {
|
|
2094
|
-
if (Date.now() - startTime > timeout) {
|
|
2201
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2095
2202
|
throw new Error(`Text ${text} not found in page`);
|
|
2096
2203
|
}
|
|
2097
2204
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -2103,44 +2210,89 @@ class StableBrowser {
|
|
|
2103
2210
|
await this._highlightElements(frame, dataAttribute);
|
|
2104
2211
|
const element = await frame.$(dataAttribute);
|
|
2105
2212
|
if (element) {
|
|
2106
|
-
await this.scrollIfNeeded(element, info);
|
|
2213
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2107
2214
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2108
2215
|
}
|
|
2109
2216
|
}
|
|
2110
|
-
|
|
2111
|
-
return info;
|
|
2217
|
+
await _screenshot(state, this);
|
|
2218
|
+
return state.info;
|
|
2112
2219
|
}
|
|
2113
2220
|
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2114
2221
|
}
|
|
2115
2222
|
catch (e) {
|
|
2116
|
-
|
|
2117
|
-
this.logger.error("verify text exist in page failed " + info.log);
|
|
2118
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2119
|
-
info.screenshotPath = screenshotPath;
|
|
2120
|
-
Object.assign(e, { info: info });
|
|
2121
|
-
error = e;
|
|
2122
|
-
throw e;
|
|
2223
|
+
await _commandError(state, e, this);
|
|
2123
2224
|
}
|
|
2124
2225
|
finally {
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2226
|
+
_commandFinally(state, this);
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
async waitForTextToDisappear(text, options = {}, world = null) {
|
|
2230
|
+
text = unEscapeString(text);
|
|
2231
|
+
const state = {
|
|
2232
|
+
text_search: text,
|
|
2233
|
+
options,
|
|
2234
|
+
world,
|
|
2235
|
+
locate: false,
|
|
2236
|
+
scroll: false,
|
|
2237
|
+
highlight: false,
|
|
2238
|
+
type: Types.WAIT_FOR_TEXT_TO_DISAPPEAR,
|
|
2239
|
+
text: `Verify text does not exist in page`,
|
|
2240
|
+
operation: "verifyTextNotExistInPage",
|
|
2241
|
+
log: "***** verify text " + text + " does not exist in page *****\n",
|
|
2242
|
+
};
|
|
2243
|
+
const timeout = this._getLoadTimeout(options);
|
|
2244
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2245
|
+
const newValue = await this._replaceWithLocalData(text, world);
|
|
2246
|
+
if (newValue !== text) {
|
|
2247
|
+
this.logger.info(text + "=" + newValue);
|
|
2248
|
+
text = newValue;
|
|
2249
|
+
}
|
|
2250
|
+
let dateAlternatives = findDateAlternatives(text);
|
|
2251
|
+
let numberAlternatives = findNumberAlternatives(text);
|
|
2252
|
+
try {
|
|
2253
|
+
await _preCommand(state, this);
|
|
2254
|
+
state.info.text = text;
|
|
2255
|
+
while (true) {
|
|
2256
|
+
const frames = this.page.frames();
|
|
2257
|
+
let results = [];
|
|
2258
|
+
for (let i = 0; i < frames.length; i++) {
|
|
2259
|
+
if (dateAlternatives.date) {
|
|
2260
|
+
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2261
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2262
|
+
result.frame = frames[i];
|
|
2263
|
+
results.push(result);
|
|
2264
|
+
}
|
|
2136
2265
|
}
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2266
|
+
else if (numberAlternatives.number) {
|
|
2267
|
+
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2268
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2269
|
+
result.frame = frames[i];
|
|
2270
|
+
results.push(result);
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
else {
|
|
2274
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2275
|
+
result.frame = frames[i];
|
|
2276
|
+
results.push(result);
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
state.info.results = results;
|
|
2280
|
+
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2281
|
+
if (resultWithElementsFound.length === 0) {
|
|
2282
|
+
await _screenshot(state, this);
|
|
2283
|
+
return state.info;
|
|
2284
|
+
}
|
|
2285
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2286
|
+
throw new Error(`Text ${text} found in page`);
|
|
2287
|
+
}
|
|
2288
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
catch (e) {
|
|
2292
|
+
await _commandError(state, e, this);
|
|
2293
|
+
}
|
|
2294
|
+
finally {
|
|
2295
|
+
_commandFinally(state, this);
|
|
2144
2296
|
}
|
|
2145
2297
|
}
|
|
2146
2298
|
_getServerUrl() {
|
|
@@ -2203,11 +2355,12 @@ class StableBrowser {
|
|
|
2203
2355
|
info.screenshotPath = screenshotPath;
|
|
2204
2356
|
Object.assign(e, { info: info });
|
|
2205
2357
|
error = e;
|
|
2206
|
-
throw e;
|
|
2358
|
+
// throw e;
|
|
2359
|
+
await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
|
|
2207
2360
|
}
|
|
2208
2361
|
finally {
|
|
2209
2362
|
const endTime = Date.now();
|
|
2210
|
-
|
|
2363
|
+
_reportToWorld(world, {
|
|
2211
2364
|
type: Types.VERIFY_VISUAL,
|
|
2212
2365
|
text: "Visual verification",
|
|
2213
2366
|
screenshotId,
|
|
@@ -2216,7 +2369,7 @@ class StableBrowser {
|
|
|
2216
2369
|
status: "FAILED",
|
|
2217
2370
|
startTime,
|
|
2218
2371
|
endTime,
|
|
2219
|
-
message: error
|
|
2372
|
+
message: error?.message,
|
|
2220
2373
|
}
|
|
2221
2374
|
: {
|
|
2222
2375
|
status: "PASSED",
|
|
@@ -2270,11 +2423,12 @@ class StableBrowser {
|
|
|
2270
2423
|
info.screenshotPath = screenshotPath;
|
|
2271
2424
|
Object.assign(e, { info: info });
|
|
2272
2425
|
error = e;
|
|
2273
|
-
throw e;
|
|
2426
|
+
// throw e;
|
|
2427
|
+
await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
|
|
2274
2428
|
}
|
|
2275
2429
|
finally {
|
|
2276
2430
|
const endTime = Date.now();
|
|
2277
|
-
|
|
2431
|
+
_reportToWorld(world, {
|
|
2278
2432
|
element_name: selectors.element_name,
|
|
2279
2433
|
type: Types.GET_TABLE_DATA,
|
|
2280
2434
|
text: "Get table data",
|
|
@@ -2284,7 +2438,7 @@ class StableBrowser {
|
|
|
2284
2438
|
status: "FAILED",
|
|
2285
2439
|
startTime,
|
|
2286
2440
|
endTime,
|
|
2287
|
-
message: error
|
|
2441
|
+
message: error?.message,
|
|
2288
2442
|
}
|
|
2289
2443
|
: {
|
|
2290
2444
|
status: "PASSED",
|
|
@@ -2435,11 +2589,12 @@ class StableBrowser {
|
|
|
2435
2589
|
info.screenshotPath = screenshotPath;
|
|
2436
2590
|
Object.assign(e, { info: info });
|
|
2437
2591
|
error = e;
|
|
2438
|
-
throw e;
|
|
2592
|
+
// throw e;
|
|
2593
|
+
await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
|
|
2439
2594
|
}
|
|
2440
2595
|
finally {
|
|
2441
2596
|
const endTime = Date.now();
|
|
2442
|
-
|
|
2597
|
+
_reportToWorld(world, {
|
|
2443
2598
|
element_name: selectors.element_name,
|
|
2444
2599
|
type: Types.ANALYZE_TABLE,
|
|
2445
2600
|
text: "Analyze table",
|
|
@@ -2449,7 +2604,7 @@ class StableBrowser {
|
|
|
2449
2604
|
status: "FAILED",
|
|
2450
2605
|
startTime,
|
|
2451
2606
|
endTime,
|
|
2452
|
-
message: error
|
|
2607
|
+
message: error?.message,
|
|
2453
2608
|
}
|
|
2454
2609
|
: {
|
|
2455
2610
|
status: "PASSED",
|
|
@@ -2461,27 +2616,7 @@ class StableBrowser {
|
|
|
2461
2616
|
}
|
|
2462
2617
|
}
|
|
2463
2618
|
async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
|
|
2464
|
-
|
|
2465
|
-
return value;
|
|
2466
|
-
}
|
|
2467
|
-
// find all the accurance of {{(.*?)}} and replace with the value
|
|
2468
|
-
let regex = /{{(.*?)}}/g;
|
|
2469
|
-
let matches = value.match(regex);
|
|
2470
|
-
if (matches) {
|
|
2471
|
-
const testData = this.getTestData(world);
|
|
2472
|
-
for (let i = 0; i < matches.length; i++) {
|
|
2473
|
-
let match = matches[i];
|
|
2474
|
-
let key = match.substring(2, match.length - 2);
|
|
2475
|
-
let newValue = objectPath.get(testData, key, null);
|
|
2476
|
-
if (newValue !== null) {
|
|
2477
|
-
value = value.replace(match, newValue);
|
|
2478
|
-
}
|
|
2479
|
-
}
|
|
2480
|
-
}
|
|
2481
|
-
if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
|
|
2482
|
-
return await decrypt(value, null, totpWait);
|
|
2483
|
-
}
|
|
2484
|
-
return value;
|
|
2619
|
+
return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
|
|
2485
2620
|
}
|
|
2486
2621
|
_getLoadTimeout(options) {
|
|
2487
2622
|
let timeout = 15000;
|
|
@@ -2532,7 +2667,7 @@ class StableBrowser {
|
|
|
2532
2667
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2533
2668
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2534
2669
|
const endTime = Date.now();
|
|
2535
|
-
|
|
2670
|
+
_reportToWorld(world, {
|
|
2536
2671
|
type: Types.GET_PAGE_STATUS,
|
|
2537
2672
|
text: "Wait for page load",
|
|
2538
2673
|
screenshotId,
|
|
@@ -2541,7 +2676,7 @@ class StableBrowser {
|
|
|
2541
2676
|
status: "FAILED",
|
|
2542
2677
|
startTime,
|
|
2543
2678
|
endTime,
|
|
2544
|
-
message: error
|
|
2679
|
+
message: error?.message,
|
|
2545
2680
|
}
|
|
2546
2681
|
: {
|
|
2547
2682
|
status: "PASSED",
|
|
@@ -2552,41 +2687,35 @@ class StableBrowser {
|
|
|
2552
2687
|
}
|
|
2553
2688
|
}
|
|
2554
2689
|
async closePage(options = {}, world = null) {
|
|
2555
|
-
const
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2690
|
+
const state = {
|
|
2691
|
+
options,
|
|
2692
|
+
world,
|
|
2693
|
+
locate: false,
|
|
2694
|
+
scroll: false,
|
|
2695
|
+
highlight: false,
|
|
2696
|
+
type: Types.CLOSE_PAGE,
|
|
2697
|
+
text: `Close page`,
|
|
2698
|
+
operation: "closePage",
|
|
2699
|
+
log: "***** close page *****\n",
|
|
2700
|
+
throwError: false,
|
|
2701
|
+
};
|
|
2560
2702
|
try {
|
|
2703
|
+
await _preCommand(state, this);
|
|
2561
2704
|
await this.page.close();
|
|
2562
2705
|
}
|
|
2563
2706
|
catch (e) {
|
|
2564
2707
|
console.log(".");
|
|
2708
|
+
await _commandError(state, e, this);
|
|
2565
2709
|
}
|
|
2566
2710
|
finally {
|
|
2567
|
-
|
|
2568
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2569
|
-
const endTime = Date.now();
|
|
2570
|
-
this._reportToWorld(world, {
|
|
2571
|
-
type: Types.CLOSE_PAGE,
|
|
2572
|
-
text: "close page",
|
|
2573
|
-
screenshotId,
|
|
2574
|
-
result: error
|
|
2575
|
-
? {
|
|
2576
|
-
status: "FAILED",
|
|
2577
|
-
startTime,
|
|
2578
|
-
endTime,
|
|
2579
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2580
|
-
}
|
|
2581
|
-
: {
|
|
2582
|
-
status: "PASSED",
|
|
2583
|
-
startTime,
|
|
2584
|
-
endTime,
|
|
2585
|
-
},
|
|
2586
|
-
info: info,
|
|
2587
|
-
});
|
|
2711
|
+
_commandFinally(state, this);
|
|
2588
2712
|
}
|
|
2589
2713
|
}
|
|
2714
|
+
saveTestDataAsGlobal(options, world) {
|
|
2715
|
+
const dataFile = this._getDataFile(world);
|
|
2716
|
+
process.env.GLOBAL_TEST_DATA_FILE = dataFile;
|
|
2717
|
+
this.logger.info("Save the scenario test data as global for the following scenarios.");
|
|
2718
|
+
}
|
|
2590
2719
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
2591
2720
|
const startTime = Date.now();
|
|
2592
2721
|
let error = null;
|
|
@@ -2604,12 +2733,13 @@ class StableBrowser {
|
|
|
2604
2733
|
}
|
|
2605
2734
|
catch (e) {
|
|
2606
2735
|
console.log(".");
|
|
2736
|
+
await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
|
|
2607
2737
|
}
|
|
2608
2738
|
finally {
|
|
2609
2739
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2610
2740
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2611
2741
|
const endTime = Date.now();
|
|
2612
|
-
|
|
2742
|
+
_reportToWorld(world, {
|
|
2613
2743
|
type: Types.SET_VIEWPORT,
|
|
2614
2744
|
text: "set viewport size to " + width + "x" + hight,
|
|
2615
2745
|
screenshotId,
|
|
@@ -2618,7 +2748,7 @@ class StableBrowser {
|
|
|
2618
2748
|
status: "FAILED",
|
|
2619
2749
|
startTime,
|
|
2620
2750
|
endTime,
|
|
2621
|
-
message: error
|
|
2751
|
+
message: error?.message,
|
|
2622
2752
|
}
|
|
2623
2753
|
: {
|
|
2624
2754
|
status: "PASSED",
|
|
@@ -2640,12 +2770,13 @@ class StableBrowser {
|
|
|
2640
2770
|
}
|
|
2641
2771
|
catch (e) {
|
|
2642
2772
|
console.log(".");
|
|
2773
|
+
await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
|
|
2643
2774
|
}
|
|
2644
2775
|
finally {
|
|
2645
2776
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2646
2777
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2647
2778
|
const endTime = Date.now();
|
|
2648
|
-
|
|
2779
|
+
_reportToWorld(world, {
|
|
2649
2780
|
type: Types.GET_PAGE_STATUS,
|
|
2650
2781
|
text: "page relaod",
|
|
2651
2782
|
screenshotId,
|
|
@@ -2654,7 +2785,7 @@ class StableBrowser {
|
|
|
2654
2785
|
status: "FAILED",
|
|
2655
2786
|
startTime,
|
|
2656
2787
|
endTime,
|
|
2657
|
-
message: error
|
|
2788
|
+
message: error?.message,
|
|
2658
2789
|
}
|
|
2659
2790
|
: {
|
|
2660
2791
|
status: "PASSED",
|
|
@@ -2681,11 +2812,37 @@ class StableBrowser {
|
|
|
2681
2812
|
console.log("#-#");
|
|
2682
2813
|
}
|
|
2683
2814
|
}
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2815
|
+
async beforeStep(world, step) {
|
|
2816
|
+
this.stepName = step.pickleStep.text;
|
|
2817
|
+
this.logger.info("step: " + this.stepName);
|
|
2818
|
+
if (this.stepIndex === undefined) {
|
|
2819
|
+
this.stepIndex = 0;
|
|
2820
|
+
}
|
|
2821
|
+
else {
|
|
2822
|
+
this.stepIndex++;
|
|
2823
|
+
}
|
|
2824
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2825
|
+
if (this.context.browserObject.context) {
|
|
2826
|
+
await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
if (this.tags === null && step && step.pickle && step.pickle.tags) {
|
|
2830
|
+
this.tags = step.pickle.tags.map((tag) => tag.name);
|
|
2831
|
+
// check if @global_test_data tag is present
|
|
2832
|
+
if (this.tags.includes("@global_test_data")) {
|
|
2833
|
+
this.saveTestDataAsGlobal({}, world);
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
async afterStep(world, step) {
|
|
2838
|
+
this.stepName = null;
|
|
2839
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2840
|
+
if (this.context.browserObject.context) {
|
|
2841
|
+
await this.context.browserObject.context.tracing.stopChunk({
|
|
2842
|
+
path: path.join(this.context.browserObject.traceFolder, `trace-${this.stepIndex}.zip`),
|
|
2843
|
+
});
|
|
2844
|
+
}
|
|
2687
2845
|
}
|
|
2688
|
-
world.attach(JSON.stringify(properties), { mediaType: "application/json" });
|
|
2689
2846
|
}
|
|
2690
2847
|
}
|
|
2691
2848
|
function createTimedPromise(promise, label) {
|