automation_model 1.0.430-dev → 1.0.430
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.d.ts +43 -1
- package/lib/api.js +241 -41
- package/lib/api.js.map +1 -1
- package/lib/auto_page.d.ts +5 -2
- package/lib/auto_page.js +177 -46
- package/lib/auto_page.js.map +1 -1
- package/lib/browser_manager.d.ts +7 -3
- package/lib/browser_manager.js +161 -49
- package/lib/browser_manager.js.map +1 -1
- package/lib/bruno.d.ts +1 -0
- package/lib/bruno.js +301 -0
- package/lib/bruno.js.map +1 -0
- package/lib/command_common.d.ts +6 -0
- package/lib/command_common.js +198 -0
- package/lib/command_common.js.map +1 -0
- package/lib/environment.d.ts +3 -0
- package/lib/environment.js +5 -2
- package/lib/environment.js.map +1 -1
- package/lib/error-messages.d.ts +6 -0
- package/lib/error-messages.js +206 -0
- package/lib/error-messages.js.map +1 -0
- 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 +2 -0
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -1
- package/lib/init_browser.d.ts +5 -2
- package/lib/init_browser.js +124 -7
- package/lib/init_browser.js.map +1 -1
- package/lib/locate_element.d.ts +7 -0
- package/lib/locate_element.js +215 -0
- package/lib/locate_element.js.map +1 -0
- 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.d.ts +3 -0
- package/lib/network.js +183 -0
- package/lib/network.js.map +1 -0
- package/lib/scripts/axe.mini.js +12 -0
- package/lib/stable_browser.d.ts +104 -36
- package/lib/stable_browser.js +1802 -1240
- 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/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 +6 -0
- package/lib/test_context.js +14 -10
- package/lib/test_context.js.map +1 -1
- package/lib/utils.d.ts +22 -2
- package/lib/utils.js +664 -11
- package/lib/utils.js.map +1 -1
- package/package.json +14 -8
package/lib/utils.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import CryptoJS from "crypto-js";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { TOTP } from "totp-generator";
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import axios from "axios";
|
|
6
|
+
import objectPath from "object-path";
|
|
4
7
|
// Function to encrypt a string
|
|
5
8
|
function encrypt(text, key = null) {
|
|
6
9
|
if (!key) {
|
|
@@ -8,8 +11,29 @@ function encrypt(text, key = null) {
|
|
|
8
11
|
}
|
|
9
12
|
return CryptoJS.AES.encrypt(text, key).toString();
|
|
10
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
|
+
}
|
|
11
35
|
// Function to decrypt a string
|
|
12
|
-
|
|
36
|
+
function decrypt(encryptedText, key = null, totpWait = true) {
|
|
13
37
|
if (!key) {
|
|
14
38
|
key = _findKey();
|
|
15
39
|
}
|
|
@@ -21,18 +45,101 @@ async function decrypt(encryptedText, key = null, totpWait = true) {
|
|
|
21
45
|
const bytes = CryptoJS.AES.decrypt(encryptedText, key);
|
|
22
46
|
encryptedText = bytes.toString(CryptoJS.enc.Utf8);
|
|
23
47
|
let { otp, expires } = TOTP.generate(encryptedText);
|
|
24
|
-
if (totpWait) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
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
|
+
// }
|
|
31
55
|
return otp;
|
|
32
56
|
}
|
|
57
|
+
if (encryptedText.startsWith("mask:")) {
|
|
58
|
+
return encryptedText.substring(5);
|
|
59
|
+
}
|
|
33
60
|
const bytes = CryptoJS.AES.decrypt(encryptedText, key);
|
|
34
61
|
return bytes.toString(CryptoJS.enc.Utf8);
|
|
35
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
|
+
}
|
|
79
|
+
function _convertToRegexQuery(text, isRegex, fullMatch, ignoreCase) {
|
|
80
|
+
let query = "internal:text=/";
|
|
81
|
+
let queryEnd = "/";
|
|
82
|
+
let pattern = "";
|
|
83
|
+
const regexEndPattern = /\/([gimuy]*)$/;
|
|
84
|
+
if (text.startsWith("/")) {
|
|
85
|
+
const match = regexEndPattern.test(text);
|
|
86
|
+
if (match) {
|
|
87
|
+
try {
|
|
88
|
+
const regex = new RegExp(text.substring(1, text.lastIndexOf("/")), text.match(regexEndPattern)[1]);
|
|
89
|
+
text = text.replace(/"/g, '\\"');
|
|
90
|
+
return "internal:text=" + text;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// not regex
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (isRegex) {
|
|
98
|
+
pattern = text;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
// first remove \n then split the text by any white space,
|
|
102
|
+
let parts = text.replace(/\\n/g, "").split(/\s+/);
|
|
103
|
+
// escape regex split part
|
|
104
|
+
parts = parts.map((part) => escapeRegex(part));
|
|
105
|
+
pattern = parts.join("\\s*");
|
|
106
|
+
}
|
|
107
|
+
if (fullMatch) {
|
|
108
|
+
pattern = "^\\s*" + pattern + "\\s*$";
|
|
109
|
+
}
|
|
110
|
+
if (ignoreCase) {
|
|
111
|
+
queryEnd += "i";
|
|
112
|
+
}
|
|
113
|
+
return query + pattern + queryEnd;
|
|
114
|
+
}
|
|
115
|
+
function escapeRegex(str) {
|
|
116
|
+
// Special regex characters that need to be escaped
|
|
117
|
+
const specialChars = [
|
|
118
|
+
"/",
|
|
119
|
+
".",
|
|
120
|
+
"*",
|
|
121
|
+
"+",
|
|
122
|
+
"?",
|
|
123
|
+
"^",
|
|
124
|
+
"$",
|
|
125
|
+
"(",
|
|
126
|
+
")",
|
|
127
|
+
"[",
|
|
128
|
+
"]",
|
|
129
|
+
"{",
|
|
130
|
+
"}",
|
|
131
|
+
"|",
|
|
132
|
+
"\\",
|
|
133
|
+
"-",
|
|
134
|
+
"'",
|
|
135
|
+
'"',
|
|
136
|
+
">", // added to avoid confusion with pw selectorsxw
|
|
137
|
+
];
|
|
138
|
+
// Create a regex that will match all special characters
|
|
139
|
+
const escapedRegex = new RegExp(specialChars.map((char) => `\\${char}`).join("|"), "g");
|
|
140
|
+
// Escape special characters by prefixing them with a backslash
|
|
141
|
+
return str.replace(escapedRegex, "\\$&");
|
|
142
|
+
}
|
|
36
143
|
function _findKey() {
|
|
37
144
|
if (process.env.PROJECT_ID) {
|
|
38
145
|
return process.env.PROJECT_ID;
|
|
@@ -45,7 +152,553 @@ function _findKey() {
|
|
|
45
152
|
// extract the base folder name
|
|
46
153
|
return path.basename(folder);
|
|
47
154
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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) {
|
|
172
|
+
let dataFile = null;
|
|
173
|
+
if (world && world.reportFolder) {
|
|
174
|
+
dataFile = path.join(world.reportFolder, "data.json");
|
|
175
|
+
}
|
|
176
|
+
else if (web && web.reportFolder) {
|
|
177
|
+
dataFile = path.join(web.reportFolder, "data.json");
|
|
178
|
+
}
|
|
179
|
+
else if (context && context.reportFolder) {
|
|
180
|
+
dataFile = path.join(context.reportFolder, "data.json");
|
|
181
|
+
}
|
|
182
|
+
else if (fs.existsSync(path.join("data", "data.json"))) {
|
|
183
|
+
dataFile = path.join("data", "data.json");
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
dataFile = "data.json";
|
|
187
|
+
}
|
|
188
|
+
return dataFile;
|
|
189
|
+
}
|
|
190
|
+
function _getTestData(world = null, context = null, web = null) {
|
|
191
|
+
const dataFile = _getDataFile(world, context, web);
|
|
192
|
+
let data = {};
|
|
193
|
+
if (fs.existsSync(dataFile)) {
|
|
194
|
+
data = JSON.parse(fs.readFileSync(dataFile, "utf8"));
|
|
195
|
+
}
|
|
196
|
+
return data;
|
|
197
|
+
}
|
|
198
|
+
async function replaceWithLocalTestData(value, world, _decrypt = true, totpWait = true, context = null, web = null) {
|
|
199
|
+
if (!value) {
|
|
200
|
+
return value;
|
|
201
|
+
}
|
|
202
|
+
let env = "";
|
|
203
|
+
if (context && context.environment) {
|
|
204
|
+
env = context.environment.name;
|
|
205
|
+
}
|
|
206
|
+
// find all the accurance of {{(.*?)}} and replace with the value
|
|
207
|
+
let regex = /{{(.*?)}}/g;
|
|
208
|
+
let matches = value.match(regex);
|
|
209
|
+
if (matches) {
|
|
210
|
+
const testData = _getTestData(world, context, web);
|
|
211
|
+
for (let i = 0; i < matches.length; i++) {
|
|
212
|
+
let match = matches[i];
|
|
213
|
+
let key = match.substring(2, match.length - 2);
|
|
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
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if ((value.startsWith("secret:") || value.startsWith("totp:") || value.startsWith("mask:")) && _decrypt) {
|
|
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);
|
|
262
|
+
}
|
|
263
|
+
return value;
|
|
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
|
+
if (obj.DataType === "totp") {
|
|
288
|
+
return `totp:${obj.value}`;
|
|
289
|
+
}
|
|
290
|
+
return obj.value;
|
|
291
|
+
}
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
function evaluateString(template, parameters) {
|
|
295
|
+
if (!parameters) {
|
|
296
|
+
parameters = {};
|
|
297
|
+
}
|
|
298
|
+
try {
|
|
299
|
+
return new Function(...Object.keys(parameters), `return \`${template}\`;`)(...Object.values(parameters));
|
|
300
|
+
}
|
|
301
|
+
catch (e) {
|
|
302
|
+
console.error(e);
|
|
303
|
+
return template;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function formatDate(dateStr, format) {
|
|
307
|
+
if (!format) {
|
|
308
|
+
return dateStr;
|
|
309
|
+
}
|
|
310
|
+
// Split the input date string
|
|
311
|
+
const [dd, mm, yyyy] = dateStr.split("-");
|
|
312
|
+
// Define replacements
|
|
313
|
+
const replacements = {
|
|
314
|
+
dd: dd,
|
|
315
|
+
mm: mm,
|
|
316
|
+
yyyy: yyyy,
|
|
317
|
+
};
|
|
318
|
+
// Replace format placeholders with actual values
|
|
319
|
+
return format.replace(/dd|mm|yyyy/g, (match) => replacements[match]);
|
|
320
|
+
}
|
|
321
|
+
function maskValue(value) {
|
|
322
|
+
if (!value) {
|
|
323
|
+
return value;
|
|
324
|
+
}
|
|
325
|
+
if (value.startsWith("secret:")) {
|
|
326
|
+
return "secret:****";
|
|
327
|
+
}
|
|
328
|
+
if (value.startsWith("totp:")) {
|
|
329
|
+
return "totp:****";
|
|
330
|
+
}
|
|
331
|
+
if (value.startsWith("mask:")) {
|
|
332
|
+
return "mask:****";
|
|
333
|
+
}
|
|
334
|
+
return value;
|
|
335
|
+
}
|
|
336
|
+
function _copyContext(from, to) {
|
|
337
|
+
to.browser = from.browser;
|
|
338
|
+
to.page = from.page;
|
|
339
|
+
to.context = from.context;
|
|
340
|
+
}
|
|
341
|
+
async function scrollPageToLoadLazyElements(page) {
|
|
342
|
+
let lastHeight = await page.evaluate(() => document.body.scrollHeight);
|
|
343
|
+
let retry = 0;
|
|
344
|
+
while (true) {
|
|
345
|
+
await page.evaluate(() => window.scrollBy(0, window.innerHeight));
|
|
346
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
347
|
+
let newHeight = await page.evaluate(() => document.body.scrollHeight);
|
|
348
|
+
if (newHeight === lastHeight) {
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
lastHeight = newHeight;
|
|
352
|
+
retry++;
|
|
353
|
+
if (retry > 10) {
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
await page.evaluate(() => window.scrollTo(0, 0));
|
|
358
|
+
}
|
|
359
|
+
function _fixUsingParams(text, _params) {
|
|
360
|
+
if (!_params || typeof text !== "string") {
|
|
361
|
+
return text;
|
|
362
|
+
}
|
|
363
|
+
for (let key in _params) {
|
|
364
|
+
let regValue = key;
|
|
365
|
+
if (key.startsWith("_")) {
|
|
366
|
+
// remove the _ prefix
|
|
367
|
+
regValue = key.substring(1);
|
|
368
|
+
}
|
|
369
|
+
text = text.replaceAll(new RegExp("{" + regValue + "}", "g"), _params[key]);
|
|
370
|
+
}
|
|
371
|
+
return text;
|
|
372
|
+
}
|
|
373
|
+
function getWebLogFile(logFolder) {
|
|
374
|
+
if (!fs.existsSync(logFolder)) {
|
|
375
|
+
fs.mkdirSync(logFolder, { recursive: true });
|
|
376
|
+
}
|
|
377
|
+
let nextIndex = 1;
|
|
378
|
+
while (fs.existsSync(path.join(logFolder, nextIndex.toString() + ".json"))) {
|
|
379
|
+
nextIndex++;
|
|
380
|
+
}
|
|
381
|
+
const fileName = nextIndex + ".json";
|
|
382
|
+
return path.join(logFolder, fileName);
|
|
383
|
+
}
|
|
384
|
+
function _fixLocatorUsingParams(locator, _params) {
|
|
385
|
+
// check if not null
|
|
386
|
+
if (!locator) {
|
|
387
|
+
return locator;
|
|
388
|
+
}
|
|
389
|
+
// clone the locator
|
|
390
|
+
locator = JSON.parse(JSON.stringify(locator));
|
|
391
|
+
scanAndManipulate(locator, _params);
|
|
392
|
+
return locator;
|
|
393
|
+
}
|
|
394
|
+
function _isObject(value) {
|
|
395
|
+
return value && typeof value === "object" && value.constructor === Object;
|
|
396
|
+
}
|
|
397
|
+
function scanAndManipulate(currentObj, _params) {
|
|
398
|
+
for (const key in currentObj) {
|
|
399
|
+
if (typeof currentObj[key] === "string") {
|
|
400
|
+
// Perform string manipulation
|
|
401
|
+
currentObj[key] = _fixUsingParams(currentObj[key], _params);
|
|
402
|
+
}
|
|
403
|
+
else if (_isObject(currentObj[key])) {
|
|
404
|
+
// Recursively scan nested objects
|
|
405
|
+
scanAndManipulate(currentObj[key], _params);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
function extractStepExampleParameters(step) {
|
|
410
|
+
if (!step ||
|
|
411
|
+
!step.gherkinDocument ||
|
|
412
|
+
!step.pickle ||
|
|
413
|
+
!step.pickle.astNodeIds ||
|
|
414
|
+
!(step.pickle.astNodeIds.length > 1) ||
|
|
415
|
+
!step.gherkinDocument.feature ||
|
|
416
|
+
!step.gherkinDocument.feature.children) {
|
|
417
|
+
return {};
|
|
418
|
+
}
|
|
419
|
+
try {
|
|
420
|
+
const scenarioId = step.pickle.astNodeIds[0];
|
|
421
|
+
const exampleId = step.pickle.astNodeIds[1];
|
|
422
|
+
// find the scenario in the gherkin document
|
|
423
|
+
const scenario = step.gherkinDocument.feature.children.find((child) => child.scenario.id === scenarioId).scenario;
|
|
424
|
+
if (!scenario || !scenario.examples || !scenario.examples[0].tableBody) {
|
|
425
|
+
return {};
|
|
426
|
+
}
|
|
427
|
+
// find the table body in the examples
|
|
428
|
+
const row = scenario.examples[0].tableBody.find((r) => r.id === exampleId);
|
|
429
|
+
if (!row) {
|
|
430
|
+
return {};
|
|
431
|
+
}
|
|
432
|
+
// extract the cells values (row.cells.value) into an array
|
|
433
|
+
const values = row.cells.map((cell) => cell.value);
|
|
434
|
+
// extract the table headers keys (scenario.examples.tableHeader.cells.value) into an array
|
|
435
|
+
const keys = scenario.examples[0].tableHeader.cells.map((cell) => cell.value);
|
|
436
|
+
// create a dictionary of the keys and values
|
|
437
|
+
const params = {};
|
|
438
|
+
for (let i = 0; i < keys.length; i++) {
|
|
439
|
+
params[keys[i]] = values[i];
|
|
440
|
+
}
|
|
441
|
+
return params;
|
|
442
|
+
}
|
|
443
|
+
catch (e) {
|
|
444
|
+
console.error(e);
|
|
445
|
+
return {};
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
export async function performAction(action, element, options, web, state, _params) {
|
|
449
|
+
let usedOptions;
|
|
450
|
+
if (!options) {
|
|
451
|
+
options = {};
|
|
452
|
+
}
|
|
453
|
+
if (!element) {
|
|
454
|
+
throw new Error("Element not found");
|
|
455
|
+
}
|
|
456
|
+
switch (action) {
|
|
457
|
+
case "click":
|
|
458
|
+
// copy any of the following options to usedOptions: button, clickCount, delay, modifiers, force, position, trial
|
|
459
|
+
usedOptions = ["button", "clickCount", "delay", "modifiers", "force", "position", "trial", "timeout"].reduce((acc, key) => {
|
|
460
|
+
if (options[key]) {
|
|
461
|
+
acc[key] = options[key];
|
|
462
|
+
}
|
|
463
|
+
return acc;
|
|
464
|
+
}, {});
|
|
465
|
+
if (!usedOptions.timeout) {
|
|
466
|
+
usedOptions.timeout = 10000;
|
|
467
|
+
if (usedOptions.position) {
|
|
468
|
+
usedOptions.timeout = 1000;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
try {
|
|
472
|
+
await element.click(usedOptions);
|
|
473
|
+
}
|
|
474
|
+
catch (e) {
|
|
475
|
+
if (usedOptions.position) {
|
|
476
|
+
// find the element bounding box
|
|
477
|
+
const rect = await element.boundingBox();
|
|
478
|
+
// calculate the x and y position
|
|
479
|
+
const x = rect.x + rect.width / 2 + (usedOptions.position.x || 0);
|
|
480
|
+
const y = rect.y + rect.height / 2 + (usedOptions.position.y || 0);
|
|
481
|
+
// click on the x and y position
|
|
482
|
+
await web.page.mouse.click(x, y);
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
if (state && state.selectors) {
|
|
486
|
+
state.element = await web._locate(state.selectors, state.info, _params);
|
|
487
|
+
element = state.element;
|
|
488
|
+
}
|
|
489
|
+
await element.dispatchEvent("click");
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
break;
|
|
493
|
+
case "hover":
|
|
494
|
+
usedOptions = ["position", "trial", "timeout"].reduce((acc, key) => {
|
|
495
|
+
acc[key] = options[key];
|
|
496
|
+
return acc;
|
|
497
|
+
}, {});
|
|
498
|
+
try {
|
|
499
|
+
await element.hover(usedOptions);
|
|
500
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
501
|
+
}
|
|
502
|
+
catch (e) {
|
|
503
|
+
if (state && state.selectors) {
|
|
504
|
+
state.info.log += "hover failed, will try again" + "\n";
|
|
505
|
+
state.element = await web._locate(state.selectors, state.info, _params);
|
|
506
|
+
element = state.element;
|
|
507
|
+
}
|
|
508
|
+
usedOptions.timeout = 10000;
|
|
509
|
+
await element.hover(usedOptions);
|
|
510
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
511
|
+
}
|
|
512
|
+
break;
|
|
513
|
+
case "hover+click":
|
|
514
|
+
await performAction("hover", element, options, web, state, _params);
|
|
515
|
+
await performAction("click", element, options, web, state, _params);
|
|
516
|
+
break;
|
|
517
|
+
default:
|
|
518
|
+
throw new Error(`Action ${action} not supported`);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
const KEYBOARD_EVENTS = [
|
|
522
|
+
"ALT",
|
|
523
|
+
"AltGraph",
|
|
524
|
+
"CapsLock",
|
|
525
|
+
"Control",
|
|
526
|
+
"Fn",
|
|
527
|
+
"FnLock",
|
|
528
|
+
"Hyper",
|
|
529
|
+
"Meta",
|
|
530
|
+
"NumLock",
|
|
531
|
+
"ScrollLock",
|
|
532
|
+
"Shift",
|
|
533
|
+
"Super",
|
|
534
|
+
"Symbol",
|
|
535
|
+
"SymbolLock",
|
|
536
|
+
"Enter",
|
|
537
|
+
"Tab",
|
|
538
|
+
"ArrowDown",
|
|
539
|
+
"ArrowLeft",
|
|
540
|
+
"ArrowRight",
|
|
541
|
+
"ArrowUp",
|
|
542
|
+
"End",
|
|
543
|
+
"Home",
|
|
544
|
+
"PageDown",
|
|
545
|
+
"PageUp",
|
|
546
|
+
"Backspace",
|
|
547
|
+
"Clear",
|
|
548
|
+
"Copy",
|
|
549
|
+
"CrSel",
|
|
550
|
+
"Cut",
|
|
551
|
+
"Delete",
|
|
552
|
+
"EraseEof",
|
|
553
|
+
"ExSel",
|
|
554
|
+
"Insert",
|
|
555
|
+
"Paste",
|
|
556
|
+
"Redo",
|
|
557
|
+
"Undo",
|
|
558
|
+
"Accept",
|
|
559
|
+
"Again",
|
|
560
|
+
"Attn",
|
|
561
|
+
"Cancel",
|
|
562
|
+
"ContextMenu",
|
|
563
|
+
"Escape",
|
|
564
|
+
"Execute",
|
|
565
|
+
"Find",
|
|
566
|
+
"Finish",
|
|
567
|
+
"Help",
|
|
568
|
+
"Pause",
|
|
569
|
+
"Play",
|
|
570
|
+
"Props",
|
|
571
|
+
"Select",
|
|
572
|
+
"ZoomIn",
|
|
573
|
+
"ZoomOut",
|
|
574
|
+
"BrightnessDown",
|
|
575
|
+
"BrightnessUp",
|
|
576
|
+
"Eject",
|
|
577
|
+
"LogOff",
|
|
578
|
+
"Power",
|
|
579
|
+
"PowerOff",
|
|
580
|
+
"PrintScreen",
|
|
581
|
+
"Hibernate",
|
|
582
|
+
"Standby",
|
|
583
|
+
"WakeUp",
|
|
584
|
+
"AllCandidates",
|
|
585
|
+
"Alphanumeric",
|
|
586
|
+
"CodeInput",
|
|
587
|
+
"Compose",
|
|
588
|
+
"Convert",
|
|
589
|
+
"Dead",
|
|
590
|
+
"FinalMode",
|
|
591
|
+
"GroupFirst",
|
|
592
|
+
"GroupLast",
|
|
593
|
+
"GroupNext",
|
|
594
|
+
"GroupPrevious",
|
|
595
|
+
"ModeChange",
|
|
596
|
+
"NextCandidate",
|
|
597
|
+
"NonConvert",
|
|
598
|
+
"PreviousCandidate",
|
|
599
|
+
"Process",
|
|
600
|
+
"SingleCandidate",
|
|
601
|
+
"HangulMode",
|
|
602
|
+
"HanjaMode",
|
|
603
|
+
"JunjaMode",
|
|
604
|
+
"Eisu",
|
|
605
|
+
"Hankaku",
|
|
606
|
+
"Hiragana",
|
|
607
|
+
"HiraganaKatakana",
|
|
608
|
+
"KanaMode",
|
|
609
|
+
"KanjiMode",
|
|
610
|
+
"Katakana",
|
|
611
|
+
"Romaji",
|
|
612
|
+
"Zenkaku",
|
|
613
|
+
"ZenkakuHanaku",
|
|
614
|
+
"F1",
|
|
615
|
+
"F2",
|
|
616
|
+
"F3",
|
|
617
|
+
"F4",
|
|
618
|
+
"F5",
|
|
619
|
+
"F6",
|
|
620
|
+
"F7",
|
|
621
|
+
"F8",
|
|
622
|
+
"F9",
|
|
623
|
+
"F10",
|
|
624
|
+
"F11",
|
|
625
|
+
"F12",
|
|
626
|
+
"Soft1",
|
|
627
|
+
"Soft2",
|
|
628
|
+
"Soft3",
|
|
629
|
+
"Soft4",
|
|
630
|
+
"ChannelDown",
|
|
631
|
+
"ChannelUp",
|
|
632
|
+
"Close",
|
|
633
|
+
"MailForward",
|
|
634
|
+
"MailReply",
|
|
635
|
+
"MailSend",
|
|
636
|
+
"MediaFastForward",
|
|
637
|
+
"MediaPause",
|
|
638
|
+
"MediaPlay",
|
|
639
|
+
"MediaPlayPause",
|
|
640
|
+
"MediaRecord",
|
|
641
|
+
"MediaRewind",
|
|
642
|
+
"MediaStop",
|
|
643
|
+
"MediaTrackNext",
|
|
644
|
+
"MediaTrackPrevious",
|
|
645
|
+
"AudioBalanceLeft",
|
|
646
|
+
"AudioBalanceRight",
|
|
647
|
+
"AudioBassBoostDown",
|
|
648
|
+
"AudioBassBoostToggle",
|
|
649
|
+
"AudioBassBoostUp",
|
|
650
|
+
"AudioFaderFront",
|
|
651
|
+
"AudioFaderRear",
|
|
652
|
+
"AudioSurroundModeNext",
|
|
653
|
+
"AudioTrebleDown",
|
|
654
|
+
"AudioTrebleUp",
|
|
655
|
+
"AudioVolumeDown",
|
|
656
|
+
"AudioVolumeMute",
|
|
657
|
+
"AudioVolumeUp",
|
|
658
|
+
"MicrophoneToggle",
|
|
659
|
+
"MicrophoneVolumeDown",
|
|
660
|
+
"MicrophoneVolumeMute",
|
|
661
|
+
"MicrophoneVolumeUp",
|
|
662
|
+
"TV",
|
|
663
|
+
"TV3DMode",
|
|
664
|
+
"TVAntennaCable",
|
|
665
|
+
"TVAudioDescription",
|
|
666
|
+
];
|
|
667
|
+
function unEscapeString(str) {
|
|
668
|
+
const placeholder = "__NEWLINE__";
|
|
669
|
+
str = str.replace(new RegExp(placeholder, "g"), "\n");
|
|
670
|
+
return str;
|
|
671
|
+
}
|
|
672
|
+
function _getServerUrl() {
|
|
673
|
+
let serviceUrl = "https://api.blinq.io";
|
|
674
|
+
if (process.env.NODE_ENV_BLINQ === "dev") {
|
|
675
|
+
serviceUrl = "https://dev.api.blinq.io";
|
|
676
|
+
}
|
|
677
|
+
else if (process.env.NODE_ENV_BLINQ === "stage") {
|
|
678
|
+
serviceUrl = "https://stage.api.blinq.io";
|
|
679
|
+
}
|
|
680
|
+
else if (process.env.NODE_ENV_BLINQ === "prod") {
|
|
681
|
+
serviceUrl = "https://api.blinq.io";
|
|
682
|
+
}
|
|
683
|
+
else if (!process.env.NODE_ENV_BLINQ) {
|
|
684
|
+
serviceUrl = "https://api.blinq.io";
|
|
685
|
+
}
|
|
686
|
+
else {
|
|
687
|
+
serviceUrl = process.env.NODE_ENV_BLINQ;
|
|
688
|
+
}
|
|
689
|
+
return serviceUrl;
|
|
690
|
+
}
|
|
691
|
+
function tryParseJson(input) {
|
|
692
|
+
if (typeof input === "string") {
|
|
693
|
+
try {
|
|
694
|
+
return JSON.parse(input);
|
|
695
|
+
}
|
|
696
|
+
catch {
|
|
697
|
+
// If parsing fails, return the original input (assumed to be plain text or another format)
|
|
698
|
+
return input;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return input;
|
|
702
|
+
}
|
|
703
|
+
export { encrypt, decrypt, replaceWithLocalTestData, maskValue, _copyContext, scrollPageToLoadLazyElements, _fixUsingParams, getWebLogFile, _fixLocatorUsingParams, _isObject, scanAndManipulate, KEYBOARD_EVENTS, unEscapeString, _getServerUrl, _convertToRegexQuery, extractStepExampleParameters, _getDataFile, tryParseJson, getTestDataValue, };
|
|
51
704
|
//# sourceMappingURL=utils.js.map
|