本文介绍如何使用 Pa11y (pa11y-ci 与 axe) 和 Cypress (与 cypress-axe) 在 GitLab CI/CD 中向您的站点添加辅助功能测试。我使用 Jekyll 网站作为示例,但任何在 CI/CD 中运行的网站技术都可以利用此设置。
准备您的网站
除了让您的网站在 CI/CD 中运行之外,我建议启用 XML 站点地图功能。站点地图允许辅助功能测试解析所有 URL,以查找整个站点的辅助功能问题。我推荐 Jekyll 站点的 jekyll-sitemap 插件。
如果无法使用站点地图,收集所有主要 URL 的列表是一个很好的替代步骤。这些 URL 应涵盖网站的所有潜在布局,例如具有最高流量或最多着陆页的页面。这种方法无法捕获所有辅助功能问题,尤其是内容级别的问题,但它将测试布局和主页。
这种情况需要 npm 或 yarn 包管理器。我在本文中使用 npm
。如果您的项目没有初始化 npm
,请运行 npm init
命令来创建 package.json
文件。
从 Pa11y 开始
Pa11y 是一款免费且开源的软件,用于测试网站的辅助功能问题。 Pa11y-ci 是面向持续集成 (CI) 的命令行实用程序。 使用 npm
将 pa11y-ci 作为开发依赖项安装
$ npm i --save-dev pa11y-ci
完成安装后,编辑 package.json
并将以下命令添加到 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:启动将运行 Jekyll 进行测试的 Web 服务器。
- pa11y-ci:home:在主页上运行 pa11y-ci 测试。 对于故障排除很有用。
- pa11y-ci:sitemap:使用站点地图运行 pa11y-ci 测试并排除 PDF。 站点地图将引用实时站点 URL,因此请将这些 URL 替换为本地 URL,以便在 CI 管道中进行测试。
添加一个名为 .pa11yci
的 JSON 文件,使用 各种选项配置 pa11y-ci。 这是一个示例文件
{
"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" }]
]
}
}
- concurrency:我将此集合减少到 1,因为增加它会导致错误(https://github.com/pa11y/pa11y-ci/issues/168 涵盖了该错误,可能已修复)。
- standard:我坚持使用默认的 WCAG2AA 作为此站点的目标。
- runners:我运行了 axe(使用 axe-core 运行测试)和 htmlcs(默认,使用 HTML CodeSniffer 运行测试)来涵盖所有潜在的辅助功能问题。
- ignore:对于较新版本的 axe 和站点的某些更改,我遇到了颜色对比度的误报。 我还有一个嵌入式 iframe,需要单独测试,axe 会报告它。 我有后续问题要检查 axe 结果,所以我暂时忽略这些标准。
- chromeLaunchConfig:pa11y-ci 使用 Chrome,我发现 GitLab CI 管道要求 Chrome 浏览器在管道中正常运行。
- reports:我使用默认的命令行报告器,但我也添加了一个自定义报告器,它以 junit 格式报告 pa11y-ci 结果。 这对于在 GitLab CI 管道中报告结果非常有用。
就是这样。 使用 npm
在本地运行此设置,您将看到以下输出(为简洁起见已截断)
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
该站点通过了测试。 此处是在 GitLab 中运行的示例作业。 pa11y 配置继续测试所有站点页面是否存在辅助功能问题并报告它们。
错误是什么样的? 这是一个例子
> 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">
您将获得给定 URL 处的错误数量的计数,然后获得有关辅助功能问题的详细信息。 它还显示了违反标准的链接以及问题在 HTML 中的位置。
尝试 Cypress
Cypress 是一个 JavaScript 测试框架,对于编写与站点交互并断言功能按预期工作的测试非常有帮助。 Cypress 的设置与使用 npm
安装的 pa11y-ci 非常相似。
$ npm i --save-dev cypress cypress-axe cypress-real-events
安装完成后,编辑 package.json
并将以下命令添加到 scripts 部分
"cypress-tests": "cypress run --browser chrome --headless"
- cypress-tests:使用无头 Chrome 浏览器运行 Cypress 测试。
首次启动 Cypress 时,您会得到一个向导来创建 配置文件。 这是一个示例文件
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',
},
})
- video:拍摄测试视频,这对于故障排除很有帮助。
- videosFolder:定义视频存储文件夹。
- reporter:设置为 junit,以便更轻松地在 GitLab CI 管道中报告结果。
- reporterOptions:包括 junit 文件的路径和关键字 [hash],以保留每个测试文件的唯一报告(否则,该文件将被覆盖)。 跳过报告器的控制台输出,并使用默认输出。
- screenshotsFolder:定义屏幕截图存储文件夹(对于故障排除很有用)。
- e2e:引用站点的本地 URL 和插件。
设置 Cypress 并编写一些测试后(请参阅下面的示例),使用 npm
在本地运行测试。 您将看到以下输出(为简洁起见已截断)
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 - - - │
...
虽然 Pa11y-ci 可以测试交互性,但 Cypress 及其插件可以做更多的事情。 对于 Jekyll 站点,我发现 pa11y-ci 没有捕获移动下拉菜单、动态搜索或手风琴功能中的任何辅助功能问题。 我运行了 Cypress 测试来与元素交互(例如执行搜索、单击菜单或单击手风琴),然后检查结果是否仍然通过辅助功能测试。 这是 搜索示例
describe('Search', () => {
it('should be accessible', () => {
cy.visit('/search')
cy.get('#search-input').type('accessibility')
cy.checkA11yWithMultipleViewPorts()
})
})
这是一个快速 视频,展示了正在运行的测试。
上述测试访问搜索页面,在搜索字段中键入单词“accessibility”,然后检查结果是否存在辅助功能问题。 我使用 cypress-axe 插件来检查 axe core 的辅助功能问题,就像 pa11y-ci 一样。 我已将 cypress-axe 函数包装在一个函数中,以测试多个窗口大小并以表格格式报告问题。
我还使用插件 cypress-real-events 通过键盘与站点交互,以检查这些功能是否可通过键盘访问。 键盘可访问性是一个关键考虑因素(WCAG 的可操作原则),并且拥有一个可以确认这些功能可通过键盘访问的自动化测试意味着,也许,需要手动运行的测试少了一个。 您可以在 此处看到一个测试示例。
这是一个错误示例
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 │
└─────────┴────────────────────────┴────────────┴────────────────────────────────────────────────────────────────────────────────────┴───────┘
Cypress 日志提供给定 URL 处的错误数量的计数,然后详细说明辅助功能问题是什么、影响以及其位置。
您可以在 Cypress 文件夹中找到更多详细信息和示例。
使用 GitLab CI/CD
现在您已经在本地运行 pa11y-ci 和 Cypress,请了解如何使用 CI/CD 功能在 GitLab 中运行自动化辅助功能测试。 GitLab 存储库位于 此处。 这是 .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
该文件当前仅定义一个阶段 test,并缓存安装时存储依赖项的文件夹。 然后
- 所有阶段使用的步骤
- 使用 Ruby 版本 2 映像,因为它与当前的 Jekyll 安装兼容。
- 我根据 在 GitLab 上运行 puppeteer 的文档安装了许多依赖项。 安装
node
和npm
以安装站点依赖项。 - 安装 Jekyll Ruby 依赖项。
- 通过
npm
安装 Cypress 和 pa11y-ci 依赖项。 - 启动 Web 服务器。
- 运行 pa11y-ci 以测试站点并将输出捕获到文件中。*
- 使用 Cypress 在其 Docker 映像配置中提供的步骤,在 cypress-tests 中安装 Chrome 浏览器依赖项。 运行 Cypress 测试并将输出捕获到文件中。*
* 将 Cypress 和 pa11y-ci 测试的输出捕获为 junit XML 文件。
这是 GitLab 管道的示例屏幕截图(取自 https://gitlab.com/civicactions/accessibility/-/pipelines/744894072)

(Daniel Mundra, CC BY-SA 4.0)
这是同一流水线中测试结果的示例

(Daniel Mundra, CC BY-SA 4.0)
GitLab CI/CD 自动获取 junit XML 文件 并以清晰的格式输出它们。Cypress 测试提供 junit XML 输出作为其特性的一部分(见上文)。我为 pa11y-ci 创建了一个自定义报告器,以 junit 格式输出(感谢 macieklewkowicz/pa11y-reporter-junit)。
注意:GitLab 版本 12.8+ 支持 Pa11y 可访问性测试(详细信息请参阅 https://docs.gitlab.com/ee/ci/testing/accessibility_testing.html)。上述设置允许自定义 pa11y-ci 并且还可以定位本地 URL。 我建议为实时站点使用他们的选项。
总结
使用上述步骤,您可以为您的站点在本地和 CI 中提供可访问性测试。此过程可帮助您跟踪和修复站点和内容中的可访问性问题。关于自动化测试的一个重要警告是,它只能捕获 57% 的问题,因此您一定要将手动测试包含在您的可访问性测试中。
更多阅读和示例
- 自动化可访问性测试:利用 GitHub Actions 和带有 axe 的 pa11y-ci.
- 使用 Travis CI 进行自动化可访问性测试.
- 如何自动化 Web 可访问性测试.
- 使用 axe 设置 Cypress 以进行可访问性测试 - Tim Deschryver.
- GitLab CI | Cypress 文档.
感谢 Marissa Fox 和 Mike Gifford 的支持、想法和反馈。
1 条评论