import { test, expect } from '../fixtures'; /** * Visual Regression Tests * Uses Playwright's screenshot comparison for UI consistency * Targets: Component-level and page-level visual testing */ test.describe('Visual Regression @visual @critical', () => { test.describe('Dashboard Visual Tests', () => { test('dashboard page should match baseline', async ({ authenticatedPage }) => { await authenticatedPage.goto('/dashboard'); await authenticatedPage.waitForLoadState('networkidle'); await expect(authenticatedPage).toHaveScreenshot('dashboard.png', { fullPage: true, maxDiffPixelRatio: 0.02, }); }); test('dashboard dark mode should match baseline', async ({ authenticatedPage }) => { await authenticatedPage.goto('/dashboard'); // Switch to dark mode await authenticatedPage.click('[data-testid="theme-toggle"]'); await authenticatedPage.waitForTimeout(500); // Wait for theme transition await expect(authenticatedPage).toHaveScreenshot('dashboard-dark.png', { fullPage: true, maxDiffPixelRatio: 0.02, }); }); test('dashboard empty state should match baseline', async ({ authenticatedPage }) => { // Clear all scenarios first await authenticatedPage.evaluate(() => { // Mock empty state localStorage.setItem('mock-empty-dashboard', 'true'); }); await authenticatedPage.goto('/dashboard'); await authenticatedPage.waitForLoadState('networkidle'); await expect(authenticatedPage).toHaveScreenshot('dashboard-empty.png', { fullPage: true, maxDiffPixelRatio: 0.02, }); }); }); test.describe('Scenarios List Visual Tests', () => { test('scenarios list page should match baseline', async ({ authenticatedPage, testData }) => { // Create some test scenarios await Promise.all([ testData.createScenario({ name: 'Visual Test 1', region: 'us-east-1', tags: ['visual'] }), testData.createScenario({ name: 'Visual Test 2', region: 'eu-west-1', tags: ['visual'] }), testData.createScenario({ name: 'Visual Test 3', region: 'ap-south-1', tags: ['visual'] }), ]); await authenticatedPage.goto('/scenarios'); await authenticatedPage.waitForLoadState('networkidle'); await expect(authenticatedPage).toHaveScreenshot('scenarios-list.png', { fullPage: true, maxDiffPixelRatio: 0.02, }); }); test('scenarios list mobile view should match baseline', async ({ page, testData }) => { // Set mobile viewport await page.setViewportSize({ width: 375, height: 667 }); await page.goto('/scenarios'); await page.waitForLoadState('networkidle'); await expect(page).toHaveScreenshot('scenarios-list-mobile.png', { fullPage: true, maxDiffPixelRatio: 0.03, }); }); }); test.describe('Scenario Detail Visual Tests', () => { test('scenario detail page should match baseline', async ({ authenticatedPage, testData }) => { const scenario = await testData.createScenario({ name: 'Visual Detail Test', region: 'us-east-1', tags: ['visual-test'], }); await testData.addScenarioLogs(scenario.id, 10); await authenticatedPage.goto(`/scenarios/${scenario.id}`); await authenticatedPage.waitForLoadState('networkidle'); await expect(authenticatedPage).toHaveScreenshot('scenario-detail.png', { fullPage: true, maxDiffPixelRatio: 0.02, }); }); test('scenario detail charts should match baseline', async ({ authenticatedPage, testData }) => { const scenario = await testData.createScenario({ name: 'Chart Visual Test', region: 'us-east-1', tags: [], }); await testData.addScenarioLogs(scenario.id, 50); await authenticatedPage.goto(`/scenarios/${scenario.id}`); await authenticatedPage.click('[data-testid="charts-tab"]'); await authenticatedPage.waitForTimeout(1000); // Wait for charts to render // Screenshot specific chart area const chart = authenticatedPage.locator('[data-testid="cost-breakdown-chart"]'); await expect(chart).toHaveScreenshot('cost-breakdown-chart.png', { maxDiffPixelRatio: 0.05, // Higher tolerance for charts }); }); }); test.describe('Forms Visual Tests', () => { test('create scenario form should match baseline', async ({ authenticatedPage }) => { await authenticatedPage.goto('/scenarios/new'); await authenticatedPage.waitForLoadState('networkidle'); await expect(authenticatedPage).toHaveScreenshot('create-scenario-form.png', { fullPage: true, maxDiffPixelRatio: 0.02, }); }); test('create scenario form with validation errors should match baseline', async ({ authenticatedPage }) => { await authenticatedPage.goto('/scenarios/new'); await authenticatedPage.click('[data-testid="create-scenario-button"]'); await expect(authenticatedPage).toHaveScreenshot('create-scenario-form-errors.png', { fullPage: true, maxDiffPixelRatio: 0.02, }); }); test('login form should match baseline', async ({ page }) => { await page.goto('/login'); await page.waitForLoadState('networkidle'); await expect(page).toHaveScreenshot('login-form.png', { fullPage: true, maxDiffPixelRatio: 0.02, }); }); }); test.describe('Comparison Visual Tests', () => { test('comparison page should match baseline', async ({ authenticatedPage, testData }) => { const scenarios = await Promise.all([ testData.createScenario({ name: 'Compare A', region: 'us-east-1', tags: [] }), testData.createScenario({ name: 'Compare B', region: 'eu-west-1', tags: [] }), ]); await testData.addScenarioLogs(scenarios[0].id, 100); await testData.addScenarioLogs(scenarios[1].id, 50); await authenticatedPage.goto(`/compare?scenarios=${scenarios[0].id},${scenarios[1].id}`); await authenticatedPage.waitForLoadState('networkidle'); await authenticatedPage.waitForTimeout(1000); // Wait for charts await expect(authenticatedPage).toHaveScreenshot('comparison-view.png', { fullPage: true, maxDiffPixelRatio: 0.03, }); }); }); test.describe('Reports Visual Tests', () => { test('reports list page should match baseline', async ({ authenticatedPage, testData }) => { const scenario = await testData.createScenario({ name: 'Reports Visual', region: 'us-east-1', tags: [], }); await testData.createReport(scenario.id, 'pdf'); await testData.createReport(scenario.id, 'csv'); await authenticatedPage.goto(`/scenarios/${scenario.id}/reports`); await authenticatedPage.waitForLoadState('networkidle'); await expect(authenticatedPage).toHaveScreenshot('reports-list.png', { fullPage: true, maxDiffPixelRatio: 0.02, }); }); }); test.describe('Components Visual Tests', () => { test('stat cards should match baseline', async ({ authenticatedPage, testData }) => { const scenario = await testData.createScenario({ name: 'Stat Card Test', region: 'us-east-1', tags: [], }); await testData.addScenarioLogs(scenario.id, 100); await authenticatedPage.goto(`/scenarios/${scenario.id}`); const statCards = authenticatedPage.locator('[data-testid="stat-cards"]'); await expect(statCards).toHaveScreenshot('stat-cards.png', { maxDiffPixelRatio: 0.02, }); }); test('modal dialogs should match baseline', async ({ authenticatedPage }) => { await authenticatedPage.goto('/scenarios'); // Open delete confirmation modal await authenticatedPage.click('[data-testid="delete-scenario-button"]').first(); const modal = authenticatedPage.locator('[data-testid="confirm-modal"]'); await expect(modal).toBeVisible(); await expect(modal).toHaveScreenshot('confirm-modal.png', { maxDiffPixelRatio: 0.02, }); }); }); test.describe('Error Pages Visual Tests', () => { test('404 page should match baseline', async ({ authenticatedPage }) => { await authenticatedPage.goto('/non-existent-page'); await authenticatedPage.waitForLoadState('networkidle'); await expect(authenticatedPage).toHaveScreenshot('404-page.png', { fullPage: true, maxDiffPixelRatio: 0.02, }); }); test('loading state should match baseline', async ({ authenticatedPage }) => { await authenticatedPage.goto('/scenarios'); // Intercept and delay API call await authenticatedPage.route('**/api/v1/scenarios', async (route) => { await new Promise(resolve => setTimeout(resolve, 5000)); await route.continue(); }); await authenticatedPage.reload(); const loadingState = authenticatedPage.locator('[data-testid="loading-skeleton"]'); await expect(loadingState).toBeVisible(); await expect(loadingState).toHaveScreenshot('loading-state.png', { maxDiffPixelRatio: 0.02, }); }); }); });