msw 2.12.13 → 2.13.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 (272) hide show
  1. package/lib/browser/index.d.mts +29 -19
  2. package/lib/browser/index.d.ts +29 -19
  3. package/lib/browser/index.js +1763 -1321
  4. package/lib/browser/index.js.map +1 -1
  5. package/lib/browser/index.mjs +1769 -1321
  6. package/lib/browser/index.mjs.map +1 -1
  7. package/lib/core/{HttpResponse-Dj6ibgFJ.d.ts → HttpResponse-CksOMVAa.d.ts} +5 -5
  8. package/lib/core/{HttpResponse-Be4eT3x6.d.mts → HttpResponse-DlRR1D-f.d.mts} +5 -5
  9. package/lib/core/HttpResponse.d.mts +1 -1
  10. package/lib/core/HttpResponse.d.ts +1 -1
  11. package/lib/core/experimental/compat.d.mts +17 -0
  12. package/lib/core/experimental/compat.d.ts +17 -0
  13. package/lib/core/experimental/compat.js +54 -0
  14. package/lib/core/experimental/compat.js.map +1 -0
  15. package/lib/core/experimental/compat.mjs +36 -0
  16. package/lib/core/experimental/compat.mjs.map +1 -0
  17. package/lib/core/experimental/define-network.d.mts +75 -0
  18. package/lib/core/experimental/define-network.d.ts +75 -0
  19. package/lib/core/experimental/define-network.js +124 -0
  20. package/lib/core/experimental/define-network.js.map +1 -0
  21. package/lib/core/experimental/define-network.mjs +107 -0
  22. package/lib/core/experimental/define-network.mjs.map +1 -0
  23. package/lib/core/experimental/frames/http-frame.d.mts +77 -0
  24. package/lib/core/experimental/frames/http-frame.d.ts +77 -0
  25. package/lib/core/experimental/frames/http-frame.js +194 -0
  26. package/lib/core/experimental/frames/http-frame.js.map +1 -0
  27. package/lib/core/experimental/frames/http-frame.mjs +176 -0
  28. package/lib/core/experimental/frames/http-frame.mjs.map +1 -0
  29. package/lib/core/experimental/frames/network-frame.d.mts +12 -0
  30. package/lib/core/experimental/frames/network-frame.d.ts +12 -0
  31. package/lib/core/{handlers/common.js → experimental/frames/network-frame.js} +19 -3
  32. package/lib/core/experimental/frames/network-frame.js.map +1 -0
  33. package/lib/core/experimental/frames/network-frame.mjs +13 -0
  34. package/lib/core/experimental/frames/network-frame.mjs.map +1 -0
  35. package/lib/core/experimental/frames/websocket-frame.d.mts +55 -0
  36. package/lib/core/experimental/frames/websocket-frame.d.ts +55 -0
  37. package/lib/core/experimental/frames/websocket-frame.js +129 -0
  38. package/lib/core/experimental/frames/websocket-frame.js.map +1 -0
  39. package/lib/core/experimental/frames/websocket-frame.mjs +116 -0
  40. package/lib/core/experimental/frames/websocket-frame.mjs.map +1 -0
  41. package/lib/core/experimental/handlers-controller.d.mts +35 -0
  42. package/lib/core/experimental/handlers-controller.d.ts +35 -0
  43. package/lib/core/experimental/handlers-controller.js +121 -0
  44. package/lib/core/experimental/handlers-controller.js.map +1 -0
  45. package/lib/core/experimental/handlers-controller.mjs +101 -0
  46. package/lib/core/experimental/handlers-controller.mjs.map +1 -0
  47. package/lib/core/experimental/index.d.mts +17 -0
  48. package/lib/core/experimental/index.d.ts +17 -0
  49. package/lib/core/experimental/index.js +36 -0
  50. package/lib/core/experimental/index.js.map +1 -0
  51. package/lib/core/experimental/index.mjs +20 -0
  52. package/lib/core/experimental/index.mjs.map +1 -0
  53. package/lib/core/experimental/on-unhandled-frame.d.mts +12 -0
  54. package/lib/core/experimental/on-unhandled-frame.d.ts +12 -0
  55. package/lib/core/experimental/on-unhandled-frame.js +90 -0
  56. package/lib/core/experimental/on-unhandled-frame.js.map +1 -0
  57. package/lib/core/experimental/on-unhandled-frame.mjs +70 -0
  58. package/lib/core/experimental/on-unhandled-frame.mjs.map +1 -0
  59. package/lib/core/experimental/request-utils.d.mts +12 -0
  60. package/lib/core/experimental/request-utils.d.ts +12 -0
  61. package/lib/core/experimental/request-utils.js +50 -0
  62. package/lib/core/experimental/request-utils.js.map +1 -0
  63. package/lib/core/experimental/request-utils.mjs +30 -0
  64. package/lib/core/experimental/request-utils.mjs.map +1 -0
  65. package/lib/core/experimental/setup-api.d.mts +33 -0
  66. package/lib/core/experimental/setup-api.d.ts +33 -0
  67. package/lib/core/experimental/setup-api.js +61 -0
  68. package/lib/core/experimental/setup-api.js.map +1 -0
  69. package/lib/core/experimental/setup-api.mjs +43 -0
  70. package/lib/core/experimental/setup-api.mjs.map +1 -0
  71. package/lib/core/experimental/sources/interceptor-source.d.mts +28 -0
  72. package/lib/core/experimental/sources/interceptor-source.d.ts +28 -0
  73. package/lib/core/experimental/sources/interceptor-source.js +142 -0
  74. package/lib/core/experimental/sources/interceptor-source.js.map +1 -0
  75. package/lib/core/experimental/sources/interceptor-source.mjs +124 -0
  76. package/lib/core/experimental/sources/interceptor-source.mjs.map +1 -0
  77. package/lib/core/experimental/sources/network-source.d.mts +31 -0
  78. package/lib/core/experimental/sources/network-source.d.ts +31 -0
  79. package/lib/core/experimental/sources/network-source.js +50 -0
  80. package/lib/core/experimental/sources/network-source.js.map +1 -0
  81. package/lib/core/experimental/sources/network-source.mjs +30 -0
  82. package/lib/core/experimental/sources/network-source.mjs.map +1 -0
  83. package/lib/core/getResponse.d.mts +1 -1
  84. package/lib/core/getResponse.d.ts +1 -1
  85. package/lib/core/graphql.d.mts +1 -1
  86. package/lib/core/graphql.d.ts +1 -1
  87. package/lib/core/handlers/GraphQLHandler.d.mts +1 -1
  88. package/lib/core/handlers/GraphQLHandler.d.ts +1 -1
  89. package/lib/core/handlers/HttpHandler.d.mts +1 -1
  90. package/lib/core/handlers/HttpHandler.d.ts +1 -1
  91. package/lib/core/handlers/RequestHandler.d.mts +1 -1
  92. package/lib/core/handlers/RequestHandler.d.ts +1 -1
  93. package/lib/core/handlers/RequestHandler.js +5 -6
  94. package/lib/core/handlers/RequestHandler.js.map +1 -1
  95. package/lib/core/handlers/RequestHandler.mjs +5 -6
  96. package/lib/core/handlers/RequestHandler.mjs.map +1 -1
  97. package/lib/core/handlers/WebSocketHandler.d.mts +8 -4
  98. package/lib/core/handlers/WebSocketHandler.d.ts +8 -4
  99. package/lib/core/handlers/WebSocketHandler.js +18 -5
  100. package/lib/core/handlers/WebSocketHandler.js.map +1 -1
  101. package/lib/core/handlers/WebSocketHandler.mjs +18 -5
  102. package/lib/core/handlers/WebSocketHandler.mjs.map +1 -1
  103. package/lib/core/http.d.mts +1 -1
  104. package/lib/core/http.d.ts +1 -1
  105. package/lib/core/index.d.mts +7 -12
  106. package/lib/core/index.d.ts +7 -12
  107. package/lib/core/index.js +2 -2
  108. package/lib/core/index.js.map +1 -1
  109. package/lib/core/index.mjs +1 -1
  110. package/lib/core/index.mjs.map +1 -1
  111. package/lib/core/network-frame-B7A0ggXE.d.mts +56 -0
  112. package/lib/core/network-frame-usYiHS0K.d.ts +56 -0
  113. package/lib/core/passthrough.d.mts +1 -1
  114. package/lib/core/passthrough.d.ts +1 -1
  115. package/lib/core/sharedOptions.d.mts +6 -2
  116. package/lib/core/sharedOptions.d.ts +6 -2
  117. package/lib/core/sharedOptions.js.map +1 -1
  118. package/lib/core/sse.d.mts +1 -1
  119. package/lib/core/sse.d.ts +1 -1
  120. package/lib/core/sse.js.map +1 -1
  121. package/lib/core/sse.mjs.map +1 -1
  122. package/lib/core/utils/HttpResponse/decorators.d.mts +1 -1
  123. package/lib/core/utils/HttpResponse/decorators.d.ts +1 -1
  124. package/lib/core/utils/cookieStore.js.map +1 -1
  125. package/lib/core/utils/cookieStore.mjs.map +1 -1
  126. package/lib/core/utils/executeHandlers.d.mts +1 -1
  127. package/lib/core/utils/executeHandlers.d.ts +1 -1
  128. package/lib/core/utils/handleRequest.d.mts +2 -1
  129. package/lib/core/utils/handleRequest.d.ts +2 -1
  130. package/lib/core/utils/internal/isHandlerKind.d.mts +3 -3
  131. package/lib/core/utils/internal/isHandlerKind.d.ts +3 -3
  132. package/lib/core/utils/internal/isHandlerKind.js +2 -1
  133. package/lib/core/utils/internal/isHandlerKind.js.map +1 -1
  134. package/lib/core/utils/internal/isHandlerKind.mjs +2 -1
  135. package/lib/core/utils/internal/isHandlerKind.mjs.map +1 -1
  136. package/lib/core/utils/internal/parseGraphQLRequest.d.mts +1 -1
  137. package/lib/core/utils/internal/parseGraphQLRequest.d.ts +1 -1
  138. package/lib/core/utils/internal/parseMultipartData.d.mts +1 -1
  139. package/lib/core/utils/internal/parseMultipartData.d.ts +1 -1
  140. package/lib/core/utils/internal/requestHandlerUtils.d.mts +1 -1
  141. package/lib/core/utils/internal/requestHandlerUtils.d.ts +1 -1
  142. package/lib/core/utils/matching/matchRequestUrl.js +1 -1
  143. package/lib/core/utils/matching/matchRequestUrl.js.map +1 -1
  144. package/lib/core/utils/matching/matchRequestUrl.mjs +1 -1
  145. package/lib/core/utils/matching/matchRequestUrl.mjs.map +1 -1
  146. package/lib/core/utils/request/onUnhandledRequest.d.mts +2 -2
  147. package/lib/core/utils/request/onUnhandledRequest.d.ts +2 -2
  148. package/lib/core/utils/request/onUnhandledRequest.js.map +1 -1
  149. package/lib/core/utils/request/onUnhandledRequest.mjs.map +1 -1
  150. package/lib/core/ws/handleWebSocketEvent.d.mts +1 -1
  151. package/lib/core/ws/handleWebSocketEvent.d.ts +1 -1
  152. package/lib/core/ws/handleWebSocketEvent.js +1 -1
  153. package/lib/core/ws/handleWebSocketEvent.js.map +1 -1
  154. package/lib/core/ws/handleWebSocketEvent.mjs +1 -1
  155. package/lib/core/ws/handleWebSocketEvent.mjs.map +1 -1
  156. package/lib/core/ws/utils/attachWebSocketLogger.d.mts +1 -1
  157. package/lib/core/ws/utils/attachWebSocketLogger.d.ts +1 -1
  158. package/lib/core/ws/utils/attachWebSocketLogger.js +39 -10
  159. package/lib/core/ws/utils/attachWebSocketLogger.js.map +1 -1
  160. package/lib/core/ws/utils/attachWebSocketLogger.mjs +39 -10
  161. package/lib/core/ws/utils/attachWebSocketLogger.mjs.map +1 -1
  162. package/lib/core/ws.d.mts +3 -3
  163. package/lib/core/ws.d.ts +3 -3
  164. package/lib/core/ws.js.map +1 -1
  165. package/lib/core/ws.mjs.map +1 -1
  166. package/lib/iife/index.js +2022 -1433
  167. package/lib/iife/index.js.map +1 -1
  168. package/lib/mockServiceWorker.js +1 -1
  169. package/lib/native/index.d.mts +21 -29
  170. package/lib/native/index.d.ts +21 -29
  171. package/lib/native/index.js +48 -116
  172. package/lib/native/index.js.map +1 -1
  173. package/lib/native/index.mjs +51 -118
  174. package/lib/native/index.mjs.map +1 -1
  175. package/lib/node/index.d.mts +55 -33
  176. package/lib/node/index.d.ts +55 -33
  177. package/lib/node/index.js +152 -154
  178. package/lib/node/index.js.map +1 -1
  179. package/lib/node/index.mjs +156 -156
  180. package/lib/node/index.mjs.map +1 -1
  181. package/package.json +10 -2
  182. package/src/browser/{setupWorker/glossary.ts → glossary.ts} +16 -33
  183. package/src/browser/index.ts +2 -3
  184. package/src/browser/{setupWorker/setupWorker.node.test.ts → setup-worker.node.test.ts} +2 -4
  185. package/src/browser/setup-worker.ts +148 -0
  186. package/src/browser/sources/fallback-http-source.ts +56 -0
  187. package/src/browser/sources/service-worker-source.ts +455 -0
  188. package/src/browser/tsconfig.browser.json +7 -2
  189. package/src/browser/utils/deserializeRequest.ts +1 -1
  190. package/src/browser/{setupWorker/start/utils/getWorkerByRegistration.ts → utils/get-worker-by-registration.ts} +3 -1
  191. package/src/browser/{setupWorker/start/utils/getWorkerInstance.ts → utils/get-worker-instance.ts} +4 -4
  192. package/src/browser/utils/pruneGetRequestBody.test.ts +1 -3
  193. package/src/browser/utils/pruneGetRequestBody.ts +1 -1
  194. package/src/browser/utils/validate-worker-scope.ts +19 -0
  195. package/src/browser/utils/workerChannel.ts +2 -2
  196. package/src/core/experimental/compat.ts +50 -0
  197. package/src/core/experimental/define-network.test.ts +124 -0
  198. package/src/core/experimental/define-network.ts +215 -0
  199. package/src/core/experimental/frames/http-frame.test.ts +360 -0
  200. package/src/core/experimental/frames/http-frame.ts +271 -0
  201. package/src/core/experimental/frames/network-frame.ts +64 -0
  202. package/src/core/experimental/frames/websocket-frame.test.ts +280 -0
  203. package/src/core/experimental/frames/websocket-frame.ts +188 -0
  204. package/src/core/experimental/handlers-controller.test.ts +198 -0
  205. package/src/core/experimental/handlers-controller.ts +145 -0
  206. package/src/core/experimental/index.ts +16 -0
  207. package/src/core/experimental/on-unhandled-frame.test.ts +360 -0
  208. package/src/core/experimental/on-unhandled-frame.ts +110 -0
  209. package/src/core/experimental/request-utils.test.ts +70 -0
  210. package/src/core/experimental/request-utils.ts +39 -0
  211. package/src/core/experimental/setup-api.ts +59 -0
  212. package/src/core/experimental/sources/interceptor-source.ts +185 -0
  213. package/src/core/experimental/sources/network-source.test.ts +74 -0
  214. package/src/core/experimental/sources/network-source.ts +56 -0
  215. package/src/core/handlers/RequestHandler.ts +9 -10
  216. package/src/core/handlers/WebSocketHandler.ts +27 -11
  217. package/src/core/index.ts +3 -7
  218. package/src/core/sharedOptions.ts +9 -4
  219. package/src/core/sse.ts +1 -1
  220. package/src/core/utils/cookieStore.ts +2 -1
  221. package/src/core/utils/internal/isHandlerKind.test.ts +20 -22
  222. package/src/core/utils/internal/isHandlerKind.ts +5 -9
  223. package/src/core/utils/matching/matchRequestUrl.test.ts +87 -3
  224. package/src/core/utils/matching/matchRequestUrl.ts +2 -2
  225. package/src/core/utils/request/onUnhandledRequest.ts +2 -2
  226. package/src/core/ws/WebSocketClientManager.test.ts +2 -10
  227. package/src/core/ws/handleWebSocketEvent.ts +5 -1
  228. package/src/core/ws/utils/attachWebSocketLogger.ts +43 -11
  229. package/src/core/ws.test.ts +1 -3
  230. package/src/core/ws.ts +6 -6
  231. package/src/iife/index.ts +1 -1
  232. package/src/native/index.ts +34 -11
  233. package/src/node/async-handlers-controller.test.ts +50 -0
  234. package/src/node/async-handlers-controller.ts +69 -0
  235. package/src/node/glossary.ts +19 -18
  236. package/src/node/index.ts +6 -2
  237. package/src/node/setup-server-common.ts +100 -0
  238. package/src/node/setup-server.ts +91 -0
  239. package/src/tsconfig.core.json +8 -0
  240. package/src/tsconfig.node.json +8 -3
  241. package/src/tsconfig.src.json +0 -2
  242. package/src/tsconfig.worker.json +2 -1
  243. package/lib/core/SetupApi.d.mts +0 -44
  244. package/lib/core/SetupApi.d.ts +0 -44
  245. package/lib/core/SetupApi.js +0 -112
  246. package/lib/core/SetupApi.js.map +0 -1
  247. package/lib/core/SetupApi.mjs +0 -92
  248. package/lib/core/SetupApi.mjs.map +0 -1
  249. package/lib/core/handlers/common.d.mts +0 -3
  250. package/lib/core/handlers/common.d.ts +0 -3
  251. package/lib/core/handlers/common.js.map +0 -1
  252. package/lib/core/handlers/common.mjs +0 -1
  253. package/lib/core/handlers/common.mjs.map +0 -1
  254. package/src/browser/setupWorker/setupWorker.ts +0 -184
  255. package/src/browser/setupWorker/start/createFallbackRequestListener.ts +0 -71
  256. package/src/browser/setupWorker/start/createRequestListener.ts +0 -138
  257. package/src/browser/setupWorker/start/createResponseListener.ts +0 -57
  258. package/src/browser/setupWorker/start/createStartHandler.ts +0 -137
  259. package/src/browser/setupWorker/start/utils/enableMocking.ts +0 -30
  260. package/src/browser/setupWorker/start/utils/prepareStartHandler.test.ts +0 -59
  261. package/src/browser/setupWorker/start/utils/prepareStartHandler.ts +0 -44
  262. package/src/browser/setupWorker/start/utils/printStartMessage.test.ts +0 -84
  263. package/src/browser/setupWorker/start/utils/printStartMessage.ts +0 -51
  264. package/src/browser/setupWorker/start/utils/validateWorkerScope.ts +0 -18
  265. package/src/browser/setupWorker/stop/utils/printStopMessage.test.ts +0 -26
  266. package/src/browser/setupWorker/stop/utils/printStopMessage.ts +0 -13
  267. package/src/browser/utils/checkWorkerIntegrity.ts +0 -42
  268. package/src/core/SetupApi.ts +0 -127
  269. package/src/core/handlers/common.ts +0 -1
  270. package/src/node/SetupServerApi.ts +0 -87
  271. package/src/node/SetupServerCommonApi.ts +0 -169
  272. package/src/node/setupServer.ts +0 -15
@@ -0,0 +1,280 @@
1
+ import { http } from '../../http'
2
+ import { graphql } from '../../graphql'
3
+ import { ws } from '../../ws'
4
+ import {
5
+ WebSocketNetworkFrame,
6
+ WebSocketNetworkFrameEventMap,
7
+ } from './websocket-frame'
8
+ import { createTestWebSocketConnection } from '../../../../test/support/ws-test-utils'
9
+ import { InMemoryHandlersController } from '#core/experimental/handlers-controller'
10
+
11
+ beforeAll(() => {
12
+ vi.spyOn(console, 'error').mockImplementation(() => {})
13
+ })
14
+
15
+ afterEach(() => {
16
+ vi.clearAllMocks()
17
+ })
18
+
19
+ afterAll(() => {
20
+ vi.restoreAllMocks()
21
+ })
22
+
23
+ function spyOnWebSocketFrame(frame: WebSocketNetworkFrame) {
24
+ const events: Array<
25
+ WebSocketNetworkFrameEventMap[keyof WebSocketNetworkFrameEventMap]
26
+ > = []
27
+
28
+ frame.events.on('*', (event) => events.push(event))
29
+
30
+ return {
31
+ events,
32
+ }
33
+ }
34
+
35
+ it('filters only websocket type handlers', async () => {
36
+ class WebSocketFrame extends WebSocketNetworkFrame {
37
+ passthrough = vi.fn()
38
+ errorWith = vi.fn()
39
+ }
40
+
41
+ const connection = createTestWebSocketConnection('ws://localhost/api')
42
+ const frame = new WebSocketFrame({ connection })
43
+
44
+ const httpHandlers = [http.post('http://localhost/api/user', () => {})]
45
+ const graphqlHandlers = [graphql.query('GetUser', () => {})]
46
+ const webSocketHandlers = [
47
+ ws.link('ws://localhost').addEventListener('connection', () => {}),
48
+ ]
49
+
50
+ const controller = new InMemoryHandlersController([
51
+ ...httpHandlers,
52
+ ...webSocketHandlers,
53
+ ...graphqlHandlers,
54
+ ])
55
+
56
+ expect(frame.getHandlers(controller)).toEqual(webSocketHandlers)
57
+ expect(frame.getHandlers(new InMemoryHandlersController([]))).toEqual([])
58
+ })
59
+
60
+ it('resolves a matching connection', async () => {
61
+ class WebSocketFrame extends WebSocketNetworkFrame {
62
+ passthrough = vi.fn()
63
+ errorWith = vi.fn()
64
+ }
65
+
66
+ const connection = createTestWebSocketConnection('ws://localhost/api')
67
+ const frame = new WebSocketFrame({ connection })
68
+ const { events } = spyOnWebSocketFrame(frame)
69
+
70
+ const connectionListener = vi.fn()
71
+ const unhandledFrameCallback = vi.fn()
72
+
73
+ const api = ws.link('ws://localhost/api')
74
+ const matches = await frame.resolve(
75
+ [api.addEventListener('connection', connectionListener)],
76
+ unhandledFrameCallback,
77
+ { quiet: true },
78
+ )
79
+
80
+ expect.soft(matches).toBe(true)
81
+ expect
82
+ .soft(connectionListener)
83
+ .toHaveBeenCalledExactlyOnceWith(expect.objectContaining(connection))
84
+ expect.soft(frame.passthrough).not.toHaveBeenCalled()
85
+ expect.soft(frame.errorWith).not.toHaveBeenCalled()
86
+ expect.soft(unhandledFrameCallback).not.toHaveBeenCalled()
87
+ expect.soft(events).toEqual([
88
+ expect.objectContaining({
89
+ type: 'connection',
90
+ url: connection.client.url,
91
+ protocols: connection.info.protocols,
92
+ }),
93
+ ])
94
+ })
95
+
96
+ it('resolves a connection when there are no handlers', async () => {
97
+ class WebSocketFrame extends WebSocketNetworkFrame {
98
+ passthrough = vi.fn()
99
+ errorWith = vi.fn()
100
+ }
101
+
102
+ const connection = createTestWebSocketConnection('ws://localhost/api')
103
+ const frame = new WebSocketFrame({ connection })
104
+ const { events } = spyOnWebSocketFrame(frame)
105
+
106
+ const connectionListener = vi.fn()
107
+ const unhandledFrameCallback = vi.fn()
108
+
109
+ const matches = await frame.resolve([], unhandledFrameCallback, {
110
+ quiet: true,
111
+ })
112
+
113
+ expect.soft(matches).toBe(false)
114
+ expect.soft(frame.passthrough).toHaveBeenCalledOnce()
115
+ expect.soft(connectionListener).not.toHaveBeenCalled()
116
+ expect.soft(frame.errorWith).not.toHaveBeenCalled()
117
+ expect.soft(unhandledFrameCallback).toHaveBeenCalledExactlyOnceWith(
118
+ expect.objectContaining({
119
+ frame,
120
+ }),
121
+ )
122
+ expect.soft(events).toEqual([
123
+ expect.objectContaining({
124
+ type: 'connection',
125
+ url: connection.client.url,
126
+ protocols: connection.info.protocols,
127
+ }),
128
+ ])
129
+ })
130
+
131
+ it('resolves a non-matching connection', async () => {
132
+ class WebSocketFrame extends WebSocketNetworkFrame {
133
+ passthrough = vi.fn()
134
+ errorWith = vi.fn()
135
+ }
136
+
137
+ const connection = createTestWebSocketConnection('ws://localhost/api')
138
+ const frame = new WebSocketFrame({ connection })
139
+ const { events } = spyOnWebSocketFrame(frame)
140
+
141
+ const connectionListener = vi.fn()
142
+ const unhandledFrameCallback = vi.fn()
143
+
144
+ const api = ws.link('ws://example.com/api')
145
+ const matches = await frame.resolve(
146
+ [api.addEventListener('connection', connectionListener)],
147
+ unhandledFrameCallback,
148
+ { quiet: true },
149
+ )
150
+
151
+ expect.soft(matches).toBe(false)
152
+ expect.soft(frame.passthrough).toHaveBeenCalledOnce()
153
+ expect.soft(connectionListener).not.toHaveBeenCalled()
154
+ expect.soft(frame.errorWith).not.toHaveBeenCalled()
155
+ expect.soft(unhandledFrameCallback).toHaveBeenCalledExactlyOnceWith(
156
+ expect.objectContaining({
157
+ frame,
158
+ }),
159
+ )
160
+ expect.soft(events).toEqual([
161
+ expect.objectContaining({
162
+ type: 'connection',
163
+ url: connection.client.url,
164
+ protocols: connection.info.protocols,
165
+ }),
166
+ ])
167
+ })
168
+
169
+ it('returns null and prints the error on unhandled exception', async () => {
170
+ class WebSocketFrame extends WebSocketNetworkFrame {
171
+ passthrough = vi.fn()
172
+ errorWith = vi.fn()
173
+ }
174
+
175
+ const connection = createTestWebSocketConnection('ws://localhost/api')
176
+ const frame = new WebSocketFrame({ connection })
177
+ const { events } = spyOnWebSocketFrame(frame)
178
+
179
+ const unhandledFrameCallback = vi.fn()
180
+
181
+ const api = ws.link('ws://localhost/api')
182
+ const exception = new Error('Unhandled exceptin')
183
+
184
+ await expect
185
+ .soft(
186
+ frame.resolve(
187
+ [
188
+ api.addEventListener('connection', () => {
189
+ throw exception
190
+ }),
191
+ ],
192
+ unhandledFrameCallback,
193
+ { quiet: true },
194
+ ),
195
+ )
196
+ .rejects.toThrow(exception)
197
+ expect.soft(frame.errorWith).not.toHaveBeenCalled()
198
+ expect.soft(frame.passthrough).not.toHaveBeenCalled()
199
+ expect.soft(unhandledFrameCallback).not.toHaveBeenCalled()
200
+ expect.soft(events).toEqual([
201
+ expect.objectContaining({
202
+ type: 'connection',
203
+ url: connection.client.url,
204
+ protocols: connection.info.protocols,
205
+ }),
206
+ expect.objectContaining({
207
+ type: 'unhandledException',
208
+ error: exception,
209
+ url: connection.client.url,
210
+ protocols: connection.info.protocols,
211
+ }),
212
+ ])
213
+
214
+ expect.soft(console.error).toHaveBeenCalledTimes(2)
215
+ expect.soft(console.error).toHaveBeenNthCalledWith(1, exception)
216
+ expect
217
+ .soft(console.error)
218
+ .toHaveBeenNthCalledWith(
219
+ 2,
220
+ '[MSW] Encountered an unhandled exception during the handler lookup for "ws://localhost/api". Please see the original error above.',
221
+ )
222
+ })
223
+
224
+ it('does not print an unhandled exception if the "unhandledException" listener is present', async () => {
225
+ class WebSocketFrame extends WebSocketNetworkFrame {
226
+ passthrough = vi.fn()
227
+ errorWith = vi.fn()
228
+ }
229
+
230
+ const connection = createTestWebSocketConnection('ws://localhost/api')
231
+ const frame = new WebSocketFrame({ connection })
232
+ const { events } = spyOnWebSocketFrame(frame)
233
+
234
+ const unhandledExceptionListener = vi.fn()
235
+ frame.events.on('unhandledException', unhandledExceptionListener)
236
+
237
+ const unhandledFrameCallback = vi.fn()
238
+
239
+ const api = ws.link('ws://localhost/api')
240
+ const exception = new Error('Unhandled exception')
241
+
242
+ await expect
243
+ .soft(
244
+ frame.resolve(
245
+ [
246
+ api.addEventListener('connection', () => {
247
+ throw exception
248
+ }),
249
+ ],
250
+ unhandledFrameCallback,
251
+ { quiet: true },
252
+ ),
253
+ )
254
+ .rejects.toThrow(exception)
255
+ expect.soft(frame.errorWith).not.toHaveBeenCalled()
256
+ expect.soft(frame.passthrough).not.toHaveBeenCalled()
257
+ expect.soft(unhandledFrameCallback).not.toHaveBeenCalled()
258
+ expect.soft(events).toEqual([
259
+ expect.objectContaining({
260
+ type: 'connection',
261
+ url: connection.client.url,
262
+ protocols: connection.info.protocols,
263
+ }),
264
+ expect.objectContaining({
265
+ type: 'unhandledException',
266
+ error: exception,
267
+ url: connection.client.url,
268
+ protocols: connection.info.protocols,
269
+ }),
270
+ ])
271
+
272
+ expect.soft(unhandledExceptionListener).toHaveBeenCalledExactlyOnceWith(
273
+ expect.objectContaining({
274
+ error: exception,
275
+ url: connection.client.url,
276
+ protocols: connection.info.protocols,
277
+ }),
278
+ )
279
+ expect.soft(console.error).not.toHaveBeenCalled()
280
+ })
@@ -0,0 +1,188 @@
1
+ import { TypedEvent } from 'rettime'
2
+ import { type WebSocketConnectionData } from '@mswjs/interceptors/WebSocket'
3
+ import {
4
+ kConnect,
5
+ kAutoConnect,
6
+ type WebSocketHandler,
7
+ } from '../../handlers/WebSocketHandler'
8
+ import {
9
+ NetworkFrame,
10
+ type NetworkFrameResolutionContext,
11
+ } from './network-frame'
12
+ import {
13
+ executeUnhandledFrameHandle,
14
+ UnhandledFrameHandle,
15
+ } from '../on-unhandled-frame'
16
+ import { devUtils } from '../../utils/internal/devUtils'
17
+ import { HandlersController, AnyHandler } from '../handlers-controller'
18
+
19
+ export interface WebSocketNetworkFrameOptions {
20
+ connection: WebSocketConnectionData
21
+ }
22
+
23
+ export type WebSocketNetworkFrameEventMap = {
24
+ connection: WebSocketConnectionEvent
25
+ unhandledException: UnhandledWebSocketExceptionEvent
26
+ }
27
+
28
+ class WebSocketConnectionEvent<
29
+ DataType extends {
30
+ url: URL
31
+ protocols: string | Array<string> | undefined
32
+ } = { url: URL; protocols: string | Array<string> | undefined },
33
+ ReturnType = void,
34
+ EventType extends string = string,
35
+ > extends TypedEvent<DataType, ReturnType, EventType> {
36
+ public readonly url: URL
37
+ public readonly protocols: string | Array<string> | undefined
38
+
39
+ constructor(type: EventType, data: DataType) {
40
+ super(...([type, {}] as any))
41
+ this.url = data.url
42
+ this.protocols = data.protocols
43
+ }
44
+ }
45
+
46
+ class UnhandledWebSocketExceptionEvent<
47
+ DataType extends {
48
+ url: URL
49
+ protocols: string | Array<string> | undefined
50
+ error: unknown
51
+ } = {
52
+ url: URL
53
+ protocols: string | Array<string> | undefined
54
+ error: unknown
55
+ },
56
+ ReturnType = void,
57
+ EventType extends string = string,
58
+ > extends TypedEvent<DataType, ReturnType, EventType> {
59
+ public readonly url: URL
60
+ public readonly protocols: string | Array<string> | undefined
61
+ public readonly error: unknown
62
+
63
+ constructor(type: EventType, data: DataType) {
64
+ super(...([type, {}] as any))
65
+ this.url = data.url
66
+ this.protocols = data.protocols
67
+ this.error = data.error
68
+ }
69
+ }
70
+
71
+ export abstract class WebSocketNetworkFrame extends NetworkFrame<
72
+ 'ws',
73
+ {
74
+ connection: WebSocketConnectionData
75
+ },
76
+ WebSocketNetworkFrameEventMap
77
+ > {
78
+ constructor(options: WebSocketNetworkFrameOptions) {
79
+ super('ws', {
80
+ connection: options.connection,
81
+ })
82
+ }
83
+
84
+ public getHandlers(controller: HandlersController): Array<AnyHandler> {
85
+ return controller.getHandlersByKind('websocket')
86
+ }
87
+
88
+ public async resolve(
89
+ handlers: Array<WebSocketHandler>,
90
+ onUnhandledFrame: UnhandledFrameHandle,
91
+ resolutionContext?: NetworkFrameResolutionContext,
92
+ ): Promise<boolean | null> {
93
+ const { connection } = this.data
94
+
95
+ this.events.emit(
96
+ new WebSocketConnectionEvent('connection', {
97
+ url: connection.client.url,
98
+ protocols: connection.info.protocols,
99
+ }),
100
+ )
101
+
102
+ // No WebSocket handlers defined.
103
+ if (handlers.length === 0) {
104
+ await executeUnhandledFrameHandle(this, onUnhandledFrame).then(
105
+ () => this.passthrough(),
106
+ (error) => this.errorWith(error),
107
+ )
108
+
109
+ return false
110
+ }
111
+
112
+ let hasMatchingHandlers = false
113
+
114
+ for (const handler of handlers) {
115
+ const handlerConnection = await handler.run(connection, {
116
+ baseUrl: resolutionContext?.baseUrl?.toString(),
117
+ /**
118
+ * @note Do not emit the "connection" event when running the handler.
119
+ * Use the run only to get the resolved connection object.
120
+ */
121
+ [kAutoConnect]: false,
122
+ })
123
+
124
+ if (!handlerConnection) {
125
+ continue
126
+ }
127
+
128
+ hasMatchingHandlers = true
129
+
130
+ /**
131
+ * @note Attach the WebSocket logger *before* emitting the "connection" event.
132
+ * Connection event listeners may perform actions that should be reflected in the logs
133
+ * (e.g. closing the connection immediately). If the logger is attached after the connection,
134
+ * those actions cannot be properly logged.
135
+ */
136
+ const removeLogger = !resolutionContext?.quiet
137
+ ? handler.log(connection)
138
+ : undefined
139
+
140
+ try {
141
+ if (!handler[kConnect](handlerConnection)) {
142
+ removeLogger?.()
143
+ }
144
+ } catch (error) {
145
+ if (
146
+ !this.events.emit(
147
+ new UnhandledWebSocketExceptionEvent('unhandledException', {
148
+ error,
149
+ url: connection.client.url,
150
+ protocols: connection.info.protocols,
151
+ }),
152
+ )
153
+ ) {
154
+ console.error(error)
155
+ devUtils.error(
156
+ 'Encountered an unhandled exception during the handler lookup for "%s". Please see the original error above.',
157
+ connection.client.url,
158
+ )
159
+ }
160
+
161
+ /**
162
+ * @note Throw the caught error so it gets picked up by WebSocketInterceptor.
163
+ * It's the interceptor who translates handler errors to WebSocket closures.
164
+ */
165
+ throw error
166
+ }
167
+ }
168
+
169
+ // No matching WebSocket handlers found.
170
+ if (!hasMatchingHandlers) {
171
+ await executeUnhandledFrameHandle(this, onUnhandledFrame).then(
172
+ () => this.passthrough(),
173
+ (error) => this.errorWith(error),
174
+ )
175
+
176
+ return false
177
+ }
178
+
179
+ return true
180
+ }
181
+
182
+ public async getUnhandledMessage(): Promise<string> {
183
+ const { connection } = this.data
184
+ const details = `\n\n \u2022 ${connection.client.url}\n\n`
185
+
186
+ return `intercepted a WebSocket connection without a matching event handler:${details}If you still wish to intercept this unhandled connection, please create an event handler for it.\nRead more: https://mswjs.io/docs/websocket`
187
+ }
188
+ }
@@ -0,0 +1,198 @@
1
+ import { http } from '../http'
2
+ import { graphql } from '../graphql'
3
+ import { ws } from '../ws'
4
+ import { InMemoryHandlersController } from './handlers-controller'
5
+
6
+ describe(InMemoryHandlersController.prototype.use, () => {
7
+ it('prepends a handler to an empty controller', () => {
8
+ const controller = new InMemoryHandlersController([])
9
+ const httpHandler = http.get('/', () => {})
10
+ controller.use([httpHandler])
11
+
12
+ expect(controller.currentHandlers()).toEqual([httpHandler])
13
+ expect(controller.getHandlersByKind('request')).toEqual([httpHandler])
14
+ })
15
+
16
+ it('prepends a single handler', () => {
17
+ const httpOne = http.get('/', () => {})
18
+ const httpTwo = http.get('/', () => {})
19
+
20
+ const controller = new InMemoryHandlersController([httpOne])
21
+ controller.use([httpTwo])
22
+
23
+ expect(controller.currentHandlers()).toEqual([httpTwo, httpOne])
24
+ expect(controller.getHandlersByKind('request')).toEqual([httpTwo, httpOne])
25
+ })
26
+
27
+ it('prepends multiple handlers', () => {
28
+ const httpOne = http.get('/', () => {})
29
+ const httpTwo = http.get('/', () => {})
30
+ const httpThree = http.get('/', () => {})
31
+
32
+ const controller = new InMemoryHandlersController([httpOne])
33
+
34
+ controller.use([httpTwo, httpThree])
35
+
36
+ expect(controller.currentHandlers()).toEqual([httpTwo, httpThree, httpOne])
37
+ expect(controller.getHandlersByKind('request')).toEqual([
38
+ httpTwo,
39
+ httpThree,
40
+ httpOne,
41
+ ])
42
+ })
43
+
44
+ it('preserves order of handlers', () => {
45
+ const httpOne = http.get('/', () => {})
46
+ const graphqlOne = graphql.query('', () => {})
47
+ const httpTwo = http.get('/', () => {})
48
+
49
+ const controller = new InMemoryHandlersController([httpOne])
50
+ controller.use([graphqlOne, httpTwo])
51
+
52
+ expect(controller.currentHandlers()).toEqual([graphqlOne, httpTwo, httpOne])
53
+ })
54
+ })
55
+
56
+ describe(InMemoryHandlersController.prototype.reset, () => {
57
+ it('resets to the initial handlers if called with an empty list', () => {
58
+ {
59
+ const controller = new InMemoryHandlersController([])
60
+ controller.reset([])
61
+ expect(controller.currentHandlers()).toEqual([])
62
+ }
63
+
64
+ {
65
+ const httpHandler = http.get('/', () => {})
66
+ const controller = new InMemoryHandlersController([httpHandler])
67
+
68
+ controller.reset([])
69
+ expect(controller.currentHandlers()).toEqual([httpHandler])
70
+ }
71
+ })
72
+
73
+ it('replaces the initial handlers if called with a list of handlers', () => {
74
+ const httpOne = http.get('/', () => {})
75
+ const httpTwo = http.get('/', () => {})
76
+ const controller = new InMemoryHandlersController([httpOne])
77
+
78
+ controller.reset([httpTwo])
79
+ expect(controller.currentHandlers()).toEqual([httpTwo])
80
+ })
81
+
82
+ it('resets the initial handlers after runtime handlers are applied', () => {
83
+ const httpOne = http.get('/', () => {})
84
+ const httpTwo = http.get('/', () => {})
85
+ const controller = new InMemoryHandlersController([])
86
+ controller.use([httpOne])
87
+
88
+ controller.reset([httpTwo])
89
+ expect(controller.currentHandlers()).toEqual([httpTwo])
90
+
91
+ controller.reset([])
92
+ /**
93
+ * @note There's no way to "clear" the initial state via ".reset()".
94
+ * You can only provide the next initial state. The public-facing ".resetHandlers()"
95
+ * spread the arguments so there's no distinction between () and ([]).
96
+ */
97
+ expect(controller.currentHandlers()).toEqual([httpTwo])
98
+ })
99
+ })
100
+
101
+ describe(InMemoryHandlersController.prototype.getHandlersByKind, () => {
102
+ it('returns an empty array given an empty controller', () => {
103
+ const controller = new InMemoryHandlersController([])
104
+ expect(controller.getHandlersByKind('request')).toEqual([])
105
+ })
106
+
107
+ it('returns an empty array given no handlers by the given kind', () => {
108
+ expect(
109
+ new InMemoryHandlersController([
110
+ http.get('/', () => {}),
111
+ graphql.query('', () => {}),
112
+ ]).getHandlersByKind('websocket'),
113
+ ).toEqual([])
114
+
115
+ expect(
116
+ new InMemoryHandlersController([
117
+ ws.link('*').addEventListener('connection', () => {}),
118
+ ]).getHandlersByKind('request'),
119
+ ).toEqual([])
120
+ })
121
+
122
+ it('returns all handlers if they all match', () => {
123
+ const httpHandler = http.get('/', () => {})
124
+ const graphqlHandler = graphql.query('', () => {})
125
+ const wsHandler = ws.link('*').addEventListener('connection', () => {})
126
+
127
+ expect(
128
+ new InMemoryHandlersController([
129
+ httpHandler,
130
+ graphqlHandler,
131
+ ]).getHandlersByKind('request'),
132
+ ).toEqual([httpHandler, graphqlHandler])
133
+
134
+ expect(
135
+ new InMemoryHandlersController([wsHandler]).getHandlersByKind(
136
+ 'websocket',
137
+ ),
138
+ ).toEqual([wsHandler])
139
+ })
140
+
141
+ it('returns only the matching handlers', () => {
142
+ const httpHandler = http.get('/', () => {})
143
+ const graphqlHandler = graphql.query('', () => {})
144
+ const wsHandler = ws.link('*').addEventListener('connection', () => {})
145
+
146
+ expect(
147
+ new InMemoryHandlersController([
148
+ httpHandler,
149
+ graphqlHandler,
150
+ wsHandler,
151
+ ]).getHandlersByKind('request'),
152
+ ).toEqual([httpHandler, graphqlHandler])
153
+
154
+ expect(
155
+ new InMemoryHandlersController([
156
+ httpHandler,
157
+ graphqlHandler,
158
+ wsHandler,
159
+ ]).getHandlersByKind('websocket'),
160
+ ).toEqual([wsHandler])
161
+ })
162
+
163
+ it('preserves the order of returned handlers', () => {
164
+ const httpOne = http.get('/', () => {})
165
+ const httpTwo = http.get('/', () => {})
166
+ const httpThree = http.get('/', () => {})
167
+
168
+ expect(
169
+ new InMemoryHandlersController([
170
+ httpOne,
171
+ httpTwo,
172
+ httpThree,
173
+ ]).getHandlersByKind('request'),
174
+ ).toEqual([httpOne, httpTwo, httpThree])
175
+
176
+ const graphqlOne = graphql.query('', () => {})
177
+ const graphqlTwo = graphql.query('', () => {})
178
+ const graphqlThree = graphql.query('', () => {})
179
+
180
+ expect(
181
+ new InMemoryHandlersController([
182
+ graphqlOne,
183
+ graphqlTwo,
184
+ graphqlThree,
185
+ ]).getHandlersByKind('request'),
186
+ ).toEqual([graphqlOne, graphqlTwo, graphqlThree])
187
+
188
+ const wsOne = ws.link('*').addEventListener('connection', () => {})
189
+ const wsTwo = ws.link('*').addEventListener('connection', () => {})
190
+ const wsThree = ws.link('*').addEventListener('connection', () => {})
191
+
192
+ expect(
193
+ new InMemoryHandlersController([wsOne, wsTwo, wsThree]).getHandlersByKind(
194
+ 'websocket',
195
+ ),
196
+ ).toEqual([wsOne, wsTwo, wsThree])
197
+ })
198
+ })