tests/e2e/control-timeline-live-history.spec.ts
Rama main · réplica de lectura del repositorio original.
import { expect, test } from "@playwright/test";
import type { UIMessage } from "ai";
import { enableScopedE2EAuth, scopedPath } from "./helpers/auth";
import {
fulfillJson,
mockBaseChrome,
mockControlSessionBootstrap,
} from "./helpers/automation-control-plane-fixtures";
import { captureRecoveryEvents } from "./helpers/control-recovery-fixtures";
test("loading earlier activity and steering a live request preserve open details and live status", async ({
page,
}) => {
await enableScopedE2EAuth(page);
await mockBaseChrome(page);
await mockControlSessionBootstrap(page);
await captureRecoveryEvents(page);
const sessionId = "00000000-0000-4000-8000-000000000077";
const messages: UIMessage[] = [
{
id: "first-request",
role: "user",
parts: [{ type: "text", text: "Inspect the source and run tests" }],
},
{
id: "live-response",
role: "assistant",
parts: [
{
type: "tool-read_file",
toolCallId: "read",
state: "output-available",
input: { path: "app/page.tsx" },
output: { content: "source" },
},
{
type: "tool-run_command",
toolCallId: "tests",
state: "input-available",
input: { command: "pnpm test" },
},
],
},
];
const session = {
id: sessionId,
title: "Live review",
project: "acme/widgets",
repo_id: "repo-control-default",
model_id: null,
orchestration_run_id: null,
pinned: false,
archived: false,
messages,
created_at: "2026-11-02T12:00:00Z",
updated_at: "2026-11-02T12:00:00Z",
};
await page.route("**/api/control/sessions**", (route) =>
fulfillJson(
route,
new URL(route.request().url()).searchParams.has("id")
? session
: [session]
)
);
await page.route("**/api/control/turns?**", (route) =>
fulfillJson(route, {
sessionId,
messages,
call: { id: "call", status: "streaming", error: null },
})
);
await page.goto(`${scopedPath("control")}?mission=${sessionId}`);
const read = page
.getByTestId("tool-activity")
.filter({ hasText: "Reading file" });
await read.locator("summary").click();
await expect(read.locator("details")).toHaveAttribute("open", "");
messages.unshift({
id: "older-activity",
role: "assistant",
parts: [{ type: "text", text: "Earlier saved activity" }],
});
messages.push({
id: "guidance",
role: "user",
parts: [{ type: "text", text: "Keep the layout" }],
});
await page.evaluate(() => {
for (const source of (
window as unknown as { recoveryEventSources: EventSource[] }
).recoveryEventSources)
source.dispatchEvent(
new MessageEvent("message", {
data: JSON.stringify({ table: "ai_calls", op: "UPDATE" }),
})
);
});
await expect(
page.getByText("Keep the layout", { exact: true })
).toBeVisible();
await expect(read.locator("details")).toHaveAttribute("open", "");
await expect(
page.getByRole("region", { name: "Earlier activity" })
).toContainText("Earlier saved activity");
await expect(
page.getByRole("region", { name: "Request 1", exact: true })
).toContainText("Inspect the source");
const command = page
.getByTestId("tool-activity")
.filter({ hasText: "Running command" });
await expect(command).toContainText("Running");
await expect(command).not.toContainText("Status unconfirmed");
await expect(command).not.toContainText("Interrupted");
});