// @vitest-environment jsdom import { render, screen } from "@testing-library/react"; import type { ReactNode } from "react"; import userEvent from "@testing-library/user-event"; import { describe, expect, it, vi } from "vitest"; import { createFailure } from "../../src/contracts/errors.ts"; import { deriveAsyncState } from "../../src/application/view-models/async-state.ts"; import { AsyncSurface } from "../../src/presentation/components/async-surface.tsx"; import { BootErrorShell } from "../../src/presentation/boundaries/boot-error-shell.tsx"; import { FeatureBoundary } from "../../src/presentation/boundaries/render-error-boundary.tsx"; function Defect(): ReactNode { throw new Error("raw render stack"); } describe("render recovery boundaries", () => { it("catches programmer defects and emits best-effort safe telemetry", () => { const onRenderFailure = vi.fn(); render( , ); expect(screen.getByRole("alert")).toHaveTextContent( "화면을 표시하지 못했습니다.", ); expect(onRenderFailure).toHaveBeenCalledWith({ routeId: "APP_HOME", buildId: "build-a", boundaryName: "feature", }); }); it("keeps normalized operational failures in normal async state", () => { const state = deriveAsyncState({ failure: createFailure("SERVER_FAILURE", "LIST", 0), }); render( , ); expect(screen.getByRole("alert")).toHaveTextContent( "요청을 완료하지 못했습니다.", ); expect(screen.getByRole("alert")).toHaveAttribute( "data-message-key", "error.server_failure", ); }); it("renders a safe boot shell with no endpoint or stack", () => { render( , ); const shell = screen.getByRole("alert"); expect(shell).toHaveTextContent("build-a:CONFIG_SCHEMA_INVALID"); expect(shell).not.toHaveTextContent("https://"); expect(shell).not.toHaveTextContent("stack"); }); it("allows a boundary reset action without reloading the page", async () => { let shouldThrow = true; function Recoverable() { if (shouldThrow) throw new Error("defect"); return

recovered

; } const user = userEvent.setup(); const view = render( , ); shouldThrow = false; await user.click(screen.getByRole("button", { name: "다시 시도" })); view.rerender( , ); expect(screen.getByText("recovered")).toBeVisible(); }); it("resets a route failure when the registered location key changes", async () => { let shouldThrow = true; function RouteContent() { if (shouldThrow) throw new Error("route defect"); return

next route

; } const view = render( , ); expect(screen.getByRole("alert")).toBeVisible(); shouldThrow = false; view.rerender( , ); expect(await screen.findByText("next route")).toBeVisible(); }); });