@devassure/tpp-engine 0.0.0-stage → 1.8.1

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 (69) hide show
  1. package/DEV_README.md +19 -0
  2. package/README.md +6 -3
  3. package/index.js +121 -0
  4. package/package.json +30 -4
  5. package/src/accessibility/combined-a11y-report.js +208 -0
  6. package/src/accessibility/tpp-a11y.js +81 -0
  7. package/src/actions.js +3201 -0
  8. package/src/api/api-auth.js +18 -0
  9. package/src/api/api-service.js +289 -0
  10. package/src/app-config.json +7 -0
  11. package/src/autoheal-actions.js +309 -0
  12. package/src/console-log/console-log-manager.js +39 -0
  13. package/src/console-log/console-log-utils.js +201 -0
  14. package/src/database/mysql/mysql-service.js +23 -0
  15. package/src/database/neo4j/neo4j-service.js +32 -0
  16. package/src/database/oracle/oracle-service.js +48 -0
  17. package/src/database/pg/pg-service.js +29 -0
  18. package/src/downloads/downloads-manager.js +86 -0
  19. package/src/downloads/downloads-utils.js +113 -0
  20. package/src/globalsetup.js +68 -0
  21. package/src/globalteardown.js +57 -0
  22. package/src/hooks/each-hook.js +45 -0
  23. package/src/hooks/hook-utils.js +21 -0
  24. package/src/library/authenticator/authenticator-utils.js +43 -0
  25. package/src/library/csv/csv-utils.js +297 -0
  26. package/src/library/date/date-utils.js +168 -0
  27. package/src/library/emailTesting/TokenConfig.js +46 -0
  28. package/src/library/emailTesting/emailTesting-utils.js +527 -0
  29. package/src/library/excel/excel-utils.js +922 -0
  30. package/src/library/file/file-action-utils.js +129 -0
  31. package/src/library/file/file-utils.js +127 -0
  32. package/src/library/json/json-utils.js +104 -0
  33. package/src/library/math/math-utils.js +283 -0
  34. package/src/library/softAssert/softAssert-utils.js +154 -0
  35. package/src/library/table/table-utils.js +92 -0
  36. package/src/library/text/text-utils.js +45 -0
  37. package/src/library/tpp-library.js +81 -0
  38. package/src/library/web/slider-utils.js +66 -0
  39. package/src/mobile/appium/appium-manager.js +158 -0
  40. package/src/mobile/device-cloud/browserstack-service.js +44 -0
  41. package/src/mobile/device-cloud/commands/browserstack-commands.js +20 -0
  42. package/src/mobile/device-cloud/commands/device-cloud-commands.js +17 -0
  43. package/src/mobile/device-cloud/device-cloud-service.js +29 -0
  44. package/src/mobile/device-cloud/pcloudy-service.js +65 -0
  45. package/src/mobile/mobile-actions.js +1568 -0
  46. package/src/mobile/recorder-utils/css-selector-generator.js +2 -0
  47. package/src/mobile/recorder-utils/finder.js +752 -0
  48. package/src/mobile/recorder-utils/mobile-recorder.js +266 -0
  49. package/src/mobile/web-driver-utils.js +110 -0
  50. package/src/models/tpp-element.js +58 -0
  51. package/src/models/tpp-mobile-element.js +50 -0
  52. package/src/network-log/network-log-manager.js +96 -0
  53. package/src/network-log/network-log-utils.js +361 -0
  54. package/src/types/elements.types.js +24 -0
  55. package/src/utils/ai-utils.js +48 -0
  56. package/src/utils/assert-utils.js +50 -0
  57. package/src/utils/autoheal-utils.js +9 -0
  58. package/src/utils/common-utils.js +120 -0
  59. package/src/utils/cookie-utils.js +70 -0
  60. package/src/utils/json-utils.js +10 -0
  61. package/src/utils/log-utils.js +10 -0
  62. package/src/utils/logger.js +76 -0
  63. package/src/utils/screenshot-utils.js +42 -0
  64. package/src/utils/token-utils.js +18 -0
  65. package/src/utils/vectordb-utils.js +125 -0
  66. package/src/visual-test/tpp-visual-test.js +122 -0
  67. package/src/web/web-executor-agent.js +149 -0
  68. package/src/web/web-executor-data-fetch.js +353 -0
  69. package/test-excel-utils.js +570 -0
@@ -0,0 +1,45 @@
1
+ const { importGetHooks } = require("./hook-utils");
2
+
3
+ class EachTestHook {
4
+
5
+ constructor(actions) {
6
+ this.actions = actions;
7
+ }
8
+
9
+ async runBeforeEach() {
10
+ const getHooks = importGetHooks();
11
+ if(!getHooks) return;
12
+
13
+ const actions = this.actions.newChild('BeforeEach');
14
+ const hooks = getHooks(actions);
15
+ if(!hooks?.find(hook => hook?.beforeEach)) return;
16
+
17
+ actions.startLog('');
18
+ for (let i = 0; i < hooks.length; i++) {
19
+ const hook = hooks[i];
20
+ hook?.beforeEach && await hook.beforeEach();
21
+ }
22
+
23
+ actions.endLog('');
24
+ }
25
+
26
+ async runAfterEach() {
27
+ const getHooks = importGetHooks();
28
+ if(!getHooks) return;
29
+
30
+ const actions = this.actions.newChild('AfterEach');
31
+ const hooks = getHooks(actions);
32
+ if(!hooks?.find(hook => hook?.afterEach)) return;
33
+
34
+ actions.startLog('');
35
+ for (let i = 0; i < hooks.length; i++) {
36
+ const hook = hooks[i];
37
+ hook?.afterEach && await hook.afterEach();
38
+ }
39
+
40
+ actions.endLog('');
41
+ }
42
+
43
+ }
44
+
45
+ exports.EachTestHook = EachTestHook;
@@ -0,0 +1,21 @@
1
+ const { chromium } = require('@playwright/test');
2
+
3
+ const openBrowser = async () => {
4
+ const browser = await chromium.launch();
5
+ const context = await browser.newContext();
6
+ const page = await context.newPage();
7
+ // await page.waitForTimeout(120000);
8
+ const { Actions } = require('../actions');
9
+ return new Actions(page, page.request, true);
10
+ };
11
+
12
+ const importGetHooks = () => {
13
+ try {
14
+ const { getAllHooks } = require('../../../../../custom-functions/hooks/tpp_all_hooks');
15
+ return getAllHooks;
16
+ } catch (_ex) {}
17
+ return undefined;
18
+ }
19
+
20
+ exports.openBrowser = openBrowser;
21
+ exports.importGetHooks = importGetHooks;
@@ -0,0 +1,43 @@
1
+ const {authenticator} = require("otplib");
2
+ const { Logger } = require("../../utils/logger");
3
+
4
+ class AuthenticatorUtils {
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
+ async get_authenticator_otp(secret) {
20
+ const logLabel = `Get Authenticator OTP from secret`;
21
+ const logger = new Logger(this.caller, this.blacklistedLogs);
22
+ logger.startLog(logLabel);
23
+ if(!secret?.trim()) {
24
+ logger.failureLog(logLabel);
25
+ throw {message: "Secret to generate OTP is empty"};
26
+ }
27
+ secret = secret.replace(/ /g, ""); // Remove spaces if any
28
+ const now = Math.floor(Date.now() / 1000);
29
+ const remaining = 30 - (now % 30);
30
+ if(remaining < 5) await new Promise(r => setTimeout(r, (remaining + 1) * 1000));
31
+ try {
32
+ let otp = authenticator.generate(secret);
33
+ logger.endLog(logLabel);
34
+ return otp;
35
+ } catch (error) {
36
+ logger.failureLog(logLabel, error);
37
+ throw {message: error.message || "Failed to generate OTP"};
38
+ }
39
+ }
40
+
41
+ }
42
+
43
+ exports.AuthenticatorUtils = AuthenticatorUtils;
@@ -0,0 +1,297 @@
1
+ const fs = require('fs');
2
+ const { Logger } = require('../../utils/logger');
3
+ const path = require('path');
4
+ const { FileUtils } = require('../file/file-utils');
5
+ const { isNumber, getAnyElements, getElementsInRange, getElementsAtIndices, getLastElements, getBoolean, getFirstElements } = require('../../utils/common-utils');
6
+ const { TableUtils } = require('../table/table-utils');
7
+
8
+ class CsvUtils {
9
+
10
+ constructor(caller, blacklistedLogs) {
11
+ this.caller = caller;
12
+ this.blacklistedLogs = blacklistedLogs;
13
+ }
14
+
15
+ setCaller(caller) {
16
+ this.caller = caller;
17
+ }
18
+
19
+ setBlacklistedLogs(blacklistedLogs) {
20
+ this.blacklistedLogs = blacklistedLogs;
21
+ }
22
+
23
+ compare_data(filePath1, filePath2) {
24
+ const logLabel = `CSV.Compare rows of ${filePath1} and ${filePath2}`;
25
+ const logger = new Logger(this.caller, this.blacklistedLogs);
26
+ logger.startLog(logLabel);
27
+ const lines1 = this.read_csv_lines(filePath1, logLabel).filter(line => !!line);
28
+ const lines2 = this.read_csv_lines(filePath2, logLabel).filter(line => !!line);
29
+
30
+ if(lines1.length !== lines2.length) {
31
+ let message = `The number of rows in the 2 files are different! File 1 has ${lines1.length} rows and File 2 has ${lines2.length} rows`;
32
+ logger.failureLog(logLabel);
33
+ throw {message};
34
+ }
35
+
36
+ const data1 = lines1.map(line => line.split(','));
37
+ const data2 = lines2.map(line => line.split(','));
38
+
39
+ let failures = [];
40
+
41
+ data1.forEach((dataRow, index) => {
42
+ let row1 = JSON.stringify(dataRow);
43
+ let row2 = JSON.stringify(data2.at(index));
44
+ if(row1 !== row2) {
45
+ let message = `❌ Row ${index} is different!`;
46
+ failures.push(message);
47
+ logger.log(logLabel, message);
48
+ logger.log(logLabel, `Row from 1st File: ${row1}`);
49
+ logger.log(logLabel, `Row from 2nd File: ${row2}`);
50
+ }
51
+ })
52
+
53
+ if(failures.length) {
54
+ logger.failureLog(logLabel);
55
+ throw {message: `There are ${failures.length} rows that do not match in the 2 files!`}
56
+ }
57
+
58
+ logger.endLog(logLabel);
59
+ }
60
+
61
+ read_rows_as_array(csvPath) {
62
+ const logLabel = `CSV.Read rows as array ${csvPath}`;
63
+ const logger = new Logger(this.caller, this.blacklistedLogs);
64
+ logger.startLog(logLabel);
65
+ const lines = this.read_csv_lines(csvPath, logLabel);
66
+ const data = lines.map(line => line.split(','));
67
+ logger.endLog(logLabel);
68
+ return data;
69
+ }
70
+
71
+ read_rows_as_object(csvPath) {
72
+ const logLabel = `CSV.Read rows as objects ${csvPath}`;
73
+ const logger = new Logger(this.caller, this.blacklistedLogs)
74
+ logger.startLog(logLabel);
75
+ const lines = this.read_csv_lines(csvPath, logLabel);
76
+ if(lines.length < 1) {
77
+ logger.failureLog(logLabel);
78
+ throw {message: `No rows or header present in CSV`};
79
+ }
80
+ const fields = lines[0].split(',').map(field => field.trim());
81
+ const dataLines = lines.slice(1);
82
+ const data = dataLines.map(line => {
83
+ const cells = line.split(',');
84
+ let row = {};
85
+ for(let i=0; i < fields.length; i++) {
86
+ if(!fields[i]) continue;
87
+ row[`${fields[i]}`] = i < cells.length ? cells[i] || '' : '';
88
+ }
89
+ return row;
90
+ })
91
+ logger.endLog(logLabel);
92
+ return data;
93
+ }
94
+
95
+ advanced_csv_read(params) {
96
+ const logLabel = `CSV.Advanced Read`;
97
+ const logger = new Logger(this.caller, this.blacklistedLogs);
98
+ logger.startLog(logLabel);
99
+ if(!params.filePath?.trim()) {
100
+ logger.failureLog(logLabel);
101
+ throw {message: `No file path provided!`}
102
+ }
103
+ const lines = this.read_csv_lines(params.filePath, logLabel);
104
+
105
+ const ignore = getBoolean(params.ignoreMissingData);
106
+ const isFirstRowHeader = getBoolean(params.isFirstRowHeader);
107
+ const getRowsAsObjects = getBoolean(params.getRowsAsObjects);
108
+
109
+ //Split header if first row is header
110
+ let firstRowHeader = isFirstRowHeader && lines?.length > 0;
111
+ let fields = firstRowHeader
112
+ ? lines[0].split(',').map(field => field.trim())
113
+ : lines[0].split(',').map((_line, index) => index);
114
+
115
+ let allFields = fields.slice();
116
+
117
+ let dataLines = isFirstRowHeader && lines?.length > 0 ? lines.slice(1) : lines;
118
+
119
+ //Filter rows
120
+ if(!!params.rows && params.rows !== 'all') {
121
+ if(params.rows.startsWith('last')) {
122
+ const count = params.rows.replaceAll('last', '').trim();
123
+ if(!isNumber(count)) {
124
+ logger.failureLog(logLabel);
125
+ throw {message: `${params.rows} is not a valid rows filter!`}
126
+ }
127
+ const data = getLastElements(dataLines, parseInt(count), ignore);
128
+ if(!data.success) {
129
+ logger.failureLog(logLabel);
130
+ throw {message: data.error};
131
+ }
132
+ dataLines = data.data;
133
+ } else if(params.rows.startsWith('first')) {
134
+ const count = params.rows.replaceAll('first', '').trim();
135
+ if(!isNumber(count)) {
136
+ logger.failureLog(logLabel);
137
+ throw {message: `${params.rows} is not a valid rows filter!`}
138
+ }
139
+ const data = getFirstElements(dataLines, parseInt(count), ignore);
140
+ if(!data.success) {
141
+ logger.failureLog(logLabel);
142
+ throw {message: data.error};
143
+ }
144
+ dataLines = data.data;
145
+ } else if(params.rows.startsWith('any')) {
146
+ const count = params.rows.replaceAll('any', '').trim();
147
+ if(!isNumber(count)) {
148
+ logger.failureLog(logLabel);
149
+ throw {message: `${params.rows} is not a valid rows filter!`}
150
+ }
151
+ const data = getAnyElements(dataLines, parseInt(count), ignore);
152
+ if(!data.success) {
153
+ logger.failureLog(logLabel);
154
+ throw {message: data.error};
155
+ }
156
+ dataLines = data.data;
157
+ } else if(params.rows.includes('-')) {
158
+ const data = getElementsInRange(dataLines, params.rows, ignore);
159
+ if(!data.success) {
160
+ logger.failureLog(logLabel);
161
+ throw {message: data.error};
162
+ }
163
+ dataLines = data.data;
164
+ } else if(params.rows.includes(',')) {
165
+ const data = getElementsAtIndices(dataLines, params.rows, ignore);
166
+ if(!data.success) {
167
+ logger.failureLog(logLabel);
168
+ throw {message: data.error};
169
+ }
170
+ dataLines = data.data;
171
+ } else {
172
+ if(!isNumber(params.rows)) {
173
+ logger.failureLog(logLabel);
174
+ throw {message: `${params.rows} is not a valid rows filter!`}
175
+ }
176
+ const index = parseInt(params.rows.trim());
177
+ if(index < 0 || (index >= dataLines.length && !ignore)) {
178
+ logger.failureLog(logLabel);
179
+ throw {message: `${params.rows} - index is invalid. Data size - ${dataLines.length}`}
180
+ }
181
+ if(ignore && index >= dataLines.length) {
182
+ dataLines = [];
183
+ } else {
184
+ dataLines = [dataLines[index]];
185
+ }
186
+ }
187
+ }
188
+
189
+ //Filter columns
190
+ fields = new TableUtils(this.caller, this.blacklistedLogs, logLabel)
191
+ .filterColumns(params, fields, ignore, firstRowHeader);
192
+
193
+ const dataObject = dataLines.map(line => {
194
+ const cells = line.split(',');
195
+ let row = getRowsAsObjects ? {} : [];
196
+ for(let i=0; i < allFields.length; i++) {
197
+ const skip = firstRowHeader
198
+ ? !fields.includes(allFields[i])
199
+ : !fields.includes(i);
200
+ if(skip) continue;
201
+ const val = i < cells.length ? cells[i] || '' : '';
202
+ if(getRowsAsObjects) {
203
+ const fieldName = firstRowHeader ? allFields[i] : `column_${i}`;
204
+ row[`${fieldName}`] = val;
205
+ } else {
206
+ row.push(val);
207
+ }
208
+ }
209
+ return row;
210
+ })
211
+ logger.endLog(logLabel);
212
+ return dataObject;
213
+ }
214
+
215
+ advanced_csv_write(params) {
216
+ const logLabel = `CSV.Advanced Write`;
217
+ const logger = new Logger(this.caller, this.blacklistedLogs)
218
+ logger.startLog(logLabel);
219
+
220
+ if (!params.data || !Array.isArray(params.data)) {
221
+ logger.failureLog(logLabel);
222
+ throw {message: `Not a valid data! Data should be an array.`}
223
+ }
224
+
225
+ const createMissingFile = getBoolean(params.createMissingFile);
226
+ const isRowsAsObjects = getBoolean(params.isRowsAsObjects);
227
+
228
+ let csvPath = params.filePath;
229
+ csvPath = new FileUtils().getFilePath(csvPath);
230
+ if(!fs.existsSync(csvPath) && !createMissingFile) {
231
+ logger.failureLog(logLabel);
232
+ throw {message: `CSV file ${csvPath} does not exists!`}
233
+ }
234
+
235
+ let lines = "";
236
+
237
+ //Write header line
238
+ if(getBoolean(params.isFirstRowHeader)) {
239
+ if(!isRowsAsObjects && !params.headers?.length) {
240
+ logger.failureLog(logLabel);
241
+ throw {message: `headers not present! Headers are mandatroy when isFirstRowHeader and isRowsAsObjects is false`}
242
+ }
243
+ if(!params.headers?.length && !params.data?.length) {
244
+ logger.failureLog(logLabel);
245
+ throw {message: `No headers present! isFirstRowHeader is true but headers and data feilds are empty.`}
246
+ }
247
+ lines += params.headers?.length || !isRowsAsObjects
248
+ ? params.headers.join(",")
249
+ : Object.keys(params.data[0]).join(",");
250
+ lines += '\n';
251
+ }
252
+
253
+ const headers = isRowsAsObjects
254
+ ? params.headers?.length ? params.headers : Object.keys(params.data[0])
255
+ : undefined
256
+
257
+ //Transform row to string line
258
+ params.data.forEach((row, i) => {
259
+ if (!row || (!isRowsAsObjects && !Array.isArray(row))) {
260
+ logger.failureLog(logLabel);
261
+ throw {message: `Row ${i} is not a valid row!.`}
262
+ }
263
+ const newLine = i === 0 ? '' : '\n';
264
+ if(!isRowsAsObjects) {
265
+ lines += `${newLine}${row.join(',')}`;
266
+ } else {
267
+ lines += `${newLine}${headers.map(header => row[header] || '').join(',')}`;
268
+ }
269
+ });
270
+
271
+ //Create parent
272
+ if(!fs.existsSync(csvPath) && createMissingFile) {
273
+ new FileUtils().createParentIfNotPresent(csvPath);
274
+ }
275
+
276
+ fs.writeFileSync(csvPath, lines);
277
+
278
+ logger.endLog(logLabel);
279
+ return path.resolve(csvPath);
280
+ }
281
+
282
+
283
+ read_csv_lines(csvPath, logLabel) {
284
+ const logger = new Logger(this.caller, this.blacklistedLogs);
285
+ csvPath = new FileUtils().getFilePath(csvPath);
286
+ if(!fs.existsSync(csvPath)) {
287
+ logger.failureLog(logLabel);
288
+ throw {message: `CSV file ${csvPath} does not exists!`}
289
+ }
290
+ const content = fs.readFileSync(csvPath, {encoding: 'utf-8'})?.replaceAll('\r', '');
291
+ const lines = content.split("\n");
292
+ return lines;
293
+ }
294
+
295
+ }
296
+
297
+ exports.CsvUtils = CsvUtils;
@@ -0,0 +1,168 @@
1
+ const { Logger } = require("../../utils/logger");
2
+ const _ = require('lodash');
3
+
4
+ const dayjs = require("dayjs");
5
+ const customParseFormat = require("dayjs/plugin/customParseFormat");
6
+ dayjs.extend(customParseFormat);
7
+
8
+ class DateUtils {
9
+
10
+ static DEFAULT_FORMAT = "YYYY-MM-DD";
11
+
12
+ constructor(caller, blacklistedLogs) {
13
+ this.caller = caller;
14
+ this.blacklistedLogs = blacklistedLogs;
15
+ }
16
+
17
+ setCaller(caller) {
18
+ this.caller = caller;
19
+ }
20
+
21
+ setBlacklistedLogs(blacklistedLogs) {
22
+ this.blacklistedLogs = blacklistedLogs;
23
+ }
24
+
25
+ relative_date_from_current_date(days, format) {
26
+ const logger = new Logger(this.caller, this.blacklistedLogs);
27
+
28
+ let safeDays = Number(days);
29
+ if (!Number.isFinite(safeDays)) safeDays = 0;
30
+
31
+ const safeFormat = this.#ensureValidFormat(format);
32
+ const logLabel = `Date.Get date ${safeDays} days from current date in format ${safeFormat}`;
33
+
34
+ try {
35
+ logger.startLog(logLabel);
36
+
37
+ // Reuse getTargetDate method for date calculation
38
+ const targetDate = this.getTargetDate({
39
+ offsetDays: safeDays,
40
+ format: safeFormat
41
+ });
42
+
43
+ const resultDate = targetDate.format(safeFormat);
44
+ logger.endLog(logLabel);
45
+ return resultDate;
46
+ } catch (err) {
47
+ try {
48
+ logger.endLog(logLabel);
49
+ } catch (_) {}
50
+ throw new Error(`relative_date_from_current_date failed: ${err.message}`);
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Parse a date string from various formats
56
+ * @param {string} dateStr - Date string to parse
57
+ * @param {string} format - Expected format (default: 'MM/DD/YYYY')
58
+ * @returns {Object} dayjs object representing the parsed date
59
+ * @throws {Error} If date cannot be parsed
60
+ */
61
+ parseDate(dateStr, format = 'MM/DD/YYYY') {
62
+ let targetDate = dayjs(dateStr);
63
+
64
+ if (!targetDate.isValid()) {
65
+ // Try parsing with custom format
66
+ targetDate = dayjs(dateStr, format);
67
+ }
68
+
69
+ if (!targetDate.isValid()) {
70
+ // Try common date formats
71
+ targetDate = dayjs(dateStr, ['YYYY-MM-DD', 'MM/DD/YYYY', 'DD/MM/YYYY', 'YYYY/MM/DD']);
72
+ }
73
+
74
+ if (!targetDate.isValid()) {
75
+ throw new Error(`Unable to parse date: ${dateStr}`);
76
+ }
77
+
78
+ return targetDate;
79
+ }
80
+
81
+ /**
82
+ * Calculate target date from offset days or parse from date string
83
+ * @param {Object} options - Options object
84
+ * @param {string} options.date - Date string to parse (optional)
85
+ * @param {number} options.offsetDays - Days to offset from today (optional, default: 0)
86
+ * @param {string} options.format - Expected date format (optional, default: 'MM/DD/YYYY')
87
+ * @returns {Object} dayjs object representing the target date
88
+ */
89
+ getTargetDate(options = {}) {
90
+ const { date, offsetDays = 0, format = 'MM/DD/YYYY' } = options;
91
+
92
+ if (date) {
93
+ return this.parseDate(date, format);
94
+ } else {
95
+ const safeOffsetDays = offsetDays !== undefined ? Number(offsetDays) : 0;
96
+ const targetDate = dayjs().add(safeOffsetDays, 'day');
97
+
98
+ if (!targetDate.isValid()) {
99
+ throw new Error(`Invalid offsetDays: ${offsetDays}`);
100
+ }
101
+
102
+ return targetDate;
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Calculate the difference in months between current date and target date
108
+ * @param {number} currentY - Current year
109
+ * @param {number} currentM - Current month (0-indexed)
110
+ * @param {number} targetY - Target year
111
+ * @param {number} targetM - Target month (0-indexed)
112
+ * @returns {number} Difference in months (positive for future, negative for past)
113
+ */
114
+ diffInMonths(currentY, currentM, targetY, targetM) {
115
+ return (targetY - currentY) * 12 + (targetM - currentM);
116
+ }
117
+
118
+ /**
119
+ * Calculate month difference between today and target date
120
+ * @param {Object} targetDate - dayjs object for target date
121
+ * @returns {number} Difference in months
122
+ */
123
+ getMonthDifference(targetDate) {
124
+ const today = dayjs();
125
+ const currentY = today.year();
126
+ const currentM = today.month(); // 0-indexed
127
+ const targetY = targetDate.year();
128
+ const targetM = targetDate.month(); // 0-indexed
129
+
130
+ return this.diffInMonths(currentY, currentM, targetY, targetM);
131
+ }
132
+
133
+ /**
134
+ * Format date in various formats for date picker matching
135
+ * @param {Object} targetDate - dayjs object for target date
136
+ * @param {string} format - User-specified format (default: 'MM/DD/YYYY')
137
+ * @returns {Array<string>} Array of formatted date strings to try
138
+ */
139
+ getDatePickerFormats(targetDate, format = 'MM/DD/YYYY') {
140
+ const options = { weekday: 'long', month: 'long', day: 'numeric' };
141
+ const formattedFutureDate = targetDate.toDate().toLocaleDateString('en-US', options);
142
+
143
+ return [
144
+ targetDate.format(format), // User-specified format
145
+ targetDate.format('D'), // Just the day number (e.g., "30")
146
+ targetDate.format('DD'), // Two-digit day (e.g., "30")
147
+ formattedFutureDate, // "Thursday, October 30"
148
+ targetDate.format('MMMM D, YYYY'), // "October 30, 2024"
149
+ ];
150
+ }
151
+
152
+ #ensureValidFormat(format) {
153
+ if (!_.isString(format) || _.isEmpty(format.trim())) {
154
+ return DateUtils.DEFAULT_FORMAT;
155
+ }
156
+ const f = format.trim();
157
+ const hasYear = /(Y{2,4})/.test(f);
158
+ const hasMonth = /\b(M{1,4})\b/.test(f);
159
+ const hasDay = /\b(D{1,2})\b/.test(f);
160
+
161
+ if (hasYear || hasMonth || hasDay) {
162
+ return f;
163
+ }
164
+ return DateUtils.DEFAULT_FORMAT;
165
+ }
166
+ }
167
+
168
+ exports.DateUtils = DateUtils;
@@ -0,0 +1,46 @@
1
+ const crypto = require('crypto');
2
+ const { FileUtils } = require('../../library/file/file-utils');
3
+
4
+ const DECRYPTION_TOKEN = "hCXkfcVxqxI8cMGO6XbQHA7lstowjzp9TbZCbJobmMkLmR16q16tIHAZQRLTSGXy";
5
+
6
+ class TokenConfig {
7
+ static async getConfig() {
8
+ try {
9
+
10
+ const keyString = Buffer.from(DECRYPTION_TOKEN);
11
+
12
+ function createKey(keyString) {
13
+ return crypto.createHash('sha256').update(keyString).digest();
14
+ }
15
+
16
+ function decrypt(encryptedBase64) {
17
+ const key = createKey(keyString);
18
+
19
+ const encrypted = Buffer.from(encryptedBase64, 'base64');
20
+
21
+ const iv = encrypted.subarray(0, 16);
22
+ const ciphertext = encrypted.subarray(16);
23
+
24
+ const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
25
+
26
+ let decrypted = decipher.update(ciphertext);
27
+ decrypted = Buffer.concat([decrypted, decipher.final()]);
28
+ return decrypted.toString('utf8');
29
+ }
30
+
31
+ const configData = new FileUtils().readTextContent('toktmp/tmp.json', "{}");
32
+ const config = JSON.parse(configData);
33
+
34
+ return {
35
+ access_token: decrypt(config.access_token),
36
+ token_type: config.token_type,
37
+ offline: config.offline
38
+ };
39
+ } catch (error) {
40
+ console.error('Error reading token config:', error);
41
+ throw error;
42
+ }
43
+ }
44
+ }
45
+
46
+ module.exports = TokenConfig;