deerflow2/frontend/tests/e2e/input-and-compose.spec.ts

194 lines
7.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { expect, test } from "@playwright/test";
import {
THREAD_FOR_WELCOME,
newChatEntry,
openChat,
reuseThreadChatEntry,
skipIfMissingThread,
} from "./support/chat-helpers";
test.describe("聊天工作台 / 输入区与发送", () => {
test("DF-INPUT-001 欢迎态输入框默认展开", async ({ page }, testInfo) => {
skipIfMissingThread(testInfo, THREAD_FOR_WELCOME, "FRONTEND_E2E_THREAD_ID");
await openChat(page, newChatEntry(THREAD_FOR_WELCOME!));
const textarea = page.locator("textarea[name='message']");
await expect
.poll(async () => {
return await textarea.evaluate((element) => {
return Math.round(
(element as HTMLTextAreaElement).getBoundingClientRect().height,
);
});
})
.toBeGreaterThan(120);
});
test("DF-INPUT-002 非欢迎态输入框可展开并在失焦后收起", async ({
page,
}, testInfo) => {
skipIfMissingThread(testInfo, THREAD_FOR_WELCOME, "FRONTEND_E2E_THREAD_ID");
await openChat(page, reuseThreadChatEntry(THREAD_FOR_WELCOME!));
const textarea = page.locator("textarea[name='message']");
const inputHeight = async () =>
await textarea.evaluate((element) => {
return Math.round(
(element as HTMLTextAreaElement).getBoundingClientRect().height,
);
});
await expect.poll(inputHeight).toBeLessThan(110);
await page.locator("div.absolute.inset-0.z-1.cursor-text").click();
await expect.poll(inputHeight).toBeGreaterThan(120);
await page
.getByRole("main")
.first()
.click({ position: { x: 20, y: 20 } });
await expect.poll(inputHeight).toBeLessThan(110);
});
test("DF-INPUT-003 点击欢迎态建议词不会导致输入区异常", async ({
page,
}, testInfo) => {
skipIfMissingThread(testInfo, THREAD_FOR_WELCOME, "FRONTEND_E2E_THREAD_ID");
await openChat(page, newChatEntry(THREAD_FOR_WELCOME!));
const suggestions = page.getByTestId("welcome-suggestions");
await expect(suggestions).toBeVisible();
await suggestions.locator("button").first().click();
const textarea = page.locator("textarea[name='message']");
await expect(textarea).toBeVisible();
await expect(page.locator(".is-user")).toHaveCount(0);
});
test("DF-INPUT-004 空消息不可提交", async ({ page }, testInfo) => {
skipIfMissingThread(testInfo, THREAD_FOR_WELCOME, "FRONTEND_E2E_THREAD_ID");
await openChat(page, newChatEntry(THREAD_FOR_WELCOME!));
const textarea = page.locator("textarea[name='message']");
const submit = page.locator("button[aria-label='Submit']");
await textarea.fill(" ");
await expect(submit).toBeDisabled();
await expect(page.locator(".is-user")).toHaveCount(0);
});
test("DF-INPUT-005 发送后输入框清空且只产生一条用户消息", async ({
page,
}, testInfo) => {
skipIfMissingThread(testInfo, THREAD_FOR_WELCOME, "FRONTEND_E2E_THREAD_ID");
await openChat(page, newChatEntry(THREAD_FOR_WELCOME!));
const textarea = page.locator("textarea[name='message']");
const submit = page.locator("button[aria-label='Submit']");
await textarea.click();
await textarea.fill("你好,测试发送");
await submit.evaluate((button) => {
(button as HTMLButtonElement).click();
});
await expect(
page.locator(".is-user").filter({ hasText: /你好,测试发送|测试发送/ }),
).toHaveCount(1);
await expect(textarea).toHaveValue("");
});
test("DF-INPUT-006 快速重复点击发送不会重复提交", async ({
page,
}, testInfo) => {
skipIfMissingThread(testInfo, THREAD_FOR_WELCOME, "FRONTEND_E2E_THREAD_ID");
await openChat(page, newChatEntry(THREAD_FOR_WELCOME!));
const textarea = page.locator("textarea[name='message']");
const submit = page.locator("button[aria-label='Submit']");
await textarea.fill("重复提交测试");
await submit.evaluate((button) => {
const target = button as HTMLButtonElement;
target.click();
target.click();
target.click();
});
await expect(
page.locator(".is-user").filter({ hasText: "重复提交测试" }),
).toHaveCount(1);
});
test("DF-INPUT-007 输入@时展示文件候选并可选择为引用 chip", async (
{ page },
testInfo,
) => {
skipIfMissingThread(testInfo, THREAD_FOR_WELCOME, "FRONTEND_E2E_THREAD_ID");
await openChat(page, reuseThreadChatEntry(THREAD_FOR_WELCOME!));
const textarea = page.locator("textarea[name='message']");
await textarea.click();
await textarea.fill("请基于这个文件回答 @");
const panel = page.getByTestId("mention-candidate-panel").first();
await expect(panel).toBeVisible();
const items = panel.locator("button");
const itemCount = await items.count();
testInfo.skip(itemCount === 0, "当前线程没有可引用文件候选。");
await items.first().click();
await expect(textarea).toBeFocused();
await expect(textarea).toHaveValue(/请基于这个文件回答/);
await expect(page.getByTestId("reference-inline-preview")).toBeVisible();
await expect(page.getByLabel("移除引用").first()).toBeVisible();
});
test("DF-INPUT-008 失效引用不会阻断文本发送(可解释 skip", async (
{ page },
testInfo,
) => {
skipIfMissingThread(testInfo, THREAD_FOR_WELCOME, "FRONTEND_E2E_THREAD_ID");
await openChat(page, reuseThreadChatEntry(THREAD_FOR_WELCOME!));
// 该场景依赖特定后端/fixture 能制造 stale 引用;若环境无能力则显式跳过。
testInfo.skip(true, "当前 E2E 环境无法稳定注入 stale 引用,使用 hooks 单测覆盖软失败逻辑。");
});
test("DF-INPUT-009 引用上限为 6第 7 个被阻止并提示", async (
{ page },
testInfo,
) => {
skipIfMissingThread(testInfo, THREAD_FOR_WELCOME, "FRONTEND_E2E_THREAD_ID");
await openChat(page, reuseThreadChatEntry(THREAD_FOR_WELCOME!));
const textarea = page.locator("textarea[name='message']");
await textarea.click();
await textarea.fill("请参考这些文件 ");
await textarea.type("@");
const panel = page.getByTestId("mention-candidate-panel").first();
await expect(panel).toBeVisible();
const initialItems = panel.locator("button");
const candidateCount = await initialItems.count();
testInfo.skip(candidateCount < 7, "当前线程候选文件不足 7 个,无法验证上限。");
for (let i = 0; i < 6; i += 1) {
await textarea.type("@");
const currentPanel = page.getByTestId("mention-candidate-panel").first();
await expect(currentPanel).toBeVisible();
await currentPanel.locator("button").nth(i).click();
}
await expect(page.getByLabel("移除引用")).toHaveCount(6);
await textarea.type("@");
await expect(panel).toBeVisible();
await panel.locator("button").nth(6).click();
await expect(page.getByLabel("移除引用")).toHaveCount(6);
await expect(page.getByText("单条消息最多引用 6 个文件")).toBeVisible();
});
});