Skip to content

CORE-951 - Use beforeCapture to set level in Sentry scope #89

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 88 additions & 1 deletion src/components/ErrorBoundary.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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(
<ErrorBoundary renderFallback>
<SessionExpiredComponent />
</ErrorBoundary>
);

// Should create error (reports[1])
renderer.create(
<ErrorBoundary renderFallback>
<ErrorComponent />
</ErrorBoundary>
);

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(
<ErrorBoundary
renderFallback
errorLevels={{ SessionExpiredError: 'debug' }}
>
<SessionExpiredComponent />
</ErrorBoundary>
);

// Should create error (reports[1])
renderer.create(
<ErrorBoundary renderFallback>
<ErrorComponent />
</ErrorBoundary>
);

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(
<ErrorBoundary
renderFallback
errorLevels={{ SessionExpiredError: unsetLevel }}
>
<SessionExpiredComponent />
</ErrorBoundary>
);

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);
Expand Down
17 changes: 17 additions & 0 deletions src/components/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ const defaultErrorFallbacks = {
</Error>
};

const defaultErrorLevels: { [_: string]: Sentry.SeverityLevel } = {
'SessionExpiredError': 'warning'
};

export const ErrorBoundary = ({
children,
renderFallback,
Expand All @@ -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<SentryError | null>(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);

Expand Down Expand Up @@ -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)}
>
Expand Down
Loading