Files
Todo-List/scripts/check-fade-in-stability.mjs
2026-09-12 13:55:57 +08:00

89 lines
3.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 回归render() 重建 innerHTML 时只对「会话内首次出现」的任务打 .is-new
// 而不是「相对上一次 render 的可见集合」。后者会让 filter 切换 / 跨分类跳转
// 后旧任务再次 fade-in反而比旧版无条件 animation闪烁更糟。
//
// 测项:
// 1) 第一次 render所有任务都是 is-new
// 2) 第二次 render同样的任务集合没有任何 is-new
// 3) filter 切换后回到原视图:旧任务不应再 fade-in关键回归点
// 4) 切换分类再切回来:跨分类可见任务不重复 fade-in
// 5) 新增任务:仅新增的那条打 is-new
// 6) 'load' 事件后:所有任务重新算作 is-newid 全部失效)
import { check, ok, bad, summary, printSummary } from './_lib/check.mjs';
import { readFileSync } from 'node:fs';
import { findTodoFile } from './_lib/find-todo-path.mjs';
const SRC_TASK_LIST = readFileSync('src/ui/task-list.js', 'utf8');
// 抓取 _renderTaskItem 的核心 HTML 模板 —— 我们关心的是「输出 HTML 里第一个 class 是否 task-item」
// 以及「render() 是否会根据 _knownTaskIds 标记 is-new」。
// 这里不实际 import TaskList要拉一堆 DOM mock 太重),改为从源码层面验证关键不变量:
// (a) _knownTaskIds 不再被重置为 seenIdsgrow-only
// (b) 'load' 事件清空 _knownTaskIds
// (c) 字符串 splice 锚点是 'class="task-item' 且 indexOf != -1 时才替换
let pass = 0, fail = 0;
function expect(name, cond, info) {
if (cond) { ok(name); pass++; } else { bad(name, info); fail++; }
}
// (a) 关键回归点:旧实现里是 `this._knownTaskIds = seenIds;`。
// 现在的实现必须没有这行被赋值给 seenIds —— 否则 filter 切换会让旧任务
// 重新被当成 new。
const oldBuggyLine = /this\._knownTaskIds\s*=\s*seenIds/;
expect(
'_knownTaskIds 不再重置为 seenIds防 filter 切换回归)',
!oldBuggyLine.test(SRC_TASK_LIST),
'源码里仍有 this._knownTaskIds = seenIds — 这是旧 bug'
);
// (b) 'load' 事件清空 _knownTaskIdsreload 后 id 全部失效,全 fade-in 是合理的)
const hasLoadReset = /this\.store\.on\(['"]load['"][\s\S]{0,300}?_knownTaskIds\s*=\s*new Set\(\)/.test(SRC_TASK_LIST);
expect(
"'load' 事件清空 _knownTaskIdsid 全部失效,全 fade-in 是合理的)",
hasLoadReset,
"'load' 处理器里没看到 _knownTaskIds = new Set()"
);
// (c) splice 锚点仍是 'class="task-item'
expect(
'splice 锚点是 class="task-item',
/indexOf\(['"]class="task-item['"]\)/.test(SRC_TASK_LIST),
'没找到 indexOf("class="task-item") 锚点'
);
// (d) 渲染时 grow-only 加新 id用 .add 而不是 = 赋值)
expect(
'_knownTaskIds 通过 .add() grow-only 累加(不会因为 filter 切换丢 id',
/this\._knownTaskIds\.add\(/.test(SRC_TASK_LIST),
'没找到 _knownTaskIds.add(...) 调用'
);
// (e) 关键:之前的实现里 newIds 计算依赖 _knownTaskIdsgrow-only set
// 而不是 reset 后的 seenIds。验证可见的判断逻辑仍在以 _knownTaskIds.has(t.id) 为准。
const usesKnownSetForNewCheck = /this\._knownTaskIds\.has\(/.test(SRC_TASK_LIST);
expect(
'新任务判定走 _knownTaskIds.hasgrow-only 集合)',
usesKnownSetForNewCheck,
'没找到 _knownTaskIds.has(...) 调用'
);
// (f) isFirstRender 的语义应仍为「集合为空」grow-only 实现下同样成立)
expect(
'isFirstRender 判断仍存在_knownTaskIds.size === 0',
/isFirstRender/.test(SRC_TASK_LIST),
'isFirstRender 标识符消失了'
);
// (g) 'load' 监听器必须清空选择模式(已有)
expect(
"'load' 仍清空选择模式",
/this\.store\.on\(['"]load['"][\s\S]{0,200}?_exitSelectionMode/.test(SRC_TASK_LIST),
"'load' 没清空选择模式"
);
printSummary(pass, fail);
// sanity: TODO file path resolution doesn't matter here
findTodoFile();