froth-webdriverio-framework 7.0.119-dev1.0 → 7.0.119-dev1.10
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 +226 -0
- package/allure-report-utils/froth-report.js +142 -0
- package/allure-report-utils/generate-allure-report.js +167 -0
- package/allure-report-utils/open-allure-report.js +97 -0
- 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
- package/froth_configs/base.config.js +47 -3
- package/froth_configs/commonhook.js +53 -0
- package/froth_configs/local/mobile.config.js +57 -9
- package/froth_configs/local/web.config.js +140 -35
- package/froth_configs/screenshot-service.js +157 -0
- package/froth_configs/wdio.common.conf.js +112 -1
- package/package.json +62 -50
|
@@ -1,58 +1,163 @@
|
|
|
1
1
|
module.exports = (bsCaps) => {
|
|
2
2
|
const browserName = (bsCaps.browserName || 'chrome').toLowerCase();
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
|
|
4
|
+
// Generate unique report directory using BROWSERSTACK_BUILD_NAME
|
|
5
|
+
const buildName = process.env.BROWSERSTACK_BUILD_NAME || 'local-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}`;
|
|
10
|
+
|
|
11
|
+
// Normalize browser names for WebDriver
|
|
12
|
+
const browserNameMap = {
|
|
13
|
+
chrome: 'chrome',
|
|
14
|
+
chromium: 'chrome',
|
|
15
|
+
firefox: 'firefox',
|
|
16
|
+
edge: 'MicrosoftEdge',
|
|
17
|
+
msedge: 'MicrosoftEdge',
|
|
18
|
+
'microsoft edge': 'MicrosoftEdge',
|
|
19
|
+
safari: 'safari',
|
|
20
|
+
ie: 'internet explorer',
|
|
21
|
+
'internet explorer': 'internet explorer',
|
|
22
|
+
'internet-explorer': 'internet explorer'
|
|
13
23
|
};
|
|
14
24
|
|
|
15
|
-
const
|
|
25
|
+
const normalizedBrowserName = browserNameMap[browserName] || 'chrome';
|
|
16
26
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
27
|
+
// Configure browser-specific capabilities
|
|
28
|
+
let capabilities;
|
|
29
|
+
let service;
|
|
20
30
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
config.capabilities = [{
|
|
31
|
+
if (normalizedBrowserName === 'chrome') {
|
|
32
|
+
capabilities = [{
|
|
24
33
|
browserName: 'chrome',
|
|
25
34
|
'goog:chromeOptions': {
|
|
26
|
-
|
|
35
|
+
args: [
|
|
36
|
+
'--no-sandbox',
|
|
37
|
+
'--disable-dev-shm-usage',
|
|
38
|
+
'--disable-gpu',
|
|
39
|
+
'--disable-web-security',
|
|
40
|
+
'--disable-features=VizDisplayCompositor'
|
|
41
|
+
]
|
|
27
42
|
}
|
|
28
43
|
}];
|
|
29
|
-
|
|
30
|
-
|
|
44
|
+
|
|
45
|
+
// Use wdio-chromedriver-service - auto-downloads ChromeDriver at runtime
|
|
46
|
+
// No admin permission needed - downloads to node_modules/
|
|
47
|
+
service = ['chromedriver', {
|
|
48
|
+
// Automatically download ChromeDriver if not present
|
|
49
|
+
chromedriverOptions: {
|
|
50
|
+
// Check for ChromeDriver updates (set to false for faster startup)
|
|
51
|
+
checkForUpdates: true,
|
|
52
|
+
// Use latest ChromeDriver compatible with installed Chrome
|
|
53
|
+
// If false, uses version specified in package.json chromedriver dependency
|
|
54
|
+
autoDownload: true
|
|
55
|
+
}
|
|
56
|
+
}]
|
|
57
|
+
|
|
58
|
+
} else if (normalizedBrowserName === 'firefox') {
|
|
59
|
+
capabilities = [{
|
|
31
60
|
browserName: 'firefox',
|
|
32
61
|
'moz:firefoxOptions': {
|
|
33
|
-
//
|
|
62
|
+
//args: ['-headless'],
|
|
63
|
+
prefs: {
|
|
64
|
+
'dom.webdriver.enabled': false,
|
|
65
|
+
'useAutomationExtension': false
|
|
66
|
+
}
|
|
34
67
|
}
|
|
35
68
|
}];
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
69
|
+
|
|
70
|
+
// Use wdio-geckodriver-service for Firefox
|
|
71
|
+
service = ['geckodriver', {
|
|
72
|
+
// Auto-download geckodriver if not present
|
|
73
|
+
geckodriverOptions: {
|
|
74
|
+
// Use the geckodriver from node_modules
|
|
75
|
+
// No need to download separately
|
|
76
|
+
}
|
|
43
77
|
}];
|
|
44
|
-
} else if (
|
|
45
|
-
|
|
46
|
-
browserName: '
|
|
78
|
+
} else if (normalizedBrowserName === 'MicrosoftEdge') {
|
|
79
|
+
capabilities = [{
|
|
80
|
+
browserName: 'MicrosoftEdge',
|
|
81
|
+
'ms:edgeOptions': {
|
|
82
|
+
args: [
|
|
83
|
+
'--no-sandbox',
|
|
84
|
+
'--disable-dev-shm-usage',
|
|
85
|
+
'--disable-gpu'
|
|
86
|
+
]
|
|
87
|
+
}
|
|
47
88
|
}];
|
|
89
|
+
|
|
90
|
+
// EdgeDriver is included with Edge browser - no download needed
|
|
91
|
+
service = ['edgedriver'];
|
|
92
|
+
|
|
48
93
|
} else {
|
|
49
94
|
// Default to Chrome for unsupported browsers
|
|
50
|
-
|
|
51
|
-
browserName: 'chrome'
|
|
95
|
+
capabilities = [{
|
|
96
|
+
browserName: 'chrome',
|
|
97
|
+
'goog:chromeOptions': {
|
|
98
|
+
args: ['--no-sandbox', '--disable-dev-shm-usage', '--disable-gpu']
|
|
99
|
+
}
|
|
100
|
+
}];
|
|
101
|
+
|
|
102
|
+
service = ['chromedriver', {
|
|
103
|
+
chromedriverOptions: {
|
|
104
|
+
checkForUpdates: true,
|
|
105
|
+
autoDownload: true
|
|
106
|
+
}
|
|
52
107
|
}];
|
|
53
108
|
}
|
|
54
109
|
|
|
55
|
-
|
|
110
|
+
const config = {
|
|
111
|
+
// Use direct WebDriver connection without Selenium Grid/Hub
|
|
112
|
+
// Opens Chrome/Firefox directly - no selenium-server
|
|
113
|
+
protocol: 'webdriver',
|
|
114
|
+
|
|
115
|
+
// Auto-download drivers service + real-time screenshot service
|
|
116
|
+
services: [
|
|
117
|
+
...(service.length > 0 ? [service] : []),
|
|
118
|
+
// Real-time screenshot capture (works even if execution stopped mid-way)
|
|
119
|
+
[require('../screenshot-service').ScreenshotService]
|
|
120
|
+
],
|
|
121
|
+
|
|
122
|
+
capabilities: capabilities,
|
|
123
|
+
|
|
124
|
+
// Allure Reporter Configuration
|
|
125
|
+
reporters: [
|
|
126
|
+
['allure', {
|
|
127
|
+
outputDir: uniqueReportDir,
|
|
128
|
+
disableWebdriverStepsReporting: false, // Enable step reporting
|
|
129
|
+
disableWebdriverScreenshotsReporting: false, // Enable automatic screenshot capture
|
|
130
|
+
useCucumberStepReporter: false,
|
|
131
|
+
disableMochaHooks: false,
|
|
132
|
+
allureCode: true,
|
|
133
|
+
captureAllureScreenshots: true
|
|
134
|
+
}]
|
|
135
|
+
],
|
|
136
|
+
|
|
137
|
+
// Auto-generate report after execution completes
|
|
138
|
+
onComplete: async function(exitCode, config, capabilities, results) {
|
|
139
|
+
const { execSync } = require('child_process');
|
|
140
|
+
const nodePath = require('path');
|
|
141
|
+
try {
|
|
142
|
+
// Add a small delay to ensure Allure finishes writing all files
|
|
143
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
144
|
+
|
|
145
|
+
// Resolve script path relative to framework package (works from any CWD)
|
|
146
|
+
const scriptPath = nodePath.resolve(__dirname, '../../allure-report-utils/generate-allure-report.js');
|
|
147
|
+
|
|
148
|
+
console.log('\n🎯 Generating Allure report...');
|
|
149
|
+
execSync(`node "${scriptPath}"`, { stdio: 'inherit', cwd: process.cwd() });
|
|
150
|
+
console.log('✅ Report generation complete!');
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error('❌ Error generating report:', error.message);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
console.log(`🌐 Local execution configured for: ${normalizedBrowserName}`);
|
|
158
|
+
console.log(`📦 Browser drivers will auto-download to node_modules/ (no admin permission needed)`);
|
|
159
|
+
console.log(`📊 Allure results will be saved in: ${uniqueReportDir}`);
|
|
160
|
+
console.log(`💡 Report will be generated automatically after execution completes`);
|
|
56
161
|
|
|
57
162
|
return config;
|
|
58
|
-
};
|
|
163
|
+
};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Froth Screenshot Service for WebDriverIO
|
|
3
|
+
* Captures screenshots in REAL-TIME:
|
|
4
|
+
* - After each KEY ACTION (click, input, navigation) - no duplicates
|
|
5
|
+
* - After each TEST (afterTest) - final state per test
|
|
6
|
+
* - Works even if execution is stopped mid-way
|
|
7
|
+
* - Deduplicates identical screenshots to avoid clutter
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const crypto = require('crypto');
|
|
13
|
+
|
|
14
|
+
class ScreenshotService {
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
this.options = options;
|
|
17
|
+
this.screenshotDir = './allure-screenshots';
|
|
18
|
+
this.actionCounter = 0;
|
|
19
|
+
this.lastScreenshotHash = null;
|
|
20
|
+
this.lastActionTime = 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Runs before test execution starts
|
|
24
|
+
async onPrepare() {
|
|
25
|
+
if (!fs.existsSync(this.screenshotDir)) {
|
|
26
|
+
fs.mkdirSync(this.screenshotDir, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
console.log('📸 Screenshot Service initialized - deduplicated real-time capture');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Runs after EACH KEY ACTION - captures screenshot (with deduplication)
|
|
32
|
+
async afterCommand(commandName, args, result, error) {
|
|
33
|
+
// Only capture for local platform
|
|
34
|
+
if (process.env.PLATFORM !== 'local') {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Skip screenshot commands to avoid recursion
|
|
39
|
+
if (commandName === 'saveScreenshot' || commandName === 'takeScreenshot') {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ONLY capture these 3 key actions: navigation, click, input
|
|
44
|
+
const captureCommands = ['url', 'click', 'setValue'];
|
|
45
|
+
|
|
46
|
+
if (!captureCommands.includes(commandName)) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Throttle: skip if last screenshot was less than 500ms ago (rapid commands)
|
|
51
|
+
const now = Date.now();
|
|
52
|
+
if (now - this.lastActionTime < 500) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
this.lastActionTime = now;
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const allure = require('@wdio/allure-reporter').default;
|
|
59
|
+
|
|
60
|
+
// Take screenshot to memory (buffer) instead of file for dedup check
|
|
61
|
+
const screenshotBase64 = await browser.takeScreenshot();
|
|
62
|
+
const imageBuffer = Buffer.from(screenshotBase64, 'base64');
|
|
63
|
+
|
|
64
|
+
// Generate hash to detect duplicates
|
|
65
|
+
const hash = crypto.createHash('md5').update(imageBuffer).digest('hex');
|
|
66
|
+
|
|
67
|
+
// Skip if identical to last screenshot (duplicate)
|
|
68
|
+
if (hash === this.lastScreenshotHash) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
this.lastScreenshotHash = hash;
|
|
72
|
+
|
|
73
|
+
this.actionCounter++;
|
|
74
|
+
const actionDesc = this.formatActionDescription(commandName, args);
|
|
75
|
+
|
|
76
|
+
// Save unique screenshot to file
|
|
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)
|
|
82
|
+
allure.addAttachment(`📸 ${commandName} ${actionDesc}`.trim(), imageBuffer, 'image/png');
|
|
83
|
+
} catch (err) {
|
|
84
|
+
// Silent fail to avoid breaking tests
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Format action description for screenshot name
|
|
89
|
+
formatActionDescription(commandName, args) {
|
|
90
|
+
try {
|
|
91
|
+
if (commandName === 'url' && args && args[0]) {
|
|
92
|
+
const url = args[0].toString();
|
|
93
|
+
return `→ ${url.substring(0, 40)}`;
|
|
94
|
+
}
|
|
95
|
+
if (commandName === 'setValue' && args && args[1] !== undefined) {
|
|
96
|
+
return `(input "${args[1].toString().substring(0, 25)}")`;
|
|
97
|
+
}
|
|
98
|
+
if (commandName === 'addValue' && args && args[1] !== undefined) {
|
|
99
|
+
return `(added "${args[1].toString().substring(0, 25)}")`;
|
|
100
|
+
}
|
|
101
|
+
if (commandName === 'click' && args && args[0]) {
|
|
102
|
+
return `(${args[0].toString().substring(0, 40)})`;
|
|
103
|
+
}
|
|
104
|
+
return '';
|
|
105
|
+
} catch (e) {
|
|
106
|
+
return '';
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Runs after each test - captures final test screenshot
|
|
111
|
+
async afterTest(test, context, { error, result, passed, retries }) {
|
|
112
|
+
if (process.env.PLATFORM !== 'local') {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
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
|
+
|
|
121
|
+
const status = passed ? 'success' : 'failure';
|
|
122
|
+
|
|
123
|
+
// Take screenshot to buffer
|
|
124
|
+
const screenshotBase64 = await browser.takeScreenshot();
|
|
125
|
+
const imageBuffer = Buffer.from(screenshotBase64, 'base64');
|
|
126
|
+
|
|
127
|
+
// Check for duplicate
|
|
128
|
+
const hash = crypto.createHash('md5').update(imageBuffer).digest('hex');
|
|
129
|
+
if (hash === this.lastScreenshotHash) {
|
|
130
|
+
// Reset for next test and skip
|
|
131
|
+
this.actionCounter = 0;
|
|
132
|
+
this.lastScreenshotHash = null;
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const screenshotName = `${cleanParent}_${cleanTitle}_${status}`;
|
|
137
|
+
const screenshotPath = path.join(this.screenshotDir, `${screenshotName}.png`);
|
|
138
|
+
fs.writeFileSync(screenshotPath, imageBuffer);
|
|
139
|
+
|
|
140
|
+
const attachmentName = passed ? '✅ Test Passed' : '❌ Test Failed';
|
|
141
|
+
allure.addAttachment(attachmentName, imageBuffer, 'image/png');
|
|
142
|
+
console.log(`📸 [Real-time] Test screenshot attached: ${status}`);
|
|
143
|
+
|
|
144
|
+
// Reset for next test
|
|
145
|
+
this.actionCounter = 0;
|
|
146
|
+
this.lastScreenshotHash = null;
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error('❌ [Real-time] Error capturing test screenshot:', error.message);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Export both as class and factory function
|
|
154
|
+
module.exports = {
|
|
155
|
+
ScreenshotService,
|
|
156
|
+
default: () => new ScreenshotService()
|
|
157
|
+
};
|
|
@@ -63,5 +63,116 @@ if (PLATFORM === 'browserstack' || PLATFORM === 'browserstacklocal') {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
// Debug logging
|
|
67
|
+
console.log('=== CONFIG MERGE DEBUG ===');
|
|
68
|
+
console.log('PLATFORM value:', PLATFORM);
|
|
69
|
+
console.log('baseConfig.afterEach exists:', !!baseConfig.afterEach);
|
|
70
|
+
console.log('envConfig type:', typeof envConfig);
|
|
71
|
+
|
|
72
|
+
// Get the env config but don't merge yet
|
|
73
|
+
const envConfigResult = envConfig(bsCaps);
|
|
74
|
+
console.log('envConfigResult.afterEach exists:', !!envConfigResult.afterEach);
|
|
75
|
+
|
|
76
|
+
// Merge the configs
|
|
77
|
+
const finalConfig = deepmerge(baseConfig, envConfigResult);
|
|
78
|
+
|
|
79
|
+
// Always preserve base config hooks for local platform
|
|
80
|
+
if (PLATFORM === 'local' && baseConfig.afterEach) {
|
|
81
|
+
console.log('Preserving baseConfig.afterEach for local platform');
|
|
82
|
+
finalConfig.afterEach = baseConfig.afterEach;
|
|
83
|
+
} else if (finalConfig.afterEach) {
|
|
84
|
+
console.log('Using envConfig afterEach');
|
|
85
|
+
} else {
|
|
86
|
+
console.log('No afterEach hook found in any config');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
console.log('finalConfig.afterEach type:', typeof finalConfig.afterEach);
|
|
90
|
+
console.log('=== END CONFIG MERGE DEBUG ===');
|
|
91
|
+
|
|
92
|
+
// Add onComplete hook to attach screenshots to test results
|
|
93
|
+
const originalOnComplete = finalConfig.onComplete;
|
|
94
|
+
finalConfig.onComplete = async function(exitCode, config, capabilities, results) {
|
|
95
|
+
console.log('🎯 Attaching screenshots to test results...');
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
const fs = require('fs');
|
|
99
|
+
const path = require('path');
|
|
100
|
+
|
|
101
|
+
// Find the most recent Allure results directory (where screenshots are saved)
|
|
102
|
+
const reportsBaseDir = './reports/allure';
|
|
103
|
+
if (fs.existsSync(reportsBaseDir)) {
|
|
104
|
+
const dirs = fs.readdirSync(reportsBaseDir)
|
|
105
|
+
.map(file => path.join(reportsBaseDir, file))
|
|
106
|
+
.filter(file => fs.statSync(file).isDirectory())
|
|
107
|
+
.sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs);
|
|
108
|
+
|
|
109
|
+
if (dirs.length > 0) {
|
|
110
|
+
const latestDir = dirs[0];
|
|
111
|
+
console.log(`📁 Report directory: ${latestDir}`);
|
|
112
|
+
|
|
113
|
+
// Find screenshots saved directly in the report directory
|
|
114
|
+
const screenshots = fs.readdirSync(latestDir)
|
|
115
|
+
.filter(file => file.endsWith('.png'))
|
|
116
|
+
.map(file => path.join(latestDir, file));
|
|
117
|
+
|
|
118
|
+
console.log(`📸 Found ${screenshots.length} screenshots in report folder`);
|
|
119
|
+
|
|
120
|
+
// Find test result files
|
|
121
|
+
const resultFiles = fs.readdirSync(latestDir)
|
|
122
|
+
.filter(file => file.endsWith('-result.json'));
|
|
123
|
+
|
|
124
|
+
console.log(`📋 Found ${resultFiles.length} test result files`);
|
|
125
|
+
|
|
126
|
+
// Attach a screenshot to each test result
|
|
127
|
+
resultFiles.forEach((resultFile, index) => {
|
|
128
|
+
try {
|
|
129
|
+
const resultPath = path.join(latestDir, resultFile);
|
|
130
|
+
const resultData = JSON.parse(fs.readFileSync(resultPath, 'utf8'));
|
|
131
|
+
|
|
132
|
+
// Add screenshot attachment
|
|
133
|
+
if (screenshots.length > 0) {
|
|
134
|
+
const screenshotIndex = Math.min(index, screenshots.length - 1);
|
|
135
|
+
const screenshotPath = screenshots[screenshotIndex];
|
|
136
|
+
const screenshotBuffer = fs.readFileSync(screenshotPath);
|
|
137
|
+
|
|
138
|
+
// Create attachment in the Allure directory
|
|
139
|
+
const attachmentName = `screenshot-${index}.png`;
|
|
140
|
+
const attachmentPath = path.join(latestDir, attachmentName);
|
|
141
|
+
|
|
142
|
+
fs.writeFileSync(attachmentPath, screenshotBuffer);
|
|
143
|
+
|
|
144
|
+
// Add attachment reference to test result
|
|
145
|
+
if (!resultData.attachments) {
|
|
146
|
+
resultData.attachments = [];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
resultData.attachments.push({
|
|
150
|
+
name: 'Screenshot',
|
|
151
|
+
source: attachmentName,
|
|
152
|
+
type: 'image/png'
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Write updated result file
|
|
156
|
+
fs.writeFileSync(resultPath, JSON.stringify(resultData, null, 2));
|
|
157
|
+
console.log(`✅ Attached screenshot to: ${resultFile}`);
|
|
158
|
+
}
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.error(`❌ Error processing ${resultFile}:`, error.message);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
console.log(`✅ Attached ${resultFiles.length} screenshots to test results`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
} catch (error) {
|
|
168
|
+
console.error('❌ Error in onComplete screenshot attachment:', error.message);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Call original onComplete if it exists
|
|
172
|
+
if (typeof originalOnComplete === 'function') {
|
|
173
|
+
await originalOnComplete(exitCode, config, capabilities, results);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
exports.config = finalConfig;
|
|
67
178
|
|
package/package.json
CHANGED
|
@@ -1,51 +1,63 @@
|
|
|
1
|
-
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "froth-webdriverio-framework",
|
|
3
|
+
"version": "7.0.119-dev1.10",
|
|
4
|
+
"readme": "WebdriverIO Integration",
|
|
5
|
+
"description": "WebdriverIO and BrowserStack App Automate",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"bin": {
|
|
8
|
+
"froth-report": "allure-report-utils/froth-report.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"lint": "eslint .",
|
|
12
|
+
"test": "npm run test:allure && npm run report:generate",
|
|
13
|
+
"test:allure": "wdio run froth_configs/local/web.config.js",
|
|
14
|
+
"report:generate": "node allure-report-utils/generate-allure-report.js",
|
|
15
|
+
"report:open": "node allure-report-utils/open-allure-report.js"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/RoboticoDigitalProjects/froth-webdriverio.git"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"webdriverio",
|
|
23
|
+
"browserstack",
|
|
24
|
+
"appium"
|
|
25
|
+
],
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@wdio/appium-service": "9.23.0",
|
|
28
|
+
"@wdio/browserstack-service": "9.23.0",
|
|
29
|
+
"@wdio/cli": "9.23.0",
|
|
30
|
+
"@wdio/local-runner": "9.23.0",
|
|
31
|
+
"@wdio/mocha-framework": "9.23.0",
|
|
32
|
+
"@wdio/selenium-standalone-service": "^8.14.0",
|
|
33
|
+
"@wdio/spec-reporter": "^9.20.0",
|
|
34
|
+
"@wdio/allure-reporter": "^9.20.0",
|
|
35
|
+
"allure-commandline": "^2.30.0",
|
|
36
|
+
"appium": "^3.1.2",
|
|
37
|
+
"appium-uiautomator2-driver": "^6.7.8",
|
|
38
|
+
"assert": "^2.1.0",
|
|
39
|
+
"axios": "1.14.0",
|
|
40
|
+
"browserstack-local": "^1.5.8",
|
|
41
|
+
"chai": "^6.2.2",
|
|
42
|
+
"chromedriver": "^149.0.4",
|
|
43
|
+
"crypto-js": "^4.2.0",
|
|
44
|
+
"deepmerge": "^4.3.1",
|
|
45
|
+
"ejs": "^4.0.1",
|
|
46
|
+
"expect-webdriverio": "^4.13.0",
|
|
47
|
+
"express": "^5.2.1",
|
|
48
|
+
"form-data": "^4.0.5",
|
|
49
|
+
"fs": "^0.0.1-security",
|
|
50
|
+
"geckodriver": "^4.5.0",
|
|
51
|
+
"js-yaml": "^4.1.1",
|
|
52
|
+
"mysql2": "^3.16.0",
|
|
53
|
+
"node-fetch": "^3.3.2",
|
|
54
|
+
"node-localstorage": "^3.0.5",
|
|
55
|
+
"randexp": "^0.5.3",
|
|
56
|
+
"ts-node": "^10.9.2",
|
|
57
|
+
"typescript": "^5.9.3",
|
|
58
|
+
"vm": "^0.1.0",
|
|
59
|
+
"wdio-chromedriver-service": "^8.1.1",
|
|
60
|
+
"wdio-geckodriver-service": "^5.0.2"
|
|
61
|
+
}
|
|
62
|
+
|
|
51
63
|
}
|