Implement approved specification #32 and tickets #33-#37. Keep host authentication server-verified and pin session interaction language. Compile scoped base selectors for browser compatibility and retain full gutters during CSS pruning.
112 lines
3.8 KiB
TypeScript
112 lines
3.8 KiB
TypeScript
import { act, render, screen } from "@testing-library/react";
|
|
import { setLocale } from "../i18n";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { useState } from "react";
|
|
import { ErrorBoundary } from "./ErrorBoundary";
|
|
|
|
/** A child that throws on demand. Toggle `boom` to control whether it crashes. */
|
|
function Boom({ boom, label = "kaboom" }: { boom: boolean; label?: string }) {
|
|
if (boom) throw new Error(label);
|
|
return <div>child ok</div>;
|
|
}
|
|
|
|
// React logs caught render errors to console.error; silence it so the suite output
|
|
// stays readable while still exercising the real error path.
|
|
let errorSpy: ReturnType<typeof vi.spyOn>;
|
|
beforeEach(() => {
|
|
errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
});
|
|
afterEach(() => {
|
|
act(() => setLocale("en"));
|
|
errorSpy.mockRestore();
|
|
});
|
|
|
|
test("updates an already displayed error boundary when the locale changes", () => {
|
|
render(<ErrorBoundary label="document"><Boom boom label="SELECT * malformed" /></ErrorBoundary>);
|
|
act(() => setLocale("it"));
|
|
expect(screen.getByRole("alert")).toHaveTextContent("Impossibile visualizzare questo elemento (documento).");
|
|
expect(screen.getByRole("button", { name: "Riprova" })).toBeVisible();
|
|
expect(screen.getByText("SELECT * malformed")).toBeInTheDocument();
|
|
});
|
|
|
|
test("renders children when nothing throws", () => {
|
|
render(
|
|
<ErrorBoundary>
|
|
<Boom boom={false} />
|
|
</ErrorBoundary>,
|
|
);
|
|
expect(screen.getByText("child ok")).toBeInTheDocument();
|
|
});
|
|
|
|
test("renders the default fallback when a child throws", () => {
|
|
render(
|
|
<ErrorBoundary label="step">
|
|
<Boom boom />
|
|
</ErrorBoundary>,
|
|
);
|
|
expect(screen.queryByText("child ok")).not.toBeInTheDocument();
|
|
// Default fallback names the labelled surface.
|
|
expect(screen.getByText(/couldn't be displayed/i)).toBeInTheDocument();
|
|
expect(screen.getByText(/step/i)).toBeInTheDocument();
|
|
});
|
|
|
|
test("uses a custom fallback render prop with the error", () => {
|
|
render(
|
|
<ErrorBoundary fallback={(err) => <p>custom: {err.message}</p>}>
|
|
<Boom boom label="explode" />
|
|
</ErrorBoundary>,
|
|
);
|
|
expect(screen.getByText("custom: explode")).toBeInTheDocument();
|
|
});
|
|
|
|
test("calls onError when a child throws", () => {
|
|
const onError = vi.fn();
|
|
render(
|
|
<ErrorBoundary onError={onError}>
|
|
<Boom boom label="reported" />
|
|
</ErrorBoundary>,
|
|
);
|
|
expect(onError).toHaveBeenCalledTimes(1);
|
|
expect(onError.mock.calls[0][0]).toBeInstanceOf(Error);
|
|
expect((onError.mock.calls[0][0] as Error).message).toBe("reported");
|
|
});
|
|
|
|
test("recovers when resetKeys change to non-crashing content", async () => {
|
|
function Harness() {
|
|
const [boom, setBoom] = useState(true);
|
|
return (
|
|
<>
|
|
<button onClick={() => setBoom(false)}>fix</button>
|
|
<ErrorBoundary resetKeys={[boom]}>
|
|
<Boom boom={boom} />
|
|
</ErrorBoundary>
|
|
</>
|
|
);
|
|
}
|
|
render(<Harness />);
|
|
// Starts crashed → fallback visible.
|
|
expect(screen.getByText(/couldn't be displayed/i)).toBeInTheDocument();
|
|
// Flip the data (and thus resetKeys); the boundary clears and re-renders children.
|
|
await userEvent.click(screen.getByRole("button", { name: "fix" }));
|
|
expect(screen.getByText("child ok")).toBeInTheDocument();
|
|
expect(screen.queryByText(/couldn't be displayed/i)).not.toBeInTheDocument();
|
|
});
|
|
|
|
test("Try again resets the boundary", async () => {
|
|
let boom = true;
|
|
function Flaky() {
|
|
if (boom) throw new Error("transient");
|
|
return <div>recovered</div>;
|
|
}
|
|
render(
|
|
<ErrorBoundary>
|
|
<Flaky />
|
|
</ErrorBoundary>,
|
|
);
|
|
expect(screen.getByText(/couldn't be displayed/i)).toBeInTheDocument();
|
|
// Simulate the underlying condition clearing, then retry.
|
|
boom = false;
|
|
await userEvent.click(screen.getByRole("button", { name: /try again/i }));
|
|
expect(screen.getByText("recovered")).toBeInTheDocument();
|
|
});
|