import { render, screen } from '@testing-library/react' import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest' import { ErrorBoundary } from '@/components/ErrorBoundary' import React from 'react' // Component that throws an error for testing const ThrowError: React.FC<{ shouldThrow: boolean }> = ({ shouldThrow }) => { if (shouldThrow) { throw new Error('Test error message') } return
No error
} // Mock console.error to suppress error output in tests const originalError = console.error beforeEach(() => { console.error = vi.fn() }) afterEach(() => { console.error = originalError }) describe('ErrorBoundary Component', () => { test('renders children when there is no error', () => { render(
Test content
) expect(screen.getByText('Test content')).toBeInTheDocument() }) test('catches errors and displays fallback UI', () => { render( ) // Should show error fallback expect(screen.getByText(/Something went wrong/i)).toBeInTheDocument() expect(screen.queryByText('No error')).not.toBeInTheDocument() }) test('displays custom error fallback when provided', () => { const CustomFallback = ({ error }: { error: Error }) => (
Custom error: {error.message}
) render( ) expect(screen.getByText('Custom error: Test error message')).toBeInTheDocument() }) test('renders different UI for page-level errors', () => { render( ) // Page-level errors should have specific styling const errorContainer = screen.getByText(/Something went wrong/i).closest('div') expect(errorContainer?.className).toContain('min-h-screen') }) test('renders different UI for component-level errors', () => { render( ) // Component-level errors should have different styling const errorContainer = screen.getByText(/Something went wrong/i).closest('div') expect(errorContainer?.className).not.toContain('min-h-screen') expect(errorContainer?.className).toContain('rounded-lg') }) test('passes error object to error fallback', () => { const error = new Error('Specific error message') const CustomFallback = ({ error: err }: { error: Error }) => (
Error occurred
{err.message}
) render( ) expect(screen.getByText('Error occurred')).toBeInTheDocument() expect(screen.getByText('Test error message')).toBeInTheDocument() }) test('handles multiple error boundaries at different levels', () => { const OuterFallback = () =>
Outer error
const InnerFallback = () =>
Inner error
render(
) // Inner boundary should catch the error expect(screen.getByText('Inner error')).toBeInTheDocument() expect(screen.queryByText('Outer error')).not.toBeInTheDocument() }) test('recovers when error condition is resolved', () => { const { rerender } = render( ) // Error is shown expect(screen.getByText(/Something went wrong/i)).toBeInTheDocument() // When component no longer throws, it should recover rerender( ) // Note: React Error Boundaries don't automatically recover, // so the error state persists. This is expected behavior. expect(screen.getByText(/Something went wrong/i)).toBeInTheDocument() }) test('logs errors to console in development', () => { const consoleErrorSpy = vi.spyOn(console, 'error') render( ) // Error should be logged expect(consoleErrorSpy).toHaveBeenCalled() }) test('renders with suspense wrapper when specified', () => { // Testing SuspenseErrorBoundary variant const LazyComponent = React.lazy(() => Promise.resolve({ default: () =>
Lazy loaded
}) ) render( Loading...}> ) // Should show loading initially expect(screen.getByText('Loading...')).toBeInTheDocument() }) })