PT | EN

Testes automatizados de acessibilidade com Playwright: como começar em um projeto novo e em projetos já existentes

25/08/2026 25/08/2026 19 minutos

A IA já atravessa quase todo o nosso fluxo: escreve o componente, sugere o teste, abre o PR. Com isso, surge uma pergunta que quase ninguém faz: se o agente entrega exatamente o que foi pedido, o que acontece com aquilo que ninguém pediu?

Esse é o caso da acessibilidade, que quase nunca é pedida. Não por má vontade: ela simplesmente não está nos critérios de aceite. O agente gera um clicável com uma fidelidade impressionante, porque foi isso que estava escrito.

A boa notícia é que a mesma automação que ajudou a criar o problema também pode ajudar a resolvê-lo. É possível ampliar a cobertura de testes automatizados com acessibilidade de forma prática e simples: bastam quatro linhas de código para executar o primeiro scan.

Este artigo mostra como aplicar essa abordagem em dois contextos bem diferentes: um projeto novo, usando Spec-Driven Development (SDD), e um projeto que já está em andamento.

Por que a acessibilidade sempre chega atrasada

Ninguém sobe doze andares e depois pergunta onde vai ficar a rampa. Em software, a gente faz isso toda semana. O custo de adicionar acessibilidade depois não é apenas maior, ele é de outra natureza.

Corrigir contraste na UI é trocar um hexadecimal. Corrigir contraste em produção é reabrir o Design System, retestar quarenta telas e negociar prazo com três times. Um vira uma tarefa de vinte minutos, o outro vira uma iniciativa com nome próprio e apresentação em comitê.

Tem também o problema de dono. Acessibilidade adicionada depois não pertence a nenhuma feature e, por isso, vira aquele ticket que atravessa quatro releases intacto, sempre importante e nunca prioritário.

Por fim, tem o pior dos três: acessibilidade remendada raramente é a mesma coisa. Um nativo carrega um contrato inteiro que o navegador implementa de graça.

Ele é anunciado como botão pelo leitor de tela, entra na ordem de tabulação, recebe contorno de foco, responde ao Enter e à barra de espaço, e ainda recebe estilo específico no modo de alto contraste do Windows.

Um com role="button" não recebe nada disso, e você precisa reimplementar cada item na mão, em JavaScript e CSS. O <div role="button"> é o falso amigo da acessibilidade: no diff parece igual, no teclado é outro produto.

O lado bom é que esse custo é uma inclinação, não um muro. A segunda metade deste artigo é sobre como subir essa ladeira sem parar o time.

Duas situações, o mesmo motor

Quase todo material sobre testes automatizados de acessibilidade assume um dos dois extremos. Ou fala com quem está começando do zero, e aí é muito fácil dizer “faça certo desde o início”.

Ou fala com quem tem quatrocentas telas e nenhuma delas navegável por teclado, e aí a recomendação vem no formato de relatório de auditoria com noventa páginas que ninguém executa.

Este texto assume os dois. E a parte interessante é que a ferramenta é a mesma: o mesmo pacote @axe-core/playwright, a mesma fixture, a mesma estrutura de contrato. O que muda são três parâmetros: o que bloqueia o build, o que apenas reporta, e o que é supressão autorizada.

A diferença entre os dois projetos não está no código do teste. Está em quantas violações você consegue olhar facilmente.

Onde o Spec-Driven Development entra nessa história

Se você acompanha o nosso blog, já viu o texto sobre Spec-Driven Development, a abordagem em que especificações estruturadas se tornam a fonte de verdade e guiam a geração de código com apoio de IA.

Vale reler aquele texto com uma lente específica: se a spec é a fonte de verdade e o agente deriva o código dela, então o que não está na spec não existe no produto. Não é falha de atenção do desenvolvedor nem alucinação do modelo. É ausência de requisito, cumprida com precisão.

Ou seja, spec sem acessibilidade não gera código inacessível por acidente. Gera por obediência. Isso muda a economia do problema.

No modelo tradicional, adicionar acessibilidade significa caçar componente, entender quem construiu, descobrir se ainda tem dono. Em Spec-Driven Development, significa editar a spec. Uma linha nos critérios de aceite, antes de existir código.

O contraponto também é verdadeiro, e é o motivo pelo qual este artigo existe: depois que 80% da implementação já foi gerada em cima de uma spec omissa, o custo volta a ser o antigo.

A janela em que acessibilidade é barata é estreita, e ela abre exatamente quando o time acha que ainda não é hora de pensar nisso.

O que o axe pega, e o que ele nunca vai pegar

O motor de verificação usado pela maior parte do mercado é o axe, da Deque, e a documentação de acessibilidade do Playwright dá três exemplos que resumem bem o que ele encontra:

  • Texto com contraste insuficiente em relação ao fundo, difícil de ler para quem tem baixa visão;
  • Controles e campos de formulário sem rótulo que um leitor de tela consiga identificar;
  • Elementos interativos com IDs duplicados, que confundem tecnologia assistiva.

A própria documentação do Playwright abre a seção com um aviso honesto: testes automatizados detectam alguns problemas comuns, como propriedades ausentes ou inválidas, mas muitos problemas só aparecem em teste manual.

A recomendação é combinar automação, avaliação manual e teste com usuários reais.

Na prática, a automação cobre algo em torno da metade dos critérios de sucesso do WCAG, dependendo bastante de como você conta. E essa divisão é a coisa mais importante deste artigo, porque ela organiza todo o resto:

  • Metade é regra: contraste, rótulo, ID duplicado, atributo ausente. Isso o axe verifica sozinho, em milissegundos, sem você escrever nada além da chamada.
  • Metade é comportamento: é possível completar a compra usando só o teclado? O foco fica visível em cada passo? O erro é anunciado para quem não vê a cor vermelha? Depois de fechar o modal, o foco volta para onde estava? Nenhuma regra de axe responde a isso, e nenhuma jamais irá responder.
  • O axe é um ótimo revisor de HTML e um péssimo usuário: ele nunca tenta pagar a compra.

Playwright na prática: três blocos de código

A instalação é uma linha:

npm i -D @axe-core/playwright

O pacote é versionado em conjunto com o axe-core e injeta o motor em todos os frames automaticamente. Não existe passo de injeção manual.

1. O scan da página inteira

import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('a home não tem violações detectáveis automaticamente', async ({ page }) => {
await page.goto('https://seu-site.com/');
const resultados = await new AxeBuilder({ page }).analyze();
expect(resultados.violations).toEqual([]);
});

Em base nova, isso passa. Em base legada, esse toEqual([]) é a linha mais otimista do repositório.

2. Restringindo ao que é WCAG

Por padrão, o axe roda um conjunto amplo de regras, e parte delas é de boa prática, não de critério WCAG. Para verificar apenas o que corresponde a critério de sucesso nível A e AA, use as tags:

const resultados = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
.analyze();

Um detalhe que derruba muita gente: analyze() escaneia a tela no estado em que ela está no momento da chamada. Modal fechado não é escaneado. Se o que você quer verificar aparece depois de um clique, abra, espere o elemento estar presente e só então chame analyze().

3. A fixture que é o pulo do gato

Repetir configuração em trinta arquivos de teste é como repetir qualquer outra coisa em trinta arquivos: funciona por dois meses.

A fixture do Playwright resolve isso, entregando um AxeBuilder já configurado para todos os testes do projeto. É também o lugar onde a política de acessibilidade do time passa a morar de verdade, e é sobre isso que fala o restante do conteúdo.

Trilha A: projeto novo com Spec-Driven Development

Esta seção é curta de propósito, e a brevidade já é parte do argumento.

Em projeto novo com SDD, acessibilidade não é tarefa de implementação. É requisito. Ela entra como uma linha nos critérios de aceite da spec, e o agente gera o com nome acessível porque foi isso que foi pedido. Custo total: uma linha.

A configuração padrão também é diferente, e mais simples: bloqueia todos os impactos. Sem supressões. Sem exclusões. Não existe dívida herdada para negociar, então a primeira violação do projeto será sempre de código escrito ontem, e código de ontem é barato de corrigir.

O momento de fazer isso é antes de qualquer código existir, ainda na análise da spec. É quando definir que “o erro é anunciado como alerta” custa apenas uma frase, e não uma refatoração do Design System.

No modelo de maturidade do texto sobre SDD, isso corresponde ao nível spec-first aplicado a um requisito não funcional. Só que esse preço de uma linha não volta. Todo mês que a feature roda sem cenário de acessibilidade, ele sobe.

Trilha B: projeto que já existe, inclusive legado

Você instala o @axe-core/playwright, roda o primeiro scan na home e o terminal devolve 84 violações. 31 são o mesmo componente de card, repetido trinta e uma vezes.

É um exame de sangue depois das festas de fim de ano. Você já suspeitava, mas ver escrito é diferente.

O erro mais comum a partir daqui é tentar rodar a configuração de projeto novo. A suíte fica vermelha no primeiro dia, permanece vermelha na primeira semana, e na primeira sprint apertada alguém comenta a verificação. Ninguém nunca descomenta.

Existe um caminho melhor, em cinco movimentos.

1. Linha de base antes de política

Rode o scan e não falhe nada. Só registre. O objetivo do primeiro dia é medir o problema, não resolver.

Junto disso, faça o inventário do que já existe: qual o nome acessível de cada controle dos fluxos que importam, se o projeto tem alguma convenção de acessibilidade, se o Design System já resolve parte da questão.

Botão sem nome acessível, aqui, não é “seletor a confirmar”. É divergência, e ela vai virar bug.

2. A catraca

Este é o mecanismo central da trilha, e a documentação do Playwright já traz a técnica na seção sobre lidar com problemas conhecidos.

Em vez de exigir zero violação, você registra o que já existe hoje: o identificador da regra e os seletores dos nós afetados. A partir daí, o teste só falha quando esse conjunto aumenta.

A documentação alerta para não guardar o array de violações inteiro, porque ele carrega o HTML renderizado dos elementos e quebra o teste a cada mudança sem relação nenhuma com acessibilidade.

A suíte não é tribunal, é catraca. Não julga o passado, só impede o retrocesso.

3. Bloqueia critical e serious, reporta moderate e minor

Cada violação que o axe reporta tem um impacto atribuído. Em base existente, a política que funciona é bloquear apenas critical e serious, e registrar moderate e minor como relatório anexo ao teste.

Isso não é frouxidão, é engenharia de adoção. Uma suíte que reprova por 84 violações herdadas não produz rigor, produz ruído. E toda regra impossível de cumprir tem data de validade: sexta-feira, 18h.

4. Supressão com dono e prazo

Vai existir exceção. O que não pode existir é exceção sem prazo.

Toda supressão exige duas informações obrigatórias: o ticket que vai resolver e a data em que ela expira. E a fixture falha de propósito quando o prazo vence, com mensagem explícita pedindo para corrigir a violação ou renovar o acordo. É a diferença entre dívida registrada e dívida esquecida.

Supressão sem prazo é um // TODO de terno.

Vale um cuidado técnico aqui, e ele está explícito na documentação do Playwright. A saída mais óbvia para calar uma violação é excluir o elemento do scan, mas exclude() remove o elemento e todos os seus descendentes, e desliga todas as regras naquele nó, não só a que está falhando.

Uma linha para silenciar um card silencia o card inteiro, para sempre. Filtrar o resultado por regra mais seletor dá o mesmo alívio sem apagar a cobertura.

5. Prioridade por fluxo, não por tela

A documentação do Cypress sobre acessibilidade tem a orientação mais sensata que já li sobre onde parar: garanta que a acessibilidade seja testada explicitamente ao menos uma vez por componente, área ou fluxo.

Depois disso, repetir asserção sobre o mesmo componente em outros testes não adiciona cobertura nenhuma. Em legado, isso é libertador. Você não precisa de quatrocentos scans. Precisa de um por fluxo crítico. Login, busca, checkout, nessa ordem.

O mesmo artigo do Cypress traz um alerta de performance que vale para o Playwright igualmente: cada verificação avalia o DOM elemento por elemento para descobrir quais regras se aplicam, roda as verificações e computa o resultado.

Com poucos scans isso é irrelevante. Com centenas, vira uma parcela considerável do tempo de pipeline, e ela cresce devagar o suficiente para ninguém notar.

E a spec, em projeto que não tem spec?

Projeto legado normalmente não tem especificação nenhuma, e a conclusão natural é que SDD não se aplica. Porém, não é o caso.

Ninguém precisa especificar o sistema inteiro. Especifica-se o trecho que está sendo mexido. Toda feature nova, todo bug corrigido, toda tela refatorada vira uma spec pequena, com a linha de acessibilidade dentro.

É o nível spec-anchored do modelo de maturidade, chegando por baixo em vez de por cima. Em dois trimestres, os fluxos que importam têm spec. O resto do legado continua legado, e está tudo bem.

O workflow que montamos na SoftDesign

O que descrevemos até aqui virou um pacote de skills de QA para agente, não um script nem um plugin. A esteira recebe uma spec, investiga a implementação real, gera cenários, produz um contrato de automação, gera o código Playwright, valida na UI real e emite um parecer final.

O objetivo declarado dela é específico e vale repetir: pegar as divergências entre o que foi pedido e o que a IA entregou. Cada requisito da spec é cruzado com o código e classificado como implementado, divergente, ausente, extra ou não verificável.

Quando o código diverge da spec, o teste é escrito conforme a spec, e a falha esperada é o bug sendo pego. Nunca se ajusta o teste para passar sobre comportamento errado.

A decisão de desenho mais importante para este artigo é o que não fizemos: acessibilidade não ganhou fase própria nem skill própria. Ela entrou como dimensão transversal, do mesmo jeito que segurança.

O motivo? Fase nova significa abrir o navegador duas vezes e criar um gate a mais para o time contornar quando o prazo aperta.

Ela atravessa as fases abaixo:

Fase O que entra 
Contexto Informações relevantes do produto, padrões adotados e requisitos já estabelecidos 
Análise da especificação Identificação dos cenários e comportamentos esperados sob a ótica do negócio 
Contrato Critérios de qualidade, conformidade, regras aplicáveis e pontos de verificação 
Implementação Estruturação dos testes e definição dos pontos de validação necessários 
Validação Execução das verificações previstas e análise dos resultados obtidos 
Revisão Conferência da cobertura dos critérios definidos e aderência ao escopo estabelecido 

Duas características merecem destaque

A primeira é a separação entre comportamento de negócio e validações técnicas. Os cenários devem permanecer claros e compreensíveis para todos os envolvidos no projeto, enquanto regras técnicas, critérios de conformidade e detalhes de implementação ficam registrados nos artefatos apropriados de apoio.

A segunda é que desvios identificados durante a validação devem ser tratados como oportunidades de correção do produto ou do processo. O objetivo não é ajustar os mecanismos de verificação para eliminar o apontamento, mas sim analisar sua causa e garantir que o comportamento esperado esteja sendo atendido.

Há também uma premissa importante: o processo depende de informações mínimas de entrada, como requisitos, especificações ou critérios definidos para o escopo avaliado.

Quando esses insumos não existem, a atividade deixa de ser uma validação orientada por requisitos e passa a exigir uma análise exploratória ou uma avaliação específica do produto. Dessa forma, trata-se de abordagens distintas, com objetivos e entregáveis diferentes.

A metade que vive no Gherkin

Vale ver como fica na prática a metade que o axe não pega. Estes são cenários reais do exemplo que acompanha o pacote, um fluxo de cupom de desconto no checkout:

@acessibilidade @p0-critico
Cenário: Erro de cupom expirado é anunciado como alerta
Quando aplico o cupom "EXPRD01"
Então a mensagem de erro é anunciada como alerta
E consigo alcançar a mensagem sem depender da cor do texto

@acessibilidade @p1-importante
Cenário: Aplicar cupom usando apenas o teclado
Quando preencho e aplico o cupom usando apenas Tab e Enter
Então a linha "Desconto (PRCT10)" deve aparecer no resumo do pedido

E o foco permanece visível em cada elemento percorrido

Nenhuma regra de axe verifica qualquer uma dessas linhas. E note que qualquer pessoa de produto consegue ler e discordar, que é justamente o ponto.

No código, esses cenários viram testes normais de Playwright, e existe um detalhe elegante: getByRole('alert') só encontra o elemento se ele realmente tiver role="alert" ou viver numa região com aria-live.

Ou seja, o locator recomendado do Playwright já é meia asserção de acessibilidade de graça.

Com uma ressalva honesta, que a documentação do Cypress faz bem: localizar um elemento pelo seu papel não prova que ele é acessível. Prova apenas que dá para encontrá-lo.

Um <div role="button"> é encontrado por getByRole('button') exatamente como um <button> de verdade, e continua sem o contrato do navegador. Localizar é uma coisa, verificar comportamento é outra.

Quando isso dá errado

  • Rodar a configuração de projeto novo em base legada: 84 violações no dia um, verificação desligada na sprint seguinte.
  • Ligar a catraca em projeto novo: você acabou de autorizar antecipadamente toda a dívida que ainda não existe.
  • Escrever regra de axe como Gherkin: o resultado é um arquivo .feature que ninguém de produto consegue ler e que, portanto, ninguém de produto revisa.
  • Um scan por teste, sem critério: pipeline mais lento sem cobertura adicional, porque você está verificando o mesmo componente pela quinquagésima vez.
  • Exclusão como resposta padrão para falha: a cobertura só encolhe, e ninguém percebe por que a suíte fica verde.
  • Checkpoint declarado no contrato e ausente no código: você tem documentação, não teste. É o pior dos mundos, porque parece cobertura.

Níveis de adoção

Um lugar para você se reconhecer, e para decidir qual é o próximo passo em vez de tentar todos.

Nível Onde você está 
Ninguém nunca rodou um scan. Talvez esteja tudo bem. Provavelmente não. 
Um scan, um fluxo crítico, bloqueando apenas critical e serious 
Linha de base registrada e catraca ligada: não piora mais 
Checkpoints declarados no contrato e cobrados na revisão de código 
Cenários de teclado, foco e anúncio no .feature, em linguagem de negócio 
Acessibilidade nos critérios de aceite da spec, antes de existir código 

Projeto novo com SDD começa no nível 5. Legado sobe um nível por trimestre, e isso é rápido o suficiente.

E se o seu projeto é Cypress?

O motor é o mesmo. O plugin comunitário cypress-axe integra o axe aos testes e adiciona um comando de verificação que roda o scan no estado atual da página, com configuração para escopo e regras.

Existem plugins construídos em cima dele, como wick-a11y e cypress-a11y-report, além de alternativas com outro motor, como o verificador da IBM.

Para teclado há o comando de tecla nativo, e o Cypress Cloud oferece uma solução paga que move os scans para fora do contexto do teste, resolvendo boa parte da questão de performance.

Ou seja: nada do que está neste texto depende de Playwright. A linha de base, a catraca, a política de impactos, a supressão com prazo e a divisão entre regra e comportamento funcionam igual nos dois.

Conclusão

A Inteligência Artificial acelerou o que sabemos especificar. Acessibilidade não ficou de fora porque é difícil. Ficou de fora porque nunca foi especificada, e agora um agente muito competente cumpre essa omissão em segundos, com uma consistência nunca vista antes.

Escrever a linha na spec é a parte simples. Lembrar de escrever é a parte que precisa de processo.

É para isso que serve colocar acessibilidade dentro do fluxo de QA em vez de em um backlog paralelo: para que a lembrança não dependa de ninguém em particular estar tendo um dia bom.

Em projetos novos, isso custa uma linha. Em projetos que já existem, custa uma catraca e um trimestre. Nos dois casos, custa muito menos do que a versão em que ninguém faz nada.

Perguntas frequentes sobre testes automatizados de acessibilidade

Teste automatizado garante que a aplicação é acessível?

Não. Ele verifica um conjunto conhecido de regras e cobre algo em torno da metade dos critérios do WCAG, dependendo da metodologia de contagem. A recomendação da própria documentação do Playwright é combinar automação, avaliação manual e teste com usuários reais.

Qual a diferença entre scan e cenário de acessibilidade?

O scan verifica regra: contraste, rótulo, ID duplicado. Por outro lado, o cenário verifica comportamento: dá para usar só o teclado, o foco fica visível, o erro é anunciado. Um não substitui o outro e, por isso, eles vivem em lugares diferentes: o scan na configuração técnica e o cenário no arquivo de feature.

Vale a pena adicionar teste de acessibilidade num projeto legado?


Vale, desde que a meta do primeiro mês não seja ficar verde. Seja parar de piorar. Um scan num fluxo crítico, linha de base registrada, bloqueio apenas dos impactos altos.

Como impedir que a suíte fique vermelha para sempre?

Três decisões: registre a linha de base das violações existentes e falhe só quando o conjunto crescer; bloqueie apenas critical e serious; e, por fim, exija ticket mais data de expiração em toda supressão, com a fixture falhando quando o prazo vence.

Preciso de ferramenta paga?

Não para começar. O axe é aberto e a integração com Playwright é um pacote de desenvolvimento. Soluções pagas resolvem principalmente escala e performance, o que é um problema de quem já tem centenas de verificações rodando.

Isso substitui teste com pessoas com deficiência?

Não, e essa é a parte que nenhuma automação vai cobrir. O axe encontra o rótulo ausente. Só uma pessoa usando o produto encontra o fluxo que tecnicamente funciona e na prática é inutilizável.

Paulo Pereira

Paulo Pereira é QA Engineer na SoftDesign há 12 anos, especialista em encontrar problemas antes dos usuários. Pós-graduado em Qualidade de Software pelo SENAC, atua com IA, automação de testes e acessibilidade. Pai da Dandara, divide seu tempo entre bugs, RPGs e animes.