automation_model 1.0.592-dev → 1.0.592-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/README.md +130 -0
- package/lib/api.js +35 -21
- package/lib/api.js.map +1 -1
- package/lib/auto_page.d.ts +4 -2
- package/lib/auto_page.js +136 -8
- package/lib/auto_page.js.map +1 -1
- package/lib/browser_manager.js +53 -16
- package/lib/browser_manager.js.map +1 -1
- package/lib/bruno.d.ts +1 -0
- package/lib/bruno.js +284 -0
- package/lib/bruno.js.map +1 -0
- package/lib/command_common.d.ts +4 -4
- package/lib/command_common.js +47 -16
- package/lib/command_common.js.map +1 -1
- package/lib/error-messages.js +18 -0
- package/lib/error-messages.js.map +1 -1
- 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 +3 -2
- package/lib/init_browser.js +64 -11
- package/lib/init_browser.js.map +1 -1
- package/lib/locate_element.js +2 -2
- package/lib/locate_element.js.map +1 -1
- package/lib/network.d.ts +1 -1
- package/lib/network.js +5 -5
- package/lib/network.js.map +1 -1
- package/lib/stable_browser.d.ts +22 -4
- package/lib/stable_browser.js +775 -197
- package/lib/stable_browser.js.map +1 -1
- package/lib/table_helper.d.ts +19 -0
- package/lib/table_helper.js +116 -0
- package/lib/table_helper.js.map +1 -0
- package/lib/test_context.d.ts +3 -0
- package/lib/test_context.js +2 -0
- package/lib/test_context.js.map +1 -1
- package/lib/utils.d.ts +9 -3
- package/lib/utils.js +309 -21
- package/lib/utils.js.map +1 -1
- package/package.json +8 -8
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
export async function findHighestWithSameInnerText(cssSelector, scope, web) {
|
|
2
|
+
await web._highlightElements(scope, cssSelector);
|
|
3
|
+
const element = await scope.locator(cssSelector).first();
|
|
4
|
+
if (!element) {
|
|
5
|
+
throw new Error("header element not found");
|
|
6
|
+
}
|
|
7
|
+
const innerText = await element.innerText();
|
|
8
|
+
// climb to the parent element until the innerText is changing, get the top element with the same innerText
|
|
9
|
+
let climb = 0;
|
|
10
|
+
let topElement = element;
|
|
11
|
+
let elementCss = cssSelector;
|
|
12
|
+
while (true) {
|
|
13
|
+
climb++;
|
|
14
|
+
// create a climb xpath: 1: .. 2: ../.. etc.
|
|
15
|
+
const climbXpath = "xpath=" + "../".repeat(climb).slice(0, -1);
|
|
16
|
+
const climbCss = elementCss + " >> " + climbXpath;
|
|
17
|
+
const climbElement = await scope.locator(climbCss).first();
|
|
18
|
+
if (!climbElement) {
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
const climbInnerText = await climbElement.innerText();
|
|
22
|
+
if (climbInnerText !== innerText) {
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
topElement = climbElement;
|
|
26
|
+
elementCss = climbCss;
|
|
27
|
+
}
|
|
28
|
+
return { element: topElement, cssSelector: elementCss, climb: climb, innerText: innerText };
|
|
29
|
+
}
|
|
30
|
+
export async function findCellRectangle(headerResult, rowResult, web, info) {
|
|
31
|
+
await web.scrollIfNeeded(rowResult.element, info);
|
|
32
|
+
// find the header cell and the row cell location
|
|
33
|
+
const headerRect = await headerResult.element.boundingBox();
|
|
34
|
+
const rowRect = await rowResult.element.boundingBox();
|
|
35
|
+
if (!headerRect || !rowRect) {
|
|
36
|
+
throw new Error("element not found");
|
|
37
|
+
}
|
|
38
|
+
// found there rectengle of cell that is in the header horizontal and the row vertical
|
|
39
|
+
return {
|
|
40
|
+
x: headerRect.x,
|
|
41
|
+
y: rowRect.y,
|
|
42
|
+
width: headerRect.width,
|
|
43
|
+
height: rowRect.height,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export async function _findCellArea(headerText, rowText, web, state) {
|
|
47
|
+
const headerFoundElements = await web.findTextInAllFrames({}, {}, headerText, state);
|
|
48
|
+
if (headerFoundElements.length === 0) {
|
|
49
|
+
throw new Error("header not found");
|
|
50
|
+
}
|
|
51
|
+
if (headerFoundElements.length > 1) {
|
|
52
|
+
throw new Error("multiple headers found");
|
|
53
|
+
}
|
|
54
|
+
const rowFoundElements = await web.findTextInAllFrames({}, {}, rowText, state);
|
|
55
|
+
if (rowFoundElements.length === 0) {
|
|
56
|
+
throw new Error("row not found");
|
|
57
|
+
}
|
|
58
|
+
if (rowFoundElements.length > 1) {
|
|
59
|
+
throw new Error("multiple rows found");
|
|
60
|
+
}
|
|
61
|
+
const headerScope = headerFoundElements[0].frame;
|
|
62
|
+
const headerResult = await findHighestWithSameInnerText(`[data-blinq-id-${headerFoundElements[0].randomToken}]`, headerScope, web);
|
|
63
|
+
const rowScope = rowFoundElements[0].frame;
|
|
64
|
+
const rowResult = await findHighestWithSameInnerText(`[data-blinq-id-${rowFoundElements[0].randomToken}]`, rowScope, web);
|
|
65
|
+
return await findCellRectangle(headerResult, rowResult, web, state.info);
|
|
66
|
+
}
|
|
67
|
+
export async function findElementsInArea(cssSelector, area, web, options) {
|
|
68
|
+
if (!cssSelector) {
|
|
69
|
+
cssSelector = "*";
|
|
70
|
+
}
|
|
71
|
+
const frames = await web.page.frames();
|
|
72
|
+
const elements = [];
|
|
73
|
+
for (const scope of frames) {
|
|
74
|
+
const count = await scope.locator(cssSelector).count();
|
|
75
|
+
for (let i = 0; i < count; i++) {
|
|
76
|
+
const element = await scope.locator(cssSelector).nth(i);
|
|
77
|
+
elements.push(element);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const foundElements = [];
|
|
81
|
+
let hTollarance = 10;
|
|
82
|
+
let vTollarance = 10;
|
|
83
|
+
if (options && options.hTollarance && options.vTollarance) {
|
|
84
|
+
hTollarance = options.hTollarance;
|
|
85
|
+
vTollarance = options.vTollarance;
|
|
86
|
+
}
|
|
87
|
+
for (const element of elements) {
|
|
88
|
+
const rect = await element.boundingBox();
|
|
89
|
+
if (!rect) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (rect.x >= area.x - hTollarance &&
|
|
93
|
+
rect.x + rect.width <= area.x + area.width + hTollarance &&
|
|
94
|
+
rect.y >= area.y - vTollarance &&
|
|
95
|
+
rect.y + rect.height <= area.y + area.height + vTollarance) {
|
|
96
|
+
foundElements.push(element);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (foundElements.length === 0) {
|
|
100
|
+
// find elements that intersect with the area
|
|
101
|
+
for (const element of elements) {
|
|
102
|
+
const rect = await element.boundingBox();
|
|
103
|
+
if (!rect) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (rect.x + rect.width >= area.x &&
|
|
107
|
+
rect.x <= area.x + area.width &&
|
|
108
|
+
rect.y + rect.height >= area.y &&
|
|
109
|
+
rect.y <= area.y + area.height) {
|
|
110
|
+
foundElements.push(element);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return foundElements;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=table_helper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table_helper.js","sourceRoot":"","sources":["../../src/table_helper.ts"],"names":[],"mappings":"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"}
|
package/lib/test_context.d.ts
CHANGED
|
@@ -2,8 +2,10 @@ import { BrowserContext, Page, Browser as PlaywrightBrowser } from "playwright";
|
|
|
2
2
|
import { Environment } from "./environment.js";
|
|
3
3
|
import { StableBrowser } from "./stable_browser.js";
|
|
4
4
|
import { Api } from "./api.js";
|
|
5
|
+
import { InitScripts } from "./generation_scripts.js";
|
|
5
6
|
declare class TestContext {
|
|
6
7
|
stable: StableBrowser | null;
|
|
8
|
+
web: StableBrowser | null;
|
|
7
9
|
browser: PlaywrightBrowser | null;
|
|
8
10
|
playContext: BrowserContext | null;
|
|
9
11
|
page: Page | null;
|
|
@@ -14,6 +16,7 @@ declare class TestContext {
|
|
|
14
16
|
headless: boolean;
|
|
15
17
|
browserName: string | null;
|
|
16
18
|
browserObject: any;
|
|
19
|
+
initScripts: InitScripts | null;
|
|
17
20
|
constructor();
|
|
18
21
|
}
|
|
19
22
|
export { TestContext };
|
package/lib/test_context.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
class TestContext {
|
|
2
2
|
stable = null;
|
|
3
|
+
web = null;
|
|
3
4
|
browser = null;
|
|
4
5
|
playContext = null;
|
|
5
6
|
page = null;
|
|
@@ -10,6 +11,7 @@ class TestContext {
|
|
|
10
11
|
headless = false;
|
|
11
12
|
browserName = null;
|
|
12
13
|
browserObject = null;
|
|
14
|
+
initScripts = null;
|
|
13
15
|
constructor() { }
|
|
14
16
|
}
|
|
15
17
|
export { TestContext };
|
package/lib/test_context.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test_context.js","sourceRoot":"","sources":["../../src/test_context.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"test_context.js","sourceRoot":"","sources":["../../src/test_context.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW;IACf,MAAM,GAAyB,IAAI,CAAC;IACpC,GAAG,GAAyB,IAAI,CAAC;IACjC,OAAO,GAA6B,IAAI,CAAC;IACzC,WAAW,GAA0B,IAAI,CAAC;IAC1C,IAAI,GAAgB,IAAI,CAAC;IACzB,WAAW,GAAuB,IAAI,CAAC;IACvC,YAAY,GAAkB,IAAI,CAAC;IACnC,GAAG,GAAe,IAAI,CAAC;IACvB,QAAQ,GAAG,KAAK,CAAC;IACjB,QAAQ,GAAG,KAAK,CAAC;IACjB,WAAW,GAAkB,IAAI,CAAC;IAClC,aAAa,GAAQ,IAAI,CAAC;IAC1B,WAAW,GAAuB,IAAI,CAAC;IACvC,gBAAe,CAAC;CACjB;AACD,OAAO,EAAE,WAAW,EAAE,CAAC"}
|
package/lib/utils.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
declare function encrypt(text: string, key?: string | null): string;
|
|
2
|
-
declare function
|
|
2
|
+
declare function getTestDataValue(key: string, environment?: string): any;
|
|
3
|
+
declare function decrypt(encryptedText: string, key?: string | null, totpWait?: boolean): string;
|
|
4
|
+
export declare function testForRegex(text: string): boolean;
|
|
3
5
|
declare function _convertToRegexQuery(text: string, isRegex: boolean, fullMatch: boolean, ignoreCase: boolean): string;
|
|
4
|
-
declare function
|
|
6
|
+
declare function _getDataFile(world?: any, context?: any, web?: any): string;
|
|
7
|
+
declare function replaceWithLocalTestData(value: string, world: any, _decrypt?: boolean, totpWait?: boolean, context?: any, web?: any): Promise<string>;
|
|
5
8
|
declare function maskValue(value: string): string;
|
|
6
9
|
declare function _copyContext(from: any, to: any): void;
|
|
7
10
|
declare function scrollPageToLoadLazyElements(page: any): Promise<void>;
|
|
@@ -11,7 +14,10 @@ declare function getWebLogFile(logFolder: string): string;
|
|
|
11
14
|
declare function _fixLocatorUsingParams(locator: any, _params: Params): any;
|
|
12
15
|
declare function _isObject(value: any): any;
|
|
13
16
|
declare function scanAndManipulate(currentObj: any, _params: Params): void;
|
|
17
|
+
declare function extractStepExampleParameters(step: any): any;
|
|
18
|
+
export declare function performAction(action: string, element: any, options: any, web: any, state: any, _params: Params): Promise<void>;
|
|
14
19
|
declare const KEYBOARD_EVENTS: string[];
|
|
15
20
|
declare function unEscapeString(str: string): string;
|
|
16
21
|
declare function _getServerUrl(): string;
|
|
17
|
-
|
|
22
|
+
declare function tryParseJson(input: any): any;
|
|
23
|
+
export { encrypt, decrypt, replaceWithLocalTestData, maskValue, _copyContext, scrollPageToLoadLazyElements, _fixUsingParams, getWebLogFile, _fixLocatorUsingParams, _isObject, scanAndManipulate, KEYBOARD_EVENTS, unEscapeString, Params, _getServerUrl, _convertToRegexQuery, extractStepExampleParameters, _getDataFile, tryParseJson, getTestDataValue, };
|
package/lib/utils.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import CryptoJS from "crypto-js";
|
|
2
|
-
import objectPath from "object-path";
|
|
3
2
|
import path from "path";
|
|
4
3
|
import { TOTP } from "totp-generator";
|
|
5
4
|
import fs from "fs";
|
|
5
|
+
import axios from "axios";
|
|
6
|
+
import objectPath from "object-path";
|
|
6
7
|
// Function to encrypt a string
|
|
7
8
|
function encrypt(text, key = null) {
|
|
8
9
|
if (!key) {
|
|
@@ -10,8 +11,29 @@ function encrypt(text, key = null) {
|
|
|
10
11
|
}
|
|
11
12
|
return CryptoJS.AES.encrypt(text, key).toString();
|
|
12
13
|
}
|
|
14
|
+
function getTestDataValue(key, environment = "*") {
|
|
15
|
+
const blinqEnvPath = "data/data.json";
|
|
16
|
+
const envPath = path.resolve(process.cwd(), blinqEnvPath);
|
|
17
|
+
const envJson = JSON.parse(fs.readFileSync(envPath, "utf-8"));
|
|
18
|
+
const dataArray = envJson[environment];
|
|
19
|
+
const item = dataArray.find((item) => item.key === key);
|
|
20
|
+
// if the item is not found in the specific env, check if it exists in the default environment
|
|
21
|
+
if (!item && environment !== "*") {
|
|
22
|
+
return getTestDataValue(key, "*");
|
|
23
|
+
}
|
|
24
|
+
if (!item) {
|
|
25
|
+
throw new Error(`Key ${key} not found in data.json`);
|
|
26
|
+
}
|
|
27
|
+
if (item.DataType === "string") {
|
|
28
|
+
return item.value;
|
|
29
|
+
}
|
|
30
|
+
else if (item.DataType === "secret" || item.DataType === "totp") {
|
|
31
|
+
return decrypt(item.value, null, false);
|
|
32
|
+
}
|
|
33
|
+
throw new Error(`Unsupported data type for key ${key}`);
|
|
34
|
+
}
|
|
13
35
|
// Function to decrypt a string
|
|
14
|
-
|
|
36
|
+
function decrypt(encryptedText, key = null, totpWait = true) {
|
|
15
37
|
if (!key) {
|
|
16
38
|
key = _findKey();
|
|
17
39
|
}
|
|
@@ -23,13 +45,13 @@ async function decrypt(encryptedText, key = null, totpWait = true) {
|
|
|
23
45
|
const bytes = CryptoJS.AES.decrypt(encryptedText, key);
|
|
24
46
|
encryptedText = bytes.toString(CryptoJS.enc.Utf8);
|
|
25
47
|
let { otp, expires } = TOTP.generate(encryptedText);
|
|
26
|
-
if (totpWait) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
48
|
+
// if (totpWait) {
|
|
49
|
+
// // expires is in unix time, check if we have at least 10 seconds left, if it's less than wait for the expires time
|
|
50
|
+
// if (expires - Date.now() < 10000) {
|
|
51
|
+
// await new Promise((resolve) => setTimeout(resolve, (expires - Date.now() + 1000) % 30000));
|
|
52
|
+
// ({ otp, expires } = TOTP.generate(encryptedText));
|
|
53
|
+
// }
|
|
54
|
+
// }
|
|
33
55
|
return otp;
|
|
34
56
|
}
|
|
35
57
|
if (encryptedText.startsWith("mask:")) {
|
|
@@ -38,6 +60,22 @@ async function decrypt(encryptedText, key = null, totpWait = true) {
|
|
|
38
60
|
const bytes = CryptoJS.AES.decrypt(encryptedText, key);
|
|
39
61
|
return bytes.toString(CryptoJS.enc.Utf8);
|
|
40
62
|
}
|
|
63
|
+
export function testForRegex(text) {
|
|
64
|
+
const regexEndPattern = /\/([gimuy]*)$/;
|
|
65
|
+
if (text.startsWith("/")) {
|
|
66
|
+
const match = regexEndPattern.test(text);
|
|
67
|
+
if (match) {
|
|
68
|
+
try {
|
|
69
|
+
const regex = new RegExp(text.substring(1, text.lastIndexOf("/")), text.match(regexEndPattern)[1]);
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// not regex
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
41
79
|
function _convertToRegexQuery(text, isRegex, fullMatch, ignoreCase) {
|
|
42
80
|
let query = "internal:text=/";
|
|
43
81
|
let queryEnd = "/";
|
|
@@ -48,6 +86,7 @@ function _convertToRegexQuery(text, isRegex, fullMatch, ignoreCase) {
|
|
|
48
86
|
if (match) {
|
|
49
87
|
try {
|
|
50
88
|
const regex = new RegExp(text.substring(1, text.lastIndexOf("/")), text.match(regexEndPattern)[1]);
|
|
89
|
+
text = text.replace(/"/g, '\\"');
|
|
51
90
|
return "internal:text=" + text;
|
|
52
91
|
}
|
|
53
92
|
catch {
|
|
@@ -113,53 +152,169 @@ function _findKey() {
|
|
|
113
152
|
// extract the base folder name
|
|
114
153
|
return path.basename(folder);
|
|
115
154
|
}
|
|
116
|
-
function _getDataFile(world = null, context = null,
|
|
155
|
+
function _getDataFile(world = null, context = null, web = null) {
|
|
156
|
+
let dataFile = null;
|
|
157
|
+
if (world && world.reportFolder) {
|
|
158
|
+
dataFile = path.join(world.reportFolder, "data.json");
|
|
159
|
+
}
|
|
160
|
+
else if (web && web.reportFolder) {
|
|
161
|
+
dataFile = path.join(web.reportFolder, "data.json");
|
|
162
|
+
}
|
|
163
|
+
else if (context && context.reportFolder) {
|
|
164
|
+
dataFile = path.join(context.reportFolder, "data.json");
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
dataFile = "data.json";
|
|
168
|
+
}
|
|
169
|
+
return dataFile;
|
|
170
|
+
}
|
|
171
|
+
function _getTestDataFile(world = null, context = null, web = null) {
|
|
117
172
|
let dataFile = null;
|
|
118
173
|
if (world && world.reportFolder) {
|
|
119
174
|
dataFile = path.join(world.reportFolder, "data.json");
|
|
120
175
|
}
|
|
121
|
-
else if (
|
|
122
|
-
dataFile = path.join(
|
|
176
|
+
else if (web && web.reportFolder) {
|
|
177
|
+
dataFile = path.join(web.reportFolder, "data.json");
|
|
123
178
|
}
|
|
124
179
|
else if (context && context.reportFolder) {
|
|
125
180
|
dataFile = path.join(context.reportFolder, "data.json");
|
|
126
181
|
}
|
|
182
|
+
else if (fs.existsSync(path.join("data", "data.json"))) {
|
|
183
|
+
dataFile = path.join("data", "data.json");
|
|
184
|
+
}
|
|
127
185
|
else {
|
|
128
186
|
dataFile = "data.json";
|
|
129
187
|
}
|
|
130
188
|
return dataFile;
|
|
131
189
|
}
|
|
132
|
-
function _getTestData(world = null, context = null,
|
|
133
|
-
const dataFile = _getDataFile(world, context,
|
|
190
|
+
function _getTestData(world = null, context = null, web = null) {
|
|
191
|
+
const dataFile = _getDataFile(world, context, web);
|
|
134
192
|
let data = {};
|
|
135
193
|
if (fs.existsSync(dataFile)) {
|
|
136
194
|
data = JSON.parse(fs.readFileSync(dataFile, "utf8"));
|
|
137
195
|
}
|
|
138
196
|
return data;
|
|
139
197
|
}
|
|
140
|
-
async function replaceWithLocalTestData(value, world, _decrypt = true, totpWait = true, context = null,
|
|
198
|
+
async function replaceWithLocalTestData(value, world, _decrypt = true, totpWait = true, context = null, web = null) {
|
|
141
199
|
if (!value) {
|
|
142
200
|
return value;
|
|
143
201
|
}
|
|
202
|
+
let env = "";
|
|
203
|
+
if (context && context.environment) {
|
|
204
|
+
env = context.environment.name;
|
|
205
|
+
}
|
|
144
206
|
// find all the accurance of {{(.*?)}} and replace with the value
|
|
145
207
|
let regex = /{{(.*?)}}/g;
|
|
146
208
|
let matches = value.match(regex);
|
|
147
209
|
if (matches) {
|
|
148
|
-
const testData = _getTestData(world, context,
|
|
210
|
+
const testData = _getTestData(world, context, web);
|
|
149
211
|
for (let i = 0; i < matches.length; i++) {
|
|
150
212
|
let match = matches[i];
|
|
151
213
|
let key = match.substring(2, match.length - 2);
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
214
|
+
if (key && key.trim().startsWith("date:")) {
|
|
215
|
+
const dateQuery = key.substring(5);
|
|
216
|
+
const parts = dateQuery.split(">>");
|
|
217
|
+
const returnTemplate = parts[1] || null;
|
|
218
|
+
let serviceUrl = _getServerUrl();
|
|
219
|
+
const config = {
|
|
220
|
+
method: "post",
|
|
221
|
+
url: `${serviceUrl}/api/runs/find-date/find`,
|
|
222
|
+
headers: {
|
|
223
|
+
"x-source": "true",
|
|
224
|
+
"Content-Type": "application/json",
|
|
225
|
+
Authorization: `Bearer ${process.env.TOKEN}`,
|
|
226
|
+
},
|
|
227
|
+
data: JSON.stringify({
|
|
228
|
+
value: parts[0],
|
|
229
|
+
}),
|
|
230
|
+
};
|
|
231
|
+
let result = await axios.request(config);
|
|
232
|
+
//console.log(JSON.stringify(frameDump[0]));
|
|
233
|
+
if (result.status !== 200 || !result.data || result.data.status !== true || !result.data.result) {
|
|
234
|
+
console.error("Failed to find date");
|
|
235
|
+
throw new Error("Failed to find date");
|
|
236
|
+
}
|
|
237
|
+
value = formatDate(result.data.result, returnTemplate);
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
let newValue = replaceTestDataValue(env, key, testData, _decrypt);
|
|
241
|
+
if (newValue !== null) {
|
|
242
|
+
value = value.replace(match, newValue);
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
newValue = replaceTestDataValue("*", key, testData, _decrypt);
|
|
246
|
+
if (newValue !== null) {
|
|
247
|
+
value = value.replace(match, newValue);
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
throw new Error(`Parameter "{{${key}}}" is undefined in the test data`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
155
253
|
}
|
|
156
254
|
}
|
|
157
255
|
}
|
|
158
256
|
if ((value.startsWith("secret:") || value.startsWith("totp:") || value.startsWith("mask:")) && _decrypt) {
|
|
159
|
-
return
|
|
257
|
+
return decrypt(value, null, totpWait);
|
|
258
|
+
}
|
|
259
|
+
// check if the value is ${}
|
|
260
|
+
if (value.startsWith("${") && value.endsWith("}")) {
|
|
261
|
+
value = evaluateString(value, context.examplesRow);
|
|
160
262
|
}
|
|
161
263
|
return value;
|
|
162
264
|
}
|
|
265
|
+
function replaceTestDataValue(env, key, testData, decryptValue = true) {
|
|
266
|
+
const path = key.split(".");
|
|
267
|
+
const value = objectPath.get(testData, path);
|
|
268
|
+
if (value && !Array.isArray(value)) {
|
|
269
|
+
return value;
|
|
270
|
+
}
|
|
271
|
+
const dataArray = testData[env];
|
|
272
|
+
if (!dataArray) {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
for (const obj of dataArray) {
|
|
276
|
+
if (obj.key !== key) {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
if (obj.DataType === "secret") {
|
|
280
|
+
if (decryptValue === true) {
|
|
281
|
+
return decrypt(`secret:${obj.value}`, null);
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
return `secret:${obj.value}`;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return obj.value;
|
|
288
|
+
}
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
function evaluateString(template, parameters) {
|
|
292
|
+
if (!parameters) {
|
|
293
|
+
parameters = {};
|
|
294
|
+
}
|
|
295
|
+
try {
|
|
296
|
+
return new Function(...Object.keys(parameters), `return \`${template}\`;`)(...Object.values(parameters));
|
|
297
|
+
}
|
|
298
|
+
catch (e) {
|
|
299
|
+
console.error(e);
|
|
300
|
+
return template;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
function formatDate(dateStr, format) {
|
|
304
|
+
if (!format) {
|
|
305
|
+
return dateStr;
|
|
306
|
+
}
|
|
307
|
+
// Split the input date string
|
|
308
|
+
const [dd, mm, yyyy] = dateStr.split("-");
|
|
309
|
+
// Define replacements
|
|
310
|
+
const replacements = {
|
|
311
|
+
dd: dd,
|
|
312
|
+
mm: mm,
|
|
313
|
+
yyyy: yyyy,
|
|
314
|
+
};
|
|
315
|
+
// Replace format placeholders with actual values
|
|
316
|
+
return format.replace(/dd|mm|yyyy/g, (match) => replacements[match]);
|
|
317
|
+
}
|
|
163
318
|
function maskValue(value) {
|
|
164
319
|
if (!value) {
|
|
165
320
|
return value;
|
|
@@ -248,6 +403,118 @@ function scanAndManipulate(currentObj, _params) {
|
|
|
248
403
|
}
|
|
249
404
|
}
|
|
250
405
|
}
|
|
406
|
+
function extractStepExampleParameters(step) {
|
|
407
|
+
if (!step ||
|
|
408
|
+
!step.gherkinDocument ||
|
|
409
|
+
!step.pickle ||
|
|
410
|
+
!step.pickle.astNodeIds ||
|
|
411
|
+
!(step.pickle.astNodeIds.length > 1) ||
|
|
412
|
+
!step.gherkinDocument.feature ||
|
|
413
|
+
!step.gherkinDocument.feature.children) {
|
|
414
|
+
return {};
|
|
415
|
+
}
|
|
416
|
+
try {
|
|
417
|
+
const scenarioId = step.pickle.astNodeIds[0];
|
|
418
|
+
const exampleId = step.pickle.astNodeIds[1];
|
|
419
|
+
// find the scenario in the gherkin document
|
|
420
|
+
const scenario = step.gherkinDocument.feature.children.find((child) => child.scenario.id === scenarioId).scenario;
|
|
421
|
+
if (!scenario || !scenario.examples || !scenario.examples[0].tableBody) {
|
|
422
|
+
return {};
|
|
423
|
+
}
|
|
424
|
+
// find the table body in the examples
|
|
425
|
+
const row = scenario.examples[0].tableBody.find((r) => r.id === exampleId);
|
|
426
|
+
if (!row) {
|
|
427
|
+
return {};
|
|
428
|
+
}
|
|
429
|
+
// extract the cells values (row.cells.value) into an array
|
|
430
|
+
const values = row.cells.map((cell) => cell.value);
|
|
431
|
+
// extract the table headers keys (scenario.examples.tableHeader.cells.value) into an array
|
|
432
|
+
const keys = scenario.examples[0].tableHeader.cells.map((cell) => cell.value);
|
|
433
|
+
// create a dictionary of the keys and values
|
|
434
|
+
const params = {};
|
|
435
|
+
for (let i = 0; i < keys.length; i++) {
|
|
436
|
+
params[keys[i]] = values[i];
|
|
437
|
+
}
|
|
438
|
+
return params;
|
|
439
|
+
}
|
|
440
|
+
catch (e) {
|
|
441
|
+
console.error(e);
|
|
442
|
+
return {};
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
export async function performAction(action, element, options, web, state, _params) {
|
|
446
|
+
let usedOptions;
|
|
447
|
+
if (!options) {
|
|
448
|
+
options = {};
|
|
449
|
+
}
|
|
450
|
+
if (!element) {
|
|
451
|
+
throw new Error("Element not found");
|
|
452
|
+
}
|
|
453
|
+
switch (action) {
|
|
454
|
+
case "click":
|
|
455
|
+
// copy any of the following options to usedOptions: button, clickCount, delay, modifiers, force, position, trial
|
|
456
|
+
usedOptions = ["button", "clickCount", "delay", "modifiers", "force", "position", "trial", "timeout"].reduce((acc, key) => {
|
|
457
|
+
if (options[key]) {
|
|
458
|
+
acc[key] = options[key];
|
|
459
|
+
}
|
|
460
|
+
return acc;
|
|
461
|
+
}, {});
|
|
462
|
+
if (!usedOptions.timeout) {
|
|
463
|
+
usedOptions.timeout = 10000;
|
|
464
|
+
if (usedOptions.position) {
|
|
465
|
+
usedOptions.timeout = 1000;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
try {
|
|
469
|
+
await element.click(usedOptions);
|
|
470
|
+
}
|
|
471
|
+
catch (e) {
|
|
472
|
+
if (usedOptions.position) {
|
|
473
|
+
// find the element bounding box
|
|
474
|
+
const rect = await element.boundingBox();
|
|
475
|
+
// calculate the x and y position
|
|
476
|
+
const x = rect.x + rect.width / 2 + (usedOptions.position.x || 0);
|
|
477
|
+
const y = rect.y + rect.height / 2 + (usedOptions.position.y || 0);
|
|
478
|
+
// click on the x and y position
|
|
479
|
+
await web.page.mouse.click(x, y);
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
if (state && state.selectors) {
|
|
483
|
+
state.element = await web._locate(state.selectors, state.info, _params);
|
|
484
|
+
element = state.element;
|
|
485
|
+
}
|
|
486
|
+
await element.dispatchEvent("click");
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
break;
|
|
490
|
+
case "hover":
|
|
491
|
+
usedOptions = ["position", "trial", "timeout"].reduce((acc, key) => {
|
|
492
|
+
acc[key] = options[key];
|
|
493
|
+
return acc;
|
|
494
|
+
}, {});
|
|
495
|
+
try {
|
|
496
|
+
await element.hover(usedOptions);
|
|
497
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
498
|
+
}
|
|
499
|
+
catch (e) {
|
|
500
|
+
if (state && state.selectors) {
|
|
501
|
+
state.info.log += "hover failed, will try again" + "\n";
|
|
502
|
+
state.element = await web._locate(state.selectors, state.info, _params);
|
|
503
|
+
element = state.element;
|
|
504
|
+
}
|
|
505
|
+
usedOptions.timeout = 10000;
|
|
506
|
+
await element.hover(usedOptions);
|
|
507
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
508
|
+
}
|
|
509
|
+
break;
|
|
510
|
+
case "hover+click":
|
|
511
|
+
await performAction("hover", element, options, web, state, _params);
|
|
512
|
+
await performAction("click", element, options, web, state, _params);
|
|
513
|
+
break;
|
|
514
|
+
default:
|
|
515
|
+
throw new Error(`Action ${action} not supported`);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
251
518
|
const KEYBOARD_EVENTS = [
|
|
252
519
|
"ALT",
|
|
253
520
|
"AltGraph",
|
|
@@ -407,7 +674,28 @@ function _getServerUrl() {
|
|
|
407
674
|
else if (process.env.NODE_ENV_BLINQ === "stage") {
|
|
408
675
|
serviceUrl = "https://stage.api.blinq.io";
|
|
409
676
|
}
|
|
677
|
+
else if (process.env.NODE_ENV_BLINQ === "prod") {
|
|
678
|
+
serviceUrl = "https://api.blinq.io";
|
|
679
|
+
}
|
|
680
|
+
else if (!process.env.NODE_ENV_BLINQ) {
|
|
681
|
+
serviceUrl = "https://api.blinq.io";
|
|
682
|
+
}
|
|
683
|
+
else {
|
|
684
|
+
serviceUrl = process.env.NODE_ENV_BLINQ;
|
|
685
|
+
}
|
|
410
686
|
return serviceUrl;
|
|
411
687
|
}
|
|
412
|
-
|
|
688
|
+
function tryParseJson(input) {
|
|
689
|
+
if (typeof input === "string") {
|
|
690
|
+
try {
|
|
691
|
+
return JSON.parse(input);
|
|
692
|
+
}
|
|
693
|
+
catch {
|
|
694
|
+
// If parsing fails, return the original input (assumed to be plain text or another format)
|
|
695
|
+
return input;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
return input;
|
|
699
|
+
}
|
|
700
|
+
export { encrypt, decrypt, replaceWithLocalTestData, maskValue, _copyContext, scrollPageToLoadLazyElements, _fixUsingParams, getWebLogFile, _fixLocatorUsingParams, _isObject, scanAndManipulate, KEYBOARD_EVENTS, unEscapeString, _getServerUrl, _convertToRegexQuery, extractStepExampleParameters, _getDataFile, tryParseJson, getTestDataValue, };
|
|
413
701
|
//# sourceMappingURL=utils.js.map
|