import { test, expect, Page } from '@playwright/test'; import { loginAsUser, TEST_CONFIG } from '../utils/test-helpers'; /** * UI/UX Dynamic Audit Suite * Scans pages for: * - Interactive elements that are too small (< 44x44px) * - Console errors * - Broken images/links * - Overflow issues */ const PAGES_TO_AUDIT = [ '/dashboard', '/library', '/marketplace', '/settings', '/profile/me', '/studio', '/messages' // Often problematic ]; test.describe('Dynamic UI/UX Audit', () => { let consoleErrors: string[] = []; test.beforeEach(async ({ page }) => { // Capture console errors page.on('console', msg => { if (msg.type() === 'error') consoleErrors.push(`[${page.url()}] ${msg.text()}`); }); // Login once await loginAsUser(page); }); for (const path of PAGES_TO_AUDIT) { test(`Audit page: ${path}`, async ({ page }) => { console.log(`\nšŸ” Auditing ${path}...`); await page.goto(`${TEST_CONFIG.FRONTEND_URL}${path}`); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); // Allow animations/layout to settle // 1. Check for Console Errors if (consoleErrors.length > 0) { console.log(`āš ļø Console Errors on ${path}:`, consoleErrors); consoleErrors = []; // Reset for next check logic (though beforeeach resets too, strictly speaking this is shared scope in this loop impl if not careful, but playwright isolates tests) } // 2. Interactive Element Sizing (Mobile Friendly Check) // Find all buttons and anchors const interactiveElements = await page.locator('button:visible, a:visible, [role="button"]:visible').all(); let smallTargets = 0; for (const el of interactiveElements) { const box = await el.boundingBox(); if (box) { // Check if smaller than 44px in either dimension (Apple guidelines) // We allow smaller if it's strictly an icon-only button inside a toolbar, but warn generally if (box.width < 32 || box.height < 32) { // 32 is lenient, 44 is ideal const html = await el.evaluate(e => e.outerHTML); // console.log(`āš ļø Small touch target (${Math.round(box.width)}x${Math.round(box.height)}):`, html.substring(0, 100)); smallTargets++; } } } if (smallTargets > 0) { console.log(`āš ļø Found ${smallTargets} interactive elements smaller than 32x32px on ${path}`); } // 3. Overflow Detection const hasHorizontalScroll = await page.evaluate(() => { return document.body.scrollWidth > window.innerWidth; }); if (hasHorizontalScroll) { console.log(`šŸ”“ Layout Issue: Horizontal scroll detected on ${path}`); } // 4. Broken Image Detection const images = await page.locator('img').all(); for (const img of images) { const isBroken = await img.evaluate((i: HTMLImageElement) => { return !i.complete || i.naturalWidth === 0; }); if (isBroken) { const src = await img.getAttribute('src'); console.log(`šŸ”“ Broken Image: ${src}`); } } console.log(`āœ… Audit complete for ${path}`); }); } });