@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,129 @@
1
+ const fs = require('fs');
2
+ const fsExtra = require('fs-extra');
3
+ const { Logger } = require('../../utils/logger');
4
+ const path = require('path');
5
+ const { FileUtils } = require('./file-utils');
6
+
7
+ class FileActionUtils {
8
+
9
+ constructor(caller, blacklistedLogs) {
10
+ this.caller = caller;
11
+ this.blacklistedLogs = blacklistedLogs;
12
+ }
13
+
14
+ setCaller(caller) {
15
+ this.caller = caller;
16
+ }
17
+
18
+ setBlacklistedLogs(blacklistedLogs) {
19
+ this.blacklistedLogs = blacklistedLogs;
20
+ }
21
+
22
+ readContent(filePath, logLabel) {
23
+ const logger = new Logger(this.caller, this.blacklistedLogs);
24
+ filePath = new FileUtils().getFilePath(filePath);
25
+ if(!fs.existsSync(filePath)) {
26
+ logger.failureLog(logLabel);
27
+ throw {message: `File ${filePath} does not exists!`}
28
+ }
29
+ const content = fs.readFileSync(filePath, {encoding: 'utf-8'})?.replaceAll('\r', '');
30
+ return content;
31
+ }
32
+
33
+ readContentIgnoreMissing(filePath, fallBack) {
34
+ filePath = new FileUtils().getFilePath(filePath);
35
+ if(!fs.existsSync(filePath)) {
36
+ return fallBack || "";
37
+ }
38
+ const content = fs.readFileSync(filePath, {encoding: 'utf-8'})?.replaceAll('\r', '');
39
+ return content;
40
+ }
41
+
42
+ read_content_as_text(filePath) {
43
+ const logLabel = `File.Read content as text ${filePath}`;
44
+ const logger = new Logger(this.caller, this.blacklistedLogs);
45
+ logger.startLog(logLabel);
46
+ const content = this.readContent(filePath, logLabel);
47
+ logger.endLog(logLabel);
48
+ return content;
49
+ }
50
+
51
+ read_content_as_json(filePath) {
52
+ const logLabel = `File.Read content as json ${filePath}`;
53
+ const logger = new Logger(this.caller, this.blacklistedLogs);
54
+ logger.startLog(logLabel);
55
+ const content = this.readContent(filePath, logLabel);
56
+ let jsonData;
57
+ try {
58
+ jsonData = JSON.parse(content?.trim());
59
+ } catch (ex) {
60
+ throw { message: `Content is not a valid JSON - ${ex}`};
61
+ }
62
+ logger.endLog(logLabel);
63
+ return jsonData;
64
+ }
65
+
66
+ write_content_to_file(content, filePath) {
67
+ const logLabel = `File.Write content to ${filePath}`;
68
+ const logger = new Logger(this.caller, this.blacklistedLogs);
69
+ logger.startLog(logLabel);
70
+ new FileUtils().writeTextContent(filePath, content);
71
+ logger.endLog(logLabel);
72
+ }
73
+
74
+ is_present(filePath) {
75
+ const logLabel = `File.Is Present ${filePath}`;
76
+ const logger = new Logger(this.caller, this.blacklistedLogs);
77
+ logger.startLog(logLabel);
78
+ let exists = fs.existsSync(filePath);
79
+ logger.endLog(logLabel);
80
+ return exists;
81
+ }
82
+
83
+ delete_file(filePath) {
84
+ const logLabel = `File.Delete file`;
85
+ const logger = new Logger(this.caller, this.blacklistedLogs);
86
+ logger.startLog(logLabel);
87
+ if(!fs.existsSync(filePath)) {
88
+ logger.failureLog(logLabel);
89
+ throw {message: `File ${filePath} does not exists!`}
90
+ }
91
+ fsExtra.removeSync(filePath);
92
+ logger.endLog(logLabel);
93
+ }
94
+
95
+ get_absolute_path(filePath) {
96
+ const logLabel = `File.Get absolute path of ${filePath}`;
97
+ const logger = new Logger(this.caller, this.blacklistedLogs);
98
+ logger.startLog(logLabel);
99
+ filePath = new FileUtils().getFilePath(filePath);
100
+ if(!fs.existsSync(filePath)) {
101
+ logger.failureLog(logLabel);
102
+ throw {message: `File ${filePath} does not exists!`}
103
+ }
104
+ const absPath = path.resolve(filePath);
105
+ logger.endLog(logLabel);
106
+ return absPath;
107
+ }
108
+
109
+ copy_file(filePath, targetDirectory) {
110
+ const logLabel = `File.Copy file ${filePath} to directory ${targetDirectory}`;
111
+ const logger = new Logger(this.caller, this.blacklistedLogs);
112
+ logger.startLog(logLabel);
113
+ filePath = new FileUtils().getFilePath(filePath);
114
+
115
+ if(!fs.existsSync(filePath)) {
116
+ logger.failureLog(logLabel);
117
+ throw {message: `File ${filePath} does not exists!`}
118
+ }
119
+
120
+ const fileName = path.basename(filePath);
121
+ const targetPath = path.join(targetDirectory, fileName);
122
+ new FileUtils().copyFile(filePath, targetPath);
123
+ logger.log(logLabel, `Copied to ${targetPath}`);
124
+ logger.endLog(logLabel);
125
+ }
126
+
127
+ }
128
+
129
+ exports.FileActionUtils = FileActionUtils;
@@ -0,0 +1,127 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const fsExtra = require('fs-extra');
4
+
5
+ class FileUtils {
6
+
7
+ isExists(filePath) {
8
+ return fs.existsSync(filePath);
9
+ }
10
+
11
+ readTextContent(filePath) {
12
+ if(!fs.existsSync(filePath)) {
13
+ throw {
14
+ message: `file ${filePath} does not exists!`
15
+ }
16
+ }
17
+ const data = fs.readFileSync(filePath, {encoding: 'utf-8'});
18
+ return data;
19
+ }
20
+
21
+ writeTextContent(filePath, content) {
22
+ this.createParentIfNotPresent(filePath);
23
+ fs.writeFileSync(filePath, content);
24
+ }
25
+
26
+ copyFile(sourcePath, targetPath) {
27
+ this.createParentIfNotPresent(targetPath);
28
+ fs.copyFileSync(sourcePath, targetPath);
29
+ }
30
+
31
+ getFilePath(filePath) {
32
+ if(filePath.startsWith('/')) {
33
+ return filePath;
34
+ }
35
+ if(!fs.existsSync(filePath)) {
36
+ const internalPath = filePath.replaceAll('\\', '/');
37
+ const startSep = internalPath.startsWith('/') ? '' : '/';
38
+ return `resources${startSep}${internalPath}`;
39
+ }
40
+ return filePath;
41
+ }
42
+
43
+ createParentIfNotPresent(filePath) {
44
+ if(!filePath.includes('/') && !filePath.includes('\\')) {
45
+ return;
46
+ }
47
+ const lastSepI = filePath.includes('/')
48
+ ? filePath.lastIndexOf('/')
49
+ : filePath.lastIndexOf(path.sep);
50
+ const parentPath = filePath.slice(0, lastSepI+1);
51
+ if(!fs.existsSync(parentPath)) {
52
+ fs.mkdirSync(parentPath, {recursive: true});
53
+ }
54
+ }
55
+
56
+ createDirIfNotPresent(dirPath) {
57
+ if(!fs.existsSync(dirPath)) {
58
+ fs.mkdirSync(dirPath, {recursive: true});
59
+ }
60
+ }
61
+
62
+ // Utility function to wait until file exists and is not empty
63
+ async waitForFile(filePath, timeout = 10000) {
64
+ const start = Date.now();
65
+ while (true) {
66
+ if (fs.existsSync(filePath)) {
67
+ const stats = fs.statSync(filePath);
68
+ if (stats.size > 0) {
69
+ return;
70
+ }
71
+ }
72
+ if (Date.now() - start > timeout) {
73
+ throw new Error(`Timeout: Video file at ${filePath} not finalized`);
74
+ }
75
+ await new Promise(resolve => setTimeout(resolve, 500));
76
+ }
77
+ }
78
+
79
+ deleteFile(filePath) {
80
+ if(!fs.existsSync(filePath)) {
81
+ return;
82
+ }
83
+ fsExtra.removeSync(filePath);
84
+ }
85
+
86
+ async pngToBase64(filePath) {
87
+ const imageBuffer = fs.readFileSync(filePath);
88
+ return imageBuffer.toString('base64');
89
+ }
90
+
91
+ async readJSONContent(filePath, create = false) {
92
+ if(!fs.existsSync(filePath)) {
93
+ if (create) {
94
+ this.createParentIfNotPresent(filePath);
95
+ fs.writeFileSync(filePath, '{}');
96
+ }
97
+ return {};
98
+ }
99
+ const data = fs.readFileSync(filePath, {encoding: 'utf-8'});
100
+ try {
101
+ return JSON.parse(data);
102
+ } catch (error) {
103
+ throw {
104
+ message: `Error parsing JSON from file ${filePath}: ${error.message}`
105
+ };
106
+ }
107
+ }
108
+
109
+ async writeJSONContent(filePath, content) {
110
+ this.createParentIfNotPresent(filePath);
111
+ try {
112
+ const jsonContent = JSON.stringify(content, null, 2);
113
+ fs.writeFileSync(filePath, jsonContent);
114
+ } catch (error) {
115
+ throw {
116
+ message: `Error writing JSON to file ${filePath}: ${error.message}`
117
+ };
118
+ }
119
+ }
120
+
121
+ getFileSizeInMB(filePath) {
122
+ filePath = this.getFilePath(filePath);
123
+ return fs.statSync(filePath).size / 1024 / 1024;
124
+ }
125
+ }
126
+
127
+ exports.FileUtils = FileUtils;
@@ -0,0 +1,104 @@
1
+ const { Logger } = require("../../utils/logger");
2
+ const _ = require('lodash');
3
+
4
+ class JsonUtils {
5
+
6
+ constructor(caller, blacklistedLogs) {
7
+ this.caller = caller;
8
+ this.blacklistedLogs = blacklistedLogs;
9
+ }
10
+
11
+ setCaller(caller) {
12
+ this.caller = caller;
13
+ }
14
+
15
+ setBlacklistedLogs(blacklistedLogs) {
16
+ this.blacklistedLogs = blacklistedLogs;
17
+ }
18
+
19
+ compareJson(obj1, obj2) {
20
+ if (_.isEqual(obj1, obj2)) {
21
+ return {success: true};
22
+ } else {
23
+ const differences = [];
24
+
25
+ function findDifferences(path, val1, val2) {
26
+ if (_.isEqual(val1, val2)) return;
27
+
28
+ if (_.isObject(val1) && _.isObject(val2)) {
29
+ const allKeys = _.union(_.keys(val1), _.keys(val2));
30
+ allKeys.forEach(key => {
31
+ if(!val1.hasOwnProperty(key)) {
32
+ differences.push({ path: `${path}.${key}`, value1: "key not found", value2: val2[key]});
33
+ return;
34
+ }
35
+ if(!val2.hasOwnProperty(key)) {
36
+ differences.push({ path: `${path}.${key}`, value1: val1[key], value2: "key not found"});
37
+ return;
38
+ }
39
+ findDifferences(`${path}.${key}`, val1[key], val2[key]);
40
+ });
41
+ } else {
42
+ differences.push({
43
+ path: `${path}`,
44
+ value1: val1,
45
+ value2: val2
46
+ });
47
+ }
48
+ }
49
+
50
+ findDifferences('root', obj1, obj2);
51
+
52
+ return {success: false, differences};
53
+ }
54
+ }
55
+
56
+ compare(json1, json2) {
57
+ const logLabel = `Json.Compare`;
58
+ const logger = new Logger(this.caller, this.blacklistedLogs);
59
+ logger.startLog(logLabel);
60
+ const result = this.compareJson(json1, json2);
61
+ if(!result.success) {
62
+ logger.log(logLabel, "JSON objects are not equal. Differences:");
63
+ result?.differences?.forEach(diff => {
64
+ logger.log(logLabel, `-> Path: ${diff.path}, Value1: ${JSON.stringify(diff.value1)}, Value2: ${JSON.stringify(diff.value2)}`);
65
+ });
66
+ logger.failureLog(logLabel);
67
+ throw {message: 'JSON objects are not equal'};
68
+ }
69
+ logger.endLog(logLabel);
70
+ }
71
+
72
+ is_equal(json1, json2) {
73
+ const logLabel = `Json.Is Equal`;
74
+ const logger = new Logger(this.caller, this.blacklistedLogs);
75
+ logger.startLog(logLabel);
76
+ const result = _.isEqual(json1, json2);
77
+ logger.log(logLabel, `JSON objects equality: ${result}`);
78
+ logger.endLog(logLabel);
79
+ return result;
80
+ }
81
+
82
+ is_valid_json(text) {
83
+ const logLabel = `Json.Is Valid JSON`;
84
+ const logger = new Logger(this.caller, this.blacklistedLogs);
85
+ logger.startLog(logLabel);
86
+ if(!_.isString(text) || !text.trim()) {
87
+ logger.log(logLabel, 'Input is empty - not a valid JSON string');
88
+ logger.endLog(logLabel);
89
+ return false;
90
+ }
91
+ try {
92
+ JSON.parse(text);
93
+ logger.endLog(logLabel);
94
+ return true;
95
+ } catch (ex) {
96
+ logger.log(logLabel, `Input is not a valid JSON string - ${ex}`);
97
+ logger.endLog(logLabel);
98
+ return false;
99
+ }
100
+ }
101
+
102
+ }
103
+
104
+ exports.JsonUtils = JsonUtils;
@@ -0,0 +1,283 @@
1
+ const { Logger } = require("../../utils/logger");
2
+ const _ = require('lodash');
3
+
4
+ class MathUtils {
5
+
6
+ constructor(caller, blacklistedLogs) {
7
+ this.caller = caller;
8
+ this.blacklistedLogs = blacklistedLogs;
9
+ }
10
+
11
+ setCaller(caller) {
12
+ this.caller = caller;
13
+ }
14
+
15
+ setBlacklistedLogs(blacklistedLogs) {
16
+ this.blacklistedLogs = blacklistedLogs;
17
+ }
18
+
19
+ round_number(number, digits) {
20
+ const logLabel = `Math.Round number ${number} to decimal places ${digits}`;
21
+ const logger = new Logger(this.caller, this.blacklistedLogs);
22
+ logger.startLog(logLabel);
23
+ const result = _.round(number, digits);
24
+ logger.endLog(logLabel);
25
+ return result;
26
+ }
27
+
28
+ random_number(min, max) {
29
+ const logLabel = `Math.Random number between ${min} and ${max}`;
30
+ const logger = new Logger(this.caller, this.blacklistedLogs);
31
+ logger.startLog(logLabel);
32
+ const result = Math.floor(Math.random() * (max - min + 1)) + min;
33
+ logger.endLog(logLabel);
34
+ return result;
35
+ }
36
+
37
+ truncate_number(number, digits) {
38
+ const logLabel = `Math.Truncate number ${number} to decimal places ${digits}`;
39
+ const logger = new Logger(this.caller, this.blacklistedLogs);
40
+ logger.startLog(logLabel);
41
+ const factor = Math.pow(10, digits);
42
+ const result = Math.trunc(number * factor) / factor;
43
+ logger.endLog(logLabel);
44
+ return result;
45
+ }
46
+
47
+ absolute_value(number) {
48
+ const logLabel = `Math.Absolute value of ${number}`;
49
+ const logger = new Logger(this.caller, this.blacklistedLogs);
50
+ logger.startLog(logLabel);
51
+ const result = Math.abs(number);
52
+ logger.endLog(logLabel);
53
+ return result;
54
+ }
55
+
56
+ max(numbers) {
57
+ const logLabel = `Math.Max of ${numbers.join(', ')}`;
58
+ const logger = new Logger(this.caller, this.blacklistedLogs);
59
+ logger.startLog(logLabel);
60
+ const result = _.max(numbers);
61
+ logger.endLog(logLabel);
62
+ return result;
63
+ }
64
+
65
+ min(numbers) {
66
+ const logLabel = `Math.Min of ${numbers.join(', ')}`;
67
+ const logger = new Logger(this.caller, this.blacklistedLogs);
68
+ logger.startLog(logLabel);
69
+ const result = _.min(numbers);
70
+ logger.endLog(logLabel);
71
+ return result;
72
+ }
73
+
74
+ sum(numbers) {
75
+ const logLabel = `Math.Sum of ${numbers.join(', ')}`;
76
+ const logger = new Logger(this.caller, this.blacklistedLogs);
77
+ logger.startLog(logLabel);
78
+ const result = _.sum(numbers);
79
+ logger.endLog(logLabel);
80
+ return result;
81
+ }
82
+
83
+ average(numbers) {
84
+ const logLabel = `Math.Average of ${numbers.join(', ')}`;
85
+ const logger = new Logger(this.caller, this.blacklistedLogs);
86
+ logger.startLog(logLabel);
87
+ const result = numbers?.length === 0 ? 0
88
+ : _.sum(numbers) / numbers.length;
89
+ logger.endLog(logLabel);
90
+ return result;
91
+ }
92
+
93
+ text_to_number(str) {
94
+ const logLabel = `Math.Text to number ${str}`;
95
+ const logger = new Logger(this.caller, this.blacklistedLogs);
96
+ logger.startLog(logLabel);
97
+ let cleanedStr = _.replace(str, /,/g, '');
98
+ const num = parseFloat(cleanedStr);
99
+ if (_.isNaN(num)) {
100
+ logger.failureLog(logLabel);
101
+ throw {message: 'Not a valid number!'};
102
+ }
103
+ logger.endLog(logLabel);
104
+ return num;
105
+ }
106
+
107
+ currency_format(number, currencyCode) {
108
+ const logLabel = `Math.Format currency ${number} currency code ${currencyCode}`;
109
+ const logger = new Logger(this.caller, this.blacklistedLogs);
110
+ logger.startLog(logLabel);
111
+ const result = this.formatToCurrency(logLabel, number, currencyCode);
112
+ logger.endLog(logLabel);
113
+ return result;
114
+ }
115
+
116
+ currency_format_locale(number, currencyCode, locale) {
117
+ const logLabel = `Math.Format currency ${number} currency code ${currencyCode} in locale ${locale}`;
118
+ const logger = new Logger(this.caller, this.blacklistedLogs);
119
+ logger.startLog(logLabel);
120
+ const result = this.formatToCurrency(logLabel, number, currencyCode, locale);
121
+ logger.endLog(logLabel);
122
+ return result;
123
+ }
124
+
125
+ formatToCurrency(logLabel, number, currencyCode, locale = 'en-US') {
126
+ if(!this.isValidCurrencyCode(currencyCode)) {
127
+ logLabel && new Logger(this.caller, this.blacklistedLogs).failureLog(logLabel);
128
+ throw {message: `${currencyCode} is not a valid currency code!`};
129
+ }
130
+ if(!this.isValidLocale(locale)) {
131
+ logLabel && new Logger(this.caller, this.blacklistedLogs).failureLog(logLabel);
132
+ throw {message: `${locale} is not a valid locale!`};
133
+ }
134
+ if(_.isNaN(number)) {
135
+ logLabel && new Logger(this.caller, this.blacklistedLogs).failureLog(logLabel);
136
+ throw {message: `${number} is not a valid number!`};
137
+ }
138
+ return new Intl.NumberFormat(locale, {
139
+ style: 'currency',
140
+ currency: currencyCode
141
+ }).format(number);
142
+ }
143
+
144
+ isValidLocale(locale) {
145
+ if (!_.isString(locale) || _.isEmpty(locale)) {
146
+ return false;
147
+ }
148
+ try {
149
+ new Intl.NumberFormat(locale);
150
+ return true;
151
+ } catch (e) {
152
+ return false;
153
+ }
154
+ }
155
+
156
+ isValidCurrencyCode(currencyCode) {
157
+ if (!_.isString(currencyCode) || _.isEmpty(currencyCode)) {
158
+ return false;
159
+ }
160
+ try {
161
+ new Intl.NumberFormat('en-US', { style: 'currency', currency: currencyCode }).format(1);
162
+ return true;
163
+ } catch (e) {
164
+ return false;
165
+ }
166
+ }
167
+
168
+ advanced_number_format(params) {
169
+ const logLabel = `Advanced number formatting`;
170
+ const logger = new Logger(this.caller, this.blacklistedLogs);
171
+ logger.startLog(logLabel);
172
+ const result = this.formatNumber(params, logLabel);
173
+ logger.endLog(logLabel);
174
+ return result;
175
+ }
176
+
177
+ custom_formula(params) {
178
+ const logLabel = `Custom Math Formula`;
179
+ const logger = new Logger(this.caller, this.blacklistedLogs);
180
+ logger.startLog(logLabel);
181
+ const value = this.evaluateFormula(params, logLabel);
182
+ const result = this.formatNumber({...params, number: value}, logLabel, true);
183
+ logger.endLog(logLabel);
184
+ return result;
185
+ }
186
+
187
+ custom_formula_with_format(params) {
188
+ const logLabel = `Custom Math Formula + Formatting`;
189
+ const logger = new Logger(this.caller, this.blacklistedLogs);
190
+ logger.startLog(logLabel);
191
+ const value = this.evaluateFormula(params, logLabel);
192
+ const result = this.formatNumber({...params, number: value}, logLabel);
193
+ logger.endLog(logLabel);
194
+ return result;
195
+ }
196
+
197
+ formatNumber({
198
+ number,
199
+ absoluteValue = false,
200
+ round = null,
201
+ truncate = null,
202
+ currencyCode = null,
203
+ locale = null,
204
+ prefix = '',
205
+ suffix = '',
206
+ }, logLabel, returnNumber) {
207
+ if(_.isNaN(number)) {
208
+ logLabel && new Logger(this.caller, this.blacklistedLogs).failureLog(logLabel);
209
+ throw {message: `${number} is not a valid number!`};
210
+ }
211
+
212
+ if (absoluteValue === true || (_.isString(absoluteValue) && absoluteValue.toLowerCase() === "true")) {
213
+ number = Math.abs(number);
214
+ }
215
+
216
+ if (round != null && (!_.isString(round) || round.trim() !== '') && !_.isNaN(Number(round))) {
217
+ number = _.round(number, Number(round));
218
+ }
219
+
220
+ if (truncate != null && (!_.isString(truncate) || truncate.trim() !== '') && !_.isNaN(Number(truncate))) {
221
+ const factor = Math.pow(10, Number(truncate));
222
+ number = Math.trunc(number * factor) / factor;
223
+ }
224
+
225
+ if(returnNumber) {
226
+ return number;
227
+ }
228
+
229
+ if(currencyCode?.trim() && !this.isValidCurrencyCode(currencyCode)) {
230
+ logLabel && new Logger(this.caller, this.blacklistedLogs).failureLog(logLabel);
231
+ throw {message: `${currencyCode} is not a valid currency code!`};
232
+ }
233
+ if(locale?.trim() && !this.isValidLocale(locale)) {
234
+ logLabel && new Logger(this.caller, this.blacklistedLogs).failureLog(logLabel);
235
+ throw {message: `${locale} is not a valid locale!`};
236
+ }
237
+
238
+ let formattedValue;
239
+ if (currencyCode) {
240
+ if (locale) {
241
+ formattedValue = new Intl.NumberFormat(locale, {
242
+ style: 'currency',
243
+ currency: currencyCode
244
+ }).format(number);
245
+ } else {
246
+ formattedValue = new Intl.NumberFormat(undefined, {
247
+ style: 'currency',
248
+ currency: currencyCode
249
+ }).format(number);
250
+ }
251
+ } else {
252
+ if (locale) {
253
+ formattedValue = new Intl.NumberFormat(locale).format(number);
254
+ } else {
255
+ formattedValue = number.toString();
256
+ }
257
+ }
258
+
259
+ formattedValue = `${prefix}${formattedValue}${suffix}`;
260
+
261
+ return formattedValue;
262
+ }
263
+
264
+ evaluateFormula(params, logLabel) {
265
+ let formula = params.formula;
266
+ formula = formula.replaceAll('^', '**');
267
+ if(params?.savedArgs) {
268
+ for (const [key, value] of Object.entries(params.savedArgs)) {
269
+ const regex = new RegExp(`\\b${key}\\b`, 'g');
270
+ formula = formula.replace(regex, value);
271
+ }
272
+ }
273
+ try {
274
+ return eval(formula);
275
+ } catch (ex) {
276
+ logLabel && new Logger(this.caller, this.blacklistedLogs).failureLog(logLabel);
277
+ throw {message: `Error evaluating formula! "${params.formula}" ${ex}`};
278
+ }
279
+ }
280
+
281
+ }
282
+
283
+ exports.MathUtils = MathUtils;