automation_model 1.0.470-dev → 1.0.470-stage
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api.d.ts +42 -1
- package/lib/api.js +221 -47
- package/lib/api.js.map +1 -1
- package/lib/auto_page.js +38 -16
- package/lib/auto_page.js.map +1 -1
- package/lib/browser_manager.d.ts +5 -3
- package/lib/browser_manager.js +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/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 +25 -19
- package/lib/stable_browser.js +612 -792
- 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 +3 -4
package/lib/stable_browser.js
CHANGED
|
@@ -10,13 +10,15 @@ import { getDateTimeValue } from "./date_time.js";
|
|
|
10
10
|
import drawRectangle from "./drawRect.js";
|
|
11
11
|
//import { closeUnexpectedPopups } from "./popups.js";
|
|
12
12
|
import { getTableCells, getTableData } from "./table_analyze.js";
|
|
13
|
-
import
|
|
14
|
-
import { decrypt } from "./utils.js";
|
|
13
|
+
import { maskValue, replaceWithLocalTestData } from "./utils.js";
|
|
15
14
|
import csv from "csv-parser";
|
|
16
15
|
import { Readable } from "node:stream";
|
|
17
16
|
import readline from "readline";
|
|
18
17
|
import { getContext } from "./init_browser.js";
|
|
19
18
|
import { locate_element } from "./locate_element.js";
|
|
19
|
+
import { randomUUID } from "crypto";
|
|
20
|
+
import { _commandError, _commandFinally, _preCommand, _validateSelectors, _screenshot, _reportToWorld, } from "./command_common.js";
|
|
21
|
+
import { registerDownloadEvent, registerNetworkEvents } from "./network.js";
|
|
20
22
|
const Types = {
|
|
21
23
|
CLICK: "click_element",
|
|
22
24
|
NAVIGATE: "navigate",
|
|
@@ -43,20 +45,27 @@ const Types = {
|
|
|
43
45
|
VERIFY_VISUAL: "verify_visual",
|
|
44
46
|
LOAD_DATA: "load_data",
|
|
45
47
|
SET_INPUT: "set_input",
|
|
48
|
+
WAIT_FOR_TEXT_TO_DISAPPEAR: "wait_for_text_to_disappear",
|
|
46
49
|
};
|
|
47
50
|
export const apps = {};
|
|
48
51
|
class StableBrowser {
|
|
52
|
+
browser;
|
|
53
|
+
page;
|
|
54
|
+
logger;
|
|
55
|
+
context;
|
|
56
|
+
world;
|
|
57
|
+
project_path = null;
|
|
58
|
+
webLogFile = null;
|
|
59
|
+
networkLogger = null;
|
|
60
|
+
configuration = null;
|
|
61
|
+
appName = "main";
|
|
62
|
+
tags = null;
|
|
49
63
|
constructor(browser, page, logger = null, context = null, world = null) {
|
|
50
64
|
this.browser = browser;
|
|
51
65
|
this.page = page;
|
|
52
66
|
this.logger = logger;
|
|
53
67
|
this.context = context;
|
|
54
68
|
this.world = world;
|
|
55
|
-
this.project_path = null;
|
|
56
|
-
this.webLogFile = null;
|
|
57
|
-
this.networkLogger = null;
|
|
58
|
-
this.configuration = null;
|
|
59
|
-
this.appName = "main";
|
|
60
69
|
if (!this.logger) {
|
|
61
70
|
this.logger = console;
|
|
62
71
|
}
|
|
@@ -81,11 +90,31 @@ class StableBrowser {
|
|
|
81
90
|
catch (e) {
|
|
82
91
|
this.logger.error("unable to read ai_config.json");
|
|
83
92
|
}
|
|
84
|
-
context.pageLoading = { status: false };
|
|
85
|
-
context.pages = [this.page];
|
|
86
93
|
const logFolder = path.join(this.project_path, "logs", "web");
|
|
87
94
|
this.world = world;
|
|
95
|
+
context.pages = [this.page];
|
|
96
|
+
context.pageLoading = { status: false };
|
|
88
97
|
this.registerEventListeners(this.context);
|
|
98
|
+
registerNetworkEvents(this.world, this, this.context, this.page);
|
|
99
|
+
registerDownloadEvent(this.page, this.world, this.context);
|
|
100
|
+
}
|
|
101
|
+
async scrollPageToLoadLazyElements() {
|
|
102
|
+
let lastHeight = await this.page.evaluate(() => document.body.scrollHeight);
|
|
103
|
+
let retry = 0;
|
|
104
|
+
while (true) {
|
|
105
|
+
await this.page.evaluate(() => window.scrollBy(0, window.innerHeight));
|
|
106
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
107
|
+
let newHeight = await this.page.evaluate(() => document.body.scrollHeight);
|
|
108
|
+
if (newHeight === lastHeight) {
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
lastHeight = newHeight;
|
|
112
|
+
retry++;
|
|
113
|
+
if (retry > 10) {
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
await this.page.evaluate(() => window.scrollTo(0, 0));
|
|
89
118
|
}
|
|
90
119
|
registerEventListeners(context) {
|
|
91
120
|
this.registerConsoleLogListener(this.page, context);
|
|
@@ -94,10 +123,17 @@ class StableBrowser {
|
|
|
94
123
|
context.pageLoading = { status: false };
|
|
95
124
|
}
|
|
96
125
|
context.playContext.on("page", async function (page) {
|
|
126
|
+
if (this.configuration && this.configuration.closePopups === true) {
|
|
127
|
+
console.log("close unexpected popups");
|
|
128
|
+
await page.close();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
97
131
|
context.pageLoading.status = true;
|
|
98
132
|
this.page = page;
|
|
99
133
|
context.page = page;
|
|
100
134
|
context.pages.push(page);
|
|
135
|
+
registerNetworkEvents(this.world, this, context, this.page);
|
|
136
|
+
registerDownloadEvent(this.page, this.world, context);
|
|
101
137
|
page.on("close", async () => {
|
|
102
138
|
if (this.context && this.context.pages && this.context.pages.length > 1) {
|
|
103
139
|
this.context.pages.pop();
|
|
@@ -127,9 +163,9 @@ class StableBrowser {
|
|
|
127
163
|
if (this.appName === appName) {
|
|
128
164
|
return;
|
|
129
165
|
}
|
|
130
|
-
let
|
|
166
|
+
let navigate = false;
|
|
131
167
|
if (!apps[appName]) {
|
|
132
|
-
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this);
|
|
168
|
+
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this, -1, this.context.reportFolder);
|
|
133
169
|
newContextCreated = true;
|
|
134
170
|
apps[appName] = {
|
|
135
171
|
context: newContext,
|
|
@@ -142,8 +178,7 @@ class StableBrowser {
|
|
|
142
178
|
this._copyContext(apps[appName], this);
|
|
143
179
|
apps[this.appName] = tempContext;
|
|
144
180
|
this.appName = appName;
|
|
145
|
-
if (
|
|
146
|
-
this.registerEventListeners(this.context);
|
|
181
|
+
if (navigate) {
|
|
147
182
|
await this.goto(this.context.environment.baseUrl);
|
|
148
183
|
await this.waitForPageLoad();
|
|
149
184
|
}
|
|
@@ -169,7 +204,6 @@ class StableBrowser {
|
|
|
169
204
|
this.context.webLogger = [];
|
|
170
205
|
}
|
|
171
206
|
page.on("console", async (msg) => {
|
|
172
|
-
var _a;
|
|
173
207
|
const obj = {
|
|
174
208
|
type: msg.type(),
|
|
175
209
|
text: msg.text(),
|
|
@@ -178,7 +212,7 @@ class StableBrowser {
|
|
|
178
212
|
};
|
|
179
213
|
this.context.webLogger.push(obj);
|
|
180
214
|
if (msg.type() === "error") {
|
|
181
|
-
|
|
215
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
|
|
182
216
|
}
|
|
183
217
|
});
|
|
184
218
|
}
|
|
@@ -187,7 +221,6 @@ class StableBrowser {
|
|
|
187
221
|
this.context.networkLogger = [];
|
|
188
222
|
}
|
|
189
223
|
page.on("request", async (data) => {
|
|
190
|
-
var _a;
|
|
191
224
|
const startTime = new Date().getTime();
|
|
192
225
|
try {
|
|
193
226
|
const pageUrl = new URL(page.url());
|
|
@@ -212,10 +245,10 @@ class StableBrowser {
|
|
|
212
245
|
startTime,
|
|
213
246
|
};
|
|
214
247
|
context.networkLogger.push(obj);
|
|
215
|
-
|
|
248
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
|
|
216
249
|
}
|
|
217
250
|
catch (error) {
|
|
218
|
-
console.error("Error in request listener", error);
|
|
251
|
+
// console.error("Error in request listener", error);
|
|
219
252
|
context.networkLogger.push({
|
|
220
253
|
error: "not able to listen",
|
|
221
254
|
message: error.message,
|
|
@@ -237,20 +270,6 @@ class StableBrowser {
|
|
|
237
270
|
timeout: 60000,
|
|
238
271
|
});
|
|
239
272
|
}
|
|
240
|
-
_validateSelectors(selectors) {
|
|
241
|
-
if (!selectors) {
|
|
242
|
-
throw new Error("selectors is null");
|
|
243
|
-
}
|
|
244
|
-
if (!selectors.locators) {
|
|
245
|
-
throw new Error("selectors.locators is null");
|
|
246
|
-
}
|
|
247
|
-
if (!Array.isArray(selectors.locators)) {
|
|
248
|
-
throw new Error("selectors.locators expected to be array");
|
|
249
|
-
}
|
|
250
|
-
if (selectors.locators.length === 0) {
|
|
251
|
-
throw new Error("selectors.locators expected to be non empty array");
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
273
|
_fixUsingParams(text, _params) {
|
|
255
274
|
if (!_params || typeof text !== "string") {
|
|
256
275
|
return text;
|
|
@@ -318,7 +337,7 @@ class StableBrowser {
|
|
|
318
337
|
locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
|
|
319
338
|
}
|
|
320
339
|
}
|
|
321
|
-
if (locator
|
|
340
|
+
if (locator?.engine) {
|
|
322
341
|
if (locator.engine === "css") {
|
|
323
342
|
locatorReturn = scope.locator(locator.selector);
|
|
324
343
|
}
|
|
@@ -342,7 +361,7 @@ class StableBrowser {
|
|
|
342
361
|
if (css && css.locator) {
|
|
343
362
|
css = css.locator;
|
|
344
363
|
}
|
|
345
|
-
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "
|
|
364
|
+
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*:not(script, style, head)", false, false, _params);
|
|
346
365
|
if (result.elementCount === 0) {
|
|
347
366
|
return;
|
|
348
367
|
}
|
|
@@ -357,7 +376,7 @@ class StableBrowser {
|
|
|
357
376
|
}
|
|
358
377
|
async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
|
|
359
378
|
//const stringifyText = JSON.stringify(text);
|
|
360
|
-
return await scope.evaluate(([text, tag, regex, partial]) => {
|
|
379
|
+
return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
|
|
361
380
|
function isParent(parent, child) {
|
|
362
381
|
let currentNode = child.parentNode;
|
|
363
382
|
while (currentNode !== null) {
|
|
@@ -394,7 +413,7 @@ class StableBrowser {
|
|
|
394
413
|
}
|
|
395
414
|
document.collectAllShadowDomElements = collectAllShadowDomElements;
|
|
396
415
|
if (!tag) {
|
|
397
|
-
tag = "
|
|
416
|
+
tag = "*:not(script, style, head)";
|
|
398
417
|
}
|
|
399
418
|
let regexpSearch = document.getRegex(text);
|
|
400
419
|
if (regexpSearch) {
|
|
@@ -476,6 +495,15 @@ class StableBrowser {
|
|
|
476
495
|
}, [text1, tag1, regex1, partial1]);
|
|
477
496
|
}
|
|
478
497
|
async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
|
|
498
|
+
if (!info) {
|
|
499
|
+
info = {};
|
|
500
|
+
}
|
|
501
|
+
if (!info.failCause) {
|
|
502
|
+
info.failCause = {};
|
|
503
|
+
}
|
|
504
|
+
if (!info.log) {
|
|
505
|
+
info.log = "";
|
|
506
|
+
}
|
|
479
507
|
let locatorSearch = selectorHierarchy[index];
|
|
480
508
|
try {
|
|
481
509
|
locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
|
|
@@ -488,13 +516,18 @@ class StableBrowser {
|
|
|
488
516
|
if (locatorSearch.climb && locatorSearch.climb >= 0) {
|
|
489
517
|
let locatorString = await this._locateElmentByTextClimbCss(scope, locatorSearch.text, locatorSearch.climb, locatorSearch.css, _params);
|
|
490
518
|
if (!locatorString) {
|
|
519
|
+
info.failCause.textNotFound = true;
|
|
520
|
+
info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
|
|
491
521
|
return;
|
|
492
522
|
}
|
|
493
523
|
locator = this._getLocator({ css: locatorString }, scope, _params);
|
|
494
524
|
}
|
|
495
525
|
else if (locatorSearch.text) {
|
|
496
|
-
let
|
|
526
|
+
let text = this._fixUsingParams(locatorSearch.text, _params);
|
|
527
|
+
let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
|
|
497
528
|
if (result.elementCount === 0) {
|
|
529
|
+
info.failCause.textNotFound = true;
|
|
530
|
+
info.failCause.lastError = "failed to locate element by text: " + text;
|
|
498
531
|
return;
|
|
499
532
|
}
|
|
500
533
|
locatorSearch.css = "[data-blinq-id='blinq-id-" + result.randomToken + "']";
|
|
@@ -511,6 +544,9 @@ class StableBrowser {
|
|
|
511
544
|
// cssHref = true;
|
|
512
545
|
// }
|
|
513
546
|
let count = await locator.count();
|
|
547
|
+
if (count > 0 && !info.failCause.count) {
|
|
548
|
+
info.failCause.count = count;
|
|
549
|
+
}
|
|
514
550
|
//info.log += "total elements found " + count + "\n";
|
|
515
551
|
//let visibleCount = 0;
|
|
516
552
|
let visibleLocator = null;
|
|
@@ -528,6 +564,8 @@ class StableBrowser {
|
|
|
528
564
|
foundLocators.push(locator.nth(j));
|
|
529
565
|
}
|
|
530
566
|
else {
|
|
567
|
+
info.failCause.visible = visible;
|
|
568
|
+
info.failCause.enabled = enabled;
|
|
531
569
|
if (!info.printMessages) {
|
|
532
570
|
info.printMessages = {};
|
|
533
571
|
}
|
|
@@ -539,6 +577,11 @@ class StableBrowser {
|
|
|
539
577
|
}
|
|
540
578
|
}
|
|
541
579
|
async closeUnexpectedPopups(info, _params) {
|
|
580
|
+
if (!info) {
|
|
581
|
+
info = {};
|
|
582
|
+
info.failCause = {};
|
|
583
|
+
info.log = "";
|
|
584
|
+
}
|
|
542
585
|
if (this.configuration.popupHandlers && this.configuration.popupHandlers.length > 0) {
|
|
543
586
|
if (!info) {
|
|
544
587
|
info = {};
|
|
@@ -570,16 +613,31 @@ class StableBrowser {
|
|
|
570
613
|
}
|
|
571
614
|
if (result.foundElements.length > 0) {
|
|
572
615
|
let dialogCloseLocator = result.foundElements[0].locator;
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
616
|
+
try {
|
|
617
|
+
await scope?.evaluate(() => {
|
|
618
|
+
window.__isClosingPopups = true;
|
|
619
|
+
});
|
|
620
|
+
await dialogCloseLocator.click();
|
|
621
|
+
// wait for the dialog to close
|
|
622
|
+
await dialogCloseLocator.waitFor({ state: "hidden" });
|
|
623
|
+
}
|
|
624
|
+
catch (e) {
|
|
625
|
+
}
|
|
626
|
+
finally {
|
|
627
|
+
await scope?.evaluate(() => {
|
|
628
|
+
window.__isClosingPopups = false;
|
|
629
|
+
});
|
|
630
|
+
}
|
|
576
631
|
return { rerun: true };
|
|
577
632
|
}
|
|
578
633
|
}
|
|
579
634
|
}
|
|
580
635
|
return { rerun: false };
|
|
581
636
|
}
|
|
582
|
-
async _locate(selectors, info, _params, timeout
|
|
637
|
+
async _locate(selectors, info, _params, timeout) {
|
|
638
|
+
if (!timeout) {
|
|
639
|
+
timeout = 30000;
|
|
640
|
+
}
|
|
583
641
|
for (let i = 0; i < 3; i++) {
|
|
584
642
|
info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
|
|
585
643
|
for (let j = 0; j < selectors.locators.length; j++) {
|
|
@@ -593,16 +651,15 @@ class StableBrowser {
|
|
|
593
651
|
}
|
|
594
652
|
throw new Error("unable to locate element " + JSON.stringify(selectors));
|
|
595
653
|
}
|
|
596
|
-
async
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
654
|
+
async _findFrameScope(selectors, timeout = 30000, info) {
|
|
655
|
+
if (!info) {
|
|
656
|
+
info = {};
|
|
657
|
+
info.failCause = {};
|
|
658
|
+
info.log = "";
|
|
659
|
+
}
|
|
602
660
|
let scope = this.page;
|
|
603
|
-
// for the simple click usecase
|
|
604
661
|
if (selectors.frame) {
|
|
605
|
-
|
|
662
|
+
return selectors.frame;
|
|
606
663
|
}
|
|
607
664
|
if (selectors.iframe_src || selectors.frameLocators) {
|
|
608
665
|
const findFrame = async (frame, framescope) => {
|
|
@@ -630,7 +687,6 @@ class StableBrowser {
|
|
|
630
687
|
}
|
|
631
688
|
return framescope;
|
|
632
689
|
};
|
|
633
|
-
info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
|
|
634
690
|
while (true) {
|
|
635
691
|
let frameFound = false;
|
|
636
692
|
if (selectors.nestFrmLoc) {
|
|
@@ -654,6 +710,8 @@ class StableBrowser {
|
|
|
654
710
|
if (!scope) {
|
|
655
711
|
info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
|
|
656
712
|
if (performance.now() - startTime > timeout) {
|
|
713
|
+
info.failCause.iframeNotFound = true;
|
|
714
|
+
info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
|
|
657
715
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
658
716
|
}
|
|
659
717
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -663,6 +721,31 @@ class StableBrowser {
|
|
|
663
721
|
}
|
|
664
722
|
}
|
|
665
723
|
}
|
|
724
|
+
if (!scope) {
|
|
725
|
+
scope = this.page;
|
|
726
|
+
}
|
|
727
|
+
return scope;
|
|
728
|
+
}
|
|
729
|
+
async _getDocumentBody(selectors, timeout = 30000, info) {
|
|
730
|
+
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
731
|
+
return scope.evaluate(() => {
|
|
732
|
+
var bodyContent = document.body.innerHTML;
|
|
733
|
+
return bodyContent;
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
737
|
+
if (!info) {
|
|
738
|
+
info = {};
|
|
739
|
+
info.failCause = {};
|
|
740
|
+
info.log = "";
|
|
741
|
+
}
|
|
742
|
+
let highPriorityTimeout = 5000;
|
|
743
|
+
let visibleOnlyTimeout = 6000;
|
|
744
|
+
let startTime = performance.now();
|
|
745
|
+
let locatorsCount = 0;
|
|
746
|
+
let lazy_scroll = false;
|
|
747
|
+
//let arrayMode = Array.isArray(selectors);
|
|
748
|
+
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
666
749
|
let selectorsLocators = null;
|
|
667
750
|
selectorsLocators = selectors.locators;
|
|
668
751
|
// group selectors by priority
|
|
@@ -755,6 +838,10 @@ class StableBrowser {
|
|
|
755
838
|
if (performance.now() - startTime > highPriorityTimeout) {
|
|
756
839
|
info.log += "high priority timeout, will try all elements" + "\n";
|
|
757
840
|
highPriorityOnly = false;
|
|
841
|
+
if (this.configuration && this.configuration.load_all_lazy === true && !lazy_scroll) {
|
|
842
|
+
lazy_scroll = true;
|
|
843
|
+
await this.scrollPageToLoadLazyElements();
|
|
844
|
+
}
|
|
758
845
|
}
|
|
759
846
|
if (performance.now() - startTime > visibleOnlyTimeout) {
|
|
760
847
|
info.log += "visible only timeout, will try all elements" + "\n";
|
|
@@ -764,6 +851,8 @@ class StableBrowser {
|
|
|
764
851
|
}
|
|
765
852
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
766
853
|
info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
|
|
854
|
+
info.failCause.locatorNotFound = true;
|
|
855
|
+
info.failCause.lastError = "failed to locate unique element";
|
|
767
856
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
768
857
|
}
|
|
769
858
|
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
@@ -795,6 +884,9 @@ class StableBrowser {
|
|
|
795
884
|
});
|
|
796
885
|
result.locatorIndex = i;
|
|
797
886
|
}
|
|
887
|
+
if (foundLocators.length > 1) {
|
|
888
|
+
info.failCause.foundMultiple = true;
|
|
889
|
+
}
|
|
798
890
|
}
|
|
799
891
|
return result;
|
|
800
892
|
}
|
|
@@ -807,12 +899,12 @@ class StableBrowser {
|
|
|
807
899
|
while (true) {
|
|
808
900
|
try {
|
|
809
901
|
const result = await locate_element(this.context, elementDescription, "click");
|
|
810
|
-
if (
|
|
902
|
+
if (result?.elementNumber >= 0) {
|
|
811
903
|
const selectors = {
|
|
812
|
-
frame: result
|
|
904
|
+
frame: result?.frame,
|
|
813
905
|
locators: [
|
|
814
906
|
{
|
|
815
|
-
css: result
|
|
907
|
+
css: result?.css,
|
|
816
908
|
},
|
|
817
909
|
],
|
|
818
910
|
};
|
|
@@ -822,7 +914,8 @@ class StableBrowser {
|
|
|
822
914
|
}
|
|
823
915
|
catch (e) {
|
|
824
916
|
if (performance.now() - startTime > timeout) {
|
|
825
|
-
throw e;
|
|
917
|
+
// throw e;
|
|
918
|
+
await _commandError({ text: "simpleClick", operation: "simpleClick", elementDescription, info: {} }, e, this);
|
|
826
919
|
}
|
|
827
920
|
}
|
|
828
921
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
@@ -837,12 +930,12 @@ class StableBrowser {
|
|
|
837
930
|
while (true) {
|
|
838
931
|
try {
|
|
839
932
|
const result = await locate_element(this.context, elementDescription, "fill", value);
|
|
840
|
-
if (
|
|
933
|
+
if (result?.elementNumber >= 0) {
|
|
841
934
|
const selectors = {
|
|
842
|
-
frame: result
|
|
935
|
+
frame: result?.frame,
|
|
843
936
|
locators: [
|
|
844
937
|
{
|
|
845
|
-
css: result
|
|
938
|
+
css: result?.css,
|
|
846
939
|
},
|
|
847
940
|
],
|
|
848
941
|
};
|
|
@@ -852,92 +945,68 @@ class StableBrowser {
|
|
|
852
945
|
}
|
|
853
946
|
catch (e) {
|
|
854
947
|
if (performance.now() - startTime > timeout) {
|
|
855
|
-
throw e;
|
|
948
|
+
// throw e;
|
|
949
|
+
await _commandError({ text: "simpleClickType", operation: "simpleClickType", value, elementDescription, info: {} }, e, this);
|
|
856
950
|
}
|
|
857
951
|
}
|
|
858
952
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
859
953
|
}
|
|
860
954
|
}
|
|
861
955
|
async click(selectors, _params, options = {}, world = null) {
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
let screenshotId = null;
|
|
873
|
-
let screenshotPath = null;
|
|
956
|
+
const state = {
|
|
957
|
+
selectors,
|
|
958
|
+
_params,
|
|
959
|
+
options,
|
|
960
|
+
world,
|
|
961
|
+
text: "Click element",
|
|
962
|
+
type: Types.CLICK,
|
|
963
|
+
operation: "click",
|
|
964
|
+
log: "***** click on " + selectors.element_name + " *****\n",
|
|
965
|
+
};
|
|
874
966
|
try {
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
967
|
+
await _preCommand(state, this);
|
|
968
|
+
if (state.options && state.options.context) {
|
|
969
|
+
state.selectors.locators[0].text = state.options.context;
|
|
970
|
+
}
|
|
878
971
|
try {
|
|
879
|
-
await
|
|
880
|
-
await
|
|
881
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
972
|
+
await state.element.click();
|
|
973
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
882
974
|
}
|
|
883
975
|
catch (e) {
|
|
884
976
|
// await this.closeUnexpectedPopups();
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
await
|
|
888
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
977
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
978
|
+
await state.element.dispatchEvent("click");
|
|
979
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
889
980
|
}
|
|
890
981
|
await this.waitForPageLoad();
|
|
891
|
-
return info;
|
|
982
|
+
return state.info;
|
|
892
983
|
}
|
|
893
984
|
catch (e) {
|
|
894
|
-
|
|
895
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
896
|
-
info.screenshotPath = screenshotPath;
|
|
897
|
-
Object.assign(e, { info: info });
|
|
898
|
-
error = e;
|
|
899
|
-
throw e;
|
|
985
|
+
await _commandError(state, e, this);
|
|
900
986
|
}
|
|
901
987
|
finally {
|
|
902
|
-
|
|
903
|
-
this._reportToWorld(world, {
|
|
904
|
-
element_name: selectors.element_name,
|
|
905
|
-
type: Types.CLICK,
|
|
906
|
-
text: `Click element`,
|
|
907
|
-
screenshotId,
|
|
908
|
-
result: error
|
|
909
|
-
? {
|
|
910
|
-
status: "FAILED",
|
|
911
|
-
startTime,
|
|
912
|
-
endTime,
|
|
913
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
914
|
-
}
|
|
915
|
-
: {
|
|
916
|
-
status: "PASSED",
|
|
917
|
-
startTime,
|
|
918
|
-
endTime,
|
|
919
|
-
},
|
|
920
|
-
info: info,
|
|
921
|
-
});
|
|
988
|
+
_commandFinally(state, this);
|
|
922
989
|
}
|
|
923
990
|
}
|
|
924
991
|
async setCheck(selectors, checked = true, _params, options = {}, world = null) {
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
992
|
+
const state = {
|
|
993
|
+
selectors,
|
|
994
|
+
_params,
|
|
995
|
+
options,
|
|
996
|
+
world,
|
|
997
|
+
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
998
|
+
text: checked ? `Check element` : `Uncheck element`,
|
|
999
|
+
operation: "setCheck",
|
|
1000
|
+
log: "***** check " + selectors.element_name + " *****\n",
|
|
1001
|
+
};
|
|
935
1002
|
try {
|
|
936
|
-
|
|
937
|
-
|
|
1003
|
+
await _preCommand(state, this);
|
|
1004
|
+
state.info.checked = checked;
|
|
1005
|
+
// let element = await this._locate(selectors, info, _params);
|
|
1006
|
+
// ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
938
1007
|
try {
|
|
939
|
-
await this._highlightElements(element);
|
|
940
|
-
await element.setChecked(checked);
|
|
1008
|
+
// await this._highlightElements(element);
|
|
1009
|
+
await state.element.setChecked(checked);
|
|
941
1010
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
942
1011
|
}
|
|
943
1012
|
catch (e) {
|
|
@@ -946,179 +1015,108 @@ class StableBrowser {
|
|
|
946
1015
|
}
|
|
947
1016
|
else {
|
|
948
1017
|
//await this.closeUnexpectedPopups();
|
|
949
|
-
info.log += "setCheck failed, will try again" + "\n";
|
|
950
|
-
element = await this._locate(selectors, info, _params);
|
|
951
|
-
await element.setChecked(checked, { timeout: 5000, force: true });
|
|
1018
|
+
state.info.log += "setCheck failed, will try again" + "\n";
|
|
1019
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
1020
|
+
await state.element.setChecked(checked, { timeout: 5000, force: true });
|
|
952
1021
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
953
1022
|
}
|
|
954
1023
|
}
|
|
955
1024
|
await this.waitForPageLoad();
|
|
956
|
-
return info;
|
|
1025
|
+
return state.info;
|
|
957
1026
|
}
|
|
958
1027
|
catch (e) {
|
|
959
|
-
|
|
960
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
961
|
-
info.screenshotPath = screenshotPath;
|
|
962
|
-
Object.assign(e, { info: info });
|
|
963
|
-
error = e;
|
|
964
|
-
throw e;
|
|
1028
|
+
await _commandError(state, e, this);
|
|
965
1029
|
}
|
|
966
1030
|
finally {
|
|
967
|
-
|
|
968
|
-
this._reportToWorld(world, {
|
|
969
|
-
element_name: selectors.element_name,
|
|
970
|
-
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
971
|
-
text: checked ? `Check element` : `Uncheck element`,
|
|
972
|
-
screenshotId,
|
|
973
|
-
result: error
|
|
974
|
-
? {
|
|
975
|
-
status: "FAILED",
|
|
976
|
-
startTime,
|
|
977
|
-
endTime,
|
|
978
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
979
|
-
}
|
|
980
|
-
: {
|
|
981
|
-
status: "PASSED",
|
|
982
|
-
startTime,
|
|
983
|
-
endTime,
|
|
984
|
-
},
|
|
985
|
-
info: info,
|
|
986
|
-
});
|
|
1031
|
+
_commandFinally(state, this);
|
|
987
1032
|
}
|
|
988
1033
|
}
|
|
989
1034
|
async hover(selectors, _params, options = {}, world = null) {
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1035
|
+
const state = {
|
|
1036
|
+
selectors,
|
|
1037
|
+
_params,
|
|
1038
|
+
options,
|
|
1039
|
+
world,
|
|
1040
|
+
type: Types.HOVER,
|
|
1041
|
+
text: `Hover element`,
|
|
1042
|
+
operation: "hover",
|
|
1043
|
+
log: "***** hover " + selectors.element_name + " *****\n",
|
|
1044
|
+
};
|
|
999
1045
|
try {
|
|
1000
|
-
|
|
1001
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1046
|
+
await _preCommand(state, this);
|
|
1002
1047
|
try {
|
|
1003
|
-
await
|
|
1004
|
-
await element.hover();
|
|
1048
|
+
await state.element.hover();
|
|
1005
1049
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1006
1050
|
}
|
|
1007
1051
|
catch (e) {
|
|
1008
1052
|
//await this.closeUnexpectedPopups();
|
|
1009
|
-
info.log += "hover failed, will try again" + "\n";
|
|
1010
|
-
element = await this._locate(selectors, info, _params);
|
|
1011
|
-
await element.hover({ timeout: 10000 });
|
|
1053
|
+
state.info.log += "hover failed, will try again" + "\n";
|
|
1054
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
1055
|
+
await state.element.hover({ timeout: 10000 });
|
|
1012
1056
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1013
1057
|
}
|
|
1014
1058
|
await this.waitForPageLoad();
|
|
1015
|
-
return info;
|
|
1059
|
+
return state.info;
|
|
1016
1060
|
}
|
|
1017
1061
|
catch (e) {
|
|
1018
|
-
|
|
1019
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1020
|
-
info.screenshotPath = screenshotPath;
|
|
1021
|
-
Object.assign(e, { info: info });
|
|
1022
|
-
error = e;
|
|
1023
|
-
throw e;
|
|
1062
|
+
await _commandError(state, e, this);
|
|
1024
1063
|
}
|
|
1025
1064
|
finally {
|
|
1026
|
-
|
|
1027
|
-
this._reportToWorld(world, {
|
|
1028
|
-
element_name: selectors.element_name,
|
|
1029
|
-
type: Types.HOVER,
|
|
1030
|
-
text: `Hover element`,
|
|
1031
|
-
screenshotId,
|
|
1032
|
-
result: error
|
|
1033
|
-
? {
|
|
1034
|
-
status: "FAILED",
|
|
1035
|
-
startTime,
|
|
1036
|
-
endTime,
|
|
1037
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1038
|
-
}
|
|
1039
|
-
: {
|
|
1040
|
-
status: "PASSED",
|
|
1041
|
-
startTime,
|
|
1042
|
-
endTime,
|
|
1043
|
-
},
|
|
1044
|
-
info: info,
|
|
1045
|
-
});
|
|
1065
|
+
_commandFinally(state, this);
|
|
1046
1066
|
}
|
|
1047
1067
|
}
|
|
1048
1068
|
async selectOption(selectors, values, _params = null, options = {}, world = null) {
|
|
1049
|
-
this._validateSelectors(selectors);
|
|
1050
1069
|
if (!values) {
|
|
1051
1070
|
throw new Error("values is null");
|
|
1052
1071
|
}
|
|
1053
|
-
const
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1072
|
+
const state = {
|
|
1073
|
+
selectors,
|
|
1074
|
+
_params,
|
|
1075
|
+
options,
|
|
1076
|
+
world,
|
|
1077
|
+
value: values.toString(),
|
|
1078
|
+
type: Types.SELECT,
|
|
1079
|
+
text: `Select option: ${values}`,
|
|
1080
|
+
operation: "selectOption",
|
|
1081
|
+
log: "***** select option " + selectors.element_name + " *****\n",
|
|
1082
|
+
};
|
|
1061
1083
|
try {
|
|
1062
|
-
|
|
1063
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1084
|
+
await _preCommand(state, this);
|
|
1064
1085
|
try {
|
|
1065
|
-
await
|
|
1066
|
-
await element.selectOption(values);
|
|
1086
|
+
await state.element.selectOption(values);
|
|
1067
1087
|
}
|
|
1068
1088
|
catch (e) {
|
|
1069
1089
|
//await this.closeUnexpectedPopups();
|
|
1070
|
-
info.log += "selectOption failed, will try force" + "\n";
|
|
1071
|
-
await element.selectOption(values, { timeout: 10000, force: true });
|
|
1090
|
+
state.info.log += "selectOption failed, will try force" + "\n";
|
|
1091
|
+
await state.element.selectOption(values, { timeout: 10000, force: true });
|
|
1072
1092
|
}
|
|
1073
1093
|
await this.waitForPageLoad();
|
|
1074
|
-
return info;
|
|
1094
|
+
return state.info;
|
|
1075
1095
|
}
|
|
1076
1096
|
catch (e) {
|
|
1077
|
-
|
|
1078
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1079
|
-
info.screenshotPath = screenshotPath;
|
|
1080
|
-
Object.assign(e, { info: info });
|
|
1081
|
-
this.logger.info("click failed, will try next selector");
|
|
1082
|
-
error = e;
|
|
1083
|
-
throw e;
|
|
1097
|
+
await _commandError(state, e, this);
|
|
1084
1098
|
}
|
|
1085
1099
|
finally {
|
|
1086
|
-
|
|
1087
|
-
this._reportToWorld(world, {
|
|
1088
|
-
element_name: selectors.element_name,
|
|
1089
|
-
type: Types.SELECT,
|
|
1090
|
-
text: `Select option: ${values}`,
|
|
1091
|
-
value: values.toString(),
|
|
1092
|
-
screenshotId,
|
|
1093
|
-
result: error
|
|
1094
|
-
? {
|
|
1095
|
-
status: "FAILED",
|
|
1096
|
-
startTime,
|
|
1097
|
-
endTime,
|
|
1098
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1099
|
-
}
|
|
1100
|
-
: {
|
|
1101
|
-
status: "PASSED",
|
|
1102
|
-
startTime,
|
|
1103
|
-
endTime,
|
|
1104
|
-
},
|
|
1105
|
-
info: info,
|
|
1106
|
-
});
|
|
1100
|
+
_commandFinally(state, this);
|
|
1107
1101
|
}
|
|
1108
1102
|
}
|
|
1109
1103
|
async type(_value, _params = null, options = {}, world = null) {
|
|
1110
|
-
const
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1104
|
+
const state = {
|
|
1105
|
+
value: _value,
|
|
1106
|
+
_params,
|
|
1107
|
+
options,
|
|
1108
|
+
world,
|
|
1109
|
+
locate: false,
|
|
1110
|
+
scroll: false,
|
|
1111
|
+
highlight: false,
|
|
1112
|
+
type: Types.TYPE_PRESS,
|
|
1113
|
+
text: `Type value: ${_value}`,
|
|
1114
|
+
operation: "type",
|
|
1115
|
+
log: "",
|
|
1116
|
+
};
|
|
1119
1117
|
try {
|
|
1120
|
-
|
|
1121
|
-
const valueSegment =
|
|
1118
|
+
await _preCommand(state, this);
|
|
1119
|
+
const valueSegment = state.value.split("&&");
|
|
1122
1120
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1123
1121
|
if (i > 0) {
|
|
1124
1122
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1138,134 +1136,76 @@ class StableBrowser {
|
|
|
1138
1136
|
await this.page.keyboard.type(value);
|
|
1139
1137
|
}
|
|
1140
1138
|
}
|
|
1141
|
-
return info;
|
|
1139
|
+
return state.info;
|
|
1142
1140
|
}
|
|
1143
1141
|
catch (e) {
|
|
1144
|
-
|
|
1145
|
-
this.logger.error("type failed " + info.log);
|
|
1146
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1147
|
-
info.screenshotPath = screenshotPath;
|
|
1148
|
-
Object.assign(e, { info: info });
|
|
1149
|
-
error = e;
|
|
1150
|
-
throw e;
|
|
1142
|
+
await _commandError(state, e, this);
|
|
1151
1143
|
}
|
|
1152
1144
|
finally {
|
|
1153
|
-
|
|
1154
|
-
this._reportToWorld(world, {
|
|
1155
|
-
type: Types.TYPE_PRESS,
|
|
1156
|
-
screenshotId,
|
|
1157
|
-
value: _value,
|
|
1158
|
-
text: `type value: ${_value}`,
|
|
1159
|
-
result: error
|
|
1160
|
-
? {
|
|
1161
|
-
status: "FAILED",
|
|
1162
|
-
startTime,
|
|
1163
|
-
endTime,
|
|
1164
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1165
|
-
}
|
|
1166
|
-
: {
|
|
1167
|
-
status: "PASSED",
|
|
1168
|
-
startTime,
|
|
1169
|
-
endTime,
|
|
1170
|
-
},
|
|
1171
|
-
info: info,
|
|
1172
|
-
});
|
|
1145
|
+
_commandFinally(state, this);
|
|
1173
1146
|
}
|
|
1174
1147
|
}
|
|
1175
1148
|
async setInputValue(selectors, value, _params = null, options = {}, world = null) {
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
let screenshotPath = null;
|
|
1149
|
+
const state = {
|
|
1150
|
+
selectors,
|
|
1151
|
+
_params,
|
|
1152
|
+
value,
|
|
1153
|
+
options,
|
|
1154
|
+
world,
|
|
1155
|
+
type: Types.SET_INPUT,
|
|
1156
|
+
text: `Set input value`,
|
|
1157
|
+
operation: "setInputValue",
|
|
1158
|
+
log: "***** set input value " + selectors.element_name + " *****\n",
|
|
1159
|
+
};
|
|
1188
1160
|
try {
|
|
1189
|
-
|
|
1190
|
-
let
|
|
1191
|
-
await this.scrollIfNeeded(element, info);
|
|
1192
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1193
|
-
await this._highlightElements(element);
|
|
1161
|
+
await _preCommand(state, this);
|
|
1162
|
+
let value = await this._replaceWithLocalData(state.value, this);
|
|
1194
1163
|
try {
|
|
1195
|
-
await element.evaluateHandle((el, value) => {
|
|
1164
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1196
1165
|
el.value = value;
|
|
1197
1166
|
}, value);
|
|
1198
1167
|
}
|
|
1199
1168
|
catch (error) {
|
|
1200
1169
|
this.logger.error("setInputValue failed, will try again");
|
|
1201
|
-
|
|
1202
|
-
info.
|
|
1203
|
-
|
|
1204
|
-
await element.evaluateHandle((el, value) => {
|
|
1170
|
+
await _screenshot(state, this);
|
|
1171
|
+
Object.assign(error, { info: state.info });
|
|
1172
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1205
1173
|
el.value = value;
|
|
1206
1174
|
});
|
|
1207
1175
|
}
|
|
1208
1176
|
}
|
|
1209
1177
|
catch (e) {
|
|
1210
|
-
|
|
1211
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1212
|
-
info.screenshotPath = screenshotPath;
|
|
1213
|
-
Object.assign(e, { info: info });
|
|
1214
|
-
error = e;
|
|
1215
|
-
throw e;
|
|
1178
|
+
await _commandError(state, e, this);
|
|
1216
1179
|
}
|
|
1217
1180
|
finally {
|
|
1218
|
-
|
|
1219
|
-
this._reportToWorld(world, {
|
|
1220
|
-
element_name: selectors.element_name,
|
|
1221
|
-
type: Types.SET_INPUT,
|
|
1222
|
-
text: `Set input value`,
|
|
1223
|
-
value: value,
|
|
1224
|
-
screenshotId,
|
|
1225
|
-
result: error
|
|
1226
|
-
? {
|
|
1227
|
-
status: "FAILED",
|
|
1228
|
-
startTime,
|
|
1229
|
-
endTime,
|
|
1230
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1231
|
-
}
|
|
1232
|
-
: {
|
|
1233
|
-
status: "PASSED",
|
|
1234
|
-
startTime,
|
|
1235
|
-
endTime,
|
|
1236
|
-
},
|
|
1237
|
-
info: info,
|
|
1238
|
-
});
|
|
1181
|
+
_commandFinally(state, this);
|
|
1239
1182
|
}
|
|
1240
1183
|
}
|
|
1241
1184
|
async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1185
|
+
const state = {
|
|
1186
|
+
selectors,
|
|
1187
|
+
_params,
|
|
1188
|
+
value: await this._replaceWithLocalData(value, this),
|
|
1189
|
+
options,
|
|
1190
|
+
world,
|
|
1191
|
+
type: Types.SET_DATE_TIME,
|
|
1192
|
+
text: `Set date time value: ${value}`,
|
|
1193
|
+
operation: "setDateTime",
|
|
1194
|
+
log: "***** set date time value " + selectors.element_name + " *****\n",
|
|
1195
|
+
throwError: false,
|
|
1196
|
+
};
|
|
1252
1197
|
try {
|
|
1253
|
-
|
|
1254
|
-
let element = await this._locate(selectors, info, _params);
|
|
1255
|
-
//insert red border around the element
|
|
1256
|
-
await this.scrollIfNeeded(element, info);
|
|
1257
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1258
|
-
await this._highlightElements(element);
|
|
1198
|
+
await _preCommand(state, this);
|
|
1259
1199
|
try {
|
|
1260
|
-
await element.click();
|
|
1200
|
+
await state.element.click();
|
|
1261
1201
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1262
1202
|
if (format) {
|
|
1263
|
-
value = dayjs(value).format(format);
|
|
1264
|
-
await element.fill(value);
|
|
1203
|
+
state.value = dayjs(state.value).format(format);
|
|
1204
|
+
await state.element.fill(state.value);
|
|
1265
1205
|
}
|
|
1266
1206
|
else {
|
|
1267
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1268
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1207
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1208
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1269
1209
|
el.value = ""; // clear input
|
|
1270
1210
|
el.value = dateTimeValue;
|
|
1271
1211
|
}, dateTimeValue);
|
|
@@ -1278,20 +1218,19 @@ class StableBrowser {
|
|
|
1278
1218
|
}
|
|
1279
1219
|
catch (err) {
|
|
1280
1220
|
//await this.closeUnexpectedPopups();
|
|
1281
|
-
this.logger.error("setting date time input failed " + JSON.stringify(info));
|
|
1221
|
+
this.logger.error("setting date time input failed " + JSON.stringify(state.info));
|
|
1282
1222
|
this.logger.info("Trying again");
|
|
1283
|
-
|
|
1284
|
-
info.
|
|
1285
|
-
Object.assign(err, { info: info });
|
|
1223
|
+
await _screenshot(state, this);
|
|
1224
|
+
Object.assign(err, { info: state.info });
|
|
1286
1225
|
await element.click();
|
|
1287
1226
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1288
1227
|
if (format) {
|
|
1289
|
-
value = dayjs(value).format(format);
|
|
1290
|
-
await element.fill(value);
|
|
1228
|
+
state.value = dayjs(state.value).format(format);
|
|
1229
|
+
await state.element.fill(state.value);
|
|
1291
1230
|
}
|
|
1292
1231
|
else {
|
|
1293
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1294
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1232
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1233
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1295
1234
|
el.value = ""; // clear input
|
|
1296
1235
|
el.value = dateTimeValue;
|
|
1297
1236
|
}, dateTimeValue);
|
|
@@ -1304,60 +1243,39 @@ class StableBrowser {
|
|
|
1304
1243
|
}
|
|
1305
1244
|
}
|
|
1306
1245
|
catch (e) {
|
|
1307
|
-
|
|
1308
|
-
throw e;
|
|
1246
|
+
await _commandError(state, e, this);
|
|
1309
1247
|
}
|
|
1310
1248
|
finally {
|
|
1311
|
-
|
|
1312
|
-
this._reportToWorld(world, {
|
|
1313
|
-
element_name: selectors.element_name,
|
|
1314
|
-
type: Types.SET_DATE_TIME,
|
|
1315
|
-
screenshotId,
|
|
1316
|
-
value: value,
|
|
1317
|
-
text: `setDateTime input with value: ${value}`,
|
|
1318
|
-
result: error
|
|
1319
|
-
? {
|
|
1320
|
-
status: "FAILED",
|
|
1321
|
-
startTime,
|
|
1322
|
-
endTime,
|
|
1323
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1324
|
-
}
|
|
1325
|
-
: {
|
|
1326
|
-
status: "PASSED",
|
|
1327
|
-
startTime,
|
|
1328
|
-
endTime,
|
|
1329
|
-
},
|
|
1330
|
-
info: info,
|
|
1331
|
-
});
|
|
1249
|
+
_commandFinally(state, this);
|
|
1332
1250
|
}
|
|
1333
1251
|
}
|
|
1334
1252
|
async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
|
|
1335
1253
|
_value = unEscapeString(_value);
|
|
1336
|
-
this._validateSelectors(selectors);
|
|
1337
|
-
const startTime = Date.now();
|
|
1338
|
-
let error = null;
|
|
1339
|
-
let screenshotId = null;
|
|
1340
|
-
let screenshotPath = null;
|
|
1341
|
-
const info = {};
|
|
1342
|
-
info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
|
|
1343
|
-
info.operation = "clickType";
|
|
1344
|
-
info.selectors = selectors;
|
|
1345
1254
|
const newValue = await this._replaceWithLocalData(_value, world);
|
|
1255
|
+
const state = {
|
|
1256
|
+
selectors,
|
|
1257
|
+
_params,
|
|
1258
|
+
value: newValue,
|
|
1259
|
+
originalValue: _value,
|
|
1260
|
+
options,
|
|
1261
|
+
world,
|
|
1262
|
+
type: Types.FILL,
|
|
1263
|
+
text: `Click type input with value: ${_value}`,
|
|
1264
|
+
operation: "clickType",
|
|
1265
|
+
log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
|
|
1266
|
+
};
|
|
1346
1267
|
if (newValue !== _value) {
|
|
1347
1268
|
//this.logger.info(_value + "=" + newValue);
|
|
1348
1269
|
_value = newValue;
|
|
1349
1270
|
}
|
|
1350
|
-
info.value = _value;
|
|
1351
1271
|
try {
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
await this.scrollIfNeeded(element, info);
|
|
1355
|
-
await this._highlightElements(element);
|
|
1272
|
+
await _preCommand(state, this);
|
|
1273
|
+
state.info.value = _value;
|
|
1356
1274
|
if (options === null || options === undefined || !options.press) {
|
|
1357
1275
|
try {
|
|
1358
|
-
let currentValue = await element.inputValue();
|
|
1276
|
+
let currentValue = await state.element.inputValue();
|
|
1359
1277
|
if (currentValue) {
|
|
1360
|
-
await element.fill("");
|
|
1278
|
+
await state.element.fill("");
|
|
1361
1279
|
}
|
|
1362
1280
|
}
|
|
1363
1281
|
catch (e) {
|
|
@@ -1366,22 +1284,22 @@ class StableBrowser {
|
|
|
1366
1284
|
}
|
|
1367
1285
|
if (options === null || options === undefined || options.press) {
|
|
1368
1286
|
try {
|
|
1369
|
-
await element.click({ timeout: 5000 });
|
|
1287
|
+
await state.element.click({ timeout: 5000 });
|
|
1370
1288
|
}
|
|
1371
1289
|
catch (e) {
|
|
1372
|
-
await element.dispatchEvent("click");
|
|
1290
|
+
await state.element.dispatchEvent("click");
|
|
1373
1291
|
}
|
|
1374
1292
|
}
|
|
1375
1293
|
else {
|
|
1376
1294
|
try {
|
|
1377
|
-
await element.focus();
|
|
1295
|
+
await state.element.focus();
|
|
1378
1296
|
}
|
|
1379
1297
|
catch (e) {
|
|
1380
|
-
await element.dispatchEvent("focus");
|
|
1298
|
+
await state.element.dispatchEvent("focus");
|
|
1381
1299
|
}
|
|
1382
1300
|
}
|
|
1383
1301
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1384
|
-
const valueSegment =
|
|
1302
|
+
const valueSegment = state.value.split("&&");
|
|
1385
1303
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1386
1304
|
if (i > 0) {
|
|
1387
1305
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1401,14 +1319,14 @@ class StableBrowser {
|
|
|
1401
1319
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1402
1320
|
}
|
|
1403
1321
|
}
|
|
1404
|
-
|
|
1322
|
+
await _screenshot(state, this);
|
|
1405
1323
|
if (enter === true) {
|
|
1406
1324
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1407
1325
|
await this.page.keyboard.press("Enter");
|
|
1408
1326
|
await this.waitForPageLoad();
|
|
1409
1327
|
}
|
|
1410
1328
|
else if (enter === false) {
|
|
1411
|
-
await element.dispatchEvent("change");
|
|
1329
|
+
await state.element.dispatchEvent("change");
|
|
1412
1330
|
//await this.page.keyboard.press("Tab");
|
|
1413
1331
|
}
|
|
1414
1332
|
else {
|
|
@@ -1417,104 +1335,50 @@ class StableBrowser {
|
|
|
1417
1335
|
await this.waitForPageLoad();
|
|
1418
1336
|
}
|
|
1419
1337
|
}
|
|
1420
|
-
return info;
|
|
1338
|
+
return state.info;
|
|
1421
1339
|
}
|
|
1422
1340
|
catch (e) {
|
|
1423
|
-
|
|
1424
|
-
this.logger.error("fill failed " + JSON.stringify(info));
|
|
1425
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1426
|
-
info.screenshotPath = screenshotPath;
|
|
1427
|
-
Object.assign(e, { info: info });
|
|
1428
|
-
error = e;
|
|
1429
|
-
throw e;
|
|
1341
|
+
await _commandError(state, e, this);
|
|
1430
1342
|
}
|
|
1431
1343
|
finally {
|
|
1432
|
-
|
|
1433
|
-
this._reportToWorld(world, {
|
|
1434
|
-
element_name: selectors.element_name,
|
|
1435
|
-
type: Types.FILL,
|
|
1436
|
-
screenshotId,
|
|
1437
|
-
value: _value,
|
|
1438
|
-
text: `clickType input with value: ${_value}`,
|
|
1439
|
-
result: error
|
|
1440
|
-
? {
|
|
1441
|
-
status: "FAILED",
|
|
1442
|
-
startTime,
|
|
1443
|
-
endTime,
|
|
1444
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1445
|
-
}
|
|
1446
|
-
: {
|
|
1447
|
-
status: "PASSED",
|
|
1448
|
-
startTime,
|
|
1449
|
-
endTime,
|
|
1450
|
-
},
|
|
1451
|
-
info: info,
|
|
1452
|
-
});
|
|
1344
|
+
_commandFinally(state, this);
|
|
1453
1345
|
}
|
|
1454
1346
|
}
|
|
1455
1347
|
async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1348
|
+
const state = {
|
|
1349
|
+
selectors,
|
|
1350
|
+
_params,
|
|
1351
|
+
value: unEscapeString(value),
|
|
1352
|
+
options,
|
|
1353
|
+
world,
|
|
1354
|
+
type: Types.FILL,
|
|
1355
|
+
text: `Fill input with value: ${value}`,
|
|
1356
|
+
operation: "fill",
|
|
1357
|
+
log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
|
|
1358
|
+
};
|
|
1467
1359
|
try {
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
await
|
|
1471
|
-
await element.fill(value);
|
|
1472
|
-
await element.dispatchEvent("change");
|
|
1360
|
+
await _preCommand(state, this);
|
|
1361
|
+
await state.element.fill(value);
|
|
1362
|
+
await state.element.dispatchEvent("change");
|
|
1473
1363
|
if (enter) {
|
|
1474
1364
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1475
1365
|
await this.page.keyboard.press("Enter");
|
|
1476
1366
|
}
|
|
1477
1367
|
await this.waitForPageLoad();
|
|
1478
|
-
return info;
|
|
1368
|
+
return state.info;
|
|
1479
1369
|
}
|
|
1480
1370
|
catch (e) {
|
|
1481
|
-
|
|
1482
|
-
this.logger.error("fill failed " + info.log);
|
|
1483
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1484
|
-
info.screenshotPath = screenshotPath;
|
|
1485
|
-
Object.assign(e, { info: info });
|
|
1486
|
-
error = e;
|
|
1487
|
-
throw e;
|
|
1371
|
+
await _commandError(state, e, this);
|
|
1488
1372
|
}
|
|
1489
1373
|
finally {
|
|
1490
|
-
|
|
1491
|
-
this._reportToWorld(world, {
|
|
1492
|
-
element_name: selectors.element_name,
|
|
1493
|
-
type: Types.FILL,
|
|
1494
|
-
screenshotId,
|
|
1495
|
-
value,
|
|
1496
|
-
text: `Fill input with value: ${value}`,
|
|
1497
|
-
result: error
|
|
1498
|
-
? {
|
|
1499
|
-
status: "FAILED",
|
|
1500
|
-
startTime,
|
|
1501
|
-
endTime,
|
|
1502
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1503
|
-
}
|
|
1504
|
-
: {
|
|
1505
|
-
status: "PASSED",
|
|
1506
|
-
startTime,
|
|
1507
|
-
endTime,
|
|
1508
|
-
},
|
|
1509
|
-
info: info,
|
|
1510
|
-
});
|
|
1374
|
+
_commandFinally(state, this);
|
|
1511
1375
|
}
|
|
1512
1376
|
}
|
|
1513
1377
|
async getText(selectors, _params = null, options = {}, info = {}, world = null) {
|
|
1514
1378
|
return await this._getText(selectors, 0, _params, options, info, world);
|
|
1515
1379
|
}
|
|
1516
1380
|
async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
|
|
1517
|
-
|
|
1381
|
+
_validateSelectors(selectors);
|
|
1518
1382
|
let screenshotId = null;
|
|
1519
1383
|
let screenshotPath = null;
|
|
1520
1384
|
if (!info.log) {
|
|
@@ -1558,166 +1422,124 @@ class StableBrowser {
|
|
|
1558
1422
|
}
|
|
1559
1423
|
}
|
|
1560
1424
|
async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
|
|
1561
|
-
var _a;
|
|
1562
|
-
this._validateSelectors(selectors);
|
|
1563
1425
|
if (!pattern) {
|
|
1564
1426
|
throw new Error("pattern is null");
|
|
1565
1427
|
}
|
|
1566
1428
|
if (!text) {
|
|
1567
1429
|
throw new Error("text is null");
|
|
1568
1430
|
}
|
|
1431
|
+
const state = {
|
|
1432
|
+
selectors,
|
|
1433
|
+
_params,
|
|
1434
|
+
pattern,
|
|
1435
|
+
value: pattern,
|
|
1436
|
+
options,
|
|
1437
|
+
world,
|
|
1438
|
+
locate: false,
|
|
1439
|
+
scroll: false,
|
|
1440
|
+
screenshot: false,
|
|
1441
|
+
highlight: false,
|
|
1442
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1443
|
+
text: `Verify element contains pattern: ${pattern}`,
|
|
1444
|
+
operation: "containsPattern",
|
|
1445
|
+
log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
|
|
1446
|
+
};
|
|
1569
1447
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1570
1448
|
if (newValue !== text) {
|
|
1571
1449
|
this.logger.info(text + "=" + newValue);
|
|
1572
1450
|
text = newValue;
|
|
1573
1451
|
}
|
|
1574
|
-
const startTime = Date.now();
|
|
1575
|
-
let error = null;
|
|
1576
|
-
let screenshotId = null;
|
|
1577
|
-
let screenshotPath = null;
|
|
1578
|
-
const info = {};
|
|
1579
|
-
info.log =
|
|
1580
|
-
"***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
|
|
1581
|
-
info.operation = "containsPattern";
|
|
1582
|
-
info.selectors = selectors;
|
|
1583
|
-
info.value = text;
|
|
1584
|
-
info.pattern = pattern;
|
|
1585
1452
|
let foundObj = null;
|
|
1586
1453
|
try {
|
|
1587
|
-
|
|
1454
|
+
await _preCommand(state, this);
|
|
1455
|
+
state.info.pattern = pattern;
|
|
1456
|
+
foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
|
|
1588
1457
|
if (foundObj && foundObj.element) {
|
|
1589
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1458
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1590
1459
|
}
|
|
1591
|
-
|
|
1460
|
+
await _screenshot(state, this);
|
|
1592
1461
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1593
1462
|
pattern = pattern.replace("{text}", escapedText);
|
|
1594
1463
|
let regex = new RegExp(pattern, "im");
|
|
1595
|
-
if (!regex.test(foundObj
|
|
1596
|
-
info.foundText = foundObj
|
|
1464
|
+
if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
|
|
1465
|
+
state.info.foundText = foundObj?.text;
|
|
1597
1466
|
throw new Error("element doesn't contain text " + text);
|
|
1598
1467
|
}
|
|
1599
|
-
return info;
|
|
1468
|
+
return state.info;
|
|
1600
1469
|
}
|
|
1601
1470
|
catch (e) {
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
|
|
1605
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1606
|
-
info.screenshotPath = screenshotPath;
|
|
1607
|
-
Object.assign(e, { info: info });
|
|
1608
|
-
error = e;
|
|
1609
|
-
throw e;
|
|
1471
|
+
this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
|
|
1472
|
+
await _commandError(state, e, this);
|
|
1610
1473
|
}
|
|
1611
1474
|
finally {
|
|
1612
|
-
|
|
1613
|
-
this._reportToWorld(world, {
|
|
1614
|
-
element_name: selectors.element_name,
|
|
1615
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1616
|
-
value: pattern,
|
|
1617
|
-
text: `Verify element contains pattern: ${pattern}`,
|
|
1618
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1619
|
-
result: error
|
|
1620
|
-
? {
|
|
1621
|
-
status: "FAILED",
|
|
1622
|
-
startTime,
|
|
1623
|
-
endTime,
|
|
1624
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1625
|
-
}
|
|
1626
|
-
: {
|
|
1627
|
-
status: "PASSED",
|
|
1628
|
-
startTime,
|
|
1629
|
-
endTime,
|
|
1630
|
-
},
|
|
1631
|
-
info: info,
|
|
1632
|
-
});
|
|
1475
|
+
_commandFinally(state, this);
|
|
1633
1476
|
}
|
|
1634
1477
|
}
|
|
1635
1478
|
async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1479
|
+
const state = {
|
|
1480
|
+
selectors,
|
|
1481
|
+
_params,
|
|
1482
|
+
value: text,
|
|
1483
|
+
options,
|
|
1484
|
+
world,
|
|
1485
|
+
locate: false,
|
|
1486
|
+
scroll: false,
|
|
1487
|
+
screenshot: false,
|
|
1488
|
+
highlight: false,
|
|
1489
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1490
|
+
text: `Verify element contains text: ${text}`,
|
|
1491
|
+
operation: "containsText",
|
|
1492
|
+
log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
|
|
1493
|
+
};
|
|
1639
1494
|
if (!text) {
|
|
1640
1495
|
throw new Error("text is null");
|
|
1641
1496
|
}
|
|
1642
|
-
|
|
1643
|
-
let error = null;
|
|
1644
|
-
let screenshotId = null;
|
|
1645
|
-
let screenshotPath = null;
|
|
1646
|
-
const info = {};
|
|
1647
|
-
info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
|
|
1648
|
-
info.operation = "containsText";
|
|
1649
|
-
info.selectors = selectors;
|
|
1497
|
+
text = unEscapeString(text);
|
|
1650
1498
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1651
1499
|
if (newValue !== text) {
|
|
1652
1500
|
this.logger.info(text + "=" + newValue);
|
|
1653
1501
|
text = newValue;
|
|
1654
1502
|
}
|
|
1655
|
-
info.value = text;
|
|
1656
1503
|
let foundObj = null;
|
|
1657
1504
|
try {
|
|
1658
|
-
|
|
1505
|
+
await _preCommand(state, this);
|
|
1506
|
+
foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
|
|
1659
1507
|
if (foundObj && foundObj.element) {
|
|
1660
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1508
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1661
1509
|
}
|
|
1662
|
-
|
|
1510
|
+
await _screenshot(state, this);
|
|
1663
1511
|
const dateAlternatives = findDateAlternatives(text);
|
|
1664
1512
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1665
1513
|
if (dateAlternatives.date) {
|
|
1666
1514
|
for (let i = 0; i < dateAlternatives.dates.length; i++) {
|
|
1667
|
-
if (
|
|
1668
|
-
|
|
1669
|
-
return info;
|
|
1515
|
+
if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
|
|
1516
|
+
foundObj?.value?.includes(dateAlternatives.dates[i])) {
|
|
1517
|
+
return state.info;
|
|
1670
1518
|
}
|
|
1671
1519
|
}
|
|
1672
1520
|
throw new Error("element doesn't contain text " + text);
|
|
1673
1521
|
}
|
|
1674
1522
|
else if (numberAlternatives.number) {
|
|
1675
1523
|
for (let i = 0; i < numberAlternatives.numbers.length; i++) {
|
|
1676
|
-
if (
|
|
1677
|
-
|
|
1678
|
-
return info;
|
|
1524
|
+
if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
|
|
1525
|
+
foundObj?.value?.includes(numberAlternatives.numbers[i])) {
|
|
1526
|
+
return state.info;
|
|
1679
1527
|
}
|
|
1680
1528
|
}
|
|
1681
1529
|
throw new Error("element doesn't contain text " + text);
|
|
1682
1530
|
}
|
|
1683
|
-
else if (!
|
|
1684
|
-
info.foundText = foundObj
|
|
1685
|
-
info.value = foundObj
|
|
1531
|
+
else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
|
|
1532
|
+
state.info.foundText = foundObj?.text;
|
|
1533
|
+
state.info.value = foundObj?.value;
|
|
1686
1534
|
throw new Error("element doesn't contain text " + text);
|
|
1687
1535
|
}
|
|
1688
|
-
return info;
|
|
1536
|
+
return state.info;
|
|
1689
1537
|
}
|
|
1690
1538
|
catch (e) {
|
|
1691
|
-
|
|
1692
|
-
this.logger.error("verify element contains text failed " + info.log);
|
|
1693
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1694
|
-
info.screenshotPath = screenshotPath;
|
|
1695
|
-
Object.assign(e, { info: info });
|
|
1696
|
-
error = e;
|
|
1697
|
-
throw e;
|
|
1539
|
+
await _commandError(state, e, this);
|
|
1698
1540
|
}
|
|
1699
1541
|
finally {
|
|
1700
|
-
|
|
1701
|
-
this._reportToWorld(world, {
|
|
1702
|
-
element_name: selectors.element_name,
|
|
1703
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1704
|
-
text: `Verify element contains text: ${text}`,
|
|
1705
|
-
value: text,
|
|
1706
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1707
|
-
result: error
|
|
1708
|
-
? {
|
|
1709
|
-
status: "FAILED",
|
|
1710
|
-
startTime,
|
|
1711
|
-
endTime,
|
|
1712
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1713
|
-
}
|
|
1714
|
-
: {
|
|
1715
|
-
status: "PASSED",
|
|
1716
|
-
startTime,
|
|
1717
|
-
endTime,
|
|
1718
|
-
},
|
|
1719
|
-
info: info,
|
|
1720
|
-
});
|
|
1542
|
+
_commandFinally(state, this);
|
|
1721
1543
|
}
|
|
1722
1544
|
}
|
|
1723
1545
|
_getDataFile(world = null) {
|
|
@@ -1899,11 +1721,9 @@ class StableBrowser {
|
|
|
1899
1721
|
if (!fs.existsSync(world.screenshotPath)) {
|
|
1900
1722
|
fs.mkdirSync(world.screenshotPath, { recursive: true });
|
|
1901
1723
|
}
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
}
|
|
1906
|
-
const screenshotPath = path.join(world.screenshotPath, nextIndex + ".png");
|
|
1724
|
+
// to make sure the path doesn't start with -
|
|
1725
|
+
const uuidStr = "id_" + randomUUID();
|
|
1726
|
+
const screenshotPath = path.join(world.screenshotPath, uuidStr + ".png");
|
|
1907
1727
|
try {
|
|
1908
1728
|
await this.takeScreenshot(screenshotPath);
|
|
1909
1729
|
// let buffer = await this.page.screenshot({ timeout: 4000 });
|
|
@@ -1917,7 +1737,7 @@ class StableBrowser {
|
|
|
1917
1737
|
catch (e) {
|
|
1918
1738
|
this.logger.info("unable to take screenshot, ignored");
|
|
1919
1739
|
}
|
|
1920
|
-
result.screenshotId =
|
|
1740
|
+
result.screenshotId = uuidStr;
|
|
1921
1741
|
result.screenshotPath = screenshotPath;
|
|
1922
1742
|
if (info && info.box) {
|
|
1923
1743
|
await drawRectangle(screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
|
|
@@ -1991,127 +1811,69 @@ class StableBrowser {
|
|
|
1991
1811
|
}
|
|
1992
1812
|
}
|
|
1993
1813
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1814
|
+
const state = {
|
|
1815
|
+
selectors,
|
|
1816
|
+
_params,
|
|
1817
|
+
options,
|
|
1818
|
+
world,
|
|
1819
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1820
|
+
text: `Verify element exists in page`,
|
|
1821
|
+
operation: "verifyElementExistInPage",
|
|
1822
|
+
log: "***** verify element " + selectors.element_name + " exists in page *****\n",
|
|
1823
|
+
};
|
|
1999
1824
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2000
|
-
const info = {};
|
|
2001
|
-
info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
|
|
2002
|
-
info.operation = "verify";
|
|
2003
|
-
info.selectors = selectors;
|
|
2004
1825
|
try {
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
}
|
|
2009
|
-
await this._highlightElements(element);
|
|
2010
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2011
|
-
await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2012
|
-
return info;
|
|
1826
|
+
await _preCommand(state, this);
|
|
1827
|
+
await expect(state.element).toHaveCount(1, { timeout: 10000 });
|
|
1828
|
+
return state.info;
|
|
2013
1829
|
}
|
|
2014
1830
|
catch (e) {
|
|
2015
|
-
|
|
2016
|
-
this.logger.error("verify failed " + info.log);
|
|
2017
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2018
|
-
info.screenshotPath = screenshotPath;
|
|
2019
|
-
Object.assign(e, { info: info });
|
|
2020
|
-
error = e;
|
|
2021
|
-
throw e;
|
|
1831
|
+
await _commandError(state, e, this);
|
|
2022
1832
|
}
|
|
2023
1833
|
finally {
|
|
2024
|
-
|
|
2025
|
-
this._reportToWorld(world, {
|
|
2026
|
-
element_name: selectors.element_name,
|
|
2027
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
2028
|
-
text: "Verify element exists in page",
|
|
2029
|
-
screenshotId,
|
|
2030
|
-
result: error
|
|
2031
|
-
? {
|
|
2032
|
-
status: "FAILED",
|
|
2033
|
-
startTime,
|
|
2034
|
-
endTime,
|
|
2035
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2036
|
-
}
|
|
2037
|
-
: {
|
|
2038
|
-
status: "PASSED",
|
|
2039
|
-
startTime,
|
|
2040
|
-
endTime,
|
|
2041
|
-
},
|
|
2042
|
-
info: info,
|
|
2043
|
-
});
|
|
1834
|
+
_commandFinally(state, this);
|
|
2044
1835
|
}
|
|
2045
1836
|
}
|
|
2046
1837
|
async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
1838
|
+
const state = {
|
|
1839
|
+
selectors,
|
|
1840
|
+
_params,
|
|
1841
|
+
attribute,
|
|
1842
|
+
variable,
|
|
1843
|
+
options,
|
|
1844
|
+
world,
|
|
1845
|
+
type: Types.EXTRACT,
|
|
1846
|
+
text: `Extract attribute from element`,
|
|
1847
|
+
operation: "extractAttribute",
|
|
1848
|
+
log: "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1849
|
+
};
|
|
2052
1850
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2053
|
-
const info = {};
|
|
2054
|
-
info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
|
|
2055
|
-
info.operation = "extract";
|
|
2056
|
-
info.selectors = selectors;
|
|
2057
1851
|
try {
|
|
2058
|
-
|
|
2059
|
-
await this._highlightElements(element);
|
|
2060
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1852
|
+
await _preCommand(state, this);
|
|
2061
1853
|
switch (attribute) {
|
|
2062
1854
|
case "inner_text":
|
|
2063
|
-
|
|
1855
|
+
state.value = await state.element.innerText();
|
|
2064
1856
|
break;
|
|
2065
1857
|
case "href":
|
|
2066
|
-
|
|
1858
|
+
state.value = await state.element.getAttribute("href");
|
|
2067
1859
|
break;
|
|
2068
1860
|
case "value":
|
|
2069
|
-
|
|
1861
|
+
state.value = await state.element.inputValue();
|
|
2070
1862
|
break;
|
|
2071
1863
|
default:
|
|
2072
|
-
|
|
1864
|
+
state.value = await state.element.getAttribute(attribute);
|
|
2073
1865
|
break;
|
|
2074
1866
|
}
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
this.setTestData({ [variable]: info.value }, world);
|
|
2080
|
-
this.logger.info("set test data: " + variable + "=" + info.value);
|
|
2081
|
-
return info;
|
|
1867
|
+
state.info.value = state.value;
|
|
1868
|
+
this.setTestData({ [variable]: state.value }, world);
|
|
1869
|
+
this.logger.info("set test data: " + variable + "=" + state.value);
|
|
1870
|
+
return state.info;
|
|
2082
1871
|
}
|
|
2083
1872
|
catch (e) {
|
|
2084
|
-
|
|
2085
|
-
this.logger.error("extract failed " + info.log);
|
|
2086
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2087
|
-
info.screenshotPath = screenshotPath;
|
|
2088
|
-
Object.assign(e, { info: info });
|
|
2089
|
-
error = e;
|
|
2090
|
-
throw e;
|
|
1873
|
+
await _commandError(state, e, this);
|
|
2091
1874
|
}
|
|
2092
1875
|
finally {
|
|
2093
|
-
|
|
2094
|
-
this._reportToWorld(world, {
|
|
2095
|
-
element_name: selectors.element_name,
|
|
2096
|
-
type: Types.EXTRACT_ATTRIBUTE,
|
|
2097
|
-
variable: variable,
|
|
2098
|
-
value: info.value,
|
|
2099
|
-
text: "Extract attribute from element",
|
|
2100
|
-
screenshotId,
|
|
2101
|
-
result: error
|
|
2102
|
-
? {
|
|
2103
|
-
status: "FAILED",
|
|
2104
|
-
startTime,
|
|
2105
|
-
endTime,
|
|
2106
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2107
|
-
}
|
|
2108
|
-
: {
|
|
2109
|
-
status: "PASSED",
|
|
2110
|
-
startTime,
|
|
2111
|
-
endTime,
|
|
2112
|
-
},
|
|
2113
|
-
info: info,
|
|
2114
|
-
});
|
|
1876
|
+
_commandFinally(state, this);
|
|
2115
1877
|
}
|
|
2116
1878
|
}
|
|
2117
1879
|
async extractEmailData(emailAddress, options, world) {
|
|
@@ -2188,7 +1950,8 @@ class StableBrowser {
|
|
|
2188
1950
|
catch (e) {
|
|
2189
1951
|
errorCount++;
|
|
2190
1952
|
if (errorCount > 3) {
|
|
2191
|
-
throw e;
|
|
1953
|
+
// throw e;
|
|
1954
|
+
await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
|
|
2192
1955
|
}
|
|
2193
1956
|
// ignore
|
|
2194
1957
|
}
|
|
@@ -2299,11 +2062,12 @@ class StableBrowser {
|
|
|
2299
2062
|
info.screenshotPath = screenshotPath;
|
|
2300
2063
|
Object.assign(e, { info: info });
|
|
2301
2064
|
error = e;
|
|
2302
|
-
throw e;
|
|
2065
|
+
// throw e;
|
|
2066
|
+
await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
|
|
2303
2067
|
}
|
|
2304
2068
|
finally {
|
|
2305
2069
|
const endTime = Date.now();
|
|
2306
|
-
|
|
2070
|
+
_reportToWorld(world, {
|
|
2307
2071
|
type: Types.VERIFY_PAGE_PATH,
|
|
2308
2072
|
text: "Verify page path",
|
|
2309
2073
|
screenshotId,
|
|
@@ -2312,7 +2076,7 @@ class StableBrowser {
|
|
|
2312
2076
|
status: "FAILED",
|
|
2313
2077
|
startTime,
|
|
2314
2078
|
endTime,
|
|
2315
|
-
message: error
|
|
2079
|
+
message: error?.message,
|
|
2316
2080
|
}
|
|
2317
2081
|
: {
|
|
2318
2082
|
status: "PASSED",
|
|
@@ -2325,52 +2089,58 @@ class StableBrowser {
|
|
|
2325
2089
|
}
|
|
2326
2090
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2327
2091
|
text = unEscapeString(text);
|
|
2328
|
-
const
|
|
2092
|
+
const state = {
|
|
2093
|
+
text_search: text,
|
|
2094
|
+
options,
|
|
2095
|
+
world,
|
|
2096
|
+
locate: false,
|
|
2097
|
+
scroll: false,
|
|
2098
|
+
highlight: false,
|
|
2099
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
2100
|
+
text: `Verify text exists in page`,
|
|
2101
|
+
operation: "verifyTextExistInPage",
|
|
2102
|
+
log: "***** verify text " + text + " exists in page *****\n",
|
|
2103
|
+
};
|
|
2329
2104
|
const timeout = this._getLoadTimeout(options);
|
|
2330
|
-
let error = null;
|
|
2331
|
-
let screenshotId = null;
|
|
2332
|
-
let screenshotPath = null;
|
|
2333
2105
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2334
|
-
const info = {};
|
|
2335
|
-
info.log = "***** verify text " + text + " exists in page *****\n";
|
|
2336
|
-
info.operation = "verifyTextExistInPage";
|
|
2337
2106
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
2338
2107
|
if (newValue !== text) {
|
|
2339
2108
|
this.logger.info(text + "=" + newValue);
|
|
2340
2109
|
text = newValue;
|
|
2341
2110
|
}
|
|
2342
|
-
info.text = text;
|
|
2343
2111
|
let dateAlternatives = findDateAlternatives(text);
|
|
2344
2112
|
let numberAlternatives = findNumberAlternatives(text);
|
|
2345
2113
|
try {
|
|
2114
|
+
await _preCommand(state, this);
|
|
2115
|
+
state.info.text = text;
|
|
2346
2116
|
while (true) {
|
|
2347
2117
|
const frames = this.page.frames();
|
|
2348
2118
|
let results = [];
|
|
2349
2119
|
for (let i = 0; i < frames.length; i++) {
|
|
2350
2120
|
if (dateAlternatives.date) {
|
|
2351
2121
|
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2352
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "
|
|
2122
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2353
2123
|
result.frame = frames[i];
|
|
2354
2124
|
results.push(result);
|
|
2355
2125
|
}
|
|
2356
2126
|
}
|
|
2357
2127
|
else if (numberAlternatives.number) {
|
|
2358
2128
|
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2359
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "
|
|
2129
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2360
2130
|
result.frame = frames[i];
|
|
2361
2131
|
results.push(result);
|
|
2362
2132
|
}
|
|
2363
2133
|
}
|
|
2364
2134
|
else {
|
|
2365
|
-
const result = await this._locateElementByText(frames[i], text, "
|
|
2135
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2366
2136
|
result.frame = frames[i];
|
|
2367
2137
|
results.push(result);
|
|
2368
2138
|
}
|
|
2369
2139
|
}
|
|
2370
|
-
info.results = results;
|
|
2140
|
+
state.info.results = results;
|
|
2371
2141
|
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2372
2142
|
if (resultWithElementsFound.length === 0) {
|
|
2373
|
-
if (Date.now() - startTime > timeout) {
|
|
2143
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2374
2144
|
throw new Error(`Text ${text} not found in page`);
|
|
2375
2145
|
}
|
|
2376
2146
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -2382,44 +2152,89 @@ class StableBrowser {
|
|
|
2382
2152
|
await this._highlightElements(frame, dataAttribute);
|
|
2383
2153
|
const element = await frame.$(dataAttribute);
|
|
2384
2154
|
if (element) {
|
|
2385
|
-
await this.scrollIfNeeded(element, info);
|
|
2155
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2386
2156
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2387
2157
|
}
|
|
2388
2158
|
}
|
|
2389
|
-
|
|
2390
|
-
return info;
|
|
2159
|
+
await _screenshot(state, this);
|
|
2160
|
+
return state.info;
|
|
2391
2161
|
}
|
|
2392
2162
|
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2393
2163
|
}
|
|
2394
2164
|
catch (e) {
|
|
2395
|
-
|
|
2396
|
-
this.logger.error("verify text exist in page failed " + info.log);
|
|
2397
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2398
|
-
info.screenshotPath = screenshotPath;
|
|
2399
|
-
Object.assign(e, { info: info });
|
|
2400
|
-
error = e;
|
|
2401
|
-
throw e;
|
|
2165
|
+
await _commandError(state, e, this);
|
|
2402
2166
|
}
|
|
2403
2167
|
finally {
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2168
|
+
_commandFinally(state, this);
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
async waitForTextToDisappear(text, options = {}, world = null) {
|
|
2172
|
+
text = unEscapeString(text);
|
|
2173
|
+
const state = {
|
|
2174
|
+
text_search: text,
|
|
2175
|
+
options,
|
|
2176
|
+
world,
|
|
2177
|
+
locate: false,
|
|
2178
|
+
scroll: false,
|
|
2179
|
+
highlight: false,
|
|
2180
|
+
type: Types.WAIT_FOR_TEXT_TO_DISAPPEAR,
|
|
2181
|
+
text: `Verify text does not exist in page`,
|
|
2182
|
+
operation: "verifyTextNotExistInPage",
|
|
2183
|
+
log: "***** verify text " + text + " does not exist in page *****\n",
|
|
2184
|
+
};
|
|
2185
|
+
const timeout = this._getLoadTimeout(options);
|
|
2186
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2187
|
+
const newValue = await this._replaceWithLocalData(text, world);
|
|
2188
|
+
if (newValue !== text) {
|
|
2189
|
+
this.logger.info(text + "=" + newValue);
|
|
2190
|
+
text = newValue;
|
|
2191
|
+
}
|
|
2192
|
+
let dateAlternatives = findDateAlternatives(text);
|
|
2193
|
+
let numberAlternatives = findNumberAlternatives(text);
|
|
2194
|
+
try {
|
|
2195
|
+
await _preCommand(state, this);
|
|
2196
|
+
state.info.text = text;
|
|
2197
|
+
while (true) {
|
|
2198
|
+
const frames = this.page.frames();
|
|
2199
|
+
let results = [];
|
|
2200
|
+
for (let i = 0; i < frames.length; i++) {
|
|
2201
|
+
if (dateAlternatives.date) {
|
|
2202
|
+
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2203
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2204
|
+
result.frame = frames[i];
|
|
2205
|
+
results.push(result);
|
|
2206
|
+
}
|
|
2415
2207
|
}
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2208
|
+
else if (numberAlternatives.number) {
|
|
2209
|
+
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2210
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2211
|
+
result.frame = frames[i];
|
|
2212
|
+
results.push(result);
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
else {
|
|
2216
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2217
|
+
result.frame = frames[i];
|
|
2218
|
+
results.push(result);
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
state.info.results = results;
|
|
2222
|
+
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2223
|
+
if (resultWithElementsFound.length === 0) {
|
|
2224
|
+
await _screenshot(state, this);
|
|
2225
|
+
return state.info;
|
|
2226
|
+
}
|
|
2227
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2228
|
+
throw new Error(`Text ${text} found in page`);
|
|
2229
|
+
}
|
|
2230
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
catch (e) {
|
|
2234
|
+
await _commandError(state, e, this);
|
|
2235
|
+
}
|
|
2236
|
+
finally {
|
|
2237
|
+
_commandFinally(state, this);
|
|
2423
2238
|
}
|
|
2424
2239
|
}
|
|
2425
2240
|
_getServerUrl() {
|
|
@@ -2482,11 +2297,12 @@ class StableBrowser {
|
|
|
2482
2297
|
info.screenshotPath = screenshotPath;
|
|
2483
2298
|
Object.assign(e, { info: info });
|
|
2484
2299
|
error = e;
|
|
2485
|
-
throw e;
|
|
2300
|
+
// throw e;
|
|
2301
|
+
await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
|
|
2486
2302
|
}
|
|
2487
2303
|
finally {
|
|
2488
2304
|
const endTime = Date.now();
|
|
2489
|
-
|
|
2305
|
+
_reportToWorld(world, {
|
|
2490
2306
|
type: Types.VERIFY_VISUAL,
|
|
2491
2307
|
text: "Visual verification",
|
|
2492
2308
|
screenshotId,
|
|
@@ -2495,7 +2311,7 @@ class StableBrowser {
|
|
|
2495
2311
|
status: "FAILED",
|
|
2496
2312
|
startTime,
|
|
2497
2313
|
endTime,
|
|
2498
|
-
message: error
|
|
2314
|
+
message: error?.message,
|
|
2499
2315
|
}
|
|
2500
2316
|
: {
|
|
2501
2317
|
status: "PASSED",
|
|
@@ -2527,7 +2343,7 @@ class StableBrowser {
|
|
|
2527
2343
|
this.logger.info("Table data verified");
|
|
2528
2344
|
}
|
|
2529
2345
|
async getTableData(selectors, _params = null, options = {}, world = null) {
|
|
2530
|
-
|
|
2346
|
+
_validateSelectors(selectors);
|
|
2531
2347
|
const startTime = Date.now();
|
|
2532
2348
|
let error = null;
|
|
2533
2349
|
let screenshotId = null;
|
|
@@ -2549,11 +2365,12 @@ class StableBrowser {
|
|
|
2549
2365
|
info.screenshotPath = screenshotPath;
|
|
2550
2366
|
Object.assign(e, { info: info });
|
|
2551
2367
|
error = e;
|
|
2552
|
-
throw e;
|
|
2368
|
+
// throw e;
|
|
2369
|
+
await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
|
|
2553
2370
|
}
|
|
2554
2371
|
finally {
|
|
2555
2372
|
const endTime = Date.now();
|
|
2556
|
-
|
|
2373
|
+
_reportToWorld(world, {
|
|
2557
2374
|
element_name: selectors.element_name,
|
|
2558
2375
|
type: Types.GET_TABLE_DATA,
|
|
2559
2376
|
text: "Get table data",
|
|
@@ -2563,7 +2380,7 @@ class StableBrowser {
|
|
|
2563
2380
|
status: "FAILED",
|
|
2564
2381
|
startTime,
|
|
2565
2382
|
endTime,
|
|
2566
|
-
message: error
|
|
2383
|
+
message: error?.message,
|
|
2567
2384
|
}
|
|
2568
2385
|
: {
|
|
2569
2386
|
status: "PASSED",
|
|
@@ -2575,7 +2392,7 @@ class StableBrowser {
|
|
|
2575
2392
|
}
|
|
2576
2393
|
}
|
|
2577
2394
|
async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
|
|
2578
|
-
|
|
2395
|
+
_validateSelectors(selectors);
|
|
2579
2396
|
if (!query) {
|
|
2580
2397
|
throw new Error("query is null");
|
|
2581
2398
|
}
|
|
@@ -2714,11 +2531,12 @@ class StableBrowser {
|
|
|
2714
2531
|
info.screenshotPath = screenshotPath;
|
|
2715
2532
|
Object.assign(e, { info: info });
|
|
2716
2533
|
error = e;
|
|
2717
|
-
throw e;
|
|
2534
|
+
// throw e;
|
|
2535
|
+
await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
|
|
2718
2536
|
}
|
|
2719
2537
|
finally {
|
|
2720
2538
|
const endTime = Date.now();
|
|
2721
|
-
|
|
2539
|
+
_reportToWorld(world, {
|
|
2722
2540
|
element_name: selectors.element_name,
|
|
2723
2541
|
type: Types.ANALYZE_TABLE,
|
|
2724
2542
|
text: "Analyze table",
|
|
@@ -2728,7 +2546,7 @@ class StableBrowser {
|
|
|
2728
2546
|
status: "FAILED",
|
|
2729
2547
|
startTime,
|
|
2730
2548
|
endTime,
|
|
2731
|
-
message: error
|
|
2549
|
+
message: error?.message,
|
|
2732
2550
|
}
|
|
2733
2551
|
: {
|
|
2734
2552
|
status: "PASSED",
|
|
@@ -2740,27 +2558,7 @@ class StableBrowser {
|
|
|
2740
2558
|
}
|
|
2741
2559
|
}
|
|
2742
2560
|
async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
|
|
2743
|
-
|
|
2744
|
-
return value;
|
|
2745
|
-
}
|
|
2746
|
-
// find all the accurance of {{(.*?)}} and replace with the value
|
|
2747
|
-
let regex = /{{(.*?)}}/g;
|
|
2748
|
-
let matches = value.match(regex);
|
|
2749
|
-
if (matches) {
|
|
2750
|
-
const testData = this.getTestData(world);
|
|
2751
|
-
for (let i = 0; i < matches.length; i++) {
|
|
2752
|
-
let match = matches[i];
|
|
2753
|
-
let key = match.substring(2, match.length - 2);
|
|
2754
|
-
let newValue = objectPath.get(testData, key, null);
|
|
2755
|
-
if (newValue !== null) {
|
|
2756
|
-
value = value.replace(match, newValue);
|
|
2757
|
-
}
|
|
2758
|
-
}
|
|
2759
|
-
}
|
|
2760
|
-
if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
|
|
2761
|
-
return await decrypt(value, null, totpWait);
|
|
2762
|
-
}
|
|
2763
|
-
return value;
|
|
2561
|
+
return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
|
|
2764
2562
|
}
|
|
2765
2563
|
_getLoadTimeout(options) {
|
|
2766
2564
|
let timeout = 15000;
|
|
@@ -2811,7 +2609,7 @@ class StableBrowser {
|
|
|
2811
2609
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2812
2610
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2813
2611
|
const endTime = Date.now();
|
|
2814
|
-
|
|
2612
|
+
_reportToWorld(world, {
|
|
2815
2613
|
type: Types.GET_PAGE_STATUS,
|
|
2816
2614
|
text: "Wait for page load",
|
|
2817
2615
|
screenshotId,
|
|
@@ -2820,7 +2618,7 @@ class StableBrowser {
|
|
|
2820
2618
|
status: "FAILED",
|
|
2821
2619
|
startTime,
|
|
2822
2620
|
endTime,
|
|
2823
|
-
message: error
|
|
2621
|
+
message: error?.message,
|
|
2824
2622
|
}
|
|
2825
2623
|
: {
|
|
2826
2624
|
status: "PASSED",
|
|
@@ -2831,41 +2629,35 @@ class StableBrowser {
|
|
|
2831
2629
|
}
|
|
2832
2630
|
}
|
|
2833
2631
|
async closePage(options = {}, world = null) {
|
|
2834
|
-
const
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2632
|
+
const state = {
|
|
2633
|
+
options,
|
|
2634
|
+
world,
|
|
2635
|
+
locate: false,
|
|
2636
|
+
scroll: false,
|
|
2637
|
+
highlight: false,
|
|
2638
|
+
type: Types.CLOSE_PAGE,
|
|
2639
|
+
text: `Close page`,
|
|
2640
|
+
operation: "closePage",
|
|
2641
|
+
log: "***** close page *****\n",
|
|
2642
|
+
throwError: false,
|
|
2643
|
+
};
|
|
2839
2644
|
try {
|
|
2645
|
+
await _preCommand(state, this);
|
|
2840
2646
|
await this.page.close();
|
|
2841
2647
|
}
|
|
2842
2648
|
catch (e) {
|
|
2843
2649
|
console.log(".");
|
|
2650
|
+
await _commandError(state, e, this);
|
|
2844
2651
|
}
|
|
2845
2652
|
finally {
|
|
2846
|
-
|
|
2847
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2848
|
-
const endTime = Date.now();
|
|
2849
|
-
this._reportToWorld(world, {
|
|
2850
|
-
type: Types.CLOSE_PAGE,
|
|
2851
|
-
text: "close page",
|
|
2852
|
-
screenshotId,
|
|
2853
|
-
result: error
|
|
2854
|
-
? {
|
|
2855
|
-
status: "FAILED",
|
|
2856
|
-
startTime,
|
|
2857
|
-
endTime,
|
|
2858
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2859
|
-
}
|
|
2860
|
-
: {
|
|
2861
|
-
status: "PASSED",
|
|
2862
|
-
startTime,
|
|
2863
|
-
endTime,
|
|
2864
|
-
},
|
|
2865
|
-
info: info,
|
|
2866
|
-
});
|
|
2653
|
+
_commandFinally(state, this);
|
|
2867
2654
|
}
|
|
2868
2655
|
}
|
|
2656
|
+
saveTestDataAsGlobal(options, world) {
|
|
2657
|
+
const dataFile = this._getDataFile(world);
|
|
2658
|
+
process.env.GLOBAL_TEST_DATA_FILE = dataFile;
|
|
2659
|
+
this.logger.info("Save the scenario test data as global for the following scenarios.");
|
|
2660
|
+
}
|
|
2869
2661
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
2870
2662
|
const startTime = Date.now();
|
|
2871
2663
|
let error = null;
|
|
@@ -2883,12 +2675,13 @@ class StableBrowser {
|
|
|
2883
2675
|
}
|
|
2884
2676
|
catch (e) {
|
|
2885
2677
|
console.log(".");
|
|
2678
|
+
await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
|
|
2886
2679
|
}
|
|
2887
2680
|
finally {
|
|
2888
2681
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2889
2682
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2890
2683
|
const endTime = Date.now();
|
|
2891
|
-
|
|
2684
|
+
_reportToWorld(world, {
|
|
2892
2685
|
type: Types.SET_VIEWPORT,
|
|
2893
2686
|
text: "set viewport size to " + width + "x" + hight,
|
|
2894
2687
|
screenshotId,
|
|
@@ -2897,7 +2690,7 @@ class StableBrowser {
|
|
|
2897
2690
|
status: "FAILED",
|
|
2898
2691
|
startTime,
|
|
2899
2692
|
endTime,
|
|
2900
|
-
message: error
|
|
2693
|
+
message: error?.message,
|
|
2901
2694
|
}
|
|
2902
2695
|
: {
|
|
2903
2696
|
status: "PASSED",
|
|
@@ -2919,12 +2712,13 @@ class StableBrowser {
|
|
|
2919
2712
|
}
|
|
2920
2713
|
catch (e) {
|
|
2921
2714
|
console.log(".");
|
|
2715
|
+
await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
|
|
2922
2716
|
}
|
|
2923
2717
|
finally {
|
|
2924
2718
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2925
2719
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2926
2720
|
const endTime = Date.now();
|
|
2927
|
-
|
|
2721
|
+
_reportToWorld(world, {
|
|
2928
2722
|
type: Types.GET_PAGE_STATUS,
|
|
2929
2723
|
text: "page relaod",
|
|
2930
2724
|
screenshotId,
|
|
@@ -2933,7 +2727,7 @@ class StableBrowser {
|
|
|
2933
2727
|
status: "FAILED",
|
|
2934
2728
|
startTime,
|
|
2935
2729
|
endTime,
|
|
2936
|
-
message: error
|
|
2730
|
+
message: error?.message,
|
|
2937
2731
|
}
|
|
2938
2732
|
: {
|
|
2939
2733
|
status: "PASSED",
|
|
@@ -2960,11 +2754,37 @@ class StableBrowser {
|
|
|
2960
2754
|
console.log("#-#");
|
|
2961
2755
|
}
|
|
2962
2756
|
}
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2757
|
+
async beforeStep(world, step) {
|
|
2758
|
+
this.stepName = step.pickleStep.text;
|
|
2759
|
+
this.logger.info("step: " + this.stepName);
|
|
2760
|
+
if (this.stepIndex === undefined) {
|
|
2761
|
+
this.stepIndex = 0;
|
|
2762
|
+
}
|
|
2763
|
+
else {
|
|
2764
|
+
this.stepIndex++;
|
|
2765
|
+
}
|
|
2766
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2767
|
+
if (this.context.browserObject.context) {
|
|
2768
|
+
await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
if (this.tags === null && step && step.pickle && step.pickle.tags) {
|
|
2772
|
+
this.tags = step.pickle.tags.map((tag) => tag.name);
|
|
2773
|
+
// check if @global_test_data tag is present
|
|
2774
|
+
if (this.tags.includes("@global_test_data")) {
|
|
2775
|
+
this.saveTestDataAsGlobal({}, world);
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
async afterStep(world, step) {
|
|
2780
|
+
this.stepName = null;
|
|
2781
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2782
|
+
if (this.context.browserObject.context) {
|
|
2783
|
+
await this.context.browserObject.context.tracing.stopChunk({
|
|
2784
|
+
path: path.join(this.context.browserObject.traceFolder, `trace-${this.stepIndex}.zip`),
|
|
2785
|
+
});
|
|
2786
|
+
}
|
|
2966
2787
|
}
|
|
2967
|
-
world.attach(JSON.stringify(properties), { mediaType: "application/json" });
|
|
2968
2788
|
}
|
|
2969
2789
|
}
|
|
2970
2790
|
function createTimedPromise(promise, label) {
|