automation_model 1.0.523-dev → 1.0.523-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 +51 -9
- 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 +49 -15
- package/lib/stable_browser.js +566 -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;
|
|
@@ -89,17 +98,17 @@ class StableBrowser {
|
|
|
89
98
|
catch (e) {
|
|
90
99
|
this.logger.error("unable to read ai_config.json");
|
|
91
100
|
}
|
|
92
|
-
context.pageLoading = { status: false };
|
|
93
|
-
context.pages = [this.page];
|
|
94
101
|
const logFolder = path.join(this.project_path, "logs", "web");
|
|
95
102
|
this.world = world;
|
|
103
|
+
context.pages = [this.page];
|
|
104
|
+
context.pageLoading = { status: false };
|
|
96
105
|
this.registerEventListeners(this.context);
|
|
97
106
|
registerNetworkEvents(this.world, this, this.context, this.page);
|
|
98
107
|
registerDownloadEvent(this.page, this.world, this.context);
|
|
99
108
|
}
|
|
100
109
|
registerEventListeners(context) {
|
|
101
110
|
this.registerConsoleLogListener(this.page, context);
|
|
102
|
-
this.registerRequestListener(this.page, context, this.webLogFile);
|
|
111
|
+
// this.registerRequestListener(this.page, context, this.webLogFile);
|
|
103
112
|
if (!context.pageLoading) {
|
|
104
113
|
context.pageLoading = { status: false };
|
|
105
114
|
}
|
|
@@ -144,7 +153,7 @@ class StableBrowser {
|
|
|
144
153
|
if (this.appName === appName) {
|
|
145
154
|
return;
|
|
146
155
|
}
|
|
147
|
-
let
|
|
156
|
+
let navigate = false;
|
|
148
157
|
if (!apps[appName]) {
|
|
149
158
|
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this, -1, this.context.reportFolder);
|
|
150
159
|
newContextCreated = true;
|
|
@@ -155,32 +164,15 @@ class StableBrowser {
|
|
|
155
164
|
};
|
|
156
165
|
}
|
|
157
166
|
const tempContext = {};
|
|
158
|
-
|
|
159
|
-
|
|
167
|
+
_copyContext(this, tempContext);
|
|
168
|
+
_copyContext(apps[appName], this);
|
|
160
169
|
apps[this.appName] = tempContext;
|
|
161
170
|
this.appName = appName;
|
|
162
|
-
if (
|
|
163
|
-
this.registerEventListeners(this.context);
|
|
171
|
+
if (navigate) {
|
|
164
172
|
await this.goto(this.context.environment.baseUrl);
|
|
165
173
|
await this.waitForPageLoad();
|
|
166
174
|
}
|
|
167
175
|
}
|
|
168
|
-
_copyContext(from, to) {
|
|
169
|
-
to.browser = from.browser;
|
|
170
|
-
to.page = from.page;
|
|
171
|
-
to.context = from.context;
|
|
172
|
-
}
|
|
173
|
-
getWebLogFile(logFolder) {
|
|
174
|
-
if (!fs.existsSync(logFolder)) {
|
|
175
|
-
fs.mkdirSync(logFolder, { recursive: true });
|
|
176
|
-
}
|
|
177
|
-
let nextIndex = 1;
|
|
178
|
-
while (fs.existsSync(path.join(logFolder, nextIndex.toString() + ".json"))) {
|
|
179
|
-
nextIndex++;
|
|
180
|
-
}
|
|
181
|
-
const fileName = nextIndex + ".json";
|
|
182
|
-
return path.join(logFolder, fileName);
|
|
183
|
-
}
|
|
184
176
|
registerConsoleLogListener(page, context) {
|
|
185
177
|
if (!this.context.webLogger) {
|
|
186
178
|
this.context.webLogger = [];
|
|
@@ -244,55 +236,48 @@ class StableBrowser {
|
|
|
244
236
|
// async closeUnexpectedPopups() {
|
|
245
237
|
// await closeUnexpectedPopups(this.page);
|
|
246
238
|
// }
|
|
247
|
-
async goto(url) {
|
|
239
|
+
async goto(url, world = null) {
|
|
248
240
|
if (!url.startsWith("http")) {
|
|
249
241
|
url = "https://" + url;
|
|
250
242
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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);
|
|
258
262
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
// remove the _ prefix
|
|
263
|
-
regValue = key.substring(1);
|
|
264
|
-
}
|
|
265
|
-
text = text.replaceAll(new RegExp("{" + regValue + "}", "g"), _params[key]);
|
|
263
|
+
catch (error) {
|
|
264
|
+
console.error("Error on goto", error);
|
|
265
|
+
_commandError(state, error, this);
|
|
266
266
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
_fixLocatorUsingParams(locator, _params) {
|
|
270
|
-
// check if not null
|
|
271
|
-
if (!locator) {
|
|
272
|
-
return locator;
|
|
267
|
+
finally {
|
|
268
|
+
_commandFinally(state, this);
|
|
273
269
|
}
|
|
274
|
-
// clone the locator
|
|
275
|
-
locator = JSON.parse(JSON.stringify(locator));
|
|
276
|
-
this.scanAndManipulate(locator, _params);
|
|
277
|
-
return locator;
|
|
278
270
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
}
|
|
288
|
-
else if (this._isObject(currentObj[key])) {
|
|
289
|
-
// Recursively scan nested objects
|
|
290
|
-
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);
|
|
291
279
|
}
|
|
292
280
|
}
|
|
293
|
-
}
|
|
294
|
-
_getLocator(locator, scope, _params) {
|
|
295
|
-
locator = this._fixLocatorUsingParams(locator, _params);
|
|
296
281
|
let locatorReturn;
|
|
297
282
|
if (locator.role) {
|
|
298
283
|
if (locator.role[1].nameReg) {
|
|
@@ -300,7 +285,7 @@ class StableBrowser {
|
|
|
300
285
|
delete locator.role[1].nameReg;
|
|
301
286
|
}
|
|
302
287
|
// if (locator.role[1].name) {
|
|
303
|
-
// locator.role[1].name =
|
|
288
|
+
// locator.role[1].name = _fixUsingParams(locator.role[1].name, _params);
|
|
304
289
|
// }
|
|
305
290
|
locatorReturn = scope.getByRole(locator.role[0], locator.role[1]);
|
|
306
291
|
}
|
|
@@ -343,140 +328,54 @@ class StableBrowser {
|
|
|
343
328
|
if (css && css.locator) {
|
|
344
329
|
css = css.locator;
|
|
345
330
|
}
|
|
346
|
-
let result = await this._locateElementByText(scope,
|
|
331
|
+
let result = await this._locateElementByText(scope, _fixUsingParams(text, _params), "*:not(script, style, head)", false, false, true, _params);
|
|
347
332
|
if (result.elementCount === 0) {
|
|
348
333
|
return;
|
|
349
334
|
}
|
|
350
|
-
let textElementCss = "[data-blinq-id
|
|
335
|
+
let textElementCss = "[data-blinq-id-" + result.randomToken + "]";
|
|
351
336
|
// css climb to parent element
|
|
352
337
|
const climbArray = [];
|
|
353
338
|
for (let i = 0; i < climb; i++) {
|
|
354
339
|
climbArray.push("..");
|
|
355
340
|
}
|
|
356
341
|
let climbXpath = "xpath=" + climbArray.join("/");
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
return
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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;
|
|
367
364
|
}
|
|
368
|
-
currentNode = currentNode.parentNode;
|
|
369
|
-
}
|
|
370
|
-
return false;
|
|
371
|
-
}
|
|
372
|
-
document.isParent = isParent;
|
|
373
|
-
function getRegex(str) {
|
|
374
|
-
const match = str.match(/^\/(.*?)\/([gimuy]*)$/);
|
|
375
|
-
if (!match) {
|
|
376
|
-
return null;
|
|
377
|
-
}
|
|
378
|
-
let [_, pattern, flags] = match;
|
|
379
|
-
return new RegExp(pattern, flags);
|
|
380
|
-
}
|
|
381
|
-
document.getRegex = getRegex;
|
|
382
|
-
function collectAllShadowDomElements(element, result = []) {
|
|
383
|
-
// Check and add the element if it has a shadow root
|
|
384
|
-
if (element.shadowRoot) {
|
|
385
|
-
result.push(element);
|
|
386
|
-
// Also search within the shadow root
|
|
387
|
-
document.collectAllShadowDomElements(element.shadowRoot, result);
|
|
388
|
-
}
|
|
389
|
-
// Iterate over child nodes
|
|
390
|
-
element.childNodes.forEach((child) => {
|
|
391
|
-
// Recursively call the function for each child node
|
|
392
|
-
document.collectAllShadowDomElements(child, result);
|
|
393
|
-
});
|
|
394
|
-
return result;
|
|
395
|
-
}
|
|
396
|
-
document.collectAllShadowDomElements = collectAllShadowDomElements;
|
|
397
|
-
if (!tag) {
|
|
398
|
-
tag = "*:not(script, style, head)";
|
|
399
|
-
}
|
|
400
|
-
let regexpSearch = document.getRegex(text);
|
|
401
|
-
if (regexpSearch) {
|
|
402
|
-
regex = true;
|
|
403
|
-
}
|
|
404
|
-
let elements = Array.from(document.querySelectorAll(tag));
|
|
405
|
-
let shadowHosts = [];
|
|
406
|
-
document.collectAllShadowDomElements(document, shadowHosts);
|
|
407
|
-
for (let i = 0; i < shadowHosts.length; i++) {
|
|
408
|
-
let shadowElement = shadowHosts[i].shadowRoot;
|
|
409
|
-
if (!shadowElement) {
|
|
410
|
-
console.log("shadowElement is null, for host " + shadowHosts[i]);
|
|
411
|
-
continue;
|
|
412
|
-
}
|
|
413
|
-
let shadowElements = Array.from(shadowElement.querySelectorAll(tag));
|
|
414
|
-
elements = elements.concat(shadowElements);
|
|
415
|
-
}
|
|
416
|
-
let randomToken = null;
|
|
417
|
-
const foundElements = [];
|
|
418
|
-
if (regex) {
|
|
419
|
-
if (!regexpSearch) {
|
|
420
|
-
regexpSearch = new RegExp(text, "im");
|
|
421
365
|
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
if ((element.innerText && regexpSearch.test(element.innerText)) ||
|
|
425
|
-
(element.value && regexpSearch.test(element.value))) {
|
|
426
|
-
foundElements.push(element);
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
else {
|
|
431
|
-
text = text.trim();
|
|
432
|
-
for (let i = 0; i < elements.length; i++) {
|
|
433
|
-
const element = elements[i];
|
|
434
|
-
if (partial) {
|
|
435
|
-
if ((element.innerText && element.innerText.toLowerCase().trim().includes(text.toLowerCase())) ||
|
|
436
|
-
(element.value && element.value.toLowerCase().includes(text.toLowerCase()))) {
|
|
437
|
-
foundElements.push(element);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
else {
|
|
441
|
-
if ((element.innerText && element.innerText.trim() === text) ||
|
|
442
|
-
(element.value && element.value === text)) {
|
|
443
|
-
foundElements.push(element);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
let noChildElements = [];
|
|
449
|
-
for (let i = 0; i < foundElements.length; i++) {
|
|
450
|
-
let element = foundElements[i];
|
|
451
|
-
let hasChild = false;
|
|
452
|
-
for (let j = 0; j < foundElements.length; j++) {
|
|
453
|
-
if (i === j) {
|
|
454
|
-
continue;
|
|
455
|
-
}
|
|
456
|
-
if (isParent(element, foundElements[j])) {
|
|
457
|
-
hasChild = true;
|
|
458
|
-
break;
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
if (!hasChild) {
|
|
462
|
-
noChildElements.push(element);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
let elementCount = 0;
|
|
466
|
-
if (noChildElements.length > 0) {
|
|
467
|
-
for (let i = 0; i < noChildElements.length; i++) {
|
|
468
|
-
if (randomToken === null) {
|
|
469
|
-
randomToken = Math.random().toString(36).substring(7);
|
|
470
|
-
}
|
|
471
|
-
let element = noChildElements[i];
|
|
472
|
-
element.setAttribute("data-blinq-id", "blinq-id-" + randomToken);
|
|
473
|
-
elementCount++;
|
|
366
|
+
if (!el.setAttribute) {
|
|
367
|
+
el = el.parentElement;
|
|
474
368
|
}
|
|
369
|
+
el.setAttribute("data-blinq-id-" + randomToken, "");
|
|
370
|
+
return true;
|
|
371
|
+
}, [tag1, randomToken]))) {
|
|
372
|
+
continue;
|
|
475
373
|
}
|
|
476
|
-
|
|
477
|
-
}
|
|
374
|
+
tagCount++;
|
|
375
|
+
}
|
|
376
|
+
return { elementCount: tagCount, randomToken };
|
|
478
377
|
}
|
|
479
|
-
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) {
|
|
480
379
|
if (!info) {
|
|
481
380
|
info = {};
|
|
482
381
|
}
|
|
@@ -485,10 +384,13 @@ class StableBrowser {
|
|
|
485
384
|
}
|
|
486
385
|
if (!info.log) {
|
|
487
386
|
info.log = "";
|
|
387
|
+
info.locatorLog = new LocatorLog(selectorHierarchy);
|
|
488
388
|
}
|
|
489
389
|
let locatorSearch = selectorHierarchy[index];
|
|
390
|
+
let originalLocatorSearch = "";
|
|
490
391
|
try {
|
|
491
|
-
|
|
392
|
+
originalLocatorSearch = _fixUsingParams(JSON.stringify(locatorSearch), _params);
|
|
393
|
+
locatorSearch = JSON.parse(originalLocatorSearch);
|
|
492
394
|
}
|
|
493
395
|
catch (e) {
|
|
494
396
|
console.error(e);
|
|
@@ -496,30 +398,31 @@ class StableBrowser {
|
|
|
496
398
|
//info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
|
|
497
399
|
let locator = null;
|
|
498
400
|
if (locatorSearch.climb && locatorSearch.climb >= 0) {
|
|
499
|
-
|
|
401
|
+
const replacedText = await this._replaceWithLocalData(locatorSearch.text, this.world);
|
|
402
|
+
let locatorString = await this._locateElmentByTextClimbCss(scope, replacedText, locatorSearch.climb, locatorSearch.css, _params);
|
|
500
403
|
if (!locatorString) {
|
|
501
404
|
info.failCause.textNotFound = true;
|
|
502
|
-
info.failCause.lastError =
|
|
405
|
+
info.failCause.lastError = `failed to locate ${formatElementName(element_name)} by text: ${locatorSearch.text}`;
|
|
503
406
|
return;
|
|
504
407
|
}
|
|
505
|
-
locator = this._getLocator({ css: locatorString }, scope, _params);
|
|
408
|
+
locator = await this._getLocator({ css: locatorString }, scope, _params);
|
|
506
409
|
}
|
|
507
410
|
else if (locatorSearch.text) {
|
|
508
|
-
let text =
|
|
509
|
-
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);
|
|
510
413
|
if (result.elementCount === 0) {
|
|
511
414
|
info.failCause.textNotFound = true;
|
|
512
|
-
info.failCause.lastError =
|
|
415
|
+
info.failCause.lastError = `failed to locate ${formatElementName(element_name)} by text: ${text}`;
|
|
513
416
|
return;
|
|
514
417
|
}
|
|
515
|
-
locatorSearch.css = "[data-blinq-id
|
|
418
|
+
locatorSearch.css = "[data-blinq-id-" + result.randomToken + "]";
|
|
516
419
|
if (locatorSearch.childCss) {
|
|
517
420
|
locatorSearch.css = locatorSearch.css + " " + locatorSearch.childCss;
|
|
518
421
|
}
|
|
519
|
-
locator = this._getLocator(locatorSearch, scope, _params);
|
|
422
|
+
locator = await this._getLocator(locatorSearch, scope, _params);
|
|
520
423
|
}
|
|
521
424
|
else {
|
|
522
|
-
locator = this._getLocator(locatorSearch, scope, _params);
|
|
425
|
+
locator = await this._getLocator(locatorSearch, scope, _params);
|
|
523
426
|
}
|
|
524
427
|
// let cssHref = false;
|
|
525
428
|
// if (locatorSearch.css && locatorSearch.css.includes("href=")) {
|
|
@@ -532,18 +435,27 @@ class StableBrowser {
|
|
|
532
435
|
//info.log += "total elements found " + count + "\n";
|
|
533
436
|
//let visibleCount = 0;
|
|
534
437
|
let visibleLocator = null;
|
|
535
|
-
if (locatorSearch.index && locatorSearch.index < count) {
|
|
438
|
+
if (typeof locatorSearch.index === "number" && locatorSearch.index < count) {
|
|
536
439
|
foundLocators.push(locator.nth(locatorSearch.index));
|
|
440
|
+
if (info.locatorLog) {
|
|
441
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND");
|
|
442
|
+
}
|
|
537
443
|
return;
|
|
538
444
|
}
|
|
445
|
+
if (info.locatorLog && count === 0) {
|
|
446
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "NOT_FOUND");
|
|
447
|
+
}
|
|
539
448
|
for (let j = 0; j < count; j++) {
|
|
540
449
|
let visible = await locator.nth(j).isVisible();
|
|
541
450
|
const enabled = await locator.nth(j).isEnabled();
|
|
542
451
|
if (!visibleOnly) {
|
|
543
452
|
visible = true;
|
|
544
453
|
}
|
|
545
|
-
if (visible && enabled) {
|
|
454
|
+
if (visible && (allowDisabled || enabled)) {
|
|
546
455
|
foundLocators.push(locator.nth(j));
|
|
456
|
+
if (info.locatorLog) {
|
|
457
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND");
|
|
458
|
+
}
|
|
547
459
|
}
|
|
548
460
|
else {
|
|
549
461
|
info.failCause.visible = visible;
|
|
@@ -551,8 +463,16 @@ class StableBrowser {
|
|
|
551
463
|
if (!info.printMessages) {
|
|
552
464
|
info.printMessages = {};
|
|
553
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
|
+
}
|
|
554
474
|
if (!info.printMessages[j.toString()]) {
|
|
555
|
-
info.log += "element " + locator + " visible " + visible + " enabled " + enabled + "\n";
|
|
475
|
+
//info.log += "element " + locator + " visible " + visible + " enabled " + enabled + "\n";
|
|
556
476
|
info.printMessages[j.toString()] = true;
|
|
557
477
|
}
|
|
558
478
|
}
|
|
@@ -568,7 +488,7 @@ class StableBrowser {
|
|
|
568
488
|
if (!info) {
|
|
569
489
|
info = {};
|
|
570
490
|
}
|
|
571
|
-
info.log += "scan for popup handlers" + "\n";
|
|
491
|
+
//info.log += "scan for popup handlers" + "\n";
|
|
572
492
|
const handlerGroup = [];
|
|
573
493
|
for (let i = 0; i < this.configuration.popupHandlers.length; i++) {
|
|
574
494
|
handlerGroup.push(this.configuration.popupHandlers[i].locator);
|
|
@@ -595,16 +515,28 @@ class StableBrowser {
|
|
|
595
515
|
}
|
|
596
516
|
if (result.foundElements.length > 0) {
|
|
597
517
|
let dialogCloseLocator = result.foundElements[0].locator;
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
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
|
+
}
|
|
601
533
|
return { rerun: true };
|
|
602
534
|
}
|
|
603
535
|
}
|
|
604
536
|
}
|
|
605
537
|
return { rerun: false };
|
|
606
538
|
}
|
|
607
|
-
async _locate(selectors, info, _params, timeout) {
|
|
539
|
+
async _locate(selectors, info, _params, timeout, allowDisabled = false) {
|
|
608
540
|
if (!timeout) {
|
|
609
541
|
timeout = 30000;
|
|
610
542
|
}
|
|
@@ -614,7 +546,7 @@ class StableBrowser {
|
|
|
614
546
|
let selector = selectors.locators[j];
|
|
615
547
|
info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
|
|
616
548
|
}
|
|
617
|
-
let element = await this._locate_internal(selectors, info, _params, timeout);
|
|
549
|
+
let element = await this._locate_internal(selectors, info, _params, timeout, allowDisabled);
|
|
618
550
|
if (!element.rerun) {
|
|
619
551
|
return element;
|
|
620
552
|
}
|
|
@@ -627,6 +559,7 @@ class StableBrowser {
|
|
|
627
559
|
info.failCause = {};
|
|
628
560
|
info.log = "";
|
|
629
561
|
}
|
|
562
|
+
let startTime = Date.now();
|
|
630
563
|
let scope = this.page;
|
|
631
564
|
if (selectors.frame) {
|
|
632
565
|
return selectors.frame;
|
|
@@ -657,9 +590,11 @@ class StableBrowser {
|
|
|
657
590
|
}
|
|
658
591
|
return framescope;
|
|
659
592
|
};
|
|
593
|
+
let fLocator = null;
|
|
660
594
|
while (true) {
|
|
661
595
|
let frameFound = false;
|
|
662
596
|
if (selectors.nestFrmLoc) {
|
|
597
|
+
fLocator = selectors.nestFrmLoc;
|
|
663
598
|
scope = await findFrame(selectors.nestFrmLoc, scope);
|
|
664
599
|
frameFound = true;
|
|
665
600
|
break;
|
|
@@ -668,6 +603,7 @@ class StableBrowser {
|
|
|
668
603
|
for (let i = 0; i < selectors.frameLocators.length; i++) {
|
|
669
604
|
let frameLocator = selectors.frameLocators[i];
|
|
670
605
|
if (frameLocator.css) {
|
|
606
|
+
fLocator = frameLocator.css;
|
|
671
607
|
scope = scope.frameLocator(frameLocator.css);
|
|
672
608
|
frameFound = true;
|
|
673
609
|
break;
|
|
@@ -675,18 +611,25 @@ class StableBrowser {
|
|
|
675
611
|
}
|
|
676
612
|
}
|
|
677
613
|
if (!frameFound && selectors.iframe_src) {
|
|
614
|
+
fLocator = selectors.iframe_src;
|
|
678
615
|
scope = this.page.frame({ url: selectors.iframe_src });
|
|
679
616
|
}
|
|
680
617
|
if (!scope) {
|
|
681
|
-
info
|
|
682
|
-
|
|
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) {
|
|
683
623
|
info.failCause.iframeNotFound = true;
|
|
684
|
-
info.failCause.lastError =
|
|
624
|
+
info.failCause.lastError = `unable to locate iframe "${selectors.iframe_src}"`;
|
|
685
625
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
686
626
|
}
|
|
687
627
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
688
628
|
}
|
|
689
629
|
else {
|
|
630
|
+
if (info && info.locatorLog) {
|
|
631
|
+
info.locatorLog.setLocatorSearchStatus("frame-" + fLocator, "FOUND");
|
|
632
|
+
}
|
|
690
633
|
break;
|
|
691
634
|
}
|
|
692
635
|
}
|
|
@@ -703,16 +646,18 @@ class StableBrowser {
|
|
|
703
646
|
return bodyContent;
|
|
704
647
|
});
|
|
705
648
|
}
|
|
706
|
-
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
649
|
+
async _locate_internal(selectors, info, _params, timeout = 30000, allowDisabled = false) {
|
|
707
650
|
if (!info) {
|
|
708
651
|
info = {};
|
|
709
652
|
info.failCause = {};
|
|
710
653
|
info.log = "";
|
|
654
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
711
655
|
}
|
|
712
656
|
let highPriorityTimeout = 5000;
|
|
713
657
|
let visibleOnlyTimeout = 6000;
|
|
714
|
-
let startTime =
|
|
658
|
+
let startTime = Date.now();
|
|
715
659
|
let locatorsCount = 0;
|
|
660
|
+
let lazy_scroll = false;
|
|
716
661
|
//let arrayMode = Array.isArray(selectors);
|
|
717
662
|
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
718
663
|
let selectorsLocators = null;
|
|
@@ -750,17 +695,17 @@ class StableBrowser {
|
|
|
750
695
|
}
|
|
751
696
|
// info.log += "scanning locators in priority 1" + "\n";
|
|
752
697
|
let onlyPriority3 = selectorsLocators[0].priority === 3;
|
|
753
|
-
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);
|
|
754
699
|
if (result.foundElements.length === 0) {
|
|
755
700
|
// info.log += "scanning locators in priority 2" + "\n";
|
|
756
|
-
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);
|
|
757
702
|
}
|
|
758
703
|
if (result.foundElements.length === 0 && onlyPriority3) {
|
|
759
|
-
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);
|
|
760
705
|
}
|
|
761
706
|
else {
|
|
762
707
|
if (result.foundElements.length === 0 && !highPriorityOnly) {
|
|
763
|
-
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);
|
|
764
709
|
}
|
|
765
710
|
}
|
|
766
711
|
let foundElements = result.foundElements;
|
|
@@ -801,26 +746,38 @@ class StableBrowser {
|
|
|
801
746
|
return maxCountElement.locator;
|
|
802
747
|
}
|
|
803
748
|
}
|
|
804
|
-
if (
|
|
749
|
+
if (Date.now() - startTime > timeout) {
|
|
805
750
|
break;
|
|
806
751
|
}
|
|
807
|
-
if (
|
|
808
|
-
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";
|
|
809
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
|
+
}
|
|
810
759
|
}
|
|
811
|
-
if (
|
|
812
|
-
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";
|
|
813
762
|
visibleOnly = false;
|
|
814
763
|
}
|
|
815
764
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
816
765
|
}
|
|
817
766
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
818
|
-
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";
|
|
819
774
|
info.failCause.locatorNotFound = true;
|
|
820
|
-
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
|
+
}
|
|
821
778
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
822
779
|
}
|
|
823
|
-
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
780
|
+
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly, allowDisabled = false, element_name) {
|
|
824
781
|
let foundElements = [];
|
|
825
782
|
const result = {
|
|
826
783
|
foundElements: foundElements,
|
|
@@ -828,14 +785,15 @@ class StableBrowser {
|
|
|
828
785
|
for (let i = 0; i < locatorsGroup.length; i++) {
|
|
829
786
|
let foundLocators = [];
|
|
830
787
|
try {
|
|
831
|
-
await this._collectLocatorInformation(locatorsGroup, i, scope, foundLocators, _params, info, visibleOnly);
|
|
788
|
+
await this._collectLocatorInformation(locatorsGroup, i, scope, foundLocators, _params, info, visibleOnly, allowDisabled, element_name);
|
|
832
789
|
}
|
|
833
790
|
catch (e) {
|
|
834
|
-
this
|
|
835
|
-
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);
|
|
836
794
|
foundLocators = [];
|
|
837
795
|
try {
|
|
838
|
-
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);
|
|
839
797
|
}
|
|
840
798
|
catch (e) {
|
|
841
799
|
this.logger.info("unable to use locator (second try) " + JSON.stringify(locatorsGroup[i]));
|
|
@@ -851,11 +809,27 @@ class StableBrowser {
|
|
|
851
809
|
}
|
|
852
810
|
if (foundLocators.length > 1) {
|
|
853
811
|
info.failCause.foundMultiple = true;
|
|
812
|
+
if (info.locatorLog) {
|
|
813
|
+
info.locatorLog.setLocatorSearchStatus(JSON.stringify(locatorsGroup[i]), "FOUND_NOT_UNIQUE");
|
|
814
|
+
}
|
|
854
815
|
}
|
|
855
816
|
}
|
|
856
817
|
return result;
|
|
857
818
|
}
|
|
858
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);
|
|
859
833
|
const startTime = Date.now();
|
|
860
834
|
let timeout = 30000;
|
|
861
835
|
if (options && options.timeout) {
|
|
@@ -880,13 +854,31 @@ class StableBrowser {
|
|
|
880
854
|
catch (e) {
|
|
881
855
|
if (performance.now() - startTime > timeout) {
|
|
882
856
|
// throw e;
|
|
883
|
-
|
|
857
|
+
try {
|
|
858
|
+
await _commandError(state, "timeout looking for " + elementDescription, this);
|
|
859
|
+
}
|
|
860
|
+
finally {
|
|
861
|
+
_commandFinally(state, this);
|
|
862
|
+
}
|
|
884
863
|
}
|
|
885
864
|
}
|
|
886
865
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
887
866
|
}
|
|
888
867
|
}
|
|
889
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);
|
|
890
882
|
const startTime = Date.now();
|
|
891
883
|
let timeout = 30000;
|
|
892
884
|
if (options && options.timeout) {
|
|
@@ -911,7 +903,12 @@ class StableBrowser {
|
|
|
911
903
|
catch (e) {
|
|
912
904
|
if (performance.now() - startTime > timeout) {
|
|
913
905
|
// throw e;
|
|
914
|
-
|
|
906
|
+
try {
|
|
907
|
+
await _commandError(state, "timeout looking for " + elementDescription, this);
|
|
908
|
+
}
|
|
909
|
+
finally {
|
|
910
|
+
_commandFinally(state, this);
|
|
911
|
+
}
|
|
915
912
|
}
|
|
916
913
|
}
|
|
917
914
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
@@ -930,9 +927,9 @@ class StableBrowser {
|
|
|
930
927
|
};
|
|
931
928
|
try {
|
|
932
929
|
await _preCommand(state, this);
|
|
933
|
-
if (state.options && state.options.context) {
|
|
934
|
-
|
|
935
|
-
}
|
|
930
|
+
// if (state.options && state.options.context) {
|
|
931
|
+
// state.selectors.locators[0].text = state.options.context;
|
|
932
|
+
// }
|
|
936
933
|
try {
|
|
937
934
|
await state.element.click();
|
|
938
935
|
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -970,9 +967,15 @@ class StableBrowser {
|
|
|
970
967
|
// let element = await this._locate(selectors, info, _params);
|
|
971
968
|
// ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
972
969
|
try {
|
|
973
|
-
//
|
|
970
|
+
// if (world && world.screenshot && !world.screenshotPath) {
|
|
971
|
+
// console.log(`Highlighting while running from recorder`);
|
|
972
|
+
await this._highlightElements(element);
|
|
974
973
|
await state.element.setChecked(checked);
|
|
975
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);
|
|
976
979
|
}
|
|
977
980
|
catch (e) {
|
|
978
981
|
if (e.message && e.message.includes("did not change its state")) {
|
|
@@ -1011,6 +1014,7 @@ class StableBrowser {
|
|
|
1011
1014
|
await _preCommand(state, this);
|
|
1012
1015
|
try {
|
|
1013
1016
|
await state.element.hover();
|
|
1017
|
+
// await _screenshot(state, this);
|
|
1014
1018
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1015
1019
|
}
|
|
1016
1020
|
catch (e) {
|
|
@@ -1018,6 +1022,7 @@ class StableBrowser {
|
|
|
1018
1022
|
state.info.log += "hover failed, will try again" + "\n";
|
|
1019
1023
|
state.element = await this._locate(selectors, state.info, _params);
|
|
1020
1024
|
await state.element.hover({ timeout: 10000 });
|
|
1025
|
+
// await _screenshot(state, this);
|
|
1021
1026
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1022
1027
|
}
|
|
1023
1028
|
await _screenshot(state, this);
|
|
@@ -1292,7 +1297,12 @@ class StableBrowser {
|
|
|
1292
1297
|
await this.waitForPageLoad();
|
|
1293
1298
|
}
|
|
1294
1299
|
else if (enter === false) {
|
|
1295
|
-
|
|
1300
|
+
try {
|
|
1301
|
+
await state.element.dispatchEvent("change", null, { timeout: 5000 });
|
|
1302
|
+
}
|
|
1303
|
+
catch (e) {
|
|
1304
|
+
// ignore
|
|
1305
|
+
}
|
|
1296
1306
|
//await this.page.keyboard.press("Tab");
|
|
1297
1307
|
}
|
|
1298
1308
|
else {
|
|
@@ -1349,6 +1359,7 @@ class StableBrowser {
|
|
|
1349
1359
|
let screenshotPath = null;
|
|
1350
1360
|
if (!info.log) {
|
|
1351
1361
|
info.log = "";
|
|
1362
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
1352
1363
|
}
|
|
1353
1364
|
info.operation = "getText";
|
|
1354
1365
|
info.selectors = selectors;
|
|
@@ -1371,6 +1382,18 @@ class StableBrowser {
|
|
|
1371
1382
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1372
1383
|
try {
|
|
1373
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
|
+
// }
|
|
1374
1397
|
const elementText = await element.innerText();
|
|
1375
1398
|
return {
|
|
1376
1399
|
text: elementText,
|
|
@@ -1382,7 +1405,7 @@ class StableBrowser {
|
|
|
1382
1405
|
}
|
|
1383
1406
|
catch (e) {
|
|
1384
1407
|
//await this.closeUnexpectedPopups();
|
|
1385
|
-
this.logger.info("no innerText will use textContent");
|
|
1408
|
+
this.logger.info("no innerText, will use textContent");
|
|
1386
1409
|
const elementText = await element.textContent();
|
|
1387
1410
|
return { text: elementText, screenshotId, screenshotPath, value: value };
|
|
1388
1411
|
}
|
|
@@ -1687,11 +1710,9 @@ class StableBrowser {
|
|
|
1687
1710
|
if (!fs.existsSync(world.screenshotPath)) {
|
|
1688
1711
|
fs.mkdirSync(world.screenshotPath, { recursive: true });
|
|
1689
1712
|
}
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
}
|
|
1694
|
-
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");
|
|
1695
1716
|
try {
|
|
1696
1717
|
await this.takeScreenshot(screenshotPath);
|
|
1697
1718
|
// let buffer = await this.page.screenshot({ timeout: 4000 });
|
|
@@ -1701,15 +1722,15 @@ class StableBrowser {
|
|
|
1701
1722
|
// this.logger.info("unable to save screenshot " + screenshotPath);
|
|
1702
1723
|
// }
|
|
1703
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
|
+
}
|
|
1704
1730
|
}
|
|
1705
1731
|
catch (e) {
|
|
1706
1732
|
this.logger.info("unable to take screenshot, ignored");
|
|
1707
1733
|
}
|
|
1708
|
-
result.screenshotId = nextIndex;
|
|
1709
|
-
result.screenshotPath = screenshotPath;
|
|
1710
|
-
if (info && info.box) {
|
|
1711
|
-
await drawRectangle(screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
|
|
1712
|
-
}
|
|
1713
1734
|
}
|
|
1714
1735
|
else if (options && options.screenshot) {
|
|
1715
1736
|
result.screenshotPath = options.screenshotPath;
|
|
@@ -1744,6 +1765,15 @@ class StableBrowser {
|
|
|
1744
1765
|
document.documentElement.clientWidth,
|
|
1745
1766
|
])));
|
|
1746
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
|
+
// }
|
|
1747
1777
|
if (this.context.browserName === "chromium") {
|
|
1748
1778
|
const client = await playContext.newCDPSession(this.page);
|
|
1749
1779
|
const { data } = await client.send("Page.captureScreenshot", {
|
|
@@ -1765,6 +1795,10 @@ class StableBrowser {
|
|
|
1765
1795
|
else {
|
|
1766
1796
|
screenshotBuffer = await this.page.screenshot();
|
|
1767
1797
|
}
|
|
1798
|
+
// if (focusedElement) {
|
|
1799
|
+
// // console.log(`Focused element ${JSON.stringify(focusedElement._selector)}`)
|
|
1800
|
+
// await this._unhighlightElements(focusedElement);
|
|
1801
|
+
// }
|
|
1768
1802
|
let image = await Jimp.read(screenshotBuffer);
|
|
1769
1803
|
// Get the image dimensions
|
|
1770
1804
|
const { width, height } = image.bitmap;
|
|
@@ -1814,6 +1848,7 @@ class StableBrowser {
|
|
|
1814
1848
|
text: `Extract attribute from element`,
|
|
1815
1849
|
operation: "extractAttribute",
|
|
1816
1850
|
log: "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1851
|
+
allowDisabled: true,
|
|
1817
1852
|
};
|
|
1818
1853
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1819
1854
|
try {
|
|
@@ -1835,6 +1870,70 @@ class StableBrowser {
|
|
|
1835
1870
|
state.info.value = state.value;
|
|
1836
1871
|
this.setTestData({ [variable]: state.value }, world);
|
|
1837
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
|
+
}
|
|
1838
1937
|
return state.info;
|
|
1839
1938
|
}
|
|
1840
1939
|
catch (e) {
|
|
@@ -1862,7 +1961,7 @@ class StableBrowser {
|
|
|
1862
1961
|
if (options && options.timeout) {
|
|
1863
1962
|
timeout = options.timeout;
|
|
1864
1963
|
}
|
|
1865
|
-
const serviceUrl =
|
|
1964
|
+
const serviceUrl = _getServerUrl() + "/api/mail/createLinkOrCodeFromEmail";
|
|
1866
1965
|
const request = {
|
|
1867
1966
|
method: "POST",
|
|
1868
1967
|
url: serviceUrl,
|
|
@@ -1933,27 +2032,32 @@ class StableBrowser {
|
|
|
1933
2032
|
async _highlightElements(scope, css) {
|
|
1934
2033
|
try {
|
|
1935
2034
|
if (!scope) {
|
|
2035
|
+
// console.log(`Scope is not defined`);
|
|
1936
2036
|
return;
|
|
1937
2037
|
}
|
|
1938
2038
|
if (!css) {
|
|
1939
2039
|
scope
|
|
1940
2040
|
.evaluate((node) => {
|
|
1941
2041
|
if (node && node.style) {
|
|
1942
|
-
let
|
|
1943
|
-
|
|
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}`);
|
|
1944
2047
|
if (window) {
|
|
1945
2048
|
window.addEventListener("beforeunload", function (e) {
|
|
1946
|
-
node.style.
|
|
2049
|
+
node.style.outline = originalOutline;
|
|
1947
2050
|
});
|
|
1948
2051
|
}
|
|
1949
2052
|
setTimeout(function () {
|
|
1950
|
-
node.style.
|
|
2053
|
+
node.style.outline = originalOutline;
|
|
1951
2054
|
}, 2000);
|
|
1952
2055
|
}
|
|
1953
2056
|
})
|
|
1954
2057
|
.then(() => { })
|
|
1955
2058
|
.catch((e) => {
|
|
1956
2059
|
// ignore
|
|
2060
|
+
// console.error(`Could not highlight node : ${e}`);
|
|
1957
2061
|
});
|
|
1958
2062
|
}
|
|
1959
2063
|
else {
|
|
@@ -1969,17 +2073,18 @@ class StableBrowser {
|
|
|
1969
2073
|
if (!element.style) {
|
|
1970
2074
|
return;
|
|
1971
2075
|
}
|
|
1972
|
-
|
|
2076
|
+
let originalOutline = element.style.outline;
|
|
2077
|
+
element.__previousOutline = originalOutline;
|
|
1973
2078
|
// Set the new border to be red and 2px solid
|
|
1974
|
-
element.style.
|
|
2079
|
+
element.style.outline = "2px solid red";
|
|
1975
2080
|
if (window) {
|
|
1976
2081
|
window.addEventListener("beforeunload", function (e) {
|
|
1977
|
-
element.style.
|
|
2082
|
+
element.style.outline = originalOutline;
|
|
1978
2083
|
});
|
|
1979
2084
|
}
|
|
1980
2085
|
// Set a timeout to revert to the original border after 2 seconds
|
|
1981
2086
|
setTimeout(function () {
|
|
1982
|
-
element.style.
|
|
2087
|
+
element.style.outline = originalOutline;
|
|
1983
2088
|
}, 2000);
|
|
1984
2089
|
}
|
|
1985
2090
|
return;
|
|
@@ -1987,6 +2092,7 @@ class StableBrowser {
|
|
|
1987
2092
|
.then(() => { })
|
|
1988
2093
|
.catch((e) => {
|
|
1989
2094
|
// ignore
|
|
2095
|
+
// console.error(`Could not highlight css: ${e}`);
|
|
1990
2096
|
});
|
|
1991
2097
|
}
|
|
1992
2098
|
}
|
|
@@ -1994,6 +2100,54 @@ class StableBrowser {
|
|
|
1994
2100
|
console.debug(error);
|
|
1995
2101
|
}
|
|
1996
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
|
+
// }
|
|
1997
2151
|
async verifyPagePath(pathPart, options = {}, world = null) {
|
|
1998
2152
|
const startTime = Date.now();
|
|
1999
2153
|
let error = null;
|
|
@@ -2055,6 +2209,35 @@ class StableBrowser {
|
|
|
2055
2209
|
});
|
|
2056
2210
|
}
|
|
2057
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
|
+
}
|
|
2058
2241
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2059
2242
|
text = unEscapeString(text);
|
|
2060
2243
|
const state = {
|
|
@@ -2064,7 +2247,7 @@ class StableBrowser {
|
|
|
2064
2247
|
locate: false,
|
|
2065
2248
|
scroll: false,
|
|
2066
2249
|
highlight: false,
|
|
2067
|
-
type: Types.
|
|
2250
|
+
type: Types.VERIFY_PAGE_CONTAINS_TEXT,
|
|
2068
2251
|
text: `Verify text exists in page`,
|
|
2069
2252
|
operation: "verifyTextExistInPage",
|
|
2070
2253
|
log: "***** verify text " + text + " exists in page *****\n",
|
|
@@ -2082,31 +2265,7 @@ class StableBrowser {
|
|
|
2082
2265
|
await _preCommand(state, this);
|
|
2083
2266
|
state.info.text = text;
|
|
2084
2267
|
while (true) {
|
|
2085
|
-
const
|
|
2086
|
-
let results = [];
|
|
2087
|
-
for (let i = 0; i < frames.length; i++) {
|
|
2088
|
-
if (dateAlternatives.date) {
|
|
2089
|
-
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2090
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2091
|
-
result.frame = frames[i];
|
|
2092
|
-
results.push(result);
|
|
2093
|
-
}
|
|
2094
|
-
}
|
|
2095
|
-
else if (numberAlternatives.number) {
|
|
2096
|
-
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2097
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2098
|
-
result.frame = frames[i];
|
|
2099
|
-
results.push(result);
|
|
2100
|
-
}
|
|
2101
|
-
}
|
|
2102
|
-
else {
|
|
2103
|
-
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2104
|
-
result.frame = frames[i];
|
|
2105
|
-
results.push(result);
|
|
2106
|
-
}
|
|
2107
|
-
}
|
|
2108
|
-
state.info.results = results;
|
|
2109
|
-
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2268
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, text, state);
|
|
2110
2269
|
if (resultWithElementsFound.length === 0) {
|
|
2111
2270
|
if (Date.now() - state.startTime > timeout) {
|
|
2112
2271
|
throw new Error(`Text ${text} not found in page`);
|
|
@@ -2116,12 +2275,29 @@ class StableBrowser {
|
|
|
2116
2275
|
}
|
|
2117
2276
|
if (resultWithElementsFound[0].randomToken) {
|
|
2118
2277
|
const frame = resultWithElementsFound[0].frame;
|
|
2119
|
-
const dataAttribute = `[data-blinq-id
|
|
2278
|
+
const dataAttribute = `[data-blinq-id-${resultWithElementsFound[0].randomToken}]`;
|
|
2120
2279
|
await this._highlightElements(frame, dataAttribute);
|
|
2121
|
-
|
|
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);
|
|
2122
2297
|
if (element) {
|
|
2123
2298
|
await this.scrollIfNeeded(element, state.info);
|
|
2124
2299
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2300
|
+
// await _screenshot(state, this, element);
|
|
2125
2301
|
}
|
|
2126
2302
|
}
|
|
2127
2303
|
await _screenshot(state, this);
|
|
@@ -2163,31 +2339,7 @@ class StableBrowser {
|
|
|
2163
2339
|
await _preCommand(state, this);
|
|
2164
2340
|
state.info.text = text;
|
|
2165
2341
|
while (true) {
|
|
2166
|
-
const
|
|
2167
|
-
let results = [];
|
|
2168
|
-
for (let i = 0; i < frames.length; i++) {
|
|
2169
|
-
if (dateAlternatives.date) {
|
|
2170
|
-
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2171
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2172
|
-
result.frame = frames[i];
|
|
2173
|
-
results.push(result);
|
|
2174
|
-
}
|
|
2175
|
-
}
|
|
2176
|
-
else if (numberAlternatives.number) {
|
|
2177
|
-
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2178
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2179
|
-
result.frame = frames[i];
|
|
2180
|
-
results.push(result);
|
|
2181
|
-
}
|
|
2182
|
-
}
|
|
2183
|
-
else {
|
|
2184
|
-
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2185
|
-
result.frame = frames[i];
|
|
2186
|
-
results.push(result);
|
|
2187
|
-
}
|
|
2188
|
-
}
|
|
2189
|
-
state.info.results = results;
|
|
2190
|
-
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2342
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, text, state);
|
|
2191
2343
|
if (resultWithElementsFound.length === 0) {
|
|
2192
2344
|
await _screenshot(state, this);
|
|
2193
2345
|
return state.info;
|
|
@@ -2205,15 +2357,102 @@ class StableBrowser {
|
|
|
2205
2357
|
_commandFinally(state, this);
|
|
2206
2358
|
}
|
|
2207
2359
|
}
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
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 });
|
|
2449
|
+
}
|
|
2450
|
+
catch (e) {
|
|
2451
|
+
await _commandError(state, e, this);
|
|
2212
2452
|
}
|
|
2213
|
-
|
|
2214
|
-
|
|
2453
|
+
finally {
|
|
2454
|
+
_commandFinally(state, this);
|
|
2215
2455
|
}
|
|
2216
|
-
return serviceUrl;
|
|
2217
2456
|
}
|
|
2218
2457
|
async visualVerification(text, options = {}, world = null) {
|
|
2219
2458
|
const startTime = Date.now();
|
|
@@ -2229,7 +2468,7 @@ class StableBrowser {
|
|
|
2229
2468
|
throw new Error("TOKEN is not set");
|
|
2230
2469
|
}
|
|
2231
2470
|
try {
|
|
2232
|
-
let serviceUrl =
|
|
2471
|
+
let serviceUrl = _getServerUrl();
|
|
2233
2472
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2234
2473
|
info.screenshotPath = screenshotPath;
|
|
2235
2474
|
const screenshot = await this.takeScreenshot();
|
|
@@ -2318,6 +2557,7 @@ class StableBrowser {
|
|
|
2318
2557
|
let screenshotPath = null;
|
|
2319
2558
|
const info = {};
|
|
2320
2559
|
info.log = "";
|
|
2560
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
2321
2561
|
info.operation = "getTableData";
|
|
2322
2562
|
info.selectors = selectors;
|
|
2323
2563
|
try {
|
|
@@ -2393,7 +2633,7 @@ class StableBrowser {
|
|
|
2393
2633
|
info.operation = "analyzeTable";
|
|
2394
2634
|
info.selectors = selectors;
|
|
2395
2635
|
info.query = query;
|
|
2396
|
-
query =
|
|
2636
|
+
query = _fixUsingParams(query, _params);
|
|
2397
2637
|
info.query_fixed = query;
|
|
2398
2638
|
info.operator = operator;
|
|
2399
2639
|
info.value = value;
|
|
@@ -2723,14 +2963,22 @@ class StableBrowser {
|
|
|
2723
2963
|
}
|
|
2724
2964
|
}
|
|
2725
2965
|
async beforeStep(world, step) {
|
|
2726
|
-
this.stepName = step.pickleStep.text;
|
|
2727
|
-
this.logger.info("step: " + this.stepName);
|
|
2728
2966
|
if (this.stepIndex === undefined) {
|
|
2729
2967
|
this.stepIndex = 0;
|
|
2730
2968
|
}
|
|
2731
2969
|
else {
|
|
2732
2970
|
this.stepIndex++;
|
|
2733
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
|
+
}
|
|
2734
2982
|
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2735
2983
|
if (this.context.browserObject.context) {
|
|
2736
2984
|
await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
|
|
@@ -2760,156 +3008,5 @@ function createTimedPromise(promise, label) {
|
|
|
2760
3008
|
.then((result) => ({ status: "fulfilled", label, result }))
|
|
2761
3009
|
.catch((error) => Promise.reject({ status: "rejected", label, error }));
|
|
2762
3010
|
}
|
|
2763
|
-
const KEYBOARD_EVENTS = [
|
|
2764
|
-
"ALT",
|
|
2765
|
-
"AltGraph",
|
|
2766
|
-
"CapsLock",
|
|
2767
|
-
"Control",
|
|
2768
|
-
"Fn",
|
|
2769
|
-
"FnLock",
|
|
2770
|
-
"Hyper",
|
|
2771
|
-
"Meta",
|
|
2772
|
-
"NumLock",
|
|
2773
|
-
"ScrollLock",
|
|
2774
|
-
"Shift",
|
|
2775
|
-
"Super",
|
|
2776
|
-
"Symbol",
|
|
2777
|
-
"SymbolLock",
|
|
2778
|
-
"Enter",
|
|
2779
|
-
"Tab",
|
|
2780
|
-
"ArrowDown",
|
|
2781
|
-
"ArrowLeft",
|
|
2782
|
-
"ArrowRight",
|
|
2783
|
-
"ArrowUp",
|
|
2784
|
-
"End",
|
|
2785
|
-
"Home",
|
|
2786
|
-
"PageDown",
|
|
2787
|
-
"PageUp",
|
|
2788
|
-
"Backspace",
|
|
2789
|
-
"Clear",
|
|
2790
|
-
"Copy",
|
|
2791
|
-
"CrSel",
|
|
2792
|
-
"Cut",
|
|
2793
|
-
"Delete",
|
|
2794
|
-
"EraseEof",
|
|
2795
|
-
"ExSel",
|
|
2796
|
-
"Insert",
|
|
2797
|
-
"Paste",
|
|
2798
|
-
"Redo",
|
|
2799
|
-
"Undo",
|
|
2800
|
-
"Accept",
|
|
2801
|
-
"Again",
|
|
2802
|
-
"Attn",
|
|
2803
|
-
"Cancel",
|
|
2804
|
-
"ContextMenu",
|
|
2805
|
-
"Escape",
|
|
2806
|
-
"Execute",
|
|
2807
|
-
"Find",
|
|
2808
|
-
"Finish",
|
|
2809
|
-
"Help",
|
|
2810
|
-
"Pause",
|
|
2811
|
-
"Play",
|
|
2812
|
-
"Props",
|
|
2813
|
-
"Select",
|
|
2814
|
-
"ZoomIn",
|
|
2815
|
-
"ZoomOut",
|
|
2816
|
-
"BrightnessDown",
|
|
2817
|
-
"BrightnessUp",
|
|
2818
|
-
"Eject",
|
|
2819
|
-
"LogOff",
|
|
2820
|
-
"Power",
|
|
2821
|
-
"PowerOff",
|
|
2822
|
-
"PrintScreen",
|
|
2823
|
-
"Hibernate",
|
|
2824
|
-
"Standby",
|
|
2825
|
-
"WakeUp",
|
|
2826
|
-
"AllCandidates",
|
|
2827
|
-
"Alphanumeric",
|
|
2828
|
-
"CodeInput",
|
|
2829
|
-
"Compose",
|
|
2830
|
-
"Convert",
|
|
2831
|
-
"Dead",
|
|
2832
|
-
"FinalMode",
|
|
2833
|
-
"GroupFirst",
|
|
2834
|
-
"GroupLast",
|
|
2835
|
-
"GroupNext",
|
|
2836
|
-
"GroupPrevious",
|
|
2837
|
-
"ModeChange",
|
|
2838
|
-
"NextCandidate",
|
|
2839
|
-
"NonConvert",
|
|
2840
|
-
"PreviousCandidate",
|
|
2841
|
-
"Process",
|
|
2842
|
-
"SingleCandidate",
|
|
2843
|
-
"HangulMode",
|
|
2844
|
-
"HanjaMode",
|
|
2845
|
-
"JunjaMode",
|
|
2846
|
-
"Eisu",
|
|
2847
|
-
"Hankaku",
|
|
2848
|
-
"Hiragana",
|
|
2849
|
-
"HiraganaKatakana",
|
|
2850
|
-
"KanaMode",
|
|
2851
|
-
"KanjiMode",
|
|
2852
|
-
"Katakana",
|
|
2853
|
-
"Romaji",
|
|
2854
|
-
"Zenkaku",
|
|
2855
|
-
"ZenkakuHanaku",
|
|
2856
|
-
"F1",
|
|
2857
|
-
"F2",
|
|
2858
|
-
"F3",
|
|
2859
|
-
"F4",
|
|
2860
|
-
"F5",
|
|
2861
|
-
"F6",
|
|
2862
|
-
"F7",
|
|
2863
|
-
"F8",
|
|
2864
|
-
"F9",
|
|
2865
|
-
"F10",
|
|
2866
|
-
"F11",
|
|
2867
|
-
"F12",
|
|
2868
|
-
"Soft1",
|
|
2869
|
-
"Soft2",
|
|
2870
|
-
"Soft3",
|
|
2871
|
-
"Soft4",
|
|
2872
|
-
"ChannelDown",
|
|
2873
|
-
"ChannelUp",
|
|
2874
|
-
"Close",
|
|
2875
|
-
"MailForward",
|
|
2876
|
-
"MailReply",
|
|
2877
|
-
"MailSend",
|
|
2878
|
-
"MediaFastForward",
|
|
2879
|
-
"MediaPause",
|
|
2880
|
-
"MediaPlay",
|
|
2881
|
-
"MediaPlayPause",
|
|
2882
|
-
"MediaRecord",
|
|
2883
|
-
"MediaRewind",
|
|
2884
|
-
"MediaStop",
|
|
2885
|
-
"MediaTrackNext",
|
|
2886
|
-
"MediaTrackPrevious",
|
|
2887
|
-
"AudioBalanceLeft",
|
|
2888
|
-
"AudioBalanceRight",
|
|
2889
|
-
"AudioBassBoostDown",
|
|
2890
|
-
"AudioBassBoostToggle",
|
|
2891
|
-
"AudioBassBoostUp",
|
|
2892
|
-
"AudioFaderFront",
|
|
2893
|
-
"AudioFaderRear",
|
|
2894
|
-
"AudioSurroundModeNext",
|
|
2895
|
-
"AudioTrebleDown",
|
|
2896
|
-
"AudioTrebleUp",
|
|
2897
|
-
"AudioVolumeDown",
|
|
2898
|
-
"AudioVolumeMute",
|
|
2899
|
-
"AudioVolumeUp",
|
|
2900
|
-
"MicrophoneToggle",
|
|
2901
|
-
"MicrophoneVolumeDown",
|
|
2902
|
-
"MicrophoneVolumeMute",
|
|
2903
|
-
"MicrophoneVolumeUp",
|
|
2904
|
-
"TV",
|
|
2905
|
-
"TV3DMode",
|
|
2906
|
-
"TVAntennaCable",
|
|
2907
|
-
"TVAudioDescription",
|
|
2908
|
-
];
|
|
2909
|
-
function unEscapeString(str) {
|
|
2910
|
-
const placeholder = "__NEWLINE__";
|
|
2911
|
-
str = str.replace(new RegExp(placeholder, "g"), "\n");
|
|
2912
|
-
return str;
|
|
2913
|
-
}
|
|
2914
3011
|
export { StableBrowser };
|
|
2915
3012
|
//# sourceMappingURL=stable_browser.js.map
|