automation_model 1.0.507-dev → 1.0.507-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 +55 -17
- package/lib/stable_browser.js +520 -451
- 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",
|
|
@@ -44,6 +48,9 @@ const Types = {
|
|
|
44
48
|
VERIFY_VISUAL: "verify_visual",
|
|
45
49
|
LOAD_DATA: "load_data",
|
|
46
50
|
SET_INPUT: "set_input",
|
|
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",
|
|
47
54
|
};
|
|
48
55
|
export const apps = {};
|
|
49
56
|
class StableBrowser {
|
|
@@ -57,6 +64,7 @@ class StableBrowser {
|
|
|
57
64
|
networkLogger = null;
|
|
58
65
|
configuration = null;
|
|
59
66
|
appName = "main";
|
|
67
|
+
tags = null;
|
|
60
68
|
constructor(browser, page, logger = null, context = null, world = null) {
|
|
61
69
|
this.browser = browser;
|
|
62
70
|
this.page = page;
|
|
@@ -87,17 +95,17 @@ class StableBrowser {
|
|
|
87
95
|
catch (e) {
|
|
88
96
|
this.logger.error("unable to read ai_config.json");
|
|
89
97
|
}
|
|
90
|
-
context.pageLoading = { status: false };
|
|
91
|
-
context.pages = [this.page];
|
|
92
98
|
const logFolder = path.join(this.project_path, "logs", "web");
|
|
93
99
|
this.world = world;
|
|
100
|
+
context.pages = [this.page];
|
|
101
|
+
context.pageLoading = { status: false };
|
|
94
102
|
this.registerEventListeners(this.context);
|
|
95
103
|
registerNetworkEvents(this.world, this, this.context, this.page);
|
|
96
104
|
registerDownloadEvent(this.page, this.world, this.context);
|
|
97
105
|
}
|
|
98
106
|
registerEventListeners(context) {
|
|
99
107
|
this.registerConsoleLogListener(this.page, context);
|
|
100
|
-
this.registerRequestListener(this.page, context, this.webLogFile);
|
|
108
|
+
// this.registerRequestListener(this.page, context, this.webLogFile);
|
|
101
109
|
if (!context.pageLoading) {
|
|
102
110
|
context.pageLoading = { status: false };
|
|
103
111
|
}
|
|
@@ -142,9 +150,9 @@ class StableBrowser {
|
|
|
142
150
|
if (this.appName === appName) {
|
|
143
151
|
return;
|
|
144
152
|
}
|
|
145
|
-
let
|
|
153
|
+
let navigate = false;
|
|
146
154
|
if (!apps[appName]) {
|
|
147
|
-
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);
|
|
148
156
|
newContextCreated = true;
|
|
149
157
|
apps[appName] = {
|
|
150
158
|
context: newContext,
|
|
@@ -153,32 +161,15 @@ class StableBrowser {
|
|
|
153
161
|
};
|
|
154
162
|
}
|
|
155
163
|
const tempContext = {};
|
|
156
|
-
|
|
157
|
-
|
|
164
|
+
_copyContext(this, tempContext);
|
|
165
|
+
_copyContext(apps[appName], this);
|
|
158
166
|
apps[this.appName] = tempContext;
|
|
159
167
|
this.appName = appName;
|
|
160
|
-
if (
|
|
161
|
-
this.registerEventListeners(this.context);
|
|
168
|
+
if (navigate) {
|
|
162
169
|
await this.goto(this.context.environment.baseUrl);
|
|
163
170
|
await this.waitForPageLoad();
|
|
164
171
|
}
|
|
165
172
|
}
|
|
166
|
-
_copyContext(from, to) {
|
|
167
|
-
to.browser = from.browser;
|
|
168
|
-
to.page = from.page;
|
|
169
|
-
to.context = from.context;
|
|
170
|
-
}
|
|
171
|
-
getWebLogFile(logFolder) {
|
|
172
|
-
if (!fs.existsSync(logFolder)) {
|
|
173
|
-
fs.mkdirSync(logFolder, { recursive: true });
|
|
174
|
-
}
|
|
175
|
-
let nextIndex = 1;
|
|
176
|
-
while (fs.existsSync(path.join(logFolder, nextIndex.toString() + ".json"))) {
|
|
177
|
-
nextIndex++;
|
|
178
|
-
}
|
|
179
|
-
const fileName = nextIndex + ".json";
|
|
180
|
-
return path.join(logFolder, fileName);
|
|
181
|
-
}
|
|
182
173
|
registerConsoleLogListener(page, context) {
|
|
183
174
|
if (!this.context.webLogger) {
|
|
184
175
|
this.context.webLogger = [];
|
|
@@ -228,7 +219,7 @@ class StableBrowser {
|
|
|
228
219
|
this.world?.attach(JSON.stringify(obj), { mediaType: "application/json+network" });
|
|
229
220
|
}
|
|
230
221
|
catch (error) {
|
|
231
|
-
console.error("Error in request listener", error);
|
|
222
|
+
// console.error("Error in request listener", error);
|
|
232
223
|
context.networkLogger.push({
|
|
233
224
|
error: "not able to listen",
|
|
234
225
|
message: error.message,
|
|
@@ -242,55 +233,48 @@ class StableBrowser {
|
|
|
242
233
|
// async closeUnexpectedPopups() {
|
|
243
234
|
// await closeUnexpectedPopups(this.page);
|
|
244
235
|
// }
|
|
245
|
-
async goto(url) {
|
|
236
|
+
async goto(url, world = null) {
|
|
246
237
|
if (!url.startsWith("http")) {
|
|
247
238
|
url = "https://" + url;
|
|
248
239
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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);
|
|
256
259
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
// remove the _ prefix
|
|
261
|
-
regValue = key.substring(1);
|
|
262
|
-
}
|
|
263
|
-
text = text.replaceAll(new RegExp("{" + regValue + "}", "g"), _params[key]);
|
|
260
|
+
catch (error) {
|
|
261
|
+
console.error("Error on goto", error);
|
|
262
|
+
_commandError(state, error, this);
|
|
264
263
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
_fixLocatorUsingParams(locator, _params) {
|
|
268
|
-
// check if not null
|
|
269
|
-
if (!locator) {
|
|
270
|
-
return locator;
|
|
264
|
+
finally {
|
|
265
|
+
_commandFinally(state, this);
|
|
271
266
|
}
|
|
272
|
-
// clone the locator
|
|
273
|
-
locator = JSON.parse(JSON.stringify(locator));
|
|
274
|
-
this.scanAndManipulate(locator, _params);
|
|
275
|
-
return locator;
|
|
276
267
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
else if (this._isObject(currentObj[key])) {
|
|
287
|
-
// Recursively scan nested objects
|
|
288
|
-
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);
|
|
289
276
|
}
|
|
290
277
|
}
|
|
291
|
-
}
|
|
292
|
-
_getLocator(locator, scope, _params) {
|
|
293
|
-
locator = this._fixLocatorUsingParams(locator, _params);
|
|
294
278
|
let locatorReturn;
|
|
295
279
|
if (locator.role) {
|
|
296
280
|
if (locator.role[1].nameReg) {
|
|
@@ -298,7 +282,7 @@ class StableBrowser {
|
|
|
298
282
|
delete locator.role[1].nameReg;
|
|
299
283
|
}
|
|
300
284
|
// if (locator.role[1].name) {
|
|
301
|
-
// locator.role[1].name =
|
|
285
|
+
// locator.role[1].name = _fixUsingParams(locator.role[1].name, _params);
|
|
302
286
|
// }
|
|
303
287
|
locatorReturn = scope.getByRole(locator.role[0], locator.role[1]);
|
|
304
288
|
}
|
|
@@ -341,143 +325,69 @@ class StableBrowser {
|
|
|
341
325
|
if (css && css.locator) {
|
|
342
326
|
css = css.locator;
|
|
343
327
|
}
|
|
344
|
-
let result = await this._locateElementByText(scope,
|
|
328
|
+
let result = await this._locateElementByText(scope, _fixUsingParams(text, _params), "*:not(script, style, head)", false, false, true, _params);
|
|
345
329
|
if (result.elementCount === 0) {
|
|
346
330
|
return;
|
|
347
331
|
}
|
|
348
|
-
let textElementCss = "[data-blinq-id
|
|
332
|
+
let textElementCss = "[data-blinq-id-" + result.randomToken + "]";
|
|
349
333
|
// css climb to parent element
|
|
350
334
|
const climbArray = [];
|
|
351
335
|
for (let i = 0; i < climb; i++) {
|
|
352
336
|
climbArray.push("..");
|
|
353
337
|
}
|
|
354
338
|
let climbXpath = "xpath=" + climbArray.join("/");
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
return
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
return new RegExp(pattern, flags);
|
|
378
|
-
}
|
|
379
|
-
document.getRegex = getRegex;
|
|
380
|
-
function collectAllShadowDomElements(element, result = []) {
|
|
381
|
-
// Check and add the element if it has a shadow root
|
|
382
|
-
if (element.shadowRoot) {
|
|
383
|
-
result.push(element);
|
|
384
|
-
// Also search within the shadow root
|
|
385
|
-
document.collectAllShadowDomElements(element.shadowRoot, result);
|
|
386
|
-
}
|
|
387
|
-
// Iterate over child nodes
|
|
388
|
-
element.childNodes.forEach((child) => {
|
|
389
|
-
// Recursively call the function for each child node
|
|
390
|
-
document.collectAllShadowDomElements(child, result);
|
|
391
|
-
});
|
|
392
|
-
return result;
|
|
393
|
-
}
|
|
394
|
-
document.collectAllShadowDomElements = collectAllShadowDomElements;
|
|
395
|
-
if (!tag) {
|
|
396
|
-
tag = "*:not(script, style, head)";
|
|
397
|
-
}
|
|
398
|
-
let regexpSearch = document.getRegex(text);
|
|
399
|
-
if (regexpSearch) {
|
|
400
|
-
regex = true;
|
|
401
|
-
}
|
|
402
|
-
let elements = Array.from(document.querySelectorAll(tag));
|
|
403
|
-
let shadowHosts = [];
|
|
404
|
-
document.collectAllShadowDomElements(document, shadowHosts);
|
|
405
|
-
for (let i = 0; i < shadowHosts.length; i++) {
|
|
406
|
-
let shadowElement = shadowHosts[i].shadowRoot;
|
|
407
|
-
if (!shadowElement) {
|
|
408
|
-
console.log("shadowElement is null, for host " + shadowHosts[i]);
|
|
409
|
-
continue;
|
|
410
|
-
}
|
|
411
|
-
let shadowElements = Array.from(shadowElement.querySelectorAll(tag));
|
|
412
|
-
elements = elements.concat(shadowElements);
|
|
413
|
-
}
|
|
414
|
-
let randomToken = null;
|
|
415
|
-
const foundElements = [];
|
|
416
|
-
if (regex) {
|
|
417
|
-
if (!regexpSearch) {
|
|
418
|
-
regexpSearch = new RegExp(text, "im");
|
|
419
|
-
}
|
|
420
|
-
for (let i = 0; i < elements.length; i++) {
|
|
421
|
-
const element = elements[i];
|
|
422
|
-
if ((element.innerText && regexpSearch.test(element.innerText)) ||
|
|
423
|
-
(element.value && regexpSearch.test(element.value))) {
|
|
424
|
-
foundElements.push(element);
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
else {
|
|
429
|
-
text = text.trim();
|
|
430
|
-
for (let i = 0; i < elements.length; i++) {
|
|
431
|
-
const element = elements[i];
|
|
432
|
-
if (partial) {
|
|
433
|
-
if ((element.innerText && element.innerText.toLowerCase().trim().includes(text.toLowerCase())) ||
|
|
434
|
-
(element.value && element.value.toLowerCase().includes(text.toLowerCase()))) {
|
|
435
|
-
foundElements.push(element);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
else {
|
|
439
|
-
if ((element.innerText && element.innerText.trim() === text) ||
|
|
440
|
-
(element.value && element.value === text)) {
|
|
441
|
-
foundElements.push(element);
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
let noChildElements = [];
|
|
447
|
-
for (let i = 0; i < foundElements.length; i++) {
|
|
448
|
-
let element = foundElements[i];
|
|
449
|
-
let hasChild = false;
|
|
450
|
-
for (let j = 0; j < foundElements.length; j++) {
|
|
451
|
-
if (i === j) {
|
|
452
|
-
continue;
|
|
453
|
-
}
|
|
454
|
-
if (isParent(element, foundElements[j])) {
|
|
455
|
-
hasChild = true;
|
|
456
|
-
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;
|
|
457
361
|
}
|
|
458
362
|
}
|
|
459
|
-
if (!
|
|
460
|
-
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
let elementCount = 0;
|
|
464
|
-
if (noChildElements.length > 0) {
|
|
465
|
-
for (let i = 0; i < noChildElements.length; i++) {
|
|
466
|
-
if (randomToken === null) {
|
|
467
|
-
randomToken = Math.random().toString(36).substring(7);
|
|
468
|
-
}
|
|
469
|
-
let element = noChildElements[i];
|
|
470
|
-
element.setAttribute("data-blinq-id", "blinq-id-" + randomToken);
|
|
471
|
-
elementCount++;
|
|
363
|
+
if (!el.setAttribute) {
|
|
364
|
+
el = el.parentElement;
|
|
472
365
|
}
|
|
366
|
+
el.setAttribute("data-blinq-id-" + randomToken, "");
|
|
367
|
+
return true;
|
|
368
|
+
}, [tag1, randomToken]))) {
|
|
369
|
+
continue;
|
|
473
370
|
}
|
|
474
|
-
|
|
475
|
-
}
|
|
371
|
+
tagCount++;
|
|
372
|
+
}
|
|
373
|
+
return { elementCount: tagCount, randomToken };
|
|
476
374
|
}
|
|
477
|
-
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
|
+
}
|
|
478
386
|
let locatorSearch = selectorHierarchy[index];
|
|
387
|
+
let originalLocatorSearch = "";
|
|
479
388
|
try {
|
|
480
|
-
|
|
389
|
+
originalLocatorSearch = _fixUsingParams(JSON.stringify(locatorSearch), _params);
|
|
390
|
+
locatorSearch = JSON.parse(originalLocatorSearch);
|
|
481
391
|
}
|
|
482
392
|
catch (e) {
|
|
483
393
|
console.error(e);
|
|
@@ -491,24 +401,24 @@ class StableBrowser {
|
|
|
491
401
|
info.failCause.lastError = "failed to locate element by text: " + locatorSearch.text;
|
|
492
402
|
return;
|
|
493
403
|
}
|
|
494
|
-
locator = this._getLocator({ css: locatorString }, scope, _params);
|
|
404
|
+
locator = await this._getLocator({ css: locatorString }, scope, _params);
|
|
495
405
|
}
|
|
496
406
|
else if (locatorSearch.text) {
|
|
497
|
-
let text =
|
|
498
|
-
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);
|
|
499
409
|
if (result.elementCount === 0) {
|
|
500
410
|
info.failCause.textNotFound = true;
|
|
501
411
|
info.failCause.lastError = "failed to locate element by text: " + text;
|
|
502
412
|
return;
|
|
503
413
|
}
|
|
504
|
-
locatorSearch.css = "[data-blinq-id
|
|
414
|
+
locatorSearch.css = "[data-blinq-id-" + result.randomToken + "]";
|
|
505
415
|
if (locatorSearch.childCss) {
|
|
506
416
|
locatorSearch.css = locatorSearch.css + " " + locatorSearch.childCss;
|
|
507
417
|
}
|
|
508
|
-
locator = this._getLocator(locatorSearch, scope, _params);
|
|
418
|
+
locator = await this._getLocator(locatorSearch, scope, _params);
|
|
509
419
|
}
|
|
510
420
|
else {
|
|
511
|
-
locator = this._getLocator(locatorSearch, scope, _params);
|
|
421
|
+
locator = await this._getLocator(locatorSearch, scope, _params);
|
|
512
422
|
}
|
|
513
423
|
// let cssHref = false;
|
|
514
424
|
// if (locatorSearch.css && locatorSearch.css.includes("href=")) {
|
|
@@ -521,18 +431,27 @@ class StableBrowser {
|
|
|
521
431
|
//info.log += "total elements found " + count + "\n";
|
|
522
432
|
//let visibleCount = 0;
|
|
523
433
|
let visibleLocator = null;
|
|
524
|
-
if (locatorSearch.index && locatorSearch.index < count) {
|
|
434
|
+
if (typeof locatorSearch.index === "number" && locatorSearch.index < count) {
|
|
525
435
|
foundLocators.push(locator.nth(locatorSearch.index));
|
|
436
|
+
if (info.locatorLog) {
|
|
437
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND");
|
|
438
|
+
}
|
|
526
439
|
return;
|
|
527
440
|
}
|
|
441
|
+
if (info.locatorLog && count === 0) {
|
|
442
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "NOT_FOUND");
|
|
443
|
+
}
|
|
528
444
|
for (let j = 0; j < count; j++) {
|
|
529
445
|
let visible = await locator.nth(j).isVisible();
|
|
530
446
|
const enabled = await locator.nth(j).isEnabled();
|
|
531
447
|
if (!visibleOnly) {
|
|
532
448
|
visible = true;
|
|
533
449
|
}
|
|
534
|
-
if (visible && enabled) {
|
|
450
|
+
if (visible && (allowDisabled || enabled)) {
|
|
535
451
|
foundLocators.push(locator.nth(j));
|
|
452
|
+
if (info.locatorLog) {
|
|
453
|
+
info.locatorLog.setLocatorSearchStatus(originalLocatorSearch, "FOUND");
|
|
454
|
+
}
|
|
536
455
|
}
|
|
537
456
|
else {
|
|
538
457
|
info.failCause.visible = visible;
|
|
@@ -540,8 +459,14 @@ class StableBrowser {
|
|
|
540
459
|
if (!info.printMessages) {
|
|
541
460
|
info.printMessages = {};
|
|
542
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
|
+
}
|
|
543
468
|
if (!info.printMessages[j.toString()]) {
|
|
544
|
-
info.log += "element " + locator + " visible " + visible + " enabled " + enabled + "\n";
|
|
469
|
+
//info.log += "element " + locator + " visible " + visible + " enabled " + enabled + "\n";
|
|
545
470
|
info.printMessages[j.toString()] = true;
|
|
546
471
|
}
|
|
547
472
|
}
|
|
@@ -557,7 +482,7 @@ class StableBrowser {
|
|
|
557
482
|
if (!info) {
|
|
558
483
|
info = {};
|
|
559
484
|
}
|
|
560
|
-
info.log += "scan for popup handlers" + "\n";
|
|
485
|
+
//info.log += "scan for popup handlers" + "\n";
|
|
561
486
|
const handlerGroup = [];
|
|
562
487
|
for (let i = 0; i < this.configuration.popupHandlers.length; i++) {
|
|
563
488
|
handlerGroup.push(this.configuration.popupHandlers[i].locator);
|
|
@@ -584,16 +509,28 @@ class StableBrowser {
|
|
|
584
509
|
}
|
|
585
510
|
if (result.foundElements.length > 0) {
|
|
586
511
|
let dialogCloseLocator = result.foundElements[0].locator;
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
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
|
+
}
|
|
590
527
|
return { rerun: true };
|
|
591
528
|
}
|
|
592
529
|
}
|
|
593
530
|
}
|
|
594
531
|
return { rerun: false };
|
|
595
532
|
}
|
|
596
|
-
async _locate(selectors, info, _params, timeout) {
|
|
533
|
+
async _locate(selectors, info, _params, timeout, allowDisabled = false) {
|
|
597
534
|
if (!timeout) {
|
|
598
535
|
timeout = 30000;
|
|
599
536
|
}
|
|
@@ -603,7 +540,7 @@ class StableBrowser {
|
|
|
603
540
|
let selector = selectors.locators[j];
|
|
604
541
|
info.log += "searching for locator " + j + ":" + JSON.stringify(selector) + "\n";
|
|
605
542
|
}
|
|
606
|
-
let element = await this._locate_internal(selectors, info, _params, timeout);
|
|
543
|
+
let element = await this._locate_internal(selectors, info, _params, timeout, allowDisabled);
|
|
607
544
|
if (!element.rerun) {
|
|
608
545
|
return element;
|
|
609
546
|
}
|
|
@@ -616,6 +553,7 @@ class StableBrowser {
|
|
|
616
553
|
info.failCause = {};
|
|
617
554
|
info.log = "";
|
|
618
555
|
}
|
|
556
|
+
let startTime = Date.now();
|
|
619
557
|
let scope = this.page;
|
|
620
558
|
if (selectors.frame) {
|
|
621
559
|
return selectors.frame;
|
|
@@ -646,9 +584,11 @@ class StableBrowser {
|
|
|
646
584
|
}
|
|
647
585
|
return framescope;
|
|
648
586
|
};
|
|
587
|
+
let fLocator = null;
|
|
649
588
|
while (true) {
|
|
650
589
|
let frameFound = false;
|
|
651
590
|
if (selectors.nestFrmLoc) {
|
|
591
|
+
fLocator = selectors.nestFrmLoc;
|
|
652
592
|
scope = await findFrame(selectors.nestFrmLoc, scope);
|
|
653
593
|
frameFound = true;
|
|
654
594
|
break;
|
|
@@ -657,6 +597,7 @@ class StableBrowser {
|
|
|
657
597
|
for (let i = 0; i < selectors.frameLocators.length; i++) {
|
|
658
598
|
let frameLocator = selectors.frameLocators[i];
|
|
659
599
|
if (frameLocator.css) {
|
|
600
|
+
fLocator = frameLocator.css;
|
|
660
601
|
scope = scope.frameLocator(frameLocator.css);
|
|
661
602
|
frameFound = true;
|
|
662
603
|
break;
|
|
@@ -664,11 +605,15 @@ class StableBrowser {
|
|
|
664
605
|
}
|
|
665
606
|
}
|
|
666
607
|
if (!frameFound && selectors.iframe_src) {
|
|
608
|
+
fLocator = selectors.iframe_src;
|
|
667
609
|
scope = this.page.frame({ url: selectors.iframe_src });
|
|
668
610
|
}
|
|
669
611
|
if (!scope) {
|
|
670
|
-
info
|
|
671
|
-
|
|
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) {
|
|
672
617
|
info.failCause.iframeNotFound = true;
|
|
673
618
|
info.failCause.lastError = "unable to locate iframe " + selectors.iframe_src;
|
|
674
619
|
throw new Error("unable to locate iframe " + selectors.iframe_src);
|
|
@@ -676,6 +621,9 @@ class StableBrowser {
|
|
|
676
621
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
677
622
|
}
|
|
678
623
|
else {
|
|
624
|
+
if (info && info.locatorLog) {
|
|
625
|
+
info.locatorLog.setLocatorSearchStatus("frame-" + fLocator, "FOUND");
|
|
626
|
+
}
|
|
679
627
|
break;
|
|
680
628
|
}
|
|
681
629
|
}
|
|
@@ -692,16 +640,18 @@ class StableBrowser {
|
|
|
692
640
|
return bodyContent;
|
|
693
641
|
});
|
|
694
642
|
}
|
|
695
|
-
async _locate_internal(selectors, info, _params, timeout = 30000) {
|
|
643
|
+
async _locate_internal(selectors, info, _params, timeout = 30000, allowDisabled = false) {
|
|
696
644
|
if (!info) {
|
|
697
645
|
info = {};
|
|
698
646
|
info.failCause = {};
|
|
699
647
|
info.log = "";
|
|
648
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
700
649
|
}
|
|
701
650
|
let highPriorityTimeout = 5000;
|
|
702
651
|
let visibleOnlyTimeout = 6000;
|
|
703
|
-
let startTime =
|
|
652
|
+
let startTime = Date.now();
|
|
704
653
|
let locatorsCount = 0;
|
|
654
|
+
let lazy_scroll = false;
|
|
705
655
|
//let arrayMode = Array.isArray(selectors);
|
|
706
656
|
let scope = await this._findFrameScope(selectors, timeout, info);
|
|
707
657
|
let selectorsLocators = null;
|
|
@@ -739,17 +689,17 @@ class StableBrowser {
|
|
|
739
689
|
}
|
|
740
690
|
// info.log += "scanning locators in priority 1" + "\n";
|
|
741
691
|
let onlyPriority3 = selectorsLocators[0].priority === 3;
|
|
742
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["1"], scope, _params, info, visibleOnly);
|
|
692
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["1"], scope, _params, info, visibleOnly, allowDisabled);
|
|
743
693
|
if (result.foundElements.length === 0) {
|
|
744
694
|
// info.log += "scanning locators in priority 2" + "\n";
|
|
745
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["2"], scope, _params, info, visibleOnly);
|
|
695
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["2"], scope, _params, info, visibleOnly, allowDisabled);
|
|
746
696
|
}
|
|
747
697
|
if (result.foundElements.length === 0 && onlyPriority3) {
|
|
748
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly);
|
|
698
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly, allowDisabled);
|
|
749
699
|
}
|
|
750
700
|
else {
|
|
751
701
|
if (result.foundElements.length === 0 && !highPriorityOnly) {
|
|
752
|
-
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly);
|
|
702
|
+
result = await this._scanLocatorsGroup(locatorsByPriority["3"], scope, _params, info, visibleOnly, allowDisabled);
|
|
753
703
|
}
|
|
754
704
|
}
|
|
755
705
|
let foundElements = result.foundElements;
|
|
@@ -790,26 +740,36 @@ class StableBrowser {
|
|
|
790
740
|
return maxCountElement.locator;
|
|
791
741
|
}
|
|
792
742
|
}
|
|
793
|
-
if (
|
|
743
|
+
if (Date.now() - startTime > timeout) {
|
|
794
744
|
break;
|
|
795
745
|
}
|
|
796
|
-
if (
|
|
797
|
-
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";
|
|
798
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
|
+
}
|
|
799
753
|
}
|
|
800
|
-
if (
|
|
801
|
-
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";
|
|
802
756
|
visibleOnly = false;
|
|
803
757
|
}
|
|
804
758
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
805
759
|
}
|
|
806
760
|
this.logger.debug("unable to locate unique element, total elements found " + locatorsCount);
|
|
807
|
-
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";
|
|
808
768
|
info.failCause.locatorNotFound = true;
|
|
809
769
|
info.failCause.lastError = "failed to locate unique element";
|
|
810
770
|
throw new Error("failed to locate first element no elements found, " + info.log);
|
|
811
771
|
}
|
|
812
|
-
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly) {
|
|
772
|
+
async _scanLocatorsGroup(locatorsGroup, scope, _params, info, visibleOnly, allowDisabled = false) {
|
|
813
773
|
let foundElements = [];
|
|
814
774
|
const result = {
|
|
815
775
|
foundElements: foundElements,
|
|
@@ -817,14 +777,15 @@ class StableBrowser {
|
|
|
817
777
|
for (let i = 0; i < locatorsGroup.length; i++) {
|
|
818
778
|
let foundLocators = [];
|
|
819
779
|
try {
|
|
820
|
-
await this._collectLocatorInformation(locatorsGroup, i, scope, foundLocators, _params, info, visibleOnly);
|
|
780
|
+
await this._collectLocatorInformation(locatorsGroup, i, scope, foundLocators, _params, info, visibleOnly, allowDisabled);
|
|
821
781
|
}
|
|
822
782
|
catch (e) {
|
|
823
|
-
this
|
|
824
|
-
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);
|
|
825
786
|
foundLocators = [];
|
|
826
787
|
try {
|
|
827
|
-
await this._collectLocatorInformation(locatorsGroup, i, this.page, foundLocators, _params, info, visibleOnly);
|
|
788
|
+
await this._collectLocatorInformation(locatorsGroup, i, this.page, foundLocators, _params, info, visibleOnly, allowDisabled);
|
|
828
789
|
}
|
|
829
790
|
catch (e) {
|
|
830
791
|
this.logger.info("unable to use locator (second try) " + JSON.stringify(locatorsGroup[i]));
|
|
@@ -840,11 +801,27 @@ class StableBrowser {
|
|
|
840
801
|
}
|
|
841
802
|
if (foundLocators.length > 1) {
|
|
842
803
|
info.failCause.foundMultiple = true;
|
|
804
|
+
if (info.locatorLog) {
|
|
805
|
+
info.locatorLog.setLocatorSearchStatus(JSON.stringify(locatorsGroup[i]), "FOUND_NOT_UNIQUE");
|
|
806
|
+
}
|
|
843
807
|
}
|
|
844
808
|
}
|
|
845
809
|
return result;
|
|
846
810
|
}
|
|
847
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);
|
|
848
825
|
const startTime = Date.now();
|
|
849
826
|
let timeout = 30000;
|
|
850
827
|
if (options && options.timeout) {
|
|
@@ -869,13 +846,31 @@ class StableBrowser {
|
|
|
869
846
|
catch (e) {
|
|
870
847
|
if (performance.now() - startTime > timeout) {
|
|
871
848
|
// throw e;
|
|
872
|
-
|
|
849
|
+
try {
|
|
850
|
+
await _commandError(state, "timeout looking for " + elementDescription, this);
|
|
851
|
+
}
|
|
852
|
+
finally {
|
|
853
|
+
_commandFinally(state, this);
|
|
854
|
+
}
|
|
873
855
|
}
|
|
874
856
|
}
|
|
875
857
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
876
858
|
}
|
|
877
859
|
}
|
|
878
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);
|
|
879
874
|
const startTime = Date.now();
|
|
880
875
|
let timeout = 30000;
|
|
881
876
|
if (options && options.timeout) {
|
|
@@ -900,7 +895,12 @@ class StableBrowser {
|
|
|
900
895
|
catch (e) {
|
|
901
896
|
if (performance.now() - startTime > timeout) {
|
|
902
897
|
// throw e;
|
|
903
|
-
|
|
898
|
+
try {
|
|
899
|
+
await _commandError(state, "timeout looking for " + elementDescription, this);
|
|
900
|
+
}
|
|
901
|
+
finally {
|
|
902
|
+
_commandFinally(state, this);
|
|
903
|
+
}
|
|
904
904
|
}
|
|
905
905
|
}
|
|
906
906
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
@@ -919,18 +919,18 @@ class StableBrowser {
|
|
|
919
919
|
};
|
|
920
920
|
try {
|
|
921
921
|
await _preCommand(state, this);
|
|
922
|
-
if (state.options && state.options.context) {
|
|
923
|
-
|
|
924
|
-
}
|
|
922
|
+
// if (state.options && state.options.context) {
|
|
923
|
+
// state.selectors.locators[0].text = state.options.context;
|
|
924
|
+
// }
|
|
925
925
|
try {
|
|
926
926
|
await state.element.click();
|
|
927
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
927
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
928
928
|
}
|
|
929
929
|
catch (e) {
|
|
930
930
|
// await this.closeUnexpectedPopups();
|
|
931
931
|
state.element = await this._locate(selectors, state.info, _params);
|
|
932
932
|
await state.element.dispatchEvent("click");
|
|
933
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
933
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
934
934
|
}
|
|
935
935
|
await this.waitForPageLoad();
|
|
936
936
|
return state.info;
|
|
@@ -1009,7 +1009,6 @@ class StableBrowser {
|
|
|
1009
1009
|
await state.element.hover({ timeout: 10000 });
|
|
1010
1010
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1011
1011
|
}
|
|
1012
|
-
await _screenshot(state, this);
|
|
1013
1012
|
await this.waitForPageLoad();
|
|
1014
1013
|
return state.info;
|
|
1015
1014
|
}
|
|
@@ -1338,6 +1337,7 @@ class StableBrowser {
|
|
|
1338
1337
|
let screenshotPath = null;
|
|
1339
1338
|
if (!info.log) {
|
|
1340
1339
|
info.log = "";
|
|
1340
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
1341
1341
|
}
|
|
1342
1342
|
info.operation = "getText";
|
|
1343
1343
|
info.selectors = selectors;
|
|
@@ -1676,11 +1676,9 @@ class StableBrowser {
|
|
|
1676
1676
|
if (!fs.existsSync(world.screenshotPath)) {
|
|
1677
1677
|
fs.mkdirSync(world.screenshotPath, { recursive: true });
|
|
1678
1678
|
}
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
}
|
|
1683
|
-
const screenshotPath = path.join(world.screenshotPath, nextIndex + ".png");
|
|
1679
|
+
// to make sure the path doesn't start with -
|
|
1680
|
+
const uuidStr = "id_" + randomUUID();
|
|
1681
|
+
const screenshotPath = path.join(world.screenshotPath, uuidStr + ".png");
|
|
1684
1682
|
try {
|
|
1685
1683
|
await this.takeScreenshot(screenshotPath);
|
|
1686
1684
|
// let buffer = await this.page.screenshot({ timeout: 4000 });
|
|
@@ -1694,7 +1692,7 @@ class StableBrowser {
|
|
|
1694
1692
|
catch (e) {
|
|
1695
1693
|
this.logger.info("unable to take screenshot, ignored");
|
|
1696
1694
|
}
|
|
1697
|
-
result.screenshotId =
|
|
1695
|
+
result.screenshotId = uuidStr;
|
|
1698
1696
|
result.screenshotPath = screenshotPath;
|
|
1699
1697
|
if (info && info.box) {
|
|
1700
1698
|
await drawRectangle(screenshotPath, info.box.x, info.box.y, info.box.width, info.box.height);
|
|
@@ -1822,10 +1820,6 @@ class StableBrowser {
|
|
|
1822
1820
|
break;
|
|
1823
1821
|
}
|
|
1824
1822
|
state.info.value = state.value;
|
|
1825
|
-
this[variable] = state.value;
|
|
1826
|
-
if (world) {
|
|
1827
|
-
world[variable] = state.value;
|
|
1828
|
-
}
|
|
1829
1823
|
this.setTestData({ [variable]: state.value }, world);
|
|
1830
1824
|
this.logger.info("set test data: " + variable + "=" + state.value);
|
|
1831
1825
|
return state.info;
|
|
@@ -1837,6 +1831,69 @@ class StableBrowser {
|
|
|
1837
1831
|
_commandFinally(state, this);
|
|
1838
1832
|
}
|
|
1839
1833
|
}
|
|
1834
|
+
async verifyAttribute(selectors, attribute, value, _params = null, options = {}, world = null) {
|
|
1835
|
+
const state = {
|
|
1836
|
+
selectors,
|
|
1837
|
+
_params,
|
|
1838
|
+
attribute,
|
|
1839
|
+
value,
|
|
1840
|
+
options,
|
|
1841
|
+
world,
|
|
1842
|
+
type: Types.VERIFY_ATTRIBUTE,
|
|
1843
|
+
highlight: true,
|
|
1844
|
+
screenshot: true,
|
|
1845
|
+
text: `Verify element attribute`,
|
|
1846
|
+
operation: "verifyAttribute",
|
|
1847
|
+
log: "***** verify attribute " + attribute + " from " + selectors.element_name + " *****\n",
|
|
1848
|
+
allowDisabled: true
|
|
1849
|
+
};
|
|
1850
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1851
|
+
let val;
|
|
1852
|
+
try {
|
|
1853
|
+
await _preCommand(state, this);
|
|
1854
|
+
switch (attribute) {
|
|
1855
|
+
case "innerText":
|
|
1856
|
+
val = String(await state.element.innerText());
|
|
1857
|
+
break;
|
|
1858
|
+
case "value":
|
|
1859
|
+
val = String(await state.element.inputValue());
|
|
1860
|
+
break;
|
|
1861
|
+
case "checked":
|
|
1862
|
+
val = String(await state.element.isChecked());
|
|
1863
|
+
break;
|
|
1864
|
+
case "disabled":
|
|
1865
|
+
val = String(await state.element.isDisabled());
|
|
1866
|
+
break;
|
|
1867
|
+
case "readOnly":
|
|
1868
|
+
const isEditable = await state.element.isEditable();
|
|
1869
|
+
val = String(!isEditable);
|
|
1870
|
+
break;
|
|
1871
|
+
default:
|
|
1872
|
+
val = String(await state.element.getAttribute(attribute));
|
|
1873
|
+
break;
|
|
1874
|
+
}
|
|
1875
|
+
state.info.expectedValue = val;
|
|
1876
|
+
let regex;
|
|
1877
|
+
if (value.startsWith("/") && value.endsWith("/")) {
|
|
1878
|
+
const patternBody = value.slice(1, -1);
|
|
1879
|
+
regex = new RegExp(patternBody, "g");
|
|
1880
|
+
}
|
|
1881
|
+
else {
|
|
1882
|
+
const escapedPattern = value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1883
|
+
regex = new RegExp(escapedPattern, "g");
|
|
1884
|
+
}
|
|
1885
|
+
if (!val.match(regex)) {
|
|
1886
|
+
throw new Error(`The ${attribute} attribute has a value of "${val}", but the expected value is "${value}"`);
|
|
1887
|
+
}
|
|
1888
|
+
return state.info;
|
|
1889
|
+
}
|
|
1890
|
+
catch (e) {
|
|
1891
|
+
await _commandError(state, e, this);
|
|
1892
|
+
}
|
|
1893
|
+
finally {
|
|
1894
|
+
_commandFinally(state, this);
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1840
1897
|
async extractEmailData(emailAddress, options, world) {
|
|
1841
1898
|
if (!emailAddress) {
|
|
1842
1899
|
throw new Error("email address is null");
|
|
@@ -1855,7 +1912,7 @@ class StableBrowser {
|
|
|
1855
1912
|
if (options && options.timeout) {
|
|
1856
1913
|
timeout = options.timeout;
|
|
1857
1914
|
}
|
|
1858
|
-
const serviceUrl =
|
|
1915
|
+
const serviceUrl = _getServerUrl() + "/api/mail/createLinkOrCodeFromEmail";
|
|
1859
1916
|
const request = {
|
|
1860
1917
|
method: "POST",
|
|
1861
1918
|
url: serviceUrl,
|
|
@@ -1932,15 +1989,15 @@ class StableBrowser {
|
|
|
1932
1989
|
scope
|
|
1933
1990
|
.evaluate((node) => {
|
|
1934
1991
|
if (node && node.style) {
|
|
1935
|
-
let originalBorder = node.style.
|
|
1936
|
-
node.style.
|
|
1992
|
+
let originalBorder = node.style.outline;
|
|
1993
|
+
node.style.outline = "2px solid red";
|
|
1937
1994
|
if (window) {
|
|
1938
1995
|
window.addEventListener("beforeunload", function (e) {
|
|
1939
|
-
node.style.
|
|
1996
|
+
node.style.outline = originalBorder;
|
|
1940
1997
|
});
|
|
1941
1998
|
}
|
|
1942
1999
|
setTimeout(function () {
|
|
1943
|
-
node.style.
|
|
2000
|
+
node.style.outline = originalBorder;
|
|
1944
2001
|
}, 2000);
|
|
1945
2002
|
}
|
|
1946
2003
|
})
|
|
@@ -1962,17 +2019,17 @@ class StableBrowser {
|
|
|
1962
2019
|
if (!element.style) {
|
|
1963
2020
|
return;
|
|
1964
2021
|
}
|
|
1965
|
-
var originalBorder = element.style.
|
|
2022
|
+
var originalBorder = element.style.outline;
|
|
1966
2023
|
// Set the new border to be red and 2px solid
|
|
1967
|
-
element.style.
|
|
2024
|
+
element.style.outline = "2px solid red";
|
|
1968
2025
|
if (window) {
|
|
1969
2026
|
window.addEventListener("beforeunload", function (e) {
|
|
1970
|
-
element.style.
|
|
2027
|
+
element.style.outline = originalBorder;
|
|
1971
2028
|
});
|
|
1972
2029
|
}
|
|
1973
2030
|
// Set a timeout to revert to the original border after 2 seconds
|
|
1974
2031
|
setTimeout(function () {
|
|
1975
|
-
element.style.
|
|
2032
|
+
element.style.outline = originalBorder;
|
|
1976
2033
|
}, 2000);
|
|
1977
2034
|
}
|
|
1978
2035
|
return;
|
|
@@ -2028,7 +2085,7 @@ class StableBrowser {
|
|
|
2028
2085
|
}
|
|
2029
2086
|
finally {
|
|
2030
2087
|
const endTime = Date.now();
|
|
2031
|
-
|
|
2088
|
+
_reportToWorld(world, {
|
|
2032
2089
|
type: Types.VERIFY_PAGE_PATH,
|
|
2033
2090
|
text: "Verify page path",
|
|
2034
2091
|
screenshotId,
|
|
@@ -2048,6 +2105,35 @@ class StableBrowser {
|
|
|
2048
2105
|
});
|
|
2049
2106
|
}
|
|
2050
2107
|
}
|
|
2108
|
+
async findTextInAllFrames(dateAlternatives, numberAlternatives, text, state) {
|
|
2109
|
+
const frames = this.page.frames();
|
|
2110
|
+
let results = [];
|
|
2111
|
+
let ignoreCase = false;
|
|
2112
|
+
for (let i = 0; i < frames.length; i++) {
|
|
2113
|
+
if (dateAlternatives.date) {
|
|
2114
|
+
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2115
|
+
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2116
|
+
result.frame = frames[i];
|
|
2117
|
+
results.push(result);
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
else if (numberAlternatives.number) {
|
|
2121
|
+
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2122
|
+
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2123
|
+
result.frame = frames[i];
|
|
2124
|
+
results.push(result);
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
else {
|
|
2128
|
+
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", false, true, ignoreCase, {});
|
|
2129
|
+
result.frame = frames[i];
|
|
2130
|
+
results.push(result);
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
state.info.results = results;
|
|
2134
|
+
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2135
|
+
return resultWithElementsFound;
|
|
2136
|
+
}
|
|
2051
2137
|
async verifyTextExistInPage(text, options = {}, world = null) {
|
|
2052
2138
|
text = unEscapeString(text);
|
|
2053
2139
|
const state = {
|
|
@@ -2057,7 +2143,7 @@ class StableBrowser {
|
|
|
2057
2143
|
locate: false,
|
|
2058
2144
|
scroll: false,
|
|
2059
2145
|
highlight: false,
|
|
2060
|
-
type: Types.
|
|
2146
|
+
type: Types.VERIFY_PAGE_CONTAINS_TEXT,
|
|
2061
2147
|
text: `Verify text exists in page`,
|
|
2062
2148
|
operation: "verifyTextExistInPage",
|
|
2063
2149
|
log: "***** verify text " + text + " exists in page *****\n",
|
|
@@ -2075,31 +2161,7 @@ class StableBrowser {
|
|
|
2075
2161
|
await _preCommand(state, this);
|
|
2076
2162
|
state.info.text = text;
|
|
2077
2163
|
while (true) {
|
|
2078
|
-
const
|
|
2079
|
-
let results = [];
|
|
2080
|
-
for (let i = 0; i < frames.length; i++) {
|
|
2081
|
-
if (dateAlternatives.date) {
|
|
2082
|
-
for (let j = 0; j < dateAlternatives.dates.length; j++) {
|
|
2083
|
-
const result = await this._locateElementByText(frames[i], dateAlternatives.dates[j], "*:not(script, style, head)", true, true, {});
|
|
2084
|
-
result.frame = frames[i];
|
|
2085
|
-
results.push(result);
|
|
2086
|
-
}
|
|
2087
|
-
}
|
|
2088
|
-
else if (numberAlternatives.number) {
|
|
2089
|
-
for (let j = 0; j < numberAlternatives.numbers.length; j++) {
|
|
2090
|
-
const result = await this._locateElementByText(frames[i], numberAlternatives.numbers[j], "*:not(script, style, head)", true, true, {});
|
|
2091
|
-
result.frame = frames[i];
|
|
2092
|
-
results.push(result);
|
|
2093
|
-
}
|
|
2094
|
-
}
|
|
2095
|
-
else {
|
|
2096
|
-
const result = await this._locateElementByText(frames[i], text, "*:not(script, style, head)", true, true, {});
|
|
2097
|
-
result.frame = frames[i];
|
|
2098
|
-
results.push(result);
|
|
2099
|
-
}
|
|
2100
|
-
}
|
|
2101
|
-
state.info.results = results;
|
|
2102
|
-
const resultWithElementsFound = results.filter((result) => result.elementCount > 0);
|
|
2164
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, text, state);
|
|
2103
2165
|
if (resultWithElementsFound.length === 0) {
|
|
2104
2166
|
if (Date.now() - state.startTime > timeout) {
|
|
2105
2167
|
throw new Error(`Text ${text} not found in page`);
|
|
@@ -2109,9 +2171,9 @@ class StableBrowser {
|
|
|
2109
2171
|
}
|
|
2110
2172
|
if (resultWithElementsFound[0].randomToken) {
|
|
2111
2173
|
const frame = resultWithElementsFound[0].frame;
|
|
2112
|
-
const dataAttribute = `[data-blinq-id
|
|
2174
|
+
const dataAttribute = `[data-blinq-id-${resultWithElementsFound[0].randomToken}]`;
|
|
2113
2175
|
await this._highlightElements(frame, dataAttribute);
|
|
2114
|
-
const element = await frame
|
|
2176
|
+
const element = await frame.locator(dataAttribute).first();
|
|
2115
2177
|
if (element) {
|
|
2116
2178
|
await this.scrollIfNeeded(element, state.info);
|
|
2117
2179
|
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
@@ -2129,15 +2191,133 @@ class StableBrowser {
|
|
|
2129
2191
|
_commandFinally(state, this);
|
|
2130
2192
|
}
|
|
2131
2193
|
}
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2194
|
+
async waitForTextToDisappear(text, options = {}, world = null) {
|
|
2195
|
+
text = unEscapeString(text);
|
|
2196
|
+
const state = {
|
|
2197
|
+
text_search: text,
|
|
2198
|
+
options,
|
|
2199
|
+
world,
|
|
2200
|
+
locate: false,
|
|
2201
|
+
scroll: false,
|
|
2202
|
+
highlight: false,
|
|
2203
|
+
type: Types.WAIT_FOR_TEXT_TO_DISAPPEAR,
|
|
2204
|
+
text: `Verify text does not exist in page`,
|
|
2205
|
+
operation: "verifyTextNotExistInPage",
|
|
2206
|
+
log: "***** verify text " + text + " does not exist in page *****\n",
|
|
2207
|
+
};
|
|
2208
|
+
const timeout = this._getLoadTimeout(options);
|
|
2209
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2210
|
+
const newValue = await this._replaceWithLocalData(text, world);
|
|
2211
|
+
if (newValue !== text) {
|
|
2212
|
+
this.logger.info(text + "=" + newValue);
|
|
2213
|
+
text = newValue;
|
|
2214
|
+
}
|
|
2215
|
+
let dateAlternatives = findDateAlternatives(text);
|
|
2216
|
+
let numberAlternatives = findNumberAlternatives(text);
|
|
2217
|
+
try {
|
|
2218
|
+
await _preCommand(state, this);
|
|
2219
|
+
state.info.text = text;
|
|
2220
|
+
while (true) {
|
|
2221
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, text, state);
|
|
2222
|
+
if (resultWithElementsFound.length === 0) {
|
|
2223
|
+
await _screenshot(state, this);
|
|
2224
|
+
return state.info;
|
|
2225
|
+
}
|
|
2226
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2227
|
+
throw new Error(`Text ${text} found in page`);
|
|
2228
|
+
}
|
|
2229
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
catch (e) {
|
|
2233
|
+
await _commandError(state, e, this);
|
|
2234
|
+
}
|
|
2235
|
+
finally {
|
|
2236
|
+
_commandFinally(state, this);
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
async verifyTextRelatedToText(textAnchor, climb, textToVerify, options = {}, world = null) {
|
|
2240
|
+
textAnchor = unEscapeString(textAnchor);
|
|
2241
|
+
textToVerify = unEscapeString(textToVerify);
|
|
2242
|
+
const state = {
|
|
2243
|
+
text_search: textToVerify,
|
|
2244
|
+
options,
|
|
2245
|
+
world,
|
|
2246
|
+
locate: false,
|
|
2247
|
+
scroll: false,
|
|
2248
|
+
highlight: false,
|
|
2249
|
+
type: Types.VERIFY_TEXT_WITH_RELATION,
|
|
2250
|
+
text: `Verify text with relation to another text`,
|
|
2251
|
+
operation: "verify_text_with_relation",
|
|
2252
|
+
log: "***** search for " + textAnchor + " climb " + climb + " and verify " + textToVerify + " found *****\n",
|
|
2253
|
+
};
|
|
2254
|
+
const timeout = this._getLoadTimeout(options);
|
|
2255
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2256
|
+
let newValue = await this._replaceWithLocalData(textAnchor, world);
|
|
2257
|
+
if (newValue !== textAnchor) {
|
|
2258
|
+
this.logger.info(textAnchor + "=" + newValue);
|
|
2259
|
+
textAnchor = newValue;
|
|
2260
|
+
}
|
|
2261
|
+
newValue = await this._replaceWithLocalData(textToVerify, world);
|
|
2262
|
+
if (newValue !== textToVerify) {
|
|
2263
|
+
this.logger.info(textToVerify + "=" + newValue);
|
|
2264
|
+
textToVerify = newValue;
|
|
2265
|
+
}
|
|
2266
|
+
let dateAlternatives = findDateAlternatives(textToVerify);
|
|
2267
|
+
let numberAlternatives = findNumberAlternatives(textToVerify);
|
|
2268
|
+
let foundAncore = false;
|
|
2269
|
+
try {
|
|
2270
|
+
await _preCommand(state, this);
|
|
2271
|
+
state.info.text = textToVerify;
|
|
2272
|
+
while (true) {
|
|
2273
|
+
const resultWithElementsFound = await this.findTextInAllFrames(dateAlternatives, numberAlternatives, textAnchor, state);
|
|
2274
|
+
if (resultWithElementsFound.length === 0) {
|
|
2275
|
+
if (Date.now() - state.startTime > timeout) {
|
|
2276
|
+
throw new Error(`Text ${foundAncore ? textToVerify : textAnchor} not found in page`);
|
|
2277
|
+
}
|
|
2278
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
2279
|
+
continue;
|
|
2280
|
+
}
|
|
2281
|
+
for (let i = 0; i < resultWithElementsFound.length; i++) {
|
|
2282
|
+
foundAncore = true;
|
|
2283
|
+
const result = resultWithElementsFound[i];
|
|
2284
|
+
const token = result.randomToken;
|
|
2285
|
+
const frame = result.frame;
|
|
2286
|
+
let css = `[data-blinq-id-${token}]`;
|
|
2287
|
+
const climbArray1 = [];
|
|
2288
|
+
for (let i = 0; i < climb; i++) {
|
|
2289
|
+
climbArray1.push("..");
|
|
2290
|
+
}
|
|
2291
|
+
let climbXpath = "xpath=" + climbArray1.join("/");
|
|
2292
|
+
css = css + " >> " + climbXpath;
|
|
2293
|
+
const count = await frame.locator(css).count();
|
|
2294
|
+
for (let j = 0; j < count; j++) {
|
|
2295
|
+
const continer = await frame.locator(css).nth(j);
|
|
2296
|
+
const result = await this._locateElementByText(continer, textToVerify, "*", false, true, true, {});
|
|
2297
|
+
if (result.elementCount > 0) {
|
|
2298
|
+
const dataAttribute = "[data-blinq-id-" + result.randomToken + "]";
|
|
2299
|
+
//const cssAnchor = `[data-blinq-id="blinq-id-${token}-anchor"]`;
|
|
2300
|
+
await this._highlightElements(frame, dataAttribute);
|
|
2301
|
+
//await this._highlightElements(frame, cssAnchor);
|
|
2302
|
+
const element = await frame.locator(dataAttribute).first();
|
|
2303
|
+
if (element) {
|
|
2304
|
+
await this.scrollIfNeeded(element, state.info);
|
|
2305
|
+
await element.dispatchEvent("bvt_verify_page_contains_text");
|
|
2306
|
+
}
|
|
2307
|
+
await _screenshot(state, this);
|
|
2308
|
+
return state.info;
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
// await expect(element).toHaveCount(1, { timeout: 10000 });
|
|
2136
2314
|
}
|
|
2137
|
-
|
|
2138
|
-
|
|
2315
|
+
catch (e) {
|
|
2316
|
+
await _commandError(state, e, this);
|
|
2317
|
+
}
|
|
2318
|
+
finally {
|
|
2319
|
+
_commandFinally(state, this);
|
|
2139
2320
|
}
|
|
2140
|
-
return serviceUrl;
|
|
2141
2321
|
}
|
|
2142
2322
|
async visualVerification(text, options = {}, world = null) {
|
|
2143
2323
|
const startTime = Date.now();
|
|
@@ -2153,7 +2333,7 @@ class StableBrowser {
|
|
|
2153
2333
|
throw new Error("TOKEN is not set");
|
|
2154
2334
|
}
|
|
2155
2335
|
try {
|
|
2156
|
-
let serviceUrl =
|
|
2336
|
+
let serviceUrl = _getServerUrl();
|
|
2157
2337
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2158
2338
|
info.screenshotPath = screenshotPath;
|
|
2159
2339
|
const screenshot = await this.takeScreenshot();
|
|
@@ -2194,7 +2374,7 @@ class StableBrowser {
|
|
|
2194
2374
|
}
|
|
2195
2375
|
finally {
|
|
2196
2376
|
const endTime = Date.now();
|
|
2197
|
-
|
|
2377
|
+
_reportToWorld(world, {
|
|
2198
2378
|
type: Types.VERIFY_VISUAL,
|
|
2199
2379
|
text: "Visual verification",
|
|
2200
2380
|
screenshotId,
|
|
@@ -2242,6 +2422,7 @@ class StableBrowser {
|
|
|
2242
2422
|
let screenshotPath = null;
|
|
2243
2423
|
const info = {};
|
|
2244
2424
|
info.log = "";
|
|
2425
|
+
info.locatorLog = new LocatorLog(selectors);
|
|
2245
2426
|
info.operation = "getTableData";
|
|
2246
2427
|
info.selectors = selectors;
|
|
2247
2428
|
try {
|
|
@@ -2262,7 +2443,7 @@ class StableBrowser {
|
|
|
2262
2443
|
}
|
|
2263
2444
|
finally {
|
|
2264
2445
|
const endTime = Date.now();
|
|
2265
|
-
|
|
2446
|
+
_reportToWorld(world, {
|
|
2266
2447
|
element_name: selectors.element_name,
|
|
2267
2448
|
type: Types.GET_TABLE_DATA,
|
|
2268
2449
|
text: "Get table data",
|
|
@@ -2317,7 +2498,7 @@ class StableBrowser {
|
|
|
2317
2498
|
info.operation = "analyzeTable";
|
|
2318
2499
|
info.selectors = selectors;
|
|
2319
2500
|
info.query = query;
|
|
2320
|
-
query =
|
|
2501
|
+
query = _fixUsingParams(query, _params);
|
|
2321
2502
|
info.query_fixed = query;
|
|
2322
2503
|
info.operator = operator;
|
|
2323
2504
|
info.value = value;
|
|
@@ -2428,7 +2609,7 @@ class StableBrowser {
|
|
|
2428
2609
|
}
|
|
2429
2610
|
finally {
|
|
2430
2611
|
const endTime = Date.now();
|
|
2431
|
-
|
|
2612
|
+
_reportToWorld(world, {
|
|
2432
2613
|
element_name: selectors.element_name,
|
|
2433
2614
|
type: Types.ANALYZE_TABLE,
|
|
2434
2615
|
text: "Analyze table",
|
|
@@ -2501,7 +2682,7 @@ class StableBrowser {
|
|
|
2501
2682
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2502
2683
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2503
2684
|
const endTime = Date.now();
|
|
2504
|
-
|
|
2685
|
+
_reportToWorld(world, {
|
|
2505
2686
|
type: Types.GET_PAGE_STATUS,
|
|
2506
2687
|
text: "Wait for page load",
|
|
2507
2688
|
screenshotId,
|
|
@@ -2545,6 +2726,11 @@ class StableBrowser {
|
|
|
2545
2726
|
_commandFinally(state, this);
|
|
2546
2727
|
}
|
|
2547
2728
|
}
|
|
2729
|
+
saveTestDataAsGlobal(options, world) {
|
|
2730
|
+
const dataFile = this._getDataFile(world);
|
|
2731
|
+
process.env.GLOBAL_TEST_DATA_FILE = dataFile;
|
|
2732
|
+
this.logger.info("Save the scenario test data as global for the following scenarios.");
|
|
2733
|
+
}
|
|
2548
2734
|
async setViewportSize(width, hight, options = {}, world = null) {
|
|
2549
2735
|
const startTime = Date.now();
|
|
2550
2736
|
let error = null;
|
|
@@ -2568,7 +2754,7 @@ class StableBrowser {
|
|
|
2568
2754
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2569
2755
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world));
|
|
2570
2756
|
const endTime = Date.now();
|
|
2571
|
-
|
|
2757
|
+
_reportToWorld(world, {
|
|
2572
2758
|
type: Types.SET_VIEWPORT,
|
|
2573
2759
|
text: "set viewport size to " + width + "x" + hight,
|
|
2574
2760
|
screenshotId,
|
|
@@ -2605,7 +2791,7 @@ class StableBrowser {
|
|
|
2605
2791
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2606
2792
|
({ screenshotId, screenshotPath } = await this._screenShot(options, world, info));
|
|
2607
2793
|
const endTime = Date.now();
|
|
2608
|
-
|
|
2794
|
+
_reportToWorld(world, {
|
|
2609
2795
|
type: Types.GET_PAGE_STATUS,
|
|
2610
2796
|
text: "page relaod",
|
|
2611
2797
|
screenshotId,
|
|
@@ -2641,11 +2827,45 @@ class StableBrowser {
|
|
|
2641
2827
|
console.log("#-#");
|
|
2642
2828
|
}
|
|
2643
2829
|
}
|
|
2644
|
-
|
|
2645
|
-
if (
|
|
2646
|
-
|
|
2830
|
+
async beforeStep(world, step) {
|
|
2831
|
+
if (this.stepIndex === undefined) {
|
|
2832
|
+
this.stepIndex = 0;
|
|
2833
|
+
}
|
|
2834
|
+
else {
|
|
2835
|
+
this.stepIndex++;
|
|
2836
|
+
}
|
|
2837
|
+
if (step && step.pickleStep && step.pickleStep.text) {
|
|
2838
|
+
this.stepName = step.pickleStep.text;
|
|
2839
|
+
this.logger.info("step: " + this.stepName);
|
|
2840
|
+
}
|
|
2841
|
+
else if (step && step.text) {
|
|
2842
|
+
this.stepName = step.text;
|
|
2843
|
+
}
|
|
2844
|
+
else {
|
|
2845
|
+
this.stepName = "step " + this.stepIndex;
|
|
2846
|
+
}
|
|
2847
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2848
|
+
if (this.context.browserObject.context) {
|
|
2849
|
+
await this.context.browserObject.context.tracing.startChunk({ title: this.stepName });
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
if (this.tags === null && step && step.pickle && step.pickle.tags) {
|
|
2853
|
+
this.tags = step.pickle.tags.map((tag) => tag.name);
|
|
2854
|
+
// check if @global_test_data tag is present
|
|
2855
|
+
if (this.tags.includes("@global_test_data")) {
|
|
2856
|
+
this.saveTestDataAsGlobal({}, world);
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
}
|
|
2860
|
+
async afterStep(world, step) {
|
|
2861
|
+
this.stepName = null;
|
|
2862
|
+
if (this.context && this.context.browserObject && this.context.browserObject.trace === true) {
|
|
2863
|
+
if (this.context.browserObject.context) {
|
|
2864
|
+
await this.context.browserObject.context.tracing.stopChunk({
|
|
2865
|
+
path: path.join(this.context.browserObject.traceFolder, `trace-${this.stepIndex}.zip`),
|
|
2866
|
+
});
|
|
2867
|
+
}
|
|
2647
2868
|
}
|
|
2648
|
-
world.attach(JSON.stringify(properties), { mediaType: "application/json" });
|
|
2649
2869
|
}
|
|
2650
2870
|
}
|
|
2651
2871
|
function createTimedPromise(promise, label) {
|
|
@@ -2653,156 +2873,5 @@ function createTimedPromise(promise, label) {
|
|
|
2653
2873
|
.then((result) => ({ status: "fulfilled", label, result }))
|
|
2654
2874
|
.catch((error) => Promise.reject({ status: "rejected", label, error }));
|
|
2655
2875
|
}
|
|
2656
|
-
const KEYBOARD_EVENTS = [
|
|
2657
|
-
"ALT",
|
|
2658
|
-
"AltGraph",
|
|
2659
|
-
"CapsLock",
|
|
2660
|
-
"Control",
|
|
2661
|
-
"Fn",
|
|
2662
|
-
"FnLock",
|
|
2663
|
-
"Hyper",
|
|
2664
|
-
"Meta",
|
|
2665
|
-
"NumLock",
|
|
2666
|
-
"ScrollLock",
|
|
2667
|
-
"Shift",
|
|
2668
|
-
"Super",
|
|
2669
|
-
"Symbol",
|
|
2670
|
-
"SymbolLock",
|
|
2671
|
-
"Enter",
|
|
2672
|
-
"Tab",
|
|
2673
|
-
"ArrowDown",
|
|
2674
|
-
"ArrowLeft",
|
|
2675
|
-
"ArrowRight",
|
|
2676
|
-
"ArrowUp",
|
|
2677
|
-
"End",
|
|
2678
|
-
"Home",
|
|
2679
|
-
"PageDown",
|
|
2680
|
-
"PageUp",
|
|
2681
|
-
"Backspace",
|
|
2682
|
-
"Clear",
|
|
2683
|
-
"Copy",
|
|
2684
|
-
"CrSel",
|
|
2685
|
-
"Cut",
|
|
2686
|
-
"Delete",
|
|
2687
|
-
"EraseEof",
|
|
2688
|
-
"ExSel",
|
|
2689
|
-
"Insert",
|
|
2690
|
-
"Paste",
|
|
2691
|
-
"Redo",
|
|
2692
|
-
"Undo",
|
|
2693
|
-
"Accept",
|
|
2694
|
-
"Again",
|
|
2695
|
-
"Attn",
|
|
2696
|
-
"Cancel",
|
|
2697
|
-
"ContextMenu",
|
|
2698
|
-
"Escape",
|
|
2699
|
-
"Execute",
|
|
2700
|
-
"Find",
|
|
2701
|
-
"Finish",
|
|
2702
|
-
"Help",
|
|
2703
|
-
"Pause",
|
|
2704
|
-
"Play",
|
|
2705
|
-
"Props",
|
|
2706
|
-
"Select",
|
|
2707
|
-
"ZoomIn",
|
|
2708
|
-
"ZoomOut",
|
|
2709
|
-
"BrightnessDown",
|
|
2710
|
-
"BrightnessUp",
|
|
2711
|
-
"Eject",
|
|
2712
|
-
"LogOff",
|
|
2713
|
-
"Power",
|
|
2714
|
-
"PowerOff",
|
|
2715
|
-
"PrintScreen",
|
|
2716
|
-
"Hibernate",
|
|
2717
|
-
"Standby",
|
|
2718
|
-
"WakeUp",
|
|
2719
|
-
"AllCandidates",
|
|
2720
|
-
"Alphanumeric",
|
|
2721
|
-
"CodeInput",
|
|
2722
|
-
"Compose",
|
|
2723
|
-
"Convert",
|
|
2724
|
-
"Dead",
|
|
2725
|
-
"FinalMode",
|
|
2726
|
-
"GroupFirst",
|
|
2727
|
-
"GroupLast",
|
|
2728
|
-
"GroupNext",
|
|
2729
|
-
"GroupPrevious",
|
|
2730
|
-
"ModeChange",
|
|
2731
|
-
"NextCandidate",
|
|
2732
|
-
"NonConvert",
|
|
2733
|
-
"PreviousCandidate",
|
|
2734
|
-
"Process",
|
|
2735
|
-
"SingleCandidate",
|
|
2736
|
-
"HangulMode",
|
|
2737
|
-
"HanjaMode",
|
|
2738
|
-
"JunjaMode",
|
|
2739
|
-
"Eisu",
|
|
2740
|
-
"Hankaku",
|
|
2741
|
-
"Hiragana",
|
|
2742
|
-
"HiraganaKatakana",
|
|
2743
|
-
"KanaMode",
|
|
2744
|
-
"KanjiMode",
|
|
2745
|
-
"Katakana",
|
|
2746
|
-
"Romaji",
|
|
2747
|
-
"Zenkaku",
|
|
2748
|
-
"ZenkakuHanaku",
|
|
2749
|
-
"F1",
|
|
2750
|
-
"F2",
|
|
2751
|
-
"F3",
|
|
2752
|
-
"F4",
|
|
2753
|
-
"F5",
|
|
2754
|
-
"F6",
|
|
2755
|
-
"F7",
|
|
2756
|
-
"F8",
|
|
2757
|
-
"F9",
|
|
2758
|
-
"F10",
|
|
2759
|
-
"F11",
|
|
2760
|
-
"F12",
|
|
2761
|
-
"Soft1",
|
|
2762
|
-
"Soft2",
|
|
2763
|
-
"Soft3",
|
|
2764
|
-
"Soft4",
|
|
2765
|
-
"ChannelDown",
|
|
2766
|
-
"ChannelUp",
|
|
2767
|
-
"Close",
|
|
2768
|
-
"MailForward",
|
|
2769
|
-
"MailReply",
|
|
2770
|
-
"MailSend",
|
|
2771
|
-
"MediaFastForward",
|
|
2772
|
-
"MediaPause",
|
|
2773
|
-
"MediaPlay",
|
|
2774
|
-
"MediaPlayPause",
|
|
2775
|
-
"MediaRecord",
|
|
2776
|
-
"MediaRewind",
|
|
2777
|
-
"MediaStop",
|
|
2778
|
-
"MediaTrackNext",
|
|
2779
|
-
"MediaTrackPrevious",
|
|
2780
|
-
"AudioBalanceLeft",
|
|
2781
|
-
"AudioBalanceRight",
|
|
2782
|
-
"AudioBassBoostDown",
|
|
2783
|
-
"AudioBassBoostToggle",
|
|
2784
|
-
"AudioBassBoostUp",
|
|
2785
|
-
"AudioFaderFront",
|
|
2786
|
-
"AudioFaderRear",
|
|
2787
|
-
"AudioSurroundModeNext",
|
|
2788
|
-
"AudioTrebleDown",
|
|
2789
|
-
"AudioTrebleUp",
|
|
2790
|
-
"AudioVolumeDown",
|
|
2791
|
-
"AudioVolumeMute",
|
|
2792
|
-
"AudioVolumeUp",
|
|
2793
|
-
"MicrophoneToggle",
|
|
2794
|
-
"MicrophoneVolumeDown",
|
|
2795
|
-
"MicrophoneVolumeMute",
|
|
2796
|
-
"MicrophoneVolumeUp",
|
|
2797
|
-
"TV",
|
|
2798
|
-
"TV3DMode",
|
|
2799
|
-
"TVAntennaCable",
|
|
2800
|
-
"TVAudioDescription",
|
|
2801
|
-
];
|
|
2802
|
-
function unEscapeString(str) {
|
|
2803
|
-
const placeholder = "__NEWLINE__";
|
|
2804
|
-
str = str.replace(new RegExp(placeholder, "g"), "\n");
|
|
2805
|
-
return str;
|
|
2806
|
-
}
|
|
2807
2876
|
export { StableBrowser };
|
|
2808
2877
|
//# sourceMappingURL=stable_browser.js.map
|