Ctrl K
ComunidadGuías…

tests/e2e/control-timeline-live-history.spec.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
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");
});