新增 TurnList 组件,优化消息渲染逻辑
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
import "@testing-library/jest-dom";
|
||||
import React from "react";
|
||||
import { render } from "@testing-library/react";
|
||||
|
||||
import { AgentWorkspace } from "./AgentWorkspace";
|
||||
import type { Message } from "./GlobalChatbox.types";
|
||||
|
||||
const renderCounts = new Map<string, number>();
|
||||
|
||||
jest.mock("next/image", () => ({
|
||||
__esModule: true,
|
||||
default: (props: React.ImgHTMLAttributes<HTMLImageElement>) => <img {...props} alt={props.alt ?? ""} />,
|
||||
}));
|
||||
|
||||
jest.mock("framer-motion", () => ({
|
||||
AnimatePresence: ({ children }: { children: React.ReactNode }) => <>{children}</>,
|
||||
motion: {
|
||||
div: ({ children, ...props }: React.HTMLAttributes<HTMLDivElement>) => <div {...props}>{children}</div>,
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock("./GlobalChatbox.parts", () => ({
|
||||
TypingIndicator: () => <div>typing</div>,
|
||||
}));
|
||||
|
||||
jest.mock("./AgentTurn", () => ({
|
||||
AgentTurn: ({ message }: { message: Message }) => {
|
||||
renderCounts.set(message.id, (renderCounts.get(message.id) ?? 0) + 1);
|
||||
return <div data-testid={`turn-${message.id}`}>{message.content}</div>;
|
||||
},
|
||||
}));
|
||||
|
||||
describe("AgentWorkspace", () => {
|
||||
const defaultProps = {
|
||||
branchGroups: [],
|
||||
branchTransition: null,
|
||||
bottomRef: { current: null },
|
||||
speakingMessageId: null,
|
||||
speechState: "idle" as const,
|
||||
onSpeak: jest.fn(),
|
||||
onPauseSpeech: jest.fn(),
|
||||
onResumeSpeech: jest.fn(),
|
||||
onStopSpeech: jest.fn(),
|
||||
isTtsSupported: false,
|
||||
onRegenerate: jest.fn(),
|
||||
onEditResubmit: jest.fn(),
|
||||
onCycleBranch: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
renderCounts.clear();
|
||||
});
|
||||
|
||||
it("keeps stable history turns from re-rendering while the last assistant message streams", () => {
|
||||
const userMessage: Message = {
|
||||
id: "user-1",
|
||||
role: "user",
|
||||
content: "question",
|
||||
};
|
||||
const assistantHistoryMessage: Message = {
|
||||
id: "assistant-1",
|
||||
role: "assistant",
|
||||
content: "stable answer",
|
||||
};
|
||||
const streamingMessage: Message = {
|
||||
id: "assistant-2",
|
||||
role: "assistant",
|
||||
content: "partial",
|
||||
};
|
||||
|
||||
const { rerender } = render(
|
||||
<AgentWorkspace
|
||||
{...defaultProps}
|
||||
isStreaming
|
||||
messages={[userMessage, assistantHistoryMessage, streamingMessage]}
|
||||
/>,
|
||||
);
|
||||
|
||||
const updatedStreamingMessage: Message = {
|
||||
...streamingMessage,
|
||||
content: "partial with more tokens",
|
||||
};
|
||||
|
||||
rerender(
|
||||
<AgentWorkspace
|
||||
{...defaultProps}
|
||||
isStreaming
|
||||
messages={[userMessage, assistantHistoryMessage, updatedStreamingMessage]}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(renderCounts.get("user-1")).toBe(1);
|
||||
expect(renderCounts.get("assistant-1")).toBe(1);
|
||||
expect(renderCounts.get("assistant-2")).toBe(2);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user