@khanacademy/wonder-blocks-testing 3.0.1 → 4.0.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.
@@ -1,65 +1,171 @@
1
1
  // @flow
2
- import {RespondWith, makeGqlMockResponse} from "../make-gql-mock-response.js";
2
+ import {RespondWith, makeMockResponse} from "../make-mock-response.js";
3
3
 
4
4
  describe("RespondWith", () => {
5
- describe("#data", () => {
6
- it("should have type data", () => {
5
+ describe("#text", () => {
6
+ it("should have type text", () => {
7
7
  // Arrange
8
8
 
9
9
  // Act
10
- const result = RespondWith.data({});
10
+ const result = RespondWith.text("SOME TEXT");
11
11
 
12
12
  // Assert
13
- expect(result).toHaveProperty("type", "data");
13
+ expect(result).toHaveProperty("type", "text");
14
14
  });
15
15
 
16
- it("should include the given data", () => {
16
+ it("should provide the given text", () => {
17
+ // Arrange
18
+
19
+ // Act
20
+ const mockResponse = RespondWith.text("SOME TEXT");
21
+ // $FlowIgnore[incompatible-use]
22
+ const result = mockResponse.text;
23
+
24
+ // Assert
25
+ expect(result).toEqual("SOME TEXT");
26
+ });
27
+ });
28
+
29
+ describe("#json", () => {
30
+ it("should have type text", () => {
31
+ // Arrange
32
+
33
+ // Act
34
+ const result = RespondWith.json({});
35
+
36
+ // Assert
37
+ expect(result).toHaveProperty("type", "text");
38
+ });
39
+
40
+ it("should provide the given data in the text", () => {
41
+ // Arrange
42
+ const json = {
43
+ foo: "bar",
44
+ };
45
+
46
+ // Act
47
+ const mockResponse = RespondWith.json(json);
48
+ // $FlowIgnore[incompatible-use]
49
+ const result = mockResponse.text();
50
+
51
+ // Assert
52
+ expect(result).toEqual(JSON.stringify(json));
53
+ });
54
+ });
55
+
56
+ describe("#graphQLData", () => {
57
+ it("should have type text", () => {
58
+ // Arrange
59
+
60
+ // Act
61
+ const result = RespondWith.graphQLData({});
62
+
63
+ // Assert
64
+ expect(result).toHaveProperty("type", "text");
65
+ });
66
+
67
+ it("should provide the given data in the text", () => {
17
68
  // Arrange
18
69
  const data = {
19
70
  foo: "bar",
20
71
  };
21
72
 
22
73
  // Act
23
- const result = RespondWith.data(data);
74
+ const mockResponse = RespondWith.graphQLData(data);
75
+ // $FlowIgnore[incompatible-use]
76
+ const result = mockResponse.text();
24
77
 
25
78
  // Assert
26
- expect(result).toHaveProperty("data", data);
79
+ expect(result).toEqual(JSON.stringify({data}));
27
80
  });
28
81
  });
29
82
 
30
83
  describe("#unparseableBody", () => {
31
- it("should have type parse", () => {
84
+ it("should have type text", () => {
32
85
  // Arrange
33
86
 
34
87
  // Act
35
88
  const result = RespondWith.unparseableBody();
36
89
 
37
90
  // Assert
38
- expect(result).toHaveProperty("type", "parse");
91
+ expect(result).toHaveProperty("type", "text");
92
+ });
93
+
94
+ it("should have text that is unparseable json", () => {
95
+ // Arrange
96
+
97
+ // Act
98
+ const mockResponse = RespondWith.unparseableBody();
99
+ // $FlowIgnore[incompatible-use]
100
+ const underTest = () => JSON.parse(mockResponse.text);
101
+
102
+ // Assert
103
+ expect(underTest).toThrowErrorMatchingInlineSnapshot(
104
+ `"Unexpected token I in JSON at position 0"`,
105
+ );
39
106
  });
40
107
  });
41
108
 
42
109
  describe("#abortedRequest", () => {
43
- it("should have type abort", () => {
110
+ it("should have type reject", () => {
44
111
  // Arrange
45
112
 
46
113
  // Act
47
114
  const result = RespondWith.abortedRequest();
48
115
 
49
116
  // Assert
50
- expect(result).toHaveProperty("type", "abort");
117
+ expect(result).toHaveProperty("type", "reject");
118
+ });
119
+
120
+ it("should provide AbortError", () => {
121
+ // Arrange
122
+
123
+ // Act
124
+ const mockResponse = RespondWith.abortedRequest();
125
+ // $FlowIgnore[incompatible-use]
126
+ const result = mockResponse.error();
127
+
128
+ // Assert
129
+ expect(result).toMatchInlineSnapshot(
130
+ `[AbortError: Mock request aborted]`,
131
+ );
132
+ });
133
+ });
134
+
135
+ describe("#reject", () => {
136
+ it("should have type reject", () => {
137
+ // Arrange
138
+
139
+ // Act
140
+ const result = RespondWith.reject(new Error("BOOM!"));
141
+
142
+ // Assert
143
+ expect(result).toHaveProperty("type", "reject");
144
+ });
145
+
146
+ it("should have the given error", () => {
147
+ // Arrange
148
+ const error = new Error("BOOM!");
149
+
150
+ // Act
151
+ const mockResponse = RespondWith.reject(error);
152
+ // $FlowIgnore[incompatible-use]
153
+ const result = mockResponse.error;
154
+
155
+ // Assert
156
+ expect(result).toBe(error);
51
157
  });
52
158
  });
53
159
 
54
160
  describe("#errorStatusCode", () => {
55
- it("should have type status", () => {
161
+ it("should have type text", () => {
56
162
  // Arrange
57
163
 
58
164
  // Act
59
165
  const result = RespondWith.errorStatusCode(400);
60
166
 
61
167
  // Assert
62
- expect(result).toHaveProperty("type", "status");
168
+ expect(result).toHaveProperty("type", "text");
63
169
  });
64
170
 
65
171
  it("should include the given status code", () => {
@@ -93,19 +199,48 @@ describe("RespondWith", () => {
93
199
  const result = RespondWith.nonGraphQLBody();
94
200
 
95
201
  // Assert
96
- expect(result).toHaveProperty("type", "invalid");
202
+ expect(result).toHaveProperty("type", "text");
203
+ });
204
+
205
+ it("should have text that is valid json", () => {
206
+ // Arrange
207
+
208
+ // Act
209
+ const mockResponse = RespondWith.nonGraphQLBody();
210
+ // $FlowIgnore[incompatible-use]
211
+ const underTest = () => JSON.parse(mockResponse.text());
212
+
213
+ // Assert
214
+ expect(underTest).not.toThrow();
215
+ });
216
+
217
+ it("should have text that is not a valid GraphQL response", () => {
218
+ // Arrange
219
+
220
+ // Act
221
+ const mockResponse = RespondWith.nonGraphQLBody();
222
+ // $FlowIgnore[incompatible-use]
223
+ const result = JSON.parse(mockResponse.text());
224
+
225
+ // Assert
226
+ expect(result).toMatchInlineSnapshot(`
227
+ Object {
228
+ "that": "is not a valid graphql response",
229
+ "valid": "json",
230
+ }
231
+ `);
97
232
  });
98
233
  });
99
234
 
100
235
  describe("#graphQLErrors", () => {
101
- it("should have type graphql", () => {
236
+ it("should have type test", () => {
102
237
  // Arrange
103
238
 
104
239
  // Act
105
240
  const result = RespondWith.graphQLErrors([]);
106
241
 
107
242
  // Assert
108
- expect(result).toHaveProperty("type", "graphql");
243
+ expect(result).toHaveProperty("type", "text");
109
244
  });
110
245
 
111
246
  it("should include the given error messages", () => {
@@ -113,10 +248,23 @@ describe("RespondWith", () => {
113
248
  const errorMessages = ["foo", "bar"];
114
249
 
115
250
  // Act
116
- const result = RespondWith.graphQLErrors(errorMessages);
251
+ const mockResponse = RespondWith.graphQLErrors(errorMessages);
252
+ // $FlowIgnore[incompatible-use]
253
+ const result = JSON.parse(mockResponse.text());
117
254
 
118
255
  // Assert
119
- expect(result).toHaveProperty("errors", errorMessages);
256
+ expect(result).toMatchInlineSnapshot(`
257
+ Object {
258
+ "errors": Array [
259
+ Object {
260
+ "message": "foo",
261
+ },
262
+ Object {
263
+ "message": "bar",
264
+ },
265
+ ],
266
+ }
267
+ `);
120
268
  });
121
269
  });
122
270
  });
@@ -127,7 +275,7 @@ describe("#makeGqlErrorResponse", () => {
127
275
 
128
276
  // Act
129
277
  const result = () =>
130
- makeGqlMockResponse(({type: "NOT A VALID TYPE"}: any));
278
+ makeMockResponse(({type: "NOT A VALID TYPE"}: any));
131
279
 
132
280
  // Assert
133
281
  expect(result).toThrowErrorMatchingInlineSnapshot(
@@ -138,10 +286,10 @@ describe("#makeGqlErrorResponse", () => {
138
286
  describe("data response", () => {
139
287
  it("should resolve to have a successful status code", async () => {
140
288
  // Arrange
141
- const mockResponse = RespondWith.data({});
289
+ const mockResponse = RespondWith.graphQLData({});
142
290
 
143
291
  // Act
144
- const result = await makeGqlMockResponse(mockResponse);
292
+ const result = await makeMockResponse(mockResponse);
145
293
 
146
294
  // Assert
147
295
  expect(result.status).toBe(200);
@@ -152,10 +300,10 @@ describe("#makeGqlErrorResponse", () => {
152
300
  const data = {
153
301
  foo: "bar",
154
302
  };
155
- const mockResponse = RespondWith.data(data);
303
+ const mockResponse = RespondWith.graphQLData(data);
156
304
 
157
305
  // Act
158
- const response = await makeGqlMockResponse(mockResponse);
306
+ const response = await makeMockResponse(mockResponse);
159
307
  const result = await response.text();
160
308
 
161
309
  // Assert
@@ -169,7 +317,7 @@ describe("#makeGqlErrorResponse", () => {
169
317
  const mockResponse = RespondWith.unparseableBody();
170
318
 
171
319
  // Act
172
- const result = await makeGqlMockResponse(mockResponse);
320
+ const result = await makeMockResponse(mockResponse);
173
321
 
174
322
  // Assert
175
323
  expect(result.status).toBe(200);
@@ -180,7 +328,7 @@ describe("#makeGqlErrorResponse", () => {
180
328
  const mockResponse = RespondWith.unparseableBody();
181
329
 
182
330
  // Act
183
- const response = await makeGqlMockResponse(mockResponse);
331
+ const response = await makeMockResponse(mockResponse);
184
332
  const text = await response.text();
185
333
  const act = () => JSON.parse(text);
186
334
 
@@ -195,7 +343,7 @@ describe("#makeGqlErrorResponse", () => {
195
343
  const mockResponse = RespondWith.abortedRequest();
196
344
 
197
345
  // Act
198
- const act = () => makeGqlMockResponse(mockResponse);
346
+ const act = () => makeMockResponse(mockResponse);
199
347
 
200
348
  // Assert
201
349
  await expect(act).rejects.toBeInstanceOf(Error);
@@ -206,20 +354,34 @@ describe("#makeGqlErrorResponse", () => {
206
354
  const mockResponse = RespondWith.abortedRequest();
207
355
 
208
356
  // Act
209
- const act = makeGqlMockResponse(mockResponse);
357
+ const act = makeMockResponse(mockResponse);
210
358
 
211
359
  // Assert
212
360
  await expect(act).rejects.toHaveProperty("name", "AbortError");
213
361
  });
214
362
  });
215
363
 
364
+ describe("rejection", () => {
365
+ it("should reject with error", async () => {
366
+ // Arrange
367
+ const error = new Error("BOOM!");
368
+ const mockResponse = RespondWith.reject(error);
369
+
370
+ // Act
371
+ const act = () => makeMockResponse(mockResponse);
372
+
373
+ // Assert
374
+ await expect(act).rejects.toBe(error);
375
+ });
376
+ });
377
+
216
378
  describe("error status code response", () => {
217
379
  it("should resolve to have the given status code", async () => {
218
380
  // Arrange
219
381
  const mockResponse = RespondWith.errorStatusCode(400);
220
382
 
221
383
  // Act
222
- const result = await makeGqlMockResponse(mockResponse);
384
+ const result = await makeMockResponse(mockResponse);
223
385
 
224
386
  // Assert
225
387
  expect(result.status).toBe(400);
@@ -230,7 +392,7 @@ describe("#makeGqlErrorResponse", () => {
230
392
  const mockResponse = RespondWith.errorStatusCode(400);
231
393
 
232
394
  // Act
233
- const response = await makeGqlMockResponse(mockResponse);
395
+ const response = await makeMockResponse(mockResponse);
234
396
  const text = await response.text();
235
397
  const act = () => JSON.parse(text);
236
398
 
@@ -245,7 +407,7 @@ describe("#makeGqlErrorResponse", () => {
245
407
  const mockResponse = RespondWith.nonGraphQLBody();
246
408
 
247
409
  // Act
248
- const result = await makeGqlMockResponse(mockResponse);
410
+ const result = await makeMockResponse(mockResponse);
249
411
 
250
412
  // Assert
251
413
  expect(result.status).toBe(200);
@@ -256,7 +418,7 @@ describe("#makeGqlErrorResponse", () => {
256
418
  const mockResponse = RespondWith.nonGraphQLBody();
257
419
 
258
420
  // Act
259
- const response = await makeGqlMockResponse(mockResponse);
421
+ const response = await makeMockResponse(mockResponse);
260
422
  const text = await response.text();
261
423
  const result = JSON.parse(text);
262
424
 
@@ -272,7 +434,7 @@ describe("#makeGqlErrorResponse", () => {
272
434
  const mockResponse = RespondWith.graphQLErrors([]);
273
435
 
274
436
  // Act
275
- const result = await makeGqlMockResponse(mockResponse);
437
+ const result = await makeMockResponse(mockResponse);
276
438
 
277
439
  // Assert
278
440
  expect(result.status).toBe(200);
@@ -284,7 +446,7 @@ describe("#makeGqlErrorResponse", () => {
284
446
  const mockResponse = RespondWith.graphQLErrors(errorMessages);
285
447
 
286
448
  // Act
287
- const response = await makeGqlMockResponse(mockResponse);
449
+ const response = await makeMockResponse(mockResponse);
288
450
  const text = await response.text();
289
451
  const result = JSON.parse(text);
290
452
 
@@ -0,0 +1,213 @@
1
+ // @flow
2
+ import {RespondWith} from "../make-mock-response.js";
3
+ import {mockRequester} from "../mock-requester.js";
4
+
5
+ describe("#mockRequester", () => {
6
+ it("should return a function", () => {
7
+ // Arrange
8
+
9
+ // Act
10
+ const result = mockRequester(jest.fn(), jest.fn());
11
+
12
+ // Assert
13
+ expect(result).toBeInstanceOf(Function);
14
+ });
15
+
16
+ it("should provide mockOperation API", () => {
17
+ // Arrange
18
+
19
+ // Act
20
+ const result = mockRequester(jest.fn(), jest.fn());
21
+
22
+ // Assert
23
+ expect(result).toHaveProperty("mockOperation", expect.any(Function));
24
+ });
25
+
26
+ it("should provide mockOperationOnce API", () => {
27
+ // Arrange
28
+
29
+ // Act
30
+ const result = mockRequester(jest.fn(), jest.fn());
31
+
32
+ // Assert
33
+ expect(result).toHaveProperty(
34
+ "mockOperationOnce",
35
+ expect.any(Function),
36
+ );
37
+ });
38
+
39
+ it("should throw with helpful details formatted by operationToString if no matching mock is found", async () => {
40
+ // Arrange
41
+ const mockFn = mockRequester(
42
+ jest.fn(),
43
+ (...args) => `TEST FORMATTING: ${JSON.stringify(args)}`,
44
+ );
45
+
46
+ // Act
47
+ const underTest = mockFn("any", "arguments", {we: {want: 42}});
48
+
49
+ // Assert
50
+ await expect(underTest).rejects.toThrowErrorMatchingInlineSnapshot(`
51
+ "No matching mock response found for request:
52
+ TEST FORMATTING: [\\"any\\",\\"arguments\\",{\\"we\\":{\\"want\\":42}}]"
53
+ `);
54
+ });
55
+
56
+ describe("mockOperation", () => {
57
+ it("should invoke matcher with mock for a request", async () => {
58
+ // Arrange
59
+ const matcher = jest.fn().mockReturnValue(true);
60
+ const operationToString = jest.fn();
61
+ const mockFn = mockRequester(matcher, operationToString);
62
+
63
+ // Act
64
+ mockFn.mockOperation(
65
+ "THE MOCK DESCRIPTION",
66
+ RespondWith.text("TADA!"),
67
+ );
68
+ await mockFn("any", "arguments", {we: {want: 42}});
69
+
70
+ // Assert
71
+ expect(matcher).toHaveBeenCalledWith(
72
+ "THE MOCK DESCRIPTION",
73
+ "any",
74
+ "arguments",
75
+ {
76
+ we: {want: 42},
77
+ },
78
+ );
79
+ });
80
+
81
+ it("should return mocked operation response if matcher returns true", async () => {
82
+ // Arrange
83
+ const matcher = jest.fn().mockReturnValue(true);
84
+ const operationToString = jest.fn();
85
+ const mockFn = mockRequester(matcher, operationToString);
86
+
87
+ // Act
88
+ mockFn.mockOperation(
89
+ "THE MOCK DESCRIPTION",
90
+ RespondWith.text("TADA!"),
91
+ );
92
+ const response = await mockFn("DO SOMETHING");
93
+ const result = response.text();
94
+
95
+ // Assert
96
+ await expect(result).resolves.toBe("TADA!");
97
+ });
98
+
99
+ it("should skip mock if matcher returns false and try more mocks", async () => {
100
+ // Arrange
101
+ const matcher = jest
102
+ .fn()
103
+ .mockReturnValueOnce(false)
104
+ .mockReturnValueOnce(true);
105
+ const operationToString = jest.fn();
106
+ const mockFn = mockRequester(matcher, operationToString);
107
+
108
+ // Act
109
+ mockFn.mockOperation(
110
+ "THE MOCK DESCRIPTION 1",
111
+ RespondWith.text("ONE"),
112
+ );
113
+ mockFn.mockOperation(
114
+ "THE MOCK DESCRIPTION 2",
115
+ RespondWith.text("TWO"),
116
+ );
117
+ const response = await mockFn("DO SOMETHING");
118
+ const result = response.text();
119
+
120
+ // Assert
121
+ await expect(result).resolves.toBe("TWO");
122
+ });
123
+ });
124
+
125
+ describe("mockOperationOnce", () => {
126
+ it("should invoke matcher with mock for a request", async () => {
127
+ // Arrange
128
+ const matcher = jest.fn().mockReturnValue(true);
129
+ const operationToString = jest.fn();
130
+ const mockFn = mockRequester(matcher, operationToString);
131
+
132
+ // Act
133
+ mockFn.mockOperationOnce(
134
+ "THE MOCK DESCRIPTION",
135
+ RespondWith.text("TADA!"),
136
+ );
137
+ await mockFn("any", "arguments", {we: {want: 42}});
138
+
139
+ // Assert
140
+ expect(matcher).toHaveBeenCalledWith(
141
+ "THE MOCK DESCRIPTION",
142
+ "any",
143
+ "arguments",
144
+ {
145
+ we: {want: 42},
146
+ },
147
+ );
148
+ });
149
+
150
+ it("should match once", async () => {
151
+ // Arrange
152
+ const matcher = jest.fn().mockReturnValue(true);
153
+ const operationToString = jest.fn();
154
+ const mockFn = mockRequester(matcher, operationToString);
155
+
156
+ // Act
157
+ mockFn.mockOperationOnce(
158
+ "THE MOCK DESCRIPTION",
159
+ RespondWith.text("TADA!"),
160
+ );
161
+ const response = await mockFn("DO SOMETHING");
162
+ const result = response.text();
163
+
164
+ // Assert
165
+ await expect(result).resolves.toBe("TADA!");
166
+ });
167
+
168
+ it("should only match once", async () => {
169
+ // Arrange
170
+ const matcher = jest.fn().mockReturnValue(true);
171
+ const operationToString = jest.fn();
172
+ const mockFn = mockRequester(matcher, operationToString);
173
+
174
+ // Act
175
+ mockFn.mockOperationOnce(
176
+ "THE MOCK DESCRIPTION",
177
+ RespondWith.text("TADA!"),
178
+ );
179
+ const result = Promise.all([
180
+ mockFn("DO SOMETHING"),
181
+ mockFn("DO SOMETHING"),
182
+ ]);
183
+
184
+ // Assert
185
+ await expect(result).rejects.toThrowError();
186
+ });
187
+
188
+ it("should skip mock if matcher returns false and try more mocks", async () => {
189
+ // Arrange
190
+ const matcher = jest
191
+ .fn()
192
+ .mockReturnValueOnce(false)
193
+ .mockReturnValueOnce(true);
194
+ const operationToString = jest.fn();
195
+ const mockFn = mockRequester(matcher, operationToString);
196
+
197
+ // Act
198
+ mockFn.mockOperationOnce(
199
+ "THE MOCK DESCRIPTION 1",
200
+ RespondWith.text("ONE"),
201
+ );
202
+ mockFn.mockOperationOnce(
203
+ "THE MOCK DESCRIPTION 2",
204
+ RespondWith.text("TWO"),
205
+ );
206
+ const response = await mockFn("DO SOMETHING");
207
+ const result = response.text();
208
+
209
+ // Assert
210
+ await expect(result).resolves.toBe("TWO");
211
+ });
212
+ });
213
+ });
@@ -0,0 +1,47 @@
1
+ // flow
2
+ import * as wst from "@khanacademy/wonder-stuff-testing";
3
+
4
+ describe("ResponseImpl", () => {
5
+ const globalResponse = globalThis.Response;
6
+
7
+ beforeEach(() => {
8
+ if (globalResponse) {
9
+ delete globalThis.Response;
10
+ }
11
+ });
12
+
13
+ afterEach(() => {
14
+ if (globalResponse) {
15
+ globalThis.Response = globalResponse;
16
+ } else {
17
+ delete globalThis.Response;
18
+ }
19
+ });
20
+
21
+ it("should use Response from node-fetch if Response does not exist", () => {
22
+ // Arrange
23
+
24
+ // Act
25
+ const {ResponseImpl: result, NodeFetchResponse} =
26
+ wst.jest.isolateModules(() => ({
27
+ ResponseImpl: require("../response-impl.js").ResponseImpl,
28
+ NodeFetchResponse: require("node-fetch").Response,
29
+ }));
30
+
31
+ // Assert
32
+ expect(result).toBe(NodeFetchResponse);
33
+ });
34
+
35
+ it("should return the existing Response type if it exists", () => {
36
+ // Arrange
37
+ globalThis.Response = class CustomResponse {};
38
+
39
+ // Act
40
+ const result = wst.jest.isolateModules(
41
+ () => require("../response-impl.js").ResponseImpl,
42
+ );
43
+
44
+ // Assert
45
+ expect(result).toBe(globalThis.Response);
46
+ });
47
+ });
@@ -0,0 +1,29 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`#mockFetch should reject with a useful error when there are no matching mocks for %s 1`] = `
4
+ "No matching mock response found for request:
5
+ Input: http://example.com/foo
6
+ Options: None"
7
+ `;
8
+
9
+ exports[`#mockFetch should reject with a useful error when there are no matching mocks for %s 2`] = `
10
+ "No matching mock response found for request:
11
+ Input: \\"http://example.com/foo\\"
12
+ Options: {
13
+ \\"method\\": \\"GET\\"
14
+ }"
15
+ `;
16
+
17
+ exports[`#mockFetch should reject with a useful error when there are no matching mocks for %s 3`] = `
18
+ "No matching mock response found for request:
19
+ Input: {
20
+ \\"size\\": 0,
21
+ \\"timeout\\": 0,
22
+ \\"follow\\": 20,
23
+ \\"compress\\": true,
24
+ \\"counter\\": 0
25
+ }
26
+ Options: {
27
+ \\"method\\": \\"POST\\"
28
+ }"
29
+ `;