@devniel/graphql-query-test-mock
Version:
Mock queries to your GraphQL backend in your Jest tests.
140 lines (117 loc) • 6.05 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getNockRequestHandlerFn = getNockRequestHandlerFn;
var _deepEqual = _interopRequireDefault(require("deep-equal"));
var _getOperationNameFromQuery = require("./getOperationNameFromQuery");
var _handleErrors = require("./handleErrors");
var _index = require("./index");
var _utils = require("./utils");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function getNockRequestHandlerFn(queryMock) {
return function handleNockRequest(uri, data, cb) {
try {
let parsedData = data;
if (typeof parsedData === 'string') {
try {
// In case of multipart form data, the proper data is located the
// multipart annotation, so first extract it.
const rg = /({.*})/gi;
const matches = parsedData.match(rg);
if (!matches) throw new Error();
parsedData = JSON.parse(matches[0]);
} catch (e) {
throw new Error("Invalid data in request. Please make sure you're actually sending the query in your fetch.");
}
}
if (parsedData && typeof parsedData === 'object') {
const query = String(parsedData.query);
const operationName = (0, _getOperationNameFromQuery.getOperationNameFromQuery)(query);
if (operationName) {
const variables = parsedData.variables !== null && typeof parsedData.variables === 'object' ? parsedData.variables : {};
const mockedQueryRecord = queryMock._getQueryMock(operationName, variables);
if (mockedQueryRecord) {
const {
queryMockConfig,
resolveQueryPromise
} = mockedQueryRecord;
const {
status
} = queryMockConfig;
if (status && status >= 400) {
// Bail early if status is a failure
throw queryMockConfig.error || new Error(`Request for operation "${operationName || 'unknown'}" failed with status ${status}. This is intentional and set up in the mock.`);
}
const hasVariablesOrMatchFn = !!(queryMockConfig.variables || queryMockConfig.matchVariables);
const shouldMatchOnVariables = queryMockConfig.matchOnVariables && hasVariablesOrMatchFn;
if (!shouldMatchOnVariables || ( // Bypass if we should not match on variables
queryMockConfig.matchVariables ? queryMockConfig.matchVariables(variables) : (0, _deepEqual.default)((0, _utils.getVariables)(variables, queryMockConfig.ignoreThesePropertiesInVariables || []), (0, _utils.getVariables)(queryMockConfig.variables, queryMockConfig.ignoreThesePropertiesInVariables || [])))) {
/**
* We turn our request handler function into an async one at this point and not earlier,
* because this is the first time we're absolutely sure we will resolve the query and that
* we won't need to throw an error. Throwing inside the async function will make the Promise swallow
* the error, which we do not want.
*/
(async () => {
const serverResponseData = {
data: queryMockConfig.data,
errors: queryMockConfig.graphqlErrors
};
/**
* This is a default function that just returns itself (ie does not change the server response)
* unless provided a custom function.
**/
const changeServerResponseFn = queryMockConfig.changeServerResponse || queryMock._changeServerResponseFn;
const serverResponse = changeServerResponseFn(queryMockConfig, serverResponseData);
let nockReturnVal = [queryMockConfig.status || 200, serverResponse];
const {
customHandler
} = queryMockConfig;
if (customHandler) {
const returnValue = customHandler(this.req, {
query,
operationName,
variables
});
nockReturnVal = returnValue instanceof Promise ? await returnValue : // $FlowFixMe
returnValue;
} // Make sure we add the call to our list
queryMock._addCall({
id: operationName,
variables,
headers: this.req.headers,
// $FlowFixMe
response: nockReturnVal[1]
}); // Wait for resolution control promise to resolve if it exists
if (resolveQueryPromise) {
await resolveQueryPromise;
}
cb(null, nockReturnVal);
})();
} else {
// More useful errors
(0, _handleErrors.printVariablesDoesNotMatchError)(queryMockConfig, shouldMatchOnVariables, operationName, variables);
}
} else {
(0, _handleErrors.printNoMockFoundError)(queryMock, operationName, variables);
}
} else {
throw new Error("Could not find operation name in request. Please make sure you're actually sending the query in your fetch.");
}
} else {
// If parsedData can't be processed then return error to avoid timeouts for the invoker.
throw new Error("Invalid data in request. Please make sure you're actually sending the query in your fetch.");
}
} catch (e) {
//this.req.emit('error', new Error(e.message));
// Returns errors just like graphql (https://graphql.org/learn/serving-over-http/#response)
// and don't throw it because some clients can't catch it (eg. axios)
const serverResponse = {
errors: [JSON.parse(JSON.stringify(e, Object.getOwnPropertyNames(e)))]
};
const nockReturnVal = [500, serverResponse];
return cb(null, nockReturnVal);
}
};
}