88 lines
2.9 KiB
React
88 lines
2.9 KiB
React
// @vitest-environment jsdom
|
|
|
|
import { render, screen } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
|
|
import { createFailure } from "../../src/contracts/errors.js";
|
|
import { deriveAsyncState } from "../../src/application/view-models/async-state.js";
|
|
import { AsyncSurface } from "../../src/presentation/components/async-surface.jsx";
|
|
import { BootErrorShell } from "../../src/presentation/boundaries/boot-error-shell.jsx";
|
|
import { FeatureBoundary } from "../../src/presentation/boundaries/render-error-boundary.jsx";
|
|
|
|
function Defect() {
|
|
throw new Error("raw render stack");
|
|
}
|
|
|
|
describe("render recovery boundaries", () => {
|
|
it("catches programmer defects and emits best-effort safe telemetry", () => {
|
|
const telemetry = { emit: vi.fn() };
|
|
render(
|
|
<FeatureBoundary
|
|
routeId="APP_HOME"
|
|
buildId="build-a"
|
|
telemetry={telemetry}
|
|
>
|
|
<Defect />
|
|
</FeatureBoundary>,
|
|
);
|
|
|
|
expect(screen.getByRole("alert")).toHaveTextContent("error.render_failure");
|
|
expect(telemetry.emit).toHaveBeenCalledWith("ui.render.failed", {
|
|
route_id: "APP_HOME",
|
|
build_id: "build-a",
|
|
component_boundary: "feature",
|
|
});
|
|
});
|
|
|
|
it("keeps normalized operational failures in normal async state", () => {
|
|
const state = deriveAsyncState({
|
|
failure: createFailure("SERVER_FAILURE", "LIST", 0),
|
|
});
|
|
render(
|
|
<FeatureBoundary routeId="APP_HOME" buildId="build-a">
|
|
<AsyncSurface state={state} />
|
|
</FeatureBoundary>,
|
|
);
|
|
expect(screen.getByRole("alert")).toHaveTextContent("error.server_failure");
|
|
});
|
|
|
|
it("renders a safe boot shell with no endpoint or stack", () => {
|
|
render(
|
|
<BootErrorShell
|
|
kind="BOOT_CONFIG_FAILURE"
|
|
code="CONFIG_SCHEMA_INVALID"
|
|
buildId="build-a"
|
|
configSchemaVersion="1"
|
|
supportReference="build-a:CONFIG_SCHEMA_INVALID"
|
|
/>,
|
|
);
|
|
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 <p>recovered</p>;
|
|
}
|
|
const user = userEvent.setup();
|
|
const view = render(
|
|
<FeatureBoundary routeId="APP_HOME" buildId="build-a">
|
|
<Recoverable />
|
|
</FeatureBoundary>,
|
|
);
|
|
shouldThrow = false;
|
|
await user.click(screen.getByRole("button", { name: "retry" }));
|
|
view.rerender(
|
|
<FeatureBoundary routeId="APP_HOME" buildId="build-a">
|
|
<Recoverable />
|
|
</FeatureBoundary>,
|
|
);
|
|
expect(screen.getByText("recovered")).toBeVisible();
|
|
});
|
|
});
|