Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

msw

Package Overview
Dependencies
Maintainers
1
Versions
270
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

msw - npm Package Compare versions

Comparing version 0.1.1 to 0.1.2

lib/msw

15

mockServiceWorker.js
self.addEventListener('install', (event) => {
event.waitUntil(self.skipWaiting())
return self.skipWaiting()
})
self.addEventListener('activate', (event) => {
event.waitUntil(self.clients.claim())
console.log(

@@ -11,2 +10,3 @@ '%cMockServiceWorker is activated!',

)
return self.clients.claim()
})

@@ -56,2 +56,3 @@

/* Converts "Headres" to the plain Object to be stringified */
const reqHeaders = {}

@@ -78,3 +79,11 @@ req.headers.forEach((value, name) => {

const res = JSON.parse(clientResponse)
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 = new Response(res.body, res)

@@ -81,0 +90,0 @@

6

package.json
{
"name": "msw",
"version": "0.1.1",
"version": "0.1.2",
"description": "Serverless client-side API mocking without a single change to the codebase.",

@@ -11,3 +11,3 @@ "main": "lib/index.js",

"example": "node example/server",
"prepublishOnly": "NODE_ENV=production npm run build"
"prepublishOnly": "npm run build"
},

@@ -25,2 +25,3 @@ "author": {

"@babel/preset-env": "^7.1.5",
"@types/jest": "^23.3.9",
"@types/ramda": "^0.25.41",

@@ -34,2 +35,3 @@ "awesome-typescript-loader": "^5.2.1",

"prettier": "^1.15.2",
"ts-jest": "^23.10.4",
"typescript": "^3.1.6",

@@ -36,0 +38,0 @@ "webpack": "^4.25.1",

@@ -6,5 +6,11 @@ ## Motivation

- Often relies on a mocking server which you need to run and maintain;
- Doesn't really mock requests, rather **replaces** requests' urls, so they go to the mocking server, instead of the production server;
- 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;
### Benefits of `msw`:
- **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

@@ -15,13 +21,11 @@

```bash
npm install msw --save-dev
```
### Configure routes
### Use
```js
import { MSW } from 'msw'
// app/mocks.js
import { msw } from 'msw'
/* Create a new instance of MockServiceWorker */
const msw = new MSW()
/* Configure mocking routes */

@@ -31,6 +35,6 @@ msw.get(

(req, res, { status, set, delay, json }) => {
const { repoName } = req.params // acces request's params
const { repoName } = req.params // access request's params
return res(
status(403), // set custom response status
status(403), // set custom status
set({ 'Custom-Header': 'foo' }), // set headers

@@ -46,5 +50,12 @@ delay(1000), // delay the response

Import your `mocks.js` module anywhere in your application to enable the mocking:
```js
// app/index.js
import './mocks.js'
```
## How does this work?
The library spawns a ServiceWorker that broadcasts any outgoing request on a page to the application. The listener then matches the request against the schema of mocking routes, and resolves with the mocked response whenever present.
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.

@@ -51,0 +62,0 @@ ## Browser support

import * as R from 'ramda'
import { MockedResponse, ResponseTransformer } from './response'
import { ResponseTransformer } from './response'

@@ -57,9 +57,16 @@ export interface MockedContext {

const set = (name, value) =>
R.ifElse(
R.always(R.is(Object, name)),
R.mergeDeepLeft({ headers: name }),
R.assocPath(['headers', name as string], value),
)
const set = (name, value) => {
return (res) => {
if (typeof name === 'object') {
Object.keys(name).forEach((headerName) => {
res.headers.append(headerName, name[headerName])
})
} else {
res.headers.append(name, value)
}
return res
}
}
const status = (statusCode, statusText) =>

@@ -66,0 +73,0 @@ R.compose(

@@ -1,3 +0,3 @@

import MSW from './msw'
export const msw = new MSW()
export { default as msw } from './msw'
export { default as res } from './response'
export { default as context } from './context'
import * as R from 'ramda'
import parseRoute, { ParsedRoute } from './utils/parseRoutes'
import assertUrl, { Mask, ParsedUrl } from './utils/assertUrl'
import stringifyMask from './utils/stringifyMask'
import res, { MockedResponse, ResponseComposition } from './response'
import context, { MockedContext } from './context'
enum RESTMethod {
get = 'get',
post = 'post',
put = 'put',
delete = 'delete',
export enum RESTMethod {
GET = 'GET',
POST = 'POST',
PUT = 'PUT',
PATCH = 'PATCH',
OPTIONS = 'OPTIONS',
DELETE = 'DELETE',
}
type Handler = (
type Resolver = (
req: Request,

@@ -19,11 +22,7 @@ res: ResponseComposition,

interface Routes {
[method: string]: {
[route: string]: Handler
}
}
type Routes = Record<RESTMethod, { [route: string]: Resolver }>
const serviceWorkerPath = '/mockServiceWorker.js'
export default class MockServiceWorker {
export class MockServiceWorker {
worker: ServiceWorker

@@ -34,2 +33,9 @@ workerRegistration: ServiceWorkerRegistration

constructor() {
if (!('serviceWorker' in navigator)) {
console.error(
'Failed to instantiate MockServiceWorker: Your current environment does not support Service Workers.',
)
return null
}
/** @todo Consider removing event listeners upon destruction */

@@ -50,42 +56,9 @@ navigator.serviceWorker.addEventListener('message', this.interceptRequest)

return this
}
interceptRequest = (event) => {
const req = JSON.parse(event.data)
const relevantRoutes = this.routes[req.method.toLowerCase()] || {}
const parsedRoute = Object.keys(relevantRoutes).reduce<ParsedRoute>(
(acc, mask) => {
const parsedRoute = parseRoute(mask, req.url)
return parsedRoute.matches ? parsedRoute : acc
},
null,
)
if (parsedRoute === null) {
return this.postMessage(event, 'not-found')
if (typeof window !== 'undefined') {
;(window as any).msw = this
}
const handler = relevantRoutes[parsedRoute.mask]
const resolvedResponse =
handler({ ...req, params: parsedRoute.params }, res, context) || {}
if (!resolvedResponse) {
console.warn(
'Expected a mocking handler function to return an Object, but got: %s. ',
resolvedResponse,
)
}
return this.postMessage(event, JSON.stringify(resolvedResponse))
return this
}
/**
* Posts a message to the active ServiceWorker.
*/
postMessage(event, message: any) {
event.ports[0].postMessage(message)
}
start(): Promise<ServiceWorkerRegistration | void> {

@@ -96,9 +69,2 @@ if (this.workerRegistration) {

if (!('serviceWorker' in navigator)) {
console.error(
'Failed to start MockServiceWorker: Your current browser does not support Service Workers.',
)
return void null
}
navigator.serviceWorker

@@ -113,2 +79,4 @@ .register(serviceWorkerPath, { scope: '/' })

const foo = setInterval(() => this.workerRegistration.update(), 1000)
return reg

@@ -121,3 +89,3 @@ })

if (!this.workerRegistration) {
return console.warn('No active instane of Service Worker is active.')
return console.warn('No active instance of Service Worker is running.')
}

@@ -131,11 +99,60 @@

addRoute = R.curry((method: RESTMethod, route: string, handler: Handler) => {
this.routes = R.assocPath([method, route], handler, this.routes)
addRoute = R.curry((method: RESTMethod, mask: Mask, resolver: Resolver) => {
const resolvedMask = stringifyMask(mask)
this.routes = R.assocPath(
[method.toLowerCase(), resolvedMask],
resolver,
this.routes,
)
return this
})
get = this.addRoute(RESTMethod.get)
post = this.addRoute(RESTMethod.post)
put = this.addRoute(RESTMethod.put)
delete = this.addRoute(RESTMethod.delete)
get = this.addRoute(RESTMethod.GET)
post = this.addRoute(RESTMethod.POST)
put = this.addRoute(RESTMethod.PUT)
patch = this.addRoute(RESTMethod.PATCH)
options = this.addRoute(RESTMethod.OPTIONS)
delete = this.addRoute(RESTMethod.DELETE)
interceptRequest = (event) => {
const req = JSON.parse(event.data)
const relevantRoutes = this.routes[req.method.toLowerCase()] || {}
const parsedRoute = Object.keys(relevantRoutes).reduce<ParsedUrl>(
(acc, mask) => {
const parsedRoute = assertUrl(mask, req.url)
return parsedRoute.matches ? parsedRoute : acc
},
null,
)
if (parsedRoute === null) {
return this.postMessage(event, 'not-found')
}
const resolver = relevantRoutes[parsedRoute.mask as string]
const resolvedResponse =
resolver({ ...req, params: parsedRoute.params }, res, context) || {}
resolvedResponse.headers = Array.from(resolvedResponse.headers.entries())
if (!resolvedResponse) {
console.warn(
'Expected a mocking resolver function to return an Object, but got: %s. ',
resolvedResponse,
)
}
this.postMessage(event, JSON.stringify(resolvedResponse))
}
/**
* Posts a message to the active ServiceWorker.
* Uses a port of the message channel created in the ServiceWorker.
*/
postMessage(event, message: any) {
event.ports[0].postMessage(message)
}
}
export default new MockServiceWorker()
import * as R from 'ramda'
import context from './context'

@@ -8,3 +7,3 @@ export interface MockedResponse {

statusText: string
headers: Object
headers?: Headers
delay: number

@@ -23,3 +22,3 @@ }

const defaultResponse: MockedResponse = {
export const defaultResponse: MockedResponse = {
status: 200,

@@ -29,22 +28,26 @@ statusText: 'OK',

delay: 0,
headers: {
Mocked: true,
},
}
const response: ResponseComposition = (...transformers) => {
const headers = new Headers()
headers.set('Mocked', 'true')
const initialResponse = {
...defaultResponse,
headers,
}
if (transformers && transformers.length > 0) {
/**
* Ignore the arity annotation from Ramda.
* Apparently, TypeScript assumes "transformers" may be modified
* before they get into pipe as arguments, thus screams at
* potentially empty array.
* Apparently, TypeScript assumes "transformers" may be modified before
* they get into pipe as arguments, thus screams at potentially empty array.
*/
// @ts-ignore
return R.pipe(...transformers)(defaultResponse)
return R.pipe(...transformers)(initialResponse)
}
return defaultResponse
return initialResponse
}
export default response

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc