import { test, expect, Page } from '@playwright/test'; /** * Visual Page Verification Test Suite for HVAC Community Events Plugin * * This test suite focuses on visual verification of all pages without requiring * specific login credentials. It tests: * - Page accessibility and loading * - Visual appearance via screenshots * - Basic page structure and content * - Error handling and redirects */ // Pages that should be accessible without authentication const PUBLIC_PAGES = [ { url: '/training-login/', title: 'Login Page', description: 'Main login page for trainers' } ]; // Pages that require authentication (will show login or access denied) const AUTHENTICATED_PAGES = [ { url: '/trainer/dashboard/', title: 'Trainer Dashboard', description: 'Personal trainer dashboard' }, { url: '/trainer/my-profile/', title: 'Trainer Profile', description: 'Trainer profile management' }, { url: '/trainer/registration/', title: 'Trainer Registration', description: 'New trainer registration' }, { url: '/trainer/documentation/', title: 'Trainer Documentation', description: 'Help and documentation' }, { url: '/trainer/event/manage/', title: 'Manage Event', description: 'Create and edit events' }, { url: '/trainer/event/summary/', title: 'Event Summary', description: 'Event details and attendee management' }, { url: '/trainer/email-attendees/', title: 'Email Attendees', description: 'Send emails to attendees' }, { url: '/trainer/certificate-reports/', title: 'Certificate Reports', description: 'View issued certificates' }, { url: '/trainer/generate-certificates/', title: 'Generate Certificates', description: 'Create new certificates' }, { url: '/trainer/communication-templates/', title: 'Communication Templates', description: 'Manage email templates' }, { url: '/trainer/communication-schedules/', title: 'Communication Schedules', description: 'Schedule automated communications' }, { url: '/trainer/attendee-profile/', title: 'Attendee Profile', description: 'View attendee profiles' }, { url: '/master-trainer/dashboard/', title: 'Master Dashboard', description: 'System-wide analytics and management' }, { url: '/master-trainer/google-sheets/', title: 'Google Sheets Integration', description: 'Google Sheets integration' }, { url: '/master-trainer/certificate-fix/', title: 'Certificate System Diagnostics', description: 'Certificate diagnostics (restricted)' } ]; // Legacy URLs that should redirect const LEGACY_REDIRECTS = [ { from: '/hvac-dashboard/', to: '/trainer/dashboard/', description: 'Dashboard redirect' }, { from: '/manage-event/', to: '/trainer/event/manage/', description: 'Manage event redirect' }, { from: '/trainer-profile/', to: '/trainer/my-profile/', description: 'Profile redirect' }, { from: '/certificate-reports/', to: '/trainer/certificate-reports/', description: 'Certificate reports redirect' }, { from: '/generate-certificates/', to: '/trainer/generate-certificates/', description: 'Generate certificates redirect' } ]; // Helper functions async function takePageScreenshot(page: Page, category: string, pageName: string) { const sanitizedName = pageName.toLowerCase().replace(/[^a-z0-9]/g, '-'); await page.screenshot({ path: `test-results/screenshots/${category}-${sanitizedName}.png`, fullPage: true }); } async function verifyPageAccessibility(page: Page, expectedUrl: string, pageTitle: string) { // Wait for page to load await page.waitForLoadState('networkidle'); // Check page is not showing generic server errors const bodyText = await page.locator('body').textContent(); // Basic checks for major errors expect(bodyText).not.toContain('Fatal error'); expect(bodyText).not.toContain('Parse error'); expect(bodyText).not.toContain('Warning: Cannot modify header'); expect(bodyText).not.toContain('500 Internal Server Error'); // Check for WordPress or plugin content const hasWordPressElements = await page.locator('body[class*="wp-"], #wp-admin-bar, .wp-').isVisible().catch(() => false); const hasPluginElements = await page.locator('[class*="hvac"], [id*="hvac"], [class*="community"], [class*="event"], [class*="trainer"]').isVisible().catch(() => false); const hasContentArea = await page.locator('.entry-content, .content, main, #content, .site-content').isVisible().catch(() => false); // Should have at least some recognizable structure const hasValidStructure = hasWordPressElements || hasPluginElements || hasContentArea; console.log(`Page: ${pageTitle}`); console.log(` - Has WordPress elements: ${hasWordPressElements}`); console.log(` - Has plugin elements: ${hasPluginElements}`); console.log(` - Has content area: ${hasContentArea}`); console.log(` - Valid structure: ${hasValidStructure}`); return { hasValidStructure, hasWordPressElements, hasPluginElements, hasContentArea, bodyText: bodyText || '' }; } test.describe('HVAC Plugin Public Pages', () => { for (const pageConfig of PUBLIC_PAGES) { test(`${pageConfig.title} loads and displays correctly`, async ({ page }) => { await page.goto(pageConfig.url); // Take screenshot for visual verification const screenshotName = pageConfig.title.toLowerCase().replace(/\s+/g, '-'); await takePageScreenshot(page, 'public', screenshotName); // Verify page accessibility const verification = await verifyPageAccessibility(page, pageConfig.url, pageConfig.title); // Public pages should load properly expect(verification.hasValidStructure).toBe(true); // Check URL is correct expect(page.url()).toContain(pageConfig.url); console.log(`✓ Verified ${pageConfig.title} page loads correctly`); }); } }); test.describe('HVAC Plugin Authenticated Pages Response', () => { for (const pageConfig of AUTHENTICATED_PAGES) { test(`${pageConfig.title} page responds appropriately when not logged in`, async ({ page }) => { await page.goto(pageConfig.url); // Take screenshot for visual verification const screenshotName = pageConfig.title.toLowerCase().replace(/\s+/g, '-'); await takePageScreenshot(page, 'auth-required', screenshotName); // Verify page responds (not server error) const verification = await verifyPageAccessibility(page, pageConfig.url, pageConfig.title); // Should either: // 1. Redirect to login page // 2. Show access denied message // 3. Show login form // 4. Load page structure (if auth is handled client-side) const currentUrl = page.url(); const isRedirectedToLogin = currentUrl.includes('/training-login/') || currentUrl.includes('/wp-login'); const showsAccessDenied = verification.bodyText.includes('Access denied') || verification.bodyText.includes('Unauthorized') || verification.bodyText.includes('Please log in') || verification.bodyText.includes('Login required'); const hasLoginForm = await page.locator('input[type="password"], input[name="pwd"], .login-form').isVisible().catch(() => false); // At least one of these should be true for proper auth handling const properAuthHandling = isRedirectedToLogin || showsAccessDenied || hasLoginForm || verification.hasValidStructure; console.log(`Page: ${pageConfig.title}`); console.log(` - Redirected to login: ${isRedirectedToLogin}`); console.log(` - Shows access denied: ${showsAccessDenied}`); console.log(` - Has login form: ${hasLoginForm}`); console.log(` - Has valid structure: ${verification.hasValidStructure}`); console.log(` - Proper auth handling: ${properAuthHandling}`); expect(properAuthHandling).toBe(true); console.log(`✓ Verified ${pageConfig.title} handles authentication properly`); }); } }); test.describe('HVAC Plugin Legacy URL Redirects', () => { for (const redirectConfig of LEGACY_REDIRECTS) { test(`Legacy URL ${redirectConfig.from} redirects properly`, async ({ page }) => { // Navigate to legacy URL await page.goto(redirectConfig.from); // Wait for any redirects to complete await page.waitForLoadState('networkidle'); // Take screenshot of final destination const screenshotName = `legacy-${redirectConfig.from.replace(/[^a-z0-9]/g, '-')}`; await takePageScreenshot(page, 'redirects', screenshotName); // Check if URL changed (redirect occurred) const finalUrl = page.url(); const redirectOccurred = !finalUrl.includes(redirectConfig.from) || finalUrl.includes(redirectConfig.to); // Verify page loads properly after redirect const verification = await verifyPageAccessibility(page, finalUrl, redirectConfig.description); console.log(`Legacy URL: ${redirectConfig.from}`); console.log(` - Final URL: ${finalUrl}`); console.log(` - Redirect occurred: ${redirectOccurred}`); console.log(` - Expected target: ${redirectConfig.to}`); console.log(` - Valid structure: ${verification.hasValidStructure}`); // Should either redirect to target or handle gracefully expect(redirectOccurred).toBe(true); expect(verification.hasValidStructure).toBe(true); console.log(`✓ Verified legacy URL ${redirectConfig.from} redirects properly`); }); } }); test.describe('HVAC Plugin Visual Documentation', () => { test('Generate comprehensive page documentation', async ({ page }) => { // Test all pages for visual documentation const allPages = [...PUBLIC_PAGES, ...AUTHENTICATED_PAGES]; let successfulPages = 0; let totalPages = allPages.length; for (const pageConfig of allPages) { try { await page.goto(pageConfig.url, { timeout: 10000 }); await page.waitForLoadState('networkidle', { timeout: 5000 }); // Take screenshot const screenshotName = pageConfig.title.toLowerCase().replace(/[^a-z0-9]/g, '-'); await takePageScreenshot(page, 'documentation', screenshotName); // Quick verification const verification = await verifyPageAccessibility(page, pageConfig.url, pageConfig.title); if (verification.hasValidStructure) { successfulPages++; console.log(`✓ Documented ${pageConfig.title}`); } else { console.log(`⚠ ${pageConfig.title} may have issues`); } } catch (error) { console.log(`✗ Failed to document ${pageConfig.title}: ${error}`); } } // Report summary console.log(`\n=== DOCUMENTATION SUMMARY ===`); console.log(`Successfully documented: ${successfulPages}/${totalPages} pages`); console.log(`Success rate: ${Math.round((successfulPages/totalPages) * 100)}%`); // At least 70% of pages should be accessible for documentation expect(successfulPages / totalPages).toBeGreaterThan(0.7); console.log('✓ Generated comprehensive visual documentation'); }); }); test.describe('HVAC Plugin Navigation Structure', () => { test('Verify hierarchical URL structure exists', async ({ page }) => { // Test that the hierarchical structure is properly implemented const hierarchicalTests = [ { url: '/trainer/', description: 'Trainer base URL' }, { url: '/trainer/dashboard/', description: 'Trainer dashboard URL' }, { url: '/master-trainer/', description: 'Master trainer base URL' }, { url: '/master-trainer/dashboard/', description: 'Master trainer dashboard URL' } ]; let workingHierarchy = 0; for (const test of hierarchicalTests) { try { await page.goto(test.url, { timeout: 10000 }); await page.waitForLoadState('networkidle', { timeout: 5000 }); const verification = await verifyPageAccessibility(page, test.url, test.description); // Take screenshot const screenshotName = test.description.toLowerCase().replace(/[^a-z0-9]/g, '-'); await takePageScreenshot(page, 'hierarchy', screenshotName); if (verification.hasValidStructure) { workingHierarchy++; console.log(`✓ ${test.description} responds properly`); } else { console.log(`⚠ ${test.description} may have issues`); } } catch (error) { console.log(`✗ ${test.description} failed: ${error}`); } } console.log(`\n=== HIERARCHY VERIFICATION ===`); console.log(`Working hierarchical URLs: ${workingHierarchy}/${hierarchicalTests.length}`); // At least 75% should work expect(workingHierarchy / hierarchicalTests.length).toBeGreaterThan(0.75); console.log('✓ Verified hierarchical URL structure exists'); }); });