import { cleanup, fireEvent, render, screen, waitFor, within } from "@testing-library/react"; import { afterEach, describe, expect, it, vi } from "vitest"; import { useRef, useState } from "react"; import { ContextMenu, ContextMenuTrigger } from "@kandev/ui/context-menu"; import { DeleteSessionDialog, DeleteSessionPopover, SessionContextMenuItems, } from "./session-tab-menu"; describe("DeleteSessionDialog", () => { it("states conversation deletion and workspace retention", () => { render( , ); const dialog = screen.getByRole("alertdialog"); expect(dialog.textContent).toContain("permanently delete the conversation history"); expect(dialog.textContent).toContain("task workspace and its files are kept"); expect(dialog.textContent).toContain("only session for this task"); const description = dialog.querySelector('[data-slot="alert-dialog-description"]'); expect(description).not.toBeNull(); expect(description?.id).toBe(dialog.getAttribute("aria-describedby")); expect(description?.classList.contains("min-w-0")).toBe(true); expect(description?.classList.contains("text-left")).toBe(true); expect(description?.querySelectorAll("p")).toHaveLength(2); // No uncommitted/unpushed warning belongs on session deletion. expect(dialog.textContent).not.toContain("uncommitted"); expect(dialog.textContent).not.toContain("unpushed"); }); it("confirms deletion when the destructive action is activated", () => { const onConfirm = vi.fn(); const onOpenChange = vi.fn(); render( , ); fireEvent.click(screen.getByRole("button", { name: "Delete" })); expect(onConfirm).toHaveBeenCalledTimes(1); expect(onOpenChange).toHaveBeenCalledWith(false); }); }); afterEach(() => { cleanup(); }); describe("SessionContextMenuItems", () => { it("keeps the delete menu action open for its local confirmation", () => { const onDelete = vi.fn<(event: Event) => void>(); render( , ); fireEvent.contextMenu(screen.getByRole("button", { name: "Session" }), { clientX: 100, clientY: 100, }); fireEvent.click(screen.getByRole("menuitem", { name: "Delete" })); expect(onDelete).toHaveBeenCalledTimes(1); expect(onDelete.mock.calls[0]?.[0].defaultPrevented).toBe(true); }); it("omits Close Others (and its separator) when the action isn't provided", () => { render( , ); fireEvent.contextMenu(screen.getByRole("button", { name: "Session" }), { clientX: 100, clientY: 100, }); expect(screen.getByRole("menuitem", { name: "Delete" })).toBeTruthy(); expect(screen.queryByRole("menuitem", { name: "Close Others" })).toBeNull(); // Only the setAsPrimary/stop-or-resume separator renders; no orphan // separator is left behind where Close Others' own separator would go. expect(screen.getAllByRole("separator")).toHaveLength(1); }); }); function SessionDeletePopoverHarness({ onConfirm }: { onConfirm: () => void }) { const [open, setOpen] = useState(true); const anchorRef = useRef(null); return ( <> ); } describe("DeleteSessionPopover", () => { it("cancels locally without dispatching an alert dialog", async () => { const onConfirm = vi.fn(); render(); const popover = await screen.findByTestId("session-delete-confirm-popover"); expect(screen.queryByRole("alertdialog")).toBeNull(); fireEvent.click(within(popover).getByRole("button", { name: "Cancel" })); expect(onConfirm).not.toHaveBeenCalled(); await waitFor(() => expect(screen.queryByTestId("session-delete-confirm-popover")).toBeNull()); }); it("confirms once after closing the local shell", async () => { let shellClosed = false; const onConfirm = vi.fn(() => { shellClosed = screen.queryByTestId("session-delete-confirm-popover") === null; }); render(); fireEvent.click(screen.getByTestId("session-delete-confirm")); await waitFor(() => expect(onConfirm).toHaveBeenCalledTimes(1)); expect(shellClosed).toBe(true); expect(screen.queryByRole("alertdialog")).toBeNull(); }); });