import { afterEach, describe, expect, it, rs } from "@rstest/core"; import { cleanup, fireEvent, render, screen } from "@testing-library/react"; const mocks = rs.hoisted(() => ({ pathname: "/workspace/chats/a", user: { id: "account-a" } as { id: string } | null, })); rs.mock("next/navigation", () => ({ usePathname: () => mocks.pathname })); rs.mock("@/components/ui/sidebar", () => ({ useSidebar: () => ({ setOpen: rs.fn() }), })); rs.mock("@/core/auth/AuthProvider", () => ({ useAuth: () => ({ user: mocks.user }), })); rs.mock("@/env", () => ({ env: { NEXT_PUBLIC_STATIC_WEBSITE_ONLY: "false" }, })); import { ArtifactsProvider, useArtifacts, } from "@/components/workspace/artifacts/context"; import { ArtifactDraftsProvider, useArtifactDraftOwner, } from "@/core/artifacts/draft-context"; const filepath = "/mnt/user-data/outputs/report.txt"; function Probe() { const { drafts, setDrafts } = useArtifacts(); const draft = drafts[filepath]; return ( <> {draft?.draftContent ?? "no draft"} ); } function AccountWorkspace({ show = true, routeKey = "stable", }: { show?: boolean; routeKey?: string; }) { useArtifactDraftOwner(mocks.user?.id ?? null); return ( <> {show ? ( ) : null} ); } function Workspace(props: { show?: boolean; workspace?: boolean; routeKey?: string; }) { return ( {props.workspace === false ? (

Home

) : ( )}
); } afterEach(() => { cleanup(); window.sessionStorage.clear(); mocks.pathname = "/workspace/chats/a"; mocks.user = { id: "account-a" }; }); describe("artifact draft navigation", () => { it("keeps drafts and the unload warning across the workspace-to-home exit", () => { const view = render(); fireEvent.click(screen.getByText("Edit")); mocks.pathname = "/"; view.rerender(); const event = new Event("beforeunload", { cancelable: true }); window.dispatchEvent(event); expect(event.defaultPrevented).toBe(true); mocks.pathname = "/workspace/chats/a"; view.rerender(); expect(screen.getByTestId("draft").textContent).toBe("unsaved edit"); }); it("keeps separate drafts through A-B-A navigation", () => { const view = render(); fireEvent.click(screen.getByText("Edit")); mocks.pathname = "/workspace/chats/b"; view.rerender(); expect(screen.getByTestId("draft").textContent).toBe("no draft"); fireEvent.click(screen.getByText("Edit")); expect(screen.getByTestId("draft").textContent).toBe("B edit"); mocks.pathname = "/workspace/chats/a"; view.rerender(); expect(screen.getByTestId("draft").textContent).toBe("unsaved edit"); mocks.pathname = "/workspace/chats/b"; view.rerender(); expect(screen.getByTestId("draft").textContent).toBe("B edit"); }); it("retains a draft when the route-local provider unmounts", () => { const view = render(); fireEvent.click(screen.getByText("Edit")); view.rerender(); view.rerender(); expect(screen.getByTestId("draft").textContent).toBe("unsaved edit"); }); it("does not show the previous account's draft on the same route", () => { const view = render(); fireEvent.click(screen.getByText("Edit")); mocks.user = { id: "account-b" }; view.rerender(); expect(screen.getByTestId("draft").textContent).toBe("no draft"); mocks.user = { id: "account-a" }; view.rerender(); expect(screen.getByTestId("draft").textContent).toBe("no draft"); }); it("clears account drafts on logout and removes their unload warning", () => { const view = render(); fireEvent.click(screen.getByText("Edit")); mocks.user = null; view.rerender(); expect(screen.getByTestId("draft").textContent).toBe("no draft"); const event = new Event("beforeunload", { cancelable: true }); window.dispatchEvent(event); expect(event.defaultPrevented).toBe(false); mocks.user = { id: "account-a" }; view.rerender(); expect(screen.getByTestId("draft").textContent).toBe("no draft"); }); it("warns about dirty drafts when no artifact page is mounted", () => { const view = render(); fireEvent.click(screen.getByText("Edit")); view.rerender(); const event = new Event("beforeunload", { cancelable: true }); window.dispatchEvent(event); expect(event.defaultPrevented).toBe(true); }); it("does not keep file contents in browser storage or across root unmount", () => { const view = render(); fireEvent.click(screen.getByText("Edit")); const stored = Object.values(window.sessionStorage).join(""); expect(stored).not.toContain("unsaved edit"); view.unmount(); render(); expect(screen.getByTestId("draft").textContent).toBe("no draft"); }); it("clears the unload warning after Save and does not revive discarded drafts", () => { const view = render(); fireEvent.click(screen.getByText("Edit")); const dirty = new Event("beforeunload", { cancelable: true }); window.dispatchEvent(dirty); expect(dirty.defaultPrevented).toBe(true); fireEvent.click(screen.getByText("Save")); const saved = new Event("beforeunload", { cancelable: true }); window.dispatchEvent(saved); expect(saved.defaultPrevented).toBe(false); fireEvent.click(screen.getByText("Discard")); view.rerender(); view.rerender(); expect(screen.getByTestId("draft").textContent).toBe("no draft"); }); });