automation_model 1.0.472-dev → 1.0.472-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.js +38 -16
- package/lib/auto_page.js.map +1 -1
- package/lib/browser_manager.d.ts +5 -3
- package/lib/browser_manager.js +65 -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/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/init_browser.d.ts +1 -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 +144 -0
- package/lib/network.js.map +1 -0
- package/lib/stable_browser.d.ts +15 -8
- package/lib/stable_browser.js +408 -296
- 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,27 @@ 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",
|
|
47
49
|
};
|
|
48
50
|
export const apps = {};
|
|
49
51
|
class StableBrowser {
|
|
52
|
+
browser;
|
|
53
|
+
page;
|
|
54
|
+
logger;
|
|
55
|
+
context;
|
|
56
|
+
world;
|
|
57
|
+
project_path = null;
|
|
58
|
+
webLogFile = null;
|
|
59
|
+
networkLogger = null;
|
|
60
|
+
configuration = null;
|
|
61
|
+
appName = "main";
|
|
62
|
+
tags = null;
|
|
50
63
|
constructor(browser, page, logger = null, context = null, world = null) {
|
|
51
64
|
this.browser = browser;
|
|
52
65
|
this.page = page;
|
|
53
66
|
this.logger = logger;
|
|
54
67
|
this.context = context;
|
|
55
68
|
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
69
|
if (!this.logger) {
|
|
62
70
|
this.logger = console;
|
|
63
71
|
}
|
|
@@ -82,11 +90,31 @@ class StableBrowser {
|
|
|
82
90
|
catch (e) {
|
|
83
91
|
this.logger.error("unable to read ai_config.json");
|
|
84
92
|
}
|
|
85
|
-
context.pageLoading = { status: false };
|
|
86
|
-
context.pages = [this.page];
|
|
87
93
|
const logFolder = path.join(this.project_path, "logs", "web");
|
|
88
94
|
this.world = world;
|
|
95
|
+
context.pages = [this.page];
|
|
96
|
+
context.pageLoading = { status: false };
|
|
89
97
|
this.registerEventListeners(this.context);
|
|
98
|
+
registerNetworkEvents(this.world, this, this.context, this.page);
|
|
99
|
+
registerDownloadEvent(this.page, this.world, this.context);
|
|
100
|
+
}
|
|
101
|
+
async scrollPageToLoadLazyElements() {
|
|
102
|
+
let lastHeight = await this.page.evaluate(() => document.body.scrollHeight);
|
|
103
|
+
let retry = 0;
|
|
104
|
+
while (true) {
|
|
105
|
+
await this.page.evaluate(() => window.scrollBy(0, window.innerHeight));
|
|
106
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
107
|
+
let newHeight = await this.page.evaluate(() => document.body.scrollHeight);
|
|
108
|
+
if (newHeight === lastHeight) {
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
lastHeight = newHeight;
|
|
112
|
+
retry++;
|
|
113
|
+
if (retry > 10) {
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
await this.page.evaluate(() => window.scrollTo(0, 0));
|
|
90
118
|
}
|
|
91
119
|
registerEventListeners(context) {
|
|
92
120
|
this.registerConsoleLogListener(this.page, context);
|
|
@@ -95,10 +123,17 @@ class StableBrowser {
|
|
|
95
123
|
context.pageLoading = { status: false };
|
|
96
124
|
}
|
|
97
125
|
context.playContext.on("page", async function (page) {
|
|
126
|
+
if (this.configuration && this.configuration.closePopups === true) {
|
|
127
|
+
console.log("close unexpected popups");
|
|
128
|
+
await page.close();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
98
131
|
context.pageLoading.status = true;
|
|
99
132
|
this.page = page;
|
|
100
133
|
context.page = page;
|
|
101
134
|
context.pages.push(page);
|
|
135
|
+
registerNetworkEvents(this.world, this, context, this.page);
|
|
136
|
+
registerDownloadEvent(this.page, this.world, context);
|
|
102
137
|
page.on("close", async () => {
|
|
103
138
|
if (this.context && this.context.pages && this.context.pages.length > 1) {
|
|
104
139
|
this.context.pages.pop();
|
|
@@ -128,9 +163,9 @@ class StableBrowser {
|
|
|
128
163
|
if (this.appName === appName) {
|
|
129
164
|
return;
|
|
130
165
|
}
|
|
131
|
-
let
|
|
166
|
+
let navigate = false;
|
|
132
167
|
if (!apps[appName]) {
|
|
133
|
-
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this);
|
|
168
|
+
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this, -1, this.context.reportFolder);
|
|
134
169
|
newContextCreated = true;
|
|
135
170
|
apps[appName] = {
|
|
136
171
|
context: newContext,
|
|
@@ -143,8 +178,7 @@ class StableBrowser {
|
|
|
143
178
|
this._copyContext(apps[appName], this);
|
|
144
179
|
apps[this.appName] = tempContext;
|
|
145
180
|
this.appName = appName;
|
|
146
|
-
if (
|
|
147
|
-
this.registerEventListeners(this.context);
|
|
181
|
+
if (navigate) {
|
|
148
182
|
await this.goto(this.context.environment.baseUrl);
|
|
149
183
|
await this.waitForPageLoad();
|
|
150
184
|
}
|
|
@@ -170,7 +204,6 @@ class StableBrowser {
|
|
|
170
204
|
this.context.webLogger = [];
|
|
171
205
|
}
|
|
172
206
|
page.on("console", async (msg) => {
|
|
173
|
-
var _a;
|
|
174
207
|
const obj = {
|
|
175
208
|
type: msg.type(),
|
|
176
209
|
text: msg.text(),
|
|
@@ -179,7 +212,7 @@ class StableBrowser {
|
|
|
179
212
|
};
|
|
180
213
|
this.context.webLogger.push(obj);
|
|
181
214
|
if (msg.type() === "error") {
|
|
182
|
-
|
|
215
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
|
|
183
216
|
}
|
|
184
217
|
});
|
|
185
218
|
}
|
|
@@ -188,7 +221,6 @@ class StableBrowser {
|
|
|
188
221
|
this.context.networkLogger = [];
|
|
189
222
|
}
|
|
190
223
|
page.on("request", async (data) => {
|
|
191
|
-
var _a;
|
|
192
224
|
const startTime = new Date().getTime();
|
|
193
225
|
try {
|
|
194
226
|
const pageUrl = new URL(page.url());
|
|
@@ -213,10 +245,10 @@ class StableBrowser {
|
|
|
213
245
|
startTime,
|
|
214
246
|
};
|
|
215
247
|
context.networkLogger.push(obj);
|
|
216
|
-
|
|
248
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
|
|
217
249
|
}
|
|
218
250
|
catch (error) {
|
|
219
|
-
console.error("Error in request listener", error);
|
|
251
|
+
// console.error("Error in request listener", error);
|
|
220
252
|
context.networkLogger.push({
|
|
221
253
|
error: "not able to listen",
|
|
222
254
|
message: error.message,
|
|
@@ -305,7 +337,7 @@ class StableBrowser {
|
|
|
305
337
|
locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
|
|
306
338
|
}
|
|
307
339
|
}
|
|
308
|
-
if (locator
|
|
340
|
+
if (locator?.engine) {
|
|
309
341
|
if (locator.engine === "css") {
|
|
310
342
|
locatorReturn = scope.locator(locator.selector);
|
|
311
343
|
}
|
|
@@ -329,7 +361,7 @@ class StableBrowser {
|
|
|
329
361
|
if (css && css.locator) {
|
|
330
362
|
css = css.locator;
|
|
331
363
|
}
|
|
332
|
-
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "
|
|
364
|
+
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*:not(script, style, head)", false, false, _params);
|
|
333
365
|
if (result.elementCount === 0) {
|
|
334
366
|
return;
|
|
335
367
|
}
|
|
@@ -344,7 +376,7 @@ class StableBrowser {
|
|
|
344
376
|
}
|
|
345
377
|
async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
|
|
346
378
|
//const stringifyText = JSON.stringify(text);
|
|
347
|
-
return await scope.evaluate(([text, tag, regex, partial]) => {
|
|
379
|
+
return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
|
|
348
380
|
function isParent(parent, child) {
|
|
349
381
|
let currentNode = child.parentNode;
|
|
350
382
|
while (currentNode !== null) {
|
|
@@ -381,7 +413,7 @@ class StableBrowser {
|
|
|
381
413
|
}
|
|
382
414
|
document.collectAllShadowDomElements = collectAllShadowDomElements;
|
|
383
415
|
if (!tag) {
|
|
384
|
-
tag = "
|
|
416
|
+
tag = "*:not(script, style, head)";
|
|
385
417
|
}
|
|
386
418
|
let regexpSearch = document.getRegex(text);
|
|
387
419
|
if (regexpSearch) {
|
|
@@ -463,6 +495,15 @@ class StableBrowser {
|
|
|
463
495
|
}, [text1, tag1, regex1, partial1]);
|
|
464
496
|
}
|
|
465
497
|
async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
|
|
498
|
+
if (!info) {
|
|
499
|
+
info = {};
|
|
500
|
+
}
|
|
501
|
+
if (!info.failCause) {
|
|
502
|
+
info.failCause = {};
|
|
503
|
+
}
|
|
504
|
+
if (!info.log) {
|
|
505
|
+
info.log = "";
|
|
506
|
+
}
|
|
466
507
|
let locatorSearch = selectorHierarchy[index];
|
|
467
508
|
try {
|
|
468
509
|
locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
|
|
@@ -475,13 +516,18 @@ class StableBrowser {
|
|
|
475
516
|
if (locatorSearch.climb && locatorSearch.climb >= 0) {
|
|
476
517
|
let locatorString = await this._locateElmentByTextClimbCss(scope, locatorSearch.text, locatorSearch.climb, locatorSearch.css, _params);
|
|
477
518
|
if (!locatorString) {
|
|
519
|
+
info.failCause.textNotFound = true;
|
|
520
|
+
info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
|
|
478
521
|
return;
|
|
479
522
|
}
|
|
480
523
|
locator = this._getLocator({ css: locatorString }, scope, _params);
|
|
481
524
|
}
|
|
482
525
|
else if (locatorSearch.text) {
|
|
483
|
-
let
|
|
526
|
+
let text = this._fixUsingParams(locatorSearch.text, _params);
|
|
527
|
+
let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
|
|
484
528
|
if (result.elementCount === 0) {
|
|
529
|
+
info.failCause.textNotFound = true;
|
|
530
|
+
info.failCause.lastError = "failed to locate element by text: " + text;
|
|
485
531
|
return;
|
|
486
532
|
}
|
|
487
533
|
locatorSearch.css = "[data-blinq-id='blinq-id-" + result.randomToken + "']";
|
|
@@ -498,10 +544,13 @@ class StableBrowser {
|
|
|
498
544
|
// cssHref = true;
|
|
499
545
|
// }
|
|
500
546
|
let count = await locator.count();
|
|
547
|
+
if (count > 0 && !info.failCause.count) {
|
|
548
|
+
info.failCause.count = count;
|
|
549
|
+
}
|
|
501
550
|
//info.log += "total elements found " + count + "\n";
|
|
502
551
|
//let visibleCount = 0;
|
|
503
552
|
let visibleLocator = null;
|
|
504
|
-
if (locatorSearch.index && locatorSearch.index < count) {
|
|
553
|
+
if (typeof locatorSearch.index === "number" && locatorSearch.index < count) {
|
|
505
554
|
foundLocators.push(locator.nth(locatorSearch.index));
|
|
506
555
|
return;
|
|
507
556
|
}
|
|
@@ -515,6 +564,8 @@ class StableBrowser {
|
|
|
515
564
|
foundLocators.push(locator.nth(j));
|
|
516
565
|
}
|
|
517
566
|
else {
|
|
567
|
+
info.failCause.visible = visible;
|
|
568
|
+
info.failCause.enabled = enabled;
|
|
518
569
|
if (!info.printMessages) {
|
|
519
570
|
info.printMessages = {};
|
|
520
571
|
}
|
|
@@ -526,6 +577,11 @@ class StableBrowser {
|
|
|
526
577
|
}
|
|
527
578
|
}
|
|
528
579
|
async closeUnexpectedPopups(info, _params) {
|
|
580
|
+
if (!info) {
|
|
581
|
+
info = {};
|
|
582
|
+
info.failCause = {};
|
|
583
|
+
info.log = "";
|
|
584
|
+
}
|
|
529
585
|
if (this.configuration.popupHandlers && this.configuration.popupHandlers.length > 0) {
|
|
530
586
|
if (!info) {
|
|
531
587
|
info = {};
|
|
@@ -557,16 +613,31 @@ class StableBrowser {
|
|
|
557
613
|
}
|
|
558
614
|
if (result.foundElements.length > 0) {
|
|
559
615
|
let dialogCloseLocator = result.foundElements[0].locator;
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
616
|
+
try {
|
|
617
|
+
await scope?.evaluate(() => {
|
|
618
|
+
window.__isClosingPopups = true;
|
|
619
|
+
});
|
|
620
|
+
await dialogCloseLocator.click();
|
|
621
|
+
// wait for the dialog to close
|
|
622
|
+
await dialogCloseLocator.waitFor({ state: "hidden" });
|
|
623
|
+
}
|
|
624
|
+
catch (e) {
|
|
625
|
+
}
|
|
626
|
+
finally {
|
|
627
|
+
await scope?.evaluate(() => {
|
|
628
|
+
window.__isClosingPopups = false;
|
|
629
|
+
});
|
|
630
|
+
}
|
|
563
631
|
return { rerun: true };
|
|
564
632
|
}
|
|
565
633
|
}
|
|
566
634
|
}
|
|
567
635
|
return { rerun: false };
|
|
568
636
|
}
|
|
569
|
-
async _locate(selectors, info, _params, timeout
|
|
637
|
+
async _locate(selectors, info, _params, timeout) {
|
|
638
|
+
if (!timeout) {
|
|
639
|
+
timeout = 30000;
|
|
640
|
+
}
|
|
570
641
|
for (let i = 0; i < 3; i++) {
|
|
571
642
|
info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
|
|
572
643
|
for (let j = 0; j < selectors.locators.length; j++) {
|
|
@@ -580,16 +651,16 @@ class StableBrowser {
|
|
|
580
651
|
}
|
|
581
652
|
throw new Error("unable to locate element " + JSON.stringify(selectors));
|
|
582
653
|
}
|
|
583
|
-
async
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
654
|
+
async _findFrameScope(selectors, timeout = 30000, info) {
|
|
655
|
+
if (!info) {
|
|
656
|
+
info = {};
|
|
657
|
+
info.failCause = {};
|
|
658
|
+
info.log = "";
|
|
659
|
+
}
|
|
660
|
+
let startTime = Date.now();
|
|
589
661
|
let scope = this.page;
|
|
590
|
-
// for the simple click usecase
|
|
591
662
|
if (selectors.frame) {
|
|
592
|
-
|
|
663
|
+
return selectors.frame;
|
|
593
664
|
}
|
|
594
665
|
if (selectors.iframe_src || selectors.frameLocators) {
|
|
595
666
|
const findFrame = async (frame, framescope) => {
|
|
@@ -617,7 +688,6 @@ class StableBrowser {
|
|
|
617
688
|
}
|
|
618
689
|
return framescope;
|
|
619
690
|
};
|
|
620
|
-
info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
|
|
621
691
|
while (true) {
|
|
622
692
|
let frameFound = false;
|
|
623
693
|
if (selectors.nestFrmLoc) {
|
|
@@ -640,7 +710,9 @@ class StableBrowser {
|
|
|
640
710
|
}
|
|
641
711
|
if (!scope) {
|
|
642
712
|
info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
|
|
643
|
-
if (
|
|
713
|
+
if (Date.now() - startTime > timeout) {
|
|
714
|
+
info.failCause.iframeNotFound = true;
|
|
715
|
+
info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
|
|
644
716
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
645
717
|
}
|
|
646
718
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -650,6 +722,31 @@ class StableBrowser {
|
|
|
650
722
|
}
|
|
651
723
|
}
|
|
652
724
|
}
|
|
725
|
+
if (!scope) {
|
|
726
|
+
scope = this.page;
|
|
727
|
+
}
|
|
728
|
+
return scope;
|
|
729
|
+
}
|
|
730
|
+
async _getDocumentBody(selectors, timeout = 30000, info) {
|
|
731
|
+
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
732
|
+
return scope.evaluate(() => {
|
|
733
|
+
var bodyContent = document.body.innerHTML;
|
|
734
|
+
return bodyContent;
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
738
|
+
if (!info) {
|
|
739
|
+
info = {};
|
|
740
|
+
info.failCause = {};
|
|
741
|
+
info.log = "";
|
|
742
|
+
}
|
|
743
|
+
let highPriorityTimeout = 5000;
|
|
744
|
+
let visibleOnlyTimeout = 6000;
|
|
745
|
+
let startTime = Date.now();
|
|
746
|
+
let locatorsCount = 0;
|
|
747
|
+
let lazy_scroll = false;
|
|
748
|
+
//let arrayMode = Array.isArray(selectors);
|
|
749
|
+
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
653
750
|
let selectorsLocators = null;
|
|
654
751
|
selectorsLocators = selectors.locators;
|
|
655
752
|
// group selectors by priority
|
|
@@ -736,14 +833,18 @@ class StableBrowser {
|
|
|
736
833
|
return maxCountElement.locator;
|
|
737
834
|
}
|
|
738
835
|
}
|
|
739
|
-
if (
|
|
836
|
+
if (Date.now() - startTime > timeout) {
|
|
740
837
|
break;
|
|
741
838
|
}
|
|
742
|
-
if (
|
|
839
|
+
if (Date.now() - startTime > highPriorityTimeout) {
|
|
743
840
|
info.log += "high priority timeout, will try all elements" + "\n";
|
|
744
841
|
highPriorityOnly = false;
|
|
842
|
+
if (this.configuration && this.configuration.load_all_lazy === true && !lazy_scroll) {
|
|
843
|
+
lazy_scroll = true;
|
|
844
|
+
await this.scrollPageToLoadLazyElements();
|
|
845
|
+
}
|
|
745
846
|
}
|
|
746
|
-
if (
|
|
847
|
+
if (Date.now() - startTime > visibleOnlyTimeout) {
|
|
747
848
|
info.log += "visible only timeout, will try all elements" + "\n";
|
|
748
849
|
visibleOnly = false;
|
|
749
850
|
}
|
|
@@ -751,6 +852,8 @@ class StableBrowser {
|
|
|
751
852
|
}
|
|
752
853
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
753
854
|
info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
|
|
855
|
+
info.failCause.locatorNotFound = true;
|
|
856
|
+
info.failCause.lastError = "failed to locate unique element";
|
|
754
857
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
755
858
|
}
|
|
756
859
|
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
@@ -782,6 +885,9 @@ class StableBrowser {
|
|
|
782
885
|
});
|
|
783
886
|
result.locatorIndex = i;
|
|
784
887
|
}
|
|
888
|
+
if (foundLocators.length > 1) {
|
|
889
|
+
info.failCause.foundMultiple = true;
|
|
890
|
+
}
|
|
785
891
|
}
|
|
786
892
|
return result;
|
|
787
893
|
}
|
|
@@ -794,12 +900,12 @@ class StableBrowser {
|
|
|
794
900
|
while (true) {
|
|
795
901
|
try {
|
|
796
902
|
const result = await locate_element(this.context, elementDescription, "click");
|
|
797
|
-
if (
|
|
903
|
+
if (result?.elementNumber >= 0) {
|
|
798
904
|
const selectors = {
|
|
799
|
-
frame: result
|
|
905
|
+
frame: result?.frame,
|
|
800
906
|
locators: [
|
|
801
907
|
{
|
|
802
|
-
css: result
|
|
908
|
+
css: result?.css,
|
|
803
909
|
},
|
|
804
910
|
],
|
|
805
911
|
};
|
|
@@ -808,8 +914,9 @@ class StableBrowser {
|
|
|
808
914
|
}
|
|
809
915
|
}
|
|
810
916
|
catch (e) {
|
|
811
|
-
if (
|
|
812
|
-
throw e;
|
|
917
|
+
if (Date.now() - startTime > timeout) {
|
|
918
|
+
// throw e;
|
|
919
|
+
await _commandError({ text: "simpleClick", operation: "simpleClick", elementDescription, info: {} }, e, this);
|
|
813
920
|
}
|
|
814
921
|
}
|
|
815
922
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
@@ -824,12 +931,12 @@ class StableBrowser {
|
|
|
824
931
|
while (true) {
|
|
825
932
|
try {
|
|
826
933
|
const result = await locate_element(this.context, elementDescription, "fill", value);
|
|
827
|
-
if (
|
|
934
|
+
if (result?.elementNumber >= 0) {
|
|
828
935
|
const selectors = {
|
|
829
|
-
frame: result
|
|
936
|
+
frame: result?.frame,
|
|
830
937
|
locators: [
|
|
831
938
|
{
|
|
832
|
-
css: result
|
|
939
|
+
css: result?.css,
|
|
833
940
|
},
|
|
834
941
|
],
|
|
835
942
|
};
|
|
@@ -838,8 +945,9 @@ class StableBrowser {
|
|
|
838
945
|
}
|
|
839
946
|
}
|
|
840
947
|
catch (e) {
|
|
841
|
-
if (
|
|
842
|
-
throw e;
|
|
948
|
+
if (Date.now() - startTime > timeout) {
|
|
949
|
+
// throw e;
|
|
950
|
+
await _commandError({ text: "simpleClickType", operation: "simpleClickType", value, elementDescription, info: {} }, e, this);
|
|
843
951
|
}
|
|
844
952
|
}
|
|
845
953
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
@@ -863,13 +971,13 @@ class StableBrowser {
|
|
|
863
971
|
}
|
|
864
972
|
try {
|
|
865
973
|
await state.element.click();
|
|
866
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
974
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
867
975
|
}
|
|
868
976
|
catch (e) {
|
|
869
977
|
// await this.closeUnexpectedPopups();
|
|
870
978
|
state.element = await this._locate(selectors, state.info, _params);
|
|
871
979
|
await state.element.dispatchEvent("click");
|
|
872
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
980
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
873
981
|
}
|
|
874
982
|
await this.waitForPageLoad();
|
|
875
983
|
return state.info;
|
|
@@ -893,7 +1001,7 @@ class StableBrowser {
|
|
|
893
1001
|
log: "***** check " + selectors.element_name + " *****\n",
|
|
894
1002
|
};
|
|
895
1003
|
try {
|
|
896
|
-
_preCommand(state, this);
|
|
1004
|
+
await _preCommand(state, this);
|
|
897
1005
|
state.info.checked = checked;
|
|
898
1006
|
// let element = await this._locate(selectors, info, _params);
|
|
899
1007
|
// ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
@@ -908,8 +1016,8 @@ class StableBrowser {
|
|
|
908
1016
|
}
|
|
909
1017
|
else {
|
|
910
1018
|
//await this.closeUnexpectedPopups();
|
|
911
|
-
info.log += "setCheck failed, will try again" + "\n";
|
|
912
|
-
state.element = await this._locate(selectors, info, _params);
|
|
1019
|
+
state.info.log += "setCheck failed, will try again" + "\n";
|
|
1020
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
913
1021
|
await state.element.setChecked(checked, { timeout: 5000, force: true });
|
|
914
1022
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
915
1023
|
}
|
|
@@ -936,7 +1044,7 @@ class StableBrowser {
|
|
|
936
1044
|
log: "***** hover " + selectors.element_name + " *****\n",
|
|
937
1045
|
};
|
|
938
1046
|
try {
|
|
939
|
-
_preCommand(state, this);
|
|
1047
|
+
await _preCommand(state, this);
|
|
940
1048
|
try {
|
|
941
1049
|
await state.element.hover();
|
|
942
1050
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -967,6 +1075,7 @@ class StableBrowser {
|
|
|
967
1075
|
_params,
|
|
968
1076
|
options,
|
|
969
1077
|
world,
|
|
1078
|
+
value: values.toString(),
|
|
970
1079
|
type: Types.SELECT,
|
|
971
1080
|
text: `Select option: ${values}`,
|
|
972
1081
|
operation: "selectOption",
|
|
@@ -1074,33 +1183,30 @@ class StableBrowser {
|
|
|
1074
1183
|
}
|
|
1075
1184
|
}
|
|
1076
1185
|
async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1186
|
+
const state = {
|
|
1187
|
+
selectors,
|
|
1188
|
+
_params,
|
|
1189
|
+
value: await this._replaceWithLocalData(value, this),
|
|
1190
|
+
options,
|
|
1191
|
+
world,
|
|
1192
|
+
type: Types.SET_DATE_TIME,
|
|
1193
|
+
text: `Set date time value: ${value}`,
|
|
1194
|
+
operation: "setDateTime",
|
|
1195
|
+
log: "***** set date time value " + selectors.element_name + " *****\n",
|
|
1196
|
+
throwError: false,
|
|
1197
|
+
};
|
|
1087
1198
|
try {
|
|
1088
|
-
|
|
1089
|
-
let element = await this._locate(selectors, info, _params);
|
|
1090
|
-
//insert red border around the element
|
|
1091
|
-
await this.scrollIfNeeded(element, info);
|
|
1092
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1093
|
-
await this._highlightElements(element);
|
|
1199
|
+
await _preCommand(state, this);
|
|
1094
1200
|
try {
|
|
1095
|
-
await element.click();
|
|
1201
|
+
await state.element.click();
|
|
1096
1202
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1097
1203
|
if (format) {
|
|
1098
|
-
value = dayjs(value).format(format);
|
|
1099
|
-
await element.fill(value);
|
|
1204
|
+
state.value = dayjs(state.value).format(format);
|
|
1205
|
+
await state.element.fill(state.value);
|
|
1100
1206
|
}
|
|
1101
1207
|
else {
|
|
1102
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1103
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1208
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1209
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1104
1210
|
el.value = ""; // clear input
|
|
1105
1211
|
el.value = dateTimeValue;
|
|
1106
1212
|
}, dateTimeValue);
|
|
@@ -1113,20 +1219,19 @@ class StableBrowser {
|
|
|
1113
1219
|
}
|
|
1114
1220
|
catch (err) {
|
|
1115
1221
|
//await this.closeUnexpectedPopups();
|
|
1116
|
-
this.logger.error("setting date time input failed " + JSON.stringify(info));
|
|
1222
|
+
this.logger.error("setting date time input failed " + JSON.stringify(state.info));
|
|
1117
1223
|
this.logger.info("Trying again");
|
|
1118
|
-
|
|
1119
|
-
info.
|
|
1120
|
-
Object.assign(err, { info: info });
|
|
1224
|
+
await _screenshot(state, this);
|
|
1225
|
+
Object.assign(err, { info: state.info });
|
|
1121
1226
|
await element.click();
|
|
1122
1227
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1123
1228
|
if (format) {
|
|
1124
|
-
value = dayjs(value).format(format);
|
|
1125
|
-
await element.fill(value);
|
|
1229
|
+
state.value = dayjs(state.value).format(format);
|
|
1230
|
+
await state.element.fill(state.value);
|
|
1126
1231
|
}
|
|
1127
1232
|
else {
|
|
1128
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1129
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1233
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1234
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1130
1235
|
el.value = ""; // clear input
|
|
1131
1236
|
el.value = dateTimeValue;
|
|
1132
1237
|
}, dateTimeValue);
|
|
@@ -1139,46 +1244,27 @@ class StableBrowser {
|
|
|
1139
1244
|
}
|
|
1140
1245
|
}
|
|
1141
1246
|
catch (e) {
|
|
1142
|
-
|
|
1143
|
-
throw e;
|
|
1247
|
+
await _commandError(state, e, this);
|
|
1144
1248
|
}
|
|
1145
1249
|
finally {
|
|
1146
|
-
|
|
1147
|
-
this._reportToWorld(world, {
|
|
1148
|
-
element_name: selectors.element_name,
|
|
1149
|
-
type: Types.SET_DATE_TIME,
|
|
1150
|
-
screenshotId,
|
|
1151
|
-
value: value,
|
|
1152
|
-
text: `setDateTime input with value: ${value}`,
|
|
1153
|
-
result: error
|
|
1154
|
-
? {
|
|
1155
|
-
status: "FAILED",
|
|
1156
|
-
startTime,
|
|
1157
|
-
endTime,
|
|
1158
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1159
|
-
}
|
|
1160
|
-
: {
|
|
1161
|
-
status: "PASSED",
|
|
1162
|
-
startTime,
|
|
1163
|
-
endTime,
|
|
1164
|
-
},
|
|
1165
|
-
info: info,
|
|
1166
|
-
});
|
|
1250
|
+
_commandFinally(state, this);
|
|
1167
1251
|
}
|
|
1168
1252
|
}
|
|
1169
1253
|
async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
|
|
1254
|
+
_value = unEscapeString(_value);
|
|
1255
|
+
const newValue = await this._replaceWithLocalData(_value, world);
|
|
1170
1256
|
const state = {
|
|
1171
1257
|
selectors,
|
|
1172
1258
|
_params,
|
|
1173
|
-
value:
|
|
1259
|
+
value: newValue,
|
|
1260
|
+
originalValue: _value,
|
|
1174
1261
|
options,
|
|
1175
1262
|
world,
|
|
1176
1263
|
type: Types.FILL,
|
|
1177
1264
|
text: `Click type input with value: ${_value}`,
|
|
1178
1265
|
operation: "clickType",
|
|
1179
|
-
log: "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n",
|
|
1266
|
+
log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
|
|
1180
1267
|
};
|
|
1181
|
-
const newValue = await this._replaceWithLocalData(state.value, world);
|
|
1182
1268
|
if (newValue !== _value) {
|
|
1183
1269
|
//this.logger.info(_value + "=" + newValue);
|
|
1184
1270
|
_value = newValue;
|
|
@@ -1190,7 +1276,7 @@ class StableBrowser {
|
|
|
1190
1276
|
try {
|
|
1191
1277
|
let currentValue = await state.element.inputValue();
|
|
1192
1278
|
if (currentValue) {
|
|
1193
|
-
await element.fill("");
|
|
1279
|
+
await state.element.fill("");
|
|
1194
1280
|
}
|
|
1195
1281
|
}
|
|
1196
1282
|
catch (e) {
|
|
@@ -1337,7 +1423,6 @@ class StableBrowser {
|
|
|
1337
1423
|
}
|
|
1338
1424
|
}
|
|
1339
1425
|
async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
|
|
1340
|
-
var _a;
|
|
1341
1426
|
if (!pattern) {
|
|
1342
1427
|
throw new Error("pattern is null");
|
|
1343
1428
|
}
|
|
@@ -1348,7 +1433,7 @@ class StableBrowser {
|
|
|
1348
1433
|
selectors,
|
|
1349
1434
|
_params,
|
|
1350
1435
|
pattern,
|
|
1351
|
-
value:
|
|
1436
|
+
value: pattern,
|
|
1352
1437
|
options,
|
|
1353
1438
|
world,
|
|
1354
1439
|
locate: false,
|
|
@@ -1377,14 +1462,14 @@ class StableBrowser {
|
|
|
1377
1462
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1378
1463
|
pattern = pattern.replace("{text}", escapedText);
|
|
1379
1464
|
let regex = new RegExp(pattern, "im");
|
|
1380
|
-
if (!regex.test(foundObj
|
|
1381
|
-
state.info.foundText = foundObj
|
|
1465
|
+
if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
|
|
1466
|
+
state.info.foundText = foundObj?.text;
|
|
1382
1467
|
throw new Error("element doesn't contain text " + text);
|
|
1383
1468
|
}
|
|
1384
1469
|
return state.info;
|
|
1385
1470
|
}
|
|
1386
1471
|
catch (e) {
|
|
1387
|
-
this.logger.error("found text " +
|
|
1472
|
+
this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
|
|
1388
1473
|
await _commandError(state, e, this);
|
|
1389
1474
|
}
|
|
1390
1475
|
finally {
|
|
@@ -1392,7 +1477,6 @@ class StableBrowser {
|
|
|
1392
1477
|
}
|
|
1393
1478
|
}
|
|
1394
1479
|
async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
|
|
1395
|
-
var _a, _b, _c;
|
|
1396
1480
|
const state = {
|
|
1397
1481
|
selectors,
|
|
1398
1482
|
_params,
|
|
@@ -1419,6 +1503,7 @@ class StableBrowser {
|
|
|
1419
1503
|
}
|
|
1420
1504
|
let foundObj = null;
|
|
1421
1505
|
try {
|
|
1506
|
+
await _preCommand(state, this);
|
|
1422
1507
|
foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
|
|
1423
1508
|
if (foundObj && foundObj.element) {
|
|
1424
1509
|
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
@@ -1428,8 +1513,8 @@ class StableBrowser {
|
|
|
1428
1513
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1429
1514
|
if (dateAlternatives.date) {
|
|
1430
1515
|
for (let i = 0; i < dateAlternatives.dates.length; i++) {
|
|
1431
|
-
if (
|
|
1432
|
-
|
|
1516
|
+
if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
|
|
1517
|
+
foundObj?.value?.includes(dateAlternatives.dates[i])) {
|
|
1433
1518
|
return state.info;
|
|
1434
1519
|
}
|
|
1435
1520
|
}
|
|
@@ -1437,16 +1522,16 @@ class StableBrowser {
|
|
|
1437
1522
|
}
|
|
1438
1523
|
else if (numberAlternatives.number) {
|
|
1439
1524
|
for (let i = 0; i < numberAlternatives.numbers.length; i++) {
|
|
1440
|
-
if (
|
|
1441
|
-
|
|
1525
|
+
if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
|
|
1526
|
+
foundObj?.value?.includes(numberAlternatives.numbers[i])) {
|
|
1442
1527
|
return state.info;
|
|
1443
1528
|
}
|
|
1444
1529
|
}
|
|
1445
1530
|
throw new Error("element doesn't contain text " + text);
|
|
1446
1531
|
}
|
|
1447
|
-
else if (!
|
|
1448
|
-
state.info.foundText = foundObj
|
|
1449
|
-
state.info.value = foundObj
|
|
1532
|
+
else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
|
|
1533
|
+
state.info.foundText = foundObj?.text;
|
|
1534
|
+
state.info.value = foundObj?.value;
|
|
1450
1535
|
throw new Error("element doesn't contain text " + text);
|
|
1451
1536
|
}
|
|
1452
1537
|
return state.info;
|
|
@@ -1637,11 +1722,9 @@ class StableBrowser {
|
|
|
1637
1722
|
if (!fs.existsSync(world.screenshotPath)) {
|
|
1638
1723
|
fs.mkdirSync(world.screenshotPath, { recursive: true });
|
|
1639
1724
|
}
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
}
|
|
1644
|
-
const screenshotPath = path.join(world.screenshotPath, nextIndex + ".png");
|
|
1725
|
+
// to make sure the path doesn't start with -
|
|
1726
|
+
const uuidStr = "id_" + randomUUID();
|
|
1727
|
+
const screenshotPath = path.join(world.screenshotPath, uuidStr + ".png");
|
|
1645
1728
|
try {
|
|
1646
1729
|
await this.takeScreenshot(screenshotPath);
|
|
1647
1730
|
// let buffer = await this.page.screenshot({ timeout: 4000 });
|
|
@@ -1655,7 +1738,7 @@ class StableBrowser {
|
|
|
1655
1738
|
catch (e) {
|
|
1656
1739
|
this.logger.info("unable to take screenshot, ignored");
|
|
1657
1740
|
}
|
|
1658
|
-
result.screenshotId =
|
|
1741
|
+
result.screenshotId = uuidStr;
|
|
1659
1742
|
result.screenshotPath = screenshotPath;
|
|
1660
1743
|
if (info && info.box) {
|
|
1661
1744
|
await drawRectangle(screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
|
|
@@ -1753,74 +1836,45 @@ class StableBrowser {
|
|
|
1753
1836
|
}
|
|
1754
1837
|
}
|
|
1755
1838
|
async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1839
|
+
const state = {
|
|
1840
|
+
selectors,
|
|
1841
|
+
_params,
|
|
1842
|
+
attribute,
|
|
1843
|
+
variable,
|
|
1844
|
+
options,
|
|
1845
|
+
world,
|
|
1846
|
+
type: Types.EXTRACT,
|
|
1847
|
+
text: `Extract attribute from element`,
|
|
1848
|
+
operation: "extractAttribute",
|
|
1849
|
+
log: "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1850
|
+
};
|
|
1761
1851
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1762
|
-
const info = {};
|
|
1763
|
-
info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
|
|
1764
|
-
info.operation = "extract";
|
|
1765
|
-
info.selectors = selectors;
|
|
1766
1852
|
try {
|
|
1767
|
-
|
|
1768
|
-
await this._highlightElements(element);
|
|
1769
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1853
|
+
await _preCommand(state, this);
|
|
1770
1854
|
switch (attribute) {
|
|
1771
1855
|
case "inner_text":
|
|
1772
|
-
|
|
1856
|
+
state.value = await state.element.innerText();
|
|
1773
1857
|
break;
|
|
1774
1858
|
case "href":
|
|
1775
|
-
|
|
1859
|
+
state.value = await state.element.getAttribute("href");
|
|
1776
1860
|
break;
|
|
1777
1861
|
case "value":
|
|
1778
|
-
|
|
1862
|
+
state.value = await state.element.inputValue();
|
|
1779
1863
|
break;
|
|
1780
1864
|
default:
|
|
1781
|
-
|
|
1865
|
+
state.value = await state.element.getAttribute(attribute);
|
|
1782
1866
|
break;
|
|
1783
1867
|
}
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
this.setTestData({ [variable]: info.value }, world);
|
|
1789
|
-
this.logger.info("set test data: " + variable + "=" + info.value);
|
|
1790
|
-
return info;
|
|
1868
|
+
state.info.value = state.value;
|
|
1869
|
+
this.setTestData({ [variable]: state.value }, world);
|
|
1870
|
+
this.logger.info("set test data: " + variable + "=" + state.value);
|
|
1871
|
+
return state.info;
|
|
1791
1872
|
}
|
|
1792
1873
|
catch (e) {
|
|
1793
|
-
|
|
1794
|
-
this.logger.error("extract failed " + info.log);
|
|
1795
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1796
|
-
info.screenshotPath = screenshotPath;
|
|
1797
|
-
Object.assign(e, { info: info });
|
|
1798
|
-
error = e;
|
|
1799
|
-
throw e;
|
|
1874
|
+
await _commandError(state, e, this);
|
|
1800
1875
|
}
|
|
1801
1876
|
finally {
|
|
1802
|
-
|
|
1803
|
-
this._reportToWorld(world, {
|
|
1804
|
-
element_name: selectors.element_name,
|
|
1805
|
-
type: Types.EXTRACT_ATTRIBUTE,
|
|
1806
|
-
variable: variable,
|
|
1807
|
-
value: info.value,
|
|
1808
|
-
text: "Extract attribute from element",
|
|
1809
|
-
screenshotId,
|
|
1810
|
-
result: error
|
|
1811
|
-
? {
|
|
1812
|
-
status: "FAILED",
|
|
1813
|
-
startTime,
|
|
1814
|
-
endTime,
|
|
1815
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1816
|
-
}
|
|
1817
|
-
: {
|
|
1818
|
-
status: "PASSED",
|
|
1819
|
-
startTime,
|
|
1820
|
-
endTime,
|
|
1821
|
-
},
|
|
1822
|
-
info: info,
|
|
1823
|
-
});
|
|
1877
|
+
_commandFinally(state, this);
|
|
1824
1878
|
}
|
|
1825
1879
|
}
|
|
1826
1880
|
async extractEmailData(emailAddress, options, world) {
|
|
@@ -1897,7 +1951,8 @@ class StableBrowser {
|
|
|
1897
1951
|
catch (e) {
|
|
1898
1952
|
errorCount++;
|
|
1899
1953
|
if (errorCount > 3) {
|
|
1900
|
-
throw e;
|
|
1954
|
+
// throw e;
|
|
1955
|
+
await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
|
|
1901
1956
|
}
|
|
1902
1957
|
// ignore
|
|
1903
1958
|
}
|
|
@@ -2008,11 +2063,12 @@ class StableBrowser {
|
|
|
2008
2063
|
info.screenshotPath = screenshotPath;
|
|
2009
2064
|
Object.assign(e, { info: info });
|
|
2010
2065
|
error = e;
|
|
2011
|
-
throw e;
|
|
2066
|
+
// throw e;
|
|
2067
|
+
await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
|
|
2012
2068
|
}
|
|
2013
2069
|
finally {
|
|
2014
2070
|
const endTime = Date.now();
|
|
2015
|
-
|
|
2071
|
+
_reportToWorld(world, {
|
|
2016
2072
|
type: Types.VERIFY_PAGE_PATH,
|
|
2017
2073
|
text: "Verify page path",
|
|
2018
2074
|
screenshotId,
|
|
@@ -2021,7 +2077,7 @@ class StableBrowser {
|
|
|
2021
2077
|
status: "FAILED",
|
|
2022
2078
|
startTime,
|
|
2023
2079
|
endTime,
|
|
2024
|
-
message: error
|
|
2080
|
+
message: error?.message,
|
|
2025
2081
|
}
|
|
2026
2082
|
: {
|
|
2027
2083
|
status: "PASSED",
|
|
@@ -2034,52 +2090,58 @@ class StableBrowser {
|
|
|
2034
2090
|
}
|
|
2035
2091
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2036
2092
|
text = unEscapeString(text);
|
|
2037
|
-
const
|
|
2093
|
+
const state = {
|
|
2094
|
+
text_search: text,
|
|
2095
|
+
options,
|
|
2096
|
+
world,
|
|
2097
|
+
locate: false,
|
|
2098
|
+
scroll: false,
|
|
2099
|
+
highlight: false,
|
|
2100
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
2101
|
+
text: `Verify text exists in page`,
|
|
2102
|
+
operation: "verifyTextExistInPage",
|
|
2103
|
+
log: "***** verify text " + text + " exists in page *****\n",
|
|
2104
|
+
};
|
|
2038
2105
|
const timeout = this._getLoadTimeout(options);
|
|
2039
|
-
let error = null;
|
|
2040
|
-
let screenshotId = null;
|
|
2041
|
-
let screenshotPath = null;
|
|
2042
2106
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2043
|
-
const info = {};
|
|
2044
|
-
info.log = "***** verify text " + text + " exists in page *****\n";
|
|
2045
|
-
info.operation = "verifyTextExistInPage";
|
|
2046
2107
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
2047
2108
|
if (newValue !== text) {
|
|
2048
2109
|
this.logger.info(text + "=" + newValue);
|
|
2049
2110
|
text = newValue;
|
|
2050
2111
|
}
|
|
2051
|
-
info.text = text;
|
|
2052
2112
|
let dateAlternatives = findDateAlternatives(text);
|
|
2053
2113
|
let numberAlternatives = findNumberAlternatives(text);
|
|
2054
2114
|
try {
|
|
2115
|
+
await _preCommand(state, this);
|
|
2116
|
+
state.info.text = text;
|
|
2055
2117
|
while (true) {
|
|
2056
2118
|
const frames = this.page.frames();
|
|
2057
2119
|
let results = [];
|
|
2058
2120
|
for (let i = 0; i < frames.length; i++) {
|
|
2059
2121
|
if (dateAlternatives.date) {
|
|
2060
2122
|
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2061
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "
|
|
2123
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2062
2124
|
result.frame = frames[i];
|
|
2063
2125
|
results.push(result);
|
|
2064
2126
|
}
|
|
2065
2127
|
}
|
|
2066
2128
|
else if (numberAlternatives.number) {
|
|
2067
2129
|
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2068
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "
|
|
2130
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2069
2131
|
result.frame = frames[i];
|
|
2070
2132
|
results.push(result);
|
|
2071
2133
|
}
|
|
2072
2134
|
}
|
|
2073
2135
|
else {
|
|
2074
|
-
const result = await this._locateElementByText(frames[i], text, "
|
|
2136
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2075
2137
|
result.frame = frames[i];
|
|
2076
2138
|
results.push(result);
|
|
2077
2139
|
}
|
|
2078
2140
|
}
|
|
2079
|
-
info.results = results;
|
|
2141
|
+
state.info.results = results;
|
|
2080
2142
|
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2081
2143
|
if (resultWithElementsFound.length === 0) {
|
|
2082
|
-
if (Date.now() - startTime > timeout) {
|
|
2144
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2083
2145
|
throw new Error(`Text ${text} not found in page`);
|
|
2084
2146
|
}
|
|
2085
2147
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -2091,44 +2153,89 @@ class StableBrowser {
|
|
|
2091
2153
|
await this._highlightElements(frame, dataAttribute);
|
|
2092
2154
|
const element = await frame.$(dataAttribute);
|
|
2093
2155
|
if (element) {
|
|
2094
|
-
await this.scrollIfNeeded(element, info);
|
|
2156
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2095
2157
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2096
2158
|
}
|
|
2097
2159
|
}
|
|
2098
|
-
|
|
2099
|
-
return info;
|
|
2160
|
+
await _screenshot(state, this);
|
|
2161
|
+
return state.info;
|
|
2100
2162
|
}
|
|
2101
2163
|
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2102
2164
|
}
|
|
2103
2165
|
catch (e) {
|
|
2104
|
-
|
|
2105
|
-
this.logger.error("verify text exist in page failed " + info.log);
|
|
2106
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2107
|
-
info.screenshotPath = screenshotPath;
|
|
2108
|
-
Object.assign(e, { info: info });
|
|
2109
|
-
error = e;
|
|
2110
|
-
throw e;
|
|
2166
|
+
await _commandError(state, e, this);
|
|
2111
2167
|
}
|
|
2112
2168
|
finally {
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2169
|
+
_commandFinally(state, this);
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
async waitForTextToDisappear(text, options = {}, world = null) {
|
|
2173
|
+
text = unEscapeString(text);
|
|
2174
|
+
const state = {
|
|
2175
|
+
text_search: text,
|
|
2176
|
+
options,
|
|
2177
|
+
world,
|
|
2178
|
+
locate: false,
|
|
2179
|
+
scroll: false,
|
|
2180
|
+
highlight: false,
|
|
2181
|
+
type: Types.WAIT_FOR_TEXT_TO_DISAPPEAR,
|
|
2182
|
+
text: `Verify text does not exist in page`,
|
|
2183
|
+
operation: "verifyTextNotExistInPage",
|
|
2184
|
+
log: "***** verify text " + text + " does not exist in page *****\n",
|
|
2185
|
+
};
|
|
2186
|
+
const timeout = this._getLoadTimeout(options);
|
|
2187
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2188
|
+
const newValue = await this._replaceWithLocalData(text, world);
|
|
2189
|
+
if (newValue !== text) {
|
|
2190
|
+
this.logger.info(text + "=" + newValue);
|
|
2191
|
+
text = newValue;
|
|
2192
|
+
}
|
|
2193
|
+
let dateAlternatives = findDateAlternatives(text);
|
|
2194
|
+
let numberAlternatives = findNumberAlternatives(text);
|
|
2195
|
+
try {
|
|
2196
|
+
await _preCommand(state, this);
|
|
2197
|
+
state.info.text = text;
|
|
2198
|
+
while (true) {
|
|
2199
|
+
const frames = this.page.frames();
|
|
2200
|
+
let results = [];
|
|
2201
|
+
for (let i = 0; i < frames.length; i++) {
|
|
2202
|
+
if (dateAlternatives.date) {
|
|
2203
|
+
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2204
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2205
|
+
result.frame = frames[i];
|
|
2206
|
+
results.push(result);
|
|
2207
|
+
}
|
|
2124
2208
|
}
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2209
|
+
else if (numberAlternatives.number) {
|
|
2210
|
+
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2211
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2212
|
+
result.frame = frames[i];
|
|
2213
|
+
results.push(result);
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
else {
|
|
2217
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2218
|
+
result.frame = frames[i];
|
|
2219
|
+
results.push(result);
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
state.info.results = results;
|
|
2223
|
+
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2224
|
+
if (resultWithElementsFound.length === 0) {
|
|
2225
|
+
await _screenshot(state, this);
|
|
2226
|
+
return state.info;
|
|
2227
|
+
}
|
|
2228
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2229
|
+
throw new Error(`Text ${text} found in page`);
|
|
2230
|
+
}
|
|
2231
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
catch (e) {
|
|
2235
|
+
await _commandError(state, e, this);
|
|
2236
|
+
}
|
|
2237
|
+
finally {
|
|
2238
|
+
_commandFinally(state, this);
|
|
2132
2239
|
}
|
|
2133
2240
|
}
|
|
2134
2241
|
_getServerUrl() {
|
|
@@ -2191,11 +2298,12 @@ class StableBrowser {
|
|
|
2191
2298
|
info.screenshotPath = screenshotPath;
|
|
2192
2299
|
Object.assign(e, { info: info });
|
|
2193
2300
|
error = e;
|
|
2194
|
-
throw e;
|
|
2301
|
+
// throw e;
|
|
2302
|
+
await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
|
|
2195
2303
|
}
|
|
2196
2304
|
finally {
|
|
2197
2305
|
const endTime = Date.now();
|
|
2198
|
-
|
|
2306
|
+
_reportToWorld(world, {
|
|
2199
2307
|
type: Types.VERIFY_VISUAL,
|
|
2200
2308
|
text: "Visual verification",
|
|
2201
2309
|
screenshotId,
|
|
@@ -2204,7 +2312,7 @@ class StableBrowser {
|
|
|
2204
2312
|
status: "FAILED",
|
|
2205
2313
|
startTime,
|
|
2206
2314
|
endTime,
|
|
2207
|
-
message: error
|
|
2315
|
+
message: error?.message,
|
|
2208
2316
|
}
|
|
2209
2317
|
: {
|
|
2210
2318
|
status: "PASSED",
|
|
@@ -2258,11 +2366,12 @@ class StableBrowser {
|
|
|
2258
2366
|
info.screenshotPath = screenshotPath;
|
|
2259
2367
|
Object.assign(e, { info: info });
|
|
2260
2368
|
error = e;
|
|
2261
|
-
throw e;
|
|
2369
|
+
// throw e;
|
|
2370
|
+
await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
|
|
2262
2371
|
}
|
|
2263
2372
|
finally {
|
|
2264
2373
|
const endTime = Date.now();
|
|
2265
|
-
|
|
2374
|
+
_reportToWorld(world, {
|
|
2266
2375
|
element_name: selectors.element_name,
|
|
2267
2376
|
type: Types.GET_TABLE_DATA,
|
|
2268
2377
|
text: "Get table data",
|
|
@@ -2272,7 +2381,7 @@ class StableBrowser {
|
|
|
2272
2381
|
status: "FAILED",
|
|
2273
2382
|
startTime,
|
|
2274
2383
|
endTime,
|
|
2275
|
-
message: error
|
|
2384
|
+
message: error?.message,
|
|
2276
2385
|
}
|
|
2277
2386
|
: {
|
|
2278
2387
|
status: "PASSED",
|
|
@@ -2423,11 +2532,12 @@ class StableBrowser {
|
|
|
2423
2532
|
info.screenshotPath = screenshotPath;
|
|
2424
2533
|
Object.assign(e, { info: info });
|
|
2425
2534
|
error = e;
|
|
2426
|
-
throw e;
|
|
2535
|
+
// throw e;
|
|
2536
|
+
await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
|
|
2427
2537
|
}
|
|
2428
2538
|
finally {
|
|
2429
2539
|
const endTime = Date.now();
|
|
2430
|
-
|
|
2540
|
+
_reportToWorld(world, {
|
|
2431
2541
|
element_name: selectors.element_name,
|
|
2432
2542
|
type: Types.ANALYZE_TABLE,
|
|
2433
2543
|
text: "Analyze table",
|
|
@@ -2437,7 +2547,7 @@ class StableBrowser {
|
|
|
2437
2547
|
status: "FAILED",
|
|
2438
2548
|
startTime,
|
|
2439
2549
|
endTime,
|
|
2440
|
-
message: error
|
|
2550
|
+
message: error?.message,
|
|
2441
2551
|
}
|
|
2442
2552
|
: {
|
|
2443
2553
|
status: "PASSED",
|
|
@@ -2449,27 +2559,7 @@ class StableBrowser {
|
|
|
2449
2559
|
}
|
|
2450
2560
|
}
|
|
2451
2561
|
async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
|
|
2452
|
-
|
|
2453
|
-
return value;
|
|
2454
|
-
}
|
|
2455
|
-
// find all the accurance of {{(.*?)}} and replace with the value
|
|
2456
|
-
let regex = /{{(.*?)}}/g;
|
|
2457
|
-
let matches = value.match(regex);
|
|
2458
|
-
if (matches) {
|
|
2459
|
-
const testData = this.getTestData(world);
|
|
2460
|
-
for (let i = 0; i < matches.length; i++) {
|
|
2461
|
-
let match = matches[i];
|
|
2462
|
-
let key = match.substring(2, match.length - 2);
|
|
2463
|
-
let newValue = objectPath.get(testData, key, null);
|
|
2464
|
-
if (newValue !== null) {
|
|
2465
|
-
value = value.replace(match, newValue);
|
|
2466
|
-
}
|
|
2467
|
-
}
|
|
2468
|
-
}
|
|
2469
|
-
if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
|
|
2470
|
-
return await decrypt(value, null, totpWait);
|
|
2471
|
-
}
|
|
2472
|
-
return value;
|
|
2562
|
+
return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
|
|
2473
2563
|
}
|
|
2474
2564
|
_getLoadTimeout(options) {
|
|
2475
2565
|
let timeout = 15000;
|
|
@@ -2520,7 +2610,7 @@ class StableBrowser {
|
|
|
2520
2610
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2521
2611
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2522
2612
|
const endTime = Date.now();
|
|
2523
|
-
|
|
2613
|
+
_reportToWorld(world, {
|
|
2524
2614
|
type: Types.GET_PAGE_STATUS,
|
|
2525
2615
|
text: "Wait for page load",
|
|
2526
2616
|
screenshotId,
|
|
@@ -2529,7 +2619,7 @@ class StableBrowser {
|
|
|
2529
2619
|
status: "FAILED",
|
|
2530
2620
|
startTime,
|
|
2531
2621
|
endTime,
|
|
2532
|
-
message: error
|
|
2622
|
+
message: error?.message,
|
|
2533
2623
|
}
|
|
2534
2624
|
: {
|
|
2535
2625
|
status: "PASSED",
|
|
@@ -2540,41 +2630,35 @@ class StableBrowser {
|
|
|
2540
2630
|
}
|
|
2541
2631
|
}
|
|
2542
2632
|
async closePage(options = {}, world = null) {
|
|
2543
|
-
const
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2633
|
+
const state = {
|
|
2634
|
+
options,
|
|
2635
|
+
world,
|
|
2636
|
+
locate: false,
|
|
2637
|
+
scroll: false,
|
|
2638
|
+
highlight: false,
|
|
2639
|
+
type: Types.CLOSE_PAGE,
|
|
2640
|
+
text: `Close page`,
|
|
2641
|
+
operation: "closePage",
|
|
2642
|
+
log: "***** close page *****\n",
|
|
2643
|
+
throwError: false,
|
|
2644
|
+
};
|
|
2548
2645
|
try {
|
|
2646
|
+
await _preCommand(state, this);
|
|
2549
2647
|
await this.page.close();
|
|
2550
2648
|
}
|
|
2551
2649
|
catch (e) {
|
|
2552
2650
|
console.log(".");
|
|
2651
|
+
await _commandError(state, e, this);
|
|
2553
2652
|
}
|
|
2554
2653
|
finally {
|
|
2555
|
-
|
|
2556
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2557
|
-
const endTime = Date.now();
|
|
2558
|
-
this._reportToWorld(world, {
|
|
2559
|
-
type: Types.CLOSE_PAGE,
|
|
2560
|
-
text: "close page",
|
|
2561
|
-
screenshotId,
|
|
2562
|
-
result: error
|
|
2563
|
-
? {
|
|
2564
|
-
status: "FAILED",
|
|
2565
|
-
startTime,
|
|
2566
|
-
endTime,
|
|
2567
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2568
|
-
}
|
|
2569
|
-
: {
|
|
2570
|
-
status: "PASSED",
|
|
2571
|
-
startTime,
|
|
2572
|
-
endTime,
|
|
2573
|
-
},
|
|
2574
|
-
info: info,
|
|
2575
|
-
});
|
|
2654
|
+
_commandFinally(state, this);
|
|
2576
2655
|
}
|
|
2577
2656
|
}
|
|
2657
|
+
saveTestDataAsGlobal(options, world) {
|
|
2658
|
+
const dataFile = this._getDataFile(world);
|
|
2659
|
+
process.env.GLOBAL_TEST_DATA_FILE = dataFile;
|
|
2660
|
+
this.logger.info("Save the scenario test data as global for the following scenarios.");
|
|
2661
|
+
}
|
|
2578
2662
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
2579
2663
|
const startTime = Date.now();
|
|
2580
2664
|
let error = null;
|
|
@@ -2592,12 +2676,13 @@ class StableBrowser {
|
|
|
2592
2676
|
}
|
|
2593
2677
|
catch (e) {
|
|
2594
2678
|
console.log(".");
|
|
2679
|
+
await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
|
|
2595
2680
|
}
|
|
2596
2681
|
finally {
|
|
2597
2682
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2598
2683
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2599
2684
|
const endTime = Date.now();
|
|
2600
|
-
|
|
2685
|
+
_reportToWorld(world, {
|
|
2601
2686
|
type: Types.SET_VIEWPORT,
|
|
2602
2687
|
text: "set viewport size to " + width + "x" + hight,
|
|
2603
2688
|
screenshotId,
|
|
@@ -2606,7 +2691,7 @@ class StableBrowser {
|
|
|
2606
2691
|
status: "FAILED",
|
|
2607
2692
|
startTime,
|
|
2608
2693
|
endTime,
|
|
2609
|
-
message: error
|
|
2694
|
+
message: error?.message,
|
|
2610
2695
|
}
|
|
2611
2696
|
: {
|
|
2612
2697
|
status: "PASSED",
|
|
@@ -2628,12 +2713,13 @@ class StableBrowser {
|
|
|
2628
2713
|
}
|
|
2629
2714
|
catch (e) {
|
|
2630
2715
|
console.log(".");
|
|
2716
|
+
await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
|
|
2631
2717
|
}
|
|
2632
2718
|
finally {
|
|
2633
2719
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2634
2720
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2635
2721
|
const endTime = Date.now();
|
|
2636
|
-
|
|
2722
|
+
_reportToWorld(world, {
|
|
2637
2723
|
type: Types.GET_PAGE_STATUS,
|
|
2638
2724
|
text: "page relaod",
|
|
2639
2725
|
screenshotId,
|
|
@@ -2642,7 +2728,7 @@ class StableBrowser {
|
|
|
2642
2728
|
status: "FAILED",
|
|
2643
2729
|
startTime,
|
|
2644
2730
|
endTime,
|
|
2645
|
-
message: error
|
|
2731
|
+
message: error?.message,
|
|
2646
2732
|
}
|
|
2647
2733
|
: {
|
|
2648
2734
|
status: "PASSED",
|
|
@@ -2669,11 +2755,37 @@ class StableBrowser {
|
|
|
2669
2755
|
console.log("#-#");
|
|
2670
2756
|
}
|
|
2671
2757
|
}
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2758
|
+
async beforeStep(world, step) {
|
|
2759
|
+
this.stepName = step.pickleStep.text;
|
|
2760
|
+
this.logger.info("step: " + this.stepName);
|
|
2761
|
+
if (this.stepIndex === undefined) {
|
|
2762
|
+
this.stepIndex = 0;
|
|
2763
|
+
}
|
|
2764
|
+
else {
|
|
2765
|
+
this.stepIndex++;
|
|
2766
|
+
}
|
|
2767
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2768
|
+
if (this.context.browserObject.context) {
|
|
2769
|
+
await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
if (this.tags === null && step && step.pickle && step.pickle.tags) {
|
|
2773
|
+
this.tags = step.pickle.tags.map((tag) => tag.name);
|
|
2774
|
+
// check if @global_test_data tag is present
|
|
2775
|
+
if (this.tags.includes("@global_test_data")) {
|
|
2776
|
+
this.saveTestDataAsGlobal({}, world);
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
async afterStep(world, step) {
|
|
2781
|
+
this.stepName = null;
|
|
2782
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2783
|
+
if (this.context.browserObject.context) {
|
|
2784
|
+
await this.context.browserObject.context.tracing.stopChunk({
|
|
2785
|
+
path: path.join(this.context.browserObject.traceFolder, `trace-${this.stepIndex}.zip`),
|
|
2786
|
+
});
|
|
2787
|
+
}
|
|
2675
2788
|
}
|
|
2676
|
-
world.attach(JSON.stringify(properties), { mediaType: "application/json" });
|
|
2677
2789
|
}
|
|
2678
2790
|
}
|
|
2679
2791
|
function createTimedPromise(promise, label) {
|