Ctrl K
ComunidadGuías…

tests/e2e/control-verification-checks.spec.ts

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

Archivo original ↗Preguntar a KiBot
import { expect, test, type Page } 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";
import { createRepositoryVerificationState } from "../../lib/agents/orchestrator/checks/state";
import { CHECK_KINDS } from "../../lib/agents/orchestrator/checks/plan";
import type { RepositoryVerificationSummary } from "../../lib/agents/orchestrator/checks/receipt";

const summary = (): RepositoryVerificationSummary => ({
  version: 1,
  changed: true,
  required: false,
  verified: true,
  error: null,
  checks: CHECK_KINDS.map((kind) => ({
    kind,
    command: `CI=1 pnpm run ${kind}`,
    status: "passed",
    exitCode: 0,
    stdout: `${kind} completed successfully.`,
  })),
});

async function setup(page: Page, evidence: RepositoryVerificationSummary) {
  await enableScopedE2EAuth(page);
  await mockBaseChrome(page);
  await mockControlSessionBootstrap(page);
  await captureRecoveryEvents(page);
  const id = "00000000-0000-4000-8000-000000000077";
  const messages: UIMessage[] = [
    {
      id: "request",
      role: "user",
      parts: [{ type: "text", text: "Update the source and run full checks." }],
    },
    {
      id: "result",
      role: "assistant",
      parts: [{ type: "text", text: "Updated the workspace." }],
      metadata: { repository_verification: evidence },
    },
  ];
  const session = {
    id,
    title: "Repository verification",
    project: "acme/widgets",
    repo_id: "repo-control-default",
    model_id: null,
    orchestration_run_id: null,
    pinned: false,
    archived: false,
    messages,
    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: id,
      messages,
      call: { id: "call-1", status: "streaming", error: null },
    })
  );
  await page.goto(`${scopedPath("control")}?mission=${id}`);
  return messages;
}

test("workspace check receipts preserve expanded output while a chat is steered", async ({
  page,
}, testInfo) => {
  const messages = await setup(page, summary());
  const card = page.getByTestId("repository-checks");
  await expect(
    card.getByRole("heading", { name: "Workspace checks passed" })
  ).toBeVisible();
  await expect(card.getByRole("listitem")).toHaveCount(4);
  const typecheck = card.locator("details").first();
  await typecheck.locator("summary").focus();
  await page.keyboard.press("Enter");
  await expect(typecheck).toContainText("CI=1 pnpm run typecheck");
  await expect(typecheck).toContainText("typecheck completed successfully.");
  await expect(typecheck).toContainText("Exit code: 0");
  messages.unshift({
    id: "older",
    role: "assistant",
    parts: [{ type: "text", text: "Earlier saved context." }],
  });
  messages.push({
    id: "steer",
    role: "user",
    parts: [{ type: "text", text: "Keep the change focused." }],
  });
  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 change focused.", { exact: true })
  ).toBeVisible();
  await expect(typecheck).toHaveAttribute("open", "");
  await expect(card).toHaveCount(1);
  await card.screenshot({
    path: testInfo.outputPath("workspace-checks-passed.png"),
  });
});

test("mobile check results distinguish failed, unconfirmed, and skipped work", async ({
  page,
}, testInfo) => {
  await page.setViewportSize({ width: 390, height: 844 });
  const evidence = summary();
  evidence.verified = false;
  evidence.checks[1] = {
    kind: "lint",
    command: "CI=1 pnpm run lint",
    status: "failed",
    exitCode: 1,
    stderr:
      "index.ts:12 Unused import\n    at lint (/vercel/sandbox/src/index.ts:12:2)\nexpected /api/health/200",
  };
  evidence.checks[2] = {
    kind: "build",
    command: "CI=1 pnpm run build",
    status: "blocked",
    exitCode: null,
    reason: "Command completion could not be confirmed.",
  };
  evidence.checks[3] = {
    kind: "test",
    command: null,
    status: "skipped",
    exitCode: null,
    reason: "The operator requested no tests for this edit.",
  };
  await setup(page, evidence);
  const card = page.getByTestId("repository-checks");
  await expect(
    card.getByRole("heading", { name: "Workspace checks need attention" })
  ).toBeVisible();
  const rows = card.getByRole("listitem");
  expect(
    (await rows.first().locator("summary").boundingBox())?.height
  ).toBeGreaterThanOrEqual(44);
  for (const [index, status] of [
    "Passed",
    "Failed",
    "Unconfirmed",
    "Skipped",
  ].entries())
    await expect(rows.nth(index).locator("summary")).toContainText(status);
  await rows.nth(1).locator("summary").click();
  await expect(rows.nth(1)).toContainText("index.ts:12 Unused import");
  await expect(rows.nth(1)).toContainText(
    "at lint (/vercel/sandbox/src/index.ts:12:2)"
  );
  await expect(rows.nth(1)).toContainText("expected /api/health/200");
  await rows.nth(2).locator("summary").click();
  await expect(rows.nth(2)).toContainText(
    "Command completion could not be confirmed."
  );
  await rows.nth(3).locator("summary").click();
  await expect(rows.nth(3)).toContainText(
    "The operator requested no tests for this edit."
  );
  expect(
    await page.evaluate(() => document.documentElement.scrollWidth > innerWidth)
  ).toBe(false);
  await card.screenshot({
    path: testInfo.outputPath("workspace-checks-mobile.png"),
  });
});

test("an interrupted check keeps earlier failed commands visible without declaring verification", async ({
  page,
}) => {
  const state = createRepositoryVerificationState();
  state.recordReceipt({
    ok: false,
    verified: false,
    receiptVersion: 1,
    commandCompletion: "unknown",
    sandboxId: "workspace-1",
    workspaceRevisionBefore: "source-one",
    workspaceRevisionAfter: null,
    checks: CHECK_KINDS.map((kind, index) => ({
      kind,
      command: `CI=1 pnpm run ${kind}`,
      status: index === 3 ? "blocked" : "failed",
      exitCode: index === 3 ? null : [134, 137, 137][index],
      stderr: index === 3 ? undefined : "Resource failure",
    })),
  });
  await setup(page, { version: 1, ...state.summary() });
  const card = page.getByTestId("repository-checks");
  await expect(
    card.getByRole("heading", { name: "Workspace checks need attention" })
  ).toBeVisible();
  await expect(card).toContainText(
    "Command completion could not be confirmed."
  );
  await expect(card.locator("summary")).toHaveText([
    /Typecheck\s*Failed/,
    /Lint\s*Failed/,
    /Build\s*Failed/,
    /Tests\s*Unconfirmed/,
  ]);
  await card.locator("summary").first().click();
  await expect(card).toContainText("CI=1 pnpm run typecheck");
  await expect(card).toContainText("Exit code: 134");
  await expect(card).toContainText("Resource failure");
});