import AxeBuilder from '@axe-core/playwright' import { expect, test } from '@playwright/test' import { loadFixtures } from './fixtures' const fixtures = loadFixtures() const pages = [ { name: 'Home', path: '/' }, { name: 'RunList', path: '/runs' }, { name: 'NewRun', path: '/runs/new' }, { name: 'RunEncounters', path: `/runs/${fixtures.runId}` }, { name: 'GenlockeList', path: '/genlockes' }, { name: 'NewGenlocke', path: '/genlockes/new' }, { name: 'GenlockeDetail', path: `/genlockes/${fixtures.genlockeId}` }, { name: 'Stats', path: '/stats' }, { name: 'AdminGames', path: '/admin/games' }, { name: 'AdminPokemon', path: '/admin/pokemon' }, { name: 'AdminEvolutions', path: '/admin/evolutions' }, ] const viewports = [ { name: 'mobile', width: 375, height: 667 }, { name: 'tablet', width: 768, height: 1024 }, { name: 'desktop', width: 1280, height: 800 }, ] as const for (const viewport of viewports) { test.describe(`Mobile layout — ${viewport.name} (${viewport.width}x${viewport.height})`, () => { test.use({ viewport: { width: viewport.width, height: viewport.height }, }) for (const { name, path } of pages) { test(`${name} (${path}) has no overflow or touch target issues`, async ({ page }) => { await page.goto(path, { waitUntil: 'networkidle' }) // Assert no horizontal overflow const overflow = await page.evaluate(() => ({ scrollWidth: document.documentElement.scrollWidth, innerWidth: window.innerWidth, })) expect( overflow.scrollWidth, `${name} at ${viewport.name}: horizontal overflow detected (scrollWidth=${overflow.scrollWidth}, innerWidth=${overflow.innerWidth})`, ).toBeLessThanOrEqual(overflow.innerWidth) // Run axe-core target-size rule for touch target validation const axeResults = await new AxeBuilder({ page }) .withRules(['target-size']) .analyze() const violations = axeResults.violations.map((v) => ({ id: v.id, impact: v.impact, nodes: v.nodes.length, })) expect( violations, `${name} at ${viewport.name}: ${violations.length} touch target violations:\n${JSON.stringify(violations, null, 2)}`, ).toHaveLength(0) // Capture full-page screenshot await page.screenshot({ path: `e2e/screenshots/${viewport.name}/${name.toLowerCase()}.png`, fullPage: true, }) }) } }) }