update
This commit is contained in:
111
scripts/check-picker-timeout.mjs
Normal file
111
scripts/check-picker-timeout.mjs
Normal file
@@ -0,0 +1,111 @@
|
||||
// 回归测试:H6 — picker 路径尝试次数上限 + 降级为 fallback
|
||||
//
|
||||
// 背景:toggleTask 在「原分类丢失 → picker 分支」时返回 restoreHint,UI 弹
|
||||
// 选择器让用户选目标分类。但如果用户反复 toggle 同一任务(原分类持续丢
|
||||
// 失 / 用户反复取消 picker),picker 会被反复触发,可能进入死循环或内存
|
||||
// 累积。修复: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] 同一任务反复 toggle(picker 不收敛)→ 降级为 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('循环内出现 fallback(picker 降级)',
|
||||
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 次 picker(picker 不动任务)
|
||||
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');
|
||||
Reference in New Issue
Block a user