msw 2.13.6 → 2.14.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.
- package/lib/core/{HttpResponse-BMMzfpjG.d.mts → HttpResponse-CxHR1nNN.d.mts} +5 -1
- package/lib/core/{HttpResponse-DPDqE4Pb.d.ts → HttpResponse-aGiIzO91.d.ts} +5 -1
- package/lib/core/HttpResponse.d.mts +1 -1
- package/lib/core/HttpResponse.d.ts +1 -1
- package/lib/core/experimental/compat.d.mts +1 -1
- package/lib/core/experimental/compat.d.ts +1 -1
- package/lib/core/experimental/define-network.d.mts +1 -1
- package/lib/core/experimental/define-network.d.ts +1 -1
- package/lib/core/experimental/frames/http-frame.d.mts +1 -1
- package/lib/core/experimental/frames/http-frame.d.ts +1 -1
- package/lib/core/experimental/frames/http-frame.js +3 -2
- package/lib/core/experimental/frames/http-frame.js.map +1 -1
- package/lib/core/experimental/frames/http-frame.mjs +3 -2
- package/lib/core/experimental/frames/http-frame.mjs.map +1 -1
- package/lib/core/experimental/frames/network-frame.d.mts +1 -1
- package/lib/core/experimental/frames/network-frame.d.ts +1 -1
- package/lib/core/experimental/frames/websocket-frame.d.mts +1 -1
- package/lib/core/experimental/frames/websocket-frame.d.ts +1 -1
- package/lib/core/experimental/handlers-controller.d.mts +1 -1
- package/lib/core/experimental/handlers-controller.d.ts +1 -1
- package/lib/core/experimental/handlers-controller.js +16 -5
- package/lib/core/experimental/handlers-controller.js.map +1 -1
- package/lib/core/experimental/handlers-controller.mjs +16 -5
- package/lib/core/experimental/handlers-controller.mjs.map +1 -1
- package/lib/core/experimental/index.d.mts +1 -1
- package/lib/core/experimental/index.d.ts +1 -1
- package/lib/core/experimental/on-unhandled-frame.d.mts +1 -1
- package/lib/core/experimental/on-unhandled-frame.d.ts +1 -1
- package/lib/core/experimental/setup-api.d.mts +1 -1
- package/lib/core/experimental/setup-api.d.ts +1 -1
- package/lib/core/experimental/sources/interceptor-source.d.mts +1 -1
- package/lib/core/experimental/sources/interceptor-source.d.ts +1 -1
- package/lib/core/experimental/sources/network-source.d.mts +1 -1
- package/lib/core/experimental/sources/network-source.d.ts +1 -1
- package/lib/core/getResponse.d.mts +1 -1
- package/lib/core/getResponse.d.ts +1 -1
- package/lib/core/graphql.d.mts +1 -1
- package/lib/core/graphql.d.ts +1 -1
- package/lib/core/handlers/GraphQLHandler.d.mts +1 -1
- package/lib/core/handlers/GraphQLHandler.d.ts +1 -1
- package/lib/core/handlers/HttpHandler.d.mts +1 -1
- package/lib/core/handlers/HttpHandler.d.ts +1 -1
- package/lib/core/handlers/RequestHandler.d.mts +1 -1
- package/lib/core/handlers/RequestHandler.d.ts +1 -1
- package/lib/core/handlers/RequestHandler.js +22 -1
- package/lib/core/handlers/RequestHandler.js.map +1 -1
- package/lib/core/handlers/RequestHandler.mjs +22 -1
- package/lib/core/handlers/RequestHandler.mjs.map +1 -1
- package/lib/core/handlers/WebSocketHandler.js +1 -1
- package/lib/core/handlers/WebSocketHandler.js.map +1 -1
- package/lib/core/handlers/WebSocketHandler.mjs +1 -1
- package/lib/core/handlers/WebSocketHandler.mjs.map +1 -1
- package/lib/core/http.d.mts +1 -1
- package/lib/core/http.d.ts +1 -1
- package/lib/core/index.d.mts +1 -1
- package/lib/core/index.d.ts +1 -1
- package/lib/core/passthrough.d.mts +1 -1
- package/lib/core/passthrough.d.ts +1 -1
- package/lib/core/sse.d.mts +1 -1
- package/lib/core/sse.d.ts +1 -1
- package/lib/core/utils/HttpResponse/decorators.d.mts +3 -3
- package/lib/core/utils/HttpResponse/decorators.d.ts +3 -3
- package/lib/core/utils/HttpResponse/decorators.js +4 -10
- package/lib/core/utils/HttpResponse/decorators.js.map +1 -1
- package/lib/core/utils/HttpResponse/decorators.mjs +4 -10
- package/lib/core/utils/HttpResponse/decorators.mjs.map +1 -1
- package/lib/core/utils/executeHandlers.d.mts +1 -1
- package/lib/core/utils/executeHandlers.d.ts +1 -1
- package/lib/core/utils/handleRequest.d.mts +1 -1
- package/lib/core/utils/handleRequest.d.ts +1 -1
- package/lib/core/utils/internal/attachSiblingHandlers.d.mts +15 -0
- package/lib/core/utils/internal/attachSiblingHandlers.d.ts +15 -0
- package/lib/core/utils/internal/attachSiblingHandlers.js +44 -0
- package/lib/core/utils/internal/attachSiblingHandlers.js.map +1 -0
- package/lib/core/utils/internal/attachSiblingHandlers.mjs +24 -0
- package/lib/core/utils/internal/attachSiblingHandlers.mjs.map +1 -0
- package/lib/core/utils/internal/isHandlerKind.d.mts +1 -1
- package/lib/core/utils/internal/isHandlerKind.d.ts +1 -1
- package/lib/core/utils/internal/parseGraphQLRequest.d.mts +1 -1
- package/lib/core/utils/internal/parseGraphQLRequest.d.ts +1 -1
- package/lib/core/utils/internal/parseMultipartData.d.mts +1 -1
- package/lib/core/utils/internal/parseMultipartData.d.ts +1 -1
- package/lib/core/utils/request/storeResponseCookies.js +1 -1
- package/lib/core/utils/request/storeResponseCookies.js.map +1 -1
- package/lib/core/utils/request/storeResponseCookies.mjs +2 -2
- package/lib/core/utils/request/storeResponseCookies.mjs.map +1 -1
- package/lib/core/ws/handleWebSocketEvent.d.mts +1 -1
- package/lib/core/ws/handleWebSocketEvent.d.ts +1 -1
- package/lib/core/ws.d.mts +17 -4
- package/lib/core/ws.d.ts +17 -4
- package/lib/core/ws.js +30 -5
- package/lib/core/ws.js.map +1 -1
- package/lib/core/ws.mjs +34 -6
- package/lib/core/ws.mjs.map +1 -1
- package/lib/iife/index.js +1208 -1142
- package/lib/iife/index.js.map +1 -1
- package/lib/mockServiceWorker.js +1 -1
- package/package.json +2 -2
- package/src/core/experimental/frames/http-frame.test.ts +6 -1
- package/src/core/experimental/frames/http-frame.ts +6 -2
- package/src/core/experimental/handlers-controller.test.ts +139 -5
- package/src/core/experimental/handlers-controller.ts +24 -9
- package/src/core/handlers/RequestHandler.ts +36 -1
- package/src/core/handlers/WebSocketHandler.ts +1 -1
- package/src/core/utils/HttpResponse/decorators.ts +6 -21
- package/src/core/utils/internal/attachSiblingHandlers.ts +28 -0
- package/src/core/utils/request/storeResponseCookies.ts +2 -4
- package/src/core/ws.ts +65 -6
package/lib/mockServiceWorker.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* - Please do NOT modify this file.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
const PACKAGE_VERSION = '2.
|
|
10
|
+
const PACKAGE_VERSION = '2.14.0'
|
|
11
11
|
const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82'
|
|
12
12
|
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
|
|
13
13
|
const activeClientIds = new Set()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "msw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.14.0",
|
|
4
4
|
"description": "Seamless REST/GraphQL API mocking library for browser and Node.js.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./lib/core/index.js",
|
|
@@ -286,7 +286,7 @@
|
|
|
286
286
|
"vitest-environment-miniflare": "^2.14.4",
|
|
287
287
|
"webpack": "^5.106.2",
|
|
288
288
|
"webpack-http-server": "^0.5.0",
|
|
289
|
-
"msw": "^2.
|
|
289
|
+
"msw": "^2.14.0"
|
|
290
290
|
},
|
|
291
291
|
"peerDependencies": {
|
|
292
292
|
"typescript": ">= 4.8.x"
|
|
@@ -4,7 +4,8 @@ import { ws } from '../../ws'
|
|
|
4
4
|
import { bypass } from '../../bypass'
|
|
5
5
|
import type { HttpNetworkFrameEventMap } from './http-frame'
|
|
6
6
|
import { HttpNetworkFrame } from './http-frame'
|
|
7
|
-
import { InMemoryHandlersController } from '
|
|
7
|
+
import { InMemoryHandlersController } from '../../experimental/handlers-controller'
|
|
8
|
+
import { getSiblingHandlers } from '../../utils/internal/attachSiblingHandlers'
|
|
8
9
|
|
|
9
10
|
beforeAll(() => {
|
|
10
11
|
vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
@@ -46,6 +47,9 @@ it('filters only request type handlers', async () => {
|
|
|
46
47
|
const webSocketHandlers = [
|
|
47
48
|
ws.link('ws://localhost').addEventListener('connection', () => {}),
|
|
48
49
|
]
|
|
50
|
+
const webSocketSiblingHandlers = webSocketHandlers.flatMap((handler) =>
|
|
51
|
+
getSiblingHandlers(handler),
|
|
52
|
+
)
|
|
49
53
|
|
|
50
54
|
const controller = new InMemoryHandlersController([
|
|
51
55
|
...httpHandlers,
|
|
@@ -55,6 +59,7 @@ it('filters only request type handlers', async () => {
|
|
|
55
59
|
|
|
56
60
|
expect(frame.getHandlers(controller)).toEqual([
|
|
57
61
|
...httpHandlers,
|
|
62
|
+
...webSocketSiblingHandlers,
|
|
58
63
|
...graphqlHandlers,
|
|
59
64
|
])
|
|
60
65
|
expect(frame.getHandlers(new InMemoryHandlersController([]))).toEqual([])
|
|
@@ -251,9 +251,13 @@ export abstract class HttpNetworkFrame extends NetworkFrame<
|
|
|
251
251
|
return null
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
const responseCloneForLogs = resolutionContext?.quiet
|
|
255
|
+
? null
|
|
256
|
+
: response.clone()
|
|
257
|
+
|
|
254
258
|
await storeResponseCookies(request, response)
|
|
255
259
|
|
|
256
|
-
this.respondWith(response
|
|
260
|
+
this.respondWith(response)
|
|
257
261
|
|
|
258
262
|
this.events.emit(
|
|
259
263
|
new RequestEvent('request:end', {
|
|
@@ -265,7 +269,7 @@ export abstract class HttpNetworkFrame extends NetworkFrame<
|
|
|
265
269
|
if (!resolutionContext?.quiet) {
|
|
266
270
|
handler.log({
|
|
267
271
|
request: requestCloneForLogs!,
|
|
268
|
-
response
|
|
272
|
+
response: responseCloneForLogs!,
|
|
269
273
|
parsedResult,
|
|
270
274
|
})
|
|
271
275
|
}
|
|
@@ -1,8 +1,68 @@
|
|
|
1
1
|
import { http } from '../http'
|
|
2
2
|
import { graphql } from '../graphql'
|
|
3
3
|
import { ws } from '../ws'
|
|
4
|
+
import { getSiblingHandlers } from '../utils/internal/attachSiblingHandlers'
|
|
4
5
|
import { InMemoryHandlersController } from './handlers-controller'
|
|
5
6
|
|
|
7
|
+
describe('constructor', () => {
|
|
8
|
+
it('places the sibling in its own kind bucket', () => {
|
|
9
|
+
const wsHandler = ws.link('*').addEventListener('connection', () => {})
|
|
10
|
+
const [upgradeHandler] = getSiblingHandlers(wsHandler)
|
|
11
|
+
|
|
12
|
+
const controller = new InMemoryHandlersController([wsHandler])
|
|
13
|
+
|
|
14
|
+
expect(controller.getHandlersByKind('websocket')).toEqual([wsHandler])
|
|
15
|
+
expect(controller.getHandlersByKind('request')).toEqual([upgradeHandler])
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('interleaves the sibling at the owner position when grouping by kind', () => {
|
|
19
|
+
const httpOne = http.get('/', () => {})
|
|
20
|
+
const wsHandler = ws.link('*').addEventListener('connection', () => {})
|
|
21
|
+
const [upgradeHandler] = getSiblingHandlers(wsHandler)
|
|
22
|
+
const httpTwo = http.get('/', () => {})
|
|
23
|
+
|
|
24
|
+
const controller = new InMemoryHandlersController([
|
|
25
|
+
httpOne,
|
|
26
|
+
wsHandler,
|
|
27
|
+
httpTwo,
|
|
28
|
+
])
|
|
29
|
+
|
|
30
|
+
expect(controller.getHandlersByKind('request')).toEqual([
|
|
31
|
+
httpOne,
|
|
32
|
+
upgradeHandler,
|
|
33
|
+
httpTwo,
|
|
34
|
+
])
|
|
35
|
+
expect(controller.getHandlersByKind('websocket')).toEqual([wsHandler])
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('extracts siblings from every owner in the input list', () => {
|
|
39
|
+
const wsOne = ws.link('*').addEventListener('connection', () => {})
|
|
40
|
+
const wsTwo = ws.link('*').addEventListener('connection', () => {})
|
|
41
|
+
const [upgradeOne] = getSiblingHandlers(wsOne)
|
|
42
|
+
const [upgradeTwo] = getSiblingHandlers(wsTwo)
|
|
43
|
+
|
|
44
|
+
const controller = new InMemoryHandlersController([wsOne, wsTwo])
|
|
45
|
+
|
|
46
|
+
expect(controller.getHandlersByKind('websocket')).toEqual([wsOne, wsTwo])
|
|
47
|
+
expect(controller.getHandlersByKind('request')).toEqual([
|
|
48
|
+
upgradeOne,
|
|
49
|
+
upgradeTwo,
|
|
50
|
+
])
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('dedupes the shared upgrade sibling across multiple handlers from the same link', () => {
|
|
54
|
+
const chat = ws.link('*')
|
|
55
|
+
const wsOne = chat.addEventListener('connection', () => {})
|
|
56
|
+
const wsTwo = chat.addEventListener('connection', () => {})
|
|
57
|
+
const [upgradeHandler] = getSiblingHandlers(wsOne)
|
|
58
|
+
|
|
59
|
+
const controller = new InMemoryHandlersController([wsOne, wsTwo])
|
|
60
|
+
|
|
61
|
+
expect(controller.getHandlersByKind('websocket')).toEqual([wsOne, wsTwo])
|
|
62
|
+
expect(controller.getHandlersByKind('request')).toEqual([upgradeHandler])
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
|
|
6
66
|
describe(InMemoryHandlersController.prototype.use, () => {
|
|
7
67
|
it('prepends a handler to an empty controller', () => {
|
|
8
68
|
const controller = new InMemoryHandlersController([])
|
|
@@ -51,6 +111,44 @@ describe(InMemoryHandlersController.prototype.use, () => {
|
|
|
51
111
|
|
|
52
112
|
expect(controller.currentHandlers()).toEqual([graphqlOne, httpTwo, httpOne])
|
|
53
113
|
})
|
|
114
|
+
|
|
115
|
+
it('propagates siblings to their kind buckets at runtime', () => {
|
|
116
|
+
const controller = new InMemoryHandlersController([])
|
|
117
|
+
const wsHandler = ws.link('*').addEventListener('connection', () => {})
|
|
118
|
+
const [upgradeHandler] = getSiblingHandlers(wsHandler)
|
|
119
|
+
|
|
120
|
+
controller.use([wsHandler])
|
|
121
|
+
|
|
122
|
+
expect(controller.getHandlersByKind('websocket')).toEqual([wsHandler])
|
|
123
|
+
expect(controller.getHandlersByKind('request')).toEqual([upgradeHandler])
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('prepends incoming siblings before existing handlers of the same kind', () => {
|
|
127
|
+
const existingHttp = http.get('/existing', () => {})
|
|
128
|
+
const controller = new InMemoryHandlersController([existingHttp])
|
|
129
|
+
const wsHandler = ws.link('*').addEventListener('connection', () => {})
|
|
130
|
+
const [upgradeHandler] = getSiblingHandlers(wsHandler)
|
|
131
|
+
|
|
132
|
+
controller.use([wsHandler])
|
|
133
|
+
|
|
134
|
+
expect(controller.getHandlersByKind('request')).toEqual([
|
|
135
|
+
upgradeHandler,
|
|
136
|
+
existingHttp,
|
|
137
|
+
])
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('dedupes the shared upgrade sibling when called with multiple handlers from the same link', () => {
|
|
141
|
+
const chat = ws.link('*')
|
|
142
|
+
const wsOne = chat.addEventListener('connection', () => {})
|
|
143
|
+
const wsTwo = chat.addEventListener('connection', () => {})
|
|
144
|
+
const [upgradeHandler] = getSiblingHandlers(wsOne)
|
|
145
|
+
|
|
146
|
+
const controller = new InMemoryHandlersController([])
|
|
147
|
+
controller.use([wsOne, wsTwo])
|
|
148
|
+
|
|
149
|
+
expect(controller.getHandlersByKind('websocket')).toEqual([wsOne, wsTwo])
|
|
150
|
+
expect(controller.getHandlersByKind('request')).toEqual([upgradeHandler])
|
|
151
|
+
})
|
|
54
152
|
})
|
|
55
153
|
|
|
56
154
|
describe(InMemoryHandlersController.prototype.reset, () => {
|
|
@@ -96,6 +194,42 @@ describe(InMemoryHandlersController.prototype.reset, () => {
|
|
|
96
194
|
*/
|
|
97
195
|
expect(controller.currentHandlers()).toEqual([httpTwo])
|
|
98
196
|
})
|
|
197
|
+
|
|
198
|
+
it('places siblings into their kind buckets when resetting to next handlers', () => {
|
|
199
|
+
const controller = new InMemoryHandlersController([])
|
|
200
|
+
const wsHandler = ws.link('*').addEventListener('connection', () => {})
|
|
201
|
+
const [upgradeHandler] = getSiblingHandlers(wsHandler)
|
|
202
|
+
|
|
203
|
+
controller.reset([wsHandler])
|
|
204
|
+
|
|
205
|
+
expect(controller.getHandlersByKind('websocket')).toEqual([wsHandler])
|
|
206
|
+
expect(controller.getHandlersByKind('request')).toEqual([upgradeHandler])
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
it('restores siblings when resetting to the initial handlers', () => {
|
|
210
|
+
const wsHandler = ws.link('*').addEventListener('connection', () => {})
|
|
211
|
+
const [upgradeHandler] = getSiblingHandlers(wsHandler)
|
|
212
|
+
const controller = new InMemoryHandlersController([wsHandler])
|
|
213
|
+
|
|
214
|
+
controller.use([http.get('/runtime', () => {})])
|
|
215
|
+
controller.reset([])
|
|
216
|
+
|
|
217
|
+
expect(controller.getHandlersByKind('websocket')).toEqual([wsHandler])
|
|
218
|
+
expect(controller.getHandlersByKind('request')).toEqual([upgradeHandler])
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
it('dedupes the shared upgrade sibling when reset with multiple handlers from the same link', () => {
|
|
222
|
+
const chat = ws.link('*')
|
|
223
|
+
const wsOne = chat.addEventListener('connection', () => {})
|
|
224
|
+
const wsTwo = chat.addEventListener('connection', () => {})
|
|
225
|
+
const [upgradeHandler] = getSiblingHandlers(wsOne)
|
|
226
|
+
|
|
227
|
+
const controller = new InMemoryHandlersController([])
|
|
228
|
+
controller.reset([wsOne, wsTwo])
|
|
229
|
+
|
|
230
|
+
expect(controller.getHandlersByKind('websocket')).toEqual([wsOne, wsTwo])
|
|
231
|
+
expect(controller.getHandlersByKind('request')).toEqual([upgradeHandler])
|
|
232
|
+
})
|
|
99
233
|
})
|
|
100
234
|
|
|
101
235
|
describe(InMemoryHandlersController.prototype.getHandlersByKind, () => {
|
|
@@ -112,11 +246,10 @@ describe(InMemoryHandlersController.prototype.getHandlersByKind, () => {
|
|
|
112
246
|
]).getHandlersByKind('websocket'),
|
|
113
247
|
).toEqual([])
|
|
114
248
|
|
|
249
|
+
const wsHandler = ws.link('*').addEventListener('connection', () => {})
|
|
115
250
|
expect(
|
|
116
|
-
new InMemoryHandlersController([
|
|
117
|
-
|
|
118
|
-
]).getHandlersByKind('request'),
|
|
119
|
-
).toEqual([])
|
|
251
|
+
new InMemoryHandlersController([wsHandler]).getHandlersByKind('request'),
|
|
252
|
+
).toEqual(getSiblingHandlers(wsHandler))
|
|
120
253
|
})
|
|
121
254
|
|
|
122
255
|
it('returns all handlers if they all match', () => {
|
|
@@ -142,6 +275,7 @@ describe(InMemoryHandlersController.prototype.getHandlersByKind, () => {
|
|
|
142
275
|
const httpHandler = http.get('/', () => {})
|
|
143
276
|
const graphqlHandler = graphql.query('', () => {})
|
|
144
277
|
const wsHandler = ws.link('*').addEventListener('connection', () => {})
|
|
278
|
+
const wsHandlerSiblings = getSiblingHandlers(wsHandler)
|
|
145
279
|
|
|
146
280
|
expect(
|
|
147
281
|
new InMemoryHandlersController([
|
|
@@ -149,7 +283,7 @@ describe(InMemoryHandlersController.prototype.getHandlersByKind, () => {
|
|
|
149
283
|
graphqlHandler,
|
|
150
284
|
wsHandler,
|
|
151
285
|
]).getHandlersByKind('request'),
|
|
152
|
-
).toEqual([httpHandler, graphqlHandler])
|
|
286
|
+
).toEqual([httpHandler, graphqlHandler, ...wsHandlerSiblings])
|
|
153
287
|
|
|
154
288
|
expect(
|
|
155
289
|
new InMemoryHandlersController([
|
|
@@ -2,6 +2,7 @@ import { invariant } from 'outvariant'
|
|
|
2
2
|
import { type RequestHandler } from '../handlers/RequestHandler'
|
|
3
3
|
import { type WebSocketHandler } from '../handlers/WebSocketHandler'
|
|
4
4
|
import { devUtils } from '../utils/internal/devUtils'
|
|
5
|
+
import { getSiblingHandlers } from '../utils/internal/attachSiblingHandlers'
|
|
5
6
|
|
|
6
7
|
export type AnyHandler = RequestHandler | WebSocketHandler
|
|
7
8
|
export type HandlersMap = Partial<Record<AnyHandler['kind'], Array<AnyHandler>>>
|
|
@@ -9,11 +10,23 @@ export type HandlersMap = Partial<Record<AnyHandler['kind'], Array<AnyHandler>>>
|
|
|
9
10
|
export function groupHandlersByKind(handlers: Array<AnyHandler>): HandlersMap {
|
|
10
11
|
const groups: HandlersMap = {}
|
|
11
12
|
|
|
13
|
+
const pushUnique = (kind: AnyHandler['kind'], handler: AnyHandler) => {
|
|
14
|
+
const bucket = (groups[kind] ||= [])
|
|
15
|
+
|
|
16
|
+
if (!bucket.includes(handler)) {
|
|
17
|
+
bucket.push(handler)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
12
21
|
/**
|
|
13
22
|
* @note `Object.groupBy` is not implemented in Node.js v20.
|
|
14
23
|
*/
|
|
15
24
|
for (const handler of handlers) {
|
|
16
|
-
|
|
25
|
+
pushUnique(handler.kind, handler)
|
|
26
|
+
|
|
27
|
+
for (const sibling of getSiblingHandlers(handler)) {
|
|
28
|
+
pushUnique(sibling.kind, sibling)
|
|
29
|
+
}
|
|
17
30
|
}
|
|
18
31
|
|
|
19
32
|
return groups
|
|
@@ -69,14 +82,16 @@ export abstract class HandlersController {
|
|
|
69
82
|
}
|
|
70
83
|
|
|
71
84
|
const { handlers } = this.getState()
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
85
|
+
const overrides = groupHandlersByKind(nextHandlers)
|
|
86
|
+
|
|
87
|
+
// Prepend overrides to their respective kind buckets so they take
|
|
88
|
+
// priority over existing handlers while preserving input order.
|
|
89
|
+
for (const kind in overrides) {
|
|
90
|
+
const overridesForKind = overrides[kind as AnyHandler['kind']]!
|
|
91
|
+
const existingForKind = handlers[kind as AnyHandler['kind']]
|
|
92
|
+
handlers[kind as AnyHandler['kind']] = existingForKind
|
|
93
|
+
? [...overridesForKind, ...existingForKind]
|
|
94
|
+
: overridesForKind
|
|
80
95
|
}
|
|
81
96
|
|
|
82
97
|
this.setState({ handlers })
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Headers as HeadersPolyfill } from 'headers-polyfill'
|
|
1
2
|
import { getCallFrame } from '../utils/internal/getCallFrame'
|
|
2
3
|
import {
|
|
3
4
|
isIterable,
|
|
@@ -12,6 +13,7 @@ import {
|
|
|
12
13
|
type DefaultUnsafeFetchResponse,
|
|
13
14
|
} from '../HttpResponse'
|
|
14
15
|
import type { GraphQLRequestBody } from './GraphQLHandler'
|
|
16
|
+
import { getRawSetCookie } from '../utils/HttpResponse/decorators'
|
|
15
17
|
|
|
16
18
|
export type DefaultRequestMultipartBody = Record<
|
|
17
19
|
string,
|
|
@@ -335,7 +337,7 @@ export abstract class RequestHandler<
|
|
|
335
337
|
...resolverExtras,
|
|
336
338
|
requestId: args.requestId,
|
|
337
339
|
request: args.request,
|
|
338
|
-
}) as Promise<Response>
|
|
340
|
+
}) as Promise<Response | undefined>
|
|
339
341
|
).catch((errorOrResponse) => {
|
|
340
342
|
// Allow throwing a Response instance in a response resolver.
|
|
341
343
|
if (errorOrResponse instanceof Response) {
|
|
@@ -348,6 +350,10 @@ export abstract class RequestHandler<
|
|
|
348
350
|
|
|
349
351
|
const mockedResponse = await mockedResponsePromise
|
|
350
352
|
|
|
353
|
+
if (mockedResponse) {
|
|
354
|
+
forwardResponseCookies(mockedResponse)
|
|
355
|
+
}
|
|
356
|
+
|
|
351
357
|
const executionResult = this.createExecutionResult({
|
|
352
358
|
// Pass the cloned request to the result so that logging
|
|
353
359
|
// and other consumers could read its body once more.
|
|
@@ -416,3 +422,32 @@ export abstract class RequestHandler<
|
|
|
416
422
|
}
|
|
417
423
|
}
|
|
418
424
|
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Forwards the cookies from the given response to `document.cookie`.
|
|
428
|
+
*/
|
|
429
|
+
export function forwardResponseCookies(response: Response): void {
|
|
430
|
+
// Cookie forwarding is only relevant in the browser.
|
|
431
|
+
if (typeof document === 'undefined') {
|
|
432
|
+
return
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const responseCookies = getRawSetCookie(response)
|
|
436
|
+
|
|
437
|
+
if (!responseCookies) {
|
|
438
|
+
return
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Write the mocked response cookies to the document.
|
|
442
|
+
// Use `headers-polyfill` to get the Set-Cookie header value correctly.
|
|
443
|
+
// This is an alternative until TypeScript 5.2
|
|
444
|
+
// and Node.js v20 become the minimum supported versions
|
|
445
|
+
// and "Headers.prototype.getSetCookie" can be used directly.
|
|
446
|
+
const allResponseCookies = HeadersPolyfill.prototype.getSetCookie.call(
|
|
447
|
+
new Headers([['set-cookie', responseCookies]]),
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
for (const cookieString of allResponseCookies) {
|
|
451
|
+
document.cookie = cookieString
|
|
452
|
+
}
|
|
453
|
+
}
|
|
@@ -118,7 +118,7 @@ export class WebSocketHandler {
|
|
|
118
118
|
params: parsedResult.match.params || {},
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
if (resolutionContext?.[kAutoConnect]) {
|
|
121
|
+
if (resolutionContext?.[kAutoConnect] ?? true) {
|
|
122
122
|
if (this[kConnect](resolvedConnection)) {
|
|
123
123
|
return resolvedConnection
|
|
124
124
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import statuses from '../../../shims/statuses'
|
|
2
|
-
import { Headers as HeadersPolyfill } from 'headers-polyfill'
|
|
3
2
|
import type { HttpResponseInit } from '../../HttpResponse'
|
|
4
3
|
|
|
5
4
|
const { message } = statuses
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
const kSetCookie = Symbol('kSetCookie')
|
|
8
7
|
|
|
9
8
|
export interface HttpResponseDecoratedInit extends HttpResponseInit {
|
|
10
9
|
status: number
|
|
@@ -31,7 +30,7 @@ export function decorateResponse(
|
|
|
31
30
|
response: Response,
|
|
32
31
|
init: HttpResponseDecoratedInit,
|
|
33
32
|
): Response {
|
|
34
|
-
// Allow
|
|
33
|
+
// Allow mocking the response type.
|
|
35
34
|
if (init.type) {
|
|
36
35
|
Object.defineProperty(response, 'type', {
|
|
37
36
|
value: init.type,
|
|
@@ -52,25 +51,11 @@ export function decorateResponse(
|
|
|
52
51
|
enumerable: false,
|
|
53
52
|
writable: false,
|
|
54
53
|
})
|
|
55
|
-
|
|
56
|
-
// Cookie forwarding is only relevant in the browser.
|
|
57
|
-
if (typeof document !== 'undefined') {
|
|
58
|
-
// Write the mocked response cookies to the document.
|
|
59
|
-
// Use `headers-polyfill` to get the Set-Cookie header value correctly.
|
|
60
|
-
// This is an alternative until TypeScript 5.2
|
|
61
|
-
// and Node.js v20 become the minimum supported version
|
|
62
|
-
// and getSetCookie in Headers can be used directly.
|
|
63
|
-
const responseCookiePairs = HeadersPolyfill.prototype.getSetCookie.call(
|
|
64
|
-
init.headers,
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
for (const cookieString of responseCookiePairs) {
|
|
68
|
-
// No need to parse the cookie headers because it's defined
|
|
69
|
-
// as the valid cookie string to begin with.
|
|
70
|
-
document.cookie = cookieString
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
54
|
}
|
|
74
55
|
|
|
75
56
|
return response
|
|
76
57
|
}
|
|
58
|
+
|
|
59
|
+
export function getRawSetCookie(response: Response): string | undefined {
|
|
60
|
+
return Reflect.get(response, kSetCookie)
|
|
61
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { invariant } from 'outvariant'
|
|
2
|
+
import type { AnyHandler } from '../../experimental/handlers-controller'
|
|
3
|
+
|
|
4
|
+
const kSiblingHandlers = Symbol('kSiblingHandlers')
|
|
5
|
+
|
|
6
|
+
export function attachSiblingHandlers<T extends AnyHandler>(
|
|
7
|
+
owner: T,
|
|
8
|
+
siblings: Array<AnyHandler>,
|
|
9
|
+
): T {
|
|
10
|
+
invariant(
|
|
11
|
+
getSiblingHandlers(owner).length === 0,
|
|
12
|
+
'Failed to merge handlers: the owner "%s" handler is already merged',
|
|
13
|
+
owner.kind,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
Object.defineProperty(owner, kSiblingHandlers, {
|
|
17
|
+
value: siblings,
|
|
18
|
+
enumerable: false,
|
|
19
|
+
writable: false,
|
|
20
|
+
configurable: false,
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
return owner
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getSiblingHandlers(owner: AnyHandler): Array<AnyHandler> {
|
|
27
|
+
return Reflect.get(owner, kSiblingHandlers) || []
|
|
28
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cookieStore } from '../cookieStore'
|
|
2
|
-
import {
|
|
2
|
+
import { getRawSetCookie } from '../HttpResponse/decorators'
|
|
3
3
|
|
|
4
4
|
export async function storeResponseCookies(
|
|
5
5
|
request: Request,
|
|
@@ -7,9 +7,7 @@ export async function storeResponseCookies(
|
|
|
7
7
|
): Promise<void> {
|
|
8
8
|
// Grab the raw "Set-Cookie" response header provided
|
|
9
9
|
// in the HeadersInit for this mocked response.
|
|
10
|
-
const responseCookies =
|
|
11
|
-
| string
|
|
12
|
-
| undefined
|
|
10
|
+
const responseCookies = getRawSetCookie(response)
|
|
13
11
|
|
|
14
12
|
if (responseCookies) {
|
|
15
13
|
await cookieStore.setCookie(responseCookies, request.url)
|
package/src/core/ws.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { invariant } from 'outvariant'
|
|
2
|
+
import { FetchResponse, resolveWebSocketUrl } from '@mswjs/interceptors'
|
|
2
3
|
import type {
|
|
3
4
|
WebSocketData,
|
|
4
5
|
WebSocketClientConnectionProtocol,
|
|
@@ -9,8 +10,15 @@ import {
|
|
|
9
10
|
type WebSocketHandlerEventMap,
|
|
10
11
|
} from './handlers/WebSocketHandler'
|
|
11
12
|
import { hasRefCounted } from './utils/internal/hasRefCounted'
|
|
12
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
type Path,
|
|
15
|
+
type PathParams,
|
|
16
|
+
isPath,
|
|
17
|
+
matchRequestUrl,
|
|
18
|
+
} from './utils/matching/matchRequestUrl'
|
|
13
19
|
import { WebSocketClientManager } from './ws/WebSocketClientManager'
|
|
20
|
+
import { http } from './http'
|
|
21
|
+
import { attachSiblingHandlers } from './utils/internal/attachSiblingHandlers'
|
|
14
22
|
|
|
15
23
|
const webSocketChannel = new BroadcastChannel('msw:websocket-client-manager')
|
|
16
24
|
|
|
@@ -100,18 +108,29 @@ function createWebSocketLinkHandler(url: Path): WebSocketLink {
|
|
|
100
108
|
|
|
101
109
|
const clientManager = new WebSocketClientManager(webSocketChannel)
|
|
102
110
|
|
|
111
|
+
// The same upgrade handler instance is attached as a sibling to every
|
|
112
|
+
// WebSocketHandler returned by this link. `groupHandlersByKind` dedupes
|
|
113
|
+
// by reference, so it lands in the `request` bucket exactly once regardless
|
|
114
|
+
// of which subset of WS handlers the user ends up registering.
|
|
115
|
+
const upgradeHandler = http.get(({ request }) => {
|
|
116
|
+
return (
|
|
117
|
+
request.headers.get('upgrade')?.toLowerCase() === 'websocket' &&
|
|
118
|
+
matchRequestUrl(new URL(resolveWebSocketUrl(request.url)), url).matches
|
|
119
|
+
)
|
|
120
|
+
}, ws.onUpgrade)
|
|
121
|
+
|
|
103
122
|
return {
|
|
104
123
|
get clients() {
|
|
105
124
|
return clientManager.clients
|
|
106
125
|
},
|
|
107
126
|
addEventListener(event, listener) {
|
|
108
|
-
const
|
|
127
|
+
const webSocketHandler = new WebSocketHandler(url)
|
|
109
128
|
|
|
110
129
|
// Add the connection event listener for when the
|
|
111
130
|
// handler matches and emits a connection event.
|
|
112
131
|
// When that happens, store that connection in the
|
|
113
132
|
// set of all connections for reference.
|
|
114
|
-
|
|
133
|
+
webSocketHandler[kEmitter].on('connection', async ({ client }) => {
|
|
115
134
|
await clientManager.addConnection(client)
|
|
116
135
|
})
|
|
117
136
|
|
|
@@ -119,9 +138,9 @@ function createWebSocketLinkHandler(url: Path): WebSocketLink {
|
|
|
119
138
|
// the "run()" method on the WebSocketHandler.
|
|
120
139
|
// If the handler matches, it will emit the "connection"
|
|
121
140
|
// event. Attach the user-defined listener to that event.
|
|
122
|
-
|
|
141
|
+
webSocketHandler[kEmitter].on(event, listener)
|
|
123
142
|
|
|
124
|
-
return
|
|
143
|
+
return attachSiblingHandlers(webSocketHandler, [upgradeHandler])
|
|
125
144
|
},
|
|
126
145
|
|
|
127
146
|
broadcast(data) {
|
|
@@ -145,6 +164,24 @@ function createWebSocketLinkHandler(url: Path): WebSocketLink {
|
|
|
145
164
|
}
|
|
146
165
|
}
|
|
147
166
|
|
|
167
|
+
const WEBSOCKET_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
|
|
168
|
+
|
|
169
|
+
interface WebSocketNamespace {
|
|
170
|
+
link: typeof createWebSocketLinkHandler
|
|
171
|
+
/**
|
|
172
|
+
* Request handler for the `upgrade` requests to the WebSocket protocol.
|
|
173
|
+
* This requires a WebSocket handler to be present to fire.
|
|
174
|
+
* @note This only affects Node.js as the `upgrade` request header is
|
|
175
|
+
* forbidden and cannot be read in the browser. Consider using the
|
|
176
|
+
* `WebSocket` API for establishing WebSocket connections in the browser.
|
|
177
|
+
*/
|
|
178
|
+
onUpgrade: (info: {
|
|
179
|
+
requestId: string
|
|
180
|
+
request: Request
|
|
181
|
+
params: PathParams
|
|
182
|
+
}) => Promise<Response | undefined> | Response | undefined
|
|
183
|
+
}
|
|
184
|
+
|
|
148
185
|
/**
|
|
149
186
|
* A namespace to intercept and mock WebSocket connections.
|
|
150
187
|
*
|
|
@@ -154,8 +191,30 @@ function createWebSocketLinkHandler(url: Path): WebSocketLink {
|
|
|
154
191
|
* @see {@link https://mswjs.io/docs/api/ws `ws` API reference}
|
|
155
192
|
* @see {@link https://mswjs.io/docs/basics/handling-websocket-events Handling WebSocket events}
|
|
156
193
|
*/
|
|
157
|
-
export const ws = {
|
|
194
|
+
export const ws: WebSocketNamespace = {
|
|
158
195
|
link: createWebSocketLinkHandler,
|
|
196
|
+
async onUpgrade({ request }) {
|
|
197
|
+
const key = request.headers.get('sec-websocket-key')
|
|
198
|
+
|
|
199
|
+
if (!key) {
|
|
200
|
+
return
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const keyBytes = new TextEncoder().encode(key + WEBSOCKET_GUID)
|
|
204
|
+
const digest = await crypto.subtle.digest('SHA-1', keyBytes)
|
|
205
|
+
const acceptValue = btoa(String.fromCharCode(...new Uint8Array(digest)))
|
|
206
|
+
|
|
207
|
+
new WebSocket(resolveWebSocketUrl(request.url))
|
|
208
|
+
|
|
209
|
+
return new FetchResponse(null, {
|
|
210
|
+
status: 101,
|
|
211
|
+
headers: {
|
|
212
|
+
upgrade: 'websocket',
|
|
213
|
+
connection: 'upgrade',
|
|
214
|
+
'sec-websocket-accept': acceptValue,
|
|
215
|
+
},
|
|
216
|
+
})
|
|
217
|
+
},
|
|
159
218
|
}
|
|
160
219
|
|
|
161
220
|
export { type WebSocketData }
|