#!/usr/bin/env node /** * COMPREHENSIVE E2E STAGING TEST SUITE * * Complete functional testing of the HVAC Community Events WordPress plugin staging site * to identify bugs, blank pages, and optimization opportunities. * * Based on zen testgen analysis and comprehensive testing plan. * * Test Coverage: * - Role-based access control validation * - Page content verification (not just HTTP status) * - Dashboard functionality testing * - Public trainer directory testing * - Mobile responsiveness verification * - JavaScript error detection * - Security validation * - Performance monitoring * * Uses MCP Playwright browser tools for real UI interaction */ const { chromium } = require('playwright'); const path = require('path'); const fs = require('fs'); // Import WordPress error detector if available let WordPressErrorDetector; try { WordPressErrorDetector = require(path.join(__dirname, 'tests', 'framework', 'utils', 'WordPressErrorDetector')); } catch (e) { console.log('āš ļø WordPress error detector not available, continuing without it'); } // Configuration const CONFIG = { baseUrl: process.env.BASE_URL || 'https://upskill-staging.measurequick.com', headless: process.env.HEADLESS !== 'false', // Default to false for debugging slowMo: process.env.HEADLESS === 'false' ? 500 : 0, timeout: 30000, viewport: { width: 1280, height: 720 } }; // Test Accounts (update these based on staging environment) const TEST_ACCOUNTS = { guest: null, // No credentials for guest testing trainer: { username: process.env.TRAINER_USERNAME || 'test_trainer', password: process.env.TRAINER_PASSWORD || 'TestTrainer123!' }, masterTrainer: { username: process.env.MASTER_USERNAME || 'test_master', password: process.env.MASTER_PASSWORD || 'TestMaster123!' } }; // Pages to test organized by access level const TEST_PAGES = { public: [ { path: '/training-login/', name: 'Training Login', expectContent: 'login' }, { path: '/trainer/registration/', name: 'Trainer Registration', expectContent: 'registration' }, { path: '/find-a-trainer/', name: 'Find a Trainer', expectContent: 'find a trainer' } ], trainer: [ { path: '/trainer/dashboard/', name: 'Trainer Dashboard', expectContent: 'trainer dashboard' }, { path: '/trainer/profile/', name: 'Trainer Profile', expectContent: 'profile' }, { path: '/trainer/event/manage/', name: 'Manage Events', expectContent: 'event' }, { path: '/trainer/certificate-reports/', name: 'Certificate Reports', expectContent: 'certificate' }, { path: '/trainer/resources/', name: 'Training Resources', expectContent: 'resources' }, { path: '/trainer/venue/list/', name: 'Venue List', expectContent: 'venue' }, { path: '/trainer/organizer/manage/', name: 'Organizer Management', expectContent: 'organizer' } ], masterTrainer: [ { path: '/master-trainer/master-dashboard/', name: 'Master Dashboard', expectContent: 'master' }, { path: '/master-trainer/pending-approvals/', name: 'Pending Approvals', expectContent: 'approvals' }, { path: '/master-trainer/announcements/', name: 'Announcements', expectContent: 'announcements' }, { path: '/master-trainer/google-sheets/', name: 'Google Sheets', expectContent: 'sheets' }, { path: '/master-trainer/trainers/', name: 'Manage Trainers', expectContent: 'trainers' }, { path: '/master-trainer/import-export/', name: 'Import/Export', expectContent: 'import' } ] }; // Test Results Tracker class TestResults { constructor() { this.results = []; this.startTime = Date.now(); this.categories = { 'ACCESS_CONTROL': 0, 'CONTENT_VERIFICATION': 0, 'FUNCTIONALITY': 0, 'MOBILE_RESPONSIVE': 0, 'SECURITY': 0, 'PERFORMANCE': 0, 'JAVASCRIPT': 0 }; } addResult(category, test, status, details = '', url = '') { this.results.push({ category, test, status, details, url, timestamp: new Date().toISOString() }); if (this.categories[category] !== undefined) { this.categories[category]++; } const icon = status === 'PASSED' ? 'āœ…' : 'āŒ'; const urlInfo = url ? ` [${url}]` : ''; console.log(`${icon} ${category} - ${test}${urlInfo}`); 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(80)); console.log('šŸ“Š COMPREHENSIVE E2E TEST SUMMARY'); console.log('='.repeat(80)); console.log(`🌐 Base URL: ${CONFIG.baseUrl}`); console.log(`ā±ļø Duration: ${duration}s`); console.log(`šŸ“Š Total Tests: ${total}`); console.log(`āœ… Passed: ${passed}`); console.log(`āŒ Failed: ${failed}`); console.log(`šŸ“ˆ Success Rate: ${total > 0 ? ((passed/total)*100).toFixed(1) : 0}%`); console.log('\nšŸ“‹ CATEGORY BREAKDOWN:'); Object.entries(this.categories).forEach(([category, count]) => { const categoryResults = this.results.filter(r => r.category === category); const categoryPassed = categoryResults.filter(r => r.status === 'PASSED').length; const categoryFailed = categoryResults.filter(r => r.status === 'FAILED').length; if (count > 0) { console.log(` ${category}: ${categoryPassed}/${count} passed (${categoryFailed} failed)`); } }); if (failed > 0) { console.log('\nāŒ FAILED TESTS:'); this.results .filter(r => r.status === 'FAILED') .forEach(r => { console.log(` - ${r.category}: ${r.test}`); if (r.url) console.log(` URL: ${r.url}`); if (r.details) console.log(` Details: ${r.details}`); }); } console.log('\n' + '='.repeat(80)); } exportResults() { const filename = `staging-test-results-${Date.now()}.json`; const exportData = { config: CONFIG, summary: { total: this.results.length, passed: this.results.filter(r => r.status === 'PASSED').length, failed: this.results.filter(r => r.status === 'FAILED').length, duration: ((Date.now() - this.startTime) / 1000).toFixed(2) }, categoryBreakdown: this.categories, results: this.results }; fs.writeFileSync(filename, JSON.stringify(exportData, null, 2)); console.log(`šŸ“ Results exported to ${filename}`); return filename; } } // Utility Functions class TestHelpers { static async loginUser(page, username, password) { try { await page.goto(`${CONFIG.baseUrl}/training-login/`); await page.fill('input[name="log"]', username); await page.fill('input[name="pwd"]', password); await page.click('input[type="submit"]'); // Wait for redirect after login await page.waitForTimeout(2000); // Check if login was successful const currentUrl = page.url(); return !currentUrl.includes('training-login'); } catch (error) { console.error(`Login failed for ${username}:`, error.message); return false; } } static async checkWordPressErrors(page) { if (!WordPressErrorDetector) return { hasErrors: false, errors: [] }; try { return await WordPressErrorDetector.checkForErrors(page); } catch (error) { return { hasErrors: false, errors: [], note: 'Error detector unavailable' }; } } static async checkJavaScriptErrors(page) { const errors = []; page.on('console', msg => { if (msg.type() === 'error') { errors.push(msg.text()); } }); return errors; } static async checkPageContent(page, expectedContent, pageName) { try { // Check page loads await page.waitForLoadState('networkidle', { timeout: 10000 }); // Check for basic content const bodyContent = await page.textContent('body'); if (!bodyContent || bodyContent.trim().length < 100) { return { valid: false, reason: 'Page appears to be blank or minimal content' }; } // Check for expected content const hasExpectedContent = bodyContent.toLowerCase().includes(expectedContent.toLowerCase()); if (!hasExpectedContent) { return { valid: false, reason: `Expected content "${expectedContent}" not found` }; } // Check for common error indicators const errorIndicators = ['404', 'not found', 'error occurred', 'access denied', 'fatal error']; const hasError = errorIndicators.some(indicator => bodyContent.toLowerCase().includes(indicator) ); if (hasError) { return { valid: false, reason: 'Page contains error indicators' }; } return { valid: true, reason: 'Page loaded with expected content' }; } catch (error) { return { valid: false, reason: `Page load error: ${error.message}` }; } } static async checkMobileResponsive(page) { const viewports = [ { width: 375, height: 667, name: 'Mobile' }, { width: 768, height: 1024, name: 'Tablet' } ]; const results = []; for (const viewport of viewports) { try { await page.setViewportSize(viewport); await page.waitForTimeout(1000); // Check if content fits viewport const bodyWidth = await page.evaluate(() => document.body.scrollWidth); const hasHorizontalScroll = bodyWidth > viewport.width + 50; // 50px tolerance results.push({ viewport: viewport.name, responsive: !hasHorizontalScroll, actualWidth: bodyWidth, viewportWidth: viewport.width }); } catch (error) { results.push({ viewport: viewport.name, responsive: false, error: error.message }); } } // Reset to desktop await page.setViewportSize(CONFIG.viewport); return results; } } // Main Test Runner async function runComprehensiveTests() { console.log('šŸš€ Starting Comprehensive E2E Testing on Staging Environment'); console.log(`🌐 Base URL: ${CONFIG.baseUrl}`); console.log(`šŸ‘ļø Headless Mode: ${CONFIG.headless}`); const results = new TestResults(); const browser = await chromium.launch({ headless: CONFIG.headless, slowMo: CONFIG.slowMo }); try { // Test 1: Public Pages Access Control console.log('\nšŸ“‹ Testing Public Pages Access Control...'); await testPublicPagesAccess(browser, results); // Test 2: Trainer Pages Access Control console.log('\nšŸ“‹ Testing Trainer Pages Access Control...'); await testTrainerPagesAccess(browser, results); // Test 3: Master Trainer Pages Access Control console.log('\nšŸ“‹ Testing Master Trainer Pages Access Control...'); await testMasterTrainerPagesAccess(browser, results); // Test 4: Content Verification console.log('\nšŸ“‹ Testing Page Content Verification...'); await testPageContentVerification(browser, results); // Test 5: Dashboard Functionality console.log('\nšŸ“‹ Testing Dashboard Functionality...'); await testDashboardFunctionality(browser, results); // Test 6: Public Directory Functionality console.log('\nšŸ“‹ Testing Public Directory Functionality...'); await testPublicDirectoryFunctionality(browser, results); // Test 7: Mobile Responsiveness console.log('\nšŸ“‹ Testing Mobile Responsiveness...'); await testMobileResponsiveness(browser, results); // Test 8: Security Validation console.log('\nšŸ“‹ Testing Security Validation...'); await testSecurityValidation(browser, results); // Test 9: Performance Monitoring console.log('\nšŸ“‹ Testing Performance Monitoring...'); await testPerformanceMonitoring(browser, results); } catch (error) { console.error('āŒ Test execution failed:', error); results.addResult('GENERAL', 'Test Execution', 'FAILED', error.message); } finally { await browser.close(); } // Print and export results results.printSummary(); results.exportResults(); // Exit with appropriate code const failedCount = results.results.filter(r => r.status === 'FAILED').length; process.exit(failedCount > 0 ? 1 : 0); } // Test Implementations async function testPublicPagesAccess(browser, results) { const context = await browser.newContext(); const page = await context.newPage(); try { for (const pageInfo of TEST_PAGES.public) { try { await page.goto(`${CONFIG.baseUrl}${pageInfo.path}`, { waitUntil: 'networkidle' }); // Check WordPress errors const wpErrors = await TestHelpers.checkWordPressErrors(page); if (wpErrors.hasErrors) { results.addResult('ACCESS_CONTROL', `Public Access - ${pageInfo.name}`, 'FAILED', `WordPress errors: ${wpErrors.errors.join(', ')}`, pageInfo.path); continue; } // Check content const contentCheck = await TestHelpers.checkPageContent(page, pageInfo.expectContent, pageInfo.name); if (contentCheck.valid) { results.addResult('ACCESS_CONTROL', `Public Access - ${pageInfo.name}`, 'PASSED', 'Page accessible with expected content', pageInfo.path); } else { results.addResult('ACCESS_CONTROL', `Public Access - ${pageInfo.name}`, 'FAILED', contentCheck.reason, pageInfo.path); } } catch (error) { results.addResult('ACCESS_CONTROL', `Public Access - ${pageInfo.name}`, 'FAILED', error.message, pageInfo.path); } } } finally { await context.close(); } } async function testTrainerPagesAccess(browser, results) { // Test guest access (should be denied/redirected) await testGuestAccessToProtectedPages(browser, results, TEST_PAGES.trainer, 'Trainer'); // Test trainer access const context = await browser.newContext(); const page = await context.newPage(); try { // Login as trainer const loginSuccess = await TestHelpers.loginUser(page, TEST_ACCOUNTS.trainer.username, TEST_ACCOUNTS.trainer.password); if (!loginSuccess) { results.addResult('ACCESS_CONTROL', 'Trainer Login', 'FAILED', 'Could not login as trainer'); return; } results.addResult('ACCESS_CONTROL', 'Trainer Login', 'PASSED', 'Successfully logged in as trainer'); // Test each trainer page for (const pageInfo of TEST_PAGES.trainer) { try { await page.goto(`${CONFIG.baseUrl}${pageInfo.path}`, { waitUntil: 'networkidle' }); const wpErrors = await TestHelpers.checkWordPressErrors(page); if (wpErrors.hasErrors) { results.addResult('ACCESS_CONTROL', `Trainer Access - ${pageInfo.name}`, 'FAILED', `WordPress errors: ${wpErrors.errors.join(', ')}`, pageInfo.path); continue; } const contentCheck = await TestHelpers.checkPageContent(page, pageInfo.expectContent, pageInfo.name); if (contentCheck.valid) { results.addResult('ACCESS_CONTROL', `Trainer Access - ${pageInfo.name}`, 'PASSED', 'Trainer can access with expected content', pageInfo.path); } else { results.addResult('ACCESS_CONTROL', `Trainer Access - ${pageInfo.name}`, 'FAILED', contentCheck.reason, pageInfo.path); } } catch (error) { results.addResult('ACCESS_CONTROL', `Trainer Access - ${pageInfo.name}`, 'FAILED', error.message, pageInfo.path); } } } finally { await context.close(); } } async function testMasterTrainerPagesAccess(browser, results) { // Test guest access (should be denied/redirected) await testGuestAccessToProtectedPages(browser, results, TEST_PAGES.masterTrainer, 'Master Trainer'); // Test regular trainer access (should be denied) await testTrainerAccessToMasterPages(browser, results); // Test master trainer access const context = await browser.newContext(); const page = await context.newPage(); try { const loginSuccess = await TestHelpers.loginUser(page, TEST_ACCOUNTS.masterTrainer.username, TEST_ACCOUNTS.masterTrainer.password); if (!loginSuccess) { results.addResult('ACCESS_CONTROL', 'Master Trainer Login', 'FAILED', 'Could not login as master trainer'); return; } results.addResult('ACCESS_CONTROL', 'Master Trainer Login', 'PASSED', 'Successfully logged in as master trainer'); for (const pageInfo of TEST_PAGES.masterTrainer) { try { await page.goto(`${CONFIG.baseUrl}${pageInfo.path}`, { waitUntil: 'networkidle' }); const wpErrors = await TestHelpers.checkWordPressErrors(page); if (wpErrors.hasErrors) { results.addResult('ACCESS_CONTROL', `Master Access - ${pageInfo.name}`, 'FAILED', `WordPress errors: ${wpErrors.errors.join(', ')}`, pageInfo.path); continue; } const contentCheck = await TestHelpers.checkPageContent(page, pageInfo.expectContent, pageInfo.name); if (contentCheck.valid) { results.addResult('ACCESS_CONTROL', `Master Access - ${pageInfo.name}`, 'PASSED', 'Master trainer can access with expected content', pageInfo.path); } else { results.addResult('ACCESS_CONTROL', `Master Access - ${pageInfo.name}`, 'FAILED', contentCheck.reason, pageInfo.path); } } catch (error) { results.addResult('ACCESS_CONTROL', `Master Access - ${pageInfo.name}`, 'FAILED', error.message, pageInfo.path); } } } finally { await context.close(); } } async function testGuestAccessToProtectedPages(browser, results, pages, pageType) { const context = await browser.newContext(); const page = await context.newPage(); try { for (const pageInfo of pages) { try { await page.goto(`${CONFIG.baseUrl}${pageInfo.path}`, { waitUntil: 'networkidle' }); const currentUrl = page.url(); if (currentUrl.includes('training-login') || currentUrl.includes('access-denied')) { results.addResult('ACCESS_CONTROL', `Guest Denied - ${pageType} ${pageInfo.name}`, 'PASSED', 'Correctly redirected guest user', pageInfo.path); } else { const bodyContent = await page.textContent('body'); if (bodyContent.toLowerCase().includes('access denied')) { results.addResult('ACCESS_CONTROL', `Guest Denied - ${pageType} ${pageInfo.name}`, 'PASSED', 'Correctly denied guest access', pageInfo.path); } else { results.addResult('ACCESS_CONTROL', `Guest Denied - ${pageType} ${pageInfo.name}`, 'FAILED', 'Guest user can access protected page', pageInfo.path); } } } catch (error) { results.addResult('ACCESS_CONTROL', `Guest Denied - ${pageType} ${pageInfo.name}`, 'FAILED', error.message, pageInfo.path); } } } finally { await context.close(); } } async function testTrainerAccessToMasterPages(browser, results) { const context = await browser.newContext(); const page = await context.newPage(); try { const loginSuccess = await TestHelpers.loginUser(page, TEST_ACCOUNTS.trainer.username, TEST_ACCOUNTS.trainer.password); if (!loginSuccess) { results.addResult('ACCESS_CONTROL', 'Trainer Access to Master Pages', 'FAILED', 'Could not login as trainer'); return; } for (const pageInfo of TEST_PAGES.masterTrainer) { try { await page.goto(`${CONFIG.baseUrl}${pageInfo.path}`, { waitUntil: 'networkidle' }); const bodyContent = await page.textContent('body'); if (bodyContent.toLowerCase().includes('access denied') || bodyContent.toLowerCase().includes('insufficient permissions')) { results.addResult('ACCESS_CONTROL', `Trainer Denied - ${pageInfo.name}`, 'PASSED', 'Correctly denied trainer access to master page', pageInfo.path); } else { results.addResult('ACCESS_CONTROL', `Trainer Denied - ${pageInfo.name}`, 'FAILED', 'Regular trainer can access master trainer page', pageInfo.path); } } catch (error) { results.addResult('ACCESS_CONTROL', `Trainer Denied - ${pageInfo.name}`, 'FAILED', error.message, pageInfo.path); } } } finally { await context.close(); } } async function testPageContentVerification(browser, results) { const context = await browser.newContext(); const page = await context.newPage(); try { // Test high-priority pages with detailed content verification const priorityPages = [ { path: '/find-a-trainer/', checks: ['.hvac-find-trainer-page', '.hvac-trainer-grid', 'h1'] }, { path: '/training-login/', checks: ['form', 'input[name="log"]', 'input[name="pwd"]'] } ]; for (const pageTest of priorityPages) { try { await page.goto(`${CONFIG.baseUrl}${pageTest.path}`, { waitUntil: 'networkidle' }); let allChecksPass = true; const failedChecks = []; for (const selector of pageTest.checks) { try { await page.waitForSelector(selector, { timeout: 5000 }); } catch (error) { allChecksPass = false; failedChecks.push(selector); } } if (allChecksPass) { results.addResult('CONTENT_VERIFICATION', `Content Check - ${pageTest.path}`, 'PASSED', 'All required elements present', pageTest.path); } else { results.addResult('CONTENT_VERIFICATION', `Content Check - ${pageTest.path}`, 'FAILED', `Missing elements: ${failedChecks.join(', ')}`, pageTest.path); } } catch (error) { results.addResult('CONTENT_VERIFICATION', `Content Check - ${pageTest.path}`, 'FAILED', error.message, pageTest.path); } } } finally { await context.close(); } } async function testDashboardFunctionality(browser, results) { const context = await browser.newContext(); const page = await context.newPage(); try { const loginSuccess = await TestHelpers.loginUser(page, TEST_ACCOUNTS.trainer.username, TEST_ACCOUNTS.trainer.password); if (!loginSuccess) { results.addResult('FUNCTIONALITY', 'Dashboard Login Required', 'FAILED', 'Could not login for dashboard tests'); return; } await page.goto(`${CONFIG.baseUrl}/trainer/dashboard/`, { waitUntil: 'networkidle' }); // Test statistics display try { await page.waitForSelector('.hvac-stat-card', { timeout: 5000 }); const statCards = await page.$$('.hvac-stat-card'); if (statCards.length >= 3) { results.addResult('FUNCTIONALITY', 'Dashboard Statistics', 'PASSED', `Found ${statCards.length} stat cards`, '/trainer/dashboard/'); } else { results.addResult('FUNCTIONALITY', 'Dashboard Statistics', 'FAILED', `Only found ${statCards.length} stat cards`, '/trainer/dashboard/'); } } catch (error) { results.addResult('FUNCTIONALITY', 'Dashboard Statistics', 'FAILED', 'No stat cards found', '/trainer/dashboard/'); } // Test events table try { await page.waitForSelector('.events-table', { timeout: 5000 }); results.addResult('FUNCTIONALITY', 'Dashboard Events Table', 'PASSED', 'Events table is present', '/trainer/dashboard/'); } catch (error) { results.addResult('FUNCTIONALITY', 'Dashboard Events Table', 'FAILED', 'Events table not found', '/trainer/dashboard/'); } // Test search functionality try { const searchInput = page.locator('#hvac-event-search'); await searchInput.fill('test search'); results.addResult('FUNCTIONALITY', 'Dashboard Search', 'PASSED', 'Search input is functional', '/trainer/dashboard/'); } catch (error) { results.addResult('FUNCTIONALITY', 'Dashboard Search', 'FAILED', 'Search input not functional', '/trainer/dashboard/'); } } finally { await context.close(); } } async function testPublicDirectoryFunctionality(browser, results) { const context = await browser.newContext(); const page = await context.newPage(); try { await page.goto(`${CONFIG.baseUrl}/find-a-trainer/`, { waitUntil: 'networkidle' }); // Test trainer cards display try { await page.waitForSelector('.hvac-trainer-card', { timeout: 5000 }); const trainerCards = await page.$$('.hvac-trainer-card'); if (trainerCards.length > 0) { results.addResult('FUNCTIONALITY', 'Directory Trainer Cards', 'PASSED', `Found ${trainerCards.length} trainer cards`, '/find-a-trainer/'); } else { results.addResult('FUNCTIONALITY', 'Directory Trainer Cards', 'FAILED', 'No trainer cards found', '/find-a-trainer/'); } } catch (error) { results.addResult('FUNCTIONALITY', 'Directory Trainer Cards', 'FAILED', 'Could not locate trainer cards', '/find-a-trainer/'); } // Test search functionality try { const searchInput = page.locator('#hvac-trainer-search'); await searchInput.fill('test'); results.addResult('FUNCTIONALITY', 'Directory Search', 'PASSED', 'Search input is functional', '/find-a-trainer/'); } catch (error) { results.addResult('FUNCTIONALITY', 'Directory Search', 'FAILED', 'Search input not functional', '/find-a-trainer/'); } // Test filter buttons try { await page.waitForSelector('button[data-filter]', { timeout: 5000 }); const filterButtons = await page.$$('button[data-filter]'); if (filterButtons.length > 0) { results.addResult('FUNCTIONALITY', 'Directory Filters', 'PASSED', `Found ${filterButtons.length} filter buttons`, '/find-a-trainer/'); } else { results.addResult('FUNCTIONALITY', 'Directory Filters', 'FAILED', 'No filter buttons found', '/find-a-trainer/'); } } catch (error) { results.addResult('FUNCTIONALITY', 'Directory Filters', 'FAILED', 'Could not locate filter buttons', '/find-a-trainer/'); } } finally { await context.close(); } } async function testMobileResponsiveness(browser, results) { const context = await browser.newContext(); const page = await context.newPage(); try { const testPages = [ '/find-a-trainer/', '/training-login/', '/trainer/dashboard/' // This will redirect to login for guest ]; for (const testPath of testPages) { try { await page.goto(`${CONFIG.baseUrl}${testPath}`, { waitUntil: 'networkidle' }); const mobileResults = await TestHelpers.checkMobileResponsive(page); let allResponsive = true; const issues = []; for (const result of mobileResults) { if (!result.responsive) { allResponsive = false; issues.push(`${result.viewport}: ${result.error || 'Not responsive'}`); } } if (allResponsive) { results.addResult('MOBILE_RESPONSIVE', `Mobile Check - ${testPath}`, 'PASSED', 'Responsive on all tested viewports', testPath); } else { results.addResult('MOBILE_RESPONSIVE', `Mobile Check - ${testPath}`, 'FAILED', `Issues: ${issues.join(', ')}`, testPath); } } catch (error) { results.addResult('MOBILE_RESPONSIVE', `Mobile Check - ${testPath}`, 'FAILED', error.message, testPath); } } } finally { await context.close(); } } async function testSecurityValidation(browser, results) { const context = await browser.newContext(); const page = await context.newPage(); try { // Test XSS prevention in search await page.goto(`${CONFIG.baseUrl}/find-a-trainer/?search=`, { waitUntil: 'networkidle' }); const searchInput = page.locator('#hvac-trainer-search'); const inputValue = await searchInput.inputValue(); if (inputValue.includes('