froth-webdriverio-framework 7.0.119-dev1.1 โ†’ 7.0.119-dev1.11

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,197 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { execSync } = require('child_process');
4
+
5
+ /**
6
+ * Generate Allure HTML report automatically after test execution
7
+ * Uses BROWSERSTACK_BUILD_NAME environment variable for report naming
8
+ */
9
+
10
+ const REPORTS_DIR = './reports/allure';
11
+ const OUTPUT_DIR = './allure-report';
12
+
13
+ function findLatestReportDir() {
14
+ if (!fs.existsSync(REPORTS_DIR)) {
15
+ console.log('โŒ No Allure reports directory found.');
16
+ console.log('๐Ÿ’ก Reports are generated in: ./reports/allure/');
17
+ return null;
18
+ }
19
+
20
+ const directories = fs.readdirSync(REPORTS_DIR)
21
+ .map(file => path.join(REPORTS_DIR, file))
22
+ .filter(file => {
23
+ const stat = fs.statSync(file);
24
+ return stat.isDirectory();
25
+ })
26
+ .sort((a, b) => {
27
+ // Sort by modification time, newest first
28
+ return fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs;
29
+ });
30
+
31
+ return directories.length > 0 ? directories[0] : null;
32
+ }
33
+
34
+ function generateAllureReport(reportDir) {
35
+ try {
36
+ console.log('๐ŸŽฏ Allure Report Generator');
37
+ console.log('================================\n');
38
+ console.log(`๐Ÿ“ Using results from: ${reportDir}`);
39
+ console.log(`๐Ÿท๏ธ Build Name: ${process.env.BROWSERSTACK_BUILD_NAME || 'local-build'}\n`);
40
+
41
+ // Get directory name and extract build name (remove timestamp)
42
+ const dirName = path.basename(reportDir);
43
+ const timestampIndex = dirName.lastIndexOf('-');
44
+ const buildName = timestampIndex > 0 ? dirName.substring(0, timestampIndex) : dirName;
45
+
46
+ // Generate HTML report folder named after build name
47
+ const reportFolderName = buildName + '_report';
48
+ const outputDir = path.join(reportDir, reportFolderName);
49
+
50
+ // Clean existing report directory if it exists
51
+ if (fs.existsSync(outputDir)) {
52
+ console.log('๐Ÿงน Cleaning existing report directory...');
53
+ fs.rmSync(outputDir, { recursive: true, force: true });
54
+ }
55
+
56
+ // Generate Allure HTML report
57
+ console.log('๐Ÿ“Š Generating Allure HTML report...');
58
+ console.log(`๐Ÿ“‚ Report folder: ${reportFolderName}\n`);
59
+ const historyPath = path.join(reportDir, 'history');
60
+
61
+ let command = `npx allure generate ${reportDir} -o ${outputDir} --clean`;
62
+
63
+ // Add history if available
64
+ if (fs.existsSync(historyPath)) {
65
+ command = `npx allure generate ${reportDir} -o ${outputDir} --clean --history ${historyPath}`;
66
+ }
67
+
68
+ execSync(command, { stdio: 'inherit' });
69
+
70
+ // Resolve absolute path to the report (for scripts that may run from anywhere)
71
+ const absOutputDir = path.resolve(outputDir);
72
+
73
+ // Create a start script for Mac/Linux (uses script's own directory)
74
+ const startScriptPath = path.join(outputDir, 'start-server.sh');
75
+ const startScriptContent = `#!/bin/bash
76
+ # Allure Report Server Starter (Mac/Linux)
77
+ # Double-click this file or run: ./start-server.sh
78
+
79
+ # Change to this script's directory (so paths always resolve)
80
+ cd "$(dirname "$0")"
81
+
82
+ echo "๐ŸŒ Starting Allure Report Server..."
83
+ echo "๐Ÿ“ Report: $(pwd)"
84
+ echo "๐Ÿ”— Open in browser: http://localhost:8080"
85
+ echo ""
86
+ echo "๐Ÿ’ก Press Ctrl+C to stop the server"
87
+ echo ""
88
+
89
+ # Find npx (walk up to project root with node_modules)
90
+ REPORT_DIR="$(pwd)"
91
+ PARENT_DIR="$(cd ../.. && pwd)"
92
+
93
+ # Try npx from project root, fall back to local
94
+ cd "$PARENT_DIR"
95
+ npx allure open "$REPORT_DIR" --port 8080
96
+ `;
97
+ fs.writeFileSync(startScriptPath, startScriptContent, { mode: 0o755 });
98
+
99
+ // Create a Windows batch file
100
+ const batchScriptPath = path.join(outputDir, 'start-server.bat');
101
+ const batchScriptContent = `@echo off
102
+ REM Allure Report Server Starter (Windows)
103
+ REM Double-click this file to view the report
104
+
105
+ REM Save report location (script's own directory)
106
+ set "REPORT_DIR=%~dp0"
107
+ REM Remove trailing backslash
108
+ if "%REPORT_DIR:~-1%"=="\\" set "REPORT_DIR=%REPORT_DIR:~0,-1%"
109
+
110
+ REM Project root is two levels up (reports/allure/build/report -> project)
111
+ for %%i in ("%REPORT_DIR%\\..\\..\\..\\..") do set "PROJECT_ROOT=%%~fi"
112
+
113
+ echo ๐ŸŒ Starting Allure Report Server...
114
+ echo ๐Ÿ“ Report: %REPORT_DIR%
115
+ echo ๐Ÿ”— Open in browser: http://localhost:8080
116
+ echo.
117
+ echo ๐Ÿ’ก Press Ctrl+C to stop the server
118
+ echo.
119
+
120
+ cd /d "%PROJECT_ROOT%"
121
+ call npx allure open "%REPORT_DIR%" --port 8080
122
+ pause
123
+ `;
124
+ fs.writeFileSync(batchScriptPath, batchScriptContent);
125
+
126
+ // Create a README
127
+ const readmePath = path.join(outputDir, 'README.md');
128
+ const readmeContent = `# Allure Test Report
129
+
130
+ ## ๐ŸŒ How to View This Report
131
+
132
+ ### Option 1: Double-click the start script (Easiest)
133
+ - **Mac/Linux:** Double-click \`start-server.sh\`
134
+ - **Windows:** Double-click \`start-server.bat\`
135
+ - Report will open at: http://localhost:8080
136
+
137
+ ### Option 2: Use npm script
138
+ From the project root, run:
139
+ \`\`\`bash
140
+ npm run report:open
141
+ \`\`\`
142
+
143
+ ### Option 3: Manual command
144
+ \`\`\`bash
145
+ npx allure open "${outputDir}" --port 8080
146
+ \`\`\`
147
+
148
+ ## โ— Why can't I just open index.html?
149
+
150
+ Allure reports use JavaScript to dynamically load test data from JSON files.
151
+ When you open index.html directly (file:// protocol), browsers block this for security reasons (CORS).
152
+
153
+ You need a web server to view the report properly - that's what the start scripts do!
154
+
155
+ ## ๐Ÿ“Š Test Results
156
+
157
+ - **Build Name:** ${process.env.BROWSERSTACK_BUILD_NAME || 'local-build'}
158
+ - **Generated:** ${new Date().toISOString()}
159
+ - **Report Location:** ${outputDir}
160
+ `;
161
+
162
+ fs.writeFileSync(readmePath, readmeContent);
163
+
164
+ console.log('\nโœ… Allure report generated successfully!');
165
+ console.log(`๐Ÿ“ Location: ${path.resolve(outputDir)}`);
166
+ console.log(`๐Ÿ“‚ Results directory: ${path.resolve(reportDir)}`);
167
+ console.log('\n๐ŸŒ To view the report:');
168
+ console.log(' 1. Run: npx froth-report open');
169
+ console.log(' 2. Or double-click: start-server.sh (Mac/Linux) / start-server.bat (Windows)');
170
+ console.log(' 3. Report opens at: http://localhost:8080');
171
+ console.log('\n๐Ÿ’ก Press Ctrl+C to stop the server when done viewing.');
172
+
173
+ return true;
174
+ } catch (error) {
175
+ console.error(`โŒ Error generating report: ${error.message}`);
176
+ return false;
177
+ }
178
+ }
179
+
180
+ function main() {
181
+ const reportDir = findLatestReportDir();
182
+
183
+ if (!reportDir) {
184
+ console.log('โŒ No Allure results found. Please run tests first.');
185
+ console.log('๐Ÿ’ก Results are saved in: ./reports/allure/');
186
+ process.exit(1);
187
+ }
188
+
189
+ generateAllureReport(reportDir);
190
+ }
191
+
192
+ // Run the script
193
+ if (require.main === module) {
194
+ main();
195
+ }
196
+
197
+ module.exports = { generateAllureReport, findLatestReportDir };
@@ -0,0 +1,97 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { execSync } = require('child_process');
4
+
5
+ /**
6
+ * Find and open the latest Allure HTML report
7
+ */
8
+
9
+ const REPORTS_DIR = './reports/allure';
10
+
11
+ function findLatestReport() {
12
+ if (!fs.existsSync(REPORTS_DIR)) {
13
+ console.log('โŒ No Allure reports directory found.');
14
+ console.log('๐Ÿ’ก Reports are generated in: ./reports/allure/');
15
+ return null;
16
+ }
17
+
18
+ const directories = fs.readdirSync(REPORTS_DIR)
19
+ .map(file => path.join(REPORTS_DIR, file))
20
+ .filter(file => fs.statSync(file).isDirectory())
21
+ .filter(file => {
22
+ // Check if any subdirectory ending with _report exists
23
+ const subdirs = fs.readdirSync(file)
24
+ .map(sub => path.join(file, sub))
25
+ .filter(sub => {
26
+ const stat = fs.statSync(sub);
27
+ return stat.isDirectory() && path.basename(sub).endsWith('_report');
28
+ });
29
+ return subdirs.length > 0;
30
+ })
31
+ .sort((a, b) => {
32
+ return fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs;
33
+ });
34
+
35
+ return directories.length > 0 ? directories[0] : null;
36
+ }
37
+
38
+ function openReport(reportDir) {
39
+ // Find the report folder ending with _report
40
+ const reportSubdirs = fs.readdirSync(reportDir)
41
+ .map(sub => path.join(reportDir, sub))
42
+ .filter(sub => {
43
+ const stat = fs.statSync(sub);
44
+ return stat.isDirectory() && path.basename(sub).endsWith('_report');
45
+ });
46
+
47
+ if (reportSubdirs.length === 0) {
48
+ console.log('โŒ No HTML report found in:', reportDir);
49
+ return false;
50
+ }
51
+
52
+ const htmlReportPath = reportSubdirs[0];
53
+ const indexPath = path.join(htmlReportPath, 'index.html');
54
+
55
+ if (!fs.existsSync(indexPath)) {
56
+ console.log('โŒ No index.html found at:', indexPath);
57
+ return false;
58
+ }
59
+
60
+ try {
61
+ console.log('๐ŸŽฏ Opening Allure Report');
62
+ console.log('========================\n');
63
+ console.log(`๐Ÿ“ Report location: ${htmlReportPath}`);
64
+ console.log(`๐Ÿท๏ธ Build: ${path.basename(reportDir)}\n`);
65
+
66
+ // Use Allure CLI to open the report
67
+ console.log('๐ŸŒ Opening report in browser on http://localhost:8080');
68
+ console.log('๐Ÿ’ก Press Ctrl+C to stop the server\n');
69
+
70
+ execSync(`npx allure open ${htmlReportPath} --port 8080`, { stdio: 'inherit' });
71
+
72
+ return true;
73
+ } catch (error) {
74
+ console.error(`โŒ Error opening report: ${error.message}`);
75
+ console.log(`\n๐Ÿ’ก To view manually, open: file://${path.resolve(indexPath)}`);
76
+ return false;
77
+ }
78
+ }
79
+
80
+ function main() {
81
+ const reportDir = findLatestReport();
82
+
83
+ if (!reportDir) {
84
+ console.log('โŒ No Allure HTML reports found. Please run tests first.');
85
+ console.log('๐Ÿ’ก Reports are generated in: ./reports/allure/');
86
+ process.exit(1);
87
+ }
88
+
89
+ openReport(reportDir);
90
+ }
91
+
92
+ // Run the script
93
+ if (require.main === module) {
94
+ main();
95
+ }
96
+
97
+ module.exports = { findLatestReport, openReport };
@@ -126,8 +126,11 @@ async function updateExecuitonDetails(frothUrl, token, id, resultdetails) {
126
126
  return;
127
127
  }
128
128
 
129
- if (BUFFER.getItem('FROTH_UPDATE_EXECUTION') === 'TRUE') {
130
- console.log('โ„น๏ธ Execution already updated โ€” skipping');
129
+ // Only skip update if the flag is EXPLICITLY set to 'TRUE'
130
+ // Proceed with update in all other cases: not set, null, undefined, 'false', 'FALSE', or empty
131
+ const updateFlag = BUFFER.getItem('FROTH_UPDATE_EXECUTION');
132
+ if (updateFlag !== null && updateFlag !== undefined && String(updateFlag).toUpperCase() === 'TRUE') {
133
+ console.log('โ„น๏ธ Execution already updated (FROTH_UPDATE_EXECUTION=TRUE) โ€” skipping');
131
134
  return;
132
135
  }
133
136
 
@@ -141,12 +144,15 @@ async function updateExecuitonDetails(frothUrl, token, id, resultdetails) {
141
144
  formData.append('excution_status', resultdetails.excution_status);
142
145
  }
143
146
 
144
- // if (
145
- // resultdetails.excution_time &&
146
- // resultdetails.excution_time !== 'NaN:NaN:NaN'
147
- // ) {
148
- // formData.append('excution_time', resultdetails.excution_time);
149
- // }
147
+ // Only include execution time for LOCAL runs
148
+ // (BrowserStack tracks its own time, so we don't override it there)
149
+ if (process.env.PLATFORM === 'local' &&
150
+ resultdetails.excution_time &&
151
+ resultdetails.excution_time !== 'NaN:NaN:NaN'
152
+ ) {
153
+ formData.append('excution_time', resultdetails.excution_time);
154
+ console.log(`โฑ๏ธ Sending local execution time: ${resultdetails.excution_time}`);
155
+ }
150
156
 
151
157
  if (resultdetails.comments === null)
152
158
  console.log("Comments is null")
@@ -187,37 +193,6 @@ async function updateExecuitonDetails(frothUrl, token, id, resultdetails) {
187
193
  }
188
194
  }
189
195
 
190
- /* ===================== UPDATE CICD / REPORT ===================== */
191
-
192
- async function update_CICDRUNID_ReportUrl(frothUrl, token, id) {
193
- if (!isValidId(id)) return;
194
-
195
- const url = `${frothUrl}/api/test-execution-update/${id}/`;
196
- const formData = new FormData();
197
-
198
- console.log('PUT:', url);
199
-
200
- try {
201
- formData.append('updated_through_bot', true);
202
- formData.append('run_id', process.env.CICD_RUN_ID);
203
-
204
- const reportUrl = BUFFER.getItem('REPORT_URL');
205
- if (reportUrl) {
206
- formData.append('report_url', reportUrl);
207
- }
208
-
209
- const response = await fetch(url, {
210
- method: 'PUT',
211
- headers: { 'Authorization': `Bearer ${token}` },
212
- body: formData
213
- });
214
-
215
- await handleResponse(response, 'update_CICDRUNID_ReportUrl');
216
-
217
- } catch (error) {
218
- console.error('โŒ update_CICDRUNID_ReportUrl error:', error.message);
219
- }
220
- }
221
196
 
222
197
  /* ===================== UPDATE SCRIPT STATUS ===================== */
223
198
 
@@ -293,6 +268,5 @@ async function updateScriptExecutionStatus(
293
268
  module.exports = {
294
269
  getExecuitonDetails,
295
270
  updateExecuitonDetails,
296
- updateScriptExecutionStatus,
297
- update_CICDRUNID_ReportUrl
271
+ updateScriptExecutionStatus
298
272
  };
@@ -11,13 +11,11 @@ module.exports = {
11
11
  specs: require(SUITE_FILE).tests,
12
12
  exclude: [],
13
13
 
14
-
15
14
  logLevel: 'info',
16
15
  coloredLogs: true,
17
16
  screenshotPath: './errorShots/',
18
17
  baseUrl: '',
19
18
 
20
-
21
19
  waitforTimeout: 90000,
22
20
  connectionRetryTimeout: 90000,
23
21
  connectionRetryCount: 3,
@@ -27,5 +25,10 @@ module.exports = {
27
25
  mochaOpts: {
28
26
  ui: 'bdd',
29
27
  timeout: 300000
30
- }
31
- };
28
+ },
29
+
30
+ // Enable screenshot capture for Allure (handled by screenshot-service)
31
+ // Screenshots stored ONLY in Allure report folder - no separate folders
32
+ };
33
+
34
+
@@ -5,6 +5,10 @@ const { getBSSessionDetails, updateBrowserStackReport } = require('../froth_api_
5
5
  let globalErrorHandled = false;
6
6
  let suiteStartTime = 0;
7
7
  let totalTestDuration = 0;
8
+ // Track real pass/fail counts across tests (exitCode is unreliable in afterSession)
9
+ let testsPassed = 0;
10
+ let testsFailed = 0;
11
+ let testsRun = 0;
8
12
  const url = require('url');
9
13
  const fs = require('fs');
10
14
  const path = require('path');
@@ -14,7 +18,7 @@ let executionUpdated = false;
14
18
  const resultdetails = {
15
19
  comments: [],
16
20
  excution_status: null,
17
- // excution_time: null
21
+ excution_time: null
18
22
  };
19
23
 
20
24
  /* ----------------- GLOBAL ERROR HANDLERS ----------------- */
@@ -90,6 +94,52 @@ async function pushComment(msg) {
90
94
  if (!global.TEST_COMMENTS) global.TEST_COMMENTS = [];
91
95
  global.TEST_COMMENTS.push(msg);
92
96
  }
97
+
98
+ /* ----------------- AUTO REPORT GENERATION ----------------- */
99
+ async function generateLocalReport() {
100
+ try {
101
+ const buildName = process.env.BROWSERSTACK_BUILD_NAME || 'local-build';
102
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0];
103
+ const reportDir = `./reports/mochawesome/${buildName}-${timestamp}`;
104
+ const jsonFile = path.join(process.cwd(), reportDir, 'mochawesome.json');
105
+
106
+ // Check if JSON report exists
107
+ if (!fs.existsSync(jsonFile)) {
108
+ console.log(`โš ๏ธ No JSON report found at: ${jsonFile}`);
109
+ console.log('๐Ÿ’ก Skipping report generation. Ensure tests ran successfully.');
110
+ return;
111
+ }
112
+
113
+ console.log(`๐Ÿ“Š Generating HTML report from: ${jsonFile}`);
114
+
115
+ // Use marge to generate HTML report
116
+ const { execSync } = require('child_process');
117
+ execSync(`npx marge "${jsonFile}" -o "${reportDir}"`, {
118
+ stdio: 'inherit',
119
+ cwd: process.cwd()
120
+ });
121
+
122
+ const htmlFile = path.join(reportDir, 'mochawesome.html');
123
+ console.log(`โœ… HTML report generated successfully!`);
124
+ console.log(`๐Ÿ“ Location: ${htmlFile}`);
125
+
126
+ // Auto-open report in browser
127
+ if (fs.existsSync(htmlFile)) {
128
+ console.log(`๐ŸŒ Opening report in browser...`);
129
+ try {
130
+ const openCommand = process.platform === 'darwin' ? 'open' :
131
+ process.platform === 'win32' ? 'start' :
132
+ 'xdg-open';
133
+ execSync(`${openCommand} "${htmlFile}"`, { stdio: 'ignore' });
134
+ } catch (error) {
135
+ console.log(`๐Ÿ’ก To view report manually, open: ${htmlFile}`);
136
+ }
137
+ }
138
+ } catch (error) {
139
+ console.error(`โŒ Error generating local report: ${error.message}`);
140
+ // Don't fail the entire process if report generation fails
141
+ }
142
+ }
93
143
  /* ------------------ COMMON CONFIG ------------------ */
94
144
 
95
145
  const commonHooks = {
@@ -102,6 +152,12 @@ const commonHooks = {
102
152
 
103
153
  console.log('==== ON PREPARE HOOK ====');
104
154
 
155
+ // Reset the "already updated" flag at the START of every run
156
+ // This prevents stale 'TRUE' values (persisted to disk by node-localstorage)
157
+ // from a previous run causing the current run to skip the execution update.
158
+ BUFFER.removeItem('FROTH_UPDATE_EXECUTION');
159
+ console.log('๐Ÿงน Reset FROTH_UPDATE_EXECUTION flag (fresh run)');
160
+
105
161
  await setAllDetails.setEnvVariables();
106
162
  await setAllDetails.setExecutionDetails();
107
163
  await setAllDetails.setSuiteDetails();
@@ -206,11 +262,6 @@ const commonHooks = {
206
262
  }
207
263
  }
208
264
 
209
- // await exeDetails.update_CICDRUNID_ReportUrl(
210
- // BUFFER.getItem('ORGANISATION_DOMAIN_URL'),
211
- // BUFFER.getItem('FROTH_LOGIN_TOKEN'),
212
- // BUFFER.getItem('FROTH_EXECUTION_ID')
213
- // );
214
265
  },
215
266
  /* ========== BEFORE TEST ========== */
216
267
  beforeTest: async (test) => {
@@ -237,6 +288,7 @@ const commonHooks = {
237
288
  console.log(`==== AFTER TEST HOOK ===='${test.title}'`);
238
289
  if (!totalTestDuration) totalTestDuration = 0;
239
290
  totalTestDuration += duration;
291
+ testsRun++;
240
292
 
241
293
  const fileName = path.basename(test.file);
242
294
  let scriptresult = "NOT RUN";
@@ -245,11 +297,13 @@ const commonHooks = {
245
297
 
246
298
  if (passed) {
247
299
  scriptresult = "PASSED"
300
+ testsPassed++;
248
301
  resultdetails.comments.push(`${test.title} - passed`);
249
302
  console.log(`====> resultdetails comments: ${resultdetails.comments}`);
250
303
  }
251
304
  else if (!finalPassed || error) {
252
305
  scriptresult = "FAILED"
306
+ testsFailed++;
253
307
  console.log(`====> Failed or error while executing the test: ${error ? error.message : "Test failed"}`);
254
308
  if (!resultdetails.comments.some(comment => typeof comment === 'string' && comment.includes(test.title)))
255
309
  resultdetails.comments.push(`${test.title} - failed: ${error ? error.message : "Test failed"}`);
@@ -285,13 +339,29 @@ const commonHooks = {
285
339
  console.log('โฑ ==== Execution time calculation ==== ');
286
340
  const endTime = Date.now();
287
341
  const totalTime = endTime - suiteStartTime; // Full WDIO+CI elapsed time
288
- // resultdetails.excution_time = await msToTime(totalTime);
289
342
 
290
- console.log("Exit code after session===>" + exitCode)
291
- if (exitCode === undefined || exitCode === null) {
292
- exitCode = 1 // treat as failure
343
+ // Calculate execution time (HH:MM:SS) and attach it for local runs.
344
+ // getexecutionDetails.js will only send this to the API when PLATFORM === 'local'.
345
+ if (process.env.PLATFORM === 'local') {
346
+ resultdetails.excution_time = await msToTime(totalTime);
347
+ console.log(`โฑ Local execution time (hh:mm:ss): ${resultdetails.excution_time}`);
293
348
  }
294
- resultdetails.excution_status = exitCode === 0 ? 'PASSED' : 'FAILED';
349
+
350
+ console.log(`๐Ÿ“Š Tests run: ${testsRun}, Passed: ${testsPassed}, Failed: ${testsFailed}`);
351
+ console.log(`Exit code after session===> ${exitCode}`);
352
+
353
+ if (testsRun > 0) {
354
+ // Tests actually executed - use real results
355
+ resultdetails.excution_status = testsFailed > 0 ? 'FAILED' : 'PASSED';
356
+ } else {
357
+ // No tests ran (session/driver failure or empty suite) - use exitCode
358
+ if (exitCode === undefined || exitCode === null) {
359
+ exitCode = 1; // treat as failure
360
+ }
361
+ resultdetails.excution_status = exitCode === 0 ? 'PASSED' : 'FAILED';
362
+ }
363
+
364
+ console.log(`๐Ÿท๏ธ Execution status determined: ${resultdetails.excution_status}`);
295
365
 
296
366
  console.log('Comments being sent:', resultdetails.comments);
297
367
  //resultdetails.comments.push(BUFFER.getItem('ALL_COMMENTS'))
@@ -311,7 +381,7 @@ const commonHooks = {
311
381
  'completed'
312
382
  );
313
383
  }
314
-
384
+
315
385
  }
316
386
 
317
387
 
@@ -348,6 +418,13 @@ const commonHooks = {
348
418
 
349
419
  await safeUpdateExecution();
350
420
  BUFFER.clear();
421
+
422
+ // Auto-generate HTML report for local execution
423
+ if (process.env.PLATFORM === 'local') {
424
+ console.log('๐Ÿ“Š Generating local test report...');
425
+ await generateLocalReport();
426
+ }
427
+
351
428
  return exitCode;
352
429
  },
353
430
 
@@ -1,11 +1,64 @@
1
- module.exports = () => ({
2
- services: ['appium'],
1
+ const commonHooks = require('../commonhook');
3
2
 
3
+ module.exports = () => {
4
+ // Generate unique report directory using BROWSERSTACK_BUILD_NAME
5
+ const buildName = process.env.BROWSERSTACK_BUILD_NAME || 'local-mobile-build';
6
+ // Remove spaces and special characters from build name for folder name - use underscores
7
+ const cleanBuildName = buildName.replace(/[^a-zA-Z0-9_]/g, '_');
8
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0]; // YYYY-MM-DD format
9
+ const uniqueReportDir = `./reports/allure/${cleanBuildName}-${timestamp}`;
4
10
 
5
- capabilities: [{
6
- platformName: 'Android',
7
- 'appium:deviceName': 'Android Emulator',
8
- 'appium:automationName': 'UiAutomator2',
9
- 'appium:app': process.env.LOCAL_APP_PATH
10
- }]
11
- });
11
+ console.log(`๐Ÿ“ฑ Mobile local execution configured`);
12
+ console.log(`๐Ÿ“Š Allure results will be saved in: ${uniqueReportDir}`);
13
+ console.log(`๐Ÿ’ก Report will be generated automatically after execution completes`);
14
+
15
+ return {
16
+ // Common hooks - enable DB status updates (execution + script status)
17
+ ...commonHooks,
18
+
19
+ services: [
20
+ 'appium',
21
+ // Real-time screenshot capture (works even if execution stopped mid-way)
22
+ [require('../screenshot-service').ScreenshotService]
23
+ ],
24
+
25
+ capabilities: [{
26
+ platformName: 'Android',
27
+ 'appium:deviceName': 'Android Emulator',
28
+ 'appium:automationName': 'UiAutomator2',
29
+ 'appium:app': process.env.LOCAL_APP_PATH
30
+ }],
31
+
32
+ // Allure Reporter Configuration
33
+ reporters: [
34
+ ['allure', {
35
+ outputDir: uniqueReportDir,
36
+ disableWebdriverStepsReporting: false, // Enable step reporting
37
+ disableWebdriverScreenshotsReporting: false, // Enable automatic screenshot capture
38
+ useCucumberStepReporter: false,
39
+ disableMochaHooks: false,
40
+ allureCode: true,
41
+ captureAllureScreenshots: true
42
+ }]
43
+ ],
44
+
45
+ // Auto-generate report after execution completes
46
+ onComplete: async function(exitCode, config, capabilities, results) {
47
+ const { execSync } = require('child_process');
48
+ const nodePath = require('path');
49
+ try {
50
+ // Add a small delay to ensure Allure finishes writing all files
51
+ await new Promise(resolve => setTimeout(resolve, 1000));
52
+
53
+ // Resolve script path relative to framework package (works from any CWD)
54
+ const scriptPath = nodePath.resolve(__dirname, '../../allure-report-utils/generate-allure-report.js');
55
+
56
+ console.log('\n๐ŸŽฏ Generating Allure report...');
57
+ execSync(`node "${scriptPath}"`, { stdio: 'inherit', cwd: process.cwd() });
58
+ console.log('โœ… Report generation complete!');
59
+ } catch (error) {
60
+ console.error('โŒ Error generating report:', error.message);
61
+ }
62
+ }
63
+ };
64
+ };