automation_model 1.0.431-dev → 1.0.431-stage
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api.d.ts +42 -1
- package/lib/api.js +199 -13
- package/lib/api.js.map +1 -1
- package/lib/auto_page.js +1 -24
- package/lib/auto_page.js.map +1 -1
- package/lib/axe/axe.mini.js +12 -0
- package/lib/browser_manager.js +8 -3
- package/lib/browser_manager.js.map +1 -1
- package/lib/command_common.d.ts +5 -0
- package/lib/command_common.js +107 -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 +553 -605
- 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,108 @@ 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
|
+
value: values.toString(),
|
|
988
|
+
type: Types.SELECT,
|
|
989
|
+
text: `Select option: ${values}`,
|
|
990
|
+
operation: "selectOption",
|
|
991
|
+
log: "***** select option " + selectors.element_name + " *****\n",
|
|
992
|
+
};
|
|
869
993
|
try {
|
|
870
|
-
|
|
871
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
994
|
+
await _preCommand(state, this);
|
|
872
995
|
try {
|
|
873
|
-
await
|
|
874
|
-
await element.selectOption(values);
|
|
996
|
+
await state.element.selectOption(values);
|
|
875
997
|
}
|
|
876
998
|
catch (e) {
|
|
877
999
|
//await this.closeUnexpectedPopups();
|
|
878
|
-
info.log += "selectOption failed, will try force" + "\n";
|
|
879
|
-
await element.selectOption(values, { timeout: 10000, force: true });
|
|
1000
|
+
state.info.log += "selectOption failed, will try force" + "\n";
|
|
1001
|
+
await state.element.selectOption(values, { timeout: 10000, force: true });
|
|
880
1002
|
}
|
|
881
1003
|
await this.waitForPageLoad();
|
|
882
|
-
return info;
|
|
1004
|
+
return state.info;
|
|
883
1005
|
}
|
|
884
1006
|
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;
|
|
1007
|
+
await _commandError(state, e, this);
|
|
892
1008
|
}
|
|
893
1009
|
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
|
-
});
|
|
1010
|
+
_commandFinally(state, this);
|
|
915
1011
|
}
|
|
916
1012
|
}
|
|
917
1013
|
async type(_value, _params = null, options = {}, world = null) {
|
|
918
|
-
const
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
1014
|
+
const state = {
|
|
1015
|
+
value: _value,
|
|
1016
|
+
_params,
|
|
1017
|
+
options,
|
|
1018
|
+
world,
|
|
1019
|
+
locate: false,
|
|
1020
|
+
scroll: false,
|
|
1021
|
+
highlight: false,
|
|
1022
|
+
type: Types.TYPE_PRESS,
|
|
1023
|
+
text: `Type value: ${_value}`,
|
|
1024
|
+
operation: "type",
|
|
1025
|
+
log: "",
|
|
1026
|
+
};
|
|
927
1027
|
try {
|
|
928
|
-
|
|
929
|
-
const valueSegment =
|
|
1028
|
+
await _preCommand(state, this);
|
|
1029
|
+
const valueSegment = state.value.split("&&");
|
|
930
1030
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
931
1031
|
if (i > 0) {
|
|
932
1032
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -946,108 +1046,53 @@ class StableBrowser {
|
|
|
946
1046
|
await this.page.keyboard.type(value);
|
|
947
1047
|
}
|
|
948
1048
|
}
|
|
949
|
-
return info;
|
|
1049
|
+
return state.info;
|
|
950
1050
|
}
|
|
951
1051
|
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;
|
|
1052
|
+
await _commandError(state, e, this);
|
|
959
1053
|
}
|
|
960
1054
|
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
|
-
});
|
|
1055
|
+
_commandFinally(state, this);
|
|
981
1056
|
}
|
|
982
1057
|
}
|
|
983
1058
|
async setInputValue(selectors, value, _params = null, options = {}, world = null) {
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
let screenshotPath = null;
|
|
1059
|
+
const state = {
|
|
1060
|
+
selectors,
|
|
1061
|
+
_params,
|
|
1062
|
+
value,
|
|
1063
|
+
options,
|
|
1064
|
+
world,
|
|
1065
|
+
type: Types.SET_INPUT,
|
|
1066
|
+
text: `Set input value`,
|
|
1067
|
+
operation: "setInputValue",
|
|
1068
|
+
log: "***** set input value " + selectors.element_name + " *****\n",
|
|
1069
|
+
};
|
|
996
1070
|
try {
|
|
997
|
-
|
|
998
|
-
let
|
|
999
|
-
await this.scrollIfNeeded(element, info);
|
|
1000
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1001
|
-
await this._highlightElements(element);
|
|
1071
|
+
await _preCommand(state, this);
|
|
1072
|
+
let value = await this._replaceWithLocalData(state.value, this);
|
|
1002
1073
|
try {
|
|
1003
|
-
await element.evaluateHandle((el, value) => {
|
|
1074
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1004
1075
|
el.value = value;
|
|
1005
1076
|
}, value);
|
|
1006
1077
|
}
|
|
1007
1078
|
catch (error) {
|
|
1008
1079
|
this.logger.error("setInputValue failed, will try again");
|
|
1009
|
-
|
|
1010
|
-
info.
|
|
1011
|
-
|
|
1012
|
-
await element.evaluateHandle((el, value) => {
|
|
1080
|
+
await _screenshot(state, this);
|
|
1081
|
+
Object.assign(error, { info: state.info });
|
|
1082
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1013
1083
|
el.value = value;
|
|
1014
1084
|
});
|
|
1015
1085
|
}
|
|
1016
1086
|
}
|
|
1017
1087
|
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;
|
|
1088
|
+
await _commandError(state, e, this);
|
|
1024
1089
|
}
|
|
1025
1090
|
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
|
-
});
|
|
1091
|
+
_commandFinally(state, this);
|
|
1047
1092
|
}
|
|
1048
1093
|
}
|
|
1049
1094
|
async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
|
|
1050
|
-
|
|
1095
|
+
_validateSelectors(selectors);
|
|
1051
1096
|
const startTime = Date.now();
|
|
1052
1097
|
let error = null;
|
|
1053
1098
|
let screenshotId = null;
|
|
@@ -1140,32 +1185,31 @@ class StableBrowser {
|
|
|
1140
1185
|
}
|
|
1141
1186
|
}
|
|
1142
1187
|
async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1188
|
+
const state = {
|
|
1189
|
+
selectors,
|
|
1190
|
+
_params,
|
|
1191
|
+
value: unEscapeString(_value),
|
|
1192
|
+
options,
|
|
1193
|
+
world,
|
|
1194
|
+
type: Types.FILL,
|
|
1195
|
+
text: `Click type input with value: ${_value}`,
|
|
1196
|
+
operation: "clickType",
|
|
1197
|
+
log: "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n",
|
|
1198
|
+
};
|
|
1199
|
+
const newValue = await this._replaceWithLocalData(state.value, world);
|
|
1153
1200
|
if (newValue !== _value) {
|
|
1154
1201
|
//this.logger.info(_value + "=" + newValue);
|
|
1155
1202
|
_value = newValue;
|
|
1203
|
+
state.value = newValue;
|
|
1156
1204
|
}
|
|
1157
|
-
info.value = _value;
|
|
1158
1205
|
try {
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
await this.scrollIfNeeded(element, info);
|
|
1162
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1163
|
-
await this._highlightElements(element);
|
|
1206
|
+
await _preCommand(state, this);
|
|
1207
|
+
state.info.value = _value;
|
|
1164
1208
|
if (options === null || options === undefined || !options.press) {
|
|
1165
1209
|
try {
|
|
1166
|
-
let currentValue = await element.inputValue();
|
|
1210
|
+
let currentValue = await state.element.inputValue();
|
|
1167
1211
|
if (currentValue) {
|
|
1168
|
-
await element.fill("");
|
|
1212
|
+
await state.element.fill("");
|
|
1169
1213
|
}
|
|
1170
1214
|
}
|
|
1171
1215
|
catch (e) {
|
|
@@ -1174,22 +1218,22 @@ class StableBrowser {
|
|
|
1174
1218
|
}
|
|
1175
1219
|
if (options === null || options === undefined || options.press) {
|
|
1176
1220
|
try {
|
|
1177
|
-
await element.click({ timeout: 5000 });
|
|
1221
|
+
await state.element.click({ timeout: 5000 });
|
|
1178
1222
|
}
|
|
1179
1223
|
catch (e) {
|
|
1180
|
-
await element.dispatchEvent("click");
|
|
1224
|
+
await state.element.dispatchEvent("click");
|
|
1181
1225
|
}
|
|
1182
1226
|
}
|
|
1183
1227
|
else {
|
|
1184
1228
|
try {
|
|
1185
|
-
await element.focus();
|
|
1229
|
+
await state.element.focus();
|
|
1186
1230
|
}
|
|
1187
1231
|
catch (e) {
|
|
1188
|
-
await element.dispatchEvent("focus");
|
|
1232
|
+
await state.element.dispatchEvent("focus");
|
|
1189
1233
|
}
|
|
1190
1234
|
}
|
|
1191
1235
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1192
|
-
const valueSegment =
|
|
1236
|
+
const valueSegment = state.value.split("&&");
|
|
1193
1237
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1194
1238
|
if (i > 0) {
|
|
1195
1239
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1209,13 +1253,14 @@ class StableBrowser {
|
|
|
1209
1253
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1210
1254
|
}
|
|
1211
1255
|
}
|
|
1256
|
+
await _screenshot(state, this);
|
|
1212
1257
|
if (enter === true) {
|
|
1213
1258
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1214
1259
|
await this.page.keyboard.press("Enter");
|
|
1215
1260
|
await this.waitForPageLoad();
|
|
1216
1261
|
}
|
|
1217
1262
|
else if (enter === false) {
|
|
1218
|
-
await element.dispatchEvent("change");
|
|
1263
|
+
await state.element.dispatchEvent("change");
|
|
1219
1264
|
//await this.page.keyboard.press("Tab");
|
|
1220
1265
|
}
|
|
1221
1266
|
else {
|
|
@@ -1224,103 +1269,50 @@ class StableBrowser {
|
|
|
1224
1269
|
await this.waitForPageLoad();
|
|
1225
1270
|
}
|
|
1226
1271
|
}
|
|
1227
|
-
return info;
|
|
1272
|
+
return state.info;
|
|
1228
1273
|
}
|
|
1229
1274
|
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;
|
|
1275
|
+
await _commandError(state, e, this);
|
|
1237
1276
|
}
|
|
1238
1277
|
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
|
-
});
|
|
1278
|
+
_commandFinally(state, this);
|
|
1260
1279
|
}
|
|
1261
1280
|
}
|
|
1262
1281
|
async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1282
|
+
const state = {
|
|
1283
|
+
selectors,
|
|
1284
|
+
_params,
|
|
1285
|
+
value: unEscapeString(value),
|
|
1286
|
+
options,
|
|
1287
|
+
world,
|
|
1288
|
+
type: Types.FILL,
|
|
1289
|
+
text: `Fill input with value: ${value}`,
|
|
1290
|
+
operation: "fill",
|
|
1291
|
+
log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
|
|
1292
|
+
};
|
|
1273
1293
|
try {
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
await
|
|
1277
|
-
await element.fill(value);
|
|
1278
|
-
await element.dispatchEvent("change");
|
|
1294
|
+
await _preCommand(state, this);
|
|
1295
|
+
await state.element.fill(value);
|
|
1296
|
+
await state.element.dispatchEvent("change");
|
|
1279
1297
|
if (enter) {
|
|
1280
1298
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1281
1299
|
await this.page.keyboard.press("Enter");
|
|
1282
1300
|
}
|
|
1283
1301
|
await this.waitForPageLoad();
|
|
1284
|
-
return info;
|
|
1302
|
+
return state.info;
|
|
1285
1303
|
}
|
|
1286
1304
|
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;
|
|
1305
|
+
await _commandError(state, e, this);
|
|
1294
1306
|
}
|
|
1295
1307
|
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
|
-
});
|
|
1308
|
+
_commandFinally(state, this);
|
|
1317
1309
|
}
|
|
1318
1310
|
}
|
|
1319
1311
|
async getText(selectors, _params = null, options = {}, info = {}, world = null) {
|
|
1320
1312
|
return await this._getText(selectors, 0, _params, options, info, world);
|
|
1321
1313
|
}
|
|
1322
1314
|
async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
|
|
1323
|
-
|
|
1315
|
+
_validateSelectors(selectors);
|
|
1324
1316
|
let screenshotId = null;
|
|
1325
1317
|
let screenshotPath = null;
|
|
1326
1318
|
if (!info.log) {
|
|
@@ -1364,165 +1356,124 @@ class StableBrowser {
|
|
|
1364
1356
|
}
|
|
1365
1357
|
}
|
|
1366
1358
|
async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
|
|
1367
|
-
var _a;
|
|
1368
|
-
this._validateSelectors(selectors);
|
|
1369
1359
|
if (!pattern) {
|
|
1370
1360
|
throw new Error("pattern is null");
|
|
1371
1361
|
}
|
|
1372
1362
|
if (!text) {
|
|
1373
1363
|
throw new Error("text is null");
|
|
1374
1364
|
}
|
|
1365
|
+
const state = {
|
|
1366
|
+
selectors,
|
|
1367
|
+
_params,
|
|
1368
|
+
pattern,
|
|
1369
|
+
value: pattern,
|
|
1370
|
+
options,
|
|
1371
|
+
world,
|
|
1372
|
+
locate: false,
|
|
1373
|
+
scroll: false,
|
|
1374
|
+
screenshot: false,
|
|
1375
|
+
highlight: false,
|
|
1376
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1377
|
+
text: `Verify element contains pattern: ${pattern}`,
|
|
1378
|
+
operation: "containsPattern",
|
|
1379
|
+
log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
|
|
1380
|
+
};
|
|
1375
1381
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1376
1382
|
if (newValue !== text) {
|
|
1377
1383
|
this.logger.info(text + "=" + newValue);
|
|
1378
1384
|
text = newValue;
|
|
1379
1385
|
}
|
|
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
1386
|
let foundObj = null;
|
|
1392
1387
|
try {
|
|
1393
|
-
|
|
1388
|
+
await _preCommand(state, this);
|
|
1389
|
+
state.info.pattern = pattern;
|
|
1390
|
+
foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
|
|
1394
1391
|
if (foundObj && foundObj.element) {
|
|
1395
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1392
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1396
1393
|
}
|
|
1397
|
-
|
|
1394
|
+
await _screenshot(state, this);
|
|
1398
1395
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1399
1396
|
pattern = pattern.replace("{text}", escapedText);
|
|
1400
1397
|
let regex = new RegExp(pattern, "im");
|
|
1401
|
-
if (!regex.test(foundObj
|
|
1402
|
-
info.foundText = foundObj
|
|
1398
|
+
if (!regex.test(foundObj?.text) && !foundObj?.value?.includes(text)) {
|
|
1399
|
+
state.info.foundText = foundObj?.text;
|
|
1403
1400
|
throw new Error("element doesn't contain text " + text);
|
|
1404
1401
|
}
|
|
1405
|
-
return info;
|
|
1402
|
+
return state.info;
|
|
1406
1403
|
}
|
|
1407
1404
|
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;
|
|
1405
|
+
this.logger.error("found text " + foundObj?.text + " pattern " + pattern);
|
|
1406
|
+
await _commandError(state, e, this);
|
|
1416
1407
|
}
|
|
1417
1408
|
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
|
-
});
|
|
1409
|
+
_commandFinally(state, this);
|
|
1439
1410
|
}
|
|
1440
1411
|
}
|
|
1441
1412
|
async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
|
|
1442
|
-
|
|
1443
|
-
|
|
1413
|
+
const state = {
|
|
1414
|
+
selectors,
|
|
1415
|
+
_params,
|
|
1416
|
+
value: text,
|
|
1417
|
+
options,
|
|
1418
|
+
world,
|
|
1419
|
+
locate: false,
|
|
1420
|
+
scroll: false,
|
|
1421
|
+
screenshot: false,
|
|
1422
|
+
highlight: false,
|
|
1423
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1424
|
+
text: `Verify element contains text: ${text}`,
|
|
1425
|
+
operation: "containsText",
|
|
1426
|
+
log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
|
|
1427
|
+
};
|
|
1444
1428
|
if (!text) {
|
|
1445
1429
|
throw new Error("text is null");
|
|
1446
1430
|
}
|
|
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;
|
|
1431
|
+
text = unEscapeString(text);
|
|
1455
1432
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1456
1433
|
if (newValue !== text) {
|
|
1457
1434
|
this.logger.info(text + "=" + newValue);
|
|
1458
1435
|
text = newValue;
|
|
1459
1436
|
}
|
|
1460
|
-
info.value = text;
|
|
1461
1437
|
let foundObj = null;
|
|
1462
1438
|
try {
|
|
1463
|
-
|
|
1439
|
+
await _preCommand(state, this);
|
|
1440
|
+
foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
|
|
1464
1441
|
if (foundObj && foundObj.element) {
|
|
1465
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1442
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1466
1443
|
}
|
|
1467
|
-
|
|
1444
|
+
await _screenshot(state, this);
|
|
1468
1445
|
const dateAlternatives = findDateAlternatives(text);
|
|
1469
1446
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1470
1447
|
if (dateAlternatives.date) {
|
|
1471
1448
|
for (let i = 0; i < dateAlternatives.dates.length; i++) {
|
|
1472
|
-
if (
|
|
1473
|
-
|
|
1474
|
-
return info;
|
|
1449
|
+
if (foundObj?.text.includes(dateAlternatives.dates[i]) ||
|
|
1450
|
+
foundObj?.value?.includes(dateAlternatives.dates[i])) {
|
|
1451
|
+
return state.info;
|
|
1475
1452
|
}
|
|
1476
1453
|
}
|
|
1477
1454
|
throw new Error("element doesn't contain text " + text);
|
|
1478
1455
|
}
|
|
1479
1456
|
else if (numberAlternatives.number) {
|
|
1480
1457
|
for (let i = 0; i < numberAlternatives.numbers.length; i++) {
|
|
1481
|
-
if (
|
|
1482
|
-
|
|
1483
|
-
return info;
|
|
1458
|
+
if (foundObj?.text.includes(numberAlternatives.numbers[i]) ||
|
|
1459
|
+
foundObj?.value?.includes(numberAlternatives.numbers[i])) {
|
|
1460
|
+
return state.info;
|
|
1484
1461
|
}
|
|
1485
1462
|
}
|
|
1486
1463
|
throw new Error("element doesn't contain text " + text);
|
|
1487
1464
|
}
|
|
1488
|
-
else if (!
|
|
1489
|
-
info.foundText = foundObj
|
|
1490
|
-
info.value = foundObj
|
|
1465
|
+
else if (!foundObj?.text.includes(text) && !foundObj?.value?.includes(text)) {
|
|
1466
|
+
state.info.foundText = foundObj?.text;
|
|
1467
|
+
state.info.value = foundObj?.value;
|
|
1491
1468
|
throw new Error("element doesn't contain text " + text);
|
|
1492
1469
|
}
|
|
1493
|
-
return info;
|
|
1470
|
+
return state.info;
|
|
1494
1471
|
}
|
|
1495
1472
|
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;
|
|
1473
|
+
await _commandError(state, e, this);
|
|
1503
1474
|
}
|
|
1504
1475
|
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
|
-
});
|
|
1476
|
+
_commandFinally(state, this);
|
|
1526
1477
|
}
|
|
1527
1478
|
}
|
|
1528
1479
|
_getDataFile(world = null) {
|
|
@@ -1541,6 +1492,29 @@ class StableBrowser {
|
|
|
1541
1492
|
}
|
|
1542
1493
|
return dataFile;
|
|
1543
1494
|
}
|
|
1495
|
+
async waitForUserInput(message, world = null) {
|
|
1496
|
+
if (!message) {
|
|
1497
|
+
message = "# Wait for user input. Press any key to continue";
|
|
1498
|
+
}
|
|
1499
|
+
else {
|
|
1500
|
+
message = "# Wait for user input. " + message;
|
|
1501
|
+
}
|
|
1502
|
+
message += "\n";
|
|
1503
|
+
const value = await new Promise((resolve) => {
|
|
1504
|
+
const rl = readline.createInterface({
|
|
1505
|
+
input: process.stdin,
|
|
1506
|
+
output: process.stdout,
|
|
1507
|
+
});
|
|
1508
|
+
rl.question(message, (answer) => {
|
|
1509
|
+
rl.close();
|
|
1510
|
+
resolve(answer);
|
|
1511
|
+
});
|
|
1512
|
+
});
|
|
1513
|
+
if (value) {
|
|
1514
|
+
this.logger.info(`{{userInput}} was set to: ${value}`);
|
|
1515
|
+
}
|
|
1516
|
+
this.setTestData({ userInput: value }, world);
|
|
1517
|
+
}
|
|
1544
1518
|
setTestData(testData, world = null) {
|
|
1545
1519
|
if (!testData) {
|
|
1546
1520
|
return;
|
|
@@ -1728,7 +1702,6 @@ class StableBrowser {
|
|
|
1728
1702
|
}
|
|
1729
1703
|
async takeScreenshot(screenshotPath) {
|
|
1730
1704
|
const playContext = this.context.playContext;
|
|
1731
|
-
const client = await playContext.newCDPSession(this.page);
|
|
1732
1705
|
// Using CDP to capture the screenshot
|
|
1733
1706
|
const viewportWidth = Math.max(...(await this.page.evaluate(() => [
|
|
1734
1707
|
document.body.scrollWidth,
|
|
@@ -1738,97 +1711,67 @@ class StableBrowser {
|
|
|
1738
1711
|
document.body.clientWidth,
|
|
1739
1712
|
document.documentElement.clientWidth,
|
|
1740
1713
|
])));
|
|
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();
|
|
1714
|
+
let screenshotBuffer = null;
|
|
1715
|
+
if (this.context.browserName === "chromium") {
|
|
1716
|
+
const client = await playContext.newCDPSession(this.page);
|
|
1717
|
+
const { data } = await client.send("Page.captureScreenshot", {
|
|
1718
|
+
format: "png",
|
|
1719
|
+
// clip: {
|
|
1720
|
+
// x: 0,
|
|
1721
|
+
// y: 0,
|
|
1722
|
+
// width: viewportWidth,
|
|
1723
|
+
// height: viewportHeight,
|
|
1724
|
+
// scale: 1,
|
|
1725
|
+
// },
|
|
1726
|
+
});
|
|
1727
|
+
await client.detach();
|
|
1728
|
+
if (!screenshotPath) {
|
|
1729
|
+
return data;
|
|
1730
|
+
}
|
|
1731
|
+
screenshotBuffer = Buffer.from(data, "base64");
|
|
1732
|
+
}
|
|
1733
|
+
else {
|
|
1734
|
+
screenshotBuffer = await this.page.screenshot();
|
|
1735
|
+
}
|
|
1736
|
+
let image = await Jimp.read(screenshotBuffer);
|
|
1737
|
+
// Get the image dimensions
|
|
1738
|
+
const { width, height } = image.bitmap;
|
|
1739
|
+
const resizeRatio = viewportWidth / width;
|
|
1740
|
+
// Resize the image to fit within the viewport dimensions without enlarging
|
|
1741
|
+
if (width > viewportWidth) {
|
|
1742
|
+
image = image.resize({ w: viewportWidth, h: height * resizeRatio }); // Resize the image while maintaining aspect ratio
|
|
1743
|
+
await image.write(screenshotPath);
|
|
1744
|
+
}
|
|
1745
|
+
else {
|
|
1746
|
+
fs.writeFileSync(screenshotPath, screenshotBuffer);
|
|
1747
|
+
}
|
|
1776
1748
|
}
|
|
1777
1749
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1750
|
+
const state = {
|
|
1751
|
+
selectors,
|
|
1752
|
+
_params,
|
|
1753
|
+
options,
|
|
1754
|
+
world,
|
|
1755
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1756
|
+
text: `Verify element exists in page`,
|
|
1757
|
+
operation: "verifyElementExistInPage",
|
|
1758
|
+
log: "***** verify element " + selectors.element_name + " exists in page *****\n",
|
|
1759
|
+
};
|
|
1783
1760
|
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
1761
|
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;
|
|
1762
|
+
await _preCommand(state, this);
|
|
1763
|
+
await expect(state.element).toHaveCount(1, { timeout: 10000 });
|
|
1764
|
+
return state.info;
|
|
1797
1765
|
}
|
|
1798
1766
|
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;
|
|
1767
|
+
await _commandError(state, e, this);
|
|
1806
1768
|
}
|
|
1807
1769
|
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
|
-
});
|
|
1770
|
+
_commandFinally(state, this);
|
|
1828
1771
|
}
|
|
1829
1772
|
}
|
|
1830
1773
|
async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
|
|
1831
|
-
|
|
1774
|
+
_validateSelectors(selectors);
|
|
1832
1775
|
const startTime = Date.now();
|
|
1833
1776
|
let error = null;
|
|
1834
1777
|
let screenshotId = null;
|
|
@@ -1887,7 +1830,7 @@ class StableBrowser {
|
|
|
1887
1830
|
status: "FAILED",
|
|
1888
1831
|
startTime,
|
|
1889
1832
|
endTime,
|
|
1890
|
-
message: error
|
|
1833
|
+
message: error?.message,
|
|
1891
1834
|
}
|
|
1892
1835
|
: {
|
|
1893
1836
|
status: "PASSED",
|
|
@@ -2096,7 +2039,7 @@ class StableBrowser {
|
|
|
2096
2039
|
status: "FAILED",
|
|
2097
2040
|
startTime,
|
|
2098
2041
|
endTime,
|
|
2099
|
-
message: error
|
|
2042
|
+
message: error?.message,
|
|
2100
2043
|
}
|
|
2101
2044
|
: {
|
|
2102
2045
|
status: "PASSED",
|
|
@@ -2132,20 +2075,20 @@ class StableBrowser {
|
|
|
2132
2075
|
for (let i = 0; i < frames.length; i++) {
|
|
2133
2076
|
if (dateAlternatives.date) {
|
|
2134
2077
|
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2135
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, {});
|
|
2078
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, true, {});
|
|
2136
2079
|
result.frame = frames[i];
|
|
2137
2080
|
results.push(result);
|
|
2138
2081
|
}
|
|
2139
2082
|
}
|
|
2140
2083
|
else if (numberAlternatives.number) {
|
|
2141
2084
|
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2142
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, {});
|
|
2085
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, true, {});
|
|
2143
2086
|
result.frame = frames[i];
|
|
2144
2087
|
results.push(result);
|
|
2145
2088
|
}
|
|
2146
2089
|
}
|
|
2147
2090
|
else {
|
|
2148
|
-
const result = await this._locateElementByText(frames[i], text, "*", true, {});
|
|
2091
|
+
const result = await this._locateElementByText(frames[i], text, "*", true, true, {});
|
|
2149
2092
|
result.frame = frames[i];
|
|
2150
2093
|
results.push(result);
|
|
2151
2094
|
}
|
|
@@ -2194,7 +2137,7 @@ class StableBrowser {
|
|
|
2194
2137
|
status: "FAILED",
|
|
2195
2138
|
startTime,
|
|
2196
2139
|
endTime,
|
|
2197
|
-
message: error
|
|
2140
|
+
message: error?.message,
|
|
2198
2141
|
}
|
|
2199
2142
|
: {
|
|
2200
2143
|
status: "PASSED",
|
|
@@ -2278,7 +2221,7 @@ class StableBrowser {
|
|
|
2278
2221
|
status: "FAILED",
|
|
2279
2222
|
startTime,
|
|
2280
2223
|
endTime,
|
|
2281
|
-
message: error
|
|
2224
|
+
message: error?.message,
|
|
2282
2225
|
}
|
|
2283
2226
|
: {
|
|
2284
2227
|
status: "PASSED",
|
|
@@ -2310,7 +2253,7 @@ class StableBrowser {
|
|
|
2310
2253
|
this.logger.info("Table data verified");
|
|
2311
2254
|
}
|
|
2312
2255
|
async getTableData(selectors, _params = null, options = {}, world = null) {
|
|
2313
|
-
|
|
2256
|
+
_validateSelectors(selectors);
|
|
2314
2257
|
const startTime = Date.now();
|
|
2315
2258
|
let error = null;
|
|
2316
2259
|
let screenshotId = null;
|
|
@@ -2346,7 +2289,7 @@ class StableBrowser {
|
|
|
2346
2289
|
status: "FAILED",
|
|
2347
2290
|
startTime,
|
|
2348
2291
|
endTime,
|
|
2349
|
-
message: error
|
|
2292
|
+
message: error?.message,
|
|
2350
2293
|
}
|
|
2351
2294
|
: {
|
|
2352
2295
|
status: "PASSED",
|
|
@@ -2358,7 +2301,7 @@ class StableBrowser {
|
|
|
2358
2301
|
}
|
|
2359
2302
|
}
|
|
2360
2303
|
async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
|
|
2361
|
-
|
|
2304
|
+
_validateSelectors(selectors);
|
|
2362
2305
|
if (!query) {
|
|
2363
2306
|
throw new Error("query is null");
|
|
2364
2307
|
}
|
|
@@ -2511,7 +2454,7 @@ class StableBrowser {
|
|
|
2511
2454
|
status: "FAILED",
|
|
2512
2455
|
startTime,
|
|
2513
2456
|
endTime,
|
|
2514
|
-
message: error
|
|
2457
|
+
message: error?.message,
|
|
2515
2458
|
}
|
|
2516
2459
|
: {
|
|
2517
2460
|
status: "PASSED",
|
|
@@ -2603,7 +2546,7 @@ class StableBrowser {
|
|
|
2603
2546
|
status: "FAILED",
|
|
2604
2547
|
startTime,
|
|
2605
2548
|
endTime,
|
|
2606
|
-
message: error
|
|
2549
|
+
message: error?.message,
|
|
2607
2550
|
}
|
|
2608
2551
|
: {
|
|
2609
2552
|
status: "PASSED",
|
|
@@ -2638,7 +2581,7 @@ class StableBrowser {
|
|
|
2638
2581
|
status: "FAILED",
|
|
2639
2582
|
startTime,
|
|
2640
2583
|
endTime,
|
|
2641
|
-
message: error
|
|
2584
|
+
message: error?.message,
|
|
2642
2585
|
}
|
|
2643
2586
|
: {
|
|
2644
2587
|
status: "PASSED",
|
|
@@ -2680,7 +2623,7 @@ class StableBrowser {
|
|
|
2680
2623
|
status: "FAILED",
|
|
2681
2624
|
startTime,
|
|
2682
2625
|
endTime,
|
|
2683
|
-
message: error
|
|
2626
|
+
message: error?.message,
|
|
2684
2627
|
}
|
|
2685
2628
|
: {
|
|
2686
2629
|
status: "PASSED",
|
|
@@ -2716,7 +2659,7 @@ class StableBrowser {
|
|
|
2716
2659
|
status: "FAILED",
|
|
2717
2660
|
startTime,
|
|
2718
2661
|
endTime,
|
|
2719
|
-
message: error
|
|
2662
|
+
message: error?.message,
|
|
2720
2663
|
}
|
|
2721
2664
|
: {
|
|
2722
2665
|
status: "PASSED",
|
|
@@ -2901,5 +2844,10 @@ const KEYBOARD_EVENTS = [
|
|
|
2901
2844
|
"TVAntennaCable",
|
|
2902
2845
|
"TVAudioDescription",
|
|
2903
2846
|
];
|
|
2847
|
+
function unEscapeString(str) {
|
|
2848
|
+
const placeholder = "__NEWLINE__";
|
|
2849
|
+
str = str.replace(new RegExp(placeholder, "g"), "\n");
|
|
2850
|
+
return str;
|
|
2851
|
+
}
|
|
2904
2852
|
export { StableBrowser };
|
|
2905
2853
|
//# sourceMappingURL=stable_browser.js.map
|