import { fireEvent, render, screen } from "@testing-library/react"
import { describe, expect, it, vi } from "vitest"
import React from "react"
// Error Boundary component for testing
class ApiErrorBoundary extends React.Component<
{ children: React.ReactNode; fallback?: React.ReactNode },
{ hasError: boolean; error: Error | null }
> {
constructor(props: { children: React.ReactNode; fallback?: React.ReactNode }) {
super(props)
this.state = { hasError: false, error: null }
}
static getDerivedStateFromError(error: Error) {
return { hasError: true, error }
}
handleRetry = () => {
this.setState({ hasError: false, error: null })
}
render() {
if (this.state.hasError) {
return (
Something went wrong
{this.state.error?.message}
)
}
return this.props.children
}
}
// Test component that throws
function ThrowingComponent({ shouldThrow }: { shouldThrow: boolean }) {
if (shouldThrow) {
throw new Error("Test error message")
}
return Child content
}
describe("ApiErrorBoundary", () => {
// Suppress React error boundary console.error in tests
const originalConsoleError = console.error
beforeAll(() => {
console.error = (...args: unknown[]) => {
if (
typeof args[0] === "string" &&
args[0].includes("React will try to recreate")
) {
return
}
originalConsoleError(...args)
}
})
afterAll(() => {
console.error = originalConsoleError
})
it("renders children normally when no error", () => {
render(
)
expect(screen.getByTestId("child-content")).toBeInTheDocument()
expect(screen.getByText("Child content")).toBeInTheDocument()
})
it("shows error UI when child throws", () => {
render(
)
expect(screen.getByTestId("error-boundary")).toBeInTheDocument()
expect(screen.getByText("Something went wrong")).toBeInTheDocument()
expect(screen.getByText("Test error message")).toBeInTheDocument()
})
it("retry button resets error state", () => {
// Use a mutable ref to control throwing
let shouldThrow = true
function ConditionalThrower() {
if (shouldThrow) throw new Error("Temp error")
return Recovered!
}
render(
)
// Error state shown
expect(screen.getByTestId("error-boundary")).toBeInTheDocument()
// Fix the condition
shouldThrow = false
// Click retry
fireEvent.click(screen.getByRole("button", { name: /retry/i }))
// Should now render children again
expect(screen.getByTestId("recovered")).toBeInTheDocument()
})
})