froth-webdriverio-framework 7.0.116 → 7.0.118
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/froth_common_actions/Utils.js +4 -2
- package/froth_common_actions/dbValidator.js +150 -0
- package/froth_configs/commonhook.js +31 -15
- package/log/key-metrics.json +0 -772
- package/log/key-metrics.json.lock +0 -0
- package/log/sdk-cli.log +539 -0
- package/package.json +2 -4
|
@@ -56,6 +56,7 @@ let amendBrowserStackLog = null;
|
|
|
56
56
|
let logJsonDataToTable = null;
|
|
57
57
|
|
|
58
58
|
let extractEmailLinks = null;
|
|
59
|
+
let comparator=null;
|
|
59
60
|
|
|
60
61
|
if (process.env.LOCATION == null || process.env.LOCATION == 'local') {
|
|
61
62
|
basepath = ".";
|
|
@@ -116,7 +117,7 @@ captureLoadNavigation = require(basepath + '/captureNavigationTime').captureLoad
|
|
|
116
117
|
|
|
117
118
|
amendBrowserStackLog = require(basepath + '/../froth_api_calls/browsersatckSessionInfo').amend2Browserstack;
|
|
118
119
|
logJsonDataToTable = require(basepath + '/logData').logJsonData2Table;
|
|
119
|
-
|
|
120
|
+
comparator=require(basepath +'/dbValidator').validateDbFields
|
|
120
121
|
//extractEmailLinks = require(basepath + '/emailParsing');
|
|
121
122
|
//export the variabels
|
|
122
123
|
module.exports = {
|
|
@@ -163,5 +164,6 @@ module.exports = {
|
|
|
163
164
|
captureLoadNavigation,
|
|
164
165
|
amendBrowserStackLog,
|
|
165
166
|
logJsonDataToTable,
|
|
166
|
-
extractEmailLinks
|
|
167
|
+
extractEmailLinks,
|
|
168
|
+
comparator
|
|
167
169
|
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
const amendBrowserStackLog = require('../froth_api_calls/browsersatckSessionInfo').amend2Browserstack;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Convert any timestamp string/number to milliseconds.
|
|
5
|
+
* Auto-detects multiple formats.
|
|
6
|
+
*/
|
|
7
|
+
async function parseTimestamp(input) {
|
|
8
|
+
if (!input) return NaN;
|
|
9
|
+
|
|
10
|
+
// Case 1: Numeric → Unix timestamp (ms or sec)
|
|
11
|
+
if (!isNaN(input)) {
|
|
12
|
+
const num = Number(input);
|
|
13
|
+
|
|
14
|
+
// If it's in seconds (10 digits), convert to ms
|
|
15
|
+
if (num.toString().length === 10) return num * 1000;
|
|
16
|
+
|
|
17
|
+
// Already ms (13 digits)
|
|
18
|
+
return num;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Case 2: Direct Date() parseable ISO formats
|
|
22
|
+
let parsed = Date.parse(input);
|
|
23
|
+
if (!isNaN(parsed)) return parsed;
|
|
24
|
+
|
|
25
|
+
// Case 3: Common non-ISO formats → manual normalization
|
|
26
|
+
const replacements = [
|
|
27
|
+
{ pattern: /\//g, replace: '-' }, // 2025/01/10 -> 2025-01-10
|
|
28
|
+
{ pattern: / /g, replace: 'T' } // 2025-01-10 12:30 -> 2025-01-10T12:30
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
let normalized = input;
|
|
32
|
+
replacements.forEach(r => {
|
|
33
|
+
normalized = normalized.replace(r.pattern, r.replace);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
parsed = Date.parse(normalized);
|
|
37
|
+
if (!isNaN(parsed)) return parsed;
|
|
38
|
+
|
|
39
|
+
// Case 4: Try reversing DD-MM-YYYY → YYYY-MM-DD
|
|
40
|
+
const dmY = /^(\d{2})-(\d{2})-(\d{4})(.*)$/;
|
|
41
|
+
if (dmY.test(input)) {
|
|
42
|
+
const parts = input.match(dmY);
|
|
43
|
+
normalized = `${parts[3]}-${parts[2]}-${parts[1]}${parts[4]}`;
|
|
44
|
+
parsed = Date.parse(normalized);
|
|
45
|
+
if (!isNaN(parsed)) return parsed;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return NaN;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Main DB validation utility
|
|
53
|
+
*/
|
|
54
|
+
async function validateDbFields(actualData, expectedData, context = 'DB Validation') {
|
|
55
|
+
await amendBrowserStackLog(`🔍 START DB VALIDATION: ${context}`, 'info');
|
|
56
|
+
|
|
57
|
+
const results = [];
|
|
58
|
+
let allPassed = true;
|
|
59
|
+
|
|
60
|
+
for (const key of Object.keys(expectedData)) {
|
|
61
|
+
|
|
62
|
+
const expectedConfig = expectedData[key];
|
|
63
|
+
const actual = actualData[key];
|
|
64
|
+
let expected = expectedConfig;
|
|
65
|
+
let status = "PASS";
|
|
66
|
+
|
|
67
|
+
// -----------------------------
|
|
68
|
+
// SKIP DYNAMIC FIELDS
|
|
69
|
+
// -----------------------------
|
|
70
|
+
if (expectedConfig === "*dynamic*") {
|
|
71
|
+
results.push({
|
|
72
|
+
key, actual, expected: "(SKIPPED - dynamic)", status: "SKIPPED"
|
|
73
|
+
});
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// -----------------------------
|
|
78
|
+
// AUTO-DETECT TIMESTAMP VALIDATION
|
|
79
|
+
// -----------------------------
|
|
80
|
+
if (typeof expectedConfig === "object" && expectedConfig.type === "timestamp") {
|
|
81
|
+
expected = expectedConfig.value;
|
|
82
|
+
const tolerance = expectedConfig.toleranceMs || 0;
|
|
83
|
+
|
|
84
|
+
let actualMs = await parseTimestamp(actual);
|
|
85
|
+
let expectedMs = await parseTimestamp(expected);
|
|
86
|
+
|
|
87
|
+
if (isNaN(actualMs) || isNaN(expectedMs)) {
|
|
88
|
+
status = "FAIL";
|
|
89
|
+
results.push({
|
|
90
|
+
key,
|
|
91
|
+
actual,
|
|
92
|
+
expected,
|
|
93
|
+
status: "FAIL - Unrecognized Timestamp Format"
|
|
94
|
+
});
|
|
95
|
+
allPassed = false;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const diff = Math.abs(actualMs - expectedMs);
|
|
100
|
+
if (diff > tolerance) status = "FAIL";
|
|
101
|
+
|
|
102
|
+
results.push({
|
|
103
|
+
key,
|
|
104
|
+
actual,
|
|
105
|
+
expected: `${expected} (±${tolerance}ms)`,
|
|
106
|
+
status
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
if (status === "FAIL") allPassed = false;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// -----------------------------
|
|
114
|
+
// NORMAL STRING/NUMBER COMPARISON
|
|
115
|
+
// -----------------------------
|
|
116
|
+
if (String(actual) !== String(expected)) {
|
|
117
|
+
status = "FAIL";
|
|
118
|
+
allPassed = false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
results.push({ key, actual, expected, status });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// -----------------------------
|
|
125
|
+
// BROWSERSTACK LOGGING
|
|
126
|
+
// -----------------------------
|
|
127
|
+
// const chunkSize = 800;
|
|
128
|
+
// const combined = results
|
|
129
|
+
// .map(r => `${r.key} => Expected: ${r.expected}, Actual: ${r.actual}, Status: ${r.status}`)
|
|
130
|
+
// .join(" | ");
|
|
131
|
+
|
|
132
|
+
// const chunks = combined.match(new RegExp(`.{1,${chunkSize}}`, "g")) || [];
|
|
133
|
+
// for (const c of chunks) await amendBrowserStackLog(c, "info");
|
|
134
|
+
|
|
135
|
+
await amendBrowserStackLog(combined, "info");
|
|
136
|
+
// -----------------------------
|
|
137
|
+
// FINAL RESULT
|
|
138
|
+
// -----------------------------
|
|
139
|
+
await amendBrowserStackLog(
|
|
140
|
+
allPassed ? "✅ DB VALIDATION PASSED" : "❌ DB VALIDATION FAILED",
|
|
141
|
+
allPassed ? "info" : "error"
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
return allPassed;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
module.exports = {
|
|
148
|
+
parseTimestamp,
|
|
149
|
+
validateDbFields
|
|
150
|
+
};
|
|
@@ -204,20 +204,20 @@ const commonHooks = {
|
|
|
204
204
|
beforeTest: async (test) => {
|
|
205
205
|
console.log(`▶️ START TEST: ${test.title}`);
|
|
206
206
|
|
|
207
|
-
//
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
207
|
+
// Get suite details and script info
|
|
208
|
+
const fileName = path.basename(test.file);
|
|
209
|
+
const suiteDetails = JSON.parse(BUFFER.getItem('FROTHE_SUITE_DETAILS'));
|
|
210
|
+
const script = suiteDetails.find(s => s.scriptName === fileName.replace('.js', ''));
|
|
211
|
+
|
|
212
|
+
if (script) {
|
|
213
|
+
await exeDetails.updateScriptExecutionStatus(
|
|
214
|
+
BUFFER.getItem('ORGANISATION_DOMAIN_URL'),
|
|
215
|
+
BUFFER.getItem('FROTH_LOGIN_TOKEN'),
|
|
216
|
+
script.scriptId,
|
|
217
|
+
script.platform.toLowerCase(),
|
|
218
|
+
"IN PROGRESS"
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
221
|
},
|
|
222
222
|
|
|
223
223
|
/* ========== AFTER TEST ========== */
|
|
@@ -306,6 +306,22 @@ const commonHooks = {
|
|
|
306
306
|
);
|
|
307
307
|
}
|
|
308
308
|
|
|
309
|
+
// Mark all scripts as SKIPPED if error causes all scripts to be skipped
|
|
310
|
+
try {
|
|
311
|
+
const suiteDetails = JSON.parse(BUFFER.getItem('FROTHE_SUITE_DETAILS'));
|
|
312
|
+
for (const script of suiteDetails) {
|
|
313
|
+
await exeDetails.updateScriptExecutionStatus(
|
|
314
|
+
BUFFER.getItem('ORGANISATION_DOMAIN_URL'),
|
|
315
|
+
BUFFER.getItem('FROTH_LOGIN_TOKEN'),
|
|
316
|
+
script.scriptId,
|
|
317
|
+
script.platform.toLowerCase(),
|
|
318
|
+
'SKIPPED'
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
} catch (e) {
|
|
322
|
+
console.error('Failed to mark scripts as SKIPPED:', e.message);
|
|
323
|
+
}
|
|
324
|
+
|
|
309
325
|
await safeUpdateExecution();
|
|
310
326
|
},
|
|
311
327
|
/* ========== ON COMPLETE ========== */
|
|
@@ -369,7 +385,7 @@ async function validateSyntax(specs) {
|
|
|
369
385
|
let syntaxFailed = false;
|
|
370
386
|
|
|
371
387
|
for (const fileUrl of specs) {
|
|
372
|
-
console.log("file path ",fileUrl)
|
|
388
|
+
console.log("file path ", fileUrl)
|
|
373
389
|
const filePath = url.fileURLToPath(fileUrl);
|
|
374
390
|
const code = fs.readFileSync(filePath, 'utf8');
|
|
375
391
|
|