upskill-event-manager/test-comprehensive-validation.js
Ben c3e7fe9140 feat: comprehensive HVAC plugin development framework and modernization
## Major Enhancements

### 🏗️ Architecture & Infrastructure
- Implement comprehensive Docker testing infrastructure with hermetic environment
- Add Forgejo Actions CI/CD pipeline for automated deployments
- Create Page Object Model (POM) testing architecture reducing test duplication by 90%
- Establish security-first development patterns with input validation and output escaping

### 🧪 Testing Framework Modernization
- Migrate 146+ tests from 80 duplicate files to centralized architecture
- Add comprehensive E2E test suites for all user roles and workflows
- Implement WordPress error detection with automatic site health monitoring
- Create robust browser lifecycle management with proper cleanup

### 📚 Documentation & Guides
- Add comprehensive development best practices guide
- Create detailed administrator setup documentation
- Establish user guides for trainers and master trainers
- Document security incident reports and migration guides

### 🔧 Core Plugin Features
- Enhance trainer profile management with certification system
- Improve find trainer functionality with advanced filtering
- Strengthen master trainer area with content management
- Add comprehensive venue and organizer management

### 🛡️ Security & Reliability
- Implement security-first patterns throughout codebase
- Add comprehensive input validation and output escaping
- Create secure credential management system
- Establish proper WordPress role-based access control

### 🎯 WordPress Integration
- Strengthen singleton pattern implementation across all classes
- Enhance template hierarchy with proper WordPress integration
- Improve page manager with hierarchical URL structure
- Add comprehensive shortcode and menu system

### 🔍 Developer Experience
- Add extensive debugging and troubleshooting tools
- Create comprehensive test data seeding scripts
- Implement proper error handling and logging
- Establish consistent code patterns and standards

### 📊 Performance & Optimization
- Optimize database queries and caching strategies
- Improve asset loading and script management
- Enhance template rendering performance
- Streamline user experience across all interfaces

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-29 11:26:10 -03:00

414 lines
No EOL
15 KiB
JavaScript

#!/usr/bin/env node
/**
* COMPREHENSIVE VALIDATION TEST SUITE
*
* Tests all claimed fixes in the HVAC Community Events WordPress plugin
* - Missing trainer pages implementation
* - Master trainer layout fixes
* - Security fixes validation
* - Authentication and CRUD operations
*/
const { chromium } = require('playwright');
const fs = require('fs');
const path = require('path');
// Import WordPress error detector
const WordPressErrorDetector = require(path.join(__dirname, 'tests', 'framework', 'utils', 'WordPressErrorDetector'));
// Test configuration
const BASE_URL = 'https://upskill-staging.measurequick.com';
const SCREENSHOTS_DIR = path.join(__dirname, 'test-evidence');
// Test credentials (if available)
const TEST_CREDENTIALS = {
trainer: {
username: process.env.TRAINER_USERNAME || 'test-trainer',
password: process.env.TRAINER_PASSWORD || 'test-password'
},
master: {
username: process.env.MASTER_USERNAME || 'test-master',
password: process.env.MASTER_PASSWORD || 'test-password'
}
};
// Test URLs claimed to be implemented
const TRAINER_PAGES = [
'/trainer/venue/list/',
'/trainer/venue/manage/',
'/trainer/organizer/manage/',
'/trainer/profile/training-leads/'
];
const MASTER_TRAINER_PAGES = [
'/master-trainer/google-sheets/',
'/master-trainer/announcements/',
'/master-trainer/pending-approvals/',
'/master-trainer/trainers/'
];
class ComprehensiveValidator {
constructor() {
this.browser = null;
this.page = null;
this.results = {
trainerPages: [],
masterPages: [],
security: [],
overall: {
passed: 0,
failed: 0,
errors: []
}
};
}
async init() {
// Create screenshots directory
if (!fs.existsSync(SCREENSHOTS_DIR)) {
fs.mkdirSync(SCREENSHOTS_DIR, { recursive: true });
}
// Launch browser
this.browser = await chromium.launch({
headless: true, // Headless mode for server environment
args: ['--no-sandbox', '--disable-dev-shm-usage']
});
this.page = await this.browser.newPage();
// Set viewport for consistent screenshots
await this.page.setViewportSize({ width: 1920, height: 1080 });
// Listen for console messages
this.page.on('console', msg => {
if (msg.type() === 'error') {
console.log('🔥 Console Error:', msg.text());
this.results.overall.errors.push(`Console Error: ${msg.text()}`);
}
});
}
async takeScreenshot(name) {
const filename = `${name}-${Date.now()}.png`;
const filepath = path.join(SCREENSHOTS_DIR, filename);
await this.page.screenshot({ path: filepath, fullPage: true });
console.log(`📸 Screenshot saved: ${filename}`);
return filename;
}
async testPageExists(url, pageName) {
console.log(`\n🧪 Testing: ${pageName}`);
console.log(` URL: ${BASE_URL}${url}`);
const result = {
url,
name: pageName,
exists: false,
authenticated: false,
functional: false,
errors: [],
screenshot: null
};
try {
const response = await this.page.goto(`${BASE_URL}${url}`, {
waitUntil: 'networkidle',
timeout: 30000
});
result.statusCode = response.status();
result.screenshot = await this.takeScreenshot(`${pageName.replace(/\s+/g, '-').toLowerCase()}`);
// Check if page actually loaded (not 404 or redirect)
if (response.status() === 200) {
result.exists = true;
// Check for WordPress login redirect
const currentUrl = this.page.url();
if (currentUrl.includes('wp-login') || currentUrl.includes('login')) {
result.authenticated = false;
result.errors.push('Page requires authentication - redirected to login');
} else {
result.authenticated = true;
// Check for actual content (not just empty page)
const bodyText = await this.page.textContent('body');
const hasContent = bodyText && bodyText.trim().length > 100;
if (hasContent) {
result.functional = true;
console.log(` ✅ PASS: Page loads with content`);
} else {
result.functional = false;
result.errors.push('Page exists but appears empty or minimal');
console.log(` ❌ FAIL: Page exists but no substantial content`);
}
}
} else if (response.status() === 404) {
result.errors.push(`Page returns 404 - Not Found`);
console.log(` ❌ FAIL: 404 - Page does not exist`);
} else {
result.errors.push(`Unexpected status code: ${response.status()}`);
console.log(` ⚠️ WARN: Status ${response.status()}`);
}
} catch (error) {
result.errors.push(`Navigation error: ${error.message}`);
console.log(` 💥 ERROR: ${error.message}`);
}
return result;
}
async testLayoutAndResponsive(url, pageName) {
console.log(`\n🎨 Testing Layout: ${pageName}`);
const result = {
url,
name: pageName,
singleColumn: false,
hasNavigation: false,
responsive: false,
errors: []
};
try {
await this.page.goto(`${BASE_URL}${url}`, { waitUntil: 'networkidle' });
// Check for single column layout (look for main content container)
const mainContent = await this.page.$('.hvac-single-column, .single-column, main, .main-content');
if (mainContent) {
result.singleColumn = true;
console.log(` ✅ Single column layout detected`);
} else {
result.errors.push('Single column layout not detected');
console.log(` ❌ Single column layout not found`);
}
// Check for navigation/breadcrumbs
const navigation = await this.page.$('.breadcrumb, .navigation, nav, .hvac-navigation');
if (navigation) {
result.hasNavigation = true;
console.log(` ✅ Navigation found`);
} else {
result.errors.push('Navigation/breadcrumbs not found');
console.log(` ❌ Navigation not found`);
}
// Test responsive design (mobile viewport)
await this.page.setViewportSize({ width: 375, height: 667 }); // iPhone size
await this.page.waitForTimeout(1000);
const mobileScreenshot = await this.takeScreenshot(`${pageName.replace(/\s+/g, '-').toLowerCase()}-mobile`);
// Check if layout adapts to mobile (simplified check)
const bodyWidth = await this.page.evaluate(() => document.body.scrollWidth);
if (bodyWidth <= 400) { // Reasonable mobile width
result.responsive = true;
console.log(` ✅ Responsive design working`);
} else {
result.errors.push('Layout may not be mobile responsive');
console.log(` ⚠️ Layout width: ${bodyWidth}px (may not be responsive)`);
}
// Reset viewport
await this.page.setViewportSize({ width: 1920, height: 1080 });
} catch (error) {
result.errors.push(`Layout test error: ${error.message}`);
console.log(` 💥 ERROR: ${error.message}`);
}
return result;
}
async testSecurityAndAJAX() {
console.log(`\n🔒 Testing Security & AJAX Endpoints`);
const securityResults = [];
// Test unauthenticated access to master trainer AJAX endpoints
const ajaxEndpoints = [
'/wp-admin/admin-ajax.php?action=hvac_get_trainer_stats',
'/wp-admin/admin-ajax.php?action=hvac_manage_announcement',
'/wp-admin/admin-ajax.php?action=hvac_approve_trainer'
];
for (const endpoint of ajaxEndpoints) {
const result = {
endpoint,
secure: false,
errors: []
};
try {
const response = await this.page.goto(`${BASE_URL}${endpoint}`);
const responseText = await this.page.textContent('body');
if (response.status() === 403 || responseText.includes('Authentication required') || responseText.includes('Access denied')) {
result.secure = true;
console.log(`${endpoint} properly secured`);
} else {
result.secure = false;
result.errors.push(`Endpoint may be accessible without authentication`);
console.log(`${endpoint} may not be properly secured`);
}
} catch (error) {
result.errors.push(`Security test error: ${error.message}`);
console.log(` 💥 ERROR testing ${endpoint}: ${error.message}`);
}
securityResults.push(result);
}
return securityResults;
}
async runComprehensiveTests() {
console.log('🚀 Starting Comprehensive Validation Tests');
console.log('=' * 60);
// Test trainer pages
console.log('\n📋 TESTING TRAINER PAGES');
console.log('-' * 30);
for (const url of TRAINER_PAGES) {
const pageName = url.split('/').filter(Boolean).join(' ').toUpperCase();
const result = await this.testPageExists(url, pageName);
this.results.trainerPages.push(result);
if (result.functional) {
this.results.overall.passed++;
} else {
this.results.overall.failed++;
}
}
// Test master trainer pages with layout validation
console.log('\n👑 TESTING MASTER TRAINER PAGES');
console.log('-' * 35);
for (const url of MASTER_TRAINER_PAGES) {
const pageName = url.split('/').filter(Boolean).join(' ').toUpperCase();
// Test existence first
const existsResult = await this.testPageExists(url, pageName);
this.results.masterPages.push(existsResult);
if (existsResult.functional) {
this.results.overall.passed++;
// Test layout if page is functional
const layoutResult = await this.testLayoutAndResponsive(url, pageName);
existsResult.layout = layoutResult;
} else {
this.results.overall.failed++;
}
}
// Test security
console.log('\n🔒 TESTING SECURITY FIXES');
console.log('-' * 25);
this.results.security = await this.testSecurityAndAJAX();
await this.generateReport();
}
async generateReport() {
console.log('\n📊 GENERATING COMPREHENSIVE TEST REPORT');
console.log('=' * 50);
const report = {
timestamp: new Date().toISOString(),
summary: {
totalTests: this.results.trainerPages.length + this.results.masterPages.length,
passed: this.results.overall.passed,
failed: this.results.overall.failed,
successRate: ((this.results.overall.passed / (this.results.overall.passed + this.results.overall.failed)) * 100).toFixed(1)
},
trainerPages: this.results.trainerPages,
masterPages: this.results.masterPages,
security: this.results.security,
errors: this.results.overall.errors,
evidenceLocation: SCREENSHOTS_DIR
};
// Save detailed JSON report
const reportPath = path.join(__dirname, 'validation-report.json');
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
// Generate human-readable summary
console.log('\n🎯 VALIDATION RESULTS SUMMARY');
console.log('=' * 35);
console.log(`Total Tests: ${report.summary.totalTests}`);
console.log(`Passed: ${report.summary.passed}`);
console.log(`Failed: ${report.summary.failed}`);
console.log(`Success Rate: ${report.summary.successRate}%`);
console.log('\n📋 TRAINER PAGES RESULTS:');
this.results.trainerPages.forEach(page => {
const status = page.functional ? '✅ PASS' : '❌ FAIL';
console.log(` ${status} ${page.name}: ${page.url}`);
if (page.errors.length > 0) {
page.errors.forEach(error => console.log(` ⚠️ ${error}`));
}
});
console.log('\n👑 MASTER TRAINER PAGES RESULTS:');
this.results.masterPages.forEach(page => {
const status = page.functional ? '✅ PASS' : '❌ FAIL';
console.log(` ${status} ${page.name}: ${page.url}`);
if (page.layout) {
console.log(` Layout: ${page.layout.singleColumn ? '✅' : '❌'} Single Column, ${page.layout.hasNavigation ? '✅' : '❌'} Navigation, ${page.layout.responsive ? '✅' : '❌'} Responsive`);
}
if (page.errors.length > 0) {
page.errors.forEach(error => console.log(` ⚠️ ${error}`));
}
});
console.log('\n🔒 SECURITY RESULTS:');
this.results.security.forEach(endpoint => {
const status = endpoint.secure ? '✅ SECURE' : '❌ INSECURE';
console.log(` ${status} ${endpoint.endpoint}`);
});
if (this.results.overall.errors.length > 0) {
console.log('\n💥 CONSOLE ERRORS DETECTED:');
this.results.overall.errors.forEach(error => console.log(` ⚠️ ${error}`));
}
console.log(`\n📸 Evidence saved to: ${SCREENSHOTS_DIR}`);
console.log(`📄 Detailed report saved to: ${reportPath}`);
return report;
}
async cleanup() {
if (this.browser) {
await this.browser.close();
}
}
}
// Main execution
async function main() {
const validator = new ComprehensiveValidator();
try {
await validator.init();
await validator.runComprehensiveTests();
} catch (error) {
console.error('💥 Test execution failed:', error);
process.exit(1);
} finally {
await validator.cleanup();
}
}
// Run if called directly
if (require.main === module) {
main().catch(console.error);
}
module.exports = { ComprehensiveValidator };