automation_model 1.0.516-dev → 1.0.516-stage
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api.d.ts +2 -1
- package/lib/api.js +97 -98
- package/lib/api.js.map +1 -1
- package/lib/auto_page.d.ts +2 -1
- package/lib/auto_page.js +19 -2
- package/lib/auto_page.js.map +1 -1
- package/lib/browser_manager.d.ts +3 -2
- package/lib/browser_manager.js +70 -27
- package/lib/browser_manager.js.map +1 -1
- package/lib/command_common.d.ts +1 -0
- package/lib/command_common.js +43 -8
- package/lib/command_common.js.map +1 -1
- package/lib/error-messages.js +20 -2
- package/lib/error-messages.js.map +1 -1
- package/lib/generation_scripts.d.ts +4 -0
- package/lib/generation_scripts.js +2 -0
- package/lib/generation_scripts.js.map +1 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/init_browser.d.ts +2 -1
- package/lib/init_browser.js +15 -4
- package/lib/init_browser.js.map +1 -1
- package/lib/locate_element.js +15 -13
- package/lib/locate_element.js.map +1 -1
- package/lib/locator.d.ts +36 -0
- package/lib/locator.js +165 -0
- package/lib/locator.js.map +1 -1
- package/lib/locator_log.d.ts +26 -0
- package/lib/locator_log.js +69 -0
- package/lib/locator_log.js.map +1 -0
- package/lib/network.js +37 -3
- package/lib/network.js.map +1 -1
- package/lib/stable_browser.d.ts +52 -17
- package/lib/stable_browser.js +455 -477
- package/lib/stable_browser.js.map +1 -1
- package/lib/table.d.ts +13 -0
- package/lib/table.js +187 -0
- package/lib/table.js.map +1 -0
- package/lib/utils.d.ts +13 -1
- package/lib/utils.js +344 -6
- package/lib/utils.js.map +1 -1
- package/package.json +6 -5
- /package/lib/{axe → scripts}/axe.mini.js +0 -0
package/lib/stable_browser.js
CHANGED
|
@@ -10,15 +10,17 @@ import { getDateTimeValue } from "./date_time.js";
|
|
|
10
10
|
import drawRectangle from "./drawRect.js";
|
|
11
11
|
//import { closeUnexpectedPopups } from "./popups.js";
|
|
12
12
|
import { getTableCells, getTableData } from "./table_analyze.js";
|
|
13
|
-
import { maskValue, replaceWithLocalTestData } from "./utils.js";
|
|
13
|
+
import { _convertToRegexQuery, _copyContext, _fixLocatorUsingParams, _fixUsingParams, _getServerUrl, KEYBOARD_EVENTS, maskValue, replaceWithLocalTestData, scrollPageToLoadLazyElements, unEscapeString, } from "./utils.js";
|
|
14
14
|
import csv from "csv-parser";
|
|
15
15
|
import { Readable } from "node:stream";
|
|
16
16
|
import readline from "readline";
|
|
17
17
|
import { getContext } from "./init_browser.js";
|
|
18
18
|
import { locate_element } from "./locate_element.js";
|
|
19
|
-
import {
|
|
19
|
+
import { randomUUID } from "crypto";
|
|
20
|
+
import { _commandError, _commandFinally, _preCommand, _validateSelectors, _screenshot, _reportToWorld, } from "./command_common.js";
|
|
20
21
|
import { registerDownloadEvent, registerNetworkEvents } from "./network.js";
|
|
21
|
-
|
|
22
|
+
import { LocatorLog } from "./locator_log.js";
|
|
23
|
+
export const Types = {
|
|
22
24
|
CLICK: "click_element",
|
|
23
25
|
NAVIGATE: "navigate",
|
|
24
26
|
FILL: "fill_element",
|
|
@@ -29,6 +31,8 @@ const Types = {
|
|
|
29
31
|
GET_PAGE_STATUS: "get_page_status",
|
|
30
32
|
CLICK_ROW_ACTION: "click_row_action",
|
|
31
33
|
VERIFY_ELEMENT_CONTAINS_TEXT: "verify_element_contains_text",
|
|
34
|
+
VERIFY_PAGE_CONTAINS_TEXT: "verify_page_contains_text",
|
|
35
|
+
VERIFY_PAGE_CONTAINS_NO_TEXT: "verify_page_contains_no_text",
|
|
32
36
|
ANALYZE_TABLE: "analyze_table",
|
|
33
37
|
SELECT: "select_combobox",
|
|
34
38
|
VERIFY_PAGE_PATH: "verify_page_path",
|
|
@@ -45,6 +49,8 @@ const Types = {
|
|
|
45
49
|
LOAD_DATA: "load_data",
|
|
46
50
|
SET_INPUT: "set_input",
|
|
47
51
|
WAIT_FOR_TEXT_TO_DISAPPEAR: "wait_for_text_to_disappear",
|
|
52
|
+
VERIFY_ATTRIBUTE: "verify_element_attribute",
|
|
53
|
+
VERIFY_TEXT_WITH_RELATION: "verify_text_with_relation",
|
|
48
54
|
};
|
|
49
55
|
export const apps = {};
|
|
50
56
|
class StableBrowser {
|
|
@@ -58,6 +64,7 @@ class StableBrowser {
|
|
|
58
64
|
networkLogger = null;
|
|
59
65
|
configuration = null;
|
|
60
66
|
appName = "main";
|
|
67
|
+
tags = null;
|
|
61
68
|
constructor(browser, page, logger = null, context = null, world = null) {
|
|
62
69
|
this.browser = browser;
|
|
63
70
|
this.page = page;
|
|
@@ -88,17 +95,17 @@ class StableBrowser {
|
|
|
88
95
|
catch (e) {
|
|
89
96
|
this.logger.error("unable to read ai_config.json");
|
|
90
97
|
}
|
|
91
|
-
context.pageLoading = { status: false };
|
|
92
|
-
context.pages = [this.page];
|
|
93
98
|
const logFolder = path.join(this.project_path, "logs", "web");
|
|
94
99
|
this.world = world;
|
|
100
|
+
context.pages = [this.page];
|
|
101
|
+
context.pageLoading = { status: false };
|
|
95
102
|
this.registerEventListeners(this.context);
|
|
96
103
|
registerNetworkEvents(this.world, this, this.context, this.page);
|
|
97
104
|
registerDownloadEvent(this.page, this.world, this.context);
|
|
98
105
|
}
|
|
99
106
|
registerEventListeners(context) {
|
|
100
107
|
this.registerConsoleLogListener(this.page, context);
|
|
101
|
-
this.registerRequestListener(this.page, context, this.webLogFile);
|
|
108
|
+
// this.registerRequestListener(this.page, context, this.webLogFile);
|
|
102
109
|
if (!context.pageLoading) {
|
|
103
110
|
context.pageLoading = { status: false };
|
|
104
111
|
}
|
|
@@ -143,7 +150,7 @@ class StableBrowser {
|
|
|
143
150
|
if (this.appName === appName) {
|
|
144
151
|
return;
|
|
145
152
|
}
|
|
146
|
-
let
|
|
153
|
+
let navigate = false;
|
|
147
154
|
if (!apps[appName]) {
|
|
148
155
|
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this, -1, this.context.reportFolder);
|
|
149
156
|
newContextCreated = true;
|
|
@@ -154,32 +161,15 @@ class StableBrowser {
|
|
|
154
161
|
};
|
|
155
162
|
}
|
|
156
163
|
const tempContext = {};
|
|
157
|
-
|
|
158
|
-
|
|
164
|
+
_copyContext(this, tempContext);
|
|
165
|
+
_copyContext(apps[appName], this);
|
|
159
166
|
apps[this.appName] = tempContext;
|
|
160
167
|
this.appName = appName;
|
|
161
|
-
if (
|
|
162
|
-
this.registerEventListeners(this.context);
|
|
168
|
+
if (navigate) {
|
|
163
169
|
await this.goto(this.context.environment.baseUrl);
|
|
164
170
|
await this.waitForPageLoad();
|
|
165
171
|
}
|
|
166
172
|
}
|
|
167
|
-
_copyContext(from, to) {
|
|
168
|
-
to.browser = from.browser;
|
|
169
|
-
to.page = from.page;
|
|
170
|
-
to.context = from.context;
|
|
171
|
-
}
|
|
172
|
-
getWebLogFile(logFolder) {
|
|
173
|
-
if (!fs.existsSync(logFolder)) {
|
|
174
|
-
fs.mkdirSync(logFolder, { recursive: true });
|
|
175
|
-
}
|
|
176
|
-
let nextIndex = 1;
|
|
177
|
-
while (fs.existsSync(path.join(logFolder, nextIndex.toString() + ".json"))) {
|
|
178
|
-
nextIndex++;
|
|
179
|
-
}
|
|
180
|
-
const fileName = nextIndex + ".json";
|
|
181
|
-
return path.join(logFolder, fileName);
|
|
182
|
-
}
|
|
183
173
|
registerConsoleLogListener(page, context) {
|
|
184
174
|
if (!this.context.webLogger) {
|
|
185
175
|
this.context.webLogger = [];
|
|
@@ -229,7 +219,7 @@ class StableBrowser {
|
|
|
229
219
|
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
|
|
230
220
|
}
|
|
231
221
|
catch (error) {
|
|
232
|
-
console.error("Error in request listener", error);
|
|
222
|
+
// console.error("Error in request listener", error);
|
|
233
223
|
context.networkLogger.push({
|
|
234
224
|
error: "not able to listen",
|
|
235
225
|
message: error.message,
|
|
@@ -243,55 +233,48 @@ class StableBrowser {
|
|
|
243
233
|
// async closeUnexpectedPopups() {
|
|
244
234
|
// await closeUnexpectedPopups(this.page);
|
|
245
235
|
// }
|
|
246
|
-
async goto(url) {
|
|
236
|
+
async goto(url, world = null) {
|
|
247
237
|
if (!url.startsWith("http")) {
|
|
248
238
|
url = "https://" + url;
|
|
249
239
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
240
|
+
const state = {
|
|
241
|
+
value: url,
|
|
242
|
+
world: world,
|
|
243
|
+
type: Types.NAVIGATE,
|
|
244
|
+
text: `Navigate Page to: ${url}`,
|
|
245
|
+
operation: "goto",
|
|
246
|
+
log: "***** navigate page to " + url + " *****\n",
|
|
247
|
+
info: {},
|
|
248
|
+
locate: false,
|
|
249
|
+
scroll: false,
|
|
250
|
+
screenshot: false,
|
|
251
|
+
highlight: false,
|
|
252
|
+
};
|
|
253
|
+
try {
|
|
254
|
+
await _preCommand(state, this);
|
|
255
|
+
await this.page.goto(url, {
|
|
256
|
+
timeout: 60000,
|
|
257
|
+
});
|
|
258
|
+
await _screenshot(state, this);
|
|
257
259
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
// remove the _ prefix
|
|
262
|
-
regValue = key.substring(1);
|
|
263
|
-
}
|
|
264
|
-
text = text.replaceAll(new RegExp("{" + regValue + "}", "g"), _params[key]);
|
|
260
|
+
catch (error) {
|
|
261
|
+
console.error("Error on goto", error);
|
|
262
|
+
_commandError(state, error, this);
|
|
265
263
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
_fixLocatorUsingParams(locator, _params) {
|
|
269
|
-
// check if not null
|
|
270
|
-
if (!locator) {
|
|
271
|
-
return locator;
|
|
264
|
+
finally {
|
|
265
|
+
_commandFinally(state, this);
|
|
272
266
|
}
|
|
273
|
-
// clone the locator
|
|
274
|
-
locator = JSON.parse(JSON.stringify(locator));
|
|
275
|
-
this.scanAndManipulate(locator, _params);
|
|
276
|
-
return locator;
|
|
277
|
-
}
|
|
278
|
-
_isObject(value) {
|
|
279
|
-
return value && typeof value === "object" && value.constructor === Object;
|
|
280
267
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
this.scanAndManipulate(currentObj[key], _params);
|
|
268
|
+
async _getLocator(locator, scope, _params) {
|
|
269
|
+
locator = _fixLocatorUsingParams(locator, _params);
|
|
270
|
+
// locator = await this._replaceWithLocalData(locator);
|
|
271
|
+
for (let key in locator) {
|
|
272
|
+
if (typeof locator[key] !== "string")
|
|
273
|
+
continue;
|
|
274
|
+
if (locator[key].includes("{{") && locator[key].includes("}}")) {
|
|
275
|
+
locator[key] = await this._replaceWithLocalData(locator[key], this.world);
|
|
290
276
|
}
|
|
291
277
|
}
|
|
292
|
-
}
|
|
293
|
-
_getLocator(locator, scope, _params) {
|
|
294
|
-
locator = this._fixLocatorUsingParams(locator, _params);
|
|
295
278
|
let locatorReturn;
|
|
296
279
|
if (locator.role) {
|
|
297
280
|
if (locator.role[1].nameReg) {
|
|
@@ -299,7 +282,7 @@ class StableBrowser {
|
|
|
299
282
|
delete locator.role[1].nameReg;
|
|
300
283
|
}
|
|
301
284
|
// if (locator.role[1].name) {
|
|
302
|
-
// locator.role[1].name =
|
|
285
|
+
// locator.role[1].name = _fixUsingParams(locator.role[1].name, _params);
|
|
303
286
|
// }
|
|
304
287
|
locatorReturn = scope.getByRole(locator.role[0], locator.role[1]);
|
|
305
288
|
}
|
|
@@ -342,140 +325,54 @@ class StableBrowser {
|
|
|
342
325
|
if (css && css.locator) {
|
|
343
326
|
css = css.locator;
|
|
344
327
|
}
|
|
345
|
-
let result = await this._locateElementByText(scope,
|
|
328
|
+
let result = await this._locateElementByText(scope, _fixUsingParams(text, _params), "*:not(script, style, head)", false, false, true, _params);
|
|
346
329
|
if (result.elementCount === 0) {
|
|
347
330
|
return;
|
|
348
331
|
}
|
|
349
|
-
let textElementCss = "[data-blinq-id
|
|
332
|
+
let textElementCss = "[data-blinq-id-" + result.randomToken + "]";
|
|
350
333
|
// css climb to parent element
|
|
351
334
|
const climbArray = [];
|
|
352
335
|
for (let i = 0; i < climb; i++) {
|
|
353
336
|
climbArray.push("..");
|
|
354
337
|
}
|
|
355
338
|
let climbXpath = "xpath=" + climbArray.join("/");
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
return
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
339
|
+
let resultCss = textElementCss + " >> " + climbXpath;
|
|
340
|
+
if (css) {
|
|
341
|
+
resultCss = resultCss + " >> " + css;
|
|
342
|
+
}
|
|
343
|
+
return resultCss;
|
|
344
|
+
}
|
|
345
|
+
async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, ignoreCase = true, _params) {
|
|
346
|
+
const query = _convertToRegexQuery(text1, regex1, !partial1, ignoreCase);
|
|
347
|
+
const locator = scope.locator(query);
|
|
348
|
+
const count = await locator.count();
|
|
349
|
+
if (!tag1) {
|
|
350
|
+
tag1 = "*";
|
|
351
|
+
}
|
|
352
|
+
const randomToken = Math.random().toString(36).substring(7);
|
|
353
|
+
let tagCount = 0;
|
|
354
|
+
for (let i = 0; i < count; i++) {
|
|
355
|
+
const element = locator.nth(i);
|
|
356
|
+
// check if the tag matches
|
|
357
|
+
if (!(await element.evaluate((el, [tag, randomToken]) => {
|
|
358
|
+
if (!tag.startsWith("*")) {
|
|
359
|
+
if (el.tagName.toLowerCase() !== tag) {
|
|
360
|
+
return false;
|
|
366
361
|
}
|
|
367
|
-
currentNode = currentNode.parentNode;
|
|
368
|
-
}
|
|
369
|
-
return false;
|
|
370
|
-
}
|
|
371
|
-
document.isParent = isParent;
|
|
372
|
-
function getRegex(str) {
|
|
373
|
-
const match = str.match(/^\/(.*?)\/([gimuy]*)$/);
|
|
374
|
-
if (!match) {
|
|
375
|
-
return null;
|
|
376
|
-
}
|
|
377
|
-
let [_, pattern, flags] = match;
|
|
378
|
-
return new RegExp(pattern, flags);
|
|
379
|
-
}
|
|
380
|
-
document.getRegex = getRegex;
|
|
381
|
-
function collectAllShadowDomElements(element, result = []) {
|
|
382
|
-
// Check and add the element if it has a shadow root
|
|
383
|
-
if (element.shadowRoot) {
|
|
384
|
-
result.push(element);
|
|
385
|
-
// Also search within the shadow root
|
|
386
|
-
document.collectAllShadowDomElements(element.shadowRoot, result);
|
|
387
|
-
}
|
|
388
|
-
// Iterate over child nodes
|
|
389
|
-
element.childNodes.forEach((child) => {
|
|
390
|
-
// Recursively call the function for each child node
|
|
391
|
-
document.collectAllShadowDomElements(child, result);
|
|
392
|
-
});
|
|
393
|
-
return result;
|
|
394
|
-
}
|
|
395
|
-
document.collectAllShadowDomElements = collectAllShadowDomElements;
|
|
396
|
-
if (!tag) {
|
|
397
|
-
tag = "*:not(script, style, head)";
|
|
398
|
-
}
|
|
399
|
-
let regexpSearch = document.getRegex(text);
|
|
400
|
-
if (regexpSearch) {
|
|
401
|
-
regex = true;
|
|
402
|
-
}
|
|
403
|
-
let elements = Array.from(document.querySelectorAll(tag));
|
|
404
|
-
let shadowHosts = [];
|
|
405
|
-
document.collectAllShadowDomElements(document, shadowHosts);
|
|
406
|
-
for (let i = 0; i < shadowHosts.length; i++) {
|
|
407
|
-
let shadowElement = shadowHosts[i].shadowRoot;
|
|
408
|
-
if (!shadowElement) {
|
|
409
|
-
console.log("shadowElement is null, for host " + shadowHosts[i]);
|
|
410
|
-
continue;
|
|
411
362
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
}
|
|
415
|
-
let randomToken = null;
|
|
416
|
-
const foundElements = [];
|
|
417
|
-
if (regex) {
|
|
418
|
-
if (!regexpSearch) {
|
|
419
|
-
regexpSearch = new RegExp(text, "im");
|
|
420
|
-
}
|
|
421
|
-
for (let i = 0; i < elements.length; i++) {
|
|
422
|
-
const element = elements[i];
|
|
423
|
-
if ((element.innerText && regexpSearch.test(element.innerText)) ||
|
|
424
|
-
(element.value && regexpSearch.test(element.value))) {
|
|
425
|
-
foundElements.push(element);
|
|
426
|
-
}
|
|
363
|
+
if (!el.setAttribute) {
|
|
364
|
+
el = el.parentElement;
|
|
427
365
|
}
|
|
366
|
+
el.setAttribute("data-blinq-id-" + randomToken, "");
|
|
367
|
+
return true;
|
|
368
|
+
}, [tag1, randomToken]))) {
|
|
369
|
+
continue;
|
|
428
370
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
const element = elements[i];
|
|
433
|
-
if (partial) {
|
|
434
|
-
if ((element.innerText && element.innerText.toLowerCase().trim().includes(text.toLowerCase())) ||
|
|
435
|
-
(element.value && element.value.toLowerCase().includes(text.toLowerCase()))) {
|
|
436
|
-
foundElements.push(element);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
else {
|
|
440
|
-
if ((element.innerText && element.innerText.trim() === text) ||
|
|
441
|
-
(element.value && element.value === text)) {
|
|
442
|
-
foundElements.push(element);
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
let noChildElements = [];
|
|
448
|
-
for (let i = 0; i < foundElements.length; i++) {
|
|
449
|
-
let element = foundElements[i];
|
|
450
|
-
let hasChild = false;
|
|
451
|
-
for (let j = 0; j < foundElements.length; j++) {
|
|
452
|
-
if (i === j) {
|
|
453
|
-
continue;
|
|
454
|
-
}
|
|
455
|
-
if (isParent(element, foundElements[j])) {
|
|
456
|
-
hasChild = true;
|
|
457
|
-
break;
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
if (!hasChild) {
|
|
461
|
-
noChildElements.push(element);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
let elementCount = 0;
|
|
465
|
-
if (noChildElements.length > 0) {
|
|
466
|
-
for (let i = 0; i < noChildElements.length; i++) {
|
|
467
|
-
if (randomToken === null) {
|
|
468
|
-
randomToken = Math.random().toString(36).substring(7);
|
|
469
|
-
}
|
|
470
|
-
let element = noChildElements[i];
|
|
471
|
-
element.setAttribute("data-blinq-id", "blinq-id-" + randomToken);
|
|
472
|
-
elementCount++;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
return { elementCount: elementCount, randomToken: randomToken };
|
|
476
|
-
}, [text1, tag1, regex1, partial1]);
|
|
371
|
+
tagCount++;
|
|
372
|
+
}
|
|
373
|
+
return { elementCount: tagCount, randomToken };
|
|
477
374
|
}
|
|
478
|
-
async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
|
|
375
|
+
async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true, allowDisabled = false) {
|
|
479
376
|
if (!info) {
|
|
480
377
|
info = {};
|
|
481
378
|
}
|
|
@@ -484,10 +381,13 @@ class StableBrowser {
|
|
|
484
381
|
}
|
|
485
382
|
if (!info.log) {
|
|
486
383
|
info.log = "";
|
|
384
|
+
info.locatorLog = new LocatorLog(selectorHierarchy);
|
|
487
385
|
}
|
|
488
386
|
let locatorSearch = selectorHierarchy[index];
|
|
387
|
+
let originalLocatorSearch = "";
|
|
489
388
|
try {
|
|
490
|
-
|
|
389
|
+
originalLocatorSearch = _fixUsingParams(JSON.stringify(locatorSearch), _params);
|
|
390
|
+
locatorSearch = JSON.parse(originalLocatorSearch);
|
|
491
391
|
}
|
|
492
392
|
catch (e) {
|
|
493
393
|
console.error(e);
|
|
@@ -501,24 +401,24 @@ class StableBrowser {
|
|
|
501
401
|
info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
|
|
502
402
|
return;
|
|
503
403
|
}
|
|
504
|
-
locator = this._getLocator({ css: locatorString }, scope, _params);
|
|
404
|
+
locator = await this._getLocator({ css: locatorString }, scope, _params);
|
|
505
405
|
}
|
|
506
406
|
else if (locatorSearch.text) {
|
|
507
|
-
let text =
|
|
508
|
-
let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
|
|
407
|
+
let text = _fixUsingParams(locatorSearch.text, _params);
|
|
408
|
+
let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, true, _params);
|
|
509
409
|
if (result.elementCount === 0) {
|
|
510
410
|
info.failCause.textNotFound = true;
|
|
511
411
|
info.failCause.lastError = "failed to locate element by text: " + text;
|
|
512
412
|
return;
|
|
513
413
|
}
|
|
514
|
-
locatorSearch.css = "[data-blinq-id
|
|
414
|
+
locatorSearch.css = "[data-blinq-id-" + result.randomToken + "]";
|
|
515
415
|
if (locatorSearch.childCss) {
|
|
516
416
|
locatorSearch.css = locatorSearch.css + " " + locatorSearch.childCss;
|
|
517
417
|
}
|
|
518
|
-
locator = this._getLocator(locatorSearch, scope, _params);
|
|
418
|
+
locator = await this._getLocator(locatorSearch, scope, _params);
|
|
519
419
|
}
|
|
520
420
|
else {
|
|
521
|
-
locator = this._getLocator(locatorSearch, scope, _params);
|
|
421
|
+
locator = await this._getLocator(locatorSearch, scope, _params);
|
|
522
422
|
}
|
|
523
423
|
// let cssHref = false;
|
|
524
424
|
// if (locatorSearch.css && locatorSearch.css.includes("href=")) {
|
|
@@ -531,18 +431,27 @@ class StableBrowser {
|
|
|
531
431
|
//info.log += "total elements found " + count + "\n";
|
|
532
432
|
//let visibleCount = 0;
|
|
533
433
|
let visibleLocator = null;
|
|
534
|
-
if (locatorSearch.index && locatorSearch.index < count) {
|
|
434
|
+
if (typeof locatorSearch.index === "number" && locatorSearch.index < count) {
|
|
535
435
|
foundLocators.push(locator.nth(locatorSearch.index));
|
|
436
|
+
if (info.locatorLog) {
|
|
437
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND");
|
|
438
|
+
}
|
|
536
439
|
return;
|
|
537
440
|
}
|
|
441
|
+
if (info.locatorLog && count === 0) {
|
|
442
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "NOT_FOUND");
|
|
443
|
+
}
|
|
538
444
|
for (let j = 0; j < count; j++) {
|
|
539
445
|
let visible = await locator.nth(j).isVisible();
|
|
540
446
|
const enabled = await locator.nth(j).isEnabled();
|
|
541
447
|
if (!visibleOnly) {
|
|
542
448
|
visible = true;
|
|
543
449
|
}
|
|
544
|
-
if (visible && enabled) {
|
|
450
|
+
if (visible && (allowDisabled || enabled)) {
|
|
545
451
|
foundLocators.push(locator.nth(j));
|
|
452
|
+
if (info.locatorLog) {
|
|
453
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND");
|
|
454
|
+
}
|
|
546
455
|
}
|
|
547
456
|
else {
|
|
548
457
|
info.failCause.visible = visible;
|
|
@@ -550,8 +459,16 @@ class StableBrowser {
|
|
|
550
459
|
if (!info.printMessages) {
|
|
551
460
|
info.printMessages = {};
|
|
552
461
|
}
|
|
462
|
+
if (info.locatorLog && !visible) {
|
|
463
|
+
info.failCause.lastError = "element " + originalLocatorSearch + " is not visible";
|
|
464
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND_NOT_VISIBLE");
|
|
465
|
+
}
|
|
466
|
+
if (info.locatorLog && !enabled) {
|
|
467
|
+
info.failCause.lastError = "element " + originalLocatorSearch + " is disabled, ";
|
|
468
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND_NOT_ENABLED");
|
|
469
|
+
}
|
|
553
470
|
if (!info.printMessages[j.toString()]) {
|
|
554
|
-
info.log += "element " + locator + " visible " + visible + " enabled " + enabled + "\n";
|
|
471
|
+
//info.log += "element " + locator + " visible " + visible + " enabled " + enabled + "\n";
|
|
555
472
|
info.printMessages[j.toString()] = true;
|
|
556
473
|
}
|
|
557
474
|
}
|
|
@@ -567,7 +484,7 @@ class StableBrowser {
|
|
|
567
484
|
if (!info) {
|
|
568
485
|
info = {};
|
|
569
486
|
}
|
|
570
|
-
info.log += "scan for popup handlers" + "\n";
|
|
487
|
+
//info.log += "scan for popup handlers" + "\n";
|
|
571
488
|
const handlerGroup = [];
|
|
572
489
|
for (let i = 0; i < this.configuration.popupHandlers.length; i++) {
|
|
573
490
|
handlerGroup.push(this.configuration.popupHandlers[i].locator);
|
|
@@ -594,16 +511,28 @@ class StableBrowser {
|
|
|
594
511
|
}
|
|
595
512
|
if (result.foundElements.length > 0) {
|
|
596
513
|
let dialogCloseLocator = result.foundElements[0].locator;
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
514
|
+
try {
|
|
515
|
+
await scope?.evaluate(() => {
|
|
516
|
+
window.__isClosingPopups = true;
|
|
517
|
+
});
|
|
518
|
+
await dialogCloseLocator.click();
|
|
519
|
+
// wait for the dialog to close
|
|
520
|
+
await dialogCloseLocator.waitFor({ state: "hidden" });
|
|
521
|
+
}
|
|
522
|
+
catch (e) {
|
|
523
|
+
}
|
|
524
|
+
finally {
|
|
525
|
+
await scope?.evaluate(() => {
|
|
526
|
+
window.__isClosingPopups = false;
|
|
527
|
+
});
|
|
528
|
+
}
|
|
600
529
|
return { rerun: true };
|
|
601
530
|
}
|
|
602
531
|
}
|
|
603
532
|
}
|
|
604
533
|
return { rerun: false };
|
|
605
534
|
}
|
|
606
|
-
async _locate(selectors, info, _params, timeout) {
|
|
535
|
+
async _locate(selectors, info, _params, timeout, allowDisabled = false) {
|
|
607
536
|
if (!timeout) {
|
|
608
537
|
timeout = 30000;
|
|
609
538
|
}
|
|
@@ -613,7 +542,7 @@ class StableBrowser {
|
|
|
613
542
|
let selector = selectors.locators[j];
|
|
614
543
|
info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
|
|
615
544
|
}
|
|
616
|
-
let element = await this._locate_internal(selectors, info, _params, timeout);
|
|
545
|
+
let element = await this._locate_internal(selectors, info, _params, timeout, allowDisabled);
|
|
617
546
|
if (!element.rerun) {
|
|
618
547
|
return element;
|
|
619
548
|
}
|
|
@@ -626,6 +555,7 @@ class StableBrowser {
|
|
|
626
555
|
info.failCause = {};
|
|
627
556
|
info.log = "";
|
|
628
557
|
}
|
|
558
|
+
let startTime = Date.now();
|
|
629
559
|
let scope = this.page;
|
|
630
560
|
if (selectors.frame) {
|
|
631
561
|
return selectors.frame;
|
|
@@ -656,9 +586,11 @@ class StableBrowser {
|
|
|
656
586
|
}
|
|
657
587
|
return framescope;
|
|
658
588
|
};
|
|
589
|
+
let fLocator = null;
|
|
659
590
|
while (true) {
|
|
660
591
|
let frameFound = false;
|
|
661
592
|
if (selectors.nestFrmLoc) {
|
|
593
|
+
fLocator = selectors.nestFrmLoc;
|
|
662
594
|
scope = await findFrame(selectors.nestFrmLoc, scope);
|
|
663
595
|
frameFound = true;
|
|
664
596
|
break;
|
|
@@ -667,6 +599,7 @@ class StableBrowser {
|
|
|
667
599
|
for (let i = 0; i < selectors.frameLocators.length; i++) {
|
|
668
600
|
let frameLocator = selectors.frameLocators[i];
|
|
669
601
|
if (frameLocator.css) {
|
|
602
|
+
fLocator = frameLocator.css;
|
|
670
603
|
scope = scope.frameLocator(frameLocator.css);
|
|
671
604
|
frameFound = true;
|
|
672
605
|
break;
|
|
@@ -674,11 +607,15 @@ class StableBrowser {
|
|
|
674
607
|
}
|
|
675
608
|
}
|
|
676
609
|
if (!frameFound && selectors.iframe_src) {
|
|
610
|
+
fLocator = selectors.iframe_src;
|
|
677
611
|
scope = this.page.frame({ url: selectors.iframe_src });
|
|
678
612
|
}
|
|
679
613
|
if (!scope) {
|
|
680
|
-
info
|
|
681
|
-
|
|
614
|
+
if (info && info.locatorLog) {
|
|
615
|
+
info.locatorLog.setLocatorSearchStatus("frame-" + fLocator, "NOT_FOUND");
|
|
616
|
+
}
|
|
617
|
+
//info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
|
|
618
|
+
if (Date.now() - startTime > timeout) {
|
|
682
619
|
info.failCause.iframeNotFound = true;
|
|
683
620
|
info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
|
|
684
621
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
@@ -686,6 +623,9 @@ class StableBrowser {
|
|
|
686
623
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
687
624
|
}
|
|
688
625
|
else {
|
|
626
|
+
if (info && info.locatorLog) {
|
|
627
|
+
info.locatorLog.setLocatorSearchStatus("frame-" + fLocator, "FOUND");
|
|
628
|
+
}
|
|
689
629
|
break;
|
|
690
630
|
}
|
|
691
631
|
}
|
|
@@ -702,16 +642,18 @@ class StableBrowser {
|
|
|
702
642
|
return bodyContent;
|
|
703
643
|
});
|
|
704
644
|
}
|
|
705
|
-
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
645
|
+
async _locate_internal(selectors, info, _params, timeout = 30000, allowDisabled = false) {
|
|
706
646
|
if (!info) {
|
|
707
647
|
info = {};
|
|
708
648
|
info.failCause = {};
|
|
709
649
|
info.log = "";
|
|
650
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
710
651
|
}
|
|
711
652
|
let highPriorityTimeout = 5000;
|
|
712
653
|
let visibleOnlyTimeout = 6000;
|
|
713
|
-
let startTime =
|
|
654
|
+
let startTime = Date.now();
|
|
714
655
|
let locatorsCount = 0;
|
|
656
|
+
let lazy_scroll = false;
|
|
715
657
|
//let arrayMode = Array.isArray(selectors);
|
|
716
658
|
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
717
659
|
let selectorsLocators = null;
|
|
@@ -749,17 +691,17 @@ class StableBrowser {
|
|
|
749
691
|
}
|
|
750
692
|
// info.log += "scanning locators in priority 1" + "\n";
|
|
751
693
|
let onlyPriority3 = selectorsLocators[0].priority === 3;
|
|
752
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["1"], scope, _params, info, visibleOnly);
|
|
694
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["1"], scope, _params, info, visibleOnly, allowDisabled);
|
|
753
695
|
if (result.foundElements.length === 0) {
|
|
754
696
|
// info.log += "scanning locators in priority 2" + "\n";
|
|
755
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["2"], scope, _params, info, visibleOnly);
|
|
697
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["2"], scope, _params, info, visibleOnly, allowDisabled);
|
|
756
698
|
}
|
|
757
699
|
if (result.foundElements.length === 0 && onlyPriority3) {
|
|
758
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly);
|
|
700
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly, allowDisabled);
|
|
759
701
|
}
|
|
760
702
|
else {
|
|
761
703
|
if (result.foundElements.length === 0 && !highPriorityOnly) {
|
|
762
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly);
|
|
704
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly, allowDisabled);
|
|
763
705
|
}
|
|
764
706
|
}
|
|
765
707
|
let foundElements = result.foundElements;
|
|
@@ -800,26 +742,38 @@ class StableBrowser {
|
|
|
800
742
|
return maxCountElement.locator;
|
|
801
743
|
}
|
|
802
744
|
}
|
|
803
|
-
if (
|
|
745
|
+
if (Date.now() - startTime > timeout) {
|
|
804
746
|
break;
|
|
805
747
|
}
|
|
806
|
-
if (
|
|
807
|
-
info.log += "high priority timeout, will try all elements" + "\n";
|
|
748
|
+
if (Date.now() - startTime > highPriorityTimeout) {
|
|
749
|
+
//info.log += "high priority timeout, will try all elements" + "\n";
|
|
808
750
|
highPriorityOnly = false;
|
|
751
|
+
if (this.configuration && this.configuration.load_all_lazy === true && !lazy_scroll) {
|
|
752
|
+
lazy_scroll = true;
|
|
753
|
+
await scrollPageToLoadLazyElements(this.page);
|
|
754
|
+
}
|
|
809
755
|
}
|
|
810
|
-
if (
|
|
811
|
-
info.log += "visible only timeout, will try all elements" + "\n";
|
|
756
|
+
if (Date.now() - startTime > visibleOnlyTimeout) {
|
|
757
|
+
//info.log += "visible only timeout, will try all elements" + "\n";
|
|
812
758
|
visibleOnly = false;
|
|
813
759
|
}
|
|
814
760
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
815
761
|
}
|
|
816
762
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
817
|
-
info.
|
|
763
|
+
// if (info.locatorLog) {
|
|
764
|
+
// const lines = info.locatorLog.toString().split("\n");
|
|
765
|
+
// for (let line of lines) {
|
|
766
|
+
// this.logger.debug(line);
|
|
767
|
+
// }
|
|
768
|
+
// }
|
|
769
|
+
//info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
|
|
818
770
|
info.failCause.locatorNotFound = true;
|
|
819
|
-
info
|
|
771
|
+
if (!info?.failCause?.lastError) {
|
|
772
|
+
info.failCause.lastError = "failed to locate unique element";
|
|
773
|
+
}
|
|
820
774
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
821
775
|
}
|
|
822
|
-
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
776
|
+
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly, allowDisabled = false) {
|
|
823
777
|
let foundElements = [];
|
|
824
778
|
const result = {
|
|
825
779
|
foundElements: foundElements,
|
|
@@ -827,14 +781,15 @@ class StableBrowser {
|
|
|
827
781
|
for (let i = 0; i < locatorsGroup.length; i++) {
|
|
828
782
|
let foundLocators = [];
|
|
829
783
|
try {
|
|
830
|
-
await this._collectLocatorInformation(locatorsGroup, i, scope, foundLocators, _params, info, visibleOnly);
|
|
784
|
+
await this._collectLocatorInformation(locatorsGroup, i, scope, foundLocators, _params, info, visibleOnly, allowDisabled);
|
|
831
785
|
}
|
|
832
786
|
catch (e) {
|
|
833
|
-
this
|
|
834
|
-
this.logger.debug(
|
|
787
|
+
// this call can fail it the browser is navigating
|
|
788
|
+
// this.logger.debug("unable to use locator " + JSON.stringify(locatorsGroup[i]));
|
|
789
|
+
// this.logger.debug(e);
|
|
835
790
|
foundLocators = [];
|
|
836
791
|
try {
|
|
837
|
-
await this._collectLocatorInformation(locatorsGroup, i, this.page, foundLocators, _params, info, visibleOnly);
|
|
792
|
+
await this._collectLocatorInformation(locatorsGroup, i, this.page, foundLocators, _params, info, visibleOnly, allowDisabled);
|
|
838
793
|
}
|
|
839
794
|
catch (e) {
|
|
840
795
|
this.logger.info("unable to use locator (second try) " + JSON.stringify(locatorsGroup[i]));
|
|
@@ -850,11 +805,27 @@ class StableBrowser {
|
|
|
850
805
|
}
|
|
851
806
|
if (foundLocators.length > 1) {
|
|
852
807
|
info.failCause.foundMultiple = true;
|
|
808
|
+
if (info.locatorLog) {
|
|
809
|
+
info.locatorLog.setLocatorSearchStatus(JSON.stringify(locatorsGroup[i]), "FOUND_NOT_UNIQUE");
|
|
810
|
+
}
|
|
853
811
|
}
|
|
854
812
|
}
|
|
855
813
|
return result;
|
|
856
814
|
}
|
|
857
815
|
async simpleClick(elementDescription, _params, options = {}, world = null) {
|
|
816
|
+
const state = {
|
|
817
|
+
locate: false,
|
|
818
|
+
scroll: false,
|
|
819
|
+
highlight: false,
|
|
820
|
+
_params,
|
|
821
|
+
options,
|
|
822
|
+
world,
|
|
823
|
+
type: Types.CLICK,
|
|
824
|
+
text: "Click element",
|
|
825
|
+
operation: "simpleClick",
|
|
826
|
+
log: "***** click on " + elementDescription + " *****\n",
|
|
827
|
+
};
|
|
828
|
+
_preCommand(state, this);
|
|
858
829
|
const startTime = Date.now();
|
|
859
830
|
let timeout = 30000;
|
|
860
831
|
if (options && options.timeout) {
|
|
@@ -879,13 +850,31 @@ class StableBrowser {
|
|
|
879
850
|
catch (e) {
|
|
880
851
|
if (performance.now() - startTime > timeout) {
|
|
881
852
|
// throw e;
|
|
882
|
-
|
|
853
|
+
try {
|
|
854
|
+
await _commandError(state, "timeout looking for " + elementDescription, this);
|
|
855
|
+
}
|
|
856
|
+
finally {
|
|
857
|
+
_commandFinally(state, this);
|
|
858
|
+
}
|
|
883
859
|
}
|
|
884
860
|
}
|
|
885
861
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
886
862
|
}
|
|
887
863
|
}
|
|
888
864
|
async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
|
|
865
|
+
const state = {
|
|
866
|
+
locate: false,
|
|
867
|
+
scroll: false,
|
|
868
|
+
highlight: false,
|
|
869
|
+
_params,
|
|
870
|
+
options,
|
|
871
|
+
world,
|
|
872
|
+
type: Types.FILL,
|
|
873
|
+
text: "Fill element",
|
|
874
|
+
operation: "simpleClickType",
|
|
875
|
+
log: "***** click type on " + elementDescription + " *****\n",
|
|
876
|
+
};
|
|
877
|
+
_preCommand(state, this);
|
|
889
878
|
const startTime = Date.now();
|
|
890
879
|
let timeout = 30000;
|
|
891
880
|
if (options && options.timeout) {
|
|
@@ -910,7 +899,12 @@ class StableBrowser {
|
|
|
910
899
|
catch (e) {
|
|
911
900
|
if (performance.now() - startTime > timeout) {
|
|
912
901
|
// throw e;
|
|
913
|
-
|
|
902
|
+
try {
|
|
903
|
+
await _commandError(state, "timeout looking for " + elementDescription, this);
|
|
904
|
+
}
|
|
905
|
+
finally {
|
|
906
|
+
_commandFinally(state, this);
|
|
907
|
+
}
|
|
914
908
|
}
|
|
915
909
|
}
|
|
916
910
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
@@ -929,18 +923,18 @@ class StableBrowser {
|
|
|
929
923
|
};
|
|
930
924
|
try {
|
|
931
925
|
await _preCommand(state, this);
|
|
932
|
-
if (state.options && state.options.context) {
|
|
933
|
-
|
|
934
|
-
}
|
|
926
|
+
// if (state.options && state.options.context) {
|
|
927
|
+
// state.selectors.locators[0].text = state.options.context;
|
|
928
|
+
// }
|
|
935
929
|
try {
|
|
936
930
|
await state.element.click();
|
|
937
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
931
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
938
932
|
}
|
|
939
933
|
catch (e) {
|
|
940
934
|
// await this.closeUnexpectedPopups();
|
|
941
935
|
state.element = await this._locate(selectors, state.info, _params);
|
|
942
936
|
await state.element.dispatchEvent("click");
|
|
943
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
937
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
944
938
|
}
|
|
945
939
|
await this.waitForPageLoad();
|
|
946
940
|
return state.info;
|
|
@@ -1019,7 +1013,6 @@ class StableBrowser {
|
|
|
1019
1013
|
await state.element.hover({ timeout: 10000 });
|
|
1020
1014
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1021
1015
|
}
|
|
1022
|
-
await _screenshot(state, this);
|
|
1023
1016
|
await this.waitForPageLoad();
|
|
1024
1017
|
return state.info;
|
|
1025
1018
|
}
|
|
@@ -1291,7 +1284,12 @@ class StableBrowser {
|
|
|
1291
1284
|
await this.waitForPageLoad();
|
|
1292
1285
|
}
|
|
1293
1286
|
else if (enter === false) {
|
|
1294
|
-
|
|
1287
|
+
try {
|
|
1288
|
+
await state.element.dispatchEvent("change", null, { timeout: 5000 });
|
|
1289
|
+
}
|
|
1290
|
+
catch (e) {
|
|
1291
|
+
// ignore
|
|
1292
|
+
}
|
|
1295
1293
|
//await this.page.keyboard.press("Tab");
|
|
1296
1294
|
}
|
|
1297
1295
|
else {
|
|
@@ -1348,6 +1346,7 @@ class StableBrowser {
|
|
|
1348
1346
|
let screenshotPath = null;
|
|
1349
1347
|
if (!info.log) {
|
|
1350
1348
|
info.log = "";
|
|
1349
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
1351
1350
|
}
|
|
1352
1351
|
info.operation = "getText";
|
|
1353
1352
|
info.selectors = selectors;
|
|
@@ -1686,11 +1685,9 @@ class StableBrowser {
|
|
|
1686
1685
|
if (!fs.existsSync(world.screenshotPath)) {
|
|
1687
1686
|
fs.mkdirSync(world.screenshotPath, { recursive: true });
|
|
1688
1687
|
}
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
}
|
|
1693
|
-
const screenshotPath = path.join(world.screenshotPath, nextIndex + ".png");
|
|
1688
|
+
// to make sure the path doesn't start with -
|
|
1689
|
+
const uuidStr = "id_" + randomUUID();
|
|
1690
|
+
const screenshotPath = path.join(world.screenshotPath, uuidStr + ".png");
|
|
1694
1691
|
try {
|
|
1695
1692
|
await this.takeScreenshot(screenshotPath);
|
|
1696
1693
|
// let buffer = await this.page.screenshot({ timeout: 4000 });
|
|
@@ -1704,7 +1701,7 @@ class StableBrowser {
|
|
|
1704
1701
|
catch (e) {
|
|
1705
1702
|
this.logger.info("unable to take screenshot, ignored");
|
|
1706
1703
|
}
|
|
1707
|
-
result.screenshotId =
|
|
1704
|
+
result.screenshotId = uuidStr;
|
|
1708
1705
|
result.screenshotPath = screenshotPath;
|
|
1709
1706
|
if (info && info.box) {
|
|
1710
1707
|
await drawRectangle(screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
|
|
@@ -1843,6 +1840,69 @@ class StableBrowser {
|
|
|
1843
1840
|
_commandFinally(state, this);
|
|
1844
1841
|
}
|
|
1845
1842
|
}
|
|
1843
|
+
async verifyAttribute(selectors, attribute, value, _params = null, options = {}, world = null) {
|
|
1844
|
+
const state = {
|
|
1845
|
+
selectors,
|
|
1846
|
+
_params,
|
|
1847
|
+
attribute,
|
|
1848
|
+
value,
|
|
1849
|
+
options,
|
|
1850
|
+
world,
|
|
1851
|
+
type: Types.VERIFY_ATTRIBUTE,
|
|
1852
|
+
highlight: true,
|
|
1853
|
+
screenshot: true,
|
|
1854
|
+
text: `Verify element attribute`,
|
|
1855
|
+
operation: "verifyAttribute",
|
|
1856
|
+
log: "***** verify attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1857
|
+
allowDisabled: true,
|
|
1858
|
+
};
|
|
1859
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1860
|
+
let val;
|
|
1861
|
+
try {
|
|
1862
|
+
await _preCommand(state, this);
|
|
1863
|
+
switch (attribute) {
|
|
1864
|
+
case "innerText":
|
|
1865
|
+
val = String(await state.element.innerText());
|
|
1866
|
+
break;
|
|
1867
|
+
case "value":
|
|
1868
|
+
val = String(await state.element.inputValue());
|
|
1869
|
+
break;
|
|
1870
|
+
case "checked":
|
|
1871
|
+
val = String(await state.element.isChecked());
|
|
1872
|
+
break;
|
|
1873
|
+
case "disabled":
|
|
1874
|
+
val = String(await state.element.isDisabled());
|
|
1875
|
+
break;
|
|
1876
|
+
case "readOnly":
|
|
1877
|
+
const isEditable = await state.element.isEditable();
|
|
1878
|
+
val = String(!isEditable);
|
|
1879
|
+
break;
|
|
1880
|
+
default:
|
|
1881
|
+
val = String(await state.element.getAttribute(attribute));
|
|
1882
|
+
break;
|
|
1883
|
+
}
|
|
1884
|
+
state.info.expectedValue = val;
|
|
1885
|
+
let regex;
|
|
1886
|
+
if (value.startsWith("/") && value.endsWith("/")) {
|
|
1887
|
+
const patternBody = value.slice(1, -1);
|
|
1888
|
+
regex = new RegExp(patternBody, "g");
|
|
1889
|
+
}
|
|
1890
|
+
else {
|
|
1891
|
+
const escapedPattern = value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1892
|
+
regex = new RegExp(escapedPattern, "g");
|
|
1893
|
+
}
|
|
1894
|
+
if (!val.match(regex)) {
|
|
1895
|
+
throw new Error(`The ${attribute} attribute has a value of "${val}", but the expected value is "${value}"`);
|
|
1896
|
+
}
|
|
1897
|
+
return state.info;
|
|
1898
|
+
}
|
|
1899
|
+
catch (e) {
|
|
1900
|
+
await _commandError(state, e, this);
|
|
1901
|
+
}
|
|
1902
|
+
finally {
|
|
1903
|
+
_commandFinally(state, this);
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1846
1906
|
async extractEmailData(emailAddress, options, world) {
|
|
1847
1907
|
if (!emailAddress) {
|
|
1848
1908
|
throw new Error("email address is null");
|
|
@@ -1861,7 +1921,7 @@ class StableBrowser {
|
|
|
1861
1921
|
if (options && options.timeout) {
|
|
1862
1922
|
timeout = options.timeout;
|
|
1863
1923
|
}
|
|
1864
|
-
const serviceUrl =
|
|
1924
|
+
const serviceUrl = _getServerUrl() + "/api/mail/createLinkOrCodeFromEmail";
|
|
1865
1925
|
const request = {
|
|
1866
1926
|
method: "POST",
|
|
1867
1927
|
url: serviceUrl,
|
|
@@ -1938,15 +1998,15 @@ class StableBrowser {
|
|
|
1938
1998
|
scope
|
|
1939
1999
|
.evaluate((node) => {
|
|
1940
2000
|
if (node && node.style) {
|
|
1941
|
-
let originalBorder = node.style.
|
|
1942
|
-
node.style.
|
|
2001
|
+
let originalBorder = node.style.outline;
|
|
2002
|
+
node.style.outline = "2px solid red";
|
|
1943
2003
|
if (window) {
|
|
1944
2004
|
window.addEventListener("beforeunload", function (e) {
|
|
1945
|
-
node.style.
|
|
2005
|
+
node.style.outline = originalBorder;
|
|
1946
2006
|
});
|
|
1947
2007
|
}
|
|
1948
2008
|
setTimeout(function () {
|
|
1949
|
-
node.style.
|
|
2009
|
+
node.style.outline = originalBorder;
|
|
1950
2010
|
}, 2000);
|
|
1951
2011
|
}
|
|
1952
2012
|
})
|
|
@@ -1968,17 +2028,17 @@ class StableBrowser {
|
|
|
1968
2028
|
if (!element.style) {
|
|
1969
2029
|
return;
|
|
1970
2030
|
}
|
|
1971
|
-
var originalBorder = element.style.
|
|
2031
|
+
var originalBorder = element.style.outline;
|
|
1972
2032
|
// Set the new border to be red and 2px solid
|
|
1973
|
-
element.style.
|
|
2033
|
+
element.style.outline = "2px solid red";
|
|
1974
2034
|
if (window) {
|
|
1975
2035
|
window.addEventListener("beforeunload", function (e) {
|
|
1976
|
-
element.style.
|
|
2036
|
+
element.style.outline = originalBorder;
|
|
1977
2037
|
});
|
|
1978
2038
|
}
|
|
1979
2039
|
// Set a timeout to revert to the original border after 2 seconds
|
|
1980
2040
|
setTimeout(function () {
|
|
1981
|
-
element.style.
|
|
2041
|
+
element.style.outline = originalBorder;
|
|
1982
2042
|
}, 2000);
|
|
1983
2043
|
}
|
|
1984
2044
|
return;
|
|
@@ -2034,7 +2094,7 @@ class StableBrowser {
|
|
|
2034
2094
|
}
|
|
2035
2095
|
finally {
|
|
2036
2096
|
const endTime = Date.now();
|
|
2037
|
-
|
|
2097
|
+
_reportToWorld(world, {
|
|
2038
2098
|
type: Types.VERIFY_PAGE_PATH,
|
|
2039
2099
|
text: "Verify page path",
|
|
2040
2100
|
screenshotId,
|
|
@@ -2054,6 +2114,35 @@ class StableBrowser {
|
|
|
2054
2114
|
});
|
|
2055
2115
|
}
|
|
2056
2116
|
}
|
|
2117
|
+
async findTextInAllFrames(dateAlternatives, numberAlternatives, text, state) {
|
|
2118
|
+
const frames = this.page.frames();
|
|
2119
|
+
let results = [];
|
|
2120
|
+
let ignoreCase = false;
|
|
2121
|
+
for (let i = 0; i < frames.length; i++) {
|
|
2122
|
+
if (dateAlternatives.date) {
|
|
2123
|
+
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2124
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2125
|
+
result.frame = frames[i];
|
|
2126
|
+
results.push(result);
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
else if (numberAlternatives.number) {
|
|
2130
|
+
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2131
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2132
|
+
result.frame = frames[i];
|
|
2133
|
+
results.push(result);
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
else {
|
|
2137
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2138
|
+
result.frame = frames[i];
|
|
2139
|
+
results.push(result);
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
state.info.results = results;
|
|
2143
|
+
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2144
|
+
return resultWithElementsFound;
|
|
2145
|
+
}
|
|
2057
2146
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2058
2147
|
text = unEscapeString(text);
|
|
2059
2148
|
const state = {
|
|
@@ -2063,7 +2152,7 @@ class StableBrowser {
|
|
|
2063
2152
|
locate: false,
|
|
2064
2153
|
scroll: false,
|
|
2065
2154
|
highlight: false,
|
|
2066
|
-
type: Types.
|
|
2155
|
+
type: Types.VERIFY_PAGE_CONTAINS_TEXT,
|
|
2067
2156
|
text: `Verify text exists in page`,
|
|
2068
2157
|
operation: "verifyTextExistInPage",
|
|
2069
2158
|
log: "***** verify text " + text + " exists in page *****\n",
|
|
@@ -2081,31 +2170,7 @@ class StableBrowser {
|
|
|
2081
2170
|
await _preCommand(state, this);
|
|
2082
2171
|
state.info.text = text;
|
|
2083
2172
|
while (true) {
|
|
2084
|
-
const
|
|
2085
|
-
let results = [];
|
|
2086
|
-
for (let i = 0; i < frames.length; i++) {
|
|
2087
|
-
if (dateAlternatives.date) {
|
|
2088
|
-
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2089
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2090
|
-
result.frame = frames[i];
|
|
2091
|
-
results.push(result);
|
|
2092
|
-
}
|
|
2093
|
-
}
|
|
2094
|
-
else if (numberAlternatives.number) {
|
|
2095
|
-
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2096
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2097
|
-
result.frame = frames[i];
|
|
2098
|
-
results.push(result);
|
|
2099
|
-
}
|
|
2100
|
-
}
|
|
2101
|
-
else {
|
|
2102
|
-
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2103
|
-
result.frame = frames[i];
|
|
2104
|
-
results.push(result);
|
|
2105
|
-
}
|
|
2106
|
-
}
|
|
2107
|
-
state.info.results = results;
|
|
2108
|
-
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2173
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, text, state);
|
|
2109
2174
|
if (resultWithElementsFound.length === 0) {
|
|
2110
2175
|
if (Date.now() - state.startTime > timeout) {
|
|
2111
2176
|
throw new Error(`Text ${text} not found in page`);
|
|
@@ -2115,9 +2180,9 @@ class StableBrowser {
|
|
|
2115
2180
|
}
|
|
2116
2181
|
if (resultWithElementsFound[0].randomToken) {
|
|
2117
2182
|
const frame = resultWithElementsFound[0].frame;
|
|
2118
|
-
const dataAttribute = `[data-blinq-id
|
|
2183
|
+
const dataAttribute = `[data-blinq-id-${resultWithElementsFound[0].randomToken}]`;
|
|
2119
2184
|
await this._highlightElements(frame, dataAttribute);
|
|
2120
|
-
const element = await frame
|
|
2185
|
+
const element = await frame.locator(dataAttribute).first();
|
|
2121
2186
|
if (element) {
|
|
2122
2187
|
await this.scrollIfNeeded(element, state.info);
|
|
2123
2188
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
@@ -2162,31 +2227,7 @@ class StableBrowser {
|
|
|
2162
2227
|
await _preCommand(state, this);
|
|
2163
2228
|
state.info.text = text;
|
|
2164
2229
|
while (true) {
|
|
2165
|
-
const
|
|
2166
|
-
let results = [];
|
|
2167
|
-
for (let i = 0; i < frames.length; i++) {
|
|
2168
|
-
if (dateAlternatives.date) {
|
|
2169
|
-
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2170
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2171
|
-
result.frame = frames[i];
|
|
2172
|
-
results.push(result);
|
|
2173
|
-
}
|
|
2174
|
-
}
|
|
2175
|
-
else if (numberAlternatives.number) {
|
|
2176
|
-
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2177
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2178
|
-
result.frame = frames[i];
|
|
2179
|
-
results.push(result);
|
|
2180
|
-
}
|
|
2181
|
-
}
|
|
2182
|
-
else {
|
|
2183
|
-
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2184
|
-
result.frame = frames[i];
|
|
2185
|
-
results.push(result);
|
|
2186
|
-
}
|
|
2187
|
-
}
|
|
2188
|
-
state.info.results = results;
|
|
2189
|
-
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2230
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, text, state);
|
|
2190
2231
|
if (resultWithElementsFound.length === 0) {
|
|
2191
2232
|
await _screenshot(state, this);
|
|
2192
2233
|
return state.info;
|
|
@@ -2204,15 +2245,88 @@ class StableBrowser {
|
|
|
2204
2245
|
_commandFinally(state, this);
|
|
2205
2246
|
}
|
|
2206
2247
|
}
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2248
|
+
async verifyTextRelatedToText(textAnchor, climb, textToVerify, options = {}, world = null) {
|
|
2249
|
+
textAnchor = unEscapeString(textAnchor);
|
|
2250
|
+
textToVerify = unEscapeString(textToVerify);
|
|
2251
|
+
const state = {
|
|
2252
|
+
text_search: textToVerify,
|
|
2253
|
+
options,
|
|
2254
|
+
world,
|
|
2255
|
+
locate: false,
|
|
2256
|
+
scroll: false,
|
|
2257
|
+
highlight: false,
|
|
2258
|
+
type: Types.VERIFY_TEXT_WITH_RELATION,
|
|
2259
|
+
text: `Verify text with relation to another text`,
|
|
2260
|
+
operation: "verify_text_with_relation",
|
|
2261
|
+
log: "***** search for " + textAnchor + " climb " + climb + " and verify " + textToVerify + " found *****\n",
|
|
2262
|
+
};
|
|
2263
|
+
const timeout = this._getLoadTimeout(options);
|
|
2264
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2265
|
+
let newValue = await this._replaceWithLocalData(textAnchor, world);
|
|
2266
|
+
if (newValue !== textAnchor) {
|
|
2267
|
+
this.logger.info(textAnchor + "=" + newValue);
|
|
2268
|
+
textAnchor = newValue;
|
|
2269
|
+
}
|
|
2270
|
+
newValue = await this._replaceWithLocalData(textToVerify, world);
|
|
2271
|
+
if (newValue !== textToVerify) {
|
|
2272
|
+
this.logger.info(textToVerify + "=" + newValue);
|
|
2273
|
+
textToVerify = newValue;
|
|
2274
|
+
}
|
|
2275
|
+
let dateAlternatives = findDateAlternatives(textToVerify);
|
|
2276
|
+
let numberAlternatives = findNumberAlternatives(textToVerify);
|
|
2277
|
+
let foundAncore = false;
|
|
2278
|
+
try {
|
|
2279
|
+
await _preCommand(state, this);
|
|
2280
|
+
state.info.text = textToVerify;
|
|
2281
|
+
while (true) {
|
|
2282
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, textAnchor, state);
|
|
2283
|
+
if (resultWithElementsFound.length === 0) {
|
|
2284
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2285
|
+
throw new Error(`Text ${foundAncore ? textToVerify : textAnchor} not found in page`);
|
|
2286
|
+
}
|
|
2287
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2288
|
+
continue;
|
|
2289
|
+
}
|
|
2290
|
+
for (let i = 0; i < resultWithElementsFound.length; i++) {
|
|
2291
|
+
foundAncore = true;
|
|
2292
|
+
const result = resultWithElementsFound[i];
|
|
2293
|
+
const token = result.randomToken;
|
|
2294
|
+
const frame = result.frame;
|
|
2295
|
+
let css = `[data-blinq-id-${token}]`;
|
|
2296
|
+
const climbArray1 = [];
|
|
2297
|
+
for (let i = 0; i < climb; i++) {
|
|
2298
|
+
climbArray1.push("..");
|
|
2299
|
+
}
|
|
2300
|
+
let climbXpath = "xpath=" + climbArray1.join("/");
|
|
2301
|
+
css = css + " >> " + climbXpath;
|
|
2302
|
+
const count = await frame.locator(css).count();
|
|
2303
|
+
for (let j = 0; j < count; j++) {
|
|
2304
|
+
const continer = await frame.locator(css).nth(j);
|
|
2305
|
+
const result = await this._locateElementByText(continer, textToVerify, "*", false, true, true, {});
|
|
2306
|
+
if (result.elementCount > 0) {
|
|
2307
|
+
const dataAttribute = "[data-blinq-id-" + result.randomToken + "]";
|
|
2308
|
+
//const cssAnchor = `[data-blinq-id="blinq-id-${token}-anchor"]`;
|
|
2309
|
+
await this._highlightElements(frame, dataAttribute);
|
|
2310
|
+
//await this._highlightElements(frame, cssAnchor);
|
|
2311
|
+
const element = await frame.locator(dataAttribute).first();
|
|
2312
|
+
if (element) {
|
|
2313
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2314
|
+
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2315
|
+
}
|
|
2316
|
+
await _screenshot(state, this);
|
|
2317
|
+
return state.info;
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2323
|
+
}
|
|
2324
|
+
catch (e) {
|
|
2325
|
+
await _commandError(state, e, this);
|
|
2211
2326
|
}
|
|
2212
|
-
|
|
2213
|
-
|
|
2327
|
+
finally {
|
|
2328
|
+
_commandFinally(state, this);
|
|
2214
2329
|
}
|
|
2215
|
-
return serviceUrl;
|
|
2216
2330
|
}
|
|
2217
2331
|
async visualVerification(text, options = {}, world = null) {
|
|
2218
2332
|
const startTime = Date.now();
|
|
@@ -2228,7 +2342,7 @@ class StableBrowser {
|
|
|
2228
2342
|
throw new Error("TOKEN is not set");
|
|
2229
2343
|
}
|
|
2230
2344
|
try {
|
|
2231
|
-
let serviceUrl =
|
|
2345
|
+
let serviceUrl = _getServerUrl();
|
|
2232
2346
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2233
2347
|
info.screenshotPath = screenshotPath;
|
|
2234
2348
|
const screenshot = await this.takeScreenshot();
|
|
@@ -2269,7 +2383,7 @@ class StableBrowser {
|
|
|
2269
2383
|
}
|
|
2270
2384
|
finally {
|
|
2271
2385
|
const endTime = Date.now();
|
|
2272
|
-
|
|
2386
|
+
_reportToWorld(world, {
|
|
2273
2387
|
type: Types.VERIFY_VISUAL,
|
|
2274
2388
|
text: "Visual verification",
|
|
2275
2389
|
screenshotId,
|
|
@@ -2317,6 +2431,7 @@ class StableBrowser {
|
|
|
2317
2431
|
let screenshotPath = null;
|
|
2318
2432
|
const info = {};
|
|
2319
2433
|
info.log = "";
|
|
2434
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
2320
2435
|
info.operation = "getTableData";
|
|
2321
2436
|
info.selectors = selectors;
|
|
2322
2437
|
try {
|
|
@@ -2337,7 +2452,7 @@ class StableBrowser {
|
|
|
2337
2452
|
}
|
|
2338
2453
|
finally {
|
|
2339
2454
|
const endTime = Date.now();
|
|
2340
|
-
|
|
2455
|
+
_reportToWorld(world, {
|
|
2341
2456
|
element_name: selectors.element_name,
|
|
2342
2457
|
type: Types.GET_TABLE_DATA,
|
|
2343
2458
|
text: "Get table data",
|
|
@@ -2392,7 +2507,7 @@ class StableBrowser {
|
|
|
2392
2507
|
info.operation = "analyzeTable";
|
|
2393
2508
|
info.selectors = selectors;
|
|
2394
2509
|
info.query = query;
|
|
2395
|
-
query =
|
|
2510
|
+
query = _fixUsingParams(query, _params);
|
|
2396
2511
|
info.query_fixed = query;
|
|
2397
2512
|
info.operator = operator;
|
|
2398
2513
|
info.value = value;
|
|
@@ -2503,7 +2618,7 @@ class StableBrowser {
|
|
|
2503
2618
|
}
|
|
2504
2619
|
finally {
|
|
2505
2620
|
const endTime = Date.now();
|
|
2506
|
-
|
|
2621
|
+
_reportToWorld(world, {
|
|
2507
2622
|
element_name: selectors.element_name,
|
|
2508
2623
|
type: Types.ANALYZE_TABLE,
|
|
2509
2624
|
text: "Analyze table",
|
|
@@ -2576,7 +2691,7 @@ class StableBrowser {
|
|
|
2576
2691
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2577
2692
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2578
2693
|
const endTime = Date.now();
|
|
2579
|
-
|
|
2694
|
+
_reportToWorld(world, {
|
|
2580
2695
|
type: Types.GET_PAGE_STATUS,
|
|
2581
2696
|
text: "Wait for page load",
|
|
2582
2697
|
screenshotId,
|
|
@@ -2620,6 +2735,11 @@ class StableBrowser {
|
|
|
2620
2735
|
_commandFinally(state, this);
|
|
2621
2736
|
}
|
|
2622
2737
|
}
|
|
2738
|
+
saveTestDataAsGlobal(options, world) {
|
|
2739
|
+
const dataFile = this._getDataFile(world);
|
|
2740
|
+
process.env.GLOBAL_TEST_DATA_FILE = dataFile;
|
|
2741
|
+
this.logger.info("Save the scenario test data as global for the following scenarios.");
|
|
2742
|
+
}
|
|
2623
2743
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
2624
2744
|
const startTime = Date.now();
|
|
2625
2745
|
let error = null;
|
|
@@ -2643,7 +2763,7 @@ class StableBrowser {
|
|
|
2643
2763
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2644
2764
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2645
2765
|
const endTime = Date.now();
|
|
2646
|
-
|
|
2766
|
+
_reportToWorld(world, {
|
|
2647
2767
|
type: Types.SET_VIEWPORT,
|
|
2648
2768
|
text: "set viewport size to " + width + "x" + hight,
|
|
2649
2769
|
screenshotId,
|
|
@@ -2680,7 +2800,7 @@ class StableBrowser {
|
|
|
2680
2800
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2681
2801
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2682
2802
|
const endTime = Date.now();
|
|
2683
|
-
|
|
2803
|
+
_reportToWorld(world, {
|
|
2684
2804
|
type: Types.GET_PAGE_STATUS,
|
|
2685
2805
|
text: "page relaod",
|
|
2686
2806
|
screenshotId,
|
|
@@ -2716,26 +2836,35 @@ class StableBrowser {
|
|
|
2716
2836
|
console.log("#-#");
|
|
2717
2837
|
}
|
|
2718
2838
|
}
|
|
2719
|
-
_reportToWorld(world, properties) {
|
|
2720
|
-
if (!world || !world.attach) {
|
|
2721
|
-
return;
|
|
2722
|
-
}
|
|
2723
|
-
world.attach(JSON.stringify(properties), { mediaType: "application/json" });
|
|
2724
|
-
}
|
|
2725
2839
|
async beforeStep(world, step) {
|
|
2726
|
-
this.stepName = step.pickleStep.text;
|
|
2727
|
-
this.logger.info("step: " + this.stepName);
|
|
2728
2840
|
if (this.stepIndex === undefined) {
|
|
2729
2841
|
this.stepIndex = 0;
|
|
2730
2842
|
}
|
|
2731
2843
|
else {
|
|
2732
2844
|
this.stepIndex++;
|
|
2733
2845
|
}
|
|
2846
|
+
if (step && step.pickleStep && step.pickleStep.text) {
|
|
2847
|
+
this.stepName = step.pickleStep.text;
|
|
2848
|
+
this.logger.info("step: " + this.stepName);
|
|
2849
|
+
}
|
|
2850
|
+
else if (step && step.text) {
|
|
2851
|
+
this.stepName = step.text;
|
|
2852
|
+
}
|
|
2853
|
+
else {
|
|
2854
|
+
this.stepName = "step " + this.stepIndex;
|
|
2855
|
+
}
|
|
2734
2856
|
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2735
2857
|
if (this.context.browserObject.context) {
|
|
2736
2858
|
await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
|
|
2737
2859
|
}
|
|
2738
2860
|
}
|
|
2861
|
+
if (this.tags === null && step && step.pickle && step.pickle.tags) {
|
|
2862
|
+
this.tags = step.pickle.tags.map((tag) => tag.name);
|
|
2863
|
+
// check if @global_test_data tag is present
|
|
2864
|
+
if (this.tags.includes("@global_test_data")) {
|
|
2865
|
+
this.saveTestDataAsGlobal({}, world);
|
|
2866
|
+
}
|
|
2867
|
+
}
|
|
2739
2868
|
}
|
|
2740
2869
|
async afterStep(world, step) {
|
|
2741
2870
|
this.stepName = null;
|
|
@@ -2753,156 +2882,5 @@ function createTimedPromise(promise, label) {
|
|
|
2753
2882
|
.then((result) => ({ status: "fulfilled", label, result }))
|
|
2754
2883
|
.catch((error) => Promise.reject({ status: "rejected", label, error }));
|
|
2755
2884
|
}
|
|
2756
|
-
const KEYBOARD_EVENTS = [
|
|
2757
|
-
"ALT",
|
|
2758
|
-
"AltGraph",
|
|
2759
|
-
"CapsLock",
|
|
2760
|
-
"Control",
|
|
2761
|
-
"Fn",
|
|
2762
|
-
"FnLock",
|
|
2763
|
-
"Hyper",
|
|
2764
|
-
"Meta",
|
|
2765
|
-
"NumLock",
|
|
2766
|
-
"ScrollLock",
|
|
2767
|
-
"Shift",
|
|
2768
|
-
"Super",
|
|
2769
|
-
"Symbol",
|
|
2770
|
-
"SymbolLock",
|
|
2771
|
-
"Enter",
|
|
2772
|
-
"Tab",
|
|
2773
|
-
"ArrowDown",
|
|
2774
|
-
"ArrowLeft",
|
|
2775
|
-
"ArrowRight",
|
|
2776
|
-
"ArrowUp",
|
|
2777
|
-
"End",
|
|
2778
|
-
"Home",
|
|
2779
|
-
"PageDown",
|
|
2780
|
-
"PageUp",
|
|
2781
|
-
"Backspace",
|
|
2782
|
-
"Clear",
|
|
2783
|
-
"Copy",
|
|
2784
|
-
"CrSel",
|
|
2785
|
-
"Cut",
|
|
2786
|
-
"Delete",
|
|
2787
|
-
"EraseEof",
|
|
2788
|
-
"ExSel",
|
|
2789
|
-
"Insert",
|
|
2790
|
-
"Paste",
|
|
2791
|
-
"Redo",
|
|
2792
|
-
"Undo",
|
|
2793
|
-
"Accept",
|
|
2794
|
-
"Again",
|
|
2795
|
-
"Attn",
|
|
2796
|
-
"Cancel",
|
|
2797
|
-
"ContextMenu",
|
|
2798
|
-
"Escape",
|
|
2799
|
-
"Execute",
|
|
2800
|
-
"Find",
|
|
2801
|
-
"Finish",
|
|
2802
|
-
"Help",
|
|
2803
|
-
"Pause",
|
|
2804
|
-
"Play",
|
|
2805
|
-
"Props",
|
|
2806
|
-
"Select",
|
|
2807
|
-
"ZoomIn",
|
|
2808
|
-
"ZoomOut",
|
|
2809
|
-
"BrightnessDown",
|
|
2810
|
-
"BrightnessUp",
|
|
2811
|
-
"Eject",
|
|
2812
|
-
"LogOff",
|
|
2813
|
-
"Power",
|
|
2814
|
-
"PowerOff",
|
|
2815
|
-
"PrintScreen",
|
|
2816
|
-
"Hibernate",
|
|
2817
|
-
"Standby",
|
|
2818
|
-
"WakeUp",
|
|
2819
|
-
"AllCandidates",
|
|
2820
|
-
"Alphanumeric",
|
|
2821
|
-
"CodeInput",
|
|
2822
|
-
"Compose",
|
|
2823
|
-
"Convert",
|
|
2824
|
-
"Dead",
|
|
2825
|
-
"FinalMode",
|
|
2826
|
-
"GroupFirst",
|
|
2827
|
-
"GroupLast",
|
|
2828
|
-
"GroupNext",
|
|
2829
|
-
"GroupPrevious",
|
|
2830
|
-
"ModeChange",
|
|
2831
|
-
"NextCandidate",
|
|
2832
|
-
"NonConvert",
|
|
2833
|
-
"PreviousCandidate",
|
|
2834
|
-
"Process",
|
|
2835
|
-
"SingleCandidate",
|
|
2836
|
-
"HangulMode",
|
|
2837
|
-
"HanjaMode",
|
|
2838
|
-
"JunjaMode",
|
|
2839
|
-
"Eisu",
|
|
2840
|
-
"Hankaku",
|
|
2841
|
-
"Hiragana",
|
|
2842
|
-
"HiraganaKatakana",
|
|
2843
|
-
"KanaMode",
|
|
2844
|
-
"KanjiMode",
|
|
2845
|
-
"Katakana",
|
|
2846
|
-
"Romaji",
|
|
2847
|
-
"Zenkaku",
|
|
2848
|
-
"ZenkakuHanaku",
|
|
2849
|
-
"F1",
|
|
2850
|
-
"F2",
|
|
2851
|
-
"F3",
|
|
2852
|
-
"F4",
|
|
2853
|
-
"F5",
|
|
2854
|
-
"F6",
|
|
2855
|
-
"F7",
|
|
2856
|
-
"F8",
|
|
2857
|
-
"F9",
|
|
2858
|
-
"F10",
|
|
2859
|
-
"F11",
|
|
2860
|
-
"F12",
|
|
2861
|
-
"Soft1",
|
|
2862
|
-
"Soft2",
|
|
2863
|
-
"Soft3",
|
|
2864
|
-
"Soft4",
|
|
2865
|
-
"ChannelDown",
|
|
2866
|
-
"ChannelUp",
|
|
2867
|
-
"Close",
|
|
2868
|
-
"MailForward",
|
|
2869
|
-
"MailReply",
|
|
2870
|
-
"MailSend",
|
|
2871
|
-
"MediaFastForward",
|
|
2872
|
-
"MediaPause",
|
|
2873
|
-
"MediaPlay",
|
|
2874
|
-
"MediaPlayPause",
|
|
2875
|
-
"MediaRecord",
|
|
2876
|
-
"MediaRewind",
|
|
2877
|
-
"MediaStop",
|
|
2878
|
-
"MediaTrackNext",
|
|
2879
|
-
"MediaTrackPrevious",
|
|
2880
|
-
"AudioBalanceLeft",
|
|
2881
|
-
"AudioBalanceRight",
|
|
2882
|
-
"AudioBassBoostDown",
|
|
2883
|
-
"AudioBassBoostToggle",
|
|
2884
|
-
"AudioBassBoostUp",
|
|
2885
|
-
"AudioFaderFront",
|
|
2886
|
-
"AudioFaderRear",
|
|
2887
|
-
"AudioSurroundModeNext",
|
|
2888
|
-
"AudioTrebleDown",
|
|
2889
|
-
"AudioTrebleUp",
|
|
2890
|
-
"AudioVolumeDown",
|
|
2891
|
-
"AudioVolumeMute",
|
|
2892
|
-
"AudioVolumeUp",
|
|
2893
|
-
"MicrophoneToggle",
|
|
2894
|
-
"MicrophoneVolumeDown",
|
|
2895
|
-
"MicrophoneVolumeMute",
|
|
2896
|
-
"MicrophoneVolumeUp",
|
|
2897
|
-
"TV",
|
|
2898
|
-
"TV3DMode",
|
|
2899
|
-
"TVAntennaCable",
|
|
2900
|
-
"TVAudioDescription",
|
|
2901
|
-
];
|
|
2902
|
-
function unEscapeString(str) {
|
|
2903
|
-
const placeholder = "__NEWLINE__";
|
|
2904
|
-
str = str.replace(new RegExp(placeholder, "g"), "\n");
|
|
2905
|
-
return str;
|
|
2906
|
-
}
|
|
2907
2885
|
export { StableBrowser };
|
|
2908
2886
|
//# sourceMappingURL=stable_browser.js.map
|