# Prompt — INFRA-F20: Playwright 视觉基线框架

> **角色**：executor
> **范围**：建立 docs site 视觉快照基线框架。创建 `playwright.config.ts` + `tests/visual/docs-pages.spec.ts`，跑 `--update-snapshots` 生成全部 baseline 截图，然后 commit。
>
> ⚠️ **本任务需要 commit**——截图是 source of truth，必须提交进仓库。
> ⚠️ **不扩范围**：只改/创建 §0 文件清单内的文件；不动 canonical / src/components / backlog.md / retrospection。
> ⚠️ §0 所有设计决策已由 plan owner 写定，**不要自行重设计**。
> ⚠️ 完成后按 §4 格式回报。

---

## §0 — Plan owner 已定裁定

### 背景

INFRA-F19 ✅ 已完成：
- `DocsShell.vue` 有 theme toggle 按钮（selector：`button.canonical-toggle`，dark/light 切换）
- `provide('docsTheme', readonly(docsTheme))` 已下发，`FigmaMembersGrid` 跟随全局 toggle
- 默认主题：dark（`isDark = ref(true)`）
- watch isDark → `document.documentElement.setAttribute('data-theme', 'dark'|'light')`

### 目标

docs site 每个 page × dark/light 两张截图 = 视觉基线。未来 canonical 组件改动如引入视觉回归，`pnpm test:visual` 自动检测。

### §0.1 — 创建文件清单（共 2 个新文件）

| 文件 | 操作 |
|---|---|
| `playwright.config.ts`（项目根） | 新建 |
| `tests/visual/docs-pages.spec.ts` | 新建 |

截图存放目录：`tests/visual/__screenshots__/`（由 `snapshotPathTemplate` 控制）。

### §0.2 — playwright.config.ts

```typescript
import { defineConfig, devices } from '@playwright/test'

export default defineConfig({
  testDir: 'tests/visual',
  snapshotPathTemplate: '{testDir}/__screenshots__/{arg}{ext}',
  use: {
    baseURL: 'http://localhost:5173',
    viewport: { width: 1280, height: 900 },
  },
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
  ],
  webServer: {
    command: 'pnpm dev',
    url: 'http://localhost:5173',
    reuseExistingServer: true,
    timeout: 120_000,
  },
  testMatch: '**/*.spec.ts',
})
```

### §0.3 — tests/visual/docs-pages.spec.ts

```typescript
import { test, expect } from '@playwright/test'
import { orderedPages, getPagePath } from '../../playground/docs/navigation'

const SCREENSHOT_OPTS = {
  maxDiffPixelRatio: 0.001,
  maxDiffPixels: 100,
  animations: 'disabled',
} as const

for (const pageItem of orderedPages) {
  test(`${pageItem.id}`, async ({ page }) => {
    await page.goto(getPagePath(pageItem.id))
    await page.waitForLoadState('networkidle')

    // Dark baseline（默认状态）
    await expect(page).toHaveScreenshot(`${pageItem.id}-dark.png`, SCREENSHOT_OPTS)

    // 切 light
    await page.click('button.canonical-toggle')
    await page.waitForFunction(
      () => document.documentElement.getAttribute('data-theme') === 'light',
    )

    // Light baseline
    await expect(page).toHaveScreenshot(`${pageItem.id}-light.png`, SCREENSHOT_OPTS)
  })
}
```

**注意**：
- `orderedPages` 从 `playground/docs/navigation.ts` 动态拿，新加 page 自动纳入，不需要硬编码列表
- `animations: 'disabled'` 避免动画截图不稳定
- 每个 test 独立 page context，无需重置 toggle

### §0.4 — package.json 加 scripts

在 `package.json` `scripts` 段加两行（在 `"test:watch"` 后面）：

```json
"test:visual": "playwright test",
"test:visual:update": "playwright test --update-snapshots"
```

---

## §1 — 必读输入

1. [`playground/docs/navigation.ts`](../../../playground/docs/navigation.ts) — `orderedPages` + `getPagePath` export（动态页面列表）
2. [`playground/docs/DocsShell.vue`](../../../playground/docs/DocsShell.vue) — 确认 `button.canonical-toggle` selector + watch isDark → data-theme
3. [`vite.config.ts`](../../../vite.config.ts) — dev server port 5173，root = `playground`（serve mode）
4. [`package.json`](../../../package.json) — playwright `^1.59.1` 已在 devDependencies，确认版本

---

## §2 — 任务清单（按顺序）

### 任务 2.1 — 统计 orderedPages 总数

读 `navigation.ts`，数出 `orderedPages` 数组的条目数。§4 报告中写"共 N 个 page，预期截图 N×2 张"。

### 任务 2.2 — 创建 playwright.config.ts

按 §0.2 写到项目根目录（和 `vite.config.ts` 同级）。

### 任务 2.3 — 创建 tests/visual/ 目录 + spec 文件

```bash
mkdir -p tests/visual
```

按 §0.3 写 `tests/visual/docs-pages.spec.ts`。

### 任务 2.4 — package.json 加 scripts

按 §0.4 加两行。

### 任务 2.5 — 生成 baseline 截图

```bash
pnpm test:visual:update
```

playwright 会自动启动 dev server（或复用已有的），跑全部 tests，生成截图到 `tests/visual/__screenshots__/`。

**如果某个 page 截图失败**（timeout / element not found / crash）：
- 不要整体放弃，单个 page 失败记录到 §4 未解决项
- 其他 page 正常截图继续

### 任务 2.6 — 验证截图

```bash
find tests/visual/__screenshots__ -name "*.png" | wc -l
find tests/visual/__screenshots__ -name "*.png" | sort
```

→ 确认截图数量 = orderedPages.length × 2，文件名符合 `{id}-dark.png` / `{id}-light.png` 格式。

### 任务 2.7 — 跑一次验证 test（无 --update-snapshots）

```bash
pnpm test:visual
```

→ 应全 pass（刚生成的 baseline 与自身对比 → 0 diff）。
→ 如有 diff：说明截图生成不稳定，在 §4 记录。

### 任务 2.8 — 确认不破 vitest

```bash
pnpm test
```

→ vitest 的 `tests/*.test.ts` 应全 pass（playwright 配置与 vitest 不冲突）。

### 任务 2.9 — commit

```bash
git add playwright.config.ts tests/visual/ package.json
git commit -m "feat(INFRA-F20): Playwright visual baseline — docs site <N> page × dark/light"
```

（N = orderedPages.length）

---

## §3 — 验收清单

- [ ] `playwright.config.ts` 按 §0.2 创建，`testDir: 'tests/visual'`，webServer 指向 5173
- [ ] `tests/visual/docs-pages.spec.ts` 按 §0.3 创建，动态循环 orderedPages
- [ ] `package.json` 加 `test:visual` + `test:visual:update` scripts
- [ ] `pnpm test:visual:update` 成功运行，截图生成到 `tests/visual/__screenshots__/`
- [ ] 截图数量 = orderedPages.length × 2
- [ ] `pnpm test:visual`（不加 update-snapshots）全 pass（0 diff）
- [ ] `pnpm test`（vitest）仍全 pass（不冲突）
- [ ] commit 包含 playwright.config.ts + tests/visual/（含截图 png）+ package.json
- [ ] **不动**：canonical / src / figma 真源 / backlog.md / retrospection / translation

---

## §4 — 完成报告

```
## INFRA-F20 Playwright 视觉基线 完成报告

### orderedPages 总数
共 N 个 page，预期截图 N×2 = M 张

### 截图生成结果
- 目录：tests/visual/__screenshots__/
- 实际截图数：M 张
- 成功 / 失败：N tests passed, M tests failed

### pnpm test:visual（验证 pass）
- 结果：N passed / M failed

### pnpm test（vitest 不冲突）
- 结果：pass / N failures

### package.json scripts
- test:visual: "playwright test" ✅
- test:visual:update: "playwright test --update-snapshots" ✅

### commit hash
[hash]

### 未解决项 / blocker
- [page id: 失败原因 / 无则写"无"]

DONE — baseline 已 commit，plan owner 可 pnpm dev 打开 docs site 视觉确认。
```
