三个独立 bug 修复: 1. MindmapTree StrictMode 双重渲染: Effect 1 创建 mm 实例, Effect 2 设置数据, StrictMode 下 Effect 1 重建 mm 但 Effect 2 不重新运行 → 新 mm 无数据 修复: 在 Effect 1 中用 useStore.getState().tree 设置初始数据 2. LinkGraph onSwitchToEditor 不稳定: App 传内联箭头函数每次渲染新引用, 导致 D3 力导向图 Effect 2 依赖变化不断重置 → 图谱闪烁/不显示 修复: useCallback 稳定 switchToEditor 引用 3. frontmatter 块式 YAML 列表解析: parseList 只支持内联 [a, b] 格式, 用户手写 links:/tags: 多行 - item 格式无法解析 → 知识图谱无边 修复: parseYaml 支持 YAML 块式列表 (- item 多行) 新增 E2E 测试 graph-render.spec.ts 验证思维导图+知识图谱渲染内容 新增单元测试覆盖块式 YAML 列表解析
67 lines
2.7 KiB
TypeScript
67 lines
2.7 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
||
import { splitFrontmatter, joinFrontmatter } from '../../apps/desktop/src/main/safe/frontmatter-codec';
|
||
import type { DocMeta } from '@hunter/shared';
|
||
|
||
describe('splitFrontmatter', () => {
|
||
it('无 frontmatter 的纯 Markdown', () => {
|
||
const { meta, body } = splitFrontmatter('# 标题\n正文');
|
||
expect(meta).toEqual({});
|
||
expect(body).toBe('# 标题\n正文');
|
||
});
|
||
it('解析四字段', () => {
|
||
const raw = '---\ntitle: 我的标题\ntags: [a, b]\nstar: true\nlinks: [x/y.md]\n---\n# 正文';
|
||
const { meta, body } = splitFrontmatter(raw);
|
||
expect(meta).toEqual<DocMeta>({ title: '我的标题', tags: ['a','b'], star: true, links: ['x/y.md'] });
|
||
expect(body).toBe('# 正文');
|
||
});
|
||
it('块式 YAML 列表(多行 - item)', () => {
|
||
const raw = '---\ntitle: 标题\nlinks:\n - a.md\n - b.md\ntags:\n - x\n - y\n---\n# 正文';
|
||
const { meta, body } = splitFrontmatter(raw);
|
||
expect(meta.links).toEqual(['a.md', 'b.md']);
|
||
expect(meta.tags).toEqual(['x', 'y']);
|
||
expect(meta.title).toBe('标题');
|
||
expect(body).toBe('# 正文');
|
||
});
|
||
it('块式列表后紧跟其他字段', () => {
|
||
const raw = '---\nlinks:\n - a.md\nstar: true\n---\n# B';
|
||
const { meta } = splitFrontmatter(raw);
|
||
expect(meta.links).toEqual(['a.md']);
|
||
expect(meta.star).toBe(true);
|
||
});
|
||
it('带引号的值', () => {
|
||
const { meta } = splitFrontmatter('---\ntitle: "含: 冒号"\n---\n');
|
||
expect(meta.title).toBe('含: 冒号');
|
||
});
|
||
it('空数组与 false 不解析为有值', () => {
|
||
const { meta } = splitFrontmatter('---\ntags: []\nstar: false\n---\n');
|
||
expect(meta.tags).toEqual([]);
|
||
expect(meta.star).toBe(false);
|
||
});
|
||
});
|
||
|
||
describe('joinFrontmatter (FR-M2 精简)', () => {
|
||
it('空 meta 不写围栏', () => {
|
||
expect(joinFrontmatter({}, '# 正文')).toBe('# 正文');
|
||
});
|
||
it('默认值不落盘:star:false / 空数组', () => {
|
||
const out = joinFrontmatter({ star: false, tags: [], links: [] }, 'body');
|
||
expect(out).toBe('body');
|
||
});
|
||
it('star:true 落盘', () => {
|
||
const out = joinFrontmatter({ star: true }, 'body');
|
||
expect(out).toContain('star: true');
|
||
});
|
||
it('含特殊字符的标题加引号', () => {
|
||
const out = joinFrontmatter({ title: 'a:b#c' }, 'body');
|
||
expect(out).toContain('title: "a:b#c"');
|
||
});
|
||
it('往返:解析后再序列化保持等价', () => {
|
||
const raw = '---\ntitle: T\ntags: [x, y]\nstar: true\n---\n# H\n正文';
|
||
const { meta, body } = splitFrontmatter(raw);
|
||
const out = joinFrontmatter(meta, body);
|
||
const again = splitFrontmatter(out);
|
||
expect(again.meta).toEqual(meta);
|
||
expect(again.body).toBe(body);
|
||
});
|
||
});
|