|
| 1 | +--- |
| 2 | +order: 4 |
| 3 | +title: ws |
| 4 | +description: Intercept WebSocket connections. |
| 5 | +keywords: |
| 6 | + - websocket |
| 7 | + - socket |
| 8 | + - event |
| 9 | + - handler |
| 10 | + - namespace |
| 11 | +--- |
| 12 | + |
| 13 | +The `ws` namespace helps you create event handlers to intercept WebSocket connections. |
| 14 | + |
| 15 | +## Call signature |
| 16 | + |
| 17 | +The `ws` namespace exposes a method called `link()`. The `link()` method creates a WebSocket link preconfigured to handle WebSocket connections matching the specified URL. |
| 18 | + |
| 19 | +```ts |
| 20 | +ws.link(url: string | URL | RegExp) |
| 21 | +``` |
| 22 | + |
| 23 | +import { PageCard } from '../../../components/react/pageCard' |
| 24 | +import { CodeBracketSquareIcon } from '@heroicons/react/24/outline' |
| 25 | + |
| 26 | +<PageCard |
| 27 | + icon={CodeBracketSquareIcon} |
| 28 | + url="https://github.com/mswjs/msw/tree/main/src/core/ws/ws.ts" |
| 29 | + title="ws.ts" |
| 30 | + description="Source code for the `ws` namespace." |
| 31 | +/> |
| 32 | + |
| 33 | +## Event handler |
| 34 | + |
| 35 | +The object returned from the `ws.link()` call is referred to as a _WebSocket link_. The link has the following properties and methods: |
| 36 | + |
| 37 | +### `.addEventListener(event, listener)` |
| 38 | + |
| 39 | +Adds a [connection listener](#connection-listener) for the outgoing WebSocket client connections. |
| 40 | + |
| 41 | +### `.clients` |
| 42 | + |
| 43 | +- `Set<WebSocketClientConnection>` |
| 44 | + |
| 45 | +The set of all active WebSocket clients. |
| 46 | + |
| 47 | +### `.broadcast(data)` |
| 48 | + |
| 49 | +- `data: string | Blob | ArrayBuffer` |
| 50 | + |
| 51 | +Sends the given data to all active WebSocket clients. |
| 52 | + |
| 53 | +```js {2} |
| 54 | +const api = ws.link('wss://*') |
| 55 | +api.broadcast('hello, everyone') |
| 56 | +``` |
| 57 | + |
| 58 | +### `.broadcastExcept(clients, data)` |
| 59 | + |
| 60 | +- `clients: WebSocketClientConnection | Array<WebSocketClientConnection>` |
| 61 | +- `data: string | Blob | ArrayBuffer` |
| 62 | + |
| 63 | +Sends the given data to all active WebSocket clients except the given `clients`. |
| 64 | + |
| 65 | +```js {4} |
| 66 | +const api = ws.link('wss://*') |
| 67 | + |
| 68 | +api.addEventListener('connection', ({ client }) => { |
| 69 | + api.broadcastExcept(client, 'all except this') |
| 70 | +}) |
| 71 | +``` |
| 72 | + |
| 73 | +You can also provide an array of WebSocket client connections as the argument to `clients`: |
| 74 | + |
| 75 | +```js |
| 76 | +const ignoredClients = Array.from(api.clients).filter((client) => { |
| 77 | + return client.url.includes('abc') |
| 78 | +}) |
| 79 | + |
| 80 | +api.broadcastExcept(ignoredClients, 'hello') |
| 81 | +``` |
| 82 | + |
| 83 | +## Connection listener |
| 84 | + |
| 85 | +| Argument | Type | Description | |
| 86 | +| -------- | --------------------------------------------------------- | ---------------------------------------------------- | |
| 87 | +| `client` | [`WebSocketClientConnection`](#websocketclientconnection) | Outgoing WebSocket client connection object. | |
| 88 | +| `server` | [`WebSocketServerConnection`](#websocketserverconnection) | Actual WebSocket server connection object. | |
| 89 | +| `params` | `Record<string, string>` | Path parameters extracted from the connection `url`. | |
| 90 | +| `info` | [`WebSocketConnectionInfo`](#websocketconnectioninfo) | Extra information about this WebSocket connection. | |
| 91 | + |
| 92 | +The connection listener is called on every outgoing WebSocket client connection. |
| 93 | + |
| 94 | +```js {7-9} |
| 95 | +import { ws } from 'msw' |
| 96 | +import { setupWorker } from 'msw/browser' |
| 97 | + |
| 98 | +const api = ws.link('wss://chat.example.com') |
| 99 | + |
| 100 | +const worker = setupWorker( |
| 101 | + api.addEventListener('connection', () => { |
| 102 | + console.log('client connected!') |
| 103 | + }), |
| 104 | +) |
| 105 | + |
| 106 | +await worker.start() |
| 107 | + |
| 108 | +const socket = new WebSocket('wss://chat.example.com') |
| 109 | +socket.onopen = () => { |
| 110 | + console.log('connection established!') |
| 111 | +} |
| 112 | +``` |
| 113 | + |
| 114 | +In this example, the WebSocket connection to `wss://chat.example.com` emits the `"connection"` event on the `api` event handler because the endpoint matches the one provided to the `ws.link()` call. Since the connection is successful, the `"open"` event is also dispatched on the `socket` instance. |
| 115 | + |
| 116 | +## `WebSocketClientConnection` |
| 117 | + |
| 118 | +The `WebSocketClientConnection` object represents an intercepted WebSocket client connection from the _server's_ perspective. This means that the `message` event on the client stands for a message _sent_ by the client and received by the "server". |
| 119 | + |
| 120 | +### `.addEventListener(event, listener, options)` |
| 121 | + |
| 122 | +Adds a listener to the given client event. These are the supported client events: |
| 123 | + |
| 124 | +| Event name | Description | |
| 125 | +| ---------- | --------------------------------------------------------------------- | |
| 126 | +| `message` | Dispatched when this client _sends_ a message. | |
| 127 | +| `error` | Dispatched when this client connection has been closed with an error. | |
| 128 | +| `close` | Dispatched when this client is closed (e.g. by your application). | |
| 129 | + |
| 130 | +### `.removeEventListener(event, listener, options)` |
| 131 | + |
| 132 | +Removes the listener for the given client event. |
| 133 | + |
| 134 | +### `.send(data)` |
| 135 | + |
| 136 | +- `data: string | Blob | ArrayBuffer` |
| 137 | + |
| 138 | +Sends data to the WebSocket client. This is equivalent to the client receiving that data from the server. |
| 139 | + |
| 140 | +```js {2-4} |
| 141 | +api.addEventListener('connection', ({ client }) => { |
| 142 | + client.send('hello') |
| 143 | + client.send(new Blob(['hello'])) |
| 144 | + client.send(new TextEncoder().encode('hello')) |
| 145 | +}) |
| 146 | +``` |
| 147 | + |
| 148 | +### `.close(code, reason)` |
| 149 | + |
| 150 | +- `code: number | undefined`, default: `1000` |
| 151 | +- `reason: string | undefined` |
| 152 | + |
| 153 | +Closes the active WebSocket client connection. |
| 154 | + |
| 155 | +```js {2} |
| 156 | +api.addEventListener('connection', ({ client }) => { |
| 157 | + client.close() |
| 158 | +}) |
| 159 | +``` |
| 160 | + |
| 161 | +Unlike the `WebSocket.prototype.close()` method, the `client.close()` method accepts non-configurable close codes. This allows you to emulate client close scenarios based on server-side errors. |
| 162 | + |
| 163 | +```js {3} |
| 164 | +api.addEventListener('connection', ({ client }) => { |
| 165 | + client.addEventListener('message', (event) => { |
| 166 | + client.close(1003, 'Invalid data') |
| 167 | + }) |
| 168 | +}) |
| 169 | +``` |
| 170 | + |
| 171 | +You can also implement custom close code and reason: |
| 172 | + |
| 173 | +```js {2} |
| 174 | +api.addEventListener('connection', ({ client }) => { |
| 175 | + client.close(4000, 'Custom close reason') |
| 176 | +}) |
| 177 | +``` |
| 178 | + |
| 179 | +## `WebSocketServerConnection` |
| 180 | + |
| 181 | +The `WebSocketServerConnection` object represents the actual WebSocket server connection. |
| 182 | + |
| 183 | +### `.connect()` |
| 184 | + |
| 185 | +Establishes connection to the actual WebSocket server. |
| 186 | + |
| 187 | +```js {2} |
| 188 | +api.addEventListener('connection', ({ server }) => { |
| 189 | + server.connect() |
| 190 | +}) |
| 191 | +``` |
| 192 | + |
| 193 | +### `.addEventListener(event, listener, options)` |
| 194 | + |
| 195 | +Adds a listener to the original server WebSocket connection. The supported events are: |
| 196 | + |
| 197 | +| Event name | Description | |
| 198 | +| ---------- | ----------------------------------------------------------------------------- | |
| 199 | +| `open` | Dispatched when the connection to the original server has been opened. | |
| 200 | +| `message` | Dispatched when the original server _sends_ a message. | |
| 201 | +| `error` | Dispatched when the original server connection has been closed with an error. | |
| 202 | +| `close` | Dispatched when the original server connection has been closed. | |
| 203 | + |
| 204 | +### `.removeEventListener(event, listener, options)` |
| 205 | + |
| 206 | +Removes the listener for the given server event. |
| 207 | + |
| 208 | +### `.send(data)` |
| 209 | + |
| 210 | +- `data: string | Blob | ArrayBuffer` |
| 211 | + |
| 212 | +Sends data to the actual WebSocket server. This is equivalent to the client sending this data to the server. |
| 213 | + |
| 214 | +```js {6} |
| 215 | +api.addEventListener('connection', ({ server }) => { |
| 216 | + server.connect() |
| 217 | + |
| 218 | + server.addEventListener('message', (event) => { |
| 219 | + if (event.data === 'hello from server') { |
| 220 | + server.send('hello from client') |
| 221 | + } |
| 222 | + }) |
| 223 | +}) |
| 224 | +``` |
| 225 | + |
| 226 | +## `WebSocketConnectionInfo` |
| 227 | + |
| 228 | +The `info` argument on the `connection` event listener contains additional WebSocket connection infromation. |
| 229 | + |
| 230 | +| Property name | Type | Description | |
| 231 | +| `protocols` | `string | string[] | undefined` | The list of protocols used when establishing this WebSocket connection. | |
| 232 | + |
| 233 | +```js |
| 234 | +api.addEventListener('connection', ({ info }) => { |
| 235 | + if (info.protocols?.includes('chat')) { |
| 236 | + // ... |
| 237 | + } |
| 238 | +}) |
| 239 | +``` |
0 commit comments