# HTML 生成与预览覆盖矩阵(含 Express 路由回归缺口)
## 结论
当前仓库并不是“完全没有”覆盖项目内生成 HTML 文件和 HTML 预览,而是:
- 已经有 **daemon 单测**、**web 组件测试**、**Playwright E2E** 三层覆盖;
- 但历史上没有把“**真实 daemon + raw HTML 路由 + iframe 成功渲染**”这条链路固定成足够强的硬门禁;
- 因此像 Express 4 -> 5 这种会影响通配路由、`req.params`、静态/raw 文件服务的底层升级,可能会绕过大量邻近测试,最终表现为“HTML 黑屏”。
## 相关 bad case
- [PR #2311](https://github.com/nexu-io/open-design/pull/2311)
`chore(deps): upgrade express 4 -> 5 in daemon`
- 用户反馈的问题形态:项目内生成的 HTML 文件无法正常渲染,预览黑屏。
这类问题的危险点在于:
1. 文件可能已经成功生成;
2. 前端 iframe 也可能已经挂载;
3. 但 iframe 请求的 `/api/projects/:id/raw/*.html` 路由在 Express 升级后失配、取不到内容、或返回错误响应;
4. 最终用户看到的是“黑屏”,而不是明显的接口 404 提示。
## 现有覆盖
### 1. 项目内生成 HTML 文件
#### E2E / 集成
- [/Users/mac/open-design/open-design-home-entry/e2e/ui/real-daemon-run.test.ts](/Users/mac/open-design/open-design-home-entry/e2e/ui/real-daemon-run.test.ts)
- 真实 run 生成 `.html` 文件
- 文件落盘
- 预览 iframe 可见
- iframe 内 heading/text 可见
- follow-up turn 再生成 HTML
- 失败态不留脏文件
- [/Users/mac/open-design/open-design-home-entry/e2e/tests/dialog/artifact-consistency.test.ts](/Users/mac/open-design/open-design-home-entry/e2e/tests/dialog/artifact-consistency.test.ts)
- run 状态、assistant message、持久化文件、`artifactManifest.renderer='html'`、原始 HTML 内容一致性
#### daemon 单测
- [/Users/mac/open-design/open-design-home-entry/apps/daemon/tests/run-artifacts.test.ts](/Users/mac/open-design/open-design-home-entry/apps/daemon/tests/run-artifacts.test.ts)
- HTML artifact 识别、计数、成功/失败归因
- [/Users/mac/open-design/open-design-home-entry/apps/daemon/tests/artifact-create.test.ts](/Users/mac/open-design/open-design-home-entry/apps/daemon/tests/artifact-create.test.ts)
- [/Users/mac/open-design/open-design-home-entry/apps/daemon/tests/artifacts-cli.test.ts](/Users/mac/open-design/open-design-home-entry/apps/daemon/tests/artifacts-cli.test.ts)
- [/Users/mac/open-design/open-design-home-entry/apps/daemon/tests/mcp-create-artifact.test.ts](/Users/mac/open-design/open-design-home-entry/apps/daemon/tests/mcp-create-artifact.test.ts)
- daemon / CLI / MCP 创建 artifact 路径
### 2. HTML 文件预览
#### web 组件测试
- [/Users/mac/open-design/open-design-home-entry/apps/web/tests/components/file-viewer-render-mode.test.ts](/Users/mac/open-design/open-design-home-entry/apps/web/tests/components/file-viewer-render-mode.test.ts)
- HTML 预览走 `url-load` 还是 `srcdoc`
- deck / comment / edit / inspect / draw / tweaks / forceInline 等模式分流
- [/Users/mac/open-design/open-design-home-entry/apps/web/tests/components/PreviewModal.test.tsx](/Users/mac/open-design/open-design-home-entry/apps/web/tests/components/PreviewModal.test.tsx)
- preview modal sandbox
- deck srcdoc 处理
- [/Users/mac/open-design/open-design-home-entry/apps/web/tests/components/preview-modal-unavailable-state.test.tsx](/Users/mac/open-design/open-design-home-entry/apps/web/tests/components/preview-modal-unavailable-state.test.tsx)
- 无 HTML 预览的占位状态
- [/Users/mac/open-design/open-design-home-entry/apps/web/tests/components/preview-modal-error-state.test.tsx](/Users/mac/open-design/open-design-home-entry/apps/web/tests/components/preview-modal-error-state.test.tsx)
- 预览加载错误态
- [/Users/mac/open-design/open-design-home-entry/apps/web/tests/components/FileViewer.test.tsx](/Users/mac/open-design/open-design-home-entry/apps/web/tests/components/FileViewer.test.tsx)
- URL-load iframe
- srcdoc transport reactivation
- 切 tab/切 source/切 view 后的 iframe 行为
- deck / bridge / download / render-mode 相关细节
#### E2E / 页面恢复
- [/Users/mac/open-design/open-design-home-entry/e2e/ui/app-restoration.test.ts](/Users/mac/open-design/open-design-home-entry/e2e/ui/app-restoration.test.ts)
- artifact tab 恢复
- deep link 到 `.html`
- 返回项目根后预览继续保持
- later run 覆盖最新 artifact 预览
- failed send 后 retry 恢复预览
- [/Users/mac/open-design/open-design-home-entry/e2e/ui/app-design-files.test.ts](/Users/mac/open-design/open-design-home-entry/e2e/ui/app-design-files.test.ts)
- HTML / image / source 文件预览路径
- `image-preview.html` 渲染图片资源
- [/Users/mac/open-design/open-design-home-entry/e2e/ui/app-manual-edit.test.ts](/Users/mac/open-design/open-design-home-entry/e2e/ui/app-manual-edit.test.ts)
- HTML 预览 iframe 下的手动编辑与预览联动
### 3. raw HTML 路由与 iframe 特殊头
- [/Users/mac/open-design/open-design-home-entry/apps/daemon/tests/origin-validation.test.ts](/Users/mac/open-design/open-design-home-entry/apps/daemon/tests/origin-validation.test.ts)
- `Origin: null` 的 raw-file 预览路由访问
- [/Users/mac/open-design/open-design-home-entry/apps/daemon/tests/server-cors.test.ts](/Users/mac/open-design/open-design-home-entry/apps/daemon/tests/server-cors.test.ts)
- `srcdoc iframe` 相关 CORS 头
- [/Users/mac/open-design/open-design-home-entry/apps/daemon/tests/plugins-preview-route.test.ts](/Users/mac/open-design/open-design-home-entry/apps/daemon/tests/plugins-preview-route.test.ts)
- HTML preview route 包裹/重写逻辑
## 为什么历史上没拦住 Express 4 -> 5 的黑屏
### 1. 当时主要验证的是 daemon 包级测试和视觉回归
在 [PR #2311](https://github.com/nexu-io/open-design/pull/2311) 里,验证重心是:
- `pnpm --filter @open-design/daemon test`
- `pnpm guard`
- `pnpm typecheck`
这些能抓到很多类型和路由注册问题,但不等于覆盖“**真实生成后的 HTML 在浏览器 iframe 里是否成功渲染**”。
### 2. 很多测试只覆盖了前端预览行为,不一定穿过 Express 通配路由坏路径
例如:
- `FileViewer` / `PreviewModal` / render-mode 测试
主要验证:
- 该走 `url-load` 还是 `srcdoc`
- iframe sandbox / bridge / mode 切换
但它们未必真的依赖 Express 5 下的:
- `/api/projects/:id/raw/*splat`
- `req.params.splat`
- 实际 HTML 内容读取和返回
### 3. 真实坏点在后端路由层,而 UI 可能还“看起来正常”
Express 5 的典型风险点:
- `/*` 需要改成 `/*splat`
- `req.params[0]` 要改成 `req.params.splat`
- wildcard 参数变成 `string | string[]`
如果这里改漏:
- HTML 文件仍可能成功写入项目目录;
- iframe 仍然被前端渲染出来;
- 但 iframe 请求的 raw HTML 路由失效,结果就是黑屏。
这类问题如果测试只断言:
- 有 iframe
- 有 artifact tab
- 有文件记录
就拦不住。
## 当前最能拦住这类事故的测试
如果把“Express 升级导致 HTML 黑屏”当成一个必须阻止的回归,现在最关键的是这几条:
### P0
- [/Users/mac/open-design/open-design-home-entry/e2e/ui/real-daemon-run.test.ts](/Users/mac/open-design/open-design-home-entry/e2e/ui/real-daemon-run.test.ts)
- 真 run 生成 HTML
- iframe 内看到 heading/text
- [/Users/mac/open-design/open-design-home-entry/e2e/tests/dialog/artifact-consistency.test.ts](/Users/mac/open-design/open-design-home-entry/e2e/tests/dialog/artifact-consistency.test.ts)
- 文件、manifest、assistant message、raw HTML 一致
- [/Users/mac/open-design/open-design-home-entry/apps/daemon/tests/origin-validation.test.ts](/Users/mac/open-design/open-design-home-entry/apps/daemon/tests/origin-validation.test.ts)
- raw HTML 路由在 iframe 场景下仍然可用
### P1
- [/Users/mac/open-design/open-design-home-entry/e2e/ui/app-restoration.test.ts](/Users/mac/open-design/open-design-home-entry/e2e/ui/app-restoration.test.ts)
- HTML 预览恢复、deep link、artifact tab 回放
- [/Users/mac/open-design/open-design-home-entry/apps/web/tests/components/FileViewer.test.tsx](/Users/mac/open-design/open-design-home-entry/apps/web/tests/components/FileViewer.test.tsx)
- URL-load / srcdoc 路径的前端渲染决策
## 仍建议补的缺口
### 1. 更聚焦的 daemon raw-route 回归测试
建议新增一条更直白的 daemon 路由契约测试,专门锁:
- `/api/projects/:id/raw/foo/bar/index.html`
- 返回 200
- `content-type` 为 HTML
- body 内包含预期 HTML 内容
- `Origin: null` 下仍允许 iframe 访问
这样可以直接拦住:
- wildcard 没命中
- `req.params.splat` 拼接错误
- 读取 path 错误
### 2. E2E 明确断言“不是只有 iframe 可见,而是 iframe 内真的有内容”
所有关键 HTML 预览 E2E 都建议守住:
- `page.getByTestId('artifact-preview-frame')` 可见
- `page.frameLocator('[data-testid="artifact-preview-frame"]').getByRole(...)` 可见
而不是只看 iframe 容器存在。
### 3. 把 HTML 预览 smoke 放进更硬的 CI 档位
如果这类场景被视为高风险回归,建议:
- `real-daemon-run` 至少保留 1 条 HTML 生成 + 预览 smoke 作为阻塞 PR
- 不要只放在夜间或非阻塞套件里
## 建议的最小门禁集合
如果目标只是“以后别再让 Express / server 升级把 HTML 预览搞黑屏”,最小集合建议是:
1. `e2e/ui/real-daemon-run.test.ts`
- 真实生成 HTML 后 iframe 内看到 heading
2. `e2e/tests/dialog/artifact-consistency.test.ts`
- artifact 文件、manifest、消息、raw HTML 一致
3. 新增 daemon raw-route 契约测试
- 明确锁 `/api/projects/:id/raw/*splat`
这 3 条合起来,比单纯靠视觉回归或组件测试更能直接拦住这类事故。