diff --git a/src/components/ErrorBoundary.spec.tsx b/src/components/ErrorBoundary.spec.tsx index ece6bef0..60857bb5 100644 --- a/src/components/ErrorBoundary.spec.tsx +++ b/src/components/ErrorBoundary.spec.tsx @@ -10,7 +10,7 @@ const { testkit, sentryTransport } = sentryTestkit(); const ErrorComponent = () => { throw new Error('Test Error') }; describe('ErrorBoundary', () => { - beforeEach(() => { + beforeAll(() => { Sentry.init({ dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0', transport: sentryTransport, @@ -72,6 +72,93 @@ describe('ErrorBoundary', () => { spy.mockRestore(); }); + it('sets level appropriately', () => { + const spy = jest.spyOn(console, 'error'); + spy.mockImplementation(() => undefined); + + // Clear previous reports + testkit.reset(); + + const SessionExpiredComponent = () => { + throw new SessionExpiredError(); + }; + + // Should create warning (reports[0]) + renderer.create( + + + + ); + + // Should create error (reports[1]) + renderer.create( + + + + ); + + const reports = testkit.reports(); + expect(reports).toHaveLength(2); + expect(reports[0].level).toBe('warning'); + expect(reports[1].level).toBe('error'); + + spy.mockRestore(); + }); + + it('can override level in error fallbacks', () => { + const spy = jest.spyOn(console, 'error'); + spy.mockImplementation(() => undefined); + + // Clear previous reports + testkit.reset(); + + const SessionExpiredComponent = () => { + throw new SessionExpiredError(); + }; + + // Round 1: Override default 'warning' level with 'debug' + // Should create debug (reports[0]) + renderer.create( + + + + ); + + // Should create error (reports[1]) + renderer.create( + + + + ); + + const reports = testkit.reports(); + expect(reports).toHaveLength(2); + expect(reports[0].level).toBe('debug'); + expect(reports[1].level).toBe('error'); + + // Round 2: Ensure 'error' level is default + testkit.reset(); + + const unsetLevel = (undefined as unknown) as Sentry.SeverityLevel + + renderer.create( + + + + ); + + expect(testkit.reports()).toHaveLength(1); + expect(testkit.reports()[0].level).toBe('error'); + + spy.mockRestore(); + }); + it('can override fallback components for specific errors', () => { const spy = jest.spyOn(console, 'error') spy.mockImplementation(() => undefined); diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx index 3aacf99a..8b4e5c42 100644 --- a/src/components/ErrorBoundary.tsx +++ b/src/components/ErrorBoundary.tsx @@ -20,6 +20,10 @@ const defaultErrorFallbacks = { }; +const defaultErrorLevels: { [_: string]: Sentry.SeverityLevel } = { + 'SessionExpiredError': 'warning' +}; + export const ErrorBoundary = ({ children, renderFallback, @@ -32,9 +36,11 @@ export const ErrorBoundary = ({ sentryDsn?: string; sentryInit?: Sentry.BrowserOptions; errorFallbacks?: { [_: string]: JSX.Element } + errorLevels?: { [_: string]: Sentry.SeverityLevel } }) => { const [error, setError] = React.useState(null); const errorFallbacks: { [_: string]: JSX.Element } = { ...defaultErrorFallbacks, ...props.errorFallbacks }; + const errorLevels = { ...defaultErrorLevels, ...props.errorLevels }; const typedFallback = error?.type ? errorFallbacks[error.type] : undefined; const initCalled = React.useRef(false); @@ -76,6 +82,17 @@ export const ErrorBoundary = ({ eventId }); }} + beforeCapture={(scope, error) => { + // We need to set the level here, before `setError` is called in `onError` + // throw -> beforeCapture -> onError -> error captured -> setError -> etc. + if (error) { + const type = getTypeFromError(error); + const errorLevel = errorLevels[type]; + if (errorLevel) { + scope.setLevel(errorLevel); + } + } + }} {...props} onReset={() => setError(null)} >