-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
369 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,5 +56,8 @@ | |
"last 1 firefox version", | ||
"last 1 safari version" | ||
] | ||
}, | ||
"msw": { | ||
"workerDirectory": "public" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,322 @@ | ||
/** | ||
* Mock Service Worker. | ||
* @see https://github.com/mswjs/msw | ||
* - Please do NOT modify this file. | ||
* - Please do NOT serve this file on production. | ||
*/ | ||
/* eslint-disable */ | ||
/* tslint:disable */ | ||
|
||
const INTEGRITY_CHECKSUM = '795882c72c7304f6fa1d4a65a2418900' | ||
const bypassHeaderName = 'x-msw-bypass' | ||
const activeClientIds = new Set() | ||
|
||
self.addEventListener('install', function () { | ||
return self.skipWaiting() | ||
}) | ||
|
||
self.addEventListener('activate', async function (event) { | ||
return self.clients.claim() | ||
}) | ||
|
||
self.addEventListener('message', async function (event) { | ||
const clientId = event.source.id | ||
|
||
if (!clientId || !self.clients) { | ||
return | ||
} | ||
|
||
const client = await self.clients.get(clientId) | ||
|
||
if (!client) { | ||
return | ||
} | ||
|
||
const allClients = await self.clients.matchAll() | ||
|
||
switch (event.data) { | ||
case 'KEEPALIVE_REQUEST': { | ||
sendToClient(client, { | ||
type: 'KEEPALIVE_RESPONSE', | ||
}) | ||
break | ||
} | ||
|
||
case 'INTEGRITY_CHECK_REQUEST': { | ||
sendToClient(client, { | ||
type: 'INTEGRITY_CHECK_RESPONSE', | ||
payload: INTEGRITY_CHECKSUM, | ||
}) | ||
break | ||
} | ||
|
||
case 'MOCK_ACTIVATE': { | ||
activeClientIds.add(clientId) | ||
|
||
sendToClient(client, { | ||
type: 'MOCKING_ENABLED', | ||
payload: true, | ||
}) | ||
break | ||
} | ||
|
||
case 'MOCK_DEACTIVATE': { | ||
activeClientIds.delete(clientId) | ||
break | ||
} | ||
|
||
case 'CLIENT_CLOSED': { | ||
activeClientIds.delete(clientId) | ||
|
||
const remainingClients = allClients.filter((client) => { | ||
return client.id !== clientId | ||
}) | ||
|
||
// Unregister itself when there are no more clients | ||
if (remainingClients.length === 0) { | ||
self.registration.unregister() | ||
} | ||
|
||
break | ||
} | ||
} | ||
}) | ||
|
||
// Resolve the "master" client for the given event. | ||
// Client that issues a request doesn't necessarily equal the client | ||
// that registered the worker. It's with the latter the worker should | ||
// communicate with during the response resolving phase. | ||
async function resolveMasterClient(event) { | ||
const client = await self.clients.get(event.clientId) | ||
|
||
if (client.frameType === 'top-level') { | ||
return client | ||
} | ||
|
||
const allClients = await self.clients.matchAll() | ||
|
||
return allClients | ||
.filter((client) => { | ||
// Get only those clients that are currently visible. | ||
return client.visibilityState === 'visible' | ||
}) | ||
.find((client) => { | ||
// Find the client ID that's recorded in the | ||
// set of clients that have registered the worker. | ||
return activeClientIds.has(client.id) | ||
}) | ||
} | ||
|
||
async function handleRequest(event, requestId) { | ||
const client = await resolveMasterClient(event) | ||
const response = await getResponse(event, client, requestId) | ||
|
||
// Send back the response clone for the "response:*" life-cycle events. | ||
// Ensure MSW is active and ready to handle the message, otherwise | ||
// this message will pend indefinitely. | ||
if (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: serializeHeaders(clonedResponse.headers), | ||
redirected: clonedResponse.redirected, | ||
}, | ||
}) | ||
})() | ||
} | ||
|
||
return response | ||
} | ||
|
||
async function getResponse(event, client, requestId) { | ||
const { request } = event | ||
const requestClone = request.clone() | ||
const getOriginalResponse = () => fetch(requestClone) | ||
|
||
// Bypass mocking when the request client is not active. | ||
if (!client) { | ||
return getOriginalResponse() | ||
} | ||
|
||
// Bypass initial page load requests (i.e. static assets). | ||
// The absence of the immediate/parent client in the map of the active clients | ||
// means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet | ||
// and is not ready to handle requests. | ||
if (!activeClientIds.has(client.id)) { | ||
return await getOriginalResponse() | ||
} | ||
|
||
// Bypass requests with the explicit bypass header | ||
if (requestClone.headers.get(bypassHeaderName) === 'true') { | ||
const cleanRequestHeaders = serializeHeaders(requestClone.headers) | ||
|
||
// Remove the bypass header to comply with the CORS preflight check. | ||
delete cleanRequestHeaders[bypassHeaderName] | ||
|
||
const originalRequest = new Request(requestClone, { | ||
headers: new Headers(cleanRequestHeaders), | ||
}) | ||
|
||
return fetch(originalRequest) | ||
} | ||
|
||
// Send the request to the client-side MSW. | ||
const reqHeaders = serializeHeaders(request.headers) | ||
const body = await request.text() | ||
|
||
const clientMessage = await sendToClient(client, { | ||
type: 'REQUEST', | ||
payload: { | ||
id: requestId, | ||
url: request.url, | ||
method: request.method, | ||
headers: reqHeaders, | ||
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, | ||
bodyUsed: request.bodyUsed, | ||
keepalive: request.keepalive, | ||
}, | ||
}) | ||
|
||
switch (clientMessage.type) { | ||
case 'MOCK_SUCCESS': { | ||
return delayPromise( | ||
() => respondWithMock(clientMessage), | ||
clientMessage.payload.delay, | ||
) | ||
} | ||
|
||
case 'MOCK_NOT_FOUND': { | ||
return getOriginalResponse() | ||
} | ||
|
||
case 'NETWORK_ERROR': { | ||
const { name, message } = clientMessage.payload | ||
const networkError = new Error(message) | ||
networkError.name = name | ||
|
||
// Rejecting a request Promise emulates a network error. | ||
throw networkError | ||
} | ||
|
||
case 'INTERNAL_ERROR': { | ||
const parsedBody = JSON.parse(clientMessage.payload.body) | ||
|
||
console.error( | ||
`\ | ||
[MSW] Request handler function for "%s %s" has thrown the following exception: | ||
${parsedBody.errorType}: ${parsedBody.message} | ||
(see more detailed error stack trace in the mocked response body) | ||
This exception has been gracefully handled as a 500 response, however, it's strongly recommended to resolve this error. | ||
If you wish to mock an error response, please refer to this guide: https://mswjs.io/docs/recipes/mocking-error-responses\ | ||
`, | ||
request.method, | ||
request.url, | ||
) | ||
|
||
return respondWithMock(clientMessage) | ||
} | ||
} | ||
|
||
return getOriginalResponse() | ||
} | ||
|
||
self.addEventListener('fetch', function (event) { | ||
const { request } = event | ||
|
||
// Bypass navigation requests. | ||
if (request.mode === 'navigate') { | ||
return | ||
} | ||
|
||
// Opening the DevTools triggers the "only-if-cached" request | ||
// that cannot be handled by the worker. Bypass such requests. | ||
if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { | ||
return | ||
} | ||
|
||
// Bypass all requests when there are no active clients. | ||
// Prevents the self-unregistered worked from handling requests | ||
// after it's been deleted (still remains active until the next reload). | ||
if (activeClientIds.size === 0) { | ||
return | ||
} | ||
|
||
const requestId = uuidv4() | ||
|
||
return event.respondWith( | ||
handleRequest(event, requestId).catch((error) => { | ||
console.error( | ||
'[MSW] Failed to mock a "%s" request to "%s": %s', | ||
request.method, | ||
request.url, | ||
error, | ||
) | ||
}), | ||
) | ||
}) | ||
|
||
function serializeHeaders(headers) { | ||
const reqHeaders = {} | ||
headers.forEach((value, name) => { | ||
reqHeaders[name] = reqHeaders[name] | ||
? [].concat(reqHeaders[name]).concat(value) | ||
: value | ||
}) | ||
return reqHeaders | ||
} | ||
|
||
function sendToClient(client, message) { | ||
return new Promise((resolve, reject) => { | ||
const channel = new MessageChannel() | ||
|
||
channel.port1.onmessage = (event) => { | ||
if (event.data && event.data.error) { | ||
return reject(event.data.error) | ||
} | ||
|
||
resolve(event.data) | ||
} | ||
|
||
client.postMessage(JSON.stringify(message), [channel.port2]) | ||
}) | ||
} | ||
|
||
function delayPromise(cb, duration) { | ||
return new Promise((resolve) => { | ||
setTimeout(() => resolve(cb()), duration) | ||
}) | ||
} | ||
|
||
function respondWithMock(clientMessage) { | ||
return new Response(clientMessage.payload.body, { | ||
...clientMessage.payload, | ||
headers: clientMessage.payload.headers, | ||
}) | ||
} | ||
|
||
function uuidv4() { | ||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { | ||
const r = (Math.random() * 16) | 0 | ||
const v = c == 'x' ? r : (r & 0x3) | 0x8 | ||
return v.toString(16) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,17 @@ | ||
import './App.css' | ||
|
||
import { Route, Router, Switch } from 'react-router-dom' | ||
import { Redirect, Route, Router, Switch } from 'react-router-dom' | ||
import { history } from 'utils/history' | ||
|
||
import { Example01 } from './examples/01' | ||
|
||
export const App = () => ( | ||
<Router history={history}> | ||
<Switch> | ||
<Route path='/page1' component={() => <h1>Page 1</h1>} /> | ||
<Route path='/page2' component={() => <h1>Page 2</h1>} /> | ||
<Route path='/page3' component={() => <h1>Page 3</h1>} /> | ||
<Route path='/page4' component={() => <h1>Page 4</h1>} /> | ||
<Route path='/page5' component={() => <h1>Page 5</h1>} /> | ||
<Route exact path='/'> | ||
<Redirect to='/example01' /> | ||
</Route> | ||
<Route path='/example01' component={Example01} /> | ||
</Switch> | ||
</Router> | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import axios from 'axios' | ||
|
||
const defaultHeaders = { | ||
'Content-Type': 'application/json', | ||
} | ||
|
||
export const instance = axios.create({ | ||
headers: defaultHeaders, | ||
baseURL: '', | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { useForm } from 'react-hook-form' | ||
|
||
import { instance } from 'api' | ||
|
||
export const Example01 = () => { | ||
const { register, handleSubmit, errors } = useForm() | ||
|
||
const onSubmit = async () => await instance.post('/example01') | ||
|
||
return ( | ||
<form onSubmit={handleSubmit(onSubmit)}> | ||
<input name='example' defaultValue='test' ref={register} /> | ||
<input name='exampleRequired' ref={register({ required: true })} /> | ||
{errors.exampleRequired && <span>This field is required</span>} | ||
<input type='submit' /> | ||
</form> | ||
) | ||
} |
Oops, something went wrong.
64fc00e
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs: