Skip to content

Commit dba3b5c

Browse files
committed
test: Revamp for rewrite
1 parent c7be57a commit dba3b5c

File tree

2 files changed

+168
-92
lines changed

2 files changed

+168
-92
lines changed

packages/react/src/profiler.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ function popActivity(activity: number | null): void {
7171
}
7272

7373
// tslint:disable-next-line:no-unsafe-any
74-
(globalTracingIntegration as any).constructor.popActivity(activity, undefined);
74+
(globalTracingIntegration as any).constructor.popActivity(activity);
7575
}
7676

7777
/**
@@ -201,15 +201,14 @@ class Profiler extends React.Component<ProfilerProps> {
201201
*/
202202
function withProfiler<P extends object>(
203203
WrappedComponent: React.ComponentType<P>,
204-
// We do not want to have `updateProps` given in options, it is instead filled through
205-
// the HOC.
204+
// We do not want to have `updateProps` given in options, it is instead filled through the HOC.
206205
options?: Pick<Partial<ProfilerProps>, Exclude<keyof ProfilerProps, 'updateProps'>>,
207206
): React.FC<P> {
208207
const componentDisplayName =
209208
(options && options.name) || WrappedComponent.displayName || WrappedComponent.name || UNKNOWN_COMPONENT;
210209

211210
const Wrapped: React.FC<P> = (props: P) => (
212-
<Profiler name={componentDisplayName} disabled={options && options.disabled} updateProps={props}>
211+
<Profiler {...options} name={componentDisplayName} updateProps={props}>
213212
<WrappedComponent {...props} />
214213
</Profiler>
215214
);

packages/react/test/profiler.test.tsx

Lines changed: 165 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,36 @@
1+
import { SpanContext } from '@sentry/types';
12
import { render } from '@testing-library/react';
23
import { renderHook } from '@testing-library/react-hooks';
34
import * as React from 'react';
45

56
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';
722
const mockPushActivity = jest.fn().mockReturnValue(1);
823
const mockPopActivity = jest.fn();
924
const mockLoggerWarn = jest.fn();
10-
11-
let integrationIsNull = false;
25+
const mockGetActivitySpan = jest.fn().mockReturnValue(TEST_SPAN);
1226

1327
jest.mock('@sentry/utils', () => ({
1428
logger: {
1529
warn: (message: string) => {
1630
mockLoggerWarn(message);
1731
},
1832
},
33+
timestampWithMs: () => TEST_TIMESTAMP,
1934
}));
2035

2136
jest.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

114188
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+
});
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

Comments
 (0)