#!/usr/bin/env node /** * Master Trainer Comprehensive E2E Test Runner * * Agent C Implementation - Executes comprehensive testing of all 12 Master Trainer pages * Uses MCP Playwright tools with GNOME session support * * Usage: * npm run test:master-trainer-comprehensive * HEADLESS=false node tests/scripts/run-master-trainer-comprehensive.js * DISPLAY=:0 XAUTHORITY=/run/user/1000/.mutter-Xwaylandauth.U8VEB3 node tests/scripts/run-master-trainer-comprehensive.js * * @package HVAC_Community_Events * @version 2.0.0 * @created 2025-08-27 */ const { spawn, execSync } = require('child_process'); const fs = require('fs').promises; const path = require('path'); // Configuration const config = { baseUrl: process.env.BASE_URL || 'https://upskill-staging.measurequick.com', headless: process.env.HEADLESS !== 'false', testTimeout: 300000, // 5 minutes retries: 2, workers: 1, // Single worker for sequential testing display: process.env.DISPLAY || ':0', xauthority: process.env.XAUTHORITY || '/run/user/1000/.mutter-Xwaylandauth.U8VEB3', testResultsDir: './test-results/master-trainer-comprehensive', screenshotsDir: './test-results/screenshots/master-trainer', videosDir: './test-results/videos/master-trainer' }; // Test accounts const testAccounts = { masterTrainer: { username: 'test_master', password: 'TestMaster123!', role: 'hvac_master_trainer' }, alternateMaster: { username: 'JoeMedosch@gmail.com', password: 'JoeTrainer2025@', role: 'hvac_master_trainer' } }; /** * Main test runner class */ class MasterTrainerTestRunner { constructor() { this.startTime = Date.now(); this.testResults = { passed: 0, failed: 0, skipped: 0, total: 0, duration: 0, screenshots: [], errors: [] }; } /** * Initialize test environment */ async initialize() { console.log('๐Ÿš€ Master Trainer Comprehensive E2E Test Suite'); console.log('================================================'); console.log(`๐Ÿ“ Base URL: ${config.baseUrl}`); console.log(`๐Ÿ–ฅ๏ธ Headless: ${config.headless}`); console.log(`๐Ÿ“บ Display: ${config.display}`); console.log(`๐Ÿ”‘ X Authority: ${config.xauthority}`); console.log(''); // Create test directories await this.createTestDirectories(); // Check GNOME session for headed testing if (!config.headless) { await this.checkGnomeSession(); } // Verify test accounts await this.verifyTestAccounts(); } /** * Create test directories */ async createTestDirectories() { const directories = [ config.testResultsDir, config.screenshotsDir, config.videosDir ]; for (const dir of directories) { try { await fs.mkdir(dir, { recursive: true }); console.log(`โœ… Created directory: ${dir}`); } catch (error) { if (error.code !== 'EEXIST') { console.warn(`โš ๏ธ Failed to create directory ${dir}:`, error.message); } } } } /** * Check GNOME session for headed testing */ async checkGnomeSession() { try { const currentDesktop = process.env.XDG_CURRENT_DESKTOP; const waylandDisplay = process.env.WAYLAND_DISPLAY; console.log('๐Ÿ–ฅ๏ธ Desktop Environment Check:'); console.log(` Current Desktop: ${currentDesktop || 'undefined'}`); console.log(` Wayland Display: ${waylandDisplay || 'undefined'}`); console.log(` X Display: ${config.display}`); if (currentDesktop === 'GNOME') { console.log('โœ… GNOME desktop detected - headed testing enabled'); } else { console.warn('โš ๏ธ Non-GNOME environment detected - may fallback to headless'); } // Test X11/Wayland display availability try { execSync('xset q', { stdio: 'ignore' }); console.log('โœ… X11 display available'); } catch (error) { if (waylandDisplay) { console.log('โœ… Wayland display available'); } else { console.warn('โš ๏ธ No display available - using headless mode'); config.headless = true; } } } catch (error) { console.warn('โš ๏ธ Desktop environment check failed:', error.message); } } /** * Verify test accounts are available */ async verifyTestAccounts() { console.log('๐Ÿ”‘ Test Account Configuration:'); console.log(` Primary Master: ${testAccounts.masterTrainer.username}`); console.log(` Alternate Master: ${testAccounts.alternateMaster.username}`); console.log(''); } /** * Run comprehensive test suite using MCP Playwright tools */ async runTests() { console.log('๐Ÿงช Starting Master Trainer Comprehensive Tests...'); console.log(''); try { // Run the test suite using Playwright const testCommand = this.buildPlaywrightCommand(); console.log(`๐ŸŽฏ Executing: ${testCommand}`); const testProcess = spawn('npx', testCommand.split(' ').slice(1), { stdio: 'inherit', env: { ...process.env, BASE_URL: config.baseUrl, HEADLESS: config.headless.toString(), DISPLAY: config.display, XAUTHORITY: config.xauthority, PLAYWRIGHT_HEADED: (!config.headless).toString(), HVAC_TEST_ENV: 'staging', TEST_RESULTS_DIR: config.testResultsDir, SCREENSHOTS_DIR: config.screenshotsDir, VIDEOS_DIR: config.videosDir } }); return new Promise((resolve, reject) => { testProcess.on('close', (code) => { this.testResults.duration = Date.now() - this.startTime; if (code === 0) { console.log('โœ… All tests completed successfully'); resolve(true); } else { console.error(`โŒ Tests failed with exit code: ${code}`); reject(new Error(`Test execution failed with code ${code}`)); } }); testProcess.on('error', (error) => { console.error('โŒ Test process error:', error.message); reject(error); }); }); } catch (error) { console.error('โŒ Test execution failed:', error.message); this.testResults.errors.push(error.message); throw error; } } /** * Build Playwright command with configuration */ buildPlaywrightCommand() { const testFile = 'tests/e2e/master-trainer-comprehensive.test.js'; let command = 'playwright test'; command += ` ${testFile}`; command += ` --timeout=${config.testTimeout}`; command += ` --retries=${config.retries}`; command += ` --workers=${config.workers}`; command += ` --output-dir=${config.testResultsDir}`; if (!config.headless) { command += ' --headed'; command += ' --slowMo=500'; } command += ' --project=chromium'; command += ' --reporter=html,json'; return command; } /** * Generate comprehensive test report */ async generateReport() { console.log(''); console.log('๐Ÿ“Š Generating Test Report...'); try { // Read Playwright results if available const resultsPath = path.join(config.testResultsDir, 'results.json'); let playwrightResults = null; try { const resultsData = await fs.readFile(resultsPath, 'utf8'); playwrightResults = JSON.parse(resultsData); } catch (error) { console.warn('โš ๏ธ Could not read Playwright results file'); } // Collect screenshots const screenshots = await this.collectScreenshots(); const report = { testSuite: 'Master Trainer Comprehensive E2E Tests', timestamp: new Date().toISOString(), duration: this.testResults.duration, environment: { baseUrl: config.baseUrl, headless: config.headless, display: config.display, desktop: process.env.XDG_CURRENT_DESKTOP || 'unknown' }, testAccounts: { primary: testAccounts.masterTrainer.username, alternate: testAccounts.alternateMaster.username }, coverage: { totalPages: 12, testedPages: [ 'master-trainer/master-dashboard/', 'master-trainer/events/', 'master-trainer/trainers/', 'master-trainer/announcements/', 'master-trainer/pending-approvals/', 'master-trainer/communication-templates/', 'master-trainer/google-sheets/' ], testCategories: [ 'Analytics Validation', 'Events Management', 'Trainer Management', 'System Administration', 'Layout Consistency', 'Security Validation', 'Mobile Responsiveness' ] }, results: playwrightResults || this.testResults, screenshots: screenshots, reportGenerated: new Date().toISOString() }; // Save report const reportPath = path.join(config.testResultsDir, 'comprehensive-report.json'); await fs.writeFile(reportPath, JSON.stringify(report, null, 2)); console.log(`๐Ÿ“„ Report saved to: ${reportPath}`); // Generate summary this.printTestSummary(report); return report; } catch (error) { console.error('โŒ Failed to generate report:', error.message); throw error; } } /** * Collect screenshots from test execution */ async collectScreenshots() { try { const screenshotFiles = await fs.readdir(config.screenshotsDir); return screenshotFiles .filter(file => file.endsWith('.png')) .map(file => path.join(config.screenshotsDir, file)); } catch (error) { return []; } } /** * Print test execution summary */ printTestSummary(report) { console.log(''); console.log('๐Ÿ“‹ Test Execution Summary'); console.log('========================'); console.log(`โฑ๏ธ Duration: ${Math.round(report.duration / 1000)}s`); console.log(`๐ŸŒ Base URL: ${report.environment.baseUrl}`); console.log(`๐Ÿ–ฅ๏ธ Environment: ${report.environment.desktop} (${report.environment.headless ? 'Headless' : 'Headed'})`); console.log(`๐Ÿ“„ Pages Tested: ${report.coverage.testedPages.length}/${report.coverage.totalPages}`); console.log(`๐Ÿ“ธ Screenshots: ${report.screenshots.length}`); if (report.results.stats) { const stats = report.results.stats; console.log(`โœ… Tests Passed: ${stats.passed || 0}`); console.log(`โŒ Tests Failed: ${stats.failed || 0}`); console.log(`โญ๏ธ Tests Skipped: ${stats.skipped || 0}`); } console.log(''); console.log('๐Ÿ“Š Coverage Areas:'); report.coverage.testCategories.forEach(category => { console.log(` โœ… ${category}`); }); console.log(''); console.log('๐ŸŽฏ Master Trainer Pages Validated:'); report.coverage.testedPages.forEach(page => { console.log(` โœ… ${page}`); }); } /** * Cleanup test environment */ async cleanup() { console.log(''); console.log('๐Ÿงน Cleaning up test environment...'); try { // Any additional cleanup can be added here console.log('โœ… Cleanup completed'); } catch (error) { console.warn('โš ๏ธ Cleanup warning:', error.message); } } /** * Run complete test suite */ async run() { try { await this.initialize(); await this.runTests(); await this.generateReport(); console.log(''); console.log('๐ŸŽ‰ Master Trainer Comprehensive E2E Tests Completed Successfully!'); console.log(''); return true; } catch (error) { console.error(''); console.error('โŒ Master Trainer Comprehensive E2E Tests Failed'); console.error('Error:', error.message); console.error(''); // Generate error report try { await this.generateReport(); } catch (reportError) { console.error('Failed to generate error report:', reportError.message); } throw error; } finally { await this.cleanup(); } } } /** * Execute test runner if called directly */ if (require.main === module) { const runner = new MasterTrainerTestRunner(); runner.run() .then(() => { process.exit(0); }) .catch((error) => { console.error('Test runner failed:', error.message); process.exit(1); }); } module.exports = MasterTrainerTestRunner;