automation_model 1.0.464-dev → 1.0.464-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 +224 -49
- package/lib/api.js.map +1 -1
- package/lib/auto_page.js +38 -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 +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 +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 +626 -773
- 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,6 +476,15 @@ 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];
|
|
477
489
|
try {
|
|
478
490
|
locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
|
|
@@ -485,13 +497,18 @@ class StableBrowser {
|
|
|
485
497
|
if (locatorSearch.climb && locatorSearch.climb >= 0) {
|
|
486
498
|
let locatorString = await this._locateElmentByTextClimbCss(scope, locatorSearch.text, locatorSearch.climb, locatorSearch.css, _params);
|
|
487
499
|
if (!locatorString) {
|
|
500
|
+
info.failCause.textNotFound = true;
|
|
501
|
+
info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
|
|
488
502
|
return;
|
|
489
503
|
}
|
|
490
504
|
locator = this._getLocator({ css: locatorString }, scope, _params);
|
|
491
505
|
}
|
|
492
506
|
else if (locatorSearch.text) {
|
|
493
|
-
let
|
|
507
|
+
let text = this._fixUsingParams(locatorSearch.text, _params);
|
|
508
|
+
let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
|
|
494
509
|
if (result.elementCount === 0) {
|
|
510
|
+
info.failCause.textNotFound = true;
|
|
511
|
+
info.failCause.lastError = "failed to locate element by text: " + text;
|
|
495
512
|
return;
|
|
496
513
|
}
|
|
497
514
|
locatorSearch.css = "[data-blinq-id='blinq-id-" + result.randomToken + "']";
|
|
@@ -508,6 +525,9 @@ class StableBrowser {
|
|
|
508
525
|
// cssHref = true;
|
|
509
526
|
// }
|
|
510
527
|
let count = await locator.count();
|
|
528
|
+
if (count > 0 && !info.failCause.count) {
|
|
529
|
+
info.failCause.count = count;
|
|
530
|
+
}
|
|
511
531
|
//info.log += "total elements found " + count + "\n";
|
|
512
532
|
//let visibleCount = 0;
|
|
513
533
|
let visibleLocator = null;
|
|
@@ -525,6 +545,8 @@ class StableBrowser {
|
|
|
525
545
|
foundLocators.push(locator.nth(j));
|
|
526
546
|
}
|
|
527
547
|
else {
|
|
548
|
+
info.failCause.visible = visible;
|
|
549
|
+
info.failCause.enabled = enabled;
|
|
528
550
|
if (!info.printMessages) {
|
|
529
551
|
info.printMessages = {};
|
|
530
552
|
}
|
|
@@ -536,6 +558,11 @@ class StableBrowser {
|
|
|
536
558
|
}
|
|
537
559
|
}
|
|
538
560
|
async closeUnexpectedPopups(info, _params) {
|
|
561
|
+
if (!info) {
|
|
562
|
+
info = {};
|
|
563
|
+
info.failCause = {};
|
|
564
|
+
info.log = "";
|
|
565
|
+
}
|
|
539
566
|
if (this.configuration.popupHandlers && this.configuration.popupHandlers.length > 0) {
|
|
540
567
|
if (!info) {
|
|
541
568
|
info = {};
|
|
@@ -576,7 +603,10 @@ class StableBrowser {
|
|
|
576
603
|
}
|
|
577
604
|
return { rerun: false };
|
|
578
605
|
}
|
|
579
|
-
async _locate(selectors, info, _params, timeout
|
|
606
|
+
async _locate(selectors, info, _params, timeout) {
|
|
607
|
+
if (!timeout) {
|
|
608
|
+
timeout = 30000;
|
|
609
|
+
}
|
|
580
610
|
for (let i = 0; i < 3; i++) {
|
|
581
611
|
info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
|
|
582
612
|
for (let j = 0; j < selectors.locators.length; j++) {
|
|
@@ -590,13 +620,16 @@ class StableBrowser {
|
|
|
590
620
|
}
|
|
591
621
|
throw new Error("unable to locate element " + JSON.stringify(selectors));
|
|
592
622
|
}
|
|
593
|
-
async
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
623
|
+
async _findFrameScope(selectors, timeout = 30000, info) {
|
|
624
|
+
if (!info) {
|
|
625
|
+
info = {};
|
|
626
|
+
info.failCause = {};
|
|
627
|
+
info.log = "";
|
|
628
|
+
}
|
|
599
629
|
let scope = this.page;
|
|
630
|
+
if (selectors.frame) {
|
|
631
|
+
return selectors.frame;
|
|
632
|
+
}
|
|
600
633
|
if (selectors.iframe_src || selectors.frameLocators) {
|
|
601
634
|
const findFrame = async (frame, framescope) => {
|
|
602
635
|
for (let i = 0; i < frame.selectors.length; i++) {
|
|
@@ -623,7 +656,6 @@ class StableBrowser {
|
|
|
623
656
|
}
|
|
624
657
|
return framescope;
|
|
625
658
|
};
|
|
626
|
-
info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
|
|
627
659
|
while (true) {
|
|
628
660
|
let frameFound = false;
|
|
629
661
|
if (selectors.nestFrmLoc) {
|
|
@@ -647,6 +679,8 @@ class StableBrowser {
|
|
|
647
679
|
if (!scope) {
|
|
648
680
|
info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
|
|
649
681
|
if (performance.now() - startTime > timeout) {
|
|
682
|
+
info.failCause.iframeNotFound = true;
|
|
683
|
+
info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
|
|
650
684
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
651
685
|
}
|
|
652
686
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -656,6 +690,30 @@ class StableBrowser {
|
|
|
656
690
|
}
|
|
657
691
|
}
|
|
658
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);
|
|
659
717
|
let selectorsLocators = null;
|
|
660
718
|
selectorsLocators = selectors.locators;
|
|
661
719
|
// group selectors by priority
|
|
@@ -757,6 +815,8 @@ class StableBrowser {
|
|
|
757
815
|
}
|
|
758
816
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
759
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";
|
|
760
820
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
761
821
|
}
|
|
762
822
|
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
@@ -788,89 +848,129 @@ class StableBrowser {
|
|
|
788
848
|
});
|
|
789
849
|
result.locatorIndex = i;
|
|
790
850
|
}
|
|
851
|
+
if (foundLocators.length > 1) {
|
|
852
|
+
info.failCause.foundMultiple = true;
|
|
853
|
+
}
|
|
791
854
|
}
|
|
792
855
|
return result;
|
|
793
856
|
}
|
|
794
|
-
async
|
|
795
|
-
this._validateSelectors(selectors);
|
|
857
|
+
async simpleClick(elementDescription, _params, options = {}, world = null) {
|
|
796
858
|
const startTime = Date.now();
|
|
797
|
-
|
|
798
|
-
|
|
859
|
+
let timeout = 30000;
|
|
860
|
+
if (options && options.timeout) {
|
|
861
|
+
timeout = options.timeout;
|
|
799
862
|
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
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
|
+
};
|
|
807
930
|
try {
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
931
|
+
await _preCommand(state, this);
|
|
932
|
+
if (state.options && state.options.context) {
|
|
933
|
+
state.selectors.locators[0].text = state.options.context;
|
|
934
|
+
}
|
|
811
935
|
try {
|
|
812
|
-
await
|
|
813
|
-
await
|
|
814
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
936
|
+
await state.element.click();
|
|
937
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
815
938
|
}
|
|
816
939
|
catch (e) {
|
|
817
940
|
// await this.closeUnexpectedPopups();
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
await
|
|
821
|
-
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));
|
|
822
944
|
}
|
|
823
945
|
await this.waitForPageLoad();
|
|
824
|
-
return info;
|
|
946
|
+
return state.info;
|
|
825
947
|
}
|
|
826
948
|
catch (e) {
|
|
827
|
-
|
|
828
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
829
|
-
info.screenshotPath = screenshotPath;
|
|
830
|
-
Object.assign(e, { info: info });
|
|
831
|
-
error = e;
|
|
832
|
-
throw e;
|
|
949
|
+
await _commandError(state, e, this);
|
|
833
950
|
}
|
|
834
951
|
finally {
|
|
835
|
-
|
|
836
|
-
this._reportToWorld(world, {
|
|
837
|
-
element_name: selectors.element_name,
|
|
838
|
-
type: Types.CLICK,
|
|
839
|
-
text: `Click element`,
|
|
840
|
-
screenshotId,
|
|
841
|
-
result: error
|
|
842
|
-
? {
|
|
843
|
-
status: "FAILED",
|
|
844
|
-
startTime,
|
|
845
|
-
endTime,
|
|
846
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
847
|
-
}
|
|
848
|
-
: {
|
|
849
|
-
status: "PASSED",
|
|
850
|
-
startTime,
|
|
851
|
-
endTime,
|
|
852
|
-
},
|
|
853
|
-
info: info,
|
|
854
|
-
});
|
|
952
|
+
_commandFinally(state, this);
|
|
855
953
|
}
|
|
856
954
|
}
|
|
857
955
|
async setCheck(selectors, checked = true, _params, options = {}, world = null) {
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
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
|
+
};
|
|
868
966
|
try {
|
|
869
|
-
|
|
870
|
-
|
|
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));
|
|
871
971
|
try {
|
|
872
|
-
await this._highlightElements(element);
|
|
873
|
-
await element.setChecked(checked);
|
|
972
|
+
// await this._highlightElements(element);
|
|
973
|
+
await state.element.setChecked(checked);
|
|
874
974
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
875
975
|
}
|
|
876
976
|
catch (e) {
|
|
@@ -879,179 +979,108 @@ class StableBrowser {
|
|
|
879
979
|
}
|
|
880
980
|
else {
|
|
881
981
|
//await this.closeUnexpectedPopups();
|
|
882
|
-
info.log += "setCheck failed, will try again" + "\n";
|
|
883
|
-
element = await this._locate(selectors, info, _params);
|
|
884
|
-
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 });
|
|
885
985
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
886
986
|
}
|
|
887
987
|
}
|
|
888
988
|
await this.waitForPageLoad();
|
|
889
|
-
return info;
|
|
989
|
+
return state.info;
|
|
890
990
|
}
|
|
891
991
|
catch (e) {
|
|
892
|
-
|
|
893
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
894
|
-
info.screenshotPath = screenshotPath;
|
|
895
|
-
Object.assign(e, { info: info });
|
|
896
|
-
error = e;
|
|
897
|
-
throw e;
|
|
992
|
+
await _commandError(state, e, this);
|
|
898
993
|
}
|
|
899
994
|
finally {
|
|
900
|
-
|
|
901
|
-
this._reportToWorld(world, {
|
|
902
|
-
element_name: selectors.element_name,
|
|
903
|
-
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
904
|
-
text: checked ? `Check element` : `Uncheck element`,
|
|
905
|
-
screenshotId,
|
|
906
|
-
result: error
|
|
907
|
-
? {
|
|
908
|
-
status: "FAILED",
|
|
909
|
-
startTime,
|
|
910
|
-
endTime,
|
|
911
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
912
|
-
}
|
|
913
|
-
: {
|
|
914
|
-
status: "PASSED",
|
|
915
|
-
startTime,
|
|
916
|
-
endTime,
|
|
917
|
-
},
|
|
918
|
-
info: info,
|
|
919
|
-
});
|
|
995
|
+
_commandFinally(state, this);
|
|
920
996
|
}
|
|
921
997
|
}
|
|
922
998
|
async hover(selectors, _params, options = {}, world = null) {
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
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
|
+
};
|
|
932
1009
|
try {
|
|
933
|
-
|
|
934
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1010
|
+
await _preCommand(state, this);
|
|
935
1011
|
try {
|
|
936
|
-
await
|
|
937
|
-
await element.hover();
|
|
1012
|
+
await state.element.hover();
|
|
938
1013
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
939
1014
|
}
|
|
940
1015
|
catch (e) {
|
|
941
1016
|
//await this.closeUnexpectedPopups();
|
|
942
|
-
info.log += "hover failed, will try again" + "\n";
|
|
943
|
-
element = await this._locate(selectors, info, _params);
|
|
944
|
-
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 });
|
|
945
1020
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
946
1021
|
}
|
|
947
1022
|
await this.waitForPageLoad();
|
|
948
|
-
return info;
|
|
1023
|
+
return state.info;
|
|
949
1024
|
}
|
|
950
1025
|
catch (e) {
|
|
951
|
-
|
|
952
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
953
|
-
info.screenshotPath = screenshotPath;
|
|
954
|
-
Object.assign(e, { info: info });
|
|
955
|
-
error = e;
|
|
956
|
-
throw e;
|
|
1026
|
+
await _commandError(state, e, this);
|
|
957
1027
|
}
|
|
958
1028
|
finally {
|
|
959
|
-
|
|
960
|
-
this._reportToWorld(world, {
|
|
961
|
-
element_name: selectors.element_name,
|
|
962
|
-
type: Types.HOVER,
|
|
963
|
-
text: `Hover element`,
|
|
964
|
-
screenshotId,
|
|
965
|
-
result: error
|
|
966
|
-
? {
|
|
967
|
-
status: "FAILED",
|
|
968
|
-
startTime,
|
|
969
|
-
endTime,
|
|
970
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
971
|
-
}
|
|
972
|
-
: {
|
|
973
|
-
status: "PASSED",
|
|
974
|
-
startTime,
|
|
975
|
-
endTime,
|
|
976
|
-
},
|
|
977
|
-
info: info,
|
|
978
|
-
});
|
|
1029
|
+
_commandFinally(state, this);
|
|
979
1030
|
}
|
|
980
1031
|
}
|
|
981
1032
|
async selectOption(selectors, values, _params = null, options = {}, world = null) {
|
|
982
|
-
this._validateSelectors(selectors);
|
|
983
1033
|
if (!values) {
|
|
984
1034
|
throw new Error("values is null");
|
|
985
1035
|
}
|
|
986
|
-
const
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
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
|
+
};
|
|
994
1047
|
try {
|
|
995
|
-
|
|
996
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1048
|
+
await _preCommand(state, this);
|
|
997
1049
|
try {
|
|
998
|
-
await
|
|
999
|
-
await element.selectOption(values);
|
|
1050
|
+
await state.element.selectOption(values);
|
|
1000
1051
|
}
|
|
1001
1052
|
catch (e) {
|
|
1002
1053
|
//await this.closeUnexpectedPopups();
|
|
1003
|
-
info.log += "selectOption failed, will try force" + "\n";
|
|
1004
|
-
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 });
|
|
1005
1056
|
}
|
|
1006
1057
|
await this.waitForPageLoad();
|
|
1007
|
-
return info;
|
|
1058
|
+
return state.info;
|
|
1008
1059
|
}
|
|
1009
1060
|
catch (e) {
|
|
1010
|
-
|
|
1011
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1012
|
-
info.screenshotPath = screenshotPath;
|
|
1013
|
-
Object.assign(e, { info: info });
|
|
1014
|
-
this.logger.info("click failed, will try next selector");
|
|
1015
|
-
error = e;
|
|
1016
|
-
throw e;
|
|
1061
|
+
await _commandError(state, e, this);
|
|
1017
1062
|
}
|
|
1018
1063
|
finally {
|
|
1019
|
-
|
|
1020
|
-
this._reportToWorld(world, {
|
|
1021
|
-
element_name: selectors.element_name,
|
|
1022
|
-
type: Types.SELECT,
|
|
1023
|
-
text: `Select option: ${values}`,
|
|
1024
|
-
value: values.toString(),
|
|
1025
|
-
screenshotId,
|
|
1026
|
-
result: error
|
|
1027
|
-
? {
|
|
1028
|
-
status: "FAILED",
|
|
1029
|
-
startTime,
|
|
1030
|
-
endTime,
|
|
1031
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1032
|
-
}
|
|
1033
|
-
: {
|
|
1034
|
-
status: "PASSED",
|
|
1035
|
-
startTime,
|
|
1036
|
-
endTime,
|
|
1037
|
-
},
|
|
1038
|
-
info: info,
|
|
1039
|
-
});
|
|
1064
|
+
_commandFinally(state, this);
|
|
1040
1065
|
}
|
|
1041
1066
|
}
|
|
1042
1067
|
async type(_value, _params = null, options = {}, world = null) {
|
|
1043
|
-
const
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
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
|
+
};
|
|
1052
1081
|
try {
|
|
1053
|
-
|
|
1054
|
-
const valueSegment =
|
|
1082
|
+
await _preCommand(state, this);
|
|
1083
|
+
const valueSegment = state.value.split("&&");
|
|
1055
1084
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1056
1085
|
if (i > 0) {
|
|
1057
1086
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1071,134 +1100,76 @@ class StableBrowser {
|
|
|
1071
1100
|
await this.page.keyboard.type(value);
|
|
1072
1101
|
}
|
|
1073
1102
|
}
|
|
1074
|
-
return info;
|
|
1103
|
+
return state.info;
|
|
1075
1104
|
}
|
|
1076
1105
|
catch (e) {
|
|
1077
|
-
|
|
1078
|
-
this.logger.error("type failed " + info.log);
|
|
1079
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1080
|
-
info.screenshotPath = screenshotPath;
|
|
1081
|
-
Object.assign(e, { info: info });
|
|
1082
|
-
error = e;
|
|
1083
|
-
throw e;
|
|
1106
|
+
await _commandError(state, e, this);
|
|
1084
1107
|
}
|
|
1085
1108
|
finally {
|
|
1086
|
-
|
|
1087
|
-
this._reportToWorld(world, {
|
|
1088
|
-
type: Types.TYPE_PRESS,
|
|
1089
|
-
screenshotId,
|
|
1090
|
-
value: _value,
|
|
1091
|
-
text: `type value: ${_value}`,
|
|
1092
|
-
result: error
|
|
1093
|
-
? {
|
|
1094
|
-
status: "FAILED",
|
|
1095
|
-
startTime,
|
|
1096
|
-
endTime,
|
|
1097
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1098
|
-
}
|
|
1099
|
-
: {
|
|
1100
|
-
status: "PASSED",
|
|
1101
|
-
startTime,
|
|
1102
|
-
endTime,
|
|
1103
|
-
},
|
|
1104
|
-
info: info,
|
|
1105
|
-
});
|
|
1109
|
+
_commandFinally(state, this);
|
|
1106
1110
|
}
|
|
1107
1111
|
}
|
|
1108
1112
|
async setInputValue(selectors, value, _params = null, options = {}, world = null) {
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
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
|
+
};
|
|
1121
1124
|
try {
|
|
1122
|
-
|
|
1123
|
-
let
|
|
1124
|
-
await this.scrollIfNeeded(element, info);
|
|
1125
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1126
|
-
await this._highlightElements(element);
|
|
1125
|
+
await _preCommand(state, this);
|
|
1126
|
+
let value = await this._replaceWithLocalData(state.value, this);
|
|
1127
1127
|
try {
|
|
1128
|
-
await element.evaluateHandle((el, value) => {
|
|
1128
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1129
1129
|
el.value = value;
|
|
1130
1130
|
}, value);
|
|
1131
1131
|
}
|
|
1132
1132
|
catch (error) {
|
|
1133
1133
|
this.logger.error("setInputValue failed, will try again");
|
|
1134
|
-
|
|
1135
|
-
info.
|
|
1136
|
-
|
|
1137
|
-
await element.evaluateHandle((el, value) => {
|
|
1134
|
+
await _screenshot(state, this);
|
|
1135
|
+
Object.assign(error, { info: state.info });
|
|
1136
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1138
1137
|
el.value = value;
|
|
1139
1138
|
});
|
|
1140
1139
|
}
|
|
1141
1140
|
}
|
|
1142
1141
|
catch (e) {
|
|
1143
|
-
|
|
1144
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1145
|
-
info.screenshotPath = screenshotPath;
|
|
1146
|
-
Object.assign(e, { info: info });
|
|
1147
|
-
error = e;
|
|
1148
|
-
throw e;
|
|
1142
|
+
await _commandError(state, e, this);
|
|
1149
1143
|
}
|
|
1150
1144
|
finally {
|
|
1151
|
-
|
|
1152
|
-
this._reportToWorld(world, {
|
|
1153
|
-
element_name: selectors.element_name,
|
|
1154
|
-
type: Types.SET_INPUT,
|
|
1155
|
-
text: `Set input value`,
|
|
1156
|
-
value: value,
|
|
1157
|
-
screenshotId,
|
|
1158
|
-
result: error
|
|
1159
|
-
? {
|
|
1160
|
-
status: "FAILED",
|
|
1161
|
-
startTime,
|
|
1162
|
-
endTime,
|
|
1163
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1164
|
-
}
|
|
1165
|
-
: {
|
|
1166
|
-
status: "PASSED",
|
|
1167
|
-
startTime,
|
|
1168
|
-
endTime,
|
|
1169
|
-
},
|
|
1170
|
-
info: info,
|
|
1171
|
-
});
|
|
1145
|
+
_commandFinally(state, this);
|
|
1172
1146
|
}
|
|
1173
1147
|
}
|
|
1174
1148
|
async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
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
|
+
};
|
|
1185
1161
|
try {
|
|
1186
|
-
|
|
1187
|
-
let element = await this._locate(selectors, info, _params);
|
|
1188
|
-
//insert red border around the element
|
|
1189
|
-
await this.scrollIfNeeded(element, info);
|
|
1190
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1191
|
-
await this._highlightElements(element);
|
|
1162
|
+
await _preCommand(state, this);
|
|
1192
1163
|
try {
|
|
1193
|
-
await element.click();
|
|
1164
|
+
await state.element.click();
|
|
1194
1165
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1195
1166
|
if (format) {
|
|
1196
|
-
value = dayjs(value).format(format);
|
|
1197
|
-
await element.fill(value);
|
|
1167
|
+
state.value = dayjs(state.value).format(format);
|
|
1168
|
+
await state.element.fill(state.value);
|
|
1198
1169
|
}
|
|
1199
1170
|
else {
|
|
1200
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1201
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1171
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1172
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1202
1173
|
el.value = ""; // clear input
|
|
1203
1174
|
el.value = dateTimeValue;
|
|
1204
1175
|
}, dateTimeValue);
|
|
@@ -1211,20 +1182,19 @@ class StableBrowser {
|
|
|
1211
1182
|
}
|
|
1212
1183
|
catch (err) {
|
|
1213
1184
|
//await this.closeUnexpectedPopups();
|
|
1214
|
-
this.logger.error("setting date time input failed " + JSON.stringify(info));
|
|
1185
|
+
this.logger.error("setting date time input failed " + JSON.stringify(state.info));
|
|
1215
1186
|
this.logger.info("Trying again");
|
|
1216
|
-
|
|
1217
|
-
info.
|
|
1218
|
-
Object.assign(err, { info: info });
|
|
1187
|
+
await _screenshot(state, this);
|
|
1188
|
+
Object.assign(err, { info: state.info });
|
|
1219
1189
|
await element.click();
|
|
1220
1190
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1221
1191
|
if (format) {
|
|
1222
|
-
value = dayjs(value).format(format);
|
|
1223
|
-
await element.fill(value);
|
|
1192
|
+
state.value = dayjs(state.value).format(format);
|
|
1193
|
+
await state.element.fill(state.value);
|
|
1224
1194
|
}
|
|
1225
1195
|
else {
|
|
1226
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1227
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1196
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1197
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1228
1198
|
el.value = ""; // clear input
|
|
1229
1199
|
el.value = dateTimeValue;
|
|
1230
1200
|
}, dateTimeValue);
|
|
@@ -1237,60 +1207,39 @@ class StableBrowser {
|
|
|
1237
1207
|
}
|
|
1238
1208
|
}
|
|
1239
1209
|
catch (e) {
|
|
1240
|
-
|
|
1241
|
-
throw e;
|
|
1210
|
+
await _commandError(state, e, this);
|
|
1242
1211
|
}
|
|
1243
1212
|
finally {
|
|
1244
|
-
|
|
1245
|
-
this._reportToWorld(world, {
|
|
1246
|
-
element_name: selectors.element_name,
|
|
1247
|
-
type: Types.SET_DATE_TIME,
|
|
1248
|
-
screenshotId,
|
|
1249
|
-
value: value,
|
|
1250
|
-
text: `setDateTime input with value: ${value}`,
|
|
1251
|
-
result: error
|
|
1252
|
-
? {
|
|
1253
|
-
status: "FAILED",
|
|
1254
|
-
startTime,
|
|
1255
|
-
endTime,
|
|
1256
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1257
|
-
}
|
|
1258
|
-
: {
|
|
1259
|
-
status: "PASSED",
|
|
1260
|
-
startTime,
|
|
1261
|
-
endTime,
|
|
1262
|
-
},
|
|
1263
|
-
info: info,
|
|
1264
|
-
});
|
|
1213
|
+
_commandFinally(state, this);
|
|
1265
1214
|
}
|
|
1266
1215
|
}
|
|
1267
1216
|
async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
|
|
1268
1217
|
_value = unEscapeString(_value);
|
|
1269
|
-
this._validateSelectors(selectors);
|
|
1270
|
-
const startTime = Date.now();
|
|
1271
|
-
let error = null;
|
|
1272
|
-
let screenshotId = null;
|
|
1273
|
-
let screenshotPath = null;
|
|
1274
|
-
const info = {};
|
|
1275
|
-
info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
|
|
1276
|
-
info.operation = "clickType";
|
|
1277
|
-
info.selectors = selectors;
|
|
1278
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
|
+
};
|
|
1279
1231
|
if (newValue !== _value) {
|
|
1280
1232
|
//this.logger.info(_value + "=" + newValue);
|
|
1281
1233
|
_value = newValue;
|
|
1282
1234
|
}
|
|
1283
|
-
info.value = _value;
|
|
1284
1235
|
try {
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
await this.scrollIfNeeded(element, info);
|
|
1288
|
-
await this._highlightElements(element);
|
|
1236
|
+
await _preCommand(state, this);
|
|
1237
|
+
state.info.value = _value;
|
|
1289
1238
|
if (options === null || options === undefined || !options.press) {
|
|
1290
1239
|
try {
|
|
1291
|
-
let currentValue = await element.inputValue();
|
|
1240
|
+
let currentValue = await state.element.inputValue();
|
|
1292
1241
|
if (currentValue) {
|
|
1293
|
-
await element.fill("");
|
|
1242
|
+
await state.element.fill("");
|
|
1294
1243
|
}
|
|
1295
1244
|
}
|
|
1296
1245
|
catch (e) {
|
|
@@ -1299,22 +1248,22 @@ class StableBrowser {
|
|
|
1299
1248
|
}
|
|
1300
1249
|
if (options === null || options === undefined || options.press) {
|
|
1301
1250
|
try {
|
|
1302
|
-
await element.click({ timeout: 5000 });
|
|
1251
|
+
await state.element.click({ timeout: 5000 });
|
|
1303
1252
|
}
|
|
1304
1253
|
catch (e) {
|
|
1305
|
-
await element.dispatchEvent("click");
|
|
1254
|
+
await state.element.dispatchEvent("click");
|
|
1306
1255
|
}
|
|
1307
1256
|
}
|
|
1308
1257
|
else {
|
|
1309
1258
|
try {
|
|
1310
|
-
await element.focus();
|
|
1259
|
+
await state.element.focus();
|
|
1311
1260
|
}
|
|
1312
1261
|
catch (e) {
|
|
1313
|
-
await element.dispatchEvent("focus");
|
|
1262
|
+
await state.element.dispatchEvent("focus");
|
|
1314
1263
|
}
|
|
1315
1264
|
}
|
|
1316
1265
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1317
|
-
const valueSegment =
|
|
1266
|
+
const valueSegment = state.value.split("&&");
|
|
1318
1267
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1319
1268
|
if (i > 0) {
|
|
1320
1269
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1334,14 +1283,14 @@ class StableBrowser {
|
|
|
1334
1283
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1335
1284
|
}
|
|
1336
1285
|
}
|
|
1337
|
-
|
|
1286
|
+
await _screenshot(state, this);
|
|
1338
1287
|
if (enter === true) {
|
|
1339
1288
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1340
1289
|
await this.page.keyboard.press("Enter");
|
|
1341
1290
|
await this.waitForPageLoad();
|
|
1342
1291
|
}
|
|
1343
1292
|
else if (enter === false) {
|
|
1344
|
-
await element.dispatchEvent("change");
|
|
1293
|
+
await state.element.dispatchEvent("change");
|
|
1345
1294
|
//await this.page.keyboard.press("Tab");
|
|
1346
1295
|
}
|
|
1347
1296
|
else {
|
|
@@ -1350,104 +1299,50 @@ class StableBrowser {
|
|
|
1350
1299
|
await this.waitForPageLoad();
|
|
1351
1300
|
}
|
|
1352
1301
|
}
|
|
1353
|
-
return info;
|
|
1302
|
+
return state.info;
|
|
1354
1303
|
}
|
|
1355
1304
|
catch (e) {
|
|
1356
|
-
|
|
1357
|
-
this.logger.error("fill failed " + JSON.stringify(info));
|
|
1358
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1359
|
-
info.screenshotPath = screenshotPath;
|
|
1360
|
-
Object.assign(e, { info: info });
|
|
1361
|
-
error = e;
|
|
1362
|
-
throw e;
|
|
1305
|
+
await _commandError(state, e, this);
|
|
1363
1306
|
}
|
|
1364
1307
|
finally {
|
|
1365
|
-
|
|
1366
|
-
this._reportToWorld(world, {
|
|
1367
|
-
element_name: selectors.element_name,
|
|
1368
|
-
type: Types.FILL,
|
|
1369
|
-
screenshotId,
|
|
1370
|
-
value: _value,
|
|
1371
|
-
text: `clickType input with value: ${_value}`,
|
|
1372
|
-
result: error
|
|
1373
|
-
? {
|
|
1374
|
-
status: "FAILED",
|
|
1375
|
-
startTime,
|
|
1376
|
-
endTime,
|
|
1377
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1378
|
-
}
|
|
1379
|
-
: {
|
|
1380
|
-
status: "PASSED",
|
|
1381
|
-
startTime,
|
|
1382
|
-
endTime,
|
|
1383
|
-
},
|
|
1384
|
-
info: info,
|
|
1385
|
-
});
|
|
1308
|
+
_commandFinally(state, this);
|
|
1386
1309
|
}
|
|
1387
1310
|
}
|
|
1388
1311
|
async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
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
|
+
};
|
|
1400
1323
|
try {
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
await
|
|
1404
|
-
await element.fill(value);
|
|
1405
|
-
await element.dispatchEvent("change");
|
|
1324
|
+
await _preCommand(state, this);
|
|
1325
|
+
await state.element.fill(value);
|
|
1326
|
+
await state.element.dispatchEvent("change");
|
|
1406
1327
|
if (enter) {
|
|
1407
1328
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1408
1329
|
await this.page.keyboard.press("Enter");
|
|
1409
1330
|
}
|
|
1410
1331
|
await this.waitForPageLoad();
|
|
1411
|
-
return info;
|
|
1332
|
+
return state.info;
|
|
1412
1333
|
}
|
|
1413
|
-
catch (e) {
|
|
1414
|
-
|
|
1415
|
-
this.logger.error("fill failed " + info.log);
|
|
1416
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1417
|
-
info.screenshotPath = screenshotPath;
|
|
1418
|
-
Object.assign(e, { info: info });
|
|
1419
|
-
error = e;
|
|
1420
|
-
throw e;
|
|
1334
|
+
catch (e) {
|
|
1335
|
+
await _commandError(state, e, this);
|
|
1421
1336
|
}
|
|
1422
1337
|
finally {
|
|
1423
|
-
|
|
1424
|
-
this._reportToWorld(world, {
|
|
1425
|
-
element_name: selectors.element_name,
|
|
1426
|
-
type: Types.FILL,
|
|
1427
|
-
screenshotId,
|
|
1428
|
-
value,
|
|
1429
|
-
text: `Fill input with value: ${value}`,
|
|
1430
|
-
result: error
|
|
1431
|
-
? {
|
|
1432
|
-
status: "FAILED",
|
|
1433
|
-
startTime,
|
|
1434
|
-
endTime,
|
|
1435
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1436
|
-
}
|
|
1437
|
-
: {
|
|
1438
|
-
status: "PASSED",
|
|
1439
|
-
startTime,
|
|
1440
|
-
endTime,
|
|
1441
|
-
},
|
|
1442
|
-
info: info,
|
|
1443
|
-
});
|
|
1338
|
+
_commandFinally(state, this);
|
|
1444
1339
|
}
|
|
1445
1340
|
}
|
|
1446
1341
|
async getText(selectors, _params = null, options = {}, info = {}, world = null) {
|
|
1447
1342
|
return await this._getText(selectors, 0, _params, options, info, world);
|
|
1448
1343
|
}
|
|
1449
1344
|
async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
|
|
1450
|
-
|
|
1345
|
+
_validateSelectors(selectors);
|
|
1451
1346
|
let screenshotId = null;
|
|
1452
1347
|
let screenshotPath = null;
|
|
1453
1348
|
if (!info.log) {
|
|
@@ -1491,166 +1386,124 @@ class StableBrowser {
|
|
|
1491
1386
|
}
|
|
1492
1387
|
}
|
|
1493
1388
|
async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
|
|
1494
|
-
var _a;
|
|
1495
|
-
this._validateSelectors(selectors);
|
|
1496
1389
|
if (!pattern) {
|
|
1497
1390
|
throw new Error("pattern is null");
|
|
1498
1391
|
}
|
|
1499
1392
|
if (!text) {
|
|
1500
1393
|
throw new Error("text is null");
|
|
1501
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
|
+
};
|
|
1502
1411
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1503
1412
|
if (newValue !== text) {
|
|
1504
1413
|
this.logger.info(text + "=" + newValue);
|
|
1505
1414
|
text = newValue;
|
|
1506
1415
|
}
|
|
1507
|
-
const startTime = Date.now();
|
|
1508
|
-
let error = null;
|
|
1509
|
-
let screenshotId = null;
|
|
1510
|
-
let screenshotPath = null;
|
|
1511
|
-
const info = {};
|
|
1512
|
-
info.log =
|
|
1513
|
-
"***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
|
|
1514
|
-
info.operation = "containsPattern";
|
|
1515
|
-
info.selectors = selectors;
|
|
1516
|
-
info.value = text;
|
|
1517
|
-
info.pattern = pattern;
|
|
1518
1416
|
let foundObj = null;
|
|
1519
1417
|
try {
|
|
1520
|
-
|
|
1418
|
+
await _preCommand(state, this);
|
|
1419
|
+
state.info.pattern = pattern;
|
|
1420
|
+
foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
|
|
1521
1421
|
if (foundObj && foundObj.element) {
|
|
1522
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1422
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1523
1423
|
}
|
|
1524
|
-
|
|
1424
|
+
await _screenshot(state, this);
|
|
1525
1425
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1526
1426
|
pattern = pattern.replace("{text}", escapedText);
|
|
1527
1427
|
let regex = new RegExp(pattern, "im");
|
|
1528
|
-
if (!regex.test(foundObj
|
|
1529
|
-
info.foundText = foundObj
|
|
1428
|
+
if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
|
|
1429
|
+
state.info.foundText = foundObj?.text;
|
|
1530
1430
|
throw new Error("element doesn't contain text " + text);
|
|
1531
1431
|
}
|
|
1532
|
-
return info;
|
|
1432
|
+
return state.info;
|
|
1533
1433
|
}
|
|
1534
1434
|
catch (e) {
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
|
|
1538
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1539
|
-
info.screenshotPath = screenshotPath;
|
|
1540
|
-
Object.assign(e, { info: info });
|
|
1541
|
-
error = e;
|
|
1542
|
-
throw e;
|
|
1435
|
+
this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
|
|
1436
|
+
await _commandError(state, e, this);
|
|
1543
1437
|
}
|
|
1544
1438
|
finally {
|
|
1545
|
-
|
|
1546
|
-
this._reportToWorld(world, {
|
|
1547
|
-
element_name: selectors.element_name,
|
|
1548
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1549
|
-
value: pattern,
|
|
1550
|
-
text: `Verify element contains pattern: ${pattern}`,
|
|
1551
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1552
|
-
result: error
|
|
1553
|
-
? {
|
|
1554
|
-
status: "FAILED",
|
|
1555
|
-
startTime,
|
|
1556
|
-
endTime,
|
|
1557
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1558
|
-
}
|
|
1559
|
-
: {
|
|
1560
|
-
status: "PASSED",
|
|
1561
|
-
startTime,
|
|
1562
|
-
endTime,
|
|
1563
|
-
},
|
|
1564
|
-
info: info,
|
|
1565
|
-
});
|
|
1439
|
+
_commandFinally(state, this);
|
|
1566
1440
|
}
|
|
1567
1441
|
}
|
|
1568
1442
|
async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
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
|
+
};
|
|
1572
1458
|
if (!text) {
|
|
1573
1459
|
throw new Error("text is null");
|
|
1574
1460
|
}
|
|
1575
|
-
|
|
1576
|
-
let error = null;
|
|
1577
|
-
let screenshotId = null;
|
|
1578
|
-
let screenshotPath = null;
|
|
1579
|
-
const info = {};
|
|
1580
|
-
info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
|
|
1581
|
-
info.operation = "containsText";
|
|
1582
|
-
info.selectors = selectors;
|
|
1461
|
+
text = unEscapeString(text);
|
|
1583
1462
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1584
1463
|
if (newValue !== text) {
|
|
1585
1464
|
this.logger.info(text + "=" + newValue);
|
|
1586
1465
|
text = newValue;
|
|
1587
1466
|
}
|
|
1588
|
-
info.value = text;
|
|
1589
1467
|
let foundObj = null;
|
|
1590
1468
|
try {
|
|
1591
|
-
|
|
1469
|
+
await _preCommand(state, this);
|
|
1470
|
+
foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
|
|
1592
1471
|
if (foundObj && foundObj.element) {
|
|
1593
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1472
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1594
1473
|
}
|
|
1595
|
-
|
|
1474
|
+
await _screenshot(state, this);
|
|
1596
1475
|
const dateAlternatives = findDateAlternatives(text);
|
|
1597
1476
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1598
1477
|
if (dateAlternatives.date) {
|
|
1599
1478
|
for (let i = 0; i < dateAlternatives.dates.length; i++) {
|
|
1600
|
-
if (
|
|
1601
|
-
|
|
1602
|
-
return info;
|
|
1479
|
+
if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
|
|
1480
|
+
foundObj?.value?.includes(dateAlternatives.dates[i])) {
|
|
1481
|
+
return state.info;
|
|
1603
1482
|
}
|
|
1604
1483
|
}
|
|
1605
1484
|
throw new Error("element doesn't contain text " + text);
|
|
1606
1485
|
}
|
|
1607
1486
|
else if (numberAlternatives.number) {
|
|
1608
1487
|
for (let i = 0; i < numberAlternatives.numbers.length; i++) {
|
|
1609
|
-
if (
|
|
1610
|
-
|
|
1611
|
-
return info;
|
|
1488
|
+
if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
|
|
1489
|
+
foundObj?.value?.includes(numberAlternatives.numbers[i])) {
|
|
1490
|
+
return state.info;
|
|
1612
1491
|
}
|
|
1613
1492
|
}
|
|
1614
1493
|
throw new Error("element doesn't contain text " + text);
|
|
1615
1494
|
}
|
|
1616
|
-
else if (!
|
|
1617
|
-
info.foundText = foundObj
|
|
1618
|
-
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;
|
|
1619
1498
|
throw new Error("element doesn't contain text " + text);
|
|
1620
1499
|
}
|
|
1621
|
-
return info;
|
|
1500
|
+
return state.info;
|
|
1622
1501
|
}
|
|
1623
1502
|
catch (e) {
|
|
1624
|
-
|
|
1625
|
-
this.logger.error("verify element contains text failed " + info.log);
|
|
1626
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1627
|
-
info.screenshotPath = screenshotPath;
|
|
1628
|
-
Object.assign(e, { info: info });
|
|
1629
|
-
error = e;
|
|
1630
|
-
throw e;
|
|
1503
|
+
await _commandError(state, e, this);
|
|
1631
1504
|
}
|
|
1632
1505
|
finally {
|
|
1633
|
-
|
|
1634
|
-
this._reportToWorld(world, {
|
|
1635
|
-
element_name: selectors.element_name,
|
|
1636
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1637
|
-
text: `Verify element contains text: ${text}`,
|
|
1638
|
-
value: text,
|
|
1639
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1640
|
-
result: error
|
|
1641
|
-
? {
|
|
1642
|
-
status: "FAILED",
|
|
1643
|
-
startTime,
|
|
1644
|
-
endTime,
|
|
1645
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1646
|
-
}
|
|
1647
|
-
: {
|
|
1648
|
-
status: "PASSED",
|
|
1649
|
-
startTime,
|
|
1650
|
-
endTime,
|
|
1651
|
-
},
|
|
1652
|
-
info: info,
|
|
1653
|
-
});
|
|
1506
|
+
_commandFinally(state, this);
|
|
1654
1507
|
}
|
|
1655
1508
|
}
|
|
1656
1509
|
_getDataFile(world = null) {
|
|
@@ -1924,127 +1777,69 @@ class StableBrowser {
|
|
|
1924
1777
|
}
|
|
1925
1778
|
}
|
|
1926
1779
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
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
|
+
};
|
|
1932
1790
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1933
|
-
const info = {};
|
|
1934
|
-
info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
|
|
1935
|
-
info.operation = "verify";
|
|
1936
|
-
info.selectors = selectors;
|
|
1937
1791
|
try {
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
}
|
|
1942
|
-
await this._highlightElements(element);
|
|
1943
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1944
|
-
await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
1945
|
-
return info;
|
|
1792
|
+
await _preCommand(state, this);
|
|
1793
|
+
await expect(state.element).toHaveCount(1, { timeout: 10000 });
|
|
1794
|
+
return state.info;
|
|
1946
1795
|
}
|
|
1947
1796
|
catch (e) {
|
|
1948
|
-
|
|
1949
|
-
this.logger.error("verify failed " + info.log);
|
|
1950
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1951
|
-
info.screenshotPath = screenshotPath;
|
|
1952
|
-
Object.assign(e, { info: info });
|
|
1953
|
-
error = e;
|
|
1954
|
-
throw e;
|
|
1797
|
+
await _commandError(state, e, this);
|
|
1955
1798
|
}
|
|
1956
1799
|
finally {
|
|
1957
|
-
|
|
1958
|
-
this._reportToWorld(world, {
|
|
1959
|
-
element_name: selectors.element_name,
|
|
1960
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1961
|
-
text: "Verify element exists in page",
|
|
1962
|
-
screenshotId,
|
|
1963
|
-
result: error
|
|
1964
|
-
? {
|
|
1965
|
-
status: "FAILED",
|
|
1966
|
-
startTime,
|
|
1967
|
-
endTime,
|
|
1968
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1969
|
-
}
|
|
1970
|
-
: {
|
|
1971
|
-
status: "PASSED",
|
|
1972
|
-
startTime,
|
|
1973
|
-
endTime,
|
|
1974
|
-
},
|
|
1975
|
-
info: info,
|
|
1976
|
-
});
|
|
1800
|
+
_commandFinally(state, this);
|
|
1977
1801
|
}
|
|
1978
1802
|
}
|
|
1979
1803
|
async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
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
|
+
};
|
|
1985
1816
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1986
|
-
const info = {};
|
|
1987
|
-
info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
|
|
1988
|
-
info.operation = "extract";
|
|
1989
|
-
info.selectors = selectors;
|
|
1990
1817
|
try {
|
|
1991
|
-
|
|
1992
|
-
await this._highlightElements(element);
|
|
1993
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1818
|
+
await _preCommand(state, this);
|
|
1994
1819
|
switch (attribute) {
|
|
1995
1820
|
case "inner_text":
|
|
1996
|
-
|
|
1821
|
+
state.value = await state.element.innerText();
|
|
1997
1822
|
break;
|
|
1998
1823
|
case "href":
|
|
1999
|
-
|
|
1824
|
+
state.value = await state.element.getAttribute("href");
|
|
2000
1825
|
break;
|
|
2001
1826
|
case "value":
|
|
2002
|
-
|
|
1827
|
+
state.value = await state.element.inputValue();
|
|
2003
1828
|
break;
|
|
2004
1829
|
default:
|
|
2005
|
-
|
|
1830
|
+
state.value = await state.element.getAttribute(attribute);
|
|
2006
1831
|
break;
|
|
2007
1832
|
}
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
this.setTestData({ [variable]: info.value }, world);
|
|
2013
|
-
this.logger.info("set test data: " + variable + "=" + info.value);
|
|
2014
|
-
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;
|
|
2015
1837
|
}
|
|
2016
1838
|
catch (e) {
|
|
2017
|
-
|
|
2018
|
-
this.logger.error("extract failed " + info.log);
|
|
2019
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2020
|
-
info.screenshotPath = screenshotPath;
|
|
2021
|
-
Object.assign(e, { info: info });
|
|
2022
|
-
error = e;
|
|
2023
|
-
throw e;
|
|
1839
|
+
await _commandError(state, e, this);
|
|
2024
1840
|
}
|
|
2025
1841
|
finally {
|
|
2026
|
-
|
|
2027
|
-
this._reportToWorld(world, {
|
|
2028
|
-
element_name: selectors.element_name,
|
|
2029
|
-
type: Types.EXTRACT_ATTRIBUTE,
|
|
2030
|
-
variable: variable,
|
|
2031
|
-
value: info.value,
|
|
2032
|
-
text: "Extract attribute from element",
|
|
2033
|
-
screenshotId,
|
|
2034
|
-
result: error
|
|
2035
|
-
? {
|
|
2036
|
-
status: "FAILED",
|
|
2037
|
-
startTime,
|
|
2038
|
-
endTime,
|
|
2039
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2040
|
-
}
|
|
2041
|
-
: {
|
|
2042
|
-
status: "PASSED",
|
|
2043
|
-
startTime,
|
|
2044
|
-
endTime,
|
|
2045
|
-
},
|
|
2046
|
-
info: info,
|
|
2047
|
-
});
|
|
1842
|
+
_commandFinally(state, this);
|
|
2048
1843
|
}
|
|
2049
1844
|
}
|
|
2050
1845
|
async extractEmailData(emailAddress, options, world) {
|
|
@@ -2121,7 +1916,8 @@ class StableBrowser {
|
|
|
2121
1916
|
catch (e) {
|
|
2122
1917
|
errorCount++;
|
|
2123
1918
|
if (errorCount > 3) {
|
|
2124
|
-
throw e;
|
|
1919
|
+
// throw e;
|
|
1920
|
+
await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
|
|
2125
1921
|
}
|
|
2126
1922
|
// ignore
|
|
2127
1923
|
}
|
|
@@ -2232,11 +2028,12 @@ class StableBrowser {
|
|
|
2232
2028
|
info.screenshotPath = screenshotPath;
|
|
2233
2029
|
Object.assign(e, { info: info });
|
|
2234
2030
|
error = e;
|
|
2235
|
-
throw e;
|
|
2031
|
+
// throw e;
|
|
2032
|
+
await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
|
|
2236
2033
|
}
|
|
2237
2034
|
finally {
|
|
2238
2035
|
const endTime = Date.now();
|
|
2239
|
-
|
|
2036
|
+
_reportToWorld(world, {
|
|
2240
2037
|
type: Types.VERIFY_PAGE_PATH,
|
|
2241
2038
|
text: "Verify page path",
|
|
2242
2039
|
screenshotId,
|
|
@@ -2245,7 +2042,7 @@ class StableBrowser {
|
|
|
2245
2042
|
status: "FAILED",
|
|
2246
2043
|
startTime,
|
|
2247
2044
|
endTime,
|
|
2248
|
-
message: error
|
|
2045
|
+
message: error?.message,
|
|
2249
2046
|
}
|
|
2250
2047
|
: {
|
|
2251
2048
|
status: "PASSED",
|
|
@@ -2258,52 +2055,58 @@ class StableBrowser {
|
|
|
2258
2055
|
}
|
|
2259
2056
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2260
2057
|
text = unEscapeString(text);
|
|
2261
|
-
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
|
+
};
|
|
2262
2070
|
const timeout = this._getLoadTimeout(options);
|
|
2263
|
-
let error = null;
|
|
2264
|
-
let screenshotId = null;
|
|
2265
|
-
let screenshotPath = null;
|
|
2266
2071
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2267
|
-
const info = {};
|
|
2268
|
-
info.log = "***** verify text " + text + " exists in page *****\n";
|
|
2269
|
-
info.operation = "verifyTextExistInPage";
|
|
2270
2072
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
2271
2073
|
if (newValue !== text) {
|
|
2272
2074
|
this.logger.info(text + "=" + newValue);
|
|
2273
2075
|
text = newValue;
|
|
2274
2076
|
}
|
|
2275
|
-
info.text = text;
|
|
2276
2077
|
let dateAlternatives = findDateAlternatives(text);
|
|
2277
2078
|
let numberAlternatives = findNumberAlternatives(text);
|
|
2278
2079
|
try {
|
|
2080
|
+
await _preCommand(state, this);
|
|
2081
|
+
state.info.text = text;
|
|
2279
2082
|
while (true) {
|
|
2280
2083
|
const frames = this.page.frames();
|
|
2281
2084
|
let results = [];
|
|
2282
2085
|
for (let i = 0; i < frames.length; i++) {
|
|
2283
2086
|
if (dateAlternatives.date) {
|
|
2284
2087
|
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2285
|
-
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, {});
|
|
2286
2089
|
result.frame = frames[i];
|
|
2287
2090
|
results.push(result);
|
|
2288
2091
|
}
|
|
2289
2092
|
}
|
|
2290
2093
|
else if (numberAlternatives.number) {
|
|
2291
2094
|
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2292
|
-
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, {});
|
|
2293
2096
|
result.frame = frames[i];
|
|
2294
2097
|
results.push(result);
|
|
2295
2098
|
}
|
|
2296
2099
|
}
|
|
2297
2100
|
else {
|
|
2298
|
-
const result = await this._locateElementByText(frames[i], text, "
|
|
2101
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2299
2102
|
result.frame = frames[i];
|
|
2300
2103
|
results.push(result);
|
|
2301
2104
|
}
|
|
2302
2105
|
}
|
|
2303
|
-
info.results = results;
|
|
2106
|
+
state.info.results = results;
|
|
2304
2107
|
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2305
2108
|
if (resultWithElementsFound.length === 0) {
|
|
2306
|
-
if (Date.now() - startTime > timeout) {
|
|
2109
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2307
2110
|
throw new Error(`Text ${text} not found in page`);
|
|
2308
2111
|
}
|
|
2309
2112
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -2315,44 +2118,89 @@ class StableBrowser {
|
|
|
2315
2118
|
await this._highlightElements(frame, dataAttribute);
|
|
2316
2119
|
const element = await frame.$(dataAttribute);
|
|
2317
2120
|
if (element) {
|
|
2318
|
-
await this.scrollIfNeeded(element, info);
|
|
2121
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2319
2122
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2320
2123
|
}
|
|
2321
2124
|
}
|
|
2322
|
-
|
|
2323
|
-
return info;
|
|
2125
|
+
await _screenshot(state, this);
|
|
2126
|
+
return state.info;
|
|
2324
2127
|
}
|
|
2325
2128
|
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2326
2129
|
}
|
|
2327
2130
|
catch (e) {
|
|
2328
|
-
|
|
2329
|
-
this.logger.error("verify text exist in page failed " + info.log);
|
|
2330
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2331
|
-
info.screenshotPath = screenshotPath;
|
|
2332
|
-
Object.assign(e, { info: info });
|
|
2333
|
-
error = e;
|
|
2334
|
-
throw e;
|
|
2131
|
+
await _commandError(state, e, this);
|
|
2335
2132
|
}
|
|
2336
2133
|
finally {
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
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
|
+
}
|
|
2348
2173
|
}
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
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);
|
|
2356
2204
|
}
|
|
2357
2205
|
}
|
|
2358
2206
|
_getServerUrl() {
|
|
@@ -2415,11 +2263,12 @@ class StableBrowser {
|
|
|
2415
2263
|
info.screenshotPath = screenshotPath;
|
|
2416
2264
|
Object.assign(e, { info: info });
|
|
2417
2265
|
error = e;
|
|
2418
|
-
throw e;
|
|
2266
|
+
// throw e;
|
|
2267
|
+
await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
|
|
2419
2268
|
}
|
|
2420
2269
|
finally {
|
|
2421
2270
|
const endTime = Date.now();
|
|
2422
|
-
|
|
2271
|
+
_reportToWorld(world, {
|
|
2423
2272
|
type: Types.VERIFY_VISUAL,
|
|
2424
2273
|
text: "Visual verification",
|
|
2425
2274
|
screenshotId,
|
|
@@ -2428,7 +2277,7 @@ class StableBrowser {
|
|
|
2428
2277
|
status: "FAILED",
|
|
2429
2278
|
startTime,
|
|
2430
2279
|
endTime,
|
|
2431
|
-
message: error
|
|
2280
|
+
message: error?.message,
|
|
2432
2281
|
}
|
|
2433
2282
|
: {
|
|
2434
2283
|
status: "PASSED",
|
|
@@ -2460,7 +2309,7 @@ class StableBrowser {
|
|
|
2460
2309
|
this.logger.info("Table data verified");
|
|
2461
2310
|
}
|
|
2462
2311
|
async getTableData(selectors, _params = null, options = {}, world = null) {
|
|
2463
|
-
|
|
2312
|
+
_validateSelectors(selectors);
|
|
2464
2313
|
const startTime = Date.now();
|
|
2465
2314
|
let error = null;
|
|
2466
2315
|
let screenshotId = null;
|
|
@@ -2482,11 +2331,12 @@ class StableBrowser {
|
|
|
2482
2331
|
info.screenshotPath = screenshotPath;
|
|
2483
2332
|
Object.assign(e, { info: info });
|
|
2484
2333
|
error = e;
|
|
2485
|
-
throw e;
|
|
2334
|
+
// throw e;
|
|
2335
|
+
await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
|
|
2486
2336
|
}
|
|
2487
2337
|
finally {
|
|
2488
2338
|
const endTime = Date.now();
|
|
2489
|
-
|
|
2339
|
+
_reportToWorld(world, {
|
|
2490
2340
|
element_name: selectors.element_name,
|
|
2491
2341
|
type: Types.GET_TABLE_DATA,
|
|
2492
2342
|
text: "Get table data",
|
|
@@ -2496,7 +2346,7 @@ class StableBrowser {
|
|
|
2496
2346
|
status: "FAILED",
|
|
2497
2347
|
startTime,
|
|
2498
2348
|
endTime,
|
|
2499
|
-
message: error
|
|
2349
|
+
message: error?.message,
|
|
2500
2350
|
}
|
|
2501
2351
|
: {
|
|
2502
2352
|
status: "PASSED",
|
|
@@ -2508,7 +2358,7 @@ class StableBrowser {
|
|
|
2508
2358
|
}
|
|
2509
2359
|
}
|
|
2510
2360
|
async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
|
|
2511
|
-
|
|
2361
|
+
_validateSelectors(selectors);
|
|
2512
2362
|
if (!query) {
|
|
2513
2363
|
throw new Error("query is null");
|
|
2514
2364
|
}
|
|
@@ -2647,11 +2497,12 @@ class StableBrowser {
|
|
|
2647
2497
|
info.screenshotPath = screenshotPath;
|
|
2648
2498
|
Object.assign(e, { info: info });
|
|
2649
2499
|
error = e;
|
|
2650
|
-
throw e;
|
|
2500
|
+
// throw e;
|
|
2501
|
+
await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
|
|
2651
2502
|
}
|
|
2652
2503
|
finally {
|
|
2653
2504
|
const endTime = Date.now();
|
|
2654
|
-
|
|
2505
|
+
_reportToWorld(world, {
|
|
2655
2506
|
element_name: selectors.element_name,
|
|
2656
2507
|
type: Types.ANALYZE_TABLE,
|
|
2657
2508
|
text: "Analyze table",
|
|
@@ -2661,7 +2512,7 @@ class StableBrowser {
|
|
|
2661
2512
|
status: "FAILED",
|
|
2662
2513
|
startTime,
|
|
2663
2514
|
endTime,
|
|
2664
|
-
message: error
|
|
2515
|
+
message: error?.message,
|
|
2665
2516
|
}
|
|
2666
2517
|
: {
|
|
2667
2518
|
status: "PASSED",
|
|
@@ -2673,27 +2524,7 @@ class StableBrowser {
|
|
|
2673
2524
|
}
|
|
2674
2525
|
}
|
|
2675
2526
|
async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
|
|
2676
|
-
|
|
2677
|
-
return value;
|
|
2678
|
-
}
|
|
2679
|
-
// find all the accurance of {{(.*?)}} and replace with the value
|
|
2680
|
-
let regex = /{{(.*?)}}/g;
|
|
2681
|
-
let matches = value.match(regex);
|
|
2682
|
-
if (matches) {
|
|
2683
|
-
const testData = this.getTestData(world);
|
|
2684
|
-
for (let i = 0; i < matches.length; i++) {
|
|
2685
|
-
let match = matches[i];
|
|
2686
|
-
let key = match.substring(2, match.length - 2);
|
|
2687
|
-
let newValue = objectPath.get(testData, key, null);
|
|
2688
|
-
if (newValue !== null) {
|
|
2689
|
-
value = value.replace(match, newValue);
|
|
2690
|
-
}
|
|
2691
|
-
}
|
|
2692
|
-
}
|
|
2693
|
-
if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
|
|
2694
|
-
return await decrypt(value, null, totpWait);
|
|
2695
|
-
}
|
|
2696
|
-
return value;
|
|
2527
|
+
return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
|
|
2697
2528
|
}
|
|
2698
2529
|
_getLoadTimeout(options) {
|
|
2699
2530
|
let timeout = 15000;
|
|
@@ -2744,7 +2575,7 @@ class StableBrowser {
|
|
|
2744
2575
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2745
2576
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2746
2577
|
const endTime = Date.now();
|
|
2747
|
-
|
|
2578
|
+
_reportToWorld(world, {
|
|
2748
2579
|
type: Types.GET_PAGE_STATUS,
|
|
2749
2580
|
text: "Wait for page load",
|
|
2750
2581
|
screenshotId,
|
|
@@ -2753,7 +2584,7 @@ class StableBrowser {
|
|
|
2753
2584
|
status: "FAILED",
|
|
2754
2585
|
startTime,
|
|
2755
2586
|
endTime,
|
|
2756
|
-
message: error
|
|
2587
|
+
message: error?.message,
|
|
2757
2588
|
}
|
|
2758
2589
|
: {
|
|
2759
2590
|
status: "PASSED",
|
|
@@ -2764,41 +2595,35 @@ class StableBrowser {
|
|
|
2764
2595
|
}
|
|
2765
2596
|
}
|
|
2766
2597
|
async closePage(options = {}, world = null) {
|
|
2767
|
-
const
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
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
|
+
};
|
|
2772
2610
|
try {
|
|
2611
|
+
await _preCommand(state, this);
|
|
2773
2612
|
await this.page.close();
|
|
2774
2613
|
}
|
|
2775
2614
|
catch (e) {
|
|
2776
2615
|
console.log(".");
|
|
2616
|
+
await _commandError(state, e, this);
|
|
2777
2617
|
}
|
|
2778
2618
|
finally {
|
|
2779
|
-
|
|
2780
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2781
|
-
const endTime = Date.now();
|
|
2782
|
-
this._reportToWorld(world, {
|
|
2783
|
-
type: Types.CLOSE_PAGE,
|
|
2784
|
-
text: "close page",
|
|
2785
|
-
screenshotId,
|
|
2786
|
-
result: error
|
|
2787
|
-
? {
|
|
2788
|
-
status: "FAILED",
|
|
2789
|
-
startTime,
|
|
2790
|
-
endTime,
|
|
2791
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2792
|
-
}
|
|
2793
|
-
: {
|
|
2794
|
-
status: "PASSED",
|
|
2795
|
-
startTime,
|
|
2796
|
-
endTime,
|
|
2797
|
-
},
|
|
2798
|
-
info: info,
|
|
2799
|
-
});
|
|
2619
|
+
_commandFinally(state, this);
|
|
2800
2620
|
}
|
|
2801
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
|
+
}
|
|
2802
2627
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
2803
2628
|
const startTime = Date.now();
|
|
2804
2629
|
let error = null;
|
|
@@ -2816,12 +2641,13 @@ class StableBrowser {
|
|
|
2816
2641
|
}
|
|
2817
2642
|
catch (e) {
|
|
2818
2643
|
console.log(".");
|
|
2644
|
+
await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
|
|
2819
2645
|
}
|
|
2820
2646
|
finally {
|
|
2821
2647
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2822
2648
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2823
2649
|
const endTime = Date.now();
|
|
2824
|
-
|
|
2650
|
+
_reportToWorld(world, {
|
|
2825
2651
|
type: Types.SET_VIEWPORT,
|
|
2826
2652
|
text: "set viewport size to " + width + "x" + hight,
|
|
2827
2653
|
screenshotId,
|
|
@@ -2830,7 +2656,7 @@ class StableBrowser {
|
|
|
2830
2656
|
status: "FAILED",
|
|
2831
2657
|
startTime,
|
|
2832
2658
|
endTime,
|
|
2833
|
-
message: error
|
|
2659
|
+
message: error?.message,
|
|
2834
2660
|
}
|
|
2835
2661
|
: {
|
|
2836
2662
|
status: "PASSED",
|
|
@@ -2852,12 +2678,13 @@ class StableBrowser {
|
|
|
2852
2678
|
}
|
|
2853
2679
|
catch (e) {
|
|
2854
2680
|
console.log(".");
|
|
2681
|
+
await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
|
|
2855
2682
|
}
|
|
2856
2683
|
finally {
|
|
2857
2684
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2858
2685
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2859
2686
|
const endTime = Date.now();
|
|
2860
|
-
|
|
2687
|
+
_reportToWorld(world, {
|
|
2861
2688
|
type: Types.GET_PAGE_STATUS,
|
|
2862
2689
|
text: "page relaod",
|
|
2863
2690
|
screenshotId,
|
|
@@ -2866,7 +2693,7 @@ class StableBrowser {
|
|
|
2866
2693
|
status: "FAILED",
|
|
2867
2694
|
startTime,
|
|
2868
2695
|
endTime,
|
|
2869
|
-
message: error
|
|
2696
|
+
message: error?.message,
|
|
2870
2697
|
}
|
|
2871
2698
|
: {
|
|
2872
2699
|
status: "PASSED",
|
|
@@ -2893,11 +2720,37 @@ class StableBrowser {
|
|
|
2893
2720
|
console.log("#-#");
|
|
2894
2721
|
}
|
|
2895
2722
|
}
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
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
|
+
}
|
|
2899
2753
|
}
|
|
2900
|
-
world.attach(JSON.stringify(properties), { mediaType: "application/json" });
|
|
2901
2754
|
}
|
|
2902
2755
|
}
|
|
2903
2756
|
function createTimedPromise(promise, label) {
|