github / github/accessibility-scanner

Scanner does not wait for client-side rendering before running axe scan

未关闭
#201 2 条评论 2 个 reaction 已指派 0 人 在 GitHub 查看
主要语言
TypeScript
星标
369
派生
40
平均合并
1 天 9 小时
30 天内合并 PR
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 `

` instead of the actual page content.

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

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。