automation_model 1.0.558-dev → 1.0.560-dev
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 +1 -0
- package/lib/api.js +5 -7
- package/lib/api.js.map +1 -1
- package/lib/browser_manager.js +24 -0
- package/lib/browser_manager.js.map +1 -1
- package/lib/command_common.js +2 -2
- package/lib/command_common.js.map +1 -1
- package/lib/locate_element.js +10 -10
- package/lib/locate_element.js.map +1 -1
- package/lib/scripts/find_text.js +125 -0
- package/lib/stable_browser.d.ts +6 -9
- package/lib/stable_browser.js +154 -396
- package/lib/stable_browser.js.map +1 -1
- package/lib/utils.d.ts +12 -1
- package/lib/utils.js +235 -3
- package/lib/utils.js.map +1 -1
- package/package.json +4 -3
- /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 { _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";
|
|
@@ -31,6 +31,8 @@ export const Types = {
|
|
|
31
31
|
GET_PAGE_STATUS: "get_page_status",
|
|
32
32
|
CLICK_ROW_ACTION: "click_row_action",
|
|
33
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",
|
|
34
36
|
ANALYZE_TABLE: "analyze_table",
|
|
35
37
|
SELECT: "select_combobox",
|
|
36
38
|
VERIFY_PAGE_PATH: "verify_page_path",
|
|
@@ -48,6 +50,7 @@ export const Types = {
|
|
|
48
50
|
SET_INPUT: "set_input",
|
|
49
51
|
WAIT_FOR_TEXT_TO_DISAPPEAR: "wait_for_text_to_disappear",
|
|
50
52
|
VERIFY_ATTRIBUTE: "verify_element_attribute",
|
|
53
|
+
VERIFY_TEXT_WITH_RELATION: "verify_text_with_relation",
|
|
51
54
|
};
|
|
52
55
|
export const apps = {};
|
|
53
56
|
class StableBrowser {
|
|
@@ -101,24 +104,6 @@ class StableBrowser {
|
|
|
101
104
|
registerNetworkEvents(this.world, this, this.context, this.page);
|
|
102
105
|
registerDownloadEvent(this.page, this.world, this.context);
|
|
103
106
|
}
|
|
104
|
-
async scrollPageToLoadLazyElements() {
|
|
105
|
-
let lastHeight = await this.page.evaluate(() => document.body.scrollHeight);
|
|
106
|
-
let retry = 0;
|
|
107
|
-
while (true) {
|
|
108
|
-
await this.page.evaluate(() => window.scrollBy(0, window.innerHeight));
|
|
109
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
110
|
-
let newHeight = await this.page.evaluate(() => document.body.scrollHeight);
|
|
111
|
-
if (newHeight === lastHeight) {
|
|
112
|
-
break;
|
|
113
|
-
}
|
|
114
|
-
lastHeight = newHeight;
|
|
115
|
-
retry++;
|
|
116
|
-
if (retry > 10) {
|
|
117
|
-
break;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
await this.page.evaluate(() => window.scrollTo(0, 0));
|
|
121
|
-
}
|
|
122
107
|
registerEventListeners(context) {
|
|
123
108
|
this.registerConsoleLogListener(this.page, context);
|
|
124
109
|
// this.registerRequestListener(this.page, context, this.webLogFile);
|
|
@@ -177,8 +162,8 @@ class StableBrowser {
|
|
|
177
162
|
};
|
|
178
163
|
}
|
|
179
164
|
const tempContext = {};
|
|
180
|
-
|
|
181
|
-
|
|
165
|
+
_copyContext(this, tempContext);
|
|
166
|
+
_copyContext(apps[appName], this);
|
|
182
167
|
apps[this.appName] = tempContext;
|
|
183
168
|
this.appName = appName;
|
|
184
169
|
if (newContextCreated) {
|
|
@@ -187,22 +172,6 @@ class StableBrowser {
|
|
|
187
172
|
await this.waitForPageLoad();
|
|
188
173
|
}
|
|
189
174
|
}
|
|
190
|
-
_copyContext(from, to) {
|
|
191
|
-
to.browser = from.browser;
|
|
192
|
-
to.page = from.page;
|
|
193
|
-
to.context = from.context;
|
|
194
|
-
}
|
|
195
|
-
getWebLogFile(logFolder) {
|
|
196
|
-
if (!fs.existsSync(logFolder)) {
|
|
197
|
-
fs.mkdirSync(logFolder, { recursive: true });
|
|
198
|
-
}
|
|
199
|
-
let nextIndex = 1;
|
|
200
|
-
while (fs.existsSync(path.join(logFolder, nextIndex.toString() + ".json"))) {
|
|
201
|
-
nextIndex++;
|
|
202
|
-
}
|
|
203
|
-
const fileName = nextIndex + ".json";
|
|
204
|
-
return path.join(logFolder, fileName);
|
|
205
|
-
}
|
|
206
175
|
registerConsoleLogListener(page, context) {
|
|
207
176
|
if (!this.context.webLogger) {
|
|
208
177
|
this.context.webLogger = [];
|
|
@@ -274,47 +243,8 @@ class StableBrowser {
|
|
|
274
243
|
timeout: 60000,
|
|
275
244
|
});
|
|
276
245
|
}
|
|
277
|
-
_fixUsingParams(text, _params) {
|
|
278
|
-
if (!_params || typeof text !== "string") {
|
|
279
|
-
return text;
|
|
280
|
-
}
|
|
281
|
-
for (let key in _params) {
|
|
282
|
-
let regValue = key;
|
|
283
|
-
if (key.startsWith("_")) {
|
|
284
|
-
// remove the _ prefix
|
|
285
|
-
regValue = key.substring(1);
|
|
286
|
-
}
|
|
287
|
-
text = text.replaceAll(new RegExp("{" + regValue + "}", "g"), _params[key]);
|
|
288
|
-
}
|
|
289
|
-
return text;
|
|
290
|
-
}
|
|
291
|
-
_fixLocatorUsingParams(locator, _params) {
|
|
292
|
-
// check if not null
|
|
293
|
-
if (!locator) {
|
|
294
|
-
return locator;
|
|
295
|
-
}
|
|
296
|
-
// clone the locator
|
|
297
|
-
locator = JSON.parse(JSON.stringify(locator));
|
|
298
|
-
this.scanAndManipulate(locator, _params);
|
|
299
|
-
return locator;
|
|
300
|
-
}
|
|
301
|
-
_isObject(value) {
|
|
302
|
-
return value && typeof value === "object" && value.constructor === Object;
|
|
303
|
-
}
|
|
304
|
-
scanAndManipulate(currentObj, _params) {
|
|
305
|
-
for (const key in currentObj) {
|
|
306
|
-
if (typeof currentObj[key] === "string") {
|
|
307
|
-
// Perform string manipulation
|
|
308
|
-
currentObj[key] = this._fixUsingParams(currentObj[key], _params);
|
|
309
|
-
}
|
|
310
|
-
else if (this._isObject(currentObj[key])) {
|
|
311
|
-
// Recursively scan nested objects
|
|
312
|
-
this.scanAndManipulate(currentObj[key], _params);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
246
|
_getLocator(locator, scope, _params) {
|
|
317
|
-
locator =
|
|
247
|
+
locator = _fixLocatorUsingParams(locator, _params);
|
|
318
248
|
let locatorReturn;
|
|
319
249
|
if (locator.role) {
|
|
320
250
|
if (locator.role[1].nameReg) {
|
|
@@ -322,7 +252,7 @@ class StableBrowser {
|
|
|
322
252
|
delete locator.role[1].nameReg;
|
|
323
253
|
}
|
|
324
254
|
// if (locator.role[1].name) {
|
|
325
|
-
// locator.role[1].name =
|
|
255
|
+
// locator.role[1].name = _fixUsingParams(locator.role[1].name, _params);
|
|
326
256
|
// }
|
|
327
257
|
locatorReturn = scope.getByRole(locator.role[0], locator.role[1]);
|
|
328
258
|
}
|
|
@@ -365,7 +295,7 @@ class StableBrowser {
|
|
|
365
295
|
if (css && css.locator) {
|
|
366
296
|
css = css.locator;
|
|
367
297
|
}
|
|
368
|
-
let result = await this._locateElementByText(scope,
|
|
298
|
+
let result = await this._locateElementByText(scope, _fixUsingParams(text, _params), "*:not(script, style, head)", false, false, _params);
|
|
369
299
|
if (result.elementCount === 0) {
|
|
370
300
|
return;
|
|
371
301
|
}
|
|
@@ -381,116 +311,28 @@ class StableBrowser {
|
|
|
381
311
|
async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, _params) {
|
|
382
312
|
//const stringifyText = JSON.stringify(text);
|
|
383
313
|
return await scope.locator(":root").evaluate((_node, [text, tag, regex, partial]) => {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
if (currentNode === parent) {
|
|
388
|
-
return true;
|
|
389
|
-
}
|
|
390
|
-
currentNode = currentNode.parentNode;
|
|
391
|
-
}
|
|
392
|
-
return false;
|
|
393
|
-
}
|
|
394
|
-
document.isParent = isParent;
|
|
395
|
-
function getRegex(str) {
|
|
396
|
-
const match = str.match(/^\/(.*?)\/([gimuy]*)$/);
|
|
397
|
-
if (!match) {
|
|
398
|
-
return null;
|
|
399
|
-
}
|
|
400
|
-
let [_, pattern, flags] = match;
|
|
401
|
-
return new RegExp(pattern, flags);
|
|
402
|
-
}
|
|
403
|
-
document.getRegex = getRegex;
|
|
404
|
-
function collectAllShadowDomElements(element, result = []) {
|
|
405
|
-
// Check and add the element if it has a shadow root
|
|
406
|
-
if (element.shadowRoot) {
|
|
407
|
-
result.push(element);
|
|
408
|
-
// Also search within the shadow root
|
|
409
|
-
document.collectAllShadowDomElements(element.shadowRoot, result);
|
|
410
|
-
}
|
|
411
|
-
// Iterate over child nodes
|
|
412
|
-
element.childNodes.forEach((child) => {
|
|
413
|
-
// Recursively call the function for each child node
|
|
414
|
-
document.collectAllShadowDomElements(child, result);
|
|
415
|
-
});
|
|
416
|
-
return result;
|
|
417
|
-
}
|
|
418
|
-
document.collectAllShadowDomElements = collectAllShadowDomElements;
|
|
419
|
-
if (!tag) {
|
|
420
|
-
tag = "*:not(script, style, head)";
|
|
421
|
-
}
|
|
422
|
-
let regexpSearch = document.getRegex(text);
|
|
423
|
-
if (regexpSearch) {
|
|
424
|
-
regex = true;
|
|
425
|
-
}
|
|
426
|
-
let elements = Array.from(document.querySelectorAll(tag));
|
|
427
|
-
let shadowHosts = [];
|
|
428
|
-
document.collectAllShadowDomElements(document, shadowHosts);
|
|
429
|
-
for (let i = 0; i < shadowHosts.length; i++) {
|
|
430
|
-
let shadowElement = shadowHosts[i].shadowRoot;
|
|
431
|
-
if (!shadowElement) {
|
|
432
|
-
console.log("shadowElement is null, for host " + shadowHosts[i]);
|
|
433
|
-
continue;
|
|
434
|
-
}
|
|
435
|
-
let shadowElements = Array.from(shadowElement.querySelectorAll(tag));
|
|
436
|
-
elements = elements.concat(shadowElements);
|
|
437
|
-
}
|
|
438
|
-
let randomToken = null;
|
|
439
|
-
const foundElements = [];
|
|
314
|
+
const options = {
|
|
315
|
+
innerText: true,
|
|
316
|
+
};
|
|
440
317
|
if (regex) {
|
|
441
|
-
|
|
442
|
-
regexpSearch = new RegExp(text, "im");
|
|
443
|
-
}
|
|
444
|
-
for (let i = 0; i < elements.length; i++) {
|
|
445
|
-
const element = elements[i];
|
|
446
|
-
if ((element.innerText && regexpSearch.test(element.innerText)) ||
|
|
447
|
-
(element.value && regexpSearch.test(element.value))) {
|
|
448
|
-
foundElements.push(element);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
318
|
+
options.singleRegex = true;
|
|
451
319
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
for (let i = 0; i < elements.length; i++) {
|
|
455
|
-
const element = elements[i];
|
|
456
|
-
if (partial) {
|
|
457
|
-
if ((element.innerText && element.innerText.toLowerCase().trim().includes(text.toLowerCase())) ||
|
|
458
|
-
(element.value && element.value.toLowerCase().includes(text.toLowerCase()))) {
|
|
459
|
-
foundElements.push(element);
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
else {
|
|
463
|
-
if ((element.innerText && element.innerText.trim() === text) ||
|
|
464
|
-
(element.value && element.value === text)) {
|
|
465
|
-
foundElements.push(element);
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
}
|
|
320
|
+
if (tag) {
|
|
321
|
+
options.tag = tag;
|
|
469
322
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
let element = foundElements[i];
|
|
473
|
-
let hasChild = false;
|
|
474
|
-
for (let j = 0; j < foundElements.length; j++) {
|
|
475
|
-
if (i === j) {
|
|
476
|
-
continue;
|
|
477
|
-
}
|
|
478
|
-
if (isParent(element, foundElements[j])) {
|
|
479
|
-
hasChild = true;
|
|
480
|
-
break;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
if (!hasChild) {
|
|
484
|
-
noChildElements.push(element);
|
|
485
|
-
}
|
|
323
|
+
if (!(partial === true)) {
|
|
324
|
+
options.exactMatch = true;
|
|
486
325
|
}
|
|
326
|
+
const elements = window.findMatchingElements(text, options);
|
|
327
|
+
let randomToken = null;
|
|
328
|
+
const foundElements = [];
|
|
487
329
|
let elementCount = 0;
|
|
488
|
-
if (
|
|
489
|
-
for (let i = 0; i <
|
|
330
|
+
if (elements.length > 0) {
|
|
331
|
+
for (let i = 0; i < elements.length; i++) {
|
|
490
332
|
if (randomToken === null) {
|
|
491
333
|
randomToken = Math.random().toString(36).substring(7);
|
|
492
334
|
}
|
|
493
|
-
let element =
|
|
335
|
+
let element = elements[i];
|
|
494
336
|
element.setAttribute("data-blinq-id", "blinq-id-" + randomToken);
|
|
495
337
|
elementCount++;
|
|
496
338
|
}
|
|
@@ -512,7 +354,7 @@ class StableBrowser {
|
|
|
512
354
|
let locatorSearch = selectorHierarchy[index];
|
|
513
355
|
let originalLocatorSearch = "";
|
|
514
356
|
try {
|
|
515
|
-
originalLocatorSearch =
|
|
357
|
+
originalLocatorSearch = _fixUsingParams(JSON.stringify(locatorSearch), _params);
|
|
516
358
|
locatorSearch = JSON.parse(originalLocatorSearch);
|
|
517
359
|
}
|
|
518
360
|
catch (e) {
|
|
@@ -530,7 +372,7 @@ class StableBrowser {
|
|
|
530
372
|
locator = this._getLocator({ css: locatorString }, scope, _params);
|
|
531
373
|
}
|
|
532
374
|
else if (locatorSearch.text) {
|
|
533
|
-
let text =
|
|
375
|
+
let text = _fixUsingParams(locatorSearch.text, _params);
|
|
534
376
|
let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
|
|
535
377
|
if (result.elementCount === 0) {
|
|
536
378
|
info.failCause.textNotFound = true;
|
|
@@ -874,7 +716,7 @@ class StableBrowser {
|
|
|
874
716
|
highPriorityOnly = false;
|
|
875
717
|
if (this.configuration && this.configuration.load_all_lazy === true && !lazy_scroll) {
|
|
876
718
|
lazy_scroll = true;
|
|
877
|
-
await this.
|
|
719
|
+
await scrollPageToLoadLazyElements(this.page);
|
|
878
720
|
}
|
|
879
721
|
}
|
|
880
722
|
if (Date.now() - startTime > visibleOnlyTimeout) {
|
|
@@ -2031,7 +1873,7 @@ class StableBrowser {
|
|
|
2031
1873
|
if (options && options.timeout) {
|
|
2032
1874
|
timeout = options.timeout;
|
|
2033
1875
|
}
|
|
2034
|
-
const serviceUrl =
|
|
1876
|
+
const serviceUrl = _getServerUrl() + "/api/mail/createLinkOrCodeFromEmail";
|
|
2035
1877
|
const request = {
|
|
2036
1878
|
method: "POST",
|
|
2037
1879
|
url: serviceUrl,
|
|
@@ -2224,6 +2066,34 @@ class StableBrowser {
|
|
|
2224
2066
|
});
|
|
2225
2067
|
}
|
|
2226
2068
|
}
|
|
2069
|
+
async findTextInAllFrames(dateAlternatives, numberAlternatives, text, state) {
|
|
2070
|
+
const frames = this.page.frames();
|
|
2071
|
+
let results = [];
|
|
2072
|
+
for (let i = 0; i < frames.length; i++) {
|
|
2073
|
+
if (dateAlternatives.date) {
|
|
2074
|
+
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2075
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", false, true, {});
|
|
2076
|
+
result.frame = frames[i];
|
|
2077
|
+
results.push(result);
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
else if (numberAlternatives.number) {
|
|
2081
|
+
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2082
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", false, true, {});
|
|
2083
|
+
result.frame = frames[i];
|
|
2084
|
+
results.push(result);
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
else {
|
|
2088
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", false, true, {});
|
|
2089
|
+
result.frame = frames[i];
|
|
2090
|
+
results.push(result);
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
state.info.results = results;
|
|
2094
|
+
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2095
|
+
return resultWithElementsFound;
|
|
2096
|
+
}
|
|
2227
2097
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2228
2098
|
text = unEscapeString(text);
|
|
2229
2099
|
const state = {
|
|
@@ -2233,7 +2103,7 @@ class StableBrowser {
|
|
|
2233
2103
|
locate: false,
|
|
2234
2104
|
scroll: false,
|
|
2235
2105
|
highlight: false,
|
|
2236
|
-
type: Types.
|
|
2106
|
+
type: Types.VERIFY_PAGE_CONTAINS_TEXT,
|
|
2237
2107
|
text: `Verify text exists in page`,
|
|
2238
2108
|
operation: "verifyTextExistInPage",
|
|
2239
2109
|
log: "***** verify text " + text + " exists in page *****\n",
|
|
@@ -2251,31 +2121,7 @@ class StableBrowser {
|
|
|
2251
2121
|
await _preCommand(state, this);
|
|
2252
2122
|
state.info.text = text;
|
|
2253
2123
|
while (true) {
|
|
2254
|
-
const
|
|
2255
|
-
let results = [];
|
|
2256
|
-
for (let i = 0; i < frames.length; i++) {
|
|
2257
|
-
if (dateAlternatives.date) {
|
|
2258
|
-
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2259
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2260
|
-
result.frame = frames[i];
|
|
2261
|
-
results.push(result);
|
|
2262
|
-
}
|
|
2263
|
-
}
|
|
2264
|
-
else if (numberAlternatives.number) {
|
|
2265
|
-
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2266
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2267
|
-
result.frame = frames[i];
|
|
2268
|
-
results.push(result);
|
|
2269
|
-
}
|
|
2270
|
-
}
|
|
2271
|
-
else {
|
|
2272
|
-
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2273
|
-
result.frame = frames[i];
|
|
2274
|
-
results.push(result);
|
|
2275
|
-
}
|
|
2276
|
-
}
|
|
2277
|
-
state.info.results = results;
|
|
2278
|
-
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2124
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, text, state);
|
|
2279
2125
|
if (resultWithElementsFound.length === 0) {
|
|
2280
2126
|
if (Date.now() - state.startTime > timeout) {
|
|
2281
2127
|
throw new Error(`Text ${text} not found in page`);
|
|
@@ -2332,31 +2178,7 @@ class StableBrowser {
|
|
|
2332
2178
|
await _preCommand(state, this);
|
|
2333
2179
|
state.info.text = text;
|
|
2334
2180
|
while (true) {
|
|
2335
|
-
const
|
|
2336
|
-
let results = [];
|
|
2337
|
-
for (let i = 0; i < frames.length; i++) {
|
|
2338
|
-
if (dateAlternatives.date) {
|
|
2339
|
-
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2340
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2341
|
-
result.frame = frames[i];
|
|
2342
|
-
results.push(result);
|
|
2343
|
-
}
|
|
2344
|
-
}
|
|
2345
|
-
else if (numberAlternatives.number) {
|
|
2346
|
-
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2347
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2348
|
-
result.frame = frames[i];
|
|
2349
|
-
results.push(result);
|
|
2350
|
-
}
|
|
2351
|
-
}
|
|
2352
|
-
else {
|
|
2353
|
-
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2354
|
-
result.frame = frames[i];
|
|
2355
|
-
results.push(result);
|
|
2356
|
-
}
|
|
2357
|
-
}
|
|
2358
|
-
state.info.results = results;
|
|
2359
|
-
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2181
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, text, state);
|
|
2360
2182
|
if (resultWithElementsFound.length === 0) {
|
|
2361
2183
|
await _screenshot(state, this);
|
|
2362
2184
|
return state.info;
|
|
@@ -2374,15 +2196,102 @@ class StableBrowser {
|
|
|
2374
2196
|
_commandFinally(state, this);
|
|
2375
2197
|
}
|
|
2376
2198
|
}
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2199
|
+
async verifyTextRelatedToText(textAnchor, climb, textToVerify, options = {}, world = null) {
|
|
2200
|
+
textAnchor = unEscapeString(textAnchor);
|
|
2201
|
+
textToVerify = unEscapeString(textToVerify);
|
|
2202
|
+
const state = {
|
|
2203
|
+
text_search: textToVerify,
|
|
2204
|
+
options,
|
|
2205
|
+
world,
|
|
2206
|
+
locate: false,
|
|
2207
|
+
scroll: false,
|
|
2208
|
+
highlight: false,
|
|
2209
|
+
type: Types.VERIFY_TEXT_WITH_RELATION,
|
|
2210
|
+
text: `Verify text with relation to another text`,
|
|
2211
|
+
operation: "verify_text_with_relation",
|
|
2212
|
+
log: "***** search for " + textAnchor + " climb " + climb + " and verify " + textToVerify + " found *****\n",
|
|
2213
|
+
};
|
|
2214
|
+
const timeout = this._getLoadTimeout(options);
|
|
2215
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2216
|
+
let newValue = await this._replaceWithLocalData(textAnchor, world);
|
|
2217
|
+
if (newValue !== textAnchor) {
|
|
2218
|
+
this.logger.info(textAnchor + "=" + newValue);
|
|
2219
|
+
textAnchor = newValue;
|
|
2220
|
+
}
|
|
2221
|
+
newValue = await this._replaceWithLocalData(textToVerify, world);
|
|
2222
|
+
if (newValue !== textToVerify) {
|
|
2223
|
+
this.logger.info(textToVerify + "=" + newValue);
|
|
2224
|
+
textToVerify = newValue;
|
|
2225
|
+
}
|
|
2226
|
+
let dateAlternatives = findDateAlternatives(textToVerify);
|
|
2227
|
+
let numberAlternatives = findNumberAlternatives(textToVerify);
|
|
2228
|
+
let foundAncore = false;
|
|
2229
|
+
try {
|
|
2230
|
+
await _preCommand(state, this);
|
|
2231
|
+
state.info.text = textToVerify;
|
|
2232
|
+
while (true) {
|
|
2233
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, textAnchor, state);
|
|
2234
|
+
if (resultWithElementsFound.length === 0) {
|
|
2235
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2236
|
+
throw new Error(`Text ${foundAncore ? textToVerify : textAnchor} not found in page`);
|
|
2237
|
+
}
|
|
2238
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2239
|
+
continue;
|
|
2240
|
+
}
|
|
2241
|
+
for (let i = 0; i < resultWithElementsFound.length; i++) {
|
|
2242
|
+
foundAncore = true;
|
|
2243
|
+
const result = resultWithElementsFound[i];
|
|
2244
|
+
const token = result.randomToken;
|
|
2245
|
+
const frame = result.frame;
|
|
2246
|
+
const css = `[data-blinq-id="blinq-id-${token}"]`;
|
|
2247
|
+
const findResult = await frame.evaluate(([css, climb, textToVerify, token]) => {
|
|
2248
|
+
const elements = Array.from(document.querySelectorAll(css));
|
|
2249
|
+
for (let i = 0; i < elements.length; i++) {
|
|
2250
|
+
const element = elements[i];
|
|
2251
|
+
let climbParent = element;
|
|
2252
|
+
for (let j = 0; j < climb; j++) {
|
|
2253
|
+
climbParent = climbParent.parentElement;
|
|
2254
|
+
if (!climbParent) {
|
|
2255
|
+
break;
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
if (!climbParent) {
|
|
2259
|
+
continue;
|
|
2260
|
+
}
|
|
2261
|
+
const foundElements = window.findMatchingElements(textToVerify, {}, climbParent);
|
|
2262
|
+
if (foundElements.length > 0) {
|
|
2263
|
+
// set the container element attribute
|
|
2264
|
+
element.setAttribute("data-blinq-id", `blinq-id-${token}-anchor`);
|
|
2265
|
+
climbParent.setAttribute("data-blinq-id", `blinq-id-${token}-container`);
|
|
2266
|
+
foundElements[0].setAttribute("data-blinq-id", `blinq-id-${token}-verify`);
|
|
2267
|
+
return { found: true };
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
return { found: false };
|
|
2271
|
+
}, [css, climb, textToVerify, result.randomToken]);
|
|
2272
|
+
if (findResult.found === true) {
|
|
2273
|
+
const dataAttribute = `[data-blinq-id="blinq-id-${token}-verify"]`;
|
|
2274
|
+
const cssAnchor = `[data-blinq-id="blinq-id-${token}-anchor"]`;
|
|
2275
|
+
await this._highlightElements(frame, dataAttribute);
|
|
2276
|
+
await this._highlightElements(frame, cssAnchor);
|
|
2277
|
+
const element = await frame.$(dataAttribute);
|
|
2278
|
+
if (element) {
|
|
2279
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2280
|
+
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2281
|
+
}
|
|
2282
|
+
await _screenshot(state, this);
|
|
2283
|
+
return state.info;
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2381
2288
|
}
|
|
2382
|
-
|
|
2383
|
-
|
|
2289
|
+
catch (e) {
|
|
2290
|
+
await _commandError(state, e, this);
|
|
2291
|
+
}
|
|
2292
|
+
finally {
|
|
2293
|
+
_commandFinally(state, this);
|
|
2384
2294
|
}
|
|
2385
|
-
return serviceUrl;
|
|
2386
2295
|
}
|
|
2387
2296
|
async visualVerification(text, options = {}, world = null) {
|
|
2388
2297
|
const startTime = Date.now();
|
|
@@ -2398,7 +2307,7 @@ class StableBrowser {
|
|
|
2398
2307
|
throw new Error("TOKEN is not set");
|
|
2399
2308
|
}
|
|
2400
2309
|
try {
|
|
2401
|
-
let serviceUrl =
|
|
2310
|
+
let serviceUrl = _getServerUrl();
|
|
2402
2311
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2403
2312
|
info.screenshotPath = screenshotPath;
|
|
2404
2313
|
const screenshot = await this.takeScreenshot();
|
|
@@ -2563,7 +2472,7 @@ class StableBrowser {
|
|
|
2563
2472
|
info.operation = "analyzeTable";
|
|
2564
2473
|
info.selectors = selectors;
|
|
2565
2474
|
info.query = query;
|
|
2566
|
-
query =
|
|
2475
|
+
query = _fixUsingParams(query, _params);
|
|
2567
2476
|
info.query_fixed = query;
|
|
2568
2477
|
info.operator = operator;
|
|
2569
2478
|
info.value = value;
|
|
@@ -2930,156 +2839,5 @@ function createTimedPromise(promise, label) {
|
|
|
2930
2839
|
.then((result) => ({ status: "fulfilled", label, result }))
|
|
2931
2840
|
.catch((error) => Promise.reject({ status: "rejected", label, error }));
|
|
2932
2841
|
}
|
|
2933
|
-
const KEYBOARD_EVENTS = [
|
|
2934
|
-
"ALT",
|
|
2935
|
-
"AltGraph",
|
|
2936
|
-
"CapsLock",
|
|
2937
|
-
"Control",
|
|
2938
|
-
"Fn",
|
|
2939
|
-
"FnLock",
|
|
2940
|
-
"Hyper",
|
|
2941
|
-
"Meta",
|
|
2942
|
-
"NumLock",
|
|
2943
|
-
"ScrollLock",
|
|
2944
|
-
"Shift",
|
|
2945
|
-
"Super",
|
|
2946
|
-
"Symbol",
|
|
2947
|
-
"SymbolLock",
|
|
2948
|
-
"Enter",
|
|
2949
|
-
"Tab",
|
|
2950
|
-
"ArrowDown",
|
|
2951
|
-
"ArrowLeft",
|
|
2952
|
-
"ArrowRight",
|
|
2953
|
-
"ArrowUp",
|
|
2954
|
-
"End",
|
|
2955
|
-
"Home",
|
|
2956
|
-
"PageDown",
|
|
2957
|
-
"PageUp",
|
|
2958
|
-
"Backspace",
|
|
2959
|
-
"Clear",
|
|
2960
|
-
"Copy",
|
|
2961
|
-
"CrSel",
|
|
2962
|
-
"Cut",
|
|
2963
|
-
"Delete",
|
|
2964
|
-
"EraseEof",
|
|
2965
|
-
"ExSel",
|
|
2966
|
-
"Insert",
|
|
2967
|
-
"Paste",
|
|
2968
|
-
"Redo",
|
|
2969
|
-
"Undo",
|
|
2970
|
-
"Accept",
|
|
2971
|
-
"Again",
|
|
2972
|
-
"Attn",
|
|
2973
|
-
"Cancel",
|
|
2974
|
-
"ContextMenu",
|
|
2975
|
-
"Escape",
|
|
2976
|
-
"Execute",
|
|
2977
|
-
"Find",
|
|
2978
|
-
"Finish",
|
|
2979
|
-
"Help",
|
|
2980
|
-
"Pause",
|
|
2981
|
-
"Play",
|
|
2982
|
-
"Props",
|
|
2983
|
-
"Select",
|
|
2984
|
-
"ZoomIn",
|
|
2985
|
-
"ZoomOut",
|
|
2986
|
-
"BrightnessDown",
|
|
2987
|
-
"BrightnessUp",
|
|
2988
|
-
"Eject",
|
|
2989
|
-
"LogOff",
|
|
2990
|
-
"Power",
|
|
2991
|
-
"PowerOff",
|
|
2992
|
-
"PrintScreen",
|
|
2993
|
-
"Hibernate",
|
|
2994
|
-
"Standby",
|
|
2995
|
-
"WakeUp",
|
|
2996
|
-
"AllCandidates",
|
|
2997
|
-
"Alphanumeric",
|
|
2998
|
-
"CodeInput",
|
|
2999
|
-
"Compose",
|
|
3000
|
-
"Convert",
|
|
3001
|
-
"Dead",
|
|
3002
|
-
"FinalMode",
|
|
3003
|
-
"GroupFirst",
|
|
3004
|
-
"GroupLast",
|
|
3005
|
-
"GroupNext",
|
|
3006
|
-
"GroupPrevious",
|
|
3007
|
-
"ModeChange",
|
|
3008
|
-
"NextCandidate",
|
|
3009
|
-
"NonConvert",
|
|
3010
|
-
"PreviousCandidate",
|
|
3011
|
-
"Process",
|
|
3012
|
-
"SingleCandidate",
|
|
3013
|
-
"HangulMode",
|
|
3014
|
-
"HanjaMode",
|
|
3015
|
-
"JunjaMode",
|
|
3016
|
-
"Eisu",
|
|
3017
|
-
"Hankaku",
|
|
3018
|
-
"Hiragana",
|
|
3019
|
-
"HiraganaKatakana",
|
|
3020
|
-
"KanaMode",
|
|
3021
|
-
"KanjiMode",
|
|
3022
|
-
"Katakana",
|
|
3023
|
-
"Romaji",
|
|
3024
|
-
"Zenkaku",
|
|
3025
|
-
"ZenkakuHanaku",
|
|
3026
|
-
"F1",
|
|
3027
|
-
"F2",
|
|
3028
|
-
"F3",
|
|
3029
|
-
"F4",
|
|
3030
|
-
"F5",
|
|
3031
|
-
"F6",
|
|
3032
|
-
"F7",
|
|
3033
|
-
"F8",
|
|
3034
|
-
"F9",
|
|
3035
|
-
"F10",
|
|
3036
|
-
"F11",
|
|
3037
|
-
"F12",
|
|
3038
|
-
"Soft1",
|
|
3039
|
-
"Soft2",
|
|
3040
|
-
"Soft3",
|
|
3041
|
-
"Soft4",
|
|
3042
|
-
"ChannelDown",
|
|
3043
|
-
"ChannelUp",
|
|
3044
|
-
"Close",
|
|
3045
|
-
"MailForward",
|
|
3046
|
-
"MailReply",
|
|
3047
|
-
"MailSend",
|
|
3048
|
-
"MediaFastForward",
|
|
3049
|
-
"MediaPause",
|
|
3050
|
-
"MediaPlay",
|
|
3051
|
-
"MediaPlayPause",
|
|
3052
|
-
"MediaRecord",
|
|
3053
|
-
"MediaRewind",
|
|
3054
|
-
"MediaStop",
|
|
3055
|
-
"MediaTrackNext",
|
|
3056
|
-
"MediaTrackPrevious",
|
|
3057
|
-
"AudioBalanceLeft",
|
|
3058
|
-
"AudioBalanceRight",
|
|
3059
|
-
"AudioBassBoostDown",
|
|
3060
|
-
"AudioBassBoostToggle",
|
|
3061
|
-
"AudioBassBoostUp",
|
|
3062
|
-
"AudioFaderFront",
|
|
3063
|
-
"AudioFaderRear",
|
|
3064
|
-
"AudioSurroundModeNext",
|
|
3065
|
-
"AudioTrebleDown",
|
|
3066
|
-
"AudioTrebleUp",
|
|
3067
|
-
"AudioVolumeDown",
|
|
3068
|
-
"AudioVolumeMute",
|
|
3069
|
-
"AudioVolumeUp",
|
|
3070
|
-
"MicrophoneToggle",
|
|
3071
|
-
"MicrophoneVolumeDown",
|
|
3072
|
-
"MicrophoneVolumeMute",
|
|
3073
|
-
"MicrophoneVolumeUp",
|
|
3074
|
-
"TV",
|
|
3075
|
-
"TV3DMode",
|
|
3076
|
-
"TVAntennaCable",
|
|
3077
|
-
"TVAudioDescription",
|
|
3078
|
-
];
|
|
3079
|
-
function unEscapeString(str) {
|
|
3080
|
-
const placeholder = "__NEWLINE__";
|
|
3081
|
-
str = str.replace(new RegExp(placeholder, "g"), "\n");
|
|
3082
|
-
return str;
|
|
3083
|
-
}
|
|
3084
2842
|
export { StableBrowser };
|
|
3085
2843
|
//# sourceMappingURL=stable_browser.js.map
|