import { test, expect } from '@playwright/test' import AxeBuilder from '@axe-core/playwright' import { loginAsAdmin } from './helpers/auth.js' const publicPages = [ { name: 'Home', path: '/' }, { name: 'Join', path: '/join' }, { name: 'Events', path: '/events' }, { name: 'Coming Soon', path: '/coming-soon' }, ] const memberPages = [ { name: 'Member Dashboard', path: '/member/dashboard' }, { name: 'Member Profile', path: '/member/profile' }, ] const adminPages = [ { name: 'Admin Members', path: '/admin/members' }, { name: 'Admin Events Create', path: '/admin/events/create' }, ] test.describe('accessibility — public pages', () => { for (const { name, path } of publicPages) { test(`${name} has no critical a11y violations`, async ({ page }) => { await page.goto(path) await page.waitForLoadState('networkidle') const results = await new AxeBuilder({ page }) .withTags(['wcag2a', 'wcag2aa']) .analyze() const critical = results.violations.filter( (v) => v.impact === 'critical' || v.impact === 'serious' ) expect(critical, `${name} has critical/serious a11y issues`).toEqual([]) }) } }) test.describe('accessibility — member pages', () => { test.beforeEach(async ({ page }) => { await loginAsAdmin(page) }) for (const { name, path } of memberPages) { test(`${name} has no critical a11y violations`, async ({ page }) => { await page.goto(path) await page.waitForLoadState('networkidle') const results = await new AxeBuilder({ page }) .withTags(['wcag2a', 'wcag2aa']) .analyze() const critical = results.violations.filter( (v) => v.impact === 'critical' || v.impact === 'serious' ) expect(critical, `${name} has critical/serious a11y issues`).toEqual([]) }) } }) test.describe('accessibility — admin pages', () => { test.beforeEach(async ({ page }) => { await loginAsAdmin(page) }) for (const { name, path } of adminPages) { test(`${name} has no critical a11y violations`, async ({ page }) => { await page.goto(path) await page.waitForLoadState('networkidle') const results = await new AxeBuilder({ page }) .withTags(['wcag2a', 'wcag2aa']) .analyze() const critical = results.violations.filter( (v) => v.impact === 'critical' || v.impact === 'serious' ) expect(critical, `${name} has critical/serious a11y issues`).toEqual([]) }) } }) test.describe('keyboard navigation', () => { test('tab through join form fields in order', async ({ page }) => { await page.goto('/join') await page.waitForLoadState('networkidle') // Focus the name field and tab through await page.locator('#join-name').focus() expect(await page.locator('#join-name').evaluate((el) => el === document.activeElement)).toBe(true) await page.keyboard.press('Tab') // Email field should receive focus next expect(await page.locator('#join-email').evaluate((el) => el === document.activeElement)).toBe(true) }) test('escape closes login modal', async ({ page }) => { await page.goto('/member/dashboard') // Wait for the sign-in prompt to appear, then click to open the modal await expect(page.getByRole('button', { name: 'Sign In' })).toBeVisible({ timeout: 10000 }) await page.getByRole('button', { name: 'Sign In' }).click() const modalTitle = page.locator('.modal-title') await expect(modalTitle).toBeVisible({ timeout: 5000 }) await page.keyboard.press('Escape') // Modal should close await expect(modalTitle).not.toBeVisible({ timeout: 5000 }) }) })