graphql-data-generator 0.4.0-alpha.6 → 0.4.0-alpha.8

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/README.md CHANGED
@@ -279,10 +279,38 @@ be helpful to disable asserting all requests are mocked. A helper,
279
279
  `allowMissingMocks`, exists to disable these assertions and can be called before
280
280
  any tests.
281
281
 
282
+ ### Issues
283
+
284
+ #### Refetch warnings
285
+
282
286
  If you are using an old version of `@apollo/client`, missing refetch requests
283
287
  will emit warnings instead of errors. You can use `failRefetchWarnings` to
284
288
  convert these warnings to errors.
285
289
 
290
+ #### Early unmounts
291
+
292
+ `@testing-library/react` automatically registers an `afterEach` hook to clean up
293
+ the DOM after each test. Similarly, `graphql-data-generator` adds its own
294
+ `afterEach` hook to verify that all mocks are consumed by the end of the test.
295
+
296
+ This can lead to unexpected errors or noisy logs if the DOM is unmounted before
297
+ `graphql-data-generator` runs its checks.
298
+
299
+ To avoid this, `graphql-data-generator` disables `@testing-library/react`'s
300
+ cleanup by default — **but only if it is loaded first**. If you can’t guarantee
301
+ the load order, you should manually disable the automatic cleanup:
302
+
303
+ ```ts
304
+ import "npm:@testing-library/react/dont-cleanup-after-each";
305
+ ```
306
+
307
+ If instead you want to disable `graphql-data-generator`'s cleanup behavior,
308
+ call:
309
+
310
+ ```ts
311
+ import { skipCleanupAfterEach } from "graphql-data-generator";
312
+ ```
313
+
286
314
  ## Extra
287
315
 
288
316
  The `init` function supports a 6th optional generic parameter, `Extra`, which
package/esm/jest.js CHANGED
@@ -8,10 +8,20 @@ import { Kind, print } from "graphql";
8
8
  import { diff as jestDiff } from "jest-diff";
9
9
  import "@testing-library/react/dont-cleanup-after-each";
10
10
  import { cleanup } from "@testing-library/react";
11
+ import { addTypenameToDocument } from "@apollo/client/utilities";
11
12
  let currentSpecResult;
12
- jasmine.getEnv().addReporter({
13
+ globalThis.jasmine
14
+ ?.getEnv()
15
+ .addReporter({
13
16
  specStarted: (result) => currentSpecResult = result,
14
17
  });
18
+ const alreadyFailed = () => {
19
+ if ((currentSpecResult?.failedExpectations.length ?? 0) > 0)
20
+ return true;
21
+ const state = expect.getState();
22
+ return state.assertionCalls > state.numPassingAsserts ||
23
+ state.suppressedErrors.length > 0;
24
+ };
15
25
  let _skipCleanupAfterEach = false;
16
26
  /**
17
27
  * `@testing-library/react` automatically unmounts React trees that were mounted
@@ -48,7 +58,7 @@ const getOperationInfo = (document) => {
48
58
  const getErrorMessage = (operation, mockLink) => {
49
59
  const operationType = getOperationType(operation);
50
60
  const key = JSON.stringify({
51
- query: print(operation.query),
61
+ query: print(addTypenameToDocument(operation.query)),
52
62
  });
53
63
  // Bypassing private variable
54
64
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -83,7 +93,7 @@ let _failRefetchWarnings = false;
83
93
  * warnings instead of being treated as standard missing mocks.
84
94
  * This utility converts those warnings into failures and ensures watch queries
85
95
  * are installed for queries with `watch: true`. This must be set when
86
- * `MockProvider` is mounted.
96
+ * `MockedProvider` is mounted.
87
97
  *
88
98
  * This is not required on modern version of `@apollo/client`.
89
99
  */
@@ -114,7 +124,7 @@ const _waitForMocks = async (mocks, { cause, timeout }) => {
114
124
  continue;
115
125
  try {
116
126
  await waitFor(() => {
117
- if (currentSpecResult.failedExpectations.length)
127
+ if (alreadyFailed())
118
128
  return;
119
129
  if (mock.result.mock.calls.length === 0) {
120
130
  throw new Error("");
@@ -134,11 +144,8 @@ const _waitForMocks = async (mocks, { cause, timeout }) => {
134
144
  }
135
145
  else if (cause)
136
146
  err.stack = cause;
137
- fail({
138
- name: "Error",
139
- message: err.message,
140
- stack: err.stack,
141
- });
147
+ err.stack = `${err.message}\n${err.stack?.split("\n").slice(1).join("\n")}`;
148
+ expect.getState().suppressedErrors.push(err);
142
149
  }
143
150
  }
144
151
  };
@@ -152,11 +159,11 @@ export const waitForMocks = async (mock = lastMocks.length, { timeout, offset =
152
159
  const matches = lastMocks.map((m, i) => [m, i])
153
160
  .filter(([m]) => getOperationName(m.request.query) === mock);
154
161
  if (matches.length <= offset) {
155
- fail({
156
- name: "Error",
157
- message: `Expected mock ${mock} to have been mocked`,
158
- stack: getStack(waitForMocks),
159
- });
162
+ const err = new Error(`Expected mock ${mock} to have been mocked`);
163
+ Error.captureStackTrace(err, waitForMocks);
164
+ err.stack = err.message + "\n" +
165
+ err.stack?.split("\n").slice(1).join("\n");
166
+ throw err;
160
167
  }
161
168
  expect(matches.length).toBeGreaterThan(offset);
162
169
  mock = matches[offset][1] + 1;
@@ -166,6 +173,15 @@ export const waitForMocks = async (mock = lastMocks.length, { timeout, offset =
166
173
  cause: getStack(waitForMocks),
167
174
  });
168
175
  };
176
+ const isDocument = (thing) => !!thing && typeof thing === "object" && "kind" in thing &&
177
+ thing.kind === "Document";
178
+ const getBareOperationName = (operation) => {
179
+ if (typeof operation === "string")
180
+ return operation;
181
+ if (isDocument(operation))
182
+ return getOperationName(operation) ?? "<unknown>";
183
+ return "<unknown>";
184
+ };
169
185
  /**
170
186
  * A wrapper for `@apollo/client/testing`, this component will assert all
171
187
  * requests have matching mocks and all defined mocks are used unless marked
@@ -190,20 +206,17 @@ export const MockedProvider = ({ mocks, stack: renderStack, children, link: pass
190
206
  !networkError?.message?.includes("No more mocked responses"))
191
207
  return;
192
208
  const { message, stack: altStack } = getErrorMessage(operation, mockLink);
193
- try {
194
- networkError.message = message;
195
- if (altStack) {
196
- networkError.stack = renderStack
197
- ? `${altStack}\nCaused By: ${renderStack}`
198
- : altStack;
199
- }
200
- else if (renderStack)
201
- networkError.stack = renderStack;
202
- fail({ name: "Error", message, stack: networkError.stack });
203
- }
204
- catch {
205
- // fail both throws and marks the test as failed in jest; we only need the latter
209
+ networkError.message = message;
210
+ if (altStack) {
211
+ networkError.stack = renderStack
212
+ ? `${altStack}\nCaused By: ${renderStack}`
213
+ : altStack;
206
214
  }
215
+ else if (renderStack)
216
+ networkError.stack = renderStack;
217
+ networkError.stack = message + "\n" +
218
+ networkError.stack?.split("\n").slice(1).join("\n");
219
+ expect.getState().suppressedErrors.push(networkError);
207
220
  });
208
221
  return ApolloLink.from([
209
222
  errorLoggingLink,
@@ -215,18 +228,12 @@ export const MockedProvider = ({ mocks, stack: renderStack, children, link: pass
215
228
  const oldWarn = console.warn.bind(console.warn);
216
229
  console.warn = (message, operation, ...etc) => {
217
230
  if (typeof message !== "string" ||
218
- !message.match(/Unknown query named.*refetchQueries/))
231
+ !message.match(/Unknown query.*refetchQueries/))
219
232
  return oldWarn(message, operation, ...etc);
220
- try {
221
- fail({
222
- name: "Error",
223
- message: `Expected query ${operation} requested in refetchQueries options.include array to have been mocked`,
224
- stack: renderStack,
225
- });
226
- }
227
- catch {
228
- // eat
229
- }
233
+ const error = new Error(`Expected query ${getBareOperationName(operation)} requested in refetchQueries options.include array to have been mocked`);
234
+ error.stack = error.message + "\n" +
235
+ renderStack?.split("\n").slice(1).join("\n");
236
+ expect.getState().suppressedErrors.push(error);
230
237
  };
231
238
  afterTest.push(() => {
232
239
  console.warn = oldWarn;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphql-data-generator",
3
- "version": "0.4.0-alpha.6",
3
+ "version": "0.4.0-alpha.8",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/voces/graphql-data-generator.git"
package/script/jest.js CHANGED
@@ -34,10 +34,20 @@ const graphql_1 = require("graphql");
34
34
  const jest_diff_1 = require("jest-diff");
35
35
  require("@testing-library/react/dont-cleanup-after-each");
36
36
  const react_2 = require("@testing-library/react");
37
+ const utilities_1 = require("@apollo/client/utilities");
37
38
  let currentSpecResult;
38
- jasmine.getEnv().addReporter({
39
+ globalThis.jasmine
40
+ ?.getEnv()
41
+ .addReporter({
39
42
  specStarted: (result) => currentSpecResult = result,
40
43
  });
44
+ const alreadyFailed = () => {
45
+ if ((currentSpecResult?.failedExpectations.length ?? 0) > 0)
46
+ return true;
47
+ const state = expect.getState();
48
+ return state.assertionCalls > state.numPassingAsserts ||
49
+ state.suppressedErrors.length > 0;
50
+ };
41
51
  let _skipCleanupAfterEach = false;
42
52
  /**
43
53
  * `@testing-library/react` automatically unmounts React trees that were mounted
@@ -75,7 +85,7 @@ const getOperationInfo = (document) => {
75
85
  const getErrorMessage = (operation, mockLink) => {
76
86
  const operationType = getOperationType(operation);
77
87
  const key = JSON.stringify({
78
- query: (0, graphql_1.print)(operation.query),
88
+ query: (0, graphql_1.print)((0, utilities_1.addTypenameToDocument)(operation.query)),
79
89
  });
80
90
  // Bypassing private variable
81
91
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -110,7 +120,7 @@ let _failRefetchWarnings = false;
110
120
  * warnings instead of being treated as standard missing mocks.
111
121
  * This utility converts those warnings into failures and ensures watch queries
112
122
  * are installed for queries with `watch: true`. This must be set when
113
- * `MockProvider` is mounted.
123
+ * `MockedProvider` is mounted.
114
124
  *
115
125
  * This is not required on modern version of `@apollo/client`.
116
126
  */
@@ -143,7 +153,7 @@ const _waitForMocks = async (mocks, { cause, timeout }) => {
143
153
  continue;
144
154
  try {
145
155
  await (0, dom_1.waitFor)(() => {
146
- if (currentSpecResult.failedExpectations.length)
156
+ if (alreadyFailed())
147
157
  return;
148
158
  if (mock.result.mock.calls.length === 0) {
149
159
  throw new Error("");
@@ -163,11 +173,8 @@ const _waitForMocks = async (mocks, { cause, timeout }) => {
163
173
  }
164
174
  else if (cause)
165
175
  err.stack = cause;
166
- fail({
167
- name: "Error",
168
- message: err.message,
169
- stack: err.stack,
170
- });
176
+ err.stack = `${err.message}\n${err.stack?.split("\n").slice(1).join("\n")}`;
177
+ expect.getState().suppressedErrors.push(err);
171
178
  }
172
179
  }
173
180
  };
@@ -181,11 +188,11 @@ const waitForMocks = async (mock = lastMocks.length, { timeout, offset = 0 } = {
181
188
  const matches = lastMocks.map((m, i) => [m, i])
182
189
  .filter(([m]) => getOperationName(m.request.query) === mock);
183
190
  if (matches.length <= offset) {
184
- fail({
185
- name: "Error",
186
- message: `Expected mock ${mock} to have been mocked`,
187
- stack: getStack(exports.waitForMocks),
188
- });
191
+ const err = new Error(`Expected mock ${mock} to have been mocked`);
192
+ Error.captureStackTrace(err, exports.waitForMocks);
193
+ err.stack = err.message + "\n" +
194
+ err.stack?.split("\n").slice(1).join("\n");
195
+ throw err;
189
196
  }
190
197
  expect(matches.length).toBeGreaterThan(offset);
191
198
  mock = matches[offset][1] + 1;
@@ -196,6 +203,15 @@ const waitForMocks = async (mock = lastMocks.length, { timeout, offset = 0 } = {
196
203
  });
197
204
  };
198
205
  exports.waitForMocks = waitForMocks;
206
+ const isDocument = (thing) => !!thing && typeof thing === "object" && "kind" in thing &&
207
+ thing.kind === "Document";
208
+ const getBareOperationName = (operation) => {
209
+ if (typeof operation === "string")
210
+ return operation;
211
+ if (isDocument(operation))
212
+ return getOperationName(operation) ?? "<unknown>";
213
+ return "<unknown>";
214
+ };
199
215
  /**
200
216
  * A wrapper for `@apollo/client/testing`, this component will assert all
201
217
  * requests have matching mocks and all defined mocks are used unless marked
@@ -220,20 +236,17 @@ const MockedProvider = ({ mocks, stack: renderStack, children, link: passedLink,
220
236
  !networkError?.message?.includes("No more mocked responses"))
221
237
  return;
222
238
  const { message, stack: altStack } = getErrorMessage(operation, mockLink);
223
- try {
224
- networkError.message = message;
225
- if (altStack) {
226
- networkError.stack = renderStack
227
- ? `${altStack}\nCaused By: ${renderStack}`
228
- : altStack;
229
- }
230
- else if (renderStack)
231
- networkError.stack = renderStack;
232
- fail({ name: "Error", message, stack: networkError.stack });
233
- }
234
- catch {
235
- // fail both throws and marks the test as failed in jest; we only need the latter
239
+ networkError.message = message;
240
+ if (altStack) {
241
+ networkError.stack = renderStack
242
+ ? `${altStack}\nCaused By: ${renderStack}`
243
+ : altStack;
236
244
  }
245
+ else if (renderStack)
246
+ networkError.stack = renderStack;
247
+ networkError.stack = message + "\n" +
248
+ networkError.stack?.split("\n").slice(1).join("\n");
249
+ expect.getState().suppressedErrors.push(networkError);
237
250
  });
238
251
  return client_1.ApolloLink.from([
239
252
  errorLoggingLink,
@@ -245,18 +258,12 @@ const MockedProvider = ({ mocks, stack: renderStack, children, link: passedLink,
245
258
  const oldWarn = console.warn.bind(console.warn);
246
259
  console.warn = (message, operation, ...etc) => {
247
260
  if (typeof message !== "string" ||
248
- !message.match(/Unknown query named.*refetchQueries/))
261
+ !message.match(/Unknown query.*refetchQueries/))
249
262
  return oldWarn(message, operation, ...etc);
250
- try {
251
- fail({
252
- name: "Error",
253
- message: `Expected query ${operation} requested in refetchQueries options.include array to have been mocked`,
254
- stack: renderStack,
255
- });
256
- }
257
- catch {
258
- // eat
259
- }
263
+ const error = new Error(`Expected query ${getBareOperationName(operation)} requested in refetchQueries options.include array to have been mocked`);
264
+ error.stack = error.message + "\n" +
265
+ renderStack?.split("\n").slice(1).join("\n");
266
+ expect.getState().suppressedErrors.push(error);
260
267
  };
261
268
  afterTest.push(() => {
262
269
  console.warn = oldWarn;
package/types/jest.d.ts CHANGED
@@ -19,7 +19,7 @@ export declare const skipCleanupAfterEach: (value?: boolean) => void;
19
19
  * warnings instead of being treated as standard missing mocks.
20
20
  * This utility converts those warnings into failures and ensures watch queries
21
21
  * are installed for queries with `watch: true`. This must be set when
22
- * `MockProvider` is mounted.
22
+ * `MockedProvider` is mounted.
23
23
  *
24
24
  * This is not required on modern version of `@apollo/client`.
25
25
  */