-
-
Notifications
You must be signed in to change notification settings - Fork 518
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support mocking WebSocket APIs #156
Comments
Hey, @Doesntmeananything, thanks for bringing this topic up. I'd love to bring SSE and WS support to the users of MSW. I admit I haven't researched the topic yet, but would use this thread for this. Technically, it comes down to the ability of Service Worker to intercept those requests/events. If the spec supports it, there shouldn't be much changes needed on the MSW side. Here's some useful resources:
Could you please try to set up a proof of concept, if those events can be intercepted in the worker's You're right about the custom request handler, we can use it to log all intercepted requests: setupWorker(
{
predicate(req) {
console.log(req)
// log captured requests, but bypass them
return false
},
resolver: () => null
}
) If we confirm it working, I'd be glad to discuss the API for a dedicated request handler for WebSocket/SSE. I can read on their specification meanwhile. |
Sounds like a plan! At a cursory glance, it does indeed seem quite doable. Let me PoC this, and I'll get back to you with my results as soon as I can. |
Hi, @kettanaito! I've set up a (very quick and dirty) repository to test these interactions over at https://github.com/Doesntmeananything/msw-sse-ws. My initial findings are the following:
I'm a bit concerned about WS events, although I hope that with some additional work it'd possible to intercept them. |
@Doesntmeananything, thank you for the investigation! I'm excited to hear that SSE can be intercepted! Wonder if there's anything we can do it intercept events as they go. I'm currently working on a NodeJS support, but can switch to this issue to help you once I'm done. I'm always open to questions or discussions, so please don't hesitate to raise those here.
|
I'm trying to get my head around the SSE example. It seems MSW should intercept the
|
One of the most useful pieces of code I've found in the w3c discussion (w3c/ServiceWorker#947 (comment)) was that the Service Worker file can establish a WebSocket connection. It appears that the WS events are not subjected to be intercepted in the If it comes down to the manual WS connection, I'd suggest to do that on the client's side, not in the worker file. There's no technical reason to move this logic to worker, at least as of how I understand such implementation now. |
Thanks very much for taking the time to look further into this! Since I've hit the wall in regards to intercepting WS connections, your suggestions come in handy. Will definitely look into this. To be clear, are you saying that mocking WS connections falls strictly outside of MSW concerns? My investigations lead me to believe this, and I would certainly not want to push for something that doesn't make sense neither on technical nor on conceptual level. |
Not necessarily. What I was trying to say is that a WebSocket event is not intercepted by the |
I've received a suggestion to look at |
Update: I've started with the WebSocket support and will keep you updated as I progress. For those interested I will post some technical insights into what that support means, what technical challenges I've faced, and what API to expect as the result. Session 1: It's all about socketsNo service for the workerUnfortunately, WebSocket events cannot be intercepted in the
Goodbye, handlers!WebSocket operates with events, not requests, making the concept of request handler in this context redundant. Instead, you should be able to receive and send messages from import { rest, ws, setupWorker } from 'msw'
// Create an interception "server" at the given URL.
const todos = ws.link('wss://api.github.com/todos')
setupWorker(
rest.put('/todo', (req, res, ctx) => {
const nextTodos = prevTodos.concat(req.body)
// Send the data to all WebSocket clients,
// for example from within a request handler.
todos.send(nextTodos)
return res(ctx.json(nextTodos))
})
)
// Or as a part of arbitrary logic.
setInterval(() => todos.send(Math.random()), 5000) URL that got awayWhen constructing a I've ended up re-implementing a Persisting WebSocket clientsThe entire idea of WebSocket is to sync data between multiple clients in real time. When you dispatch a mocked Usually a solution to this kind of problems is to lift the state up and maintain a record of clients in the upper context shared with all the clients (pages). However, in JavaScript there isn't that may ways to share and persist data between clients. In case of WebSocket clients one needs to store references to
const channel = new BroadcastChannel('ws-support')
// One client sends a data.
channel.send('some-data')
// All clients can react to it.
channel.addEventListener('message', (event) => {
event.data // "some-data"
}) I find |
You could use an ES6 Proxy. It can mess with ctors. |
SSE and WebSockets are different issues. |
@BlackGlory, MSW should support |
@kettanaito Although export const worker = setupWorker(
rest.get('/sse', (req, res, ctx) => {
return res(
ctx.status(200)
, ctx.set('Content-Type', 'text/event-stream')
, ctx.body(sse(function* () {
yield 'message1'
yield 'message2'
}))
)
})
)
function sse(gfn) {
let iter
return new ReadableStream({
start() {
iter = gfn()
}
, pull(controller) {
controller.enqueue(`data: ${iter.next().value}\n\n`)
}
})
} Browser:
Node.js:
|
Hey, @BlackGlory. Could you please report this as a separate issue? Thanks. |
@SerkanSipahi, the issue is that only browser-side interception is not a finite feature. I wouldn't merge things that don't make sense on their own into The best I can do is release the browser implementation under a Honestly, this makes little sense to me, and if you wish to give this a try, consider using GitHub pull requests as dependencies in your project. Add the Contributing to the |
Hello @kettanaito! My team and I have been following this thread, and we saw that the change has been merged in so that msw will support EventSource. I was hoping to inquire when an official release might be happening that includes this update? Thank you in advance! |
Hey, @wade-gooch-kr. Excited to hear that. I'm in the middle of some test rewrites, I will publish that branch when I have a minute. Meanwhile, you can specify that PR as your dependency in package.json and should be able to try it out. |
Hey, whats the status on this? It seems like some part of the job was done in this merged PR 🤔 Should this be marked closed / done or is this still WIP? |
September 2023 Status Updatemswjs/interceptors#236 (comment) @Stackustack, supporting SSE is unfortunately not enough to ship WebSocket support. See the status update in the linked comment above. |
UpdateI've had some success implementing a WebSocket class-based interceptor (mswjs/interceptors#501). This means that the WebSocket support is coming to MSW rather soon! The browser test suite is currently passing. The Node.js test suite using Undici's Now, before anyone gets overly excited about this, let me clarify a few things.
What's left?Feedback. You can help land this API sooner by helping with the following:
Meanwhile, I will improve the test coverage of the interceptor to make sure it's fully compatible with the standard when you're using it. |
Turns out that the initial WebSocket implementation will support SocketIO also! If you want to be able to mock SocketIO with MSW, please read and upvote this: Thank you. |
WebSocket Support BetaPlease participate in the discussion about the upcoming WebSocket API to help us shape it and ship it faster: Thank you! |
I'm renaming this issue so it focuses on the WebSocket API mocking exclusively. Server-Sent Events (SSE) are quite different, and to my best knowledge, they can be intercepted by the Service Worker. Their implementation will be different. If someone needs it, I encourage you to open a new feature proposal and describe it in more detail (e.g. how you are using SSE in your application, how you imagine mocking to look like, etc). |
Update: Give the RC a try!You can install the RC with the WebSocket support today: Please participate and share your feedback! The more feedback we get, the faster and better the end API will be released. Thank you! |
@kettanaito I am getting when using websocket mocks:
Code I am trying:
Platform: Node |
@95th, hi! What version of Node.js are you running? It looks like it's older than v18. MSW itself doesn't support Node.js <18. Please update and have the global |
I have a regression with Resolved: looks like I can listen for events directly on the
|
@johnhunter, hi. That is not a regression but a change in the API. I've removed |
Released: v2.6.0 🎉This has been released in v2.6.0! Make sure to always update to the latest version ( Predictable release automation by @ossjs/release. |
👋 Is it expected to have to mock
using the node import { setupServer } from 'msw/node';
export const server = setupServer(...handlers); Since the Cheers, |
Hi, @AdrienFromToulouse. Thanks for reporting his. Looks like the same issue as mswjs/data#306 (comment). Please use the supported version range of Node.js and stay away from browser-like environments that meddle with your environment.
No. |
Thank you for you detailed answer and thank you for maintaining |
I will do my best to make in-browser automation accessible and clear in the months to come. This experience has to go, it's absurd. Sorry you have to go through this. It will get better. |
I am planning to migrate to |
Is it possible to use msw to mock server-sent events (SSE) and WebSocket (WS) connections?
My use case is to have a somewhat full client-side mocking story (including REST, SSE, and WS functionality), and since msw is such a joy to use when mocking out REST APIs, I was wondering if it makes sense to use it to mock more specialised server interactions.
Have you thought about this? I admit that I haven't looked much into whether it's possible just to use custom request handlers to add this functionality, emulating SSE and WS behaviour in some way. I wanted to know if you already had something in mind regarding this question. Thanks!
The text was updated successfully, but these errors were encountered: