/**
* 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)');
});
});