@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.
- package/DEV_README.md +19 -0
- package/README.md +6 -3
- package/index.js +121 -0
- package/package.json +30 -4
- package/src/accessibility/combined-a11y-report.js +208 -0
- package/src/accessibility/tpp-a11y.js +81 -0
- package/src/actions.js +3201 -0
- package/src/api/api-auth.js +18 -0
- package/src/api/api-service.js +289 -0
- package/src/app-config.json +7 -0
- package/src/autoheal-actions.js +309 -0
- package/src/console-log/console-log-manager.js +39 -0
- package/src/console-log/console-log-utils.js +201 -0
- package/src/database/mysql/mysql-service.js +23 -0
- package/src/database/neo4j/neo4j-service.js +32 -0
- package/src/database/oracle/oracle-service.js +48 -0
- package/src/database/pg/pg-service.js +29 -0
- package/src/downloads/downloads-manager.js +86 -0
- package/src/downloads/downloads-utils.js +113 -0
- package/src/globalsetup.js +68 -0
- package/src/globalteardown.js +57 -0
- package/src/hooks/each-hook.js +45 -0
- package/src/hooks/hook-utils.js +21 -0
- package/src/library/authenticator/authenticator-utils.js +43 -0
- package/src/library/csv/csv-utils.js +297 -0
- package/src/library/date/date-utils.js +168 -0
- package/src/library/emailTesting/TokenConfig.js +46 -0
- package/src/library/emailTesting/emailTesting-utils.js +527 -0
- package/src/library/excel/excel-utils.js +922 -0
- package/src/library/file/file-action-utils.js +129 -0
- package/src/library/file/file-utils.js +127 -0
- package/src/library/json/json-utils.js +104 -0
- package/src/library/math/math-utils.js +283 -0
- package/src/library/softAssert/softAssert-utils.js +154 -0
- package/src/library/table/table-utils.js +92 -0
- package/src/library/text/text-utils.js +45 -0
- package/src/library/tpp-library.js +81 -0
- package/src/library/web/slider-utils.js +66 -0
- package/src/mobile/appium/appium-manager.js +158 -0
- package/src/mobile/device-cloud/browserstack-service.js +44 -0
- package/src/mobile/device-cloud/commands/browserstack-commands.js +20 -0
- package/src/mobile/device-cloud/commands/device-cloud-commands.js +17 -0
- package/src/mobile/device-cloud/device-cloud-service.js +29 -0
- package/src/mobile/device-cloud/pcloudy-service.js +65 -0
- package/src/mobile/mobile-actions.js +1568 -0
- package/src/mobile/recorder-utils/css-selector-generator.js +2 -0
- package/src/mobile/recorder-utils/finder.js +752 -0
- package/src/mobile/recorder-utils/mobile-recorder.js +266 -0
- package/src/mobile/web-driver-utils.js +110 -0
- package/src/models/tpp-element.js +58 -0
- package/src/models/tpp-mobile-element.js +50 -0
- package/src/network-log/network-log-manager.js +96 -0
- package/src/network-log/network-log-utils.js +361 -0
- package/src/types/elements.types.js +24 -0
- package/src/utils/ai-utils.js +48 -0
- package/src/utils/assert-utils.js +50 -0
- package/src/utils/autoheal-utils.js +9 -0
- package/src/utils/common-utils.js +120 -0
- package/src/utils/cookie-utils.js +70 -0
- package/src/utils/json-utils.js +10 -0
- package/src/utils/log-utils.js +10 -0
- package/src/utils/logger.js +76 -0
- package/src/utils/screenshot-utils.js +42 -0
- package/src/utils/token-utils.js +18 -0
- package/src/utils/vectordb-utils.js +125 -0
- package/src/visual-test/tpp-visual-test.js +122 -0
- package/src/web/web-executor-agent.js +149 -0
- package/src/web/web-executor-data-fetch.js +353 -0
- 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;
|