// @vitest-environment happy-dom import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { createSpawnForm, type SpawnFormHandle } from "./spawnForm.js"; const BATCH_A = ["cello", "fugue", "lyre"]; /** Stub the fetches the form's initial load makes. Records every /api/names * URL so tests can assert how many handles were asked for. */ function stubFetch(batches: string[][]): string[] { let batch = 0; const nameUrls: string[] = []; vi.stubGlobal( "fetch", vi.fn(async (input: RequestInfo | URL) => { const url = String(input); if (url.includes("/api/names")) { nameUrls.push(url); const names = batches[Math.min(batch, batches.length - 1)]; batch += 1; return new Response(JSON.stringify({ names }), { status: 200 }); } const body = url.includes("/api/info") ? { hostname: "h", version: "0", home_dir: "/home/user", presets: [] } : []; return new Response(JSON.stringify(body), { status: 200 }); }), ); return nameUrls; } describe("spawnForm naming (#131)", () => { let handle: SpawnFormHandle | null = null; let nameUrls: string[]; beforeEach(() => { vi.useFakeTimers(); }); afterEach(() => { handle?.destroy(); handle = null; vi.unstubAllGlobals(); vi.useRealTimers(); }); async function mount(seed?: Parameters[0]["seed"]) { nameUrls = stubFetch([BATCH_A]); handle = createSpawnForm({ seed, onSuccess() {} }); document.body.appendChild(handle.el); for (let i = 0; i < 40; i++) await Promise.resolve(); return handle.el; } const nameOf = (form: HTMLFormElement) => form.querySelector("input[name=name]")!.value; const branchOf = (form: HTMLFormElement) => form.querySelector("input[name=worktree_branch]")!.value; it("leaves the name field empty so the daemon names the session", async () => { const form = await mount(); expect(nameOf(form)).toBe(""); expect(form.querySelector(".reroll")).toBeNull(); }); it("still seeds the worktree branch with one generated handle", async () => { const form = await mount(); expect(BATCH_A).toContain(branchOf(form)); expect(nameUrls).toHaveLength(1); expect(nameUrls[0]).toContain("count=1"); }); it("keeps a name a clone prefilled, and seeds the branch from it", async () => { const form = await mount({ name: "my-session" }); expect(nameOf(form)).toBe("my-session"); expect(branchOf(form)).toBe("my-session"); }); it("seeds the branch from a typed name, overriding the fetched handle", async () => { const form = await mount(); const input = form.querySelector("input[name=name]")!; input.value = "hand-typed"; input.dispatchEvent(new Event("input")); expect(nameOf(form)).toBe("hand-typed"); expect(branchOf(form)).toBe("hand-typed"); }); it("never overwrites a branch the operator typed", async () => { nameUrls = stubFetch([BATCH_A]); handle = createSpawnForm({ onSuccess() {} }); const form = handle.el; document.body.appendChild(form); const branch = form.querySelector("input[name=worktree_branch]")!; branch.value = "feat/mine"; branch.dispatchEvent(new Event("input")); for (let i = 0; i < 40; i++) await Promise.resolve(); expect(branchOf(form)).toBe("feat/mine"); }); });