diff --git a/apps/pwa/e2e/layout.spec.ts b/apps/pwa/e2e/layout.spec.ts index 4e4e490..5f382bd 100644 --- a/apps/pwa/e2e/layout.spec.ts +++ b/apps/pwa/e2e/layout.spec.ts @@ -199,6 +199,40 @@ test.describe('Rule 2 — No horizontal overflow', () => { }); }); +// ── D-01 Regression Guard: FAB does not overlap BottomTabBar (Workstream A) ── +// +// Permanent overlap assertion for the seed defect: New Event FAB was landing on the +// Admin tab at 390×844, clipping the color-legend chips behind the BottomTabBar. +// Fixed by calc(var(--bottom-chrome-h) + var(--space-6)) in CalendarShell.tsx. +// Runs on iphone + pixel (the profiles that exposed the issue); skipped on desktop. + +test.describe('D-01 regression guard — FAB does not overlap BottomTabBar', () => { + test('New Event FAB does not overlap BottomTabBar (A — phone only)', async ({ + page, + }, testInfo) => { + test.skip(testInfo.project.name === 'desktop', 'Phone-only assertion'); + + await page.goto('/calendar'); + + const fab = page.getByRole('button', { name: 'New Event' }); + const nav = page.getByRole('navigation', { name: 'Main navigation' }); + + await expect(fab).toBeVisible(); + await expect(nav).toBeVisible(); + + const fabBox = await fab.boundingBox(); + const navBox = await nav.boundingBox(); + + expect(fabBox, 'New Event FAB bounding box must not be null').not.toBeNull(); + expect(navBox, 'BottomTabBar bounding box must not be null').not.toBeNull(); + + expect( + fabBox!.y + fabBox!.height, + `FAB bottom edge (${fabBox!.y + fabBox!.height}) must be ≤ BottomTabBar top edge (${navBox!.y}) — no overlap`, + ).toBeLessThanOrEqual(navBox!.y); + }); +}); + // ── Harness self-validation — injected-defect proofs (TEST-01 acceptance bar) ── // // Each test is a PASSING test that proves the assertion would have failed under a