// @vitest-environment happy-dom import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { DaemonInfo, Project, Session } from "../../types.js"; import { SpawnModal } from "./SpawnModal.js"; vi.mock("../../api.js", () => ({ createSession: vi.fn(), fetchBranches: vi.fn(), fetchNames: vi.fn(), getDaemonInfo: vi.fn(), gitRoot: vi.fn(), listDir: vi.fn(), projects: vi.fn(), })); import { createSession, fetchNames, getDaemonInfo, listDir, projects as fetchProjects } from "../../api.js"; function info(): DaemonInfo { return { hostname: "h", version: "1", home_dir: "/home/n", presets: [{ id: "claude", label: "Claude", command: "claude" }] }; } function project(over: Partial = {}): Project { return { path: "/repos/p", name: "p", ...over }; } beforeEach(() => { vi.mocked(getDaemonInfo).mockResolvedValue(info()); vi.mocked(fetchProjects).mockResolvedValue([project()]); vi.mocked(fetchNames).mockResolvedValue({ names: ["amber-fox"] }); vi.mocked(listDir).mockResolvedValue({ path: "", entries: [] }); }); afterEach(() => { cleanup(); vi.clearAllMocks(); }); async function settle() { await waitFor(() => expect(screen.queryByText("Loading projects…")).toBeNull()); } describe("SpawnModal", () => { it("mounts the spawn form inside a dialog", async () => { render(); await settle(); expect(screen.getByRole("dialog")).not.toBeNull(); expect(screen.getByRole("button", { name: "Spawn" })).not.toBeNull(); }); it("titles a seeded open as a clone", async () => { render(); await settle(); expect(screen.getByText("Spawn similar")).not.toBeNull(); }); it("titles a plain open as a new session", async () => { render(); await settle(); expect(screen.getByText("Spawn session")).not.toBeNull(); }); it("closes on Escape, on Cancel and on a backdrop click", async () => { const onClose = vi.fn(); const { unmount } = render(); await settle(); fireEvent.keyDown(screen.getByRole("dialog"), { key: "Escape" }); expect(onClose).toHaveBeenCalledTimes(1); fireEvent.click(screen.getByRole("button", { name: "Cancel" })); expect(onClose).toHaveBeenCalledTimes(2); unmount(); render(); await settle(); fireEvent.click(document.querySelector(".modal-backdrop")!); expect(onClose).toHaveBeenCalledTimes(3); }); it("closes before handing the created session to its caller", async () => { const created: Session = { id: "s1", cwd: "/repos/p", command: "claude", env: {}, metadata: {}, status: "Running", created_at: "2026-01-01T00:00:00Z", }; vi.mocked(createSession).mockResolvedValue(created); const calls: string[] = []; const onClose = vi.fn(() => calls.push("close")); const onSuccess = vi.fn(() => calls.push("success")); render(); await settle(); fireEvent.click(screen.getByText("p")); fireEvent.click(screen.getByRole("button", { name: "Spawn" })); await waitFor(() => expect(onSuccess).toHaveBeenCalledWith(created)); expect(calls).toEqual(["close", "success"]); }); });