1- import { beforeEach , describe , expect , it } from 'vitest' ;
1+ import { afterEach , beforeEach , describe , expect , it } from 'vitest' ;
22import type { Client } from '../../../src/client' ;
33import { thirdPartyErrorFilterIntegration } from '../../../src/integrations/third-party-errors-filter' ;
44import { addMetadataToStackFrames } from '../../../src/metadata' ;
@@ -626,7 +626,7 @@ describe('ThirdPartyErrorFilter', () => {
626626 expect ( result ) . toBeDefined ( ) ;
627627 } ) ;
628628
629- it ( 'does not match when filename does not contain both helpers and sentry' , async ( ) => {
629+ it ( 'does not match when filename does not contain both helpers and sentry and function name is not sentryWrapped ' , async ( ) => {
630630 const eventWithWrongFilename : Event = {
631631 exception : {
632632 values : [
@@ -636,7 +636,7 @@ describe('ThirdPartyErrorFilter', () => {
636636 {
637637 colno : 2 ,
638638 filename : 'some-helpers.js' ,
639- function : 'sentryWrapped ' ,
639+ function : 'someFunction ' ,
640640 lineno : 117 ,
641641 context_line : ' return fn.apply(this, wrappedArguments);' ,
642642 pre_context : [
@@ -667,7 +667,219 @@ describe('ThirdPartyErrorFilter', () => {
667667
668668 const event = clone ( eventWithWrongFilename ) ;
669669 const result = await integration . processEvent ?.( event , { } , MOCK_CLIENT ) ;
670- // Should not drop because filename doesn't contain "sentry"
670+ // Should not drop because filename doesn't contain "sentry" and function name is not "sentryWrapped"
671+ expect ( result ) . toBeDefined ( ) ;
672+ } ) ;
673+ } ) ;
674+
675+ describe ( 'minified/bundled code detection' , ( ) => {
676+ afterEach ( ( ) => {
677+ GLOBAL_OBJ . _sentryWrappedDepth = 0 ;
678+ } ) ;
679+
680+ it ( 'detects Sentry internal frame when preprocessEvent snapshots depth inside a sentryWrapped call' , async ( ) => {
681+ const eventWithMinifiedSentryFrame : Event = {
682+ exception : {
683+ values : [
684+ {
685+ stacktrace : {
686+ frames : [
687+ {
688+ colno : 12345 ,
689+ filename : 'https://example.com/assets/app-abc123.js' ,
690+ function : 'a' ,
691+ lineno : 1 ,
692+ } ,
693+ {
694+ colno : 1 ,
695+ filename : 'other-file.js' ,
696+ function : 'function' ,
697+ lineno : 1 ,
698+ } ,
699+ ] ,
700+ } ,
701+ type : 'Error' ,
702+ value : 'Third party error' ,
703+ } ,
704+ ] ,
705+ } ,
706+ } ;
707+
708+ const integration = thirdPartyErrorFilterIntegration ( {
709+ behaviour : 'drop-error-if-exclusively-contains-third-party-frames' ,
710+ filterKeys : [ 'some-key' ] ,
711+ ignoreSentryInternalFrames : true ,
712+ } ) ;
713+
714+ // Simulate being inside a sentryWrapped call
715+ GLOBAL_OBJ . _sentryWrappedDepth = 1 ;
716+ const event = clone ( eventWithMinifiedSentryFrame ) ;
717+ // preprocessEvent snapshots the depth onto the event
718+ integration . preprocessEvent ?.( event , { } , MOCK_CLIENT ) ;
719+ // Even if depth resets before processEvent (async processor scenario), the snapshot survives
720+ GLOBAL_OBJ . _sentryWrappedDepth = 0 ;
721+ const result = await integration . processEvent ?.( event , { } , MOCK_CLIENT ) ;
722+ expect ( result ) . toBe ( null ) ;
723+ } ) ;
724+
725+ it ( 'detects Sentry internal frame by function name sentryWrapped even without source patterns' , async ( ) => {
726+ const eventWithFunctionName : Event = {
727+ exception : {
728+ values : [
729+ {
730+ stacktrace : {
731+ frames : [
732+ {
733+ colno : 12345 ,
734+ filename : 'https://example.com/assets/app-abc123.js' ,
735+ function : 'sentryWrapped' ,
736+ lineno : 1 ,
737+ } ,
738+ {
739+ colno : 1 ,
740+ filename : 'other-file.js' ,
741+ function : 'function' ,
742+ lineno : 1 ,
743+ } ,
744+ ] ,
745+ } ,
746+ type : 'Error' ,
747+ value : 'Third party error' ,
748+ } ,
749+ ] ,
750+ } ,
751+ } ;
752+
753+ const integration = thirdPartyErrorFilterIntegration ( {
754+ behaviour : 'drop-error-if-exclusively-contains-third-party-frames' ,
755+ filterKeys : [ 'some-key' ] ,
756+ ignoreSentryInternalFrames : true ,
757+ } ) ;
758+
759+ const event = clone ( eventWithFunctionName ) ;
760+ const result = await integration . processEvent ?.( event , { } , MOCK_CLIENT ) ;
761+ expect ( result ) . toBe ( null ) ;
762+ } ) ;
763+
764+ it ( 'does not detect minified frame as Sentry internal when not inside sentryWrapped and function name is mangled' , async ( ) => {
765+ const eventWithMinifiedFrame : Event = {
766+ exception : {
767+ values : [
768+ {
769+ stacktrace : {
770+ frames : [
771+ {
772+ colno : 12345 ,
773+ filename : 'https://example.com/assets/app-abc123.js' ,
774+ function : 'a' ,
775+ lineno : 1 ,
776+ } ,
777+ {
778+ colno : 1 ,
779+ filename : 'other-file.js' ,
780+ function : 'function' ,
781+ lineno : 1 ,
782+ } ,
783+ ] ,
784+ } ,
785+ type : 'Error' ,
786+ value : 'Third party error' ,
787+ } ,
788+ ] ,
789+ } ,
790+ } ;
791+
792+ const integration = thirdPartyErrorFilterIntegration ( {
793+ behaviour : 'drop-error-if-exclusively-contains-third-party-frames' ,
794+ filterKeys : [ 'some-key' ] ,
795+ ignoreSentryInternalFrames : true ,
796+ } ) ;
797+
798+ GLOBAL_OBJ . _sentryWrappedDepth = 0 ;
799+ const event = clone ( eventWithMinifiedFrame ) ;
800+ integration . preprocessEvent ?.( event , { } , MOCK_CLIENT ) ;
801+ const result = await integration . processEvent ?.( event , { } , MOCK_CLIENT ) ;
802+ expect ( result ) . toBeDefined ( ) ;
803+ } ) ;
804+
805+ it ( 'does not exclude non-minified frame even when inside sentryWrapped (parser already stripped it)' , async ( ) => {
806+ const eventWithNonMinifiedFrame : Event = {
807+ exception : {
808+ values : [
809+ {
810+ stacktrace : {
811+ frames : [
812+ {
813+ colno : 10 ,
814+ filename : 'app.js' ,
815+ function : 'handleClick' ,
816+ lineno : 42 ,
817+ context_line : ' throw new Error("oops");' ,
818+ } ,
819+ ] ,
820+ } ,
821+ type : 'Error' ,
822+ value : 'oops' ,
823+ } ,
824+ ] ,
825+ } ,
826+ } ;
827+
828+ const integration = thirdPartyErrorFilterIntegration ( {
829+ behaviour : 'drop-error-if-exclusively-contains-third-party-frames' ,
830+ filterKeys : [ 'some-key' ] ,
831+ ignoreSentryInternalFrames : true ,
832+ } ) ;
833+
834+ GLOBAL_OBJ . _sentryWrappedDepth = 1 ;
835+ const event = clone ( eventWithNonMinifiedFrame ) ;
836+ integration . preprocessEvent ?.( event , { } , MOCK_CLIENT ) ;
837+ GLOBAL_OBJ . _sentryWrappedDepth = 0 ;
838+ const result = await integration . processEvent ?.( event , { } , MOCK_CLIENT ) ;
839+ // Should NOT be dropped — the frame has context_line, so it's not a minified sentryWrapped frame.
840+ // The real sentryWrapped frame was already stripped by the parser.
841+ expect ( result ) . toBeDefined ( ) ;
842+ } ) ;
843+
844+ it ( 'does not use sentryWrappedDepth when ignoreSentryInternalFrames is false' , async ( ) => {
845+ const eventWithMinifiedSentryFrame : Event = {
846+ exception : {
847+ values : [
848+ {
849+ stacktrace : {
850+ frames : [
851+ {
852+ colno : 12345 ,
853+ filename : 'https://example.com/assets/app-abc123.js' ,
854+ function : 'a' ,
855+ lineno : 1 ,
856+ } ,
857+ {
858+ colno : 1 ,
859+ filename : 'other-file.js' ,
860+ function : 'function' ,
861+ lineno : 1 ,
862+ } ,
863+ ] ,
864+ } ,
865+ type : 'Error' ,
866+ value : 'Third party error' ,
867+ } ,
868+ ] ,
869+ } ,
870+ } ;
871+
872+ const integration = thirdPartyErrorFilterIntegration ( {
873+ behaviour : 'drop-error-if-exclusively-contains-third-party-frames' ,
874+ filterKeys : [ 'some-key' ] ,
875+ ignoreSentryInternalFrames : false ,
876+ } ) ;
877+
878+ GLOBAL_OBJ . _sentryWrappedDepth = 1 ;
879+ const event = clone ( eventWithMinifiedSentryFrame ) ;
880+ integration . preprocessEvent ?.( event , { } , MOCK_CLIENT ) ;
881+ GLOBAL_OBJ . _sentryWrappedDepth = 0 ;
882+ const result = await integration . processEvent ?.( event , { } , MOCK_CLIENT ) ;
671883 expect ( result ) . toBeDefined ( ) ;
672884 } ) ;
673885 } ) ;
0 commit comments