froth-webdriverio-framework 7.0.119-dev1.10 → 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/allure-report-utils/allure-helper.js +10 -22
- package/allure-report-utils/generate-allure-report.js +39 -9
- package/froth_api_calls/getexecutionDetails.js +15 -41
- package/froth_configs/base.config.js +3 -44
- package/froth_configs/commonhook.js +36 -12
- package/froth_configs/local/mobile.config.js +5 -0
- package/froth_configs/local/web.config.js +5 -0
- package/froth_configs/screenshot-service.js +6 -27
- package/package.json +1 -1
- package/allure-screenshots/Recorded_Scripts_Recorded_test_steps_success.png +0 -0
- package/allure-screenshots/Recorded_Scripts_Recorded_test_steps_success_2026-06-19T08-07-53-694Z.png +0 -0
- package/allure-screenshots/Recorded_Scripts_Recorded_test_steps_success_2026-06-19T08-10-50-605Z.png +0 -0
- package/allure-screenshots/action_001_setWindowSize_2026-06-19T08-10-35-850Z.png +0 -0
- package/allure-screenshots/action_001_url.png +0 -0
- package/allure-screenshots/action_002_click.png +0 -0
- package/allure-screenshots/action_002_url_2026-06-19T08-10-37-002Z.png +0 -0
- package/allure-screenshots/action_003_clearValue.png +0 -0
- package/allure-screenshots/action_003_setValue.png +0 -0
- package/allure-screenshots/action_003_waitForExist_2026-06-19T08-10-42-230Z.png +0 -0
- package/allure-screenshots/action_004_click.png +0 -0
- package/allure-screenshots/action_004_waitForExist_2026-06-19T08-10-42-353Z.png +0 -0
- package/allure-screenshots/action_005_clearValue.png +0 -0
- package/allure-screenshots/action_005_click_2026-06-19T08-10-43-186Z.png +0 -0
- package/allure-screenshots/action_005_setValue.png +0 -0
- package/allure-screenshots/action_006_click_2026-06-19T08-10-43-301Z.png +0 -0
- package/allure-screenshots/action_007_clearValue_2026-06-19T08-10-43-974Z.png +0 -0
- package/allure-screenshots/action_008_clearValue_2026-06-19T08-10-44-058Z.png +0 -0
- package/allure-screenshots/action_009_addValue_2026-06-19T08-10-44-559Z.png +0 -0
- package/allure-screenshots/action_010_addValue_2026-06-19T08-10-44-656Z.png +0 -0
- package/allure-screenshots/action_011_setValue_2026-06-19T08-10-44-779Z.png +0 -0
- package/allure-screenshots/action_012_setValue_2026-06-19T08-10-44-905Z.png +0 -0
- package/allure-screenshots/action_013_click_2026-06-19T08-10-45-787Z.png +0 -0
- package/allure-screenshots/action_014_click_2026-06-19T08-10-45-892Z.png +0 -0
- package/allure-screenshots/action_015_clearValue_2026-06-19T08-10-46-549Z.png +0 -0
- package/allure-screenshots/action_016_clearValue_2026-06-19T08-10-46-635Z.png +0 -0
- package/allure-screenshots/action_017_addValue_2026-06-19T08-10-46-868Z.png +0 -0
- package/allure-screenshots/action_018_addValue_2026-06-19T08-10-46-997Z.png +0 -0
- package/allure-screenshots/action_019_setValue_2026-06-19T08-10-47-080Z.png +0 -0
- package/allure-screenshots/action_020_setValue_2026-06-19T08-10-47-163Z.png +0 -0
- package/allure-screenshots/action_021_click_2026-06-19T08-10-47-377Z.png +0 -0
- package/allure-screenshots/action_022_click_2026-06-19T08-10-47-472Z.png +0 -0
|
@@ -41,32 +41,20 @@ class AllureHelper {
|
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
43
|
* Capture and attach screenshot to Allure report
|
|
44
|
+
* Screenshots stored ONLY in Allure report folder (via addAttachment)
|
|
44
45
|
* @param {string} screenshotName - Name for the screenshot
|
|
45
46
|
*/
|
|
46
47
|
static async captureScreenshot(screenshotName = 'screenshot') {
|
|
47
48
|
try {
|
|
48
|
-
//
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
// Read the screenshot
|
|
59
|
-
if (fs.existsSync(screenshotPath)) {
|
|
60
|
-
const imageBuffer = fs.readFileSync(screenshotPath);
|
|
61
|
-
|
|
62
|
-
// Attach to Allure using the proper method
|
|
63
|
-
const allure = require('@wdio/allure-reporter').default;
|
|
64
|
-
allure.addAttachment(screenshotName, imageBuffer, 'image/png');
|
|
65
|
-
|
|
66
|
-
console.log(`📸 Screenshot captured: ${screenshotName}`);
|
|
67
|
-
} else {
|
|
68
|
-
console.warn(`⚠️ Screenshot file not found: ${screenshotPath}`);
|
|
69
|
-
}
|
|
49
|
+
// Take screenshot to buffer (no separate file storage)
|
|
50
|
+
const screenshotBase64 = await browser.takeScreenshot();
|
|
51
|
+
const imageBuffer = Buffer.from(screenshotBase64, 'base64');
|
|
52
|
+
|
|
53
|
+
// Attach to Allure using the proper method (stored in report folder)
|
|
54
|
+
const allure = require('@wdio/allure-reporter').default;
|
|
55
|
+
allure.addAttachment(screenshotName, imageBuffer, 'image/png');
|
|
56
|
+
|
|
57
|
+
console.log(`📸 Screenshot captured: ${screenshotName}`);
|
|
70
58
|
} catch (error) {
|
|
71
59
|
console.error(`❌ Error capturing screenshot: ${error.message}`);
|
|
72
60
|
}
|
|
@@ -67,29 +67,59 @@ function generateAllureReport(reportDir) {
|
|
|
67
67
|
|
|
68
68
|
execSync(command, { stdio: 'inherit' });
|
|
69
69
|
|
|
70
|
-
//
|
|
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)
|
|
71
74
|
const startScriptPath = path.join(outputDir, 'start-server.sh');
|
|
72
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
|
+
|
|
73
82
|
echo "🌐 Starting Allure Report Server..."
|
|
74
|
-
echo "📁 Report: $
|
|
75
|
-
echo "🔗 Open: http://localhost:8080"
|
|
83
|
+
echo "📁 Report: $(pwd)"
|
|
84
|
+
echo "🔗 Open in browser: http://localhost:8080"
|
|
76
85
|
echo ""
|
|
77
|
-
echo "Press Ctrl+C to stop the server"
|
|
86
|
+
echo "💡 Press Ctrl+C to stop the server"
|
|
78
87
|
echo ""
|
|
79
|
-
|
|
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
|
|
80
96
|
`;
|
|
81
97
|
fs.writeFileSync(startScriptPath, startScriptContent, { mode: 0o755 });
|
|
82
98
|
|
|
83
99
|
// Create a Windows batch file
|
|
84
100
|
const batchScriptPath = path.join(outputDir, 'start-server.bat');
|
|
85
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
|
+
|
|
86
113
|
echo 🌐 Starting Allure Report Server...
|
|
87
|
-
echo 📁 Report:
|
|
88
|
-
echo 🔗 Open: http://localhost:8080
|
|
114
|
+
echo 📁 Report: %REPORT_DIR%
|
|
115
|
+
echo 🔗 Open in browser: http://localhost:8080
|
|
89
116
|
echo.
|
|
90
|
-
echo Press Ctrl+C to stop the server
|
|
117
|
+
echo 💡 Press Ctrl+C to stop the server
|
|
91
118
|
echo.
|
|
92
|
-
|
|
119
|
+
|
|
120
|
+
cd /d "%PROJECT_ROOT%"
|
|
121
|
+
call npx allure open "%REPORT_DIR%" --port 8080
|
|
122
|
+
pause
|
|
93
123
|
`;
|
|
94
124
|
fs.writeFileSync(batchScriptPath, batchScriptContent);
|
|
95
125
|
|
|
@@ -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
|
};
|
|
@@ -27,49 +27,8 @@ module.exports = {
|
|
|
27
27
|
timeout: 300000
|
|
28
28
|
},
|
|
29
29
|
|
|
30
|
-
// Enable screenshot capture for Allure (
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
// Test hooks
|
|
34
|
-
before: async function() {
|
|
35
|
-
console.log('🔧 BEFORE hook triggered in base config');
|
|
36
|
-
},
|
|
37
|
-
|
|
38
|
-
// Capture final screenshot directly in the report folder
|
|
39
|
-
after: async function(results) {
|
|
40
|
-
console.log('🔧 AFTER hook triggered in base config');
|
|
41
|
-
|
|
42
|
-
// Only process if running locally
|
|
43
|
-
if (process.env.PLATFORM === 'local') {
|
|
44
|
-
try {
|
|
45
|
-
const fs = require('fs');
|
|
46
|
-
const nodePath = require('path');
|
|
47
|
-
|
|
48
|
-
// Find the most recent Allure results directory
|
|
49
|
-
const reportsBaseDir = './reports/allure';
|
|
50
|
-
if (fs.existsSync(reportsBaseDir)) {
|
|
51
|
-
const dirs = fs.readdirSync(reportsBaseDir)
|
|
52
|
-
.map(file => nodePath.join(reportsBaseDir, file))
|
|
53
|
-
.filter(file => fs.statSync(file).isDirectory())
|
|
54
|
-
.sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs);
|
|
55
|
-
|
|
56
|
-
if (dirs.length > 0) {
|
|
57
|
-
const latestDir = dirs[0];
|
|
58
|
-
console.log(`📁 Report directory: ${latestDir}`);
|
|
30
|
+
// Enable screenshot capture for Allure (handled by screenshot-service)
|
|
31
|
+
// Screenshots stored ONLY in Allure report folder - no separate folders
|
|
32
|
+
};
|
|
59
33
|
|
|
60
|
-
// Save final screenshot directly to the report directory
|
|
61
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
62
|
-
const screenshotName = `Test_Suite_Completion_${timestamp}.png`;
|
|
63
|
-
const screenshotPath = nodePath.join(latestDir, screenshotName);
|
|
64
34
|
|
|
65
|
-
console.log(`📸 Saving final screenshot directly to report: ${screenshotName}`);
|
|
66
|
-
await browser.saveScreenshot(screenshotPath);
|
|
67
|
-
console.log(`✅ Screenshot saved in report folder: ${screenshotName}`);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
} catch (error) {
|
|
71
|
-
console.error('❌ Error saving screenshot to report folder:', error.message);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
};
|
|
@@ -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 ----------------- */
|
|
@@ -148,6 +152,12 @@ const commonHooks = {
|
|
|
148
152
|
|
|
149
153
|
console.log('==== ON PREPARE HOOK ====');
|
|
150
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
|
+
|
|
151
161
|
await setAllDetails.setEnvVariables();
|
|
152
162
|
await setAllDetails.setExecutionDetails();
|
|
153
163
|
await setAllDetails.setSuiteDetails();
|
|
@@ -252,11 +262,6 @@ const commonHooks = {
|
|
|
252
262
|
}
|
|
253
263
|
}
|
|
254
264
|
|
|
255
|
-
// await exeDetails.update_CICDRUNID_ReportUrl(
|
|
256
|
-
// BUFFER.getItem('ORGANISATION_DOMAIN_URL'),
|
|
257
|
-
// BUFFER.getItem('FROTH_LOGIN_TOKEN'),
|
|
258
|
-
// BUFFER.getItem('FROTH_EXECUTION_ID')
|
|
259
|
-
// );
|
|
260
265
|
},
|
|
261
266
|
/* ========== BEFORE TEST ========== */
|
|
262
267
|
beforeTest: async (test) => {
|
|
@@ -283,6 +288,7 @@ const commonHooks = {
|
|
|
283
288
|
console.log(`==== AFTER TEST HOOK ===='${test.title}'`);
|
|
284
289
|
if (!totalTestDuration) totalTestDuration = 0;
|
|
285
290
|
totalTestDuration += duration;
|
|
291
|
+
testsRun++;
|
|
286
292
|
|
|
287
293
|
const fileName = path.basename(test.file);
|
|
288
294
|
let scriptresult = "NOT RUN";
|
|
@@ -291,11 +297,13 @@ const commonHooks = {
|
|
|
291
297
|
|
|
292
298
|
if (passed) {
|
|
293
299
|
scriptresult = "PASSED"
|
|
300
|
+
testsPassed++;
|
|
294
301
|
resultdetails.comments.push(`${test.title} - passed`);
|
|
295
302
|
console.log(`====> resultdetails comments: ${resultdetails.comments}`);
|
|
296
303
|
}
|
|
297
304
|
else if (!finalPassed || error) {
|
|
298
305
|
scriptresult = "FAILED"
|
|
306
|
+
testsFailed++;
|
|
299
307
|
console.log(`====> Failed or error while executing the test: ${error ? error.message : "Test failed"}`);
|
|
300
308
|
if (!resultdetails.comments.some(comment => typeof comment === 'string' && comment.includes(test.title)))
|
|
301
309
|
resultdetails.comments.push(`${test.title} - failed: ${error ? error.message : "Test failed"}`);
|
|
@@ -331,13 +339,29 @@ const commonHooks = {
|
|
|
331
339
|
console.log('⏱ ==== Execution time calculation ==== ');
|
|
332
340
|
const endTime = Date.now();
|
|
333
341
|
const totalTime = endTime - suiteStartTime; // Full WDIO+CI elapsed time
|
|
334
|
-
// resultdetails.excution_time = await msToTime(totalTime);
|
|
335
342
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
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}`);
|
|
339
348
|
}
|
|
340
|
-
|
|
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}`);
|
|
341
365
|
|
|
342
366
|
console.log('Comments being sent:', resultdetails.comments);
|
|
343
367
|
//resultdetails.comments.push(BUFFER.getItem('ALL_COMMENTS'))
|
|
@@ -357,7 +381,7 @@ const commonHooks = {
|
|
|
357
381
|
'completed'
|
|
358
382
|
);
|
|
359
383
|
}
|
|
360
|
-
|
|
384
|
+
|
|
361
385
|
}
|
|
362
386
|
|
|
363
387
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const commonHooks = require('../commonhook');
|
|
2
|
+
|
|
1
3
|
module.exports = () => {
|
|
2
4
|
// Generate unique report directory using BROWSERSTACK_BUILD_NAME
|
|
3
5
|
const buildName = process.env.BROWSERSTACK_BUILD_NAME || 'local-mobile-build';
|
|
@@ -11,6 +13,9 @@ module.exports = () => {
|
|
|
11
13
|
console.log(`💡 Report will be generated automatically after execution completes`);
|
|
12
14
|
|
|
13
15
|
return {
|
|
16
|
+
// Common hooks - enable DB status updates (execution + script status)
|
|
17
|
+
...commonHooks,
|
|
18
|
+
|
|
14
19
|
services: [
|
|
15
20
|
'appium',
|
|
16
21
|
// Real-time screenshot capture (works even if execution stopped mid-way)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const commonHooks = require('../commonhook');
|
|
2
|
+
|
|
1
3
|
module.exports = (bsCaps) => {
|
|
2
4
|
const browserName = (bsCaps.browserName || 'chrome').toLowerCase();
|
|
3
5
|
|
|
@@ -108,6 +110,9 @@ module.exports = (bsCaps) => {
|
|
|
108
110
|
}
|
|
109
111
|
|
|
110
112
|
const config = {
|
|
113
|
+
// Common hooks - enable DB status updates (execution + script status)
|
|
114
|
+
...commonHooks,
|
|
115
|
+
|
|
111
116
|
// Use direct WebDriver connection without Selenium Grid/Hub
|
|
112
117
|
// Opens Chrome/Firefox directly - no selenium-server
|
|
113
118
|
protocol: 'webdriver',
|
|
@@ -4,17 +4,15 @@
|
|
|
4
4
|
* - After each KEY ACTION (click, input, navigation) - no duplicates
|
|
5
5
|
* - After each TEST (afterTest) - final state per test
|
|
6
6
|
* - Works even if execution is stopped mid-way
|
|
7
|
+
* - Screenshots stored ONLY in Allure report (via addAttachment) - no separate folders
|
|
7
8
|
* - Deduplicates identical screenshots to avoid clutter
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
|
-
const fs = require('fs');
|
|
11
|
-
const path = require('path');
|
|
12
11
|
const crypto = require('crypto');
|
|
13
12
|
|
|
14
13
|
class ScreenshotService {
|
|
15
14
|
constructor(options = {}) {
|
|
16
15
|
this.options = options;
|
|
17
|
-
this.screenshotDir = './allure-screenshots';
|
|
18
16
|
this.actionCounter = 0;
|
|
19
17
|
this.lastScreenshotHash = null;
|
|
20
18
|
this.lastActionTime = 0;
|
|
@@ -22,10 +20,7 @@ class ScreenshotService {
|
|
|
22
20
|
|
|
23
21
|
// Runs before test execution starts
|
|
24
22
|
async onPrepare() {
|
|
25
|
-
|
|
26
|
-
fs.mkdirSync(this.screenshotDir, { recursive: true });
|
|
27
|
-
}
|
|
28
|
-
console.log('📸 Screenshot Service initialized - deduplicated real-time capture');
|
|
23
|
+
console.log('📸 Screenshot Service initialized - Allure-only capture (no duplicate folders)');
|
|
29
24
|
}
|
|
30
25
|
|
|
31
26
|
// Runs after EACH KEY ACTION - captures screenshot (with deduplication)
|
|
@@ -57,7 +52,7 @@ class ScreenshotService {
|
|
|
57
52
|
try {
|
|
58
53
|
const allure = require('@wdio/allure-reporter').default;
|
|
59
54
|
|
|
60
|
-
// Take screenshot to memory (buffer)
|
|
55
|
+
// Take screenshot to memory (buffer) - no separate file
|
|
61
56
|
const screenshotBase64 = await browser.takeScreenshot();
|
|
62
57
|
const imageBuffer = Buffer.from(screenshotBase64, 'base64');
|
|
63
58
|
|
|
@@ -73,12 +68,7 @@ class ScreenshotService {
|
|
|
73
68
|
this.actionCounter++;
|
|
74
69
|
const actionDesc = this.formatActionDescription(commandName, args);
|
|
75
70
|
|
|
76
|
-
//
|
|
77
|
-
const screenshotName = `action_${String(this.actionCounter).padStart(3, '0')}_${commandName}`;
|
|
78
|
-
const screenshotPath = path.join(this.screenshotDir, `${screenshotName}.png`);
|
|
79
|
-
fs.writeFileSync(screenshotPath, imageBuffer);
|
|
80
|
-
|
|
81
|
-
// Attach to Allure IMMEDIATELY (real-time)
|
|
71
|
+
// Attach to Allure IMMEDIATELY (Allure stores it in its report directory)
|
|
82
72
|
allure.addAttachment(`📸 ${commandName} ${actionDesc}`.trim(), imageBuffer, 'image/png');
|
|
83
73
|
} catch (err) {
|
|
84
74
|
// Silent fail to avoid breaking tests
|
|
@@ -95,9 +85,6 @@ class ScreenshotService {
|
|
|
95
85
|
if (commandName === 'setValue' && args && args[1] !== undefined) {
|
|
96
86
|
return `(input "${args[1].toString().substring(0, 25)}")`;
|
|
97
87
|
}
|
|
98
|
-
if (commandName === 'addValue' && args && args[1] !== undefined) {
|
|
99
|
-
return `(added "${args[1].toString().substring(0, 25)}")`;
|
|
100
|
-
}
|
|
101
88
|
if (commandName === 'click' && args && args[0]) {
|
|
102
89
|
return `(${args[0].toString().substring(0, 40)})`;
|
|
103
90
|
}
|
|
@@ -115,12 +102,8 @@ class ScreenshotService {
|
|
|
115
102
|
|
|
116
103
|
try {
|
|
117
104
|
const allure = require('@wdio/allure-reporter').default;
|
|
118
|
-
const cleanTitle = test.title.replace(/[^a-zA-Z0-9]/g, '_');
|
|
119
|
-
const cleanParent = test.parent ? test.parent.replace(/[^a-zA-Z0-9]/g, '_') : 'test';
|
|
120
105
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
// Take screenshot to buffer
|
|
106
|
+
// Take screenshot to buffer - no separate file
|
|
124
107
|
const screenshotBase64 = await browser.takeScreenshot();
|
|
125
108
|
const imageBuffer = Buffer.from(screenshotBase64, 'base64');
|
|
126
109
|
|
|
@@ -133,13 +116,9 @@ class ScreenshotService {
|
|
|
133
116
|
return;
|
|
134
117
|
}
|
|
135
118
|
|
|
136
|
-
const screenshotName = `${cleanParent}_${cleanTitle}_${status}`;
|
|
137
|
-
const screenshotPath = path.join(this.screenshotDir, `${screenshotName}.png`);
|
|
138
|
-
fs.writeFileSync(screenshotPath, imageBuffer);
|
|
139
|
-
|
|
140
119
|
const attachmentName = passed ? '✅ Test Passed' : '❌ Test Failed';
|
|
141
120
|
allure.addAttachment(attachmentName, imageBuffer, 'image/png');
|
|
142
|
-
console.log(`📸 [Real-time] Test screenshot attached: ${
|
|
121
|
+
console.log(`📸 [Real-time] Test screenshot attached: ${passed ? 'success' : 'failure'}`);
|
|
143
122
|
|
|
144
123
|
// Reset for next test
|
|
145
124
|
this.actionCounter = 0;
|
package/package.json
CHANGED
|
Binary file
|
package/allure-screenshots/Recorded_Scripts_Recorded_test_steps_success_2026-06-19T08-07-53-694Z.png
DELETED
|
Binary file
|
package/allure-screenshots/Recorded_Scripts_Recorded_test_steps_success_2026-06-19T08-10-50-605Z.png
DELETED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|