@mswjs/interceptors 0.24.0 → 0.25.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.
Files changed (30) hide show
  1. package/lib/browser/{chunk-B74BGPYH.mjs → chunk-ANLPTCZ5.mjs} +10 -1
  2. package/lib/browser/{chunk-2TENISKM.js → chunk-DBFLI5DJ.js} +27 -5
  3. package/lib/browser/{chunk-UGP4JOAM.js → chunk-OSIUQA4X.js} +10 -1
  4. package/lib/browser/{chunk-LHYX2GOM.mjs → chunk-VMXB5F2J.mjs} +27 -5
  5. package/lib/browser/interceptors/XMLHttpRequest/index.js +2 -2
  6. package/lib/browser/interceptors/XMLHttpRequest/index.mjs +1 -1
  7. package/lib/browser/interceptors/fetch/index.js +2 -2
  8. package/lib/browser/interceptors/fetch/index.mjs +1 -1
  9. package/lib/browser/presets/browser.js +4 -4
  10. package/lib/browser/presets/browser.mjs +2 -2
  11. package/lib/node/RemoteHttpInterceptor.js +4 -4
  12. package/lib/node/RemoteHttpInterceptor.mjs +2 -2
  13. package/lib/node/{chunk-OOSIWXHX.js → chunk-3MYUI4B2.js} +9 -1
  14. package/lib/node/{chunk-YCEMBJEM.js → chunk-E6YC337Q.js} +10 -1
  15. package/lib/node/{chunk-UWSK5F3S.mjs → chunk-HSCXCLVT.mjs} +10 -1
  16. package/lib/node/{chunk-PSIO3L7D.mjs → chunk-OL7OR4RL.mjs} +9 -1
  17. package/lib/node/interceptors/ClientRequest/index.js +2 -2
  18. package/lib/node/interceptors/ClientRequest/index.mjs +1 -1
  19. package/lib/node/interceptors/XMLHttpRequest/index.js +2 -2
  20. package/lib/node/interceptors/XMLHttpRequest/index.mjs +1 -1
  21. package/lib/node/interceptors/fetch/index.js +27 -5
  22. package/lib/node/interceptors/fetch/index.mjs +27 -5
  23. package/lib/node/presets/node.js +4 -4
  24. package/lib/node/presets/node.mjs +2 -2
  25. package/package.json +6 -6
  26. package/src/interceptors/ClientRequest/NodeClientRequest.ts +18 -1
  27. package/src/interceptors/ClientRequest/index.test.ts +28 -0
  28. package/src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts +9 -0
  29. package/src/interceptors/XMLHttpRequest/utils/createResponse.ts +15 -1
  30. package/src/interceptors/fetch/index.ts +45 -5
@@ -202,8 +202,10 @@ function parseJson(data) {
202
202
  }
203
203
 
204
204
  // src/interceptors/XMLHttpRequest/utils/createResponse.ts
205
+ var statusCodesWithoutBody = [204, 205, 304];
205
206
  function createResponse(request, body) {
206
- return new Response(body, {
207
+ const responseBodyOrNull = statusCodesWithoutBody.includes(request.status) ? null : body;
208
+ return new Response(responseBodyOrNull, {
207
209
  status: request.status,
208
210
  statusText: request.statusText,
209
211
  headers: createHeadersFromXMLHttpReqestHeaders(
@@ -692,6 +694,13 @@ function createXMLHttpRequestProxy({
692
694
  mockedResponse.status,
693
695
  mockedResponse.statusText
694
696
  );
697
+ if (mockedResponse.type === "error") {
698
+ this.logger.info(
699
+ "received a network error response, rejecting the request promise..."
700
+ );
701
+ requestController.errorWith(new TypeError("Network error"));
702
+ return;
703
+ }
695
704
  return requestController.respondWith(mockedResponse);
696
705
  }
697
706
  this.logger.info(
@@ -8,6 +8,7 @@ var _chunkPCFJD76Xjs = require('./chunk-PCFJD76X.js');
8
8
  var _chunk4CFMDU7Zjs = require('./chunk-4CFMDU7Z.js');
9
9
 
10
10
  // src/interceptors/fetch/index.ts
11
+ var _deferredpromise = require('@open-draft/deferred-promise');
11
12
  var _outvariant = require('outvariant');
12
13
  var _until = require('@open-draft/until');
13
14
  var _FetchInterceptor = class extends _chunk4CFMDU7Zjs.Interceptor {
@@ -38,27 +39,43 @@ var _FetchInterceptor = class extends _chunk4CFMDU7Zjs.Interceptor {
38
39
  requestId
39
40
  });
40
41
  this.logger.info("awaiting for the mocked response...");
42
+ const signal = interactiveRequest.signal;
43
+ const requestAborted = new (0, _deferredpromise.DeferredPromise)();
44
+ signal.addEventListener(
45
+ "abort",
46
+ () => {
47
+ requestAborted.reject(signal.reason);
48
+ },
49
+ { once: true }
50
+ );
41
51
  const resolverResult = await _until.until.call(void 0, async () => {
42
- await this.emitter.untilIdle(
52
+ const allListenersResolved = this.emitter.untilIdle(
43
53
  "request",
44
54
  ({ args: [{ requestId: pendingRequestId }] }) => {
45
55
  return pendingRequestId === requestId;
46
56
  }
47
57
  );
58
+ await Promise.race([requestAborted, allListenersResolved]);
48
59
  this.logger.info("all request listeners have been resolved!");
49
60
  const [mockedResponse2] = await interactiveRequest.respondWith.invoked();
50
61
  this.logger.info("event.respondWith called with:", mockedResponse2);
51
62
  return mockedResponse2;
52
63
  });
64
+ if (requestAborted.state === "rejected") {
65
+ return Promise.reject(requestAborted.rejectionReason);
66
+ }
53
67
  if (resolverResult.error) {
54
- const error = Object.assign(new TypeError("Failed to fetch"), {
55
- cause: resolverResult.error
56
- });
57
- return Promise.reject(error);
68
+ return Promise.reject(createNetworkError(resolverResult.error));
58
69
  }
59
70
  const mockedResponse = resolverResult.data;
60
71
  if (mockedResponse && !((_a = request.signal) == null ? void 0 : _a.aborted)) {
61
72
  this.logger.info("received mocked response:", mockedResponse);
73
+ if (mockedResponse.type === "error") {
74
+ this.logger.info(
75
+ "received a network error response, rejecting the request promise..."
76
+ );
77
+ return Promise.reject(createNetworkError(mockedResponse));
78
+ }
62
79
  const responseClone = mockedResponse.clone();
63
80
  this.emitter.emit("response", {
64
81
  response: responseClone,
@@ -107,6 +124,11 @@ var _FetchInterceptor = class extends _chunk4CFMDU7Zjs.Interceptor {
107
124
  };
108
125
  var FetchInterceptor = _FetchInterceptor;
109
126
  FetchInterceptor.symbol = Symbol("fetch");
127
+ function createNetworkError(cause) {
128
+ return Object.assign(new TypeError("Failed to fetch"), {
129
+ cause
130
+ });
131
+ }
110
132
 
111
133
 
112
134
 
@@ -202,8 +202,10 @@ function parseJson(data) {
202
202
  }
203
203
 
204
204
  // src/interceptors/XMLHttpRequest/utils/createResponse.ts
205
+ var statusCodesWithoutBody = [204, 205, 304];
205
206
  function createResponse(request, body) {
206
- return new Response(body, {
207
+ const responseBodyOrNull = statusCodesWithoutBody.includes(request.status) ? null : body;
208
+ return new Response(responseBodyOrNull, {
207
209
  status: request.status,
208
210
  statusText: request.statusText,
209
211
  headers: createHeadersFromXMLHttpReqestHeaders(
@@ -692,6 +694,13 @@ function createXMLHttpRequestProxy({
692
694
  mockedResponse.status,
693
695
  mockedResponse.statusText
694
696
  );
697
+ if (mockedResponse.type === "error") {
698
+ this.logger.info(
699
+ "received a network error response, rejecting the request promise..."
700
+ );
701
+ requestController.errorWith(new TypeError("Network error"));
702
+ return;
703
+ }
695
704
  return requestController.respondWith(mockedResponse);
696
705
  }
697
706
  this.logger.info(
@@ -8,6 +8,7 @@ import {
8
8
  } from "./chunk-GXJLJMOT.mjs";
9
9
 
10
10
  // src/interceptors/fetch/index.ts
11
+ import { DeferredPromise } from "@open-draft/deferred-promise";
11
12
  import { invariant } from "outvariant";
12
13
  import { until } from "@open-draft/until";
13
14
  var _FetchInterceptor = class extends Interceptor {
@@ -38,27 +39,43 @@ var _FetchInterceptor = class extends Interceptor {
38
39
  requestId
39
40
  });
40
41
  this.logger.info("awaiting for the mocked response...");
42
+ const signal = interactiveRequest.signal;
43
+ const requestAborted = new DeferredPromise();
44
+ signal.addEventListener(
45
+ "abort",
46
+ () => {
47
+ requestAborted.reject(signal.reason);
48
+ },
49
+ { once: true }
50
+ );
41
51
  const resolverResult = await until(async () => {
42
- await this.emitter.untilIdle(
52
+ const allListenersResolved = this.emitter.untilIdle(
43
53
  "request",
44
54
  ({ args: [{ requestId: pendingRequestId }] }) => {
45
55
  return pendingRequestId === requestId;
46
56
  }
47
57
  );
58
+ await Promise.race([requestAborted, allListenersResolved]);
48
59
  this.logger.info("all request listeners have been resolved!");
49
60
  const [mockedResponse2] = await interactiveRequest.respondWith.invoked();
50
61
  this.logger.info("event.respondWith called with:", mockedResponse2);
51
62
  return mockedResponse2;
52
63
  });
64
+ if (requestAborted.state === "rejected") {
65
+ return Promise.reject(requestAborted.rejectionReason);
66
+ }
53
67
  if (resolverResult.error) {
54
- const error = Object.assign(new TypeError("Failed to fetch"), {
55
- cause: resolverResult.error
56
- });
57
- return Promise.reject(error);
68
+ return Promise.reject(createNetworkError(resolverResult.error));
58
69
  }
59
70
  const mockedResponse = resolverResult.data;
60
71
  if (mockedResponse && !((_a = request.signal) == null ? void 0 : _a.aborted)) {
61
72
  this.logger.info("received mocked response:", mockedResponse);
73
+ if (mockedResponse.type === "error") {
74
+ this.logger.info(
75
+ "received a network error response, rejecting the request promise..."
76
+ );
77
+ return Promise.reject(createNetworkError(mockedResponse));
78
+ }
62
79
  const responseClone = mockedResponse.clone();
63
80
  this.emitter.emit("response", {
64
81
  response: responseClone,
@@ -107,6 +124,11 @@ var _FetchInterceptor = class extends Interceptor {
107
124
  };
108
125
  var FetchInterceptor = _FetchInterceptor;
109
126
  FetchInterceptor.symbol = Symbol("fetch");
127
+ function createNetworkError(cause) {
128
+ return Object.assign(new TypeError("Failed to fetch"), {
129
+ cause
130
+ });
131
+ }
110
132
 
111
133
  export {
112
134
  FetchInterceptor
@@ -1,9 +1,9 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
- var _chunkUGP4JOAMjs = require('../../chunk-UGP4JOAM.js');
3
+ var _chunkOSIUQA4Xjs = require('../../chunk-OSIUQA4X.js');
4
4
  require('../../chunk-3LFH2WCF.js');
5
5
  require('../../chunk-PCFJD76X.js');
6
6
  require('../../chunk-4CFMDU7Z.js');
7
7
 
8
8
 
9
- exports.XMLHttpRequestInterceptor = _chunkUGP4JOAMjs.XMLHttpRequestInterceptor;
9
+ exports.XMLHttpRequestInterceptor = _chunkOSIUQA4Xjs.XMLHttpRequestInterceptor;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  XMLHttpRequestInterceptor
3
- } from "../../chunk-B74BGPYH.mjs";
3
+ } from "../../chunk-ANLPTCZ5.mjs";
4
4
  import "../../chunk-7II4SWKS.mjs";
5
5
  import "../../chunk-RT3ATOJH.mjs";
6
6
  import "../../chunk-GXJLJMOT.mjs";
@@ -1,8 +1,8 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
- var _chunk2TENISKMjs = require('../../chunk-2TENISKM.js');
3
+ var _chunkDBFLI5DJjs = require('../../chunk-DBFLI5DJ.js');
4
4
  require('../../chunk-PCFJD76X.js');
5
5
  require('../../chunk-4CFMDU7Z.js');
6
6
 
7
7
 
8
- exports.FetchInterceptor = _chunk2TENISKMjs.FetchInterceptor;
8
+ exports.FetchInterceptor = _chunkDBFLI5DJjs.FetchInterceptor;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  FetchInterceptor
3
- } from "../../chunk-LHYX2GOM.mjs";
3
+ } from "../../chunk-VMXB5F2J.mjs";
4
4
  import "../../chunk-RT3ATOJH.mjs";
5
5
  import "../../chunk-GXJLJMOT.mjs";
6
6
  export {
@@ -1,17 +1,17 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
- var _chunkUGP4JOAMjs = require('../chunk-UGP4JOAM.js');
3
+ var _chunkOSIUQA4Xjs = require('../chunk-OSIUQA4X.js');
4
4
  require('../chunk-3LFH2WCF.js');
5
5
 
6
6
 
7
- var _chunk2TENISKMjs = require('../chunk-2TENISKM.js');
7
+ var _chunkDBFLI5DJjs = require('../chunk-DBFLI5DJ.js');
8
8
  require('../chunk-PCFJD76X.js');
9
9
  require('../chunk-4CFMDU7Z.js');
10
10
 
11
11
  // src/presets/browser.ts
12
12
  var browser_default = [
13
- new (0, _chunk2TENISKMjs.FetchInterceptor)(),
14
- new (0, _chunkUGP4JOAMjs.XMLHttpRequestInterceptor)()
13
+ new (0, _chunkDBFLI5DJjs.FetchInterceptor)(),
14
+ new (0, _chunkOSIUQA4Xjs.XMLHttpRequestInterceptor)()
15
15
  ];
16
16
 
17
17
 
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  XMLHttpRequestInterceptor
3
- } from "../chunk-B74BGPYH.mjs";
3
+ } from "../chunk-ANLPTCZ5.mjs";
4
4
  import "../chunk-7II4SWKS.mjs";
5
5
  import {
6
6
  FetchInterceptor
7
- } from "../chunk-LHYX2GOM.mjs";
7
+ } from "../chunk-VMXB5F2J.mjs";
8
8
  import "../chunk-RT3ATOJH.mjs";
9
9
  import "../chunk-GXJLJMOT.mjs";
10
10
 
@@ -3,10 +3,10 @@
3
3
  var _chunkMVPEJK4Vjs = require('./chunk-MVPEJK4V.js');
4
4
 
5
5
 
6
- var _chunkOOSIWXHXjs = require('./chunk-OOSIWXHX.js');
6
+ var _chunk3MYUI4B2js = require('./chunk-3MYUI4B2.js');
7
7
 
8
8
 
9
- var _chunkYCEMBJEMjs = require('./chunk-YCEMBJEM.js');
9
+ var _chunkE6YC337Qjs = require('./chunk-E6YC337Q.js');
10
10
  require('./chunk-3LFH2WCF.js');
11
11
  require('./chunk-VQ4DZOBB.js');
12
12
 
@@ -22,8 +22,8 @@ var RemoteHttpInterceptor = class extends _chunkMVPEJK4Vjs.BatchInterceptor {
22
22
  super({
23
23
  name: "remote-interceptor",
24
24
  interceptors: [
25
- new (0, _chunkOOSIWXHXjs.ClientRequestInterceptor)(),
26
- new (0, _chunkYCEMBJEMjs.XMLHttpRequestInterceptor)()
25
+ new (0, _chunk3MYUI4B2js.ClientRequestInterceptor)(),
26
+ new (0, _chunkE6YC337Qjs.XMLHttpRequestInterceptor)()
27
27
  ]
28
28
  });
29
29
  }
@@ -3,10 +3,10 @@ import {
3
3
  } from "./chunk-VS3GJPUE.mjs";
4
4
  import {
5
5
  ClientRequestInterceptor
6
- } from "./chunk-PSIO3L7D.mjs";
6
+ } from "./chunk-OL7OR4RL.mjs";
7
7
  import {
8
8
  XMLHttpRequestInterceptor
9
- } from "./chunk-UWSK5F3S.mjs";
9
+ } from "./chunk-HSCXCLVT.mjs";
10
10
  import "./chunk-7II4SWKS.mjs";
11
11
  import "./chunk-GFH37L5D.mjs";
12
12
  import {
@@ -249,8 +249,16 @@ var _NodeClientRequest = class extends _http.ClientRequest {
249
249
  }
250
250
  const mockedResponse = resolverResult.data;
251
251
  if (mockedResponse) {
252
- const responseClone = mockedResponse.clone();
253
252
  this.logger.info("received mocked response:", mockedResponse);
253
+ if (mockedResponse.type === "error") {
254
+ this.logger.info(
255
+ "received network error response, aborting request..."
256
+ );
257
+ this.emit("error", new TypeError("Network error"));
258
+ this.terminate();
259
+ return this;
260
+ }
261
+ const responseClone = mockedResponse.clone();
254
262
  this.responseSource = "mock";
255
263
  this.respondWith(mockedResponse);
256
264
  this.logger.info(
@@ -204,8 +204,10 @@ function parseJson(data) {
204
204
  }
205
205
 
206
206
  // src/interceptors/XMLHttpRequest/utils/createResponse.ts
207
+ var statusCodesWithoutBody = [204, 205, 304];
207
208
  function createResponse(request, body) {
208
- return new Response(body, {
209
+ const responseBodyOrNull = statusCodesWithoutBody.includes(request.status) ? null : body;
210
+ return new Response(responseBodyOrNull, {
209
211
  status: request.status,
210
212
  statusText: request.statusText,
211
213
  headers: createHeadersFromXMLHttpReqestHeaders(
@@ -694,6 +696,13 @@ function createXMLHttpRequestProxy({
694
696
  mockedResponse.status,
695
697
  mockedResponse.statusText
696
698
  );
699
+ if (mockedResponse.type === "error") {
700
+ this.logger.info(
701
+ "received a network error response, rejecting the request promise..."
702
+ );
703
+ requestController.errorWith(new TypeError("Network error"));
704
+ return;
705
+ }
697
706
  return requestController.respondWith(mockedResponse);
698
707
  }
699
708
  this.logger.info(
@@ -204,8 +204,10 @@ function parseJson(data) {
204
204
  }
205
205
 
206
206
  // src/interceptors/XMLHttpRequest/utils/createResponse.ts
207
+ var statusCodesWithoutBody = [204, 205, 304];
207
208
  function createResponse(request, body) {
208
- return new Response(body, {
209
+ const responseBodyOrNull = statusCodesWithoutBody.includes(request.status) ? null : body;
210
+ return new Response(responseBodyOrNull, {
209
211
  status: request.status,
210
212
  statusText: request.statusText,
211
213
  headers: createHeadersFromXMLHttpReqestHeaders(
@@ -694,6 +696,13 @@ function createXMLHttpRequestProxy({
694
696
  mockedResponse.status,
695
697
  mockedResponse.statusText
696
698
  );
699
+ if (mockedResponse.type === "error") {
700
+ this.logger.info(
701
+ "received a network error response, rejecting the request promise..."
702
+ );
703
+ requestController.errorWith(new TypeError("Network error"));
704
+ return;
705
+ }
697
706
  return requestController.respondWith(mockedResponse);
698
707
  }
699
708
  this.logger.info(
@@ -249,8 +249,16 @@ var _NodeClientRequest = class extends ClientRequest {
249
249
  }
250
250
  const mockedResponse = resolverResult.data;
251
251
  if (mockedResponse) {
252
- const responseClone = mockedResponse.clone();
253
252
  this.logger.info("received mocked response:", mockedResponse);
253
+ if (mockedResponse.type === "error") {
254
+ this.logger.info(
255
+ "received network error response, aborting request..."
256
+ );
257
+ this.emit("error", new TypeError("Network error"));
258
+ this.terminate();
259
+ return this;
260
+ }
261
+ const responseClone = mockedResponse.clone();
254
262
  this.responseSource = "mock";
255
263
  this.respondWith(mockedResponse);
256
264
  this.logger.info(
@@ -1,8 +1,8 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
- var _chunkOOSIWXHXjs = require('../../chunk-OOSIWXHX.js');
3
+ var _chunk3MYUI4B2js = require('../../chunk-3MYUI4B2.js');
4
4
  require('../../chunk-ZJOF5MEZ.js');
5
5
  require('../../chunk-XYZRP5S2.js');
6
6
 
7
7
 
8
- exports.ClientRequestInterceptor = _chunkOOSIWXHXjs.ClientRequestInterceptor;
8
+ exports.ClientRequestInterceptor = _chunk3MYUI4B2js.ClientRequestInterceptor;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ClientRequestInterceptor
3
- } from "../../chunk-PSIO3L7D.mjs";
3
+ } from "../../chunk-OL7OR4RL.mjs";
4
4
  import "../../chunk-STA6QBYM.mjs";
5
5
  import "../../chunk-RGYCLCLK.mjs";
6
6
  export {
@@ -1,10 +1,10 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
- var _chunkYCEMBJEMjs = require('../../chunk-YCEMBJEM.js');
3
+ var _chunkE6YC337Qjs = require('../../chunk-E6YC337Q.js');
4
4
  require('../../chunk-3LFH2WCF.js');
5
5
  require('../../chunk-VQ4DZOBB.js');
6
6
  require('../../chunk-ZJOF5MEZ.js');
7
7
  require('../../chunk-XYZRP5S2.js');
8
8
 
9
9
 
10
- exports.XMLHttpRequestInterceptor = _chunkYCEMBJEMjs.XMLHttpRequestInterceptor;
10
+ exports.XMLHttpRequestInterceptor = _chunkE6YC337Qjs.XMLHttpRequestInterceptor;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  XMLHttpRequestInterceptor
3
- } from "../../chunk-UWSK5F3S.mjs";
3
+ } from "../../chunk-HSCXCLVT.mjs";
4
4
  import "../../chunk-7II4SWKS.mjs";
5
5
  import "../../chunk-GFH37L5D.mjs";
6
6
  import "../../chunk-STA6QBYM.mjs";
@@ -10,6 +10,7 @@ var _chunkZJOF5MEZjs = require('../../chunk-ZJOF5MEZ.js');
10
10
  var _chunkXYZRP5S2js = require('../../chunk-XYZRP5S2.js');
11
11
 
12
12
  // src/interceptors/fetch/index.ts
13
+ var _deferredpromise = require('@open-draft/deferred-promise');
13
14
  var _outvariant = require('outvariant');
14
15
  var _until = require('@open-draft/until');
15
16
  var _FetchInterceptor = class extends _chunkXYZRP5S2js.Interceptor {
@@ -40,27 +41,43 @@ var _FetchInterceptor = class extends _chunkXYZRP5S2js.Interceptor {
40
41
  requestId
41
42
  });
42
43
  this.logger.info("awaiting for the mocked response...");
44
+ const signal = interactiveRequest.signal;
45
+ const requestAborted = new (0, _deferredpromise.DeferredPromise)();
46
+ signal.addEventListener(
47
+ "abort",
48
+ () => {
49
+ requestAborted.reject(signal.reason);
50
+ },
51
+ { once: true }
52
+ );
43
53
  const resolverResult = await _until.until.call(void 0, async () => {
44
- await this.emitter.untilIdle(
54
+ const allListenersResolved = this.emitter.untilIdle(
45
55
  "request",
46
56
  ({ args: [{ requestId: pendingRequestId }] }) => {
47
57
  return pendingRequestId === requestId;
48
58
  }
49
59
  );
60
+ await Promise.race([requestAborted, allListenersResolved]);
50
61
  this.logger.info("all request listeners have been resolved!");
51
62
  const [mockedResponse2] = await interactiveRequest.respondWith.invoked();
52
63
  this.logger.info("event.respondWith called with:", mockedResponse2);
53
64
  return mockedResponse2;
54
65
  });
66
+ if (requestAborted.state === "rejected") {
67
+ return Promise.reject(requestAborted.rejectionReason);
68
+ }
55
69
  if (resolverResult.error) {
56
- const error = Object.assign(new TypeError("Failed to fetch"), {
57
- cause: resolverResult.error
58
- });
59
- return Promise.reject(error);
70
+ return Promise.reject(createNetworkError(resolverResult.error));
60
71
  }
61
72
  const mockedResponse = resolverResult.data;
62
73
  if (mockedResponse && !((_a = request.signal) == null ? void 0 : _a.aborted)) {
63
74
  this.logger.info("received mocked response:", mockedResponse);
75
+ if (mockedResponse.type === "error") {
76
+ this.logger.info(
77
+ "received a network error response, rejecting the request promise..."
78
+ );
79
+ return Promise.reject(createNetworkError(mockedResponse));
80
+ }
64
81
  const responseClone = mockedResponse.clone();
65
82
  this.emitter.emit("response", {
66
83
  response: responseClone,
@@ -109,6 +126,11 @@ var _FetchInterceptor = class extends _chunkXYZRP5S2js.Interceptor {
109
126
  };
110
127
  var FetchInterceptor = _FetchInterceptor;
111
128
  FetchInterceptor.symbol = Symbol("fetch");
129
+ function createNetworkError(cause) {
130
+ return Object.assign(new TypeError("Failed to fetch"), {
131
+ cause
132
+ });
133
+ }
112
134
 
113
135
 
114
136
  exports.FetchInterceptor = FetchInterceptor;
@@ -10,6 +10,7 @@ import {
10
10
  } from "../../chunk-RGYCLCLK.mjs";
11
11
 
12
12
  // src/interceptors/fetch/index.ts
13
+ import { DeferredPromise } from "@open-draft/deferred-promise";
13
14
  import { invariant } from "outvariant";
14
15
  import { until } from "@open-draft/until";
15
16
  var _FetchInterceptor = class extends Interceptor {
@@ -40,27 +41,43 @@ var _FetchInterceptor = class extends Interceptor {
40
41
  requestId
41
42
  });
42
43
  this.logger.info("awaiting for the mocked response...");
44
+ const signal = interactiveRequest.signal;
45
+ const requestAborted = new DeferredPromise();
46
+ signal.addEventListener(
47
+ "abort",
48
+ () => {
49
+ requestAborted.reject(signal.reason);
50
+ },
51
+ { once: true }
52
+ );
43
53
  const resolverResult = await until(async () => {
44
- await this.emitter.untilIdle(
54
+ const allListenersResolved = this.emitter.untilIdle(
45
55
  "request",
46
56
  ({ args: [{ requestId: pendingRequestId }] }) => {
47
57
  return pendingRequestId === requestId;
48
58
  }
49
59
  );
60
+ await Promise.race([requestAborted, allListenersResolved]);
50
61
  this.logger.info("all request listeners have been resolved!");
51
62
  const [mockedResponse2] = await interactiveRequest.respondWith.invoked();
52
63
  this.logger.info("event.respondWith called with:", mockedResponse2);
53
64
  return mockedResponse2;
54
65
  });
66
+ if (requestAborted.state === "rejected") {
67
+ return Promise.reject(requestAborted.rejectionReason);
68
+ }
55
69
  if (resolverResult.error) {
56
- const error = Object.assign(new TypeError("Failed to fetch"), {
57
- cause: resolverResult.error
58
- });
59
- return Promise.reject(error);
70
+ return Promise.reject(createNetworkError(resolverResult.error));
60
71
  }
61
72
  const mockedResponse = resolverResult.data;
62
73
  if (mockedResponse && !((_a = request.signal) == null ? void 0 : _a.aborted)) {
63
74
  this.logger.info("received mocked response:", mockedResponse);
75
+ if (mockedResponse.type === "error") {
76
+ this.logger.info(
77
+ "received a network error response, rejecting the request promise..."
78
+ );
79
+ return Promise.reject(createNetworkError(mockedResponse));
80
+ }
64
81
  const responseClone = mockedResponse.clone();
65
82
  this.emitter.emit("response", {
66
83
  response: responseClone,
@@ -109,6 +126,11 @@ var _FetchInterceptor = class extends Interceptor {
109
126
  };
110
127
  var FetchInterceptor = _FetchInterceptor;
111
128
  FetchInterceptor.symbol = Symbol("fetch");
129
+ function createNetworkError(cause) {
130
+ return Object.assign(new TypeError("Failed to fetch"), {
131
+ cause
132
+ });
133
+ }
112
134
  export {
113
135
  FetchInterceptor
114
136
  };
@@ -1,9 +1,9 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
- var _chunkOOSIWXHXjs = require('../chunk-OOSIWXHX.js');
3
+ var _chunk3MYUI4B2js = require('../chunk-3MYUI4B2.js');
4
4
 
5
5
 
6
- var _chunkYCEMBJEMjs = require('../chunk-YCEMBJEM.js');
6
+ var _chunkE6YC337Qjs = require('../chunk-E6YC337Q.js');
7
7
  require('../chunk-3LFH2WCF.js');
8
8
  require('../chunk-VQ4DZOBB.js');
9
9
  require('../chunk-ZJOF5MEZ.js');
@@ -11,8 +11,8 @@ require('../chunk-XYZRP5S2.js');
11
11
 
12
12
  // src/presets/node.ts
13
13
  var node_default = [
14
- new (0, _chunkOOSIWXHXjs.ClientRequestInterceptor)(),
15
- new (0, _chunkYCEMBJEMjs.XMLHttpRequestInterceptor)()
14
+ new (0, _chunk3MYUI4B2js.ClientRequestInterceptor)(),
15
+ new (0, _chunkE6YC337Qjs.XMLHttpRequestInterceptor)()
16
16
  ];
17
17
 
18
18
 
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  ClientRequestInterceptor
3
- } from "../chunk-PSIO3L7D.mjs";
3
+ } from "../chunk-OL7OR4RL.mjs";
4
4
  import {
5
5
  XMLHttpRequestInterceptor
6
- } from "../chunk-UWSK5F3S.mjs";
6
+ } from "../chunk-HSCXCLVT.mjs";
7
7
  import "../chunk-7II4SWKS.mjs";
8
8
  import "../chunk-GFH37L5D.mjs";
9
9
  import "../chunk-STA6QBYM.mjs";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mswjs/interceptors",
3
3
  "description": "Low-level HTTP/HTTPS/XHR/fetch request interception library.",
4
- "version": "0.24.0",
4
+ "version": "0.25.0",
5
5
  "main": "./lib/node/index.js",
6
6
  "module": "./lib/node/index.mjs",
7
7
  "types": "./lib/node/index.d.ts",
@@ -45,10 +45,10 @@
45
45
  },
46
46
  "./RemoteHttpInterceptor": {
47
47
  "browser": null,
48
- "types": "./lib/node/interceptors/RemoteHttpInterceptor.d.ts",
49
- "require": "./lib/node/interceptors/RemoteHttpInterceptor.js",
50
- "import": "./lib/node/interceptors/RemoteHttpInterceptor.mjs",
51
- "default": "./lib/node/interceptors/RemoteHttpInterceptor.js"
48
+ "types": "./lib/node/RemoteHttpInterceptor.d.ts",
49
+ "require": "./lib/node/RemoteHttpInterceptor.js",
50
+ "import": "./lib/node/RemoteHttpInterceptor.mjs",
51
+ "default": "./lib/node/RemoteHttpInterceptor.js"
52
52
  },
53
53
  "./presets/node": {
54
54
  "browser": null,
@@ -88,7 +88,7 @@
88
88
  "@commitlint/cli": "^16.0.2",
89
89
  "@commitlint/config-conventional": "^16.0.0",
90
90
  "@open-draft/test-server": "^0.5.1",
91
- "@ossjs/release": "^0.5.1",
91
+ "@ossjs/release": "^0.7.2",
92
92
  "@playwright/test": "^1.37.1",
93
93
  "@types/cors": "^2.8.12",
94
94
  "@types/express": "^4.17.13",
@@ -212,9 +212,26 @@ export class NodeClientRequest extends ClientRequest {
212
212
  const mockedResponse = resolverResult.data
213
213
 
214
214
  if (mockedResponse) {
215
+ this.logger.info('received mocked response:', mockedResponse)
216
+
217
+ // Handle mocked "Response.error" network error responses.
218
+ if (mockedResponse.type === 'error') {
219
+ this.logger.info(
220
+ 'received network error response, aborting request...'
221
+ )
222
+
223
+ /**
224
+ * There is no standardized error format for network errors
225
+ * in Node.js. Instead, emit a generic TypeError.
226
+ */
227
+ this.emit('error', new TypeError('Network error'))
228
+ this.terminate()
229
+
230
+ return this
231
+ }
232
+
215
233
  const responseClone = mockedResponse.clone()
216
234
 
217
- this.logger.info('received mocked response:', mockedResponse)
218
235
  this.responseSource = 'mock'
219
236
 
220
237
  this.respondWith(mockedResponse)
@@ -3,6 +3,7 @@ import http from 'http'
3
3
  import { HttpServer } from '@open-draft/test-server/http'
4
4
  import { DeferredPromise } from '@open-draft/deferred-promise'
5
5
  import { ClientRequestInterceptor } from '.'
6
+ import { sleep } from '../../../test/helpers'
6
7
 
7
8
  const httpServer = new HttpServer((app) => {
8
9
  app.get('/', (_req, res) => {
@@ -55,3 +56,30 @@ it('forbids calling "respondWith" multiple times for the same request', async ()
55
56
  expect(response.statusCode).toBe(200)
56
57
  expect(response.statusMessage).toBe('')
57
58
  })
59
+
60
+
61
+ it('abort the request if the abort signal is emitted', async () => {
62
+ const requestUrl = httpServer.http.url('/')
63
+
64
+ const requestEmitted = new DeferredPromise<void>()
65
+ interceptor.on('request', async function delayedResponse({ request }) {
66
+ requestEmitted.resolve()
67
+ await sleep(10000)
68
+ request.respondWith(new Response())
69
+ })
70
+
71
+ const abortController = new AbortController()
72
+ const request = http.get(requestUrl, { signal: abortController.signal })
73
+
74
+ await requestEmitted
75
+
76
+ abortController.abort()
77
+
78
+ const requestAborted = new DeferredPromise<void>()
79
+ request.on('error', function(err) {
80
+ expect(err.name).toEqual('AbortError')
81
+ requestAborted.resolve()
82
+ })
83
+
84
+ await requestAborted
85
+ })
@@ -104,6 +104,15 @@ export function createXMLHttpRequestProxy({
104
104
  mockedResponse.statusText
105
105
  )
106
106
 
107
+ if (mockedResponse.type === 'error') {
108
+ this.logger.info(
109
+ 'received a network error response, rejecting the request promise...'
110
+ )
111
+
112
+ requestController.errorWith(new TypeError('Network error'))
113
+ return
114
+ }
115
+
107
116
  return requestController.respondWith(mockedResponse)
108
117
  }
109
118
 
@@ -1,3 +1,5 @@
1
+ const statusCodesWithoutBody = [204, 205, 304]
2
+
1
3
  /**
2
4
  * Creates a Fetch API `Response` instance from the given
3
5
  * `XMLHttpRequest` instance and a response body.
@@ -6,7 +8,19 @@ export function createResponse(
6
8
  request: XMLHttpRequest,
7
9
  body: BodyInit | null
8
10
  ): Response {
9
- return new Response(body, {
11
+ /**
12
+ * Handle XMLHttpRequest responses that must have null as the
13
+ * response body when represented using Fetch API Response.
14
+ * XMLHttpRequest response will always have an empty string
15
+ * as the "request.response" in those cases, resulting in an error
16
+ * when constructing a Response instance.
17
+ * @see https://github.com/mswjs/interceptors/issues/379
18
+ */
19
+ const responseBodyOrNull = statusCodesWithoutBody.includes(request.status)
20
+ ? null
21
+ : body
22
+
23
+ return new Response(responseBodyOrNull, {
10
24
  status: request.status,
11
25
  statusText: request.statusText,
12
26
  headers: createHeadersFromXMLHttpReqestHeaders(
@@ -1,3 +1,4 @@
1
+ import { DeferredPromise } from '@open-draft/deferred-promise'
1
2
  import { invariant } from 'outvariant'
2
3
  import { until } from '@open-draft/until'
3
4
  import { HttpRequestEventMap, IS_PATCHED_MODULE } from '../../glossary'
@@ -46,13 +47,27 @@ export class FetchInterceptor extends Interceptor<HttpRequestEventMap> {
46
47
 
47
48
  this.logger.info('awaiting for the mocked response...')
48
49
 
50
+ const signal = interactiveRequest.signal
51
+ const requestAborted = new DeferredPromise()
52
+
53
+ signal.addEventListener(
54
+ 'abort',
55
+ () => {
56
+ requestAborted.reject(signal.reason)
57
+ },
58
+ { once: true }
59
+ )
60
+
49
61
  const resolverResult = await until(async () => {
50
- await this.emitter.untilIdle(
62
+ const allListenersResolved = this.emitter.untilIdle(
51
63
  'request',
52
64
  ({ args: [{ requestId: pendingRequestId }] }) => {
53
65
  return pendingRequestId === requestId
54
66
  }
55
67
  )
68
+
69
+ await Promise.race([requestAborted, allListenersResolved])
70
+
56
71
  this.logger.info('all request listeners have been resolved!')
57
72
 
58
73
  const [mockedResponse] = await interactiveRequest.respondWith.invoked()
@@ -61,17 +76,36 @@ export class FetchInterceptor extends Interceptor<HttpRequestEventMap> {
61
76
  return mockedResponse
62
77
  })
63
78
 
79
+ if (requestAborted.state === 'rejected') {
80
+ return Promise.reject(requestAborted.rejectionReason)
81
+ }
82
+
64
83
  if (resolverResult.error) {
65
- const error = Object.assign(new TypeError('Failed to fetch'), {
66
- cause: resolverResult.error,
67
- })
68
- return Promise.reject(error)
84
+ return Promise.reject(createNetworkError(resolverResult.error))
69
85
  }
70
86
 
71
87
  const mockedResponse = resolverResult.data
72
88
 
73
89
  if (mockedResponse && !request.signal?.aborted) {
74
90
  this.logger.info('received mocked response:', mockedResponse)
91
+
92
+ // Reject the request Promise on mocked "Response.error" responses.
93
+ if (mockedResponse.type === 'error') {
94
+ this.logger.info(
95
+ 'received a network error response, rejecting the request promise...'
96
+ )
97
+
98
+ /**
99
+ * Set the cause of the request promise rejection to the
100
+ * network error Response instance. This different from Undici.
101
+ * Undici will forward the "response.error" custom property
102
+ * as the rejection reason but for "Response.error()" static method
103
+ * "response.error" will equal to undefined, making "cause" an empty Error.
104
+ * @see https://github.com/nodejs/undici/blob/83cb522ae0157a19d149d72c7d03d46e34510d0a/lib/fetch/response.js#L344
105
+ */
106
+ return Promise.reject(createNetworkError(mockedResponse))
107
+ }
108
+
75
109
  const responseClone = mockedResponse.clone()
76
110
 
77
111
  this.emitter.emit('response', {
@@ -131,3 +165,9 @@ export class FetchInterceptor extends Interceptor<HttpRequestEventMap> {
131
165
  })
132
166
  }
133
167
  }
168
+
169
+ function createNetworkError(cause: unknown) {
170
+ return Object.assign(new TypeError('Failed to fetch'), {
171
+ cause,
172
+ })
173
+ }