Skip to content

Commit c485989

Browse files
authoredMar 17, 2024··
fix(setupWorker): emit the correct life-cycle events for bypassed requests (#2094)
1 parent 2c8570b commit c485989

File tree

7 files changed

+108
-23
lines changed

7 files changed

+108
-23
lines changed
 

‎src/browser/setupWorker/start/createRequestListener.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export const createRequestListener = (
4848
context.emitter,
4949
{
5050
onPassthroughResponse() {
51-
messageChannel.postMessage('NOT_FOUND')
51+
messageChannel.postMessage('PASSTHROUGH')
5252
},
5353
async onMockedResponse(response, { handler, parsedResult }) {
5454
// Clone the mocked response so its body could be read

‎src/browser/setupWorker/start/createResponseListener.ts

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ export function createResponseListener(context: SetupWorkerInternalContext) {
2121
const request = context.requests.get(requestId)!
2222
context.requests.delete(requestId)
2323

24+
console.log('RESPONSE LISTENER', responseJson, context.requests)
Has a conversation. Original line has a conversation.
25+
2426
/**
2527
* CORS requests with `mode: "no-cors"` result in "opaque" responses.
2628
* That kind of responses cannot be manipulated in JavaScript due

‎src/browser/setupWorker/start/utils/createMessageChannel.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ interface WorkerChannelEventsMap {
1616
data: StringifiedResponse,
1717
transfer?: [ReadableStream<Uint8Array>],
1818
]
19-
NOT_FOUND: []
19+
PASSTHROUGH: []
2020
}
2121

2222
export class WorkerChannel {

‎src/core/utils/handleRequest.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export async function handleRequest(
5252
): Promise<Response | undefined> {
5353
emitter.emit('request:start', { request, requestId })
5454

55-
// Perform bypassed requests (i.e. issued via "ctx.fetch") as-is.
55+
// Perform bypassed requests (i.e. wrapped in "bypass()") as-is.
5656
if (request.headers.get('x-msw-intention') === 'bypass') {
5757
emitter.emit('request:end', { request, requestId })
5858
handleRequestOptions?.onPassthroughResponse?.(request)

‎src/mockServiceWorker.js

+1-8
Original file line numberDiff line numberDiff line change
@@ -206,13 +206,6 @@ async function getResponse(event, client, requestId) {
206206
return passthrough()
207207
}
208208

209-
// Bypass requests with the explicit bypass header.
210-
// Such requests can be issued by "ctx.fetch()".
211-
const mswIntention = request.headers.get('x-msw-intention')
212-
if (['bypass', 'passthrough'].includes(mswIntention)) {
213-
return passthrough()
214-
}
215-
216209
// Notify the client that a request has been intercepted.
217210
const requestBuffer = await request.arrayBuffer()
218211
const clientMessage = await sendToClient(
@@ -244,7 +237,7 @@ async function getResponse(event, client, requestId) {
244237
return respondWithMock(clientMessage.data)
245238
}
246239

247-
case 'MOCK_NOT_FOUND': {
240+
case 'PASSTHROUGH': {
248241
return passthrough()
249242
}
250243
}

‎test/browser/msw-api/setup-worker/life-cycle-events/on.mocks.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { HttpResponse, http, LifeCycleEventsMap } from 'msw'
1+
import {
2+
HttpResponse,
3+
http,
4+
LifeCycleEventsMap,
5+
passthrough,
6+
bypass,
7+
} from 'msw'
28
import { setupWorker } from 'msw/browser'
39

410
const worker = setupWorker(
@@ -8,6 +14,12 @@ const worker = setupWorker(
814
http.post('*/no-response', () => {
915
return
1016
}),
17+
http.get('*/passthrough', () => {
18+
return passthrough()
19+
}),
20+
http.get('*/bypass', async ({ request }) => {
21+
return fetch(bypass(request, { method: 'POST' }))
22+
}),
1123
http.get('*/unhandled-exception', () => {
1224
throw new Error('Unhandled resolver error')
1325
}),

‎test/browser/msw-api/setup-worker/life-cycle-events/on.test.ts

+89-11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { SetupWorker } from 'msw/lib/browser'
1+
import type { SetupWorker } from 'msw/browser'
22
import { HttpServer } from '@open-draft/test-server/http'
33
import type { ConsoleMessages } from 'page-with'
44
import { test, expect } from '../../../playwright.extend'
@@ -11,7 +11,20 @@ declare namespace window {
1111

1212
const ON_EXAMPLE = require.resolve('./on.mocks.ts')
1313

14-
let server: HttpServer
14+
const server = new HttpServer((app) => {
15+
app.post('/no-response', (_req, res) => {
16+
res.send('original-response')
17+
})
18+
app.get('/unknown-route', (_req, res) => {
19+
res.send('majestic-unknown')
20+
})
21+
app.get('/passthrough', (_req, res) => {
22+
res.send('passthrough-response')
23+
})
24+
app.post('/bypass', (_req, res) => {
25+
res.send('bypassed-response')
26+
})
27+
})
1528

1629
export function getRequestId(messages: ConsoleMessages) {
1730
const requestStartMessage = messages.get('warning')?.find((message) => {
@@ -20,15 +33,12 @@ export function getRequestId(messages: ConsoleMessages) {
2033
return requestStartMessage?.split(' ')?.[3]
2134
}
2235

23-
test.beforeEach(async ({ createServer }) => {
24-
server = await createServer((app) => {
25-
app.post('/no-response', (req, res) => {
26-
res.send('original-response')
27-
})
28-
app.get('/unknown-route', (req, res) => {
29-
res.send('majestic-unknown')
30-
})
31-
})
36+
test.beforeAll(async () => {
37+
await server.listen()
38+
})
39+
40+
test.afterAll(async () => {
41+
await server.close()
3242
})
3343

3444
test('emits events for a handled request and mocked response', async ({
@@ -111,6 +121,74 @@ test('emits events for an unhandled request', async ({
111121
])
112122
})
113123

124+
test('emits events for a passthrough request', async ({
125+
loadExample,
126+
spyOnConsole,
127+
fetch,
128+
waitFor,
129+
}) => {
130+
const consoleSpy = spyOnConsole()
131+
await loadExample(ON_EXAMPLE)
132+
133+
// Explicit "passthrough()" request must go through the
134+
// same request processing pipeline to contain both
135+
// "request" and "response" in the life-cycle event listener.
136+
const url = server.http.url('/passthrough')
137+
await fetch(url)
138+
const requestId = getRequestId(consoleSpy)
139+
140+
await waitFor(() => {
141+
expect(consoleSpy.get('warning')).toEqual([
142+
`[request:start] GET ${url} ${requestId}`,
143+
`[request:end] GET ${url} ${requestId}`,
144+
`[response:bypass] ${url} passthrough-response GET ${url} ${requestId}`,
145+
])
146+
})
147+
})
148+
149+
test('emits events for a bypassed request', async ({
150+
loadExample,
151+
spyOnConsole,
152+
fetch,
153+
waitFor,
154+
page,
155+
}) => {
156+
const consoleSpy = spyOnConsole()
157+
await loadExample(ON_EXAMPLE)
158+
159+
const pageErrors: Array<Error> = []
160+
page.on('pageerror', (error) => pageErrors.push(error))
161+
162+
const url = server.http.url('/bypass')
163+
await fetch(url)
164+
165+
await waitFor(() => {
166+
// First, must print the events for the original (mocked) request.
167+
expect(consoleSpy.get('warning')).toEqual(
168+
expect.arrayContaining([
169+
expect.stringContaining(`[request:start] GET ${url}`),
170+
expect.stringContaining(`[request:end] GET ${url}`),
171+
expect.stringContaining(
172+
`[response:mocked] ${url} bypassed-response GET ${url}`,
173+
),
174+
]),
175+
)
176+
177+
// Then, must also print events for the bypassed request.
178+
expect(consoleSpy.get('warning')).toEqual(
179+
expect.arrayContaining([
180+
expect.stringContaining(`[request:start] POST ${url}`),
181+
expect.stringContaining(`[request:end] POST ${url}`),
182+
expect.stringContaining(
183+
`[response:bypass] ${url} bypassed-response POST ${url}`,
184+
),
185+
]),
186+
)
187+
188+
expect(pageErrors).toEqual([])
189+
})
190+
})
191+
114192
test('emits unhandled exceptions in the request handler', async ({
115193
loadExample,
116194
spyOnConsole,

0 commit comments

Comments
 (0)
Please sign in to comment.