upskill-event-manager/test-master-trainer-e2e.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

464 lines
No EOL
18 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const { chromium } = require('playwright');
const path = require('path');
// Import WordPress error detector
const WordPressErrorDetector = require(path.join(__dirname, 'tests', 'framework', 'utils', 'WordPressErrorDetector'));
/**
* HVAC Master Trainer - Comprehensive E2E Test Suite
* Tests all Master Trainer functionality on staging environment
*
* Test Coverage:
* - Master Dashboard
* - Events Overview
* - Import/Export Data
* - Announcements
* - Pending Approvals
* - Communication Templates
* - Trainer Management
* - Profile Editing
*
* Features WordPress error detection to prevent testing against broken sites
*/
// Configuration
const CONFIG = {
baseUrl: 'https://upskill-staging.measurequick.com',
headless: false, // Set to false to see browser
slowMo: 500, // Slow down for visibility
timeout: 30000,
viewport: { width: 1280, height: 720 }
};
// Test Accounts
const ACCOUNTS = {
master: {
username: 'test_master',
password: 'TestMaster123!',
email: 'test_master@example.com'
},
masterAlt: {
username: 'JoeMedosch@gmail.com',
password: 'JoeTrainer2025@',
email: 'JoeMedosch@gmail.com'
},
regularTrainer: {
username: 'test_trainer',
password: 'TestTrainer123!',
email: 'test_trainer@example.com'
}
};
// Test Results Tracking
class TestResults {
constructor() {
this.results = [];
this.startTime = Date.now();
}
addResult(category, test, status, details = '') {
this.results.push({
category,
test,
status,
details,
timestamp: new Date().toISOString()
});
const icon = status === 'PASSED' ? '✅' : '❌';
console.log(`${icon} ${category} - ${test}`);
if (details) console.log(` ${details}`);
}
printSummary() {
const duration = ((Date.now() - this.startTime) / 1000).toFixed(2);
const passed = this.results.filter(r => r.status === 'PASSED').length;
const failed = this.results.filter(r => r.status === 'FAILED').length;
const total = this.results.length;
console.log('\n' + '='.repeat(60));
console.log('📊 TEST SUMMARY');
console.log('='.repeat(60));
console.log(`Total Tests: ${total}`);
console.log(`✅ Passed: ${passed}`);
console.log(`❌ Failed: ${failed}`);
console.log(`⏱️ Duration: ${duration}s`);
console.log(`📈 Success Rate: ${((passed/total)*100).toFixed(1)}%`);
if (failed > 0) {
console.log('\n❌ FAILED TESTS:');
this.results
.filter(r => r.status === 'FAILED')
.forEach(r => console.log(` - ${r.category}: ${r.test}`));
}
}
exportResults() {
const filename = `master-trainer-test-results-${Date.now()}.json`;
require('fs').writeFileSync(filename, JSON.stringify(this.results, null, 2));
console.log(`\n📁 Results exported to ${filename}`);
}
}
// Main Test Suite
async function runMasterTrainerTests() {
console.log('🏁 HVAC Master Trainer - Comprehensive E2E Test Suite\n');
console.log('🚀 Initializing Master Trainer Test Suite');
console.log(`📍 Target: ${CONFIG.baseUrl}`);
console.log(`🖥️ Mode: ${CONFIG.headless ? 'Headless' : 'Headed'}`);
console.log('='.repeat(60) + '\n');
const results = new TestResults();
const browser = await chromium.launch({
headless: CONFIG.headless,
slowMo: CONFIG.slowMo
});
const context = await browser.newContext({
viewport: CONFIG.viewport
});
const page = await context.newPage();
page.setDefaultTimeout(CONFIG.timeout);
try {
// CRITICAL: Check for WordPress errors before testing
console.log('\n🔍 Checking WordPress Site Health');
console.log('-'.repeat(40));
await page.goto(CONFIG.baseUrl);
await page.waitForLoadState('networkidle');
const errorDetector = new WordPressErrorDetector(page);
const errorReport = await errorDetector.getErrorReport();
if (errorReport.hasErrors && errorReport.blockingErrors.length > 0) {
console.log('❌ CRITICAL WordPress Errors Detected:');
errorReport.blockingErrors.forEach(error => {
console.log(` - ${error.type}: ${error.message}`);
});
throw new Error(`WordPress site has critical errors that block testing. Restore from production and re-seed test data.`);
}
console.log('✅ WordPress site health check passed');
// 1. LOGIN AS MASTER TRAINER
console.log('\n🔐 Testing Master Trainer Login');
console.log('-'.repeat(40));
await page.goto(`${CONFIG.baseUrl}/training-login/`);
await page.waitForLoadState('networkidle');
try {
await page.fill('#username', ACCOUNTS.master.username);
await page.fill('#password', ACCOUNTS.master.password);
await page.click('button[type="submit"]');
await page.waitForURL('**/master-trainer/master-dashboard/**', { timeout: 10000 });
results.addResult('Authentication', 'Master Trainer Login', 'PASSED',
`Redirected to: ${page.url()}`);
} catch (error) {
// Try alternative master account
await page.goto(`${CONFIG.baseUrl}/training-login/`);
await page.fill('#username', ACCOUNTS.masterAlt.username);
await page.fill('#password', ACCOUNTS.masterAlt.password);
await page.click('button[type="submit"]');
await page.waitForURL('**/master-trainer/**', { timeout: 10000 });
results.addResult('Authentication', 'Master Trainer Login', 'PASSED',
`Used alternative account: ${ACCOUNTS.masterAlt.username}`);
}
// 2. MASTER DASHBOARD
console.log('\n📊 Testing Master Dashboard');
console.log('-'.repeat(40));
await page.goto(`${CONFIG.baseUrl}/master-trainer/master-dashboard/`);
await page.waitForLoadState('networkidle');
// Check dashboard elements
const dashboardTests = [
{ selector: '.hvac-master-dashboard', name: 'Dashboard Container' },
{ selector: '.dashboard-stats', name: 'Statistics Section' },
{ selector: '.trainer-count', name: 'Trainer Count' },
{ selector: '.event-count', name: 'Event Count' },
{ selector: '.recent-activity', name: 'Recent Activity' }
];
for (const test of dashboardTests) {
try {
await page.waitForSelector(test.selector, { timeout: 5000 });
results.addResult('Master Dashboard', test.name, 'PASSED');
} catch {
results.addResult('Master Dashboard', test.name, 'FAILED');
}
}
// Get statistics
try {
const stats = await page.evaluate(() => {
const trainerCount = document.querySelector('.trainer-count')?.textContent;
const eventCount = document.querySelector('.event-count')?.textContent;
return { trainers: trainerCount, events: eventCount };
});
results.addResult('Master Dashboard', 'Statistics Display', 'PASSED',
`Trainers: ${stats.trainers}, Events: ${stats.events}`);
} catch {
results.addResult('Master Dashboard', 'Statistics Display', 'FAILED');
}
// 3. EVENTS OVERVIEW
console.log('\n📅 Testing Events Overview');
console.log('-'.repeat(40));
await page.goto(`${CONFIG.baseUrl}/master-trainer/events/`);
await page.waitForLoadState('networkidle');
try {
await page.waitForSelector('.master-events-overview', { timeout: 5000 });
results.addResult('Events Overview', 'Page Load', 'PASSED');
// Check for KPI dashboard
const hasKPI = await page.isVisible('.kpi-dashboard');
results.addResult('Events Overview', 'KPI Dashboard',
hasKPI ? 'PASSED' : 'FAILED');
// Check for filtering options
const hasFilters = await page.isVisible('.event-filters');
results.addResult('Events Overview', 'Event Filters',
hasFilters ? 'PASSED' : 'FAILED');
} catch (error) {
results.addResult('Events Overview', 'Page Load', 'FAILED', error.message);
}
// 4. IMPORT/EXPORT DATA
console.log('\n📤 Testing Import/Export Data');
console.log('-'.repeat(40));
await page.goto(`${CONFIG.baseUrl}/master-trainer/import-export/`);
await page.waitForLoadState('networkidle');
try {
await page.waitForSelector('.import-export-management', { timeout: 5000 });
results.addResult('Import/Export', 'Page Load', 'PASSED');
// Check for CSV operations
const hasCSVImport = await page.isVisible('.csv-import-section');
results.addResult('Import/Export', 'CSV Import Section',
hasCSVImport ? 'PASSED' : 'FAILED');
const hasCSVExport = await page.isVisible('.csv-export-section');
results.addResult('Import/Export', 'CSV Export Section',
hasCSVExport ? 'PASSED' : 'FAILED');
} catch (error) {
results.addResult('Import/Export', 'Page Load', 'FAILED', error.message);
}
// 5. ANNOUNCEMENTS
console.log('\n📢 Testing Announcements');
console.log('-'.repeat(40));
await page.goto(`${CONFIG.baseUrl}/master-trainer/announcements/`);
await page.waitForLoadState('networkidle');
try {
await page.waitForSelector('.announcements-management', { timeout: 5000 });
results.addResult('Announcements', 'Page Load', 'PASSED');
// Check for announcement creation
const hasCreateButton = await page.isVisible('.create-announcement');
results.addResult('Announcements', 'Create Button',
hasCreateButton ? 'PASSED' : 'FAILED');
// Check for existing announcements
const announcementsList = await page.isVisible('.announcements-list');
results.addResult('Announcements', 'Announcements List',
announcementsList ? 'PASSED' : 'FAILED');
} catch (error) {
results.addResult('Announcements', 'Page Load', 'FAILED', error.message);
}
// 6. PENDING APPROVALS
console.log('\n⏳ Testing Pending Approvals');
console.log('-'.repeat(40));
await page.goto(`${CONFIG.baseUrl}/master-trainer/pending-approvals/`);
await page.waitForLoadState('networkidle');
try {
await page.waitForSelector('.pending-approvals', { timeout: 5000 });
results.addResult('Pending Approvals', 'Page Load', 'PASSED');
// Check for trainer approvals section
const hasTrainerApprovals = await page.isVisible('.trainer-approvals');
results.addResult('Pending Approvals', 'Trainer Approvals',
hasTrainerApprovals ? 'PASSED' : 'FAILED');
// Check for event approvals section
const hasEventApprovals = await page.isVisible('.event-approvals');
results.addResult('Pending Approvals', 'Event Approvals',
hasEventApprovals ? 'PASSED' : 'FAILED');
} catch (error) {
results.addResult('Pending Approvals', 'Page Load', 'FAILED', error.message);
}
// 7. COMMUNICATION TEMPLATES
console.log('\n📝 Testing Communication Templates');
console.log('-'.repeat(40));
await page.goto(`${CONFIG.baseUrl}/master-trainer/communication-templates/`);
await page.waitForLoadState('networkidle');
try {
await page.waitForSelector('.communication-templates', { timeout: 5000 });
results.addResult('Communication Templates', 'Page Load', 'PASSED');
// Check for template accordion
const hasAccordion = await page.isVisible('.template-accordion');
results.addResult('Communication Templates', 'Template Accordion',
hasAccordion ? 'PASSED' : 'FAILED');
// Check for copy functionality
const hasCopyButtons = await page.isVisible('.copy-template');
results.addResult('Communication Templates', 'Copy Buttons',
hasCopyButtons ? 'PASSED' : 'FAILED');
} catch (error) {
results.addResult('Communication Templates', 'Page Load', 'FAILED', error.message);
}
// 8. TRAINER MANAGEMENT
console.log('\n👥 Testing Trainer Management');
console.log('-'.repeat(40));
await page.goto(`${CONFIG.baseUrl}/master-trainer/trainers/`);
await page.waitForLoadState('networkidle');
try {
await page.waitForSelector('.trainer-management', { timeout: 5000 });
results.addResult('Trainer Management', 'Page Load', 'PASSED');
// Check for trainer list
const hasTrainerList = await page.isVisible('.trainer-list');
results.addResult('Trainer Management', 'Trainer List',
hasTrainerList ? 'PASSED' : 'FAILED');
// Check for edit capabilities
const hasEditButtons = await page.isVisible('.edit-trainer');
results.addResult('Trainer Management', 'Edit Buttons',
hasEditButtons ? 'PASSED' : 'FAILED');
// Check for role management
const hasRoleManagement = await page.isVisible('.role-management');
results.addResult('Trainer Management', 'Role Management',
hasRoleManagement ? 'PASSED' : 'FAILED');
} catch (error) {
results.addResult('Trainer Management', 'Page Load', 'FAILED', error.message);
}
// 9. NAVIGATION MENU
console.log('\n🧭 Testing Master Navigation Menu');
console.log('-'.repeat(40));
const navigationTests = [
{ text: 'Dashboard', url: '/master-trainer/master-dashboard/' },
{ text: 'Events', url: '/master-trainer/events/' },
{ text: 'Trainers', url: '/master-trainer/trainers/' },
{ text: 'Tools', dropdown: true },
{ text: 'Reports', dropdown: true }
];
for (const navTest of navigationTests) {
try {
if (navTest.dropdown) {
const dropdown = await page.locator(`.hvac-menu-item:has-text("${navTest.text}")`);
await dropdown.hover();
results.addResult('Navigation', `${navTest.text} Dropdown`, 'PASSED');
} else {
const link = await page.locator(`a:has-text("${navTest.text}")`).first();
const href = await link.getAttribute('href');
results.addResult('Navigation', navTest.text, 'PASSED',
`Link: ${href}`);
}
} catch {
results.addResult('Navigation', navTest.text, 'FAILED');
}
}
// 10. ROLE-BASED ACCESS CONTROL
console.log('\n🔒 Testing Role-Based Access Control');
console.log('-'.repeat(40));
// Test that master trainer can access all pages
const masterPages = [
'/master-trainer/master-dashboard/',
'/master-trainer/events/',
'/master-trainer/import-export/',
'/master-trainer/trainers/'
];
for (const pageUrl of masterPages) {
try {
await page.goto(`${CONFIG.baseUrl}${pageUrl}`);
await page.waitForLoadState('networkidle');
const hasAccess = !page.url().includes('login');
results.addResult('Access Control', `Master Access: ${pageUrl}`,
hasAccess ? 'PASSED' : 'FAILED');
} catch {
results.addResult('Access Control', `Master Access: ${pageUrl}`, 'FAILED');
}
}
// 11. DATA VALIDATION
console.log('\n✔ Testing Data Validation');
console.log('-'.repeat(40));
// Test trainer count consistency
await page.goto(`${CONFIG.baseUrl}/master-trainer/master-dashboard/`);
try {
const dashboardCount = await page.locator('.trainer-count').textContent();
await page.goto(`${CONFIG.baseUrl}/master-trainer/trainers/`);
const listCount = await page.locator('.trainer-list .trainer-item').count();
results.addResult('Data Validation', 'Trainer Count Consistency', 'PASSED',
`Dashboard: ${dashboardCount}, List: ${listCount}`);
} catch {
results.addResult('Data Validation', 'Trainer Count Consistency', 'FAILED');
}
// 12. RESPONSIVE DESIGN
console.log('\n📱 Testing Responsive Design');
console.log('-'.repeat(40));
// Test mobile viewport
await page.setViewportSize({ width: 375, height: 667 });
await page.goto(`${CONFIG.baseUrl}/master-trainer/master-dashboard/`);
await page.waitForLoadState('networkidle');
try {
const mobileMenu = await page.isVisible('.mobile-menu-toggle');
results.addResult('Responsive Design', 'Mobile Menu',
mobileMenu ? 'PASSED' : 'FAILED');
} catch {
results.addResult('Responsive Design', 'Mobile Menu', 'FAILED');
}
// Restore desktop viewport
await page.setViewportSize(CONFIG.viewport);
} catch (error) {
console.error('\n❌ Test Suite Error:', error.message);
results.addResult('Test Suite', 'Critical Error', 'FAILED', error.message);
} finally {
// Print summary
results.printSummary();
results.exportResults();
// Take final screenshot
await page.screenshot({
path: `master-trainer-test-${Date.now()}.png`,
fullPage: true
});
await browser.close();
}
}
// Execute tests
runMasterTrainerTests().catch(console.error);