Testes Automatizados de Acessibilidade

A11Y fazendo parte do seu dia a dia com Jest, aXe e Puppeteer.

Um trio parada dura para ambientes de integração e testes E2E com acessibilidade! 💪 💅

Como desenvolvedores web, sempre tivemos ao nosso lado os padrões web. Tá certo que, para quem já está nesse barco a um tempo, essa afirmação não passa aquela segurança. Mas ó, pelo menos tem alguém tentando :P, tem melhorado ano após ano, ou, olhando por outro lado, ano após ano o navegador vilão e mocinho são alterados! (Estou 👀 para vocês Chrome/IE/Safari).

O que muitas vezes passa despercebido, é que Acessibilidade também é um padrão, e a web é Acessível por padrão, somos nós (e os Designers 👀) que gostamos de "pensar fora da caixa" (se é que esse termo se encaixa aqui).

Nossas soluções atuais

Se tem uma coisa que todas as aplicações tem (ok, nem todas :P) são testes. De unitários a funcionais, o que é importante é extrair valor dos seus testes.

Com a evolução de nossas ferramentas na linguagem JavaScript, hoje temos soluções bem sólidas para rodar testes, e uma delas é o Jest (você pode conferir meu guia sobre Jest aqui).

Também temos uma ferramentas para automatizar testes de acessibilidade em HTML chamada aXe. Que deixa bem simples a integração com diversas ferramentas. Vale lembra, que ele necessita de um navegador real, com sua aplicação rodando, para excutar os testes.

Até aqui, temos o seguinte:

  • Uma ótima plataforma para rodar testes, Jest
  • Uma ótima ferramenta para rodar testes de acessibilidade, aXe

O que está pendente aqui? Um navegador real para rodar os testes!

Como podemos resolver isso?

Resolvendo a última pendência

No final do ano passado, o time do Chrome DevTools lançou uma ferramenta chamada Puppeteer.

Puppeteer é um biblioteca Node, que disponibiliza uma API para controlar uma versão headless (sem a interface gráfica) do Chrome.

Ou seja, você pode rodar o Chrome, através da linha de comando e executar qualquer comando do navegador que você desejar, tais como: gerar PDF, testar eventos, prints de páginas e até mesmo, testes, principalmente os de acessibilidade!

Essa é a peça que faltava para criarmos testes automatizados de alta qualidade para validar a acessibilidade da sua aplicação.

Nosso projeto de exemplo

No exemplo a seguir, iremos abordar a seguinte estrutura:

  • Uma pasta public para o código do nosso exemplo
  • Dentro de public , um arquivo index.html
  • Usaremos jest , jest-axe e puppeteer para criar um novo ambiente de testes para o Jest + Puppeteer e executar a validação padrão do aXe.

Vamos lá:

Iremos iniciar um repositório Git, para acompanhar as mudanças:

Vamos iniciar nosso package.json e adicionar nossas depêndencias:

Vamos adicionar as mudanças ao nosso repositório:

Agora, vamos focar na configuração do Jest. Teremos que alterar 3 ciclos de vida do Jest, e segundo a documentação, a integração é simples.

Configurando Jest e Puppeteer

Essas alterações são relacionadas ao globalSetup , globalTeardown e testEnvironment. Precisamos criar um ambiente diferente para os testes de integração de acessibilidade. Esse ambiente que iremos criar, contém um navegador real, rodando através da linha de comando. Ou seja, isso é um ambiente de testes E2E completo!

Vamos criar, a raíz do nosso projeto, um arquivo jest.config.js

E adicionar:

Vamos analisar passo a passo cada arquivo, mas primeiro, vamos criar a pasta integration e adicionar tudo ao Git.

Agora, vamos abrir integration/config/setup.js e adicionar:

Eita! Quanta coisa acontecendo! Vamos analisar as etapas [A] , [B] e [C] :

  • [A]: Estamos guardando a instância do Puppeteer em global.BROWSER , para podermos fechar o navegador após todos os testes forem realizados no teardown.js
  • [B]: Estamos escrevendo, na pasta tmp do sistema operacional, o endereço do WebSockets do DevTools criado pela etapa [A]. Iremos precisar dele em puppeteer_environment.js , onde iremos conectar ao navegador para realizar os testes
  • [C]: Aqui, um pequeno truque, quando não estivermos usando --watch, iremos criar uma instância do express em global.SERVER apontando para a pasta public . Isso é necessário pois, ao rodar os testes em CI, você não terá seu servidor de desenvolvimento rodando. Iremos ver mais detalhes disso em puppeteer_environment.js

Vamos adicionar express ao package.json e colocar tudo no Git:

Agora, vamos para o próximo arquivo, teardown.js :

Como o próximo nome diz, iremos destruir/limpar toda a bagunça que fizemos. No nosso caso, fechar a instância do navegador, remover a pasta do WebSocket e, caso nossa instância do express exista, fechar o servidor.

Perceba que temos rimraf como nova dependência.

Vamos adicionar isso ao package.json e ao Git:

Vamos voltar nossa atenção ao arquivo puppeteer_environment :

Simple, mas complexo? Vamos analisar as etapas [A] , [B], [C] e [D]:

  • [A]: Estamos criando uma nova classe PuppeteerEnvironment que extende o NodeEnvironment, que é um dos ambientes disponíveis no Jest, estamos usando a dependência jest-envrironment-node para isso. Isso é necessário para não executarmos nehum outro ambiente disponível no Jest (como o jsdom ). Ou seja, esse novo ambiente, é e roda por padrão, em Node.
  • [B]: Aqui é a continuação do truque que vimos em setup.js . Caso você execute os testes de integração com --watch , iremos buscar a aplicação na porta 3000 (normalmente, a porta de desenvolvimento), disponibilizando assim, uma experiência em tempo real para o desenvolvedor detectar os erros de acessibilidade. Caso contrário, iremos procurar a porta da instância express que criamos.
  • [C]: Aqui, outro pequeno truque, estamos guarando a URL em global.ROOT , para facilitar o acesso da URL da aplicação nos testes de integração. Iremos ver mais detalhes disso ao escrever nosso primeiro teste.
  • [D]: Nesse ponto, estamos lendo o endereço de WebSockets do DevTools para podermos realizar a comunicação entre instância e API.

Vamos adicionar nossa nova dependência e colocar tudo no Git:

Ufa 😅… a parte de configuração já passou. Agora, vamos ao divertido!

Nosso primeiro teste e um pouco de HTML!

Lembra que criamos um arquivo HTML em public/index.html ? (É, nem eu, depois de tudo isso de configuração 👽).

Vamos adicionar um pouco de HTML nele, para podermos criar algum teste:

Em public/index.html :

E agora, dentro de integration iremos criar home.test.js e adicionar:

Parece um teste, mas e aí? Curiosidades em [A] , [B], [C] e [D]:

  • [A]: Antes de todos os testes, precisamos conectar o navegador na nossa aplicação. Com os truques que introduzimos na configuração, podemos acessar a instância do navegador e a URL aplicação através do objeto global .
  • [B]: Estamos adicionando, dinamicamente, a URL para o axe-core , assim como demonstrado no repositório oficial. Dessa maneira, você não precisa ficar lembrando de adicionar o script que contém as validações de acessibilidade. axe-core é uma dependência de jest-axe .
  • [C]: Como exemplo de um ambiente completo E2E, aqui estamos avaliando o conteúdo da página e testando se está correto.
  • [D]: É aqui que executamos nosso teste de acessibilidade utilizando a biblioteca aXe. Da mesma maneira que realizamos na interface do navegador. Irado!!1 :) 🎉🎉🎉

Agora, vamos executar nosso teste com:

🔥 Booom! Nosso primeiro teste falha na validação de acessibilidade!

E nossa validação de acessibilidade com aXe já achou um erro e os testes estão em vermelho! O bacana é que o erro é bem amigável, contendo até uma URL para as regras utilizadas pelo aXe com possíveis soluções!

🔥 Acessando a URL mostrada na mensagem de erro, temos essa ótima ajuda sobre o erro em questão!

Antes de resolver esse erro, vamos adicionar tudo ao Git:

Refatorando código para passar nos testes!

Para resolver o problema acima, podemos adicionar um HTML semântico para a regra landmark-one-main , em public/index.html adicione:

E vamos rodar os testes novamente:

Com HTML semântico nossos testes de acessibilidades passam!

Com nosso código arrumado, vamos adicionar ao Git:

Agora não tem desculpa, A11Y todo dia!

Como exercício, vou deixar você encarregado de me mandar um PR para o servidor de desenvolvimento. Assim, podemos executar yarn jest --watch e ter um ambiente de desenvolvimento com testes de integração de acessibilidade ao mesmo tempo.

O repositório desse exemplo você pode encontrar aqui.

Utilizando essa integração, temos certeza estarmos sempre verificando a acessibilidade básica da nossa aplicação, com um simples script na linha de comando, você faz a validação completa da sua aplicação.

Puppeteer é uma solução e tanto. Unindo esse ambiente com Jest, as possibilidades de testes de integração e regressão são enormes! (Por exemplo, você pode usar a API do Puppeteer e tirar print do simulador web mobile do Chrome em N devices e gravar tudo isso com snapshots).

Você leitor, meu muito obrigado! Se quiser me perguntar algo (dúvidas, projetos, etc), pode me mandar uma mensagem no Twitter ou deixar seu comentário abaixo. 🙏 🎉 🐔

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store