## 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>
		
			
				
	
	
		
			332 lines
		
	
	
		
			No EOL
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			332 lines
		
	
	
		
			No EOL
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | ||
|  * Test script for the new trainer certification system
 | ||
|  * 
 | ||
|  * This script will:
 | ||
|  * 1. Create sample certification data via WordPress CLI
 | ||
|  * 2. Test the trainer profile display with Playwright
 | ||
|  * 3. Verify the new certification cards are working
 | ||
|  */
 | ||
| 
 | ||
| const { chromium } = require('playwright');
 | ||
| const { execSync } = require('child_process');
 | ||
| 
 | ||
| // Configuration
 | ||
| const BASE_URL = process.env.BASE_URL || 'https://upskill-staging.measurequick.com';
 | ||
| const HEADLESS = process.env.HEADLESS !== 'false';
 | ||
| 
 | ||
| // Test data for certifications
 | ||
| const testCertifications = [
 | ||
|     {
 | ||
|         trainer_id: null, // Will be set after finding a test trainer
 | ||
|         certification_type: 'measureQuick Certified Trainer',
 | ||
|         status: 'active',
 | ||
|         issue_date: '2024-01-15',
 | ||
|         expiration_date: '2026-01-15',
 | ||
|         certification_number: 'MQT-2024-001',
 | ||
|         notes: 'Initial certification - Test data'
 | ||
|     },
 | ||
|     {
 | ||
|         trainer_id: null,
 | ||
|         certification_type: 'measureQuick Certified Champion',
 | ||
|         status: 'active', 
 | ||
|         issue_date: '2024-06-01',
 | ||
|         expiration_date: '2025-01-15', // Expiring soon
 | ||
|         certification_number: 'MQC-2024-015',
 | ||
|         notes: 'Champion level certification - Test data'
 | ||
|     },
 | ||
|     {
 | ||
|         trainer_id: null,
 | ||
|         certification_type: 'measureQuick Certified Trainer',
 | ||
|         status: 'expired',
 | ||
|         issue_date: '2022-03-10',
 | ||
|         expiration_date: '2024-03-10', // Already expired
 | ||
|         certification_number: 'MQT-2022-045',
 | ||
|         notes: 'Previous certification - Test data'
 | ||
|     }
 | ||
| ];
 | ||
| 
 | ||
| async function createSampleCertifications() {
 | ||
|     console.log('🏗️ Creating sample certification data...');
 | ||
|     
 | ||
|     try {
 | ||
|         // First, find a test trainer user
 | ||
|         console.log('🔍 Finding test trainer...');
 | ||
|         const trainers = execSync(`
 | ||
|             UPSKILL_STAGING_URL="${BASE_URL}" wp-cli.phar --url=$UPSKILL_STAGING_URL --ssh=root@upskill-staging.measurequick.com user list --role=hvac_trainer --fields=ID,user_login,user_email --format=json
 | ||
|         `, { encoding: 'utf8' });
 | ||
|         
 | ||
|         const trainersList = JSON.parse(trainers);
 | ||
|         if (trainersList.length === 0) {
 | ||
|             throw new Error('No trainers found to test with');
 | ||
|         }
 | ||
|         
 | ||
|         const testTrainer = trainersList[0];
 | ||
|         console.log(`✅ Using test trainer: ${testTrainer.user_login} (ID: ${testTrainer.ID})`);
 | ||
|         
 | ||
|         // Update test certification data with trainer ID
 | ||
|         testCertifications.forEach(cert => {
 | ||
|             cert.trainer_id = testTrainer.ID;
 | ||
|         });
 | ||
|         
 | ||
|         // Create sample certifications via WordPress
 | ||
|         for (let i = 0; i < testCertifications.length; i++) {
 | ||
|             const cert = testCertifications[i];
 | ||
|             console.log(`📝 Creating certification ${i + 1}: ${cert.certification_type} (${cert.status})`);
 | ||
|             
 | ||
|             try {
 | ||
|                 // Create the certification post via WP-CLI
 | ||
|                 const result = execSync(`
 | ||
|                     UPSKILL_STAGING_URL="${BASE_URL}" wp-cli.phar --url=$UPSKILL_STAGING_URL --ssh=root@upskill-staging.measurequick.com post create --post_type=trainer_certification --post_title="${cert.certification_type} - ${testTrainer.user_login}" --post_status=publish --meta_input='{"trainer_id":"${cert.trainer_id}","certification_type":"${cert.certification_type}","status":"${cert.status}","issue_date":"${cert.issue_date}","expiration_date":"${cert.expiration_date}","certification_number":"${cert.certification_number}","notes":"${cert.notes}"}' --porcelain
 | ||
|                 `, { encoding: 'utf8' });
 | ||
|                 
 | ||
|                 const postId = result.trim();
 | ||
|                 console.log(`✅ Created certification post ID: ${postId}`);
 | ||
|                 
 | ||
|             } catch (error) {
 | ||
|                 console.log(`⚠️ Certification creation via WP-CLI failed, trying alternative method...`);
 | ||
|                 console.log(`Error: ${error.message}`);
 | ||
|                 
 | ||
|                 // Alternative: Create via PHP script
 | ||
|                 const phpScript = `
 | ||
| <?php
 | ||
| // Load WordPress
 | ||
| require_once('/var/www/html/wp-config.php');
 | ||
| 
 | ||
| // Create certification post
 | ||
| $post_data = array(
 | ||
|     'post_type' => 'trainer_certification',
 | ||
|     'post_title' => '${cert.certification_type} - ${testTrainer.user_login}',
 | ||
|     'post_status' => 'publish'
 | ||
| );
 | ||
| 
 | ||
| $post_id = wp_insert_post($post_data);
 | ||
| 
 | ||
| if ($post_id) {
 | ||
|     update_post_meta($post_id, 'trainer_id', ${cert.trainer_id});
 | ||
|     update_post_meta($post_id, 'certification_type', '${cert.certification_type}');
 | ||
|     update_post_meta($post_id, 'status', '${cert.status}');
 | ||
|     update_post_meta($post_id, 'issue_date', '${cert.issue_date}');
 | ||
|     update_post_meta($post_id, 'expiration_date', '${cert.expiration_date}');
 | ||
|     update_post_meta($post_id, 'certification_number', '${cert.certification_number}');
 | ||
|     update_post_meta($post_id, 'notes', '${cert.notes}');
 | ||
|     
 | ||
|     echo "Created certification post ID: " . $post_id . "\\n";
 | ||
| } else {
 | ||
|     echo "Failed to create certification post\\n";
 | ||
| }
 | ||
| ?>`;
 | ||
|                 
 | ||
|                 // Save PHP script temporarily
 | ||
|                 require('fs').writeFileSync('/tmp/create_cert.php', phpScript);
 | ||
|                 
 | ||
|                 // Execute PHP script on server
 | ||
|                 try {
 | ||
|                     const phpResult = execSync(`
 | ||
|                         ssh root@upskill-staging.measurequick.com 'cat > /tmp/create_cert.php' < /tmp/create_cert.php &&
 | ||
|                         ssh root@upskill-staging.measurequick.com 'cd /var/www/html && php /tmp/create_cert.php && rm /tmp/create_cert.php'
 | ||
|                     `, { encoding: 'utf8' });
 | ||
|                     
 | ||
|                     console.log(`📋 PHP result: ${phpResult.trim()}`);
 | ||
|                 } catch (phpError) {
 | ||
|                     console.log(`❌ PHP script failed: ${phpError.message}`);
 | ||
|                 }
 | ||
|             }
 | ||
|         }
 | ||
|         
 | ||
|         return {
 | ||
|             trainerId: testTrainer.ID,
 | ||
|             trainerLogin: testTrainer.user_login,
 | ||
|             certificationsCreated: testCertifications.length
 | ||
|         };
 | ||
|         
 | ||
|     } catch (error) {
 | ||
|         console.error('❌ Failed to create sample certifications:', error.message);
 | ||
|         throw error;
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| async function testCertificationDisplay(testTrainer) {
 | ||
|     console.log('🎭 Testing certification display with Playwright...');
 | ||
|     
 | ||
|     const browser = await chromium.launch({ headless: HEADLESS });
 | ||
|     const page = await browser.newPage();
 | ||
|     
 | ||
|     try {
 | ||
|         // Navigate to trainer profile or find-a-trainer page
 | ||
|         console.log('📍 Navigating to find-a-trainer page...');
 | ||
|         await page.goto(`${BASE_URL}/find-a-trainer/`);
 | ||
|         await page.waitForLoadState('networkidle');
 | ||
|         
 | ||
|         // Take screenshot of initial state
 | ||
|         await page.screenshot({ path: '/tmp/certification-test-initial.png' });
 | ||
|         console.log('📸 Screenshot saved: /tmp/certification-test-initial.png');
 | ||
|         
 | ||
|         // Look for trainer profiles on the page
 | ||
|         console.log('🔍 Looking for trainer profiles...');
 | ||
|         await page.waitForTimeout(3000); // Allow JS to load
 | ||
|         
 | ||
|         // Check if certification cards are being displayed
 | ||
|         const certificationCards = await page.locator('.hvac-certification-card').count();
 | ||
|         console.log(`🎴 Found ${certificationCards} certification cards`);
 | ||
|         
 | ||
|         const certificationGrids = await page.locator('.hvac-certifications-grid').count();
 | ||
|         console.log(`📱 Found ${certificationGrids} certification grids`);
 | ||
|         
 | ||
|         // Check for legacy certification display
 | ||
|         const legacyCerts = await page.locator('.hvac-certification-section').count();
 | ||
|         console.log(`📜 Found ${legacyCerts} legacy certification sections`);
 | ||
|         
 | ||
|         // Try to find specific trainer profile
 | ||
|         if (testTrainer && testTrainer.trainerLogin) {
 | ||
|             console.log(`🎯 Looking for specific trainer: ${testTrainer.trainerLogin}`);
 | ||
|             
 | ||
|             // Look for trainer name or profile link
 | ||
|             const trainerElements = await page.locator(`text=${testTrainer.trainerLogin}`).count();
 | ||
|             console.log(`👤 Found ${trainerElements} references to trainer ${testTrainer.trainerLogin}`);
 | ||
|         }
 | ||
|         
 | ||
|         // Check console for any JavaScript errors
 | ||
|         const consoleMessages = [];
 | ||
|         page.on('console', msg => {
 | ||
|             consoleMessages.push(`${msg.type()}: ${msg.text()}`);
 | ||
|         });
 | ||
|         
 | ||
|         // Reload page to catch any console messages
 | ||
|         await page.reload();
 | ||
|         await page.waitForLoadState('networkidle');
 | ||
|         await page.waitForTimeout(2000);
 | ||
|         
 | ||
|         if (consoleMessages.length > 0) {
 | ||
|             console.log('📝 Console messages:');
 | ||
|             consoleMessages.forEach(msg => console.log(`   ${msg}`));
 | ||
|         }
 | ||
|         
 | ||
|         // Take final screenshot
 | ||
|         await page.screenshot({ path: '/tmp/certification-test-final.png', fullPage: true });
 | ||
|         console.log('📸 Full page screenshot saved: /tmp/certification-test-final.png');
 | ||
|         
 | ||
|         // Test results
 | ||
|         const results = {
 | ||
|             certificationCards,
 | ||
|             certificationGrids,
 | ||
|             legacyCerts,
 | ||
|             consoleErrors: consoleMessages.filter(msg => msg.startsWith('error:')).length,
 | ||
|             testTrainer: testTrainer || null
 | ||
|         };
 | ||
|         
 | ||
|         return results;
 | ||
|         
 | ||
|     } catch (error) {
 | ||
|         console.error('❌ Test failed:', error.message);
 | ||
|         await page.screenshot({ path: '/tmp/certification-test-error.png' });
 | ||
|         console.log('📸 Error screenshot saved: /tmp/certification-test-error.png');
 | ||
|         throw error;
 | ||
|     } finally {
 | ||
|         await browser.close();
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| async function verifyDatabaseData(trainerId) {
 | ||
|     console.log('🔍 Verifying certification data in database...');
 | ||
|     
 | ||
|     try {
 | ||
|         // Check if certification posts were created
 | ||
|         const posts = execSync(`
 | ||
|             UPSKILL_STAGING_URL="${BASE_URL}" wp-cli.phar --url=$UPSKILL_STAGING_URL --ssh=root@upskill-staging.measurequick.com post list --post_type=trainer_certification --meta_key=trainer_id --meta_value=${trainerId} --fields=ID,post_title,post_status --format=json
 | ||
|         `, { encoding: 'utf8' });
 | ||
|         
 | ||
|         const certPosts = JSON.parse(posts);
 | ||
|         console.log(`📊 Found ${certPosts.length} certification posts for trainer ${trainerId}:`);
 | ||
|         
 | ||
|         certPosts.forEach(post => {
 | ||
|             console.log(`   - ${post.post_title} (ID: ${post.ID}, Status: ${post.post_status})`);
 | ||
|         });
 | ||
|         
 | ||
|         // Get meta data for first post
 | ||
|         if (certPosts.length > 0) {
 | ||
|             const firstPostId = certPosts[0].ID;
 | ||
|             const metaData = execSync(`
 | ||
|                 UPSKILL_STAGING_URL="${BASE_URL}" wp-cli.phar --url=$UPSKILL_STAGING_URL --ssh=root@upskill-staging.measurequick.com post meta list ${firstPostId} --format=json
 | ||
|             `, { encoding: 'utf8' });
 | ||
|             
 | ||
|             const meta = JSON.parse(metaData);
 | ||
|             console.log(`🏷️ Meta data for post ${firstPostId}:`);
 | ||
|             meta.forEach(item => {
 | ||
|                 if (item.meta_key.startsWith('certification_') || item.meta_key === 'trainer_id' || item.meta_key === 'status') {
 | ||
|                     console.log(`   - ${item.meta_key}: ${item.meta_value}`);
 | ||
|                 }
 | ||
|             });
 | ||
|         }
 | ||
|         
 | ||
|         return certPosts;
 | ||
|         
 | ||
|     } catch (error) {
 | ||
|         console.error('❌ Database verification failed:', error.message);
 | ||
|         return [];
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| async function cleanupTestData(trainerId) {
 | ||
|     console.log('🧹 Cleaning up test certification data...');
 | ||
|     
 | ||
|     try {
 | ||
|         // Delete test certification posts
 | ||
|         const result = execSync(`
 | ||
|             UPSKILL_STAGING_URL="${BASE_URL}" wp-cli.phar --url=$UPSKILL_STAGING_URL --ssh=root@upskill-staging.measurequick.com post delete $(wp post list --post_type=trainer_certification --meta_key=trainer_id --meta_value=${trainerId} --field=ID) --force
 | ||
|         `, { encoding: 'utf8' });
 | ||
|         
 | ||
|         console.log('✅ Test certification data cleaned up');
 | ||
|         console.log(`📋 Cleanup result: ${result.trim()}`);
 | ||
|         
 | ||
|     } catch (error) {
 | ||
|         console.log(`⚠️ Cleanup warning: ${error.message}`);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| // Main test execution
 | ||
| async function main() {
 | ||
|     console.log('🚀 Starting certification system test...\n');
 | ||
|     
 | ||
|     let testTrainer = null;
 | ||
|     
 | ||
|     try {
 | ||
|         // Step 1: Create sample data
 | ||
|         testTrainer = await createSampleCertifications();
 | ||
|         console.log(`\n✅ Sample data created for trainer ${testTrainer.trainerLogin}\n`);
 | ||
|         
 | ||
|         // Step 2: Verify database data
 | ||
|         const dbResults = await verifyDatabaseData(testTrainer.trainerId);
 | ||
|         console.log(`\n📊 Database verification: ${dbResults.length} posts found\n`);
 | ||
|         
 | ||
|         // Step 3: Test display
 | ||
|         const displayResults = await testCertificationDisplay(testTrainer);
 | ||
|         console.log('\n🎭 Display test results:');
 | ||
|         console.log(`   - Certification cards: ${displayResults.certificationCards}`);
 | ||
|         console.log(`   - Certification grids: ${displayResults.certificationGrids}`);
 | ||
|         console.log(`   - Legacy sections: ${displayResults.legacyCerts}`);
 | ||
|         console.log(`   - Console errors: ${displayResults.consoleErrors}`);
 | ||
|         
 | ||
|         // Test evaluation
 | ||
|         if (displayResults.certificationCards > 0) {
 | ||
|             console.log('\n✅ SUCCESS: New certification cards are being displayed!');
 | ||
|         } else if (displayResults.legacyCerts > 0) {
 | ||
|             console.log('\n⚠️ PARTIAL: Only legacy certification display found');
 | ||
|         } else {
 | ||
|             console.log('\n❌ ISSUE: No certification display found');
 | ||
|         }
 | ||
|         
 | ||
|     } catch (error) {
 | ||
|         console.error('\n💥 Test failed:', error.message);
 | ||
|         process.exit(1);
 | ||
|     } finally {
 | ||
|         // Step 4: Cleanup
 | ||
|         if (testTrainer) {
 | ||
|             await cleanupTestData(testTrainer.trainerId);
 | ||
|         }
 | ||
|     }
 | ||
|     
 | ||
|     console.log('\n🎉 Certification system test completed!');
 | ||
| }
 | ||
| 
 | ||
| // Run the test
 | ||
| main().catch(console.error); |