Ctrl K
ComunidadGuías…

tests/e2e/connections-mcp-no-cap.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 {
  mockBaseChrome,
  mockControlSessionBootstrap,
} from "./helpers/automation-control-plane-fixtures";
import {
  fulfillJson,
  setupWorkspaceRoutes,
} from "./helpers/connections-presets-fixtures";
import type { Page } from "@playwright/test";

// More than the five servers the removed cap allowed.
const EXISTING_MCP_COUNT = 7;
const CAP_COPY = /at cap|max \d|reached/i;
// Server names render beside a scope badge, so they match as substrings.

function mcpConnection(index: number, name = `Fixture MCP ${index}`) {
  return {
    id: `conn-mcp-${index}`,
    user_id: "user-1",
    name,
    type: "mcp_server",
    base_url: null,
    auth_type: "none",
    auth_header: null,
    mcp_transport: "http",
    mcp_url: `https://mcp-${index}.example.com/mcp`,
    description: null,
    is_enabled: true,
    approval_mode: "auto",
    health_status: "healthy",
    scope: "global",
    repo_id: null,
    oauth_client_id: null,
    oauth_authorize_url: null,
    oauth_token_url: null,
    oauth_scopes: null,
    oauth_authorized_at: null,
    oauth_token_expires_at: null,
    source_preset: null,
    created_at: "2026-11-09T00:00:00.000Z",
    updated_at: "2026-11-09T00:00:00.000Z",
  };
}

const existingNames = Array.from(
  { length: EXISTING_MCP_COUNT },
  (_, index) => `Fixture MCP ${index + 1}`
);

/**
 * Serves one in-memory list of MCP connections from every connections read
 * and appends whatever the UI creates.
 */
async function mockManyMcpConnections(page: Page) {
  const connections = existingNames.map((name, index) =>
    mcpConnection(index + 1, name)
  );
  const created: Array<Record<string, unknown>> = [];
  const resolved = () => ({
    connections,
    overrides: [],
    resolved_mcp_count: connections.length,
  });
  await page.route(/\/api\/repos\/repo-1\/connections$/, (route) =>
    fulfillJson(route, resolved())
  );
  await page.route("**/api/connections", async (route) => {
    if (route.request().method() !== "POST") {
      await fulfillJson(route, { connections });
      return;
    }
    const body = route.request().postDataJSON() as Record<string, unknown>;
    created.push(body);
    const connection = {
      ...mcpConnection(connections.length + 1, String(body.name)),
      scope: body.scope === "project" ? "project" : "global",
    };
    connections.push(connection);
    await fulfillJson(route, { connection }, 201);
  });
  return { created };
}

/** Opens the repo-1 workspace and returns its first agent pane. */
async function openWorkspace(page: Page) {
  await enableScopedE2EAuth(page);
  await setupWorkspaceRoutes(page);
  const mocks = await mockManyMcpConnections(page);
  await page.goto(scopedPath("projects/workspace"));
  await page.waitForLoadState("networkidle");
  await page.getByTestId("home-open-workspace-repo-1").click();
  return {
    ...mocks,
    agentPane: page.locator('[data-pane-type="agent"]').first(),
  };
}

test("should list every MCP server and keep adding open past five in the workspace chat menu", async ({
  page,
}) => {
  const { created, agentPane } = await openWorkspace(page);

  await agentPane.getByTitle("MCP tools status").click();
  const menu = page.getByRole("dialog", { name: "MCP servers" });
  for (const name of existingNames) {
    await expect(menu.getByText(name)).toBeVisible();
  }
  await expect(menu.getByText(CAP_COPY)).toHaveCount(0);
  const addFromMenu = menu.getByRole("button", {
    name: "+ Add MCP server",
    exact: true,
  });
  await expect(addFromMenu).toBeEnabled();
  await addFromMenu.click();
  await expect(menu.getByText(CAP_COPY)).toHaveCount(0);
  await menu.getByPlaceholder("Name (e.g. Supabase)").fill("Menu MCP");
  await menu
    .getByPlaceholder("https://mcp.example.com/sse")
    .fill("https://menu.example.com/mcp");
  await menu.getByRole("button", { name: "Add", exact: true }).click();
  await expect(menu.getByText("Menu MCP")).toBeVisible();

  expect(created).toEqual([
    expect.objectContaining({ type: "mcp_server", name: "Menu MCP" }),
  ]);
});

test("should list every MCP server and keep adding open past five in the workspace connections pane", async ({
  page,
}) => {
  const { created, agentPane } = await openWorkspace(page);

  await agentPane.getByTitle("Add pane").click();
  await page.getByRole("menuitem", { name: "Connections" }).first().click();
  const pane = page.locator('[data-pane-type="connections"]').last();
  await expect(
    pane.getByText(`${EXISTING_MCP_COUNT} MCPs`, { exact: true })
  ).toBeVisible();
  // A narrow pane can squeeze the name column to zero width, so rows are
  // matched by their text rather than by the name's visibility.
  const rows = pane.locator("[data-connection-id]");
  await expect(rows).toHaveCount(EXISTING_MCP_COUNT);
  for (const name of existingNames) {
    await expect(rows.filter({ hasText: name })).toHaveCount(1);
  }
  await expect(pane.getByText(CAP_COPY)).toHaveCount(0);
  await pane.getByRole("button", { name: "Add connection" }).click();
  const mcpType = pane.getByRole("button", { name: "MCP Server", exact: true });
  await expect(mcpType).toBeEnabled();
  await mcpType.click();
  await pane.getByPlaceholder("Connection name").fill("Pane MCP");
  await pane
    .getByPlaceholder("MCP Server URL")
    .fill("https://pane.example.com/mcp");
  await pane.getByRole("button", { name: "Create" }).click();
  await expect(
    pane.getByText(`${EXISTING_MCP_COUNT + 1} MCPs`, { exact: true })
  ).toBeVisible();
  await expect(rows).toHaveCount(EXISTING_MCP_COUNT + 1);
  await expect(rows.filter({ hasText: "Pane MCP" })).toHaveCount(1);

  expect(created).toEqual([
    expect.objectContaining({ type: "mcp_server", name: "Pane MCP" }),
  ]);
});

test("should list every global MCP server and keep adding open past five in the Control composer", async ({
  page,
}) => {
  await enableScopedE2EAuth(page);
  await mockBaseChrome(page);
  await mockControlSessionBootstrap(page);
  const { created } = await mockManyMcpConnections(page);
  await page.route("**/api/control/chat", (route) =>
    route.fulfill({
      status: 200,
      contentType: "text/event-stream",
      headers: { "x-vercel-ai-ui-message-stream": "v1" },
      body: 'data: {"type":"start","messageId":"fixture-answer"}\n\ndata: {"type":"finish"}\n\ndata: [DONE]\n\n',
    })
  );

  await page.goto(scopedPath("/control"));
  await page
    .getByPlaceholder("Ask anything or run a command...")
    .fill("List the MCP servers");
  await page.getByRole("button", { name: "Start mission" }).click();
  await expect(page.getByRole("button", { name: "Send" })).toBeVisible();

  await page.getByTitle("MCP tools status").click();
  const menu = page.getByRole("dialog", { name: "MCP servers" });
  for (const name of existingNames) {
    await expect(menu.getByText(name)).toBeVisible();
  }
  await expect(menu.getByText(CAP_COPY)).toHaveCount(0);
  const addFromMenu = menu.getByRole("button", {
    name: "+ Add MCP server",
    exact: true,
  });
  await expect(addFromMenu).toBeEnabled();
  await addFromMenu.click();
  await expect(menu.getByText(CAP_COPY)).toHaveCount(0);
  await menu.getByPlaceholder("Name (e.g. Supabase)").fill("Control MCP");
  await menu
    .getByPlaceholder("https://mcp.example.com/sse")
    .fill("https://control.example.com/mcp");
  await menu.getByRole("button", { name: "Add", exact: true }).click();
  await expect(menu.getByText("Control MCP")).toBeVisible();

  expect(created).toEqual([
    expect.objectContaining({
      type: "mcp_server",
      name: "Control MCP",
      scope: "global",
    }),
  ]);
});