import { test, expect } from "@playwright/test"; import { requireAuth, injectAuth } from "./helpers"; const VIEWPORTS = [ { name: "mobile-sm (375px)", width: 375, height: 812 }, { name: "mobile-lg (430px)", width: 430, height: 932 }, { name: "tablet (768px)", width: 768, height: 1024 }, { name: "desktop (1280px)", width: 1280, height: 800 }, { name: "desktop-xl (1440px)", width: 1440, height: 900 }, ]; const ROUTES = ["/cap", "/leaderboard", "/community", "/settings", "/notifications"]; for (const vp of VIEWPORTS) { test(`no horizontal overflow — ${vp.name}`, async ({ page }) => { requireAuth(); await page.setViewportSize({ width: vp.width, height: vp.height }); await injectAuth(page); for (const route of ROUTES) { await page.goto(route); await page.waitForLoadState("networkidle", { timeout: 15_000 }); const overflow = await page.evaluate(() => { return document.documentElement.scrollWidth > document.documentElement.clientWidth; }); expect(overflow, `Horizontal overflow at ${route} / ${vp.name}`).toBe(false); } }); test(`no interactive elements clipped off-screen — ${vp.name}`, async ({ page }) => { requireAuth(); await page.setViewportSize({ width: vp.width, height: vp.height }); await injectAuth(page); await page.goto("/cap"); await page.waitForLoadState("networkidle", { timeout: 15_000 }); const clipped = await page.evaluate((vpWidth) => { return Array.from(document.querySelectorAll("button, a, input")) .filter((el) => { const r = el.getBoundingClientRect(); return r.right < 0 || r.left > vpWidth + 50; }).length; }, vp.width); expect(clipped, `${clipped} elements clipped at ${vp.name}`).toBe(0); }); }