/** * Tests for ErrorBoundary component */ import React from 'react'; import { render, fireEvent } from '@testing-library/react-native'; import { Text, View } from 'react-native'; import { ErrorBoundary, withErrorBoundary } from '../ErrorBoundary'; // Component that throws an error function ThrowingComponent({ shouldThrow = true }: { shouldThrow?: boolean }) { if (shouldThrow) { throw new Error('Test error'); } return Content rendered; } // Suppress console errors during tests const originalConsoleError = console.error; beforeAll(() => { console.error = jest.fn(); }); afterAll(() => { console.error = originalConsoleError; }); describe('ErrorBoundary', () => { beforeEach(() => { jest.clearAllMocks(); }); it('should render children when no error occurs', () => { const { getByText } = render( Hello World ); expect(getByText('Hello World')).toBeTruthy(); }); it('should render fallback UI when error occurs', () => { const { getByText } = render( ); expect(getByText('Oops! Something went wrong')).toBeTruthy(); expect(getByText(/The app encountered an unexpected error/)).toBeTruthy(); }); it('should render custom fallback when provided', () => { const { getByText } = render( Custom Error UI}> ); expect(getByText('Custom Error UI')).toBeTruthy(); }); it('should call onError callback when error occurs', () => { const onError = jest.fn(); render( ); expect(onError).toHaveBeenCalledTimes(1); expect(onError).toHaveBeenCalledWith( expect.any(Error), expect.objectContaining({ componentStack: expect.any(String), }) ); }); it('should provide retry functionality', () => { const onRetry = jest.fn(); const { getByText } = render( ); const tryAgainButton = getByText('Try Again'); fireEvent.press(tryAgainButton); expect(onRetry).toHaveBeenCalledTimes(1); }); it('should reset error state when retry is pressed', () => { let shouldThrow = true; const TestComponent = () => { if (shouldThrow) { throw new Error('Test'); } return Recovered; }; const { getByText, queryByText, rerender } = render( { shouldThrow = false; }}> ); // Error is shown expect(getByText('Oops! Something went wrong')).toBeTruthy(); // Press retry fireEvent.press(getByText('Try Again')); // Re-render to see if it recovers rerender( ); expect(getByText('Recovered')).toBeTruthy(); expect(queryByText('Oops! Something went wrong')).toBeNull(); }); it('should reset error state when resetKey changes', () => { const { rerender, getByText, queryByText } = render( ); expect(getByText('Oops! Something went wrong')).toBeTruthy(); // Change resetKey and provide non-throwing child rerender( ); expect(getByText('Content rendered')).toBeTruthy(); expect(queryByText('Oops! Something went wrong')).toBeNull(); }); it('should show error details in development mode', () => { const { getByText, queryByText } = render( ); // Details toggle should be present expect(getByText(/Error Details/)).toBeTruthy(); // Click to expand fireEvent.press(getByText(/Error Details/)); // Error message should be visible expect(getByText('Test error')).toBeTruthy(); }); it('should hide error details when showDetails is false', () => { const { queryByText } = render( ); expect(queryByText(/Error Details/)).toBeNull(); }); }); describe('withErrorBoundary HOC', () => { it('should wrap component with error boundary', () => { const MyComponent = () => My Component; const WrappedComponent = withErrorBoundary(MyComponent); const { getByText } = render(); expect(getByText('My Component')).toBeTruthy(); }); it('should catch errors from wrapped component', () => { const onError = jest.fn(); const WrappedComponent = withErrorBoundary(ThrowingComponent, { onError }); const { getByText } = render(); expect(getByText('Oops! Something went wrong')).toBeTruthy(); expect(onError).toHaveBeenCalled(); }); it('should pass props to wrapped component', () => { interface Props { message: string; } const MyComponent = ({ message }: Props) => {message}; const WrappedComponent = withErrorBoundary(MyComponent); const { getByText } = render(); expect(getByText('Hello Props')).toBeTruthy(); }); it('should set correct displayName', () => { const MyComponent = () => Test; MyComponent.displayName = 'MyComponent'; const WrappedComponent = withErrorBoundary(MyComponent); expect(WrappedComponent.displayName).toBe('withErrorBoundary(MyComponent)'); }); });