/** * Comprehensive Certification System E2E Tests * * Tests all new multiple certification features: * 1. Find-a-trainer page with multiple certification displays * 2. Find-a-trainer modal popups with certification badges * 3. Personal trainer profile read-only certification display * 4. Master Trainer CRUD operations for certifications * 5. Backward compatibility with legacy system */ const { chromium } = require('playwright'); const fs = require('fs').promises; const path = require('path'); // Configuration const BASE_URL = process.env.BASE_URL || 'https://upskill-staging.measurequick.com'; const HEADLESS = process.env.HEADLESS !== 'false'; const TIMEOUT = 30000; // Test Credentials const TEST_ACCOUNTS = { trainer: { username: 'test_trainer', password: 'TestTrainer123!', email: 'test_trainer@example.com' }, master: { username: 'test_master', password: 'JoeTrainer2025@', email: 'JoeMedosch@gmail.com' } }; class CertificationTestSuite { constructor() { this.results = []; this.passed = 0; this.failed = 0; this.screenshotDir = '/tmp/certification-tests'; } async setup() { // Create screenshot directory try { await fs.mkdir(this.screenshotDir, { recursive: true }); } catch (error) { // Directory might already exist } this.browser = await chromium.launch({ headless: HEADLESS, args: ['--no-sandbox', '--disable-setuid-sandbox'] }); this.context = await this.browser.newContext({ viewport: { width: 1280, height: 720 } }); this.page = await this.context.newPage(); // Enable request/response logging for debugging this.page.on('response', response => { if (response.status() >= 400) { console.log(`āŒ HTTP ${response.status()}: ${response.url()}`); } }); } async teardown() { if (this.browser) { await this.browser.close(); } } async addResult(name, status, details = '', screenshotName = null) { const result = { name, status, details, timestamp: new Date().toISOString() }; if (screenshotName && this.page) { const screenshotPath = `${this.screenshotDir}/${screenshotName}`; await this.page.screenshot({ path: screenshotPath, fullPage: true }); result.screenshot = screenshotPath; } this.results.push(result); if (status === 'PASS') { this.passed++; console.log(`āœ… ${name}`); } else { this.failed++; console.log(`āŒ ${name}`); } if (details) { console.log(` ${details}`); } } // Test 1: Find-a-Trainer Multiple Certifications Display async testFindTrainerCertifications() { console.log('\nšŸŽÆ Testing Find-a-Trainer Multiple Certifications...'); try { await this.page.goto(`${BASE_URL}/find-a-trainer/`); await this.page.waitForLoadState('networkidle'); await this.page.waitForTimeout(3000); // Check for new certification system elements const certificationBadges = await this.page.locator('.hvac-trainer-cert-badge').count(); const certificationContainers = await this.page.locator('.hvac-trainer-certifications').count(); const trainerCards = await this.page.locator('.hvac-trainer-card').count(); await this.addResult( 'Find-a-Trainer Page Loads', certificationContainers > 0 || trainerCards > 0 ? 'PASS' : 'FAIL', `Found ${trainerCards} trainer cards, ${certificationContainers} cert containers, ${certificationBadges} cert badges`, 'find-trainer-page.png' ); // Test multiple certification badges on trainer cards if (certificationBadges > 0) { // Check for different certification types const trainerBadges = await this.page.locator('.hvac-cert-trainer').count(); const championBadges = await this.page.locator('.hvac-cert-champion').count(); const legacyBadges = await this.page.locator('.hvac-cert-legacy').count(); await this.addResult( 'Multiple Certification Types Display', (trainerBadges > 0 || championBadges > 0) ? 'PASS' : 'FAIL', `Trainer badges: ${trainerBadges}, Champion badges: ${championBadges}, Legacy: ${legacyBadges}` ); // Test badge styling and visibility const visibleBadges = await this.page.locator('.hvac-trainer-cert-badge:visible').count(); await this.addResult( 'Certification Badges Visible', visibleBadges > 0 ? 'PASS' : 'FAIL', `${visibleBadges} certification badges are visible` ); } // Test Champion vs Trainer clickability const clickableTrainers = await this.page.locator('.hvac-trainer-card:not(.hvac-champion-card) .hvac-open-profile').count(); const championCards = await this.page.locator('.hvac-champion-card').count(); await this.addResult( 'Trainer/Champion Clickability Logic', 'PASS', // This is hard to test automatically, so we just log the counts `Clickable trainers: ${clickableTrainers}, Champion cards: ${championCards}` ); } catch (error) { await this.addResult( 'Find-a-Trainer Certification Test', 'FAIL', `Error: ${error.message}`, 'find-trainer-error.png' ); } } // Test 2: Find-a-Trainer Modal Multiple Certifications async testFindTrainerModals() { console.log('\nšŸŽ­ Testing Find-a-Trainer Modal Certifications...'); try { // Find a clickable trainer card and click it const clickableTrainer = this.page.locator('.hvac-trainer-card:not(.hvac-champion-card) .hvac-open-profile').first(); if (await clickableTrainer.count() > 0) { await clickableTrainer.click(); await this.page.waitForTimeout(2000); // Check if modal opened const modal = this.page.locator('#hvac-trainer-modal'); const isModalVisible = await modal.isVisible(); await this.addResult( 'Trainer Modal Opens', isModalVisible ? 'PASS' : 'FAIL', `Modal visibility: ${isModalVisible}`, 'trainer-modal.png' ); if (isModalVisible) { // Check for certification badges in modal const modalCertBadges = await modal.locator('.hvac-trainer-cert-badge').count(); const certificationContainer = await modal.locator('.hvac-modal-certification-badges').count(); await this.addResult( 'Modal Contains Multiple Certifications', modalCertBadges > 0 || certificationContainer > 0 ? 'PASS' : 'FAIL', `Modal cert badges: ${modalCertBadges}, cert containers: ${certificationContainer}` ); // Test modal certification content if (modalCertBadges > 0) { const modalTrainerBadges = await modal.locator('.hvac-cert-trainer').count(); const modalChampionBadges = await modal.locator('.hvac-cert-champion').count(); await this.addResult( 'Modal Certification Types Display', 'PASS', `Modal trainer badges: ${modalTrainerBadges}, champion badges: ${modalChampionBadges}` ); } // Close modal await this.page.locator('.hvac-modal-close').click(); await this.page.waitForTimeout(1000); } } else { await this.addResult( 'Find Clickable Trainer for Modal Test', 'FAIL', 'No clickable trainers found for modal testing' ); } } catch (error) { await this.addResult( 'Find-a-Trainer Modal Test', 'FAIL', `Error: ${error.message}`, 'modal-test-error.png' ); } } // Test 3: Personal Trainer Profile Certification Display async testPersonalProfileCertifications() { console.log('\nšŸ‘¤ Testing Personal Trainer Profile Certifications...'); try { // Login as trainer await this.loginAs('trainer'); // Navigate to trainer profile await this.page.goto(`${BASE_URL}/trainer/profile/`); await this.page.waitForLoadState('networkidle'); await this.page.waitForTimeout(2000); const pageTitle = await this.page.title(); const isProfilePage = pageTitle.toLowerCase().includes('profile') || this.page.url().includes('/trainer/profile'); await this.addResult( 'Personal Profile Page Access', isProfilePage ? 'PASS' : 'FAIL', `Page title: ${pageTitle}, URL: ${this.page.url()}`, 'personal-profile.png' ); if (isProfilePage) { // Check for new certification system const certificationCards = await this.page.locator('.hvac-certification-card').count(); const certificationGrid = await this.page.locator('.hvac-certifications-grid').count(); const legacyCertSection = await this.page.locator('.hvac-certification-section').count(); await this.addResult( 'Personal Profile Certification Display', certificationCards > 0 || certificationGrid > 0 || legacyCertSection > 0 ? 'PASS' : 'FAIL', `New cert cards: ${certificationCards}, grids: ${certificationGrid}, legacy sections: ${legacyCertSection}` ); // If new system is active, test its components if (certificationCards > 0) { const certTitles = await this.page.locator('.hvac-certification-title').count(); const statusBadges = await this.page.locator('.hvac-certification-status-badge').count(); const certDetails = await this.page.locator('.hvac-certification-detail').count(); const expirationInfo = await this.page.locator('.hvac-certification-expiration').count(); await this.addResult( 'Personal Profile Certification Components', 'PASS', `Titles: ${certTitles}, Status badges: ${statusBadges}, Details: ${certDetails}, Expiration: ${expirationInfo}` ); // Test read-only nature (no edit buttons should be present) const editButtons = await this.page.locator('button[class*="edit"], .edit-certification, [class*="crud"]').count(); await this.addResult( 'Personal Profile Read-Only Verification', editButtons === 0 ? 'PASS' : 'FAIL', `Found ${editButtons} edit buttons (should be 0 for read-only)` ); } } } catch (error) { await this.addResult( 'Personal Profile Certification Test', 'FAIL', `Error: ${error.message}`, 'personal-profile-error.png' ); } } // Test 4: Master Trainer Certification CRUD Operations async testMasterTrainerCertificationCRUD() { console.log('\nšŸ”§ Testing Master Trainer Certification CRUD...'); try { // Ensure clean session for master trainer login await this.ensureCleanSession(); // Login as master trainer await this.loginAs('master'); // Navigate to master trainer edit page await this.page.goto(`${BASE_URL}/master-trainer/edit-trainer-profile/`); await this.page.waitForLoadState('networkidle'); await this.page.waitForTimeout(2000); const pageTitle = await this.page.title(); const isMasterPage = pageTitle.toLowerCase().includes('edit') || this.page.url().includes('edit-trainer-profile'); await this.addResult( 'Master Trainer Edit Page Access', isMasterPage ? 'PASS' : 'FAIL', `Page title: ${pageTitle}, URL: ${this.page.url()}`, 'master-edit-page.png' ); if (isMasterPage) { // Test trainer selection const trainerSelect = this.page.locator('#select-trainer'); const selectExists = await trainerSelect.count() > 0; await this.addResult( 'Trainer Selection Dropdown Present', selectExists ? 'PASS' : 'FAIL', `Trainer select element exists: ${selectExists}` ); if (selectExists) { // Get available trainers and select one const options = await trainerSelect.locator('option').count(); if (options > 1) { // More than just the default "-- Select a Trainer --" option await trainerSelect.selectOption({ index: 1 }); // Select first real trainer await this.page.waitForTimeout(2000); // Check if profile form appeared const profileForm = this.page.locator('#trainer-profile-edit-form'); const formVisible = await profileForm.isVisible(); await this.addResult( 'Trainer Profile Form Loads', formVisible ? 'PASS' : 'FAIL', `Profile edit form visible: ${formVisible}`, 'master-profile-form.png' ); if (formVisible) { // Test certification management components await this.testCertificationCRUDComponents(); } } else { await this.addResult( 'Available Trainers for Selection', 'FAIL', `Only ${options} options found (need trainers to select)` ); } } } } catch (error) { await this.addResult( 'Master Trainer CRUD Test', 'FAIL', `Error: ${error.message}`, 'master-crud-error.png' ); } } async testCertificationCRUDComponents() { console.log('\n šŸ” Testing CRUD Components...'); try { // Check for certification management elements const certificationsList = this.page.locator('#certifications-list'); const addButton = this.page.locator('#add-certification-btn'); const certModal = this.page.locator('#certification-modal'); const listExists = await certificationsList.count() > 0; const addButtonExists = await addButton.count() > 0; const modalExists = await certModal.count() > 0; await this.addResult( 'Certification CRUD Elements Present', listExists && addButtonExists && modalExists ? 'PASS' : 'FAIL', `List: ${listExists}, Add button: ${addButtonExists}, Modal: ${modalExists}` ); // Test Add Certification Modal if (addButtonExists) { await addButton.click(); await this.page.waitForTimeout(1000); const modalVisible = await certModal.isVisible(); await this.addResult( 'Add Certification Modal Opens', modalVisible ? 'PASS' : 'FAIL', `Modal visible after clicking add: ${modalVisible}`, 'add-cert-modal.png' ); if (modalVisible) { // Test modal form elements const certTypeSelect = await certModal.locator('#cert-type').count(); const statusSelect = await certModal.locator('#cert-status').count(); const certNumber = await certModal.locator('#cert-number').count(); const issueDate = await certModal.locator('#issue-date').count(); const expirationDate = await certModal.locator('#expiration-date').count(); const saveButton = await certModal.locator('#save-certification-btn').count(); const allFieldsPresent = certTypeSelect > 0 && statusSelect > 0 && certNumber > 0 && issueDate > 0 && expirationDate > 0 && saveButton > 0; await this.addResult( 'Certification Form Fields Present', allFieldsPresent ? 'PASS' : 'FAIL', `Type: ${certTypeSelect}, Status: ${statusSelect}, Number: ${certNumber}, Issue: ${issueDate}, Expiry: ${expirationDate}, Save: ${saveButton}` ); // Close modal await this.page.locator('#close-certification-modal').click(); await this.page.waitForTimeout(500); } } // Test existing certification display and edit/delete buttons const existingCerts = await certificationsList.locator('.certification-item').count(); if (existingCerts > 0) { const editButtons = await certificationsList.locator('.edit-certification').count(); const deleteButtons = await certificationsList.locator('.delete-certification').count(); await this.addResult( 'Existing Certifications Management', editButtons > 0 && deleteButtons > 0 ? 'PASS' : 'FAIL', `Found ${existingCerts} certifications with ${editButtons} edit and ${deleteButtons} delete buttons` ); } } catch (error) { await this.addResult( 'Certification CRUD Components Test', 'FAIL', `Error testing CRUD components: ${error.message}` ); } } // Test 5: Backward Compatibility async testBackwardCompatibility() { console.log('\nšŸ”„ Testing Backward Compatibility...'); try { // Test find-a-trainer page for legacy elements await this.page.goto(`${BASE_URL}/find-a-trainer/`); await this.page.waitForLoadState('networkidle'); await this.page.waitForTimeout(2000); const newSystemElements = await this.page.locator('.hvac-trainer-cert-badge').count(); const legacyElements = await this.page.locator('.hvac-trainer-certification').count(); const defaultBadges = await this.page.locator('.hvac-cert-default').count(); await this.addResult( 'Backward Compatibility - Find-a-Trainer', 'PASS', // This is informational `New system elements: ${newSystemElements}, Legacy elements: ${legacyElements}, Default badges: ${defaultBadges}` ); // Test that page doesn't break with mixed data const jsErrors = []; this.page.on('pageerror', error => jsErrors.push(error.message)); await this.page.reload(); await this.page.waitForLoadState('networkidle'); await this.page.waitForTimeout(2000); await this.addResult( 'JavaScript Error Check', jsErrors.length === 0 ? 'PASS' : 'FAIL', jsErrors.length > 0 ? `Errors: ${jsErrors.join(', ')}` : 'No JavaScript errors detected' ); } catch (error) { await this.addResult( 'Backward Compatibility Test', 'FAIL', `Error: ${error.message}`, 'backward-compatibility-error.png' ); } } // Helper method for login async loginAs(accountType) { const account = TEST_ACCOUNTS[accountType]; if (!account) { throw new Error(`Unknown account type: ${accountType}`); } await this.page.goto(`${BASE_URL}/training-login/`); await this.page.waitForLoadState('networkidle'); // Fill login form (use correct field IDs from custom login form) await this.page.fill('#user_login', account.username); await this.page.fill('#user_pass', account.password); await this.page.click('button[type="submit"]'); // Wait for redirect await this.page.waitForLoadState('networkidle'); await this.page.waitForTimeout(2000); // Verify login success const currentUrl = this.page.url(); const loginSuccessful = currentUrl.includes('/dashboard') || currentUrl.includes('/trainer/') || currentUrl.includes('/master-trainer/'); if (!loginSuccessful) { throw new Error(`Login failed for ${accountType}. Current URL: ${currentUrl}`); } } // Helper method to ensure clean session async ensureCleanSession() { try { // Clear browser context and start fresh await this.page.context().clearCookies(); await this.page.goto(`${BASE_URL}/`); await this.page.waitForLoadState('networkidle'); await this.page.waitForTimeout(500); } catch (error) { console.log('Note: Session cleanup may have failed, continuing...'); } } // Main test runner async runAllTests() { console.log('šŸš€ Starting Comprehensive Certification System Tests'); console.log(`🌐 Base URL: ${BASE_URL}`); console.log(`šŸ‘ļø Headless: ${HEADLESS}`); console.log('='.repeat(60)); try { await this.setup(); // Run all test suites await this.testFindTrainerCertifications(); await this.testFindTrainerModals(); await this.testPersonalProfileCertifications(); await this.testMasterTrainerCertificationCRUD(); await this.testBackwardCompatibility(); } catch (error) { console.error('šŸ’„ Test suite failed:', error.message); await this.addResult('Test Suite Execution', 'FAIL', error.message); } finally { await this.teardown(); } // Generate final report await this.generateReport(); } async generateReport() { const total = this.passed + this.failed; const successRate = total > 0 ? Math.round((this.passed / total) * 100) : 0; console.log('\n' + '='.repeat(60)); console.log('šŸ“Š CERTIFICATION SYSTEM TEST RESULTS'); console.log('='.repeat(60)); console.log(`Total Tests: ${total}`); console.log(`āœ… Passed: ${this.passed}`); console.log(`āŒ Failed: ${this.failed}`); console.log(`šŸ“ˆ Success Rate: ${successRate}%`); console.log(`šŸ“ø Screenshots: ${this.screenshotDir}`); // Detailed results console.log('\nšŸ“‹ Detailed Results:'); this.results.forEach(result => { const icon = result.status === 'PASS' ? 'āœ…' : 'āŒ'; console.log(`${icon} ${result.name}`); if (result.details) { console.log(` ${result.details}`); } if (result.screenshot) { console.log(` šŸ“ø ${result.screenshot}`); } }); // Save JSON report const report = { summary: { total, passed: this.passed, failed: this.failed, successRate: `${successRate}%` }, results: this.results, timestamp: new Date().toISOString() }; const reportPath = `${this.screenshotDir}/certification-test-report.json`; await fs.writeFile(reportPath, JSON.stringify(report, null, 2)); console.log(`\nšŸ’¾ Full report saved: ${reportPath}`); // Final assessment if (successRate >= 80) { console.log('\nšŸŽ‰ OVERALL ASSESSMENT: SUCCESS - Certification system is working well!'); } else if (successRate >= 60) { console.log('\nāš ļø OVERALL ASSESSMENT: PARTIAL - Some issues need attention'); } else { console.log('\nāŒ OVERALL ASSESSMENT: FAILURE - Significant issues detected'); } } } // Execute the test suite async function main() { const testSuite = new CertificationTestSuite(); await testSuite.runAllTests(); } // Handle uncaught errors process.on('unhandledRejection', (reason, promise) => { console.error('šŸ’„ Unhandled Rejection at:', promise, 'reason:', reason); process.exit(1); }); // Run the tests main().catch(console.error);