import { expect, gotoWorkbench, saveScreenshot, test } from "../fixtures/test"; import { selectors, sessionTab } from "../fixtures/selectors"; test("TraceTimeline renders readable rows and hides backend noise", async ({ page }, testInfo) => { await gotoWorkbench(page, "/workbench/sessions/ses_completed"); await expect(page.locator(sessionTab("ses_completed"))).toHaveAttribute("data-active", "true"); const trace = page.locator(`${selectors.traceTimeline}[data-status="completed"]`); await expect(trace).toBeVisible(); await trace.locator("summary.trace-disclosure-summary").click(); await expect(page.locator(`${selectors.traceRow}[data-row-kind="tool"]`)).toContainText("commandExecution"); await expect(page.locator(`${selectors.traceRow}[data-terminal="true"]`)).toContainText("构建和 Trace 渲染检查完成"); await expect(page.locator(selectors.conversationList)).not.toContainText("token_count"); await page.locator(`${selectors.messageCard}[data-role="agent"] .message-detail-button`).first().click(); await expect(page.locator(selectors.messageDetailDialog)).toContainText("trc_completed"); await expect(page.locator(selectors.messageDetailDialog)).toContainText("ses_completed"); await saveScreenshot(page, testInfo, "trace-rendering-detail"); }); test.describe("trace disclosure policy", () => { test.use({ scenarioId: "event-replay" }); test("manual trace disclosure survives terminal lifecycle updates", async ({ page }, testInfo) => { await gotoWorkbench(page); const runningTrace = page.locator(`${selectors.traceTimeline}[data-status="running"]`).first(); const runningDetails = runningTrace.locator("details.trace-disclosure"); await expect(runningTrace).toBeVisible(); await expect(runningDetails).toHaveJSProperty("open", false); await runningTrace.locator("summary.trace-disclosure-summary").click(); await expect(runningDetails).toHaveJSProperty("open", true); const completedTrace = page.locator(`${selectors.traceTimeline}[data-status="completed"]`).first(); const completedDetails = completedTrace.locator("details.trace-disclosure"); await expect(completedTrace).toBeVisible(); await expect(completedDetails).toHaveJSProperty("open", true); await saveScreenshot(page, testInfo, "trace-disclosure-policy-manual-terminal"); }); }); test.describe("non-terminal assistant progress", () => { test.use({ scenarioId: "progress-only-final-response" }); test("progress text stays in trace until terminal final response arrives", async ({ page }, testInfo) => { await gotoWorkbench(page, "/workbench/sessions/ses_running"); const runningCard = page.locator(`${selectors.messageCard}[data-role="agent"][data-status="running"]`).last(); await expect(runningCard).toBeVisible(); await expect(runningCard.locator(selectors.traceTimeline)).toContainText("PROGRESS_ONLY_SHOULD_NOT_RENDER_AS_FINAL"); const runningFinalTexts = await runningCard.evaluate((node) => Array.from(node.querySelectorAll(".message-markdown.message-text")).map((item) => item.textContent ?? "")); expect(runningFinalTexts).toEqual([]); await saveScreenshot(page, testInfo, "running-before-final-response"); const completedCard = page.locator(`${selectors.messageCard}[data-role="agent"][data-status="completed"]`).last(); await expect(completedCard.locator(".message-markdown.message-text")).toContainText("FINAL_ONLY_SHOULD_RENDER"); await expect(completedCard.locator(selectors.traceTimeline)).toBeVisible(); await saveScreenshot(page, testInfo, "terminal-after-final-response"); }); }); test("failed trace exposes readable failure row", async ({ page }, testInfo) => { await gotoWorkbench(page, "/workbench/sessions/ses_failed"); const trace = page.locator(`${selectors.traceTimeline}[data-status="failed"]`); await expect(trace).toBeVisible(); await trace.locator("summary.trace-disclosure-summary").click(); await expect(page.locator(`${selectors.traceRow}[data-event-status="blocked"][data-row-kind="tool"]`)).toContainText("missing controlled dependency"); await expect(page.locator(`${selectors.messageCard}[data-status="failed"]`)).toContainText("缺少受控依赖"); await saveScreenshot(page, testInfo, "trace-rendering-failed"); }); test.describe("terminal empty trace copy", () => { test.use({ scenarioId: "terminal-empty-trace" }); test("completed message details do not retain thinking placeholder", async ({ page }) => { await gotoWorkbench(page, "/workbench/sessions/ses_terminal_empty"); const card = page.locator(`${selectors.messageCard}[data-status="completed"]`).last(); await expect(card).toContainText("终态空 Trace 已完成。"); await expect(card.locator(".trace-empty")).toContainText("运行已完成,暂无可读 Trace"); expect((await card.textContent()) ?? "").not.toContain("思考中"); }); });