Some checks are pending
HVAC Plugin CI/CD Pipeline / Security Analysis (push) Waiting to run
HVAC Plugin CI/CD Pipeline / Code Quality & Standards (push) Waiting to run
HVAC Plugin CI/CD Pipeline / Unit Tests (push) Waiting to run
HVAC Plugin CI/CD Pipeline / Integration Tests (push) Waiting to run
HVAC Plugin CI/CD Pipeline / Deploy to Staging (push) Blocked by required conditions
HVAC Plugin CI/CD Pipeline / Deploy to Production (push) Blocked by required conditions
HVAC Plugin CI/CD Pipeline / Notification (push) Blocked by required conditions
Security Monitoring & Compliance / Dependency Vulnerability Scan (push) Waiting to run
Security Monitoring & Compliance / Secrets & Credential Scan (push) Waiting to run
Security Monitoring & Compliance / WordPress Security Analysis (push) Waiting to run
Security Monitoring & Compliance / Static Code Security Analysis (push) Waiting to run
Security Monitoring & Compliance / Security Compliance Validation (push) Waiting to run
Security Monitoring & Compliance / Security Summary Report (push) Blocked by required conditions
Security Monitoring & Compliance / Security Team Notification (push) Blocked by required conditions
- Add 90+ test files including E2E, unit, and integration tests - Implement Page Object Model (POM) architecture - Add Docker testing environment with comprehensive services - Include modernized test framework with error recovery - Add specialized test suites for master trainer and trainer workflows - Update .gitignore to properly track test infrastructure 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
388 lines
No EOL
14 KiB
JavaScript
388 lines
No EOL
14 KiB
JavaScript
/**
|
||
* Global Test Teardown for HVAC Testing Framework
|
||
*
|
||
* Runs once after all tests to:
|
||
* - Clean up test data and artifacts
|
||
* - Generate final reports
|
||
* - Archive screenshots and videos
|
||
* - Display test summary
|
||
*
|
||
* @package HVAC_Community_Events
|
||
* @version 2.0.0
|
||
* @created 2025-08-27
|
||
*/
|
||
|
||
const ConfigManager = require('../core/ConfigManager');
|
||
const AuthManager = require('../core/AuthManager');
|
||
const WordPressUtils = require('../utils/WordPressUtils');
|
||
const ScreenshotManager = require('../utils/ScreenshotManager');
|
||
const fs = require('fs').promises;
|
||
const path = require('path');
|
||
|
||
async function globalTeardown(config) {
|
||
console.log('\n🏁 HVAC Testing Framework - Global Teardown');
|
||
console.log('='.repeat(50));
|
||
|
||
const startTime = Date.now();
|
||
const configManager = ConfigManager;
|
||
const authManager = AuthManager;
|
||
const wpUtils = new WordPressUtils();
|
||
const screenshotManager = new ScreenshotManager();
|
||
|
||
try {
|
||
// 1. Clean Up Test Data
|
||
console.log('\n🧹 1. Cleaning Up Test Data');
|
||
|
||
const environment = configManager.getEnvironment();
|
||
|
||
if (configManager.get('testData.cleanupAfterTests') && environment !== 'staging') {
|
||
try {
|
||
// Clean up WordPress test data
|
||
const testIds = await getTestIds();
|
||
for (const testId of testIds) {
|
||
await wpUtils.cleanupTestData(testId);
|
||
}
|
||
|
||
console.log(` ✅ Cleaned up ${testIds.length} test datasets`);
|
||
} catch (error) {
|
||
console.warn(' ⚠️ Test data cleanup failed:', error.message);
|
||
}
|
||
} else {
|
||
console.log(' ℹ️ Skipping test data cleanup (staging environment)');
|
||
}
|
||
|
||
// 2. Authentication State Management
|
||
console.log('\n🔐 2. Authentication State Management');
|
||
|
||
// Get auth status before cleanup
|
||
const authStatus = await authManager.getAuthStatus();
|
||
console.log(` 📊 Storage states: ${Object.keys(authStatus).length} roles`);
|
||
|
||
// Clear validation cache
|
||
authManager.clearValidationCache();
|
||
console.log(' ✅ Validation cache cleared');
|
||
|
||
// Clean up old storage states if configured
|
||
if (environment === 'local') {
|
||
// Only clean on local environment
|
||
const oldStatesCount = await cleanOldStorageStates();
|
||
if (oldStatesCount > 0) {
|
||
console.log(` 🗑️ Removed ${oldStatesCount} old storage states`);
|
||
}
|
||
}
|
||
|
||
// 3. Generate Screenshot Reports
|
||
console.log('\n📸 3. Processing Screenshots and Media');
|
||
|
||
try {
|
||
// Generate screenshot report
|
||
const screenshotReport = await screenshotManager.generateReport();
|
||
if (screenshotReport) {
|
||
console.log(` 📊 Screenshot report: ${screenshotReport.totalScreenshots} files`);
|
||
}
|
||
|
||
// Create screenshot gallery
|
||
const galleryPath = await screenshotManager.createGallery('HVAC Test Results');
|
||
if (galleryPath) {
|
||
console.log(` 🖼️ Screenshot gallery: ${galleryPath}`);
|
||
}
|
||
|
||
// Clean up old screenshots if configured
|
||
const daysToKeep = 7;
|
||
await screenshotManager.cleanup(daysToKeep);
|
||
|
||
} catch (error) {
|
||
console.warn(' ⚠️ Screenshot processing failed:', error.message);
|
||
}
|
||
|
||
// 4. Generate Final Test Report
|
||
console.log('\n📊 4. Generating Final Reports');
|
||
|
||
try {
|
||
const finalReport = await generateFinalReport(startTime);
|
||
const reportPath = path.resolve('./test-results/final-report.json');
|
||
await fs.writeFile(reportPath, JSON.stringify(finalReport, null, 2));
|
||
|
||
console.log(` ✅ Final report: ${reportPath}`);
|
||
|
||
// Generate HTML summary
|
||
const htmlReport = await generateHTMLReport(finalReport);
|
||
const htmlPath = path.resolve('./test-results/summary.html');
|
||
await fs.writeFile(htmlPath, htmlReport);
|
||
|
||
console.log(` 📄 HTML report: ${htmlPath}`);
|
||
|
||
} catch (error) {
|
||
console.warn(' ⚠️ Report generation failed:', error.message);
|
||
}
|
||
|
||
// 5. Archive Test Artifacts
|
||
console.log('\n📦 5. Archiving Test Artifacts');
|
||
|
||
try {
|
||
await archiveTestArtifacts();
|
||
console.log(' ✅ Test artifacts archived');
|
||
} catch (error) {
|
||
console.warn(' ⚠️ Archiving failed:', error.message);
|
||
}
|
||
|
||
// 6. WordPress Cleanup (if safe)
|
||
console.log('\n⚙️ 6. WordPress Environment Cleanup');
|
||
|
||
if (environment === 'local' || environment === 'docker') {
|
||
try {
|
||
// Clear cache one final time
|
||
await wpUtils.clearCache();
|
||
console.log(' ✅ Final cache clear');
|
||
|
||
} catch (error) {
|
||
console.warn(' ⚠️ WordPress cleanup failed:', error.message);
|
||
}
|
||
} else {
|
||
console.log(' ℹ️ Skipping WordPress cleanup (staging environment)');
|
||
}
|
||
|
||
// 7. Display Final Summary
|
||
console.log('\n📋 7. Test Session Summary');
|
||
|
||
await displayTestSummary();
|
||
|
||
// 8. Final Teardown Report
|
||
const duration = ((Date.now() - startTime) / 1000).toFixed(2);
|
||
console.log('\n' + '='.repeat(50));
|
||
console.log('✅ Global Teardown Complete');
|
||
console.log(`⏱️ Teardown Duration: ${duration}s`);
|
||
console.log(`🌍 Environment: ${environment}`);
|
||
console.log('='.repeat(50) + '\n');
|
||
|
||
} catch (error) {
|
||
console.error('\n❌ Global Teardown Failed');
|
||
console.error('Error:', error.message);
|
||
console.error('='.repeat(50) + '\n');
|
||
// Don't throw - teardown failures shouldn't fail the test suite
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Get list of test IDs for cleanup
|
||
*/
|
||
async function getTestIds() {
|
||
try {
|
||
const testResults = await fs.readFile('./test-results/test-metrics.jsonl', 'utf8');
|
||
const lines = testResults.trim().split('\n');
|
||
const testIds = lines
|
||
.map(line => JSON.parse(line))
|
||
.map(test => test.testId)
|
||
.filter(id => id.startsWith('hvac-test-'));
|
||
|
||
return [...new Set(testIds)]; // Remove duplicates
|
||
} catch (error) {
|
||
return [];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Clean old storage states
|
||
*/
|
||
async function cleanOldStorageStates() {
|
||
try {
|
||
const storageStatesDir = path.resolve(__dirname, '../fixtures/storage-states');
|
||
const files = await fs.readdir(storageStatesDir);
|
||
const cutoffDate = new Date();
|
||
cutoffDate.setDate(cutoffDate.getDate() - 7);
|
||
|
||
let deletedCount = 0;
|
||
for (const file of files) {
|
||
if (file.endsWith('.json')) {
|
||
const filePath = path.join(storageStatesDir, file);
|
||
const stats = await fs.stat(filePath);
|
||
|
||
if (stats.mtime < cutoffDate) {
|
||
await fs.unlink(filePath);
|
||
deletedCount++;
|
||
}
|
||
}
|
||
}
|
||
|
||
return deletedCount;
|
||
} catch (error) {
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Generate comprehensive final report
|
||
*/
|
||
async function generateFinalReport(startTime) {
|
||
const report = {
|
||
timestamp: new Date().toISOString(),
|
||
environment: ConfigManager.getEnvironment(),
|
||
teardownDuration: Date.now() - startTime,
|
||
framework: {
|
||
version: '2.0.0',
|
||
features: ConfigManager.get('features')
|
||
}
|
||
};
|
||
|
||
// Add test metrics if available
|
||
try {
|
||
const metricsFile = './test-results/test-metrics.jsonl';
|
||
const metricsData = await fs.readFile(metricsFile, 'utf8');
|
||
const metrics = metricsData.trim().split('\n').map(line => JSON.parse(line));
|
||
|
||
report.testMetrics = {
|
||
totalTests: metrics.length,
|
||
passed: metrics.filter(t => t.passed).length,
|
||
failed: metrics.filter(t => !t.passed).length,
|
||
categories: groupBy(metrics, 'category'),
|
||
avgDuration: metrics.reduce((sum, t) => sum + t.duration, 0) / metrics.length
|
||
};
|
||
} catch (error) {
|
||
report.testMetrics = { error: 'Metrics not available' };
|
||
}
|
||
|
||
return report;
|
||
}
|
||
|
||
/**
|
||
* Generate HTML report
|
||
*/
|
||
async function generateHTMLReport(report) {
|
||
const testMetrics = report.testMetrics || {};
|
||
const passRate = testMetrics.totalTests ?
|
||
((testMetrics.passed / testMetrics.totalTests) * 100).toFixed(1) : 'N/A';
|
||
|
||
return `
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>HVAC Test Results Summary</title>
|
||
<style>
|
||
body { font-family: system-ui, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
|
||
.header { text-align: center; background: white; padding: 30px; border-radius: 8px; margin-bottom: 20px; }
|
||
.metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 20px 0; }
|
||
.metric { background: white; padding: 20px; border-radius: 8px; text-align: center; }
|
||
.metric h3 { margin: 0; color: #333; }
|
||
.metric .value { font-size: 2em; font-weight: bold; margin: 10px 0; }
|
||
.passed { color: #28a745; }
|
||
.failed { color: #dc3545; }
|
||
.info { background: white; padding: 20px; border-radius: 8px; margin-top: 20px; }
|
||
pre { background: #f8f9fa; padding: 15px; border-radius: 4px; overflow-x: auto; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="header">
|
||
<h1>🧪 HVAC Testing Framework Results</h1>
|
||
<p><strong>Environment:</strong> ${report.environment}</p>
|
||
<p><strong>Generated:</strong> ${new Date(report.timestamp).toLocaleString()}</p>
|
||
</div>
|
||
|
||
<div class="metrics">
|
||
<div class="metric">
|
||
<h3>Total Tests</h3>
|
||
<div class="value">${testMetrics.totalTests || 0}</div>
|
||
</div>
|
||
<div class="metric">
|
||
<h3>Passed</h3>
|
||
<div class="value passed">${testMetrics.passed || 0}</div>
|
||
</div>
|
||
<div class="metric">
|
||
<h3>Failed</h3>
|
||
<div class="value failed">${testMetrics.failed || 0}</div>
|
||
</div>
|
||
<div class="metric">
|
||
<h3>Pass Rate</h3>
|
||
<div class="value">${passRate}%</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="info">
|
||
<h2>📊 Detailed Report</h2>
|
||
<pre>${JSON.stringify(report, null, 2)}</pre>
|
||
</div>
|
||
</body>
|
||
</html>
|
||
`;
|
||
}
|
||
|
||
/**
|
||
* Archive test artifacts
|
||
*/
|
||
async function archiveTestArtifacts() {
|
||
const archiveDir = path.resolve('./test-results/archives');
|
||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||
const archiveName = `test-session-${timestamp}`;
|
||
const sessionArchive = path.join(archiveDir, archiveName);
|
||
|
||
await fs.mkdir(sessionArchive, { recursive: true });
|
||
|
||
// Copy important files to archive
|
||
const filesToArchive = [
|
||
'./test-results/final-report.json',
|
||
'./test-results/summary.html',
|
||
'./test-results/setup-report.json'
|
||
];
|
||
|
||
for (const file of filesToArchive) {
|
||
try {
|
||
const filename = path.basename(file);
|
||
const sourcePath = path.resolve(file);
|
||
const destPath = path.join(sessionArchive, filename);
|
||
|
||
await fs.copyFile(sourcePath, destPath);
|
||
} catch (error) {
|
||
// File might not exist, continue
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Display test session summary
|
||
*/
|
||
async function displayTestSummary() {
|
||
try {
|
||
const metricsFile = './test-results/test-metrics.jsonl';
|
||
const metricsData = await fs.readFile(metricsFile, 'utf8');
|
||
const metrics = metricsData.trim().split('\n').map(line => JSON.parse(line));
|
||
|
||
const totalTests = metrics.length;
|
||
const passed = metrics.filter(t => t.passed).length;
|
||
const failed = metrics.filter(t => !t.passed).length;
|
||
const passRate = ((passed / totalTests) * 100).toFixed(1);
|
||
|
||
console.log(` 📊 Total Tests: ${totalTests}`);
|
||
console.log(` ✅ Passed: ${passed}`);
|
||
console.log(` ❌ Failed: ${failed}`);
|
||
console.log(` 📈 Pass Rate: ${passRate}%`);
|
||
|
||
if (failed > 0) {
|
||
console.log('\n ❌ Failed Tests:');
|
||
metrics
|
||
.filter(t => !t.passed)
|
||
.slice(0, 5) // Show first 5 failures
|
||
.forEach(t => console.log(` - ${t.title}`));
|
||
|
||
if (failed > 5) {
|
||
console.log(` ... and ${failed - 5} more`);
|
||
}
|
||
}
|
||
|
||
} catch (error) {
|
||
console.log(' ℹ️ No test metrics available');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Helper function to group by property
|
||
*/
|
||
function groupBy(array, property) {
|
||
return array.reduce((groups, item) => {
|
||
const key = item[property] || 'unknown';
|
||
groups[key] = (groups[key] || 0) + 1;
|
||
return groups;
|
||
}, {});
|
||
}
|
||
|
||
module.exports = globalTeardown; |