@depup/mswjs__interceptors 0.41.3-depup.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 (248) hide show
  1. package/ClientRequest/package.json +11 -0
  2. package/LICENSE.md +9 -0
  3. package/README.md +31 -0
  4. package/RemoteHttpInterceptor/package.json +11 -0
  5. package/WebSocket/package.json +12 -0
  6. package/XMLHttpRequest/package.json +12 -0
  7. package/changes.json +10 -0
  8. package/fetch/package.json +12 -0
  9. package/lib/browser/Interceptor-Deczogc8.d.cts +65 -0
  10. package/lib/browser/Interceptor-gqKgs-aF.d.mts +65 -0
  11. package/lib/browser/XMLHttpRequest-BACqefB-.cjs +761 -0
  12. package/lib/browser/XMLHttpRequest-BACqefB-.cjs.map +1 -0
  13. package/lib/browser/XMLHttpRequest-BvxZV0WU.mjs +756 -0
  14. package/lib/browser/XMLHttpRequest-BvxZV0WU.mjs.map +1 -0
  15. package/lib/browser/bufferUtils-BiiO6HZv.mjs +20 -0
  16. package/lib/browser/bufferUtils-BiiO6HZv.mjs.map +1 -0
  17. package/lib/browser/bufferUtils-Uc0eRItL.cjs +38 -0
  18. package/lib/browser/bufferUtils-Uc0eRItL.cjs.map +1 -0
  19. package/lib/browser/createRequestId-Cs4oXfa1.cjs +205 -0
  20. package/lib/browser/createRequestId-Cs4oXfa1.cjs.map +1 -0
  21. package/lib/browser/createRequestId-DQcIlohW.mjs +170 -0
  22. package/lib/browser/createRequestId-DQcIlohW.mjs.map +1 -0
  23. package/lib/browser/fetch-DdKEdDOR.mjs +248 -0
  24. package/lib/browser/fetch-DdKEdDOR.mjs.map +1 -0
  25. package/lib/browser/fetch-U3v3Y4ap.cjs +253 -0
  26. package/lib/browser/fetch-U3v3Y4ap.cjs.map +1 -0
  27. package/lib/browser/getRawRequest-BTaNLFr0.mjs +218 -0
  28. package/lib/browser/getRawRequest-BTaNLFr0.mjs.map +1 -0
  29. package/lib/browser/getRawRequest-zx8rUJL2.cjs +259 -0
  30. package/lib/browser/getRawRequest-zx8rUJL2.cjs.map +1 -0
  31. package/lib/browser/glossary-BdLS4k1H.d.cts +70 -0
  32. package/lib/browser/glossary-DYwOrogs.d.mts +70 -0
  33. package/lib/browser/handleRequest-CvX2G-Lz.cjs +189 -0
  34. package/lib/browser/handleRequest-CvX2G-Lz.cjs.map +1 -0
  35. package/lib/browser/handleRequest-D7kpTI5U.mjs +178 -0
  36. package/lib/browser/handleRequest-D7kpTI5U.mjs.map +1 -0
  37. package/lib/browser/hasConfigurableGlobal-BvCTG97d.cjs +45 -0
  38. package/lib/browser/hasConfigurableGlobal-BvCTG97d.cjs.map +1 -0
  39. package/lib/browser/hasConfigurableGlobal-npXitu1-.mjs +33 -0
  40. package/lib/browser/hasConfigurableGlobal-npXitu1-.mjs.map +1 -0
  41. package/lib/browser/index.cjs +70 -0
  42. package/lib/browser/index.cjs.map +1 -0
  43. package/lib/browser/index.d.cts +96 -0
  44. package/lib/browser/index.d.mts +96 -0
  45. package/lib/browser/index.mjs +56 -0
  46. package/lib/browser/index.mjs.map +1 -0
  47. package/lib/browser/interceptors/WebSocket/index.cjs +622 -0
  48. package/lib/browser/interceptors/WebSocket/index.cjs.map +1 -0
  49. package/lib/browser/interceptors/WebSocket/index.d.cts +277 -0
  50. package/lib/browser/interceptors/WebSocket/index.d.mts +277 -0
  51. package/lib/browser/interceptors/WebSocket/index.mjs +615 -0
  52. package/lib/browser/interceptors/WebSocket/index.mjs.map +1 -0
  53. package/lib/browser/interceptors/XMLHttpRequest/index.cjs +7 -0
  54. package/lib/browser/interceptors/XMLHttpRequest/index.d.cts +15 -0
  55. package/lib/browser/interceptors/XMLHttpRequest/index.d.mts +15 -0
  56. package/lib/browser/interceptors/XMLHttpRequest/index.mjs +7 -0
  57. package/lib/browser/interceptors/fetch/index.cjs +6 -0
  58. package/lib/browser/interceptors/fetch/index.d.cts +13 -0
  59. package/lib/browser/interceptors/fetch/index.d.mts +13 -0
  60. package/lib/browser/interceptors/fetch/index.mjs +6 -0
  61. package/lib/browser/presets/browser.cjs +17 -0
  62. package/lib/browser/presets/browser.cjs.map +1 -0
  63. package/lib/browser/presets/browser.d.cts +12 -0
  64. package/lib/browser/presets/browser.d.mts +14 -0
  65. package/lib/browser/presets/browser.mjs +17 -0
  66. package/lib/browser/presets/browser.mjs.map +1 -0
  67. package/lib/browser/resolveWebSocketUrl-6K6EgqsA.cjs +31 -0
  68. package/lib/browser/resolveWebSocketUrl-6K6EgqsA.cjs.map +1 -0
  69. package/lib/browser/resolveWebSocketUrl-C83-x9iE.mjs +25 -0
  70. package/lib/browser/resolveWebSocketUrl-C83-x9iE.mjs.map +1 -0
  71. package/lib/node/BatchInterceptor-3LnAnLTx.cjs +49 -0
  72. package/lib/node/BatchInterceptor-3LnAnLTx.cjs.map +1 -0
  73. package/lib/node/BatchInterceptor-D7mXzHcQ.d.mts +26 -0
  74. package/lib/node/BatchInterceptor-DFaBPilf.mjs +44 -0
  75. package/lib/node/BatchInterceptor-DFaBPilf.mjs.map +1 -0
  76. package/lib/node/BatchInterceptor-D_YqR8qU.d.cts +26 -0
  77. package/lib/node/ClientRequest-2rDe54Ui.cjs +1043 -0
  78. package/lib/node/ClientRequest-2rDe54Ui.cjs.map +1 -0
  79. package/lib/node/ClientRequest-Ca8Qykuv.mjs +1034 -0
  80. package/lib/node/ClientRequest-Ca8Qykuv.mjs.map +1 -0
  81. package/lib/node/Interceptor-DEazpLJd.d.mts +133 -0
  82. package/lib/node/Interceptor-DJ2akVWI.d.cts +133 -0
  83. package/lib/node/RemoteHttpInterceptor.cjs +154 -0
  84. package/lib/node/RemoteHttpInterceptor.cjs.map +1 -0
  85. package/lib/node/RemoteHttpInterceptor.d.cts +39 -0
  86. package/lib/node/RemoteHttpInterceptor.d.mts +39 -0
  87. package/lib/node/RemoteHttpInterceptor.mjs +152 -0
  88. package/lib/node/RemoteHttpInterceptor.mjs.map +1 -0
  89. package/lib/node/XMLHttpRequest-B7kJdYYI.cjs +763 -0
  90. package/lib/node/XMLHttpRequest-B7kJdYYI.cjs.map +1 -0
  91. package/lib/node/XMLHttpRequest-C8dIZpds.mjs +757 -0
  92. package/lib/node/XMLHttpRequest-C8dIZpds.mjs.map +1 -0
  93. package/lib/node/bufferUtils-DiCTqG-7.cjs +38 -0
  94. package/lib/node/bufferUtils-DiCTqG-7.cjs.map +1 -0
  95. package/lib/node/bufferUtils-_8XfKIfX.mjs +20 -0
  96. package/lib/node/bufferUtils-_8XfKIfX.mjs.map +1 -0
  97. package/lib/node/chunk-CbDLau6x.cjs +34 -0
  98. package/lib/node/fetch-BmXpK10r.cjs +272 -0
  99. package/lib/node/fetch-BmXpK10r.cjs.map +1 -0
  100. package/lib/node/fetch-G1DVwDKG.mjs +265 -0
  101. package/lib/node/fetch-G1DVwDKG.mjs.map +1 -0
  102. package/lib/node/fetchUtils-BaY5iWXw.cjs +419 -0
  103. package/lib/node/fetchUtils-BaY5iWXw.cjs.map +1 -0
  104. package/lib/node/fetchUtils-CoU35g3M.mjs +359 -0
  105. package/lib/node/fetchUtils-CoU35g3M.mjs.map +1 -0
  106. package/lib/node/getRawRequest-BavnMWh_.cjs +36 -0
  107. package/lib/node/getRawRequest-BavnMWh_.cjs.map +1 -0
  108. package/lib/node/getRawRequest-DnwmXyOW.mjs +24 -0
  109. package/lib/node/getRawRequest-DnwmXyOW.mjs.map +1 -0
  110. package/lib/node/glossary-BLKRyLBd.cjs +12 -0
  111. package/lib/node/glossary-BLKRyLBd.cjs.map +1 -0
  112. package/lib/node/glossary-glQBRnVD.mjs +6 -0
  113. package/lib/node/glossary-glQBRnVD.mjs.map +1 -0
  114. package/lib/node/handleRequest-Bb7Y-XLw.cjs +220 -0
  115. package/lib/node/handleRequest-Bb7Y-XLw.cjs.map +1 -0
  116. package/lib/node/handleRequest-Y97UwBbF.mjs +190 -0
  117. package/lib/node/handleRequest-Y97UwBbF.mjs.map +1 -0
  118. package/lib/node/hasConfigurableGlobal-C97fWuaA.cjs +26 -0
  119. package/lib/node/hasConfigurableGlobal-C97fWuaA.cjs.map +1 -0
  120. package/lib/node/hasConfigurableGlobal-DBJA0vjm.mjs +20 -0
  121. package/lib/node/hasConfigurableGlobal-DBJA0vjm.mjs.map +1 -0
  122. package/lib/node/index-BMbJ8FXL.d.cts +113 -0
  123. package/lib/node/index-C0YAQ36w.d.mts +113 -0
  124. package/lib/node/index.cjs +54 -0
  125. package/lib/node/index.cjs.map +1 -0
  126. package/lib/node/index.d.cts +75 -0
  127. package/lib/node/index.d.mts +75 -0
  128. package/lib/node/index.mjs +40 -0
  129. package/lib/node/index.mjs.map +1 -0
  130. package/lib/node/interceptors/ClientRequest/index.cjs +6 -0
  131. package/lib/node/interceptors/ClientRequest/index.d.cts +2 -0
  132. package/lib/node/interceptors/ClientRequest/index.d.mts +3 -0
  133. package/lib/node/interceptors/ClientRequest/index.mjs +6 -0
  134. package/lib/node/interceptors/XMLHttpRequest/index.cjs +6 -0
  135. package/lib/node/interceptors/XMLHttpRequest/index.d.cts +14 -0
  136. package/lib/node/interceptors/XMLHttpRequest/index.d.mts +14 -0
  137. package/lib/node/interceptors/XMLHttpRequest/index.mjs +6 -0
  138. package/lib/node/interceptors/fetch/index.cjs +5 -0
  139. package/lib/node/interceptors/fetch/index.d.cts +12 -0
  140. package/lib/node/interceptors/fetch/index.d.mts +12 -0
  141. package/lib/node/interceptors/fetch/index.mjs +5 -0
  142. package/lib/node/node-DwCc6iuP.mjs +27 -0
  143. package/lib/node/node-DwCc6iuP.mjs.map +1 -0
  144. package/lib/node/node-dKdAf3tC.cjs +39 -0
  145. package/lib/node/node-dKdAf3tC.cjs.map +1 -0
  146. package/lib/node/presets/node.cjs +22 -0
  147. package/lib/node/presets/node.cjs.map +1 -0
  148. package/lib/node/presets/node.d.cts +13 -0
  149. package/lib/node/presets/node.d.mts +15 -0
  150. package/lib/node/presets/node.mjs +22 -0
  151. package/lib/node/presets/node.mjs.map +1 -0
  152. package/lib/node/utils/node/index.cjs +4 -0
  153. package/lib/node/utils/node/index.d.cts +16 -0
  154. package/lib/node/utils/node/index.d.mts +16 -0
  155. package/lib/node/utils/node/index.mjs +3 -0
  156. package/package.json +204 -0
  157. package/presets/browser/package.json +5 -0
  158. package/presets/node/package.json +11 -0
  159. package/src/BatchInterceptor.test.ts +255 -0
  160. package/src/BatchInterceptor.ts +95 -0
  161. package/src/Interceptor.test.ts +205 -0
  162. package/src/Interceptor.ts +249 -0
  163. package/src/InterceptorError.ts +7 -0
  164. package/src/RemoteHttpInterceptor.ts +251 -0
  165. package/src/RequestController.test.ts +104 -0
  166. package/src/RequestController.ts +109 -0
  167. package/src/createRequestId.test.ts +7 -0
  168. package/src/createRequestId.ts +9 -0
  169. package/src/getRawRequest.ts +21 -0
  170. package/src/glossary.ts +37 -0
  171. package/src/index.ts +15 -0
  172. package/src/interceptors/ClientRequest/MockHttpSocket.ts +724 -0
  173. package/src/interceptors/ClientRequest/agents.ts +110 -0
  174. package/src/interceptors/ClientRequest/index.test.ts +75 -0
  175. package/src/interceptors/ClientRequest/index.ts +193 -0
  176. package/src/interceptors/ClientRequest/utils/getIncomingMessageBody.test.ts +54 -0
  177. package/src/interceptors/ClientRequest/utils/getIncomingMessageBody.ts +45 -0
  178. package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.test.ts +427 -0
  179. package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.ts +268 -0
  180. package/src/interceptors/ClientRequest/utils/parserUtils.ts +48 -0
  181. package/src/interceptors/ClientRequest/utils/recordRawHeaders.test.ts +258 -0
  182. package/src/interceptors/ClientRequest/utils/recordRawHeaders.ts +262 -0
  183. package/src/interceptors/Socket/MockSocket.test.ts +264 -0
  184. package/src/interceptors/Socket/MockSocket.ts +58 -0
  185. package/src/interceptors/Socket/utils/baseUrlFromConnectionOptions.ts +26 -0
  186. package/src/interceptors/Socket/utils/normalizeSocketWriteArgs.test.ts +52 -0
  187. package/src/interceptors/Socket/utils/normalizeSocketWriteArgs.ts +33 -0
  188. package/src/interceptors/WebSocket/WebSocketClassTransport.ts +116 -0
  189. package/src/interceptors/WebSocket/WebSocketClientConnection.ts +152 -0
  190. package/src/interceptors/WebSocket/WebSocketOverride.ts +252 -0
  191. package/src/interceptors/WebSocket/WebSocketServerConnection.ts +420 -0
  192. package/src/interceptors/WebSocket/WebSocketTransport.ts +39 -0
  193. package/src/interceptors/WebSocket/index.ts +191 -0
  194. package/src/interceptors/WebSocket/utils/bindEvent.test.ts +27 -0
  195. package/src/interceptors/WebSocket/utils/bindEvent.ts +21 -0
  196. package/src/interceptors/WebSocket/utils/events.test.ts +101 -0
  197. package/src/interceptors/WebSocket/utils/events.ts +94 -0
  198. package/src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts +746 -0
  199. package/src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts +121 -0
  200. package/src/interceptors/XMLHttpRequest/index.ts +61 -0
  201. package/src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts +51 -0
  202. package/src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts +17 -0
  203. package/src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts +12 -0
  204. package/src/interceptors/XMLHttpRequest/utils/concateArrayBuffer.test.ts +12 -0
  205. package/src/interceptors/XMLHttpRequest/utils/createEvent.test.ts +26 -0
  206. package/src/interceptors/XMLHttpRequest/utils/createEvent.ts +41 -0
  207. package/src/interceptors/XMLHttpRequest/utils/createResponse.ts +49 -0
  208. package/src/interceptors/XMLHttpRequest/utils/getBodyByteLength.test.ts +164 -0
  209. package/src/interceptors/XMLHttpRequest/utils/getBodyByteLength.ts +16 -0
  210. package/src/interceptors/XMLHttpRequest/utils/isDomParserSupportedType.ts +14 -0
  211. package/src/interceptors/fetch/index.ts +214 -0
  212. package/src/interceptors/fetch/utils/brotli-decompress.browser.ts +14 -0
  213. package/src/interceptors/fetch/utils/brotli-decompress.ts +31 -0
  214. package/src/interceptors/fetch/utils/createNetworkError.ts +5 -0
  215. package/src/interceptors/fetch/utils/decompression.ts +85 -0
  216. package/src/interceptors/fetch/utils/followRedirect.ts +114 -0
  217. package/src/presets/browser.ts +11 -0
  218. package/src/presets/node.ts +13 -0
  219. package/src/utils/bufferUtils.test.ts +21 -0
  220. package/src/utils/bufferUtils.ts +22 -0
  221. package/src/utils/canParseUrl.ts +13 -0
  222. package/src/utils/cloneObject.test.ts +94 -0
  223. package/src/utils/cloneObject.ts +36 -0
  224. package/src/utils/createProxy.test.ts +164 -0
  225. package/src/utils/createProxy.ts +104 -0
  226. package/src/utils/emitAsync.ts +25 -0
  227. package/src/utils/fetchUtils.ts +119 -0
  228. package/src/utils/findPropertySource.test.ts +27 -0
  229. package/src/utils/findPropertySource.ts +20 -0
  230. package/src/utils/getCleanUrl.test.ts +32 -0
  231. package/src/utils/getCleanUrl.ts +6 -0
  232. package/src/utils/getUrlByRequestOptions.test.ts +163 -0
  233. package/src/utils/getUrlByRequestOptions.ts +152 -0
  234. package/src/utils/getValueBySymbol.test.ts +14 -0
  235. package/src/utils/getValueBySymbol.ts +19 -0
  236. package/src/utils/handleRequest.ts +205 -0
  237. package/src/utils/hasConfigurableGlobal.test.ts +83 -0
  238. package/src/utils/hasConfigurableGlobal.ts +34 -0
  239. package/src/utils/isNodeLikeError.ts +13 -0
  240. package/src/utils/isObject.test.ts +21 -0
  241. package/src/utils/isObject.ts +8 -0
  242. package/src/utils/isPropertyAccessible.ts +19 -0
  243. package/src/utils/nextTick.ts +11 -0
  244. package/src/utils/node/index.ts +39 -0
  245. package/src/utils/parseJson.test.ts +10 -0
  246. package/src/utils/parseJson.ts +12 -0
  247. package/src/utils/resolveWebSocketUrl.ts +45 -0
  248. package/src/utils/responseUtils.ts +59 -0
@@ -0,0 +1,264 @@
1
+ /**
2
+ * @vitest-environment node
3
+ */
4
+ import { Socket } from 'node:net'
5
+ import { vi, it, expect } from 'vitest'
6
+ import { MockSocket } from './MockSocket'
7
+
8
+ it(`keeps the socket connecting until it's destroyed`, () => {
9
+ const socket = new MockSocket({
10
+ write: vi.fn(),
11
+ read: vi.fn(),
12
+ })
13
+
14
+ expect(socket.connecting).toBe(true)
15
+
16
+ socket.destroy()
17
+ expect(socket.connecting).toBe(false)
18
+ })
19
+
20
+ it('calls the "write" on "socket.write()"', () => {
21
+ const writeCallback = vi.fn()
22
+ const socket = new MockSocket({
23
+ write: writeCallback,
24
+ read: vi.fn(),
25
+ })
26
+
27
+ socket.write()
28
+ expect(writeCallback).toHaveBeenCalledWith(undefined, undefined, undefined)
29
+ })
30
+
31
+ it('calls the "write" on "socket.write(chunk)"', () => {
32
+ const writeCallback = vi.fn()
33
+ const socket = new MockSocket({
34
+ write: writeCallback,
35
+ read: vi.fn(),
36
+ })
37
+
38
+ socket.write('hello')
39
+ expect(writeCallback).toHaveBeenCalledWith('hello', undefined, undefined)
40
+ })
41
+
42
+ it('calls the "write" on "socket.write(chunk, encoding)"', () => {
43
+ const writeCallback = vi.fn()
44
+ const socket = new MockSocket({
45
+ write: writeCallback,
46
+ read: vi.fn(),
47
+ })
48
+
49
+ socket.write('hello', 'utf8')
50
+ expect(writeCallback).toHaveBeenCalledWith('hello', 'utf8', undefined)
51
+ })
52
+
53
+ it('calls the "write" on "socket.write(chunk, encoding, callback)"', () => {
54
+ const writeCallback = vi.fn()
55
+ const socket = new MockSocket({
56
+ write: writeCallback,
57
+ read: vi.fn(),
58
+ })
59
+
60
+ const callback = vi.fn()
61
+ socket.write('hello', 'utf8', callback)
62
+ expect(writeCallback).toHaveBeenCalledWith('hello', 'utf8', callback)
63
+ })
64
+
65
+ it('calls the "write" on "socket.end()"', () => {
66
+ const writeCallback = vi.fn()
67
+ const socket = new MockSocket({
68
+ write: writeCallback,
69
+ read: vi.fn(),
70
+ })
71
+
72
+ socket.end()
73
+ expect(writeCallback).toHaveBeenCalledWith(undefined, undefined, undefined)
74
+ })
75
+
76
+ it('calls the "write" on "socket.end(chunk)"', () => {
77
+ const writeCallback = vi.fn()
78
+ const socket = new MockSocket({
79
+ write: writeCallback,
80
+ read: vi.fn(),
81
+ })
82
+
83
+ socket.end('final')
84
+ expect(writeCallback).toHaveBeenCalledWith('final', undefined, undefined)
85
+ })
86
+
87
+ it('calls the "write" on "socket.end(chunk, encoding)"', () => {
88
+ const writeCallback = vi.fn()
89
+ const socket = new MockSocket({
90
+ write: writeCallback,
91
+ read: vi.fn(),
92
+ })
93
+
94
+ socket.end('final', 'utf8')
95
+ expect(writeCallback).toHaveBeenCalledWith('final', 'utf8', undefined)
96
+ })
97
+
98
+ it('calls the "write" on "socket.end(chunk, encoding, callback)"', () => {
99
+ const writeCallback = vi.fn()
100
+ const socket = new MockSocket({
101
+ write: writeCallback,
102
+ read: vi.fn(),
103
+ })
104
+
105
+ const callback = vi.fn()
106
+ socket.end('final', 'utf8', callback)
107
+ expect(writeCallback).toHaveBeenCalledWith('final', 'utf8', callback)
108
+ })
109
+
110
+ it('calls the "write" on "socket.end()" without any arguments', () => {
111
+ const writeCallback = vi.fn()
112
+ const socket = new MockSocket({
113
+ write: writeCallback,
114
+ read: vi.fn(),
115
+ })
116
+
117
+ socket.end()
118
+ expect(writeCallback).toHaveBeenCalledWith(undefined, undefined, undefined)
119
+ })
120
+
121
+ it('emits "finished" on .end() without any arguments', async () => {
122
+ const finishListener = vi.fn()
123
+ const socket = new MockSocket({
124
+ write: vi.fn(),
125
+ read: vi.fn(),
126
+ })
127
+ socket.on('finish', finishListener)
128
+ socket.end()
129
+
130
+ await vi.waitFor(() => {
131
+ expect(finishListener).toHaveBeenCalledTimes(1)
132
+ })
133
+ })
134
+
135
+ it('calls the "read" on "socket.read(chunk)"', () => {
136
+ const readCallback = vi.fn()
137
+ const socket = new MockSocket({
138
+ write: vi.fn(),
139
+ read: readCallback,
140
+ })
141
+
142
+ socket.push('hello')
143
+ expect(readCallback).toHaveBeenCalledWith('hello', undefined)
144
+ })
145
+
146
+ it('calls the "read" on "socket.read(chunk, encoding)"', () => {
147
+ const readCallback = vi.fn()
148
+ const socket = new MockSocket({
149
+ write: vi.fn(),
150
+ read: readCallback,
151
+ })
152
+
153
+ socket.push('world', 'utf8')
154
+ expect(readCallback).toHaveBeenCalledWith('world', 'utf8')
155
+ })
156
+
157
+ it('calls the "read" on "socket.read(null)"', () => {
158
+ const readCallback = vi.fn()
159
+ const socket = new MockSocket({
160
+ write: vi.fn(),
161
+ read: readCallback,
162
+ })
163
+
164
+ socket.push(null)
165
+ expect(readCallback).toHaveBeenCalledWith(null, undefined)
166
+ })
167
+
168
+ it('updates the writable state on "socket.end()"', async () => {
169
+ const finishListener = vi.fn()
170
+ const endListener = vi.fn()
171
+ const socket = new MockSocket({
172
+ write: vi.fn(),
173
+ read: vi.fn(),
174
+ })
175
+ socket.on('finish', finishListener)
176
+ socket.on('end', endListener)
177
+
178
+ expect(socket.writable).toBe(true)
179
+ expect(socket.writableEnded).toBe(false)
180
+ expect(socket.writableFinished).toBe(false)
181
+
182
+ socket.write('hello')
183
+ // Finish the writable stream.
184
+ socket.end()
185
+
186
+ expect(socket.writable).toBe(false)
187
+ expect(socket.writableEnded).toBe(true)
188
+
189
+ // The "finish" event is emitted when writable is done.
190
+ // I.e. "socket.end()" is called.
191
+ await vi.waitFor(() => {
192
+ expect(finishListener).toHaveBeenCalledTimes(1)
193
+ })
194
+ expect(socket.writableFinished).toBe(true)
195
+ })
196
+
197
+ it('updates the readable state on "socket.push(null)"', async () => {
198
+ const endListener = vi.fn()
199
+ const socket = new MockSocket({
200
+ write: vi.fn(),
201
+ read: vi.fn(),
202
+ })
203
+ socket.on('end', endListener)
204
+
205
+ expect(socket.readable).toBe(true)
206
+ expect(socket.readableEnded).toBe(false)
207
+
208
+ socket.push('hello')
209
+ socket.push(null)
210
+
211
+ expect(socket.readable).toBe(true)
212
+ expect(socket.readableEnded).toBe(false)
213
+
214
+ // Read the data to free the buffer and
215
+ // make Socket emit "end".
216
+ socket.read()
217
+
218
+ await vi.waitFor(() => {
219
+ expect(endListener).toHaveBeenCalledTimes(1)
220
+ })
221
+ expect(socket.readable).toBe(false)
222
+ expect(socket.readableEnded).toBe(true)
223
+ })
224
+
225
+ it('updates the readable/writable state on "socket.destroy()"', async () => {
226
+ const finishListener = vi.fn()
227
+ const endListener = vi.fn()
228
+ const closeListener = vi.fn()
229
+ const socket = new MockSocket({
230
+ write: vi.fn(),
231
+ read: vi.fn(),
232
+ })
233
+ socket.on('finish', finishListener)
234
+ socket.on('end', endListener)
235
+ socket.on('close', closeListener)
236
+
237
+ expect(socket.writable).toBe(true)
238
+ expect(socket.writableEnded).toBe(false)
239
+ expect(socket.writableFinished).toBe(false)
240
+ expect(socket.readable).toBe(true)
241
+
242
+ socket.destroy()
243
+
244
+ expect(socket.writable).toBe(false)
245
+ // The ".end()" wasn't called.
246
+ expect(socket.writableEnded).toBe(false)
247
+ expect(socket.writableFinished).toBe(false)
248
+ expect(socket.readable).toBe(false)
249
+
250
+ await vi.waitFor(() => {
251
+ expect(closeListener).toHaveBeenCalledTimes(1)
252
+ })
253
+
254
+ // Neither "finish" nor "end" events are emitted
255
+ // when you destroy the stream. If you want those,
256
+ // call ".end()", then destroy the stream.
257
+ expect(finishListener).not.toHaveBeenCalled()
258
+ expect(endListener).not.toHaveBeenCalled()
259
+ expect(socket.writableFinished).toBe(false)
260
+
261
+ // The "end" event was never emitted so "readableEnded"
262
+ // remains false.
263
+ expect(socket.readableEnded).toBe(false)
264
+ })
@@ -0,0 +1,58 @@
1
+ import net from 'node:net'
2
+ import {
3
+ normalizeSocketWriteArgs,
4
+ type WriteArgs,
5
+ type WriteCallback,
6
+ } from './utils/normalizeSocketWriteArgs'
7
+
8
+ export interface MockSocketOptions {
9
+ write: (
10
+ chunk: Buffer | string,
11
+ encoding: BufferEncoding | undefined,
12
+ callback?: WriteCallback
13
+ ) => void
14
+
15
+ read: (chunk: Buffer, encoding: BufferEncoding | undefined) => void
16
+ }
17
+
18
+ export class MockSocket extends net.Socket {
19
+ public connecting: boolean
20
+
21
+ constructor(protected readonly options: MockSocketOptions) {
22
+ super()
23
+ this.connecting = false
24
+ this.connect()
25
+
26
+ this._final = (callback) => {
27
+ callback(null)
28
+ }
29
+ }
30
+
31
+ public connect() {
32
+ // The connection will remain pending until
33
+ // the consumer decides to handle it.
34
+ this.connecting = true
35
+ return this
36
+ }
37
+
38
+ public write(...args: Array<unknown>): boolean {
39
+ const [chunk, encoding, callback] = normalizeSocketWriteArgs(
40
+ args as WriteArgs
41
+ )
42
+ this.options.write(chunk, encoding, callback)
43
+ return true
44
+ }
45
+
46
+ public end(...args: Array<unknown>) {
47
+ const [chunk, encoding, callback] = normalizeSocketWriteArgs(
48
+ args as WriteArgs
49
+ )
50
+ this.options.write(chunk, encoding, callback)
51
+ return super.end.apply(this, args as any)
52
+ }
53
+
54
+ public push(chunk: any, encoding?: BufferEncoding): boolean {
55
+ this.options.read(chunk, encoding)
56
+ return super.push(chunk, encoding)
57
+ }
58
+ }
@@ -0,0 +1,26 @@
1
+ export function baseUrlFromConnectionOptions(options: any): URL {
2
+ if ('href' in options) {
3
+ return new URL(options.href)
4
+ }
5
+
6
+ const protocol = options.port === 443 ? 'https:' : 'http:'
7
+ const host = options.host
8
+
9
+ const url = new URL(`${protocol}//${host}`)
10
+
11
+ if (options.port) {
12
+ url.port = options.port.toString()
13
+ }
14
+
15
+ if (options.path) {
16
+ url.pathname = options.path
17
+ }
18
+
19
+ if (options.auth) {
20
+ const [username, password] = options.auth.split(':')
21
+ url.username = username
22
+ url.password = password
23
+ }
24
+
25
+ return url
26
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @vitest-environment node
3
+ */
4
+ import { it, expect } from 'vitest'
5
+ import { normalizeSocketWriteArgs } from './normalizeSocketWriteArgs'
6
+
7
+ it('normalizes .write()', () => {
8
+ expect(normalizeSocketWriteArgs([undefined])).toEqual([
9
+ undefined,
10
+ undefined,
11
+ undefined,
12
+ ])
13
+ expect(normalizeSocketWriteArgs([null])).toEqual([null, undefined, undefined])
14
+ })
15
+
16
+ it('normalizes .write(chunk)', () => {
17
+ expect(normalizeSocketWriteArgs([Buffer.from('hello')])).toEqual([
18
+ Buffer.from('hello'),
19
+ undefined,
20
+ undefined,
21
+ ])
22
+ expect(normalizeSocketWriteArgs(['hello'])).toEqual([
23
+ 'hello',
24
+ undefined,
25
+ undefined,
26
+ ])
27
+ expect(normalizeSocketWriteArgs([null])).toEqual([null, undefined, undefined])
28
+ })
29
+
30
+ it('normalizes .write(chunk, encoding)', () => {
31
+ expect(normalizeSocketWriteArgs([Buffer.from('hello'), 'utf8'])).toEqual([
32
+ Buffer.from('hello'),
33
+ 'utf8',
34
+ undefined,
35
+ ])
36
+ })
37
+
38
+ it('normalizes .write(chunk, callback)', () => {
39
+ const callback = () => {}
40
+ expect(normalizeSocketWriteArgs([Buffer.from('hello'), callback])).toEqual([
41
+ Buffer.from('hello'),
42
+ undefined,
43
+ callback,
44
+ ])
45
+ })
46
+
47
+ it('normalizes .write(chunk, encoding, callback)', () => {
48
+ const callback = () => {}
49
+ expect(
50
+ normalizeSocketWriteArgs([Buffer.from('hello'), 'utf8', callback])
51
+ ).toEqual([Buffer.from('hello'), 'utf8', callback])
52
+ })
@@ -0,0 +1,33 @@
1
+ export type WriteCallback = (error?: Error | null) => void
2
+
3
+ export type WriteArgs =
4
+ | [chunk: unknown, callback?: WriteCallback]
5
+ | [chunk: unknown, encoding: BufferEncoding, callback?: WriteCallback]
6
+
7
+ export type NormalizedSocketWriteArgs = [
8
+ chunk: any,
9
+ encoding?: BufferEncoding,
10
+ callback?: WriteCallback,
11
+ ]
12
+
13
+ /**
14
+ * Normalizes the arguments provided to the `Writable.prototype.write()`
15
+ * and `Writable.prototype.end()`.
16
+ */
17
+ export function normalizeSocketWriteArgs(
18
+ args: WriteArgs
19
+ ): NormalizedSocketWriteArgs {
20
+ const normalized: NormalizedSocketWriteArgs = [args[0], undefined, undefined]
21
+
22
+ if (typeof args[1] === 'string') {
23
+ normalized[1] = args[1]
24
+ } else if (typeof args[1] === 'function') {
25
+ normalized[2] = args[1]
26
+ }
27
+
28
+ if (typeof args[2] === 'function') {
29
+ normalized[2] = args[2]
30
+ }
31
+
32
+ return normalized
33
+ }
@@ -0,0 +1,116 @@
1
+ import { bindEvent } from './utils/bindEvent'
2
+ import {
3
+ StrictEventListenerOrEventListenerObject,
4
+ WebSocketData,
5
+ WebSocketTransport,
6
+ WebSocketTransportEventMap,
7
+ } from './WebSocketTransport'
8
+ import { kOnSend, kClose, WebSocketOverride } from './WebSocketOverride'
9
+ import { CancelableMessageEvent, CloseEvent } from './utils/events'
10
+
11
+ /**
12
+ * Abstraction over the given mock `WebSocket` instance that allows
13
+ * for controlling that instance (e.g. sending and receiving messages).
14
+ */
15
+ export class WebSocketClassTransport
16
+ extends EventTarget
17
+ implements WebSocketTransport
18
+ {
19
+ constructor(protected readonly socket: WebSocketOverride) {
20
+ super()
21
+
22
+ // Emit the "close" event on the transport if the close
23
+ // originates from the WebSocket client. E.g. the application
24
+ // calls "ws.close()", not the interceptor.
25
+ this.socket.addEventListener('close', (event) => {
26
+ this.dispatchEvent(bindEvent(this.socket, new CloseEvent('close', event)))
27
+ })
28
+
29
+ /**
30
+ * Emit the "outgoing" event on the transport
31
+ * whenever the WebSocket client sends data ("ws.send()").
32
+ */
33
+ this.socket[kOnSend] = (data) => {
34
+ this.dispatchEvent(
35
+ bindEvent(
36
+ this.socket,
37
+ // Dispatch this as cancelable because "client" connection
38
+ // re-creates this message event (cannot dispatch the same event).
39
+ new CancelableMessageEvent('outgoing', {
40
+ data,
41
+ origin: this.socket.url,
42
+ cancelable: true,
43
+ })
44
+ )
45
+ )
46
+ }
47
+ }
48
+
49
+ public addEventListener<EventType extends keyof WebSocketTransportEventMap>(
50
+ type: EventType,
51
+ callback: StrictEventListenerOrEventListenerObject<
52
+ WebSocketTransportEventMap[EventType]
53
+ > | null,
54
+ options?: boolean | AddEventListenerOptions
55
+ ): void {
56
+ return super.addEventListener(type, callback as EventListener, options)
57
+ }
58
+
59
+ public dispatchEvent<EventType extends keyof WebSocketTransportEventMap>(
60
+ event: WebSocketTransportEventMap[EventType]
61
+ ): boolean {
62
+ return super.dispatchEvent(event)
63
+ }
64
+
65
+ public send(data: WebSocketData): void {
66
+ queueMicrotask(() => {
67
+ if (
68
+ this.socket.readyState === this.socket.CLOSING ||
69
+ this.socket.readyState === this.socket.CLOSED
70
+ ) {
71
+ return
72
+ }
73
+
74
+ const dispatchEvent = () => {
75
+ this.socket.dispatchEvent(
76
+ bindEvent(
77
+ /**
78
+ * @note Setting this event's "target" to the
79
+ * WebSocket override instance is important.
80
+ * This way it can tell apart original incoming events
81
+ * (must be forwarded to the transport) from the
82
+ * mocked message events like the one below
83
+ * (must be dispatched on the client instance).
84
+ */
85
+ this.socket,
86
+ new MessageEvent('message', {
87
+ data,
88
+ origin: this.socket.url,
89
+ })
90
+ )
91
+ )
92
+ }
93
+
94
+ if (this.socket.readyState === this.socket.CONNECTING) {
95
+ this.socket.addEventListener(
96
+ 'open',
97
+ () => {
98
+ dispatchEvent()
99
+ },
100
+ { once: true }
101
+ )
102
+ } else {
103
+ dispatchEvent()
104
+ }
105
+ })
106
+ }
107
+
108
+ public close(code: number, reason?: string): void {
109
+ /**
110
+ * @note Call the internal close method directly
111
+ * to allow closing the connection with the status codes
112
+ * that are non-configurable by the user (> 1000 <= 1015).
113
+ */
114
+ this.socket[kClose](code, reason)
115
+ }
116
+ }
@@ -0,0 +1,152 @@
1
+ import type { WebSocketData, WebSocketTransport } from './WebSocketTransport'
2
+ import type { WebSocketEventListener } from './WebSocketOverride'
3
+ import { bindEvent } from './utils/bindEvent'
4
+ import { CancelableMessageEvent, CloseEvent } from './utils/events'
5
+ import { createRequestId } from '../../createRequestId'
6
+
7
+ const kEmitter = Symbol('kEmitter')
8
+ const kBoundListener = Symbol('kBoundListener')
9
+
10
+ export interface WebSocketClientEventMap {
11
+ message: MessageEvent<WebSocketData>
12
+ close: CloseEvent
13
+ }
14
+
15
+ export abstract class WebSocketClientConnectionProtocol {
16
+ abstract id: string
17
+ abstract url: URL
18
+ public abstract send(data: WebSocketData): void
19
+ public abstract close(code?: number, reason?: string): void
20
+
21
+ public abstract addEventListener<
22
+ EventType extends keyof WebSocketClientEventMap,
23
+ >(
24
+ type: EventType,
25
+ listener: WebSocketEventListener<WebSocketClientEventMap[EventType]>,
26
+ options?: AddEventListenerOptions | boolean
27
+ ): void
28
+
29
+ public abstract removeEventListener<
30
+ EventType extends keyof WebSocketClientEventMap,
31
+ >(
32
+ event: EventType,
33
+ listener: WebSocketEventListener<WebSocketClientEventMap[EventType]>,
34
+ options?: EventListenerOptions | boolean
35
+ ): void
36
+ }
37
+
38
+ /**
39
+ * The WebSocket client instance represents an incoming
40
+ * client connection. The user can control the connection,
41
+ * send and receive events.
42
+ */
43
+ export class WebSocketClientConnection implements WebSocketClientConnectionProtocol {
44
+ public readonly id: string
45
+ public readonly url: URL
46
+
47
+ private [kEmitter]: EventTarget
48
+
49
+ constructor(
50
+ public readonly socket: WebSocket,
51
+ private readonly transport: WebSocketTransport
52
+ ) {
53
+ this.id = createRequestId()
54
+ this.url = new URL(socket.url)
55
+ this[kEmitter] = new EventTarget()
56
+
57
+ // Emit outgoing client data ("ws.send()") as "message"
58
+ // events on the "client" connection.
59
+ this.transport.addEventListener('outgoing', (event) => {
60
+ const message = bindEvent(
61
+ this.socket,
62
+ new CancelableMessageEvent('message', {
63
+ data: event.data,
64
+ origin: event.origin,
65
+ cancelable: true,
66
+ })
67
+ )
68
+
69
+ this[kEmitter].dispatchEvent(message)
70
+
71
+ // This is a bit silly but forward the cancellation state
72
+ // of the "client" message event to the "outgoing" transport event.
73
+ // This way, other agens (like "server" connection) can know
74
+ // whether the client listener has pervented the default.
75
+ if (message.defaultPrevented) {
76
+ event.preventDefault()
77
+ }
78
+ })
79
+
80
+ /**
81
+ * Emit the "close" event on the "client" connection
82
+ * whenever the underlying transport is closed.
83
+ * @note "client.close()" does NOT dispatch the "close"
84
+ * event on the WebSocket because it uses non-configurable
85
+ * close status code. Thus, we listen to the transport
86
+ * instead of the WebSocket's "close" event.
87
+ */
88
+ this.transport.addEventListener('close', (event) => {
89
+ this[kEmitter].dispatchEvent(
90
+ bindEvent(this.socket, new CloseEvent('close', event))
91
+ )
92
+ })
93
+ }
94
+
95
+ /**
96
+ * Listen for the outgoing events from the connected WebSocket client.
97
+ */
98
+ public addEventListener<EventType extends keyof WebSocketClientEventMap>(
99
+ type: EventType,
100
+ listener: WebSocketEventListener<WebSocketClientEventMap[EventType]>,
101
+ options?: AddEventListenerOptions | boolean
102
+ ): void {
103
+ if (!Reflect.has(listener, kBoundListener)) {
104
+ const boundListener = listener.bind(this.socket)
105
+
106
+ // Store the bound listener on the original listener
107
+ // so the exact bound function can be accessed in "removeEventListener()".
108
+ Object.defineProperty(listener, kBoundListener, {
109
+ value: boundListener,
110
+ enumerable: false,
111
+ configurable: false,
112
+ })
113
+ }
114
+
115
+ this[kEmitter].addEventListener(
116
+ type,
117
+ Reflect.get(listener, kBoundListener) as EventListener,
118
+ options
119
+ )
120
+ }
121
+
122
+ /**
123
+ * Removes the listener for the given event.
124
+ */
125
+ public removeEventListener<EventType extends keyof WebSocketClientEventMap>(
126
+ event: EventType,
127
+ listener: WebSocketEventListener<WebSocketClientEventMap[EventType]>,
128
+ options?: EventListenerOptions | boolean
129
+ ): void {
130
+ this[kEmitter].removeEventListener(
131
+ event,
132
+ Reflect.get(listener, kBoundListener) as EventListener,
133
+ options
134
+ )
135
+ }
136
+
137
+ /**
138
+ * Send data to the connected client.
139
+ */
140
+ public send(data: WebSocketData): void {
141
+ this.transport.send(data)
142
+ }
143
+
144
+ /**
145
+ * Close the WebSocket connection.
146
+ * @param {number} code A status code (see https://www.rfc-editor.org/rfc/rfc6455#section-7.4.1).
147
+ * @param {string} reason A custom connection close reason.
148
+ */
149
+ public close(code?: number, reason?: string): void {
150
+ this.transport.close(code, reason)
151
+ }
152
+ }