Skip to content
Open
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
7 changes: 2 additions & 5 deletions packages/react/src/error.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { captureException, withScope } from '@sentry/browser';
import { captureException } from '@sentry/browser';
import { isError } from '@sentry/core/browser';
import type { ErrorInfo } from 'react';
import { version } from 'react';
Expand Down Expand Up @@ -64,10 +64,7 @@ export function captureReactException(
setCause(error, errorBoundaryError);
}

return withScope(scope => {
scope.setContext('react', { componentStack });
return captureException(error, hint);
});
return captureException(error, hint);
}

/**
Expand Down
25 changes: 6 additions & 19 deletions packages/react/test/errorboundary.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import type { ErrorBoundaryProps, FallbackRender } from '../src/errorboundary';
import { ErrorBoundary, UNKNOWN_COMPONENT, withErrorBoundary } from '../src/errorboundary';

const mockScope = new Scope();
const scopeSetContextSpy = vi.spyOn(mockScope, 'setContext');
const mockCaptureException = vi.fn();
const mockShowReportDialog = vi.fn();
const mockClientOn = vi.fn();
Expand Down Expand Up @@ -388,16 +387,13 @@ describe('ErrorBoundary', () => {
mechanism: { handled: true, type: 'auto.function.react.error_boundary' },
});

expect(scopeSetContextSpy).toHaveBeenCalledTimes(1);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leftover mockClear() call on non-spy method breaks tests

High Severity

The vi.spyOn(mockScope, 'setContext') call was removed, but the afterEach block still calls (mockScope.setContext as any).mockClear(). Since mockScope.setContext is no longer a spy, it's a plain Scope method without a .mockClear() property — this will throw a TypeError at runtime, causing every test inside describe('ErrorBoundary', ...) to fail.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 89d1a79. Configure here.

expect(scopeSetContextSpy).toHaveBeenCalledWith('react', { componentStack: expect.any(String) });

expect(mockOnError.mock.calls[0]?.[0]).toEqual(mockCaptureException.mock.calls[0]?.[0]);

// Check if error.cause -> react component stack
const error = mockCaptureException.mock.calls[0]?.[0];
const cause = error.cause;

expect(cause.stack).toEqual(scopeSetContextSpy.mock.calls[0]?.[1]?.componentStack);
expect(cause.stack).toEqual(expect.any(String));
expect(cause.name).toContain('React ErrorBoundary');
expect(cause.message).toEqual(error.message);
});
Expand Down Expand Up @@ -447,9 +443,6 @@ describe('ErrorBoundary', () => {
mechanism: { handled: true, type: 'auto.function.react.error_boundary' },
});

expect(scopeSetContextSpy).toHaveBeenCalledTimes(1);
expect(scopeSetContextSpy).toHaveBeenCalledWith('react', { componentStack: expect.any(String) });

// Check if error.cause -> react component stack
const error = mockCaptureException.mock.calls[0]?.[0];
expect(error.cause).not.toBeDefined();
Expand Down Expand Up @@ -486,16 +479,13 @@ describe('ErrorBoundary', () => {
mechanism: { handled: true, type: 'auto.function.react.error_boundary' },
});

expect(scopeSetContextSpy).toHaveBeenCalledTimes(1);
expect(scopeSetContextSpy).toHaveBeenCalledWith('react', { componentStack: expect.any(String) });

expect(mockOnError.mock.calls[0]?.[0]).toEqual(mockCaptureException.mock.calls[0]?.[0]);

const thirdError = mockCaptureException.mock.calls[0]?.[0];
const secondError = thirdError.cause;
const firstError = secondError.cause;
const cause = firstError.cause;
expect(cause.stack).toEqual(scopeSetContextSpy.mock.calls[0]?.[1]?.componentStack);
expect(cause.stack).toEqual(expect.any(String));
expect(cause.name).toContain('React ErrorBoundary');
expect(cause.message).toEqual(thirdError.message);
});
Expand Down Expand Up @@ -530,15 +520,14 @@ describe('ErrorBoundary', () => {
mechanism: { handled: true, type: 'auto.function.react.error_boundary' },
});

expect(scopeSetContextSpy).toHaveBeenCalledTimes(1);
expect(scopeSetContextSpy).toHaveBeenCalledWith('react', { componentStack: expect.any(String) });

expect(mockOnError.mock.calls[0]?.[0]).toEqual(mockCaptureException.mock.calls[0]?.[0]);

const error = mockCaptureException.mock.calls[0]?.[0];
const cause = error.cause;
// We need to make sure that recursive error.cause does not cause infinite loop
expect(cause.stack).not.toEqual(scopeSetContextSpy.mock.calls[0]?.[1]?.componentStack);
// We need to make sure that recursive error.cause does not cause infinite loop:
// when the cause chain loops, captureReactException bails out of setCause and
// leaves the original (non-ErrorBoundary) cause intact instead of overwriting
// it with `errorBoundaryError`.
expect(cause.name).not.toContain('React ErrorBoundary');
});

Expand Down Expand Up @@ -698,8 +687,6 @@ describe('ErrorBoundary', () => {
mechanism: { handled: expected, type: 'auto.function.react.error_boundary' },
});

expect(scopeSetContextSpy).toHaveBeenCalledTimes(1);
expect(scopeSetContextSpy).toHaveBeenCalledWith('react', { componentStack: expect.any(String) });
},
);
});
Expand Down