From c0a72dbaf2be56f4034abda3fbd562dd0a334032 Mon Sep 17 00:00:00 2001 From: Lloyd Brookes Date: Wed, 25 Nov 2015 20:53:09 +0000 Subject: [PATCH] mocks: docs, examples, tests --- README.md | 106 +++++++++++++++++++++++++--- example/mock/.local-web-server.json | 8 +-- example/mock/css/style.css | 7 -- example/mock/index.html | 8 --- example/mock/index.js | 9 --- example/mock/mocks/data.json | 3 + example/mock/mocks/five.js | 5 +- example/mock/mocks/four.js | 4 +- jsdoc2md/README.hbs | 106 +++++++++++++++++++++++++--- lib/local-web-server.js | 6 +- test/test.js | 134 ++++++++++++++++++++++++++++++++++++ 11 files changed, 338 insertions(+), 58 deletions(-) delete mode 100644 example/mock/css/style.css delete mode 100644 example/mock/index.html delete mode 100644 example/mock/index.js create mode 100644 example/mock/mocks/data.json diff --git a/README.md b/README.md index ce07e19..c389359 100644 --- a/README.md +++ b/README.md @@ -47,16 +47,6 @@ By default, typical SPA urls (e.g. `/user/1`, `/login`) would return `404 Not Fo [Example](https://github.com/75lb/local-web-server/tree/master/example/spa). -### Access Control - -By default, access to all files is allowed (including dot files). Use `--forbid` to establish a blacklist: -```sh -$ ws --forbid '*.json' '*.yml' -serving at http://localhost:8000 -``` - -[Example](https://github.com/75lb/local-web-server/tree/master/example/forbid). - ### URL rewriting Your application requested `/css/style.css` but it's stored at `/build/css/style.css`. To avoid a 404 you need a rewrite rule: @@ -79,7 +69,6 @@ $ ws --rewrite '/css/* -> /build/css/$1' this rewrites `/css/a` as `/build/css/a`, `/css/a/b/c` as `/build/css/a/b/c` etc. - #### Proxied requests If the `to` URL contains a remote host, local-web-server will act as a proxy - fetching and responding with the remote resource. @@ -96,6 +85,91 @@ $ ws --rewrite '/:user/repos/:name -> https://api.github.com/repos/:user/:name' [Example](https://github.com/75lb/local-web-server/tree/master/example/rewrite). +### Mock Responses + +Mock a data service, serve any custom/dynamic content. + +A mock definition maps a route to a response. Mock a home page. +```json +{ + "mocks": [ + { + "route": "/", + "response": { + "body": "

Welcome to the Mock Responses example

" + } + } + ] +} +``` + +Conditional response, depending on the request. +```json +{ + "mocks": [ + { + "route": "/two", + "request": { "accepts": "xml" }, + "response": { + "body": "" + } + } + ] +} +``` + +Multiple potential responses. First request to match. +```json +{ + "mocks": [ + { + "route": "/three", + "responses": [ + { + "request": { "method": "GET" }, + "response": { + "body": "

Mock response for 'GET' request on /three

" + } + }, + { + "request": { "method": "POST" }, + "response": { + "status": 400, + "body": { "message": "That method is not allowed." } + } + } + ] + } + ] +} +``` + +More dynamic response. +```json +{ + "mocks": [ + { + "route": "/four", + "module": "/mocks/four.js" + } + ] +} +``` + +Tokens in the route are passed to the response. +```json +{ + "mocks": [ + { + "route": "/five/:id\\?name=:name", + "module": "/mocks/five.js" + } + ] +} +``` + +[Example](https://github.com/75lb/local-web-server/tree/master/example/mock). + ### Stored config Use the same port and blacklist every time? Persist it to `package.json`: @@ -136,6 +210,16 @@ serving at http://localhost:8000 The format value supplied is passed directly to [morgan](https://github.com/expressjs/morgan). The exception is `--log-format none` which disables all output. +### Access Control + +By default, access to all files is allowed (including dot files). Use `--forbid` to establish a blacklist: +```sh +$ ws --forbid '*.json' '*.yml' +serving at http://localhost:8000 +``` + +[Example](https://github.com/75lb/local-web-server/tree/master/example/forbid). + ### Other usage #### Debugging diff --git a/example/mock/.local-web-server.json b/example/mock/.local-web-server.json index 5b49c4f..ffa1c2b 100644 --- a/example/mock/.local-web-server.json +++ b/example/mock/.local-web-server.json @@ -1,9 +1,9 @@ { "mocks": [ { - "route": "/one", + "route": "/", "response": { - "body": { "id": 1, "name": "whatever" } + "body": "

Welcome to the Mock Responses example

" } }, { @@ -15,11 +15,11 @@ }, { "route": "/three", - "targets": [ + "responses": [ { "request": { "method": "GET" }, "response": { - "body": { "id": 1, "name": "whatever" } + "body": "

Mock response for 'GET' request on /three

" } }, { diff --git a/example/mock/css/style.css b/example/mock/css/style.css deleted file mode 100644 index 7fb71e5..0000000 --- a/example/mock/css/style.css +++ /dev/null @@ -1,7 +0,0 @@ -body { - background-color: #AA3939; - color: #FFE2E2 -} -svg { - fill: #000 -} diff --git a/example/mock/index.html b/example/mock/index.html deleted file mode 100644 index eec74b2..0000000 --- a/example/mock/index.html +++ /dev/null @@ -1,8 +0,0 @@ - - - -

Mock responses

- - diff --git a/example/mock/index.js b/example/mock/index.js deleted file mode 100644 index ce602ed..0000000 --- a/example/mock/index.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict' -const request = require('req-then') -const $ = document.querySelector.bind(document) - -request('http://localhost:8000/tree').then(response => { - $('ul').innerHTML = JSON.parse(response.data).map(tree => { - return `
  • ${tree.name}
  • ` - }).join('') -}) diff --git a/example/mock/mocks/data.json b/example/mock/mocks/data.json new file mode 100644 index 0000000..fe13696 --- /dev/null +++ b/example/mock/mocks/data.json @@ -0,0 +1,3 @@ +{ + "one": "static data" +} diff --git a/example/mock/mocks/five.js b/example/mock/mocks/five.js index 168708d..9138cc0 100644 --- a/example/mock/mocks/five.js +++ b/example/mock/mocks/five.js @@ -1,8 +1,5 @@ module.exports = { response: function (ctx, id, name) { - this.body = { - id: id, - name: name - } + ctx.body = `

    id: ${id}, name: ${name}

    ` } } diff --git a/example/mock/mocks/four.js b/example/mock/mocks/four.js index 4beeb30..981fe26 100644 --- a/example/mock/mocks/four.js +++ b/example/mock/mocks/four.js @@ -1,5 +1,7 @@ +const fs = require('fs') + module.exports = { response: { - body: { id: 2, name: 'eucalyptus', maxHeight: 210 } + body: fs.createReadStream(__filename) } } diff --git a/jsdoc2md/README.hbs b/jsdoc2md/README.hbs index 1c66e8d..e5a7c1c 100644 --- a/jsdoc2md/README.hbs +++ b/jsdoc2md/README.hbs @@ -47,16 +47,6 @@ By default, typical SPA urls (e.g. `/user/1`, `/login`) would return `404 Not Fo [Example](https://github.com/75lb/local-web-server/tree/master/example/spa). -### Access Control - -By default, access to all files is allowed (including dot files). Use `--forbid` to establish a blacklist: -```sh -$ ws --forbid '*.json' '*.yml' -serving at http://localhost:8000 -``` - -[Example](https://github.com/75lb/local-web-server/tree/master/example/forbid). - ### URL rewriting Your application requested `/css/style.css` but it's stored at `/build/css/style.css`. To avoid a 404 you need a rewrite rule: @@ -79,7 +69,6 @@ $ ws --rewrite '/css/* -> /build/css/$1' this rewrites `/css/a` as `/build/css/a`, `/css/a/b/c` as `/build/css/a/b/c` etc. - #### Proxied requests If the `to` URL contains a remote host, local-web-server will act as a proxy - fetching and responding with the remote resource. @@ -96,6 +85,91 @@ $ ws --rewrite '/:user/repos/:name -> https://api.github.com/repos/:user/:name' [Example](https://github.com/75lb/local-web-server/tree/master/example/rewrite). +### Mock Responses + +Mock a data service, serve any custom/dynamic content. + +A mock definition maps a route to a response. Mock a home page. +```json +{ + "mocks": [ + { + "route": "/", + "response": { + "body": "

    Welcome to the Mock Responses example

    " + } + } + ] +} +``` + +Conditional response, depending on the request. +```json +{ + "mocks": [ + { + "route": "/two", + "request": { "accepts": "xml" }, + "response": { + "body": "" + } + } + ] +} +``` + +Multiple potential responses. First request to match. +```json +{ + "mocks": [ + { + "route": "/three", + "responses": [ + { + "request": { "method": "GET" }, + "response": { + "body": "

    Mock response for 'GET' request on /three

    " + } + }, + { + "request": { "method": "POST" }, + "response": { + "status": 400, + "body": { "message": "That method is not allowed." } + } + } + ] + } + ] +} +``` + +More dynamic response. +```json +{ + "mocks": [ + { + "route": "/four", + "module": "/mocks/four.js" + } + ] +} +``` + +Tokens in the route are passed to the response. +```json +{ + "mocks": [ + { + "route": "/five/:id\\?name=:name", + "module": "/mocks/five.js" + } + ] +} +``` + +[Example](https://github.com/75lb/local-web-server/tree/master/example/mock). + ### Stored config Use the same port and blacklist every time? Persist it to `package.json`: @@ -136,6 +210,16 @@ serving at http://localhost:8000 The format value supplied is passed directly to [morgan](https://github.com/expressjs/morgan). The exception is `--log-format none` which disables all output. +### Access Control + +By default, access to all files is allowed (including dot files). Use `--forbid` to establish a blacklist: +```sh +$ ws --forbid '*.json' '*.yml' +serving at http://localhost:8000 +``` + +[Example](https://github.com/75lb/local-web-server/tree/master/example/forbid). + ### Other usage #### Debugging diff --git a/lib/local-web-server.js b/lib/local-web-server.js index 0621a4e..7972c1f 100644 --- a/lib/local-web-server.js +++ b/lib/local-web-server.js @@ -148,11 +148,11 @@ function localWebServer (options) { /* Mock Responses */ options.mocks.forEach(mock => { if (mock.module) { - mock.targets = require(path.join(options.static.root, mock.module)) + mock.responses = require(path.join(options.static.root, mock.module)) } - if (mock.targets) { - app.use(mw.mockResponses(mock.route, mock.targets)) + if (mock.responses) { + app.use(mw.mockResponses(mock.route, mock.responses)) } else if (mock.response) { mock.target = { request: mock.request, diff --git a/test/test.js b/test/test.js index a917551..ca73da0 100644 --- a/test/test.js +++ b/test/test.js @@ -17,6 +17,13 @@ function launchServer (app, options) { }) } +function checkResponse (t, status, body) { + return function (response) { + if (status) t.strictEqual(response.res.statusCode, status) + if (body) t.ok(body.test(response.data)) + } +} + test('static', function (t) { t.plan(1) const app = localWebServer({ @@ -201,3 +208,130 @@ test('rewrite: proxy with port', function (t) { }) }) }) + +test('mock: simple response', function (t) { + t.plan(2) + const app = localWebServer({ + log: { format: 'none' }, + mocks: [ + { route: '/test', response: { body: 'test' } } + ] + }) + launchServer(app, { path: '/test', onSuccess: response => { + t.strictEqual(response.res.statusCode, 200) + t.ok(/test/.test(response.data)) + }}) +}) + +test('mock: method request filter', function (t) { + t.plan(3) + const app = localWebServer({ + log: { format: 'none' }, + mocks: [ + { + route: '/test', + request: { method: 'POST' }, + response: { body: 'test' } + } + ] + }) + const server = http.createServer(app.callback()) + server.listen(8100, () => { + request('http://localhost:8100/test') + .then(checkResponse(t, 404)) + .then(() => request('http://localhost:8100/test', { data: 'something' })) + .then(checkResponse(t, 200, /test/)) + .then(server.close.bind(server)) + }) +}) + +test('mock: accepts request filter', function (t) { + t.plan(3) + const app = localWebServer({ + log: { format: 'none' }, + mocks: [ + { + route: '/test', + request: { accepts: 'text' }, + response: { body: 'test' } + } + ] + }) + const server = http.createServer(app.callback()) + server.listen(8100, () => { + request('http://localhost:8100/test', { headers: { Accept: '*/json' }}) + .then(checkResponse(t, 404)) + .then(() => request('http://localhost:8100/test', { headers: { Accept: 'text/plain' }})) + .then(checkResponse(t, 200, /test/)) + .then(server.close.bind(server)) + }) +}) + +test('mock: responses array', function (t) { + t.plan(4) + const app = localWebServer({ + log: { format: 'none' }, + mocks: [ + { + route: '/test', + responses: [ + { request: { method: 'GET' }, response: { body: 'get' } }, + { request: { method: 'POST' }, response: { body: 'post' } } + ] + } + ] + }) + const server = http.createServer(app.callback()) + server.listen(8100, () => { + request('http://localhost:8100/test') + .then(checkResponse(t, 200, /get/)) + .then(() => request('http://localhost:8100/test', { method: 'POST' })) + .then(checkResponse(t, 200, /post/)) + .then(server.close.bind(server)) + }) +}) + +test('mock: response function', function (t) { + t.plan(4) + const app = localWebServer({ + log: { format: 'none' }, + mocks: [ + { + route: '/test', + responses: [ + { request: { method: 'GET' }, response: ctx => ctx.body = 'get' }, + { request: { method: 'POST' }, response: ctx => ctx.body = 'post' } + ] + } + ] + }) + const server = http.createServer(app.callback()) + server.listen(8100, () => { + request('http://localhost:8100/test') + .then(checkResponse(t, 200, /get/)) + .then(() => request('http://localhost:8100/test', { method: 'POST' })) + .then(checkResponse(t, 200, /post/)) + .then(server.close.bind(server)) + }) +}) + +test('mock: response function args', function (t) { + t.plan(2) + const app = localWebServer({ + log: { format: 'none' }, + mocks: [ + { + route: '/test/:one', + responses: [ + { request: { method: 'GET' }, response: (ctx, one) => ctx.body = one } + ] + } + ] + }) + const server = http.createServer(app.callback()) + server.listen(8100, () => { + request('http://localhost:8100/test/yeah') + .then(checkResponse(t, 200, /yeah/)) + .then(server.close.bind(server)) + }) +})