Ctrl K
ComunidadGuías…

tests/e2e/triggers-pane.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 { linkedVercelCapability } from "./helpers/activation-fixtures";
import type { Route } from "@playwright/test";

const connectedUser = {
  id: "user-1",
  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,
};

const modelId = "minimax/minimax-m2.5";

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

type TriggerPayload = {
  installation_id?: number;
  agent_id?: string;
  event?: string;
  is_default?: boolean;
};

test("triggers installation combobox shows scope and supports repo-name search", async ({
  page,
}) => {
  let postedTrigger: TriggerPayload | undefined;

  await enableScopedE2EAuth(page);

  await page.route("**/api/auth/user", (route) =>
    fulfillJson(route, { user: connectedUser })
  );
  await page.route("**/api/repos**", (route) =>
    fulfillJson(route, [
      { id: "repo-1", full_name: "acme/web-app", github_installation_id: 101 },
      { id: "repo-2", full_name: "acme/api", github_installation_id: 101 },
      {
        id: "repo-3",
        full_name: "alex/personal-tool",
        github_installation_id: 202,
      },
    ])
  );
  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/agents", (route) =>
    fulfillJson(route, [
      { id: "agent-1", name: "Triage", slug: "triage", model: modelId },
      { id: "agent-2", name: "CI Watcher", slug: "ci-watcher", model: modelId },
    ])
  );
  await page.route("**/api/github/installations", (route) =>
    fulfillJson(route, [
      {
        id: "inst-org",
        installation_id: 101,
        account_login: "acme",
        account_type: "Organization",
        target_type: "Organization",
        repositories: [
          { id: "repo-1", full_name: "acme/web-app" },
          { id: "repo-2", full_name: "acme/api" },
        ],
      },
      {
        id: "inst-user",
        installation_id: 202,
        account_login: "alex",
        account_type: "User",
        target_type: "User",
        repositories: [{ id: "repo-3", full_name: "alex/personal-tool" }],
      },
    ])
  );
  await page.route("**/api/triggers", async (route) => {
    if (route.request().method() === "GET") {
      await fulfillJson(route, []);
      return;
    }

    const payload = route.request().postDataJSON() as TriggerPayload;
    postedTrigger = payload;
    await fulfillJson(
      route,
      {
        id: "trigger-1",
        installation_id: payload.installation_id,
        agent_id: payload.agent_id,
        event: payload.event,
        enabled: true,
        is_default: payload.is_default,
        agents: {
          id: "agent-2",
          name: "CI Watcher",
          slug: "ci-watcher",
          model: modelId,
        },
      },
      201
    );
  });

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

  const emptyState = page.getByTestId("triggers-empty-state");
  await expect(emptyState).toBeVisible();
  await expect(emptyState).toContainText("Create your first triggered agent");
  await expect(emptyState).toContainText("Choose an installation");

  await page.getByRole("button", { name: "Create first trigger" }).click();

  const combobox = page.getByTestId("trigger-installation-combobox");
  await combobox.click();
  await expect(page.getByText("acme/web-app, acme/api")).toBeVisible();
  await expect(page.getByText("alex/personal-tool")).toBeVisible();

  await page
    .getByPlaceholder("Search installations or repos...")
    .fill("personal-tool");
  await page.getByRole("option", { name: /alex/i }).click();
  await page.keyboard.press("Escape");

  await expect(combobox).toContainText("alex");
  await expect(combobox).toContainText("User · alex/personal-tool");

  await page
    .locator("select")
    .filter({ has: page.locator('option[value="agent-2"]') })
    .last()
    .selectOption("agent-2");
  await page
    .locator("select")
    .filter({ has: page.locator('option[value="ci_failure"]') })
    .first()
    .selectOption("ci_failure");
  await page.getByRole("button", { name: "Create" }).click();

  const submittedTrigger = postedTrigger;
  expect(submittedTrigger).toBeTruthy();
  if (!submittedTrigger) {
    throw new Error("Trigger payload was not captured");
  }
  expect(submittedTrigger.installation_id).toBe(202);
  expect(submittedTrigger.agent_id).toBe("agent-2");
  expect(submittedTrigger.event).toBe("ci_failure");
});

test("triggers empty state explains missing GitHub App installations", async ({
  page,
}) => {
  await enableScopedE2EAuth(page);

  await page.route("**/api/auth/user", (route) =>
    fulfillJson(route, {
      user: {
        ...connectedUser,
        github_app_connected: false,
        github_connection_mode: "oauth",
      },
    })
  );
  await page.route("**/api/repos**", (route) =>
    fulfillJson(route, [
      {
        id: "repo-1",
        full_name: "alex/personal-tool",
        github_installation_id: null,
      },
      { id: "repo-2", full_name: "alex/infra", github_installation_id: null },
    ])
  );
  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/agents", (route) =>
    fulfillJson(route, [
      { id: "agent-1", name: "Triage", slug: "triage", model: modelId },
    ])
  );
  await page.route("**/api/github/installations", (route) =>
    fulfillJson(route, [])
  );
  await page.route("**/api/triggers", (route) => fulfillJson(route, []));

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

  const emptyState = page.getByTestId("triggers-empty-state");
  await expect(emptyState).toContainText("No GitHub App installations found");
  await expect(emptyState).toContainText("2 synced repos");
  await expect(
    page.getByRole("link", { name: "Install GitHub App" })
  ).toBeVisible();
  await page
    .getByRole("link", { name: "Install GitHub App" })
    .first()
    .evaluate((node) => node.getAttribute("href"));

  await page
    .getByRole("link", { name: "Install GitHub App" })
    .first()
    .isVisible();
  await page
    .getByRole("link", { name: "Install GitHub App" })
    .first()
    .evaluate((node) => {
      if (
        !(node instanceof HTMLAnchorElement) ||
        node.getAttribute("href") !== "/api/auth/github"
      ) {
        throw new Error("Expected install CTA to target /api/auth/github");
      }
    });

  await page.getByRole("button", { name: "New trigger" }).click();
  await expect(
    page.getByText("Install the GitHub App to create triggers")
  ).toBeVisible();
  await expect(
    page.getByText("Current connection: GitHub OAuth")
  ).toBeVisible();
});