Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: [M3-7859]: Upgrade MSW to 2.2.3 #10285

Merged
merged 11 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/development-guide/09-mocking-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ To enable the MSW, open the Local Dev Tools and check the "Mock Service Worker"
import { rest } from "msw";

const handlers = [
rest.get("*/profile", (req, res, ctx) => {
http.get("*/profile", () => {
//
const profile = profileFactory.build({ restricted: true });
return res(ctx.json(profile));
return HttpResponse.json((profile));
}),
// ... other handlers
];
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"resolutions": {
"@babel/traverse": "^7.23.3",
"minimist": "^1.2.3",
"yargs-parser": "^18.1.3",
"yargs-parser": "^21.1.1",
"kind-of": "^6.0.3",
"node-fetch": "^2.6.7",
"ua-parser-js": "^0.7.33",
Expand Down
2 changes: 1 addition & 1 deletion packages/manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@
"junit2json": "^3.1.4",
"lint-staged": "^13.2.2",
"mocha-junit-reporter": "^2.2.1",
"msw": "~1.3.2",
"msw": "^2.2.3",
"prettier": "~2.2.1",
"react-test-renderer": "16.14.0",
"redux-mock-store": "^1.5.3",
Expand Down
175 changes: 77 additions & 98 deletions packages/manager/public/mockServiceWorker.js
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is all generated via npx msw init

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.2.3).
* @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,17 +87,6 @@ self.addEventListener('message', async function (event) {

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

// Bypass launch darkly
if (request.url.includes("launchdarkly.com")) {
return
}
abailly-akamai marked this conversation as resolved.
Show resolved Hide resolved

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

// Bypass navigation requests.
if (request.mode === 'navigate') {
Expand All @@ -117,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 @@ -151,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 @@ -201,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 @@ -232,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 @@ -266,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 @@ -292,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
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
notificationFactory,
} from 'src/factories/notification';
import { makeResourcePage } from 'src/mocks/serverHandlers';
import { rest, server } from 'src/mocks/testServer';
import { HttpResponse, http, server } from 'src/mocks/testServer';
import { getAbuseTickets } from 'src/store/selectors/getAbuseTicket';
import { renderWithTheme, wrapWithTheme } from 'src/utilities/testHelpers';

Expand All @@ -17,11 +17,9 @@ const TICKET_TESTID = 'abuse-ticket-link';
describe('Abuse ticket banner', () => {
it('should render a banner for an abuse ticket', async () => {
server.use(
rest.get('*/account/notifications', (req, res, ctx) => {
return res(
ctx.json(
makeResourcePage(abuseTicketNotificationFactory.buildList(1))
)
http.get('*/account/notifications', () => {
return HttpResponse.json(
makeResourcePage(abuseTicketNotificationFactory.buildList(1))
);
})
);
Expand All @@ -34,11 +32,9 @@ describe('Abuse ticket banner', () => {

it('should aggregate multiple abuse tickets', async () => {
server.use(
rest.get('*/account/notifications', (req, res, ctx) => {
return res(
ctx.json(
makeResourcePage(abuseTicketNotificationFactory.buildList(2))
)
http.get('*/account/notifications', () => {
return HttpResponse.json(
makeResourcePage(abuseTicketNotificationFactory.buildList(2))
);
})
);
Expand All @@ -52,8 +48,8 @@ describe('Abuse ticket banner', () => {
it('should link to the ticket if there is a single abuse ticket', async () => {
const mockAbuseTicket = abuseTicketNotificationFactory.build();
server.use(
rest.get('*/account/notifications', (req, res, ctx) => {
return res(ctx.json(makeResourcePage([mockAbuseTicket])));
http.get('*/account/notifications', () => {
return HttpResponse.json(makeResourcePage([mockAbuseTicket]));
})
);
const { getByTestId } = renderWithTheme(<AbuseTicketBanner />);
Expand All @@ -67,8 +63,8 @@ describe('Abuse ticket banner', () => {
it('should link to the ticket list view if there are multiple tickets', async () => {
const mockAbuseTickets = abuseTicketNotificationFactory.buildList(2);
server.use(
rest.get('*/account/notifications', (req, res, ctx) => {
return res(ctx.json(makeResourcePage(mockAbuseTickets)));
http.get('*/account/notifications', () => {
return HttpResponse.json(makeResourcePage(mockAbuseTickets));
})
);
const { getByTestId } = renderWithTheme(<AbuseTicketBanner />);
Expand All @@ -81,8 +77,8 @@ describe('Abuse ticket banner', () => {

it('should return null if there are no abuse tickets', () => {
server.use(
rest.get('*/account/notifications', (req, res, ctx) => {
return res(ctx.json(makeResourcePage([])));
http.get('*/account/notifications', () => {
return HttpResponse.json(makeResourcePage([]));
})
);
const { queryByTestId } = renderWithTheme(<AbuseTicketBanner />);
Expand Down
Loading
Loading