automation_model 1.0.522-dev → 1.0.522-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 +90 -88
- package/lib/api.js.map +1 -1
- package/lib/auto_page.d.ts +2 -1
- package/lib/auto_page.js +8 -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 -1
- package/lib/command_common.js +58 -10
- 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/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_log.d.ts +26 -0
- package/lib/locator_log.js +69 -0
- package/lib/locator_log.js.map +1 -0
- package/lib/network.js +51 -12
- package/lib/network.js.map +1 -1
- package/lib/stable_browser.d.ts +50 -15
- package/lib/stable_browser.js +575 -469
- package/lib/stable_browser.js.map +1 -1
- 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 { randomUUID } from "crypto";
|
|
19
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,8 +49,13 @@ 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 = {};
|
|
56
|
+
const formatElementName = (elementName) => {
|
|
57
|
+
return elementName ? JSON.stringify(elementName) : "element";
|
|
58
|
+
};
|
|
50
59
|
class StableBrowser {
|
|
51
60
|
browser;
|
|
52
61
|
page;
|
|
@@ -58,6 +67,7 @@ class StableBrowser {
|
|
|
58
67
|
networkLogger = null;
|
|
59
68
|
configuration = null;
|
|
60
69
|
appName = "main";
|
|
70
|
+
tags = null;
|
|
61
71
|
constructor(browser, page, logger = null, context = null, world = null) {
|
|
62
72
|
this.browser = browser;
|
|
63
73
|
this.page = page;
|
|
@@ -88,17 +98,17 @@ class StableBrowser {
|
|
|
88
98
|
catch (e) {
|
|
89
99
|
this.logger.error("unable to read ai_config.json");
|
|
90
100
|
}
|
|
91
|
-
context.pageLoading = { status: false };
|
|
92
|
-
context.pages = [this.page];
|
|
93
101
|
const logFolder = path.join(this.project_path, "logs", "web");
|
|
94
102
|
this.world = world;
|
|
103
|
+
context.pages = [this.page];
|
|
104
|
+
context.pageLoading = { status: false };
|
|
95
105
|
this.registerEventListeners(this.context);
|
|
96
106
|
registerNetworkEvents(this.world, this, this.context, this.page);
|
|
97
107
|
registerDownloadEvent(this.page, this.world, this.context);
|
|
98
108
|
}
|
|
99
109
|
registerEventListeners(context) {
|
|
100
110
|
this.registerConsoleLogListener(this.page, context);
|
|
101
|
-
this.registerRequestListener(this.page, context, this.webLogFile);
|
|
111
|
+
// this.registerRequestListener(this.page, context, this.webLogFile);
|
|
102
112
|
if (!context.pageLoading) {
|
|
103
113
|
context.pageLoading = { status: false };
|
|
104
114
|
}
|
|
@@ -143,7 +153,7 @@ class StableBrowser {
|
|
|
143
153
|
if (this.appName === appName) {
|
|
144
154
|
return;
|
|
145
155
|
}
|
|
146
|
-
let
|
|
156
|
+
let navigate = false;
|
|
147
157
|
if (!apps[appName]) {
|
|
148
158
|
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this, -1, this.context.reportFolder);
|
|
149
159
|
newContextCreated = true;
|
|
@@ -154,32 +164,15 @@ class StableBrowser {
|
|
|
154
164
|
};
|
|
155
165
|
}
|
|
156
166
|
const tempContext = {};
|
|
157
|
-
|
|
158
|
-
|
|
167
|
+
_copyContext(this, tempContext);
|
|
168
|
+
_copyContext(apps[appName], this);
|
|
159
169
|
apps[this.appName] = tempContext;
|
|
160
170
|
this.appName = appName;
|
|
161
|
-
if (
|
|
162
|
-
this.registerEventListeners(this.context);
|
|
171
|
+
if (navigate) {
|
|
163
172
|
await this.goto(this.context.environment.baseUrl);
|
|
164
173
|
await this.waitForPageLoad();
|
|
165
174
|
}
|
|
166
175
|
}
|
|
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
176
|
registerConsoleLogListener(page, context) {
|
|
184
177
|
if (!this.context.webLogger) {
|
|
185
178
|
this.context.webLogger = [];
|
|
@@ -243,55 +236,48 @@ class StableBrowser {
|
|
|
243
236
|
// async closeUnexpectedPopups() {
|
|
244
237
|
// await closeUnexpectedPopups(this.page);
|
|
245
238
|
// }
|
|
246
|
-
async goto(url) {
|
|
239
|
+
async goto(url, world = null) {
|
|
247
240
|
if (!url.startsWith("http")) {
|
|
248
241
|
url = "https://" + url;
|
|
249
242
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
243
|
+
const state = {
|
|
244
|
+
value: url,
|
|
245
|
+
world: world,
|
|
246
|
+
type: Types.NAVIGATE,
|
|
247
|
+
text: `Navigate Page to: ${url}`,
|
|
248
|
+
operation: "goto",
|
|
249
|
+
log: "***** navigate page to " + url + " *****\n",
|
|
250
|
+
info: {},
|
|
251
|
+
locate: false,
|
|
252
|
+
scroll: false,
|
|
253
|
+
screenshot: false,
|
|
254
|
+
highlight: false,
|
|
255
|
+
};
|
|
256
|
+
try {
|
|
257
|
+
await _preCommand(state, this);
|
|
258
|
+
await this.page.goto(url, {
|
|
259
|
+
timeout: 60000,
|
|
260
|
+
});
|
|
261
|
+
await _screenshot(state, this);
|
|
257
262
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
// remove the _ prefix
|
|
262
|
-
regValue = key.substring(1);
|
|
263
|
-
}
|
|
264
|
-
text = text.replaceAll(new RegExp("{" + regValue + "}", "g"), _params[key]);
|
|
263
|
+
catch (error) {
|
|
264
|
+
console.error("Error on goto", error);
|
|
265
|
+
_commandError(state, error, this);
|
|
265
266
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
_fixLocatorUsingParams(locator, _params) {
|
|
269
|
-
// check if not null
|
|
270
|
-
if (!locator) {
|
|
271
|
-
return locator;
|
|
267
|
+
finally {
|
|
268
|
+
_commandFinally(state, this);
|
|
272
269
|
}
|
|
273
|
-
// clone the locator
|
|
274
|
-
locator = JSON.parse(JSON.stringify(locator));
|
|
275
|
-
this.scanAndManipulate(locator, _params);
|
|
276
|
-
return locator;
|
|
277
270
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
}
|
|
287
|
-
else if (this._isObject(currentObj[key])) {
|
|
288
|
-
// Recursively scan nested objects
|
|
289
|
-
this.scanAndManipulate(currentObj[key], _params);
|
|
271
|
+
async _getLocator(locator, scope, _params) {
|
|
272
|
+
locator = _fixLocatorUsingParams(locator, _params);
|
|
273
|
+
// locator = await this._replaceWithLocalData(locator);
|
|
274
|
+
for (let key in locator) {
|
|
275
|
+
if (typeof locator[key] !== "string")
|
|
276
|
+
continue;
|
|
277
|
+
if (locator[key].includes("{{") && locator[key].includes("}}")) {
|
|
278
|
+
locator[key] = await this._replaceWithLocalData(locator[key], this.world);
|
|
290
279
|
}
|
|
291
280
|
}
|
|
292
|
-
}
|
|
293
|
-
_getLocator(locator, scope, _params) {
|
|
294
|
-
locator = this._fixLocatorUsingParams(locator, _params);
|
|
295
281
|
let locatorReturn;
|
|
296
282
|
if (locator.role) {
|
|
297
283
|
if (locator.role[1].nameReg) {
|
|
@@ -299,7 +285,7 @@ class StableBrowser {
|
|
|
299
285
|
delete locator.role[1].nameReg;
|
|
300
286
|
}
|
|
301
287
|
// if (locator.role[1].name) {
|
|
302
|
-
// locator.role[1].name =
|
|
288
|
+
// locator.role[1].name = _fixUsingParams(locator.role[1].name, _params);
|
|
303
289
|
// }
|
|
304
290
|
locatorReturn = scope.getByRole(locator.role[0], locator.role[1]);
|
|
305
291
|
}
|
|
@@ -342,140 +328,54 @@ class StableBrowser {
|
|
|
342
328
|
if (css && css.locator) {
|
|
343
329
|
css = css.locator;
|
|
344
330
|
}
|
|
345
|
-
let result = await this._locateElementByText(scope,
|
|
331
|
+
let result = await this._locateElementByText(scope, _fixUsingParams(text, _params), "*:not(script, style, head)", false, false, true, _params);
|
|
346
332
|
if (result.elementCount === 0) {
|
|
347
333
|
return;
|
|
348
334
|
}
|
|
349
|
-
let textElementCss = "[data-blinq-id
|
|
335
|
+
let textElementCss = "[data-blinq-id-" + result.randomToken + "]";
|
|
350
336
|
// css climb to parent element
|
|
351
337
|
const climbArray = [];
|
|
352
338
|
for (let i = 0; i < climb; i++) {
|
|
353
339
|
climbArray.push("..");
|
|
354
340
|
}
|
|
355
341
|
let climbXpath = "xpath=" + climbArray.join("/");
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
return
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
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
|
-
}
|
|
412
|
-
let shadowElements = Array.from(shadowElement.querySelectorAll(tag));
|
|
413
|
-
elements = elements.concat(shadowElements);
|
|
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
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
else {
|
|
430
|
-
text = text.trim();
|
|
431
|
-
for (let i = 0; i < elements.length; i++) {
|
|
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
|
-
}
|
|
342
|
+
let resultCss = textElementCss + " >> " + climbXpath;
|
|
343
|
+
if (css) {
|
|
344
|
+
resultCss = resultCss + " >> " + css;
|
|
345
|
+
}
|
|
346
|
+
return resultCss;
|
|
347
|
+
}
|
|
348
|
+
async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, ignoreCase = true, _params) {
|
|
349
|
+
const query = _convertToRegexQuery(text1, regex1, !partial1, ignoreCase);
|
|
350
|
+
const locator = scope.locator(query);
|
|
351
|
+
const count = await locator.count();
|
|
352
|
+
if (!tag1) {
|
|
353
|
+
tag1 = "*";
|
|
354
|
+
}
|
|
355
|
+
const randomToken = Math.random().toString(36).substring(7);
|
|
356
|
+
let tagCount = 0;
|
|
357
|
+
for (let i = 0; i < count; i++) {
|
|
358
|
+
const element = locator.nth(i);
|
|
359
|
+
// check if the tag matches
|
|
360
|
+
if (!(await element.evaluate((el, [tag, randomToken]) => {
|
|
361
|
+
if (!tag.startsWith("*")) {
|
|
362
|
+
if (el.tagName.toLowerCase() !== tag) {
|
|
363
|
+
return false;
|
|
444
364
|
}
|
|
445
365
|
}
|
|
446
|
-
|
|
447
|
-
|
|
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++;
|
|
366
|
+
if (!el.setAttribute) {
|
|
367
|
+
el = el.parentElement;
|
|
473
368
|
}
|
|
369
|
+
el.setAttribute("data-blinq-id-" + randomToken, "");
|
|
370
|
+
return true;
|
|
371
|
+
}, [tag1, randomToken]))) {
|
|
372
|
+
continue;
|
|
474
373
|
}
|
|
475
|
-
|
|
476
|
-
}
|
|
374
|
+
tagCount++;
|
|
375
|
+
}
|
|
376
|
+
return { elementCount: tagCount, randomToken };
|
|
477
377
|
}
|
|
478
|
-
async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
|
|
378
|
+
async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true, allowDisabled = false, element_name = null) {
|
|
479
379
|
if (!info) {
|
|
480
380
|
info = {};
|
|
481
381
|
}
|
|
@@ -484,10 +384,13 @@ class StableBrowser {
|
|
|
484
384
|
}
|
|
485
385
|
if (!info.log) {
|
|
486
386
|
info.log = "";
|
|
387
|
+
info.locatorLog = new LocatorLog(selectorHierarchy);
|
|
487
388
|
}
|
|
488
389
|
let locatorSearch = selectorHierarchy[index];
|
|
390
|
+
let originalLocatorSearch = "";
|
|
489
391
|
try {
|
|
490
|
-
|
|
392
|
+
originalLocatorSearch = _fixUsingParams(JSON.stringify(locatorSearch), _params);
|
|
393
|
+
locatorSearch = JSON.parse(originalLocatorSearch);
|
|
491
394
|
}
|
|
492
395
|
catch (e) {
|
|
493
396
|
console.error(e);
|
|
@@ -495,30 +398,31 @@ class StableBrowser {
|
|
|
495
398
|
//info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
|
|
496
399
|
let locator = null;
|
|
497
400
|
if (locatorSearch.climb && locatorSearch.climb >= 0) {
|
|
498
|
-
|
|
401
|
+
const replacedText = await this._replaceWithLocalData(locatorSearch.text, this.world);
|
|
402
|
+
let locatorString = await this._locateElmentByTextClimbCss(scope, replacedText, locatorSearch.climb, locatorSearch.css, _params);
|
|
499
403
|
if (!locatorString) {
|
|
500
404
|
info.failCause.textNotFound = true;
|
|
501
|
-
info.failCause.lastError =
|
|
405
|
+
info.failCause.lastError = `failed to locate ${formatElementName(element_name)} by text: ${locatorSearch.text}`;
|
|
502
406
|
return;
|
|
503
407
|
}
|
|
504
|
-
locator = this._getLocator({ css: locatorString }, scope, _params);
|
|
408
|
+
locator = await this._getLocator({ css: locatorString }, scope, _params);
|
|
505
409
|
}
|
|
506
410
|
else if (locatorSearch.text) {
|
|
507
|
-
let text =
|
|
508
|
-
let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
|
|
411
|
+
let text = _fixUsingParams(locatorSearch.text, _params);
|
|
412
|
+
let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, true, _params);
|
|
509
413
|
if (result.elementCount === 0) {
|
|
510
414
|
info.failCause.textNotFound = true;
|
|
511
|
-
info.failCause.lastError =
|
|
415
|
+
info.failCause.lastError = `failed to locate ${formatElementName(element_name)} by text: ${text}`;
|
|
512
416
|
return;
|
|
513
417
|
}
|
|
514
|
-
locatorSearch.css = "[data-blinq-id
|
|
418
|
+
locatorSearch.css = "[data-blinq-id-" + result.randomToken + "]";
|
|
515
419
|
if (locatorSearch.childCss) {
|
|
516
420
|
locatorSearch.css = locatorSearch.css + " " + locatorSearch.childCss;
|
|
517
421
|
}
|
|
518
|
-
locator = this._getLocator(locatorSearch, scope, _params);
|
|
422
|
+
locator = await this._getLocator(locatorSearch, scope, _params);
|
|
519
423
|
}
|
|
520
424
|
else {
|
|
521
|
-
locator = this._getLocator(locatorSearch, scope, _params);
|
|
425
|
+
locator = await this._getLocator(locatorSearch, scope, _params);
|
|
522
426
|
}
|
|
523
427
|
// let cssHref = false;
|
|
524
428
|
// if (locatorSearch.css && locatorSearch.css.includes("href=")) {
|
|
@@ -531,18 +435,27 @@ class StableBrowser {
|
|
|
531
435
|
//info.log += "total elements found " + count + "\n";
|
|
532
436
|
//let visibleCount = 0;
|
|
533
437
|
let visibleLocator = null;
|
|
534
|
-
if (locatorSearch.index && locatorSearch.index < count) {
|
|
438
|
+
if (typeof locatorSearch.index === "number" && locatorSearch.index < count) {
|
|
535
439
|
foundLocators.push(locator.nth(locatorSearch.index));
|
|
440
|
+
if (info.locatorLog) {
|
|
441
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND");
|
|
442
|
+
}
|
|
536
443
|
return;
|
|
537
444
|
}
|
|
445
|
+
if (info.locatorLog && count === 0) {
|
|
446
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "NOT_FOUND");
|
|
447
|
+
}
|
|
538
448
|
for (let j = 0; j < count; j++) {
|
|
539
449
|
let visible = await locator.nth(j).isVisible();
|
|
540
450
|
const enabled = await locator.nth(j).isEnabled();
|
|
541
451
|
if (!visibleOnly) {
|
|
542
452
|
visible = true;
|
|
543
453
|
}
|
|
544
|
-
if (visible && enabled) {
|
|
454
|
+
if (visible && (allowDisabled || enabled)) {
|
|
545
455
|
foundLocators.push(locator.nth(j));
|
|
456
|
+
if (info.locatorLog) {
|
|
457
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND");
|
|
458
|
+
}
|
|
546
459
|
}
|
|
547
460
|
else {
|
|
548
461
|
info.failCause.visible = visible;
|
|
@@ -550,8 +463,16 @@ class StableBrowser {
|
|
|
550
463
|
if (!info.printMessages) {
|
|
551
464
|
info.printMessages = {};
|
|
552
465
|
}
|
|
466
|
+
if (info.locatorLog && !visible) {
|
|
467
|
+
info.failCause.lastError = `${formatElementName(element_name)} is not visible, searching for ${originalLocatorSearch}`;
|
|
468
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND_NOT_VISIBLE");
|
|
469
|
+
}
|
|
470
|
+
if (info.locatorLog && !enabled) {
|
|
471
|
+
info.failCause.lastError = `${formatElementName(element_name)} is disabled, searching for ${originalLocatorSearch}`;
|
|
472
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND_NOT_ENABLED");
|
|
473
|
+
}
|
|
553
474
|
if (!info.printMessages[j.toString()]) {
|
|
554
|
-
info.log += "element " + locator + " visible " + visible + " enabled " + enabled + "\n";
|
|
475
|
+
//info.log += "element " + locator + " visible " + visible + " enabled " + enabled + "\n";
|
|
555
476
|
info.printMessages[j.toString()] = true;
|
|
556
477
|
}
|
|
557
478
|
}
|
|
@@ -567,7 +488,7 @@ class StableBrowser {
|
|
|
567
488
|
if (!info) {
|
|
568
489
|
info = {};
|
|
569
490
|
}
|
|
570
|
-
info.log += "scan for popup handlers" + "\n";
|
|
491
|
+
//info.log += "scan for popup handlers" + "\n";
|
|
571
492
|
const handlerGroup = [];
|
|
572
493
|
for (let i = 0; i < this.configuration.popupHandlers.length; i++) {
|
|
573
494
|
handlerGroup.push(this.configuration.popupHandlers[i].locator);
|
|
@@ -594,16 +515,28 @@ class StableBrowser {
|
|
|
594
515
|
}
|
|
595
516
|
if (result.foundElements.length > 0) {
|
|
596
517
|
let dialogCloseLocator = result.foundElements[0].locator;
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
518
|
+
try {
|
|
519
|
+
await scope?.evaluate(() => {
|
|
520
|
+
window.__isClosingPopups = true;
|
|
521
|
+
});
|
|
522
|
+
await dialogCloseLocator.click();
|
|
523
|
+
// wait for the dialog to close
|
|
524
|
+
await dialogCloseLocator.waitFor({ state: "hidden" });
|
|
525
|
+
}
|
|
526
|
+
catch (e) {
|
|
527
|
+
}
|
|
528
|
+
finally {
|
|
529
|
+
await scope?.evaluate(() => {
|
|
530
|
+
window.__isClosingPopups = false;
|
|
531
|
+
});
|
|
532
|
+
}
|
|
600
533
|
return { rerun: true };
|
|
601
534
|
}
|
|
602
535
|
}
|
|
603
536
|
}
|
|
604
537
|
return { rerun: false };
|
|
605
538
|
}
|
|
606
|
-
async _locate(selectors, info, _params, timeout) {
|
|
539
|
+
async _locate(selectors, info, _params, timeout, allowDisabled = false) {
|
|
607
540
|
if (!timeout) {
|
|
608
541
|
timeout = 30000;
|
|
609
542
|
}
|
|
@@ -613,7 +546,7 @@ class StableBrowser {
|
|
|
613
546
|
let selector = selectors.locators[j];
|
|
614
547
|
info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
|
|
615
548
|
}
|
|
616
|
-
let element = await this._locate_internal(selectors, info, _params, timeout);
|
|
549
|
+
let element = await this._locate_internal(selectors, info, _params, timeout, allowDisabled);
|
|
617
550
|
if (!element.rerun) {
|
|
618
551
|
return element;
|
|
619
552
|
}
|
|
@@ -626,6 +559,7 @@ class StableBrowser {
|
|
|
626
559
|
info.failCause = {};
|
|
627
560
|
info.log = "";
|
|
628
561
|
}
|
|
562
|
+
let startTime = Date.now();
|
|
629
563
|
let scope = this.page;
|
|
630
564
|
if (selectors.frame) {
|
|
631
565
|
return selectors.frame;
|
|
@@ -656,9 +590,11 @@ class StableBrowser {
|
|
|
656
590
|
}
|
|
657
591
|
return framescope;
|
|
658
592
|
};
|
|
593
|
+
let fLocator = null;
|
|
659
594
|
while (true) {
|
|
660
595
|
let frameFound = false;
|
|
661
596
|
if (selectors.nestFrmLoc) {
|
|
597
|
+
fLocator = selectors.nestFrmLoc;
|
|
662
598
|
scope = await findFrame(selectors.nestFrmLoc, scope);
|
|
663
599
|
frameFound = true;
|
|
664
600
|
break;
|
|
@@ -667,6 +603,7 @@ class StableBrowser {
|
|
|
667
603
|
for (let i = 0; i < selectors.frameLocators.length; i++) {
|
|
668
604
|
let frameLocator = selectors.frameLocators[i];
|
|
669
605
|
if (frameLocator.css) {
|
|
606
|
+
fLocator = frameLocator.css;
|
|
670
607
|
scope = scope.frameLocator(frameLocator.css);
|
|
671
608
|
frameFound = true;
|
|
672
609
|
break;
|
|
@@ -674,18 +611,25 @@ class StableBrowser {
|
|
|
674
611
|
}
|
|
675
612
|
}
|
|
676
613
|
if (!frameFound && selectors.iframe_src) {
|
|
614
|
+
fLocator = selectors.iframe_src;
|
|
677
615
|
scope = this.page.frame({ url: selectors.iframe_src });
|
|
678
616
|
}
|
|
679
617
|
if (!scope) {
|
|
680
|
-
info
|
|
681
|
-
|
|
618
|
+
if (info && info.locatorLog) {
|
|
619
|
+
info.locatorLog.setLocatorSearchStatus("frame-" + fLocator, "NOT_FOUND");
|
|
620
|
+
}
|
|
621
|
+
//info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
|
|
622
|
+
if (Date.now() - startTime > timeout) {
|
|
682
623
|
info.failCause.iframeNotFound = true;
|
|
683
|
-
info.failCause.lastError =
|
|
624
|
+
info.failCause.lastError = `unable to locate iframe "${selectors.iframe_src}"`;
|
|
684
625
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
685
626
|
}
|
|
686
627
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
687
628
|
}
|
|
688
629
|
else {
|
|
630
|
+
if (info && info.locatorLog) {
|
|
631
|
+
info.locatorLog.setLocatorSearchStatus("frame-" + fLocator, "FOUND");
|
|
632
|
+
}
|
|
689
633
|
break;
|
|
690
634
|
}
|
|
691
635
|
}
|
|
@@ -702,16 +646,18 @@ class StableBrowser {
|
|
|
702
646
|
return bodyContent;
|
|
703
647
|
});
|
|
704
648
|
}
|
|
705
|
-
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
649
|
+
async _locate_internal(selectors, info, _params, timeout = 30000, allowDisabled = false) {
|
|
706
650
|
if (!info) {
|
|
707
651
|
info = {};
|
|
708
652
|
info.failCause = {};
|
|
709
653
|
info.log = "";
|
|
654
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
710
655
|
}
|
|
711
656
|
let highPriorityTimeout = 5000;
|
|
712
657
|
let visibleOnlyTimeout = 6000;
|
|
713
|
-
let startTime =
|
|
658
|
+
let startTime = Date.now();
|
|
714
659
|
let locatorsCount = 0;
|
|
660
|
+
let lazy_scroll = false;
|
|
715
661
|
//let arrayMode = Array.isArray(selectors);
|
|
716
662
|
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
717
663
|
let selectorsLocators = null;
|
|
@@ -749,17 +695,17 @@ class StableBrowser {
|
|
|
749
695
|
}
|
|
750
696
|
// info.log += "scanning locators in priority 1" + "\n";
|
|
751
697
|
let onlyPriority3 = selectorsLocators[0].priority === 3;
|
|
752
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["1"], scope, _params, info, visibleOnly);
|
|
698
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["1"], scope, _params, info, visibleOnly, allowDisabled, selectors?.element_name);
|
|
753
699
|
if (result.foundElements.length === 0) {
|
|
754
700
|
// info.log += "scanning locators in priority 2" + "\n";
|
|
755
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["2"], scope, _params, info, visibleOnly);
|
|
701
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["2"], scope, _params, info, visibleOnly, allowDisabled, selectors?.element_name);
|
|
756
702
|
}
|
|
757
703
|
if (result.foundElements.length === 0 && onlyPriority3) {
|
|
758
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly);
|
|
704
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly, allowDisabled, selectors?.element_name);
|
|
759
705
|
}
|
|
760
706
|
else {
|
|
761
707
|
if (result.foundElements.length === 0 && !highPriorityOnly) {
|
|
762
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly);
|
|
708
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly, allowDisabled, selectors?.element_name);
|
|
763
709
|
}
|
|
764
710
|
}
|
|
765
711
|
let foundElements = result.foundElements;
|
|
@@ -800,26 +746,38 @@ class StableBrowser {
|
|
|
800
746
|
return maxCountElement.locator;
|
|
801
747
|
}
|
|
802
748
|
}
|
|
803
|
-
if (
|
|
749
|
+
if (Date.now() - startTime > timeout) {
|
|
804
750
|
break;
|
|
805
751
|
}
|
|
806
|
-
if (
|
|
807
|
-
info.log += "high priority timeout, will try all elements" + "\n";
|
|
752
|
+
if (Date.now() - startTime > highPriorityTimeout) {
|
|
753
|
+
//info.log += "high priority timeout, will try all elements" + "\n";
|
|
808
754
|
highPriorityOnly = false;
|
|
755
|
+
if (this.configuration && this.configuration.load_all_lazy === true && !lazy_scroll) {
|
|
756
|
+
lazy_scroll = true;
|
|
757
|
+
await scrollPageToLoadLazyElements(this.page);
|
|
758
|
+
}
|
|
809
759
|
}
|
|
810
|
-
if (
|
|
811
|
-
info.log += "visible only timeout, will try all elements" + "\n";
|
|
760
|
+
if (Date.now() - startTime > visibleOnlyTimeout) {
|
|
761
|
+
//info.log += "visible only timeout, will try all elements" + "\n";
|
|
812
762
|
visibleOnly = false;
|
|
813
763
|
}
|
|
814
764
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
815
765
|
}
|
|
816
766
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
817
|
-
info.
|
|
767
|
+
// if (info.locatorLog) {
|
|
768
|
+
// const lines = info.locatorLog.toString().split("\n");
|
|
769
|
+
// for (let line of lines) {
|
|
770
|
+
// this.logger.debug(line);
|
|
771
|
+
// }
|
|
772
|
+
// }
|
|
773
|
+
//info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
|
|
818
774
|
info.failCause.locatorNotFound = true;
|
|
819
|
-
info
|
|
775
|
+
if (!info?.failCause?.lastError) {
|
|
776
|
+
info.failCause.lastError = `failed to locate ${formatElementName(selectors.element_name)}, ${locatorsCount > 0 ? `${locatorsCount} matching elements found` : "no matching elements found"}`;
|
|
777
|
+
}
|
|
820
778
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
821
779
|
}
|
|
822
|
-
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
780
|
+
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly, allowDisabled = false, element_name) {
|
|
823
781
|
let foundElements = [];
|
|
824
782
|
const result = {
|
|
825
783
|
foundElements: foundElements,
|
|
@@ -827,14 +785,15 @@ class StableBrowser {
|
|
|
827
785
|
for (let i = 0; i < locatorsGroup.length; i++) {
|
|
828
786
|
let foundLocators = [];
|
|
829
787
|
try {
|
|
830
|
-
await this._collectLocatorInformation(locatorsGroup, i, scope, foundLocators, _params, info, visibleOnly);
|
|
788
|
+
await this._collectLocatorInformation(locatorsGroup, i, scope, foundLocators, _params, info, visibleOnly, allowDisabled, element_name);
|
|
831
789
|
}
|
|
832
790
|
catch (e) {
|
|
833
|
-
this
|
|
834
|
-
this.logger.debug(
|
|
791
|
+
// this call can fail it the browser is navigating
|
|
792
|
+
// this.logger.debug("unable to use locator " + JSON.stringify(locatorsGroup[i]));
|
|
793
|
+
// this.logger.debug(e);
|
|
835
794
|
foundLocators = [];
|
|
836
795
|
try {
|
|
837
|
-
await this._collectLocatorInformation(locatorsGroup, i, this.page, foundLocators, _params, info, visibleOnly);
|
|
796
|
+
await this._collectLocatorInformation(locatorsGroup, i, this.page, foundLocators, _params, info, visibleOnly, allowDisabled, element_name);
|
|
838
797
|
}
|
|
839
798
|
catch (e) {
|
|
840
799
|
this.logger.info("unable to use locator (second try) " + JSON.stringify(locatorsGroup[i]));
|
|
@@ -850,11 +809,27 @@ class StableBrowser {
|
|
|
850
809
|
}
|
|
851
810
|
if (foundLocators.length > 1) {
|
|
852
811
|
info.failCause.foundMultiple = true;
|
|
812
|
+
if (info.locatorLog) {
|
|
813
|
+
info.locatorLog.setLocatorSearchStatus(JSON.stringify(locatorsGroup[i]), "FOUND_NOT_UNIQUE");
|
|
814
|
+
}
|
|
853
815
|
}
|
|
854
816
|
}
|
|
855
817
|
return result;
|
|
856
818
|
}
|
|
857
819
|
async simpleClick(elementDescription, _params, options = {}, world = null) {
|
|
820
|
+
const state = {
|
|
821
|
+
locate: false,
|
|
822
|
+
scroll: false,
|
|
823
|
+
highlight: false,
|
|
824
|
+
_params,
|
|
825
|
+
options,
|
|
826
|
+
world,
|
|
827
|
+
type: Types.CLICK,
|
|
828
|
+
text: "Click element",
|
|
829
|
+
operation: "simpleClick",
|
|
830
|
+
log: "***** click on " + elementDescription + " *****\n",
|
|
831
|
+
};
|
|
832
|
+
_preCommand(state, this);
|
|
858
833
|
const startTime = Date.now();
|
|
859
834
|
let timeout = 30000;
|
|
860
835
|
if (options && options.timeout) {
|
|
@@ -879,13 +854,31 @@ class StableBrowser {
|
|
|
879
854
|
catch (e) {
|
|
880
855
|
if (performance.now() - startTime > timeout) {
|
|
881
856
|
// throw e;
|
|
882
|
-
|
|
857
|
+
try {
|
|
858
|
+
await _commandError(state, "timeout looking for " + elementDescription, this);
|
|
859
|
+
}
|
|
860
|
+
finally {
|
|
861
|
+
_commandFinally(state, this);
|
|
862
|
+
}
|
|
883
863
|
}
|
|
884
864
|
}
|
|
885
865
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
886
866
|
}
|
|
887
867
|
}
|
|
888
868
|
async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
|
|
869
|
+
const state = {
|
|
870
|
+
locate: false,
|
|
871
|
+
scroll: false,
|
|
872
|
+
highlight: false,
|
|
873
|
+
_params,
|
|
874
|
+
options,
|
|
875
|
+
world,
|
|
876
|
+
type: Types.FILL,
|
|
877
|
+
text: "Fill element",
|
|
878
|
+
operation: "simpleClickType",
|
|
879
|
+
log: "***** click type on " + elementDescription + " *****\n",
|
|
880
|
+
};
|
|
881
|
+
_preCommand(state, this);
|
|
889
882
|
const startTime = Date.now();
|
|
890
883
|
let timeout = 30000;
|
|
891
884
|
if (options && options.timeout) {
|
|
@@ -910,7 +903,12 @@ class StableBrowser {
|
|
|
910
903
|
catch (e) {
|
|
911
904
|
if (performance.now() - startTime > timeout) {
|
|
912
905
|
// throw e;
|
|
913
|
-
|
|
906
|
+
try {
|
|
907
|
+
await _commandError(state, "timeout looking for " + elementDescription, this);
|
|
908
|
+
}
|
|
909
|
+
finally {
|
|
910
|
+
_commandFinally(state, this);
|
|
911
|
+
}
|
|
914
912
|
}
|
|
915
913
|
}
|
|
916
914
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
@@ -929,9 +927,9 @@ class StableBrowser {
|
|
|
929
927
|
};
|
|
930
928
|
try {
|
|
931
929
|
await _preCommand(state, this);
|
|
932
|
-
if (state.options && state.options.context) {
|
|
933
|
-
|
|
934
|
-
}
|
|
930
|
+
// if (state.options && state.options.context) {
|
|
931
|
+
// state.selectors.locators[0].text = state.options.context;
|
|
932
|
+
// }
|
|
935
933
|
try {
|
|
936
934
|
await state.element.click();
|
|
937
935
|
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -969,9 +967,15 @@ class StableBrowser {
|
|
|
969
967
|
// let element = await this._locate(selectors, info, _params);
|
|
970
968
|
// ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
971
969
|
try {
|
|
972
|
-
//
|
|
970
|
+
// if (world && world.screenshot && !world.screenshotPath) {
|
|
971
|
+
// console.log(`Highlighting while running from recorder`);
|
|
972
|
+
await this._highlightElements(element);
|
|
973
973
|
await state.element.setChecked(checked);
|
|
974
974
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
975
|
+
// await this._unHighlightElements(element);
|
|
976
|
+
// }
|
|
977
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
978
|
+
// await this._unHighlightElements(element);
|
|
975
979
|
}
|
|
976
980
|
catch (e) {
|
|
977
981
|
if (e.message && e.message.includes("did not change its state")) {
|
|
@@ -1010,6 +1014,7 @@ class StableBrowser {
|
|
|
1010
1014
|
await _preCommand(state, this);
|
|
1011
1015
|
try {
|
|
1012
1016
|
await state.element.hover();
|
|
1017
|
+
// await _screenshot(state, this);
|
|
1013
1018
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1014
1019
|
}
|
|
1015
1020
|
catch (e) {
|
|
@@ -1017,6 +1022,7 @@ class StableBrowser {
|
|
|
1017
1022
|
state.info.log += "hover failed, will try again" + "\n";
|
|
1018
1023
|
state.element = await this._locate(selectors, state.info, _params);
|
|
1019
1024
|
await state.element.hover({ timeout: 10000 });
|
|
1025
|
+
// await _screenshot(state, this);
|
|
1020
1026
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1021
1027
|
}
|
|
1022
1028
|
await _screenshot(state, this);
|
|
@@ -1291,7 +1297,12 @@ class StableBrowser {
|
|
|
1291
1297
|
await this.waitForPageLoad();
|
|
1292
1298
|
}
|
|
1293
1299
|
else if (enter === false) {
|
|
1294
|
-
|
|
1300
|
+
try {
|
|
1301
|
+
await state.element.dispatchEvent("change", null, { timeout: 5000 });
|
|
1302
|
+
}
|
|
1303
|
+
catch (e) {
|
|
1304
|
+
// ignore
|
|
1305
|
+
}
|
|
1295
1306
|
//await this.page.keyboard.press("Tab");
|
|
1296
1307
|
}
|
|
1297
1308
|
else {
|
|
@@ -1348,6 +1359,7 @@ class StableBrowser {
|
|
|
1348
1359
|
let screenshotPath = null;
|
|
1349
1360
|
if (!info.log) {
|
|
1350
1361
|
info.log = "";
|
|
1362
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
1351
1363
|
}
|
|
1352
1364
|
info.operation = "getText";
|
|
1353
1365
|
info.selectors = selectors;
|
|
@@ -1370,6 +1382,18 @@ class StableBrowser {
|
|
|
1370
1382
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1371
1383
|
try {
|
|
1372
1384
|
await this._highlightElements(element);
|
|
1385
|
+
// if (world && world.screenshot && !world.screenshotPath) {
|
|
1386
|
+
// // console.log(`Highlighting for get text while running from recorder`);
|
|
1387
|
+
// this._highlightElements(element)
|
|
1388
|
+
// .then(async () => {
|
|
1389
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1390
|
+
// this._unhighlightElements(element).then(
|
|
1391
|
+
// () => {}
|
|
1392
|
+
// // console.log(`Unhighlighting vrtr in recorder is successful`)
|
|
1393
|
+
// );
|
|
1394
|
+
// })
|
|
1395
|
+
// .catch(e);
|
|
1396
|
+
// }
|
|
1373
1397
|
const elementText = await element.innerText();
|
|
1374
1398
|
return {
|
|
1375
1399
|
text: elementText,
|
|
@@ -1381,7 +1405,7 @@ class StableBrowser {
|
|
|
1381
1405
|
}
|
|
1382
1406
|
catch (e) {
|
|
1383
1407
|
//await this.closeUnexpectedPopups();
|
|
1384
|
-
this.logger.info("no innerText will use textContent");
|
|
1408
|
+
this.logger.info("no innerText, will use textContent");
|
|
1385
1409
|
const elementText = await element.textContent();
|
|
1386
1410
|
return { text: elementText, screenshotId, screenshotPath, value: value };
|
|
1387
1411
|
}
|
|
@@ -1686,11 +1710,9 @@ class StableBrowser {
|
|
|
1686
1710
|
if (!fs.existsSync(world.screenshotPath)) {
|
|
1687
1711
|
fs.mkdirSync(world.screenshotPath, { recursive: true });
|
|
1688
1712
|
}
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
}
|
|
1693
|
-
const screenshotPath = path.join(world.screenshotPath, nextIndex + ".png");
|
|
1713
|
+
// to make sure the path doesn't start with -
|
|
1714
|
+
const uuidStr = "id_" + randomUUID();
|
|
1715
|
+
const screenshotPath = path.join(world.screenshotPath, uuidStr + ".png");
|
|
1694
1716
|
try {
|
|
1695
1717
|
await this.takeScreenshot(screenshotPath);
|
|
1696
1718
|
// let buffer = await this.page.screenshot({ timeout: 4000 });
|
|
@@ -1700,15 +1722,15 @@ class StableBrowser {
|
|
|
1700
1722
|
// this.logger.info("unable to save screenshot " + screenshotPath);
|
|
1701
1723
|
// }
|
|
1702
1724
|
// });
|
|
1725
|
+
result.screenshotId = uuidStr;
|
|
1726
|
+
result.screenshotPath = screenshotPath;
|
|
1727
|
+
if (info && info.box) {
|
|
1728
|
+
await drawRectangle(screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
|
|
1729
|
+
}
|
|
1703
1730
|
}
|
|
1704
1731
|
catch (e) {
|
|
1705
1732
|
this.logger.info("unable to take screenshot, ignored");
|
|
1706
1733
|
}
|
|
1707
|
-
result.screenshotId = nextIndex;
|
|
1708
|
-
result.screenshotPath = screenshotPath;
|
|
1709
|
-
if (info && info.box) {
|
|
1710
|
-
await drawRectangle(screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
|
|
1711
|
-
}
|
|
1712
1734
|
}
|
|
1713
1735
|
else if (options && options.screenshot) {
|
|
1714
1736
|
result.screenshotPath = options.screenshotPath;
|
|
@@ -1743,6 +1765,15 @@ class StableBrowser {
|
|
|
1743
1765
|
document.documentElement.clientWidth,
|
|
1744
1766
|
])));
|
|
1745
1767
|
let screenshotBuffer = null;
|
|
1768
|
+
// if (focusedElement) {
|
|
1769
|
+
// // console.log(`Focused element ${JSON.stringify(focusedElement._selector)}`)
|
|
1770
|
+
// await this._unhighlightElements(focusedElement);
|
|
1771
|
+
// await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1772
|
+
// console.log(`Unhighlighted previous element`);
|
|
1773
|
+
// }
|
|
1774
|
+
// if (focusedElement) {
|
|
1775
|
+
// await this._highlightElements(focusedElement);
|
|
1776
|
+
// }
|
|
1746
1777
|
if (this.context.browserName === "chromium") {
|
|
1747
1778
|
const client = await playContext.newCDPSession(this.page);
|
|
1748
1779
|
const { data } = await client.send("Page.captureScreenshot", {
|
|
@@ -1764,6 +1795,10 @@ class StableBrowser {
|
|
|
1764
1795
|
else {
|
|
1765
1796
|
screenshotBuffer = await this.page.screenshot();
|
|
1766
1797
|
}
|
|
1798
|
+
// if (focusedElement) {
|
|
1799
|
+
// // console.log(`Focused element ${JSON.stringify(focusedElement._selector)}`)
|
|
1800
|
+
// await this._unhighlightElements(focusedElement);
|
|
1801
|
+
// }
|
|
1767
1802
|
let image = await Jimp.read(screenshotBuffer);
|
|
1768
1803
|
// Get the image dimensions
|
|
1769
1804
|
const { width, height } = image.bitmap;
|
|
@@ -1813,6 +1848,7 @@ class StableBrowser {
|
|
|
1813
1848
|
text: `Extract attribute from element`,
|
|
1814
1849
|
operation: "extractAttribute",
|
|
1815
1850
|
log: "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1851
|
+
allowDisabled: true,
|
|
1816
1852
|
};
|
|
1817
1853
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1818
1854
|
try {
|
|
@@ -1834,6 +1870,70 @@ class StableBrowser {
|
|
|
1834
1870
|
state.info.value = state.value;
|
|
1835
1871
|
this.setTestData({ [variable]: state.value }, world);
|
|
1836
1872
|
this.logger.info("set test data: " + variable + "=" + state.value);
|
|
1873
|
+
// await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1874
|
+
return state.info;
|
|
1875
|
+
}
|
|
1876
|
+
catch (e) {
|
|
1877
|
+
await _commandError(state, e, this);
|
|
1878
|
+
}
|
|
1879
|
+
finally {
|
|
1880
|
+
_commandFinally(state, this);
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
async verifyAttribute(selectors, attribute, value, _params = null, options = {}, world = null) {
|
|
1884
|
+
const state = {
|
|
1885
|
+
selectors,
|
|
1886
|
+
_params,
|
|
1887
|
+
attribute,
|
|
1888
|
+
value,
|
|
1889
|
+
options,
|
|
1890
|
+
world,
|
|
1891
|
+
type: Types.VERIFY_ATTRIBUTE,
|
|
1892
|
+
highlight: true,
|
|
1893
|
+
screenshot: true,
|
|
1894
|
+
text: `Verify element attribute`,
|
|
1895
|
+
operation: "verifyAttribute",
|
|
1896
|
+
log: "***** verify attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1897
|
+
allowDisabled: true,
|
|
1898
|
+
};
|
|
1899
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1900
|
+
let val;
|
|
1901
|
+
try {
|
|
1902
|
+
await _preCommand(state, this);
|
|
1903
|
+
switch (attribute) {
|
|
1904
|
+
case "innerText":
|
|
1905
|
+
val = String(await state.element.innerText());
|
|
1906
|
+
break;
|
|
1907
|
+
case "value":
|
|
1908
|
+
val = String(await state.element.inputValue());
|
|
1909
|
+
break;
|
|
1910
|
+
case "checked":
|
|
1911
|
+
val = String(await state.element.isChecked());
|
|
1912
|
+
break;
|
|
1913
|
+
case "disabled":
|
|
1914
|
+
val = String(await state.element.isDisabled());
|
|
1915
|
+
break;
|
|
1916
|
+
case "readOnly":
|
|
1917
|
+
const isEditable = await state.element.isEditable();
|
|
1918
|
+
val = String(!isEditable);
|
|
1919
|
+
break;
|
|
1920
|
+
default:
|
|
1921
|
+
val = String(await state.element.getAttribute(attribute));
|
|
1922
|
+
break;
|
|
1923
|
+
}
|
|
1924
|
+
state.info.expectedValue = val;
|
|
1925
|
+
let regex;
|
|
1926
|
+
if (value.startsWith("/") && value.endsWith("/")) {
|
|
1927
|
+
const patternBody = value.slice(1, -1);
|
|
1928
|
+
regex = new RegExp(patternBody, "g");
|
|
1929
|
+
}
|
|
1930
|
+
else {
|
|
1931
|
+
const escapedPattern = value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1932
|
+
regex = new RegExp(escapedPattern, "g");
|
|
1933
|
+
}
|
|
1934
|
+
if (!val.match(regex)) {
|
|
1935
|
+
throw new Error(`The ${attribute} attribute has a value of "${val}", but the expected value is "${value}"`);
|
|
1936
|
+
}
|
|
1837
1937
|
return state.info;
|
|
1838
1938
|
}
|
|
1839
1939
|
catch (e) {
|
|
@@ -1861,7 +1961,7 @@ class StableBrowser {
|
|
|
1861
1961
|
if (options && options.timeout) {
|
|
1862
1962
|
timeout = options.timeout;
|
|
1863
1963
|
}
|
|
1864
|
-
const serviceUrl =
|
|
1964
|
+
const serviceUrl = _getServerUrl() + "/api/mail/createLinkOrCodeFromEmail";
|
|
1865
1965
|
const request = {
|
|
1866
1966
|
method: "POST",
|
|
1867
1967
|
url: serviceUrl,
|
|
@@ -1932,27 +2032,32 @@ class StableBrowser {
|
|
|
1932
2032
|
async _highlightElements(scope, css) {
|
|
1933
2033
|
try {
|
|
1934
2034
|
if (!scope) {
|
|
2035
|
+
// console.log(`Scope is not defined`);
|
|
1935
2036
|
return;
|
|
1936
2037
|
}
|
|
1937
2038
|
if (!css) {
|
|
1938
2039
|
scope
|
|
1939
2040
|
.evaluate((node) => {
|
|
1940
2041
|
if (node && node.style) {
|
|
1941
|
-
let
|
|
1942
|
-
|
|
2042
|
+
let originalOutline = node.style.outline;
|
|
2043
|
+
// console.log(`Original outline was: ${originalOutline}`);
|
|
2044
|
+
// node.__previousOutline = originalOutline;
|
|
2045
|
+
node.style.outline = "2px solid red";
|
|
2046
|
+
// console.log(`New outline is: ${node.style.outline}`);
|
|
1943
2047
|
if (window) {
|
|
1944
2048
|
window.addEventListener("beforeunload", function (e) {
|
|
1945
|
-
node.style.
|
|
2049
|
+
node.style.outline = originalOutline;
|
|
1946
2050
|
});
|
|
1947
2051
|
}
|
|
1948
2052
|
setTimeout(function () {
|
|
1949
|
-
node.style.
|
|
2053
|
+
node.style.outline = originalOutline;
|
|
1950
2054
|
}, 2000);
|
|
1951
2055
|
}
|
|
1952
2056
|
})
|
|
1953
2057
|
.then(() => { })
|
|
1954
2058
|
.catch((e) => {
|
|
1955
2059
|
// ignore
|
|
2060
|
+
// console.error(`Could not highlight node : ${e}`);
|
|
1956
2061
|
});
|
|
1957
2062
|
}
|
|
1958
2063
|
else {
|
|
@@ -1968,17 +2073,18 @@ class StableBrowser {
|
|
|
1968
2073
|
if (!element.style) {
|
|
1969
2074
|
return;
|
|
1970
2075
|
}
|
|
1971
|
-
|
|
2076
|
+
let originalOutline = element.style.outline;
|
|
2077
|
+
element.__previousOutline = originalOutline;
|
|
1972
2078
|
// Set the new border to be red and 2px solid
|
|
1973
|
-
element.style.
|
|
2079
|
+
element.style.outline = "2px solid red";
|
|
1974
2080
|
if (window) {
|
|
1975
2081
|
window.addEventListener("beforeunload", function (e) {
|
|
1976
|
-
element.style.
|
|
2082
|
+
element.style.outline = originalOutline;
|
|
1977
2083
|
});
|
|
1978
2084
|
}
|
|
1979
2085
|
// Set a timeout to revert to the original border after 2 seconds
|
|
1980
2086
|
setTimeout(function () {
|
|
1981
|
-
element.style.
|
|
2087
|
+
element.style.outline = originalOutline;
|
|
1982
2088
|
}, 2000);
|
|
1983
2089
|
}
|
|
1984
2090
|
return;
|
|
@@ -1986,6 +2092,7 @@ class StableBrowser {
|
|
|
1986
2092
|
.then(() => { })
|
|
1987
2093
|
.catch((e) => {
|
|
1988
2094
|
// ignore
|
|
2095
|
+
// console.error(`Could not highlight css: ${e}`);
|
|
1989
2096
|
});
|
|
1990
2097
|
}
|
|
1991
2098
|
}
|
|
@@ -1993,6 +2100,54 @@ class StableBrowser {
|
|
|
1993
2100
|
console.debug(error);
|
|
1994
2101
|
}
|
|
1995
2102
|
}
|
|
2103
|
+
// async _unhighlightElements(scope, css) {
|
|
2104
|
+
// try {
|
|
2105
|
+
// if (!scope) {
|
|
2106
|
+
// return;
|
|
2107
|
+
// }
|
|
2108
|
+
// if (!css) {
|
|
2109
|
+
// scope
|
|
2110
|
+
// .evaluate((node) => {
|
|
2111
|
+
// if (node && node.style) {
|
|
2112
|
+
// if (!node.__previousOutline) {
|
|
2113
|
+
// node.style.outline = "";
|
|
2114
|
+
// } else {
|
|
2115
|
+
// node.style.outline = node.__previousOutline;
|
|
2116
|
+
// }
|
|
2117
|
+
// }
|
|
2118
|
+
// })
|
|
2119
|
+
// .then(() => {})
|
|
2120
|
+
// .catch((e) => {
|
|
2121
|
+
// // console.log(`Error while unhighlighting node ${JSON.stringify(scope)}: ${e}`);
|
|
2122
|
+
// });
|
|
2123
|
+
// } else {
|
|
2124
|
+
// scope
|
|
2125
|
+
// .evaluate(([css]) => {
|
|
2126
|
+
// if (!css) {
|
|
2127
|
+
// return;
|
|
2128
|
+
// }
|
|
2129
|
+
// let elements = Array.from(document.querySelectorAll(css));
|
|
2130
|
+
// for (i = 0; i < elements.length; i++) {
|
|
2131
|
+
// let element = elements[i];
|
|
2132
|
+
// if (!element.style) {
|
|
2133
|
+
// return;
|
|
2134
|
+
// }
|
|
2135
|
+
// if (!element.__previousOutline) {
|
|
2136
|
+
// element.style.outline = "";
|
|
2137
|
+
// } else {
|
|
2138
|
+
// element.style.outline = element.__previousOutline;
|
|
2139
|
+
// }
|
|
2140
|
+
// }
|
|
2141
|
+
// })
|
|
2142
|
+
// .then(() => {})
|
|
2143
|
+
// .catch((e) => {
|
|
2144
|
+
// // console.error(`Error while unhighlighting element in css: ${e}`);
|
|
2145
|
+
// });
|
|
2146
|
+
// }
|
|
2147
|
+
// } catch (error) {
|
|
2148
|
+
// // console.debug(error);
|
|
2149
|
+
// }
|
|
2150
|
+
// }
|
|
1996
2151
|
async verifyPagePath(pathPart, options = {}, world = null) {
|
|
1997
2152
|
const startTime = Date.now();
|
|
1998
2153
|
let error = null;
|
|
@@ -2054,6 +2209,35 @@ class StableBrowser {
|
|
|
2054
2209
|
});
|
|
2055
2210
|
}
|
|
2056
2211
|
}
|
|
2212
|
+
async findTextInAllFrames(dateAlternatives, numberAlternatives, text, state) {
|
|
2213
|
+
const frames = this.page.frames();
|
|
2214
|
+
let results = [];
|
|
2215
|
+
let ignoreCase = false;
|
|
2216
|
+
for (let i = 0; i < frames.length; i++) {
|
|
2217
|
+
if (dateAlternatives.date) {
|
|
2218
|
+
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2219
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2220
|
+
result.frame = frames[i];
|
|
2221
|
+
results.push(result);
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
else if (numberAlternatives.number) {
|
|
2225
|
+
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2226
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2227
|
+
result.frame = frames[i];
|
|
2228
|
+
results.push(result);
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
else {
|
|
2232
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2233
|
+
result.frame = frames[i];
|
|
2234
|
+
results.push(result);
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
state.info.results = results;
|
|
2238
|
+
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2239
|
+
return resultWithElementsFound;
|
|
2240
|
+
}
|
|
2057
2241
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2058
2242
|
text = unEscapeString(text);
|
|
2059
2243
|
const state = {
|
|
@@ -2063,7 +2247,7 @@ class StableBrowser {
|
|
|
2063
2247
|
locate: false,
|
|
2064
2248
|
scroll: false,
|
|
2065
2249
|
highlight: false,
|
|
2066
|
-
type: Types.
|
|
2250
|
+
type: Types.VERIFY_PAGE_CONTAINS_TEXT,
|
|
2067
2251
|
text: `Verify text exists in page`,
|
|
2068
2252
|
operation: "verifyTextExistInPage",
|
|
2069
2253
|
log: "***** verify text " + text + " exists in page *****\n",
|
|
@@ -2081,31 +2265,7 @@ class StableBrowser {
|
|
|
2081
2265
|
await _preCommand(state, this);
|
|
2082
2266
|
state.info.text = text;
|
|
2083
2267
|
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);
|
|
2268
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, text, state);
|
|
2109
2269
|
if (resultWithElementsFound.length === 0) {
|
|
2110
2270
|
if (Date.now() - state.startTime > timeout) {
|
|
2111
2271
|
throw new Error(`Text ${text} not found in page`);
|
|
@@ -2115,12 +2275,29 @@ class StableBrowser {
|
|
|
2115
2275
|
}
|
|
2116
2276
|
if (resultWithElementsFound[0].randomToken) {
|
|
2117
2277
|
const frame = resultWithElementsFound[0].frame;
|
|
2118
|
-
const dataAttribute = `[data-blinq-id
|
|
2278
|
+
const dataAttribute = `[data-blinq-id-${resultWithElementsFound[0].randomToken}]`;
|
|
2119
2279
|
await this._highlightElements(frame, dataAttribute);
|
|
2120
|
-
|
|
2280
|
+
// if (world && world.screenshot && !world.screenshotPath) {
|
|
2281
|
+
// console.log(`Highlighting for verify text is found while running from recorder`);
|
|
2282
|
+
// this._highlightElements(frame, dataAttribute).then(async () => {
|
|
2283
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2284
|
+
// this._unhighlightElements(frame, dataAttribute)
|
|
2285
|
+
// .then(async () => {
|
|
2286
|
+
// console.log(`Unhighlighted frame dataAttribute successfully`);
|
|
2287
|
+
// })
|
|
2288
|
+
// .catch(
|
|
2289
|
+
// (e) => {}
|
|
2290
|
+
// console.error(e)
|
|
2291
|
+
// );
|
|
2292
|
+
// });
|
|
2293
|
+
// }
|
|
2294
|
+
const element = await frame.locator(dataAttribute).first();
|
|
2295
|
+
// await new Promise((resolve) => setTimeout(resolve, 100));
|
|
2296
|
+
// await this._unhighlightElements(frame, dataAttribute);
|
|
2121
2297
|
if (element) {
|
|
2122
2298
|
await this.scrollIfNeeded(element, state.info);
|
|
2123
2299
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2300
|
+
// await _screenshot(state, this, element);
|
|
2124
2301
|
}
|
|
2125
2302
|
}
|
|
2126
2303
|
await _screenshot(state, this);
|
|
@@ -2162,31 +2339,7 @@ class StableBrowser {
|
|
|
2162
2339
|
await _preCommand(state, this);
|
|
2163
2340
|
state.info.text = text;
|
|
2164
2341
|
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);
|
|
2342
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, text, state);
|
|
2190
2343
|
if (resultWithElementsFound.length === 0) {
|
|
2191
2344
|
await _screenshot(state, this);
|
|
2192
2345
|
return state.info;
|
|
@@ -2204,15 +2357,102 @@ class StableBrowser {
|
|
|
2204
2357
|
_commandFinally(state, this);
|
|
2205
2358
|
}
|
|
2206
2359
|
}
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2360
|
+
async verifyTextRelatedToText(textAnchor, climb, textToVerify, options = {}, world = null) {
|
|
2361
|
+
textAnchor = unEscapeString(textAnchor);
|
|
2362
|
+
textToVerify = unEscapeString(textToVerify);
|
|
2363
|
+
const state = {
|
|
2364
|
+
text_search: textToVerify,
|
|
2365
|
+
options,
|
|
2366
|
+
world,
|
|
2367
|
+
locate: false,
|
|
2368
|
+
scroll: false,
|
|
2369
|
+
highlight: false,
|
|
2370
|
+
type: Types.VERIFY_TEXT_WITH_RELATION,
|
|
2371
|
+
text: `Verify text with relation to another text`,
|
|
2372
|
+
operation: "verify_text_with_relation",
|
|
2373
|
+
log: "***** search for " + textAnchor + " climb " + climb + " and verify " + textToVerify + " found *****\n",
|
|
2374
|
+
};
|
|
2375
|
+
const timeout = this._getLoadTimeout(options);
|
|
2376
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2377
|
+
let newValue = await this._replaceWithLocalData(textAnchor, world);
|
|
2378
|
+
if (newValue !== textAnchor) {
|
|
2379
|
+
this.logger.info(textAnchor + "=" + newValue);
|
|
2380
|
+
textAnchor = newValue;
|
|
2381
|
+
}
|
|
2382
|
+
newValue = await this._replaceWithLocalData(textToVerify, world);
|
|
2383
|
+
if (newValue !== textToVerify) {
|
|
2384
|
+
this.logger.info(textToVerify + "=" + newValue);
|
|
2385
|
+
textToVerify = newValue;
|
|
2386
|
+
}
|
|
2387
|
+
let dateAlternatives = findDateAlternatives(textToVerify);
|
|
2388
|
+
let numberAlternatives = findNumberAlternatives(textToVerify);
|
|
2389
|
+
let foundAncore = false;
|
|
2390
|
+
try {
|
|
2391
|
+
await _preCommand(state, this);
|
|
2392
|
+
state.info.text = textToVerify;
|
|
2393
|
+
while (true) {
|
|
2394
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, textAnchor, state);
|
|
2395
|
+
if (resultWithElementsFound.length === 0) {
|
|
2396
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2397
|
+
throw new Error(`Text ${foundAncore ? textToVerify : textAnchor} not found in page`);
|
|
2398
|
+
}
|
|
2399
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2400
|
+
continue;
|
|
2401
|
+
}
|
|
2402
|
+
for (let i = 0; i < resultWithElementsFound.length; i++) {
|
|
2403
|
+
foundAncore = true;
|
|
2404
|
+
const result = resultWithElementsFound[i];
|
|
2405
|
+
const token = result.randomToken;
|
|
2406
|
+
const frame = result.frame;
|
|
2407
|
+
let css = `[data-blinq-id-${token}]`;
|
|
2408
|
+
const climbArray1 = [];
|
|
2409
|
+
for (let i = 0; i < climb; i++) {
|
|
2410
|
+
climbArray1.push("..");
|
|
2411
|
+
}
|
|
2412
|
+
let climbXpath = "xpath=" + climbArray1.join("/");
|
|
2413
|
+
css = css + " >> " + climbXpath;
|
|
2414
|
+
const count = await frame.locator(css).count();
|
|
2415
|
+
for (let j = 0; j < count; j++) {
|
|
2416
|
+
const continer = await frame.locator(css).nth(j);
|
|
2417
|
+
const result = await this._locateElementByText(continer, textToVerify, "*", false, true, true, {});
|
|
2418
|
+
if (result.elementCount > 0) {
|
|
2419
|
+
const dataAttribute = "[data-blinq-id-" + result.randomToken + "]";
|
|
2420
|
+
await this._highlightElements(frame, dataAttribute);
|
|
2421
|
+
//const cssAnchor = `[data-blinq-id="blinq-id-${token}-anchor"]`;
|
|
2422
|
+
// if (world && world.screenshot && !world.screenshotPath) {
|
|
2423
|
+
// console.log(`Highlighting for vtrt while running from recorder`);
|
|
2424
|
+
// this._highlightElements(frame, dataAttribute)
|
|
2425
|
+
// .then(async () => {
|
|
2426
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2427
|
+
// this._unhighlightElements(frame, dataAttribute).then(
|
|
2428
|
+
// () => {}
|
|
2429
|
+
// console.log(`Unhighlighting vrtr in recorder is successful`)
|
|
2430
|
+
// );
|
|
2431
|
+
// })
|
|
2432
|
+
// .catch(e);
|
|
2433
|
+
// }
|
|
2434
|
+
//await this._highlightElements(frame, cssAnchor);
|
|
2435
|
+
const element = await frame.locator(dataAttribute).first();
|
|
2436
|
+
// await new Promise((resolve) => setTimeout(resolve, 100));
|
|
2437
|
+
// await this._unhighlightElements(frame, dataAttribute);
|
|
2438
|
+
if (element) {
|
|
2439
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2440
|
+
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2441
|
+
}
|
|
2442
|
+
await _screenshot(state, this);
|
|
2443
|
+
return state.info;
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2211
2449
|
}
|
|
2212
|
-
|
|
2213
|
-
|
|
2450
|
+
catch (e) {
|
|
2451
|
+
await _commandError(state, e, this);
|
|
2452
|
+
}
|
|
2453
|
+
finally {
|
|
2454
|
+
_commandFinally(state, this);
|
|
2214
2455
|
}
|
|
2215
|
-
return serviceUrl;
|
|
2216
2456
|
}
|
|
2217
2457
|
async visualVerification(text, options = {}, world = null) {
|
|
2218
2458
|
const startTime = Date.now();
|
|
@@ -2228,7 +2468,7 @@ class StableBrowser {
|
|
|
2228
2468
|
throw new Error("TOKEN is not set");
|
|
2229
2469
|
}
|
|
2230
2470
|
try {
|
|
2231
|
-
let serviceUrl =
|
|
2471
|
+
let serviceUrl = _getServerUrl();
|
|
2232
2472
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2233
2473
|
info.screenshotPath = screenshotPath;
|
|
2234
2474
|
const screenshot = await this.takeScreenshot();
|
|
@@ -2317,6 +2557,7 @@ class StableBrowser {
|
|
|
2317
2557
|
let screenshotPath = null;
|
|
2318
2558
|
const info = {};
|
|
2319
2559
|
info.log = "";
|
|
2560
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
2320
2561
|
info.operation = "getTableData";
|
|
2321
2562
|
info.selectors = selectors;
|
|
2322
2563
|
try {
|
|
@@ -2392,7 +2633,7 @@ class StableBrowser {
|
|
|
2392
2633
|
info.operation = "analyzeTable";
|
|
2393
2634
|
info.selectors = selectors;
|
|
2394
2635
|
info.query = query;
|
|
2395
|
-
query =
|
|
2636
|
+
query = _fixUsingParams(query, _params);
|
|
2396
2637
|
info.query_fixed = query;
|
|
2397
2638
|
info.operator = operator;
|
|
2398
2639
|
info.value = value;
|
|
@@ -2623,6 +2864,7 @@ class StableBrowser {
|
|
|
2623
2864
|
saveTestDataAsGlobal(options, world) {
|
|
2624
2865
|
const dataFile = this._getDataFile(world);
|
|
2625
2866
|
process.env.GLOBAL_TEST_DATA_FILE = dataFile;
|
|
2867
|
+
this.logger.info("Save the scenario test data as global for the following scenarios.");
|
|
2626
2868
|
}
|
|
2627
2869
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
2628
2870
|
const startTime = Date.now();
|
|
@@ -2721,19 +2963,34 @@ class StableBrowser {
|
|
|
2721
2963
|
}
|
|
2722
2964
|
}
|
|
2723
2965
|
async beforeStep(world, step) {
|
|
2724
|
-
this.stepName = step.pickleStep.text;
|
|
2725
|
-
this.logger.info("step: " + this.stepName);
|
|
2726
2966
|
if (this.stepIndex === undefined) {
|
|
2727
2967
|
this.stepIndex = 0;
|
|
2728
2968
|
}
|
|
2729
2969
|
else {
|
|
2730
2970
|
this.stepIndex++;
|
|
2731
2971
|
}
|
|
2972
|
+
if (step && step.pickleStep && step.pickleStep.text) {
|
|
2973
|
+
this.stepName = step.pickleStep.text;
|
|
2974
|
+
this.logger.info("step: " + this.stepName);
|
|
2975
|
+
}
|
|
2976
|
+
else if (step && step.text) {
|
|
2977
|
+
this.stepName = step.text;
|
|
2978
|
+
}
|
|
2979
|
+
else {
|
|
2980
|
+
this.stepName = "step " + this.stepIndex;
|
|
2981
|
+
}
|
|
2732
2982
|
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2733
2983
|
if (this.context.browserObject.context) {
|
|
2734
2984
|
await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
|
|
2735
2985
|
}
|
|
2736
2986
|
}
|
|
2987
|
+
if (this.tags === null && step && step.pickle && step.pickle.tags) {
|
|
2988
|
+
this.tags = step.pickle.tags.map((tag) => tag.name);
|
|
2989
|
+
// check if @global_test_data tag is present
|
|
2990
|
+
if (this.tags.includes("@global_test_data")) {
|
|
2991
|
+
this.saveTestDataAsGlobal({}, world);
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2737
2994
|
}
|
|
2738
2995
|
async afterStep(world, step) {
|
|
2739
2996
|
this.stepName = null;
|
|
@@ -2751,156 +3008,5 @@ function createTimedPromise(promise, label) {
|
|
|
2751
3008
|
.then((result) => ({ status: "fulfilled", label, result }))
|
|
2752
3009
|
.catch((error) => Promise.reject({ status: "rejected", label, error }));
|
|
2753
3010
|
}
|
|
2754
|
-
const KEYBOARD_EVENTS = [
|
|
2755
|
-
"ALT",
|
|
2756
|
-
"AltGraph",
|
|
2757
|
-
"CapsLock",
|
|
2758
|
-
"Control",
|
|
2759
|
-
"Fn",
|
|
2760
|
-
"FnLock",
|
|
2761
|
-
"Hyper",
|
|
2762
|
-
"Meta",
|
|
2763
|
-
"NumLock",
|
|
2764
|
-
"ScrollLock",
|
|
2765
|
-
"Shift",
|
|
2766
|
-
"Super",
|
|
2767
|
-
"Symbol",
|
|
2768
|
-
"SymbolLock",
|
|
2769
|
-
"Enter",
|
|
2770
|
-
"Tab",
|
|
2771
|
-
"ArrowDown",
|
|
2772
|
-
"ArrowLeft",
|
|
2773
|
-
"ArrowRight",
|
|
2774
|
-
"ArrowUp",
|
|
2775
|
-
"End",
|
|
2776
|
-
"Home",
|
|
2777
|
-
"PageDown",
|
|
2778
|
-
"PageUp",
|
|
2779
|
-
"Backspace",
|
|
2780
|
-
"Clear",
|
|
2781
|
-
"Copy",
|
|
2782
|
-
"CrSel",
|
|
2783
|
-
"Cut",
|
|
2784
|
-
"Delete",
|
|
2785
|
-
"EraseEof",
|
|
2786
|
-
"ExSel",
|
|
2787
|
-
"Insert",
|
|
2788
|
-
"Paste",
|
|
2789
|
-
"Redo",
|
|
2790
|
-
"Undo",
|
|
2791
|
-
"Accept",
|
|
2792
|
-
"Again",
|
|
2793
|
-
"Attn",
|
|
2794
|
-
"Cancel",
|
|
2795
|
-
"ContextMenu",
|
|
2796
|
-
"Escape",
|
|
2797
|
-
"Execute",
|
|
2798
|
-
"Find",
|
|
2799
|
-
"Finish",
|
|
2800
|
-
"Help",
|
|
2801
|
-
"Pause",
|
|
2802
|
-
"Play",
|
|
2803
|
-
"Props",
|
|
2804
|
-
"Select",
|
|
2805
|
-
"ZoomIn",
|
|
2806
|
-
"ZoomOut",
|
|
2807
|
-
"BrightnessDown",
|
|
2808
|
-
"BrightnessUp",
|
|
2809
|
-
"Eject",
|
|
2810
|
-
"LogOff",
|
|
2811
|
-
"Power",
|
|
2812
|
-
"PowerOff",
|
|
2813
|
-
"PrintScreen",
|
|
2814
|
-
"Hibernate",
|
|
2815
|
-
"Standby",
|
|
2816
|
-
"WakeUp",
|
|
2817
|
-
"AllCandidates",
|
|
2818
|
-
"Alphanumeric",
|
|
2819
|
-
"CodeInput",
|
|
2820
|
-
"Compose",
|
|
2821
|
-
"Convert",
|
|
2822
|
-
"Dead",
|
|
2823
|
-
"FinalMode",
|
|
2824
|
-
"GroupFirst",
|
|
2825
|
-
"GroupLast",
|
|
2826
|
-
"GroupNext",
|
|
2827
|
-
"GroupPrevious",
|
|
2828
|
-
"ModeChange",
|
|
2829
|
-
"NextCandidate",
|
|
2830
|
-
"NonConvert",
|
|
2831
|
-
"PreviousCandidate",
|
|
2832
|
-
"Process",
|
|
2833
|
-
"SingleCandidate",
|
|
2834
|
-
"HangulMode",
|
|
2835
|
-
"HanjaMode",
|
|
2836
|
-
"JunjaMode",
|
|
2837
|
-
"Eisu",
|
|
2838
|
-
"Hankaku",
|
|
2839
|
-
"Hiragana",
|
|
2840
|
-
"HiraganaKatakana",
|
|
2841
|
-
"KanaMode",
|
|
2842
|
-
"KanjiMode",
|
|
2843
|
-
"Katakana",
|
|
2844
|
-
"Romaji",
|
|
2845
|
-
"Zenkaku",
|
|
2846
|
-
"ZenkakuHanaku",
|
|
2847
|
-
"F1",
|
|
2848
|
-
"F2",
|
|
2849
|
-
"F3",
|
|
2850
|
-
"F4",
|
|
2851
|
-
"F5",
|
|
2852
|
-
"F6",
|
|
2853
|
-
"F7",
|
|
2854
|
-
"F8",
|
|
2855
|
-
"F9",
|
|
2856
|
-
"F10",
|
|
2857
|
-
"F11",
|
|
2858
|
-
"F12",
|
|
2859
|
-
"Soft1",
|
|
2860
|
-
"Soft2",
|
|
2861
|
-
"Soft3",
|
|
2862
|
-
"Soft4",
|
|
2863
|
-
"ChannelDown",
|
|
2864
|
-
"ChannelUp",
|
|
2865
|
-
"Close",
|
|
2866
|
-
"MailForward",
|
|
2867
|
-
"MailReply",
|
|
2868
|
-
"MailSend",
|
|
2869
|
-
"MediaFastForward",
|
|
2870
|
-
"MediaPause",
|
|
2871
|
-
"MediaPlay",
|
|
2872
|
-
"MediaPlayPause",
|
|
2873
|
-
"MediaRecord",
|
|
2874
|
-
"MediaRewind",
|
|
2875
|
-
"MediaStop",
|
|
2876
|
-
"MediaTrackNext",
|
|
2877
|
-
"MediaTrackPrevious",
|
|
2878
|
-
"AudioBalanceLeft",
|
|
2879
|
-
"AudioBalanceRight",
|
|
2880
|
-
"AudioBassBoostDown",
|
|
2881
|
-
"AudioBassBoostToggle",
|
|
2882
|
-
"AudioBassBoostUp",
|
|
2883
|
-
"AudioFaderFront",
|
|
2884
|
-
"AudioFaderRear",
|
|
2885
|
-
"AudioSurroundModeNext",
|
|
2886
|
-
"AudioTrebleDown",
|
|
2887
|
-
"AudioTrebleUp",
|
|
2888
|
-
"AudioVolumeDown",
|
|
2889
|
-
"AudioVolumeMute",
|
|
2890
|
-
"AudioVolumeUp",
|
|
2891
|
-
"MicrophoneToggle",
|
|
2892
|
-
"MicrophoneVolumeDown",
|
|
2893
|
-
"MicrophoneVolumeMute",
|
|
2894
|
-
"MicrophoneVolumeUp",
|
|
2895
|
-
"TV",
|
|
2896
|
-
"TV3DMode",
|
|
2897
|
-
"TVAntennaCable",
|
|
2898
|
-
"TVAudioDescription",
|
|
2899
|
-
];
|
|
2900
|
-
function unEscapeString(str) {
|
|
2901
|
-
const placeholder = "__NEWLINE__";
|
|
2902
|
-
str = str.replace(new RegExp(placeholder, "g"), "\n");
|
|
2903
|
-
return str;
|
|
2904
|
-
}
|
|
2905
3011
|
export { StableBrowser };
|
|
2906
3012
|
//# sourceMappingURL=stable_browser.js.map
|