froth-webdriverio-framework 6.0.68 → 6.0.70

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.
@@ -0,0 +1,425 @@
1
+ const setAllDetails = require('./setallDatailinBuffer');
2
+ const exeDetails = require('../froth_api_calls/getexecutionDetails');
3
+ const { getBSSessionDetails } = require('../froth_api_calls/browsersatckSessionInfo');
4
+ let globalErrorHandled = false;
5
+ let suiteStartTime;
6
+ let totalTestDuration = 0;
7
+ const url = require('url');
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ const resultdetails = {
12
+ comments: [],
13
+ excution_status: null,
14
+ excution_time: null
15
+ };
16
+
17
+ /* ----------------- GLOBAL ERROR HANDLERS ----------------- */
18
+ let globalErrorRegistered = false;
19
+ function registerGlobalErrorHandlers() {
20
+ if (globalErrorRegistered) return;
21
+ globalErrorRegistered = true;
22
+
23
+ process.on('uncaughtException', async (err) => {
24
+ console.error('🔥 Uncaught Exception:', err);
25
+ resultdetails.excution_status = 'FAILED';
26
+ resultdetails.comments.push(`Uncaught Exception: ${err.message}`);
27
+ await safeUpdateExecution();
28
+ process.exit(1);
29
+ });
30
+
31
+ process.on('unhandledRejection', async (reason) => {
32
+ console.error('🔥 Unhandled Rejection:', reason);
33
+ resultdetails.excution_status = 'FAILED';
34
+ resultdetails.comments.push(`Unhandled Rejection: ${reason?.message || reason}`);
35
+ await safeUpdateExecution();
36
+ process.exit(1);
37
+ });
38
+ }
39
+
40
+ async function safeUpdateExecution() {
41
+ try {
42
+ await exeDetails.updateExecuitonDetails(
43
+ BUFFER.getItem('ORGANISATION_DOMAIN_URL'),
44
+ BUFFER.getItem('FROTH_LOGIN_TOKEN'),
45
+ BUFFER.getItem('FROTH_EXECUTION_ID'),
46
+ resultdetails
47
+ );
48
+ } catch (e) {
49
+ console.error('❌ Failed to update execution details:', e.message);
50
+ }
51
+ }
52
+
53
+ /* ----------------- HELPER FUNCTIONS ----------------- */
54
+ function msToTime(ms) {
55
+ const sec = Math.floor(ms / 1000);
56
+ return new Date(sec * 1000).toISOString().substr(11, 8);
57
+ }
58
+
59
+ async function failExecution(reason) {
60
+ resultdetails.excution_status = 'FAILED';
61
+ resultdetails.comments.push(reason);
62
+ await safeUpdateExecution();
63
+ process.exit(1);
64
+ }
65
+ /* ------------------ COMMON CONFIG ------------------ */
66
+
67
+ const commonHooks = {
68
+
69
+ /* ========== ON PREPARE ========== */
70
+ onPrepare: async () => {
71
+ registerGlobalErrorHandlers();
72
+
73
+ console.log('==== ON PREPARE HOOK ====');
74
+ await setAllDetails.setEnvVariables();
75
+ await setAllDetails.setExecutionDetails();
76
+ await setAllDetails.setSuiteDetails();
77
+ await setAllDetails.setTestDataDetails();
78
+
79
+ console.log('✅ All Env Varibale set');
80
+ },
81
+
82
+ /* ========== BEFORE SESSION ========== */
83
+
84
+ beforeSession: async function (config, capabilities, specs) {
85
+ console.log('==== BEFORE SESSION ====');
86
+ try {
87
+ /** 1️⃣ Validate test syntax */
88
+ let syntaxFailed = false;
89
+
90
+ for (const fileUrl of specs) {
91
+ const filePath = url.fileURLToPath(fileUrl);
92
+ try {
93
+ new Function(fs.readFileSync(filePath, 'utf8'));
94
+ } catch (err) {
95
+ const msg = `❌ Syntax error in ${path.basename(filePath)}: ${err.message}`;
96
+ console.error(msg);
97
+ resultdetails.comments.push(msg);
98
+ syntaxFailed = true;
99
+ }
100
+ }
101
+
102
+ if (syntaxFailed) {
103
+ await failExecution('Syntax validation failed');
104
+ }
105
+ /** 2️⃣ BrowserStack capability normalization */
106
+
107
+ if (process.env.PLATFORM === 'browserstack' || process.env.PLATFORM === 'browserstacklocal') {
108
+ const bsOpts = capabilities['bstack:options'] || {};
109
+ if (process.env.BS_SESSION_TYPE === 'app-automate') {
110
+ const appPath = process.env.BROWSERSTACK_APP_PATH;
111
+ if (!appPath) {
112
+ await failExecution('BROWSERSTACK_APP_PATH is missing');
113
+ }
114
+
115
+ bsOpts.app = appPath;
116
+ capabilities['appium:app'] = appPath;
117
+ console.log('✅ App & media set in before session');
118
+
119
+ }
120
+ /** Media upload */
121
+ if (process.env.MEDIA_FILES) {
122
+ try {
123
+ bsOpts['browserstack.uploadMedia'] = JSON.parse(process.env.MEDIA_FILES);
124
+ } catch {
125
+ console.warn('⚠️ MEDIA_FILES is not valid JSON');
126
+ }
127
+ }
128
+ capabilities['bstack:options'] = bsOpts;
129
+
130
+ }
131
+ console.log('final cinfig dteials :' + JSON.stringify(capabilities))
132
+
133
+ console.log("config details " + JSON.stringify(config))
134
+
135
+
136
+ } catch (error) {
137
+ console.error('🚨 Error in beforeSession:', error);
138
+ console.error('🚨 Error in beforeSession:', error.message);
139
+ }
140
+ },
141
+ /**
142
+ * Gets executed before the suite starts (in Mocha/Jasmine only).
143
+ * @param {object} suite suite details
144
+ */
145
+ beforeSuite: async function (suite) {
146
+ try {
147
+ console.log('==== BEFORE SUITE HOOK ====');
148
+ console.log(`====> Test suite has been started ' ${suite.title}' `,);
149
+
150
+ starttime = new Date().getTime();
151
+
152
+ if (process.env.PLATFORM === 'browserstack')
153
+ await getBSSessionDetails(process.env.BS_SESSION_TYPE, process.env.BROWSERSTACK_USERNAME, process.env.BROWSERSTACK_ACCESS_KEY);
154
+
155
+
156
+ // const resultdetails = {};
157
+ await exeDetails.update_CICDRUNID_ReportUrl(BUFFER.getItem("ORGANISATION_DOMAIN_URL"), BUFFER.getItem("FROTH_LOGIN_TOKEN"), BUFFER.getItem("FROTH_EXECUTION_ID"))
158
+ BUFFER.setItem("UPDATE_EXECUTION", 'FALSE') //i need to recall
159
+
160
+ } catch (e) {
161
+ console.log("Error in beforeSuite:", e);
162
+ }
163
+ },
164
+
165
+ before: async function (capabilities, specs) {
166
+ if (process.env.BS_SESSION_TYPE === 'automate') {
167
+ console.log('==== BEFORE HOOK ====')
168
+ await browser.maximizeWindow()
169
+ }
170
+ else {
171
+ console.log('==== BEFORE HOOK FOR MOBILE ====');
172
+ }
173
+
174
+ },
175
+
176
+ /**
177
+ * Function to be executed before a test (in Mocha/Jasmine only)
178
+ * @param {object} test test object
179
+ * @param {object} context scope object the test was executed with
180
+ */
181
+ beforeTest: async function (test, context) {
182
+
183
+ console.log('==== BEFORE TEST HOOK ====');
184
+ console.log(`====> Test Started '${test.title}'`);
185
+
186
+ },
187
+
188
+
189
+ /**
190
+ * Function to be executed after a test (in Mocha/Jasmine only)
191
+ * @param {object} test test object
192
+ * @param {object} context scope object the test was executed with
193
+ * @param {Error} result.error error object in case the test fails, otherwise `undefined`
194
+ * @param {*} result.result return object of test function
195
+ * @param {number} result.duration duration of test
196
+ * @param {boolean} result.passed true if test has passed, otherwise false
197
+ * @param {object} result.retries information about spec related retries, e.g. `{ attempts: 0, limit: 0 }`
198
+ */
199
+ afterTest: async function (test, _, { error, duration, passed }) {
200
+ console.log('==== AFTER TEST HOOK ====');
201
+ console.log(`====> Test name finished '${test.title}'`);
202
+
203
+ const fileName = path.basename(test.file);
204
+ console.log('====> Test File Name:', fileName);
205
+ // BUFFER.setItem("FROTH_TOTAL_DURATION", Number(BUFFER.getItem("FROTH_TOTAL_DURATION")) + duration)
206
+ console.log(`====> Total Duration for this test: ${duration}ms`);
207
+ totalTestDuration += duration;
208
+ console.log(`====> Total Duration for all tests: ${totalTestDuration}ms`);
209
+ console.log(`====> Status of test: ${passed}`);
210
+
211
+ let scriptresult = "NOT RUN";
212
+ // Default assumption: test passed (unless soft assertion failed)
213
+ let finalPassed = passed;
214
+
215
+ if (passed) {
216
+ scriptresult = "PASSED"
217
+ resultdetails.comments.push(`${test.title} - passed`);
218
+ console.log(`====> resultdetails comments: ${resultdetails.comments}`);
219
+ }
220
+ else if (!finalPassed || error) {
221
+ scriptresult = "FAILED"
222
+ console.log(`====> Failed or error while executing the test: ${error ? error.message : "Test failed"}`);
223
+ if (!resultdetails.comments.some(comment => typeof comment === 'string' && comment.includes(test.title)))
224
+ resultdetails.comments.push(`${test.title} - failed: ${error ? error.message : "Test failed"}`);
225
+
226
+ }
227
+
228
+ let scriptDetails = BUFFER.getItem("FROTHE_SUITE_DETAILS");
229
+ if (typeof scriptDetails === "string") {
230
+ scriptDetails = JSON.parse(scriptDetails);
231
+ }
232
+ const jwtScript = scriptDetails.find(s => s.scriptName === fileName.replace(".js", ""));
233
+ console.log("jwtScript:", jwtScript);
234
+
235
+ await exeDetails.updateScriptExecutionStatus(
236
+ BUFFER.getItem("ORGANISATION_DOMAIN_URL"),
237
+ BUFFER.getItem("FROTH_LOGIN_TOKEN"),
238
+ jwtScript.scriptId,
239
+ jwtScript.platform.toLowerCase(),
240
+ scriptresult)
241
+
242
+
243
+ },
244
+ /**
245
+ * Hook that gets executed after the suite has ended (in Mocha/Jasmine only).
246
+ * @param {object} suite suite details
247
+ */
248
+ afterSuite: async function (suite) {
249
+ console.log('==== AFTER SUITE HOOK ====');
250
+ console.log(`====> Test suite has completed '${suite.title}'`);
251
+ },
252
+ /**
253
+ * Gets executed after all tests are done. You still have access to all global variables from
254
+ * the test.
255
+ * @param {number} result 0 - test pass, 1 - test fail
256
+ * @param {Array.<Object>} capabilities list of capabilities details
257
+ * @param {Array.<String>} specs List of spec file paths that ran
258
+ */
259
+ after: async function (result, config, capabilities, specs) {
260
+ console.log('==== AFTER HOOK ====');
261
+ console.log('====> All tests are completed.' + result);
262
+ BUFFER.setItem("RESULT_DATA", result);
263
+ console.log("====> Result data :" + BUFFER.getItem("RESULT_DATA"))
264
+ resultdetails.excution_status = BUFFER.getItem("RESULT_DATA") == 0 ? 'PASSED' : 'FAILED'
265
+ console.log("====> Total Duration taken for the suite:" + BUFFER.getItem("FROTH_TOTAL_DURATION"));
266
+ console.log("====> Execution Status from results" + resultdetails.excution_status);
267
+ // Capture WebDriver session errors
268
+
269
+ if (result !== 0) {
270
+ if (resultdetails.comments.length === 0) {
271
+ resultdetails.comments.push("❌ WebDriver session failed or timed out.");
272
+ }
273
+ resultdetails.comments.push(`Execution failed with exit code: ${result}`);
274
+
275
+ process.on('uncaughtException', (err) => {
276
+ console.error("Uncaught Exception:", err);
277
+ resultdetails.comments.push(`Execution failed : ${err}`);
278
+ });
279
+
280
+ }
281
+
282
+ },
283
+
284
+ onError: async function (error) {
285
+ console.error('==== ON ERROR HOOK ====');
286
+ console.error('====> Error occurred:', error.message);
287
+ if (error.message.includes('Automate testing time expired')) {
288
+ console.log('❌ Global error: Session timed out on BrowserStack.');
289
+ resultdetails.excution_status = 'FAILED';
290
+ resultdetails.comments.push(`Global error: Session timed out on BrowserStack.Please check ths session: ${result}`);
291
+
292
+ await exeDetails.updateExecuitonDetails(BUFFER.getItem("ORGANISATION_DOMAIN_URL"), BUFFER.getItem("FROTH_LOGIN_TOKEN"), BUFFER.getItem("FROTH_EXECUTION_ID"), resultdetails)
293
+ process.exit(1); // Stop execution if session timed out
294
+ }
295
+ },
296
+ afterSession: async function (config, capabilities, specs) {
297
+ console.log('==== AFTER SESSION HOOK ====');
298
+ console.log('====> This is the aftersession hook');
299
+ // console.log("Capabilities:", capabilities);
300
+ console.log("Specs:", specs);
301
+ //console.log("Config:", config);
302
+
303
+ process.on('uncaughtException', (err) => {
304
+ console.error("Uncaught Exception:", err);
305
+ resultdetails.comments.push(`Execution failed with exit code: ${err}`);
306
+ });
307
+
308
+ // Capture unhandled promise rejections
309
+ process.on('unhandledRejection', (reason, promise) => {
310
+ console.error("Unhandled Promise Rejection:", reason);
311
+ resultdetails.comments.push(`Execution failed with exit code: ${reason}`);
312
+ });
313
+
314
+ endtime = new Date().getTime();
315
+ let totalDuration = endtime - starttime;
316
+ console.log("====> Total Duration taken for :" + BUFFER.getItem("FROTH_TOTAL_DURATION"));
317
+ console.log("====> Total Duration in after session based on start time and end time:" + totalDuration);
318
+ console.log("====> Total Duration in after session based on summing up the test execution times:" + totalTestDuration);
319
+
320
+ resultdetails.excution_status = BUFFER.getItem("RESULT_DATA") == 0 ? 'PASSED' : 'FAILED'
321
+ const frothDuration = BUFFER.getItem("FROTH_TOTAL_DURATION");
322
+
323
+ if (await isInvalidDuration(frothDuration)) {
324
+ // console.log("inside froth duration");
325
+ if (await isInvalidDuration(totalTestDuration)) {
326
+ console.log("inside froth totalTestDuration");
327
+ resultdetails.excution_time = await millisecondsToTime(totalDuration);
328
+ } else {
329
+ console.log("inside froth totalTestDuration comparision");
330
+ if (totalDuration > totalTestDuration)
331
+ resultdetails.excution_time = await millisecondsToTime(totalDuration);
332
+ else
333
+ resultdetails.excution_time = await millisecondsToTime(totalTestDuration);
334
+ }
335
+ } else {
336
+ console.log("inside froth duration else");
337
+ resultdetails.excution_time = await secondsToTime(frothDuration);
338
+ }
339
+
340
+ console.log("====> Total Duration calculation:" + resultdetails.excution_time);
341
+
342
+ await exeDetails.updateExecuitonDetails(BUFFER.getItem("ORGANISATION_DOMAIN_URL"), BUFFER.getItem("FROTH_LOGIN_TOKEN"), BUFFER.getItem("FROTH_EXECUTION_ID"), resultdetails)
343
+
344
+ // Perform any cleanup or post-test actions here
345
+ BUFFER.clear();
346
+ },
347
+
348
+ onComplete: async function (exitCode, config, capabilities, results) {
349
+ console.log('==== ON COMPLETE HOOK ====');
350
+ console.log('====> Results:', results);
351
+ console.log('==== Test Results Summary ======');
352
+ console.log('Total Tests:', results.total);
353
+ console.log('Passed:', results.passed);
354
+ console.log('Failed:', results.failed);
355
+ console.log('Exit Code:', exitCode);
356
+
357
+ console.log('==== ALL TESTS ARE COMPLETED ====');
358
+ return exitCode;
359
+ }
360
+
361
+ };
362
+ async function isInvalidDuration(val) {
363
+ console.log("val in isValidDuration:" + val);
364
+ // Check if the value is null, 0, or undefined
365
+ let isValid;
366
+ if (typeof val === 'string') {
367
+ val = val.trim().toLowerCase();
368
+ isValid = val === "null" || val === "0" || val === "undefined";
369
+
370
+ } else
371
+ isValid = val === null || val === 0 || val === undefined;
372
+
373
+ console.log("isValid in isValidDuration:" + isValid);
374
+
375
+ return isValid;
376
+ }
377
+ async function secondsToTime(secs) {
378
+ console.log("secs in secondsToTime :" + secs);
379
+ let hours = Math.floor(secs / 3600);
380
+ let minutes = Math.floor((secs % 3600) / 60);
381
+ let seconds = secs % 60;
382
+
383
+ // Adding leading zeros if the value is less than 10
384
+ if (hours < 10) hours = '0' + hours;
385
+ if (minutes < 10) minutes = '0' + minutes;
386
+ if (seconds < 10) seconds = '0' + seconds;
387
+ console.log("Time:" + hours + ':' + minutes + ':' + seconds);
388
+ return hours + ':' + minutes + ':' + seconds;
389
+ }
390
+ async function millisecondsToTime(ms) {
391
+ console.log("ms in millisecondsToTime: " + ms);
392
+
393
+ // Ensure input is a number
394
+ ms = Number(ms);
395
+ if (isNaN(ms)) {
396
+ return '00:00:00';
397
+ }
398
+
399
+ let totalSeconds = Math.floor(ms / 1000);
400
+ let hours = Math.floor(totalSeconds / 3600);
401
+ let minutes = Math.floor((totalSeconds % 3600) / 60);
402
+ let seconds = totalSeconds % 60;
403
+
404
+ // Pad with leading zeros
405
+ hours = hours < 10 ? '0' + hours : hours;
406
+ minutes = minutes < 10 ? '0' + minutes : minutes;
407
+ seconds = seconds < 10 ? '0' + seconds : seconds;
408
+
409
+ const timeStr = `${hours}:${minutes}:${seconds}`;
410
+ console.log("Formatted time:", timeStr);
411
+ return timeStr;
412
+ }
413
+
414
+ async function convertTimetoHHMMSS(time) {
415
+ const seconds = Math.floor((time / 1000) % 60);
416
+ const minutes = Math.floor((time / (1000 * 60)) % 60);
417
+ const hours = Math.floor((time / (1000 * 60 * 60)) % 24);
418
+ const formattedTime = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
419
+
420
+ console.log("Time:" + hours + ':' + minutes + ':' + seconds);
421
+
422
+ return formattedTime;
423
+ }
424
+
425
+ module.exports = commonHooks;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "froth-webdriverio-framework",
3
- "version": "6.0.68",
3
+ "version": "6.0.70",
4
4
  "readme": "WendriverIO Integration with [BrowserStack](https://www.browserstack.com)",
5
5
  "description": "Selenium examples for WebdriverIO and BrowserStack App Automate",
6
6
  "scripts": {
@@ -1,265 +0,0 @@
1
-
2
- const setAllDetails = require('./setallDatailinBuffer');
3
- const exeDetails = require('../froth_api_calls/getexecutionDetails');
4
- const { getBSSessionDetails } = require('../froth_api_calls/browsersatckSessionInfo');
5
- let globalErrorHandled = false;
6
- let suiteStartTime;
7
- let totalTestDuration = 0;
8
- const url = require('url');
9
- const fs = require('fs');
10
- const path = require('path');
11
-
12
- const resultdetails = {
13
- comments: [],
14
- excution_status: null,
15
- excution_time: null
16
- };
17
-
18
- /* ----------------- GLOBAL ERROR HANDLERS ----------------- */
19
- let globalErrorRegistered = false;
20
- function registerGlobalErrorHandlers() {
21
- if (globalErrorRegistered) return;
22
- globalErrorRegistered = true;
23
-
24
- process.on('uncaughtException', async (err) => {
25
- console.error('🔥 Uncaught Exception:', err);
26
- resultdetails.excution_status = 'FAILED';
27
- resultdetails.comments.push(`Uncaught Exception: ${err.message}`);
28
- await safeUpdateExecution();
29
- process.exit(1);
30
- });
31
-
32
- process.on('unhandledRejection', async (reason) => {
33
- console.error('🔥 Unhandled Rejection:', reason);
34
- resultdetails.excution_status = 'FAILED';
35
- resultdetails.comments.push(`Unhandled Rejection: ${reason?.message || reason}`);
36
- await safeUpdateExecution();
37
- process.exit(1);
38
- });
39
- }
40
-
41
- async function safeUpdateExecution() {
42
- try {
43
- await exeDetails.updateExecuitonDetails(
44
- BUFFER.getItem('ORGANISATION_DOMAIN_URL'),
45
- BUFFER.getItem('FROTH_LOGIN_TOKEN'),
46
- BUFFER.getItem('FROTH_EXECUTION_ID'),
47
- resultdetails
48
- );
49
- } catch (e) {
50
- console.error('❌ Failed to update execution details:', e.message);
51
- }
52
- }
53
-
54
- /* ----------------- HELPER FUNCTIONS ----------------- */
55
- function msToTime(ms) {
56
- if (!ms || isNaN(ms)) return '00:00:00';
57
-
58
- let seconds = Math.floor(ms / 1000);
59
- let hours = Math.floor(seconds / 3600);
60
- seconds %= 3600;
61
- let minutes = Math.floor(seconds / 60);
62
- seconds %= 60;
63
-
64
- return [
65
- hours.toString().padStart(2, '0'),
66
- minutes.toString().padStart(2, '0'),
67
- seconds.toString().padStart(2, '0')
68
- ].join(':');
69
- }
70
-
71
- async function failExecution(reason) {
72
- resultdetails.excution_status = 'FAILED';
73
- resultdetails.comments.push(reason);
74
- await safeUpdateExecution();
75
- process.exit(1);
76
- }
77
- /* ------------------ COMMON CONFIG ------------------ */
78
-
79
- const commonHooks = {
80
-
81
- /* ========== ON PREPARE ========== */
82
- onPrepare: async () => {
83
- registerGlobalErrorHandlers();
84
-
85
- console.log('==== ON PREPARE HOOK ====');
86
- await setAllDetails.setEnvVariables();
87
- await setAllDetails.setExecutionDetails();
88
- await setAllDetails.setSuiteDetails();
89
- await setAllDetails.setTestDataDetails();
90
-
91
- console.log('✅ All Env Varibale set');
92
- },
93
-
94
- /* ========== BEFORE SESSION ========== */
95
-
96
- beforeSession: async function (config, capabilities, specs) {
97
- console.log('==== BEFORE SESSION ====');
98
- try {
99
- /** 1️⃣ Validate test syntax */
100
- let syntaxFailed = false;
101
-
102
- for (const fileUrl of specs) {
103
- const filePath = url.fileURLToPath(fileUrl);
104
- try {
105
- new Function(fs.readFileSync(filePath, 'utf8'));
106
- } catch (err) {
107
- const msg = `❌ Syntax error in ${path.basename(filePath)}: ${err.message}`;
108
- console.error(msg);
109
- resultdetails.comments.push(msg);
110
- syntaxFailed = true;
111
- }
112
- }
113
-
114
- if (syntaxFailed) {
115
- await failExecution('Syntax validation failed');
116
- }
117
- /** 2️⃣ BrowserStack capability normalization */
118
-
119
- if (process.env.PLATFORM === 'browserstack' || process.env.PLATFORM === 'browserstacklocal') {
120
- const bsOpts = capabilities['bstack:options'] || {};
121
- if (process.env.BS_SESSION_TYPE === 'app-automate') {
122
- const appPath = process.env.BROWSERSTACK_APP_PATH;
123
- if (!appPath) {
124
- await failExecution('BROWSERSTACK_APP_PATH is missing');
125
- }
126
-
127
- bsOpts.app = appPath;
128
- capabilities['appium:app'] = appPath;
129
- console.log('✅ App & media set in before session');
130
-
131
- }
132
- /** Media upload */
133
- if (process.env.MEDIA_FILES) {
134
- try {
135
- bsOpts['browserstack.uploadMedia'] = JSON.parse(process.env.MEDIA_FILES);
136
- } catch {
137
- console.warn('⚠️ MEDIA_FILES is not valid JSON');
138
- }
139
- }
140
- capabilities['bstack:options'] = bsOpts;
141
-
142
- }
143
- console.log('final cinfig dteials :' + JSON.stringify(capabilities))
144
-
145
- console.log("config details " + JSON.stringify(config))
146
-
147
-
148
- } catch (error) {
149
- console.error('🚨 Error in beforeSession:', error);
150
- console.error('🚨 Error in beforeSession:', error.message);
151
- }
152
- },
153
- /* ========== BEFORE SUITE ========== */
154
- beforeSuite: async (suite) => {
155
- console.log(`==== BEFORE SUITE: ${suite.title} ====`);
156
- suiteStartTime = Date.now();
157
-
158
- if (process.env.PLATFORM === 'browserstack' || process.env.PLATFORM === 'browserstacklocal') {
159
- await getBSSessionDetails(
160
- process.env.BS_SESSION_TYPE,
161
- process.env.BROWSERSTACK_USERNAME,
162
- process.env.BROWSERSTACK_ACCESS_KEY
163
- );
164
- }
165
-
166
- await exeDetails.update_CICDRUNID_ReportUrl(
167
- BUFFER.getItem('ORGANISATION_DOMAIN_URL'),
168
- BUFFER.getItem('FROTH_LOGIN_TOKEN'),
169
- BUFFER.getItem('FROTH_EXECUTION_ID')
170
- );
171
- },
172
- /* ========== BEFORE TEST ========== */
173
- beforeTest: async (test) => {
174
- console.log(`▶️ START TEST: ${test.title}`);
175
- },
176
-
177
- /* ========== AFTER TEST ========== */
178
- afterTest: async (test, _, { error, duration, passed }) => {
179
- console.log(`==== AFTER TEST HOOK ===='${test.title}'`);
180
- totalTestDuration += duration;
181
-
182
- const fileName = path.basename(test.file);
183
- const status = passed ? 'PASSED' : 'FAILED';
184
-
185
- if (!passed) {
186
- resultdetails.comments.push(`${test.title} - ${error?.message || 'Failed'}`);
187
- }
188
-
189
- const suiteDetails = JSON.parse(BUFFER.getItem('FROTHE_SUITE_DETAILS'));
190
- const script = suiteDetails.find(s => s.scriptName === fileName.replace('.js', ''));
191
- console.log("Script Details:", script);
192
-
193
- await exeDetails.updateScriptExecutionStatus(
194
- BUFFER.getItem('ORGANISATION_DOMAIN_URL'),
195
- BUFFER.getItem('FROTH_LOGIN_TOKEN'),
196
- script.scriptId,
197
- script.platform.toLowerCase(),
198
- status
199
- );
200
- },
201
-
202
- /**
203
- * Hook that gets executed after the suite has ended (in Mocha/Jasmine only).
204
- * @param {object} suite suite details
205
- */
206
- afterSuite: async function (suite) {
207
- console.log('==== AFTER SUITE HOOK ====');
208
- console.log(`====> Test suite has completed '${suite.title}'`);
209
- },
210
-
211
- /* ==== AFTER SESSION ==== */
212
- afterSession: async (config, capabilities, specs, exitCode) => {
213
- console.log('==== AFTER SESSION ====');
214
- const endTime = Date.now();
215
-
216
- if (!suiteStartTime) {
217
- suiteStartTime = endTime; // fallback safety
218
- }
219
-
220
- const totalTime = Math.max(endTime - suiteStartTime, totalTestDuration);
221
- resultdetails.excution_status = exitCode === 0 ? 'PASSED' : 'FAILED';
222
- resultdetails.excution_time = msToTime(Math.max(totalTime, totalTestDuration));
223
-
224
- console.log('⏱ Final execution time:', resultdetails.excution_time);
225
-
226
- console.log('📤 Updating DB with final execution details');
227
- console.log('Status:', resultdetails.excution_status);
228
- console.log('Duration:', resultdetails.excution_time);
229
-
230
- await safeUpdateExecution();
231
- BUFFER.clear();
232
- },
233
-
234
- /* ========== ON ERROR ========== */
235
-
236
- onError: async function (error) {
237
- console.error('==== ON ERROR HOOK ====');
238
- console.error('Error occurred:', error.message);
239
-
240
- // Normalize all framework-level failures
241
- resultdetails.excution_status = 'FAILED';
242
- resultdetails.comments.push(`WDIO Error: ${error.message}`);
243
-
244
- // Special handling for BrowserStack timeout
245
- if (error.message?.includes('Automate testing time expired')) {
246
- resultdetails.comments.push(
247
- 'BrowserStack session timed out (Automate testing time expired)'
248
- );
249
- }
250
-
251
- await safeUpdateExecution();
252
- },
253
- /* ========== ON COMPLETE ========== */
254
- onComplete: async (exitCode, _, __, results) => {
255
- console.log('==== ON COMPLETE ====');
256
- console.log(`Total: ${results.total}, Passed: ${results.passed}, Failed: ${results.failed}`);
257
-
258
- return exitCode;
259
- },
260
-
261
-
262
-
263
- };
264
-
265
- module.exports = commonHooks;