@@ -20,8 +20,9 @@ import {
2020 UI_LOAD ,
2121} from '../../../src/js/tracing' ;
2222import {
23+ _captureAppStart ,
2324 _clearRootComponentCreationTimestampMs ,
24- _setAppStartEndTimestampMs ,
25+ _setAppStartEndData ,
2526 _setRootComponentCreationTimestampMs ,
2627 appStartIntegration ,
2728 setRootComponentCreationTimestampMs ,
@@ -788,6 +789,169 @@ describe('App Start Integration', () => {
788789 } ) ;
789790} ) ;
790791
792+ describe ( 'Frame Data Integration' , ( ) => {
793+ it ( 'attaches frame data to standalone cold app start span' , async ( ) => {
794+ const mockEndFrames = {
795+ totalFrames : 150 ,
796+ slowFrames : 5 ,
797+ frozenFrames : 2 ,
798+ } ;
799+
800+ mockFunction ( NATIVE . fetchNativeFrames ) . mockResolvedValue ( mockEndFrames ) ;
801+
802+ mockAppStart ( { cold : true } ) ;
803+
804+ const actualEvent = await captureStandAloneAppStart ( ) ;
805+
806+ const appStartSpan = actualEvent ! . spans ! . find ( ( { description } ) => description === 'Cold App Start' ) ;
807+
808+ expect ( appStartSpan ) . toBeDefined ( ) ;
809+ expect ( appStartSpan ! . data ) . toEqual (
810+ expect . objectContaining ( {
811+ 'frames.total' : 150 ,
812+ 'frames.slow' : 5 ,
813+ 'frames.frozen' : 2 ,
814+ [ SEMANTIC_ATTRIBUTE_SENTRY_OP ] : APP_START_COLD_OP ,
815+ [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : SPAN_ORIGIN_AUTO_APP_START ,
816+ } ) ,
817+ ) ;
818+ } ) ;
819+
820+ it ( 'attaches frame data to standalone warm app start span' , async ( ) => {
821+ const mockEndFrames = {
822+ totalFrames : 200 ,
823+ slowFrames : 8 ,
824+ frozenFrames : 1 ,
825+ } ;
826+
827+ mockFunction ( NATIVE . fetchNativeFrames ) . mockResolvedValue ( mockEndFrames ) ;
828+
829+ mockAppStart ( { cold : false } ) ;
830+
831+ const actualEvent = await captureStandAloneAppStart ( ) ;
832+
833+ const appStartSpan = actualEvent ! . spans ! . find ( ( { description } ) => description === 'Warm App Start' ) ;
834+
835+ expect ( appStartSpan ) . toBeDefined ( ) ;
836+ expect ( appStartSpan ! . data ) . toEqual (
837+ expect . objectContaining ( {
838+ 'frames.total' : 200 ,
839+ 'frames.slow' : 8 ,
840+ 'frames.frozen' : 1 ,
841+ [ SEMANTIC_ATTRIBUTE_SENTRY_OP ] : APP_START_WARM_OP ,
842+ [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : SPAN_ORIGIN_AUTO_APP_START ,
843+ } ) ,
844+ ) ;
845+ } ) ;
846+
847+ it ( 'attaches frame data to attached cold app start span' , async ( ) => {
848+ const mockEndFrames = {
849+ totalFrames : 120 ,
850+ slowFrames : 3 ,
851+ frozenFrames : 0 ,
852+ } ;
853+
854+ mockFunction ( NATIVE . fetchNativeFrames ) . mockResolvedValue ( mockEndFrames ) ;
855+
856+ mockAppStart ( { cold : true } ) ;
857+
858+ await _captureAppStart ( { isManual : false } ) ;
859+
860+ const actualEvent = await processEvent ( getMinimalTransactionEvent ( ) ) ;
861+
862+ const appStartSpan = actualEvent ! . spans ! . find ( ( { description } ) => description === 'Cold App Start' ) ;
863+
864+ expect ( appStartSpan ) . toBeDefined ( ) ;
865+ expect ( appStartSpan ! . data ) . toEqual (
866+ expect . objectContaining ( {
867+ 'frames.total' : 120 ,
868+ 'frames.slow' : 3 ,
869+ 'frames.frozen' : 0 ,
870+ [ SEMANTIC_ATTRIBUTE_SENTRY_OP ] : APP_START_COLD_OP ,
871+ [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : SPAN_ORIGIN_AUTO_APP_START ,
872+ } ) ,
873+ ) ;
874+ } ) ;
875+
876+ it ( 'attaches frame data to attached warm app start span' , async ( ) => {
877+ const mockEndFrames = {
878+ totalFrames : 180 ,
879+ slowFrames : 12 ,
880+ frozenFrames : 3 ,
881+ } ;
882+
883+ mockFunction ( NATIVE . fetchNativeFrames ) . mockResolvedValue ( mockEndFrames ) ;
884+
885+ mockAppStart ( { cold : false } ) ;
886+
887+ await _captureAppStart ( { isManual : false } ) ;
888+
889+ const actualEvent = await processEvent ( getMinimalTransactionEvent ( ) ) ;
890+
891+ const appStartSpan = actualEvent ! . spans ! . find ( ( { description } ) => description === 'Warm App Start' ) ;
892+
893+ expect ( appStartSpan ) . toBeDefined ( ) ;
894+ expect ( appStartSpan ! . data ) . toEqual (
895+ expect . objectContaining ( {
896+ 'frames.total' : 180 ,
897+ 'frames.slow' : 12 ,
898+ 'frames.frozen' : 3 ,
899+ [ SEMANTIC_ATTRIBUTE_SENTRY_OP ] : APP_START_WARM_OP ,
900+ [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : SPAN_ORIGIN_AUTO_APP_START ,
901+ } ) ,
902+ ) ;
903+ } ) ;
904+
905+ it ( 'does not attach frame data when native frames are not available' , async ( ) => {
906+ mockFunction ( NATIVE . fetchNativeFrames ) . mockRejectedValue ( new Error ( 'Native frames not available' ) ) ;
907+
908+ mockAppStart ( { cold : true } ) ;
909+
910+ const actualEvent = await captureStandAloneAppStart ( ) ;
911+
912+ const appStartSpan = actualEvent ! . spans ! . find ( ( { description } ) => description === 'Cold App Start' ) ;
913+
914+ expect ( appStartSpan ) . toBeDefined ( ) ;
915+ expect ( appStartSpan ! . data ) . toEqual (
916+ expect . objectContaining ( {
917+ [ SEMANTIC_ATTRIBUTE_SENTRY_OP ] : APP_START_COLD_OP ,
918+ [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : SPAN_ORIGIN_AUTO_APP_START ,
919+ } ) ,
920+ ) ;
921+
922+ expect ( appStartSpan ! . data ) . not . toHaveProperty ( 'frames.total' ) ;
923+ expect ( appStartSpan ! . data ) . not . toHaveProperty ( 'frames.slow' ) ;
924+ expect ( appStartSpan ! . data ) . not . toHaveProperty ( 'frames.frozen' ) ;
925+ } ) ;
926+
927+ it ( 'does not attach frame data when NATIVE is not enabled' , async ( ) => {
928+ const originalEnableNative = NATIVE . enableNative ;
929+ ( NATIVE as any ) . enableNative = false ;
930+
931+ try {
932+ mockAppStart ( { cold : true } ) ;
933+
934+ const actualEvent = await captureStandAloneAppStart ( ) ;
935+
936+ const appStartSpan = actualEvent ! . spans ! . find ( ( { description } ) => description === 'Cold App Start' ) ;
937+
938+ expect ( appStartSpan ) . toBeDefined ( ) ;
939+ expect ( appStartSpan ! . data ) . toEqual (
940+ expect . objectContaining ( {
941+ [ SEMANTIC_ATTRIBUTE_SENTRY_OP ] : APP_START_COLD_OP ,
942+ [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : SPAN_ORIGIN_AUTO_APP_START ,
943+ } ) ,
944+ ) ;
945+
946+ expect ( appStartSpan ! . data ) . not . toHaveProperty ( 'frames.total' ) ;
947+ expect ( appStartSpan ! . data ) . not . toHaveProperty ( 'frames.slow' ) ;
948+ expect ( appStartSpan ! . data ) . not . toHaveProperty ( 'frames.frozen' ) ;
949+ } finally {
950+ ( NATIVE as any ) . enableNative = originalEnableNative ;
951+ }
952+ } ) ;
953+ } ) ;
954+
791955function setupIntegration ( ) {
792956 const client = new TestClient ( getDefaultTestClientOptions ( ) ) ;
793957 const integration = appStartIntegration ( ) ;
@@ -1095,7 +1259,10 @@ function mockAppStart({
10951259 : [ ] ,
10961260 } ;
10971261
1098- _setAppStartEndTimestampMs ( appStartEndTimestampMs || timeOriginMilliseconds ) ;
1262+ _setAppStartEndData ( {
1263+ timestampMs : appStartEndTimestampMs || timeOriginMilliseconds ,
1264+ endFrames : null ,
1265+ } ) ;
10991266 mockFunction ( getTimeOriginMilliseconds ) . mockReturnValue ( timeOriginMilliseconds ) ;
11001267 mockFunction ( NATIVE . fetchNativeAppStart ) . mockResolvedValue ( mockAppStartResponse ) ;
11011268
@@ -1112,7 +1279,10 @@ function mockTooLongAppStart() {
11121279 spans : [ ] ,
11131280 } ;
11141281
1115- _setAppStartEndTimestampMs ( timeOriginMilliseconds ) ;
1282+ _setAppStartEndData ( {
1283+ timestampMs : timeOriginMilliseconds ,
1284+ endFrames : null ,
1285+ } ) ;
11161286 mockFunction ( getTimeOriginMilliseconds ) . mockReturnValue ( timeOriginMilliseconds ) ;
11171287 mockFunction ( NATIVE . fetchNativeAppStart ) . mockResolvedValue ( mockAppStartResponse ) ;
11181288
@@ -1134,7 +1304,10 @@ function mockTooOldAppStart() {
11341304
11351305 // App start finish timestamp
11361306 // App start length is 5 seconds
1137- _setAppStartEndTimestampMs ( appStartEndTimestampMilliseconds ) ;
1307+ _setAppStartEndData ( {
1308+ timestampMs : appStartEndTimestampMilliseconds ,
1309+ endFrames : null ,
1310+ } ) ;
11381311 mockFunction ( getTimeOriginMilliseconds ) . mockReturnValue ( timeOriginMilliseconds - 64000 ) ;
11391312 mockFunction ( NATIVE . fetchNativeAppStart ) . mockResolvedValue ( mockAppStartResponse ) ;
11401313 // Transaction start timestamp
0 commit comments