automation_model 1.0.519-dev → 1.0.519-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 +96 -98
- package/lib/api.js.map +1 -1
- package/lib/auto_page.d.ts +2 -1
- package/lib/auto_page.js +19 -2
- package/lib/auto_page.js.map +1 -1
- package/lib/browser_manager.d.ts +3 -2
- package/lib/browser_manager.js +70 -27
- package/lib/browser_manager.js.map +1 -1
- package/lib/command_common.d.ts +3 -2
- package/lib/command_common.js +68 -16
- package/lib/command_common.js.map +1 -1
- package/lib/error-messages.js +20 -2
- package/lib/error-messages.js.map +1 -1
- package/lib/generation_scripts.d.ts +4 -0
- package/lib/generation_scripts.js +2 -0
- package/lib/generation_scripts.js.map +1 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/init_browser.d.ts +2 -1
- package/lib/init_browser.js +15 -4
- package/lib/init_browser.js.map +1 -1
- package/lib/locate_element.js +15 -13
- package/lib/locate_element.js.map +1 -1
- package/lib/locator.d.ts +36 -0
- package/lib/locator.js +165 -0
- package/lib/locator.js.map +1 -1
- package/lib/locator_log.d.ts +26 -0
- package/lib/locator_log.js +69 -0
- package/lib/locator_log.js.map +1 -0
- package/lib/network.js +51 -12
- package/lib/network.js.map +1 -1
- package/lib/stable_browser.d.ts +55 -19
- package/lib/stable_browser.js +626 -524
- package/lib/stable_browser.js.map +1 -1
- package/lib/table.d.ts +13 -0
- package/lib/table.js +187 -0
- package/lib/table.js.map +1 -0
- package/lib/utils.d.ts +13 -1
- package/lib/utils.js +344 -6
- package/lib/utils.js.map +1 -1
- package/package.json +6 -5
- /package/lib/{axe → scripts}/axe.mini.js +0 -0
package/lib/stable_browser.js
CHANGED
|
@@ -7,18 +7,19 @@ import path from "path";
|
|
|
7
7
|
import reg_parser from "regex-parser";
|
|
8
8
|
import { findDateAlternatives, findNumberAlternatives } from "./analyze_helper.js";
|
|
9
9
|
import { getDateTimeValue } from "./date_time.js";
|
|
10
|
-
import drawRectangle from "./drawRect.js";
|
|
11
10
|
//import { closeUnexpectedPopups } from "./popups.js";
|
|
12
11
|
import { getTableCells, getTableData } from "./table_analyze.js";
|
|
13
|
-
import { maskValue, replaceWithLocalTestData } from "./utils.js";
|
|
12
|
+
import { _convertToRegexQuery, _copyContext, _fixLocatorUsingParams, _fixUsingParams, _getServerUrl, KEYBOARD_EVENTS, maskValue, replaceWithLocalTestData, scrollPageToLoadLazyElements, unEscapeString, } from "./utils.js";
|
|
14
13
|
import csv from "csv-parser";
|
|
15
14
|
import { Readable } from "node:stream";
|
|
16
15
|
import readline from "readline";
|
|
17
16
|
import { getContext } from "./init_browser.js";
|
|
18
17
|
import { locate_element } from "./locate_element.js";
|
|
19
|
-
import {
|
|
18
|
+
import { randomUUID } from "crypto";
|
|
19
|
+
import { _commandError, _commandFinally, _preCommand, _validateSelectors, _screenshot, _reportToWorld, } from "./command_common.js";
|
|
20
20
|
import { registerDownloadEvent, registerNetworkEvents } from "./network.js";
|
|
21
|
-
|
|
21
|
+
import { LocatorLog } from "./locator_log.js";
|
|
22
|
+
export const Types = {
|
|
22
23
|
CLICK: "click_element",
|
|
23
24
|
NAVIGATE: "navigate",
|
|
24
25
|
FILL: "fill_element",
|
|
@@ -29,6 +30,8 @@ const Types = {
|
|
|
29
30
|
GET_PAGE_STATUS: "get_page_status",
|
|
30
31
|
CLICK_ROW_ACTION: "click_row_action",
|
|
31
32
|
VERIFY_ELEMENT_CONTAINS_TEXT: "verify_element_contains_text",
|
|
33
|
+
VERIFY_PAGE_CONTAINS_TEXT: "verify_page_contains_text",
|
|
34
|
+
VERIFY_PAGE_CONTAINS_NO_TEXT: "verify_page_contains_no_text",
|
|
32
35
|
ANALYZE_TABLE: "analyze_table",
|
|
33
36
|
SELECT: "select_combobox",
|
|
34
37
|
VERIFY_PAGE_PATH: "verify_page_path",
|
|
@@ -45,6 +48,8 @@ const Types = {
|
|
|
45
48
|
LOAD_DATA: "load_data",
|
|
46
49
|
SET_INPUT: "set_input",
|
|
47
50
|
WAIT_FOR_TEXT_TO_DISAPPEAR: "wait_for_text_to_disappear",
|
|
51
|
+
VERIFY_ATTRIBUTE: "verify_element_attribute",
|
|
52
|
+
VERIFY_TEXT_WITH_RELATION: "verify_text_with_relation",
|
|
48
53
|
};
|
|
49
54
|
export const apps = {};
|
|
50
55
|
class StableBrowser {
|
|
@@ -58,6 +63,7 @@ class StableBrowser {
|
|
|
58
63
|
networkLogger = null;
|
|
59
64
|
configuration = null;
|
|
60
65
|
appName = "main";
|
|
66
|
+
tags = null;
|
|
61
67
|
constructor(browser, page, logger = null, context = null, world = null) {
|
|
62
68
|
this.browser = browser;
|
|
63
69
|
this.page = page;
|
|
@@ -88,17 +94,17 @@ class StableBrowser {
|
|
|
88
94
|
catch (e) {
|
|
89
95
|
this.logger.error("unable to read ai_config.json");
|
|
90
96
|
}
|
|
91
|
-
context.pageLoading = { status: false };
|
|
92
|
-
context.pages = [this.page];
|
|
93
97
|
const logFolder = path.join(this.project_path, "logs", "web");
|
|
94
98
|
this.world = world;
|
|
99
|
+
context.pages = [this.page];
|
|
100
|
+
context.pageLoading = { status: false };
|
|
95
101
|
this.registerEventListeners(this.context);
|
|
96
102
|
registerNetworkEvents(this.world, this, this.context, this.page);
|
|
97
103
|
registerDownloadEvent(this.page, this.world, this.context);
|
|
98
104
|
}
|
|
99
105
|
registerEventListeners(context) {
|
|
100
106
|
this.registerConsoleLogListener(this.page, context);
|
|
101
|
-
this.registerRequestListener(this.page, context, this.webLogFile);
|
|
107
|
+
// this.registerRequestListener(this.page, context, this.webLogFile);
|
|
102
108
|
if (!context.pageLoading) {
|
|
103
109
|
context.pageLoading = { status: false };
|
|
104
110
|
}
|
|
@@ -143,7 +149,7 @@ class StableBrowser {
|
|
|
143
149
|
if (this.appName === appName) {
|
|
144
150
|
return;
|
|
145
151
|
}
|
|
146
|
-
let
|
|
152
|
+
let navigate = false;
|
|
147
153
|
if (!apps[appName]) {
|
|
148
154
|
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this, -1, this.context.reportFolder);
|
|
149
155
|
newContextCreated = true;
|
|
@@ -154,32 +160,15 @@ class StableBrowser {
|
|
|
154
160
|
};
|
|
155
161
|
}
|
|
156
162
|
const tempContext = {};
|
|
157
|
-
|
|
158
|
-
|
|
163
|
+
_copyContext(this, tempContext);
|
|
164
|
+
_copyContext(apps[appName], this);
|
|
159
165
|
apps[this.appName] = tempContext;
|
|
160
166
|
this.appName = appName;
|
|
161
|
-
if (
|
|
162
|
-
this.registerEventListeners(this.context);
|
|
167
|
+
if (navigate) {
|
|
163
168
|
await this.goto(this.context.environment.baseUrl);
|
|
164
169
|
await this.waitForPageLoad();
|
|
165
170
|
}
|
|
166
171
|
}
|
|
167
|
-
_copyContext(from, to) {
|
|
168
|
-
to.browser = from.browser;
|
|
169
|
-
to.page = from.page;
|
|
170
|
-
to.context = from.context;
|
|
171
|
-
}
|
|
172
|
-
getWebLogFile(logFolder) {
|
|
173
|
-
if (!fs.existsSync(logFolder)) {
|
|
174
|
-
fs.mkdirSync(logFolder, { recursive: true });
|
|
175
|
-
}
|
|
176
|
-
let nextIndex = 1;
|
|
177
|
-
while (fs.existsSync(path.join(logFolder, nextIndex.toString() + ".json"))) {
|
|
178
|
-
nextIndex++;
|
|
179
|
-
}
|
|
180
|
-
const fileName = nextIndex + ".json";
|
|
181
|
-
return path.join(logFolder, fileName);
|
|
182
|
-
}
|
|
183
172
|
registerConsoleLogListener(page, context) {
|
|
184
173
|
if (!this.context.webLogger) {
|
|
185
174
|
this.context.webLogger = [];
|
|
@@ -243,55 +232,48 @@ class StableBrowser {
|
|
|
243
232
|
// async closeUnexpectedPopups() {
|
|
244
233
|
// await closeUnexpectedPopups(this.page);
|
|
245
234
|
// }
|
|
246
|
-
async goto(url) {
|
|
235
|
+
async goto(url, world = null) {
|
|
247
236
|
if (!url.startsWith("http")) {
|
|
248
237
|
url = "https://" + url;
|
|
249
238
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
239
|
+
const state = {
|
|
240
|
+
value: url,
|
|
241
|
+
world: world,
|
|
242
|
+
type: Types.NAVIGATE,
|
|
243
|
+
text: `Navigate Page to: ${url}`,
|
|
244
|
+
operation: "goto",
|
|
245
|
+
log: "***** navigate page to " + url + " *****\n",
|
|
246
|
+
info: {},
|
|
247
|
+
locate: false,
|
|
248
|
+
scroll: false,
|
|
249
|
+
screenshot: false,
|
|
250
|
+
highlight: false,
|
|
251
|
+
};
|
|
252
|
+
try {
|
|
253
|
+
await _preCommand(state, this, world);
|
|
254
|
+
await this.page.goto(url, {
|
|
255
|
+
timeout: 60000,
|
|
256
|
+
});
|
|
257
|
+
await _screenshot(state, this);
|
|
257
258
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
// remove the _ prefix
|
|
262
|
-
regValue = key.substring(1);
|
|
263
|
-
}
|
|
264
|
-
text = text.replaceAll(new RegExp("{" + regValue + "}", "g"), _params[key]);
|
|
259
|
+
catch (error) {
|
|
260
|
+
console.error("Error on goto", error);
|
|
261
|
+
_commandError(state, error, this);
|
|
265
262
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
_fixLocatorUsingParams(locator, _params) {
|
|
269
|
-
// check if not null
|
|
270
|
-
if (!locator) {
|
|
271
|
-
return locator;
|
|
263
|
+
finally {
|
|
264
|
+
_commandFinally(state, this);
|
|
272
265
|
}
|
|
273
|
-
// clone the locator
|
|
274
|
-
locator = JSON.parse(JSON.stringify(locator));
|
|
275
|
-
this.scanAndManipulate(locator, _params);
|
|
276
|
-
return locator;
|
|
277
266
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
}
|
|
287
|
-
else if (this._isObject(currentObj[key])) {
|
|
288
|
-
// Recursively scan nested objects
|
|
289
|
-
this.scanAndManipulate(currentObj[key], _params);
|
|
267
|
+
async _getLocator(locator, scope, _params) {
|
|
268
|
+
locator = _fixLocatorUsingParams(locator, _params);
|
|
269
|
+
// locator = await this._replaceWithLocalData(locator);
|
|
270
|
+
for (let key in locator) {
|
|
271
|
+
if (typeof locator[key] !== "string")
|
|
272
|
+
continue;
|
|
273
|
+
if (locator[key].includes("{{") && locator[key].includes("}}")) {
|
|
274
|
+
locator[key] = await this._replaceWithLocalData(locator[key], this.world);
|
|
290
275
|
}
|
|
291
276
|
}
|
|
292
|
-
}
|
|
293
|
-
_getLocator(locator, scope, _params) {
|
|
294
|
-
locator = this._fixLocatorUsingParams(locator, _params);
|
|
295
277
|
let locatorReturn;
|
|
296
278
|
if (locator.role) {
|
|
297
279
|
if (locator.role[1].nameReg) {
|
|
@@ -299,7 +281,7 @@ class StableBrowser {
|
|
|
299
281
|
delete locator.role[1].nameReg;
|
|
300
282
|
}
|
|
301
283
|
// if (locator.role[1].name) {
|
|
302
|
-
// locator.role[1].name =
|
|
284
|
+
// locator.role[1].name = _fixUsingParams(locator.role[1].name, _params);
|
|
303
285
|
// }
|
|
304
286
|
locatorReturn = scope.getByRole(locator.role[0], locator.role[1]);
|
|
305
287
|
}
|
|
@@ -342,140 +324,54 @@ class StableBrowser {
|
|
|
342
324
|
if (css && css.locator) {
|
|
343
325
|
css = css.locator;
|
|
344
326
|
}
|
|
345
|
-
let result = await this._locateElementByText(scope,
|
|
327
|
+
let result = await this._locateElementByText(scope, _fixUsingParams(text, _params), "*:not(script, style, head)", false, false, true, _params);
|
|
346
328
|
if (result.elementCount === 0) {
|
|
347
329
|
return;
|
|
348
330
|
}
|
|
349
|
-
let textElementCss = "[data-blinq-id
|
|
331
|
+
let textElementCss = "[data-blinq-id-" + result.randomToken + "]";
|
|
350
332
|
// css climb to parent element
|
|
351
333
|
const climbArray = [];
|
|
352
334
|
for (let i = 0; i < climb; i++) {
|
|
353
335
|
climbArray.push("..");
|
|
354
336
|
}
|
|
355
337
|
let climbXpath = "xpath=" + climbArray.join("/");
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
return
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
338
|
+
let resultCss = textElementCss + " >> " + climbXpath;
|
|
339
|
+
if (css) {
|
|
340
|
+
resultCss = resultCss + " >> " + css;
|
|
341
|
+
}
|
|
342
|
+
return resultCss;
|
|
343
|
+
}
|
|
344
|
+
async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, ignoreCase = true, _params) {
|
|
345
|
+
const query = _convertToRegexQuery(text1, regex1, !partial1, ignoreCase);
|
|
346
|
+
const locator = scope.locator(query);
|
|
347
|
+
const count = await locator.count();
|
|
348
|
+
if (!tag1) {
|
|
349
|
+
tag1 = "*";
|
|
350
|
+
}
|
|
351
|
+
const randomToken = Math.random().toString(36).substring(7);
|
|
352
|
+
let tagCount = 0;
|
|
353
|
+
for (let i = 0; i < count; i++) {
|
|
354
|
+
const element = locator.nth(i);
|
|
355
|
+
// check if the tag matches
|
|
356
|
+
if (!(await element.evaluate((el, [tag, randomToken]) => {
|
|
357
|
+
if (!tag.startsWith("*")) {
|
|
358
|
+
if (el.tagName.toLowerCase() !== tag) {
|
|
359
|
+
return false;
|
|
366
360
|
}
|
|
367
|
-
currentNode = currentNode.parentNode;
|
|
368
|
-
}
|
|
369
|
-
return false;
|
|
370
|
-
}
|
|
371
|
-
document.isParent = isParent;
|
|
372
|
-
function getRegex(str) {
|
|
373
|
-
const match = str.match(/^\/(.*?)\/([gimuy]*)$/);
|
|
374
|
-
if (!match) {
|
|
375
|
-
return null;
|
|
376
|
-
}
|
|
377
|
-
let [_, pattern, flags] = match;
|
|
378
|
-
return new RegExp(pattern, flags);
|
|
379
|
-
}
|
|
380
|
-
document.getRegex = getRegex;
|
|
381
|
-
function collectAllShadowDomElements(element, result = []) {
|
|
382
|
-
// Check and add the element if it has a shadow root
|
|
383
|
-
if (element.shadowRoot) {
|
|
384
|
-
result.push(element);
|
|
385
|
-
// Also search within the shadow root
|
|
386
|
-
document.collectAllShadowDomElements(element.shadowRoot, result);
|
|
387
|
-
}
|
|
388
|
-
// Iterate over child nodes
|
|
389
|
-
element.childNodes.forEach((child) => {
|
|
390
|
-
// Recursively call the function for each child node
|
|
391
|
-
document.collectAllShadowDomElements(child, result);
|
|
392
|
-
});
|
|
393
|
-
return result;
|
|
394
|
-
}
|
|
395
|
-
document.collectAllShadowDomElements = collectAllShadowDomElements;
|
|
396
|
-
if (!tag) {
|
|
397
|
-
tag = "*:not(script, style, head)";
|
|
398
|
-
}
|
|
399
|
-
let regexpSearch = document.getRegex(text);
|
|
400
|
-
if (regexpSearch) {
|
|
401
|
-
regex = true;
|
|
402
|
-
}
|
|
403
|
-
let elements = Array.from(document.querySelectorAll(tag));
|
|
404
|
-
let shadowHosts = [];
|
|
405
|
-
document.collectAllShadowDomElements(document, shadowHosts);
|
|
406
|
-
for (let i = 0; i < shadowHosts.length; i++) {
|
|
407
|
-
let shadowElement = shadowHosts[i].shadowRoot;
|
|
408
|
-
if (!shadowElement) {
|
|
409
|
-
console.log("shadowElement is null, for host " + shadowHosts[i]);
|
|
410
|
-
continue;
|
|
411
|
-
}
|
|
412
|
-
let shadowElements = Array.from(shadowElement.querySelectorAll(tag));
|
|
413
|
-
elements = elements.concat(shadowElements);
|
|
414
|
-
}
|
|
415
|
-
let randomToken = null;
|
|
416
|
-
const foundElements = [];
|
|
417
|
-
if (regex) {
|
|
418
|
-
if (!regexpSearch) {
|
|
419
|
-
regexpSearch = new RegExp(text, "im");
|
|
420
361
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
if ((element.innerText && regexpSearch.test(element.innerText)) ||
|
|
424
|
-
(element.value && regexpSearch.test(element.value))) {
|
|
425
|
-
foundElements.push(element);
|
|
426
|
-
}
|
|
362
|
+
if (!el.setAttribute) {
|
|
363
|
+
el = el.parentElement;
|
|
427
364
|
}
|
|
365
|
+
el.setAttribute("data-blinq-id-" + randomToken, "");
|
|
366
|
+
return true;
|
|
367
|
+
}, [tag1, randomToken]))) {
|
|
368
|
+
continue;
|
|
428
369
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
const element = elements[i];
|
|
433
|
-
if (partial) {
|
|
434
|
-
if ((element.innerText && element.innerText.toLowerCase().trim().includes(text.toLowerCase())) ||
|
|
435
|
-
(element.value && element.value.toLowerCase().includes(text.toLowerCase()))) {
|
|
436
|
-
foundElements.push(element);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
else {
|
|
440
|
-
if ((element.innerText && element.innerText.trim() === text) ||
|
|
441
|
-
(element.value && element.value === text)) {
|
|
442
|
-
foundElements.push(element);
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
let noChildElements = [];
|
|
448
|
-
for (let i = 0; i < foundElements.length; i++) {
|
|
449
|
-
let element = foundElements[i];
|
|
450
|
-
let hasChild = false;
|
|
451
|
-
for (let j = 0; j < foundElements.length; j++) {
|
|
452
|
-
if (i === j) {
|
|
453
|
-
continue;
|
|
454
|
-
}
|
|
455
|
-
if (isParent(element, foundElements[j])) {
|
|
456
|
-
hasChild = true;
|
|
457
|
-
break;
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
if (!hasChild) {
|
|
461
|
-
noChildElements.push(element);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
let elementCount = 0;
|
|
465
|
-
if (noChildElements.length > 0) {
|
|
466
|
-
for (let i = 0; i < noChildElements.length; i++) {
|
|
467
|
-
if (randomToken === null) {
|
|
468
|
-
randomToken = Math.random().toString(36).substring(7);
|
|
469
|
-
}
|
|
470
|
-
let element = noChildElements[i];
|
|
471
|
-
element.setAttribute("data-blinq-id", "blinq-id-" + randomToken);
|
|
472
|
-
elementCount++;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
return { elementCount: elementCount, randomToken: randomToken };
|
|
476
|
-
}, [text1, tag1, regex1, partial1]);
|
|
370
|
+
tagCount++;
|
|
371
|
+
}
|
|
372
|
+
return { elementCount: tagCount, randomToken };
|
|
477
373
|
}
|
|
478
|
-
async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
|
|
374
|
+
async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true, allowDisabled = false) {
|
|
479
375
|
if (!info) {
|
|
480
376
|
info = {};
|
|
481
377
|
}
|
|
@@ -484,10 +380,13 @@ class StableBrowser {
|
|
|
484
380
|
}
|
|
485
381
|
if (!info.log) {
|
|
486
382
|
info.log = "";
|
|
383
|
+
info.locatorLog = new LocatorLog(selectorHierarchy);
|
|
487
384
|
}
|
|
488
385
|
let locatorSearch = selectorHierarchy[index];
|
|
386
|
+
let originalLocatorSearch = "";
|
|
489
387
|
try {
|
|
490
|
-
|
|
388
|
+
originalLocatorSearch = _fixUsingParams(JSON.stringify(locatorSearch), _params);
|
|
389
|
+
locatorSearch = JSON.parse(originalLocatorSearch);
|
|
491
390
|
}
|
|
492
391
|
catch (e) {
|
|
493
392
|
console.error(e);
|
|
@@ -495,30 +394,31 @@ class StableBrowser {
|
|
|
495
394
|
//info.log += "searching for locator " + JSON.stringify(locatorSearch) + "\n";
|
|
496
395
|
let locator = null;
|
|
497
396
|
if (locatorSearch.climb && locatorSearch.climb >= 0) {
|
|
498
|
-
|
|
397
|
+
const replacedText = await this._replaceWithLocalData(locatorSearch.text, this.world);
|
|
398
|
+
let locatorString = await this._locateElmentByTextClimbCss(scope, replacedText, locatorSearch.climb, locatorSearch.css, _params);
|
|
499
399
|
if (!locatorString) {
|
|
500
400
|
info.failCause.textNotFound = true;
|
|
501
401
|
info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
|
|
502
402
|
return;
|
|
503
403
|
}
|
|
504
|
-
locator = this._getLocator({ css: locatorString }, scope, _params);
|
|
404
|
+
locator = await this._getLocator({ css: locatorString }, scope, _params);
|
|
505
405
|
}
|
|
506
406
|
else if (locatorSearch.text) {
|
|
507
|
-
let text =
|
|
508
|
-
let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
|
|
407
|
+
let text = _fixUsingParams(locatorSearch.text, _params);
|
|
408
|
+
let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, true, _params);
|
|
509
409
|
if (result.elementCount === 0) {
|
|
510
410
|
info.failCause.textNotFound = true;
|
|
511
411
|
info.failCause.lastError = "failed to locate element by text: " + text;
|
|
512
412
|
return;
|
|
513
413
|
}
|
|
514
|
-
locatorSearch.css = "[data-blinq-id
|
|
414
|
+
locatorSearch.css = "[data-blinq-id-" + result.randomToken + "]";
|
|
515
415
|
if (locatorSearch.childCss) {
|
|
516
416
|
locatorSearch.css = locatorSearch.css + " " + locatorSearch.childCss;
|
|
517
417
|
}
|
|
518
|
-
locator = this._getLocator(locatorSearch, scope, _params);
|
|
418
|
+
locator = await this._getLocator(locatorSearch, scope, _params);
|
|
519
419
|
}
|
|
520
420
|
else {
|
|
521
|
-
locator = this._getLocator(locatorSearch, scope, _params);
|
|
421
|
+
locator = await this._getLocator(locatorSearch, scope, _params);
|
|
522
422
|
}
|
|
523
423
|
// let cssHref = false;
|
|
524
424
|
// if (locatorSearch.css && locatorSearch.css.includes("href=")) {
|
|
@@ -531,18 +431,27 @@ class StableBrowser {
|
|
|
531
431
|
//info.log += "total elements found " + count + "\n";
|
|
532
432
|
//let visibleCount = 0;
|
|
533
433
|
let visibleLocator = null;
|
|
534
|
-
if (locatorSearch.index && locatorSearch.index < count) {
|
|
434
|
+
if (typeof locatorSearch.index === "number" && locatorSearch.index < count) {
|
|
535
435
|
foundLocators.push(locator.nth(locatorSearch.index));
|
|
436
|
+
if (info.locatorLog) {
|
|
437
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND");
|
|
438
|
+
}
|
|
536
439
|
return;
|
|
537
440
|
}
|
|
441
|
+
if (info.locatorLog && count === 0) {
|
|
442
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "NOT_FOUND");
|
|
443
|
+
}
|
|
538
444
|
for (let j = 0; j < count; j++) {
|
|
539
445
|
let visible = await locator.nth(j).isVisible();
|
|
540
446
|
const enabled = await locator.nth(j).isEnabled();
|
|
541
447
|
if (!visibleOnly) {
|
|
542
448
|
visible = true;
|
|
543
449
|
}
|
|
544
|
-
if (visible && enabled) {
|
|
450
|
+
if (visible && (allowDisabled || enabled)) {
|
|
545
451
|
foundLocators.push(locator.nth(j));
|
|
452
|
+
if (info.locatorLog) {
|
|
453
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND");
|
|
454
|
+
}
|
|
546
455
|
}
|
|
547
456
|
else {
|
|
548
457
|
info.failCause.visible = visible;
|
|
@@ -550,8 +459,16 @@ class StableBrowser {
|
|
|
550
459
|
if (!info.printMessages) {
|
|
551
460
|
info.printMessages = {};
|
|
552
461
|
}
|
|
462
|
+
if (info.locatorLog && !visible) {
|
|
463
|
+
info.failCause.lastError = "element " + originalLocatorSearch + " is not visible";
|
|
464
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND_NOT_VISIBLE");
|
|
465
|
+
}
|
|
466
|
+
if (info.locatorLog && !enabled) {
|
|
467
|
+
info.failCause.lastError = "element " + originalLocatorSearch + " is disabled, ";
|
|
468
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND_NOT_ENABLED");
|
|
469
|
+
}
|
|
553
470
|
if (!info.printMessages[j.toString()]) {
|
|
554
|
-
info.log += "element " + locator + " visible " + visible + " enabled " + enabled + "\n";
|
|
471
|
+
//info.log += "element " + locator + " visible " + visible + " enabled " + enabled + "\n";
|
|
555
472
|
info.printMessages[j.toString()] = true;
|
|
556
473
|
}
|
|
557
474
|
}
|
|
@@ -567,7 +484,7 @@ class StableBrowser {
|
|
|
567
484
|
if (!info) {
|
|
568
485
|
info = {};
|
|
569
486
|
}
|
|
570
|
-
info.log += "scan for popup handlers" + "\n";
|
|
487
|
+
//info.log += "scan for popup handlers" + "\n";
|
|
571
488
|
const handlerGroup = [];
|
|
572
489
|
for (let i = 0; i < this.configuration.popupHandlers.length; i++) {
|
|
573
490
|
handlerGroup.push(this.configuration.popupHandlers[i].locator);
|
|
@@ -594,16 +511,28 @@ class StableBrowser {
|
|
|
594
511
|
}
|
|
595
512
|
if (result.foundElements.length > 0) {
|
|
596
513
|
let dialogCloseLocator = result.foundElements[0].locator;
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
514
|
+
try {
|
|
515
|
+
await scope?.evaluate(() => {
|
|
516
|
+
window.__isClosingPopups = true;
|
|
517
|
+
});
|
|
518
|
+
await dialogCloseLocator.click();
|
|
519
|
+
// wait for the dialog to close
|
|
520
|
+
await dialogCloseLocator.waitFor({ state: "hidden" });
|
|
521
|
+
}
|
|
522
|
+
catch (e) {
|
|
523
|
+
}
|
|
524
|
+
finally {
|
|
525
|
+
await scope?.evaluate(() => {
|
|
526
|
+
window.__isClosingPopups = false;
|
|
527
|
+
});
|
|
528
|
+
}
|
|
600
529
|
return { rerun: true };
|
|
601
530
|
}
|
|
602
531
|
}
|
|
603
532
|
}
|
|
604
533
|
return { rerun: false };
|
|
605
534
|
}
|
|
606
|
-
async _locate(selectors, info, _params, timeout) {
|
|
535
|
+
async _locate(selectors, info, _params, timeout, allowDisabled = false) {
|
|
607
536
|
if (!timeout) {
|
|
608
537
|
timeout = 30000;
|
|
609
538
|
}
|
|
@@ -613,7 +542,7 @@ class StableBrowser {
|
|
|
613
542
|
let selector = selectors.locators[j];
|
|
614
543
|
info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
|
|
615
544
|
}
|
|
616
|
-
let element = await this._locate_internal(selectors, info, _params, timeout);
|
|
545
|
+
let element = await this._locate_internal(selectors, info, _params, timeout, allowDisabled);
|
|
617
546
|
if (!element.rerun) {
|
|
618
547
|
return element;
|
|
619
548
|
}
|
|
@@ -626,6 +555,7 @@ class StableBrowser {
|
|
|
626
555
|
info.failCause = {};
|
|
627
556
|
info.log = "";
|
|
628
557
|
}
|
|
558
|
+
let startTime = Date.now();
|
|
629
559
|
let scope = this.page;
|
|
630
560
|
if (selectors.frame) {
|
|
631
561
|
return selectors.frame;
|
|
@@ -656,9 +586,11 @@ class StableBrowser {
|
|
|
656
586
|
}
|
|
657
587
|
return framescope;
|
|
658
588
|
};
|
|
589
|
+
let fLocator = null;
|
|
659
590
|
while (true) {
|
|
660
591
|
let frameFound = false;
|
|
661
592
|
if (selectors.nestFrmLoc) {
|
|
593
|
+
fLocator = selectors.nestFrmLoc;
|
|
662
594
|
scope = await findFrame(selectors.nestFrmLoc, scope);
|
|
663
595
|
frameFound = true;
|
|
664
596
|
break;
|
|
@@ -667,6 +599,7 @@ class StableBrowser {
|
|
|
667
599
|
for (let i = 0; i < selectors.frameLocators.length; i++) {
|
|
668
600
|
let frameLocator = selectors.frameLocators[i];
|
|
669
601
|
if (frameLocator.css) {
|
|
602
|
+
fLocator = frameLocator.css;
|
|
670
603
|
scope = scope.frameLocator(frameLocator.css);
|
|
671
604
|
frameFound = true;
|
|
672
605
|
break;
|
|
@@ -674,11 +607,15 @@ class StableBrowser {
|
|
|
674
607
|
}
|
|
675
608
|
}
|
|
676
609
|
if (!frameFound && selectors.iframe_src) {
|
|
610
|
+
fLocator = selectors.iframe_src;
|
|
677
611
|
scope = this.page.frame({ url: selectors.iframe_src });
|
|
678
612
|
}
|
|
679
613
|
if (!scope) {
|
|
680
|
-
info
|
|
681
|
-
|
|
614
|
+
if (info && info.locatorLog) {
|
|
615
|
+
info.locatorLog.setLocatorSearchStatus("frame-" + fLocator, "NOT_FOUND");
|
|
616
|
+
}
|
|
617
|
+
//info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
|
|
618
|
+
if (Date.now() - startTime > timeout) {
|
|
682
619
|
info.failCause.iframeNotFound = true;
|
|
683
620
|
info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
|
|
684
621
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
@@ -686,6 +623,9 @@ class StableBrowser {
|
|
|
686
623
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
687
624
|
}
|
|
688
625
|
else {
|
|
626
|
+
if (info && info.locatorLog) {
|
|
627
|
+
info.locatorLog.setLocatorSearchStatus("frame-" + fLocator, "FOUND");
|
|
628
|
+
}
|
|
689
629
|
break;
|
|
690
630
|
}
|
|
691
631
|
}
|
|
@@ -702,16 +642,18 @@ class StableBrowser {
|
|
|
702
642
|
return bodyContent;
|
|
703
643
|
});
|
|
704
644
|
}
|
|
705
|
-
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
645
|
+
async _locate_internal(selectors, info, _params, timeout = 30000, allowDisabled = false) {
|
|
706
646
|
if (!info) {
|
|
707
647
|
info = {};
|
|
708
648
|
info.failCause = {};
|
|
709
649
|
info.log = "";
|
|
650
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
710
651
|
}
|
|
711
652
|
let highPriorityTimeout = 5000;
|
|
712
653
|
let visibleOnlyTimeout = 6000;
|
|
713
|
-
let startTime =
|
|
654
|
+
let startTime = Date.now();
|
|
714
655
|
let locatorsCount = 0;
|
|
656
|
+
let lazy_scroll = false;
|
|
715
657
|
//let arrayMode = Array.isArray(selectors);
|
|
716
658
|
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
717
659
|
let selectorsLocators = null;
|
|
@@ -749,17 +691,17 @@ class StableBrowser {
|
|
|
749
691
|
}
|
|
750
692
|
// info.log += "scanning locators in priority 1" + "\n";
|
|
751
693
|
let onlyPriority3 = selectorsLocators[0].priority === 3;
|
|
752
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["1"], scope, _params, info, visibleOnly);
|
|
694
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["1"], scope, _params, info, visibleOnly, allowDisabled);
|
|
753
695
|
if (result.foundElements.length === 0) {
|
|
754
696
|
// info.log += "scanning locators in priority 2" + "\n";
|
|
755
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["2"], scope, _params, info, visibleOnly);
|
|
697
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["2"], scope, _params, info, visibleOnly, allowDisabled);
|
|
756
698
|
}
|
|
757
699
|
if (result.foundElements.length === 0 && onlyPriority3) {
|
|
758
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly);
|
|
700
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly, allowDisabled);
|
|
759
701
|
}
|
|
760
702
|
else {
|
|
761
703
|
if (result.foundElements.length === 0 && !highPriorityOnly) {
|
|
762
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly);
|
|
704
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly, allowDisabled);
|
|
763
705
|
}
|
|
764
706
|
}
|
|
765
707
|
let foundElements = result.foundElements;
|
|
@@ -800,26 +742,38 @@ class StableBrowser {
|
|
|
800
742
|
return maxCountElement.locator;
|
|
801
743
|
}
|
|
802
744
|
}
|
|
803
|
-
if (
|
|
745
|
+
if (Date.now() - startTime > timeout) {
|
|
804
746
|
break;
|
|
805
747
|
}
|
|
806
|
-
if (
|
|
807
|
-
info.log += "high priority timeout, will try all elements" + "\n";
|
|
748
|
+
if (Date.now() - startTime > highPriorityTimeout) {
|
|
749
|
+
//info.log += "high priority timeout, will try all elements" + "\n";
|
|
808
750
|
highPriorityOnly = false;
|
|
751
|
+
if (this.configuration && this.configuration.load_all_lazy === true && !lazy_scroll) {
|
|
752
|
+
lazy_scroll = true;
|
|
753
|
+
await scrollPageToLoadLazyElements(this.page);
|
|
754
|
+
}
|
|
809
755
|
}
|
|
810
|
-
if (
|
|
811
|
-
info.log += "visible only timeout, will try all elements" + "\n";
|
|
756
|
+
if (Date.now() - startTime > visibleOnlyTimeout) {
|
|
757
|
+
//info.log += "visible only timeout, will try all elements" + "\n";
|
|
812
758
|
visibleOnly = false;
|
|
813
759
|
}
|
|
814
760
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
815
761
|
}
|
|
816
762
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
817
|
-
info.
|
|
763
|
+
// if (info.locatorLog) {
|
|
764
|
+
// const lines = info.locatorLog.toString().split("\n");
|
|
765
|
+
// for (let line of lines) {
|
|
766
|
+
// this.logger.debug(line);
|
|
767
|
+
// }
|
|
768
|
+
// }
|
|
769
|
+
//info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
|
|
818
770
|
info.failCause.locatorNotFound = true;
|
|
819
|
-
info
|
|
771
|
+
if (!info?.failCause?.lastError) {
|
|
772
|
+
info.failCause.lastError = "failed to locate unique element";
|
|
773
|
+
}
|
|
820
774
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
821
775
|
}
|
|
822
|
-
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
776
|
+
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly, allowDisabled = false) {
|
|
823
777
|
let foundElements = [];
|
|
824
778
|
const result = {
|
|
825
779
|
foundElements: foundElements,
|
|
@@ -827,14 +781,15 @@ class StableBrowser {
|
|
|
827
781
|
for (let i = 0; i < locatorsGroup.length; i++) {
|
|
828
782
|
let foundLocators = [];
|
|
829
783
|
try {
|
|
830
|
-
await this._collectLocatorInformation(locatorsGroup, i, scope, foundLocators, _params, info, visibleOnly);
|
|
784
|
+
await this._collectLocatorInformation(locatorsGroup, i, scope, foundLocators, _params, info, visibleOnly, allowDisabled);
|
|
831
785
|
}
|
|
832
786
|
catch (e) {
|
|
833
|
-
this
|
|
834
|
-
this.logger.debug(
|
|
787
|
+
// this call can fail it the browser is navigating
|
|
788
|
+
// this.logger.debug("unable to use locator " + JSON.stringify(locatorsGroup[i]));
|
|
789
|
+
// this.logger.debug(e);
|
|
835
790
|
foundLocators = [];
|
|
836
791
|
try {
|
|
837
|
-
await this._collectLocatorInformation(locatorsGroup, i, this.page, foundLocators, _params, info, visibleOnly);
|
|
792
|
+
await this._collectLocatorInformation(locatorsGroup, i, this.page, foundLocators, _params, info, visibleOnly, allowDisabled);
|
|
838
793
|
}
|
|
839
794
|
catch (e) {
|
|
840
795
|
this.logger.info("unable to use locator (second try) " + JSON.stringify(locatorsGroup[i]));
|
|
@@ -850,11 +805,27 @@ class StableBrowser {
|
|
|
850
805
|
}
|
|
851
806
|
if (foundLocators.length > 1) {
|
|
852
807
|
info.failCause.foundMultiple = true;
|
|
808
|
+
if (info.locatorLog) {
|
|
809
|
+
info.locatorLog.setLocatorSearchStatus(JSON.stringify(locatorsGroup[i]), "FOUND_NOT_UNIQUE");
|
|
810
|
+
}
|
|
853
811
|
}
|
|
854
812
|
}
|
|
855
813
|
return result;
|
|
856
814
|
}
|
|
857
815
|
async simpleClick(elementDescription, _params, options = {}, world = null) {
|
|
816
|
+
const state = {
|
|
817
|
+
locate: false,
|
|
818
|
+
scroll: false,
|
|
819
|
+
highlight: false,
|
|
820
|
+
_params,
|
|
821
|
+
options,
|
|
822
|
+
world,
|
|
823
|
+
type: Types.CLICK,
|
|
824
|
+
text: "Click element",
|
|
825
|
+
operation: "simpleClick",
|
|
826
|
+
log: "***** click on " + elementDescription + " *****\n",
|
|
827
|
+
};
|
|
828
|
+
_preCommand(state, this, world);
|
|
858
829
|
const startTime = Date.now();
|
|
859
830
|
let timeout = 30000;
|
|
860
831
|
if (options && options.timeout) {
|
|
@@ -879,13 +850,31 @@ class StableBrowser {
|
|
|
879
850
|
catch (e) {
|
|
880
851
|
if (performance.now() - startTime > timeout) {
|
|
881
852
|
// throw e;
|
|
882
|
-
|
|
853
|
+
try {
|
|
854
|
+
await _commandError(state, "timeout looking for " + elementDescription, this);
|
|
855
|
+
}
|
|
856
|
+
finally {
|
|
857
|
+
_commandFinally(state, this);
|
|
858
|
+
}
|
|
883
859
|
}
|
|
884
860
|
}
|
|
885
861
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
886
862
|
}
|
|
887
863
|
}
|
|
888
864
|
async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
|
|
865
|
+
const state = {
|
|
866
|
+
locate: false,
|
|
867
|
+
scroll: false,
|
|
868
|
+
highlight: false,
|
|
869
|
+
_params,
|
|
870
|
+
options,
|
|
871
|
+
world,
|
|
872
|
+
type: Types.FILL,
|
|
873
|
+
text: "Fill element",
|
|
874
|
+
operation: "simpleClickType",
|
|
875
|
+
log: "***** click type on " + elementDescription + " *****\n",
|
|
876
|
+
};
|
|
877
|
+
_preCommand(state, this, world);
|
|
889
878
|
const startTime = Date.now();
|
|
890
879
|
let timeout = 30000;
|
|
891
880
|
if (options && options.timeout) {
|
|
@@ -910,7 +899,12 @@ class StableBrowser {
|
|
|
910
899
|
catch (e) {
|
|
911
900
|
if (performance.now() - startTime > timeout) {
|
|
912
901
|
// throw e;
|
|
913
|
-
|
|
902
|
+
try {
|
|
903
|
+
await _commandError(state, "timeout looking for " + elementDescription, this);
|
|
904
|
+
}
|
|
905
|
+
finally {
|
|
906
|
+
_commandFinally(state, this);
|
|
907
|
+
}
|
|
914
908
|
}
|
|
915
909
|
}
|
|
916
910
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
@@ -928,10 +922,10 @@ class StableBrowser {
|
|
|
928
922
|
log: "***** click on " + selectors.element_name + " *****\n",
|
|
929
923
|
};
|
|
930
924
|
try {
|
|
931
|
-
await _preCommand(state, this);
|
|
932
|
-
if (state.options && state.options.context) {
|
|
933
|
-
|
|
934
|
-
}
|
|
925
|
+
await _preCommand(state, this, world);
|
|
926
|
+
// if (state.options && state.options.context) {
|
|
927
|
+
// state.selectors.locators[0].text = state.options.context;
|
|
928
|
+
// }
|
|
935
929
|
try {
|
|
936
930
|
await state.element.click();
|
|
937
931
|
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -964,14 +958,20 @@ class StableBrowser {
|
|
|
964
958
|
log: "***** check " + selectors.element_name + " *****\n",
|
|
965
959
|
};
|
|
966
960
|
try {
|
|
967
|
-
await _preCommand(state, this);
|
|
961
|
+
await _preCommand(state, this, world);
|
|
968
962
|
state.info.checked = checked;
|
|
969
963
|
// let element = await this._locate(selectors, info, _params);
|
|
970
964
|
// ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
971
965
|
try {
|
|
972
|
-
|
|
966
|
+
if (world && world.screenshot && !world.screenshotPath) {
|
|
967
|
+
// console.log(`Highlighting while running from recorder`);
|
|
968
|
+
await this._highlightElements(element);
|
|
969
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
970
|
+
await this._unHighlightElements(element);
|
|
971
|
+
}
|
|
973
972
|
await state.element.setChecked(checked);
|
|
974
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
973
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
974
|
+
// await this._unHighlightElements(element);
|
|
975
975
|
}
|
|
976
976
|
catch (e) {
|
|
977
977
|
if (e.message && e.message.includes("did not change its state")) {
|
|
@@ -1007,9 +1007,10 @@ class StableBrowser {
|
|
|
1007
1007
|
log: "***** hover " + selectors.element_name + " *****\n",
|
|
1008
1008
|
};
|
|
1009
1009
|
try {
|
|
1010
|
-
await _preCommand(state, this);
|
|
1010
|
+
await _preCommand(state, this, world);
|
|
1011
1011
|
try {
|
|
1012
1012
|
await state.element.hover();
|
|
1013
|
+
await _screenshot(state, this);
|
|
1013
1014
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1014
1015
|
}
|
|
1015
1016
|
catch (e) {
|
|
@@ -1017,9 +1018,10 @@ class StableBrowser {
|
|
|
1017
1018
|
state.info.log += "hover failed, will try again" + "\n";
|
|
1018
1019
|
state.element = await this._locate(selectors, state.info, _params);
|
|
1019
1020
|
await state.element.hover({ timeout: 10000 });
|
|
1021
|
+
await _screenshot(state, this);
|
|
1020
1022
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1021
1023
|
}
|
|
1022
|
-
await _screenshot(state, this);
|
|
1024
|
+
// await _screenshot(state, this);
|
|
1023
1025
|
await this.waitForPageLoad();
|
|
1024
1026
|
return state.info;
|
|
1025
1027
|
}
|
|
@@ -1046,7 +1048,7 @@ class StableBrowser {
|
|
|
1046
1048
|
log: "***** select option " + selectors.element_name + " *****\n",
|
|
1047
1049
|
};
|
|
1048
1050
|
try {
|
|
1049
|
-
await _preCommand(state, this);
|
|
1051
|
+
await _preCommand(state, this, world);
|
|
1050
1052
|
try {
|
|
1051
1053
|
await state.element.selectOption(values);
|
|
1052
1054
|
}
|
|
@@ -1080,7 +1082,7 @@ class StableBrowser {
|
|
|
1080
1082
|
log: "",
|
|
1081
1083
|
};
|
|
1082
1084
|
try {
|
|
1083
|
-
await _preCommand(state, this);
|
|
1085
|
+
await _preCommand(state, this, world);
|
|
1084
1086
|
const valueSegment = state.value.split("&&");
|
|
1085
1087
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1086
1088
|
if (i > 0) {
|
|
@@ -1123,7 +1125,7 @@ class StableBrowser {
|
|
|
1123
1125
|
log: "***** set input value " + selectors.element_name + " *****\n",
|
|
1124
1126
|
};
|
|
1125
1127
|
try {
|
|
1126
|
-
await _preCommand(state, this);
|
|
1128
|
+
await _preCommand(state, this, world);
|
|
1127
1129
|
let value = await this._replaceWithLocalData(state.value, this);
|
|
1128
1130
|
try {
|
|
1129
1131
|
await state.element.evaluateHandle((el, value) => {
|
|
@@ -1160,7 +1162,7 @@ class StableBrowser {
|
|
|
1160
1162
|
throwError: false,
|
|
1161
1163
|
};
|
|
1162
1164
|
try {
|
|
1163
|
-
await _preCommand(state, this);
|
|
1165
|
+
await _preCommand(state, this, world);
|
|
1164
1166
|
try {
|
|
1165
1167
|
await state.element.click();
|
|
1166
1168
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
@@ -1234,7 +1236,7 @@ class StableBrowser {
|
|
|
1234
1236
|
_value = newValue;
|
|
1235
1237
|
}
|
|
1236
1238
|
try {
|
|
1237
|
-
await _preCommand(state, this);
|
|
1239
|
+
await _preCommand(state, this, world);
|
|
1238
1240
|
state.info.value = _value;
|
|
1239
1241
|
if (options === null || options === undefined || !options.press) {
|
|
1240
1242
|
try {
|
|
@@ -1291,7 +1293,12 @@ class StableBrowser {
|
|
|
1291
1293
|
await this.waitForPageLoad();
|
|
1292
1294
|
}
|
|
1293
1295
|
else if (enter === false) {
|
|
1294
|
-
|
|
1296
|
+
try {
|
|
1297
|
+
await state.element.dispatchEvent("change", null, { timeout: 5000 });
|
|
1298
|
+
}
|
|
1299
|
+
catch (e) {
|
|
1300
|
+
// ignore
|
|
1301
|
+
}
|
|
1295
1302
|
//await this.page.keyboard.press("Tab");
|
|
1296
1303
|
}
|
|
1297
1304
|
else {
|
|
@@ -1322,7 +1329,7 @@ class StableBrowser {
|
|
|
1322
1329
|
log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
|
|
1323
1330
|
};
|
|
1324
1331
|
try {
|
|
1325
|
-
await _preCommand(state, this);
|
|
1332
|
+
await _preCommand(state, this, world);
|
|
1326
1333
|
await state.element.fill(value);
|
|
1327
1334
|
await state.element.dispatchEvent("change");
|
|
1328
1335
|
if (enter) {
|
|
@@ -1348,6 +1355,7 @@ class StableBrowser {
|
|
|
1348
1355
|
let screenshotPath = null;
|
|
1349
1356
|
if (!info.log) {
|
|
1350
1357
|
info.log = "";
|
|
1358
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
1351
1359
|
}
|
|
1352
1360
|
info.operation = "getText";
|
|
1353
1361
|
info.selectors = selectors;
|
|
@@ -1367,9 +1375,19 @@ class StableBrowser {
|
|
|
1367
1375
|
catch (e) {
|
|
1368
1376
|
//ignore
|
|
1369
1377
|
}
|
|
1370
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1378
|
+
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info, element));
|
|
1371
1379
|
try {
|
|
1372
|
-
|
|
1380
|
+
if (world && world.screenshot && !world.screenshotPath) {
|
|
1381
|
+
// console.log(`Highlighting for get text while running from recorder`);
|
|
1382
|
+
this._highlightElements(element)
|
|
1383
|
+
.then(async () => {
|
|
1384
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1385
|
+
this._unhighlightElements(element).then(() => { }
|
|
1386
|
+
// console.log(`Unhighlighting vrtr in recorder is successful`)
|
|
1387
|
+
);
|
|
1388
|
+
})
|
|
1389
|
+
.catch(e);
|
|
1390
|
+
}
|
|
1373
1391
|
const elementText = await element.innerText();
|
|
1374
1392
|
return {
|
|
1375
1393
|
text: elementText,
|
|
@@ -1381,7 +1399,7 @@ class StableBrowser {
|
|
|
1381
1399
|
}
|
|
1382
1400
|
catch (e) {
|
|
1383
1401
|
//await this.closeUnexpectedPopups();
|
|
1384
|
-
this.logger.info("no innerText will use textContent");
|
|
1402
|
+
this.logger.info("no innerText, will use textContent");
|
|
1385
1403
|
const elementText = await element.textContent();
|
|
1386
1404
|
return { text: elementText, screenshotId, screenshotPath, value: value };
|
|
1387
1405
|
}
|
|
@@ -1416,13 +1434,13 @@ class StableBrowser {
|
|
|
1416
1434
|
}
|
|
1417
1435
|
let foundObj = null;
|
|
1418
1436
|
try {
|
|
1419
|
-
await _preCommand(state, this);
|
|
1437
|
+
await _preCommand(state, this, world);
|
|
1420
1438
|
state.info.pattern = pattern;
|
|
1421
1439
|
foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
|
|
1422
1440
|
if (foundObj && foundObj.element) {
|
|
1423
1441
|
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1424
1442
|
}
|
|
1425
|
-
await _screenshot(state, this);
|
|
1443
|
+
await _screenshot(state, this, foundObj.element);
|
|
1426
1444
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1427
1445
|
pattern = pattern.replace("{text}", escapedText);
|
|
1428
1446
|
let regex = new RegExp(pattern, "im");
|
|
@@ -1467,12 +1485,12 @@ class StableBrowser {
|
|
|
1467
1485
|
}
|
|
1468
1486
|
let foundObj = null;
|
|
1469
1487
|
try {
|
|
1470
|
-
await _preCommand(state, this);
|
|
1488
|
+
await _preCommand(state, this, world);
|
|
1471
1489
|
foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
|
|
1472
1490
|
if (foundObj && foundObj.element) {
|
|
1473
1491
|
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1474
1492
|
}
|
|
1475
|
-
await _screenshot(state, this);
|
|
1493
|
+
await _screenshot(state, this, foundObj.element);
|
|
1476
1494
|
const dateAlternatives = findDateAlternatives(text);
|
|
1477
1495
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1478
1496
|
if (dateAlternatives.date) {
|
|
@@ -1661,7 +1679,7 @@ class StableBrowser {
|
|
|
1661
1679
|
}
|
|
1662
1680
|
return data;
|
|
1663
1681
|
}
|
|
1664
|
-
async _screenShot(options = {}, world = null, info = null) {
|
|
1682
|
+
async _screenShot(options = {}, world = null, info = null, focusedElement = null) {
|
|
1665
1683
|
// collect url/path/title
|
|
1666
1684
|
if (info) {
|
|
1667
1685
|
if (!info.title) {
|
|
@@ -1686,13 +1704,11 @@ class StableBrowser {
|
|
|
1686
1704
|
if (!fs.existsSync(world.screenshotPath)) {
|
|
1687
1705
|
fs.mkdirSync(world.screenshotPath, { recursive: true });
|
|
1688
1706
|
}
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
}
|
|
1693
|
-
const screenshotPath = path.join(world.screenshotPath, nextIndex + ".png");
|
|
1707
|
+
// to make sure the path doesn't start with -
|
|
1708
|
+
const uuidStr = "id_" + randomUUID();
|
|
1709
|
+
const screenshotPath = path.join(world.screenshotPath, uuidStr + ".png");
|
|
1694
1710
|
try {
|
|
1695
|
-
await this.takeScreenshot(screenshotPath);
|
|
1711
|
+
await this.takeScreenshot(screenshotPath, focusedElement);
|
|
1696
1712
|
// let buffer = await this.page.screenshot({ timeout: 4000 });
|
|
1697
1713
|
// // save the buffer to the screenshot path asynchrously
|
|
1698
1714
|
// fs.writeFile(screenshotPath, buffer, (err) => {
|
|
@@ -1700,20 +1716,20 @@ class StableBrowser {
|
|
|
1700
1716
|
// this.logger.info("unable to save screenshot " + screenshotPath);
|
|
1701
1717
|
// }
|
|
1702
1718
|
// });
|
|
1719
|
+
result.screenshotId = uuidStr;
|
|
1720
|
+
result.screenshotPath = screenshotPath;
|
|
1721
|
+
if (info && info.box) {
|
|
1722
|
+
// await drawRectangle(screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
|
|
1723
|
+
}
|
|
1703
1724
|
}
|
|
1704
1725
|
catch (e) {
|
|
1705
1726
|
this.logger.info("unable to take screenshot, ignored");
|
|
1706
1727
|
}
|
|
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
1728
|
}
|
|
1713
1729
|
else if (options && options.screenshot) {
|
|
1714
1730
|
result.screenshotPath = options.screenshotPath;
|
|
1715
1731
|
try {
|
|
1716
|
-
await this.takeScreenshot(options.screenshotPath);
|
|
1732
|
+
await this.takeScreenshot(options.screenshotPath, focusedElement);
|
|
1717
1733
|
// let buffer = await this.page.screenshot({ timeout: 4000 });
|
|
1718
1734
|
// // save the buffer to the screenshot path asynchrously
|
|
1719
1735
|
// fs.writeFile(options.screenshotPath, buffer, (err) => {
|
|
@@ -1726,12 +1742,12 @@ class StableBrowser {
|
|
|
1726
1742
|
this.logger.info("unable to take screenshot, ignored");
|
|
1727
1743
|
}
|
|
1728
1744
|
if (info && info.box) {
|
|
1729
|
-
await drawRectangle(options.screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
|
|
1745
|
+
// await drawRectangle(options.screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
|
|
1730
1746
|
}
|
|
1731
1747
|
}
|
|
1732
1748
|
return result;
|
|
1733
1749
|
}
|
|
1734
|
-
async takeScreenshot(screenshotPath) {
|
|
1750
|
+
async takeScreenshot(screenshotPath, focusedElement = null) {
|
|
1735
1751
|
const playContext = this.context.playContext;
|
|
1736
1752
|
// Using CDP to capture the screenshot
|
|
1737
1753
|
const viewportWidth = Math.max(...(await this.page.evaluate(() => [
|
|
@@ -1743,6 +1759,15 @@ class StableBrowser {
|
|
|
1743
1759
|
document.documentElement.clientWidth,
|
|
1744
1760
|
])));
|
|
1745
1761
|
let screenshotBuffer = null;
|
|
1762
|
+
// if (focusedElement) {
|
|
1763
|
+
// // console.log(`Focused element ${JSON.stringify(focusedElement._selector)}`)
|
|
1764
|
+
// await this._unhighlightElements(focusedElement);
|
|
1765
|
+
// await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1766
|
+
// console.log(`Unhighlighted previous element`);
|
|
1767
|
+
// }
|
|
1768
|
+
if (focusedElement) {
|
|
1769
|
+
await this._highlightElements(focusedElement);
|
|
1770
|
+
}
|
|
1746
1771
|
if (this.context.browserName === "chromium") {
|
|
1747
1772
|
const client = await playContext.newCDPSession(this.page);
|
|
1748
1773
|
const { data } = await client.send("Page.captureScreenshot", {
|
|
@@ -1764,6 +1789,10 @@ class StableBrowser {
|
|
|
1764
1789
|
else {
|
|
1765
1790
|
screenshotBuffer = await this.page.screenshot();
|
|
1766
1791
|
}
|
|
1792
|
+
if (focusedElement) {
|
|
1793
|
+
// console.log(`Focused element ${JSON.stringify(focusedElement._selector)}`)
|
|
1794
|
+
await this._unhighlightElements(focusedElement);
|
|
1795
|
+
}
|
|
1767
1796
|
let image = await Jimp.read(screenshotBuffer);
|
|
1768
1797
|
// Get the image dimensions
|
|
1769
1798
|
const { width, height } = image.bitmap;
|
|
@@ -1790,7 +1819,7 @@ class StableBrowser {
|
|
|
1790
1819
|
};
|
|
1791
1820
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1792
1821
|
try {
|
|
1793
|
-
await _preCommand(state, this);
|
|
1822
|
+
await _preCommand(state, this, world);
|
|
1794
1823
|
await expect(state.element).toHaveCount(1, { timeout: 10000 });
|
|
1795
1824
|
return state.info;
|
|
1796
1825
|
}
|
|
@@ -1816,7 +1845,7 @@ class StableBrowser {
|
|
|
1816
1845
|
};
|
|
1817
1846
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1818
1847
|
try {
|
|
1819
|
-
await _preCommand(state, this);
|
|
1848
|
+
await _preCommand(state, this, world);
|
|
1820
1849
|
switch (attribute) {
|
|
1821
1850
|
case "inner_text":
|
|
1822
1851
|
state.value = await state.element.innerText();
|
|
@@ -1834,6 +1863,70 @@ class StableBrowser {
|
|
|
1834
1863
|
state.info.value = state.value;
|
|
1835
1864
|
this.setTestData({ [variable]: state.value }, world);
|
|
1836
1865
|
this.logger.info("set test data: " + variable + "=" + state.value);
|
|
1866
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1867
|
+
return state.info;
|
|
1868
|
+
}
|
|
1869
|
+
catch (e) {
|
|
1870
|
+
await _commandError(state, e, this);
|
|
1871
|
+
}
|
|
1872
|
+
finally {
|
|
1873
|
+
_commandFinally(state, this);
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
async verifyAttribute(selectors, attribute, value, _params = null, options = {}, world = null) {
|
|
1877
|
+
const state = {
|
|
1878
|
+
selectors,
|
|
1879
|
+
_params,
|
|
1880
|
+
attribute,
|
|
1881
|
+
value,
|
|
1882
|
+
options,
|
|
1883
|
+
world,
|
|
1884
|
+
type: Types.VERIFY_ATTRIBUTE,
|
|
1885
|
+
highlight: true,
|
|
1886
|
+
screenshot: true,
|
|
1887
|
+
text: `Verify element attribute`,
|
|
1888
|
+
operation: "verifyAttribute",
|
|
1889
|
+
log: "***** verify attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1890
|
+
allowDisabled: true,
|
|
1891
|
+
};
|
|
1892
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1893
|
+
let val;
|
|
1894
|
+
try {
|
|
1895
|
+
await _preCommand(state, this, world);
|
|
1896
|
+
switch (attribute) {
|
|
1897
|
+
case "innerText":
|
|
1898
|
+
val = String(await state.element.innerText());
|
|
1899
|
+
break;
|
|
1900
|
+
case "value":
|
|
1901
|
+
val = String(await state.element.inputValue());
|
|
1902
|
+
break;
|
|
1903
|
+
case "checked":
|
|
1904
|
+
val = String(await state.element.isChecked());
|
|
1905
|
+
break;
|
|
1906
|
+
case "disabled":
|
|
1907
|
+
val = String(await state.element.isDisabled());
|
|
1908
|
+
break;
|
|
1909
|
+
case "readOnly":
|
|
1910
|
+
const isEditable = await state.element.isEditable();
|
|
1911
|
+
val = String(!isEditable);
|
|
1912
|
+
break;
|
|
1913
|
+
default:
|
|
1914
|
+
val = String(await state.element.getAttribute(attribute));
|
|
1915
|
+
break;
|
|
1916
|
+
}
|
|
1917
|
+
state.info.expectedValue = val;
|
|
1918
|
+
let regex;
|
|
1919
|
+
if (value.startsWith("/") && value.endsWith("/")) {
|
|
1920
|
+
const patternBody = value.slice(1, -1);
|
|
1921
|
+
regex = new RegExp(patternBody, "g");
|
|
1922
|
+
}
|
|
1923
|
+
else {
|
|
1924
|
+
const escapedPattern = value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1925
|
+
regex = new RegExp(escapedPattern, "g");
|
|
1926
|
+
}
|
|
1927
|
+
if (!val.match(regex)) {
|
|
1928
|
+
throw new Error(`The ${attribute} attribute has a value of "${val}", but the expected value is "${value}"`);
|
|
1929
|
+
}
|
|
1837
1930
|
return state.info;
|
|
1838
1931
|
}
|
|
1839
1932
|
catch (e) {
|
|
@@ -1861,7 +1954,7 @@ class StableBrowser {
|
|
|
1861
1954
|
if (options && options.timeout) {
|
|
1862
1955
|
timeout = options.timeout;
|
|
1863
1956
|
}
|
|
1864
|
-
const serviceUrl =
|
|
1957
|
+
const serviceUrl = _getServerUrl() + "/api/mail/createLinkOrCodeFromEmail";
|
|
1865
1958
|
const request = {
|
|
1866
1959
|
method: "POST",
|
|
1867
1960
|
url: serviceUrl,
|
|
@@ -1932,27 +2025,32 @@ class StableBrowser {
|
|
|
1932
2025
|
async _highlightElements(scope, css) {
|
|
1933
2026
|
try {
|
|
1934
2027
|
if (!scope) {
|
|
2028
|
+
// console.log(`Scope is not defined`);
|
|
1935
2029
|
return;
|
|
1936
2030
|
}
|
|
1937
2031
|
if (!css) {
|
|
1938
2032
|
scope
|
|
1939
2033
|
.evaluate((node) => {
|
|
1940
2034
|
if (node && node.style) {
|
|
1941
|
-
let
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
}
|
|
2035
|
+
let originalOutline = node.style.outline;
|
|
2036
|
+
// console.log(`Original outline was: ${originalOutline}`);
|
|
2037
|
+
node.__previousOutline = originalOutline;
|
|
2038
|
+
node.style.outline = "2px solid red";
|
|
2039
|
+
// console.log(`New outline is: ${node.style.outline}`);
|
|
2040
|
+
// if (window) {
|
|
2041
|
+
// window.addEventListener("beforeunload", function (e) {
|
|
2042
|
+
// node.style.outline = originalBorder;
|
|
2043
|
+
// });
|
|
2044
|
+
// }
|
|
2045
|
+
// setTimeout(function () {
|
|
2046
|
+
// node.style.outline = originalBorder;
|
|
2047
|
+
//}, 2000);
|
|
1951
2048
|
}
|
|
1952
2049
|
})
|
|
1953
2050
|
.then(() => { })
|
|
1954
2051
|
.catch((e) => {
|
|
1955
2052
|
// ignore
|
|
2053
|
+
// console.error(`Could not highlight node : ${e}`);
|
|
1956
2054
|
});
|
|
1957
2055
|
}
|
|
1958
2056
|
else {
|
|
@@ -1968,24 +2066,26 @@ class StableBrowser {
|
|
|
1968
2066
|
if (!element.style) {
|
|
1969
2067
|
return;
|
|
1970
2068
|
}
|
|
1971
|
-
|
|
2069
|
+
let originalOutline = element.style.outline;
|
|
2070
|
+
element.__previousOutline = originalOutline;
|
|
1972
2071
|
// Set the new border to be red and 2px solid
|
|
1973
|
-
element.style.
|
|
1974
|
-
if (window) {
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
}
|
|
2072
|
+
element.style.outline = "2px solid red";
|
|
2073
|
+
// if (window) {
|
|
2074
|
+
// window.addEventListener("beforeunload", function (e) {
|
|
2075
|
+
// element.style.outline = originalBorder;
|
|
2076
|
+
// });
|
|
2077
|
+
// }
|
|
1979
2078
|
// Set a timeout to revert to the original border after 2 seconds
|
|
1980
|
-
setTimeout(function () {
|
|
1981
|
-
|
|
1982
|
-
}, 2000);
|
|
2079
|
+
// setTimeout(function () {
|
|
2080
|
+
// element.style.outline = originalBorder;
|
|
2081
|
+
// }, 2000);
|
|
1983
2082
|
}
|
|
1984
2083
|
return;
|
|
1985
2084
|
}, [css])
|
|
1986
2085
|
.then(() => { })
|
|
1987
2086
|
.catch((e) => {
|
|
1988
2087
|
// ignore
|
|
2088
|
+
// console.error(`Could not highlight css: ${e}`);
|
|
1989
2089
|
});
|
|
1990
2090
|
}
|
|
1991
2091
|
}
|
|
@@ -1993,6 +2093,58 @@ class StableBrowser {
|
|
|
1993
2093
|
console.debug(error);
|
|
1994
2094
|
}
|
|
1995
2095
|
}
|
|
2096
|
+
async _unhighlightElements(scope, css) {
|
|
2097
|
+
try {
|
|
2098
|
+
if (!scope) {
|
|
2099
|
+
return;
|
|
2100
|
+
}
|
|
2101
|
+
if (!css) {
|
|
2102
|
+
scope
|
|
2103
|
+
.evaluate((node) => {
|
|
2104
|
+
if (node && node.style) {
|
|
2105
|
+
if (!node.__previousOutline) {
|
|
2106
|
+
node.style.outline = "";
|
|
2107
|
+
}
|
|
2108
|
+
else {
|
|
2109
|
+
node.style.outline = node.__previousOutline;
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
})
|
|
2113
|
+
.then(() => { })
|
|
2114
|
+
.catch((e) => {
|
|
2115
|
+
// console.log(`Error while unhighlighting node ${JSON.stringify(scope)}: ${e}`);
|
|
2116
|
+
});
|
|
2117
|
+
}
|
|
2118
|
+
else {
|
|
2119
|
+
scope
|
|
2120
|
+
.evaluate(([css]) => {
|
|
2121
|
+
if (!css) {
|
|
2122
|
+
return;
|
|
2123
|
+
}
|
|
2124
|
+
let elements = Array.from(document.querySelectorAll(css));
|
|
2125
|
+
for (i = 0; i < elements.length; i++) {
|
|
2126
|
+
let element = elements[i];
|
|
2127
|
+
if (!element.style) {
|
|
2128
|
+
return;
|
|
2129
|
+
}
|
|
2130
|
+
if (!element.__previousOutline) {
|
|
2131
|
+
element.style.outline = "";
|
|
2132
|
+
}
|
|
2133
|
+
else {
|
|
2134
|
+
element.style.outline = element.__previousOutline;
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
})
|
|
2138
|
+
.then(() => { })
|
|
2139
|
+
.catch((e) => {
|
|
2140
|
+
// console.error(`Error while unhighlighting element in css: ${e}`);
|
|
2141
|
+
});
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
catch (error) {
|
|
2145
|
+
// console.debug(error);
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
1996
2148
|
async verifyPagePath(pathPart, options = {}, world = null) {
|
|
1997
2149
|
const startTime = Date.now();
|
|
1998
2150
|
let error = null;
|
|
@@ -2034,7 +2186,7 @@ class StableBrowser {
|
|
|
2034
2186
|
}
|
|
2035
2187
|
finally {
|
|
2036
2188
|
const endTime = Date.now();
|
|
2037
|
-
|
|
2189
|
+
_reportToWorld(world, {
|
|
2038
2190
|
type: Types.VERIFY_PAGE_PATH,
|
|
2039
2191
|
text: "Verify page path",
|
|
2040
2192
|
screenshotId,
|
|
@@ -2054,6 +2206,35 @@ class StableBrowser {
|
|
|
2054
2206
|
});
|
|
2055
2207
|
}
|
|
2056
2208
|
}
|
|
2209
|
+
async findTextInAllFrames(dateAlternatives, numberAlternatives, text, state) {
|
|
2210
|
+
const frames = this.page.frames();
|
|
2211
|
+
let results = [];
|
|
2212
|
+
let ignoreCase = false;
|
|
2213
|
+
for (let i = 0; i < frames.length; i++) {
|
|
2214
|
+
if (dateAlternatives.date) {
|
|
2215
|
+
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2216
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2217
|
+
result.frame = frames[i];
|
|
2218
|
+
results.push(result);
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
else if (numberAlternatives.number) {
|
|
2222
|
+
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2223
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2224
|
+
result.frame = frames[i];
|
|
2225
|
+
results.push(result);
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
else {
|
|
2229
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2230
|
+
result.frame = frames[i];
|
|
2231
|
+
results.push(result);
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
state.info.results = results;
|
|
2235
|
+
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2236
|
+
return resultWithElementsFound;
|
|
2237
|
+
}
|
|
2057
2238
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2058
2239
|
text = unEscapeString(text);
|
|
2059
2240
|
const state = {
|
|
@@ -2063,7 +2244,7 @@ class StableBrowser {
|
|
|
2063
2244
|
locate: false,
|
|
2064
2245
|
scroll: false,
|
|
2065
2246
|
highlight: false,
|
|
2066
|
-
type: Types.
|
|
2247
|
+
type: Types.VERIFY_PAGE_CONTAINS_TEXT,
|
|
2067
2248
|
text: `Verify text exists in page`,
|
|
2068
2249
|
operation: "verifyTextExistInPage",
|
|
2069
2250
|
log: "***** verify text " + text + " exists in page *****\n",
|
|
@@ -2078,34 +2259,10 @@ class StableBrowser {
|
|
|
2078
2259
|
let dateAlternatives = findDateAlternatives(text);
|
|
2079
2260
|
let numberAlternatives = findNumberAlternatives(text);
|
|
2080
2261
|
try {
|
|
2081
|
-
await _preCommand(state, this);
|
|
2262
|
+
await _preCommand(state, this, world);
|
|
2082
2263
|
state.info.text = text;
|
|
2083
2264
|
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);
|
|
2265
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, text, state);
|
|
2109
2266
|
if (resultWithElementsFound.length === 0) {
|
|
2110
2267
|
if (Date.now() - state.startTime > timeout) {
|
|
2111
2268
|
throw new Error(`Text ${text} not found in page`);
|
|
@@ -2115,15 +2272,32 @@ class StableBrowser {
|
|
|
2115
2272
|
}
|
|
2116
2273
|
if (resultWithElementsFound[0].randomToken) {
|
|
2117
2274
|
const frame = resultWithElementsFound[0].frame;
|
|
2118
|
-
const dataAttribute = `[data-blinq-id
|
|
2119
|
-
|
|
2120
|
-
|
|
2275
|
+
const dataAttribute = `[data-blinq-id-${resultWithElementsFound[0].randomToken}]`;
|
|
2276
|
+
if (world && world.screenshot && !world.screenshotPath) {
|
|
2277
|
+
// console.log(`Highlighting for verify text is found while running from recorder`);
|
|
2278
|
+
this._highlightElements(frame, dataAttribute).then(async () => {
|
|
2279
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2280
|
+
this._unhighlightElements(frame, dataAttribute)
|
|
2281
|
+
.then(async () => {
|
|
2282
|
+
// console.log(`Unhighlighted frame dataAttribute successfully`);
|
|
2283
|
+
})
|
|
2284
|
+
.catch((e) => { }
|
|
2285
|
+
// console.error(e)
|
|
2286
|
+
);
|
|
2287
|
+
});
|
|
2288
|
+
}
|
|
2289
|
+
const element = await frame.locator(dataAttribute).first();
|
|
2290
|
+
// await new Promise((resolve) => setTimeout(resolve, 100));
|
|
2291
|
+
// await this._unhighlightElements(frame, dataAttribute);
|
|
2121
2292
|
if (element) {
|
|
2122
2293
|
await this.scrollIfNeeded(element, state.info);
|
|
2123
2294
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2295
|
+
await _screenshot(state, this, element);
|
|
2124
2296
|
}
|
|
2125
2297
|
}
|
|
2126
|
-
|
|
2298
|
+
else {
|
|
2299
|
+
await _screenshot(state, this);
|
|
2300
|
+
}
|
|
2127
2301
|
return state.info;
|
|
2128
2302
|
}
|
|
2129
2303
|
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
@@ -2159,34 +2333,10 @@ class StableBrowser {
|
|
|
2159
2333
|
let dateAlternatives = findDateAlternatives(text);
|
|
2160
2334
|
let numberAlternatives = findNumberAlternatives(text);
|
|
2161
2335
|
try {
|
|
2162
|
-
await _preCommand(state, this);
|
|
2336
|
+
await _preCommand(state, this, world);
|
|
2163
2337
|
state.info.text = text;
|
|
2164
2338
|
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);
|
|
2339
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, text, state);
|
|
2190
2340
|
if (resultWithElementsFound.length === 0) {
|
|
2191
2341
|
await _screenshot(state, this);
|
|
2192
2342
|
return state.info;
|
|
@@ -2204,15 +2354,103 @@ class StableBrowser {
|
|
|
2204
2354
|
_commandFinally(state, this);
|
|
2205
2355
|
}
|
|
2206
2356
|
}
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2357
|
+
async verifyTextRelatedToText(textAnchor, climb, textToVerify, options = {}, world = null) {
|
|
2358
|
+
textAnchor = unEscapeString(textAnchor);
|
|
2359
|
+
textToVerify = unEscapeString(textToVerify);
|
|
2360
|
+
const state = {
|
|
2361
|
+
text_search: textToVerify,
|
|
2362
|
+
options,
|
|
2363
|
+
world,
|
|
2364
|
+
locate: false,
|
|
2365
|
+
scroll: false,
|
|
2366
|
+
highlight: false,
|
|
2367
|
+
type: Types.VERIFY_TEXT_WITH_RELATION,
|
|
2368
|
+
text: `Verify text with relation to another text`,
|
|
2369
|
+
operation: "verify_text_with_relation",
|
|
2370
|
+
log: "***** search for " + textAnchor + " climb " + climb + " and verify " + textToVerify + " found *****\n",
|
|
2371
|
+
};
|
|
2372
|
+
const timeout = this._getLoadTimeout(options);
|
|
2373
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2374
|
+
let newValue = await this._replaceWithLocalData(textAnchor, world);
|
|
2375
|
+
if (newValue !== textAnchor) {
|
|
2376
|
+
this.logger.info(textAnchor + "=" + newValue);
|
|
2377
|
+
textAnchor = newValue;
|
|
2378
|
+
}
|
|
2379
|
+
newValue = await this._replaceWithLocalData(textToVerify, world);
|
|
2380
|
+
if (newValue !== textToVerify) {
|
|
2381
|
+
this.logger.info(textToVerify + "=" + newValue);
|
|
2382
|
+
textToVerify = newValue;
|
|
2383
|
+
}
|
|
2384
|
+
let dateAlternatives = findDateAlternatives(textToVerify);
|
|
2385
|
+
let numberAlternatives = findNumberAlternatives(textToVerify);
|
|
2386
|
+
let foundAncore = false;
|
|
2387
|
+
try {
|
|
2388
|
+
await _preCommand(state, this, world);
|
|
2389
|
+
state.info.text = textToVerify;
|
|
2390
|
+
while (true) {
|
|
2391
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, textAnchor, state);
|
|
2392
|
+
if (resultWithElementsFound.length === 0) {
|
|
2393
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2394
|
+
throw new Error(`Text ${foundAncore ? textToVerify : textAnchor} not found in page`);
|
|
2395
|
+
}
|
|
2396
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2397
|
+
continue;
|
|
2398
|
+
}
|
|
2399
|
+
for (let i = 0; i < resultWithElementsFound.length; i++) {
|
|
2400
|
+
foundAncore = true;
|
|
2401
|
+
const result = resultWithElementsFound[i];
|
|
2402
|
+
const token = result.randomToken;
|
|
2403
|
+
const frame = result.frame;
|
|
2404
|
+
let css = `[data-blinq-id-${token}]`;
|
|
2405
|
+
const climbArray1 = [];
|
|
2406
|
+
for (let i = 0; i < climb; i++) {
|
|
2407
|
+
climbArray1.push("..");
|
|
2408
|
+
}
|
|
2409
|
+
let climbXpath = "xpath=" + climbArray1.join("/");
|
|
2410
|
+
css = css + " >> " + climbXpath;
|
|
2411
|
+
const count = await frame.locator(css).count();
|
|
2412
|
+
for (let j = 0; j < count; j++) {
|
|
2413
|
+
const continer = await frame.locator(css).nth(j);
|
|
2414
|
+
const result = await this._locateElementByText(continer, textToVerify, "*", false, true, true, {});
|
|
2415
|
+
if (result.elementCount > 0) {
|
|
2416
|
+
const dataAttribute = "[data-blinq-id-" + result.randomToken + "]";
|
|
2417
|
+
//const cssAnchor = `[data-blinq-id="blinq-id-${token}-anchor"]`;
|
|
2418
|
+
if (world && world.screenshot && !world.screenshotPath) {
|
|
2419
|
+
// console.log(`Highlighting for vtrt while running from recorder`);
|
|
2420
|
+
this._highlightElements(frame, dataAttribute)
|
|
2421
|
+
.then(async () => {
|
|
2422
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2423
|
+
this._unhighlightElements(frame, dataAttribute).then(() => { }
|
|
2424
|
+
// console.log(`Unhighlighting vrtr in recorder is successful`)
|
|
2425
|
+
);
|
|
2426
|
+
})
|
|
2427
|
+
.catch(e);
|
|
2428
|
+
}
|
|
2429
|
+
//await this._highlightElements(frame, cssAnchor);
|
|
2430
|
+
const element = await frame.locator(dataAttribute).first();
|
|
2431
|
+
// await new Promise((resolve) => setTimeout(resolve, 100));
|
|
2432
|
+
// await this._unhighlightElements(frame, dataAttribute);
|
|
2433
|
+
if (element) {
|
|
2434
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2435
|
+
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2436
|
+
await _screenshot(state, this, element);
|
|
2437
|
+
}
|
|
2438
|
+
else {
|
|
2439
|
+
await _screenshot(state, this);
|
|
2440
|
+
}
|
|
2441
|
+
return state.info;
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2211
2447
|
}
|
|
2212
|
-
|
|
2213
|
-
|
|
2448
|
+
catch (e) {
|
|
2449
|
+
await _commandError(state, e, this);
|
|
2450
|
+
}
|
|
2451
|
+
finally {
|
|
2452
|
+
_commandFinally(state, this);
|
|
2214
2453
|
}
|
|
2215
|
-
return serviceUrl;
|
|
2216
2454
|
}
|
|
2217
2455
|
async visualVerification(text, options = {}, world = null) {
|
|
2218
2456
|
const startTime = Date.now();
|
|
@@ -2228,7 +2466,7 @@ class StableBrowser {
|
|
|
2228
2466
|
throw new Error("TOKEN is not set");
|
|
2229
2467
|
}
|
|
2230
2468
|
try {
|
|
2231
|
-
let serviceUrl =
|
|
2469
|
+
let serviceUrl = _getServerUrl();
|
|
2232
2470
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2233
2471
|
info.screenshotPath = screenshotPath;
|
|
2234
2472
|
const screenshot = await this.takeScreenshot();
|
|
@@ -2269,7 +2507,7 @@ class StableBrowser {
|
|
|
2269
2507
|
}
|
|
2270
2508
|
finally {
|
|
2271
2509
|
const endTime = Date.now();
|
|
2272
|
-
|
|
2510
|
+
_reportToWorld(world, {
|
|
2273
2511
|
type: Types.VERIFY_VISUAL,
|
|
2274
2512
|
text: "Visual verification",
|
|
2275
2513
|
screenshotId,
|
|
@@ -2317,18 +2555,19 @@ class StableBrowser {
|
|
|
2317
2555
|
let screenshotPath = null;
|
|
2318
2556
|
const info = {};
|
|
2319
2557
|
info.log = "";
|
|
2558
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
2320
2559
|
info.operation = "getTableData";
|
|
2321
2560
|
info.selectors = selectors;
|
|
2322
2561
|
try {
|
|
2323
2562
|
let table = await this._locate(selectors, info, _params);
|
|
2324
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2563
|
+
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info, table));
|
|
2325
2564
|
const tableData = await getTableData(this.page, table);
|
|
2326
2565
|
return tableData;
|
|
2327
2566
|
}
|
|
2328
2567
|
catch (e) {
|
|
2329
2568
|
this.logger.error("getTableData failed " + info.log);
|
|
2330
2569
|
this.logger.error(e);
|
|
2331
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2570
|
+
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info, table));
|
|
2332
2571
|
info.screenshotPath = screenshotPath;
|
|
2333
2572
|
Object.assign(e, { info: info });
|
|
2334
2573
|
error = e;
|
|
@@ -2337,7 +2576,7 @@ class StableBrowser {
|
|
|
2337
2576
|
}
|
|
2338
2577
|
finally {
|
|
2339
2578
|
const endTime = Date.now();
|
|
2340
|
-
|
|
2579
|
+
_reportToWorld(world, {
|
|
2341
2580
|
element_name: selectors.element_name,
|
|
2342
2581
|
type: Types.GET_TABLE_DATA,
|
|
2343
2582
|
text: "Get table data",
|
|
@@ -2392,7 +2631,7 @@ class StableBrowser {
|
|
|
2392
2631
|
info.operation = "analyzeTable";
|
|
2393
2632
|
info.selectors = selectors;
|
|
2394
2633
|
info.query = query;
|
|
2395
|
-
query =
|
|
2634
|
+
query = _fixUsingParams(query, _params);
|
|
2396
2635
|
info.query_fixed = query;
|
|
2397
2636
|
info.operator = operator;
|
|
2398
2637
|
info.value = value;
|
|
@@ -2503,7 +2742,7 @@ class StableBrowser {
|
|
|
2503
2742
|
}
|
|
2504
2743
|
finally {
|
|
2505
2744
|
const endTime = Date.now();
|
|
2506
|
-
|
|
2745
|
+
_reportToWorld(world, {
|
|
2507
2746
|
element_name: selectors.element_name,
|
|
2508
2747
|
type: Types.ANALYZE_TABLE,
|
|
2509
2748
|
text: "Analyze table",
|
|
@@ -2576,7 +2815,7 @@ class StableBrowser {
|
|
|
2576
2815
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2577
2816
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2578
2817
|
const endTime = Date.now();
|
|
2579
|
-
|
|
2818
|
+
_reportToWorld(world, {
|
|
2580
2819
|
type: Types.GET_PAGE_STATUS,
|
|
2581
2820
|
text: "Wait for page load",
|
|
2582
2821
|
screenshotId,
|
|
@@ -2609,7 +2848,7 @@ class StableBrowser {
|
|
|
2609
2848
|
throwError: false,
|
|
2610
2849
|
};
|
|
2611
2850
|
try {
|
|
2612
|
-
await _preCommand(state, this);
|
|
2851
|
+
await _preCommand(state, this, world);
|
|
2613
2852
|
await this.page.close();
|
|
2614
2853
|
}
|
|
2615
2854
|
catch (e) {
|
|
@@ -2620,6 +2859,11 @@ class StableBrowser {
|
|
|
2620
2859
|
_commandFinally(state, this);
|
|
2621
2860
|
}
|
|
2622
2861
|
}
|
|
2862
|
+
saveTestDataAsGlobal(options, world) {
|
|
2863
|
+
const dataFile = this._getDataFile(world);
|
|
2864
|
+
process.env.GLOBAL_TEST_DATA_FILE = dataFile;
|
|
2865
|
+
this.logger.info("Save the scenario test data as global for the following scenarios.");
|
|
2866
|
+
}
|
|
2623
2867
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
2624
2868
|
const startTime = Date.now();
|
|
2625
2869
|
let error = null;
|
|
@@ -2643,7 +2887,7 @@ class StableBrowser {
|
|
|
2643
2887
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2644
2888
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2645
2889
|
const endTime = Date.now();
|
|
2646
|
-
|
|
2890
|
+
_reportToWorld(world, {
|
|
2647
2891
|
type: Types.SET_VIEWPORT,
|
|
2648
2892
|
text: "set viewport size to " + width + "x" + hight,
|
|
2649
2893
|
screenshotId,
|
|
@@ -2680,7 +2924,7 @@ class StableBrowser {
|
|
|
2680
2924
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2681
2925
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2682
2926
|
const endTime = Date.now();
|
|
2683
|
-
|
|
2927
|
+
_reportToWorld(world, {
|
|
2684
2928
|
type: Types.GET_PAGE_STATUS,
|
|
2685
2929
|
text: "page relaod",
|
|
2686
2930
|
screenshotId,
|
|
@@ -2716,26 +2960,35 @@ class StableBrowser {
|
|
|
2716
2960
|
console.log("#-#");
|
|
2717
2961
|
}
|
|
2718
2962
|
}
|
|
2719
|
-
_reportToWorld(world, properties) {
|
|
2720
|
-
if (!world || !world.attach) {
|
|
2721
|
-
return;
|
|
2722
|
-
}
|
|
2723
|
-
world.attach(JSON.stringify(properties), { mediaType: "application/json" });
|
|
2724
|
-
}
|
|
2725
2963
|
async beforeStep(world, step) {
|
|
2726
|
-
this.stepName = step.pickleStep.text;
|
|
2727
|
-
this.logger.info("step: " + this.stepName);
|
|
2728
2964
|
if (this.stepIndex === undefined) {
|
|
2729
2965
|
this.stepIndex = 0;
|
|
2730
2966
|
}
|
|
2731
2967
|
else {
|
|
2732
2968
|
this.stepIndex++;
|
|
2733
2969
|
}
|
|
2970
|
+
if (step && step.pickleStep && step.pickleStep.text) {
|
|
2971
|
+
this.stepName = step.pickleStep.text;
|
|
2972
|
+
this.logger.info("step: " + this.stepName);
|
|
2973
|
+
}
|
|
2974
|
+
else if (step && step.text) {
|
|
2975
|
+
this.stepName = step.text;
|
|
2976
|
+
}
|
|
2977
|
+
else {
|
|
2978
|
+
this.stepName = "step " + this.stepIndex;
|
|
2979
|
+
}
|
|
2734
2980
|
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2735
2981
|
if (this.context.browserObject.context) {
|
|
2736
2982
|
await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
|
|
2737
2983
|
}
|
|
2738
2984
|
}
|
|
2985
|
+
if (this.tags === null && step && step.pickle && step.pickle.tags) {
|
|
2986
|
+
this.tags = step.pickle.tags.map((tag) => tag.name);
|
|
2987
|
+
// check if @global_test_data tag is present
|
|
2988
|
+
if (this.tags.includes("@global_test_data")) {
|
|
2989
|
+
this.saveTestDataAsGlobal({}, world);
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
2739
2992
|
}
|
|
2740
2993
|
async afterStep(world, step) {
|
|
2741
2994
|
this.stepName = null;
|
|
@@ -2753,156 +3006,5 @@ function createTimedPromise(promise, label) {
|
|
|
2753
3006
|
.then((result) => ({ status: "fulfilled", label, result }))
|
|
2754
3007
|
.catch((error) => Promise.reject({ status: "rejected", label, error }));
|
|
2755
3008
|
}
|
|
2756
|
-
const KEYBOARD_EVENTS = [
|
|
2757
|
-
"ALT",
|
|
2758
|
-
"AltGraph",
|
|
2759
|
-
"CapsLock",
|
|
2760
|
-
"Control",
|
|
2761
|
-
"Fn",
|
|
2762
|
-
"FnLock",
|
|
2763
|
-
"Hyper",
|
|
2764
|
-
"Meta",
|
|
2765
|
-
"NumLock",
|
|
2766
|
-
"ScrollLock",
|
|
2767
|
-
"Shift",
|
|
2768
|
-
"Super",
|
|
2769
|
-
"Symbol",
|
|
2770
|
-
"SymbolLock",
|
|
2771
|
-
"Enter",
|
|
2772
|
-
"Tab",
|
|
2773
|
-
"ArrowDown",
|
|
2774
|
-
"ArrowLeft",
|
|
2775
|
-
"ArrowRight",
|
|
2776
|
-
"ArrowUp",
|
|
2777
|
-
"End",
|
|
2778
|
-
"Home",
|
|
2779
|
-
"PageDown",
|
|
2780
|
-
"PageUp",
|
|
2781
|
-
"Backspace",
|
|
2782
|
-
"Clear",
|
|
2783
|
-
"Copy",
|
|
2784
|
-
"CrSel",
|
|
2785
|
-
"Cut",
|
|
2786
|
-
"Delete",
|
|
2787
|
-
"EraseEof",
|
|
2788
|
-
"ExSel",
|
|
2789
|
-
"Insert",
|
|
2790
|
-
"Paste",
|
|
2791
|
-
"Redo",
|
|
2792
|
-
"Undo",
|
|
2793
|
-
"Accept",
|
|
2794
|
-
"Again",
|
|
2795
|
-
"Attn",
|
|
2796
|
-
"Cancel",
|
|
2797
|
-
"ContextMenu",
|
|
2798
|
-
"Escape",
|
|
2799
|
-
"Execute",
|
|
2800
|
-
"Find",
|
|
2801
|
-
"Finish",
|
|
2802
|
-
"Help",
|
|
2803
|
-
"Pause",
|
|
2804
|
-
"Play",
|
|
2805
|
-
"Props",
|
|
2806
|
-
"Select",
|
|
2807
|
-
"ZoomIn",
|
|
2808
|
-
"ZoomOut",
|
|
2809
|
-
"BrightnessDown",
|
|
2810
|
-
"BrightnessUp",
|
|
2811
|
-
"Eject",
|
|
2812
|
-
"LogOff",
|
|
2813
|
-
"Power",
|
|
2814
|
-
"PowerOff",
|
|
2815
|
-
"PrintScreen",
|
|
2816
|
-
"Hibernate",
|
|
2817
|
-
"Standby",
|
|
2818
|
-
"WakeUp",
|
|
2819
|
-
"AllCandidates",
|
|
2820
|
-
"Alphanumeric",
|
|
2821
|
-
"CodeInput",
|
|
2822
|
-
"Compose",
|
|
2823
|
-
"Convert",
|
|
2824
|
-
"Dead",
|
|
2825
|
-
"FinalMode",
|
|
2826
|
-
"GroupFirst",
|
|
2827
|
-
"GroupLast",
|
|
2828
|
-
"GroupNext",
|
|
2829
|
-
"GroupPrevious",
|
|
2830
|
-
"ModeChange",
|
|
2831
|
-
"NextCandidate",
|
|
2832
|
-
"NonConvert",
|
|
2833
|
-
"PreviousCandidate",
|
|
2834
|
-
"Process",
|
|
2835
|
-
"SingleCandidate",
|
|
2836
|
-
"HangulMode",
|
|
2837
|
-
"HanjaMode",
|
|
2838
|
-
"JunjaMode",
|
|
2839
|
-
"Eisu",
|
|
2840
|
-
"Hankaku",
|
|
2841
|
-
"Hiragana",
|
|
2842
|
-
"HiraganaKatakana",
|
|
2843
|
-
"KanaMode",
|
|
2844
|
-
"KanjiMode",
|
|
2845
|
-
"Katakana",
|
|
2846
|
-
"Romaji",
|
|
2847
|
-
"Zenkaku",
|
|
2848
|
-
"ZenkakuHanaku",
|
|
2849
|
-
"F1",
|
|
2850
|
-
"F2",
|
|
2851
|
-
"F3",
|
|
2852
|
-
"F4",
|
|
2853
|
-
"F5",
|
|
2854
|
-
"F6",
|
|
2855
|
-
"F7",
|
|
2856
|
-
"F8",
|
|
2857
|
-
"F9",
|
|
2858
|
-
"F10",
|
|
2859
|
-
"F11",
|
|
2860
|
-
"F12",
|
|
2861
|
-
"Soft1",
|
|
2862
|
-
"Soft2",
|
|
2863
|
-
"Soft3",
|
|
2864
|
-
"Soft4",
|
|
2865
|
-
"ChannelDown",
|
|
2866
|
-
"ChannelUp",
|
|
2867
|
-
"Close",
|
|
2868
|
-
"MailForward",
|
|
2869
|
-
"MailReply",
|
|
2870
|
-
"MailSend",
|
|
2871
|
-
"MediaFastForward",
|
|
2872
|
-
"MediaPause",
|
|
2873
|
-
"MediaPlay",
|
|
2874
|
-
"MediaPlayPause",
|
|
2875
|
-
"MediaRecord",
|
|
2876
|
-
"MediaRewind",
|
|
2877
|
-
"MediaStop",
|
|
2878
|
-
"MediaTrackNext",
|
|
2879
|
-
"MediaTrackPrevious",
|
|
2880
|
-
"AudioBalanceLeft",
|
|
2881
|
-
"AudioBalanceRight",
|
|
2882
|
-
"AudioBassBoostDown",
|
|
2883
|
-
"AudioBassBoostToggle",
|
|
2884
|
-
"AudioBassBoostUp",
|
|
2885
|
-
"AudioFaderFront",
|
|
2886
|
-
"AudioFaderRear",
|
|
2887
|
-
"AudioSurroundModeNext",
|
|
2888
|
-
"AudioTrebleDown",
|
|
2889
|
-
"AudioTrebleUp",
|
|
2890
|
-
"AudioVolumeDown",
|
|
2891
|
-
"AudioVolumeMute",
|
|
2892
|
-
"AudioVolumeUp",
|
|
2893
|
-
"MicrophoneToggle",
|
|
2894
|
-
"MicrophoneVolumeDown",
|
|
2895
|
-
"MicrophoneVolumeMute",
|
|
2896
|
-
"MicrophoneVolumeUp",
|
|
2897
|
-
"TV",
|
|
2898
|
-
"TV3DMode",
|
|
2899
|
-
"TVAntennaCable",
|
|
2900
|
-
"TVAudioDescription",
|
|
2901
|
-
];
|
|
2902
|
-
function unEscapeString(str) {
|
|
2903
|
-
const placeholder = "__NEWLINE__";
|
|
2904
|
-
str = str.replace(new RegExp(placeholder, "g"), "\n");
|
|
2905
|
-
return str;
|
|
2906
|
-
}
|
|
2907
3009
|
export { StableBrowser };
|
|
2908
3010
|
//# sourceMappingURL=stable_browser.js.map
|