# @a11y-oracle/playwright-plugin 中文文档

规范地址：https://npmcn.com/package/%40a11y-oracle/playwright-plugin/

版本：1\.3\.2
npm 元数据更新时间：2026-03-12T05:47:22.672Z
中文译文更新时间：2026-11-06T12:52:51.599Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40a11y-oracle/playwright-plugin
原文对照：https://npmcn.com/package/%40a11y-oracle/playwright-plugin/?lang=original
项目主页：https://github.com/a11y-oracle/a11y-oracle/tree/main/libs/playwright-plugin
源码仓库：https://github.com/a11y-oracle/a11y-oracle

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：35.7 kB，11 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：2 个
- peer 依赖：@playwright/test \>=1\.40\.0
- 首次发布：2026-03-04T19:25:09.609Z
- 最新版本发布：2026-03-12T05:47:22.539Z
- 近 30 天下载量：3178（2026-10-05 至 2026-11-03，npm 官方下载统计）
- 已发布版本数：10（近 12 个月 10 个）

## 中文简介

Playwright 为无障碍语音断言、键盘导航和焦点指标验证而设的测试固定

## 安装

```sh
npm install @a11y-oracle/playwright-plugin
```

也可以使用：`pnpm add @a11y-oracle/playwright-plugin`、`yarn add @a11y-oracle/playwright-plugin`、`bun add @a11y-oracle/playwright-plugin`

## README

# @a11y- oracle/ playwright- 插件

A11y-Oracle 的 Playwright 集成. 提供测试固定和包装类,通过Chrome DevTools协议读取浏览器的可访问树,发送本地键盘事件,并分析视觉焦点指标.

> **QQ 建议插件 .** Playwright提供直接的一等CDP会话访问,使这成为A11y-Oracle最稳定和最能表现的集成. 如果您选择了 E2E 框架, 则使用此框架 .

```typescript
import { test, expect } from '@a11y-oracle/playwright-plugin';

test('dropdown button announces correctly', async ({ page, a11y }) => {
  await page.goto('/dropdown-nav.html');

  const speech = await a11y.press('Tab');
  expect(speech).toContain('Home');
  expect(speech).toContain('menu item');
});
```

## 安装

```bash
npm install -D @a11y-oracle/playwright-plugin @a11y-oracle/core-engine @playwright/test
```

> **只有铬.** Playwright的Firefox或WebKit不提供 CDP会话.

## 使用量

### 测试固定( 建议)

插件导出扩展名 `test` 函数 `a11y` 修补。 CDP会话在每次测试前创建,之后自动清理.

```typescript
import { test, expect } from '@a11y-oracle/playwright-plugin';

test.describe('Navigation', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/my-page.html');
  });

  test('Tab to button announces name and role', async ({ a11y }) => {
    const speech = await a11y.press('Tab');
    expect(speech).toBe('Submit, button');
  });

  test('checkbox announces checked state', async ({ a11y }) => {
    await a11y.press('Tab');
    await a11y.press('Tab');
    const speech = await a11y.press('Space');
    expect(speech).toContain('checkbox');
    expect(speech).toContain('checked');
  });

  test('navigation landmark exists', async ({ a11y }) => {
    const tree = await a11y.getFullTreeSpeech();
    const nav = tree.find(r => r.speech.includes('navigation landmark'));
    expect(nav).toBeDefined();
  });
});
```

### 统一国家

这个 `pressKey()` 方法返回完整 `A11yState` 将语音输出、重点要素信息和重点指标分析结合起来的快照:

```typescript
test('focus indicator meets WCAG 2.4.12 AA', async ({ page, a11y }) => {
  await page.goto('/my-page.html');

  const state = await a11y.pressKey('Tab');

  // Speech
  expect(state.speech).toContain('Submit');
  expect(state.speechResult?.role).toBe('button');

  // Focused element
  expect(state.focusedElement?.tag).toBe('BUTTON');
  expect(state.focusedElement?.id).toBe('submit-btn');
  expect(state.focusedElement?.tabIndex).toBe(0);

  // Focus indicator CSS analysis
  expect(state.focusIndicator.isVisible).toBe(true);
  expect(state.focusIndicator.contrastRatio).toBeGreaterThanOrEqual(3.0);
  expect(state.focusIndicator.meetsWCAG_AA).toBe(true);
});

test('Shift+Tab navigates backward', async ({ page, a11y }) => {
  await page.goto('/my-page.html');

  await a11y.pressKey('Tab');
  const state1 = await a11y.pressKey('Tab');
  const state2 = await a11y.pressKey('Tab', { shift: true });

  expect(state2.focusedElement?.id).toBe(state1.focusedElement?.id);
});
```

### 标签顺序和键盘陷阱检测

```typescript
test('page has correct tab order', async ({ page, a11y }) => {
  await page.goto('/my-page.html');

  const report = await a11y.traverseTabOrder();
  expect(report.totalCount).toBeGreaterThan(0);
  expect(report.entries[0].tag).toBe('A');
});

test('modal does not trap keyboard focus', async ({ page, a11y }) => {
  await page.goto('/modal.html');

  const result = await a11y.traverseSubTree('#modal-container', 20);
  expect(result.isTrapped).toBe(false);
  expect(result.escapeElement).not.toBeNull();
});
```

### 审计和问题报告

使用 `OracleAuditor` 从 `@a11y-oracle/audit-formatter` 自动检查 WCAG 对每次交互的规则并积累任何问题:

```bash
npm install -D @a11y-oracle/audit-formatter
```

```typescript
import { test, expect } from '@a11y-oracle/playwright-plugin';
import { OracleAuditor } from '@a11y-oracle/audit-formatter';

test('all focus indicators pass oracle rules', async ({ page, a11y }) => {
  await page.goto('/my-page.html');

  const auditor = new OracleAuditor(a11y, {
    project: 'my-app',
    specName: 'navigation.spec.ts',
  });

  // Each pressKey() automatically checks all 5 state-based rules
  await auditor.pressKey('Tab');
  await auditor.pressKey('Tab');
  await auditor.pressKey('Tab');

  // checkTrap() automatically checks keyboard-trap
  await auditor.checkTrap('#modal-container');

  // Assert no issues found across all interactions
  expect(auditor.getIssues()).toHaveLength(0);
});
```

在套件末尾将问题写入 JSON 报告文件:

```typescript
import { test } from '@a11y-oracle/playwright-plugin';
import { OracleAuditor, type OracleIssue } from '@a11y-oracle/audit-formatter';
import * as fs from 'fs';

const allIssues: OracleIssue[] = [];

test('check page focus indicators', async ({ page, a11y }) => {
  await page.goto('/my-page.html');
  const auditor = new OracleAuditor(a11y, {
    project: 'my-app',
    specName: 'nav.spec.ts',
  });

  await auditor.pressKey('Tab');
  await auditor.pressKey('Tab');
  allIssues.push(...auditor.getIssues());
});

test.afterAll(() => {
  if (allIssues.length > 0) {
    fs.writeFileSync('oracle-results.json', JSON.stringify(allIssues, null, 2));
  }
});
```

关于每项规则的详细补救指导意见,见 [补救指南](https://npmcn.com/package/docs/REMEDIATION.md)。 。 。 .

### 自定义选项

每个测试组覆盖语音引擎选项 `test.use()`数字 :

```typescript
test.describe('without landmark suffix', () => {
  test.use({ a11yOptions: { includeLandmarks: false } });

  test('nav role without landmark', async ({ page, a11y }) => {
    await page.goto('/my-page.html');
    const tree = await a11y.getFullTreeSpeech();
    const nav = tree.find(r => r.role === 'navigation');
    expect(nav?.speech).toBe('Main, navigation');
  });
});
```

可用的选项 :

| 选项 | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| `includeLandmarks` | `boolean` | `true` | 在标志性角色中添加"地印" |
| `includeDescription` | `boolean` | `false` | 包含 `aria-describedby` 输出中的文本 |
| `focusSettleMs` | `number` | `50` | 在按下焦点/ CSS 键后延迟 |

### 手动使用

如果需要对生命周期进行更多的控制(例如,附在某一页的中试),请使用 `A11yOracle` 直接分类 :

```typescript
import { A11yOracle } from '@a11y-oracle/playwright-plugin';
import { test, expect } from '@playwright/test';

test('manual setup', async ({ page }) => {
  await page.goto('/my-page.html');

  const a11y = new A11yOracle(page, { includeDescription: true });
  await a11y.init();

  const speech = await a11y.press('Tab');
  expect(speech).toContain('button');

  await a11y.dispose();
});
```

## API 参考

### `A11yOracle`

管理 CDP 会话,并为当前页面提供访问测试.

#### `constructor(page: Page, options?: A11yOrchestratorOptions)`

创建新实例 .

-   `page` - 剧作家 `Page` 随附.
-   `options.includeLandmarks` ——将"地标"附录为地标性角色. 默认 `true`。 。 。 .
-   `options.includeDescription` - 包括说明案文。 默认 `false`。 。 。 .
-   `options.focusSettleMs` ——按键后延迟焦点/CSS结算. 默认 `50`。 。 。 .

#### `init(): Promise<void>`

打开 CDP 会话并启用无障碍域. 一定要在任何其他方法之前叫出来 测试固定器自动调用.

#### 仅限发言的 API

##### `press(key: string): Promise<string>`

按键盘键( 通过 Playwright 键) `page.keyboard.press()`),并返回新聚焦元素的演讲. 如果没有元素焦点,则返回空字符串.

```typescript
const speech = await a11y.press('Tab');
// "Products, button, collapsed"
```

##### `getSpeech(): Promise<string>`

获取当前焦点元素的语音字符串而不按键 .

##### `getSpeechResult(): Promise<SpeechResult | null>`

获取焦点元素的全部结构化结果.

##### `getFullTreeSpeech(): Promise<SpeechResult[]>`

在无障碍树上获取所有非被忽略的节点的语音.

#### 统一国家

##### `pressKey(key: string, modifiers?: ModifierKeys): Promise<A11yState>`

通过本地 CDP 发送密钥 `Input.dispatchKeyEvent` 并返回统一无障碍状态。 与 `press()`,这使用硬件级别密钥发送并返回完整的状态.

```typescript
const state = await a11y.pressKey('Tab');
// state.speech           → "Products, button, collapsed"
// state.focusedElement   → { tag: 'BUTTON', id: '...', ... }
// state.focusIndicator   → { isVisible: true, meetsWCAG_AA: true, ... }
```

##### `getA11yState(): Promise<A11yState>`

获取当前统一状态而不按键 .

```typescript
await page.focus('#my-button');
const state = await a11y.getA11yState();
```

##### `traverseTabOrder(): Promise<TabOrderReport>`

在 DOM 标签顺序中提取所有可制表元素 .

##### `traverseSubTree(selector: string, maxTabs?: number): Promise<TraversalResult>`

检测容器是否夹住键盘焦点(WCAG 2.1.2).

#### 生命周期

##### `dispose(): Promise<void>`

解开社发委会会会会和免费资源. 测试固定器自动调用.

### 测试固定

这个 `test` 导出扩展 Playwright 的 `test` 有两个固定装置:

| 固定线 | 类型 | 说明 |
| --- | --- | --- |
| `a11y` | `A11yOracle` | 初始化实例, 每次测试后自动处理 |
| `a11yOptions` | `A11yOrchestratorOptions` | 通过 `test.use()` |

### 出口

```typescript
// Test fixture (recommended)
export { test, expect } from '@a11y-oracle/playwright-plugin';

// Manual usage
export { A11yOracle } from '@a11y-oracle/playwright-plugin';

// Fixture types
export type { A11yOracleFixtures } from '@a11y-oracle/playwright-plugin';

// Re-exported types from core-engine
export type {
  A11yState,
  A11yFocusedElement,
  A11yFocusIndicator,
  A11yOrchestratorOptions,
  SpeechResult,
  SpeechEngineOptions,
  ModifierKeys,
  TabOrderReport,
  TabOrderEntry,
  TraversalResult,
  FocusIndicator,
} from '@a11y-oracle/playwright-plugin';
```

## 剧作家配置

插件需要铬. 一个典型 `playwright.config.ts`数字 :

```typescript
import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    baseURL: 'http://localhost:4200',
  },
  projects: [
    {
      name: 'chromium',
      use: { browserName: 'chromium' },
    },
  ],
  webServer: {
    command: 'npm run serve',
    url: 'http://localhost:4200',
    reuseExistingServer: !process.env.CI,
  },
});
```

## 如何运作

1.  固定会话通过 `page.context().newCDPSession(page)`
2.  它创造了两个 `SpeechEngine` 还有一个 `A11yOrchestrator` 该届会议的报告
3.  **`press(key)`** ——使用 Playwright 的键盘 API,等待50ms,然后读取 AXTree 的语音
4.  **`pressKey(key)`** 使用本地CDP `Input.dispatchKeyEvent` 用于硬件级别发送, 等等 `focusSettleMs`,然后同时收集语音+焦点要素+焦点指标
5.  重点指标分析 `Runtime.evaluate` 读取计算 CSS 样式并计算对比率
6.  在处置时, CPP 会话是分开的

语音格式如下: `[Computed Name], [Role], [State/Properties]`

关于角色和状态图的完整清单,见 [@a11y- oracle/ 核心引擎 README](https://npmcn.com/package/%40a11y-oracle/core-engine/README.md)。 。 。 .

## 常见问题

### @a11y-oracle/playwright-plugin 是什么？

Playwright 为无障碍语音断言、键盘导航和焦点指标验证而设的测试固定

### 如何安装 @a11y-oracle/playwright-plugin？

在项目目录运行 npm install @a11y-oracle/playwright-plugin，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：@playwright/test（\>=1\.40\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @a11y-oracle/playwright-plugin 支持 TypeScript 吗？

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

### @a11y-oracle/playwright-plugin 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @a11y-oracle/playwright-plugin 可以商用吗？

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

### @a11y-oracle/playwright-plugin 的下载量有多少？

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

### @a11y-oracle/playwright-plugin 还在维护吗？

最新版本 1\.3\.2 发布于 2026/03/12（7 个月前）；至今共发布 10 个版本，其中过去 12 个月发布了 10 个；首次发布于 2026/03/05。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

