lyrebird
Version:
A wrapper for writing more reusable and cleaner mocks using mswjs.io.
72 lines • 7.45 kB
JSON
{
"name": "lyrebird",
"version": "0.1.3",
"description": "A wrapper for writing more reusable and cleaner mocks using mswjs.io.",
"author": "Mohammad Ataei",
"license": "MIT",
"keywords": [
"msw",
"mswjs",
"api",
"mock",
"mocking",
"worker",
"prototype",
"server",
"service",
"handler"
],
"repository": {
"type": "git",
"url": "https://github.com/mammadataei/lyrebird/"
},
"bugs": {
"url": "https://github.com/mammadataei/lyrebird/issues"
},
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"exports": {
".": {
"require": "./dist/index.js",
"import": "./dist/index.mjs",
"types": "./dist/index.d.ts"
}
},
"files": [
"dist"
],
"lint-staged": {
"*.{js,ts,jsx,tsx}": "eslint --fix",
"*.{js,jsx,ts,tsx,md,html,css}": "prettier --write"
},
"peerDependencies": {
"msw": "^0.35.0"
},
"devDependencies": {
"@commitlint/cli": "^15.0.0",
"@commitlint/config-conventional": "^15.0.0",
"@types/jest": "^27.0.3",
"@typescript-eslint/eslint-plugin": "^5.4.0",
"@typescript-eslint/parser": "^5.4.0",
"axios": "^0.24.0",
"eslint": "^8.3.0",
"eslint-config-prettier": "^8.3.0",
"husky": "^7.0.0",
"jest": "^27.3.1",
"lint-staged": "^12.1.2",
"msw": "^0.35.0",
"prettier": "2.5.0",
"ts-jest": "^27.0.7",
"tsup": "^5.10.0",
"typescript": "^4.5.2"
},
"scripts": {
"build": "tsup",
"test": "jest",
"style": "prettier --write . && pnpm lint",
"lint": "eslint --ext .js,.jsx,.ts,.tsx .",
"pre-commit": "lint-staged"
},
"readme": "<br/>\n\n<h1 align='center'>Lyrebird</h1>\n\n<p align='center'>A wrapper for writing more reusable and cleaner mocks using mswjs.io.</p>\n\n<br/>\n\n## Introduction\n\nMSW is a great API mocking library for creating API mocks that you can use for\ntesting, development, and debugging. However, as the project grows, it will be\nincreasingly difficult to handle multiple mocks (e.g., different error\nresponses) for the same endpoint. You can't enable Multiple handlers for a\nsingle endpoint at once, so you may have to define your handlers as functions\nand then import them and use them as you need. As a result, there will be lots\nof imports throughout the project, which can lead to maintainability issues. And\nin the case of using mocks for manual testing in the browser, this solution is\nnot going to work.\n\nAdditionally, the MSW's API is a bit verbose. There is no doubt that it's very\nflexible, but in most cases, you won't need that much flexibility and, this can\nend up with lots of duplication and less readable code.\n\nLong story short, Lyrebird is a simple wrapper for MSW that makes it easy to\ncreate and manage more declarative and cleaner mock handlers by providing an\nabstracted interface for MSW's API and useful utilities for creating and\nmanaging mock handlers.\n\n## Features\n\n- Managing mock handlers using HandlerCollection\n- Selectively enable predefined handlers\n- Utilities for checking request parameters and payload\n- Devtools panel (soon)\n- ...\n\n## Getting started\n\nFirst, install `msw` and `lyrebird`:\n\n```bash\n# NPM\nnpm install msw lyrebird --save-dev\n\n# Yarn\nyarn add msw lyrebird --dev\n\n# PNPM\npnpm add msw lyrebird --save-dev\n```\n\nNext, create a `HandlerCollection` and define your mocks using `RestHandler`:\n\n```typescript\n// handlers.ts\n\nimport { HandlerCollection, RestHandler } from 'lyrebird'\n\nconst handlerCollection = new HandlerCollection()\n\nhandlerCollection.collect(\n new RestHandler()\n .onGet('/users')\n .reply(200, {\n users: [],\n })\n .as('getAllUsers'), // Add a name for the handler\n\n new RestHandler()\n .onPost('/register')\n .reply(200, {\n success: true,\n message: 'users registered successfully.',\n })\n .as('register'),\n)\n\nexport { handlerCollection }\n```\n\nThen set up the mock server using `setupServer()` or `setupWorker()` and\ninstantiate Lyrebird's `MockServer` passing the `handlerCollection` to it:\n\n```typescript\n// mockServer.ts\n\nimport { setupServer } from 'msw/node'\nimport { MockServer } from 'lyrebird'\nimport { handlerCollection } from './handlers'\n\nconst mockServer = setupServer()\n\n// In case that you are using jest for testing\nbeforeAll(() => mockServer.listen())\nafterAll(() => mockServer.close())\nafterEach(() => mockServer.resetHandlers())\n\nexport const server = new MockServer(mockServer, {\n collection: handlerCollection,\n})\n```\n\nNow use `server.use()` or `server.enable()` to enable your predefined mocks by\ntheir name:\n\n```typescript\nimport { server } from './mockServer'\n\ntest('should fetch all users', () => {\n server.use('getAllUsers')\n\n // ...\n})\n\ntest('new users can register', () => {\n server.enable('register')\n\n // ...\n})\n```\n\n## Recipes\n\n### Defining inline mocks\n\nIn some cases, you may need to create a one-time mock in your tests without\nusing the `HandlerCollection`. You can do this by creating a new handler and\npassing it directly to the server.use() method.\n\n```typescript\nserver.use(\n new RestHandler().onGet('/users').reply(200, {\n users: [],\n }),\n)\n```\n\n### Request parameters constraints\n\nOccasionally, you may need to check request parameters to match a specific\nvalue. To do so, you can use the `withParams()` method of the Handler instance:\n\n```typescript\n// Only the requests with 'active=true' query parameter will be handled\nconst handler = new RestHandler()\n .onGet('/users')\n .withParams({ active: 'true' })\n .reply(200, response)\n```\n\n### Request body constraints\n\nUse the `withPayload()` method of the Handler instance to ensure that incoming\nrequests contain a specific payload.\n\n```typescript\ninterface RegistrationRequest {\n email: string\n password: string\n}\n\ninterface Response {\n success: boolean\n message: string\n}\n\nconst handler = new RestHandler()\n .onPost('/register')\n .withPayload<RegistrationRequest>({\n email: 'user@example.com',\n password: 'secret',\n })\n .reply<Response>(200, {\n success: true,\n message: 'User registered successfully.',\n })\n```\n\n### Advanced handlers\n\nIf you need full control over the handler's functionality, you can directly\ndefine the MSW resolver function using the `resolve()` method of the Handler\ninstance. The resolver callback receives an object with the same utilities as\nthe resolver's parameters in MSW\n\nPlease keep in mind that when you use the resolve() method, Lyrebird will pass\nthe resolver function directly to the MSW server, so methods that control the\nbehavior of the handler like `withParams()`, `withPayload()`, or `reply()` won't\nwork, and you need to take care of its functionality.\n\n```typescript\ninterface LoginRequest {\n username: string\n password: string\n}\n\ninterface LoginResponse {\n username: string\n firstName: string\n}\n\nconst handler = new RestHandler()\n .onPost('/login')\n .resolve<LoginRequest, LoginResponse>(({ response, request, context }) => {\n const { username } = request.body\n\n return response(\n context.status(200),\n context.json({\n username,\n firstName: 'John',\n }),\n )\n })\n```\n\n## License\n\n[MIT](./LICENSE) License © 2021 [Mohammad Ataei](https://github.com/mammadataei)\n"
}