Files
Todo-List/scripts/check-category-name-chars.mjs
2026-09-12 13:55:57 +08:00

111 lines
4.1 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.
// 一次性验证脚本:分类名禁用 `|` 字符。
//
// 背景TaskList 用 `cat + '|' + id` 作为 selectedTaskIds 的 Set key_selKey
// 批量浮栏的 _updateBatchBarState / _handleBatchAction / _showBatchMoveMenu
// 都通过 key.split('|') 反查分类名与 id。
//
// 如果分类名本身含 `|`,比如 "Foo|Bar"
// - add 时的 key = "Foo|Bar|t_xxx_xxxxx"
// - split('|') → ["Foo", "Bar", "t_xxx_xxxxx"]
// - 解构 [cat, id] = key.split('|') → cat = "Foo", id = "Bar"
// - 后续 store.batchMoveTasks / batchToggleCompleted / batchDeleteTasks
// 全都按错误的 (cat, id) 去找任务 → 静默空操作 → 用户以为「批量没生效」。
//
// 防线store 的 addSubCategory / renameCategory 必须拒绝任何含 `|` 的输入。
// 这份脚本守住这条防线 —— 加了 `_taskRefKey` 死代码 + `|` 漏洞的回归测试都靠它。
//
// 用法node scripts/check-category-name-chars.mjs
import { TaskStore } from '../src/task-store.js';
import { check, ok, bad, summary, printSummary } from './_lib/check.mjs';
import { makeStore } from './_lib/store-fixture.mjs';
console.log('[1] addSubCategory 拒绝含 `|` 的名字');
{
const store = makeStore();
const names = [
'Foo|Bar', // 经典带管道符
'|leading', // 前导
'trailing|', // 末尾
'a||b', // 双管道
'工作|子分类', // 含中文字符 + |
];
for (const name of names) {
const before = store.categories.length;
const result = store.addSubCategory(name);
check(
`addSubCategory(${JSON.stringify(name)}) 返回 null`,
result === null,
`实际 ${JSON.stringify(result)}`
);
check(
`categories 长度未变`,
store.categories.length === before,
`${before}${store.categories.length}`
);
}
}
console.log('\n[2] addSubCategory 接受不含 `|` 的合法名字');
{
const store = makeStore();
const ok = store.addSubCategory('正常名字');
check('addSubCategory("正常名字") 返回 cat 对象', ok !== null);
check('分类名确实是"正常名字"', ok && ok.name === '正常名字');
}
console.log('\n[3] renameCategory 拒绝把名字改成含 `|`');
{
const store = makeStore();
store.addSubCategory('副业');
const badNames = ['Foo|Bar', 'a|', '|b'];
for (const newName of badNames) {
const ok = store.renameCategory('副业', newName);
check(
`rename('副业' → ${JSON.stringify(newName)}) 返回 false`,
ok === false,
`实际 ${JSON.stringify(ok)}`
);
check(
`「副业」名字没被改`,
store.getCategory('副业') !== null,
`当前: ${store.categories.map(c => c.name).join('、')}`
);
}
}
console.log('\n[4] 集成回归:合法分类的批量操作 key 拆 / 拼对称');
{
// 这条不属于「拒绝非法名字」的范畴,但是和 `|` 同款的端到端检查:
// 走完整 addSubCategory → addTask → 模拟 _selKey + split('|'),确认
// 「cat + '|' + id」拆出来仍然是 (cat, id) —— 不会被 split 干扰。
// 主要是防回归:将来谁把 _selKey 改成 JSON.stringify 之类方案,会让行为改变。
const store = makeStore();
// loadDefault 默认建了「工作/学习/生活/未分类」,用「副业」避开重名
const cat = store.addSubCategory('副业');
check('addSubCategory("副业") 返回非 null', cat !== null);
if (!cat) {
fail++; // 后续 check 无意义 —— 直接结束
} else {
const t = store.addTask('副业', '示例任务');
check('addTask 返回非 null', t !== null);
if (t) {
const key = `${cat.name}|${t.id}`;
const [catOut, idOut] = key.split('|');
check(`_selKey 拼出的 key 能 split 回 (cat, id)`, catOut === cat.name && idOut === t.id);
// 验证 split 不会产生多余片段(极端回归:`_selKey` 不会意外产生多余 `|`
check('split 后只有 2 段', key.split('|').length === 2);
}
}
}
console.log('\n' + '='.repeat(64));
console.log(`结果:${summary.pass} 通过, ${summary.fail} 失败`);
console.log('='.repeat(64));
process.exit(summary.fail === 0 ? 0 : 1);