fix(frontend): reset active chat after deletion (#3519)

This commit is contained in:
zgenu
2026-06-14 22:06:19 +08:00
committed by GitHub
parent ec520e6427
commit 34e126ee4b
7 changed files with 481 additions and 51 deletions
+152 -26
View File
@@ -16,6 +16,13 @@ export const MOCK_THREAD_ID = "00000000-0000-0000-0000-000000000001";
export const MOCK_THREAD_ID_2 = "00000000-0000-0000-0000-000000000002";
export const MOCK_RUN_ID = "00000000-0000-0000-0000-000000000099";
const MOCK_AUTH_USER = {
id: "default",
email: "default@test.local",
system_role: "admin",
needs_setup: false,
};
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
@@ -71,6 +78,21 @@ const DEFAULT_SKILLS: MockSkill[] = [
},
];
function mockStreamMessages() {
return [
{
type: "human",
id: "msg-human-1",
content: [{ type: "text", text: "Hello" }],
},
{
type: "ai",
id: "msg-ai-1",
content: "Hello from DeerFlow!",
},
];
}
// ---------------------------------------------------------------------------
// mockLangGraphAPI
// ---------------------------------------------------------------------------
@@ -81,23 +103,84 @@ const DEFAULT_SKILLS: MockSkill[] = [
* for a real backend.
*/
export function mockLangGraphAPI(page: Page, options?: MockAPIOptions) {
const threads = options?.threads ?? [];
let threads = [...(options?.threads ?? [])];
const agents = options?.agents ?? [];
const skills = options?.skills ?? DEFAULT_SKILLS;
const upsertThread = (thread: MockThread) => {
threads = [
thread,
...threads.filter((existing) => existing.thread_id !== thread.thread_id),
];
};
const threadSearchResult = (thread: MockThread) => ({
thread_id: thread.thread_id,
created_at: "2025-01-01T00:00:00Z",
updated_at: thread.updated_at ?? "2025-01-01T00:00:00Z",
metadata: {
...(thread.metadata ?? {}),
...(thread.agent_name ? { agent_name: thread.agent_name } : {}),
},
status: "idle",
values: { title: thread.title ?? "Untitled" },
});
// Auth — keep workspace tests independent from a real gateway session.
void page.route("**/api/v1/auth/me", (route) => {
if (route.request().method() === "GET") {
return route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify(MOCK_AUTH_USER),
});
}
return route.fallback();
});
void page.route("**/api/v1/auth/setup-status", (route) => {
if (route.request().method() === "GET") {
return route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ needs_setup: false }),
});
}
return route.fallback();
});
void page.route("**/api/v1/auth/logout", (route) => {
if (route.request().method() === "POST") {
return route.fulfill({ status: 204 });
}
return route.fallback();
});
void page.route("**/api/channels/providers", (route) => {
if (route.request().method() === "GET") {
return route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ enabled: false, providers: [] }),
});
}
return route.fallback();
});
void page.route("**/api/channels/connections", (route) => {
if (route.request().method() === "GET") {
return route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ connections: [] }),
});
}
return route.fallback();
});
// Thread search — sidebar thread list & chats list page
void page.route("**/api/langgraph/threads/search", async (route) => {
const body = threads.map((t) => ({
thread_id: t.thread_id,
created_at: "2025-01-01T00:00:00Z",
updated_at: t.updated_at ?? "2025-01-01T00:00:00Z",
metadata: {
...(t.metadata ?? {}),
...(t.agent_name ? { agent_name: t.agent_name } : {}),
},
status: "idle",
values: { title: t.title ?? "Untitled" },
}));
const body = threads.map(threadSearchResult);
let limit: number | undefined;
let offset = 0;
@@ -131,6 +214,12 @@ export function mockLangGraphAPI(page: Page, options?: MockAPIOptions) {
// Thread create — called when user sends first message in a new chat
void page.route("**/api/langgraph/threads", (route) => {
if (route.request().method() === "POST") {
upsertThread({
thread_id: MOCK_THREAD_ID,
title: "New Chat",
updated_at: new Date().toISOString(),
messages: mockStreamMessages(),
});
return route.fulfill({
status: 200,
contentType: "application/json",
@@ -149,6 +238,26 @@ export function mockLangGraphAPI(page: Page, options?: MockAPIOptions) {
// Thread update (PATCH) — metadata update after creation
void page.route("**/api/langgraph/threads/*", (route) => {
const threadId = decodeURIComponent(
new URL(route.request().url()).pathname.split("/").at(-1) ?? "",
);
const matchingThread = threads.find(
(thread) => thread.thread_id === threadId,
);
if (route.request().method() === "GET") {
if (!matchingThread) {
return route.fulfill({
status: 404,
contentType: "application/json",
body: JSON.stringify({ detail: "Thread not found" }),
});
}
return route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify(threadSearchResult(matchingThread)),
});
}
if (route.request().method() === "PATCH") {
return route.fulfill({
status: 200,
@@ -156,6 +265,21 @@ export function mockLangGraphAPI(page: Page, options?: MockAPIOptions) {
body: JSON.stringify({ thread_id: MOCK_THREAD_ID }),
});
}
if (route.request().method() === "DELETE") {
threads = threads.filter((thread) => thread.thread_id !== threadId);
return route.fulfill({
status: 204,
});
}
return route.fallback();
});
void page.route(/\/api\/threads\/[^/]+$/, (route) => {
if (route.request().method() === "DELETE") {
return route.fulfill({
status: 204,
});
}
return route.fallback();
});
@@ -299,8 +423,21 @@ export function mockLangGraphAPI(page: Page, options?: MockAPIOptions) {
);
// Run stream — returns a minimal SSE response with an AI message
void page.route("**/api/langgraph/runs/stream", handleRunStream);
void page.route("**/api/langgraph/threads/*/runs/stream", handleRunStream);
const handleMockRunStream = (route: Route) => {
upsertThread({
thread_id: MOCK_THREAD_ID,
title: "New Chat",
updated_at: new Date().toISOString(),
messages: mockStreamMessages(),
});
return handleRunStream(route);
};
void page.route("**/api/langgraph/runs/stream", handleMockRunStream);
void page.route(
"**/api/langgraph/threads/*/runs/stream",
handleMockRunStream,
);
// Models list — model picker dropdown
void page.route("**/api/models", (route) => {
@@ -391,18 +528,7 @@ export function handleRunStream(route: Route) {
{
event: "values",
data: {
messages: [
{
type: "human",
id: "msg-human-1",
content: [{ type: "text", text: "Hello" }],
},
{
type: "ai",
id: "msg-ai-1",
content: "Hello from DeerFlow!",
},
],
messages: mockStreamMessages(),
},
},
{ event: "end", data: {} },