Skip to content

Commit 5c1085d

Browse files
committed
fix: improve handling of abort
1 parent 0c1a0b8 commit 5c1085d

File tree

2 files changed

+49
-7
lines changed

2 files changed

+49
-7
lines changed

packages/fetch-mock/src/Router.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,19 @@ export default class Router {
167167

168168
const requestBody = request?.body || options?.body;
169169
if (requestBody instanceof ReadableStream) {
170-
requestBody.cancel(error);
170+
if (requestBody.locked) {
171+
requestBody.getReader().cancel(error);
172+
} else {
173+
requestBody.cancel(error);
174+
}
171175
}
172176

173177
if (callLog?.response?.body) {
174-
callLog.response.body.cancel(error);
178+
if (callLog.response.body.locked) {
179+
callLog.response.body.getReader().cancel(error);
180+
} else {
181+
callLog.response.body.cancel(error);
182+
}
175183
}
176184
reject(error);
177185
};
@@ -180,6 +188,7 @@ export default class Router {
180188
}
181189
callLog.signal.addEventListener('abort', abort);
182190
}
191+
183192
if (this.needsToReadBody(request)) {
184193
options.body = await options.body;
185194
}

packages/fetch-mock/src/__tests__/FetchMock/response-negotiation.test.js

+38-5
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ describe('response negotiation', () => {
286286
);
287287
});
288288

289-
it('aborts sending request options body stream', async () => {
289+
it('aborts sending when a body stream is provided as a request option', async () => {
290290
fm.route('*', 200, { delay: 50 });
291291
const body = new ReadableStream();
292292
vi.spyOn(body, 'cancel');
@@ -304,7 +304,7 @@ describe('response negotiation', () => {
304304
);
305305
});
306306

307-
// this doesn't work as the callLog creatde from the request awaits the body
307+
// this doesn't work as the callLog created from the request awaits the body
308308
it.skip('aborts sending request body stream', async () => {
309309
fm.route('*', 200, { delay: 50 });
310310
const body = new ReadableStream();
@@ -323,9 +323,42 @@ describe('response negotiation', () => {
323323
);
324324
});
325325

326-
it.skip('aborts receiving response body stream', async () => {
327-
// so fiddly to implement a test for this. Uses the same mechanism as cancelling request body though
328-
// so I trust that if one works the other does
326+
it.skip('aborts receiving body stream response', async () => {
327+
const controller = new AbortController();
328+
329+
const body = new ReadableStream();
330+
vi.spyOn(body, 'cancel');
331+
fm.route('*', body);
332+
const res = await fm.fetchHandler('http://a.com', {
333+
signal: controller.signal,
334+
});
335+
controller.abort();
336+
await expect(res.bytes()).rejects.toThrowError(
337+
new DOMException('The operation was aborted.', 'AbortError'),
338+
);
339+
340+
expect(body.cancel).toHaveBeenCalledWith(
341+
new DOMException('The operation was aborted.', 'AbortError'),
342+
);
343+
});
344+
345+
it.skip('aborts receiving body stream response when in middle of reading stream', async () => {
346+
const controller = new AbortController();
347+
348+
const body = new ReadableStream();
349+
vi.spyOn(body, 'cancel');
350+
fm.route('*', body);
351+
const res = await fm.fetchHandler('http://a.com', {
352+
signal: controller.signal,
353+
});
354+
const bodyPromise = res.bytes();
355+
controller.abort();
356+
await expect(bodyPromise).rejects.toThrowError(
357+
new DOMException('The operation was aborted.', 'AbortError'),
358+
);
359+
expect(body.cancel).toHaveBeenCalledWith(
360+
new DOMException('The operation was aborted.', 'AbortError'),
361+
);
329362
});
330363

331364
it('go into `done` state even when aborted', async () => {

0 commit comments

Comments
 (0)