@khanacademy/wonder-blocks-testing 7.0.5 → 7.1.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.
- package/CHANGELOG.md +10 -0
- package/dist/es/index.js +160 -62
- package/dist/index.js +326 -167
- package/package.json +1 -1
- package/src/__docs__/exports.respond-with.stories.mdx +17 -6
- package/src/__docs__/exports.settle-controller.stories.mdx +32 -0
- package/src/__docs__/types.mock-response.stories.mdx +6 -2
- package/src/__tests__/mock-requester.test.js +1 -1
- package/src/__tests__/respond-with.test.js +525 -0
- package/src/__tests__/settle-controller.test.js +29 -0
- package/src/__tests__/settle-signal.test.js +105 -0
- package/src/fetch/__tests__/mock-fetch.test.js +1 -1
- package/src/fetch/types.js +1 -1
- package/src/gql/__tests__/mock-gql-fetch.test.js +1 -1
- package/src/gql/__tests__/wb-data-integration.test.js +1 -1
- package/src/gql/types.js +1 -1
- package/src/index.js +3 -2
- package/src/mock-requester.js +2 -3
- package/src/respond-with.js +236 -0
- package/src/settle-controller.js +35 -0
- package/src/settle-signal.js +41 -0
- package/src/types.js +1 -1
- package/src/__tests__/make-mock-response.test.js +0 -460
- package/src/make-mock-response.js +0 -150
|
@@ -16,19 +16,23 @@ interface RespondWith {
|
|
|
16
16
|
/**
|
|
17
17
|
* Rejects with an AbortError to simulate an aborted request.
|
|
18
18
|
*/
|
|
19
|
-
abortedRequest: () => MockResponse<any>;
|
|
19
|
+
abortedRequest: (signal: ?SettleSignal = null) => MockResponse<any>;
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* A non-200 status code with empty text body.
|
|
23
23
|
* Equivalent to calling `ResponseWith.text("", statusCode)`.
|
|
24
24
|
*/
|
|
25
|
-
errorStatusCode: (
|
|
25
|
+
errorStatusCode: (
|
|
26
|
+
statusCode: number,
|
|
27
|
+
signal: ?SettleSignal = null,
|
|
28
|
+
) => MockResponse<any>;
|
|
26
29
|
|
|
27
30
|
/**
|
|
28
31
|
* Response with GraphQL data JSON body and status code 200.
|
|
29
32
|
*/
|
|
30
33
|
graphQLData: <TData: {...}>(
|
|
31
34
|
data: TData,
|
|
35
|
+
signal: ?SettleSignal = null,
|
|
32
36
|
) => MockResponse<GraphQLJson<TData>>;
|
|
33
37
|
|
|
34
38
|
/**
|
|
@@ -36,22 +40,26 @@ interface RespondWith {
|
|
|
36
40
|
*/
|
|
37
41
|
graphQLErrors: (
|
|
38
42
|
errorMessages: $ReadOnlyArray<string>,
|
|
43
|
+
signal: ?SettleSignal = null,
|
|
39
44
|
) => MockResponse<any>;
|
|
40
45
|
|
|
41
46
|
/**
|
|
42
47
|
* Response with JSON body and status code 200.
|
|
43
48
|
*/
|
|
44
|
-
json: <TJson: {...}>(
|
|
49
|
+
json: <TJson: {...}>(
|
|
50
|
+
json: TJson,
|
|
51
|
+
signal: ?SettleSignal = null,
|
|
52
|
+
): MockResponse<TJson>;
|
|
45
53
|
|
|
46
54
|
/**
|
|
47
55
|
* Response body that is valid JSON but not a valid GraphQL response.
|
|
48
56
|
*/
|
|
49
|
-
nonGraphQLBody: () => MockResponse<any>;
|
|
57
|
+
nonGraphQLBody: (signal: ?SettleSignal = null) => MockResponse<any>;
|
|
50
58
|
|
|
51
59
|
/**
|
|
52
60
|
* Rejects with the given error.
|
|
53
61
|
*/
|
|
54
|
-
reject: (error: Error) => MockResponse<any>;
|
|
62
|
+
reject: (error: Error, signal: ?SettleSignal = null) => MockResponse<any>;
|
|
55
63
|
|
|
56
64
|
/**
|
|
57
65
|
* Response with text body and status code.
|
|
@@ -60,14 +68,17 @@ interface RespondWith {
|
|
|
60
68
|
text: <TData = string>(
|
|
61
69
|
text: string,
|
|
62
70
|
statusCode: number = 200,
|
|
71
|
+
signal: ?SettleSignal = null,
|
|
63
72
|
) => MockResponse<TData>;
|
|
64
73
|
|
|
65
74
|
/**
|
|
66
75
|
* Response with body that will not parse as JSON and status code 200.
|
|
67
76
|
*/
|
|
68
|
-
unparseableBody: () => MockResponse<any>;
|
|
77
|
+
unparseableBody: (signal: ?SettleSignal = null) => MockResponse<any>;
|
|
69
78
|
});
|
|
70
79
|
```
|
|
71
80
|
|
|
72
81
|
The `RespondWith` object is a helper for defining mock responses to use with
|
|
73
82
|
mock request methods such as [`mockGqlFetch`](/docs/testing-mocking-exports-mockgqlfetch--page).
|
|
83
|
+
|
|
84
|
+
Each call takes an optional `signal` that can be used to control when the promise generated from the call resolves. See [`SettleController`](/docs/testing-mocking-exports-settlecontroller--page) for related information.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {Meta} from "@storybook/addon-docs";
|
|
2
|
+
|
|
3
|
+
<Meta
|
|
4
|
+
title="Testing / Mocking / Exports / SettleController"
|
|
5
|
+
parameters={{
|
|
6
|
+
chromatic: {
|
|
7
|
+
disableSnapshot: true,
|
|
8
|
+
},
|
|
9
|
+
}}
|
|
10
|
+
/>
|
|
11
|
+
|
|
12
|
+
# SettleController
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
class SettleController {
|
|
16
|
+
/**
|
|
17
|
+
* The signal to pass to the `RespondWith` API.
|
|
18
|
+
*/
|
|
19
|
+
get signal(): SettleSignal;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Settle the signal and therefore any associated responses.
|
|
23
|
+
*
|
|
24
|
+
* @throws {Error} if the signal has already been settled.
|
|
25
|
+
*/
|
|
26
|
+
settle(): void;
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The `SettleController` is used to control the settling of a signal. This is specifically created to work with the [`RespondWith`](/docs/testing-mocking-exports-respondwith--page) API. The `signal` property it exposes can be passed to `RespondWith` methods and then the `settle` method can be invoked to settle the signal, causing the related responses to either reject or resolve as appropriate.
|
|
31
|
+
|
|
32
|
+
This can be useful for tests where the order of operations needs to be controlled in order to verify the expected behaviour of the system under test.
|
|
@@ -12,7 +12,11 @@ import {Meta} from "@storybook/addon-docs";
|
|
|
12
12
|
# MockResponse<>
|
|
13
13
|
|
|
14
14
|
```ts
|
|
15
|
-
|
|
15
|
+
type MockResponse<TJson> = {|
|
|
16
|
+
toPromise: () => Promise<Response>,
|
|
17
|
+
|};
|
|
16
18
|
```
|
|
17
19
|
|
|
18
|
-
This
|
|
20
|
+
This type specifies a mock response. Values of this type are generated by the [`RespondWith`](/docs/testing-mocking-exports-respondwith--page) API. The type parameter is included to allow uses to enforce if they only support `MockResponses` that resolve to a specific JSON pattern.
|
|
21
|
+
|
|
22
|
+
The `toPromise` method can be used to generate a new promise from the mocked response definition. Note that `toPromise` will always generate a new promise and any promise created will settle according to any signal passed to the corresponding `RespondWith` API call.
|
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import {SettleController} from "../settle-controller.js";
|
|
3
|
+
import {RespondWith} from "../respond-with.js";
|
|
4
|
+
|
|
5
|
+
describe("RespondWith", () => {
|
|
6
|
+
describe("#text.toPromise", () => {
|
|
7
|
+
it("should respond with the given text", async () => {
|
|
8
|
+
// Arrange
|
|
9
|
+
|
|
10
|
+
// Act
|
|
11
|
+
const response = await RespondWith.text("SOME TEXT").toPromise();
|
|
12
|
+
const result = await response.text();
|
|
13
|
+
|
|
14
|
+
// Assert
|
|
15
|
+
expect(result).toBe("SOME TEXT");
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("should respond with the given status code", async () => {
|
|
19
|
+
// Arrange
|
|
20
|
+
|
|
21
|
+
// Act
|
|
22
|
+
const result = await RespondWith.text("SOME TEXT", 204).toPromise();
|
|
23
|
+
|
|
24
|
+
// Assert
|
|
25
|
+
expect(result).toHaveProperty("status", 204);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("should not settle if the signal is not raised", async () => {
|
|
29
|
+
// Arrange
|
|
30
|
+
const settleController = new SettleController();
|
|
31
|
+
const settleableResponse = RespondWith.text(
|
|
32
|
+
"SIGNALLED",
|
|
33
|
+
200,
|
|
34
|
+
settleController.signal,
|
|
35
|
+
).toPromise();
|
|
36
|
+
const otherResponse = RespondWith.text("NO SIGNAL").toPromise();
|
|
37
|
+
|
|
38
|
+
// Act
|
|
39
|
+
const firstResponse = await Promise.race([
|
|
40
|
+
settleableResponse,
|
|
41
|
+
otherResponse,
|
|
42
|
+
]);
|
|
43
|
+
const result = await firstResponse.text();
|
|
44
|
+
|
|
45
|
+
// Assert
|
|
46
|
+
expect(result).toBe("NO SIGNAL");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should settle if the signal is raised", async () => {
|
|
50
|
+
// Arrange
|
|
51
|
+
const settleController = new SettleController();
|
|
52
|
+
const settleableResponse = RespondWith.text(
|
|
53
|
+
"SIGNALLED",
|
|
54
|
+
200,
|
|
55
|
+
settleController.signal,
|
|
56
|
+
).toPromise();
|
|
57
|
+
const otherResponse = RespondWith.text("NO SIGNAL").toPromise();
|
|
58
|
+
|
|
59
|
+
// Act
|
|
60
|
+
settleController.settle();
|
|
61
|
+
const firstResponse = await Promise.race([
|
|
62
|
+
settleableResponse,
|
|
63
|
+
otherResponse,
|
|
64
|
+
]);
|
|
65
|
+
const result = await firstResponse.text();
|
|
66
|
+
|
|
67
|
+
// Assert
|
|
68
|
+
expect(result).toBe("SIGNALLED");
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe("#json.toPromise", () => {
|
|
73
|
+
it("should respond with the given json", async () => {
|
|
74
|
+
// Arrange
|
|
75
|
+
|
|
76
|
+
// Act
|
|
77
|
+
const response = await RespondWith.json({
|
|
78
|
+
some: "json",
|
|
79
|
+
}).toPromise();
|
|
80
|
+
const result = await response.json();
|
|
81
|
+
|
|
82
|
+
// Assert
|
|
83
|
+
expect(result).toStrictEqual({some: "json"});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("should not settle if the signal is not raised", async () => {
|
|
87
|
+
// Arrange
|
|
88
|
+
const settleController = new SettleController();
|
|
89
|
+
const settleableResponse = RespondWith.json(
|
|
90
|
+
{result: "SIGNALLED"},
|
|
91
|
+
settleController.signal,
|
|
92
|
+
).toPromise();
|
|
93
|
+
const otherResponse = RespondWith.text("NO SIGNAL").toPromise();
|
|
94
|
+
|
|
95
|
+
// Act
|
|
96
|
+
const firstResponse = await Promise.race([
|
|
97
|
+
settleableResponse,
|
|
98
|
+
otherResponse,
|
|
99
|
+
]);
|
|
100
|
+
const result = await firstResponse.text();
|
|
101
|
+
|
|
102
|
+
// Assert
|
|
103
|
+
expect(result).toBe("NO SIGNAL");
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("should settle if the signal is raised", async () => {
|
|
107
|
+
// Arrange
|
|
108
|
+
const settleController = new SettleController();
|
|
109
|
+
const settleableResponse = RespondWith.json(
|
|
110
|
+
{result: "SIGNALLED"},
|
|
111
|
+
settleController.signal,
|
|
112
|
+
).toPromise();
|
|
113
|
+
const otherResponse = RespondWith.text("NO SIGNAL").toPromise();
|
|
114
|
+
|
|
115
|
+
// Act
|
|
116
|
+
settleController.settle();
|
|
117
|
+
const firstResponse = await Promise.race([
|
|
118
|
+
settleableResponse,
|
|
119
|
+
otherResponse,
|
|
120
|
+
]);
|
|
121
|
+
const result = await firstResponse.json();
|
|
122
|
+
|
|
123
|
+
// Assert
|
|
124
|
+
expect(result).toStrictEqual({result: "SIGNALLED"});
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe("#graphQLData", () => {
|
|
129
|
+
it("should respond with the given GraphQL data", async () => {
|
|
130
|
+
// Arrange
|
|
131
|
+
|
|
132
|
+
// Act
|
|
133
|
+
const response = await RespondWith.graphQLData({
|
|
134
|
+
some: "json",
|
|
135
|
+
}).toPromise();
|
|
136
|
+
const result = await response.json();
|
|
137
|
+
|
|
138
|
+
// Assert
|
|
139
|
+
expect(result).toStrictEqual({data: {some: "json"}});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("should not settle if the signal is not raised", async () => {
|
|
143
|
+
// Arrange
|
|
144
|
+
const settleController = new SettleController();
|
|
145
|
+
const settleableResponse = RespondWith.graphQLData(
|
|
146
|
+
{result: "SIGNALLED"},
|
|
147
|
+
settleController.signal,
|
|
148
|
+
).toPromise();
|
|
149
|
+
const otherResponse = RespondWith.text("NO SIGNAL").toPromise();
|
|
150
|
+
|
|
151
|
+
// Act
|
|
152
|
+
const firstResponse = await Promise.race([
|
|
153
|
+
settleableResponse,
|
|
154
|
+
otherResponse,
|
|
155
|
+
]);
|
|
156
|
+
const result = await firstResponse.text();
|
|
157
|
+
|
|
158
|
+
// Assert
|
|
159
|
+
expect(result).toBe("NO SIGNAL");
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("should settle if the signal is raised", async () => {
|
|
163
|
+
// Arrange
|
|
164
|
+
const settleController = new SettleController();
|
|
165
|
+
const settleableResponse = RespondWith.graphQLData(
|
|
166
|
+
{result: "SIGNALLED"},
|
|
167
|
+
settleController.signal,
|
|
168
|
+
).toPromise();
|
|
169
|
+
const otherResponse = RespondWith.text("NO SIGNAL").toPromise();
|
|
170
|
+
|
|
171
|
+
// Act
|
|
172
|
+
settleController.settle();
|
|
173
|
+
const firstResponse = await Promise.race([
|
|
174
|
+
settleableResponse,
|
|
175
|
+
otherResponse,
|
|
176
|
+
]);
|
|
177
|
+
const result = await firstResponse.json();
|
|
178
|
+
|
|
179
|
+
// Assert
|
|
180
|
+
expect(result).toStrictEqual({data: {result: "SIGNALLED"}});
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
describe("#unparseableBody", () => {
|
|
185
|
+
it("should reject JSON as unparseable", async () => {
|
|
186
|
+
// Arrange
|
|
187
|
+
|
|
188
|
+
// Act
|
|
189
|
+
const response = await RespondWith.unparseableBody().toPromise();
|
|
190
|
+
const act = response.json();
|
|
191
|
+
|
|
192
|
+
// Assert
|
|
193
|
+
await expect(act).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
194
|
+
`"invalid json response body at reason: Unexpected token I in JSON at position 0"`,
|
|
195
|
+
);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("should not settle if the signal is not raised", async () => {
|
|
199
|
+
// Arrange
|
|
200
|
+
const settleController = new SettleController();
|
|
201
|
+
const settleableResponse = RespondWith.unparseableBody(
|
|
202
|
+
settleController.signal,
|
|
203
|
+
).toPromise();
|
|
204
|
+
const otherResponse = RespondWith.text("NO SIGNAL").toPromise();
|
|
205
|
+
|
|
206
|
+
// Act
|
|
207
|
+
const firstResponse = await Promise.race([
|
|
208
|
+
settleableResponse,
|
|
209
|
+
otherResponse,
|
|
210
|
+
]);
|
|
211
|
+
const result = await firstResponse.text();
|
|
212
|
+
|
|
213
|
+
// Assert
|
|
214
|
+
expect(result).toBe("NO SIGNAL");
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it("should settle if the signal is raised", async () => {
|
|
218
|
+
// Arrange
|
|
219
|
+
const settleController = new SettleController();
|
|
220
|
+
const settleableResponse = RespondWith.unparseableBody(
|
|
221
|
+
settleController.signal,
|
|
222
|
+
).toPromise();
|
|
223
|
+
const otherResponse = RespondWith.text("NO SIGNAL").toPromise();
|
|
224
|
+
|
|
225
|
+
// Act
|
|
226
|
+
settleController.settle();
|
|
227
|
+
const firstResponse = await Promise.race([
|
|
228
|
+
settleableResponse,
|
|
229
|
+
otherResponse,
|
|
230
|
+
]);
|
|
231
|
+
const act = firstResponse.json();
|
|
232
|
+
|
|
233
|
+
// Assert
|
|
234
|
+
await expect(act).rejects.toThrowError();
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
describe("#abortedRequest", () => {
|
|
239
|
+
it("should reject with AbortError", async () => {
|
|
240
|
+
// Arrange
|
|
241
|
+
|
|
242
|
+
// Act
|
|
243
|
+
const act = RespondWith.abortedRequest().toPromise();
|
|
244
|
+
|
|
245
|
+
// Assert
|
|
246
|
+
await expect(act).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
247
|
+
`"Mock request aborted"`,
|
|
248
|
+
);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it("should not settle if the signal is not raised", async () => {
|
|
252
|
+
// Arrange
|
|
253
|
+
const settleController = new SettleController();
|
|
254
|
+
const settleableResponse = RespondWith.abortedRequest(
|
|
255
|
+
settleController.signal,
|
|
256
|
+
).toPromise();
|
|
257
|
+
const otherResponse = RespondWith.text("NO SIGNAL").toPromise();
|
|
258
|
+
|
|
259
|
+
// Act
|
|
260
|
+
const act = Promise.race([settleableResponse, otherResponse]);
|
|
261
|
+
|
|
262
|
+
// Assert
|
|
263
|
+
await expect(act).resolves;
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("should settle if the signal is raised", async () => {
|
|
267
|
+
// Arrange
|
|
268
|
+
const settleController = new SettleController();
|
|
269
|
+
const settleableResponse = RespondWith.abortedRequest(
|
|
270
|
+
settleController.signal,
|
|
271
|
+
).toPromise();
|
|
272
|
+
const otherResponse = RespondWith.text("NO SIGNAL").toPromise();
|
|
273
|
+
|
|
274
|
+
// Act
|
|
275
|
+
settleController.settle();
|
|
276
|
+
const act = Promise.race([settleableResponse, otherResponse]);
|
|
277
|
+
|
|
278
|
+
// Assert
|
|
279
|
+
await expect(act).rejects.toThrowError();
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
describe("#reject", () => {
|
|
284
|
+
it("should reject with AbortError", async () => {
|
|
285
|
+
// Arrange
|
|
286
|
+
|
|
287
|
+
// Act
|
|
288
|
+
const act = RespondWith.reject(new Error("BOOM!")).toPromise();
|
|
289
|
+
|
|
290
|
+
// Assert
|
|
291
|
+
await expect(act).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
292
|
+
`"BOOM!"`,
|
|
293
|
+
);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it("should not settle if the signal is not raised", async () => {
|
|
297
|
+
// Arrange
|
|
298
|
+
const settleController = new SettleController();
|
|
299
|
+
const settleableResponse = RespondWith.reject(
|
|
300
|
+
new Error("BOOM!"),
|
|
301
|
+
settleController.signal,
|
|
302
|
+
).toPromise();
|
|
303
|
+
const otherResponse = RespondWith.text("NO SIGNAL").toPromise();
|
|
304
|
+
|
|
305
|
+
// Act
|
|
306
|
+
const act = Promise.race([settleableResponse, otherResponse]);
|
|
307
|
+
|
|
308
|
+
// Assert
|
|
309
|
+
await expect(act).resolves;
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it("should settle if the signal is raised", async () => {
|
|
313
|
+
// Arrange
|
|
314
|
+
const settleController = new SettleController();
|
|
315
|
+
const settleableResponse = RespondWith.reject(
|
|
316
|
+
new Error("BOOM!"),
|
|
317
|
+
settleController.signal,
|
|
318
|
+
).toPromise();
|
|
319
|
+
const otherResponse = RespondWith.text("NO SIGNAL").toPromise();
|
|
320
|
+
|
|
321
|
+
// Act
|
|
322
|
+
settleController.settle();
|
|
323
|
+
const act = Promise.race([settleableResponse, otherResponse]);
|
|
324
|
+
|
|
325
|
+
// Assert
|
|
326
|
+
await expect(act).rejects.toThrowError();
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
describe("#errorStatusCode", () => {
|
|
331
|
+
it("should throw if the status code represents success", () => {
|
|
332
|
+
// Arrange
|
|
333
|
+
|
|
334
|
+
// Act
|
|
335
|
+
const result = () => RespondWith.errorStatusCode(200);
|
|
336
|
+
|
|
337
|
+
// Assert
|
|
338
|
+
expect(result).toThrowErrorMatchingInlineSnapshot(
|
|
339
|
+
`"200 is not a valid error status code"`,
|
|
340
|
+
);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it("should respond with the given status code", async () => {
|
|
344
|
+
// Arrange
|
|
345
|
+
|
|
346
|
+
// Act
|
|
347
|
+
const result = await RespondWith.errorStatusCode(400).toPromise();
|
|
348
|
+
|
|
349
|
+
// Assert
|
|
350
|
+
expect(result).toHaveProperty("status", 400);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it("should not settle if the signal is not raised", async () => {
|
|
354
|
+
// Arrange
|
|
355
|
+
const settleController = new SettleController();
|
|
356
|
+
const settleableResponse = RespondWith.errorStatusCode(
|
|
357
|
+
500,
|
|
358
|
+
settleController.signal,
|
|
359
|
+
).toPromise();
|
|
360
|
+
const otherResponse = RespondWith.text("NO SIGNAL").toPromise();
|
|
361
|
+
|
|
362
|
+
// Act
|
|
363
|
+
const firstResponse = await Promise.race([
|
|
364
|
+
settleableResponse,
|
|
365
|
+
otherResponse,
|
|
366
|
+
]);
|
|
367
|
+
const result = await firstResponse.text();
|
|
368
|
+
|
|
369
|
+
// Assert
|
|
370
|
+
expect(result).toBe("NO SIGNAL");
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it("should settle if the signal is raised", async () => {
|
|
374
|
+
// Arrange
|
|
375
|
+
const settleController = new SettleController();
|
|
376
|
+
const settleableResponse = RespondWith.errorStatusCode(
|
|
377
|
+
500,
|
|
378
|
+
settleController.signal,
|
|
379
|
+
).toPromise();
|
|
380
|
+
const otherResponse = RespondWith.text("NO SIGNAL").toPromise();
|
|
381
|
+
|
|
382
|
+
// Act
|
|
383
|
+
settleController.settle();
|
|
384
|
+
const result = await Promise.race([
|
|
385
|
+
settleableResponse,
|
|
386
|
+
otherResponse,
|
|
387
|
+
]);
|
|
388
|
+
|
|
389
|
+
// Assert
|
|
390
|
+
expect(result).toHaveProperty("status", 500);
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
describe("#nonGraphQLBody", () => {
|
|
395
|
+
it("should respond with valid json", async () => {
|
|
396
|
+
// Arrange
|
|
397
|
+
|
|
398
|
+
// Act
|
|
399
|
+
const response = await RespondWith.nonGraphQLBody().toPromise();
|
|
400
|
+
const act = response.json();
|
|
401
|
+
|
|
402
|
+
// Assert
|
|
403
|
+
await expect(act).resolves.not.toThrow();
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
it("should respond with JSON that is not a valid GraphQL response", async () => {
|
|
407
|
+
// Arrange
|
|
408
|
+
|
|
409
|
+
// Act
|
|
410
|
+
const response = await RespondWith.nonGraphQLBody().toPromise();
|
|
411
|
+
const result = await response.json();
|
|
412
|
+
|
|
413
|
+
// Assert
|
|
414
|
+
expect(result).toMatchInlineSnapshot(`
|
|
415
|
+
Object {
|
|
416
|
+
"that": "is not a valid graphql response",
|
|
417
|
+
"valid": "json",
|
|
418
|
+
}
|
|
419
|
+
`);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it("should not settle if the signal is not raised", async () => {
|
|
423
|
+
// Arrange
|
|
424
|
+
const settleController = new SettleController();
|
|
425
|
+
const settleableResponse = RespondWith.nonGraphQLBody(
|
|
426
|
+
settleController.signal,
|
|
427
|
+
).toPromise();
|
|
428
|
+
const otherResponse = RespondWith.text("NO SIGNAL").toPromise();
|
|
429
|
+
|
|
430
|
+
// Act
|
|
431
|
+
const firstResponse = await Promise.race([
|
|
432
|
+
settleableResponse,
|
|
433
|
+
otherResponse,
|
|
434
|
+
]);
|
|
435
|
+
const result = await firstResponse.text();
|
|
436
|
+
|
|
437
|
+
// Assert
|
|
438
|
+
expect(result).toBe("NO SIGNAL");
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it("should settle if the signal is raised", async () => {
|
|
442
|
+
// Arrange
|
|
443
|
+
const settleController = new SettleController();
|
|
444
|
+
const settleableResponse = RespondWith.nonGraphQLBody(
|
|
445
|
+
settleController.signal,
|
|
446
|
+
).toPromise();
|
|
447
|
+
const otherResponse = RespondWith.text("NO SIGNAL").toPromise();
|
|
448
|
+
|
|
449
|
+
// Act
|
|
450
|
+
settleController.settle();
|
|
451
|
+
const firstResponse = await Promise.race([
|
|
452
|
+
settleableResponse,
|
|
453
|
+
otherResponse,
|
|
454
|
+
]);
|
|
455
|
+
const result = await firstResponse.json();
|
|
456
|
+
|
|
457
|
+
// Assert
|
|
458
|
+
expect(result).toStrictEqual({
|
|
459
|
+
valid: "json",
|
|
460
|
+
that: "is not a valid graphql response",
|
|
461
|
+
});
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
describe("#graphQLErrors", () => {
|
|
466
|
+
it("should respond with the given GraphQL errors", async () => {
|
|
467
|
+
// Arrange
|
|
468
|
+
|
|
469
|
+
// Act
|
|
470
|
+
const response = await RespondWith.graphQLErrors([
|
|
471
|
+
"BOOM!",
|
|
472
|
+
"BANG!",
|
|
473
|
+
]).toPromise();
|
|
474
|
+
const result = await response.json();
|
|
475
|
+
|
|
476
|
+
// Assert
|
|
477
|
+
expect(result).toStrictEqual({
|
|
478
|
+
errors: [{message: "BOOM!"}, {message: "BANG!"}],
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
it("should not settle if the signal is not raised", async () => {
|
|
483
|
+
// Arrange
|
|
484
|
+
const settleController = new SettleController();
|
|
485
|
+
const settleableResponse = RespondWith.graphQLErrors(
|
|
486
|
+
["BOOM!", "BANG!"],
|
|
487
|
+
settleController.signal,
|
|
488
|
+
).toPromise();
|
|
489
|
+
const otherResponse = RespondWith.text("NO SIGNAL").toPromise();
|
|
490
|
+
|
|
491
|
+
// Act
|
|
492
|
+
const firstResponse = await Promise.race([
|
|
493
|
+
settleableResponse,
|
|
494
|
+
otherResponse,
|
|
495
|
+
]);
|
|
496
|
+
const result = await firstResponse.text();
|
|
497
|
+
|
|
498
|
+
// Assert
|
|
499
|
+
expect(result).toBe("NO SIGNAL");
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
it("should settle if the signal is raised", async () => {
|
|
503
|
+
// Arrange
|
|
504
|
+
const settleController = new SettleController();
|
|
505
|
+
const settleableResponse = RespondWith.graphQLErrors(
|
|
506
|
+
["SIGNALLED"],
|
|
507
|
+
settleController.signal,
|
|
508
|
+
).toPromise();
|
|
509
|
+
const otherResponse = RespondWith.text("NO SIGNAL").toPromise();
|
|
510
|
+
|
|
511
|
+
// Act
|
|
512
|
+
settleController.settle();
|
|
513
|
+
const firstResponse = await Promise.race([
|
|
514
|
+
settleableResponse,
|
|
515
|
+
otherResponse,
|
|
516
|
+
]);
|
|
517
|
+
const result = await firstResponse.json();
|
|
518
|
+
|
|
519
|
+
// Assert
|
|
520
|
+
expect(result).toStrictEqual({
|
|
521
|
+
errors: [{message: "SIGNALLED"}],
|
|
522
|
+
});
|
|
523
|
+
});
|
|
524
|
+
});
|
|
525
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import {SettleController} from "../settle-controller.js";
|
|
3
|
+
import {SettleSignal} from "../settle-signal.js";
|
|
4
|
+
|
|
5
|
+
describe("SettleController", () => {
|
|
6
|
+
it("should have a signal", () => {
|
|
7
|
+
// Arrange
|
|
8
|
+
|
|
9
|
+
// Act
|
|
10
|
+
const result = new SettleController();
|
|
11
|
+
|
|
12
|
+
// Assert
|
|
13
|
+
expect(result).toHaveProperty("signal", expect.any(SettleSignal));
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe("#settle", () => {
|
|
17
|
+
it("should settle the signal", () => {
|
|
18
|
+
// Arrange
|
|
19
|
+
const controller = new SettleController();
|
|
20
|
+
const signal = controller.signal;
|
|
21
|
+
|
|
22
|
+
// Act
|
|
23
|
+
controller.settle();
|
|
24
|
+
|
|
25
|
+
// Assert
|
|
26
|
+
expect(signal.settled).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
});
|