This commit is contained in:
2026-09-12 13:55:57 +08:00
commit 30f1cedd39
95 changed files with 42057 additions and 0 deletions

View File

@@ -0,0 +1,122 @@
// 回归测试H5 — [⭐] / [▶] 行尾原子块识别
//
// 背景:旧版 MARKER_TAIL_RE 强制前导 `\s`,导致:
// - `unimportant[⭐]`(任务文本紧贴 [⭐],无空格)→ 解析为普通文本important=false
// - 下次保存 serialize 后,原文 `unimportant[⭐]` → 变成 `unimportant`(标记丢失)
//
// 修复v3.7+tail 正则去掉 `\s` 前导,`[⭐]` / `[▶]` 在行尾被识别为
// 内联原子块(不是「必须前面有空白字符」)。本脚本验证这条修复路径。
//
// 跑法node scripts/check-marker-tail.mjs
import {
parseMarkdown,
extractInlineMarkers
} from '../src/markdown-parser.js';
import { check, ok, summary, printSummary } from './_lib/check.mjs';
// ── [1] extractInlineMarkers行尾 [⭐] 不再要求前导空白 ──
console.log('\n[1] extractInlineMarkers 行尾 [⭐] / [▶] 不要求前导空白');
{
// 修复前:\s 前导强制要求 → 失败;修复后:直接 [⭐]$ 匹配
const r1 = extractInlineMarkers('unimportant[⭐]');
check('unimportant[⭐](无前导空白)→ important=true',
r1.important === true,
`important=${r1.important}, text=${r1.text}`);
check('unimportant[⭐] 文本剥到只剩 unimportant',
r1.text === 'unimportant',
`text=${JSON.stringify(r1.text)}`);
// 带前导空白的老用法 —— 仍然支持(无回归)
const r2 = extractInlineMarkers('important task [⭐]');
check('带空白的 important task [⭐] 仍识别',
r2.important === true && r2.text === 'important task',
`important=${r2.important}, text=${JSON.stringify(r2.text)}`);
// 行尾 [▶] 同款修复
const r3 = extractInlineMarkers('focus[▶]');
check('focus[▶](无前导空白)→ current=true',
r3.current === true && r3.text === 'focus',
`current=${r3.current}, text=${JSON.stringify(r3.text)}`);
// 行尾同时有 ⭐ 和 ▶ —— 不带空格紧凑写法
const r4 = extractInlineMarkers('critical work[⭐][▶]');
check('critical work[⭐][▶] 两个标记都识别',
r4.important === true && r4.current === true,
`important=${r4.important}, current=${r4.current}, text=${JSON.stringify(r4.text)}`);
// loop 顺序剥:先 [⭐] 后 [▶]text 应剥到 "critical work"
check('critical work[⭐][▶] 文本剥到只剩 critical work',
r4.text === 'critical work',
`text=${JSON.stringify(r4.text)}`);
// 行内非尾部 [⭐] 不应被剥离(中间嵌入文本不应误识别)
const r5 = extractInlineMarkers('关键任务 [⭐] 描述');
// 这里 [⭐] 在中间 —— 不在行尾,按设计应该不剥离(不是 H5 修复范围)
// 行为约定MARKER_TAIL_RE 的 `$` 锚点要求标记在行尾;中间位置不识别。
check('行内 [⭐] 不被尾部规则剥离(不在 H5 修复范围)',
r5.text.includes('[⭐]') && !r5.important,
`text=${JSON.stringify(r5.text)}, important=${r5.important}`);
// 不含标记的纯文本
const r6 = extractInlineMarkers('just text');
check('无标记 → important/current 都 falsetext 不变',
!r6.important && !r6.current && r6.text === 'just text');
}
// ── [2] parseMarkdown 端到端:行尾 [⭐] 被解析为 important ──
console.log('\n[2] parseMarkdown 端到端:行尾 [⭐] 解析为 important');
{
const md = `# 全部任务
## 工作
- [ ] unimportant[⭐]
- [ ] 关注今日[▶]
- [ ] 双标[⭐][▶]
- [ ] 普通任务
## 未分类
`;
const cats = parseMarkdown(md);
const work = cats.find(c => c.name === '工作');
const find = (text) => work.tasks.find(t => t.text === text);
const t1 = find('unimportant');
check('「工作」下 unimportant[⭐] → text=unimportant, important=true',
!!t1 && t1.important === true && t1.completed === false,
`task=${JSON.stringify(t1)}`);
const t2 = find('关注今日');
check('「工作」下 关注今日[▶] → text=关注今日, current=true',
!!t2 && t2.current === true && t2.important === false,
`task=${JSON.stringify(t2)}`);
const t3 = find('双标');
check('「工作」下 双标[⭐][▶] → 两种标记同时 true',
!!t3 && t3.important === true && t3.current === true,
`task=${JSON.stringify(t3)}`);
const t4 = find('普通任务');
check('「工作」下 普通任务 → 标记均为 false',
!!t4 && !t4.important && !t4.current);
}
// ── [3] 写回往返important 不会丢 ──
console.log('\n[3] 写回往返:行尾 [⭐] 经 serialize → parse 后标记保留');
{
// 这里只验证解析侧的不可逆writeMarkdown 单测已在 check-md-ui-sync 覆盖):
// 解析出来后task.important === true ⇒ 文本里仍可看到 [⭐]
// 后续写回会在 writer 端用 [⭐] 行首标记(统一格式)输出,不再依赖原始紧凑写法。
const md = `- [ ] hello[⭐]\n`;
const cats = parseMarkdown(`# 全部任务\n\n## 工作\n\n${md}\n\n## 未分类\n\n`);
const task = cats.find(c => c.name === '工作')?.tasks[0];
check('hello[⭐] 解析后 important=true持久性证据',
task && task.important === true && task.text === 'hello');
}
printSummary('check-marker-tail');