mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-07-21 10:15:47 +00:00
183 lines
5.9 KiB
TypeScript
183 lines
5.9 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
|
|
import {
|
|
handleRunStream,
|
|
mockLangGraphAPI,
|
|
MOCK_THREAD_ID,
|
|
} from "./utils/mock-api";
|
|
|
|
const MOCK_AGENTS = [
|
|
{
|
|
name: "test-agent",
|
|
description: "A test agent for E2E tests",
|
|
system_prompt: "You are a test agent.",
|
|
},
|
|
{
|
|
name: "second-agent",
|
|
description: "Another test agent for E2E tests",
|
|
system_prompt: "You are another test agent.",
|
|
},
|
|
];
|
|
|
|
test.describe("Agent chat", () => {
|
|
test("agent gallery page loads and shows agents", async ({ page }) => {
|
|
mockLangGraphAPI(page, { agents: MOCK_AGENTS });
|
|
|
|
await page.goto("/workspace/agents");
|
|
|
|
// The agent card should appear with the agent name
|
|
await expect(page.getByText("test-agent")).toBeVisible({
|
|
timeout: 15_000,
|
|
});
|
|
});
|
|
|
|
test("agent chat page loads with input box", async ({ page }) => {
|
|
mockLangGraphAPI(page, { agents: MOCK_AGENTS });
|
|
|
|
await page.goto("/workspace/agents/test-agent/chats/new");
|
|
|
|
// The prompt input textarea should be visible
|
|
const textarea = page.getByPlaceholder(/how can i assist you/i);
|
|
await expect(textarea).toBeVisible({ timeout: 15_000 });
|
|
});
|
|
|
|
test("keeps new-chat drafts isolated between agents", async ({ page }) => {
|
|
mockLangGraphAPI(page, { agents: MOCK_AGENTS });
|
|
|
|
await page.goto("/workspace/agents/test-agent/chats/new");
|
|
const firstAgentInput = page.getByPlaceholder(/how can i assist you/i);
|
|
await expect(firstAgentInput).toBeVisible({ timeout: 15_000 });
|
|
await firstAgentInput.fill("Draft for the first agent");
|
|
|
|
await page.goto("/workspace/agents/second-agent/chats/new");
|
|
const secondAgentInput = page.getByPlaceholder(/how can i assist you/i);
|
|
await expect(secondAgentInput).toHaveValue("");
|
|
await secondAgentInput.fill("Draft for the second agent");
|
|
|
|
await page.goto("/workspace/agents/test-agent/chats/new");
|
|
await expect(page.getByPlaceholder(/how can i assist you/i)).toHaveValue(
|
|
"Draft for the first agent",
|
|
);
|
|
});
|
|
|
|
test("agent chat page shows agent badge", async ({ page }) => {
|
|
mockLangGraphAPI(page, { agents: MOCK_AGENTS });
|
|
|
|
await page.goto("/workspace/agents/test-agent/chats/new");
|
|
|
|
// The agent badge should display in the header (scoped to header to avoid
|
|
// matching the welcome area which also shows the agent name)
|
|
await expect(
|
|
page.locator("header span", { hasText: "test-agent" }),
|
|
).toBeVisible({ timeout: 15_000 });
|
|
});
|
|
|
|
test("agent chat can regenerate its latest response", async ({ page }) => {
|
|
const humanMessage = {
|
|
type: "human",
|
|
id: "msg-human-agent",
|
|
content: [{ type: "text", text: "Original agent question" }],
|
|
};
|
|
const aiMessage = {
|
|
type: "ai",
|
|
id: "msg-ai-agent",
|
|
content: "Custom agent response",
|
|
};
|
|
mockLangGraphAPI(page, {
|
|
agents: MOCK_AGENTS,
|
|
threads: [
|
|
{
|
|
thread_id: MOCK_THREAD_ID,
|
|
title: "Agent conversation",
|
|
agent_name: "test-agent",
|
|
messages: [humanMessage, aiMessage],
|
|
},
|
|
],
|
|
});
|
|
|
|
let prepareMessageId: string | undefined;
|
|
let streamBody: Record<string, unknown> | undefined;
|
|
await page.route(
|
|
`**/api/threads/${MOCK_THREAD_ID}/runs/regenerate/prepare`,
|
|
(route) => {
|
|
prepareMessageId = (
|
|
route.request().postDataJSON() as { message_id?: string }
|
|
).message_id;
|
|
return route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
input: { messages: [humanMessage] },
|
|
checkpoint: {
|
|
checkpoint_id: "checkpoint-before-human",
|
|
checkpoint_ns: "",
|
|
checkpoint_map: null,
|
|
},
|
|
metadata: {
|
|
regenerate_from_message_id: aiMessage.id,
|
|
regenerate_from_run_id: `run-${MOCK_THREAD_ID}`,
|
|
regenerate_checkpoint_id: "checkpoint-before-human",
|
|
},
|
|
target_run_id: `run-${MOCK_THREAD_ID}`,
|
|
}),
|
|
});
|
|
},
|
|
);
|
|
await page.route(
|
|
`**/api/langgraph/threads/${MOCK_THREAD_ID}/runs/stream`,
|
|
(route) => {
|
|
streamBody = route.request().postDataJSON() as Record<string, unknown>;
|
|
return handleRunStream(route);
|
|
},
|
|
);
|
|
|
|
await page.goto(`/workspace/agents/test-agent/chats/${MOCK_THREAD_ID}`);
|
|
await expect(page.getByText(aiMessage.content)).toBeVisible({
|
|
timeout: 15_000,
|
|
});
|
|
|
|
await page.evaluate((selectedText) => {
|
|
const element = Array.from(document.querySelectorAll("p")).find(
|
|
(candidate) => candidate.textContent?.includes(selectedText),
|
|
);
|
|
const textNode = element?.firstChild;
|
|
if (!element || !textNode) {
|
|
throw new Error("Unable to find the custom agent response");
|
|
}
|
|
const range = document.createRange();
|
|
range.selectNodeContents(textNode);
|
|
const selection = window.getSelection();
|
|
selection?.removeAllRanges();
|
|
selection?.addRange(range);
|
|
element.dispatchEvent(new MouseEvent("mouseup", { bubbles: true }));
|
|
}, aiMessage.content);
|
|
await expect(
|
|
page.getByRole("button", { name: "Ask in side chat" }),
|
|
).toBeVisible();
|
|
await page.keyboard.press("Escape");
|
|
|
|
const assistantTurn = page.locator("[data-assistant-turn]").last();
|
|
await assistantTurn.hover();
|
|
await page.getByRole("button", { name: "Regenerate" }).click();
|
|
|
|
await expect.poll(() => prepareMessageId).toBe(aiMessage.id);
|
|
await expect.poll(() => streamBody).toBeDefined();
|
|
expect(streamBody).toMatchObject({
|
|
checkpoint: {
|
|
checkpoint_id: "checkpoint-before-human",
|
|
checkpoint_ns: "",
|
|
checkpoint_map: null,
|
|
},
|
|
metadata: {
|
|
regenerate_from_message_id: aiMessage.id,
|
|
regenerate_from_run_id: `run-${MOCK_THREAD_ID}`,
|
|
regenerate_checkpoint_id: "checkpoint-before-human",
|
|
},
|
|
context: {
|
|
agent_name: "test-agent",
|
|
thread_id: MOCK_THREAD_ID,
|
|
},
|
|
});
|
|
});
|
|
});
|