Skip to content

Commit 20e0921

Browse files
author
Ian Walter
authored
Merge pull request #57 from ianwalter/v2-dev
v2 dev
2 parents 61540aa + 9a6da8b commit 20e0921

File tree

14 files changed

+523
-211
lines changed

14 files changed

+523
-211
lines changed

.github/main.workflow

Lines changed: 0 additions & 23 deletions
This file was deleted.

.github/workflows/ci.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: CI
2+
on: push
3+
jobs:
4+
build:
5+
runs-on: ubuntu-latest
6+
container:
7+
image: node:12.10-slim
8+
steps:
9+
- name: Checkout
10+
uses: actions/checkout@master
11+
- name: Install
12+
run: yarn
13+
- name: Lint
14+
run: yarn lint
15+
- name: Test
16+
run: yarn test

README.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
# @ianwalter/test-server
2-
> Easily create a minimal [Koa][koaUrl] server for testing
2+
> Easily create a minimal [Koa][koaUrl] or [Express][expressUrl] server for
3+
> testing
34
45
[![npm page][npmImage]][npmUrl]
6+
[![CI][ciImage]][ciUrl]
57

68
## About
79

@@ -17,12 +19,13 @@ yarn add @ianwalter/test-server --dev
1719

1820
```js
1921
const { test } = require('@ianwalter/bff')
20-
const createTestServer = require('@ianwalter/test-server')
22+
const { createKoaServer } = require('@ianwalter/test-server')
23+
const { requester } = require('@ianwalter/requester')
2124

22-
test('got', ({ expect }) => {
23-
const server = await createTestServer()
25+
test('requester', ({ expect }) => {
26+
const server = await createKoaServer()
2427
server.use(ctx => (ctx.body = 'Hello World!'))
25-
const response = await got(server.url)
28+
const response = await requester.get(server.url)
2629
expect(response.body).toBe('Hello World!')
2730
await server.close()
2831
})
@@ -36,8 +39,11 @@ Apache 2.0 with Commons Clause - See [LICENSE][licenseUrl]
3639

3740
Created by [Ian Walter](https://iankwalter.com)
3841

42+
[koaUrl]: https://github.com/koajs/koa
43+
[expressUrl]: https://expressjs.com/
3944
[npmImage]: https://img.shields.io/npm/v/@ianwalter/test-server.svg
4045
[npmUrl]: https://www.npmjs.com/package/@ianwalter/test-server
41-
[koaUrl]: https://github.com/koajs/koa
46+
[ciImage]: https://github.com/ianwalter/test-server/workflows/CI/badge.svg
47+
[ciUrl]: https://github.com/ianwalter/test-server/actions
4248
[ctsUrl]: https://github.com/lukechilds/create-test-server
4349
[licenseUrl]: https://github.com/ianwalter/test-server/blob/master/LICENSE

express.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
const http = require('http')
2+
const express = require('express')
3+
const bodyParser = require('body-parser')
4+
const { print } = require('@ianwalter/print')
5+
const enableDestroy = require('server-destroy')
6+
7+
const defaultOptions = { cors: false }
8+
9+
module.exports = function createExpressServer (options = defaultOptions) {
10+
// Create the Exoress app instance.
11+
const app = express()
12+
13+
// Tell Express to parse requests with text content-type bodies.
14+
app.use(bodyParser.text())
15+
16+
// Tell Express to parse requests with application/json content-type bodies.
17+
app.use(bodyParser.json())
18+
19+
// Add error-handling middleware.
20+
app.use(function errorHandlingMiddleware (err, req, res, next) {
21+
print.error(err)
22+
res.status(err.statusCode || err.status || 500)
23+
})
24+
25+
// Create the server that will listen and execute the Koa app on all requests
26+
// it receives.
27+
const server = http.createServer(app)
28+
29+
// Add a destroy method to the server instance.
30+
// https://github.com/nodejs/node/issues/2642
31+
enableDestroy(server)
32+
33+
// Add a close method to the Koa app to allow the caller / receiver of the Koa
34+
// app to close the server when done with it.
35+
app.close = function close () {
36+
return new Promise(resolve => server.destroy(resolve))
37+
}
38+
39+
// Return the Koa app instance when the server has started listening.
40+
return new Promise((resolve, reject) => {
41+
server.listen(process.env.TEST_SERVER_PORT, err => {
42+
if (err) {
43+
reject(err)
44+
} else {
45+
// Add the server's port and URL to the app so it's easily accessible.
46+
app.port = server.address().port
47+
app.url = `http://localhost:${app.port}`
48+
49+
resolve(app)
50+
}
51+
})
52+
})
53+
}

index.js

Lines changed: 3 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,4 @@
1-
const http = require('http')
2-
const Koa = require('koa')
3-
const json = require('koa-json')
4-
const { print } = require('@ianwalter/print')
5-
const enableDestroy = require('server-destroy')
6-
const bodyParser = require('koa-bodyparser')
1+
const createKoaServer = require('./koa')
2+
const createExpressServer = require('./express')
73

8-
const defaultOptions = { cors: false }
9-
10-
module.exports = function createTestServer (options = defaultOptions) {
11-
// Create the Koa app instance.
12-
const app = new Koa()
13-
14-
// Add the bodyparser middleware that can parse a request body into json, etc.
15-
app.use(bodyParser({ enableTypes: ['json', 'form', 'text'] }))
16-
17-
// Add error-handling middleware.
18-
app.use(async function errorHandlingMiddleware (ctx, next) {
19-
try {
20-
await next()
21-
} catch (err) {
22-
print.error(err)
23-
ctx.status = err.statusCode || err.status || 500
24-
ctx.app.emit('error', err, ctx)
25-
}
26-
})
27-
28-
// Use middleware that automatically pretty-prints JSON responses.
29-
app.use(json())
30-
31-
// If CORS is disabled, add the Access-Control-Allow-Origin header that
32-
// accepts all requests to the response.
33-
if (!options.cors) {
34-
app.use(async function disableCorsMiddleware (ctx, next) {
35-
ctx.set('Access-Control-Allow-Origin', '*')
36-
ctx.set('Access-Control-Allow-Headers', '*')
37-
return next()
38-
})
39-
}
40-
41-
// Create the server that will listen and execute the Koa app on all requests
42-
// it receives.
43-
const server = http.createServer(app.callback())
44-
45-
// Add a destroy method to the server instance.
46-
// https://github.com/nodejs/node/issues/2642
47-
enableDestroy(server)
48-
49-
// Add a close method to the Koa app to allow the caller / receiver of the Koa
50-
// app to close the server when done with it.
51-
app.close = function close () {
52-
return new Promise(resolve => server.destroy(resolve))
53-
}
54-
55-
// Return the Koa app instance when the server has started listening.
56-
return new Promise((resolve, reject) => {
57-
server.listen(process.env.TEST_SERVER_PORT, err => {
58-
if (err) {
59-
reject(err)
60-
} else {
61-
// Add the server's port and URL to the app so it's easily accessible.
62-
app.port = server.address().port
63-
app.url = `http://localhost:${app.port}`
64-
65-
resolve(app)
66-
}
67-
})
68-
})
69-
}
4+
module.exports = { createKoaServer, createExpressServer }

koa.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
const http = require('http')
2+
const Koa = require('koa')
3+
const json = require('koa-json')
4+
const { print } = require('@ianwalter/print')
5+
const enableDestroy = require('server-destroy')
6+
const bodyParser = require('koa-bodyparser')
7+
8+
const defaultOptions = { cors: false }
9+
10+
module.exports = function createKoaServer (options = defaultOptions) {
11+
// Create the Koa app instance.
12+
const app = new Koa()
13+
14+
// Add error-handling middleware.
15+
app.use(async function errorHandlingMiddleware (ctx, next) {
16+
try {
17+
await next()
18+
} catch (err) {
19+
print.error(err)
20+
ctx.status = err.statusCode || err.status || 500
21+
ctx.app.emit('error', err, ctx)
22+
}
23+
})
24+
25+
// Add the bodyparser middleware that can parse a request body into json, etc.
26+
app.use(bodyParser({ enableTypes: ['json', 'form', 'text'] }))
27+
28+
// Use middleware that automatically pretty-prints JSON responses.
29+
app.use(json())
30+
31+
// If CORS is disabled, add the Access-Control-Allow-Origin header that
32+
// accepts all requests to the response.
33+
if (!options.cors) {
34+
app.use(async function disableCorsMiddleware (ctx, next) {
35+
ctx.set('Access-Control-Allow-Origin', '*')
36+
ctx.set('Access-Control-Allow-Headers', '*')
37+
return next()
38+
})
39+
}
40+
41+
// Create the server that will listen and execute the Koa app on all requests
42+
// it receives.
43+
const server = http.createServer(app.callback())
44+
45+
// Add a destroy method to the server instance.
46+
// https://github.com/nodejs/node/issues/2642
47+
enableDestroy(server)
48+
49+
// Add a close method to the Koa app to allow the caller / receiver of the Koa
50+
// app to close the server when done with it.
51+
app.close = function close () {
52+
return new Promise(resolve => server.destroy(resolve))
53+
}
54+
55+
// Return the Koa app instance when the server has started listening.
56+
return new Promise((resolve, reject) => {
57+
server.listen(process.env.TEST_SERVER_PORT, err => {
58+
if (err) {
59+
reject(err)
60+
} else {
61+
// Add the server's port and URL to the app so it's easily accessible.
62+
app.port = server.address().port
63+
app.url = `http://localhost:${app.port}`
64+
65+
resolve(app)
66+
}
67+
})
68+
})
69+
}

package.json

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@ianwalter/test-server",
33
"version": "1.4.3",
4-
"description": "Easily create a minimal [Koa][koaUrl] server for testing",
4+
"description": "Easily create a minimal Koa or Express server for testing",
55
"license": "SEE LICENSE IN LICENSE",
66
"main": "index.js",
77
"repository": {
@@ -15,22 +15,27 @@
1515
"koa",
1616
"testing",
1717
"unit",
18-
"mock"
18+
"mock",
19+
"express"
1920
],
2021
"author": "Ian Walter <public@iankwalter.com> (https://iankwalter.com)",
2122
"bugs": {
2223
"url": "https://github.com/ianwalter/test-server/issues"
2324
},
2425
"homepage": "https://github.com/ianwalter/test-server#readme",
2526
"files": [
26-
"index.js"
27+
"index.js",
28+
"koa.js",
29+
"express.js"
2730
],
2831
"scripts": {
29-
"lint": "eslint index.js tests",
32+
"lint": "eslint .",
3033
"test": "bff"
3134
},
3235
"dependencies": {
3336
"@ianwalter/print": "^3.2.0",
37+
"body-parser": "^1.19.0",
38+
"express": "^4.17.1",
3439
"koa": "^2.8.1",
3540
"koa-bodyparser": "^4.2.1",
3641
"koa-json": "^2.0.2",
@@ -42,7 +47,7 @@
4247
"@ianwalter/puppeteer-helper": "^4.0.0",
4348
"@ianwalter/release": "^3.0.3",
4449
"@ianwalter/renovate-config": "^1.2.0",
45-
"r2": "^2.0.1"
50+
"@ianwalter/requester": "^1.2.0"
4651
},
4752
"eslintConfig": {
4853
"root": true,

tests/express.tests.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
const { test } = require('@ianwalter/bff')
2+
const { requester } = require('@ianwalter/requester')
3+
const { createExpressServer } = require('..')
4+
5+
test('Express server created', async ({ expect }) => {
6+
const server = await createExpressServer()
7+
expect(server.port).toBeGreaterThan(0)
8+
expect(server.url).toBeTruthy()
9+
await server.close()
10+
})
11+
12+
test('Express request handler', async ({ expect }) => {
13+
const server = await createExpressServer()
14+
const msg = 'Nobody Lost, Nobody Found'
15+
server.use((req, res) => res.send(msg))
16+
const { body } = await requester.get(server.url)
17+
expect(body).toBe(msg)
18+
await server.close()
19+
})
20+
21+
test('Express json response', async ({ expect }) => {
22+
const server = await createExpressServer()
23+
server.use((req, res) => res.json({ name: 'Out There On the Ice' }))
24+
const { body } = await requester.get(server.url)
25+
expect(body).toMatchSnapshot()
26+
await server.close()
27+
})
28+
29+
test('Express json request', async ({ expect }) => {
30+
const server = await createExpressServer()
31+
server.use((req, res) => res.json(req.body))
32+
const body = { name: 'When Am I Gonna Lose You' }
33+
const response = await requester.post(server.url, { body })
34+
expect(response.body).toEqual(body)
35+
await server.close()
36+
})
37+
38+
test.skip('Express cors', async (t, page) => {
39+
const server = await createExpressServer()
40+
server.use(ctx => (ctx.body = 'Moments'))
41+
const result = await page.evaluate(
42+
url => window.fetch(url).then(response => response.text()),
43+
server.url
44+
)
45+
t.is(result, 'Moments')
46+
})
47+
48+
test('Express error', async ({ pass }) => {
49+
const server = await createExpressServer()
50+
server.use((req, res, next) => next(new Error('Test Error')))
51+
try {
52+
await requester.get(server.url)
53+
} catch (err) {
54+
pass()
55+
} finally {
56+
await server.close()
57+
}
58+
})

0 commit comments

Comments
 (0)