/** * Enhanced TEC Template Visual Validation Script * * Comprehensive E2E testing with visual validation using DISPLAY=:0 * Tests 100% field population success rate with visual evidence capture * * Usage: DISPLAY=:0 node test-enhanced-tec-visual-validation.js */ const { chromium } = require('playwright'); const fs = require('fs').promises; const path = require('path'); // Test configuration with DISPLAY=:0 visual testing const config = { baseUrl: process.env.UPSKILL_STAGING_URL || 'https://upskill-staging.measurequick.com', timeout: 45000, testCredentials: { username: 'test_trainer', password: 'TestTrainer123!' }, visual: { display: process.env.DISPLAY || ':0', headed: true, slowMo: 1500, // Slow down for visual verification screenshotDir: 'test-results/visual-validation', viewport: { width: 1920, height: 1080 } }, testEventData: { // Core WordPress fields for 100% population test title: 'Enhanced TEC Template Visual Validation Test', content: `

Comprehensive HVAC Training Event - Visual Validation

This event tests the enhanced TEC template system with visual validation and 100% field population success rate verification.

Training Modules:

Requirements: Basic HVAC knowledge, laptop, measurement tools.

`, excerpt: 'Comprehensive HVAC training with enhanced template testing. Covers advanced diagnostics, electrical troubleshooting, and energy efficiency for professional technicians.', // Enhanced taxonomy fields categories: [1, 2], tags: ['HVAC', 'diagnostics', 'training', 'certification', 'visual-test', 'enhanced-template'], // TEC specific fields venue: 'Visual Test Training Center', organizer: 'Enhanced Template Testing Company', start_date: '2025-09-20', start_time: '08:30', end_date: '2025-09-20', end_time: '16:30', cost: '399' } }; console.log('šŸŽÆ Enhanced TEC Template Visual Validation Suite'); console.log('==============================================='); console.log(`🌐 Testing URL: ${config.baseUrl}`); console.log(`šŸ–„ļø Display: ${config.visual.display}`); console.log(`šŸ“‹ Target: 100% field population success rate with visual evidence`); console.log(''); async function runVisualValidationTest() { // Ensure screenshot directory exists await ensureDirectoryExists(config.visual.screenshotDir); console.log(`šŸ–„ļø Launching browser with DISPLAY=${config.visual.display}`); // Check if DISPLAY is properly set and accessible const displayAvailable = process.env.DISPLAY && process.env.DISPLAY !== ''; console.log(`šŸ–„ļø Display available: ${displayAvailable}, DISPLAY=${process.env.DISPLAY}`); const browser = await chromium.launch({ headless: !displayAvailable, // Use headless if display not available slowMo: config.visual.slowMo, args: [ '--no-sandbox', '--disable-dev-shm-usage', '--disable-web-security', '--allow-running-insecure-content', '--disable-gpu', '--disable-dev-shm-usage', '--disable-setuid-sandbox', '--no-first-run', '--no-zygote', '--single-process' ] }); const context = await browser.newContext({ viewport: config.visual.viewport, ignoreHTTPSErrors: true }); const page = await context.newPage(); // Enhanced console logging for visual debugging page.on('console', msg => { if (msg.type() === 'log' && ( msg.text().includes('HVAC') || msg.text().includes('Enhanced') || msg.text().includes('Field Population') || msg.text().includes('Visual') )) { console.log(`šŸ” Browser: ${msg.text()}`); } }); // Track errors for comprehensive reporting const pageErrors = []; page.on('pageerror', error => { pageErrors.push(error.message); console.error(`āŒ Page Error: ${error.message}`); }); const visualTestResults = { screenshots: [], templateVerification: null, fieldPopulation: null, visualInspection: null, formSubmission: null, errors: pageErrors }; try { console.log('šŸ“‹ Step 1: Authentication and Navigation'); await takeScreenshot(page, 'step-1-start', 'Initial browser launch'); // Navigate to login await page.goto(`${config.baseUrl}/training-login/`); await page.waitForLoadState('networkidle'); await takeScreenshot(page, 'step-1-login-page', 'Login page loaded'); // Perform login with visual verification const loginSuccess = await performLoginWithVisualVerification(page, config.testCredentials); if (!loginSuccess) { throw new Error('Login failed - visual verification'); } console.log('šŸ“‹ Step 2: Navigate to Enhanced Event Creation Form'); // Navigate to enhanced event creation form const formUrl = '/events/network/add'; await page.goto(`${config.baseUrl}${formUrl}`); await page.waitForLoadState('networkidle'); await page.waitForTimeout(3000); // Allow enhanced template to load await takeScreenshot(page, 'step-2-form-loaded', 'Enhanced TEC form initial load'); console.log('šŸ“‹ Step 3: Visual Template Verification'); visualTestResults.templateVerification = await performVisualTemplateVerification(page); await takeScreenshot(page, 'step-3-template-verified', 'Template verification complete'); console.log('šŸ“‹ Step 4: Enhanced Field Population Testing'); visualTestResults.fieldPopulation = await performVisualFieldPopulation(page, config.testEventData); await takeScreenshot(page, 'step-4-fields-populated', 'All fields populated for testing'); console.log('šŸ“‹ Step 5: Visual Field Inspection'); visualTestResults.visualInspection = await performVisualFieldInspection(page); await takeScreenshot(page, 'step-5-visual-inspection', 'Visual field inspection complete'); console.log('šŸ“‹ Step 6: Form Submission Preparation'); visualTestResults.formSubmission = await performFormSubmissionPreparation(page); await takeScreenshot(page, 'step-6-submission-ready', 'Form ready for submission'); console.log('šŸ“‹ Step 7: Generate Comprehensive Visual Report'); const visualReport = await generateVisualTestReport(visualTestResults); // Save detailed report await saveVisualTestReport(visualReport); console.log('\nšŸŽ‰ VISUAL VALIDATION TEST COMPLETE'); console.log('='.repeat(50)); console.log(visualReport.summary); return visualReport; } catch (error) { console.error('āŒ Visual validation test failed:', error.message); // Take error screenshot await takeScreenshot(page, 'error-state', `Error: ${error.message}`); throw error; } finally { await browser.close(); } async function takeScreenshot(page, name, description) { try { const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const filename = `${timestamp}_${name}.png`; const filepath = path.join(config.visual.screenshotDir, filename); await page.screenshot({ path: filepath, fullPage: true }); visualTestResults.screenshots.push({ name, description, filename, filepath, timestamp }); console.log(`šŸ“ø Screenshot: ${description} -> ${filename}`); } catch (error) { console.error(`āŒ Screenshot failed: ${error.message}`); } } } async function performLoginWithVisualVerification(page, credentials) { try { console.log('šŸ” Performing login with visual verification...'); // Find and fill login form with visual verification const usernameField = await page.locator('input[name="log"], #user_login, input[type="text"]').first(); const passwordField = await page.locator('input[name="pwd"], #user_pass, input[type="password"]').first(); const submitButton = await page.locator('input[type="submit"], button[type="submit"], .wp-submit').first(); if (await usernameField.count() === 0 || await passwordField.count() === 0) { throw new Error('Login form not found in visual verification'); } // Visual highlighting before filling await usernameField.highlight(); await usernameField.fill(credentials.username); await page.waitForTimeout(500); await passwordField.highlight(); await passwordField.fill(credentials.password); await page.waitForTimeout(500); await submitButton.highlight(); await submitButton.click(); await page.waitForLoadState('networkidle'); await page.waitForTimeout(2000); // Visual verification of login success const loginError = await page.locator('.login_error, .error, #login_error').first(); if (await loginError.count() > 0) { const errorText = await loginError.textContent(); throw new Error(`Login failed in visual verification: ${errorText}`); } console.log('āœ… Login successful - visual verification passed'); return true; } catch (error) { console.error('āŒ Login error in visual verification:', error.message); return false; } } async function performVisualTemplateVerification(page) { console.log('šŸ” Performing visual template verification...'); const templateChecks = { enhanced_indicator: { selector: '.hvac-success-indicator', description: 'Enhanced template success indicator' }, enhanced_form: { selector: '.hvac-tec-enhanced-form', description: 'Enhanced form wrapper' }, excerpt_section: { selector: '.hvac-excerpt-field, #hvac-excerpt-section', description: 'Excerpt field section' }, categories_section: { selector: '.hvac-categories-field, #hvac-categories-section', description: 'Categories field section' }, featured_image_section: { selector: '.hvac-featured-image-field, #hvac-featured-image-section', description: 'Featured image field section' }, tags_section: { selector: '.hvac-tags-field, #hvac-tags-section', description: 'Tags field section' }, enhanced_styles: { selector: '#hvac-tec-enhanced-styles', description: 'Enhanced template styles' } }; const results = {}; let visuallyVerified = 0; for (let [name, check] of Object.entries(templateChecks)) { const element = await page.locator(check.selector).first(); const exists = await element.count() > 0; results[name] = exists; if (exists) { // Visual highlighting for confirmation try { await element.highlight(); await page.waitForTimeout(300); visuallyVerified++; console.log(`āœ… ${check.description}: Found and highlighted`); } catch (e) { console.log(`āœ… ${check.description}: Found (highlight failed)`); } } else { console.log(`āŒ ${check.description}: Not found (${check.selector})`); } } const totalChecks = Object.keys(templateChecks).length; const successRate = Math.round((visuallyVerified / totalChecks) * 100); console.log(`šŸ“Š Visual template verification: ${visuallyVerified}/${totalChecks} elements found and highlighted (${successRate}%)`); return { results, successRate, visuallyVerified, totalChecks }; } async function performVisualFieldPopulation(page, eventData) { console.log('šŸŽÆ Performing visual field population testing...'); // First, check if enhanced field population system is available const enhancedSystemCheck = await page.evaluate(() => { return { available: typeof window.HVACEnhancedFieldPopulation !== 'undefined', debug: window.HVACEnhancedFieldPopulation?.debug || {} }; }); if (!enhancedSystemCheck.available) { console.log('āš ļø Enhanced field population system not found, testing manually...'); return await performManualFieldPopulation(page, eventData); } console.log('āœ… Enhanced field population system found, testing automated population...'); // Run enhanced field population with visual verification const populationResult = await page.evaluate((testData) => { // Run field access test first const accessTest = window.HVACEnhancedFieldPopulation.testFieldAccess(); // Run field population const populationTest = window.HVACEnhancedFieldPopulation.populateAllFields(testData); return { access_test: accessTest, population_test: populationTest, debug_info: window.HVACEnhancedFieldPopulation.debug || {} }; }, eventData); // Visual verification of populated fields await page.waitForTimeout(2000); // Allow population to complete const accessRate = populationResult.access_test?.access_rate || 0; const populationRate = populationResult.population_test?.successRate || 0; console.log(`šŸ” Field Access Rate: ${accessRate}% (${populationResult.access_test?.found_fields}/${populationResult.access_test?.total_fields})`); console.log(`šŸŽÆ Population Success Rate: ${populationRate}% (${populationResult.population_test?.populated_fields}/${populationResult.population_test?.total_fields})`); // Visual confirmation of key fields await performVisualFieldConfirmation(page); return { ...populationResult, visualConfirmation: true, targetAchieved: populationRate === 100 }; } async function performManualFieldPopulation(page, eventData) { console.log('šŸ› ļø Performing manual field population for visual verification...'); const manualFields = [ { selector: 'input[name*="title"], #post-title-0', value: eventData.title, name: 'Title' }, { selector: 'textarea[name*="content"], #post-content-0', value: eventData.content, name: 'Content' }, { selector: '#hvac_post_excerpt', value: eventData.excerpt, name: 'Excerpt' }, { selector: 'input[name*="venue"]', value: eventData.venue, name: 'Venue' }, { selector: 'input[name*="organizer"]', value: eventData.organizer, name: 'Organizer' }, { selector: 'input[name*="cost"]', value: eventData.cost, name: 'Cost' } ]; let populated = 0; const results = []; for (let field of manualFields) { try { const element = await page.locator(field.selector).first(); if (await element.count() > 0) { await element.highlight(); await element.fill(field.value); await page.waitForTimeout(300); populated++; results.push({ field: field.name, success: true }); console.log(`āœ… ${field.name}: Populated and highlighted`); } else { results.push({ field: field.name, success: false, reason: 'Not found' }); console.log(`āŒ ${field.name}: Field not found`); } } catch (error) { results.push({ field: field.name, success: false, reason: error.message }); console.log(`āŒ ${field.name}: Population failed - ${error.message}`); } } const successRate = Math.round((populated / manualFields.length) * 100); console.log(`šŸ“Š Manual field population: ${populated}/${manualFields.length} fields (${successRate}%)`); return { manual: true, successRate, populated_fields: populated, total_fields: manualFields.length, results }; } async function performVisualFieldConfirmation(page) { console.log('šŸ‘ļø Performing visual field confirmation...'); const confirmationFields = [ { selector: 'input[name*="title"], #post-title-0', name: 'Title Field' }, { selector: '#hvac_post_excerpt', name: 'Excerpt Field' }, { selector: '.hvac-categories-field input[type="checkbox"]:checked', name: 'Selected Categories' }, { selector: '.hvac-tags-field .hvac-tag-item', name: 'Added Tags' }, { selector: 'input[name*="venue"]', name: 'Venue Field' }, { selector: 'input[name*="cost"]', name: 'Cost Field' } ]; for (let field of confirmationFields) { try { const elements = await page.locator(field.selector); const count = await elements.count(); if (count > 0) { // Highlight each found element for (let i = 0; i < count; i++) { try { await elements.nth(i).highlight(); await page.waitForTimeout(200); } catch (e) { // Continue if highlight fails } } console.log(`āœ… ${field.name}: ${count} element(s) confirmed visually`); } else { console.log(`āš ļø ${field.name}: No elements found for confirmation`); } } catch (error) { console.log(`āŒ ${field.name}: Confirmation failed - ${error.message}`); } } } async function performVisualFieldInspection(page) { console.log('šŸ” Performing detailed visual field inspection...'); const inspection = { enhancedFields: [], standardFields: [], interactions: [], styling: [] }; // Inspect enhanced fields const enhancedFieldSelectors = [ '.hvac-excerpt-field', '.hvac-categories-field', '.hvac-featured-image-field', '.hvac-tags-field' ]; for (let selector of enhancedFieldSelectors) { const element = await page.locator(selector).first(); if (await element.count() > 0) { try { await element.highlight(); const boundingBox = await element.boundingBox(); const styles = await element.evaluate(el => { const computed = window.getComputedStyle(el); return { display: computed.display, visibility: computed.visibility, backgroundColor: computed.backgroundColor, border: computed.border }; }); inspection.enhancedFields.push({ selector, visible: true, boundingBox, styles }); console.log(`āœ… Enhanced field ${selector}: Visible and styled properly`); } catch (error) { inspection.enhancedFields.push({ selector, visible: false, error: error.message }); console.log(`āŒ Enhanced field ${selector}: Inspection failed`); } } } // Test interactions await testVisualInteractions(page, inspection); return inspection; } async function testVisualInteractions(page, inspection) { console.log('šŸ–±ļø Testing visual interactions...'); // Test category selection try { const categoryCheckbox = await page.locator('.hvac-categories-field input[type="checkbox"]').first(); if (await categoryCheckbox.count() > 0) { await categoryCheckbox.highlight(); await categoryCheckbox.check(); await page.waitForTimeout(500); inspection.interactions.push({ type: 'category_selection', success: true }); console.log('āœ… Category selection interaction: Working'); } } catch (error) { inspection.interactions.push({ type: 'category_selection', success: false, error: error.message }); } // Test tags input try { const tagsInput = await page.locator('#hvac_tags_input').first(); if (await tagsInput.count() > 0) { await tagsInput.highlight(); await tagsInput.fill('visual-test-tag'); await tagsInput.press('Enter'); await page.waitForTimeout(500); inspection.interactions.push({ type: 'tags_addition', success: true }); console.log('āœ… Tags addition interaction: Working'); } } catch (error) { inspection.interactions.push({ type: 'tags_addition', success: false, error: error.message }); } } async function performFormSubmissionPreparation(page) { console.log('šŸ“¤ Preparing form submission test...'); // Find submit button const submitSelectors = [ 'input[type="submit"]', 'button[type="submit"]', '.tribe-events-c-nav__list-item--publish', '#publish', '[name="save"]' ]; let submitButton = null; let submitSelector = null; for (let selector of submitSelectors) { const element = await page.locator(selector).last(); if (await element.count() > 0) { submitButton = element; submitSelector = selector; break; } } if (submitButton) { try { await submitButton.highlight(); console.log(`āœ… Submit button found and highlighted: ${submitSelector}`); console.log('ā„¹ļø Form submission preparation complete (not executing submission in test mode)'); return { ready: true, selector: submitSelector }; } catch (error) { console.log(`āš ļø Submit button found but highlighting failed: ${error.message}`); return { ready: true, selector: submitSelector, highlightError: error.message }; } } else { console.log('āŒ Submit button not found'); return { ready: false }; } } async function generateVisualTestReport(results) { const report = { timestamp: new Date().toISOString(), summary: '', details: results, success: false, fieldPopulationRate: 0, visualVerificationRate: 0, productionReady: false }; // Calculate success rates if (results.templateVerification) { report.visualVerificationRate = results.templateVerification.successRate; } if (results.fieldPopulation) { if (results.fieldPopulation.population_test) { report.fieldPopulationRate = results.fieldPopulation.population_test.successRate; } else if (results.fieldPopulation.successRate) { report.fieldPopulationRate = results.fieldPopulation.successRate; } } // Determine overall success report.success = report.visualVerificationRate >= 80 && report.fieldPopulationRate >= 90; report.productionReady = report.success && results.formSubmission?.ready; // Generate summary let summary = '\nšŸ“Š VISUAL VALIDATION TEST REPORT\n'; summary += '='.repeat(50) + '\n\n'; summary += `šŸŽÆ FIELD POPULATION SUCCESS RATE: ${report.fieldPopulationRate}%\n`; if (report.fieldPopulationRate === 100) { summary += ' šŸŽ‰ TARGET ACHIEVED: 100% field population success!\n'; } summary += `šŸ‘ļø VISUAL VERIFICATION RATE: ${report.visualVerificationRate}%\n`; summary += `šŸ“· SCREENSHOTS CAPTURED: ${results.screenshots.length}\n`; summary += `šŸ–±ļø INTERACTIONS TESTED: ${results.visualInspection?.interactions?.length || 0}\n`; summary += `šŸ“¤ FORM SUBMISSION: ${results.formSubmission?.ready ? 'āœ… READY' : 'āŒ NOT READY'}\n`; summary += `āŒ ERRORS DETECTED: ${results.errors.length}\n`; summary += '\nšŸ† OVERALL RESULT: '; if (report.productionReady) { summary += 'āœ… PRODUCTION READY\n'; summary += 'āœ… Enhanced TEC template fully functional with visual confirmation\n'; summary += 'āœ… 100% field population verified with visual evidence\n'; summary += 'āœ… All interactions tested and working properly\n'; } else if (report.success) { summary += 'āš ļø SUCCESS WITH MINOR ISSUES\n'; summary += 'āœ… Enhanced template working with minor improvements needed\n'; } else { summary += 'āŒ NEEDS IMPROVEMENT\n'; summary += 'āŒ Significant issues detected requiring investigation\n'; } if (results.errors.length > 0) { summary += '\nāŒ PAGE ERRORS:\n'; results.errors.forEach((error, index) => { summary += ` ${index + 1}. ${error}\n`; }); } report.summary = summary; return report; } async function saveVisualTestReport(report) { try { const reportPath = path.join(config.visual.screenshotDir, 'visual-test-report.json'); await fs.writeFile(reportPath, JSON.stringify(report, null, 2)); const summaryPath = path.join(config.visual.screenshotDir, 'visual-test-summary.txt'); await fs.writeFile(summaryPath, report.summary); console.log(`šŸ“„ Visual test report saved: ${reportPath}`); console.log(`šŸ“„ Visual test summary saved: ${summaryPath}`); } catch (error) { console.error(`āŒ Failed to save visual test report: ${error.message}`); } } async function ensureDirectoryExists(dirPath) { try { await fs.access(dirPath); } catch { await fs.mkdir(dirPath, { recursive: true }); console.log(`šŸ“ Created directory: ${dirPath}`); } } // Main execution if (require.main === module) { console.log(`šŸ–„ļø Setting DISPLAY=${config.visual.display} for visual testing`); process.env.DISPLAY = config.visual.display; runVisualValidationTest() .then(report => { console.log('\nāœ… Visual validation test completed successfully'); console.log(`šŸ“Š Field Population Rate: ${report.fieldPopulationRate}%`); console.log(`šŸ‘ļø Visual Verification Rate: ${report.visualVerificationRate}%`); console.log(`šŸŽÆ Production Ready: ${report.productionReady ? 'YES' : 'NO'}`); process.exit(0); }) .catch(error => { console.error('\nāŒ Visual validation test failed:', error.message); process.exit(1); }); } module.exports = { runVisualValidationTest };