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.
- package/README.md +245 -0
- package/allure-report-utils/allure-helper.js +214 -0
- package/allure-report-utils/froth-report.js +142 -0
- package/allure-report-utils/generate-allure-report.js +197 -0
- package/allure-report-utils/open-allure-report.js +97 -0
- package/froth_api_calls/getexecutionDetails.js +15 -41
- package/froth_configs/base.config.js +7 -4
- package/froth_configs/commonhook.js +89 -12
- package/froth_configs/local/mobile.config.js +62 -9
- package/froth_configs/local/web.config.js +144 -33
- package/froth_configs/screenshot-service.js +136 -0
- package/froth_configs/wdio.common.conf.js +112 -1
- package/package.json +62 -50
|
@@ -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
|
|
130
|
-
|
|
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
|
-
//
|
|
145
|
-
//
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
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
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
+
};
|