Files
selfrelease e65da6f294 feat: 接入法制日报机构并新增 15 应用 Playwright 回归测试
- 新增法制日报机构种子数据(机构/用户/分类/15应用/知识库),登录页默认账号预填
- 引入 Playwright e2e:登录复用 storageState,覆盖对话/补全/工作流/智能体四类交互
- 配置测试脚本与产物 gitignore

Co-authored-by: Cursor <cursor@cursor.com>
2026-06-25 19:47:15 +08:00

103 lines
3.8 KiB
TypeScript

import { test, expect, type Page } from "@playwright/test";
import {
chatExamples,
agentExamples,
completionExamples,
workflowExamples,
} from "./examples";
/** 断言结果容器中渲染了实质内容(markdown 文本长度足够)。 */
async function expectNonEmptyAnswer(locator: ReturnType<Page["locator"]>) {
await expect(locator).toBeVisible({ timeout: 90_000 });
await expect
.poll(async () => (await locator.innerText()).trim().length, { timeout: 90_000 })
.toBeGreaterThan(15);
}
test.describe("对话型 / 智能体型应用(textarea + 发送)", () => {
for (const app of [...chatExamples, ...agentExamples]) {
test(`${app.name} (${app.slug})`, async ({ page }) => {
await page.goto(`/chat/${app.slug}`);
const textarea = page.getByPlaceholder(/输入消息/);
await expect(textarea).toBeVisible({ timeout: 30_000 });
await textarea.fill(app.input);
const busy = app.kind === "agent" ? /思考中/ : /生成中/;
await page.getByRole("button", { name: /发送/ }).click();
// 等待回复结束(发送按钮文本从 busy 恢复为“发送”)。
await expect(page.getByRole("button", { name: busy })).toBeVisible({ timeout: 30_000 });
await expect(page.getByRole("button", { name: /发送/ })).toBeVisible({ timeout: 90_000 });
// 最后一条 AI 回复(justify-start 容器)应有非空内容。
const lastAiReply = page.locator("div.justify-start").last();
await expectNonEmptyAnswer(lastAiReply);
});
}
});
test.describe("补全型应用(textarea + 生成)", () => {
for (const app of completionExamples) {
test(`${app.name} (${app.slug})`, async ({ page }) => {
await page.goto(`/chat/${app.slug}`);
const textarea = page.getByPlaceholder(/请输入|在此输入/).first();
await expect(textarea).toBeVisible({ timeout: 30_000 });
await textarea.fill(app.input);
await page.getByRole("button", { name: /^生成$/ }).click();
// 生成中 -> 生成 恢复。
await expect(page.getByRole("button", { name: /生成中/ })).toBeVisible({ timeout: 30_000 });
await expect(page.getByRole("button", { name: /^生成$/ })).toBeVisible({ timeout: 90_000 });
// 输出结果卡片(emerald 主题)渲染非空。
const output = page.locator(".bg-emerald-50\\/30").last();
await expectNonEmptyAnswer(output);
});
}
});
test.describe("工作流型应用(多步表单 + 运行)", () => {
for (const app of workflowExamples) {
test(`${app.name} (${app.slug})`, async ({ page }) => {
await page.goto(`/chat/${app.slug}`);
// 等待第一步表单渲染。
await expect(page.getByRole("heading", { name: /步骤 1/ })).toBeVisible({
timeout: 30_000,
});
for (let i = 0; i < app.steps.length; i++) {
const step = app.steps[i];
const isLast = i === app.steps.length - 1;
if (step.type === "select") {
await page.getByRole("button", { name: step.value, exact: true }).click();
} else {
const ta = page.locator("textarea");
await expect(ta).toBeVisible();
await ta.fill(step.value);
}
if (isLast) {
await page.getByRole("button", { name: /运行/ }).click();
} else {
await page.getByRole("button", { name: /下一步/ }).click();
// 等待进入下一步。
await expect(
page.getByRole("heading", { name: new RegExp(`步骤 ${i + 2}`) }),
).toBeVisible({ timeout: 15_000 });
}
}
// 运行后等待“处理结果”出现并渲染非空内容。
await expect(page.getByRole("heading", { name: /处理结果/ })).toBeVisible({
timeout: 90_000,
});
const result = page.locator(".bg-emerald-50\\/30").last();
await expectNonEmptyAnswer(result);
});
}
});