tsoa
This commit is contained in:
100
node_modules/supertest/lib/agent.js
generated
vendored
Normal file
100
node_modules/supertest/lib/agent.js
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
const { agent: Agent } = require('superagent');
|
||||
const methods = require('methods');
|
||||
const http = require('http');
|
||||
let http2;
|
||||
try {
|
||||
http2 = require('http2'); // eslint-disable-line global-require
|
||||
} catch (_) {
|
||||
// eslint-disable-line no-empty
|
||||
}
|
||||
const Test = require('./test.js');
|
||||
|
||||
/**
|
||||
* Initialize a new `TestAgent`.
|
||||
*
|
||||
* @param {Function|Server} app
|
||||
* @param {Object} options
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function TestAgent(app, options = {}) {
|
||||
if (!(this instanceof TestAgent)) return new TestAgent(app, options);
|
||||
|
||||
const agent = new Agent(options);
|
||||
Object.assign(this, agent);
|
||||
|
||||
this._options = options;
|
||||
|
||||
if (typeof app === 'function') {
|
||||
if (options.http2) {
|
||||
if (!http2) {
|
||||
throw new Error(
|
||||
'supertest: this version of Node.js does not support http2'
|
||||
);
|
||||
}
|
||||
app = http2.createServer(app); // eslint-disable-line no-param-reassign
|
||||
} else {
|
||||
app = http.createServer(app); // eslint-disable-line no-param-reassign
|
||||
}
|
||||
}
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherits from `Agent.prototype`.
|
||||
*/
|
||||
|
||||
Object.setPrototypeOf(TestAgent.prototype, Agent.prototype);
|
||||
|
||||
// Preserve the original query method before overriding HTTP methods
|
||||
const originalQuery = Agent.prototype.query;
|
||||
|
||||
// set a host name
|
||||
TestAgent.prototype.host = function(host) {
|
||||
this._host = host;
|
||||
return this;
|
||||
};
|
||||
|
||||
// override HTTP verb methods
|
||||
methods.forEach(function(method) {
|
||||
// Skip 'query' method to prevent overwriting superagent's query functionality
|
||||
if (method === 'query') {
|
||||
return;
|
||||
}
|
||||
|
||||
TestAgent.prototype[method] = function(url, fn) { // eslint-disable-line no-unused-vars
|
||||
const req = new Test(this.app, method.toUpperCase(), url);
|
||||
if (this._options.http2) {
|
||||
req.http2();
|
||||
}
|
||||
|
||||
if (this._host) {
|
||||
req.set('host', this._host);
|
||||
}
|
||||
|
||||
req.on('response', this._saveCookies.bind(this));
|
||||
req.on('redirect', this._saveCookies.bind(this));
|
||||
req.on('redirect', this._attachCookies.bind(this, req));
|
||||
this._setDefaults(req);
|
||||
this._attachCookies(req);
|
||||
|
||||
return req;
|
||||
};
|
||||
});
|
||||
|
||||
// Restore the original query method
|
||||
TestAgent.prototype.query = originalQuery;
|
||||
|
||||
TestAgent.prototype.del = TestAgent.prototype.delete;
|
||||
|
||||
/**
|
||||
* Expose `Agent`.
|
||||
*/
|
||||
|
||||
module.exports = TestAgent;
|
||||
497
node_modules/supertest/lib/cookies/assertion.js
generated
vendored
Normal file
497
node_modules/supertest/lib/cookies/assertion.js
generated
vendored
Normal file
@@ -0,0 +1,497 @@
|
||||
/** Copyright 2015 Gregory Langlais. See LICENSE.txt. */
|
||||
|
||||
'use strict';
|
||||
|
||||
const signature = require('cookie-signature');
|
||||
|
||||
/**
|
||||
* Assert that an object has specific properties (supports array of keys or object)
|
||||
*
|
||||
* @param {object} obj
|
||||
* @param {object|array} props
|
||||
*/
|
||||
function assertHasProperties(obj, props) {
|
||||
if (Array.isArray(props)) {
|
||||
props.forEach(function (key) {
|
||||
if (!(key in obj)) {
|
||||
throw new Error('expected object to have property ' + key);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Object.keys(props).forEach(function (key) {
|
||||
if (!(key in obj)) {
|
||||
throw new Error('expected object to have property ' + key);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that an object does not have specific properties (supports array of keys or object)
|
||||
* When checking with empty props, throws if object exists (matches should.js behavior)
|
||||
*
|
||||
* @param {object} obj
|
||||
* @param {object|array} props
|
||||
*/
|
||||
function assertNotHasProperties(obj, props) {
|
||||
if (Array.isArray(props)) {
|
||||
// When empty array is passed, should.js throws 'false negative fail' if object exists
|
||||
if (props.length === 0) {
|
||||
throw new Error('expected object to not have properties (false negative fail)');
|
||||
}
|
||||
props.forEach(function (key) {
|
||||
if (key in obj) {
|
||||
throw new Error('expected object to not have property ' + key);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// When empty object is passed, should.js throws 'false negative fail' if object exists
|
||||
let keys = Object.keys(props);
|
||||
if (keys.length === 0) {
|
||||
throw new Error('expected object to not have properties (false negative fail)');
|
||||
}
|
||||
keys.forEach(function (key) {
|
||||
if (key in obj) {
|
||||
throw new Error('expected object to not have property ' + key);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that two values are equal
|
||||
*
|
||||
* @param {*} actual
|
||||
* @param {*} expected
|
||||
*/
|
||||
function assertEqual(actual, expected) {
|
||||
if (actual !== expected) {
|
||||
throw new Error(
|
||||
'expected ' + JSON.stringify(actual) + ' to equal ' + JSON.stringify(expected)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that two values are not equal
|
||||
*
|
||||
* @param {*} actual
|
||||
* @param {*} expected
|
||||
*/
|
||||
function assertNotEqual(actual, expected) {
|
||||
if (actual === expected) {
|
||||
throw new Error(
|
||||
'expected ' + JSON.stringify(actual) + ' to not equal ' + JSON.stringify(expected)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build Assertion function
|
||||
*
|
||||
* {object|object[]} expects cookies
|
||||
* {string} expects.<name> and value of cookie
|
||||
* {object} expects.options
|
||||
* {string} [expects.options.domain]
|
||||
* {string} [expects.options.path]
|
||||
* {string} [expects.options.expires] UTC string using date.toUTCString()
|
||||
* {number} [expects.options.max-age]
|
||||
* {boolean} [expects.options.secure]
|
||||
* {boolean} [expects.options.httponly]
|
||||
* {string|string[]} [expects.secret]
|
||||
*
|
||||
* @param {null|string|string[]} [secret]
|
||||
* @param {function|function[]} [asserts]
|
||||
* @returns {Assertion}
|
||||
*/
|
||||
module.exports = function (secret, asserts) {
|
||||
let assertions = [];
|
||||
|
||||
if (typeof secret === 'string') secret = [secret]; // eslint-disable-line no-param-reassign
|
||||
else if (!Array.isArray(secret)) secret = []; // eslint-disable-line no-param-reassign
|
||||
|
||||
if (Array.isArray(asserts)) assertions = asserts;
|
||||
else if (typeof asserts === 'function') assertions.push(asserts);
|
||||
|
||||
/**
|
||||
* Assertion function with static chainable methods
|
||||
*
|
||||
* @param {object} res
|
||||
* @returns {undefined|string}
|
||||
* @constructor
|
||||
*/
|
||||
function Assertion(res) {
|
||||
if (typeof res !== 'object') throw new Error('res argument must be object');
|
||||
|
||||
// request and response object initialization
|
||||
let request = {
|
||||
headers: res.req.getHeaders(),
|
||||
cookies: []
|
||||
};
|
||||
|
||||
let response = {
|
||||
headers: res.headers,
|
||||
cookies: []
|
||||
};
|
||||
|
||||
// build assertions request object
|
||||
if (request.headers.cookie) {
|
||||
const cookies = String(request.headers.cookie);
|
||||
cookies.split(/; */).forEach(function (cookie) {
|
||||
request.cookies.push(Assertion.parse(cookie));
|
||||
});
|
||||
}
|
||||
|
||||
// build assertions response object
|
||||
if (
|
||||
Array.isArray(response.headers['set-cookie'])
|
||||
&& response.headers['set-cookie'].length > 0
|
||||
) {
|
||||
response.headers['set-cookie'].forEach(function (val) {
|
||||
response.cookies.push(Assertion.parse(val));
|
||||
});
|
||||
}
|
||||
|
||||
// run assertions
|
||||
let result;
|
||||
assertions.every(function (assertion) {
|
||||
result = assertion(request, response);
|
||||
return (typeof (result) !== 'string');
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find cookie in stack/array
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {array} stack
|
||||
* @returns {object|undefined} cookie
|
||||
*/
|
||||
Assertion.find = function (name, stack) {
|
||||
let cookie;
|
||||
|
||||
stack.every(function (val) {
|
||||
if (name !== val.name) return true;
|
||||
cookie = val;
|
||||
return false;
|
||||
});
|
||||
|
||||
return cookie;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse cookie string
|
||||
*
|
||||
* @param {string} str
|
||||
* @param {object} [options]
|
||||
* @param {function} [options.decode] uri
|
||||
* @param {undefined|boolean} [options.request] headers
|
||||
* @returns {object}
|
||||
*/
|
||||
Assertion.parse = function (str, options) {
|
||||
if (typeof str !== 'string') throw new TypeError('argument str must be a string');
|
||||
|
||||
if (typeof options !== 'object') options = {}; // eslint-disable-line no-param-reassign
|
||||
|
||||
let decode = options.decode || decodeURIComponent;
|
||||
|
||||
let parts = str.split(/; */);
|
||||
|
||||
let cookie = {};
|
||||
|
||||
parts.forEach(function (part, i) {
|
||||
if (i === 1) cookie.options = {};
|
||||
|
||||
let equalsIndex = part.indexOf('=');
|
||||
|
||||
// things that don't look like key=value get true flag
|
||||
if (equalsIndex < 0) {
|
||||
cookie.options[part.trim().toLowerCase()] = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const key = part.substr(0, equalsIndex).trim().toLowerCase();
|
||||
// only assign once
|
||||
if (typeof cookie[key] !== 'undefined') return;
|
||||
|
||||
equalsIndex += 1;
|
||||
let val = part.substr(equalsIndex, part.length).trim();
|
||||
// quoted values
|
||||
if (val[0] === '"') val = val.slice(1, -1);
|
||||
|
||||
let value;
|
||||
try {
|
||||
value = decode(val);
|
||||
} catch (e) {
|
||||
value = val;
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
cookie.options[key] = value;
|
||||
return;
|
||||
}
|
||||
|
||||
cookie.name = key;
|
||||
cookie.value = decode(val);
|
||||
});
|
||||
|
||||
if (typeof cookie.options === 'undefined') cookie.options = {};
|
||||
|
||||
return cookie;
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterate expects
|
||||
*
|
||||
* @param {object|object[]} expects
|
||||
* @param {boolean|function} hasValues
|
||||
* @param {function} [cb]
|
||||
*/
|
||||
Assertion.expects = function (expects, hasValues, cb) {
|
||||
if (!Array.isArray(expects) && typeof expects === 'object') expects = [expects]; // eslint-disable-line no-param-reassign
|
||||
|
||||
let resolvedCb;
|
||||
let resolvedHasValues;
|
||||
if (typeof cb === 'undefined' && typeof hasValues === 'function') {
|
||||
resolvedCb = hasValues;
|
||||
resolvedHasValues = false;
|
||||
} else {
|
||||
resolvedCb = cb;
|
||||
resolvedHasValues = hasValues;
|
||||
}
|
||||
|
||||
expects.forEach(function (expect) {
|
||||
let options = expect.options;
|
||||
if (typeof options !== 'object' && !Array.isArray(options)) {
|
||||
options = (resolvedHasValues) ? {} : [];
|
||||
}
|
||||
|
||||
resolvedCb(Object.assign({}, expect, { options }));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Assert cookies and options are set
|
||||
*
|
||||
* @param {object|object[]} expects cookies
|
||||
* @param {undefined|boolean} [assert]
|
||||
* @returns {function} Assertion
|
||||
*/
|
||||
Assertion.set = function (expects, assert) {
|
||||
if (typeof assert === 'undefined') assert = true; // eslint-disable-line no-param-reassign
|
||||
|
||||
Assertion.expects(expects, function (expect) {
|
||||
assertions.push(function (req, res) {
|
||||
// get expectation cookie
|
||||
const cookie = Assertion.find(expect.name, res.cookies);
|
||||
|
||||
if (assert && !cookie) throw new Error('expected: ' + expect.name + ' cookie to be set');
|
||||
|
||||
if (assert) assertHasProperties(cookie.options, expect.options);
|
||||
else if (cookie) assertNotHasProperties(cookie.options, expect.options);
|
||||
});
|
||||
});
|
||||
|
||||
return Assertion;
|
||||
};
|
||||
|
||||
/**
|
||||
* Assert cookies has been reset
|
||||
*
|
||||
* @param {object|object[]} expects cookies
|
||||
* @param {undefined|boolean} [assert]
|
||||
* @returns {function} Assertion
|
||||
*/
|
||||
Assertion.reset = function (expects, assert) {
|
||||
if (typeof assert === 'undefined') assert = true; // eslint-disable-line no-param-reassign
|
||||
|
||||
Assertion.expects(expects, function (expect) {
|
||||
assertions.push(function (req, res) {
|
||||
// get sent cookie
|
||||
const cookieReq = Assertion.find(expect.name, req.cookies);
|
||||
// get expectation cookie
|
||||
const cookieRes = Assertion.find(expect.name, res.cookies);
|
||||
|
||||
if (assert && (!cookieReq || !cookieRes)) {
|
||||
throw new Error('expected: ' + expect.name + ' cookie to be set');
|
||||
} else if (!assert && cookieReq && cookieRes) {
|
||||
throw new Error('expected: ' + expect.name + ' cookie to be set');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return Assertion;
|
||||
};
|
||||
|
||||
/**
|
||||
* Assert cookies is set and new
|
||||
*
|
||||
* @param {object|object[]} expects cookies
|
||||
* @param {undefined|boolean} [assert]
|
||||
* @returns {function} Assertion
|
||||
*/
|
||||
Assertion.new = function (expects, assert) {
|
||||
if (typeof assert === 'undefined') assert = true; // eslint-disable-line no-param-reassign
|
||||
|
||||
Assertion.expects(expects, function (expect) {
|
||||
assertions.push(function (req, res) {
|
||||
// get sent cookie
|
||||
const cookieReq = Assertion.find(expect.name, req.cookies);
|
||||
// get expectation cookie
|
||||
const cookieRes = Assertion.find(expect.name, res.cookies);
|
||||
|
||||
if (assert) {
|
||||
if (!cookieRes) throw new Error('expected: ' + expect.name + ' cookie to be set');
|
||||
if (cookieReq && cookieRes) {
|
||||
throw new Error('expected: ' + expect.name + ' cookie to NOT already be set');
|
||||
}
|
||||
} else if (!cookieReq || !cookieRes) {
|
||||
throw new Error('expected: ' + expect.name + ' cookie to be set');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return Assertion;
|
||||
};
|
||||
|
||||
/**
|
||||
* Assert cookies expires or max-age has increased
|
||||
*
|
||||
* @param {object|object[]} expects cookies
|
||||
* @param {undefined|boolean} [assert]
|
||||
* @returns {function} Assertion
|
||||
*/
|
||||
Assertion.renew = function (expects, assert) {
|
||||
if (typeof assert === 'undefined') assert = true; // eslint-disable-line no-param-reassign
|
||||
|
||||
Assertion.expects(expects, true, function (expect) {
|
||||
const expectExpires = new Date(expect.options.expires);
|
||||
const expectMaxAge = parseFloat(expect.options['max-age']);
|
||||
|
||||
let baseMessage = 'expected: ' + expect.name;
|
||||
if (!expectExpires.getTime() && !expectMaxAge) {
|
||||
throw new Error(baseMessage + ' expects to have expires or max-age option');
|
||||
}
|
||||
|
||||
assertions.push(function (req, res) {
|
||||
// get sent cookie
|
||||
const cookieReq = Assertion.find(expect.name, req.cookies);
|
||||
// get expectation cookie
|
||||
const cookieRes = Assertion.find(expect.name, res.cookies);
|
||||
|
||||
const cookieMaxAge = (expectMaxAge && cookieRes)
|
||||
? parseFloat(cookieRes.options['max-age'])
|
||||
: undefined;
|
||||
const cookieExpires = (expectExpires.getTime() && cookieRes)
|
||||
? new Date(cookieRes.options.expires)
|
||||
: undefined;
|
||||
|
||||
if (assert) {
|
||||
if (!cookieReq || !cookieRes) {
|
||||
throw new Error(baseMessage + ' cookie to be set');
|
||||
}
|
||||
if (expectMaxAge && (!cookieMaxAge || cookieMaxAge <= expectMaxAge)) {
|
||||
throw new Error(baseMessage + ' cookie max-age to be greater than existing value');
|
||||
}
|
||||
|
||||
if (
|
||||
expectExpires.getTime()
|
||||
&& (!cookieExpires.getTime() || cookieExpires <= expectExpires)
|
||||
) {
|
||||
throw new Error(baseMessage + ' cookie expires to be greater than existing value');
|
||||
}
|
||||
} else if (cookieRes) {
|
||||
if (expectMaxAge && cookieMaxAge > expectMaxAge) {
|
||||
throw new Error(
|
||||
baseMessage + ' cookie max-age to be less than or equal to existing value'
|
||||
);
|
||||
}
|
||||
|
||||
if (expectExpires.getTime() && cookieExpires > expectExpires) {
|
||||
throw new Error(
|
||||
baseMessage + ' cookie expires to be less than or equal to existing value'
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return Assertion;
|
||||
};
|
||||
|
||||
/**
|
||||
* Assert cookies contains values
|
||||
*
|
||||
* @param {object|object[]} expects cookies
|
||||
* @param {undefined|boolean} [assert]
|
||||
* @returns {function} Assertion
|
||||
*/
|
||||
Assertion.contain = function (expects, assert) {
|
||||
if (typeof assert === 'undefined') assert = true; // eslint-disable-line no-param-reassign
|
||||
|
||||
Assertion.expects(expects, function (expect) {
|
||||
const keys = Object.keys(expect.options);
|
||||
|
||||
assertions.push(function (req, res) {
|
||||
// get expectation cookie
|
||||
const cookie = Assertion.find(expect.name, res.cookies);
|
||||
|
||||
if (!cookie) throw new Error('expected: ' + expect.name + ' cookie to be set');
|
||||
|
||||
// check cookie values are equal
|
||||
if ('value' in expect) {
|
||||
try {
|
||||
if (assert) assertEqual(cookie.value, expect.value);
|
||||
else assertNotEqual(cookie.value, expect.value);
|
||||
} catch (e) {
|
||||
if (secret.length) {
|
||||
let value;
|
||||
secret.every(function (sec) {
|
||||
value = signature.unsign(cookie.value.slice(2), sec);
|
||||
return !(value && value === expect.value);
|
||||
});
|
||||
|
||||
if (assert && !value) {
|
||||
throw new Error('expected: ' + expect.name + ' value to equal ' + expect.value);
|
||||
} else if (!assert && value) {
|
||||
throw new Error('expected: ' + expect.name + ' value to NOT equal ' + expect.value);
|
||||
}
|
||||
} else throw e;
|
||||
}
|
||||
}
|
||||
|
||||
keys.forEach(function (key) {
|
||||
const expected = (
|
||||
key === 'max-age'
|
||||
? expect.options[key].toString()
|
||||
: expect.options[key]
|
||||
);
|
||||
if (assert) assertEqual(cookie.options[key], expected);
|
||||
else assertNotEqual(cookie.options[key], expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return Assertion;
|
||||
};
|
||||
|
||||
/**
|
||||
* Assert NOT modifier
|
||||
*
|
||||
* @param {function} method
|
||||
* @param {...*}
|
||||
*/
|
||||
Assertion.not = function (method) {
|
||||
let args = [];
|
||||
|
||||
for (let i = 1; i < arguments.length; i += 1) args.push(arguments[i]);
|
||||
|
||||
args.push(false);
|
||||
|
||||
return Assertion[method].apply(Assertion, args);
|
||||
};
|
||||
|
||||
return Assertion;
|
||||
};
|
||||
32
node_modules/supertest/lib/cookies/index.js
generated
vendored
Normal file
32
node_modules/supertest/lib/cookies/index.js
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
/** Copyright 2015 Gregory Langlais. See LICENSE.txt. */
|
||||
|
||||
'use strict';
|
||||
|
||||
const Assertion = require('./assertion');
|
||||
|
||||
/**
|
||||
* Construct cookies assertion (function)
|
||||
*
|
||||
* @param {null|string|string[]} [secret]
|
||||
* @param {function(req, res)[]} [asserts] ran within returned assertion function
|
||||
* @returns {function} assertion
|
||||
* @constructor
|
||||
*/
|
||||
function ExpectCookies(secret, asserts) {
|
||||
return Assertion(secret, asserts);
|
||||
}
|
||||
|
||||
// build ExpectCookies proxy methods
|
||||
const assertion = Assertion();
|
||||
const methods = Object.getOwnPropertyNames(assertion);
|
||||
|
||||
methods.forEach(function(method) {
|
||||
if (typeof assertion[method] === 'function' && typeof Function[method] === 'undefined') {
|
||||
ExpectCookies[method] = function() {
|
||||
const newAssertion = Assertion();
|
||||
return newAssertion[method].apply(newAssertion, arguments);
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = ExpectCookies;
|
||||
403
node_modules/supertest/lib/test.js
generated
vendored
Normal file
403
node_modules/supertest/lib/test.js
generated
vendored
Normal file
@@ -0,0 +1,403 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
const { inspect } = require('util');
|
||||
const http = require('http');
|
||||
const { STATUS_CODES } = require('http');
|
||||
const { Server } = require('tls');
|
||||
const { deepStrictEqual } = require('assert');
|
||||
const { Request } = require('superagent');
|
||||
let http2;
|
||||
try {
|
||||
http2 = require('http2'); // eslint-disable-line global-require
|
||||
} catch (_) {
|
||||
// eslint-disable-line no-empty
|
||||
}
|
||||
|
||||
/** @typedef {import('superagent').Response} Response */
|
||||
|
||||
class Test extends Request {
|
||||
/**
|
||||
* Initialize a new `Test` with the given `app`,
|
||||
* request `method` and `path`.
|
||||
*
|
||||
* @param {Server} app
|
||||
* @param {String} method
|
||||
* @param {String} path
|
||||
* @api public
|
||||
*/
|
||||
constructor (app, method, path, optHttp2) {
|
||||
super(method.toUpperCase(), path);
|
||||
|
||||
if (typeof app === 'function') {
|
||||
if (optHttp2) {
|
||||
app = http2.createServer(app); // eslint-disable-line no-param-reassign
|
||||
} else {
|
||||
app = http.createServer(app); // eslint-disable-line no-param-reassign
|
||||
}
|
||||
}
|
||||
|
||||
this.redirects(0);
|
||||
this.buffer();
|
||||
this.app = app;
|
||||
this._asserts = [];
|
||||
this.url = typeof app === 'string'
|
||||
? app + path
|
||||
: this.serverAddress(app, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a URL, extracted from a server.
|
||||
*
|
||||
* @param {Server} app
|
||||
* @param {String} path
|
||||
* @returns {String} URL address
|
||||
* @api private
|
||||
*/
|
||||
serverAddress(app, path) {
|
||||
const addr = app.address();
|
||||
|
||||
if (!addr) this._server = app.listen(0);
|
||||
// } else {
|
||||
// this._server = app;
|
||||
// }
|
||||
const port = app.address().port;
|
||||
const protocol = app instanceof Server ? 'https' : 'http';
|
||||
return protocol + '://127.0.0.1:' + port + path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expectations:
|
||||
*
|
||||
* .expect(200)
|
||||
* .expect(200, fn)
|
||||
* .expect(200, body)
|
||||
* .expect('Some body')
|
||||
* .expect('Some body', fn)
|
||||
* .expect(['json array body', { key: 'val' }])
|
||||
* .expect('Content-Type', 'application/json')
|
||||
* .expect('Content-Type', 'application/json', fn)
|
||||
* .expect(fn)
|
||||
* .expect([200, 404])
|
||||
*
|
||||
* @return {Test}
|
||||
* @api public
|
||||
*/
|
||||
expect(a, b, c) {
|
||||
// callback
|
||||
if (typeof a === 'function') {
|
||||
this._asserts.push(wrapAssertFn(a));
|
||||
return this;
|
||||
}
|
||||
if (typeof b === 'function') this.end(b);
|
||||
if (typeof c === 'function') this.end(c);
|
||||
|
||||
// status
|
||||
if (typeof a === 'number') {
|
||||
this._asserts.push(wrapAssertFn(this._assertStatus.bind(this, a)));
|
||||
// body
|
||||
if (typeof b !== 'function' && arguments.length > 1) {
|
||||
this._asserts.push(wrapAssertFn(this._assertBody.bind(this, b)));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// multiple statuses
|
||||
if (Array.isArray(a) && a.length > 0 && a.every(val => typeof val === 'number')) {
|
||||
this._asserts.push(wrapAssertFn(this._assertStatusArray.bind(this, a)));
|
||||
return this;
|
||||
}
|
||||
|
||||
// header field
|
||||
if (typeof b === 'string' || typeof b === 'number' || b instanceof RegExp) {
|
||||
this._asserts.push(wrapAssertFn(this._assertHeader.bind(this, { name: '' + a, value: b })));
|
||||
return this;
|
||||
}
|
||||
|
||||
// body
|
||||
this._asserts.push(wrapAssertFn(this._assertBody.bind(this, a)));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defer invoking superagent's `.end()` until
|
||||
* the server is listening.
|
||||
*
|
||||
* @param {?Function} fn
|
||||
* @api public
|
||||
*/
|
||||
end(fn) {
|
||||
const server = this._server;
|
||||
|
||||
super.end((err, res) => {
|
||||
const localAssert = () => {
|
||||
this.assert(err, res, fn);
|
||||
};
|
||||
|
||||
if (server && server._handle) {
|
||||
// Handle server closing with error handling for already closed servers
|
||||
return server.close((closeError) => {
|
||||
// Ignore ERR_SERVER_NOT_RUNNING errors as the server is already closed
|
||||
if (closeError && closeError.code === 'ERR_SERVER_NOT_RUNNING') {
|
||||
return localAssert();
|
||||
}
|
||||
// For other errors, pass them through
|
||||
if (closeError) {
|
||||
return localAssert();
|
||||
}
|
||||
localAssert();
|
||||
});
|
||||
}
|
||||
|
||||
localAssert();
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform assertions and invoke `fn(err, res)`.
|
||||
*
|
||||
* @param {?Error} resError
|
||||
* @param {Response} res
|
||||
* @param {Function} fn
|
||||
* @api private
|
||||
*/
|
||||
assert(resError, res, fn) {
|
||||
let errorObj;
|
||||
|
||||
// check for unexpected network errors or server not running/reachable errors
|
||||
// when there is no response and superagent sends back a System Error
|
||||
// do not check further for other asserts, if any, in such case
|
||||
// https://nodejs.org/api/errors.html#errors_common_system_errors
|
||||
const sysErrors = {
|
||||
ECONNREFUSED: 'Connection refused',
|
||||
ECONNRESET: 'Connection reset by peer',
|
||||
EPIPE: 'Broken pipe',
|
||||
ETIMEDOUT: 'Operation timed out'
|
||||
};
|
||||
|
||||
if (!res && resError) {
|
||||
if (resError instanceof Error && resError.syscall === 'connect'
|
||||
&& Object.getOwnPropertyNames(sysErrors).indexOf(resError.code) >= 0) {
|
||||
errorObj = new Error(resError.code + ': ' + sysErrors[resError.code]);
|
||||
} else {
|
||||
errorObj = resError;
|
||||
}
|
||||
}
|
||||
|
||||
// asserts
|
||||
for (let i = 0; i < this._asserts.length && !errorObj; i += 1) {
|
||||
errorObj = this._assertFunction(this._asserts[i], res);
|
||||
}
|
||||
|
||||
// set unexpected superagent error if no other error has occurred.
|
||||
if (!errorObj && resError instanceof Error && (!res || resError.status !== res.status)) {
|
||||
errorObj = resError;
|
||||
}
|
||||
|
||||
if (fn) {
|
||||
fn.call(this, errorObj || null, res);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Adds a set Authorization Bearer
|
||||
*
|
||||
* @param {Bearer} Bearer Token
|
||||
* Shortcut for .set('Authorization', `Bearer ${token}`)
|
||||
*/
|
||||
|
||||
bearer(token) {
|
||||
this.set('Authorization', `Bearer ${token}`);
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* Adds a set Authorization Bearer
|
||||
*
|
||||
* @param {Bearer} Bearer Token
|
||||
* Shortcut for .set('Authorization', `Bearer ${token}`)
|
||||
*/
|
||||
|
||||
bearer(token) {
|
||||
this.set('Authorization', `Bearer ${token}`);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform assertions on a response body and return an Error upon failure.
|
||||
*
|
||||
* @param {Mixed} body
|
||||
* @param {Response} res
|
||||
* @return {?Error}
|
||||
* @api private
|
||||
*/// eslint-disable-next-line class-methods-use-this
|
||||
_assertBody(body, res) {
|
||||
const isRegexp = body instanceof RegExp;
|
||||
|
||||
// parsed
|
||||
if (typeof body === 'object' && !isRegexp) {
|
||||
try {
|
||||
deepStrictEqual(body, res.body);
|
||||
} catch (err) {
|
||||
const a = inspect(body);
|
||||
const b = inspect(res.body);
|
||||
return error('expected ' + a + ' response body, got ' + b, body, res.body);
|
||||
}
|
||||
} else if (body !== res.text) {
|
||||
// string
|
||||
const a = inspect(body);
|
||||
const b = inspect(res.text);
|
||||
|
||||
// regexp
|
||||
if (isRegexp) {
|
||||
if (!body.test(res.text)) {
|
||||
return error('expected body ' + b + ' to match ' + body, body, res.body);
|
||||
}
|
||||
} else {
|
||||
return error('expected ' + a + ' response body, got ' + b, body, res.body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform assertions on a response header and return an Error upon failure.
|
||||
*
|
||||
* @param {Object} header
|
||||
* @param {Response} res
|
||||
* @return {?Error}
|
||||
* @api private
|
||||
*/// eslint-disable-next-line class-methods-use-this
|
||||
_assertHeader(header, res) {
|
||||
const field = header.name;
|
||||
const actual = res.header[field.toLowerCase()];
|
||||
const fieldExpected = header.value;
|
||||
|
||||
if (typeof actual === 'undefined') return new Error('expected "' + field + '" header field');
|
||||
// This check handles header values that may be a String or single element Array
|
||||
if ((Array.isArray(actual) && actual.toString() === fieldExpected)
|
||||
|| fieldExpected === actual) {
|
||||
return;
|
||||
}
|
||||
if (fieldExpected instanceof RegExp) {
|
||||
if (!fieldExpected.test(actual)) {
|
||||
return new Error('expected "' + field + '" matching '
|
||||
+ fieldExpected + ', got "' + actual + '"');
|
||||
}
|
||||
} else {
|
||||
return new Error('expected "' + field + '" of "' + fieldExpected + '", got "' + actual + '"');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform assertions on the response status and return an Error upon failure.
|
||||
*
|
||||
* @param {Number} status
|
||||
* @param {Response} res
|
||||
* @return {?Error}
|
||||
* @api private
|
||||
*/// eslint-disable-next-line class-methods-use-this
|
||||
_assertStatus(status, res) {
|
||||
if (res.status !== status) {
|
||||
const a = STATUS_CODES[status];
|
||||
const b = STATUS_CODES[res.status];
|
||||
return new Error('expected ' + status + ' "' + a + '", got ' + res.status + ' "' + b + '"');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform assertions on the response status and return an Error upon failure.
|
||||
*
|
||||
* @param {Array<Number>} statusArray
|
||||
* @param {Response} res
|
||||
* @return {?Error}
|
||||
* @api private
|
||||
*/// eslint-disable-next-line class-methods-use-this
|
||||
_assertStatusArray(statusArray, res) {
|
||||
if (!statusArray.includes(res.status)) {
|
||||
const b = STATUS_CODES[res.status];
|
||||
const expectedList = statusArray.join(', ');
|
||||
return new Error(
|
||||
'expected one of "' + expectedList + '", got ' + res.status + ' "' + b + '"'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an assertion by calling a function and return an Error upon failure.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @param {Response} res
|
||||
* @return {?Error}
|
||||
* @api private
|
||||
*/// eslint-disable-next-line class-methods-use-this
|
||||
_assertFunction(fn, res) {
|
||||
let err;
|
||||
try {
|
||||
err = fn(res);
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
if (err instanceof Error) return err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps an assert function into another.
|
||||
* The wrapper function edit the stack trace of any assertion error, prepending a more useful stack to it.
|
||||
*
|
||||
* @param {Function} assertFn
|
||||
* @returns {Function} wrapped assert function
|
||||
*/
|
||||
|
||||
function wrapAssertFn(assertFn) {
|
||||
const savedStack = new Error().stack.split('\n').slice(3);
|
||||
|
||||
return function(res) {
|
||||
let badStack;
|
||||
let err;
|
||||
try {
|
||||
err = assertFn(res);
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
if (err instanceof Error && err.stack) {
|
||||
badStack = err.stack.replace(err.message, '').split('\n').slice(1);
|
||||
err.stack = [err.toString()]
|
||||
.concat(savedStack)
|
||||
.concat('----')
|
||||
.concat(badStack)
|
||||
.join('\n');
|
||||
}
|
||||
return err;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an `Error` with `msg` and results properties.
|
||||
*
|
||||
* @param {String} msg
|
||||
* @param {Mixed} expected
|
||||
* @param {Mixed} actual
|
||||
* @return {Error}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function error(msg, expected, actual) {
|
||||
const err = new Error(msg);
|
||||
err.expected = expected;
|
||||
err.actual = actual;
|
||||
err.showDiff = true;
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose `Test`.
|
||||
*/
|
||||
|
||||
module.exports = Test;
|
||||
Reference in New Issue
Block a user