import test from "node:test"; import assert from "node:assert/strict"; import path from "node:path"; import { readFile } from "node:fs/promises"; import { fileURLToPath } from "node:url"; import { parseChatMarkdown } from "../src/lib/chat-markdown"; const testsDir = path.dirname(fileURLToPath(import.meta.url)); async function readWorkspaceFile(relativePath: string) { return readFile(path.join(testsDir, "..", relativePath), "utf8"); } test("parseChatMarkdown recognizes section labels and preserves reading order", () => { const blocks = parseChatMarkdown( "项目目标:完成 Boss 真机回归\n当前进度:已完成 UI 调整\n下一步:推送到 Gitea", ); assert.equal(blocks.length, 3); assert.deepEqual( blocks.map((block) => block.kind), ["label", "label", "label"], ); assert.equal(blocks[0]?.label, "项目目标"); assert.equal(blocks[0]?.text, "完成 Boss 真机回归"); assert.equal(blocks[1]?.label, "当前进度"); assert.equal(blocks[2]?.label, "下一步"); }); test("parseChatMarkdown keeps bullets, ordered items, quotes, and fenced code distinct", () => { const blocks = parseChatMarkdown( "# 标题\n\n- 第一项\n1. 第二项\n> 引用\n```ts\nconst ok = true;\n```", ); assert.deepEqual( blocks.map((block) => block.kind), ["heading", "bullet", "ordered", "quote", "code"], ); assert.equal(blocks[0]?.text, "标题"); assert.equal(blocks[1]?.text, "第一项"); assert.equal(blocks[2]?.order, "1."); assert.equal(blocks[3]?.text, "引用"); assert.match(blocks[4]?.text ?? "", /const ok = true;/); }); test("ChatBubble renders parsed markdown blocks instead of raw plain text bodies", async () => { const source = await readWorkspaceFile("src/components/app-ui.tsx"); assert.match(source, /import \{ parseChatMarkdown(?:, type ChatMarkdownBlock)? \} from "@\/lib\/chat-markdown"/); assert.match(source, /const blocks = parseChatMarkdown\(body\);/); assert.match(source, /function ChatBubbleMarkdown/); });