diff --git a/packages/angular/src/errorhandler.ts b/packages/angular/src/errorhandler.ts index b23b352db170..14ca380ea3ea 100644 --- a/packages/angular/src/errorhandler.ts +++ b/packages/angular/src/errorhandler.ts @@ -85,7 +85,7 @@ class SentryErrorHandler implements AngularErrorHandler, OnDestroy { protected readonly _options: ErrorHandlerOptions; /** The cleanup function is executed when the injector is destroyed. */ - private _removeAfterSendEventListener?: VoidFunction; + private _removeAfterSendEventListener?: () => void; public constructor(@Inject('errorHandlerOptions') options?: ErrorHandlerOptions) { this._options = { @@ -130,7 +130,7 @@ class SentryErrorHandler implements AngularErrorHandler, OnDestroy { this._removeAfterSendEventListener = client.on('afterSendEvent', (event: Event) => { if (!event.type && event.event_id) { runOutsideAngular(() => { - Sentry.showReportDialog({ ...this._options.dialogOptions, eventId: event.event_id! }); + Sentry.showReportDialog({ ...this._options.dialogOptions, eventId: event.event_id }); }); } }); diff --git a/packages/angular/test/errorhandler.test.ts b/packages/angular/test/errorhandler.test.ts index c30a7a87efc9..1ae415c5706d 100644 --- a/packages/angular/test/errorhandler.test.ts +++ b/packages/angular/test/errorhandler.test.ts @@ -546,5 +546,49 @@ describe('SentryErrorHandler', () => { expect(showReportDialogSpy).toBeCalledTimes(1); }); }); + + it('only registers the client "afterSendEvent" listener to open the dialog once', () => { + const unsubScribeSpy = vi.fn(); + const client = { + cbs: [] as ((event: Event) => void)[], + on: vi.fn((_, cb) => { + client.cbs.push(cb); + return unsubScribeSpy; + }), + }; + + vi.spyOn(SentryBrowser, 'getClient').mockImplementation(() => client as unknown as Client); + + const errorhandler = createErrorHandler({ showDialog: true }); + expect(client.cbs).toHaveLength(0); + + errorhandler.handleError(new Error('error 1')); + expect(client.cbs).toHaveLength(1); + + errorhandler.handleError(new Error('error 2')); + errorhandler.handleError(new Error('error 3')); + expect(client.cbs).toHaveLength(1); + }); + + it('cleans up the "afterSendEvent" listener once the ErrorHandler is destroyed', () => { + const unsubScribeSpy = vi.fn(); + const client = { + cbs: [] as ((event: Event) => void)[], + on: vi.fn((_, cb) => { + client.cbs.push(cb); + return unsubScribeSpy; + }), + }; + + vi.spyOn(SentryBrowser, 'getClient').mockImplementation(() => client as unknown as Client); + + const errorhandler = createErrorHandler({ showDialog: true }); + + errorhandler.handleError(new Error('error 1')); + expect(client.cbs).toHaveLength(1); + + errorhandler.ngOnDestroy(); + expect(unsubScribeSpy).toHaveBeenCalledTimes(1); + }); }); });