import { test, expect } from '@playwright/test'; /** * Horizontal Overflow Tests * * Tests that no production route has accidental horizontal overflow * across all viewport sizes. */ const routes = [ '/', '/portfolio', '/research', '/plans', '/markets', '/screener', '/watchlist', '/alerts', '/settings', ]; test.describe('Horizontal Overflow', () => { routes.forEach((route) => { test(`Route ${route} has no horizontal overflow`, async ({ page }) => { await page.goto(route); // Wait for page to load await page.waitForLoadState('networkidle'); // Check body width vs viewport width const metrics = await page.evaluate(() => { const body = document.body; const viewportWidth = window.innerWidth; const bodyWidth = body.scrollWidth; return { bodyWidth, viewportWidth, overflow: bodyWidth > viewportWidth, overflowAmount: bodyWidth - viewportWidth, }; }); // Allow 1px tolerance for rounding errors expect(metrics.overflowAmount).toBeLessThanOrEqual(1); // Check for any elements causing overflow const overflowElements = await page.evaluate(() => { const elements: Array<{ tag: string; className: string; width: number; scrollWidth: number }> = []; const allElements = document.querySelectorAll('*'); allElements.forEach((el) => { const rect = el.getBoundingClientRect(); const scrollWidth = el.scrollWidth; const clientWidth = el.clientWidth; if (scrollWidth > clientWidth && scrollWidth > window.innerWidth) { elements.push({ tag: el.tagName, className: el.className, width: clientWidth, scrollWidth, }); } }); return elements; }); expect(overflowElements.length).toBe(0); }); }); test('Critical routes at common breakpoints', async ({ page }) => { const breakpoints = [375, 768, 1024, 1200, 1440]; for (const width of breakpoints) { await page.setViewportSize({ width, height: 800 }); await page.goto('/'); const overflowAmount = await page.evaluate(() => { return document.body.scrollWidth - window.innerWidth; }); expect(overflowAmount).toBeLessThanOrEqual(1); } }); });