UNPKG

15.2 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3var graphql_1 = require("graphql");
4var graphql_2 = require("graphql");
5var uuid = require("uuid");
6var schemaGenerator_1 = require("./schemaGenerator");
7// This function wraps addMockFunctionsToSchema for more convenience
8function mockServer(schema, mocks, preserveResolvers) {
9 if (preserveResolvers === void 0) { preserveResolvers = false; }
10 var mySchema;
11 if (!(schema instanceof graphql_1.GraphQLSchema)) {
12 // TODO: provide useful error messages here if this fails
13 mySchema = schemaGenerator_1.buildSchemaFromTypeDefinitions(schema);
14 }
15 else {
16 mySchema = schema;
17 }
18 addMockFunctionsToSchema({ schema: mySchema, mocks: mocks, preserveResolvers: preserveResolvers });
19 return { query: function (query, vars) { return graphql_2.graphql(mySchema, query, {}, {}, vars); } };
20}
21exports.mockServer = mockServer;
22// TODO allow providing a seed such that lengths of list could be deterministic
23// this could be done by using casual to get a random list length if the casual
24// object is global.
25function addMockFunctionsToSchema(_a) {
26 var schema = _a.schema, _b = _a.mocks, mocks = _b === void 0 ? {} : _b, _c = _a.preserveResolvers, preserveResolvers = _c === void 0 ? false : _c;
27 function isObject(thing) {
28 return thing === Object(thing) && !Array.isArray(thing);
29 }
30 if (!schema) {
31 throw new Error('Must provide schema to mock');
32 }
33 if (!(schema instanceof graphql_1.GraphQLSchema)) {
34 throw new Error('Value at "schema" must be of type GraphQLSchema');
35 }
36 if (!isObject(mocks)) {
37 throw new Error('mocks must be of type Object');
38 }
39 // use Map internally, because that API is nicer.
40 var mockFunctionMap = new Map();
41 Object.keys(mocks).forEach(function (typeName) {
42 mockFunctionMap.set(typeName, mocks[typeName]);
43 });
44 mockFunctionMap.forEach(function (mockFunction, mockTypeName) {
45 if (typeof mockFunction !== 'function') {
46 throw new Error("mockFunctionMap[" + mockTypeName + "] must be a function");
47 }
48 });
49 var defaultMockMap = new Map();
50 defaultMockMap.set('Int', function () { return Math.round(Math.random() * 200) - 100; });
51 defaultMockMap.set('Float', function () { return Math.random() * 200 - 100; });
52 defaultMockMap.set('String', function () { return 'Hello World'; });
53 defaultMockMap.set('Boolean', function () { return Math.random() > 0.5; });
54 defaultMockMap.set('ID', function () { return uuid.v4(); });
55 function mergeObjects(a, b) {
56 return Object.assign(a, b);
57 }
58 function copyOwnPropsIfNotPresent(target, source) {
59 Object.getOwnPropertyNames(source).forEach(function (prop) {
60 if (!Object.getOwnPropertyDescriptor(target, prop)) {
61 Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop));
62 }
63 });
64 }
65 function copyOwnProps(target) {
66 var sources = [];
67 for (var _i = 1; _i < arguments.length; _i++) {
68 sources[_i - 1] = arguments[_i];
69 }
70 sources.forEach(function (source) {
71 var chain = source;
72 while (chain) {
73 copyOwnPropsIfNotPresent(target, chain);
74 chain = Object.getPrototypeOf(chain);
75 }
76 });
77 return target;
78 }
79 // returns a random element from that ary
80 function getRandomElement(ary) {
81 var sample = Math.floor(Math.random() * ary.length);
82 return ary[sample];
83 }
84 // takes either an object or a (possibly nested) array
85 // and completes the customMock object with any fields
86 // defined on genericMock
87 // only merges objects or arrays. Scalars are returned as is
88 function mergeMocks(genericMockFunction, customMock) {
89 if (Array.isArray(customMock)) {
90 return customMock.map(function (el) { return mergeMocks(genericMockFunction, el); });
91 }
92 if (isObject(customMock)) {
93 return mergeObjects(genericMockFunction(), customMock);
94 }
95 return customMock;
96 }
97 function getResolveType(namedFieldType) {
98 if (namedFieldType instanceof graphql_1.GraphQLInterfaceType ||
99 namedFieldType instanceof graphql_1.GraphQLUnionType) {
100 return namedFieldType.resolveType;
101 }
102 else {
103 return undefined;
104 }
105 }
106 function assignResolveType(type) {
107 var fieldType = graphql_1.getNullableType(type);
108 var namedFieldType = graphql_1.getNamedType(fieldType);
109 var oldResolveType = getResolveType(namedFieldType);
110 if (preserveResolvers && oldResolveType && oldResolveType.length) {
111 return;
112 }
113 if (namedFieldType instanceof graphql_1.GraphQLUnionType ||
114 namedFieldType instanceof graphql_1.GraphQLInterfaceType) {
115 // the default `resolveType` always returns null. We add a fallback
116 // resolution that works with how unions and interface are mocked
117 namedFieldType.resolveType = function (data, context, info) {
118 return info.schema.getType(data.__typename);
119 };
120 }
121 }
122 var mockType = function (type, typeName, fieldName) {
123 // order of precendence for mocking:
124 // 1. if the object passed in already has fieldName, just use that
125 // --> if it's a function, that becomes your resolver
126 // --> if it's a value, the mock resolver will return that
127 // 2. if the nullableType is a list, recurse
128 // 2. if there's a mock defined for this typeName, that will be used
129 // 3. if there's no mock defined, use the default mocks for this type
130 return function (root, args, context, info) {
131 // nullability doesn't matter for the purpose of mocking.
132 var fieldType = graphql_1.getNullableType(type);
133 var namedFieldType = graphql_1.getNamedType(fieldType);
134 if (root && typeof root[fieldName] !== 'undefined') {
135 var result = void 0;
136 // if we're here, the field is already defined
137 if (typeof root[fieldName] === 'function') {
138 result = root[fieldName](root, args, context, info);
139 if (result instanceof MockList) {
140 result = result.mock(root, args, context, info, fieldType, mockType);
141 }
142 }
143 else {
144 result = root[fieldName];
145 }
146 // Now we merge the result with the default mock for this type.
147 // This allows overriding defaults while writing very little code.
148 if (mockFunctionMap.has(namedFieldType.name)) {
149 result = mergeMocks(mockFunctionMap
150 .get(namedFieldType.name)
151 .bind(null, root, args, context, info), result);
152 }
153 return result;
154 }
155 if (fieldType instanceof graphql_1.GraphQLList) {
156 return [
157 mockType(fieldType.ofType)(root, args, context, info),
158 mockType(fieldType.ofType)(root, args, context, info),
159 ];
160 }
161 if (mockFunctionMap.has(fieldType.name) &&
162 !(fieldType instanceof graphql_1.GraphQLUnionType ||
163 fieldType instanceof graphql_1.GraphQLInterfaceType)) {
164 // the object passed doesn't have this field, so we apply the default mock
165 return mockFunctionMap.get(fieldType.name)(root, args, context, info);
166 }
167 if (fieldType instanceof graphql_1.GraphQLObjectType) {
168 // objects don't return actual data, we only need to mock scalars!
169 return {};
170 }
171 // if a mock function is provided for unionType or interfaceType, execute it to resolve the concrete type
172 // otherwise randomly pick a type from all implementation types
173 if (fieldType instanceof graphql_1.GraphQLUnionType ||
174 fieldType instanceof graphql_1.GraphQLInterfaceType) {
175 var implementationType = void 0;
176 if (mockFunctionMap.has(fieldType.name)) {
177 var interfaceMockObj = mockFunctionMap.get(fieldType.name)(root, args, context, info);
178 if (!interfaceMockObj || !interfaceMockObj.__typename) {
179 return Error("Please return a __typename in \"" + fieldType.name + "\"");
180 }
181 implementationType = schema.getType(interfaceMockObj.__typename);
182 }
183 else {
184 var possibleTypes = schema.getPossibleTypes(fieldType);
185 implementationType = getRandomElement(possibleTypes);
186 }
187 return Object.assign({ __typename: implementationType }, mockType(implementationType)(root, args, context, info));
188 }
189 if (fieldType instanceof graphql_1.GraphQLEnumType) {
190 return getRandomElement(fieldType.getValues()).value;
191 }
192 if (defaultMockMap.has(fieldType.name)) {
193 return defaultMockMap.get(fieldType.name)(root, args, context, info);
194 }
195 // if we get to here, we don't have a value, and we don't have a mock for this type,
196 // we could return undefined, but that would be hard to debug, so we throw instead.
197 // however, we returning it instead of throwing it, so preserveResolvers can handle the failures.
198 return Error("No mock defined for type \"" + fieldType.name + "\"");
199 };
200 };
201 schemaGenerator_1.forEachField(schema, function (field, typeName, fieldName) {
202 assignResolveType(field.type);
203 var mockResolver;
204 // we have to handle the root mutation and root query types differently,
205 // because no resolver is called at the root.
206 /* istanbul ignore next: Must provide schema DefinitionNode with query type or a type named Query. */
207 var isOnQueryType = schema.getQueryType()
208 ? schema.getQueryType().name === typeName
209 : false;
210 var isOnMutationType = schema.getMutationType()
211 ? schema.getMutationType().name === typeName
212 : false;
213 if (isOnQueryType || isOnMutationType) {
214 if (mockFunctionMap.has(typeName)) {
215 var rootMock_1 = mockFunctionMap.get(typeName);
216 // XXX: BUG in here, need to provide proper signature for rootMock.
217 if (rootMock_1(undefined, {}, {}, {})[fieldName]) {
218 // TODO: assert that it's a function
219 mockResolver = function (root, args, context, info) {
220 var updatedRoot = root || {}; // TODO: should we clone instead?
221 updatedRoot[fieldName] = rootMock_1(root, args, context, info)[fieldName];
222 // XXX this is a bit of a hack to still use mockType, which
223 // lets you mock lists etc. as well
224 // otherwise we could just set field.resolve to rootMock()[fieldName]
225 // it's like pretending there was a resolve function that ran before
226 // the root resolve function.
227 return mockType(field.type, typeName, fieldName)(updatedRoot, args, context, info);
228 };
229 }
230 }
231 }
232 if (!mockResolver) {
233 mockResolver = mockType(field.type, typeName, fieldName);
234 }
235 if (!preserveResolvers || !field.resolve) {
236 field.resolve = mockResolver;
237 }
238 else {
239 var oldResolver_1 = field.resolve;
240 field.resolve = function (rootObject, args, context, info) {
241 return Promise.all([
242 mockResolver(rootObject, args, context, info),
243 oldResolver_1(rootObject, args, context, info),
244 ]).then(function (values) {
245 var mockedValue = values[0], resolvedValue = values[1];
246 // In case we couldn't mock
247 if (mockedValue instanceof Error) {
248 // only if value was not resolved, populate the error.
249 if (undefined === resolvedValue) {
250 throw mockedValue;
251 }
252 return resolvedValue;
253 }
254 if (resolvedValue instanceof Date && mockedValue instanceof Date) {
255 return undefined !== resolvedValue ? resolvedValue : mockedValue;
256 }
257 if (isObject(mockedValue) && isObject(resolvedValue)) {
258 // Object.assign() won't do here, as we need to all properties, including
259 // the non-enumerable ones and defined using Object.defineProperty
260 var emptyObject = Object.create(Object.getPrototypeOf(resolvedValue));
261 return copyOwnProps(emptyObject, resolvedValue, mockedValue);
262 }
263 return undefined !== resolvedValue ? resolvedValue : mockedValue;
264 });
265 };
266 }
267 });
268}
269exports.addMockFunctionsToSchema = addMockFunctionsToSchema;
270var MockList = /** @class */ (function () {
271 // wrappedFunction can return another MockList or a value
272 function MockList(len, wrappedFunction) {
273 this.len = len;
274 if (typeof wrappedFunction !== 'undefined') {
275 if (typeof wrappedFunction !== 'function') {
276 throw new Error('Second argument to MockList must be a function or undefined');
277 }
278 this.wrappedFunction = wrappedFunction;
279 }
280 }
281 MockList.prototype.mock = function (root, args, context, info, fieldType, mockTypeFunc) {
282 var arr;
283 if (Array.isArray(this.len)) {
284 arr = new Array(this.randint(this.len[0], this.len[1]));
285 }
286 else {
287 arr = new Array(this.len);
288 }
289 for (var i = 0; i < arr.length; i++) {
290 if (typeof this.wrappedFunction === 'function') {
291 var res = this.wrappedFunction(root, args, context, info);
292 if (res instanceof MockList) {
293 var nullableType = graphql_1.getNullableType(fieldType.ofType);
294 arr[i] = res.mock(root, args, context, info, nullableType, mockTypeFunc);
295 }
296 else {
297 arr[i] = res;
298 }
299 }
300 else {
301 arr[i] = mockTypeFunc(fieldType.ofType)(root, args, context, info);
302 }
303 }
304 return arr;
305 };
306 MockList.prototype.randint = function (low, high) {
307 return Math.floor(Math.random() * (high - low + 1) + low);
308 };
309 return MockList;
310}());
311exports.MockList = MockList;
312//# sourceMappingURL=mock.js.map
\No newline at end of file