27 / 74 · 10 Visual & Accessibility Testing · axe-core Integration← prev⊞ allnext →☰ Read as one page
5.4Testing Interactive States
Accessibility issues often appear in interactive states that axe-core's default scan misses:
test('modal accessibility after opening', async ({ page }) => {
await page.goto('/');
// axe scan of the page BEFORE modal
const beforeResults = await new AxeBuilder({ page }).analyze();
expect(beforeResults.violations).toEqual([]);
// Open modal
await page.click('[data-testid="open-modal"]');
await page.waitForSelector('[role="dialog"]');
// axe scan AFTER modal opens -- catches issues in modal content
const afterResults = await new AxeBuilder({ page })
.include('[role="dialog"]')
.analyze();
expect(afterResults.violations).toEqual([]);
});
test('form error state accessibility', async ({ page }) => {
await page.goto('/register');
// Submit empty form to trigger validation
await page.click('[data-testid="submit-btn"]');
// Scan the page in its error state
const results = await new AxeBuilder({ page }).analyze();
expect(results.violations).toEqual([]);
// Verify error messages are linked to inputs via aria-describedby
const errorInputs = await page.locator('[aria-invalid="true"]').all();
for (const input of errorInputs) {
const describedby = await input.getAttribute('aria-describedby');
expect(describedby).toBeTruthy();
// Verify the referenced error message exists and is visible
const errorMsg = page.locator(`#${describedby}`);
await expect(errorMsg).toBeVisible();
}
});