Files
Todo-List/scripts/check-picker-timeout.mjs
2026-09-12 13:55:57 +08:00

111 lines
4.4 KiB
JavaScript
Raw Permalink 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.
// 回归测试H6 — picker 路径尝试次数上限 + 降级为 fallback
//
// 背景toggleTask 在「原分类丢失 → picker 分支」时返回 restoreHintUI 弹
// 选择器让用户选目标分类。但如果用户反复 toggle 同一任务(原分类持续丢
// 失 / 用户反复取消 pickerpicker 会被反复触发,可能进入死循环或内存
// 累积。修复store 内部记 _pickerAttempts[taskId],超过 _PICKER_ATTEMPT_LIMIT
// 后强制降级为 fallback落「未分类」避免 picker 风暴。
//
// 跑法node scripts/check-picker-timeout.mjs
import { check, summary, printSummary } from './_lib/check.mjs';
import { makeStore } from './_lib/store-fixture.mjs';
const BASE_DOC = `# 全部任务
## 工作
- [ ] 写周报
## 学习
- [ ] 看 Rust 入门
## 未分类
`;
// ── [1] 第一次 picker 命中 ──
console.log('\n[1] 首次 picker原分类丢失 → restoreHint.kind === "picker"');
{
const store = makeStore(BASE_DOC);
const workCat = store.getCategory('工作');
const task = workCat.tasks.find(t => t.text === '写周报');
store.toggleTask('工作', task.id); // 勾选 → 进「已完成任务」
// 删除「工作」→ 让原分类丢失
store.deleteCategory('工作');
const r = store.toggleTask('已完成任务', task.id);
check('首次 picker 路径返回 restoreHint.kind === "picker"',
r && r.restoreHint && r.restoreHint.kind === 'picker',
`restoreHint=${JSON.stringify(r?.restoreHint)}`);
check('picker 带 missing = "工作"',
r && r.restoreHint && r.restoreHint.missing === '工作');
}
// ── [2] 同一任务反复 togglepicker 不收敛)→ 降级为 fallback ──
console.log('\n[2] picker 不收敛时降级为 fallback');
{
const store = makeStore(BASE_DOC);
const workCat = store.getCategory('工作');
const task = workCat.tasks.find(t => t.text === '写周报');
// 让任务进入「已完成任务」,再删工作 → 进入 picker 路径
store.toggleTask('工作', task.id);
store.deleteCategory('工作');
// 反复 toggle 同一任务picker 不收敛 —— 用户反复取消选择对话框)
// 记录每次的 restoreHint直到出现 fallback不要用「最后一次」——
// fallback 触发后任务已搬到「未分类」,后续 toggleTask 返回值可能不再带 restoreHint
let fallbackResult = null;
const hintTrace = [];
for (let i = 0; i < 6 && !fallbackResult; i++) {
const r = store.toggleTask('已完成任务', task.id);
hintTrace.push(r?.restoreHint?.kind || 'no-result');
if (r?.restoreHint?.kind === 'fallback') fallbackResult = r;
}
// 断言:循环内出现了至少一次 fallback
check('循环内出现 fallbackpicker 降级)',
fallbackResult !== null,
`hint trace: ${hintTrace.join(' → ')}`);
check('fallback restoreHint 带 forceDowngrade=true',
fallbackResult && fallbackResult.restoreHint && fallbackResult.restoreHint.forceDowngrade === true,
`restoreHint=${JSON.stringify(fallbackResult?.restoreHint)}`);
// 兜底:任务应落到「未分类」
check('降级后任务落到「未分类」',
store.getCategory('未分类').tasks.some(t => t.text === '写周报'));
}
// ── [3] 不同任务互不干扰 ──
console.log('\n[3] 不同任务的 picker 计数独立');
{
const store = makeStore(BASE_DOC);
const workCat = store.getCategory('工作');
const studyCat = store.getCategory('学习');
const t1 = workCat.tasks.find(t => t.text === '写周报');
const t2 = studyCat.tasks.find(t => t.text === '看 Rust 入门');
// 两条都 toggle 进「已完成任务」
store.toggleTask('工作', t1.id);
store.toggleTask('学习', t2.id);
// 删「工作」让 t1 走 picker
store.deleteCategory('工作');
store.toggleTask('已完成任务', t1.id); // 1 次 picker
store.toggleTask('已完成任务', t1.id); // 2 次 pickerpicker 不动任务)
store.toggleTask('已完成任务', t1.id); // 3 次 picker
// t2 此时 picker 计数应独立(仍是 0
const r2 = store.toggleTask('已完成任务', t2.id);
// t2 的原分类「学习」还在 → 走 category 分支(不是 picker
check('独立任务互不干扰 —— t2 走 category 分支',
r2 && r2.restoreHint && r2.restoreHint.kind === 'category',
`r2.restoreHint=${JSON.stringify(r2?.restoreHint)}`);
}
printSummary('check-picker-timeout');