feat: 聊天页优化 — 欢迎页、标题自动生成、消息操作
This commit is contained in:
167
tests/web/components/ChatPanel.test.tsx
Normal file
167
tests/web/components/ChatPanel.test.tsx
Normal file
@@ -0,0 +1,167 @@
|
||||
import { fireEvent, screen, waitFor } from "@testing-library/react";
|
||||
import { describe, expect, mock, test } from "bun:test";
|
||||
import { createElement } from "react";
|
||||
|
||||
import type { Conversation, Model } from "../../../src/shared/api";
|
||||
|
||||
import { ChatPanel } from "../../../src/web/consoles/workbench/components/chat/ChatPanel";
|
||||
import { installFetchMock, jsonResponse, renderWithProviders } from "../test-utils";
|
||||
|
||||
const PROJECT_ID = "proj-1";
|
||||
|
||||
const TEXT_MODEL: Model = {
|
||||
capabilities: ["text"],
|
||||
contextLength: null,
|
||||
createdAt: "2024-01-01T00:00:00.000Z",
|
||||
id: "model-1",
|
||||
maxOutputTokens: null,
|
||||
modelId: "gpt-4o",
|
||||
name: "GPT-4o",
|
||||
providerId: "pv1",
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
};
|
||||
|
||||
const CONVERSATION: Conversation = {
|
||||
createdAt: "2024-01-01T00:00:00.000Z",
|
||||
id: "conv-1",
|
||||
modelId: "model-1",
|
||||
projectId: PROJECT_ID,
|
||||
title: "新会话",
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
};
|
||||
|
||||
const noop = () => {
|
||||
return undefined;
|
||||
};
|
||||
|
||||
void mock.module("@ai-sdk/react", () => ({
|
||||
useChat: () => ({
|
||||
messages: [],
|
||||
regenerate: noop,
|
||||
sendMessage: noop,
|
||||
setMessages: (msgs: unknown) => msgs,
|
||||
status: "ready",
|
||||
stop: noop,
|
||||
}),
|
||||
}));
|
||||
|
||||
void mock.module("ai", () => ({
|
||||
DefaultChatTransport: function () {
|
||||
return undefined;
|
||||
},
|
||||
}));
|
||||
|
||||
function getSendButton() {
|
||||
return screen.getByRole("button", { name: /发.*送/ });
|
||||
}
|
||||
|
||||
function setupFetchMock() {
|
||||
return installFetchMock((call) => {
|
||||
if (call.url.includes("/models")) {
|
||||
return jsonResponse({ items: [TEXT_MODEL], total: 1 });
|
||||
}
|
||||
if (call.url.endsWith("/conversations") && call.method === "POST") {
|
||||
return jsonResponse({ conversation: { ...CONVERSATION, id: "conv-new" } }, { status: 201 });
|
||||
}
|
||||
if (/\/conversations\/conv-1$/.exec(call.url)) {
|
||||
return jsonResponse({ conversation: CONVERSATION });
|
||||
}
|
||||
if (call.url.includes("/messages")) {
|
||||
return jsonResponse({ items: [], total: 0 });
|
||||
}
|
||||
if (/\/conversations$/.exec(call.url) && call.method === "GET") {
|
||||
return jsonResponse({ items: [], total: 0 });
|
||||
}
|
||||
return jsonResponse({ error: "not found" }, { status: 404 });
|
||||
});
|
||||
}
|
||||
|
||||
describe("ChatPanel", () => {
|
||||
describe("欢迎页", () => {
|
||||
test("无会话时显示欢迎页和输入框", () => {
|
||||
setupFetchMock();
|
||||
renderWithProviders(
|
||||
createElement(ChatPanel, {
|
||||
conversationId: null,
|
||||
onConversationCreated: noop,
|
||||
projectId: PROJECT_ID,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(screen.getByText("你好,我是阿福")).toBeTruthy();
|
||||
expect(screen.getByText("有什么我可以帮助你的吗?")).toBeTruthy();
|
||||
expect(screen.getByPlaceholderText("输入消息...")).toBeTruthy();
|
||||
expect(getSendButton()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("自动创建会话", () => {
|
||||
test("输入并发送后自动创建会话并通知父组件", async () => {
|
||||
const calls = setupFetchMock();
|
||||
const onCreated = mock<(id: string) => void>(() => undefined);
|
||||
|
||||
renderWithProviders(
|
||||
createElement(ChatPanel, {
|
||||
conversationId: null,
|
||||
onConversationCreated: onCreated,
|
||||
projectId: PROJECT_ID,
|
||||
}),
|
||||
);
|
||||
|
||||
const input = screen.getByPlaceholderText("输入消息...");
|
||||
fireEvent.change(input, { target: { value: "你好" } });
|
||||
fireEvent.click(getSendButton());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onCreated).toHaveBeenCalledWith("conv-new");
|
||||
});
|
||||
|
||||
const createCall = calls.find((c) => c.url.endsWith("/conversations") && c.method === "POST");
|
||||
expect(createCall).toBeTruthy();
|
||||
});
|
||||
|
||||
test("创建会话失败时恢复输入文本", async () => {
|
||||
installFetchMock((call) => {
|
||||
if (call.url.includes("/models")) {
|
||||
return jsonResponse({ items: [TEXT_MODEL], total: 1 });
|
||||
}
|
||||
if (call.url.endsWith("/conversations") && call.method === "POST") {
|
||||
return jsonResponse({ error: "服务器错误" }, { status: 500 });
|
||||
}
|
||||
return jsonResponse({ error: "not found" }, { status: 404 });
|
||||
});
|
||||
|
||||
renderWithProviders(
|
||||
createElement(ChatPanel, {
|
||||
conversationId: null,
|
||||
onConversationCreated: noop,
|
||||
projectId: PROJECT_ID,
|
||||
}),
|
||||
);
|
||||
|
||||
const input = screen.getByPlaceholderText("输入消息...");
|
||||
fireEvent.change(input, { target: { value: "测试输入" } });
|
||||
fireEvent.click(getSendButton());
|
||||
|
||||
await waitFor(() => {
|
||||
expect((input as HTMLTextAreaElement).value).toBe("测试输入");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("聊天面板", () => {
|
||||
test("选中会话时显示消息列表区域", () => {
|
||||
setupFetchMock();
|
||||
renderWithProviders(
|
||||
createElement(ChatPanel, {
|
||||
conversationId: "conv-1",
|
||||
onConversationCreated: noop,
|
||||
projectId: PROJECT_ID,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(screen.queryByText("你好,我是阿福")).toBeNull();
|
||||
expect(screen.getByPlaceholderText("输入消息...")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user