1+ import { SpanContext } from '@sentry/types' ;
12import { render } from '@testing-library/react' ;
23import { renderHook } from '@testing-library/react-hooks' ;
34import * as React from 'react' ;
45
56import { UNKNOWN_COMPONENT , useProfiler , withProfiler } from '../src/profiler' ;
6-
7+ /*(
8+ for key in SENTRY_FEATURES:
9+ SENTRY_FEATURES[key] = True
10+
11+ SENTRY_APM_SAMPLING = 1
12+ )*/
13+ const TEST_SPAN_ID = '518999beeceb49af' ;
14+
15+ const mockSpanFinish = jest . fn ( ) ;
16+ const mockStartChild = jest . fn ( ( spanArgs : SpanContext ) => ( { ...spanArgs , finish : mockSpanFinish } ) ) ;
17+ const TEST_SPAN = {
18+ spanId : TEST_SPAN_ID ,
19+ startChild : mockStartChild ,
20+ } ;
21+ const TEST_TIMESTAMP = '123456' ;
722const mockPushActivity = jest . fn ( ) . mockReturnValue ( 1 ) ;
823const mockPopActivity = jest . fn ( ) ;
924const mockLoggerWarn = jest . fn ( ) ;
10-
11- let integrationIsNull = false ;
25+ const mockGetActivitySpan = jest . fn ( ) . mockReturnValue ( TEST_SPAN ) ;
1226
1327jest . mock ( '@sentry/utils' , ( ) => ( {
1428 logger : {
1529 warn : ( message : string ) => {
1630 mockLoggerWarn ( message ) ;
1731 } ,
1832 } ,
33+ timestampWithMs : ( ) => TEST_TIMESTAMP ,
1934} ) ) ;
2035
2136jest . mock ( '@sentry/browser' , ( ) => ( {
@@ -29,26 +44,23 @@ jest.mock('@sentry/browser', () => ({
2944 public setupOnce : ( ) => void = jest . fn ( ) ;
3045 public static pushActivity : ( ) => void = mockPushActivity ;
3146 public static popActivity : ( ) => void = mockPopActivity ;
47+ public static getActivitySpan : ( ) => void = mockGetActivitySpan ;
3248 }
33-
34- if ( ! integrationIsNull ) {
35- return new MockIntegration ( 'test' ) ;
36- }
37-
38- return null ;
49+ return new MockIntegration ( 'test' ) ;
3950 } ,
4051 } ) ,
4152} ) ) ;
4253
43- describe ( 'withProfiler' , ( ) => {
44- beforeEach ( ( ) => {
45- jest . useFakeTimers ( ) ;
46- mockPushActivity . mockClear ( ) ;
47- mockPopActivity . mockClear ( ) ;
48- mockLoggerWarn . mockClear ( ) ;
49- integrationIsNull = false ;
50- } ) ;
54+ beforeEach ( ( ) => {
55+ mockPushActivity . mockClear ( ) ;
56+ mockPopActivity . mockClear ( ) ;
57+ mockLoggerWarn . mockClear ( ) ;
58+ mockGetActivitySpan . mockClear ( ) ;
59+ mockStartChild . mockClear ( ) ;
60+ mockSpanFinish . mockClear ( ) ;
61+ } ) ;
5162
63+ describe ( 'withProfiler' , ( ) => {
5264 it ( 'sets displayName properly' , ( ) => {
5365 const TestComponent = ( ) => < h1 > Hello World</ h1 > ;
5466
@@ -59,7 +71,7 @@ describe('withProfiler', () => {
5971 it ( 'sets a custom displayName' , ( ) => {
6072 const TestComponent = ( ) => < h1 > Hello World</ h1 > ;
6173
62- const ProfiledComponent = withProfiler ( TestComponent , 'BestComponent' ) ;
74+ const ProfiledComponent = withProfiler ( TestComponent , { name : 'BestComponent' } ) ;
6375 expect ( ProfiledComponent . displayName ) . toBe ( 'profiler(BestComponent)' ) ;
6476 } ) ;
6577
@@ -68,95 +80,160 @@ describe('withProfiler', () => {
6880 expect ( ProfiledComponent . displayName ) . toBe ( `profiler(${ UNKNOWN_COMPONENT } )` ) ;
6981 } ) ;
7082
71- it ( 'popActivity() is called when unmounted' , ( ) => {
72- const ProfiledComponent = withProfiler ( ( ) => < h1 > Hello World</ h1 > ) ;
73-
74- expect ( mockPopActivity ) . toHaveBeenCalledTimes ( 0 ) ;
75- const profiler = render ( < ProfiledComponent /> ) ;
76- profiler . unmount ( ) ;
77-
78- jest . runAllTimers ( ) ;
83+ describe ( 'mount span' , ( ) => {
84+ it ( 'does not get created if Profiler is disabled' , ( ) => {
85+ const ProfiledComponent = withProfiler ( ( ) => < h1 > Testing</ h1 > , { disabled : true } ) ;
86+ expect ( mockPushActivity ) . toHaveBeenCalledTimes ( 0 ) ;
87+ render ( < ProfiledComponent /> ) ;
88+ expect ( mockPushActivity ) . toHaveBeenCalledTimes ( 0 ) ;
89+ } ) ;
7990
80- expect ( mockPopActivity ) . toHaveBeenCalledTimes ( 1 ) ;
81- expect ( mockPopActivity ) . toHaveBeenLastCalledWith ( 1 ) ;
91+ it ( 'is created when a component is mounted' , ( ) => {
92+ const ProfiledComponent = withProfiler ( ( ) => < h1 > Testing</ h1 > ) ;
93+
94+ expect ( mockPushActivity ) . toHaveBeenCalledTimes ( 0 ) ;
95+ expect ( mockGetActivitySpan ) . toHaveBeenCalledTimes ( 0 ) ;
96+ expect ( mockPopActivity ) . toHaveBeenCalledTimes ( 0 ) ;
97+
98+ render ( < ProfiledComponent /> ) ;
99+
100+ expect ( mockPushActivity ) . toHaveBeenCalledTimes ( 1 ) ;
101+ expect ( mockPushActivity ) . toHaveBeenLastCalledWith (
102+ UNKNOWN_COMPONENT ,
103+ {
104+ description : `<${ UNKNOWN_COMPONENT } >` ,
105+ op : 'react.mount' ,
106+ } ,
107+ undefined ,
108+ ) ;
109+ expect ( mockGetActivitySpan ) . toHaveBeenCalledTimes ( 1 ) ;
110+ expect ( mockGetActivitySpan ) . toHaveBeenLastCalledWith ( 1 ) ;
111+
112+ expect ( mockPopActivity ) . toHaveBeenCalledTimes ( 1 ) ;
113+ expect ( mockPopActivity ) . toHaveBeenLastCalledWith ( 1 ) ;
114+ } ) ;
82115 } ) ;
83116
84- it ( 'pushActivity() is called when mounted' , ( ) => {
85- const ProfiledComponent = withProfiler ( ( ) => < h1 > Testing</ h1 > ) ;
117+ describe ( 'render span' , ( ) => {
118+ it ( 'does not get created by default' , ( ) => {
119+ const ProfiledComponent = withProfiler ( ( ) => < h1 > Testing</ h1 > ) ;
120+ expect ( mockStartChild ) . toHaveBeenCalledTimes ( 0 ) ;
121+ render ( < ProfiledComponent /> ) ;
122+ expect ( mockStartChild ) . toHaveBeenCalledTimes ( 0 ) ;
123+ } ) ;
86124
87- expect ( mockPushActivity ) . toHaveBeenCalledTimes ( 0 ) ;
88- render ( < ProfiledComponent /> ) ;
89- expect ( mockPushActivity ) . toHaveBeenCalledTimes ( 1 ) ;
90- expect ( mockPushActivity ) . toHaveBeenLastCalledWith ( UNKNOWN_COMPONENT , {
91- description : `<${ UNKNOWN_COMPONENT } >` ,
92- op : 'react' ,
125+ it ( 'is created when given hasRenderSpan option' , ( ) => {
126+ const ProfiledComponent = withProfiler ( ( ) => < h1 > Testing</ h1 > , { hasRenderSpan : true } ) ;
127+ expect ( mockStartChild ) . toHaveBeenCalledTimes ( 0 ) ;
128+ const component = render ( < ProfiledComponent /> ) ;
129+ expect ( mockStartChild ) . toHaveBeenCalledTimes ( 1 ) ;
130+ expect ( mockStartChild ) . toHaveBeenLastCalledWith ( {
131+ description : `<${ UNKNOWN_COMPONENT } >` ,
132+ op : 'react.render' ,
133+ } ) ;
134+
135+ expect ( mockSpanFinish ) . toHaveBeenCalledTimes ( 0 ) ;
136+ component . unmount ( ) ;
137+ expect ( mockSpanFinish ) . toHaveBeenCalledTimes ( 1 ) ;
93138 } ) ;
94139 } ) ;
95140
96- it ( 'does not start an activity when integration is disabled' , ( ) => {
97- integrationIsNull = true ;
98- const ProfiledComponent = withProfiler ( ( ) => < h1 > Hello World</ h1 > ) ;
99-
100- expect ( mockPushActivity ) . toHaveBeenCalledTimes ( 0 ) ;
101- expect ( mockLoggerWarn ) . toHaveBeenCalledTimes ( 0 ) ;
102-
103- const profiler = render ( < ProfiledComponent /> ) ;
104- expect ( mockPopActivity ) . toHaveBeenCalledTimes ( 0 ) ;
105- expect ( mockPushActivity ) . toHaveBeenCalledTimes ( 0 ) ;
141+ describe ( 'update span' , ( ) => {
142+ it ( 'is created when component is updated' , ( ) => {
143+ const ProfiledComponent = withProfiler ( ( props : { num : number } ) => < div > { props . num } </ div > ) ;
144+ const { rerender } = render ( < ProfiledComponent num = { 0 } /> ) ;
145+ expect ( mockStartChild ) . toHaveBeenCalledTimes ( 0 ) ;
146+
147+ // Dispatch new props
148+ rerender ( < ProfiledComponent num = { 1 } /> ) ;
149+ expect ( mockStartChild ) . toHaveBeenCalledTimes ( 1 ) ;
150+ expect ( mockStartChild ) . toHaveBeenLastCalledWith ( {
151+ data : { changedProps : [ 'num' ] } ,
152+ description : `<${ UNKNOWN_COMPONENT } >` ,
153+ endTimestamp : TEST_TIMESTAMP ,
154+ op : 'react.update' ,
155+ startTimestamp : TEST_TIMESTAMP ,
156+ } ) ;
157+
158+ // New props yet again
159+ rerender ( < ProfiledComponent num = { 2 } /> ) ;
160+ expect ( mockStartChild ) . toHaveBeenCalledTimes ( 2 ) ;
161+ expect ( mockStartChild ) . toHaveBeenLastCalledWith ( {
162+ data : { changedProps : [ 'num' ] } ,
163+ description : `<${ UNKNOWN_COMPONENT } >` ,
164+ endTimestamp : TEST_TIMESTAMP ,
165+ op : 'react.update' ,
166+ startTimestamp : TEST_TIMESTAMP ,
167+ } ) ;
168+
169+ // Should not create spans if props haven't changed
170+ rerender ( < ProfiledComponent num = { 2 } /> ) ;
171+ expect ( mockStartChild ) . toHaveBeenCalledTimes ( 2 ) ;
172+ } ) ;
106173
107- expect ( mockLoggerWarn ) . toHaveBeenCalledTimes ( 1 ) ;
174+ it ( 'does not get created if hasUpdateSpan is false' , ( ) => {
175+ const ProfiledComponent = withProfiler ( ( props : { num : number } ) => < div > { props . num } </ div > , {
176+ hasUpdateSpan : false ,
177+ } ) ;
178+ const { rerender } = render ( < ProfiledComponent num = { 0 } /> ) ;
179+ expect ( mockStartChild ) . toHaveBeenCalledTimes ( 0 ) ;
108180
109- profiler . unmount ( ) ;
110- expect ( mockPopActivity ) . toHaveBeenCalledTimes ( 0 ) ;
181+ // Dispatch new props
182+ rerender ( < ProfiledComponent num = { 1 } /> ) ;
183+ expect ( mockStartChild ) . toHaveBeenCalledTimes ( 0 ) ;
184+ } ) ;
111185 } ) ;
112186} ) ;
113187
114188describe ( 'useProfiler()' , ( ) => {
115- beforeEach ( ( ) => {
116- jest . useFakeTimers ( ) ;
117- mockPushActivity . mockClear ( ) ;
118- mockPopActivity . mockClear ( ) ;
119- mockLoggerWarn . mockClear ( ) ;
120- integrationIsNull = false ;
121- } ) ;
122-
123- it ( 'popActivity() is called when unmounted' , ( ) => {
124- // tslint:disable-next-line: no-void-expression
125- const profiler = renderHook ( ( ) => useProfiler ( 'Example' ) ) ;
126- expect ( mockPopActivity ) . toHaveBeenCalledTimes ( 0 ) ;
127- profiler . unmount ( ) ;
128-
129- jest . runAllTimers ( ) ;
130-
131- expect ( mockPopActivity ) . toHaveBeenCalled ( ) ;
132- expect ( mockPopActivity ) . toHaveBeenLastCalledWith ( 1 ) ;
133- } ) ;
189+ describe ( 'mount span' , ( ) => {
190+ it ( 'does not get created if Profiler is disabled' , ( ) => {
191+ // tslint:disable-next-line: no-void-expression
192+ renderHook ( ( ) => useProfiler ( 'Example' , { disabled : true } ) ) ;
193+ expect ( mockPushActivity ) . toHaveBeenCalledTimes ( 0 ) ;
194+ } ) ;
134195
135- it ( 'pushActivity() is called when mounted' , ( ) => {
136- expect ( mockPushActivity ) . toHaveBeenCalledTimes ( 0 ) ;
137- // tslint:disable-next-line: no-void-expression
138- const profiler = renderHook ( ( ) => useProfiler ( 'Example' ) ) ;
139- profiler . unmount ( ) ;
140- expect ( mockPushActivity ) . toHaveBeenCalledTimes ( 1 ) ;
141- expect ( mockPushActivity ) . toHaveBeenLastCalledWith ( 'Example' , {
142- description : `<Example>` ,
143- op : 'react' ,
196+ it ( 'is created when a component is mounted' , ( ) => {
197+ // tslint:disable-next-line: no-void-expression
198+ renderHook ( ( ) => useProfiler ( 'Example' ) ) ;
199+
200+ expect ( mockPushActivity ) . toHaveBeenCalledTimes ( 1 ) ;
201+ expect ( mockPushActivity ) . toHaveBeenLastCalledWith (
202+ 'Example' ,
203+ {
204+ description : '<Example>' ,
205+ op : 'react.mount' ,
206+ } ,
207+ undefined ,
208+ ) ;
209+ expect ( mockGetActivitySpan ) . toHaveBeenCalledTimes ( 1 ) ;
210+ expect ( mockGetActivitySpan ) . toHaveBeenLastCalledWith ( 1 ) ;
211+
212+ expect ( mockPopActivity ) . toHaveBeenCalledTimes ( 1 ) ;
213+ expect ( mockPopActivity ) . toHaveBeenLastCalledWith ( 1 ) ;
144214 } ) ;
145215 } ) ;
146216
147- it ( 'does not start an activity when integration is disabled' , ( ) => {
148- integrationIsNull = true ;
149- expect ( mockPushActivity ) . toHaveBeenCalledTimes ( 0 ) ;
150- expect ( mockLoggerWarn ) . toHaveBeenCalledTimes ( 0 ) ;
217+ describe ( 'render span' , ( ) => {
218+ it ( 'does not get created by default' , ( ) => {
219+ // tslint:disable-next-line: no-void-expression
220+ renderHook ( ( ) => useProfiler ( 'Example' ) ) ;
221+ expect ( mockStartChild ) . toHaveBeenCalledTimes ( 0 ) ;
222+ } ) ;
151223
152- // tslint:disable-next-line: no-void-expression
153- const profiler = renderHook ( ( ) => useProfiler ( 'Example' ) ) ;
154- expect ( mockPopActivity ) . toHaveBeenCalledTimes ( 0 ) ;
155- expect ( mockPushActivity ) . toHaveBeenCalledTimes ( 0 ) ;
224+ it ( 'is created when given hasRenderSpan option' , ( ) => {
225+ // tslint:disable-next-line: no-void-expression
226+ const component = renderHook ( ( ) => useProfiler ( 'Example' , { hasRenderSpan : true } ) ) ;
156227
157- expect ( mockLoggerWarn ) . toHaveBeenCalledTimes ( 1 ) ;
228+ expect ( mockStartChild ) . toHaveBeenCalledTimes ( 1 ) ;
229+ expect ( mockStartChild ) . toHaveBeenLastCalledWith ( {
230+ description : '<Example>' ,
231+ op : 'react.render' ,
232+ } ) ;
158233
159- profiler . unmount ( ) ;
160- expect ( mockPopActivity ) . toHaveBeenCalledTimes ( 0 ) ;
234+ expect ( mockSpanFinish ) . toHaveBeenCalledTimes ( 0 ) ;
235+ component . unmount ( ) ;
236+ expect ( mockSpanFinish ) . toHaveBeenCalledTimes ( 1 ) ;
237+ } ) ;
161238 } ) ;
162239} ) ;
0 commit comments