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