@apollo/client
Version:
A fully-featured caching GraphQL client.
194 lines (192 loc) • 7.99 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MockLink = void 0;
exports.realisticDelay = realisticDelay;
exports.stringifyMockedResponse = stringifyMockedResponse;
const equality_1 = require("@wry/equality");
const rxjs_1 = require("rxjs");
const link_1 = require("@apollo/client/link");
const utilities_1 = require("@apollo/client/utilities");
const internal_1 = require("@apollo/client/utilities/internal");
const invariant_1 = require("@apollo/client/utilities/invariant");
function realisticDelay({ min = 20, max = 50, } = {}) {
(0, invariant_1.invariant)(max > min, 21);
return () => Math.floor(Math.random() * (max - min) + min);
}
class MockLink extends link_1.ApolloLink {
operation;
showWarnings = true;
defaultDelay;
mockedResponsesByKey = {};
static defaultOptions = {
delay: realisticDelay(),
};
constructor(mockedResponses, options = {}) {
super();
const defaultOptions = options.defaultOptions ?? MockLink.defaultOptions;
this.showWarnings = options.showWarnings ?? true;
this.defaultDelay = defaultOptions?.delay ?? realisticDelay();
if (mockedResponses) {
mockedResponses.forEach((mockedResponse) => {
this.addMockedResponse(mockedResponse);
});
}
}
addMockedResponse(mockedResponse) {
validateMockedResponse(mockedResponse);
const normalized = this.normalizeMockedResponse(mockedResponse);
this.getMockedResponses(normalized.request).push(normalized);
}
request(operation) {
this.operation = operation;
const unmatchedVars = [];
const mocks = this.getMockedResponses(operation);
const index = mocks.findIndex((mock) => {
const { variables } = mock.request;
if (typeof variables === "function") {
const matched = variables(operation.variables);
if (!matched) {
unmatchedVars.push(`<function ${variables.name}>`);
}
return matched;
}
const withDefaults = mock.variablesWithDefaults;
if ((0, equality_1.equal)(withDefaults, operation.variables)) {
return true;
}
unmatchedVars.push(
// Include default variables from the query in unmatched variables
// output
Object.keys(withDefaults).length > 0 ?
withDefaults
: variables || "<undefined>");
return false;
});
const matched = index >= 0 ? mocks[index] : void 0;
if (!matched) {
const message = getErrorMessage(operation, unmatchedVars);
if (this.showWarnings) {
console.warn(message +
"\nThis typically indicates a configuration error in your mocks " +
"setup, usually due to a typo or mismatched variable.");
}
return (0, rxjs_1.throwError)(() => new Error(message)).pipe((0, rxjs_1.observeOn)(rxjs_1.asapScheduler));
}
if (matched.maxUsageCount > 1) {
matched.maxUsageCount--;
}
else {
mocks.splice(index, 1);
}
const delay = typeof matched.delay === "function" ?
matched.delay(operation)
: matched.delay;
if (!matched.result && !matched.error && delay !== Infinity) {
return (0, rxjs_1.throwError)(() => new Error(`Mocked response should contain either \`result\`, \`error\` or a \`delay\` of \`Infinity\`:\n${stringifyMockedResponse(matched.original)}`));
}
if (matched.delay === Infinity) {
return new rxjs_1.Observable();
}
return new rxjs_1.Observable((observer) => {
const timer = setTimeout(() => {
if (matched.error) {
return observer.error(matched.error);
}
if (matched.result) {
observer.next(typeof matched.result === "function" ?
matched.result(operation.variables)
: matched.result);
}
observer.complete();
}, delay);
return () => {
clearTimeout(timer);
};
});
}
getMockedResponses(request) {
const key = JSON.stringify({
query: (0, utilities_1.print)((0, utilities_1.addTypenameToDocument)(request.query)),
});
let mockedResponses = this.mockedResponsesByKey[key];
if (!mockedResponses) {
mockedResponses = this.mockedResponsesByKey[key] = [];
}
return mockedResponses;
}
normalizeMockedResponse(mockedResponse) {
const { request } = mockedResponse;
const response = (0, internal_1.cloneDeep)(mockedResponse);
response.original = mockedResponse;
response.request.query = getServerQuery(request.query);
response.maxUsageCount ??= 1;
response.variablesWithDefaults = {
...(0, internal_1.getDefaultValues)((0, internal_1.getOperationDefinition)(request.query)),
...request.variables,
};
response.delay ??= this.defaultDelay;
return response;
}
}
exports.MockLink = MockLink;
function getErrorMessage(operation, unmatchedVars) {
return `No more mocked responses for the query:
${(0, utilities_1.print)(operation.query)}
Request variables: ${stringifyForDebugging(operation.variables)}
${unmatchedVars.length > 0 ?
`
Failed to match variables against ${unmatchedVars.length} mock${unmatchedVars.length === 1 ? "" : "s"} for this query. The available mocks had the following variables:
${unmatchedVars.map((d) => ` ${stringifyForDebugging(d)}`).join("\n")}
`
: ""}`;
}
function getServerQuery(query) {
const queryWithoutClientOnlyDirectives = (0, internal_1.removeDirectivesFromDocument)([{ name: "connection" }, { name: "nonreactive" }, { name: "unmask" }], query);
(0, invariant_1.invariant)(queryWithoutClientOnlyDirectives, 22);
const serverQuery = (0, internal_1.removeDirectivesFromDocument)([{ name: "client", remove: true }], queryWithoutClientOnlyDirectives);
(0, invariant_1.invariant)(serverQuery, 23);
return serverQuery;
}
function validateMockedResponse(mock) {
(0, internal_1.checkDocument)(mock.request.query);
(0, invariant_1.invariant)((mock.maxUsageCount ?? 1) > 0, 24, mock.maxUsageCount);
}
/**
* @internal
*
* @deprecated This is an internal API and should not be used directly. This can be removed or changed at any time.
*/
function stringifyMockedResponse(mockedResponse) {
return JSON.stringify(mockedResponse, (_, value) => {
if ((0, internal_1.isDocumentNode)(value)) {
return (0, utilities_1.print)(value);
}
if (typeof value === "function") {
return "<function>";
}
return value;
}, 2);
}
// This is similar to the stringifyForDisplay utility we ship, but includes
// support for NaN in addition to undefined. More values may be handled in the
// future. This is not added to the primary stringifyForDisplay helper since it
// is used for the cache and other purposes. We need this for debugging only.
function stringifyForDebugging(value, space = 0) {
if (typeof value === "string") {
return value;
}
const undefId = (0, internal_1.makeUniqueId)("undefined");
const nanId = (0, internal_1.makeUniqueId)("NaN");
return JSON.stringify(value, (_, value) => {
if (value === void 0) {
return undefId;
}
if (Number.isNaN(value)) {
return nanId;
}
return value;
}, space)
.replace(new RegExp(JSON.stringify(undefId), "g"), "<undefined>")
.replace(new RegExp(JSON.stringify(nanId), "g"), "NaN");
}
//# sourceMappingURL=mockLink.cjs.map