@devassure/tpp-engine 0.0.0-stage → 1.8.0

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 (68) hide show
  1. package/README.md +18 -2
  2. package/index.js +121 -0
  3. package/package.json +30 -4
  4. package/src/accessibility/combined-a11y-report.js +208 -0
  5. package/src/accessibility/tpp-a11y.js +81 -0
  6. package/src/actions.js +3201 -0
  7. package/src/api/api-auth.js +18 -0
  8. package/src/api/api-service.js +289 -0
  9. package/src/app-config.json +7 -0
  10. package/src/autoheal-actions.js +309 -0
  11. package/src/console-log/console-log-manager.js +39 -0
  12. package/src/console-log/console-log-utils.js +201 -0
  13. package/src/database/mysql/mysql-service.js +23 -0
  14. package/src/database/neo4j/neo4j-service.js +32 -0
  15. package/src/database/oracle/oracle-service.js +48 -0
  16. package/src/database/pg/pg-service.js +29 -0
  17. package/src/downloads/downloads-manager.js +86 -0
  18. package/src/downloads/downloads-utils.js +113 -0
  19. package/src/globalsetup.js +68 -0
  20. package/src/globalteardown.js +57 -0
  21. package/src/hooks/each-hook.js +45 -0
  22. package/src/hooks/hook-utils.js +21 -0
  23. package/src/library/authenticator/authenticator-utils.js +43 -0
  24. package/src/library/csv/csv-utils.js +297 -0
  25. package/src/library/date/date-utils.js +168 -0
  26. package/src/library/emailTesting/TokenConfig.js +46 -0
  27. package/src/library/emailTesting/emailTesting-utils.js +527 -0
  28. package/src/library/excel/excel-utils.js +922 -0
  29. package/src/library/file/file-action-utils.js +129 -0
  30. package/src/library/file/file-utils.js +127 -0
  31. package/src/library/json/json-utils.js +104 -0
  32. package/src/library/math/math-utils.js +283 -0
  33. package/src/library/softAssert/softAssert-utils.js +154 -0
  34. package/src/library/table/table-utils.js +92 -0
  35. package/src/library/text/text-utils.js +45 -0
  36. package/src/library/tpp-library.js +81 -0
  37. package/src/library/web/slider-utils.js +66 -0
  38. package/src/mobile/appium/appium-manager.js +158 -0
  39. package/src/mobile/device-cloud/browserstack-service.js +44 -0
  40. package/src/mobile/device-cloud/commands/browserstack-commands.js +20 -0
  41. package/src/mobile/device-cloud/commands/device-cloud-commands.js +17 -0
  42. package/src/mobile/device-cloud/device-cloud-service.js +29 -0
  43. package/src/mobile/device-cloud/pcloudy-service.js +65 -0
  44. package/src/mobile/mobile-actions.js +1568 -0
  45. package/src/mobile/recorder-utils/css-selector-generator.js +2 -0
  46. package/src/mobile/recorder-utils/finder.js +752 -0
  47. package/src/mobile/recorder-utils/mobile-recorder.js +266 -0
  48. package/src/mobile/web-driver-utils.js +110 -0
  49. package/src/models/tpp-element.js +58 -0
  50. package/src/models/tpp-mobile-element.js +50 -0
  51. package/src/network-log/network-log-manager.js +96 -0
  52. package/src/network-log/network-log-utils.js +361 -0
  53. package/src/types/elements.types.js +24 -0
  54. package/src/utils/ai-utils.js +48 -0
  55. package/src/utils/assert-utils.js +50 -0
  56. package/src/utils/autoheal-utils.js +9 -0
  57. package/src/utils/common-utils.js +120 -0
  58. package/src/utils/cookie-utils.js +70 -0
  59. package/src/utils/json-utils.js +10 -0
  60. package/src/utils/log-utils.js +10 -0
  61. package/src/utils/logger.js +76 -0
  62. package/src/utils/screenshot-utils.js +42 -0
  63. package/src/utils/token-utils.js +18 -0
  64. package/src/utils/vectordb-utils.js +125 -0
  65. package/src/visual-test/tpp-visual-test.js +122 -0
  66. package/src/web/web-executor-agent.js +149 -0
  67. package/src/web/web-executor-data-fetch.js +353 -0
  68. package/test-excel-utils.js +570 -0
@@ -0,0 +1,10 @@
1
+ const ansiRegex = ({onlyFirst = false} = {}) => {
2
+ const pattern = [
3
+ '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
4
+ '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))'
5
+ ].join('|');
6
+
7
+ return new RegExp(pattern, onlyFirst ? undefined : 'g');
8
+ }
9
+
10
+ exports.ansiRegex = ansiRegex;
@@ -0,0 +1,76 @@
1
+ const LOG_PREFIX = '[[TPP]] ';
2
+
3
+ class Logger {
4
+
5
+ constructor(caller, blacklistedLogs) {
6
+ this.caller = caller;
7
+ this.blacklistedLogs = blacklistedLogs;
8
+ }
9
+
10
+ action_log(log) {
11
+ this.blacklistedLogs?.forEach(text => {
12
+ if(log?.includes(log)) log = log.replaceAll(text, '************');
13
+ })
14
+ console.log(log);
15
+ }
16
+
17
+ log(stepName, msg) {
18
+ if(!!stepName) {
19
+ this.action_log(`${LOG_PREFIX}${this.caller} :: ${stepName} :: ${msg}`);
20
+ } else {
21
+ this.action_log(`${LOG_PREFIX}${this.caller} :: ${msg}`);
22
+ }
23
+ }
24
+
25
+ logWithoutMasking(stepName, msg) {
26
+ if(!!stepName) {
27
+ console.log(`${LOG_PREFIX}${this.caller} :: ${stepName} :: ${msg}`);
28
+ } else {
29
+ console.log(`${LOG_PREFIX}${this.caller} :: ${msg}`);
30
+ }
31
+ }
32
+
33
+ startLog(stepName) {
34
+ if(!!stepName) {
35
+ this.action_log(`${LOG_PREFIX}${this.caller} :: ${stepName} :: [BEGIN]`);
36
+ } else {
37
+ this.action_log(`${LOG_PREFIX}${this.caller} :: [BEGIN]`);
38
+ }
39
+ }
40
+
41
+ endLog(stepName) {
42
+ if(!!stepName) {
43
+ this.action_log(`${LOG_PREFIX}${this.caller} :: ${stepName} :: [END]`);
44
+ } else {
45
+ this.action_log(`${LOG_PREFIX}${this.caller} :: [END]`);
46
+ }
47
+ }
48
+
49
+ failureLog(stepName) {
50
+ if(!!stepName) {
51
+ this.action_log(`${LOG_PREFIX}${this.caller} :: ${stepName} :: [ERROR]`);
52
+ } else {
53
+ this.action_log(`${LOG_PREFIX}${this.caller} :: [ERROR]`);
54
+ }
55
+ }
56
+
57
+ elementBackupRetryLog(tppElement, backupIndex, label) {
58
+ const backup = tppElement.element.backupLocators[backupIndex]
59
+ this.log(label, `Unable to find element : ${tppElement.element.label} : ${tppElement.getSelector()}${this.getIndexLog(tppElement)}, retrying with fallback locator: ${backup.locatorType} - ${backup.locator}`);
60
+ }
61
+
62
+ logBackupLocatorUsed(tppElement) {
63
+ const backupData = {
64
+ element: tppElement.element.label,
65
+ locatorType: tppElement.element.locatorType,
66
+ locator: tppElement.element.locator,
67
+ };
68
+ console.log(`${LOG_PREFIX}${this.caller} :: [backup-locator-used] ${JSON.stringify(backupData)}`);
69
+ }
70
+
71
+ getIndexLog = (tppElement) => tppElement.element.index != null ? ` : element index = ${tppElement.element.index}` : '';
72
+
73
+ }
74
+
75
+ exports.Logger = Logger;
76
+ exports.LOG_PREFIX = LOG_PREFIX;
@@ -0,0 +1,42 @@
1
+ const path = require('path');
2
+ const { LOG_PREFIX } = require("./logger");
3
+
4
+ function getScreenshotPath(imageName, caller, retry, isVideo) {
5
+ let fileName = caller?.replaceAll('>', '___')
6
+ ?.replaceAll(":", "--")
7
+ ?.replace(/[\b\f\n\r\t\"\\]/g, '_');
8
+ if(!fileName || fileName.length > 100) {
9
+ fileName = Date.now().toString();
10
+ }
11
+ if (retry) {
12
+ fileName += `__retry${retry}`;
13
+ }
14
+ if (imageName?.trim()) {
15
+ fileName += `__${imageName.trim().replaceAll(' ', '_')}`;
16
+ }
17
+
18
+ if(isVideo) {
19
+ return `screenshots/${fileName}.webm`;
20
+ }
21
+
22
+ return `screenshots/${fileName}.png`;
23
+ }
24
+
25
+ function getScreenshotLog(imageName, filePath, caller, isVideo) {
26
+ const screenshotLog = isVideo
27
+ ? {videoPath: path.resolve(filePath)}
28
+ : {screenshotPath: path.resolve(filePath)};
29
+ if(imageName?.trim()) {
30
+ screenshotLog.label = imageName.trim();
31
+ }
32
+ const log = JSON.stringify(screenshotLog);
33
+
34
+ if(isVideo) {
35
+ return `${LOG_PREFIX}${caller} :: [video] ${log}`
36
+ }
37
+
38
+ return `${LOG_PREFIX}${caller} :: [screenshot] ${log}`
39
+ }
40
+
41
+ exports.getScreenshotPath = getScreenshotPath;
42
+ exports.getScreenshotLog = getScreenshotLog;
@@ -0,0 +1,18 @@
1
+ const { FileUtils } = require('../library/file/file-utils');
2
+
3
+ function resetTokenFile() {
4
+ const fileUtils = new FileUtils();
5
+ const filePath = "toktmp/tmp.json";
6
+
7
+ try {
8
+ if (fileUtils.isExists(filePath)) {
9
+ fileUtils.writeTextContent(filePath, "{}");
10
+ } else {
11
+ console.error(`File '${filePath}' does not exist.`);
12
+ }
13
+ } catch (error) {
14
+ console.error(`An error occurred while resetting the file: ${error.message}`);
15
+ }
16
+ }
17
+
18
+ exports.resetTokenFile = resetTokenFile;
@@ -0,0 +1,125 @@
1
+ const path = require('path');
2
+ const { spawn } = require('child_process');
3
+ const fs = require('fs');
4
+ const axios = require('axios');
5
+ const os = require('os');
6
+ const config = require('../app-config.json');
7
+
8
+ const VECTORDB_VERSION = '1.0.5';
9
+
10
+ const downloadAppDependency = async (os, targetDir) => {
11
+ const baseUrl = config.s3Url;
12
+ // macos/tpp-vector-db/1.0.4/DevAssure.zip
13
+ const url = getVectorDbPath(baseUrl);
14
+ const downloadedFileName = 'tpp-vector-db';
15
+ const targetFileName = os === 'windows' ? `${downloadedFileName}.exe` : downloadedFileName;
16
+ const zipFilePath = path.join(targetDir, 'vectordb.zip');
17
+ const targetFilePath = path.join(targetDir, targetFileName);
18
+
19
+ try {
20
+
21
+ console.log(`[[TPP]] Downloading excel library...`);
22
+ const response = await axios.get(url, { responseType: 'stream' });
23
+ const writer = fs.createWriteStream(zipFilePath);
24
+ response.data.pipe(writer);
25
+
26
+ await new Promise((resolve, reject) => {
27
+ writer.on('finish', resolve);
28
+ writer.on('error', reject);
29
+ });
30
+
31
+ const unzip = require('unzipper');
32
+ await fs.createReadStream(zipFilePath).pipe(unzip.Extract({ path: targetDir })).promise();
33
+
34
+ const extractedFilePath = path.join(targetDir, downloadedFileName);
35
+ fs.renameSync(extractedFilePath, targetFilePath);
36
+
37
+ fs.chmodSync(targetFilePath, 0o755);
38
+
39
+ fs.unlinkSync(zipFilePath);
40
+
41
+ console.log(`[[TPP]] Excel library downloaded and prepared successfully.`);
42
+ return true;
43
+ } catch (error) {
44
+ console.error(`[[TPP]] Failed to download dependency: ${error.message}`);
45
+ return false;
46
+ }
47
+ };
48
+
49
+ const processExcel = async (dependenciesPath, filePath) => {
50
+ return new Promise(async (resolve, reject) => {
51
+ let vectorDbPath;
52
+ if (process.platform === 'darwin' || process.platform === 'linux') {
53
+ vectorDbPath = path.join(dependenciesPath, './tpp-vector-db');
54
+ } else {
55
+ vectorDbPath = path.join(dependenciesPath, './tpp-vector-db.exe');
56
+ }
57
+
58
+ // Check if vectorDbPath exists
59
+ if (!fs.existsSync(vectorDbPath)) {
60
+ const targetDir = path.dirname(vectorDbPath);
61
+ const os = process.platform === 'win32' ? 'windows' : process.platform === 'darwin' ? 'macos' : 'linux';
62
+ const downloadStatus = await downloadAppDependency(os, targetDir);
63
+ if (!downloadStatus) {
64
+ return reject(new Error("[[TPP]] Failed to download excel library"));
65
+ }
66
+ }
67
+
68
+ const args = [
69
+ '--reqid', 'sdsd',
70
+ '--action', 'process_excel',
71
+ '--token', 'excel-token',
72
+ '--index', 'none',
73
+ '--sheet', filePath,
74
+ ];
75
+
76
+ const child = spawn(vectorDbPath, args);
77
+
78
+ let buffer = '';
79
+
80
+ child.stdout.on('data', (data) => {
81
+ buffer += data.toString();
82
+ if (data.includes('[[RESULT]]')) {
83
+ try {
84
+ const result = JSON.parse(data.toString().replace('[[RESULT]]', '').trim());
85
+ resolve(result);
86
+ } catch (err) {
87
+ reject(new Error('Failed to parse result from processing excel: ' + err.message));
88
+ }
89
+ }
90
+ });
91
+
92
+ child.stdout.on('close', () => {
93
+ if (!buffer.includes('[[ERROR]]')) {
94
+ reject(new Error(buffer.replace('[[ERROR]]', '')));
95
+ }
96
+ });
97
+ });
98
+ };
99
+
100
+ const getVectorDbPath = (baseUrl) => {
101
+ const platform = process.platform;
102
+ const arch = os.arch();
103
+ let osPath;
104
+
105
+ if (platform === 'win32') {
106
+ osPath = `windows/tpp-vector-db/${VECTORDB_VERSION}/DevAssure.zip`;
107
+ } else if (platform === 'darwin') {
108
+ osPath = arch === 'arm64'
109
+ ? `macos/tpp-vector-db/${VECTORDB_VERSION}/DevAssure.zip`
110
+ : `macos_intel/tpp-vector-db/${VECTORDB_VERSION}/DevAssure.zip`;
111
+ } else if (platform === 'linux') {
112
+ osPath = arch === 'x64'
113
+ ? `linux_latest/tpp-vector-db/${VECTORDB_VERSION}/DevAssure.zip`
114
+ : `linux/tpp-vector-db/${VECTORDB_VERSION}/DevAssure.zip`;
115
+ } else {
116
+ throw new Error(`Unsupported platform: ${platform}`);
117
+ }
118
+
119
+ return `${baseUrl}/${osPath}`;
120
+ };
121
+
122
+ module.exports = {
123
+ processExcel,
124
+ getVectorDbPath
125
+ };
@@ -0,0 +1,122 @@
1
+ const { expect } = require("@playwright/test");
2
+ const { default: matchers } = require("expect-axe-playwright");
3
+ const { ansiRegex } = require("../utils/log-utils");
4
+
5
+ expect.extend(matchers);
6
+
7
+ class TppVisualCompare {
8
+
9
+ constructor(page, caller, timeouts, softAssert) {
10
+ this.page = page;
11
+ this.caller = caller;
12
+ this.timeouts = timeouts;
13
+ this.softAssert = softAssert;
14
+ }
15
+
16
+ getPageAndFrame() {
17
+ let page, frame;
18
+ if(this.page.page && this.page.page()) {
19
+ frame = this.page;
20
+ page = this.page.page();
21
+ } else {
22
+ page = this.page;
23
+ }
24
+ return {page, frame};
25
+ }
26
+
27
+ async comparePage(label) {
28
+ const {page, frame} = this.getPageAndFrame();
29
+ try {
30
+ await expect(page).toHaveScreenshot(`${label}.png`, {timeout: 10000, });
31
+ } catch (_e) {
32
+ try {
33
+ await page.waitForTimeout(5000);
34
+ await expect(page).toHaveScreenshot(`${label}.png`, {timeout: (this.timeouts?.actionTimeout || 30) * 1000 });
35
+ } catch (e) {
36
+ this.handleException(e, label);
37
+ }
38
+ }
39
+ }
40
+
41
+ async compareElement(tppElement, label) {
42
+ const locator = tppElement.locator();
43
+ try {
44
+ await expect(locator).toHaveScreenshot(`${label}.png`, {
45
+ timeout: (this.timeouts?.actionTimeout || 30) * 1000,
46
+ fullPage: true,
47
+ });
48
+ } catch (e) {
49
+ this.handleException(e, label);
50
+ }
51
+ }
52
+
53
+ async advancedCompare(tppElement, data) {
54
+ const {page, frame} = this.getPageAndFrame();
55
+ const object = tppElement ? tppElement.locator() : page;
56
+ if(data?.ignoreFailure) {
57
+ this.ignoreFailure = true;
58
+ }
59
+ try {
60
+ await expect(object).toHaveScreenshot(`${data.label}.png`, {
61
+ timeout: data.timeout ? Number(data.timeout) : (this.timeouts?.actionTimeout || 30) * 1000,
62
+ mask: data.maskLocators?.map(loc => {
63
+ let sanitisedLoc = loc?.startsWith('/html') ? `/${loc}` : loc;
64
+ return this.page.locator(sanitisedLoc);
65
+ }) || [],
66
+ omitBackground: data.ignoreBackground === 'true',
67
+ fullPage: !!data.fullPage,
68
+ threshold: data.threshold != null ? Number(data.threshold) : 0.2,
69
+ });
70
+ } catch (e) {
71
+ this.handleException(e, data.label);
72
+ }
73
+ }
74
+
75
+ handleException(ex, label) {
76
+ console.log("Visual test exception: ", ex);
77
+ const message = ex.message.replace(ansiRegex(), '');
78
+ if(message.startsWith('Screenshot comparison failed') || message.includes('pixels) are different')) {
79
+ const failureLog = `[visual-comparison-failure] ${this.transformFailureDetails(message, label)}`;
80
+ if(this.softAssert?.softAssert || this.ignoreFailure) {
81
+ if(this.softAssert?.logMessage) {
82
+ this.softAssert.logMessage(this.ignoreFailure ? "Visual comparison failed!"
83
+ : "Soft Assert - Visual comparison failed!");
84
+ this.softAssert.logMessage(failureLog);
85
+ } else {
86
+ console.log(`${LOG_PREFIX} ${failureLog}`);
87
+ }
88
+ this.softAssert.softAssert && this.softAssert.softAssert.assertions?.push({ result: false, message: this.softAssert?.assertMessage || 'Visual comparison failed!' });
89
+ } else {
90
+ throw {message: 'Visual comparison failed!', logs: [failureLog]};
91
+ }
92
+ } else {
93
+ //TODO handle
94
+ throw ex;
95
+ }
96
+ }
97
+
98
+ transformFailureDetails(message, label) {
99
+ const result = {};
100
+ message.split('\n').forEach(line => {
101
+ if(line.startsWith('Expected: ')) {
102
+ //NOOP
103
+ } else if(line.startsWith('Received: ')) {
104
+ result.actual = line.replace('Received: ', '');
105
+ } else if(line.trim().startsWith('Diff: ')) {
106
+ result.diff = line.trim().replace('Diff: ', '');
107
+ }
108
+ });
109
+ result.expected = result.actual.replace('actual.png', 'expected.png');
110
+ const caller = this.caller.split('::')?.at(0)?.trim()?.split('>');
111
+ const testLabel = caller?.slice(0, -1)
112
+ ?.join('>')?.trim();
113
+ const imageName = label ? `${label}-${caller?.at(-1)?.trim()}-${process.platform}.png`
114
+ : undefined;
115
+ result.caller = testLabel || "";
116
+ result.imageName = imageName;
117
+ return JSON.stringify(result);
118
+ }
119
+
120
+ }
121
+
122
+ exports.TppVisualCompare = TppVisualCompare;
@@ -0,0 +1,149 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { FileUtils } = require('../library/file/file-utils');
4
+ const { fetchWebState } = require('./web-executor-data-fetch');
5
+ const { sleep } = require('../utils/common-utils');
6
+
7
+ const EVENT_TAG = "[[TPP_EVENT_LOG]]";
8
+
9
+ class WebExecutorAgent {
10
+
11
+ constructor(actions) {
12
+ this.actions = actions;
13
+ }
14
+
15
+ async openWebExecutorAgent() {
16
+ console.log(`${EVENT_TAG} [[TPP WebExecutorAgent Connected]]`)
17
+
18
+ const commandFilePath = `.web-exe-agent-commands/commands_${Date.now()}`;
19
+ new FileUtils().writeTextContent(commandFilePath, 'ready')
20
+ console.log(`${EVENT_TAG} [[TPP WebExecutorAgent FILE]] ${path.resolve(commandFilePath)}`);
21
+
22
+ async function readCommandFromFile() {
23
+ return new Promise((resolve) => {
24
+ const checkInterval = setInterval(() => {
25
+ if (fs.existsSync(commandFilePath)) {
26
+ const commands = fs.readFileSync(commandFilePath, 'utf-8').trim();
27
+ if (commands !== 'ready') {
28
+ commands?.trim() && console.log(`Received commands: ${commands}`);
29
+ clearInterval(checkInterval);
30
+ resolve(commands);
31
+ }
32
+ } else {
33
+ resolve('stop_web_executor_agent');
34
+ }
35
+ }, 500); // Check every 500ms
36
+ });
37
+ };
38
+
39
+ async function deleteCommandFile() {
40
+ if (await fs.existsSync(commandFilePath)) {
41
+ fs.unlinkSync(commandFilePath);
42
+ }
43
+ }
44
+
45
+ let killAgent = false;
46
+
47
+ while (true) {
48
+ console.log('Waiting for a new command...');
49
+ const commandText = await readCommandFromFile();
50
+ if (commandText.trim().length === 0) continue;
51
+ const commands = commandText.split('\n');
52
+
53
+ for (let i = 0; i < commands.length; i++) {
54
+ const command = commands[i];
55
+
56
+ if (command === 'stop_web_executor_agent') {
57
+ console.log('Stopping Executor Agent...');
58
+ console.log(`${EVENT_TAG} [[TPP WebExecutorAgent Execution Stop]]`);
59
+ killAgent = true;
60
+ break;
61
+ }
62
+
63
+ fs.writeFileSync(commandFilePath, 'ready');
64
+ try {
65
+ if (!this.actions.page) {
66
+ throw new Error('Web page is closed/not initialized. Please ensure the page is loaded before executing commands.');
67
+ }
68
+ await this.processWebExecutorCommand(command, i == commands.length - 1);
69
+ } catch(error) {
70
+ this.actions.log(undefined, `[EXECUTOR-AGENT-ERROR] ${error?.message || 'Error performing action on web page!'}`);
71
+ console.log(`${EVENT_TAG} [[TPP WebExecutorAgent Execution Stop]]`);
72
+ killAgent = true;
73
+ break
74
+ }
75
+ }
76
+
77
+ if(killAgent) {
78
+ deleteCommandFile();
79
+ break;
80
+ }
81
+
82
+ }
83
+
84
+ if (fs.existsSync(commandFilePath)) { fs.unlinkSync(commandFilePath); }
85
+ }
86
+
87
+ async printWebState() {
88
+ let i = 0;
89
+ let webState;
90
+ while(i < 4 && !webState) {
91
+ try {
92
+ await this.actions.page?.waitForLoadState();
93
+ await sleep(1000);
94
+ webState = await fetchWebState(this.actions.page);
95
+ } catch (e) {
96
+ i++;
97
+ if(i < 4 && (this.actions.isNavigationError(e) || this.isScreenshotTimeout(e))) {
98
+ this.actions.log(undefined, `Page reloaded or redirected. Retrying... #${i}`);
99
+ await sleep(1000);
100
+ } else {
101
+ throw e;
102
+ }
103
+ }
104
+ }
105
+ console.log(`${EVENT_TAG} [[TPP WebExecutorAgent State]] ${JSON.stringify(webState)}`);
106
+ }
107
+
108
+ //TODO add try catch and throw appropriate error message
109
+ async processWebExecutorCommand(command, last) {
110
+ console.log(`${EVENT_TAG} [[TPP WebExecutorAgent Command Execution]] ${command}`);
111
+ if(!command?.trim()) {
112
+ return;
113
+ } else if(command.trim().startsWith('open_url')) {
114
+ await this.actions.open_url(command.split('open_url').at(-1).trim());
115
+ } else if (command.trim().startsWith('right_click')) {
116
+ const data = JSON.parse(command.replace('validation', '').split('right_click').at(-1).trim());
117
+ await this.actions.click_on_xy(data.x, data.y, { button: 'right' });
118
+ } else if(command.trim().startsWith('click_on_element')) {
119
+ const data = JSON.parse(command.replace('validation', '').split('click_on_element').at(-1).trim());
120
+ await this.actions.click_on_xy(data.x, data.y);
121
+ } else if (command.trim().startsWith('hover')) {
122
+ const data = JSON.parse(command.split('hover').at(-1).trim());
123
+ await this.actions.hover_xy(data.x, data.y);
124
+ } else if (command.trim().startsWith('double_click')) {
125
+ const data = JSON.parse(command.split('double_click').at(-1).trim());
126
+ await this.actions.click_on_xy(data.x, data.y, { clickCount: 2 });
127
+ } else if (command.trim().startsWith('key_down')) {
128
+ await this.actions.key_down_xy(command.split('key_down').at(-1).trim());
129
+ } else if(command.trim().startsWith('input_value')) {
130
+ const data = JSON.parse(command.split('input_value').at(-1).trim());
131
+ await this.actions.input_on_xy(data.x, data.y, data.value || '');
132
+ } else if(command.trim().startsWith('print_page_state') && last) {
133
+ await this.printWebState();
134
+ } else if(command.trim().startsWith('clear_input')) {
135
+ const data = JSON.parse(command.split('clear_input').at(-1).trim());
136
+ await this.actions.clear_input(data.x, data.y);
137
+ } else {
138
+ this.actions.log(undefined, `[EXECUTOR-AGENT-ERROR] Invalid command entered by the AI agent`);
139
+ return;
140
+ }
141
+ }
142
+
143
+ isScreenshotTimeout(error) {
144
+ error?.message?.includes("page.screenshot")
145
+ && error?.message?.includes("Timeout")
146
+ }
147
+ }
148
+
149
+ exports.WebExecutorAgent = WebExecutorAgent;