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