Skip to content
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

feat: support mocking WebSocket APIs #2011

Merged
merged 123 commits into from
Oct 29, 2024
Merged

feat: support mocking WebSocket APIs #2011

merged 123 commits into from
Oct 29, 2024

Conversation

kettanaito
Copy link
Member

@kettanaito kettanaito commented Feb 3, 2024

Roadmap

  • Support runtime overrides for event handlers.
  • Extend Handler in RequestHandler. This way HttpHandler and GraphQLHandler will be assignable to type Handler, and Array<Handler> can annotate both HTTP and WebSocket handlers.
    • Bad idea. Even abstracted, RequestHandler relies on caching to clone requests, parsed result, etc. Not enough common ground to reuse between it and WebSocketHandler. WS handler is better off as a standalone class (doesn't support once anyway).
  • Call handleWebSocketEvent() in SetupWorkerApi and SetupServerApi.
  • handleRequest: Check that the handler is instanceof RequestHandler to skip WebSocket handlers.
  • Update to @mswjs/interceptors that ships WebSocketInterceptor.
  • Improve types. The data argument of message listeners must be annotated across the board (includes annotating the MessageEvent in the Interceptors).
    • Not a good idea. The data you receive is still string | Blob | ArrayBuffer. You cannot just cast it to something else. Instead, introduce a custom parser that does that casting, as well as the runtime typeof checks. That's the way.
    • Document this approach.
  • Add links to jsdoc blocks of the new API.
  • Bug: look into why pnpm test:modules:node hangs forever on setupServer() without the explicit process.exit() in the generated runtime.* scripts.
  • Add Node.js tests.
  • Add browser tests.
  • Add .use() override tests (i.e. event.stopImmediatePropagation()).
  • Defer the interceptor application to the .start()/.listen() calls.
  • Add server.close() API (a task for Interceptors, here just add tests).
  • Invoke onUnhandledRequest() (or similar) for unhandled WebSocket connections. Technically, there are no requests, so the existing method reads weird. WebSocket Support Beta #2010 (comment)
  • feat(ws): add logging to WebSocket connections #2112
  • WebSocket: Decide how to handle actual server errors interceptors#539
  • Forward client events to the server by default (WebSocket Support Beta #2010 (comment)).
  • Fix .broadcast() not broadcasting from N(1) -> N+ but working from N+ -> N+. (discovered in add WebSocket + ws example examples#111) (fix(WebSocketClientManager): use localStorage for clients persistence #2127).
  • Annotate this: WebSocket in the client/server event listeners.
  • Refreshing the page keeps adding the same client under a new ID to the localStorage (fix: fix: purge persisted clients on page reload #2133)
Screenshot 2024-04-12 at 13 09 35
  • Remove console.log (
    console.log('get clients()', inMemoryClients, this.getSerializedClients())
    )
  • Update the "Client-to-server forwarding" docs. Both forwarding is automatic now, and can be opt-out by calling event.preventDefault(). (proof)
  • Docs: document how runtime handlers are supposed to work (they are event-based) WebSocket Support Beta #2010 (reply in thread)
  • WebSocketClientManager still has the "first-tab-only" issue with multiple tabs.
  • wss://localhost:* (* as port throws on path-to-regexp). Quite likely fixed by Support URLPattern as request predicate #1921
  • Fix an issue where having multiple event handlers on the same page leads to the DOMException: Key already exists in the object store. error from IndexedDB (concurrent writes to the db).
  • Fix (node:15076) MaxListenersExceededWarning: Possible EventTarget memory leak detected in pnpm test:unit src/core/ws/WebSocketClientManager.test.ts

@@ -18,9 +18,13 @@ import { createFallbackStop } from './stop/createFallbackStop'
import { devUtils } from '~/core/utils/internal/devUtils'
import { SetupApi } from '~/core/SetupApi'
import { mergeRight } from '~/core/utils/internal/mergeRight'
import { LifeCycleEventsMap } from '~/core/sharedOptions'
import type { LifeCycleEventsMap } from '~/core/sharedOptions'
import type { WebSocketHandler } from '~/core/handlers/WebSocketHandler'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Do you want to bundle the handlers together or should these handler placed in the ~/core/ws ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I don't understand what you mean. Can you elaborate?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the related imports to WebSockets are coming from the core/ws. So i'm only suggesting that the WebSocketHandler should we imported from this path as well.

Something like this;

import type { WebSocketHandler } from '~/core/ws/handlers/WebSocketHandler'
...
import { webSocketInterceptor } from '~/core/ws/webSocketInterceptor'
import { handleWebSocketEvent } from '~/core/ws/handleWebSocketEvent'
import { attachWebSocketLogger } from '~/core/ws/utils/attachWebSocketLogger'

Or do you want to separate these functionalities and types?

src/core/utils/handleRequest.ts Outdated Show resolved Hide resolved
src/core/ws/WebSocketClientManager.ts Show resolved Hide resolved
@kettanaito
Copy link
Member Author

📦 v2.3.0-ws.rc-10

  • Expose the server WebSocket instance on server.socket.
  • Make the close server event cancelable.

@kettanaito
Copy link
Member Author

📦 2.3.0-ws.rc-11

  • Simplified logging events.

@kettanaito
Copy link
Member Author

📦 v2.3.0-ws.rc-12

  • Supports event.stopPropagation() and event.stopImmediatePropagation() for client and server events. You can now manage the event flow across different listeners and event handlers with better granularity.

@kesupile
Copy link

kesupile commented Oct 21, 2024

📦 v2.3.0-ws.rc-12

  • Supports event.stopPropagation() and event.stopImmediatePropagation() for client and server events. You can now manage the event flow across different listeners and event handlers with better granularity.

This works really well for our needs; thanks for all the hard work!

Is there a timeline for when it might be included in the main release branch? I noticed that none of this functionality is included in release 2.4.11

@kettanaito
Copy link
Member Author

kettanaito commented Oct 21, 2024

@kesupile, happy to hear that!

The WebSocket support is a release candidate right now. I'm planning on publishing it this year after I wrap up the remaining bits. You can use the release candidate for now, it will land on main as pretty much 1-1. I also try to keep this branch up-to-date to have the latest changes from main included in the release candidate.

Also, if you're using this at work, please consider becoming MSW sponsor 🙏 I could use your support to keep improving and maintaining the project.

Let me know if you have any feedback while using the new API!

@kettanaito kettanaito merged commit ae786f5 into main Oct 29, 2024
11 checks passed
@kettanaito kettanaito deleted the feat/ws branch October 29, 2024 11:16
@kettanaito
Copy link
Member Author

Released: v2.6.0 🎉

This has been released in v2.6.0!

Make sure to always update to the latest version (npm i msw@latest) to get the newest features and bug fixes.


Predictable release automation by @ossjs/release.

@AlexDroll
Copy link

Hi @kettanaito
I fear that this PR introduced a breaking change in e.g. the listHandlers() method, since the return types changed (see here].
This is also not reflected yet in the documentation see for example here

@kettanaito
Copy link
Member Author

kettanaito commented Oct 30, 2024

Hi, @AlexDroll. Thanks for sharing this.

Adding a new type to the union isn't a breaking change. I will update the docs to mention that the method now returns all handlers. PR: mswjs/mswjs.io#427

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support mocking WebSocket APIs
6 participants