Skip to content

Commit f33a268

Browse files
chaxusclaude
andcommitted
test(landing): read the generated pages from the render, not from disk
Only Lint was red on #192, and all three failures were the same thing: the landing pages are now rendered by bin/build-pages.mjs at build/dev time and deliberately not committed, but three tests still read them out of public/. They passed locally because a build had left the files there -- i.e. they were asserting against my last build's leftovers -- and failed on a clean checkout. generated-pages 62 uncommitted outputs reported "stale" design-contract found 5 pages, expected >15 landing-pages ENOENT public/webmcp-document-editor.html generate({ outDir: null }) already renders every page in memory, which is what the body of landing-pages.test.ts uses, so the remaining three switch to it. The WebMCP and homepage checks now iterate every locale that has the page instead of naming en and zh, so the next language is covered for free. Also untracks public/zh-CN/index.html: the only output that has to stay in the repo is the root index.html (a Rollup entry -- a build must not depend on a plugin having written its inputs). ja/index.html was already ignored, so half the homepages were committed and half were not. check() now consults an explicit COMMITTED set instead of every output. Reverse-verified: appending a comment to index.html reddens the staleness check; renaming the shell's <header class="bar"> reddens the chrome check on every generated page; renaming a tool in lib/web-mcp.ts reddens the WebMCP check. Ran in a clean worktree with no generated pages on disk (CI's shape): 47 files, 1255 tests green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 21713f8 commit f33a268

8 files changed

Lines changed: 139 additions & 492 deletions

File tree

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public/zh-CN/convert/csv-to-xlsx.html
7777
public/help.html
7878
public/help/
7979
public/changelog.html
80+
public/zh-CN/index.html
8081
public/zh-CN/help.html
8182
public/zh-CN/help/
8283
public/zh-CN/changelog.html

‎bin/build-pages.mjs‎

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@
1313
* all locales of the same slug to each other (hreflang + language select).
1414
* Adding a locale to LOCALES is enough for the shell; content is per file.
1515
*
16-
* Output is deterministic and committed (public/ is served as-is by vite dev
17-
* and copied by the build); bin/build.sh runs this first, and
18-
* test/unit/generated-pages.test.ts fails when a committed page is stale.
16+
* Output is deterministic and NOT committed: the vite plugin `generated-pages`
17+
* writes it at dev/build time, and .gitignore keeps it out of the repo so a
18+
* page cannot go stale. The one exception is COMMITTED below -- the root
19+
* index.html, which Rollup takes as a build entry -- and `--check` is what
20+
* keeps that one honest.
1921
*
2022
* node bin/build-pages.mjs # write into public/
21-
* node bin/build-pages.mjs --check # exit 1 if any output would change
23+
* node bin/build-pages.mjs --check # exit 1 if a committed output would change
2224
* node bin/build-pages.mjs --out DIR # write elsewhere (tests)
2325
*/
2426
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
@@ -1070,10 +1072,20 @@ export function generate({ outDir = resolve(ROOT, 'public') } = {}) {
10701072
return outputs;
10711073
}
10721074

1073-
/** True when every committed output matches a fresh render. */
1075+
/**
1076+
* The outputs that stay in the repo. Only the default locale's homepage: it is
1077+
* index.html at the repo root, a Vite build entry, and a build must not depend
1078+
* on a plugin having written its inputs first. Every other page -- including
1079+
* the other locales' homepages -- is generated into a gitignored path, so
1080+
* there is nothing to go stale and nothing to check.
1081+
*/
1082+
const COMMITTED = new Set(['index.html']);
1083+
1084+
/** The committed outputs a fresh render would change (empty when all current). */
10741085
export function check({ publicDir = resolve(ROOT, 'public') } = {}) {
10751086
const stale = [];
10761087
for (const o of generate({ outDir: null })) {
1088+
if (!COMMITTED.has(o.rel)) continue;
10771089
const target = pathFor(o, publicDir);
10781090
if (!existsSync(target) || readFileSync(target, 'utf8') !== o.html) stale.push(o.rel);
10791091
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# PR #192 的 Lint 红:单测还在磁盘上找那些"不再入库"的页面
2+
3+
日期:2026-08-22
4+
分支:`landing-generated`(PR #192)
5+
现象:只有 `Lint and Validate` 红,三条用例挂,E2E 全绿。
6+
7+
## 挂的是哪三条
8+
9+
```
10+
generated-pages.test.ts has no committed page that a fresh render would change
11+
→ ['ja/index.html', 'help.html', …(62)] 不等于 []
12+
design-contract.test.ts finds the pages (sanity) → expected 5 to be greater than 15
13+
landing-pages.test.ts the WebMCP page lists exactly the tools the adapter registers
14+
→ ENOENT public/webmcp-document-editor.html
15+
```
16+
17+
三条是同一个原因:这个 PR 把落地页改成"由 `bin/build-pages.mjs` 在 build/dev 时
18+
渲染进 public/,产物不入库",而这三个用例还在从磁盘读那些产物。本地全绿只是因为
19+
本地跑过 dev/build——CI 是干净 checkout,public/ 下根本没有这些文件。
20+
21+
也就是说:**它们过去测的是"我这台机器上一次构建的残留"**。
22+
23+
## 改法:谁生成的就问谁要
24+
25+
`generate({ outDir: null })` 已经能在内存里渲染出全部页面(`landing-pages.test.ts`
26+
的主体早就这么做了),把剩下三处也换过去:
27+
28+
- `design-contract.test.ts` 的 page chrome:磁盘只留手写页(`404.html`、
29+
`embed-demo.html`、`history.html`),生成页走内存渲染。
30+
- `landing-pages.test.ts` 的 WebMCP 用例:改成遍历所有 `webmcp-document-editor.html`
31+
的渲染结果,不再只查 en/zh 两个写死路径。
32+
- `sw-register.test.ts` 的两条首页策略用例:同理改成遍历 `kind === 'home'` 的渲染,
33+
新增一个语言自动被覆盖(这条本轮还没红,但下一步会红,见下)。
34+
35+
## 顺手拔掉的一根刺:`public/zh-CN/index.html` 还在库里
36+
37+
`check()` 原本拿**每一个**输出去比磁盘,而"入库"的其实只该有一个——仓库根的
38+
`index.html`,因为它是 Rollup 的构建入口,构建不能依赖插件先跑过。`ja/index.html`
39+
上一个提交已经进了 `.gitignore`,`zh-CN/index.html` 却漏了,于是同一类文件一半入库
40+
一半不入库。
41+
42+
现在 `bin/build-pages.mjs` 里显式写出 `COMMITTED = new Set(['index.html'])`,`check()`
43+
只看它;`public/zh-CN/index.html` 从索引里删掉并进 `.gitignore`。落地页的语言从此
44+
真的只是 `content/<locale>/` 一个目录。
45+
46+
## 反向验证(三条都做了)
47+
48+
| 拆掉什么 | 应该红的用例 | 结果 |
49+
| ----------------------------------------------- | -------------------------------------------------------------- | ---- |
50+
| 往 `index.html` 尾部追加一行注释 | generated-pages「committed page…」 | 红 |
51+
| 生成器 shell 的 `<header class="bar">` 改成 nav | design-contract「carries the site header」(每个生成页各一条) | 红 |
52+
| `lib/web-mcp.ts` 的 `get_document_state` 改名 | landing-pages「WebMCP page lists…」 | 红 |
53+
54+
跑法:在 `scratchpad/` 建一个干净 worktree(软链 `node_modules`),那里 public/ 下
55+
没有任何生成页,正是 CI 的样子。修完 47 个测试文件 1255 条全绿。
56+
57+
## 教训
58+
59+
产物不入库是对的(不会过期),但**用例得跟着换水源**。判断标准很简单:这个文件
60+
`.gitignore` 里有没有?有,就不能 `readFileSync`。

0 commit comments

Comments
 (0)