Hey! Thank you for deciding to contribute to Mock Service Worker! This page will help you land your first contribution by giving you a better understanding about the project's structure, dependencies, and development workflow.
Getting yourself familiar with the tools below will substantially ease your contribution experience.
Mock Service Worker depends on multiple other libraries.
Library name | Purpose |
---|---|
cookies | Enables cookies persistence and inference between environments. |
headers-utils | Headers polyfill to manage request/response headers. |
interceptors | Provisions request interception in Node.js (internals of setupServer ). |
There are cases when an issue originates from one of the said dependencies. Don't hesitate to address it in the respective repository, as they all are governed by the same team.
Please use the GitHub UI to fork this repository (read more about Forking a repository). Mock Service Worker has forked builds enabled in the CI, so you will see the build status of your fork's branch.
$ cd msw
$ pnpm install
$ pnpm start
Please use PNPM version 7.12 while working on this project. Guide on how to install a specific PNPM version can be found here.
# Checkout the default branch and ensure it's up-to-date
$ git checkout main
$ git pull --rebase
# Create a feature branch
$ git checkout -b feature/graphql-subscriptions
# Commit the changes
$ git add .
$ git commit
# Follow the interactive prompt to compose a commit message
# Push
$ git push -u origin feature/graphql-subscriptions
We are using Conventional Commits naming convention. It helps us automate library releases and ensure clean and standardized commit tree. Please take a moment to read about the said convention before you name your commits.
Tip: running
git commit
will open an interactive prompt in your terminal. Follow the prompt to compose a valid commit message.
Once you have pushed the changes to your remote feature branch, create a pull request on GitHub. Undergo the process of code review, where the maintainers of the library will help you get the changes from good to great, and enjoy your implementation merged to the default branch.
Please be respectful when requesting and going through the code review. Everyone on the team is interested in merging quality and well tested code, and we're hopeful that you have the same goal. It may take some time and iterations to get it right, and we will assist you throughout the process.
Build the library with the following command:
$ pnpm build
There are two levels of tests on the project:
- Unit tests, cover small independent functions.
- Integration tests, test in-browser usage scenarios.
Always begin your implementation from tests. When tackling an issue, a test for it must be missing, or is incomplete. When working on a feature, starting with a test allows you to model the feature's usage before diving into implementation.
Unit tests are placed next to the tested code. For example, if you're testing a newly added multiply
function, create a multiple.test.ts
file next to where the function is located:
$ touch src/utils/multiply.test.ts
Proceed by writing a unit test that resembles the usage of the function. Make sure to cover all the scenarios
// src/utils/multiply.test.ts
import { multiply } from './multiply'
test('multiplies two given numbers', () => {
expect(multiply(2, 3)).toEqual(6)
})
Please avoid nesting while you're testing.
Once your test is written, run it in isolation.
$ pnpm test:unit src/utils/multiply.test.ts
At this point, the actual implementation is not ready yet, so you can expect your test to fail. That's perfect. Add the necessary modules and logic, and gradually see your test cases pass.
$ pnpm test:unit
We follow an example-driven testing paradigm, meaning that each integration test represents a usage example. Mock Service Worker can be used in different environments (browser, Node.js), making such usage examples different.
Make sure that you build the library before running the integration tests. It's a good idea to keep the build running (
pnpm start
) while working on the tests. Keeping both compiler and test runner in watch mode boosts your productivity.
You can find all the browser integration tests under ./test/browser
. Those tests are run with Playwright and usually consist of two parts:
[test-name].mocks.ts
, the usage example of MSW;[test-name].test.ts
, the test suite that loads the usage example, does actions and performs assertions.
It's also a great idea to get familiar with our Playwright configuration and extensions:
Let's write an example integration test that asserts the interception of a GET request. First, start with the *.mocks.ts
file:
// test/browser/example.mocks.ts
import { rest, setupWorker } from 'msw'
const worker = setupWorker(
rest.get('/books', (req, res, ctx) => {
return res(
ctx.json([
{
id: 'ea42ffcb-e729-4dd5-bfac-7a5b645cb1da',
title: 'The Lord of the Rings',
publishedAt: -486867600,
},
]),
)
}),
)
worker.start()
Notice how there's nothing test-specific in the example? The
example.mocks.ts
file is a copy-paste example of intercepting theGET /books
request. This allows to share these mocks with the users as a legitimate example, because it is!
Once the *.mocks.ts
file is written, proceed by creating a test file:
// test/browser/example.test.ts
import * as path from 'path'
import { test, expect } from './playwright.extend'
test('returns a mocked response', async ({ loadExample, fetch }) => {
// Compile the given usage example on runtime.
await loadExample(require.resolve('./example.mocks.ts'))
// Perform the "GET /books" request in the browser.
const res = await fetch('/books')
// Assert the returned response body.
expect(await res.json()).toEqual([
{
id: 'ea42ffcb-e729-4dd5-bfac-7a5b645cb1da',
title: 'The Lord of the Rings',
publishedAt: -486867600,
},
])
})
pnpm test:browser
pnpm test:browser ./test/browser/example.test.ts
Integration tests showcase a usage example in Node.js and are often placed next to the in-browser tests. Node.js integration tests reside in the ./test/node
directory.
Similar to the browser tests, these are going to contain a usage example and the assertions over it. However, for Node.js tests there is no need to create a separate *.mocks.ts
file. Instead, keep the usage example in the test file directly.
Let's replicate the same GET /books
integration test in Node.js.
// test/node/example.test.ts
import fetch from 'node-fetch'
import { rest } from 'msw'
import { setupServer } from 'msw/node'
const server = setupServer(
rest.get('/books', (req, res, ctx) => {
return res(
ctx.json([
{
id: 'ea42ffcb-e729-4dd5-bfac-7a5b645cb1da',
title: 'The Lord of the Rings',
publishedAt: -486867600,
},
]),
)
}),
)
beforeAll(() => server.listen())
afterAll(() => server.close())
test('returns a mocked response', async () => {
const res = await fetch('/books')
expect(await res.json()).toEqual([
{
id: 'ea42ffcb-e729-4dd5-bfac-7a5b645cb1da',
title: 'The Lord of the Rings',
publishedAt: -486867600,
},
])
})
pnpm test:node
pnpm test:node ./test/node/example.test.ts
Build the library with the following command:
$ pnpm build