Comparing version 0.1.2 to 0.2.0
@@ -0,1 +1,7 @@ | ||
/** | ||
* Mock Service Worker. | ||
* @see https://github.com/kettanaito/msw | ||
* This Service Worker is meant for development usage only. | ||
* Make sure not to include it on production. | ||
*/ | ||
self.addEventListener('install', (event) => { | ||
@@ -6,6 +12,3 @@ return self.skipWaiting() | ||
self.addEventListener('activate', (event) => { | ||
console.log( | ||
'%cMockServiceWorker is activated!', | ||
'color:green;font-weight:bold;', | ||
) | ||
console.log('%c[MSW] Activated!', 'color:green;font-weight:bold;') | ||
return self.clients.claim() | ||
@@ -16,9 +19,9 @@ }) | ||
switch (event.data) { | ||
case 'mock-activate': { | ||
self.__mockActive = true | ||
case 'MOCK_ACTIVATE': { | ||
self.__isMswEnabled = true | ||
break | ||
} | ||
case 'mock-deactivate': { | ||
self.__mockActive = false | ||
case 'MOCK_DEACTIVATE': { | ||
self.__isMswEnabled = false | ||
break | ||
@@ -29,3 +32,3 @@ } | ||
const sendMessageToClient = (client, message) => { | ||
const messageClient = (client, message) => { | ||
return new Promise((resolve, reject) => { | ||
@@ -48,13 +51,16 @@ const channel = new MessageChannel() | ||
const { clientId, request: req } = event | ||
const getOriginalResponse = () => fetch(req) | ||
const defaultResponse = () => fetch(req) | ||
event.respondWith( | ||
new Promise(async (resolve, reject) => { | ||
new Promise(async (resolve) => { | ||
const client = await event.target.clients.get(clientId) | ||
if (!client || !self.__mockActive) { | ||
return resolve(defaultResponse()) | ||
if (!client || !self.__isMswEnabled) { | ||
return resolve(getOriginalResponse()) | ||
} | ||
/* Converts "Headres" to the plain Object to be stringified */ | ||
/** | ||
* Converts "Headers" to the plain Object to be stringified. | ||
* @todo See how this handles multipe headers with the same name. | ||
*/ | ||
const reqHeaders = {} | ||
@@ -65,3 +71,3 @@ req.headers.forEach((value, name) => { | ||
const clientResponse = await sendMessageToClient(client, { | ||
const clientResponse = await messageClient(client, { | ||
url: req.url, | ||
@@ -78,20 +84,23 @@ method: req.method, | ||
if (clientResponse === 'not-found') { | ||
return resolve(defaultResponse()) | ||
if (clientResponse === 'MOCK_NOT_FOUND') { | ||
return resolve(getOriginalResponse()) | ||
} | ||
const res = JSON.parse(clientResponse, (key, value) => { | ||
return key === 'headers' | ||
? value.reduce((acc, [headerName, headerValue]) => { | ||
acc.append(headerName, headerValue) | ||
return acc | ||
}, new Headers()) | ||
: value | ||
const mockedResponse = JSON.parse(clientResponse, (key, value) => { | ||
return key === 'headers' ? new Headers(value) : value | ||
}) | ||
const mockedResponse = new Response(res.body, res) | ||
setTimeout(resolve.bind(this, mockedResponse), res.delay) | ||
}).catch(console.error), | ||
setTimeout( | ||
resolve.bind(this, new Response(mockedResponse.body, mockedResponse)), | ||
mockedResponse.delay, | ||
) | ||
}).catch((error) => { | ||
console.error( | ||
'[MSW] Failed to mock a "%s" request to "%s": %s', | ||
req.method, | ||
req.url, | ||
error, | ||
) | ||
}), | ||
) | ||
}) |
{ | ||
"name": "msw", | ||
"version": "0.1.2", | ||
"version": "0.2.0", | ||
"description": "Serverless client-side API mocking without a single change to the codebase.", | ||
"main": "lib/index.js", | ||
"bin": { | ||
"msw": "cli/msw.js" | ||
}, | ||
"scripts": { | ||
@@ -29,5 +32,7 @@ "start": "cross-env NODE_ENV=development webpack --watch", | ||
"babel-plugin-ramda": "^1.6.3", | ||
"chalk": "^2.4.1", | ||
"cross-env": "^5.2.0", | ||
"express": "^4.16.4", | ||
"jest": "^23.6.0", | ||
"node-fetch": "^2.3.0", | ||
"prettier": "^1.15.2", | ||
@@ -34,0 +39,0 @@ "ts-jest": "^23.10.4", |
@@ -0,25 +1,44 @@ | ||
<h1 align="center">MSW</h1> | ||
<p align="center">Serverless offline client-side API mocking for your applications.</p> | ||
## Features | ||
- **Serverless**. Doesn't establish any servers, lives entirely in a browser; | ||
- **Deviation-free**. Request the same resources you would in production, and let MSW handle the mocking of the respective responses; | ||
- **Mocking as a tool**. Enable/disable/change mocking logic on runtime instantly without any compilations or rebuilds. Control the MSW lifecycle from your browser's DevTools. | ||
- **Essentials**. Emulate status codes, headers, cookies, delays, and more. | ||
## Motivation | ||
### Problems of traditional mocking: | ||
There are several points that I find annoying when conducting API mocking with any solution I've found: | ||
- Often relies on a mocking server which you need to run and maintain; | ||
- Doesn't really mock requests, rather **replaces** their urls to point to a mocking server, instead of a production server; | ||
- Brings extra dependencies to your application, instead of being a dependency-free development tool; | ||
- Doesn't really mock requests, rather _replaces_ their urls to point to a mocking server, instead of a real server; | ||
- Brings extra dependencies to your application, instead of being a simple dependency-free development tool. | ||
### Benefits of `msw`: | ||
This library aims to eradicate those problems, as it takes an entirely different approach to the client-side API mocking. | ||
- **Serverless**. Doesn't establish any mocking servers whatsoever; | ||
- **Deviation-free**. Request the same resources as you would in production, let the library handle response mocking of those that match your defined routes; | ||
- **A tool**. Mocking is a development process, thus enable/disable it at any point, change the routes without any rebuilds, control the lifecycle from your browser's DevTools; | ||
## Getting started | ||
### Install | ||
### 1. Install | ||
```bash | ||
npm install msw --save-dev | ||
npm install msw --dev | ||
``` | ||
### Use | ||
### 2. Configure | ||
Run the following command in your project's root directory: | ||
```bash | ||
msw create <rootDir> | ||
``` | ||
> Replace `rootDir` with the relative path to your server's root directory (i.e. `msw create public`). | ||
This is going to copy the Mock Service Worker to the specified directory, so it could be served as a static file from your server. This makes it possible to be registered from the client application. | ||
### 3. Use | ||
```js | ||
@@ -43,7 +62,7 @@ // app/mocks.js | ||
/* Start the Service Worker */ | ||
/* Start the Mock Service Worker */ | ||
msw.start() | ||
``` | ||
Import your `mocks.js` module anywhere in your application to enable the mocking: | ||
Import your `mocks.js` module anywhere in the root of your application to enable the mocking: | ||
@@ -55,10 +74,24 @@ ```js | ||
## How does this work? | ||
## Update on reload | ||
The library spawns a ServiceWorker that notifies the library about any outgoing requests from your application. A request is than matched against the mocking routes you have defined, and is mocked with the first match. | ||
Service Workers are designed as a caching tool. However, we don't want our mocking definitions to be cached, which may result into out-of-date logic during development. | ||
It's highly recommend to **enable "Update on reload"** option in the "Application" tab of Chrome's DevTools (under "Service Workers" section). This will force Service Worker to update on each page reload, ensuring the latest logic is applied. | ||
![Service Workers: Update on reload](./media/sw-update-on-reload.png) | ||
> Read more on [The Service Worker Lifecycle](https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle). | ||
## How does it work? | ||
MSW uses Service Worker API with its primary ability to intercept requests, only instead of caching their responses it immitates them. In a nutshell, it works as follows: | ||
1. MSW spawns a dedicated Service Worker and creates a communication channel between the worker and the client. | ||
1. Service Worker then signals any outgoing requests on the page to the MSW, which attempts to match them against the defined mocking routes. | ||
1. When any match occurs, the `resolver` function is executed, and its payload is returned as the mocked response. | ||
## Browser support | ||
Please note that this library is meant to be used for **development only**. It doesn't require nor encourage you to install any ServiceWorker on the production environment. | ||
This library is meant to be used for **development only**. It doesn't require, nor encourage to install any Service Worker on the production environment. | ||
[**See browser support for ServiceWorkers**](https://caniuse.com/#feat=serviceworkers) | ||
> [**See browser support for ServiceWorkers**](https://caniuse.com/#feat=serviceworkers) |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
652848
96
1
17
9
10770
2