├── .node-version ├── test ├── mocha.opts ├── fixtures │ └── mock-parser.js └── lib.test.js ├── .travis.yml ├── .npmignore ├── CHANGELOG.md ├── .gitignore ├── package.json ├── lib └── index.js └── README.md /.node-version: -------------------------------------------------------------------------------- 1 | 7.2.1 -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --ui mocha-ui-exports 2 | --reporter spec -------------------------------------------------------------------------------- /test/fixtures/mock-parser.js: -------------------------------------------------------------------------------- 1 | module.exports = (opts) => { 2 | module.exports.opts = opts; 3 | return (q,r,n) => n(opts.throw) 4 | } 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6" 4 | - "7" 5 | - "8" 6 | - "10" 7 | - "12" 8 | - "14" 9 | - "16" 10 | - "18" 11 | - "20" 12 | 13 | after_success: 14 | npm run coveralls 15 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | temp 2 | tmp 3 | tmp.* 4 | test 5 | coverage 6 | reports 7 | .gitignore 8 | .npmignore 9 | .hgignore 10 | .settings 11 | .node-version 12 | *.njsproj 13 | *.log 14 | *.tmp 15 | *.fpr 16 | Jenkinsfile 17 | Dockerfile 18 | sonar-project.properties -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | mock-web-server 2 | ================ 3 | 4 | ## 0.9.2 5 | - support preResponse hook 6 | - change lisence to ISC 7 | 8 | ## 0.9.1 9 | - fix issue #1 - after .reset(response) - server still responds with old response 10 | 11 | ## 0.9.0 12 | - Initial release 13 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #Generated files 2 | coverage 3 | *.js.map 4 | apps/monitor/web/monitorResults.json 5 | 6 | #pacakge manager folder 7 | node_modules 8 | package-lock.json 9 | 10 | #OS 11 | .DS_Store 12 | .Trashes 13 | thumbs.db 14 | 15 | #IDE & Workspace files 16 | .idea 17 | .jshintrc 18 | .settings 19 | yarn.lock 20 | *.iml 21 | build/Release 22 | .lock-wscript 23 | *_local.js* 24 | *_override.js* 25 | 26 | #Runtime & Tmp files 27 | logs 28 | pids 29 | tmp 30 | tmp* 31 | *.tmp 32 | *.log 33 | *.wmlog 34 | *.pid 35 | *.seed 36 | .lock-wscript 37 | dump.rdb 38 | erl_crash.dump -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mock-web-server", 3 | "version": "0.9.2", 4 | "description": "", 5 | "main": "lib", 6 | "dependencies": { 7 | "body-parser": "^1.18.2" 8 | }, 9 | "devDependencies": { 10 | "async": "^2.5.0", 11 | "coveralls": "^3.0.0", 12 | "istanbul": "*", 13 | "mocha": "^4.0.0", 14 | "mocha-ui-exports": "^1.1.0", 15 | "request": "^2.83.0", 16 | "should": "^13.1.3" 17 | }, 18 | "license": "ISC", 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/osher/mock-web-server.git" 22 | }, 23 | "publishConfig": { 24 | "registry": "https://registry.npmjs.org/" 25 | }, 26 | "scripts": { 27 | "test": "mocha", 28 | "posttest": "npm run cover", 29 | "prepush": "mocha", 30 | "cover": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot", 31 | "coveralls": "istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- --reporter dot && cat coverage/lcov.info | coveralls --verbose" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | const bodyParser = require('body-parser'); 2 | const mockSvrFactory = module.exports = (response, { bodyParsers = ['json','raw','text',{urlencoded:{extended:true}}]} = {}) => { 3 | //support array of: 4 | // - string - name of factory to invoke without settings 5 | // if name is not found to be a method of body-parser - it's assumed to be a module to require 6 | // - object who's first key is parser type and value is configs to pass to this factory 7 | // if name is not found to be a method of body-parser - it's assumed to be a module to require 8 | // - custom body-parser function 9 | const parsers = mockSvrFactory.mapParsers(bodyParsers); 10 | //a REPL-friendly request view 11 | const collectAccepted = ({ httpVersion, method, url, headers, rawHeaders, upgrade, body, trailers, rawTrailers }, parseError) => { 12 | const mapped = {httpVersion, method, url, headers, rawHeaders, upgrade, parseError, body, trailers, rawTrailers}; 13 | accepted.push(mapped); 14 | return mapped; 15 | }; 16 | const svr = require('http').createServer((q,r) => { 17 | parseBody(parsers, q,r, (parseError) => { 18 | const acc = collectAccepted(q, parseError); 19 | {//emit response 20 | const { status, body, headers = {} } = 21 | 'function' == typeof response.preResponse 22 | ? Object.assign({}, response, response.preResponse(acc, response) || response) 23 | : response; 24 | Object.keys(headers).forEach(h => r.setHeader(h, headers[h])) 25 | r.statusCode = status || 200; 26 | r.end( JSON.stringify(body) ) 27 | } 28 | }) 29 | }); 30 | const accepted = []; 31 | //return a REPL friendly interface module 32 | const impl = { 33 | response, 34 | accepted, 35 | reset: (r) => { accepted.length = 0; if (r) impl.response = response = r; return impl }, 36 | listen: (...args) => { svr.listen.apply(svr, args); return impl }, 37 | close: (done) => { svr.close(); if (done) done() } 38 | } 39 | return impl 40 | } 41 | 42 | function parseBody(parsers, q, r, done) { 43 | let i = 0; 44 | next() 45 | function next(e) { 46 | if (e) return done(e); 47 | const parser = parsers[i++]; 48 | if (!parser) return done(); 49 | parser(q,r, next) 50 | } 51 | } 52 | 53 | module.exports.mapParsers = (bodyParsers) => bodyParsers.map(parser => { 54 | switch(typeof parser) { 55 | case 'string': 56 | return bodyParser[parser] ? bodyParser[parser]({}) : require(parser)({}) 57 | case 'object': 58 | for (type in parser) 59 | return bodyParser[type] ? bodyParser[type](parser[type]) : require(type)(parser[type]) 60 | case 'function': 61 | default: 62 | return parser 63 | } 64 | }); 65 | -------------------------------------------------------------------------------- /test/lib.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const request = require('request'); 3 | const asyn = require('async'); 4 | const sut = require('../'); 5 | 6 | var svr; 7 | 8 | module.exports = 9 | { "mock-web-server" : 10 | { "should be a factory function that names 1 arguments - response (it has an optional 2nd param for options)" : 11 | () => Should(sut).be.a.Function().have.property('length', 1) 12 | , "when provided only a response object" : 13 | { beforeAll: () => { try { svr = sut({}) } catch(e) { svr = e } } 14 | , "should not fail and return an server instance" : () => Should(svr).not.be.an.Error() 15 | } 16 | , "when provided a response object and the optional config object" : 17 | { beforeAll: () => { try { svr = sut({}, {}) } catch(e) { svr = e } } 18 | , "should not fail and return an server instance" : () => Should(svr).not.be.an.Error() 19 | } 20 | , "a server instance obtained by the factory" : block(() => { 21 | const response = {status: 201, headers: {'x-foo': 'bar'}, body: {ok: true}}; 22 | let foundRes; 23 | let foundErr; 24 | return { 25 | beforeAll: () => { svr = sut(response) } 26 | , "supported API:" : 27 | { "method .listen(port, done) to start the server" : 28 | () => Should(svr).have.property('listen').be.a.Function() 29 | , "method .close(done) to close it" : 30 | () => Should(svr).have.property('close').be.a.Function() 31 | , "attribute .response as the provided response" : 32 | () => Should(svr).have.property('response').equal(response) 33 | , "attribute .accepted as array of accepted requests" : 34 | () => Should(svr).have.property('accepted').be.an.Array() 35 | , "method .reset() to clear the accepted requests and optionally - reset the response": 36 | () => Should(svr).have.property('reset').be.a.Function() 37 | } 38 | , "starting and closing the server should work" : 39 | { timeout: "5s" 40 | , beforeAll: 41 | (next) => svr.listen(3030, next) 42 | , "and the server should serve requests with" : 43 | { beforeAll: 44 | (next) => request({ url: 'http://localhost:3030/', json: true }, (e, r) => { 45 | foundErr = e; 46 | foundRes = r; 47 | next() 48 | }) 49 | , "the provided status code" : 50 | () => Should(foundRes).have.property('statusCode', response.status) 51 | , "the provided headers" : 52 | () => Should(foundRes).have.property('headers').have.property('x-foo', 'bar') 53 | , "the provided body" : 54 | () => Should(foundRes).have.property('body').eql(response.body) 55 | } 56 | , "and server keeps a REPL-friendly view of the accepted requests that is cleared with .reset()" : 57 | { beforeAll: 58 | (done) => { 59 | svr.reset(); 60 | asyn.waterfall([ 61 | (next) => request({ url: 'http://localhost:3030/qq', json: true }, () => next()), 62 | (next) => request({ url: 'http://localhost:3030/aa', method: 'POST', json: true }, () => next()), 63 | (next) => request({ url: 'http://localhost:3030/bb', json: true }, () => next()), 64 | ], done) 65 | } 66 | , "found 3 requests": 67 | () => Should(svr.accepted).have.property('length', 3) 68 | , "views are serializable": //will throw on circular references... 69 | () => JSON.stringify( svr.accepted ) 70 | , "structure of a request view should contain" : 71 | "httpVersion, method, url, headers, rawHeaders, upgrade, body, trailers, rawTrailers" 72 | .split(', ') 73 | .reduce((suite, key) => Object.assign(suite, 74 | { [key]: 75 | () => Should( svr.accepted.find( res => 'undefined' == typeof res[key] ) ).be.Undefined() 76 | }) 77 | , {} 78 | ) 79 | , ".reset() returns the interface and clears the accepted array" : 80 | () => Should(svr.reset()) 81 | .have.property('accepted') 82 | .be.an.Array() 83 | .have.property('length', 0) 84 | , ".reset(response) returns the interface and sets the response" : 85 | (done) => Should(svr.reset({body: {set: true}})) 86 | .have.property('response', {body: {set: true}}) 87 | && request({ url: 'http://localhost:3030/dd', json: true}, (e,r,b) => { 88 | Should(b).eql({set: true}); 89 | done() 90 | }) 91 | } 92 | , afterAll: 93 | () => svr.close() 94 | } 95 | , "when response object has a preResponse hook" : 96 | { beforeAll: 97 | next => svr.listen(3030, next), 98 | "and the response hook returns an object with cascading properties" : 99 | { beforeAll: 100 | () => svr.reset( 101 | { status: 299 //<-- will be taken from response object 102 | , headers: { 'x-static': 'true' } //<-- will be overriden 103 | , preResponse: 104 | () => ({ headers: { 'x-dynamic': 'true' }, body: { generated: true }}) 105 | } 106 | ) 107 | , "the server should serve request with the cascaded attributes returned by the hook": 108 | { beforeAll: 109 | (next) => request({ url: 'http://localhost:3030/', json: true }, (e, r) => { 110 | foundErr = e; 111 | foundRes = r; 112 | next() 113 | }) 114 | , "the provided status code" : 115 | () => Should(foundRes).have.property('statusCode', 299) 116 | , "the provided headers" : 117 | () => Should(foundRes).have.property('headers').have.property('x-dynamic', 'true') 118 | , "the provided body" : 119 | () => Should(foundRes).have.property('body').eql({ generated: true }) 120 | } 121 | } 122 | , "and the response hook mutates current response using `this`" : 123 | { beforeAll: 124 | () => svr.reset({ 125 | preResponse: function() { 126 | this.status = 288; 127 | this.headers = { 'x-dynamic': 'very' }; 128 | this.body = { gen: 'erated' } 129 | } 130 | }) 131 | , "the server should serve request with response mutated by the hook" : 132 | { beforeAll: 133 | (next) => request({ url: 'http://localhost:3030/', json: true }, (e, r) => { 134 | foundErr = e; 135 | foundRes = r; 136 | next() 137 | }) 138 | , "the provided status code" : 139 | () => Should(foundRes).have.property('statusCode', 288) 140 | , "the provided headers" : 141 | () => Should(foundRes).have.property('headers').have.property('x-dynamic', 'very') 142 | , "the provided body" : 143 | () => Should(foundRes).have.property('body').eql({ gen: 'erated' }) 144 | } 145 | } 146 | , afterAll: 147 | () => svr.close() 148 | } 149 | , "closing the server with a callback" : 150 | { "should call the callback as well as closing the server" : 151 | (done) => asyn.waterfall([ 152 | (next) => svr.listen(3030, next), 153 | (next) => svr.close(next) 154 | ], done) 155 | } 156 | , "an error passed by body-parser" : 157 | { beforeAll: 158 | (done) => { 159 | svr = sut({}, { bodyParsers: 160 | [ { "../test/fixtures/mock-parser" : 161 | { throw: Object.assign(new Error('oups'), {mock: 'yup, it is'}) 162 | } 163 | } 164 | ] 165 | }); 166 | foundRes = foundErr = null; 167 | asyn.waterfall( 168 | [ (next) => svr.listen(3030, next) 169 | , (next) => request('http://localhost:3030/foo', (e, r) => { next() }) 170 | ] 171 | , done 172 | ) 173 | } 174 | , afterAll: 175 | () => svr.close() 176 | , "should be collected to the request view as .parseError" : 177 | () => Should(svr.accepted[0]) 178 | .have.property('parseError') 179 | .have.property('mock', 'yup, it is') 180 | } 181 | } 182 | }) 183 | } 184 | , "~internals" : 185 | { ".mapParsers(parsers)" : 186 | { "should be a function that names 1 argument - parsers" : 187 | () => Should(sut.mapParsers).be.a.Function().have.property('length', 1) 188 | , "when called with an object element" : 189 | { "should not fail" : 190 | () => Should(sut.mapParsers([{json: {special: "options"}}])).be.an.Array().have.property('length',1) 191 | } 192 | , "when called with a function element" : 193 | { "should not fail" : 194 | () => Should(sut.mapParsers([(q,r,n) => n()])).be.an.Array().have.property('length',1) 195 | } 196 | , "when called with a string element that is not a body-parser built-in" : 197 | { "should not fail and map it to an instance produced by the required module" : 198 | () => Should(sut.mapParsers(["../test/fixtures/mock-parser"])).be.an.Array().have.property('length',1) 199 | } 200 | , "when called with an object element who's first key is not a body-parser built-in" : 201 | block(() => { 202 | let res; 203 | let err; 204 | return { 205 | beforeAll: 206 | () => { 207 | try{ res = sut.mapParsers([ {"../test/fixtures/mock-parser": { foo: 'bar' }}]) } 208 | catch(e) { err = e } 209 | } 210 | , "should not fail" : 211 | () => Should.not.exist(err) 212 | , "should map it to an instance produced by the required module" : 213 | () => Should(res).be.an.Array().have.property('length',1) 214 | , 'should pass it the arguments' : 215 | () => Should(require('../test/fixtures/mock-parser').opts).eql( {foo: 'bar'} ) 216 | } 217 | }) 218 | } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mock-web-server [![Build Status](https://secure.travis-ci.org/osher/mock-web-server.png?branch=master)](http://travis-ci.org/osher/mock-web-server) [![Coverage Status](https://coveralls.io/repos/github/osher/mock-web-server/badge.svg)](https://coveralls.io/github/osher/mock-web-server) 2 | A lightweight simple helper utility to mock web-servers 3 | 4 | ## Synopsis 5 | Consider working on a API web client whose concerns are: 6 | - format user-provided arguments into the web protcol and fire the request 7 | - expect a reply in a given format 8 | - unwrap data from protocol envelopes and yield/resolve them to the user 9 | - handle net, http and logical errors 10 | 11 | Consider the API server to be an expansive resource to access: 12 | - it could require API keys 13 | - it could be tethering your requests and fail builds 14 | - it risk resulting with monetary transactions 15 | - or whatever reason that drove you to decide to develop with a mock server 16 | 17 | 18 | This utility helps you launch the simplest tiniest nodejs actual web-server, 19 | bind it to a port and configure it to play the role of the real API server. 20 | 21 | **But the real intention is to faciliate testing.** 22 | The good part - is that you run it in-process to your test suite, 23 | and therefore can inspect progrmatically what requests it received (and perform asserts against them) 24 | and easily manipulate the response it emits. 25 | 26 | ## Example - the common simple case: 27 | This example uses a simple static response descriptor. 28 | Every time the server receives a request - it will: 29 | 1. capture a REPL-friendly detailed view of the request. 30 | 2. respond with the same respopnse, no matter what are the characteristics of the request. 31 | 32 | You can replace the response descriptor using `svr.reset(newResponseDescriptor)`. 33 | 34 | This example uses mocha, however - it can be used with every any test runner. 35 | 36 | ```javascript 37 | const mockSvrFactory = require('mock-web-server'); 38 | const sut = require('../lib/my-web-client'); 39 | const Should = require('should'); 40 | const request = require('request'); 41 | 42 | describe('lib/my-web-client', () => { 43 | let client; 44 | before(() => client = sut({baseUrl: "http://localhost:3030"})); 45 | 46 | it('should be a module object', 47 | () => Should(sut).be.an.Object() 48 | ); 49 | it('should have .get API', () => { 50 | () => Should(sut).have.properties(['get']) 51 | }); 52 | 53 | describe('when calling .get(id) with a valid id', () => { 54 | let svr = mockSvrFactory({ 55 | status: 200, 56 | headers: { 'content-type' : 'application/json' }, 57 | body: { 58 | status: 'OK', 59 | data: { 60 | entity: { id: 333, note: 'it is what it is' } 61 | } 62 | } 63 | }); 64 | before((done) => svr.listen(3030, done)); 65 | after(() => svr.close()); 66 | 67 | 68 | describe('and the server responds with valid response', () => { 69 | let foundErr, foundRes; 70 | beforeAll(() => { 71 | svr.reset() 72 | return client.get(333) 73 | .then(entity => foundRes = entity) 74 | .catch(err => foundErr = err) 75 | }); 76 | 77 | it('should send content type header (app/json)', () => { 78 | Should(svr.accepted[0]) 79 | .have.property('headers') 80 | .have.property('content-type', 'application/json') 81 | }); 82 | 83 | it('should hit the correct URL', () => { 84 | Should(svr.accepted[0]) 85 | .have.properties({ 86 | method: 'GET', 87 | url: 'http://localhost:3030/entity/333' 88 | }) 89 | }); 90 | 91 | it('should resolve to the entity, unwrapped from protocol envelopes', () => { 92 | Should(foundRes).eql({ id: 333, note: 'it is what it is' }) 93 | }) 94 | }) 95 | 96 | describe('and the server responds with a malformed response', () => { 97 | let foundErr, foundRes; 98 | beforeAll(() => { 99 | //replace the response to a malformed response 100 | svr.reset({status: 200, body: {}, headres: {} }); 101 | return client.get(333) 102 | .then(entity => foundRes = entity) 103 | .catch(err => foundErr = err) 104 | }) 105 | 106 | it('should reject with a friendly error', () => { 107 | Should(foundErr).have.property('message').match(/Bad response from backend service/) 108 | Should(foundErr).have.property('id', 333); 109 | Should(foundErr).have.property('innerError').be.an.Error(); 110 | }) 111 | }) 112 | 113 | describe('and the server does not respond at all', () => { 114 | let foundErr, foundRes; 115 | beforeAll(() => { 116 | //replace the response to a malformed response 117 | svr.close(); 118 | return client.get(333) 119 | .then(entity => foundRes = entity) 120 | .catch(err => foundErr = err) 121 | }) 122 | 123 | it('should reject with a friendly error', () => { 124 | Should(foundErr).have.property('message').match(/No response from backend service/) 125 | Should(foundErr).have.property('id', 333); 126 | Should(foundErr).have.property('innerError').be.an.Error(); 127 | }) 128 | }) 129 | }) 130 | }) 131 | 132 | ``` 133 | 134 | ## Example - logic for dynamic responses 135 | (since: `0.9.2`) 136 | 137 | In most cases, a static response to a single API is enough. 138 | However, sometimes you need your mock server to support few endpoints, and/or control for a non-idempotent API that accumulates state between responses. 139 | 140 | For this, you can provide an `preResponse` hook. The hook can map an accepted request to a response, or mutate the response object behind the server. 141 | * The hook should be synchronous. 142 | * The hook is passed the accepted request view as a 1st argument. 143 | * The hook is passed the response descriptor the server holds as a 2nd parameter. 144 | * The hook is also called on the context of the response descriptor. 145 | 146 | The server will keep collecting REPL-friendly views of the accepted requests as usual, so you can ask it what it heard from your tested client. 147 | 148 | ### mapper preResponse hook 149 | 150 | If you return a value from your preResponse hook - its props cascades their equivalents on the response descriptor the server holds. 151 | 152 | In this example, the response descriptor the sever is initiated with defaults to a reply of *Not-found*. However, when the uri represents a supported entry - the returned object cascades the `status: 200` and the `body` for that entry. 153 | 154 | ```js 155 | const routes = { 156 | '/api/user/1': { name: 'John Snow '}, 157 | '/api/user/2': { name: 'John Doe '}, 158 | }; 159 | const svr = mockSvrFactory({ 160 | status: 404, 161 | headers: { 162 | 'Content-Type': 'application/json; charset: utf-8', 163 | }, 164 | body: { err: 'not-found' }, 165 | preResponse: ({ uri }) => routes[uri] && ({ status: 200, body: this.routes[uri] }), 166 | }); 167 | ``` 168 | 169 | ### mutator preResponse hook 170 | 171 | Using the hook as a mutator lets you manage a simple in-memory state on the response descriptor. 172 | If you don't want to manage it in a closure - you can use this object as a context object. 173 | 174 | The response descriptor is both passed to the hook as a 2nd argument, and used as the context on which the hook is called, so you can use `this`, or an arrow-function according to your preferences. 175 | 176 | This example uses a queue of bodies on the response descriptor, and accesses it via the `this` keyword. 177 | 178 | ```js 179 | const faker = require('faker'); 180 | const svr = mockSvrFactory({ 181 | queue: [{ one: 1 }, { two: 2 }, { three: 3 }], 182 | preResponse: ({ method }, response) => { 183 | const body = this.queue.unshift(); 184 | if (!body) return { status: 404, body: { status: 'empty' } }; 185 | 186 | return { status: 200, body }; 187 | }, 188 | }); 189 | ``` 190 | 191 | This example uses a `sum` counter on the response desriptor, and accesses it via the 2nd argument: 192 | 193 | ```js 194 | const svr = mockSvrFactory({ 195 | sum: 0, 196 | preResponse: ({ body: { sum = 0 } }, response) => { 197 | this.sum += sum; 198 | return { status: 200, body: { requests: svr.accepted.length, sum: this.sum } }; 199 | }, 200 | }); 201 | ``` 202 | 203 | ## Specs 204 | 205 | ``` 206 | 207 | mock-web-server 208 | ✓ should be a factory function that names 1 arguments - response (it has an optional 2nd param for options) 209 | when provided only a response object 210 | ✓ should not fail and return an server instance 211 | when provided a response object and the optional config object 212 | ✓ should not fail and return an server instance 213 | a server instance obtained by the factory 214 | supported API: 215 | ✓ method .listen(port, done) to start the server 216 | ✓ method .close(done) to close it 217 | ✓ attribute .response as the provided response 218 | ✓ attribute .accepted as array of accepted requests 219 | ✓ method .reset() to clear the accepted requests and optionally - reset the response 220 | starting and closing the server should work 221 | and the server should serve requests with 222 | ✓ the provided status code 223 | ✓ the provided headers 224 | ✓ the provided body 225 | and server keeps a REPL-friendly view of the accepted requests that is cleared with .reset() 226 | ✓ found 3 requests 227 | ✓ views are serializable 228 | ✓ .reset() returns the interface and clears the accepted array 229 | ✓ .reset(response) returns the interface and sets the response 230 | structure of a request view should contain 231 | ✓ httpVersion 232 | ✓ method 233 | ✓ url 234 | ✓ headers 235 | ✓ rawHeaders 236 | ✓ upgrade 237 | ✓ body 238 | ✓ trailers 239 | ✓ rawTrailers 240 | when response object has a preResponse hook 241 | and the response hook returns an object 242 | the server should serve request with response returned by the hook 243 | ✓ the provided status code 244 | ✓ the provided headers 245 | ✓ the provided body 246 | and the response hook mutates current response using `this` 247 | the server should serve request with response mutated by the hook 248 | ✓ the provided status code 249 | ✓ the provided headers 250 | ✓ the provided body 251 | closing the server with a callback 252 | ✓ should call the callback as well as closing the server 253 | an error passed by body-parser 254 | ✓ should be collected to the request view as .parseError 255 | 256 | ~internals 257 | .mapParsers(parsers) 258 | ✓ should be a function that names 1 argument - parsers 259 | when called with an object element 260 | ✓ should not fail 261 | when called with a function element 262 | ✓ should not fail 263 | when called with a string element that is not a body-parser built-in 264 | ✓ should not fail and map it to an instance produced by the required module 265 | when called with an object element who's first key is not a body-parser built-in 266 | ✓ should not fail 267 | ✓ should map it to an instance produced by the required module 268 | ✓ should pass it the arguments 269 | 270 | 271 | 39 passing (53ms) 272 | 273 | ``` 274 | --------------------------------------------------------------------------------