From 1bab8a87acfb3c1eac01ef32d3d2886a64b962ad Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Fri, 27 Mar 2026 16:42:03 -0700 Subject: [PATCH] feat(a11y): add axe-core accessibility E2E spec with focus-visible test --- web/e2e/accessibility.spec.ts | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 web/e2e/accessibility.spec.ts diff --git a/web/e2e/accessibility.spec.ts b/web/e2e/accessibility.spec.ts new file mode 100644 index 0000000..d0451ed --- /dev/null +++ b/web/e2e/accessibility.spec.ts @@ -0,0 +1,39 @@ +import { test, expect } from '@playwright/test'; +import AxeBuilder from '@axe-core/playwright'; + +const routes = ['/', '/dashboard', '/workspaces', '/search', '/reviews']; + +for (const route of routes) { + test(`accessibility: ${route} has no critical violations`, async ({ page }) => { + await page.goto(route); + await page.waitForLoadState('networkidle'); + + const results = await new AxeBuilder({ page }) + .withTags(['wcag2a', 'wcag2aa', 'best-practice']) + .analyze(); + + const critical = results.violations.filter( + (v) => v.impact === 'critical' || v.impact === 'serious' + ); + + if (critical.length > 0) { + const summary = critical + .map((v) => `[${v.impact}] ${v.id}: ${v.description} (${v.nodes.length} nodes)`) + .join('\n'); + console.log(`Accessibility violations on ${route}:\n${summary}`); + } + + expect(critical).toHaveLength(0); + }); +} + +test('accessibility: focus-visible ring appears on tab navigation', async ({ page }) => { + await page.goto('/dashboard'); + await page.waitForLoadState('networkidle'); + await page.keyboard.press('Tab'); + await page.keyboard.press('Tab'); + + const focused = page.locator(':focus-visible'); + const count = await focused.count(); + expect(count).toBeGreaterThan(0); +});