UNPKG

relay-test-utils

Version:

Utilities for testing Relay applications.

506 lines (505 loc) • 25.6 kB
'use strict'; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault")["default"]; var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _objectSpread3 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2")); var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); var _excluded = ["generateDeferredPayload"]; var invariant = require('invariant'); var _require = require('relay-runtime'), __internal = _require.__internal, RelayConcreteNode = _require.RelayConcreteNode, TYPENAME_KEY = _require.TYPENAME_KEY, getModuleComponentKey = _require.getModuleComponentKey, getModuleOperationKey = _require.getModuleOperationKey; var ACTOR_CHANGE = RelayConcreteNode.ACTOR_CHANGE, CLIENT_COMPONENT = RelayConcreteNode.CLIENT_COMPONENT, CLIENT_EDGE_TO_CLIENT_OBJECT = RelayConcreteNode.CLIENT_EDGE_TO_CLIENT_OBJECT, CLIENT_EXTENSION = RelayConcreteNode.CLIENT_EXTENSION, CONDITION = RelayConcreteNode.CONDITION, CONNECTION = RelayConcreteNode.CONNECTION, DEFER = RelayConcreteNode.DEFER, FRAGMENT_SPREAD = RelayConcreteNode.FRAGMENT_SPREAD, INLINE_FRAGMENT = RelayConcreteNode.INLINE_FRAGMENT, LINKED_FIELD = RelayConcreteNode.LINKED_FIELD, LINKED_HANDLE = RelayConcreteNode.LINKED_HANDLE, MODULE_IMPORT = RelayConcreteNode.MODULE_IMPORT, RELAY_RESOLVER = RelayConcreteNode.RELAY_RESOLVER, RELAY_LIVE_RESOLVER = RelayConcreteNode.RELAY_LIVE_RESOLVER, SCALAR_FIELD = RelayConcreteNode.SCALAR_FIELD, SCALAR_HANDLE = RelayConcreteNode.SCALAR_HANDLE, STREAM = RelayConcreteNode.STREAM, TYPE_DISCRIMINATOR = RelayConcreteNode.TYPE_DISCRIMINATOR; function createIdGenerator() { var id = 0; return function () { return ++id; }; } var DEFAULT_MOCK_RESOLVERS = { ID: function ID(context, generateId) { return "<".concat(context.parentType != null && context.parentType !== DEFAULT_MOCK_TYPENAME ? context.parentType + '-' : '', "mock-id-").concat(generateId(), ">"); }, Boolean: function Boolean() { return false; }, Int: function Int() { return 42; }, Float: function Float() { return 4.2; } }; var DEFAULT_MOCK_TYPENAME = '__MockObject'; function valueResolver(generateId, mockResolvers, typeName, context) { var plural = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; var defaultValue = arguments.length > 5 ? arguments[5] : undefined; var generateValue = function generateValue(possibleDefaultValue) { var mockValue; var mockResolver = typeName != null && mockResolvers != null ? mockResolvers[typeName] : null; if (mockResolver != null) { mockValue = mockResolver(context, generateId); } if (mockValue === undefined) { var _ref, _context$alias; mockValue = possibleDefaultValue !== null && possibleDefaultValue !== void 0 ? possibleDefaultValue : typeName === 'ID' ? DEFAULT_MOCK_RESOLVERS.ID(context, generateId) : "<mock-value-for-field-\"".concat((_ref = (_context$alias = context.alias) !== null && _context$alias !== void 0 ? _context$alias : context.name) !== null && _ref !== void 0 ? _ref : 'undefined', "\">"); } return mockValue; }; return plural === true ? generateMockList(Array.isArray(defaultValue) ? defaultValue : Array(1).fill(), generateValue) : generateValue(defaultValue); } function createValueResolver(mockResolvers) { var generateId = createIdGenerator(); return function () { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return valueResolver.apply(void 0, [generateId, mockResolvers].concat(args)); }; } function generateMockList(placeholderArray, generateListItem) { return placeholderArray.map(function (possibleDefaultValue, index) { return generateListItem(possibleDefaultValue, index); }); } var RelayMockPayloadGenerator = /*#__PURE__*/function () { function RelayMockPayloadGenerator(options) { var _options$mockResolver, _options$selectionMet, _options$mockClientDa, _options$generateDefe; this._variables = options.variables; this._mockResolvers = (0, _objectSpread3["default"])((0, _objectSpread3["default"])({}, DEFAULT_MOCK_RESOLVERS), (_options$mockResolver = options.mockResolvers) !== null && _options$mockResolver !== void 0 ? _options$mockResolver : {}); this._selectionMetadata = (_options$selectionMet = options.selectionMetadata) !== null && _options$selectionMet !== void 0 ? _options$selectionMet : {}; this._resolveValue = createValueResolver(this._mockResolvers); this._mockClientData = (_options$mockClientDa = options.mockClientData) !== null && _options$mockClientDa !== void 0 ? _options$mockClientDa : false; this._generateDeferredPayload = (_options$generateDefe = options.generateDeferredPayload) !== null && _options$generateDefe !== void 0 ? _options$generateDefe : false; this._deferredPayloads = []; } var _proto = RelayMockPayloadGenerator.prototype; _proto.generate = function generate(selections, operationType) { var defaultValues = this._getDefaultValuesForObject(operationType, null, null, [], {}); var data = this._traverse({ selections: selections, typeName: operationType, isAbstractType: false, name: null, alias: null, args: null }, [], null, defaultValues); return [{ data: data }].concat((0, _toConsumableArray2["default"])(this._deferredPayloads)); }; _proto._traverse = function _traverse(traversable, path, prevData, defaultValues) { var selections = traversable.selections, typeName = traversable.typeName, isAbstractType = traversable.isAbstractType; return this._traverseSelections(selections, typeName, isAbstractType, path, prevData, defaultValues); }; _proto._traverseSelections = function _traverseSelections(selections, typeName, isAbstractType, path, prevData, defaultValues) { var _this = this; var mockData = prevData !== null && prevData !== void 0 ? prevData : {}; selections.forEach(function (selection) { switch (selection.kind) { case SCALAR_FIELD: { mockData = _this._mockScalar(selection, typeName, path, mockData, defaultValues); break; } case CONNECTION: { mockData = _this._traverseSelections([selection.edges, selection.pageInfo], typeName, isAbstractType, path, prevData, defaultValues); break; } case LINKED_FIELD: { mockData = _this._mockLink(selection, path, mockData, defaultValues); break; } case CONDITION: var conditionValue = _this._getVariableValue(selection.condition); if (conditionValue === selection.passingValue) { mockData = _this._traverseSelections(selection.selections, typeName, isAbstractType, path, mockData, defaultValues); } break; case CLIENT_EXTENSION: if (!_this._mockClientData) { break; } mockData = _this._traverseSelections(selection.selections, typeName, isAbstractType, path, mockData, defaultValues); break; case DEFER: case STREAM: { var isDeferreable = selection["if"] == null || _this._variables[selection["if"]]; if (_this._generateDeferredPayload && isDeferreable) { var deferredData = _this._traverseSelections(selection.selections, typeName, isAbstractType, path, {}, defaultValues); _this._deferredPayloads.push({ path: (0, _toConsumableArray2["default"])(path), label: selection.label, data: deferredData }); break; } mockData = _this._traverseSelections(selection.selections, typeName, isAbstractType, path, mockData, defaultValues); break; } case CLIENT_COMPONENT: { mockData = _this._traverseSelections(selection.fragment.selections, typeName, isAbstractType, path, mockData, defaultValues); break; } case FRAGMENT_SPREAD: { var prevVariables = _this._variables; _this._variables = __internal.getLocalVariables(_this._variables, selection.fragment.argumentDefinitions, selection.args); mockData = _this._traverseSelections(selection.fragment.selections, typeName, isAbstractType, path, mockData, defaultValues); _this._variables = prevVariables; break; } case INLINE_FRAGMENT: { var _abstractKey = selection.abstractKey; if (_abstractKey != null) { var shouldMockFragment = (defaultValues === null || defaultValues === void 0 ? void 0 : defaultValues[_abstractKey]) === undefined || !!(defaultValues !== null && defaultValues !== void 0 && defaultValues[_abstractKey]); if (!shouldMockFragment) { break; } if (mockData != null) { mockData[_abstractKey] = true; } mockData = _this._traverseSelections(selection.selections, typeName, isAbstractType, path, mockData, defaultValues); break; } if (mockData != null && (mockData[TYPENAME_KEY] == null || mockData[TYPENAME_KEY] === DEFAULT_MOCK_TYPENAME)) { var _defaultValues$TYPENA; mockData[TYPENAME_KEY] = (_defaultValues$TYPENA = defaultValues === null || defaultValues === void 0 ? void 0 : defaultValues[TYPENAME_KEY]) !== null && _defaultValues$TYPENA !== void 0 ? _defaultValues$TYPENA : selection.type; } if (isAbstractType === true && mockData != null && mockData[TYPENAME_KEY] === typeName) { mockData[TYPENAME_KEY] = selection.type; } if (mockData != null && mockData[TYPENAME_KEY] === selection.type) { var defaults = _this._getDefaultValuesForObject(selection.type, path[path.length - 1], null, path); var defaultsForAbstractType = typeName !== selection.type ? _this._getDefaultValuesForObject(typeName, path[path.length - 1], null, path) : defaults; var defaultValuesForSelection = defaults; if (defaultValuesForSelection === undefined) { defaultValuesForSelection = defaultsForAbstractType; } if (defaultValuesForSelection === undefined) { defaultValuesForSelection = defaultValues; } if (defaultValuesForSelection === null) { mockData = null; break; } mockData = _this._traverseSelections(selection.selections, selection.type, isAbstractType, path, mockData, defaultValuesForSelection); if (mockData[TYPENAME_KEY] != null) { mockData[TYPENAME_KEY] = selection.type; } if (mockData.id != null && defaults != null && defaults.id != null) { mockData.id = defaults.id; } } break; } case MODULE_IMPORT: if (defaultValues != null) { var _objectSpread2; if (defaultValues.__typename !== typeName) { break; } var operation = defaultValues.__module_operation; !(typeof operation === 'object' && operation !== null && operation.kind === 'SplitOperation' && Array.isArray(operation.selections) && typeof operation.name === 'string') ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayMockPayloadGenerator(): Unexpected default value for ' + 'a field `__module_operation` in the mock resolver for ' + '@module dependency. Provided value is "%s" and we\'re ' + 'expecting an object of a type `NormalizationSplitOperation`. ' + 'Please adjust mock resolver for the type "%s". ' + 'Typically it should require a file "%s$normalization.graphql".', JSON.stringify(operation), typeName, selection.fragmentName) : invariant(false) : void 0; var splitOperation = operation; var documentName = selection.documentName; if (mockData == null) { mockData = {}; } mockData = (0, _objectSpread3["default"])((0, _objectSpread3["default"])({}, mockData), {}, (_objectSpread2 = {}, (0, _defineProperty2["default"])(_objectSpread2, TYPENAME_KEY, typeName), (0, _defineProperty2["default"])(_objectSpread2, getModuleOperationKey(documentName), operation.name), (0, _defineProperty2["default"])(_objectSpread2, getModuleComponentKey(documentName), defaultValues.__module_component), _objectSpread2), _this._traverseSelections(splitOperation.selections, typeName, false, path, null, defaultValues)); } break; case TYPE_DISCRIMINATOR: var abstractKey = selection.abstractKey; if (mockData != null) { mockData[abstractKey] = true; } break; case SCALAR_HANDLE: case LINKED_HANDLE: break; case ACTOR_CHANGE: throw new Error('ActorChange fields are not yet supported.'); case RELAY_LIVE_RESOLVER: case RELAY_RESOLVER: if (selection.fragment) { mockData = _this._traverseSelections(selection.fragment.selections, typeName, isAbstractType, path, mockData, defaultValues); } break; case CLIENT_EDGE_TO_CLIENT_OBJECT: mockData = _this._traverseSelections([selection.backingField], typeName, isAbstractType, path, mockData, defaultValues); break; default: selection; !false ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayMockPayloadGenerator(): Unexpected AST kind `%s`.', selection.kind) : invariant(false) : void 0; } }); return mockData; }; _proto._getCorrectDefaultEnum = function _getCorrectDefaultEnum(enumValues, value, path, applicationName) { if (value === undefined) { return value; } if (value === null) { return value; } var valueToValidate = Array.isArray(value) ? value.map(function (v) { return String(v).toUpperCase(); }) : [String(value).toUpperCase()]; var enumValuesNormalized = enumValues.map(function (s) { return s.toUpperCase(); }); var correctValues = valueToValidate.filter(function (v) { return enumValuesNormalized.includes(v); }); if (correctValues.length !== valueToValidate.length) { !false ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayMockPayloadGenerator: Invalid value "%s" provided for enum ' + 'field "%s" via MockResolver.' + 'Expected one of the following values: %s.', value, "".concat(path.join('.'), ".").concat(applicationName), enumValues.map(function (v) { return "\"".concat(v, "\""); }).join(', ')) : invariant(false) : void 0; } var correctSpellingValues = valueToValidate.map(function (v) { var correctSpellingEnumIndex = enumValuesNormalized.indexOf(String(v).toUpperCase()); return enumValues[correctSpellingEnumIndex]; }); return Array.isArray(value) ? correctSpellingValues : correctSpellingValues[0]; }; _proto._mockScalar = function _mockScalar(field, typeName, path, mockData, defaultValues) { var _field$alias; var data = mockData !== null && mockData !== void 0 ? mockData : {}; var applicationName = (_field$alias = field.alias) !== null && _field$alias !== void 0 ? _field$alias : field.name; if (data.hasOwnProperty(applicationName) && field.name !== TYPENAME_KEY) { return data; } var value; if (field.name === TYPENAME_KEY) { value = typeName !== null && typeName !== void 0 ? typeName : DEFAULT_MOCK_TYPENAME; } var selectionPath = [].concat((0, _toConsumableArray2["default"])(path), [applicationName]); var _this$_getScalarField = this._getScalarFieldTypeDetails(field, typeName, selectionPath), type = _this$_getScalarField.type, plural = _this$_getScalarField.plural, enumValues = _this$_getScalarField.enumValues; if (defaultValues != null && defaultValues.hasOwnProperty(applicationName)) { value = defaultValues[applicationName]; if (enumValues != null) { value = this._getCorrectDefaultEnum(enumValues, value, path, applicationName); } if (value !== undefined && plural && !Array.isArray(value)) { value = [value]; } } if (value === undefined) { var defaultValue = enumValues != null ? enumValues[0] : undefined; value = this._resolveValue(type, { parentType: typeName, name: field.name, alias: field.alias, path: selectionPath, args: this._getFieldArgs(field) }, plural, defaultValue); } data[applicationName] = value; return data; }; _proto._mockLink = function _mockLink(field, path, prevData, defaultValues) { var _this2 = this; var _field$alias2, _this$_getTypeDetails, _field$concreteType; var applicationName = (_field$alias2 = field.alias) !== null && _field$alias2 !== void 0 ? _field$alias2 : field.name; var data = prevData !== null && prevData !== void 0 ? prevData : {}; var args = this._getFieldArgs(field); var selectionPath = [].concat((0, _toConsumableArray2["default"])(path), [applicationName]); var typeFromSelection = (_this$_getTypeDetails = this._getTypeDetailsForPath(selectionPath)) !== null && _this$_getTypeDetails !== void 0 ? _this$_getTypeDetails : { type: DEFAULT_MOCK_TYPENAME }; var defaults; if (defaultValues != null && typeof defaultValues[applicationName] === 'object') { defaults = defaultValues[applicationName]; } if (defaults === null) { data[applicationName] = null; return data; } var typeName = (_field$concreteType = field.concreteType) !== null && _field$concreteType !== void 0 ? _field$concreteType : defaults != null && typeof defaults[TYPENAME_KEY] === 'string' ? defaults[TYPENAME_KEY] : typeFromSelection.type; var isAbstractType = field.concreteType == null && typeName === typeFromSelection.type; var generateDataForField = function generateDataForField(possibleDefaultValue, index) { var _index$toString, _this$_getDefaultValu, _field$concreteType2; var fieldPath = field.plural ? [].concat((0, _toConsumableArray2["default"])(selectionPath), [(_index$toString = index === null || index === void 0 ? void 0 : index.toString(10)) !== null && _index$toString !== void 0 ? _index$toString : '0']) : selectionPath; var fieldDefaultValue = (_this$_getDefaultValu = _this2._getDefaultValuesForObject((_field$concreteType2 = field.concreteType) !== null && _field$concreteType2 !== void 0 ? _field$concreteType2 : typeFromSelection.type, field.name, field.alias, fieldPath, args)) !== null && _this$_getDefaultValu !== void 0 ? _this$_getDefaultValu : possibleDefaultValue; if (fieldDefaultValue === null) { return null; } return _this2._traverse({ selections: field.selections, typeName: typeName, isAbstractType: isAbstractType, name: field.name, alias: field.alias, args: args }, fieldPath, typeof data[applicationName] === 'object' ? data[applicationName] : null, fieldDefaultValue); }; data[applicationName] = field.kind === 'LinkedField' && field.plural ? generateMockList(Array.isArray(defaults) ? defaults : Array(1).fill(), generateDataForField) : generateDataForField(defaults); return data; }; _proto._getVariableValue = function _getVariableValue(name) { !this._variables.hasOwnProperty(name) ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayMockPayloadGenerator(): Undefined variable `%s`.', name) : invariant(false) : void 0; return this._variables[name]; }; _proto._getDefaultValuesForObject = function _getDefaultValuesForObject(typeName, fieldName, fieldAlias, path, args) { var data; if (typeName != null && this._mockResolvers[typeName] != null) { data = this._resolveValue(typeName, { parentType: null, name: fieldName, alias: fieldAlias, args: args, path: path }, false); } if (typeof data === 'object') { return data; } }; _proto._getFieldArgs = function _getFieldArgs(field) { var _this3 = this; var args = {}; if (field.args != null) { field.args.forEach(function (arg) { args[arg.name] = _this3._getArgValue(arg); }); } return args; }; _proto._getArgValue = function _getArgValue(arg) { var _this4 = this; switch (arg.kind) { case 'Literal': return arg.value; case 'Variable': return this._getVariableValue(arg.variableName); case 'ObjectValue': { var value = {}; arg.fields.forEach(function (field) { value[field.name] = _this4._getArgValue(field); }); return value; } case 'ListValue': { var _value = []; arg.items.forEach(function (item) { _value.push(item != null ? _this4._getArgValue(item) : null); }); return _value; } } }; _proto._getScalarFieldTypeDetails = function _getScalarFieldTypeDetails(field, typeName, selectionPath) { var _this$_getTypeDetails2; return (_this$_getTypeDetails2 = this._getTypeDetailsForPath(selectionPath)) !== null && _this$_getTypeDetails2 !== void 0 ? _this$_getTypeDetails2 : { type: field.name === 'id' ? 'ID' : 'String', plural: false, enumValues: null, nullable: false }; }; _proto._getTypeDetailsForPath = function _getTypeDetailsForPath(path) { return this._selectionMetadata[path.filter(function (field) { return isNaN(parseInt(field, 10)); }).join('.')]; }; return RelayMockPayloadGenerator; }(); function generateData(node, variables, mockResolvers, selectionMetadata, options) { var mockGenerator = new RelayMockPayloadGenerator({ variables: variables, mockResolvers: mockResolvers, selectionMetadata: selectionMetadata, mockClientData: options === null || options === void 0 ? void 0 : options.mockClientData, generateDeferredPayload: options === null || options === void 0 ? void 0 : options.generateDeferredPayload }); var operationType; if (node.name.endsWith('Mutation')) { operationType = 'Mutation'; } else if (node.name.endsWith('Subscription')) { operationType = 'Subscription'; } else { operationType = 'Query'; } return mockGenerator.generate(node.selections, operationType); } function getSelectionMetadataFromOperation(operation) { var _operation$request$no; var selectionTypeInfo = (_operation$request$no = operation.request.node.params.metadata) === null || _operation$request$no === void 0 ? void 0 : _operation$request$no.relayTestingSelectionTypeInfo; if (selectionTypeInfo != null && !Array.isArray(selectionTypeInfo) && typeof selectionTypeInfo === 'object') { var selectionMetadata = {}; Object.keys(selectionTypeInfo).forEach(function (path) { var item = selectionTypeInfo[path]; if (item != null && !Array.isArray(item) && typeof item === 'object') { if (typeof item.type === 'string' && typeof item.plural === 'boolean' && typeof item.nullable === 'boolean' && (item.enumValues === null || Array.isArray(item.enumValues))) { selectionMetadata[path] = { type: item.type, plural: item.plural, nullable: item.nullable, enumValues: Array.isArray(item.enumValues) ? item.enumValues.map(String) : null }; } } }); return selectionMetadata; } return null; } function generateDataForOperation(operation, mockResolvers, options) { var concreteOperation = operation.request.node.operation; var _generateData = generateData(concreteOperation, operation.request.variables, mockResolvers !== null && mockResolvers !== void 0 ? mockResolvers : null, getSelectionMetadataFromOperation(operation), (0, _objectSpread3["default"])((0, _objectSpread3["default"])({}, options), {}, { generateDeferredPayload: false })), initialPayload = _generateData[0]; return initialPayload; } function generateWithDefer(operation, mockResolvers, options) { var _ref2 = options !== null && options !== void 0 ? options : {}, _ref2$generateDeferre = _ref2.generateDeferredPayload, generateDeferredPayload = _ref2$generateDeferre === void 0 ? false : _ref2$generateDeferre, otherOptions = (0, _objectWithoutPropertiesLoose2["default"])(_ref2, _excluded); var concreteOperation = operation.request.node.operation; var payloads = generateData(concreteOperation, operation.request.variables, mockResolvers !== null && mockResolvers !== void 0 ? mockResolvers : null, getSelectionMetadataFromOperation(operation), (0, _objectSpread3["default"])((0, _objectSpread3["default"])({}, otherOptions), {}, { generateDeferredPayload: generateDeferredPayload })); if (!generateDeferredPayload) { return payloads[0]; } return payloads; } module.exports = { generate: generateDataForOperation, generateWithDefer: generateWithDefer };