Skip to content

Commit

Permalink
chore: upgrade MSW to version 2.x.x
Browse files Browse the repository at this point in the history
Signed-off-by: John Cowen <john.cowen@konghq.com>
  • Loading branch information
johncowen committed Jan 30, 2024
1 parent 7676fcc commit 95fcc93
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 328 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
"jsdom": "24.0.0",
"lockfile-lint": "4.12.1",
"marked": "11.2.0",
"msw": "1.3.2",
"msw": "2.1.5",
"postcss-html": "1.6.0",
"sass": "1.70.0",
"standard": "17.1.0",
Expand Down
170 changes: 77 additions & 93 deletions public/mockServiceWorker.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
/* tslint:disable */

/**
* Mock Service Worker (1.3.2).
* Mock Service Worker (2.1.5).
* @see https://github.com/mswjs/msw
* - Please do NOT modify this file.
* - Please do NOT serve this file on production.
*/

const INTEGRITY_CHECKSUM = '3d6b9f06410d179a7f7404d4bf4c3c70'
const INTEGRITY_CHECKSUM = '223d191a56023cd36aa88c802961b911'
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
const activeClientIds = new Set()

self.addEventListener('install', function () {
Expand Down Expand Up @@ -86,12 +87,6 @@ self.addEventListener('message', async function (event) {

self.addEventListener('fetch', function (event) {
const { request } = event
const accept = request.headers.get('accept') || ''

// Bypass server-sent events.
if (accept.includes('text/event-stream')) {
return
}

// Bypass navigation requests.
if (request.mode === 'navigate') {
Expand All @@ -112,29 +107,8 @@ self.addEventListener('fetch', function (event) {
}

// Generate unique request ID.
const requestId = Math.random().toString(16).slice(2)

event.respondWith(
handleRequest(event, requestId).catch((error) => {
if (error.name === 'NetworkError') {
console.warn(
'[MSW] Successfully emulated a network error for the "%s %s" request.',
request.method,
request.url,
)
return
}

// At this point, any exception indicates an issue with the original request/response.
console.error(
`\
[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`,
request.method,
request.url,
`${error.name}: ${error.message}`,
)
}),
)
const requestId = crypto.randomUUID()
event.respondWith(handleRequest(event, requestId))
})

async function handleRequest(event, requestId) {
Expand All @@ -146,21 +120,24 @@ async function handleRequest(event, requestId) {
// this message will pend indefinitely.
if (client && activeClientIds.has(client.id)) {
;(async function () {
const clonedResponse = response.clone()
sendToClient(client, {
type: 'RESPONSE',
payload: {
requestId,
type: clonedResponse.type,
ok: clonedResponse.ok,
status: clonedResponse.status,
statusText: clonedResponse.statusText,
body:
clonedResponse.body === null ? null : await clonedResponse.text(),
headers: Object.fromEntries(clonedResponse.headers.entries()),
redirected: clonedResponse.redirected,
const responseClone = response.clone()

sendToClient(
client,
{
type: 'RESPONSE',
payload: {
requestId,
isMockedResponse: IS_MOCKED_RESPONSE in response,
type: responseClone.type,
status: responseClone.status,
statusText: responseClone.statusText,
body: responseClone.body,
headers: Object.fromEntries(responseClone.headers.entries()),
},
},
})
[responseClone.body],
)
})()
}

Expand Down Expand Up @@ -196,20 +173,20 @@ async function resolveMainClient(event) {

async function getResponse(event, client, requestId) {
const { request } = event
const clonedRequest = request.clone()

// Clone the request because it might've been already used
// (i.e. its body has been read and sent to the client).
const requestClone = request.clone()

function passthrough() {
// Clone the request because it might've been already used
// (i.e. its body has been read and sent to the client).
const headers = Object.fromEntries(clonedRequest.headers.entries())
const headers = Object.fromEntries(requestClone.headers.entries())

// Remove MSW-specific request headers so the bypassed requests
// comply with the server's CORS preflight check.
// Operate with the headers as an object because request "Headers"
// are immutable.
delete headers['x-msw-bypass']
// Remove internal MSW request header so the passthrough request
// complies with any potential CORS preflight checks on the server.
// Some servers forbid unknown request headers.
delete headers['x-msw-intention']

return fetch(clonedRequest, { headers })
return fetch(requestClone, { headers })
}

// Bypass mocking when the client is not active.
Expand All @@ -227,31 +204,36 @@ async function getResponse(event, client, requestId) {

// Bypass requests with the explicit bypass header.
// Such requests can be issued by "ctx.fetch()".
if (request.headers.get('x-msw-bypass') === 'true') {
const mswIntention = request.headers.get('x-msw-intention')
if (['bypass', 'passthrough'].includes(mswIntention)) {
return passthrough()
}

// Notify the client that a request has been intercepted.
const clientMessage = await sendToClient(client, {
type: 'REQUEST',
payload: {
id: requestId,
url: request.url,
method: request.method,
headers: Object.fromEntries(request.headers.entries()),
cache: request.cache,
mode: request.mode,
credentials: request.credentials,
destination: request.destination,
integrity: request.integrity,
redirect: request.redirect,
referrer: request.referrer,
referrerPolicy: request.referrerPolicy,
body: await request.text(),
bodyUsed: request.bodyUsed,
keepalive: request.keepalive,
const requestBuffer = await request.arrayBuffer()
const clientMessage = await sendToClient(
client,
{
type: 'REQUEST',
payload: {
id: requestId,
url: request.url,
mode: request.mode,
method: request.method,
headers: Object.fromEntries(request.headers.entries()),
cache: request.cache,
credentials: request.credentials,
destination: request.destination,
integrity: request.integrity,
redirect: request.redirect,
referrer: request.referrer,
referrerPolicy: request.referrerPolicy,
body: requestBuffer,
keepalive: request.keepalive,
},
},
})
[requestBuffer],
)

switch (clientMessage.type) {
case 'MOCK_RESPONSE': {
Expand All @@ -261,21 +243,12 @@ async function getResponse(event, client, requestId) {
case 'MOCK_NOT_FOUND': {
return passthrough()
}

case 'NETWORK_ERROR': {
const { name, message } = clientMessage.data
const networkError = new Error(message)
networkError.name = name

// Rejecting a "respondWith" promise emulates a network error.
throw networkError
}
}

return passthrough()
}

function sendToClient(client, message) {
function sendToClient(client, message, transferrables = []) {
return new Promise((resolve, reject) => {
const channel = new MessageChannel()

Expand All @@ -287,17 +260,28 @@ function sendToClient(client, message) {
resolve(event.data)
}

client.postMessage(message, [channel.port2])
client.postMessage(
message,
[channel.port2].concat(transferrables.filter(Boolean)),
)
})
}

function sleep(timeMs) {
return new Promise((resolve) => {
setTimeout(resolve, timeMs)
async function respondWithMock(response) {
// Setting response status code to 0 is a no-op.
// However, when responding with a "Response.error()", the produced Response
// instance will have status code set to 0. Since it's not possible to create
// a Response instance with status code 0, handle that use-case separately.
if (response.status === 0) {
return Response.error()
}

const mockedResponse = new Response(response.body, response)

Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
value: true,
enumerable: true,
})
}

async function respondWithMock(response) {
await sleep(response.delay)
return new Response(response.body, response)
return mockedResponse
}
26 changes: 11 additions & 15 deletions src/services/development.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { setupWorker, MockedRequest } from 'msw'
import { setupWorker } from 'msw/browser'

import DebugKClipboardProvider from '@/app/application/components/debug-k-clipboard-provider/DebugKClipboardProvider.vue'
import debugI18n from '@/app/application/services/i18n/DebugI18n'
Expand Down Expand Up @@ -69,14 +69,9 @@ export const services: ServiceConfigurator<SupportedTokens> = (app) => [

[token('development.components'), {
service: () => {
// unit testing is likely to have process
// no matter which runner is used
// if we are unit testing don't decorate KClipboardProvider
return typeof process === 'undefined'
? [
['KClipboardProvider', DebugKClipboardProvider],
]
: []
return [
['KClipboardProvider', DebugKClipboardProvider],
]
},
labels: [
app.components,
Expand All @@ -103,18 +98,19 @@ export const services: ServiceConfigurator<SupportedTokens> = (app) => [

return worker.start({
quiet: true,
onUnhandledRequest(req: MockedRequest) {
onUnhandledRequest(req: Request) {
// Ignores warnings about unhandled requests.
const { pathname, href } = new URL(req.url)
if (
req.url.pathname.startsWith('/@fs') ||
req.url.pathname.startsWith('/node_modules') ||
req.url.pathname.startsWith('/src/assets') ||
req.url.href.match(/\.(vue|ts|js|json)(\?.*)?$/)
pathname.startsWith('/@fs') ||
pathname.startsWith('/node_modules') ||
pathname.startsWith('/src/assets') ||
href.match(/\.(vue|ts|js|json)(\?.*)?$/)
) {
return
}

console.warn('Found an unhandled %s request to %s', req.method, req.url.href)
console.warn('Found an unhandled %s request to %s', req.method, href)
},
})
},
Expand Down
37 changes: 15 additions & 22 deletions src/test-support/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import deepmerge from 'deepmerge'
import { rest } from 'msw'
import { setupServer } from 'msw/node'
import { http, HttpResponse } from 'msw'

import { dependencies, escapeRoute } from './fake'
import type { FakeEndpoint, MockResponse, FS, AEnv, Env, AppEnvKeys, MockEnvKeys, RestRequest } from './fake'
Expand All @@ -11,7 +10,6 @@ export type { FS, EndpointDependencies, MockResponder } from './fake'
export type Merge = (obj: Partial<MockResponse>) => MockResponse
export type Callback = (merge: Merge, req: RestRequest, response: MockResponse) => MockResponse
export type Options = Record<string, string>
type Server = ReturnType<typeof setupServer>

export const undefinedSymbol = Symbol('undefined')

Expand Down Expand Up @@ -42,7 +40,7 @@ export const createMerge = (response: MockResponse): Merge => (obj) => {
}))
}

export const useResponder = <T extends RestRequest>(fs: FS, env: AEnv) => {
export const useResponder = (fs: FS, env: AEnv) => {
return (route: string, opts: Options = {}, cb: Callback = noop) => {
const mockEnv: Env = (key, d = '') => (opts[key as MockEnvKeys] ?? '') || env(key as AppEnvKeys, d)
if (route !== '*') {
Expand All @@ -53,7 +51,7 @@ export const useResponder = <T extends RestRequest>(fs: FS, env: AEnv) => {
...dependencies,
env: mockEnv,
})
return async (req: T): Promise<MockResponse> => {
return async (req: RestRequest): Promise<MockResponse> => {
const _response = fetch(req)
const latency = parseInt(mockEnv('KUMA_LATENCY', '0'))
if (latency !== 0) {
Expand All @@ -65,15 +63,19 @@ export const useResponder = <T extends RestRequest>(fs: FS, env: AEnv) => {
}
export const handler = (fs: FS, env: AEnv) => {
const baseUrl = env('KUMA_API_URL')
const responder = useResponder<RestRequest>(fs, env)
const responder = useResponder(fs, env)
return (route: string, opts: Options = {}, cb: Callback = noop) => {
const respond = responder(route, opts, cb)
return rest.all(`${route.includes('://') ? '' : baseUrl}${escapeRoute(route)}`, async (req, res, ctx) => {
const response = await respond(req)
return res(
ctx.status(parseInt(response.headers['Status-Code'] ?? '200')),
ctx.json(response.body),
)
return http.all(`${route.includes('://') ? '' : baseUrl}${escapeRoute(route)}`, async ({ request, params }) => {
const response = await respond({
method: request.method,
url: new URL(request.url),
body: request.body,
params,
})
return HttpResponse.json(response.body, {
status: parseInt(response.headers['Status-Code'] ?? '200'),
})
})
}
}
Expand Down Expand Up @@ -112,13 +114,4 @@ export const fakeApi = (env: AEnv, fs: FS) => {
}
}
}
export const mocker = (env: AEnv, server: Server, fs: FS) => {
const handlerFor = handler(fs, env)

return (route: string, opts: Options = {}, cb: Callback = noop) => {
return server.use(
handlerFor(route, opts, cb),
)
}
}
export type Mocker = ReturnType<typeof mocker>
export type Mocker = (route: string, opts: Options, cb: Callback) => void
Loading

0 comments on commit 95fcc93

Please sign in to comment.