This commit is contained in:
2026-03-03 15:23:00 +00:00
parent 5e3726de39
commit 8e223bfbec
3689 changed files with 955330 additions and 1011 deletions

650
node_modules/node-mocks-http/lib/mockRequest.js generated vendored Normal file
View File

@@ -0,0 +1,650 @@
/**
* File: mockRequest
*
* This file implements node.js's implementation of a 'request' object.
* This is actually closer to what Express offers the user, in that the
* body is really a parsed object of values.
*
* @author Howard Abrams <howard.abrams@gmail.com>
*/
/**
* Function: createRequest
*
* Creates a new mock 'request' instance. All values are reset to the
* defaults.
*
* Parameters:
*
* options - An object of named parameters.
*
* Options:
*
* method - The method value, see <mockRequest._setMethod>
* url - The url value, see <mockRequest._setURL>
* originalUrl - The originalUrl value, see <mockRequest._setOriginalUrl>
* baseUrl - The baseUrl value, see <mockRequest._setBaseUrl>
* params - The parameters, see <mockRequest._setParam>
* body - The body values, , see <mockRequest._setBody>
*/
const url = require('url');
const typeis = require('type-is');
const accepts = require('accepts');
const parseRange = require('range-parser');
let { EventEmitter } = require('events');
const querystring = require('querystring');
const { createHeaders, getHeaderValue } = require('./headers');
const standardRequestOptions = [
'method',
'url',
'originalUrl',
'baseUrl',
'path',
'params',
'session',
'cookies',
'headers',
'body',
'query',
'files'
];
function createRequest(options = {}) {
if (options.eventEmitter) {
EventEmitter = options.eventEmitter;
}
// create mockRequest
const mockRequest = Object.create(EventEmitter.prototype);
EventEmitter.call(mockRequest);
mockRequest.method = options.method ? options.method : 'GET';
mockRequest.url = options.url || options.path || '';
mockRequest.originalUrl = options.originalUrl || mockRequest.url;
mockRequest.baseUrl = options.baseUrl || mockRequest.url;
mockRequest.path = options.path || (options.url ? url.parse(options.url).pathname : '');
mockRequest.params = options.params ? options.params : {};
if (options.session) {
mockRequest.session = options.session;
}
mockRequest.cookies = options.cookies ? options.cookies : {};
if (options.signedCookies) {
mockRequest.signedCookies = options.signedCookies;
}
// Create headers using the Headers.js module
mockRequest.headers = createHeaders(options.headers);
mockRequest.body = options.body ? options.body : {};
mockRequest.query = options.query ? options.query : {};
mockRequest.files = options.files ? options.files : {};
mockRequest.socket = options.socket ? options.socket : {};
mockRequest.ip = options.ip || '127.0.0.1';
mockRequest.ips = [mockRequest.ip];
mockRequest.destroy = () => {};
// parse query string from url to object
if (Object.keys(mockRequest.query).length === 0) {
mockRequest.query = querystring.parse(mockRequest.url.split('?')[1]);
if (!mockRequest.query.hasOwnProperty) {
Object.defineProperty(mockRequest.query, 'hasOwnProperty', {
enumerable: false,
value: Object.hasOwnProperty.bind(mockRequest.query)
});
}
}
// attach any other provided objects into the request for more advanced testing
for (const n in options) {
if (standardRequestOptions.indexOf(n) === -1) {
mockRequest[n] = options[n];
}
}
/**
* Return request header.
*
* The `Referrer` header field is special-cased,
* both `Referrer` and `Referer` are interchangeable.
*
* Examples:
*
* mockRequest.get('Content-Type');
* // => "text/plain"
*
* mockRequest.get('content-type');
* // => "text/plain"
*
* mockRequest.get('Something');
* // => undefined
*
* Aliased as `mockRequest.header()`.
*
* @param {String} name
* @return {String}
* @api public
*/
mockRequest.getHeader = function getHeader(name) {
return getHeaderValue(mockRequest.headers, name);
};
mockRequest.header = mockRequest.getHeader;
mockRequest.get = mockRequest.getHeader;
/**
* Function: is
*
* Checks for matching content types in the content-type header.
* Requires a request body, identified by transfer-encoding or content-length headers
*
* Examples:
*
* mockRequest.headers['content-type'] = 'text/html';
* mockRequest.headers['transfer-encoding'] = 'chunked';
* mockRequest.headers['content-length'] = '100';
*
* mockRequest.is('html');
* // => "html"
*
* mockRequest.is('json');
* // => false
*
* mockRequest.is(['json', 'html', 'text']);
* // => "html"
*
* @param {String|String[]} types content type or array of types to match
* @return {String|false|null} Matching content type as string, false if no match, null if request has no body.
* @api public
*/
mockRequest.is = function isContentType(...args) {
let types = args;
if (Array.isArray(args[0])) {
types = args[0];
}
return typeis(mockRequest, types);
};
/**
* Function: accepts
*
* Checks for matching content types in the Accept header.
*
* Examples:
*
* mockRequest.headers['accept'] = 'application/json'
*
* mockRequest.accepts('json');
* // => 'json'
*
* mockRequest.accepts('html');
* // => false
*
* mockRequest.accepts(['html', 'json']);
* // => 'json'
*
* @param {String|String[]} types Mime type(s) to check against
* @return {String|false} Matching type or false if no match.
*/
mockRequest.accepts = function acceptsTypes(types) {
const Accepts = accepts(mockRequest);
return Accepts.type(types);
};
/**
* Check if the given `encoding`s are accepted.
*
* @param {String} ...encoding
* @return {String|Array}
* @public
*/
mockRequest.acceptsEncodings = function acceptsEncodings(...args) {
let encodings = args;
if (Array.isArray(args[0])) {
encodings = args[0];
}
const accept = accepts(mockRequest);
return accept.encodings(encodings);
};
/**
* Check if the given `charset`s are acceptable,
* otherwise you should respond with 406 "Not Acceptable".
*
* @param {String} ...charset
* @return {String|Array}
* @public
*/
mockRequest.acceptsCharsets = function acceptsCharsets(...args) {
let charsets = args;
if (Array.isArray(args[0])) {
charsets = args[0];
}
const accept = accepts(mockRequest);
return accept.charsets(charsets);
};
/**
* Check if the given `lang`s are acceptable,
* otherwise you should respond with 406 "Not Acceptable".
*
* @param {String} ...lang
* @return {String|Array}
* @public
*/
mockRequest.acceptsLanguages = function acceptsLanguages(...args) {
let languages = args;
if (Array.isArray(args[0])) {
languages = args[0];
}
const accept = accepts(mockRequest);
return accept.languages(languages);
};
/**
* Function: range
*
* Parse Range header field, capping to the given `size`.
*
* Unspecified ranges such as "0-" require knowledge of your resource length. In
* the case of a byte range this is of course the total number of bytes. If the
* Range header field is not given `undefined` is returned, `-1` when unsatisfiable,
* and `-2` when syntactically invalid.
*
* When ranges are returned, the array has a "type" property which is the type of
* range that is required (most commonly, "bytes"). Each array element is an object
* with a "start" and "end" property for the portion of the range.
*
* The "combine" option can be set to `true` and overlapping & adjacent ranges
* will be combined into a single range.
*
* NOTE: remember that ranges are inclusive, so for example "Range: users=0-3"
* should respond with 4 users when available, not 3.
*
* @param {number} size
* @param {object} [opts]
* @param {boolean} [opts.combine=false]
* @return {false|number|array}
* @public
*/
mockRequest.range = function isRange(size, opts) {
const range = mockRequest.get('Range');
if (!range) {
return undefined;
}
return parseRange(size, range, opts);
};
/**
* Function: param
*
* Return the value of param name when present.
* Lookup is performed in the following order:
* - req.params
* - req.body
* - req.query
*/
mockRequest.param = function param(parameterName, defaultValue) {
if ({}.hasOwnProperty.call(mockRequest.params, parameterName)) {
return mockRequest.params[parameterName];
}
if ({}.hasOwnProperty.call(mockRequest.body, parameterName)) {
return mockRequest.body[parameterName];
}
if ({}.hasOwnProperty.call(mockRequest.query, parameterName)) {
return mockRequest.query[parameterName];
}
return defaultValue;
};
/**
* Function: _setParameter
*
* Set parameters that the client can then get using the 'params'
* key.
*
* Parameters:
*
* key - The key. For instance, 'bob' would be accessed: request.params.bob
* value - The value to return when accessed.
*/
mockRequest._setParameter = function _setParameter(key, value) {
mockRequest.params[key] = value;
};
/**
* Sets a variable that is stored in the session.
*
* @param variable The variable to store in the session
* @param value The value associated with the variable
*/
mockRequest._setSessionVariable = function _setSessionVariable(variable, value) {
if (typeof mockRequest.session !== 'object') {
mockRequest.session = {};
}
mockRequest.session[variable] = value;
};
/**
* Sets a variable that is stored in the cookies.
*
* @param variable The variable to store in the cookies
* @param value The value associated with the variable
*/
mockRequest._setCookiesVariable = function _setCookiesVariable(variable, value) {
mockRequest.cookies[variable] = value;
};
/**
* Sets a variable that is stored in the signed cookies.
*
* @param variable The variable to store in the signed cookies
* @param value The value associated with the variable
*/
mockRequest._setSignedCookiesVariable = function _setSignedCookiesVariable(variable, value) {
if (typeof mockRequest.signedCookies !== 'object') {
mockRequest.signedCookies = {};
}
mockRequest.signedCookies[variable] = value;
};
/**
* Sets a variable that is stored in the headers.
*
* @param variable The variable to store in the headers
* @param value The value associated with the variable
*/
mockRequest._setHeadersVariable = function _setHeadersVariable(variable, value) {
mockRequest.headers[variable] = value;
};
/**
* Sets a variable that is stored in the files.
*
* @param variable The variable to store in the files
* @param value The value associated with the variable
*/
mockRequest._setFilesVariable = function _setFilesVariable(variable, value) {
mockRequest.files[variable] = value;
};
/**
* Function: _setMethod
*
* Sets the HTTP method that the client gets when the called the 'method'
* property. This defaults to 'GET' if it is not set.
*
* Parameters:
*
* method - The HTTP method, e.g. GET, POST, PUT, DELETE, etc.
*
* Note: We don't validate the string. We just return it.
*/
mockRequest._setMethod = function _setMethod(method) {
mockRequest.method = method;
};
/**
* Function: _setURL
*
* Sets the URL value that the client gets when the called the 'url'
* property.
*
* Parameters:
*
* value - The request path, e.g. /my-route/452
*
* Note: We don't validate the string. We just return it. Typically, these
* do not include hostname, port or that part of the URL.
*/
mockRequest._setURL = function _setURL(value) {
mockRequest.url = value;
};
/**
* Function: _setBaseUrl
*
* Sets the URL value that the client gets when the called the 'baseUrl'
* property.
*
* Parameters:
*
* value - The request base path, e.g. /my-route
*
* Note: We don't validate the string. We just return it. Typically, these
* do not include hostname, port or that part of the URL.
*/
mockRequest._setBaseUrl = function _setBaseUrl(value) {
mockRequest.baseUrl = value;
};
/**
* Function: _setOriginalUrl
*
* Sets the URL value that the client gets when the called the 'originalUrl'
* property.
*
* Parameters:
*
* value - The request path, e.g. /my-route/452
*
* Note: We don't validate the string. We just return it. Typically, these
* do not include hostname, port or that part of the URL.
*/
mockRequest._setOriginalUrl = function _setOriginalUrl(value) {
mockRequest.originalUrl = value;
};
/**
* Function: _setBody
*
* Sets the body that the client gets when the called the 'body'
* parameter. This defaults to 'GET' if it is not set.
*
* Parameters:
*
* body - An object representing the body.
*
* If you expect the 'body' to come from a form, this typically means that
* it would be a flat object of properties and values, as in:
*
* > { name: 'Howard Abrams',
* > age: 522
* > }
*
* If the client is expecting a JSON object through a REST interface, then
* this object could be anything.
*/
mockRequest._setBody = function _setBody(body) {
mockRequest.body = body;
};
/**
* Function: _addBody
*
* Adds another body parameter the client gets when calling the 'body'
* parameter with another property value, e.g. the name of a form element
* that was passed in.
*
* Parameters:
*
* key - The key. For instance, 'bob' would be accessed: request.params.bob
* value - The value to return when accessed.
*/
mockRequest._addBody = function _addBody(key, value) {
mockRequest.body[key] = value;
};
/**
* Function: send
*
* Write data to the request stream which will trigger request's 'data', and 'end' event
*
* Parameters:
*
* data - string, array, object, number, buffer
*/
mockRequest.send = function send(data) {
if (Buffer.isBuffer(data)) {
this.emit('data', data);
} else if (typeof data === 'object' || typeof data === 'number') {
this.emit('data', Buffer.from(JSON.stringify(data)));
} else if (typeof data === 'string') {
this.emit('data', Buffer.from(data));
}
this.emit('end');
};
/**
* Function: hostname
*
* If Hostname is not set explicitly, then derive it from the Host header without port information
*
*/
if (!mockRequest.hostname) {
mockRequest.hostname = (function getHostname() {
if (!mockRequest.headers.host) {
return '';
}
const hostname = mockRequest.headers.host.split(':')[0].split('.');
return hostname.join('.');
})();
}
/**
* Function: subdomains
*
* Subdomains are the dot-separated parts of the host before the main domain of the app.
*
*/
mockRequest.subdomains = (function getSubdomains() {
if (!mockRequest.headers.host) {
return [];
}
const offset = 2;
const subdomains = mockRequest.headers.host.split('.').reverse();
return subdomains.slice(offset);
})();
/**
* Function: asyncIterator
*
* Buffers data, error, end, and close events and yields them in order.
* Unlike stream.Readable, this async iterator implementation will not exit
* early on error or close.
*/
mockRequest[Symbol.asyncIterator] = async function* asyncIterator() {
let ended = false;
let closed = false;
let error = null;
const chunks = [];
let resolvePromise = null;
const promiseExecutor = (resolve) => {
resolvePromise = resolve;
};
const promiseResolver = () => {
if (resolvePromise) {
resolvePromise();
resolvePromise = null;
}
};
const dataEventHandler = (chunk) => {
if (ended || closed || error) {
return;
}
chunks.push(chunk);
promiseResolver();
};
const endEventHandler = () => {
if (ended || closed || error) {
return;
}
ended = true;
promiseResolver();
};
const closeEventHandler = () => {
if (closed || error) {
return;
}
closed = true;
promiseResolver();
};
const errorEventHandler = (err) => {
if (closed || error) {
return;
}
error = err;
promiseResolver();
};
mockRequest.on('data', dataEventHandler);
mockRequest.on('end', endEventHandler);
mockRequest.on('close', closeEventHandler);
mockRequest.on('error', errorEventHandler);
// Emit custom event after entering the loop.
setTimeout(() => {
this.emit('async_iterator');
});
try {
for (;;) {
// eslint-disable-next-line no-await-in-loop
await new Promise(promiseExecutor);
let i = 0;
for (;;) {
if (error) {
throw error;
}
if (closed) {
return;
}
const hasChunks = i < chunks.length;
if (!hasChunks) {
if (ended) {
// End signaled. Bail.
return;
}
// Wait for next push.
break;
}
const chunk = chunks[i];
chunks[i] = undefined;
i += 1;
yield chunk;
}
chunks.length = 0;
}
} finally {
chunks.length = 0;
error = null;
mockRequest.off('data', dataEventHandler);
mockRequest.off('end', endEventHandler);
mockRequest.off('close', closeEventHandler);
mockRequest.off('error', errorEventHandler);
}
};
return mockRequest;
}
module.exports.createRequest = createRequest;