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