Skip to content

Commit b4ebafe

Browse files
committed
reorganize and add tests
1 parent 08f7ca1 commit b4ebafe

File tree

1 file changed

+143
-92
lines changed

1 file changed

+143
-92
lines changed

packages/tracing/test/browser/request.test.ts

+143-92
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Hub, makeMain } from '@sentry/core';
33
import * as utils from '@sentry/utils';
44

55
import { Span, spanStatusfromHttpCode, Transaction } from '../../src';
6-
import { fetchCallback, FetchData, instrumentOutgoingRequests, xhrCallback } from '../../src/browser/request';
6+
import { fetchCallback, FetchData, instrumentOutgoingRequests, xhrCallback, XHRData } from '../../src/browser/request';
77
import { addExtensionMethods } from '../../src/hubextensions';
88
import * as tracingUtils from '../../src/utils';
99
import { getDefaultBrowserClientOptions } from '../testutils';
@@ -48,29 +48,9 @@ describe('callbacks', () => {
4848
let hub: Hub;
4949
let transaction: Transaction;
5050
const alwaysCreateSpan = () => true;
51-
const neverCreateSpan = () => false;
51+
const alwaysAttachHeaders = () => true;
5252
const startTimestamp = 1356996072000;
5353
const endTimestamp = 1356996072000;
54-
const fetchHandlerData: FetchData = {
55-
args: ['http://dogs.are.great/', {}],
56-
fetchData: { url: 'http://dogs.are.great/', method: 'GET' },
57-
startTimestamp,
58-
};
59-
const xhrHandlerData = {
60-
xhr: {
61-
__sentry_xhr__: {
62-
method: 'GET',
63-
url: 'http://dogs.are.great/',
64-
status_code: 200,
65-
data: {},
66-
},
67-
__sentry_xhr_span_id__: '1231201211212012',
68-
// eslint-disable-next-line @typescript-eslint/unbound-method
69-
// setRequestHeader: XMLHttpRequest.prototype.setRequestHeader,
70-
setRequestHeader,
71-
},
72-
startTimestamp,
73-
};
7454

7555
beforeAll(() => {
7656
const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 });
@@ -83,52 +63,91 @@ describe('callbacks', () => {
8363
hub.configureScope(scope => scope.setSpan(transaction));
8464
});
8565

86-
describe('fetchCallback()', () => {
87-
it('does not create span if shouldCreateSpan returns false', () => {
88-
const spans = {};
66+
afterEach(() => {
67+
jest.clearAllMocks();
68+
});
8969

90-
fetchCallback(fetchHandlerData, neverCreateSpan, spans);
70+
describe('fetchCallback()', () => {
71+
let fetchHandlerData: FetchData;
9172

92-
expect(spans).toEqual({});
73+
const fetchSpan = {
74+
data: {
75+
method: 'GET',
76+
url: 'http://dogs.are.great/',
77+
type: 'fetch',
78+
},
79+
description: 'GET http://dogs.are.great/',
80+
op: 'http.client',
81+
parentSpanId: expect.any(String),
82+
spanId: expect.any(String),
83+
startTimestamp: expect.any(Number),
84+
traceId: expect.any(String),
85+
};
86+
87+
beforeEach(() => {
88+
fetchHandlerData = {
89+
args: ['http://dogs.are.great/', {}],
90+
fetchData: { url: 'http://dogs.are.great/', method: 'GET' },
91+
startTimestamp,
92+
};
9393
});
9494

95-
it('does not create span if there is no fetch data in handler data', () => {
96-
const noFetchData = { args: fetchHandlerData.args, startTimestamp: fetchHandlerData.startTimestamp };
97-
const spans = {};
98-
99-
fetchCallback(noFetchData, alwaysCreateSpan, spans);
100-
expect(spans).toEqual({});
101-
});
95+
it.each([
96+
// each case is [shouldCreateSpanReturnValue, shouldAttachHeadersReturnValue, expectedSpan, expectedHeaderKeys]
97+
[true, true, expect.objectContaining(fetchSpan), ['sentry-trace', 'baggage']],
98+
[true, false, expect.objectContaining(fetchSpan), []],
99+
// If there's no span then there's no parent span id to stick into a header, so no headers, even if there's a
100+
// `tracingOrigins` match
101+
[false, true, undefined, []],
102+
[false, false, undefined, []],
103+
])(
104+
'span creation/header attachment interaction - shouldCreateSpan: %s, shouldAttachHeaders: %s',
105+
(shouldCreateSpanReturnValue, shouldAttachHeadersReturnValue, expectedSpan, expectedHeaderKeys) => {
106+
fetchCallback(
107+
fetchHandlerData,
108+
() => shouldCreateSpanReturnValue,
109+
() => shouldAttachHeadersReturnValue,
110+
{},
111+
);
112+
113+
// spans[0] is the transaction itself
114+
const newSpan = transaction.spanRecorder?.spans[1] as Span;
115+
expect(newSpan).toEqual(expectedSpan);
116+
117+
const headers = (fetchHandlerData.args[1].headers as Record<string, string>) || {};
118+
expect(Object.keys(headers)).toEqual(expectedHeaderKeys);
119+
},
120+
);
102121

103-
it('does not add fetch request spans if tracing is disabled', () => {
104-
hasTracingEnabled.mockReturnValueOnce(false);
122+
it('adds neither fetch request spans nor fetch request headers if there is no fetch data in handler data', () => {
123+
delete fetchHandlerData.fetchData;
105124
const spans = {};
106125

107-
fetchCallback(fetchHandlerData, alwaysCreateSpan, spans);
126+
fetchCallback(fetchHandlerData, alwaysCreateSpan, alwaysAttachHeaders, spans);
127+
108128
expect(spans).toEqual({});
129+
130+
const headers = (fetchHandlerData.args[1].headers as Record<string, string>) || {};
131+
expect(Object.keys(headers)).toEqual([]);
109132
});
110133

111-
it('does not add fetch request headers if tracing is disabled', () => {
134+
it('adds neither fetch request spans nor fetch request headers if tracing is disabled', () => {
112135
hasTracingEnabled.mockReturnValueOnce(false);
136+
const spans = {};
113137

114-
// make a local copy so the global one doesn't get mutated
115-
const handlerData: FetchData = {
116-
args: ['http://dogs.are.great/', {}],
117-
fetchData: { url: 'http://dogs.are.great/', method: 'GET' },
118-
startTimestamp: 1353501072000,
119-
};
138+
fetchCallback(fetchHandlerData, alwaysCreateSpan, alwaysAttachHeaders, spans);
120139

121-
fetchCallback(handlerData, alwaysCreateSpan, {});
140+
expect(spans).toEqual({});
122141

123-
const headers = (handlerData.args[1].headers as Record<string, string>) || {};
124-
expect(headers['sentry-trace']).not.toBeDefined();
142+
const headers = (fetchHandlerData.args[1].headers as Record<string, string>) || {};
143+
expect(Object.keys(headers)).toEqual([]);
125144
});
126145

127146
it('creates and finishes fetch span on active transaction', () => {
128147
const spans = {};
129148

130149
// triggered by request being sent
131-
fetchCallback(fetchHandlerData, alwaysCreateSpan, spans);
150+
fetchCallback(fetchHandlerData, alwaysCreateSpan, alwaysAttachHeaders, spans);
132151

133152
const newSpan = transaction.spanRecorder?.spans[1] as Span;
134153

@@ -149,7 +168,7 @@ describe('callbacks', () => {
149168
};
150169

151170
// triggered by response coming back
152-
fetchCallback(postRequestFetchHandlerData, alwaysCreateSpan, spans);
171+
fetchCallback(postRequestFetchHandlerData, alwaysCreateSpan, alwaysAttachHeaders, spans);
153172

154173
expect(newSpan.endTimestamp).toBeDefined();
155174
});
@@ -158,7 +177,7 @@ describe('callbacks', () => {
158177
const spans: Record<string, Span> = {};
159178

160179
// triggered by request being sent
161-
fetchCallback(fetchHandlerData, alwaysCreateSpan, spans);
180+
fetchCallback(fetchHandlerData, alwaysCreateSpan, alwaysAttachHeaders, spans);
162181

163182
const newSpan = transaction.spanRecorder?.spans[1] as Span;
164183

@@ -171,7 +190,7 @@ describe('callbacks', () => {
171190
};
172191

173192
// triggered by response coming back
174-
fetchCallback(postRequestFetchHandlerData, alwaysCreateSpan, spans);
193+
fetchCallback(postRequestFetchHandlerData, alwaysCreateSpan, alwaysAttachHeaders, spans);
175194

176195
expect(newSpan.status).toBe(spanStatusfromHttpCode(404));
177196
});
@@ -186,7 +205,7 @@ describe('callbacks', () => {
186205
};
187206

188207
// in that case, the response coming back will be ignored
189-
fetchCallback(postRequestFetchHandlerData, alwaysCreateSpan, {});
208+
fetchCallback(postRequestFetchHandlerData, alwaysCreateSpan, alwaysAttachHeaders, {});
190209

191210
const newSpan = transaction.spanRecorder?.spans[1];
192211

@@ -199,57 +218,89 @@ describe('callbacks', () => {
199218

200219
expect(transaction.metadata.propagations).toBe(0);
201220

202-
fetchCallback(firstReqData, alwaysCreateSpan, {});
221+
fetchCallback(firstReqData, alwaysCreateSpan, alwaysAttachHeaders, {});
203222
expect(transaction.metadata.propagations).toBe(1);
204223

205-
fetchCallback(secondReqData, alwaysCreateSpan, {});
224+
fetchCallback(secondReqData, alwaysCreateSpan, alwaysAttachHeaders, {});
206225
expect(transaction.metadata.propagations).toBe(2);
207226
});
208-
209-
it('adds sentry-trace header to fetch requests', () => {
210-
// TODO
211-
});
212227
});
213228

214229
describe('xhrCallback()', () => {
215-
it('does not create span if shouldCreateSpan returns false', () => {
216-
const spans = {};
217-
218-
xhrCallback(xhrHandlerData, neverCreateSpan, spans);
230+
let xhrHandlerData: XHRData;
219231

220-
expect(spans).toEqual({});
232+
const xhrSpan = {
233+
data: {
234+
method: 'GET',
235+
url: 'http://dogs.are.great/',
236+
type: 'xhr',
237+
},
238+
description: 'GET http://dogs.are.great/',
239+
op: 'http.client',
240+
parentSpanId: expect.any(String),
241+
spanId: expect.any(String),
242+
startTimestamp: expect.any(Number),
243+
traceId: expect.any(String),
244+
};
245+
246+
beforeEach(() => {
247+
xhrHandlerData = {
248+
xhr: {
249+
__sentry_xhr__: {
250+
method: 'GET',
251+
url: 'http://dogs.are.great/',
252+
status_code: 200,
253+
data: {},
254+
},
255+
__sentry_xhr_span_id__: '1231201211212012',
256+
setRequestHeader,
257+
},
258+
startTimestamp,
259+
};
221260
});
222261

223-
it('does not add xhr request spans if tracing is disabled', () => {
224-
hasTracingEnabled.mockReturnValueOnce(false);
225-
const spans = {};
226-
227-
xhrCallback(xhrHandlerData, alwaysCreateSpan, spans);
228-
expect(spans).toEqual({});
229-
});
262+
it.each([
263+
// each case is [shouldCreateSpanReturnValue, shouldAttachHeadersReturnValue, expectedSpan, expectedHeaderKeys]
264+
[true, true, expect.objectContaining(xhrSpan), ['sentry-trace', 'baggage']],
265+
[true, false, expect.objectContaining(xhrSpan), []],
266+
// If there's no span then there's no parent span id to stick into a header, so no headers, even if there's a
267+
// `tracingOrigins` match
268+
[false, true, undefined, []],
269+
[false, false, undefined, []],
270+
])(
271+
'span creation/header attachment interaction - shouldCreateSpan: %s, shouldAttachHeaders: %s',
272+
(shouldCreateSpanReturnValue, shouldAttachHeadersReturnValue, expectedSpan, expectedHeaderKeys) => {
273+
xhrCallback(
274+
xhrHandlerData,
275+
() => shouldCreateSpanReturnValue,
276+
() => shouldAttachHeadersReturnValue,
277+
{},
278+
);
279+
280+
// spans[0] is the transaction itself
281+
const newSpan = transaction.spanRecorder?.spans[1] as Span;
282+
expect(newSpan).toEqual(expectedSpan);
283+
284+
const headerKeys = setRequestHeader.mock.calls.map(header => header[0]);
285+
expect(headerKeys).toEqual(expectedHeaderKeys);
286+
},
287+
);
230288

231-
it('does not add xhr request headers if tracing is disabled', () => {
289+
it('adds neither xhr request spans nor xhr request headers if tracing is disabled', () => {
232290
hasTracingEnabled.mockReturnValueOnce(false);
291+
const spans = {};
233292

234-
xhrCallback(xhrHandlerData, alwaysCreateSpan, {});
293+
xhrCallback(xhrHandlerData, alwaysCreateSpan, alwaysAttachHeaders, spans);
235294

295+
expect(spans).toEqual({});
236296
expect(setRequestHeader).not.toHaveBeenCalled();
237297
});
238298

239-
it('adds sentry-trace header to XHR requests', () => {
240-
xhrCallback(xhrHandlerData, alwaysCreateSpan, {});
241-
242-
expect(setRequestHeader).toHaveBeenCalledWith(
243-
'sentry-trace',
244-
expect.stringMatching(tracingUtils.TRACEPARENT_REGEXP),
245-
);
246-
});
247-
248299
it('creates and finishes XHR span on active transaction', () => {
249300
const spans = {};
250301

251302
// triggered by request being sent
252-
xhrCallback(xhrHandlerData, alwaysCreateSpan, spans);
303+
xhrCallback(xhrHandlerData, alwaysCreateSpan, alwaysAttachHeaders, spans);
253304

254305
const newSpan = transaction.spanRecorder?.spans[1] as Span;
255306

@@ -261,16 +312,16 @@ describe('callbacks', () => {
261312
});
262313
expect(newSpan.description).toBe('GET http://dogs.are.great/');
263314
expect(newSpan.op).toBe('http.client');
264-
expect(xhrHandlerData.xhr.__sentry_xhr_span_id__).toBeDefined();
265-
expect(xhrHandlerData.xhr.__sentry_xhr_span_id__).toEqual(newSpan?.spanId);
315+
expect(xhrHandlerData.xhr?.__sentry_xhr_span_id__).toBeDefined();
316+
expect(xhrHandlerData.xhr?.__sentry_xhr_span_id__).toEqual(newSpan?.spanId);
266317

267318
const postRequestXHRHandlerData = {
268319
...xhrHandlerData,
269320
endTimestamp,
270321
};
271322

272323
// triggered by response coming back
273-
xhrCallback(postRequestXHRHandlerData, alwaysCreateSpan, spans);
324+
xhrCallback(postRequestXHRHandlerData, alwaysCreateSpan, alwaysAttachHeaders, spans);
274325

275326
expect(newSpan.endTimestamp).toBeDefined();
276327
});
@@ -279,7 +330,7 @@ describe('callbacks', () => {
279330
const spans = {};
280331

281332
// triggered by request being sent
282-
xhrCallback(xhrHandlerData, alwaysCreateSpan, spans);
333+
xhrCallback(xhrHandlerData, alwaysCreateSpan, alwaysAttachHeaders, spans);
283334

284335
const newSpan = transaction.spanRecorder?.spans[1] as Span;
285336

@@ -289,10 +340,10 @@ describe('callbacks', () => {
289340
...xhrHandlerData,
290341
endTimestamp,
291342
};
292-
postRequestXHRHandlerData.xhr.__sentry_xhr__.status_code = 404;
343+
postRequestXHRHandlerData.xhr!.__sentry_xhr__!.status_code = 404;
293344

294345
// triggered by response coming back
295-
xhrCallback(postRequestXHRHandlerData, alwaysCreateSpan, spans);
346+
xhrCallback(postRequestXHRHandlerData, alwaysCreateSpan, alwaysAttachHeaders, spans);
296347

297348
expect(newSpan.status).toBe(spanStatusfromHttpCode(404));
298349
});
@@ -303,15 +354,15 @@ describe('callbacks', () => {
303354
const postRequestXHRHandlerData = {
304355
...{
305356
xhr: {
306-
__sentry_xhr__: xhrHandlerData.xhr.__sentry_xhr__,
357+
__sentry_xhr__: xhrHandlerData.xhr?.__sentry_xhr__,
307358
},
308359
},
309360
startTimestamp,
310361
endTimestamp,
311362
};
312363

313364
// in that case, the response coming back will be ignored
314-
xhrCallback(postRequestXHRHandlerData, alwaysCreateSpan, {});
365+
xhrCallback(postRequestXHRHandlerData, alwaysCreateSpan, alwaysAttachHeaders, {});
315366

316367
const newSpan = transaction.spanRecorder?.spans[1];
317368

@@ -324,10 +375,10 @@ describe('callbacks', () => {
324375

325376
expect(transaction.metadata.propagations).toBe(0);
326377

327-
xhrCallback(firstReqData, alwaysCreateSpan, {});
378+
xhrCallback(firstReqData, alwaysCreateSpan, alwaysAttachHeaders, {});
328379
expect(transaction.metadata.propagations).toBe(1);
329380

330-
xhrCallback(secondReqData, alwaysCreateSpan, {});
381+
xhrCallback(secondReqData, alwaysCreateSpan, alwaysAttachHeaders, {});
331382
expect(transaction.metadata.propagations).toBe(2);
332383
});
333384
});

0 commit comments

Comments
 (0)