├── .gitignore ├── .travis.yml ├── LICENSE ├── Readme.md ├── lib ├── applyMixins.js ├── getAddress.js ├── index.js ├── mixins │ ├── assert.js │ ├── body.js │ ├── header.js │ ├── index.js │ ├── json.js │ ├── run.js │ ├── status.js │ └── type.js ├── parseArgs.js └── utils │ └── getChecker.js ├── package.json └── test ├── integration.js └── unit ├── getAddress.js ├── mixins ├── assert.js ├── body.js ├── header.js ├── json.js ├── run.js ├── status.js └── type.js └── parseArgs.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4" 4 | - "5" 5 | script: 6 | - npm run test-cov 7 | after_script: 8 | - npm install codecov 9 | - ./node_modules/.bin/codecov 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 assert-request contributors 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Assert Request 2 | 3 | This is a tool to assert properties of responses to requests, such as the body, headers, and status code. 4 | This returns a promise with additional assertion methods builtin to it. 5 | It requires node 4 or higher to run (that's by design, and will not be changed even if you have a PR). 6 | 7 | ## Example 8 | 9 | This example uses [Mocha](https://mochajs.org/) tests, which natively support promises. 10 | However, `assert-request` is not limited to Mocha. 11 | 12 | ```js 13 | let AssertRequest = require('assert-request'); 14 | let request = AssertRequest(app.listen()); // You can use a server or protocol and host 15 | 16 | describe('/example', function () { 17 | it('should return HTML', function () { 18 | return request('/example') 19 | .type('text/html') 20 | .okay(); 21 | }); 22 | }); 23 | 24 | describe('/api', function () { 25 | it('should return the correct JSON', function () { 26 | return request.post('/api') 27 | .type('application/json') 28 | .status(200) 29 | .json(json => json.foo && json.bar); 30 | }); 31 | }); 32 | ``` 33 | 34 | ## Return Type 35 | 36 | The returned value is a Promise. 37 | Because of that, you can use `.then` and `.catch`. 38 | `.then` will be passed the response object. 39 | Many Node.JS utilities such as Mocha will support promises. 40 | 41 | ## Mixin Documentation 42 | 43 | Note: most mixins simply return `this.then(res => assert(...))`. 44 | This means that assertions are guaranteed to run in order. 45 | While technically `Promise.all` should be used here, this makes for a much simpler code base. 46 | In addition, this should be the faster way as no mixins created so far return a Promise (i.e. do an asynchronous operation). 47 | 48 | ### .assert(function) 49 | 50 | Calls the function with the response object, and asserts that the return value is truthy. 51 | 52 | ### .status(expected) 53 | 54 | Asserts that the response status is equal to the one specified. 55 | Expected can be a String, Number, Function, or RegExp. 56 | Strings and Numbers will be checked without strict matching (`==` not `===`) so the status will be cast to a String. 57 | 58 | ### .okay() 59 | 60 | Asserts that the response status is 200. Equivalent to `.status(200)` 61 | 62 | ### .header(header, value, someHeaders) 63 | 64 | Asserts that the header is present, and if value is specified, asserts that it is equal to the value. 65 | In some edge cases like `Set-Cookie`, the header value is an array. 66 | In this case, by default all headers must match the value. 67 | However, if someHeaders is true, then only one must match the value. 68 | Value can be a String, Function, or RegExp. 69 | 70 | ### .body(expected) 71 | 72 | Asserts that the body is equal to the one specified. 73 | Expected can be a String, Function, or RegExp. 74 | 75 | ### .json(expected) 76 | 77 | Asserts that the body is JSON and is equal to the JSON specified. 78 | If expected is a function, then it is passed the parsed JSON. 79 | Expected can be a String, Function, or RegExp. 80 | 81 | 82 | ### .type(expected) 83 | 84 | This is a general mixin to check `Content-Type`. 85 | If the expected type is a string, it checkes if any part of the Content-Type is equal. 86 | All of the following will match `text/html; charset=utf-8` 87 | 88 | - 'text/html; charset=utf-8' 89 | - 'text/html' 90 | - 'charset=utf-8' 91 | - /html/ 92 | - /^text\/html; charset=utf-8$/ 93 | - type => type.includes('html') 94 | 95 | The following will NOT match that type: 96 | 97 | - /^text\/html$/ 98 | - type => type === 'text/html' 99 | 100 | Expected can be a String, Function, or RegExp. 101 | However, partial matching is only applied to strings as specified above. 102 | 103 | ## Inspiration 104 | 105 | This was made as a replacement for [supertest](https://github.com/visionmedia/supertest). 106 | Inspiration was taken from that API. 107 | However, using it also showed how it could be improved, and pitfalls to avoid. 108 | -------------------------------------------------------------------------------- /lib/applyMixins.js: -------------------------------------------------------------------------------- 1 | const mixins = require('./mixins'); 2 | 3 | module.exports = function (obj) { 4 | Object.assign.apply(Object, [obj].concat(mixins)); 5 | const oldThen = obj.then; 6 | ['then', 'catch'].forEach(function (name) { 7 | const oldFn = obj[name]; 8 | obj[name] = function () { 9 | return module.exports(oldFn.apply(this, arguments)); 10 | }; 11 | }); 12 | return obj; 13 | } 14 | -------------------------------------------------------------------------------- /lib/getAddress.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const https = require('https'); 4 | 5 | module.exports = function (host) { 6 | let protocol = 'http'; 7 | if (host && host.listen) { 8 | // Probably an HTTP server 9 | if (host instanceof https.Server) { 10 | protocol = 'https'; 11 | } 12 | host = host.listen(); 13 | } 14 | if (host && host.address) { 15 | // Probably a TCP server from a HTTP server 16 | const address = host.address(); 17 | return protocol + '://localhost:' + address.port; 18 | } else { 19 | return host; 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const request = require('request-promise'); 4 | const methods = require('methods'); 5 | const parseArgs = require('./parseArgs'); 6 | const getAddress = require('./getAddress'); 7 | const applyMixins = require('./applyMixins'); 8 | 9 | module.exports = function (host) { 10 | let urlPrefix = getAddress(host); 11 | 12 | const fn = function () { 13 | const options = parseArgs(urlPrefix, arguments); 14 | Object.assign(options, { 15 | simple: false, 16 | resolveWithFullResponse: true 17 | }); 18 | const promise = request(options).then(res => res); 19 | applyMixins(promise); 20 | return promise; 21 | }; 22 | 23 | methods.forEach(method => fn[method] = function () { 24 | const options = parseArgs(urlPrefix, arguments); 25 | options.method = options.method || method; 26 | return fn(options); 27 | }); 28 | 29 | return fn; 30 | }; 31 | -------------------------------------------------------------------------------- /lib/mixins/assert.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | 5 | module.exports = { 6 | assert: function (fn) { 7 | return this.then(function (res) { 8 | assert(fn(res)); 9 | return res; 10 | }); 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /lib/mixins/body.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getChecker = require('../utils/getChecker'); 4 | 5 | module.exports = { 6 | body: function (expected) { 7 | let matches = getChecker(expected); 8 | return this.then(function (res) { 9 | if (!matches(res.body)) { 10 | throw new Error('Got body:\n' + res.body + '\n\nExpected body:\n' + expected); 11 | } 12 | return res; 13 | }); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /lib/mixins/header.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getChecker = require('../utils/getChecker'); 4 | 5 | module.exports = { 6 | header: function (header, matcher, someHeaders) { 7 | let matches = getChecker(matcher); 8 | return this.then(function (res) { 9 | let val = res.headers[header.toLowerCase()]; 10 | if (val instanceof Array) { 11 | if (someHeaders) { 12 | if (val.some(matches)) return res; 13 | } else { 14 | if (val.every(matches)) return res; 15 | } 16 | } else { 17 | if (matches(val)) return res; 18 | } 19 | throw new Error('The header "' + header + '" has the value "' + val + '" (expected "' + matcher + '")'); 20 | }); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /lib/mixins/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | 6 | const files = fs.readdirSync(__dirname).filter(file => file !== 'index.js'); 7 | module.exports = files.map(file => require(path.join(__dirname, file))); 8 | -------------------------------------------------------------------------------- /lib/mixins/json.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const getChecker = require('../utils/getChecker'); 5 | 6 | module.exports = { 7 | json: function (expected) { 8 | let matches = getChecker(expected); 9 | return this.then(function (res) { 10 | let json; 11 | if (typeof res.body === 'object') { 12 | json = res.body; 13 | } else { 14 | try { 15 | json = JSON.parse(res.body); 16 | } catch (err) { 17 | throw new Error('Did not receive valid JSON'); 18 | } 19 | } 20 | if (expected instanceof Function && expected(json)) { 21 | return res; 22 | } else if (matches(res.body)) { 23 | return res; 24 | } 25 | try { 26 | if (JSON.stringify(expected) === JSON.stringify(json)) return res; 27 | } catch (err) {} // Just a JSON stringify error 28 | throw new Error('Got JSON:\n' + util.inspect(json) + '\n\nExpected:\n' + expected); 29 | }); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /lib/mixins/run.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | 5 | module.exports = { 6 | run: function (fn) { 7 | let savedRes; 8 | return this.then(function (res) { 9 | savedRes = res; 10 | return fn(res); 11 | }).then(() => savedRes); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /lib/mixins/status.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getChecker = require('../utils/getChecker'); 4 | 5 | module.exports = { 6 | status: function (status) { 7 | let matches = getChecker(status); 8 | return this.then(function (res) { 9 | if (!matches(res.statusCode)) { 10 | throw new Error('Got status code ' + res.statusCode + ' (expected ' + status + ')'); 11 | } 12 | return res; 13 | }); 14 | }, 15 | okay: function () { 16 | return this.status(200); 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /lib/mixins/type.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getChecker = require('../utils/getChecker'); 4 | 5 | module.exports = { 6 | type: function (expected) { 7 | let matches = getChecker(expected); 8 | return this.then(function (res) { 9 | let fullType = res.headers['content-type']; 10 | if (!fullType) throw new Error('Expected a Content-Type header'); 11 | if (matches(fullType)) return res; 12 | if (typeof expected === 'string' || expected instanceof String) { 13 | let types = fullType.split(';').map(str => str.trim()); 14 | if (types.some(type => matches(type))) return res; 15 | } 16 | throw new Error('Got type ' + fullType + ' (expected ' + expected + ')'); 17 | }); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /lib/parseArgs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (uriPrefix, args) { 4 | let uri = args[0]; 5 | let options = args[1]; 6 | if (!options && uri instanceof Object) { 7 | return uri; 8 | } 9 | options = options || {}; 10 | if (!options.uri && uri) { 11 | if (!uri.startsWith('/') && uriPrefix) { 12 | uri = '/' + uri; 13 | } 14 | options.uri = uriPrefix + uri; 15 | } 16 | return options; 17 | } 18 | -------------------------------------------------------------------------------- /lib/utils/getChecker.js: -------------------------------------------------------------------------------- 1 | module.exports = function (matcher) { 2 | if (matcher === undefined) { 3 | return val => val !== undefined && val !== null; 4 | } else if (matcher instanceof Function) { 5 | return matcher; 6 | } else if (matcher instanceof RegExp) { 7 | return str => matcher.test(str); 8 | } else { 9 | return str => matcher == str; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "assert-request", 3 | "version": "1.0.6", 4 | "description": "Assert responses to HTTP requests", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "test": "./node_modules/.bin/mocha $(find test -name '*.js')", 8 | "test-cov": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha $(find test -name '*.js')" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/PlasmaPower/assert-request.git" 13 | }, 14 | "keywords": [ 15 | "http", 16 | "request", 17 | "assert", 18 | "testing" 19 | ], 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/PlasmaPower/assert-request/issues" 23 | }, 24 | "homepage": "https://github.com/PlasmaPower/assert-request#readme", 25 | "dependencies": { 26 | "methods": "^1.1.2", 27 | "request-promise": "^2.0.1" 28 | }, 29 | "devDependencies": { 30 | "istanbul": "^0.4.2", 31 | "mocha": "^2.4.5" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/integration.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const http = require('http'); 5 | const acceptRequest = require('..'); 6 | 7 | const exampleServer = new http.Server(function (req, res) { 8 | res.setHeader('Content-Type', 'text/plain; charset=utf-8'); 9 | if (req.method.toLowerCase() === 'post') { 10 | req.pipe(res); 11 | } else { 12 | res.end('Hello world!'); 13 | } 14 | }); 15 | const request = acceptRequest(exampleServer); 16 | 17 | describe('general integration', function () { 18 | this.slow(150); 19 | 20 | it('should not throw an error if the response is matched', function () { 21 | return request('/') 22 | .okay() 23 | .type('text/plain') 24 | .header('X-Example', null) 25 | .body(/hello/i); 26 | }); 27 | 28 | it('should have methods for HTTP methods', function () { 29 | return request.post('/', { body: 'foobar' }) 30 | .okay() 31 | .type('text/plain') 32 | .body('foobar'); 33 | }); 34 | 35 | it('should throw an error if the response is mismatched', function () { 36 | return request('/') 37 | .okay() 38 | .type('text/plain') 39 | .header('X-Example') 40 | .body(/hello/i) 41 | .then(err => assert(err instanceof Error)) 42 | .catch(err => err); 43 | }); 44 | 45 | after((done) => exampleServer.close(done)); 46 | }); 47 | -------------------------------------------------------------------------------- /test/unit/getAddress.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const http = require('http'); 5 | const https = require('https'); 6 | const getAddress = require('../../lib/getAddress.js'); 7 | 8 | const exampleHttpServer = new http.Server(() => {}); 9 | const exampleHttpsServer = new https.Server(() => {}); 10 | 11 | describe('getAddress', function () { 12 | it('should accept a normal host', function () { 13 | assert.equal(getAddress('example.com'), 'example.com'); 14 | }); 15 | 16 | it('should get a IPv4 address from a server', function () { 17 | assert.equal(getAddress({ 18 | address: () => ({ family: 'IPv4', address: '127.0.0.1', port: '1234' }) 19 | }), 'http://localhost:1234'); 20 | }); 21 | 22 | it('should get a IPv6 address from a server', function () { 23 | assert.equal(getAddress({ 24 | address: () => ({ family: 'IPv6', address: '::', port: '1234' }) 25 | }), 'http://localhost:1234'); 26 | }); 27 | 28 | it('should accept a HTTP server', function () { 29 | assert(/^http:\/\/localhost:\d+$/.test(getAddress(exampleHttpServer))); 30 | }); 31 | 32 | it('should accept a HTTPS server', function () { 33 | assert(/^https:\/\/localhost:\d+$/.test(getAddress(exampleHttpsServer))); 34 | }); 35 | 36 | it('should accept an already started HTTP server', function () { 37 | assert(/^http:\/\/localhost:\d+$/.test(getAddress(exampleHttpServer.listen()))); 38 | }); 39 | 40 | after(function (done) { 41 | exampleHttpServer.close(done); 42 | }); 43 | 44 | after(function (done) { 45 | exampleHttpsServer.close(done); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/unit/mixins/assert.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const assertMixin = require('../../../lib/mixins/assert.js').assert; 5 | 6 | describe('assert mixin', function () { 7 | it('should do nothing if the assertion is true', function () { 8 | return assertMixin 9 | .apply(Promise.resolve({ example: true }), [res => res && res.example]) 10 | .then(res => assert(res && res.example)); 11 | }); 12 | 13 | it('should throw an error if the assertion is false', function () { 14 | return assertMixin 15 | .apply(Promise.resolve({ example: false }), [res => res && res.example]) 16 | .catch(err => err) 17 | .then(err => assert(err instanceof Error)); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/unit/mixins/body.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const bodyMixin = require('../../../lib/mixins/body.js').body; 5 | 6 | describe('body mixin', function () { 7 | it('should do nothing if the body is equal', function () { 8 | return Promise.all(['', 'example', 'foo\nbar'].map(function (body) { 9 | return bodyMixin 10 | .apply(Promise.resolve({ body }), [body]) 11 | .then(res => assert(res && res.body === body)); 12 | })); 13 | }); 14 | 15 | it('should accept a RegExp', function () { 16 | return Promise.all(['abc', 'abc example', 'abc\nfoobar'].map(function (body) { 17 | return bodyMixin 18 | .apply(Promise.resolve({ body }), [/^abc/]) 19 | .then(res => assert(res && res.body === body)); 20 | })); 21 | }); 22 | 23 | it('should accept a function', function () { 24 | return Promise.all(['foobar', 'example foobar', 'abc\nfoobar'].map(function (body) { 25 | return bodyMixin 26 | .apply(Promise.resolve({ body }), [body => body.endsWith('foobar')]) 27 | .then(res => assert(res && res.body === body)); 28 | })); 29 | }); 30 | 31 | it('should throw an error if the body is not equal', function () { 32 | return Promise.all(['', 'foobar', 'example'].map(function (body) { 33 | return bodyMixin 34 | .apply(Promise.resolve({ body: body + '\n' }), [body]) 35 | .catch(err => err) 36 | .then(err => assert(err instanceof Error)); 37 | })); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /test/unit/mixins/header.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const headerMixin = require('../../../lib/mixins/header.js').header; 5 | 6 | describe('header mixin', function () { 7 | it('should do nothing if the header is equal', function () { 8 | return Promise.all(['', 'example', 'foobar'].map(function (header) { 9 | return headerMixin 10 | .apply(Promise.resolve({ headers: { 'x-example': header } }), ['X-Example', header]) 11 | .then(res => assert(res && res.headers)); 12 | })); 13 | }); 14 | 15 | it('should accept a RegExp', function () { 16 | return Promise.all(['abc', 'abc example', 'abc foobar'].map(function (header) { 17 | return headerMixin 18 | .apply(Promise.resolve({ headers: { 'x-foobar': header } }), ['X-Foobar', /^abc/]) 19 | .then(res => assert(res && res.headers)); 20 | })); 21 | }); 22 | 23 | it('should accept a function', function () { 24 | return Promise.all(['abc', 'example abc', 'foobar abc'].map(function (header) { 25 | return headerMixin 26 | .apply(Promise.resolve({ headers: { 'x-foobar': header } }), ['X-Foobar', header => header.endsWith('abc')]) 27 | .then(res => assert(res && res.headers)); 28 | })); 29 | }); 30 | 31 | it('should throw an error if the header is not equal', function () { 32 | return Promise.all(['', 'foobar', 'example'].map(function (header) { 33 | return headerMixin 34 | .apply(Promise.resolve({ headers: { 'x-foobar': header + ' foobar' } }), ['X-Foobar', header]) 35 | .catch(err => err) 36 | .then(err => assert(err instanceof Error)); 37 | })); 38 | }); 39 | 40 | it('should no nothing if passed no value and the header exists', function () { 41 | return Promise.all(['', 'X-Foobar', 'Content-Type'].map(function (header) { 42 | let headerObj = {}; 43 | headerObj[header.toLowerCase()] = 'foobar'; 44 | return headerMixin 45 | .apply(Promise.resolve({ headers: headerObj }), [header]) 46 | .then(res => assert(res && res.headers)); 47 | })); 48 | }); 49 | 50 | it('should throw an error if the header does not exist', function () { 51 | return Promise.all([undefined, null].map(function (value) { 52 | return headerMixin 53 | .apply(Promise.resolve({ headers: { 'content-length': value } }), ['Content-Length']) 54 | .catch(err => err) 55 | .then(err => assert(err instanceof Error)); 56 | })); 57 | }); 58 | 59 | it('should do nothing if passed null for a value and the header does not exist', function () { 60 | return headerMixin 61 | .apply(Promise.resolve({ headers: {} }), ['Content-Length', null]) 62 | .then(res => assert(res && res.headers)); 63 | }); 64 | 65 | it('should throw an error if passed null for a value and the header exists', function () { 66 | return headerMixin 67 | .apply(Promise.resolve({ headers: { 'content-length': 'foobar' } }), ['Content-Length', null]) 68 | .catch(err => err) 69 | .then(err => assert(err instanceof Error)); 70 | }); 71 | 72 | it('should do nothing if every header matches', function () { 73 | return headerMixin 74 | .apply(Promise.resolve({ headers: { 'set-cookie': ['abc', 'abc example'] } }), ['Set-Cookie', /^abc/]) 75 | .then(res => assert(res && res.headers)); 76 | }); 77 | 78 | it('should throw an error if a header does not match', function () { 79 | return headerMixin 80 | .apply(Promise.resolve({ headers: { 'set-cookie': ['abc', 'example'] } }), ['Set-Cookie', /^abc/]) 81 | .catch(err => err) 82 | .then(err => assert(err instanceof Error)); 83 | }); 84 | 85 | it('should do nothing if some headers do not match but sosmeHeaders is true', function () { 86 | return headerMixin 87 | .apply(Promise.resolve({ headers: { 'set-cookie': ['abc', 'example', 'foobar']} }), ['Set-Cookie', 'example', true]) 88 | .then(res => assert(res && res.headers)); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /test/unit/mixins/json.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const jsonMixin = require('../../../lib/mixins/json.js').json; 5 | 6 | describe('json mixin', function () { 7 | it('should do nothing if the json is equal', function () { 8 | return Promise.all(['{ "key": "value" }', '{"key":"value"}', '{"key":"value"}\n'].map(function (body) { 9 | return jsonMixin 10 | .apply(Promise.resolve({ body }), [{ key: 'value' }]) 11 | .then(res => assert(res && res.body === body)); 12 | })); 13 | }); 14 | 15 | it('should accept already parsed JSON', function () { 16 | return jsonMixin 17 | .apply(Promise.resolve({ body: { key: 'value' } }), [{ key: 'value' }]) 18 | .then(assert); 19 | }); 20 | 21 | it('should accept a RegExp', function () { 22 | return Promise.all(['{ "key": "value" }'].map(function (body) { 23 | return jsonMixin 24 | .apply(Promise.resolve({ body }), [/^{/]) 25 | .then(res => assert(res && res.body === body)); 26 | })); 27 | }); 28 | 29 | it('should accept a function handling the JSON itself', function () { 30 | return Promise.all(['{ "foo": [ "bar" ] }', '{"foo":["bar"]}'].map(function (body) { 31 | return jsonMixin 32 | .apply(Promise.resolve({ body }), [json => json.foo.length === 1]) 33 | .then(res => assert(res && res.body === body)); 34 | })); 35 | }); 36 | 37 | it('should throw an error if the function returns false', function () { 38 | return Promise.all(['{ "foo": [ "bar" ] }', '{"foo":["bar"]}'].map(function (body) { 39 | return jsonMixin 40 | .apply(Promise.resolve({ body }), [json => json.foo.length === 2]) 41 | .catch(err => err) 42 | .then(err => assert(err instanceof Error)); 43 | })); 44 | }); 45 | 46 | it('should throw an error if the body matches the string but is not valid JSON', function () { 47 | return Promise.all(['{foo: \'bar\'}', '{'].map(function (body) { 48 | return jsonMixin 49 | .apply(Promise.resolve({ body }), [body]) 50 | .catch(err => err) 51 | .then(err => assert(err instanceof Error)); 52 | })); 53 | }); 54 | 55 | it('should throw an error if the JSON is not equal', function () { 56 | return Promise.all(['{ "key": ["value"] }', '{ "otherKey": "otherValue" }'].map(function (body) { 57 | return jsonMixin 58 | .apply(Promise.resolve({ body: '{"key":["otherValue", "value"]}' }), [body]) 59 | .catch(err => err) 60 | .then(err => assert(err instanceof Error)); 61 | })); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /test/unit/mixins/run.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const runMixin = require('../../../lib/mixins/run.js').run; 5 | 6 | describe('run mixin', function () { 7 | it('should ignore the return value', function () { 8 | return runMixin 9 | .apply(Promise.resolve({ example: true }), [res => assert(res && res.example)]) 10 | .then(res => assert(res && res.example)); 11 | }); 12 | 13 | it('should throw errors up the chain', function () { 14 | return runMixin 15 | .apply(Promise.resolve({ example: false }), [res => assert(res && res.example)]) 16 | .catch(err => err) 17 | .then(err => assert(err instanceof Error)); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/unit/mixins/status.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const okayMixin = require('../../../lib/mixins/status.js').okay; 5 | const statusMixin = require('../../../lib/mixins/status.js').status; 6 | 7 | describe('status mixin', function () { 8 | it('should do nothing if the status is equal', function () { 9 | return Promise.all([200, 401, 404].map(function (status) { 10 | return statusMixin 11 | .apply(Promise.resolve({ statusCode: status }), [status]) 12 | .then(res => assert(res && res.statusCode === status)); 13 | })); 14 | }); 15 | 16 | it('should accept a RegExp', function () { 17 | return Promise.all([401, 403, 404].map(function (status) { 18 | return statusMixin 19 | .apply(Promise.resolve({ statusCode: status }), [/^4..$/]) 20 | .then(res => assert(res && res.statusCode === status)); 21 | })); 22 | }); 23 | 24 | it('should accept a function', function () { 25 | return Promise.all([401, 403, 404].map(function (status) { 26 | return statusMixin 27 | .apply(Promise.resolve({ statusCode: status }), [status => Math.floor(status / 10) === 40]) 28 | .then(res => assert(res && res.statusCode === status)); 29 | })); 30 | }); 31 | 32 | it('should throw an error if the status is not equal', function () { 33 | return Promise.all([200, 401, 404].map(function (status) { 34 | return statusMixin 35 | .apply(Promise.resolve({ statusCode: status + 100 }), [status]) 36 | .catch(err => err) 37 | .then(err => assert(err instanceof Error)); 38 | })); 39 | }); 40 | }); 41 | 42 | describe('okay mixin', function () { 43 | it('should do nothing if the status is 200', function () { 44 | const self = Promise.resolve({ statusCode: 200 }); 45 | self.status = statusMixin; 46 | return okayMixin 47 | .apply(self, []) 48 | .then(res => assert(res && res.statusCode === 200)); 49 | }); 50 | 51 | it('should throw an error if the status is not 200', function () { 52 | const self = Promise.resolve({ statusCode: 500 }); 53 | self.status = statusMixin; 54 | return okayMixin 55 | .apply(self, []) 56 | .catch(err => err) 57 | .then(err => assert(err instanceof Error)); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /test/unit/mixins/type.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const typeMixin = require('../../../lib/mixins/type.js').type; 5 | 6 | describe('type mixin', function () { 7 | it('should do nothing if the type is equal', function () { 8 | return Promise.all(['text/html', 'application/json', 'text/html; charset=utf-8'].map(function (type) { 9 | return typeMixin 10 | .apply(Promise.resolve({ headers: { 'content-type': type } }), [type]) 11 | .then(res => assert(res && res.headers)); 12 | })); 13 | }); 14 | 15 | it('should do nothing if the type is part of the whole', function () { 16 | return Promise.all(['text/html', 'charset=utf-8', 'text/html; charset=utf-8'].map(function (type) { 17 | return typeMixin 18 | .apply(Promise.resolve({ headers: { 'content-type': 'text/html; charset=utf-8' } }), [type]) 19 | .then(res => assert(res && res.headers)); 20 | })); 21 | }); 22 | 23 | it('should do nothing if the type matches a regex', function () { 24 | return Promise.all([/html/, /text\/html/, /^text\/html/].map(function (type) { 25 | return typeMixin 26 | .apply(Promise.resolve({ headers: { 'content-type': 'text/html; charset=utf-8' } }), [type]) 27 | .then(res => assert(res && res.headers)); 28 | })); 29 | }); 30 | 31 | it('should throw an error if the regex only matches part of the type', function () { 32 | return Promise.all([/^text\/html$/, /html$/].map(function (type) { 33 | return typeMixin 34 | .apply(Promise.resolve({ headers: { 'content-type': 'text/html; charset=utf-8' } }), [type]) 35 | .catch(err => err) 36 | .then(err => assert(err instanceof Error)); 37 | })); 38 | }); 39 | 40 | it('should throw an error if the type does not match ', function () { 41 | return Promise.all(['application/json', 'application/json; charset=utf-8'].map(function (type) { 42 | return typeMixin 43 | .apply(Promise.resolve({ headers: { 'content-type': type } }), ['text/html']) 44 | .catch(err => err) 45 | .then(err => assert(err instanceof Error)); 46 | })); 47 | }); 48 | 49 | it('should do nothing if no type is specified and Content-Type is present', function () { 50 | return typeMixin 51 | .apply(Promise.resolve({ headers: { 'content-type': 'text/html' } }), []) 52 | .then(res => assert(res && res.headers)); 53 | }); 54 | 55 | it('should throw an error if no type is specified and Content-Type is not present', function () { 56 | return typeMixin 57 | .apply(Promise.resolve({ headers: {} }), []) 58 | .catch(err => err) 59 | .then(err => assert(err instanceof Error)); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /test/unit/parseArgs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const parseArgs = require('../../lib/parseArgs.js'); 5 | 6 | describe('parseArgs', function () { 7 | it('should take the options if an only parameter', function () { 8 | let opts = {}; 9 | assert.equal(parseArgs('', [opts]), opts); 10 | }); 11 | 12 | it('should not touch options.uri', function () { 13 | let opts = { uri: 'example' }; 14 | assert.equal(parseArgs('prefix', ['otherUri', opts]).uri, opts.uri); 15 | }); 16 | 17 | it('should prefix the URI', function () { 18 | assert.equal(parseArgs('[prefix]', ['/example', {}]).uri, '[prefix]/example'); 19 | }); 20 | 21 | it('should add a slash if needed', function () { 22 | assert.equal(parseArgs('[prefix]', ['example', {}]).uri, '[prefix]/example'); 23 | }); 24 | 25 | it('should default to empty options', function () { 26 | assert.equal(parseArgs('', ['/example']).uri, '/example'); 27 | }); 28 | }); 29 | --------------------------------------------------------------------------------