Files
Agent-System/web/src/engine/autoNumber.test.ts
T

227 lines
10 KiB
TypeScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { recomputeNumbering, cleanHeadingContent } from './autoNumber';
import { markdownToBlockAST } from './markdownToBlock';
import { renderInlineMarkdown } from './renderMarkdown';
import { DocumentBlock } from '../types/block';
test('cleanHeadingContent: 手工多级序号前缀清洗与专业词汇保护', () => {
// 1. 阿拉伯数字与多级序号清洗
assert.equal(cleanHeadingContent('1. 项目研发背景与临床需求'), '项目研发背景与临床需求');
assert.equal(cleanHeadingContent('2. 监管分类与强制性合规标准矩阵'), '监管分类与强制性合规标准矩阵');
assert.equal(cleanHeadingContent('2.1. 电气安全与电磁兼容'), '电气安全与电磁兼容');
assert.equal(cleanHeadingContent('2.1 电气安全与电磁兼容'), '电气安全与电磁兼容');
assert.equal(cleanHeadingContent('1.1.1.2 绝缘距离规范要求'), '绝缘距离规范要求');
assert.equal(cleanHeadingContent('3、 核心成像光路与时序'), '核心成像光路与时序');
// 2. 中文大写与括号序号清洗
assert.equal(cleanHeadingContent('一、 临床真实世界调研'), '临床真实世界调研');
assert.equal(cleanHeadingContent('(一) 核心痛点汇总'), '核心痛点汇总');
assert.equal(cleanHeadingContent('(二) 方案可行性评估'), '方案可行性评估');
assert.equal(cleanHeadingContent('第一章 绪论与背景'), '绪论与背景');
assert.equal(cleanHeadingContent('第3节 供应链安全评估'), '供应链安全评估');
// 3. 专业术语、数字开头的规格参数严格保护(绝不误伤)
assert.equal(cleanHeadingContent('4K超高清荧光内窥镜系统'), '4K超高清荧光内窥镜系统');
assert.equal(cleanHeadingContent('5G远程外科运镜低延迟控制'), '5G远程外科运镜低延迟控制');
assert.equal(cleanHeadingContent('100% 国产化替代方案'), '100% 国产化替代方案');
assert.equal(cleanHeadingContent('2026年度预算执行计划'), '2026年度预算执行计划');
});
test('标题多层级自动编号:单 H1 文档总标题自适应模式 (1 -> 1.1 -> 1.1.1)', () => {
const root: DocumentBlock = {
id: 'root',
type: 'document',
content: '',
children: [
{ id: 'h_title', type: 'heading', level: 1, content: '4K超高清荧光内窥镜系统立项与可行性报告' },
{ id: 'h_c1', type: 'heading', level: 2, content: '项目研发背景与临床需求' },
{ id: 'h_c1_1', type: 'heading', level: 3, content: '竞品横向性能对标' },
{ id: 'h_c1_2', type: 'heading', level: 3, content: '外科临床痛点' },
{ id: 'h_c1_2_1', type: 'heading', level: 4, content: '术中低延迟实测' },
{ id: 'h_c2', type: 'heading', level: 2, content: '监管分类与强制性合规标准矩阵' },
{ id: 'h_c2_1', type: 'heading', level: 3, content: '电气安全 GB 9706.1-2020' },
{ id: 'h_c3', type: 'heading', level: 2, content: '系统架构与时序链路' },
],
};
recomputeNumbering(root);
const children = root.children!;
// H1 文档大标题免章节编号
assert.equal(children[0].numbering, undefined);
// 正文 H2 起算第一大章
assert.equal(children[1].numbering, '1');
// H3 子节
assert.equal(children[2].numbering, '1.1');
assert.equal(children[3].numbering, '1.2');
// H4 次级子节
assert.equal(children[4].numbering, '1.2.1');
// 第二大章 H2
assert.equal(children[5].numbering, '2');
assert.equal(children[6].numbering, '2.1');
// 第三大章 H2
assert.equal(children[7].numbering, '3');
});
test('标题多层级自动编号:多 H1 标准大章模式', () => {
const root: DocumentBlock = {
id: 'root',
type: 'document',
content: '',
children: [
{ id: 'h_1', type: 'heading', level: 1, content: '第一章 概述' },
{ id: 'h_1_1', type: 'heading', level: 2, content: '研究背景' },
{ id: 'h_2', type: 'heading', level: 1, content: '第二章 方案设计' },
{ id: 'h_2_1', type: 'heading', level: 2, content: '硬件系统架构' },
],
};
recomputeNumbering(root);
const children = root.children!;
assert.equal(children[0].numbering, '1');
assert.equal(children[1].numbering, '1.1');
assert.equal(children[2].numbering, '2');
assert.equal(children[3].numbering, '2.1');
});
test('跨章节图、表、公式多级按章重编号测试 (表 1-1, 表 2-1, 图 3-1, 式 (4-1))', () => {
const root: DocumentBlock = {
id: 'root',
type: 'document',
content: '',
children: [
{ id: 'h0', type: 'heading', level: 1, content: '内窥镜立项评估总报告' },
// 第一章:含两个表格、一个图
{ id: 'h1', type: 'heading', level: 2, content: '技术指标与竞品分析' },
{ id: 't1', type: 'table', content: '', properties: { caption: '竞品指标对照表' } },
{ id: 't2', type: 'table', content: '', properties: { caption: '光学分辨率实测表' } },
{ id: 'm1', type: 'mermaid', content: 'graph TD; A-->B', properties: { caption: '竞品演进路线' } },
// 第二章:含一个表格
{ id: 'h2', type: 'heading', level: 2, content: '合规标准矩阵' },
{ id: 't3', type: 'table', content: '', properties: { caption: '强标符合性明细表' } },
// 第三章:含一个流程图
{ id: 'h3', type: 'heading', level: 2, content: '系统架构设计' },
{ id: 'm2', type: 'mermaid', content: 'graph LR; A-->B', properties: { caption: '时序拓扑图' } },
// 第四章:含两个数学公式
{ id: 'h4', type: 'heading', level: 2, content: '信噪比算法模型' },
{ id: 'f1', type: 'math', content: 'SNR = 42dB', properties: { caption: '近红外信噪比公式' } },
{ id: 'f2', type: 'math', content: 'E = mc^2', properties: { caption: '能量转换判据' } },
],
};
recomputeNumbering(root);
const c = root.children!;
// 第一章
assert.equal(c[1].numbering, '1');
assert.equal(c[2].numbering, '表 1-1');
assert.equal(c[3].numbering, '表 1-2');
assert.equal(c[4].numbering, '图 1-1');
// 第二章:表计数器重置为 1,章节为 2
assert.equal(c[5].numbering, '2');
assert.equal(c[6].numbering, '表 2-1');
// 第三章:图计数器重置为 1,章节为 3
assert.equal(c[7].numbering, '3');
assert.equal(c[8].numbering, '图 3-1');
// 第四章:公式计数器按章编号
assert.equal(c[9].numbering, '4');
assert.equal(c[10].numbering, '式 (4-1)');
assert.equal(c[11].numbering, '式 (4-2)');
});
test('有序列表与无序列表编号与渲染功能测试', () => {
// 1. 行内/段落级有序列表渲染
const mdOrdered = `核心痛点如下:
1. **模式切换闪烁失真**:存在 200ms 黑屏;
2. **边缘荧光色彩溢出**:反光组织假阳性;
3. **长期灭菌镜体老化**:透光率下降。`;
const html = renderInlineMarkdown(mdOrdered, { preserveNewlines: true });
assert.ok(html.includes('<ol class="doc-ol" start="1"'));
assert.ok(html.includes('<strong>模式切换闪烁失真</strong>'));
assert.ok(html.includes('<strong>边缘荧光色彩溢出</strong>'));
assert.ok(html.includes('<strong>长期灭菌镜体老化</strong>'));
// 2. 块级独立列表解析与编号
const mdListBlock = `# 需求清单
1. 光学分光棱镜方案
2. 双路 CMOS 选型
3. FPGA 配准算法开发`;
const ast = markdownToBlockAST(mdListBlock);
const listBlock = ast.rootBlock.children?.[1];
assert.ok(listBlock);
assert.equal(listBlock?.type, 'list');
assert.equal(listBlock?.properties?.isOrdered, true);
assert.equal(listBlock?.properties?.startNumber, 1);
assert.equal(listBlock?.properties?.listItems?.length, 3);
assert.equal(listBlock?.properties?.listItems?.[0], '光学分光棱镜方案');
assert.equal(listBlock?.properties?.listItems?.[1], '双路 CMOS 选型');
assert.equal(listBlock?.properties?.listItems?.[2], 'FPGA 配准算法开发');
});
test('动态块操作闭环重编号验证 (Insert, Move, Delete)', () => {
const root: DocumentBlock = {
id: 'root',
type: 'document',
content: '',
children: [
{ id: 'h0', type: 'heading', level: 1, content: '报告总标题' },
{ id: 'h1', type: 'heading', level: 2, content: '第一章 研发背景' },
{ id: 't1', type: 'table', content: '', properties: { caption: '背景表' } },
{ id: 'h2', type: 'heading', level: 2, content: '第二章 方案规格' },
{ id: 't2', type: 'table', content: '', properties: { caption: '规格表' } },
{ id: 'm1', type: 'mermaid', content: '', properties: { caption: '架构图' } },
],
};
recomputeNumbering(root);
assert.equal(root.children![1].numbering, '1');
assert.equal(root.children![2].numbering, '表 1-1');
assert.equal(root.children![3].numbering, '2');
assert.equal(root.children![4].numbering, '表 2-1');
assert.equal(root.children![5].numbering, '图 2-1');
// 操作1:在第1章和第2章之间插入一个“合规审查”新章节 (包含一个表格)
const newHeading: DocumentBlock = {
id: 'h_insert',
type: 'heading',
level: 2,
content: '合规标准准入',
};
const newTable: DocumentBlock = {
id: 't_insert',
type: 'table',
content: '',
properties: { caption: '合规准入清单' },
};
root.children!.splice(3, 0, newHeading, newTable);
recomputeNumbering(root);
// 原先第 2 章应自动后移为第 3 章,其内部的表与图也自动变更为 3-1
assert.equal(root.children![1].numbering, '1');
assert.equal(root.children![2].numbering, '表 1-1');
assert.equal(root.children![3].numbering, '2'); // 插入的章节
assert.equal(root.children![4].numbering, '表 2-1'); // 插入的表格
assert.equal(root.children![5].numbering, '3'); // 原第2章升为第3章
assert.equal(root.children![6].numbering, '表 3-1'); // 原表2-1变表3-1
assert.equal(root.children![7].numbering, '图 3-1'); // 原图2-1变图3-1
// 操作2:删除新插入的第2章及其表格
root.children!.splice(3, 2);
recomputeNumbering(root);
// 应平滑恢复为第 1 章与第 2 章
assert.equal(root.children![1].numbering, '1');
assert.equal(root.children![2].numbering, '表 1-1');
assert.equal(root.children![3].numbering, '2');
assert.equal(root.children![4].numbering, '表 2-1');
assert.equal(root.children![5].numbering, '图 2-1');
});