automation_model 1.0.508-dev → 1.0.508-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 +98 -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 +6 -3
- package/lib/browser_manager.js +90 -32
- package/lib/browser_manager.js.map +1 -1
- package/lib/command_common.d.ts +1 -0
- package/lib/command_common.js +37 -5
- package/lib/command_common.js.map +1 -1
- package/lib/error-messages.js +8 -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 +20 -6
- 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 +16 -3
- package/lib/network.js.map +1 -1
- package/lib/stable_browser.d.ts +54 -17
- package/lib/stable_browser.js +481 -477
- 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/test_context.d.ts +1 -0
- package/lib/test_context.js +1 -0
- package/lib/test_context.js.map +1 -1
- package/lib/utils.d.ts +13 -1
- package/lib/utils.js +298 -3
- 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,9 +150,9 @@ 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
|
-
let newContext = await getContext(null, this.context.headless ? this.context.headless : false, this, this.logger, appName, false, this);
|
|
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;
|
|
150
157
|
apps[appName] = {
|
|
151
158
|
context: newContext,
|
|
@@ -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
267
|
}
|
|
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);
|
|
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
|
-
|
|
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;
|
|
366
361
|
}
|
|
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
362
|
}
|
|
421
|
-
|
|
422
|
-
|
|
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
|
-
}
|
|
363
|
+
if (!el.setAttribute) {
|
|
364
|
+
el = el.parentElement;
|
|
445
365
|
}
|
|
366
|
+
el.setAttribute("data-blinq-id-" + randomToken, "");
|
|
367
|
+
return true;
|
|
368
|
+
}, [tag1, randomToken]))) {
|
|
369
|
+
continue;
|
|
446
370
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
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]);
|
|
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,14 @@ class StableBrowser {
|
|
|
541
459
|
if (!info.printMessages) {
|
|
542
460
|
info.printMessages = {};
|
|
543
461
|
}
|
|
462
|
+
if (info.locatorLog && !visible) {
|
|
463
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND_NOT_VISIBLE");
|
|
464
|
+
}
|
|
465
|
+
if (info.locatorLog && !enabled) {
|
|
466
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND_NOT_ENABLED");
|
|
467
|
+
}
|
|
544
468
|
if (!info.printMessages[j.toString()]) {
|
|
545
|
-
info.log += "element " + locator + " visible " + visible + " enabled " + enabled + "\n";
|
|
469
|
+
//info.log += "element " + locator + " visible " + visible + " enabled " + enabled + "\n";
|
|
546
470
|
info.printMessages[j.toString()] = true;
|
|
547
471
|
}
|
|
548
472
|
}
|
|
@@ -558,7 +482,7 @@ class StableBrowser {
|
|
|
558
482
|
if (!info) {
|
|
559
483
|
info = {};
|
|
560
484
|
}
|
|
561
|
-
info.log += "scan for popup handlers" + "\n";
|
|
485
|
+
//info.log += "scan for popup handlers" + "\n";
|
|
562
486
|
const handlerGroup = [];
|
|
563
487
|
for (let i = 0; i < this.configuration.popupHandlers.length; i++) {
|
|
564
488
|
handlerGroup.push(this.configuration.popupHandlers[i].locator);
|
|
@@ -585,16 +509,28 @@ class StableBrowser {
|
|
|
585
509
|
}
|
|
586
510
|
if (result.foundElements.length > 0) {
|
|
587
511
|
let dialogCloseLocator = result.foundElements[0].locator;
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
512
|
+
try {
|
|
513
|
+
await scope?.evaluate(() => {
|
|
514
|
+
window.__isClosingPopups = true;
|
|
515
|
+
});
|
|
516
|
+
await dialogCloseLocator.click();
|
|
517
|
+
// wait for the dialog to close
|
|
518
|
+
await dialogCloseLocator.waitFor({ state: "hidden" });
|
|
519
|
+
}
|
|
520
|
+
catch (e) {
|
|
521
|
+
}
|
|
522
|
+
finally {
|
|
523
|
+
await scope?.evaluate(() => {
|
|
524
|
+
window.__isClosingPopups = false;
|
|
525
|
+
});
|
|
526
|
+
}
|
|
591
527
|
return { rerun: true };
|
|
592
528
|
}
|
|
593
529
|
}
|
|
594
530
|
}
|
|
595
531
|
return { rerun: false };
|
|
596
532
|
}
|
|
597
|
-
async _locate(selectors, info, _params, timeout) {
|
|
533
|
+
async _locate(selectors, info, _params, timeout, allowDisabled = false) {
|
|
598
534
|
if (!timeout) {
|
|
599
535
|
timeout = 30000;
|
|
600
536
|
}
|
|
@@ -604,7 +540,7 @@ class StableBrowser {
|
|
|
604
540
|
let selector = selectors.locators[j];
|
|
605
541
|
info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
|
|
606
542
|
}
|
|
607
|
-
let element = await this._locate_internal(selectors, info, _params, timeout);
|
|
543
|
+
let element = await this._locate_internal(selectors, info, _params, timeout, allowDisabled);
|
|
608
544
|
if (!element.rerun) {
|
|
609
545
|
return element;
|
|
610
546
|
}
|
|
@@ -617,6 +553,7 @@ class StableBrowser {
|
|
|
617
553
|
info.failCause = {};
|
|
618
554
|
info.log = "";
|
|
619
555
|
}
|
|
556
|
+
let startTime = Date.now();
|
|
620
557
|
let scope = this.page;
|
|
621
558
|
if (selectors.frame) {
|
|
622
559
|
return selectors.frame;
|
|
@@ -647,9 +584,11 @@ class StableBrowser {
|
|
|
647
584
|
}
|
|
648
585
|
return framescope;
|
|
649
586
|
};
|
|
587
|
+
let fLocator = null;
|
|
650
588
|
while (true) {
|
|
651
589
|
let frameFound = false;
|
|
652
590
|
if (selectors.nestFrmLoc) {
|
|
591
|
+
fLocator = selectors.nestFrmLoc;
|
|
653
592
|
scope = await findFrame(selectors.nestFrmLoc, scope);
|
|
654
593
|
frameFound = true;
|
|
655
594
|
break;
|
|
@@ -658,6 +597,7 @@ class StableBrowser {
|
|
|
658
597
|
for (let i = 0; i < selectors.frameLocators.length; i++) {
|
|
659
598
|
let frameLocator = selectors.frameLocators[i];
|
|
660
599
|
if (frameLocator.css) {
|
|
600
|
+
fLocator = frameLocator.css;
|
|
661
601
|
scope = scope.frameLocator(frameLocator.css);
|
|
662
602
|
frameFound = true;
|
|
663
603
|
break;
|
|
@@ -665,11 +605,15 @@ class StableBrowser {
|
|
|
665
605
|
}
|
|
666
606
|
}
|
|
667
607
|
if (!frameFound && selectors.iframe_src) {
|
|
608
|
+
fLocator = selectors.iframe_src;
|
|
668
609
|
scope = this.page.frame({ url: selectors.iframe_src });
|
|
669
610
|
}
|
|
670
611
|
if (!scope) {
|
|
671
|
-
info
|
|
672
|
-
|
|
612
|
+
if (info && info.locatorLog) {
|
|
613
|
+
info.locatorLog.setLocatorSearchStatus("frame-" + fLocator, "NOT_FOUND");
|
|
614
|
+
}
|
|
615
|
+
//info.log += "unable to locate iframe " + selectors.iframe_src + "\n";
|
|
616
|
+
if (Date.now() - startTime > timeout) {
|
|
673
617
|
info.failCause.iframeNotFound = true;
|
|
674
618
|
info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
|
|
675
619
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
@@ -677,6 +621,9 @@ class StableBrowser {
|
|
|
677
621
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
678
622
|
}
|
|
679
623
|
else {
|
|
624
|
+
if (info && info.locatorLog) {
|
|
625
|
+
info.locatorLog.setLocatorSearchStatus("frame-" + fLocator, "FOUND");
|
|
626
|
+
}
|
|
680
627
|
break;
|
|
681
628
|
}
|
|
682
629
|
}
|
|
@@ -693,16 +640,18 @@ class StableBrowser {
|
|
|
693
640
|
return bodyContent;
|
|
694
641
|
});
|
|
695
642
|
}
|
|
696
|
-
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
643
|
+
async _locate_internal(selectors, info, _params, timeout = 30000, allowDisabled = false) {
|
|
697
644
|
if (!info) {
|
|
698
645
|
info = {};
|
|
699
646
|
info.failCause = {};
|
|
700
647
|
info.log = "";
|
|
648
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
701
649
|
}
|
|
702
650
|
let highPriorityTimeout = 5000;
|
|
703
651
|
let visibleOnlyTimeout = 6000;
|
|
704
|
-
let startTime =
|
|
652
|
+
let startTime = Date.now();
|
|
705
653
|
let locatorsCount = 0;
|
|
654
|
+
let lazy_scroll = false;
|
|
706
655
|
//let arrayMode = Array.isArray(selectors);
|
|
707
656
|
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
708
657
|
let selectorsLocators = null;
|
|
@@ -740,17 +689,17 @@ class StableBrowser {
|
|
|
740
689
|
}
|
|
741
690
|
// info.log += "scanning locators in priority 1" + "\n";
|
|
742
691
|
let onlyPriority3 = selectorsLocators[0].priority === 3;
|
|
743
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["1"], scope, _params, info, visibleOnly);
|
|
692
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["1"], scope, _params, info, visibleOnly, allowDisabled);
|
|
744
693
|
if (result.foundElements.length === 0) {
|
|
745
694
|
// info.log += "scanning locators in priority 2" + "\n";
|
|
746
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["2"], scope, _params, info, visibleOnly);
|
|
695
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["2"], scope, _params, info, visibleOnly, allowDisabled);
|
|
747
696
|
}
|
|
748
697
|
if (result.foundElements.length === 0 && onlyPriority3) {
|
|
749
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly);
|
|
698
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly, allowDisabled);
|
|
750
699
|
}
|
|
751
700
|
else {
|
|
752
701
|
if (result.foundElements.length === 0 && !highPriorityOnly) {
|
|
753
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly);
|
|
702
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly, allowDisabled);
|
|
754
703
|
}
|
|
755
704
|
}
|
|
756
705
|
let foundElements = result.foundElements;
|
|
@@ -791,26 +740,36 @@ class StableBrowser {
|
|
|
791
740
|
return maxCountElement.locator;
|
|
792
741
|
}
|
|
793
742
|
}
|
|
794
|
-
if (
|
|
743
|
+
if (Date.now() - startTime > timeout) {
|
|
795
744
|
break;
|
|
796
745
|
}
|
|
797
|
-
if (
|
|
798
|
-
info.log += "high priority timeout, will try all elements" + "\n";
|
|
746
|
+
if (Date.now() - startTime > highPriorityTimeout) {
|
|
747
|
+
//info.log += "high priority timeout, will try all elements" + "\n";
|
|
799
748
|
highPriorityOnly = false;
|
|
749
|
+
if (this.configuration && this.configuration.load_all_lazy === true && !lazy_scroll) {
|
|
750
|
+
lazy_scroll = true;
|
|
751
|
+
await scrollPageToLoadLazyElements(this.page);
|
|
752
|
+
}
|
|
800
753
|
}
|
|
801
|
-
if (
|
|
802
|
-
info.log += "visible only timeout, will try all elements" + "\n";
|
|
754
|
+
if (Date.now() - startTime > visibleOnlyTimeout) {
|
|
755
|
+
//info.log += "visible only timeout, will try all elements" + "\n";
|
|
803
756
|
visibleOnly = false;
|
|
804
757
|
}
|
|
805
758
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
806
759
|
}
|
|
807
760
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
808
|
-
info.
|
|
761
|
+
// if (info.locatorLog) {
|
|
762
|
+
// const lines = info.locatorLog.toString().split("\n");
|
|
763
|
+
// for (let line of lines) {
|
|
764
|
+
// this.logger.debug(line);
|
|
765
|
+
// }
|
|
766
|
+
// }
|
|
767
|
+
//info.log += "failed to locate unique element, total elements found " + locatorsCount + "\n";
|
|
809
768
|
info.failCause.locatorNotFound = true;
|
|
810
769
|
info.failCause.lastError = "failed to locate unique element";
|
|
811
770
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
812
771
|
}
|
|
813
|
-
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
772
|
+
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly, allowDisabled = false) {
|
|
814
773
|
let foundElements = [];
|
|
815
774
|
const result = {
|
|
816
775
|
foundElements: foundElements,
|
|
@@ -818,14 +777,15 @@ class StableBrowser {
|
|
|
818
777
|
for (let i = 0; i < locatorsGroup.length; i++) {
|
|
819
778
|
let foundLocators = [];
|
|
820
779
|
try {
|
|
821
|
-
await this._collectLocatorInformation(locatorsGroup, i, scope, foundLocators, _params, info, visibleOnly);
|
|
780
|
+
await this._collectLocatorInformation(locatorsGroup, i, scope, foundLocators, _params, info, visibleOnly, allowDisabled);
|
|
822
781
|
}
|
|
823
782
|
catch (e) {
|
|
824
|
-
this
|
|
825
|
-
this.logger.debug(
|
|
783
|
+
// this call can fail it the browser is navigating
|
|
784
|
+
// this.logger.debug("unable to use locator " + JSON.stringify(locatorsGroup[i]));
|
|
785
|
+
// this.logger.debug(e);
|
|
826
786
|
foundLocators = [];
|
|
827
787
|
try {
|
|
828
|
-
await this._collectLocatorInformation(locatorsGroup, i, this.page, foundLocators, _params, info, visibleOnly);
|
|
788
|
+
await this._collectLocatorInformation(locatorsGroup, i, this.page, foundLocators, _params, info, visibleOnly, allowDisabled);
|
|
829
789
|
}
|
|
830
790
|
catch (e) {
|
|
831
791
|
this.logger.info("unable to use locator (second try) " + JSON.stringify(locatorsGroup[i]));
|
|
@@ -841,11 +801,27 @@ class StableBrowser {
|
|
|
841
801
|
}
|
|
842
802
|
if (foundLocators.length > 1) {
|
|
843
803
|
info.failCause.foundMultiple = true;
|
|
804
|
+
if (info.locatorLog) {
|
|
805
|
+
info.locatorLog.setLocatorSearchStatus(JSON.stringify(locatorsGroup[i]), "FOUND_NOT_UNIQUE");
|
|
806
|
+
}
|
|
844
807
|
}
|
|
845
808
|
}
|
|
846
809
|
return result;
|
|
847
810
|
}
|
|
848
811
|
async simpleClick(elementDescription, _params, options = {}, world = null) {
|
|
812
|
+
const state = {
|
|
813
|
+
locate: false,
|
|
814
|
+
scroll: false,
|
|
815
|
+
highlight: false,
|
|
816
|
+
_params,
|
|
817
|
+
options,
|
|
818
|
+
world,
|
|
819
|
+
type: Types.CLICK,
|
|
820
|
+
text: "Click element",
|
|
821
|
+
operation: "simpleClick",
|
|
822
|
+
log: "***** click on " + elementDescription + " *****\n",
|
|
823
|
+
};
|
|
824
|
+
_preCommand(state, this);
|
|
849
825
|
const startTime = Date.now();
|
|
850
826
|
let timeout = 30000;
|
|
851
827
|
if (options && options.timeout) {
|
|
@@ -870,13 +846,31 @@ class StableBrowser {
|
|
|
870
846
|
catch (e) {
|
|
871
847
|
if (performance.now() - startTime > timeout) {
|
|
872
848
|
// throw e;
|
|
873
|
-
|
|
849
|
+
try {
|
|
850
|
+
await _commandError(state, "timeout looking for " + elementDescription, this);
|
|
851
|
+
}
|
|
852
|
+
finally {
|
|
853
|
+
_commandFinally(state, this);
|
|
854
|
+
}
|
|
874
855
|
}
|
|
875
856
|
}
|
|
876
857
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
877
858
|
}
|
|
878
859
|
}
|
|
879
860
|
async simpleClickType(elementDescription, value, _params, options = {}, world = null) {
|
|
861
|
+
const state = {
|
|
862
|
+
locate: false,
|
|
863
|
+
scroll: false,
|
|
864
|
+
highlight: false,
|
|
865
|
+
_params,
|
|
866
|
+
options,
|
|
867
|
+
world,
|
|
868
|
+
type: Types.FILL,
|
|
869
|
+
text: "Fill element",
|
|
870
|
+
operation: "simpleClickType",
|
|
871
|
+
log: "***** click type on " + elementDescription + " *****\n",
|
|
872
|
+
};
|
|
873
|
+
_preCommand(state, this);
|
|
880
874
|
const startTime = Date.now();
|
|
881
875
|
let timeout = 30000;
|
|
882
876
|
if (options && options.timeout) {
|
|
@@ -901,7 +895,12 @@ class StableBrowser {
|
|
|
901
895
|
catch (e) {
|
|
902
896
|
if (performance.now() - startTime > timeout) {
|
|
903
897
|
// throw e;
|
|
904
|
-
|
|
898
|
+
try {
|
|
899
|
+
await _commandError(state, "timeout looking for " + elementDescription, this);
|
|
900
|
+
}
|
|
901
|
+
finally {
|
|
902
|
+
_commandFinally(state, this);
|
|
903
|
+
}
|
|
905
904
|
}
|
|
906
905
|
}
|
|
907
906
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
@@ -920,18 +919,18 @@ class StableBrowser {
|
|
|
920
919
|
};
|
|
921
920
|
try {
|
|
922
921
|
await _preCommand(state, this);
|
|
923
|
-
if (state.options && state.options.context) {
|
|
924
|
-
|
|
925
|
-
}
|
|
922
|
+
// if (state.options && state.options.context) {
|
|
923
|
+
// state.selectors.locators[0].text = state.options.context;
|
|
924
|
+
// }
|
|
926
925
|
try {
|
|
927
926
|
await state.element.click();
|
|
928
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
927
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
929
928
|
}
|
|
930
929
|
catch (e) {
|
|
931
930
|
// await this.closeUnexpectedPopups();
|
|
932
931
|
state.element = await this._locate(selectors, state.info, _params);
|
|
933
932
|
await state.element.dispatchEvent("click");
|
|
934
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
933
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
935
934
|
}
|
|
936
935
|
await this.waitForPageLoad();
|
|
937
936
|
return state.info;
|
|
@@ -1010,7 +1009,6 @@ class StableBrowser {
|
|
|
1010
1009
|
await state.element.hover({ timeout: 10000 });
|
|
1011
1010
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1012
1011
|
}
|
|
1013
|
-
await _screenshot(state, this);
|
|
1014
1012
|
await this.waitForPageLoad();
|
|
1015
1013
|
return state.info;
|
|
1016
1014
|
}
|
|
@@ -1282,7 +1280,12 @@ class StableBrowser {
|
|
|
1282
1280
|
await this.waitForPageLoad();
|
|
1283
1281
|
}
|
|
1284
1282
|
else if (enter === false) {
|
|
1285
|
-
|
|
1283
|
+
try {
|
|
1284
|
+
await state.element.dispatchEvent("change", null, { timeout: 5000 });
|
|
1285
|
+
}
|
|
1286
|
+
catch (e) {
|
|
1287
|
+
// ignore
|
|
1288
|
+
}
|
|
1286
1289
|
//await this.page.keyboard.press("Tab");
|
|
1287
1290
|
}
|
|
1288
1291
|
else {
|
|
@@ -1339,6 +1342,7 @@ class StableBrowser {
|
|
|
1339
1342
|
let screenshotPath = null;
|
|
1340
1343
|
if (!info.log) {
|
|
1341
1344
|
info.log = "";
|
|
1345
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
1342
1346
|
}
|
|
1343
1347
|
info.operation = "getText";
|
|
1344
1348
|
info.selectors = selectors;
|
|
@@ -1677,11 +1681,9 @@ class StableBrowser {
|
|
|
1677
1681
|
if (!fs.existsSync(world.screenshotPath)) {
|
|
1678
1682
|
fs.mkdirSync(world.screenshotPath, { recursive: true });
|
|
1679
1683
|
}
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
}
|
|
1684
|
-
const screenshotPath = path.join(world.screenshotPath, nextIndex + ".png");
|
|
1684
|
+
// to make sure the path doesn't start with -
|
|
1685
|
+
const uuidStr = "id_" + randomUUID();
|
|
1686
|
+
const screenshotPath = path.join(world.screenshotPath, uuidStr + ".png");
|
|
1685
1687
|
try {
|
|
1686
1688
|
await this.takeScreenshot(screenshotPath);
|
|
1687
1689
|
// let buffer = await this.page.screenshot({ timeout: 4000 });
|
|
@@ -1695,7 +1697,7 @@ class StableBrowser {
|
|
|
1695
1697
|
catch (e) {
|
|
1696
1698
|
this.logger.info("unable to take screenshot, ignored");
|
|
1697
1699
|
}
|
|
1698
|
-
result.screenshotId =
|
|
1700
|
+
result.screenshotId = uuidStr;
|
|
1699
1701
|
result.screenshotPath = screenshotPath;
|
|
1700
1702
|
if (info && info.box) {
|
|
1701
1703
|
await drawRectangle(screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
|
|
@@ -1823,10 +1825,6 @@ class StableBrowser {
|
|
|
1823
1825
|
break;
|
|
1824
1826
|
}
|
|
1825
1827
|
state.info.value = state.value;
|
|
1826
|
-
this[variable] = state.value;
|
|
1827
|
-
if (world) {
|
|
1828
|
-
world[variable] = state.value;
|
|
1829
|
-
}
|
|
1830
1828
|
this.setTestData({ [variable]: state.value }, world);
|
|
1831
1829
|
this.logger.info("set test data: " + variable + "=" + state.value);
|
|
1832
1830
|
return state.info;
|
|
@@ -1838,6 +1836,69 @@ class StableBrowser {
|
|
|
1838
1836
|
_commandFinally(state, this);
|
|
1839
1837
|
}
|
|
1840
1838
|
}
|
|
1839
|
+
async verifyAttribute(selectors, attribute, value, _params = null, options = {}, world = null) {
|
|
1840
|
+
const state = {
|
|
1841
|
+
selectors,
|
|
1842
|
+
_params,
|
|
1843
|
+
attribute,
|
|
1844
|
+
value,
|
|
1845
|
+
options,
|
|
1846
|
+
world,
|
|
1847
|
+
type: Types.VERIFY_ATTRIBUTE,
|
|
1848
|
+
highlight: true,
|
|
1849
|
+
screenshot: true,
|
|
1850
|
+
text: `Verify element attribute`,
|
|
1851
|
+
operation: "verifyAttribute",
|
|
1852
|
+
log: "***** verify attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1853
|
+
allowDisabled: true,
|
|
1854
|
+
};
|
|
1855
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1856
|
+
let val;
|
|
1857
|
+
try {
|
|
1858
|
+
await _preCommand(state, this);
|
|
1859
|
+
switch (attribute) {
|
|
1860
|
+
case "innerText":
|
|
1861
|
+
val = String(await state.element.innerText());
|
|
1862
|
+
break;
|
|
1863
|
+
case "value":
|
|
1864
|
+
val = String(await state.element.inputValue());
|
|
1865
|
+
break;
|
|
1866
|
+
case "checked":
|
|
1867
|
+
val = String(await state.element.isChecked());
|
|
1868
|
+
break;
|
|
1869
|
+
case "disabled":
|
|
1870
|
+
val = String(await state.element.isDisabled());
|
|
1871
|
+
break;
|
|
1872
|
+
case "readOnly":
|
|
1873
|
+
const isEditable = await state.element.isEditable();
|
|
1874
|
+
val = String(!isEditable);
|
|
1875
|
+
break;
|
|
1876
|
+
default:
|
|
1877
|
+
val = String(await state.element.getAttribute(attribute));
|
|
1878
|
+
break;
|
|
1879
|
+
}
|
|
1880
|
+
state.info.expectedValue = val;
|
|
1881
|
+
let regex;
|
|
1882
|
+
if (value.startsWith("/") && value.endsWith("/")) {
|
|
1883
|
+
const patternBody = value.slice(1, -1);
|
|
1884
|
+
regex = new RegExp(patternBody, "g");
|
|
1885
|
+
}
|
|
1886
|
+
else {
|
|
1887
|
+
const escapedPattern = value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1888
|
+
regex = new RegExp(escapedPattern, "g");
|
|
1889
|
+
}
|
|
1890
|
+
if (!val.match(regex)) {
|
|
1891
|
+
throw new Error(`The ${attribute} attribute has a value of "${val}", but the expected value is "${value}"`);
|
|
1892
|
+
}
|
|
1893
|
+
return state.info;
|
|
1894
|
+
}
|
|
1895
|
+
catch (e) {
|
|
1896
|
+
await _commandError(state, e, this);
|
|
1897
|
+
}
|
|
1898
|
+
finally {
|
|
1899
|
+
_commandFinally(state, this);
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1841
1902
|
async extractEmailData(emailAddress, options, world) {
|
|
1842
1903
|
if (!emailAddress) {
|
|
1843
1904
|
throw new Error("email address is null");
|
|
@@ -1856,7 +1917,7 @@ class StableBrowser {
|
|
|
1856
1917
|
if (options && options.timeout) {
|
|
1857
1918
|
timeout = options.timeout;
|
|
1858
1919
|
}
|
|
1859
|
-
const serviceUrl =
|
|
1920
|
+
const serviceUrl = _getServerUrl() + "/api/mail/createLinkOrCodeFromEmail";
|
|
1860
1921
|
const request = {
|
|
1861
1922
|
method: "POST",
|
|
1862
1923
|
url: serviceUrl,
|
|
@@ -1933,15 +1994,15 @@ class StableBrowser {
|
|
|
1933
1994
|
scope
|
|
1934
1995
|
.evaluate((node) => {
|
|
1935
1996
|
if (node && node.style) {
|
|
1936
|
-
let originalBorder = node.style.
|
|
1937
|
-
node.style.
|
|
1997
|
+
let originalBorder = node.style.outline;
|
|
1998
|
+
node.style.outline = "2px solid red";
|
|
1938
1999
|
if (window) {
|
|
1939
2000
|
window.addEventListener("beforeunload", function (e) {
|
|
1940
|
-
node.style.
|
|
2001
|
+
node.style.outline = originalBorder;
|
|
1941
2002
|
});
|
|
1942
2003
|
}
|
|
1943
2004
|
setTimeout(function () {
|
|
1944
|
-
node.style.
|
|
2005
|
+
node.style.outline = originalBorder;
|
|
1945
2006
|
}, 2000);
|
|
1946
2007
|
}
|
|
1947
2008
|
})
|
|
@@ -1963,17 +2024,17 @@ class StableBrowser {
|
|
|
1963
2024
|
if (!element.style) {
|
|
1964
2025
|
return;
|
|
1965
2026
|
}
|
|
1966
|
-
var originalBorder = element.style.
|
|
2027
|
+
var originalBorder = element.style.outline;
|
|
1967
2028
|
// Set the new border to be red and 2px solid
|
|
1968
|
-
element.style.
|
|
2029
|
+
element.style.outline = "2px solid red";
|
|
1969
2030
|
if (window) {
|
|
1970
2031
|
window.addEventListener("beforeunload", function (e) {
|
|
1971
|
-
element.style.
|
|
2032
|
+
element.style.outline = originalBorder;
|
|
1972
2033
|
});
|
|
1973
2034
|
}
|
|
1974
2035
|
// Set a timeout to revert to the original border after 2 seconds
|
|
1975
2036
|
setTimeout(function () {
|
|
1976
|
-
element.style.
|
|
2037
|
+
element.style.outline = originalBorder;
|
|
1977
2038
|
}, 2000);
|
|
1978
2039
|
}
|
|
1979
2040
|
return;
|
|
@@ -2029,7 +2090,7 @@ class StableBrowser {
|
|
|
2029
2090
|
}
|
|
2030
2091
|
finally {
|
|
2031
2092
|
const endTime = Date.now();
|
|
2032
|
-
|
|
2093
|
+
_reportToWorld(world, {
|
|
2033
2094
|
type: Types.VERIFY_PAGE_PATH,
|
|
2034
2095
|
text: "Verify page path",
|
|
2035
2096
|
screenshotId,
|
|
@@ -2049,6 +2110,35 @@ class StableBrowser {
|
|
|
2049
2110
|
});
|
|
2050
2111
|
}
|
|
2051
2112
|
}
|
|
2113
|
+
async findTextInAllFrames(dateAlternatives, numberAlternatives, text, state) {
|
|
2114
|
+
const frames = this.page.frames();
|
|
2115
|
+
let results = [];
|
|
2116
|
+
let ignoreCase = false;
|
|
2117
|
+
for (let i = 0; i < frames.length; i++) {
|
|
2118
|
+
if (dateAlternatives.date) {
|
|
2119
|
+
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2120
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2121
|
+
result.frame = frames[i];
|
|
2122
|
+
results.push(result);
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
else if (numberAlternatives.number) {
|
|
2126
|
+
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2127
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2128
|
+
result.frame = frames[i];
|
|
2129
|
+
results.push(result);
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
else {
|
|
2133
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2134
|
+
result.frame = frames[i];
|
|
2135
|
+
results.push(result);
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
state.info.results = results;
|
|
2139
|
+
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2140
|
+
return resultWithElementsFound;
|
|
2141
|
+
}
|
|
2052
2142
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2053
2143
|
text = unEscapeString(text);
|
|
2054
2144
|
const state = {
|
|
@@ -2058,7 +2148,7 @@ class StableBrowser {
|
|
|
2058
2148
|
locate: false,
|
|
2059
2149
|
scroll: false,
|
|
2060
2150
|
highlight: false,
|
|
2061
|
-
type: Types.
|
|
2151
|
+
type: Types.VERIFY_PAGE_CONTAINS_TEXT,
|
|
2062
2152
|
text: `Verify text exists in page`,
|
|
2063
2153
|
operation: "verifyTextExistInPage",
|
|
2064
2154
|
log: "***** verify text " + text + " exists in page *****\n",
|
|
@@ -2076,31 +2166,7 @@ class StableBrowser {
|
|
|
2076
2166
|
await _preCommand(state, this);
|
|
2077
2167
|
state.info.text = text;
|
|
2078
2168
|
while (true) {
|
|
2079
|
-
const
|
|
2080
|
-
let results = [];
|
|
2081
|
-
for (let i = 0; i < frames.length; i++) {
|
|
2082
|
-
if (dateAlternatives.date) {
|
|
2083
|
-
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2084
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2085
|
-
result.frame = frames[i];
|
|
2086
|
-
results.push(result);
|
|
2087
|
-
}
|
|
2088
|
-
}
|
|
2089
|
-
else if (numberAlternatives.number) {
|
|
2090
|
-
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2091
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2092
|
-
result.frame = frames[i];
|
|
2093
|
-
results.push(result);
|
|
2094
|
-
}
|
|
2095
|
-
}
|
|
2096
|
-
else {
|
|
2097
|
-
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2098
|
-
result.frame = frames[i];
|
|
2099
|
-
results.push(result);
|
|
2100
|
-
}
|
|
2101
|
-
}
|
|
2102
|
-
state.info.results = results;
|
|
2103
|
-
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2169
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, text, state);
|
|
2104
2170
|
if (resultWithElementsFound.length === 0) {
|
|
2105
2171
|
if (Date.now() - state.startTime > timeout) {
|
|
2106
2172
|
throw new Error(`Text ${text} not found in page`);
|
|
@@ -2110,9 +2176,9 @@ class StableBrowser {
|
|
|
2110
2176
|
}
|
|
2111
2177
|
if (resultWithElementsFound[0].randomToken) {
|
|
2112
2178
|
const frame = resultWithElementsFound[0].frame;
|
|
2113
|
-
const dataAttribute = `[data-blinq-id
|
|
2179
|
+
const dataAttribute = `[data-blinq-id-${resultWithElementsFound[0].randomToken}]`;
|
|
2114
2180
|
await this._highlightElements(frame, dataAttribute);
|
|
2115
|
-
const element = await frame
|
|
2181
|
+
const element = await frame.locator(dataAttribute).first();
|
|
2116
2182
|
if (element) {
|
|
2117
2183
|
await this.scrollIfNeeded(element, state.info);
|
|
2118
2184
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
@@ -2157,31 +2223,7 @@ class StableBrowser {
|
|
|
2157
2223
|
await _preCommand(state, this);
|
|
2158
2224
|
state.info.text = text;
|
|
2159
2225
|
while (true) {
|
|
2160
|
-
const
|
|
2161
|
-
let results = [];
|
|
2162
|
-
for (let i = 0; i < frames.length; i++) {
|
|
2163
|
-
if (dateAlternatives.date) {
|
|
2164
|
-
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2165
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2166
|
-
result.frame = frames[i];
|
|
2167
|
-
results.push(result);
|
|
2168
|
-
}
|
|
2169
|
-
}
|
|
2170
|
-
else if (numberAlternatives.number) {
|
|
2171
|
-
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2172
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2173
|
-
result.frame = frames[i];
|
|
2174
|
-
results.push(result);
|
|
2175
|
-
}
|
|
2176
|
-
}
|
|
2177
|
-
else {
|
|
2178
|
-
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2179
|
-
result.frame = frames[i];
|
|
2180
|
-
results.push(result);
|
|
2181
|
-
}
|
|
2182
|
-
}
|
|
2183
|
-
state.info.results = results;
|
|
2184
|
-
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2226
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, text, state);
|
|
2185
2227
|
if (resultWithElementsFound.length === 0) {
|
|
2186
2228
|
await _screenshot(state, this);
|
|
2187
2229
|
return state.info;
|
|
@@ -2199,15 +2241,88 @@ class StableBrowser {
|
|
|
2199
2241
|
_commandFinally(state, this);
|
|
2200
2242
|
}
|
|
2201
2243
|
}
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2244
|
+
async verifyTextRelatedToText(textAnchor, climb, textToVerify, options = {}, world = null) {
|
|
2245
|
+
textAnchor = unEscapeString(textAnchor);
|
|
2246
|
+
textToVerify = unEscapeString(textToVerify);
|
|
2247
|
+
const state = {
|
|
2248
|
+
text_search: textToVerify,
|
|
2249
|
+
options,
|
|
2250
|
+
world,
|
|
2251
|
+
locate: false,
|
|
2252
|
+
scroll: false,
|
|
2253
|
+
highlight: false,
|
|
2254
|
+
type: Types.VERIFY_TEXT_WITH_RELATION,
|
|
2255
|
+
text: `Verify text with relation to another text`,
|
|
2256
|
+
operation: "verify_text_with_relation",
|
|
2257
|
+
log: "***** search for " + textAnchor + " climb " + climb + " and verify " + textToVerify + " found *****\n",
|
|
2258
|
+
};
|
|
2259
|
+
const timeout = this._getLoadTimeout(options);
|
|
2260
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2261
|
+
let newValue = await this._replaceWithLocalData(textAnchor, world);
|
|
2262
|
+
if (newValue !== textAnchor) {
|
|
2263
|
+
this.logger.info(textAnchor + "=" + newValue);
|
|
2264
|
+
textAnchor = newValue;
|
|
2265
|
+
}
|
|
2266
|
+
newValue = await this._replaceWithLocalData(textToVerify, world);
|
|
2267
|
+
if (newValue !== textToVerify) {
|
|
2268
|
+
this.logger.info(textToVerify + "=" + newValue);
|
|
2269
|
+
textToVerify = newValue;
|
|
2270
|
+
}
|
|
2271
|
+
let dateAlternatives = findDateAlternatives(textToVerify);
|
|
2272
|
+
let numberAlternatives = findNumberAlternatives(textToVerify);
|
|
2273
|
+
let foundAncore = false;
|
|
2274
|
+
try {
|
|
2275
|
+
await _preCommand(state, this);
|
|
2276
|
+
state.info.text = textToVerify;
|
|
2277
|
+
while (true) {
|
|
2278
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, textAnchor, state);
|
|
2279
|
+
if (resultWithElementsFound.length === 0) {
|
|
2280
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2281
|
+
throw new Error(`Text ${foundAncore ? textToVerify : textAnchor} not found in page`);
|
|
2282
|
+
}
|
|
2283
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2284
|
+
continue;
|
|
2285
|
+
}
|
|
2286
|
+
for (let i = 0; i < resultWithElementsFound.length; i++) {
|
|
2287
|
+
foundAncore = true;
|
|
2288
|
+
const result = resultWithElementsFound[i];
|
|
2289
|
+
const token = result.randomToken;
|
|
2290
|
+
const frame = result.frame;
|
|
2291
|
+
let css = `[data-blinq-id-${token}]`;
|
|
2292
|
+
const climbArray1 = [];
|
|
2293
|
+
for (let i = 0; i < climb; i++) {
|
|
2294
|
+
climbArray1.push("..");
|
|
2295
|
+
}
|
|
2296
|
+
let climbXpath = "xpath=" + climbArray1.join("/");
|
|
2297
|
+
css = css + " >> " + climbXpath;
|
|
2298
|
+
const count = await frame.locator(css).count();
|
|
2299
|
+
for (let j = 0; j < count; j++) {
|
|
2300
|
+
const continer = await frame.locator(css).nth(j);
|
|
2301
|
+
const result = await this._locateElementByText(continer, textToVerify, "*", false, true, true, {});
|
|
2302
|
+
if (result.elementCount > 0) {
|
|
2303
|
+
const dataAttribute = "[data-blinq-id-" + result.randomToken + "]";
|
|
2304
|
+
//const cssAnchor = `[data-blinq-id="blinq-id-${token}-anchor"]`;
|
|
2305
|
+
await this._highlightElements(frame, dataAttribute);
|
|
2306
|
+
//await this._highlightElements(frame, cssAnchor);
|
|
2307
|
+
const element = await frame.locator(dataAttribute).first();
|
|
2308
|
+
if (element) {
|
|
2309
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2310
|
+
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2311
|
+
}
|
|
2312
|
+
await _screenshot(state, this);
|
|
2313
|
+
return state.info;
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2319
|
+
}
|
|
2320
|
+
catch (e) {
|
|
2321
|
+
await _commandError(state, e, this);
|
|
2206
2322
|
}
|
|
2207
|
-
|
|
2208
|
-
|
|
2323
|
+
finally {
|
|
2324
|
+
_commandFinally(state, this);
|
|
2209
2325
|
}
|
|
2210
|
-
return serviceUrl;
|
|
2211
2326
|
}
|
|
2212
2327
|
async visualVerification(text, options = {}, world = null) {
|
|
2213
2328
|
const startTime = Date.now();
|
|
@@ -2223,7 +2338,7 @@ class StableBrowser {
|
|
|
2223
2338
|
throw new Error("TOKEN is not set");
|
|
2224
2339
|
}
|
|
2225
2340
|
try {
|
|
2226
|
-
let serviceUrl =
|
|
2341
|
+
let serviceUrl = _getServerUrl();
|
|
2227
2342
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2228
2343
|
info.screenshotPath = screenshotPath;
|
|
2229
2344
|
const screenshot = await this.takeScreenshot();
|
|
@@ -2264,7 +2379,7 @@ class StableBrowser {
|
|
|
2264
2379
|
}
|
|
2265
2380
|
finally {
|
|
2266
2381
|
const endTime = Date.now();
|
|
2267
|
-
|
|
2382
|
+
_reportToWorld(world, {
|
|
2268
2383
|
type: Types.VERIFY_VISUAL,
|
|
2269
2384
|
text: "Visual verification",
|
|
2270
2385
|
screenshotId,
|
|
@@ -2312,6 +2427,7 @@ class StableBrowser {
|
|
|
2312
2427
|
let screenshotPath = null;
|
|
2313
2428
|
const info = {};
|
|
2314
2429
|
info.log = "";
|
|
2430
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
2315
2431
|
info.operation = "getTableData";
|
|
2316
2432
|
info.selectors = selectors;
|
|
2317
2433
|
try {
|
|
@@ -2332,7 +2448,7 @@ class StableBrowser {
|
|
|
2332
2448
|
}
|
|
2333
2449
|
finally {
|
|
2334
2450
|
const endTime = Date.now();
|
|
2335
|
-
|
|
2451
|
+
_reportToWorld(world, {
|
|
2336
2452
|
element_name: selectors.element_name,
|
|
2337
2453
|
type: Types.GET_TABLE_DATA,
|
|
2338
2454
|
text: "Get table data",
|
|
@@ -2387,7 +2503,7 @@ class StableBrowser {
|
|
|
2387
2503
|
info.operation = "analyzeTable";
|
|
2388
2504
|
info.selectors = selectors;
|
|
2389
2505
|
info.query = query;
|
|
2390
|
-
query =
|
|
2506
|
+
query = _fixUsingParams(query, _params);
|
|
2391
2507
|
info.query_fixed = query;
|
|
2392
2508
|
info.operator = operator;
|
|
2393
2509
|
info.value = value;
|
|
@@ -2498,7 +2614,7 @@ class StableBrowser {
|
|
|
2498
2614
|
}
|
|
2499
2615
|
finally {
|
|
2500
2616
|
const endTime = Date.now();
|
|
2501
|
-
|
|
2617
|
+
_reportToWorld(world, {
|
|
2502
2618
|
element_name: selectors.element_name,
|
|
2503
2619
|
type: Types.ANALYZE_TABLE,
|
|
2504
2620
|
text: "Analyze table",
|
|
@@ -2571,7 +2687,7 @@ class StableBrowser {
|
|
|
2571
2687
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2572
2688
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2573
2689
|
const endTime = Date.now();
|
|
2574
|
-
|
|
2690
|
+
_reportToWorld(world, {
|
|
2575
2691
|
type: Types.GET_PAGE_STATUS,
|
|
2576
2692
|
text: "Wait for page load",
|
|
2577
2693
|
screenshotId,
|
|
@@ -2615,6 +2731,11 @@ class StableBrowser {
|
|
|
2615
2731
|
_commandFinally(state, this);
|
|
2616
2732
|
}
|
|
2617
2733
|
}
|
|
2734
|
+
saveTestDataAsGlobal(options, world) {
|
|
2735
|
+
const dataFile = this._getDataFile(world);
|
|
2736
|
+
process.env.GLOBAL_TEST_DATA_FILE = dataFile;
|
|
2737
|
+
this.logger.info("Save the scenario test data as global for the following scenarios.");
|
|
2738
|
+
}
|
|
2618
2739
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
2619
2740
|
const startTime = Date.now();
|
|
2620
2741
|
let error = null;
|
|
@@ -2638,7 +2759,7 @@ class StableBrowser {
|
|
|
2638
2759
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2639
2760
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2640
2761
|
const endTime = Date.now();
|
|
2641
|
-
|
|
2762
|
+
_reportToWorld(world, {
|
|
2642
2763
|
type: Types.SET_VIEWPORT,
|
|
2643
2764
|
text: "set viewport size to " + width + "x" + hight,
|
|
2644
2765
|
screenshotId,
|
|
@@ -2675,7 +2796,7 @@ class StableBrowser {
|
|
|
2675
2796
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2676
2797
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2677
2798
|
const endTime = Date.now();
|
|
2678
|
-
|
|
2799
|
+
_reportToWorld(world, {
|
|
2679
2800
|
type: Types.GET_PAGE_STATUS,
|
|
2680
2801
|
text: "page relaod",
|
|
2681
2802
|
screenshotId,
|
|
@@ -2711,11 +2832,45 @@ class StableBrowser {
|
|
|
2711
2832
|
console.log("#-#");
|
|
2712
2833
|
}
|
|
2713
2834
|
}
|
|
2714
|
-
|
|
2715
|
-
if (
|
|
2716
|
-
|
|
2835
|
+
async beforeStep(world, step) {
|
|
2836
|
+
if (this.stepIndex === undefined) {
|
|
2837
|
+
this.stepIndex = 0;
|
|
2838
|
+
}
|
|
2839
|
+
else {
|
|
2840
|
+
this.stepIndex++;
|
|
2841
|
+
}
|
|
2842
|
+
if (step && step.pickleStep && step.pickleStep.text) {
|
|
2843
|
+
this.stepName = step.pickleStep.text;
|
|
2844
|
+
this.logger.info("step: " + this.stepName);
|
|
2845
|
+
}
|
|
2846
|
+
else if (step && step.text) {
|
|
2847
|
+
this.stepName = step.text;
|
|
2848
|
+
}
|
|
2849
|
+
else {
|
|
2850
|
+
this.stepName = "step " + this.stepIndex;
|
|
2851
|
+
}
|
|
2852
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2853
|
+
if (this.context.browserObject.context) {
|
|
2854
|
+
await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
if (this.tags === null && step && step.pickle && step.pickle.tags) {
|
|
2858
|
+
this.tags = step.pickle.tags.map((tag) => tag.name);
|
|
2859
|
+
// check if @global_test_data tag is present
|
|
2860
|
+
if (this.tags.includes("@global_test_data")) {
|
|
2861
|
+
this.saveTestDataAsGlobal({}, world);
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
async afterStep(world, step) {
|
|
2866
|
+
this.stepName = null;
|
|
2867
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2868
|
+
if (this.context.browserObject.context) {
|
|
2869
|
+
await this.context.browserObject.context.tracing.stopChunk({
|
|
2870
|
+
path: path.join(this.context.browserObject.traceFolder, `trace-${this.stepIndex}.zip`),
|
|
2871
|
+
});
|
|
2872
|
+
}
|
|
2717
2873
|
}
|
|
2718
|
-
world.attach(JSON.stringify(properties), { mediaType: "application/json" });
|
|
2719
2874
|
}
|
|
2720
2875
|
}
|
|
2721
2876
|
function createTimedPromise(promise, label) {
|
|
@@ -2723,156 +2878,5 @@ function createTimedPromise(promise, label) {
|
|
|
2723
2878
|
.then((result) => ({ status: "fulfilled", label, result }))
|
|
2724
2879
|
.catch((error) => Promise.reject({ status: "rejected", label, error }));
|
|
2725
2880
|
}
|
|
2726
|
-
const KEYBOARD_EVENTS = [
|
|
2727
|
-
"ALT",
|
|
2728
|
-
"AltGraph",
|
|
2729
|
-
"CapsLock",
|
|
2730
|
-
"Control",
|
|
2731
|
-
"Fn",
|
|
2732
|
-
"FnLock",
|
|
2733
|
-
"Hyper",
|
|
2734
|
-
"Meta",
|
|
2735
|
-
"NumLock",
|
|
2736
|
-
"ScrollLock",
|
|
2737
|
-
"Shift",
|
|
2738
|
-
"Super",
|
|
2739
|
-
"Symbol",
|
|
2740
|
-
"SymbolLock",
|
|
2741
|
-
"Enter",
|
|
2742
|
-
"Tab",
|
|
2743
|
-
"ArrowDown",
|
|
2744
|
-
"ArrowLeft",
|
|
2745
|
-
"ArrowRight",
|
|
2746
|
-
"ArrowUp",
|
|
2747
|
-
"End",
|
|
2748
|
-
"Home",
|
|
2749
|
-
"PageDown",
|
|
2750
|
-
"PageUp",
|
|
2751
|
-
"Backspace",
|
|
2752
|
-
"Clear",
|
|
2753
|
-
"Copy",
|
|
2754
|
-
"CrSel",
|
|
2755
|
-
"Cut",
|
|
2756
|
-
"Delete",
|
|
2757
|
-
"EraseEof",
|
|
2758
|
-
"ExSel",
|
|
2759
|
-
"Insert",
|
|
2760
|
-
"Paste",
|
|
2761
|
-
"Redo",
|
|
2762
|
-
"Undo",
|
|
2763
|
-
"Accept",
|
|
2764
|
-
"Again",
|
|
2765
|
-
"Attn",
|
|
2766
|
-
"Cancel",
|
|
2767
|
-
"ContextMenu",
|
|
2768
|
-
"Escape",
|
|
2769
|
-
"Execute",
|
|
2770
|
-
"Find",
|
|
2771
|
-
"Finish",
|
|
2772
|
-
"Help",
|
|
2773
|
-
"Pause",
|
|
2774
|
-
"Play",
|
|
2775
|
-
"Props",
|
|
2776
|
-
"Select",
|
|
2777
|
-
"ZoomIn",
|
|
2778
|
-
"ZoomOut",
|
|
2779
|
-
"BrightnessDown",
|
|
2780
|
-
"BrightnessUp",
|
|
2781
|
-
"Eject",
|
|
2782
|
-
"LogOff",
|
|
2783
|
-
"Power",
|
|
2784
|
-
"PowerOff",
|
|
2785
|
-
"PrintScreen",
|
|
2786
|
-
"Hibernate",
|
|
2787
|
-
"Standby",
|
|
2788
|
-
"WakeUp",
|
|
2789
|
-
"AllCandidates",
|
|
2790
|
-
"Alphanumeric",
|
|
2791
|
-
"CodeInput",
|
|
2792
|
-
"Compose",
|
|
2793
|
-
"Convert",
|
|
2794
|
-
"Dead",
|
|
2795
|
-
"FinalMode",
|
|
2796
|
-
"GroupFirst",
|
|
2797
|
-
"GroupLast",
|
|
2798
|
-
"GroupNext",
|
|
2799
|
-
"GroupPrevious",
|
|
2800
|
-
"ModeChange",
|
|
2801
|
-
"NextCandidate",
|
|
2802
|
-
"NonConvert",
|
|
2803
|
-
"PreviousCandidate",
|
|
2804
|
-
"Process",
|
|
2805
|
-
"SingleCandidate",
|
|
2806
|
-
"HangulMode",
|
|
2807
|
-
"HanjaMode",
|
|
2808
|
-
"JunjaMode",
|
|
2809
|
-
"Eisu",
|
|
2810
|
-
"Hankaku",
|
|
2811
|
-
"Hiragana",
|
|
2812
|
-
"HiraganaKatakana",
|
|
2813
|
-
"KanaMode",
|
|
2814
|
-
"KanjiMode",
|
|
2815
|
-
"Katakana",
|
|
2816
|
-
"Romaji",
|
|
2817
|
-
"Zenkaku",
|
|
2818
|
-
"ZenkakuHanaku",
|
|
2819
|
-
"F1",
|
|
2820
|
-
"F2",
|
|
2821
|
-
"F3",
|
|
2822
|
-
"F4",
|
|
2823
|
-
"F5",
|
|
2824
|
-
"F6",
|
|
2825
|
-
"F7",
|
|
2826
|
-
"F8",
|
|
2827
|
-
"F9",
|
|
2828
|
-
"F10",
|
|
2829
|
-
"F11",
|
|
2830
|
-
"F12",
|
|
2831
|
-
"Soft1",
|
|
2832
|
-
"Soft2",
|
|
2833
|
-
"Soft3",
|
|
2834
|
-
"Soft4",
|
|
2835
|
-
"ChannelDown",
|
|
2836
|
-
"ChannelUp",
|
|
2837
|
-
"Close",
|
|
2838
|
-
"MailForward",
|
|
2839
|
-
"MailReply",
|
|
2840
|
-
"MailSend",
|
|
2841
|
-
"MediaFastForward",
|
|
2842
|
-
"MediaPause",
|
|
2843
|
-
"MediaPlay",
|
|
2844
|
-
"MediaPlayPause",
|
|
2845
|
-
"MediaRecord",
|
|
2846
|
-
"MediaRewind",
|
|
2847
|
-
"MediaStop",
|
|
2848
|
-
"MediaTrackNext",
|
|
2849
|
-
"MediaTrackPrevious",
|
|
2850
|
-
"AudioBalanceLeft",
|
|
2851
|
-
"AudioBalanceRight",
|
|
2852
|
-
"AudioBassBoostDown",
|
|
2853
|
-
"AudioBassBoostToggle",
|
|
2854
|
-
"AudioBassBoostUp",
|
|
2855
|
-
"AudioFaderFront",
|
|
2856
|
-
"AudioFaderRear",
|
|
2857
|
-
"AudioSurroundModeNext",
|
|
2858
|
-
"AudioTrebleDown",
|
|
2859
|
-
"AudioTrebleUp",
|
|
2860
|
-
"AudioVolumeDown",
|
|
2861
|
-
"AudioVolumeMute",
|
|
2862
|
-
"AudioVolumeUp",
|
|
2863
|
-
"MicrophoneToggle",
|
|
2864
|
-
"MicrophoneVolumeDown",
|
|
2865
|
-
"MicrophoneVolumeMute",
|
|
2866
|
-
"MicrophoneVolumeUp",
|
|
2867
|
-
"TV",
|
|
2868
|
-
"TV3DMode",
|
|
2869
|
-
"TVAntennaCable",
|
|
2870
|
-
"TVAudioDescription",
|
|
2871
|
-
];
|
|
2872
|
-
function unEscapeString(str) {
|
|
2873
|
-
const placeholder = "__NEWLINE__";
|
|
2874
|
-
str = str.replace(new RegExp(placeholder, "g"), "\n");
|
|
2875
|
-
return str;
|
|
2876
|
-
}
|
|
2877
2881
|
export { StableBrowser };
|
|
2878
2882
|
//# sourceMappingURL=stable_browser.js.map
|