@mswjs/interceptors 0.26.14 → 0.27.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.
@@ -28,56 +28,32 @@ export class WebSocketServerConnection {
28
28
  this[kEmitter] = new EventTarget()
29
29
  this.mockCloseController = new AbortController()
30
30
 
31
- // Handle incoming events from the actual server.
32
- // The (mock) WebSocket instance will call this
33
- // whenever a "message" event from the actual server
34
- // is dispatched on it (the dispatch will be skipped).
35
- this.transport.onIncoming = (event) => {
36
- // Clone the event to dispatch it on this class
37
- // once again and prevent the "already being dispatched"
38
- // exception. Clone it here so we can observe this event
39
- // being prevented in the "server.on()" listeners.
40
- const messageEvent = bindEvent(
41
- this.realWebSocket!,
42
- new CancelableMessageEvent('message', {
43
- data: event.data,
44
- origin: event.origin,
45
- cancelable: true,
46
- })
47
- )
48
-
49
- /**
50
- * @note Emit "message" event on the WebSocketClassServer
51
- * instance to let the interceptor know about these
52
- * incoming events from the original server. In that listener,
53
- * the interceptor can modify or skip the event forwarding
54
- * to the mock WebSocket instance.
55
- */
56
- this[kEmitter].dispatchEvent(messageEvent)
57
-
58
- /**
59
- * @note Forward the incoming server events to the client.
60
- * Preventing the default on the message event stops this.
61
- */
62
- if (!messageEvent.defaultPrevented) {
63
- this.socket.dispatchEvent(
64
- bindEvent(
65
- /**
66
- * @note Bind the forwarded original server events
67
- * to the mock WebSocket instance so it would
68
- * dispatch them straight away.
69
- */
70
- this.socket,
71
- // Clone the message event again to prevent
72
- // the "already being dispatched" exception.
73
- new MessageEvent('message', {
74
- data: event.data,
75
- origin: event.origin,
76
- })
77
- )
78
- )
31
+ // Automatically forward outgoing client events
32
+ // to the actual server unless the outgoing message event
33
+ // has been prevented. The "outgoing" transport event it
34
+ // dispatched by the "client" connection.
35
+ this.transport.addEventListener('outgoing', (event) => {
36
+ // Ignore client messages if the server connection
37
+ // hasn't been established yet. Nowhere to forward.
38
+ if (this.readyState === -1) {
39
+ return
79
40
  }
80
- }
41
+
42
+ // Every outgoing client message can prevent this forwarding
43
+ // by preventing the default of the outgoing message event.
44
+ // This listener will be added before user-defined listeners,
45
+ // so execute the logic on the next tick.
46
+ queueMicrotask(() => {
47
+ if (!event.defaultPrevented) {
48
+ this.send(event.data)
49
+ }
50
+ })
51
+ })
52
+
53
+ this.transport.addEventListener(
54
+ 'incoming',
55
+ this.handleIncomingMessage.bind(this)
56
+ )
81
57
  }
82
58
 
83
59
  /**
@@ -108,8 +84,33 @@ export class WebSocketServerConnection {
108
84
  // Inherit the binary type from the mock WebSocket client.
109
85
  realWebSocket.binaryType = this.socket.binaryType
110
86
 
87
+ // Allow the interceptor to listen to when the server connection
88
+ // has been established. This isn't necessary to operate with the connection
89
+ // but may be beneficial in some cases (like conditionally adding logging).
90
+ realWebSocket.addEventListener(
91
+ 'open',
92
+ (event) => {
93
+ this[kEmitter].dispatchEvent(
94
+ bindEvent(this.realWebSocket!, new Event('open', event))
95
+ )
96
+ },
97
+ { once: true }
98
+ )
99
+
111
100
  realWebSocket.addEventListener('message', (event) => {
112
- this.transport.onIncoming(event)
101
+ // Dispatch the "incoming" transport event instead of
102
+ // invoking the internal handler directly. This way,
103
+ // anyone can listen to the "incoming" event but this
104
+ // class is the one resulting in it.
105
+ this.transport.dispatchEvent(
106
+ bindEvent(
107
+ this.realWebSocket!,
108
+ new MessageEvent('incoming', {
109
+ data: event.data,
110
+ origin: event.origin,
111
+ })
112
+ )
113
+ )
113
114
  })
114
115
 
115
116
  // Close the original connection when the mock client closes.
@@ -215,6 +216,9 @@ export class WebSocketServerConnection {
215
216
  this.socket.url
216
217
  )
217
218
 
219
+ // Remove the "close" event listener from the server
220
+ // so it doesn't close the underlying WebSocket client
221
+ // when you call "server.close()".
218
222
  realWebSocket.removeEventListener('close', this.handleRealClose)
219
223
 
220
224
  if (
@@ -225,6 +229,60 @@ export class WebSocketServerConnection {
225
229
  }
226
230
 
227
231
  realWebSocket.close()
232
+
233
+ // Dispatch the "close" event on the server connection.
234
+ queueMicrotask(() => {
235
+ this[kEmitter].dispatchEvent(
236
+ bindEvent(this.realWebSocket, new CloseEvent('close'))
237
+ )
238
+ })
239
+ }
240
+
241
+ private handleIncomingMessage(event: MessageEvent<WebSocketData>): void {
242
+ // Clone the event to dispatch it on this class
243
+ // once again and prevent the "already being dispatched"
244
+ // exception. Clone it here so we can observe this event
245
+ // being prevented in the "server.on()" listeners.
246
+ const messageEvent = bindEvent(
247
+ event.target,
248
+ new CancelableMessageEvent('message', {
249
+ data: event.data,
250
+ origin: event.origin,
251
+ cancelable: true,
252
+ })
253
+ )
254
+
255
+ /**
256
+ * @note Emit "message" event on the server connection
257
+ * instance to let the interceptor know about these
258
+ * incoming events from the original server. In that listener,
259
+ * the interceptor can modify or skip the event forwarding
260
+ * to the mock WebSocket instance.
261
+ */
262
+ this[kEmitter].dispatchEvent(messageEvent)
263
+
264
+ /**
265
+ * @note Forward the incoming server events to the client.
266
+ * Preventing the default on the message event stops this.
267
+ */
268
+ if (!messageEvent.defaultPrevented) {
269
+ this.socket.dispatchEvent(
270
+ bindEvent(
271
+ /**
272
+ * @note Bind the forwarded original server events
273
+ * to the mock WebSocket instance so it would
274
+ * dispatch them straight away.
275
+ */
276
+ this.socket,
277
+ // Clone the message event again to prevent
278
+ // the "already being dispatched" exception.
279
+ new MessageEvent('message', {
280
+ data: event.data,
281
+ origin: event.origin,
282
+ })
283
+ )
284
+ )
285
+ }
228
286
  }
229
287
 
230
288
  private handleMockClose(_event: Event): void {
@@ -1,40 +1,39 @@
1
- export type WebSocketData = string | ArrayBufferLike | Blob | ArrayBufferView
2
-
3
- export type WebSocketTransportOnIncomingCallback = (
4
- event: MessageEvent<WebSocketData>
5
- ) => void
6
-
7
- export type WebSocketTransportOnOutgoingCallback = (data: WebSocketData) => void
1
+ import { CloseEvent } from './utils/events'
8
2
 
9
- export type WebSocketTransportOnCloseCallback = (event: CloseEvent) => void
10
-
11
- export abstract class WebSocketTransport {
12
- /**
13
- * A callback for the incoming server events.
14
- * This is called when the WebSocket client receives
15
- * a message from the server.
16
- */
17
- abstract onIncoming: WebSocketTransportOnIncomingCallback
3
+ export type WebSocketData = string | ArrayBufferLike | Blob | ArrayBufferView
18
4
 
19
- /**
20
- * A callback for outgoing client events.
21
- * This is called when the WebSocket client sends data.
22
- */
23
- abstract onOutgoing: WebSocketTransportOnOutgoingCallback
5
+ export type WebSocketTransportEventMap = {
6
+ incoming: MessageEvent<WebSocketData>
7
+ outgoing: MessageEvent<WebSocketData>
8
+ close: CloseEvent
9
+ }
24
10
 
25
- /**
26
- * A callback for the close client event.
27
- * This is called when the WebSocket client is closed.
28
- */
29
- abstract onClose: WebSocketTransportOnCloseCallback
11
+ export type StrictEventListenerOrEventListenerObject<EventType extends Event> =
12
+ | ((event: EventType) => void)
13
+ | {
14
+ handleEvent(event: EventType): void
15
+ }
16
+
17
+ export interface WebSocketTransport {
18
+ addEventListener<EventType extends keyof WebSocketTransportEventMap>(
19
+ event: EventType,
20
+ listener: StrictEventListenerOrEventListenerObject<
21
+ WebSocketTransportEventMap[EventType]
22
+ > | null,
23
+ options?: boolean | AddEventListenerOptions
24
+ ): void
25
+
26
+ dispatchEvent<EventType extends keyof WebSocketTransportEventMap>(
27
+ event: WebSocketTransportEventMap[EventType]
28
+ ): boolean
30
29
 
31
30
  /**
32
31
  * Send the data from the server to this client.
33
32
  */
34
- abstract send(data: WebSocketData): void
33
+ send(data: WebSocketData): void
35
34
 
36
35
  /**
37
36
  * Close the client connection.
38
37
  */
39
- abstract close(code?: number, reason?: string): void
38
+ close(code?: number, reason?: string): void
40
39
  }