Skip to content

Latest commit

 

History

History
270 lines (185 loc) · 9.06 KB

CONTRIBUTING.md

File metadata and controls

270 lines (185 loc) · 9.06 KB

Contributing

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.

Tools

Getting yourself familiar with the tools below will substantially ease your contribution experience.

Dependencies

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.

Getting started

Fork the repository

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.

Install

$ cd msw
$ pnpm install
$ pnpm start

Please use PNPM version 8.15 while working on this project. Guide on how to install a specific PNPM version can be found here.

Git workflow

# 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

Build the library with the following command:

$ pnpm build

Tests

Testing levels

There are two levels of tests on the project:

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

Writing a unit test

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.

Running a single unit test

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.

Running all unit tests

$ pnpm test:unit

Integration tests

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.

Browser integration tests

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 { http, HttpResponse } from 'msw'
import { setupWorker } from 'msw/browser'

const worker = setupWorker(
  http.get('/books', () => {
    return HttpResponse.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 the GET /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,
    },
  ])
})
Running all browser tests
pnpm test:browser

Running a single browser test

pnpm test:browser ./test/browser/example.test.ts

Node.js integration test

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 { http, HttpResponse } from 'msw'
import { setupServer } from 'msw/node'

const server = setupServer(
  http.get('/books', () => {
    return HttpResponse.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,
    },
  ])
})
Running all Node.js tests
pnpm test:node
Running a single Node.js test
pnpm test:node ./test/node/example.test.ts

Build

Build the library with the following command:

$ pnpm build