-
Notifications
You must be signed in to change notification settings - Fork 82
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
there's been the idea of a mockserver for quite a while. there has also been an implementation once that has never been finished. this is a quite versatile and lightweight approach that makes use of service workers. during development (and only if CLUSTER_URL is not set) we'll load src/mocks/handlers.ts. that file contains only a couple routes for now that have been copied from cypress and sets a cookie as else we're redirected to the login-screen. generally we can get rid of the http-service mocking AND the cypress mocking with this approach. let's go there eventually. the first use case will be the implementation of "jobs with dependencies" related stuff as i don't have a metronome build around to test against.
- Loading branch information
1 parent
65d98e1
commit 753e04a
Showing
12 changed files
with
1,407 additions
and
50 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 |
---|---|---|
@@ -0,0 +1,228 @@ | ||
/** | ||
* 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 = "ca2c3cd7453d8c614e2c19db63ede1a1"; | ||
const bypassHeaderName = "x-msw-bypass"; | ||
|
||
let clients = {}; | ||
|
||
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; | ||
const client = await event.currentTarget.clients.get(clientId); | ||
const allClients = await self.clients.matchAll(); | ||
const allClientIds = allClients.map((client) => client.id); | ||
|
||
switch (event.data) { | ||
case "INTEGRITY_CHECK_REQUEST": { | ||
sendToClient(client, { | ||
type: "INTEGRITY_CHECK_RESPONSE", | ||
payload: INTEGRITY_CHECKSUM, | ||
}); | ||
break; | ||
} | ||
|
||
case "MOCK_ACTIVATE": { | ||
clients = ensureKeys(allClientIds, clients); | ||
clients[clientId] = true; | ||
|
||
sendToClient(client, { | ||
type: "MOCKING_ENABLED", | ||
payload: true, | ||
}); | ||
break; | ||
} | ||
|
||
case "MOCK_DEACTIVATE": { | ||
clients = ensureKeys(allClientIds, clients); | ||
clients[clientId] = false; | ||
break; | ||
} | ||
|
||
case "CLIENT_CLOSED": { | ||
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; | ||
} | ||
} | ||
}); | ||
|
||
self.addEventListener("fetch", async function (event) { | ||
const { clientId, request } = event; | ||
const requestClone = request.clone(); | ||
const getOriginalResponse = () => fetch(requestClone); | ||
|
||
// 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; | ||
} | ||
|
||
event.respondWith( | ||
new Promise(async (resolve, reject) => { | ||
const client = await event.target.clients.get(clientId); | ||
|
||
if ( | ||
// Bypass mocking when no clients active | ||
!client || | ||
// Bypass mocking if the current client has mocking disabled | ||
!clients[clientId] || | ||
// Bypass mocking for navigation requests | ||
request.mode === "navigate" | ||
) { | ||
return resolve(getOriginalResponse()); | ||
} | ||
|
||
// Bypass requests with the explicit bypass header | ||
if (requestClone.headers.get(bypassHeaderName) === "true") { | ||
const modifiedHeaders = serializeHeaders(requestClone.headers); | ||
// Remove the bypass header to comply with the CORS preflight check | ||
delete modifiedHeaders[bypassHeaderName]; | ||
|
||
const originalRequest = new Request(requestClone, { | ||
headers: new Headers(modifiedHeaders), | ||
}); | ||
|
||
return resolve(fetch(originalRequest)); | ||
} | ||
|
||
const reqHeaders = serializeHeaders(request.headers); | ||
const body = await request.text(); | ||
|
||
const rawClientMessage = await sendToClient(client, { | ||
type: "REQUEST", | ||
payload: { | ||
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, | ||
}, | ||
}); | ||
|
||
const clientMessage = rawClientMessage; | ||
|
||
switch (clientMessage.type) { | ||
case "MOCK_SUCCESS": { | ||
setTimeout( | ||
resolve.bind(this, createResponse(clientMessage)), | ||
clientMessage.payload.delay | ||
); | ||
break; | ||
} | ||
|
||
case "MOCK_NOT_FOUND": { | ||
return resolve(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. | ||
return reject(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 resolve(createResponse(clientMessage)); | ||
} | ||
} | ||
}).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) { | ||
reject(event.data.error); | ||
} else { | ||
resolve(event.data); | ||
} | ||
}; | ||
|
||
client.postMessage(JSON.stringify(message), [channel.port2]); | ||
}); | ||
} | ||
|
||
function createResponse(clientMessage) { | ||
return new Response(clientMessage.payload.body, { | ||
...clientMessage.payload, | ||
headers: clientMessage.payload.headers, | ||
}); | ||
} | ||
|
||
function ensureKeys(keys, obj) { | ||
return Object.keys(obj).reduce((acc, key) => { | ||
if (keys.includes(key)) { | ||
acc[key] = obj[key]; | ||
} | ||
|
||
return acc; | ||
}, {}); | ||
} |
Oops, something went wrong.