103 lines
3.4 KiB
TypeScript
103 lines
3.4 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
||
import { v4 as uuid } from "uuid";
|
||
|
||
import {
|
||
THREAD_FOR_WELCOME,
|
||
newChatEntry,
|
||
openChat,
|
||
reuseThreadChatEntry,
|
||
sendMessage,
|
||
skipIfMissingThread,
|
||
waitForAnyMessages,
|
||
waitForMessageListReady,
|
||
} from "./support/chat-helpers";
|
||
|
||
test.use({
|
||
screenshot: "on",
|
||
video: "on",
|
||
});
|
||
|
||
test.describe("线程路由(无 isnew)", () => {
|
||
test("/new 始终走欢迎态,发送后进入具体 thread 路由", async ({ page }, testInfo) => {
|
||
skipIfMissingThread(testInfo, THREAD_FOR_WELCOME, "FRONTEND_E2E_THREAD_ID");
|
||
|
||
await openChat(page, newChatEntry(THREAD_FOR_WELCOME!));
|
||
await expect(page.getByTestId("welcome-suggestions")).toBeVisible();
|
||
});
|
||
|
||
test("/chats/:thread_id 直接复用并渲染历史", async ({ page }, testInfo) => {
|
||
skipIfMissingThread(testInfo, THREAD_FOR_WELCOME, "FRONTEND_E2E_THREAD_ID");
|
||
|
||
await openChat(page, reuseThreadChatEntry(THREAD_FOR_WELCOME!));
|
||
await waitForMessageListReady(page, { requireMessages: false });
|
||
const messageCount = await waitForAnyMessages(page);
|
||
testInfo.skip(messageCount === 0, "当前线程没有可见历史消息。");
|
||
|
||
await expect(page).toHaveURL(new RegExp(`/workspace/chats/${THREAD_FOR_WELCOME!}`));
|
||
await expect(page.locator(".is-user, .is-assistant").first()).toBeVisible();
|
||
});
|
||
|
||
test("/new 使用 uuid thread_id 发送后触发 stream(cod=0) 并进入 thread 路由", async ({
|
||
page,
|
||
}) => {
|
||
const threadId = uuid();
|
||
const text = `e2e-${threadId.slice(0, 8)}`;
|
||
|
||
await openChat(page, newChatEntry(threadId));
|
||
await expect(page.getByTestId("welcome-suggestions")).toBeVisible();
|
||
|
||
const streamRequestPromise = page.waitForRequest(
|
||
(request) => {
|
||
const url = request.url();
|
||
if (!url.includes("/stream")) return false;
|
||
if (!url.includes(threadId)) return false;
|
||
try {
|
||
const parsed = new URL(url);
|
||
return parsed.searchParams.get("cancel_on_disconnect") === "0";
|
||
} catch {
|
||
return url.includes("cancel_on_disconnect=0");
|
||
}
|
||
},
|
||
{ timeout: 30_000 },
|
||
);
|
||
|
||
await sendMessage(page, text);
|
||
await expect(page.locator(".is-user").filter({ hasText: text })).toHaveCount(1);
|
||
await expect
|
||
.poll(
|
||
async () => await page.locator(".is-assistant").count(),
|
||
{ timeout: 30_000 },
|
||
)
|
||
.toBeGreaterThan(0);
|
||
|
||
const streamRequest = await streamRequestPromise;
|
||
expect(streamRequest.url()).toContain("cancel_on_disconnect=0");
|
||
|
||
await expect(page).toHaveURL(
|
||
new RegExp(`/workspace/chats/${threadId}\\?is_chatting=true`),
|
||
{ timeout: 30_000 },
|
||
);
|
||
});
|
||
|
||
test("streaming 中点击停止可中断输出", async ({ page }) => {
|
||
const threadId = uuid();
|
||
const text =
|
||
"请逐行输出 1 到 500 的数字,并在每一行前面加上“第N行:”前缀,不要省略。";
|
||
|
||
await openChat(page, newChatEntry(threadId));
|
||
await expect(page.getByTestId("welcome-suggestions")).toBeVisible();
|
||
|
||
await sendMessage(page, text);
|
||
|
||
const submitButton = page.locator("button[aria-label='Submit']");
|
||
|
||
await expect(submitButton).toHaveText("停止", { timeout: 30_000 });
|
||
await expect(submitButton).toBeEnabled();
|
||
|
||
await submitButton.click();
|
||
|
||
// 点击停止后应退出 streaming 态,按钮文本不再是“停止”
|
||
await expect(submitButton).toHaveText("发送", { timeout: 30_000 });
|
||
});
|
||
});
|