@mswjs/interceptors 0.21.1 → 0.22.1

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 (57) hide show
  1. package/ClientRequest/package.json +6 -0
  2. package/RemoteHttpInterceptor/package.json +6 -0
  3. package/XMLHttpRequest/package.json +6 -0
  4. package/fetch/package.json +6 -0
  5. package/lib/browser/interceptors/XMLHttpRequest/index.js +0 -4
  6. package/lib/browser/interceptors/XMLHttpRequest/index.mjs +0 -4
  7. package/lib/node/RemoteHttpInterceptor.js +11 -15
  8. package/lib/node/RemoteHttpInterceptor.mjs +5 -9
  9. package/lib/node/{chunk-ZSI7MX3V.mjs → chunk-37CATPNG.mjs} +0 -34
  10. package/lib/node/{chunk-ZWCZGO3W.mjs → chunk-CYWTKHFI.mjs} +2 -7
  11. package/lib/node/{chunk-CIN5URNI.mjs → chunk-G6ZTHYZQ.mjs} +1 -1
  12. package/lib/node/{chunk-HDUJCCWF.js → chunk-GGD5JOGB.js} +9 -14
  13. package/lib/node/{chunk-JISWS3Y3.mjs → chunk-KZEQH4YW.mjs} +1 -1
  14. package/lib/node/{chunk-UVNTVJHD.js → chunk-PRX3F52M.js} +17 -17
  15. package/lib/node/{chunk-KZJG2UW7.js → chunk-Q56TMOP5.js} +2 -2
  16. package/lib/node/{chunk-6GWWOJ23.js → chunk-SNNL2EXF.js} +3 -3
  17. package/lib/node/{chunk-Z5JAVEOB.mjs → chunk-SWJ33XIS.mjs} +10 -10
  18. package/lib/node/{chunk-QMIXLBOU.js → chunk-WWHITCCI.js} +2 -36
  19. package/lib/node/index.js +4 -4
  20. package/lib/node/index.mjs +3 -3
  21. package/lib/node/interceptors/ClientRequest/index.js +3 -4
  22. package/lib/node/interceptors/ClientRequest/index.mjs +2 -3
  23. package/lib/node/interceptors/XMLHttpRequest/index.js +4 -5
  24. package/lib/node/interceptors/XMLHttpRequest/index.mjs +3 -4
  25. package/lib/node/interceptors/fetch/index.js +2 -2
  26. package/lib/node/interceptors/fetch/index.mjs +1 -1
  27. package/package.json +16 -10
  28. package/src/BatchInterceptor.test.ts +8 -7
  29. package/src/Interceptor.test.ts +6 -5
  30. package/src/RemoteHttpInterceptor.ts +0 -1
  31. package/src/interceptors/ClientRequest/NodeClientRequest.test.ts +107 -85
  32. package/src/interceptors/ClientRequest/NodeClientRequest.ts +14 -6
  33. package/src/interceptors/ClientRequest/index.test.ts +20 -8
  34. package/src/interceptors/ClientRequest/utils/cloneIncomingMessage.test.ts +2 -1
  35. package/src/interceptors/ClientRequest/utils/createRequest.test.ts +1 -0
  36. package/src/interceptors/ClientRequest/utils/createRequest.ts +0 -1
  37. package/src/interceptors/ClientRequest/utils/createResponse.test.ts +1 -3
  38. package/src/interceptors/ClientRequest/utils/createResponse.ts +0 -1
  39. package/src/interceptors/ClientRequest/utils/getIncomingMessageBody.test.ts +5 -4
  40. package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.test.ts +19 -18
  41. package/src/interceptors/ClientRequest/utils/normalizeClientRequestEndArgs.test.ts +6 -5
  42. package/src/interceptors/ClientRequest/utils/normalizeClientRequestWriteArgs.test.ts +5 -4
  43. package/src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts +0 -1
  44. package/src/interceptors/XMLHttpRequest/utils/concateArrayBuffer.test.ts +1 -3
  45. package/src/interceptors/XMLHttpRequest/utils/createEvent.test.ts +4 -5
  46. package/src/interceptors/XMLHttpRequest/utils/createResponse.ts +0 -1
  47. package/src/interceptors/fetch/index.ts +1 -2
  48. package/src/utils/AsyncEventEmitter.test.ts +8 -7
  49. package/src/utils/bufferUtils.test.ts +1 -0
  50. package/src/utils/cloneObject.test.ts +6 -5
  51. package/src/utils/getCleanUrl.test.ts +5 -4
  52. package/src/utils/getUrlByRequestOptions.test.ts +11 -10
  53. package/src/utils/getUrlByRequestOptions.ts +14 -1
  54. package/src/utils/isObject.test.ts +4 -3
  55. package/src/utils/parseJson.test.ts +3 -2
  56. package/lib/node/chunk-6V3JXLBF.js +0 -6093
  57. package/lib/node/chunk-NNVTJLQA.mjs +0 -6093
@@ -1,9 +1,8 @@
1
1
  import {
2
2
  ClientRequestInterceptor
3
- } from "../../chunk-Z5JAVEOB.mjs";
4
- import "../../chunk-NNVTJLQA.mjs";
3
+ } from "../../chunk-SWJ33XIS.mjs";
5
4
  import "../../chunk-STA6QBYM.mjs";
6
- import "../../chunk-ZSI7MX3V.mjs";
5
+ import "../../chunk-37CATPNG.mjs";
7
6
  export {
8
7
  ClientRequestInterceptor
9
8
  };
@@ -1,11 +1,10 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
- var _chunkHDUJCCWFjs = require('../../chunk-HDUJCCWF.js');
4
- require('../../chunk-6GWWOJ23.js');
5
- require('../../chunk-6V3JXLBF.js');
3
+ var _chunkGGD5JOGBjs = require('../../chunk-GGD5JOGB.js');
4
+ require('../../chunk-SNNL2EXF.js');
6
5
  require('../../chunk-VQ4DZOBB.js');
7
6
  require('../../chunk-ZJOF5MEZ.js');
8
- require('../../chunk-QMIXLBOU.js');
7
+ require('../../chunk-WWHITCCI.js');
9
8
 
10
9
 
11
- exports.XMLHttpRequestInterceptor = _chunkHDUJCCWFjs.XMLHttpRequestInterceptor;
10
+ exports.XMLHttpRequestInterceptor = _chunkGGD5JOGBjs.XMLHttpRequestInterceptor;
@@ -1,11 +1,10 @@
1
1
  import {
2
2
  XMLHttpRequestInterceptor
3
- } from "../../chunk-ZWCZGO3W.mjs";
4
- import "../../chunk-CIN5URNI.mjs";
5
- import "../../chunk-NNVTJLQA.mjs";
3
+ } from "../../chunk-CYWTKHFI.mjs";
4
+ import "../../chunk-G6ZTHYZQ.mjs";
6
5
  import "../../chunk-GFH37L5D.mjs";
7
6
  import "../../chunk-STA6QBYM.mjs";
8
- import "../../chunk-ZSI7MX3V.mjs";
7
+ import "../../chunk-37CATPNG.mjs";
9
8
  export {
10
9
  XMLHttpRequestInterceptor
11
10
  };
@@ -7,11 +7,11 @@ var _chunkVQ4DZOBBjs = require('../../chunk-VQ4DZOBB.js');
7
7
  var _chunkZJOF5MEZjs = require('../../chunk-ZJOF5MEZ.js');
8
8
 
9
9
 
10
- var _chunkQMIXLBOUjs = require('../../chunk-QMIXLBOU.js');
10
+ var _chunkWWHITCCIjs = require('../../chunk-WWHITCCI.js');
11
11
 
12
12
  // src/interceptors/fetch/index.ts
13
13
  var _outvariant = require('outvariant');
14
- var _FetchInterceptor = class extends _chunkQMIXLBOUjs.Interceptor {
14
+ var _FetchInterceptor = class extends _chunkWWHITCCIjs.Interceptor {
15
15
  constructor() {
16
16
  super(_FetchInterceptor.symbol);
17
17
  }
@@ -7,7 +7,7 @@ import {
7
7
  } from "../../chunk-STA6QBYM.mjs";
8
8
  import {
9
9
  Interceptor
10
- } from "../../chunk-ZSI7MX3V.mjs";
10
+ } from "../../chunk-37CATPNG.mjs";
11
11
 
12
12
  // src/interceptors/fetch/index.ts
13
13
  import { invariant } from "outvariant";
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.21.1",
4
+ "version": "0.22.1",
5
5
  "main": "./lib/node/index.js",
6
6
  "module": "./lib/node/index.mjs",
7
7
  "types": "./lib/node/index.d.ts",
@@ -54,15 +54,15 @@
54
54
  "author": "Artem Zakharchenko",
55
55
  "license": "MIT",
56
56
  "engines": {
57
- "node": ">=14"
57
+ "node": ">=18"
58
58
  },
59
59
  "scripts": {
60
60
  "start": "tsc --build -w",
61
- "test": "yarn test:internal && yarn test:integration",
62
- "test:internal": "jest",
61
+ "test": "yarn test:unit && yarn test:integration",
62
+ "test:unit": "vitest run",
63
63
  "test:integration": "yarn test:integration:node && yarn test:integration:browser",
64
- "test:integration:node": "jest --c test/jest.node.config.js --runInBand",
65
- "test:integration:browser": "jest --c test/jest.browser.config.js",
64
+ "test:integration:node": "vitest run -c test/vitest.config.js",
65
+ "test:integration:browser": "playwright test -c test/playwright.config.ts",
66
66
  "clean": "rimraf lib",
67
67
  "build": "yarn clean && cross-env NODE_ENV=production tsup --splitting",
68
68
  "prepare": "yarn simple-git-hooks init",
@@ -72,7 +72,11 @@
72
72
  "files": [
73
73
  "lib",
74
74
  "README.md",
75
- "src"
75
+ "src",
76
+ "ClientRequest",
77
+ "fetch",
78
+ "RemoteHttpInterceptor",
79
+ "XMLHttpRequest"
76
80
  ],
77
81
  "repository": {
78
82
  "type": "git",
@@ -83,7 +87,7 @@
83
87
  "@commitlint/config-conventional": "^16.0.0",
84
88
  "@open-draft/test-server": "^0.4.2",
85
89
  "@ossjs/release": "^0.4.0",
86
- "@remix-run/web-fetch": "^4.3.1",
90
+ "@playwright/test": "^1.31.1",
87
91
  "@types/cors": "^2.8.12",
88
92
  "@types/express": "^4.17.13",
89
93
  "@types/express-rate-limit": "^6.0.0",
@@ -104,7 +108,7 @@
104
108
  "got": "^11.8.3",
105
109
  "jest": "^27.4.3",
106
110
  "node-fetch": "2.6.7",
107
- "page-with": "^0.6.0",
111
+ "playwright": "^1.31.1",
108
112
  "rimraf": "^3.0.2",
109
113
  "simple-git-hooks": "^2.7.0",
110
114
  "superagent": "^6.1.0",
@@ -112,8 +116,10 @@
112
116
  "ts-jest": "^27.1.1",
113
117
  "tsup": "^6.5.0",
114
118
  "typescript": "^4.9.4",
119
+ "vitest": "^0.28.5",
115
120
  "wait-for-expect": "^3.0.2",
116
- "web-encoding": "^1.1.5"
121
+ "web-encoding": "^1.1.5",
122
+ "webpack-http-server": "^0.5.0"
117
123
  },
118
124
  "dependencies": {
119
125
  "@open-draft/deferred-promise": "^2.1.0",
@@ -1,8 +1,9 @@
1
+ import { vi, it, expect, afterEach } from 'vitest'
1
2
  import { Interceptor } from './Interceptor'
2
3
  import { BatchInterceptor } from './BatchInterceptor'
3
4
 
4
5
  afterEach(() => {
5
- jest.resetAllMocks()
6
+ vi.resetAllMocks()
6
7
  })
7
8
 
8
9
  it('applies child interceptors', () => {
@@ -28,8 +29,8 @@ it('applies child interceptors', () => {
28
29
  interceptors: [instances.primary, instances.secondary],
29
30
  })
30
31
 
31
- const primaryApplySpy = jest.spyOn(instances.primary, 'apply')
32
- const secondaryApplySpy = jest.spyOn(instances.secondary, 'apply')
32
+ const primaryApplySpy = vi.spyOn(instances.primary, 'apply')
33
+ const secondaryApplySpy = vi.spyOn(instances.secondary, 'apply')
33
34
 
34
35
  interceptor.apply()
35
36
 
@@ -62,10 +63,10 @@ it('proxies event listeners to the interceptors', () => {
62
63
  interceptors: [instances.primary, instances.secondary],
63
64
  })
64
65
 
65
- const helloListener = jest.fn()
66
+ const helloListener = vi.fn()
66
67
  interceptor.on('hello', helloListener)
67
68
 
68
- const goodbyeListener = jest.fn()
69
+ const goodbyeListener = vi.fn()
69
70
  interceptor.on('goodbye', goodbyeListener)
70
71
 
71
72
  // Emulate the child interceptor emitting events.
@@ -102,8 +103,8 @@ it('disposes of child interceptors', async () => {
102
103
  interceptors: [instances.primary, instances.secondary],
103
104
  })
104
105
 
105
- const primaryDisposeSpy = jest.spyOn(instances.primary, 'dispose')
106
- const secondaryDisposeSpy = jest.spyOn(instances.secondary, 'dispose')
106
+ const primaryDisposeSpy = vi.spyOn(instances.primary, 'dispose')
107
+ const secondaryDisposeSpy = vi.spyOn(instances.secondary, 'dispose')
107
108
 
108
109
  interceptor.apply()
109
110
  interceptor.dispose()
@@ -1,3 +1,4 @@
1
+ import { describe, vi, it, expect, afterEach } from 'vitest'
1
2
  import {
2
3
  Interceptor,
3
4
  getGlobalSymbol,
@@ -84,7 +85,7 @@ describe('readyState', () => {
84
85
  describe('apply', () => {
85
86
  it('does not apply the same interceptor multiple times', () => {
86
87
  const interceptor = new Interceptor(symbol)
87
- const setupSpy = jest.spyOn(
88
+ const setupSpy = vi.spyOn(
88
89
  interceptor,
89
90
  // @ts-expect-error Protected property spy.
90
91
  'setup'
@@ -109,7 +110,7 @@ describe('apply', () => {
109
110
  }
110
111
 
111
112
  const interceptor = new MyInterceptor(Symbol('test'))
112
- const setupSpy = jest.spyOn(
113
+ const setupSpy = vi.spyOn(
113
114
  interceptor,
114
115
  // @ts-expect-error Protected property spy.
115
116
  'setup'
@@ -124,11 +125,11 @@ describe('apply', () => {
124
125
  const secondInterceptor = new Interceptor(symbol)
125
126
 
126
127
  firstInterceptor.apply()
127
- const firstListener = jest.fn()
128
+ const firstListener = vi.fn()
128
129
  firstInterceptor.on('test', firstListener)
129
130
 
130
131
  secondInterceptor.apply()
131
- const secondListener = jest.fn()
132
+ const secondListener = vi.fn()
132
133
  secondInterceptor.on('test', secondListener)
133
134
 
134
135
  // Emitting event in the first interceptor will bubble to the second one.
@@ -149,7 +150,7 @@ describe('dispose', () => {
149
150
  const interceptor = new Interceptor(symbol)
150
151
 
151
152
  interceptor.apply()
152
- const listener = jest.fn()
153
+ const listener = vi.fn()
153
154
  interceptor.on('test', listener)
154
155
  interceptor.dispose()
155
156
 
@@ -1,5 +1,4 @@
1
1
  import { ChildProcess } from 'child_process'
2
- import { Request, Response } from '@remix-run/web-fetch'
3
2
  import { Headers, HeadersObject, headersToObject } from 'headers-polyfill'
4
3
  import { HttpRequestEventMap } from './glossary'
5
4
  import { Interceptor } from './Interceptor'
@@ -1,10 +1,9 @@
1
- /**
2
- * @jest-environment node
3
- */
1
+ import { vi, it, expect, beforeAll, afterAll } from 'vitest'
4
2
  import { debug } from 'debug'
5
- import * as express from 'express'
3
+ import express from 'express'
4
+ import { IncomingMessage } from 'http'
6
5
  import { HttpServer } from '@open-draft/test-server/http'
7
- import { Response } from '@remix-run/web-fetch'
6
+ import { DeferredPromise } from '@open-draft/deferred-promise'
8
7
  import { NodeClientRequest } from './NodeClientRequest'
9
8
  import { getIncomingMessageBody } from './utils/getIncomingMessageBody'
10
9
  import { normalizeClientRequestArgs } from './utils/normalizeClientRequestArgs'
@@ -37,7 +36,7 @@ afterAll(async () => {
37
36
  await httpServer.close()
38
37
  })
39
38
 
40
- test('gracefully finishes the request when it has a mocked response', (done) => {
39
+ it('gracefully finishes the request when it has a mocked response', async () => {
41
40
  const emitter = new AsyncEventEmitter<HttpRequestEventMap>()
42
41
  const request = new NodeClientRequest(
43
42
  normalizeClientRequestArgs('http:', 'http://any.thing', {
@@ -60,26 +59,28 @@ test('gracefully finishes the request when it has a mocked response', (done) =>
60
59
  )
61
60
  })
62
61
 
63
- request.on('response', async (response) => {
64
- // Request must be marked as finished.
65
- expect(request.finished).toEqual(true)
66
- expect(request.writableEnded).toEqual(true)
67
- expect(request.writableFinished).toEqual(true)
68
- expect(request.writableCorked).toEqual(0)
62
+ request.end()
69
63
 
70
- expect(response.statusCode).toEqual(301)
71
- expect(response.headers).toHaveProperty('x-custom-header', 'yes')
64
+ const responseReceived = new DeferredPromise<IncomingMessage>()
65
+ request.on('response', async (response) => {
66
+ responseReceived.resolve(response)
67
+ })
68
+ const response = await responseReceived
72
69
 
73
- const text = await getIncomingMessageBody(response)
74
- expect(text).toEqual('mocked-response')
70
+ // Request must be marked as finished.
71
+ expect(request.finished).toEqual(true)
72
+ expect(request.writableEnded).toEqual(true)
73
+ expect(request.writableFinished).toEqual(true)
74
+ expect(request.writableCorked).toEqual(0)
75
75
 
76
- done()
77
- })
76
+ expect(response.statusCode).toEqual(301)
77
+ expect(response.headers).toHaveProperty('x-custom-header', 'yes')
78
78
 
79
- request.end()
79
+ const text = await getIncomingMessageBody(response)
80
+ expect(text).toEqual('mocked-response')
80
81
  })
81
82
 
82
- test('responds with a mocked response when requesting an existing hostname', (done) => {
83
+ it('responds with a mocked response when requesting an existing hostname', async () => {
83
84
  const emitter = new AsyncEventEmitter<HttpRequestEventMap>()
84
85
  const request = new NodeClientRequest(
85
86
  normalizeClientRequestArgs('http:', httpServer.http.url('/comment')),
@@ -93,19 +94,21 @@ test('responds with a mocked response when requesting an existing hostname', (do
93
94
  request.respondWith(new Response('mocked-response', { status: 201 }))
94
95
  })
95
96
 
96
- request.on('response', async (response) => {
97
- expect(response.statusCode).toEqual(201)
98
-
99
- const text = await getIncomingMessageBody(response)
100
- expect(text).toEqual('mocked-response')
97
+ request.end()
101
98
 
102
- done()
99
+ const responseReceived = new DeferredPromise<IncomingMessage>()
100
+ request.on('response', async (response) => {
101
+ responseReceived.resolve(response)
103
102
  })
103
+ const response = await responseReceived
104
104
 
105
- request.end()
105
+ expect(response.statusCode).toEqual(201)
106
+
107
+ const text = await getIncomingMessageBody(response)
108
+ expect(text).toEqual('mocked-response')
106
109
  })
107
110
 
108
- test('performs the request as-is given resolver returned no mocked response', (done) => {
111
+ it('performs the request as-is given resolver returned no mocked response', async () => {
109
112
  const emitter = new AsyncEventEmitter<HttpRequestEventMap>()
110
113
  const request = new NodeClientRequest(
111
114
  normalizeClientRequestArgs('http:', httpServer.http.url('/comment'), {
@@ -117,40 +120,44 @@ test('performs the request as-is given resolver returned no mocked response', (d
117
120
  }
118
121
  )
119
122
 
120
- request.on('response', async (response) => {
121
- expect(request.finished).toEqual(true)
122
- expect(request.writableEnded).toEqual(true)
123
+ request.end()
123
124
 
124
- expect(response.statusCode).toEqual(200)
125
- expect(response.statusMessage).toEqual('OK')
126
- expect(response.headers).toHaveProperty('x-powered-by', 'Express')
125
+ const responseReceived = new DeferredPromise<IncomingMessage>()
126
+ request.on('response', async (response) => {
127
+ responseReceived.resolve(response)
128
+ })
129
+ const response = await responseReceived
127
130
 
128
- const text = await getIncomingMessageBody(response)
129
- expect(text).toEqual('original-response')
131
+ expect(request.finished).toEqual(true)
132
+ expect(request.writableEnded).toEqual(true)
130
133
 
131
- done()
132
- })
134
+ expect(response.statusCode).toEqual(200)
135
+ expect(response.statusMessage).toEqual('OK')
136
+ expect(response.headers).toHaveProperty('x-powered-by', 'Express')
133
137
 
134
- request.end()
138
+ const text = await getIncomingMessageBody(response)
139
+ expect(text).toEqual('original-response')
135
140
  })
136
141
 
137
- test('emits the ENOTFOUND error connecting to a non-existing hostname given no mocked response', (done) => {
142
+ it('emits the ENOTFOUND error connecting to a non-existing hostname given no mocked response', async () => {
138
143
  const emitter = new AsyncEventEmitter<HttpRequestEventMap>()
139
144
  const request = new NodeClientRequest(
140
145
  normalizeClientRequestArgs('http:', 'http://non-existing-url.com'),
141
146
  { emitter, log }
142
147
  )
148
+ request.end()
143
149
 
144
- request.on('error', (error: NodeJS.ErrnoException) => {
145
- expect(error.code).toEqual('ENOTFOUND')
146
- expect(error.syscall).toEqual('getaddrinfo')
147
- done()
150
+ const errorReceived = new DeferredPromise<NodeJS.ErrnoException>()
151
+ request.on('error', async (error) => {
152
+ errorReceived.resolve(error)
148
153
  })
154
+ const error = await errorReceived
149
155
 
150
- request.end()
156
+ expect(error.code).toEqual('ENOTFOUND')
157
+ expect(error.syscall).toEqual('getaddrinfo')
151
158
  })
152
159
 
153
- test('emits the ECONNREFUSED error connecting to an inactive server given no mocked response', (done) => {
160
+ it('emits the ECONNREFUSED error connecting to an inactive server given no mocked response', async () => {
154
161
  const emitter = new AsyncEventEmitter<HttpRequestEventMap>()
155
162
  const request = new NodeClientRequest(
156
163
  normalizeClientRequestArgs('http:', 'http://127.0.0.1:12345'),
@@ -160,21 +167,25 @@ test('emits the ECONNREFUSED error connecting to an inactive server given no moc
160
167
  }
161
168
  )
162
169
 
163
- request.on('error', (error: ErrorConnectionRefused) => {
164
- expect(error.code).toEqual('ECONNREFUSED')
165
- expect(error.syscall).toEqual('connect')
166
- expect(error.address).toEqual('127.0.0.1')
167
- expect(error.port).toEqual(12345)
170
+ request.end()
168
171
 
169
- done()
172
+ const errorReceived = new DeferredPromise<ErrorConnectionRefused>()
173
+ request.on('error', async (error: ErrorConnectionRefused) => {
174
+ errorReceived.resolve(error)
170
175
  })
171
-
172
176
  request.end()
177
+
178
+ const error = await errorReceived
179
+
180
+ expect(error.code).toEqual('ECONNREFUSED')
181
+ expect(error.syscall).toEqual('connect')
182
+ expect(error.address).toEqual('127.0.0.1')
183
+ expect(error.port).toEqual(12345)
173
184
  })
174
185
 
175
- test('does not emit ENOTFOUND error connecting to an inactive server given mocked response', (done) => {
186
+ it('does not emit ENOTFOUND error connecting to an inactive server given mocked response', async () => {
176
187
  const emitter = new AsyncEventEmitter<HttpRequestEventMap>()
177
- const handleError = jest.fn()
188
+ const handleError = vi.fn()
178
189
  const request = new NodeClientRequest(
179
190
  normalizeClientRequestArgs('http:', 'http://non-existing-url.com'),
180
191
  { emitter, log }
@@ -187,19 +198,24 @@ test('does not emit ENOTFOUND error connecting to an inactive server given mocke
187
198
  )
188
199
  })
189
200
 
201
+ request.end()
202
+
190
203
  request.on('error', handleError)
204
+
205
+ const responseReceived = new DeferredPromise<IncomingMessage>()
191
206
  request.on('response', (response) => {
192
- expect(handleError).not.toHaveBeenCalled()
193
- expect(response.statusCode).toEqual(200)
194
- expect(response.statusMessage).toEqual('Works')
195
- done()
207
+ responseReceived.resolve(response)
196
208
  })
197
- request.end()
209
+ const response = await responseReceived
210
+
211
+ expect(handleError).not.toHaveBeenCalled()
212
+ expect(response.statusCode).toEqual(200)
213
+ expect(response.statusMessage).toEqual('Works')
198
214
  })
199
215
 
200
- test('does not emit ECONNREFUSED error connecting to an inactive server given mocked response', (done) => {
216
+ it('does not emit ECONNREFUSED error connecting to an inactive server given mocked response', async () => {
201
217
  const emitter = new AsyncEventEmitter<HttpRequestEventMap>()
202
- const handleError = jest.fn()
218
+ const handleError = vi.fn()
203
219
  const request = new NodeClientRequest(
204
220
  normalizeClientRequestArgs('http:', 'http://localhost:9876'),
205
221
  {
@@ -216,16 +232,20 @@ test('does not emit ECONNREFUSED error connecting to an inactive server given mo
216
232
  })
217
233
 
218
234
  request.on('error', handleError)
235
+ request.end()
236
+
237
+ const responseReceived = new DeferredPromise<IncomingMessage>()
219
238
  request.on('response', (response) => {
220
- expect(handleError).not.toHaveBeenCalled()
221
- expect(response.statusCode).toEqual(200)
222
- expect(response.statusMessage).toEqual('Works')
223
- done()
239
+ responseReceived.resolve(response)
224
240
  })
225
- request.end()
241
+ const response = await responseReceived
242
+
243
+ expect(handleError).not.toHaveBeenCalled()
244
+ expect(response.statusCode).toEqual(200)
245
+ expect(response.statusMessage).toEqual('Works')
226
246
  })
227
247
 
228
- test('sends the request body to the server given no mocked response', (done) => {
248
+ it('sends the request body to the server given no mocked response', async () => {
229
249
  const emitter = new AsyncEventEmitter<HttpRequestEventMap>()
230
250
  const request = new NodeClientRequest(
231
251
  normalizeClientRequestArgs('http:', httpServer.http.url('/write'), {
@@ -242,20 +262,21 @@ test('sends the request body to the server given no mocked response', (done) =>
242
262
 
243
263
  request.write('one')
244
264
  request.write('two')
265
+ request.end('three')
245
266
 
246
- request.on('response', async (response) => {
247
- expect(response.statusCode).toEqual(200)
248
-
249
- const text = await getIncomingMessageBody(response)
250
- expect(text).toEqual('onetwothree')
251
-
252
- done()
267
+ const responseReceived = new DeferredPromise<IncomingMessage>()
268
+ request.on('response', (response) => {
269
+ responseReceived.resolve(response)
253
270
  })
271
+ const response = await responseReceived
254
272
 
255
- request.end('three')
273
+ expect(response.statusCode).toEqual(200)
274
+
275
+ const text = await getIncomingMessageBody(response)
276
+ expect(text).toEqual('onetwothree')
256
277
  })
257
278
 
258
- test('does not send request body to the original server given mocked response', (done) => {
279
+ it('does not send request body to the original server given mocked response', async () => {
259
280
  const emitter = new AsyncEventEmitter<HttpRequestEventMap>()
260
281
  const request = new NodeClientRequest(
261
282
  normalizeClientRequestArgs('http:', httpServer.http.url('/write'), {
@@ -274,15 +295,16 @@ test('does not send request body to the original server given mocked response',
274
295
 
275
296
  request.write('one')
276
297
  request.write('two')
298
+ request.end()
277
299
 
278
- request.on('response', async (response) => {
279
- expect(response.statusCode).toEqual(301)
280
-
281
- const text = await getIncomingMessageBody(response)
282
- expect(text).toEqual('mock created!')
283
-
284
- done()
300
+ const responseReceived = new DeferredPromise<IncomingMessage>()
301
+ request.on('response', (response) => {
302
+ responseReceived.resolve(response)
285
303
  })
304
+ const response = await responseReceived
286
305
 
287
- request.end()
306
+ expect(response.statusCode).toEqual(301)
307
+
308
+ const text = await getIncomingMessageBody(response)
309
+ expect(text).toEqual('mock created!')
288
310
  })
@@ -177,6 +177,20 @@ export class NodeClientRequest extends ClientRequest {
177
177
  }).then(([resolverException, mockedResponse]) => {
178
178
  this.log('the listeners promise awaited!')
179
179
 
180
+ /**
181
+ * @fixme We are in the "end()" method that still executes in parallel
182
+ * to our mocking logic here. This can be solved by migrating to the
183
+ * Proxy-based approach and deferring the passthrough "end()" properly.
184
+ * @see https://github.com/mswjs/interceptors/issues/346
185
+ */
186
+ if (!this.headersSent) {
187
+ // Forward any request headers that the "request" listener
188
+ // may have modified before proceeding with this request.
189
+ for (const [headerName, headerValue] of capturedRequest.headers) {
190
+ this.setHeader(headerName, headerValue)
191
+ }
192
+ }
193
+
180
194
  // Halt the request whenever the resolver throws an exception.
181
195
  if (resolverException) {
182
196
  this.log(
@@ -189,12 +203,6 @@ export class NodeClientRequest extends ClientRequest {
189
203
  return this
190
204
  }
191
205
 
192
- // Forward any request headers that the "request" listener
193
- // may have modified before proceeding with this request.
194
- for (const [headerName, headerValue] of capturedRequest.headers) {
195
- this.setHeader(headerName, headerValue)
196
- }
197
-
198
206
  if (mockedResponse) {
199
207
  const responseClone = mockedResponse.clone()
200
208
 
@@ -1,6 +1,7 @@
1
- import * as http from 'http'
1
+ import { it, expect, beforeAll, afterAll } from 'vitest'
2
+ import http from 'http'
2
3
  import { HttpServer } from '@open-draft/test-server/http'
3
- import { Response } from '@remix-run/web-fetch'
4
+ import { DeferredPromise } from '@open-draft/deferred-promise'
4
5
  import { ClientRequestInterceptor } from '.'
5
6
 
6
7
  const httpServer = new HttpServer((app) => {
@@ -15,8 +16,8 @@ const httpServer = new HttpServer((app) => {
15
16
  const interceptor = new ClientRequestInterceptor()
16
17
 
17
18
  beforeAll(async () => {
18
- await httpServer.listen()
19
19
  interceptor.apply()
20
+ await httpServer.listen()
20
21
  })
21
22
 
22
23
  afterAll(async () => {
@@ -24,22 +25,33 @@ afterAll(async () => {
24
25
  await httpServer.close()
25
26
  })
26
27
 
27
- it('forbids calling "respondWith" multiple times for the same request', (done) => {
28
+ it('forbids calling "respondWith" multiple times for the same request', async () => {
28
29
  const requestUrl = httpServer.http.url('/')
29
30
 
30
- interceptor.on('request', (request) => {
31
+ interceptor.on('request', function firstRequestListener(request) {
31
32
  request.respondWith(new Response())
32
33
  })
33
34
 
34
- interceptor.on('request', (request) => {
35
+ const secondRequestEmitted = new DeferredPromise<void>()
36
+ interceptor.on('request', function secondRequestListener(request) {
35
37
  expect(() =>
36
38
  request.respondWith(new Response(null, { status: 301 }))
37
39
  ).toThrow(
38
40
  `Failed to respond to "GET ${requestUrl}" request: the "request" event has already been responded to.`
39
41
  )
40
42
 
41
- done()
43
+ secondRequestEmitted.resolve()
44
+ })
45
+
46
+ const request = http.get(requestUrl)
47
+ await secondRequestEmitted
48
+
49
+ const responseReceived = new DeferredPromise<http.IncomingMessage>()
50
+ request.on('response', (response) => {
51
+ responseReceived.resolve(response)
42
52
  })
43
53
 
44
- http.get(requestUrl)
54
+ const response = await responseReceived
55
+ expect(response.statusCode).toBe(200)
56
+ expect(response.statusMessage).toBe('')
45
57
  })
@@ -1,9 +1,10 @@
1
+ import { it, expect } from 'vitest'
1
2
  import { Socket } from 'net'
2
3
  import { IncomingMessage } from 'http'
3
4
  import { Stream, Readable, EventEmitter } from 'stream'
4
5
  import { cloneIncomingMessage, IS_CLONE } from './cloneIncomingMessage'
5
6
 
6
- test('clones a given IncomingMessage', () => {
7
+ it('clones a given IncomingMessage', () => {
7
8
  const message = new IncomingMessage(new Socket())
8
9
  message.statusCode = 200
9
10
  message.statusMessage = 'OK'
@@ -1,3 +1,4 @@
1
+ import { it, expect } from 'vitest'
1
2
  import { debug } from 'debug'
2
3
  import { HttpRequestEventMap } from '../../..'
3
4
  import { AsyncEventEmitter } from '../../../utils/AsyncEventEmitter'