UNPKG

10.9 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.mockServer = exports.MockList = exports.addMockFunctionsToSchema = undefined;
7
8var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
9
10var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
11
12var _type = require('graphql/type');
13
14var _graphql = require('graphql');
15
16var _nodeUuid = require('node-uuid');
17
18var _nodeUuid2 = _interopRequireDefault(_nodeUuid);
19
20var _schemaGenerator = require('./schemaGenerator');
21
22function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
23
24function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
25
26// This function wraps addMockFunctionsToSchema for more convenience
27function mockServer(schema) {
28 var mocks = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
29 var preserveResolvers = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
30
31 var mySchema = schema;
32 if (!(schema instanceof _type.GraphQLSchema)) {
33 // TODO: provide useful error messages here if this fails
34 mySchema = (0, _schemaGenerator.buildSchemaFromTypeDefinitions)(schema);
35 }
36 addMockFunctionsToSchema({ schema: mySchema, mocks: mocks, preserveResolvers: preserveResolvers });
37
38 return { query: function query(_query, vars) {
39 return (0, _graphql.graphql)(mySchema, _query, {}, {}, vars);
40 } };
41}
42
43// TODO allow providing a seed such that lengths of list could be deterministic
44// this could be done by using casual to get a random list length if the casual
45// object is global.
46function addMockFunctionsToSchema() {
47 var _ref = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
48
49 var schema = _ref.schema;
50 var _ref$mocks = _ref.mocks;
51 var mocks = _ref$mocks === undefined ? {} : _ref$mocks;
52 var _ref$preserveResolver = _ref.preserveResolvers;
53 var preserveResolvers = _ref$preserveResolver === undefined ? false : _ref$preserveResolver;
54
55 function isObject(thing) {
56 return thing === Object(thing) && !Array.isArray(thing);
57 }
58 if (!schema) {
59 // XXX should we check that schema is an instance of GraphQLSchema?
60 throw new Error('Must provide schema to mock');
61 }
62 if (!isObject(mocks)) {
63 throw new Error('mocks must be of type Object');
64 }
65
66 // use Map internally, because that API is nicer.
67 var mockFunctionMap = new Map();
68 Object.keys(mocks).forEach(function (typeName) {
69 mockFunctionMap.set(typeName, mocks[typeName]);
70 });
71
72 mockFunctionMap.forEach(function (mockFunction, mockTypeName) {
73 if (typeof mockFunction !== 'function') {
74 throw new Error('mockFunctionMap[' + mockTypeName + '] must be a function');
75 }
76 });
77
78 var defaultMockMap = new Map();
79 defaultMockMap.set('Int', function () {
80 return Math.round(Math.random() * 200) - 100;
81 });
82 defaultMockMap.set('Float', function () {
83 return Math.random() * 200 - 100;
84 });
85 defaultMockMap.set('String', function () {
86 return 'Hello World';
87 });
88 defaultMockMap.set('Boolean', function () {
89 return Math.random() > 0.5;
90 });
91 defaultMockMap.set('ID', function () {
92 return _nodeUuid2.default.v4();
93 });
94
95 function mergeObjects(a, b) {
96 return Object.assign(a, b);
97 }
98
99 // returns a random element from that ary
100 function getRandomElement(ary) {
101 var sample = Math.floor(Math.random() * ary.length);
102 return ary[sample];
103 }
104
105 // takes either an object or a (possibly nested) array
106 // and completes the customMock object with any fields
107 // defined on genericMock
108 // only merges objects or arrays. Scalars are returned as is
109 function mergeMocks(genericMockFunction, customMock) {
110 if (Array.isArray(customMock)) {
111 return customMock.map(function (el) {
112 return mergeMocks(genericMockFunction, el);
113 });
114 }
115 if (isObject(customMock)) {
116 return mergeObjects(genericMockFunction(), customMock);
117 }
118 return customMock;
119 }
120
121 var mockType = function mockType(type, typeName, fieldName) {
122 // order of precendence for mocking:
123 // 1. if the object passed in already has fieldName, just use that
124 // --> if it's a function, that becomes your resolver
125 // --> if it's a value, the mock resolver will return that
126 // 2. if the nullableType is a list, recurse
127 // 2. if there's a mock defined for this typeName, that will be used
128 // 3. if there's no mock defined, use the default mocks for this type
129 return function (o, a, c, r) {
130 // nullability doesn't matter for the purpose of mocking.
131 var fieldType = (0, _type.getNullableType)(type);
132 var namedFieldType = (0, _type.getNamedType)(fieldType);
133
134 if (o && typeof o[fieldName] !== 'undefined') {
135 var result = void 0;
136 // if we're here, the field is already defined
137 if (typeof o[fieldName] === 'function') {
138 result = o[fieldName](o, a, c, r);
139 if (result instanceof MockList) {
140 result = result.mock(o, a, c, r, fieldType, mockType);
141 }
142 } else {
143 result = o[fieldName];
144 }
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.get(namedFieldType.name).bind(null, o, a, c, r), result);
150 }
151 return result;
152 }
153
154 if (fieldType instanceof _type.GraphQLList) {
155 return [mockType(type.ofType)(o, a, c, r), mockType(type.ofType)(o, a, c, r)];
156 }
157 if (mockFunctionMap.has(fieldType.name)) {
158 // the object passed doesn't have this field, so we apply the default mock
159 return mockFunctionMap.get(fieldType.name)(o, a, c, r);
160 }
161 if (fieldType instanceof _type.GraphQLObjectType) {
162 // objects don't return actual data, we only need to mock scalars!
163 return {};
164 }
165 // TODO mocking Interface and Union types will require determining the
166 // resolve type before passing it on.
167 // XXX we recommend a generic way for resolve type here, which is defining
168 // typename on the object.
169 if (fieldType instanceof _type.GraphQLUnionType) {
170 // TODO: if a union type doesn't implement resolveType, we could overwrite
171 // it with a default that works with what's below.
172 return { typename: getRandomElement(fieldType.getTypes()) };
173 }
174 if (fieldType instanceof _type.GraphQLEnumType) {
175 return getRandomElement(fieldType.getValues()).value;
176 }
177 if (defaultMockMap.has(fieldType.name)) {
178 return defaultMockMap.get(fieldType.name)(o, a, c, r);
179 }
180 // if we get to here, we don't have a value, and we don't have a mock for this type,
181 // we could return undefined, but that would be hard to debug, so we throw instead.
182 throw new Error('No mock defined for type "' + fieldType.name + '"');
183 };
184 };
185
186 (0, _schemaGenerator.forEachField)(schema, function (field, typeName, fieldName) {
187 if (preserveResolvers && field.resolve) {
188 return;
189 }
190
191 // we have to handle the root mutation and root query types differently,
192 // because no resolver is called at the root.
193 var isOnQueryType = typeName === (schema.getQueryType() || {}).name;
194 var isOnMutationType = typeName === (schema.getMutationType() || {}).name;
195 if (isOnQueryType || isOnMutationType) {
196 if (mockFunctionMap.has(typeName)) {
197 var _ret = function () {
198 var rootMock = mockFunctionMap.get(typeName);
199 if (rootMock()[fieldName]) {
200 // TODO: assert that it's a function
201 // eslint-disable-next-line no-param-reassign
202 field.resolve = function (o, a, c, r) {
203 var u = o || {}; // TODO: should we clone instead?
204 u[fieldName] = rootMock()[fieldName];
205 // XXX this is a bit of a hack to still use mockType, which
206 // lets you mock lists etc. as well
207 // otherwise we could just set field.resolve to rootMock()[fieldName]
208 // it's like pretending there was a resolve function that ran before
209 // the root resolve function.
210 return mockType(field.type, typeName, fieldName)(u, a, c, r);
211 };
212 return {
213 v: void 0
214 };
215 }
216 }();
217
218 if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v;
219 }
220 }
221 // eslint-disable-next-line no-param-reassign
222 field.resolve = mockType(field.type, typeName, fieldName);
223 });
224}
225
226var MockList = function () {
227 // wrappedFunction can return another MockList or a value
228
229 function MockList(len, wrappedFunction) {
230 _classCallCheck(this, MockList);
231
232 this.len = len;
233 if (typeof wrappedFunction !== 'undefined') {
234 if (typeof wrappedFunction !== 'function') {
235 throw new Error('Second argument to MockList must be a function or undefined');
236 }
237 this.wrappedFunction = wrappedFunction;
238 }
239 }
240
241 _createClass(MockList, [{
242 key: 'mock',
243 value: function mock(o, a, c, r, fieldType, mockTypeFunc) {
244 function randint(low, high) {
245 return Math.floor(Math.random() * (high - low + 1) + low);
246 }
247 var arr = void 0;
248 if (Array.isArray(this.len)) {
249 arr = new Array(randint(this.len[0], this.len[1]));
250 } else {
251 arr = new Array(this.len);
252 }
253 for (var i = 0; i < arr.length; i++) {
254 if (typeof this.wrappedFunction === 'function') {
255 var res = this.wrappedFunction(o, a, c, r);
256 if (res instanceof MockList) {
257 arr[i] = res.mock(o, a, c, r, (0, _type.getNullableType)(fieldType.ofType), mockTypeFunc);
258 } else {
259 arr[i] = res;
260 }
261 } else {
262 arr[i] = mockTypeFunc(fieldType.ofType)(o, a, c, r);
263 }
264 }
265 return arr;
266 }
267 }]);
268
269 return MockList;
270}();
271
272exports.addMockFunctionsToSchema = addMockFunctionsToSchema;
273exports.MockList = MockList;
274exports.mockServer = mockServer;
\No newline at end of file