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();
});
});