automation_model 1.0.526-dev → 1.0.526-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 +12 -11
- package/lib/api.js.map +1 -1
- package/lib/auto_page.d.ts +2 -1
- package/lib/auto_page.js +7 -3
- 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 +12 -0
- 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 +29 -5
- 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 -16
- package/lib/stable_browser.js +583 -483
- package/lib/stable_browser.js.map +1 -1
- package/lib/utils.d.ts +14 -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,7 +10,7 @@ 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, _getDataFile, } from "./utils.js";
|
|
14
14
|
import csv from "csv-parser";
|
|
15
15
|
import { Readable } from "node:stream";
|
|
16
16
|
import readline from "readline";
|
|
@@ -19,7 +19,8 @@ import { locate_element } from "./locate_element.js";
|
|
|
19
19
|
import { randomUUID } from "crypto";
|
|
20
20
|
import { _commandError, _commandFinally, _preCommand, _validateSelectors, _screenshot, _reportToWorld, } from "./command_common.js";
|
|
21
21
|
import { registerDownloadEvent, registerNetworkEvents } from "./network.js";
|
|
22
|
-
|
|
22
|
+
import { LocatorLog } from "./locator_log.js";
|
|
23
|
+
export const Types = {
|
|
23
24
|
CLICK: "click_element",
|
|
24
25
|
NAVIGATE: "navigate",
|
|
25
26
|
FILL: "fill_element",
|
|
@@ -30,6 +31,8 @@ const Types = {
|
|
|
30
31
|
GET_PAGE_STATUS: "get_page_status",
|
|
31
32
|
CLICK_ROW_ACTION: "click_row_action",
|
|
32
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",
|
|
33
36
|
ANALYZE_TABLE: "analyze_table",
|
|
34
37
|
SELECT: "select_combobox",
|
|
35
38
|
VERIFY_PAGE_PATH: "verify_page_path",
|
|
@@ -46,8 +49,13 @@ const Types = {
|
|
|
46
49
|
LOAD_DATA: "load_data",
|
|
47
50
|
SET_INPUT: "set_input",
|
|
48
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",
|
|
49
54
|
};
|
|
50
55
|
export const apps = {};
|
|
56
|
+
const formatElementName = (elementName) => {
|
|
57
|
+
return elementName ? JSON.stringify(elementName) : "element";
|
|
58
|
+
};
|
|
51
59
|
class StableBrowser {
|
|
52
60
|
browser;
|
|
53
61
|
page;
|
|
@@ -90,17 +98,17 @@ class StableBrowser {
|
|
|
90
98
|
catch (e) {
|
|
91
99
|
this.logger.error("unable to read ai_config.json");
|
|
92
100
|
}
|
|
93
|
-
context.pageLoading = { status: false };
|
|
94
|
-
context.pages = [this.page];
|
|
95
101
|
const logFolder = path.join(this.project_path, "logs", "web");
|
|
96
102
|
this.world = world;
|
|
103
|
+
context.pages = [this.page];
|
|
104
|
+
context.pageLoading = { status: false };
|
|
97
105
|
this.registerEventListeners(this.context);
|
|
98
106
|
registerNetworkEvents(this.world, this, this.context, this.page);
|
|
99
107
|
registerDownloadEvent(this.page, this.world, this.context);
|
|
100
108
|
}
|
|
101
109
|
registerEventListeners(context) {
|
|
102
110
|
this.registerConsoleLogListener(this.page, context);
|
|
103
|
-
this.registerRequestListener(this.page, context, this.webLogFile);
|
|
111
|
+
// this.registerRequestListener(this.page, context, this.webLogFile);
|
|
104
112
|
if (!context.pageLoading) {
|
|
105
113
|
context.pageLoading = { status: false };
|
|
106
114
|
}
|
|
@@ -145,7 +153,7 @@ class StableBrowser {
|
|
|
145
153
|
if (this.appName === appName) {
|
|
146
154
|
return;
|
|
147
155
|
}
|
|
148
|
-
let
|
|
156
|
+
let navigate = false;
|
|
149
157
|
if (!apps[appName]) {
|
|
150
158
|
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this, -1, this.context.reportFolder);
|
|
151
159
|
newContextCreated = true;
|
|
@@ -156,32 +164,15 @@ class StableBrowser {
|
|
|
156
164
|
};
|
|
157
165
|
}
|
|
158
166
|
const tempContext = {};
|
|
159
|
-
|
|
160
|
-
|
|
167
|
+
_copyContext(this, tempContext);
|
|
168
|
+
_copyContext(apps[appName], this);
|
|
161
169
|
apps[this.appName] = tempContext;
|
|
162
170
|
this.appName = appName;
|
|
163
|
-
if (
|
|
164
|
-
this.registerEventListeners(this.context);
|
|
171
|
+
if (navigate) {
|
|
165
172
|
await this.goto(this.context.environment.baseUrl);
|
|
166
173
|
await this.waitForPageLoad();
|
|
167
174
|
}
|
|
168
175
|
}
|
|
169
|
-
_copyContext(from, to) {
|
|
170
|
-
to.browser = from.browser;
|
|
171
|
-
to.page = from.page;
|
|
172
|
-
to.context = from.context;
|
|
173
|
-
}
|
|
174
|
-
getWebLogFile(logFolder) {
|
|
175
|
-
if (!fs.existsSync(logFolder)) {
|
|
176
|
-
fs.mkdirSync(logFolder, { recursive: true });
|
|
177
|
-
}
|
|
178
|
-
let nextIndex = 1;
|
|
179
|
-
while (fs.existsSync(path.join(logFolder, nextIndex.toString() + ".json"))) {
|
|
180
|
-
nextIndex++;
|
|
181
|
-
}
|
|
182
|
-
const fileName = nextIndex + ".json";
|
|
183
|
-
return path.join(logFolder, fileName);
|
|
184
|
-
}
|
|
185
176
|
registerConsoleLogListener(page, context) {
|
|
186
177
|
if (!this.context.webLogger) {
|
|
187
178
|
this.context.webLogger = [];
|
|
@@ -245,55 +236,48 @@ class StableBrowser {
|
|
|
245
236
|
// async closeUnexpectedPopups() {
|
|
246
237
|
// await closeUnexpectedPopups(this.page);
|
|
247
238
|
// }
|
|
248
|
-
async goto(url) {
|
|
239
|
+
async goto(url, world = null) {
|
|
249
240
|
if (!url.startsWith("http")) {
|
|
250
241
|
url = "https://" + url;
|
|
251
242
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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);
|
|
259
262
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
// remove the _ prefix
|
|
264
|
-
regValue = key.substring(1);
|
|
265
|
-
}
|
|
266
|
-
text = text.replaceAll(new RegExp("{" + regValue + "}", "g"), _params[key]);
|
|
263
|
+
catch (error) {
|
|
264
|
+
console.error("Error on goto", error);
|
|
265
|
+
_commandError(state, error, this);
|
|
267
266
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
_fixLocatorUsingParams(locator, _params) {
|
|
271
|
-
// check if not null
|
|
272
|
-
if (!locator) {
|
|
273
|
-
return locator;
|
|
267
|
+
finally {
|
|
268
|
+
_commandFinally(state, this);
|
|
274
269
|
}
|
|
275
|
-
// clone the locator
|
|
276
|
-
locator = JSON.parse(JSON.stringify(locator));
|
|
277
|
-
this.scanAndManipulate(locator, _params);
|
|
278
|
-
return locator;
|
|
279
|
-
}
|
|
280
|
-
_isObject(value) {
|
|
281
|
-
return value && typeof value === "object" && value.constructor === Object;
|
|
282
270
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
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);
|
|
292
279
|
}
|
|
293
280
|
}
|
|
294
|
-
}
|
|
295
|
-
_getLocator(locator, scope, _params) {
|
|
296
|
-
locator = this._fixLocatorUsingParams(locator, _params);
|
|
297
281
|
let locatorReturn;
|
|
298
282
|
if (locator.role) {
|
|
299
283
|
if (locator.role[1].nameReg) {
|
|
@@ -301,7 +285,7 @@ class StableBrowser {
|
|
|
301
285
|
delete locator.role[1].nameReg;
|
|
302
286
|
}
|
|
303
287
|
// if (locator.role[1].name) {
|
|
304
|
-
// locator.role[1].name =
|
|
288
|
+
// locator.role[1].name = _fixUsingParams(locator.role[1].name, _params);
|
|
305
289
|
// }
|
|
306
290
|
locatorReturn = scope.getByRole(locator.role[0], locator.role[1]);
|
|
307
291
|
}
|
|
@@ -344,140 +328,54 @@ class StableBrowser {
|
|
|
344
328
|
if (css && css.locator) {
|
|
345
329
|
css = css.locator;
|
|
346
330
|
}
|
|
347
|
-
let result = await this._locateElementByText(scope,
|
|
331
|
+
let result = await this._locateElementByText(scope, _fixUsingParams(text, _params), "*:not(script, style, head)", false, false, true, _params);
|
|
348
332
|
if (result.elementCount === 0) {
|
|
349
333
|
return;
|
|
350
334
|
}
|
|
351
|
-
let textElementCss = "[data-blinq-id
|
|
335
|
+
let textElementCss = "[data-blinq-id-" + result.randomToken + "]";
|
|
352
336
|
// css climb to parent element
|
|
353
337
|
const climbArray = [];
|
|
354
338
|
for (let i = 0; i < climb; i++) {
|
|
355
339
|
climbArray.push("..");
|
|
356
340
|
}
|
|
357
341
|
let climbXpath = "xpath=" + climbArray.join("/");
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
return
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
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;
|
|
368
364
|
}
|
|
369
|
-
currentNode = currentNode.parentNode;
|
|
370
|
-
}
|
|
371
|
-
return false;
|
|
372
|
-
}
|
|
373
|
-
document.isParent = isParent;
|
|
374
|
-
function getRegex(str) {
|
|
375
|
-
const match = str.match(/^\/(.*?)\/([gimuy]*)$/);
|
|
376
|
-
if (!match) {
|
|
377
|
-
return null;
|
|
378
|
-
}
|
|
379
|
-
let [_, pattern, flags] = match;
|
|
380
|
-
return new RegExp(pattern, flags);
|
|
381
|
-
}
|
|
382
|
-
document.getRegex = getRegex;
|
|
383
|
-
function collectAllShadowDomElements(element, result = []) {
|
|
384
|
-
// Check and add the element if it has a shadow root
|
|
385
|
-
if (element.shadowRoot) {
|
|
386
|
-
result.push(element);
|
|
387
|
-
// Also search within the shadow root
|
|
388
|
-
document.collectAllShadowDomElements(element.shadowRoot, result);
|
|
389
|
-
}
|
|
390
|
-
// Iterate over child nodes
|
|
391
|
-
element.childNodes.forEach((child) => {
|
|
392
|
-
// Recursively call the function for each child node
|
|
393
|
-
document.collectAllShadowDomElements(child, result);
|
|
394
|
-
});
|
|
395
|
-
return result;
|
|
396
|
-
}
|
|
397
|
-
document.collectAllShadowDomElements = collectAllShadowDomElements;
|
|
398
|
-
if (!tag) {
|
|
399
|
-
tag = "*:not(script, style, head)";
|
|
400
|
-
}
|
|
401
|
-
let regexpSearch = document.getRegex(text);
|
|
402
|
-
if (regexpSearch) {
|
|
403
|
-
regex = true;
|
|
404
|
-
}
|
|
405
|
-
let elements = Array.from(document.querySelectorAll(tag));
|
|
406
|
-
let shadowHosts = [];
|
|
407
|
-
document.collectAllShadowDomElements(document, shadowHosts);
|
|
408
|
-
for (let i = 0; i < shadowHosts.length; i++) {
|
|
409
|
-
let shadowElement = shadowHosts[i].shadowRoot;
|
|
410
|
-
if (!shadowElement) {
|
|
411
|
-
console.log("shadowElement is null, for host " + shadowHosts[i]);
|
|
412
|
-
continue;
|
|
413
365
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
}
|
|
417
|
-
let randomToken = null;
|
|
418
|
-
const foundElements = [];
|
|
419
|
-
if (regex) {
|
|
420
|
-
if (!regexpSearch) {
|
|
421
|
-
regexpSearch = new RegExp(text, "im");
|
|
422
|
-
}
|
|
423
|
-
for (let i = 0; i < elements.length; i++) {
|
|
424
|
-
const element = elements[i];
|
|
425
|
-
if ((element.innerText && regexpSearch.test(element.innerText)) ||
|
|
426
|
-
(element.value && regexpSearch.test(element.value))) {
|
|
427
|
-
foundElements.push(element);
|
|
428
|
-
}
|
|
366
|
+
if (!el.setAttribute) {
|
|
367
|
+
el = el.parentElement;
|
|
429
368
|
}
|
|
369
|
+
el.setAttribute("data-blinq-id-" + randomToken, "");
|
|
370
|
+
return true;
|
|
371
|
+
}, [tag1, randomToken]))) {
|
|
372
|
+
continue;
|
|
430
373
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
const element = elements[i];
|
|
435
|
-
if (partial) {
|
|
436
|
-
if ((element.innerText && element.innerText.toLowerCase().trim().includes(text.toLowerCase())) ||
|
|
437
|
-
(element.value && element.value.toLowerCase().includes(text.toLowerCase()))) {
|
|
438
|
-
foundElements.push(element);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
else {
|
|
442
|
-
if ((element.innerText && element.innerText.trim() === text) ||
|
|
443
|
-
(element.value && element.value === text)) {
|
|
444
|
-
foundElements.push(element);
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
let noChildElements = [];
|
|
450
|
-
for (let i = 0; i < foundElements.length; i++) {
|
|
451
|
-
let element = foundElements[i];
|
|
452
|
-
let hasChild = false;
|
|
453
|
-
for (let j = 0; j < foundElements.length; j++) {
|
|
454
|
-
if (i === j) {
|
|
455
|
-
continue;
|
|
456
|
-
}
|
|
457
|
-
if (isParent(element, foundElements[j])) {
|
|
458
|
-
hasChild = true;
|
|
459
|
-
break;
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
if (!hasChild) {
|
|
463
|
-
noChildElements.push(element);
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
let elementCount = 0;
|
|
467
|
-
if (noChildElements.length > 0) {
|
|
468
|
-
for (let i = 0; i < noChildElements.length; i++) {
|
|
469
|
-
if (randomToken === null) {
|
|
470
|
-
randomToken = Math.random().toString(36).substring(7);
|
|
471
|
-
}
|
|
472
|
-
let element = noChildElements[i];
|
|
473
|
-
element.setAttribute("data-blinq-id", "blinq-id-" + randomToken);
|
|
474
|
-
elementCount++;
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
return { elementCount: elementCount, randomToken: randomToken };
|
|
478
|
-
}, [text1, tag1, regex1, partial1]);
|
|
374
|
+
tagCount++;
|
|
375
|
+
}
|
|
376
|
+
return { elementCount: tagCount, randomToken };
|
|
479
377
|
}
|
|
480
|
-
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) {
|
|
481
379
|
if (!info) {
|
|
482
380
|
info = {};
|
|
483
381
|
}
|
|
@@ -486,10 +384,13 @@ class StableBrowser {
|
|
|
486
384
|
}
|
|
487
385
|
if (!info.log) {
|
|
488
386
|
info.log = "";
|
|
387
|
+
info.locatorLog = new LocatorLog(selectorHierarchy);
|
|
489
388
|
}
|
|
490
389
|
let locatorSearch = selectorHierarchy[index];
|
|
390
|
+
let originalLocatorSearch = "";
|
|
491
391
|
try {
|
|
492
|
-
|
|
392
|
+
originalLocatorSearch = _fixUsingParams(JSON.stringify(locatorSearch), _params);
|
|
393
|
+
locatorSearch = JSON.parse(originalLocatorSearch);
|
|
493
394
|
}
|
|
494
395
|
catch (e) {
|
|
495
396
|
console.error(e);
|
|
@@ -497,30 +398,31 @@ class StableBrowser {
|
|
|
497
398
|
//info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
|
|
498
399
|
let locator = null;
|
|
499
400
|
if (locatorSearch.climb && locatorSearch.climb >= 0) {
|
|
500
|
-
|
|
401
|
+
const replacedText = await this._replaceWithLocalData(locatorSearch.text, this.world);
|
|
402
|
+
let locatorString = await this._locateElmentByTextClimbCss(scope, replacedText, locatorSearch.climb, locatorSearch.css, _params);
|
|
501
403
|
if (!locatorString) {
|
|
502
404
|
info.failCause.textNotFound = true;
|
|
503
|
-
info.failCause.lastError =
|
|
405
|
+
info.failCause.lastError = `failed to locate ${formatElementName(element_name)} by text: ${locatorSearch.text}`;
|
|
504
406
|
return;
|
|
505
407
|
}
|
|
506
|
-
locator = this._getLocator({ css: locatorString }, scope, _params);
|
|
408
|
+
locator = await this._getLocator({ css: locatorString }, scope, _params);
|
|
507
409
|
}
|
|
508
410
|
else if (locatorSearch.text) {
|
|
509
|
-
let text =
|
|
510
|
-
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);
|
|
511
413
|
if (result.elementCount === 0) {
|
|
512
414
|
info.failCause.textNotFound = true;
|
|
513
|
-
info.failCause.lastError =
|
|
415
|
+
info.failCause.lastError = `failed to locate ${formatElementName(element_name)} by text: ${text}`;
|
|
514
416
|
return;
|
|
515
417
|
}
|
|
516
|
-
locatorSearch.css = "[data-blinq-id
|
|
418
|
+
locatorSearch.css = "[data-blinq-id-" + result.randomToken + "]";
|
|
517
419
|
if (locatorSearch.childCss) {
|
|
518
420
|
locatorSearch.css = locatorSearch.css + " " + locatorSearch.childCss;
|
|
519
421
|
}
|
|
520
|
-
locator = this._getLocator(locatorSearch, scope, _params);
|
|
422
|
+
locator = await this._getLocator(locatorSearch, scope, _params);
|
|
521
423
|
}
|
|
522
424
|
else {
|
|
523
|
-
locator = this._getLocator(locatorSearch, scope, _params);
|
|
425
|
+
locator = await this._getLocator(locatorSearch, scope, _params);
|
|
524
426
|
}
|
|
525
427
|
// let cssHref = false;
|
|
526
428
|
// if (locatorSearch.css && locatorSearch.css.includes("href=")) {
|
|
@@ -533,18 +435,27 @@ class StableBrowser {
|
|
|
533
435
|
//info.log += "total elements found " + count + "\n";
|
|
534
436
|
//let visibleCount = 0;
|
|
535
437
|
let visibleLocator = null;
|
|
536
|
-
if (locatorSearch.index && locatorSearch.index < count) {
|
|
438
|
+
if (typeof locatorSearch.index === "number" && locatorSearch.index < count) {
|
|
537
439
|
foundLocators.push(locator.nth(locatorSearch.index));
|
|
440
|
+
if (info.locatorLog) {
|
|
441
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND");
|
|
442
|
+
}
|
|
538
443
|
return;
|
|
539
444
|
}
|
|
445
|
+
if (info.locatorLog && count === 0) {
|
|
446
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "NOT_FOUND");
|
|
447
|
+
}
|
|
540
448
|
for (let j = 0; j < count; j++) {
|
|
541
449
|
let visible = await locator.nth(j).isVisible();
|
|
542
450
|
const enabled = await locator.nth(j).isEnabled();
|
|
543
451
|
if (!visibleOnly) {
|
|
544
452
|
visible = true;
|
|
545
453
|
}
|
|
546
|
-
if (visible && enabled) {
|
|
454
|
+
if (visible && (allowDisabled || enabled)) {
|
|
547
455
|
foundLocators.push(locator.nth(j));
|
|
456
|
+
if (info.locatorLog) {
|
|
457
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND");
|
|
458
|
+
}
|
|
548
459
|
}
|
|
549
460
|
else {
|
|
550
461
|
info.failCause.visible = visible;
|
|
@@ -552,8 +463,16 @@ class StableBrowser {
|
|
|
552
463
|
if (!info.printMessages) {
|
|
553
464
|
info.printMessages = {};
|
|
554
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
|
+
}
|
|
555
474
|
if (!info.printMessages[j.toString()]) {
|
|
556
|
-
info.log += "element " + locator + " visible " + visible + " enabled " + enabled + "\n";
|
|
475
|
+
//info.log += "element " + locator + " visible " + visible + " enabled " + enabled + "\n";
|
|
557
476
|
info.printMessages[j.toString()] = true;
|
|
558
477
|
}
|
|
559
478
|
}
|
|
@@ -569,7 +488,7 @@ class StableBrowser {
|
|
|
569
488
|
if (!info) {
|
|
570
489
|
info = {};
|
|
571
490
|
}
|
|
572
|
-
info.log += "scan for popup handlers" + "\n";
|
|
491
|
+
//info.log += "scan for popup handlers" + "\n";
|
|
573
492
|
const handlerGroup = [];
|
|
574
493
|
for (let i = 0; i < this.configuration.popupHandlers.length; i++) {
|
|
575
494
|
handlerGroup.push(this.configuration.popupHandlers[i].locator);
|
|
@@ -596,16 +515,28 @@ class StableBrowser {
|
|
|
596
515
|
}
|
|
597
516
|
if (result.foundElements.length > 0) {
|
|
598
517
|
let dialogCloseLocator = result.foundElements[0].locator;
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
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
|
+
}
|
|
602
533
|
return { rerun: true };
|
|
603
534
|
}
|
|
604
535
|
}
|
|
605
536
|
}
|
|
606
537
|
return { rerun: false };
|
|
607
538
|
}
|
|
608
|
-
async _locate(selectors, info, _params, timeout) {
|
|
539
|
+
async _locate(selectors, info, _params, timeout, allowDisabled = false) {
|
|
609
540
|
if (!timeout) {
|
|
610
541
|
timeout = 30000;
|
|
611
542
|
}
|
|
@@ -615,9 +546,15 @@ class StableBrowser {
|
|
|
615
546
|
let selector = selectors.locators[j];
|
|
616
547
|
info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
|
|
617
548
|
}
|
|
618
|
-
let element = await this._locate_internal(selectors, info, _params, timeout);
|
|
549
|
+
let element = await this._locate_internal(selectors, info, _params, timeout, allowDisabled);
|
|
619
550
|
if (!element.rerun) {
|
|
620
|
-
|
|
551
|
+
const randomToken = Math.random().toString(36).substring(7);
|
|
552
|
+
element.evaluate((el, randomToken) => {
|
|
553
|
+
el.setAttribute("data-blinq-id-" + randomToken, "");
|
|
554
|
+
}, randomToken);
|
|
555
|
+
const scope = element._frame ?? element.page();
|
|
556
|
+
const newSelector = scope.locator("[data-blinq-id-" + randomToken + "]");
|
|
557
|
+
return newSelector;
|
|
621
558
|
}
|
|
622
559
|
}
|
|
623
560
|
throw new Error("unable to locate element " + JSON.stringify(selectors));
|
|
@@ -628,6 +565,7 @@ class StableBrowser {
|
|
|
628
565
|
info.failCause = {};
|
|
629
566
|
info.log = "";
|
|
630
567
|
}
|
|
568
|
+
let startTime = Date.now();
|
|
631
569
|
let scope = this.page;
|
|
632
570
|
if (selectors.frame) {
|
|
633
571
|
return selectors.frame;
|
|
@@ -658,9 +596,11 @@ class StableBrowser {
|
|
|
658
596
|
}
|
|
659
597
|
return framescope;
|
|
660
598
|
};
|
|
599
|
+
let fLocator = null;
|
|
661
600
|
while (true) {
|
|
662
601
|
let frameFound = false;
|
|
663
602
|
if (selectors.nestFrmLoc) {
|
|
603
|
+
fLocator = selectors.nestFrmLoc;
|
|
664
604
|
scope = await findFrame(selectors.nestFrmLoc, scope);
|
|
665
605
|
frameFound = true;
|
|
666
606
|
break;
|
|
@@ -669,6 +609,7 @@ class StableBrowser {
|
|
|
669
609
|
for (let i = 0; i < selectors.frameLocators.length; i++) {
|
|
670
610
|
let frameLocator = selectors.frameLocators[i];
|
|
671
611
|
if (frameLocator.css) {
|
|
612
|
+
fLocator = frameLocator.css;
|
|
672
613
|
scope = scope.frameLocator(frameLocator.css);
|
|
673
614
|
frameFound = true;
|
|
674
615
|
break;
|
|
@@ -676,18 +617,25 @@ class StableBrowser {
|
|
|
676
617
|
}
|
|
677
618
|
}
|
|
678
619
|
if (!frameFound && selectors.iframe_src) {
|
|
620
|
+
fLocator = selectors.iframe_src;
|
|
679
621
|
scope = this.page.frame({ url: selectors.iframe_src });
|
|
680
622
|
}
|
|
681
623
|
if (!scope) {
|
|
682
|
-
info
|
|
683
|
-
|
|
624
|
+
if (info && info.locatorLog) {
|
|
625
|
+
info.locatorLog.setLocatorSearchStatus("frame-" + fLocator, "NOT_FOUND");
|
|
626
|
+
}
|
|
627
|
+
//info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
|
|
628
|
+
if (Date.now() - startTime > timeout) {
|
|
684
629
|
info.failCause.iframeNotFound = true;
|
|
685
|
-
info.failCause.lastError =
|
|
630
|
+
info.failCause.lastError = `unable to locate iframe "${selectors.iframe_src}"`;
|
|
686
631
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
687
632
|
}
|
|
688
633
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
689
634
|
}
|
|
690
635
|
else {
|
|
636
|
+
if (info && info.locatorLog) {
|
|
637
|
+
info.locatorLog.setLocatorSearchStatus("frame-" + fLocator, "FOUND");
|
|
638
|
+
}
|
|
691
639
|
break;
|
|
692
640
|
}
|
|
693
641
|
}
|
|
@@ -704,16 +652,18 @@ class StableBrowser {
|
|
|
704
652
|
return bodyContent;
|
|
705
653
|
});
|
|
706
654
|
}
|
|
707
|
-
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
655
|
+
async _locate_internal(selectors, info, _params, timeout = 30000, allowDisabled = false) {
|
|
708
656
|
if (!info) {
|
|
709
657
|
info = {};
|
|
710
658
|
info.failCause = {};
|
|
711
659
|
info.log = "";
|
|
660
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
712
661
|
}
|
|
713
662
|
let highPriorityTimeout = 5000;
|
|
714
663
|
let visibleOnlyTimeout = 6000;
|
|
715
|
-
let startTime =
|
|
664
|
+
let startTime = Date.now();
|
|
716
665
|
let locatorsCount = 0;
|
|
666
|
+
let lazy_scroll = false;
|
|
717
667
|
//let arrayMode = Array.isArray(selectors);
|
|
718
668
|
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
719
669
|
let selectorsLocators = null;
|
|
@@ -751,17 +701,17 @@ class StableBrowser {
|
|
|
751
701
|
}
|
|
752
702
|
// info.log += "scanning locators in priority 1" + "\n";
|
|
753
703
|
let onlyPriority3 = selectorsLocators[0].priority === 3;
|
|
754
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["1"], scope, _params, info, visibleOnly);
|
|
704
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["1"], scope, _params, info, visibleOnly, allowDisabled, selectors?.element_name);
|
|
755
705
|
if (result.foundElements.length === 0) {
|
|
756
706
|
// info.log += "scanning locators in priority 2" + "\n";
|
|
757
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["2"], scope, _params, info, visibleOnly);
|
|
707
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["2"], scope, _params, info, visibleOnly, allowDisabled, selectors?.element_name);
|
|
758
708
|
}
|
|
759
709
|
if (result.foundElements.length === 0 && onlyPriority3) {
|
|
760
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly);
|
|
710
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly, allowDisabled, selectors?.element_name);
|
|
761
711
|
}
|
|
762
712
|
else {
|
|
763
713
|
if (result.foundElements.length === 0 && !highPriorityOnly) {
|
|
764
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly);
|
|
714
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly, allowDisabled, selectors?.element_name);
|
|
765
715
|
}
|
|
766
716
|
}
|
|
767
717
|
let foundElements = result.foundElements;
|
|
@@ -802,26 +752,38 @@ class StableBrowser {
|
|
|
802
752
|
return maxCountElement.locator;
|
|
803
753
|
}
|
|
804
754
|
}
|
|
805
|
-
if (
|
|
755
|
+
if (Date.now() - startTime > timeout) {
|
|
806
756
|
break;
|
|
807
757
|
}
|
|
808
|
-
if (
|
|
809
|
-
info.log += "high priority timeout, will try all elements" + "\n";
|
|
758
|
+
if (Date.now() - startTime > highPriorityTimeout) {
|
|
759
|
+
//info.log += "high priority timeout, will try all elements" + "\n";
|
|
810
760
|
highPriorityOnly = false;
|
|
761
|
+
if (this.configuration && this.configuration.load_all_lazy === true && !lazy_scroll) {
|
|
762
|
+
lazy_scroll = true;
|
|
763
|
+
await scrollPageToLoadLazyElements(this.page);
|
|
764
|
+
}
|
|
811
765
|
}
|
|
812
|
-
if (
|
|
813
|
-
info.log += "visible only timeout, will try all elements" + "\n";
|
|
766
|
+
if (Date.now() - startTime > visibleOnlyTimeout) {
|
|
767
|
+
//info.log += "visible only timeout, will try all elements" + "\n";
|
|
814
768
|
visibleOnly = false;
|
|
815
769
|
}
|
|
816
770
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
817
771
|
}
|
|
818
772
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
819
|
-
info.
|
|
773
|
+
// if (info.locatorLog) {
|
|
774
|
+
// const lines = info.locatorLog.toString().split("\n");
|
|
775
|
+
// for (let line of lines) {
|
|
776
|
+
// this.logger.debug(line);
|
|
777
|
+
// }
|
|
778
|
+
// }
|
|
779
|
+
//info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
|
|
820
780
|
info.failCause.locatorNotFound = true;
|
|
821
|
-
info
|
|
781
|
+
if (!info?.failCause?.lastError) {
|
|
782
|
+
info.failCause.lastError = `failed to locate ${formatElementName(selectors.element_name)}, ${locatorsCount > 0 ? `${locatorsCount} matching elements found` : "no matching elements found"}`;
|
|
783
|
+
}
|
|
822
784
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
823
785
|
}
|
|
824
|
-
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
786
|
+
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly, allowDisabled = false, element_name) {
|
|
825
787
|
let foundElements = [];
|
|
826
788
|
const result = {
|
|
827
789
|
foundElements: foundElements,
|
|
@@ -829,14 +791,15 @@ class StableBrowser {
|
|
|
829
791
|
for (let i = 0; i < locatorsGroup.length; i++) {
|
|
830
792
|
let foundLocators = [];
|
|
831
793
|
try {
|
|
832
|
-
await this._collectLocatorInformation(locatorsGroup, i, scope, foundLocators, _params, info, visibleOnly);
|
|
794
|
+
await this._collectLocatorInformation(locatorsGroup, i, scope, foundLocators, _params, info, visibleOnly, allowDisabled, element_name);
|
|
833
795
|
}
|
|
834
796
|
catch (e) {
|
|
835
|
-
this
|
|
836
|
-
this.logger.debug(
|
|
797
|
+
// this call can fail it the browser is navigating
|
|
798
|
+
// this.logger.debug("unable to use locator " + JSON.stringify(locatorsGroup[i]));
|
|
799
|
+
// this.logger.debug(e);
|
|
837
800
|
foundLocators = [];
|
|
838
801
|
try {
|
|
839
|
-
await this._collectLocatorInformation(locatorsGroup, i, this.page, foundLocators, _params, info, visibleOnly);
|
|
802
|
+
await this._collectLocatorInformation(locatorsGroup, i, this.page, foundLocators, _params, info, visibleOnly, allowDisabled, element_name);
|
|
840
803
|
}
|
|
841
804
|
catch (e) {
|
|
842
805
|
this.logger.info("unable to use locator (second try) " + JSON.stringify(locatorsGroup[i]));
|
|
@@ -852,11 +815,27 @@ class StableBrowser {
|
|
|
852
815
|
}
|
|
853
816
|
if (foundLocators.length > 1) {
|
|
854
817
|
info.failCause.foundMultiple = true;
|
|
818
|
+
if (info.locatorLog) {
|
|
819
|
+
info.locatorLog.setLocatorSearchStatus(JSON.stringify(locatorsGroup[i]), "FOUND_NOT_UNIQUE");
|
|
820
|
+
}
|
|
855
821
|
}
|
|
856
822
|
}
|
|
857
823
|
return result;
|
|
858
824
|
}
|
|
859
825
|
async simpleClick(elementDescription, _params, options = {}, world = null) {
|
|
826
|
+
const state = {
|
|
827
|
+
locate: false,
|
|
828
|
+
scroll: false,
|
|
829
|
+
highlight: false,
|
|
830
|
+
_params,
|
|
831
|
+
options,
|
|
832
|
+
world,
|
|
833
|
+
type: Types.CLICK,
|
|
834
|
+
text: "Click element",
|
|
835
|
+
operation: "simpleClick",
|
|
836
|
+
log: "***** click on " + elementDescription + " *****\n",
|
|
837
|
+
};
|
|
838
|
+
_preCommand(state, this);
|
|
860
839
|
const startTime = Date.now();
|
|
861
840
|
let timeout = 30000;
|
|
862
841
|
if (options && options.timeout) {
|
|
@@ -881,13 +860,31 @@ class StableBrowser {
|
|
|
881
860
|
catch (e) {
|
|
882
861
|
if (performance.now() - startTime > timeout) {
|
|
883
862
|
// throw e;
|
|
884
|
-
|
|
863
|
+
try {
|
|
864
|
+
await _commandError(state, "timeout looking for " + elementDescription, this);
|
|
865
|
+
}
|
|
866
|
+
finally {
|
|
867
|
+
_commandFinally(state, this);
|
|
868
|
+
}
|
|
885
869
|
}
|
|
886
870
|
}
|
|
887
871
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
888
872
|
}
|
|
889
873
|
}
|
|
890
874
|
async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
|
|
875
|
+
const state = {
|
|
876
|
+
locate: false,
|
|
877
|
+
scroll: false,
|
|
878
|
+
highlight: false,
|
|
879
|
+
_params,
|
|
880
|
+
options,
|
|
881
|
+
world,
|
|
882
|
+
type: Types.FILL,
|
|
883
|
+
text: "Fill element",
|
|
884
|
+
operation: "simpleClickType",
|
|
885
|
+
log: "***** click type on " + elementDescription + " *****\n",
|
|
886
|
+
};
|
|
887
|
+
_preCommand(state, this);
|
|
891
888
|
const startTime = Date.now();
|
|
892
889
|
let timeout = 30000;
|
|
893
890
|
if (options && options.timeout) {
|
|
@@ -912,7 +909,12 @@ class StableBrowser {
|
|
|
912
909
|
catch (e) {
|
|
913
910
|
if (performance.now() - startTime > timeout) {
|
|
914
911
|
// throw e;
|
|
915
|
-
|
|
912
|
+
try {
|
|
913
|
+
await _commandError(state, "timeout looking for " + elementDescription, this);
|
|
914
|
+
}
|
|
915
|
+
finally {
|
|
916
|
+
_commandFinally(state, this);
|
|
917
|
+
}
|
|
916
918
|
}
|
|
917
919
|
}
|
|
918
920
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
@@ -931,9 +933,9 @@ class StableBrowser {
|
|
|
931
933
|
};
|
|
932
934
|
try {
|
|
933
935
|
await _preCommand(state, this);
|
|
934
|
-
if (state.options && state.options.context) {
|
|
935
|
-
|
|
936
|
-
}
|
|
936
|
+
// if (state.options && state.options.context) {
|
|
937
|
+
// state.selectors.locators[0].text = state.options.context;
|
|
938
|
+
// }
|
|
937
939
|
try {
|
|
938
940
|
await state.element.click();
|
|
939
941
|
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -971,9 +973,15 @@ class StableBrowser {
|
|
|
971
973
|
// let element = await this._locate(selectors, info, _params);
|
|
972
974
|
// ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
973
975
|
try {
|
|
974
|
-
//
|
|
976
|
+
// if (world && world.screenshot && !world.screenshotPath) {
|
|
977
|
+
// console.log(`Highlighting while running from recorder`);
|
|
978
|
+
await this._highlightElements(element);
|
|
975
979
|
await state.element.setChecked(checked);
|
|
976
980
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
981
|
+
// await this._unHighlightElements(element);
|
|
982
|
+
// }
|
|
983
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
984
|
+
// await this._unHighlightElements(element);
|
|
977
985
|
}
|
|
978
986
|
catch (e) {
|
|
979
987
|
if (e.message && e.message.includes("did not change its state")) {
|
|
@@ -1012,6 +1020,7 @@ class StableBrowser {
|
|
|
1012
1020
|
await _preCommand(state, this);
|
|
1013
1021
|
try {
|
|
1014
1022
|
await state.element.hover();
|
|
1023
|
+
// await _screenshot(state, this);
|
|
1015
1024
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1016
1025
|
}
|
|
1017
1026
|
catch (e) {
|
|
@@ -1019,6 +1028,7 @@ class StableBrowser {
|
|
|
1019
1028
|
state.info.log += "hover failed, will try again" + "\n";
|
|
1020
1029
|
state.element = await this._locate(selectors, state.info, _params);
|
|
1021
1030
|
await state.element.hover({ timeout: 10000 });
|
|
1031
|
+
// await _screenshot(state, this);
|
|
1022
1032
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1023
1033
|
}
|
|
1024
1034
|
await _screenshot(state, this);
|
|
@@ -1293,7 +1303,12 @@ class StableBrowser {
|
|
|
1293
1303
|
await this.waitForPageLoad();
|
|
1294
1304
|
}
|
|
1295
1305
|
else if (enter === false) {
|
|
1296
|
-
|
|
1306
|
+
try {
|
|
1307
|
+
await state.element.dispatchEvent("change", null, { timeout: 5000 });
|
|
1308
|
+
}
|
|
1309
|
+
catch (e) {
|
|
1310
|
+
// ignore
|
|
1311
|
+
}
|
|
1297
1312
|
//await this.page.keyboard.press("Tab");
|
|
1298
1313
|
}
|
|
1299
1314
|
else {
|
|
@@ -1350,6 +1365,7 @@ class StableBrowser {
|
|
|
1350
1365
|
let screenshotPath = null;
|
|
1351
1366
|
if (!info.log) {
|
|
1352
1367
|
info.log = "";
|
|
1368
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
1353
1369
|
}
|
|
1354
1370
|
info.operation = "getText";
|
|
1355
1371
|
info.selectors = selectors;
|
|
@@ -1372,6 +1388,18 @@ class StableBrowser {
|
|
|
1372
1388
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1373
1389
|
try {
|
|
1374
1390
|
await this._highlightElements(element);
|
|
1391
|
+
// if (world && world.screenshot && !world.screenshotPath) {
|
|
1392
|
+
// // console.log(`Highlighting for get text while running from recorder`);
|
|
1393
|
+
// this._highlightElements(element)
|
|
1394
|
+
// .then(async () => {
|
|
1395
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1396
|
+
// this._unhighlightElements(element).then(
|
|
1397
|
+
// () => {}
|
|
1398
|
+
// // console.log(`Unhighlighting vrtr in recorder is successful`)
|
|
1399
|
+
// );
|
|
1400
|
+
// })
|
|
1401
|
+
// .catch(e);
|
|
1402
|
+
// }
|
|
1375
1403
|
const elementText = await element.innerText();
|
|
1376
1404
|
return {
|
|
1377
1405
|
text: elementText,
|
|
@@ -1383,7 +1411,7 @@ class StableBrowser {
|
|
|
1383
1411
|
}
|
|
1384
1412
|
catch (e) {
|
|
1385
1413
|
//await this.closeUnexpectedPopups();
|
|
1386
|
-
this.logger.info("no innerText will use textContent");
|
|
1414
|
+
this.logger.info("no innerText, will use textContent");
|
|
1387
1415
|
const elementText = await element.textContent();
|
|
1388
1416
|
return { text: elementText, screenshotId, screenshotPath, value: value };
|
|
1389
1417
|
}
|
|
@@ -1509,22 +1537,6 @@ class StableBrowser {
|
|
|
1509
1537
|
_commandFinally(state, this);
|
|
1510
1538
|
}
|
|
1511
1539
|
}
|
|
1512
|
-
_getDataFile(world = null) {
|
|
1513
|
-
let dataFile = null;
|
|
1514
|
-
if (world && world.reportFolder) {
|
|
1515
|
-
dataFile = path.join(world.reportFolder, "data.json");
|
|
1516
|
-
}
|
|
1517
|
-
else if (this.reportFolder) {
|
|
1518
|
-
dataFile = path.join(this.reportFolder, "data.json");
|
|
1519
|
-
}
|
|
1520
|
-
else if (this.context && this.context.reportFolder) {
|
|
1521
|
-
dataFile = path.join(this.context.reportFolder, "data.json");
|
|
1522
|
-
}
|
|
1523
|
-
else {
|
|
1524
|
-
dataFile = "data.json";
|
|
1525
|
-
}
|
|
1526
|
-
return dataFile;
|
|
1527
|
-
}
|
|
1528
1540
|
async waitForUserInput(message, world = null) {
|
|
1529
1541
|
if (!message) {
|
|
1530
1542
|
message = "# Wait for user input. Press any key to continue";
|
|
@@ -1553,7 +1565,7 @@ class StableBrowser {
|
|
|
1553
1565
|
return;
|
|
1554
1566
|
}
|
|
1555
1567
|
// if data file exists, load it
|
|
1556
|
-
const dataFile =
|
|
1568
|
+
const dataFile = _getDataFile(world, this.context, this);
|
|
1557
1569
|
let data = this.getTestData(world);
|
|
1558
1570
|
// merge the testData with the existing data
|
|
1559
1571
|
Object.assign(data, testData);
|
|
@@ -1656,7 +1668,7 @@ class StableBrowser {
|
|
|
1656
1668
|
}
|
|
1657
1669
|
}
|
|
1658
1670
|
getTestData(world = null) {
|
|
1659
|
-
const dataFile =
|
|
1671
|
+
const dataFile = _getDataFile(world, this.context, this);
|
|
1660
1672
|
let data = {};
|
|
1661
1673
|
if (fs.existsSync(dataFile)) {
|
|
1662
1674
|
data = JSON.parse(fs.readFileSync(dataFile, "utf8"));
|
|
@@ -1700,15 +1712,15 @@ class StableBrowser {
|
|
|
1700
1712
|
// this.logger.info("unable to save screenshot " + screenshotPath);
|
|
1701
1713
|
// }
|
|
1702
1714
|
// });
|
|
1715
|
+
result.screenshotId = uuidStr;
|
|
1716
|
+
result.screenshotPath = screenshotPath;
|
|
1717
|
+
if (info && info.box) {
|
|
1718
|
+
await drawRectangle(screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
|
|
1719
|
+
}
|
|
1703
1720
|
}
|
|
1704
1721
|
catch (e) {
|
|
1705
1722
|
this.logger.info("unable to take screenshot, ignored");
|
|
1706
1723
|
}
|
|
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
1724
|
}
|
|
1713
1725
|
else if (options && options.screenshot) {
|
|
1714
1726
|
result.screenshotPath = options.screenshotPath;
|
|
@@ -1743,6 +1755,15 @@ class StableBrowser {
|
|
|
1743
1755
|
document.documentElement.clientWidth,
|
|
1744
1756
|
])));
|
|
1745
1757
|
let screenshotBuffer = null;
|
|
1758
|
+
// if (focusedElement) {
|
|
1759
|
+
// // console.log(`Focused element ${JSON.stringify(focusedElement._selector)}`)
|
|
1760
|
+
// await this._unhighlightElements(focusedElement);
|
|
1761
|
+
// await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1762
|
+
// console.log(`Unhighlighted previous element`);
|
|
1763
|
+
// }
|
|
1764
|
+
// if (focusedElement) {
|
|
1765
|
+
// await this._highlightElements(focusedElement);
|
|
1766
|
+
// }
|
|
1746
1767
|
if (this.context.browserName === "chromium") {
|
|
1747
1768
|
const client = await playContext.newCDPSession(this.page);
|
|
1748
1769
|
const { data } = await client.send("Page.captureScreenshot", {
|
|
@@ -1764,6 +1785,10 @@ class StableBrowser {
|
|
|
1764
1785
|
else {
|
|
1765
1786
|
screenshotBuffer = await this.page.screenshot();
|
|
1766
1787
|
}
|
|
1788
|
+
// if (focusedElement) {
|
|
1789
|
+
// // console.log(`Focused element ${JSON.stringify(focusedElement._selector)}`)
|
|
1790
|
+
// await this._unhighlightElements(focusedElement);
|
|
1791
|
+
// }
|
|
1767
1792
|
let image = await Jimp.read(screenshotBuffer);
|
|
1768
1793
|
// Get the image dimensions
|
|
1769
1794
|
const { width, height } = image.bitmap;
|
|
@@ -1813,6 +1838,7 @@ class StableBrowser {
|
|
|
1813
1838
|
text: `Extract attribute from element`,
|
|
1814
1839
|
operation: "extractAttribute",
|
|
1815
1840
|
log: "***** extract attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1841
|
+
allowDisabled: true,
|
|
1816
1842
|
};
|
|
1817
1843
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1818
1844
|
try {
|
|
@@ -1834,6 +1860,71 @@ class StableBrowser {
|
|
|
1834
1860
|
state.info.value = state.value;
|
|
1835
1861
|
this.setTestData({ [variable]: state.value }, world);
|
|
1836
1862
|
this.logger.info("set test data: " + variable + "=" + state.value);
|
|
1863
|
+
// await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1864
|
+
return state.info;
|
|
1865
|
+
}
|
|
1866
|
+
catch (e) {
|
|
1867
|
+
await _commandError(state, e, this);
|
|
1868
|
+
}
|
|
1869
|
+
finally {
|
|
1870
|
+
_commandFinally(state, this);
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
async verifyAttribute(selectors, attribute, value, _params = null, options = {}, world = null) {
|
|
1874
|
+
const state = {
|
|
1875
|
+
selectors,
|
|
1876
|
+
_params,
|
|
1877
|
+
attribute,
|
|
1878
|
+
value,
|
|
1879
|
+
options,
|
|
1880
|
+
world,
|
|
1881
|
+
type: Types.VERIFY_ATTRIBUTE,
|
|
1882
|
+
highlight: true,
|
|
1883
|
+
screenshot: true,
|
|
1884
|
+
text: `Verify element attribute`,
|
|
1885
|
+
operation: "verifyAttribute",
|
|
1886
|
+
log: "***** verify attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1887
|
+
allowDisabled: true,
|
|
1888
|
+
};
|
|
1889
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1890
|
+
let val;
|
|
1891
|
+
try {
|
|
1892
|
+
await _preCommand(state, this);
|
|
1893
|
+
switch (attribute) {
|
|
1894
|
+
case "innerText":
|
|
1895
|
+
val = String(await state.element.innerText());
|
|
1896
|
+
break;
|
|
1897
|
+
case "value":
|
|
1898
|
+
val = String(await state.element.inputValue());
|
|
1899
|
+
break;
|
|
1900
|
+
case "checked":
|
|
1901
|
+
val = String(await state.element.isChecked());
|
|
1902
|
+
break;
|
|
1903
|
+
case "disabled":
|
|
1904
|
+
val = String(await state.element.isDisabled());
|
|
1905
|
+
break;
|
|
1906
|
+
case "readOnly":
|
|
1907
|
+
const isEditable = await state.element.isEditable();
|
|
1908
|
+
val = String(!isEditable);
|
|
1909
|
+
break;
|
|
1910
|
+
default:
|
|
1911
|
+
val = String(await state.element.getAttribute(attribute));
|
|
1912
|
+
break;
|
|
1913
|
+
}
|
|
1914
|
+
let regex;
|
|
1915
|
+
if (value.startsWith("/") && value.endsWith("/")) {
|
|
1916
|
+
const patternBody = value.slice(1, -1);
|
|
1917
|
+
regex = new RegExp(patternBody, "g");
|
|
1918
|
+
}
|
|
1919
|
+
else {
|
|
1920
|
+
const escapedPattern = value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1921
|
+
regex = new RegExp(escapedPattern, "g");
|
|
1922
|
+
}
|
|
1923
|
+
if (!val.match(regex)) {
|
|
1924
|
+
throw new Error(`The ${attribute} attribute has a value of "${val}", but the expected value is "${value}"`);
|
|
1925
|
+
}
|
|
1926
|
+
state.info.expectedValue = value;
|
|
1927
|
+
state.info.value = val;
|
|
1837
1928
|
return state.info;
|
|
1838
1929
|
}
|
|
1839
1930
|
catch (e) {
|
|
@@ -1861,7 +1952,7 @@ class StableBrowser {
|
|
|
1861
1952
|
if (options && options.timeout) {
|
|
1862
1953
|
timeout = options.timeout;
|
|
1863
1954
|
}
|
|
1864
|
-
const serviceUrl =
|
|
1955
|
+
const serviceUrl = _getServerUrl() + "/api/mail/createLinkOrCodeFromEmail";
|
|
1865
1956
|
const request = {
|
|
1866
1957
|
method: "POST",
|
|
1867
1958
|
url: serviceUrl,
|
|
@@ -1932,27 +2023,32 @@ class StableBrowser {
|
|
|
1932
2023
|
async _highlightElements(scope, css) {
|
|
1933
2024
|
try {
|
|
1934
2025
|
if (!scope) {
|
|
2026
|
+
// console.log(`Scope is not defined`);
|
|
1935
2027
|
return;
|
|
1936
2028
|
}
|
|
1937
2029
|
if (!css) {
|
|
1938
2030
|
scope
|
|
1939
2031
|
.evaluate((node) => {
|
|
1940
2032
|
if (node && node.style) {
|
|
1941
|
-
let
|
|
1942
|
-
|
|
2033
|
+
let originalOutline = node.style.outline;
|
|
2034
|
+
// console.log(`Original outline was: ${originalOutline}`);
|
|
2035
|
+
// node.__previousOutline = originalOutline;
|
|
2036
|
+
node.style.outline = "2px solid red";
|
|
2037
|
+
// console.log(`New outline is: ${node.style.outline}`);
|
|
1943
2038
|
if (window) {
|
|
1944
2039
|
window.addEventListener("beforeunload", function (e) {
|
|
1945
|
-
node.style.
|
|
2040
|
+
node.style.outline = originalOutline;
|
|
1946
2041
|
});
|
|
1947
2042
|
}
|
|
1948
2043
|
setTimeout(function () {
|
|
1949
|
-
node.style.
|
|
2044
|
+
node.style.outline = originalOutline;
|
|
1950
2045
|
}, 2000);
|
|
1951
2046
|
}
|
|
1952
2047
|
})
|
|
1953
2048
|
.then(() => { })
|
|
1954
2049
|
.catch((e) => {
|
|
1955
2050
|
// ignore
|
|
2051
|
+
// console.error(`Could not highlight node : ${e}`);
|
|
1956
2052
|
});
|
|
1957
2053
|
}
|
|
1958
2054
|
else {
|
|
@@ -1968,17 +2064,18 @@ class StableBrowser {
|
|
|
1968
2064
|
if (!element.style) {
|
|
1969
2065
|
return;
|
|
1970
2066
|
}
|
|
1971
|
-
|
|
2067
|
+
let originalOutline = element.style.outline;
|
|
2068
|
+
element.__previousOutline = originalOutline;
|
|
1972
2069
|
// Set the new border to be red and 2px solid
|
|
1973
|
-
element.style.
|
|
2070
|
+
element.style.outline = "2px solid red";
|
|
1974
2071
|
if (window) {
|
|
1975
2072
|
window.addEventListener("beforeunload", function (e) {
|
|
1976
|
-
element.style.
|
|
2073
|
+
element.style.outline = originalOutline;
|
|
1977
2074
|
});
|
|
1978
2075
|
}
|
|
1979
2076
|
// Set a timeout to revert to the original border after 2 seconds
|
|
1980
2077
|
setTimeout(function () {
|
|
1981
|
-
element.style.
|
|
2078
|
+
element.style.outline = originalOutline;
|
|
1982
2079
|
}, 2000);
|
|
1983
2080
|
}
|
|
1984
2081
|
return;
|
|
@@ -1986,6 +2083,7 @@ class StableBrowser {
|
|
|
1986
2083
|
.then(() => { })
|
|
1987
2084
|
.catch((e) => {
|
|
1988
2085
|
// ignore
|
|
2086
|
+
// console.error(`Could not highlight css: ${e}`);
|
|
1989
2087
|
});
|
|
1990
2088
|
}
|
|
1991
2089
|
}
|
|
@@ -1993,6 +2091,54 @@ class StableBrowser {
|
|
|
1993
2091
|
console.debug(error);
|
|
1994
2092
|
}
|
|
1995
2093
|
}
|
|
2094
|
+
// async _unhighlightElements(scope, css) {
|
|
2095
|
+
// try {
|
|
2096
|
+
// if (!scope) {
|
|
2097
|
+
// return;
|
|
2098
|
+
// }
|
|
2099
|
+
// if (!css) {
|
|
2100
|
+
// scope
|
|
2101
|
+
// .evaluate((node) => {
|
|
2102
|
+
// if (node && node.style) {
|
|
2103
|
+
// if (!node.__previousOutline) {
|
|
2104
|
+
// node.style.outline = "";
|
|
2105
|
+
// } else {
|
|
2106
|
+
// node.style.outline = node.__previousOutline;
|
|
2107
|
+
// }
|
|
2108
|
+
// }
|
|
2109
|
+
// })
|
|
2110
|
+
// .then(() => {})
|
|
2111
|
+
// .catch((e) => {
|
|
2112
|
+
// // console.log(`Error while unhighlighting node ${JSON.stringify(scope)}: ${e}`);
|
|
2113
|
+
// });
|
|
2114
|
+
// } else {
|
|
2115
|
+
// scope
|
|
2116
|
+
// .evaluate(([css]) => {
|
|
2117
|
+
// if (!css) {
|
|
2118
|
+
// return;
|
|
2119
|
+
// }
|
|
2120
|
+
// let elements = Array.from(document.querySelectorAll(css));
|
|
2121
|
+
// for (i = 0; i < elements.length; i++) {
|
|
2122
|
+
// let element = elements[i];
|
|
2123
|
+
// if (!element.style) {
|
|
2124
|
+
// return;
|
|
2125
|
+
// }
|
|
2126
|
+
// if (!element.__previousOutline) {
|
|
2127
|
+
// element.style.outline = "";
|
|
2128
|
+
// } else {
|
|
2129
|
+
// element.style.outline = element.__previousOutline;
|
|
2130
|
+
// }
|
|
2131
|
+
// }
|
|
2132
|
+
// })
|
|
2133
|
+
// .then(() => {})
|
|
2134
|
+
// .catch((e) => {
|
|
2135
|
+
// // console.error(`Error while unhighlighting element in css: ${e}`);
|
|
2136
|
+
// });
|
|
2137
|
+
// }
|
|
2138
|
+
// } catch (error) {
|
|
2139
|
+
// // console.debug(error);
|
|
2140
|
+
// }
|
|
2141
|
+
// }
|
|
1996
2142
|
async verifyPagePath(pathPart, options = {}, world = null) {
|
|
1997
2143
|
const startTime = Date.now();
|
|
1998
2144
|
let error = null;
|
|
@@ -2054,6 +2200,35 @@ class StableBrowser {
|
|
|
2054
2200
|
});
|
|
2055
2201
|
}
|
|
2056
2202
|
}
|
|
2203
|
+
async findTextInAllFrames(dateAlternatives, numberAlternatives, text, state) {
|
|
2204
|
+
const frames = this.page.frames();
|
|
2205
|
+
let results = [];
|
|
2206
|
+
let ignoreCase = false;
|
|
2207
|
+
for (let i = 0; i < frames.length; i++) {
|
|
2208
|
+
if (dateAlternatives.date) {
|
|
2209
|
+
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2210
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2211
|
+
result.frame = frames[i];
|
|
2212
|
+
results.push(result);
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
else if (numberAlternatives.number) {
|
|
2216
|
+
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2217
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2218
|
+
result.frame = frames[i];
|
|
2219
|
+
results.push(result);
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
else {
|
|
2223
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2224
|
+
result.frame = frames[i];
|
|
2225
|
+
results.push(result);
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
state.info.results = results;
|
|
2229
|
+
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2230
|
+
return resultWithElementsFound;
|
|
2231
|
+
}
|
|
2057
2232
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2058
2233
|
text = unEscapeString(text);
|
|
2059
2234
|
const state = {
|
|
@@ -2063,7 +2238,7 @@ class StableBrowser {
|
|
|
2063
2238
|
locate: false,
|
|
2064
2239
|
scroll: false,
|
|
2065
2240
|
highlight: false,
|
|
2066
|
-
type: Types.
|
|
2241
|
+
type: Types.VERIFY_PAGE_CONTAINS_TEXT,
|
|
2067
2242
|
text: `Verify text exists in page`,
|
|
2068
2243
|
operation: "verifyTextExistInPage",
|
|
2069
2244
|
log: "***** verify text " + text + " exists in page *****\n",
|
|
@@ -2081,31 +2256,7 @@ class StableBrowser {
|
|
|
2081
2256
|
await _preCommand(state, this);
|
|
2082
2257
|
state.info.text = text;
|
|
2083
2258
|
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);
|
|
2259
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, text, state);
|
|
2109
2260
|
if (resultWithElementsFound.length === 0) {
|
|
2110
2261
|
if (Date.now() - state.startTime > timeout) {
|
|
2111
2262
|
throw new Error(`Text ${text} not found in page`);
|
|
@@ -2115,12 +2266,29 @@ class StableBrowser {
|
|
|
2115
2266
|
}
|
|
2116
2267
|
if (resultWithElementsFound[0].randomToken) {
|
|
2117
2268
|
const frame = resultWithElementsFound[0].frame;
|
|
2118
|
-
const dataAttribute = `[data-blinq-id
|
|
2269
|
+
const dataAttribute = `[data-blinq-id-${resultWithElementsFound[0].randomToken}]`;
|
|
2119
2270
|
await this._highlightElements(frame, dataAttribute);
|
|
2120
|
-
|
|
2271
|
+
// if (world && world.screenshot && !world.screenshotPath) {
|
|
2272
|
+
// console.log(`Highlighting for verify text is found while running from recorder`);
|
|
2273
|
+
// this._highlightElements(frame, dataAttribute).then(async () => {
|
|
2274
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2275
|
+
// this._unhighlightElements(frame, dataAttribute)
|
|
2276
|
+
// .then(async () => {
|
|
2277
|
+
// console.log(`Unhighlighted frame dataAttribute successfully`);
|
|
2278
|
+
// })
|
|
2279
|
+
// .catch(
|
|
2280
|
+
// (e) => {}
|
|
2281
|
+
// console.error(e)
|
|
2282
|
+
// );
|
|
2283
|
+
// });
|
|
2284
|
+
// }
|
|
2285
|
+
const element = await frame.locator(dataAttribute).first();
|
|
2286
|
+
// await new Promise((resolve) => setTimeout(resolve, 100));
|
|
2287
|
+
// await this._unhighlightElements(frame, dataAttribute);
|
|
2121
2288
|
if (element) {
|
|
2122
2289
|
await this.scrollIfNeeded(element, state.info);
|
|
2123
2290
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2291
|
+
// await _screenshot(state, this, element);
|
|
2124
2292
|
}
|
|
2125
2293
|
}
|
|
2126
2294
|
await _screenshot(state, this);
|
|
@@ -2162,31 +2330,7 @@ class StableBrowser {
|
|
|
2162
2330
|
await _preCommand(state, this);
|
|
2163
2331
|
state.info.text = text;
|
|
2164
2332
|
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);
|
|
2333
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, text, state);
|
|
2190
2334
|
if (resultWithElementsFound.length === 0) {
|
|
2191
2335
|
await _screenshot(state, this);
|
|
2192
2336
|
return state.info;
|
|
@@ -2204,15 +2348,102 @@ class StableBrowser {
|
|
|
2204
2348
|
_commandFinally(state, this);
|
|
2205
2349
|
}
|
|
2206
2350
|
}
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2351
|
+
async verifyTextRelatedToText(textAnchor, climb, textToVerify, options = {}, world = null) {
|
|
2352
|
+
textAnchor = unEscapeString(textAnchor);
|
|
2353
|
+
textToVerify = unEscapeString(textToVerify);
|
|
2354
|
+
const state = {
|
|
2355
|
+
text_search: textToVerify,
|
|
2356
|
+
options,
|
|
2357
|
+
world,
|
|
2358
|
+
locate: false,
|
|
2359
|
+
scroll: false,
|
|
2360
|
+
highlight: false,
|
|
2361
|
+
type: Types.VERIFY_TEXT_WITH_RELATION,
|
|
2362
|
+
text: `Verify text with relation to another text`,
|
|
2363
|
+
operation: "verify_text_with_relation",
|
|
2364
|
+
log: "***** search for " + textAnchor + " climb " + climb + " and verify " + textToVerify + " found *****\n",
|
|
2365
|
+
};
|
|
2366
|
+
const timeout = this._getLoadTimeout(options);
|
|
2367
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2368
|
+
let newValue = await this._replaceWithLocalData(textAnchor, world);
|
|
2369
|
+
if (newValue !== textAnchor) {
|
|
2370
|
+
this.logger.info(textAnchor + "=" + newValue);
|
|
2371
|
+
textAnchor = newValue;
|
|
2372
|
+
}
|
|
2373
|
+
newValue = await this._replaceWithLocalData(textToVerify, world);
|
|
2374
|
+
if (newValue !== textToVerify) {
|
|
2375
|
+
this.logger.info(textToVerify + "=" + newValue);
|
|
2376
|
+
textToVerify = newValue;
|
|
2377
|
+
}
|
|
2378
|
+
let dateAlternatives = findDateAlternatives(textToVerify);
|
|
2379
|
+
let numberAlternatives = findNumberAlternatives(textToVerify);
|
|
2380
|
+
let foundAncore = false;
|
|
2381
|
+
try {
|
|
2382
|
+
await _preCommand(state, this);
|
|
2383
|
+
state.info.text = textToVerify;
|
|
2384
|
+
while (true) {
|
|
2385
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, textAnchor, state);
|
|
2386
|
+
if (resultWithElementsFound.length === 0) {
|
|
2387
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2388
|
+
throw new Error(`Text ${foundAncore ? textToVerify : textAnchor} not found in page`);
|
|
2389
|
+
}
|
|
2390
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2391
|
+
continue;
|
|
2392
|
+
}
|
|
2393
|
+
for (let i = 0; i < resultWithElementsFound.length; i++) {
|
|
2394
|
+
foundAncore = true;
|
|
2395
|
+
const result = resultWithElementsFound[i];
|
|
2396
|
+
const token = result.randomToken;
|
|
2397
|
+
const frame = result.frame;
|
|
2398
|
+
let css = `[data-blinq-id-${token}]`;
|
|
2399
|
+
const climbArray1 = [];
|
|
2400
|
+
for (let i = 0; i < climb; i++) {
|
|
2401
|
+
climbArray1.push("..");
|
|
2402
|
+
}
|
|
2403
|
+
let climbXpath = "xpath=" + climbArray1.join("/");
|
|
2404
|
+
css = css + " >> " + climbXpath;
|
|
2405
|
+
const count = await frame.locator(css).count();
|
|
2406
|
+
for (let j = 0; j < count; j++) {
|
|
2407
|
+
const continer = await frame.locator(css).nth(j);
|
|
2408
|
+
const result = await this._locateElementByText(continer, textToVerify, "*", false, true, true, {});
|
|
2409
|
+
if (result.elementCount > 0) {
|
|
2410
|
+
const dataAttribute = "[data-blinq-id-" + result.randomToken + "]";
|
|
2411
|
+
await this._highlightElements(frame, dataAttribute);
|
|
2412
|
+
//const cssAnchor = `[data-blinq-id="blinq-id-${token}-anchor"]`;
|
|
2413
|
+
// if (world && world.screenshot && !world.screenshotPath) {
|
|
2414
|
+
// console.log(`Highlighting for vtrt while running from recorder`);
|
|
2415
|
+
// this._highlightElements(frame, dataAttribute)
|
|
2416
|
+
// .then(async () => {
|
|
2417
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2418
|
+
// this._unhighlightElements(frame, dataAttribute).then(
|
|
2419
|
+
// () => {}
|
|
2420
|
+
// console.log(`Unhighlighting vrtr in recorder is successful`)
|
|
2421
|
+
// );
|
|
2422
|
+
// })
|
|
2423
|
+
// .catch(e);
|
|
2424
|
+
// }
|
|
2425
|
+
//await this._highlightElements(frame, cssAnchor);
|
|
2426
|
+
const element = await frame.locator(dataAttribute).first();
|
|
2427
|
+
// await new Promise((resolve) => setTimeout(resolve, 100));
|
|
2428
|
+
// await this._unhighlightElements(frame, dataAttribute);
|
|
2429
|
+
if (element) {
|
|
2430
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2431
|
+
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2432
|
+
}
|
|
2433
|
+
await _screenshot(state, this);
|
|
2434
|
+
return state.info;
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2440
|
+
}
|
|
2441
|
+
catch (e) {
|
|
2442
|
+
await _commandError(state, e, this);
|
|
2211
2443
|
}
|
|
2212
|
-
|
|
2213
|
-
|
|
2444
|
+
finally {
|
|
2445
|
+
_commandFinally(state, this);
|
|
2214
2446
|
}
|
|
2215
|
-
return serviceUrl;
|
|
2216
2447
|
}
|
|
2217
2448
|
async visualVerification(text, options = {}, world = null) {
|
|
2218
2449
|
const startTime = Date.now();
|
|
@@ -2228,7 +2459,7 @@ class StableBrowser {
|
|
|
2228
2459
|
throw new Error("TOKEN is not set");
|
|
2229
2460
|
}
|
|
2230
2461
|
try {
|
|
2231
|
-
let serviceUrl =
|
|
2462
|
+
let serviceUrl = _getServerUrl();
|
|
2232
2463
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2233
2464
|
info.screenshotPath = screenshotPath;
|
|
2234
2465
|
const screenshot = await this.takeScreenshot();
|
|
@@ -2317,6 +2548,7 @@ class StableBrowser {
|
|
|
2317
2548
|
let screenshotPath = null;
|
|
2318
2549
|
const info = {};
|
|
2319
2550
|
info.log = "";
|
|
2551
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
2320
2552
|
info.operation = "getTableData";
|
|
2321
2553
|
info.selectors = selectors;
|
|
2322
2554
|
try {
|
|
@@ -2392,7 +2624,7 @@ class StableBrowser {
|
|
|
2392
2624
|
info.operation = "analyzeTable";
|
|
2393
2625
|
info.selectors = selectors;
|
|
2394
2626
|
info.query = query;
|
|
2395
|
-
query =
|
|
2627
|
+
query = _fixUsingParams(query, _params);
|
|
2396
2628
|
info.query_fixed = query;
|
|
2397
2629
|
info.operator = operator;
|
|
2398
2630
|
info.value = value;
|
|
@@ -2537,6 +2769,17 @@ class StableBrowser {
|
|
|
2537
2769
|
}
|
|
2538
2770
|
return timeout;
|
|
2539
2771
|
}
|
|
2772
|
+
async saveStoreState(path = null, world = null) {
|
|
2773
|
+
const storageState = await this.page.context().storageState();
|
|
2774
|
+
//const testDataFile = _getDataFile(world, this.context, this);
|
|
2775
|
+
if (path) {
|
|
2776
|
+
// save { storageState: storageState } into the path
|
|
2777
|
+
fs.writeFileSync(path, JSON.stringify({ storageState: storageState }, null, 2));
|
|
2778
|
+
}
|
|
2779
|
+
else {
|
|
2780
|
+
await this.setTestData({ storageState: storageState }, world);
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2540
2783
|
async waitForPageLoad(options = {}, world = null) {
|
|
2541
2784
|
let timeout = this._getLoadTimeout(options);
|
|
2542
2785
|
const promiseArray = [];
|
|
@@ -2722,14 +2965,22 @@ class StableBrowser {
|
|
|
2722
2965
|
}
|
|
2723
2966
|
}
|
|
2724
2967
|
async beforeStep(world, step) {
|
|
2725
|
-
this.stepName = step.pickleStep.text;
|
|
2726
|
-
this.logger.info("step: " + this.stepName);
|
|
2727
2968
|
if (this.stepIndex === undefined) {
|
|
2728
2969
|
this.stepIndex = 0;
|
|
2729
2970
|
}
|
|
2730
2971
|
else {
|
|
2731
2972
|
this.stepIndex++;
|
|
2732
2973
|
}
|
|
2974
|
+
if (step && step.pickleStep && step.pickleStep.text) {
|
|
2975
|
+
this.stepName = step.pickleStep.text;
|
|
2976
|
+
this.logger.info("step: " + this.stepName);
|
|
2977
|
+
}
|
|
2978
|
+
else if (step && step.text) {
|
|
2979
|
+
this.stepName = step.text;
|
|
2980
|
+
}
|
|
2981
|
+
else {
|
|
2982
|
+
this.stepName = "step " + this.stepIndex;
|
|
2983
|
+
}
|
|
2733
2984
|
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2734
2985
|
if (this.context.browserObject.context) {
|
|
2735
2986
|
await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
|
|
@@ -2759,156 +3010,5 @@ function createTimedPromise(promise, label) {
|
|
|
2759
3010
|
.then((result) => ({ status: "fulfilled", label, result }))
|
|
2760
3011
|
.catch((error) => Promise.reject({ status: "rejected", label, error }));
|
|
2761
3012
|
}
|
|
2762
|
-
const KEYBOARD_EVENTS = [
|
|
2763
|
-
"ALT",
|
|
2764
|
-
"AltGraph",
|
|
2765
|
-
"CapsLock",
|
|
2766
|
-
"Control",
|
|
2767
|
-
"Fn",
|
|
2768
|
-
"FnLock",
|
|
2769
|
-
"Hyper",
|
|
2770
|
-
"Meta",
|
|
2771
|
-
"NumLock",
|
|
2772
|
-
"ScrollLock",
|
|
2773
|
-
"Shift",
|
|
2774
|
-
"Super",
|
|
2775
|
-
"Symbol",
|
|
2776
|
-
"SymbolLock",
|
|
2777
|
-
"Enter",
|
|
2778
|
-
"Tab",
|
|
2779
|
-
"ArrowDown",
|
|
2780
|
-
"ArrowLeft",
|
|
2781
|
-
"ArrowRight",
|
|
2782
|
-
"ArrowUp",
|
|
2783
|
-
"End",
|
|
2784
|
-
"Home",
|
|
2785
|
-
"PageDown",
|
|
2786
|
-
"PageUp",
|
|
2787
|
-
"Backspace",
|
|
2788
|
-
"Clear",
|
|
2789
|
-
"Copy",
|
|
2790
|
-
"CrSel",
|
|
2791
|
-
"Cut",
|
|
2792
|
-
"Delete",
|
|
2793
|
-
"EraseEof",
|
|
2794
|
-
"ExSel",
|
|
2795
|
-
"Insert",
|
|
2796
|
-
"Paste",
|
|
2797
|
-
"Redo",
|
|
2798
|
-
"Undo",
|
|
2799
|
-
"Accept",
|
|
2800
|
-
"Again",
|
|
2801
|
-
"Attn",
|
|
2802
|
-
"Cancel",
|
|
2803
|
-
"ContextMenu",
|
|
2804
|
-
"Escape",
|
|
2805
|
-
"Execute",
|
|
2806
|
-
"Find",
|
|
2807
|
-
"Finish",
|
|
2808
|
-
"Help",
|
|
2809
|
-
"Pause",
|
|
2810
|
-
"Play",
|
|
2811
|
-
"Props",
|
|
2812
|
-
"Select",
|
|
2813
|
-
"ZoomIn",
|
|
2814
|
-
"ZoomOut",
|
|
2815
|
-
"BrightnessDown",
|
|
2816
|
-
"BrightnessUp",
|
|
2817
|
-
"Eject",
|
|
2818
|
-
"LogOff",
|
|
2819
|
-
"Power",
|
|
2820
|
-
"PowerOff",
|
|
2821
|
-
"PrintScreen",
|
|
2822
|
-
"Hibernate",
|
|
2823
|
-
"Standby",
|
|
2824
|
-
"WakeUp",
|
|
2825
|
-
"AllCandidates",
|
|
2826
|
-
"Alphanumeric",
|
|
2827
|
-
"CodeInput",
|
|
2828
|
-
"Compose",
|
|
2829
|
-
"Convert",
|
|
2830
|
-
"Dead",
|
|
2831
|
-
"FinalMode",
|
|
2832
|
-
"GroupFirst",
|
|
2833
|
-
"GroupLast",
|
|
2834
|
-
"GroupNext",
|
|
2835
|
-
"GroupPrevious",
|
|
2836
|
-
"ModeChange",
|
|
2837
|
-
"NextCandidate",
|
|
2838
|
-
"NonConvert",
|
|
2839
|
-
"PreviousCandidate",
|
|
2840
|
-
"Process",
|
|
2841
|
-
"SingleCandidate",
|
|
2842
|
-
"HangulMode",
|
|
2843
|
-
"HanjaMode",
|
|
2844
|
-
"JunjaMode",
|
|
2845
|
-
"Eisu",
|
|
2846
|
-
"Hankaku",
|
|
2847
|
-
"Hiragana",
|
|
2848
|
-
"HiraganaKatakana",
|
|
2849
|
-
"KanaMode",
|
|
2850
|
-
"KanjiMode",
|
|
2851
|
-
"Katakana",
|
|
2852
|
-
"Romaji",
|
|
2853
|
-
"Zenkaku",
|
|
2854
|
-
"ZenkakuHanaku",
|
|
2855
|
-
"F1",
|
|
2856
|
-
"F2",
|
|
2857
|
-
"F3",
|
|
2858
|
-
"F4",
|
|
2859
|
-
"F5",
|
|
2860
|
-
"F6",
|
|
2861
|
-
"F7",
|
|
2862
|
-
"F8",
|
|
2863
|
-
"F9",
|
|
2864
|
-
"F10",
|
|
2865
|
-
"F11",
|
|
2866
|
-
"F12",
|
|
2867
|
-
"Soft1",
|
|
2868
|
-
"Soft2",
|
|
2869
|
-
"Soft3",
|
|
2870
|
-
"Soft4",
|
|
2871
|
-
"ChannelDown",
|
|
2872
|
-
"ChannelUp",
|
|
2873
|
-
"Close",
|
|
2874
|
-
"MailForward",
|
|
2875
|
-
"MailReply",
|
|
2876
|
-
"MailSend",
|
|
2877
|
-
"MediaFastForward",
|
|
2878
|
-
"MediaPause",
|
|
2879
|
-
"MediaPlay",
|
|
2880
|
-
"MediaPlayPause",
|
|
2881
|
-
"MediaRecord",
|
|
2882
|
-
"MediaRewind",
|
|
2883
|
-
"MediaStop",
|
|
2884
|
-
"MediaTrackNext",
|
|
2885
|
-
"MediaTrackPrevious",
|
|
2886
|
-
"AudioBalanceLeft",
|
|
2887
|
-
"AudioBalanceRight",
|
|
2888
|
-
"AudioBassBoostDown",
|
|
2889
|
-
"AudioBassBoostToggle",
|
|
2890
|
-
"AudioBassBoostUp",
|
|
2891
|
-
"AudioFaderFront",
|
|
2892
|
-
"AudioFaderRear",
|
|
2893
|
-
"AudioSurroundModeNext",
|
|
2894
|
-
"AudioTrebleDown",
|
|
2895
|
-
"AudioTrebleUp",
|
|
2896
|
-
"AudioVolumeDown",
|
|
2897
|
-
"AudioVolumeMute",
|
|
2898
|
-
"AudioVolumeUp",
|
|
2899
|
-
"MicrophoneToggle",
|
|
2900
|
-
"MicrophoneVolumeDown",
|
|
2901
|
-
"MicrophoneVolumeMute",
|
|
2902
|
-
"MicrophoneVolumeUp",
|
|
2903
|
-
"TV",
|
|
2904
|
-
"TV3DMode",
|
|
2905
|
-
"TVAntennaCable",
|
|
2906
|
-
"TVAudioDescription",
|
|
2907
|
-
];
|
|
2908
|
-
function unEscapeString(str) {
|
|
2909
|
-
const placeholder = "__NEWLINE__";
|
|
2910
|
-
str = str.replace(new RegExp(placeholder, "g"), "\n");
|
|
2911
|
-
return str;
|
|
2912
|
-
}
|
|
2913
3013
|
export { StableBrowser };
|
|
2914
3014
|
//# sourceMappingURL=stable_browser.js.map
|