import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import test from "node:test"; import { homeSurface as pageSource } from "./home-surface.ts"; const composerSource = readFileSync(new URL("../src/components/chat-composer.tsx", import.meta.url), "utf8"); const draftStoreSource = readFileSync(new URL("../src/lib/composer-draft.ts", import.meta.url), "utf8"); // 原值:homeSurface 连同独立 /people 表单一起检查聊天根草稿。 // 新值:Home 及其业务 hooks;原隔离断言不变。 // 原因:人物编辑表单的局部 draft 不属于聊天根状态,不能把它误判为击键重渲染 Home。 const chatOwnerSource = [ "../src/app/(app)/page.tsx", "../src/hooks/use-session-management.ts", "../src/hooks/use-consultation-run.ts", "../src/hooks/use-profile-onboarding.ts", "../src/hooks/use-rectification-surface.ts", "../src/hooks/use-synastry.ts", ].map((path) => readFileSync(new URL(path, import.meta.url), "utf8")).join("\n"); function sourceBetween(source: string, startMarker: string, endMarker: string) { const start = source.indexOf(startMarker); const end = source.indexOf(endMarker, start); assert.notEqual(start, -1); assert.notEqual(end, -1); return source.slice(start, end); } test("keystrokes never re-render the chat page root", () => { // Given: the composer text used to live in page-level React state. assert.doesNotMatch(chatOwnerSource, /const \[draft, setDraft\] = useState/); assert.doesNotMatch(pageSource, /const \[draftTheme, setDraftTheme\] = useState/); assert.doesNotMatch(pageSource, /const \[draftEntrypoint, setDraftEntrypoint\] = useState/); // When: the page keeps only the hidden routing values it reads at submit time. assert.match(pageSource, /const draftTheme = useRef\(null\)/); assert.match(pageSource, /const draftEntrypoint = useRef\(null\)/); // Then: every writer the page still exposes updates a ref or the external store. assert.match(pageSource, /function setDraft\(value: string\) \{\n setComposerDraft\(value\);\n \}/); assert.match(pageSource, /function setDraftTheme\(theme: Theme \| null\) \{\n draftTheme\.current = theme;\n \}/); assert.match(pageSource, /function setDraftEntrypoint\(entrypoint: ConsultationEntrypoint \| null\) \{\n draftEntrypoint\.current = entrypoint;\n \}/); assert.match(pageSource, /import \{ composerDraftSnapshot, setComposerDraft \} from "@\/lib\/composer-draft"/); assert.match(pageSource, /import \{ showChatNotice as setComposerNotice \} from "@\/lib\/chat-notice"/); }); test("the textarea lives in an isolated composer that owns the draft subscription", () => { // Given: the page renders the composer without handing it the draft text. assert.match(pageSource, /import \{ ChatComposer \} from "@\/components\/chat-composer"/); const composerElement = sourceBetween(pageSource, ""); assert.doesNotMatch(composerElement, /\bdraft=/); assert.doesNotMatch(pageSource, /