automation_model 1.0.471-dev → 1.0.471-stage
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api.d.ts +42 -1
- package/lib/api.js +221 -47
- package/lib/api.js.map +1 -1
- package/lib/auto_page.js +38 -16
- package/lib/auto_page.js.map +1 -1
- package/lib/browser_manager.d.ts +5 -3
- package/lib/browser_manager.js +65 -18
- package/lib/browser_manager.js.map +1 -1
- package/lib/command_common.d.ts +6 -0
- package/lib/command_common.js +148 -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 +31 -4
- package/lib/init_browser.js.map +1 -1
- package/lib/locate_element.js +5 -3
- package/lib/locate_element.js.map +1 -1
- package/lib/locator.d.ts +36 -0
- package/lib/locator.js +165 -0
- package/lib/locator.js.map +1 -1
- package/lib/network.d.ts +3 -0
- package/lib/network.js +144 -0
- package/lib/network.js.map +1 -0
- package/lib/stable_browser.d.ts +25 -19
- package/lib/stable_browser.js +633 -812
- package/lib/stable_browser.js.map +1 -1
- package/lib/table.d.ts +13 -0
- package/lib/table.js +187 -0
- package/lib/table.js.map +1 -0
- package/lib/test_context.d.ts +1 -0
- package/lib/test_context.js +12 -12
- package/lib/test_context.js.map +1 -1
- package/lib/utils.d.ts +3 -1
- package/lib/utils.js +68 -1
- package/lib/utils.js.map +1 -1
- package/package.json +5 -4
package/lib/stable_browser.js
CHANGED
|
@@ -10,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,16 @@ 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
|
+
}
|
|
660
|
+
let startTime = Date.now();
|
|
602
661
|
let scope = this.page;
|
|
603
|
-
// for the simple click usecase
|
|
604
662
|
if (selectors.frame) {
|
|
605
|
-
|
|
663
|
+
return selectors.frame;
|
|
606
664
|
}
|
|
607
665
|
if (selectors.iframe_src || selectors.frameLocators) {
|
|
608
666
|
const findFrame = async (frame, framescope) => {
|
|
@@ -630,7 +688,6 @@ class StableBrowser {
|
|
|
630
688
|
}
|
|
631
689
|
return framescope;
|
|
632
690
|
};
|
|
633
|
-
info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
|
|
634
691
|
while (true) {
|
|
635
692
|
let frameFound = false;
|
|
636
693
|
if (selectors.nestFrmLoc) {
|
|
@@ -653,7 +710,9 @@ class StableBrowser {
|
|
|
653
710
|
}
|
|
654
711
|
if (!scope) {
|
|
655
712
|
info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
|
|
656
|
-
if (
|
|
713
|
+
if (Date.now() - startTime > timeout) {
|
|
714
|
+
info.failCause.iframeNotFound = true;
|
|
715
|
+
info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
|
|
657
716
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
658
717
|
}
|
|
659
718
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -663,6 +722,31 @@ class StableBrowser {
|
|
|
663
722
|
}
|
|
664
723
|
}
|
|
665
724
|
}
|
|
725
|
+
if (!scope) {
|
|
726
|
+
scope = this.page;
|
|
727
|
+
}
|
|
728
|
+
return scope;
|
|
729
|
+
}
|
|
730
|
+
async _getDocumentBody(selectors, timeout = 30000, info) {
|
|
731
|
+
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
732
|
+
return scope.evaluate(() => {
|
|
733
|
+
var bodyContent = document.body.innerHTML;
|
|
734
|
+
return bodyContent;
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
738
|
+
if (!info) {
|
|
739
|
+
info = {};
|
|
740
|
+
info.failCause = {};
|
|
741
|
+
info.log = "";
|
|
742
|
+
}
|
|
743
|
+
let highPriorityTimeout = 5000;
|
|
744
|
+
let visibleOnlyTimeout = 6000;
|
|
745
|
+
let startTime = Date.now();
|
|
746
|
+
let locatorsCount = 0;
|
|
747
|
+
let lazy_scroll = false;
|
|
748
|
+
//let arrayMode = Array.isArray(selectors);
|
|
749
|
+
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
666
750
|
let selectorsLocators = null;
|
|
667
751
|
selectorsLocators = selectors.locators;
|
|
668
752
|
// group selectors by priority
|
|
@@ -749,14 +833,18 @@ class StableBrowser {
|
|
|
749
833
|
return maxCountElement.locator;
|
|
750
834
|
}
|
|
751
835
|
}
|
|
752
|
-
if (
|
|
836
|
+
if (Date.now() - startTime > timeout) {
|
|
753
837
|
break;
|
|
754
838
|
}
|
|
755
|
-
if (
|
|
839
|
+
if (Date.now() - startTime > highPriorityTimeout) {
|
|
756
840
|
info.log += "high priority timeout, will try all elements" + "\n";
|
|
757
841
|
highPriorityOnly = false;
|
|
842
|
+
if (this.configuration && this.configuration.load_all_lazy === true && !lazy_scroll) {
|
|
843
|
+
lazy_scroll = true;
|
|
844
|
+
await this.scrollPageToLoadLazyElements();
|
|
845
|
+
}
|
|
758
846
|
}
|
|
759
|
-
if (
|
|
847
|
+
if (Date.now() - startTime > visibleOnlyTimeout) {
|
|
760
848
|
info.log += "visible only timeout, will try all elements" + "\n";
|
|
761
849
|
visibleOnly = false;
|
|
762
850
|
}
|
|
@@ -764,6 +852,8 @@ class StableBrowser {
|
|
|
764
852
|
}
|
|
765
853
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
766
854
|
info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
|
|
855
|
+
info.failCause.locatorNotFound = true;
|
|
856
|
+
info.failCause.lastError = "failed to locate unique element";
|
|
767
857
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
768
858
|
}
|
|
769
859
|
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
@@ -795,6 +885,9 @@ class StableBrowser {
|
|
|
795
885
|
});
|
|
796
886
|
result.locatorIndex = i;
|
|
797
887
|
}
|
|
888
|
+
if (foundLocators.length > 1) {
|
|
889
|
+
info.failCause.foundMultiple = true;
|
|
890
|
+
}
|
|
798
891
|
}
|
|
799
892
|
return result;
|
|
800
893
|
}
|
|
@@ -807,12 +900,12 @@ class StableBrowser {
|
|
|
807
900
|
while (true) {
|
|
808
901
|
try {
|
|
809
902
|
const result = await locate_element(this.context, elementDescription, "click");
|
|
810
|
-
if (
|
|
903
|
+
if (result?.elementNumber >= 0) {
|
|
811
904
|
const selectors = {
|
|
812
|
-
frame: result
|
|
905
|
+
frame: result?.frame,
|
|
813
906
|
locators: [
|
|
814
907
|
{
|
|
815
|
-
css: result
|
|
908
|
+
css: result?.css,
|
|
816
909
|
},
|
|
817
910
|
],
|
|
818
911
|
};
|
|
@@ -821,8 +914,9 @@ class StableBrowser {
|
|
|
821
914
|
}
|
|
822
915
|
}
|
|
823
916
|
catch (e) {
|
|
824
|
-
if (
|
|
825
|
-
throw e;
|
|
917
|
+
if (Date.now() - startTime > timeout) {
|
|
918
|
+
// throw e;
|
|
919
|
+
await _commandError({ text: "simpleClick", operation: "simpleClick", elementDescription, info: {} }, e, this);
|
|
826
920
|
}
|
|
827
921
|
}
|
|
828
922
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
@@ -837,12 +931,12 @@ class StableBrowser {
|
|
|
837
931
|
while (true) {
|
|
838
932
|
try {
|
|
839
933
|
const result = await locate_element(this.context, elementDescription, "fill", value);
|
|
840
|
-
if (
|
|
934
|
+
if (result?.elementNumber >= 0) {
|
|
841
935
|
const selectors = {
|
|
842
|
-
frame: result
|
|
936
|
+
frame: result?.frame,
|
|
843
937
|
locators: [
|
|
844
938
|
{
|
|
845
|
-
css: result
|
|
939
|
+
css: result?.css,
|
|
846
940
|
},
|
|
847
941
|
],
|
|
848
942
|
};
|
|
@@ -851,93 +945,69 @@ class StableBrowser {
|
|
|
851
945
|
}
|
|
852
946
|
}
|
|
853
947
|
catch (e) {
|
|
854
|
-
if (
|
|
855
|
-
throw e;
|
|
948
|
+
if (Date.now() - startTime > timeout) {
|
|
949
|
+
// throw e;
|
|
950
|
+
await _commandError({ text: "simpleClickType", operation: "simpleClickType", value, elementDescription, info: {} }, e, this);
|
|
856
951
|
}
|
|
857
952
|
}
|
|
858
953
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
859
954
|
}
|
|
860
955
|
}
|
|
861
956
|
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;
|
|
957
|
+
const state = {
|
|
958
|
+
selectors,
|
|
959
|
+
_params,
|
|
960
|
+
options,
|
|
961
|
+
world,
|
|
962
|
+
text: "Click element",
|
|
963
|
+
type: Types.CLICK,
|
|
964
|
+
operation: "click",
|
|
965
|
+
log: "***** click on " + selectors.element_name + " *****\n",
|
|
966
|
+
};
|
|
874
967
|
try {
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
968
|
+
await _preCommand(state, this);
|
|
969
|
+
if (state.options && state.options.context) {
|
|
970
|
+
state.selectors.locators[0].text = state.options.context;
|
|
971
|
+
}
|
|
878
972
|
try {
|
|
879
|
-
await
|
|
880
|
-
await
|
|
881
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
973
|
+
await state.element.click();
|
|
974
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
882
975
|
}
|
|
883
976
|
catch (e) {
|
|
884
977
|
// await this.closeUnexpectedPopups();
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
await
|
|
888
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
978
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
979
|
+
await state.element.dispatchEvent("click");
|
|
980
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
889
981
|
}
|
|
890
982
|
await this.waitForPageLoad();
|
|
891
|
-
return info;
|
|
983
|
+
return state.info;
|
|
892
984
|
}
|
|
893
985
|
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;
|
|
986
|
+
await _commandError(state, e, this);
|
|
900
987
|
}
|
|
901
988
|
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
|
-
});
|
|
989
|
+
_commandFinally(state, this);
|
|
922
990
|
}
|
|
923
991
|
}
|
|
924
992
|
async setCheck(selectors, checked = true, _params, options = {}, world = null) {
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
993
|
+
const state = {
|
|
994
|
+
selectors,
|
|
995
|
+
_params,
|
|
996
|
+
options,
|
|
997
|
+
world,
|
|
998
|
+
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
999
|
+
text: checked ? `Check element` : `Uncheck element`,
|
|
1000
|
+
operation: "setCheck",
|
|
1001
|
+
log: "***** check " + selectors.element_name + " *****\n",
|
|
1002
|
+
};
|
|
935
1003
|
try {
|
|
936
|
-
|
|
937
|
-
|
|
1004
|
+
await _preCommand(state, this);
|
|
1005
|
+
state.info.checked = checked;
|
|
1006
|
+
// let element = await this._locate(selectors, info, _params);
|
|
1007
|
+
// ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
938
1008
|
try {
|
|
939
|
-
await this._highlightElements(element);
|
|
940
|
-
await element.setChecked(checked);
|
|
1009
|
+
// await this._highlightElements(element);
|
|
1010
|
+
await state.element.setChecked(checked);
|
|
941
1011
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
942
1012
|
}
|
|
943
1013
|
catch (e) {
|
|
@@ -946,179 +1016,108 @@ class StableBrowser {
|
|
|
946
1016
|
}
|
|
947
1017
|
else {
|
|
948
1018
|
//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 });
|
|
1019
|
+
state.info.log += "setCheck failed, will try again" + "\n";
|
|
1020
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
1021
|
+
await state.element.setChecked(checked, { timeout: 5000, force: true });
|
|
952
1022
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
953
1023
|
}
|
|
954
1024
|
}
|
|
955
1025
|
await this.waitForPageLoad();
|
|
956
|
-
return info;
|
|
1026
|
+
return state.info;
|
|
957
1027
|
}
|
|
958
1028
|
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;
|
|
1029
|
+
await _commandError(state, e, this);
|
|
965
1030
|
}
|
|
966
1031
|
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
|
-
});
|
|
1032
|
+
_commandFinally(state, this);
|
|
987
1033
|
}
|
|
988
1034
|
}
|
|
989
1035
|
async hover(selectors, _params, options = {}, world = null) {
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1036
|
+
const state = {
|
|
1037
|
+
selectors,
|
|
1038
|
+
_params,
|
|
1039
|
+
options,
|
|
1040
|
+
world,
|
|
1041
|
+
type: Types.HOVER,
|
|
1042
|
+
text: `Hover element`,
|
|
1043
|
+
operation: "hover",
|
|
1044
|
+
log: "***** hover " + selectors.element_name + " *****\n",
|
|
1045
|
+
};
|
|
999
1046
|
try {
|
|
1000
|
-
|
|
1001
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1047
|
+
await _preCommand(state, this);
|
|
1002
1048
|
try {
|
|
1003
|
-
await
|
|
1004
|
-
await element.hover();
|
|
1049
|
+
await state.element.hover();
|
|
1005
1050
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1006
1051
|
}
|
|
1007
1052
|
catch (e) {
|
|
1008
1053
|
//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 });
|
|
1054
|
+
state.info.log += "hover failed, will try again" + "\n";
|
|
1055
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
1056
|
+
await state.element.hover({ timeout: 10000 });
|
|
1012
1057
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1013
1058
|
}
|
|
1014
1059
|
await this.waitForPageLoad();
|
|
1015
|
-
return info;
|
|
1060
|
+
return state.info;
|
|
1016
1061
|
}
|
|
1017
1062
|
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;
|
|
1063
|
+
await _commandError(state, e, this);
|
|
1024
1064
|
}
|
|
1025
1065
|
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
|
-
});
|
|
1066
|
+
_commandFinally(state, this);
|
|
1046
1067
|
}
|
|
1047
1068
|
}
|
|
1048
1069
|
async selectOption(selectors, values, _params = null, options = {}, world = null) {
|
|
1049
|
-
this._validateSelectors(selectors);
|
|
1050
1070
|
if (!values) {
|
|
1051
1071
|
throw new Error("values is null");
|
|
1052
1072
|
}
|
|
1053
|
-
const
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1073
|
+
const state = {
|
|
1074
|
+
selectors,
|
|
1075
|
+
_params,
|
|
1076
|
+
options,
|
|
1077
|
+
world,
|
|
1078
|
+
value: values.toString(),
|
|
1079
|
+
type: Types.SELECT,
|
|
1080
|
+
text: `Select option: ${values}`,
|
|
1081
|
+
operation: "selectOption",
|
|
1082
|
+
log: "***** select option " + selectors.element_name + " *****\n",
|
|
1083
|
+
};
|
|
1061
1084
|
try {
|
|
1062
|
-
|
|
1063
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1085
|
+
await _preCommand(state, this);
|
|
1064
1086
|
try {
|
|
1065
|
-
await
|
|
1066
|
-
await element.selectOption(values);
|
|
1087
|
+
await state.element.selectOption(values);
|
|
1067
1088
|
}
|
|
1068
1089
|
catch (e) {
|
|
1069
1090
|
//await this.closeUnexpectedPopups();
|
|
1070
|
-
info.log += "selectOption failed, will try force" + "\n";
|
|
1071
|
-
await element.selectOption(values, { timeout: 10000, force: true });
|
|
1091
|
+
state.info.log += "selectOption failed, will try force" + "\n";
|
|
1092
|
+
await state.element.selectOption(values, { timeout: 10000, force: true });
|
|
1072
1093
|
}
|
|
1073
1094
|
await this.waitForPageLoad();
|
|
1074
|
-
return info;
|
|
1095
|
+
return state.info;
|
|
1075
1096
|
}
|
|
1076
1097
|
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;
|
|
1098
|
+
await _commandError(state, e, this);
|
|
1084
1099
|
}
|
|
1085
1100
|
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
|
-
});
|
|
1101
|
+
_commandFinally(state, this);
|
|
1107
1102
|
}
|
|
1108
1103
|
}
|
|
1109
1104
|
async type(_value, _params = null, options = {}, world = null) {
|
|
1110
|
-
const
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1105
|
+
const state = {
|
|
1106
|
+
value: _value,
|
|
1107
|
+
_params,
|
|
1108
|
+
options,
|
|
1109
|
+
world,
|
|
1110
|
+
locate: false,
|
|
1111
|
+
scroll: false,
|
|
1112
|
+
highlight: false,
|
|
1113
|
+
type: Types.TYPE_PRESS,
|
|
1114
|
+
text: `Type value: ${_value}`,
|
|
1115
|
+
operation: "type",
|
|
1116
|
+
log: "",
|
|
1117
|
+
};
|
|
1119
1118
|
try {
|
|
1120
|
-
|
|
1121
|
-
const valueSegment =
|
|
1119
|
+
await _preCommand(state, this);
|
|
1120
|
+
const valueSegment = state.value.split("&&");
|
|
1122
1121
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1123
1122
|
if (i > 0) {
|
|
1124
1123
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1138,134 +1137,76 @@ class StableBrowser {
|
|
|
1138
1137
|
await this.page.keyboard.type(value);
|
|
1139
1138
|
}
|
|
1140
1139
|
}
|
|
1141
|
-
return info;
|
|
1140
|
+
return state.info;
|
|
1142
1141
|
}
|
|
1143
1142
|
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;
|
|
1143
|
+
await _commandError(state, e, this);
|
|
1151
1144
|
}
|
|
1152
1145
|
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
|
-
});
|
|
1146
|
+
_commandFinally(state, this);
|
|
1173
1147
|
}
|
|
1174
1148
|
}
|
|
1175
1149
|
async setInputValue(selectors, value, _params = null, options = {}, world = null) {
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
let screenshotPath = null;
|
|
1150
|
+
const state = {
|
|
1151
|
+
selectors,
|
|
1152
|
+
_params,
|
|
1153
|
+
value,
|
|
1154
|
+
options,
|
|
1155
|
+
world,
|
|
1156
|
+
type: Types.SET_INPUT,
|
|
1157
|
+
text: `Set input value`,
|
|
1158
|
+
operation: "setInputValue",
|
|
1159
|
+
log: "***** set input value " + selectors.element_name + " *****\n",
|
|
1160
|
+
};
|
|
1188
1161
|
try {
|
|
1189
|
-
|
|
1190
|
-
let
|
|
1191
|
-
await this.scrollIfNeeded(element, info);
|
|
1192
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1193
|
-
await this._highlightElements(element);
|
|
1162
|
+
await _preCommand(state, this);
|
|
1163
|
+
let value = await this._replaceWithLocalData(state.value, this);
|
|
1194
1164
|
try {
|
|
1195
|
-
await element.evaluateHandle((el, value) => {
|
|
1165
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1196
1166
|
el.value = value;
|
|
1197
1167
|
}, value);
|
|
1198
1168
|
}
|
|
1199
1169
|
catch (error) {
|
|
1200
1170
|
this.logger.error("setInputValue failed, will try again");
|
|
1201
|
-
|
|
1202
|
-
info.
|
|
1203
|
-
|
|
1204
|
-
await element.evaluateHandle((el, value) => {
|
|
1171
|
+
await _screenshot(state, this);
|
|
1172
|
+
Object.assign(error, { info: state.info });
|
|
1173
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1205
1174
|
el.value = value;
|
|
1206
1175
|
});
|
|
1207
1176
|
}
|
|
1208
1177
|
}
|
|
1209
1178
|
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;
|
|
1179
|
+
await _commandError(state, e, this);
|
|
1216
1180
|
}
|
|
1217
1181
|
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
|
-
});
|
|
1182
|
+
_commandFinally(state, this);
|
|
1239
1183
|
}
|
|
1240
1184
|
}
|
|
1241
1185
|
async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1186
|
+
const state = {
|
|
1187
|
+
selectors,
|
|
1188
|
+
_params,
|
|
1189
|
+
value: await this._replaceWithLocalData(value, this),
|
|
1190
|
+
options,
|
|
1191
|
+
world,
|
|
1192
|
+
type: Types.SET_DATE_TIME,
|
|
1193
|
+
text: `Set date time value: ${value}`,
|
|
1194
|
+
operation: "setDateTime",
|
|
1195
|
+
log: "***** set date time value " + selectors.element_name + " *****\n",
|
|
1196
|
+
throwError: false,
|
|
1197
|
+
};
|
|
1252
1198
|
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);
|
|
1199
|
+
await _preCommand(state, this);
|
|
1259
1200
|
try {
|
|
1260
|
-
await element.click();
|
|
1201
|
+
await state.element.click();
|
|
1261
1202
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1262
1203
|
if (format) {
|
|
1263
|
-
value = dayjs(value).format(format);
|
|
1264
|
-
await element.fill(value);
|
|
1204
|
+
state.value = dayjs(state.value).format(format);
|
|
1205
|
+
await state.element.fill(state.value);
|
|
1265
1206
|
}
|
|
1266
1207
|
else {
|
|
1267
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1268
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1208
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1209
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1269
1210
|
el.value = ""; // clear input
|
|
1270
1211
|
el.value = dateTimeValue;
|
|
1271
1212
|
}, dateTimeValue);
|
|
@@ -1278,20 +1219,19 @@ class StableBrowser {
|
|
|
1278
1219
|
}
|
|
1279
1220
|
catch (err) {
|
|
1280
1221
|
//await this.closeUnexpectedPopups();
|
|
1281
|
-
this.logger.error("setting date time input failed " + JSON.stringify(info));
|
|
1222
|
+
this.logger.error("setting date time input failed " + JSON.stringify(state.info));
|
|
1282
1223
|
this.logger.info("Trying again");
|
|
1283
|
-
|
|
1284
|
-
info.
|
|
1285
|
-
Object.assign(err, { info: info });
|
|
1224
|
+
await _screenshot(state, this);
|
|
1225
|
+
Object.assign(err, { info: state.info });
|
|
1286
1226
|
await element.click();
|
|
1287
1227
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1288
1228
|
if (format) {
|
|
1289
|
-
value = dayjs(value).format(format);
|
|
1290
|
-
await element.fill(value);
|
|
1229
|
+
state.value = dayjs(state.value).format(format);
|
|
1230
|
+
await state.element.fill(state.value);
|
|
1291
1231
|
}
|
|
1292
1232
|
else {
|
|
1293
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1294
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1233
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1234
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1295
1235
|
el.value = ""; // clear input
|
|
1296
1236
|
el.value = dateTimeValue;
|
|
1297
1237
|
}, dateTimeValue);
|
|
@@ -1304,60 +1244,39 @@ class StableBrowser {
|
|
|
1304
1244
|
}
|
|
1305
1245
|
}
|
|
1306
1246
|
catch (e) {
|
|
1307
|
-
|
|
1308
|
-
throw e;
|
|
1247
|
+
await _commandError(state, e, this);
|
|
1309
1248
|
}
|
|
1310
1249
|
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
|
-
});
|
|
1250
|
+
_commandFinally(state, this);
|
|
1332
1251
|
}
|
|
1333
1252
|
}
|
|
1334
1253
|
async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
|
|
1335
1254
|
_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
1255
|
const newValue = await this._replaceWithLocalData(_value, world);
|
|
1256
|
+
const state = {
|
|
1257
|
+
selectors,
|
|
1258
|
+
_params,
|
|
1259
|
+
value: newValue,
|
|
1260
|
+
originalValue: _value,
|
|
1261
|
+
options,
|
|
1262
|
+
world,
|
|
1263
|
+
type: Types.FILL,
|
|
1264
|
+
text: `Click type input with value: ${_value}`,
|
|
1265
|
+
operation: "clickType",
|
|
1266
|
+
log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
|
|
1267
|
+
};
|
|
1346
1268
|
if (newValue !== _value) {
|
|
1347
1269
|
//this.logger.info(_value + "=" + newValue);
|
|
1348
1270
|
_value = newValue;
|
|
1349
1271
|
}
|
|
1350
|
-
info.value = _value;
|
|
1351
1272
|
try {
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
await this.scrollIfNeeded(element, info);
|
|
1355
|
-
await this._highlightElements(element);
|
|
1273
|
+
await _preCommand(state, this);
|
|
1274
|
+
state.info.value = _value;
|
|
1356
1275
|
if (options === null || options === undefined || !options.press) {
|
|
1357
1276
|
try {
|
|
1358
|
-
let currentValue = await element.inputValue();
|
|
1277
|
+
let currentValue = await state.element.inputValue();
|
|
1359
1278
|
if (currentValue) {
|
|
1360
|
-
await element.fill("");
|
|
1279
|
+
await state.element.fill("");
|
|
1361
1280
|
}
|
|
1362
1281
|
}
|
|
1363
1282
|
catch (e) {
|
|
@@ -1366,22 +1285,22 @@ class StableBrowser {
|
|
|
1366
1285
|
}
|
|
1367
1286
|
if (options === null || options === undefined || options.press) {
|
|
1368
1287
|
try {
|
|
1369
|
-
await element.click({ timeout: 5000 });
|
|
1288
|
+
await state.element.click({ timeout: 5000 });
|
|
1370
1289
|
}
|
|
1371
1290
|
catch (e) {
|
|
1372
|
-
await element.dispatchEvent("click");
|
|
1291
|
+
await state.element.dispatchEvent("click");
|
|
1373
1292
|
}
|
|
1374
1293
|
}
|
|
1375
1294
|
else {
|
|
1376
1295
|
try {
|
|
1377
|
-
await element.focus();
|
|
1296
|
+
await state.element.focus();
|
|
1378
1297
|
}
|
|
1379
1298
|
catch (e) {
|
|
1380
|
-
await element.dispatchEvent("focus");
|
|
1299
|
+
await state.element.dispatchEvent("focus");
|
|
1381
1300
|
}
|
|
1382
1301
|
}
|
|
1383
1302
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1384
|
-
const valueSegment =
|
|
1303
|
+
const valueSegment = state.value.split("&&");
|
|
1385
1304
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1386
1305
|
if (i > 0) {
|
|
1387
1306
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1401,14 +1320,14 @@ class StableBrowser {
|
|
|
1401
1320
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1402
1321
|
}
|
|
1403
1322
|
}
|
|
1404
|
-
|
|
1323
|
+
await _screenshot(state, this);
|
|
1405
1324
|
if (enter === true) {
|
|
1406
1325
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1407
1326
|
await this.page.keyboard.press("Enter");
|
|
1408
1327
|
await this.waitForPageLoad();
|
|
1409
1328
|
}
|
|
1410
1329
|
else if (enter === false) {
|
|
1411
|
-
await element.dispatchEvent("change");
|
|
1330
|
+
await state.element.dispatchEvent("change");
|
|
1412
1331
|
//await this.page.keyboard.press("Tab");
|
|
1413
1332
|
}
|
|
1414
1333
|
else {
|
|
@@ -1417,104 +1336,50 @@ class StableBrowser {
|
|
|
1417
1336
|
await this.waitForPageLoad();
|
|
1418
1337
|
}
|
|
1419
1338
|
}
|
|
1420
|
-
return info;
|
|
1421
|
-
}
|
|
1422
|
-
catch (e) {
|
|
1423
|
-
//await this.closeUnexpectedPopups();
|
|
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;
|
|
1430
|
-
}
|
|
1431
|
-
finally {
|
|
1432
|
-
const endTime = Date.now();
|
|
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
|
-
});
|
|
1453
|
-
}
|
|
1454
|
-
}
|
|
1455
|
-
async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
|
|
1456
|
-
this._validateSelectors(selectors);
|
|
1457
|
-
value = unEscapeString(value);
|
|
1458
|
-
const startTime = Date.now();
|
|
1459
|
-
let error = null;
|
|
1460
|
-
let screenshotId = null;
|
|
1461
|
-
let screenshotPath = null;
|
|
1462
|
-
const info = {};
|
|
1463
|
-
info.log = "***** fill on " + selectors.element_name + " with value " + value + "*****\n";
|
|
1464
|
-
info.operation = "fill";
|
|
1465
|
-
info.selectors = selectors;
|
|
1466
|
-
info.value = value;
|
|
1467
|
-
try {
|
|
1468
|
-
let element = await this._locate(selectors, info, _params);
|
|
1469
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1470
|
-
await this._highlightElements(element);
|
|
1471
|
-
await element.fill(value);
|
|
1472
|
-
await element.dispatchEvent("change");
|
|
1473
|
-
if (enter) {
|
|
1474
|
-
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1475
|
-
await this.page.keyboard.press("Enter");
|
|
1476
|
-
}
|
|
1477
|
-
await this.waitForPageLoad();
|
|
1478
|
-
return info;
|
|
1339
|
+
return state.info;
|
|
1479
1340
|
}
|
|
1480
1341
|
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;
|
|
1342
|
+
await _commandError(state, e, this);
|
|
1488
1343
|
}
|
|
1489
|
-
finally {
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1344
|
+
finally {
|
|
1345
|
+
_commandFinally(state, this);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
|
|
1349
|
+
const state = {
|
|
1350
|
+
selectors,
|
|
1351
|
+
_params,
|
|
1352
|
+
value: unEscapeString(value),
|
|
1353
|
+
options,
|
|
1354
|
+
world,
|
|
1355
|
+
type: Types.FILL,
|
|
1356
|
+
text: `Fill input with value: ${value}`,
|
|
1357
|
+
operation: "fill",
|
|
1358
|
+
log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
|
|
1359
|
+
};
|
|
1360
|
+
try {
|
|
1361
|
+
await _preCommand(state, this);
|
|
1362
|
+
await state.element.fill(value);
|
|
1363
|
+
await state.element.dispatchEvent("change");
|
|
1364
|
+
if (enter) {
|
|
1365
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1366
|
+
await this.page.keyboard.press("Enter");
|
|
1367
|
+
}
|
|
1368
|
+
await this.waitForPageLoad();
|
|
1369
|
+
return state.info;
|
|
1370
|
+
}
|
|
1371
|
+
catch (e) {
|
|
1372
|
+
await _commandError(state, e, this);
|
|
1373
|
+
}
|
|
1374
|
+
finally {
|
|
1375
|
+
_commandFinally(state, this);
|
|
1511
1376
|
}
|
|
1512
1377
|
}
|
|
1513
1378
|
async getText(selectors, _params = null, options = {}, info = {}, world = null) {
|
|
1514
1379
|
return await this._getText(selectors, 0, _params, options, info, world);
|
|
1515
1380
|
}
|
|
1516
1381
|
async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
|
|
1517
|
-
|
|
1382
|
+
_validateSelectors(selectors);
|
|
1518
1383
|
let screenshotId = null;
|
|
1519
1384
|
let screenshotPath = null;
|
|
1520
1385
|
if (!info.log) {
|
|
@@ -1558,166 +1423,124 @@ class StableBrowser {
|
|
|
1558
1423
|
}
|
|
1559
1424
|
}
|
|
1560
1425
|
async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
|
|
1561
|
-
var _a;
|
|
1562
|
-
this._validateSelectors(selectors);
|
|
1563
1426
|
if (!pattern) {
|
|
1564
1427
|
throw new Error("pattern is null");
|
|
1565
1428
|
}
|
|
1566
1429
|
if (!text) {
|
|
1567
1430
|
throw new Error("text is null");
|
|
1568
1431
|
}
|
|
1432
|
+
const state = {
|
|
1433
|
+
selectors,
|
|
1434
|
+
_params,
|
|
1435
|
+
pattern,
|
|
1436
|
+
value: pattern,
|
|
1437
|
+
options,
|
|
1438
|
+
world,
|
|
1439
|
+
locate: false,
|
|
1440
|
+
scroll: false,
|
|
1441
|
+
screenshot: false,
|
|
1442
|
+
highlight: false,
|
|
1443
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1444
|
+
text: `Verify element contains pattern: ${pattern}`,
|
|
1445
|
+
operation: "containsPattern",
|
|
1446
|
+
log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
|
|
1447
|
+
};
|
|
1569
1448
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1570
1449
|
if (newValue !== text) {
|
|
1571
1450
|
this.logger.info(text + "=" + newValue);
|
|
1572
1451
|
text = newValue;
|
|
1573
1452
|
}
|
|
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
1453
|
let foundObj = null;
|
|
1586
1454
|
try {
|
|
1587
|
-
|
|
1455
|
+
await _preCommand(state, this);
|
|
1456
|
+
state.info.pattern = pattern;
|
|
1457
|
+
foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
|
|
1588
1458
|
if (foundObj && foundObj.element) {
|
|
1589
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1459
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1590
1460
|
}
|
|
1591
|
-
|
|
1461
|
+
await _screenshot(state, this);
|
|
1592
1462
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1593
1463
|
pattern = pattern.replace("{text}", escapedText);
|
|
1594
1464
|
let regex = new RegExp(pattern, "im");
|
|
1595
|
-
if (!regex.test(foundObj
|
|
1596
|
-
info.foundText = foundObj
|
|
1465
|
+
if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
|
|
1466
|
+
state.info.foundText = foundObj?.text;
|
|
1597
1467
|
throw new Error("element doesn't contain text " + text);
|
|
1598
1468
|
}
|
|
1599
|
-
return info;
|
|
1469
|
+
return state.info;
|
|
1600
1470
|
}
|
|
1601
1471
|
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;
|
|
1472
|
+
this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
|
|
1473
|
+
await _commandError(state, e, this);
|
|
1610
1474
|
}
|
|
1611
1475
|
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
|
-
});
|
|
1476
|
+
_commandFinally(state, this);
|
|
1633
1477
|
}
|
|
1634
1478
|
}
|
|
1635
1479
|
async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1480
|
+
const state = {
|
|
1481
|
+
selectors,
|
|
1482
|
+
_params,
|
|
1483
|
+
value: text,
|
|
1484
|
+
options,
|
|
1485
|
+
world,
|
|
1486
|
+
locate: false,
|
|
1487
|
+
scroll: false,
|
|
1488
|
+
screenshot: false,
|
|
1489
|
+
highlight: false,
|
|
1490
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1491
|
+
text: `Verify element contains text: ${text}`,
|
|
1492
|
+
operation: "containsText",
|
|
1493
|
+
log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
|
|
1494
|
+
};
|
|
1639
1495
|
if (!text) {
|
|
1640
1496
|
throw new Error("text is null");
|
|
1641
1497
|
}
|
|
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;
|
|
1498
|
+
text = unEscapeString(text);
|
|
1650
1499
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1651
1500
|
if (newValue !== text) {
|
|
1652
1501
|
this.logger.info(text + "=" + newValue);
|
|
1653
1502
|
text = newValue;
|
|
1654
1503
|
}
|
|
1655
|
-
info.value = text;
|
|
1656
1504
|
let foundObj = null;
|
|
1657
1505
|
try {
|
|
1658
|
-
|
|
1506
|
+
await _preCommand(state, this);
|
|
1507
|
+
foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
|
|
1659
1508
|
if (foundObj && foundObj.element) {
|
|
1660
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1509
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1661
1510
|
}
|
|
1662
|
-
|
|
1511
|
+
await _screenshot(state, this);
|
|
1663
1512
|
const dateAlternatives = findDateAlternatives(text);
|
|
1664
1513
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1665
1514
|
if (dateAlternatives.date) {
|
|
1666
1515
|
for (let i = 0; i < dateAlternatives.dates.length; i++) {
|
|
1667
|
-
if (
|
|
1668
|
-
|
|
1669
|
-
return info;
|
|
1516
|
+
if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
|
|
1517
|
+
foundObj?.value?.includes(dateAlternatives.dates[i])) {
|
|
1518
|
+
return state.info;
|
|
1670
1519
|
}
|
|
1671
1520
|
}
|
|
1672
1521
|
throw new Error("element doesn't contain text " + text);
|
|
1673
1522
|
}
|
|
1674
1523
|
else if (numberAlternatives.number) {
|
|
1675
1524
|
for (let i = 0; i < numberAlternatives.numbers.length; i++) {
|
|
1676
|
-
if (
|
|
1677
|
-
|
|
1678
|
-
return info;
|
|
1525
|
+
if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
|
|
1526
|
+
foundObj?.value?.includes(numberAlternatives.numbers[i])) {
|
|
1527
|
+
return state.info;
|
|
1679
1528
|
}
|
|
1680
1529
|
}
|
|
1681
1530
|
throw new Error("element doesn't contain text " + text);
|
|
1682
1531
|
}
|
|
1683
|
-
else if (!
|
|
1684
|
-
info.foundText = foundObj
|
|
1685
|
-
info.value = foundObj
|
|
1532
|
+
else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
|
|
1533
|
+
state.info.foundText = foundObj?.text;
|
|
1534
|
+
state.info.value = foundObj?.value;
|
|
1686
1535
|
throw new Error("element doesn't contain text " + text);
|
|
1687
1536
|
}
|
|
1688
|
-
return info;
|
|
1537
|
+
return state.info;
|
|
1689
1538
|
}
|
|
1690
1539
|
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;
|
|
1540
|
+
await _commandError(state, e, this);
|
|
1698
1541
|
}
|
|
1699
1542
|
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
|
-
});
|
|
1543
|
+
_commandFinally(state, this);
|
|
1721
1544
|
}
|
|
1722
1545
|
}
|
|
1723
1546
|
_getDataFile(world = null) {
|
|
@@ -1899,11 +1722,9 @@ class StableBrowser {
|
|
|
1899
1722
|
if (!fs.existsSync(world.screenshotPath)) {
|
|
1900
1723
|
fs.mkdirSync(world.screenshotPath, { recursive: true });
|
|
1901
1724
|
}
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
}
|
|
1906
|
-
const screenshotPath = path.join(world.screenshotPath, nextIndex + ".png");
|
|
1725
|
+
// to make sure the path doesn't start with -
|
|
1726
|
+
const uuidStr = "id_" + randomUUID();
|
|
1727
|
+
const screenshotPath = path.join(world.screenshotPath, uuidStr + ".png");
|
|
1907
1728
|
try {
|
|
1908
1729
|
await this.takeScreenshot(screenshotPath);
|
|
1909
1730
|
// let buffer = await this.page.screenshot({ timeout: 4000 });
|
|
@@ -1917,7 +1738,7 @@ class StableBrowser {
|
|
|
1917
1738
|
catch (e) {
|
|
1918
1739
|
this.logger.info("unable to take screenshot, ignored");
|
|
1919
1740
|
}
|
|
1920
|
-
result.screenshotId =
|
|
1741
|
+
result.screenshotId = uuidStr;
|
|
1921
1742
|
result.screenshotPath = screenshotPath;
|
|
1922
1743
|
if (info && info.box) {
|
|
1923
1744
|
await drawRectangle(screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
|
|
@@ -1991,127 +1812,69 @@ class StableBrowser {
|
|
|
1991
1812
|
}
|
|
1992
1813
|
}
|
|
1993
1814
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1815
|
+
const state = {
|
|
1816
|
+
selectors,
|
|
1817
|
+
_params,
|
|
1818
|
+
options,
|
|
1819
|
+
world,
|
|
1820
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1821
|
+
text: `Verify element exists in page`,
|
|
1822
|
+
operation: "verifyElementExistInPage",
|
|
1823
|
+
log: "***** verify element " + selectors.element_name + " exists in page *****\n",
|
|
1824
|
+
};
|
|
1999
1825
|
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
1826
|
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;
|
|
1827
|
+
await _preCommand(state, this);
|
|
1828
|
+
await expect(state.element).toHaveCount(1, { timeout: 10000 });
|
|
1829
|
+
return state.info;
|
|
2013
1830
|
}
|
|
2014
1831
|
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;
|
|
1832
|
+
await _commandError(state, e, this);
|
|
2022
1833
|
}
|
|
2023
1834
|
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
|
-
});
|
|
1835
|
+
_commandFinally(state, this);
|
|
2044
1836
|
}
|
|
2045
1837
|
}
|
|
2046
1838
|
async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
1839
|
+
const state = {
|
|
1840
|
+
selectors,
|
|
1841
|
+
_params,
|
|
1842
|
+
attribute,
|
|
1843
|
+
variable,
|
|
1844
|
+
options,
|
|
1845
|
+
world,
|
|
1846
|
+
type: Types.EXTRACT,
|
|
1847
|
+
text: `Extract attribute from element`,
|
|
1848
|
+
operation: "extractAttribute",
|
|
1849
|
+
log: "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1850
|
+
};
|
|
2052
1851
|
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
1852
|
try {
|
|
2058
|
-
|
|
2059
|
-
await this._highlightElements(element);
|
|
2060
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1853
|
+
await _preCommand(state, this);
|
|
2061
1854
|
switch (attribute) {
|
|
2062
1855
|
case "inner_text":
|
|
2063
|
-
|
|
1856
|
+
state.value = await state.element.innerText();
|
|
2064
1857
|
break;
|
|
2065
1858
|
case "href":
|
|
2066
|
-
|
|
1859
|
+
state.value = await state.element.getAttribute("href");
|
|
2067
1860
|
break;
|
|
2068
1861
|
case "value":
|
|
2069
|
-
|
|
1862
|
+
state.value = await state.element.inputValue();
|
|
2070
1863
|
break;
|
|
2071
1864
|
default:
|
|
2072
|
-
|
|
1865
|
+
state.value = await state.element.getAttribute(attribute);
|
|
2073
1866
|
break;
|
|
2074
1867
|
}
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
this.setTestData({ [variable]: info.value }, world);
|
|
2080
|
-
this.logger.info("set test data: " + variable + "=" + info.value);
|
|
2081
|
-
return info;
|
|
1868
|
+
state.info.value = state.value;
|
|
1869
|
+
this.setTestData({ [variable]: state.value }, world);
|
|
1870
|
+
this.logger.info("set test data: " + variable + "=" + state.value);
|
|
1871
|
+
return state.info;
|
|
2082
1872
|
}
|
|
2083
1873
|
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;
|
|
1874
|
+
await _commandError(state, e, this);
|
|
2091
1875
|
}
|
|
2092
1876
|
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
|
-
});
|
|
1877
|
+
_commandFinally(state, this);
|
|
2115
1878
|
}
|
|
2116
1879
|
}
|
|
2117
1880
|
async extractEmailData(emailAddress, options, world) {
|
|
@@ -2188,7 +1951,8 @@ class StableBrowser {
|
|
|
2188
1951
|
catch (e) {
|
|
2189
1952
|
errorCount++;
|
|
2190
1953
|
if (errorCount > 3) {
|
|
2191
|
-
throw e;
|
|
1954
|
+
// throw e;
|
|
1955
|
+
await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
|
|
2192
1956
|
}
|
|
2193
1957
|
// ignore
|
|
2194
1958
|
}
|
|
@@ -2299,11 +2063,12 @@ class StableBrowser {
|
|
|
2299
2063
|
info.screenshotPath = screenshotPath;
|
|
2300
2064
|
Object.assign(e, { info: info });
|
|
2301
2065
|
error = e;
|
|
2302
|
-
throw e;
|
|
2066
|
+
// throw e;
|
|
2067
|
+
await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
|
|
2303
2068
|
}
|
|
2304
2069
|
finally {
|
|
2305
2070
|
const endTime = Date.now();
|
|
2306
|
-
|
|
2071
|
+
_reportToWorld(world, {
|
|
2307
2072
|
type: Types.VERIFY_PAGE_PATH,
|
|
2308
2073
|
text: "Verify page path",
|
|
2309
2074
|
screenshotId,
|
|
@@ -2312,7 +2077,7 @@ class StableBrowser {
|
|
|
2312
2077
|
status: "FAILED",
|
|
2313
2078
|
startTime,
|
|
2314
2079
|
endTime,
|
|
2315
|
-
message: error
|
|
2080
|
+
message: error?.message,
|
|
2316
2081
|
}
|
|
2317
2082
|
: {
|
|
2318
2083
|
status: "PASSED",
|
|
@@ -2325,52 +2090,58 @@ class StableBrowser {
|
|
|
2325
2090
|
}
|
|
2326
2091
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2327
2092
|
text = unEscapeString(text);
|
|
2328
|
-
const
|
|
2093
|
+
const state = {
|
|
2094
|
+
text_search: text,
|
|
2095
|
+
options,
|
|
2096
|
+
world,
|
|
2097
|
+
locate: false,
|
|
2098
|
+
scroll: false,
|
|
2099
|
+
highlight: false,
|
|
2100
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
2101
|
+
text: `Verify text exists in page`,
|
|
2102
|
+
operation: "verifyTextExistInPage",
|
|
2103
|
+
log: "***** verify text " + text + " exists in page *****\n",
|
|
2104
|
+
};
|
|
2329
2105
|
const timeout = this._getLoadTimeout(options);
|
|
2330
|
-
let error = null;
|
|
2331
|
-
let screenshotId = null;
|
|
2332
|
-
let screenshotPath = null;
|
|
2333
2106
|
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
2107
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
2338
2108
|
if (newValue !== text) {
|
|
2339
2109
|
this.logger.info(text + "=" + newValue);
|
|
2340
2110
|
text = newValue;
|
|
2341
2111
|
}
|
|
2342
|
-
info.text = text;
|
|
2343
2112
|
let dateAlternatives = findDateAlternatives(text);
|
|
2344
2113
|
let numberAlternatives = findNumberAlternatives(text);
|
|
2345
2114
|
try {
|
|
2115
|
+
await _preCommand(state, this);
|
|
2116
|
+
state.info.text = text;
|
|
2346
2117
|
while (true) {
|
|
2347
2118
|
const frames = this.page.frames();
|
|
2348
2119
|
let results = [];
|
|
2349
2120
|
for (let i = 0; i < frames.length; i++) {
|
|
2350
2121
|
if (dateAlternatives.date) {
|
|
2351
2122
|
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2352
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "
|
|
2123
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2353
2124
|
result.frame = frames[i];
|
|
2354
2125
|
results.push(result);
|
|
2355
2126
|
}
|
|
2356
2127
|
}
|
|
2357
2128
|
else if (numberAlternatives.number) {
|
|
2358
2129
|
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2359
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "
|
|
2130
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2360
2131
|
result.frame = frames[i];
|
|
2361
2132
|
results.push(result);
|
|
2362
2133
|
}
|
|
2363
2134
|
}
|
|
2364
2135
|
else {
|
|
2365
|
-
const result = await this._locateElementByText(frames[i], text, "
|
|
2136
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2366
2137
|
result.frame = frames[i];
|
|
2367
2138
|
results.push(result);
|
|
2368
2139
|
}
|
|
2369
2140
|
}
|
|
2370
|
-
info.results = results;
|
|
2141
|
+
state.info.results = results;
|
|
2371
2142
|
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2372
2143
|
if (resultWithElementsFound.length === 0) {
|
|
2373
|
-
if (Date.now() - startTime > timeout) {
|
|
2144
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2374
2145
|
throw new Error(`Text ${text} not found in page`);
|
|
2375
2146
|
}
|
|
2376
2147
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -2382,44 +2153,89 @@ class StableBrowser {
|
|
|
2382
2153
|
await this._highlightElements(frame, dataAttribute);
|
|
2383
2154
|
const element = await frame.$(dataAttribute);
|
|
2384
2155
|
if (element) {
|
|
2385
|
-
await this.scrollIfNeeded(element, info);
|
|
2156
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2386
2157
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2387
2158
|
}
|
|
2388
2159
|
}
|
|
2389
|
-
|
|
2390
|
-
return info;
|
|
2160
|
+
await _screenshot(state, this);
|
|
2161
|
+
return state.info;
|
|
2391
2162
|
}
|
|
2392
2163
|
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2393
2164
|
}
|
|
2394
2165
|
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;
|
|
2166
|
+
await _commandError(state, e, this);
|
|
2402
2167
|
}
|
|
2403
2168
|
finally {
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2169
|
+
_commandFinally(state, this);
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
async waitForTextToDisappear(text, options = {}, world = null) {
|
|
2173
|
+
text = unEscapeString(text);
|
|
2174
|
+
const state = {
|
|
2175
|
+
text_search: text,
|
|
2176
|
+
options,
|
|
2177
|
+
world,
|
|
2178
|
+
locate: false,
|
|
2179
|
+
scroll: false,
|
|
2180
|
+
highlight: false,
|
|
2181
|
+
type: Types.WAIT_FOR_TEXT_TO_DISAPPEAR,
|
|
2182
|
+
text: `Verify text does not exist in page`,
|
|
2183
|
+
operation: "verifyTextNotExistInPage",
|
|
2184
|
+
log: "***** verify text " + text + " does not exist in page *****\n",
|
|
2185
|
+
};
|
|
2186
|
+
const timeout = this._getLoadTimeout(options);
|
|
2187
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2188
|
+
const newValue = await this._replaceWithLocalData(text, world);
|
|
2189
|
+
if (newValue !== text) {
|
|
2190
|
+
this.logger.info(text + "=" + newValue);
|
|
2191
|
+
text = newValue;
|
|
2192
|
+
}
|
|
2193
|
+
let dateAlternatives = findDateAlternatives(text);
|
|
2194
|
+
let numberAlternatives = findNumberAlternatives(text);
|
|
2195
|
+
try {
|
|
2196
|
+
await _preCommand(state, this);
|
|
2197
|
+
state.info.text = text;
|
|
2198
|
+
while (true) {
|
|
2199
|
+
const frames = this.page.frames();
|
|
2200
|
+
let results = [];
|
|
2201
|
+
for (let i = 0; i < frames.length; i++) {
|
|
2202
|
+
if (dateAlternatives.date) {
|
|
2203
|
+
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2204
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2205
|
+
result.frame = frames[i];
|
|
2206
|
+
results.push(result);
|
|
2207
|
+
}
|
|
2415
2208
|
}
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2209
|
+
else if (numberAlternatives.number) {
|
|
2210
|
+
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2211
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2212
|
+
result.frame = frames[i];
|
|
2213
|
+
results.push(result);
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
else {
|
|
2217
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2218
|
+
result.frame = frames[i];
|
|
2219
|
+
results.push(result);
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
state.info.results = results;
|
|
2223
|
+
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2224
|
+
if (resultWithElementsFound.length === 0) {
|
|
2225
|
+
await _screenshot(state, this);
|
|
2226
|
+
return state.info;
|
|
2227
|
+
}
|
|
2228
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2229
|
+
throw new Error(`Text ${text} found in page`);
|
|
2230
|
+
}
|
|
2231
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
catch (e) {
|
|
2235
|
+
await _commandError(state, e, this);
|
|
2236
|
+
}
|
|
2237
|
+
finally {
|
|
2238
|
+
_commandFinally(state, this);
|
|
2423
2239
|
}
|
|
2424
2240
|
}
|
|
2425
2241
|
_getServerUrl() {
|
|
@@ -2482,11 +2298,12 @@ class StableBrowser {
|
|
|
2482
2298
|
info.screenshotPath = screenshotPath;
|
|
2483
2299
|
Object.assign(e, { info: info });
|
|
2484
2300
|
error = e;
|
|
2485
|
-
throw e;
|
|
2301
|
+
// throw e;
|
|
2302
|
+
await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
|
|
2486
2303
|
}
|
|
2487
2304
|
finally {
|
|
2488
2305
|
const endTime = Date.now();
|
|
2489
|
-
|
|
2306
|
+
_reportToWorld(world, {
|
|
2490
2307
|
type: Types.VERIFY_VISUAL,
|
|
2491
2308
|
text: "Visual verification",
|
|
2492
2309
|
screenshotId,
|
|
@@ -2495,7 +2312,7 @@ class StableBrowser {
|
|
|
2495
2312
|
status: "FAILED",
|
|
2496
2313
|
startTime,
|
|
2497
2314
|
endTime,
|
|
2498
|
-
message: error
|
|
2315
|
+
message: error?.message,
|
|
2499
2316
|
}
|
|
2500
2317
|
: {
|
|
2501
2318
|
status: "PASSED",
|
|
@@ -2527,7 +2344,7 @@ class StableBrowser {
|
|
|
2527
2344
|
this.logger.info("Table data verified");
|
|
2528
2345
|
}
|
|
2529
2346
|
async getTableData(selectors, _params = null, options = {}, world = null) {
|
|
2530
|
-
|
|
2347
|
+
_validateSelectors(selectors);
|
|
2531
2348
|
const startTime = Date.now();
|
|
2532
2349
|
let error = null;
|
|
2533
2350
|
let screenshotId = null;
|
|
@@ -2549,11 +2366,12 @@ class StableBrowser {
|
|
|
2549
2366
|
info.screenshotPath = screenshotPath;
|
|
2550
2367
|
Object.assign(e, { info: info });
|
|
2551
2368
|
error = e;
|
|
2552
|
-
throw e;
|
|
2369
|
+
// throw e;
|
|
2370
|
+
await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
|
|
2553
2371
|
}
|
|
2554
2372
|
finally {
|
|
2555
2373
|
const endTime = Date.now();
|
|
2556
|
-
|
|
2374
|
+
_reportToWorld(world, {
|
|
2557
2375
|
element_name: selectors.element_name,
|
|
2558
2376
|
type: Types.GET_TABLE_DATA,
|
|
2559
2377
|
text: "Get table data",
|
|
@@ -2563,7 +2381,7 @@ class StableBrowser {
|
|
|
2563
2381
|
status: "FAILED",
|
|
2564
2382
|
startTime,
|
|
2565
2383
|
endTime,
|
|
2566
|
-
message: error
|
|
2384
|
+
message: error?.message,
|
|
2567
2385
|
}
|
|
2568
2386
|
: {
|
|
2569
2387
|
status: "PASSED",
|
|
@@ -2575,7 +2393,7 @@ class StableBrowser {
|
|
|
2575
2393
|
}
|
|
2576
2394
|
}
|
|
2577
2395
|
async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
|
|
2578
|
-
|
|
2396
|
+
_validateSelectors(selectors);
|
|
2579
2397
|
if (!query) {
|
|
2580
2398
|
throw new Error("query is null");
|
|
2581
2399
|
}
|
|
@@ -2714,11 +2532,12 @@ class StableBrowser {
|
|
|
2714
2532
|
info.screenshotPath = screenshotPath;
|
|
2715
2533
|
Object.assign(e, { info: info });
|
|
2716
2534
|
error = e;
|
|
2717
|
-
throw e;
|
|
2535
|
+
// throw e;
|
|
2536
|
+
await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
|
|
2718
2537
|
}
|
|
2719
2538
|
finally {
|
|
2720
2539
|
const endTime = Date.now();
|
|
2721
|
-
|
|
2540
|
+
_reportToWorld(world, {
|
|
2722
2541
|
element_name: selectors.element_name,
|
|
2723
2542
|
type: Types.ANALYZE_TABLE,
|
|
2724
2543
|
text: "Analyze table",
|
|
@@ -2728,7 +2547,7 @@ class StableBrowser {
|
|
|
2728
2547
|
status: "FAILED",
|
|
2729
2548
|
startTime,
|
|
2730
2549
|
endTime,
|
|
2731
|
-
message: error
|
|
2550
|
+
message: error?.message,
|
|
2732
2551
|
}
|
|
2733
2552
|
: {
|
|
2734
2553
|
status: "PASSED",
|
|
@@ -2740,27 +2559,7 @@ class StableBrowser {
|
|
|
2740
2559
|
}
|
|
2741
2560
|
}
|
|
2742
2561
|
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;
|
|
2562
|
+
return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
|
|
2764
2563
|
}
|
|
2765
2564
|
_getLoadTimeout(options) {
|
|
2766
2565
|
let timeout = 15000;
|
|
@@ -2811,7 +2610,7 @@ class StableBrowser {
|
|
|
2811
2610
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2812
2611
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2813
2612
|
const endTime = Date.now();
|
|
2814
|
-
|
|
2613
|
+
_reportToWorld(world, {
|
|
2815
2614
|
type: Types.GET_PAGE_STATUS,
|
|
2816
2615
|
text: "Wait for page load",
|
|
2817
2616
|
screenshotId,
|
|
@@ -2820,7 +2619,7 @@ class StableBrowser {
|
|
|
2820
2619
|
status: "FAILED",
|
|
2821
2620
|
startTime,
|
|
2822
2621
|
endTime,
|
|
2823
|
-
message: error
|
|
2622
|
+
message: error?.message,
|
|
2824
2623
|
}
|
|
2825
2624
|
: {
|
|
2826
2625
|
status: "PASSED",
|
|
@@ -2831,41 +2630,35 @@ class StableBrowser {
|
|
|
2831
2630
|
}
|
|
2832
2631
|
}
|
|
2833
2632
|
async closePage(options = {}, world = null) {
|
|
2834
|
-
const
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2633
|
+
const state = {
|
|
2634
|
+
options,
|
|
2635
|
+
world,
|
|
2636
|
+
locate: false,
|
|
2637
|
+
scroll: false,
|
|
2638
|
+
highlight: false,
|
|
2639
|
+
type: Types.CLOSE_PAGE,
|
|
2640
|
+
text: `Close page`,
|
|
2641
|
+
operation: "closePage",
|
|
2642
|
+
log: "***** close page *****\n",
|
|
2643
|
+
throwError: false,
|
|
2644
|
+
};
|
|
2839
2645
|
try {
|
|
2646
|
+
await _preCommand(state, this);
|
|
2840
2647
|
await this.page.close();
|
|
2841
2648
|
}
|
|
2842
2649
|
catch (e) {
|
|
2843
2650
|
console.log(".");
|
|
2651
|
+
await _commandError(state, e, this);
|
|
2844
2652
|
}
|
|
2845
2653
|
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
|
-
});
|
|
2654
|
+
_commandFinally(state, this);
|
|
2867
2655
|
}
|
|
2868
2656
|
}
|
|
2657
|
+
saveTestDataAsGlobal(options, world) {
|
|
2658
|
+
const dataFile = this._getDataFile(world);
|
|
2659
|
+
process.env.GLOBAL_TEST_DATA_FILE = dataFile;
|
|
2660
|
+
this.logger.info("Save the scenario test data as global for the following scenarios.");
|
|
2661
|
+
}
|
|
2869
2662
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
2870
2663
|
const startTime = Date.now();
|
|
2871
2664
|
let error = null;
|
|
@@ -2883,12 +2676,13 @@ class StableBrowser {
|
|
|
2883
2676
|
}
|
|
2884
2677
|
catch (e) {
|
|
2885
2678
|
console.log(".");
|
|
2679
|
+
await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
|
|
2886
2680
|
}
|
|
2887
2681
|
finally {
|
|
2888
2682
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2889
2683
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2890
2684
|
const endTime = Date.now();
|
|
2891
|
-
|
|
2685
|
+
_reportToWorld(world, {
|
|
2892
2686
|
type: Types.SET_VIEWPORT,
|
|
2893
2687
|
text: "set viewport size to " + width + "x" + hight,
|
|
2894
2688
|
screenshotId,
|
|
@@ -2897,7 +2691,7 @@ class StableBrowser {
|
|
|
2897
2691
|
status: "FAILED",
|
|
2898
2692
|
startTime,
|
|
2899
2693
|
endTime,
|
|
2900
|
-
message: error
|
|
2694
|
+
message: error?.message,
|
|
2901
2695
|
}
|
|
2902
2696
|
: {
|
|
2903
2697
|
status: "PASSED",
|
|
@@ -2919,12 +2713,13 @@ class StableBrowser {
|
|
|
2919
2713
|
}
|
|
2920
2714
|
catch (e) {
|
|
2921
2715
|
console.log(".");
|
|
2716
|
+
await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
|
|
2922
2717
|
}
|
|
2923
2718
|
finally {
|
|
2924
2719
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2925
2720
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2926
2721
|
const endTime = Date.now();
|
|
2927
|
-
|
|
2722
|
+
_reportToWorld(world, {
|
|
2928
2723
|
type: Types.GET_PAGE_STATUS,
|
|
2929
2724
|
text: "page relaod",
|
|
2930
2725
|
screenshotId,
|
|
@@ -2933,7 +2728,7 @@ class StableBrowser {
|
|
|
2933
2728
|
status: "FAILED",
|
|
2934
2729
|
startTime,
|
|
2935
2730
|
endTime,
|
|
2936
|
-
message: error
|
|
2731
|
+
message: error?.message,
|
|
2937
2732
|
}
|
|
2938
2733
|
: {
|
|
2939
2734
|
status: "PASSED",
|
|
@@ -2960,11 +2755,37 @@ class StableBrowser {
|
|
|
2960
2755
|
console.log("#-#");
|
|
2961
2756
|
}
|
|
2962
2757
|
}
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2758
|
+
async beforeStep(world, step) {
|
|
2759
|
+
this.stepName = step.pickleStep.text;
|
|
2760
|
+
this.logger.info("step: " + this.stepName);
|
|
2761
|
+
if (this.stepIndex === undefined) {
|
|
2762
|
+
this.stepIndex = 0;
|
|
2763
|
+
}
|
|
2764
|
+
else {
|
|
2765
|
+
this.stepIndex++;
|
|
2766
|
+
}
|
|
2767
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2768
|
+
if (this.context.browserObject.context) {
|
|
2769
|
+
await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
if (this.tags === null && step && step.pickle && step.pickle.tags) {
|
|
2773
|
+
this.tags = step.pickle.tags.map((tag) => tag.name);
|
|
2774
|
+
// check if @global_test_data tag is present
|
|
2775
|
+
if (this.tags.includes("@global_test_data")) {
|
|
2776
|
+
this.saveTestDataAsGlobal({}, world);
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
async afterStep(world, step) {
|
|
2781
|
+
this.stepName = null;
|
|
2782
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2783
|
+
if (this.context.browserObject.context) {
|
|
2784
|
+
await this.context.browserObject.context.tracing.stopChunk({
|
|
2785
|
+
path: path.join(this.context.browserObject.traceFolder, `trace-${this.stepIndex}.zip`),
|
|
2786
|
+
});
|
|
2787
|
+
}
|
|
2966
2788
|
}
|
|
2967
|
-
world.attach(JSON.stringify(properties), { mediaType: "application/json" });
|
|
2968
2789
|
}
|
|
2969
2790
|
}
|
|
2970
2791
|
function createTimedPromise(promise, label) {
|