diff --git a/src/mockServiceWorker.js b/src/mockServiceWorker.js index 364d44023..7d768456c 100644 --- a/src/mockServiceWorker.js +++ b/src/mockServiceWorker.js @@ -199,7 +199,19 @@ async function getResponse(event, client, requestId) { // Remove the "accept" header value that marked this request as passthrough. // This prevents request alteration and also keeps it compliant with the // user-defined CORS policies. - headers.delete('accept', 'msw/passthrough') + const acceptHeader = headers.get('accept') + if (acceptHeader) { + const values = acceptHeader.split(',').map((value) => value.trim()) + const filteredValues = values.filter( + (value) => value !== 'msw/passthrough', + ) + + if (filteredValues.length > 0) { + headers.set('accept', filteredValues.join(', ')) + } else { + headers.delete('accept') + } + } return fetch(requestClone, { headers }) } diff --git a/test/browser/msw-api/setup-worker/worker-passthrough-header.mocks.ts b/test/browser/msw-api/setup-worker/worker-passthrough-header.mocks.ts new file mode 100644 index 000000000..e7303fb79 --- /dev/null +++ b/test/browser/msw-api/setup-worker/worker-passthrough-header.mocks.ts @@ -0,0 +1,10 @@ +import { http, passthrough } from 'msw' +import { setupWorker } from 'msw/browser' + +const worker = setupWorker( + http.get('*/resource', function originalResolver() { + return passthrough() + }), +) + +worker.start() diff --git a/test/browser/msw-api/setup-worker/worker-passthrough-header.test.ts b/test/browser/msw-api/setup-worker/worker-passthrough-header.test.ts new file mode 100644 index 000000000..783e8c571 --- /dev/null +++ b/test/browser/msw-api/setup-worker/worker-passthrough-header.test.ts @@ -0,0 +1,57 @@ +import { HttpServer } from '@open-draft/test-server/lib/http' +import { test, expect } from '../../playwright.extend' + +const httpServer = new HttpServer((app) => { + app.get('/resource', (req, res) => { + res.set(req.headers) + res.send('hello world') + }) +}) + +test.beforeAll(async () => { + await httpServer.listen() +}) + +test.afterAll(async () => { + await httpServer.close() +}) + +test('removes the internal passthrough request header', async ({ + loadExample, + fetch, +}) => { + await loadExample(require.resolve('./worker-passthrough-header.mocks.ts')) + + const response = await fetch(httpServer.http.url('/resource'), { + headers: { 'x-custom-header': 'yes' }, + }) + const headers = await response.allHeaders() + + expect(headers).toMatchObject({ + // The default header value. + accept: '*/*', + 'x-custom-header': 'yes', + }) + await expect(response.text()).resolves.toBe('hello world') +}) + +test('preserves existing "accept" header values when removing the internal passthrough request header', async ({ + loadExample, + fetch, +}) => { + await loadExample(require.resolve('./worker-passthrough-header.mocks.ts')) + + const response = await fetch(httpServer.http.url('/resource'), { + headers: { + accept: 'text/plain, application/json', + 'x-custom-header': 'yes', + }, + }) + const headers = await response.allHeaders() + + expect(headers).toMatchObject({ + accept: 'text/plain, application/json', + 'x-custom-header': 'yes', + }) + await expect(response.text()).resolves.toBe('hello world') +})