@@ -11,6 +11,15 @@ import { AnalyticsEventBuilder } from '../../../../util/analytics/AnalyticsEvent
1111
1212jest . mock ( '../../../hooks/useAnalytics/useAnalytics' ) ;
1313
14+ const mockTrace = jest . fn ( ) ;
15+ const mockEndTrace = jest . fn ( ) ;
16+
17+ jest . mock ( '../../../../util/trace' , ( ) => ( {
18+ ...jest . requireActual ( '../../../../util/trace' ) ,
19+ trace : ( ...args : unknown [ ] ) => mockTrace ( ...args ) ,
20+ endTrace : ( ...args : unknown [ ] ) => mockEndTrace ( ...args ) ,
21+ } ) ) ;
22+
1423const mockSetIsChartBeingTouched = jest . fn ( ) ;
1524jest . mock ( '../PriceChart/PriceChart.context' , ( ) => ( {
1625 usePriceChart : ( ) => ( {
@@ -830,4 +839,203 @@ describe('PriceAdvanced', () => {
830839 expect ( mockSetIsChartBeingTouched ) . toHaveBeenCalledWith ( false ) ;
831840 } ) ;
832841 } ) ;
842+
843+ describe ( 'performance tracing' , ( ) => {
844+ beforeEach ( ( ) => {
845+ mockTrace . mockClear ( ) ;
846+ mockEndTrace . mockClear ( ) ;
847+ } ) ;
848+
849+ it ( 'starts initial visibility trace when component mounts with advanced chart' , ( ) => {
850+ render ( < PriceAdvanced { ...baseProps } /> ) ;
851+
852+ expect ( mockTrace ) . toHaveBeenCalledWith (
853+ expect . objectContaining ( {
854+ name : expect . stringContaining ( 'Advanced Chart Initial Visible' ) ,
855+ op : expect . stringContaining ( 'token_overview.advanced_chart' ) ,
856+ } ) ,
857+ ) ;
858+ } ) ;
859+
860+ it ( 'ends trace when onSkeletonHidden is called with matching series key' , ( ) => {
861+ const { getByTestId } = render ( < PriceAdvanced { ...baseProps } /> ) ;
862+ const advancedChart = getByTestId ( 'mock-advanced-chart' ) ;
863+
864+ mockEndTrace . mockClear ( ) ;
865+
866+ act ( ( ) => {
867+ advancedChart . props . onSkeletonHidden ?.( ) ;
868+ } ) ;
869+
870+ expect ( mockEndTrace ) . toHaveBeenCalledWith (
871+ expect . objectContaining ( {
872+ name : expect . stringContaining ( 'Advanced Chart Initial Visible' ) ,
873+ } ) ,
874+ ) ;
875+ } ) ;
876+
877+ it ( 'ends trace with error data when onError is called' , ( ) => {
878+ const { getByTestId } = render ( < PriceAdvanced { ...baseProps } /> ) ;
879+ const advancedChart = getByTestId ( 'mock-advanced-chart' ) ;
880+
881+ mockEndTrace . mockClear ( ) ;
882+
883+ act ( ( ) => {
884+ advancedChart . props . onError ?.( 'WebView failed to load' ) ;
885+ } ) ;
886+
887+ expect ( mockEndTrace ) . toHaveBeenCalledWith (
888+ expect . objectContaining ( {
889+ name : expect . stringContaining ( 'Advanced Chart Initial Visible' ) ,
890+ data : expect . objectContaining ( {
891+ errorMessage : 'WebView failed to load' ,
892+ } ) ,
893+ } ) ,
894+ ) ;
895+ } ) ;
896+
897+ it ( 'starts time range visibility trace when time range changes' , ( ) => {
898+ const { getByTestId } = render ( < PriceAdvanced { ...baseProps } /> ) ;
899+
900+ mockTrace . mockClear ( ) ;
901+
902+ act ( ( ) => {
903+ fireEvent . press ( getByTestId ( 'select-1W' ) ) ;
904+ } ) ;
905+
906+ expect ( mockTrace ) . toHaveBeenCalledWith (
907+ expect . objectContaining ( {
908+ name : expect . stringContaining ( 'Time Range Visible' ) ,
909+ op : expect . stringContaining ( 'time_range' ) ,
910+ } ) ,
911+ ) ;
912+ } ) ;
913+
914+ it ( 'supersedes previous trace when series key changes before skeleton hidden' , ( ) => {
915+ const { getByTestId } = render ( < PriceAdvanced { ...baseProps } /> ) ;
916+
917+ mockEndTrace . mockClear ( ) ;
918+
919+ act ( ( ) => {
920+ fireEvent . press ( getByTestId ( 'select-1W' ) ) ;
921+ } ) ;
922+
923+ expect ( mockEndTrace ) . toHaveBeenCalledWith (
924+ expect . objectContaining ( {
925+ data : expect . objectContaining ( {
926+ superseded : true ,
927+ } ) ,
928+ } ) ,
929+ ) ;
930+ } ) ;
931+
932+ it ( 'ends trace with fallbackToLegacy when switching to legacy chart' , ( ) => {
933+ const { rerender } = render ( < PriceAdvanced { ...baseProps } /> ) ;
934+
935+ mockEndTrace . mockClear ( ) ;
936+
937+ mockUseOHLCVChart . mockReturnValueOnce ( {
938+ ohlcvData : [ ] ,
939+ isLoading : false ,
940+ error : undefined ,
941+ hasMore : false ,
942+ nextCursor : null ,
943+ hasEmptyData : true ,
944+ } ) ;
945+
946+ rerender ( < PriceAdvanced { ...baseProps } /> ) ;
947+
948+ expect ( mockEndTrace ) . toHaveBeenCalledWith (
949+ expect . objectContaining ( {
950+ data : expect . objectContaining ( {
951+ fallbackToLegacy : true ,
952+ } ) ,
953+ } ) ,
954+ ) ;
955+ } ) ;
956+
957+ it ( 'includes assetId in trace data when available' , ( ) => {
958+ mockTrace . mockClear ( ) ;
959+
960+ render ( < PriceAdvanced { ...baseProps } /> ) ;
961+
962+ expect ( mockTrace ) . toHaveBeenCalledWith (
963+ expect . objectContaining ( {
964+ data : expect . objectContaining ( {
965+ assetId : expect . any ( String ) ,
966+ } ) ,
967+ } ) ,
968+ ) ;
969+ } ) ;
970+
971+ it ( 'does not start trace when falling back to legacy chart immediately' , ( ) => {
972+ mockTrace . mockClear ( ) ;
973+
974+ mockUseOHLCVChart . mockReturnValueOnce ( {
975+ ohlcvData : [ ] ,
976+ isLoading : false ,
977+ error : undefined ,
978+ hasMore : false ,
979+ nextCursor : null ,
980+ hasEmptyData : true ,
981+ } ) ;
982+
983+ render ( < PriceAdvanced { ...baseProps } /> ) ;
984+
985+ expect ( mockTrace ) . not . toHaveBeenCalled ( ) ;
986+ } ) ;
987+
988+ it ( 'ends trace with unmounted flag when component unmounts with open trace' , ( ) => {
989+ const { unmount } = render ( < PriceAdvanced { ...baseProps } /> ) ;
990+
991+ mockEndTrace . mockClear ( ) ;
992+
993+ unmount ( ) ;
994+
995+ expect ( mockEndTrace ) . toHaveBeenCalledWith (
996+ expect . objectContaining ( {
997+ name : expect . stringContaining ( 'Advanced Chart Initial Visible' ) ,
998+ data : expect . objectContaining ( {
999+ unmounted : true ,
1000+ } ) ,
1001+ } ) ,
1002+ ) ;
1003+ } ) ;
1004+
1005+ it ( 'does not end trace on unmount when trace was already completed' , ( ) => {
1006+ const { getByTestId, unmount } = render ( < PriceAdvanced { ...baseProps } /> ) ;
1007+ const advancedChart = getByTestId ( 'mock-advanced-chart' ) ;
1008+
1009+ act ( ( ) => {
1010+ advancedChart . props . onSkeletonHidden ?.( ) ;
1011+ } ) ;
1012+
1013+ mockEndTrace . mockClear ( ) ;
1014+
1015+ unmount ( ) ;
1016+
1017+ expect ( mockEndTrace ) . not . toHaveBeenCalled ( ) ;
1018+ } ) ;
1019+
1020+ it ( 'truncates error message to 200 characters' , ( ) => {
1021+ const { getByTestId } = render ( < PriceAdvanced { ...baseProps } /> ) ;
1022+ const advancedChart = getByTestId ( 'mock-advanced-chart' ) ;
1023+
1024+ mockEndTrace . mockClear ( ) ;
1025+
1026+ const longError = 'A' . repeat ( 300 ) ;
1027+
1028+ act ( ( ) => {
1029+ advancedChart . props . onError ?.( longError ) ;
1030+ } ) ;
1031+
1032+ expect ( mockEndTrace ) . toHaveBeenCalledWith (
1033+ expect . objectContaining ( {
1034+ data : expect . objectContaining ( {
1035+ errorMessage : 'A' . repeat ( 200 ) ,
1036+ } ) ,
1037+ } ) ,
1038+ ) ;
1039+ } ) ;
1040+ } ) ;
8331041} ) ;
0 commit comments