Skip to content

Commit

Permalink
chore: use playwright/test for integration tests (#1394)
Browse files Browse the repository at this point in the history
* chore: add webpack http server

* chore: configure playwright/test

* chore: use "webpack-http-server"

* chore: remove unnecessary configs

* chore: migrate more tests to playwright

* test: migrate more tests to playwright

* chore: migrate even more test to playwright

* chore: enable new tests on ci

* test: mark "response-patching" tests as fixme

* test: migrate graphql tests to playwright

* test: migrating "msw-api" tests to playwright

* chore: migrate "msw-api"

* chore: fixing wrong testing setup

* test: migrate "response-logging" to pw/test

* chore: update "webpack-http-server"

* test: migrate "shared-worker" test to pw/test

* chore: use designated "browser" folder

* chore: add "test:node" script

* chore: split graphql tests

* chore: move "msw-api" to playwright tests

* chore: isolate node integration tests

* chore: resolve test flakiness

* test: use "list" reporter

* chore: update playwright/test

* chore: enable test retries

* chore: use 2 cores for tests on ci

* test(playwright configuration): use fullyParallel flag to isolate tests (#1532)

* test(playwright configuration): use fullyParallel flag to isolate tests

This flag makes playwright run each single test in a separate context in parallel, providing easier
isolation, teardown and better performance when running in parallel in many workers

* test(rest-api and grapghql-api tests): ensure each test has dedicated setup and teardown

Since fullyParallel flag is enabled we should consider each test as independent unit. This changes
make sure each test has separate setup and teardown.

* fix missed hook

* chore: try to ensure clearing of workerConsole

* chore: use esbuild-loader vs ts-loader

* chore: rework worker console, use parallel webpack server

* test(fallback-mode): write to fs on "beforeEach"

* chore: decrease playwright timeout to 10s

* chore: dispose of test compilations

* docs: update contributing guidelines

* chore: skip iframe integration tests

* chore: write runtime console logs

* chore: upload test artifacts

* chore: use "use.trace" from playwright

* test: fix response patching tests

* chore: restore headers-multiple test

* chore: restore iframe tests

---------

Co-authored-by: Maciej Szwarc <58426925+shwarcu@users.noreply.github.com>
  • Loading branch information
kettanaito and shwarcu authored Feb 19, 2023
1 parent 1cc61f1 commit 1df33a3
Show file tree
Hide file tree
Showing 302 changed files with 5,649 additions and 4,500 deletions.
14 changes: 12 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,18 @@ jobs:
- name: Build
run: pnpm build

- name: Integration tests
run: pnpm test:integration
- name: Node.js tests
run: pnpm test:node

- name: Browser tests
run: pnpm test:browser

- name: Upload test artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: playwright-report
path: test/browser/test-results

# Checks the library's compatibility with different
# TypeScript versions to discover type regressions.
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ stats.html
.vscode
msw-*.tgz
.husky/_
.env
.env
**/test-results
83 changes: 54 additions & 29 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ Getting yourself familiar with the tools below will substantially ease your cont

- [TypeScript](https://www.typescriptlang.org/)
- [Jest](https://jestjs.io/)
- [Playwright](https://playwright.dev/)

## Dependencies

Mock Service Worker depends on multiple other libraries.

| Library name | Purpose |
| ----------------------------------------------------------- | ------------------------------------------------------------------------ |
| [cookies](https://github.com/mswjs/cookies) | Enables cookies persistance and inference between environments. |
| [headers-utils](https://github.com/mswjs/headers-utils) | `Headers` polyfill to manage request/response headers. |
| [interceptors](https://github.com/mswjs/interceptors) | Provisions request interception in Node.js (internals of `setupServer`). |
| [node-match-path](https://github.com/mswjs/node-match-path) | Matches a request URL against a path. |
| Library name | Purpose |
| ------------------------------------------------------- | ------------------------------------------------------------------------ |
| [cookies](https://github.com/mswjs/cookies) | Enables cookies persistence and inference between environments. |
| [headers-utils](https://github.com/mswjs/headers-utils) | `Headers` polyfill to manage request/response headers. |
| [interceptors](https://github.com/mswjs/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.

Expand Down Expand Up @@ -65,6 +65,16 @@ Once you have pushed the changes to your remote feature branch, [create a pull r

> 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:

```bash
$ pnpm build
```

[jest-url]: https://jestjs.io

## Tests

### Testing levels
Expand Down Expand Up @@ -123,15 +133,20 @@ We follow an example-driven testing paradigm, meaning that each integration test
#### Browser integration tests

In-browser integration tests are powered by the [page-with][page-with-url] library (Chrome automation tool) and still run in Jest. These tests usually consists of two parts:
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 actual usage example.
- `[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.

Let's write an integration test that asserts the interception of a GET request. First, start with the `*.mocks.ts` file:
It's also a great idea to get familiar with our Playwright configuration and extensions:

- [**Playwright configuration file**](./test/browser/playwright.config.ts)
- [Playwright extensions](./test/browser/playwright.extend.ts)

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

```js
// test/rest-api/basic.mocks.ts
// test/browser/example.mocks.ts
import { rest, setupWorker } from 'msw'

const worker = setupWorker(
Expand All @@ -151,24 +166,23 @@ const worker = setupWorker(
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!
> 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:

```ts
// test/rest-api/basic.test.ts
// test/browser/example.test.ts
import * as path from 'path'
import { pageWith } from 'page-with'
import { test, expect } from './playwright.extend'

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'),
})
test('returns a mocked response', async ({ loadExample, fetch }) => {
// Compile the given usage example on runtime.
await loadExample(require.resolve('./example.mocks.ts'))

// Dispatch a "GET /books" request.
const res = await runtime.request('/books')
// 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',
Expand All @@ -179,18 +193,28 @@ test('returns a mocked response', async () => {
})
```

> Read more about all the `page-with` features in its [documentation](https://github.com/kettanaito/page-with).
##### Running all browser tests

```sh
pnpm test:browser
```

#### Running a single browser test

```sh
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. A Node.js integration test file has the `*.node.test.ts` suffix.
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.

```ts
// test/setup-server/basic.test.ts
// test/node/example.test.ts
import fetch from 'node-fetch'
import { rest } from 'msw'
import { setupServer } from 'msw/node'
Expand All @@ -214,6 +238,7 @@ 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',
Expand All @@ -224,16 +249,16 @@ test('returns a mocked response', async () => {
})
```

#### Running all integration tests
##### Running all Node.js tests

```bash
$ pnpm test:integration
```sh
pnpm test:node
```

#### Running a single integration test
##### Running a single Node.js test

```bash
$ pnpm test:integration test/rest-api/basic.test.ts
```sh
pnpm test:node ./test/node/example.test.ts
```

## Build
Expand Down
17 changes: 11 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@
"clean": "rimraf ./lib",
"lint": "eslint \"{cli,config,src,test}/**/*.ts\"",
"build": "cross-env NODE_ENV=production tsup",
"test": "pnpm test:unit && pnpm test:integration",
"test": "pnpm test:unit && pnpm test:node && pnpm test:browser",
"test:unit": "cross-env BABEL_ENV=test jest --maxWorkers=3",
"test:integration": "jest --config=./test/jest.config.js --maxWorkers=1",
"test:node": "jest --config=./test/jest.config.js",
"test:browser": "playwright test -c ./test/browser/playwright.config.ts",
"test:ts": "ts-node test/typings/run.ts",
"prepare": "pnpm simple-git-hooks init",
"prepack": "pnpm build",
Expand Down Expand Up @@ -111,10 +112,12 @@
"@babel/preset-env": "^7.16.11",
"@commitlint/cli": "^16.1.0",
"@commitlint/config-conventional": "^16.0.0",
"@open-draft/test-server": "^0.2.3",
"@open-draft/test-server": "^0.4.2",
"@ossjs/release": "^0.4.0",
"@playwright/test": "^1.30.0",
"@swc/core": "^1.3.15",
"@swc/jest": "^0.2.23",
"@types/express": "^4.17.17",
"@types/fs-extra": "^9.0.13",
"@types/jest": "26",
"@types/json-bigint": "^1.0.1",
Expand All @@ -129,9 +132,11 @@
"cross-env": "^7.0.3",
"cross-fetch": "^3.1.5",
"cz-conventional-changelog": "3.3.0",
"esbuild-loader": "^2.21.0",
"eslint": "^7.30.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
"express": "^4.18.2",
"fs-extra": "^10.0.0",
"fs-teardown": "^0.3.0",
"jest": "26",
Expand All @@ -143,13 +148,13 @@
"rimraf": "^3.0.2",
"simple-git-hooks": "^2.8.0",
"statuses": "^2.0.0",
"ts-loader": "^9.2.6",
"ts-node": "^10.1.0",
"ts-node": "^10.9.1",
"tsup": "^5.12.8",
"typescript": "^4.9.3",
"url-loader": "^4.1.1",
"webpack": "^5.68.0",
"webpack-dev-server": "^3.11.2"
"webpack-dev-server": "^3.11.2",
"webpack-http-server": "^0.5.0"
},
"peerDependencies": {
"typescript": ">= 4.4.x <= 4.9.x"
Expand Down
Loading

0 comments on commit 1df33a3

Please sign in to comment.