Ctrl K
ComunidadGuías…

tests/e2e/flows-publish-activate.spec.ts

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

Archivo original ↗Preguntar a KiBot
import { expect, test } from "@playwright/test";
import { buildE2EAuthHeaders } from "./helpers/auth";
import { linkedVercelCapability } from "./helpers/activation-fixtures";
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,
  vercel: linkedVercelCapability,
};

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

type TestFlowPayload = {
  id: string;
  installation_id: number;
  name: string;
  description: string;
  notes: string;
  source_kind: "github";
  status: "active" | "inactive";
  last_run_status: string | null;
  published_version_id: string | null;
  published_version: {
    id: string;
    flow_id: string;
    version_number: number;
    graph: Record<string, unknown>;
    created_at: string;
  } | null;
  draft_graph: {
    nodes: Array<Record<string, unknown>>;
    edges: Array<Record<string, unknown>>;
    viewport: { x: number; y: number; zoom: number };
  };
};

test("publishing a flow makes it live without a separate activation step", async ({
  page,
}) => {
  await page.context().setExtraHTTPHeaders({
    ...buildE2EAuthHeaders(connectedUser.id),
    "x-mogplex-scope-kind": "personal",
    "x-mogplex-scope-slug": connectedUser.username,
    "x-mogplex-scope-id": connectedUser.id,
  });
  await page.addInitScript(() => {
    window.localStorage.setItem("theme", "dark");
  });

  let publishCalls = 0;
  const statusUpdates: Array<"active" | "inactive"> = [];
  let currentFlow: TestFlowPayload = {
    id: "flow-1",
    installation_id: 101,
    name: "Review Flow",
    description: "Flow description",
    notes: "Flow notes",
    source_kind: "github",
    status: "inactive" as const,
    last_run_status: null,
    published_version_id: null as string | null,
    published_version: null as {
      id: string;
      flow_id: string;
      version_number: number;
      graph: Record<string, unknown>;
      created_at: string;
    } | null,
    draft_graph: {
      nodes: [
        {
          id: "start",
          type: "start",
          position: { x: 120, y: 160 },
          data: { label: "PR opened", event: "pr_opened" },
        },
        {
          id: "agent-a",
          type: "agent",
          position: { x: 380, y: 160 },
          data: { label: "Reviewer A", agentId: "agent-a" },
        },
        {
          id: "end",
          type: "end",
          position: { x: 660, y: 160 },
          data: { label: "Done" },
        },
      ],
      edges: [
        { id: "edge-1", source: "start", target: "agent-a" },
        { id: "edge-2", source: "agent-a", target: "end" },
      ],
      viewport: { x: 0, y: 0, zoom: 1 },
    },
  };

  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: [
        {
          id: "minimax/minimax-m2.5",
          provider: "minimax",
          name: "MiniMax M2.5",
          context_length: 200000,
          capabilities: ["text"],
          is_available: true,
        },
      ],
      catalog: [
        {
          id: "minimax/minimax-m2.5",
          provider: "minimax",
          name: "MiniMax M2.5",
          context_length: 200000,
          capabilities: ["text"],
          is_available: true,
          is_enabled: true,
        },
      ],
    })
  );
  await page.route("**/api/agents", (route) =>
    fulfillJson(route, [
      {
        id: "agent-a",
        name: "Reviewer A",
        slug: "reviewer-a",
        model: "minimax/minimax-m2.5",
      },
    ])
  );
  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, [currentFlow])
  );
  await page.route("**/api/flows/flow-1", async (route) => {
    if (route.request().method() === "GET") {
      await fulfillJson(route, currentFlow);
      return;
    }

    const payload = route.request().postDataJSON() as Partial<TestFlowPayload>;
    if (payload.status === "active" || payload.status === "inactive") {
      statusUpdates.push(payload.status);
    }

    currentFlow = {
      ...currentFlow,
      ...payload,
      draft_graph: payload.draft_graph ?? currentFlow.draft_graph,
    };
    await fulfillJson(route, currentFlow);
  });
  await page.route("**/api/flows/flow-1/publish", async (route) => {
    publishCalls += 1;
    const publishedGraph = structuredClone(currentFlow.draft_graph);
    currentFlow = {
      ...currentFlow,
      status: "active",
      published_version_id: "version-1",
      published_version: {
        id: "version-1",
        flow_id: "flow-1",
        version_number: 1,
        graph: publishedGraph,
        created_at: "2026-03-28T17:00:00.000Z",
      },
    };
    await fulfillJson(route, currentFlow);
  });
  await page.route("**/api/flows/flow-1/runs?limit=12", (route) =>
    fulfillJson(route, { runs: [] })
  );

  await page.goto("/alex/workflows");
  await page.waitForLoadState("networkidle");

  const publishButton = page.getByRole("button", {
    name: "Publish & activate",
  });
  await expect(publishButton).toBeVisible();

  await publishButton.click();

  await expect.poll(() => publishCalls).toBe(1);
  await expect.poll(() => currentFlow.status).toBe("active");
  await expect(page.getByTestId("flow-publish-button")).toContainText(
    "Published"
  );
  await expect(statusUpdates).toEqual([]);

  await page.reload();
  await page.waitForLoadState("networkidle");

  const cleanPublishButton = page.getByTestId("flow-publish-button");
  await expect(cleanPublishButton).toHaveText("Publish changes");
  await expect(cleanPublishButton).toBeDisabled();
  expect(publishCalls).toBe(1);

  await page.getByRole("button", { name: "Add agent" }).click();
  await expect(cleanPublishButton).toBeEnabled();
});