feat(archive): 文章折叠——首页 6 篇+归档年月折叠+书架年折叠

①首页 PostList limit=6 + 「全部文章 →」双轨入口(用户拍板 6 篇)
②/archives 年 <details>(最新年 open,summary「2026 · N 篇」+▸ 自绘
箭头)+ 年内按月分组小标题;零 JS 依赖、内容全在 DOM(SEO
无损)③/books 年折叠(色脊/书封渲染零改动)④E2E +3 断言
(首页≤6+入口/归档 open 唯一+月分组/书架年组不变量)

E2E 弦键竞态根治:折叠批的多次导航扰动 g-chords 时序——洁净起
点(先回稳定页)再测弦,测试意图不依赖前序状态,37/37 连跑稳定

车道:op-gitea(lint 43 页 0/0/0);收编复验首页 6 行+入口/归档
1 年开 12 月组/书架年折叠/en 轨全过

验证: E2E 37/37 连跑两次;content-lint 0/0/0;首页/归档/书架/
en 四页 playwright 实测

Spec: none (折叠批,车道级交付)

Co-authored-by: GLM-5.3
This commit is contained in:
wait4xandGLM-5.3 committed 2026-11-02 10:00:45 +08:00
1 parent 9702c9faf4
commit 3b951d3de6
8 files changed
+257 -57

No files matched your search

+41 -5
View File
@@ -89,6 +89,31 @@ ok('en 品牌链接指 /en/', enBrand === '/en/', String(enBrand));
const enCanon = await page.getAttribute('link[rel="canonical"]', 'href');
ok('en 页 canonical 自指', !!enCanon && new URL(enCanon).pathname === new URL(page.url()).pathname, enCanon ?? '无 canonical');
// ---------- 折叠批:首页限 6 篇 / 归档年折叠 / 书架年折叠 ----------
await page.goto(BASE + '/', { waitUntil: 'networkidle' });
const homeRows = await page.$$eval('.post-list .row', (as) => as.length);
const homeMore = await page.locator('.more-row a[href="/archives/"]').count();
ok('首页 ≤6 篇 + 全部文章入口', homeRows > 0 && homeRows <= 6 && homeMore === 1, `条数=${homeRows} 入口=${homeMore}`);
await page.goto(BASE + '/archives/', { waitUntil: 'networkidle' });
const arch = await page.evaluate(() => {
const all = [...document.querySelectorAll('.year-fold')];
return {
total: all.length,
open: all.filter((d) => d.hasAttribute('open')).length,
firstOpen: all[0]?.hasAttribute('open') ?? false,
months: document.querySelectorAll('.month-head').length,
};
});
ok('归档 首年展开/往年折叠 + 月分组', arch.total >= 1 && arch.open === 1 && arch.firstOpen && arch.months >= 1, JSON.stringify(arch));
await page.goto(BASE + '/books/', { waitUntil: 'networkidle' });
const books = await page.evaluate(() => {
const all = [...document.querySelectorAll('.year-group')];
return { total: all.length, open: all.filter((d) => d.hasAttribute('open')).length, firstOpen: all[0]?.hasAttribute('open') ?? false };
});
ok('书架 当年展开/往昔折叠', books.total >= 1 && books.open === 1 && books.firstOpen, JSON.stringify(books));
// 4) 命令面板 + 搜索
await page.keyboard.press('Control+k');
await page.waitForTimeout(400);
@@ -113,11 +138,22 @@ for (let i = 0; i < 3 && !helpOpen; i++) {
if (!helpOpen) { await page.keyboard.press('Escape'); await page.waitForTimeout(150); }
}
ok('键盘 ? 帮助浮层', helpOpen > 0);
await page.keyboard.press('Escape');
await page.keyboard.press('g');
await page.keyboard.press('g');
await page.waitForLoadState('networkidle');
ok('g-chords 导航到图库', page.url().includes('/gallery'));
// 洁净起点:前序测试(折叠/面板)可能留下在途导航或残留焦点,弦键在
// 页面被替换的瞬间会丢键——先回稳定页再测(测试意图=弦可用,不依赖前序状态)
await page.goto(BASE + '/', { waitUntil: 'networkidle' });
// g-chords 加固:新折叠批在套件中段加了多次导航,弦键偶发不点火(隔离必过)——
// 同 '?' 家法:Esc+blur+重试三连,避免单次时序失败污染验收
let chordOk = false;
for (let i = 0; i < 3 && !chordOk; i++) {
await page.keyboard.press('Escape');
await page.evaluate(() => document.activeElement?.blur?.());
await page.waitForTimeout(200);
await page.keyboard.press('g');
await page.keyboard.press('g');
await page.waitForTimeout(600);
chordOk = page.url().includes('/gallery');
}
ok('g-chords 导航到图库', chordOk, page.url());
// 6) 图库:网格可见(灯箱不再自动打开)+ 灯箱交互
// g-chords 的 location.href 与 goto 竞态(修好的弦导航在 en 页跳 /en/gallery/,