next
Version:
The React Framework
385 lines (384 loc) • 13.8 kB
JavaScript
import Stream from 'stream';
import { fromNodeOutgoingHttpHeaders, toNodeOutgoingHttpHeaders } from '../web/utils';
export class MockedRequest extends Stream.Readable {
constructor({ url, headers, method, socket = null, readable }){
super(), // This is hardcoded for now, but can be updated to be configurable if needed.
this.httpVersion = '1.0', this.httpVersionMajor = 1, this.httpVersionMinor = 0, // If we don't actually have a socket, we'll just use a mock one that
// always returns false for the `encrypted` property and undefined for the
// `remoteAddress` property.
this.socket = new Proxy({}, {
get: (_target, prop)=>{
if (prop !== 'encrypted' && prop !== 'remoteAddress') {
throw Object.defineProperty(new Error('Method not implemented'), "__NEXT_ERROR_CODE", {
value: "E52",
enumerable: false,
configurable: true
});
}
if (prop === 'remoteAddress') return undefined;
// For this mock request, always ensure we just respond with the encrypted
// set to false to ensure there's no odd leakages.
return false;
}
});
this.url = url;
this.headers = headers;
this.method = method;
if (readable) {
this.bodyReadable = readable;
this.bodyReadable.on('end', ()=>this.emit('end'));
this.bodyReadable.on('close', ()=>this.emit('close'));
}
if (socket) {
this.socket = socket;
}
}
get headersDistinct() {
const headers = {};
for (const [key, value] of Object.entries(this.headers)){
if (!value) continue;
headers[key] = Array.isArray(value) ? value : [
value
];
}
return headers;
}
_read(size) {
if (this.bodyReadable) {
return this.bodyReadable._read(size);
} else {
this.emit('end');
this.emit('close');
}
}
/**
* The `connection` property is just an alias for the `socket` property.
*
* @deprecated — since v13.0.0 - Use socket instead.
*/ get connection() {
return this.socket;
}
// The following methods are not implemented as they are not used in the
// Next.js codebase.
get aborted() {
throw Object.defineProperty(new Error('Method not implemented'), "__NEXT_ERROR_CODE", {
value: "E52",
enumerable: false,
configurable: true
});
}
get complete() {
throw Object.defineProperty(new Error('Method not implemented'), "__NEXT_ERROR_CODE", {
value: "E52",
enumerable: false,
configurable: true
});
}
get trailers() {
throw Object.defineProperty(new Error('Method not implemented'), "__NEXT_ERROR_CODE", {
value: "E52",
enumerable: false,
configurable: true
});
}
get trailersDistinct() {
throw Object.defineProperty(new Error('Method not implemented'), "__NEXT_ERROR_CODE", {
value: "E52",
enumerable: false,
configurable: true
});
}
get rawTrailers() {
throw Object.defineProperty(new Error('Method not implemented'), "__NEXT_ERROR_CODE", {
value: "E52",
enumerable: false,
configurable: true
});
}
get rawHeaders() {
throw Object.defineProperty(new Error('Method not implemented.'), "__NEXT_ERROR_CODE", {
value: "E41",
enumerable: false,
configurable: true
});
}
setTimeout() {
throw Object.defineProperty(new Error('Method not implemented.'), "__NEXT_ERROR_CODE", {
value: "E41",
enumerable: false,
configurable: true
});
}
}
export class MockedResponse extends Stream.Writable {
constructor(res = {}){
super(), this.statusMessage = '', this.finished = false, this.headersSent = false, /**
* A list of buffers that have been written to the response.
*
* @internal - used internally by Next.js
*/ this.buffers = [];
this.statusCode = res.statusCode ?? 200;
this.socket = res.socket ?? null;
this.headers = res.headers ? fromNodeOutgoingHttpHeaders(res.headers) : new Headers();
this.headPromise = new Promise((resolve)=>{
this.headPromiseResolve = resolve;
});
// Attach listeners for the `finish`, `end`, and `error` events to the
// `MockedResponse` instance.
this.hasStreamed = new Promise((resolve, reject)=>{
this.on('finish', ()=>resolve(true));
this.on('end', ()=>resolve(true));
this.on('error', (err)=>reject(err));
}).then((val)=>{
this.headPromiseResolve == null ? void 0 : this.headPromiseResolve.call(this);
return val;
});
if (res.resWriter) {
this.resWriter = res.resWriter;
}
}
appendHeader(name, value) {
const values = Array.isArray(value) ? value : [
value
];
for (const v of values){
this.headers.append(name, v);
}
return this;
}
/**
* Returns true if the response has been sent, false otherwise.
*
* @internal - used internally by Next.js
*/ get isSent() {
return this.finished || this.headersSent;
}
/**
* The `connection` property is just an alias for the `socket` property.
*
* @deprecated — since v13.0.0 - Use socket instead.
*/ get connection() {
return this.socket;
}
write(chunk) {
if (this.resWriter) {
return this.resWriter(chunk);
}
this.buffers.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
return true;
}
end() {
this.finished = true;
return super.end(...arguments);
}
/**
* This method is a no-op because the `MockedResponse` instance is not
* actually connected to a socket. This method is not specified on the
* interface type for `ServerResponse` but is called by Node.js.
*
* @see https://github.com/nodejs/node/pull/7949
*/ _implicitHeader() {}
_write(chunk, _encoding, callback) {
this.write(chunk);
// According to Node.js documentation, the callback MUST be invoked to
// signal that the write completed successfully. If this callback is not
// invoked, the 'finish' event will not be emitted.
//
// https://nodejs.org/docs/latest-v16.x/api/stream.html#writable_writechunk-encoding-callback
callback();
}
writeHead(statusCode, statusMessage, headers) {
if (!headers && typeof statusMessage !== 'string') {
headers = statusMessage;
} else if (typeof statusMessage === 'string' && statusMessage.length > 0) {
this.statusMessage = statusMessage;
}
if (headers) {
// When headers have been set with response.setHeader(), they will be
// merged with any headers passed to response.writeHead(), with the
// headers passed to response.writeHead() given precedence.
//
// https://nodejs.org/api/http.html#responsewriteheadstatuscode-statusmessage-headers
//
// For this reason, we need to only call `set` to ensure that this will
// overwrite any existing headers.
if (Array.isArray(headers)) {
// headers may be an Array where the keys and values are in the same list.
// It is not a list of tuples. So, the even-numbered offsets are key
// values, and the odd-numbered offsets are the associated values. The
// array is in the same format as request.rawHeaders.
for(let i = 0; i < headers.length; i += 2){
// The header key is always a string according to the spec.
this.setHeader(headers[i], headers[i + 1]);
}
} else {
for (const [key, value] of Object.entries(headers)){
// Skip undefined values
if (typeof value === 'undefined') continue;
this.setHeader(key, value);
}
}
}
this.statusCode = statusCode;
this.headersSent = true;
this.headPromiseResolve == null ? void 0 : this.headPromiseResolve.call(this);
return this;
}
hasHeader(name) {
return this.headers.has(name);
}
getHeader(name) {
return this.headers.get(name) ?? undefined;
}
getHeaders() {
return toNodeOutgoingHttpHeaders(this.headers);
}
getHeaderNames() {
return Array.from(this.headers.keys());
}
setHeader(name, value) {
if (Array.isArray(value)) {
// Because `set` here should override any existing values, we need to
// delete the existing values before setting the new ones via `append`.
this.headers.delete(name);
for (const v of value){
this.headers.append(name, v);
}
} else if (typeof value === 'number') {
this.headers.set(name, value.toString());
} else {
this.headers.set(name, value);
}
return this;
}
removeHeader(name) {
this.headers.delete(name);
}
flushHeaders() {
// This is a no-op because we don't actually have a socket to flush the
// headers to.
}
// The following methods are not implemented as they are not used in the
// Next.js codebase.
get strictContentLength() {
throw Object.defineProperty(new Error('Method not implemented.'), "__NEXT_ERROR_CODE", {
value: "E41",
enumerable: false,
configurable: true
});
}
writeEarlyHints() {
throw Object.defineProperty(new Error('Method not implemented.'), "__NEXT_ERROR_CODE", {
value: "E41",
enumerable: false,
configurable: true
});
}
get req() {
throw Object.defineProperty(new Error('Method not implemented.'), "__NEXT_ERROR_CODE", {
value: "E41",
enumerable: false,
configurable: true
});
}
assignSocket() {
throw Object.defineProperty(new Error('Method not implemented.'), "__NEXT_ERROR_CODE", {
value: "E41",
enumerable: false,
configurable: true
});
}
detachSocket() {
throw Object.defineProperty(new Error('Method not implemented.'), "__NEXT_ERROR_CODE", {
value: "E41",
enumerable: false,
configurable: true
});
}
writeContinue() {
throw Object.defineProperty(new Error('Method not implemented.'), "__NEXT_ERROR_CODE", {
value: "E41",
enumerable: false,
configurable: true
});
}
writeProcessing() {
throw Object.defineProperty(new Error('Method not implemented.'), "__NEXT_ERROR_CODE", {
value: "E41",
enumerable: false,
configurable: true
});
}
get upgrading() {
throw Object.defineProperty(new Error('Method not implemented.'), "__NEXT_ERROR_CODE", {
value: "E41",
enumerable: false,
configurable: true
});
}
get chunkedEncoding() {
throw Object.defineProperty(new Error('Method not implemented.'), "__NEXT_ERROR_CODE", {
value: "E41",
enumerable: false,
configurable: true
});
}
get shouldKeepAlive() {
throw Object.defineProperty(new Error('Method not implemented.'), "__NEXT_ERROR_CODE", {
value: "E41",
enumerable: false,
configurable: true
});
}
get useChunkedEncodingByDefault() {
throw Object.defineProperty(new Error('Method not implemented.'), "__NEXT_ERROR_CODE", {
value: "E41",
enumerable: false,
configurable: true
});
}
get sendDate() {
throw Object.defineProperty(new Error('Method not implemented.'), "__NEXT_ERROR_CODE", {
value: "E41",
enumerable: false,
configurable: true
});
}
setTimeout() {
throw Object.defineProperty(new Error('Method not implemented.'), "__NEXT_ERROR_CODE", {
value: "E41",
enumerable: false,
configurable: true
});
}
addTrailers() {
throw Object.defineProperty(new Error('Method not implemented.'), "__NEXT_ERROR_CODE", {
value: "E41",
enumerable: false,
configurable: true
});
}
setHeaders() {
throw Object.defineProperty(new Error('Method not implemented.'), "__NEXT_ERROR_CODE", {
value: "E41",
enumerable: false,
configurable: true
});
}
}
export function createRequestResponseMocks({ url, headers = {}, method = 'GET', bodyReadable, resWriter, socket = null }) {
return {
req: new MockedRequest({
url,
headers,
method,
socket,
readable: bodyReadable
}),
res: new MockedResponse({
socket,
resWriter
})
};
}
//# sourceMappingURL=mock-request.js.map