import { expect, test } from '@playwright/test' import { addTask, dragCardToColumn } from './helpers' test.beforeEach(async ({ page }) => { await page.goto('/') }) test('每个用例使用独立上下文,初始看板为空', async ({ page }) => { await expect(page.getByText('共 0 个任务')).toBeVisible() await expect(page.locator('article')).toHaveCount(0) await expect(page.getByText('暂无任务')).toHaveCount(3) }) test('新增 → 拖到进行中 → 标记完成 → 刷新后状态仍正确', async ({ page }) => { await addTask(page, 'E2E 主链路任务') await expect(page.locator('[data-column-id="todo"] article')).toHaveCount(1) await dragCardToColumn(page, 'E2E 主链路任务', 'doing') await expect(page.locator('[data-column-id="doing"] article')).toHaveCount(1) await expect(page.locator('[data-column-id="todo"] article')).toHaveCount(0) // 刷新后位置保持 —— 这一条是「拖拽结果真的落盘了」的关键证据 await page.reload() await expect( page.locator('[data-column-id="doing"] article').filter({ hasText: 'E2E 主链路任务' }), ).toBeVisible() // 标记完成 await page .locator('[data-column-id="doing"] article') .filter({ hasText: 'E2E 主链路任务' }) .locator('button') .first() .click() await expect(page.locator('[data-column-id="done"] article')).toHaveCount(1) await page.reload() await expect( page.locator('[data-column-id="done"] article').filter({ hasText: 'E2E 主链路任务' }), ).toBeVisible() }) test('详情抽屉可编辑并持久化', async ({ page }) => { await addTask(page, '待编辑任务') await page.locator('article').filter({ hasText: '待编辑任务' }).click() const titleInput = page.getByLabel('标题') await expect(titleInput).toHaveValue('待编辑任务') await titleInput.fill('已编辑任务') await page.getByLabel('描述').fill('这是描述') // 工具栏也有「高」按钮,必须限定在详情抽屉内 await page.getByRole('dialog').getByRole('button', { name: '高' }).click() await page.getByRole('button', { name: '保存' }).click() await expect(page.locator('article').filter({ hasText: '已编辑任务' })).toBeVisible() await page.reload() const card = page.locator('article').filter({ hasText: '已编辑任务' }) await expect(card).toBeVisible() await expect(card).toContainText('高') }) test('删除后可撤销', async ({ page }) => { await addTask(page, '待删除任务') await page.locator('article').filter({ hasText: '待删除任务' }).click() await page.getByRole('button', { name: '删除' }).click() await expect(page.locator('article')).toHaveCount(0) await page.getByRole('button', { name: '撤销' }).click() await expect(page.locator('article').filter({ hasText: '待删除任务' })).toBeVisible() await page.reload() await expect(page.locator('article').filter({ hasText: '待删除任务' })).toBeVisible() }) test('搜索筛选后列计数同步变化', async ({ page }) => { await addTask(page, '写方案') await addTask(page, '买咖啡') await expect(page.locator('article')).toHaveCount(2) await page.getByLabel('搜索任务').fill('方案') // 关键词有 200ms 防抖 await expect(page.locator('article')).toHaveCount(1) await expect(page.locator('article').filter({ hasText: '写方案' })).toBeVisible() await expect(page.locator('section').first().locator('[data-role="column-count"]')).toHaveText('1') }) test('键盘快捷键:/ 聚焦搜索,N 聚焦新增,? 打开帮助', async ({ page }) => { await page.keyboard.press('/') await expect(page.getByLabel('搜索任务')).toBeFocused() await page.keyboard.press('Escape') await page.locator('body').click({ position: { x: 5, y: 5 } }) await page.keyboard.press('n') await expect(page.getByLabel('添加任务').first()).toBeFocused() await page.locator('body').click({ position: { x: 5, y: 5 } }) await page.keyboard.press('?') await expect(page.getByText('聚焦「待办」列的快速新增输入框')).toBeVisible() }) test('输入框内输入不会触发快捷键', async ({ page }) => { const search = page.getByLabel('搜索任务') await search.click() await search.type('n') // 焦点应仍在搜索框,而不是跳到新增输入框 await expect(search).toBeFocused() await expect(search).toHaveValue('n') }) test('深色模式可切换并在刷新后保持', async ({ page }) => { await page.goto('/') const html = page.locator('html') const themeButton = page.getByRole('button', { name: /主题/ }) await expect(html).not.toHaveClass(/dark/) // 系统 → 浅色 → 深色 await themeButton.click() await expect(html).not.toHaveClass(/dark/) await themeButton.click() await expect(html).toHaveClass(/dark/) await page.reload() await expect(html).toHaveClass(/dark/) // 再点一次回到跟随系统 await themeButton.click() await expect(html).not.toHaveClass(/dark/) }) test('键盘方向键可以移动任务卡片', async ({ page }) => { await addTask(page, '键盘移动我') const card = page.locator('article').filter({ hasText: '键盘移动我' }) await card.focus() await page.keyboard.press('ArrowRight') await expect(page.locator('[data-column-id="doing"] article')).toHaveCount(1) await page.keyboard.press('ArrowLeft') await expect(page.locator('[data-column-id="todo"] article')).toHaveCount(1) await page.reload() await expect(page.locator('[data-column-id="todo"] article')).toHaveCount(1) })