automation_model 1.0.437-dev → 1.0.437-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 +523 -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 +5 -4
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,26 @@ 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) {
|
|
85
105
|
context.pageLoading.status = true;
|
|
86
106
|
this.page = page;
|
|
87
107
|
context.page = page;
|
|
88
108
|
context.pages.push(page);
|
|
109
|
+
registerNetworkEvents(this.world, this, context, this.page);
|
|
110
|
+
registerDownloadEvent(this.page, this.world, context);
|
|
89
111
|
page.on("close", async () => {
|
|
90
112
|
if (this.context && this.context.pages && this.context.pages.length > 1) {
|
|
91
113
|
this.context.pages.pop();
|
|
@@ -110,6 +132,36 @@ class StableBrowser {
|
|
|
110
132
|
context.pageLoading.status = false;
|
|
111
133
|
}.bind(this));
|
|
112
134
|
}
|
|
135
|
+
async switchApp(appName) {
|
|
136
|
+
// check if the current app (this.appName) is the same as the new app
|
|
137
|
+
if (this.appName === appName) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
let navigate = false;
|
|
141
|
+
if (!apps[appName]) {
|
|
142
|
+
let newContext = await getContext(null, false, this.logger, appName, false, this);
|
|
143
|
+
navigate = true;
|
|
144
|
+
apps[appName] = {
|
|
145
|
+
context: newContext,
|
|
146
|
+
browser: newContext.browser,
|
|
147
|
+
page: newContext.page,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
const tempContext = {};
|
|
151
|
+
this._copyContext(this, tempContext);
|
|
152
|
+
this._copyContext(apps[appName], this);
|
|
153
|
+
apps[this.appName] = tempContext;
|
|
154
|
+
this.appName = appName;
|
|
155
|
+
if (navigate) {
|
|
156
|
+
await this.goto(this.context.environment.baseUrl);
|
|
157
|
+
await this.waitForPageLoad();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
_copyContext(from, to) {
|
|
161
|
+
to.browser = from.browser;
|
|
162
|
+
to.page = from.page;
|
|
163
|
+
to.context = from.context;
|
|
164
|
+
}
|
|
113
165
|
getWebLogFile(logFolder) {
|
|
114
166
|
if (!fs.existsSync(logFolder)) {
|
|
115
167
|
fs.mkdirSync(logFolder, { recursive: true });
|
|
@@ -121,37 +173,63 @@ class StableBrowser {
|
|
|
121
173
|
const fileName = nextIndex + ".json";
|
|
122
174
|
return path.join(logFolder, fileName);
|
|
123
175
|
}
|
|
124
|
-
registerConsoleLogListener(page, context
|
|
176
|
+
registerConsoleLogListener(page, context) {
|
|
125
177
|
if (!this.context.webLogger) {
|
|
126
178
|
this.context.webLogger = [];
|
|
127
179
|
}
|
|
128
180
|
page.on("console", async (msg) => {
|
|
129
|
-
|
|
181
|
+
const obj = {
|
|
130
182
|
type: msg.type(),
|
|
131
183
|
text: msg.text(),
|
|
132
184
|
location: msg.location(),
|
|
133
185
|
time: new Date().toISOString(),
|
|
134
|
-
}
|
|
135
|
-
|
|
186
|
+
};
|
|
187
|
+
this.context.webLogger.push(obj);
|
|
188
|
+
if (msg.type() === "error") {
|
|
189
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+log" });
|
|
190
|
+
}
|
|
136
191
|
});
|
|
137
192
|
}
|
|
138
|
-
registerRequestListener() {
|
|
139
|
-
this.
|
|
193
|
+
registerRequestListener(page, context, logFile) {
|
|
194
|
+
if (!this.context.networkLogger) {
|
|
195
|
+
this.context.networkLogger = [];
|
|
196
|
+
}
|
|
197
|
+
page.on("request", async (data) => {
|
|
198
|
+
const startTime = new Date().getTime();
|
|
140
199
|
try {
|
|
141
|
-
const pageUrl = new URL(
|
|
200
|
+
const pageUrl = new URL(page.url());
|
|
142
201
|
const requestUrl = new URL(data.url());
|
|
143
202
|
if (pageUrl.hostname === requestUrl.hostname) {
|
|
144
203
|
const method = data.method();
|
|
145
|
-
if (
|
|
204
|
+
if (["POST", "GET", "PUT", "DELETE", "PATCH"].includes(method)) {
|
|
146
205
|
const token = await data.headerValue("Authorization");
|
|
147
206
|
if (token) {
|
|
148
|
-
|
|
207
|
+
context.authtoken = token;
|
|
149
208
|
}
|
|
150
209
|
}
|
|
151
210
|
}
|
|
211
|
+
const response = await data.response();
|
|
212
|
+
const endTime = new Date().getTime();
|
|
213
|
+
const obj = {
|
|
214
|
+
url: data.url(),
|
|
215
|
+
method: data.method(),
|
|
216
|
+
postData: data.postData(),
|
|
217
|
+
error: data.failure() ? data.failure().errorText : null,
|
|
218
|
+
duration: endTime - startTime,
|
|
219
|
+
startTime,
|
|
220
|
+
};
|
|
221
|
+
context.networkLogger.push(obj);
|
|
222
|
+
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
|
|
152
223
|
}
|
|
153
224
|
catch (error) {
|
|
154
225
|
console.error("Error in request listener", error);
|
|
226
|
+
context.networkLogger.push({
|
|
227
|
+
error: "not able to listen",
|
|
228
|
+
message: error.message,
|
|
229
|
+
stack: error.stack,
|
|
230
|
+
time: new Date().toISOString(),
|
|
231
|
+
});
|
|
232
|
+
// await fs.promises.writeFile(logFile, JSON.stringify(context.networkLogger, null, 2));
|
|
155
233
|
}
|
|
156
234
|
});
|
|
157
235
|
}
|
|
@@ -166,20 +244,6 @@ class StableBrowser {
|
|
|
166
244
|
timeout: 60000,
|
|
167
245
|
});
|
|
168
246
|
}
|
|
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
247
|
_fixUsingParams(text, _params) {
|
|
184
248
|
if (!_params || typeof text !== "string") {
|
|
185
249
|
return text;
|
|
@@ -247,7 +311,7 @@ class StableBrowser {
|
|
|
247
311
|
locatorReturn = scope.getByRole(role, { name }, { exact: flags === "i" });
|
|
248
312
|
}
|
|
249
313
|
}
|
|
250
|
-
if (locator
|
|
314
|
+
if (locator?.engine) {
|
|
251
315
|
if (locator.engine === "css") {
|
|
252
316
|
locatorReturn = scope.locator(locator.selector);
|
|
253
317
|
}
|
|
@@ -268,7 +332,10 @@ class StableBrowser {
|
|
|
268
332
|
return locatorReturn;
|
|
269
333
|
}
|
|
270
334
|
async _locateElmentByTextClimbCss(scope, text, climb, css, _params) {
|
|
271
|
-
|
|
335
|
+
if (css && css.locator) {
|
|
336
|
+
css = css.locator;
|
|
337
|
+
}
|
|
338
|
+
let result = await this._locateElementByText(scope, this._fixUsingParams(text, _params), "*", false, false, _params);
|
|
272
339
|
if (result.elementCount === 0) {
|
|
273
340
|
return;
|
|
274
341
|
}
|
|
@@ -283,7 +350,7 @@ class StableBrowser {
|
|
|
283
350
|
}
|
|
284
351
|
async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
|
|
285
352
|
//const stringifyText = JSON.stringify(text);
|
|
286
|
-
return await scope.evaluate(([text, tag, regex, partial]) => {
|
|
353
|
+
return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
|
|
287
354
|
function isParent(parent, child) {
|
|
288
355
|
let currentNode = child.parentNode;
|
|
289
356
|
while (currentNode !== null) {
|
|
@@ -295,6 +362,15 @@ class StableBrowser {
|
|
|
295
362
|
return false;
|
|
296
363
|
}
|
|
297
364
|
document.isParent = isParent;
|
|
365
|
+
function getRegex(str) {
|
|
366
|
+
const match = str.match(/^\/(.*?)\/([gimuy]*)$/);
|
|
367
|
+
if (!match) {
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
let [_, pattern, flags] = match;
|
|
371
|
+
return new RegExp(pattern, flags);
|
|
372
|
+
}
|
|
373
|
+
document.getRegex = getRegex;
|
|
298
374
|
function collectAllShadowDomElements(element, result = []) {
|
|
299
375
|
// Check and add the element if it has a shadow root
|
|
300
376
|
if (element.shadowRoot) {
|
|
@@ -313,6 +389,10 @@ class StableBrowser {
|
|
|
313
389
|
if (!tag) {
|
|
314
390
|
tag = "*";
|
|
315
391
|
}
|
|
392
|
+
let regexpSearch = document.getRegex(text);
|
|
393
|
+
if (regexpSearch) {
|
|
394
|
+
regex = true;
|
|
395
|
+
}
|
|
316
396
|
let elements = Array.from(document.querySelectorAll(tag));
|
|
317
397
|
let shadowHosts = [];
|
|
318
398
|
document.collectAllShadowDomElements(document, shadowHosts);
|
|
@@ -328,7 +408,9 @@ class StableBrowser {
|
|
|
328
408
|
let randomToken = null;
|
|
329
409
|
const foundElements = [];
|
|
330
410
|
if (regex) {
|
|
331
|
-
|
|
411
|
+
if (!regexpSearch) {
|
|
412
|
+
regexpSearch = new RegExp(text, "im");
|
|
413
|
+
}
|
|
332
414
|
for (let i = 0; i < elements.length; i++) {
|
|
333
415
|
const element = elements[i];
|
|
334
416
|
if ((element.innerText && regexpSearch.test(element.innerText)) ||
|
|
@@ -342,8 +424,8 @@ class StableBrowser {
|
|
|
342
424
|
for (let i = 0; i < elements.length; i++) {
|
|
343
425
|
const element = elements[i];
|
|
344
426
|
if (partial) {
|
|
345
|
-
if ((element.innerText && element.innerText.trim().includes(text)) ||
|
|
346
|
-
(element.value && element.value.includes(text))) {
|
|
427
|
+
if ((element.innerText && element.innerText.toLowerCase().trim().includes(text.toLowerCase())) ||
|
|
428
|
+
(element.value && element.value.toLowerCase().includes(text.toLowerCase()))) {
|
|
347
429
|
foundElements.push(element);
|
|
348
430
|
}
|
|
349
431
|
}
|
|
@@ -388,6 +470,12 @@ class StableBrowser {
|
|
|
388
470
|
}
|
|
389
471
|
async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
|
|
390
472
|
let locatorSearch = selectorHierarchy[index];
|
|
473
|
+
try {
|
|
474
|
+
locatorSearch = JSON.parse(this._fixUsingParams(JSON.stringify(locatorSearch), _params));
|
|
475
|
+
}
|
|
476
|
+
catch (e) {
|
|
477
|
+
console.error(e);
|
|
478
|
+
}
|
|
391
479
|
//info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
|
|
392
480
|
let locator = null;
|
|
393
481
|
if (locatorSearch.climb && locatorSearch.climb >= 0) {
|
|
@@ -476,13 +564,18 @@ class StableBrowser {
|
|
|
476
564
|
if (result.foundElements.length > 0) {
|
|
477
565
|
let dialogCloseLocator = result.foundElements[0].locator;
|
|
478
566
|
await dialogCloseLocator.click();
|
|
567
|
+
// wait for the dialog to close
|
|
568
|
+
await dialogCloseLocator.waitFor({ state: "hidden" });
|
|
479
569
|
return { rerun: true };
|
|
480
570
|
}
|
|
481
571
|
}
|
|
482
572
|
}
|
|
483
573
|
return { rerun: false };
|
|
484
574
|
}
|
|
485
|
-
async _locate(selectors, info, _params, timeout
|
|
575
|
+
async _locate(selectors, info, _params, timeout) {
|
|
576
|
+
if (!timeout) {
|
|
577
|
+
timeout = 30000;
|
|
578
|
+
}
|
|
486
579
|
for (let i = 0; i < 3; i++) {
|
|
487
580
|
info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
|
|
488
581
|
for (let j = 0; j < selectors.locators.length; j++) {
|
|
@@ -496,32 +589,41 @@ class StableBrowser {
|
|
|
496
589
|
}
|
|
497
590
|
throw new Error("unable to locate element " + JSON.stringify(selectors));
|
|
498
591
|
}
|
|
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);
|
|
592
|
+
async _findFrameScope(selectors, timeout = 30000) {
|
|
505
593
|
let scope = this.page;
|
|
594
|
+
if (selectors.frame) {
|
|
595
|
+
return selectors.frame;
|
|
596
|
+
}
|
|
506
597
|
if (selectors.iframe_src || selectors.frameLocators) {
|
|
507
|
-
const findFrame = (frame, framescope) => {
|
|
598
|
+
const findFrame = async (frame, framescope) => {
|
|
508
599
|
for (let i = 0; i < frame.selectors.length; i++) {
|
|
509
600
|
let frameLocator = frame.selectors[i];
|
|
510
601
|
if (frameLocator.css) {
|
|
511
|
-
|
|
512
|
-
|
|
602
|
+
let testframescope = framescope.frameLocator(frameLocator.css);
|
|
603
|
+
if (frameLocator.index) {
|
|
604
|
+
testframescope = framescope.nth(frameLocator.index);
|
|
605
|
+
}
|
|
606
|
+
try {
|
|
607
|
+
await testframescope.owner().evaluateHandle(() => true, null, {
|
|
608
|
+
timeout: 5000,
|
|
609
|
+
});
|
|
610
|
+
framescope = testframescope;
|
|
611
|
+
break;
|
|
612
|
+
}
|
|
613
|
+
catch (error) {
|
|
614
|
+
console.error("frame not found " + frameLocator.css);
|
|
615
|
+
}
|
|
513
616
|
}
|
|
514
617
|
}
|
|
515
618
|
if (frame.children) {
|
|
516
|
-
return findFrame(frame.children, framescope);
|
|
619
|
+
return await findFrame(frame.children, framescope);
|
|
517
620
|
}
|
|
518
621
|
return framescope;
|
|
519
622
|
};
|
|
520
|
-
info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
|
|
521
623
|
while (true) {
|
|
522
624
|
let frameFound = false;
|
|
523
625
|
if (selectors.nestFrmLoc) {
|
|
524
|
-
scope = findFrame(selectors.nestFrmLoc, scope);
|
|
626
|
+
scope = await findFrame(selectors.nestFrmLoc, scope);
|
|
525
627
|
frameFound = true;
|
|
526
628
|
break;
|
|
527
629
|
}
|
|
@@ -550,6 +652,25 @@ class StableBrowser {
|
|
|
550
652
|
}
|
|
551
653
|
}
|
|
552
654
|
}
|
|
655
|
+
if (!scope) {
|
|
656
|
+
scope = this.page;
|
|
657
|
+
}
|
|
658
|
+
return scope;
|
|
659
|
+
}
|
|
660
|
+
async _getDocumentBody(selectors, timeout = 30000) {
|
|
661
|
+
let scope = await this._findFrameScope(selectors, timeout);
|
|
662
|
+
return scope.evaluate(() => {
|
|
663
|
+
var bodyContent = document.body.innerHTML;
|
|
664
|
+
return bodyContent;
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
668
|
+
let highPriorityTimeout = 5000;
|
|
669
|
+
let visibleOnlyTimeout = 6000;
|
|
670
|
+
let startTime = performance.now();
|
|
671
|
+
let locatorsCount = 0;
|
|
672
|
+
//let arrayMode = Array.isArray(selectors);
|
|
673
|
+
let scope = await this._findFrameScope(selectors, timeout);
|
|
553
674
|
let selectorsLocators = null;
|
|
554
675
|
selectorsLocators = selectors.locators;
|
|
555
676
|
// group selectors by priority
|
|
@@ -685,86 +806,121 @@ class StableBrowser {
|
|
|
685
806
|
}
|
|
686
807
|
return result;
|
|
687
808
|
}
|
|
688
|
-
async
|
|
689
|
-
this._validateSelectors(selectors);
|
|
809
|
+
async simpleClick(elementDescription, _params, options = {}, world = null) {
|
|
690
810
|
const startTime = Date.now();
|
|
691
|
-
|
|
692
|
-
|
|
811
|
+
let timeout = 30000;
|
|
812
|
+
if (options && options.timeout) {
|
|
813
|
+
timeout = options.timeout;
|
|
693
814
|
}
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
815
|
+
while (true) {
|
|
816
|
+
try {
|
|
817
|
+
const result = await locate_element(this.context, elementDescription, "click");
|
|
818
|
+
if (result?.elementNumber >= 0) {
|
|
819
|
+
const selectors = {
|
|
820
|
+
frame: result?.frame,
|
|
821
|
+
locators: [
|
|
822
|
+
{
|
|
823
|
+
css: result?.css,
|
|
824
|
+
},
|
|
825
|
+
],
|
|
826
|
+
};
|
|
827
|
+
await this.click(selectors, _params, options, world);
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
catch (e) {
|
|
832
|
+
if (performance.now() - startTime > timeout) {
|
|
833
|
+
throw e;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
|
|
840
|
+
const startTime = Date.now();
|
|
841
|
+
let timeout = 30000;
|
|
842
|
+
if (options && options.timeout) {
|
|
843
|
+
timeout = options.timeout;
|
|
844
|
+
}
|
|
845
|
+
while (true) {
|
|
846
|
+
try {
|
|
847
|
+
const result = await locate_element(this.context, elementDescription, "fill", value);
|
|
848
|
+
if (result?.elementNumber >= 0) {
|
|
849
|
+
const selectors = {
|
|
850
|
+
frame: result?.frame,
|
|
851
|
+
locators: [
|
|
852
|
+
{
|
|
853
|
+
css: result?.css,
|
|
854
|
+
},
|
|
855
|
+
],
|
|
856
|
+
};
|
|
857
|
+
await this.clickType(selectors, value, false, _params, options, world);
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
catch (e) {
|
|
862
|
+
if (performance.now() - startTime > timeout) {
|
|
863
|
+
throw e;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
async click(selectors, _params, options = {}, world = null) {
|
|
870
|
+
const state = {
|
|
871
|
+
selectors,
|
|
872
|
+
_params,
|
|
873
|
+
options,
|
|
874
|
+
world,
|
|
875
|
+
text: "Click element",
|
|
876
|
+
type: Types.CLICK,
|
|
877
|
+
operation: "click",
|
|
878
|
+
log: "***** click on " + selectors.element_name + " *****\n",
|
|
879
|
+
};
|
|
701
880
|
try {
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
881
|
+
await _preCommand(state, this);
|
|
882
|
+
if (state.options && state.options.context) {
|
|
883
|
+
state.selectors.locators[0].text = state.options.context;
|
|
884
|
+
}
|
|
705
885
|
try {
|
|
706
|
-
await
|
|
707
|
-
await element.click();
|
|
886
|
+
await state.element.click();
|
|
708
887
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
709
888
|
}
|
|
710
889
|
catch (e) {
|
|
711
890
|
// await this.closeUnexpectedPopups();
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
await element.dispatchEvent("click");
|
|
891
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
892
|
+
await state.element.dispatchEvent("click");
|
|
715
893
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
716
894
|
}
|
|
717
895
|
await this.waitForPageLoad();
|
|
718
|
-
return info;
|
|
896
|
+
return state.info;
|
|
719
897
|
}
|
|
720
898
|
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;
|
|
899
|
+
await _commandError(state, e, this);
|
|
727
900
|
}
|
|
728
901
|
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
|
-
});
|
|
902
|
+
_commandFinally(state, this);
|
|
749
903
|
}
|
|
750
904
|
}
|
|
751
905
|
async setCheck(selectors, checked = true, _params, options = {}, world = null) {
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
906
|
+
const state = {
|
|
907
|
+
selectors,
|
|
908
|
+
_params,
|
|
909
|
+
options,
|
|
910
|
+
world,
|
|
911
|
+
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
912
|
+
text: checked ? `Check element` : `Uncheck element`,
|
|
913
|
+
operation: "setCheck",
|
|
914
|
+
log: "***** check " + selectors.element_name + " *****\n",
|
|
915
|
+
};
|
|
762
916
|
try {
|
|
763
|
-
|
|
764
|
-
|
|
917
|
+
await _preCommand(state, this);
|
|
918
|
+
state.info.checked = checked;
|
|
919
|
+
// let element = await this._locate(selectors, info, _params);
|
|
920
|
+
// ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
765
921
|
try {
|
|
766
|
-
await this._highlightElements(element);
|
|
767
|
-
await element.setChecked(checked);
|
|
922
|
+
// await this._highlightElements(element);
|
|
923
|
+
await state.element.setChecked(checked);
|
|
768
924
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
769
925
|
}
|
|
770
926
|
catch (e) {
|
|
@@ -773,179 +929,108 @@ class StableBrowser {
|
|
|
773
929
|
}
|
|
774
930
|
else {
|
|
775
931
|
//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 });
|
|
932
|
+
state.info.log += "setCheck failed, will try again" + "\n";
|
|
933
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
934
|
+
await state.element.setChecked(checked, { timeout: 5000, force: true });
|
|
779
935
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
780
936
|
}
|
|
781
937
|
}
|
|
782
938
|
await this.waitForPageLoad();
|
|
783
|
-
return info;
|
|
939
|
+
return state.info;
|
|
784
940
|
}
|
|
785
941
|
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;
|
|
942
|
+
await _commandError(state, e, this);
|
|
792
943
|
}
|
|
793
944
|
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
|
-
});
|
|
945
|
+
_commandFinally(state, this);
|
|
814
946
|
}
|
|
815
947
|
}
|
|
816
948
|
async hover(selectors, _params, options = {}, world = null) {
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
949
|
+
const state = {
|
|
950
|
+
selectors,
|
|
951
|
+
_params,
|
|
952
|
+
options,
|
|
953
|
+
world,
|
|
954
|
+
type: Types.HOVER,
|
|
955
|
+
text: `Hover element`,
|
|
956
|
+
operation: "hover",
|
|
957
|
+
log: "***** hover " + selectors.element_name + " *****\n",
|
|
958
|
+
};
|
|
826
959
|
try {
|
|
827
|
-
|
|
828
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
960
|
+
await _preCommand(state, this);
|
|
829
961
|
try {
|
|
830
|
-
await
|
|
831
|
-
await element.hover();
|
|
962
|
+
await state.element.hover();
|
|
832
963
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
833
964
|
}
|
|
834
965
|
catch (e) {
|
|
835
966
|
//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 });
|
|
967
|
+
state.info.log += "hover failed, will try again" + "\n";
|
|
968
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
969
|
+
await state.element.hover({ timeout: 10000 });
|
|
839
970
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
840
971
|
}
|
|
841
972
|
await this.waitForPageLoad();
|
|
842
|
-
return info;
|
|
973
|
+
return state.info;
|
|
843
974
|
}
|
|
844
975
|
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;
|
|
976
|
+
await _commandError(state, e, this);
|
|
851
977
|
}
|
|
852
978
|
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
|
-
});
|
|
979
|
+
_commandFinally(state, this);
|
|
873
980
|
}
|
|
874
981
|
}
|
|
875
982
|
async selectOption(selectors, values, _params = null, options = {}, world = null) {
|
|
876
|
-
this._validateSelectors(selectors);
|
|
877
983
|
if (!values) {
|
|
878
984
|
throw new Error("values is null");
|
|
879
985
|
}
|
|
880
|
-
const
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
986
|
+
const state = {
|
|
987
|
+
selectors,
|
|
988
|
+
_params,
|
|
989
|
+
options,
|
|
990
|
+
world,
|
|
991
|
+
value: values.toString(),
|
|
992
|
+
type: Types.SELECT,
|
|
993
|
+
text: `Select option: ${values}`,
|
|
994
|
+
operation: "selectOption",
|
|
995
|
+
log: "***** select option " + selectors.element_name + " *****\n",
|
|
996
|
+
};
|
|
888
997
|
try {
|
|
889
|
-
|
|
890
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
998
|
+
await _preCommand(state, this);
|
|
891
999
|
try {
|
|
892
|
-
await
|
|
893
|
-
await element.selectOption(values);
|
|
1000
|
+
await state.element.selectOption(values);
|
|
894
1001
|
}
|
|
895
1002
|
catch (e) {
|
|
896
1003
|
//await this.closeUnexpectedPopups();
|
|
897
|
-
info.log += "selectOption failed, will try force" + "\n";
|
|
898
|
-
await element.selectOption(values, { timeout: 10000, force: true });
|
|
1004
|
+
state.info.log += "selectOption failed, will try force" + "\n";
|
|
1005
|
+
await state.element.selectOption(values, { timeout: 10000, force: true });
|
|
899
1006
|
}
|
|
900
1007
|
await this.waitForPageLoad();
|
|
901
|
-
return info;
|
|
1008
|
+
return state.info;
|
|
902
1009
|
}
|
|
903
1010
|
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;
|
|
1011
|
+
await _commandError(state, e, this);
|
|
911
1012
|
}
|
|
912
1013
|
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
|
-
});
|
|
1014
|
+
_commandFinally(state, this);
|
|
934
1015
|
}
|
|
935
1016
|
}
|
|
936
1017
|
async type(_value, _params = null, options = {}, world = null) {
|
|
937
|
-
const
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1018
|
+
const state = {
|
|
1019
|
+
value: _value,
|
|
1020
|
+
_params,
|
|
1021
|
+
options,
|
|
1022
|
+
world,
|
|
1023
|
+
locate: false,
|
|
1024
|
+
scroll: false,
|
|
1025
|
+
highlight: false,
|
|
1026
|
+
type: Types.TYPE_PRESS,
|
|
1027
|
+
text: `Type value: ${_value}`,
|
|
1028
|
+
operation: "type",
|
|
1029
|
+
log: "",
|
|
1030
|
+
};
|
|
946
1031
|
try {
|
|
947
|
-
|
|
948
|
-
const valueSegment =
|
|
1032
|
+
await _preCommand(state, this);
|
|
1033
|
+
const valueSegment = state.value.split("&&");
|
|
949
1034
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
950
1035
|
if (i > 0) {
|
|
951
1036
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -965,108 +1050,53 @@ class StableBrowser {
|
|
|
965
1050
|
await this.page.keyboard.type(value);
|
|
966
1051
|
}
|
|
967
1052
|
}
|
|
968
|
-
return info;
|
|
1053
|
+
return state.info;
|
|
969
1054
|
}
|
|
970
1055
|
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;
|
|
1056
|
+
await _commandError(state, e, this);
|
|
978
1057
|
}
|
|
979
1058
|
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
|
-
});
|
|
1059
|
+
_commandFinally(state, this);
|
|
1000
1060
|
}
|
|
1001
1061
|
}
|
|
1002
1062
|
async setInputValue(selectors, value, _params = null, options = {}, world = null) {
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
let screenshotPath = null;
|
|
1063
|
+
const state = {
|
|
1064
|
+
selectors,
|
|
1065
|
+
_params,
|
|
1066
|
+
value,
|
|
1067
|
+
options,
|
|
1068
|
+
world,
|
|
1069
|
+
type: Types.SET_INPUT,
|
|
1070
|
+
text: `Set input value`,
|
|
1071
|
+
operation: "setInputValue",
|
|
1072
|
+
log: "***** set input value " + selectors.element_name + " *****\n",
|
|
1073
|
+
};
|
|
1015
1074
|
try {
|
|
1016
|
-
|
|
1017
|
-
let
|
|
1018
|
-
await this.scrollIfNeeded(element, info);
|
|
1019
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1020
|
-
await this._highlightElements(element);
|
|
1075
|
+
await _preCommand(state, this);
|
|
1076
|
+
let value = await this._replaceWithLocalData(state.value, this);
|
|
1021
1077
|
try {
|
|
1022
|
-
await element.evaluateHandle((el, value) => {
|
|
1078
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1023
1079
|
el.value = value;
|
|
1024
1080
|
}, value);
|
|
1025
1081
|
}
|
|
1026
1082
|
catch (error) {
|
|
1027
1083
|
this.logger.error("setInputValue failed, will try again");
|
|
1028
|
-
|
|
1029
|
-
info.
|
|
1030
|
-
|
|
1031
|
-
await element.evaluateHandle((el, value) => {
|
|
1084
|
+
await _screenshot(state, this);
|
|
1085
|
+
Object.assign(error, { info: state.info });
|
|
1086
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1032
1087
|
el.value = value;
|
|
1033
1088
|
});
|
|
1034
1089
|
}
|
|
1035
1090
|
}
|
|
1036
1091
|
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;
|
|
1092
|
+
await _commandError(state, e, this);
|
|
1043
1093
|
}
|
|
1044
1094
|
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
|
-
});
|
|
1095
|
+
_commandFinally(state, this);
|
|
1066
1096
|
}
|
|
1067
1097
|
}
|
|
1068
1098
|
async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
|
|
1069
|
-
|
|
1099
|
+
_validateSelectors(selectors);
|
|
1070
1100
|
const startTime = Date.now();
|
|
1071
1101
|
let error = null;
|
|
1072
1102
|
let screenshotId = null;
|
|
@@ -1159,32 +1189,32 @@ class StableBrowser {
|
|
|
1159
1189
|
}
|
|
1160
1190
|
}
|
|
1161
1191
|
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;
|
|
1192
|
+
_value = unEscapeString(_value);
|
|
1171
1193
|
const newValue = await this._replaceWithLocalData(_value, world);
|
|
1194
|
+
const state = {
|
|
1195
|
+
selectors,
|
|
1196
|
+
_params,
|
|
1197
|
+
value: newValue,
|
|
1198
|
+
originalValue: _value,
|
|
1199
|
+
options,
|
|
1200
|
+
world,
|
|
1201
|
+
type: Types.FILL,
|
|
1202
|
+
text: `Click type input with value: ${_value}`,
|
|
1203
|
+
operation: "clickType",
|
|
1204
|
+
log: "***** clickType on " + selectors.element_name + " with value " + maskValue(_value) + "*****\n",
|
|
1205
|
+
};
|
|
1172
1206
|
if (newValue !== _value) {
|
|
1173
1207
|
//this.logger.info(_value + "=" + newValue);
|
|
1174
1208
|
_value = newValue;
|
|
1175
1209
|
}
|
|
1176
|
-
info.value = _value;
|
|
1177
1210
|
try {
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
await this.scrollIfNeeded(element, info);
|
|
1181
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1182
|
-
await this._highlightElements(element);
|
|
1211
|
+
await _preCommand(state, this);
|
|
1212
|
+
state.info.value = _value;
|
|
1183
1213
|
if (options === null || options === undefined || !options.press) {
|
|
1184
1214
|
try {
|
|
1185
|
-
let currentValue = await element.inputValue();
|
|
1215
|
+
let currentValue = await state.element.inputValue();
|
|
1186
1216
|
if (currentValue) {
|
|
1187
|
-
await element.fill("");
|
|
1217
|
+
await state.element.fill("");
|
|
1188
1218
|
}
|
|
1189
1219
|
}
|
|
1190
1220
|
catch (e) {
|
|
@@ -1193,22 +1223,22 @@ class StableBrowser {
|
|
|
1193
1223
|
}
|
|
1194
1224
|
if (options === null || options === undefined || options.press) {
|
|
1195
1225
|
try {
|
|
1196
|
-
await element.click({ timeout: 5000 });
|
|
1226
|
+
await state.element.click({ timeout: 5000 });
|
|
1197
1227
|
}
|
|
1198
1228
|
catch (e) {
|
|
1199
|
-
await element.dispatchEvent("click");
|
|
1229
|
+
await state.element.dispatchEvent("click");
|
|
1200
1230
|
}
|
|
1201
1231
|
}
|
|
1202
1232
|
else {
|
|
1203
1233
|
try {
|
|
1204
|
-
await element.focus();
|
|
1234
|
+
await state.element.focus();
|
|
1205
1235
|
}
|
|
1206
1236
|
catch (e) {
|
|
1207
|
-
await element.dispatchEvent("focus");
|
|
1237
|
+
await state.element.dispatchEvent("focus");
|
|
1208
1238
|
}
|
|
1209
1239
|
}
|
|
1210
1240
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1211
|
-
const valueSegment =
|
|
1241
|
+
const valueSegment = state.value.split("&&");
|
|
1212
1242
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1213
1243
|
if (i > 0) {
|
|
1214
1244
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1228,13 +1258,14 @@ class StableBrowser {
|
|
|
1228
1258
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1229
1259
|
}
|
|
1230
1260
|
}
|
|
1261
|
+
await _screenshot(state, this);
|
|
1231
1262
|
if (enter === true) {
|
|
1232
1263
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1233
1264
|
await this.page.keyboard.press("Enter");
|
|
1234
1265
|
await this.waitForPageLoad();
|
|
1235
1266
|
}
|
|
1236
1267
|
else if (enter === false) {
|
|
1237
|
-
await element.dispatchEvent("change");
|
|
1268
|
+
await state.element.dispatchEvent("change");
|
|
1238
1269
|
//await this.page.keyboard.press("Tab");
|
|
1239
1270
|
}
|
|
1240
1271
|
else {
|
|
@@ -1243,103 +1274,50 @@ class StableBrowser {
|
|
|
1243
1274
|
await this.waitForPageLoad();
|
|
1244
1275
|
}
|
|
1245
1276
|
}
|
|
1246
|
-
return info;
|
|
1277
|
+
return state.info;
|
|
1247
1278
|
}
|
|
1248
1279
|
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;
|
|
1280
|
+
await _commandError(state, e, this);
|
|
1256
1281
|
}
|
|
1257
1282
|
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
|
-
});
|
|
1283
|
+
_commandFinally(state, this);
|
|
1279
1284
|
}
|
|
1280
1285
|
}
|
|
1281
1286
|
async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1287
|
+
const state = {
|
|
1288
|
+
selectors,
|
|
1289
|
+
_params,
|
|
1290
|
+
value: unEscapeString(value),
|
|
1291
|
+
options,
|
|
1292
|
+
world,
|
|
1293
|
+
type: Types.FILL,
|
|
1294
|
+
text: `Fill input with value: ${value}`,
|
|
1295
|
+
operation: "fill",
|
|
1296
|
+
log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
|
|
1297
|
+
};
|
|
1292
1298
|
try {
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
await
|
|
1296
|
-
await element.fill(value);
|
|
1297
|
-
await element.dispatchEvent("change");
|
|
1299
|
+
await _preCommand(state, this);
|
|
1300
|
+
await state.element.fill(value);
|
|
1301
|
+
await state.element.dispatchEvent("change");
|
|
1298
1302
|
if (enter) {
|
|
1299
1303
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1300
1304
|
await this.page.keyboard.press("Enter");
|
|
1301
1305
|
}
|
|
1302
1306
|
await this.waitForPageLoad();
|
|
1303
|
-
return info;
|
|
1307
|
+
return state.info;
|
|
1304
1308
|
}
|
|
1305
1309
|
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;
|
|
1310
|
+
await _commandError(state, e, this);
|
|
1313
1311
|
}
|
|
1314
1312
|
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
|
-
});
|
|
1313
|
+
_commandFinally(state, this);
|
|
1336
1314
|
}
|
|
1337
1315
|
}
|
|
1338
1316
|
async getText(selectors, _params = null, options = {}, info = {}, world = null) {
|
|
1339
1317
|
return await this._getText(selectors, 0, _params, options, info, world);
|
|
1340
1318
|
}
|
|
1341
1319
|
async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
|
|
1342
|
-
|
|
1320
|
+
_validateSelectors(selectors);
|
|
1343
1321
|
let screenshotId = null;
|
|
1344
1322
|
let screenshotPath = null;
|
|
1345
1323
|
if (!info.log) {
|
|
@@ -1383,165 +1361,124 @@ class StableBrowser {
|
|
|
1383
1361
|
}
|
|
1384
1362
|
}
|
|
1385
1363
|
async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
|
|
1386
|
-
var _a;
|
|
1387
|
-
this._validateSelectors(selectors);
|
|
1388
1364
|
if (!pattern) {
|
|
1389
1365
|
throw new Error("pattern is null");
|
|
1390
1366
|
}
|
|
1391
1367
|
if (!text) {
|
|
1392
1368
|
throw new Error("text is null");
|
|
1393
1369
|
}
|
|
1370
|
+
const state = {
|
|
1371
|
+
selectors,
|
|
1372
|
+
_params,
|
|
1373
|
+
pattern,
|
|
1374
|
+
value: pattern,
|
|
1375
|
+
options,
|
|
1376
|
+
world,
|
|
1377
|
+
locate: false,
|
|
1378
|
+
scroll: false,
|
|
1379
|
+
screenshot: false,
|
|
1380
|
+
highlight: false,
|
|
1381
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1382
|
+
text: `Verify element contains pattern: ${pattern}`,
|
|
1383
|
+
operation: "containsPattern",
|
|
1384
|
+
log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
|
|
1385
|
+
};
|
|
1394
1386
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1395
1387
|
if (newValue !== text) {
|
|
1396
1388
|
this.logger.info(text + "=" + newValue);
|
|
1397
1389
|
text = newValue;
|
|
1398
1390
|
}
|
|
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
1391
|
let foundObj = null;
|
|
1411
1392
|
try {
|
|
1412
|
-
|
|
1393
|
+
await _preCommand(state, this);
|
|
1394
|
+
state.info.pattern = pattern;
|
|
1395
|
+
foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
|
|
1413
1396
|
if (foundObj && foundObj.element) {
|
|
1414
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1397
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1415
1398
|
}
|
|
1416
|
-
|
|
1399
|
+
await _screenshot(state, this);
|
|
1417
1400
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1418
1401
|
pattern = pattern.replace("{text}", escapedText);
|
|
1419
1402
|
let regex = new RegExp(pattern, "im");
|
|
1420
|
-
if (!regex.test(foundObj
|
|
1421
|
-
info.foundText = foundObj
|
|
1403
|
+
if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
|
|
1404
|
+
state.info.foundText = foundObj?.text;
|
|
1422
1405
|
throw new Error("element doesn't contain text " + text);
|
|
1423
1406
|
}
|
|
1424
|
-
return info;
|
|
1407
|
+
return state.info;
|
|
1425
1408
|
}
|
|
1426
1409
|
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;
|
|
1410
|
+
this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
|
|
1411
|
+
await _commandError(state, e, this);
|
|
1435
1412
|
}
|
|
1436
1413
|
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
|
-
});
|
|
1414
|
+
_commandFinally(state, this);
|
|
1458
1415
|
}
|
|
1459
1416
|
}
|
|
1460
1417
|
async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
|
|
1461
|
-
|
|
1462
|
-
|
|
1418
|
+
const state = {
|
|
1419
|
+
selectors,
|
|
1420
|
+
_params,
|
|
1421
|
+
value: text,
|
|
1422
|
+
options,
|
|
1423
|
+
world,
|
|
1424
|
+
locate: false,
|
|
1425
|
+
scroll: false,
|
|
1426
|
+
screenshot: false,
|
|
1427
|
+
highlight: false,
|
|
1428
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1429
|
+
text: `Verify element contains text: ${text}`,
|
|
1430
|
+
operation: "containsText",
|
|
1431
|
+
log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
|
|
1432
|
+
};
|
|
1463
1433
|
if (!text) {
|
|
1464
1434
|
throw new Error("text is null");
|
|
1465
1435
|
}
|
|
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;
|
|
1436
|
+
text = unEscapeString(text);
|
|
1474
1437
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1475
1438
|
if (newValue !== text) {
|
|
1476
1439
|
this.logger.info(text + "=" + newValue);
|
|
1477
1440
|
text = newValue;
|
|
1478
1441
|
}
|
|
1479
|
-
info.value = text;
|
|
1480
1442
|
let foundObj = null;
|
|
1481
1443
|
try {
|
|
1482
|
-
|
|
1444
|
+
await _preCommand(state, this);
|
|
1445
|
+
foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
|
|
1483
1446
|
if (foundObj && foundObj.element) {
|
|
1484
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1447
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1485
1448
|
}
|
|
1486
|
-
|
|
1449
|
+
await _screenshot(state, this);
|
|
1487
1450
|
const dateAlternatives = findDateAlternatives(text);
|
|
1488
1451
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1489
1452
|
if (dateAlternatives.date) {
|
|
1490
1453
|
for (let i = 0; i < dateAlternatives.dates.length; i++) {
|
|
1491
|
-
if (
|
|
1492
|
-
|
|
1493
|
-
return info;
|
|
1454
|
+
if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
|
|
1455
|
+
foundObj?.value?.includes(dateAlternatives.dates[i])) {
|
|
1456
|
+
return state.info;
|
|
1494
1457
|
}
|
|
1495
1458
|
}
|
|
1496
1459
|
throw new Error("element doesn't contain text " + text);
|
|
1497
1460
|
}
|
|
1498
1461
|
else if (numberAlternatives.number) {
|
|
1499
1462
|
for (let i = 0; i < numberAlternatives.numbers.length; i++) {
|
|
1500
|
-
if (
|
|
1501
|
-
|
|
1502
|
-
return info;
|
|
1463
|
+
if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
|
|
1464
|
+
foundObj?.value?.includes(numberAlternatives.numbers[i])) {
|
|
1465
|
+
return state.info;
|
|
1503
1466
|
}
|
|
1504
1467
|
}
|
|
1505
1468
|
throw new Error("element doesn't contain text " + text);
|
|
1506
1469
|
}
|
|
1507
|
-
else if (!
|
|
1508
|
-
info.foundText = foundObj
|
|
1509
|
-
info.value = foundObj
|
|
1470
|
+
else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
|
|
1471
|
+
state.info.foundText = foundObj?.text;
|
|
1472
|
+
state.info.value = foundObj?.value;
|
|
1510
1473
|
throw new Error("element doesn't contain text " + text);
|
|
1511
1474
|
}
|
|
1512
|
-
return info;
|
|
1475
|
+
return state.info;
|
|
1513
1476
|
}
|
|
1514
1477
|
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;
|
|
1478
|
+
await _commandError(state, e, this);
|
|
1522
1479
|
}
|
|
1523
1480
|
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
|
-
});
|
|
1481
|
+
_commandFinally(state, this);
|
|
1545
1482
|
}
|
|
1546
1483
|
}
|
|
1547
1484
|
_getDataFile(world = null) {
|
|
@@ -1770,7 +1707,6 @@ class StableBrowser {
|
|
|
1770
1707
|
}
|
|
1771
1708
|
async takeScreenshot(screenshotPath) {
|
|
1772
1709
|
const playContext = this.context.playContext;
|
|
1773
|
-
const client = await playContext.newCDPSession(this.page);
|
|
1774
1710
|
// Using CDP to capture the screenshot
|
|
1775
1711
|
const viewportWidth = Math.max(...(await this.page.evaluate(() => [
|
|
1776
1712
|
document.body.scrollWidth,
|
|
@@ -1780,97 +1716,67 @@ class StableBrowser {
|
|
|
1780
1716
|
document.body.clientWidth,
|
|
1781
1717
|
document.documentElement.clientWidth,
|
|
1782
1718
|
])));
|
|
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();
|
|
1719
|
+
let screenshotBuffer = null;
|
|
1720
|
+
if (this.context.browserName === "chromium") {
|
|
1721
|
+
const client = await playContext.newCDPSession(this.page);
|
|
1722
|
+
const { data } = await client.send("Page.captureScreenshot", {
|
|
1723
|
+
format: "png",
|
|
1724
|
+
// clip: {
|
|
1725
|
+
// x: 0,
|
|
1726
|
+
// y: 0,
|
|
1727
|
+
// width: viewportWidth,
|
|
1728
|
+
// height: viewportHeight,
|
|
1729
|
+
// scale: 1,
|
|
1730
|
+
// },
|
|
1731
|
+
});
|
|
1732
|
+
await client.detach();
|
|
1733
|
+
if (!screenshotPath) {
|
|
1734
|
+
return data;
|
|
1735
|
+
}
|
|
1736
|
+
screenshotBuffer = Buffer.from(data, "base64");
|
|
1737
|
+
}
|
|
1738
|
+
else {
|
|
1739
|
+
screenshotBuffer = await this.page.screenshot();
|
|
1740
|
+
}
|
|
1741
|
+
let image = await Jimp.read(screenshotBuffer);
|
|
1742
|
+
// Get the image dimensions
|
|
1743
|
+
const { width, height } = image.bitmap;
|
|
1744
|
+
const resizeRatio = viewportWidth / width;
|
|
1745
|
+
// Resize the image to fit within the viewport dimensions without enlarging
|
|
1746
|
+
if (width > viewportWidth) {
|
|
1747
|
+
image = image.resize({ w: viewportWidth, h: height * resizeRatio }); // Resize the image while maintaining aspect ratio
|
|
1748
|
+
await image.write(screenshotPath);
|
|
1749
|
+
}
|
|
1750
|
+
else {
|
|
1751
|
+
fs.writeFileSync(screenshotPath, screenshotBuffer);
|
|
1752
|
+
}
|
|
1818
1753
|
}
|
|
1819
1754
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1755
|
+
const state = {
|
|
1756
|
+
selectors,
|
|
1757
|
+
_params,
|
|
1758
|
+
options,
|
|
1759
|
+
world,
|
|
1760
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1761
|
+
text: `Verify element exists in page`,
|
|
1762
|
+
operation: "verifyElementExistInPage",
|
|
1763
|
+
log: "***** verify element " + selectors.element_name + " exists in page *****\n",
|
|
1764
|
+
};
|
|
1825
1765
|
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
1766
|
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;
|
|
1767
|
+
await _preCommand(state, this);
|
|
1768
|
+
await expect(state.element).toHaveCount(1, { timeout: 10000 });
|
|
1769
|
+
return state.info;
|
|
1839
1770
|
}
|
|
1840
1771
|
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;
|
|
1772
|
+
await _commandError(state, e, this);
|
|
1848
1773
|
}
|
|
1849
1774
|
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
|
-
});
|
|
1775
|
+
_commandFinally(state, this);
|
|
1870
1776
|
}
|
|
1871
1777
|
}
|
|
1872
1778
|
async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
|
|
1873
|
-
|
|
1779
|
+
_validateSelectors(selectors);
|
|
1874
1780
|
const startTime = Date.now();
|
|
1875
1781
|
let error = null;
|
|
1876
1782
|
let screenshotId = null;
|
|
@@ -1929,7 +1835,7 @@ class StableBrowser {
|
|
|
1929
1835
|
status: "FAILED",
|
|
1930
1836
|
startTime,
|
|
1931
1837
|
endTime,
|
|
1932
|
-
message: error
|
|
1838
|
+
message: error?.message,
|
|
1933
1839
|
}
|
|
1934
1840
|
: {
|
|
1935
1841
|
status: "PASSED",
|
|
@@ -2138,7 +2044,7 @@ class StableBrowser {
|
|
|
2138
2044
|
status: "FAILED",
|
|
2139
2045
|
startTime,
|
|
2140
2046
|
endTime,
|
|
2141
|
-
message: error
|
|
2047
|
+
message: error?.message,
|
|
2142
2048
|
}
|
|
2143
2049
|
: {
|
|
2144
2050
|
status: "PASSED",
|
|
@@ -2174,20 +2080,20 @@ class StableBrowser {
|
|
|
2174
2080
|
for (let i = 0; i < frames.length; i++) {
|
|
2175
2081
|
if (dateAlternatives.date) {
|
|
2176
2082
|
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2177
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, {});
|
|
2083
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, true, {});
|
|
2178
2084
|
result.frame = frames[i];
|
|
2179
2085
|
results.push(result);
|
|
2180
2086
|
}
|
|
2181
2087
|
}
|
|
2182
2088
|
else if (numberAlternatives.number) {
|
|
2183
2089
|
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2184
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, {});
|
|
2090
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, true, {});
|
|
2185
2091
|
result.frame = frames[i];
|
|
2186
2092
|
results.push(result);
|
|
2187
2093
|
}
|
|
2188
2094
|
}
|
|
2189
2095
|
else {
|
|
2190
|
-
const result = await this._locateElementByText(frames[i], text, "*", true, {});
|
|
2096
|
+
const result = await this._locateElementByText(frames[i], text, "*", true, true, {});
|
|
2191
2097
|
result.frame = frames[i];
|
|
2192
2098
|
results.push(result);
|
|
2193
2099
|
}
|
|
@@ -2236,7 +2142,7 @@ class StableBrowser {
|
|
|
2236
2142
|
status: "FAILED",
|
|
2237
2143
|
startTime,
|
|
2238
2144
|
endTime,
|
|
2239
|
-
message: error
|
|
2145
|
+
message: error?.message,
|
|
2240
2146
|
}
|
|
2241
2147
|
: {
|
|
2242
2148
|
status: "PASSED",
|
|
@@ -2320,7 +2226,7 @@ class StableBrowser {
|
|
|
2320
2226
|
status: "FAILED",
|
|
2321
2227
|
startTime,
|
|
2322
2228
|
endTime,
|
|
2323
|
-
message: error
|
|
2229
|
+
message: error?.message,
|
|
2324
2230
|
}
|
|
2325
2231
|
: {
|
|
2326
2232
|
status: "PASSED",
|
|
@@ -2352,7 +2258,7 @@ class StableBrowser {
|
|
|
2352
2258
|
this.logger.info("Table data verified");
|
|
2353
2259
|
}
|
|
2354
2260
|
async getTableData(selectors, _params = null, options = {}, world = null) {
|
|
2355
|
-
|
|
2261
|
+
_validateSelectors(selectors);
|
|
2356
2262
|
const startTime = Date.now();
|
|
2357
2263
|
let error = null;
|
|
2358
2264
|
let screenshotId = null;
|
|
@@ -2388,7 +2294,7 @@ class StableBrowser {
|
|
|
2388
2294
|
status: "FAILED",
|
|
2389
2295
|
startTime,
|
|
2390
2296
|
endTime,
|
|
2391
|
-
message: error
|
|
2297
|
+
message: error?.message,
|
|
2392
2298
|
}
|
|
2393
2299
|
: {
|
|
2394
2300
|
status: "PASSED",
|
|
@@ -2400,7 +2306,7 @@ class StableBrowser {
|
|
|
2400
2306
|
}
|
|
2401
2307
|
}
|
|
2402
2308
|
async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
|
|
2403
|
-
|
|
2309
|
+
_validateSelectors(selectors);
|
|
2404
2310
|
if (!query) {
|
|
2405
2311
|
throw new Error("query is null");
|
|
2406
2312
|
}
|
|
@@ -2553,7 +2459,7 @@ class StableBrowser {
|
|
|
2553
2459
|
status: "FAILED",
|
|
2554
2460
|
startTime,
|
|
2555
2461
|
endTime,
|
|
2556
|
-
message: error
|
|
2462
|
+
message: error?.message,
|
|
2557
2463
|
}
|
|
2558
2464
|
: {
|
|
2559
2465
|
status: "PASSED",
|
|
@@ -2565,27 +2471,7 @@ class StableBrowser {
|
|
|
2565
2471
|
}
|
|
2566
2472
|
}
|
|
2567
2473
|
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;
|
|
2474
|
+
return await replaceWithLocalTestData(value, world, _decrypt, totpWait, this.context, this);
|
|
2589
2475
|
}
|
|
2590
2476
|
_getLoadTimeout(options) {
|
|
2591
2477
|
let timeout = 15000;
|
|
@@ -2645,7 +2531,7 @@ class StableBrowser {
|
|
|
2645
2531
|
status: "FAILED",
|
|
2646
2532
|
startTime,
|
|
2647
2533
|
endTime,
|
|
2648
|
-
message: error
|
|
2534
|
+
message: error?.message,
|
|
2649
2535
|
}
|
|
2650
2536
|
: {
|
|
2651
2537
|
status: "PASSED",
|
|
@@ -2680,7 +2566,7 @@ class StableBrowser {
|
|
|
2680
2566
|
status: "FAILED",
|
|
2681
2567
|
startTime,
|
|
2682
2568
|
endTime,
|
|
2683
|
-
message: error
|
|
2569
|
+
message: error?.message,
|
|
2684
2570
|
}
|
|
2685
2571
|
: {
|
|
2686
2572
|
status: "PASSED",
|
|
@@ -2722,7 +2608,7 @@ class StableBrowser {
|
|
|
2722
2608
|
status: "FAILED",
|
|
2723
2609
|
startTime,
|
|
2724
2610
|
endTime,
|
|
2725
|
-
message: error
|
|
2611
|
+
message: error?.message,
|
|
2726
2612
|
}
|
|
2727
2613
|
: {
|
|
2728
2614
|
status: "PASSED",
|
|
@@ -2758,7 +2644,7 @@ class StableBrowser {
|
|
|
2758
2644
|
status: "FAILED",
|
|
2759
2645
|
startTime,
|
|
2760
2646
|
endTime,
|
|
2761
|
-
message: error
|
|
2647
|
+
message: error?.message,
|
|
2762
2648
|
}
|
|
2763
2649
|
: {
|
|
2764
2650
|
status: "PASSED",
|
|
@@ -2943,5 +2829,10 @@ const KEYBOARD_EVENTS = [
|
|
|
2943
2829
|
"TVAntennaCable",
|
|
2944
2830
|
"TVAudioDescription",
|
|
2945
2831
|
];
|
|
2832
|
+
function unEscapeString(str) {
|
|
2833
|
+
const placeholder = "__NEWLINE__";
|
|
2834
|
+
str = str.replace(new RegExp(placeholder, "g"), "\n");
|
|
2835
|
+
return str;
|
|
2836
|
+
}
|
|
2946
2837
|
export { StableBrowser };
|
|
2947
2838
|
//# sourceMappingURL=stable_browser.js.map
|