automation_model 1.0.465-dev → 1.0.465-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 +576 -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(),
|
|
@@ -177,7 +193,9 @@ class StableBrowser {
|
|
|
177
193
|
time: new Date().toISOString(),
|
|
178
194
|
};
|
|
179
195
|
this.context.webLogger.push(obj);
|
|
180
|
-
|
|
196
|
+
if (msg.type() === "error") {
|
|
197
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
|
|
198
|
+
}
|
|
181
199
|
});
|
|
182
200
|
}
|
|
183
201
|
registerRequestListener(page, context, logFile) {
|
|
@@ -185,7 +203,6 @@ class StableBrowser {
|
|
|
185
203
|
this.context.networkLogger = [];
|
|
186
204
|
}
|
|
187
205
|
page.on("request", async (data) => {
|
|
188
|
-
var _a;
|
|
189
206
|
const startTime = new Date().getTime();
|
|
190
207
|
try {
|
|
191
208
|
const pageUrl = new URL(page.url());
|
|
@@ -210,10 +227,10 @@ class StableBrowser {
|
|
|
210
227
|
startTime,
|
|
211
228
|
};
|
|
212
229
|
context.networkLogger.push(obj);
|
|
213
|
-
|
|
230
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
|
|
214
231
|
}
|
|
215
232
|
catch (error) {
|
|
216
|
-
console.error("Error in request listener", error);
|
|
233
|
+
// console.error("Error in request listener", error);
|
|
217
234
|
context.networkLogger.push({
|
|
218
235
|
error: "not able to listen",
|
|
219
236
|
message: error.message,
|
|
@@ -235,20 +252,6 @@ class StableBrowser {
|
|
|
235
252
|
timeout: 60000,
|
|
236
253
|
});
|
|
237
254
|
}
|
|
238
|
-
_validateSelectors(selectors) {
|
|
239
|
-
if (!selectors) {
|
|
240
|
-
throw new Error("selectors is null");
|
|
241
|
-
}
|
|
242
|
-
if (!selectors.locators) {
|
|
243
|
-
throw new Error("selectors.locators is null");
|
|
244
|
-
}
|
|
245
|
-
if (!Array.isArray(selectors.locators)) {
|
|
246
|
-
throw new Error("selectors.locators expected to be array");
|
|
247
|
-
}
|
|
248
|
-
if (selectors.locators.length === 0) {
|
|
249
|
-
throw new Error("selectors.locators expected to be non empty array");
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
255
|
_fixUsingParams(text, _params) {
|
|
253
256
|
if (!_params || typeof text !== "string") {
|
|
254
257
|
return text;
|
|
@@ -316,7 +319,7 @@ class StableBrowser {
|
|
|
316
319
|
locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
|
|
317
320
|
}
|
|
318
321
|
}
|
|
319
|
-
if (locator
|
|
322
|
+
if (locator?.engine) {
|
|
320
323
|
if (locator.engine === "css") {
|
|
321
324
|
locatorReturn = scope.locator(locator.selector);
|
|
322
325
|
}
|
|
@@ -340,7 +343,7 @@ class StableBrowser {
|
|
|
340
343
|
if (css && css.locator) {
|
|
341
344
|
css = css.locator;
|
|
342
345
|
}
|
|
343
|
-
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);
|
|
344
347
|
if (result.elementCount === 0) {
|
|
345
348
|
return;
|
|
346
349
|
}
|
|
@@ -355,7 +358,7 @@ class StableBrowser {
|
|
|
355
358
|
}
|
|
356
359
|
async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
|
|
357
360
|
//const stringifyText = JSON.stringify(text);
|
|
358
|
-
return await scope.evaluate(([text, tag, regex, partial]) => {
|
|
361
|
+
return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
|
|
359
362
|
function isParent(parent, child) {
|
|
360
363
|
let currentNode = child.parentNode;
|
|
361
364
|
while (currentNode !== null) {
|
|
@@ -392,7 +395,7 @@ class StableBrowser {
|
|
|
392
395
|
}
|
|
393
396
|
document.collectAllShadowDomElements = collectAllShadowDomElements;
|
|
394
397
|
if (!tag) {
|
|
395
|
-
tag = "
|
|
398
|
+
tag = "*:not(script, style, head)";
|
|
396
399
|
}
|
|
397
400
|
let regexpSearch = document.getRegex(text);
|
|
398
401
|
if (regexpSearch) {
|
|
@@ -474,6 +477,15 @@ class StableBrowser {
|
|
|
474
477
|
}, [text1, tag1, regex1, partial1]);
|
|
475
478
|
}
|
|
476
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
|
+
}
|
|
477
489
|
let locatorSearch = selectorHierarchy[index];
|
|
478
490
|
try {
|
|
479
491
|
locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
|
|
@@ -486,13 +498,18 @@ class StableBrowser {
|
|
|
486
498
|
if (locatorSearch.climb && locatorSearch.climb >= 0) {
|
|
487
499
|
let locatorString = await this._locateElmentByTextClimbCss(scope, locatorSearch.text, locatorSearch.climb, locatorSearch.css, _params);
|
|
488
500
|
if (!locatorString) {
|
|
501
|
+
info.failCause.textNotFound = true;
|
|
502
|
+
info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
|
|
489
503
|
return;
|
|
490
504
|
}
|
|
491
505
|
locator = this._getLocator({ css: locatorString }, scope, _params);
|
|
492
506
|
}
|
|
493
507
|
else if (locatorSearch.text) {
|
|
494
|
-
let
|
|
508
|
+
let text = this._fixUsingParams(locatorSearch.text, _params);
|
|
509
|
+
let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
|
|
495
510
|
if (result.elementCount === 0) {
|
|
511
|
+
info.failCause.textNotFound = true;
|
|
512
|
+
info.failCause.lastError = "failed to locate element by text: " + text;
|
|
496
513
|
return;
|
|
497
514
|
}
|
|
498
515
|
locatorSearch.css = "[data-blinq-id='blinq-id-" + result.randomToken + "']";
|
|
@@ -509,6 +526,9 @@ class StableBrowser {
|
|
|
509
526
|
// cssHref = true;
|
|
510
527
|
// }
|
|
511
528
|
let count = await locator.count();
|
|
529
|
+
if (count > 0 && !info.failCause.count) {
|
|
530
|
+
info.failCause.count = count;
|
|
531
|
+
}
|
|
512
532
|
//info.log += "total elements found " + count + "\n";
|
|
513
533
|
//let visibleCount = 0;
|
|
514
534
|
let visibleLocator = null;
|
|
@@ -526,6 +546,8 @@ class StableBrowser {
|
|
|
526
546
|
foundLocators.push(locator.nth(j));
|
|
527
547
|
}
|
|
528
548
|
else {
|
|
549
|
+
info.failCause.visible = visible;
|
|
550
|
+
info.failCause.enabled = enabled;
|
|
529
551
|
if (!info.printMessages) {
|
|
530
552
|
info.printMessages = {};
|
|
531
553
|
}
|
|
@@ -537,6 +559,11 @@ class StableBrowser {
|
|
|
537
559
|
}
|
|
538
560
|
}
|
|
539
561
|
async closeUnexpectedPopups(info, _params) {
|
|
562
|
+
if (!info) {
|
|
563
|
+
info = {};
|
|
564
|
+
info.failCause = {};
|
|
565
|
+
info.log = "";
|
|
566
|
+
}
|
|
540
567
|
if (this.configuration.popupHandlers && this.configuration.popupHandlers.length > 0) {
|
|
541
568
|
if (!info) {
|
|
542
569
|
info = {};
|
|
@@ -577,7 +604,10 @@ class StableBrowser {
|
|
|
577
604
|
}
|
|
578
605
|
return { rerun: false };
|
|
579
606
|
}
|
|
580
|
-
async _locate(selectors, info, _params, timeout
|
|
607
|
+
async _locate(selectors, info, _params, timeout) {
|
|
608
|
+
if (!timeout) {
|
|
609
|
+
timeout = 30000;
|
|
610
|
+
}
|
|
581
611
|
for (let i = 0; i < 3; i++) {
|
|
582
612
|
info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
|
|
583
613
|
for (let j = 0; j < selectors.locators.length; j++) {
|
|
@@ -591,16 +621,15 @@ class StableBrowser {
|
|
|
591
621
|
}
|
|
592
622
|
throw new Error("unable to locate element " + JSON.stringify(selectors));
|
|
593
623
|
}
|
|
594
|
-
async
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
624
|
+
async _findFrameScope(selectors, timeout = 30000, info) {
|
|
625
|
+
if (!info) {
|
|
626
|
+
info = {};
|
|
627
|
+
info.failCause = {};
|
|
628
|
+
info.log = "";
|
|
629
|
+
}
|
|
600
630
|
let scope = this.page;
|
|
601
|
-
// for the simple click usecase
|
|
602
631
|
if (selectors.frame) {
|
|
603
|
-
|
|
632
|
+
return selectors.frame;
|
|
604
633
|
}
|
|
605
634
|
if (selectors.iframe_src || selectors.frameLocators) {
|
|
606
635
|
const findFrame = async (frame, framescope) => {
|
|
@@ -628,7 +657,6 @@ class StableBrowser {
|
|
|
628
657
|
}
|
|
629
658
|
return framescope;
|
|
630
659
|
};
|
|
631
|
-
info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
|
|
632
660
|
while (true) {
|
|
633
661
|
let frameFound = false;
|
|
634
662
|
if (selectors.nestFrmLoc) {
|
|
@@ -652,6 +680,8 @@ class StableBrowser {
|
|
|
652
680
|
if (!scope) {
|
|
653
681
|
info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
|
|
654
682
|
if (performance.now() - startTime > timeout) {
|
|
683
|
+
info.failCause.iframeNotFound = true;
|
|
684
|
+
info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
|
|
655
685
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
656
686
|
}
|
|
657
687
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -661,6 +691,30 @@ class StableBrowser {
|
|
|
661
691
|
}
|
|
662
692
|
}
|
|
663
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);
|
|
664
718
|
let selectorsLocators = null;
|
|
665
719
|
selectorsLocators = selectors.locators;
|
|
666
720
|
// group selectors by priority
|
|
@@ -762,6 +816,8 @@ class StableBrowser {
|
|
|
762
816
|
}
|
|
763
817
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
764
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";
|
|
765
821
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
766
822
|
}
|
|
767
823
|
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
@@ -793,6 +849,9 @@ class StableBrowser {
|
|
|
793
849
|
});
|
|
794
850
|
result.locatorIndex = i;
|
|
795
851
|
}
|
|
852
|
+
if (foundLocators.length > 1) {
|
|
853
|
+
info.failCause.foundMultiple = true;
|
|
854
|
+
}
|
|
796
855
|
}
|
|
797
856
|
return result;
|
|
798
857
|
}
|
|
@@ -805,12 +864,12 @@ class StableBrowser {
|
|
|
805
864
|
while (true) {
|
|
806
865
|
try {
|
|
807
866
|
const result = await locate_element(this.context, elementDescription, "click");
|
|
808
|
-
if (
|
|
867
|
+
if (result?.elementNumber >= 0) {
|
|
809
868
|
const selectors = {
|
|
810
|
-
frame: result
|
|
869
|
+
frame: result?.frame,
|
|
811
870
|
locators: [
|
|
812
871
|
{
|
|
813
|
-
css: result
|
|
872
|
+
css: result?.css,
|
|
814
873
|
},
|
|
815
874
|
],
|
|
816
875
|
};
|
|
@@ -820,7 +879,8 @@ class StableBrowser {
|
|
|
820
879
|
}
|
|
821
880
|
catch (e) {
|
|
822
881
|
if (performance.now() - startTime > timeout) {
|
|
823
|
-
throw e;
|
|
882
|
+
// throw e;
|
|
883
|
+
await _commandError({ text: "simpleClick", operation: "simpleClick", elementDescription, info: {} }, e, this);
|
|
824
884
|
}
|
|
825
885
|
}
|
|
826
886
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
@@ -835,12 +895,12 @@ class StableBrowser {
|
|
|
835
895
|
while (true) {
|
|
836
896
|
try {
|
|
837
897
|
const result = await locate_element(this.context, elementDescription, "fill", value);
|
|
838
|
-
if (
|
|
898
|
+
if (result?.elementNumber >= 0) {
|
|
839
899
|
const selectors = {
|
|
840
|
-
frame: result
|
|
900
|
+
frame: result?.frame,
|
|
841
901
|
locators: [
|
|
842
902
|
{
|
|
843
|
-
css: result
|
|
903
|
+
css: result?.css,
|
|
844
904
|
},
|
|
845
905
|
],
|
|
846
906
|
};
|
|
@@ -850,92 +910,68 @@ class StableBrowser {
|
|
|
850
910
|
}
|
|
851
911
|
catch (e) {
|
|
852
912
|
if (performance.now() - startTime > timeout) {
|
|
853
|
-
throw e;
|
|
913
|
+
// throw e;
|
|
914
|
+
await _commandError({ text: "simpleClickType", operation: "simpleClickType", value, elementDescription, info: {} }, e, this);
|
|
854
915
|
}
|
|
855
916
|
}
|
|
856
917
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
857
918
|
}
|
|
858
919
|
}
|
|
859
920
|
async click(selectors, _params, options = {}, world = null) {
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
let screenshotId = null;
|
|
871
|
-
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
|
+
};
|
|
872
931
|
try {
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
932
|
+
await _preCommand(state, this);
|
|
933
|
+
if (state.options && state.options.context) {
|
|
934
|
+
state.selectors.locators[0].text = state.options.context;
|
|
935
|
+
}
|
|
876
936
|
try {
|
|
877
|
-
await
|
|
878
|
-
await
|
|
879
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
937
|
+
await state.element.click();
|
|
938
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
880
939
|
}
|
|
881
940
|
catch (e) {
|
|
882
941
|
// await this.closeUnexpectedPopups();
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
await
|
|
886
|
-
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));
|
|
887
945
|
}
|
|
888
946
|
await this.waitForPageLoad();
|
|
889
|
-
return info;
|
|
947
|
+
return state.info;
|
|
890
948
|
}
|
|
891
949
|
catch (e) {
|
|
892
|
-
|
|
893
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
894
|
-
info.screenshotPath = screenshotPath;
|
|
895
|
-
Object.assign(e, { info: info });
|
|
896
|
-
error = e;
|
|
897
|
-
throw e;
|
|
950
|
+
await _commandError(state, e, this);
|
|
898
951
|
}
|
|
899
952
|
finally {
|
|
900
|
-
|
|
901
|
-
this._reportToWorld(world, {
|
|
902
|
-
element_name: selectors.element_name,
|
|
903
|
-
type: Types.CLICK,
|
|
904
|
-
text: `Click element`,
|
|
905
|
-
screenshotId,
|
|
906
|
-
result: error
|
|
907
|
-
? {
|
|
908
|
-
status: "FAILED",
|
|
909
|
-
startTime,
|
|
910
|
-
endTime,
|
|
911
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
912
|
-
}
|
|
913
|
-
: {
|
|
914
|
-
status: "PASSED",
|
|
915
|
-
startTime,
|
|
916
|
-
endTime,
|
|
917
|
-
},
|
|
918
|
-
info: info,
|
|
919
|
-
});
|
|
953
|
+
_commandFinally(state, this);
|
|
920
954
|
}
|
|
921
955
|
}
|
|
922
956
|
async setCheck(selectors, checked = true, _params, options = {}, world = null) {
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
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
|
+
};
|
|
933
967
|
try {
|
|
934
|
-
|
|
935
|
-
|
|
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));
|
|
936
972
|
try {
|
|
937
|
-
await this._highlightElements(element);
|
|
938
|
-
await element.setChecked(checked);
|
|
973
|
+
// await this._highlightElements(element);
|
|
974
|
+
await state.element.setChecked(checked);
|
|
939
975
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
940
976
|
}
|
|
941
977
|
catch (e) {
|
|
@@ -944,179 +980,108 @@ class StableBrowser {
|
|
|
944
980
|
}
|
|
945
981
|
else {
|
|
946
982
|
//await this.closeUnexpectedPopups();
|
|
947
|
-
info.log += "setCheck failed, will try again" + "\n";
|
|
948
|
-
element = await this._locate(selectors, info, _params);
|
|
949
|
-
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 });
|
|
950
986
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
951
987
|
}
|
|
952
988
|
}
|
|
953
989
|
await this.waitForPageLoad();
|
|
954
|
-
return info;
|
|
990
|
+
return state.info;
|
|
955
991
|
}
|
|
956
992
|
catch (e) {
|
|
957
|
-
|
|
958
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
959
|
-
info.screenshotPath = screenshotPath;
|
|
960
|
-
Object.assign(e, { info: info });
|
|
961
|
-
error = e;
|
|
962
|
-
throw e;
|
|
993
|
+
await _commandError(state, e, this);
|
|
963
994
|
}
|
|
964
995
|
finally {
|
|
965
|
-
|
|
966
|
-
this._reportToWorld(world, {
|
|
967
|
-
element_name: selectors.element_name,
|
|
968
|
-
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
969
|
-
text: checked ? `Check element` : `Uncheck element`,
|
|
970
|
-
screenshotId,
|
|
971
|
-
result: error
|
|
972
|
-
? {
|
|
973
|
-
status: "FAILED",
|
|
974
|
-
startTime,
|
|
975
|
-
endTime,
|
|
976
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
977
|
-
}
|
|
978
|
-
: {
|
|
979
|
-
status: "PASSED",
|
|
980
|
-
startTime,
|
|
981
|
-
endTime,
|
|
982
|
-
},
|
|
983
|
-
info: info,
|
|
984
|
-
});
|
|
996
|
+
_commandFinally(state, this);
|
|
985
997
|
}
|
|
986
998
|
}
|
|
987
999
|
async hover(selectors, _params, options = {}, world = null) {
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
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
|
+
};
|
|
997
1010
|
try {
|
|
998
|
-
|
|
999
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1011
|
+
await _preCommand(state, this);
|
|
1000
1012
|
try {
|
|
1001
|
-
await
|
|
1002
|
-
await element.hover();
|
|
1013
|
+
await state.element.hover();
|
|
1003
1014
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1004
1015
|
}
|
|
1005
1016
|
catch (e) {
|
|
1006
1017
|
//await this.closeUnexpectedPopups();
|
|
1007
|
-
info.log += "hover failed, will try again" + "\n";
|
|
1008
|
-
element = await this._locate(selectors, info, _params);
|
|
1009
|
-
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 });
|
|
1010
1021
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1011
1022
|
}
|
|
1012
1023
|
await this.waitForPageLoad();
|
|
1013
|
-
return info;
|
|
1024
|
+
return state.info;
|
|
1014
1025
|
}
|
|
1015
1026
|
catch (e) {
|
|
1016
|
-
|
|
1017
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1018
|
-
info.screenshotPath = screenshotPath;
|
|
1019
|
-
Object.assign(e, { info: info });
|
|
1020
|
-
error = e;
|
|
1021
|
-
throw e;
|
|
1027
|
+
await _commandError(state, e, this);
|
|
1022
1028
|
}
|
|
1023
1029
|
finally {
|
|
1024
|
-
|
|
1025
|
-
this._reportToWorld(world, {
|
|
1026
|
-
element_name: selectors.element_name,
|
|
1027
|
-
type: Types.HOVER,
|
|
1028
|
-
text: `Hover element`,
|
|
1029
|
-
screenshotId,
|
|
1030
|
-
result: error
|
|
1031
|
-
? {
|
|
1032
|
-
status: "FAILED",
|
|
1033
|
-
startTime,
|
|
1034
|
-
endTime,
|
|
1035
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1036
|
-
}
|
|
1037
|
-
: {
|
|
1038
|
-
status: "PASSED",
|
|
1039
|
-
startTime,
|
|
1040
|
-
endTime,
|
|
1041
|
-
},
|
|
1042
|
-
info: info,
|
|
1043
|
-
});
|
|
1030
|
+
_commandFinally(state, this);
|
|
1044
1031
|
}
|
|
1045
1032
|
}
|
|
1046
1033
|
async selectOption(selectors, values, _params = null, options = {}, world = null) {
|
|
1047
|
-
this._validateSelectors(selectors);
|
|
1048
1034
|
if (!values) {
|
|
1049
1035
|
throw new Error("values is null");
|
|
1050
1036
|
}
|
|
1051
|
-
const
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
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
|
+
};
|
|
1059
1048
|
try {
|
|
1060
|
-
|
|
1061
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1049
|
+
await _preCommand(state, this);
|
|
1062
1050
|
try {
|
|
1063
|
-
await
|
|
1064
|
-
await element.selectOption(values);
|
|
1051
|
+
await state.element.selectOption(values);
|
|
1065
1052
|
}
|
|
1066
1053
|
catch (e) {
|
|
1067
1054
|
//await this.closeUnexpectedPopups();
|
|
1068
|
-
info.log += "selectOption failed, will try force" + "\n";
|
|
1069
|
-
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 });
|
|
1070
1057
|
}
|
|
1071
1058
|
await this.waitForPageLoad();
|
|
1072
|
-
return info;
|
|
1059
|
+
return state.info;
|
|
1073
1060
|
}
|
|
1074
1061
|
catch (e) {
|
|
1075
|
-
|
|
1076
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1077
|
-
info.screenshotPath = screenshotPath;
|
|
1078
|
-
Object.assign(e, { info: info });
|
|
1079
|
-
this.logger.info("click failed, will try next selector");
|
|
1080
|
-
error = e;
|
|
1081
|
-
throw e;
|
|
1062
|
+
await _commandError(state, e, this);
|
|
1082
1063
|
}
|
|
1083
1064
|
finally {
|
|
1084
|
-
|
|
1085
|
-
this._reportToWorld(world, {
|
|
1086
|
-
element_name: selectors.element_name,
|
|
1087
|
-
type: Types.SELECT,
|
|
1088
|
-
text: `Select option: ${values}`,
|
|
1089
|
-
value: values.toString(),
|
|
1090
|
-
screenshotId,
|
|
1091
|
-
result: error
|
|
1092
|
-
? {
|
|
1093
|
-
status: "FAILED",
|
|
1094
|
-
startTime,
|
|
1095
|
-
endTime,
|
|
1096
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1097
|
-
}
|
|
1098
|
-
: {
|
|
1099
|
-
status: "PASSED",
|
|
1100
|
-
startTime,
|
|
1101
|
-
endTime,
|
|
1102
|
-
},
|
|
1103
|
-
info: info,
|
|
1104
|
-
});
|
|
1065
|
+
_commandFinally(state, this);
|
|
1105
1066
|
}
|
|
1106
1067
|
}
|
|
1107
1068
|
async type(_value, _params = null, options = {}, world = null) {
|
|
1108
|
-
const
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
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
|
+
};
|
|
1117
1082
|
try {
|
|
1118
|
-
|
|
1119
|
-
const valueSegment =
|
|
1083
|
+
await _preCommand(state, this);
|
|
1084
|
+
const valueSegment = state.value.split("&&");
|
|
1120
1085
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1121
1086
|
if (i > 0) {
|
|
1122
1087
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1136,134 +1101,76 @@ class StableBrowser {
|
|
|
1136
1101
|
await this.page.keyboard.type(value);
|
|
1137
1102
|
}
|
|
1138
1103
|
}
|
|
1139
|
-
return info;
|
|
1104
|
+
return state.info;
|
|
1140
1105
|
}
|
|
1141
1106
|
catch (e) {
|
|
1142
|
-
|
|
1143
|
-
this.logger.error("type failed " + info.log);
|
|
1144
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1145
|
-
info.screenshotPath = screenshotPath;
|
|
1146
|
-
Object.assign(e, { info: info });
|
|
1147
|
-
error = e;
|
|
1148
|
-
throw e;
|
|
1107
|
+
await _commandError(state, e, this);
|
|
1149
1108
|
}
|
|
1150
1109
|
finally {
|
|
1151
|
-
|
|
1152
|
-
this._reportToWorld(world, {
|
|
1153
|
-
type: Types.TYPE_PRESS,
|
|
1154
|
-
screenshotId,
|
|
1155
|
-
value: _value,
|
|
1156
|
-
text: `type value: ${_value}`,
|
|
1157
|
-
result: error
|
|
1158
|
-
? {
|
|
1159
|
-
status: "FAILED",
|
|
1160
|
-
startTime,
|
|
1161
|
-
endTime,
|
|
1162
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1163
|
-
}
|
|
1164
|
-
: {
|
|
1165
|
-
status: "PASSED",
|
|
1166
|
-
startTime,
|
|
1167
|
-
endTime,
|
|
1168
|
-
},
|
|
1169
|
-
info: info,
|
|
1170
|
-
});
|
|
1110
|
+
_commandFinally(state, this);
|
|
1171
1111
|
}
|
|
1172
1112
|
}
|
|
1173
1113
|
async setInputValue(selectors, value, _params = null, options = {}, world = null) {
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
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
|
+
};
|
|
1186
1125
|
try {
|
|
1187
|
-
|
|
1188
|
-
let
|
|
1189
|
-
await this.scrollIfNeeded(element, info);
|
|
1190
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1191
|
-
await this._highlightElements(element);
|
|
1126
|
+
await _preCommand(state, this);
|
|
1127
|
+
let value = await this._replaceWithLocalData(state.value, this);
|
|
1192
1128
|
try {
|
|
1193
|
-
await element.evaluateHandle((el, value) => {
|
|
1129
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1194
1130
|
el.value = value;
|
|
1195
1131
|
}, value);
|
|
1196
1132
|
}
|
|
1197
1133
|
catch (error) {
|
|
1198
1134
|
this.logger.error("setInputValue failed, will try again");
|
|
1199
|
-
|
|
1200
|
-
info.
|
|
1201
|
-
|
|
1202
|
-
await element.evaluateHandle((el, value) => {
|
|
1135
|
+
await _screenshot(state, this);
|
|
1136
|
+
Object.assign(error, { info: state.info });
|
|
1137
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1203
1138
|
el.value = value;
|
|
1204
1139
|
});
|
|
1205
1140
|
}
|
|
1206
1141
|
}
|
|
1207
1142
|
catch (e) {
|
|
1208
|
-
|
|
1209
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1210
|
-
info.screenshotPath = screenshotPath;
|
|
1211
|
-
Object.assign(e, { info: info });
|
|
1212
|
-
error = e;
|
|
1213
|
-
throw e;
|
|
1143
|
+
await _commandError(state, e, this);
|
|
1214
1144
|
}
|
|
1215
1145
|
finally {
|
|
1216
|
-
|
|
1217
|
-
this._reportToWorld(world, {
|
|
1218
|
-
element_name: selectors.element_name,
|
|
1219
|
-
type: Types.SET_INPUT,
|
|
1220
|
-
text: `Set input value`,
|
|
1221
|
-
value: value,
|
|
1222
|
-
screenshotId,
|
|
1223
|
-
result: error
|
|
1224
|
-
? {
|
|
1225
|
-
status: "FAILED",
|
|
1226
|
-
startTime,
|
|
1227
|
-
endTime,
|
|
1228
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1229
|
-
}
|
|
1230
|
-
: {
|
|
1231
|
-
status: "PASSED",
|
|
1232
|
-
startTime,
|
|
1233
|
-
endTime,
|
|
1234
|
-
},
|
|
1235
|
-
info: info,
|
|
1236
|
-
});
|
|
1146
|
+
_commandFinally(state, this);
|
|
1237
1147
|
}
|
|
1238
1148
|
}
|
|
1239
1149
|
async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
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
|
+
};
|
|
1250
1162
|
try {
|
|
1251
|
-
|
|
1252
|
-
let element = await this._locate(selectors, info, _params);
|
|
1253
|
-
//insert red border around the element
|
|
1254
|
-
await this.scrollIfNeeded(element, info);
|
|
1255
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1256
|
-
await this._highlightElements(element);
|
|
1163
|
+
await _preCommand(state, this);
|
|
1257
1164
|
try {
|
|
1258
|
-
await element.click();
|
|
1165
|
+
await state.element.click();
|
|
1259
1166
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1260
1167
|
if (format) {
|
|
1261
|
-
value = dayjs(value).format(format);
|
|
1262
|
-
await element.fill(value);
|
|
1168
|
+
state.value = dayjs(state.value).format(format);
|
|
1169
|
+
await state.element.fill(state.value);
|
|
1263
1170
|
}
|
|
1264
1171
|
else {
|
|
1265
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1266
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1172
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1173
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1267
1174
|
el.value = ""; // clear input
|
|
1268
1175
|
el.value = dateTimeValue;
|
|
1269
1176
|
}, dateTimeValue);
|
|
@@ -1276,20 +1183,19 @@ class StableBrowser {
|
|
|
1276
1183
|
}
|
|
1277
1184
|
catch (err) {
|
|
1278
1185
|
//await this.closeUnexpectedPopups();
|
|
1279
|
-
this.logger.error("setting date time input failed " + JSON.stringify(info));
|
|
1186
|
+
this.logger.error("setting date time input failed " + JSON.stringify(state.info));
|
|
1280
1187
|
this.logger.info("Trying again");
|
|
1281
|
-
|
|
1282
|
-
info.
|
|
1283
|
-
Object.assign(err, { info: info });
|
|
1188
|
+
await _screenshot(state, this);
|
|
1189
|
+
Object.assign(err, { info: state.info });
|
|
1284
1190
|
await element.click();
|
|
1285
1191
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1286
1192
|
if (format) {
|
|
1287
|
-
value = dayjs(value).format(format);
|
|
1288
|
-
await element.fill(value);
|
|
1193
|
+
state.value = dayjs(state.value).format(format);
|
|
1194
|
+
await state.element.fill(state.value);
|
|
1289
1195
|
}
|
|
1290
1196
|
else {
|
|
1291
|
-
const dateTimeValue = await getDateTimeValue({ value, element });
|
|
1292
|
-
await element.evaluateHandle((el, dateTimeValue) => {
|
|
1197
|
+
const dateTimeValue = await getDateTimeValue({ value: state.value, element: state.element });
|
|
1198
|
+
await state.element.evaluateHandle((el, dateTimeValue) => {
|
|
1293
1199
|
el.value = ""; // clear input
|
|
1294
1200
|
el.value = dateTimeValue;
|
|
1295
1201
|
}, dateTimeValue);
|
|
@@ -1302,60 +1208,39 @@ class StableBrowser {
|
|
|
1302
1208
|
}
|
|
1303
1209
|
}
|
|
1304
1210
|
catch (e) {
|
|
1305
|
-
|
|
1306
|
-
throw e;
|
|
1211
|
+
await _commandError(state, e, this);
|
|
1307
1212
|
}
|
|
1308
1213
|
finally {
|
|
1309
|
-
|
|
1310
|
-
this._reportToWorld(world, {
|
|
1311
|
-
element_name: selectors.element_name,
|
|
1312
|
-
type: Types.SET_DATE_TIME,
|
|
1313
|
-
screenshotId,
|
|
1314
|
-
value: value,
|
|
1315
|
-
text: `setDateTime input with value: ${value}`,
|
|
1316
|
-
result: error
|
|
1317
|
-
? {
|
|
1318
|
-
status: "FAILED",
|
|
1319
|
-
startTime,
|
|
1320
|
-
endTime,
|
|
1321
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1322
|
-
}
|
|
1323
|
-
: {
|
|
1324
|
-
status: "PASSED",
|
|
1325
|
-
startTime,
|
|
1326
|
-
endTime,
|
|
1327
|
-
},
|
|
1328
|
-
info: info,
|
|
1329
|
-
});
|
|
1214
|
+
_commandFinally(state, this);
|
|
1330
1215
|
}
|
|
1331
1216
|
}
|
|
1332
1217
|
async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
|
|
1333
1218
|
_value = unEscapeString(_value);
|
|
1334
|
-
this._validateSelectors(selectors);
|
|
1335
|
-
const startTime = Date.now();
|
|
1336
|
-
let error = null;
|
|
1337
|
-
let screenshotId = null;
|
|
1338
|
-
let screenshotPath = null;
|
|
1339
|
-
const info = {};
|
|
1340
|
-
info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
|
|
1341
|
-
info.operation = "clickType";
|
|
1342
|
-
info.selectors = selectors;
|
|
1343
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
|
+
};
|
|
1344
1232
|
if (newValue !== _value) {
|
|
1345
1233
|
//this.logger.info(_value + "=" + newValue);
|
|
1346
1234
|
_value = newValue;
|
|
1347
1235
|
}
|
|
1348
|
-
info.value = _value;
|
|
1349
1236
|
try {
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
await this.scrollIfNeeded(element, info);
|
|
1353
|
-
await this._highlightElements(element);
|
|
1237
|
+
await _preCommand(state, this);
|
|
1238
|
+
state.info.value = _value;
|
|
1354
1239
|
if (options === null || options === undefined || !options.press) {
|
|
1355
1240
|
try {
|
|
1356
|
-
let currentValue = await element.inputValue();
|
|
1241
|
+
let currentValue = await state.element.inputValue();
|
|
1357
1242
|
if (currentValue) {
|
|
1358
|
-
await element.fill("");
|
|
1243
|
+
await state.element.fill("");
|
|
1359
1244
|
}
|
|
1360
1245
|
}
|
|
1361
1246
|
catch (e) {
|
|
@@ -1364,22 +1249,22 @@ class StableBrowser {
|
|
|
1364
1249
|
}
|
|
1365
1250
|
if (options === null || options === undefined || options.press) {
|
|
1366
1251
|
try {
|
|
1367
|
-
await element.click({ timeout: 5000 });
|
|
1252
|
+
await state.element.click({ timeout: 5000 });
|
|
1368
1253
|
}
|
|
1369
1254
|
catch (e) {
|
|
1370
|
-
await element.dispatchEvent("click");
|
|
1255
|
+
await state.element.dispatchEvent("click");
|
|
1371
1256
|
}
|
|
1372
1257
|
}
|
|
1373
1258
|
else {
|
|
1374
1259
|
try {
|
|
1375
|
-
await element.focus();
|
|
1260
|
+
await state.element.focus();
|
|
1376
1261
|
}
|
|
1377
1262
|
catch (e) {
|
|
1378
|
-
await element.dispatchEvent("focus");
|
|
1263
|
+
await state.element.dispatchEvent("focus");
|
|
1379
1264
|
}
|
|
1380
1265
|
}
|
|
1381
1266
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1382
|
-
const valueSegment =
|
|
1267
|
+
const valueSegment = state.value.split("&&");
|
|
1383
1268
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1384
1269
|
if (i > 0) {
|
|
1385
1270
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1399,14 +1284,14 @@ class StableBrowser {
|
|
|
1399
1284
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1400
1285
|
}
|
|
1401
1286
|
}
|
|
1402
|
-
|
|
1287
|
+
await _screenshot(state, this);
|
|
1403
1288
|
if (enter === true) {
|
|
1404
1289
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1405
1290
|
await this.page.keyboard.press("Enter");
|
|
1406
1291
|
await this.waitForPageLoad();
|
|
1407
1292
|
}
|
|
1408
1293
|
else if (enter === false) {
|
|
1409
|
-
await element.dispatchEvent("change");
|
|
1294
|
+
await state.element.dispatchEvent("change");
|
|
1410
1295
|
//await this.page.keyboard.press("Tab");
|
|
1411
1296
|
}
|
|
1412
1297
|
else {
|
|
@@ -1415,104 +1300,50 @@ class StableBrowser {
|
|
|
1415
1300
|
await this.waitForPageLoad();
|
|
1416
1301
|
}
|
|
1417
1302
|
}
|
|
1418
|
-
return info;
|
|
1303
|
+
return state.info;
|
|
1419
1304
|
}
|
|
1420
1305
|
catch (e) {
|
|
1421
|
-
|
|
1422
|
-
this.logger.error("fill failed " + JSON.stringify(info));
|
|
1423
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1424
|
-
info.screenshotPath = screenshotPath;
|
|
1425
|
-
Object.assign(e, { info: info });
|
|
1426
|
-
error = e;
|
|
1427
|
-
throw e;
|
|
1306
|
+
await _commandError(state, e, this);
|
|
1428
1307
|
}
|
|
1429
1308
|
finally {
|
|
1430
|
-
|
|
1431
|
-
this._reportToWorld(world, {
|
|
1432
|
-
element_name: selectors.element_name,
|
|
1433
|
-
type: Types.FILL,
|
|
1434
|
-
screenshotId,
|
|
1435
|
-
value: _value,
|
|
1436
|
-
text: `clickType input with value: ${_value}`,
|
|
1437
|
-
result: error
|
|
1438
|
-
? {
|
|
1439
|
-
status: "FAILED",
|
|
1440
|
-
startTime,
|
|
1441
|
-
endTime,
|
|
1442
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1443
|
-
}
|
|
1444
|
-
: {
|
|
1445
|
-
status: "PASSED",
|
|
1446
|
-
startTime,
|
|
1447
|
-
endTime,
|
|
1448
|
-
},
|
|
1449
|
-
info: info,
|
|
1450
|
-
});
|
|
1309
|
+
_commandFinally(state, this);
|
|
1451
1310
|
}
|
|
1452
1311
|
}
|
|
1453
1312
|
async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
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
|
+
};
|
|
1465
1324
|
try {
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
await
|
|
1469
|
-
await element.fill(value);
|
|
1470
|
-
await element.dispatchEvent("change");
|
|
1325
|
+
await _preCommand(state, this);
|
|
1326
|
+
await state.element.fill(value);
|
|
1327
|
+
await state.element.dispatchEvent("change");
|
|
1471
1328
|
if (enter) {
|
|
1472
1329
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1473
1330
|
await this.page.keyboard.press("Enter");
|
|
1474
1331
|
}
|
|
1475
1332
|
await this.waitForPageLoad();
|
|
1476
|
-
return info;
|
|
1333
|
+
return state.info;
|
|
1477
1334
|
}
|
|
1478
1335
|
catch (e) {
|
|
1479
|
-
|
|
1480
|
-
this.logger.error("fill failed " + info.log);
|
|
1481
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1482
|
-
info.screenshotPath = screenshotPath;
|
|
1483
|
-
Object.assign(e, { info: info });
|
|
1484
|
-
error = e;
|
|
1485
|
-
throw e;
|
|
1336
|
+
await _commandError(state, e, this);
|
|
1486
1337
|
}
|
|
1487
1338
|
finally {
|
|
1488
|
-
|
|
1489
|
-
this._reportToWorld(world, {
|
|
1490
|
-
element_name: selectors.element_name,
|
|
1491
|
-
type: Types.FILL,
|
|
1492
|
-
screenshotId,
|
|
1493
|
-
value,
|
|
1494
|
-
text: `Fill input with value: ${value}`,
|
|
1495
|
-
result: error
|
|
1496
|
-
? {
|
|
1497
|
-
status: "FAILED",
|
|
1498
|
-
startTime,
|
|
1499
|
-
endTime,
|
|
1500
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1501
|
-
}
|
|
1502
|
-
: {
|
|
1503
|
-
status: "PASSED",
|
|
1504
|
-
startTime,
|
|
1505
|
-
endTime,
|
|
1506
|
-
},
|
|
1507
|
-
info: info,
|
|
1508
|
-
});
|
|
1339
|
+
_commandFinally(state, this);
|
|
1509
1340
|
}
|
|
1510
1341
|
}
|
|
1511
1342
|
async getText(selectors, _params = null, options = {}, info = {}, world = null) {
|
|
1512
1343
|
return await this._getText(selectors, 0, _params, options, info, world);
|
|
1513
1344
|
}
|
|
1514
1345
|
async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
|
|
1515
|
-
|
|
1346
|
+
_validateSelectors(selectors);
|
|
1516
1347
|
let screenshotId = null;
|
|
1517
1348
|
let screenshotPath = null;
|
|
1518
1349
|
if (!info.log) {
|
|
@@ -1556,166 +1387,124 @@ class StableBrowser {
|
|
|
1556
1387
|
}
|
|
1557
1388
|
}
|
|
1558
1389
|
async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
|
|
1559
|
-
var _a;
|
|
1560
|
-
this._validateSelectors(selectors);
|
|
1561
1390
|
if (!pattern) {
|
|
1562
1391
|
throw new Error("pattern is null");
|
|
1563
1392
|
}
|
|
1564
1393
|
if (!text) {
|
|
1565
1394
|
throw new Error("text is null");
|
|
1566
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
|
+
};
|
|
1567
1412
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1568
1413
|
if (newValue !== text) {
|
|
1569
1414
|
this.logger.info(text + "=" + newValue);
|
|
1570
1415
|
text = newValue;
|
|
1571
1416
|
}
|
|
1572
|
-
const startTime = Date.now();
|
|
1573
|
-
let error = null;
|
|
1574
|
-
let screenshotId = null;
|
|
1575
|
-
let screenshotPath = null;
|
|
1576
|
-
const info = {};
|
|
1577
|
-
info.log =
|
|
1578
|
-
"***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
|
|
1579
|
-
info.operation = "containsPattern";
|
|
1580
|
-
info.selectors = selectors;
|
|
1581
|
-
info.value = text;
|
|
1582
|
-
info.pattern = pattern;
|
|
1583
1417
|
let foundObj = null;
|
|
1584
1418
|
try {
|
|
1585
|
-
|
|
1419
|
+
await _preCommand(state, this);
|
|
1420
|
+
state.info.pattern = pattern;
|
|
1421
|
+
foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
|
|
1586
1422
|
if (foundObj && foundObj.element) {
|
|
1587
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1423
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1588
1424
|
}
|
|
1589
|
-
|
|
1425
|
+
await _screenshot(state, this);
|
|
1590
1426
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1591
1427
|
pattern = pattern.replace("{text}", escapedText);
|
|
1592
1428
|
let regex = new RegExp(pattern, "im");
|
|
1593
|
-
if (!regex.test(foundObj
|
|
1594
|
-
info.foundText = foundObj
|
|
1429
|
+
if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
|
|
1430
|
+
state.info.foundText = foundObj?.text;
|
|
1595
1431
|
throw new Error("element doesn't contain text " + text);
|
|
1596
1432
|
}
|
|
1597
|
-
return info;
|
|
1433
|
+
return state.info;
|
|
1598
1434
|
}
|
|
1599
1435
|
catch (e) {
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
|
|
1603
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1604
|
-
info.screenshotPath = screenshotPath;
|
|
1605
|
-
Object.assign(e, { info: info });
|
|
1606
|
-
error = e;
|
|
1607
|
-
throw e;
|
|
1436
|
+
this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
|
|
1437
|
+
await _commandError(state, e, this);
|
|
1608
1438
|
}
|
|
1609
1439
|
finally {
|
|
1610
|
-
|
|
1611
|
-
this._reportToWorld(world, {
|
|
1612
|
-
element_name: selectors.element_name,
|
|
1613
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1614
|
-
value: pattern,
|
|
1615
|
-
text: `Verify element contains pattern: ${pattern}`,
|
|
1616
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1617
|
-
result: error
|
|
1618
|
-
? {
|
|
1619
|
-
status: "FAILED",
|
|
1620
|
-
startTime,
|
|
1621
|
-
endTime,
|
|
1622
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1623
|
-
}
|
|
1624
|
-
: {
|
|
1625
|
-
status: "PASSED",
|
|
1626
|
-
startTime,
|
|
1627
|
-
endTime,
|
|
1628
|
-
},
|
|
1629
|
-
info: info,
|
|
1630
|
-
});
|
|
1440
|
+
_commandFinally(state, this);
|
|
1631
1441
|
}
|
|
1632
1442
|
}
|
|
1633
1443
|
async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
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
|
+
};
|
|
1637
1459
|
if (!text) {
|
|
1638
1460
|
throw new Error("text is null");
|
|
1639
1461
|
}
|
|
1640
|
-
|
|
1641
|
-
let error = null;
|
|
1642
|
-
let screenshotId = null;
|
|
1643
|
-
let screenshotPath = null;
|
|
1644
|
-
const info = {};
|
|
1645
|
-
info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
|
|
1646
|
-
info.operation = "containsText";
|
|
1647
|
-
info.selectors = selectors;
|
|
1462
|
+
text = unEscapeString(text);
|
|
1648
1463
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1649
1464
|
if (newValue !== text) {
|
|
1650
1465
|
this.logger.info(text + "=" + newValue);
|
|
1651
1466
|
text = newValue;
|
|
1652
1467
|
}
|
|
1653
|
-
info.value = text;
|
|
1654
1468
|
let foundObj = null;
|
|
1655
1469
|
try {
|
|
1656
|
-
|
|
1470
|
+
await _preCommand(state, this);
|
|
1471
|
+
foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
|
|
1657
1472
|
if (foundObj && foundObj.element) {
|
|
1658
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1473
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1659
1474
|
}
|
|
1660
|
-
|
|
1475
|
+
await _screenshot(state, this);
|
|
1661
1476
|
const dateAlternatives = findDateAlternatives(text);
|
|
1662
1477
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1663
1478
|
if (dateAlternatives.date) {
|
|
1664
1479
|
for (let i = 0; i < dateAlternatives.dates.length; i++) {
|
|
1665
|
-
if (
|
|
1666
|
-
|
|
1667
|
-
return info;
|
|
1480
|
+
if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
|
|
1481
|
+
foundObj?.value?.includes(dateAlternatives.dates[i])) {
|
|
1482
|
+
return state.info;
|
|
1668
1483
|
}
|
|
1669
1484
|
}
|
|
1670
1485
|
throw new Error("element doesn't contain text " + text);
|
|
1671
1486
|
}
|
|
1672
1487
|
else if (numberAlternatives.number) {
|
|
1673
1488
|
for (let i = 0; i < numberAlternatives.numbers.length; i++) {
|
|
1674
|
-
if (
|
|
1675
|
-
|
|
1676
|
-
return info;
|
|
1489
|
+
if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
|
|
1490
|
+
foundObj?.value?.includes(numberAlternatives.numbers[i])) {
|
|
1491
|
+
return state.info;
|
|
1677
1492
|
}
|
|
1678
1493
|
}
|
|
1679
1494
|
throw new Error("element doesn't contain text " + text);
|
|
1680
1495
|
}
|
|
1681
|
-
else if (!
|
|
1682
|
-
info.foundText = foundObj
|
|
1683
|
-
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;
|
|
1684
1499
|
throw new Error("element doesn't contain text " + text);
|
|
1685
1500
|
}
|
|
1686
|
-
return info;
|
|
1501
|
+
return state.info;
|
|
1687
1502
|
}
|
|
1688
1503
|
catch (e) {
|
|
1689
|
-
|
|
1690
|
-
this.logger.error("verify element contains text failed " + info.log);
|
|
1691
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1692
|
-
info.screenshotPath = screenshotPath;
|
|
1693
|
-
Object.assign(e, { info: info });
|
|
1694
|
-
error = e;
|
|
1695
|
-
throw e;
|
|
1504
|
+
await _commandError(state, e, this);
|
|
1696
1505
|
}
|
|
1697
1506
|
finally {
|
|
1698
|
-
|
|
1699
|
-
this._reportToWorld(world, {
|
|
1700
|
-
element_name: selectors.element_name,
|
|
1701
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1702
|
-
text: `Verify element contains text: ${text}`,
|
|
1703
|
-
value: text,
|
|
1704
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1705
|
-
result: error
|
|
1706
|
-
? {
|
|
1707
|
-
status: "FAILED",
|
|
1708
|
-
startTime,
|
|
1709
|
-
endTime,
|
|
1710
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1711
|
-
}
|
|
1712
|
-
: {
|
|
1713
|
-
status: "PASSED",
|
|
1714
|
-
startTime,
|
|
1715
|
-
endTime,
|
|
1716
|
-
},
|
|
1717
|
-
info: info,
|
|
1718
|
-
});
|
|
1507
|
+
_commandFinally(state, this);
|
|
1719
1508
|
}
|
|
1720
1509
|
}
|
|
1721
1510
|
_getDataFile(world = null) {
|
|
@@ -1897,11 +1686,9 @@ class StableBrowser {
|
|
|
1897
1686
|
if (!fs.existsSync(world.screenshotPath)) {
|
|
1898
1687
|
fs.mkdirSync(world.screenshotPath, { recursive: true });
|
|
1899
1688
|
}
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
}
|
|
1904
|
-
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");
|
|
1905
1692
|
try {
|
|
1906
1693
|
await this.takeScreenshot(screenshotPath);
|
|
1907
1694
|
// let buffer = await this.page.screenshot({ timeout: 4000 });
|
|
@@ -1915,7 +1702,7 @@ class StableBrowser {
|
|
|
1915
1702
|
catch (e) {
|
|
1916
1703
|
this.logger.info("unable to take screenshot, ignored");
|
|
1917
1704
|
}
|
|
1918
|
-
result.screenshotId =
|
|
1705
|
+
result.screenshotId = uuidStr;
|
|
1919
1706
|
result.screenshotPath = screenshotPath;
|
|
1920
1707
|
if (info && info.box) {
|
|
1921
1708
|
await drawRectangle(screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
|
|
@@ -1989,127 +1776,69 @@ class StableBrowser {
|
|
|
1989
1776
|
}
|
|
1990
1777
|
}
|
|
1991
1778
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
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
|
+
};
|
|
1997
1789
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1998
|
-
const info = {};
|
|
1999
|
-
info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
|
|
2000
|
-
info.operation = "verify";
|
|
2001
|
-
info.selectors = selectors;
|
|
2002
1790
|
try {
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
}
|
|
2007
|
-
await this._highlightElements(element);
|
|
2008
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2009
|
-
await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2010
|
-
return info;
|
|
1791
|
+
await _preCommand(state, this);
|
|
1792
|
+
await expect(state.element).toHaveCount(1, { timeout: 10000 });
|
|
1793
|
+
return state.info;
|
|
2011
1794
|
}
|
|
2012
1795
|
catch (e) {
|
|
2013
|
-
|
|
2014
|
-
this.logger.error("verify failed " + info.log);
|
|
2015
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2016
|
-
info.screenshotPath = screenshotPath;
|
|
2017
|
-
Object.assign(e, { info: info });
|
|
2018
|
-
error = e;
|
|
2019
|
-
throw e;
|
|
1796
|
+
await _commandError(state, e, this);
|
|
2020
1797
|
}
|
|
2021
1798
|
finally {
|
|
2022
|
-
|
|
2023
|
-
this._reportToWorld(world, {
|
|
2024
|
-
element_name: selectors.element_name,
|
|
2025
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
2026
|
-
text: "Verify element exists in page",
|
|
2027
|
-
screenshotId,
|
|
2028
|
-
result: error
|
|
2029
|
-
? {
|
|
2030
|
-
status: "FAILED",
|
|
2031
|
-
startTime,
|
|
2032
|
-
endTime,
|
|
2033
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2034
|
-
}
|
|
2035
|
-
: {
|
|
2036
|
-
status: "PASSED",
|
|
2037
|
-
startTime,
|
|
2038
|
-
endTime,
|
|
2039
|
-
},
|
|
2040
|
-
info: info,
|
|
2041
|
-
});
|
|
1799
|
+
_commandFinally(state, this);
|
|
2042
1800
|
}
|
|
2043
1801
|
}
|
|
2044
1802
|
async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
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
|
+
};
|
|
2050
1815
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2051
|
-
const info = {};
|
|
2052
|
-
info.log = "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n";
|
|
2053
|
-
info.operation = "extract";
|
|
2054
|
-
info.selectors = selectors;
|
|
2055
1816
|
try {
|
|
2056
|
-
|
|
2057
|
-
await this._highlightElements(element);
|
|
2058
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1817
|
+
await _preCommand(state, this);
|
|
2059
1818
|
switch (attribute) {
|
|
2060
1819
|
case "inner_text":
|
|
2061
|
-
|
|
1820
|
+
state.value = await state.element.innerText();
|
|
2062
1821
|
break;
|
|
2063
1822
|
case "href":
|
|
2064
|
-
|
|
1823
|
+
state.value = await state.element.getAttribute("href");
|
|
2065
1824
|
break;
|
|
2066
1825
|
case "value":
|
|
2067
|
-
|
|
1826
|
+
state.value = await state.element.inputValue();
|
|
2068
1827
|
break;
|
|
2069
1828
|
default:
|
|
2070
|
-
|
|
1829
|
+
state.value = await state.element.getAttribute(attribute);
|
|
2071
1830
|
break;
|
|
2072
1831
|
}
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
this.setTestData({ [variable]: info.value }, world);
|
|
2078
|
-
this.logger.info("set test data: " + variable + "=" + info.value);
|
|
2079
|
-
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;
|
|
2080
1836
|
}
|
|
2081
1837
|
catch (e) {
|
|
2082
|
-
|
|
2083
|
-
this.logger.error("extract failed " + info.log);
|
|
2084
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2085
|
-
info.screenshotPath = screenshotPath;
|
|
2086
|
-
Object.assign(e, { info: info });
|
|
2087
|
-
error = e;
|
|
2088
|
-
throw e;
|
|
1838
|
+
await _commandError(state, e, this);
|
|
2089
1839
|
}
|
|
2090
1840
|
finally {
|
|
2091
|
-
|
|
2092
|
-
this._reportToWorld(world, {
|
|
2093
|
-
element_name: selectors.element_name,
|
|
2094
|
-
type: Types.EXTRACT_ATTRIBUTE,
|
|
2095
|
-
variable: variable,
|
|
2096
|
-
value: info.value,
|
|
2097
|
-
text: "Extract attribute from element",
|
|
2098
|
-
screenshotId,
|
|
2099
|
-
result: error
|
|
2100
|
-
? {
|
|
2101
|
-
status: "FAILED",
|
|
2102
|
-
startTime,
|
|
2103
|
-
endTime,
|
|
2104
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2105
|
-
}
|
|
2106
|
-
: {
|
|
2107
|
-
status: "PASSED",
|
|
2108
|
-
startTime,
|
|
2109
|
-
endTime,
|
|
2110
|
-
},
|
|
2111
|
-
info: info,
|
|
2112
|
-
});
|
|
1841
|
+
_commandFinally(state, this);
|
|
2113
1842
|
}
|
|
2114
1843
|
}
|
|
2115
1844
|
async extractEmailData(emailAddress, options, world) {
|
|
@@ -2186,7 +1915,8 @@ class StableBrowser {
|
|
|
2186
1915
|
catch (e) {
|
|
2187
1916
|
errorCount++;
|
|
2188
1917
|
if (errorCount > 3) {
|
|
2189
|
-
throw e;
|
|
1918
|
+
// throw e;
|
|
1919
|
+
await _commandError({ text: "extractEmailData", operation: "extractEmailData", emailAddress, info: {} }, e, this);
|
|
2190
1920
|
}
|
|
2191
1921
|
// ignore
|
|
2192
1922
|
}
|
|
@@ -2297,11 +2027,12 @@ class StableBrowser {
|
|
|
2297
2027
|
info.screenshotPath = screenshotPath;
|
|
2298
2028
|
Object.assign(e, { info: info });
|
|
2299
2029
|
error = e;
|
|
2300
|
-
throw e;
|
|
2030
|
+
// throw e;
|
|
2031
|
+
await _commandError({ text: "verifyPagePath", operation: "verifyPagePath", pathPart, info }, e, this);
|
|
2301
2032
|
}
|
|
2302
2033
|
finally {
|
|
2303
2034
|
const endTime = Date.now();
|
|
2304
|
-
|
|
2035
|
+
_reportToWorld(world, {
|
|
2305
2036
|
type: Types.VERIFY_PAGE_PATH,
|
|
2306
2037
|
text: "Verify page path",
|
|
2307
2038
|
screenshotId,
|
|
@@ -2310,7 +2041,7 @@ class StableBrowser {
|
|
|
2310
2041
|
status: "FAILED",
|
|
2311
2042
|
startTime,
|
|
2312
2043
|
endTime,
|
|
2313
|
-
message: error
|
|
2044
|
+
message: error?.message,
|
|
2314
2045
|
}
|
|
2315
2046
|
: {
|
|
2316
2047
|
status: "PASSED",
|
|
@@ -2323,52 +2054,58 @@ class StableBrowser {
|
|
|
2323
2054
|
}
|
|
2324
2055
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2325
2056
|
text = unEscapeString(text);
|
|
2326
|
-
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
|
+
};
|
|
2327
2069
|
const timeout = this._getLoadTimeout(options);
|
|
2328
|
-
let error = null;
|
|
2329
|
-
let screenshotId = null;
|
|
2330
|
-
let screenshotPath = null;
|
|
2331
2070
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2332
|
-
const info = {};
|
|
2333
|
-
info.log = "***** verify text " + text + " exists in page *****\n";
|
|
2334
|
-
info.operation = "verifyTextExistInPage";
|
|
2335
2071
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
2336
2072
|
if (newValue !== text) {
|
|
2337
2073
|
this.logger.info(text + "=" + newValue);
|
|
2338
2074
|
text = newValue;
|
|
2339
2075
|
}
|
|
2340
|
-
info.text = text;
|
|
2341
2076
|
let dateAlternatives = findDateAlternatives(text);
|
|
2342
2077
|
let numberAlternatives = findNumberAlternatives(text);
|
|
2343
2078
|
try {
|
|
2079
|
+
await _preCommand(state, this);
|
|
2080
|
+
state.info.text = text;
|
|
2344
2081
|
while (true) {
|
|
2345
2082
|
const frames = this.page.frames();
|
|
2346
2083
|
let results = [];
|
|
2347
2084
|
for (let i = 0; i < frames.length; i++) {
|
|
2348
2085
|
if (dateAlternatives.date) {
|
|
2349
2086
|
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2350
|
-
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, {});
|
|
2351
2088
|
result.frame = frames[i];
|
|
2352
2089
|
results.push(result);
|
|
2353
2090
|
}
|
|
2354
2091
|
}
|
|
2355
2092
|
else if (numberAlternatives.number) {
|
|
2356
2093
|
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2357
|
-
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, {});
|
|
2358
2095
|
result.frame = frames[i];
|
|
2359
2096
|
results.push(result);
|
|
2360
2097
|
}
|
|
2361
2098
|
}
|
|
2362
2099
|
else {
|
|
2363
|
-
const result = await this._locateElementByText(frames[i], text, "
|
|
2100
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2364
2101
|
result.frame = frames[i];
|
|
2365
2102
|
results.push(result);
|
|
2366
2103
|
}
|
|
2367
2104
|
}
|
|
2368
|
-
info.results = results;
|
|
2105
|
+
state.info.results = results;
|
|
2369
2106
|
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2370
2107
|
if (resultWithElementsFound.length === 0) {
|
|
2371
|
-
if (Date.now() - startTime > timeout) {
|
|
2108
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2372
2109
|
throw new Error(`Text ${text} not found in page`);
|
|
2373
2110
|
}
|
|
2374
2111
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -2380,44 +2117,89 @@ class StableBrowser {
|
|
|
2380
2117
|
await this._highlightElements(frame, dataAttribute);
|
|
2381
2118
|
const element = await frame.$(dataAttribute);
|
|
2382
2119
|
if (element) {
|
|
2383
|
-
await this.scrollIfNeeded(element, info);
|
|
2120
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2384
2121
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2385
2122
|
}
|
|
2386
2123
|
}
|
|
2387
|
-
|
|
2388
|
-
return info;
|
|
2124
|
+
await _screenshot(state, this);
|
|
2125
|
+
return state.info;
|
|
2389
2126
|
}
|
|
2390
2127
|
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2391
2128
|
}
|
|
2392
2129
|
catch (e) {
|
|
2393
|
-
|
|
2394
|
-
this.logger.error("verify text exist in page failed " + info.log);
|
|
2395
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2396
|
-
info.screenshotPath = screenshotPath;
|
|
2397
|
-
Object.assign(e, { info: info });
|
|
2398
|
-
error = e;
|
|
2399
|
-
throw e;
|
|
2130
|
+
await _commandError(state, e, this);
|
|
2400
2131
|
}
|
|
2401
2132
|
finally {
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
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
|
+
}
|
|
2413
2172
|
}
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
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);
|
|
2421
2203
|
}
|
|
2422
2204
|
}
|
|
2423
2205
|
_getServerUrl() {
|
|
@@ -2480,11 +2262,12 @@ class StableBrowser {
|
|
|
2480
2262
|
info.screenshotPath = screenshotPath;
|
|
2481
2263
|
Object.assign(e, { info: info });
|
|
2482
2264
|
error = e;
|
|
2483
|
-
throw e;
|
|
2265
|
+
// throw e;
|
|
2266
|
+
await _commandError({ text: "visualVerification", operation: "visualVerification", text, info }, e, this);
|
|
2484
2267
|
}
|
|
2485
2268
|
finally {
|
|
2486
2269
|
const endTime = Date.now();
|
|
2487
|
-
|
|
2270
|
+
_reportToWorld(world, {
|
|
2488
2271
|
type: Types.VERIFY_VISUAL,
|
|
2489
2272
|
text: "Visual verification",
|
|
2490
2273
|
screenshotId,
|
|
@@ -2493,7 +2276,7 @@ class StableBrowser {
|
|
|
2493
2276
|
status: "FAILED",
|
|
2494
2277
|
startTime,
|
|
2495
2278
|
endTime,
|
|
2496
|
-
message: error
|
|
2279
|
+
message: error?.message,
|
|
2497
2280
|
}
|
|
2498
2281
|
: {
|
|
2499
2282
|
status: "PASSED",
|
|
@@ -2525,7 +2308,7 @@ class StableBrowser {
|
|
|
2525
2308
|
this.logger.info("Table data verified");
|
|
2526
2309
|
}
|
|
2527
2310
|
async getTableData(selectors, _params = null, options = {}, world = null) {
|
|
2528
|
-
|
|
2311
|
+
_validateSelectors(selectors);
|
|
2529
2312
|
const startTime = Date.now();
|
|
2530
2313
|
let error = null;
|
|
2531
2314
|
let screenshotId = null;
|
|
@@ -2547,11 +2330,12 @@ class StableBrowser {
|
|
|
2547
2330
|
info.screenshotPath = screenshotPath;
|
|
2548
2331
|
Object.assign(e, { info: info });
|
|
2549
2332
|
error = e;
|
|
2550
|
-
throw e;
|
|
2333
|
+
// throw e;
|
|
2334
|
+
await _commandError({ text: "getTableData", operation: "getTableData", selectors, info }, e, this);
|
|
2551
2335
|
}
|
|
2552
2336
|
finally {
|
|
2553
2337
|
const endTime = Date.now();
|
|
2554
|
-
|
|
2338
|
+
_reportToWorld(world, {
|
|
2555
2339
|
element_name: selectors.element_name,
|
|
2556
2340
|
type: Types.GET_TABLE_DATA,
|
|
2557
2341
|
text: "Get table data",
|
|
@@ -2561,7 +2345,7 @@ class StableBrowser {
|
|
|
2561
2345
|
status: "FAILED",
|
|
2562
2346
|
startTime,
|
|
2563
2347
|
endTime,
|
|
2564
|
-
message: error
|
|
2348
|
+
message: error?.message,
|
|
2565
2349
|
}
|
|
2566
2350
|
: {
|
|
2567
2351
|
status: "PASSED",
|
|
@@ -2573,7 +2357,7 @@ class StableBrowser {
|
|
|
2573
2357
|
}
|
|
2574
2358
|
}
|
|
2575
2359
|
async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
|
|
2576
|
-
|
|
2360
|
+
_validateSelectors(selectors);
|
|
2577
2361
|
if (!query) {
|
|
2578
2362
|
throw new Error("query is null");
|
|
2579
2363
|
}
|
|
@@ -2712,11 +2496,12 @@ class StableBrowser {
|
|
|
2712
2496
|
info.screenshotPath = screenshotPath;
|
|
2713
2497
|
Object.assign(e, { info: info });
|
|
2714
2498
|
error = e;
|
|
2715
|
-
throw e;
|
|
2499
|
+
// throw e;
|
|
2500
|
+
await _commandError({ text: "analyzeTable", operation: "analyzeTable", selectors, query, operator, value }, e, this);
|
|
2716
2501
|
}
|
|
2717
2502
|
finally {
|
|
2718
2503
|
const endTime = Date.now();
|
|
2719
|
-
|
|
2504
|
+
_reportToWorld(world, {
|
|
2720
2505
|
element_name: selectors.element_name,
|
|
2721
2506
|
type: Types.ANALYZE_TABLE,
|
|
2722
2507
|
text: "Analyze table",
|
|
@@ -2726,7 +2511,7 @@ class StableBrowser {
|
|
|
2726
2511
|
status: "FAILED",
|
|
2727
2512
|
startTime,
|
|
2728
2513
|
endTime,
|
|
2729
|
-
message: error
|
|
2514
|
+
message: error?.message,
|
|
2730
2515
|
}
|
|
2731
2516
|
: {
|
|
2732
2517
|
status: "PASSED",
|
|
@@ -2738,27 +2523,7 @@ class StableBrowser {
|
|
|
2738
2523
|
}
|
|
2739
2524
|
}
|
|
2740
2525
|
async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
|
|
2741
|
-
|
|
2742
|
-
return value;
|
|
2743
|
-
}
|
|
2744
|
-
// find all the accurance of {{(.*?)}} and replace with the value
|
|
2745
|
-
let regex = /{{(.*?)}}/g;
|
|
2746
|
-
let matches = value.match(regex);
|
|
2747
|
-
if (matches) {
|
|
2748
|
-
const testData = this.getTestData(world);
|
|
2749
|
-
for (let i = 0; i < matches.length; i++) {
|
|
2750
|
-
let match = matches[i];
|
|
2751
|
-
let key = match.substring(2, match.length - 2);
|
|
2752
|
-
let newValue = objectPath.get(testData, key, null);
|
|
2753
|
-
if (newValue !== null) {
|
|
2754
|
-
value = value.replace(match, newValue);
|
|
2755
|
-
}
|
|
2756
|
-
}
|
|
2757
|
-
}
|
|
2758
|
-
if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
|
|
2759
|
-
return await decrypt(value, null, totpWait);
|
|
2760
|
-
}
|
|
2761
|
-
return value;
|
|
2526
|
+
return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
|
|
2762
2527
|
}
|
|
2763
2528
|
_getLoadTimeout(options) {
|
|
2764
2529
|
let timeout = 15000;
|
|
@@ -2809,7 +2574,7 @@ class StableBrowser {
|
|
|
2809
2574
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2810
2575
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2811
2576
|
const endTime = Date.now();
|
|
2812
|
-
|
|
2577
|
+
_reportToWorld(world, {
|
|
2813
2578
|
type: Types.GET_PAGE_STATUS,
|
|
2814
2579
|
text: "Wait for page load",
|
|
2815
2580
|
screenshotId,
|
|
@@ -2818,7 +2583,7 @@ class StableBrowser {
|
|
|
2818
2583
|
status: "FAILED",
|
|
2819
2584
|
startTime,
|
|
2820
2585
|
endTime,
|
|
2821
|
-
message: error
|
|
2586
|
+
message: error?.message,
|
|
2822
2587
|
}
|
|
2823
2588
|
: {
|
|
2824
2589
|
status: "PASSED",
|
|
@@ -2829,41 +2594,35 @@ class StableBrowser {
|
|
|
2829
2594
|
}
|
|
2830
2595
|
}
|
|
2831
2596
|
async closePage(options = {}, world = null) {
|
|
2832
|
-
const
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
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
|
+
};
|
|
2837
2609
|
try {
|
|
2610
|
+
await _preCommand(state, this);
|
|
2838
2611
|
await this.page.close();
|
|
2839
2612
|
}
|
|
2840
2613
|
catch (e) {
|
|
2841
2614
|
console.log(".");
|
|
2615
|
+
await _commandError(state, e, this);
|
|
2842
2616
|
}
|
|
2843
2617
|
finally {
|
|
2844
|
-
|
|
2845
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2846
|
-
const endTime = Date.now();
|
|
2847
|
-
this._reportToWorld(world, {
|
|
2848
|
-
type: Types.CLOSE_PAGE,
|
|
2849
|
-
text: "close page",
|
|
2850
|
-
screenshotId,
|
|
2851
|
-
result: error
|
|
2852
|
-
? {
|
|
2853
|
-
status: "FAILED",
|
|
2854
|
-
startTime,
|
|
2855
|
-
endTime,
|
|
2856
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
2857
|
-
}
|
|
2858
|
-
: {
|
|
2859
|
-
status: "PASSED",
|
|
2860
|
-
startTime,
|
|
2861
|
-
endTime,
|
|
2862
|
-
},
|
|
2863
|
-
info: info,
|
|
2864
|
-
});
|
|
2618
|
+
_commandFinally(state, this);
|
|
2865
2619
|
}
|
|
2866
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
|
+
}
|
|
2867
2626
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
2868
2627
|
const startTime = Date.now();
|
|
2869
2628
|
let error = null;
|
|
@@ -2881,12 +2640,13 @@ class StableBrowser {
|
|
|
2881
2640
|
}
|
|
2882
2641
|
catch (e) {
|
|
2883
2642
|
console.log(".");
|
|
2643
|
+
await _commandError({ text: "setViewportSize", operation: "setViewportSize", width, hight, info }, e, this);
|
|
2884
2644
|
}
|
|
2885
2645
|
finally {
|
|
2886
2646
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2887
2647
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2888
2648
|
const endTime = Date.now();
|
|
2889
|
-
|
|
2649
|
+
_reportToWorld(world, {
|
|
2890
2650
|
type: Types.SET_VIEWPORT,
|
|
2891
2651
|
text: "set viewport size to " + width + "x" + hight,
|
|
2892
2652
|
screenshotId,
|
|
@@ -2895,7 +2655,7 @@ class StableBrowser {
|
|
|
2895
2655
|
status: "FAILED",
|
|
2896
2656
|
startTime,
|
|
2897
2657
|
endTime,
|
|
2898
|
-
message: error
|
|
2658
|
+
message: error?.message,
|
|
2899
2659
|
}
|
|
2900
2660
|
: {
|
|
2901
2661
|
status: "PASSED",
|
|
@@ -2917,12 +2677,13 @@ class StableBrowser {
|
|
|
2917
2677
|
}
|
|
2918
2678
|
catch (e) {
|
|
2919
2679
|
console.log(".");
|
|
2680
|
+
await _commandError({ text: "reloadPage", operation: "reloadPage", info }, e, this);
|
|
2920
2681
|
}
|
|
2921
2682
|
finally {
|
|
2922
2683
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2923
2684
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2924
2685
|
const endTime = Date.now();
|
|
2925
|
-
|
|
2686
|
+
_reportToWorld(world, {
|
|
2926
2687
|
type: Types.GET_PAGE_STATUS,
|
|
2927
2688
|
text: "page relaod",
|
|
2928
2689
|
screenshotId,
|
|
@@ -2931,7 +2692,7 @@ class StableBrowser {
|
|
|
2931
2692
|
status: "FAILED",
|
|
2932
2693
|
startTime,
|
|
2933
2694
|
endTime,
|
|
2934
|
-
message: error
|
|
2695
|
+
message: error?.message,
|
|
2935
2696
|
}
|
|
2936
2697
|
: {
|
|
2937
2698
|
status: "PASSED",
|
|
@@ -2958,11 +2719,37 @@ class StableBrowser {
|
|
|
2958
2719
|
console.log("#-#");
|
|
2959
2720
|
}
|
|
2960
2721
|
}
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
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
|
+
}
|
|
2964
2752
|
}
|
|
2965
|
-
world.attach(JSON.stringify(properties), { mediaType: "application/json" });
|
|
2966
2753
|
}
|
|
2967
2754
|
}
|
|
2968
2755
|
function createTimedPromise(promise, label) {
|