automation_model 1.0.514-dev → 1.0.514-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 +97 -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 +1 -0
- package/lib/command_common.js +43 -8
- 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 +52 -17
- package/lib/stable_browser.js +466 -479
- 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
|
@@ -10,15 +10,17 @@ import { getDateTimeValue } from "./date_time.js";
|
|
|
10
10
|
import drawRectangle from "./drawRect.js";
|
|
11
11
|
//import { closeUnexpectedPopups } from "./popups.js";
|
|
12
12
|
import { getTableCells, getTableData } from "./table_analyze.js";
|
|
13
|
-
import { maskValue, replaceWithLocalTestData } from "./utils.js";
|
|
13
|
+
import { _convertToRegexQuery, _copyContext, _fixLocatorUsingParams, _fixUsingParams, _getServerUrl, KEYBOARD_EVENTS, maskValue, replaceWithLocalTestData, scrollPageToLoadLazyElements, unEscapeString, } from "./utils.js";
|
|
14
14
|
import csv from "csv-parser";
|
|
15
15
|
import { Readable } from "node:stream";
|
|
16
16
|
import readline from "readline";
|
|
17
17
|
import { getContext } from "./init_browser.js";
|
|
18
18
|
import { locate_element } from "./locate_element.js";
|
|
19
|
-
import {
|
|
19
|
+
import { randomUUID } from "crypto";
|
|
20
|
+
import { _commandError, _commandFinally, _preCommand, _validateSelectors, _screenshot, _reportToWorld, } from "./command_common.js";
|
|
20
21
|
import { registerDownloadEvent, registerNetworkEvents } from "./network.js";
|
|
21
|
-
|
|
22
|
+
import { LocatorLog } from "./locator_log.js";
|
|
23
|
+
export const Types = {
|
|
22
24
|
CLICK: "click_element",
|
|
23
25
|
NAVIGATE: "navigate",
|
|
24
26
|
FILL: "fill_element",
|
|
@@ -29,6 +31,8 @@ const Types = {
|
|
|
29
31
|
GET_PAGE_STATUS: "get_page_status",
|
|
30
32
|
CLICK_ROW_ACTION: "click_row_action",
|
|
31
33
|
VERIFY_ELEMENT_CONTAINS_TEXT: "verify_element_contains_text",
|
|
34
|
+
VERIFY_PAGE_CONTAINS_TEXT: "verify_page_contains_text",
|
|
35
|
+
VERIFY_PAGE_CONTAINS_NO_TEXT: "verify_page_contains_no_text",
|
|
32
36
|
ANALYZE_TABLE: "analyze_table",
|
|
33
37
|
SELECT: "select_combobox",
|
|
34
38
|
VERIFY_PAGE_PATH: "verify_page_path",
|
|
@@ -45,6 +49,8 @@ const Types = {
|
|
|
45
49
|
LOAD_DATA: "load_data",
|
|
46
50
|
SET_INPUT: "set_input",
|
|
47
51
|
WAIT_FOR_TEXT_TO_DISAPPEAR: "wait_for_text_to_disappear",
|
|
52
|
+
VERIFY_ATTRIBUTE: "verify_element_attribute",
|
|
53
|
+
VERIFY_TEXT_WITH_RELATION: "verify_text_with_relation",
|
|
48
54
|
};
|
|
49
55
|
export const apps = {};
|
|
50
56
|
class StableBrowser {
|
|
@@ -58,6 +64,7 @@ class StableBrowser {
|
|
|
58
64
|
networkLogger = null;
|
|
59
65
|
configuration = null;
|
|
60
66
|
appName = "main";
|
|
67
|
+
tags = null;
|
|
61
68
|
constructor(browser, page, logger = null, context = null, world = null) {
|
|
62
69
|
this.browser = browser;
|
|
63
70
|
this.page = page;
|
|
@@ -88,17 +95,17 @@ class StableBrowser {
|
|
|
88
95
|
catch (e) {
|
|
89
96
|
this.logger.error("unable to read ai_config.json");
|
|
90
97
|
}
|
|
91
|
-
context.pageLoading = { status: false };
|
|
92
|
-
context.pages = [this.page];
|
|
93
98
|
const logFolder = path.join(this.project_path, "logs", "web");
|
|
94
99
|
this.world = world;
|
|
100
|
+
context.pages = [this.page];
|
|
101
|
+
context.pageLoading = { status: false };
|
|
95
102
|
this.registerEventListeners(this.context);
|
|
96
103
|
registerNetworkEvents(this.world, this, this.context, this.page);
|
|
97
104
|
registerDownloadEvent(this.page, this.world, this.context);
|
|
98
105
|
}
|
|
99
106
|
registerEventListeners(context) {
|
|
100
107
|
this.registerConsoleLogListener(this.page, context);
|
|
101
|
-
this.registerRequestListener(this.page, context, this.webLogFile);
|
|
108
|
+
// this.registerRequestListener(this.page, context, this.webLogFile);
|
|
102
109
|
if (!context.pageLoading) {
|
|
103
110
|
context.pageLoading = { status: false };
|
|
104
111
|
}
|
|
@@ -143,7 +150,7 @@ class StableBrowser {
|
|
|
143
150
|
if (this.appName === appName) {
|
|
144
151
|
return;
|
|
145
152
|
}
|
|
146
|
-
let
|
|
153
|
+
let navigate = false;
|
|
147
154
|
if (!apps[appName]) {
|
|
148
155
|
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this, -1, this.context.reportFolder);
|
|
149
156
|
newContextCreated = true;
|
|
@@ -154,32 +161,15 @@ class StableBrowser {
|
|
|
154
161
|
};
|
|
155
162
|
}
|
|
156
163
|
const tempContext = {};
|
|
157
|
-
|
|
158
|
-
|
|
164
|
+
_copyContext(this, tempContext);
|
|
165
|
+
_copyContext(apps[appName], this);
|
|
159
166
|
apps[this.appName] = tempContext;
|
|
160
167
|
this.appName = appName;
|
|
161
|
-
if (
|
|
162
|
-
this.registerEventListeners(this.context);
|
|
168
|
+
if (navigate) {
|
|
163
169
|
await this.goto(this.context.environment.baseUrl);
|
|
164
170
|
await this.waitForPageLoad();
|
|
165
171
|
}
|
|
166
172
|
}
|
|
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
173
|
registerConsoleLogListener(page, context) {
|
|
184
174
|
if (!this.context.webLogger) {
|
|
185
175
|
this.context.webLogger = [];
|
|
@@ -229,7 +219,7 @@ class StableBrowser {
|
|
|
229
219
|
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
|
|
230
220
|
}
|
|
231
221
|
catch (error) {
|
|
232
|
-
console.error("Error in request listener", error);
|
|
222
|
+
// console.error("Error in request listener", error);
|
|
233
223
|
context.networkLogger.push({
|
|
234
224
|
error: "not able to listen",
|
|
235
225
|
message: error.message,
|
|
@@ -243,55 +233,48 @@ class StableBrowser {
|
|
|
243
233
|
// async closeUnexpectedPopups() {
|
|
244
234
|
// await closeUnexpectedPopups(this.page);
|
|
245
235
|
// }
|
|
246
|
-
async goto(url) {
|
|
236
|
+
async goto(url, world = null) {
|
|
247
237
|
if (!url.startsWith("http")) {
|
|
248
238
|
url = "https://" + url;
|
|
249
239
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
240
|
+
const state = {
|
|
241
|
+
value: url,
|
|
242
|
+
world: world,
|
|
243
|
+
type: Types.NAVIGATE,
|
|
244
|
+
text: `Navigate Page to: ${url}`,
|
|
245
|
+
operation: "goto",
|
|
246
|
+
log: "***** navigate page to " + url + " *****\n",
|
|
247
|
+
info: {},
|
|
248
|
+
locate: false,
|
|
249
|
+
scroll: false,
|
|
250
|
+
screenshot: false,
|
|
251
|
+
highlight: false,
|
|
252
|
+
};
|
|
253
|
+
try {
|
|
254
|
+
await _preCommand(state, this);
|
|
255
|
+
await this.page.goto(url, {
|
|
256
|
+
timeout: 60000,
|
|
257
|
+
});
|
|
258
|
+
await _screenshot(state, this);
|
|
257
259
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
// remove the _ prefix
|
|
262
|
-
regValue = key.substring(1);
|
|
263
|
-
}
|
|
264
|
-
text = text.replaceAll(new RegExp("{" + regValue + "}", "g"), _params[key]);
|
|
260
|
+
catch (error) {
|
|
261
|
+
console.error("Error on goto", error);
|
|
262
|
+
_commandError(state, error, this);
|
|
265
263
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
_fixLocatorUsingParams(locator, _params) {
|
|
269
|
-
// check if not null
|
|
270
|
-
if (!locator) {
|
|
271
|
-
return locator;
|
|
264
|
+
finally {
|
|
265
|
+
_commandFinally(state, this);
|
|
272
266
|
}
|
|
273
|
-
// clone the locator
|
|
274
|
-
locator = JSON.parse(JSON.stringify(locator));
|
|
275
|
-
this.scanAndManipulate(locator, _params);
|
|
276
|
-
return locator;
|
|
277
|
-
}
|
|
278
|
-
_isObject(value) {
|
|
279
|
-
return value && typeof value === "object" && value.constructor === Object;
|
|
280
267
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
this.scanAndManipulate(currentObj[key], _params);
|
|
268
|
+
async _getLocator(locator, scope, _params) {
|
|
269
|
+
locator = _fixLocatorUsingParams(locator, _params);
|
|
270
|
+
// locator = await this._replaceWithLocalData(locator);
|
|
271
|
+
for (let key in locator) {
|
|
272
|
+
if (typeof locator[key] !== "string")
|
|
273
|
+
continue;
|
|
274
|
+
if (locator[key].includes("{{") && locator[key].includes("}}")) {
|
|
275
|
+
locator[key] = await this._replaceWithLocalData(locator[key], this.world);
|
|
290
276
|
}
|
|
291
277
|
}
|
|
292
|
-
}
|
|
293
|
-
_getLocator(locator, scope, _params) {
|
|
294
|
-
locator = this._fixLocatorUsingParams(locator, _params);
|
|
295
278
|
let locatorReturn;
|
|
296
279
|
if (locator.role) {
|
|
297
280
|
if (locator.role[1].nameReg) {
|
|
@@ -299,7 +282,7 @@ class StableBrowser {
|
|
|
299
282
|
delete locator.role[1].nameReg;
|
|
300
283
|
}
|
|
301
284
|
// if (locator.role[1].name) {
|
|
302
|
-
// locator.role[1].name =
|
|
285
|
+
// locator.role[1].name = _fixUsingParams(locator.role[1].name, _params);
|
|
303
286
|
// }
|
|
304
287
|
locatorReturn = scope.getByRole(locator.role[0], locator.role[1]);
|
|
305
288
|
}
|
|
@@ -342,143 +325,69 @@ class StableBrowser {
|
|
|
342
325
|
if (css && css.locator) {
|
|
343
326
|
css = css.locator;
|
|
344
327
|
}
|
|
345
|
-
let result = await this._locateElementByText(scope,
|
|
328
|
+
let result = await this._locateElementByText(scope, _fixUsingParams(text, _params), "*:not(script, style, head)", false, false, true, _params);
|
|
346
329
|
if (result.elementCount === 0) {
|
|
347
330
|
return;
|
|
348
331
|
}
|
|
349
|
-
let textElementCss = "[data-blinq-id
|
|
332
|
+
let textElementCss = "[data-blinq-id-" + result.randomToken + "]";
|
|
350
333
|
// css climb to parent element
|
|
351
334
|
const climbArray = [];
|
|
352
335
|
for (let i = 0; i < climb; i++) {
|
|
353
336
|
climbArray.push("..");
|
|
354
337
|
}
|
|
355
338
|
let climbXpath = "xpath=" + climbArray.join("/");
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
return
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
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
|
-
}
|
|
421
|
-
for (let i = 0; i < elements.length; i++) {
|
|
422
|
-
const element = elements[i];
|
|
423
|
-
if ((element.innerText && regexpSearch.test(element.innerText)) ||
|
|
424
|
-
(element.value && regexpSearch.test(element.value))) {
|
|
425
|
-
foundElements.push(element);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
else {
|
|
430
|
-
text = text.trim();
|
|
431
|
-
for (let i = 0; i < elements.length; i++) {
|
|
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;
|
|
339
|
+
let resultCss = textElementCss + " >> " + climbXpath;
|
|
340
|
+
if (css) {
|
|
341
|
+
resultCss = resultCss + " >> " + css;
|
|
342
|
+
}
|
|
343
|
+
return resultCss;
|
|
344
|
+
}
|
|
345
|
+
async _locateElementByText(scope, text1, tag1, regex1 = false, partial1, ignoreCase = true, _params) {
|
|
346
|
+
const query = _convertToRegexQuery(text1, regex1, !partial1, ignoreCase);
|
|
347
|
+
const locator = scope.locator(query);
|
|
348
|
+
const count = await locator.count();
|
|
349
|
+
if (!tag1) {
|
|
350
|
+
tag1 = "*";
|
|
351
|
+
}
|
|
352
|
+
const randomToken = Math.random().toString(36).substring(7);
|
|
353
|
+
let tagCount = 0;
|
|
354
|
+
for (let i = 0; i < count; i++) {
|
|
355
|
+
const element = locator.nth(i);
|
|
356
|
+
// check if the tag matches
|
|
357
|
+
if (!(await element.evaluate((el, [tag, randomToken]) => {
|
|
358
|
+
if (!tag.startsWith("*")) {
|
|
359
|
+
if (el.tagName.toLowerCase() !== tag) {
|
|
360
|
+
return false;
|
|
458
361
|
}
|
|
459
362
|
}
|
|
460
|
-
if (!
|
|
461
|
-
|
|
363
|
+
if (!el.setAttribute) {
|
|
364
|
+
el = el.parentElement;
|
|
462
365
|
}
|
|
366
|
+
el.setAttribute("data-blinq-id-" + randomToken, "");
|
|
367
|
+
return true;
|
|
368
|
+
}, [tag1, randomToken]))) {
|
|
369
|
+
continue;
|
|
463
370
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
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]);
|
|
371
|
+
tagCount++;
|
|
372
|
+
}
|
|
373
|
+
return { elementCount: tagCount, randomToken };
|
|
477
374
|
}
|
|
478
|
-
async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true) {
|
|
375
|
+
async _collectLocatorInformation(selectorHierarchy, index = 0, scope, foundLocators, _params, info, visibleOnly = true, allowDisabled = false) {
|
|
376
|
+
if (!info) {
|
|
377
|
+
info = {};
|
|
378
|
+
}
|
|
379
|
+
if (!info.failCause) {
|
|
380
|
+
info.failCause = {};
|
|
381
|
+
}
|
|
382
|
+
if (!info.log) {
|
|
383
|
+
info.log = "";
|
|
384
|
+
info.locatorLog = new LocatorLog(selectorHierarchy);
|
|
385
|
+
}
|
|
479
386
|
let locatorSearch = selectorHierarchy[index];
|
|
387
|
+
let originalLocatorSearch = "";
|
|
480
388
|
try {
|
|
481
|
-
|
|
389
|
+
originalLocatorSearch = _fixUsingParams(JSON.stringify(locatorSearch), _params);
|
|
390
|
+
locatorSearch = JSON.parse(originalLocatorSearch);
|
|
482
391
|
}
|
|
483
392
|
catch (e) {
|
|
484
393
|
console.error(e);
|
|
@@ -492,24 +401,24 @@ class StableBrowser {
|
|
|
492
401
|
info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
|
|
493
402
|
return;
|
|
494
403
|
}
|
|
495
|
-
locator = this._getLocator({ css: locatorString }, scope, _params);
|
|
404
|
+
locator = await this._getLocator({ css: locatorString }, scope, _params);
|
|
496
405
|
}
|
|
497
406
|
else if (locatorSearch.text) {
|
|
498
|
-
let text =
|
|
499
|
-
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);
|
|
500
409
|
if (result.elementCount === 0) {
|
|
501
410
|
info.failCause.textNotFound = true;
|
|
502
411
|
info.failCause.lastError = "failed to locate element by text: " + text;
|
|
503
412
|
return;
|
|
504
413
|
}
|
|
505
|
-
locatorSearch.css = "[data-blinq-id
|
|
414
|
+
locatorSearch.css = "[data-blinq-id-" + result.randomToken + "]";
|
|
506
415
|
if (locatorSearch.childCss) {
|
|
507
416
|
locatorSearch.css = locatorSearch.css + " " + locatorSearch.childCss;
|
|
508
417
|
}
|
|
509
|
-
locator = this._getLocator(locatorSearch, scope, _params);
|
|
418
|
+
locator = await this._getLocator(locatorSearch, scope, _params);
|
|
510
419
|
}
|
|
511
420
|
else {
|
|
512
|
-
locator = this._getLocator(locatorSearch, scope, _params);
|
|
421
|
+
locator = await this._getLocator(locatorSearch, scope, _params);
|
|
513
422
|
}
|
|
514
423
|
// let cssHref = false;
|
|
515
424
|
// if (locatorSearch.css && locatorSearch.css.includes("href=")) {
|
|
@@ -522,18 +431,27 @@ class StableBrowser {
|
|
|
522
431
|
//info.log += "total elements found " + count + "\n";
|
|
523
432
|
//let visibleCount = 0;
|
|
524
433
|
let visibleLocator = null;
|
|
525
|
-
if (locatorSearch.index && locatorSearch.index < count) {
|
|
434
|
+
if (typeof locatorSearch.index === "number" && locatorSearch.index < count) {
|
|
526
435
|
foundLocators.push(locator.nth(locatorSearch.index));
|
|
436
|
+
if (info.locatorLog) {
|
|
437
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND");
|
|
438
|
+
}
|
|
527
439
|
return;
|
|
528
440
|
}
|
|
441
|
+
if (info.locatorLog && count === 0) {
|
|
442
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "NOT_FOUND");
|
|
443
|
+
}
|
|
529
444
|
for (let j = 0; j < count; j++) {
|
|
530
445
|
let visible = await locator.nth(j).isVisible();
|
|
531
446
|
const enabled = await locator.nth(j).isEnabled();
|
|
532
447
|
if (!visibleOnly) {
|
|
533
448
|
visible = true;
|
|
534
449
|
}
|
|
535
|
-
if (visible && enabled) {
|
|
450
|
+
if (visible && (allowDisabled || enabled)) {
|
|
536
451
|
foundLocators.push(locator.nth(j));
|
|
452
|
+
if (info.locatorLog) {
|
|
453
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND");
|
|
454
|
+
}
|
|
537
455
|
}
|
|
538
456
|
else {
|
|
539
457
|
info.failCause.visible = visible;
|
|
@@ -541,8 +459,16 @@ class StableBrowser {
|
|
|
541
459
|
if (!info.printMessages) {
|
|
542
460
|
info.printMessages = {};
|
|
543
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
|
+
}
|
|
544
470
|
if (!info.printMessages[j.toString()]) {
|
|
545
|
-
info.log += "element " + locator + " visible " + visible + " enabled " + enabled + "\n";
|
|
471
|
+
//info.log += "element " + locator + " visible " + visible + " enabled " + enabled + "\n";
|
|
546
472
|
info.printMessages[j.toString()] = true;
|
|
547
473
|
}
|
|
548
474
|
}
|
|
@@ -558,7 +484,7 @@ class StableBrowser {
|
|
|
558
484
|
if (!info) {
|
|
559
485
|
info = {};
|
|
560
486
|
}
|
|
561
|
-
info.log += "scan for popup handlers" + "\n";
|
|
487
|
+
//info.log += "scan for popup handlers" + "\n";
|
|
562
488
|
const handlerGroup = [];
|
|
563
489
|
for (let i = 0; i < this.configuration.popupHandlers.length; i++) {
|
|
564
490
|
handlerGroup.push(this.configuration.popupHandlers[i].locator);
|
|
@@ -585,16 +511,28 @@ class StableBrowser {
|
|
|
585
511
|
}
|
|
586
512
|
if (result.foundElements.length > 0) {
|
|
587
513
|
let dialogCloseLocator = result.foundElements[0].locator;
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
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
|
+
}
|
|
591
529
|
return { rerun: true };
|
|
592
530
|
}
|
|
593
531
|
}
|
|
594
532
|
}
|
|
595
533
|
return { rerun: false };
|
|
596
534
|
}
|
|
597
|
-
async _locate(selectors, info, _params, timeout) {
|
|
535
|
+
async _locate(selectors, info, _params, timeout, allowDisabled = false) {
|
|
598
536
|
if (!timeout) {
|
|
599
537
|
timeout = 30000;
|
|
600
538
|
}
|
|
@@ -604,7 +542,7 @@ class StableBrowser {
|
|
|
604
542
|
let selector = selectors.locators[j];
|
|
605
543
|
info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
|
|
606
544
|
}
|
|
607
|
-
let element = await this._locate_internal(selectors, info, _params, timeout);
|
|
545
|
+
let element = await this._locate_internal(selectors, info, _params, timeout, allowDisabled);
|
|
608
546
|
if (!element.rerun) {
|
|
609
547
|
return element;
|
|
610
548
|
}
|
|
@@ -617,6 +555,7 @@ class StableBrowser {
|
|
|
617
555
|
info.failCause = {};
|
|
618
556
|
info.log = "";
|
|
619
557
|
}
|
|
558
|
+
let startTime = Date.now();
|
|
620
559
|
let scope = this.page;
|
|
621
560
|
if (selectors.frame) {
|
|
622
561
|
return selectors.frame;
|
|
@@ -647,9 +586,11 @@ class StableBrowser {
|
|
|
647
586
|
}
|
|
648
587
|
return framescope;
|
|
649
588
|
};
|
|
589
|
+
let fLocator = null;
|
|
650
590
|
while (true) {
|
|
651
591
|
let frameFound = false;
|
|
652
592
|
if (selectors.nestFrmLoc) {
|
|
593
|
+
fLocator = selectors.nestFrmLoc;
|
|
653
594
|
scope = await findFrame(selectors.nestFrmLoc, scope);
|
|
654
595
|
frameFound = true;
|
|
655
596
|
break;
|
|
@@ -658,6 +599,7 @@ class StableBrowser {
|
|
|
658
599
|
for (let i = 0; i < selectors.frameLocators.length; i++) {
|
|
659
600
|
let frameLocator = selectors.frameLocators[i];
|
|
660
601
|
if (frameLocator.css) {
|
|
602
|
+
fLocator = frameLocator.css;
|
|
661
603
|
scope = scope.frameLocator(frameLocator.css);
|
|
662
604
|
frameFound = true;
|
|
663
605
|
break;
|
|
@@ -665,11 +607,15 @@ class StableBrowser {
|
|
|
665
607
|
}
|
|
666
608
|
}
|
|
667
609
|
if (!frameFound && selectors.iframe_src) {
|
|
610
|
+
fLocator = selectors.iframe_src;
|
|
668
611
|
scope = this.page.frame({ url: selectors.iframe_src });
|
|
669
612
|
}
|
|
670
613
|
if (!scope) {
|
|
671
|
-
info
|
|
672
|
-
|
|
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) {
|
|
673
619
|
info.failCause.iframeNotFound = true;
|
|
674
620
|
info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
|
|
675
621
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
@@ -677,6 +623,9 @@ class StableBrowser {
|
|
|
677
623
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
678
624
|
}
|
|
679
625
|
else {
|
|
626
|
+
if (info && info.locatorLog) {
|
|
627
|
+
info.locatorLog.setLocatorSearchStatus("frame-" + fLocator, "FOUND");
|
|
628
|
+
}
|
|
680
629
|
break;
|
|
681
630
|
}
|
|
682
631
|
}
|
|
@@ -693,16 +642,18 @@ class StableBrowser {
|
|
|
693
642
|
return bodyContent;
|
|
694
643
|
});
|
|
695
644
|
}
|
|
696
|
-
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
645
|
+
async _locate_internal(selectors, info, _params, timeout = 30000, allowDisabled = false) {
|
|
697
646
|
if (!info) {
|
|
698
647
|
info = {};
|
|
699
648
|
info.failCause = {};
|
|
700
649
|
info.log = "";
|
|
650
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
701
651
|
}
|
|
702
652
|
let highPriorityTimeout = 5000;
|
|
703
653
|
let visibleOnlyTimeout = 6000;
|
|
704
|
-
let startTime =
|
|
654
|
+
let startTime = Date.now();
|
|
705
655
|
let locatorsCount = 0;
|
|
656
|
+
let lazy_scroll = false;
|
|
706
657
|
//let arrayMode = Array.isArray(selectors);
|
|
707
658
|
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
708
659
|
let selectorsLocators = null;
|
|
@@ -740,17 +691,17 @@ class StableBrowser {
|
|
|
740
691
|
}
|
|
741
692
|
// info.log += "scanning locators in priority 1" + "\n";
|
|
742
693
|
let onlyPriority3 = selectorsLocators[0].priority === 3;
|
|
743
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["1"], scope, _params, info, visibleOnly);
|
|
694
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["1"], scope, _params, info, visibleOnly, allowDisabled);
|
|
744
695
|
if (result.foundElements.length === 0) {
|
|
745
696
|
// info.log += "scanning locators in priority 2" + "\n";
|
|
746
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["2"], scope, _params, info, visibleOnly);
|
|
697
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["2"], scope, _params, info, visibleOnly, allowDisabled);
|
|
747
698
|
}
|
|
748
699
|
if (result.foundElements.length === 0 && onlyPriority3) {
|
|
749
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly);
|
|
700
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly, allowDisabled);
|
|
750
701
|
}
|
|
751
702
|
else {
|
|
752
703
|
if (result.foundElements.length === 0 && !highPriorityOnly) {
|
|
753
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly);
|
|
704
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly, allowDisabled);
|
|
754
705
|
}
|
|
755
706
|
}
|
|
756
707
|
let foundElements = result.foundElements;
|
|
@@ -791,26 +742,38 @@ class StableBrowser {
|
|
|
791
742
|
return maxCountElement.locator;
|
|
792
743
|
}
|
|
793
744
|
}
|
|
794
|
-
if (
|
|
745
|
+
if (Date.now() - startTime > timeout) {
|
|
795
746
|
break;
|
|
796
747
|
}
|
|
797
|
-
if (
|
|
798
|
-
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";
|
|
799
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
|
+
}
|
|
800
755
|
}
|
|
801
|
-
if (
|
|
802
|
-
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";
|
|
803
758
|
visibleOnly = false;
|
|
804
759
|
}
|
|
805
760
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
806
761
|
}
|
|
807
762
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
808
|
-
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";
|
|
809
770
|
info.failCause.locatorNotFound = true;
|
|
810
|
-
info
|
|
771
|
+
if (!info?.failCause?.lastError) {
|
|
772
|
+
info.failCause.lastError = "failed to locate unique element";
|
|
773
|
+
}
|
|
811
774
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
812
775
|
}
|
|
813
|
-
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
776
|
+
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly, allowDisabled = false) {
|
|
814
777
|
let foundElements = [];
|
|
815
778
|
const result = {
|
|
816
779
|
foundElements: foundElements,
|
|
@@ -818,14 +781,15 @@ class StableBrowser {
|
|
|
818
781
|
for (let i = 0; i < locatorsGroup.length; i++) {
|
|
819
782
|
let foundLocators = [];
|
|
820
783
|
try {
|
|
821
|
-
await this._collectLocatorInformation(locatorsGroup, i, scope, foundLocators, _params, info, visibleOnly);
|
|
784
|
+
await this._collectLocatorInformation(locatorsGroup, i, scope, foundLocators, _params, info, visibleOnly, allowDisabled);
|
|
822
785
|
}
|
|
823
786
|
catch (e) {
|
|
824
|
-
this
|
|
825
|
-
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);
|
|
826
790
|
foundLocators = [];
|
|
827
791
|
try {
|
|
828
|
-
await this._collectLocatorInformation(locatorsGroup, i, this.page, foundLocators, _params, info, visibleOnly);
|
|
792
|
+
await this._collectLocatorInformation(locatorsGroup, i, this.page, foundLocators, _params, info, visibleOnly, allowDisabled);
|
|
829
793
|
}
|
|
830
794
|
catch (e) {
|
|
831
795
|
this.logger.info("unable to use locator (second try) " + JSON.stringify(locatorsGroup[i]));
|
|
@@ -841,11 +805,27 @@ class StableBrowser {
|
|
|
841
805
|
}
|
|
842
806
|
if (foundLocators.length > 1) {
|
|
843
807
|
info.failCause.foundMultiple = true;
|
|
808
|
+
if (info.locatorLog) {
|
|
809
|
+
info.locatorLog.setLocatorSearchStatus(JSON.stringify(locatorsGroup[i]), "FOUND_NOT_UNIQUE");
|
|
810
|
+
}
|
|
844
811
|
}
|
|
845
812
|
}
|
|
846
813
|
return result;
|
|
847
814
|
}
|
|
848
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);
|
|
849
829
|
const startTime = Date.now();
|
|
850
830
|
let timeout = 30000;
|
|
851
831
|
if (options && options.timeout) {
|
|
@@ -870,13 +850,31 @@ class StableBrowser {
|
|
|
870
850
|
catch (e) {
|
|
871
851
|
if (performance.now() - startTime > timeout) {
|
|
872
852
|
// throw e;
|
|
873
|
-
|
|
853
|
+
try {
|
|
854
|
+
await _commandError(state, "timeout looking for " + elementDescription, this);
|
|
855
|
+
}
|
|
856
|
+
finally {
|
|
857
|
+
_commandFinally(state, this);
|
|
858
|
+
}
|
|
874
859
|
}
|
|
875
860
|
}
|
|
876
861
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
877
862
|
}
|
|
878
863
|
}
|
|
879
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);
|
|
880
878
|
const startTime = Date.now();
|
|
881
879
|
let timeout = 30000;
|
|
882
880
|
if (options && options.timeout) {
|
|
@@ -901,7 +899,12 @@ class StableBrowser {
|
|
|
901
899
|
catch (e) {
|
|
902
900
|
if (performance.now() - startTime > timeout) {
|
|
903
901
|
// throw e;
|
|
904
|
-
|
|
902
|
+
try {
|
|
903
|
+
await _commandError(state, "timeout looking for " + elementDescription, this);
|
|
904
|
+
}
|
|
905
|
+
finally {
|
|
906
|
+
_commandFinally(state, this);
|
|
907
|
+
}
|
|
905
908
|
}
|
|
906
909
|
}
|
|
907
910
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
@@ -920,18 +923,18 @@ class StableBrowser {
|
|
|
920
923
|
};
|
|
921
924
|
try {
|
|
922
925
|
await _preCommand(state, this);
|
|
923
|
-
if (state.options && state.options.context) {
|
|
924
|
-
|
|
925
|
-
}
|
|
926
|
+
// if (state.options && state.options.context) {
|
|
927
|
+
// state.selectors.locators[0].text = state.options.context;
|
|
928
|
+
// }
|
|
926
929
|
try {
|
|
927
930
|
await state.element.click();
|
|
928
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
931
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
929
932
|
}
|
|
930
933
|
catch (e) {
|
|
931
934
|
// await this.closeUnexpectedPopups();
|
|
932
935
|
state.element = await this._locate(selectors, state.info, _params);
|
|
933
936
|
await state.element.dispatchEvent("click");
|
|
934
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
937
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
935
938
|
}
|
|
936
939
|
await this.waitForPageLoad();
|
|
937
940
|
return state.info;
|
|
@@ -1010,7 +1013,6 @@ class StableBrowser {
|
|
|
1010
1013
|
await state.element.hover({ timeout: 10000 });
|
|
1011
1014
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1012
1015
|
}
|
|
1013
|
-
await _screenshot(state, this);
|
|
1014
1016
|
await this.waitForPageLoad();
|
|
1015
1017
|
return state.info;
|
|
1016
1018
|
}
|
|
@@ -1282,7 +1284,12 @@ class StableBrowser {
|
|
|
1282
1284
|
await this.waitForPageLoad();
|
|
1283
1285
|
}
|
|
1284
1286
|
else if (enter === false) {
|
|
1285
|
-
|
|
1287
|
+
try {
|
|
1288
|
+
await state.element.dispatchEvent("change", null, { timeout: 5000 });
|
|
1289
|
+
}
|
|
1290
|
+
catch (e) {
|
|
1291
|
+
// ignore
|
|
1292
|
+
}
|
|
1286
1293
|
//await this.page.keyboard.press("Tab");
|
|
1287
1294
|
}
|
|
1288
1295
|
else {
|
|
@@ -1339,6 +1346,7 @@ class StableBrowser {
|
|
|
1339
1346
|
let screenshotPath = null;
|
|
1340
1347
|
if (!info.log) {
|
|
1341
1348
|
info.log = "";
|
|
1349
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
1342
1350
|
}
|
|
1343
1351
|
info.operation = "getText";
|
|
1344
1352
|
info.selectors = selectors;
|
|
@@ -1677,11 +1685,9 @@ class StableBrowser {
|
|
|
1677
1685
|
if (!fs.existsSync(world.screenshotPath)) {
|
|
1678
1686
|
fs.mkdirSync(world.screenshotPath, { recursive: true });
|
|
1679
1687
|
}
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
}
|
|
1684
|
-
const screenshotPath = path.join(world.screenshotPath, nextIndex + ".png");
|
|
1688
|
+
// to make sure the path doesn't start with -
|
|
1689
|
+
const uuidStr = "id_" + randomUUID();
|
|
1690
|
+
const screenshotPath = path.join(world.screenshotPath, uuidStr + ".png");
|
|
1685
1691
|
try {
|
|
1686
1692
|
await this.takeScreenshot(screenshotPath);
|
|
1687
1693
|
// let buffer = await this.page.screenshot({ timeout: 4000 });
|
|
@@ -1695,7 +1701,7 @@ class StableBrowser {
|
|
|
1695
1701
|
catch (e) {
|
|
1696
1702
|
this.logger.info("unable to take screenshot, ignored");
|
|
1697
1703
|
}
|
|
1698
|
-
result.screenshotId =
|
|
1704
|
+
result.screenshotId = uuidStr;
|
|
1699
1705
|
result.screenshotPath = screenshotPath;
|
|
1700
1706
|
if (info && info.box) {
|
|
1701
1707
|
await drawRectangle(screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
|
|
@@ -1834,6 +1840,69 @@ class StableBrowser {
|
|
|
1834
1840
|
_commandFinally(state, this);
|
|
1835
1841
|
}
|
|
1836
1842
|
}
|
|
1843
|
+
async verifyAttribute(selectors, attribute, value, _params = null, options = {}, world = null) {
|
|
1844
|
+
const state = {
|
|
1845
|
+
selectors,
|
|
1846
|
+
_params,
|
|
1847
|
+
attribute,
|
|
1848
|
+
value,
|
|
1849
|
+
options,
|
|
1850
|
+
world,
|
|
1851
|
+
type: Types.VERIFY_ATTRIBUTE,
|
|
1852
|
+
highlight: true,
|
|
1853
|
+
screenshot: true,
|
|
1854
|
+
text: `Verify element attribute`,
|
|
1855
|
+
operation: "verifyAttribute",
|
|
1856
|
+
log: "***** verify attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1857
|
+
allowDisabled: true,
|
|
1858
|
+
};
|
|
1859
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1860
|
+
let val;
|
|
1861
|
+
try {
|
|
1862
|
+
await _preCommand(state, this);
|
|
1863
|
+
switch (attribute) {
|
|
1864
|
+
case "innerText":
|
|
1865
|
+
val = String(await state.element.innerText());
|
|
1866
|
+
break;
|
|
1867
|
+
case "value":
|
|
1868
|
+
val = String(await state.element.inputValue());
|
|
1869
|
+
break;
|
|
1870
|
+
case "checked":
|
|
1871
|
+
val = String(await state.element.isChecked());
|
|
1872
|
+
break;
|
|
1873
|
+
case "disabled":
|
|
1874
|
+
val = String(await state.element.isDisabled());
|
|
1875
|
+
break;
|
|
1876
|
+
case "readOnly":
|
|
1877
|
+
const isEditable = await state.element.isEditable();
|
|
1878
|
+
val = String(!isEditable);
|
|
1879
|
+
break;
|
|
1880
|
+
default:
|
|
1881
|
+
val = String(await state.element.getAttribute(attribute));
|
|
1882
|
+
break;
|
|
1883
|
+
}
|
|
1884
|
+
state.info.expectedValue = val;
|
|
1885
|
+
let regex;
|
|
1886
|
+
if (value.startsWith("/") && value.endsWith("/")) {
|
|
1887
|
+
const patternBody = value.slice(1, -1);
|
|
1888
|
+
regex = new RegExp(patternBody, "g");
|
|
1889
|
+
}
|
|
1890
|
+
else {
|
|
1891
|
+
const escapedPattern = value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1892
|
+
regex = new RegExp(escapedPattern, "g");
|
|
1893
|
+
}
|
|
1894
|
+
if (!val.match(regex)) {
|
|
1895
|
+
throw new Error(`The ${attribute} attribute has a value of "${val}", but the expected value is "${value}"`);
|
|
1896
|
+
}
|
|
1897
|
+
return state.info;
|
|
1898
|
+
}
|
|
1899
|
+
catch (e) {
|
|
1900
|
+
await _commandError(state, e, this);
|
|
1901
|
+
}
|
|
1902
|
+
finally {
|
|
1903
|
+
_commandFinally(state, this);
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1837
1906
|
async extractEmailData(emailAddress, options, world) {
|
|
1838
1907
|
if (!emailAddress) {
|
|
1839
1908
|
throw new Error("email address is null");
|
|
@@ -1852,7 +1921,7 @@ class StableBrowser {
|
|
|
1852
1921
|
if (options && options.timeout) {
|
|
1853
1922
|
timeout = options.timeout;
|
|
1854
1923
|
}
|
|
1855
|
-
const serviceUrl =
|
|
1924
|
+
const serviceUrl = _getServerUrl() + "/api/mail/createLinkOrCodeFromEmail";
|
|
1856
1925
|
const request = {
|
|
1857
1926
|
method: "POST",
|
|
1858
1927
|
url: serviceUrl,
|
|
@@ -1929,15 +1998,15 @@ class StableBrowser {
|
|
|
1929
1998
|
scope
|
|
1930
1999
|
.evaluate((node) => {
|
|
1931
2000
|
if (node && node.style) {
|
|
1932
|
-
let originalBorder = node.style.
|
|
1933
|
-
node.style.
|
|
2001
|
+
let originalBorder = node.style.outline;
|
|
2002
|
+
node.style.outline = "2px solid red";
|
|
1934
2003
|
if (window) {
|
|
1935
2004
|
window.addEventListener("beforeunload", function (e) {
|
|
1936
|
-
node.style.
|
|
2005
|
+
node.style.outline = originalBorder;
|
|
1937
2006
|
});
|
|
1938
2007
|
}
|
|
1939
2008
|
setTimeout(function () {
|
|
1940
|
-
node.style.
|
|
2009
|
+
node.style.outline = originalBorder;
|
|
1941
2010
|
}, 2000);
|
|
1942
2011
|
}
|
|
1943
2012
|
})
|
|
@@ -1959,17 +2028,17 @@ class StableBrowser {
|
|
|
1959
2028
|
if (!element.style) {
|
|
1960
2029
|
return;
|
|
1961
2030
|
}
|
|
1962
|
-
var originalBorder = element.style.
|
|
2031
|
+
var originalBorder = element.style.outline;
|
|
1963
2032
|
// Set the new border to be red and 2px solid
|
|
1964
|
-
element.style.
|
|
2033
|
+
element.style.outline = "2px solid red";
|
|
1965
2034
|
if (window) {
|
|
1966
2035
|
window.addEventListener("beforeunload", function (e) {
|
|
1967
|
-
element.style.
|
|
2036
|
+
element.style.outline = originalBorder;
|
|
1968
2037
|
});
|
|
1969
2038
|
}
|
|
1970
2039
|
// Set a timeout to revert to the original border after 2 seconds
|
|
1971
2040
|
setTimeout(function () {
|
|
1972
|
-
element.style.
|
|
2041
|
+
element.style.outline = originalBorder;
|
|
1973
2042
|
}, 2000);
|
|
1974
2043
|
}
|
|
1975
2044
|
return;
|
|
@@ -2025,7 +2094,7 @@ class StableBrowser {
|
|
|
2025
2094
|
}
|
|
2026
2095
|
finally {
|
|
2027
2096
|
const endTime = Date.now();
|
|
2028
|
-
|
|
2097
|
+
_reportToWorld(world, {
|
|
2029
2098
|
type: Types.VERIFY_PAGE_PATH,
|
|
2030
2099
|
text: "Verify page path",
|
|
2031
2100
|
screenshotId,
|
|
@@ -2045,6 +2114,35 @@ class StableBrowser {
|
|
|
2045
2114
|
});
|
|
2046
2115
|
}
|
|
2047
2116
|
}
|
|
2117
|
+
async findTextInAllFrames(dateAlternatives, numberAlternatives, text, state) {
|
|
2118
|
+
const frames = this.page.frames();
|
|
2119
|
+
let results = [];
|
|
2120
|
+
let ignoreCase = false;
|
|
2121
|
+
for (let i = 0; i < frames.length; i++) {
|
|
2122
|
+
if (dateAlternatives.date) {
|
|
2123
|
+
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2124
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2125
|
+
result.frame = frames[i];
|
|
2126
|
+
results.push(result);
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
else if (numberAlternatives.number) {
|
|
2130
|
+
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2131
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2132
|
+
result.frame = frames[i];
|
|
2133
|
+
results.push(result);
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
else {
|
|
2137
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2138
|
+
result.frame = frames[i];
|
|
2139
|
+
results.push(result);
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
state.info.results = results;
|
|
2143
|
+
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2144
|
+
return resultWithElementsFound;
|
|
2145
|
+
}
|
|
2048
2146
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2049
2147
|
text = unEscapeString(text);
|
|
2050
2148
|
const state = {
|
|
@@ -2054,7 +2152,7 @@ class StableBrowser {
|
|
|
2054
2152
|
locate: false,
|
|
2055
2153
|
scroll: false,
|
|
2056
2154
|
highlight: false,
|
|
2057
|
-
type: Types.
|
|
2155
|
+
type: Types.VERIFY_PAGE_CONTAINS_TEXT,
|
|
2058
2156
|
text: `Verify text exists in page`,
|
|
2059
2157
|
operation: "verifyTextExistInPage",
|
|
2060
2158
|
log: "***** verify text " + text + " exists in page *****\n",
|
|
@@ -2072,31 +2170,7 @@ class StableBrowser {
|
|
|
2072
2170
|
await _preCommand(state, this);
|
|
2073
2171
|
state.info.text = text;
|
|
2074
2172
|
while (true) {
|
|
2075
|
-
const
|
|
2076
|
-
let results = [];
|
|
2077
|
-
for (let i = 0; i < frames.length; i++) {
|
|
2078
|
-
if (dateAlternatives.date) {
|
|
2079
|
-
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2080
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2081
|
-
result.frame = frames[i];
|
|
2082
|
-
results.push(result);
|
|
2083
|
-
}
|
|
2084
|
-
}
|
|
2085
|
-
else if (numberAlternatives.number) {
|
|
2086
|
-
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2087
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2088
|
-
result.frame = frames[i];
|
|
2089
|
-
results.push(result);
|
|
2090
|
-
}
|
|
2091
|
-
}
|
|
2092
|
-
else {
|
|
2093
|
-
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2094
|
-
result.frame = frames[i];
|
|
2095
|
-
results.push(result);
|
|
2096
|
-
}
|
|
2097
|
-
}
|
|
2098
|
-
state.info.results = results;
|
|
2099
|
-
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2173
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, text, state);
|
|
2100
2174
|
if (resultWithElementsFound.length === 0) {
|
|
2101
2175
|
if (Date.now() - state.startTime > timeout) {
|
|
2102
2176
|
throw new Error(`Text ${text} not found in page`);
|
|
@@ -2106,9 +2180,9 @@ class StableBrowser {
|
|
|
2106
2180
|
}
|
|
2107
2181
|
if (resultWithElementsFound[0].randomToken) {
|
|
2108
2182
|
const frame = resultWithElementsFound[0].frame;
|
|
2109
|
-
const dataAttribute = `[data-blinq-id
|
|
2183
|
+
const dataAttribute = `[data-blinq-id-${resultWithElementsFound[0].randomToken}]`;
|
|
2110
2184
|
await this._highlightElements(frame, dataAttribute);
|
|
2111
|
-
const element = await frame
|
|
2185
|
+
const element = await frame.locator(dataAttribute).first();
|
|
2112
2186
|
if (element) {
|
|
2113
2187
|
await this.scrollIfNeeded(element, state.info);
|
|
2114
2188
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
@@ -2153,31 +2227,7 @@ class StableBrowser {
|
|
|
2153
2227
|
await _preCommand(state, this);
|
|
2154
2228
|
state.info.text = text;
|
|
2155
2229
|
while (true) {
|
|
2156
|
-
const
|
|
2157
|
-
let results = [];
|
|
2158
|
-
for (let i = 0; i < frames.length; i++) {
|
|
2159
|
-
if (dateAlternatives.date) {
|
|
2160
|
-
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2161
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2162
|
-
result.frame = frames[i];
|
|
2163
|
-
results.push(result);
|
|
2164
|
-
}
|
|
2165
|
-
}
|
|
2166
|
-
else if (numberAlternatives.number) {
|
|
2167
|
-
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2168
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2169
|
-
result.frame = frames[i];
|
|
2170
|
-
results.push(result);
|
|
2171
|
-
}
|
|
2172
|
-
}
|
|
2173
|
-
else {
|
|
2174
|
-
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2175
|
-
result.frame = frames[i];
|
|
2176
|
-
results.push(result);
|
|
2177
|
-
}
|
|
2178
|
-
}
|
|
2179
|
-
state.info.results = results;
|
|
2180
|
-
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2230
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, text, state);
|
|
2181
2231
|
if (resultWithElementsFound.length === 0) {
|
|
2182
2232
|
await _screenshot(state, this);
|
|
2183
2233
|
return state.info;
|
|
@@ -2195,15 +2245,88 @@ class StableBrowser {
|
|
|
2195
2245
|
_commandFinally(state, this);
|
|
2196
2246
|
}
|
|
2197
2247
|
}
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2248
|
+
async verifyTextRelatedToText(textAnchor, climb, textToVerify, options = {}, world = null) {
|
|
2249
|
+
textAnchor = unEscapeString(textAnchor);
|
|
2250
|
+
textToVerify = unEscapeString(textToVerify);
|
|
2251
|
+
const state = {
|
|
2252
|
+
text_search: textToVerify,
|
|
2253
|
+
options,
|
|
2254
|
+
world,
|
|
2255
|
+
locate: false,
|
|
2256
|
+
scroll: false,
|
|
2257
|
+
highlight: false,
|
|
2258
|
+
type: Types.VERIFY_TEXT_WITH_RELATION,
|
|
2259
|
+
text: `Verify text with relation to another text`,
|
|
2260
|
+
operation: "verify_text_with_relation",
|
|
2261
|
+
log: "***** search for " + textAnchor + " climb " + climb + " and verify " + textToVerify + " found *****\n",
|
|
2262
|
+
};
|
|
2263
|
+
const timeout = this._getLoadTimeout(options);
|
|
2264
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2265
|
+
let newValue = await this._replaceWithLocalData(textAnchor, world);
|
|
2266
|
+
if (newValue !== textAnchor) {
|
|
2267
|
+
this.logger.info(textAnchor + "=" + newValue);
|
|
2268
|
+
textAnchor = newValue;
|
|
2269
|
+
}
|
|
2270
|
+
newValue = await this._replaceWithLocalData(textToVerify, world);
|
|
2271
|
+
if (newValue !== textToVerify) {
|
|
2272
|
+
this.logger.info(textToVerify + "=" + newValue);
|
|
2273
|
+
textToVerify = newValue;
|
|
2274
|
+
}
|
|
2275
|
+
let dateAlternatives = findDateAlternatives(textToVerify);
|
|
2276
|
+
let numberAlternatives = findNumberAlternatives(textToVerify);
|
|
2277
|
+
let foundAncore = false;
|
|
2278
|
+
try {
|
|
2279
|
+
await _preCommand(state, this);
|
|
2280
|
+
state.info.text = textToVerify;
|
|
2281
|
+
while (true) {
|
|
2282
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, textAnchor, state);
|
|
2283
|
+
if (resultWithElementsFound.length === 0) {
|
|
2284
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2285
|
+
throw new Error(`Text ${foundAncore ? textToVerify : textAnchor} not found in page`);
|
|
2286
|
+
}
|
|
2287
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2288
|
+
continue;
|
|
2289
|
+
}
|
|
2290
|
+
for (let i = 0; i < resultWithElementsFound.length; i++) {
|
|
2291
|
+
foundAncore = true;
|
|
2292
|
+
const result = resultWithElementsFound[i];
|
|
2293
|
+
const token = result.randomToken;
|
|
2294
|
+
const frame = result.frame;
|
|
2295
|
+
let css = `[data-blinq-id-${token}]`;
|
|
2296
|
+
const climbArray1 = [];
|
|
2297
|
+
for (let i = 0; i < climb; i++) {
|
|
2298
|
+
climbArray1.push("..");
|
|
2299
|
+
}
|
|
2300
|
+
let climbXpath = "xpath=" + climbArray1.join("/");
|
|
2301
|
+
css = css + " >> " + climbXpath;
|
|
2302
|
+
const count = await frame.locator(css).count();
|
|
2303
|
+
for (let j = 0; j < count; j++) {
|
|
2304
|
+
const continer = await frame.locator(css).nth(j);
|
|
2305
|
+
const result = await this._locateElementByText(continer, textToVerify, "*", false, true, true, {});
|
|
2306
|
+
if (result.elementCount > 0) {
|
|
2307
|
+
const dataAttribute = "[data-blinq-id-" + result.randomToken + "]";
|
|
2308
|
+
//const cssAnchor = `[data-blinq-id="blinq-id-${token}-anchor"]`;
|
|
2309
|
+
await this._highlightElements(frame, dataAttribute);
|
|
2310
|
+
//await this._highlightElements(frame, cssAnchor);
|
|
2311
|
+
const element = await frame.locator(dataAttribute).first();
|
|
2312
|
+
if (element) {
|
|
2313
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2314
|
+
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2315
|
+
}
|
|
2316
|
+
await _screenshot(state, this);
|
|
2317
|
+
return state.info;
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2323
|
+
}
|
|
2324
|
+
catch (e) {
|
|
2325
|
+
await _commandError(state, e, this);
|
|
2202
2326
|
}
|
|
2203
|
-
|
|
2204
|
-
|
|
2327
|
+
finally {
|
|
2328
|
+
_commandFinally(state, this);
|
|
2205
2329
|
}
|
|
2206
|
-
return serviceUrl;
|
|
2207
2330
|
}
|
|
2208
2331
|
async visualVerification(text, options = {}, world = null) {
|
|
2209
2332
|
const startTime = Date.now();
|
|
@@ -2219,7 +2342,7 @@ class StableBrowser {
|
|
|
2219
2342
|
throw new Error("TOKEN is not set");
|
|
2220
2343
|
}
|
|
2221
2344
|
try {
|
|
2222
|
-
let serviceUrl =
|
|
2345
|
+
let serviceUrl = _getServerUrl();
|
|
2223
2346
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2224
2347
|
info.screenshotPath = screenshotPath;
|
|
2225
2348
|
const screenshot = await this.takeScreenshot();
|
|
@@ -2260,7 +2383,7 @@ class StableBrowser {
|
|
|
2260
2383
|
}
|
|
2261
2384
|
finally {
|
|
2262
2385
|
const endTime = Date.now();
|
|
2263
|
-
|
|
2386
|
+
_reportToWorld(world, {
|
|
2264
2387
|
type: Types.VERIFY_VISUAL,
|
|
2265
2388
|
text: "Visual verification",
|
|
2266
2389
|
screenshotId,
|
|
@@ -2308,6 +2431,7 @@ class StableBrowser {
|
|
|
2308
2431
|
let screenshotPath = null;
|
|
2309
2432
|
const info = {};
|
|
2310
2433
|
info.log = "";
|
|
2434
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
2311
2435
|
info.operation = "getTableData";
|
|
2312
2436
|
info.selectors = selectors;
|
|
2313
2437
|
try {
|
|
@@ -2328,7 +2452,7 @@ class StableBrowser {
|
|
|
2328
2452
|
}
|
|
2329
2453
|
finally {
|
|
2330
2454
|
const endTime = Date.now();
|
|
2331
|
-
|
|
2455
|
+
_reportToWorld(world, {
|
|
2332
2456
|
element_name: selectors.element_name,
|
|
2333
2457
|
type: Types.GET_TABLE_DATA,
|
|
2334
2458
|
text: "Get table data",
|
|
@@ -2383,7 +2507,7 @@ class StableBrowser {
|
|
|
2383
2507
|
info.operation = "analyzeTable";
|
|
2384
2508
|
info.selectors = selectors;
|
|
2385
2509
|
info.query = query;
|
|
2386
|
-
query =
|
|
2510
|
+
query = _fixUsingParams(query, _params);
|
|
2387
2511
|
info.query_fixed = query;
|
|
2388
2512
|
info.operator = operator;
|
|
2389
2513
|
info.value = value;
|
|
@@ -2494,7 +2618,7 @@ class StableBrowser {
|
|
|
2494
2618
|
}
|
|
2495
2619
|
finally {
|
|
2496
2620
|
const endTime = Date.now();
|
|
2497
|
-
|
|
2621
|
+
_reportToWorld(world, {
|
|
2498
2622
|
element_name: selectors.element_name,
|
|
2499
2623
|
type: Types.ANALYZE_TABLE,
|
|
2500
2624
|
text: "Analyze table",
|
|
@@ -2567,7 +2691,7 @@ class StableBrowser {
|
|
|
2567
2691
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2568
2692
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2569
2693
|
const endTime = Date.now();
|
|
2570
|
-
|
|
2694
|
+
_reportToWorld(world, {
|
|
2571
2695
|
type: Types.GET_PAGE_STATUS,
|
|
2572
2696
|
text: "Wait for page load",
|
|
2573
2697
|
screenshotId,
|
|
@@ -2611,6 +2735,11 @@ class StableBrowser {
|
|
|
2611
2735
|
_commandFinally(state, this);
|
|
2612
2736
|
}
|
|
2613
2737
|
}
|
|
2738
|
+
saveTestDataAsGlobal(options, world) {
|
|
2739
|
+
const dataFile = this._getDataFile(world);
|
|
2740
|
+
process.env.GLOBAL_TEST_DATA_FILE = dataFile;
|
|
2741
|
+
this.logger.info("Save the scenario test data as global for the following scenarios.");
|
|
2742
|
+
}
|
|
2614
2743
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
2615
2744
|
const startTime = Date.now();
|
|
2616
2745
|
let error = null;
|
|
@@ -2634,7 +2763,7 @@ class StableBrowser {
|
|
|
2634
2763
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2635
2764
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2636
2765
|
const endTime = Date.now();
|
|
2637
|
-
|
|
2766
|
+
_reportToWorld(world, {
|
|
2638
2767
|
type: Types.SET_VIEWPORT,
|
|
2639
2768
|
text: "set viewport size to " + width + "x" + hight,
|
|
2640
2769
|
screenshotId,
|
|
@@ -2671,7 +2800,7 @@ class StableBrowser {
|
|
|
2671
2800
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2672
2801
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2673
2802
|
const endTime = Date.now();
|
|
2674
|
-
|
|
2803
|
+
_reportToWorld(world, {
|
|
2675
2804
|
type: Types.GET_PAGE_STATUS,
|
|
2676
2805
|
text: "page relaod",
|
|
2677
2806
|
screenshotId,
|
|
@@ -2707,24 +2836,33 @@ class StableBrowser {
|
|
|
2707
2836
|
console.log("#-#");
|
|
2708
2837
|
}
|
|
2709
2838
|
}
|
|
2710
|
-
_reportToWorld(world, properties) {
|
|
2711
|
-
if (!world || !world.attach) {
|
|
2712
|
-
return;
|
|
2713
|
-
}
|
|
2714
|
-
world.attach(JSON.stringify(properties), { mediaType: "application/json" });
|
|
2715
|
-
}
|
|
2716
2839
|
async beforeStep(world, step) {
|
|
2717
|
-
this.
|
|
2718
|
-
this.logger.info("step: " + stepName);
|
|
2719
|
-
if (!this.stepIndex) {
|
|
2840
|
+
if (this.stepIndex === undefined) {
|
|
2720
2841
|
this.stepIndex = 0;
|
|
2721
2842
|
}
|
|
2722
2843
|
else {
|
|
2723
2844
|
this.stepIndex++;
|
|
2724
2845
|
}
|
|
2846
|
+
if (step && step.pickleStep && step.pickleStep.text) {
|
|
2847
|
+
this.stepName = step.pickleStep.text;
|
|
2848
|
+
this.logger.info("step: " + this.stepName);
|
|
2849
|
+
}
|
|
2850
|
+
else if (step && step.text) {
|
|
2851
|
+
this.stepName = step.text;
|
|
2852
|
+
}
|
|
2853
|
+
else {
|
|
2854
|
+
this.stepName = "step " + this.stepIndex;
|
|
2855
|
+
}
|
|
2725
2856
|
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2726
2857
|
if (this.context.browserObject.context) {
|
|
2727
|
-
await this.context.browserObject.context.tracing.startChunk({ title: stepName });
|
|
2858
|
+
await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
|
|
2859
|
+
}
|
|
2860
|
+
}
|
|
2861
|
+
if (this.tags === null && step && step.pickle && step.pickle.tags) {
|
|
2862
|
+
this.tags = step.pickle.tags.map((tag) => tag.name);
|
|
2863
|
+
// check if @global_test_data tag is present
|
|
2864
|
+
if (this.tags.includes("@global_test_data")) {
|
|
2865
|
+
this.saveTestDataAsGlobal({}, world);
|
|
2728
2866
|
}
|
|
2729
2867
|
}
|
|
2730
2868
|
}
|
|
@@ -2744,156 +2882,5 @@ function createTimedPromise(promise, label) {
|
|
|
2744
2882
|
.then((result) => ({ status: "fulfilled", label, result }))
|
|
2745
2883
|
.catch((error) => Promise.reject({ status: "rejected", label, error }));
|
|
2746
2884
|
}
|
|
2747
|
-
const KEYBOARD_EVENTS = [
|
|
2748
|
-
"ALT",
|
|
2749
|
-
"AltGraph",
|
|
2750
|
-
"CapsLock",
|
|
2751
|
-
"Control",
|
|
2752
|
-
"Fn",
|
|
2753
|
-
"FnLock",
|
|
2754
|
-
"Hyper",
|
|
2755
|
-
"Meta",
|
|
2756
|
-
"NumLock",
|
|
2757
|
-
"ScrollLock",
|
|
2758
|
-
"Shift",
|
|
2759
|
-
"Super",
|
|
2760
|
-
"Symbol",
|
|
2761
|
-
"SymbolLock",
|
|
2762
|
-
"Enter",
|
|
2763
|
-
"Tab",
|
|
2764
|
-
"ArrowDown",
|
|
2765
|
-
"ArrowLeft",
|
|
2766
|
-
"ArrowRight",
|
|
2767
|
-
"ArrowUp",
|
|
2768
|
-
"End",
|
|
2769
|
-
"Home",
|
|
2770
|
-
"PageDown",
|
|
2771
|
-
"PageUp",
|
|
2772
|
-
"Backspace",
|
|
2773
|
-
"Clear",
|
|
2774
|
-
"Copy",
|
|
2775
|
-
"CrSel",
|
|
2776
|
-
"Cut",
|
|
2777
|
-
"Delete",
|
|
2778
|
-
"EraseEof",
|
|
2779
|
-
"ExSel",
|
|
2780
|
-
"Insert",
|
|
2781
|
-
"Paste",
|
|
2782
|
-
"Redo",
|
|
2783
|
-
"Undo",
|
|
2784
|
-
"Accept",
|
|
2785
|
-
"Again",
|
|
2786
|
-
"Attn",
|
|
2787
|
-
"Cancel",
|
|
2788
|
-
"ContextMenu",
|
|
2789
|
-
"Escape",
|
|
2790
|
-
"Execute",
|
|
2791
|
-
"Find",
|
|
2792
|
-
"Finish",
|
|
2793
|
-
"Help",
|
|
2794
|
-
"Pause",
|
|
2795
|
-
"Play",
|
|
2796
|
-
"Props",
|
|
2797
|
-
"Select",
|
|
2798
|
-
"ZoomIn",
|
|
2799
|
-
"ZoomOut",
|
|
2800
|
-
"BrightnessDown",
|
|
2801
|
-
"BrightnessUp",
|
|
2802
|
-
"Eject",
|
|
2803
|
-
"LogOff",
|
|
2804
|
-
"Power",
|
|
2805
|
-
"PowerOff",
|
|
2806
|
-
"PrintScreen",
|
|
2807
|
-
"Hibernate",
|
|
2808
|
-
"Standby",
|
|
2809
|
-
"WakeUp",
|
|
2810
|
-
"AllCandidates",
|
|
2811
|
-
"Alphanumeric",
|
|
2812
|
-
"CodeInput",
|
|
2813
|
-
"Compose",
|
|
2814
|
-
"Convert",
|
|
2815
|
-
"Dead",
|
|
2816
|
-
"FinalMode",
|
|
2817
|
-
"GroupFirst",
|
|
2818
|
-
"GroupLast",
|
|
2819
|
-
"GroupNext",
|
|
2820
|
-
"GroupPrevious",
|
|
2821
|
-
"ModeChange",
|
|
2822
|
-
"NextCandidate",
|
|
2823
|
-
"NonConvert",
|
|
2824
|
-
"PreviousCandidate",
|
|
2825
|
-
"Process",
|
|
2826
|
-
"SingleCandidate",
|
|
2827
|
-
"HangulMode",
|
|
2828
|
-
"HanjaMode",
|
|
2829
|
-
"JunjaMode",
|
|
2830
|
-
"Eisu",
|
|
2831
|
-
"Hankaku",
|
|
2832
|
-
"Hiragana",
|
|
2833
|
-
"HiraganaKatakana",
|
|
2834
|
-
"KanaMode",
|
|
2835
|
-
"KanjiMode",
|
|
2836
|
-
"Katakana",
|
|
2837
|
-
"Romaji",
|
|
2838
|
-
"Zenkaku",
|
|
2839
|
-
"ZenkakuHanaku",
|
|
2840
|
-
"F1",
|
|
2841
|
-
"F2",
|
|
2842
|
-
"F3",
|
|
2843
|
-
"F4",
|
|
2844
|
-
"F5",
|
|
2845
|
-
"F6",
|
|
2846
|
-
"F7",
|
|
2847
|
-
"F8",
|
|
2848
|
-
"F9",
|
|
2849
|
-
"F10",
|
|
2850
|
-
"F11",
|
|
2851
|
-
"F12",
|
|
2852
|
-
"Soft1",
|
|
2853
|
-
"Soft2",
|
|
2854
|
-
"Soft3",
|
|
2855
|
-
"Soft4",
|
|
2856
|
-
"ChannelDown",
|
|
2857
|
-
"ChannelUp",
|
|
2858
|
-
"Close",
|
|
2859
|
-
"MailForward",
|
|
2860
|
-
"MailReply",
|
|
2861
|
-
"MailSend",
|
|
2862
|
-
"MediaFastForward",
|
|
2863
|
-
"MediaPause",
|
|
2864
|
-
"MediaPlay",
|
|
2865
|
-
"MediaPlayPause",
|
|
2866
|
-
"MediaRecord",
|
|
2867
|
-
"MediaRewind",
|
|
2868
|
-
"MediaStop",
|
|
2869
|
-
"MediaTrackNext",
|
|
2870
|
-
"MediaTrackPrevious",
|
|
2871
|
-
"AudioBalanceLeft",
|
|
2872
|
-
"AudioBalanceRight",
|
|
2873
|
-
"AudioBassBoostDown",
|
|
2874
|
-
"AudioBassBoostToggle",
|
|
2875
|
-
"AudioBassBoostUp",
|
|
2876
|
-
"AudioFaderFront",
|
|
2877
|
-
"AudioFaderRear",
|
|
2878
|
-
"AudioSurroundModeNext",
|
|
2879
|
-
"AudioTrebleDown",
|
|
2880
|
-
"AudioTrebleUp",
|
|
2881
|
-
"AudioVolumeDown",
|
|
2882
|
-
"AudioVolumeMute",
|
|
2883
|
-
"AudioVolumeUp",
|
|
2884
|
-
"MicrophoneToggle",
|
|
2885
|
-
"MicrophoneVolumeDown",
|
|
2886
|
-
"MicrophoneVolumeMute",
|
|
2887
|
-
"MicrophoneVolumeUp",
|
|
2888
|
-
"TV",
|
|
2889
|
-
"TV3DMode",
|
|
2890
|
-
"TVAntennaCable",
|
|
2891
|
-
"TVAudioDescription",
|
|
2892
|
-
];
|
|
2893
|
-
function unEscapeString(str) {
|
|
2894
|
-
const placeholder = "__NEWLINE__";
|
|
2895
|
-
str = str.replace(new RegExp(placeholder, "g"), "\n");
|
|
2896
|
-
return str;
|
|
2897
|
-
}
|
|
2898
2885
|
export { StableBrowser };
|
|
2899
2886
|
//# sourceMappingURL=stable_browser.js.map
|