Ctrl K
ComunidadGuías…

tests/e2e/flows-status-visibility.spec.ts

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

Archivo original ↗Preguntar a KiBot
import { expect, test } from "@playwright/test";
import { enableScopedE2EAuth, scopedPath } from "./helpers/auth";
import type { Route } from "@playwright/test";

const connectedUser = {
  id: "00000000-0000-4000-8000-000000000001",
  email: "alex@example.com",
  username: "alex",
  name: "Alex",
  avatar_url: "https://example.com/avatar.png",
  github_connected: true,
  github_app_connected: true,
  github_app_available: true,
  github_connection_mode: "app" as const,
};

async function fulfillJson(route: Route, data: unknown, status = 200) {
  await route.fulfill({
    status,
    contentType: "application/json",
    body: JSON.stringify(data),
  });
}

function buildFlow(input: {
  id: string;
  name: string;
  status: "active" | "inactive";
}) {
  return {
    id: input.id,
    installation_id: 101,
    name: input.name,
    description: "",
    notes: "",
    source_kind: "github",
    status: input.status,
    last_run_status: null,
    published_version_id: null,
    published_version: null,
    draft_graph: {
      nodes: [
        {
          id: "start",
          type: "start",
          position: { x: 0, y: 160 },
          data: { label: "Start", config: { event: "pr_opened", filter: {} } },
        },
      ],
      edges: [],
      viewport: { x: 0, y: 0, zoom: 1 },
    },
  };
}

test("workflow picker surfaces active/inactive status and deletion shows an informational toast", async ({
  page,
}) => {
  await enableScopedE2EAuth(page);

  let flows = [
    buildFlow({ id: "flow-1", name: "Live Review", status: "active" }),
    buildFlow({ id: "flow-2", name: "Paused Merger", status: "inactive" }),
  ];
  let deleteCalls = 0;

  await page.route("**/api/auth/user", (route) =>
    fulfillJson(route, { user: connectedUser })
  );
  await page.route("**/api/settings", (route) =>
    fulfillJson(route, { default_model: "minimax/minimax-m2.5", theme: "dark" })
  );
  await page.route("**/api/models", (route) =>
    fulfillJson(route, { models: [], catalog: [] })
  );
  await page.route("**/api/agents", (route) => fulfillJson(route, []));
  await page.route("**/api/github/installations", (route) =>
    fulfillJson(route, [
      {
        id: "inst-1",
        installation_id: 101,
        account_login: "webrenew",
        repositories: [{ id: "repo-1", full_name: "webrenew/blackbox" }],
      },
    ])
  );
  await page.route("**/api/flows", (route) => fulfillJson(route, flows));
  await page.route("**/api/flows/flow-1", async (route) => {
    if (route.request().method() === "DELETE") {
      deleteCalls += 1;
      flows = flows.filter((flow) => flow.id !== "flow-1");
      await fulfillJson(route, { success: true });
      return;
    }
    const flow = flows.find((candidate) => candidate.id === "flow-1");
    await fulfillJson(route, flow ?? {}, flow ? 200 : 404);
  });
  await page.route("**/api/flows/flow-2", (route) =>
    fulfillJson(
      route,
      flows.find((flow) => flow.id === "flow-2")
    )
  );
  await page.route("**/api/flows/*/runs?limit=12", (route) =>
    fulfillJson(route, { runs: [] })
  );

  // At 1280px the centered flow-name pill overlaps the header action buttons
  // (it is hidden below xl); widen past the collision so Delete is clickable.
  await page.setViewportSize({ width: 1680, height: 900 });
  await page.goto(scopedPath("workflows"));
  await page.waitForLoadState("networkidle");

  // The label row summarizes how many workflows are live without opening
  // the picker.
  await expect(page.getByTestId("flow-active-count")).toHaveText("1/2 active");

  // The open picker doubles as the status overview: every workflow is listed
  // with its active/inactive state exposed to accessibility queries.
  await page.getByLabel("Select workflow").click();
  await expect(
    page.getByRole("option", { name: "Live Review (active)" })
  ).toBeVisible();
  await expect(
    page.getByRole("option", { name: "Paused Merger (inactive)" })
  ).toBeVisible();
  await page.keyboard.press("Escape");

  // Deleting the selected workflow confirms with a red (destructive) toast
  // that names the deleted workflow.
  page.on("dialog", (dialog) => void dialog.accept());
  await page.getByRole("button", { name: "Delete flow" }).click();

  await expect.poll(() => deleteCalls).toBe(1);
  const toast = page
    .locator('[data-variant="info"]')
    .filter({ hasText: "Workflow deleted" });
  await expect(toast).toBeVisible();
  await expect(toast).toContainText('"Live Review" was permanently deleted.');
});