import AxeBuilder from '@axe-core/playwright' import { expect, test, type Page } from '@playwright/test' import { authenticateAs, authenticateConsumer } from '@/e2e/fixtures/session' const consumerProfile = { id: 'user-id', mobile: '09123456789', firstName: 'علی', lastName: 'رضایی', gender: 'male', cityId: null, bio: 'کاربر تست', avatarUrl: null, status: 'active', identityStatus: 'verified', role: 'user', } const consumerViewports = [ { name: 'mobile', width: 360, height: 800 }, { name: 'tablet', width: 768, height: 1024 }, { name: 'desktop', width: 1440, height: 900 }, ] as const const expectNoSeriousViolations = async (page: Page) => { await page.waitForLoadState('domcontentloaded') await page.locator('body').waitFor({ state: 'visible' }) let violations try { ;({ violations } = await new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa', 'wcag21aa']).analyze()) } catch (error) { if (!(error instanceof Error) || !error.message.includes('Execution context was destroyed')) throw error await page.waitForLoadState('domcontentloaded') ;({ violations } = await new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa', 'wcag21aa']).analyze()) } const blocking = violations.filter(({ impact }) => impact === 'critical' || impact === 'serious') expect(blocking, blocking.map(({ id, help }) => `${id}: ${help}`).join('\n')).toEqual([]) } test('auth route has no serious automated accessibility violations', async ({ page }) => { await page.goto('/auth') await expectNoSeriousViolations(page) }) test('public discovery route has no serious automated accessibility violations', async ({ page }) => { await page.route('**/api/v1/**', (route) => route.fulfill({ json: { success: true, data: { items: [], meta: {} } } })) await page.goto('/') await expectNoSeriousViolations(page) }) test('admin shell has no serious automated accessibility violations', async ({ context, page }) => { await authenticateAs(context, page, 'admin') await page.route('**/api/v1/**', (route) => route.fulfill({ json: { success: true, data: {} } })) await page.route('**/api/v1/admin/reports/overview', (route) => route.fulfill({ json: { success: true, data: { users: { total: 0, active: 0, pending: 0, suspended: 0 }, events: { total: 0, active: 0, totalHeld: 0, byStatus: { draft: 0, pending_review: 0, published: 0, full: 0, cancelled: 0, completed: 0 }, }, bookings: { total: 0, byStatus: { pending_payment: 0, confirmed: 0, cancelled: 0, expired: 0, refunded: 0, no_show: 0 } }, identityVerifications: { none: 0, pending: 0, verified: 0, rejected: 0 }, financial: { totalCommissionCollected: '0', totalWalletBalance: '0' }, }, }, }) ) await page.goto('/dashboard') await expect(page.getByText('کل کاربران').first()).toBeVisible() await expect(page.locator('#mainContent')).toHaveCSS('opacity', '1') await expectNoSeriousViolations(page) }) for (const viewport of consumerViewports) { test(`consumer shell has no serious violations or horizontal overflow at ${viewport.name} width`, async ({ context, page }) => { await page.setViewportSize({ width: viewport.width, height: viewport.height }) await authenticateConsumer(context, page) await page.route('**/api/v1/users/me', (route) => route.fulfill({ json: { success: true, data: consumerProfile } })) await page.route('**/api/v1/users/me/wallet', (route) => route.fulfill({ json: { success: true, data: { id: 'wallet-1', balance: 250000 } } }) ) await page.route('**/api/v1/organizers/me/following?**', (route) => route.fulfill({ json: { success: true, data: { items: [], pagination: { page: 1, pageSize: 1, totalItemsCount: 0, totalPagesCount: 0 } } }, }) ) await page.route('**/api/v1/conversations/me/unread-count', (route) => route.fulfill({ json: { success: true, data: { totalUnread: 0 } } }) ) await page.route('**/api/v1/notifications/me/unread-count', (route) => route.fulfill({ json: { success: true, data: { totalUnread: 0 } } }) ) await page.goto('/profile') await expect(page.getByRole('heading', { level: 1, name: 'پروفایل من' })).toBeVisible() await expect(page.getByRole('main')).toBeVisible() const bottomNavigation = page.getByRole('navigation', { name: 'ناوبری پایین' }) await expect(bottomNavigation).toBeVisible() const navigationItemSizes = await bottomNavigation.getByRole('link').evaluateAll((links) => links.map((link) => { const bounds = link.getBoundingClientRect() return { height: bounds.height, width: bounds.width } }) ) const hasHorizontalOverflow = await page.evaluate(() => document.documentElement.scrollWidth > document.documentElement.clientWidth) expect(hasHorizontalOverflow).toBe(false) expect(navigationItemSizes.every(({ height, width }) => height >= 44 && width >= 44)).toBe(true) await expectNoSeriousViolations(page) }) }