Skip to content

Latest commit

 

History

History
249 lines (174 loc) · 8.73 KB

CONTRIBUTING.md

File metadata and controls

249 lines (174 loc) · 8.73 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 persistance and inference between environments.
headers-utils Headers polyfill to manage request/response headers.
interceptors Provisions request interception in Node.js (internals of setupServer).
node-match-path Matches a request URL against a path.

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
$ yarn install
$ yarn start

Please use Yarn while working on this project.

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.

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.

$ yarn 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

$ yarn 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 (yarn start) while working on the tests. Keeping both compiler and test runner in watch mode boosts your productivity.

Browser integration tests

In-browser integration tests are powered by the page-with library (Chrome automation tool) and still run in Jest. These tests usually consists of two parts:

  • [test-name].mocks.ts, the actual usage example.
  • [test-name].test.ts, the test suite that loads the usage example, does actions and performs assertions.

Let's write an integration test that asserts the interception of a GET request. First, start with the *.mocks.ts file:

// test/rest-api/basic.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 basic.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/rest-api/basic.test.ts
import * as path from 'path'
import { pageWith } from 'page-with'

test('returns a mocked response', async () => {
  // Create a Chrome instance with the usage example loaded.
  const runtime = await pageWith({
    example: path(__dirname, 'basic.mocks.ts'),
  })

  // Dispatch a "GET /books" request.
  const res = await runtime.request('/books')

  expect(await res.json()).toEqual([
    {
      id: 'ea42ffcb-e729-4dd5-bfac-7a5b645cb1da',
      title: 'The Lord of the Rings',
      publishedAt: -486867600,
    },
  ])
})

Read more about all the page-with features in its documentation.

Node.js integration test

Integration tests showcase a usage example in Node.js and are often placed next to the in-browser tests. A Node.js integration test file has the *.node.test.ts suffix.

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/setup-server/basic.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,
    },
  ])
})

Running all integration tests

$ yarn test:integration

Running a single integration test

$ yarn test:integration test/rest-api/basic.test.ts

Build

Build the library with the following command:

$ yarn build