automation_model 1.0.495-dev → 1.0.495

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.
Files changed (77) hide show
  1. package/README.md +133 -0
  2. package/lib/analyze_helper.js.map +1 -1
  3. package/lib/api.d.ts +2 -2
  4. package/lib/api.js +151 -120
  5. package/lib/api.js.map +1 -1
  6. package/lib/auto_page.d.ts +7 -2
  7. package/lib/auto_page.js +297 -17
  8. package/lib/auto_page.js.map +1 -1
  9. package/lib/browser_manager.d.ts +6 -3
  10. package/lib/browser_manager.js +222 -52
  11. package/lib/browser_manager.js.map +1 -1
  12. package/lib/bruno.d.ts +2 -0
  13. package/lib/bruno.js +381 -0
  14. package/lib/bruno.js.map +1 -0
  15. package/lib/check_performance.d.ts +1 -0
  16. package/lib/check_performance.js +57 -0
  17. package/lib/check_performance.js.map +1 -0
  18. package/lib/command_common.d.ts +5 -4
  19. package/lib/command_common.js +126 -22
  20. package/lib/command_common.js.map +1 -1
  21. package/lib/date_time.js.map +1 -1
  22. package/lib/drawRect.js.map +1 -1
  23. package/lib/environment.d.ts +1 -0
  24. package/lib/environment.js +1 -0
  25. package/lib/environment.js.map +1 -1
  26. package/lib/error-messages.d.ts +6 -0
  27. package/lib/error-messages.js +206 -0
  28. package/lib/error-messages.js.map +1 -0
  29. package/lib/file_checker.d.ts +1 -0
  30. package/lib/file_checker.js +172 -0
  31. package/lib/file_checker.js.map +1 -0
  32. package/lib/find_function.js.map +1 -1
  33. package/lib/generation_scripts.d.ts +4 -0
  34. package/lib/generation_scripts.js +2 -0
  35. package/lib/generation_scripts.js.map +1 -0
  36. package/lib/index.d.ts +3 -0
  37. package/lib/index.js +4 -0
  38. package/lib/index.js.map +1 -1
  39. package/lib/init_browser.d.ts +4 -3
  40. package/lib/init_browser.js +160 -90
  41. package/lib/init_browser.js.map +1 -1
  42. package/lib/locate_element.js +16 -14
  43. package/lib/locate_element.js.map +1 -1
  44. package/lib/locator.d.ts +37 -0
  45. package/lib/locator.js +172 -0
  46. package/lib/locator.js.map +1 -1
  47. package/lib/locator_log.d.ts +26 -0
  48. package/lib/locator_log.js +69 -0
  49. package/lib/locator_log.js.map +1 -0
  50. package/lib/network.d.ts +4 -2
  51. package/lib/network.js +435 -69
  52. package/lib/network.js.map +1 -1
  53. package/lib/route.d.ts +83 -0
  54. package/lib/route.js +691 -0
  55. package/lib/route.js.map +1 -0
  56. package/lib/scripts/axe.mini.js +24005 -0
  57. package/lib/snapshot_validation.d.ts +37 -0
  58. package/lib/snapshot_validation.js +360 -0
  59. package/lib/snapshot_validation.js.map +1 -0
  60. package/lib/stable_browser.d.ts +147 -47
  61. package/lib/stable_browser.js +2602 -819
  62. package/lib/stable_browser.js.map +1 -1
  63. package/lib/table.d.ts +15 -0
  64. package/lib/table.js +257 -0
  65. package/lib/table.js.map +1 -0
  66. package/lib/table_analyze.js.map +1 -1
  67. package/lib/table_helper.d.ts +19 -0
  68. package/lib/table_helper.js +130 -0
  69. package/lib/table_helper.js.map +1 -0
  70. package/lib/test_context.d.ts +6 -0
  71. package/lib/test_context.js +5 -0
  72. package/lib/test_context.js.map +1 -1
  73. package/lib/utils.d.ts +38 -3
  74. package/lib/utils.js +747 -35
  75. package/lib/utils.js.map +1 -1
  76. package/package.json +31 -13
  77. package/lib/axe/axe.mini.js +0 -12
package/lib/utils.js CHANGED
@@ -1,17 +1,62 @@
1
+ import { check_performance } from "./check_performance.js";
1
2
  import CryptoJS from "crypto-js";
2
- import objectPath from "object-path";
3
3
  import path from "path";
4
4
  import { TOTP } from "totp-generator";
5
5
  import fs from "fs";
6
- // Function to encrypt a string
6
+ import axios from "axios";
7
+ import objectPath from "object-path";
8
+ import { faker } from "@faker-js/faker/locale/en_US";
9
+ const measureAsync = async (name, fn) => {
10
+ const id = `${name}-${Math.random().toString(36).slice(2, 9)}`;
11
+ const startMark = `${id}-start`;
12
+ const endMark = `${id}-end`;
13
+ try {
14
+ performance.mark(startMark);
15
+ try {
16
+ return await fn();
17
+ }
18
+ finally {
19
+ performance.mark(endMark);
20
+ const measure = performance.measure(id, startMark, endMark);
21
+ console.log(`${name}: ${measure.duration.toFixed(3)}ms`);
22
+ performance.clearMarks(startMark);
23
+ performance.clearMarks(endMark);
24
+ performance.clearMeasures(id);
25
+ }
26
+ }
27
+ catch (error) {
28
+ if (!(error instanceof DOMException) || !error.message.includes("performance mark")) {
29
+ throw error;
30
+ }
31
+ }
32
+ };
7
33
  function encrypt(text, key = null) {
8
34
  if (!key) {
9
35
  key = _findKey();
10
36
  }
11
37
  return CryptoJS.AES.encrypt(text, key).toString();
12
38
  }
13
- // Function to decrypt a string
14
- async function decrypt(encryptedText, key = null, totpWait = true) {
39
+ function getTestDataValue(key, environment = "*") {
40
+ const blinqEnvPath = "data/data.json";
41
+ const envPath = path.resolve(process.cwd(), blinqEnvPath);
42
+ const envJson = JSON.parse(fs.readFileSync(envPath, "utf-8"));
43
+ const dataArray = envJson[environment];
44
+ const item = dataArray.find((item) => item.key === key);
45
+ if (!item && environment !== "*") {
46
+ return getTestDataValue(key, "*");
47
+ }
48
+ if (!item) {
49
+ throw new Error(`Key ${key} not found in data.json`);
50
+ }
51
+ if (item.DataType === "string") {
52
+ return item.value;
53
+ }
54
+ else if (item.DataType === "secret" || item.DataType === "totp") {
55
+ return decrypt(item.value, null, false);
56
+ }
57
+ throw new Error(`Unsupported data type for key ${key}`);
58
+ }
59
+ function decrypt(encryptedText, key = null, totpWait = true) {
15
60
  if (!key) {
16
61
  key = _findKey();
17
62
  }
@@ -23,13 +68,13 @@ async function decrypt(encryptedText, key = null, totpWait = true) {
23
68
  const bytes = CryptoJS.AES.decrypt(encryptedText, key);
24
69
  encryptedText = bytes.toString(CryptoJS.enc.Utf8);
25
70
  let { otp, expires } = TOTP.generate(encryptedText);
26
- if (totpWait) {
27
- // expires is in unix time, check if we have at least 10 seconds left, if it's less than wait for the expires time
28
- if (expires - Date.now() < 10000) {
29
- await new Promise((resolve) => setTimeout(resolve, (expires - Date.now() + 1000) % 30000));
30
- ({ otp, expires } = TOTP.generate(encryptedText));
31
- }
32
- }
71
+ // if (totpWait) {
72
+ // // expires is in unix time, check if we have at least 10 seconds left, if it's less than wait for the expires time
73
+ // if (expires - Date.now() < 10000) {
74
+ // await new Promise((resolve) => setTimeout(resolve, (expires - Date.now() + 1000) % 30000));
75
+ // ({ otp, expires } = TOTP.generate(encryptedText));
76
+ // }
77
+ // }
33
78
  return otp;
34
79
  }
35
80
  if (encryptedText.startsWith("mask:")) {
@@ -38,7 +83,90 @@ async function decrypt(encryptedText, key = null, totpWait = true) {
38
83
  const bytes = CryptoJS.AES.decrypt(encryptedText, key);
39
84
  return bytes.toString(CryptoJS.enc.Utf8);
40
85
  }
86
+ export function testForRegex(text) {
87
+ const regexEndPattern = /\/([gimuy]*)$/;
88
+ if (text.startsWith("/")) {
89
+ const match = regexEndPattern.test(text);
90
+ if (match) {
91
+ try {
92
+ const regex = new RegExp(text.substring(1, text.lastIndexOf("/")), text.match(regexEndPattern)[1]);
93
+ return true;
94
+ }
95
+ catch {
96
+ // not regex
97
+ }
98
+ }
99
+ }
100
+ return false;
101
+ }
102
+ function _convertToRegexQuery(text, isRegex, fullMatch, ignoreCase) {
103
+ let query = "internal:text=/";
104
+ let queryEnd = "/";
105
+ let pattern = "";
106
+ const regexEndPattern = /\/([gimuy]*)$/;
107
+ if (text.startsWith("/")) {
108
+ const match = regexEndPattern.test(text);
109
+ if (match) {
110
+ try {
111
+ const regex = new RegExp(text.substring(1, text.lastIndexOf("/")), text.match(regexEndPattern)[1]);
112
+ text = text.replace(/"/g, '\\"');
113
+ return "internal:text=" + text;
114
+ }
115
+ catch {
116
+ // not regex
117
+ }
118
+ }
119
+ }
120
+ if (isRegex) {
121
+ pattern = text;
122
+ }
123
+ else {
124
+ // first remove \n then split the text by any white space,
125
+ let parts = text.replace(/\\n/g, "").split(/\s+/);
126
+ // escape regex split part
127
+ parts = parts.map((part) => escapeRegex(part));
128
+ pattern = parts.join("\\s*");
129
+ }
130
+ if (fullMatch) {
131
+ pattern = "^\\s*" + pattern + "\\s*$";
132
+ }
133
+ if (ignoreCase) {
134
+ queryEnd += "i";
135
+ }
136
+ return query + pattern + queryEnd + " >> visible=true";
137
+ }
138
+ function escapeRegex(str) {
139
+ // Special regex characters that need to be escaped
140
+ const specialChars = [
141
+ "/",
142
+ ".",
143
+ "*",
144
+ "+",
145
+ "?",
146
+ "^",
147
+ "$",
148
+ "(",
149
+ ")",
150
+ "[",
151
+ "]",
152
+ "{",
153
+ "}",
154
+ "|",
155
+ "\\",
156
+ "-",
157
+ "'",
158
+ '"',
159
+ ">", // added to avoid confusion with pw selectorsxw
160
+ ];
161
+ // Create a regex that will match all special characters
162
+ const escapedRegex = new RegExp(specialChars.map((char) => `\\${char}`).join("|"), "g");
163
+ // Escape special characters by prefixing them with a backslash
164
+ return str.replace(escapedRegex, "\\$&");
165
+ }
41
166
  function _findKey() {
167
+ if (process.env.PROJECT_KEY) {
168
+ return process.env.PROJECT_KEY;
169
+ }
42
170
  if (process.env.PROJECT_ID) {
43
171
  return process.env.PROJECT_ID;
44
172
  }
@@ -50,53 +178,269 @@ function _findKey() {
50
178
  // extract the base folder name
51
179
  return path.basename(folder);
52
180
  }
53
- function _getDataFile(world = null, context = null, stable = null) {
181
+ function _getDataFile(world = null, context = null, web = null) {
182
+ let dataFile = null;
183
+ if (world && world.reportFolder) {
184
+ dataFile = path.join(world.reportFolder, "data.json");
185
+ }
186
+ else if (web && web.reportFolder) {
187
+ dataFile = path.join(web.reportFolder, "data.json");
188
+ }
189
+ else if (context && context.reportFolder) {
190
+ dataFile = path.join(context.reportFolder, "data.json");
191
+ }
192
+ else if (web && web.project_path) {
193
+ dataFile = path.join(web.project_path, "data", "data.json");
194
+ }
195
+ else {
196
+ dataFile = "data.json";
197
+ }
198
+ return dataFile;
199
+ }
200
+ function _getTestDataFile(world = null, context = null, web = null) {
54
201
  let dataFile = null;
55
202
  if (world && world.reportFolder) {
56
203
  dataFile = path.join(world.reportFolder, "data.json");
57
204
  }
58
- else if (stable && stable.reportFolder) {
59
- dataFile = path.join(stable.reportFolder, "data.json");
205
+ else if (web && web.reportFolder) {
206
+ dataFile = path.join(web.reportFolder, "data.json");
60
207
  }
61
208
  else if (context && context.reportFolder) {
62
209
  dataFile = path.join(context.reportFolder, "data.json");
63
210
  }
211
+ else if (fs.existsSync(path.join("data", "data.json"))) {
212
+ dataFile = path.join("data", "data.json");
213
+ }
64
214
  else {
65
215
  dataFile = "data.json";
66
216
  }
67
217
  return dataFile;
68
218
  }
69
- function _getTestData(world = null, context = null, stable = null) {
70
- const dataFile = _getDataFile(world, context, stable);
219
+ function _getTestData(world = null, context = null, web = null) {
220
+ const dataFile = _getDataFile(world, context, web);
71
221
  let data = {};
72
- if (fs.existsSync(dataFile)) {
73
- data = JSON.parse(fs.readFileSync(dataFile, "utf8"));
222
+ let fileContent = "";
223
+ try {
224
+ if (fs.existsSync(dataFile)) {
225
+ // convert \r\n to \n
226
+ fileContent = fs.readFileSync(dataFile, "utf8").replace(/\r\n/g, "\n");
227
+ data = JSON.parse(fileContent);
228
+ }
229
+ }
230
+ catch (error) {
231
+ console.error("🟥 Failed to read or parse test data file:", error, JSON.stringify({ dataFile, fileContent }, null, 2));
232
+ throw new Error("Failed to read or parse test data file: " + error?.message);
74
233
  }
75
234
  return data;
76
235
  }
77
- async function replaceWithLocalTestData(value, world, _decrypt = true, totpWait = true, context = null, stable = null) {
236
+ function getObjectDataPathFromKey(key) {
237
+ const keyParts = [];
238
+ let currentPart = "";
239
+ let state = "outside";
240
+ for (let i = 0; i < key.length; i++) {
241
+ const char = key[i];
242
+ switch (state) {
243
+ case "outside":
244
+ switch (char) {
245
+ case "'":
246
+ state = "inside";
247
+ currentPart += char;
248
+ break;
249
+ case ".":
250
+ if (currentPart) {
251
+ keyParts.push(currentPart);
252
+ currentPart = "";
253
+ }
254
+ break;
255
+ default:
256
+ currentPart += char;
257
+ break;
258
+ }
259
+ break;
260
+ case "inside":
261
+ switch (char) {
262
+ case "\\":
263
+ state = "escape";
264
+ break;
265
+ case "'":
266
+ state = "outside";
267
+ currentPart += char; // Keep quotes in the part
268
+ break;
269
+ default:
270
+ currentPart += char;
271
+ break;
272
+ }
273
+ break;
274
+ case "escape":
275
+ currentPart += char;
276
+ state = "inside";
277
+ break;
278
+ }
279
+ }
280
+ if (currentPart) {
281
+ keyParts.push(currentPart);
282
+ }
283
+ // Remove surrounding quotes from parts
284
+ for (let i = 0; i < keyParts.length; i++) {
285
+ if (keyParts[i].startsWith("'") && keyParts[i].endsWith("'")) {
286
+ keyParts[i] = keyParts[i].slice(1, -1).replace(/\\'/g, "'");
287
+ }
288
+ }
289
+ return keyParts;
290
+ }
291
+ async function replaceWithLocalTestData(value, world, _decrypt = true, totpWait = true, context = null, web = null, throwError = true) {
78
292
  if (!value) {
79
293
  return value;
80
294
  }
81
- // find all the accurance of {{(.*?)}} and replace with the value
82
- let regex = /{{(.*?)}}/g;
83
- let matches = value.match(regex);
84
- if (matches) {
85
- const testData = _getTestData(world, context, stable);
86
- for (let i = 0; i < matches.length; i++) {
87
- let match = matches[i];
88
- let key = match.substring(2, match.length - 2);
89
- let newValue = objectPath.get(testData, key, null);
90
- if (newValue !== null) {
91
- value = value.replace(match, newValue);
295
+ let env = context?.environment?.name || "";
296
+ // used for unit tests
297
+ let testData = context?._data_ ? context._data_ : _getTestData(world, context, web);
298
+ const resolveDatePlaceholder = async (key) => {
299
+ const dateQuery = key.substring(5);
300
+ const parts = dateQuery.split(">>");
301
+ const returnTemplate = parts[1] || null;
302
+ const serviceUrl = _getServerUrl();
303
+ const config = {
304
+ method: "post",
305
+ url: `${serviceUrl}/api/runs/find-date/find`,
306
+ headers: {
307
+ "x-source": "true",
308
+ "Content-Type": "application/json",
309
+ Authorization: `Bearer ${process.env.TOKEN}`,
310
+ },
311
+ data: JSON.stringify({ value: parts[0] }),
312
+ };
313
+ const result = await axios.request(config);
314
+ if (result.status !== 200 || !result.data?.status || !result.data.result) {
315
+ console.error("Failed to find date");
316
+ throw new Error("Failed to find date");
317
+ }
318
+ return formatDate(result.data.result, returnTemplate);
319
+ };
320
+ const resolvePlaceholder = async (key) => {
321
+ if (key.startsWith("date:")) {
322
+ return await resolveDatePlaceholder(key);
323
+ }
324
+ let resolved = replaceTestDataValue(env, key, testData, _decrypt);
325
+ if (resolved !== null)
326
+ return resolved;
327
+ resolved = replaceTestDataValue("*", key, testData, _decrypt);
328
+ if (resolved !== null)
329
+ return resolved;
330
+ if (throwError) {
331
+ throw new Error(`Parameter "{{${key}}}" is undefined in the test data`);
332
+ }
333
+ else {
334
+ // console.warn(`Parameter "{{${key}}}" is undefined in the test data`);
335
+ let templateForFaker = key;
336
+ const possibleTestDataMatches = key.match(/{{(.*?)}}/g);
337
+ for (const testDataMatch of possibleTestDataMatches || []) {
338
+ const testDataKey = testDataMatch.slice(2, -2);
339
+ // const path = testDataKey.split(".");
340
+ const path = getObjectDataPathFromKey(testDataKey);
341
+ let value = objectPath.get(testData, path);
342
+ if (value !== undefined) {
343
+ templateForFaker = templateForFaker.replace(testDataMatch, String(value));
344
+ }
345
+ }
346
+ try {
347
+ const fake = faker.helpers.fake(`{{${templateForFaker}}}`);
348
+ if (fake !== `{{${templateForFaker}}}`)
349
+ return fake;
350
+ else
351
+ throw new Error("No faker match for " + templateForFaker);
352
+ }
353
+ catch (e) {
354
+ console.error(e instanceof Error ? e.message : JSON.stringify(e));
355
+ return null;
92
356
  }
93
357
  }
94
- }
358
+ };
359
+ const recursiveReplace = async (input) => {
360
+ const regex = /{{([^{}]+)}}/g;
361
+ let result = input;
362
+ let match;
363
+ let anyChange = false;
364
+ while ((match = regex.exec(result)) !== null) {
365
+ const fullMatch = match[0];
366
+ const key = match[1].trim();
367
+ const replacement = await resolvePlaceholder(key);
368
+ if (replacement !== null) {
369
+ result = result.replace(fullMatch, replacement);
370
+ anyChange = true;
371
+ regex.lastIndex = 0; // reset index due to string mutation
372
+ }
373
+ }
374
+ // If replacements were made, repeat to resolve nested placeholders
375
+ return anyChange ? await recursiveReplace(result) : result;
376
+ };
377
+ value = await recursiveReplace(value);
378
+ // Only evaluate if value contains a JS expression
95
379
  if ((value.startsWith("secret:") || value.startsWith("totp:") || value.startsWith("mask:")) && _decrypt) {
96
- return await decrypt(value, null, totpWait);
380
+ return decrypt(value, null, totpWait);
381
+ }
382
+ if (value.startsWith("${") && value.endsWith("}")) {
383
+ value = evaluateString(value, context?.examplesRow);
97
384
  }
98
385
  return value;
99
386
  }
387
+ function replaceTestDataValue(env, key, testData, decryptValue = true) {
388
+ // const path = key.split(".");
389
+ const path = getObjectDataPathFromKey(key);
390
+ const value = objectPath.get(testData, path);
391
+ if (value && !Array.isArray(value)) {
392
+ return value;
393
+ }
394
+ const dataArray = testData[env];
395
+ if (!dataArray) {
396
+ return null;
397
+ }
398
+ for (const obj of dataArray) {
399
+ if (obj.key !== key) {
400
+ continue;
401
+ }
402
+ if (obj.DataType === "secret") {
403
+ if (decryptValue === true) {
404
+ return decrypt(`secret:${obj.value}`, null);
405
+ }
406
+ else {
407
+ return `secret:${obj.value}`;
408
+ }
409
+ }
410
+ if (obj.DataType === "totp") {
411
+ return `totp:${obj.value}`;
412
+ }
413
+ return obj.value;
414
+ }
415
+ return null;
416
+ }
417
+ function evaluateString(template, parameters) {
418
+ if (!parameters) {
419
+ parameters = {};
420
+ }
421
+ try {
422
+ return new Function(...Object.keys(parameters), `return \`${template}\`;`)(...Object.values(parameters));
423
+ }
424
+ catch (e) {
425
+ console.error(e);
426
+ return template;
427
+ }
428
+ }
429
+ function formatDate(dateStr, format) {
430
+ if (!format) {
431
+ return dateStr;
432
+ }
433
+ // Split the input date string
434
+ const [dd, mm, yyyy] = dateStr.split("-");
435
+ // Define replacements
436
+ const replacements = {
437
+ dd: dd,
438
+ mm: mm,
439
+ yyyy: yyyy,
440
+ };
441
+ // Replace format placeholders with actual values
442
+ return format.replace(/dd|mm|yyyy/g, (match) => replacements[match]);
443
+ }
100
444
  function maskValue(value) {
101
445
  if (!value) {
102
446
  return value;
@@ -112,7 +456,375 @@ function maskValue(value) {
112
456
  }
113
457
  return value;
114
458
  }
115
- // console.log(encrypt("Hello, World!", null));
116
- // console.log(decrypt("U2FsdGVkX1+6mavadgkMgJPIhR3IO1pDkx36TjTyoyE=", null));
117
- export { encrypt, decrypt, replaceWithLocalTestData, maskValue };
459
+ function _copyContext(from, to) {
460
+ to.browser = from.browser;
461
+ to.page = from.page;
462
+ to.context = from.context;
463
+ }
464
+ async function scrollPageToLoadLazyElements(page) {
465
+ let lastHeight = await page.evaluate(() => document.body.scrollHeight);
466
+ let retry = 0;
467
+ while (true) {
468
+ await page.evaluate(() => window.scrollBy(0, window.innerHeight));
469
+ await new Promise((resolve) => setTimeout(resolve, 1000));
470
+ let newHeight = await page.evaluate(() => document.body.scrollHeight);
471
+ if (newHeight === lastHeight) {
472
+ break;
473
+ }
474
+ lastHeight = newHeight;
475
+ retry++;
476
+ if (retry > 10) {
477
+ break;
478
+ }
479
+ }
480
+ await page.evaluate(() => window.scrollTo(0, 0));
481
+ }
482
+ function _fixUsingParams(text, _params) {
483
+ if (!_params || typeof text !== "string") {
484
+ return text;
485
+ }
486
+ for (let key in _params) {
487
+ let regValue = key;
488
+ if (key.startsWith("_")) {
489
+ // remove the _ prefix
490
+ regValue = key.substring(1);
491
+ }
492
+ text = text.replaceAll(new RegExp("{" + regValue + "}", "g"), _params[key]);
493
+ }
494
+ return text;
495
+ }
496
+ function getWebLogFile(logFolder) {
497
+ if (!fs.existsSync(logFolder)) {
498
+ fs.mkdirSync(logFolder, { recursive: true });
499
+ }
500
+ let nextIndex = 1;
501
+ while (fs.existsSync(path.join(logFolder, nextIndex.toString() + ".json"))) {
502
+ nextIndex++;
503
+ }
504
+ const fileName = nextIndex + ".json";
505
+ return path.join(logFolder, fileName);
506
+ }
507
+ function _fixLocatorUsingParams(locator, _params) {
508
+ // check if not null
509
+ if (!locator) {
510
+ return locator;
511
+ }
512
+ // clone the locator
513
+ locator = JSON.parse(JSON.stringify(locator));
514
+ scanAndManipulate(locator, _params);
515
+ return locator;
516
+ }
517
+ function _isObject(value) {
518
+ return value && typeof value === "object" && value.constructor === Object;
519
+ }
520
+ function scanAndManipulate(currentObj, _params) {
521
+ for (const key in currentObj) {
522
+ if (typeof currentObj[key] === "string") {
523
+ // Perform string manipulation
524
+ currentObj[key] = _fixUsingParams(currentObj[key], _params);
525
+ }
526
+ else if (_isObject(currentObj[key])) {
527
+ // Recursively scan nested objects
528
+ scanAndManipulate(currentObj[key], _params);
529
+ }
530
+ }
531
+ }
532
+ function extractStepExampleParameters(step) {
533
+ if (!step ||
534
+ !step.gherkinDocument ||
535
+ !step.pickle ||
536
+ !step.pickle.astNodeIds ||
537
+ !(step.pickle.astNodeIds.length > 1) ||
538
+ !step.gherkinDocument.feature ||
539
+ !step.gherkinDocument.feature.children) {
540
+ return {};
541
+ }
542
+ try {
543
+ const scenarioId = step.pickle.astNodeIds[0];
544
+ const exampleId = step.pickle.astNodeIds[1];
545
+ // find the scenario in the gherkin document
546
+ const scenario = step.gherkinDocument.feature.children.find((child) => child.scenario.id === scenarioId).scenario;
547
+ if (!scenario || !scenario.examples || !scenario.examples[0].tableBody) {
548
+ return {};
549
+ }
550
+ // find the table body in the examples
551
+ const row = scenario.examples[0].tableBody.find((r) => r.id === exampleId);
552
+ if (!row) {
553
+ return {};
554
+ }
555
+ // extract the cells values (row.cells.value) into an array
556
+ const values = row.cells.map((cell) => cell.value);
557
+ // extract the table headers keys (scenario.examples.tableHeader.cells.value) into an array
558
+ const keys = scenario.examples[0].tableHeader.cells.map((cell) => cell.value);
559
+ // create a dictionary of the keys and values
560
+ const params = {};
561
+ for (let i = 0; i < keys.length; i++) {
562
+ params[keys[i]] = values[i];
563
+ }
564
+ return params;
565
+ }
566
+ catch (e) {
567
+ console.error(e);
568
+ return {};
569
+ }
570
+ }
571
+ export async function performAction(action, element, options, web, state, _params) {
572
+ let usedOptions;
573
+ if (!options) {
574
+ options = {};
575
+ }
576
+ if (!element) {
577
+ throw new Error("Element not found");
578
+ }
579
+ switch (action) {
580
+ case "click":
581
+ // copy any of the following options to usedOptions: button, clickCount, delay, modifiers, force, position, trial
582
+ usedOptions = ["button", "clickCount", "delay", "modifiers", "force", "position", "trial", "timeout"].reduce((acc, key) => {
583
+ if (options[key]) {
584
+ acc[key] = options[key];
585
+ }
586
+ return acc;
587
+ }, {});
588
+ if (!usedOptions.timeout) {
589
+ usedOptions.timeout = 10000;
590
+ if (usedOptions.position) {
591
+ usedOptions.timeout = 1000;
592
+ }
593
+ }
594
+ if (usedOptions && usedOptions.clickCount) {
595
+ state.info.count = usedOptions.clickCount;
596
+ }
597
+ try {
598
+ check_performance("click_action", web.context, true);
599
+ await element.click(usedOptions);
600
+ check_performance("click_action", web.context, false);
601
+ }
602
+ catch (e) {
603
+ if (usedOptions.position) {
604
+ // find the element bounding box
605
+ const rect = await element.boundingBox();
606
+ // calculate the x and y position
607
+ const x = rect.x + rect.width / 2 + (usedOptions.position.x || 0);
608
+ const y = rect.y + rect.height / 2 + (usedOptions.position.y || 0);
609
+ // click on the x and y position
610
+ await web.page.mouse.click(x, y);
611
+ }
612
+ else {
613
+ if (state && state.selectors) {
614
+ state.element = await web._locate(state.selectors, state.info, _params);
615
+ element = state.element;
616
+ }
617
+ await element.dispatchEvent("click");
618
+ }
619
+ }
620
+ break;
621
+ case "hover":
622
+ usedOptions = ["position", "trial", "timeout"].reduce((acc, key) => {
623
+ acc[key] = options[key];
624
+ return acc;
625
+ }, {});
626
+ try {
627
+ await element.hover(usedOptions);
628
+ await new Promise((resolve) => setTimeout(resolve, 1000));
629
+ }
630
+ catch (e) {
631
+ if (state && state.selectors) {
632
+ state.info.log += "hover failed, will try again" + "\n";
633
+ state.element = await web._locate(state.selectors, state.info, _params);
634
+ element = state.element;
635
+ }
636
+ usedOptions.timeout = 10000;
637
+ await element.hover(usedOptions);
638
+ await new Promise((resolve) => setTimeout(resolve, 1000));
639
+ }
640
+ break;
641
+ case "hover+click":
642
+ await performAction("hover", element, options, web, state, _params);
643
+ await performAction("click", element, options, web, state, _params);
644
+ break;
645
+ default:
646
+ throw new Error(`Action ${action} not supported`);
647
+ }
648
+ }
649
+ const KEYBOARD_EVENTS = [
650
+ "ALT",
651
+ "AltGraph",
652
+ "CapsLock",
653
+ "Control",
654
+ "Fn",
655
+ "FnLock",
656
+ "Hyper",
657
+ "Meta",
658
+ "NumLock",
659
+ "ScrollLock",
660
+ "Shift",
661
+ "Super",
662
+ "Symbol",
663
+ "SymbolLock",
664
+ "Enter",
665
+ "Tab",
666
+ "ArrowDown",
667
+ "ArrowLeft",
668
+ "ArrowRight",
669
+ "ArrowUp",
670
+ "End",
671
+ "Home",
672
+ "PageDown",
673
+ "PageUp",
674
+ "Backspace",
675
+ "Clear",
676
+ "Copy",
677
+ "CrSel",
678
+ "Cut",
679
+ "Delete",
680
+ "EraseEof",
681
+ "ExSel",
682
+ "Insert",
683
+ "Paste",
684
+ "Redo",
685
+ "Undo",
686
+ "Accept",
687
+ "Again",
688
+ "Attn",
689
+ "Cancel",
690
+ "ContextMenu",
691
+ "Escape",
692
+ "Execute",
693
+ "Find",
694
+ "Finish",
695
+ "Help",
696
+ "Pause",
697
+ "Play",
698
+ "Props",
699
+ "Select",
700
+ "ZoomIn",
701
+ "ZoomOut",
702
+ "BrightnessDown",
703
+ "BrightnessUp",
704
+ "Eject",
705
+ "LogOff",
706
+ "Power",
707
+ "PowerOff",
708
+ "PrintScreen",
709
+ "Hibernate",
710
+ "Standby",
711
+ "WakeUp",
712
+ "AllCandidates",
713
+ "Alphanumeric",
714
+ "CodeInput",
715
+ "Compose",
716
+ "Convert",
717
+ "Dead",
718
+ "FinalMode",
719
+ "GroupFirst",
720
+ "GroupLast",
721
+ "GroupNext",
722
+ "GroupPrevious",
723
+ "ModeChange",
724
+ "NextCandidate",
725
+ "NonConvert",
726
+ "PreviousCandidate",
727
+ "Process",
728
+ "SingleCandidate",
729
+ "HangulMode",
730
+ "HanjaMode",
731
+ "JunjaMode",
732
+ "Eisu",
733
+ "Hankaku",
734
+ "Hiragana",
735
+ "HiraganaKatakana",
736
+ "KanaMode",
737
+ "KanjiMode",
738
+ "Katakana",
739
+ "Romaji",
740
+ "Zenkaku",
741
+ "ZenkakuHanaku",
742
+ "F1",
743
+ "F2",
744
+ "F3",
745
+ "F4",
746
+ "F5",
747
+ "F6",
748
+ "F7",
749
+ "F8",
750
+ "F9",
751
+ "F10",
752
+ "F11",
753
+ "F12",
754
+ "Soft1",
755
+ "Soft2",
756
+ "Soft3",
757
+ "Soft4",
758
+ "ChannelDown",
759
+ "ChannelUp",
760
+ "Close",
761
+ "MailForward",
762
+ "MailReply",
763
+ "MailSend",
764
+ "MediaFastForward",
765
+ "MediaPause",
766
+ "MediaPlay",
767
+ "MediaPlayPause",
768
+ "MediaRecord",
769
+ "MediaRewind",
770
+ "MediaStop",
771
+ "MediaTrackNext",
772
+ "MediaTrackPrevious",
773
+ "AudioBalanceLeft",
774
+ "AudioBalanceRight",
775
+ "AudioBassBoostDown",
776
+ "AudioBassBoostToggle",
777
+ "AudioBassBoostUp",
778
+ "AudioFaderFront",
779
+ "AudioFaderRear",
780
+ "AudioSurroundModeNext",
781
+ "AudioTrebleDown",
782
+ "AudioTrebleUp",
783
+ "AudioVolumeDown",
784
+ "AudioVolumeMute",
785
+ "AudioVolumeUp",
786
+ "MicrophoneToggle",
787
+ "MicrophoneVolumeDown",
788
+ "MicrophoneVolumeMute",
789
+ "MicrophoneVolumeUp",
790
+ "TV",
791
+ "TV3DMode",
792
+ "TVAntennaCable",
793
+ "TVAudioDescription",
794
+ ];
795
+ function unEscapeString(str) {
796
+ return str.replace(/\\n/g, "\n");
797
+ }
798
+ function _getServerUrl() {
799
+ let serviceUrl = "https://api.blinq.io";
800
+ if (process.env.NODE_ENV_BLINQ === "dev") {
801
+ serviceUrl = "https://dev.api.blinq.io";
802
+ }
803
+ else if (process.env.NODE_ENV_BLINQ === "stage") {
804
+ serviceUrl = "https://stage.api.blinq.io";
805
+ }
806
+ else if (process.env.NODE_ENV_BLINQ === "prod") {
807
+ serviceUrl = "https://api.blinq.io";
808
+ }
809
+ else if (!process.env.NODE_ENV_BLINQ) {
810
+ serviceUrl = "https://api.blinq.io";
811
+ }
812
+ else {
813
+ serviceUrl = process.env.NODE_ENV_BLINQ;
814
+ }
815
+ return serviceUrl;
816
+ }
817
+ function tryParseJson(input) {
818
+ if (typeof input === "string") {
819
+ try {
820
+ return JSON.parse(input);
821
+ }
822
+ catch {
823
+ // If parsing fails, return the original input (assumed to be plain text or another format)
824
+ return input;
825
+ }
826
+ }
827
+ return input;
828
+ }
829
+ export { measureAsync, encrypt, decrypt, getObjectDataPathFromKey, replaceWithLocalTestData, formatDate, evaluateString, maskValue, _copyContext, scrollPageToLoadLazyElements, _fixUsingParams, getWebLogFile, _fixLocatorUsingParams, _isObject, scanAndManipulate, KEYBOARD_EVENTS, unEscapeString, _getServerUrl, _convertToRegexQuery, extractStepExampleParameters, _getDataFile, tryParseJson, getTestDataValue, replaceTestDataValue, _getTestData, };
118
830
  //# sourceMappingURL=utils.js.map