import { afterEach, expect, test } from "bun:test"; import { Window } from "happy-dom"; import { act, createElement } from "react"; import { islandRootCount, mountIslands, remountIslands, unmountIslands, } from "../src/island-runtime.ts"; (globalThis as any).IS_REACT_ACT_ENVIRONMENT = true; function host(window: Window): ParentNode { return window.document.body as unknown as ParentNode; } function domWith(html: string) { const window = new Window(); window.document.body.innerHTML = html; (globalThis as any).window = window; (globalThis as any).document = window.document; return window; } afterEach(() => { const doc = (globalThis as any).document; if (!doc) return; act(() => { unmountIslands(doc); }); }); const marker = `
`; test("remount replaces island output without leaking roots", async () => { const window = domWith(marker); const first = async () => ({ default: () => createElement("span", null, "v1") }); const second = async () => ({ default: () => createElement("span", null, "v2") }); await act(async () => { await mountIslands(host(window), { loader: first }); }); expect(window.document.body.textContent).toContain("v1"); expect(islandRootCount()).toBe(1); await act(async () => { await remountIslands(host(window), { loader: second }); }); expect(window.document.body.textContent).toContain("v2"); expect(window.document.body.textContent).not.toContain("v1"); expect(islandRootCount()).toBe(1); }); test("repeated remounts stay at one root", async () => { const window = domWith(marker); const loader = async () => ({ default: () => createElement("span", null, "x") }); await act(async () => { await mountIslands(host(window), { loader }); }); for (let i = 0; i < 4; i += 1) { await act(async () => { await remountIslands(host(window), { loader }); }); } expect(islandRootCount()).toBe(1); }); test("remount re-renders in place instead of creating a second root", async () => { const window = domWith(marker); const loader = async () => ({ default: () => createElement("span", null, "v1") }); await act(async () => { await mountIslands(host(window), { loader }); }); expect(islandRootCount()).toBe(1); // Simulate HMR morphing server markup back over the mounted island: React's // rendered DOM is gone, so unmounting it would throw. const el = window.document.querySelector("[data-wrn-island]")!; el.innerHTML = ""; await act(async () => { await remountIslands(host(window), { loader }); }); expect(islandRootCount()).toBe(1); expect(window.document.body.textContent).toContain("v1"); });