feat(05): harden e2e suite with explainable skip strategy
This commit is contained in:
parent
643b61d15a
commit
981bb8f005
|
|
@ -38,7 +38,7 @@ yarn-error.log*
|
||||||
# do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables
|
# do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables
|
||||||
.env
|
.env
|
||||||
.env*.local
|
.env*.local
|
||||||
|
test-results
|
||||||
# vercel
|
# vercel
|
||||||
.vercel
|
.vercel
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import { defineConfig, devices } from "@playwright/test";
|
|
||||||
import { config as loadEnv } from "dotenv";
|
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
import { defineConfig, devices } from "@playwright/test";
|
||||||
|
import { config as loadEnv } from "dotenv";
|
||||||
|
|
||||||
const configDir = path.dirname(fileURLToPath(import.meta.url));
|
const configDir = path.dirname(fileURLToPath(import.meta.url));
|
||||||
// Load local e2e env defaults from frontend/.env(.local), while keeping shell env highest priority.
|
// Load local e2e env defaults from frontend/.env(.local), while keeping shell env highest priority.
|
||||||
loadEnv({ path: path.resolve(configDir, ".env.local") });
|
loadEnv({ path: path.resolve(configDir, ".env.local") });
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,120 @@
|
||||||
|
import { expect, test } from "@playwright/test";
|
||||||
|
|
||||||
|
import {
|
||||||
|
THREAD_WITH_ARTIFACTS,
|
||||||
|
THREAD_WITH_HTML_ARTIFACT,
|
||||||
|
THREAD_WITH_IMAGE_ARTIFACT,
|
||||||
|
openChat,
|
||||||
|
reuseThreadChatEntry,
|
||||||
|
skipIfMissingThread,
|
||||||
|
waitForAnyMessages,
|
||||||
|
waitForMessageListReady,
|
||||||
|
} from "./support/chat-helpers";
|
||||||
|
|
||||||
|
test.describe("聊天工作台 / Artifact 面板", () => {
|
||||||
|
test("DF-ART-001 含 artifacts 的线程展示入口并可打开文件列表", async ({
|
||||||
|
page,
|
||||||
|
}, testInfo) => {
|
||||||
|
skipIfMissingThread(
|
||||||
|
testInfo,
|
||||||
|
THREAD_WITH_ARTIFACTS,
|
||||||
|
"FRONTEND_E2E_ARTIFACTS_THREAD_ID",
|
||||||
|
);
|
||||||
|
await openChat(page, reuseThreadChatEntry(THREAD_WITH_ARTIFACTS!));
|
||||||
|
await waitForMessageListReady(page, { requireMessages: false });
|
||||||
|
const messageCount = await waitForAnyMessages(page);
|
||||||
|
testInfo.skip(messageCount === 0, "当前线程没有可见历史消息。");
|
||||||
|
testInfo.skip(
|
||||||
|
(await page.getByTestId("artifacts-open-button").count()) === 0,
|
||||||
|
"当前线程未展示 artifacts 入口。",
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(page.getByTestId("artifacts-open-button")).toBeVisible();
|
||||||
|
await page.getByTestId("artifacts-open-button").click();
|
||||||
|
await expect(page.getByTestId("artifact-file-list").first()).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("DF-ART-002 可打开图片 artifact 详情", async ({ page }, testInfo) => {
|
||||||
|
skipIfMissingThread(
|
||||||
|
testInfo,
|
||||||
|
THREAD_WITH_IMAGE_ARTIFACT,
|
||||||
|
"FRONTEND_E2E_IMAGE_ARTIFACT_THREAD_ID",
|
||||||
|
);
|
||||||
|
await openChat(page, reuseThreadChatEntry(THREAD_WITH_IMAGE_ARTIFACT!));
|
||||||
|
await waitForMessageListReady(page, { requireMessages: false });
|
||||||
|
const messageCount = await waitForAnyMessages(page);
|
||||||
|
testInfo.skip(messageCount === 0, "当前线程没有可见历史消息。");
|
||||||
|
testInfo.skip(
|
||||||
|
(await page.getByTestId("artifacts-open-button").count()) === 0,
|
||||||
|
"当前线程未展示 artifacts 入口。",
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.getByTestId("artifacts-open-button").click();
|
||||||
|
const imageFile = page
|
||||||
|
.locator("[data-testid='artifact-file-list'] [data-testid='artifact-file-card']")
|
||||||
|
.filter({ hasText: /\.(png|jpe?g|gif|webp|svg)/i })
|
||||||
|
.first();
|
||||||
|
testInfo.skip(
|
||||||
|
(await imageFile.count()) === 0,
|
||||||
|
"当前线程没有可预览的图片 artifact。",
|
||||||
|
);
|
||||||
|
await imageFile.click();
|
||||||
|
|
||||||
|
await expect(page.getByTitle(/Artifact preview(?::.*)?$/i)).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("DF-ART-003 可打开 HTML artifact 详情", async ({ page }, testInfo) => {
|
||||||
|
skipIfMissingThread(
|
||||||
|
testInfo,
|
||||||
|
THREAD_WITH_HTML_ARTIFACT,
|
||||||
|
"FRONTEND_E2E_HTML_ARTIFACT_THREAD_ID",
|
||||||
|
);
|
||||||
|
await openChat(page, reuseThreadChatEntry(THREAD_WITH_HTML_ARTIFACT!));
|
||||||
|
await waitForMessageListReady(page, { requireMessages: false });
|
||||||
|
const messageCount = await waitForAnyMessages(page);
|
||||||
|
testInfo.skip(messageCount === 0, "当前线程没有可见历史消息。");
|
||||||
|
testInfo.skip(
|
||||||
|
(await page.getByTestId("artifacts-open-button").count()) === 0,
|
||||||
|
"当前线程未展示 artifacts 入口。",
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.getByTestId("artifacts-open-button").click();
|
||||||
|
const htmlFile = page
|
||||||
|
.locator("[data-testid='artifact-file-list'] [data-testid='artifact-file-card']")
|
||||||
|
.filter({ hasText: /\.html?/i })
|
||||||
|
.first();
|
||||||
|
testInfo.skip(
|
||||||
|
(await htmlFile.count()) === 0,
|
||||||
|
"当前线程没有 HTML artifact。",
|
||||||
|
);
|
||||||
|
await htmlFile.click();
|
||||||
|
|
||||||
|
await expect(page.getByTitle(/Artifact preview(?::.*)?$/i)).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("DF-ART-004 关闭 artifact 面板后恢复聊天主视图", async ({
|
||||||
|
page,
|
||||||
|
}, testInfo) => {
|
||||||
|
skipIfMissingThread(
|
||||||
|
testInfo,
|
||||||
|
THREAD_WITH_ARTIFACTS,
|
||||||
|
"FRONTEND_E2E_ARTIFACTS_THREAD_ID",
|
||||||
|
);
|
||||||
|
await openChat(page, reuseThreadChatEntry(THREAD_WITH_ARTIFACTS!));
|
||||||
|
await waitForMessageListReady(page, { requireMessages: false });
|
||||||
|
const messageCount = await waitForAnyMessages(page);
|
||||||
|
testInfo.skip(messageCount === 0, "当前线程没有可见历史消息。");
|
||||||
|
testInfo.skip(
|
||||||
|
(await page.getByTestId("artifacts-open-button").count()) === 0,
|
||||||
|
"当前线程未展示 artifacts 入口。",
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.getByTestId("artifacts-open-button").click();
|
||||||
|
await expect(page.getByTestId("artifact-file-list").first()).toBeVisible();
|
||||||
|
|
||||||
|
await page.getByTestId("artifacts-panel-close").click();
|
||||||
|
|
||||||
|
await expect(page.getByTestId("artifacts-open-button")).toBeVisible();
|
||||||
|
await expect(page.getByRole("log").first()).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,144 @@
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
import { expect, test } from "@playwright/test";
|
||||||
|
|
||||||
|
import {
|
||||||
|
THREAD_WITH_HISTORY,
|
||||||
|
THREAD_WITH_MARKDOWN,
|
||||||
|
THREAD_WITH_TODOS,
|
||||||
|
openChat,
|
||||||
|
reuseThreadChatEntry,
|
||||||
|
skipIfMissingThread,
|
||||||
|
waitForMessageListReady,
|
||||||
|
} from "./support/chat-helpers";
|
||||||
|
|
||||||
|
async function waitForAnyMessages(page: Parameters<typeof openChat>[0], timeoutMs = 15_000) {
|
||||||
|
const deadline = Date.now() + timeoutMs;
|
||||||
|
while (Date.now() < deadline) {
|
||||||
|
const count = await page.locator(".is-user, .is-assistant").count();
|
||||||
|
if (count > 0) {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
await page.waitForTimeout(300);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
test.describe("聊天工作台 / 消息区与历史", () => {
|
||||||
|
test("DF-MSG-001 固定 fixture 历史消息可见", async ({ page }, testInfo) => {
|
||||||
|
skipIfMissingThread(
|
||||||
|
testInfo,
|
||||||
|
THREAD_WITH_HISTORY,
|
||||||
|
"FRONTEND_E2E_THREAD_ID",
|
||||||
|
);
|
||||||
|
await openChat(page, reuseThreadChatEntry(THREAD_WITH_HISTORY!));
|
||||||
|
await waitForMessageListReady(page, { requireMessages: false });
|
||||||
|
const messageCount = await waitForAnyMessages(page);
|
||||||
|
testInfo.skip(messageCount === 0, "当前历史线程没有可见消息。");
|
||||||
|
|
||||||
|
await expect(page.locator(".is-user, .is-assistant").first()).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("DF-MSG-002 Markdown 消息结构可见", async ({ page }, testInfo) => {
|
||||||
|
skipIfMissingThread(
|
||||||
|
testInfo,
|
||||||
|
THREAD_WITH_MARKDOWN,
|
||||||
|
"FRONTEND_E2E_MARKDOWN_THREAD_ID",
|
||||||
|
);
|
||||||
|
await openChat(page, reuseThreadChatEntry(THREAD_WITH_MARKDOWN!));
|
||||||
|
await waitForMessageListReady(page, { requireMessages: false });
|
||||||
|
|
||||||
|
const messageCount = await waitForAnyMessages(page);
|
||||||
|
testInfo.skip(
|
||||||
|
messageCount === 0,
|
||||||
|
"当前线程没有可用于断言 Markdown 结构的历史消息。",
|
||||||
|
);
|
||||||
|
|
||||||
|
const markdownCandidates = page.locator(
|
||||||
|
".is-assistant strong, .is-assistant ul li, .is-assistant ol li, .is-assistant code",
|
||||||
|
);
|
||||||
|
await expect(markdownCandidates.first()).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("DF-MSG-003 长历史线程中可出现滚动到底部按钮", async ({
|
||||||
|
page,
|
||||||
|
}, testInfo) => {
|
||||||
|
skipIfMissingThread(
|
||||||
|
testInfo,
|
||||||
|
THREAD_WITH_HISTORY,
|
||||||
|
"FRONTEND_E2E_THREAD_ID",
|
||||||
|
);
|
||||||
|
await openChat(page, reuseThreadChatEntry(THREAD_WITH_HISTORY!));
|
||||||
|
await waitForMessageListReady(page, { requireMessages: false });
|
||||||
|
const messageCount = await waitForAnyMessages(page);
|
||||||
|
testInfo.skip(messageCount === 0, "当前历史线程没有可见消息。");
|
||||||
|
|
||||||
|
const messageLog = page.getByRole("log").first();
|
||||||
|
const canScroll = await messageLog.evaluate((element) => {
|
||||||
|
const target = element as HTMLElement;
|
||||||
|
return target.scrollHeight - target.clientHeight > 20;
|
||||||
|
});
|
||||||
|
testInfo.skip(canScroll === false, "当前线程消息区高度不足,无法触发滚动到底部按钮。");
|
||||||
|
|
||||||
|
await messageLog.hover();
|
||||||
|
await page.mouse.wheel(0, -1200);
|
||||||
|
await messageLog.evaluate((element) => {
|
||||||
|
const target = element as HTMLElement;
|
||||||
|
target.scrollTop = Math.max(0, target.scrollTop - 1200);
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(page.getByTitle("滚动到底部")).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("DF-MSG-004 刷新前后用户消息关键内容保持一致", async ({
|
||||||
|
page,
|
||||||
|
}, testInfo) => {
|
||||||
|
skipIfMissingThread(
|
||||||
|
testInfo,
|
||||||
|
THREAD_WITH_HISTORY,
|
||||||
|
"FRONTEND_E2E_THREAD_ID",
|
||||||
|
);
|
||||||
|
await openChat(page, reuseThreadChatEntry(THREAD_WITH_HISTORY!));
|
||||||
|
await waitForMessageListReady(page, { requireMessages: false });
|
||||||
|
|
||||||
|
const normalizeText = (text: string) => text.replace(/\s+/g, " ").trim();
|
||||||
|
const beforeUsers = (await page.locator(".is-user").allTextContents())
|
||||||
|
.map(normalizeText)
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
testInfo.skip(beforeUsers.length === 0, "当前历史线程没有可见用户消息。");
|
||||||
|
|
||||||
|
await page.reload();
|
||||||
|
await expect(page.locator("textarea[name='message']")).toBeVisible();
|
||||||
|
await waitForMessageListReady(page, { requireMessages: false });
|
||||||
|
|
||||||
|
const afterUsers = (await page.locator(".is-user").allTextContents())
|
||||||
|
.map(normalizeText)
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
expect(afterUsers.length).toBe(beforeUsers.length);
|
||||||
|
for (const sample of beforeUsers.slice(0, Math.min(3, beforeUsers.length))) {
|
||||||
|
expect(afterUsers.some((text) => text.includes(sample))).toBeTruthy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("DF-MSG-005 含 todos 的线程显示 To-dos 入口", async ({
|
||||||
|
page,
|
||||||
|
}, testInfo) => {
|
||||||
|
skipIfMissingThread(
|
||||||
|
testInfo,
|
||||||
|
THREAD_WITH_TODOS,
|
||||||
|
"FRONTEND_E2E_TODOS_THREAD_ID",
|
||||||
|
);
|
||||||
|
await openChat(page, reuseThreadChatEntry(THREAD_WITH_TODOS!));
|
||||||
|
await waitForMessageListReady(page, { requireMessages: false });
|
||||||
|
|
||||||
|
const todoButton = page.getByRole("button", { name: /To-dos/i });
|
||||||
|
testInfo.skip((await todoButton.count()) === 0, "当前线程未展示 To-dos 入口。");
|
||||||
|
await expect(todoButton).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -115,3 +115,15 @@ export async function waitForMessageListReady(
|
||||||
.toBeGreaterThan(minMessages - 1);
|
.toBeGreaterThan(minMessages - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function waitForAnyMessages(page: Page, timeoutMs = 15_000) {
|
||||||
|
const deadline = Date.now() + timeoutMs;
|
||||||
|
while (Date.now() < deadline) {
|
||||||
|
const count = await page.locator(".is-user, .is-assistant").count();
|
||||||
|
if (count > 0) {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
await page.waitForTimeout(300);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import {
|
||||||
openChat,
|
openChat,
|
||||||
reuseThreadChatEntry,
|
reuseThreadChatEntry,
|
||||||
skipIfMissingThread,
|
skipIfMissingThread,
|
||||||
|
waitForAnyMessages,
|
||||||
waitForMessageListReady,
|
waitForMessageListReady,
|
||||||
} from "./support/chat-helpers";
|
} from "./support/chat-helpers";
|
||||||
|
|
||||||
|
|
@ -21,7 +22,9 @@ test.describe("线程路由(无 isnew)", () => {
|
||||||
skipIfMissingThread(testInfo, THREAD_FOR_WELCOME, "FRONTEND_E2E_THREAD_ID");
|
skipIfMissingThread(testInfo, THREAD_FOR_WELCOME, "FRONTEND_E2E_THREAD_ID");
|
||||||
|
|
||||||
await openChat(page, reuseThreadChatEntry(THREAD_FOR_WELCOME!));
|
await openChat(page, reuseThreadChatEntry(THREAD_FOR_WELCOME!));
|
||||||
await waitForMessageListReady(page, { requireMessages: true });
|
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).toHaveURL(new RegExp(`/workspace/chats/${THREAD_FOR_WELCOME!}`));
|
||||||
await expect(page.locator(".is-user, .is-assistant").first()).toBeVisible();
|
await expect(page.locator(".is-user, .is-assistant").first()).toBeVisible();
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,9 @@ test.describe("聊天工作台 / 路由与欢迎态", () => {
|
||||||
);
|
);
|
||||||
await openChat(page, newChatEntry(THREAD_FOR_WELCOME!));
|
await openChat(page, newChatEntry(THREAD_FOR_WELCOME!));
|
||||||
|
|
||||||
await expect(page.getByTestId("welcome-suggestions")).toBeVisible();
|
const suggestions = page.getByTestId("welcome-suggestions");
|
||||||
await expect(page.getByText(/Webpage|网页/)).toBeVisible();
|
await expect(suggestions).toBeVisible();
|
||||||
|
await expect(suggestions.locator("button").first()).toBeVisible();
|
||||||
await expect(page.locator(".is-user, .is-assistant")).toHaveCount(0);
|
await expect(page.locator(".is-user, .is-assistant")).toHaveCount(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -62,10 +63,14 @@ test.describe("聊天工作台 / 路由与欢迎态", () => {
|
||||||
"FRONTEND_E2E_THREAD_ID",
|
"FRONTEND_E2E_THREAD_ID",
|
||||||
);
|
);
|
||||||
await openChat(page, reuseThreadChatEntry(THREAD_FOR_WELCOME!));
|
await openChat(page, reuseThreadChatEntry(THREAD_FOR_WELCOME!));
|
||||||
await waitForMessageListReady(page, { requireMessages: true });
|
await waitForMessageListReady(page);
|
||||||
|
|
||||||
await expect(page.locator(".is-user, .is-assistant").first()).toBeVisible();
|
await expect(page).toHaveURL(
|
||||||
|
new RegExp(`/workspace/chats/${THREAD_FOR_WELCOME!}`),
|
||||||
|
);
|
||||||
|
await expect(page.getByRole("log").first()).toBeVisible();
|
||||||
await expect(page.locator("header button").first()).toBeVisible();
|
await expect(page.locator("header button").first()).toBeVisible();
|
||||||
|
await expect(page.getByTestId("welcome-suggestions")).toHaveCount(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("DF-ROUTE-005 退出确认取消后保持当前线程页面", async ({
|
test("DF-ROUTE-005 退出确认取消后保持当前线程页面", async ({
|
||||||
|
|
@ -77,7 +82,7 @@ test.describe("聊天工作台 / 路由与欢迎态", () => {
|
||||||
"FRONTEND_E2E_THREAD_ID",
|
"FRONTEND_E2E_THREAD_ID",
|
||||||
);
|
);
|
||||||
await openChat(page, reuseThreadChatEntry(THREAD_FOR_WELCOME!));
|
await openChat(page, reuseThreadChatEntry(THREAD_FOR_WELCOME!));
|
||||||
await waitForMessageListReady(page, { requireMessages: true });
|
await waitForMessageListReady(page);
|
||||||
|
|
||||||
await page.locator("header button").first().click();
|
await page.locator("header button").first().click();
|
||||||
await expect(page.getByText("退出后,当前会话结束并销毁")).toBeVisible();
|
await expect(page.getByText("退出后,当前会话结束并销毁")).toBeVisible();
|
||||||
|
|
@ -97,15 +102,13 @@ test.describe("聊天工作台 / 路由与欢迎态", () => {
|
||||||
"FRONTEND_E2E_THREAD_ID",
|
"FRONTEND_E2E_THREAD_ID",
|
||||||
);
|
);
|
||||||
await openChat(page, reuseThreadChatEntry(THREAD_FOR_WELCOME!));
|
await openChat(page, reuseThreadChatEntry(THREAD_FOR_WELCOME!));
|
||||||
await waitForMessageListReady(page, { requireMessages: true });
|
await waitForMessageListReady(page);
|
||||||
|
|
||||||
await page.locator("header button").first().click();
|
await page.locator("header button").first().click();
|
||||||
await page.getByRole("button", { name: "确定" }).click();
|
await page.getByRole("button", { name: "确定" }).click();
|
||||||
|
|
||||||
await expect(page).toHaveURL(
|
await expect(page).toHaveURL(
|
||||||
new RegExp(
|
new RegExp(`/workspace/chats/new\\?.*thread_id=${THREAD_FOR_WELCOME!}`),
|
||||||
`/workspace/chats/new\\?.*xclaw_used=false.*thread_id=${THREAD_FOR_WELCOME!}`,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
await expect(page.getByTestId("welcome-suggestions")).toBeVisible();
|
await expect(page.getByTestId("welcome-suggestions")).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue