Skip to content

Commit 3410160

Browse files
authored
Signals: always include headers object (#1192)
1 parent c95baab commit 3410160

File tree

3 files changed

+55
-21
lines changed

3 files changed

+55
-21
lines changed

.changeset/dull-monkeys-matter.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@segment/analytics-signals': patch
3+
---
4+
5+
Always include headers in network interceptor, even if empty

packages/signals/signals/src/core/signal-generators/network-gen/__tests__/network-interceptor.test.ts

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { NetworkInterceptor } from '../network-interceptor'
1+
import {
2+
NetworkInterceptor,
3+
NetworkRequestHandler,
4+
NetworkResponseHandler,
5+
} from '../network-interceptor'
26
import { Response } from 'node-fetch'
37
import { EventEmitter } from 'events'
48

@@ -9,10 +13,11 @@ describe(NetworkInterceptor, () => {
913
interceptor.cleanup()
1014
})
1115

16+
const mockRequestHandler: jest.MockedFn<NetworkRequestHandler> = jest.fn()
17+
const mockResponseHandler: jest.MockedFn<NetworkResponseHandler> = jest.fn()
18+
1219
it('should intercept fetch requests and responses', async () => {
1320
interceptor = new NetworkInterceptor()
14-
const mockRequestHandler = jest.fn()
15-
const mockResponseHandler = jest.fn()
1621
const mockResponse = new Response(JSON.stringify({ data: 'test' }), {
1722
headers: { 'Content-Type': 'application/json' },
1823
})
@@ -51,27 +56,36 @@ describe(NetworkInterceptor, () => {
5156

5257
// Very primitive mock for XMLHttpRequest -- better tests are at the integration level
5358
it('should intercept XHR requests and responses', async () => {
54-
const mockRequestHandler = jest.fn()
55-
const mockResponseHandler = jest.fn()
59+
interface XMLHttpRequestMock {
60+
open: XMLHttpRequest['open']
61+
send: XMLHttpRequest['send']
62+
setRequestHeader: XMLHttpRequest['setRequestHeader']
63+
getAllResponseHeaders: XMLHttpRequest['getAllResponseHeaders']
64+
addEventListener: XMLHttpRequest['addEventListener']
65+
onreadystatechange: XMLHttpRequest['onreadystatechange']
66+
}
5667

57-
class MockXMLHttpRequest {
68+
class MockXMLHttpRequest implements XMLHttpRequestMock {
5869
UNSENT = 0
5970
OPENED = 1
6071
HEADERS_RECEIVED = 2
6172
LOADING = 3
6273
DONE = 4
6374

64-
private _emitter = new EventEmitter()
75+
private _emitter: EventEmitter
6576
public readyState = 0
6677
public status = 0
6778
public responseText = ''
68-
public onreadystatechange: (() => void) | null = null
6979
public responseURL = ''
70-
public _responseMethod = ''
71-
public _responseHeaders = ''
80+
private _responseHeaders = ''
81+
public onreadystatechange: () => void = () => undefined
82+
constructor() {
83+
this._emitter = new EventEmitter().on('readystatechange', () => {
84+
this.onreadystatechange()
85+
})
86+
}
7287

73-
open(method: string, url: string) {
74-
this._responseMethod = method
88+
open(_method: string, url: string) {
7589
this.responseURL = url
7690
}
7791

@@ -91,17 +105,23 @@ describe(NetworkInterceptor, () => {
91105
this.status = 200
92106
this.responseText = JSON.stringify({ data: 'test' })
93107
this.responseURL = 'http://example.com'
94-
this._responseHeaders = 'Content-Type: application/json'
108+
this._responseHeaders =
109+
[
110+
'content-type: application/json; charset=utf-8',
111+
'cache-control: max-age=3600',
112+
'x-content-type-options: nosniff',
113+
'date: Mon, 18 Nov 2000 12:00:00 GMT',
114+
].join('\r\n') + '\r\n' // trailing CRLF to be realistic
115+
95116
this._emitter.emit('readystatechange')
96-
if (this.onreadystatechange) {
97-
this.onreadystatechange()
98-
}
99117
}, 40)
100118
}
101119

102-
setRequestHeader = jest.fn()
120+
setRequestHeader() {
121+
// no-op
122+
}
103123

104-
addEventListener(event: string, listener: () => void) {
124+
addEventListener(event: string, listener: (ev: any) => void) {
105125
this._emitter.on(event, listener)
106126
}
107127

@@ -121,15 +141,24 @@ describe(NetworkInterceptor, () => {
121141

122142
const xhr = new XMLHttpRequest()
123143
xhr.open('POST', 'http://example.com')
144+
xhr.setRequestHeader('accept', 'application/json')
145+
xhr.setRequestHeader('x-something-else', 'foo')
124146
xhr.send()
125147

126148
await new Promise((resolve) => setTimeout(resolve, 100))
127149

128150
expect(mockRequestHandler).toHaveBeenCalled()
151+
const request = mockRequestHandler.mock.calls[0][0]
152+
expect(request.headers).toBeInstanceOf(Headers)
153+
expect(request.headers!.get('accept')).toBe('application/json')
154+
expect(request.headers!.get('x-something-else')).toBe('foo')
129155
expect(mockResponseHandler).toHaveBeenCalled()
130156
const response = mockResponseHandler.mock.calls[0][0]
131157
expect(response.headers).toBeInstanceOf(Headers)
132-
expect(response.headers.get('content-type')).toBe('application/json')
158+
expect(response.headers.get('content-type')).toBe(
159+
'application/json; charset=utf-8'
160+
)
161+
expect(response.headers.get('date')).toBe('Mon, 18 Nov 2000 12:00:00 GMT')
133162
expect(response.url).toBe('http://example.com')
134163
expect(response.status).toBe(200)
135164
})

packages/signals/signals/src/core/signal-generators/network-gen/network-interceptor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export interface NetworkInterceptorRequest {
2828
method: HTTPMethod
2929
contentType: string | undefined
3030
body: string | undefined
31-
headers: Headers | undefined
31+
headers: Headers
3232
id: string
3333
}
3434

@@ -61,7 +61,7 @@ const createInterceptorRequest = ({
6161
}): NetworkInterceptorRequest => ({
6262
url: url.toString(),
6363
method: method,
64-
headers,
64+
headers: headers ?? new Headers(),
6565
contentType: headers?.get('content-type') ?? undefined,
6666
body: typeof body == 'string' ? body : undefined,
6767
id,

0 commit comments

Comments
 (0)