automation_model 1.0.466-dev → 1.0.466-stage
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api.d.ts +42 -1
- package/lib/api.js +224 -49
- package/lib/api.js.map +1 -1
- package/lib/auto_page.js +38 -16
- package/lib/auto_page.js.map +1 -1
- package/lib/axe/axe.mini.js +12 -0
- package/lib/browser_manager.d.ts +5 -3
- package/lib/browser_manager.js +44 -19
- package/lib/browser_manager.js.map +1 -1
- package/lib/command_common.d.ts +6 -0
- package/lib/command_common.js +138 -0
- package/lib/command_common.js.map +1 -0
- package/lib/environment.js +5 -3
- package/lib/environment.js.map +1 -1
- package/lib/error-messages.d.ts +6 -0
- package/lib/error-messages.js +188 -0
- package/lib/error-messages.js.map +1 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/init_browser.d.ts +1 -1
- package/lib/init_browser.js +18 -3
- package/lib/init_browser.js.map +1 -1
- package/lib/locate_element.js +6 -3
- package/lib/locate_element.js.map +1 -1
- package/lib/locator.d.ts +36 -0
- package/lib/locator.js +165 -0
- package/lib/locator.js.map +1 -1
- package/lib/network.d.ts +3 -0
- package/lib/network.js +144 -0
- package/lib/network.js.map +1 -0
- package/lib/stable_browser.d.ts +24 -19
- package/lib/stable_browser.js +574 -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 +4 -5
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,13 @@ 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);
|
|
89
100
|
}
|
|
90
101
|
registerEventListeners(context) {
|
|
91
102
|
this.registerConsoleLogListener(this.page, context);
|
|
@@ -94,10 +105,17 @@ class StableBrowser {
|
|
|
94
105
|
context.pageLoading = { status: false };
|
|
95
106
|
}
|
|
96
107
|
context.playContext.on("page", async function (page) {
|
|
108
|
+
if (this.configuration && this.configuration.closePopups === true) {
|
|
109
|
+
console.log("close unexpected popups");
|
|
110
|
+
await page.close();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
97
113
|
context.pageLoading.status = true;
|
|
98
114
|
this.page = page;
|
|
99
115
|
context.page = page;
|
|
100
116
|
context.pages.push(page);
|
|
117
|
+
registerNetworkEvents(this.world, this, context, this.page);
|
|
118
|
+
registerDownloadEvent(this.page, this.world, context);
|
|
101
119
|
page.on("close", async () => {
|
|
102
120
|
if (this.context && this.context.pages && this.context.pages.length > 1) {
|
|
103
121
|
this.context.pages.pop();
|
|
@@ -127,9 +145,9 @@ class StableBrowser {
|
|
|
127
145
|
if (this.appName === appName) {
|
|
128
146
|
return;
|
|
129
147
|
}
|
|
130
|
-
let
|
|
148
|
+
let navigate = false;
|
|
131
149
|
if (!apps[appName]) {
|
|
132
|
-
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this);
|
|
150
|
+
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this, -1, this.context.reportFolder);
|
|
133
151
|
newContextCreated = true;
|
|
134
152
|
apps[appName] = {
|
|
135
153
|
context: newContext,
|
|
@@ -142,8 +160,7 @@ class StableBrowser {
|
|
|
142
160
|
this._copyContext(apps[appName], this);
|
|
143
161
|
apps[this.appName] = tempContext;
|
|
144
162
|
this.appName = appName;
|
|
145
|
-
if (
|
|
146
|
-
this.registerEventListeners(this.context);
|
|
163
|
+
if (navigate) {
|
|
147
164
|
await this.goto(this.context.environment.baseUrl);
|
|
148
165
|
await this.waitForPageLoad();
|
|
149
166
|
}
|
|
@@ -169,7 +186,6 @@ class StableBrowser {
|
|
|
169
186
|
this.context.webLogger = [];
|
|
170
187
|
}
|
|
171
188
|
page.on("console", async (msg) => {
|
|
172
|
-
var _a;
|
|
173
189
|
const obj = {
|
|
174
190
|
type: msg.type(),
|
|
175
191
|
text: msg.text(),
|
|
@@ -178,7 +194,7 @@ class StableBrowser {
|
|
|
178
194
|
};
|
|
179
195
|
this.context.webLogger.push(obj);
|
|
180
196
|
if (msg.type() === "error") {
|
|
181
|
-
|
|
197
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
|
|
182
198
|
}
|
|
183
199
|
});
|
|
184
200
|
}
|
|
@@ -187,7 +203,6 @@ class StableBrowser {
|
|
|
187
203
|
this.context.networkLogger = [];
|
|
188
204
|
}
|
|
189
205
|
page.on("request", async (data) => {
|
|
190
|
-
var _a;
|
|
191
206
|
const startTime = new Date().getTime();
|
|
192
207
|
try {
|
|
193
208
|
const pageUrl = new URL(page.url());
|
|
@@ -212,10 +227,10 @@ class StableBrowser {
|
|
|
212
227
|
startTime,
|
|
213
228
|
};
|
|
214
229
|
context.networkLogger.push(obj);
|
|
215
|
-
|
|
230
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
|
|
216
231
|
}
|
|
217
232
|
catch (error) {
|
|
218
|
-
console.error("Error in request listener", error);
|
|
233
|
+
// console.error("Error in request listener", error);
|
|
219
234
|
context.networkLogger.push({
|
|
220
235
|
error: "not able to listen",
|
|
221
236
|
message: error.message,
|
|
@@ -237,20 +252,6 @@ class StableBrowser {
|
|
|
237
252
|
timeout: 60000,
|
|
238
253
|
});
|
|
239
254
|
}
|
|
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
255
|
_fixUsingParams(text, _params) {
|
|
255
256
|
if (!_params || typeof text !== "string") {
|
|
256
257
|
return text;
|
|
@@ -318,7 +319,7 @@ class StableBrowser {
|
|
|
318
319
|
locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
|
|
319
320
|
}
|
|
320
321
|
}
|
|
321
|
-
if (locator
|
|
322
|
+
if (locator?.engine) {
|
|
322
323
|
if (locator.engine === "css") {
|
|
323
324
|
locatorReturn = scope.locator(locator.selector);
|
|
324
325
|
}
|
|
@@ -342,7 +343,7 @@ class StableBrowser {
|
|
|
342
343
|
if (css && css.locator) {
|
|
343
344
|
css = css.locator;
|
|
344
345
|
}
|
|
345
|
-
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "
|
|
346
|
+
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*:not(script, style, head)", false, false, _params);
|
|
346
347
|
if (result.elementCount === 0) {
|
|
347
348
|
return;
|
|
348
349
|
}
|
|
@@ -357,7 +358,7 @@ class StableBrowser {
|
|
|
357
358
|
}
|
|
358
359
|
async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
|
|
359
360
|
//const stringifyText = JSON.stringify(text);
|
|
360
|
-
return await scope.evaluate(([text, tag, regex, partial]) => {
|
|
361
|
+
return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
|
|
361
362
|
function isParent(parent, child) {
|
|
362
363
|
let currentNode = child.parentNode;
|
|
363
364
|
while (currentNode !== null) {
|
|
@@ -394,7 +395,7 @@ class StableBrowser {
|
|
|
394
395
|
}
|
|
395
396
|
document.collectAllShadowDomElements = collectAllShadowDomElements;
|
|
396
397
|
if (!tag) {
|
|
397
|
-
tag = "
|
|
398
|
+
tag = "*:not(script, style, head)";
|
|
398
399
|
}
|
|
399
400
|
let regexpSearch = document.getRegex(text);
|
|
400
401
|
if (regexpSearch) {
|
|
@@ -476,6 +477,15 @@ class StableBrowser {
|
|
|
476
477
|
}, [text1, tag1, regex1, partial1]);
|
|
477
478
|
}
|
|
478
479
|
async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
|
|
480
|
+
if (!info) {
|
|
481
|
+
info = {};
|
|
482
|
+
}
|
|
483
|
+
if (!info.failCause) {
|
|
484
|
+
info.failCause = {};
|
|
485
|
+
}
|
|
486
|
+
if (!info.log) {
|
|
487
|
+
info.log = "";
|
|
488
|
+
}
|
|
479
489
|
let locatorSearch = selectorHierarchy[index];
|
|
480
490
|
try {
|
|
481
491
|
locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
|
|
@@ -488,13 +498,18 @@ class StableBrowser {
|
|
|
488
498
|
if (locatorSearch.climb && locatorSearch.climb >= 0) {
|
|
489
499
|
let locatorString = await this._locateElmentByTextClimbCss(scope, locatorSearch.text, locatorSearch.climb, locatorSearch.css, _params);
|
|
490
500
|
if (!locatorString) {
|
|
501
|
+
info.failCause.textNotFound = true;
|
|
502
|
+
info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
|
|
491
503
|
return;
|
|
492
504
|
}
|
|
493
505
|
locator = this._getLocator({ css: locatorString }, scope, _params);
|
|
494
506
|
}
|
|
495
507
|
else if (locatorSearch.text) {
|
|
496
|
-
let
|
|
508
|
+
let text = this._fixUsingParams(locatorSearch.text, _params);
|
|
509
|
+
let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
|
|
497
510
|
if (result.elementCount === 0) {
|
|
511
|
+
info.failCause.textNotFound = true;
|
|
512
|
+
info.failCause.lastError = "failed to locate element by text: " + text;
|
|
498
513
|
return;
|
|
499
514
|
}
|
|
500
515
|
locatorSearch.css = "[data-blinq-id='blinq-id-" + result.randomToken + "']";
|
|
@@ -511,6 +526,9 @@ class StableBrowser {
|
|
|
511
526
|
// cssHref = true;
|
|
512
527
|
// }
|
|
513
528
|
let count = await locator.count();
|
|
529
|
+
if (count > 0 && !info.failCause.count) {
|
|
530
|
+
info.failCause.count = count;
|
|
531
|
+
}
|
|
514
532
|
//info.log += "total elements found " + count + "\n";
|
|
515
533
|
//let visibleCount = 0;
|
|
516
534
|
let visibleLocator = null;
|
|
@@ -528,6 +546,8 @@ class StableBrowser {
|
|
|
528
546
|
foundLocators.push(locator.nth(j));
|
|
529
547
|
}
|
|
530
548
|
else {
|
|
549
|
+
info.failCause.visible = visible;
|
|
550
|
+
info.failCause.enabled = enabled;
|
|
531
551
|
if (!info.printMessages) {
|
|
532
552
|
info.printMessages = {};
|
|
533
553
|
}
|
|
@@ -539,6 +559,11 @@ class StableBrowser {
|
|
|
539
559
|
}
|
|
540
560
|
}
|
|
541
561
|
async closeUnexpectedPopups(info, _params) {
|
|
562
|
+
if (!info) {
|
|
563
|
+
info = {};
|
|
564
|
+
info.failCause = {};
|
|
565
|
+
info.log = "";
|
|
566
|
+
}
|
|
542
567
|
if (this.configuration.popupHandlers && this.configuration.popupHandlers.length > 0) {
|
|
543
568
|
if (!info) {
|
|
544
569
|
info = {};
|
|
@@ -579,7 +604,10 @@ class StableBrowser {
|
|
|
579
604
|
}
|
|
580
605
|
return { rerun: false };
|
|
581
606
|
}
|
|
582
|
-
async _locate(selectors, info, _params, timeout
|
|
607
|
+
async _locate(selectors, info, _params, timeout) {
|
|
608
|
+
if (!timeout) {
|
|
609
|
+
timeout = 30000;
|
|
610
|
+
}
|
|
583
611
|
for (let i = 0; i < 3; i++) {
|
|
584
612
|
info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
|
|
585
613
|
for (let j = 0; j < selectors.locators.length; j++) {
|
|
@@ -593,16 +621,15 @@ class StableBrowser {
|
|
|
593
621
|
}
|
|
594
622
|
throw new Error("unable to locate element " + JSON.stringify(selectors));
|
|
595
623
|
}
|
|
596
|
-
async
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
624
|
+
async _findFrameScope(selectors, timeout = 30000, info) {
|
|
625
|
+
if (!info) {
|
|
626
|
+
info = {};
|
|
627
|
+
info.failCause = {};
|
|
628
|
+
info.log = "";
|
|
629
|
+
}
|
|
602
630
|
let scope = this.page;
|
|
603
|
-
// for the simple click usecase
|
|
604
631
|
if (selectors.frame) {
|
|
605
|
-
|
|
632
|
+
return selectors.frame;
|
|
606
633
|
}
|
|
607
634
|
if (selectors.iframe_src || selectors.frameLocators) {
|
|
608
635
|
const findFrame = async (frame, framescope) => {
|
|
@@ -630,7 +657,6 @@ class StableBrowser {
|
|
|
630
657
|
}
|
|
631
658
|
return framescope;
|
|
632
659
|
};
|
|
633
|
-
info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
|
|
634
660
|
while (true) {
|
|
635
661
|
let frameFound = false;
|
|
636
662
|
if (selectors.nestFrmLoc) {
|
|
@@ -654,6 +680,8 @@ class StableBrowser {
|
|
|
654
680
|
if (!scope) {
|
|
655
681
|
info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
|
|
656
682
|
if (performance.now() - startTime > timeout) {
|
|
683
|
+
info.failCause.iframeNotFound = true;
|
|
684
|
+
info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
|
|
657
685
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
658
686
|
}
|
|
659
687
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -663,6 +691,30 @@ class StableBrowser {
|
|
|
663
691
|
}
|
|
664
692
|
}
|
|
665
693
|
}
|
|
694
|
+
if (!scope) {
|
|
695
|
+
scope = this.page;
|
|
696
|
+
}
|
|
697
|
+
return scope;
|
|
698
|
+
}
|
|
699
|
+
async _getDocumentBody(selectors, timeout = 30000, info) {
|
|
700
|
+
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
701
|
+
return scope.evaluate(() => {
|
|
702
|
+
var bodyContent = document.body.innerHTML;
|
|
703
|
+
return bodyContent;
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
707
|
+
if (!info) {
|
|
708
|
+
info = {};
|
|
709
|
+
info.failCause = {};
|
|
710
|
+
info.log = "";
|
|
711
|
+
}
|
|
712
|
+
let highPriorityTimeout = 5000;
|
|
713
|
+
let visibleOnlyTimeout = 6000;
|
|
714
|
+
let startTime = performance.now();
|
|
715
|
+
let locatorsCount = 0;
|
|
716
|
+
//let arrayMode = Array.isArray(selectors);
|
|
717
|
+
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
666
718
|
let selectorsLocators = null;
|
|
667
719
|
selectorsLocators = selectors.locators;
|
|
668
720
|
// group selectors by priority
|
|
@@ -764,6 +816,8 @@ class StableBrowser {
|
|
|
764
816
|
}
|
|
765
817
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
766
818
|
info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
|
|
819
|
+
info.failCause.locatorNotFound = true;
|
|
820
|
+
info.failCause.lastError = "failed to locate unique element";
|
|
767
821
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
768
822
|
}
|
|
769
823
|
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
@@ -795,6 +849,9 @@ class StableBrowser {
|
|
|
795
849
|
});
|
|
796
850
|
result.locatorIndex = i;
|
|
797
851
|
}
|
|
852
|
+
if (foundLocators.length > 1) {
|
|
853
|
+
info.failCause.foundMultiple = true;
|
|
854
|
+
}
|
|
798
855
|
}
|
|
799
856
|
return result;
|
|
800
857
|
}
|
|
@@ -807,12 +864,12 @@ class StableBrowser {
|
|
|
807
864
|
while (true) {
|
|
808
865
|
try {
|
|
809
866
|
const result = await locate_element(this.context, elementDescription, "click");
|
|
810
|
-
if (
|
|
867
|
+
if (result?.elementNumber >= 0) {
|
|
811
868
|
const selectors = {
|
|
812
|
-
frame: result
|
|
869
|
+
frame: result?.frame,
|
|
813
870
|
locators: [
|
|
814
871
|
{
|
|
815
|
-
css: result
|
|
872
|
+
css: result?.css,
|
|
816
873
|
},
|
|
817
874
|
],
|
|
818
875
|
};
|
|
@@ -822,7 +879,8 @@ class StableBrowser {
|
|
|
822
879
|
}
|
|
823
880
|
catch (e) {
|
|
824
881
|
if (performance.now() - startTime > timeout) {
|
|
825
|
-
throw e;
|
|
882
|
+
// throw e;
|
|
883
|
+
await _commandError({ text: "simpleClick", operation: "simpleClick", elementDescription, info: {} }, e, this);
|
|
826
884
|
}
|
|
827
885
|
}
|
|
828
886
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
@@ -837,12 +895,12 @@ class StableBrowser {
|
|
|
837
895
|
while (true) {
|
|
838
896
|
try {
|
|
839
897
|
const result = await locate_element(this.context, elementDescription, "fill", value);
|
|
840
|
-
if (
|
|
898
|
+
if (result?.elementNumber >= 0) {
|
|
841
899
|
const selectors = {
|
|
842
|
-
frame: result
|
|
900
|
+
frame: result?.frame,
|
|
843
901
|
locators: [
|
|
844
902
|
{
|
|
845
|
-
css: result
|
|
903
|
+
css: result?.css,
|
|
846
904
|
},
|
|
847
905
|
],
|
|
848
906
|
};
|
|
@@ -852,92 +910,68 @@ class StableBrowser {
|
|
|
852
910
|
}
|
|
853
911
|
catch (e) {
|
|
854
912
|
if (performance.now() - startTime > timeout) {
|
|
855
|
-
throw e;
|
|
913
|
+
// throw e;
|
|
914
|
+
await _commandError({ text: "simpleClickType", operation: "simpleClickType", value, elementDescription, info: {} }, e, this);
|
|
856
915
|
}
|
|
857
916
|
}
|
|
858
917
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
859
918
|
}
|
|
860
919
|
}
|
|
861
920
|
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;
|
|
921
|
+
const state = {
|
|
922
|
+
selectors,
|
|
923
|
+
_params,
|
|
924
|
+
options,
|
|
925
|
+
world,
|
|
926
|
+
text: "Click element",
|
|
927
|
+
type: Types.CLICK,
|
|
928
|
+
operation: "click",
|
|
929
|
+
log: "***** click on " + selectors.element_name + " *****\n",
|
|
930
|
+
};
|
|
874
931
|
try {
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
932
|
+
await _preCommand(state, this);
|
|
933
|
+
if (state.options && state.options.context) {
|
|
934
|
+
state.selectors.locators[0].text = state.options.context;
|
|
935
|
+
}
|
|
878
936
|
try {
|
|
879
|
-
await
|
|
880
|
-
await
|
|
881
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
937
|
+
await state.element.click();
|
|
938
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
882
939
|
}
|
|
883
940
|
catch (e) {
|
|
884
941
|
// await this.closeUnexpectedPopups();
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
await
|
|
888
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
942
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
943
|
+
await state.element.dispatchEvent("click");
|
|
944
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
889
945
|
}
|
|
890
946
|
await this.waitForPageLoad();
|
|
891
|
-
return info;
|
|
947
|
+
return state.info;
|
|
892
948
|
}
|
|
893
949
|
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;
|
|
950
|
+
await _commandError(state, e, this);
|
|
900
951
|
}
|
|
901
952
|
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
|
-
});
|
|
953
|
+
_commandFinally(state, this);
|
|
922
954
|
}
|
|
923
955
|
}
|
|
924
956
|
async setCheck(selectors, checked = true, _params, options = {}, world = null) {
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
957
|
+
const state = {
|
|
958
|
+
selectors,
|
|
959
|
+
_params,
|
|
960
|
+
options,
|
|
961
|
+
world,
|
|
962
|
+
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
963
|
+
text: checked ? `Check element` : `Uncheck element`,
|
|
964
|
+
operation: "setCheck",
|
|
965
|
+
log: "***** check " + selectors.element_name + " *****\n",
|
|
966
|
+
};
|
|
935
967
|
try {
|
|
936
|
-
|
|
937
|
-
|
|
968
|
+
await _preCommand(state, this);
|
|
969
|
+
state.info.checked = checked;
|
|
970
|
+
// let element = await this._locate(selectors, info, _params);
|
|
971
|
+
// ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
938
972
|
try {
|
|
939
|
-
await this._highlightElements(element);
|
|
940
|
-
await element.setChecked(checked);
|
|
973
|
+
// await this._highlightElements(element);
|
|
974
|
+
await state.element.setChecked(checked);
|
|
941
975
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
942
976
|
}
|
|
943
977
|
catch (e) {
|
|
@@ -946,179 +980,108 @@ class StableBrowser {
|
|
|
946
980
|
}
|
|
947
981
|
else {
|
|
948
982
|
//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 });
|
|
983
|
+
state.info.log += "setCheck failed, will try again" + "\n";
|
|
984
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
985
|
+
await state.element.setChecked(checked, { timeout: 5000, force: true });
|
|
952
986
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
953
987
|
}
|
|
954
988
|
}
|
|
955
989
|
await this.waitForPageLoad();
|
|
956
|
-
return info;
|
|
990
|
+
return state.info;
|
|
957
991
|
}
|
|
958
992
|
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;
|
|
993
|
+
await _commandError(state, e, this);
|
|
965
994
|
}
|
|
966
995
|
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
|
-
});
|
|
996
|
+
_commandFinally(state, this);
|
|
987
997
|
}
|
|
988
998
|
}
|
|
989
999
|
async hover(selectors, _params, options = {}, world = null) {
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1000
|
+
const state = {
|
|
1001
|
+
selectors,
|
|
1002
|
+
_params,
|
|
1003
|
+
options,
|
|
1004
|
+
world,
|
|
1005
|
+
type: Types.HOVER,
|
|
1006
|
+
text: `Hover element`,
|
|
1007
|
+
operation: "hover",
|
|
1008
|
+
log: "***** hover " + selectors.element_name + " *****\n",
|
|
1009
|
+
};
|
|
999
1010
|
try {
|
|
1000
|
-
|
|
1001
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1011
|
+
await _preCommand(state, this);
|
|
1002
1012
|
try {
|
|
1003
|
-
await
|
|
1004
|
-
await element.hover();
|
|
1013
|
+
await state.element.hover();
|
|
1005
1014
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1006
1015
|
}
|
|
1007
1016
|
catch (e) {
|
|
1008
1017
|
//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 });
|
|
1018
|
+
state.info.log += "hover failed, will try again" + "\n";
|
|
1019
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
1020
|
+
await state.element.hover({ timeout: 10000 });
|
|
1012
1021
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1013
1022
|
}
|
|
1014
1023
|
await this.waitForPageLoad();
|
|
1015
|
-
return info;
|
|
1024
|
+
return state.info;
|
|
1016
1025
|
}
|
|
1017
1026
|
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;
|
|
1027
|
+
await _commandError(state, e, this);
|
|
1024
1028
|
}
|
|
1025
1029
|
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
|
-
});
|
|
1030
|
+
_commandFinally(state, this);
|
|
1046
1031
|
}
|
|
1047
1032
|
}
|
|
1048
1033
|
async selectOption(selectors, values, _params = null, options = {}, world = null) {
|
|
1049
|
-
this._validateSelectors(selectors);
|
|
1050
1034
|
if (!values) {
|
|
1051
1035
|
throw new Error("values is null");
|
|
1052
1036
|
}
|
|
1053
|
-
const
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1037
|
+
const state = {
|
|
1038
|
+
selectors,
|
|
1039
|
+
_params,
|
|
1040
|
+
options,
|
|
1041
|
+
world,
|
|
1042
|
+
value: values.toString(),
|
|
1043
|
+
type: Types.SELECT,
|
|
1044
|
+
text: `Select option: ${values}`,
|
|
1045
|
+
operation: "selectOption",
|
|
1046
|
+
log: "***** select option " + selectors.element_name + " *****\n",
|
|
1047
|
+
};
|
|
1061
1048
|
try {
|
|
1062
|
-
|
|
1063
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1049
|
+
await _preCommand(state, this);
|
|
1064
1050
|
try {
|
|
1065
|
-
await
|
|
1066
|
-
await element.selectOption(values);
|
|
1051
|
+
await state.element.selectOption(values);
|
|
1067
1052
|
}
|
|
1068
1053
|
catch (e) {
|
|
1069
1054
|
//await this.closeUnexpectedPopups();
|
|
1070
|
-
info.log += "selectOption failed, will try force" + "\n";
|
|
1071
|
-
await element.selectOption(values, { timeout: 10000, force: true });
|
|
1055
|
+
state.info.log += "selectOption failed, will try force" + "\n";
|
|
1056
|
+
await state.element.selectOption(values, { timeout: 10000, force: true });
|
|
1072
1057
|
}
|
|
1073
1058
|
await this.waitForPageLoad();
|
|
1074
|
-
return info;
|
|
1059
|
+
return state.info;
|
|
1075
1060
|
}
|
|
1076
1061
|
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;
|
|
1062
|
+
await _commandError(state, e, this);
|
|
1084
1063
|
}
|
|
1085
1064
|
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
|
-
});
|
|
1065
|
+
_commandFinally(state, this);
|
|
1107
1066
|
}
|
|
1108
1067
|
}
|
|
1109
1068
|
async type(_value, _params = null, options = {}, world = null) {
|
|
1110
|
-
const
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1069
|
+
const state = {
|
|
1070
|
+
value: _value,
|
|
1071
|
+
_params,
|
|
1072
|
+
options,
|
|
1073
|
+
world,
|
|
1074
|
+
locate: false,
|
|
1075
|
+
scroll: false,
|
|
1076
|
+
highlight: false,
|
|
1077
|
+
type: Types.TYPE_PRESS,
|
|
1078
|
+
text: `Type value: ${_value}`,
|
|
1079
|
+
operation: "type",
|
|
1080
|
+
log: "",
|
|
1081
|
+
};
|
|
1119
1082
|
try {
|
|
1120
|
-
|
|
1121
|
-
const valueSegment =
|
|
1083
|
+
await _preCommand(state, this);
|
|
1084
|
+
const valueSegment = state.value.split("&&");
|
|
1122
1085
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1123
1086
|
if (i > 0) {
|
|
1124
1087
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1138,134 +1101,76 @@ class StableBrowser {
|
|
|
1138
1101
|
await this.page.keyboard.type(value);
|
|
1139
1102
|
}
|
|
1140
1103
|
}
|
|
1141
|
-
return info;
|
|
1104
|
+
return state.info;
|
|
1142
1105
|
}
|
|
1143
1106
|
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;
|
|
1107
|
+
await _commandError(state, e, this);
|
|
1151
1108
|
}
|
|
1152
1109
|
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
|
-
});
|
|
1110
|
+
_commandFinally(state, this);
|
|
1173
1111
|
}
|
|
1174
1112
|
}
|
|
1175
1113
|
async setInputValue(selectors, value, _params = null, options = {}, world = null) {
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
let screenshotPath = null;
|
|
1114
|
+
const state = {
|
|
1115
|
+
selectors,
|
|
1116
|
+
_params,
|
|
1117
|
+
value,
|
|
1118
|
+
options,
|
|
1119
|
+
world,
|
|
1120
|
+
type: Types.SET_INPUT,
|
|
1121
|
+
text: `Set input value`,
|
|
1122
|
+
operation: "setInputValue",
|
|
1123
|
+
log: "***** set input value " + selectors.element_name + " *****\n",
|
|
1124
|
+
};
|
|
1188
1125
|
try {
|
|
1189
|
-
|
|
1190
|
-
let
|
|
1191
|
-
await this.scrollIfNeeded(element, info);
|
|
1192
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1193
|
-
await this._highlightElements(element);
|
|
1126
|
+
await _preCommand(state, this);
|
|
1127
|
+
let value = await this._replaceWithLocalData(state.value, this);
|
|
1194
1128
|
try {
|
|
1195
|
-
await element.evaluateHandle((el, value) => {
|
|
1129
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1196
1130
|
el.value = value;
|
|
1197
1131
|
}, value);
|
|
1198
1132
|
}
|
|
1199
1133
|
catch (error) {
|
|
1200
1134
|
this.logger.error("setInputValue failed, will try again");
|
|
1201
|
-
|
|
1202
|
-
info.
|
|
1203
|
-
|
|
1204
|
-
await element.evaluateHandle((el, value) => {
|
|
1135
|
+
await _screenshot(state, this);
|
|
1136
|
+
Object.assign(error, { info: state.info });
|
|
1137
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1205
1138
|
el.value = value;
|
|
1206
1139
|
});
|
|
1207
1140
|
}
|
|
1208
1141
|
}
|
|
1209
1142
|
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;
|
|
1143
|
+
await _commandError(state, e, this);
|
|
1216
1144
|
}
|
|
1217
1145
|
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
|
-
});
|
|
1146
|
+
_commandFinally(state, this);
|
|
1239
1147
|
}
|
|
1240
1148
|
}
|
|
1241
1149
|
async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1150
|
+
const state = {
|
|
1151
|
+
selectors,
|
|
1152
|
+
_params,
|
|
1153
|
+
value: await this._replaceWithLocalData(value, this),
|
|
1154
|
+
options,
|
|
1155
|
+
world,
|
|
1156
|
+
type: Types.SET_DATE_TIME,
|
|
1157
|
+
text: `Set date time value: ${value}`,
|
|
1158
|
+
operation: "setDateTime",
|
|
1159
|
+
log: "***** set date time value " + selectors.element_name + " *****\n",
|
|
1160
|
+
throwError: false,
|
|
1161
|
+
};
|
|
1252
1162
|
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);
|
|
1163
|
+
await _preCommand(state, this);
|
|
1259
1164
|
try {
|
|
1260
|
-
await element.click();
|
|
1165
|
+
await state.element.click();
|
|
1261
1166
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1262
1167
|
if (format) {
|
|
1263
|
-
value = dayjs(value).format(format);
|
|
1264
|
-
await element.fill(value);
|
|
1168
|
+
state.value = dayjs(state.value).format(format);
|
|
1169
|
+
await state.element.fill(state.value);
|
|
1265
1170
|
}
|
|
1266
1171
|
else {
|
|
1267
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1268
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1172
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1173
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1269
1174
|
el.value = ""; // clear input
|
|
1270
1175
|
el.value = dateTimeValue;
|
|
1271
1176
|
}, dateTimeValue);
|
|
@@ -1278,20 +1183,19 @@ class StableBrowser {
|
|
|
1278
1183
|
}
|
|
1279
1184
|
catch (err) {
|
|
1280
1185
|
//await this.closeUnexpectedPopups();
|
|
1281
|
-
this.logger.error("setting date time input failed " + JSON.stringify(info));
|
|
1186
|
+
this.logger.error("setting date time input failed " + JSON.stringify(state.info));
|
|
1282
1187
|
this.logger.info("Trying again");
|
|
1283
|
-
|
|
1284
|
-
info.
|
|
1285
|
-
Object.assign(err, { info: info });
|
|
1188
|
+
await _screenshot(state, this);
|
|
1189
|
+
Object.assign(err, { info: state.info });
|
|
1286
1190
|
await element.click();
|
|
1287
1191
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1288
1192
|
if (format) {
|
|
1289
|
-
value = dayjs(value).format(format);
|
|
1290
|
-
await element.fill(value);
|
|
1193
|
+
state.value = dayjs(state.value).format(format);
|
|
1194
|
+
await state.element.fill(state.value);
|
|
1291
1195
|
}
|
|
1292
1196
|
else {
|
|
1293
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1294
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1197
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1198
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1295
1199
|
el.value = ""; // clear input
|
|
1296
1200
|
el.value = dateTimeValue;
|
|
1297
1201
|
}, dateTimeValue);
|
|
@@ -1304,60 +1208,39 @@ class StableBrowser {
|
|
|
1304
1208
|
}
|
|
1305
1209
|
}
|
|
1306
1210
|
catch (e) {
|
|
1307
|
-
|
|
1308
|
-
throw e;
|
|
1211
|
+
await _commandError(state, e, this);
|
|
1309
1212
|
}
|
|
1310
1213
|
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
|
-
});
|
|
1214
|
+
_commandFinally(state, this);
|
|
1332
1215
|
}
|
|
1333
1216
|
}
|
|
1334
1217
|
async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
|
|
1335
1218
|
_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
1219
|
const newValue = await this._replaceWithLocalData(_value, world);
|
|
1220
|
+
const state = {
|
|
1221
|
+
selectors,
|
|
1222
|
+
_params,
|
|
1223
|
+
value: newValue,
|
|
1224
|
+
originalValue: _value,
|
|
1225
|
+
options,
|
|
1226
|
+
world,
|
|
1227
|
+
type: Types.FILL,
|
|
1228
|
+
text: `Click type input with value: ${_value}`,
|
|
1229
|
+
operation: "clickType",
|
|
1230
|
+
log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
|
|
1231
|
+
};
|
|
1346
1232
|
if (newValue !== _value) {
|
|
1347
1233
|
//this.logger.info(_value + "=" + newValue);
|
|
1348
1234
|
_value = newValue;
|
|
1349
1235
|
}
|
|
1350
|
-
info.value = _value;
|
|
1351
1236
|
try {
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
await this.scrollIfNeeded(element, info);
|
|
1355
|
-
await this._highlightElements(element);
|
|
1237
|
+
await _preCommand(state, this);
|
|
1238
|
+
state.info.value = _value;
|
|
1356
1239
|
if (options === null || options === undefined || !options.press) {
|
|
1357
1240
|
try {
|
|
1358
|
-
let currentValue = await element.inputValue();
|
|
1241
|
+
let currentValue = await state.element.inputValue();
|
|
1359
1242
|
if (currentValue) {
|
|
1360
|
-
await element.fill("");
|
|
1243
|
+
await state.element.fill("");
|
|
1361
1244
|
}
|
|
1362
1245
|
}
|
|
1363
1246
|
catch (e) {
|
|
@@ -1366,22 +1249,22 @@ class StableBrowser {
|
|
|
1366
1249
|
}
|
|
1367
1250
|
if (options === null || options === undefined || options.press) {
|
|
1368
1251
|
try {
|
|
1369
|
-
await element.click({ timeout: 5000 });
|
|
1252
|
+
await state.element.click({ timeout: 5000 });
|
|
1370
1253
|
}
|
|
1371
1254
|
catch (e) {
|
|
1372
|
-
await element.dispatchEvent("click");
|
|
1255
|
+
await state.element.dispatchEvent("click");
|
|
1373
1256
|
}
|
|
1374
1257
|
}
|
|
1375
1258
|
else {
|
|
1376
1259
|
try {
|
|
1377
|
-
await element.focus();
|
|
1260
|
+
await state.element.focus();
|
|
1378
1261
|
}
|
|
1379
1262
|
catch (e) {
|
|
1380
|
-
await element.dispatchEvent("focus");
|
|
1263
|
+
await state.element.dispatchEvent("focus");
|
|
1381
1264
|
}
|
|
1382
1265
|
}
|
|
1383
1266
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1384
|
-
const valueSegment =
|
|
1267
|
+
const valueSegment = state.value.split("&&");
|
|
1385
1268
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1386
1269
|
if (i > 0) {
|
|
1387
1270
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1401,14 +1284,14 @@ class StableBrowser {
|
|
|
1401
1284
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1402
1285
|
}
|
|
1403
1286
|
}
|
|
1404
|
-
|
|
1287
|
+
await _screenshot(state, this);
|
|
1405
1288
|
if (enter === true) {
|
|
1406
1289
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1407
1290
|
await this.page.keyboard.press("Enter");
|
|
1408
1291
|
await this.waitForPageLoad();
|
|
1409
1292
|
}
|
|
1410
1293
|
else if (enter === false) {
|
|
1411
|
-
await element.dispatchEvent("change");
|
|
1294
|
+
await state.element.dispatchEvent("change");
|
|
1412
1295
|
//await this.page.keyboard.press("Tab");
|
|
1413
1296
|
}
|
|
1414
1297
|
else {
|
|
@@ -1417,104 +1300,50 @@ class StableBrowser {
|
|
|
1417
1300
|
await this.waitForPageLoad();
|
|
1418
1301
|
}
|
|
1419
1302
|
}
|
|
1420
|
-
return info;
|
|
1303
|
+
return state.info;
|
|
1421
1304
|
}
|
|
1422
1305
|
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;
|
|
1306
|
+
await _commandError(state, e, this);
|
|
1430
1307
|
}
|
|
1431
1308
|
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
|
-
});
|
|
1309
|
+
_commandFinally(state, this);
|
|
1453
1310
|
}
|
|
1454
1311
|
}
|
|
1455
1312
|
async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1313
|
+
const state = {
|
|
1314
|
+
selectors,
|
|
1315
|
+
_params,
|
|
1316
|
+
value: unEscapeString(value),
|
|
1317
|
+
options,
|
|
1318
|
+
world,
|
|
1319
|
+
type: Types.FILL,
|
|
1320
|
+
text: `Fill input with value: ${value}`,
|
|
1321
|
+
operation: "fill",
|
|
1322
|
+
log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
|
|
1323
|
+
};
|
|
1467
1324
|
try {
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
await
|
|
1471
|
-
await element.fill(value);
|
|
1472
|
-
await element.dispatchEvent("change");
|
|
1325
|
+
await _preCommand(state, this);
|
|
1326
|
+
await state.element.fill(value);
|
|
1327
|
+
await state.element.dispatchEvent("change");
|
|
1473
1328
|
if (enter) {
|
|
1474
1329
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1475
1330
|
await this.page.keyboard.press("Enter");
|
|
1476
1331
|
}
|
|
1477
1332
|
await this.waitForPageLoad();
|
|
1478
|
-
return info;
|
|
1333
|
+
return state.info;
|
|
1479
1334
|
}
|
|
1480
1335
|
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;
|
|
1336
|
+
await _commandError(state, e, this);
|
|
1488
1337
|
}
|
|
1489
1338
|
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
|
-
});
|
|
1339
|
+
_commandFinally(state, this);
|
|
1511
1340
|
}
|
|
1512
1341
|
}
|
|
1513
1342
|
async getText(selectors, _params = null, options = {}, info = {}, world = null) {
|
|
1514
1343
|
return await this._getText(selectors, 0, _params, options, info, world);
|
|
1515
1344
|
}
|
|
1516
1345
|
async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
|
|
1517
|
-
|
|
1346
|
+
_validateSelectors(selectors);
|
|
1518
1347
|
let screenshotId = null;
|
|
1519
1348
|
let screenshotPath = null;
|
|
1520
1349
|
if (!info.log) {
|
|
@@ -1558,166 +1387,124 @@ class StableBrowser {
|
|
|
1558
1387
|
}
|
|
1559
1388
|
}
|
|
1560
1389
|
async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
|
|
1561
|
-
var _a;
|
|
1562
|
-
this._validateSelectors(selectors);
|
|
1563
1390
|
if (!pattern) {
|
|
1564
1391
|
throw new Error("pattern is null");
|
|
1565
1392
|
}
|
|
1566
1393
|
if (!text) {
|
|
1567
1394
|
throw new Error("text is null");
|
|
1568
1395
|
}
|
|
1396
|
+
const state = {
|
|
1397
|
+
selectors,
|
|
1398
|
+
_params,
|
|
1399
|
+
pattern,
|
|
1400
|
+
value: pattern,
|
|
1401
|
+
options,
|
|
1402
|
+
world,
|
|
1403
|
+
locate: false,
|
|
1404
|
+
scroll: false,
|
|
1405
|
+
screenshot: false,
|
|
1406
|
+
highlight: false,
|
|
1407
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1408
|
+
text: `Verify element contains pattern: ${pattern}`,
|
|
1409
|
+
operation: "containsPattern",
|
|
1410
|
+
log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
|
|
1411
|
+
};
|
|
1569
1412
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1570
1413
|
if (newValue !== text) {
|
|
1571
1414
|
this.logger.info(text + "=" + newValue);
|
|
1572
1415
|
text = newValue;
|
|
1573
1416
|
}
|
|
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
1417
|
let foundObj = null;
|
|
1586
1418
|
try {
|
|
1587
|
-
|
|
1419
|
+
await _preCommand(state, this);
|
|
1420
|
+
state.info.pattern = pattern;
|
|
1421
|
+
foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
|
|
1588
1422
|
if (foundObj && foundObj.element) {
|
|
1589
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1423
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1590
1424
|
}
|
|
1591
|
-
|
|
1425
|
+
await _screenshot(state, this);
|
|
1592
1426
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1593
1427
|
pattern = pattern.replace("{text}", escapedText);
|
|
1594
1428
|
let regex = new RegExp(pattern, "im");
|
|
1595
|
-
if (!regex.test(foundObj
|
|
1596
|
-
info.foundText = foundObj
|
|
1429
|
+
if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
|
|
1430
|
+
state.info.foundText = foundObj?.text;
|
|
1597
1431
|
throw new Error("element doesn't contain text " + text);
|
|
1598
1432
|
}
|
|
1599
|
-
return info;
|
|
1433
|
+
return state.info;
|
|
1600
1434
|
}
|
|
1601
1435
|
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;
|
|
1436
|
+
this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
|
|
1437
|
+
await _commandError(state, e, this);
|
|
1610
1438
|
}
|
|
1611
1439
|
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
|
-
});
|
|
1440
|
+
_commandFinally(state, this);
|
|
1633
1441
|
}
|
|
1634
1442
|
}
|
|
1635
1443
|
async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1444
|
+
const state = {
|
|
1445
|
+
selectors,
|
|
1446
|
+
_params,
|
|
1447
|
+
value: text,
|
|
1448
|
+
options,
|
|
1449
|
+
world,
|
|
1450
|
+
locate: false,
|
|
1451
|
+
scroll: false,
|
|
1452
|
+
screenshot: false,
|
|
1453
|
+
highlight: false,
|
|
1454
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1455
|
+
text: `Verify element contains text: ${text}`,
|
|
1456
|
+
operation: "containsText",
|
|
1457
|
+
log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
|
|
1458
|
+
};
|
|
1639
1459
|
if (!text) {
|
|
1640
1460
|
throw new Error("text is null");
|
|
1641
1461
|
}
|
|
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;
|
|
1462
|
+
text = unEscapeString(text);
|
|
1650
1463
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1651
1464
|
if (newValue !== text) {
|
|
1652
1465
|
this.logger.info(text + "=" + newValue);
|
|
1653
1466
|
text = newValue;
|
|
1654
1467
|
}
|
|
1655
|
-
info.value = text;
|
|
1656
1468
|
let foundObj = null;
|
|
1657
1469
|
try {
|
|
1658
|
-
|
|
1470
|
+
await _preCommand(state, this);
|
|
1471
|
+
foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
|
|
1659
1472
|
if (foundObj && foundObj.element) {
|
|
1660
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1473
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1661
1474
|
}
|
|
1662
|
-
|
|
1475
|
+
await _screenshot(state, this);
|
|
1663
1476
|
const dateAlternatives = findDateAlternatives(text);
|
|
1664
1477
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1665
1478
|
if (dateAlternatives.date) {
|
|
1666
1479
|
for (let i = 0; i < dateAlternatives.dates.length; i++) {
|
|
1667
|
-
if (
|
|
1668
|
-
|
|
1669
|
-
return info;
|
|
1480
|
+
if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
|
|
1481
|
+
foundObj?.value?.includes(dateAlternatives.dates[i])) {
|
|
1482
|
+
return state.info;
|
|
1670
1483
|
}
|
|
1671
1484
|
}
|
|
1672
1485
|
throw new Error("element doesn't contain text " + text);
|
|
1673
1486
|
}
|
|
1674
1487
|
else if (numberAlternatives.number) {
|
|
1675
1488
|
for (let i = 0; i < numberAlternatives.numbers.length; i++) {
|
|
1676
|
-
if (
|
|
1677
|
-
|
|
1678
|
-
return info;
|
|
1489
|
+
if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
|
|
1490
|
+
foundObj?.value?.includes(numberAlternatives.numbers[i])) {
|
|
1491
|
+
return state.info;
|
|
1679
1492
|
}
|
|
1680
1493
|
}
|
|
1681
1494
|
throw new Error("element doesn't contain text " + text);
|
|
1682
1495
|
}
|
|
1683
|
-
else if (!
|
|
1684
|
-
info.foundText = foundObj
|
|
1685
|
-
info.value = foundObj
|
|
1496
|
+
else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
|
|
1497
|
+
state.info.foundText = foundObj?.text;
|
|
1498
|
+
state.info.value = foundObj?.value;
|
|
1686
1499
|
throw new Error("element doesn't contain text " + text);
|
|
1687
1500
|
}
|
|
1688
|
-
return info;
|
|
1501
|
+
return state.info;
|
|
1689
1502
|
}
|
|
1690
1503
|
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;
|
|
1504
|
+
await _commandError(state, e, this);
|
|
1698
1505
|
}
|
|
1699
1506
|
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
|
-
});
|
|
1507
|
+
_commandFinally(state, this);
|
|
1721
1508
|
}
|
|
1722
1509
|
}
|
|
1723
1510
|
_getDataFile(world = null) {
|
|
@@ -1899,11 +1686,9 @@ class StableBrowser {
|
|
|
1899
1686
|
if (!fs.existsSync(world.screenshotPath)) {
|
|
1900
1687
|
fs.mkdirSync(world.screenshotPath, { recursive: true });
|
|
1901
1688
|
}
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
}
|
|
1906
|
-
const screenshotPath = path.join(world.screenshotPath, nextIndex + ".png");
|
|
1689
|
+
// to make sure the path doesn't start with -
|
|
1690
|
+
const uuidStr = "id_" + randomUUID();
|
|
1691
|
+
const screenshotPath = path.join(world.screenshotPath, uuidStr + ".png");
|
|
1907
1692
|
try {
|
|
1908
1693
|
await this.takeScreenshot(screenshotPath);
|
|
1909
1694
|
// let buffer = await this.page.screenshot({ timeout: 4000 });
|
|
@@ -1917,7 +1702,7 @@ class StableBrowser {
|
|
|
1917
1702
|
catch (e) {
|
|
1918
1703
|
this.logger.info("unable to take screenshot, ignored");
|
|
1919
1704
|
}
|
|
1920
|
-
result.screenshotId =
|
|
1705
|
+
result.screenshotId = uuidStr;
|
|
1921
1706
|
result.screenshotPath = screenshotPath;
|
|
1922
1707
|
if (info && info.box) {
|
|
1923
1708
|
await drawRectangle(screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
|
|
@@ -1991,127 +1776,69 @@ class StableBrowser {
|
|
|
1991
1776
|
}
|
|
1992
1777
|
}
|
|
1993
1778
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1779
|
+
const state = {
|
|
1780
|
+
selectors,
|
|
1781
|
+
_params,
|
|
1782
|
+
options,
|
|
1783
|
+
world,
|
|
1784
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1785
|
+
text: `Verify element exists in page`,
|
|
1786
|
+
operation: "verifyElementExistInPage",
|
|
1787
|
+
log: "***** verify element " + selectors.element_name + " exists in page *****\n",
|
|
1788
|
+
};
|
|
1999
1789
|
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
1790
|
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;
|
|
1791
|
+
await _preCommand(state, this);
|
|
1792
|
+
await expect(state.element).toHaveCount(1, { timeout: 10000 });
|
|
1793
|
+
return state.info;
|
|
2013
1794
|
}
|
|
2014
1795
|
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;
|
|
1796
|
+
await _commandError(state, e, this);
|
|
2022
1797
|
}
|
|
2023
1798
|
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
|
-
});
|
|
1799
|
+
_commandFinally(state, this);
|
|
2044
1800
|
}
|
|
2045
1801
|
}
|
|
2046
1802
|
async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
1803
|
+
const state = {
|
|
1804
|
+
selectors,
|
|
1805
|
+
_params,
|
|
1806
|
+
attribute,
|
|
1807
|
+
variable,
|
|
1808
|
+
options,
|
|
1809
|
+
world,
|
|
1810
|
+
type: Types.EXTRACT,
|
|
1811
|
+
text: `Extract attribute from element`,
|
|
1812
|
+
operation: "extractAttribute",
|
|
1813
|
+
log: "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1814
|
+
};
|
|
2052
1815
|
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
1816
|
try {
|
|
2058
|
-
|
|
2059
|
-
await this._highlightElements(element);
|
|
2060
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1817
|
+
await _preCommand(state, this);
|
|
2061
1818
|
switch (attribute) {
|
|
2062
1819
|
case "inner_text":
|
|
2063
|
-
|
|
1820
|
+
state.value = await state.element.innerText();
|
|
2064
1821
|
break;
|
|
2065
1822
|
case "href":
|
|
2066
|
-
|
|
1823
|
+
state.value = await state.element.getAttribute("href");
|
|
2067
1824
|
break;
|
|
2068
1825
|
case "value":
|
|
2069
|
-
|
|
1826
|
+
state.value = await state.element.inputValue();
|
|
2070
1827
|
break;
|
|
2071
1828
|
default:
|
|
2072
|
-
|
|
1829
|
+
state.value = await state.element.getAttribute(attribute);
|
|
2073
1830
|
break;
|
|
2074
1831
|
}
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
this.setTestData({ [variable]: info.value }, world);
|
|
2080
|
-
this.logger.info("set test data: " + variable + "=" + info.value);
|
|
2081
|
-
return info;
|
|
1832
|
+
state.info.value = state.value;
|
|
1833
|
+
this.setTestData({ [variable]: state.value }, world);
|
|
1834
|
+
this.logger.info("set test data: " + variable + "=" + state.value);
|
|
1835
|
+
return state.info;
|
|
2082
1836
|
}
|
|
2083
1837
|
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;
|
|
1838
|
+
await _commandError(state, e, this);
|
|
2091
1839
|
}
|
|
2092
1840
|
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
|
-
});
|
|
1841
|
+
_commandFinally(state, this);
|
|
2115
1842
|
}
|
|
2116
1843
|
}
|
|
2117
1844
|
async extractEmailData(emailAddress, options, world) {
|
|
@@ -2188,7 +1915,8 @@ class StableBrowser {
|
|
|
2188
1915
|
catch (e) {
|
|
2189
1916
|
errorCount++;
|
|
2190
1917
|
if (errorCount > 3) {
|
|
2191
|
-
throw e;
|
|
1918
|
+
// throw e;
|
|
1919
|
+
await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
|
|
2192
1920
|
}
|
|
2193
1921
|
// ignore
|
|
2194
1922
|
}
|
|
@@ -2299,11 +2027,12 @@ class StableBrowser {
|
|
|
2299
2027
|
info.screenshotPath = screenshotPath;
|
|
2300
2028
|
Object.assign(e, { info: info });
|
|
2301
2029
|
error = e;
|
|
2302
|
-
throw e;
|
|
2030
|
+
// throw e;
|
|
2031
|
+
await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
|
|
2303
2032
|
}
|
|
2304
2033
|
finally {
|
|
2305
2034
|
const endTime = Date.now();
|
|
2306
|
-
|
|
2035
|
+
_reportToWorld(world, {
|
|
2307
2036
|
type: Types.VERIFY_PAGE_PATH,
|
|
2308
2037
|
text: "Verify page path",
|
|
2309
2038
|
screenshotId,
|
|
@@ -2312,7 +2041,7 @@ class StableBrowser {
|
|
|
2312
2041
|
status: "FAILED",
|
|
2313
2042
|
startTime,
|
|
2314
2043
|
endTime,
|
|
2315
|
-
message: error
|
|
2044
|
+
message: error?.message,
|
|
2316
2045
|
}
|
|
2317
2046
|
: {
|
|
2318
2047
|
status: "PASSED",
|
|
@@ -2325,52 +2054,58 @@ class StableBrowser {
|
|
|
2325
2054
|
}
|
|
2326
2055
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2327
2056
|
text = unEscapeString(text);
|
|
2328
|
-
const
|
|
2057
|
+
const state = {
|
|
2058
|
+
text_search: text,
|
|
2059
|
+
options,
|
|
2060
|
+
world,
|
|
2061
|
+
locate: false,
|
|
2062
|
+
scroll: false,
|
|
2063
|
+
highlight: false,
|
|
2064
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
2065
|
+
text: `Verify text exists in page`,
|
|
2066
|
+
operation: "verifyTextExistInPage",
|
|
2067
|
+
log: "***** verify text " + text + " exists in page *****\n",
|
|
2068
|
+
};
|
|
2329
2069
|
const timeout = this._getLoadTimeout(options);
|
|
2330
|
-
let error = null;
|
|
2331
|
-
let screenshotId = null;
|
|
2332
|
-
let screenshotPath = null;
|
|
2333
2070
|
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
2071
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
2338
2072
|
if (newValue !== text) {
|
|
2339
2073
|
this.logger.info(text + "=" + newValue);
|
|
2340
2074
|
text = newValue;
|
|
2341
2075
|
}
|
|
2342
|
-
info.text = text;
|
|
2343
2076
|
let dateAlternatives = findDateAlternatives(text);
|
|
2344
2077
|
let numberAlternatives = findNumberAlternatives(text);
|
|
2345
2078
|
try {
|
|
2079
|
+
await _preCommand(state, this);
|
|
2080
|
+
state.info.text = text;
|
|
2346
2081
|
while (true) {
|
|
2347
2082
|
const frames = this.page.frames();
|
|
2348
2083
|
let results = [];
|
|
2349
2084
|
for (let i = 0; i < frames.length; i++) {
|
|
2350
2085
|
if (dateAlternatives.date) {
|
|
2351
2086
|
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2352
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "
|
|
2087
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2353
2088
|
result.frame = frames[i];
|
|
2354
2089
|
results.push(result);
|
|
2355
2090
|
}
|
|
2356
2091
|
}
|
|
2357
2092
|
else if (numberAlternatives.number) {
|
|
2358
2093
|
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2359
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "
|
|
2094
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2360
2095
|
result.frame = frames[i];
|
|
2361
2096
|
results.push(result);
|
|
2362
2097
|
}
|
|
2363
2098
|
}
|
|
2364
2099
|
else {
|
|
2365
|
-
const result = await this._locateElementByText(frames[i], text, "
|
|
2100
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2366
2101
|
result.frame = frames[i];
|
|
2367
2102
|
results.push(result);
|
|
2368
2103
|
}
|
|
2369
2104
|
}
|
|
2370
|
-
info.results = results;
|
|
2105
|
+
state.info.results = results;
|
|
2371
2106
|
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2372
2107
|
if (resultWithElementsFound.length === 0) {
|
|
2373
|
-
if (Date.now() - startTime > timeout) {
|
|
2108
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2374
2109
|
throw new Error(`Text ${text} not found in page`);
|
|
2375
2110
|
}
|
|
2376
2111
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -2382,44 +2117,89 @@ class StableBrowser {
|
|
|
2382
2117
|
await this._highlightElements(frame, dataAttribute);
|
|
2383
2118
|
const element = await frame.$(dataAttribute);
|
|
2384
2119
|
if (element) {
|
|
2385
|
-
await this.scrollIfNeeded(element, info);
|
|
2120
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2386
2121
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2387
2122
|
}
|
|
2388
2123
|
}
|
|
2389
|
-
|
|
2390
|
-
return info;
|
|
2124
|
+
await _screenshot(state, this);
|
|
2125
|
+
return state.info;
|
|
2391
2126
|
}
|
|
2392
2127
|
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2393
2128
|
}
|
|
2394
2129
|
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;
|
|
2130
|
+
await _commandError(state, e, this);
|
|
2402
2131
|
}
|
|
2403
2132
|
finally {
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2133
|
+
_commandFinally(state, this);
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
async waitForTextToDisappear(text, options = {}, world = null) {
|
|
2137
|
+
text = unEscapeString(text);
|
|
2138
|
+
const state = {
|
|
2139
|
+
text_search: text,
|
|
2140
|
+
options,
|
|
2141
|
+
world,
|
|
2142
|
+
locate: false,
|
|
2143
|
+
scroll: false,
|
|
2144
|
+
highlight: false,
|
|
2145
|
+
type: Types.WAIT_FOR_TEXT_TO_DISAPPEAR,
|
|
2146
|
+
text: `Verify text does not exist in page`,
|
|
2147
|
+
operation: "verifyTextNotExistInPage",
|
|
2148
|
+
log: "***** verify text " + text + " does not exist in page *****\n",
|
|
2149
|
+
};
|
|
2150
|
+
const timeout = this._getLoadTimeout(options);
|
|
2151
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2152
|
+
const newValue = await this._replaceWithLocalData(text, world);
|
|
2153
|
+
if (newValue !== text) {
|
|
2154
|
+
this.logger.info(text + "=" + newValue);
|
|
2155
|
+
text = newValue;
|
|
2156
|
+
}
|
|
2157
|
+
let dateAlternatives = findDateAlternatives(text);
|
|
2158
|
+
let numberAlternatives = findNumberAlternatives(text);
|
|
2159
|
+
try {
|
|
2160
|
+
await _preCommand(state, this);
|
|
2161
|
+
state.info.text = text;
|
|
2162
|
+
while (true) {
|
|
2163
|
+
const frames = this.page.frames();
|
|
2164
|
+
let results = [];
|
|
2165
|
+
for (let i = 0; i < frames.length; i++) {
|
|
2166
|
+
if (dateAlternatives.date) {
|
|
2167
|
+
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2168
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2169
|
+
result.frame = frames[i];
|
|
2170
|
+
results.push(result);
|
|
2171
|
+
}
|
|
2415
2172
|
}
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2173
|
+
else if (numberAlternatives.number) {
|
|
2174
|
+
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2175
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2176
|
+
result.frame = frames[i];
|
|
2177
|
+
results.push(result);
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
else {
|
|
2181
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2182
|
+
result.frame = frames[i];
|
|
2183
|
+
results.push(result);
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
state.info.results = results;
|
|
2187
|
+
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2188
|
+
if (resultWithElementsFound.length === 0) {
|
|
2189
|
+
await _screenshot(state, this);
|
|
2190
|
+
return state.info;
|
|
2191
|
+
}
|
|
2192
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2193
|
+
throw new Error(`Text ${text} found in page`);
|
|
2194
|
+
}
|
|
2195
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
catch (e) {
|
|
2199
|
+
await _commandError(state, e, this);
|
|
2200
|
+
}
|
|
2201
|
+
finally {
|
|
2202
|
+
_commandFinally(state, this);
|
|
2423
2203
|
}
|
|
2424
2204
|
}
|
|
2425
2205
|
_getServerUrl() {
|
|
@@ -2482,11 +2262,12 @@ class StableBrowser {
|
|
|
2482
2262
|
info.screenshotPath = screenshotPath;
|
|
2483
2263
|
Object.assign(e, { info: info });
|
|
2484
2264
|
error = e;
|
|
2485
|
-
throw e;
|
|
2265
|
+
// throw e;
|
|
2266
|
+
await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
|
|
2486
2267
|
}
|
|
2487
2268
|
finally {
|
|
2488
2269
|
const endTime = Date.now();
|
|
2489
|
-
|
|
2270
|
+
_reportToWorld(world, {
|
|
2490
2271
|
type: Types.VERIFY_VISUAL,
|
|
2491
2272
|
text: "Visual verification",
|
|
2492
2273
|
screenshotId,
|
|
@@ -2495,7 +2276,7 @@ class StableBrowser {
|
|
|
2495
2276
|
status: "FAILED",
|
|
2496
2277
|
startTime,
|
|
2497
2278
|
endTime,
|
|
2498
|
-
message: error
|
|
2279
|
+
message: error?.message,
|
|
2499
2280
|
}
|
|
2500
2281
|
: {
|
|
2501
2282
|
status: "PASSED",
|
|
@@ -2527,7 +2308,7 @@ class StableBrowser {
|
|
|
2527
2308
|
this.logger.info("Table data verified");
|
|
2528
2309
|
}
|
|
2529
2310
|
async getTableData(selectors, _params = null, options = {}, world = null) {
|
|
2530
|
-
|
|
2311
|
+
_validateSelectors(selectors);
|
|
2531
2312
|
const startTime = Date.now();
|
|
2532
2313
|
let error = null;
|
|
2533
2314
|
let screenshotId = null;
|
|
@@ -2549,11 +2330,12 @@ class StableBrowser {
|
|
|
2549
2330
|
info.screenshotPath = screenshotPath;
|
|
2550
2331
|
Object.assign(e, { info: info });
|
|
2551
2332
|
error = e;
|
|
2552
|
-
throw e;
|
|
2333
|
+
// throw e;
|
|
2334
|
+
await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
|
|
2553
2335
|
}
|
|
2554
2336
|
finally {
|
|
2555
2337
|
const endTime = Date.now();
|
|
2556
|
-
|
|
2338
|
+
_reportToWorld(world, {
|
|
2557
2339
|
element_name: selectors.element_name,
|
|
2558
2340
|
type: Types.GET_TABLE_DATA,
|
|
2559
2341
|
text: "Get table data",
|
|
@@ -2563,7 +2345,7 @@ class StableBrowser {
|
|
|
2563
2345
|
status: "FAILED",
|
|
2564
2346
|
startTime,
|
|
2565
2347
|
endTime,
|
|
2566
|
-
message: error
|
|
2348
|
+
message: error?.message,
|
|
2567
2349
|
}
|
|
2568
2350
|
: {
|
|
2569
2351
|
status: "PASSED",
|
|
@@ -2575,7 +2357,7 @@ class StableBrowser {
|
|
|
2575
2357
|
}
|
|
2576
2358
|
}
|
|
2577
2359
|
async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
|
|
2578
|
-
|
|
2360
|
+
_validateSelectors(selectors);
|
|
2579
2361
|
if (!query) {
|
|
2580
2362
|
throw new Error("query is null");
|
|
2581
2363
|
}
|
|
@@ -2714,11 +2496,12 @@ class StableBrowser {
|
|
|
2714
2496
|
info.screenshotPath = screenshotPath;
|
|
2715
2497
|
Object.assign(e, { info: info });
|
|
2716
2498
|
error = e;
|
|
2717
|
-
throw e;
|
|
2499
|
+
// throw e;
|
|
2500
|
+
await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
|
|
2718
2501
|
}
|
|
2719
2502
|
finally {
|
|
2720
2503
|
const endTime = Date.now();
|
|
2721
|
-
|
|
2504
|
+
_reportToWorld(world, {
|
|
2722
2505
|
element_name: selectors.element_name,
|
|
2723
2506
|
type: Types.ANALYZE_TABLE,
|
|
2724
2507
|
text: "Analyze table",
|
|
@@ -2728,7 +2511,7 @@ class StableBrowser {
|
|
|
2728
2511
|
status: "FAILED",
|
|
2729
2512
|
startTime,
|
|
2730
2513
|
endTime,
|
|
2731
|
-
message: error
|
|
2514
|
+
message: error?.message,
|
|
2732
2515
|
}
|
|
2733
2516
|
: {
|
|
2734
2517
|
status: "PASSED",
|
|
@@ -2740,27 +2523,7 @@ class StableBrowser {
|
|
|
2740
2523
|
}
|
|
2741
2524
|
}
|
|
2742
2525
|
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;
|
|
2526
|
+
return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
|
|
2764
2527
|
}
|
|
2765
2528
|
_getLoadTimeout(options) {
|
|
2766
2529
|
let timeout = 15000;
|
|
@@ -2811,7 +2574,7 @@ class StableBrowser {
|
|
|
2811
2574
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2812
2575
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2813
2576
|
const endTime = Date.now();
|
|
2814
|
-
|
|
2577
|
+
_reportToWorld(world, {
|
|
2815
2578
|
type: Types.GET_PAGE_STATUS,
|
|
2816
2579
|
text: "Wait for page load",
|
|
2817
2580
|
screenshotId,
|
|
@@ -2820,7 +2583,7 @@ class StableBrowser {
|
|
|
2820
2583
|
status: "FAILED",
|
|
2821
2584
|
startTime,
|
|
2822
2585
|
endTime,
|
|
2823
|
-
message: error
|
|
2586
|
+
message: error?.message,
|
|
2824
2587
|
}
|
|
2825
2588
|
: {
|
|
2826
2589
|
status: "PASSED",
|
|
@@ -2831,41 +2594,35 @@ class StableBrowser {
|
|
|
2831
2594
|
}
|
|
2832
2595
|
}
|
|
2833
2596
|
async closePage(options = {}, world = null) {
|
|
2834
|
-
const
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2597
|
+
const state = {
|
|
2598
|
+
options,
|
|
2599
|
+
world,
|
|
2600
|
+
locate: false,
|
|
2601
|
+
scroll: false,
|
|
2602
|
+
highlight: false,
|
|
2603
|
+
type: Types.CLOSE_PAGE,
|
|
2604
|
+
text: `Close page`,
|
|
2605
|
+
operation: "closePage",
|
|
2606
|
+
log: "***** close page *****\n",
|
|
2607
|
+
throwError: false,
|
|
2608
|
+
};
|
|
2839
2609
|
try {
|
|
2610
|
+
await _preCommand(state, this);
|
|
2840
2611
|
await this.page.close();
|
|
2841
2612
|
}
|
|
2842
2613
|
catch (e) {
|
|
2843
2614
|
console.log(".");
|
|
2615
|
+
await _commandError(state, e, this);
|
|
2844
2616
|
}
|
|
2845
2617
|
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
|
-
});
|
|
2618
|
+
_commandFinally(state, this);
|
|
2867
2619
|
}
|
|
2868
2620
|
}
|
|
2621
|
+
saveTestDataAsGlobal(options, world) {
|
|
2622
|
+
const dataFile = this._getDataFile(world);
|
|
2623
|
+
process.env.GLOBAL_TEST_DATA_FILE = dataFile;
|
|
2624
|
+
this.logger.info("Save the scenario test data as global for the following scenarios.");
|
|
2625
|
+
}
|
|
2869
2626
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
2870
2627
|
const startTime = Date.now();
|
|
2871
2628
|
let error = null;
|
|
@@ -2883,12 +2640,13 @@ class StableBrowser {
|
|
|
2883
2640
|
}
|
|
2884
2641
|
catch (e) {
|
|
2885
2642
|
console.log(".");
|
|
2643
|
+
await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
|
|
2886
2644
|
}
|
|
2887
2645
|
finally {
|
|
2888
2646
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2889
2647
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2890
2648
|
const endTime = Date.now();
|
|
2891
|
-
|
|
2649
|
+
_reportToWorld(world, {
|
|
2892
2650
|
type: Types.SET_VIEWPORT,
|
|
2893
2651
|
text: "set viewport size to " + width + "x" + hight,
|
|
2894
2652
|
screenshotId,
|
|
@@ -2897,7 +2655,7 @@ class StableBrowser {
|
|
|
2897
2655
|
status: "FAILED",
|
|
2898
2656
|
startTime,
|
|
2899
2657
|
endTime,
|
|
2900
|
-
message: error
|
|
2658
|
+
message: error?.message,
|
|
2901
2659
|
}
|
|
2902
2660
|
: {
|
|
2903
2661
|
status: "PASSED",
|
|
@@ -2919,12 +2677,13 @@ class StableBrowser {
|
|
|
2919
2677
|
}
|
|
2920
2678
|
catch (e) {
|
|
2921
2679
|
console.log(".");
|
|
2680
|
+
await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
|
|
2922
2681
|
}
|
|
2923
2682
|
finally {
|
|
2924
2683
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2925
2684
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2926
2685
|
const endTime = Date.now();
|
|
2927
|
-
|
|
2686
|
+
_reportToWorld(world, {
|
|
2928
2687
|
type: Types.GET_PAGE_STATUS,
|
|
2929
2688
|
text: "page relaod",
|
|
2930
2689
|
screenshotId,
|
|
@@ -2933,7 +2692,7 @@ class StableBrowser {
|
|
|
2933
2692
|
status: "FAILED",
|
|
2934
2693
|
startTime,
|
|
2935
2694
|
endTime,
|
|
2936
|
-
message: error
|
|
2695
|
+
message: error?.message,
|
|
2937
2696
|
}
|
|
2938
2697
|
: {
|
|
2939
2698
|
status: "PASSED",
|
|
@@ -2960,11 +2719,37 @@ class StableBrowser {
|
|
|
2960
2719
|
console.log("#-#");
|
|
2961
2720
|
}
|
|
2962
2721
|
}
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2722
|
+
async beforeStep(world, step) {
|
|
2723
|
+
this.stepName = step.pickleStep.text;
|
|
2724
|
+
this.logger.info("step: " + this.stepName);
|
|
2725
|
+
if (this.stepIndex === undefined) {
|
|
2726
|
+
this.stepIndex = 0;
|
|
2727
|
+
}
|
|
2728
|
+
else {
|
|
2729
|
+
this.stepIndex++;
|
|
2730
|
+
}
|
|
2731
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2732
|
+
if (this.context.browserObject.context) {
|
|
2733
|
+
await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
if (this.tags === null && step && step.pickle && step.pickle.tags) {
|
|
2737
|
+
this.tags = step.pickle.tags.map((tag) => tag.name);
|
|
2738
|
+
// check if @global_test_data tag is present
|
|
2739
|
+
if (this.tags.includes("@global_test_data")) {
|
|
2740
|
+
this.saveTestDataAsGlobal({}, world);
|
|
2741
|
+
}
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
async afterStep(world, step) {
|
|
2745
|
+
this.stepName = null;
|
|
2746
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2747
|
+
if (this.context.browserObject.context) {
|
|
2748
|
+
await this.context.browserObject.context.tracing.stopChunk({
|
|
2749
|
+
path: path.join(this.context.browserObject.traceFolder, `trace-${this.stepIndex}.zip`),
|
|
2750
|
+
});
|
|
2751
|
+
}
|
|
2966
2752
|
}
|
|
2967
|
-
world.attach(JSON.stringify(properties), { mediaType: "application/json" });
|
|
2968
2753
|
}
|
|
2969
2754
|
}
|
|
2970
2755
|
function createTimedPromise(promise, label) {
|