Ctrl K
ComunidadGuías…

tests/e2e/control-sidebar-organization.spec.ts

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

Archivo original ↗Preguntar a KiBot
import { expect, test } from "@playwright/test";
import { setupControlSidebar as setup } from "./helpers/control-sidebar-fixture";

test("sidebar organization and sorting persist across reload and preserve selection", async ({
  page,
}) => {
  const { sidebar } = await setup(page);
  await sidebar.getByRole("button", { name: "Sidebar options" }).click();
  await page.getByRole("menuitem", { name: "Organize sidebar" }).hover();
  await expect(
    page.getByRole("menuitemradio", { name: "By project" })
  ).toBeChecked();
  await page.getByRole("menuitemradio", { name: "In one list" }).click();
  await expect(
    sidebar.getByRole("button", { name: "Actions for acme/widgets" })
  ).toHaveCount(0);
  await expect(
    sidebar.getByRole("button", { name: /^Zebra investigation/ })
  ).toBeVisible();
  await expect(page).toHaveURL(/mission=chat-0/);
  await page.screenshot({ path: "/tmp/mogplex-sidebar-one-list.png" });
  await page.reload();
  await expect(
    sidebar.getByRole("button", { name: /^Zebra investigation/ })
  ).toBeVisible();
  await expect(
    sidebar.getByRole("button", { name: "Actions for acme/widgets" })
  ).toHaveCount(0);
  await sidebar.getByRole("button", { name: "Sidebar options" }).click();
  await page.getByRole("menuitem", { name: "Sort chats by" }).hover();
  await page.getByRole("menuitemradio", { name: "Name" }).click();
  await expect(
    sidebar.locator("nav button:not([aria-label])").nth(1)
  ).toContainText("Fix 1");
  await expect(sidebar.locator("nav button[aria-current]")).toHaveText(
    /Zebra investigation/
  );
  await page.reload();
  await expect(sidebar.locator("nav button").first()).toContainText(
    "Another project"
  );
  await sidebar.getByRole("button", { name: "Sidebar options" }).click();
  await page.getByRole("menuitem", { name: "Organize sidebar" }).hover();
  await expect(
    page.getByRole("menuitemradio", { name: "In one list" })
  ).toBeChecked();
  await page.screenshot({
    path: "/tmp/mogplex-sidebar-organize-menu.png",
    animations: "disabled",
  });
  await page.getByRole("menuitemradio", { name: "By project" }).click();
  await expect(
    sidebar.getByRole("button", { name: "Actions for acme/widgets" })
  ).toBeVisible();
  await page.screenshot({ path: "/tmp/mogplex-sidebar-organize.png" });
});

test("partial archive reports failures and Undo restores only the chats that changed", async ({
  page,
}) => {
  const { sidebar, sessions, failArchiveId } = await setup(page);
  failArchiveId("chat-3");
  await sidebar
    .getByRole("button", { name: "Actions for acme/widgets" })
    .click();
  await page
    .getByRole("menuitem", { name: "Archive chats", exact: true })
    .click();
  await expect(
    page.getByText("6 chats archived", { exact: true })
  ).toBeVisible();
  await expect(
    page.getByText("1 could not be archived. Try again.", { exact: true })
  ).toBeVisible();
  await expect(sidebar.getByRole("button", { name: /^Fix 3 / })).toBeVisible();
  expect(sessions.filter((session) => session.archived)).toHaveLength(6);
  await page.getByRole("button", { name: "Undo", exact: true }).click();
  await expect(
    sidebar.getByRole("button", { name: /acme\/widgets.*7/ })
  ).toBeVisible();
  expect(sessions.filter((session) => session.archived)).toHaveLength(0);
});

test("group archive includes hidden chats, supports Undo, and restores after reload", async ({
  page,
}) => {
  const { sidebar, sessions } = await setup(page);
  const archive = async () => {
    await sidebar
      .getByRole("button", { name: "Actions for acme/widgets" })
      .click();
    await page
      .getByRole("menuitem", { name: "Archive chats", exact: true })
      .click();
    await expect(
      page.getByText("7 chats archived", { exact: true })
    ).toBeVisible();
  };
  await archive();
  await expect(
    sidebar.getByRole("button", { name: "Actions for acme/widgets" })
  ).toHaveCount(0);
  await expect(
    sidebar.getByRole("button", { name: "Actions for acme/other" })
  ).toBeVisible();
  expect(sessions.filter((s) => s.archived)).toHaveLength(7);
  await expect(page).toHaveURL(/\/control$/);
  await page.getByRole("button", { name: "Undo", exact: true }).click();
  await expect(
    sidebar.getByRole("button", { name: /acme\/widgets.*7/ })
  ).toBeVisible();
  expect(sessions.filter((s) => s.archived)).toHaveLength(0);
  await archive();
  await expect(
    sidebar.getByRole("button", { name: "Actions for acme/widgets" })
  ).toHaveCount(0);
  await page.reload();
  await sidebar
    .getByRole("button", { name: "Archived chats", exact: true })
    .click();
  await expect(
    sidebar.getByRole("button", { name: "Restore Zebra investigation" })
  ).toBeVisible();
  await expect(sidebar.getByRole("button", { name: /^Restore / })).toHaveCount(
    7
  );
  await page.screenshot({ path: "/tmp/mogplex-sidebar-archive.png" });
  await sidebar
    .getByRole("button", { name: "Restore Zebra investigation" })
    .click();
  await expect(
    sidebar.getByRole("button", { name: "Restore Zebra investigation" })
  ).toHaveCount(0);
  await sidebar.getByRole("button", { name: "Back to chats" }).click();
  await expect(
    sidebar.getByRole("button", { name: /^Zebra investigation/ })
  ).toBeVisible();
});

test("archive failure leaves chats visible and reports the error", async ({
  page,
}) => {
  const { sidebar, failArchives } = await setup(page);
  failArchives();
  await sidebar
    .getByRole("button", { name: "Actions for acme/widgets" })
    .click();
  await page
    .getByRole("menuitem", { name: "Archive chats", exact: true })
    .click();
  await expect(
    page.getByText("Could not archive 7 chats. Try again.", { exact: true })
  ).toBeVisible();
  await expect(
    sidebar.getByRole("button", { name: /acme\/widgets.*7/ })
  ).toBeVisible();
  await expect(page).toHaveURL(/mission=chat-0/);
});

test("group archive keeps a submitted chat running while it archives the other chats", async ({
  page,
}) => {
  const { sidebar, sessions } = await setup(page);
  let finishResponse!: () => void;
  const responseReady = new Promise<void>((resolve) => {
    finishResponse = resolve;
  });
  await page.route("**/api/control/chat", async (route) => {
    await responseReady;
    await route.fulfill({
      status: 200,
      headers: {
        "content-type": "text/event-stream",
        "x-vercel-ai-ui-message-stream": "v1",
      },
      body: 'data: {"type":"start"}\n\ndata: {"type":"finish"}\n\ndata: [DONE]\n\n',
    });
  });
  try {
    await page
      .getByPlaceholder("Ask for follow-up changes or attach images")
      .fill("Keep working on this chat");
    await page.keyboard.press("Enter");
    await sidebar.getByRole("button", { name: "Show more" }).click();
    await expect(
      sidebar.getByRole("button", { name: /^Working Zebra investigation/ })
    ).toBeVisible();
    await sidebar
      .getByRole("button", { name: "Actions for acme/widgets" })
      .click();
    await page
      .getByRole("menuitem", { name: "Archive chats", exact: true })
      .click();
    await expect(
      sidebar.getByRole("button", { name: /acme\/widgets.*1/ })
    ).toBeVisible();
    await expect(
      sidebar.getByRole("button", { name: /^Working Zebra investigation/ })
    ).toBeVisible();
    expect(sessions.filter((session) => session.archived)).toHaveLength(6);
    expect(sessions.find((session) => session.id === "chat-0")?.archived).toBe(
      false
    );
    await expect(page).toHaveURL(/mission=chat-0/);
  } finally {
    finishResponse();
  }
});