automation_model 1.0.424-dev → 1.0.424-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 +102 -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 +23 -14
- package/lib/stable_browser.js +513 -599
- 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 +9 -7
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,6 +557,8 @@ 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
|
}
|
|
@@ -483,7 +567,7 @@ class StableBrowser {
|
|
|
483
567
|
}
|
|
484
568
|
async _locate(selectors, info, _params, timeout = 30000) {
|
|
485
569
|
for (let i = 0; i < 3; i++) {
|
|
486
|
-
info.log += "attempt " + i + ":
|
|
570
|
+
info.log += "attempt " + i + ": total locators " + selectors.locators.length + "\n";
|
|
487
571
|
for (let j = 0; j < selectors.locators.length; j++) {
|
|
488
572
|
let selector = selectors.locators[j];
|
|
489
573
|
info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
|
|
@@ -502,10 +586,44 @@ class StableBrowser {
|
|
|
502
586
|
let locatorsCount = 0;
|
|
503
587
|
//let arrayMode = Array.isArray(selectors);
|
|
504
588
|
let scope = this.page;
|
|
589
|
+
// for the simple click usecase
|
|
590
|
+
if (selectors.frame) {
|
|
591
|
+
scope = selectors.frame;
|
|
592
|
+
}
|
|
505
593
|
if (selectors.iframe_src || selectors.frameLocators) {
|
|
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
|
+
};
|
|
506
619
|
info.log += "searching for iframe " + selectors.iframe_src + "/" + selectors.frameLocators + "\n";
|
|
507
620
|
while (true) {
|
|
508
621
|
let frameFound = false;
|
|
622
|
+
if (selectors.nestFrmLoc) {
|
|
623
|
+
scope = await findFrame(selectors.nestFrmLoc, scope);
|
|
624
|
+
frameFound = true;
|
|
625
|
+
break;
|
|
626
|
+
}
|
|
509
627
|
if (selectors.frameLocators) {
|
|
510
628
|
for (let i = 0; i < selectors.frameLocators.length; i++) {
|
|
511
629
|
let frameLocator = selectors.frameLocators[i];
|
|
@@ -666,86 +784,121 @@ class StableBrowser {
|
|
|
666
784
|
}
|
|
667
785
|
return result;
|
|
668
786
|
}
|
|
669
|
-
async
|
|
670
|
-
this._validateSelectors(selectors);
|
|
787
|
+
async simpleClick(elementDescription, _params, options = {}, world = null) {
|
|
671
788
|
const startTime = Date.now();
|
|
672
|
-
|
|
673
|
-
|
|
789
|
+
let timeout = 30000;
|
|
790
|
+
if (options && options.timeout) {
|
|
791
|
+
timeout = options.timeout;
|
|
674
792
|
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
793
|
+
while (true) {
|
|
794
|
+
try {
|
|
795
|
+
const result = await locate_element(this.context, elementDescription, "click");
|
|
796
|
+
if ((result === null || result === void 0 ? void 0 : result.elementNumber) >= 0) {
|
|
797
|
+
const selectors = {
|
|
798
|
+
frame: result === null || result === void 0 ? void 0 : result.frame,
|
|
799
|
+
locators: [
|
|
800
|
+
{
|
|
801
|
+
css: result === null || result === void 0 ? void 0 : result.css,
|
|
802
|
+
},
|
|
803
|
+
],
|
|
804
|
+
};
|
|
805
|
+
await this.click(selectors, _params, options, world);
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
catch (e) {
|
|
810
|
+
if (performance.now() - startTime > timeout) {
|
|
811
|
+
throw e;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
|
|
818
|
+
const startTime = Date.now();
|
|
819
|
+
let timeout = 30000;
|
|
820
|
+
if (options && options.timeout) {
|
|
821
|
+
timeout = options.timeout;
|
|
822
|
+
}
|
|
823
|
+
while (true) {
|
|
824
|
+
try {
|
|
825
|
+
const result = await locate_element(this.context, elementDescription, "fill", value);
|
|
826
|
+
if ((result === null || result === void 0 ? void 0 : result.elementNumber) >= 0) {
|
|
827
|
+
const selectors = {
|
|
828
|
+
frame: result === null || result === void 0 ? void 0 : result.frame,
|
|
829
|
+
locators: [
|
|
830
|
+
{
|
|
831
|
+
css: result === null || result === void 0 ? void 0 : result.css,
|
|
832
|
+
},
|
|
833
|
+
],
|
|
834
|
+
};
|
|
835
|
+
await this.clickType(selectors, value, false, _params, options, world);
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
catch (e) {
|
|
840
|
+
if (performance.now() - startTime > timeout) {
|
|
841
|
+
throw e;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
async click(selectors, _params, options = {}, world = null) {
|
|
848
|
+
const state = {
|
|
849
|
+
selectors,
|
|
850
|
+
_params,
|
|
851
|
+
options,
|
|
852
|
+
world,
|
|
853
|
+
text: "Click element",
|
|
854
|
+
type: Types.CLICK,
|
|
855
|
+
operation: "click",
|
|
856
|
+
log: "***** click on " + selectors.element_name + " *****\n",
|
|
857
|
+
};
|
|
682
858
|
try {
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
859
|
+
await _preCommand(state, this);
|
|
860
|
+
if (state.options && state.options.context) {
|
|
861
|
+
state.selectors.locators[0].text = state.options.context;
|
|
862
|
+
}
|
|
686
863
|
try {
|
|
687
|
-
await
|
|
688
|
-
await element.click();
|
|
864
|
+
await state.element.click();
|
|
689
865
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
690
866
|
}
|
|
691
867
|
catch (e) {
|
|
692
868
|
// await this.closeUnexpectedPopups();
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
await element.click({ timeout: 10000, force: true });
|
|
869
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
870
|
+
await state.element.dispatchEvent("click");
|
|
696
871
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
697
872
|
}
|
|
698
873
|
await this.waitForPageLoad();
|
|
699
|
-
return info;
|
|
874
|
+
return state.info;
|
|
700
875
|
}
|
|
701
876
|
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;
|
|
877
|
+
await _commandError(state, e, this);
|
|
708
878
|
}
|
|
709
879
|
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
|
-
});
|
|
880
|
+
_commandFinally(state, this);
|
|
730
881
|
}
|
|
731
882
|
}
|
|
732
883
|
async setCheck(selectors, checked = true, _params, options = {}, world = null) {
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
884
|
+
const state = {
|
|
885
|
+
selectors,
|
|
886
|
+
_params,
|
|
887
|
+
options,
|
|
888
|
+
world,
|
|
889
|
+
type: checked ? Types.CHECK : Types.UNCHECK,
|
|
890
|
+
text: checked ? `Check element` : `Uncheck element`,
|
|
891
|
+
operation: "setCheck",
|
|
892
|
+
log: "***** check " + selectors.element_name + " *****\n",
|
|
893
|
+
};
|
|
743
894
|
try {
|
|
744
|
-
|
|
745
|
-
|
|
895
|
+
_preCommand(state, this);
|
|
896
|
+
state.info.checked = checked;
|
|
897
|
+
// let element = await this._locate(selectors, info, _params);
|
|
898
|
+
// ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
746
899
|
try {
|
|
747
|
-
await this._highlightElements(element);
|
|
748
|
-
await element.setChecked(checked);
|
|
900
|
+
// await this._highlightElements(element);
|
|
901
|
+
await state.element.setChecked(checked);
|
|
749
902
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
750
903
|
}
|
|
751
904
|
catch (e) {
|
|
@@ -755,178 +908,106 @@ class StableBrowser {
|
|
|
755
908
|
else {
|
|
756
909
|
//await this.closeUnexpectedPopups();
|
|
757
910
|
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 });
|
|
911
|
+
state.element = await this._locate(selectors, info, _params);
|
|
912
|
+
await state.element.setChecked(checked, { timeout: 5000, force: true });
|
|
760
913
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
761
914
|
}
|
|
762
915
|
}
|
|
763
916
|
await this.waitForPageLoad();
|
|
764
|
-
return info;
|
|
917
|
+
return state.info;
|
|
765
918
|
}
|
|
766
919
|
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;
|
|
920
|
+
await _commandError(state, e, this);
|
|
773
921
|
}
|
|
774
922
|
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
|
-
});
|
|
923
|
+
_commandFinally(state, this);
|
|
795
924
|
}
|
|
796
925
|
}
|
|
797
926
|
async hover(selectors, _params, options = {}, world = null) {
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
927
|
+
const state = {
|
|
928
|
+
selectors,
|
|
929
|
+
_params,
|
|
930
|
+
options,
|
|
931
|
+
world,
|
|
932
|
+
type: Types.HOVER,
|
|
933
|
+
text: `Hover element`,
|
|
934
|
+
operation: "hover",
|
|
935
|
+
log: "***** hover " + selectors.element_name + " *****\n",
|
|
936
|
+
};
|
|
807
937
|
try {
|
|
808
|
-
|
|
809
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
938
|
+
_preCommand(state, this);
|
|
810
939
|
try {
|
|
811
|
-
await
|
|
812
|
-
await element.hover();
|
|
940
|
+
await state.element.hover();
|
|
813
941
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
814
942
|
}
|
|
815
943
|
catch (e) {
|
|
816
944
|
//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 });
|
|
945
|
+
state.info.log += "hover failed, will try again" + "\n";
|
|
946
|
+
state.element = await this._locate(selectors, state.info, _params);
|
|
947
|
+
await state.element.hover({ timeout: 10000 });
|
|
820
948
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
821
949
|
}
|
|
822
950
|
await this.waitForPageLoad();
|
|
823
|
-
return info;
|
|
951
|
+
return state.info;
|
|
824
952
|
}
|
|
825
953
|
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;
|
|
954
|
+
await _commandError(state, e, this);
|
|
832
955
|
}
|
|
833
956
|
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
|
-
});
|
|
957
|
+
_commandFinally(state, this);
|
|
854
958
|
}
|
|
855
959
|
}
|
|
856
960
|
async selectOption(selectors, values, _params = null, options = {}, world = null) {
|
|
857
|
-
this._validateSelectors(selectors);
|
|
858
961
|
if (!values) {
|
|
859
962
|
throw new Error("values is null");
|
|
860
963
|
}
|
|
861
|
-
const
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
964
|
+
const state = {
|
|
965
|
+
selectors,
|
|
966
|
+
_params,
|
|
967
|
+
options,
|
|
968
|
+
world,
|
|
969
|
+
type: Types.SELECT,
|
|
970
|
+
text: `Select option: ${values}`,
|
|
971
|
+
operation: "selectOption",
|
|
972
|
+
log: "***** select option " + selectors.element_name + " *****\n",
|
|
973
|
+
};
|
|
869
974
|
try {
|
|
870
|
-
|
|
871
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
975
|
+
await _preCommand(state, this);
|
|
872
976
|
try {
|
|
873
|
-
await
|
|
874
|
-
await element.selectOption(values);
|
|
977
|
+
await state.element.selectOption(values);
|
|
875
978
|
}
|
|
876
979
|
catch (e) {
|
|
877
980
|
//await this.closeUnexpectedPopups();
|
|
878
|
-
info.log += "selectOption failed, will try force" + "\n";
|
|
879
|
-
await element.selectOption(values, { timeout: 10000, force: true });
|
|
981
|
+
state.info.log += "selectOption failed, will try force" + "\n";
|
|
982
|
+
await state.element.selectOption(values, { timeout: 10000, force: true });
|
|
880
983
|
}
|
|
881
984
|
await this.waitForPageLoad();
|
|
882
|
-
return info;
|
|
985
|
+
return state.info;
|
|
883
986
|
}
|
|
884
987
|
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;
|
|
988
|
+
await _commandError(state, e, this);
|
|
892
989
|
}
|
|
893
990
|
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
|
-
});
|
|
991
|
+
_commandFinally(state, this);
|
|
915
992
|
}
|
|
916
993
|
}
|
|
917
994
|
async type(_value, _params = null, options = {}, world = null) {
|
|
918
|
-
const
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
995
|
+
const state = {
|
|
996
|
+
value: _value,
|
|
997
|
+
_params,
|
|
998
|
+
options,
|
|
999
|
+
world,
|
|
1000
|
+
locate: false,
|
|
1001
|
+
scroll: false,
|
|
1002
|
+
highlight: false,
|
|
1003
|
+
type: Types.TYPE_PRESS,
|
|
1004
|
+
text: `Type value: ${_value}`,
|
|
1005
|
+
operation: "type",
|
|
1006
|
+
log: "",
|
|
1007
|
+
};
|
|
927
1008
|
try {
|
|
928
|
-
|
|
929
|
-
const valueSegment =
|
|
1009
|
+
await _preCommand(state, this);
|
|
1010
|
+
const valueSegment = state.value.split("&&");
|
|
930
1011
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
931
1012
|
if (i > 0) {
|
|
932
1013
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -946,108 +1027,53 @@ class StableBrowser {
|
|
|
946
1027
|
await this.page.keyboard.type(value);
|
|
947
1028
|
}
|
|
948
1029
|
}
|
|
949
|
-
return info;
|
|
1030
|
+
return state.info;
|
|
950
1031
|
}
|
|
951
1032
|
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;
|
|
1033
|
+
await _commandError(state, e, this);
|
|
959
1034
|
}
|
|
960
1035
|
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
|
-
});
|
|
1036
|
+
_commandFinally(state, this);
|
|
981
1037
|
}
|
|
982
1038
|
}
|
|
983
1039
|
async setInputValue(selectors, value, _params = null, options = {}, world = null) {
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
let screenshotPath = null;
|
|
1040
|
+
const state = {
|
|
1041
|
+
selectors,
|
|
1042
|
+
_params,
|
|
1043
|
+
value,
|
|
1044
|
+
options,
|
|
1045
|
+
world,
|
|
1046
|
+
type: Types.SET_INPUT,
|
|
1047
|
+
text: `Set input value`,
|
|
1048
|
+
operation: "setInputValue",
|
|
1049
|
+
log: "***** set input value " + selectors.element_name + " *****\n",
|
|
1050
|
+
};
|
|
996
1051
|
try {
|
|
997
|
-
|
|
998
|
-
let
|
|
999
|
-
await this.scrollIfNeeded(element, info);
|
|
1000
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1001
|
-
await this._highlightElements(element);
|
|
1052
|
+
await _preCommand(state, this);
|
|
1053
|
+
let value = await this._replaceWithLocalData(state.value, this);
|
|
1002
1054
|
try {
|
|
1003
|
-
await element.evaluateHandle((el, value) => {
|
|
1055
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1004
1056
|
el.value = value;
|
|
1005
1057
|
}, value);
|
|
1006
1058
|
}
|
|
1007
1059
|
catch (error) {
|
|
1008
1060
|
this.logger.error("setInputValue failed, will try again");
|
|
1009
|
-
|
|
1010
|
-
info.
|
|
1011
|
-
|
|
1012
|
-
await element.evaluateHandle((el, value) => {
|
|
1061
|
+
await _screenshot(state, this);
|
|
1062
|
+
Object.assign(error, { info: state.info });
|
|
1063
|
+
await state.element.evaluateHandle((el, value) => {
|
|
1013
1064
|
el.value = value;
|
|
1014
1065
|
});
|
|
1015
1066
|
}
|
|
1016
1067
|
}
|
|
1017
1068
|
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;
|
|
1069
|
+
await _commandError(state, e, this);
|
|
1024
1070
|
}
|
|
1025
1071
|
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
|
-
});
|
|
1072
|
+
_commandFinally(state, this);
|
|
1047
1073
|
}
|
|
1048
1074
|
}
|
|
1049
1075
|
async setDateTime(selectors, value, format = null, enter = false, _params = null, options = {}, world = null) {
|
|
1050
|
-
|
|
1076
|
+
_validateSelectors(selectors);
|
|
1051
1077
|
const startTime = Date.now();
|
|
1052
1078
|
let error = null;
|
|
1053
1079
|
let screenshotId = null;
|
|
@@ -1140,30 +1166,28 @@ class StableBrowser {
|
|
|
1140
1166
|
}
|
|
1141
1167
|
}
|
|
1142
1168
|
async clickType(selectors, _value, enter = false, _params = null, options = {}, world = null) {
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1169
|
+
const state = {
|
|
1170
|
+
selectors,
|
|
1171
|
+
_params,
|
|
1172
|
+
value: unEscapeString(_value),
|
|
1173
|
+
options,
|
|
1174
|
+
world,
|
|
1175
|
+
type: Types.FILL,
|
|
1176
|
+
text: `Click type input with value: ${_value}`,
|
|
1177
|
+
operation: "clickType",
|
|
1178
|
+
log: "***** clickType on " + selectors.element_name + " with value " + _value + "*****\n",
|
|
1179
|
+
};
|
|
1180
|
+
const newValue = await this._replaceWithLocalData(state.value, world);
|
|
1153
1181
|
if (newValue !== _value) {
|
|
1154
1182
|
//this.logger.info(_value + "=" + newValue);
|
|
1155
1183
|
_value = newValue;
|
|
1156
1184
|
}
|
|
1157
|
-
info.value = _value;
|
|
1158
1185
|
try {
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
await this.scrollIfNeeded(element, info);
|
|
1162
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1163
|
-
await this._highlightElements(element);
|
|
1186
|
+
await _preCommand(state, this);
|
|
1187
|
+
state.info.value = _value;
|
|
1164
1188
|
if (options === null || options === undefined || !options.press) {
|
|
1165
1189
|
try {
|
|
1166
|
-
let currentValue = await element.inputValue();
|
|
1190
|
+
let currentValue = await state.element.inputValue();
|
|
1167
1191
|
if (currentValue) {
|
|
1168
1192
|
await element.fill("");
|
|
1169
1193
|
}
|
|
@@ -1174,22 +1198,22 @@ class StableBrowser {
|
|
|
1174
1198
|
}
|
|
1175
1199
|
if (options === null || options === undefined || options.press) {
|
|
1176
1200
|
try {
|
|
1177
|
-
await element.click({ timeout: 5000 });
|
|
1201
|
+
await state.element.click({ timeout: 5000 });
|
|
1178
1202
|
}
|
|
1179
1203
|
catch (e) {
|
|
1180
|
-
await element.dispatchEvent("click");
|
|
1204
|
+
await state.element.dispatchEvent("click");
|
|
1181
1205
|
}
|
|
1182
1206
|
}
|
|
1183
1207
|
else {
|
|
1184
1208
|
try {
|
|
1185
|
-
await element.focus();
|
|
1209
|
+
await state.element.focus();
|
|
1186
1210
|
}
|
|
1187
1211
|
catch (e) {
|
|
1188
|
-
await element.dispatchEvent("focus");
|
|
1212
|
+
await state.element.dispatchEvent("focus");
|
|
1189
1213
|
}
|
|
1190
1214
|
}
|
|
1191
1215
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1192
|
-
const valueSegment =
|
|
1216
|
+
const valueSegment = state.value.split("&&");
|
|
1193
1217
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1194
1218
|
if (i > 0) {
|
|
1195
1219
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1209,13 +1233,14 @@ class StableBrowser {
|
|
|
1209
1233
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1210
1234
|
}
|
|
1211
1235
|
}
|
|
1236
|
+
await _screenshot(state, this);
|
|
1212
1237
|
if (enter === true) {
|
|
1213
1238
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1214
1239
|
await this.page.keyboard.press("Enter");
|
|
1215
1240
|
await this.waitForPageLoad();
|
|
1216
1241
|
}
|
|
1217
1242
|
else if (enter === false) {
|
|
1218
|
-
await element.dispatchEvent("change");
|
|
1243
|
+
await state.element.dispatchEvent("change");
|
|
1219
1244
|
//await this.page.keyboard.press("Tab");
|
|
1220
1245
|
}
|
|
1221
1246
|
else {
|
|
@@ -1224,103 +1249,50 @@ class StableBrowser {
|
|
|
1224
1249
|
await this.waitForPageLoad();
|
|
1225
1250
|
}
|
|
1226
1251
|
}
|
|
1227
|
-
return info;
|
|
1252
|
+
return state.info;
|
|
1228
1253
|
}
|
|
1229
1254
|
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;
|
|
1255
|
+
await _commandError(state, e, this);
|
|
1237
1256
|
}
|
|
1238
1257
|
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
|
-
});
|
|
1258
|
+
_commandFinally(state, this);
|
|
1260
1259
|
}
|
|
1261
1260
|
}
|
|
1262
1261
|
async fill(selectors, value, enter = false, _params = null, options = {}, world = null) {
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1262
|
+
const state = {
|
|
1263
|
+
selectors,
|
|
1264
|
+
_params,
|
|
1265
|
+
value: unEscapeString(value),
|
|
1266
|
+
options,
|
|
1267
|
+
world,
|
|
1268
|
+
type: Types.FILL,
|
|
1269
|
+
text: `Fill input with value: ${value}`,
|
|
1270
|
+
operation: "fill",
|
|
1271
|
+
log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
|
|
1272
|
+
};
|
|
1273
1273
|
try {
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
await
|
|
1277
|
-
await element.fill(value);
|
|
1278
|
-
await element.dispatchEvent("change");
|
|
1274
|
+
await _preCommand(state, this);
|
|
1275
|
+
await state.element.fill(value);
|
|
1276
|
+
await state.element.dispatchEvent("change");
|
|
1279
1277
|
if (enter) {
|
|
1280
1278
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1281
1279
|
await this.page.keyboard.press("Enter");
|
|
1282
1280
|
}
|
|
1283
1281
|
await this.waitForPageLoad();
|
|
1284
|
-
return info;
|
|
1282
|
+
return state.info;
|
|
1285
1283
|
}
|
|
1286
1284
|
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;
|
|
1285
|
+
await _commandError(state, e, this);
|
|
1294
1286
|
}
|
|
1295
1287
|
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
|
-
});
|
|
1288
|
+
_commandFinally(state, this);
|
|
1317
1289
|
}
|
|
1318
1290
|
}
|
|
1319
1291
|
async getText(selectors, _params = null, options = {}, info = {}, world = null) {
|
|
1320
1292
|
return await this._getText(selectors, 0, _params, options, info, world);
|
|
1321
1293
|
}
|
|
1322
1294
|
async _getText(selectors, climb, _params = null, options = {}, info = {}, world = null) {
|
|
1323
|
-
|
|
1295
|
+
_validateSelectors(selectors);
|
|
1324
1296
|
let screenshotId = null;
|
|
1325
1297
|
let screenshotPath = null;
|
|
1326
1298
|
if (!info.log) {
|
|
@@ -1365,113 +1337,99 @@ class StableBrowser {
|
|
|
1365
1337
|
}
|
|
1366
1338
|
async containsPattern(selectors, pattern, text, _params = null, options = {}, world = null) {
|
|
1367
1339
|
var _a;
|
|
1368
|
-
this._validateSelectors(selectors);
|
|
1369
1340
|
if (!pattern) {
|
|
1370
1341
|
throw new Error("pattern is null");
|
|
1371
1342
|
}
|
|
1372
1343
|
if (!text) {
|
|
1373
1344
|
throw new Error("text is null");
|
|
1374
1345
|
}
|
|
1346
|
+
const state = {
|
|
1347
|
+
selectors,
|
|
1348
|
+
_params,
|
|
1349
|
+
pattern,
|
|
1350
|
+
value: text,
|
|
1351
|
+
options,
|
|
1352
|
+
world,
|
|
1353
|
+
locate: false,
|
|
1354
|
+
scroll: false,
|
|
1355
|
+
screenshot: false,
|
|
1356
|
+
highlight: false,
|
|
1357
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1358
|
+
text: `Verify element contains pattern: ${pattern}`,
|
|
1359
|
+
operation: "containsPattern",
|
|
1360
|
+
log: "***** verify element " + selectors.element_name + " contains pattern " + pattern + " *****\n",
|
|
1361
|
+
};
|
|
1375
1362
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1376
1363
|
if (newValue !== text) {
|
|
1377
1364
|
this.logger.info(text + "=" + newValue);
|
|
1378
1365
|
text = newValue;
|
|
1379
1366
|
}
|
|
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
1367
|
let foundObj = null;
|
|
1392
1368
|
try {
|
|
1393
|
-
|
|
1369
|
+
await _preCommand(state, this);
|
|
1370
|
+
state.info.pattern = pattern;
|
|
1371
|
+
foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
|
|
1394
1372
|
if (foundObj && foundObj.element) {
|
|
1395
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1373
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1396
1374
|
}
|
|
1397
|
-
|
|
1375
|
+
await _screenshot(state, this);
|
|
1398
1376
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1399
1377
|
pattern = pattern.replace("{text}", escapedText);
|
|
1400
1378
|
let regex = new RegExp(pattern, "im");
|
|
1401
1379
|
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;
|
|
1380
|
+
state.info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
|
|
1403
1381
|
throw new Error("element doesn't contain text " + text);
|
|
1404
1382
|
}
|
|
1405
|
-
return info;
|
|
1383
|
+
return state.info;
|
|
1406
1384
|
}
|
|
1407
1385
|
catch (e) {
|
|
1408
|
-
//await this.closeUnexpectedPopups();
|
|
1409
|
-
this.logger.error("verify element contains text failed " + info.log);
|
|
1410
1386
|
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;
|
|
1387
|
+
await _commandError(state, e, this);
|
|
1416
1388
|
}
|
|
1417
1389
|
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
|
-
});
|
|
1390
|
+
_commandFinally(state, this);
|
|
1439
1391
|
}
|
|
1440
1392
|
}
|
|
1441
1393
|
async containsText(selectors, text, climb, _params = null, options = {}, world = null) {
|
|
1442
1394
|
var _a, _b, _c;
|
|
1443
|
-
|
|
1395
|
+
const state = {
|
|
1396
|
+
selectors,
|
|
1397
|
+
_params,
|
|
1398
|
+
value: text,
|
|
1399
|
+
options,
|
|
1400
|
+
world,
|
|
1401
|
+
locate: false,
|
|
1402
|
+
scroll: false,
|
|
1403
|
+
screenshot: false,
|
|
1404
|
+
highlight: false,
|
|
1405
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1406
|
+
text: `Verify element contains text: ${text}`,
|
|
1407
|
+
operation: "containsText",
|
|
1408
|
+
log: "***** verify element " + selectors.element_name + " contains text " + text + " *****\n",
|
|
1409
|
+
};
|
|
1444
1410
|
if (!text) {
|
|
1445
1411
|
throw new Error("text is null");
|
|
1446
1412
|
}
|
|
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;
|
|
1413
|
+
text = unEscapeString(text);
|
|
1455
1414
|
const newValue = await this._replaceWithLocalData(text, world);
|
|
1456
1415
|
if (newValue !== text) {
|
|
1457
1416
|
this.logger.info(text + "=" + newValue);
|
|
1458
1417
|
text = newValue;
|
|
1459
1418
|
}
|
|
1460
|
-
info.value = text;
|
|
1461
1419
|
let foundObj = null;
|
|
1462
1420
|
try {
|
|
1463
|
-
foundObj = await this._getText(selectors, climb, _params, options, info, world);
|
|
1421
|
+
foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
|
|
1464
1422
|
if (foundObj && foundObj.element) {
|
|
1465
|
-
await this.scrollIfNeeded(foundObj.element, info);
|
|
1423
|
+
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1466
1424
|
}
|
|
1467
|
-
|
|
1425
|
+
await _screenshot(state, this);
|
|
1468
1426
|
const dateAlternatives = findDateAlternatives(text);
|
|
1469
1427
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1470
1428
|
if (dateAlternatives.date) {
|
|
1471
1429
|
for (let i = 0; i < dateAlternatives.dates.length; i++) {
|
|
1472
1430
|
if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(dateAlternatives.dates[i])) ||
|
|
1473
1431
|
((_a = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _a === void 0 ? void 0 : _a.includes(dateAlternatives.dates[i]))) {
|
|
1474
|
-
return info;
|
|
1432
|
+
return state.info;
|
|
1475
1433
|
}
|
|
1476
1434
|
}
|
|
1477
1435
|
throw new Error("element doesn't contain text " + text);
|
|
@@ -1480,49 +1438,23 @@ class StableBrowser {
|
|
|
1480
1438
|
for (let i = 0; i < numberAlternatives.numbers.length; i++) {
|
|
1481
1439
|
if ((foundObj === null || foundObj === void 0 ? void 0 : foundObj.text.includes(numberAlternatives.numbers[i])) ||
|
|
1482
1440
|
((_b = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value) === null || _b === void 0 ? void 0 : _b.includes(numberAlternatives.numbers[i]))) {
|
|
1483
|
-
return info;
|
|
1441
|
+
return state.info;
|
|
1484
1442
|
}
|
|
1485
1443
|
}
|
|
1486
1444
|
throw new Error("element doesn't contain text " + text);
|
|
1487
1445
|
}
|
|
1488
1446
|
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;
|
|
1447
|
+
state.info.foundText = foundObj === null || foundObj === void 0 ? void 0 : foundObj.text;
|
|
1448
|
+
state.info.value = foundObj === null || foundObj === void 0 ? void 0 : foundObj.value;
|
|
1491
1449
|
throw new Error("element doesn't contain text " + text);
|
|
1492
1450
|
}
|
|
1493
|
-
return info;
|
|
1451
|
+
return state.info;
|
|
1494
1452
|
}
|
|
1495
1453
|
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;
|
|
1454
|
+
await _commandError(state, e, this);
|
|
1503
1455
|
}
|
|
1504
1456
|
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
|
-
});
|
|
1457
|
+
_commandFinally(state, this);
|
|
1526
1458
|
}
|
|
1527
1459
|
}
|
|
1528
1460
|
_getDataFile(world = null) {
|
|
@@ -1541,6 +1473,29 @@ class StableBrowser {
|
|
|
1541
1473
|
}
|
|
1542
1474
|
return dataFile;
|
|
1543
1475
|
}
|
|
1476
|
+
async waitForUserInput(message, world = null) {
|
|
1477
|
+
if (!message) {
|
|
1478
|
+
message = "# Wait for user input. Press any key to continue";
|
|
1479
|
+
}
|
|
1480
|
+
else {
|
|
1481
|
+
message = "# Wait for user input. " + message;
|
|
1482
|
+
}
|
|
1483
|
+
message += "\n";
|
|
1484
|
+
const value = await new Promise((resolve) => {
|
|
1485
|
+
const rl = readline.createInterface({
|
|
1486
|
+
input: process.stdin,
|
|
1487
|
+
output: process.stdout,
|
|
1488
|
+
});
|
|
1489
|
+
rl.question(message, (answer) => {
|
|
1490
|
+
rl.close();
|
|
1491
|
+
resolve(answer);
|
|
1492
|
+
});
|
|
1493
|
+
});
|
|
1494
|
+
if (value) {
|
|
1495
|
+
this.logger.info(`{{userInput}} was set to: ${value}`);
|
|
1496
|
+
}
|
|
1497
|
+
this.setTestData({ userInput: value }, world);
|
|
1498
|
+
}
|
|
1544
1499
|
setTestData(testData, world = null) {
|
|
1545
1500
|
if (!testData) {
|
|
1546
1501
|
return;
|
|
@@ -1728,7 +1683,6 @@ class StableBrowser {
|
|
|
1728
1683
|
}
|
|
1729
1684
|
async takeScreenshot(screenshotPath) {
|
|
1730
1685
|
const playContext = this.context.playContext;
|
|
1731
|
-
const client = await playContext.newCDPSession(this.page);
|
|
1732
1686
|
// Using CDP to capture the screenshot
|
|
1733
1687
|
const viewportWidth = Math.max(...(await this.page.evaluate(() => [
|
|
1734
1688
|
document.body.scrollWidth,
|
|
@@ -1738,97 +1692,67 @@ class StableBrowser {
|
|
|
1738
1692
|
document.body.clientWidth,
|
|
1739
1693
|
document.documentElement.clientWidth,
|
|
1740
1694
|
])));
|
|
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();
|
|
1695
|
+
let screenshotBuffer = null;
|
|
1696
|
+
if (this.context.browserName === "chromium") {
|
|
1697
|
+
const client = await playContext.newCDPSession(this.page);
|
|
1698
|
+
const { data } = await client.send("Page.captureScreenshot", {
|
|
1699
|
+
format: "png",
|
|
1700
|
+
// clip: {
|
|
1701
|
+
// x: 0,
|
|
1702
|
+
// y: 0,
|
|
1703
|
+
// width: viewportWidth,
|
|
1704
|
+
// height: viewportHeight,
|
|
1705
|
+
// scale: 1,
|
|
1706
|
+
// },
|
|
1707
|
+
});
|
|
1708
|
+
await client.detach();
|
|
1709
|
+
if (!screenshotPath) {
|
|
1710
|
+
return data;
|
|
1711
|
+
}
|
|
1712
|
+
screenshotBuffer = Buffer.from(data, "base64");
|
|
1713
|
+
}
|
|
1714
|
+
else {
|
|
1715
|
+
screenshotBuffer = await this.page.screenshot();
|
|
1716
|
+
}
|
|
1717
|
+
let image = await Jimp.read(screenshotBuffer);
|
|
1718
|
+
// Get the image dimensions
|
|
1719
|
+
const { width, height } = image.bitmap;
|
|
1720
|
+
const resizeRatio = viewportWidth / width;
|
|
1721
|
+
// Resize the image to fit within the viewport dimensions without enlarging
|
|
1722
|
+
if (width > viewportWidth) {
|
|
1723
|
+
image = image.resize({ w: viewportWidth, h: height * resizeRatio }); // Resize the image while maintaining aspect ratio
|
|
1724
|
+
await image.write(screenshotPath);
|
|
1725
|
+
}
|
|
1726
|
+
else {
|
|
1727
|
+
fs.writeFileSync(screenshotPath, screenshotBuffer);
|
|
1728
|
+
}
|
|
1776
1729
|
}
|
|
1777
1730
|
async verifyElementExistInPage(selectors, _params = null, options = {}, world = null) {
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1731
|
+
const state = {
|
|
1732
|
+
selectors,
|
|
1733
|
+
_params,
|
|
1734
|
+
options,
|
|
1735
|
+
world,
|
|
1736
|
+
type: Types.VERIFY_ELEMENT_CONTAINS_TEXT,
|
|
1737
|
+
text: `Verify element exists in page`,
|
|
1738
|
+
operation: "verifyElementExistInPage",
|
|
1739
|
+
log: "***** verify element " + selectors.element_name + " exists in page *****\n",
|
|
1740
|
+
};
|
|
1783
1741
|
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
1742
|
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;
|
|
1743
|
+
await _preCommand(state, this);
|
|
1744
|
+
await expect(state.element).toHaveCount(1, { timeout: 10000 });
|
|
1745
|
+
return state.info;
|
|
1797
1746
|
}
|
|
1798
1747
|
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;
|
|
1748
|
+
await _commandError(state, e, this);
|
|
1806
1749
|
}
|
|
1807
1750
|
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
|
-
});
|
|
1751
|
+
_commandFinally(state, this);
|
|
1828
1752
|
}
|
|
1829
1753
|
}
|
|
1830
1754
|
async extractAttribute(selectors, attribute, variable, _params = null, options = {}, world = null) {
|
|
1831
|
-
|
|
1755
|
+
_validateSelectors(selectors);
|
|
1832
1756
|
const startTime = Date.now();
|
|
1833
1757
|
let error = null;
|
|
1834
1758
|
let screenshotId = null;
|
|
@@ -2132,20 +2056,20 @@ class StableBrowser {
|
|
|
2132
2056
|
for (let i = 0; i < frames.length; i++) {
|
|
2133
2057
|
if (dateAlternatives.date) {
|
|
2134
2058
|
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2135
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, {});
|
|
2059
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*", true, true, {});
|
|
2136
2060
|
result.frame = frames[i];
|
|
2137
2061
|
results.push(result);
|
|
2138
2062
|
}
|
|
2139
2063
|
}
|
|
2140
2064
|
else if (numberAlternatives.number) {
|
|
2141
2065
|
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2142
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, {});
|
|
2066
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*", true, true, {});
|
|
2143
2067
|
result.frame = frames[i];
|
|
2144
2068
|
results.push(result);
|
|
2145
2069
|
}
|
|
2146
2070
|
}
|
|
2147
2071
|
else {
|
|
2148
|
-
const result = await this._locateElementByText(frames[i], text, "*", true, {});
|
|
2072
|
+
const result = await this._locateElementByText(frames[i], text, "*", true, true, {});
|
|
2149
2073
|
result.frame = frames[i];
|
|
2150
2074
|
results.push(result);
|
|
2151
2075
|
}
|
|
@@ -2310,7 +2234,7 @@ class StableBrowser {
|
|
|
2310
2234
|
this.logger.info("Table data verified");
|
|
2311
2235
|
}
|
|
2312
2236
|
async getTableData(selectors, _params = null, options = {}, world = null) {
|
|
2313
|
-
|
|
2237
|
+
_validateSelectors(selectors);
|
|
2314
2238
|
const startTime = Date.now();
|
|
2315
2239
|
let error = null;
|
|
2316
2240
|
let screenshotId = null;
|
|
@@ -2358,7 +2282,7 @@ class StableBrowser {
|
|
|
2358
2282
|
}
|
|
2359
2283
|
}
|
|
2360
2284
|
async analyzeTable(selectors, query, operator, value, _params = null, options = {}, world = null) {
|
|
2361
|
-
|
|
2285
|
+
_validateSelectors(selectors);
|
|
2362
2286
|
if (!query) {
|
|
2363
2287
|
throw new Error("query is null");
|
|
2364
2288
|
}
|
|
@@ -2580,13 +2504,13 @@ class StableBrowser {
|
|
|
2580
2504
|
}
|
|
2581
2505
|
catch (e) {
|
|
2582
2506
|
if (e.label === "networkidle") {
|
|
2583
|
-
console.log("
|
|
2507
|
+
console.log("waited for the network to be idle timeout");
|
|
2584
2508
|
}
|
|
2585
2509
|
else if (e.label === "load") {
|
|
2586
|
-
console.log("
|
|
2510
|
+
console.log("waited for the load timeout");
|
|
2587
2511
|
}
|
|
2588
2512
|
else if (e.label === "domcontentloaded") {
|
|
2589
|
-
console.log("
|
|
2513
|
+
console.log("waited for the domcontent loaded timeout");
|
|
2590
2514
|
}
|
|
2591
2515
|
console.log(".");
|
|
2592
2516
|
}
|
|
@@ -2729,33 +2653,18 @@ class StableBrowser {
|
|
|
2729
2653
|
}
|
|
2730
2654
|
async scrollIfNeeded(element, info) {
|
|
2731
2655
|
try {
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
if (rect &&
|
|
2735
|
-
rect.top >= 0 &&
|
|
2736
|
-
rect.left >= 0 &&
|
|
2737
|
-
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
|
|
2738
|
-
rect.right <= (window.innerWidth || document.documentElement.clientWidth)) {
|
|
2739
|
-
return false;
|
|
2740
|
-
}
|
|
2741
|
-
else {
|
|
2742
|
-
node.scrollIntoView({
|
|
2743
|
-
behavior: "smooth",
|
|
2744
|
-
block: "center",
|
|
2745
|
-
inline: "center",
|
|
2746
|
-
});
|
|
2747
|
-
return true;
|
|
2748
|
-
}
|
|
2656
|
+
await element.scrollIntoViewIfNeeded({
|
|
2657
|
+
timeout: 2000,
|
|
2749
2658
|
});
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
}
|
|
2659
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
2660
|
+
if (info) {
|
|
2661
|
+
info.box = await element.boundingBox({
|
|
2662
|
+
timeout: 1000,
|
|
2663
|
+
});
|
|
2755
2664
|
}
|
|
2756
2665
|
}
|
|
2757
2666
|
catch (e) {
|
|
2758
|
-
console.log("
|
|
2667
|
+
console.log("#-#");
|
|
2759
2668
|
}
|
|
2760
2669
|
}
|
|
2761
2670
|
_reportToWorld(world, properties) {
|
|
@@ -2916,5 +2825,10 @@ const KEYBOARD_EVENTS = [
|
|
|
2916
2825
|
"TVAntennaCable",
|
|
2917
2826
|
"TVAudioDescription",
|
|
2918
2827
|
];
|
|
2828
|
+
function unEscapeString(str) {
|
|
2829
|
+
const placeholder = "__NEWLINE__";
|
|
2830
|
+
str = str.replace(new RegExp(placeholder, "g"), "\n");
|
|
2831
|
+
return str;
|
|
2832
|
+
}
|
|
2919
2833
|
export { StableBrowser };
|
|
2920
2834
|
//# sourceMappingURL=stable_browser.js.map
|