# mnote 测试参考

这份文档面向 `/mnt/Data1T/mnote/scripts` 目录下的现有测试脚本，目标不是重新设计测试体系，而是把当前已经在用的 smoke、回归脚本、截图取证和人工复核方式整理成一套可执行参考。

当前结论先说在前面：

- `scripts/` 里的主力仍然是 `Playwright smoke + Node 断言`。
- 对 `mnote` 来说，最有价值的不是把所有测试都换成“浏览器插件式控制”，而是把已有 smoke 的结果变得更可看、更易复核。
- `/doko` 适合作为 smoke 之后的“真实页面直观复核层”，不是用来替代 smoke。
- 2026-05-20 起，默认主入口固定按 `http://127.0.0.1:3000` 理解；历史记录里的 `3001` 只代表当时临时 mnote-web 实例，不再作为默认验收入口。
- 历史 smoke 分为 `current` 与 `retired/debug` 两类维护；退役脚本不应进入默认回归，除非脚本自身要求显式环境变量。
- 2026-05-27 起，默认 smoke 基线只覆盖 `3000 Rust SSR + leptos-tiptap + local-first` 主路径；Convex export、Convex 兼容、Next、3104、BlockNote 默认路径脚本不再放在默认候选里。

## 0. 当前 smoke 状态清单

这一节用于避免历史脚本被误当作当前架构主链验收。

### 0.0 2026-05-26 默认基线

建议日常 smoke 先跑这一组，确保当前主入口、认证、文档 island、Page Aggregate、local-first 和关键 runtime surface 没断：

```bash
node scripts/task114-rust-web-gateway-entry-smoke.js
node scripts/task159-auth-entry-smoke.js
node scripts/task164-desktop-hot-local-folder-main-entry-smoke.js
node scripts/task166-local-first-managed-workspace-no-convex-smoke.js
node scripts/task167-local-markdown-title-body-options-no-convex-smoke.js
node scripts/task490-runtime-surfaces-smoke.js
```

其中 `task490-runtime-surfaces-smoke.js` 是轻量浏览器 smoke，验证页面设置、Page AI drawer 打开/关闭、Page AI stop-run abort、slash menu、block handle menu 能在当前 leptos-tiptap 文档页工作；它使用 mock run 覆盖停止按钮，不要求真实模型返回，也不替代更重的页面设置、Hermes/Page AI 或编辑器菜单专项脚本。

### 0.1 当前主链优先脚本

- 入口与认证：`task114-rust-web-gateway-entry-smoke.js`、`task159-auth-entry-smoke.js`、`task097-homepage-entry-smoke.js`
- Rust SSR 文档页 / Page Aggregate：local-first 默认优先 `task167-local-markdown-title-body-options-no-convex-smoke.js`；Page Aggregate browser conversion / compat fallback 改动补跑 `task522-page-aggregate-compat-fallback-contract.js`；cloud/control-plane 文档可补跑 `task110-page-title-single-truth-smoke.js`、`task-page-aggregate-body-sync-smoke.js`、`task-page-aggregate-options-sync-smoke.js`、`task-page-aggregate-refresh-persistence-smoke.js`
- leptos-tiptap runtime surface：`task490-runtime-surfaces-smoke.js`；需要验证保存回读可补跑 `task121-rust-web-editor-island-hydration-smoke.js`，但它仍使用 `/api/tree/commands create` 准备文档，不作为无 Convex 默认基线；需要验证浮层互斥和更多菜单状态时再跑 `task158-e30-menu-state-smoke.js`
- local-first 本地工作区：`task164-desktop-hot-local-folder-main-entry-smoke.js`、`task166-local-first-managed-workspace-no-convex-smoke.js`、`task167-local-markdown-title-body-options-no-convex-smoke.js`、`task436-local-markdown-open-document-external-change-smoke.js`、`task443-local-markdown-asset-upload-smoke.js`、`task451-local-markdown-conflict-resolution-ui-smoke.js`、`task452-local-search-index-browser-smoke.js`、`task453-local-folder-page-ai-changed-files-smoke.js`；WorkspacePath / ObjectIdentity runtime 消费统一改动补跑 `task524-workspace-object-identity-matrix-smoke.js`
- LightRAG 资料库：`task534-knowledge-rag-source-management-scope-smoke.js` 覆盖本地文件夹 source 加入资料库、registry 状态与范围管理；`task529-knowledge-rag-citation-resource-tab-smoke.js`、`task530-knowledge-rag-page-ai-final-answer-smoke.js` 覆盖资料库引用回到资源页与 Page AI 最终回答。旧 `task526-local-folder-ocr-api-smoke.js` / OCR sidecar 链路已退役并软归档。
- local Markdown conflict regression：`task510-local-markdown-conflict-regression-group.js` 串联真实冲突、连续上传、新建页面、外部恢复、多 tab CAS 与上传 409 孤儿策略；可用 `MNOTE_CONFLICT_REGRESSION_TASKS=a.js,b.js` 做定点子集。
- tree realtime / live cache：`task446-tree-rename-dual-browser-live-smoke.js`、`task447-tree-move-order-dual-browser-live-smoke.js`、`task448-tree-resync-recovery-dual-browser-smoke.js`、`task449-tree-sse-reconnect-snapshot-recovery-smoke.js`
- 资源对象与 mindmap：`task455-local-folder-mindmap-clean-smoke.js`、`task456-resource-object-shell-sync-smoke.js`、`task166-mindmap-phase6-block-smoke.js`、`task167-mindmap-kmind-parity-smoke.js`、`task168-mindmap-put-validator-smoke.js`；Page AI mindmap skill / 资源生成改动补跑 `task503-mindmap-skill-capability-smoke.js`，真实 mindmap resource tab / Page AI target 改动补跑 `task525-page-ai-mindmap-resource-target-smoke.js`
- Page AI / agent history / ChatOnly：`task502-page-ai-agent-selector-context-smoke.js` 覆盖 Page AI agent/context/target picker、skills source 和 run payload；raw local resource target 改动补跑 `task520-page-ai-raw-resource-target-smoke.js`；`task504-page-ai-history-agent-filter-smoke.js` 覆盖 Page AI 历史按 agent 过滤；ChatOnly / provider session 绑定改动优先跑 `task512-chatonly-doubao-sync-smoke.js`，跨 provider 同步补跑 `task513-chatonly-provider-sync-smoke.js`
- Dev hot / OnlyOffice live bridge：Sidebar dev hot reload 入口改动补跑 `task514-sidebar-dev-hot-reload-gating-smoke.js`；OnlyOffice live bridge session / scope / HTTP 工具边界改动补跑 `task515-onlyoffice-live-scope-http-smoke.js`；bridge session/token/queue/current/close 和 `docKey/pageOrigin` 元数据改动补跑 `task516-onlyoffice-bridge-multisession-browser-smoke.js`；bridge plugin index / `Asc.plugin` direct loop / plugin 元数据透传改动补跑 `task517-onlyoffice-bridge-plugin-direct-smoke.js`；真实 ONLYOFFICE iframe / DocumentServer session、同文档双 tab、resource scope 和非 dry-run 写入落点改动补跑 `task518-onlyoffice-real-iframe-session-scope-smoke.js`；Page AI 真实 UI 选择 Office target 并冻结 live bridge session 改动补跑 `task523-page-ai-onlyoffice-real-target-session-smoke.js`
- local resource lifecycle API：`task437-local-folder-asset-trash-lifecycle-smoke.js`，现在只验证 `local-file:*` delete / restore / purge，不再依赖普通 `.txt` 是否出现在 UI 文件树。

### 0.2 需要退役或拆分的历史脚本

- `task444-convex-workspace-export-local-fixture-smoke.js`、`task455-convex-export-plan-rollback-smoke.js` 与 `export-convex-workspace-to-local.js`：Convex export 迁移验证已退出默认 smoke，已归档到 `recycle/scripts/retired-convex-export-smokes-20260526/`。
- 注意编号复用：`scripts/task444-filetree-create-mindmap-title-cache-smoke.js` 与 `scripts/task455-local-folder-mindmap-clean-smoke.js` 仍是当前本地 filetree / mindmap 脚本；退役的是带 `convex-export` 名称的 task444 / task455。
- 旧 Convex 兼容 smoke 已软删除到 `recycle/20260527-legacy-cleanup/convex/scripts/`，包括 `task163`、`task174`、`task175`、`task427`、`task428`、`task429`、`task430`、`task431`、`task432`、`task433`、`task434`。
- `task179-tree-create-delete-no-reload-smoke.js`：覆盖 `/tree` debug shell。`/tree` 已是显式 debug/internal 边界，脚本默认阻断；只有设置 `MNOTE_ALLOW_DEBUG_TREE_SMOKE=1` 时才可执行。
- `task123-rust-web-tree-live-stream-consumer-smoke.js`：只证明 `/api/tree/events` SSE fallback 仍可用。当前 realtime 主链是 WebSocket push + SSE fallback；验证主链时优先使用 446 / 447 / 448，只有排查 fallback 时再跑 123。
- 旧 `task019`、`task021`、`task022` 这类早期 UI regression 脚本已软删除到 `recycle/scripts/retired-ui-regressions/`，只作为历史对照；后续默认不要用于当前 Rust SSR / local-first 主路径验收。

### 0.3 其他 current 候选脚本

这些脚本不是最小默认基线，但仍属于当前 Rust SSR / leptos-tiptap / local-first 维护面，按改动范围选择性运行：

- `task426-mnote-web-main-no-reload-smoke.js`
- `task109-leptos-island-multipage-save-smoke.js`
- `task122-rust-web-create-page-ui-smoke.js`
- `task125-rust-web-search-server-first-smoke.js`
- `task165-rust-web-dual-pane-smoke.js`
- `task458-local-create-page-no-conflict-smoke.js`
- `task459-local-markdown-attachment-tab-smoke.js`
- `task484-local-folder-page-body-refresh-readback-smoke.js`
- `task486-local-markdown-save-error-editor-preserves-content-smoke.js`
- `task487-local-folder-tree-live-consumer-smoke.js`

## 1. 当前测试分层

按目录里的现状，脚本大致分成四层。

### 1.1 入口与网关层

这一层主要验证路由、网关、owner、认证入口、基础响应是否正确，特点是快、稳定、适合作为最早执行的守门测试。

代表脚本：

- `task097-homepage-entry-smoke.js`
- `task114-rust-web-gateway-entry-smoke.js`
- `task159-auth-entry-smoke.js`

适用场景：

- 刚切流 Rust Web
- 改了入口路由、鉴权、兼容代理
- 需要先判断“服务有没有活着、入口是不是对的”

### 1.2 页面交互 smoke 层

这一层是当前最核心的 UI 验证方式：启动页面、登录测试账号、进入真实文档页或工作区，然后通过 Playwright 操作页面并做断言。

代表脚本：

- `task110-page-title-single-truth-smoke.js`
- `task112-tree-rust-family-regression-smoke.js`
- `task120-rust-web-tree-integration-smoke.js`
- `task122-rust-web-create-page-ui-smoke.js`
- `task164-page-options-visible-effect-smoke.js`
- `task490-runtime-surfaces-smoke.js`
- `task165-rust-web-dual-pane-smoke.js`
- `task164-desktop-hot-local-folder-main-entry-smoke.js`
- `task166-local-first-managed-workspace-no-convex-smoke.js`
- `task455-local-folder-mindmap-clean-smoke.js`

这一层已经覆盖了当前主线里最重要的部分：

- tree / sidebar / create page
- document shell / island hydration
- 页面设置、双栏、AI、local folder
- Rust Web 主路径

### 1.3 Wolai 对标与视觉取证层

这一层不是单纯判断“是否通过”，而是要保留截图、矩阵、对比结论，方便和 Wolai 体验做真实核对。

代表脚本：

- `task119-rust-web-wolai-visual-regression-smoke.js`
- `task129-wolai-aline-baseline-smoke.js`
- `task130` 到 `task137` 一系列 `wolai-aline-*`
- `task160-wolai-page-settings-shell-smoke.js`
- 页面 AI 旧 `/api/ai-agent/run` 视觉 smoke 已移入本机 gitignored `recycle/scripts/retired-ai-agent-run-smokes/`；当前页面 AI 验证优先使用 Hermes 相关 smoke 与 `task-page-block-ai-tools-smoke.js`

这一层最重要的原则不是“文案命中”，而是：

- 必须有截图
- 必须能说明视口
- 必须能说明对比对象
- 最终仍需要人工看图

### 1.4 共享 helper 与登录/临时文档支撑层

这部分决定了 smoke 是否能稳定复用。

关键文件：

- `tree-shell-smoke-helpers.js`
- `task114-rust-web-gateway-entry-smoke.js`

目前 helper 已经沉淀了几个稳定约定：

- 默认前端入口：`MNOTE_UI_BASE_URL`，缺省 `http://127.0.0.1:3000`
- 默认测试账号：
  - 邮箱：`mnote.e2e@example.com`
  - 密码：`MnoteE2E123!`
  - 快捷按钮：`测试账号快速登录`
- 通过 API 或 UI 兜底完成认证
- 为 smoke 自动创建、重命名、清理临时页面

后续新脚本优先复用这些 helper，不要在每个任务里再复制一套登录和清理逻辑。

## 2. 当前推荐测试顺序

对本项目，推荐不要一上来就跑最重的 UI 对标脚本，而是按下面顺序推进。

### 2.1 第一步：入口守门

先跑入口和网关类 smoke，确认不是服务没起或路由错了。

建议优先：

```bash
node scripts/task114-rust-web-gateway-entry-smoke.js
node scripts/task159-auth-entry-smoke.js
node scripts/task097-homepage-entry-smoke.js
```

适用时机：

- 本地服务刚启动
- 刚切换分支
- 改了认证、入口、gateway、compat

### 2.2 第二步：主路径交互 smoke

入口没问题后，再跑和当前改动最相关的页面交互脚本。

例如：

- tree / sidebar 改动：`task112`、`task120`、`task122`
- document shell / island：`task110`、`task121`
- local-first：优先使用 `task164-desktop-hot-local-folder-main-entry-smoke.js`、`task166-local-first-managed-workspace-no-convex-smoke.js`、`task167-local-markdown-title-body-options-no-convex-smoke.js`、`task455-local-folder-mindmap-clean-smoke.js`
- 页面设置：`task160`、`task164-page-options-visible-effect-smoke.js`
- 双栏：`task165`
- AI：优先使用 `task-hermes-page-ai-retirement-guard.js`、`task-page-block-ai-tools-smoke.js` 和当前 Hermes 页面 AI smoke；Page AI history / skills / ChatOnly 相关改动补跑 `task503-mindmap-skill-capability-smoke.js`、`task504-page-ai-history-agent-filter-smoke.js`、`task512-chatonly-doubao-sync-smoke.js`、`task513-chatonly-provider-sync-smoke.js`；OnlyOffice live bridge 改动补跑 `task515-onlyoffice-live-scope-http-smoke.js`、`task516-onlyoffice-bridge-multisession-browser-smoke.js`、`task517-onlyoffice-bridge-plugin-direct-smoke.js`、`task518-onlyoffice-real-iframe-session-scope-smoke.js`，其中 `task516/517` 覆盖 bridge session lifecycle 与 `docKey/pageOrigin` 元数据；Page AI 真实 target picker / Office live session 改动补跑 `task523-page-ai-onlyoffice-real-target-session-smoke.js`；`task155`、`task156`、`task161` 等旧 `/api/ai-agent/run` smoke 只保留在本机 gitignored `recycle/scripts/retired-ai-agent-run-smokes/` 做历史对照

原则：

- 只跑和当前改动相关的脚本
- 不要无差别全量跑
- 先窄后宽

### 2.3 第三步：截图证据复核

如果脚本本身已经输出：

- `screenshot`
- `screenshotDir`
- `measure`
- `matrixPath`
- `resultPath`

那这些产物不应只当“附带文件”，而应作为测试结果的一部分。

推荐做法：

- 成功时也保留关键截图
- 失败时至少保留失败前最后一张截图
- 重要 UI 任务保留整组截图目录

### 2.4 第四步：`/doko` 真实页面复核

当 smoke 已经告诉你“逻辑大体通过”，但你仍想确认页面是否真的像人看到的一样时，再补 `/doko`。

`/doko` 更适合回答这类问题：

- 首屏布局是不是顺眼
- Sidebar 层级、图标、文案是否真可读
- 页面设置弹层是否真的展开到位
- 登录后工作区是否像预期渲染

`/doko` 不擅长替代：

- 稳定断言
- 自动点击链路
- 大量重复回归

所以推荐组合是：

`smoke 先筛出通过/失败 -> 看截图 -> 必要时再用 /doko 复核真实页面`

## 3. 为什么当前不建议把主线转成“浏览器插件测试”

当前讨论过 `codex cli + 浏览器插件` 的方向，但对 `mnote` 现阶段的主要收益并不明显。

原因有三点：

1. 你当前真正缺的是“页面直观证据”，不是“替换掉 Playwright”
2. `scripts/` 里的 smoke 已经能覆盖多数关键交互，只是证据层不够统一
3. 浏览器插件路线更适合复用真实 Chrome 当前标签页和真实登录环境，不是当前项目测试的首要瓶颈

因此，现阶段更推荐：

- 保留 Playwright smoke 作为自动化骨架
- 补足截图、trace、结果摘要
- 用 `/doko` 做人工视角复核

## 4. 新增或修改 smoke 的写法建议

这里不是要求统一重构旧脚本，而是约束新增脚本尽量沿着同一条路走。

### 4.1 优先复用 helper

优先从下面两个文件拿能力：

- `tree-shell-smoke-helpers.js`
- `task114-rust-web-gateway-entry-smoke.js`

尤其是：

- 登录
- 创建临时文档
- 清理临时文档
- `fetchWithTimeout`
- `findFreePort`
- `waitForGateway`

### 4.2 让脚本输出结构化结果

推荐脚本结束时输出 JSON，至少包含：

```json
{
  "ok": true,
  "baseUrl": "http://127.0.0.1:3000",
  "task": "taskxxx-name",
  "screenshot": "/abs/path/to/file.png",
  "screenshotDir": "/abs/path/to/dir"
}
```

如果没有截图，也应该至少输出：

- `ok`
- `baseUrl`
- `task`
- 关键对象 id（如 `documentId`、`workspaceId`）

### 4.3 对 UI 任务默认保留截图

满足下面任一条件时，建议默认保留截图：

- 首屏入口变化
- Sidebar / tree 结构变化
- modal / popover / dropdown 变化
- editor block 行为变化
- Wolai 对标

推荐命名方式延续现有风格：

- `01-before-*`
- `02-after-*`
- `03-after-reload-*`

这样后续人工复核时不需要重新猜步骤。

### 4.4 失败信息要面向定位

断言报错要直接描述“哪个状态不对”，不要只写泛化失败。

推荐：

- `开启宽版后 htmlWide 应为 true，实际 false`
- `测试账号登录后仍停留在 /auth`
- `本地首屏未捕获工作区特征`

不推荐：

- `smoke failed`
- `assert error`

## 5. 针对当前 mnote 的推荐组合

如果后续只是做日常开发验证，推荐这样选：

### 5.1 快速检查

适合日常改完立刻看：

```bash
node scripts/task114-rust-web-gateway-entry-smoke.js
node scripts/task159-auth-entry-smoke.js
```

### 5.2 主路径回归

按当前改动挑 1 到 3 个最相关脚本，例如：

```bash
node scripts/task120-rust-web-tree-integration-smoke.js
node scripts/task122-rust-web-create-page-ui-smoke.js
node scripts/task164-page-options-visible-effect-smoke.js
```

### 5.3 视觉复核

如果任务明显涉及视觉或交互形态：

- 先看 smoke 产出的截图
- 再用 `/doko` 打开真实页面复核

### 5.4 Wolai 对标任务

对标任务不要只看断言结果，至少补：

- 本地 smoke
- Wolai 基线截图
- 本地截图
- 必要时 comparison matrix

## 6. 对后续演进的建议

这部分不是要求本轮立刻实现，而是后续维护时优先考虑。

### 6.1 在现有 smoke 上统一证据产物

优先级最高的增强不是“换工具”，而是统一输出：

- `tmp/.../screenshots`
- `tmp/.../trace.zip`
- `tmp/.../result.json`

这样 smoke 从“脚本 pass/fail”升级成“脚本 + 可看证据”。

### 6.2 把 `/doko` 固化成复核步骤

适合写进任务流程的话术是：

- 先跑 smoke
- 再看截图
- 仍有疑问时用 `/doko` 看真实渲染

不要让 `/doko` 承担自动化断言职责。

### 6.3 新脚本优先沿当前命名方式继续

目前 `taskNNN-...-smoke.js` 已经形成了可追踪的任务链，后续建议继续沿用，避免引入第二套命名体系。

---

如果后续要继续推进，最值得做的不是重写测试体系，而是：

1. 给最常用的 smoke 补统一截图/trace 产物
2. 再补一份“如何读取 smoke 结果”的轻量脚本或汇总器

这样能直接解决当前最真实的痛点：`A + C`，也就是“结果更直观、测试更像真的看过页面”。
