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,360 @@
1
+ import { http } from '../../http'
2
+ import { graphql } from '../../graphql'
3
+ import { ws } from '../../ws'
4
+ import { bypass } from '../../bypass'
5
+ import { HttpNetworkFrame, HttpNetworkFrameEventMap } from './http-frame'
6
+ import { InMemoryHandlersController } from '#core/experimental/handlers-controller'
7
+
8
+ beforeAll(() => {
9
+ vi.spyOn(console, 'error').mockImplementation(() => {})
10
+ })
11
+
12
+ afterEach(() => {
13
+ vi.clearAllMocks()
14
+ })
15
+
16
+ afterAll(() => {
17
+ vi.restoreAllMocks()
18
+ })
19
+
20
+ function spyOnNetworkFrame(frame: HttpNetworkFrame) {
21
+ const events: Array<
22
+ HttpNetworkFrameEventMap[keyof HttpNetworkFrameEventMap]
23
+ > = []
24
+
25
+ frame.events.on('*', (event) => events.push(event))
26
+
27
+ return {
28
+ events,
29
+ }
30
+ }
31
+
32
+ it('filters only request type handlers', async () => {
33
+ class HttpFrame extends HttpNetworkFrame {
34
+ respondWith = vi.fn()
35
+ passthrough = vi.fn()
36
+ errorWith = vi.fn()
37
+ }
38
+
39
+ const frame = new HttpFrame({
40
+ request: new Request('http://localhost/api'),
41
+ })
42
+
43
+ const httpHandlers = [http.post('http://localhost/api/user', () => {})]
44
+ const graphqlHandlers = [graphql.query('GetUser', () => {})]
45
+ const webSocketHandlers = [
46
+ ws.link('ws://localhost').addEventListener('connection', () => {}),
47
+ ]
48
+
49
+ const controller = new InMemoryHandlersController([
50
+ ...httpHandlers,
51
+ ...webSocketHandlers,
52
+ ...graphqlHandlers,
53
+ ])
54
+
55
+ expect(frame.getHandlers(controller)).toEqual([
56
+ ...httpHandlers,
57
+ ...graphqlHandlers,
58
+ ])
59
+ expect(frame.getHandlers(new InMemoryHandlersController([]))).toEqual([])
60
+ })
61
+
62
+ it('resolves a matching request', async () => {
63
+ class HttpFrame extends HttpNetworkFrame {
64
+ respondWith = vi.fn()
65
+ passthrough = vi.fn()
66
+ errorWith = vi.fn()
67
+ }
68
+
69
+ const frame = new HttpFrame({
70
+ request: new Request('http://localhost/api'),
71
+ })
72
+ const { events } = spyOnNetworkFrame(frame)
73
+ const unhandledFrameCallback = vi.fn()
74
+
75
+ const matches = await frame.resolve(
76
+ [
77
+ http.get('http://localhost/api', () => {
78
+ return new Response(null, { status: 204 })
79
+ }),
80
+ ],
81
+ unhandledFrameCallback,
82
+ { quiet: true },
83
+ )
84
+
85
+ expect.soft(matches).toBe(true)
86
+ expect.soft(frame.respondWith).toHaveBeenCalledExactlyOnceWith(
87
+ expect.objectContaining({
88
+ status: 204,
89
+ }),
90
+ )
91
+ expect.soft(frame.passthrough).not.toHaveBeenCalled()
92
+ expect.soft(frame.errorWith).not.toHaveBeenCalled()
93
+ expect.soft(unhandledFrameCallback).not.toHaveBeenCalled()
94
+ expect.soft(events).toEqual([
95
+ expect.objectContaining({
96
+ type: 'request:start',
97
+ requestId: frame.data.id,
98
+ request: frame.data.request,
99
+ }),
100
+ expect.objectContaining({
101
+ type: 'request:match',
102
+ requestId: frame.data.id,
103
+ request: frame.data.request,
104
+ }),
105
+ expect.objectContaining({
106
+ type: 'request:end',
107
+ requestId: frame.data.id,
108
+ request: frame.data.request,
109
+ }),
110
+ ])
111
+ })
112
+
113
+ it('resolves a non-matching request', async () => {
114
+ class HttpFrame extends HttpNetworkFrame {
115
+ respondWith = vi.fn()
116
+ passthrough = vi.fn()
117
+ errorWith = vi.fn()
118
+ }
119
+
120
+ const frame = new HttpFrame({
121
+ request: new Request('http://localhost/api'),
122
+ })
123
+ const { events } = spyOnNetworkFrame(frame)
124
+ const unhandledFrameCallback = vi.fn()
125
+
126
+ const matches = await frame.resolve(
127
+ [
128
+ http.get('http://example.com/resource', () => {
129
+ return new Response(null, { status: 204 })
130
+ }),
131
+ ],
132
+ unhandledFrameCallback,
133
+ { quiet: true },
134
+ )
135
+
136
+ expect.soft(matches).toBe(false)
137
+ expect.soft(frame.passthrough).toHaveBeenCalledOnce()
138
+ expect.soft(frame.respondWith).not.toHaveBeenCalled()
139
+ expect.soft(frame.errorWith).not.toHaveBeenCalled()
140
+ expect.soft(unhandledFrameCallback).toHaveBeenCalledExactlyOnceWith(
141
+ expect.objectContaining({
142
+ frame,
143
+ }),
144
+ )
145
+ expect.soft(events).toEqual([
146
+ expect.objectContaining({
147
+ type: 'request:start',
148
+ requestId: frame.data.id,
149
+ request: frame.data.request,
150
+ }),
151
+ expect.objectContaining({
152
+ type: 'request:unhandled',
153
+ requestId: frame.data.id,
154
+ request: frame.data.request,
155
+ }),
156
+ expect.objectContaining({
157
+ type: 'request:end',
158
+ requestId: frame.data.id,
159
+ request: frame.data.request,
160
+ }),
161
+ ])
162
+ })
163
+
164
+ it('resolves a matched passthrough', async () => {
165
+ class HttpFrame extends HttpNetworkFrame {
166
+ respondWith = vi.fn()
167
+ passthrough = vi.fn()
168
+ errorWith = vi.fn()
169
+ }
170
+
171
+ const frame = new HttpFrame({
172
+ request: new Request('http://localhost/api'),
173
+ })
174
+ const { events } = spyOnNetworkFrame(frame)
175
+ const unhandledFrameCallback = vi.fn()
176
+
177
+ const matches = await frame.resolve(
178
+ [
179
+ http.get('http://localhost/api', () => {
180
+ // Intentionally do nothing to passthrough.
181
+ }),
182
+ ],
183
+ unhandledFrameCallback,
184
+ { quiet: true },
185
+ )
186
+
187
+ expect.soft(matches).toBeNull()
188
+ expect.soft(frame.passthrough).toHaveBeenCalledOnce()
189
+ expect.soft(frame.respondWith).not.toHaveBeenCalled()
190
+ expect.soft(frame.errorWith).not.toHaveBeenCalled()
191
+ expect.soft(unhandledFrameCallback).not.toHaveBeenCalled()
192
+ expect.soft(events).toEqual([
193
+ expect.objectContaining({
194
+ type: 'request:start',
195
+ requestId: frame.data.id,
196
+ request: frame.data.request,
197
+ }),
198
+ expect.objectContaining({
199
+ type: 'request:match',
200
+ requestId: frame.data.id,
201
+ request: frame.data.request,
202
+ }),
203
+ expect.objectContaining({
204
+ type: 'request:end',
205
+ requestId: frame.data.id,
206
+ request: frame.data.request,
207
+ }),
208
+ ])
209
+ })
210
+
211
+ it('resolves a bypassed request', async () => {
212
+ class HttpFrame extends HttpNetworkFrame {
213
+ respondWith = vi.fn()
214
+ passthrough = vi.fn()
215
+ errorWith = vi.fn()
216
+ }
217
+
218
+ const frame = new HttpFrame({
219
+ request: bypass(new Request('http://localhost/api')),
220
+ })
221
+ const { events } = spyOnNetworkFrame(frame)
222
+ const unhandledFrameCallback = vi.fn()
223
+
224
+ const matches = await frame.resolve(
225
+ [
226
+ http.get('http://localhost/api', () => {
227
+ return new Response(null, { status: 204 })
228
+ }),
229
+ ],
230
+ unhandledFrameCallback,
231
+ { quiet: true },
232
+ )
233
+
234
+ expect.soft(matches).toBeNull()
235
+ expect.soft(frame.passthrough).toHaveBeenCalledOnce()
236
+ expect.soft(frame.respondWith).not.toHaveBeenCalled()
237
+ expect.soft(frame.errorWith).not.toHaveBeenCalled()
238
+ expect.soft(unhandledFrameCallback).not.toHaveBeenCalled()
239
+ expect.soft(events).toEqual([
240
+ expect.objectContaining({
241
+ type: 'request:start',
242
+ requestId: frame.data.id,
243
+ request: frame.data.request,
244
+ }),
245
+ expect.objectContaining({
246
+ type: 'request:end',
247
+ requestId: frame.data.id,
248
+ request: frame.data.request,
249
+ }),
250
+ ])
251
+ })
252
+
253
+ it('errors the request on unhandled exception', async () => {
254
+ class HttpFrame extends HttpNetworkFrame {
255
+ respondWith = vi.fn()
256
+ passthrough = vi.fn()
257
+ errorWith = vi.fn()
258
+ }
259
+
260
+ const frame = new HttpFrame({
261
+ request: new Request('http://localhost/api'),
262
+ })
263
+ const { events } = spyOnNetworkFrame(frame)
264
+ const unhandledFrameCallback = vi.fn()
265
+
266
+ const exception = new Error('Unhandled exception')
267
+ const matches = await frame.resolve(
268
+ [
269
+ http.get('http://localhost/api', () => {
270
+ throw exception
271
+ }),
272
+ ],
273
+ unhandledFrameCallback,
274
+ { quiet: true },
275
+ )
276
+
277
+ expect.soft(matches).toBeNull()
278
+ expect.soft(frame.errorWith).toHaveBeenCalledExactlyOnceWith(exception)
279
+ expect.soft(frame.passthrough).not.toHaveBeenCalled()
280
+ expect.soft(frame.respondWith).not.toHaveBeenCalled()
281
+ expect.soft(unhandledFrameCallback).not.toHaveBeenCalled()
282
+ expect.soft(events).toEqual([
283
+ expect.objectContaining({
284
+ type: 'request:start',
285
+ requestId: frame.data.id,
286
+ request: frame.data.request,
287
+ }),
288
+ expect.objectContaining({
289
+ type: 'unhandledException',
290
+ error: exception,
291
+ requestId: frame.data.id,
292
+ request: frame.data.request,
293
+ }),
294
+ ])
295
+
296
+ expect.soft(console.error).toHaveBeenCalledTimes(2)
297
+ expect.soft(console.error).toHaveBeenNthCalledWith(1, exception)
298
+ expect
299
+ .soft(console.error)
300
+ .toHaveBeenNthCalledWith(
301
+ 2,
302
+ '[MSW] Encountered an unhandled exception during the handler lookup for "GET http://localhost/api". Please see the original error above.',
303
+ )
304
+ })
305
+
306
+ it('does not print an unhandled exception if the "unhandledException" listener is present', async () => {
307
+ class HttpFrame extends HttpNetworkFrame {
308
+ respondWith = vi.fn()
309
+ passthrough = vi.fn()
310
+ errorWith = vi.fn()
311
+ }
312
+
313
+ const frame = new HttpFrame({
314
+ request: new Request('http://localhost/api'),
315
+ })
316
+ const { events } = spyOnNetworkFrame(frame)
317
+ const unhandledFrameCallback = vi.fn()
318
+
319
+ const unhandledExceptionListener = vi.fn()
320
+ frame.events.on('unhandledException', unhandledExceptionListener)
321
+
322
+ const exception = new Error('Unhandled exception')
323
+ const matches = await frame.resolve(
324
+ [
325
+ http.get('http://localhost/api', () => {
326
+ throw exception
327
+ }),
328
+ ],
329
+ unhandledFrameCallback,
330
+ { quiet: true },
331
+ )
332
+
333
+ expect.soft(matches).toBeNull()
334
+ expect.soft(frame.errorWith).toHaveBeenCalledExactlyOnceWith(exception)
335
+ expect.soft(frame.passthrough).not.toHaveBeenCalled()
336
+ expect.soft(frame.respondWith).not.toHaveBeenCalled()
337
+ expect.soft(unhandledFrameCallback).not.toHaveBeenCalled()
338
+ expect.soft(events).toEqual([
339
+ expect.objectContaining({
340
+ type: 'request:start',
341
+ requestId: frame.data.id,
342
+ request: frame.data.request,
343
+ }),
344
+ expect.objectContaining({
345
+ type: 'unhandledException',
346
+ error: exception,
347
+ requestId: frame.data.id,
348
+ request: frame.data.request,
349
+ }),
350
+ ])
351
+
352
+ expect.soft(console.error).not.toHaveBeenCalled()
353
+ expect.soft(unhandledExceptionListener).toHaveBeenCalledExactlyOnceWith(
354
+ expect.objectContaining({
355
+ error: exception,
356
+ requestId: frame.data.id,
357
+ request: frame.data.request,
358
+ }),
359
+ )
360
+ })
@@ -0,0 +1,271 @@
1
+ import { TypedEvent } from 'rettime'
2
+ import { until } from 'until-async'
3
+ import { createRequestId } from '@mswjs/interceptors'
4
+ import { NetworkFrame, NetworkFrameResolutionContext } from './network-frame'
5
+ import { toPublicUrl } from '../../utils/request/toPublicUrl'
6
+ import { executeHandlers } from '../../utils/executeHandlers'
7
+ import { storeResponseCookies } from '../../utils/request/storeResponseCookies'
8
+ import { isPassthroughResponse, shouldBypassRequest } from '../request-utils'
9
+ import { devUtils } from '../../utils/internal/devUtils'
10
+ import {
11
+ executeUnhandledFrameHandle,
12
+ type UnhandledFrameHandle,
13
+ } from '../on-unhandled-frame'
14
+ import { HandlersController, AnyHandler } from '../handlers-controller'
15
+ import { type RequestHandler } from '../../handlers/RequestHandler'
16
+
17
+ interface HttpNetworkFrameOptions {
18
+ id?: string
19
+ request: Request
20
+ }
21
+
22
+ export class RequestEvent<
23
+ DataType extends { requestId: string; request: Request } = {
24
+ requestId: string
25
+ request: Request
26
+ },
27
+ ReturnType = void,
28
+ EventType extends string = string,
29
+ > extends TypedEvent<DataType, ReturnType, EventType> {
30
+ public readonly requestId: string
31
+ public readonly request: Request
32
+
33
+ constructor(type: EventType, data: DataType) {
34
+ super(...([type, {}] as any))
35
+ this.requestId = data.requestId
36
+ this.request = data.request
37
+ }
38
+ }
39
+
40
+ export class ResponseEvent<
41
+ DataType extends {
42
+ requestId: string
43
+ request: Request
44
+ response: Response
45
+ } = {
46
+ requestId: string
47
+ request: Request
48
+ response: Response
49
+ },
50
+ ReturnType = void,
51
+ EventType extends string = string,
52
+ > extends TypedEvent<DataType, ReturnType, EventType> {
53
+ public readonly requestId: string
54
+ public readonly request: Request
55
+ public readonly response: Response
56
+
57
+ constructor(type: EventType, data: DataType) {
58
+ super(...([type, {}] as any))
59
+ this.requestId = data.requestId
60
+ this.request = data.request
61
+ this.response = data.response
62
+ }
63
+ }
64
+
65
+ export class UnhandledExceptionEvent<
66
+ DataType extends {
67
+ error: Error
68
+ requestId: string
69
+ request: Request
70
+ } = {
71
+ error: Error
72
+ requestId: string
73
+ request: Request
74
+ },
75
+ ReturnType = void,
76
+ EventType extends string = string,
77
+ > extends TypedEvent<DataType, ReturnType, EventType> {
78
+ public readonly error: Error
79
+ public readonly requestId: string
80
+ public readonly request: Request
81
+
82
+ constructor(type: EventType, data: DataType) {
83
+ super(...([type, {}] as any))
84
+ this.error = data.error
85
+ this.requestId = data.requestId
86
+ this.request = data.request
87
+ }
88
+ }
89
+
90
+ export type HttpNetworkFrameEventMap = {
91
+ 'request:start': RequestEvent
92
+ 'request:match': RequestEvent
93
+ 'request:unhandled': RequestEvent
94
+ 'request:end': RequestEvent
95
+ 'response:mocked': ResponseEvent
96
+ 'response:bypass': ResponseEvent
97
+ unhandledException: UnhandledExceptionEvent
98
+ }
99
+
100
+ export abstract class HttpNetworkFrame extends NetworkFrame<
101
+ 'http',
102
+ {
103
+ id: string
104
+ request: Request
105
+ },
106
+ HttpNetworkFrameEventMap
107
+ > {
108
+ constructor(options: HttpNetworkFrameOptions) {
109
+ const id = options.id || createRequestId()
110
+ super('http', { id, request: options.request })
111
+ }
112
+
113
+ public getHandlers(controller: HandlersController): Array<AnyHandler> {
114
+ return controller.getHandlersByKind('request')
115
+ }
116
+
117
+ public abstract respondWith(response?: Response): void
118
+
119
+ public async getUnhandledMessage(): Promise<string> {
120
+ const { request } = this.data
121
+
122
+ const url = new URL(request.url)
123
+ const publicUrl = toPublicUrl(url) + url.search
124
+ const requestBody =
125
+ request.body == null ? null : await request.clone().text()
126
+
127
+ const details = `\n\n \u2022 ${request.method} ${publicUrl}\n\n${requestBody ? ` \u2022 Request body: ${requestBody}\n\n` : ''}`
128
+ const message = `intercepted a request without a matching request handler:${details}If you still wish to intercept this unhandled request, please create a request handler for it.\nRead more: https://mswjs.io/docs/http/intercepting-requests`
129
+
130
+ return message
131
+ }
132
+
133
+ public async resolve(
134
+ handlers: Array<RequestHandler>,
135
+ onUnhandledFrame: UnhandledFrameHandle,
136
+ resolutionContext?: NetworkFrameResolutionContext,
137
+ ): Promise<boolean | null> {
138
+ const { id: requestId, request } = this.data
139
+ const requestCloneForLogs = resolutionContext?.quiet
140
+ ? null
141
+ : request.clone()
142
+
143
+ this.events.emit(new RequestEvent('request:start', { requestId, request }))
144
+
145
+ // Requests wrapped in explicit "bypass(request)".
146
+ if (shouldBypassRequest(request)) {
147
+ this.events.emit(new RequestEvent('request:end', { requestId, request }))
148
+ this.passthrough()
149
+ return null
150
+ }
151
+
152
+ const [lookupError, lookupResult] = await until(() => {
153
+ return executeHandlers({
154
+ requestId,
155
+ request,
156
+ handlers,
157
+ resolutionContext: {
158
+ baseUrl: resolutionContext?.baseUrl?.toString(),
159
+ quiet: resolutionContext?.quiet,
160
+ },
161
+ })
162
+ })
163
+
164
+ if (lookupError != null) {
165
+ if (
166
+ !this.events.emit(
167
+ new UnhandledExceptionEvent('unhandledException', {
168
+ error: lookupError,
169
+ requestId,
170
+ request,
171
+ }),
172
+ )
173
+ ) {
174
+ // Surface the error to the developer since they haven't handled it.
175
+ console.error(lookupError)
176
+ devUtils.error(
177
+ 'Encountered an unhandled exception during the handler lookup for "%s %s". Please see the original error above.',
178
+ request.method,
179
+ request.url,
180
+ )
181
+ }
182
+
183
+ this.errorWith(lookupError)
184
+ return null
185
+ }
186
+
187
+ // No matching handlers.
188
+ if (lookupResult == null) {
189
+ this.events.emit(
190
+ new RequestEvent('request:unhandled', {
191
+ requestId,
192
+ request,
193
+ }),
194
+ )
195
+
196
+ /**
197
+ * @note The unhandled frame handle must be executed during the request resolution
198
+ * since it can influence it (e.g. error the request if the "error" startegy was used).
199
+ */
200
+ await executeUnhandledFrameHandle(this, onUnhandledFrame).then(
201
+ () => this.passthrough(),
202
+ (error) => this.errorWith(error),
203
+ )
204
+
205
+ this.events.emit(
206
+ new RequestEvent('request:end', {
207
+ requestId,
208
+ request,
209
+ }),
210
+ )
211
+
212
+ return false
213
+ }
214
+
215
+ const { response, handler, parsedResult } = lookupResult
216
+
217
+ this.events.emit(
218
+ new RequestEvent('request:match', {
219
+ requestId,
220
+ request,
221
+ }),
222
+ )
223
+
224
+ // Handlers that returned no mocked response.
225
+ if (response == null) {
226
+ this.events.emit(
227
+ new RequestEvent('request:end', {
228
+ requestId,
229
+ request,
230
+ }),
231
+ )
232
+
233
+ this.passthrough()
234
+ return null
235
+ }
236
+
237
+ // Handlers that returned explicit `passthrough()`.
238
+ if (isPassthroughResponse(response)) {
239
+ this.events.emit(
240
+ new RequestEvent('request:end', {
241
+ requestId,
242
+ request,
243
+ }),
244
+ )
245
+
246
+ this.passthrough()
247
+ return null
248
+ }
249
+
250
+ await storeResponseCookies(request, response)
251
+
252
+ this.respondWith(response.clone())
253
+
254
+ this.events.emit(
255
+ new RequestEvent('request:end', {
256
+ requestId,
257
+ request,
258
+ }),
259
+ )
260
+
261
+ if (!resolutionContext?.quiet) {
262
+ handler.log({
263
+ request: requestCloneForLogs!,
264
+ response,
265
+ parsedResult,
266
+ })
267
+ }
268
+
269
+ return true
270
+ }
271
+ }
@@ -0,0 +1,64 @@
1
+ import { Emitter, type DefaultEventMap } from 'rettime'
2
+ import type { AnyHandler, HandlersController } from '../handlers-controller'
3
+ import type { UnhandledFrameHandle } from '../on-unhandled-frame'
4
+
5
+ export type AnyNetworkFrame = NetworkFrame<string, unknown, any>
6
+
7
+ export type ExtractFrameEvents<Frame> =
8
+ Frame extends NetworkFrame<any, any, infer Events> ? Events : never
9
+
10
+ export interface NetworkFrameResolutionContext {
11
+ baseUrl?: string | URL
12
+ quiet?: boolean
13
+ }
14
+
15
+ /**
16
+ * The base for the network frames. Extend this abstract class
17
+ * to implement custom network frames.
18
+ */
19
+ export abstract class NetworkFrame<
20
+ Protocol extends string,
21
+ Data,
22
+ Events extends DefaultEventMap,
23
+ > {
24
+ public events: Emitter<Events>
25
+
26
+ constructor(
27
+ public readonly protocol: Protocol,
28
+ public readonly data: Data,
29
+ ) {
30
+ this.events = new Emitter()
31
+ }
32
+
33
+ public abstract getHandlers(controller: HandlersController): Array<AnyHandler>
34
+
35
+ /**
36
+ * Resolve the current frame against the given list of handlers.
37
+ * Optionally, use a custom resolution context to control behaviors
38
+ * like `baseUrl`.
39
+ *
40
+ * Returns `true` if the frame was handled, `false` if it wasn't, and `null`
41
+ * if its handling was skipped (e.g. the frame was bypassed).
42
+ */
43
+ public abstract resolve(
44
+ handlers: Array<AnyHandler>,
45
+ onUnhandledFrame: UnhandledFrameHandle,
46
+ resolutionContext?: NetworkFrameResolutionContext,
47
+ ): Promise<boolean | null>
48
+
49
+ /**
50
+ * Perform this network frame as-is.
51
+ */
52
+ public abstract passthrough(): void
53
+
54
+ /**
55
+ * Error the underling network frame.
56
+ * @param reason The reason for the error.
57
+ */
58
+ public abstract errorWith(reason?: unknown): void
59
+
60
+ /**
61
+ * Get a message to be used when this frame is unhandled.
62
+ */
63
+ public abstract getUnhandledMessage(): Promise<string>
64
+ }