Ctrl K
ComunidadGuías…

tests/e2e/connections-presets-settings.spec.ts

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

Archivo original ↗Preguntar a KiBot
import { mockSettingsShell } from "./helpers/billing-settings-fixtures";
import { expect, test } from "@playwright/test";
import { enableScopedE2EAuth, scopedPath } from "./helpers/auth";
import {
  connectedUser,
  modelId,
  fulfillJson,
} from "./helpers/connections-presets-fixtures";

test.beforeEach(async ({ page }) => {
  await mockSettingsShell(page);
});

test("settings handles duplicate preset adds gracefully and shows preset origin badge", async ({
  page,
}) => {
  let connections: Array<Record<string, unknown>> = [];
  const existingConnection = {
    id: "conn-supabase-existing",
    user_id: "user-1",
    name: "Supabase",
    type: "mcp_server",
    base_url: null,
    auth_type: "bearer",
    auth_header: null,
    mcp_transport: "http",
    mcp_url: "https://mcp.supabase.com/mcp",
    description: "Database, auth, storage, and edge functions",
    is_enabled: true,
    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: "supabase",
    created_at: new Date().toISOString(),
    updated_at: new Date().toISOString(),
  };

  await enableScopedE2EAuth(page);

  await page.route("**/api/auth/user", (route) =>
    fulfillJson(route, { user: connectedUser })
  );
  await page.route("**/api/settings", (route) =>
    fulfillJson(route, { default_model: modelId, theme: "dark" })
  );
  await page.route("**/api/models", (route) =>
    fulfillJson(route, {
      models: [{ id: modelId, context_length: 128000 }],
      catalog: [{ id: modelId, context_length: 128000, is_enabled: true }],
    })
  );
  await page.route("**/api/connections", async (route) => {
    if (route.request().method() === "GET") {
      await fulfillJson(route, { connections });
      return;
    }

    if (route.request().method() === "POST") {
      connections = [existingConnection];
      await fulfillJson(
        route,
        {
          error: "Supabase is already connected from this preset",
          code: "PRESET_ALREADY_CONNECTED",
          connection: existingConnection,
        },
        409
      );
      return;
    }

    await fulfillJson(route, { ok: true });
  });

  await page.goto(scopedPath("connections"));
  await page.waitForLoadState("networkidle");

  await expect(page.getByTestId("settings-preset-manual-hint")).toContainText(
    "Need another advanced MCP? Use Add Connection instead."
  );
  await expect(page.getByTestId("settings-preset-notion")).toHaveCount(1);
  await expect(page.getByTestId("settings-preset-zapier")).toHaveCount(1);

  const presetCard = page.getByTestId("settings-preset-supabase");
  await presetCard.getByRole("button", { name: "+ Add" }).click();
  await presetCard.getByPlaceholder("sbp_...").fill("sbp_duplicate_token");
  await presetCard.getByRole("button", { name: "Add" }).click();

  await expect(presetCard).toContainText("Connected");
  await expect(presetCard).toContainText("already connected from this preset");
  await expect(page.getByText("preset · Supabase")).toBeVisible();
});

test("settings starts provider OAuth for hosted MCP presets instead of auto-testing them", async ({
  page,
}) => {
  const connections: Array<Record<string, unknown>> = [];
  let oauthStarts = 0;
  let oauthTests = 0;

  await enableScopedE2EAuth(page);

  await page.route("**/api/auth/user", (route) =>
    fulfillJson(route, { user: connectedUser })
  );
  await page.route("**/api/settings", (route) =>
    fulfillJson(route, { default_model: modelId, theme: "dark" })
  );
  await page.route("**/api/models", (route) =>
    fulfillJson(route, {
      models: [{ id: modelId, context_length: 128000 }],
      catalog: [{ id: modelId, context_length: 128000, is_enabled: true }],
    })
  );
  await page.route("**/api/connections", async (route) => {
    if (route.request().method() === "GET") {
      await fulfillJson(route, { connections });
      return;
    }

    const created = {
      id: "conn-notion-new",
      user_id: "user-1",
      name: "Notion",
      type: "mcp_server",
      base_url: null,
      auth_type: "oauth",
      auth_header: "Authorization",
      mcp_transport: "http",
      mcp_url: "https://mcp.notion.com/mcp",
      description: "Workspace search, pages, databases, and comments",
      is_enabled: true,
      health_status: "unknown",
      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: "notion",
      created_at: new Date().toISOString(),
      updated_at: new Date().toISOString(),
    };
    connections.splice(0, connections.length, created);
    await fulfillJson(route, { connection: created }, 201);
  });
  await page.route(
    "**/api/connections/oauth?connectionId=conn-notion-new",
    async (route) => {
      oauthStarts += 1;
      await route.fulfill({
        status: 302,
        headers: {
          location: scopedPath("connections?oauth=success"),
        },
        body: "",
      });
    }
  );
  await page.route("**/api/connections/conn-notion-new/test", async (route) => {
    oauthTests += 1;
    await fulfillJson(route, { healthy: false, error: "should not run" }, 500);
  });

  await page.goto(scopedPath("connections"));
  await page.waitForLoadState("networkidle");

  const presetCard = page.getByTestId("settings-preset-notion");
  await presetCard.getByRole("button", { name: "+ Add" }).click();
  await expect(presetCard).toContainText(
    "Connect through the provider OAuth flow"
  );
  await presetCard.getByRole("button", { name: "Connect" }).click();

  await page.waitForURL("**/connections?oauth=success");
  await expect(
    page.getByText("OAuth connection established. Run a test to verify tools.")
  ).toBeVisible();
  await expect(oauthStarts).toBe(1);
  await expect(oauthTests).toBe(0);
});

test("settings quick-add supports Zapier's secret full MCP URL", async ({
  page,
}) => {
  const connections: Array<Record<string, unknown>> = [];

  await enableScopedE2EAuth(page);

  await page.route("**/api/auth/user", (route) =>
    fulfillJson(route, { user: connectedUser })
  );
  await page.route("**/api/settings", (route) =>
    fulfillJson(route, { default_model: modelId, theme: "dark" })
  );
  await page.route("**/api/models", (route) =>
    fulfillJson(route, {
      models: [{ id: modelId, context_length: 128000 }],
      catalog: [{ id: modelId, context_length: 128000, is_enabled: true }],
    })
  );
  await page.route("**/api/connections", async (route) => {
    if (route.request().method() === "GET") {
      await fulfillJson(route, { connections });
      return;
    }

    const body = route.request().postDataJSON() as Record<string, string>;
    expect(body.source_preset).toBe("zapier");
    expect(body.mcp_url).toBe("https://mcp.zapier.com/custom/server-secret");
    expect(body.credentials).toBeUndefined();

    const created = {
      id: "conn-zapier-new",
      user_id: "user-1",
      name: "Zapier",
      type: "mcp_server",
      base_url: null,
      auth_type: "none",
      auth_header: "Authorization",
      mcp_transport: "http",
      mcp_url: "https://mcp.zapier.com/custom/server-secret",
      description: "User-specific Zapier MCP server and tool actions",
      is_enabled: true,
      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: "zapier",
      created_at: new Date().toISOString(),
      updated_at: new Date().toISOString(),
    };
    connections.splice(0, connections.length, created);
    await fulfillJson(route, { connection: created }, 201);
  });
  await page.route("**/api/connections/conn-zapier-new/test", async (route) => {
    await fulfillJson(route, { healthy: true, toolCount: 12 });
  });

  await page.goto(scopedPath("connections"));
  await page.waitForLoadState("networkidle");

  const presetCard = page.getByTestId("settings-preset-zapier");
  await presetCard.getByRole("button", { name: "+ Add" }).click();
  await expect(presetCard).toContainText(
    "Paste the full MCP server URL from Zapier's Connect tab"
  );
  await presetCard
    .getByPlaceholder("https://mcp.zapier.com/...")
    .fill("https://mcp.zapier.com/custom/server-secret");
  await presetCard.getByRole("button", { name: "Add" }).click();

  await expect(presetCard).toContainText("Connected · 12 tools");
  await expect(page.getByText("preset · Zapier")).toBeVisible();
});

test("settings manual add preserves input and surfaces API errors", async ({
  page,
}) => {
  await enableScopedE2EAuth(page);

  await page.route("**/api/auth/user", (route) =>
    fulfillJson(route, { user: connectedUser })
  );
  await page.route("**/api/settings", (route) =>
    fulfillJson(route, { default_model: modelId, theme: "dark" })
  );
  await page.route("**/api/models", (route) =>
    fulfillJson(route, {
      models: [{ id: modelId, context_length: 128000 }],
      catalog: [{ id: modelId, context_length: 128000, is_enabled: true }],
    })
  );
  await page.route("**/api/connections", async (route) => {
    if (route.request().method() === "GET") {
      await fulfillJson(route, { connections: [] });
      return;
    }

    await fulfillJson(route, { error: "mcp_url could not be reached" }, 400);
  });

  await page.goto(scopedPath("connections"));
  await page.waitForLoadState("networkidle");

  const connectionTypeSelect = page
    .locator("select")
    .filter({ has: page.locator('option[value="mcp_server"]') })
    .last();
  await connectionTypeSelect.selectOption("mcp_server");
  await page.getByPlaceholder("name").fill("Browserbase MCP");
  await page
    .getByPlaceholder("https://mcp.example.com")
    .fill("https://mcp.browserbase.com/mcp");
  await page.getByPlaceholder("token").fill("bb_live_123");
  await page.getByRole("button", { name: "Add Connection" }).click();

  await expect(page.getByText("mcp_url could not be reached")).toBeVisible();
  await expect(page.getByPlaceholder("name")).toHaveValue("Browserbase MCP");
  await expect(page.getByPlaceholder("https://mcp.example.com")).toHaveValue(
    "https://mcp.browserbase.com/mcp"
  );
  await expect(page.getByPlaceholder("token")).toHaveValue("bb_live_123");
});

test("settings manual add surfaces network failures and offers OAuth only for MCP servers", async ({
  page,
}) => {
  await enableScopedE2EAuth(page);

  await page.route("**/api/auth/user", (route) =>
    fulfillJson(route, { user: connectedUser })
  );
  await page.route("**/api/settings", (route) =>
    fulfillJson(route, { default_model: modelId, theme: "dark" })
  );
  await page.route("**/api/models", (route) =>
    fulfillJson(route, {
      models: [{ id: modelId, context_length: 128000 }],
      catalog: [{ id: modelId, context_length: 128000, is_enabled: true }],
    })
  );
  await page.route("**/api/connections", async (route) => {
    if (route.request().method() === "GET") {
      await fulfillJson(route, { connections: [] });
      return;
    }

    await route.abort("failed");
  });

  await page.goto(scopedPath("connections"));
  await page.waitForLoadState("networkidle");

  const connectionTypeSelect = page
    .locator("select")
    .filter({ has: page.locator('option[value="mcp_server"]') })
    .last();
  const authSelect = page
    .locator("select")
    .filter({ has: page.locator('option[value="bearer"]') })
    .last();
  await expect(authSelect.locator('option[value="oauth"]')).toHaveCount(0);

  await connectionTypeSelect.selectOption("mcp_server");
  await expect(authSelect.locator('option[value="oauth"]')).toHaveCount(1);

  await page.getByPlaceholder("name").fill("Network Error MCP");
  await page
    .getByPlaceholder("https://mcp.example.com")
    .fill("https://example.com/mcp");
  await page.getByPlaceholder("token").fill("secret-token");
  await page.getByRole("button", { name: "Add Connection" }).click();

  await expect(
    page.getByText("Network error while adding connection")
  ).toBeVisible();
  await expect(page.getByPlaceholder("name")).toHaveValue("Network Error MCP");
  await expect(page.getByPlaceholder("https://mcp.example.com")).toHaveValue(
    "https://example.com/mcp"
  );
  await expect(page.getByPlaceholder("token")).toHaveValue("secret-token");
});

test("settings shows oauth reconnect and surfaces hard connection test failures", async ({
  page,
}) => {
  const oauthConnection = {
    id: "conn-oauth-reconnect",
    user_id: "user-1",
    name: "Linear OAuth",
    type: "mcp_server",
    base_url: null,
    auth_type: "oauth",
    auth_header: "Authorization",
    mcp_transport: "http",
    mcp_url: "https://mcp.linear.app/mcp",
    description: "OAuth-backed MCP",
    is_enabled: true,
    health_status: "auth_failed",
    scope: "global" as const,
    repo_id: null,
    oauth_client_id: "client-123",
    oauth_authorize_url: "https://linear.app/oauth/authorize",
    oauth_token_url: "https://api.linear.app/oauth/token",
    oauth_scopes: "read write",
    oauth_authorized_at: new Date(Date.now() - 120_000).toISOString(),
    oauth_token_expires_at: new Date(Date.now() - 60_000).toISOString(),
    source_preset: null,
    last_tested_at: new Date().toISOString(),
    last_test_error:
      "OAuth token expired and no refresh token available — re-authorize required",
    last_test_http_status: null,
    last_test_tool_count: null,
    created_at: new Date().toISOString(),
    updated_at: new Date().toISOString(),
  };

  await enableScopedE2EAuth(page);

  await page.route("**/api/auth/user", (route) =>
    fulfillJson(route, { user: connectedUser })
  );
  await page.route("**/api/settings", (route) =>
    fulfillJson(route, { default_model: modelId, theme: "dark" })
  );
  await page.route("**/api/models", (route) =>
    fulfillJson(route, {
      models: [{ id: modelId, context_length: 128000 }],
      catalog: [{ id: modelId, context_length: 128000, is_enabled: true }],
    })
  );
  await page.route("**/api/connections", async (route) => {
    if (route.request().method() === "GET") {
      await fulfillJson(route, { connections: [oauthConnection] });
      return;
    }

    await fulfillJson(route, { ok: true });
  });
  await page.route(
    "**/api/connections/conn-oauth-reconnect/test",
    async (route) => {
      await fulfillJson(
        route,
        {
          error: "Connection test completed but the result could not be saved",
          code: "TEST_STATUS_PERSIST_FAILED",
        },
        500
      );
    }
  );

  await page.goto(scopedPath("connections"));
  await page.waitForLoadState("networkidle");

  await page.getByLabel("Connection actions").click();
  await expect(
    page.getByRole("menuitem", { name: "Reconnect OAuth" })
  ).toBeVisible();
  await page.getByRole("menuitem", { name: "Test Connection" }).click();

  await expect(
    page.getByText(
      "Connection test completed but the result could not be saved"
    )
  ).toBeVisible();
});