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