automation_model 1.0.462-dev → 1.0.462-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 +181 -7
- package/lib/api.js.map +1 -1
- package/lib/auto_page.js +32 -16
- package/lib/auto_page.js.map +1 -1
- package/lib/axe/axe.mini.js +12 -0
- package/lib/browser_manager.d.ts +5 -3
- package/lib/browser_manager.js +44 -19
- package/lib/browser_manager.js.map +1 -1
- package/lib/command_common.d.ts +6 -0
- package/lib/command_common.js +138 -0
- package/lib/command_common.js.map +1 -0
- package/lib/environment.js +5 -3
- package/lib/environment.js.map +1 -1
- package/lib/error-messages.d.ts +6 -0
- package/lib/error-messages.js +182 -0
- package/lib/error-messages.js.map +1 -0
- package/lib/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 +18 -3
- package/lib/init_browser.js.map +1 -1
- package/lib/locate_element.d.ts +7 -0
- package/lib/locate_element.js +213 -0
- package/lib/locate_element.js.map +1 -0
- package/lib/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 +26 -19
- package/lib/stable_browser.js +647 -779
- 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 +4 -5
package/lib/stable_browser.js
CHANGED
|
@@ -10,12 +10,14 @@ import { getDateTimeValue } from "./date_time.js";
|
|
|
10
10
|
import drawRectangle from "./drawRect.js";
|
|
11
11
|
//import { closeUnexpectedPopups } from "./popups.js";
|
|
12
12
|
import { getTableCells, getTableData } from "./table_analyze.js";
|
|
13
|
-
import
|
|
14
|
-
import { decrypt } from "./utils.js";
|
|
13
|
+
import { maskValue, replaceWithLocalTestData } from "./utils.js";
|
|
15
14
|
import csv from "csv-parser";
|
|
16
15
|
import { Readable } from "node:stream";
|
|
17
16
|
import readline from "readline";
|
|
18
17
|
import { getContext } from "./init_browser.js";
|
|
18
|
+
import { locate_element } from "./locate_element.js";
|
|
19
|
+
import { _commandError, _commandFinally, _preCommand, _validateSelectors, _screenshot, _reportToWorld, } from "./command_common.js";
|
|
20
|
+
import { registerDownloadEvent, registerNetworkEvents } from "./network.js";
|
|
19
21
|
const Types = {
|
|
20
22
|
CLICK: "click_element",
|
|
21
23
|
NAVIGATE: "navigate",
|
|
@@ -42,20 +44,27 @@ const Types = {
|
|
|
42
44
|
VERIFY_VISUAL: "verify_visual",
|
|
43
45
|
LOAD_DATA: "load_data",
|
|
44
46
|
SET_INPUT: "set_input",
|
|
47
|
+
WAIT_FOR_TEXT_TO_DISAPPEAR: "wait_for_text_to_disappear",
|
|
45
48
|
};
|
|
46
49
|
export const apps = {};
|
|
47
50
|
class StableBrowser {
|
|
51
|
+
browser;
|
|
52
|
+
page;
|
|
53
|
+
logger;
|
|
54
|
+
context;
|
|
55
|
+
world;
|
|
56
|
+
project_path = null;
|
|
57
|
+
webLogFile = null;
|
|
58
|
+
networkLogger = null;
|
|
59
|
+
configuration = null;
|
|
60
|
+
appName = "main";
|
|
61
|
+
tags = null;
|
|
48
62
|
constructor(browser, page, logger = null, context = null, world = null) {
|
|
49
63
|
this.browser = browser;
|
|
50
64
|
this.page = page;
|
|
51
65
|
this.logger = logger;
|
|
52
66
|
this.context = context;
|
|
53
67
|
this.world = world;
|
|
54
|
-
this.project_path = null;
|
|
55
|
-
this.webLogFile = null;
|
|
56
|
-
this.networkLogger = null;
|
|
57
|
-
this.configuration = null;
|
|
58
|
-
this.appName = "main";
|
|
59
68
|
if (!this.logger) {
|
|
60
69
|
this.logger = console;
|
|
61
70
|
}
|
|
@@ -80,11 +89,13 @@ class StableBrowser {
|
|
|
80
89
|
catch (e) {
|
|
81
90
|
this.logger.error("unable to read ai_config.json");
|
|
82
91
|
}
|
|
83
|
-
context.pageLoading = { status: false };
|
|
84
|
-
context.pages = [this.page];
|
|
85
92
|
const logFolder = path.join(this.project_path, "logs", "web");
|
|
86
93
|
this.world = world;
|
|
94
|
+
context.pages = [this.page];
|
|
95
|
+
context.pageLoading = { status: false };
|
|
87
96
|
this.registerEventListeners(this.context);
|
|
97
|
+
registerNetworkEvents(this.world, this, this.context, this.page);
|
|
98
|
+
registerDownloadEvent(this.page, this.world, this.context);
|
|
88
99
|
}
|
|
89
100
|
registerEventListeners(context) {
|
|
90
101
|
this.registerConsoleLogListener(this.page, context);
|
|
@@ -93,10 +104,17 @@ class StableBrowser {
|
|
|
93
104
|
context.pageLoading = { status: false };
|
|
94
105
|
}
|
|
95
106
|
context.playContext.on("page", async function (page) {
|
|
107
|
+
if (this.configuration && this.configuration.closePopups === true) {
|
|
108
|
+
console.log("close unexpected popups");
|
|
109
|
+
await page.close();
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
96
112
|
context.pageLoading.status = true;
|
|
97
113
|
this.page = page;
|
|
98
114
|
context.page = page;
|
|
99
115
|
context.pages.push(page);
|
|
116
|
+
registerNetworkEvents(this.world, this, context, this.page);
|
|
117
|
+
registerDownloadEvent(this.page, this.world, context);
|
|
100
118
|
page.on("close", async () => {
|
|
101
119
|
if (this.context && this.context.pages && this.context.pages.length > 1) {
|
|
102
120
|
this.context.pages.pop();
|
|
@@ -126,9 +144,9 @@ class StableBrowser {
|
|
|
126
144
|
if (this.appName === appName) {
|
|
127
145
|
return;
|
|
128
146
|
}
|
|
129
|
-
let
|
|
147
|
+
let navigate = false;
|
|
130
148
|
if (!apps[appName]) {
|
|
131
|
-
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this);
|
|
149
|
+
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this, -1, this.context.reportFolder);
|
|
132
150
|
newContextCreated = true;
|
|
133
151
|
apps[appName] = {
|
|
134
152
|
context: newContext,
|
|
@@ -141,8 +159,7 @@ class StableBrowser {
|
|
|
141
159
|
this._copyContext(apps[appName], this);
|
|
142
160
|
apps[this.appName] = tempContext;
|
|
143
161
|
this.appName = appName;
|
|
144
|
-
if (
|
|
145
|
-
this.registerEventListeners(this.context);
|
|
162
|
+
if (navigate) {
|
|
146
163
|
await this.goto(this.context.environment.baseUrl);
|
|
147
164
|
await this.waitForPageLoad();
|
|
148
165
|
}
|
|
@@ -168,7 +185,6 @@ class StableBrowser {
|
|
|
168
185
|
this.context.webLogger = [];
|
|
169
186
|
}
|
|
170
187
|
page.on("console", async (msg) => {
|
|
171
|
-
var _a;
|
|
172
188
|
const obj = {
|
|
173
189
|
type: msg.type(),
|
|
174
190
|
text: msg.text(),
|
|
@@ -176,7 +192,9 @@ class StableBrowser {
|
|
|
176
192
|
time: new Date().toISOString(),
|
|
177
193
|
};
|
|
178
194
|
this.context.webLogger.push(obj);
|
|
179
|
-
|
|
195
|
+
if (msg.type() === "error") {
|
|
196
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
|
|
197
|
+
}
|
|
180
198
|
});
|
|
181
199
|
}
|
|
182
200
|
registerRequestListener(page, context, logFile) {
|
|
@@ -184,7 +202,6 @@ class StableBrowser {
|
|
|
184
202
|
this.context.networkLogger = [];
|
|
185
203
|
}
|
|
186
204
|
page.on("request", async (data) => {
|
|
187
|
-
var _a;
|
|
188
205
|
const startTime = new Date().getTime();
|
|
189
206
|
try {
|
|
190
207
|
const pageUrl = new URL(page.url());
|
|
@@ -209,10 +226,10 @@ class StableBrowser {
|
|
|
209
226
|
startTime,
|
|
210
227
|
};
|
|
211
228
|
context.networkLogger.push(obj);
|
|
212
|
-
|
|
229
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
|
|
213
230
|
}
|
|
214
231
|
catch (error) {
|
|
215
|
-
console.error("Error in request listener", error);
|
|
232
|
+
// console.error("Error in request listener", error);
|
|
216
233
|
context.networkLogger.push({
|
|
217
234
|
error: "not able to listen",
|
|
218
235
|
message: error.message,
|
|
@@ -234,20 +251,6 @@ class StableBrowser {
|
|
|
234
251
|
timeout: 60000,
|
|
235
252
|
});
|
|
236
253
|
}
|
|
237
|
-
_validateSelectors(selectors) {
|
|
238
|
-
if (!selectors) {
|
|
239
|
-
throw new Error("selectors is null");
|
|
240
|
-
}
|
|
241
|
-
if (!selectors.locators) {
|
|
242
|
-
throw new Error("selectors.locators is null");
|
|
243
|
-
}
|
|
244
|
-
if (!Array.isArray(selectors.locators)) {
|
|
245
|
-
throw new Error("selectors.locators expected to be array");
|
|
246
|
-
}
|
|
247
|
-
if (selectors.locators.length === 0) {
|
|
248
|
-
throw new Error("selectors.locators expected to be non empty array");
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
254
|
_fixUsingParams(text, _params) {
|
|
252
255
|
if (!_params || typeof text !== "string") {
|
|
253
256
|
return text;
|
|
@@ -315,7 +318,7 @@ class StableBrowser {
|
|
|
315
318
|
locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
|
|
316
319
|
}
|
|
317
320
|
}
|
|
318
|
-
if (locator
|
|
321
|
+
if (locator?.engine) {
|
|
319
322
|
if (locator.engine === "css") {
|
|
320
323
|
locatorReturn = scope.locator(locator.selector);
|
|
321
324
|
}
|
|
@@ -339,7 +342,7 @@ class StableBrowser {
|
|
|
339
342
|
if (css && css.locator) {
|
|
340
343
|
css = css.locator;
|
|
341
344
|
}
|
|
342
|
-
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "
|
|
345
|
+
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*:not(script, style, head)", false, false, _params);
|
|
343
346
|
if (result.elementCount === 0) {
|
|
344
347
|
return;
|
|
345
348
|
}
|
|
@@ -354,7 +357,7 @@ class StableBrowser {
|
|
|
354
357
|
}
|
|
355
358
|
async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
|
|
356
359
|
//const stringifyText = JSON.stringify(text);
|
|
357
|
-
return await scope.evaluate(([text, tag, regex, partial]) => {
|
|
360
|
+
return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
|
|
358
361
|
function isParent(parent, child) {
|
|
359
362
|
let currentNode = child.parentNode;
|
|
360
363
|
while (currentNode !== null) {
|
|
@@ -391,7 +394,7 @@ class StableBrowser {
|
|
|
391
394
|
}
|
|
392
395
|
document.collectAllShadowDomElements = collectAllShadowDomElements;
|
|
393
396
|
if (!tag) {
|
|
394
|
-
tag = "
|
|
397
|
+
tag = "*:not(script, style, head)";
|
|
395
398
|
}
|
|
396
399
|
let regexpSearch = document.getRegex(text);
|
|
397
400
|
if (regexpSearch) {
|
|
@@ -473,19 +476,39 @@ class StableBrowser {
|
|
|
473
476
|
}, [text1, tag1, regex1, partial1]);
|
|
474
477
|
}
|
|
475
478
|
async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
|
|
479
|
+
if (!info) {
|
|
480
|
+
info = {};
|
|
481
|
+
}
|
|
482
|
+
if (!info.failCause) {
|
|
483
|
+
info.failCause = {};
|
|
484
|
+
}
|
|
485
|
+
if (!info.log) {
|
|
486
|
+
info.log = "";
|
|
487
|
+
}
|
|
476
488
|
let locatorSearch = selectorHierarchy[index];
|
|
489
|
+
try {
|
|
490
|
+
locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
|
|
491
|
+
}
|
|
492
|
+
catch (e) {
|
|
493
|
+
console.error(e);
|
|
494
|
+
}
|
|
477
495
|
//info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
|
|
478
496
|
let locator = null;
|
|
479
497
|
if (locatorSearch.climb && locatorSearch.climb >= 0) {
|
|
480
498
|
let locatorString = await this._locateElmentByTextClimbCss(scope, locatorSearch.text, locatorSearch.climb, locatorSearch.css, _params);
|
|
481
499
|
if (!locatorString) {
|
|
500
|
+
info.failCause.textNotFound = true;
|
|
501
|
+
info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
|
|
482
502
|
return;
|
|
483
503
|
}
|
|
484
504
|
locator = this._getLocator({ css: locatorString }, scope, _params);
|
|
485
505
|
}
|
|
486
506
|
else if (locatorSearch.text) {
|
|
487
|
-
let
|
|
507
|
+
let text = this._fixUsingParams(locatorSearch.text, _params);
|
|
508
|
+
let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
|
|
488
509
|
if (result.elementCount === 0) {
|
|
510
|
+
info.failCause.textNotFound = true;
|
|
511
|
+
info.failCause.lastError = "failed to locate element by text: " + text;
|
|
489
512
|
return;
|
|
490
513
|
}
|
|
491
514
|
locatorSearch.css = "[data-blinq-id='blinq-id-" + result.randomToken + "']";
|
|
@@ -502,6 +525,9 @@ class StableBrowser {
|
|
|
502
525
|
// cssHref = true;
|
|
503
526
|
// }
|
|
504
527
|
let count = await locator.count();
|
|
528
|
+
if (count > 0 && !info.failCause.count) {
|
|
529
|
+
info.failCause.count = count;
|
|
530
|
+
}
|
|
505
531
|
//info.log += "total elements found " + count + "\n";
|
|
506
532
|
//let visibleCount = 0;
|
|
507
533
|
let visibleLocator = null;
|
|
@@ -519,6 +545,8 @@ class StableBrowser {
|
|
|
519
545
|
foundLocators.push(locator.nth(j));
|
|
520
546
|
}
|
|
521
547
|
else {
|
|
548
|
+
info.failCause.visible = visible;
|
|
549
|
+
info.failCause.enabled = enabled;
|
|
522
550
|
if (!info.printMessages) {
|
|
523
551
|
info.printMessages = {};
|
|
524
552
|
}
|
|
@@ -530,6 +558,11 @@ class StableBrowser {
|
|
|
530
558
|
}
|
|
531
559
|
}
|
|
532
560
|
async closeUnexpectedPopups(info, _params) {
|
|
561
|
+
if (!info) {
|
|
562
|
+
info = {};
|
|
563
|
+
info.failCause = {};
|
|
564
|
+
info.log = "";
|
|
565
|
+
}
|
|
533
566
|
if (this.configuration.popupHandlers && this.configuration.popupHandlers.length > 0) {
|
|
534
567
|
if (!info) {
|
|
535
568
|
info = {};
|
|
@@ -570,7 +603,10 @@ class StableBrowser {
|
|
|
570
603
|
}
|
|
571
604
|
return { rerun: false };
|
|
572
605
|
}
|
|
573
|
-
async _locate(selectors, info, _params, timeout
|
|
606
|
+
async _locate(selectors, info, _params, timeout) {
|
|
607
|
+
if (!timeout) {
|
|
608
|
+
timeout = 30000;
|
|
609
|
+
}
|
|
574
610
|
for (let i = 0; i < 3; i++) {
|
|
575
611
|
info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
|
|
576
612
|
for (let j = 0; j < selectors.locators.length; j++) {
|
|
@@ -584,35 +620,46 @@ class StableBrowser {
|
|
|
584
620
|
}
|
|
585
621
|
throw new Error("unable to locate element " + JSON.stringify(selectors));
|
|
586
622
|
}
|
|
587
|
-
async
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
623
|
+
async _findFrameScope(selectors, timeout = 30000, info) {
|
|
624
|
+
if (!info) {
|
|
625
|
+
info = {};
|
|
626
|
+
info.failCause = {};
|
|
627
|
+
info.log = "";
|
|
628
|
+
}
|
|
593
629
|
let scope = this.page;
|
|
630
|
+
if (selectors.frame) {
|
|
631
|
+
return selectors.frame;
|
|
632
|
+
}
|
|
594
633
|
if (selectors.iframe_src || selectors.frameLocators) {
|
|
595
|
-
const findFrame = (frame, framescope) => {
|
|
634
|
+
const findFrame = async (frame, framescope) => {
|
|
596
635
|
for (let i = 0; i < frame.selectors.length; i++) {
|
|
597
636
|
let frameLocator = frame.selectors[i];
|
|
598
637
|
if (frameLocator.css) {
|
|
599
|
-
|
|
638
|
+
let testframescope = framescope.frameLocator(frameLocator.css);
|
|
600
639
|
if (frameLocator.index) {
|
|
601
|
-
|
|
640
|
+
testframescope = framescope.nth(frameLocator.index);
|
|
641
|
+
}
|
|
642
|
+
try {
|
|
643
|
+
await testframescope.owner().evaluateHandle(() => true, null, {
|
|
644
|
+
timeout: 5000,
|
|
645
|
+
});
|
|
646
|
+
framescope = testframescope;
|
|
647
|
+
break;
|
|
648
|
+
}
|
|
649
|
+
catch (error) {
|
|
650
|
+
console.error("frame not found " + frameLocator.css);
|
|
602
651
|
}
|
|
603
|
-
break;
|
|
604
652
|
}
|
|
605
653
|
}
|
|
606
654
|
if (frame.children) {
|
|
607
|
-
return findFrame(frame.children, framescope);
|
|
655
|
+
return await findFrame(frame.children, framescope);
|
|
608
656
|
}
|
|
609
657
|
return framescope;
|
|
610
658
|
};
|
|
611
|
-
info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
|
|
612
659
|
while (true) {
|
|
613
660
|
let frameFound = false;
|
|
614
661
|
if (selectors.nestFrmLoc) {
|
|
615
|
-
scope = findFrame(selectors.nestFrmLoc, scope);
|
|
662
|
+
scope = await findFrame(selectors.nestFrmLoc, scope);
|
|
616
663
|
frameFound = true;
|
|
617
664
|
break;
|
|
618
665
|
}
|
|
@@ -632,6 +679,8 @@ class StableBrowser {
|
|
|
632
679
|
if (!scope) {
|
|
633
680
|
info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
|
|
634
681
|
if (performance.now() - startTime > timeout) {
|
|
682
|
+
info.failCause.iframeNotFound = true;
|
|
683
|
+
info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
|
|
635
684
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
636
685
|
}
|
|
637
686
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -641,6 +690,30 @@ class StableBrowser {
|
|
|
641
690
|
}
|
|
642
691
|
}
|
|
643
692
|
}
|
|
693
|
+
if (!scope) {
|
|
694
|
+
scope = this.page;
|
|
695
|
+
}
|
|
696
|
+
return scope;
|
|
697
|
+
}
|
|
698
|
+
async _getDocumentBody(selectors, timeout = 30000, info) {
|
|
699
|
+
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
700
|
+
return scope.evaluate(() => {
|
|
701
|
+
var bodyContent = document.body.innerHTML;
|
|
702
|
+
return bodyContent;
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
706
|
+
if (!info) {
|
|
707
|
+
info = {};
|
|
708
|
+
info.failCause = {};
|
|
709
|
+
info.log = "";
|
|
710
|
+
}
|
|
711
|
+
let highPriorityTimeout = 5000;
|
|
712
|
+
let visibleOnlyTimeout = 6000;
|
|
713
|
+
let startTime = performance.now();
|
|
714
|
+
let locatorsCount = 0;
|
|
715
|
+
//let arrayMode = Array.isArray(selectors);
|
|
716
|
+
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
644
717
|
let selectorsLocators = null;
|
|
645
718
|
selectorsLocators = selectors.locators;
|
|
646
719
|
// group selectors by priority
|
|
@@ -742,6 +815,8 @@ class StableBrowser {
|
|
|
742
815
|
}
|
|
743
816
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
744
817
|
info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
|
|
818
|
+
info.failCause.locatorNotFound = true;
|
|
819
|
+
info.failCause.lastError = "failed to locate unique element";
|
|
745
820
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
746
821
|
}
|
|
747
822
|
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
@@ -773,89 +848,129 @@ class StableBrowser {
|
|
|
773
848
|
});
|
|
774
849
|
result.locatorIndex = i;
|
|
775
850
|
}
|
|
851
|
+
if (foundLocators.length > 1) {
|
|
852
|
+
info.failCause.foundMultiple = true;
|
|
853
|
+
}
|
|
776
854
|
}
|
|
777
855
|
return result;
|
|
778
856
|
}
|
|
779
|
-
async
|
|
780
|
-
this._validateSelectors(selectors);
|
|
857
|
+
async simpleClick(elementDescription, _params, options = {}, world = null) {
|
|
781
858
|
const startTime = Date.now();
|
|
782
|
-
|
|
783
|
-
|
|
859
|
+
let timeout = 30000;
|
|
860
|
+
if (options && options.timeout) {
|
|
861
|
+
timeout = options.timeout;
|
|
784
862
|
}
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
863
|
+
while (true) {
|
|
864
|
+
try {
|
|
865
|
+
const result = await locate_element(this.context, elementDescription, "click");
|
|
866
|
+
if (result?.elementNumber >= 0) {
|
|
867
|
+
const selectors = {
|
|
868
|
+
frame: result?.frame,
|
|
869
|
+
locators: [
|
|
870
|
+
{
|
|
871
|
+
css: result?.css,
|
|
872
|
+
},
|
|
873
|
+
],
|
|
874
|
+
};
|
|
875
|
+
await this.click(selectors, _params, options, world);
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
catch (e) {
|
|
880
|
+
if (performance.now() - startTime > timeout) {
|
|
881
|
+
// throw e;
|
|
882
|
+
await _commandError({ text: "simpleClick", operation: "simpleClick", elementDescription, info: {} }, e, this);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
|
|
889
|
+
const startTime = Date.now();
|
|
890
|
+
let timeout = 30000;
|
|
891
|
+
if (options && options.timeout) {
|
|
892
|
+
timeout = options.timeout;
|
|
893
|
+
}
|
|
894
|
+
while (true) {
|
|
895
|
+
try {
|
|
896
|
+
const result = await locate_element(this.context, elementDescription, "fill", value);
|
|
897
|
+
if (result?.elementNumber >= 0) {
|
|
898
|
+
const selectors = {
|
|
899
|
+
frame: result?.frame,
|
|
900
|
+
locators: [
|
|
901
|
+
{
|
|
902
|
+
css: result?.css,
|
|
903
|
+
},
|
|
904
|
+
],
|
|
905
|
+
};
|
|
906
|
+
await this.clickType(selectors, value, false, _params, options, world);
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
catch (e) {
|
|
911
|
+
if (performance.now() - startTime > timeout) {
|
|
912
|
+
// throw e;
|
|
913
|
+
await _commandError({ text: "simpleClickType", operation: "simpleClickType", value, elementDescription, info: {} }, e, this);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
async click(selectors, _params, options = {}, world = null) {
|
|
920
|
+
const state = {
|
|
921
|
+
selectors,
|
|
922
|
+
_params,
|
|
923
|
+
options,
|
|
924
|
+
world,
|
|
925
|
+
text: "Click element",
|
|
926
|
+
type: Types.CLICK,
|
|
927
|
+
operation: "click",
|
|
928
|
+
log: "***** click on " + selectors.element_name + " *****\n",
|
|
929
|
+
};
|
|
792
930
|
try {
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
931
|
+
await _preCommand(state, this);
|
|
932
|
+
if (state.options && state.options.context) {
|
|
933
|
+
state.selectors.locators[0].text = state.options.context;
|
|
934
|
+
}
|
|
796
935
|
try {
|
|
797
|
-
await
|
|
798
|
-
await
|
|
799
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
936
|
+
await state.element.click();
|
|
937
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
800
938
|
}
|
|
801
939
|
catch (e) {
|
|
802
940
|
// await this.closeUnexpectedPopups();
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
await
|
|
806
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
941
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
942
|
+
await state.element.dispatchEvent("click");
|
|
943
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
807
944
|
}
|
|
808
945
|
await this.waitForPageLoad();
|
|
809
|
-
return info;
|
|
946
|
+
return state.info;
|
|
810
947
|
}
|
|
811
948
|
catch (e) {
|
|
812
|
-
|
|
813
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
814
|
-
info.screenshotPath = screenshotPath;
|
|
815
|
-
Object.assign(e, { info: info });
|
|
816
|
-
error = e;
|
|
817
|
-
throw e;
|
|
949
|
+
await _commandError(state, e, this);
|
|
818
950
|
}
|
|
819
951
|
finally {
|
|
820
|
-
|
|
821
|
-
this._reportToWorld(world, {
|
|
822
|
-
element_name: selectors.element_name,
|
|
823
|
-
type: Types.CLICK,
|
|
824
|
-
text: `Click element`,
|
|
825
|
-
screenshotId,
|
|
826
|
-
result: error
|
|
827
|
-
? {
|
|
828
|
-
status: "FAILED",
|
|
829
|
-
startTime,
|
|
830
|
-
endTime,
|
|
831
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
832
|
-
}
|
|
833
|
-
: {
|
|
834
|
-
status: "PASSED",
|
|
835
|
-
startTime,
|
|
836
|
-
endTime,
|
|
837
|
-
},
|
|
838
|
-
info: info,
|
|
839
|
-
});
|
|
952
|
+
_commandFinally(state, this);
|
|
840
953
|
}
|
|
841
954
|
}
|
|
842
955
|
async setCheck(selectors, checked = true, _params, options = {}, world = null) {
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
956
|
+
const state = {
|
|
957
|
+
selectors,
|
|
958
|
+
_params,
|
|
959
|
+
options,
|
|
960
|
+
world,
|
|
961
|
+
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
962
|
+
text: checked ? `Check element` : `Uncheck element`,
|
|
963
|
+
operation: "setCheck",
|
|
964
|
+
log: "***** check " + selectors.element_name + " *****\n",
|
|
965
|
+
};
|
|
853
966
|
try {
|
|
854
|
-
|
|
855
|
-
|
|
967
|
+
await _preCommand(state, this);
|
|
968
|
+
state.info.checked = checked;
|
|
969
|
+
// let element = await this._locate(selectors, info, _params);
|
|
970
|
+
// ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
856
971
|
try {
|
|
857
|
-
await this._highlightElements(element);
|
|
858
|
-
await element.setChecked(checked);
|
|
972
|
+
// await this._highlightElements(element);
|
|
973
|
+
await state.element.setChecked(checked);
|
|
859
974
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
860
975
|
}
|
|
861
976
|
catch (e) {
|
|
@@ -864,179 +979,108 @@ class StableBrowser {
|
|
|
864
979
|
}
|
|
865
980
|
else {
|
|
866
981
|
//await this.closeUnexpectedPopups();
|
|
867
|
-
info.log += "setCheck failed, will try again" + "\n";
|
|
868
|
-
element = await this._locate(selectors, info, _params);
|
|
869
|
-
await element.setChecked(checked, { timeout: 5000, force: true });
|
|
982
|
+
state.info.log += "setCheck failed, will try again" + "\n";
|
|
983
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
984
|
+
await state.element.setChecked(checked, { timeout: 5000, force: true });
|
|
870
985
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
871
986
|
}
|
|
872
987
|
}
|
|
873
988
|
await this.waitForPageLoad();
|
|
874
|
-
return info;
|
|
989
|
+
return state.info;
|
|
875
990
|
}
|
|
876
991
|
catch (e) {
|
|
877
|
-
|
|
878
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
879
|
-
info.screenshotPath = screenshotPath;
|
|
880
|
-
Object.assign(e, { info: info });
|
|
881
|
-
error = e;
|
|
882
|
-
throw e;
|
|
992
|
+
await _commandError(state, e, this);
|
|
883
993
|
}
|
|
884
994
|
finally {
|
|
885
|
-
|
|
886
|
-
this._reportToWorld(world, {
|
|
887
|
-
element_name: selectors.element_name,
|
|
888
|
-
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
889
|
-
text: checked ? `Check element` : `Uncheck element`,
|
|
890
|
-
screenshotId,
|
|
891
|
-
result: error
|
|
892
|
-
? {
|
|
893
|
-
status: "FAILED",
|
|
894
|
-
startTime,
|
|
895
|
-
endTime,
|
|
896
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
897
|
-
}
|
|
898
|
-
: {
|
|
899
|
-
status: "PASSED",
|
|
900
|
-
startTime,
|
|
901
|
-
endTime,
|
|
902
|
-
},
|
|
903
|
-
info: info,
|
|
904
|
-
});
|
|
995
|
+
_commandFinally(state, this);
|
|
905
996
|
}
|
|
906
997
|
}
|
|
907
998
|
async hover(selectors, _params, options = {}, world = null) {
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
999
|
+
const state = {
|
|
1000
|
+
selectors,
|
|
1001
|
+
_params,
|
|
1002
|
+
options,
|
|
1003
|
+
world,
|
|
1004
|
+
type: Types.HOVER,
|
|
1005
|
+
text: `Hover element`,
|
|
1006
|
+
operation: "hover",
|
|
1007
|
+
log: "***** hover " + selectors.element_name + " *****\n",
|
|
1008
|
+
};
|
|
917
1009
|
try {
|
|
918
|
-
|
|
919
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1010
|
+
await _preCommand(state, this);
|
|
920
1011
|
try {
|
|
921
|
-
await
|
|
922
|
-
await element.hover();
|
|
1012
|
+
await state.element.hover();
|
|
923
1013
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
924
1014
|
}
|
|
925
1015
|
catch (e) {
|
|
926
1016
|
//await this.closeUnexpectedPopups();
|
|
927
|
-
info.log += "hover failed, will try again" + "\n";
|
|
928
|
-
element = await this._locate(selectors, info, _params);
|
|
929
|
-
await element.hover({ timeout: 10000 });
|
|
1017
|
+
state.info.log += "hover failed, will try again" + "\n";
|
|
1018
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
1019
|
+
await state.element.hover({ timeout: 10000 });
|
|
930
1020
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
931
1021
|
}
|
|
932
1022
|
await this.waitForPageLoad();
|
|
933
|
-
return info;
|
|
1023
|
+
return state.info;
|
|
934
1024
|
}
|
|
935
1025
|
catch (e) {
|
|
936
|
-
|
|
937
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
938
|
-
info.screenshotPath = screenshotPath;
|
|
939
|
-
Object.assign(e, { info: info });
|
|
940
|
-
error = e;
|
|
941
|
-
throw e;
|
|
1026
|
+
await _commandError(state, e, this);
|
|
942
1027
|
}
|
|
943
1028
|
finally {
|
|
944
|
-
|
|
945
|
-
this._reportToWorld(world, {
|
|
946
|
-
element_name: selectors.element_name,
|
|
947
|
-
type: Types.HOVER,
|
|
948
|
-
text: `Hover element`,
|
|
949
|
-
screenshotId,
|
|
950
|
-
result: error
|
|
951
|
-
? {
|
|
952
|
-
status: "FAILED",
|
|
953
|
-
startTime,
|
|
954
|
-
endTime,
|
|
955
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
956
|
-
}
|
|
957
|
-
: {
|
|
958
|
-
status: "PASSED",
|
|
959
|
-
startTime,
|
|
960
|
-
endTime,
|
|
961
|
-
},
|
|
962
|
-
info: info,
|
|
963
|
-
});
|
|
1029
|
+
_commandFinally(state, this);
|
|
964
1030
|
}
|
|
965
1031
|
}
|
|
966
1032
|
async selectOption(selectors, values, _params = null, options = {}, world = null) {
|
|
967
|
-
this._validateSelectors(selectors);
|
|
968
1033
|
if (!values) {
|
|
969
1034
|
throw new Error("values is null");
|
|
970
1035
|
}
|
|
971
|
-
const
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
1036
|
+
const state = {
|
|
1037
|
+
selectors,
|
|
1038
|
+
_params,
|
|
1039
|
+
options,
|
|
1040
|
+
world,
|
|
1041
|
+
value: values.toString(),
|
|
1042
|
+
type: Types.SELECT,
|
|
1043
|
+
text: `Select option: ${values}`,
|
|
1044
|
+
operation: "selectOption",
|
|
1045
|
+
log: "***** select option " + selectors.element_name + " *****\n",
|
|
1046
|
+
};
|
|
979
1047
|
try {
|
|
980
|
-
|
|
981
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1048
|
+
await _preCommand(state, this);
|
|
982
1049
|
try {
|
|
983
|
-
await
|
|
984
|
-
await element.selectOption(values);
|
|
1050
|
+
await state.element.selectOption(values);
|
|
985
1051
|
}
|
|
986
1052
|
catch (e) {
|
|
987
1053
|
//await this.closeUnexpectedPopups();
|
|
988
|
-
info.log += "selectOption failed, will try force" + "\n";
|
|
989
|
-
await element.selectOption(values, { timeout: 10000, force: true });
|
|
1054
|
+
state.info.log += "selectOption failed, will try force" + "\n";
|
|
1055
|
+
await state.element.selectOption(values, { timeout: 10000, force: true });
|
|
990
1056
|
}
|
|
991
1057
|
await this.waitForPageLoad();
|
|
992
|
-
return info;
|
|
1058
|
+
return state.info;
|
|
993
1059
|
}
|
|
994
1060
|
catch (e) {
|
|
995
|
-
|
|
996
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
997
|
-
info.screenshotPath = screenshotPath;
|
|
998
|
-
Object.assign(e, { info: info });
|
|
999
|
-
this.logger.info("click failed, will try next selector");
|
|
1000
|
-
error = e;
|
|
1001
|
-
throw e;
|
|
1061
|
+
await _commandError(state, e, this);
|
|
1002
1062
|
}
|
|
1003
1063
|
finally {
|
|
1004
|
-
|
|
1005
|
-
this._reportToWorld(world, {
|
|
1006
|
-
element_name: selectors.element_name,
|
|
1007
|
-
type: Types.SELECT,
|
|
1008
|
-
text: `Select option: ${values}`,
|
|
1009
|
-
value: values.toString(),
|
|
1010
|
-
screenshotId,
|
|
1011
|
-
result: error
|
|
1012
|
-
? {
|
|
1013
|
-
status: "FAILED",
|
|
1014
|
-
startTime,
|
|
1015
|
-
endTime,
|
|
1016
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1017
|
-
}
|
|
1018
|
-
: {
|
|
1019
|
-
status: "PASSED",
|
|
1020
|
-
startTime,
|
|
1021
|
-
endTime,
|
|
1022
|
-
},
|
|
1023
|
-
info: info,
|
|
1024
|
-
});
|
|
1064
|
+
_commandFinally(state, this);
|
|
1025
1065
|
}
|
|
1026
1066
|
}
|
|
1027
1067
|
async type(_value, _params = null, options = {}, world = null) {
|
|
1028
|
-
const
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1068
|
+
const state = {
|
|
1069
|
+
value: _value,
|
|
1070
|
+
_params,
|
|
1071
|
+
options,
|
|
1072
|
+
world,
|
|
1073
|
+
locate: false,
|
|
1074
|
+
scroll: false,
|
|
1075
|
+
highlight: false,
|
|
1076
|
+
type: Types.TYPE_PRESS,
|
|
1077
|
+
text: `Type value: ${_value}`,
|
|
1078
|
+
operation: "type",
|
|
1079
|
+
log: "",
|
|
1080
|
+
};
|
|
1037
1081
|
try {
|
|
1038
|
-
|
|
1039
|
-
const valueSegment =
|
|
1082
|
+
await _preCommand(state, this);
|
|
1083
|
+
const valueSegment = state.value.split("&&");
|
|
1040
1084
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1041
1085
|
if (i > 0) {
|
|
1042
1086
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1056,134 +1100,76 @@ class StableBrowser {
|
|
|
1056
1100
|
await this.page.keyboard.type(value);
|
|
1057
1101
|
}
|
|
1058
1102
|
}
|
|
1059
|
-
return info;
|
|
1103
|
+
return state.info;
|
|
1060
1104
|
}
|
|
1061
1105
|
catch (e) {
|
|
1062
|
-
|
|
1063
|
-
this.logger.error("type failed " + info.log);
|
|
1064
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1065
|
-
info.screenshotPath = screenshotPath;
|
|
1066
|
-
Object.assign(e, { info: info });
|
|
1067
|
-
error = e;
|
|
1068
|
-
throw e;
|
|
1106
|
+
await _commandError(state, e, this);
|
|
1069
1107
|
}
|
|
1070
1108
|
finally {
|
|
1071
|
-
|
|
1072
|
-
this._reportToWorld(world, {
|
|
1073
|
-
type: Types.TYPE_PRESS,
|
|
1074
|
-
screenshotId,
|
|
1075
|
-
value: _value,
|
|
1076
|
-
text: `type value: ${_value}`,
|
|
1077
|
-
result: error
|
|
1078
|
-
? {
|
|
1079
|
-
status: "FAILED",
|
|
1080
|
-
startTime,
|
|
1081
|
-
endTime,
|
|
1082
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1083
|
-
}
|
|
1084
|
-
: {
|
|
1085
|
-
status: "PASSED",
|
|
1086
|
-
startTime,
|
|
1087
|
-
endTime,
|
|
1088
|
-
},
|
|
1089
|
-
info: info,
|
|
1090
|
-
});
|
|
1109
|
+
_commandFinally(state, this);
|
|
1091
1110
|
}
|
|
1092
1111
|
}
|
|
1093
1112
|
async setInputValue(selectors, value, _params = null, options = {}, world = null) {
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
let screenshotPath = null;
|
|
1113
|
+
const state = {
|
|
1114
|
+
selectors,
|
|
1115
|
+
_params,
|
|
1116
|
+
value,
|
|
1117
|
+
options,
|
|
1118
|
+
world,
|
|
1119
|
+
type: Types.SET_INPUT,
|
|
1120
|
+
text: `Set input value`,
|
|
1121
|
+
operation: "setInputValue",
|
|
1122
|
+
log: "***** set input value " + selectors.element_name + " *****\n",
|
|
1123
|
+
};
|
|
1106
1124
|
try {
|
|
1107
|
-
|
|
1108
|
-
let
|
|
1109
|
-
await this.scrollIfNeeded(element, info);
|
|
1110
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1111
|
-
await this._highlightElements(element);
|
|
1125
|
+
await _preCommand(state, this);
|
|
1126
|
+
let value = await this._replaceWithLocalData(state.value, this);
|
|
1112
1127
|
try {
|
|
1113
|
-
await element.evaluateHandle((el, value) => {
|
|
1128
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1114
1129
|
el.value = value;
|
|
1115
1130
|
}, value);
|
|
1116
1131
|
}
|
|
1117
1132
|
catch (error) {
|
|
1118
1133
|
this.logger.error("setInputValue failed, will try again");
|
|
1119
|
-
|
|
1120
|
-
info.
|
|
1121
|
-
|
|
1122
|
-
await element.evaluateHandle((el, value) => {
|
|
1134
|
+
await _screenshot(state, this);
|
|
1135
|
+
Object.assign(error, { info: state.info });
|
|
1136
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1123
1137
|
el.value = value;
|
|
1124
1138
|
});
|
|
1125
1139
|
}
|
|
1126
1140
|
}
|
|
1127
1141
|
catch (e) {
|
|
1128
|
-
|
|
1129
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1130
|
-
info.screenshotPath = screenshotPath;
|
|
1131
|
-
Object.assign(e, { info: info });
|
|
1132
|
-
error = e;
|
|
1133
|
-
throw e;
|
|
1142
|
+
await _commandError(state, e, this);
|
|
1134
1143
|
}
|
|
1135
1144
|
finally {
|
|
1136
|
-
|
|
1137
|
-
this._reportToWorld(world, {
|
|
1138
|
-
element_name: selectors.element_name,
|
|
1139
|
-
type: Types.SET_INPUT,
|
|
1140
|
-
text: `Set input value`,
|
|
1141
|
-
value: value,
|
|
1142
|
-
screenshotId,
|
|
1143
|
-
result: error
|
|
1144
|
-
? {
|
|
1145
|
-
status: "FAILED",
|
|
1146
|
-
startTime,
|
|
1147
|
-
endTime,
|
|
1148
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1149
|
-
}
|
|
1150
|
-
: {
|
|
1151
|
-
status: "PASSED",
|
|
1152
|
-
startTime,
|
|
1153
|
-
endTime,
|
|
1154
|
-
},
|
|
1155
|
-
info: info,
|
|
1156
|
-
});
|
|
1145
|
+
_commandFinally(state, this);
|
|
1157
1146
|
}
|
|
1158
1147
|
}
|
|
1159
1148
|
async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1149
|
+
const state = {
|
|
1150
|
+
selectors,
|
|
1151
|
+
_params,
|
|
1152
|
+
value: await this._replaceWithLocalData(value, this),
|
|
1153
|
+
options,
|
|
1154
|
+
world,
|
|
1155
|
+
type: Types.SET_DATE_TIME,
|
|
1156
|
+
text: `Set date time value: ${value}`,
|
|
1157
|
+
operation: "setDateTime",
|
|
1158
|
+
log: "***** set date time value " + selectors.element_name + " *****\n",
|
|
1159
|
+
throwError: false,
|
|
1160
|
+
};
|
|
1170
1161
|
try {
|
|
1171
|
-
|
|
1172
|
-
let element = await this._locate(selectors, info, _params);
|
|
1173
|
-
//insert red border around the element
|
|
1174
|
-
await this.scrollIfNeeded(element, info);
|
|
1175
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1176
|
-
await this._highlightElements(element);
|
|
1162
|
+
await _preCommand(state, this);
|
|
1177
1163
|
try {
|
|
1178
|
-
await element.click();
|
|
1164
|
+
await state.element.click();
|
|
1179
1165
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1180
1166
|
if (format) {
|
|
1181
|
-
value = dayjs(value).format(format);
|
|
1182
|
-
await element.fill(value);
|
|
1167
|
+
state.value = dayjs(state.value).format(format);
|
|
1168
|
+
await state.element.fill(state.value);
|
|
1183
1169
|
}
|
|
1184
1170
|
else {
|
|
1185
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1186
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1171
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1172
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1187
1173
|
el.value = ""; // clear input
|
|
1188
1174
|
el.value = dateTimeValue;
|
|
1189
1175
|
}, dateTimeValue);
|
|
@@ -1196,20 +1182,19 @@ class StableBrowser {
|
|
|
1196
1182
|
}
|
|
1197
1183
|
catch (err) {
|
|
1198
1184
|
//await this.closeUnexpectedPopups();
|
|
1199
|
-
this.logger.error("setting date time input failed " + JSON.stringify(info));
|
|
1185
|
+
this.logger.error("setting date time input failed " + JSON.stringify(state.info));
|
|
1200
1186
|
this.logger.info("Trying again");
|
|
1201
|
-
|
|
1202
|
-
info.
|
|
1203
|
-
Object.assign(err, { info: info });
|
|
1187
|
+
await _screenshot(state, this);
|
|
1188
|
+
Object.assign(err, { info: state.info });
|
|
1204
1189
|
await element.click();
|
|
1205
1190
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1206
1191
|
if (format) {
|
|
1207
|
-
value = dayjs(value).format(format);
|
|
1208
|
-
await element.fill(value);
|
|
1192
|
+
state.value = dayjs(state.value).format(format);
|
|
1193
|
+
await state.element.fill(state.value);
|
|
1209
1194
|
}
|
|
1210
1195
|
else {
|
|
1211
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1212
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1196
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1197
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1213
1198
|
el.value = ""; // clear input
|
|
1214
1199
|
el.value = dateTimeValue;
|
|
1215
1200
|
}, dateTimeValue);
|
|
@@ -1222,60 +1207,39 @@ class StableBrowser {
|
|
|
1222
1207
|
}
|
|
1223
1208
|
}
|
|
1224
1209
|
catch (e) {
|
|
1225
|
-
|
|
1226
|
-
throw e;
|
|
1210
|
+
await _commandError(state, e, this);
|
|
1227
1211
|
}
|
|
1228
1212
|
finally {
|
|
1229
|
-
|
|
1230
|
-
this._reportToWorld(world, {
|
|
1231
|
-
element_name: selectors.element_name,
|
|
1232
|
-
type: Types.SET_DATE_TIME,
|
|
1233
|
-
screenshotId,
|
|
1234
|
-
value: value,
|
|
1235
|
-
text: `setDateTime input with value: ${value}`,
|
|
1236
|
-
result: error
|
|
1237
|
-
? {
|
|
1238
|
-
status: "FAILED",
|
|
1239
|
-
startTime,
|
|
1240
|
-
endTime,
|
|
1241
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1242
|
-
}
|
|
1243
|
-
: {
|
|
1244
|
-
status: "PASSED",
|
|
1245
|
-
startTime,
|
|
1246
|
-
endTime,
|
|
1247
|
-
},
|
|
1248
|
-
info: info,
|
|
1249
|
-
});
|
|
1213
|
+
_commandFinally(state, this);
|
|
1250
1214
|
}
|
|
1251
1215
|
}
|
|
1252
1216
|
async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
|
|
1253
1217
|
_value = unEscapeString(_value);
|
|
1254
|
-
this._validateSelectors(selectors);
|
|
1255
|
-
const startTime = Date.now();
|
|
1256
|
-
let error = null;
|
|
1257
|
-
let screenshotId = null;
|
|
1258
|
-
let screenshotPath = null;
|
|
1259
|
-
const info = {};
|
|
1260
|
-
info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
|
|
1261
|
-
info.operation = "clickType";
|
|
1262
|
-
info.selectors = selectors;
|
|
1263
1218
|
const newValue = await this._replaceWithLocalData(_value, world);
|
|
1219
|
+
const state = {
|
|
1220
|
+
selectors,
|
|
1221
|
+
_params,
|
|
1222
|
+
value: newValue,
|
|
1223
|
+
originalValue: _value,
|
|
1224
|
+
options,
|
|
1225
|
+
world,
|
|
1226
|
+
type: Types.FILL,
|
|
1227
|
+
text: `Click type input with value: ${_value}`,
|
|
1228
|
+
operation: "clickType",
|
|
1229
|
+
log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
|
|
1230
|
+
};
|
|
1264
1231
|
if (newValue !== _value) {
|
|
1265
1232
|
//this.logger.info(_value + "=" + newValue);
|
|
1266
1233
|
_value = newValue;
|
|
1267
1234
|
}
|
|
1268
|
-
info.value = _value;
|
|
1269
1235
|
try {
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
await this.scrollIfNeeded(element, info);
|
|
1273
|
-
await this._highlightElements(element);
|
|
1236
|
+
await _preCommand(state, this);
|
|
1237
|
+
state.info.value = _value;
|
|
1274
1238
|
if (options === null || options === undefined || !options.press) {
|
|
1275
1239
|
try {
|
|
1276
|
-
let currentValue = await element.inputValue();
|
|
1240
|
+
let currentValue = await state.element.inputValue();
|
|
1277
1241
|
if (currentValue) {
|
|
1278
|
-
await element.fill("");
|
|
1242
|
+
await state.element.fill("");
|
|
1279
1243
|
}
|
|
1280
1244
|
}
|
|
1281
1245
|
catch (e) {
|
|
@@ -1284,22 +1248,22 @@ class StableBrowser {
|
|
|
1284
1248
|
}
|
|
1285
1249
|
if (options === null || options === undefined || options.press) {
|
|
1286
1250
|
try {
|
|
1287
|
-
await element.click({ timeout: 5000 });
|
|
1251
|
+
await state.element.click({ timeout: 5000 });
|
|
1288
1252
|
}
|
|
1289
1253
|
catch (e) {
|
|
1290
|
-
await element.dispatchEvent("click");
|
|
1254
|
+
await state.element.dispatchEvent("click");
|
|
1291
1255
|
}
|
|
1292
1256
|
}
|
|
1293
1257
|
else {
|
|
1294
1258
|
try {
|
|
1295
|
-
await element.focus();
|
|
1259
|
+
await state.element.focus();
|
|
1296
1260
|
}
|
|
1297
1261
|
catch (e) {
|
|
1298
|
-
await element.dispatchEvent("focus");
|
|
1262
|
+
await state.element.dispatchEvent("focus");
|
|
1299
1263
|
}
|
|
1300
1264
|
}
|
|
1301
1265
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1302
|
-
const valueSegment =
|
|
1266
|
+
const valueSegment = state.value.split("&&");
|
|
1303
1267
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1304
1268
|
if (i > 0) {
|
|
1305
1269
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1319,14 +1283,14 @@ class StableBrowser {
|
|
|
1319
1283
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1320
1284
|
}
|
|
1321
1285
|
}
|
|
1322
|
-
|
|
1286
|
+
await _screenshot(state, this);
|
|
1323
1287
|
if (enter === true) {
|
|
1324
1288
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1325
1289
|
await this.page.keyboard.press("Enter");
|
|
1326
1290
|
await this.waitForPageLoad();
|
|
1327
1291
|
}
|
|
1328
1292
|
else if (enter === false) {
|
|
1329
|
-
await element.dispatchEvent("change");
|
|
1293
|
+
await state.element.dispatchEvent("change");
|
|
1330
1294
|
//await this.page.keyboard.press("Tab");
|
|
1331
1295
|
}
|
|
1332
1296
|
else {
|
|
@@ -1335,104 +1299,50 @@ class StableBrowser {
|
|
|
1335
1299
|
await this.waitForPageLoad();
|
|
1336
1300
|
}
|
|
1337
1301
|
}
|
|
1338
|
-
return info;
|
|
1302
|
+
return state.info;
|
|
1339
1303
|
}
|
|
1340
1304
|
catch (e) {
|
|
1341
|
-
|
|
1342
|
-
this.logger.error("fill failed " + JSON.stringify(info));
|
|
1343
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1344
|
-
info.screenshotPath = screenshotPath;
|
|
1345
|
-
Object.assign(e, { info: info });
|
|
1346
|
-
error = e;
|
|
1347
|
-
throw e;
|
|
1305
|
+
await _commandError(state, e, this);
|
|
1348
1306
|
}
|
|
1349
1307
|
finally {
|
|
1350
|
-
|
|
1351
|
-
this._reportToWorld(world, {
|
|
1352
|
-
element_name: selectors.element_name,
|
|
1353
|
-
type: Types.FILL,
|
|
1354
|
-
screenshotId,
|
|
1355
|
-
value: _value,
|
|
1356
|
-
text: `clickType input with value: ${_value}`,
|
|
1357
|
-
result: error
|
|
1358
|
-
? {
|
|
1359
|
-
status: "FAILED",
|
|
1360
|
-
startTime,
|
|
1361
|
-
endTime,
|
|
1362
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1363
|
-
}
|
|
1364
|
-
: {
|
|
1365
|
-
status: "PASSED",
|
|
1366
|
-
startTime,
|
|
1367
|
-
endTime,
|
|
1368
|
-
},
|
|
1369
|
-
info: info,
|
|
1370
|
-
});
|
|
1308
|
+
_commandFinally(state, this);
|
|
1371
1309
|
}
|
|
1372
1310
|
}
|
|
1373
1311
|
async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1312
|
+
const state = {
|
|
1313
|
+
selectors,
|
|
1314
|
+
_params,
|
|
1315
|
+
value: unEscapeString(value),
|
|
1316
|
+
options,
|
|
1317
|
+
world,
|
|
1318
|
+
type: Types.FILL,
|
|
1319
|
+
text: `Fill input with value: ${value}`,
|
|
1320
|
+
operation: "fill",
|
|
1321
|
+
log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
|
|
1322
|
+
};
|
|
1385
1323
|
try {
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
await
|
|
1389
|
-
await element.fill(value);
|
|
1390
|
-
await element.dispatchEvent("change");
|
|
1324
|
+
await _preCommand(state, this);
|
|
1325
|
+
await state.element.fill(value);
|
|
1326
|
+
await state.element.dispatchEvent("change");
|
|
1391
1327
|
if (enter) {
|
|
1392
1328
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1393
1329
|
await this.page.keyboard.press("Enter");
|
|
1394
1330
|
}
|
|
1395
1331
|
await this.waitForPageLoad();
|
|
1396
|
-
return info;
|
|
1332
|
+
return state.info;
|
|
1397
1333
|
}
|
|
1398
|
-
catch (e) {
|
|
1399
|
-
|
|
1400
|
-
this.logger.error("fill failed " + info.log);
|
|
1401
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1402
|
-
info.screenshotPath = screenshotPath;
|
|
1403
|
-
Object.assign(e, { info: info });
|
|
1404
|
-
error = e;
|
|
1405
|
-
throw e;
|
|
1334
|
+
catch (e) {
|
|
1335
|
+
await _commandError(state, e, this);
|
|
1406
1336
|
}
|
|
1407
1337
|
finally {
|
|
1408
|
-
|
|
1409
|
-
this._reportToWorld(world, {
|
|
1410
|
-
element_name: selectors.element_name,
|
|
1411
|
-
type: Types.FILL,
|
|
1412
|
-
screenshotId,
|
|
1413
|
-
value,
|
|
1414
|
-
text: `Fill input with value: ${value}`,
|
|
1415
|
-
result: error
|
|
1416
|
-
? {
|
|
1417
|
-
status: "FAILED",
|
|
1418
|
-
startTime,
|
|
1419
|
-
endTime,
|
|
1420
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1421
|
-
}
|
|
1422
|
-
: {
|
|
1423
|
-
status: "PASSED",
|
|
1424
|
-
startTime,
|
|
1425
|
-
endTime,
|
|
1426
|
-
},
|
|
1427
|
-
info: info,
|
|
1428
|
-
});
|
|
1338
|
+
_commandFinally(state, this);
|
|
1429
1339
|
}
|
|
1430
1340
|
}
|
|
1431
1341
|
async getText(selectors, _params = null, options = {}, info = {}, world = null) {
|
|
1432
1342
|
return await this._getText(selectors, 0, _params, options, info, world);
|
|
1433
1343
|
}
|
|
1434
1344
|
async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
|
|
1435
|
-
|
|
1345
|
+
_validateSelectors(selectors);
|
|
1436
1346
|
let screenshotId = null;
|
|
1437
1347
|
let screenshotPath = null;
|
|
1438
1348
|
if (!info.log) {
|
|
@@ -1476,166 +1386,124 @@ class StableBrowser {
|
|
|
1476
1386
|
}
|
|
1477
1387
|
}
|
|
1478
1388
|
async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
|
|
1479
|
-
var _a;
|
|
1480
|
-
this._validateSelectors(selectors);
|
|
1481
1389
|
if (!pattern) {
|
|
1482
1390
|
throw new Error("pattern is null");
|
|
1483
1391
|
}
|
|
1484
1392
|
if (!text) {
|
|
1485
1393
|
throw new Error("text is null");
|
|
1486
1394
|
}
|
|
1395
|
+
const state = {
|
|
1396
|
+
selectors,
|
|
1397
|
+
_params,
|
|
1398
|
+
pattern,
|
|
1399
|
+
value: pattern,
|
|
1400
|
+
options,
|
|
1401
|
+
world,
|
|
1402
|
+
locate: false,
|
|
1403
|
+
scroll: false,
|
|
1404
|
+
screenshot: false,
|
|
1405
|
+
highlight: false,
|
|
1406
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1407
|
+
text: `Verify element contains pattern: ${pattern}`,
|
|
1408
|
+
operation: "containsPattern",
|
|
1409
|
+
log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
|
|
1410
|
+
};
|
|
1487
1411
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1488
1412
|
if (newValue !== text) {
|
|
1489
1413
|
this.logger.info(text + "=" + newValue);
|
|
1490
1414
|
text = newValue;
|
|
1491
1415
|
}
|
|
1492
|
-
const startTime = Date.now();
|
|
1493
|
-
let error = null;
|
|
1494
|
-
let screenshotId = null;
|
|
1495
|
-
let screenshotPath = null;
|
|
1496
|
-
const info = {};
|
|
1497
|
-
info.log =
|
|
1498
|
-
"***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
|
|
1499
|
-
info.operation = "containsPattern";
|
|
1500
|
-
info.selectors = selectors;
|
|
1501
|
-
info.value = text;
|
|
1502
|
-
info.pattern = pattern;
|
|
1503
1416
|
let foundObj = null;
|
|
1504
1417
|
try {
|
|
1505
|
-
|
|
1418
|
+
await _preCommand(state, this);
|
|
1419
|
+
state.info.pattern = pattern;
|
|
1420
|
+
foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
|
|
1506
1421
|
if (foundObj && foundObj.element) {
|
|
1507
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1422
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1508
1423
|
}
|
|
1509
|
-
|
|
1424
|
+
await _screenshot(state, this);
|
|
1510
1425
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1511
1426
|
pattern = pattern.replace("{text}", escapedText);
|
|
1512
1427
|
let regex = new RegExp(pattern, "im");
|
|
1513
|
-
if (!regex.test(foundObj
|
|
1514
|
-
info.foundText = foundObj
|
|
1428
|
+
if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
|
|
1429
|
+
state.info.foundText = foundObj?.text;
|
|
1515
1430
|
throw new Error("element doesn't contain text " + text);
|
|
1516
1431
|
}
|
|
1517
|
-
return info;
|
|
1432
|
+
return state.info;
|
|
1518
1433
|
}
|
|
1519
1434
|
catch (e) {
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
|
|
1523
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1524
|
-
info.screenshotPath = screenshotPath;
|
|
1525
|
-
Object.assign(e, { info: info });
|
|
1526
|
-
error = e;
|
|
1527
|
-
throw e;
|
|
1435
|
+
this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
|
|
1436
|
+
await _commandError(state, e, this);
|
|
1528
1437
|
}
|
|
1529
1438
|
finally {
|
|
1530
|
-
|
|
1531
|
-
this._reportToWorld(world, {
|
|
1532
|
-
element_name: selectors.element_name,
|
|
1533
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1534
|
-
value: pattern,
|
|
1535
|
-
text: `Verify element contains pattern: ${pattern}`,
|
|
1536
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1537
|
-
result: error
|
|
1538
|
-
? {
|
|
1539
|
-
status: "FAILED",
|
|
1540
|
-
startTime,
|
|
1541
|
-
endTime,
|
|
1542
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1543
|
-
}
|
|
1544
|
-
: {
|
|
1545
|
-
status: "PASSED",
|
|
1546
|
-
startTime,
|
|
1547
|
-
endTime,
|
|
1548
|
-
},
|
|
1549
|
-
info: info,
|
|
1550
|
-
});
|
|
1439
|
+
_commandFinally(state, this);
|
|
1551
1440
|
}
|
|
1552
1441
|
}
|
|
1553
1442
|
async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1443
|
+
const state = {
|
|
1444
|
+
selectors,
|
|
1445
|
+
_params,
|
|
1446
|
+
value: text,
|
|
1447
|
+
options,
|
|
1448
|
+
world,
|
|
1449
|
+
locate: false,
|
|
1450
|
+
scroll: false,
|
|
1451
|
+
screenshot: false,
|
|
1452
|
+
highlight: false,
|
|
1453
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1454
|
+
text: `Verify element contains text: ${text}`,
|
|
1455
|
+
operation: "containsText",
|
|
1456
|
+
log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
|
|
1457
|
+
};
|
|
1557
1458
|
if (!text) {
|
|
1558
1459
|
throw new Error("text is null");
|
|
1559
1460
|
}
|
|
1560
|
-
|
|
1561
|
-
let error = null;
|
|
1562
|
-
let screenshotId = null;
|
|
1563
|
-
let screenshotPath = null;
|
|
1564
|
-
const info = {};
|
|
1565
|
-
info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
|
|
1566
|
-
info.operation = "containsText";
|
|
1567
|
-
info.selectors = selectors;
|
|
1461
|
+
text = unEscapeString(text);
|
|
1568
1462
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1569
1463
|
if (newValue !== text) {
|
|
1570
1464
|
this.logger.info(text + "=" + newValue);
|
|
1571
1465
|
text = newValue;
|
|
1572
1466
|
}
|
|
1573
|
-
info.value = text;
|
|
1574
1467
|
let foundObj = null;
|
|
1575
1468
|
try {
|
|
1576
|
-
|
|
1469
|
+
await _preCommand(state, this);
|
|
1470
|
+
foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
|
|
1577
1471
|
if (foundObj && foundObj.element) {
|
|
1578
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1472
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1579
1473
|
}
|
|
1580
|
-
|
|
1474
|
+
await _screenshot(state, this);
|
|
1581
1475
|
const dateAlternatives = findDateAlternatives(text);
|
|
1582
1476
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1583
1477
|
if (dateAlternatives.date) {
|
|
1584
1478
|
for (let i = 0; i < dateAlternatives.dates.length; i++) {
|
|
1585
|
-
if (
|
|
1586
|
-
|
|
1587
|
-
return info;
|
|
1479
|
+
if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
|
|
1480
|
+
foundObj?.value?.includes(dateAlternatives.dates[i])) {
|
|
1481
|
+
return state.info;
|
|
1588
1482
|
}
|
|
1589
1483
|
}
|
|
1590
1484
|
throw new Error("element doesn't contain text " + text);
|
|
1591
1485
|
}
|
|
1592
1486
|
else if (numberAlternatives.number) {
|
|
1593
1487
|
for (let i = 0; i < numberAlternatives.numbers.length; i++) {
|
|
1594
|
-
if (
|
|
1595
|
-
|
|
1596
|
-
return info;
|
|
1488
|
+
if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
|
|
1489
|
+
foundObj?.value?.includes(numberAlternatives.numbers[i])) {
|
|
1490
|
+
return state.info;
|
|
1597
1491
|
}
|
|
1598
1492
|
}
|
|
1599
1493
|
throw new Error("element doesn't contain text " + text);
|
|
1600
1494
|
}
|
|
1601
|
-
else if (!
|
|
1602
|
-
info.foundText = foundObj
|
|
1603
|
-
info.value = foundObj
|
|
1495
|
+
else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
|
|
1496
|
+
state.info.foundText = foundObj?.text;
|
|
1497
|
+
state.info.value = foundObj?.value;
|
|
1604
1498
|
throw new Error("element doesn't contain text " + text);
|
|
1605
1499
|
}
|
|
1606
|
-
return info;
|
|
1500
|
+
return state.info;
|
|
1607
1501
|
}
|
|
1608
1502
|
catch (e) {
|
|
1609
|
-
|
|
1610
|
-
this.logger.error("verify element contains text failed " + info.log);
|
|
1611
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1612
|
-
info.screenshotPath = screenshotPath;
|
|
1613
|
-
Object.assign(e, { info: info });
|
|
1614
|
-
error = e;
|
|
1615
|
-
throw e;
|
|
1503
|
+
await _commandError(state, e, this);
|
|
1616
1504
|
}
|
|
1617
1505
|
finally {
|
|
1618
|
-
|
|
1619
|
-
this._reportToWorld(world, {
|
|
1620
|
-
element_name: selectors.element_name,
|
|
1621
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1622
|
-
text: `Verify element contains text: ${text}`,
|
|
1623
|
-
value: text,
|
|
1624
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1625
|
-
result: error
|
|
1626
|
-
? {
|
|
1627
|
-
status: "FAILED",
|
|
1628
|
-
startTime,
|
|
1629
|
-
endTime,
|
|
1630
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1631
|
-
}
|
|
1632
|
-
: {
|
|
1633
|
-
status: "PASSED",
|
|
1634
|
-
startTime,
|
|
1635
|
-
endTime,
|
|
1636
|
-
},
|
|
1637
|
-
info: info,
|
|
1638
|
-
});
|
|
1506
|
+
_commandFinally(state, this);
|
|
1639
1507
|
}
|
|
1640
1508
|
}
|
|
1641
1509
|
_getDataFile(world = null) {
|
|
@@ -1909,127 +1777,69 @@ class StableBrowser {
|
|
|
1909
1777
|
}
|
|
1910
1778
|
}
|
|
1911
1779
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1780
|
+
const state = {
|
|
1781
|
+
selectors,
|
|
1782
|
+
_params,
|
|
1783
|
+
options,
|
|
1784
|
+
world,
|
|
1785
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1786
|
+
text: `Verify element exists in page`,
|
|
1787
|
+
operation: "verifyElementExistInPage",
|
|
1788
|
+
log: "***** verify element " + selectors.element_name + " exists in page *****\n",
|
|
1789
|
+
};
|
|
1917
1790
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1918
|
-
const info = {};
|
|
1919
|
-
info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
|
|
1920
|
-
info.operation = "verify";
|
|
1921
|
-
info.selectors = selectors;
|
|
1922
1791
|
try {
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
}
|
|
1927
|
-
await this._highlightElements(element);
|
|
1928
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1929
|
-
await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
1930
|
-
return info;
|
|
1792
|
+
await _preCommand(state, this);
|
|
1793
|
+
await expect(state.element).toHaveCount(1, { timeout: 10000 });
|
|
1794
|
+
return state.info;
|
|
1931
1795
|
}
|
|
1932
1796
|
catch (e) {
|
|
1933
|
-
|
|
1934
|
-
this.logger.error("verify failed " + info.log);
|
|
1935
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1936
|
-
info.screenshotPath = screenshotPath;
|
|
1937
|
-
Object.assign(e, { info: info });
|
|
1938
|
-
error = e;
|
|
1939
|
-
throw e;
|
|
1797
|
+
await _commandError(state, e, this);
|
|
1940
1798
|
}
|
|
1941
1799
|
finally {
|
|
1942
|
-
|
|
1943
|
-
this._reportToWorld(world, {
|
|
1944
|
-
element_name: selectors.element_name,
|
|
1945
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1946
|
-
text: "Verify element exists in page",
|
|
1947
|
-
screenshotId,
|
|
1948
|
-
result: error
|
|
1949
|
-
? {
|
|
1950
|
-
status: "FAILED",
|
|
1951
|
-
startTime,
|
|
1952
|
-
endTime,
|
|
1953
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1954
|
-
}
|
|
1955
|
-
: {
|
|
1956
|
-
status: "PASSED",
|
|
1957
|
-
startTime,
|
|
1958
|
-
endTime,
|
|
1959
|
-
},
|
|
1960
|
-
info: info,
|
|
1961
|
-
});
|
|
1800
|
+
_commandFinally(state, this);
|
|
1962
1801
|
}
|
|
1963
1802
|
}
|
|
1964
1803
|
async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1804
|
+
const state = {
|
|
1805
|
+
selectors,
|
|
1806
|
+
_params,
|
|
1807
|
+
attribute,
|
|
1808
|
+
variable,
|
|
1809
|
+
options,
|
|
1810
|
+
world,
|
|
1811
|
+
type: Types.EXTRACT,
|
|
1812
|
+
text: `Extract attribute from element`,
|
|
1813
|
+
operation: "extractAttribute",
|
|
1814
|
+
log: "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1815
|
+
};
|
|
1970
1816
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1971
|
-
const info = {};
|
|
1972
|
-
info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
|
|
1973
|
-
info.operation = "extract";
|
|
1974
|
-
info.selectors = selectors;
|
|
1975
1817
|
try {
|
|
1976
|
-
|
|
1977
|
-
await this._highlightElements(element);
|
|
1978
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1818
|
+
await _preCommand(state, this);
|
|
1979
1819
|
switch (attribute) {
|
|
1980
1820
|
case "inner_text":
|
|
1981
|
-
|
|
1821
|
+
state.value = await state.element.innerText();
|
|
1982
1822
|
break;
|
|
1983
1823
|
case "href":
|
|
1984
|
-
|
|
1824
|
+
state.value = await state.element.getAttribute("href");
|
|
1985
1825
|
break;
|
|
1986
1826
|
case "value":
|
|
1987
|
-
|
|
1827
|
+
state.value = await state.element.inputValue();
|
|
1988
1828
|
break;
|
|
1989
1829
|
default:
|
|
1990
|
-
|
|
1830
|
+
state.value = await state.element.getAttribute(attribute);
|
|
1991
1831
|
break;
|
|
1992
1832
|
}
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
this.setTestData({ [variable]: info.value }, world);
|
|
1998
|
-
this.logger.info("set test data: " + variable + "=" + info.value);
|
|
1999
|
-
return info;
|
|
1833
|
+
state.info.value = state.value;
|
|
1834
|
+
this.setTestData({ [variable]: state.value }, world);
|
|
1835
|
+
this.logger.info("set test data: " + variable + "=" + state.value);
|
|
1836
|
+
return state.info;
|
|
2000
1837
|
}
|
|
2001
1838
|
catch (e) {
|
|
2002
|
-
|
|
2003
|
-
this.logger.error("extract failed " + info.log);
|
|
2004
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2005
|
-
info.screenshotPath = screenshotPath;
|
|
2006
|
-
Object.assign(e, { info: info });
|
|
2007
|
-
error = e;
|
|
2008
|
-
throw e;
|
|
1839
|
+
await _commandError(state, e, this);
|
|
2009
1840
|
}
|
|
2010
1841
|
finally {
|
|
2011
|
-
|
|
2012
|
-
this._reportToWorld(world, {
|
|
2013
|
-
element_name: selectors.element_name,
|
|
2014
|
-
type: Types.EXTRACT_ATTRIBUTE,
|
|
2015
|
-
variable: variable,
|
|
2016
|
-
value: info.value,
|
|
2017
|
-
text: "Extract attribute from element",
|
|
2018
|
-
screenshotId,
|
|
2019
|
-
result: error
|
|
2020
|
-
? {
|
|
2021
|
-
status: "FAILED",
|
|
2022
|
-
startTime,
|
|
2023
|
-
endTime,
|
|
2024
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2025
|
-
}
|
|
2026
|
-
: {
|
|
2027
|
-
status: "PASSED",
|
|
2028
|
-
startTime,
|
|
2029
|
-
endTime,
|
|
2030
|
-
},
|
|
2031
|
-
info: info,
|
|
2032
|
-
});
|
|
1842
|
+
_commandFinally(state, this);
|
|
2033
1843
|
}
|
|
2034
1844
|
}
|
|
2035
1845
|
async extractEmailData(emailAddress, options, world) {
|
|
@@ -2106,7 +1916,8 @@ class StableBrowser {
|
|
|
2106
1916
|
catch (e) {
|
|
2107
1917
|
errorCount++;
|
|
2108
1918
|
if (errorCount > 3) {
|
|
2109
|
-
throw e;
|
|
1919
|
+
// throw e;
|
|
1920
|
+
await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
|
|
2110
1921
|
}
|
|
2111
1922
|
// ignore
|
|
2112
1923
|
}
|
|
@@ -2217,11 +2028,12 @@ class StableBrowser {
|
|
|
2217
2028
|
info.screenshotPath = screenshotPath;
|
|
2218
2029
|
Object.assign(e, { info: info });
|
|
2219
2030
|
error = e;
|
|
2220
|
-
throw e;
|
|
2031
|
+
// throw e;
|
|
2032
|
+
await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
|
|
2221
2033
|
}
|
|
2222
2034
|
finally {
|
|
2223
2035
|
const endTime = Date.now();
|
|
2224
|
-
|
|
2036
|
+
_reportToWorld(world, {
|
|
2225
2037
|
type: Types.VERIFY_PAGE_PATH,
|
|
2226
2038
|
text: "Verify page path",
|
|
2227
2039
|
screenshotId,
|
|
@@ -2230,7 +2042,7 @@ class StableBrowser {
|
|
|
2230
2042
|
status: "FAILED",
|
|
2231
2043
|
startTime,
|
|
2232
2044
|
endTime,
|
|
2233
|
-
message: error
|
|
2045
|
+
message: error?.message,
|
|
2234
2046
|
}
|
|
2235
2047
|
: {
|
|
2236
2048
|
status: "PASSED",
|
|
@@ -2243,52 +2055,58 @@ class StableBrowser {
|
|
|
2243
2055
|
}
|
|
2244
2056
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2245
2057
|
text = unEscapeString(text);
|
|
2246
|
-
const
|
|
2058
|
+
const state = {
|
|
2059
|
+
text_search: text,
|
|
2060
|
+
options,
|
|
2061
|
+
world,
|
|
2062
|
+
locate: false,
|
|
2063
|
+
scroll: false,
|
|
2064
|
+
highlight: false,
|
|
2065
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
2066
|
+
text: `Verify text exists in page`,
|
|
2067
|
+
operation: "verifyTextExistInPage",
|
|
2068
|
+
log: "***** verify text " + text + " exists in page *****\n",
|
|
2069
|
+
};
|
|
2247
2070
|
const timeout = this._getLoadTimeout(options);
|
|
2248
|
-
let error = null;
|
|
2249
|
-
let screenshotId = null;
|
|
2250
|
-
let screenshotPath = null;
|
|
2251
2071
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2252
|
-
const info = {};
|
|
2253
|
-
info.log = "***** verify text " + text + " exists in page *****\n";
|
|
2254
|
-
info.operation = "verifyTextExistInPage";
|
|
2255
2072
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
2256
2073
|
if (newValue !== text) {
|
|
2257
2074
|
this.logger.info(text + "=" + newValue);
|
|
2258
2075
|
text = newValue;
|
|
2259
2076
|
}
|
|
2260
|
-
info.text = text;
|
|
2261
2077
|
let dateAlternatives = findDateAlternatives(text);
|
|
2262
2078
|
let numberAlternatives = findNumberAlternatives(text);
|
|
2263
2079
|
try {
|
|
2080
|
+
await _preCommand(state, this);
|
|
2081
|
+
state.info.text = text;
|
|
2264
2082
|
while (true) {
|
|
2265
2083
|
const frames = this.page.frames();
|
|
2266
2084
|
let results = [];
|
|
2267
2085
|
for (let i = 0; i < frames.length; i++) {
|
|
2268
2086
|
if (dateAlternatives.date) {
|
|
2269
2087
|
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2270
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "
|
|
2088
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2271
2089
|
result.frame = frames[i];
|
|
2272
2090
|
results.push(result);
|
|
2273
2091
|
}
|
|
2274
2092
|
}
|
|
2275
2093
|
else if (numberAlternatives.number) {
|
|
2276
2094
|
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2277
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "
|
|
2095
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2278
2096
|
result.frame = frames[i];
|
|
2279
2097
|
results.push(result);
|
|
2280
2098
|
}
|
|
2281
2099
|
}
|
|
2282
2100
|
else {
|
|
2283
|
-
const result = await this._locateElementByText(frames[i], text, "
|
|
2101
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2284
2102
|
result.frame = frames[i];
|
|
2285
2103
|
results.push(result);
|
|
2286
2104
|
}
|
|
2287
2105
|
}
|
|
2288
|
-
info.results = results;
|
|
2106
|
+
state.info.results = results;
|
|
2289
2107
|
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2290
2108
|
if (resultWithElementsFound.length === 0) {
|
|
2291
|
-
if (Date.now() - startTime > timeout) {
|
|
2109
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2292
2110
|
throw new Error(`Text ${text} not found in page`);
|
|
2293
2111
|
}
|
|
2294
2112
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -2300,44 +2118,89 @@ class StableBrowser {
|
|
|
2300
2118
|
await this._highlightElements(frame, dataAttribute);
|
|
2301
2119
|
const element = await frame.$(dataAttribute);
|
|
2302
2120
|
if (element) {
|
|
2303
|
-
await this.scrollIfNeeded(element, info);
|
|
2121
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2304
2122
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2305
2123
|
}
|
|
2306
2124
|
}
|
|
2307
|
-
|
|
2308
|
-
return info;
|
|
2125
|
+
await _screenshot(state, this);
|
|
2126
|
+
return state.info;
|
|
2309
2127
|
}
|
|
2310
2128
|
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2311
2129
|
}
|
|
2312
2130
|
catch (e) {
|
|
2313
|
-
|
|
2314
|
-
this.logger.error("verify text exist in page failed " + info.log);
|
|
2315
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2316
|
-
info.screenshotPath = screenshotPath;
|
|
2317
|
-
Object.assign(e, { info: info });
|
|
2318
|
-
error = e;
|
|
2319
|
-
throw e;
|
|
2131
|
+
await _commandError(state, e, this);
|
|
2320
2132
|
}
|
|
2321
2133
|
finally {
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2134
|
+
_commandFinally(state, this);
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
async waitForTextToDisappear(text, options = {}, world = null) {
|
|
2138
|
+
text = unEscapeString(text);
|
|
2139
|
+
const state = {
|
|
2140
|
+
text_search: text,
|
|
2141
|
+
options,
|
|
2142
|
+
world,
|
|
2143
|
+
locate: false,
|
|
2144
|
+
scroll: false,
|
|
2145
|
+
highlight: false,
|
|
2146
|
+
type: Types.WAIT_FOR_TEXT_TO_DISAPPEAR,
|
|
2147
|
+
text: `Verify text does not exist in page`,
|
|
2148
|
+
operation: "verifyTextNotExistInPage",
|
|
2149
|
+
log: "***** verify text " + text + " does not exist in page *****\n",
|
|
2150
|
+
};
|
|
2151
|
+
const timeout = this._getLoadTimeout(options);
|
|
2152
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2153
|
+
const newValue = await this._replaceWithLocalData(text, world);
|
|
2154
|
+
if (newValue !== text) {
|
|
2155
|
+
this.logger.info(text + "=" + newValue);
|
|
2156
|
+
text = newValue;
|
|
2157
|
+
}
|
|
2158
|
+
let dateAlternatives = findDateAlternatives(text);
|
|
2159
|
+
let numberAlternatives = findNumberAlternatives(text);
|
|
2160
|
+
try {
|
|
2161
|
+
await _preCommand(state, this);
|
|
2162
|
+
state.info.text = text;
|
|
2163
|
+
while (true) {
|
|
2164
|
+
const frames = this.page.frames();
|
|
2165
|
+
let results = [];
|
|
2166
|
+
for (let i = 0; i < frames.length; i++) {
|
|
2167
|
+
if (dateAlternatives.date) {
|
|
2168
|
+
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2169
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2170
|
+
result.frame = frames[i];
|
|
2171
|
+
results.push(result);
|
|
2172
|
+
}
|
|
2333
2173
|
}
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2174
|
+
else if (numberAlternatives.number) {
|
|
2175
|
+
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2176
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2177
|
+
result.frame = frames[i];
|
|
2178
|
+
results.push(result);
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
else {
|
|
2182
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2183
|
+
result.frame = frames[i];
|
|
2184
|
+
results.push(result);
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
state.info.results = results;
|
|
2188
|
+
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2189
|
+
if (resultWithElementsFound.length === 0) {
|
|
2190
|
+
await _screenshot(state, this);
|
|
2191
|
+
return state.info;
|
|
2192
|
+
}
|
|
2193
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2194
|
+
throw new Error(`Text ${text} found in page`);
|
|
2195
|
+
}
|
|
2196
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
catch (e) {
|
|
2200
|
+
await _commandError(state, e, this);
|
|
2201
|
+
}
|
|
2202
|
+
finally {
|
|
2203
|
+
_commandFinally(state, this);
|
|
2341
2204
|
}
|
|
2342
2205
|
}
|
|
2343
2206
|
_getServerUrl() {
|
|
@@ -2400,11 +2263,12 @@ class StableBrowser {
|
|
|
2400
2263
|
info.screenshotPath = screenshotPath;
|
|
2401
2264
|
Object.assign(e, { info: info });
|
|
2402
2265
|
error = e;
|
|
2403
|
-
throw e;
|
|
2266
|
+
// throw e;
|
|
2267
|
+
await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
|
|
2404
2268
|
}
|
|
2405
2269
|
finally {
|
|
2406
2270
|
const endTime = Date.now();
|
|
2407
|
-
|
|
2271
|
+
_reportToWorld(world, {
|
|
2408
2272
|
type: Types.VERIFY_VISUAL,
|
|
2409
2273
|
text: "Visual verification",
|
|
2410
2274
|
screenshotId,
|
|
@@ -2413,7 +2277,7 @@ class StableBrowser {
|
|
|
2413
2277
|
status: "FAILED",
|
|
2414
2278
|
startTime,
|
|
2415
2279
|
endTime,
|
|
2416
|
-
message: error
|
|
2280
|
+
message: error?.message,
|
|
2417
2281
|
}
|
|
2418
2282
|
: {
|
|
2419
2283
|
status: "PASSED",
|
|
@@ -2445,7 +2309,7 @@ class StableBrowser {
|
|
|
2445
2309
|
this.logger.info("Table data verified");
|
|
2446
2310
|
}
|
|
2447
2311
|
async getTableData(selectors, _params = null, options = {}, world = null) {
|
|
2448
|
-
|
|
2312
|
+
_validateSelectors(selectors);
|
|
2449
2313
|
const startTime = Date.now();
|
|
2450
2314
|
let error = null;
|
|
2451
2315
|
let screenshotId = null;
|
|
@@ -2467,11 +2331,12 @@ class StableBrowser {
|
|
|
2467
2331
|
info.screenshotPath = screenshotPath;
|
|
2468
2332
|
Object.assign(e, { info: info });
|
|
2469
2333
|
error = e;
|
|
2470
|
-
throw e;
|
|
2334
|
+
// throw e;
|
|
2335
|
+
await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
|
|
2471
2336
|
}
|
|
2472
2337
|
finally {
|
|
2473
2338
|
const endTime = Date.now();
|
|
2474
|
-
|
|
2339
|
+
_reportToWorld(world, {
|
|
2475
2340
|
element_name: selectors.element_name,
|
|
2476
2341
|
type: Types.GET_TABLE_DATA,
|
|
2477
2342
|
text: "Get table data",
|
|
@@ -2481,7 +2346,7 @@ class StableBrowser {
|
|
|
2481
2346
|
status: "FAILED",
|
|
2482
2347
|
startTime,
|
|
2483
2348
|
endTime,
|
|
2484
|
-
message: error
|
|
2349
|
+
message: error?.message,
|
|
2485
2350
|
}
|
|
2486
2351
|
: {
|
|
2487
2352
|
status: "PASSED",
|
|
@@ -2493,7 +2358,7 @@ class StableBrowser {
|
|
|
2493
2358
|
}
|
|
2494
2359
|
}
|
|
2495
2360
|
async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
|
|
2496
|
-
|
|
2361
|
+
_validateSelectors(selectors);
|
|
2497
2362
|
if (!query) {
|
|
2498
2363
|
throw new Error("query is null");
|
|
2499
2364
|
}
|
|
@@ -2632,11 +2497,12 @@ class StableBrowser {
|
|
|
2632
2497
|
info.screenshotPath = screenshotPath;
|
|
2633
2498
|
Object.assign(e, { info: info });
|
|
2634
2499
|
error = e;
|
|
2635
|
-
throw e;
|
|
2500
|
+
// throw e;
|
|
2501
|
+
await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
|
|
2636
2502
|
}
|
|
2637
2503
|
finally {
|
|
2638
2504
|
const endTime = Date.now();
|
|
2639
|
-
|
|
2505
|
+
_reportToWorld(world, {
|
|
2640
2506
|
element_name: selectors.element_name,
|
|
2641
2507
|
type: Types.ANALYZE_TABLE,
|
|
2642
2508
|
text: "Analyze table",
|
|
@@ -2646,7 +2512,7 @@ class StableBrowser {
|
|
|
2646
2512
|
status: "FAILED",
|
|
2647
2513
|
startTime,
|
|
2648
2514
|
endTime,
|
|
2649
|
-
message: error
|
|
2515
|
+
message: error?.message,
|
|
2650
2516
|
}
|
|
2651
2517
|
: {
|
|
2652
2518
|
status: "PASSED",
|
|
@@ -2658,27 +2524,7 @@ class StableBrowser {
|
|
|
2658
2524
|
}
|
|
2659
2525
|
}
|
|
2660
2526
|
async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
|
|
2661
|
-
|
|
2662
|
-
return value;
|
|
2663
|
-
}
|
|
2664
|
-
// find all the accurance of {{(.*?)}} and replace with the value
|
|
2665
|
-
let regex = /{{(.*?)}}/g;
|
|
2666
|
-
let matches = value.match(regex);
|
|
2667
|
-
if (matches) {
|
|
2668
|
-
const testData = this.getTestData(world);
|
|
2669
|
-
for (let i = 0; i < matches.length; i++) {
|
|
2670
|
-
let match = matches[i];
|
|
2671
|
-
let key = match.substring(2, match.length - 2);
|
|
2672
|
-
let newValue = objectPath.get(testData, key, null);
|
|
2673
|
-
if (newValue !== null) {
|
|
2674
|
-
value = value.replace(match, newValue);
|
|
2675
|
-
}
|
|
2676
|
-
}
|
|
2677
|
-
}
|
|
2678
|
-
if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
|
|
2679
|
-
return await decrypt(value, null, totpWait);
|
|
2680
|
-
}
|
|
2681
|
-
return value;
|
|
2527
|
+
return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
|
|
2682
2528
|
}
|
|
2683
2529
|
_getLoadTimeout(options) {
|
|
2684
2530
|
let timeout = 15000;
|
|
@@ -2729,7 +2575,7 @@ class StableBrowser {
|
|
|
2729
2575
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2730
2576
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2731
2577
|
const endTime = Date.now();
|
|
2732
|
-
|
|
2578
|
+
_reportToWorld(world, {
|
|
2733
2579
|
type: Types.GET_PAGE_STATUS,
|
|
2734
2580
|
text: "Wait for page load",
|
|
2735
2581
|
screenshotId,
|
|
@@ -2738,7 +2584,7 @@ class StableBrowser {
|
|
|
2738
2584
|
status: "FAILED",
|
|
2739
2585
|
startTime,
|
|
2740
2586
|
endTime,
|
|
2741
|
-
message: error
|
|
2587
|
+
message: error?.message,
|
|
2742
2588
|
}
|
|
2743
2589
|
: {
|
|
2744
2590
|
status: "PASSED",
|
|
@@ -2749,41 +2595,35 @@ class StableBrowser {
|
|
|
2749
2595
|
}
|
|
2750
2596
|
}
|
|
2751
2597
|
async closePage(options = {}, world = null) {
|
|
2752
|
-
const
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2598
|
+
const state = {
|
|
2599
|
+
options,
|
|
2600
|
+
world,
|
|
2601
|
+
locate: false,
|
|
2602
|
+
scroll: false,
|
|
2603
|
+
highlight: false,
|
|
2604
|
+
type: Types.CLOSE_PAGE,
|
|
2605
|
+
text: `Close page`,
|
|
2606
|
+
operation: "closePage",
|
|
2607
|
+
log: "***** close page *****\n",
|
|
2608
|
+
throwError: false,
|
|
2609
|
+
};
|
|
2757
2610
|
try {
|
|
2611
|
+
await _preCommand(state, this);
|
|
2758
2612
|
await this.page.close();
|
|
2759
2613
|
}
|
|
2760
2614
|
catch (e) {
|
|
2761
2615
|
console.log(".");
|
|
2616
|
+
await _commandError(state, e, this);
|
|
2762
2617
|
}
|
|
2763
2618
|
finally {
|
|
2764
|
-
|
|
2765
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2766
|
-
const endTime = Date.now();
|
|
2767
|
-
this._reportToWorld(world, {
|
|
2768
|
-
type: Types.CLOSE_PAGE,
|
|
2769
|
-
text: "close page",
|
|
2770
|
-
screenshotId,
|
|
2771
|
-
result: error
|
|
2772
|
-
? {
|
|
2773
|
-
status: "FAILED",
|
|
2774
|
-
startTime,
|
|
2775
|
-
endTime,
|
|
2776
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2777
|
-
}
|
|
2778
|
-
: {
|
|
2779
|
-
status: "PASSED",
|
|
2780
|
-
startTime,
|
|
2781
|
-
endTime,
|
|
2782
|
-
},
|
|
2783
|
-
info: info,
|
|
2784
|
-
});
|
|
2619
|
+
_commandFinally(state, this);
|
|
2785
2620
|
}
|
|
2786
2621
|
}
|
|
2622
|
+
saveTestDataAsGlobal(options, world) {
|
|
2623
|
+
const dataFile = this._getDataFile(world);
|
|
2624
|
+
process.env.GLOBAL_TEST_DATA_FILE = dataFile;
|
|
2625
|
+
this.logger.info("Save the scenario test data as global for the following scenarios.");
|
|
2626
|
+
}
|
|
2787
2627
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
2788
2628
|
const startTime = Date.now();
|
|
2789
2629
|
let error = null;
|
|
@@ -2801,12 +2641,13 @@ class StableBrowser {
|
|
|
2801
2641
|
}
|
|
2802
2642
|
catch (e) {
|
|
2803
2643
|
console.log(".");
|
|
2644
|
+
await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
|
|
2804
2645
|
}
|
|
2805
2646
|
finally {
|
|
2806
2647
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2807
2648
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2808
2649
|
const endTime = Date.now();
|
|
2809
|
-
|
|
2650
|
+
_reportToWorld(world, {
|
|
2810
2651
|
type: Types.SET_VIEWPORT,
|
|
2811
2652
|
text: "set viewport size to " + width + "x" + hight,
|
|
2812
2653
|
screenshotId,
|
|
@@ -2815,7 +2656,7 @@ class StableBrowser {
|
|
|
2815
2656
|
status: "FAILED",
|
|
2816
2657
|
startTime,
|
|
2817
2658
|
endTime,
|
|
2818
|
-
message: error
|
|
2659
|
+
message: error?.message,
|
|
2819
2660
|
}
|
|
2820
2661
|
: {
|
|
2821
2662
|
status: "PASSED",
|
|
@@ -2837,12 +2678,13 @@ class StableBrowser {
|
|
|
2837
2678
|
}
|
|
2838
2679
|
catch (e) {
|
|
2839
2680
|
console.log(".");
|
|
2681
|
+
await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
|
|
2840
2682
|
}
|
|
2841
2683
|
finally {
|
|
2842
2684
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2843
2685
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2844
2686
|
const endTime = Date.now();
|
|
2845
|
-
|
|
2687
|
+
_reportToWorld(world, {
|
|
2846
2688
|
type: Types.GET_PAGE_STATUS,
|
|
2847
2689
|
text: "page relaod",
|
|
2848
2690
|
screenshotId,
|
|
@@ -2851,7 +2693,7 @@ class StableBrowser {
|
|
|
2851
2693
|
status: "FAILED",
|
|
2852
2694
|
startTime,
|
|
2853
2695
|
endTime,
|
|
2854
|
-
message: error
|
|
2696
|
+
message: error?.message,
|
|
2855
2697
|
}
|
|
2856
2698
|
: {
|
|
2857
2699
|
status: "PASSED",
|
|
@@ -2878,11 +2720,37 @@ class StableBrowser {
|
|
|
2878
2720
|
console.log("#-#");
|
|
2879
2721
|
}
|
|
2880
2722
|
}
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2723
|
+
async beforeStep(world, step) {
|
|
2724
|
+
this.stepName = step.pickleStep.text;
|
|
2725
|
+
this.logger.info("step: " + this.stepName);
|
|
2726
|
+
if (this.stepIndex === undefined) {
|
|
2727
|
+
this.stepIndex = 0;
|
|
2728
|
+
}
|
|
2729
|
+
else {
|
|
2730
|
+
this.stepIndex++;
|
|
2731
|
+
}
|
|
2732
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2733
|
+
if (this.context.browserObject.context) {
|
|
2734
|
+
await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
if (this.tags === null && step && step.pickle && step.pickle.tags) {
|
|
2738
|
+
this.tags = step.pickle.tags.map((tag) => tag.name);
|
|
2739
|
+
// check if @global_test_data tag is present
|
|
2740
|
+
if (this.tags.includes("@global_test_data")) {
|
|
2741
|
+
this.saveTestDataAsGlobal({}, world);
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2745
|
+
async afterStep(world, step) {
|
|
2746
|
+
this.stepName = null;
|
|
2747
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2748
|
+
if (this.context.browserObject.context) {
|
|
2749
|
+
await this.context.browserObject.context.tracing.stopChunk({
|
|
2750
|
+
path: path.join(this.context.browserObject.traceFolder, `trace-${this.stepIndex}.zip`),
|
|
2751
|
+
});
|
|
2752
|
+
}
|
|
2884
2753
|
}
|
|
2885
|
-
world.attach(JSON.stringify(properties), { mediaType: "application/json" });
|
|
2886
2754
|
}
|
|
2887
2755
|
}
|
|
2888
2756
|
function createTimedPromise(promise, label) {
|