automation_model 1.0.432-dev → 1.0.432-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 +199 -13
- package/lib/api.js.map +1 -1
- package/lib/auto_page.js +1 -24
- package/lib/auto_page.js.map +1 -1
- package/lib/axe/axe.mini.js +12 -0
- package/lib/browser_manager.js +8 -3
- package/lib/browser_manager.js.map +1 -1
- package/lib/command_common.d.ts +5 -0
- package/lib/command_common.js +111 -0
- package/lib/command_common.js.map +1 -0
- package/lib/environment.d.ts +3 -0
- package/lib/environment.js +5 -2
- package/lib/environment.js.map +1 -1
- package/lib/init_browser.d.ts +2 -1
- package/lib/init_browser.js +38 -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/stable_browser.d.ts +26 -16
- package/lib/stable_browser.js +535 -628
- package/lib/stable_browser.js.map +1 -1
- package/lib/test_context.d.ts +2 -0
- package/lib/test_context.js +11 -10
- 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 +5 -4
package/lib/stable_browser.js
CHANGED
|
@@ -2,19 +2,21 @@
|
|
|
2
2
|
import { expect } from "@playwright/test";
|
|
3
3
|
import dayjs from "dayjs";
|
|
4
4
|
import fs from "fs";
|
|
5
|
+
import { Jimp } from "jimp";
|
|
5
6
|
import path from "path";
|
|
6
7
|
import reg_parser from "regex-parser";
|
|
7
|
-
import sharp from "sharp";
|
|
8
8
|
import { findDateAlternatives, findNumberAlternatives } from "./analyze_helper.js";
|
|
9
9
|
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";
|
|
17
|
+
import { getContext } from "./init_browser.js";
|
|
18
|
+
import { locate_element } from "./locate_element.js";
|
|
19
|
+
import { _commandError, _commandFinally, _preCommand, _validateSelectors, _screenshot } from "./command_common.js";
|
|
18
20
|
const Types = {
|
|
19
21
|
CLICK: "click_element",
|
|
20
22
|
NAVIGATE: "navigate",
|
|
@@ -42,15 +44,24 @@ const Types = {
|
|
|
42
44
|
LOAD_DATA: "load_data",
|
|
43
45
|
SET_INPUT: "set_input",
|
|
44
46
|
};
|
|
47
|
+
export const apps = {};
|
|
45
48
|
class StableBrowser {
|
|
46
|
-
|
|
49
|
+
browser;
|
|
50
|
+
page;
|
|
51
|
+
logger;
|
|
52
|
+
context;
|
|
53
|
+
world;
|
|
54
|
+
project_path = null;
|
|
55
|
+
webLogFile = null;
|
|
56
|
+
networkLogger = null;
|
|
57
|
+
configuration = null;
|
|
58
|
+
appName = "main";
|
|
59
|
+
constructor(browser, page, logger = null, context = null, world = null) {
|
|
47
60
|
this.browser = browser;
|
|
48
61
|
this.page = page;
|
|
49
62
|
this.logger = logger;
|
|
50
63
|
this.context = context;
|
|
51
|
-
this.
|
|
52
|
-
this.webLogFile = null;
|
|
53
|
-
this.configuration = null;
|
|
64
|
+
this.world = world;
|
|
54
65
|
if (!this.logger) {
|
|
55
66
|
this.logger = console;
|
|
56
67
|
}
|
|
@@ -76,11 +87,17 @@ class StableBrowser {
|
|
|
76
87
|
this.logger.error("unable to read ai_config.json");
|
|
77
88
|
}
|
|
78
89
|
const logFolder = path.join(this.project_path, "logs", "web");
|
|
79
|
-
this.
|
|
80
|
-
this.registerConsoleLogListener(page, context, this.webLogFile);
|
|
81
|
-
this.registerRequestListener();
|
|
90
|
+
this.world = world;
|
|
82
91
|
context.pages = [this.page];
|
|
83
92
|
context.pageLoading = { status: false };
|
|
93
|
+
this.registerEventListeners(this.context);
|
|
94
|
+
}
|
|
95
|
+
registerEventListeners(context) {
|
|
96
|
+
this.registerConsoleLogListener(this.page, context);
|
|
97
|
+
this.registerRequestListener(this.page, context, this.webLogFile);
|
|
98
|
+
if (!context.pageLoading) {
|
|
99
|
+
context.pageLoading = { status: false };
|
|
100
|
+
}
|
|
84
101
|
context.playContext.on("page", async function (page) {
|
|
85
102
|
context.pageLoading.status = true;
|
|
86
103
|
this.page = page;
|
|
@@ -110,6 +127,36 @@ class StableBrowser {
|
|
|
110
127
|
context.pageLoading.status = false;
|
|
111
128
|
}.bind(this));
|
|
112
129
|
}
|
|
130
|
+
async switchApp(appName) {
|
|
131
|
+
// check if the current app (this.appName) is the same as the new app
|
|
132
|
+
if (this.appName === appName) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
let navigate = false;
|
|
136
|
+
if (!apps[appName]) {
|
|
137
|
+
let newContext = await getContext(null, false, this.logger, appName, false, this);
|
|
138
|
+
navigate = true;
|
|
139
|
+
apps[appName] = {
|
|
140
|
+
context: newContext,
|
|
141
|
+
browser: newContext.browser,
|
|
142
|
+
page: newContext.page,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const tempContext = {};
|
|
146
|
+
this._copyContext(this, tempContext);
|
|
147
|
+
this._copyContext(apps[appName], this);
|
|
148
|
+
apps[this.appName] = tempContext;
|
|
149
|
+
this.appName = appName;
|
|
150
|
+
if (navigate) {
|
|
151
|
+
await this.goto(this.context.environment.baseUrl);
|
|
152
|
+
await this.waitForPageLoad();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
_copyContext(from, to) {
|
|
156
|
+
to.browser = from.browser;
|
|
157
|
+
to.page = from.page;
|
|
158
|
+
to.context = from.context;
|
|
159
|
+
}
|
|
113
160
|
getWebLogFile(logFolder) {
|
|
114
161
|
if (!fs.existsSync(logFolder)) {
|
|
115
162
|
fs.mkdirSync(logFolder, { recursive: true });
|
|
@@ -121,37 +168,63 @@ class StableBrowser {
|
|
|
121
168
|
const fileName = nextIndex + ".json";
|
|
122
169
|
return path.join(logFolder, fileName);
|
|
123
170
|
}
|
|
124
|
-
registerConsoleLogListener(page, context
|
|
171
|
+
registerConsoleLogListener(page, context) {
|
|
125
172
|
if (!this.context.webLogger) {
|
|
126
173
|
this.context.webLogger = [];
|
|
127
174
|
}
|
|
128
175
|
page.on("console", async (msg) => {
|
|
129
|
-
|
|
176
|
+
const obj = {
|
|
130
177
|
type: msg.type(),
|
|
131
178
|
text: msg.text(),
|
|
132
179
|
location: msg.location(),
|
|
133
180
|
time: new Date().toISOString(),
|
|
134
|
-
}
|
|
135
|
-
|
|
181
|
+
};
|
|
182
|
+
this.context.webLogger.push(obj);
|
|
183
|
+
if (msg.type() === "error") {
|
|
184
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
|
|
185
|
+
}
|
|
136
186
|
});
|
|
137
187
|
}
|
|
138
|
-
registerRequestListener() {
|
|
139
|
-
this.
|
|
188
|
+
registerRequestListener(page, context, logFile) {
|
|
189
|
+
if (!this.context.networkLogger) {
|
|
190
|
+
this.context.networkLogger = [];
|
|
191
|
+
}
|
|
192
|
+
page.on("request", async (data) => {
|
|
193
|
+
const startTime = new Date().getTime();
|
|
140
194
|
try {
|
|
141
|
-
const pageUrl = new URL(
|
|
195
|
+
const pageUrl = new URL(page.url());
|
|
142
196
|
const requestUrl = new URL(data.url());
|
|
143
197
|
if (pageUrl.hostname === requestUrl.hostname) {
|
|
144
198
|
const method = data.method();
|
|
145
|
-
if (
|
|
199
|
+
if (["POST", "GET", "PUT", "DELETE", "PATCH"].includes(method)) {
|
|
146
200
|
const token = await data.headerValue("Authorization");
|
|
147
201
|
if (token) {
|
|
148
|
-
|
|
202
|
+
context.authtoken = token;
|
|
149
203
|
}
|
|
150
204
|
}
|
|
151
205
|
}
|
|
206
|
+
const response = await data.response();
|
|
207
|
+
const endTime = new Date().getTime();
|
|
208
|
+
const obj = {
|
|
209
|
+
url: data.url(),
|
|
210
|
+
method: data.method(),
|
|
211
|
+
postData: data.postData(),
|
|
212
|
+
error: data.failure() ? data.failure().errorText : null,
|
|
213
|
+
duration: endTime - startTime,
|
|
214
|
+
startTime,
|
|
215
|
+
};
|
|
216
|
+
context.networkLogger.push(obj);
|
|
217
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
|
|
152
218
|
}
|
|
153
219
|
catch (error) {
|
|
154
220
|
console.error("Error in request listener", error);
|
|
221
|
+
context.networkLogger.push({
|
|
222
|
+
error: "not able to listen",
|
|
223
|
+
message: error.message,
|
|
224
|
+
stack: error.stack,
|
|
225
|
+
time: new Date().toISOString(),
|
|
226
|
+
});
|
|
227
|
+
// await fs.promises.writeFile(logFile, JSON.stringify(context.networkLogger, null, 2));
|
|
155
228
|
}
|
|
156
229
|
});
|
|
157
230
|
}
|
|
@@ -166,20 +239,6 @@ class StableBrowser {
|
|
|
166
239
|
timeout: 60000,
|
|
167
240
|
});
|
|
168
241
|
}
|
|
169
|
-
_validateSelectors(selectors) {
|
|
170
|
-
if (!selectors) {
|
|
171
|
-
throw new Error("selectors is null");
|
|
172
|
-
}
|
|
173
|
-
if (!selectors.locators) {
|
|
174
|
-
throw new Error("selectors.locators is null");
|
|
175
|
-
}
|
|
176
|
-
if (!Array.isArray(selectors.locators)) {
|
|
177
|
-
throw new Error("selectors.locators expected to be array");
|
|
178
|
-
}
|
|
179
|
-
if (selectors.locators.length === 0) {
|
|
180
|
-
throw new Error("selectors.locators expected to be non empty array");
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
242
|
_fixUsingParams(text, _params) {
|
|
184
243
|
if (!_params || typeof text !== "string") {
|
|
185
244
|
return text;
|
|
@@ -247,7 +306,7 @@ class StableBrowser {
|
|
|
247
306
|
locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
|
|
248
307
|
}
|
|
249
308
|
}
|
|
250
|
-
if (locator
|
|
309
|
+
if (locator?.engine) {
|
|
251
310
|
if (locator.engine === "css") {
|
|
252
311
|
locatorReturn = scope.locator(locator.selector);
|
|
253
312
|
}
|
|
@@ -268,7 +327,10 @@ class StableBrowser {
|
|
|
268
327
|
return locatorReturn;
|
|
269
328
|
}
|
|
270
329
|
async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
|
|
271
|
-
|
|
330
|
+
if (css && css.locator) {
|
|
331
|
+
css = css.locator;
|
|
332
|
+
}
|
|
333
|
+
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, false, _params);
|
|
272
334
|
if (result.elementCount === 0) {
|
|
273
335
|
return;
|
|
274
336
|
}
|
|
@@ -283,7 +345,7 @@ class StableBrowser {
|
|
|
283
345
|
}
|
|
284
346
|
async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
|
|
285
347
|
//const stringifyText = JSON.stringify(text);
|
|
286
|
-
return await scope.evaluate(([text, tag, regex, partial]) => {
|
|
348
|
+
return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
|
|
287
349
|
function isParent(parent, child) {
|
|
288
350
|
let currentNode = child.parentNode;
|
|
289
351
|
while (currentNode !== null) {
|
|
@@ -295,6 +357,15 @@ class StableBrowser {
|
|
|
295
357
|
return false;
|
|
296
358
|
}
|
|
297
359
|
document.isParent = isParent;
|
|
360
|
+
function getRegex(str) {
|
|
361
|
+
const match = str.match(/^\/(.*?)\/([gimuy]*)$/);
|
|
362
|
+
if (!match) {
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
let [_, pattern, flags] = match;
|
|
366
|
+
return new RegExp(pattern, flags);
|
|
367
|
+
}
|
|
368
|
+
document.getRegex = getRegex;
|
|
298
369
|
function collectAllShadowDomElements(element, result = []) {
|
|
299
370
|
// Check and add the element if it has a shadow root
|
|
300
371
|
if (element.shadowRoot) {
|
|
@@ -313,6 +384,10 @@ class StableBrowser {
|
|
|
313
384
|
if (!tag) {
|
|
314
385
|
tag = "*";
|
|
315
386
|
}
|
|
387
|
+
let regexpSearch = document.getRegex(text);
|
|
388
|
+
if (regexpSearch) {
|
|
389
|
+
regex = true;
|
|
390
|
+
}
|
|
316
391
|
let elements = Array.from(document.querySelectorAll(tag));
|
|
317
392
|
let shadowHosts = [];
|
|
318
393
|
document.collectAllShadowDomElements(document, shadowHosts);
|
|
@@ -328,7 +403,9 @@ class StableBrowser {
|
|
|
328
403
|
let randomToken = null;
|
|
329
404
|
const foundElements = [];
|
|
330
405
|
if (regex) {
|
|
331
|
-
|
|
406
|
+
if (!regexpSearch) {
|
|
407
|
+
regexpSearch = new RegExp(text, "im");
|
|
408
|
+
}
|
|
332
409
|
for (let i = 0; i < elements.length; i++) {
|
|
333
410
|
const element = elements[i];
|
|
334
411
|
if ((element.innerText && regexpSearch.test(element.innerText)) ||
|
|
@@ -342,8 +419,8 @@ class StableBrowser {
|
|
|
342
419
|
for (let i = 0; i < elements.length; i++) {
|
|
343
420
|
const element = elements[i];
|
|
344
421
|
if (partial) {
|
|
345
|
-
if ((element.innerText && element.innerText.trim().includes(text)) ||
|
|
346
|
-
(element.value && element.value.includes(text))) {
|
|
422
|
+
if ((element.innerText && element.innerText.toLowerCase().trim().includes(text.toLowerCase())) ||
|
|
423
|
+
(element.value && element.value.toLowerCase().includes(text.toLowerCase()))) {
|
|
347
424
|
foundElements.push(element);
|
|
348
425
|
}
|
|
349
426
|
}
|
|
@@ -388,6 +465,12 @@ class StableBrowser {
|
|
|
388
465
|
}
|
|
389
466
|
async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
|
|
390
467
|
let locatorSearch = selectorHierarchy[index];
|
|
468
|
+
try {
|
|
469
|
+
locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
|
|
470
|
+
}
|
|
471
|
+
catch (e) {
|
|
472
|
+
console.error(e);
|
|
473
|
+
}
|
|
391
474
|
//info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
|
|
392
475
|
let locator = null;
|
|
393
476
|
if (locatorSearch.climb && locatorSearch.climb >= 0) {
|
|
@@ -476,13 +559,18 @@ class StableBrowser {
|
|
|
476
559
|
if (result.foundElements.length > 0) {
|
|
477
560
|
let dialogCloseLocator = result.foundElements[0].locator;
|
|
478
561
|
await dialogCloseLocator.click();
|
|
562
|
+
// wait for the dialog to close
|
|
563
|
+
await dialogCloseLocator.waitFor({ state: "hidden" });
|
|
479
564
|
return { rerun: true };
|
|
480
565
|
}
|
|
481
566
|
}
|
|
482
567
|
}
|
|
483
568
|
return { rerun: false };
|
|
484
569
|
}
|
|
485
|
-
async _locate(selectors, info, _params, timeout
|
|
570
|
+
async _locate(selectors, info, _params, timeout) {
|
|
571
|
+
if (!timeout) {
|
|
572
|
+
timeout = 30000;
|
|
573
|
+
}
|
|
486
574
|
for (let i = 0; i < 3; i++) {
|
|
487
575
|
info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
|
|
488
576
|
for (let j = 0; j < selectors.locators.length; j++) {
|
|
@@ -496,17 +584,44 @@ class StableBrowser {
|
|
|
496
584
|
}
|
|
497
585
|
throw new Error("unable to locate element " + JSON.stringify(selectors));
|
|
498
586
|
}
|
|
499
|
-
async
|
|
500
|
-
let highPriorityTimeout = 5000;
|
|
501
|
-
let visibleOnlyTimeout = 6000;
|
|
502
|
-
let startTime = performance.now();
|
|
503
|
-
let locatorsCount = 0;
|
|
504
|
-
//let arrayMode = Array.isArray(selectors);
|
|
587
|
+
async _findFrameScope(selectors, timeout = 30000) {
|
|
505
588
|
let scope = this.page;
|
|
589
|
+
if (selectors.frame) {
|
|
590
|
+
return selectors.frame;
|
|
591
|
+
}
|
|
506
592
|
if (selectors.iframe_src || selectors.frameLocators) {
|
|
507
|
-
|
|
593
|
+
const findFrame = async (frame, framescope) => {
|
|
594
|
+
for (let i = 0; i < frame.selectors.length; i++) {
|
|
595
|
+
let frameLocator = frame.selectors[i];
|
|
596
|
+
if (frameLocator.css) {
|
|
597
|
+
let testframescope = framescope.frameLocator(frameLocator.css);
|
|
598
|
+
if (frameLocator.index) {
|
|
599
|
+
testframescope = framescope.nth(frameLocator.index);
|
|
600
|
+
}
|
|
601
|
+
try {
|
|
602
|
+
await testframescope.owner().evaluateHandle(() => true, null, {
|
|
603
|
+
timeout: 5000,
|
|
604
|
+
});
|
|
605
|
+
framescope = testframescope;
|
|
606
|
+
break;
|
|
607
|
+
}
|
|
608
|
+
catch (error) {
|
|
609
|
+
console.error("frame not found " + frameLocator.css);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
if (frame.children) {
|
|
614
|
+
return await findFrame(frame.children, framescope);
|
|
615
|
+
}
|
|
616
|
+
return framescope;
|
|
617
|
+
};
|
|
508
618
|
while (true) {
|
|
509
619
|
let frameFound = false;
|
|
620
|
+
if (selectors.nestFrmLoc) {
|
|
621
|
+
scope = await findFrame(selectors.nestFrmLoc, scope);
|
|
622
|
+
frameFound = true;
|
|
623
|
+
break;
|
|
624
|
+
}
|
|
510
625
|
if (selectors.frameLocators) {
|
|
511
626
|
for (let i = 0; i < selectors.frameLocators.length; i++) {
|
|
512
627
|
let frameLocator = selectors.frameLocators[i];
|
|
@@ -532,6 +647,25 @@ class StableBrowser {
|
|
|
532
647
|
}
|
|
533
648
|
}
|
|
534
649
|
}
|
|
650
|
+
if (!scope) {
|
|
651
|
+
scope = this.page;
|
|
652
|
+
}
|
|
653
|
+
return scope;
|
|
654
|
+
}
|
|
655
|
+
async _getDocumentBody(selectors, timeout = 30000) {
|
|
656
|
+
let scope = await this._findFrameScope(selectors, timeout);
|
|
657
|
+
return scope.evaluate(() => {
|
|
658
|
+
var bodyContent = document.body.innerHTML;
|
|
659
|
+
return bodyContent;
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
663
|
+
let highPriorityTimeout = 5000;
|
|
664
|
+
let visibleOnlyTimeout = 6000;
|
|
665
|
+
let startTime = performance.now();
|
|
666
|
+
let locatorsCount = 0;
|
|
667
|
+
//let arrayMode = Array.isArray(selectors);
|
|
668
|
+
let scope = await this._findFrameScope(selectors, timeout);
|
|
535
669
|
let selectorsLocators = null;
|
|
536
670
|
selectorsLocators = selectors.locators;
|
|
537
671
|
// group selectors by priority
|
|
@@ -667,86 +801,121 @@ class StableBrowser {
|
|
|
667
801
|
}
|
|
668
802
|
return result;
|
|
669
803
|
}
|
|
670
|
-
async
|
|
671
|
-
this._validateSelectors(selectors);
|
|
804
|
+
async simpleClick(elementDescription, _params, options = {}, world = null) {
|
|
672
805
|
const startTime = Date.now();
|
|
673
|
-
|
|
674
|
-
|
|
806
|
+
let timeout = 30000;
|
|
807
|
+
if (options && options.timeout) {
|
|
808
|
+
timeout = options.timeout;
|
|
675
809
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
810
|
+
while (true) {
|
|
811
|
+
try {
|
|
812
|
+
const result = await locate_element(this.context, elementDescription, "click");
|
|
813
|
+
if (result?.elementNumber >= 0) {
|
|
814
|
+
const selectors = {
|
|
815
|
+
frame: result?.frame,
|
|
816
|
+
locators: [
|
|
817
|
+
{
|
|
818
|
+
css: result?.css,
|
|
819
|
+
},
|
|
820
|
+
],
|
|
821
|
+
};
|
|
822
|
+
await this.click(selectors, _params, options, world);
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
catch (e) {
|
|
827
|
+
if (performance.now() - startTime > timeout) {
|
|
828
|
+
throw e;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
|
|
835
|
+
const startTime = Date.now();
|
|
836
|
+
let timeout = 30000;
|
|
837
|
+
if (options && options.timeout) {
|
|
838
|
+
timeout = options.timeout;
|
|
839
|
+
}
|
|
840
|
+
while (true) {
|
|
841
|
+
try {
|
|
842
|
+
const result = await locate_element(this.context, elementDescription, "fill", value);
|
|
843
|
+
if (result?.elementNumber >= 0) {
|
|
844
|
+
const selectors = {
|
|
845
|
+
frame: result?.frame,
|
|
846
|
+
locators: [
|
|
847
|
+
{
|
|
848
|
+
css: result?.css,
|
|
849
|
+
},
|
|
850
|
+
],
|
|
851
|
+
};
|
|
852
|
+
await this.clickType(selectors, value, false, _params, options, world);
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
catch (e) {
|
|
857
|
+
if (performance.now() - startTime > timeout) {
|
|
858
|
+
throw e;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
async click(selectors, _params, options = {}, world = null) {
|
|
865
|
+
const state = {
|
|
866
|
+
selectors,
|
|
867
|
+
_params,
|
|
868
|
+
options,
|
|
869
|
+
world,
|
|
870
|
+
text: "Click element",
|
|
871
|
+
type: Types.CLICK,
|
|
872
|
+
operation: "click",
|
|
873
|
+
log: "***** click on " + selectors.element_name + " *****\n",
|
|
874
|
+
};
|
|
683
875
|
try {
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
876
|
+
await _preCommand(state, this);
|
|
877
|
+
if (state.options && state.options.context) {
|
|
878
|
+
state.selectors.locators[0].text = state.options.context;
|
|
879
|
+
}
|
|
687
880
|
try {
|
|
688
|
-
await
|
|
689
|
-
await element.click();
|
|
881
|
+
await state.element.click();
|
|
690
882
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
691
883
|
}
|
|
692
884
|
catch (e) {
|
|
693
885
|
// await this.closeUnexpectedPopups();
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
await element.dispatchEvent("click");
|
|
886
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
887
|
+
await state.element.dispatchEvent("click");
|
|
697
888
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
698
889
|
}
|
|
699
890
|
await this.waitForPageLoad();
|
|
700
|
-
return info;
|
|
891
|
+
return state.info;
|
|
701
892
|
}
|
|
702
893
|
catch (e) {
|
|
703
|
-
|
|
704
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
705
|
-
info.screenshotPath = screenshotPath;
|
|
706
|
-
Object.assign(e, { info: info });
|
|
707
|
-
error = e;
|
|
708
|
-
throw e;
|
|
894
|
+
await _commandError(state, e, this);
|
|
709
895
|
}
|
|
710
896
|
finally {
|
|
711
|
-
|
|
712
|
-
this._reportToWorld(world, {
|
|
713
|
-
element_name: selectors.element_name,
|
|
714
|
-
type: Types.CLICK,
|
|
715
|
-
text: `Click element`,
|
|
716
|
-
screenshotId,
|
|
717
|
-
result: error
|
|
718
|
-
? {
|
|
719
|
-
status: "FAILED",
|
|
720
|
-
startTime,
|
|
721
|
-
endTime,
|
|
722
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
723
|
-
}
|
|
724
|
-
: {
|
|
725
|
-
status: "PASSED",
|
|
726
|
-
startTime,
|
|
727
|
-
endTime,
|
|
728
|
-
},
|
|
729
|
-
info: info,
|
|
730
|
-
});
|
|
897
|
+
_commandFinally(state, this);
|
|
731
898
|
}
|
|
732
899
|
}
|
|
733
900
|
async setCheck(selectors, checked = true, _params, options = {}, world = null) {
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
901
|
+
const state = {
|
|
902
|
+
selectors,
|
|
903
|
+
_params,
|
|
904
|
+
options,
|
|
905
|
+
world,
|
|
906
|
+
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
907
|
+
text: checked ? `Check element` : `Uncheck element`,
|
|
908
|
+
operation: "setCheck",
|
|
909
|
+
log: "***** check " + selectors.element_name + " *****\n",
|
|
910
|
+
};
|
|
744
911
|
try {
|
|
745
|
-
|
|
746
|
-
|
|
912
|
+
await _preCommand(state, this);
|
|
913
|
+
state.info.checked = checked;
|
|
914
|
+
// let element = await this._locate(selectors, info, _params);
|
|
915
|
+
// ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
747
916
|
try {
|
|
748
|
-
await this._highlightElements(element);
|
|
749
|
-
await element.setChecked(checked);
|
|
917
|
+
// await this._highlightElements(element);
|
|
918
|
+
await state.element.setChecked(checked);
|
|
750
919
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
751
920
|
}
|
|
752
921
|
catch (e) {
|
|
@@ -755,179 +924,108 @@ class StableBrowser {
|
|
|
755
924
|
}
|
|
756
925
|
else {
|
|
757
926
|
//await this.closeUnexpectedPopups();
|
|
758
|
-
info.log += "setCheck failed, will try again" + "\n";
|
|
759
|
-
element = await this._locate(selectors, info, _params);
|
|
760
|
-
await element.setChecked(checked, { timeout: 5000, force: true });
|
|
927
|
+
state.info.log += "setCheck failed, will try again" + "\n";
|
|
928
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
929
|
+
await state.element.setChecked(checked, { timeout: 5000, force: true });
|
|
761
930
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
762
931
|
}
|
|
763
932
|
}
|
|
764
933
|
await this.waitForPageLoad();
|
|
765
|
-
return info;
|
|
934
|
+
return state.info;
|
|
766
935
|
}
|
|
767
936
|
catch (e) {
|
|
768
|
-
|
|
769
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
770
|
-
info.screenshotPath = screenshotPath;
|
|
771
|
-
Object.assign(e, { info: info });
|
|
772
|
-
error = e;
|
|
773
|
-
throw e;
|
|
937
|
+
await _commandError(state, e, this);
|
|
774
938
|
}
|
|
775
939
|
finally {
|
|
776
|
-
|
|
777
|
-
this._reportToWorld(world, {
|
|
778
|
-
element_name: selectors.element_name,
|
|
779
|
-
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
780
|
-
text: checked ? `Check element` : `Uncheck element`,
|
|
781
|
-
screenshotId,
|
|
782
|
-
result: error
|
|
783
|
-
? {
|
|
784
|
-
status: "FAILED",
|
|
785
|
-
startTime,
|
|
786
|
-
endTime,
|
|
787
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
788
|
-
}
|
|
789
|
-
: {
|
|
790
|
-
status: "PASSED",
|
|
791
|
-
startTime,
|
|
792
|
-
endTime,
|
|
793
|
-
},
|
|
794
|
-
info: info,
|
|
795
|
-
});
|
|
940
|
+
_commandFinally(state, this);
|
|
796
941
|
}
|
|
797
942
|
}
|
|
798
943
|
async hover(selectors, _params, options = {}, world = null) {
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
944
|
+
const state = {
|
|
945
|
+
selectors,
|
|
946
|
+
_params,
|
|
947
|
+
options,
|
|
948
|
+
world,
|
|
949
|
+
type: Types.HOVER,
|
|
950
|
+
text: `Hover element`,
|
|
951
|
+
operation: "hover",
|
|
952
|
+
log: "***** hover " + selectors.element_name + " *****\n",
|
|
953
|
+
};
|
|
808
954
|
try {
|
|
809
|
-
|
|
810
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
955
|
+
await _preCommand(state, this);
|
|
811
956
|
try {
|
|
812
|
-
await
|
|
813
|
-
await element.hover();
|
|
957
|
+
await state.element.hover();
|
|
814
958
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
815
959
|
}
|
|
816
960
|
catch (e) {
|
|
817
961
|
//await this.closeUnexpectedPopups();
|
|
818
|
-
info.log += "hover failed, will try again" + "\n";
|
|
819
|
-
element = await this._locate(selectors, info, _params);
|
|
820
|
-
await element.hover({ timeout: 10000 });
|
|
962
|
+
state.info.log += "hover failed, will try again" + "\n";
|
|
963
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
964
|
+
await state.element.hover({ timeout: 10000 });
|
|
821
965
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
822
966
|
}
|
|
823
967
|
await this.waitForPageLoad();
|
|
824
|
-
return info;
|
|
968
|
+
return state.info;
|
|
825
969
|
}
|
|
826
970
|
catch (e) {
|
|
827
|
-
|
|
828
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
829
|
-
info.screenshotPath = screenshotPath;
|
|
830
|
-
Object.assign(e, { info: info });
|
|
831
|
-
error = e;
|
|
832
|
-
throw e;
|
|
971
|
+
await _commandError(state, e, this);
|
|
833
972
|
}
|
|
834
973
|
finally {
|
|
835
|
-
|
|
836
|
-
this._reportToWorld(world, {
|
|
837
|
-
element_name: selectors.element_name,
|
|
838
|
-
type: Types.HOVER,
|
|
839
|
-
text: `Hover element`,
|
|
840
|
-
screenshotId,
|
|
841
|
-
result: error
|
|
842
|
-
? {
|
|
843
|
-
status: "FAILED",
|
|
844
|
-
startTime,
|
|
845
|
-
endTime,
|
|
846
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
847
|
-
}
|
|
848
|
-
: {
|
|
849
|
-
status: "PASSED",
|
|
850
|
-
startTime,
|
|
851
|
-
endTime,
|
|
852
|
-
},
|
|
853
|
-
info: info,
|
|
854
|
-
});
|
|
974
|
+
_commandFinally(state, this);
|
|
855
975
|
}
|
|
856
976
|
}
|
|
857
977
|
async selectOption(selectors, values, _params = null, options = {}, world = null) {
|
|
858
|
-
this._validateSelectors(selectors);
|
|
859
978
|
if (!values) {
|
|
860
979
|
throw new Error("values is null");
|
|
861
980
|
}
|
|
862
|
-
const
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
981
|
+
const state = {
|
|
982
|
+
selectors,
|
|
983
|
+
_params,
|
|
984
|
+
options,
|
|
985
|
+
world,
|
|
986
|
+
value: values.toString(),
|
|
987
|
+
type: Types.SELECT,
|
|
988
|
+
text: `Select option: ${values}`,
|
|
989
|
+
operation: "selectOption",
|
|
990
|
+
log: "***** select option " + selectors.element_name + " *****\n",
|
|
991
|
+
};
|
|
870
992
|
try {
|
|
871
|
-
|
|
872
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
993
|
+
await _preCommand(state, this);
|
|
873
994
|
try {
|
|
874
|
-
await
|
|
875
|
-
await element.selectOption(values);
|
|
995
|
+
await state.element.selectOption(values);
|
|
876
996
|
}
|
|
877
997
|
catch (e) {
|
|
878
998
|
//await this.closeUnexpectedPopups();
|
|
879
|
-
info.log += "selectOption failed, will try force" + "\n";
|
|
880
|
-
await element.selectOption(values, { timeout: 10000, force: true });
|
|
999
|
+
state.info.log += "selectOption failed, will try force" + "\n";
|
|
1000
|
+
await state.element.selectOption(values, { timeout: 10000, force: true });
|
|
881
1001
|
}
|
|
882
1002
|
await this.waitForPageLoad();
|
|
883
|
-
return info;
|
|
1003
|
+
return state.info;
|
|
884
1004
|
}
|
|
885
1005
|
catch (e) {
|
|
886
|
-
|
|
887
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
888
|
-
info.screenshotPath = screenshotPath;
|
|
889
|
-
Object.assign(e, { info: info });
|
|
890
|
-
this.logger.info("click failed, will try next selector");
|
|
891
|
-
error = e;
|
|
892
|
-
throw e;
|
|
1006
|
+
await _commandError(state, e, this);
|
|
893
1007
|
}
|
|
894
1008
|
finally {
|
|
895
|
-
|
|
896
|
-
this._reportToWorld(world, {
|
|
897
|
-
element_name: selectors.element_name,
|
|
898
|
-
type: Types.SELECT,
|
|
899
|
-
text: `Select option: ${values}`,
|
|
900
|
-
value: values.toString(),
|
|
901
|
-
screenshotId,
|
|
902
|
-
result: error
|
|
903
|
-
? {
|
|
904
|
-
status: "FAILED",
|
|
905
|
-
startTime,
|
|
906
|
-
endTime,
|
|
907
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
908
|
-
}
|
|
909
|
-
: {
|
|
910
|
-
status: "PASSED",
|
|
911
|
-
startTime,
|
|
912
|
-
endTime,
|
|
913
|
-
},
|
|
914
|
-
info: info,
|
|
915
|
-
});
|
|
1009
|
+
_commandFinally(state, this);
|
|
916
1010
|
}
|
|
917
1011
|
}
|
|
918
1012
|
async type(_value, _params = null, options = {}, world = null) {
|
|
919
|
-
const
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1013
|
+
const state = {
|
|
1014
|
+
value: _value,
|
|
1015
|
+
_params,
|
|
1016
|
+
options,
|
|
1017
|
+
world,
|
|
1018
|
+
locate: false,
|
|
1019
|
+
scroll: false,
|
|
1020
|
+
highlight: false,
|
|
1021
|
+
type: Types.TYPE_PRESS,
|
|
1022
|
+
text: `Type value: ${_value}`,
|
|
1023
|
+
operation: "type",
|
|
1024
|
+
log: "",
|
|
1025
|
+
};
|
|
928
1026
|
try {
|
|
929
|
-
|
|
930
|
-
const valueSegment =
|
|
1027
|
+
await _preCommand(state, this);
|
|
1028
|
+
const valueSegment = state.value.split("&&");
|
|
931
1029
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
932
1030
|
if (i > 0) {
|
|
933
1031
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -947,108 +1045,53 @@ class StableBrowser {
|
|
|
947
1045
|
await this.page.keyboard.type(value);
|
|
948
1046
|
}
|
|
949
1047
|
}
|
|
950
|
-
return info;
|
|
1048
|
+
return state.info;
|
|
951
1049
|
}
|
|
952
1050
|
catch (e) {
|
|
953
|
-
|
|
954
|
-
this.logger.error("type failed " + info.log);
|
|
955
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
956
|
-
info.screenshotPath = screenshotPath;
|
|
957
|
-
Object.assign(e, { info: info });
|
|
958
|
-
error = e;
|
|
959
|
-
throw e;
|
|
1051
|
+
await _commandError(state, e, this);
|
|
960
1052
|
}
|
|
961
1053
|
finally {
|
|
962
|
-
|
|
963
|
-
this._reportToWorld(world, {
|
|
964
|
-
type: Types.TYPE_PRESS,
|
|
965
|
-
screenshotId,
|
|
966
|
-
value: _value,
|
|
967
|
-
text: `type value: ${_value}`,
|
|
968
|
-
result: error
|
|
969
|
-
? {
|
|
970
|
-
status: "FAILED",
|
|
971
|
-
startTime,
|
|
972
|
-
endTime,
|
|
973
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
974
|
-
}
|
|
975
|
-
: {
|
|
976
|
-
status: "PASSED",
|
|
977
|
-
startTime,
|
|
978
|
-
endTime,
|
|
979
|
-
},
|
|
980
|
-
info: info,
|
|
981
|
-
});
|
|
1054
|
+
_commandFinally(state, this);
|
|
982
1055
|
}
|
|
983
1056
|
}
|
|
984
1057
|
async setInputValue(selectors, value, _params = null, options = {}, world = null) {
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
let screenshotPath = null;
|
|
1058
|
+
const state = {
|
|
1059
|
+
selectors,
|
|
1060
|
+
_params,
|
|
1061
|
+
value,
|
|
1062
|
+
options,
|
|
1063
|
+
world,
|
|
1064
|
+
type: Types.SET_INPUT,
|
|
1065
|
+
text: `Set input value`,
|
|
1066
|
+
operation: "setInputValue",
|
|
1067
|
+
log: "***** set input value " + selectors.element_name + " *****\n",
|
|
1068
|
+
};
|
|
997
1069
|
try {
|
|
998
|
-
|
|
999
|
-
let
|
|
1000
|
-
await this.scrollIfNeeded(element, info);
|
|
1001
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1002
|
-
await this._highlightElements(element);
|
|
1070
|
+
await _preCommand(state, this);
|
|
1071
|
+
let value = await this._replaceWithLocalData(state.value, this);
|
|
1003
1072
|
try {
|
|
1004
|
-
await element.evaluateHandle((el, value) => {
|
|
1073
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1005
1074
|
el.value = value;
|
|
1006
1075
|
}, value);
|
|
1007
1076
|
}
|
|
1008
1077
|
catch (error) {
|
|
1009
1078
|
this.logger.error("setInputValue failed, will try again");
|
|
1010
|
-
|
|
1011
|
-
info.
|
|
1012
|
-
|
|
1013
|
-
await element.evaluateHandle((el, value) => {
|
|
1079
|
+
await _screenshot(state, this);
|
|
1080
|
+
Object.assign(error, { info: state.info });
|
|
1081
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1014
1082
|
el.value = value;
|
|
1015
1083
|
});
|
|
1016
1084
|
}
|
|
1017
1085
|
}
|
|
1018
1086
|
catch (e) {
|
|
1019
|
-
|
|
1020
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1021
|
-
info.screenshotPath = screenshotPath;
|
|
1022
|
-
Object.assign(e, { info: info });
|
|
1023
|
-
error = e;
|
|
1024
|
-
throw e;
|
|
1087
|
+
await _commandError(state, e, this);
|
|
1025
1088
|
}
|
|
1026
1089
|
finally {
|
|
1027
|
-
|
|
1028
|
-
this._reportToWorld(world, {
|
|
1029
|
-
element_name: selectors.element_name,
|
|
1030
|
-
type: Types.SET_INPUT,
|
|
1031
|
-
text: `Set input value`,
|
|
1032
|
-
value: value,
|
|
1033
|
-
screenshotId,
|
|
1034
|
-
result: error
|
|
1035
|
-
? {
|
|
1036
|
-
status: "FAILED",
|
|
1037
|
-
startTime,
|
|
1038
|
-
endTime,
|
|
1039
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1040
|
-
}
|
|
1041
|
-
: {
|
|
1042
|
-
status: "PASSED",
|
|
1043
|
-
startTime,
|
|
1044
|
-
endTime,
|
|
1045
|
-
},
|
|
1046
|
-
info: info,
|
|
1047
|
-
});
|
|
1090
|
+
_commandFinally(state, this);
|
|
1048
1091
|
}
|
|
1049
1092
|
}
|
|
1050
1093
|
async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
|
|
1051
|
-
|
|
1094
|
+
_validateSelectors(selectors);
|
|
1052
1095
|
const startTime = Date.now();
|
|
1053
1096
|
let error = null;
|
|
1054
1097
|
let screenshotId = null;
|
|
@@ -1141,32 +1184,32 @@ class StableBrowser {
|
|
|
1141
1184
|
}
|
|
1142
1185
|
}
|
|
1143
1186
|
async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
|
|
1144
|
-
|
|
1145
|
-
const startTime = Date.now();
|
|
1146
|
-
let error = null;
|
|
1147
|
-
let screenshotId = null;
|
|
1148
|
-
let screenshotPath = null;
|
|
1149
|
-
const info = {};
|
|
1150
|
-
info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
|
|
1151
|
-
info.operation = "clickType";
|
|
1152
|
-
info.selectors = selectors;
|
|
1187
|
+
_value = unEscapeString(_value);
|
|
1153
1188
|
const newValue = await this._replaceWithLocalData(_value, world);
|
|
1189
|
+
const state = {
|
|
1190
|
+
selectors,
|
|
1191
|
+
_params,
|
|
1192
|
+
value: newValue,
|
|
1193
|
+
originalValue: _value,
|
|
1194
|
+
options,
|
|
1195
|
+
world,
|
|
1196
|
+
type: Types.FILL,
|
|
1197
|
+
text: `Click type input with value: ${_value}`,
|
|
1198
|
+
operation: "clickType",
|
|
1199
|
+
log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
|
|
1200
|
+
};
|
|
1154
1201
|
if (newValue !== _value) {
|
|
1155
1202
|
//this.logger.info(_value + "=" + newValue);
|
|
1156
1203
|
_value = newValue;
|
|
1157
1204
|
}
|
|
1158
|
-
info.value = _value;
|
|
1159
1205
|
try {
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
await this.scrollIfNeeded(element, info);
|
|
1163
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1164
|
-
await this._highlightElements(element);
|
|
1206
|
+
await _preCommand(state, this);
|
|
1207
|
+
state.info.value = _value;
|
|
1165
1208
|
if (options === null || options === undefined || !options.press) {
|
|
1166
1209
|
try {
|
|
1167
|
-
let currentValue = await element.inputValue();
|
|
1210
|
+
let currentValue = await state.element.inputValue();
|
|
1168
1211
|
if (currentValue) {
|
|
1169
|
-
await element.fill("");
|
|
1212
|
+
await state.element.fill("");
|
|
1170
1213
|
}
|
|
1171
1214
|
}
|
|
1172
1215
|
catch (e) {
|
|
@@ -1175,22 +1218,22 @@ class StableBrowser {
|
|
|
1175
1218
|
}
|
|
1176
1219
|
if (options === null || options === undefined || options.press) {
|
|
1177
1220
|
try {
|
|
1178
|
-
await element.click({ timeout: 5000 });
|
|
1221
|
+
await state.element.click({ timeout: 5000 });
|
|
1179
1222
|
}
|
|
1180
1223
|
catch (e) {
|
|
1181
|
-
await element.dispatchEvent("click");
|
|
1224
|
+
await state.element.dispatchEvent("click");
|
|
1182
1225
|
}
|
|
1183
1226
|
}
|
|
1184
1227
|
else {
|
|
1185
1228
|
try {
|
|
1186
|
-
await element.focus();
|
|
1229
|
+
await state.element.focus();
|
|
1187
1230
|
}
|
|
1188
1231
|
catch (e) {
|
|
1189
|
-
await element.dispatchEvent("focus");
|
|
1232
|
+
await state.element.dispatchEvent("focus");
|
|
1190
1233
|
}
|
|
1191
1234
|
}
|
|
1192
1235
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1193
|
-
const valueSegment =
|
|
1236
|
+
const valueSegment = state.value.split("&&");
|
|
1194
1237
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1195
1238
|
if (i > 0) {
|
|
1196
1239
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1210,13 +1253,14 @@ class StableBrowser {
|
|
|
1210
1253
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1211
1254
|
}
|
|
1212
1255
|
}
|
|
1256
|
+
await _screenshot(state, this);
|
|
1213
1257
|
if (enter === true) {
|
|
1214
1258
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1215
1259
|
await this.page.keyboard.press("Enter");
|
|
1216
1260
|
await this.waitForPageLoad();
|
|
1217
1261
|
}
|
|
1218
1262
|
else if (enter === false) {
|
|
1219
|
-
await element.dispatchEvent("change");
|
|
1263
|
+
await state.element.dispatchEvent("change");
|
|
1220
1264
|
//await this.page.keyboard.press("Tab");
|
|
1221
1265
|
}
|
|
1222
1266
|
else {
|
|
@@ -1225,103 +1269,50 @@ class StableBrowser {
|
|
|
1225
1269
|
await this.waitForPageLoad();
|
|
1226
1270
|
}
|
|
1227
1271
|
}
|
|
1228
|
-
return info;
|
|
1272
|
+
return state.info;
|
|
1229
1273
|
}
|
|
1230
1274
|
catch (e) {
|
|
1231
|
-
|
|
1232
|
-
this.logger.error("fill failed " + JSON.stringify(info));
|
|
1233
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1234
|
-
info.screenshotPath = screenshotPath;
|
|
1235
|
-
Object.assign(e, { info: info });
|
|
1236
|
-
error = e;
|
|
1237
|
-
throw e;
|
|
1275
|
+
await _commandError(state, e, this);
|
|
1238
1276
|
}
|
|
1239
1277
|
finally {
|
|
1240
|
-
|
|
1241
|
-
this._reportToWorld(world, {
|
|
1242
|
-
element_name: selectors.element_name,
|
|
1243
|
-
type: Types.FILL,
|
|
1244
|
-
screenshotId,
|
|
1245
|
-
value: _value,
|
|
1246
|
-
text: `clickType input with value: ${_value}`,
|
|
1247
|
-
result: error
|
|
1248
|
-
? {
|
|
1249
|
-
status: "FAILED",
|
|
1250
|
-
startTime,
|
|
1251
|
-
endTime,
|
|
1252
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1253
|
-
}
|
|
1254
|
-
: {
|
|
1255
|
-
status: "PASSED",
|
|
1256
|
-
startTime,
|
|
1257
|
-
endTime,
|
|
1258
|
-
},
|
|
1259
|
-
info: info,
|
|
1260
|
-
});
|
|
1278
|
+
_commandFinally(state, this);
|
|
1261
1279
|
}
|
|
1262
1280
|
}
|
|
1263
1281
|
async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1282
|
+
const state = {
|
|
1283
|
+
selectors,
|
|
1284
|
+
_params,
|
|
1285
|
+
value: unEscapeString(value),
|
|
1286
|
+
options,
|
|
1287
|
+
world,
|
|
1288
|
+
type: Types.FILL,
|
|
1289
|
+
text: `Fill input with value: ${value}`,
|
|
1290
|
+
operation: "fill",
|
|
1291
|
+
log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
|
|
1292
|
+
};
|
|
1274
1293
|
try {
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
await
|
|
1278
|
-
await element.fill(value);
|
|
1279
|
-
await element.dispatchEvent("change");
|
|
1294
|
+
await _preCommand(state, this);
|
|
1295
|
+
await state.element.fill(value);
|
|
1296
|
+
await state.element.dispatchEvent("change");
|
|
1280
1297
|
if (enter) {
|
|
1281
1298
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1282
1299
|
await this.page.keyboard.press("Enter");
|
|
1283
1300
|
}
|
|
1284
1301
|
await this.waitForPageLoad();
|
|
1285
|
-
return info;
|
|
1302
|
+
return state.info;
|
|
1286
1303
|
}
|
|
1287
1304
|
catch (e) {
|
|
1288
|
-
|
|
1289
|
-
this.logger.error("fill failed " + info.log);
|
|
1290
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1291
|
-
info.screenshotPath = screenshotPath;
|
|
1292
|
-
Object.assign(e, { info: info });
|
|
1293
|
-
error = e;
|
|
1294
|
-
throw e;
|
|
1305
|
+
await _commandError(state, e, this);
|
|
1295
1306
|
}
|
|
1296
1307
|
finally {
|
|
1297
|
-
|
|
1298
|
-
this._reportToWorld(world, {
|
|
1299
|
-
element_name: selectors.element_name,
|
|
1300
|
-
type: Types.FILL,
|
|
1301
|
-
screenshotId,
|
|
1302
|
-
value,
|
|
1303
|
-
text: `Fill input with value: ${value}`,
|
|
1304
|
-
result: error
|
|
1305
|
-
? {
|
|
1306
|
-
status: "FAILED",
|
|
1307
|
-
startTime,
|
|
1308
|
-
endTime,
|
|
1309
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1310
|
-
}
|
|
1311
|
-
: {
|
|
1312
|
-
status: "PASSED",
|
|
1313
|
-
startTime,
|
|
1314
|
-
endTime,
|
|
1315
|
-
},
|
|
1316
|
-
info: info,
|
|
1317
|
-
});
|
|
1308
|
+
_commandFinally(state, this);
|
|
1318
1309
|
}
|
|
1319
1310
|
}
|
|
1320
1311
|
async getText(selectors, _params = null, options = {}, info = {}, world = null) {
|
|
1321
1312
|
return await this._getText(selectors, 0, _params, options, info, world);
|
|
1322
1313
|
}
|
|
1323
1314
|
async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
|
|
1324
|
-
|
|
1315
|
+
_validateSelectors(selectors);
|
|
1325
1316
|
let screenshotId = null;
|
|
1326
1317
|
let screenshotPath = null;
|
|
1327
1318
|
if (!info.log) {
|
|
@@ -1365,165 +1356,124 @@ class StableBrowser {
|
|
|
1365
1356
|
}
|
|
1366
1357
|
}
|
|
1367
1358
|
async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
|
|
1368
|
-
var _a;
|
|
1369
|
-
this._validateSelectors(selectors);
|
|
1370
1359
|
if (!pattern) {
|
|
1371
1360
|
throw new Error("pattern is null");
|
|
1372
1361
|
}
|
|
1373
1362
|
if (!text) {
|
|
1374
1363
|
throw new Error("text is null");
|
|
1375
1364
|
}
|
|
1365
|
+
const state = {
|
|
1366
|
+
selectors,
|
|
1367
|
+
_params,
|
|
1368
|
+
pattern,
|
|
1369
|
+
value: pattern,
|
|
1370
|
+
options,
|
|
1371
|
+
world,
|
|
1372
|
+
locate: false,
|
|
1373
|
+
scroll: false,
|
|
1374
|
+
screenshot: false,
|
|
1375
|
+
highlight: false,
|
|
1376
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1377
|
+
text: `Verify element contains pattern: ${pattern}`,
|
|
1378
|
+
operation: "containsPattern",
|
|
1379
|
+
log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
|
|
1380
|
+
};
|
|
1376
1381
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1377
1382
|
if (newValue !== text) {
|
|
1378
1383
|
this.logger.info(text + "=" + newValue);
|
|
1379
1384
|
text = newValue;
|
|
1380
1385
|
}
|
|
1381
|
-
const startTime = Date.now();
|
|
1382
|
-
let error = null;
|
|
1383
|
-
let screenshotId = null;
|
|
1384
|
-
let screenshotPath = null;
|
|
1385
|
-
const info = {};
|
|
1386
|
-
info.log =
|
|
1387
|
-
"***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
|
|
1388
|
-
info.operation = "containsPattern";
|
|
1389
|
-
info.selectors = selectors;
|
|
1390
|
-
info.value = text;
|
|
1391
|
-
info.pattern = pattern;
|
|
1392
1386
|
let foundObj = null;
|
|
1393
1387
|
try {
|
|
1394
|
-
|
|
1388
|
+
await _preCommand(state, this);
|
|
1389
|
+
state.info.pattern = pattern;
|
|
1390
|
+
foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
|
|
1395
1391
|
if (foundObj && foundObj.element) {
|
|
1396
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1392
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1397
1393
|
}
|
|
1398
|
-
|
|
1394
|
+
await _screenshot(state, this);
|
|
1399
1395
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1400
1396
|
pattern = pattern.replace("{text}", escapedText);
|
|
1401
1397
|
let regex = new RegExp(pattern, "im");
|
|
1402
|
-
if (!regex.test(foundObj
|
|
1403
|
-
info.foundText = foundObj
|
|
1398
|
+
if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
|
|
1399
|
+
state.info.foundText = foundObj?.text;
|
|
1404
1400
|
throw new Error("element doesn't contain text " + text);
|
|
1405
1401
|
}
|
|
1406
|
-
return info;
|
|
1402
|
+
return state.info;
|
|
1407
1403
|
}
|
|
1408
1404
|
catch (e) {
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
|
|
1412
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1413
|
-
info.screenshotPath = screenshotPath;
|
|
1414
|
-
Object.assign(e, { info: info });
|
|
1415
|
-
error = e;
|
|
1416
|
-
throw e;
|
|
1405
|
+
this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
|
|
1406
|
+
await _commandError(state, e, this);
|
|
1417
1407
|
}
|
|
1418
1408
|
finally {
|
|
1419
|
-
|
|
1420
|
-
this._reportToWorld(world, {
|
|
1421
|
-
element_name: selectors.element_name,
|
|
1422
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1423
|
-
value: pattern,
|
|
1424
|
-
text: `Verify element contains pattern: ${pattern}`,
|
|
1425
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1426
|
-
result: error
|
|
1427
|
-
? {
|
|
1428
|
-
status: "FAILED",
|
|
1429
|
-
startTime,
|
|
1430
|
-
endTime,
|
|
1431
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1432
|
-
}
|
|
1433
|
-
: {
|
|
1434
|
-
status: "PASSED",
|
|
1435
|
-
startTime,
|
|
1436
|
-
endTime,
|
|
1437
|
-
},
|
|
1438
|
-
info: info,
|
|
1439
|
-
});
|
|
1409
|
+
_commandFinally(state, this);
|
|
1440
1410
|
}
|
|
1441
1411
|
}
|
|
1442
1412
|
async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
|
|
1443
|
-
|
|
1444
|
-
|
|
1413
|
+
const state = {
|
|
1414
|
+
selectors,
|
|
1415
|
+
_params,
|
|
1416
|
+
value: text,
|
|
1417
|
+
options,
|
|
1418
|
+
world,
|
|
1419
|
+
locate: false,
|
|
1420
|
+
scroll: false,
|
|
1421
|
+
screenshot: false,
|
|
1422
|
+
highlight: false,
|
|
1423
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1424
|
+
text: `Verify element contains text: ${text}`,
|
|
1425
|
+
operation: "containsText",
|
|
1426
|
+
log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
|
|
1427
|
+
};
|
|
1445
1428
|
if (!text) {
|
|
1446
1429
|
throw new Error("text is null");
|
|
1447
1430
|
}
|
|
1448
|
-
|
|
1449
|
-
let error = null;
|
|
1450
|
-
let screenshotId = null;
|
|
1451
|
-
let screenshotPath = null;
|
|
1452
|
-
const info = {};
|
|
1453
|
-
info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
|
|
1454
|
-
info.operation = "containsText";
|
|
1455
|
-
info.selectors = selectors;
|
|
1431
|
+
text = unEscapeString(text);
|
|
1456
1432
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1457
1433
|
if (newValue !== text) {
|
|
1458
1434
|
this.logger.info(text + "=" + newValue);
|
|
1459
1435
|
text = newValue;
|
|
1460
1436
|
}
|
|
1461
|
-
info.value = text;
|
|
1462
1437
|
let foundObj = null;
|
|
1463
1438
|
try {
|
|
1464
|
-
|
|
1439
|
+
await _preCommand(state, this);
|
|
1440
|
+
foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
|
|
1465
1441
|
if (foundObj && foundObj.element) {
|
|
1466
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1442
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1467
1443
|
}
|
|
1468
|
-
|
|
1444
|
+
await _screenshot(state, this);
|
|
1469
1445
|
const dateAlternatives = findDateAlternatives(text);
|
|
1470
1446
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1471
1447
|
if (dateAlternatives.date) {
|
|
1472
1448
|
for (let i = 0; i < dateAlternatives.dates.length; i++) {
|
|
1473
|
-
if (
|
|
1474
|
-
|
|
1475
|
-
return info;
|
|
1449
|
+
if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
|
|
1450
|
+
foundObj?.value?.includes(dateAlternatives.dates[i])) {
|
|
1451
|
+
return state.info;
|
|
1476
1452
|
}
|
|
1477
1453
|
}
|
|
1478
1454
|
throw new Error("element doesn't contain text " + text);
|
|
1479
1455
|
}
|
|
1480
1456
|
else if (numberAlternatives.number) {
|
|
1481
1457
|
for (let i = 0; i < numberAlternatives.numbers.length; i++) {
|
|
1482
|
-
if (
|
|
1483
|
-
|
|
1484
|
-
return info;
|
|
1458
|
+
if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
|
|
1459
|
+
foundObj?.value?.includes(numberAlternatives.numbers[i])) {
|
|
1460
|
+
return state.info;
|
|
1485
1461
|
}
|
|
1486
1462
|
}
|
|
1487
1463
|
throw new Error("element doesn't contain text " + text);
|
|
1488
1464
|
}
|
|
1489
|
-
else if (!
|
|
1490
|
-
info.foundText = foundObj
|
|
1491
|
-
info.value = foundObj
|
|
1465
|
+
else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
|
|
1466
|
+
state.info.foundText = foundObj?.text;
|
|
1467
|
+
state.info.value = foundObj?.value;
|
|
1492
1468
|
throw new Error("element doesn't contain text " + text);
|
|
1493
1469
|
}
|
|
1494
|
-
return info;
|
|
1470
|
+
return state.info;
|
|
1495
1471
|
}
|
|
1496
1472
|
catch (e) {
|
|
1497
|
-
|
|
1498
|
-
this.logger.error("verify element contains text failed " + info.log);
|
|
1499
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1500
|
-
info.screenshotPath = screenshotPath;
|
|
1501
|
-
Object.assign(e, { info: info });
|
|
1502
|
-
error = e;
|
|
1503
|
-
throw e;
|
|
1473
|
+
await _commandError(state, e, this);
|
|
1504
1474
|
}
|
|
1505
1475
|
finally {
|
|
1506
|
-
|
|
1507
|
-
this._reportToWorld(world, {
|
|
1508
|
-
element_name: selectors.element_name,
|
|
1509
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1510
|
-
text: `Verify element contains text: ${text}`,
|
|
1511
|
-
value: text,
|
|
1512
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1513
|
-
result: error
|
|
1514
|
-
? {
|
|
1515
|
-
status: "FAILED",
|
|
1516
|
-
startTime,
|
|
1517
|
-
endTime,
|
|
1518
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1519
|
-
}
|
|
1520
|
-
: {
|
|
1521
|
-
status: "PASSED",
|
|
1522
|
-
startTime,
|
|
1523
|
-
endTime,
|
|
1524
|
-
},
|
|
1525
|
-
info: info,
|
|
1526
|
-
});
|
|
1476
|
+
_commandFinally(state, this);
|
|
1527
1477
|
}
|
|
1528
1478
|
}
|
|
1529
1479
|
_getDataFile(world = null) {
|
|
@@ -1544,7 +1494,10 @@ class StableBrowser {
|
|
|
1544
1494
|
}
|
|
1545
1495
|
async waitForUserInput(message, world = null) {
|
|
1546
1496
|
if (!message) {
|
|
1547
|
-
message = "Press any key to continue";
|
|
1497
|
+
message = "# Wait for user input. Press any key to continue";
|
|
1498
|
+
}
|
|
1499
|
+
else {
|
|
1500
|
+
message = "# Wait for user input. " + message;
|
|
1548
1501
|
}
|
|
1549
1502
|
message += "\n";
|
|
1550
1503
|
const value = await new Promise((resolve) => {
|
|
@@ -1749,7 +1702,6 @@ class StableBrowser {
|
|
|
1749
1702
|
}
|
|
1750
1703
|
async takeScreenshot(screenshotPath) {
|
|
1751
1704
|
const playContext = this.context.playContext;
|
|
1752
|
-
const client = await playContext.newCDPSession(this.page);
|
|
1753
1705
|
// Using CDP to capture the screenshot
|
|
1754
1706
|
const viewportWidth = Math.max(...(await this.page.evaluate(() => [
|
|
1755
1707
|
document.body.scrollWidth,
|
|
@@ -1759,97 +1711,67 @@ class StableBrowser {
|
|
|
1759
1711
|
document.body.clientWidth,
|
|
1760
1712
|
document.documentElement.clientWidth,
|
|
1761
1713
|
])));
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
await client.detach();
|
|
1714
|
+
let screenshotBuffer = null;
|
|
1715
|
+
if (this.context.browserName === "chromium") {
|
|
1716
|
+
const client = await playContext.newCDPSession(this.page);
|
|
1717
|
+
const { data } = await client.send("Page.captureScreenshot", {
|
|
1718
|
+
format: "png",
|
|
1719
|
+
// clip: {
|
|
1720
|
+
// x: 0,
|
|
1721
|
+
// y: 0,
|
|
1722
|
+
// width: viewportWidth,
|
|
1723
|
+
// height: viewportHeight,
|
|
1724
|
+
// scale: 1,
|
|
1725
|
+
// },
|
|
1726
|
+
});
|
|
1727
|
+
await client.detach();
|
|
1728
|
+
if (!screenshotPath) {
|
|
1729
|
+
return data;
|
|
1730
|
+
}
|
|
1731
|
+
screenshotBuffer = Buffer.from(data, "base64");
|
|
1732
|
+
}
|
|
1733
|
+
else {
|
|
1734
|
+
screenshotBuffer = await this.page.screenshot();
|
|
1735
|
+
}
|
|
1736
|
+
let image = await Jimp.read(screenshotBuffer);
|
|
1737
|
+
// Get the image dimensions
|
|
1738
|
+
const { width, height } = image.bitmap;
|
|
1739
|
+
const resizeRatio = viewportWidth / width;
|
|
1740
|
+
// Resize the image to fit within the viewport dimensions without enlarging
|
|
1741
|
+
if (width > viewportWidth) {
|
|
1742
|
+
image = image.resize({ w: viewportWidth, h: height * resizeRatio }); // Resize the image while maintaining aspect ratio
|
|
1743
|
+
await image.write(screenshotPath);
|
|
1744
|
+
}
|
|
1745
|
+
else {
|
|
1746
|
+
fs.writeFileSync(screenshotPath, screenshotBuffer);
|
|
1747
|
+
}
|
|
1797
1748
|
}
|
|
1798
1749
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1750
|
+
const state = {
|
|
1751
|
+
selectors,
|
|
1752
|
+
_params,
|
|
1753
|
+
options,
|
|
1754
|
+
world,
|
|
1755
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1756
|
+
text: `Verify element exists in page`,
|
|
1757
|
+
operation: "verifyElementExistInPage",
|
|
1758
|
+
log: "***** verify element " + selectors.element_name + " exists in page *****\n",
|
|
1759
|
+
};
|
|
1804
1760
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1805
|
-
const info = {};
|
|
1806
|
-
info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
|
|
1807
|
-
info.operation = "verify";
|
|
1808
|
-
info.selectors = selectors;
|
|
1809
1761
|
try {
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
}
|
|
1814
|
-
await this._highlightElements(element);
|
|
1815
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1816
|
-
await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
1817
|
-
return info;
|
|
1762
|
+
await _preCommand(state, this);
|
|
1763
|
+
await expect(state.element).toHaveCount(1, { timeout: 10000 });
|
|
1764
|
+
return state.info;
|
|
1818
1765
|
}
|
|
1819
1766
|
catch (e) {
|
|
1820
|
-
|
|
1821
|
-
this.logger.error("verify failed " + info.log);
|
|
1822
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1823
|
-
info.screenshotPath = screenshotPath;
|
|
1824
|
-
Object.assign(e, { info: info });
|
|
1825
|
-
error = e;
|
|
1826
|
-
throw e;
|
|
1767
|
+
await _commandError(state, e, this);
|
|
1827
1768
|
}
|
|
1828
1769
|
finally {
|
|
1829
|
-
|
|
1830
|
-
this._reportToWorld(world, {
|
|
1831
|
-
element_name: selectors.element_name,
|
|
1832
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1833
|
-
text: "Verify element exists in page",
|
|
1834
|
-
screenshotId,
|
|
1835
|
-
result: error
|
|
1836
|
-
? {
|
|
1837
|
-
status: "FAILED",
|
|
1838
|
-
startTime,
|
|
1839
|
-
endTime,
|
|
1840
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1841
|
-
}
|
|
1842
|
-
: {
|
|
1843
|
-
status: "PASSED",
|
|
1844
|
-
startTime,
|
|
1845
|
-
endTime,
|
|
1846
|
-
},
|
|
1847
|
-
info: info,
|
|
1848
|
-
});
|
|
1770
|
+
_commandFinally(state, this);
|
|
1849
1771
|
}
|
|
1850
1772
|
}
|
|
1851
1773
|
async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
|
|
1852
|
-
|
|
1774
|
+
_validateSelectors(selectors);
|
|
1853
1775
|
const startTime = Date.now();
|
|
1854
1776
|
let error = null;
|
|
1855
1777
|
let screenshotId = null;
|
|
@@ -1908,7 +1830,7 @@ class StableBrowser {
|
|
|
1908
1830
|
status: "FAILED",
|
|
1909
1831
|
startTime,
|
|
1910
1832
|
endTime,
|
|
1911
|
-
message: error
|
|
1833
|
+
message: error?.message,
|
|
1912
1834
|
}
|
|
1913
1835
|
: {
|
|
1914
1836
|
status: "PASSED",
|
|
@@ -2117,7 +2039,7 @@ class StableBrowser {
|
|
|
2117
2039
|
status: "FAILED",
|
|
2118
2040
|
startTime,
|
|
2119
2041
|
endTime,
|
|
2120
|
-
message: error
|
|
2042
|
+
message: error?.message,
|
|
2121
2043
|
}
|
|
2122
2044
|
: {
|
|
2123
2045
|
status: "PASSED",
|
|
@@ -2153,20 +2075,20 @@ class StableBrowser {
|
|
|
2153
2075
|
for (let i = 0; i < frames.length; i++) {
|
|
2154
2076
|
if (dateAlternatives.date) {
|
|
2155
2077
|
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2156
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, {});
|
|
2078
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, true, {});
|
|
2157
2079
|
result.frame = frames[i];
|
|
2158
2080
|
results.push(result);
|
|
2159
2081
|
}
|
|
2160
2082
|
}
|
|
2161
2083
|
else if (numberAlternatives.number) {
|
|
2162
2084
|
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2163
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, {});
|
|
2085
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, true, {});
|
|
2164
2086
|
result.frame = frames[i];
|
|
2165
2087
|
results.push(result);
|
|
2166
2088
|
}
|
|
2167
2089
|
}
|
|
2168
2090
|
else {
|
|
2169
|
-
const result = await this._locateElementByText(frames[i], text, "*", true, {});
|
|
2091
|
+
const result = await this._locateElementByText(frames[i], text, "*", true, true, {});
|
|
2170
2092
|
result.frame = frames[i];
|
|
2171
2093
|
results.push(result);
|
|
2172
2094
|
}
|
|
@@ -2215,7 +2137,7 @@ class StableBrowser {
|
|
|
2215
2137
|
status: "FAILED",
|
|
2216
2138
|
startTime,
|
|
2217
2139
|
endTime,
|
|
2218
|
-
message: error
|
|
2140
|
+
message: error?.message,
|
|
2219
2141
|
}
|
|
2220
2142
|
: {
|
|
2221
2143
|
status: "PASSED",
|
|
@@ -2299,7 +2221,7 @@ class StableBrowser {
|
|
|
2299
2221
|
status: "FAILED",
|
|
2300
2222
|
startTime,
|
|
2301
2223
|
endTime,
|
|
2302
|
-
message: error
|
|
2224
|
+
message: error?.message,
|
|
2303
2225
|
}
|
|
2304
2226
|
: {
|
|
2305
2227
|
status: "PASSED",
|
|
@@ -2331,7 +2253,7 @@ class StableBrowser {
|
|
|
2331
2253
|
this.logger.info("Table data verified");
|
|
2332
2254
|
}
|
|
2333
2255
|
async getTableData(selectors, _params = null, options = {}, world = null) {
|
|
2334
|
-
|
|
2256
|
+
_validateSelectors(selectors);
|
|
2335
2257
|
const startTime = Date.now();
|
|
2336
2258
|
let error = null;
|
|
2337
2259
|
let screenshotId = null;
|
|
@@ -2367,7 +2289,7 @@ class StableBrowser {
|
|
|
2367
2289
|
status: "FAILED",
|
|
2368
2290
|
startTime,
|
|
2369
2291
|
endTime,
|
|
2370
|
-
message: error
|
|
2292
|
+
message: error?.message,
|
|
2371
2293
|
}
|
|
2372
2294
|
: {
|
|
2373
2295
|
status: "PASSED",
|
|
@@ -2379,7 +2301,7 @@ class StableBrowser {
|
|
|
2379
2301
|
}
|
|
2380
2302
|
}
|
|
2381
2303
|
async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
|
|
2382
|
-
|
|
2304
|
+
_validateSelectors(selectors);
|
|
2383
2305
|
if (!query) {
|
|
2384
2306
|
throw new Error("query is null");
|
|
2385
2307
|
}
|
|
@@ -2532,7 +2454,7 @@ class StableBrowser {
|
|
|
2532
2454
|
status: "FAILED",
|
|
2533
2455
|
startTime,
|
|
2534
2456
|
endTime,
|
|
2535
|
-
message: error
|
|
2457
|
+
message: error?.message,
|
|
2536
2458
|
}
|
|
2537
2459
|
: {
|
|
2538
2460
|
status: "PASSED",
|
|
@@ -2544,27 +2466,7 @@ class StableBrowser {
|
|
|
2544
2466
|
}
|
|
2545
2467
|
}
|
|
2546
2468
|
async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
|
|
2547
|
-
|
|
2548
|
-
return value;
|
|
2549
|
-
}
|
|
2550
|
-
// find all the accurance of {{(.*?)}} and replace with the value
|
|
2551
|
-
let regex = /{{(.*?)}}/g;
|
|
2552
|
-
let matches = value.match(regex);
|
|
2553
|
-
if (matches) {
|
|
2554
|
-
const testData = this.getTestData(world);
|
|
2555
|
-
for (let i = 0; i < matches.length; i++) {
|
|
2556
|
-
let match = matches[i];
|
|
2557
|
-
let key = match.substring(2, match.length - 2);
|
|
2558
|
-
let newValue = objectPath.get(testData, key, null);
|
|
2559
|
-
if (newValue !== null) {
|
|
2560
|
-
value = value.replace(match, newValue);
|
|
2561
|
-
}
|
|
2562
|
-
}
|
|
2563
|
-
}
|
|
2564
|
-
if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
|
|
2565
|
-
return await decrypt(value, null, totpWait);
|
|
2566
|
-
}
|
|
2567
|
-
return value;
|
|
2469
|
+
return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
|
|
2568
2470
|
}
|
|
2569
2471
|
_getLoadTimeout(options) {
|
|
2570
2472
|
let timeout = 15000;
|
|
@@ -2624,7 +2526,7 @@ class StableBrowser {
|
|
|
2624
2526
|
status: "FAILED",
|
|
2625
2527
|
startTime,
|
|
2626
2528
|
endTime,
|
|
2627
|
-
message: error
|
|
2529
|
+
message: error?.message,
|
|
2628
2530
|
}
|
|
2629
2531
|
: {
|
|
2630
2532
|
status: "PASSED",
|
|
@@ -2659,7 +2561,7 @@ class StableBrowser {
|
|
|
2659
2561
|
status: "FAILED",
|
|
2660
2562
|
startTime,
|
|
2661
2563
|
endTime,
|
|
2662
|
-
message: error
|
|
2564
|
+
message: error?.message,
|
|
2663
2565
|
}
|
|
2664
2566
|
: {
|
|
2665
2567
|
status: "PASSED",
|
|
@@ -2701,7 +2603,7 @@ class StableBrowser {
|
|
|
2701
2603
|
status: "FAILED",
|
|
2702
2604
|
startTime,
|
|
2703
2605
|
endTime,
|
|
2704
|
-
message: error
|
|
2606
|
+
message: error?.message,
|
|
2705
2607
|
}
|
|
2706
2608
|
: {
|
|
2707
2609
|
status: "PASSED",
|
|
@@ -2737,7 +2639,7 @@ class StableBrowser {
|
|
|
2737
2639
|
status: "FAILED",
|
|
2738
2640
|
startTime,
|
|
2739
2641
|
endTime,
|
|
2740
|
-
message: error
|
|
2642
|
+
message: error?.message,
|
|
2741
2643
|
}
|
|
2742
2644
|
: {
|
|
2743
2645
|
status: "PASSED",
|
|
@@ -2922,5 +2824,10 @@ const KEYBOARD_EVENTS = [
|
|
|
2922
2824
|
"TVAntennaCable",
|
|
2923
2825
|
"TVAudioDescription",
|
|
2924
2826
|
];
|
|
2827
|
+
function unEscapeString(str) {
|
|
2828
|
+
const placeholder = "__NEWLINE__";
|
|
2829
|
+
str = str.replace(new RegExp(placeholder, "g"), "\n");
|
|
2830
|
+
return str;
|
|
2831
|
+
}
|
|
2925
2832
|
export { StableBrowser };
|
|
2926
2833
|
//# sourceMappingURL=stable_browser.js.map
|