Skip to content
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
5 changes: 5 additions & 0 deletions .changeset/happy-days-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@commercetools-frontend/sentry': patch
---

Update @sentry/react to v8 for React 19 compatibility. Migrate deprecated `configureScope` API to `getCurrentScope()` and update type signatures to satisfy v8 requirements.
6 changes: 3 additions & 3 deletions packages/sentry/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
"@babel/runtime-corejs3": "^7.22.15",
"@commercetools-frontend/browser-history": "24.13.0",
"@commercetools-frontend/constants": "24.13.0",
"@sentry/browser": "7.120.4",
"@sentry/react": "7.120.4",
"@sentry/types": "7.120.4",
"@sentry/browser": "8.55.0",
"@sentry/react": "8.55.0",
"@sentry/types": "8.55.0",
"@types/prop-types": "^15.7.5",
"@types/react": "^19.0.3",
"prop-types": "15.8.1"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import * as Sentry from '@sentry/react';
import { render } from '@testing-library/react';
import type { ApplicationWindow } from '@commercetools-frontend/constants';

import SentryUserLogoutTracker from './sentry-user-logout-tracker';

declare let window: ApplicationWindow;

jest.mock('@sentry/react');

describe('SentryUserLogoutTracker', () => {
beforeEach(() => {
// Reset mocks and window state before each test
jest.clearAllMocks();
delete window.app?.trackingSentry;
// Mock getCurrentScope to return an object with a clear method
(Sentry.getCurrentScope as jest.Mock) = jest.fn(() => ({
clear: jest.fn(),
}));
});

describe('when Sentry tracking is enabled', () => {
it('should clear the Sentry scope on mount', () => {
window.app = { trackingSentry: 'enabled' } as ApplicationWindow['app'];
const clearMock = jest.fn();
(Sentry.getCurrentScope as jest.Mock).mockReturnValue({
clear: clearMock,
});

render(<SentryUserLogoutTracker />);

expect(Sentry.getCurrentScope).toHaveBeenCalled();
expect(clearMock).toHaveBeenCalled();
});
});

describe('when Sentry tracking is not enabled', () => {
it('should do nothing when trackingSentry is not set', () => {
window.app = {} as ApplicationWindow['app'];
const clearMock = jest.fn();
(Sentry.getCurrentScope as jest.Mock).mockReturnValue({
clear: clearMock,
});

render(<SentryUserLogoutTracker />);

expect(Sentry.getCurrentScope).not.toHaveBeenCalled();
expect(clearMock).not.toHaveBeenCalled();
});

it('should not clear the Sentry scope when trackingSentry is undefined', () => {
window.app = { trackingSentry: undefined } as ApplicationWindow['app'];
const clearMock = jest.fn();
(Sentry.getCurrentScope as jest.Mock).mockReturnValue({
clear: clearMock,
});

render(<SentryUserLogoutTracker />);

expect(Sentry.getCurrentScope).not.toHaveBeenCalled();
expect(clearMock).not.toHaveBeenCalled();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ class SentryUserLogoutTracker extends Component {
// When the user is not logged in anymore (e.g. on logout) we still track
// errors but without the user data in context.
if (window.app.trackingSentry) {
Sentry.configureScope((scope) => {
scope.clear();
});
Sentry.getCurrentScope().clear();
}
}
render() {
Expand Down
15 changes: 11 additions & 4 deletions packages/sentry/src/sentry.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import * as Sentry from '@sentry/react';
// eslint-disable-next-line import/order
import { globalHandlersIntegration } from '@sentry/browser'; // Must import after `@sentry/react`
import type { Extra, Extras, Event } from '@sentry/types';
import type {
Extra,
Extras,
Event,
ErrorEvent as SentryErrorEvent,
} from '@sentry/types';
import history from '@commercetools-frontend/browser-history';
import type { ApplicationWindow } from '@commercetools-frontend/constants';

Expand Down Expand Up @@ -103,8 +108,10 @@ export const boot = () => {
// we can implement the `tracesSampler` function.
// https://docs.sentry.io/platforms/javascript/guides/react/configuration/sampling/#sampling-transaction-events
tracesSampleRate: 0.05,
beforeSend(event) {
return redactUnsafeEventFields(event);
// v8 requires the hint parameter which provides additional error context.
// We only redact PII from event fields, so hint is unused for now.
beforeSend(event, _hint) {
return redactUnsafeEventFields(event) as SentryErrorEvent;
},
});
const sentryScope = Sentry.getCurrentScope();
Expand Down Expand Up @@ -160,7 +167,7 @@ export const reportErrorToSentry = (
// The error stack should be available in Sentry, so there is no
// need to print it in the console as well.
// We just notify that an error occurred and provide the error ID.
console.error(`[SENTRY]: An error occured (ID: ${errorId}).`);
console.error(`[SENTRY]: An error occurred (ID: ${errorId}).`);
return errorId;
}

Expand Down
116 changes: 64 additions & 52 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.