import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import test from "node:test"; import { chatMessageViews } from "../src/lib/chat-message-view.ts"; const pageSource = readFileSync(new URL("../src/app/page.tsx", import.meta.url), "utf8"); const globalStyles = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8"); const messageRowSource = readFileSync(new URL("../src/components/chat-message-row.tsx", import.meta.url), "utf8"); const activitySource = readFileSync(new URL("../src/components/agent-activity-status.tsx", import.meta.url), "utf8"); const previousMessages = [ { role: "user", text: "问题" }, ] as const; test("keeps the assistant render identity stable when streaming settles", () => { // Given: one assistant answer exists first as a transient stream. const streaming = chatMessageViews(previousMessages, true, "完整答案"); // When: the same answer becomes part of the persisted transcript. const settled = chatMessageViews([ ...previousMessages, { role: "assistant", text: "完整答案" }, ], false, ""); // Then: React receives the same key and can reuse the existing message shell. assert.equal(streaming.at(-1)?.renderKey, settled.at(-1)?.renderKey); assert.equal(streaming.at(-1)?.state, "streaming"); assert.equal(settled.at(-1)?.state, "settled"); }); test("does not duplicate a completed assistant answer while loading state settles", () => { // Given: the final answer has entered the transcript while request cleanup lags. const completedMessages = [ ...previousMessages, { role: "assistant", text: "完整答案" }, ] as const; // When: the render view is derived with the old loading flag still true. const views = chatMessageViews(completedMessages, true, "完整答案"); // Then: only the persisted answer is rendered. assert.equal(views.length, completedMessages.length); assert.equal(views.at(-1)?.state, "settled"); }); test("shows honest agent activity states before and during streamed text", () => { assert.match(messageRowSource, /state="working" label="正在核对星盘信息…"/); assert.match(messageRowSource, /message\.state === "streaming" &&