diff --git a/admin-frontend-vue/e2e/task-18-accessibility.spec.ts b/admin-frontend-vue/e2e/task-18-accessibility.spec.ts new file mode 100644 index 00000000..8bc0b123 --- /dev/null +++ b/admin-frontend-vue/e2e/task-18-accessibility.spec.ts @@ -0,0 +1,77 @@ +import { expect, test, type Page } from '@playwright/test' + +async function openUsers(page: Page) { + await page.goto('/admin-vue/') + await expect(page.locator('h2')).toHaveText('用户管理') +} + +test('test_task_018_shell_accessibility_normal_primary_path', async ({ page }) => { + await openUsers(page) + await expect(page.getByRole('navigation', { name: '侧边栏菜单' })).toBeVisible() +}) + +test('test_task_018_shell_accessibility_normal_variant_input', async ({ page }) => { + await openUsers(page) + const toggle = page.locator('.sidebar-collapse') + await expect(toggle).toHaveAttribute('aria-label', '切换侧边栏') + await expect(toggle).toHaveAttribute('aria-expanded', 'true') + await expect(toggle).toHaveAttribute('type', 'button') +}) + +test('test_task_018_shell_accessibility_normal_repeated_operation_is_idempotent', async ({ page }) => { + await openUsers(page) + const toggle = page.locator('.sidebar-collapse') + await toggle.click() + await expect(toggle).toHaveAttribute('aria-expanded', 'false') + await toggle.click() + await expect(toggle).toHaveAttribute('aria-expanded', 'true') +}) + +test('test_task_018_shell_accessibility_boundary_empty_input', async ({ page }) => { + await openUsers(page) + await expect(page.locator('.skip-link')).toHaveAttribute('href', '#admin-content') + await expect(page.locator('#admin-content')).toHaveCount(1) +}) + +test('test_task_018_shell_accessibility_boundary_single_item', async ({ page }) => { + await openUsers(page) + // 键盘可聚焦并可回车触发折叠切换。 + await page.locator('.sidebar-collapse').focus() + await expect(page.locator('.sidebar-collapse')).toBeFocused() + await page.keyboard.press('Enter') + await expect(page.locator('.admin-shell')).toHaveClass(/is-collapsed/) +}) + +test('test_task_018_shell_accessibility_boundary_limit_or_missing_field', async ({ page }) => { + await openUsers(page) + await expect(page.locator('main#admin-content')).toBeVisible() + await expect(page.getByRole('main')).toHaveCount(1) +}) + +test('test_task_018_shell_accessibility_invalid_input_rejected', async ({ page }) => { + await openUsers(page) + // 折叠后品牌文案不应留在可感知/可聚焦树中。 + await page.locator('.sidebar-collapse').click() + await expect(page.locator('.admin-brand-copy')).toHaveCount(0) +}) + +test('test_task_018_shell_accessibility_dependency_failure_returns_actionable_message', async ({ page }) => { + await openUsers(page) + // 标题层级:顶栏 h1(页面标题) + 页面 h2;键盘焦点样式已定义。 + await expect(page.locator('.admin-topbar h1')).toHaveText('用户管理') + await expect(page.locator('main h2')).toHaveText('用户管理') + const hasFocusStyle = await page.evaluate(() => { + const rules: string[] = [] + for (const sheet of document.styleSheets) { + try { + for (const rule of sheet.cssRules) { + if (rule.cssText.includes(':focus-visible')) rules.push(rule.cssText) + } + } catch { + // 跨域样式表忽略 + } + } + return rules.some((t) => t.includes('sidebar-collapse')) + }) + expect(hasFocusStyle).toBe(true) +}) diff --git a/admin-frontend-vue/src/layout/AdminLayout.vue b/admin-frontend-vue/src/layout/AdminLayout.vue index 362f87e1..16f60a55 100644 --- a/admin-frontend-vue/src/layout/AdminLayout.vue +++ b/admin-frontend-vue/src/layout/AdminLayout.vue @@ -79,7 +79,8 @@ async function signOut() {