automation_model 1.0.517-dev → 1.0.517-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 +37 -3
- package/lib/network.js.map +1 -1
- package/lib/stable_browser.d.ts +55 -19
- package/lib/stable_browser.js +633 -528
- 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 = [];
|
|
@@ -229,7 +218,7 @@ class StableBrowser {
|
|
|
229
218
|
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
|
|
230
219
|
}
|
|
231
220
|
catch (error) {
|
|
232
|
-
console.error("Error in request listener", error);
|
|
221
|
+
// console.error("Error in request listener", error);
|
|
233
222
|
context.networkLogger.push({
|
|
234
223
|
error: "not able to listen",
|
|
235
224
|
message: error.message,
|
|
@@ -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);
|
|
@@ -501,24 +400,24 @@ class StableBrowser {
|
|
|
501
400
|
info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
|
|
502
401
|
return;
|
|
503
402
|
}
|
|
504
|
-
locator = this._getLocator({ css: locatorString }, scope, _params);
|
|
403
|
+
locator = await this._getLocator({ css: locatorString }, scope, _params);
|
|
505
404
|
}
|
|
506
405
|
else if (locatorSearch.text) {
|
|
507
|
-
let text =
|
|
508
|
-
let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, _params);
|
|
406
|
+
let text = _fixUsingParams(locatorSearch.text, _params);
|
|
407
|
+
let result = await this._locateElementByText(scope, text, locatorSearch.tag, false, locatorSearch.partial === true, true, _params);
|
|
509
408
|
if (result.elementCount === 0) {
|
|
510
409
|
info.failCause.textNotFound = true;
|
|
511
410
|
info.failCause.lastError = "failed to locate element by text: " + text;
|
|
512
411
|
return;
|
|
513
412
|
}
|
|
514
|
-
locatorSearch.css = "[data-blinq-id
|
|
413
|
+
locatorSearch.css = "[data-blinq-id-" + result.randomToken + "]";
|
|
515
414
|
if (locatorSearch.childCss) {
|
|
516
415
|
locatorSearch.css = locatorSearch.css + " " + locatorSearch.childCss;
|
|
517
416
|
}
|
|
518
|
-
locator = this._getLocator(locatorSearch, scope, _params);
|
|
417
|
+
locator = await this._getLocator(locatorSearch, scope, _params);
|
|
519
418
|
}
|
|
520
419
|
else {
|
|
521
|
-
locator = this._getLocator(locatorSearch, scope, _params);
|
|
420
|
+
locator = await this._getLocator(locatorSearch, scope, _params);
|
|
522
421
|
}
|
|
523
422
|
// let cssHref = false;
|
|
524
423
|
// if (locatorSearch.css && locatorSearch.css.includes("href=")) {
|
|
@@ -531,18 +430,27 @@ class StableBrowser {
|
|
|
531
430
|
//info.log += "total elements found " + count + "\n";
|
|
532
431
|
//let visibleCount = 0;
|
|
533
432
|
let visibleLocator = null;
|
|
534
|
-
if (locatorSearch.index && locatorSearch.index < count) {
|
|
433
|
+
if (typeof locatorSearch.index === "number" && locatorSearch.index < count) {
|
|
535
434
|
foundLocators.push(locator.nth(locatorSearch.index));
|
|
435
|
+
if (info.locatorLog) {
|
|
436
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND");
|
|
437
|
+
}
|
|
536
438
|
return;
|
|
537
439
|
}
|
|
440
|
+
if (info.locatorLog && count === 0) {
|
|
441
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "NOT_FOUND");
|
|
442
|
+
}
|
|
538
443
|
for (let j = 0; j < count; j++) {
|
|
539
444
|
let visible = await locator.nth(j).isVisible();
|
|
540
445
|
const enabled = await locator.nth(j).isEnabled();
|
|
541
446
|
if (!visibleOnly) {
|
|
542
447
|
visible = true;
|
|
543
448
|
}
|
|
544
|
-
if (visible && enabled) {
|
|
449
|
+
if (visible && (allowDisabled || enabled)) {
|
|
545
450
|
foundLocators.push(locator.nth(j));
|
|
451
|
+
if (info.locatorLog) {
|
|
452
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND");
|
|
453
|
+
}
|
|
546
454
|
}
|
|
547
455
|
else {
|
|
548
456
|
info.failCause.visible = visible;
|
|
@@ -550,8 +458,16 @@ class StableBrowser {
|
|
|
550
458
|
if (!info.printMessages) {
|
|
551
459
|
info.printMessages = {};
|
|
552
460
|
}
|
|
461
|
+
if (info.locatorLog && !visible) {
|
|
462
|
+
info.failCause.lastError = "element " + originalLocatorSearch + " is not visible";
|
|
463
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND_NOT_VISIBLE");
|
|
464
|
+
}
|
|
465
|
+
if (info.locatorLog && !enabled) {
|
|
466
|
+
info.failCause.lastError = "element " + originalLocatorSearch + " is disabled, ";
|
|
467
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND_NOT_ENABLED");
|
|
468
|
+
}
|
|
553
469
|
if (!info.printMessages[j.toString()]) {
|
|
554
|
-
info.log += "element " + locator + " visible " + visible + " enabled " + enabled + "\n";
|
|
470
|
+
//info.log += "element " + locator + " visible " + visible + " enabled " + enabled + "\n";
|
|
555
471
|
info.printMessages[j.toString()] = true;
|
|
556
472
|
}
|
|
557
473
|
}
|
|
@@ -567,7 +483,7 @@ class StableBrowser {
|
|
|
567
483
|
if (!info) {
|
|
568
484
|
info = {};
|
|
569
485
|
}
|
|
570
|
-
info.log += "scan for popup handlers" + "\n";
|
|
486
|
+
//info.log += "scan for popup handlers" + "\n";
|
|
571
487
|
const handlerGroup = [];
|
|
572
488
|
for (let i = 0; i < this.configuration.popupHandlers.length; i++) {
|
|
573
489
|
handlerGroup.push(this.configuration.popupHandlers[i].locator);
|
|
@@ -594,16 +510,28 @@ class StableBrowser {
|
|
|
594
510
|
}
|
|
595
511
|
if (result.foundElements.length > 0) {
|
|
596
512
|
let dialogCloseLocator = result.foundElements[0].locator;
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
513
|
+
try {
|
|
514
|
+
await scope?.evaluate(() => {
|
|
515
|
+
window.__isClosingPopups = true;
|
|
516
|
+
});
|
|
517
|
+
await dialogCloseLocator.click();
|
|
518
|
+
// wait for the dialog to close
|
|
519
|
+
await dialogCloseLocator.waitFor({ state: "hidden" });
|
|
520
|
+
}
|
|
521
|
+
catch (e) {
|
|
522
|
+
}
|
|
523
|
+
finally {
|
|
524
|
+
await scope?.evaluate(() => {
|
|
525
|
+
window.__isClosingPopups = false;
|
|
526
|
+
});
|
|
527
|
+
}
|
|
600
528
|
return { rerun: true };
|
|
601
529
|
}
|
|
602
530
|
}
|
|
603
531
|
}
|
|
604
532
|
return { rerun: false };
|
|
605
533
|
}
|
|
606
|
-
async _locate(selectors, info, _params, timeout) {
|
|
534
|
+
async _locate(selectors, info, _params, timeout, allowDisabled = false) {
|
|
607
535
|
if (!timeout) {
|
|
608
536
|
timeout = 30000;
|
|
609
537
|
}
|
|
@@ -613,7 +541,7 @@ class StableBrowser {
|
|
|
613
541
|
let selector = selectors.locators[j];
|
|
614
542
|
info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
|
|
615
543
|
}
|
|
616
|
-
let element = await this._locate_internal(selectors, info, _params, timeout);
|
|
544
|
+
let element = await this._locate_internal(selectors, info, _params, timeout, allowDisabled);
|
|
617
545
|
if (!element.rerun) {
|
|
618
546
|
return element;
|
|
619
547
|
}
|
|
@@ -626,6 +554,7 @@ class StableBrowser {
|
|
|
626
554
|
info.failCause = {};
|
|
627
555
|
info.log = "";
|
|
628
556
|
}
|
|
557
|
+
let startTime = Date.now();
|
|
629
558
|
let scope = this.page;
|
|
630
559
|
if (selectors.frame) {
|
|
631
560
|
return selectors.frame;
|
|
@@ -656,9 +585,11 @@ class StableBrowser {
|
|
|
656
585
|
}
|
|
657
586
|
return framescope;
|
|
658
587
|
};
|
|
588
|
+
let fLocator = null;
|
|
659
589
|
while (true) {
|
|
660
590
|
let frameFound = false;
|
|
661
591
|
if (selectors.nestFrmLoc) {
|
|
592
|
+
fLocator = selectors.nestFrmLoc;
|
|
662
593
|
scope = await findFrame(selectors.nestFrmLoc, scope);
|
|
663
594
|
frameFound = true;
|
|
664
595
|
break;
|
|
@@ -667,6 +598,7 @@ class StableBrowser {
|
|
|
667
598
|
for (let i = 0; i < selectors.frameLocators.length; i++) {
|
|
668
599
|
let frameLocator = selectors.frameLocators[i];
|
|
669
600
|
if (frameLocator.css) {
|
|
601
|
+
fLocator = frameLocator.css;
|
|
670
602
|
scope = scope.frameLocator(frameLocator.css);
|
|
671
603
|
frameFound = true;
|
|
672
604
|
break;
|
|
@@ -674,11 +606,15 @@ class StableBrowser {
|
|
|
674
606
|
}
|
|
675
607
|
}
|
|
676
608
|
if (!frameFound && selectors.iframe_src) {
|
|
609
|
+
fLocator = selectors.iframe_src;
|
|
677
610
|
scope = this.page.frame({ url: selectors.iframe_src });
|
|
678
611
|
}
|
|
679
612
|
if (!scope) {
|
|
680
|
-
info
|
|
681
|
-
|
|
613
|
+
if (info && info.locatorLog) {
|
|
614
|
+
info.locatorLog.setLocatorSearchStatus("frame-" + fLocator, "NOT_FOUND");
|
|
615
|
+
}
|
|
616
|
+
//info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
|
|
617
|
+
if (Date.now() - startTime > timeout) {
|
|
682
618
|
info.failCause.iframeNotFound = true;
|
|
683
619
|
info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
|
|
684
620
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
@@ -686,6 +622,9 @@ class StableBrowser {
|
|
|
686
622
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
687
623
|
}
|
|
688
624
|
else {
|
|
625
|
+
if (info && info.locatorLog) {
|
|
626
|
+
info.locatorLog.setLocatorSearchStatus("frame-" + fLocator, "FOUND");
|
|
627
|
+
}
|
|
689
628
|
break;
|
|
690
629
|
}
|
|
691
630
|
}
|
|
@@ -702,16 +641,18 @@ class StableBrowser {
|
|
|
702
641
|
return bodyContent;
|
|
703
642
|
});
|
|
704
643
|
}
|
|
705
|
-
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
644
|
+
async _locate_internal(selectors, info, _params, timeout = 30000, allowDisabled = false) {
|
|
706
645
|
if (!info) {
|
|
707
646
|
info = {};
|
|
708
647
|
info.failCause = {};
|
|
709
648
|
info.log = "";
|
|
649
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
710
650
|
}
|
|
711
651
|
let highPriorityTimeout = 5000;
|
|
712
652
|
let visibleOnlyTimeout = 6000;
|
|
713
|
-
let startTime =
|
|
653
|
+
let startTime = Date.now();
|
|
714
654
|
let locatorsCount = 0;
|
|
655
|
+
let lazy_scroll = false;
|
|
715
656
|
//let arrayMode = Array.isArray(selectors);
|
|
716
657
|
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
717
658
|
let selectorsLocators = null;
|
|
@@ -749,17 +690,17 @@ class StableBrowser {
|
|
|
749
690
|
}
|
|
750
691
|
// info.log += "scanning locators in priority 1" + "\n";
|
|
751
692
|
let onlyPriority3 = selectorsLocators[0].priority === 3;
|
|
752
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["1"], scope, _params, info, visibleOnly);
|
|
693
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["1"], scope, _params, info, visibleOnly, allowDisabled);
|
|
753
694
|
if (result.foundElements.length === 0) {
|
|
754
695
|
// info.log += "scanning locators in priority 2" + "\n";
|
|
755
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["2"], scope, _params, info, visibleOnly);
|
|
696
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["2"], scope, _params, info, visibleOnly, allowDisabled);
|
|
756
697
|
}
|
|
757
698
|
if (result.foundElements.length === 0 && onlyPriority3) {
|
|
758
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly);
|
|
699
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly, allowDisabled);
|
|
759
700
|
}
|
|
760
701
|
else {
|
|
761
702
|
if (result.foundElements.length === 0 && !highPriorityOnly) {
|
|
762
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly);
|
|
703
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly, allowDisabled);
|
|
763
704
|
}
|
|
764
705
|
}
|
|
765
706
|
let foundElements = result.foundElements;
|
|
@@ -800,26 +741,38 @@ class StableBrowser {
|
|
|
800
741
|
return maxCountElement.locator;
|
|
801
742
|
}
|
|
802
743
|
}
|
|
803
|
-
if (
|
|
744
|
+
if (Date.now() - startTime > timeout) {
|
|
804
745
|
break;
|
|
805
746
|
}
|
|
806
|
-
if (
|
|
807
|
-
info.log += "high priority timeout, will try all elements" + "\n";
|
|
747
|
+
if (Date.now() - startTime > highPriorityTimeout) {
|
|
748
|
+
//info.log += "high priority timeout, will try all elements" + "\n";
|
|
808
749
|
highPriorityOnly = false;
|
|
750
|
+
if (this.configuration && this.configuration.load_all_lazy === true && !lazy_scroll) {
|
|
751
|
+
lazy_scroll = true;
|
|
752
|
+
await scrollPageToLoadLazyElements(this.page);
|
|
753
|
+
}
|
|
809
754
|
}
|
|
810
|
-
if (
|
|
811
|
-
info.log += "visible only timeout, will try all elements" + "\n";
|
|
755
|
+
if (Date.now() - startTime > visibleOnlyTimeout) {
|
|
756
|
+
//info.log += "visible only timeout, will try all elements" + "\n";
|
|
812
757
|
visibleOnly = false;
|
|
813
758
|
}
|
|
814
759
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
815
760
|
}
|
|
816
761
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
817
|
-
info.
|
|
762
|
+
// if (info.locatorLog) {
|
|
763
|
+
// const lines = info.locatorLog.toString().split("\n");
|
|
764
|
+
// for (let line of lines) {
|
|
765
|
+
// this.logger.debug(line);
|
|
766
|
+
// }
|
|
767
|
+
// }
|
|
768
|
+
//info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
|
|
818
769
|
info.failCause.locatorNotFound = true;
|
|
819
|
-
info
|
|
770
|
+
if (!info?.failCause?.lastError) {
|
|
771
|
+
info.failCause.lastError = "failed to locate unique element";
|
|
772
|
+
}
|
|
820
773
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
821
774
|
}
|
|
822
|
-
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
775
|
+
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly, allowDisabled = false) {
|
|
823
776
|
let foundElements = [];
|
|
824
777
|
const result = {
|
|
825
778
|
foundElements: foundElements,
|
|
@@ -827,14 +780,15 @@ class StableBrowser {
|
|
|
827
780
|
for (let i = 0; i < locatorsGroup.length; i++) {
|
|
828
781
|
let foundLocators = [];
|
|
829
782
|
try {
|
|
830
|
-
await this._collectLocatorInformation(locatorsGroup, i, scope, foundLocators, _params, info, visibleOnly);
|
|
783
|
+
await this._collectLocatorInformation(locatorsGroup, i, scope, foundLocators, _params, info, visibleOnly, allowDisabled);
|
|
831
784
|
}
|
|
832
785
|
catch (e) {
|
|
833
|
-
this
|
|
834
|
-
this.logger.debug(
|
|
786
|
+
// this call can fail it the browser is navigating
|
|
787
|
+
// this.logger.debug("unable to use locator " + JSON.stringify(locatorsGroup[i]));
|
|
788
|
+
// this.logger.debug(e);
|
|
835
789
|
foundLocators = [];
|
|
836
790
|
try {
|
|
837
|
-
await this._collectLocatorInformation(locatorsGroup, i, this.page, foundLocators, _params, info, visibleOnly);
|
|
791
|
+
await this._collectLocatorInformation(locatorsGroup, i, this.page, foundLocators, _params, info, visibleOnly, allowDisabled);
|
|
838
792
|
}
|
|
839
793
|
catch (e) {
|
|
840
794
|
this.logger.info("unable to use locator (second try) " + JSON.stringify(locatorsGroup[i]));
|
|
@@ -850,11 +804,27 @@ class StableBrowser {
|
|
|
850
804
|
}
|
|
851
805
|
if (foundLocators.length > 1) {
|
|
852
806
|
info.failCause.foundMultiple = true;
|
|
807
|
+
if (info.locatorLog) {
|
|
808
|
+
info.locatorLog.setLocatorSearchStatus(JSON.stringify(locatorsGroup[i]), "FOUND_NOT_UNIQUE");
|
|
809
|
+
}
|
|
853
810
|
}
|
|
854
811
|
}
|
|
855
812
|
return result;
|
|
856
813
|
}
|
|
857
814
|
async simpleClick(elementDescription, _params, options = {}, world = null) {
|
|
815
|
+
const state = {
|
|
816
|
+
locate: false,
|
|
817
|
+
scroll: false,
|
|
818
|
+
highlight: false,
|
|
819
|
+
_params,
|
|
820
|
+
options,
|
|
821
|
+
world,
|
|
822
|
+
type: Types.CLICK,
|
|
823
|
+
text: "Click element",
|
|
824
|
+
operation: "simpleClick",
|
|
825
|
+
log: "***** click on " + elementDescription + " *****\n",
|
|
826
|
+
};
|
|
827
|
+
_preCommand(state, this, world);
|
|
858
828
|
const startTime = Date.now();
|
|
859
829
|
let timeout = 30000;
|
|
860
830
|
if (options && options.timeout) {
|
|
@@ -879,13 +849,31 @@ class StableBrowser {
|
|
|
879
849
|
catch (e) {
|
|
880
850
|
if (performance.now() - startTime > timeout) {
|
|
881
851
|
// throw e;
|
|
882
|
-
|
|
852
|
+
try {
|
|
853
|
+
await _commandError(state, "timeout looking for " + elementDescription, this);
|
|
854
|
+
}
|
|
855
|
+
finally {
|
|
856
|
+
_commandFinally(state, this);
|
|
857
|
+
}
|
|
883
858
|
}
|
|
884
859
|
}
|
|
885
860
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
886
861
|
}
|
|
887
862
|
}
|
|
888
863
|
async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
|
|
864
|
+
const state = {
|
|
865
|
+
locate: false,
|
|
866
|
+
scroll: false,
|
|
867
|
+
highlight: false,
|
|
868
|
+
_params,
|
|
869
|
+
options,
|
|
870
|
+
world,
|
|
871
|
+
type: Types.FILL,
|
|
872
|
+
text: "Fill element",
|
|
873
|
+
operation: "simpleClickType",
|
|
874
|
+
log: "***** click type on " + elementDescription + " *****\n",
|
|
875
|
+
};
|
|
876
|
+
_preCommand(state, this, world);
|
|
889
877
|
const startTime = Date.now();
|
|
890
878
|
let timeout = 30000;
|
|
891
879
|
if (options && options.timeout) {
|
|
@@ -910,7 +898,12 @@ class StableBrowser {
|
|
|
910
898
|
catch (e) {
|
|
911
899
|
if (performance.now() - startTime > timeout) {
|
|
912
900
|
// throw e;
|
|
913
|
-
|
|
901
|
+
try {
|
|
902
|
+
await _commandError(state, "timeout looking for " + elementDescription, this);
|
|
903
|
+
}
|
|
904
|
+
finally {
|
|
905
|
+
_commandFinally(state, this);
|
|
906
|
+
}
|
|
914
907
|
}
|
|
915
908
|
}
|
|
916
909
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
@@ -928,19 +921,19 @@ class StableBrowser {
|
|
|
928
921
|
log: "***** click on " + selectors.element_name + " *****\n",
|
|
929
922
|
};
|
|
930
923
|
try {
|
|
931
|
-
await _preCommand(state, this);
|
|
932
|
-
if (state.options && state.options.context) {
|
|
933
|
-
|
|
934
|
-
}
|
|
924
|
+
await _preCommand(state, this, world);
|
|
925
|
+
// if (state.options && state.options.context) {
|
|
926
|
+
// state.selectors.locators[0].text = state.options.context;
|
|
927
|
+
// }
|
|
935
928
|
try {
|
|
936
929
|
await state.element.click();
|
|
937
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
930
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
938
931
|
}
|
|
939
932
|
catch (e) {
|
|
940
933
|
// await this.closeUnexpectedPopups();
|
|
941
934
|
state.element = await this._locate(selectors, state.info, _params);
|
|
942
935
|
await state.element.dispatchEvent("click");
|
|
943
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
936
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
944
937
|
}
|
|
945
938
|
await this.waitForPageLoad();
|
|
946
939
|
return state.info;
|
|
@@ -964,14 +957,20 @@ class StableBrowser {
|
|
|
964
957
|
log: "***** check " + selectors.element_name + " *****\n",
|
|
965
958
|
};
|
|
966
959
|
try {
|
|
967
|
-
await _preCommand(state, this);
|
|
960
|
+
await _preCommand(state, this, world);
|
|
968
961
|
state.info.checked = checked;
|
|
969
962
|
// let element = await this._locate(selectors, info, _params);
|
|
970
963
|
// ({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
971
964
|
try {
|
|
972
|
-
|
|
965
|
+
if (world && world.screenshot && !world.screenshotPath) {
|
|
966
|
+
// console.log(`Highlighting while running from recorder`);
|
|
967
|
+
await this._highlightElements(element);
|
|
968
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
969
|
+
await this._unHighlightElements(element);
|
|
970
|
+
}
|
|
973
971
|
await state.element.setChecked(checked);
|
|
974
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
972
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
973
|
+
// await this._unHighlightElements(element);
|
|
975
974
|
}
|
|
976
975
|
catch (e) {
|
|
977
976
|
if (e.message && e.message.includes("did not change its state")) {
|
|
@@ -1007,9 +1006,10 @@ class StableBrowser {
|
|
|
1007
1006
|
log: "***** hover " + selectors.element_name + " *****\n",
|
|
1008
1007
|
};
|
|
1009
1008
|
try {
|
|
1010
|
-
await _preCommand(state, this);
|
|
1009
|
+
await _preCommand(state, this, world);
|
|
1011
1010
|
try {
|
|
1012
1011
|
await state.element.hover();
|
|
1012
|
+
await _screenshot(state, this);
|
|
1013
1013
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1014
1014
|
}
|
|
1015
1015
|
catch (e) {
|
|
@@ -1017,9 +1017,10 @@ class StableBrowser {
|
|
|
1017
1017
|
state.info.log += "hover failed, will try again" + "\n";
|
|
1018
1018
|
state.element = await this._locate(selectors, state.info, _params);
|
|
1019
1019
|
await state.element.hover({ timeout: 10000 });
|
|
1020
|
+
await _screenshot(state, this);
|
|
1020
1021
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1021
1022
|
}
|
|
1022
|
-
await _screenshot(state, this);
|
|
1023
|
+
// await _screenshot(state, this);
|
|
1023
1024
|
await this.waitForPageLoad();
|
|
1024
1025
|
return state.info;
|
|
1025
1026
|
}
|
|
@@ -1046,7 +1047,7 @@ class StableBrowser {
|
|
|
1046
1047
|
log: "***** select option " + selectors.element_name + " *****\n",
|
|
1047
1048
|
};
|
|
1048
1049
|
try {
|
|
1049
|
-
await _preCommand(state, this);
|
|
1050
|
+
await _preCommand(state, this, world);
|
|
1050
1051
|
try {
|
|
1051
1052
|
await state.element.selectOption(values);
|
|
1052
1053
|
}
|
|
@@ -1080,7 +1081,7 @@ class StableBrowser {
|
|
|
1080
1081
|
log: "",
|
|
1081
1082
|
};
|
|
1082
1083
|
try {
|
|
1083
|
-
await _preCommand(state, this);
|
|
1084
|
+
await _preCommand(state, this, world);
|
|
1084
1085
|
const valueSegment = state.value.split("&&");
|
|
1085
1086
|
for (let i = 0; i < valueSegment.length; i++) {
|
|
1086
1087
|
if (i > 0) {
|
|
@@ -1123,7 +1124,7 @@ class StableBrowser {
|
|
|
1123
1124
|
log: "***** set input value " + selectors.element_name + " *****\n",
|
|
1124
1125
|
};
|
|
1125
1126
|
try {
|
|
1126
|
-
await _preCommand(state, this);
|
|
1127
|
+
await _preCommand(state, this, world);
|
|
1127
1128
|
let value = await this._replaceWithLocalData(state.value, this);
|
|
1128
1129
|
try {
|
|
1129
1130
|
await state.element.evaluateHandle((el, value) => {
|
|
@@ -1160,7 +1161,7 @@ class StableBrowser {
|
|
|
1160
1161
|
throwError: false,
|
|
1161
1162
|
};
|
|
1162
1163
|
try {
|
|
1163
|
-
await _preCommand(state, this);
|
|
1164
|
+
await _preCommand(state, this, world);
|
|
1164
1165
|
try {
|
|
1165
1166
|
await state.element.click();
|
|
1166
1167
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
@@ -1234,7 +1235,7 @@ class StableBrowser {
|
|
|
1234
1235
|
_value = newValue;
|
|
1235
1236
|
}
|
|
1236
1237
|
try {
|
|
1237
|
-
await _preCommand(state, this);
|
|
1238
|
+
await _preCommand(state, this, world);
|
|
1238
1239
|
state.info.value = _value;
|
|
1239
1240
|
if (options === null || options === undefined || !options.press) {
|
|
1240
1241
|
try {
|
|
@@ -1291,7 +1292,12 @@ class StableBrowser {
|
|
|
1291
1292
|
await this.waitForPageLoad();
|
|
1292
1293
|
}
|
|
1293
1294
|
else if (enter === false) {
|
|
1294
|
-
|
|
1295
|
+
try {
|
|
1296
|
+
await state.element.dispatchEvent("change", null, { timeout: 5000 });
|
|
1297
|
+
}
|
|
1298
|
+
catch (e) {
|
|
1299
|
+
// ignore
|
|
1300
|
+
}
|
|
1295
1301
|
//await this.page.keyboard.press("Tab");
|
|
1296
1302
|
}
|
|
1297
1303
|
else {
|
|
@@ -1322,7 +1328,7 @@ class StableBrowser {
|
|
|
1322
1328
|
log: "***** fill on " + selectors.element_name + " with value " + value + "*****\n",
|
|
1323
1329
|
};
|
|
1324
1330
|
try {
|
|
1325
|
-
await _preCommand(state, this);
|
|
1331
|
+
await _preCommand(state, this, world);
|
|
1326
1332
|
await state.element.fill(value);
|
|
1327
1333
|
await state.element.dispatchEvent("change");
|
|
1328
1334
|
if (enter) {
|
|
@@ -1348,6 +1354,7 @@ class StableBrowser {
|
|
|
1348
1354
|
let screenshotPath = null;
|
|
1349
1355
|
if (!info.log) {
|
|
1350
1356
|
info.log = "";
|
|
1357
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
1351
1358
|
}
|
|
1352
1359
|
info.operation = "getText";
|
|
1353
1360
|
info.selectors = selectors;
|
|
@@ -1367,9 +1374,19 @@ class StableBrowser {
|
|
|
1367
1374
|
catch (e) {
|
|
1368
1375
|
//ignore
|
|
1369
1376
|
}
|
|
1370
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
1377
|
+
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info, element));
|
|
1371
1378
|
try {
|
|
1372
|
-
|
|
1379
|
+
if (world && world.screenshot && !world.screenshotPath) {
|
|
1380
|
+
// console.log(`Highlighting for get text while running from recorder`);
|
|
1381
|
+
this._highlightElements(element)
|
|
1382
|
+
.then(async () => {
|
|
1383
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1384
|
+
this._unhighlightElements(element).then(() => { }
|
|
1385
|
+
// console.log(`Unhighlighting vrtr in recorder is successful`)
|
|
1386
|
+
);
|
|
1387
|
+
})
|
|
1388
|
+
.catch(e);
|
|
1389
|
+
}
|
|
1373
1390
|
const elementText = await element.innerText();
|
|
1374
1391
|
return {
|
|
1375
1392
|
text: elementText,
|
|
@@ -1381,7 +1398,7 @@ class StableBrowser {
|
|
|
1381
1398
|
}
|
|
1382
1399
|
catch (e) {
|
|
1383
1400
|
//await this.closeUnexpectedPopups();
|
|
1384
|
-
this.logger.info("no innerText will use textContent");
|
|
1401
|
+
this.logger.info("no innerText, will use textContent");
|
|
1385
1402
|
const elementText = await element.textContent();
|
|
1386
1403
|
return { text: elementText, screenshotId, screenshotPath, value: value };
|
|
1387
1404
|
}
|
|
@@ -1416,13 +1433,13 @@ class StableBrowser {
|
|
|
1416
1433
|
}
|
|
1417
1434
|
let foundObj = null;
|
|
1418
1435
|
try {
|
|
1419
|
-
await _preCommand(state, this);
|
|
1436
|
+
await _preCommand(state, this, world);
|
|
1420
1437
|
state.info.pattern = pattern;
|
|
1421
1438
|
foundObj = await this._getText(selectors, 0, _params, options, state.info, world);
|
|
1422
1439
|
if (foundObj && foundObj.element) {
|
|
1423
1440
|
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1424
1441
|
}
|
|
1425
|
-
await _screenshot(state, this);
|
|
1442
|
+
await _screenshot(state, this, foundObj.element);
|
|
1426
1443
|
let escapedText = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
1427
1444
|
pattern = pattern.replace("{text}", escapedText);
|
|
1428
1445
|
let regex = new RegExp(pattern, "im");
|
|
@@ -1467,12 +1484,12 @@ class StableBrowser {
|
|
|
1467
1484
|
}
|
|
1468
1485
|
let foundObj = null;
|
|
1469
1486
|
try {
|
|
1470
|
-
await _preCommand(state, this);
|
|
1487
|
+
await _preCommand(state, this, world);
|
|
1471
1488
|
foundObj = await this._getText(selectors, climb, _params, options, state.info, world);
|
|
1472
1489
|
if (foundObj && foundObj.element) {
|
|
1473
1490
|
await this.scrollIfNeeded(foundObj.element, state.info);
|
|
1474
1491
|
}
|
|
1475
|
-
await _screenshot(state, this);
|
|
1492
|
+
await _screenshot(state, this, foundObj.element);
|
|
1476
1493
|
const dateAlternatives = findDateAlternatives(text);
|
|
1477
1494
|
const numberAlternatives = findNumberAlternatives(text);
|
|
1478
1495
|
if (dateAlternatives.date) {
|
|
@@ -1661,7 +1678,7 @@ class StableBrowser {
|
|
|
1661
1678
|
}
|
|
1662
1679
|
return data;
|
|
1663
1680
|
}
|
|
1664
|
-
async _screenShot(options = {}, world = null, info = null) {
|
|
1681
|
+
async _screenShot(options = {}, world = null, info = null, focusedElement = null) {
|
|
1665
1682
|
// collect url/path/title
|
|
1666
1683
|
if (info) {
|
|
1667
1684
|
if (!info.title) {
|
|
@@ -1686,13 +1703,11 @@ class StableBrowser {
|
|
|
1686
1703
|
if (!fs.existsSync(world.screenshotPath)) {
|
|
1687
1704
|
fs.mkdirSync(world.screenshotPath, { recursive: true });
|
|
1688
1705
|
}
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
}
|
|
1693
|
-
const screenshotPath = path.join(world.screenshotPath, nextIndex + ".png");
|
|
1706
|
+
// to make sure the path doesn't start with -
|
|
1707
|
+
const uuidStr = "id_" + randomUUID();
|
|
1708
|
+
const screenshotPath = path.join(world.screenshotPath, uuidStr + ".png");
|
|
1694
1709
|
try {
|
|
1695
|
-
await this.takeScreenshot(screenshotPath);
|
|
1710
|
+
await this.takeScreenshot(screenshotPath, focusedElement);
|
|
1696
1711
|
// let buffer = await this.page.screenshot({ timeout: 4000 });
|
|
1697
1712
|
// // save the buffer to the screenshot path asynchrously
|
|
1698
1713
|
// fs.writeFile(screenshotPath, buffer, (err) => {
|
|
@@ -1700,20 +1715,20 @@ class StableBrowser {
|
|
|
1700
1715
|
// this.logger.info("unable to save screenshot " + screenshotPath);
|
|
1701
1716
|
// }
|
|
1702
1717
|
// });
|
|
1718
|
+
result.screenshotId = uuidStr;
|
|
1719
|
+
result.screenshotPath = screenshotPath;
|
|
1720
|
+
if (info && info.box) {
|
|
1721
|
+
// await drawRectangle(screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
|
|
1722
|
+
}
|
|
1703
1723
|
}
|
|
1704
1724
|
catch (e) {
|
|
1705
1725
|
this.logger.info("unable to take screenshot, ignored");
|
|
1706
1726
|
}
|
|
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
1727
|
}
|
|
1713
1728
|
else if (options && options.screenshot) {
|
|
1714
1729
|
result.screenshotPath = options.screenshotPath;
|
|
1715
1730
|
try {
|
|
1716
|
-
await this.takeScreenshot(options.screenshotPath);
|
|
1731
|
+
await this.takeScreenshot(options.screenshotPath, focusedElement);
|
|
1717
1732
|
// let buffer = await this.page.screenshot({ timeout: 4000 });
|
|
1718
1733
|
// // save the buffer to the screenshot path asynchrously
|
|
1719
1734
|
// fs.writeFile(options.screenshotPath, buffer, (err) => {
|
|
@@ -1726,12 +1741,12 @@ class StableBrowser {
|
|
|
1726
1741
|
this.logger.info("unable to take screenshot, ignored");
|
|
1727
1742
|
}
|
|
1728
1743
|
if (info && info.box) {
|
|
1729
|
-
await drawRectangle(options.screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
|
|
1744
|
+
// await drawRectangle(options.screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
|
|
1730
1745
|
}
|
|
1731
1746
|
}
|
|
1732
1747
|
return result;
|
|
1733
1748
|
}
|
|
1734
|
-
async takeScreenshot(screenshotPath) {
|
|
1749
|
+
async takeScreenshot(screenshotPath, focusedElement = null) {
|
|
1735
1750
|
const playContext = this.context.playContext;
|
|
1736
1751
|
// Using CDP to capture the screenshot
|
|
1737
1752
|
const viewportWidth = Math.max(...(await this.page.evaluate(() => [
|
|
@@ -1743,6 +1758,15 @@ class StableBrowser {
|
|
|
1743
1758
|
document.documentElement.clientWidth,
|
|
1744
1759
|
])));
|
|
1745
1760
|
let screenshotBuffer = null;
|
|
1761
|
+
// if (focusedElement) {
|
|
1762
|
+
// // console.log(`Focused element ${JSON.stringify(focusedElement._selector)}`)
|
|
1763
|
+
// await this._unhighlightElements(focusedElement);
|
|
1764
|
+
// await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1765
|
+
// console.log(`Unhighlighted previous element`);
|
|
1766
|
+
// }
|
|
1767
|
+
if (focusedElement) {
|
|
1768
|
+
await this._highlightElements(focusedElement);
|
|
1769
|
+
}
|
|
1746
1770
|
if (this.context.browserName === "chromium") {
|
|
1747
1771
|
const client = await playContext.newCDPSession(this.page);
|
|
1748
1772
|
const { data } = await client.send("Page.captureScreenshot", {
|
|
@@ -1764,6 +1788,10 @@ class StableBrowser {
|
|
|
1764
1788
|
else {
|
|
1765
1789
|
screenshotBuffer = await this.page.screenshot();
|
|
1766
1790
|
}
|
|
1791
|
+
if (focusedElement) {
|
|
1792
|
+
// console.log(`Focused element ${JSON.stringify(focusedElement._selector)}`)
|
|
1793
|
+
await this._unhighlightElements(focusedElement);
|
|
1794
|
+
}
|
|
1767
1795
|
let image = await Jimp.read(screenshotBuffer);
|
|
1768
1796
|
// Get the image dimensions
|
|
1769
1797
|
const { width, height } = image.bitmap;
|
|
@@ -1790,7 +1818,7 @@ class StableBrowser {
|
|
|
1790
1818
|
};
|
|
1791
1819
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1792
1820
|
try {
|
|
1793
|
-
await _preCommand(state, this);
|
|
1821
|
+
await _preCommand(state, this, world);
|
|
1794
1822
|
await expect(state.element).toHaveCount(1, { timeout: 10000 });
|
|
1795
1823
|
return state.info;
|
|
1796
1824
|
}
|
|
@@ -1816,7 +1844,7 @@ class StableBrowser {
|
|
|
1816
1844
|
};
|
|
1817
1845
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1818
1846
|
try {
|
|
1819
|
-
await _preCommand(state, this);
|
|
1847
|
+
await _preCommand(state, this, world);
|
|
1820
1848
|
switch (attribute) {
|
|
1821
1849
|
case "inner_text":
|
|
1822
1850
|
state.value = await state.element.innerText();
|
|
@@ -1834,6 +1862,70 @@ class StableBrowser {
|
|
|
1834
1862
|
state.info.value = state.value;
|
|
1835
1863
|
this.setTestData({ [variable]: state.value }, world);
|
|
1836
1864
|
this.logger.info("set test data: " + variable + "=" + state.value);
|
|
1865
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1866
|
+
return state.info;
|
|
1867
|
+
}
|
|
1868
|
+
catch (e) {
|
|
1869
|
+
await _commandError(state, e, this);
|
|
1870
|
+
}
|
|
1871
|
+
finally {
|
|
1872
|
+
_commandFinally(state, this);
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
async verifyAttribute(selectors, attribute, value, _params = null, options = {}, world = null) {
|
|
1876
|
+
const state = {
|
|
1877
|
+
selectors,
|
|
1878
|
+
_params,
|
|
1879
|
+
attribute,
|
|
1880
|
+
value,
|
|
1881
|
+
options,
|
|
1882
|
+
world,
|
|
1883
|
+
type: Types.VERIFY_ATTRIBUTE,
|
|
1884
|
+
highlight: true,
|
|
1885
|
+
screenshot: true,
|
|
1886
|
+
text: `Verify element attribute`,
|
|
1887
|
+
operation: "verifyAttribute",
|
|
1888
|
+
log: "***** verify attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1889
|
+
allowDisabled: true,
|
|
1890
|
+
};
|
|
1891
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1892
|
+
let val;
|
|
1893
|
+
try {
|
|
1894
|
+
await _preCommand(state, this, world);
|
|
1895
|
+
switch (attribute) {
|
|
1896
|
+
case "innerText":
|
|
1897
|
+
val = String(await state.element.innerText());
|
|
1898
|
+
break;
|
|
1899
|
+
case "value":
|
|
1900
|
+
val = String(await state.element.inputValue());
|
|
1901
|
+
break;
|
|
1902
|
+
case "checked":
|
|
1903
|
+
val = String(await state.element.isChecked());
|
|
1904
|
+
break;
|
|
1905
|
+
case "disabled":
|
|
1906
|
+
val = String(await state.element.isDisabled());
|
|
1907
|
+
break;
|
|
1908
|
+
case "readOnly":
|
|
1909
|
+
const isEditable = await state.element.isEditable();
|
|
1910
|
+
val = String(!isEditable);
|
|
1911
|
+
break;
|
|
1912
|
+
default:
|
|
1913
|
+
val = String(await state.element.getAttribute(attribute));
|
|
1914
|
+
break;
|
|
1915
|
+
}
|
|
1916
|
+
state.info.expectedValue = val;
|
|
1917
|
+
let regex;
|
|
1918
|
+
if (value.startsWith("/") && value.endsWith("/")) {
|
|
1919
|
+
const patternBody = value.slice(1, -1);
|
|
1920
|
+
regex = new RegExp(patternBody, "g");
|
|
1921
|
+
}
|
|
1922
|
+
else {
|
|
1923
|
+
const escapedPattern = value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1924
|
+
regex = new RegExp(escapedPattern, "g");
|
|
1925
|
+
}
|
|
1926
|
+
if (!val.match(regex)) {
|
|
1927
|
+
throw new Error(`The ${attribute} attribute has a value of "${val}", but the expected value is "${value}"`);
|
|
1928
|
+
}
|
|
1837
1929
|
return state.info;
|
|
1838
1930
|
}
|
|
1839
1931
|
catch (e) {
|
|
@@ -1861,7 +1953,7 @@ class StableBrowser {
|
|
|
1861
1953
|
if (options && options.timeout) {
|
|
1862
1954
|
timeout = options.timeout;
|
|
1863
1955
|
}
|
|
1864
|
-
const serviceUrl =
|
|
1956
|
+
const serviceUrl = _getServerUrl() + "/api/mail/createLinkOrCodeFromEmail";
|
|
1865
1957
|
const request = {
|
|
1866
1958
|
method: "POST",
|
|
1867
1959
|
url: serviceUrl,
|
|
@@ -1932,27 +2024,33 @@ class StableBrowser {
|
|
|
1932
2024
|
async _highlightElements(scope, css) {
|
|
1933
2025
|
try {
|
|
1934
2026
|
if (!scope) {
|
|
2027
|
+
// console.log(`Scope is not defined`);
|
|
1935
2028
|
return;
|
|
1936
2029
|
}
|
|
1937
2030
|
if (!css) {
|
|
1938
2031
|
scope
|
|
1939
2032
|
.evaluate((node) => {
|
|
1940
2033
|
if (node && node.style) {
|
|
1941
|
-
let
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
}
|
|
2034
|
+
let originalOutline = node.style.outline;
|
|
2035
|
+
// console.log(`Original outline was: ${originalOutline}`);
|
|
2036
|
+
node.__previousOutline = originalOutline;
|
|
2037
|
+
node.style.outline = "2px solid red";
|
|
2038
|
+
// console.log(`New outline is: ${node.style.outline}`);
|
|
2039
|
+
// if (window) {
|
|
2040
|
+
// window.addEventListener("beforeunload", function (e) {
|
|
2041
|
+
// node.style.outline = originalBorder;
|
|
2042
|
+
// });
|
|
2043
|
+
// }
|
|
2044
|
+
// setTimeout(function () {
|
|
2045
|
+
// node.style.outline = originalBorder;
|
|
2046
|
+
//}, 2000);
|
|
1951
2047
|
}
|
|
1952
2048
|
})
|
|
1953
|
-
.then(() => {
|
|
2049
|
+
.then(() => {
|
|
2050
|
+
})
|
|
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,27 @@ 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
|
-
.then(() => {
|
|
2085
|
+
.then(() => {
|
|
2086
|
+
})
|
|
1987
2087
|
.catch((e) => {
|
|
1988
2088
|
// ignore
|
|
2089
|
+
// console.error(`Could not highlight css: ${e}`);
|
|
1989
2090
|
});
|
|
1990
2091
|
}
|
|
1991
2092
|
}
|
|
@@ -1993,6 +2094,60 @@ class StableBrowser {
|
|
|
1993
2094
|
console.debug(error);
|
|
1994
2095
|
}
|
|
1995
2096
|
}
|
|
2097
|
+
async _unhighlightElements(scope, css) {
|
|
2098
|
+
try {
|
|
2099
|
+
if (!scope) {
|
|
2100
|
+
return;
|
|
2101
|
+
}
|
|
2102
|
+
if (!css) {
|
|
2103
|
+
scope
|
|
2104
|
+
.evaluate((node) => {
|
|
2105
|
+
if (node && node.style) {
|
|
2106
|
+
if (!node.__previousOutline) {
|
|
2107
|
+
node.style.outline = "";
|
|
2108
|
+
}
|
|
2109
|
+
else {
|
|
2110
|
+
node.style.outline = node.__previousOutline;
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
})
|
|
2114
|
+
.then(() => {
|
|
2115
|
+
})
|
|
2116
|
+
.catch((e) => {
|
|
2117
|
+
// console.log(`Error while unhighlighting node ${JSON.stringify(scope)}: ${e}`);
|
|
2118
|
+
});
|
|
2119
|
+
}
|
|
2120
|
+
else {
|
|
2121
|
+
scope
|
|
2122
|
+
.evaluate(([css]) => {
|
|
2123
|
+
if (!css) {
|
|
2124
|
+
return;
|
|
2125
|
+
}
|
|
2126
|
+
let elements = Array.from(document.querySelectorAll(css));
|
|
2127
|
+
for (i = 0; i < elements.length; i++) {
|
|
2128
|
+
let element = elements[i];
|
|
2129
|
+
if (!element.style) {
|
|
2130
|
+
return;
|
|
2131
|
+
}
|
|
2132
|
+
if (!element.__previousOutline) {
|
|
2133
|
+
element.style.outline = "";
|
|
2134
|
+
}
|
|
2135
|
+
else {
|
|
2136
|
+
element.style.outline = element.__previousOutline;
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
})
|
|
2140
|
+
.then(() => {
|
|
2141
|
+
})
|
|
2142
|
+
.catch((e) => {
|
|
2143
|
+
// console.error(`Error while unhighlighting element in css: ${e}`);
|
|
2144
|
+
});
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
catch (error) {
|
|
2148
|
+
// console.debug(error);
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
1996
2151
|
async verifyPagePath(pathPart, options = {}, world = null) {
|
|
1997
2152
|
const startTime = Date.now();
|
|
1998
2153
|
let error = null;
|
|
@@ -2034,7 +2189,7 @@ class StableBrowser {
|
|
|
2034
2189
|
}
|
|
2035
2190
|
finally {
|
|
2036
2191
|
const endTime = Date.now();
|
|
2037
|
-
|
|
2192
|
+
_reportToWorld(world, {
|
|
2038
2193
|
type: Types.VERIFY_PAGE_PATH,
|
|
2039
2194
|
text: "Verify page path",
|
|
2040
2195
|
screenshotId,
|
|
@@ -2054,6 +2209,35 @@ class StableBrowser {
|
|
|
2054
2209
|
});
|
|
2055
2210
|
}
|
|
2056
2211
|
}
|
|
2212
|
+
async findTextInAllFrames(dateAlternatives, numberAlternatives, text, state) {
|
|
2213
|
+
const frames = this.page.frames();
|
|
2214
|
+
let results = [];
|
|
2215
|
+
let ignoreCase = false;
|
|
2216
|
+
for (let i = 0; i < frames.length; i++) {
|
|
2217
|
+
if (dateAlternatives.date) {
|
|
2218
|
+
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2219
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2220
|
+
result.frame = frames[i];
|
|
2221
|
+
results.push(result);
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
else if (numberAlternatives.number) {
|
|
2225
|
+
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2226
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2227
|
+
result.frame = frames[i];
|
|
2228
|
+
results.push(result);
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
else {
|
|
2232
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2233
|
+
result.frame = frames[i];
|
|
2234
|
+
results.push(result);
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
state.info.results = results;
|
|
2238
|
+
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2239
|
+
return resultWithElementsFound;
|
|
2240
|
+
}
|
|
2057
2241
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2058
2242
|
text = unEscapeString(text);
|
|
2059
2243
|
const state = {
|
|
@@ -2063,7 +2247,7 @@ class StableBrowser {
|
|
|
2063
2247
|
locate: false,
|
|
2064
2248
|
scroll: false,
|
|
2065
2249
|
highlight: false,
|
|
2066
|
-
type: Types.
|
|
2250
|
+
type: Types.VERIFY_PAGE_CONTAINS_TEXT,
|
|
2067
2251
|
text: `Verify text exists in page`,
|
|
2068
2252
|
operation: "verifyTextExistInPage",
|
|
2069
2253
|
log: "***** verify text " + text + " exists in page *****\n",
|
|
@@ -2078,34 +2262,10 @@ class StableBrowser {
|
|
|
2078
2262
|
let dateAlternatives = findDateAlternatives(text);
|
|
2079
2263
|
let numberAlternatives = findNumberAlternatives(text);
|
|
2080
2264
|
try {
|
|
2081
|
-
await _preCommand(state, this);
|
|
2265
|
+
await _preCommand(state, this, world);
|
|
2082
2266
|
state.info.text = text;
|
|
2083
2267
|
while (true) {
|
|
2084
|
-
const
|
|
2085
|
-
let results = [];
|
|
2086
|
-
for (let i = 0; i < frames.length; i++) {
|
|
2087
|
-
if (dateAlternatives.date) {
|
|
2088
|
-
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2089
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2090
|
-
result.frame = frames[i];
|
|
2091
|
-
results.push(result);
|
|
2092
|
-
}
|
|
2093
|
-
}
|
|
2094
|
-
else if (numberAlternatives.number) {
|
|
2095
|
-
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2096
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2097
|
-
result.frame = frames[i];
|
|
2098
|
-
results.push(result);
|
|
2099
|
-
}
|
|
2100
|
-
}
|
|
2101
|
-
else {
|
|
2102
|
-
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2103
|
-
result.frame = frames[i];
|
|
2104
|
-
results.push(result);
|
|
2105
|
-
}
|
|
2106
|
-
}
|
|
2107
|
-
state.info.results = results;
|
|
2108
|
-
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2268
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, text, state);
|
|
2109
2269
|
if (resultWithElementsFound.length === 0) {
|
|
2110
2270
|
if (Date.now() - state.startTime > timeout) {
|
|
2111
2271
|
throw new Error(`Text ${text} not found in page`);
|
|
@@ -2115,15 +2275,32 @@ class StableBrowser {
|
|
|
2115
2275
|
}
|
|
2116
2276
|
if (resultWithElementsFound[0].randomToken) {
|
|
2117
2277
|
const frame = resultWithElementsFound[0].frame;
|
|
2118
|
-
const dataAttribute = `[data-blinq-id
|
|
2119
|
-
|
|
2120
|
-
|
|
2278
|
+
const dataAttribute = `[data-blinq-id-${resultWithElementsFound[0].randomToken}]`;
|
|
2279
|
+
if (world && world.screenshot && !world.screenshotPath) {
|
|
2280
|
+
// console.log(`Highlighting for verify text is found while running from recorder`);
|
|
2281
|
+
this._highlightElements(frame, dataAttribute).then(async () => {
|
|
2282
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2283
|
+
this._unhighlightElements(frame, dataAttribute)
|
|
2284
|
+
.then(async () => {
|
|
2285
|
+
// console.log(`Unhighlighted frame dataAttribute successfully`);
|
|
2286
|
+
})
|
|
2287
|
+
.catch((e) => { }
|
|
2288
|
+
// console.error(e)
|
|
2289
|
+
);
|
|
2290
|
+
});
|
|
2291
|
+
}
|
|
2292
|
+
const element = await frame.locator(dataAttribute).first();
|
|
2293
|
+
// await new Promise((resolve) => setTimeout(resolve, 100));
|
|
2294
|
+
// await this._unhighlightElements(frame, dataAttribute);
|
|
2121
2295
|
if (element) {
|
|
2122
2296
|
await this.scrollIfNeeded(element, state.info);
|
|
2123
2297
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2298
|
+
await _screenshot(state, this, element);
|
|
2124
2299
|
}
|
|
2125
2300
|
}
|
|
2126
|
-
|
|
2301
|
+
else {
|
|
2302
|
+
await _screenshot(state, this);
|
|
2303
|
+
}
|
|
2127
2304
|
return state.info;
|
|
2128
2305
|
}
|
|
2129
2306
|
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
@@ -2159,34 +2336,10 @@ class StableBrowser {
|
|
|
2159
2336
|
let dateAlternatives = findDateAlternatives(text);
|
|
2160
2337
|
let numberAlternatives = findNumberAlternatives(text);
|
|
2161
2338
|
try {
|
|
2162
|
-
await _preCommand(state, this);
|
|
2339
|
+
await _preCommand(state, this, world);
|
|
2163
2340
|
state.info.text = text;
|
|
2164
2341
|
while (true) {
|
|
2165
|
-
const
|
|
2166
|
-
let results = [];
|
|
2167
|
-
for (let i = 0; i < frames.length; i++) {
|
|
2168
|
-
if (dateAlternatives.date) {
|
|
2169
|
-
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2170
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2171
|
-
result.frame = frames[i];
|
|
2172
|
-
results.push(result);
|
|
2173
|
-
}
|
|
2174
|
-
}
|
|
2175
|
-
else if (numberAlternatives.number) {
|
|
2176
|
-
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2177
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2178
|
-
result.frame = frames[i];
|
|
2179
|
-
results.push(result);
|
|
2180
|
-
}
|
|
2181
|
-
}
|
|
2182
|
-
else {
|
|
2183
|
-
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2184
|
-
result.frame = frames[i];
|
|
2185
|
-
results.push(result);
|
|
2186
|
-
}
|
|
2187
|
-
}
|
|
2188
|
-
state.info.results = results;
|
|
2189
|
-
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2342
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, text, state);
|
|
2190
2343
|
if (resultWithElementsFound.length === 0) {
|
|
2191
2344
|
await _screenshot(state, this);
|
|
2192
2345
|
return state.info;
|
|
@@ -2204,15 +2357,103 @@ class StableBrowser {
|
|
|
2204
2357
|
_commandFinally(state, this);
|
|
2205
2358
|
}
|
|
2206
2359
|
}
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2360
|
+
async verifyTextRelatedToText(textAnchor, climb, textToVerify, options = {}, world = null) {
|
|
2361
|
+
textAnchor = unEscapeString(textAnchor);
|
|
2362
|
+
textToVerify = unEscapeString(textToVerify);
|
|
2363
|
+
const state = {
|
|
2364
|
+
text_search: textToVerify,
|
|
2365
|
+
options,
|
|
2366
|
+
world,
|
|
2367
|
+
locate: false,
|
|
2368
|
+
scroll: false,
|
|
2369
|
+
highlight: false,
|
|
2370
|
+
type: Types.VERIFY_TEXT_WITH_RELATION,
|
|
2371
|
+
text: `Verify text with relation to another text`,
|
|
2372
|
+
operation: "verify_text_with_relation",
|
|
2373
|
+
log: "***** search for " + textAnchor + " climb " + climb + " and verify " + textToVerify + " found *****\n",
|
|
2374
|
+
};
|
|
2375
|
+
const timeout = this._getLoadTimeout(options);
|
|
2376
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2377
|
+
let newValue = await this._replaceWithLocalData(textAnchor, world);
|
|
2378
|
+
if (newValue !== textAnchor) {
|
|
2379
|
+
this.logger.info(textAnchor + "=" + newValue);
|
|
2380
|
+
textAnchor = newValue;
|
|
2381
|
+
}
|
|
2382
|
+
newValue = await this._replaceWithLocalData(textToVerify, world);
|
|
2383
|
+
if (newValue !== textToVerify) {
|
|
2384
|
+
this.logger.info(textToVerify + "=" + newValue);
|
|
2385
|
+
textToVerify = newValue;
|
|
2386
|
+
}
|
|
2387
|
+
let dateAlternatives = findDateAlternatives(textToVerify);
|
|
2388
|
+
let numberAlternatives = findNumberAlternatives(textToVerify);
|
|
2389
|
+
let foundAncore = false;
|
|
2390
|
+
try {
|
|
2391
|
+
await _preCommand(state, this, world);
|
|
2392
|
+
state.info.text = textToVerify;
|
|
2393
|
+
while (true) {
|
|
2394
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, textAnchor, state);
|
|
2395
|
+
if (resultWithElementsFound.length === 0) {
|
|
2396
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2397
|
+
throw new Error(`Text ${foundAncore ? textToVerify : textAnchor} not found in page`);
|
|
2398
|
+
}
|
|
2399
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2400
|
+
continue;
|
|
2401
|
+
}
|
|
2402
|
+
for (let i = 0; i < resultWithElementsFound.length; i++) {
|
|
2403
|
+
foundAncore = true;
|
|
2404
|
+
const result = resultWithElementsFound[i];
|
|
2405
|
+
const token = result.randomToken;
|
|
2406
|
+
const frame = result.frame;
|
|
2407
|
+
let css = `[data-blinq-id-${token}]`;
|
|
2408
|
+
const climbArray1 = [];
|
|
2409
|
+
for (let i = 0; i < climb; i++) {
|
|
2410
|
+
climbArray1.push("..");
|
|
2411
|
+
}
|
|
2412
|
+
let climbXpath = "xpath=" + climbArray1.join("/");
|
|
2413
|
+
css = css + " >> " + climbXpath;
|
|
2414
|
+
const count = await frame.locator(css).count();
|
|
2415
|
+
for (let j = 0; j < count; j++) {
|
|
2416
|
+
const continer = await frame.locator(css).nth(j);
|
|
2417
|
+
const result = await this._locateElementByText(continer, textToVerify, "*", false, true, true, {});
|
|
2418
|
+
if (result.elementCount > 0) {
|
|
2419
|
+
const dataAttribute = "[data-blinq-id-" + result.randomToken + "]";
|
|
2420
|
+
//const cssAnchor = `[data-blinq-id="blinq-id-${token}-anchor"]`;
|
|
2421
|
+
if (world && world.screenshot && !world.screenshotPath) {
|
|
2422
|
+
// console.log(`Highlighting for vtrt while running from recorder`);
|
|
2423
|
+
this._highlightElements(frame, dataAttribute)
|
|
2424
|
+
.then(async () => {
|
|
2425
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2426
|
+
this._unhighlightElements(frame, dataAttribute).then(() => { }
|
|
2427
|
+
// console.log(`Unhighlighting vrtr in recorder is successful`)
|
|
2428
|
+
);
|
|
2429
|
+
})
|
|
2430
|
+
.catch(e);
|
|
2431
|
+
}
|
|
2432
|
+
//await this._highlightElements(frame, cssAnchor);
|
|
2433
|
+
const element = await frame.locator(dataAttribute).first();
|
|
2434
|
+
// await new Promise((resolve) => setTimeout(resolve, 100));
|
|
2435
|
+
// await this._unhighlightElements(frame, dataAttribute);
|
|
2436
|
+
if (element) {
|
|
2437
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2438
|
+
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2439
|
+
await _screenshot(state, this, element);
|
|
2440
|
+
}
|
|
2441
|
+
else {
|
|
2442
|
+
await _screenshot(state, this);
|
|
2443
|
+
}
|
|
2444
|
+
return state.info;
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2211
2450
|
}
|
|
2212
|
-
|
|
2213
|
-
|
|
2451
|
+
catch (e) {
|
|
2452
|
+
await _commandError(state, e, this);
|
|
2453
|
+
}
|
|
2454
|
+
finally {
|
|
2455
|
+
_commandFinally(state, this);
|
|
2214
2456
|
}
|
|
2215
|
-
return serviceUrl;
|
|
2216
2457
|
}
|
|
2217
2458
|
async visualVerification(text, options = {}, world = null) {
|
|
2218
2459
|
const startTime = Date.now();
|
|
@@ -2228,7 +2469,7 @@ class StableBrowser {
|
|
|
2228
2469
|
throw new Error("TOKEN is not set");
|
|
2229
2470
|
}
|
|
2230
2471
|
try {
|
|
2231
|
-
let serviceUrl =
|
|
2472
|
+
let serviceUrl = _getServerUrl();
|
|
2232
2473
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2233
2474
|
info.screenshotPath = screenshotPath;
|
|
2234
2475
|
const screenshot = await this.takeScreenshot();
|
|
@@ -2269,7 +2510,7 @@ class StableBrowser {
|
|
|
2269
2510
|
}
|
|
2270
2511
|
finally {
|
|
2271
2512
|
const endTime = Date.now();
|
|
2272
|
-
|
|
2513
|
+
_reportToWorld(world, {
|
|
2273
2514
|
type: Types.VERIFY_VISUAL,
|
|
2274
2515
|
text: "Visual verification",
|
|
2275
2516
|
screenshotId,
|
|
@@ -2317,18 +2558,19 @@ class StableBrowser {
|
|
|
2317
2558
|
let screenshotPath = null;
|
|
2318
2559
|
const info = {};
|
|
2319
2560
|
info.log = "";
|
|
2561
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
2320
2562
|
info.operation = "getTableData";
|
|
2321
2563
|
info.selectors = selectors;
|
|
2322
2564
|
try {
|
|
2323
2565
|
let table = await this._locate(selectors, info, _params);
|
|
2324
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2566
|
+
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info, table));
|
|
2325
2567
|
const tableData = await getTableData(this.page, table);
|
|
2326
2568
|
return tableData;
|
|
2327
2569
|
}
|
|
2328
2570
|
catch (e) {
|
|
2329
2571
|
this.logger.error("getTableData failed " + info.log);
|
|
2330
2572
|
this.logger.error(e);
|
|
2331
|
-
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2573
|
+
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info, table));
|
|
2332
2574
|
info.screenshotPath = screenshotPath;
|
|
2333
2575
|
Object.assign(e, { info: info });
|
|
2334
2576
|
error = e;
|
|
@@ -2337,7 +2579,7 @@ class StableBrowser {
|
|
|
2337
2579
|
}
|
|
2338
2580
|
finally {
|
|
2339
2581
|
const endTime = Date.now();
|
|
2340
|
-
|
|
2582
|
+
_reportToWorld(world, {
|
|
2341
2583
|
element_name: selectors.element_name,
|
|
2342
2584
|
type: Types.GET_TABLE_DATA,
|
|
2343
2585
|
text: "Get table data",
|
|
@@ -2392,7 +2634,7 @@ class StableBrowser {
|
|
|
2392
2634
|
info.operation = "analyzeTable";
|
|
2393
2635
|
info.selectors = selectors;
|
|
2394
2636
|
info.query = query;
|
|
2395
|
-
query =
|
|
2637
|
+
query = _fixUsingParams(query, _params);
|
|
2396
2638
|
info.query_fixed = query;
|
|
2397
2639
|
info.operator = operator;
|
|
2398
2640
|
info.value = value;
|
|
@@ -2503,7 +2745,7 @@ class StableBrowser {
|
|
|
2503
2745
|
}
|
|
2504
2746
|
finally {
|
|
2505
2747
|
const endTime = Date.now();
|
|
2506
|
-
|
|
2748
|
+
_reportToWorld(world, {
|
|
2507
2749
|
element_name: selectors.element_name,
|
|
2508
2750
|
type: Types.ANALYZE_TABLE,
|
|
2509
2751
|
text: "Analyze table",
|
|
@@ -2576,7 +2818,7 @@ class StableBrowser {
|
|
|
2576
2818
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2577
2819
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2578
2820
|
const endTime = Date.now();
|
|
2579
|
-
|
|
2821
|
+
_reportToWorld(world, {
|
|
2580
2822
|
type: Types.GET_PAGE_STATUS,
|
|
2581
2823
|
text: "Wait for page load",
|
|
2582
2824
|
screenshotId,
|
|
@@ -2609,7 +2851,7 @@ class StableBrowser {
|
|
|
2609
2851
|
throwError: false,
|
|
2610
2852
|
};
|
|
2611
2853
|
try {
|
|
2612
|
-
await _preCommand(state, this);
|
|
2854
|
+
await _preCommand(state, this, world);
|
|
2613
2855
|
await this.page.close();
|
|
2614
2856
|
}
|
|
2615
2857
|
catch (e) {
|
|
@@ -2620,6 +2862,11 @@ class StableBrowser {
|
|
|
2620
2862
|
_commandFinally(state, this);
|
|
2621
2863
|
}
|
|
2622
2864
|
}
|
|
2865
|
+
saveTestDataAsGlobal(options, world) {
|
|
2866
|
+
const dataFile = this._getDataFile(world);
|
|
2867
|
+
process.env.GLOBAL_TEST_DATA_FILE = dataFile;
|
|
2868
|
+
this.logger.info("Save the scenario test data as global for the following scenarios.");
|
|
2869
|
+
}
|
|
2623
2870
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
2624
2871
|
const startTime = Date.now();
|
|
2625
2872
|
let error = null;
|
|
@@ -2643,7 +2890,7 @@ class StableBrowser {
|
|
|
2643
2890
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2644
2891
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2645
2892
|
const endTime = Date.now();
|
|
2646
|
-
|
|
2893
|
+
_reportToWorld(world, {
|
|
2647
2894
|
type: Types.SET_VIEWPORT,
|
|
2648
2895
|
text: "set viewport size to " + width + "x" + hight,
|
|
2649
2896
|
screenshotId,
|
|
@@ -2680,7 +2927,7 @@ class StableBrowser {
|
|
|
2680
2927
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2681
2928
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2682
2929
|
const endTime = Date.now();
|
|
2683
|
-
|
|
2930
|
+
_reportToWorld(world, {
|
|
2684
2931
|
type: Types.GET_PAGE_STATUS,
|
|
2685
2932
|
text: "page relaod",
|
|
2686
2933
|
screenshotId,
|
|
@@ -2716,26 +2963,35 @@ class StableBrowser {
|
|
|
2716
2963
|
console.log("#-#");
|
|
2717
2964
|
}
|
|
2718
2965
|
}
|
|
2719
|
-
_reportToWorld(world, properties) {
|
|
2720
|
-
if (!world || !world.attach) {
|
|
2721
|
-
return;
|
|
2722
|
-
}
|
|
2723
|
-
world.attach(JSON.stringify(properties), { mediaType: "application/json" });
|
|
2724
|
-
}
|
|
2725
2966
|
async beforeStep(world, step) {
|
|
2726
|
-
this.stepName = step.pickleStep.text;
|
|
2727
|
-
this.logger.info("step: " + this.stepName);
|
|
2728
2967
|
if (this.stepIndex === undefined) {
|
|
2729
2968
|
this.stepIndex = 0;
|
|
2730
2969
|
}
|
|
2731
2970
|
else {
|
|
2732
2971
|
this.stepIndex++;
|
|
2733
2972
|
}
|
|
2973
|
+
if (step && step.pickleStep && step.pickleStep.text) {
|
|
2974
|
+
this.stepName = step.pickleStep.text;
|
|
2975
|
+
this.logger.info("step: " + this.stepName);
|
|
2976
|
+
}
|
|
2977
|
+
else if (step && step.text) {
|
|
2978
|
+
this.stepName = step.text;
|
|
2979
|
+
}
|
|
2980
|
+
else {
|
|
2981
|
+
this.stepName = "step " + this.stepIndex;
|
|
2982
|
+
}
|
|
2734
2983
|
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2735
2984
|
if (this.context.browserObject.context) {
|
|
2736
2985
|
await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
|
|
2737
2986
|
}
|
|
2738
2987
|
}
|
|
2988
|
+
if (this.tags === null && step && step.pickle && step.pickle.tags) {
|
|
2989
|
+
this.tags = step.pickle.tags.map((tag) => tag.name);
|
|
2990
|
+
// check if @global_test_data tag is present
|
|
2991
|
+
if (this.tags.includes("@global_test_data")) {
|
|
2992
|
+
this.saveTestDataAsGlobal({}, world);
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2739
2995
|
}
|
|
2740
2996
|
async afterStep(world, step) {
|
|
2741
2997
|
this.stepName = null;
|
|
@@ -2753,156 +3009,5 @@ function createTimedPromise(promise, label) {
|
|
|
2753
3009
|
.then((result) => ({ status: "fulfilled", label, result }))
|
|
2754
3010
|
.catch((error) => Promise.reject({ status: "rejected", label, error }));
|
|
2755
3011
|
}
|
|
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
3012
|
export { StableBrowser };
|
|
2908
3013
|
//# sourceMappingURL=stable_browser.js.map
|