@accesslint/vitest

中文文档
0.3.2发布于 2026/07/27MITTypeScriptESM + CJS构建来源已验证近 30 天下载 4.8万

Accessibility assertions for Vitest — toBeAccessible() matcher powered by AccessLint

原始使用文档
文档目录17 节
  1. Installation
  2. Setup
  3. Manual registration
  4. Usage
  5. Framework recipes
  6. React — @testing-library/react
  7. Vue — @testing-library/vue
  8. Svelte — @testing-library/svelte
  9. Angular — @testing-library/angular
  10. Options
  11. Failure messages
  12. Snapshot baselines
  13. Trend reports
  14. Audit memoization (opt-in fixture)
  15. TypeScript
  16. What it checks
  17. License

@accesslint/vitest

Accessibility assertions for Vitest. Adds a toBeAccessible() matcher powered by AccessLint that checks for WCAG 2.2 Level A and AA violations.

Installation

npm install --save-dev @accesslint/vitest

vitest >= 2.0 is required as a peer dependency.

Setup

Add @accesslint/vitest as a setup file in your Vitest config:

// vitest.config.ts
import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    setupFiles: ["@accesslint/vitest"],
  },
});

This automatically registers the toBeAccessible() matcher.

Manual registration

import { accesslintMatchers } from "@accesslint/vitest/matchers";
import { expect } from "vitest";

expect.extend(accesslintMatchers);

Usage

Pass any DOM Element to expect() and call toBeAccessible():

const container = document.createElement("div");
container.innerHTML = '<img src="photo.jpg" alt="A photo">';

expect(container).toBeAccessible();

Violations are scoped to descendants of the element you pass, so you can test components in isolation. When the element is not the document root, page-level rules (html-has-lang, document-title, landmarks, etc.) are skipped automatically — override with componentMode: false if you want them back.

Framework recipes

Works with any DOM produced in a jsdom or happy-dom environment.

React — @testing-library/react

import { render } from "@testing-library/react";
import { LoginForm } from "./LoginForm";

test("LoginForm is accessible", () => {
  const { container } = render(<LoginForm />);
  expect(container).toBeAccessible();
});

Vue — @testing-library/vue

import { render } from "@testing-library/vue";
import LoginForm from "./LoginForm.vue";

test("LoginForm is accessible", () => {
  const { container } = render(LoginForm);
  expect(container).toBeAccessible();
});

Svelte — @testing-library/svelte

import { render } from "@testing-library/svelte";
import LoginForm from "./LoginForm.svelte";

test("LoginForm is accessible", () => {
  const { container } = render(LoginForm);
  expect(container).toBeAccessible();
});

Angular — @testing-library/angular

import { render } from "@testing-library/angular";
import { LoginFormComponent } from "./login-form.component";

test("LoginForm is accessible", async () => {
  const { container } = await render(LoginFormComponent);
  expect(container).toBeAccessible();
});

The matcher accepts anything that's an Element — container, screen.getByRole('form'), a plain document.createElement(...), or document.body for a full-page audit.

Options

expect(container).toBeAccessible({
  /** Rule IDs to disable for this assertion. */
  disabledRules: ["distinguishable/color-contrast"],

  /** Additional rules to run on top of the built-in set. */
  additionalRules: [myCompiledDeclarativeRule],

  /** Include AAA-level rules (excluded by default). */
  includeAAA: true,

  /** Skip page-level rules — defaults to true for non-root elements. */
  componentMode: true,

  /** Translated rule messages and guidance (e.g. "en", "es"). */
  locale: "es",

  /** Minimum impact that causes failure. "minor" (default) fails on anything. */
  failOn: "serious",

  /** Compare against a baseline instead of asserting zero violations. */
  snapshot: "login-form",

  /** Where to store snapshot files. Defaults to {cwd}/accessibility-snapshots/. */
  snapshotDir: "./test/a11y-snapshots",
});

Failure messages

Failures include impact, WCAG criterion, level, selector, optional context, suggested fix, and remediation guidance:

Expected element to have no accessibility violations, but found 2:

  [critical] text-alternatives/img-alt (WCAG 1.1.1, A) — Images must have alternate text
    selector: img
    fix: add-attribute alt=""
    guidance: Decorative images should have alt=""; informative images should describe content.

  [serious] distinguishable/color-contrast (WCAG 1.4.3, AA) — Text must have sufficient color contrast
    selector: p.subtitle

Snapshot baselines

Lock in the current accessibility state of a component and fail only on new violations. Useful when adopting AccessLint in a legacy codebase.

expect(container).toBeAccessible({ snapshot: "login-form" });
  • First run creates accessibility-snapshots/login-form.json and passes.
  • Subsequent runs fail if any new violation appears beyond the baseline.
  • Ratchet-down — when only fixed violations are detected (none added), the baseline updates automatically.
  • Force refresh the baseline with ACCESSLINT_UPDATE=1 or Vitest's -u / --update flag.

Violation identity starts at ruleId + selector and falls back through a tiered multi-signal matcher powered by @accesslint/heal-diff:

  1. Exact selector match (fast path).
  2. Anchor attribute (data-testid, id, name, href, for, aria-label).
  3. Computed ARIA role + accessible name.
  4. HTML fingerprint (normalized outerHTML hash).
  5. Relative location (nearest landmark trail), uniqueness-gated.

When a non-exact tier matches the baseline's selector auto-heals to the current one, the snapshot is rewritten, and a "healed" event is logged to .history.ndjson. The test still passes. When no tier matches but weaker signals overlap, the failure message attaches a likely moved from: hint so you can tell "same issue, different selector" from "genuinely new issue" at a glance.

Trend reports

Every create / ratchet-down / force-update event appends a record to accessibility-snapshots/.history.ndjson alongside the baseline JSON files. Generate a trend report from that history with @accesslint/report:

npx @accesslint/report --format md > a11y-report.md
npx @accesslint/report --format html --out a11y-report.html

The report shows a stacked chart of total violations per snapshot over time, plus a per-rule movement table that joins in WCAG metadata from @accesslint/core. The sidecar file is append-only and safe to commit; merge conflicts on it resolve trivially by concatenating both sides.

Audit memoization (opt-in fixture)

By default every toBeAccessible() call runs a fresh audit. For component tests that chain multiple assertions against the same DOM state, import the fixture to reuse one audit across assertions:

import { test } from "@accesslint/vitest/fixture";
import { expect } from "vitest";

test("Form", ({ a11y }) => {
  render(<Form />);
  a11y.refresh(); // snapshot current DOM state

  // both assertions reuse one runAudit() call
  expect(screen.getByRole("form")).toBeAccessible();
  expect(screen.getByRole("button")).toBeAccessible();
});

Call a11y.refresh() after any DOM mutation that should be re-audited.

TypeScript

Types are included. Importing the package augments Vitest's expect with toBeAccessible() automatically.

What it checks

WCAG 2.2 Level A and AA rules via @accesslint/core, covering images, forms, ARIA attributes, color contrast, landmarks, links, tables, document language, and more. See the core rules table for the full list.

License

MIT

版本与发布记录

@accesslint/vitest 共发布 11 个版本,首次发布于 2026/02/17;过去 12 个月发布了 11 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.3.2latest2026/07/273 个月前
0.3.12026/04/186 个月前
0.3.02026/04/186 个月前
0.1.92026/02/268 个月前
0.1.82026/02/258 个月前
0.1.72026/02/238 个月前

关于 @accesslint/vitest 的常见问题

@accesslint/vitest 是什么?

Accessibility assertions for Vitest — toBeAccessible() matcher powered by AccessLint

如何安装 @accesslint/vitest?

在项目目录运行 npm install @accesslint/vitest,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:vitest(>=2.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@accesslint/vitest 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@accesslint/vitest 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@accesslint/vitest 可以商用吗?

@accesslint/vitest 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@accesslint/vitest 的下载量有多少?

根据 npm 官方下载统计,@accesslint/vitest 在 2026/10/05 – 2026/11/03 这 30 天内被下载 48,234 次,日均约 1,608 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@accesslint/vitest 还在维护吗?

最新版本 0.3.2 发布于 2026/07/27(3 个月前);至今共发布 11 个版本,其中过去 12 个月发布了 11 个;首次发布于 2026/02/17。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。