github / github/accessibility-scanner
Scanner does not wait for client-side rendering before running axe scan
- 主要言語
- TypeScript
- スター
- 369
- フォーク
- 40
- 平均マージ
- 1日 9時間
- マージ済み PR(30日)
- 10
説明
## Problem
When scanning single-page applications (React, Vue, Angular, etc.), the scanner runs the axe scan immediately after `page.goto()` resolves (which waits for the `load` event). At that point, the JavaScript bundles are loaded but the framework hasn't finished rendering the DOM yet. This means axe scans a nearly-empty `
This leads to:
- **False positives**: Document-level violations like `landmark-one-main` and `page-has-heading-one` are reported because the landmarks and headings haven't been rendered yet.
- **False negatives**: Element-level violations like `button-name` are missed because the elements don't exist in the DOM yet.
- **Misleading screenshots**: Screenshots are taken *after* axe runs (inside `addFinding`), by which time React has finished rendering. So the screenshots show the correct, fully-rendered page — even though axe scanned a different DOM state.
## Steps to reproduce
1. Set up the scanner against any React/SPA application
2. Run a scan on a page that has proper `` landmarks and `
` headings rendered by the framework
3. Observe that `landmark-one-main` and `page-has-heading-one` violations are reported
4. Run axe dev tools manually in the browser on the same page — these violations are not found
5. Observe that element-level violations found by axe dev tools (e.g. `button-name`) are not reported by the scanner
## Root cause
In [`findForUrl.ts`](https://github.com/github/accessibility-scanner/blob/main/.github/actions/find/src/findForUrl.ts):
```ts
await page.goto(url)
// axe runs immediately — no wait for client-side rendering
const rawFindings = await new AxeBuilder({page}).analyze()
```
`page.goto()` resolves on the `load` event, which fires when HTML/CSS/JS resources are loaded — but before the JS framework has executed and rendered the DOM.
## Suggested fix
Add a wait for the page to be idle before running the axe scan. For example:
```ts
await page.goto(url)
await page.waitForLoadState('networkidle')
// or: await page.waitForTimeout(2000)
// or: await page.waitForFunction(() => document.querySelector('[data-testid]') !== null)
const rawFindings = await new AxeBuilder({page}).analyze()
```
`waitForLoadState('networkidle')` waits until there are no network connections for at least 500ms, which is a reasonable heuristic for "the SPA has finished its initial API calls and rendered."
## Environment
- `github/accessibility-scanner@v2` (SHA: `7866232dda98e447fed8ec0d7798b322d888fd27`)
- React 19 application with Mantine UI, served from Docker containers via Caddy
- Authenticated via `auth_context` input with session cookies
コントリビューションガイド
調査の方向性
Start in .github/actions/find/src/findForUrl.ts and inspect the sequence from page.goto(url) to AxeBuilder.analyze(). Reproduce the scan against a client-rendered React page and verify that axe observes the rendered DOM rather than the initial root element; done means the reported findings and scan timing match the fully rendered page.
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- playwright, react, typescript
- 領域
- accessibility, frontend, testing-qa
- issue の種類
- バグ
- 難易度
- 3/5
- 見積もり時間
- 1〜2日
- 活発さ
- 静か
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 58/100