#!/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 };