/** * Phase 3 mobile — touch target sizing audit. * * Apple HIG says ≥ 44×44 pt. Material says ≥ 48×48 dp. We assert ≥ 44 px * on critical interactive elements at mobile viewports (the device * descriptor used by this project sets `deviceScaleFactor` but * `boundingBox` returns CSS pixels, so the threshold is in CSS px). * * Each test grabs the bounding box for the locator and asserts both * dimensions are ≥ 44. Fails surface with the actual sizing so the * mismatch is obvious. */ import { test, expect } from '../../fixtures/test'; const MIN_TOUCH = 44; async function assertTouchTarget(box: { width: number; height: number } | null, name: string) { if (!box) throw new Error(`${name} not visible — no bounding box`); expect.soft(box.width, `${name} width ≥ ${MIN_TOUCH}px (got ${box.width})`).toBeGreaterThanOrEqual(MIN_TOUCH); expect.soft(box.height, `${name} height ≥ ${MIN_TOUCH}px (got ${box.height})`).toBeGreaterThanOrEqual(MIN_TOUCH); } test.describe('Mobile — touch target audit', () => { test('bottom nav links and FAB are ≥ 44×44 px on /feed', async ({ page, guest, signIn }) => { const g = await guest('Touchy'); await signIn(page, g); await page.goto('/feed'); const galerie = page.getByRole('link', { name: 'Galerie' }); const fab = page.getByRole('button', { name: 'Hochladen' }); const konto = page.getByRole('link', { name: 'Konto' }); await expect(galerie).toBeVisible(); await assertTouchTarget(await galerie.boundingBox(), 'Galerie nav link'); await assertTouchTarget(await fab.boundingBox(), 'Upload FAB'); await assertTouchTarget(await konto.boundingBox(), 'Konto nav link'); }); test('join submit and PIN-modal buttons are ≥ 44×44 px', async ({ page }) => { await page.goto('/join'); const submit = page.getByTestId('join-submit'); await assertTouchTarget(await submit.boundingBox(), 'Join submit button'); }); test('admin login submit button is ≥ 44×44 px', async ({ page }) => { await page.goto('/admin/login'); const submit = page.getByTestId('admin-login-submit'); await assertTouchTarget(await submit.boundingBox(), 'Admin login submit'); }); test('PIN copy + continue buttons in the PIN modal are ≥ 44×44 px', async ({ page }) => { await page.goto('/join'); await page.getByTestId('join-name-input').fill('TouchTargetPinModal'); await page.getByTestId('join-submit').click(); await expect(page.getByTestId('pin-modal')).toBeVisible(); await assertTouchTarget(await page.getByTestId('pin-copy').boundingBox(), 'PIN copy button'); await assertTouchTarget(await page.getByTestId('continue-to-feed').boundingBox(), 'Continue-to-feed button'); }); });