UNPKG

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