├── .editorconfig ├── .eslintrc.yaml ├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc.yaml ├── .remarkrc.yaml ├── LICENSE.md ├── README.md ├── __tests__ ├── callback.ts ├── e2e.ts ├── express.ts ├── fastify.ts ├── koa.ts └── tests.ts ├── index.ts ├── jest.config.js ├── package-lock.json ├── package.json ├── tsconfig.json └── types.d.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | max_line_length = 100 10 | trim_trailing_whitespace = true 11 | 12 | [COMMIT_EDITMSG] 13 | max_line_length = 72 14 | -------------------------------------------------------------------------------- /.eslintrc.yaml: -------------------------------------------------------------------------------- 1 | extends: 2 | - remcohaszing 3 | env: 4 | jest: true 5 | rules: 6 | import/no-extraneous-dependencies: off 7 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: [main] 7 | tags: ['*'] 8 | 9 | jobs: 10 | eslint: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: actions/setup-node@v3 15 | with: 16 | node-version: 20 17 | - run: npm ci 18 | - run: npx eslint . 19 | 20 | jest: 21 | runs-on: ubuntu-latest 22 | strategy: 23 | matrix: 24 | node-version: 25 | - 16 26 | - 18 27 | - 20 28 | steps: 29 | - uses: actions/checkout@v3 30 | - uses: actions/setup-node@v3 31 | with: 32 | node-version: ${{ matrix.node-version }} 33 | - run: npm ci 34 | - run: npm test 35 | - uses: codecov/codecov-action@v3 36 | if: ${{ matrix.node-version == 20 }} 37 | 38 | pack: 39 | runs-on: ubuntu-latest 40 | steps: 41 | - uses: actions/checkout@v3 42 | - uses: actions/setup-node@v3 43 | with: 44 | node-version: 20 45 | - run: npm ci 46 | - run: npm pack 47 | - uses: actions/upload-artifact@v3 48 | with: 49 | name: package 50 | path: '*.tgz' 51 | 52 | prettier: 53 | runs-on: ubuntu-latest 54 | steps: 55 | - uses: actions/checkout@v3 56 | - uses: actions/setup-node@v3 57 | with: 58 | node-version: 20 59 | - run: npm ci 60 | - run: npx prettier --check . 61 | 62 | remark: 63 | runs-on: ubuntu-latest 64 | steps: 65 | - uses: actions/checkout@v3 66 | - uses: actions/setup-node@v3 67 | with: 68 | node-version: 20 69 | - run: npm ci 70 | - run: npx remark --frail . 71 | 72 | tsc: 73 | runs-on: ubuntu-latest 74 | steps: 75 | - uses: actions/checkout@v3 76 | - uses: actions/setup-node@v3 77 | with: 78 | node-version: 20 79 | - run: npm ci 80 | - run: npx tsc 81 | 82 | release: 83 | runs-on: ubuntu-latest 84 | needs: 85 | - eslint 86 | - jest 87 | - pack 88 | - prettier 89 | - remark 90 | - tsc 91 | if: startsWith(github.ref, 'refs/tags/') 92 | permissions: 93 | id-token: write 94 | steps: 95 | - uses: actions/setup-node@v3 96 | with: 97 | node-version: 20 98 | registry-url: https://registry.npmjs.org 99 | - uses: actions/download-artifact@v3 100 | with: { name: package } 101 | - run: npm publish *.tgz --provenance 102 | env: 103 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 104 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /coverage/ 2 | /index.js 3 | /index.d.ts 4 | /__tests__/*.js 5 | /__tests__/*.d.ts 6 | node_modules/ 7 | *.log 8 | *.tgz 9 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | lockfile-version = 3 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /coverage/ 2 | node_modules/ 3 | /index.js 4 | /index.d.ts 5 | -------------------------------------------------------------------------------- /.prettierrc.yaml: -------------------------------------------------------------------------------- 1 | proseWrap: always 2 | semi: false 3 | singleQuote: true 4 | trailingComma: none 5 | -------------------------------------------------------------------------------- /.remarkrc.yaml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - remark-preset-remcohaszing 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright © 2019 Remco Haszing 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 6 | associated documentation files (the “Software”), to deal in the Software without restriction, 7 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 8 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or substantial 12 | portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 15 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 16 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES 17 | OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Axios Test Instance 2 | 3 | > Test NodeJS backends using Axios 4 | 5 | [![build status](https://github.com/remcohaszing/axios-test-instance/workflows/ci/badge.svg)](https://github.com/remcohaszing/axios-test-instance/actions/workflows/ci.yaml) 6 | [![codecov](https://codecov.io/gh/remcohaszing/axios-test-instance/branch/main/graph/badge.svg)](https://codecov.io/gh/remcohaszing/axios-test-instance) 7 | [![npm](https://img.shields.io/npm/v/axios-test-instance)](https://www.npmjs.com/package/axios-test-instance) 8 | [![prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://prettier.io) 9 | [![jest](https://jestjs.io/img/jest-badge.svg)](https://jestjs.io) 10 | 11 | ## Table of Contents 12 | 13 | - [Installation](#installation) 14 | - [Usage](#usage) 15 | - [Examples](#examples) 16 | - [See also](#see-also) 17 | - [License](#license) 18 | 19 | ## Installation 20 | 21 | ```sh 22 | npm install axios-test-instance 23 | ``` 24 | 25 | ## Usage 26 | 27 | 1. Create an Axios test instance by passing your app, which may be a Koa app, an Express app, or an 28 | HTTP request handler, to `setTestApp` in a `beforeAll` or `beforeEach` block. 29 | 2. Import `request` and use it in tests. 30 | 31 | ```js 32 | import { request, setTestApp } from 'axios-test-instance' 33 | 34 | import { app } from './app.js' 35 | 36 | beforeAll(async () => { 37 | await setTestApp(app) 38 | }) 39 | ``` 40 | 41 | The method above works with Jest, but it might not work with your testing framework. For this use 42 | case, a test instance can be created manually using `createInstance`. 43 | 44 | ```js 45 | import { createInstance } from 'axios-test-instance' 46 | 47 | import { app } from './app.js' 48 | 49 | let instance 50 | 51 | beforeAll(async () => { 52 | instance = await createInstance(app) 53 | }) 54 | 55 | afterAll(async () => { 56 | await instance.close() 57 | }) 58 | ``` 59 | 60 | Chances are you’re already using an Axios instance in your frontend. In this case, `patchInstance` 61 | can be used to patch this instance instead. 62 | 63 | ```js 64 | import { patchInstance } from 'axios-test-instance' 65 | 66 | import { app } from './app.js' 67 | import { request } from './request.js' 68 | 69 | let instance 70 | 71 | beforeAll(async () => { 72 | instance = await patchInstance(request, app) 73 | }) 74 | 75 | afterAll(async () => { 76 | await instance.close() 77 | }) 78 | ``` 79 | 80 | Now requests made using this instance, will be redirected to the app under test. 81 | 82 | ## Examples 83 | 84 | For usages examples, have a look at our test cases: 85 | 86 | - [Koa example](__tests__/koa.ts) 87 | - [Express example](__tests__/express.ts) 88 | - [Fastify example](__tests__/fastify.ts) (See 89 | [#2](https://github.com/remcohaszing/axios-test-instance/issues/2) for limitations) 90 | - [HTTP callback example](__tests__/callback.ts) 91 | - [End to end example](__tests__/e2e.ts) 92 | 93 | ## See also 94 | 95 | - [jest-axios-snapshot](https://github.com/remcohaszing/jest-axios-snapshot) asserts axios responses 96 | using jest snapshots. 97 | 98 | ## License 99 | 100 | [MIT](LICENSE.md) © [Remco Haszing](https://github.com/remcohaszing) 101 | -------------------------------------------------------------------------------- /__tests__/callback.ts: -------------------------------------------------------------------------------- 1 | import { type RequestListener } from 'node:http' 2 | 3 | import { request, setTestApp } from 'axios-test-instance' 4 | 5 | const app: RequestListener = (req, res) => { 6 | res.setHeader('content-type', 'application/json; charset=utf-8') 7 | res.end(JSON.stringify({ hello: 'world' })) 8 | } 9 | 10 | beforeAll(async () => { 11 | await setTestApp(app) 12 | }) 13 | 14 | it('should work with an http request listener', async () => { 15 | const { data, headers, status } = await request.get('/') 16 | expect(status).toBe(200) 17 | expect(headers).toMatchObject({ 'content-type': 'application/json; charset=utf-8' }) 18 | expect(data).toStrictEqual({ hello: 'world' }) 19 | }) 20 | -------------------------------------------------------------------------------- /__tests__/e2e.ts: -------------------------------------------------------------------------------- 1 | import { create } from 'axios' 2 | import { type AxiosTestInstance, patchInstance } from 'axios-test-instance' 3 | import { json } from 'body-parser' 4 | import * as express from 'express' 5 | 6 | // ——— Shared types ——— 7 | 8 | interface Credentials { 9 | /** 10 | * The password the user entered. 11 | */ 12 | password: string 13 | 14 | /** 15 | * The username the user entered. 16 | */ 17 | username: string 18 | } 19 | 20 | interface TokenResponse { 21 | /** 22 | * An OAuth2 access token. 23 | */ 24 | access_token: string 25 | } 26 | 27 | // ——— Backend ——— 28 | 29 | const users: Credentials[] = [ 30 | { 31 | password: 'I love krabby patties!', 32 | username: 'spongebob' 33 | } 34 | ] 35 | 36 | const backend = express() 37 | backend.use(json()) 38 | backend.post('/api/token', (req, res) => { 39 | const { password, username } = req.body 40 | const user = users.find((u) => u.username === username && u.password === password) 41 | if (user) { 42 | res.json({ access_token: 'super.secret.token' }) 43 | } else { 44 | res.status(401) 45 | } 46 | }) 47 | 48 | // ——— Frontend ——— 49 | 50 | const request = create({ baseURL: '/api' }) 51 | 52 | /** 53 | * Authorize the client side default axios instance. 54 | * 55 | * @param credentials The credentials to login with. 56 | */ 57 | async function login(credentials: Credentials): Promise { 58 | const { data } = await request.post('/token', credentials) 59 | request.defaults.headers.common.Authorization = `Bearer ${data.access_token}` 60 | } 61 | 62 | // ——— Test ——— 63 | 64 | let instance: AxiosTestInstance 65 | 66 | beforeAll(async () => { 67 | instance = await patchInstance(request, backend) 68 | }) 69 | 70 | afterAll(async () => { 71 | await instance.close() 72 | }) 73 | 74 | it('should be possible to login', async () => { 75 | await login({ 76 | password: 'I love krabby patties!', 77 | username: 'spongebob' 78 | }) 79 | expect(request.defaults.headers.common.Authorization).toBe('Bearer super.secret.token') 80 | }) 81 | -------------------------------------------------------------------------------- /__tests__/express.ts: -------------------------------------------------------------------------------- 1 | import { request, setTestApp } from 'axios-test-instance' 2 | import * as express from 'express' 3 | 4 | const app = express() 5 | app.get('/', (req, res) => { 6 | res.json({ hello: 'world' }) 7 | }) 8 | 9 | beforeAll(async () => { 10 | await setTestApp(app) 11 | }) 12 | 13 | it('should work with an express app', async () => { 14 | const { data, headers, status } = await request.get('/') 15 | expect(status).toBe(200) 16 | expect(headers).toMatchObject({ 'content-type': 'application/json; charset=utf-8' }) 17 | expect(data).toStrictEqual({ hello: 'world' }) 18 | }) 19 | -------------------------------------------------------------------------------- /__tests__/fastify.ts: -------------------------------------------------------------------------------- 1 | import { request, setTestApp } from 'axios-test-instance' 2 | import { fastify } from 'fastify' 3 | 4 | const app = fastify() 5 | app.get('/', async (req, reply) => { 6 | await reply.send({ hello: 'world' }) 7 | }) 8 | 9 | beforeAll(async () => { 10 | await setTestApp(app) 11 | }) 12 | 13 | it('should work with a fastify app', async () => { 14 | const { data, headers, status } = await request.get('/') 15 | expect(status).toBe(200) 16 | expect(headers).toMatchObject({ 'content-type': 'application/json; charset=utf-8' }) 17 | expect(data).toStrictEqual({ hello: 'world' }) 18 | }) 19 | -------------------------------------------------------------------------------- /__tests__/koa.ts: -------------------------------------------------------------------------------- 1 | import { request, setTestApp } from 'axios-test-instance' 2 | import * as Koa from 'koa' 3 | 4 | const app = new Koa() 5 | app.use((ctx) => { 6 | ctx.body = { hello: 'world' } 7 | }) 8 | 9 | beforeAll(async () => { 10 | await setTestApp(app) 11 | }) 12 | 13 | it('should work with a koa app', async () => { 14 | const { data, headers, status } = await request.get('/') 15 | expect(status).toBe(200) 16 | expect(headers).toMatchObject({ 'content-type': 'application/json; charset=utf-8' }) 17 | expect(data).toStrictEqual({ hello: 'world' }) 18 | }) 19 | -------------------------------------------------------------------------------- /__tests__/tests.ts: -------------------------------------------------------------------------------- 1 | import * as http from 'node:http' 2 | 3 | import { create } from 'axios' 4 | import { closeTestApp, createInstance, patchInstance } from 'axios-test-instance' 5 | import * as express from 'express' 6 | import { type FastifyInstance } from 'fastify' 7 | 8 | const app = express() 9 | app.get('/', (req, res) => { 10 | res.status(500) 11 | res.end() 12 | }) 13 | app.get('/redirect', (req, res) => { 14 | res.redirect('/') 15 | }) 16 | 17 | afterEach(() => { 18 | jest.restoreAllMocks() 19 | }) 20 | 21 | it('should not follow redirects', async () => { 22 | const instance = await createInstance(app) 23 | const { status } = await instance.get('/redirect') 24 | expect(status).toBe(302) 25 | await instance.close() 26 | }) 27 | 28 | it('should not throw on an error response', async () => { 29 | const instance = await createInstance(app) 30 | const { status } = await instance.get('/') 31 | expect(status).toBe(500) 32 | await instance.close() 33 | }) 34 | 35 | it('should be fine to call close twice', async () => { 36 | const instance = await createInstance(app) 37 | const result1 = await instance.close() 38 | const result2 = await instance.close() 39 | expect(result1).toBeUndefined() 40 | expect(result2).toBeUndefined() 41 | await instance.close() 42 | }) 43 | 44 | it('should restore the patched baseURL', async () => { 45 | const originalInstance = create({ baseURL: '/test' }) 46 | const testInstance = await patchInstance(originalInstance, app) 47 | expect(testInstance).toBe(originalInstance) 48 | expect(testInstance.defaults.baseURL).toMatch(/^http:\/\/127.0.0.1:\d+\/test$/) 49 | await testInstance.close() 50 | expect(testInstance.defaults.baseURL).toBe('/test') 51 | }) 52 | 53 | it('should reject close if starting the fastify server fails', async () => { 54 | const error = new Error('stub') 55 | const fakeApp = { 56 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 57 | listen: (options, cb) => (cb as any)(error, ''), 58 | server: null as unknown as http.Server, 59 | close: null as unknown as () => Promise 60 | } as Partial as FastifyInstance 61 | await expect(createInstance(fakeApp)).rejects.toThrow(error) 62 | }) 63 | 64 | it('should reject close if closing the server fails', async () => { 65 | const error = new Error('stub') 66 | const server = { 67 | listen: (port: number, host: string, cb: () => void) => cb(), 68 | address: () => ({ port: 1337 }), 69 | close(cb: (err?: Error) => void) { 70 | cb(error) 71 | } 72 | } 73 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 74 | jest.spyOn(http, 'createServer').mockReturnValue(server as any) 75 | // eslint-disable-next-line @typescript-eslint/no-empty-function 76 | const instance = await createInstance(() => {}) 77 | await expect(instance.close()).rejects.toThrow(error) 78 | }) 79 | 80 | it('should not crash when closing the default instance', async () => { 81 | const result1 = await closeTestApp() 82 | expect(result1).toBeUndefined() 83 | const result2 = await closeTestApp() 84 | expect(result2).toBeUndefined() 85 | }) 86 | 87 | it('should not crash if afterAll is not defined', () => { 88 | // @ts-expect-error This is deleted to fake a non-jest environment. 89 | delete global.afterAll 90 | jest.resetModules() 91 | 92 | expect(() => require('axios-test-instance')).not.toThrow() 93 | }) 94 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import { createServer } from 'node:http' 2 | import { type AddressInfo } from 'node:net' 3 | 4 | import { type AxiosInstance, type AxiosRequestConfig, create } from 'axios' 5 | 6 | import { type Application } from './types.js' 7 | 8 | /** 9 | * An Axios instance that is bound to a test server. 10 | */ 11 | export interface AxiosTestInstance extends AxiosInstance { 12 | /** 13 | * Close the internal http server and restore the original baseURL. 14 | */ 15 | close: () => Promise 16 | } 17 | 18 | interface RunningServer { 19 | /** 20 | * The URI to set as a base URI. 21 | */ 22 | uri: string 23 | 24 | /** 25 | * A function to close the running server. 26 | */ 27 | close: () => Promise 28 | } 29 | 30 | export { Application } 31 | 32 | /** 33 | * Start a server for the given application. 34 | * 35 | * @param app The application to start a server for. 36 | * @returns An internal server configuration. 37 | */ 38 | async function startServer(app: Application): Promise { 39 | if ('server' in app && 'listen' in app && 'close' in app) { 40 | return new Promise((resolve, reject) => { 41 | app.listen({ host: '127.0.0.1', port: 0 }, (error, uri) => { 42 | if (error) { 43 | reject(error) 44 | } else { 45 | resolve({ 46 | uri, 47 | async close() { 48 | await app.close() 49 | } 50 | }) 51 | } 52 | }) 53 | }) 54 | } 55 | const server = createServer(app instanceof Function ? app : app.callback()) 56 | await new Promise((resolve) => { 57 | server.listen(undefined, '127.0.0.1', resolve) 58 | }) 59 | const { address, port } = server.address() as AddressInfo 60 | return { 61 | uri: `http://${address}:${port}`, 62 | close: (): Promise => 63 | new Promise((resolve, reject) => { 64 | server.close((error) => { 65 | if (error) { 66 | reject(error) 67 | } else { 68 | resolve() 69 | } 70 | }) 71 | }) 72 | } 73 | } 74 | 75 | /** 76 | * Patch an Axios instance so its requests are redirected to the test server. 77 | * 78 | * Note that this will modify the input instance. 79 | * 80 | * Don’t forget to close the test instance after the test! 81 | * 82 | * @param instance The instance to patch. 83 | * @param app The HTTP callback function or Koa app to which requests will be redirected. 84 | * @returns the patched instance. 85 | * @example 86 | * import { patchInstance, AxiosTestInstance } from 'axios-test-instance'; 87 | * 88 | * import { instance } from './request'; 89 | * import app from './app'; 90 | * 91 | * let testInstance: AxiosTestInstance; 92 | * 93 | * beforeAll(async () => { 94 | * testInstance = await patchInstance(instance, app); 95 | * }); 96 | * 97 | * afterAll(async () => { 98 | * await testInstance.close(); 99 | * }); 100 | */ 101 | export async function patchInstance( 102 | instance: AxiosInstance, 103 | app: Application 104 | ): Promise { 105 | const { close, uri } = await startServer(app) 106 | const inst = instance as AxiosTestInstance 107 | const { baseURL } = instance.defaults 108 | inst.defaults.baseURL = String(new URL(baseURL || '', uri)) 109 | inst.close = async (): Promise => { 110 | inst.defaults.baseURL = baseURL 111 | await close() 112 | inst.close = (): Promise => Promise.resolve() 113 | } 114 | return inst 115 | } 116 | 117 | /** 118 | * Create an axios instance for testing an app. 119 | * 120 | * Don’t forget to close the test instance after the test! 121 | * 122 | * @param app An http callback function or a Koa app instance. 123 | * @param axiosConfig Configuration options to pass to the axios create call. 124 | * @returns An axios instance that is bound to a test server. 125 | * @example 126 | * import { createInstance, AxiosTestInstance } from 'axios-test-instance'; 127 | * 128 | * import app from './app'; 129 | * 130 | * let instance: AxiosTestInstance; 131 | * 132 | * beforeAll(async () => { 133 | * instance = await createInstance(app); 134 | * }); 135 | * 136 | * afterAll(async () => { 137 | * await instance.close(); 138 | * }); 139 | */ 140 | export function createInstance( 141 | app: Application, 142 | axiosConfig?: AxiosRequestConfig 143 | ): Promise { 144 | return patchInstance( 145 | create({ 146 | maxRedirects: 0, 147 | validateStatus: () => true, 148 | ...axiosConfig 149 | }), 150 | app 151 | ) 152 | } 153 | 154 | /** 155 | * A default axios test instance. 156 | * 157 | * Don’t forget to close the test instance after the test! 158 | * 159 | * @example 160 | * import { closeTestApp, request, setTestApp } from 'axios-test-instance'; 161 | * 162 | * import app from './app'; 163 | * 164 | * beforeAll(async () => { 165 | * await setTestApp(app); 166 | * }); 167 | * 168 | * afterAll(closeTestApp); 169 | */ 170 | export const request: AxiosTestInstance = Object.assign( 171 | create({ maxRedirects: 0, validateStatus: () => true }), 172 | { close: () => Promise.resolve() } 173 | ) 174 | 175 | /** 176 | * Close the default axios test instance. 177 | * 178 | * This can be passed directly to the `afterEach()` or `afterAll() function of the testing 179 | * framework. 180 | * 181 | * @see request for more details 182 | */ 183 | export async function closeTestApp(): Promise { 184 | await request.close() 185 | } 186 | 187 | /** 188 | * Set the test app for the default axios test instance. 189 | * 190 | * @param app An http callback function or a Koa app instance. 191 | * @returns The default axios test instance 192 | * @see request for more details 193 | */ 194 | export async function setTestApp(app: Application): Promise { 195 | await closeTestApp() 196 | return patchInstance(request, app) 197 | } 198 | 199 | if (typeof afterAll !== 'undefined') { 200 | afterAll(closeTestApp) 201 | } 202 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest' 3 | } 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "axios-test-instance", 3 | "version": "8.0.0", 4 | "description": "Test NodeJS backends using Axios", 5 | "author": "Remco Haszing ", 6 | "license": "MIT", 7 | "exports": "./index.js", 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/remcohaszing/axios-test-instance.git" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/remcohaszing/axios-test-instance/issues" 14 | }, 15 | "homepage": "https://github.com/remcohaszing/axios-test-instance#readme", 16 | "keywords": [ 17 | "axios", 18 | "express", 19 | "fastify", 20 | "http", 21 | "jest", 22 | "koa", 23 | "test", 24 | "testing" 25 | ], 26 | "files": [ 27 | "index.js", 28 | "index.d.ts", 29 | "types.d.ts" 30 | ], 31 | "funding": { 32 | "url": "https://github.com/sponsors/remcohaszing" 33 | }, 34 | "scripts": { 35 | "prepack": "tsc --noEmit false", 36 | "start": "jest --watchAll", 37 | "test": "jest" 38 | }, 39 | "dependencies": { 40 | "@types/node": "*", 41 | "axios": "^1.0.0" 42 | }, 43 | "devDependencies": { 44 | "@types/jest": "^29.0.0", 45 | "@types/koa": "^2.0.0", 46 | "body-parser": "^1.0.0", 47 | "eslint": "^8.0.0", 48 | "eslint-config-remcohaszing": "^9.0.0", 49 | "express": "^4.0.0", 50 | "fastify": "^4.0.0", 51 | "jest": "^29.0.0", 52 | "koa": "^2.0.0", 53 | "prettier": "^2.0.0", 54 | "remark-cli": "^11.0.0", 55 | "remark-preset-remcohaszing": "^1.0.0", 56 | "ts-jest": "^29.0.0", 57 | "typescript": "^5.0.0" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "lib": ["es2020"], 5 | "module": "commonjs", 6 | "moduleResolution": "node16", 7 | "noEmit": true, 8 | "strict": true, 9 | "target": "es2020" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /types.d.ts: -------------------------------------------------------------------------------- 1 | import { type RequestListener } from 'node:http' 2 | 3 | // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error 4 | // @ts-ignore 5 | import { type FastifyInstance } from 'fastify' 6 | // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error 7 | // @ts-ignore 8 | import type * as Koa from 'koa' 9 | 10 | type FastifyLike = unknown extends FastifyInstance ? never : FastifyInstance 11 | type KoaLike = unknown extends Koa ? never : Koa 12 | 13 | /** 14 | * A web server application that represents either an HTTP callback function or a Koa or Fastify 15 | * instance. 16 | */ 17 | export type Application = FastifyLike | KoaLike | RequestListener 18 | --------------------------------------------------------------------------------