diff --git a/package.json b/package.json index 101c3ce5..2a2cd155 100644 --- a/package.json +++ b/package.json @@ -137,7 +137,7 @@ "@bundled-es-modules/statuses": "^1.0.1", "@inquirer/confirm": "^3.0.0", "@mswjs/cookies": "^1.1.0", - "@mswjs/interceptors": "^0.28.3", + "@mswjs/interceptors": "^0.29.0", "@open-draft/until": "^2.1.0", "@types/cookie": "^0.6.0", "@types/statuses": "^2.0.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 944767ac..2aa6f5fa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,8 +18,8 @@ dependencies: specifier: ^1.1.0 version: 1.1.0 '@mswjs/interceptors': - specifier: ^0.28.3 - version: 0.28.3 + specifier: ^0.29.0 + version: 0.29.0 '@open-draft/until': specifier: ^2.1.0 version: 2.1.0 @@ -1410,8 +1410,8 @@ packages: engines: {node: '>=18'} dev: false - /@mswjs/interceptors@0.28.3: - resolution: {integrity: sha512-+4px/YLSyG3nL+R6nOKJpK59OvmuSl4CHH8wq8/7oGiZgDJTDOXMAgBrUPrjB5wScv81d/Drk1tP8+/BePBJfQ==} + /@mswjs/interceptors@0.29.0: + resolution: {integrity: sha512-eppU9TxaRS2t5IcR00nuh+36zMHcK09pyhUvWJLO1ae5+U8KL7iatUGKlLUlbxXaq3BvDjlcF0Q8Xhzyosk/xA==} engines: {node: '>=18'} dependencies: '@open-draft/deferred-promise': 2.2.0 diff --git a/src/core/utils/internal/devUtils.ts b/src/core/utils/internal/devUtils.ts index 47abfb75..e39b6926 100644 --- a/src/core/utils/internal/devUtils.ts +++ b/src/core/utils/internal/devUtils.ts @@ -29,3 +29,7 @@ export const devUtils = { warn, error, } + +export class InternalError extends Error { + // +} diff --git a/src/core/utils/request/onUnhandledRequest.ts b/src/core/utils/request/onUnhandledRequest.ts index fcf4d505..02708b13 100644 --- a/src/core/utils/request/onUnhandledRequest.ts +++ b/src/core/utils/request/onUnhandledRequest.ts @@ -1,5 +1,5 @@ import { toPublicUrl } from './toPublicUrl' -import { devUtils } from '../internal/devUtils' +import { InternalError, devUtils } from '../internal/devUtils' export interface UnhandledRequestPrint { warning(): void @@ -33,7 +33,7 @@ export async function onUnhandledRequest( devUtils.error('Error: %s', unhandledRequestMessage) // Throw an exception to halt request processing and not perform the original request. - throw new Error( + throw new InternalError( devUtils.formatMessage( 'Cannot bypass a request when using the "error" strategy for the "onUnhandledRequest" option.', ), @@ -49,7 +49,7 @@ export async function onUnhandledRequest( break default: - throw new Error( + throw new InternalError( devUtils.formatMessage( 'Failed to react to an unhandled request: unknown strategy "%s". Please provide one of the supported strategies ("bypass", "warn", "error") or a custom callback function as the value of the "onUnhandledRequest" option.', strategy, diff --git a/src/node/SetupServerCommonApi.ts b/src/node/SetupServerCommonApi.ts index fc742429..85c534d3 100644 --- a/src/node/SetupServerCommonApi.ts +++ b/src/node/SetupServerCommonApi.ts @@ -15,7 +15,7 @@ import { SetupApi } from '~/core/SetupApi' import { handleRequest } from '~/core/utils/handleRequest' import type { RequestHandler } from '~/core/handlers/RequestHandler' import { mergeRight } from '~/core/utils/internal/mergeRight' -import { devUtils } from '~/core/utils/internal/devUtils' +import { InternalError, devUtils } from '~/core/utils/internal/devUtils' import type { SetupServerCommon } from './glossary' export const DEFAULT_LISTEN_OPTIONS: RequiredDeep = { @@ -68,6 +68,12 @@ export class SetupServerCommonApi return }) + this.interceptor.on('unhandledException', ({ error }) => { + if (error instanceof InternalError) { + throw error + } + }) + this.interceptor.on( 'response', ({ response, isMockedResponse, request, requestId }) => { diff --git a/test/node/msw-api/setup-server/scenarios/on-unhandled-request/error.node.test.ts b/test/node/msw-api/setup-server/scenarios/on-unhandled-request/error.node.test.ts index a84080bb..307a22c3 100644 --- a/test/node/msw-api/setup-server/scenarios/on-unhandled-request/error.node.test.ts +++ b/test/node/msw-api/setup-server/scenarios/on-unhandled-request/error.node.test.ts @@ -68,12 +68,7 @@ test('errors on unhandled request when using the "error" value', async () => { const requestError = await makeRequest() - expect(requestError.message).toBe('Failed to fetch') - /** - * @note Undici wraps fetch rejections in a generic "Failed to fetch" error, - * forwarding the actual rejection in the "error.cause" property. - */ - expect(requestError.cause).toEqual( + expect(requestError).toEqual( new Error( '[MSW] Cannot bypass a request when using the "error" strategy for the "onUnhandledRequest" option.', ), diff --git a/test/node/rest-api/response/response-cookies.test.ts b/test/node/rest-api/response/response-cookies.test.ts new file mode 100644 index 00000000..bfdcaefd --- /dev/null +++ b/test/node/rest-api/response/response-cookies.test.ts @@ -0,0 +1,50 @@ +/** + * @vitest-environment node + */ +import { http, HttpResponse } from 'msw' +import { setupServer } from 'msw/node' + +const server = setupServer() + +beforeAll(() => { + server.listen() +}) + +afterEach(() => { + server.resetHandlers() +}) + +afterAll(() => { + server.close() +}) + +it('supports mocking a response cookie', async () => { + server.use( + http.get('*/resource', () => { + return new HttpResponse(null, { + headers: { + 'Set-Cookie': 'a=1', + }, + }) + }), + ) + + const response = await fetch('http://localhost/resource') + expect(response.headers.get('Set-Cookie')).toBe('a=1') +}) + +it('supports mocking multiple response cookies', async () => { + server.use( + http.get('*/resource', () => { + return new HttpResponse(null, { + headers: [ + ['Set-Cookie', 'a=1'], + ['Set-Cookie', 'b=2'], + ], + }) + }), + ) + + const response = await fetch('http://localhost/resource') + expect(response.headers.get('Set-Cookie')).toBe('a=1, b=2') +})