Como faço testes automatizados de acessibilidade para meu site
Acompanhe este exemplo de realização de testes de acessibilidade no GitLab com Pa11y e Cypress em um site Jekyll.
Este artigo aborda a adição de testes de acessibilidade ao seu site usando Pa11y (pa11y-ci com axe) e Cypress (com cypress-axe) no GitLab CI/CD. Eu uso um site Jekyll como exemplo, mas qualquer tecnologia de site executada em CI/CD pode aproveitar essa configuração.
Prepare seu site
Além de fazer seu site rodar em CI/CD, recomendo habilitar um recurso de mapa de site XML. Um mapa do site permite que os testes de acessibilidade analisem todos os URLs para encontrar problemas de acessibilidade no site. Eu recomendo o plugin jekyll-sitemap para sites Jekyll.
Coletar uma lista de todos os URLs principais é uma boa alternativa se um mapa do site não for possível. Os URLs devem cobrir todos os layouts potenciais do site, como páginas com maior tráfego ou mais destinos. Essa abordagem não resolverá todos os problemas de acessibilidade, especialmente questões de nível de conteúdo, mas testará o layout e as páginas principais.
Este cenário requer os gerenciadores de pacotes npm ou yarn. Usei npm
para este artigo. Se o seu projeto não tiver npm
inicializado, execute o comando npm init
para criar o arquivo package.json
.
Comece com Pa11y
Pa11y é um software gratuito e de código aberto que testa sites quanto a problemas de acessibilidade. Pa11y-ci é o utilitário de linha de comando voltado para integração contínua (CI). Instale pa11y-ci como uma dependência de desenvolvimento com npm
:
$ npm i --save-dev pa11y-ci
Após concluir a instalação, edite package.json
e adicione os seguintes comandos à seção scripts:
"start-detached": "bundle exec jekyll serve --detach",
"pa11y-ci:home": "pa11y-ci http://127.0.0.1:4000",
"pa11y-ci:sitemap": "pa11y-ci --sitemap http://127.0.0.1:4000/sitemap.xml --sitemap-find https://accessibility.civicactions.com --sitemap-replace http://127.0.0.1:4000 --sitemap-exclude \"/*.pdf\""
- start-detached: Inicia o servidor web que executará o Jekyll para teste.
- pa11y-ci:home: Executa testes pa11y-ci na página inicial. Útil para solução de problemas.
- pa11y-ci:sitemap: executa testes pa11y-ci usando o mapa do site e exclui PDFs. O mapa do site se referirá aos URLs ativos do site, portanto, substitua-os por URLs locais para teste no pipeline de CI.
Adicione um arquivo JSON chamado .pa11yci
que configura pa11y-ci com várias opções. Aqui está um arquivo de exemplo:
{
"defaults": {
"concurrency": 1,
"standard": "WCAG2AA",
"runners": ["axe", "htmlcs"],
"ignore": [
"color-contrast",
"frame-tested"
],
"chromeLaunchConfig": {
"args": ["--disable-dev-shm-usage", "--no-sandbox", "--disable-gpu"]
},
"reporters": [
"cli",
["./pa11y-reporter-junit.js", { "fileName": "./pa11y-report-junit.xml" }]
]
}
}
- simultaneidade: reduzi esse conjunto para 1 porque aumentá-lo causou erros (https://github.com/pa11y/pa11y-ci/issues/168 cobre o bug, que pode ser corrigido).
- padrão: mantive o WCAG2AA padrão como objetivo deste site.
- corredores: executei ax (executar testes usando axe-core) e htmlcs (padrão, executar testes usando HTML CodeSniffer) para cobrir todos os possíveis problemas de acessibilidade.
- ignore: Com versões mais recentes do axe e algumas alterações no site, encontrei falsos positivos de contraste de cores. Eu também tenho um iframe incorporado que requer testes separados sobre os quais o ax irá relatar. Tenho questões de acompanhamento para examinar os resultados do machado, por isso estou ignorando esses critérios por enquanto.
- chromeLaunchConfig: pa11y-ci usa o Chrome e descobri que o pipeline do GitLab CI exige que o navegador Chrome seja executado corretamente no pipeline.
- relatórios: eu uso o repórter de linha de comando padrão, mas também adicionei um repórter personalizado que relata os resultados do pa11y-ci em formato junit. Isso foi útil para relatar os resultados no pipeline de CI do GitLab.
É isso. Execute esta configuração localmente usando npm
e você verá a seguinte saída (truncada por questões de brevidade):
dmundra in ~/workspace/accessibility/accessibility on branch main > npm run start-detached
> start-detached
> bundle exec jekyll serve --detach
Configuration file: /Users/dmundra/workspace/accessibility/accessibility/_config.yml
Source: /Users/dmundra/workspace/accessibility/accessibility
Destination: /Users/dmundra/workspace/accessibility/accessibility/_site
Incremental build: disabled. Enable with --incremental
Generating...
done in 8.217 seconds.
Auto-regeneration: disabled when running server detached.
Server address: http://127.0.0.1:4000
Server detached with pid '14850'. Run `pkill -f jekyll' or `kill -9 14850' to stop the server.
dmundra in ~/workspace/accessibility/accessibility on branch main > npm run pa11y-ci:sitemap
> pa11y-ci:sitemap
> pa11y-ci --sitemap http://localhost:4000/sitemap.xml --sitemap-exclude "/*.pdf"
Running Pa11y on 110 URLs:
> http://localhost:4000/guide/glossary - 0 errors
> http://localhost:4000/guide/introduction - 0 errors
> http://localhost:4000/guide/history - 0 errors
> http://localhost:4000/guide/design - 0 errors
...
✔ 110/110 URLs passed
O site passa nos testes. Aqui está um exemplo de trabalho em execução no GitLab. A configuração pa11y continua testando todas as páginas do site em busca de problemas de acessibilidade e reportando-os.
Qual é a aparência de um erro? Aqui está um exemplo:
> http://localhost:4000/guide/introduction - 1 errors
Errors in http://localhost:4000/guide/introduction:
• <ul> and <ol> must only directly contain <li>, <script> or <template>
elements (https://dequeuniversity.com/rules/axe/3.5/list?application=axeAPI)
(#main-content > div:nth-child(2) > div > div > div > div:nth-child(1) > nav
> ul)
<ul class="usa-sidenav">
Você obtém uma contagem do número de erros em um determinado URL e, em seguida, detalhes sobre o problema de acessibilidade. Também exibe um link para os critérios que estão sendo violados e a localização no HTML do problema.
Experimente Cipreste
Cypress é uma estrutura de teste de JavaScript e é muito útil para escrever testes que interagem com o site e afirmam que os recursos funcionam conforme o esperado. A configuração do Cypress é muito semelhante ao pa11y-ci em termos de instalação com npm
.
$ npm i --save-dev cypress cypress-axe cypress-real-events
Após a conclusão da instalação, edite o package.json
e adicione os seguintes comandos à seção scripts:
"cypress-tests": "cypress run --browser chrome --headless"
- cypress-tests: Execute os testes Cypress com um navegador Chrome sem cabeça.
Ao iniciar o Cypress pela primeira vez, você recebe um assistente para criar o arquivo de configuração. Aqui está um arquivo de exemplo:
const { defineConfig } = require('cypress')
module.exports = defineConfig({
video: true,
videosFolder: 'cypress/results',
reporter: 'junit',
reporterOptions: {
mochaFile: 'cypress/results/junit.[hash].xml',
toConsole: false,
},
screenshotsFolder: 'cypress/results/screenshots',
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require('./cypress/plugins/index.js')(on, config)
},
baseUrl: 'http://localhost:4000',
},
})
- vídeo: faça vídeos dos testes, que são úteis para solução de problemas.
- videosFolder: Define a pasta de armazenamento do vídeo.
- repórter: definido como junit para facilitar o relatório dos resultados no pipeline de CI do GitLab.
- reporterOptions: Inclui um caminho para os arquivos junit e a palavra-chave [hash] para preservar relatórios exclusivos para cada arquivo de teste (caso contrário, o arquivo será sobrescrito). Ignore a saída do console para o repórter e use a saída padrão.
- screenshotsFolder: Define a pasta de armazenamento de capturas de tela (útil para solução de problemas).
- e2e: Faz referência à URL local do site e aos plugins.
Depois de configurar o Cypress e escrever alguns testes (veja exemplos abaixo), execute os testes localmente usando npm
. Você verá a seguinte saída (truncada por questões de brevidade):
dmundra in ~/workspace/accessibility/accessibility on branch main > npm run cypress-tests
> cypress-tests
> cypress run --browser chrome --headless
====================================================================================================
(Run Starting)
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Cypress: 11.2.0 │
│ Browser: Chrome 109 (headless) │
│ Node Version: v18.10.0 (/usr/local/Cellar/node/18.10.0/bin/node) │
│ Specs: 5 found (accordion.cy.js, home.cy.js, images.cy.js, menu.cy.js, search.cy.js) │
│ Searched: cypress/e2e/**/*.cy.{js,jsx,ts,tsx} │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: search.cy.js (5 of 5)
(Results)
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Tests: 1 │
│ Passing: 1 │
│ Failing: 0 │
│ Pending: 0 │
│ Skipped: 0 │
│ Screenshots: 0 │
│ Video: true │
│ Duration: 2 seconds │
│ Spec Ran: search.cy.js │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
(Video)
- Started processing: Compressing to 32 CRF
- Finished processing: /Users/dmundra/workspace/accessibility/accessibility/cypres (0 seconds)
s/results/search.cy.js.mp4
...
(Run Finished)
Spec Tests Passing Failing Pending Skipped
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ ✔ search.cy.js 00:02 1 1 - - - │
...
Embora o Pa11y-ci possa testar a interatividade, o Cypress e seus plugins podem fazer muito mais. Para um site Jekyll, descobri que pa11y-ci não detectou nenhum problema de acessibilidade no menu suspenso móvel, pesquisa dinâmica ou recursos de acordeão. Executei testes do Cypress para interagir com os elementos (como realizar pesquisas, clicar em menus ou clicar no acordeão) e depois verifiquei se os resultados ainda passaram nos testes de acessibilidade. Aqui está o exemplo de pesquisa:
describe('Search', () => {
it('should be accessible', () => {
cy.visit('/search')
cy.get('#search-input').type('accessibility')
cy.checkA11yWithMultipleViewPorts()
})
})
Aqui está um vídeo rápido do teste em execução.
O teste acima visita a página de pesquisa, digita a palavra “acessibilidade” no campo de pesquisa e verifica os resultados em busca de problemas de acessibilidade. Eu uso o plugin cypress-axe para verificar problemas de acessibilidade com o ax core, assim como o pa11y-ci. Envolvi as funções do cypress-axe em uma função para testar vários tamanhos de janela e relatar os problemas em formato de tabela.
Eu também uso o plugin cypress-real-events para interagir com o site com um teclado para verificar se os recursos estão acessíveis pelo teclado. A acessibilidade do teclado é uma consideração crítica (princípio operável das WCAG), e ter um teste automatizado que pode confirmar se os recursos são acessíveis pelo teclado significa que, talvez, haja um teste a menos para executar manualmente. Você pode ver um exemplo do teste aqui.
Aqui está um exemplo da aparência de um erro:
Running: a11y/anonymous_a11y.cy.js (1 of 36)
cy.log(): Accessibility scanning: Home (/)
cy.log(): 4 accessibility violations were detected
┌─────────┬────────────────────────┬────────────┬────────────────────────────────────────────────────────────────────────────────────┬───────┐
│ (index) │ id │ impact │ description │ nodes │
├─────────┼────────────────────────┼────────────┼────────────────────────────────────────────────────────────────────────────────────┼───────┤
│ 0 │ 'image-alt' │ 'critical' │ 'Ensures <img> elements have alternate text or a role of none or presentation' │ 4 │
│ 1 │ 'link-name' │ 'serious' │ 'Ensures links have discernible text' │ 3 │
│ 2 │ 'page-has-heading-one' │ 'moderate' │ 'Ensure that the page, or at least one of its frames contains a level-one heading' │ 1 │
│ 3 │ 'region' │ 'moderate' │ 'Ensures all page content is contained by landmarks' │ 2 │
└─────────┴────────────────────────┴────────────┴────────────────────────────────────────────────────────────────────────────────────┴───────┘
Os logs do Cypress fornecem uma contagem do número de erros em um determinado URL e, em seguida, detalhes sobre qual é o problema de acessibilidade, o impacto e sua localização.
Você pode encontrar detalhes e exemplos adicionais na pasta Cypress.
Use o CI/CD do GitLab
Agora que você tem pa11y-ci e Cypress rodando localmente, veja como executar testes automatizados de acessibilidade no GitLab usando recursos de CI/CD. O repositório GitLab está disponível aqui. Aqui está a configuração do arquivo .gitlab-ci.yml
:
stages:
- test
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
- .npm/
- vendor/ruby
default:
image: ruby:2
before_script:
- apt-get update
- apt-get install -yq gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libnss3 lsb-release xdg-utils wget libgbm1 xvfb
- apt-get install -y nodejs npm
- bundle install -j $(nproc) --path vendor/ruby
- npm ci --cache .npm --prefer-offline
- npm run start-detached
pa11y-tests:
stage: test
script:
- npm run pa11y-ci:sitemap
artifacts:
when: always
reports:
junit:
- pa11y-report-junit.xml
expire_in: 1 day
cypress-tests:
stage: test
script:
# Install chrome browser manually, taken from https://github.com/cypress-io/cypress-docker-images/blob/master/browsers/node16.14.2-slim-chrome100-ff99-edge/Dockerfile#L48
- wget --no-verbose -O /usr/src/google-chrome-stable_current_amd64.deb "http://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_105.0.5195.125-1_amd64.deb"
- dpkg -i /usr/src/google-chrome-stable_current_amd64.deb
- rm -f /usr/src/google-chrome-stable_current_amd64.deb
- npm run cypress-tests
artifacts:
when: always
paths:
- cypress/results/
reports:
junit:
- cypress/results/*.xml
expire_in: 1 day
O arquivo atualmente define apenas um estágio de teste e armazena em cache pastas que armazenam dependências quando instaladas. Então:
Etapas usadas por todas as etapas:
- Use a imagem Ruby versão 2 porque ela é compatível com a instalação atual do Jekyll.
- Eu instalo muitas dependências com base na documentação em execução do puppeteer no GitLab. Instale
node
enpm
para instalar dependências do site. - Instale as dependências do Jekyll Ruby.
- Instale as dependências Cypress e pa11y-ci via
npm
. - Inicie o servidor web.
* Capture a saída dos testes Cypress e pa11y-ci como arquivos XML junit.
Aqui está um exemplo de captura de tela do pipeline do GitLab (retirado de https://gitlab.com/civicactions/accessibility/-/pipelines/744894072):
(Daniel Mundra, CC BY-SA 4.0)
Aqui está um exemplo dos resultados do teste no mesmo pipeline:
(Daniel Mundra, CC BY-SA 4.0)
GitLab CI/CD pega automaticamente arquivos XML junit e os gera em um formato claro. Os testes Cypress fornecem a saída XML junit como parte de seus recursos (veja acima). Criei um repórter personalizado para pa11y-ci para gerar o formato em junit (crédito para macieklewkowicz/pa11y-reporter-junit).
Nota: GitLab versão 12.8+ suporta testes de acessibilidade Pa11y (consulte https://docs.gitlab.com/ee/ci/testing/accessibility_testing.html para obter detalhes). A configuração acima permite a personalização do pa11y-ci e também a segmentação de URLs locais. Eu recomendo usar suas opções para sites ativos.
Embrulhar
Usando as etapas acima, você pode fornecer testes de acessibilidade para seu site localmente e em CI. Este processo ajuda a rastrear e corrigir problemas de acessibilidade em seu site e no conteúdo. Uma advertência importante sobre testes automatizados é que eles detectam apenas 57% dos problemas, então você definitivamente deseja incluir testes manuais em seus testes de acessibilidade.
Leitura adicional e exemplos
- Teste automatizado de acessibilidade: aproveitando GitHub Actions e pa11y-ci com axe.
- Testes automatizados de acessibilidade com Travis CI.
- Como automatizar testes de acessibilidade na Web.
- Configurando Cypress com machado para acessibilidade - Tim Deschryver.
- CI GitLab | Documentação Cipreste.
Obrigado a Marissa Fox e Mike Gifford por seu apoio, opiniões e comentários.