@graphql-tools/mock 8.6.13 → 8.7.0-alpha-b76ec274.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deepResolveMockList = exports.MockList = exports.isMockList = void 0;
4
+ /**
5
+ * @internal
6
+ */
7
+ function isMockList(obj) {
8
+ if (typeof (obj === null || obj === void 0 ? void 0 : obj.len) === 'number' || (Array.isArray(obj === null || obj === void 0 ? void 0 : obj.len) && typeof (obj === null || obj === void 0 ? void 0 : obj.len[0]) === 'number')) {
9
+ if (typeof obj.wrappedFunction === 'undefined' || typeof obj.wrappedFunction === 'function') {
10
+ return true;
11
+ }
12
+ }
13
+ return false;
14
+ }
15
+ exports.isMockList = isMockList;
16
+ /**
17
+ * This is an object you can return from your mock resolvers which calls the
18
+ * provided `mockFunction` once for each list item.
19
+ */
20
+ class MockList {
21
+ /**
22
+ * @param length Either the exact length of items to return or an inclusive
23
+ * range of possible lengths.
24
+ * @param mockFunction The function to call for each item in the list to
25
+ * resolve it. It can return another MockList or a value.
26
+ */
27
+ constructor(length, mockFunction) {
28
+ this.len = length;
29
+ if (typeof mockFunction !== 'undefined') {
30
+ if (typeof mockFunction !== 'function') {
31
+ throw new Error('Second argument to MockList must be a function or undefined');
32
+ }
33
+ this.wrappedFunction = mockFunction;
34
+ }
35
+ }
36
+ /**
37
+ * @internal
38
+ */
39
+ mock() {
40
+ let arr;
41
+ if (Array.isArray(this.len)) {
42
+ arr = new Array(this.randint(this.len[0], this.len[1]));
43
+ }
44
+ else {
45
+ arr = new Array(this.len);
46
+ }
47
+ for (let i = 0; i < arr.length; i++) {
48
+ if (typeof this.wrappedFunction === 'function') {
49
+ const res = this.wrappedFunction();
50
+ if (isMockList(res)) {
51
+ arr[i] = res.mock();
52
+ }
53
+ else {
54
+ arr[i] = res;
55
+ }
56
+ }
57
+ else {
58
+ arr[i] = undefined;
59
+ }
60
+ }
61
+ return arr;
62
+ }
63
+ randint(low, high) {
64
+ return Math.floor(Math.random() * (high - low + 1) + low);
65
+ }
66
+ }
67
+ exports.MockList = MockList;
68
+ function deepResolveMockList(mockList) {
69
+ return mockList.mock().map(v => {
70
+ if (isMockList(v))
71
+ return deepResolveMockList(v);
72
+ return v;
73
+ });
74
+ }
75
+ exports.deepResolveMockList = deepResolveMockList;
@@ -0,0 +1,489 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createMockStore = exports.MockStore = exports.defaultMocks = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const graphql_1 = require("graphql");
6
+ const fast_json_stable_stringify_1 = tslib_1.__importDefault(require("fast-json-stable-stringify"));
7
+ const types_js_1 = require("./types.js");
8
+ const utils_js_1 = require("./utils.js");
9
+ const MockList_js_1 = require("./MockList.js");
10
+ exports.defaultMocks = {
11
+ Int: () => Math.round(Math.random() * 200) - 100,
12
+ Float: () => Math.random() * 200 - 100,
13
+ String: () => 'Hello World',
14
+ Boolean: () => Math.random() > 0.5,
15
+ ID: () => (0, utils_js_1.uuidv4)(),
16
+ };
17
+ const defaultKeyFieldNames = ['id', '_id'];
18
+ class MockStore {
19
+ constructor({ schema, mocks, typePolicies, }) {
20
+ this.store = {};
21
+ this.schema = schema;
22
+ this.mocks = { ...exports.defaultMocks, ...mocks };
23
+ this.typePolicies = typePolicies || {};
24
+ }
25
+ has(typeName, key) {
26
+ return !!this.store[typeName] && !!this.store[typeName][key];
27
+ }
28
+ get(_typeName, _key, _fieldName, _fieldArgs) {
29
+ if (typeof _typeName !== 'string') {
30
+ if (_key === undefined) {
31
+ if ((0, types_js_1.isRef)(_typeName)) {
32
+ throw new Error("Can't provide a ref as first argument and no other argument");
33
+ }
34
+ // get({...})
35
+ return this.getImpl(_typeName);
36
+ }
37
+ else {
38
+ (0, types_js_1.assertIsRef)(_typeName);
39
+ const { $ref } = _typeName;
40
+ // arguments shift
41
+ _fieldArgs = _fieldName;
42
+ _fieldName = _key;
43
+ _key = $ref.key;
44
+ _typeName = $ref.typeName;
45
+ }
46
+ }
47
+ const args = {
48
+ typeName: _typeName,
49
+ };
50
+ if ((0, types_js_1.isRecord)(_key) || _key === undefined) {
51
+ // get('User', { name: 'Alex'})
52
+ args.defaultValue = _key;
53
+ return this.getImpl(args);
54
+ }
55
+ args.key = _key;
56
+ if (Array.isArray(_fieldName) && _fieldName.length === 1) {
57
+ _fieldName = _fieldName[0];
58
+ }
59
+ if (typeof _fieldName !== 'string' && !Array.isArray(_fieldName)) {
60
+ // get('User', 'me', { name: 'Alex'})
61
+ args.defaultValue = _fieldName;
62
+ return this.getImpl(args);
63
+ }
64
+ if (Array.isArray(_fieldName)) {
65
+ // get('User', 'me', ['father', 'name'])
66
+ const ref = this.get(_typeName, _key, _fieldName[0], _fieldArgs);
67
+ (0, types_js_1.assertIsRef)(ref);
68
+ return this.get(ref.$ref.typeName, ref.$ref.key, _fieldName.slice(1, _fieldName.length));
69
+ }
70
+ // get('User', 'me', 'name'...);
71
+ args.fieldName = _fieldName;
72
+ args.fieldArgs = _fieldArgs;
73
+ return this.getImpl(args);
74
+ }
75
+ set(_typeName, _key, _fieldName, _value) {
76
+ if (typeof _typeName !== 'string') {
77
+ if (_key === undefined) {
78
+ if ((0, types_js_1.isRef)(_typeName)) {
79
+ throw new Error("Can't provide a ref as first argument and no other argument");
80
+ }
81
+ // set({...})
82
+ return this.setImpl(_typeName);
83
+ }
84
+ else {
85
+ (0, types_js_1.assertIsRef)(_typeName);
86
+ const { $ref } = _typeName;
87
+ // arguments shift
88
+ _value = _fieldName;
89
+ _fieldName = _key;
90
+ _key = $ref.key;
91
+ _typeName = $ref.typeName;
92
+ }
93
+ }
94
+ assertIsDefined(_key, 'key was not provided');
95
+ const args = {
96
+ typeName: _typeName,
97
+ key: _key,
98
+ };
99
+ if (typeof _fieldName !== 'string') {
100
+ // set('User', 1, { name: 'Foo' })
101
+ if (!(0, types_js_1.isRecord)(_fieldName))
102
+ throw new Error('Expected value to be a record');
103
+ args.value = _fieldName;
104
+ return this.setImpl(args);
105
+ }
106
+ args.fieldName = _fieldName;
107
+ args.value = _value;
108
+ return this.setImpl(args);
109
+ }
110
+ reset() {
111
+ this.store = {};
112
+ }
113
+ filter(key, predicate) {
114
+ const entity = this.store[key];
115
+ return Object.values(entity).filter(predicate);
116
+ }
117
+ find(key, predicate) {
118
+ const entity = this.store[key];
119
+ return Object.values(entity).find(predicate);
120
+ }
121
+ getImpl(args) {
122
+ const { typeName, key, fieldName, fieldArgs, defaultValue } = args;
123
+ if (!fieldName) {
124
+ if (defaultValue !== undefined && !(0, types_js_1.isRecord)(defaultValue)) {
125
+ throw new Error('`defaultValue` should be an object');
126
+ }
127
+ let valuesToInsert = defaultValue || {};
128
+ if (key) {
129
+ valuesToInsert = { ...valuesToInsert, ...(0, utils_js_1.makeRef)(typeName, key) };
130
+ }
131
+ return this.insert(typeName, valuesToInsert, true);
132
+ }
133
+ assertIsDefined(key, 'key argument should be given when fieldName is given');
134
+ const fieldNameInStore = getFieldNameInStore(fieldName, fieldArgs);
135
+ if (this.store[typeName] === undefined ||
136
+ this.store[typeName][key] === undefined ||
137
+ this.store[typeName][key][fieldNameInStore] === undefined) {
138
+ let value;
139
+ if (defaultValue !== undefined) {
140
+ value = defaultValue;
141
+ }
142
+ else if (this.isKeyField(typeName, fieldName)) {
143
+ value = key;
144
+ }
145
+ else {
146
+ value = this.generateFieldValue(typeName, fieldName, (otherFieldName, otherValue) => {
147
+ // if we get a key field in the mix we don't care
148
+ if (this.isKeyField(typeName, otherFieldName))
149
+ return;
150
+ this.set({ typeName, key, fieldName: otherFieldName, value: otherValue, noOverride: true });
151
+ });
152
+ }
153
+ this.set({ typeName, key, fieldName, fieldArgs, value, noOverride: true });
154
+ }
155
+ return this.store[typeName][key][fieldNameInStore];
156
+ }
157
+ setImpl(args) {
158
+ const { typeName, key, fieldName, fieldArgs, noOverride } = args;
159
+ let { value } = args;
160
+ if ((0, MockList_js_1.isMockList)(value)) {
161
+ value = (0, MockList_js_1.deepResolveMockList)(value);
162
+ }
163
+ if (this.store[typeName] === undefined) {
164
+ this.store[typeName] = {};
165
+ }
166
+ if (this.store[typeName][key] === undefined) {
167
+ this.store[typeName][key] = {};
168
+ }
169
+ if (!fieldName) {
170
+ if (!(0, types_js_1.isRecord)(value)) {
171
+ throw new Error('When no `fieldName` is provided, `value` should be a record.');
172
+ }
173
+ for (const fieldName in value) {
174
+ this.setImpl({
175
+ typeName,
176
+ key,
177
+ fieldName,
178
+ value: value[fieldName],
179
+ noOverride,
180
+ });
181
+ }
182
+ return;
183
+ }
184
+ const fieldNameInStore = getFieldNameInStore(fieldName, fieldArgs);
185
+ if (this.isKeyField(typeName, fieldName) && value !== key) {
186
+ throw new Error(`Field ${fieldName} is a key field of ${typeName} and you are trying to set it to ${value} while the key is ${key}`);
187
+ }
188
+ // if already set and we don't override
189
+ if (this.store[typeName][key][fieldNameInStore] !== undefined && noOverride) {
190
+ return;
191
+ }
192
+ const fieldType = this.getFieldType(typeName, fieldName);
193
+ const currentValue = this.store[typeName][key][fieldNameInStore];
194
+ let valueToStore;
195
+ try {
196
+ valueToStore = this.normalizeValueToStore(fieldType, value, currentValue, (typeName, values) => this.insert(typeName, values, noOverride));
197
+ }
198
+ catch (e) {
199
+ throw new Error(`Value to set in ${typeName}.${fieldName} in not normalizable: ${e.message}`);
200
+ }
201
+ this.store[typeName][key] = {
202
+ ...this.store[typeName][key],
203
+ [fieldNameInStore]: valueToStore,
204
+ };
205
+ }
206
+ normalizeValueToStore(fieldType, value, currentValue, onInsertType) {
207
+ const fieldTypeName = fieldType.toString();
208
+ if (value === null) {
209
+ if (!(0, graphql_1.isNullableType)(fieldType)) {
210
+ throw new Error(`should not be null because ${fieldTypeName} is not nullable. Received null.`);
211
+ }
212
+ return null;
213
+ }
214
+ const nullableFieldType = (0, graphql_1.getNullableType)(fieldType);
215
+ if (value === undefined)
216
+ return this.generateValueFromType(nullableFieldType);
217
+ // deal with nesting insert
218
+ if ((0, graphql_1.isCompositeType)(nullableFieldType)) {
219
+ if (!(0, types_js_1.isRecord)(value))
220
+ throw new Error(`should be an object or null or undefined. Received ${value}`);
221
+ let joinedTypeName;
222
+ if ((0, graphql_1.isAbstractType)(nullableFieldType)) {
223
+ if ((0, types_js_1.isRef)(value)) {
224
+ joinedTypeName = value.$ref.typeName;
225
+ }
226
+ else {
227
+ if (typeof value['__typename'] !== 'string') {
228
+ throw new Error(`should contain a '__typename' because ${nullableFieldType.name} an abstract type`);
229
+ }
230
+ joinedTypeName = value['__typename'];
231
+ }
232
+ }
233
+ else {
234
+ joinedTypeName = nullableFieldType.name;
235
+ }
236
+ return onInsertType(joinedTypeName, (0, types_js_1.isRef)(currentValue) ? { ...currentValue, ...value } : value);
237
+ }
238
+ if ((0, graphql_1.isListType)(nullableFieldType)) {
239
+ if (!Array.isArray(value))
240
+ throw new Error(`should be an array or null or undefined. Received ${value}`);
241
+ return value.map((v, index) => {
242
+ return this.normalizeValueToStore(nullableFieldType.ofType, v, typeof currentValue === 'object' && currentValue != null && currentValue[index] ? currentValue : undefined, onInsertType);
243
+ });
244
+ }
245
+ return value;
246
+ }
247
+ insert(typeName, values, noOverride) {
248
+ const keyFieldName = this.getKeyFieldName(typeName);
249
+ let key;
250
+ // when we generate a key for the type, we might produce
251
+ // other associated values with it
252
+ // We keep track of them and we'll insert them, with propririty
253
+ // for the ones that we areasked to insert
254
+ const otherValues = {};
255
+ if ((0, types_js_1.isRef)(values)) {
256
+ key = values.$ref.key;
257
+ }
258
+ else if (keyFieldName && keyFieldName in values) {
259
+ key = values[keyFieldName];
260
+ }
261
+ else {
262
+ key = this.generateKeyForType(typeName, (otherFieldName, otherFieldValue) => {
263
+ otherValues[otherFieldName] = otherFieldValue;
264
+ });
265
+ }
266
+ const toInsert = { ...otherValues, ...values };
267
+ for (const fieldName in toInsert) {
268
+ if (fieldName === '$ref')
269
+ continue;
270
+ if (fieldName === '__typename')
271
+ continue;
272
+ this.set({
273
+ typeName,
274
+ key,
275
+ fieldName,
276
+ value: toInsert[fieldName],
277
+ noOverride,
278
+ });
279
+ }
280
+ if (this.store[typeName] === undefined) {
281
+ this.store[typeName] = {};
282
+ }
283
+ if (this.store[typeName][key] === undefined) {
284
+ this.store[typeName][key] = {};
285
+ }
286
+ return (0, utils_js_1.makeRef)(typeName, key);
287
+ }
288
+ generateFieldValue(typeName, fieldName, onOtherFieldsGenerated) {
289
+ const mockedValue = this.generateFieldValueFromMocks(typeName, fieldName, onOtherFieldsGenerated);
290
+ if (mockedValue !== undefined)
291
+ return mockedValue;
292
+ const fieldType = this.getFieldType(typeName, fieldName);
293
+ return this.generateValueFromType(fieldType);
294
+ }
295
+ generateFieldValueFromMocks(typeName, fieldName, onOtherFieldsGenerated) {
296
+ let value;
297
+ const mock = this.mocks ? this.mocks[typeName] : undefined;
298
+ if (mock) {
299
+ if (typeof mock === 'function') {
300
+ const values = mock();
301
+ if (typeof values !== 'object' || values == null) {
302
+ throw new Error(`Value returned by the mock for ${typeName} is not an object`);
303
+ }
304
+ for (const otherFieldName in values) {
305
+ if (otherFieldName === fieldName)
306
+ continue;
307
+ if (typeof values[otherFieldName] === 'function')
308
+ continue;
309
+ onOtherFieldsGenerated && onOtherFieldsGenerated(otherFieldName, values[otherFieldName]);
310
+ }
311
+ value = values[fieldName];
312
+ if (typeof value === 'function')
313
+ value = value();
314
+ }
315
+ else if (typeof mock === 'object' && mock != null && typeof mock[fieldName] === 'function') {
316
+ value = mock[fieldName]();
317
+ }
318
+ }
319
+ if (value !== undefined)
320
+ return value;
321
+ const type = this.getType(typeName);
322
+ // GraphQL 14 Compatibility
323
+ const interfaces = 'getInterfaces' in type ? type.getInterfaces() : [];
324
+ if (interfaces.length > 0) {
325
+ for (const interface_ of interfaces) {
326
+ if (value)
327
+ break;
328
+ value = this.generateFieldValueFromMocks(interface_.name, fieldName, onOtherFieldsGenerated);
329
+ }
330
+ }
331
+ return value;
332
+ }
333
+ generateKeyForType(typeName, onOtherFieldsGenerated) {
334
+ const keyFieldName = this.getKeyFieldName(typeName);
335
+ if (!keyFieldName)
336
+ return (0, utils_js_1.uuidv4)();
337
+ return this.generateFieldValue(typeName, keyFieldName, onOtherFieldsGenerated);
338
+ }
339
+ generateValueFromType(fieldType) {
340
+ const nullableType = (0, graphql_1.getNullableType)(fieldType);
341
+ if ((0, graphql_1.isScalarType)(nullableType)) {
342
+ const mockFn = this.mocks[nullableType.name];
343
+ if (typeof mockFn !== 'function')
344
+ throw new Error(`No mock defined for type "${nullableType.name}"`);
345
+ return mockFn();
346
+ }
347
+ else if ((0, graphql_1.isEnumType)(nullableType)) {
348
+ const mockFn = this.mocks[nullableType.name];
349
+ if (typeof mockFn === 'function')
350
+ return mockFn();
351
+ const values = nullableType.getValues().map(v => v.value);
352
+ return (0, utils_js_1.takeRandom)(values);
353
+ }
354
+ else if ((0, graphql_1.isObjectType)(nullableType)) {
355
+ // this will create a new random ref
356
+ return this.insert(nullableType.name, {});
357
+ }
358
+ else if ((0, graphql_1.isListType)(nullableType)) {
359
+ return [...new Array((0, utils_js_1.randomListLength)())].map(() => this.generateValueFromType(nullableType.ofType));
360
+ }
361
+ else if ((0, graphql_1.isAbstractType)(nullableType)) {
362
+ const mock = this.mocks[nullableType.name];
363
+ let typeName;
364
+ let values = {};
365
+ if (!mock) {
366
+ typeName = (0, utils_js_1.takeRandom)(this.schema.getPossibleTypes(nullableType).map(t => t.name));
367
+ }
368
+ else if (typeof mock === 'function') {
369
+ const mockRes = mock();
370
+ if (mockRes === null)
371
+ return null;
372
+ if (!(0, types_js_1.isRecord)(mockRes)) {
373
+ throw new Error(`Value returned by the mock for ${nullableType.name} is not an object or null`);
374
+ }
375
+ values = mockRes;
376
+ if (typeof values['__typename'] !== 'string') {
377
+ throw new Error(`Please return a __typename in "${nullableType.name}"`);
378
+ }
379
+ typeName = values['__typename'];
380
+ }
381
+ else if (typeof mock === 'object' && mock != null && typeof mock['__typename'] === 'function') {
382
+ const mockRes = mock['__typename']();
383
+ if (typeof mockRes !== 'string')
384
+ throw new Error(`'__typename' returned by the mock for abstract type ${nullableType.name} is not a string`);
385
+ typeName = mockRes;
386
+ }
387
+ else {
388
+ throw new Error(`Please return a __typename in "${nullableType.name}"`);
389
+ }
390
+ const toInsert = {};
391
+ for (const fieldName in values) {
392
+ if (fieldName === '__typename')
393
+ continue;
394
+ const fieldValue = values[fieldName];
395
+ toInsert[fieldName] = typeof fieldValue === 'function' ? fieldValue() : fieldValue;
396
+ }
397
+ return this.insert(typeName, toInsert);
398
+ }
399
+ else {
400
+ throw new Error(`${nullableType} not implemented`);
401
+ }
402
+ }
403
+ getFieldType(typeName, fieldName) {
404
+ if (fieldName === '__typename') {
405
+ return graphql_1.GraphQLString;
406
+ }
407
+ const type = this.getType(typeName);
408
+ const field = type.getFields()[fieldName];
409
+ if (!field) {
410
+ throw new Error(`${fieldName} does not exist on type ${typeName}`);
411
+ }
412
+ return field.type;
413
+ }
414
+ getType(typeName) {
415
+ const type = this.schema.getType(typeName);
416
+ if (!type || !((0, graphql_1.isObjectType)(type) || (0, graphql_1.isInterfaceType)(type))) {
417
+ throw new Error(`${typeName} does not exist on schema or is not an object or interface`);
418
+ }
419
+ return type;
420
+ }
421
+ isKeyField(typeName, fieldName) {
422
+ return this.getKeyFieldName(typeName) === fieldName;
423
+ }
424
+ getKeyFieldName(typeName) {
425
+ var _a;
426
+ const typePolicyKeyField = (_a = this.typePolicies[typeName]) === null || _a === void 0 ? void 0 : _a.keyFieldName;
427
+ if (typePolicyKeyField !== undefined) {
428
+ if (typePolicyKeyField === false)
429
+ return null;
430
+ return typePolicyKeyField;
431
+ }
432
+ // How about common key field names?
433
+ const gqlType = this.getType(typeName);
434
+ for (const fieldName in gqlType.getFields()) {
435
+ if (defaultKeyFieldNames.includes(fieldName)) {
436
+ return fieldName;
437
+ }
438
+ }
439
+ return null;
440
+ }
441
+ }
442
+ exports.MockStore = MockStore;
443
+ const getFieldNameInStore = (fieldName, fieldArgs) => {
444
+ if (!fieldArgs)
445
+ return fieldName;
446
+ if (typeof fieldArgs === 'string') {
447
+ return `${fieldName}:${fieldArgs}`;
448
+ }
449
+ // empty args
450
+ if (Object.keys(fieldArgs).length === 0) {
451
+ return fieldName;
452
+ }
453
+ return `${fieldName}:${(0, fast_json_stable_stringify_1.default)(fieldArgs)}`;
454
+ };
455
+ function assertIsDefined(value, message) {
456
+ if (value !== undefined && value !== null) {
457
+ return;
458
+ }
459
+ throw new Error(process.env['NODE_ENV'] === 'production' ? 'Invariant failed:' : `Invariant failed: ${message || ''}`);
460
+ }
461
+ /**
462
+ * Will create `MockStore` for the given `schema`.
463
+ *
464
+ * A `MockStore` will generate mock values for the given schem when queried.
465
+ *
466
+ * It will stores generated mocks, so that, provided with same arguments
467
+ * the returned values will be the same.
468
+ *
469
+ * Its API also allows to modify the stored values.
470
+ *
471
+ * Basic example:
472
+ * ```ts
473
+ * store.get('User', 1, 'name');
474
+ * // > "Hello World"
475
+ * store.set('User', 1, 'name', 'Alexandre');
476
+ * store.get('User', 1, 'name');
477
+ * // > "Alexandre"
478
+ * ```
479
+ *
480
+ * The storage key will correspond to the "key field"
481
+ * of the type. Field with name `id` or `_id` will be
482
+ * by default considered as the key field for the type.
483
+ * However, use `typePolicies` to precise the field to use
484
+ * as key.
485
+ */
486
+ function createMockStore(options) {
487
+ return new MockStore(options);
488
+ }
489
+ exports.createMockStore = createMockStore;