graphql-data-generator 0.3.0-alpha.2 → 0.3.0-alpha.20

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.
package/esm/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export { init } from "./init.js";
2
2
  export { codegen } from "./codegen.js";
3
- export { proxy } from "./proxy.js";
3
+ export { clear, proxy } from "./proxy.js";
4
4
  export { formatCode, toObject } from "./util.js";
package/esm/init.js CHANGED
@@ -1,11 +1,24 @@
1
+ import * as dntShim from "./_dnt.shims.js";
2
+ import { createRequire } from "node:module";
1
3
  import { readFileSync } from "node:fs";
2
4
  import { Kind, parse } from "graphql";
3
- import { dirname, join } from "node:path";
4
5
  import { gqlPluckFromCodeStringSync } from "@graphql-tools/graphql-tag-pluck";
5
6
  import { operation, proxy, withGetDefaultPatch } from "./proxy.js";
6
7
  import { toObject } from "./util.js";
8
+ import { dirname, resolve } from "node:path";
9
+ globalThis.require ??= createRequire(dntShim.Deno.cwd());
7
10
  const files = {};
8
- const loadFile = (path) => files[path] || (files[path] = readFileSync(path, "utf-8").replace(/#import "(.*)"/, (_, fragmentPath) => loadFile(join(dirname(path), fragmentPath))));
11
+ const loadFile = (path) => {
12
+ if (files[path])
13
+ return files[path];
14
+ const raw = files[path] = readFileSync(path, "utf-8");
15
+ const imports = Array.from(raw.matchAll(/#import "(.*)"/gm), ([, importPath]) => loadFile(require.resolve(importPath.startsWith(".")
16
+ ? resolve(dntShim.Deno.cwd(), dirname(path), importPath)
17
+ : importPath, { paths: [dntShim.Deno.cwd()] })));
18
+ if (!imports.length)
19
+ return raw;
20
+ return [raw, ...imports].join("\n\n");
21
+ };
9
22
  const getOperationContentMap = {};
10
23
  const getOperationContent = (path, operationName) => {
11
24
  const existing = getOperationContentMap[path];
@@ -14,14 +27,14 @@ const getOperationContent = (path, operationName) => {
14
27
  return existing;
15
28
  return getOperationContentMap[path][operationName];
16
29
  }
17
- const fileContent = readFileSync(path, "utf-8").replace(/#import "(.*)"/, (_, fragmentPath) => loadFile(join(dirname(path), fragmentPath)));
30
+ const fileContent = loadFile(path);
18
31
  try {
19
32
  const sources = gqlPluckFromCodeStringSync(path, fileContent);
20
33
  getOperationContentMap[path] = Object.fromEntries(sources.map((s) => {
21
34
  const document = parse(s);
22
35
  const firstOp = document.definitions.find((d) => d.kind === Kind.OPERATION_DEFINITION);
23
36
  if (!firstOp)
24
- throw new Error(`Cound not find an operation in ${path}`);
37
+ throw new Error(`Could not find an operation in ${path}`);
25
38
  return [firstOp.name?.value, document];
26
39
  }));
27
40
  }
@@ -30,7 +43,16 @@ const getOperationContent = (path, operationName) => {
30
43
  }
31
44
  return getOperationContent(path, operationName);
32
45
  };
33
- // - Types will be suffixed with their type: fooQuery or fooMutation
46
+ /**
47
+ * Initialize the data builder.
48
+ * @param schema The plain text of your schema.
49
+ * @param queries List of queries exported from generated types.
50
+ * @param mutations List of mutations exported from generated types.
51
+ * @param subscriptions List of subscriptions exported from generated types.
52
+ * @param types List of types exported from generated types.
53
+ * @param inputs List of types exported from generated types.
54
+ * @param scalars A mapping to generate scalar values. Function values will be invoked with their `__typename`.
55
+ */
34
56
  export const init = (schema, queries, mutations, subscriptions, types, inputs, scalars, options) => (fn) => {
35
57
  const doc = parse(schema);
36
58
  const build = {};
@@ -104,7 +126,6 @@ export const init = (schema, queries, mutations, subscriptions, types, inputs, s
104
126
  : variables,
105
127
  });
106
128
  Error.captureStackTrace(mock, obj.variables);
107
- mock.stack = mock.stack?.slice(6);
108
129
  return mock;
109
130
  },
110
131
  },
@@ -125,7 +146,6 @@ export const init = (schema, queries, mutations, subscriptions, types, inputs, s
125
146
  : data,
126
147
  });
127
148
  Error.captureStackTrace(mock, obj.data);
128
- mock.stack = mock.stack?.slice(6);
129
149
  return mock;
130
150
  },
131
151
  },
@@ -146,7 +166,6 @@ export const init = (schema, queries, mutations, subscriptions, types, inputs, s
146
166
  const builder = build[operation];
147
167
  const mock = builder(prevInput, patch);
148
168
  Error.captureStackTrace(mock, obj[name]);
149
- mock.stack = mock.stack?.slice(6);
150
169
  return mock;
151
170
  },
152
171
  }])));
@@ -158,14 +177,16 @@ export const init = (schema, queries, mutations, subscriptions, types, inputs, s
158
177
  if (transforms[name] && "default" in transforms[name]) {
159
178
  patches = [transforms[name].default, ...patches];
160
179
  }
161
- const { request: { query: parsedQuery, ...request }, ...raw } = operation(doc.definitions, scalars, query, ...patches);
162
- const mock = toObject({
163
- request: { ...request },
164
- ...raw,
180
+ const { mock, parsedQuery } = withGetDefaultPatch((type) => transforms[type]?.default, () => {
181
+ const { request: { query: parsedQuery, ...request }, ...raw } = operation(doc.definitions, scalars, query, ...patches);
182
+ const mock = toObject({
183
+ request: { ...request },
184
+ ...raw,
185
+ });
186
+ return { mock, parsedQuery };
165
187
  });
166
188
  mock.request.query = parsedQuery;
167
189
  Error.captureStackTrace(mock, builder);
168
- mock.stack = mock.stack?.slice(6);
169
190
  return addOperationTransforms(name, options?.finalizeOperation
170
191
  ? options.finalizeOperation(mock)
171
192
  : mock);
package/esm/jest.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import * as dntShim from "./_dnt.shims.js";
2
2
  import React, { useMemo } from "react";
3
- import { ApolloLink } from "@apollo/client";
4
- import { ErrorLink } from "@apollo/client/link/error/index.js";
5
- import { MockedProvider, MockLink, } from "@apollo/client/testing/index.js";
3
+ import { ApolloLink, useApolloClient, } from "@apollo/client";
4
+ import { onError } from "@apollo/client/link/error";
5
+ import { MockedProvider as ApolloMockedProvider, MockLink, } from "@apollo/client/testing";
6
6
  import { waitFor } from "@testing-library/dom";
7
- import { print } from "graphql";
7
+ import { Kind, print } from "graphql";
8
8
  import { diff as jestDiff } from "jest-diff";
9
9
  let currentSpecResult;
10
10
  jasmine.getEnv().addReporter({
@@ -12,25 +12,26 @@ jasmine.getEnv().addReporter({
12
12
  });
13
13
  const afterTest = [];
14
14
  afterEach(async () => {
15
- for (const hook of afterTest)
15
+ const hooks = afterTest.splice(0);
16
+ for (const hook of hooks)
16
17
  await hook();
17
- afterTest.splice(0);
18
18
  });
19
- const diff = (a, b) => jestDiff(a, b, {
20
- omitAnnotationLines: true,
21
- // aColor: green,
22
- // bColor: red,
23
- // changeColor: inverse,
24
- // commonColor: dim,
25
- // patchColor: yellow,
26
- })
19
+ const diff = (a, b) => jestDiff(a, b, { omitAnnotationLines: true })
27
20
  ?.replace(/\w+ \{/g, "{") // Remove class names
28
21
  .replace(/\w+ \[/g, "["); // Remove array class names
22
+ const getOperationDefinition = (document) => document.definitions.find((d) => d.kind === Kind.OPERATION_DEFINITION);
23
+ const getOperationType = (operation) => getOperationDefinition(operation.query)?.operation ??
24
+ "<unknown operation type>";
25
+ const getOperationName = (document) => getOperationDefinition(document)?.name?.value;
26
+ const getOperationInfo = (document) => {
27
+ const def = getOperationDefinition(document);
28
+ return {
29
+ name: def?.name?.value ?? "<unknown operation>",
30
+ operationType: def?.operation ?? "<unknown operation type>",
31
+ };
32
+ };
29
33
  const getErrorMessage = (operation, mockLink) => {
30
- const definition = operation.query.definitions[0];
31
- const operationType = definition.kind === "OperationDefinition"
32
- ? definition.operation
33
- : "<unknown operation type>";
34
+ const operationType = getOperationType(operation);
34
35
  const key = JSON.stringify({
35
36
  query: print(operation.query),
36
37
  });
@@ -38,7 +39,7 @@ const getErrorMessage = (operation, mockLink) => {
38
39
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
39
40
  const alts = (mockLink
40
41
  .mockedResponsesByKey[key] ?? []);
41
- let errorMessage = `Expected GraphQL ${operationType} ${operation.operationName} to have been mocked`;
42
+ let errorMessage = `Expected ${operationType} ${operation.operationName} to have been mocked`;
42
43
  if (alts.length || Object.keys(operation.variables).length > 0) {
43
44
  errorMessage += ` with variables ${dntShim.Deno.inspect(operation.variables, { depth: Infinity, colors: true })}`;
44
45
  }
@@ -61,59 +62,162 @@ const getErrorMessage = (operation, mockLink) => {
61
62
  : undefined,
62
63
  };
63
64
  };
64
- export const MockProvider = ({ mocks, stack, children }) => {
65
- const observableMocks = useMemo(() => {
66
- const observableMocks = mocks.map((m) => ({
67
- ...m,
68
- stack: m.stack,
69
- result: Object.assign(jest.fn(() => m.result), m.result),
70
- }));
71
- afterTest.push(async () => {
65
+ let _failRefetchWarnings = false;
66
+ /**
67
+ * In older versions of `@apollo/client`, refetches with missing mocks trigger
68
+ * warnings instead of being treated as standard missing mocks.
69
+ * This utility converts those warnings into failures and ensures watch queries
70
+ * are installed for queries with `watch: true`. This must be set when
71
+ * `MockProvider` is mounted.
72
+ *
73
+ * This is not required on modern version of `@apollo/client`.
74
+ */
75
+ export const failRefetchWarnings = (value = true) => _failRefetchWarnings = value;
76
+ let _allowMissingMocks = false;
77
+ /**
78
+ * Allows missing mocks, resulting in tests passing. Usage is intended to ease
79
+ * migration.
80
+ */
81
+ export const allowMissingMocks = (value) => _allowMissingMocks = value;
82
+ const AutoWatch = ({ mocks }) => {
83
+ const client = useApolloClient();
84
+ for (const mock of mocks)
85
+ if (mock.watch)
86
+ client.watchQuery(mock.request);
87
+ return null;
88
+ };
89
+ let lastMocks = [];
90
+ // deno-lint-ignore ban-types
91
+ const getStack = (to) => {
92
+ const obj = {};
93
+ Error.captureStackTrace(obj, to);
94
+ return obj.stack;
95
+ };
96
+ const _waitForMocks = async (mocks, cause) => {
97
+ for (const mock of mocks) {
98
+ if (mock.optional || mock.error)
99
+ continue;
100
+ await waitFor(() => {
72
101
  if (currentSpecResult.failedExpectations.length)
73
102
  return;
74
- for (const mock of observableMocks) {
75
- if ("optional" in mock && mock.optional || mock.error)
76
- continue;
77
- await waitFor(() => {
78
- if (currentSpecResult.failedExpectations.length)
79
- return;
80
- if (mock.result.mock.calls.length === 0) {
81
- const err = new Error(`Expected to have used mock ${mock.request.variables
82
- ? ` with variables ${dntShim.Deno.inspect(mock.request.variables, {
83
- depth: Infinity,
84
- colors: true,
85
- })}`
86
- : ""}`);
87
- err.stack = `${err.message}\n${mock.stack ?? stack ?? err.stack?.slice(6)}`;
88
- throw err;
89
- }
90
- }, { onTimeout: (e) => e });
103
+ if (mock.result.mock.calls.length === 0) {
104
+ const { name, operationType } = getOperationInfo(mock.request.query);
105
+ const err = new Error(`Expected to have used ${operationType} ${name}${mock.request.variables
106
+ ? ` with variables ${dntShim.Deno.inspect(mock.request.variables, {
107
+ depth: Infinity,
108
+ colors: true,
109
+ })}`
110
+ : ""}`);
111
+ if (mock.stack) {
112
+ err.stack = `${mock.stack}${cause ? `\nCaused by: ${cause}` : ""}`;
113
+ }
114
+ else if (cause)
115
+ err.stack = cause;
116
+ throw err;
91
117
  }
92
118
  });
119
+ }
120
+ };
121
+ /**
122
+ * Wait for mocks to have been used.
123
+ * @param mock If `undefined`, waits for all mocks. If a number, waits fort he first `mocks` mocks. If a string, waits for all mocks up until and including that mock.
124
+ * @param offset If `mocks` is a string, grabs the `offset`th mock of that name (e.g., the third `getReport` mock)
125
+ */
126
+ export const waitForMocks = async (mock = lastMocks.length, offset = 0) => {
127
+ if (typeof mock === "string") {
128
+ const matches = lastMocks.map((m, i) => [m, i])
129
+ .filter(([m]) => getOperationName(m.request.query) === mock);
130
+ if (matches.length <= offset) {
131
+ fail({
132
+ name: "Error",
133
+ message: `Expected mock ${mock} to have been mocked`,
134
+ stack: getStack(waitForMocks),
135
+ });
136
+ }
137
+ expect(matches.length).toBeGreaterThan(offset);
138
+ mock = matches[offset][1] + 1;
139
+ }
140
+ await _waitForMocks(lastMocks.slice(0, mock), getStack(waitForMocks));
141
+ };
142
+ /**
143
+ * A wrapper for `@apollo/client/testing`, this component will assert all
144
+ * requests have matching mocks and all defined mocks are used unless marked
145
+ * `optional`.
146
+ */
147
+ export const MockedProvider = ({ mocks, stack: renderStack, children, link: passedLink, ...rest }) => {
148
+ const observableMocks = useMemo(() => {
149
+ const observableMocks = mocks.flatMap((m) => [
150
+ {
151
+ ...m,
152
+ stack: m.stack,
153
+ result: Object.assign(jest.fn(() => m.result), m.result),
154
+ },
155
+ ...(m.watch
156
+ ? [{
157
+ ...m,
158
+ stack: m.stack,
159
+ result: Object.assign(jest.fn(() => m.result), m.result),
160
+ watch: false,
161
+ }]
162
+ : []),
163
+ ]);
164
+ lastMocks = observableMocks;
165
+ afterTest.push(() => _waitForMocks(lastMocks, renderStack));
93
166
  return observableMocks;
94
167
  }, [mocks]);
95
168
  const link = useMemo(() => {
96
169
  const mockLink = new MockLink(observableMocks);
97
170
  mockLink.showWarnings = false;
98
- const errorLoggingLink = new ErrorLink(({ networkError, operation }) => {
99
- if (!networkError?.message.includes("No more mocked responses"))
171
+ const errorLoggingLink = onError(({ networkError, operation }) => {
172
+ if (_allowMissingMocks ||
173
+ !networkError?.message?.includes("No more mocked responses"))
100
174
  return;
101
175
  const { message, stack: altStack } = getErrorMessage(operation, mockLink);
102
176
  try {
103
177
  networkError.message = message;
104
- const finalStack = altStack ?? stack
105
- ? `${message}\n${altStack ?? stack}`
106
- : undefined;
107
- if (finalStack)
108
- networkError.stack = finalStack;
109
- fail({ name: "Error", message, stack: finalStack });
178
+ if (altStack) {
179
+ networkError.stack = renderStack
180
+ ? `${altStack}\nCaused By: ${renderStack}`
181
+ : altStack;
182
+ }
183
+ else if (renderStack)
184
+ networkError.stack = renderStack;
185
+ fail({ name: "Error", message, stack: networkError.stack });
110
186
  }
111
187
  catch {
112
188
  // fail both throws and marks the test as failed in jest; we only need the latter
113
189
  }
114
190
  });
115
- // @ts-ignore It's fine
116
- return ApolloLink.from([errorLoggingLink, mockLink]);
117
- }, [observableMocks, stack]);
118
- return React.createElement(MockedProvider, { link: link }, children);
191
+ return ApolloLink.from([
192
+ errorLoggingLink,
193
+ mockLink,
194
+ ...(passedLink ? [passedLink] : []),
195
+ ]);
196
+ }, [observableMocks, renderStack]);
197
+ if (_failRefetchWarnings) {
198
+ const oldWarn = console.warn.bind(console.warn);
199
+ console.warn = (message, operation, ...etc) => {
200
+ if (message !==
201
+ 'Unknown query named "%s" requested in refetchQueries in options.include array') {
202
+ return oldWarn(message, operation, ...etc);
203
+ }
204
+ try {
205
+ fail({
206
+ name: "Error",
207
+ message: `Expected query ${operation} requested in refetchQueries options.include array to have bene mocked`,
208
+ stack: renderStack,
209
+ });
210
+ }
211
+ catch {
212
+ // eat
213
+ }
214
+ };
215
+ afterTest.push(() => {
216
+ console.warn = oldWarn;
217
+ });
218
+ }
219
+ return (React.createElement(ApolloMockedProvider, { ...rest, link: link },
220
+ React.createElement(React.Fragment, null,
221
+ React.createElement(AutoWatch, { mocks: observableMocks }),
222
+ children)));
119
223
  };
package/esm/proxy.js CHANGED
@@ -1,5 +1,14 @@
1
1
  import { Kind, Location, parse } from "graphql";
2
2
  import { absurd } from "./util.js";
3
+ /**
4
+ * Updates the value to the base value, bypassing default transformations.
5
+ * Useful to clear optional inputs, resulting in `undefined` rather than `null`.
6
+ */
7
+ export const clear = Symbol("clear");
8
+ /**
9
+ * Called to insert default patches at the front of nested objects (props &
10
+ * arrays). `build` will insert default patches for top-level objects.
11
+ */
3
12
  let getDefaultPatch = (__typename) => undefined;
4
13
  export const withGetDefaultPatch = (newGetDefaultPatch, fn) => {
5
14
  const prev = getDefaultPatch;
@@ -131,7 +140,7 @@ const resolveConcreteType = (definitions, definition, patch, prev) => {
131
140
  };
132
141
  const resolveValue = (definitions, scalars, type, ctx) => {
133
142
  if (type.kind !== Kind.NON_NULL_TYPE)
134
- return null;
143
+ return ctx.input ? undefined : null;
135
144
  if (type.type.kind === Kind.LIST_TYPE)
136
145
  return [];
137
146
  type = type.type;
@@ -161,7 +170,7 @@ const resolveValue = (definitions, scalars, type, ctx) => {
161
170
  ? rawDefaultPatch(base)
162
171
  : rawDefaultPatch;
163
172
  if (defaultPatch) {
164
- return _proxy(definitions, scalars, fieldTypeDefinitions[0].name.value, [base, defaultPatch]);
173
+ return _proxy(definitions, scalars, fieldTypeDefinitions[0].name.value, [base, defaultPatch], { selectionSet: ctx.selectionSet });
165
174
  }
166
175
  return base;
167
176
  }
@@ -258,7 +267,7 @@ const selectionSetToKeys = (definitions, selectionSet, typename) => {
258
267
  }
259
268
  return keys;
260
269
  };
261
- const _proxy = (definitions, scalars, path, patches, { prev, resolvedType = resolveType(definitions, path), selectionSet, } = {}) => {
270
+ let _proxy = (definitions, scalars, path, patches, { prev, resolvedType = resolveType(definitions, path), selectionSet, } = {}) => {
262
271
  const { parent = path, definition } = resolvedType;
263
272
  let type = resolvedType.type;
264
273
  if (!prev && patches.length) {
@@ -270,9 +279,18 @@ const _proxy = (definitions, scalars, path, patches, { prev, resolvedType = reso
270
279
  const patch = typeof rawPatch === "function"
271
280
  ? prev ? rawPatch(prev) : undefined
272
281
  : rawPatch;
282
+ if (patch === clear) {
283
+ return _proxy(definitions, scalars, path, [], { selectionSet });
284
+ }
273
285
  if (type.kind !== Kind.NON_NULL_TYPE) {
274
- if (!patches.length)
275
- return (prev ?? null);
286
+ if (!patches.length) {
287
+ return (prev ??
288
+ (resolveType(definitions, path.split(".").slice(0, -1).join("."))
289
+ .definition?.kind ===
290
+ Kind.INPUT_OBJECT_TYPE_DEFINITION
291
+ ? undefined
292
+ : null));
293
+ }
276
294
  }
277
295
  else
278
296
  type = type.type;
@@ -280,6 +298,9 @@ const _proxy = (definitions, scalars, path, patches, { prev, resolvedType = reso
280
298
  if (!patches.length)
281
299
  return (prev ?? []);
282
300
  const value = (patches.at(-1) ?? []);
301
+ if (value === clear) {
302
+ return _proxy(definitions, scalars, path, [], { selectionSet });
303
+ }
283
304
  const previousArray = (prev ?? []);
284
305
  const lastIndex = Math.max(previousArray.length - 1, 0);
285
306
  const nextIndex = previousArray.length;
@@ -297,7 +318,14 @@ const _proxy = (definitions, scalars, path, patches, { prev, resolvedType = reso
297
318
  }));
298
319
  const arr = [];
299
320
  for (let i = 0; i < length; i++) {
321
+ const nestType = type.type.kind === Kind.NAMED_TYPE
322
+ ? type.type.name.value
323
+ : type.type.kind === Kind.NON_NULL_TYPE &&
324
+ type.type.type.kind === Kind.NAMED_TYPE
325
+ ? type.type.type.name.value
326
+ : undefined;
300
327
  arr[i] = _proxy(definitions, scalars, `${path}.${i}`, [
328
+ nestType && !previousArray[i] ? getDefaultPatch(nestType) : undefined,
301
329
  value[i],
302
330
  i === lastIndex ? value.last : undefined,
303
331
  i === nextIndex ? value.next : undefined,
@@ -335,6 +363,9 @@ const _proxy = (definitions, scalars, path, patches, { prev, resolvedType = reso
335
363
  const value = typeof rawValue === "function"
336
364
  ? rawValue(prev)
337
365
  : rawValue;
366
+ if (value === clear) {
367
+ return target[prop] = _proxy(definitions, scalars, `${path}.${prop}`, [], { selectionSet });
368
+ }
338
369
  if (value && typeof value === "object") {
339
370
  const nonNullFieldType = field.type.kind === Kind.NON_NULL_TYPE
340
371
  ? field.type.type
@@ -373,6 +404,7 @@ const _proxy = (definitions, scalars, path, patches, { prev, resolvedType = reso
373
404
  selectionSet: selectionSet
374
405
  ? getSelectionSetSelection(definitions, selectionSet, prop)
375
406
  : undefined,
407
+ input: definition.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION,
376
408
  });
377
409
  };
378
410
  const keys = [
@@ -466,7 +498,7 @@ const _proxy = (definitions, scalars, path, patches, { prev, resolvedType = reso
466
498
  mock.variables[name] = mockPrev.variables[name];
467
499
  continue;
468
500
  }
469
- const result = resolveValue(definitions, scalars, variableDefinition.type, { hostType: `${path}Variables`, prop: name });
501
+ const result = resolveValue(definitions, scalars, variableDefinition.type, { hostType: `${path}Variables`, prop: name, input: false });
470
502
  if (result != null) {
471
503
  if (!mock.variables)
472
504
  mock.variables = {};
@@ -489,6 +521,10 @@ const _proxy = (definitions, scalars, path, patches, { prev, resolvedType = reso
489
521
  // Query, Mutation, Subscription
490
522
  const operationKey = definition.operation[0].toUpperCase() +
491
523
  definition.operation.slice(1);
524
+ if (value === clear) {
525
+ mock.data[prop] = _proxy(definitions, scalars, `${operationKey}.${selection.name.value}`, [], { selectionSet: selection.selectionSet });
526
+ continue;
527
+ }
492
528
  mock.data[prop] = _proxy(definitions, scalars, `${operationKey}.${selection.name.value}`, value != null ? [value] : [], {
493
529
  prev: mockPrev?.data?.[prop],
494
530
  selectionSet: selection.selectionSet,
@@ -534,6 +570,14 @@ const _proxy = (definitions, scalars, path, patches, { prev, resolvedType = reso
534
570
  const value = typeof scalar === "function" ? scalar(parent) : scalar;
535
571
  return value;
536
572
  }
573
+ case Kind.ENUM_TYPE_DEFINITION: {
574
+ const patch = patches[patches.length - 1];
575
+ if (patch != null)
576
+ return patch;
577
+ if (prev != null)
578
+ return prev;
579
+ return definition.values?.[0]?.name.value;
580
+ }
537
581
  default:
538
582
  throw new Error(`Unhandled definition kind '${definition.kind}'`);
539
583
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphql-data-generator",
3
- "version": "0.3.0-alpha.2",
3
+ "version": "0.3.0-alpha.20",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/voces/graphql-data-generator.git"
@@ -35,6 +35,16 @@
35
35
  "bin": {
36
36
  "graphql-data-generator": "./esm/cli.js"
37
37
  },
38
+ "typesVersions": {
39
+ "*": {
40
+ "script/jest": [
41
+ "types/jest.d.ts"
42
+ ],
43
+ "script/plugin": [
44
+ "types/plugin.d.ts"
45
+ ]
46
+ }
47
+ },
38
48
  "dependencies": {
39
49
  "@graphql-codegen/visitor-plugin-common": "*",
40
50
  "fast-glob": "*",
package/script/index.js CHANGED
@@ -1,11 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.toObject = exports.formatCode = exports.proxy = exports.codegen = exports.init = void 0;
3
+ exports.toObject = exports.formatCode = exports.proxy = exports.clear = exports.codegen = exports.init = void 0;
4
4
  var init_js_1 = require("./init.js");
5
5
  Object.defineProperty(exports, "init", { enumerable: true, get: function () { return init_js_1.init; } });
6
6
  var codegen_js_1 = require("./codegen.js");
7
7
  Object.defineProperty(exports, "codegen", { enumerable: true, get: function () { return codegen_js_1.codegen; } });
8
8
  var proxy_js_1 = require("./proxy.js");
9
+ Object.defineProperty(exports, "clear", { enumerable: true, get: function () { return proxy_js_1.clear; } });
9
10
  Object.defineProperty(exports, "proxy", { enumerable: true, get: function () { return proxy_js_1.proxy; } });
10
11
  var util_js_1 = require("./util.js");
11
12
  Object.defineProperty(exports, "formatCode", { enumerable: true, get: function () { return util_js_1.formatCode; } });
package/script/init.js CHANGED
@@ -1,14 +1,50 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  Object.defineProperty(exports, "__esModule", { value: true });
3
26
  exports.init = void 0;
27
+ const dntShim = __importStar(require("./_dnt.shims.js"));
28
+ const node_module_1 = require("node:module");
4
29
  const node_fs_1 = require("node:fs");
5
30
  const graphql_1 = require("graphql");
6
- const node_path_1 = require("node:path");
7
31
  const graphql_tag_pluck_1 = require("@graphql-tools/graphql-tag-pluck");
8
32
  const proxy_js_1 = require("./proxy.js");
9
33
  const util_js_1 = require("./util.js");
34
+ const node_path_1 = require("node:path");
35
+ globalThis.require ??= (0, node_module_1.createRequire)(dntShim.Deno.cwd());
10
36
  const files = {};
11
- const loadFile = (path) => files[path] || (files[path] = (0, node_fs_1.readFileSync)(path, "utf-8").replace(/#import "(.*)"/, (_, fragmentPath) => loadFile((0, node_path_1.join)((0, node_path_1.dirname)(path), fragmentPath))));
37
+ const loadFile = (path) => {
38
+ if (files[path])
39
+ return files[path];
40
+ const raw = files[path] = (0, node_fs_1.readFileSync)(path, "utf-8");
41
+ const imports = Array.from(raw.matchAll(/#import "(.*)"/gm), ([, importPath]) => loadFile(require.resolve(importPath.startsWith(".")
42
+ ? (0, node_path_1.resolve)(dntShim.Deno.cwd(), (0, node_path_1.dirname)(path), importPath)
43
+ : importPath, { paths: [dntShim.Deno.cwd()] })));
44
+ if (!imports.length)
45
+ return raw;
46
+ return [raw, ...imports].join("\n\n");
47
+ };
12
48
  const getOperationContentMap = {};
13
49
  const getOperationContent = (path, operationName) => {
14
50
  const existing = getOperationContentMap[path];
@@ -17,14 +53,14 @@ const getOperationContent = (path, operationName) => {
17
53
  return existing;
18
54
  return getOperationContentMap[path][operationName];
19
55
  }
20
- const fileContent = (0, node_fs_1.readFileSync)(path, "utf-8").replace(/#import "(.*)"/, (_, fragmentPath) => loadFile((0, node_path_1.join)((0, node_path_1.dirname)(path), fragmentPath)));
56
+ const fileContent = loadFile(path);
21
57
  try {
22
58
  const sources = (0, graphql_tag_pluck_1.gqlPluckFromCodeStringSync)(path, fileContent);
23
59
  getOperationContentMap[path] = Object.fromEntries(sources.map((s) => {
24
60
  const document = (0, graphql_1.parse)(s);
25
61
  const firstOp = document.definitions.find((d) => d.kind === graphql_1.Kind.OPERATION_DEFINITION);
26
62
  if (!firstOp)
27
- throw new Error(`Cound not find an operation in ${path}`);
63
+ throw new Error(`Could not find an operation in ${path}`);
28
64
  return [firstOp.name?.value, document];
29
65
  }));
30
66
  }
@@ -33,7 +69,16 @@ const getOperationContent = (path, operationName) => {
33
69
  }
34
70
  return getOperationContent(path, operationName);
35
71
  };
36
- // - Types will be suffixed with their type: fooQuery or fooMutation
72
+ /**
73
+ * Initialize the data builder.
74
+ * @param schema The plain text of your schema.
75
+ * @param queries List of queries exported from generated types.
76
+ * @param mutations List of mutations exported from generated types.
77
+ * @param subscriptions List of subscriptions exported from generated types.
78
+ * @param types List of types exported from generated types.
79
+ * @param inputs List of types exported from generated types.
80
+ * @param scalars A mapping to generate scalar values. Function values will be invoked with their `__typename`.
81
+ */
37
82
  const init = (schema, queries, mutations, subscriptions, types, inputs, scalars, options) => (fn) => {
38
83
  const doc = (0, graphql_1.parse)(schema);
39
84
  const build = {};
@@ -107,7 +152,6 @@ const init = (schema, queries, mutations, subscriptions, types, inputs, scalars,
107
152
  : variables,
108
153
  });
109
154
  Error.captureStackTrace(mock, obj.variables);
110
- mock.stack = mock.stack?.slice(6);
111
155
  return mock;
112
156
  },
113
157
  },
@@ -128,7 +172,6 @@ const init = (schema, queries, mutations, subscriptions, types, inputs, scalars,
128
172
  : data,
129
173
  });
130
174
  Error.captureStackTrace(mock, obj.data);
131
- mock.stack = mock.stack?.slice(6);
132
175
  return mock;
133
176
  },
134
177
  },
@@ -149,7 +192,6 @@ const init = (schema, queries, mutations, subscriptions, types, inputs, scalars,
149
192
  const builder = build[operation];
150
193
  const mock = builder(prevInput, patch);
151
194
  Error.captureStackTrace(mock, obj[name]);
152
- mock.stack = mock.stack?.slice(6);
153
195
  return mock;
154
196
  },
155
197
  }])));
@@ -161,14 +203,16 @@ const init = (schema, queries, mutations, subscriptions, types, inputs, scalars,
161
203
  if (transforms[name] && "default" in transforms[name]) {
162
204
  patches = [transforms[name].default, ...patches];
163
205
  }
164
- const { request: { query: parsedQuery, ...request }, ...raw } = (0, proxy_js_1.operation)(doc.definitions, scalars, query, ...patches);
165
- const mock = (0, util_js_1.toObject)({
166
- request: { ...request },
167
- ...raw,
206
+ const { mock, parsedQuery } = (0, proxy_js_1.withGetDefaultPatch)((type) => transforms[type]?.default, () => {
207
+ const { request: { query: parsedQuery, ...request }, ...raw } = (0, proxy_js_1.operation)(doc.definitions, scalars, query, ...patches);
208
+ const mock = (0, util_js_1.toObject)({
209
+ request: { ...request },
210
+ ...raw,
211
+ });
212
+ return { mock, parsedQuery };
168
213
  });
169
214
  mock.request.query = parsedQuery;
170
215
  Error.captureStackTrace(mock, builder);
171
- mock.stack = mock.stack?.slice(6);
172
216
  return addOperationTransforms(name, options?.finalizeOperation
173
217
  ? options.finalizeOperation(mock)
174
218
  : mock);
package/script/jest.js CHANGED
@@ -23,12 +23,12 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.MockProvider = void 0;
26
+ exports.MockedProvider = exports.waitForMocks = exports.allowMissingMocks = exports.failRefetchWarnings = void 0;
27
27
  const dntShim = __importStar(require("./_dnt.shims.js"));
28
28
  const react_1 = __importStar(require("react"));
29
29
  const client_1 = require("@apollo/client");
30
- const index_js_1 = require("@apollo/client/link/error/index.js");
31
- const index_js_2 = require("@apollo/client/testing/index.js");
30
+ const error_1 = require("@apollo/client/link/error");
31
+ const testing_1 = require("@apollo/client/testing");
32
32
  const dom_1 = require("@testing-library/dom");
33
33
  const graphql_1 = require("graphql");
34
34
  const jest_diff_1 = require("jest-diff");
@@ -38,25 +38,26 @@ jasmine.getEnv().addReporter({
38
38
  });
39
39
  const afterTest = [];
40
40
  afterEach(async () => {
41
- for (const hook of afterTest)
41
+ const hooks = afterTest.splice(0);
42
+ for (const hook of hooks)
42
43
  await hook();
43
- afterTest.splice(0);
44
44
  });
45
- const diff = (a, b) => (0, jest_diff_1.diff)(a, b, {
46
- omitAnnotationLines: true,
47
- // aColor: green,
48
- // bColor: red,
49
- // changeColor: inverse,
50
- // commonColor: dim,
51
- // patchColor: yellow,
52
- })
45
+ const diff = (a, b) => (0, jest_diff_1.diff)(a, b, { omitAnnotationLines: true })
53
46
  ?.replace(/\w+ \{/g, "{") // Remove class names
54
47
  .replace(/\w+ \[/g, "["); // Remove array class names
48
+ const getOperationDefinition = (document) => document.definitions.find((d) => d.kind === graphql_1.Kind.OPERATION_DEFINITION);
49
+ const getOperationType = (operation) => getOperationDefinition(operation.query)?.operation ??
50
+ "<unknown operation type>";
51
+ const getOperationName = (document) => getOperationDefinition(document)?.name?.value;
52
+ const getOperationInfo = (document) => {
53
+ const def = getOperationDefinition(document);
54
+ return {
55
+ name: def?.name?.value ?? "<unknown operation>",
56
+ operationType: def?.operation ?? "<unknown operation type>",
57
+ };
58
+ };
55
59
  const getErrorMessage = (operation, mockLink) => {
56
- const definition = operation.query.definitions[0];
57
- const operationType = definition.kind === "OperationDefinition"
58
- ? definition.operation
59
- : "<unknown operation type>";
60
+ const operationType = getOperationType(operation);
60
61
  const key = JSON.stringify({
61
62
  query: (0, graphql_1.print)(operation.query),
62
63
  });
@@ -64,7 +65,7 @@ const getErrorMessage = (operation, mockLink) => {
64
65
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
65
66
  const alts = (mockLink
66
67
  .mockedResponsesByKey[key] ?? []);
67
- let errorMessage = `Expected GraphQL ${operationType} ${operation.operationName} to have been mocked`;
68
+ let errorMessage = `Expected ${operationType} ${operation.operationName} to have been mocked`;
68
69
  if (alts.length || Object.keys(operation.variables).length > 0) {
69
70
  errorMessage += ` with variables ${dntShim.Deno.inspect(operation.variables, { depth: Infinity, colors: true })}`;
70
71
  }
@@ -87,60 +88,166 @@ const getErrorMessage = (operation, mockLink) => {
87
88
  : undefined,
88
89
  };
89
90
  };
90
- const MockProvider = ({ mocks, stack, children }) => {
91
- const observableMocks = (0, react_1.useMemo)(() => {
92
- const observableMocks = mocks.map((m) => ({
93
- ...m,
94
- stack: m.stack,
95
- result: Object.assign(jest.fn(() => m.result), m.result),
96
- }));
97
- afterTest.push(async () => {
91
+ let _failRefetchWarnings = false;
92
+ /**
93
+ * In older versions of `@apollo/client`, refetches with missing mocks trigger
94
+ * warnings instead of being treated as standard missing mocks.
95
+ * This utility converts those warnings into failures and ensures watch queries
96
+ * are installed for queries with `watch: true`. This must be set when
97
+ * `MockProvider` is mounted.
98
+ *
99
+ * This is not required on modern version of `@apollo/client`.
100
+ */
101
+ const failRefetchWarnings = (value = true) => _failRefetchWarnings = value;
102
+ exports.failRefetchWarnings = failRefetchWarnings;
103
+ let _allowMissingMocks = false;
104
+ /**
105
+ * Allows missing mocks, resulting in tests passing. Usage is intended to ease
106
+ * migration.
107
+ */
108
+ const allowMissingMocks = (value) => _allowMissingMocks = value;
109
+ exports.allowMissingMocks = allowMissingMocks;
110
+ const AutoWatch = ({ mocks }) => {
111
+ const client = (0, client_1.useApolloClient)();
112
+ for (const mock of mocks)
113
+ if (mock.watch)
114
+ client.watchQuery(mock.request);
115
+ return null;
116
+ };
117
+ let lastMocks = [];
118
+ // deno-lint-ignore ban-types
119
+ const getStack = (to) => {
120
+ const obj = {};
121
+ Error.captureStackTrace(obj, to);
122
+ return obj.stack;
123
+ };
124
+ const _waitForMocks = async (mocks, cause) => {
125
+ for (const mock of mocks) {
126
+ if (mock.optional || mock.error)
127
+ continue;
128
+ await (0, dom_1.waitFor)(() => {
98
129
  if (currentSpecResult.failedExpectations.length)
99
130
  return;
100
- for (const mock of observableMocks) {
101
- if ("optional" in mock && mock.optional || mock.error)
102
- continue;
103
- await (0, dom_1.waitFor)(() => {
104
- if (currentSpecResult.failedExpectations.length)
105
- return;
106
- if (mock.result.mock.calls.length === 0) {
107
- const err = new Error(`Expected to have used mock ${mock.request.variables
108
- ? ` with variables ${dntShim.Deno.inspect(mock.request.variables, {
109
- depth: Infinity,
110
- colors: true,
111
- })}`
112
- : ""}`);
113
- err.stack = `${err.message}\n${mock.stack ?? stack ?? err.stack?.slice(6)}`;
114
- throw err;
115
- }
116
- }, { onTimeout: (e) => e });
131
+ if (mock.result.mock.calls.length === 0) {
132
+ const { name, operationType } = getOperationInfo(mock.request.query);
133
+ const err = new Error(`Expected to have used ${operationType} ${name}${mock.request.variables
134
+ ? ` with variables ${dntShim.Deno.inspect(mock.request.variables, {
135
+ depth: Infinity,
136
+ colors: true,
137
+ })}`
138
+ : ""}`);
139
+ if (mock.stack) {
140
+ err.stack = `${mock.stack}${cause ? `\nCaused by: ${cause}` : ""}`;
141
+ }
142
+ else if (cause)
143
+ err.stack = cause;
144
+ throw err;
117
145
  }
118
146
  });
147
+ }
148
+ };
149
+ /**
150
+ * Wait for mocks to have been used.
151
+ * @param mock If `undefined`, waits for all mocks. If a number, waits fort he first `mocks` mocks. If a string, waits for all mocks up until and including that mock.
152
+ * @param offset If `mocks` is a string, grabs the `offset`th mock of that name (e.g., the third `getReport` mock)
153
+ */
154
+ const waitForMocks = async (mock = lastMocks.length, offset = 0) => {
155
+ if (typeof mock === "string") {
156
+ const matches = lastMocks.map((m, i) => [m, i])
157
+ .filter(([m]) => getOperationName(m.request.query) === mock);
158
+ if (matches.length <= offset) {
159
+ fail({
160
+ name: "Error",
161
+ message: `Expected mock ${mock} to have been mocked`,
162
+ stack: getStack(exports.waitForMocks),
163
+ });
164
+ }
165
+ expect(matches.length).toBeGreaterThan(offset);
166
+ mock = matches[offset][1] + 1;
167
+ }
168
+ await _waitForMocks(lastMocks.slice(0, mock), getStack(exports.waitForMocks));
169
+ };
170
+ exports.waitForMocks = waitForMocks;
171
+ /**
172
+ * A wrapper for `@apollo/client/testing`, this component will assert all
173
+ * requests have matching mocks and all defined mocks are used unless marked
174
+ * `optional`.
175
+ */
176
+ const MockedProvider = ({ mocks, stack: renderStack, children, link: passedLink, ...rest }) => {
177
+ const observableMocks = (0, react_1.useMemo)(() => {
178
+ const observableMocks = mocks.flatMap((m) => [
179
+ {
180
+ ...m,
181
+ stack: m.stack,
182
+ result: Object.assign(jest.fn(() => m.result), m.result),
183
+ },
184
+ ...(m.watch
185
+ ? [{
186
+ ...m,
187
+ stack: m.stack,
188
+ result: Object.assign(jest.fn(() => m.result), m.result),
189
+ watch: false,
190
+ }]
191
+ : []),
192
+ ]);
193
+ lastMocks = observableMocks;
194
+ afterTest.push(() => _waitForMocks(lastMocks, renderStack));
119
195
  return observableMocks;
120
196
  }, [mocks]);
121
197
  const link = (0, react_1.useMemo)(() => {
122
- const mockLink = new index_js_2.MockLink(observableMocks);
198
+ const mockLink = new testing_1.MockLink(observableMocks);
123
199
  mockLink.showWarnings = false;
124
- const errorLoggingLink = new index_js_1.ErrorLink(({ networkError, operation }) => {
125
- if (!networkError?.message.includes("No more mocked responses"))
200
+ const errorLoggingLink = (0, error_1.onError)(({ networkError, operation }) => {
201
+ if (_allowMissingMocks ||
202
+ !networkError?.message?.includes("No more mocked responses"))
126
203
  return;
127
204
  const { message, stack: altStack } = getErrorMessage(operation, mockLink);
128
205
  try {
129
206
  networkError.message = message;
130
- const finalStack = altStack ?? stack
131
- ? `${message}\n${altStack ?? stack}`
132
- : undefined;
133
- if (finalStack)
134
- networkError.stack = finalStack;
135
- fail({ name: "Error", message, stack: finalStack });
207
+ if (altStack) {
208
+ networkError.stack = renderStack
209
+ ? `${altStack}\nCaused By: ${renderStack}`
210
+ : altStack;
211
+ }
212
+ else if (renderStack)
213
+ networkError.stack = renderStack;
214
+ fail({ name: "Error", message, stack: networkError.stack });
136
215
  }
137
216
  catch {
138
217
  // fail both throws and marks the test as failed in jest; we only need the latter
139
218
  }
140
219
  });
141
- // @ts-ignore It's fine
142
- return client_1.ApolloLink.from([errorLoggingLink, mockLink]);
143
- }, [observableMocks, stack]);
144
- return react_1.default.createElement(index_js_2.MockedProvider, { link: link }, children);
220
+ return client_1.ApolloLink.from([
221
+ errorLoggingLink,
222
+ mockLink,
223
+ ...(passedLink ? [passedLink] : []),
224
+ ]);
225
+ }, [observableMocks, renderStack]);
226
+ if (_failRefetchWarnings) {
227
+ const oldWarn = console.warn.bind(console.warn);
228
+ console.warn = (message, operation, ...etc) => {
229
+ if (message !==
230
+ 'Unknown query named "%s" requested in refetchQueries in options.include array') {
231
+ return oldWarn(message, operation, ...etc);
232
+ }
233
+ try {
234
+ fail({
235
+ name: "Error",
236
+ message: `Expected query ${operation} requested in refetchQueries options.include array to have bene mocked`,
237
+ stack: renderStack,
238
+ });
239
+ }
240
+ catch {
241
+ // eat
242
+ }
243
+ };
244
+ afterTest.push(() => {
245
+ console.warn = oldWarn;
246
+ });
247
+ }
248
+ return (react_1.default.createElement(testing_1.MockedProvider, { ...rest, link: link },
249
+ react_1.default.createElement(react_1.default.Fragment, null,
250
+ react_1.default.createElement(AutoWatch, { mocks: observableMocks }),
251
+ children)));
145
252
  };
146
- exports.MockProvider = MockProvider;
253
+ exports.MockedProvider = MockedProvider;
package/script/proxy.js CHANGED
@@ -1,8 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.operation = exports.proxy = exports.withGetDefaultPatch = void 0;
3
+ exports.operation = exports.proxy = exports.withGetDefaultPatch = exports.clear = void 0;
4
4
  const graphql_1 = require("graphql");
5
5
  const util_js_1 = require("./util.js");
6
+ /**
7
+ * Updates the value to the base value, bypassing default transformations.
8
+ * Useful to clear optional inputs, resulting in `undefined` rather than `null`.
9
+ */
10
+ exports.clear = Symbol("clear");
11
+ /**
12
+ * Called to insert default patches at the front of nested objects (props &
13
+ * arrays). `build` will insert default patches for top-level objects.
14
+ */
6
15
  let getDefaultPatch = (__typename) => undefined;
7
16
  const withGetDefaultPatch = (newGetDefaultPatch, fn) => {
8
17
  const prev = getDefaultPatch;
@@ -135,7 +144,7 @@ const resolveConcreteType = (definitions, definition, patch, prev) => {
135
144
  };
136
145
  const resolveValue = (definitions, scalars, type, ctx) => {
137
146
  if (type.kind !== graphql_1.Kind.NON_NULL_TYPE)
138
- return null;
147
+ return ctx.input ? undefined : null;
139
148
  if (type.type.kind === graphql_1.Kind.LIST_TYPE)
140
149
  return [];
141
150
  type = type.type;
@@ -165,7 +174,7 @@ const resolveValue = (definitions, scalars, type, ctx) => {
165
174
  ? rawDefaultPatch(base)
166
175
  : rawDefaultPatch;
167
176
  if (defaultPatch) {
168
- return _proxy(definitions, scalars, fieldTypeDefinitions[0].name.value, [base, defaultPatch]);
177
+ return _proxy(definitions, scalars, fieldTypeDefinitions[0].name.value, [base, defaultPatch], { selectionSet: ctx.selectionSet });
169
178
  }
170
179
  return base;
171
180
  }
@@ -262,7 +271,7 @@ const selectionSetToKeys = (definitions, selectionSet, typename) => {
262
271
  }
263
272
  return keys;
264
273
  };
265
- const _proxy = (definitions, scalars, path, patches, { prev, resolvedType = resolveType(definitions, path), selectionSet, } = {}) => {
274
+ let _proxy = (definitions, scalars, path, patches, { prev, resolvedType = resolveType(definitions, path), selectionSet, } = {}) => {
266
275
  const { parent = path, definition } = resolvedType;
267
276
  let type = resolvedType.type;
268
277
  if (!prev && patches.length) {
@@ -274,9 +283,18 @@ const _proxy = (definitions, scalars, path, patches, { prev, resolvedType = reso
274
283
  const patch = typeof rawPatch === "function"
275
284
  ? prev ? rawPatch(prev) : undefined
276
285
  : rawPatch;
286
+ if (patch === exports.clear) {
287
+ return _proxy(definitions, scalars, path, [], { selectionSet });
288
+ }
277
289
  if (type.kind !== graphql_1.Kind.NON_NULL_TYPE) {
278
- if (!patches.length)
279
- return (prev ?? null);
290
+ if (!patches.length) {
291
+ return (prev ??
292
+ (resolveType(definitions, path.split(".").slice(0, -1).join("."))
293
+ .definition?.kind ===
294
+ graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION
295
+ ? undefined
296
+ : null));
297
+ }
280
298
  }
281
299
  else
282
300
  type = type.type;
@@ -284,6 +302,9 @@ const _proxy = (definitions, scalars, path, patches, { prev, resolvedType = reso
284
302
  if (!patches.length)
285
303
  return (prev ?? []);
286
304
  const value = (patches.at(-1) ?? []);
305
+ if (value === exports.clear) {
306
+ return _proxy(definitions, scalars, path, [], { selectionSet });
307
+ }
287
308
  const previousArray = (prev ?? []);
288
309
  const lastIndex = Math.max(previousArray.length - 1, 0);
289
310
  const nextIndex = previousArray.length;
@@ -301,7 +322,14 @@ const _proxy = (definitions, scalars, path, patches, { prev, resolvedType = reso
301
322
  }));
302
323
  const arr = [];
303
324
  for (let i = 0; i < length; i++) {
325
+ const nestType = type.type.kind === graphql_1.Kind.NAMED_TYPE
326
+ ? type.type.name.value
327
+ : type.type.kind === graphql_1.Kind.NON_NULL_TYPE &&
328
+ type.type.type.kind === graphql_1.Kind.NAMED_TYPE
329
+ ? type.type.type.name.value
330
+ : undefined;
304
331
  arr[i] = _proxy(definitions, scalars, `${path}.${i}`, [
332
+ nestType && !previousArray[i] ? getDefaultPatch(nestType) : undefined,
305
333
  value[i],
306
334
  i === lastIndex ? value.last : undefined,
307
335
  i === nextIndex ? value.next : undefined,
@@ -339,6 +367,9 @@ const _proxy = (definitions, scalars, path, patches, { prev, resolvedType = reso
339
367
  const value = typeof rawValue === "function"
340
368
  ? rawValue(prev)
341
369
  : rawValue;
370
+ if (value === exports.clear) {
371
+ return target[prop] = _proxy(definitions, scalars, `${path}.${prop}`, [], { selectionSet });
372
+ }
342
373
  if (value && typeof value === "object") {
343
374
  const nonNullFieldType = field.type.kind === graphql_1.Kind.NON_NULL_TYPE
344
375
  ? field.type.type
@@ -377,6 +408,7 @@ const _proxy = (definitions, scalars, path, patches, { prev, resolvedType = reso
377
408
  selectionSet: selectionSet
378
409
  ? getSelectionSetSelection(definitions, selectionSet, prop)
379
410
  : undefined,
411
+ input: definition.kind === graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION,
380
412
  });
381
413
  };
382
414
  const keys = [
@@ -470,7 +502,7 @@ const _proxy = (definitions, scalars, path, patches, { prev, resolvedType = reso
470
502
  mock.variables[name] = mockPrev.variables[name];
471
503
  continue;
472
504
  }
473
- const result = resolveValue(definitions, scalars, variableDefinition.type, { hostType: `${path}Variables`, prop: name });
505
+ const result = resolveValue(definitions, scalars, variableDefinition.type, { hostType: `${path}Variables`, prop: name, input: false });
474
506
  if (result != null) {
475
507
  if (!mock.variables)
476
508
  mock.variables = {};
@@ -493,6 +525,10 @@ const _proxy = (definitions, scalars, path, patches, { prev, resolvedType = reso
493
525
  // Query, Mutation, Subscription
494
526
  const operationKey = definition.operation[0].toUpperCase() +
495
527
  definition.operation.slice(1);
528
+ if (value === exports.clear) {
529
+ mock.data[prop] = _proxy(definitions, scalars, `${operationKey}.${selection.name.value}`, [], { selectionSet: selection.selectionSet });
530
+ continue;
531
+ }
496
532
  mock.data[prop] = _proxy(definitions, scalars, `${operationKey}.${selection.name.value}`, value != null ? [value] : [], {
497
533
  prev: mockPrev?.data?.[prop],
498
534
  selectionSet: selection.selectionSet,
@@ -538,6 +574,14 @@ const _proxy = (definitions, scalars, path, patches, { prev, resolvedType = reso
538
574
  const value = typeof scalar === "function" ? scalar(parent) : scalar;
539
575
  return value;
540
576
  }
577
+ case graphql_1.Kind.ENUM_TYPE_DEFINITION: {
578
+ const patch = patches[patches.length - 1];
579
+ if (patch != null)
580
+ return patch;
581
+ if (prev != null)
582
+ return prev;
583
+ return definition.values?.[0]?.name.value;
584
+ }
541
585
  default:
542
586
  throw new Error(`Unhandled definition kind '${definition.kind}'`);
543
587
  }
package/types/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { init } from "./init.js";
2
- export type { DeepPartial, Patch } from "./types.js";
2
+ export type { DeepPartial, OperationMock, Patch } from "./types.js";
3
3
  export { codegen } from "./codegen.js";
4
- export { proxy } from "./proxy.js";
4
+ export { clear, proxy } from "./proxy.js";
5
5
  export { formatCode, toObject } from "./util.js";
package/types/init.d.ts CHANGED
@@ -1,5 +1,15 @@
1
1
  import type { Build, MapObjectsToTransforms, MapOperationsToTransforms } from "./extendedTypes.js";
2
2
  import type { ContravariantEmpty, OperationMock } from "./types.js";
3
+ /**
4
+ * Initialize the data builder.
5
+ * @param schema The plain text of your schema.
6
+ * @param queries List of queries exported from generated types.
7
+ * @param mutations List of mutations exported from generated types.
8
+ * @param subscriptions List of subscriptions exported from generated types.
9
+ * @param types List of types exported from generated types.
10
+ * @param inputs List of types exported from generated types.
11
+ * @param scalars A mapping to generate scalar values. Function values will be invoked with their `__typename`.
12
+ */
3
13
  export declare const init: <Query extends Record<string, {
4
14
  data: Record<string, unknown>;
5
15
  variables?: Record<string, unknown>;
package/types/jest.d.ts CHANGED
@@ -1,7 +1,33 @@
1
1
  import React from "react";
2
+ import { MockedProviderProps } from "@apollo/client/testing";
2
3
  import type { OperationMock } from "./types.js";
3
- export declare const MockProvider: ({ mocks, stack, children }: {
4
- mocks: OperationMock[];
4
+ /**
5
+ * In older versions of `@apollo/client`, refetches with missing mocks trigger
6
+ * warnings instead of being treated as standard missing mocks.
7
+ * This utility converts those warnings into failures and ensures watch queries
8
+ * are installed for queries with `watch: true`. This must be set when
9
+ * `MockProvider` is mounted.
10
+ *
11
+ * This is not required on modern version of `@apollo/client`.
12
+ */
13
+ export declare const failRefetchWarnings: (value?: boolean) => boolean;
14
+ /**
15
+ * Allows missing mocks, resulting in tests passing. Usage is intended to ease
16
+ * migration.
17
+ */
18
+ export declare const allowMissingMocks: (value: true) => true;
19
+ /**
20
+ * Wait for mocks to have been used.
21
+ * @param mock If `undefined`, waits for all mocks. If a number, waits fort he first `mocks` mocks. If a string, waits for all mocks up until and including that mock.
22
+ * @param offset If `mocks` is a string, grabs the `offset`th mock of that name (e.g., the third `getReport` mock)
23
+ */
24
+ export declare const waitForMocks: (mock?: number | string, offset?: number) => Promise<void>;
25
+ /**
26
+ * A wrapper for `@apollo/client/testing`, this component will assert all
27
+ * requests have matching mocks and all defined mocks are used unless marked
28
+ * `optional`.
29
+ */
30
+ export declare const MockedProvider: ({ mocks, stack: renderStack, children, link: passedLink, ...rest }: Omit<MockedProviderProps, "mocks"> & {
31
+ mocks: ReadonlyArray<OperationMock>;
5
32
  stack?: string;
6
- children?: React.ReactNode;
7
33
  }) => React.JSX.Element;
package/types/proxy.d.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  import type { DefinitionNode, DocumentNode, GraphQLError } from "graphql";
2
2
  import type { OperationMock, Patch, SimpleOperationMock } from "./types.js";
3
+ /**
4
+ * Updates the value to the base value, bypassing default transformations.
5
+ * Useful to clear optional inputs, resulting in `undefined` rather than `null`.
6
+ */
7
+ export declare const clear: undefined;
3
8
  export declare const withGetDefaultPatch: <T>(newGetDefaultPatch: <U>(__typename: string) => Patch<U> | ((prev: U) => Patch<U> | undefined) | undefined, fn: () => T) => T;
4
9
  export declare const proxy: <T>(definitions: readonly DefinitionNode[], scalars: Record<string, unknown | ((typename: string) => unknown)>, type: string, ...patches: (Patch<T> | ((prev: T) => Patch<T>))[]) => T;
5
10
  export declare const operation: <O extends SimpleOperationMock, Extra = object>(definitions: readonly DefinitionNode[], scalars: Record<string, unknown | ((typename: string) => unknown)>, query: string | DocumentNode, ...patches: (Patch<Omit<O, "error" | "errors">> & {
package/types/types.d.ts CHANGED
@@ -28,12 +28,16 @@ export type OperationMock<Data extends Record<string, unknown> = Record<string,
28
28
  };
29
29
  error?: Error;
30
30
  stack?: string;
31
+ watch?: boolean;
32
+ optional?: boolean;
31
33
  };
32
34
  export type SimpleOperationMock<Data extends Record<string, unknown> = Record<string, unknown>, Variables = Record<string, unknown> | never> = {
33
35
  data: Data;
34
36
  variables?: Variables;
35
37
  error?: Error;
36
38
  errors?: GraphQLError[];
39
+ watch?: boolean;
40
+ optional?: boolean;
37
41
  };
38
42
  export type SimpleOperationPatch<Data extends Record<string, unknown> = Record<string, unknown>, Variables = Record<string, unknown> | never> = Patch<{
39
43
  data: Data;
@@ -41,5 +45,7 @@ export type SimpleOperationPatch<Data extends Record<string, unknown> = Record<s
41
45
  }> & {
42
46
  error?: Error;
43
47
  errors?: GraphQLError[];
48
+ watch?: boolean;
49
+ optional?: boolean;
44
50
  };
45
51
  export {};