diff --git a/docs/plans/barra-navegacao-inferior-mobile-ajustes-impl.md b/docs/plans/barra-navegacao-inferior-mobile-ajustes-impl.md index d1ed76ae..785b47a4 100644 --- a/docs/plans/barra-navegacao-inferior-mobile-ajustes-impl.md +++ b/docs/plans/barra-navegacao-inferior-mobile-ajustes-impl.md @@ -64,7 +64,7 @@ N/A — chrome de navegação, sem dados hexibidos (a intenção já declara que - **Risco:** rótulo 10px quebra acessibilidade percebida. **Mitigação:** é o tamanho do canvas aprovado; rótulos continuam `font-medium`, ícone `size-5` inalterado, alvo de toque pleno; nenhum rótulo some. - **Risco:** `nowrap` + fonte pequena ainda estoura em viewport 320px. **Mitigação:** 10px "Atualizações" ≈ 66px em célula ≥64px (320px/5); o teste e2e novo pina a não-sobreposição em 390px (viewport do suite) e valida `fontSize`; se 320px for exigido depois, é outro item. -- **Risco:** mudança quebra o FAB. **Mitigação:** FAB test (y < nav.y) já cobre; barra cresce ~6px, `bottom-[7rem]` sobra. +- **Risco:** mudança quebra o FAB. **Mitigação:** FAB test (y < nav.y) já cobre; barra cresce ~10px, `bottom-[7rem]` sobra. ## Aceite de engenharia diff --git a/tests/e2e/campaignBottomNav.e2e.spec.ts b/tests/e2e/campaignBottomNav.e2e.spec.ts index 9f528d28..6b92ba47 100644 --- a/tests/e2e/campaignBottomNav.e2e.spec.ts +++ b/tests/e2e/campaignBottomNav.e2e.spec.ts @@ -127,29 +127,41 @@ test.describe('Mobile bottom nav (B164)', () => { await page.goto('/campanha') const bottomNav = page.locator('[aria-label="Navegação principal"]') - await expect(bottomNav).toBeVisible() - // A small breathing gap between the item content and the bar's top border. - const paddingTop = await bottomNav.evaluate((el) => parseFloat(getComputedStyle(el).paddingTop)) - expect(paddingTop).toBeGreaterThanOrEqual(8) + const expectLabelsLegible = async () => { + await expect(bottomNav).toBeVisible() - // Labels render smaller than the original text-xs (12px). - const labels = bottomNav.locator('a > span, button > span') - await expect(labels).toHaveCount(5) - const sizes = await labels.evaluateAll((els) => - els.map((el) => parseFloat(getComputedStyle(el).fontSize)), - ) - for (const fontSize of sizes) expect(fontSize).toBeLessThan(12) + // A small breathing gap between the item content and the bar's top edge + // (pt-2.5 computes to 10px). + const paddingTop = await bottomNav.evaluate((el) => + parseFloat(getComputedStyle(el).paddingTop), + ) + expect(paddingTop).toBeGreaterThanOrEqual(10) - // Adjacent labels must not overlap horizontally on a mobile viewport. - const boxes = await labels.evaluateAll((els) => - els.map((el) => { - const rect = el.getBoundingClientRect() - return { left: rect.left, right: rect.right } - }), - ) - for (let i = 1; i < boxes.length; i++) { - expect(boxes[i].left).toBeGreaterThanOrEqual(boxes[i - 1].right - 0.5) + // Labels render smaller than the original text-xs (12px). + const labels = bottomNav.locator('a > span, button > span') + await expect(labels).toHaveCount(5) + const sizes = await labels.evaluateAll((els) => + els.map((el) => parseFloat(getComputedStyle(el).fontSize)), + ) + for (const fontSize of sizes) expect(fontSize).toBeLessThan(12) + + // Adjacent labels must not overlap horizontally on a mobile viewport. + const boxes = await labels.evaluateAll((els) => + els.map((el) => { + const rect = el.getBoundingClientRect() + return { left: rect.left, right: rect.right } + }), + ) + for (let i = 1; i < boxes.length; i++) { + expect(boxes[i].left).toBeGreaterThanOrEqual(boxes[i - 1].right - 0.5) + } } + + // Default narrow phone (390px) and an extra-narrow one — the acceptance + // promises readable labels on "any mobile viewport", so cover both. + await expectLabelsLegible() + await page.setViewportSize({ width: 320, height: 640 }) + await expectLabelsLegible() }) })