-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c5df9e8
commit cabbd68
Showing
12 changed files
with
5,328 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# EditorConfig is awesome: http://EditorConfig.org | ||
|
||
[*] | ||
end_of_line = lf | ||
indent_size = 2 | ||
indent_style = space | ||
insert_final_newline = true | ||
trim_trailing_whitespace = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
parser: '@typescript-eslint/parser' | ||
|
||
parserOptions: | ||
ecmaVersion: 2018 | ||
sourceType: 'module' | ||
project: './tsconfig.json' | ||
|
||
plugins: | ||
- simple-import-sort | ||
- filenames | ||
|
||
ignorePatterns: | ||
- coverage | ||
- dist | ||
- node_modules | ||
|
||
extends: | ||
- 'eslint:recommended' | ||
- 'plugin:node/recommended' | ||
- 'plugin:jest/recommended' | ||
- 'plugin:@typescript-eslint/eslint-recommended' | ||
- 'plugin:@typescript-eslint/recommended' | ||
- 'plugin:prettier/recommended' | ||
- 'prettier/@typescript-eslint' | ||
|
||
rules: | ||
'@typescript-eslint/explicit-module-boundary-types': 0 | ||
'@typescript-eslint/no-namespace': 0 | ||
'node/no-unsupported-features/es-syntax': 0 | ||
'node/no-unpublished-import': 0 | ||
'node/no-missing-import': [ | ||
2, | ||
{ tryExtensions: ['.js', '.json', '.ts', '.d.ts'] } | ||
] | ||
'@typescript-eslint/explicit-function-return-type': 0 | ||
'@typescript-eslint/no-explicit-any': 0 | ||
'@typescript-eslint/no-non-null-assertion': 0 | ||
'@typescript-eslint/no-use-before-define': 0 | ||
'@typescript-eslint/no-unused-vars': [ | ||
2, | ||
{ ignoreRestSiblings: true } | ||
] | ||
'@typescript-eslint/no-floating-promises': 2 | ||
'@typescript-eslint/no-require-imports': 2 | ||
'no-empty-pattern': 0 | ||
'no-console': 2 | ||
'simple-import-sort/sort': 2 | ||
'filenames/match-regex': [2, '^[0-9a-z-.]+$'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
name: Build | ||
|
||
on: | ||
push: | ||
branches: [ master ] | ||
pull_request: | ||
branches: [ master ] | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- uses: actions/setup-node@v1 | ||
with: | ||
node-version: 12 | ||
- run: yarn --frozen-lockfile | ||
- run: yarn lint:check | ||
- run: yarn build | ||
- run: yarn test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created | ||
# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages | ||
|
||
name: Node.js Package | ||
|
||
on: | ||
release: | ||
types: [created] | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- uses: actions/setup-node@v1 | ||
with: | ||
node-version: 12 | ||
- run: yarn --frozen-lockfile | ||
- run: yarn lint:check | ||
- run: yarn build | ||
- run: yarn test | ||
|
||
publish-npm: | ||
needs: build | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- uses: actions/setup-node@v1 | ||
with: | ||
node-version: 12 | ||
registry-url: https://registry.npmjs.org/ | ||
- run: yarn --frozen-lockfile | ||
- run: yarn build | ||
- run: npm publish | ||
env: | ||
NODE_AUTH_TOKEN: ${{secrets.npm_token}} | ||
|
||
publish-gpr: | ||
needs: build | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- uses: actions/setup-node@v1 | ||
with: | ||
node-version: 12 | ||
registry-url: https://npm.pkg.github.com/ | ||
- run: yarn --frozen-lockfile | ||
- run: yarn build | ||
- run: npm publish | ||
env: | ||
NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"parser": "typescript", | ||
"printWidth": 120, | ||
"singleQuote": true, | ||
"arrowParens": "always" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,47 @@ | ||
# jest-mock-http-server | ||
Test your http clients with jest | ||
# jest-mock-server | ||
Test your http clients with jest: | ||
* use Koa API to define http handlers | ||
* use Jest API to define asserts | ||
|
||
# Usage example | ||
```typescript | ||
import { MockServer } from 'jest-mock-server'; | ||
import fetch from 'node-fetch'; | ||
|
||
describe('Testing node-fetch HTTP client', () => { | ||
const server = new MockServer(); | ||
|
||
beforeAll(() => server.start()); | ||
afterAll(() => server.stop()); | ||
beforeEach(() => server.reset()); | ||
|
||
it('Receives a status over the network', async () => { | ||
const route = server | ||
.get('/') | ||
// Look ma, plain Jest API! | ||
.mockImplementationOnce((ctx) => { | ||
// ...and plain Koa API | ||
ctx.status = 200; | ||
}) | ||
.mockImplementationOnce((ctx) => { | ||
ctx.status = 201; | ||
}); | ||
|
||
// Since we did not passed any port into server constructor, server was started at random free port | ||
const url = server.getURL(); | ||
|
||
const res1 = await fetch(url); | ||
expect(res1.status).toBe(200); | ||
|
||
const res2 = await fetch(url); | ||
expect(res2.status).toBe(201); | ||
|
||
const res3 = await fetch(url); | ||
expect(res3.status).toBe(404); | ||
|
||
expect(route).toHaveBeenCalledTimes(3); // Yep, jest API again | ||
}); | ||
}); | ||
|
||
``` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"preset": "ts-jest", | ||
"testEnvironment": "node", | ||
"collectCoverage": true, | ||
"testMatch": [ | ||
"**/**/*.test.ts" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
{ | ||
"name": "jest-mock-server", | ||
"version": "0.0.1", | ||
"description": "Test your http clients with jest", | ||
"main": "dist/src/server.js", | ||
"types": "dist/src/server.d.ts", | ||
"repository": "[email protected]:DanielHreben/jest-mock-server.git", | ||
"author": "Daniel Hreben <[email protected]>", | ||
"license": "MIT", | ||
"private": false, | ||
"files": [ | ||
"dist/src" | ||
], | ||
"tags": [ | ||
"jest", | ||
"mock", | ||
"http", | ||
"server", | ||
"plugin" | ||
], | ||
"scripts": { | ||
"build": "rm -rf ./dist; tsc", | ||
"lint:check": "eslint . --ext .ts", | ||
"lint": "yarn lint:check --fix", | ||
"test": "jest" | ||
}, | ||
"devDependencies": { | ||
"@types/koa-bodyparser": "^4.3.0", | ||
"@types/koa-router": "^7.4.1", | ||
"@types/node": "^14.14.3", | ||
"@types/node-fetch": "^2.5.7", | ||
"@typescript-eslint/eslint-plugin": "^4.3.0", | ||
"@typescript-eslint/parser": "^4.3.0", | ||
"eslint": "^7.10.0", | ||
"eslint-config-prettier": "^6.12.0", | ||
"eslint-plugin-filenames": "^1.3.2", | ||
"eslint-plugin-jest": "^24.0.2", | ||
"eslint-plugin-node": "^11.1.0", | ||
"eslint-plugin-prettier": "^3.1.4", | ||
"eslint-plugin-simple-import-sort": "^5.0.3", | ||
"jest": "^26.4.2", | ||
"node-fetch": "^2.6.1", | ||
"prettier": "^2.1.2", | ||
"ts-jest": "^26.4.1", | ||
"typescript": "^4.0.3" | ||
}, | ||
"dependencies": { | ||
"@types/jest": "^26.0.14", | ||
"@types/koa": "^2.11.6", | ||
"koa": "^2.13.0", | ||
"koa-bodyparser": "^4.3.0", | ||
"koa-router": "^9.4.0" | ||
}, | ||
"peerDependencies": { | ||
"jest": "*" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
import { createServer, RequestListener, Server } from 'http'; | ||
import Koa from 'koa'; | ||
import buildBodyParser from 'koa-bodyparser'; | ||
import Router from 'koa-router'; | ||
import { AddressInfo } from 'net'; | ||
import { URL } from 'url'; | ||
|
||
interface Config { | ||
buildApp?: () => Koa; | ||
port?: number; | ||
} | ||
|
||
type Path = string | RegExp; | ||
|
||
function buildDefaultApp() { | ||
const app = new Koa(); | ||
|
||
const bodyParser = buildBodyParser({ | ||
extendTypes: { | ||
json: ['application/json'], | ||
}, | ||
}); | ||
|
||
app.use(bodyParser); | ||
|
||
return app; | ||
} | ||
|
||
export class MockServer { | ||
private router!: Router; | ||
private server?: Server; | ||
private requestListener!: RequestListener; | ||
|
||
constructor(private config: Config = {}) { | ||
this.init(); | ||
} | ||
|
||
public async start(): Promise<MockServer> { | ||
const server = createServer((req, res) => this.requestListener(req, res)); | ||
|
||
await new Promise((resolve, reject) => | ||
server | ||
.on('error', reject) | ||
.on('listening', resolve) | ||
.listen({ port: this.config.port || 0 }) | ||
); | ||
|
||
this.server = server; | ||
return this; | ||
} | ||
|
||
public get(path: Path) { | ||
return this.mockPath('get', path); | ||
} | ||
|
||
public head(path: Path) { | ||
return this.mockPath('head', path); | ||
} | ||
|
||
public post(path: Path) { | ||
return this.mockPath('post', path); | ||
} | ||
|
||
public put(path: Path) { | ||
return this.mockPath('put', path); | ||
} | ||
|
||
public delete(path: Path) { | ||
return this.mockPath('delete', path); | ||
} | ||
|
||
public options(path: Path) { | ||
return this.mockPath('options', path); | ||
} | ||
|
||
public trace(path: Path) { | ||
return this.mockPath('trace', path); | ||
} | ||
|
||
public patch(path: Path) { | ||
return this.mockPath('patch', path); | ||
} | ||
|
||
public all(path: Path) { | ||
const mock = this.getDefaultMock(); | ||
this.router.all(path, mock); | ||
return mock; | ||
} | ||
|
||
public getURL(): URL { | ||
const server = this.server; | ||
|
||
if (!server) { | ||
throw new Error('Server is not started'); | ||
} | ||
|
||
const { port } = server.address() as AddressInfo; | ||
const host = 'http://localhost'; | ||
return new URL(`${host}:${port}`); | ||
} | ||
|
||
public async stop(): Promise<MockServer> { | ||
const server = this.server; | ||
|
||
if (!server) { | ||
return this; | ||
} | ||
|
||
await new Promise((resolve, reject) => { | ||
server.close((error) => (error ? reject(error) : resolve())); | ||
}); | ||
|
||
return this; | ||
} | ||
|
||
public reset(): MockServer { | ||
this.init(); | ||
return this; | ||
} | ||
|
||
private init() { | ||
this.router = new Router(); | ||
const app = this.config.buildApp?.() || buildDefaultApp(); | ||
app.use(this.router.routes()); | ||
this.requestListener = app.callback(); | ||
} | ||
|
||
private mockPath(method: string, path: Path) { | ||
const mock = this.getDefaultMock(); | ||
this.router.register(path, [method], mock); | ||
|
||
return mock; | ||
} | ||
|
||
private getDefaultMock() { | ||
return jest.fn<ReturnType<Koa.Middleware>, Parameters<Koa.Middleware>>((ctx, next) => next()); | ||
} | ||
} |
Oops, something went wrong.