automation_model 1.0.461-dev → 1.0.461-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 +181 -7
- package/lib/api.js.map +1 -1
- package/lib/auto_page.js +32 -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 +182 -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.d.ts +7 -0
- package/lib/locate_element.js +213 -0
- package/lib/locate_element.js.map +1 -0
- 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 +640 -781
- 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,12 +10,14 @@ 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";
|
|
18
|
+
import { locate_element } from "./locate_element.js";
|
|
19
|
+
import { _commandError, _commandFinally, _preCommand, _validateSelectors, _screenshot, _reportToWorld, } from "./command_common.js";
|
|
20
|
+
import { registerDownloadEvent, registerNetworkEvents } from "./network.js";
|
|
19
21
|
const Types = {
|
|
20
22
|
CLICK: "click_element",
|
|
21
23
|
NAVIGATE: "navigate",
|
|
@@ -42,20 +44,26 @@ const Types = {
|
|
|
42
44
|
VERIFY_VISUAL: "verify_visual",
|
|
43
45
|
LOAD_DATA: "load_data",
|
|
44
46
|
SET_INPUT: "set_input",
|
|
47
|
+
WAIT_FOR_TEXT_TO_DISAPPEAR: "wait_for_text_to_disappear",
|
|
45
48
|
};
|
|
46
49
|
export const apps = {};
|
|
47
50
|
class StableBrowser {
|
|
51
|
+
browser;
|
|
52
|
+
page;
|
|
53
|
+
logger;
|
|
54
|
+
context;
|
|
55
|
+
world;
|
|
56
|
+
project_path = null;
|
|
57
|
+
webLogFile = null;
|
|
58
|
+
networkLogger = null;
|
|
59
|
+
configuration = null;
|
|
60
|
+
appName = "main";
|
|
48
61
|
constructor(browser, page, logger = null, context = null, world = null) {
|
|
49
62
|
this.browser = browser;
|
|
50
63
|
this.page = page;
|
|
51
64
|
this.logger = logger;
|
|
52
65
|
this.context = context;
|
|
53
66
|
this.world = world;
|
|
54
|
-
this.project_path = null;
|
|
55
|
-
this.webLogFile = null;
|
|
56
|
-
this.networkLogger = null;
|
|
57
|
-
this.configuration = null;
|
|
58
|
-
this.appName = "main";
|
|
59
67
|
if (!this.logger) {
|
|
60
68
|
this.logger = console;
|
|
61
69
|
}
|
|
@@ -80,11 +88,13 @@ class StableBrowser {
|
|
|
80
88
|
catch (e) {
|
|
81
89
|
this.logger.error("unable to read ai_config.json");
|
|
82
90
|
}
|
|
83
|
-
context.pageLoading = { status: false };
|
|
84
|
-
context.pages = [this.page];
|
|
85
91
|
const logFolder = path.join(this.project_path, "logs", "web");
|
|
86
92
|
this.world = world;
|
|
93
|
+
context.pages = [this.page];
|
|
94
|
+
context.pageLoading = { status: false };
|
|
87
95
|
this.registerEventListeners(this.context);
|
|
96
|
+
registerNetworkEvents(this.world, this, this.context, this.page);
|
|
97
|
+
registerDownloadEvent(this.page, this.world, this.context);
|
|
88
98
|
}
|
|
89
99
|
registerEventListeners(context) {
|
|
90
100
|
this.registerConsoleLogListener(this.page, context);
|
|
@@ -93,10 +103,17 @@ class StableBrowser {
|
|
|
93
103
|
context.pageLoading = { status: false };
|
|
94
104
|
}
|
|
95
105
|
context.playContext.on("page", async function (page) {
|
|
106
|
+
if (this.configuration && this.configuration.closePopups === true) {
|
|
107
|
+
console.log("close unexpected popups");
|
|
108
|
+
await page.close();
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
96
111
|
context.pageLoading.status = true;
|
|
97
112
|
this.page = page;
|
|
98
113
|
context.page = page;
|
|
99
114
|
context.pages.push(page);
|
|
115
|
+
registerNetworkEvents(this.world, this, context, this.page);
|
|
116
|
+
registerDownloadEvent(this.page, this.world, context);
|
|
100
117
|
page.on("close", async () => {
|
|
101
118
|
if (this.context && this.context.pages && this.context.pages.length > 1) {
|
|
102
119
|
this.context.pages.pop();
|
|
@@ -126,9 +143,9 @@ class StableBrowser {
|
|
|
126
143
|
if (this.appName === appName) {
|
|
127
144
|
return;
|
|
128
145
|
}
|
|
129
|
-
let
|
|
146
|
+
let navigate = false;
|
|
130
147
|
if (!apps[appName]) {
|
|
131
|
-
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this);
|
|
148
|
+
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this, -1, this.context.reportFolder);
|
|
132
149
|
newContextCreated = true;
|
|
133
150
|
apps[appName] = {
|
|
134
151
|
context: newContext,
|
|
@@ -141,8 +158,7 @@ class StableBrowser {
|
|
|
141
158
|
this._copyContext(apps[appName], this);
|
|
142
159
|
apps[this.appName] = tempContext;
|
|
143
160
|
this.appName = appName;
|
|
144
|
-
if (
|
|
145
|
-
this.registerEventListeners(this.context);
|
|
161
|
+
if (navigate) {
|
|
146
162
|
await this.goto(this.context.environment.baseUrl);
|
|
147
163
|
await this.waitForPageLoad();
|
|
148
164
|
}
|
|
@@ -168,7 +184,6 @@ class StableBrowser {
|
|
|
168
184
|
this.context.webLogger = [];
|
|
169
185
|
}
|
|
170
186
|
page.on("console", async (msg) => {
|
|
171
|
-
var _a;
|
|
172
187
|
const obj = {
|
|
173
188
|
type: msg.type(),
|
|
174
189
|
text: msg.text(),
|
|
@@ -176,7 +191,9 @@ class StableBrowser {
|
|
|
176
191
|
time: new Date().toISOString(),
|
|
177
192
|
};
|
|
178
193
|
this.context.webLogger.push(obj);
|
|
179
|
-
|
|
194
|
+
if (msg.type() === "error") {
|
|
195
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
|
|
196
|
+
}
|
|
180
197
|
});
|
|
181
198
|
}
|
|
182
199
|
registerRequestListener(page, context, logFile) {
|
|
@@ -184,7 +201,6 @@ class StableBrowser {
|
|
|
184
201
|
this.context.networkLogger = [];
|
|
185
202
|
}
|
|
186
203
|
page.on("request", async (data) => {
|
|
187
|
-
var _a;
|
|
188
204
|
const startTime = new Date().getTime();
|
|
189
205
|
try {
|
|
190
206
|
const pageUrl = new URL(page.url());
|
|
@@ -209,10 +225,10 @@ class StableBrowser {
|
|
|
209
225
|
startTime,
|
|
210
226
|
};
|
|
211
227
|
context.networkLogger.push(obj);
|
|
212
|
-
|
|
228
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
|
|
213
229
|
}
|
|
214
230
|
catch (error) {
|
|
215
|
-
console.error("Error in request listener", error);
|
|
231
|
+
// console.error("Error in request listener", error);
|
|
216
232
|
context.networkLogger.push({
|
|
217
233
|
error: "not able to listen",
|
|
218
234
|
message: error.message,
|
|
@@ -234,20 +250,6 @@ class StableBrowser {
|
|
|
234
250
|
timeout: 60000,
|
|
235
251
|
});
|
|
236
252
|
}
|
|
237
|
-
_validateSelectors(selectors) {
|
|
238
|
-
if (!selectors) {
|
|
239
|
-
throw new Error("selectors is null");
|
|
240
|
-
}
|
|
241
|
-
if (!selectors.locators) {
|
|
242
|
-
throw new Error("selectors.locators is null");
|
|
243
|
-
}
|
|
244
|
-
if (!Array.isArray(selectors.locators)) {
|
|
245
|
-
throw new Error("selectors.locators expected to be array");
|
|
246
|
-
}
|
|
247
|
-
if (selectors.locators.length === 0) {
|
|
248
|
-
throw new Error("selectors.locators expected to be non empty array");
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
253
|
_fixUsingParams(text, _params) {
|
|
252
254
|
if (!_params || typeof text !== "string") {
|
|
253
255
|
return text;
|
|
@@ -315,7 +317,7 @@ class StableBrowser {
|
|
|
315
317
|
locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
|
|
316
318
|
}
|
|
317
319
|
}
|
|
318
|
-
if (locator
|
|
320
|
+
if (locator?.engine) {
|
|
319
321
|
if (locator.engine === "css") {
|
|
320
322
|
locatorReturn = scope.locator(locator.selector);
|
|
321
323
|
}
|
|
@@ -339,7 +341,7 @@ class StableBrowser {
|
|
|
339
341
|
if (css && css.locator) {
|
|
340
342
|
css = css.locator;
|
|
341
343
|
}
|
|
342
|
-
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "
|
|
344
|
+
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*:not(script, style, head)", false, false, _params);
|
|
343
345
|
if (result.elementCount === 0) {
|
|
344
346
|
return;
|
|
345
347
|
}
|
|
@@ -354,7 +356,7 @@ class StableBrowser {
|
|
|
354
356
|
}
|
|
355
357
|
async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
|
|
356
358
|
//const stringifyText = JSON.stringify(text);
|
|
357
|
-
return await scope.evaluate(([text, tag, regex, partial]) => {
|
|
359
|
+
return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
|
|
358
360
|
function isParent(parent, child) {
|
|
359
361
|
let currentNode = child.parentNode;
|
|
360
362
|
while (currentNode !== null) {
|
|
@@ -391,7 +393,7 @@ class StableBrowser {
|
|
|
391
393
|
}
|
|
392
394
|
document.collectAllShadowDomElements = collectAllShadowDomElements;
|
|
393
395
|
if (!tag) {
|
|
394
|
-
tag = "
|
|
396
|
+
tag = "*:not(script, style, head)";
|
|
395
397
|
}
|
|
396
398
|
let regexpSearch = document.getRegex(text);
|
|
397
399
|
if (regexpSearch) {
|
|
@@ -473,19 +475,39 @@ class StableBrowser {
|
|
|
473
475
|
}, [text1, tag1, regex1, partial1]);
|
|
474
476
|
}
|
|
475
477
|
async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
|
|
478
|
+
if (!info) {
|
|
479
|
+
info = {};
|
|
480
|
+
}
|
|
481
|
+
if (!info.failCause) {
|
|
482
|
+
info.failCause = {};
|
|
483
|
+
}
|
|
484
|
+
if (!info.log) {
|
|
485
|
+
info.log = "";
|
|
486
|
+
}
|
|
476
487
|
let locatorSearch = selectorHierarchy[index];
|
|
488
|
+
try {
|
|
489
|
+
locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
|
|
490
|
+
}
|
|
491
|
+
catch (e) {
|
|
492
|
+
console.error(e);
|
|
493
|
+
}
|
|
477
494
|
//info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
|
|
478
495
|
let locator = null;
|
|
479
496
|
if (locatorSearch.climb && locatorSearch.climb >= 0) {
|
|
480
497
|
let locatorString = await this._locateElmentByTextClimbCss(scope, locatorSearch.text, locatorSearch.climb, locatorSearch.css, _params);
|
|
481
498
|
if (!locatorString) {
|
|
499
|
+
info.failCause.textNotFound = true;
|
|
500
|
+
info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
|
|
482
501
|
return;
|
|
483
502
|
}
|
|
484
503
|
locator = this._getLocator({ css: locatorString }, scope, _params);
|
|
485
504
|
}
|
|
486
505
|
else if (locatorSearch.text) {
|
|
487
|
-
let
|
|
506
|
+
let text = this._fixUsingParams(locatorSearch.text, _params);
|
|
507
|
+
let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
|
|
488
508
|
if (result.elementCount === 0) {
|
|
509
|
+
info.failCause.textNotFound = true;
|
|
510
|
+
info.failCause.lastError = "failed to locate element by text: " + text;
|
|
489
511
|
return;
|
|
490
512
|
}
|
|
491
513
|
locatorSearch.css = "[data-blinq-id='blinq-id-" + result.randomToken + "']";
|
|
@@ -502,6 +524,9 @@ class StableBrowser {
|
|
|
502
524
|
// cssHref = true;
|
|
503
525
|
// }
|
|
504
526
|
let count = await locator.count();
|
|
527
|
+
if (count > 0 && !info.failCause.count) {
|
|
528
|
+
info.failCause.count = count;
|
|
529
|
+
}
|
|
505
530
|
//info.log += "total elements found " + count + "\n";
|
|
506
531
|
//let visibleCount = 0;
|
|
507
532
|
let visibleLocator = null;
|
|
@@ -519,6 +544,8 @@ class StableBrowser {
|
|
|
519
544
|
foundLocators.push(locator.nth(j));
|
|
520
545
|
}
|
|
521
546
|
else {
|
|
547
|
+
info.failCause.visible = visible;
|
|
548
|
+
info.failCause.enabled = enabled;
|
|
522
549
|
if (!info.printMessages) {
|
|
523
550
|
info.printMessages = {};
|
|
524
551
|
}
|
|
@@ -530,6 +557,11 @@ class StableBrowser {
|
|
|
530
557
|
}
|
|
531
558
|
}
|
|
532
559
|
async closeUnexpectedPopups(info, _params) {
|
|
560
|
+
if (!info) {
|
|
561
|
+
info = {};
|
|
562
|
+
info.failCause = {};
|
|
563
|
+
info.log = "";
|
|
564
|
+
}
|
|
533
565
|
if (this.configuration.popupHandlers && this.configuration.popupHandlers.length > 0) {
|
|
534
566
|
if (!info) {
|
|
535
567
|
info = {};
|
|
@@ -570,7 +602,10 @@ class StableBrowser {
|
|
|
570
602
|
}
|
|
571
603
|
return { rerun: false };
|
|
572
604
|
}
|
|
573
|
-
async _locate(selectors, info, _params, timeout
|
|
605
|
+
async _locate(selectors, info, _params, timeout) {
|
|
606
|
+
if (!timeout) {
|
|
607
|
+
timeout = 30000;
|
|
608
|
+
}
|
|
574
609
|
for (let i = 0; i < 3; i++) {
|
|
575
610
|
info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
|
|
576
611
|
for (let j = 0; j < selectors.locators.length; j++) {
|
|
@@ -584,35 +619,46 @@ class StableBrowser {
|
|
|
584
619
|
}
|
|
585
620
|
throw new Error("unable to locate element " + JSON.stringify(selectors));
|
|
586
621
|
}
|
|
587
|
-
async
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
622
|
+
async _findFrameScope(selectors, timeout = 30000, info) {
|
|
623
|
+
if (!info) {
|
|
624
|
+
info = {};
|
|
625
|
+
info.failCause = {};
|
|
626
|
+
info.log = "";
|
|
627
|
+
}
|
|
593
628
|
let scope = this.page;
|
|
629
|
+
if (selectors.frame) {
|
|
630
|
+
return selectors.frame;
|
|
631
|
+
}
|
|
594
632
|
if (selectors.iframe_src || selectors.frameLocators) {
|
|
595
|
-
const findFrame = (frame, framescope) => {
|
|
633
|
+
const findFrame = async (frame, framescope) => {
|
|
596
634
|
for (let i = 0; i < frame.selectors.length; i++) {
|
|
597
635
|
let frameLocator = frame.selectors[i];
|
|
598
636
|
if (frameLocator.css) {
|
|
599
|
-
|
|
637
|
+
let testframescope = framescope.frameLocator(frameLocator.css);
|
|
600
638
|
if (frameLocator.index) {
|
|
601
|
-
|
|
639
|
+
testframescope = framescope.nth(frameLocator.index);
|
|
640
|
+
}
|
|
641
|
+
try {
|
|
642
|
+
await testframescope.owner().evaluateHandle(() => true, null, {
|
|
643
|
+
timeout: 5000,
|
|
644
|
+
});
|
|
645
|
+
framescope = testframescope;
|
|
646
|
+
break;
|
|
647
|
+
}
|
|
648
|
+
catch (error) {
|
|
649
|
+
console.error("frame not found " + frameLocator.css);
|
|
602
650
|
}
|
|
603
|
-
break;
|
|
604
651
|
}
|
|
605
652
|
}
|
|
606
653
|
if (frame.children) {
|
|
607
|
-
return findFrame(frame.children, framescope);
|
|
654
|
+
return await findFrame(frame.children, framescope);
|
|
608
655
|
}
|
|
609
656
|
return framescope;
|
|
610
657
|
};
|
|
611
|
-
info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
|
|
612
658
|
while (true) {
|
|
613
659
|
let frameFound = false;
|
|
614
660
|
if (selectors.nestFrmLoc) {
|
|
615
|
-
scope = findFrame(selectors.nestFrmLoc, scope);
|
|
661
|
+
scope = await findFrame(selectors.nestFrmLoc, scope);
|
|
616
662
|
frameFound = true;
|
|
617
663
|
break;
|
|
618
664
|
}
|
|
@@ -632,6 +678,8 @@ class StableBrowser {
|
|
|
632
678
|
if (!scope) {
|
|
633
679
|
info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
|
|
634
680
|
if (performance.now() - startTime > timeout) {
|
|
681
|
+
info.failCause.iframeNotFound = true;
|
|
682
|
+
info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
|
|
635
683
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
636
684
|
}
|
|
637
685
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -641,6 +689,30 @@ class StableBrowser {
|
|
|
641
689
|
}
|
|
642
690
|
}
|
|
643
691
|
}
|
|
692
|
+
if (!scope) {
|
|
693
|
+
scope = this.page;
|
|
694
|
+
}
|
|
695
|
+
return scope;
|
|
696
|
+
}
|
|
697
|
+
async _getDocumentBody(selectors, timeout = 30000, info) {
|
|
698
|
+
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
699
|
+
return scope.evaluate(() => {
|
|
700
|
+
var bodyContent = document.body.innerHTML;
|
|
701
|
+
return bodyContent;
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
705
|
+
if (!info) {
|
|
706
|
+
info = {};
|
|
707
|
+
info.failCause = {};
|
|
708
|
+
info.log = "";
|
|
709
|
+
}
|
|
710
|
+
let highPriorityTimeout = 5000;
|
|
711
|
+
let visibleOnlyTimeout = 6000;
|
|
712
|
+
let startTime = performance.now();
|
|
713
|
+
let locatorsCount = 0;
|
|
714
|
+
//let arrayMode = Array.isArray(selectors);
|
|
715
|
+
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
644
716
|
let selectorsLocators = null;
|
|
645
717
|
selectorsLocators = selectors.locators;
|
|
646
718
|
// group selectors by priority
|
|
@@ -742,6 +814,8 @@ class StableBrowser {
|
|
|
742
814
|
}
|
|
743
815
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
744
816
|
info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
|
|
817
|
+
info.failCause.locatorNotFound = true;
|
|
818
|
+
info.failCause.lastError = "failed to locate unique element";
|
|
745
819
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
746
820
|
}
|
|
747
821
|
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
@@ -773,89 +847,129 @@ class StableBrowser {
|
|
|
773
847
|
});
|
|
774
848
|
result.locatorIndex = i;
|
|
775
849
|
}
|
|
850
|
+
if (foundLocators.length > 1) {
|
|
851
|
+
info.failCause.foundMultiple = true;
|
|
852
|
+
}
|
|
776
853
|
}
|
|
777
854
|
return result;
|
|
778
855
|
}
|
|
779
|
-
async
|
|
780
|
-
this._validateSelectors(selectors);
|
|
856
|
+
async simpleClick(elementDescription, _params, options = {}, world = null) {
|
|
781
857
|
const startTime = Date.now();
|
|
782
|
-
|
|
783
|
-
|
|
858
|
+
let timeout = 30000;
|
|
859
|
+
if (options && options.timeout) {
|
|
860
|
+
timeout = options.timeout;
|
|
784
861
|
}
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
862
|
+
while (true) {
|
|
863
|
+
try {
|
|
864
|
+
const result = await locate_element(this.context, elementDescription, "click");
|
|
865
|
+
if (result?.elementNumber >= 0) {
|
|
866
|
+
const selectors = {
|
|
867
|
+
frame: result?.frame,
|
|
868
|
+
locators: [
|
|
869
|
+
{
|
|
870
|
+
css: result?.css,
|
|
871
|
+
},
|
|
872
|
+
],
|
|
873
|
+
};
|
|
874
|
+
await this.click(selectors, _params, options, world);
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
catch (e) {
|
|
879
|
+
if (performance.now() - startTime > timeout) {
|
|
880
|
+
// throw e;
|
|
881
|
+
await _commandError({ text: "simpleClick", operation: "simpleClick", elementDescription, info: {} }, e, this);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
|
|
888
|
+
const startTime = Date.now();
|
|
889
|
+
let timeout = 30000;
|
|
890
|
+
if (options && options.timeout) {
|
|
891
|
+
timeout = options.timeout;
|
|
892
|
+
}
|
|
893
|
+
while (true) {
|
|
894
|
+
try {
|
|
895
|
+
const result = await locate_element(this.context, elementDescription, "fill", value);
|
|
896
|
+
if (result?.elementNumber >= 0) {
|
|
897
|
+
const selectors = {
|
|
898
|
+
frame: result?.frame,
|
|
899
|
+
locators: [
|
|
900
|
+
{
|
|
901
|
+
css: result?.css,
|
|
902
|
+
},
|
|
903
|
+
],
|
|
904
|
+
};
|
|
905
|
+
await this.clickType(selectors, value, false, _params, options, world);
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
catch (e) {
|
|
910
|
+
if (performance.now() - startTime > timeout) {
|
|
911
|
+
// throw e;
|
|
912
|
+
await _commandError({ text: "simpleClickType", operation: "simpleClickType", value, elementDescription, info: {} }, e, this);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
async click(selectors, _params, options = {}, world = null) {
|
|
919
|
+
const state = {
|
|
920
|
+
selectors,
|
|
921
|
+
_params,
|
|
922
|
+
options,
|
|
923
|
+
world,
|
|
924
|
+
text: "Click element",
|
|
925
|
+
type: Types.CLICK,
|
|
926
|
+
operation: "click",
|
|
927
|
+
log: "***** click on " + selectors.element_name + " *****\n",
|
|
928
|
+
};
|
|
792
929
|
try {
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
930
|
+
await _preCommand(state, this);
|
|
931
|
+
if (state.options && state.options.context) {
|
|
932
|
+
state.selectors.locators[0].text = state.options.context;
|
|
933
|
+
}
|
|
796
934
|
try {
|
|
797
|
-
await
|
|
798
|
-
await
|
|
799
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
935
|
+
await state.element.click();
|
|
936
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
800
937
|
}
|
|
801
938
|
catch (e) {
|
|
802
939
|
// await this.closeUnexpectedPopups();
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
await
|
|
806
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
940
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
941
|
+
await state.element.dispatchEvent("click");
|
|
942
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
807
943
|
}
|
|
808
944
|
await this.waitForPageLoad();
|
|
809
|
-
return info;
|
|
945
|
+
return state.info;
|
|
810
946
|
}
|
|
811
947
|
catch (e) {
|
|
812
|
-
|
|
813
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
814
|
-
info.screenshotPath = screenshotPath;
|
|
815
|
-
Object.assign(e, { info: info });
|
|
816
|
-
error = e;
|
|
817
|
-
throw e;
|
|
948
|
+
await _commandError(state, e, this);
|
|
818
949
|
}
|
|
819
950
|
finally {
|
|
820
|
-
|
|
821
|
-
this._reportToWorld(world, {
|
|
822
|
-
element_name: selectors.element_name,
|
|
823
|
-
type: Types.CLICK,
|
|
824
|
-
text: `Click element`,
|
|
825
|
-
screenshotId,
|
|
826
|
-
result: error
|
|
827
|
-
? {
|
|
828
|
-
status: "FAILED",
|
|
829
|
-
startTime,
|
|
830
|
-
endTime,
|
|
831
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
832
|
-
}
|
|
833
|
-
: {
|
|
834
|
-
status: "PASSED",
|
|
835
|
-
startTime,
|
|
836
|
-
endTime,
|
|
837
|
-
},
|
|
838
|
-
info: info,
|
|
839
|
-
});
|
|
951
|
+
_commandFinally(state, this);
|
|
840
952
|
}
|
|
841
953
|
}
|
|
842
954
|
async setCheck(selectors, checked = true, _params, options = {}, world = null) {
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
955
|
+
const state = {
|
|
956
|
+
selectors,
|
|
957
|
+
_params,
|
|
958
|
+
options,
|
|
959
|
+
world,
|
|
960
|
+
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
961
|
+
text: checked ? `Check element` : `Uncheck element`,
|
|
962
|
+
operation: "setCheck",
|
|
963
|
+
log: "***** check " + selectors.element_name + " *****\n",
|
|
964
|
+
};
|
|
853
965
|
try {
|
|
854
|
-
|
|
855
|
-
|
|
966
|
+
await _preCommand(state, this);
|
|
967
|
+
state.info.checked = checked;
|
|
968
|
+
// let element = await this._locate(selectors, info, _params);
|
|
969
|
+
// ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
856
970
|
try {
|
|
857
|
-
await this._highlightElements(element);
|
|
858
|
-
await element.setChecked(checked);
|
|
971
|
+
// await this._highlightElements(element);
|
|
972
|
+
await state.element.setChecked(checked);
|
|
859
973
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
860
974
|
}
|
|
861
975
|
catch (e) {
|
|
@@ -864,179 +978,108 @@ class StableBrowser {
|
|
|
864
978
|
}
|
|
865
979
|
else {
|
|
866
980
|
//await this.closeUnexpectedPopups();
|
|
867
|
-
info.log += "setCheck failed, will try again" + "\n";
|
|
868
|
-
element = await this._locate(selectors, info, _params);
|
|
869
|
-
await element.setChecked(checked, { timeout: 5000, force: true });
|
|
981
|
+
state.info.log += "setCheck failed, will try again" + "\n";
|
|
982
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
983
|
+
await state.element.setChecked(checked, { timeout: 5000, force: true });
|
|
870
984
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
871
985
|
}
|
|
872
986
|
}
|
|
873
987
|
await this.waitForPageLoad();
|
|
874
|
-
return info;
|
|
988
|
+
return state.info;
|
|
875
989
|
}
|
|
876
990
|
catch (e) {
|
|
877
|
-
|
|
878
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
879
|
-
info.screenshotPath = screenshotPath;
|
|
880
|
-
Object.assign(e, { info: info });
|
|
881
|
-
error = e;
|
|
882
|
-
throw e;
|
|
991
|
+
await _commandError(state, e, this);
|
|
883
992
|
}
|
|
884
993
|
finally {
|
|
885
|
-
|
|
886
|
-
this._reportToWorld(world, {
|
|
887
|
-
element_name: selectors.element_name,
|
|
888
|
-
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
889
|
-
text: checked ? `Check element` : `Uncheck element`,
|
|
890
|
-
screenshotId,
|
|
891
|
-
result: error
|
|
892
|
-
? {
|
|
893
|
-
status: "FAILED",
|
|
894
|
-
startTime,
|
|
895
|
-
endTime,
|
|
896
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
897
|
-
}
|
|
898
|
-
: {
|
|
899
|
-
status: "PASSED",
|
|
900
|
-
startTime,
|
|
901
|
-
endTime,
|
|
902
|
-
},
|
|
903
|
-
info: info,
|
|
904
|
-
});
|
|
994
|
+
_commandFinally(state, this);
|
|
905
995
|
}
|
|
906
996
|
}
|
|
907
997
|
async hover(selectors, _params, options = {}, world = null) {
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
998
|
+
const state = {
|
|
999
|
+
selectors,
|
|
1000
|
+
_params,
|
|
1001
|
+
options,
|
|
1002
|
+
world,
|
|
1003
|
+
type: Types.HOVER,
|
|
1004
|
+
text: `Hover element`,
|
|
1005
|
+
operation: "hover",
|
|
1006
|
+
log: "***** hover " + selectors.element_name + " *****\n",
|
|
1007
|
+
};
|
|
917
1008
|
try {
|
|
918
|
-
|
|
919
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1009
|
+
await _preCommand(state, this);
|
|
920
1010
|
try {
|
|
921
|
-
await
|
|
922
|
-
await element.hover();
|
|
1011
|
+
await state.element.hover();
|
|
923
1012
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
924
1013
|
}
|
|
925
1014
|
catch (e) {
|
|
926
1015
|
//await this.closeUnexpectedPopups();
|
|
927
|
-
info.log += "hover failed, will try again" + "\n";
|
|
928
|
-
element = await this._locate(selectors, info, _params);
|
|
929
|
-
await element.hover({ timeout: 10000 });
|
|
1016
|
+
state.info.log += "hover failed, will try again" + "\n";
|
|
1017
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
1018
|
+
await state.element.hover({ timeout: 10000 });
|
|
930
1019
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
931
1020
|
}
|
|
932
1021
|
await this.waitForPageLoad();
|
|
933
|
-
return info;
|
|
1022
|
+
return state.info;
|
|
934
1023
|
}
|
|
935
1024
|
catch (e) {
|
|
936
|
-
|
|
937
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
938
|
-
info.screenshotPath = screenshotPath;
|
|
939
|
-
Object.assign(e, { info: info });
|
|
940
|
-
error = e;
|
|
941
|
-
throw e;
|
|
1025
|
+
await _commandError(state, e, this);
|
|
942
1026
|
}
|
|
943
1027
|
finally {
|
|
944
|
-
|
|
945
|
-
this._reportToWorld(world, {
|
|
946
|
-
element_name: selectors.element_name,
|
|
947
|
-
type: Types.HOVER,
|
|
948
|
-
text: `Hover element`,
|
|
949
|
-
screenshotId,
|
|
950
|
-
result: error
|
|
951
|
-
? {
|
|
952
|
-
status: "FAILED",
|
|
953
|
-
startTime,
|
|
954
|
-
endTime,
|
|
955
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
956
|
-
}
|
|
957
|
-
: {
|
|
958
|
-
status: "PASSED",
|
|
959
|
-
startTime,
|
|
960
|
-
endTime,
|
|
961
|
-
},
|
|
962
|
-
info: info,
|
|
963
|
-
});
|
|
1028
|
+
_commandFinally(state, this);
|
|
964
1029
|
}
|
|
965
1030
|
}
|
|
966
1031
|
async selectOption(selectors, values, _params = null, options = {}, world = null) {
|
|
967
|
-
this._validateSelectors(selectors);
|
|
968
1032
|
if (!values) {
|
|
969
1033
|
throw new Error("values is null");
|
|
970
1034
|
}
|
|
971
|
-
const
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
1035
|
+
const state = {
|
|
1036
|
+
selectors,
|
|
1037
|
+
_params,
|
|
1038
|
+
options,
|
|
1039
|
+
world,
|
|
1040
|
+
value: values.toString(),
|
|
1041
|
+
type: Types.SELECT,
|
|
1042
|
+
text: `Select option: ${values}`,
|
|
1043
|
+
operation: "selectOption",
|
|
1044
|
+
log: "***** select option " + selectors.element_name + " *****\n",
|
|
1045
|
+
};
|
|
979
1046
|
try {
|
|
980
|
-
|
|
981
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1047
|
+
await _preCommand(state, this);
|
|
982
1048
|
try {
|
|
983
|
-
await
|
|
984
|
-
await element.selectOption(values);
|
|
1049
|
+
await state.element.selectOption(values);
|
|
985
1050
|
}
|
|
986
1051
|
catch (e) {
|
|
987
1052
|
//await this.closeUnexpectedPopups();
|
|
988
|
-
info.log += "selectOption failed, will try force" + "\n";
|
|
989
|
-
await element.selectOption(values, { timeout: 10000, force: true });
|
|
1053
|
+
state.info.log += "selectOption failed, will try force" + "\n";
|
|
1054
|
+
await state.element.selectOption(values, { timeout: 10000, force: true });
|
|
990
1055
|
}
|
|
991
1056
|
await this.waitForPageLoad();
|
|
992
|
-
return info;
|
|
1057
|
+
return state.info;
|
|
993
1058
|
}
|
|
994
1059
|
catch (e) {
|
|
995
|
-
|
|
996
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
997
|
-
info.screenshotPath = screenshotPath;
|
|
998
|
-
Object.assign(e, { info: info });
|
|
999
|
-
this.logger.info("click failed, will try next selector");
|
|
1000
|
-
error = e;
|
|
1001
|
-
throw e;
|
|
1060
|
+
await _commandError(state, e, this);
|
|
1002
1061
|
}
|
|
1003
1062
|
finally {
|
|
1004
|
-
|
|
1005
|
-
this._reportToWorld(world, {
|
|
1006
|
-
element_name: selectors.element_name,
|
|
1007
|
-
type: Types.SELECT,
|
|
1008
|
-
text: `Select option: ${values}`,
|
|
1009
|
-
value: values.toString(),
|
|
1010
|
-
screenshotId,
|
|
1011
|
-
result: error
|
|
1012
|
-
? {
|
|
1013
|
-
status: "FAILED",
|
|
1014
|
-
startTime,
|
|
1015
|
-
endTime,
|
|
1016
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1017
|
-
}
|
|
1018
|
-
: {
|
|
1019
|
-
status: "PASSED",
|
|
1020
|
-
startTime,
|
|
1021
|
-
endTime,
|
|
1022
|
-
},
|
|
1023
|
-
info: info,
|
|
1024
|
-
});
|
|
1063
|
+
_commandFinally(state, this);
|
|
1025
1064
|
}
|
|
1026
1065
|
}
|
|
1027
1066
|
async type(_value, _params = null, options = {}, world = null) {
|
|
1028
|
-
const
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1067
|
+
const state = {
|
|
1068
|
+
value: _value,
|
|
1069
|
+
_params,
|
|
1070
|
+
options,
|
|
1071
|
+
world,
|
|
1072
|
+
locate: false,
|
|
1073
|
+
scroll: false,
|
|
1074
|
+
highlight: false,
|
|
1075
|
+
type: Types.TYPE_PRESS,
|
|
1076
|
+
text: `Type value: ${_value}`,
|
|
1077
|
+
operation: "type",
|
|
1078
|
+
log: "",
|
|
1079
|
+
};
|
|
1037
1080
|
try {
|
|
1038
|
-
|
|
1039
|
-
const valueSegment =
|
|
1081
|
+
await _preCommand(state, this);
|
|
1082
|
+
const valueSegment = state.value.split("&&");
|
|
1040
1083
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1041
1084
|
if (i > 0) {
|
|
1042
1085
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1056,134 +1099,76 @@ class StableBrowser {
|
|
|
1056
1099
|
await this.page.keyboard.type(value);
|
|
1057
1100
|
}
|
|
1058
1101
|
}
|
|
1059
|
-
return info;
|
|
1102
|
+
return state.info;
|
|
1060
1103
|
}
|
|
1061
1104
|
catch (e) {
|
|
1062
|
-
|
|
1063
|
-
this.logger.error("type failed " + info.log);
|
|
1064
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1065
|
-
info.screenshotPath = screenshotPath;
|
|
1066
|
-
Object.assign(e, { info: info });
|
|
1067
|
-
error = e;
|
|
1068
|
-
throw e;
|
|
1105
|
+
await _commandError(state, e, this);
|
|
1069
1106
|
}
|
|
1070
1107
|
finally {
|
|
1071
|
-
|
|
1072
|
-
this._reportToWorld(world, {
|
|
1073
|
-
type: Types.TYPE_PRESS,
|
|
1074
|
-
screenshotId,
|
|
1075
|
-
value: _value,
|
|
1076
|
-
text: `type value: ${_value}`,
|
|
1077
|
-
result: error
|
|
1078
|
-
? {
|
|
1079
|
-
status: "FAILED",
|
|
1080
|
-
startTime,
|
|
1081
|
-
endTime,
|
|
1082
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1083
|
-
}
|
|
1084
|
-
: {
|
|
1085
|
-
status: "PASSED",
|
|
1086
|
-
startTime,
|
|
1087
|
-
endTime,
|
|
1088
|
-
},
|
|
1089
|
-
info: info,
|
|
1090
|
-
});
|
|
1108
|
+
_commandFinally(state, this);
|
|
1091
1109
|
}
|
|
1092
1110
|
}
|
|
1093
1111
|
async setInputValue(selectors, value, _params = null, options = {}, world = null) {
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
let screenshotPath = null;
|
|
1112
|
+
const state = {
|
|
1113
|
+
selectors,
|
|
1114
|
+
_params,
|
|
1115
|
+
value,
|
|
1116
|
+
options,
|
|
1117
|
+
world,
|
|
1118
|
+
type: Types.SET_INPUT,
|
|
1119
|
+
text: `Set input value`,
|
|
1120
|
+
operation: "setInputValue",
|
|
1121
|
+
log: "***** set input value " + selectors.element_name + " *****\n",
|
|
1122
|
+
};
|
|
1106
1123
|
try {
|
|
1107
|
-
|
|
1108
|
-
let
|
|
1109
|
-
await this.scrollIfNeeded(element, info);
|
|
1110
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1111
|
-
await this._highlightElements(element);
|
|
1124
|
+
await _preCommand(state, this);
|
|
1125
|
+
let value = await this._replaceWithLocalData(state.value, this);
|
|
1112
1126
|
try {
|
|
1113
|
-
await element.evaluateHandle((el, value) => {
|
|
1127
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1114
1128
|
el.value = value;
|
|
1115
1129
|
}, value);
|
|
1116
1130
|
}
|
|
1117
1131
|
catch (error) {
|
|
1118
1132
|
this.logger.error("setInputValue failed, will try again");
|
|
1119
|
-
|
|
1120
|
-
info.
|
|
1121
|
-
|
|
1122
|
-
await element.evaluateHandle((el, value) => {
|
|
1133
|
+
await _screenshot(state, this);
|
|
1134
|
+
Object.assign(error, { info: state.info });
|
|
1135
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1123
1136
|
el.value = value;
|
|
1124
1137
|
});
|
|
1125
1138
|
}
|
|
1126
1139
|
}
|
|
1127
1140
|
catch (e) {
|
|
1128
|
-
|
|
1129
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1130
|
-
info.screenshotPath = screenshotPath;
|
|
1131
|
-
Object.assign(e, { info: info });
|
|
1132
|
-
error = e;
|
|
1133
|
-
throw e;
|
|
1141
|
+
await _commandError(state, e, this);
|
|
1134
1142
|
}
|
|
1135
1143
|
finally {
|
|
1136
|
-
|
|
1137
|
-
this._reportToWorld(world, {
|
|
1138
|
-
element_name: selectors.element_name,
|
|
1139
|
-
type: Types.SET_INPUT,
|
|
1140
|
-
text: `Set input value`,
|
|
1141
|
-
value: value,
|
|
1142
|
-
screenshotId,
|
|
1143
|
-
result: error
|
|
1144
|
-
? {
|
|
1145
|
-
status: "FAILED",
|
|
1146
|
-
startTime,
|
|
1147
|
-
endTime,
|
|
1148
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1149
|
-
}
|
|
1150
|
-
: {
|
|
1151
|
-
status: "PASSED",
|
|
1152
|
-
startTime,
|
|
1153
|
-
endTime,
|
|
1154
|
-
},
|
|
1155
|
-
info: info,
|
|
1156
|
-
});
|
|
1144
|
+
_commandFinally(state, this);
|
|
1157
1145
|
}
|
|
1158
1146
|
}
|
|
1159
1147
|
async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1148
|
+
const state = {
|
|
1149
|
+
selectors,
|
|
1150
|
+
_params,
|
|
1151
|
+
value: await this._replaceWithLocalData(value, this),
|
|
1152
|
+
options,
|
|
1153
|
+
world,
|
|
1154
|
+
type: Types.SET_DATE_TIME,
|
|
1155
|
+
text: `Set date time value: ${value}`,
|
|
1156
|
+
operation: "setDateTime",
|
|
1157
|
+
log: "***** set date time value " + selectors.element_name + " *****\n",
|
|
1158
|
+
throwError: false,
|
|
1159
|
+
};
|
|
1170
1160
|
try {
|
|
1171
|
-
|
|
1172
|
-
let element = await this._locate(selectors, info, _params);
|
|
1173
|
-
//insert red border around the element
|
|
1174
|
-
await this.scrollIfNeeded(element, info);
|
|
1175
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1176
|
-
await this._highlightElements(element);
|
|
1161
|
+
await _preCommand(state, this);
|
|
1177
1162
|
try {
|
|
1178
|
-
await element.click();
|
|
1163
|
+
await state.element.click();
|
|
1179
1164
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1180
1165
|
if (format) {
|
|
1181
|
-
value = dayjs(value).format(format);
|
|
1182
|
-
await element.fill(value);
|
|
1166
|
+
state.value = dayjs(state.value).format(format);
|
|
1167
|
+
await state.element.fill(state.value);
|
|
1183
1168
|
}
|
|
1184
1169
|
else {
|
|
1185
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1186
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1170
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1171
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1187
1172
|
el.value = ""; // clear input
|
|
1188
1173
|
el.value = dateTimeValue;
|
|
1189
1174
|
}, dateTimeValue);
|
|
@@ -1196,20 +1181,19 @@ class StableBrowser {
|
|
|
1196
1181
|
}
|
|
1197
1182
|
catch (err) {
|
|
1198
1183
|
//await this.closeUnexpectedPopups();
|
|
1199
|
-
this.logger.error("setting date time input failed " + JSON.stringify(info));
|
|
1184
|
+
this.logger.error("setting date time input failed " + JSON.stringify(state.info));
|
|
1200
1185
|
this.logger.info("Trying again");
|
|
1201
|
-
|
|
1202
|
-
info.
|
|
1203
|
-
Object.assign(err, { info: info });
|
|
1186
|
+
await _screenshot(state, this);
|
|
1187
|
+
Object.assign(err, { info: state.info });
|
|
1204
1188
|
await element.click();
|
|
1205
1189
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1206
1190
|
if (format) {
|
|
1207
|
-
value = dayjs(value).format(format);
|
|
1208
|
-
await element.fill(value);
|
|
1191
|
+
state.value = dayjs(state.value).format(format);
|
|
1192
|
+
await state.element.fill(state.value);
|
|
1209
1193
|
}
|
|
1210
1194
|
else {
|
|
1211
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1212
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1195
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1196
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1213
1197
|
el.value = ""; // clear input
|
|
1214
1198
|
el.value = dateTimeValue;
|
|
1215
1199
|
}, dateTimeValue);
|
|
@@ -1222,61 +1206,39 @@ class StableBrowser {
|
|
|
1222
1206
|
}
|
|
1223
1207
|
}
|
|
1224
1208
|
catch (e) {
|
|
1225
|
-
|
|
1226
|
-
throw e;
|
|
1209
|
+
await _commandError(state, e, this);
|
|
1227
1210
|
}
|
|
1228
1211
|
finally {
|
|
1229
|
-
|
|
1230
|
-
this._reportToWorld(world, {
|
|
1231
|
-
element_name: selectors.element_name,
|
|
1232
|
-
type: Types.SET_DATE_TIME,
|
|
1233
|
-
screenshotId,
|
|
1234
|
-
value: value,
|
|
1235
|
-
text: `setDateTime input with value: ${value}`,
|
|
1236
|
-
result: error
|
|
1237
|
-
? {
|
|
1238
|
-
status: "FAILED",
|
|
1239
|
-
startTime,
|
|
1240
|
-
endTime,
|
|
1241
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1242
|
-
}
|
|
1243
|
-
: {
|
|
1244
|
-
status: "PASSED",
|
|
1245
|
-
startTime,
|
|
1246
|
-
endTime,
|
|
1247
|
-
},
|
|
1248
|
-
info: info,
|
|
1249
|
-
});
|
|
1212
|
+
_commandFinally(state, this);
|
|
1250
1213
|
}
|
|
1251
1214
|
}
|
|
1252
1215
|
async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
|
|
1253
1216
|
_value = unEscapeString(_value);
|
|
1254
|
-
this._validateSelectors(selectors);
|
|
1255
|
-
const startTime = Date.now();
|
|
1256
|
-
let error = null;
|
|
1257
|
-
let screenshotId = null;
|
|
1258
|
-
let screenshotPath = null;
|
|
1259
|
-
const info = {};
|
|
1260
|
-
info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
|
|
1261
|
-
info.operation = "clickType";
|
|
1262
|
-
info.selectors = selectors;
|
|
1263
1217
|
const newValue = await this._replaceWithLocalData(_value, world);
|
|
1218
|
+
const state = {
|
|
1219
|
+
selectors,
|
|
1220
|
+
_params,
|
|
1221
|
+
value: newValue,
|
|
1222
|
+
originalValue: _value,
|
|
1223
|
+
options,
|
|
1224
|
+
world,
|
|
1225
|
+
type: Types.FILL,
|
|
1226
|
+
text: `Click type input with value: ${_value}`,
|
|
1227
|
+
operation: "clickType",
|
|
1228
|
+
log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
|
|
1229
|
+
};
|
|
1264
1230
|
if (newValue !== _value) {
|
|
1265
1231
|
//this.logger.info(_value + "=" + newValue);
|
|
1266
1232
|
_value = newValue;
|
|
1267
1233
|
}
|
|
1268
|
-
info.value = _value;
|
|
1269
1234
|
try {
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
await this.scrollIfNeeded(element, info);
|
|
1273
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1274
|
-
await this._highlightElements(element);
|
|
1235
|
+
await _preCommand(state, this);
|
|
1236
|
+
state.info.value = _value;
|
|
1275
1237
|
if (options === null || options === undefined || !options.press) {
|
|
1276
1238
|
try {
|
|
1277
|
-
let currentValue = await element.inputValue();
|
|
1239
|
+
let currentValue = await state.element.inputValue();
|
|
1278
1240
|
if (currentValue) {
|
|
1279
|
-
await element.fill("");
|
|
1241
|
+
await state.element.fill("");
|
|
1280
1242
|
}
|
|
1281
1243
|
}
|
|
1282
1244
|
catch (e) {
|
|
@@ -1285,22 +1247,22 @@ class StableBrowser {
|
|
|
1285
1247
|
}
|
|
1286
1248
|
if (options === null || options === undefined || options.press) {
|
|
1287
1249
|
try {
|
|
1288
|
-
await element.click({ timeout: 5000 });
|
|
1250
|
+
await state.element.click({ timeout: 5000 });
|
|
1289
1251
|
}
|
|
1290
1252
|
catch (e) {
|
|
1291
|
-
await element.dispatchEvent("click");
|
|
1253
|
+
await state.element.dispatchEvent("click");
|
|
1292
1254
|
}
|
|
1293
1255
|
}
|
|
1294
1256
|
else {
|
|
1295
1257
|
try {
|
|
1296
|
-
await element.focus();
|
|
1258
|
+
await state.element.focus();
|
|
1297
1259
|
}
|
|
1298
1260
|
catch (e) {
|
|
1299
|
-
await element.dispatchEvent("focus");
|
|
1261
|
+
await state.element.dispatchEvent("focus");
|
|
1300
1262
|
}
|
|
1301
1263
|
}
|
|
1302
1264
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1303
|
-
const valueSegment =
|
|
1265
|
+
const valueSegment = state.value.split("&&");
|
|
1304
1266
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1305
1267
|
if (i > 0) {
|
|
1306
1268
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1320,13 +1282,14 @@ class StableBrowser {
|
|
|
1320
1282
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1321
1283
|
}
|
|
1322
1284
|
}
|
|
1285
|
+
await _screenshot(state, this);
|
|
1323
1286
|
if (enter === true) {
|
|
1324
1287
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1325
1288
|
await this.page.keyboard.press("Enter");
|
|
1326
1289
|
await this.waitForPageLoad();
|
|
1327
1290
|
}
|
|
1328
1291
|
else if (enter === false) {
|
|
1329
|
-
await element.dispatchEvent("change");
|
|
1292
|
+
await state.element.dispatchEvent("change");
|
|
1330
1293
|
//await this.page.keyboard.press("Tab");
|
|
1331
1294
|
}
|
|
1332
1295
|
else {
|
|
@@ -1335,104 +1298,50 @@ class StableBrowser {
|
|
|
1335
1298
|
await this.waitForPageLoad();
|
|
1336
1299
|
}
|
|
1337
1300
|
}
|
|
1338
|
-
return info;
|
|
1301
|
+
return state.info;
|
|
1339
1302
|
}
|
|
1340
1303
|
catch (e) {
|
|
1341
|
-
|
|
1342
|
-
this.logger.error("fill failed " + JSON.stringify(info));
|
|
1343
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1344
|
-
info.screenshotPath = screenshotPath;
|
|
1345
|
-
Object.assign(e, { info: info });
|
|
1346
|
-
error = e;
|
|
1347
|
-
throw e;
|
|
1304
|
+
await _commandError(state, e, this);
|
|
1348
1305
|
}
|
|
1349
1306
|
finally {
|
|
1350
|
-
|
|
1351
|
-
this._reportToWorld(world, {
|
|
1352
|
-
element_name: selectors.element_name,
|
|
1353
|
-
type: Types.FILL,
|
|
1354
|
-
screenshotId,
|
|
1355
|
-
value: _value,
|
|
1356
|
-
text: `clickType input with value: ${_value}`,
|
|
1357
|
-
result: error
|
|
1358
|
-
? {
|
|
1359
|
-
status: "FAILED",
|
|
1360
|
-
startTime,
|
|
1361
|
-
endTime,
|
|
1362
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1363
|
-
}
|
|
1364
|
-
: {
|
|
1365
|
-
status: "PASSED",
|
|
1366
|
-
startTime,
|
|
1367
|
-
endTime,
|
|
1368
|
-
},
|
|
1369
|
-
info: info,
|
|
1370
|
-
});
|
|
1307
|
+
_commandFinally(state, this);
|
|
1371
1308
|
}
|
|
1372
1309
|
}
|
|
1373
1310
|
async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1311
|
+
const state = {
|
|
1312
|
+
selectors,
|
|
1313
|
+
_params,
|
|
1314
|
+
value: unEscapeString(value),
|
|
1315
|
+
options,
|
|
1316
|
+
world,
|
|
1317
|
+
type: Types.FILL,
|
|
1318
|
+
text: `Fill input with value: ${value}`,
|
|
1319
|
+
operation: "fill",
|
|
1320
|
+
log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
|
|
1321
|
+
};
|
|
1385
1322
|
try {
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
await
|
|
1389
|
-
await element.fill(value);
|
|
1390
|
-
await element.dispatchEvent("change");
|
|
1323
|
+
await _preCommand(state, this);
|
|
1324
|
+
await state.element.fill(value);
|
|
1325
|
+
await state.element.dispatchEvent("change");
|
|
1391
1326
|
if (enter) {
|
|
1392
1327
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1393
1328
|
await this.page.keyboard.press("Enter");
|
|
1394
1329
|
}
|
|
1395
|
-
await this.waitForPageLoad();
|
|
1396
|
-
return info;
|
|
1397
|
-
}
|
|
1398
|
-
catch (e) {
|
|
1399
|
-
|
|
1400
|
-
this.logger.error("fill failed " + info.log);
|
|
1401
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1402
|
-
info.screenshotPath = screenshotPath;
|
|
1403
|
-
Object.assign(e, { info: info });
|
|
1404
|
-
error = e;
|
|
1405
|
-
throw e;
|
|
1330
|
+
await this.waitForPageLoad();
|
|
1331
|
+
return state.info;
|
|
1332
|
+
}
|
|
1333
|
+
catch (e) {
|
|
1334
|
+
await _commandError(state, e, this);
|
|
1406
1335
|
}
|
|
1407
1336
|
finally {
|
|
1408
|
-
|
|
1409
|
-
this._reportToWorld(world, {
|
|
1410
|
-
element_name: selectors.element_name,
|
|
1411
|
-
type: Types.FILL,
|
|
1412
|
-
screenshotId,
|
|
1413
|
-
value,
|
|
1414
|
-
text: `Fill input with value: ${value}`,
|
|
1415
|
-
result: error
|
|
1416
|
-
? {
|
|
1417
|
-
status: "FAILED",
|
|
1418
|
-
startTime,
|
|
1419
|
-
endTime,
|
|
1420
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1421
|
-
}
|
|
1422
|
-
: {
|
|
1423
|
-
status: "PASSED",
|
|
1424
|
-
startTime,
|
|
1425
|
-
endTime,
|
|
1426
|
-
},
|
|
1427
|
-
info: info,
|
|
1428
|
-
});
|
|
1337
|
+
_commandFinally(state, this);
|
|
1429
1338
|
}
|
|
1430
1339
|
}
|
|
1431
1340
|
async getText(selectors, _params = null, options = {}, info = {}, world = null) {
|
|
1432
1341
|
return await this._getText(selectors, 0, _params, options, info, world);
|
|
1433
1342
|
}
|
|
1434
1343
|
async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
|
|
1435
|
-
|
|
1344
|
+
_validateSelectors(selectors);
|
|
1436
1345
|
let screenshotId = null;
|
|
1437
1346
|
let screenshotPath = null;
|
|
1438
1347
|
if (!info.log) {
|
|
@@ -1476,166 +1385,124 @@ class StableBrowser {
|
|
|
1476
1385
|
}
|
|
1477
1386
|
}
|
|
1478
1387
|
async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
|
|
1479
|
-
var _a;
|
|
1480
|
-
this._validateSelectors(selectors);
|
|
1481
1388
|
if (!pattern) {
|
|
1482
1389
|
throw new Error("pattern is null");
|
|
1483
1390
|
}
|
|
1484
1391
|
if (!text) {
|
|
1485
1392
|
throw new Error("text is null");
|
|
1486
1393
|
}
|
|
1394
|
+
const state = {
|
|
1395
|
+
selectors,
|
|
1396
|
+
_params,
|
|
1397
|
+
pattern,
|
|
1398
|
+
value: pattern,
|
|
1399
|
+
options,
|
|
1400
|
+
world,
|
|
1401
|
+
locate: false,
|
|
1402
|
+
scroll: false,
|
|
1403
|
+
screenshot: false,
|
|
1404
|
+
highlight: false,
|
|
1405
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1406
|
+
text: `Verify element contains pattern: ${pattern}`,
|
|
1407
|
+
operation: "containsPattern",
|
|
1408
|
+
log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
|
|
1409
|
+
};
|
|
1487
1410
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1488
1411
|
if (newValue !== text) {
|
|
1489
1412
|
this.logger.info(text + "=" + newValue);
|
|
1490
1413
|
text = newValue;
|
|
1491
1414
|
}
|
|
1492
|
-
const startTime = Date.now();
|
|
1493
|
-
let error = null;
|
|
1494
|
-
let screenshotId = null;
|
|
1495
|
-
let screenshotPath = null;
|
|
1496
|
-
const info = {};
|
|
1497
|
-
info.log =
|
|
1498
|
-
"***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
|
|
1499
|
-
info.operation = "containsPattern";
|
|
1500
|
-
info.selectors = selectors;
|
|
1501
|
-
info.value = text;
|
|
1502
|
-
info.pattern = pattern;
|
|
1503
1415
|
let foundObj = null;
|
|
1504
1416
|
try {
|
|
1505
|
-
|
|
1417
|
+
await _preCommand(state, this);
|
|
1418
|
+
state.info.pattern = pattern;
|
|
1419
|
+
foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
|
|
1506
1420
|
if (foundObj && foundObj.element) {
|
|
1507
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1421
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1508
1422
|
}
|
|
1509
|
-
|
|
1423
|
+
await _screenshot(state, this);
|
|
1510
1424
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1511
1425
|
pattern = pattern.replace("{text}", escapedText);
|
|
1512
1426
|
let regex = new RegExp(pattern, "im");
|
|
1513
|
-
if (!regex.test(foundObj
|
|
1514
|
-
info.foundText = foundObj
|
|
1427
|
+
if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
|
|
1428
|
+
state.info.foundText = foundObj?.text;
|
|
1515
1429
|
throw new Error("element doesn't contain text " + text);
|
|
1516
1430
|
}
|
|
1517
|
-
return info;
|
|
1431
|
+
return state.info;
|
|
1518
1432
|
}
|
|
1519
1433
|
catch (e) {
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
|
|
1523
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1524
|
-
info.screenshotPath = screenshotPath;
|
|
1525
|
-
Object.assign(e, { info: info });
|
|
1526
|
-
error = e;
|
|
1527
|
-
throw e;
|
|
1434
|
+
this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
|
|
1435
|
+
await _commandError(state, e, this);
|
|
1528
1436
|
}
|
|
1529
1437
|
finally {
|
|
1530
|
-
|
|
1531
|
-
this._reportToWorld(world, {
|
|
1532
|
-
element_name: selectors.element_name,
|
|
1533
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1534
|
-
value: pattern,
|
|
1535
|
-
text: `Verify element contains pattern: ${pattern}`,
|
|
1536
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1537
|
-
result: error
|
|
1538
|
-
? {
|
|
1539
|
-
status: "FAILED",
|
|
1540
|
-
startTime,
|
|
1541
|
-
endTime,
|
|
1542
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1543
|
-
}
|
|
1544
|
-
: {
|
|
1545
|
-
status: "PASSED",
|
|
1546
|
-
startTime,
|
|
1547
|
-
endTime,
|
|
1548
|
-
},
|
|
1549
|
-
info: info,
|
|
1550
|
-
});
|
|
1438
|
+
_commandFinally(state, this);
|
|
1551
1439
|
}
|
|
1552
1440
|
}
|
|
1553
1441
|
async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1442
|
+
const state = {
|
|
1443
|
+
selectors,
|
|
1444
|
+
_params,
|
|
1445
|
+
value: text,
|
|
1446
|
+
options,
|
|
1447
|
+
world,
|
|
1448
|
+
locate: false,
|
|
1449
|
+
scroll: false,
|
|
1450
|
+
screenshot: false,
|
|
1451
|
+
highlight: false,
|
|
1452
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1453
|
+
text: `Verify element contains text: ${text}`,
|
|
1454
|
+
operation: "containsText",
|
|
1455
|
+
log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
|
|
1456
|
+
};
|
|
1557
1457
|
if (!text) {
|
|
1558
1458
|
throw new Error("text is null");
|
|
1559
1459
|
}
|
|
1560
|
-
|
|
1561
|
-
let error = null;
|
|
1562
|
-
let screenshotId = null;
|
|
1563
|
-
let screenshotPath = null;
|
|
1564
|
-
const info = {};
|
|
1565
|
-
info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
|
|
1566
|
-
info.operation = "containsText";
|
|
1567
|
-
info.selectors = selectors;
|
|
1460
|
+
text = unEscapeString(text);
|
|
1568
1461
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1569
1462
|
if (newValue !== text) {
|
|
1570
1463
|
this.logger.info(text + "=" + newValue);
|
|
1571
1464
|
text = newValue;
|
|
1572
1465
|
}
|
|
1573
|
-
info.value = text;
|
|
1574
1466
|
let foundObj = null;
|
|
1575
1467
|
try {
|
|
1576
|
-
|
|
1468
|
+
await _preCommand(state, this);
|
|
1469
|
+
foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
|
|
1577
1470
|
if (foundObj && foundObj.element) {
|
|
1578
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1471
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1579
1472
|
}
|
|
1580
|
-
|
|
1473
|
+
await _screenshot(state, this);
|
|
1581
1474
|
const dateAlternatives = findDateAlternatives(text);
|
|
1582
1475
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1583
1476
|
if (dateAlternatives.date) {
|
|
1584
1477
|
for (let i = 0; i < dateAlternatives.dates.length; i++) {
|
|
1585
|
-
if (
|
|
1586
|
-
|
|
1587
|
-
return info;
|
|
1478
|
+
if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
|
|
1479
|
+
foundObj?.value?.includes(dateAlternatives.dates[i])) {
|
|
1480
|
+
return state.info;
|
|
1588
1481
|
}
|
|
1589
1482
|
}
|
|
1590
1483
|
throw new Error("element doesn't contain text " + text);
|
|
1591
1484
|
}
|
|
1592
1485
|
else if (numberAlternatives.number) {
|
|
1593
1486
|
for (let i = 0; i < numberAlternatives.numbers.length; i++) {
|
|
1594
|
-
if (
|
|
1595
|
-
|
|
1596
|
-
return info;
|
|
1487
|
+
if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
|
|
1488
|
+
foundObj?.value?.includes(numberAlternatives.numbers[i])) {
|
|
1489
|
+
return state.info;
|
|
1597
1490
|
}
|
|
1598
1491
|
}
|
|
1599
1492
|
throw new Error("element doesn't contain text " + text);
|
|
1600
1493
|
}
|
|
1601
|
-
else if (!
|
|
1602
|
-
info.foundText = foundObj
|
|
1603
|
-
info.value = foundObj
|
|
1494
|
+
else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
|
|
1495
|
+
state.info.foundText = foundObj?.text;
|
|
1496
|
+
state.info.value = foundObj?.value;
|
|
1604
1497
|
throw new Error("element doesn't contain text " + text);
|
|
1605
1498
|
}
|
|
1606
|
-
return info;
|
|
1499
|
+
return state.info;
|
|
1607
1500
|
}
|
|
1608
1501
|
catch (e) {
|
|
1609
|
-
|
|
1610
|
-
this.logger.error("verify element contains text failed " + info.log);
|
|
1611
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1612
|
-
info.screenshotPath = screenshotPath;
|
|
1613
|
-
Object.assign(e, { info: info });
|
|
1614
|
-
error = e;
|
|
1615
|
-
throw e;
|
|
1502
|
+
await _commandError(state, e, this);
|
|
1616
1503
|
}
|
|
1617
1504
|
finally {
|
|
1618
|
-
|
|
1619
|
-
this._reportToWorld(world, {
|
|
1620
|
-
element_name: selectors.element_name,
|
|
1621
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1622
|
-
text: `Verify element contains text: ${text}`,
|
|
1623
|
-
value: text,
|
|
1624
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1625
|
-
result: error
|
|
1626
|
-
? {
|
|
1627
|
-
status: "FAILED",
|
|
1628
|
-
startTime,
|
|
1629
|
-
endTime,
|
|
1630
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1631
|
-
}
|
|
1632
|
-
: {
|
|
1633
|
-
status: "PASSED",
|
|
1634
|
-
startTime,
|
|
1635
|
-
endTime,
|
|
1636
|
-
},
|
|
1637
|
-
info: info,
|
|
1638
|
-
});
|
|
1505
|
+
_commandFinally(state, this);
|
|
1639
1506
|
}
|
|
1640
1507
|
}
|
|
1641
1508
|
_getDataFile(world = null) {
|
|
@@ -1909,127 +1776,69 @@ class StableBrowser {
|
|
|
1909
1776
|
}
|
|
1910
1777
|
}
|
|
1911
1778
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
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
|
+
};
|
|
1917
1789
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1918
|
-
const info = {};
|
|
1919
|
-
info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
|
|
1920
|
-
info.operation = "verify";
|
|
1921
|
-
info.selectors = selectors;
|
|
1922
1790
|
try {
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
}
|
|
1927
|
-
await this._highlightElements(element);
|
|
1928
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1929
|
-
await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
1930
|
-
return info;
|
|
1791
|
+
await _preCommand(state, this);
|
|
1792
|
+
await expect(state.element).toHaveCount(1, { timeout: 10000 });
|
|
1793
|
+
return state.info;
|
|
1931
1794
|
}
|
|
1932
1795
|
catch (e) {
|
|
1933
|
-
|
|
1934
|
-
this.logger.error("verify failed " + info.log);
|
|
1935
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1936
|
-
info.screenshotPath = screenshotPath;
|
|
1937
|
-
Object.assign(e, { info: info });
|
|
1938
|
-
error = e;
|
|
1939
|
-
throw e;
|
|
1796
|
+
await _commandError(state, e, this);
|
|
1940
1797
|
}
|
|
1941
1798
|
finally {
|
|
1942
|
-
|
|
1943
|
-
this._reportToWorld(world, {
|
|
1944
|
-
element_name: selectors.element_name,
|
|
1945
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1946
|
-
text: "Verify element exists in page",
|
|
1947
|
-
screenshotId,
|
|
1948
|
-
result: error
|
|
1949
|
-
? {
|
|
1950
|
-
status: "FAILED",
|
|
1951
|
-
startTime,
|
|
1952
|
-
endTime,
|
|
1953
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1954
|
-
}
|
|
1955
|
-
: {
|
|
1956
|
-
status: "PASSED",
|
|
1957
|
-
startTime,
|
|
1958
|
-
endTime,
|
|
1959
|
-
},
|
|
1960
|
-
info: info,
|
|
1961
|
-
});
|
|
1799
|
+
_commandFinally(state, this);
|
|
1962
1800
|
}
|
|
1963
1801
|
}
|
|
1964
1802
|
async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
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
|
+
};
|
|
1970
1815
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1971
|
-
const info = {};
|
|
1972
|
-
info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
|
|
1973
|
-
info.operation = "extract";
|
|
1974
|
-
info.selectors = selectors;
|
|
1975
1816
|
try {
|
|
1976
|
-
|
|
1977
|
-
await this._highlightElements(element);
|
|
1978
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1817
|
+
await _preCommand(state, this);
|
|
1979
1818
|
switch (attribute) {
|
|
1980
1819
|
case "inner_text":
|
|
1981
|
-
|
|
1820
|
+
state.value = await state.element.innerText();
|
|
1982
1821
|
break;
|
|
1983
1822
|
case "href":
|
|
1984
|
-
|
|
1823
|
+
state.value = await state.element.getAttribute("href");
|
|
1985
1824
|
break;
|
|
1986
1825
|
case "value":
|
|
1987
|
-
|
|
1826
|
+
state.value = await state.element.inputValue();
|
|
1988
1827
|
break;
|
|
1989
1828
|
default:
|
|
1990
|
-
|
|
1829
|
+
state.value = await state.element.getAttribute(attribute);
|
|
1991
1830
|
break;
|
|
1992
1831
|
}
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
this.setTestData({ [variable]: info.value }, world);
|
|
1998
|
-
this.logger.info("set test data: " + variable + "=" + info.value);
|
|
1999
|
-
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;
|
|
2000
1836
|
}
|
|
2001
1837
|
catch (e) {
|
|
2002
|
-
|
|
2003
|
-
this.logger.error("extract failed " + info.log);
|
|
2004
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2005
|
-
info.screenshotPath = screenshotPath;
|
|
2006
|
-
Object.assign(e, { info: info });
|
|
2007
|
-
error = e;
|
|
2008
|
-
throw e;
|
|
1838
|
+
await _commandError(state, e, this);
|
|
2009
1839
|
}
|
|
2010
1840
|
finally {
|
|
2011
|
-
|
|
2012
|
-
this._reportToWorld(world, {
|
|
2013
|
-
element_name: selectors.element_name,
|
|
2014
|
-
type: Types.EXTRACT_ATTRIBUTE,
|
|
2015
|
-
variable: variable,
|
|
2016
|
-
value: info.value,
|
|
2017
|
-
text: "Extract attribute from element",
|
|
2018
|
-
screenshotId,
|
|
2019
|
-
result: error
|
|
2020
|
-
? {
|
|
2021
|
-
status: "FAILED",
|
|
2022
|
-
startTime,
|
|
2023
|
-
endTime,
|
|
2024
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2025
|
-
}
|
|
2026
|
-
: {
|
|
2027
|
-
status: "PASSED",
|
|
2028
|
-
startTime,
|
|
2029
|
-
endTime,
|
|
2030
|
-
},
|
|
2031
|
-
info: info,
|
|
2032
|
-
});
|
|
1841
|
+
_commandFinally(state, this);
|
|
2033
1842
|
}
|
|
2034
1843
|
}
|
|
2035
1844
|
async extractEmailData(emailAddress, options, world) {
|
|
@@ -2106,7 +1915,8 @@ class StableBrowser {
|
|
|
2106
1915
|
catch (e) {
|
|
2107
1916
|
errorCount++;
|
|
2108
1917
|
if (errorCount > 3) {
|
|
2109
|
-
throw e;
|
|
1918
|
+
// throw e;
|
|
1919
|
+
await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
|
|
2110
1920
|
}
|
|
2111
1921
|
// ignore
|
|
2112
1922
|
}
|
|
@@ -2217,11 +2027,12 @@ class StableBrowser {
|
|
|
2217
2027
|
info.screenshotPath = screenshotPath;
|
|
2218
2028
|
Object.assign(e, { info: info });
|
|
2219
2029
|
error = e;
|
|
2220
|
-
throw e;
|
|
2030
|
+
// throw e;
|
|
2031
|
+
await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
|
|
2221
2032
|
}
|
|
2222
2033
|
finally {
|
|
2223
2034
|
const endTime = Date.now();
|
|
2224
|
-
|
|
2035
|
+
_reportToWorld(world, {
|
|
2225
2036
|
type: Types.VERIFY_PAGE_PATH,
|
|
2226
2037
|
text: "Verify page path",
|
|
2227
2038
|
screenshotId,
|
|
@@ -2230,7 +2041,7 @@ class StableBrowser {
|
|
|
2230
2041
|
status: "FAILED",
|
|
2231
2042
|
startTime,
|
|
2232
2043
|
endTime,
|
|
2233
|
-
message: error
|
|
2044
|
+
message: error?.message,
|
|
2234
2045
|
}
|
|
2235
2046
|
: {
|
|
2236
2047
|
status: "PASSED",
|
|
@@ -2243,52 +2054,58 @@ class StableBrowser {
|
|
|
2243
2054
|
}
|
|
2244
2055
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2245
2056
|
text = unEscapeString(text);
|
|
2246
|
-
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
|
+
};
|
|
2247
2069
|
const timeout = this._getLoadTimeout(options);
|
|
2248
|
-
let error = null;
|
|
2249
|
-
let screenshotId = null;
|
|
2250
|
-
let screenshotPath = null;
|
|
2251
2070
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2252
|
-
const info = {};
|
|
2253
|
-
info.log = "***** verify text " + text + " exists in page *****\n";
|
|
2254
|
-
info.operation = "verifyTextExistInPage";
|
|
2255
2071
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
2256
2072
|
if (newValue !== text) {
|
|
2257
2073
|
this.logger.info(text + "=" + newValue);
|
|
2258
2074
|
text = newValue;
|
|
2259
2075
|
}
|
|
2260
|
-
info.text = text;
|
|
2261
2076
|
let dateAlternatives = findDateAlternatives(text);
|
|
2262
2077
|
let numberAlternatives = findNumberAlternatives(text);
|
|
2263
2078
|
try {
|
|
2079
|
+
await _preCommand(state, this);
|
|
2080
|
+
state.info.text = text;
|
|
2264
2081
|
while (true) {
|
|
2265
2082
|
const frames = this.page.frames();
|
|
2266
2083
|
let results = [];
|
|
2267
2084
|
for (let i = 0; i < frames.length; i++) {
|
|
2268
2085
|
if (dateAlternatives.date) {
|
|
2269
2086
|
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2270
|
-
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, {});
|
|
2271
2088
|
result.frame = frames[i];
|
|
2272
2089
|
results.push(result);
|
|
2273
2090
|
}
|
|
2274
2091
|
}
|
|
2275
2092
|
else if (numberAlternatives.number) {
|
|
2276
2093
|
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2277
|
-
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, {});
|
|
2278
2095
|
result.frame = frames[i];
|
|
2279
2096
|
results.push(result);
|
|
2280
2097
|
}
|
|
2281
2098
|
}
|
|
2282
2099
|
else {
|
|
2283
|
-
const result = await this._locateElementByText(frames[i], text, "
|
|
2100
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2284
2101
|
result.frame = frames[i];
|
|
2285
2102
|
results.push(result);
|
|
2286
2103
|
}
|
|
2287
2104
|
}
|
|
2288
|
-
info.results = results;
|
|
2105
|
+
state.info.results = results;
|
|
2289
2106
|
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2290
2107
|
if (resultWithElementsFound.length === 0) {
|
|
2291
|
-
if (Date.now() - startTime > timeout) {
|
|
2108
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2292
2109
|
throw new Error(`Text ${text} not found in page`);
|
|
2293
2110
|
}
|
|
2294
2111
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -2300,44 +2117,89 @@ class StableBrowser {
|
|
|
2300
2117
|
await this._highlightElements(frame, dataAttribute);
|
|
2301
2118
|
const element = await frame.$(dataAttribute);
|
|
2302
2119
|
if (element) {
|
|
2303
|
-
await this.scrollIfNeeded(element, info);
|
|
2120
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2304
2121
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2305
2122
|
}
|
|
2306
2123
|
}
|
|
2307
|
-
|
|
2308
|
-
return info;
|
|
2124
|
+
await _screenshot(state, this);
|
|
2125
|
+
return state.info;
|
|
2309
2126
|
}
|
|
2310
2127
|
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2311
2128
|
}
|
|
2312
2129
|
catch (e) {
|
|
2313
|
-
|
|
2314
|
-
this.logger.error("verify text exist in page failed " + info.log);
|
|
2315
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2316
|
-
info.screenshotPath = screenshotPath;
|
|
2317
|
-
Object.assign(e, { info: info });
|
|
2318
|
-
error = e;
|
|
2319
|
-
throw e;
|
|
2130
|
+
await _commandError(state, e, this);
|
|
2320
2131
|
}
|
|
2321
2132
|
finally {
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
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
|
+
}
|
|
2333
2172
|
}
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
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);
|
|
2341
2203
|
}
|
|
2342
2204
|
}
|
|
2343
2205
|
_getServerUrl() {
|
|
@@ -2400,11 +2262,12 @@ class StableBrowser {
|
|
|
2400
2262
|
info.screenshotPath = screenshotPath;
|
|
2401
2263
|
Object.assign(e, { info: info });
|
|
2402
2264
|
error = e;
|
|
2403
|
-
throw e;
|
|
2265
|
+
// throw e;
|
|
2266
|
+
await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
|
|
2404
2267
|
}
|
|
2405
2268
|
finally {
|
|
2406
2269
|
const endTime = Date.now();
|
|
2407
|
-
|
|
2270
|
+
_reportToWorld(world, {
|
|
2408
2271
|
type: Types.VERIFY_VISUAL,
|
|
2409
2272
|
text: "Visual verification",
|
|
2410
2273
|
screenshotId,
|
|
@@ -2413,7 +2276,7 @@ class StableBrowser {
|
|
|
2413
2276
|
status: "FAILED",
|
|
2414
2277
|
startTime,
|
|
2415
2278
|
endTime,
|
|
2416
|
-
message: error
|
|
2279
|
+
message: error?.message,
|
|
2417
2280
|
}
|
|
2418
2281
|
: {
|
|
2419
2282
|
status: "PASSED",
|
|
@@ -2445,7 +2308,7 @@ class StableBrowser {
|
|
|
2445
2308
|
this.logger.info("Table data verified");
|
|
2446
2309
|
}
|
|
2447
2310
|
async getTableData(selectors, _params = null, options = {}, world = null) {
|
|
2448
|
-
|
|
2311
|
+
_validateSelectors(selectors);
|
|
2449
2312
|
const startTime = Date.now();
|
|
2450
2313
|
let error = null;
|
|
2451
2314
|
let screenshotId = null;
|
|
@@ -2467,11 +2330,12 @@ class StableBrowser {
|
|
|
2467
2330
|
info.screenshotPath = screenshotPath;
|
|
2468
2331
|
Object.assign(e, { info: info });
|
|
2469
2332
|
error = e;
|
|
2470
|
-
throw e;
|
|
2333
|
+
// throw e;
|
|
2334
|
+
await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
|
|
2471
2335
|
}
|
|
2472
2336
|
finally {
|
|
2473
2337
|
const endTime = Date.now();
|
|
2474
|
-
|
|
2338
|
+
_reportToWorld(world, {
|
|
2475
2339
|
element_name: selectors.element_name,
|
|
2476
2340
|
type: Types.GET_TABLE_DATA,
|
|
2477
2341
|
text: "Get table data",
|
|
@@ -2481,7 +2345,7 @@ class StableBrowser {
|
|
|
2481
2345
|
status: "FAILED",
|
|
2482
2346
|
startTime,
|
|
2483
2347
|
endTime,
|
|
2484
|
-
message: error
|
|
2348
|
+
message: error?.message,
|
|
2485
2349
|
}
|
|
2486
2350
|
: {
|
|
2487
2351
|
status: "PASSED",
|
|
@@ -2493,7 +2357,7 @@ class StableBrowser {
|
|
|
2493
2357
|
}
|
|
2494
2358
|
}
|
|
2495
2359
|
async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
|
|
2496
|
-
|
|
2360
|
+
_validateSelectors(selectors);
|
|
2497
2361
|
if (!query) {
|
|
2498
2362
|
throw new Error("query is null");
|
|
2499
2363
|
}
|
|
@@ -2632,11 +2496,12 @@ class StableBrowser {
|
|
|
2632
2496
|
info.screenshotPath = screenshotPath;
|
|
2633
2497
|
Object.assign(e, { info: info });
|
|
2634
2498
|
error = e;
|
|
2635
|
-
throw e;
|
|
2499
|
+
// throw e;
|
|
2500
|
+
await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
|
|
2636
2501
|
}
|
|
2637
2502
|
finally {
|
|
2638
2503
|
const endTime = Date.now();
|
|
2639
|
-
|
|
2504
|
+
_reportToWorld(world, {
|
|
2640
2505
|
element_name: selectors.element_name,
|
|
2641
2506
|
type: Types.ANALYZE_TABLE,
|
|
2642
2507
|
text: "Analyze table",
|
|
@@ -2646,7 +2511,7 @@ class StableBrowser {
|
|
|
2646
2511
|
status: "FAILED",
|
|
2647
2512
|
startTime,
|
|
2648
2513
|
endTime,
|
|
2649
|
-
message: error
|
|
2514
|
+
message: error?.message,
|
|
2650
2515
|
}
|
|
2651
2516
|
: {
|
|
2652
2517
|
status: "PASSED",
|
|
@@ -2658,27 +2523,7 @@ class StableBrowser {
|
|
|
2658
2523
|
}
|
|
2659
2524
|
}
|
|
2660
2525
|
async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
|
|
2661
|
-
|
|
2662
|
-
return value;
|
|
2663
|
-
}
|
|
2664
|
-
// find all the accurance of {{(.*?)}} and replace with the value
|
|
2665
|
-
let regex = /{{(.*?)}}/g;
|
|
2666
|
-
let matches = value.match(regex);
|
|
2667
|
-
if (matches) {
|
|
2668
|
-
const testData = this.getTestData(world);
|
|
2669
|
-
for (let i = 0; i < matches.length; i++) {
|
|
2670
|
-
let match = matches[i];
|
|
2671
|
-
let key = match.substring(2, match.length - 2);
|
|
2672
|
-
let newValue = objectPath.get(testData, key, null);
|
|
2673
|
-
if (newValue !== null) {
|
|
2674
|
-
value = value.replace(match, newValue);
|
|
2675
|
-
}
|
|
2676
|
-
}
|
|
2677
|
-
}
|
|
2678
|
-
if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
|
|
2679
|
-
return await decrypt(value, null, totpWait);
|
|
2680
|
-
}
|
|
2681
|
-
return value;
|
|
2526
|
+
return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
|
|
2682
2527
|
}
|
|
2683
2528
|
_getLoadTimeout(options) {
|
|
2684
2529
|
let timeout = 15000;
|
|
@@ -2729,7 +2574,7 @@ class StableBrowser {
|
|
|
2729
2574
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2730
2575
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2731
2576
|
const endTime = Date.now();
|
|
2732
|
-
|
|
2577
|
+
_reportToWorld(world, {
|
|
2733
2578
|
type: Types.GET_PAGE_STATUS,
|
|
2734
2579
|
text: "Wait for page load",
|
|
2735
2580
|
screenshotId,
|
|
@@ -2738,7 +2583,7 @@ class StableBrowser {
|
|
|
2738
2583
|
status: "FAILED",
|
|
2739
2584
|
startTime,
|
|
2740
2585
|
endTime,
|
|
2741
|
-
message: error
|
|
2586
|
+
message: error?.message,
|
|
2742
2587
|
}
|
|
2743
2588
|
: {
|
|
2744
2589
|
status: "PASSED",
|
|
@@ -2749,41 +2594,34 @@ class StableBrowser {
|
|
|
2749
2594
|
}
|
|
2750
2595
|
}
|
|
2751
2596
|
async closePage(options = {}, world = null) {
|
|
2752
|
-
const
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
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
|
+
};
|
|
2757
2609
|
try {
|
|
2610
|
+
await _preCommand(state, this);
|
|
2758
2611
|
await this.page.close();
|
|
2759
2612
|
}
|
|
2760
2613
|
catch (e) {
|
|
2761
2614
|
console.log(".");
|
|
2615
|
+
await _commandError(state, e, this);
|
|
2762
2616
|
}
|
|
2763
2617
|
finally {
|
|
2764
|
-
|
|
2765
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2766
|
-
const endTime = Date.now();
|
|
2767
|
-
this._reportToWorld(world, {
|
|
2768
|
-
type: Types.CLOSE_PAGE,
|
|
2769
|
-
text: "close page",
|
|
2770
|
-
screenshotId,
|
|
2771
|
-
result: error
|
|
2772
|
-
? {
|
|
2773
|
-
status: "FAILED",
|
|
2774
|
-
startTime,
|
|
2775
|
-
endTime,
|
|
2776
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2777
|
-
}
|
|
2778
|
-
: {
|
|
2779
|
-
status: "PASSED",
|
|
2780
|
-
startTime,
|
|
2781
|
-
endTime,
|
|
2782
|
-
},
|
|
2783
|
-
info: info,
|
|
2784
|
-
});
|
|
2618
|
+
_commandFinally(state, this);
|
|
2785
2619
|
}
|
|
2786
2620
|
}
|
|
2621
|
+
saveTestDataAsGlobal(options, world) {
|
|
2622
|
+
const dataFile = this._getDataFile(world);
|
|
2623
|
+
process.env.GLOBAL_TEST_DATA_FILE = dataFile;
|
|
2624
|
+
}
|
|
2787
2625
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
2788
2626
|
const startTime = Date.now();
|
|
2789
2627
|
let error = null;
|
|
@@ -2801,12 +2639,13 @@ class StableBrowser {
|
|
|
2801
2639
|
}
|
|
2802
2640
|
catch (e) {
|
|
2803
2641
|
console.log(".");
|
|
2642
|
+
await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
|
|
2804
2643
|
}
|
|
2805
2644
|
finally {
|
|
2806
2645
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2807
2646
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2808
2647
|
const endTime = Date.now();
|
|
2809
|
-
|
|
2648
|
+
_reportToWorld(world, {
|
|
2810
2649
|
type: Types.SET_VIEWPORT,
|
|
2811
2650
|
text: "set viewport size to " + width + "x" + hight,
|
|
2812
2651
|
screenshotId,
|
|
@@ -2815,7 +2654,7 @@ class StableBrowser {
|
|
|
2815
2654
|
status: "FAILED",
|
|
2816
2655
|
startTime,
|
|
2817
2656
|
endTime,
|
|
2818
|
-
message: error
|
|
2657
|
+
message: error?.message,
|
|
2819
2658
|
}
|
|
2820
2659
|
: {
|
|
2821
2660
|
status: "PASSED",
|
|
@@ -2837,12 +2676,13 @@ class StableBrowser {
|
|
|
2837
2676
|
}
|
|
2838
2677
|
catch (e) {
|
|
2839
2678
|
console.log(".");
|
|
2679
|
+
await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
|
|
2840
2680
|
}
|
|
2841
2681
|
finally {
|
|
2842
2682
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2843
2683
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2844
2684
|
const endTime = Date.now();
|
|
2845
|
-
|
|
2685
|
+
_reportToWorld(world, {
|
|
2846
2686
|
type: Types.GET_PAGE_STATUS,
|
|
2847
2687
|
text: "page relaod",
|
|
2848
2688
|
screenshotId,
|
|
@@ -2851,7 +2691,7 @@ class StableBrowser {
|
|
|
2851
2691
|
status: "FAILED",
|
|
2852
2692
|
startTime,
|
|
2853
2693
|
endTime,
|
|
2854
|
-
message: error
|
|
2694
|
+
message: error?.message,
|
|
2855
2695
|
}
|
|
2856
2696
|
: {
|
|
2857
2697
|
status: "PASSED",
|
|
@@ -2878,11 +2718,30 @@ class StableBrowser {
|
|
|
2878
2718
|
console.log("#-#");
|
|
2879
2719
|
}
|
|
2880
2720
|
}
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2721
|
+
async beforeStep(world, step) {
|
|
2722
|
+
this.stepName = step.pickleStep.text;
|
|
2723
|
+
this.logger.info("step: " + this.stepName);
|
|
2724
|
+
if (this.stepIndex === undefined) {
|
|
2725
|
+
this.stepIndex = 0;
|
|
2726
|
+
}
|
|
2727
|
+
else {
|
|
2728
|
+
this.stepIndex++;
|
|
2729
|
+
}
|
|
2730
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2731
|
+
if (this.context.browserObject.context) {
|
|
2732
|
+
await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
async afterStep(world, step) {
|
|
2737
|
+
this.stepName = null;
|
|
2738
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2739
|
+
if (this.context.browserObject.context) {
|
|
2740
|
+
await this.context.browserObject.context.tracing.stopChunk({
|
|
2741
|
+
path: path.join(this.context.browserObject.traceFolder, `trace-${this.stepIndex}.zip`),
|
|
2742
|
+
});
|
|
2743
|
+
}
|
|
2884
2744
|
}
|
|
2885
|
-
world.attach(JSON.stringify(properties), { mediaType: "application/json" });
|
|
2886
2745
|
}
|
|
2887
2746
|
}
|
|
2888
2747
|
function createTimedPromise(promise, label) {
|