From 9665b8f8030e4492dd92f78f38148a04a08d1ac4 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 31 Aug 2026 17:08:38 +0000 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20aria-current=3D"page"=20nos=20itens?= =?UTF-8?q?=20de=20navega=C3=A7=C3=A3o=20ativos=20(closes=20#118)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sidebar, BottomNav e os links inline do dialog "Menu" marcavam o item ativo só via className, sem nada programaticamente determinável para leitor de tela — os 11+ links se anunciavam idênticos. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01J1hGTYJx2gfTHSDdWFTWTa --- __tests__/unit/a11y-pagina-atual.test.ts | 33 ++++++++++++++++++++++++ components/layout/BottomNav.tsx | 2 ++ components/layout/Sidebar.tsx | 1 + 3 files changed, 36 insertions(+) create mode 100644 __tests__/unit/a11y-pagina-atual.test.ts diff --git a/__tests__/unit/a11y-pagina-atual.test.ts b/__tests__/unit/a11y-pagina-atual.test.ts new file mode 100644 index 0000000..9c7e73d --- /dev/null +++ b/__tests__/unit/a11y-pagina-atual.test.ts @@ -0,0 +1,33 @@ +import { describe, it, expect } from 'vitest' +import { readFileSync } from 'node:fs' +import { join } from 'node:path' + +// ─── Sidebar / BottomNav — item de navegação atual exposto a leitor de tela (#118) ─ +// +// Não há infra de render de componente no projeto (sem jsdom/testing-library). +// O gate possível é sobre o texto-fonte: garantir que `aria-current` esteja +// amarrado à expressão de estado, e que seja `'page'` — não `true` nem um +// literal fixo. `aria-current="page"` fixo marcaria os 11+ links como a +// página atual (pior que o bug original); `aria-current={active}` renderiza +// `aria-current="false"` no item inativo, que ARIA trata como estado +// presente (não ausência) — React só omite `aria-*` para `undefined`/`null`. +// Ambas as correções erradas passariam num `toMatch(/aria-current/)` genérico. + +const sidebar = readFileSync(join(process.cwd(), 'components/layout/Sidebar.tsx'), 'utf-8') +const bottomNav = readFileSync(join(process.cwd(), 'components/layout/BottomNav.tsx'), 'utf-8') + +describe('Sidebar — aria-current amarrado ao estado (#118, site 1)', () => { + it('expõe aria-current="page" no item ativo do NavItem, e undefined nos demais', () => { + expect(sidebar).toMatch(/aria-current=\{active \? 'page' : undefined\}/) + }) +}) + +describe('BottomNav — aria-current amarrado ao estado (#118, sites 2 e 3)', () => { + it('expõe aria-current="page" no NavItem da barra inferior', () => { + expect(bottomNav).toMatch(/aria-current=\{active \? 'page' : undefined\}/) + }) + + it('expõe aria-current="page" nos links inline do dialog "Menu"', () => { + expect(bottomNav).toMatch(/aria-current=\{isActive\(href\) \? 'page' : undefined\}/) + }) +}) diff --git a/components/layout/BottomNav.tsx b/components/layout/BottomNav.tsx index 589b223..947914c 100644 --- a/components/layout/BottomNav.tsx +++ b/components/layout/BottomNav.tsx @@ -63,6 +63,7 @@ function NavItem({
Date: Mon, 31 Aug 2026 19:16:10 +0000 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20ancorar=20asser=C3=A7=C3=B5es=20de?= =?UTF-8?q?=20aria-current=20na=20linha=20inteira=20(#118)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit toMatch(/aria-current=.../) sem âncora passava com o atributo comentado, deixando os 22 links de navegação voltar a se anunciar idênticos sem que o gate acusasse — mesmo modo de falha que a11y-estado-selecao.test.ts (#107) já evita para aria-pressed. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01J1hGTYJx2gfTHSDdWFTWTa --- __tests__/unit/a11y-pagina-atual.test.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/__tests__/unit/a11y-pagina-atual.test.ts b/__tests__/unit/a11y-pagina-atual.test.ts index 9c7e73d..79c7e19 100644 --- a/__tests__/unit/a11y-pagina-atual.test.ts +++ b/__tests__/unit/a11y-pagina-atual.test.ts @@ -12,22 +12,27 @@ import { join } from 'node:path' // `aria-current="false"` no item inativo, que ARIA trata como estado // presente (não ausência) — React só omite `aria-*` para `undefined`/`null`. // Ambas as correções erradas passariam num `toMatch(/aria-current/)` genérico. +// As asserções ancoram na linha inteira (`^\s*...$` com flag `m`), não em +// qualquer ocorrência do arquivo — senão o atributo comentado deixaria o +// teste verde com os 22 links de volta a se anunciar idênticos, mesmo modo +// de falha que `a11y-estado-selecao.test.ts` (#107) documenta para +// `aria-pressed`. const sidebar = readFileSync(join(process.cwd(), 'components/layout/Sidebar.tsx'), 'utf-8') const bottomNav = readFileSync(join(process.cwd(), 'components/layout/BottomNav.tsx'), 'utf-8') describe('Sidebar — aria-current amarrado ao estado (#118, site 1)', () => { it('expõe aria-current="page" no item ativo do NavItem, e undefined nos demais', () => { - expect(sidebar).toMatch(/aria-current=\{active \? 'page' : undefined\}/) + expect(sidebar).toMatch(/^\s*aria-current=\{active \? 'page' : undefined\}$/m) }) }) describe('BottomNav — aria-current amarrado ao estado (#118, sites 2 e 3)', () => { it('expõe aria-current="page" no NavItem da barra inferior', () => { - expect(bottomNav).toMatch(/aria-current=\{active \? 'page' : undefined\}/) + expect(bottomNav).toMatch(/^\s*aria-current=\{active \? 'page' : undefined\}$/m) }) it('expõe aria-current="page" nos links inline do dialog "Menu"', () => { - expect(bottomNav).toMatch(/aria-current=\{isActive\(href\) \? 'page' : undefined\}/) + expect(bottomNav).toMatch(/^\s*aria-current=\{isActive\(href\) \? 'page' : undefined\}$/m) }) }) From ff043918018d7e4ca7ab03965e8691ed0b46df95 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 4 Sep 2026 19:22:59 +0000 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20ancorar=20asser=C3=A7=C3=B5es=20de?= =?UTF-8?q?=20aria-current=20tamb=C3=A9m=20no=20elemento=20(#118)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A âncora de linha (f50ed9e) protegia contra o atributo comentado, mas não amarrava aria-current ao — o mesmo atributo movido para um filho (a
/ que já carrega o destaque visual do item ativo) mantinha as três asserções verdes com o sem aria-current na árvore, a mesma classe de correção errada que a família #126/#127/#128 nomeia. [^<]*, não [^>]*: o do dialog "Menu" tem onClick={() => {...}} na tag de abertura, e a seta => contém um > que faria [^>]* parar cedo demais e reprovar o fonte correto. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01J1hGTYJx2gfTHSDdWFTWTa --- __tests__/unit/a11y-pagina-atual.test.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/__tests__/unit/a11y-pagina-atual.test.ts b/__tests__/unit/a11y-pagina-atual.test.ts index 79c7e19..ce0eb10 100644 --- a/__tests__/unit/a11y-pagina-atual.test.ts +++ b/__tests__/unit/a11y-pagina-atual.test.ts @@ -16,23 +16,32 @@ import { join } from 'node:path' // qualquer ocorrência do arquivo — senão o atributo comentado deixaria o // teste verde com os 22 links de volta a se anunciar idênticos, mesmo modo // de falha que `a11y-estado-selecao.test.ts` (#107) documenta para -// `aria-pressed`. +// `aria-pressed`. Ancoram também no elemento (`` (ex: a `
`/`` +// que já carrega o destaque visual do item ativo) mantém a asserção verde +// com o `` sem `aria-current` na árvore, a mesma classe de correção +// errada que a família #126/#127/#128 nomeia como "aria-label no ícone em +// vez do botão". `[^<]*`, não `[^>]*`: o `` do dialog "Menu" tem +// `onClick={() => {...}}` na tag de abertura, e a seta `=>` contém um `>` +// que faria `[^>]*` parar cedo demais e reprovar o fonte correto. const sidebar = readFileSync(join(process.cwd(), 'components/layout/Sidebar.tsx'), 'utf-8') const bottomNav = readFileSync(join(process.cwd(), 'components/layout/BottomNav.tsx'), 'utf-8') describe('Sidebar — aria-current amarrado ao estado (#118, site 1)', () => { it('expõe aria-current="page" no item ativo do NavItem, e undefined nos demais', () => { - expect(sidebar).toMatch(/^\s*aria-current=\{active \? 'page' : undefined\}$/m) + expect(sidebar).toMatch(/ { it('expõe aria-current="page" no NavItem da barra inferior', () => { - expect(bottomNav).toMatch(/^\s*aria-current=\{active \? 'page' : undefined\}$/m) + expect(bottomNav).toMatch(/ { - expect(bottomNav).toMatch(/^\s*aria-current=\{isActive\(href\) \? 'page' : undefined\}$/m) + expect(bottomNav).toMatch( + /