import { NetworkError } from '@data-client/rest'; import { act, render, waitFor } from '@testing-library/react'; import { ReactElement } from 'react'; import ErrorBoundary from '../ErrorBoundary'; describe('', () => { let errorSpy: jest.SpyInstance; function onError(e: any) { e.preventDefault(); } beforeEach(() => { errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); if (typeof addEventListener === 'function') addEventListener('error', onError); }); afterEach(() => { errorSpy.mockRestore(); if (typeof removeEventListener === 'function') removeEventListener('error', onError); }); it('should render children with no error', () => { const tree = hi; const { getByText } = render(tree); expect(getByText(/hi/i)).not.toBeNull(); }); it('should catch non-network errors', () => { let renderCount = 0; function Throw(): ReactElement { renderCount++; throw new Error('you failed'); } const tree = (
hi
); const { getByText } = render(tree); expect(getByText(/you failed/i)).toBeDefined(); expect(renderCount).toBeLessThan(10); }); it('should render error case when thrown', () => { function Throw(): ReactElement { throw new NetworkError( new Response('you failed', { statusText: '', status: 500, }), ); } const tree = (
hi
); const { getByText, queryByText, container } = render(tree); expect(getByText(/500/i)).toBeDefined(); expect(queryByText(/hi/i)).toBe(null); expect( (container.firstChild as any)?.classList?.contains?.('error'), ).toBeTruthy(); }); it('should render response.statusText using default fallback', () => { function Throw(): ReactElement { const response = new Response('', { statusText: 'my status text', status: 500, }); throw new NetworkError(response); } const tree = (
hi
); const { getByText, queryByText } = render(tree); expect(getByText(/my status text/i)).toBeDefined(); expect(queryByText(/hi/i)).toBe(null); }); it('should reset error when listener triggers', async () => { let shouldThrow = true; function Throw(): ReactElement { const response = new Response('', { statusText: 'my status text', status: 500, }); if (shouldThrow) throw new NetworkError(response); return <>; } let listener = () => {}; const listen = (l: () => void) => { listener = l; return () => { listener = () => {}; }; }; const tree = (
hi
); const { getByText, queryByText } = render(tree); expect(getByText(/my status text/i)).toBeDefined(); shouldThrow = false; act(() => listener()); expect(queryByText(/my status text/i)).toBe(null); expect(getByText(/hi/i)).toBeDefined(); }); it('should ignore listener when unregistered', async () => { let shouldThrow = true; function Throw(): ReactElement { const response = new Response('', { statusText: 'my status text', status: 500, }); if (shouldThrow) throw new NetworkError(response); return <>; } let listener = () => {}; const listen = (l: () => void) => { listener = l; return () => { listener = () => {}; }; }; const tree = (
hi
); const { getByText, queryByText, rerender } = render(tree); expect(getByText(/my status text/i)).toBeDefined(); rerender(
hi
, ); shouldThrow = false; act(() => listener()); expect(getByText(/my status text/i)).toBeDefined(); // validate re-register shouldThrow = true; rerender(
hi
, ); expect(getByText(/my status text/i)).toBeDefined(); shouldThrow = false; act(() => listener()); expect(queryByText(/my status text/i)).toBe(null); expect(getByText(/hi/i)).toBeDefined(); }); it('should reset error when handler is called from fallback component', async () => { let shouldThrow = true; function Throw(): ReactElement { const response = new Response('', { statusText: 'my status text', status: 500, }); if (shouldThrow) throw new NetworkError(response); return <>; } const Fallback = ({ error, resetErrorBoundary, className, }: { error: Error; resetErrorBoundary: () => void; className?: string; }) => (
        {error.message}{' '}
        
      
); const tree = (
hi
); const { getByText, queryByText } = render(tree); expect(getByText(/my status text/i)).toBeDefined(); const resetButton = queryByText('Clear Error'); expect(resetButton).not.toBeNull(); shouldThrow = false; act(() => resetButton?.click()); expect(queryByText(/my status text/i)).toBe(null); expect(getByText(/hi/i)).toBeDefined(); }); });