/** * TEC Template Cross-Browser Compatibility Test Suite * * Comprehensive testing across Chrome, Firefox, and Safari to ensure * the enhanced TEC Community Events template works consistently * across all major browsers. * * Features: * - Field population testing in all browsers * - JavaScript functionality validation * - CSS rendering and responsive design testing * - Performance comparison across browsers * - Browser-specific issue detection * * @author Claude Code - Test Automation Specialist * @version 1.0.0 * @date August 12, 2025 */ const { chromium, firefox, webkit } = require('playwright'); const fs = require('fs'); // Cross-browser test configuration const BROWSER_CONFIG = { browsers: { chromium: { name: 'Chrome', headless: false, viewport: { width: 1920, height: 1080 }, userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' }, firefox: { name: 'Firefox', headless: false, viewport: { width: 1920, height: 1080 }, userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0' }, webkit: { name: 'Safari', headless: false, viewport: { width: 1920, height: 1080 }, userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15' } }, testSuite: { baseUrl: 'https://upskill-staging.measurequick.com', timeout: 60000, slowMo: 800, credentials: { username: 'test_trainer', password: 'TestTrainer123!' } }, compatibility: { targetCompatibilityRate: 95, // 95% compatibility across browsers criticalFeatures: [ 'form_accessibility', 'field_population', 'javascript_enhanced_features', 'responsive_design', 'form_submission' ] } }; // Test data for cross-browser validation const CROSS_BROWSER_TEST_DATA = { basicEvent: { title: 'Cross-Browser HVAC Workshop', content: 'Testing enhanced template across all browsers', excerpt: 'Cross-browser compatibility validation event', startDate: '2025-09-20', endDate: '2025-09-20', startTime: '10:00', endTime: '16:00', cost: '199' }, enhancedFeatures: { categories: [1, 2], tags: ['cross-browser', 'testing', 'HVAC'], featuredImage: { id: 'test-123', url: 'https://example.com/test.jpg' } }, responsiveBreakpoints: [ { name: 'Mobile', width: 375, height: 667 }, { name: 'Tablet', width: 768, height: 1024 }, { name: 'Desktop', width: 1920, height: 1080 }, { name: 'Large Desktop', width: 2560, height: 1440 } ] }; /** * Cross-Browser Compatibility Test Suite */ class CrossBrowserTestSuite { constructor() { this.results = { startTime: Date.now(), browsers: {}, compatibility: { overallRate: 0, criticalFeaturesOk: false, browserComparison: {} }, performance: {}, issues: [], screenshots: {} }; } /** * Run comprehensive cross-browser tests */ async runCrossBrowserTests() { console.log('๐ŸŒ TEC TEMPLATE CROSS-BROWSER COMPATIBILITY SUITE'); console.log('================================================='); console.log('Testing: Chrome, Firefox, Safari'); console.log(`Target compatibility: ${BROWSER_CONFIG.compatibility.targetCompatibilityRate}%`); console.log(''); try { // Test each browser for (const [browserType, config] of Object.entries(BROWSER_CONFIG.browsers)) { console.log(`\n๐Ÿงช Testing ${config.name}...`); console.log('-'.repeat(30)); await this.testBrowser(browserType, config); } // Analyze compatibility results this.analyzeCompatibilityResults(); // Generate comprehensive report this.generateCompatibilityReport(); return this.results; } catch (error) { console.error('โŒ Cross-browser testing failed:', error.message); throw error; } } /** * Test individual browser */ async testBrowser(browserType, config) { const browserEngine = this.getBrowserEngine(browserType); const browser = await browserEngine.launch({ headless: config.headless, slowMo: BROWSER_CONFIG.testSuite.slowMo }); const context = await browser.newContext({ viewport: config.viewport, userAgent: config.userAgent }); const page = await context.newPage(); // Initialize browser results this.results.browsers[browserType] = { name: config.name, tests: {}, performance: {}, issues: [], screenshots: {}, overallScore: 0 }; try { // Console monitoring page.on('console', msg => { if (msg.type() === 'error') { this.results.browsers[browserType].issues.push(`Console Error: ${msg.text()}`); } }); // Error monitoring page.on('pageerror', error => { this.results.browsers[browserType].issues.push(`Page Error: ${error.message}`); }); // Test suite for this browser await this.loginAndNavigate(page, browserType); await this.testFormAccessibility(page, browserType); await this.testFieldPopulation(page, browserType); await this.testJavaScriptFeatures(page, browserType); await this.testResponsiveDesign(page, browserType, context); await this.testFormSubmissionReadiness(page, browserType); await this.measurePerformance(page, browserType); // Calculate browser score this.calculateBrowserScore(browserType); } catch (error) { console.error(`โŒ ${config.name} testing failed:`, error.message); this.results.browsers[browserType].issues.push(`Test failure: ${error.message}`); } finally { await browser.close(); } } /** * Get browser engine */ getBrowserEngine(browserType) { switch (browserType) { case 'chromium': return chromium; case 'firefox': return firefox; case 'webkit': return webkit; default: throw new Error(`Unknown browser type: ${browserType}`); } } /** * Login and navigate to form */ async loginAndNavigate(page, browserType) { console.log(` ๐Ÿ” ${this.results.browsers[browserType].name}: Logging in...`); const startTime = Date.now(); try { // Navigate to login await page.goto(`${BROWSER_CONFIG.testSuite.baseUrl}/training-login/`); await page.waitForLoadState('networkidle'); // Login await page.fill('input[name="log"]', BROWSER_CONFIG.testSuite.credentials.username); await page.fill('input[name="pwd"]', BROWSER_CONFIG.testSuite.credentials.password); await page.click('input[type="submit"]'); await page.waitForLoadState('networkidle'); // Navigate to event form await page.goto(`${BROWSER_CONFIG.testSuite.baseUrl}/trainer/event/create/`); await page.waitForLoadState('networkidle'); await page.waitForTimeout(3000); const endTime = Date.now(); this.results.browsers[browserType].tests.login = { success: true, loadTime: endTime - startTime }; console.log(` โœ… Login successful (${endTime - startTime}ms)`); } catch (error) { this.results.browsers[browserType].tests.login = { success: false, error: error.message }; console.log(` โŒ Login failed: ${error.message}`); throw error; } } /** * Test form accessibility */ async testFormAccessibility(page, browserType) { console.log(` ๐Ÿ” ${this.results.browsers[browserType].name}: Testing form accessibility...`); const accessibilityTest = await page.evaluate(() => { const results = { formExists: false, enhancedIndicator: false, basicFields: 0, enhancedFields: 0, totalFields: 0 }; // Check form existence const form = document.querySelector('form, .hvac-tec-enhanced-form'); results.formExists = form !== null; // Check enhanced template indicator results.enhancedIndicator = document.querySelector('.hvac-success-indicator') !== null; // Count basic fields const basicFieldSelectors = [ 'input[name="post_title"]', '#title', 'textarea[name="post_content"]', '#content', '#tcepostcontent', 'input[name="EventStartDate"]', '#EventStartDate', 'input[name="EventEndDate"]', '#EventEndDate' ]; basicFieldSelectors.forEach(selector => { if (document.querySelector(selector)) { results.basicFields++; } }); // Count enhanced fields const enhancedFieldSelectors = [ '#hvac_post_excerpt', '#hvac-categories-section', '#hvac-featured-image-section', '#hvac-tags-section' ]; enhancedFieldSelectors.forEach(selector => { if (document.querySelector(selector)) { results.enhancedFields++; } }); results.totalFields = results.basicFields + results.enhancedFields; return results; }); this.results.browsers[browserType].tests.accessibility = accessibilityTest; const accessibilityScore = Math.round( (accessibilityTest.totalFields / 10) * 100 // Expecting ~10 key fields ); console.log(` ๐Ÿ“Š Form fields accessible: ${accessibilityTest.totalFields} (${accessibilityScore}%)`); console.log(` ๐Ÿ”ง Enhanced features: ${accessibilityTest.enhancedFields > 0 ? 'โœ…' : 'โŒ'}`); } /** * Test field population */ async testFieldPopulation(page, browserType) { console.log(` ๐ŸŽฏ ${this.results.browsers[browserType].name}: Testing field population...`); const populationTests = [ { name: 'title', selector: '#title, input[name="post_title"]', value: CROSS_BROWSER_TEST_DATA.basicEvent.title, type: 'text' }, { name: 'content', selector: '#content, #tcepostcontent', value: CROSS_BROWSER_TEST_DATA.basicEvent.content, type: 'textarea' }, { name: 'excerpt', selector: '#hvac_post_excerpt', value: CROSS_BROWSER_TEST_DATA.basicEvent.excerpt, type: 'textarea' }, { name: 'start_date', selector: '#EventStartDate', value: CROSS_BROWSER_TEST_DATA.basicEvent.startDate, type: 'date' }, { name: 'end_date', selector: '#EventEndDate', value: CROSS_BROWSER_TEST_DATA.basicEvent.endDate, type: 'date' }, { name: 'cost', selector: '#EventCost', value: CROSS_BROWSER_TEST_DATA.basicEvent.cost, type: 'number' } ]; let populatedCount = 0; const populationResults = {}; for (const test of populationTests) { try { const element = page.locator(test.selector).first(); if (await element.count() > 0) { await element.fill(test.value); populatedCount++; populationResults[test.name] = { success: true }; console.log(` โœ… ${test.name}: Populated`); } else { populationResults[test.name] = { success: false, reason: 'Field not found' }; console.log(` โŒ ${test.name}: Field not found`); } } catch (error) { populationResults[test.name] = { success: false, reason: error.message }; console.log(` โŒ ${test.name}: ${error.message}`); } } const populationRate = Math.round((populatedCount / populationTests.length) * 100); this.results.browsers[browserType].tests.fieldPopulation = { populatedFields: populatedCount, totalFields: populationTests.length, successRate: populationRate, details: populationResults }; console.log(` ๐Ÿ“Š Population success: ${populatedCount}/${populationTests.length} (${populationRate}%)`); } /** * Test JavaScript enhanced features */ async testJavaScriptFeatures(page, browserType) { console.log(` ๐Ÿ”ง ${this.results.browsers[browserType].name}: Testing JavaScript features...`); const jsFeatureTest = await page.evaluate(() => { const results = { enhancedSystemPresent: false, jqueryPresent: false, windowObjectsPresent: 0, errorsDetected: [] }; try { // Check enhanced field population system results.enhancedSystemPresent = typeof window.HVACEnhancedFieldPopulation !== 'undefined'; // Check jQuery results.jqueryPresent = typeof window.jQuery !== 'undefined'; // Check for important window objects const expectedObjects = ['wp', 'tribe', 'ajaxurl']; expectedObjects.forEach(obj => { if (typeof window[obj] !== 'undefined') { results.windowObjectsPresent++; } }); // Test enhanced system if available if (results.enhancedSystemPresent) { const testResult = window.HVACEnhancedFieldPopulation.testFieldAccess(); results.enhancedSystemTest = testResult; } } catch (error) { results.errorsDetected.push(error.message); } return results; }); this.results.browsers[browserType].tests.javascriptFeatures = jsFeatureTest; console.log(` ๐Ÿ”ง Enhanced system: ${jsFeatureTest.enhancedSystemPresent ? 'โœ…' : 'โŒ'}`); console.log(` ๐Ÿ“š jQuery available: ${jsFeatureTest.jqueryPresent ? 'โœ…' : 'โŒ'}`); console.log(` ๐ŸชŸ Window objects: ${jsFeatureTest.windowObjectsPresent}/3`); if (jsFeatureTest.errorsDetected.length > 0) { console.log(` โš ๏ธ JS errors: ${jsFeatureTest.errorsDetected.length}`); this.results.browsers[browserType].issues.push(...jsFeatureTest.errorsDetected); } } /** * Test responsive design */ async testResponsiveDesign(page, browserType, context) { console.log(` ๐Ÿ“ฑ ${this.results.browsers[browserType].name}: Testing responsive design...`); const responsiveResults = {}; for (const breakpoint of CROSS_BROWSER_TEST_DATA.responsiveBreakpoints) { try { // Set viewport await page.setViewportSize({ width: breakpoint.width, height: breakpoint.height }); await page.waitForTimeout(1000); // Allow layout to adjust // Test layout at this breakpoint const layoutTest = await page.evaluate(() => { const form = document.querySelector('form, .hvac-tec-enhanced-form'); if (!form) return { visible: false }; const rect = form.getBoundingClientRect(); const isVisible = rect.width > 0 && rect.height > 0; // Check for horizontal scroll const hasHorizontalScroll = document.body.scrollWidth > window.innerWidth; // Check if important elements are visible const titleField = document.querySelector('#title, input[name="post_title"]'); const submitButton = document.querySelector('input[type="submit"], button[type="submit"]'); return { visible: isVisible, width: rect.width, height: rect.height, hasHorizontalScroll: hasHorizontalScroll, titleFieldVisible: titleField ? titleField.offsetWidth > 0 : false, submitButtonVisible: submitButton ? submitButton.offsetWidth > 0 : false }; }); responsiveResults[breakpoint.name] = { viewport: breakpoint, layout: layoutTest, success: layoutTest.visible && !layoutTest.hasHorizontalScroll }; // Take screenshot const screenshotPath = `test-results/cross-browser/${browserType}-${breakpoint.name.toLowerCase()}.png`; await page.screenshot({ path: screenshotPath }); console.log(` ๐Ÿ“ฑ ${breakpoint.name} (${breakpoint.width}x${breakpoint.height}): ${layoutTest.visible ? 'โœ…' : 'โŒ'}`); } catch (error) { responsiveResults[breakpoint.name] = { viewport: breakpoint, success: false, error: error.message }; console.log(` โŒ ${breakpoint.name}: ${error.message}`); } } this.results.browsers[browserType].tests.responsiveDesign = responsiveResults; // Reset to desktop viewport await page.setViewportSize({ width: 1920, height: 1080 }); } /** * Test form submission readiness */ async testFormSubmissionReadiness(page, browserType) { console.log(` ๐Ÿ“ค ${this.results.browsers[browserType].name}: Testing form submission readiness...`); const submissionTest = await page.evaluate(() => { const results = { formExists: false, submitButtonExists: false, requiredFieldsPresent: 0, formAction: null, formMethod: null }; const form = document.querySelector('form'); if (form) { results.formExists = true; results.formAction = form.action; results.formMethod = form.method; } const submitButton = document.querySelector('input[type="submit"], button[type="submit"]'); results.submitButtonExists = submitButton !== null; // Check for required fields const requiredSelectors = [ '#title, input[name="post_title"]', '#EventStartDate', '#EventEndDate' ]; requiredSelectors.forEach(selector => { if (document.querySelector(selector)) { results.requiredFieldsPresent++; } }); return results; }); this.results.browsers[browserType].tests.formSubmission = submissionTest; console.log(` ๐Ÿ“ค Form ready: ${submissionTest.formExists && submissionTest.submitButtonExists ? 'โœ…' : 'โŒ'}`); console.log(` ๐Ÿ“‹ Required fields: ${submissionTest.requiredFieldsPresent}/3`); } /** * Measure performance */ async measurePerformance(page, browserType) { console.log(` โฑ๏ธ ${this.results.browsers[browserType].name}: Measuring performance...`); const performanceMetrics = await page.evaluate(() => { if (!window.performance) return null; const navigation = performance.getEntriesByType('navigation')[0]; const paintEntries = performance.getEntriesByType('paint'); return { domContentLoaded: navigation.domContentLoadedEventEnd - navigation.domContentLoadedEventStart, loadComplete: navigation.loadEventEnd - navigation.loadEventStart, domInteractive: navigation.domInteractive - navigation.fetchStart, firstPaint: paintEntries.find(entry => entry.name === 'first-paint')?.startTime || 0, firstContentfulPaint: paintEntries.find(entry => entry.name === 'first-contentful-paint')?.startTime || 0 }; }); this.results.browsers[browserType].performance = performanceMetrics; if (performanceMetrics) { console.log(` โฑ๏ธ DOM ready: ${Math.round(performanceMetrics.domInteractive)}ms`); console.log(` โฑ๏ธ First paint: ${Math.round(performanceMetrics.firstPaint)}ms`); } } /** * Calculate browser compatibility score */ calculateBrowserScore(browserType) { const tests = this.results.browsers[browserType].tests; let totalScore = 0; let maxScore = 0; // Login test (20 points) maxScore += 20; if (tests.login?.success) totalScore += 20; // Accessibility test (25 points) maxScore += 25; if (tests.accessibility) { const accessScore = Math.min(25, (tests.accessibility.totalFields / 10) * 25); totalScore += accessScore; } // Field population test (25 points) maxScore += 25; if (tests.fieldPopulation) { totalScore += (tests.fieldPopulation.successRate / 100) * 25; } // JavaScript features test (15 points) maxScore += 15; if (tests.javascriptFeatures) { let jsScore = 0; if (tests.javascriptFeatures.enhancedSystemPresent) jsScore += 8; if (tests.javascriptFeatures.jqueryPresent) jsScore += 4; if (tests.javascriptFeatures.windowObjectsPresent >= 2) jsScore += 3; totalScore += jsScore; } // Responsive design test (10 points) maxScore += 10; if (tests.responsiveDesign) { const responsiveBreakpoints = Object.values(tests.responsiveDesign); const passedBreakpoints = responsiveBreakpoints.filter(bp => bp.success).length; totalScore += (passedBreakpoints / responsiveBreakpoints.length) * 10; } // Form submission test (5 points) maxScore += 5; if (tests.formSubmission?.formExists && tests.formSubmission?.submitButtonExists) { totalScore += 5; } const browserScore = Math.round((totalScore / maxScore) * 100); this.results.browsers[browserType].overallScore = browserScore; console.log(` ๐Ÿ“Š ${this.results.browsers[browserType].name} Compatibility Score: ${browserScore}%`); } /** * Analyze compatibility results */ analyzeCompatibilityResults() { console.log('\n๐Ÿ“Š ANALYZING CROSS-BROWSER COMPATIBILITY...'); console.log('--------------------------------------------'); const browserScores = Object.values(this.results.browsers).map(b => b.overallScore); const averageScore = Math.round(browserScores.reduce((a, b) => a + b, 0) / browserScores.length); this.results.compatibility.overallRate = averageScore; // Check critical features across browsers const criticalFeatureResults = {}; BROWSER_CONFIG.compatibility.criticalFeatures.forEach(feature => { criticalFeatureResults[feature] = this.checkCriticalFeatureAcrossBrowsers(feature); }); const criticalFeaturesPassCount = Object.values(criticalFeatureResults).filter(Boolean).length; this.results.compatibility.criticalFeaturesOk = criticalFeaturesPassCount === BROWSER_CONFIG.compatibility.criticalFeatures.length; // Browser comparison this.results.compatibility.browserComparison = Object.entries(this.results.browsers).reduce((acc, [browserType, data]) => { acc[browserType] = { name: data.name, score: data.overallScore, issues: data.issues.length, performance: data.performance }; return acc; }, {}); console.log(`๐Ÿ“Š Average compatibility score: ${averageScore}%`); console.log(`๐ŸŽฏ Target (${BROWSER_CONFIG.compatibility.targetCompatibilityRate}%): ${averageScore >= BROWSER_CONFIG.compatibility.targetCompatibilityRate ? 'โœ… ACHIEVED' : 'โŒ NOT MET'}`); console.log(`๐Ÿ”‘ Critical features: ${this.results.compatibility.criticalFeaturesOk ? 'โœ… ALL WORKING' : 'โŒ SOME ISSUES'}`); } /** * Check critical feature across browsers */ checkCriticalFeatureAcrossBrowsers(feature) { const browserResults = Object.values(this.results.browsers); switch (feature) { case 'form_accessibility': return browserResults.every(b => b.tests.accessibility?.formExists); case 'field_population': return browserResults.every(b => (b.tests.fieldPopulation?.successRate || 0) >= 80); case 'javascript_enhanced_features': return browserResults.every(b => b.tests.javascriptFeatures?.jqueryPresent); case 'responsive_design': return browserResults.every(b => { const responsive = b.tests.responsiveDesign; if (!responsive) return false; const passedBreakpoints = Object.values(responsive).filter(bp => bp.success).length; return passedBreakpoints >= 3; // At least 3 out of 4 breakpoints working }); case 'form_submission': return browserResults.every(b => b.tests.formSubmission?.formExists && b.tests.formSubmission?.submitButtonExists); default: return false; } } /** * Generate compatibility report */ generateCompatibilityReport() { console.log('\n๐ŸŒ CROSS-BROWSER COMPATIBILITY REPORT'); console.log('===================================='); // Overall summary const targetMet = this.results.compatibility.overallRate >= BROWSER_CONFIG.compatibility.targetCompatibilityRate; const criticalFeaturesOk = this.results.compatibility.criticalFeaturesOk; console.log(`๐ŸŽฏ OVERALL COMPATIBILITY: ${this.results.compatibility.overallRate}%`); if (targetMet && criticalFeaturesOk) { console.log('โœ… SUCCESS - Cross-browser compatibility achieved!'); } else { console.log('โŒ ISSUES DETECTED - Cross-browser compatibility needs improvement'); } // Browser-by-browser results console.log('\n๐Ÿ“Š BROWSER RESULTS:'); Object.entries(this.results.compatibility.browserComparison).forEach(([browserType, data]) => { console.log(` ${data.name}: ${data.score}% (${data.issues} issues)`); }); // Critical features summary console.log('\n๐Ÿ”‘ CRITICAL FEATURES:'); BROWSER_CONFIG.compatibility.criticalFeatures.forEach(feature => { const working = this.checkCriticalFeatureAcrossBrowsers(feature); console.log(` ${feature}: ${working ? 'โœ…' : 'โŒ'}`); }); // Performance comparison console.log('\nโฑ๏ธ PERFORMANCE COMPARISON:'); Object.entries(this.results.compatibility.browserComparison).forEach(([browserType, data]) => { if (data.performance?.domInteractive) { console.log(` ${data.name}: ${Math.round(data.performance.domInteractive)}ms DOM ready`); } }); // Save results this.saveCrossBrowserResults(); return targetMet && criticalFeaturesOk; } /** * Save cross-browser results */ saveCrossBrowserResults() { const resultsDir = 'test-results/cross-browser'; try { if (!fs.existsSync(resultsDir)) { fs.mkdirSync(resultsDir, { recursive: true }); } const resultsFile = `${resultsDir}/compatibility-results.json`; fs.writeFileSync(resultsFile, JSON.stringify(this.results, null, 2)); console.log(`๐Ÿ’พ Cross-browser results saved to: ${resultsFile}`); } catch (error) { console.error(`โŒ Failed to save results: ${error.message}`); } } } /** * Run cross-browser compatibility tests */ async function runCrossBrowserCompatibilityTests() { const testSuite = new CrossBrowserTestSuite(); try { const results = await testSuite.runCrossBrowserTests(); const success = results.compatibility.overallRate >= BROWSER_CONFIG.compatibility.targetCompatibilityRate && results.compatibility.criticalFeaturesOk; if (success) { console.log('\n๐ŸŽ‰ Cross-Browser Compatibility Tests - SUCCESS!'); process.exit(0); } else { console.log('\nโš ๏ธ Cross-Browser Compatibility Tests - FAILED'); process.exit(1); } } catch (error) { console.error('\nโŒ Cross-Browser Compatibility Tests - ERROR:', error.message); process.exit(1); } } // Export for module usage module.exports = { CrossBrowserTestSuite, runCrossBrowserCompatibilityTests, BROWSER_CONFIG, CROSS_BROWSER_TEST_DATA }; // Run if called directly if (require.main === module) { runCrossBrowserCompatibilityTests(); }