From 51362746ff2ec7e4b54004dcd64eb45c42a1c940 Mon Sep 17 00:00:00 2001 From: lucas Date: Tue, 22 Apr 2025 22:29:39 -0300 Subject: [PATCH 1/7] add fix for console loop --- .../src/js/integrations/debugsymbolicator.ts | 8 +++ .../integrations/debugsymbolicator.test.ts | 58 +++++++++++++++++++ samples/react-native/src/App.tsx | 2 + .../react-native/src/Screens/ErrorsScreen.tsx | 20 +++++++ 4 files changed, 88 insertions(+) diff --git a/packages/core/src/js/integrations/debugsymbolicator.ts b/packages/core/src/js/integrations/debugsymbolicator.ts index 8529d0eeb6..1d42bd7d7a 100644 --- a/packages/core/src/js/integrations/debugsymbolicator.ts +++ b/packages/core/src/js/integrations/debugsymbolicator.ts @@ -33,6 +33,14 @@ export const debugSymbolicatorIntegration = (): Integration => { }; async function processEvent(event: Event, hint: EventHint): Promise { + // event created by consoleIntegration, symbolicator can trigger those events. + // so we drop the event to avoid an infinite loop. + if ( + event.extra?.['arguments'] && + event.message?.startsWith("Assertion failed: 'this' is expected an Event object, but got") + ) { + return event; + } if (event.exception?.values && isErrorLike(hint.originalException)) { // originalException is ErrorLike object const errorGroup = getExceptionGroup(hint.originalException); diff --git a/packages/core/test/integrations/debugsymbolicator.test.ts b/packages/core/test/integrations/debugsymbolicator.test.ts index 9ed5728319..e810f9fa4b 100644 --- a/packages/core/test/integrations/debugsymbolicator.test.ts +++ b/packages/core/test/integrations/debugsymbolicator.test.ts @@ -9,6 +9,7 @@ import { parseErrorStack, symbolicateStackTrace, } from '../../src/js/integrations/debugsymbolicatorutils'; +import * as ErrorUtils from '../../src/js/utils/error'; import type * as ReactNative from '../../src/js/vendor/react-native'; async function processEvent(mockedEvent: Event, mockedHint: EventHint): Promise { @@ -365,6 +366,63 @@ describe('Debug Symbolicator Integration', () => { }); }); + it('skips metro events created by Sentry console integration', async () => { + const spy = jest.spyOn(ErrorUtils, 'isErrorLike').mockImplementation(() => { + throw new Error('only first if condition should be called'); + }); + + const symbolicatedEvent = await processEvent( + { + exception: { + values: [], + }, + extra: { + arguments: [false, "'this' is expected an Event object, but got", ''], + }, + message: "Assertion failed: 'this' is expected an Event object, but got", + }, + {}, + ); + + expect(symbolicatedEvent).toStrictEqual({ + exception: { + values: [], + }, + extra: { + arguments: [false, "'this' is expected an Event object, but got", ''], + }, + message: "Assertion failed: 'this' is expected an Event object, but got", + }); + + // This is the second if condition after the tested element, it is here to make sure the returned code + // came from the console filter. + expect(spy).not.toHaveBeenCalled(); + spy.mockRestore(); + }); + + it('do not skip events similar to metro events created by Sentry console integration', async () => { + const spy = jest.spyOn(ErrorUtils, 'isErrorLike').mockImplementation(() => { + throw new Error('second if condition called.'); + }); + + try { + await processEvent( + { + exception: { + values: [], + }, + message: "Assertion failed: 'this' is expected an Event object, but got", + }, + {}, + ); + } catch (err: Error | unknown) { + expect((err as Error).message).toBe('second if condition called.'); + } finally { + expect(spy).toHaveBeenCalled(); + spy.mockRestore(); + } + }); + it('should symbolicate error with cause ', async () => { (parseErrorStack as jest.Mock) .mockReturnValueOnce(>[ diff --git a/samples/react-native/src/App.tsx b/samples/react-native/src/App.tsx index 0e18f9071b..14258fdde9 100644 --- a/samples/react-native/src/App.tsx +++ b/samples/react-native/src/App.tsx @@ -16,6 +16,7 @@ import Animated, { // Import the Sentry React Native SDK import * as Sentry from '@sentry/react-native'; +import * as SentryCore from '@sentry/core'; import { FeedbackWidget } from '@sentry/react-native'; import ErrorsScreen from './Screens/ErrorsScreen'; @@ -81,6 +82,7 @@ Sentry.init({ integrations(integrations) { integrations.push( reactNavigationIntegration, + SentryCore.captureConsoleIntegration(), Sentry.reactNativeTracingIntegration({ // The time to wait in ms until the transaction will be finished, For testing, default is 1000 ms idleTimeoutMs: 5_000, diff --git a/samples/react-native/src/Screens/ErrorsScreen.tsx b/samples/react-native/src/Screens/ErrorsScreen.tsx index c1e3209246..b39fe50663 100644 --- a/samples/react-native/src/Screens/ErrorsScreen.tsx +++ b/samples/react-native/src/Screens/ErrorsScreen.tsx @@ -239,6 +239,26 @@ const ErrorsScreen = (_props: Props) => { /> ) : null} +