/* Electron 日历页的判据(2026-10-04)。 ★ 起因:用户实测报告「点击切换月份的按钮,直接从10月跳到八月卡死」。 我用 CDP 真机复现时看到的是: animation-play-state: running,opacity: 0, transform: translateX(-94.8px),元素 x = -15(跑出视口) ⇒ 日历整块滑出屏幕且**永不回来**。 */ import { test } from 'node:test'; import assert from 'node:assert/strict'; import { code } from './lib/read.mjs'; const src = p => code(`src/${p}`); test('★ 翻页动画状态必须是 state,不能是 ref', () => { const cal = src('components/CalendarView.tsx'); // ★ 核心:注释写着「用**声明式** key + 类」,实现却是命令式 ref。 // ref 改动不触发重渲染 ⇒ slideClass 只能用上一次的方向, // 且 class 名永不变 ⇒ 动画只播一次就停在起始帧 ⇒ 观感「卡死」。 assert.match(cal, /const \[slide, setSlide\] = useState/, '★ 翻页动画状态还在用 ref —— ref 改动不触发重渲染,动画会卡在起始帧'); assert.doesNotMatch(cal, /navigated\.current|slideDir\.current/, '★ 还在用 navigated/slideDir ref(命令式)—— 与注释宣称的「声明式」矛盾'); }); test('★ 每次翻页都要重放一次动画(key 含 seq)', () => { const cal = src('components/CalendarView.tsx'); assert.match(cal, /seq: \(s\?\.seq \?\? 0\) \+ 1/, 'seq 没有递增'); assert.match(cal, /key=\{`\$\{anchor\.getTime\(\)\}-\$\{scale\}-\$\{slide\?\.seq \?\? 0\}`\}/, '★ key 不含 seq —— 同方向连续翻页时动画可能不重放'); }); test('★ 动画结束必须摘掉 class(fill-mode:both 的兜底)', () => { const cal = src('components/CalendarView.tsx'); // ★ animation-fill-mode: both 会把元素留在帧之间。动画一旦没跑到终点 // (连点 / 切后台 / reduced-motion 切换),元素就永久停在 // translateX(-12%) + opacity:0 —— 实测元素 x=-15,整块日历跑出视口。 assert.match(cal, /setTimeout\(\(\) => setSlide\(null\)/, '★ 动画结束后没有清掉 class —— 元素会永久停在动画帧上(用户实测「卡死」)'); }); test('翻页方向只有一个来源(按钮与手势共用 shift)', () => { const cal = src('components/CalendarView.tsx'); assert.match(cal, /function shift\(dir: 1 \| -1\)/, '没有统一的 shift()'); const n = (cal.match(/shift\(/g) || []).length; assert.ok(n >= 3, `shift 只有 ${n} 处引用,按钮/手势可能各写一套`); }); test('翻页用函数式更新(连点不丢更新)', () => { const cal = src('components/CalendarView.tsx'); assert.match(cal, /setAnchor\(a => addMonths\(a, dir\)\)/, '★ setAnchor 没用函数式更新 —— 连点时会基于同一个旧 anchor 算,跳月会丢步'); assert.match(cal, /addDays\(a, dir \* 7\)/, '周视图没有按 7 天翻页'); }); test('addMonths 先归到 1 号(setMonth 会溢出)', () => { const lib = src('lib/calendar.ts'); assert.match(lib, /new Date\(d\.getFullYear\(\), d\.getMonth\(\) \+ n, 1\)/, '★ addMonths 没有先归到 1 号 —— 1月31日加一月会溢出到 3 月'); });