@juit/lib-fetch-mock
Version:
Easy Mocking of Node.js' own `fetch`
225 lines (223 loc) • 7.42 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// index.ts
var index_exports = {};
__export(index_exports, {
FetchMock: () => FetchMock,
sendData: () => sendData,
sendJson: () => sendJson,
sendStatus: () => sendStatus,
sendText: () => sendText
});
module.exports = __toCommonJS(index_exports);
var import_node_assert = __toESM(require("node:assert"));
var import_node_http = require("node:http");
var mockSymbol = /* @__PURE__ */ Symbol.for("juit.fetch.mock");
process.on("exit", () => process.exit(process.exitCode));
var FetchMockImpl = class {
_handlers = [];
_fetch;
_baseurl;
constructor(baseurl) {
this._baseurl = new URL(baseurl || "http://test/");
}
/* === FETCH ============================================================== */
async $fetch(info, init) {
(0, import_node_assert.default)(this._fetch, "Global `fetch` not available (mock not enabled?)");
const realFetch = this._fetch;
const fetchWrapper = (info2, init2) => realFetch.call(globalThis, info2, init2);
let response = void 0;
for (const handler of this._handlers) {
const request = info instanceof URL || typeof info === "string" ? new Request(new URL(info, this._baseurl), init) : new Request(info.url, info);
const result = await handler(request, fetchWrapper);
if (result === void 0 || result === null) continue;
response = typeof result === "number" ? sendStatus(result) : result;
break;
}
if (response) return response;
if (this._fetch[mockSymbol]) {
return this._fetch[mockSymbol].$fetch(info, init);
}
return sendStatus(404);
}
/* === HANDLERS =========================================================== */
on(method, path, handler) {
this._handlers.push((request, fetch) => {
if (request.method !== method.toUpperCase()) return;
const pathname = new URL(request.url).pathname;
if (typeof path === "string" && path === pathname || path instanceof RegExp && pathname.match(path)) {
return handler(request, fetch);
}
});
return this;
}
handle(handler) {
this._handlers.push(handler);
return this;
}
reset() {
this._handlers = [];
return this;
}
/* === INTERCEPTOR ======================================================== */
intercept() {
const queue = [];
queue.push(new Deferred());
const next = () => {
return new Promise((resolve, reject) => {
queue[0].promise.then((request) => {
queue.splice(0, 1);
resolve(request);
}, reject);
});
};
this._handlers.push((request, fetch) => {
queue.push(new Deferred());
const deferredResponse = new Deferred();
const deferredRequest = new DeferredRequestImpl(request, deferredResponse, fetch);
queue[queue.length - 2].resolve(deferredRequest);
return deferredResponse.promise;
});
return next;
}
/* === INSTALL / DESTROY ================================================== */
install() {
if (mockSymbol in globalThis.fetch) {
if (globalThis.fetch[mockSymbol] === this) return this;
}
let fn = globalThis.fetch;
while (fn && fn[mockSymbol]) {
if (fn[mockSymbol] === this) {
throw new Error("Global `fetch` already mocked by this instance");
} else {
fn = fn[mockSymbol]._fetch;
}
}
this._fetch = globalThis.fetch;
globalThis.fetch = this.$fetch.bind(this);
Object.defineProperty(globalThis.fetch, mockSymbol, { value: this });
return this;
}
destroy() {
if (!(mockSymbol in globalThis.fetch)) return;
if (!this._fetch) return;
let parent = void 0;
let fn = globalThis.fetch;
while (fn[mockSymbol]) {
if (fn[mockSymbol] === this) {
if (!parent) globalThis.fetch = this._fetch;
else parent._fetch = this._fetch;
break;
} else {
parent = fn[mockSymbol];
fn = fn[mockSymbol]._fetch;
}
}
this._fetch = void 0;
}
};
var FetchMock = FetchMockImpl;
var Deferred = class {
promise;
resolve;
reject;
constructor() {
let resolve;
let reject;
this.promise = new Promise((resolver, rejector) => {
resolve = resolver;
reject = rejector;
});
this.resolve = resolve;
this.reject = reject;
}
};
var DeferredRequestImpl = class extends Request {
constructor(request, _deferred, _fetch) {
super(request);
this._deferred = _deferred;
this._fetch = _fetch;
}
_deferred;
_fetch;
fail(failure) {
this._deferred.reject(failure || new Error(`Error: ${this.url}`));
}
fetch(...args) {
this._deferred.resolve(this._fetch(...args));
}
send(response) {
this._deferred.resolve(response || new Response());
}
sendStatus(status) {
this._deferred.resolve(sendStatus(status));
}
sendText(text, status = 200) {
this._deferred.resolve(sendText(text, status));
}
sendJson(json, status = 200) {
this._deferred.resolve(sendJson(json, status));
}
sendData(data, status = 200) {
this._deferred.resolve(sendData(data, status));
}
};
function sendStatus(status, statusText = import_node_http.STATUS_CODES[status]) {
return new Response(void 0, { status, statusText });
}
function sendText(text, status = 200) {
return new Response(text, {
headers: { "content-type": "text/plain; charset=utf-8" },
statusText: import_node_http.STATUS_CODES[status],
status
});
}
function sendJson(data, status = 200) {
return new Response(JSON.stringify(data), {
headers: { "content-type": "application/json; charset=utf-8" },
statusText: import_node_http.STATUS_CODES[status],
status
});
}
function sendData(data, status = 200) {
return new Response(Buffer.from(data), {
headers: { "content-type": "application/octet-stream" },
statusText: import_node_http.STATUS_CODES[status],
status
});
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
FetchMock,
sendData,
sendJson,
sendStatus,
sendText
});
//# sourceMappingURL=index.cjs.map