@@ -899,6 +899,18 @@ describe('BaseClient', () => {
899899 expect ( TestClient . instance ! . event ! . message ) . toEqual ( 'hello' ) ;
900900 } ) ;
901901
902+ test ( 'calls `beforeSendTransaction` and uses original event without any changes' , ( ) => {
903+ expect . assertions ( 1 ) ;
904+
905+ const beforeSendTransaction = jest . fn ( event => event ) ;
906+ const options = getDefaultTestClientOptions ( { dsn : PUBLIC_DSN , beforeSendTransaction } ) ;
907+ const client = new TestClient ( options ) ;
908+
909+ client . captureEvent ( { transaction : '/dogs/are/great' , type : 'transaction' } ) ;
910+
911+ expect ( TestClient . instance ! . event ! . transaction ) . toBe ( '/dogs/are/great' ) ;
912+ } ) ;
913+
902914 test ( 'calls `beforeSend` and uses the modified event' , ( ) => {
903915 expect . assertions ( 1 ) ;
904916
@@ -914,6 +926,21 @@ describe('BaseClient', () => {
914926 expect ( TestClient . instance ! . event ! . message ) . toEqual ( 'changed1' ) ;
915927 } ) ;
916928
929+ test ( 'calls `beforeSendTransaction` and uses the modified event' , ( ) => {
930+ expect . assertions ( 1 ) ;
931+
932+ const beforeSendTransaction = jest . fn ( event => {
933+ event . transaction = '/adopt/dont/shop' ;
934+ return event ;
935+ } ) ;
936+ const options = getDefaultTestClientOptions ( { dsn : PUBLIC_DSN , beforeSendTransaction } ) ;
937+ const client = new TestClient ( options ) ;
938+
939+ client . captureEvent ( { transaction : '/dogs/are/great' , type : 'transaction' } ) ;
940+
941+ expect ( TestClient . instance ! . event ! . transaction ) . toBe ( '/adopt/dont/shop' ) ;
942+ } ) ;
943+
917944 test ( 'calls `beforeSend` and discards the event' , ( ) => {
918945 expect . assertions ( 3 ) ;
919946
@@ -932,6 +959,24 @@ describe('BaseClient', () => {
932959 expect ( loggerWarnSpy ) . toBeCalledWith ( '`beforeSend` returned `null`, will not send event.' ) ;
933960 } ) ;
934961
962+ test ( 'calls `beforeSendTransaction` and discards the event' , ( ) => {
963+ expect . assertions ( 3 ) ;
964+
965+ const beforeSendTransaction = jest . fn ( ( ) => null ) ;
966+ const options = getDefaultTestClientOptions ( { dsn : PUBLIC_DSN , beforeSendTransaction } ) ;
967+ const client = new TestClient ( options ) ;
968+ const captureExceptionSpy = jest . spyOn ( client , 'captureException' ) ;
969+ const loggerWarnSpy = jest . spyOn ( logger , 'log' ) ;
970+
971+ client . captureEvent ( { transaction : '/dogs/are/great' , type : 'transaction' } ) ;
972+
973+ expect ( TestClient . instance ! . event ) . toBeUndefined ( ) ;
974+ // This proves that the reason the event didn't send/didn't get set on the test client is not because there was an
975+ // error, but because `beforeSendTransaction` returned `null`
976+ expect ( captureExceptionSpy ) . not . toBeCalled ( ) ;
977+ expect ( loggerWarnSpy ) . toBeCalledWith ( '`beforeSendTransaction` returned `null`, will not send event.' ) ;
978+ } ) ;
979+
935980 test ( 'calls `beforeSend` and logs info about invalid return value' , ( ) => {
936981 const invalidValues = [ undefined , false , true , [ ] , 1 ] ;
937982 expect . assertions ( invalidValues . length * 2 ) ;
@@ -950,6 +995,26 @@ describe('BaseClient', () => {
950995 }
951996 } ) ;
952997
998+ test ( 'calls `beforeSendTransaction` and logs info about invalid return value' , ( ) => {
999+ const invalidValues = [ undefined , false , true , [ ] , 1 ] ;
1000+ expect . assertions ( invalidValues . length * 2 ) ;
1001+
1002+ for ( const val of invalidValues ) {
1003+ const beforeSendTransaction = jest . fn ( ( ) => val ) ;
1004+ // @ts -ignore we need to test regular-js behavior
1005+ const options = getDefaultTestClientOptions ( { dsn : PUBLIC_DSN , beforeSendTransaction } ) ;
1006+ const client = new TestClient ( options ) ;
1007+ const loggerWarnSpy = jest . spyOn ( logger , 'warn' ) ;
1008+
1009+ client . captureEvent ( { transaction : '/dogs/are/great' , type : 'transaction' } ) ;
1010+
1011+ expect ( TestClient . instance ! . event ) . toBeUndefined ( ) ;
1012+ expect ( loggerWarnSpy ) . toBeCalledWith (
1013+ new SentryError ( '`beforeSendTransaction` must return `null` or a valid event.' ) ,
1014+ ) ;
1015+ }
1016+ } ) ;
1017+
9531018 test ( 'calls async `beforeSend` and uses original event without any changes' , done => {
9541019 jest . useFakeTimers ( ) ;
9551020 expect . assertions ( 1 ) ;
@@ -979,6 +1044,35 @@ describe('BaseClient', () => {
9791044 jest . runOnlyPendingTimers ( ) ;
9801045 } ) ;
9811046
1047+ test ( 'calls async `beforeSendTransaction` and uses original event without any changes' , done => {
1048+ jest . useFakeTimers ( ) ;
1049+ expect . assertions ( 1 ) ;
1050+
1051+ const beforeSendTransaction = jest . fn (
1052+ async event =>
1053+ new Promise < Event > ( resolve => {
1054+ setTimeout ( ( ) => {
1055+ resolve ( event ) ;
1056+ } , 1 ) ;
1057+ } ) ,
1058+ ) ;
1059+ const options = getDefaultTestClientOptions ( { dsn : PUBLIC_DSN , beforeSendTransaction } ) ;
1060+ const client = new TestClient ( options ) ;
1061+
1062+ client . captureEvent ( { transaction : '/dogs/are/great' , type : 'transaction' } ) ;
1063+ jest . runOnlyPendingTimers ( ) ;
1064+
1065+ TestClient . sendEventCalled = ( event : Event ) => {
1066+ expect ( event . transaction ) . toBe ( '/dogs/are/great' ) ;
1067+ } ;
1068+
1069+ setTimeout ( ( ) => {
1070+ done ( ) ;
1071+ } , 5 ) ;
1072+
1073+ jest . runOnlyPendingTimers ( ) ;
1074+ } ) ;
1075+
9821076 test ( 'calls async `beforeSend` and uses the modified event' , done => {
9831077 jest . useFakeTimers ( ) ;
9841078 expect . assertions ( 1 ) ;
@@ -1008,6 +1102,35 @@ describe('BaseClient', () => {
10081102 jest . runOnlyPendingTimers ( ) ;
10091103 } ) ;
10101104
1105+ test ( 'calls async `beforeSendTransaction` and uses the modified event' , done => {
1106+ jest . useFakeTimers ( ) ;
1107+ expect . assertions ( 1 ) ;
1108+
1109+ const beforeSendTransaction = jest . fn ( async event => {
1110+ event . transaction = '/adopt/dont/shop' ;
1111+ return new Promise < Event > ( resolve => {
1112+ setTimeout ( ( ) => {
1113+ resolve ( event ) ;
1114+ } , 1 ) ;
1115+ } ) ;
1116+ } ) ;
1117+ const options = getDefaultTestClientOptions ( { dsn : PUBLIC_DSN , beforeSendTransaction } ) ;
1118+ const client = new TestClient ( options ) ;
1119+
1120+ client . captureEvent ( { transaction : '/dogs/are/great' , type : 'transaction' } ) ;
1121+ jest . runOnlyPendingTimers ( ) ;
1122+
1123+ TestClient . sendEventCalled = ( event : Event ) => {
1124+ expect ( event . transaction ) . toBe ( '/adopt/dont/shop' ) ;
1125+ } ;
1126+
1127+ setTimeout ( ( ) => {
1128+ done ( ) ;
1129+ } , 5 ) ;
1130+
1131+ jest . runOnlyPendingTimers ( ) ;
1132+ } ) ;
1133+
10111134 test ( 'calls async `beforeSend` and discards the event' , ( ) => {
10121135 jest . useFakeTimers ( ) ;
10131136 expect . assertions ( 1 ) ;
@@ -1029,6 +1152,27 @@ describe('BaseClient', () => {
10291152 expect ( TestClient . instance ! . event ) . toBeUndefined ( ) ;
10301153 } ) ;
10311154
1155+ test ( 'calls async `beforeSendTransaction` and discards the event' , ( ) => {
1156+ jest . useFakeTimers ( ) ;
1157+ expect . assertions ( 1 ) ;
1158+
1159+ const beforeSendTransaction = jest . fn (
1160+ async ( ) =>
1161+ new Promise < null > ( resolve => {
1162+ setTimeout ( ( ) => {
1163+ resolve ( null ) ;
1164+ } ) ;
1165+ } ) ,
1166+ ) ;
1167+ const options = getDefaultTestClientOptions ( { dsn : PUBLIC_DSN , beforeSendTransaction } ) ;
1168+ const client = new TestClient ( options ) ;
1169+
1170+ client . captureEvent ( { transaction : '/dogs/are/great' , type : 'transaction' } ) ;
1171+ jest . runAllTimers ( ) ;
1172+
1173+ expect ( TestClient . instance ! . event ) . toBeUndefined ( ) ;
1174+ } ) ;
1175+
10321176 test ( '`beforeSend` gets access to a hint as a second argument' , ( ) => {
10331177 expect . assertions ( 2 ) ;
10341178
@@ -1042,6 +1186,22 @@ describe('BaseClient', () => {
10421186 expect ( ( TestClient . instance ! . event ! as any ) . data ) . toEqual ( 'someRandomThing' ) ;
10431187 } ) ;
10441188
1189+ test ( '`beforeSendTransaction` gets access to a hint as a second argument' , ( ) => {
1190+ expect . assertions ( 2 ) ;
1191+
1192+ const beforeSendTransaction = jest . fn ( ( event , hint ) => ( { ...event , data : hint . data } ) ) ;
1193+ const options = getDefaultTestClientOptions ( { dsn : PUBLIC_DSN , beforeSendTransaction } ) ;
1194+ const client = new TestClient ( options ) ;
1195+
1196+ client . captureEvent (
1197+ { transaction : '/dogs/are/great' , type : 'transaction' } ,
1198+ { data : { dogs : 'yes' , cats : 'maybe' } } ,
1199+ ) ;
1200+
1201+ expect ( TestClient . instance ! . event ! . transaction ) . toBe ( '/dogs/are/great' ) ;
1202+ expect ( ( TestClient . instance ! . event ! as any ) . data ) . toEqual ( { dogs : 'yes' , cats : 'maybe' } ) ;
1203+ } ) ;
1204+
10451205 test ( '`beforeSend` records dropped events' , ( ) => {
10461206 expect . assertions ( 1 ) ;
10471207
@@ -1061,7 +1221,26 @@ describe('BaseClient', () => {
10611221 expect ( recordLostEventSpy ) . toHaveBeenCalledWith ( 'before_send' , 'error' ) ;
10621222 } ) ;
10631223
1064- test ( 'event processor drops the event when it returns `null`' , ( ) => {
1224+ test ( '`beforeSendTransaction` records dropped events' , ( ) => {
1225+ expect . assertions ( 1 ) ;
1226+
1227+ const client = new TestClient (
1228+ getDefaultTestClientOptions ( {
1229+ dsn : PUBLIC_DSN ,
1230+ beforeSendTransaction ( ) {
1231+ return null ;
1232+ } ,
1233+ } ) ,
1234+ ) ;
1235+
1236+ const recordLostEventSpy = jest . spyOn ( client , 'recordDroppedEvent' ) ;
1237+
1238+ client . captureEvent ( { transaction : '/dogs/are/great' , type : 'transaction' } ) ;
1239+
1240+ expect ( recordLostEventSpy ) . toHaveBeenCalledWith ( 'before_send' , 'transaction' ) ;
1241+ } ) ;
1242+
1243+ test ( 'event processor drops error event when it returns `null`' , ( ) => {
10651244 expect . assertions ( 3 ) ;
10661245
10671246 const client = new TestClient ( getDefaultTestClientOptions ( { dsn : PUBLIC_DSN } ) ) ;
@@ -1079,7 +1258,25 @@ describe('BaseClient', () => {
10791258 expect ( loggerLogSpy ) . toBeCalledWith ( 'An event processor returned `null`, will not send event.' ) ;
10801259 } ) ;
10811260
1082- test ( 'event processor records dropped events' , ( ) => {
1261+ test ( 'event processor drops transaction event when it returns `null`' , ( ) => {
1262+ expect . assertions ( 3 ) ;
1263+
1264+ const client = new TestClient ( getDefaultTestClientOptions ( { dsn : PUBLIC_DSN } ) ) ;
1265+ const captureExceptionSpy = jest . spyOn ( client , 'captureException' ) ;
1266+ const loggerLogSpy = jest . spyOn ( logger , 'log' ) ;
1267+ const scope = new Scope ( ) ;
1268+ scope . addEventProcessor ( ( ) => null ) ;
1269+
1270+ client . captureEvent ( { transaction : '/dogs/are/great' , type : 'transaction' } , { } , scope ) ;
1271+
1272+ expect ( TestClient . instance ! . event ) . toBeUndefined ( ) ;
1273+ // This proves that the reason the event didn't send/didn't get set on the test client is not because there was an
1274+ // error, but because the event processor returned `null`
1275+ expect ( captureExceptionSpy ) . not . toBeCalled ( ) ;
1276+ expect ( loggerLogSpy ) . toBeCalledWith ( 'An event processor returned `null`, will not send event.' ) ;
1277+ } ) ;
1278+
1279+ test ( 'event processor records dropped error events' , ( ) => {
10831280 expect . assertions ( 1 ) ;
10841281
10851282 const options = getDefaultTestClientOptions ( { dsn : PUBLIC_DSN } ) ;
@@ -1095,6 +1292,22 @@ describe('BaseClient', () => {
10951292 expect ( recordLostEventSpy ) . toHaveBeenCalledWith ( 'event_processor' , 'error' ) ;
10961293 } ) ;
10971294
1295+ test ( 'event processor records dropped transaction events' , ( ) => {
1296+ expect . assertions ( 1 ) ;
1297+
1298+ const options = getDefaultTestClientOptions ( { dsn : PUBLIC_DSN } ) ;
1299+ const client = new TestClient ( options ) ;
1300+
1301+ const recordLostEventSpy = jest . spyOn ( client , 'recordDroppedEvent' ) ;
1302+
1303+ const scope = new Scope ( ) ;
1304+ scope . addEventProcessor ( ( ) => null ) ;
1305+
1306+ client . captureEvent ( { transaction : '/dogs/are/great' , type : 'transaction' } , { } , scope ) ;
1307+
1308+ expect ( recordLostEventSpy ) . toHaveBeenCalledWith ( 'event_processor' , 'transaction' ) ;
1309+ } ) ;
1310+
10981311 test ( 'mutating transaction name with event processors sets transaction-name-change metadata' , ( ) => {
10991312 const options = getDefaultTestClientOptions ( { dsn : PUBLIC_DSN , enableSend : true } ) ;
11001313 const client = new TestClient ( options ) ;
@@ -1130,6 +1343,38 @@ describe('BaseClient', () => {
11301343 } ) ;
11311344 } ) ;
11321345
1346+ test ( 'mutating transaction name with `beforeSendTransaction` sets transaction-name-change metadata' , ( ) => {
1347+ const beforeSendTransaction = jest . fn ( event => {
1348+ event . transaction = '/adopt/dont/shop' ;
1349+ return event ;
1350+ } ) ;
1351+ const options = getDefaultTestClientOptions ( { dsn : PUBLIC_DSN , beforeSendTransaction } ) ;
1352+ const client = new TestClient ( options ) ;
1353+
1354+ client . captureEvent ( {
1355+ transaction : '/dogs/are/great' ,
1356+ type : 'transaction' ,
1357+ transaction_info : {
1358+ source : 'url' ,
1359+ changes : [ ] ,
1360+ propagations : 3 ,
1361+ } ,
1362+ } ) ;
1363+
1364+ expect ( TestClient . instance ! . event ! . transaction ) . toBe ( '/adopt/dont/shop' ) ;
1365+ expect ( TestClient . instance ! . event ! . transaction_info ) . toEqual ( {
1366+ source : 'custom' ,
1367+ changes : [
1368+ {
1369+ propagations : 3 ,
1370+ source : 'custom' ,
1371+ timestamp : expect . any ( Number ) ,
1372+ } ,
1373+ ] ,
1374+ propagations : 3 ,
1375+ } ) ;
1376+ } ) ;
1377+
11331378 test ( 'event processor sends an event and logs when it crashes' , ( ) => {
11341379 expect . assertions ( 3 ) ;
11351380
@@ -1375,6 +1620,7 @@ describe('BaseClient', () => {
13751620 client . recordDroppedEvent ( 'network_error' , 'transaction' ) ;
13761621 client . recordDroppedEvent ( 'network_error' , 'transaction' ) ;
13771622 client . recordDroppedEvent ( 'before_send' , 'error' ) ;
1623+ client . recordDroppedEvent ( 'before_send' , 'transaction' ) ;
13781624 client . recordDroppedEvent ( 'event_processor' , 'attachment' ) ;
13791625 client . recordDroppedEvent ( 'network_error' , 'transaction' ) ;
13801626
@@ -1397,6 +1643,11 @@ describe('BaseClient', () => {
13971643 category : 'error' ,
13981644 quantity : 1 ,
13991645 } ,
1646+ {
1647+ reason : 'before_send' ,
1648+ category : 'transaction' ,
1649+ quantity : 1 ,
1650+ } ,
14001651 {
14011652 reason : 'event_processor' ,
14021653 category : 'attachment' ,
0 commit comments