feat(ux): R1+R2 审稿裁决人体学 — 冲突卡内联片段 + 分组/排序/跳下一条/主次按钮
R1: 新 lib/review/snippet.ts 从 where 解析段号取终稿命中段预览,ConflictCard 渲染引用块。 R2: 新 lib/review/grouping.ts 按 type 分组+严重度排序(仅改显示顺序,保留原始 index 守 conflict_index 不变量),组计数徽标、跳到下一条未裁决(环回)、采纳改法主按钮/忽略手改次级。 TDD: snippet 8 测 + grouping 10 测。前端门禁绿: lint/tsc/vitest 201/build。
This commit is contained in:
50
apps/web/lib/review/snippet.test.ts
Normal file
50
apps/web/lib/review/snippet.test.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { conflictSnippet, parseParagraphNo } from "./snippet";
|
||||
|
||||
describe("parseParagraphNo", () => {
|
||||
it("extracts 1-based paragraph number from 第 N 段 wording", () => {
|
||||
expect(parseParagraphNo("第 3 段,主角忽然示弱")).toBe(3);
|
||||
expect(parseParagraphNo("第9段老者道破来历")).toBe(9);
|
||||
});
|
||||
|
||||
it("returns null when no paragraph number present", () => {
|
||||
expect(parseParagraphNo("战斗场景")).toBeNull();
|
||||
expect(parseParagraphNo("开篇提到三日后")).toBeNull();
|
||||
expect(parseParagraphNo("")).toBeNull();
|
||||
});
|
||||
|
||||
it("takes the first number for a range like 第 4–6 段", () => {
|
||||
expect(parseParagraphNo("第 4–6 段反复描写天气")).toBe(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe("conflictSnippet", () => {
|
||||
const text = "第一段正文。\n\n第二段正文。\n\n第三段正文,命中冲突的句子。";
|
||||
|
||||
it("returns the trimmed paragraph for a parseable where", () => {
|
||||
expect(conflictSnippet(text, "第 3 段,主角忽然示弱")).toBe(
|
||||
"第三段正文,命中冲突的句子。",
|
||||
);
|
||||
});
|
||||
|
||||
it("returns null when paragraph number is unparseable", () => {
|
||||
expect(conflictSnippet(text, "战斗场景")).toBeNull();
|
||||
});
|
||||
|
||||
it("returns null when paragraph index is out of range", () => {
|
||||
expect(conflictSnippet(text, "第 9 段")).toBeNull();
|
||||
});
|
||||
|
||||
it("returns null when the located paragraph is empty", () => {
|
||||
expect(conflictSnippet("\n\n\n\n正文", "第 1 段")).toBeNull();
|
||||
});
|
||||
|
||||
it("truncates long paragraphs with an ellipsis", () => {
|
||||
const long = "甲".repeat(200);
|
||||
const snippet = conflictSnippet(long, "第 1 段");
|
||||
expect(snippet).not.toBeNull();
|
||||
expect(snippet!.length).toBeLessThanOrEqual(81);
|
||||
expect(snippet!.endsWith("…")).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user