Skip to content

Commit

Permalink
feat: upgrade to @mswjs/interceptors 0.16 (#1262)
Browse files Browse the repository at this point in the history
fix: set "response.url" on the mocked responses

fix: prevent "EAI_AGAIN" from halting requests
  • Loading branch information
kettanaito committed May 30, 2022
1 parent e2b687e commit 2176577
Show file tree
Hide file tree
Showing 16 changed files with 280 additions and 192 deletions.
4 changes: 2 additions & 2 deletions package.json
Expand Up @@ -68,7 +68,7 @@
"sideEffects": false,
"dependencies": {
"@mswjs/cookies": "^0.2.0",
"@mswjs/interceptors": "^0.15.1",
"@mswjs/interceptors": "^0.16.3",
"@open-draft/until": "^1.0.3",
"@types/cookie": "^0.4.1",
"@types/js-levenshtein": "^1.1.1",
Expand Down Expand Up @@ -147,4 +147,4 @@
"path": "./node_modules/cz-conventional-changelog"
}
}
}
}
2 changes: 1 addition & 1 deletion src/context/fetch.ts
@@ -1,6 +1,6 @@
import { isNodeProcess } from 'is-node-process'
import { Headers } from 'headers-polyfill'
import { MockedRequest } from '../handlers/RequestHandler'
import type { MockedRequest } from '../handlers/RequestHandler'

const useFetch: (input: RequestInfo, init?: RequestInit) => Promise<Response> =
isNodeProcess() ? require('node-fetch') : window.fetch
Expand Down
4 changes: 2 additions & 2 deletions src/native/index.ts
@@ -1,6 +1,6 @@
import { interceptXMLHttpRequest } from '@mswjs/interceptors/lib/interceptors/XMLHttpRequest'
import { XMLHttpRequestInterceptor } from '@mswjs/interceptors/lib/interceptors/XMLHttpRequest'
import { createSetupServer } from '../node/createSetupServer'

// Provision request interception via patching the `XMLHttpRequest` class only
// in React Native. There is no `http`/`https` modules in that environment.
export const setupServer = createSetupServer(interceptXMLHttpRequest)
export const setupServer = createSetupServer(new XMLHttpRequestInterceptor())
57 changes: 34 additions & 23 deletions src/node/createSetupServer.ts
Expand Up @@ -2,9 +2,10 @@ import { bold } from 'chalk'
import { isNodeProcess } from 'is-node-process'
import { StrictEventEmitter } from 'strict-event-emitter'
import {
createInterceptor,
BatchInterceptor,
MockedResponse as MockedInterceptedResponse,
Interceptor,
HttpRequestEventMap,
} from '@mswjs/interceptors'
import * as requestHandlerUtils from '../utils/internal/requestHandlerUtils'
import { ServerLifecycleEventsMap, SetupServerApi } from './glossary'
Expand All @@ -25,7 +26,9 @@ const DEFAULT_LISTEN_OPTIONS: RequiredDeep<SharedOptions> = {
* Creates a `setupServer` API using given request interceptors.
* Useful to generate identical API using different patches to request issuing modules.
*/
export function createSetupServer(...interceptors: Interceptor[]) {
export function createSetupServer(
...interceptors: Interceptor<HttpRequestEventMap>[]
) {
const emitter = new StrictEventEmitter<ServerLifecycleEventsMap>()
const publicEmitter = new StrictEventEmitter<ServerLifecycleEventsMap>()
pipeEvents(emitter, publicEmitter)
Expand Down Expand Up @@ -57,27 +60,35 @@ export function createSetupServer(...interceptors: Interceptor[]) {

let resolvedOptions = {} as RequiredDeep<SharedOptions>

const interceptor = createInterceptor({
modules: interceptors,
async resolver(request) {
const mockedRequest = parseIsomorphicRequest(request)
return handleRequest<MockedInterceptedResponse>(
mockedRequest,
currentHandlers,
resolvedOptions,
emitter,
{
transformResponse(response) {
return {
status: response.status,
statusText: response.statusText,
headers: response.headers.all(),
body: response.body,
}
},
const interceptor = new BatchInterceptor({
name: 'setup-server',
interceptors,
})

interceptor.on('request', async function setupServerListener(request) {
const mockedRequest = parseIsomorphicRequest(request)
const response = await handleRequest<MockedInterceptedResponse>(
mockedRequest,
currentHandlers,
resolvedOptions,
emitter,
{
transformResponse(response) {
return {
status: response.status,
statusText: response.statusText,
headers: response.headers.all(),
body: response.body,
}
},
)
},
},
)

if (response) {
request.respondWith(response)
}

return
})

interceptor.on('response', (request, response) => {
Expand Down Expand Up @@ -146,7 +157,7 @@ ${bold(`${pragma} ${header}`)}
close() {
emitter.removeAllListeners()
publicEmitter.removeAllListeners()
interceptor.restore()
interceptor.dispose()
},
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/node/glossary.ts
@@ -1,5 +1,5 @@
import { PartialDeep } from 'type-fest'
import { IsomorphicResponse } from '@mswjs/interceptors'
import type { PartialDeep } from 'type-fest'
import type { IsomorphicResponse } from '@mswjs/interceptors'
import { RequestHandler } from '../handlers/RequestHandler'
import {
LifeCycleEventEmitter,
Expand Down
8 changes: 4 additions & 4 deletions src/node/setupServer.ts
@@ -1,5 +1,5 @@
import { interceptClientRequest } from '@mswjs/interceptors/lib/interceptors/ClientRequest'
import { interceptXMLHttpRequest } from '@mswjs/interceptors/lib/interceptors/XMLHttpRequest'
import { ClientRequestInterceptor } from '@mswjs/interceptors/lib/interceptors/ClientRequest'
import { XMLHttpRequestInterceptor } from '@mswjs/interceptors/lib/interceptors/XMLHttpRequest'
import { createSetupServer } from './createSetupServer'

/**
Expand All @@ -10,6 +10,6 @@ import { createSetupServer } from './createSetupServer'
export const setupServer = createSetupServer(
// List each interceptor separately instead of using the "node" preset
// so that MSW wouldn't bundle the unnecessary classes (i.e. "SocketPolyfill").
interceptClientRequest,
interceptXMLHttpRequest,
new ClientRequestInterceptor(),
new XMLHttpRequestInterceptor(),
)
6 changes: 3 additions & 3 deletions src/setupWorker/glossary.ts
Expand Up @@ -7,7 +7,7 @@ import {
} from '../sharedOptions'
import { ServiceWorkerMessage } from '../utils/createBroadcastChannel'
import { DefaultBodyType, RequestHandler } from '../handlers/RequestHandler'
import { InterceptorApi } from '@mswjs/interceptors'
import type { HttpRequestEventMap, Interceptor } from '@mswjs/interceptors'
import { Path } from '../utils/matching/matchRequestUrl'
import { RequiredDeep } from '../typeUtils'

Expand Down Expand Up @@ -40,7 +40,7 @@ export interface ServiceWorkerIncomingRequest extends RequestWithoutMethods {
/**
* Text response body.
*/
body: string | undefined
body?: string
}

export type ServiceWorkerIncomingResponse = Pick<
Expand Down Expand Up @@ -134,7 +134,7 @@ export interface SetupWorkerInternalContext {
>
}
useFallbackMode: boolean
fallbackInterceptor?: InterceptorApi
fallbackInterceptor?: Interceptor<HttpRequestEventMap>
}

export type ServiceWorkerInstanceTuple = [
Expand Down
2 changes: 1 addition & 1 deletion src/setupWorker/stop/createFallbackStop.ts
Expand Up @@ -5,7 +5,7 @@ export function createFallbackStop(
context: SetupWorkerInternalContext,
): StopHandler {
return function stop() {
context.fallbackInterceptor?.restore()
context.fallbackInterceptor?.dispose()
printStopMessage({ quiet: context.startOptions?.quiet })
}
}
2 changes: 1 addition & 1 deletion src/utils/request/parseIsomorphicRequest.ts
@@ -1,4 +1,4 @@
import { IsomorphicRequest } from '@mswjs/interceptors'
import type { IsomorphicRequest } from '@mswjs/interceptors'
import { MockedRequest, passthrough } from '../../handlers/RequestHandler'
import { parseBody } from './parseBody'
import { setRequestCookies } from './setRequestCookies'
Expand Down
91 changes: 51 additions & 40 deletions src/utils/worker/createFallbackRequestListener.ts
@@ -1,55 +1,66 @@
import { createInterceptor, InterceptorApi } from '@mswjs/interceptors'
import { interceptFetch } from '@mswjs/interceptors/lib/interceptors/fetch'
import { interceptXMLHttpRequest } from '@mswjs/interceptors/lib/interceptors/XMLHttpRequest'
import { RequestHandler } from '../../handlers/RequestHandler'
import {
Interceptor,
BatchInterceptor,
HttpRequestEventMap,
} from '@mswjs/interceptors'
import { FetchInterceptor } from '@mswjs/interceptors/lib/interceptors/fetch'
import { XMLHttpRequestInterceptor } from '@mswjs/interceptors/lib/interceptors/XMLHttpRequest'
import type { RequestHandler } from '../../handlers/RequestHandler'
import {
SerializedResponse,
SetupWorkerInternalContext,
StartOptions,
} from '../../setupWorker/glossary'
import { RequiredDeep } from '../../typeUtils'
import type { RequiredDeep } from '../../typeUtils'
import { handleRequest } from '../handleRequest'
import { parseIsomorphicRequest } from '../request/parseIsomorphicRequest'

export function createFallbackRequestListener(
context: SetupWorkerInternalContext,
options: RequiredDeep<StartOptions>,
): InterceptorApi {
const interceptor = createInterceptor({
modules: [interceptFetch, interceptXMLHttpRequest],
async resolver(request) {
const mockedRequest = parseIsomorphicRequest(request)
return handleRequest<SerializedResponse>(
mockedRequest,
context.requestHandlers,
options,
context.emitter,
{
transformResponse(response) {
return {
status: response.status,
statusText: response.statusText,
headers: response.headers.all(),
body: response.body,
delay: response.delay,
}
},
onMockedResponseSent(
response,
{ handler, publicRequest, parsedRequest },
) {
if (!options.quiet) {
handler.log(
publicRequest,
response,
handler as RequestHandler,
parsedRequest,
)
}
},
): Interceptor<HttpRequestEventMap> {
const interceptor = new BatchInterceptor({
name: 'fallback',
interceptors: [new FetchInterceptor(), new XMLHttpRequestInterceptor()],
})

interceptor.on('request', async (request) => {
const mockedRequest = parseIsomorphicRequest(request)

const response = await handleRequest<SerializedResponse>(
mockedRequest,
context.requestHandlers,
options,
context.emitter,
{
transformResponse(response) {
return {
status: response.status,
statusText: response.statusText,
headers: response.headers.all(),
body: response.body,
delay: response.delay,
}
},
onMockedResponseSent(
response,
{ handler, publicRequest, parsedRequest },
) {
if (!options.quiet) {
handler.log(
publicRequest,
response,
handler as RequestHandler,
parsedRequest,
)
}
},
)
},
},
)

if (response) {
request.respondWith(response)
}
})

interceptor.apply()
Expand Down

0 comments on commit 2176577

Please sign in to comment.