automation_model 1.0.438-dev → 1.0.438-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 +197 -14
- package/lib/api.js.map +1 -1
- package/lib/auto_page.js +21 -43
- package/lib/auto_page.js.map +1 -1
- package/lib/axe/axe.mini.js +12 -0
- package/lib/browser_manager.d.ts +2 -2
- package/lib/browser_manager.js +29 -54
- 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 +51 -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/network.d.ts +3 -0
- package/lib/network.js +144 -0
- package/lib/network.js.map +1 -0
- package/lib/stable_browser.d.ts +26 -16
- package/lib/stable_browser.js +528 -632
- 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 +6 -7
package/lib/stable_browser.js
CHANGED
|
@@ -2,19 +2,22 @@
|
|
|
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";
|
|
20
|
+
import { registerDownloadEvent, registerNetworkEvents } from "./network.js";
|
|
18
21
|
const Types = {
|
|
19
22
|
CLICK: "click_element",
|
|
20
23
|
NAVIGATE: "navigate",
|
|
@@ -42,15 +45,24 @@ const Types = {
|
|
|
42
45
|
LOAD_DATA: "load_data",
|
|
43
46
|
SET_INPUT: "set_input",
|
|
44
47
|
};
|
|
48
|
+
export const apps = {};
|
|
45
49
|
class StableBrowser {
|
|
46
|
-
|
|
50
|
+
browser;
|
|
51
|
+
page;
|
|
52
|
+
logger;
|
|
53
|
+
context;
|
|
54
|
+
world;
|
|
55
|
+
project_path = null;
|
|
56
|
+
webLogFile = null;
|
|
57
|
+
networkLogger = null;
|
|
58
|
+
configuration = null;
|
|
59
|
+
appName = "main";
|
|
60
|
+
constructor(browser, page, logger = null, context = null, world = null) {
|
|
47
61
|
this.browser = browser;
|
|
48
62
|
this.page = page;
|
|
49
63
|
this.logger = logger;
|
|
50
64
|
this.context = context;
|
|
51
|
-
this.
|
|
52
|
-
this.webLogFile = null;
|
|
53
|
-
this.configuration = null;
|
|
65
|
+
this.world = world;
|
|
54
66
|
if (!this.logger) {
|
|
55
67
|
this.logger = console;
|
|
56
68
|
}
|
|
@@ -76,16 +88,31 @@ class StableBrowser {
|
|
|
76
88
|
this.logger.error("unable to read ai_config.json");
|
|
77
89
|
}
|
|
78
90
|
const logFolder = path.join(this.project_path, "logs", "web");
|
|
79
|
-
this.
|
|
80
|
-
this.registerConsoleLogListener(page, context, this.webLogFile);
|
|
81
|
-
this.registerRequestListener();
|
|
91
|
+
this.world = world;
|
|
82
92
|
context.pages = [this.page];
|
|
83
93
|
context.pageLoading = { status: false };
|
|
94
|
+
this.registerEventListeners(this.context);
|
|
95
|
+
registerNetworkEvents(this.world, this, this.context, this.page);
|
|
96
|
+
registerDownloadEvent(this.page, this.world, this.context);
|
|
97
|
+
}
|
|
98
|
+
registerEventListeners(context) {
|
|
99
|
+
this.registerConsoleLogListener(this.page, context);
|
|
100
|
+
this.registerRequestListener(this.page, context, this.webLogFile);
|
|
101
|
+
if (!context.pageLoading) {
|
|
102
|
+
context.pageLoading = { status: false };
|
|
103
|
+
}
|
|
84
104
|
context.playContext.on("page", async function (page) {
|
|
105
|
+
if (this.configuration && this.configuration.closePopups === true) {
|
|
106
|
+
console.log("close unexpected popups");
|
|
107
|
+
await page.close();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
85
110
|
context.pageLoading.status = true;
|
|
86
111
|
this.page = page;
|
|
87
112
|
context.page = page;
|
|
88
113
|
context.pages.push(page);
|
|
114
|
+
registerNetworkEvents(this.world, this, context, this.page);
|
|
115
|
+
registerDownloadEvent(this.page, this.world, context);
|
|
89
116
|
page.on("close", async () => {
|
|
90
117
|
if (this.context && this.context.pages && this.context.pages.length > 1) {
|
|
91
118
|
this.context.pages.pop();
|
|
@@ -110,6 +137,36 @@ class StableBrowser {
|
|
|
110
137
|
context.pageLoading.status = false;
|
|
111
138
|
}.bind(this));
|
|
112
139
|
}
|
|
140
|
+
async switchApp(appName) {
|
|
141
|
+
// check if the current app (this.appName) is the same as the new app
|
|
142
|
+
if (this.appName === appName) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
let navigate = false;
|
|
146
|
+
if (!apps[appName]) {
|
|
147
|
+
let newContext = await getContext(null, false, this.logger, appName, false, this);
|
|
148
|
+
navigate = true;
|
|
149
|
+
apps[appName] = {
|
|
150
|
+
context: newContext,
|
|
151
|
+
browser: newContext.browser,
|
|
152
|
+
page: newContext.page,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
const tempContext = {};
|
|
156
|
+
this._copyContext(this, tempContext);
|
|
157
|
+
this._copyContext(apps[appName], this);
|
|
158
|
+
apps[this.appName] = tempContext;
|
|
159
|
+
this.appName = appName;
|
|
160
|
+
if (navigate) {
|
|
161
|
+
await this.goto(this.context.environment.baseUrl);
|
|
162
|
+
await this.waitForPageLoad();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
_copyContext(from, to) {
|
|
166
|
+
to.browser = from.browser;
|
|
167
|
+
to.page = from.page;
|
|
168
|
+
to.context = from.context;
|
|
169
|
+
}
|
|
113
170
|
getWebLogFile(logFolder) {
|
|
114
171
|
if (!fs.existsSync(logFolder)) {
|
|
115
172
|
fs.mkdirSync(logFolder, { recursive: true });
|
|
@@ -121,37 +178,63 @@ class StableBrowser {
|
|
|
121
178
|
const fileName = nextIndex + ".json";
|
|
122
179
|
return path.join(logFolder, fileName);
|
|
123
180
|
}
|
|
124
|
-
registerConsoleLogListener(page, context
|
|
181
|
+
registerConsoleLogListener(page, context) {
|
|
125
182
|
if (!this.context.webLogger) {
|
|
126
183
|
this.context.webLogger = [];
|
|
127
184
|
}
|
|
128
185
|
page.on("console", async (msg) => {
|
|
129
|
-
|
|
186
|
+
const obj = {
|
|
130
187
|
type: msg.type(),
|
|
131
188
|
text: msg.text(),
|
|
132
189
|
location: msg.location(),
|
|
133
190
|
time: new Date().toISOString(),
|
|
134
|
-
}
|
|
135
|
-
|
|
191
|
+
};
|
|
192
|
+
this.context.webLogger.push(obj);
|
|
193
|
+
if (msg.type() === "error") {
|
|
194
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
|
|
195
|
+
}
|
|
136
196
|
});
|
|
137
197
|
}
|
|
138
|
-
registerRequestListener() {
|
|
139
|
-
this.
|
|
198
|
+
registerRequestListener(page, context, logFile) {
|
|
199
|
+
if (!this.context.networkLogger) {
|
|
200
|
+
this.context.networkLogger = [];
|
|
201
|
+
}
|
|
202
|
+
page.on("request", async (data) => {
|
|
203
|
+
const startTime = new Date().getTime();
|
|
140
204
|
try {
|
|
141
|
-
const pageUrl = new URL(
|
|
205
|
+
const pageUrl = new URL(page.url());
|
|
142
206
|
const requestUrl = new URL(data.url());
|
|
143
207
|
if (pageUrl.hostname === requestUrl.hostname) {
|
|
144
208
|
const method = data.method();
|
|
145
|
-
if (
|
|
209
|
+
if (["POST", "GET", "PUT", "DELETE", "PATCH"].includes(method)) {
|
|
146
210
|
const token = await data.headerValue("Authorization");
|
|
147
211
|
if (token) {
|
|
148
|
-
|
|
212
|
+
context.authtoken = token;
|
|
149
213
|
}
|
|
150
214
|
}
|
|
151
215
|
}
|
|
216
|
+
const response = await data.response();
|
|
217
|
+
const endTime = new Date().getTime();
|
|
218
|
+
const obj = {
|
|
219
|
+
url: data.url(),
|
|
220
|
+
method: data.method(),
|
|
221
|
+
postData: data.postData(),
|
|
222
|
+
error: data.failure() ? data.failure().errorText : null,
|
|
223
|
+
duration: endTime - startTime,
|
|
224
|
+
startTime,
|
|
225
|
+
};
|
|
226
|
+
context.networkLogger.push(obj);
|
|
227
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
|
|
152
228
|
}
|
|
153
229
|
catch (error) {
|
|
154
230
|
console.error("Error in request listener", error);
|
|
231
|
+
context.networkLogger.push({
|
|
232
|
+
error: "not able to listen",
|
|
233
|
+
message: error.message,
|
|
234
|
+
stack: error.stack,
|
|
235
|
+
time: new Date().toISOString(),
|
|
236
|
+
});
|
|
237
|
+
// await fs.promises.writeFile(logFile, JSON.stringify(context.networkLogger, null, 2));
|
|
155
238
|
}
|
|
156
239
|
});
|
|
157
240
|
}
|
|
@@ -166,20 +249,6 @@ class StableBrowser {
|
|
|
166
249
|
timeout: 60000,
|
|
167
250
|
});
|
|
168
251
|
}
|
|
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
252
|
_fixUsingParams(text, _params) {
|
|
184
253
|
if (!_params || typeof text !== "string") {
|
|
185
254
|
return text;
|
|
@@ -247,7 +316,7 @@ class StableBrowser {
|
|
|
247
316
|
locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
|
|
248
317
|
}
|
|
249
318
|
}
|
|
250
|
-
if (locator
|
|
319
|
+
if (locator?.engine) {
|
|
251
320
|
if (locator.engine === "css") {
|
|
252
321
|
locatorReturn = scope.locator(locator.selector);
|
|
253
322
|
}
|
|
@@ -268,7 +337,10 @@ class StableBrowser {
|
|
|
268
337
|
return locatorReturn;
|
|
269
338
|
}
|
|
270
339
|
async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
|
|
271
|
-
|
|
340
|
+
if (css && css.locator) {
|
|
341
|
+
css = css.locator;
|
|
342
|
+
}
|
|
343
|
+
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, false, _params);
|
|
272
344
|
if (result.elementCount === 0) {
|
|
273
345
|
return;
|
|
274
346
|
}
|
|
@@ -283,7 +355,7 @@ class StableBrowser {
|
|
|
283
355
|
}
|
|
284
356
|
async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
|
|
285
357
|
//const stringifyText = JSON.stringify(text);
|
|
286
|
-
return await scope.evaluate(([text, tag, regex, partial]) => {
|
|
358
|
+
return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
|
|
287
359
|
function isParent(parent, child) {
|
|
288
360
|
let currentNode = child.parentNode;
|
|
289
361
|
while (currentNode !== null) {
|
|
@@ -295,6 +367,15 @@ class StableBrowser {
|
|
|
295
367
|
return false;
|
|
296
368
|
}
|
|
297
369
|
document.isParent = isParent;
|
|
370
|
+
function getRegex(str) {
|
|
371
|
+
const match = str.match(/^\/(.*?)\/([gimuy]*)$/);
|
|
372
|
+
if (!match) {
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
let [_, pattern, flags] = match;
|
|
376
|
+
return new RegExp(pattern, flags);
|
|
377
|
+
}
|
|
378
|
+
document.getRegex = getRegex;
|
|
298
379
|
function collectAllShadowDomElements(element, result = []) {
|
|
299
380
|
// Check and add the element if it has a shadow root
|
|
300
381
|
if (element.shadowRoot) {
|
|
@@ -313,6 +394,10 @@ class StableBrowser {
|
|
|
313
394
|
if (!tag) {
|
|
314
395
|
tag = "*";
|
|
315
396
|
}
|
|
397
|
+
let regexpSearch = document.getRegex(text);
|
|
398
|
+
if (regexpSearch) {
|
|
399
|
+
regex = true;
|
|
400
|
+
}
|
|
316
401
|
let elements = Array.from(document.querySelectorAll(tag));
|
|
317
402
|
let shadowHosts = [];
|
|
318
403
|
document.collectAllShadowDomElements(document, shadowHosts);
|
|
@@ -328,7 +413,9 @@ class StableBrowser {
|
|
|
328
413
|
let randomToken = null;
|
|
329
414
|
const foundElements = [];
|
|
330
415
|
if (regex) {
|
|
331
|
-
|
|
416
|
+
if (!regexpSearch) {
|
|
417
|
+
regexpSearch = new RegExp(text, "im");
|
|
418
|
+
}
|
|
332
419
|
for (let i = 0; i < elements.length; i++) {
|
|
333
420
|
const element = elements[i];
|
|
334
421
|
if ((element.innerText && regexpSearch.test(element.innerText)) ||
|
|
@@ -342,8 +429,8 @@ class StableBrowser {
|
|
|
342
429
|
for (let i = 0; i < elements.length; i++) {
|
|
343
430
|
const element = elements[i];
|
|
344
431
|
if (partial) {
|
|
345
|
-
if ((element.innerText && element.innerText.trim().includes(text)) ||
|
|
346
|
-
(element.value && element.value.includes(text))) {
|
|
432
|
+
if ((element.innerText && element.innerText.toLowerCase().trim().includes(text.toLowerCase())) ||
|
|
433
|
+
(element.value && element.value.toLowerCase().includes(text.toLowerCase()))) {
|
|
347
434
|
foundElements.push(element);
|
|
348
435
|
}
|
|
349
436
|
}
|
|
@@ -388,6 +475,12 @@ class StableBrowser {
|
|
|
388
475
|
}
|
|
389
476
|
async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
|
|
390
477
|
let locatorSearch = selectorHierarchy[index];
|
|
478
|
+
try {
|
|
479
|
+
locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
|
|
480
|
+
}
|
|
481
|
+
catch (e) {
|
|
482
|
+
console.error(e);
|
|
483
|
+
}
|
|
391
484
|
//info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
|
|
392
485
|
let locator = null;
|
|
393
486
|
if (locatorSearch.climb && locatorSearch.climb >= 0) {
|
|
@@ -476,13 +569,18 @@ class StableBrowser {
|
|
|
476
569
|
if (result.foundElements.length > 0) {
|
|
477
570
|
let dialogCloseLocator = result.foundElements[0].locator;
|
|
478
571
|
await dialogCloseLocator.click();
|
|
572
|
+
// wait for the dialog to close
|
|
573
|
+
await dialogCloseLocator.waitFor({ state: "hidden" });
|
|
479
574
|
return { rerun: true };
|
|
480
575
|
}
|
|
481
576
|
}
|
|
482
577
|
}
|
|
483
578
|
return { rerun: false };
|
|
484
579
|
}
|
|
485
|
-
async _locate(selectors, info, _params, timeout
|
|
580
|
+
async _locate(selectors, info, _params, timeout) {
|
|
581
|
+
if (!timeout) {
|
|
582
|
+
timeout = 30000;
|
|
583
|
+
}
|
|
486
584
|
for (let i = 0; i < 3; i++) {
|
|
487
585
|
info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
|
|
488
586
|
for (let j = 0; j < selectors.locators.length; j++) {
|
|
@@ -496,32 +594,41 @@ class StableBrowser {
|
|
|
496
594
|
}
|
|
497
595
|
throw new Error("unable to locate element " + JSON.stringify(selectors));
|
|
498
596
|
}
|
|
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);
|
|
597
|
+
async _findFrameScope(selectors, timeout = 30000) {
|
|
505
598
|
let scope = this.page;
|
|
599
|
+
if (selectors.frame) {
|
|
600
|
+
return selectors.frame;
|
|
601
|
+
}
|
|
506
602
|
if (selectors.iframe_src || selectors.frameLocators) {
|
|
507
|
-
const findFrame = (frame, framescope) => {
|
|
603
|
+
const findFrame = async (frame, framescope) => {
|
|
508
604
|
for (let i = 0; i < frame.selectors.length; i++) {
|
|
509
605
|
let frameLocator = frame.selectors[i];
|
|
510
606
|
if (frameLocator.css) {
|
|
511
|
-
|
|
512
|
-
|
|
607
|
+
let testframescope = framescope.frameLocator(frameLocator.css);
|
|
608
|
+
if (frameLocator.index) {
|
|
609
|
+
testframescope = framescope.nth(frameLocator.index);
|
|
610
|
+
}
|
|
611
|
+
try {
|
|
612
|
+
await testframescope.owner().evaluateHandle(() => true, null, {
|
|
613
|
+
timeout: 5000,
|
|
614
|
+
});
|
|
615
|
+
framescope = testframescope;
|
|
616
|
+
break;
|
|
617
|
+
}
|
|
618
|
+
catch (error) {
|
|
619
|
+
console.error("frame not found " + frameLocator.css);
|
|
620
|
+
}
|
|
513
621
|
}
|
|
514
622
|
}
|
|
515
623
|
if (frame.children) {
|
|
516
|
-
return findFrame(frame.children, framescope);
|
|
624
|
+
return await findFrame(frame.children, framescope);
|
|
517
625
|
}
|
|
518
626
|
return framescope;
|
|
519
627
|
};
|
|
520
|
-
info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
|
|
521
628
|
while (true) {
|
|
522
629
|
let frameFound = false;
|
|
523
630
|
if (selectors.nestFrmLoc) {
|
|
524
|
-
scope = findFrame(selectors.nestFrmLoc, scope);
|
|
631
|
+
scope = await findFrame(selectors.nestFrmLoc, scope);
|
|
525
632
|
frameFound = true;
|
|
526
633
|
break;
|
|
527
634
|
}
|
|
@@ -550,6 +657,25 @@ class StableBrowser {
|
|
|
550
657
|
}
|
|
551
658
|
}
|
|
552
659
|
}
|
|
660
|
+
if (!scope) {
|
|
661
|
+
scope = this.page;
|
|
662
|
+
}
|
|
663
|
+
return scope;
|
|
664
|
+
}
|
|
665
|
+
async _getDocumentBody(selectors, timeout = 30000) {
|
|
666
|
+
let scope = await this._findFrameScope(selectors, timeout);
|
|
667
|
+
return scope.evaluate(() => {
|
|
668
|
+
var bodyContent = document.body.innerHTML;
|
|
669
|
+
return bodyContent;
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
673
|
+
let highPriorityTimeout = 5000;
|
|
674
|
+
let visibleOnlyTimeout = 6000;
|
|
675
|
+
let startTime = performance.now();
|
|
676
|
+
let locatorsCount = 0;
|
|
677
|
+
//let arrayMode = Array.isArray(selectors);
|
|
678
|
+
let scope = await this._findFrameScope(selectors, timeout);
|
|
553
679
|
let selectorsLocators = null;
|
|
554
680
|
selectorsLocators = selectors.locators;
|
|
555
681
|
// group selectors by priority
|
|
@@ -685,86 +811,121 @@ class StableBrowser {
|
|
|
685
811
|
}
|
|
686
812
|
return result;
|
|
687
813
|
}
|
|
688
|
-
async
|
|
689
|
-
this._validateSelectors(selectors);
|
|
814
|
+
async simpleClick(elementDescription, _params, options = {}, world = null) {
|
|
690
815
|
const startTime = Date.now();
|
|
691
|
-
|
|
692
|
-
|
|
816
|
+
let timeout = 30000;
|
|
817
|
+
if (options && options.timeout) {
|
|
818
|
+
timeout = options.timeout;
|
|
693
819
|
}
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
820
|
+
while (true) {
|
|
821
|
+
try {
|
|
822
|
+
const result = await locate_element(this.context, elementDescription, "click");
|
|
823
|
+
if (result?.elementNumber >= 0) {
|
|
824
|
+
const selectors = {
|
|
825
|
+
frame: result?.frame,
|
|
826
|
+
locators: [
|
|
827
|
+
{
|
|
828
|
+
css: result?.css,
|
|
829
|
+
},
|
|
830
|
+
],
|
|
831
|
+
};
|
|
832
|
+
await this.click(selectors, _params, options, world);
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
catch (e) {
|
|
837
|
+
if (performance.now() - startTime > timeout) {
|
|
838
|
+
throw e;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
|
|
845
|
+
const startTime = Date.now();
|
|
846
|
+
let timeout = 30000;
|
|
847
|
+
if (options && options.timeout) {
|
|
848
|
+
timeout = options.timeout;
|
|
849
|
+
}
|
|
850
|
+
while (true) {
|
|
851
|
+
try {
|
|
852
|
+
const result = await locate_element(this.context, elementDescription, "fill", value);
|
|
853
|
+
if (result?.elementNumber >= 0) {
|
|
854
|
+
const selectors = {
|
|
855
|
+
frame: result?.frame,
|
|
856
|
+
locators: [
|
|
857
|
+
{
|
|
858
|
+
css: result?.css,
|
|
859
|
+
},
|
|
860
|
+
],
|
|
861
|
+
};
|
|
862
|
+
await this.clickType(selectors, value, false, _params, options, world);
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
catch (e) {
|
|
867
|
+
if (performance.now() - startTime > timeout) {
|
|
868
|
+
throw e;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
async click(selectors, _params, options = {}, world = null) {
|
|
875
|
+
const state = {
|
|
876
|
+
selectors,
|
|
877
|
+
_params,
|
|
878
|
+
options,
|
|
879
|
+
world,
|
|
880
|
+
text: "Click element",
|
|
881
|
+
type: Types.CLICK,
|
|
882
|
+
operation: "click",
|
|
883
|
+
log: "***** click on " + selectors.element_name + " *****\n",
|
|
884
|
+
};
|
|
701
885
|
try {
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
886
|
+
await _preCommand(state, this);
|
|
887
|
+
if (state.options && state.options.context) {
|
|
888
|
+
state.selectors.locators[0].text = state.options.context;
|
|
889
|
+
}
|
|
705
890
|
try {
|
|
706
|
-
await
|
|
707
|
-
await element.click();
|
|
891
|
+
await state.element.click();
|
|
708
892
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
709
893
|
}
|
|
710
894
|
catch (e) {
|
|
711
895
|
// await this.closeUnexpectedPopups();
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
await element.dispatchEvent("click");
|
|
896
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
897
|
+
await state.element.dispatchEvent("click");
|
|
715
898
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
716
899
|
}
|
|
717
900
|
await this.waitForPageLoad();
|
|
718
|
-
return info;
|
|
901
|
+
return state.info;
|
|
719
902
|
}
|
|
720
903
|
catch (e) {
|
|
721
|
-
|
|
722
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
723
|
-
info.screenshotPath = screenshotPath;
|
|
724
|
-
Object.assign(e, { info: info });
|
|
725
|
-
error = e;
|
|
726
|
-
throw e;
|
|
904
|
+
await _commandError(state, e, this);
|
|
727
905
|
}
|
|
728
906
|
finally {
|
|
729
|
-
|
|
730
|
-
this._reportToWorld(world, {
|
|
731
|
-
element_name: selectors.element_name,
|
|
732
|
-
type: Types.CLICK,
|
|
733
|
-
text: `Click element`,
|
|
734
|
-
screenshotId,
|
|
735
|
-
result: error
|
|
736
|
-
? {
|
|
737
|
-
status: "FAILED",
|
|
738
|
-
startTime,
|
|
739
|
-
endTime,
|
|
740
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
741
|
-
}
|
|
742
|
-
: {
|
|
743
|
-
status: "PASSED",
|
|
744
|
-
startTime,
|
|
745
|
-
endTime,
|
|
746
|
-
},
|
|
747
|
-
info: info,
|
|
748
|
-
});
|
|
907
|
+
_commandFinally(state, this);
|
|
749
908
|
}
|
|
750
909
|
}
|
|
751
910
|
async setCheck(selectors, checked = true, _params, options = {}, world = null) {
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
911
|
+
const state = {
|
|
912
|
+
selectors,
|
|
913
|
+
_params,
|
|
914
|
+
options,
|
|
915
|
+
world,
|
|
916
|
+
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
917
|
+
text: checked ? `Check element` : `Uncheck element`,
|
|
918
|
+
operation: "setCheck",
|
|
919
|
+
log: "***** check " + selectors.element_name + " *****\n",
|
|
920
|
+
};
|
|
762
921
|
try {
|
|
763
|
-
|
|
764
|
-
|
|
922
|
+
await _preCommand(state, this);
|
|
923
|
+
state.info.checked = checked;
|
|
924
|
+
// let element = await this._locate(selectors, info, _params);
|
|
925
|
+
// ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
765
926
|
try {
|
|
766
|
-
await this._highlightElements(element);
|
|
767
|
-
await element.setChecked(checked);
|
|
927
|
+
// await this._highlightElements(element);
|
|
928
|
+
await state.element.setChecked(checked);
|
|
768
929
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
769
930
|
}
|
|
770
931
|
catch (e) {
|
|
@@ -773,179 +934,108 @@ class StableBrowser {
|
|
|
773
934
|
}
|
|
774
935
|
else {
|
|
775
936
|
//await this.closeUnexpectedPopups();
|
|
776
|
-
info.log += "setCheck failed, will try again" + "\n";
|
|
777
|
-
element = await this._locate(selectors, info, _params);
|
|
778
|
-
await element.setChecked(checked, { timeout: 5000, force: true });
|
|
937
|
+
state.info.log += "setCheck failed, will try again" + "\n";
|
|
938
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
939
|
+
await state.element.setChecked(checked, { timeout: 5000, force: true });
|
|
779
940
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
780
941
|
}
|
|
781
942
|
}
|
|
782
943
|
await this.waitForPageLoad();
|
|
783
|
-
return info;
|
|
944
|
+
return state.info;
|
|
784
945
|
}
|
|
785
946
|
catch (e) {
|
|
786
|
-
|
|
787
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
788
|
-
info.screenshotPath = screenshotPath;
|
|
789
|
-
Object.assign(e, { info: info });
|
|
790
|
-
error = e;
|
|
791
|
-
throw e;
|
|
947
|
+
await _commandError(state, e, this);
|
|
792
948
|
}
|
|
793
949
|
finally {
|
|
794
|
-
|
|
795
|
-
this._reportToWorld(world, {
|
|
796
|
-
element_name: selectors.element_name,
|
|
797
|
-
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
798
|
-
text: checked ? `Check element` : `Uncheck element`,
|
|
799
|
-
screenshotId,
|
|
800
|
-
result: error
|
|
801
|
-
? {
|
|
802
|
-
status: "FAILED",
|
|
803
|
-
startTime,
|
|
804
|
-
endTime,
|
|
805
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
806
|
-
}
|
|
807
|
-
: {
|
|
808
|
-
status: "PASSED",
|
|
809
|
-
startTime,
|
|
810
|
-
endTime,
|
|
811
|
-
},
|
|
812
|
-
info: info,
|
|
813
|
-
});
|
|
950
|
+
_commandFinally(state, this);
|
|
814
951
|
}
|
|
815
952
|
}
|
|
816
953
|
async hover(selectors, _params, options = {}, world = null) {
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
954
|
+
const state = {
|
|
955
|
+
selectors,
|
|
956
|
+
_params,
|
|
957
|
+
options,
|
|
958
|
+
world,
|
|
959
|
+
type: Types.HOVER,
|
|
960
|
+
text: `Hover element`,
|
|
961
|
+
operation: "hover",
|
|
962
|
+
log: "***** hover " + selectors.element_name + " *****\n",
|
|
963
|
+
};
|
|
826
964
|
try {
|
|
827
|
-
|
|
828
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
965
|
+
await _preCommand(state, this);
|
|
829
966
|
try {
|
|
830
|
-
await
|
|
831
|
-
await element.hover();
|
|
967
|
+
await state.element.hover();
|
|
832
968
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
833
969
|
}
|
|
834
970
|
catch (e) {
|
|
835
971
|
//await this.closeUnexpectedPopups();
|
|
836
|
-
info.log += "hover failed, will try again" + "\n";
|
|
837
|
-
element = await this._locate(selectors, info, _params);
|
|
838
|
-
await element.hover({ timeout: 10000 });
|
|
972
|
+
state.info.log += "hover failed, will try again" + "\n";
|
|
973
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
974
|
+
await state.element.hover({ timeout: 10000 });
|
|
839
975
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
840
976
|
}
|
|
841
977
|
await this.waitForPageLoad();
|
|
842
|
-
return info;
|
|
978
|
+
return state.info;
|
|
843
979
|
}
|
|
844
980
|
catch (e) {
|
|
845
|
-
|
|
846
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
847
|
-
info.screenshotPath = screenshotPath;
|
|
848
|
-
Object.assign(e, { info: info });
|
|
849
|
-
error = e;
|
|
850
|
-
throw e;
|
|
981
|
+
await _commandError(state, e, this);
|
|
851
982
|
}
|
|
852
983
|
finally {
|
|
853
|
-
|
|
854
|
-
this._reportToWorld(world, {
|
|
855
|
-
element_name: selectors.element_name,
|
|
856
|
-
type: Types.HOVER,
|
|
857
|
-
text: `Hover element`,
|
|
858
|
-
screenshotId,
|
|
859
|
-
result: error
|
|
860
|
-
? {
|
|
861
|
-
status: "FAILED",
|
|
862
|
-
startTime,
|
|
863
|
-
endTime,
|
|
864
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
865
|
-
}
|
|
866
|
-
: {
|
|
867
|
-
status: "PASSED",
|
|
868
|
-
startTime,
|
|
869
|
-
endTime,
|
|
870
|
-
},
|
|
871
|
-
info: info,
|
|
872
|
-
});
|
|
984
|
+
_commandFinally(state, this);
|
|
873
985
|
}
|
|
874
986
|
}
|
|
875
987
|
async selectOption(selectors, values, _params = null, options = {}, world = null) {
|
|
876
|
-
this._validateSelectors(selectors);
|
|
877
988
|
if (!values) {
|
|
878
989
|
throw new Error("values is null");
|
|
879
990
|
}
|
|
880
|
-
const
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
991
|
+
const state = {
|
|
992
|
+
selectors,
|
|
993
|
+
_params,
|
|
994
|
+
options,
|
|
995
|
+
world,
|
|
996
|
+
value: values.toString(),
|
|
997
|
+
type: Types.SELECT,
|
|
998
|
+
text: `Select option: ${values}`,
|
|
999
|
+
operation: "selectOption",
|
|
1000
|
+
log: "***** select option " + selectors.element_name + " *****\n",
|
|
1001
|
+
};
|
|
888
1002
|
try {
|
|
889
|
-
|
|
890
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1003
|
+
await _preCommand(state, this);
|
|
891
1004
|
try {
|
|
892
|
-
await
|
|
893
|
-
await element.selectOption(values);
|
|
1005
|
+
await state.element.selectOption(values);
|
|
894
1006
|
}
|
|
895
1007
|
catch (e) {
|
|
896
1008
|
//await this.closeUnexpectedPopups();
|
|
897
|
-
info.log += "selectOption failed, will try force" + "\n";
|
|
898
|
-
await element.selectOption(values, { timeout: 10000, force: true });
|
|
1009
|
+
state.info.log += "selectOption failed, will try force" + "\n";
|
|
1010
|
+
await state.element.selectOption(values, { timeout: 10000, force: true });
|
|
899
1011
|
}
|
|
900
1012
|
await this.waitForPageLoad();
|
|
901
|
-
return info;
|
|
1013
|
+
return state.info;
|
|
902
1014
|
}
|
|
903
1015
|
catch (e) {
|
|
904
|
-
|
|
905
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
906
|
-
info.screenshotPath = screenshotPath;
|
|
907
|
-
Object.assign(e, { info: info });
|
|
908
|
-
this.logger.info("click failed, will try next selector");
|
|
909
|
-
error = e;
|
|
910
|
-
throw e;
|
|
1016
|
+
await _commandError(state, e, this);
|
|
911
1017
|
}
|
|
912
1018
|
finally {
|
|
913
|
-
|
|
914
|
-
this._reportToWorld(world, {
|
|
915
|
-
element_name: selectors.element_name,
|
|
916
|
-
type: Types.SELECT,
|
|
917
|
-
text: `Select option: ${values}`,
|
|
918
|
-
value: values.toString(),
|
|
919
|
-
screenshotId,
|
|
920
|
-
result: error
|
|
921
|
-
? {
|
|
922
|
-
status: "FAILED",
|
|
923
|
-
startTime,
|
|
924
|
-
endTime,
|
|
925
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
926
|
-
}
|
|
927
|
-
: {
|
|
928
|
-
status: "PASSED",
|
|
929
|
-
startTime,
|
|
930
|
-
endTime,
|
|
931
|
-
},
|
|
932
|
-
info: info,
|
|
933
|
-
});
|
|
1019
|
+
_commandFinally(state, this);
|
|
934
1020
|
}
|
|
935
1021
|
}
|
|
936
1022
|
async type(_value, _params = null, options = {}, world = null) {
|
|
937
|
-
const
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1023
|
+
const state = {
|
|
1024
|
+
value: _value,
|
|
1025
|
+
_params,
|
|
1026
|
+
options,
|
|
1027
|
+
world,
|
|
1028
|
+
locate: false,
|
|
1029
|
+
scroll: false,
|
|
1030
|
+
highlight: false,
|
|
1031
|
+
type: Types.TYPE_PRESS,
|
|
1032
|
+
text: `Type value: ${_value}`,
|
|
1033
|
+
operation: "type",
|
|
1034
|
+
log: "",
|
|
1035
|
+
};
|
|
946
1036
|
try {
|
|
947
|
-
|
|
948
|
-
const valueSegment =
|
|
1037
|
+
await _preCommand(state, this);
|
|
1038
|
+
const valueSegment = state.value.split("&&");
|
|
949
1039
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
950
1040
|
if (i > 0) {
|
|
951
1041
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -965,108 +1055,53 @@ class StableBrowser {
|
|
|
965
1055
|
await this.page.keyboard.type(value);
|
|
966
1056
|
}
|
|
967
1057
|
}
|
|
968
|
-
return info;
|
|
1058
|
+
return state.info;
|
|
969
1059
|
}
|
|
970
1060
|
catch (e) {
|
|
971
|
-
|
|
972
|
-
this.logger.error("type failed " + info.log);
|
|
973
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
974
|
-
info.screenshotPath = screenshotPath;
|
|
975
|
-
Object.assign(e, { info: info });
|
|
976
|
-
error = e;
|
|
977
|
-
throw e;
|
|
1061
|
+
await _commandError(state, e, this);
|
|
978
1062
|
}
|
|
979
1063
|
finally {
|
|
980
|
-
|
|
981
|
-
this._reportToWorld(world, {
|
|
982
|
-
type: Types.TYPE_PRESS,
|
|
983
|
-
screenshotId,
|
|
984
|
-
value: _value,
|
|
985
|
-
text: `type value: ${_value}`,
|
|
986
|
-
result: error
|
|
987
|
-
? {
|
|
988
|
-
status: "FAILED",
|
|
989
|
-
startTime,
|
|
990
|
-
endTime,
|
|
991
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
992
|
-
}
|
|
993
|
-
: {
|
|
994
|
-
status: "PASSED",
|
|
995
|
-
startTime,
|
|
996
|
-
endTime,
|
|
997
|
-
},
|
|
998
|
-
info: info,
|
|
999
|
-
});
|
|
1064
|
+
_commandFinally(state, this);
|
|
1000
1065
|
}
|
|
1001
1066
|
}
|
|
1002
1067
|
async setInputValue(selectors, value, _params = null, options = {}, world = null) {
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
let screenshotPath = null;
|
|
1068
|
+
const state = {
|
|
1069
|
+
selectors,
|
|
1070
|
+
_params,
|
|
1071
|
+
value,
|
|
1072
|
+
options,
|
|
1073
|
+
world,
|
|
1074
|
+
type: Types.SET_INPUT,
|
|
1075
|
+
text: `Set input value`,
|
|
1076
|
+
operation: "setInputValue",
|
|
1077
|
+
log: "***** set input value " + selectors.element_name + " *****\n",
|
|
1078
|
+
};
|
|
1015
1079
|
try {
|
|
1016
|
-
|
|
1017
|
-
let
|
|
1018
|
-
await this.scrollIfNeeded(element, info);
|
|
1019
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1020
|
-
await this._highlightElements(element);
|
|
1080
|
+
await _preCommand(state, this);
|
|
1081
|
+
let value = await this._replaceWithLocalData(state.value, this);
|
|
1021
1082
|
try {
|
|
1022
|
-
await element.evaluateHandle((el, value) => {
|
|
1083
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1023
1084
|
el.value = value;
|
|
1024
1085
|
}, value);
|
|
1025
1086
|
}
|
|
1026
1087
|
catch (error) {
|
|
1027
1088
|
this.logger.error("setInputValue failed, will try again");
|
|
1028
|
-
|
|
1029
|
-
info.
|
|
1030
|
-
|
|
1031
|
-
await element.evaluateHandle((el, value) => {
|
|
1089
|
+
await _screenshot(state, this);
|
|
1090
|
+
Object.assign(error, { info: state.info });
|
|
1091
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1032
1092
|
el.value = value;
|
|
1033
1093
|
});
|
|
1034
1094
|
}
|
|
1035
1095
|
}
|
|
1036
1096
|
catch (e) {
|
|
1037
|
-
|
|
1038
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1039
|
-
info.screenshotPath = screenshotPath;
|
|
1040
|
-
Object.assign(e, { info: info });
|
|
1041
|
-
error = e;
|
|
1042
|
-
throw e;
|
|
1097
|
+
await _commandError(state, e, this);
|
|
1043
1098
|
}
|
|
1044
1099
|
finally {
|
|
1045
|
-
|
|
1046
|
-
this._reportToWorld(world, {
|
|
1047
|
-
element_name: selectors.element_name,
|
|
1048
|
-
type: Types.SET_INPUT,
|
|
1049
|
-
text: `Set input value`,
|
|
1050
|
-
value: value,
|
|
1051
|
-
screenshotId,
|
|
1052
|
-
result: error
|
|
1053
|
-
? {
|
|
1054
|
-
status: "FAILED",
|
|
1055
|
-
startTime,
|
|
1056
|
-
endTime,
|
|
1057
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1058
|
-
}
|
|
1059
|
-
: {
|
|
1060
|
-
status: "PASSED",
|
|
1061
|
-
startTime,
|
|
1062
|
-
endTime,
|
|
1063
|
-
},
|
|
1064
|
-
info: info,
|
|
1065
|
-
});
|
|
1100
|
+
_commandFinally(state, this);
|
|
1066
1101
|
}
|
|
1067
1102
|
}
|
|
1068
1103
|
async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
|
|
1069
|
-
|
|
1104
|
+
_validateSelectors(selectors);
|
|
1070
1105
|
const startTime = Date.now();
|
|
1071
1106
|
let error = null;
|
|
1072
1107
|
let screenshotId = null;
|
|
@@ -1159,32 +1194,32 @@ class StableBrowser {
|
|
|
1159
1194
|
}
|
|
1160
1195
|
}
|
|
1161
1196
|
async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
|
|
1162
|
-
|
|
1163
|
-
const startTime = Date.now();
|
|
1164
|
-
let error = null;
|
|
1165
|
-
let screenshotId = null;
|
|
1166
|
-
let screenshotPath = null;
|
|
1167
|
-
const info = {};
|
|
1168
|
-
info.log = "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n";
|
|
1169
|
-
info.operation = "clickType";
|
|
1170
|
-
info.selectors = selectors;
|
|
1197
|
+
_value = unEscapeString(_value);
|
|
1171
1198
|
const newValue = await this._replaceWithLocalData(_value, world);
|
|
1199
|
+
const state = {
|
|
1200
|
+
selectors,
|
|
1201
|
+
_params,
|
|
1202
|
+
value: newValue,
|
|
1203
|
+
originalValue: _value,
|
|
1204
|
+
options,
|
|
1205
|
+
world,
|
|
1206
|
+
type: Types.FILL,
|
|
1207
|
+
text: `Click type input with value: ${_value}`,
|
|
1208
|
+
operation: "clickType",
|
|
1209
|
+
log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
|
|
1210
|
+
};
|
|
1172
1211
|
if (newValue !== _value) {
|
|
1173
1212
|
//this.logger.info(_value + "=" + newValue);
|
|
1174
1213
|
_value = newValue;
|
|
1175
1214
|
}
|
|
1176
|
-
info.value = _value;
|
|
1177
1215
|
try {
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
await this.scrollIfNeeded(element, info);
|
|
1181
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1182
|
-
await this._highlightElements(element);
|
|
1216
|
+
await _preCommand(state, this);
|
|
1217
|
+
state.info.value = _value;
|
|
1183
1218
|
if (options === null || options === undefined || !options.press) {
|
|
1184
1219
|
try {
|
|
1185
|
-
let currentValue = await element.inputValue();
|
|
1220
|
+
let currentValue = await state.element.inputValue();
|
|
1186
1221
|
if (currentValue) {
|
|
1187
|
-
await element.fill("");
|
|
1222
|
+
await state.element.fill("");
|
|
1188
1223
|
}
|
|
1189
1224
|
}
|
|
1190
1225
|
catch (e) {
|
|
@@ -1193,22 +1228,22 @@ class StableBrowser {
|
|
|
1193
1228
|
}
|
|
1194
1229
|
if (options === null || options === undefined || options.press) {
|
|
1195
1230
|
try {
|
|
1196
|
-
await element.click({ timeout: 5000 });
|
|
1231
|
+
await state.element.click({ timeout: 5000 });
|
|
1197
1232
|
}
|
|
1198
1233
|
catch (e) {
|
|
1199
|
-
await element.dispatchEvent("click");
|
|
1234
|
+
await state.element.dispatchEvent("click");
|
|
1200
1235
|
}
|
|
1201
1236
|
}
|
|
1202
1237
|
else {
|
|
1203
1238
|
try {
|
|
1204
|
-
await element.focus();
|
|
1239
|
+
await state.element.focus();
|
|
1205
1240
|
}
|
|
1206
1241
|
catch (e) {
|
|
1207
|
-
await element.dispatchEvent("focus");
|
|
1242
|
+
await state.element.dispatchEvent("focus");
|
|
1208
1243
|
}
|
|
1209
1244
|
}
|
|
1210
1245
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1211
|
-
const valueSegment =
|
|
1246
|
+
const valueSegment = state.value.split("&&");
|
|
1212
1247
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1213
1248
|
if (i > 0) {
|
|
1214
1249
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1228,13 +1263,14 @@ class StableBrowser {
|
|
|
1228
1263
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1229
1264
|
}
|
|
1230
1265
|
}
|
|
1266
|
+
await _screenshot(state, this);
|
|
1231
1267
|
if (enter === true) {
|
|
1232
1268
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1233
1269
|
await this.page.keyboard.press("Enter");
|
|
1234
1270
|
await this.waitForPageLoad();
|
|
1235
1271
|
}
|
|
1236
1272
|
else if (enter === false) {
|
|
1237
|
-
await element.dispatchEvent("change");
|
|
1273
|
+
await state.element.dispatchEvent("change");
|
|
1238
1274
|
//await this.page.keyboard.press("Tab");
|
|
1239
1275
|
}
|
|
1240
1276
|
else {
|
|
@@ -1243,103 +1279,50 @@ class StableBrowser {
|
|
|
1243
1279
|
await this.waitForPageLoad();
|
|
1244
1280
|
}
|
|
1245
1281
|
}
|
|
1246
|
-
return info;
|
|
1282
|
+
return state.info;
|
|
1247
1283
|
}
|
|
1248
1284
|
catch (e) {
|
|
1249
|
-
|
|
1250
|
-
this.logger.error("fill failed " + JSON.stringify(info));
|
|
1251
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1252
|
-
info.screenshotPath = screenshotPath;
|
|
1253
|
-
Object.assign(e, { info: info });
|
|
1254
|
-
error = e;
|
|
1255
|
-
throw e;
|
|
1285
|
+
await _commandError(state, e, this);
|
|
1256
1286
|
}
|
|
1257
1287
|
finally {
|
|
1258
|
-
|
|
1259
|
-
this._reportToWorld(world, {
|
|
1260
|
-
element_name: selectors.element_name,
|
|
1261
|
-
type: Types.FILL,
|
|
1262
|
-
screenshotId,
|
|
1263
|
-
value: _value,
|
|
1264
|
-
text: `clickType input with value: ${_value}`,
|
|
1265
|
-
result: error
|
|
1266
|
-
? {
|
|
1267
|
-
status: "FAILED",
|
|
1268
|
-
startTime,
|
|
1269
|
-
endTime,
|
|
1270
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1271
|
-
}
|
|
1272
|
-
: {
|
|
1273
|
-
status: "PASSED",
|
|
1274
|
-
startTime,
|
|
1275
|
-
endTime,
|
|
1276
|
-
},
|
|
1277
|
-
info: info,
|
|
1278
|
-
});
|
|
1288
|
+
_commandFinally(state, this);
|
|
1279
1289
|
}
|
|
1280
1290
|
}
|
|
1281
1291
|
async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
+
const state = {
|
|
1293
|
+
selectors,
|
|
1294
|
+
_params,
|
|
1295
|
+
value: unEscapeString(value),
|
|
1296
|
+
options,
|
|
1297
|
+
world,
|
|
1298
|
+
type: Types.FILL,
|
|
1299
|
+
text: `Fill input with value: ${value}`,
|
|
1300
|
+
operation: "fill",
|
|
1301
|
+
log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
|
|
1302
|
+
};
|
|
1292
1303
|
try {
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
await
|
|
1296
|
-
await element.fill(value);
|
|
1297
|
-
await element.dispatchEvent("change");
|
|
1304
|
+
await _preCommand(state, this);
|
|
1305
|
+
await state.element.fill(value);
|
|
1306
|
+
await state.element.dispatchEvent("change");
|
|
1298
1307
|
if (enter) {
|
|
1299
1308
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1300
1309
|
await this.page.keyboard.press("Enter");
|
|
1301
1310
|
}
|
|
1302
1311
|
await this.waitForPageLoad();
|
|
1303
|
-
return info;
|
|
1312
|
+
return state.info;
|
|
1304
1313
|
}
|
|
1305
1314
|
catch (e) {
|
|
1306
|
-
|
|
1307
|
-
this.logger.error("fill failed " + info.log);
|
|
1308
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1309
|
-
info.screenshotPath = screenshotPath;
|
|
1310
|
-
Object.assign(e, { info: info });
|
|
1311
|
-
error = e;
|
|
1312
|
-
throw e;
|
|
1315
|
+
await _commandError(state, e, this);
|
|
1313
1316
|
}
|
|
1314
1317
|
finally {
|
|
1315
|
-
|
|
1316
|
-
this._reportToWorld(world, {
|
|
1317
|
-
element_name: selectors.element_name,
|
|
1318
|
-
type: Types.FILL,
|
|
1319
|
-
screenshotId,
|
|
1320
|
-
value,
|
|
1321
|
-
text: `Fill input with value: ${value}`,
|
|
1322
|
-
result: error
|
|
1323
|
-
? {
|
|
1324
|
-
status: "FAILED",
|
|
1325
|
-
startTime,
|
|
1326
|
-
endTime,
|
|
1327
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1328
|
-
}
|
|
1329
|
-
: {
|
|
1330
|
-
status: "PASSED",
|
|
1331
|
-
startTime,
|
|
1332
|
-
endTime,
|
|
1333
|
-
},
|
|
1334
|
-
info: info,
|
|
1335
|
-
});
|
|
1318
|
+
_commandFinally(state, this);
|
|
1336
1319
|
}
|
|
1337
1320
|
}
|
|
1338
1321
|
async getText(selectors, _params = null, options = {}, info = {}, world = null) {
|
|
1339
1322
|
return await this._getText(selectors, 0, _params, options, info, world);
|
|
1340
1323
|
}
|
|
1341
1324
|
async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
|
|
1342
|
-
|
|
1325
|
+
_validateSelectors(selectors);
|
|
1343
1326
|
let screenshotId = null;
|
|
1344
1327
|
let screenshotPath = null;
|
|
1345
1328
|
if (!info.log) {
|
|
@@ -1383,165 +1366,124 @@ class StableBrowser {
|
|
|
1383
1366
|
}
|
|
1384
1367
|
}
|
|
1385
1368
|
async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
|
|
1386
|
-
var _a;
|
|
1387
|
-
this._validateSelectors(selectors);
|
|
1388
1369
|
if (!pattern) {
|
|
1389
1370
|
throw new Error("pattern is null");
|
|
1390
1371
|
}
|
|
1391
1372
|
if (!text) {
|
|
1392
1373
|
throw new Error("text is null");
|
|
1393
1374
|
}
|
|
1375
|
+
const state = {
|
|
1376
|
+
selectors,
|
|
1377
|
+
_params,
|
|
1378
|
+
pattern,
|
|
1379
|
+
value: pattern,
|
|
1380
|
+
options,
|
|
1381
|
+
world,
|
|
1382
|
+
locate: false,
|
|
1383
|
+
scroll: false,
|
|
1384
|
+
screenshot: false,
|
|
1385
|
+
highlight: false,
|
|
1386
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1387
|
+
text: `Verify element contains pattern: ${pattern}`,
|
|
1388
|
+
operation: "containsPattern",
|
|
1389
|
+
log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
|
|
1390
|
+
};
|
|
1394
1391
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1395
1392
|
if (newValue !== text) {
|
|
1396
1393
|
this.logger.info(text + "=" + newValue);
|
|
1397
1394
|
text = newValue;
|
|
1398
1395
|
}
|
|
1399
|
-
const startTime = Date.now();
|
|
1400
|
-
let error = null;
|
|
1401
|
-
let screenshotId = null;
|
|
1402
|
-
let screenshotPath = null;
|
|
1403
|
-
const info = {};
|
|
1404
|
-
info.log =
|
|
1405
|
-
"***** verify element " + selectors.element_name + " contains pattern " + pattern + "/" + text + " *****\n";
|
|
1406
|
-
info.operation = "containsPattern";
|
|
1407
|
-
info.selectors = selectors;
|
|
1408
|
-
info.value = text;
|
|
1409
|
-
info.pattern = pattern;
|
|
1410
1396
|
let foundObj = null;
|
|
1411
1397
|
try {
|
|
1412
|
-
|
|
1398
|
+
await _preCommand(state, this);
|
|
1399
|
+
state.info.pattern = pattern;
|
|
1400
|
+
foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
|
|
1413
1401
|
if (foundObj && foundObj.element) {
|
|
1414
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1402
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1415
1403
|
}
|
|
1416
|
-
|
|
1404
|
+
await _screenshot(state, this);
|
|
1417
1405
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1418
1406
|
pattern = pattern.replace("{text}", escapedText);
|
|
1419
1407
|
let regex = new RegExp(pattern, "im");
|
|
1420
|
-
if (!regex.test(foundObj
|
|
1421
|
-
info.foundText = foundObj
|
|
1408
|
+
if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
|
|
1409
|
+
state.info.foundText = foundObj?.text;
|
|
1422
1410
|
throw new Error("element doesn't contain text " + text);
|
|
1423
1411
|
}
|
|
1424
|
-
return info;
|
|
1412
|
+
return state.info;
|
|
1425
1413
|
}
|
|
1426
1414
|
catch (e) {
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
this.logger.error("found text " + (foundObj === null || foundObj === void 0 ? void 0 : foundObj.text) + " pattern " + pattern);
|
|
1430
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1431
|
-
info.screenshotPath = screenshotPath;
|
|
1432
|
-
Object.assign(e, { info: info });
|
|
1433
|
-
error = e;
|
|
1434
|
-
throw e;
|
|
1415
|
+
this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
|
|
1416
|
+
await _commandError(state, e, this);
|
|
1435
1417
|
}
|
|
1436
1418
|
finally {
|
|
1437
|
-
|
|
1438
|
-
this._reportToWorld(world, {
|
|
1439
|
-
element_name: selectors.element_name,
|
|
1440
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1441
|
-
value: pattern,
|
|
1442
|
-
text: `Verify element contains pattern: ${pattern}`,
|
|
1443
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1444
|
-
result: error
|
|
1445
|
-
? {
|
|
1446
|
-
status: "FAILED",
|
|
1447
|
-
startTime,
|
|
1448
|
-
endTime,
|
|
1449
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1450
|
-
}
|
|
1451
|
-
: {
|
|
1452
|
-
status: "PASSED",
|
|
1453
|
-
startTime,
|
|
1454
|
-
endTime,
|
|
1455
|
-
},
|
|
1456
|
-
info: info,
|
|
1457
|
-
});
|
|
1419
|
+
_commandFinally(state, this);
|
|
1458
1420
|
}
|
|
1459
1421
|
}
|
|
1460
1422
|
async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
|
|
1461
|
-
|
|
1462
|
-
|
|
1423
|
+
const state = {
|
|
1424
|
+
selectors,
|
|
1425
|
+
_params,
|
|
1426
|
+
value: text,
|
|
1427
|
+
options,
|
|
1428
|
+
world,
|
|
1429
|
+
locate: false,
|
|
1430
|
+
scroll: false,
|
|
1431
|
+
screenshot: false,
|
|
1432
|
+
highlight: false,
|
|
1433
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1434
|
+
text: `Verify element contains text: ${text}`,
|
|
1435
|
+
operation: "containsText",
|
|
1436
|
+
log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
|
|
1437
|
+
};
|
|
1463
1438
|
if (!text) {
|
|
1464
1439
|
throw new Error("text is null");
|
|
1465
1440
|
}
|
|
1466
|
-
|
|
1467
|
-
let error = null;
|
|
1468
|
-
let screenshotId = null;
|
|
1469
|
-
let screenshotPath = null;
|
|
1470
|
-
const info = {};
|
|
1471
|
-
info.log = "***** verify element " + selectors.element_name + " contains text " + text + " *****\n";
|
|
1472
|
-
info.operation = "containsText";
|
|
1473
|
-
info.selectors = selectors;
|
|
1441
|
+
text = unEscapeString(text);
|
|
1474
1442
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1475
1443
|
if (newValue !== text) {
|
|
1476
1444
|
this.logger.info(text + "=" + newValue);
|
|
1477
1445
|
text = newValue;
|
|
1478
1446
|
}
|
|
1479
|
-
info.value = text;
|
|
1480
1447
|
let foundObj = null;
|
|
1481
1448
|
try {
|
|
1482
|
-
|
|
1449
|
+
await _preCommand(state, this);
|
|
1450
|
+
foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
|
|
1483
1451
|
if (foundObj && foundObj.element) {
|
|
1484
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1452
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1485
1453
|
}
|
|
1486
|
-
|
|
1454
|
+
await _screenshot(state, this);
|
|
1487
1455
|
const dateAlternatives = findDateAlternatives(text);
|
|
1488
1456
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1489
1457
|
if (dateAlternatives.date) {
|
|
1490
1458
|
for (let i = 0; i < dateAlternatives.dates.length; i++) {
|
|
1491
|
-
if (
|
|
1492
|
-
|
|
1493
|
-
return info;
|
|
1459
|
+
if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
|
|
1460
|
+
foundObj?.value?.includes(dateAlternatives.dates[i])) {
|
|
1461
|
+
return state.info;
|
|
1494
1462
|
}
|
|
1495
1463
|
}
|
|
1496
1464
|
throw new Error("element doesn't contain text " + text);
|
|
1497
1465
|
}
|
|
1498
1466
|
else if (numberAlternatives.number) {
|
|
1499
1467
|
for (let i = 0; i < numberAlternatives.numbers.length; i++) {
|
|
1500
|
-
if (
|
|
1501
|
-
|
|
1502
|
-
return info;
|
|
1468
|
+
if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
|
|
1469
|
+
foundObj?.value?.includes(numberAlternatives.numbers[i])) {
|
|
1470
|
+
return state.info;
|
|
1503
1471
|
}
|
|
1504
1472
|
}
|
|
1505
1473
|
throw new Error("element doesn't contain text " + text);
|
|
1506
1474
|
}
|
|
1507
|
-
else if (!
|
|
1508
|
-
info.foundText = foundObj
|
|
1509
|
-
info.value = foundObj
|
|
1475
|
+
else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
|
|
1476
|
+
state.info.foundText = foundObj?.text;
|
|
1477
|
+
state.info.value = foundObj?.value;
|
|
1510
1478
|
throw new Error("element doesn't contain text " + text);
|
|
1511
1479
|
}
|
|
1512
|
-
return info;
|
|
1480
|
+
return state.info;
|
|
1513
1481
|
}
|
|
1514
1482
|
catch (e) {
|
|
1515
|
-
|
|
1516
|
-
this.logger.error("verify element contains text failed " + info.log);
|
|
1517
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1518
|
-
info.screenshotPath = screenshotPath;
|
|
1519
|
-
Object.assign(e, { info: info });
|
|
1520
|
-
error = e;
|
|
1521
|
-
throw e;
|
|
1483
|
+
await _commandError(state, e, this);
|
|
1522
1484
|
}
|
|
1523
1485
|
finally {
|
|
1524
|
-
|
|
1525
|
-
this._reportToWorld(world, {
|
|
1526
|
-
element_name: selectors.element_name,
|
|
1527
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1528
|
-
text: `Verify element contains text: ${text}`,
|
|
1529
|
-
value: text,
|
|
1530
|
-
screenshotId: foundObj === null || foundObj === void 0 ? void 0 : foundObj.screenshotId,
|
|
1531
|
-
result: error
|
|
1532
|
-
? {
|
|
1533
|
-
status: "FAILED",
|
|
1534
|
-
startTime,
|
|
1535
|
-
endTime,
|
|
1536
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1537
|
-
}
|
|
1538
|
-
: {
|
|
1539
|
-
status: "PASSED",
|
|
1540
|
-
startTime,
|
|
1541
|
-
endTime,
|
|
1542
|
-
},
|
|
1543
|
-
info: info,
|
|
1544
|
-
});
|
|
1486
|
+
_commandFinally(state, this);
|
|
1545
1487
|
}
|
|
1546
1488
|
}
|
|
1547
1489
|
_getDataFile(world = null) {
|
|
@@ -1770,7 +1712,6 @@ class StableBrowser {
|
|
|
1770
1712
|
}
|
|
1771
1713
|
async takeScreenshot(screenshotPath) {
|
|
1772
1714
|
const playContext = this.context.playContext;
|
|
1773
|
-
const client = await playContext.newCDPSession(this.page);
|
|
1774
1715
|
// Using CDP to capture the screenshot
|
|
1775
1716
|
const viewportWidth = Math.max(...(await this.page.evaluate(() => [
|
|
1776
1717
|
document.body.scrollWidth,
|
|
@@ -1780,97 +1721,67 @@ class StableBrowser {
|
|
|
1780
1721
|
document.body.clientWidth,
|
|
1781
1722
|
document.documentElement.clientWidth,
|
|
1782
1723
|
])));
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
await client.detach();
|
|
1724
|
+
let screenshotBuffer = null;
|
|
1725
|
+
if (this.context.browserName === "chromium") {
|
|
1726
|
+
const client = await playContext.newCDPSession(this.page);
|
|
1727
|
+
const { data } = await client.send("Page.captureScreenshot", {
|
|
1728
|
+
format: "png",
|
|
1729
|
+
// clip: {
|
|
1730
|
+
// x: 0,
|
|
1731
|
+
// y: 0,
|
|
1732
|
+
// width: viewportWidth,
|
|
1733
|
+
// height: viewportHeight,
|
|
1734
|
+
// scale: 1,
|
|
1735
|
+
// },
|
|
1736
|
+
});
|
|
1737
|
+
await client.detach();
|
|
1738
|
+
if (!screenshotPath) {
|
|
1739
|
+
return data;
|
|
1740
|
+
}
|
|
1741
|
+
screenshotBuffer = Buffer.from(data, "base64");
|
|
1742
|
+
}
|
|
1743
|
+
else {
|
|
1744
|
+
screenshotBuffer = await this.page.screenshot();
|
|
1745
|
+
}
|
|
1746
|
+
let image = await Jimp.read(screenshotBuffer);
|
|
1747
|
+
// Get the image dimensions
|
|
1748
|
+
const { width, height } = image.bitmap;
|
|
1749
|
+
const resizeRatio = viewportWidth / width;
|
|
1750
|
+
// Resize the image to fit within the viewport dimensions without enlarging
|
|
1751
|
+
if (width > viewportWidth) {
|
|
1752
|
+
image = image.resize({ w: viewportWidth, h: height * resizeRatio }); // Resize the image while maintaining aspect ratio
|
|
1753
|
+
await image.write(screenshotPath);
|
|
1754
|
+
}
|
|
1755
|
+
else {
|
|
1756
|
+
fs.writeFileSync(screenshotPath, screenshotBuffer);
|
|
1757
|
+
}
|
|
1818
1758
|
}
|
|
1819
1759
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1760
|
+
const state = {
|
|
1761
|
+
selectors,
|
|
1762
|
+
_params,
|
|
1763
|
+
options,
|
|
1764
|
+
world,
|
|
1765
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1766
|
+
text: `Verify element exists in page`,
|
|
1767
|
+
operation: "verifyElementExistInPage",
|
|
1768
|
+
log: "***** verify element " + selectors.element_name + " exists in page *****\n",
|
|
1769
|
+
};
|
|
1825
1770
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1826
|
-
const info = {};
|
|
1827
|
-
info.log = "***** verify element " + selectors.element_name + " exists in page *****\n";
|
|
1828
|
-
info.operation = "verify";
|
|
1829
|
-
info.selectors = selectors;
|
|
1830
1771
|
try {
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
}
|
|
1835
|
-
await this._highlightElements(element);
|
|
1836
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1837
|
-
await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
1838
|
-
return info;
|
|
1772
|
+
await _preCommand(state, this);
|
|
1773
|
+
await expect(state.element).toHaveCount(1, { timeout: 10000 });
|
|
1774
|
+
return state.info;
|
|
1839
1775
|
}
|
|
1840
1776
|
catch (e) {
|
|
1841
|
-
|
|
1842
|
-
this.logger.error("verify failed " + info.log);
|
|
1843
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1844
|
-
info.screenshotPath = screenshotPath;
|
|
1845
|
-
Object.assign(e, { info: info });
|
|
1846
|
-
error = e;
|
|
1847
|
-
throw e;
|
|
1777
|
+
await _commandError(state, e, this);
|
|
1848
1778
|
}
|
|
1849
1779
|
finally {
|
|
1850
|
-
|
|
1851
|
-
this._reportToWorld(world, {
|
|
1852
|
-
element_name: selectors.element_name,
|
|
1853
|
-
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1854
|
-
text: "Verify element exists in page",
|
|
1855
|
-
screenshotId,
|
|
1856
|
-
result: error
|
|
1857
|
-
? {
|
|
1858
|
-
status: "FAILED",
|
|
1859
|
-
startTime,
|
|
1860
|
-
endTime,
|
|
1861
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
1862
|
-
}
|
|
1863
|
-
: {
|
|
1864
|
-
status: "PASSED",
|
|
1865
|
-
startTime,
|
|
1866
|
-
endTime,
|
|
1867
|
-
},
|
|
1868
|
-
info: info,
|
|
1869
|
-
});
|
|
1780
|
+
_commandFinally(state, this);
|
|
1870
1781
|
}
|
|
1871
1782
|
}
|
|
1872
1783
|
async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
|
|
1873
|
-
|
|
1784
|
+
_validateSelectors(selectors);
|
|
1874
1785
|
const startTime = Date.now();
|
|
1875
1786
|
let error = null;
|
|
1876
1787
|
let screenshotId = null;
|
|
@@ -1929,7 +1840,7 @@ class StableBrowser {
|
|
|
1929
1840
|
status: "FAILED",
|
|
1930
1841
|
startTime,
|
|
1931
1842
|
endTime,
|
|
1932
|
-
message: error
|
|
1843
|
+
message: error?.message,
|
|
1933
1844
|
}
|
|
1934
1845
|
: {
|
|
1935
1846
|
status: "PASSED",
|
|
@@ -2138,7 +2049,7 @@ class StableBrowser {
|
|
|
2138
2049
|
status: "FAILED",
|
|
2139
2050
|
startTime,
|
|
2140
2051
|
endTime,
|
|
2141
|
-
message: error
|
|
2052
|
+
message: error?.message,
|
|
2142
2053
|
}
|
|
2143
2054
|
: {
|
|
2144
2055
|
status: "PASSED",
|
|
@@ -2174,20 +2085,20 @@ class StableBrowser {
|
|
|
2174
2085
|
for (let i = 0; i < frames.length; i++) {
|
|
2175
2086
|
if (dateAlternatives.date) {
|
|
2176
2087
|
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2177
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, {});
|
|
2088
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, true, {});
|
|
2178
2089
|
result.frame = frames[i];
|
|
2179
2090
|
results.push(result);
|
|
2180
2091
|
}
|
|
2181
2092
|
}
|
|
2182
2093
|
else if (numberAlternatives.number) {
|
|
2183
2094
|
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2184
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, {});
|
|
2095
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, true, {});
|
|
2185
2096
|
result.frame = frames[i];
|
|
2186
2097
|
results.push(result);
|
|
2187
2098
|
}
|
|
2188
2099
|
}
|
|
2189
2100
|
else {
|
|
2190
|
-
const result = await this._locateElementByText(frames[i], text, "*", true, {});
|
|
2101
|
+
const result = await this._locateElementByText(frames[i], text, "*", true, true, {});
|
|
2191
2102
|
result.frame = frames[i];
|
|
2192
2103
|
results.push(result);
|
|
2193
2104
|
}
|
|
@@ -2236,7 +2147,7 @@ class StableBrowser {
|
|
|
2236
2147
|
status: "FAILED",
|
|
2237
2148
|
startTime,
|
|
2238
2149
|
endTime,
|
|
2239
|
-
message: error
|
|
2150
|
+
message: error?.message,
|
|
2240
2151
|
}
|
|
2241
2152
|
: {
|
|
2242
2153
|
status: "PASSED",
|
|
@@ -2320,7 +2231,7 @@ class StableBrowser {
|
|
|
2320
2231
|
status: "FAILED",
|
|
2321
2232
|
startTime,
|
|
2322
2233
|
endTime,
|
|
2323
|
-
message: error
|
|
2234
|
+
message: error?.message,
|
|
2324
2235
|
}
|
|
2325
2236
|
: {
|
|
2326
2237
|
status: "PASSED",
|
|
@@ -2352,7 +2263,7 @@ class StableBrowser {
|
|
|
2352
2263
|
this.logger.info("Table data verified");
|
|
2353
2264
|
}
|
|
2354
2265
|
async getTableData(selectors, _params = null, options = {}, world = null) {
|
|
2355
|
-
|
|
2266
|
+
_validateSelectors(selectors);
|
|
2356
2267
|
const startTime = Date.now();
|
|
2357
2268
|
let error = null;
|
|
2358
2269
|
let screenshotId = null;
|
|
@@ -2388,7 +2299,7 @@ class StableBrowser {
|
|
|
2388
2299
|
status: "FAILED",
|
|
2389
2300
|
startTime,
|
|
2390
2301
|
endTime,
|
|
2391
|
-
message: error
|
|
2302
|
+
message: error?.message,
|
|
2392
2303
|
}
|
|
2393
2304
|
: {
|
|
2394
2305
|
status: "PASSED",
|
|
@@ -2400,7 +2311,7 @@ class StableBrowser {
|
|
|
2400
2311
|
}
|
|
2401
2312
|
}
|
|
2402
2313
|
async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
|
|
2403
|
-
|
|
2314
|
+
_validateSelectors(selectors);
|
|
2404
2315
|
if (!query) {
|
|
2405
2316
|
throw new Error("query is null");
|
|
2406
2317
|
}
|
|
@@ -2553,7 +2464,7 @@ class StableBrowser {
|
|
|
2553
2464
|
status: "FAILED",
|
|
2554
2465
|
startTime,
|
|
2555
2466
|
endTime,
|
|
2556
|
-
message: error
|
|
2467
|
+
message: error?.message,
|
|
2557
2468
|
}
|
|
2558
2469
|
: {
|
|
2559
2470
|
status: "PASSED",
|
|
@@ -2565,27 +2476,7 @@ class StableBrowser {
|
|
|
2565
2476
|
}
|
|
2566
2477
|
}
|
|
2567
2478
|
async _replaceWithLocalData(value, world, _decrypt = true, totpWait = true) {
|
|
2568
|
-
|
|
2569
|
-
return value;
|
|
2570
|
-
}
|
|
2571
|
-
// find all the accurance of {{(.*?)}} and replace with the value
|
|
2572
|
-
let regex = /{{(.*?)}}/g;
|
|
2573
|
-
let matches = value.match(regex);
|
|
2574
|
-
if (matches) {
|
|
2575
|
-
const testData = this.getTestData(world);
|
|
2576
|
-
for (let i = 0; i < matches.length; i++) {
|
|
2577
|
-
let match = matches[i];
|
|
2578
|
-
let key = match.substring(2, match.length - 2);
|
|
2579
|
-
let newValue = objectPath.get(testData, key, null);
|
|
2580
|
-
if (newValue !== null) {
|
|
2581
|
-
value = value.replace(match, newValue);
|
|
2582
|
-
}
|
|
2583
|
-
}
|
|
2584
|
-
}
|
|
2585
|
-
if ((value.startsWith("secret:") || value.startsWith("totp:")) && _decrypt) {
|
|
2586
|
-
return await decrypt(value, null, totpWait);
|
|
2587
|
-
}
|
|
2588
|
-
return value;
|
|
2479
|
+
return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
|
|
2589
2480
|
}
|
|
2590
2481
|
_getLoadTimeout(options) {
|
|
2591
2482
|
let timeout = 15000;
|
|
@@ -2645,7 +2536,7 @@ class StableBrowser {
|
|
|
2645
2536
|
status: "FAILED",
|
|
2646
2537
|
startTime,
|
|
2647
2538
|
endTime,
|
|
2648
|
-
message: error
|
|
2539
|
+
message: error?.message,
|
|
2649
2540
|
}
|
|
2650
2541
|
: {
|
|
2651
2542
|
status: "PASSED",
|
|
@@ -2680,7 +2571,7 @@ class StableBrowser {
|
|
|
2680
2571
|
status: "FAILED",
|
|
2681
2572
|
startTime,
|
|
2682
2573
|
endTime,
|
|
2683
|
-
message: error
|
|
2574
|
+
message: error?.message,
|
|
2684
2575
|
}
|
|
2685
2576
|
: {
|
|
2686
2577
|
status: "PASSED",
|
|
@@ -2722,7 +2613,7 @@ class StableBrowser {
|
|
|
2722
2613
|
status: "FAILED",
|
|
2723
2614
|
startTime,
|
|
2724
2615
|
endTime,
|
|
2725
|
-
message: error
|
|
2616
|
+
message: error?.message,
|
|
2726
2617
|
}
|
|
2727
2618
|
: {
|
|
2728
2619
|
status: "PASSED",
|
|
@@ -2758,7 +2649,7 @@ class StableBrowser {
|
|
|
2758
2649
|
status: "FAILED",
|
|
2759
2650
|
startTime,
|
|
2760
2651
|
endTime,
|
|
2761
|
-
message: error
|
|
2652
|
+
message: error?.message,
|
|
2762
2653
|
}
|
|
2763
2654
|
: {
|
|
2764
2655
|
status: "PASSED",
|
|
@@ -2943,5 +2834,10 @@ const KEYBOARD_EVENTS = [
|
|
|
2943
2834
|
"TVAntennaCable",
|
|
2944
2835
|
"TVAudioDescription",
|
|
2945
2836
|
];
|
|
2837
|
+
function unEscapeString(str) {
|
|
2838
|
+
const placeholder = "__NEWLINE__";
|
|
2839
|
+
str = str.replace(new RegExp(placeholder, "g"), "\n");
|
|
2840
|
+
return str;
|
|
2841
|
+
}
|
|
2946
2842
|
export { StableBrowser };
|
|
2947
2843
|
//# sourceMappingURL=stable_browser.js.map
|