msw-snapshot
Version:
Transparently create an API cache for testing.
254 lines • 12.5 kB
JavaScript
;
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.toHashString = exports.getSortedEntries = exports.getEntries = exports.snapshot = exports.context = exports.DEFAULT_NAMESPACE = void 0;
var node_util_1 = require("node:util");
var msw_1 = require("msw");
var node_path_1 = require("node:path");
var node_fs_1 = require("node:fs");
var node_crypto_1 = require("node:crypto");
var pureFetch = globalThis.fetch;
__exportStar(require("./mask.js"), exports);
/**
* Default namespace.
*/
exports.DEFAULT_NAMESPACE = 'default';
/**
* Context of snapshotting.
* You can use it for separating snapshot files with modifying context.namespace.
*/
exports.context = {
namespace: exports.DEFAULT_NAMESPACE,
};
/**
* Create snapshot RequestHandler.
*/
var snapshot = function (config) {
var _a;
return msw_1.http.all((_a = config.test) !== null && _a !== void 0 ? _a : /.*/, function (mswInfo) { return __awaiter(void 0, void 0, void 0, function () {
var clonedInfo, snapshotPath, _a, _b, snapshot_1, filteredHeaders_1, response, snapshot, _c, _d, _e, _f, shouldUpdateSnapshots, filteredHeaders;
var _g, _h, _j;
var _k, _l, _m;
return __generator(this, function (_o) {
switch (_o.label) {
case 0:
clonedInfo = function () {
return {
request: mswInfo.request.clone(),
cookies: __assign({}, mswInfo.cookies),
context: exports.context,
};
};
_a = node_path_1.join;
_b = [config.basePath];
return [4 /*yield*/, createSnapshotPath(clonedInfo(), config)];
case 1:
snapshotPath = _a.apply(void 0, _b.concat([_o.sent()]));
// Fetch from snapshot
if (!config.ignoreSnapshots && (0, node_fs_1.existsSync)(snapshotPath)) {
try {
snapshot_1 = JSON.parse((0, node_fs_1.readFileSync)(snapshotPath).toString('utf8'));
(_k = config.onFetchFromSnapshot) === null || _k === void 0 ? void 0 : _k.call(config, clonedInfo(), snapshot_1);
filteredHeaders_1 = snapshot_1.response.headers.filter(function (_a) {
var key = _a[0], _ = _a[1];
return !key.toLowerCase().includes('content-encoding') &&
!key.toLowerCase().includes('transfer-encoding');
});
return [2 /*return*/, new Response(new TextEncoder().encode(snapshot_1.response.body), {
headers: new Headers(filteredHeaders_1),
status: snapshot_1.response.status,
statusText: snapshot_1.response.statusText,
})];
}
catch (e) {
console.error("Can't parse snapshot file: ".concat(snapshotPath), e);
}
}
return [4 /*yield*/, pureFetch(mswInfo.request.clone())];
case 2:
response = _o.sent();
_g = {};
_h = {
method: mswInfo.request.method,
url: mswInfo.request.url
};
_d = (_c = new node_util_1.TextDecoder('utf-8')).decode;
return [4 /*yield*/, mswInfo.request.clone().arrayBuffer()];
case 3:
_g.request = (_h.body = _d.apply(_c, [_o.sent()]),
_h.headers = getSortedEntries(mswInfo.request.headers),
_h.cookies = getSortedEntries(mswInfo.cookies),
_h);
_j = {
status: response.status,
statusText: response.statusText
};
_f = (_e = new node_util_1.TextDecoder('utf-8')).decode;
return [4 /*yield*/, response.arrayBuffer()];
case 4:
snapshot = (_g.response = (_j.body = _f.apply(_e, [_o.sent()]),
_j.headers = getSortedEntries(response.headers),
_j),
_g);
(_l = config.onFetchFromServer) === null || _l === void 0 ? void 0 : _l.call(config, clonedInfo(), snapshot);
shouldUpdateSnapshots = false;
shouldUpdateSnapshots = shouldUpdateSnapshots || config.updateSnapshots === true;
shouldUpdateSnapshots = shouldUpdateSnapshots || config.updateSnapshots === 'all';
shouldUpdateSnapshots = shouldUpdateSnapshots || config.updateSnapshots === 'missing' && !(0, node_fs_1.existsSync)(snapshotPath);
if (shouldUpdateSnapshots) {
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(snapshotPath), { recursive: true });
(0, node_fs_1.writeFileSync)(snapshotPath, JSON.stringify(snapshot, undefined, 2));
(_m = config.onSnapshotUpdated) === null || _m === void 0 ? void 0 : _m.call(config, clonedInfo(), snapshot);
}
filteredHeaders = snapshot.response.headers.filter(function (_a) {
var key = _a[0], _ = _a[1];
return !key.toLowerCase().includes('content-encoding') &&
!key.toLowerCase().includes('transfer-encoding');
});
return [2 /*return*/, new Response(new TextEncoder().encode(snapshot.response.body), {
headers: new Headers(filteredHeaders),
status: snapshot.response.status,
statusText: snapshot.response.statusText,
})];
}
});
}); });
};
exports.snapshot = snapshot;
/**
* Get sorted array of [key, val] tuple from ***#entries.
*/
function getEntries(iter) {
if (iter instanceof Headers) {
var entries_1 = [];
iter.forEach(function (v, k) { return entries_1.push([k, v]); });
return entries_1;
}
else if (iter instanceof FormData) {
var entries_2 = [];
iter.forEach(function (v, k) { return entries_2.push([k, v.toString()]); });
return entries_2;
}
else if (iter instanceof URLSearchParams) {
return Array.from(iter.entries());
}
return Object.entries(iter).map(function (_a) {
var k = _a[0], v = _a[1];
return [k, Array.isArray(v) ? v.join(',') : v];
});
}
exports.getEntries = getEntries;
/**
* Get sorted array of [key, val] tuple from ***#entries.
*/
function getSortedEntries(iter) {
return getEntries(iter).sort(function (_a, _b) {
var a = _a[0];
var b = _b[0];
return a.localeCompare(b);
});
}
exports.getSortedEntries = getSortedEntries;
;
/**
* Create hash string from object.
*/
function toHashString(object) {
return (0, node_crypto_1.createHash)('md5').update(JSON.stringify(object), 'binary').digest('hex');
}
exports.toHashString = toHashString;
/**
* Create snapshot name from request.
*/
function createSnapshotPath(info, config) {
return __awaiter(this, void 0, void 0, function () {
var url, _a, _b, _c, _d, _e;
return __generator(this, function (_f) {
switch (_f.label) {
case 0:
if (!config.createSnapshotPath) return [3 /*break*/, 2];
return [4 /*yield*/, config.createSnapshotPath(info)];
case 1: return [2 /*return*/, _f.sent()];
case 2:
url = new URL(info.request.url);
_a = [
exports.context.namespace,
info.request.method,
url.origin,
url.pathname,
].join('/') + '/';
_b = toHashString;
_c = [getSortedEntries(url.searchParams),
getSortedEntries(info.request.headers),
getSortedEntries(info.cookies)];
_e = (_d = new node_util_1.TextDecoder('utf-8')).decode;
return [4 /*yield*/, info.request.arrayBuffer()];
case 3: return [2 /*return*/, _a + _b.apply(void 0, [_c.concat([
_e.apply(_d, [_f.sent()])
])])];
}
});
});
}
;
//# sourceMappingURL=index.js.map