import { test, expect, type Page } from "@playwright/test"; import { chatExamples, agentExamples, completionExamples, workflowExamples, } from "./examples"; /** 断言结果容器中渲染了实质内容(markdown 文本长度足够)。 */ async function expectNonEmptyAnswer(locator: ReturnType) { 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); }); } });