1
+ import { SpanContext } from '@sentry/types' ;
1
2
import { render } from '@testing-library/react' ;
2
3
import { renderHook } from '@testing-library/react-hooks' ;
3
4
import * as React from 'react' ;
4
5
5
6
import { 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' ;
7
22
const mockPushActivity = jest . fn ( ) . mockReturnValue ( 1 ) ;
8
23
const mockPopActivity = jest . fn ( ) ;
9
24
const mockLoggerWarn = jest . fn ( ) ;
10
-
11
- let integrationIsNull = false ;
25
+ const mockGetActivitySpan = jest . fn ( ) . mockReturnValue ( TEST_SPAN ) ;
12
26
13
27
jest . mock ( '@sentry/utils' , ( ) => ( {
14
28
logger : {
15
29
warn : ( message : string ) => {
16
30
mockLoggerWarn ( message ) ;
17
31
} ,
18
32
} ,
33
+ timestampWithMs : ( ) => TEST_TIMESTAMP ,
19
34
} ) ) ;
20
35
21
36
jest . mock ( '@sentry/browser' , ( ) => ( {
@@ -29,26 +44,23 @@ jest.mock('@sentry/browser', () => ({
29
44
public setupOnce : ( ) => void = jest . fn ( ) ;
30
45
public static pushActivity : ( ) => void = mockPushActivity ;
31
46
public static popActivity : ( ) => void = mockPopActivity ;
47
+ public static getActivitySpan : ( ) => void = mockGetActivitySpan ;
32
48
}
33
-
34
- if ( ! integrationIsNull ) {
35
- return new MockIntegration ( 'test' ) ;
36
- }
37
-
38
- return null ;
49
+ return new MockIntegration ( 'test' ) ;
39
50
} ,
40
51
} ) ,
41
52
} ) ) ;
42
53
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
+ } ) ;
51
62
63
+ describe ( 'withProfiler' , ( ) => {
52
64
it ( 'sets displayName properly' , ( ) => {
53
65
const TestComponent = ( ) => < h1 > Hello World</ h1 > ;
54
66
@@ -59,7 +71,7 @@ describe('withProfiler', () => {
59
71
it ( 'sets a custom displayName' , ( ) => {
60
72
const TestComponent = ( ) => < h1 > Hello World</ h1 > ;
61
73
62
- const ProfiledComponent = withProfiler ( TestComponent , 'BestComponent' ) ;
74
+ const ProfiledComponent = withProfiler ( TestComponent , { name : 'BestComponent' } ) ;
63
75
expect ( ProfiledComponent . displayName ) . toBe ( 'profiler(BestComponent)' ) ;
64
76
} ) ;
65
77
@@ -68,95 +80,160 @@ describe('withProfiler', () => {
68
80
expect ( ProfiledComponent . displayName ) . toBe ( `profiler(${ UNKNOWN_COMPONENT } )` ) ;
69
81
} ) ;
70
82
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
+ } ) ;
79
90
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
+ } ) ;
82
115
} ) ;
83
116
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
+ } ) ;
86
124
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 ) ;
93
138
} ) ;
94
139
} ) ;
95
140
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
+ } ) ;
106
173
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 ) ;
108
180
109
- profiler . unmount ( ) ;
110
- expect ( mockPopActivity ) . toHaveBeenCalledTimes ( 0 ) ;
181
+ // Dispatch new props
182
+ rerender ( < ProfiledComponent num = { 1 } /> ) ;
183
+ expect ( mockStartChild ) . toHaveBeenCalledTimes ( 0 ) ;
184
+ } ) ;
111
185
} ) ;
112
186
} ) ;
113
187
114
188
describe ( '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
+ } ) ;
134
195
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 ) ;
144
214
} ) ;
145
215
} ) ;
146
216
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
+ } ) ;
151
223
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 } ) ) ;
156
227
157
- expect ( mockLoggerWarn ) . toHaveBeenCalledTimes ( 1 ) ;
228
+ expect ( mockStartChild ) . toHaveBeenCalledTimes ( 1 ) ;
229
+ expect ( mockStartChild ) . toHaveBeenLastCalledWith ( {
230
+ description : '<Example>' ,
231
+ op : 'react.render' ,
232
+ } ) ;
158
233
159
- profiler . unmount ( ) ;
160
- expect ( mockPopActivity ) . toHaveBeenCalledTimes ( 0 ) ;
234
+ expect ( mockSpanFinish ) . toHaveBeenCalledTimes ( 0 ) ;
235
+ component . unmount ( ) ;
236
+ expect ( mockSpanFinish ) . toHaveBeenCalledTimes ( 1 ) ;
237
+ } ) ;
161
238
} ) ;
162
239
} ) ;
0 commit comments