/** * Phase 3 mobile — viewport reflow. * * Asserts the layout still works at landscape orientation, a narrow * "small phone" viewport, and a "phablet" viewport. The bottom nav must * remain reachable; the FAB stays centered; no horizontal overflow. */ import { test, expect } from '../../fixtures/test'; const VIEWPORTS = [ { name: 'portrait (default Pixel 7)', width: 412, height: 915 }, { name: 'landscape (Pixel 7 rotated)', width: 915, height: 412 }, { name: 'narrow small phone', width: 320, height: 568 }, { name: 'phablet', width: 480, height: 1024 }, ]; test.describe('Mobile — viewport reflow', () => { for (const vp of VIEWPORTS) { test(`bottom nav remains usable at ${vp.name} (${vp.width}×${vp.height})`, async ({ page, guest, signIn }) => { const g = await guest(`Reflow_${vp.width}x${vp.height}`); await signIn(page, g); await page.setViewportSize({ width: vp.width, height: vp.height }); await page.goto('/feed'); const nav = page.locator('nav').filter({ has: page.getByRole('link', { name: 'Galerie' }) }).first(); const fab = page.getByRole('button', { name: 'Hochladen' }); await expect(nav).toBeVisible(); await expect(fab).toBeVisible(); // No horizontal overflow on . const overflowX = await page.evaluate(() => { const html = document.documentElement; return html.scrollWidth - html.clientWidth; }); expect.soft(overflowX, 'no horizontal overflow').toBeLessThanOrEqual(1); // FAB is roughly centered: its x-mid should be within 30% of the viewport mid. const fabBox = await fab.boundingBox(); if (!fabBox) throw new Error('FAB has no bounding box'); const fabMidX = fabBox.x + fabBox.width / 2; const expectedMid = vp.width / 2; expect.soft(Math.abs(fabMidX - expectedMid)).toBeLessThanOrEqual(vp.width * 0.30); }); } test('rotation portrait → landscape preserves auth + bottom nav', async ({ page, guest, signIn }) => { const g = await guest('Rotate'); await signIn(page, g); await page.goto('/feed'); await expect(page.getByRole('link', { name: 'Galerie' })).toBeVisible(); await page.setViewportSize({ width: 915, height: 412 }); // The same nav should still be visible — no layout shift forces a re-render that loses auth. await expect(page.getByRole('link', { name: 'Galerie' })).toBeVisible(); const stillAuthed = await page.evaluate(() => !!localStorage.getItem('eventsnap_jwt')); expect(stillAuthed).toBe(true); }); });