-
-
Notifications
You must be signed in to change notification settings - Fork 562
feat: support cross-process interception via setupRemoteServer
#1617
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
Open
kettanaito
wants to merge
287
commits into
main
Choose a base branch
from
feat/ws-sync-handlers
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+4,025
−279
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1905467
to
85c0c5a
Compare
6e57192
to
d9d07df
Compare
535f280
to
209b65c
Compare
b7d905e
to
837f141
Compare
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Intention
Introduce an API that allows one process to modify the traffic of another process. The most apparent application for this is testing server-side behaviors of a JavaScript application:
This API is designed exclusively for use cases when the request-issuing process and the request-resolving process (i.e. where you run MSW) are two different processes.
Important
While the feature itself is unopinionated in how it's used, in practice it implies that you either have a fixed list of handlers OR you provide test-app isolation by spawning an app instance per test case. That is so handler overrides in one test don't affect the remote network for other tests. The app becomes a shared state here with no means to distinguish who is the consumer (test -> client -> server chain loses any identifiers).
Proposed API
With consideration to the existing MSW user experience, I suggest we add a
setupRemoteServer()
API that implements theSetupApi
interface and has a similar API tosetupServer
. The main user-facing distinction here is thatsetupRemoteServer
is affecting a remote process, as indicated by the name.The
.listen()
and.close()
methods of the remote server become async since they now establish and terminate an internal server instance respectively.You can then operate with the
remote
server as you would with a regularsetupServer
, keeping in mind that it doesn't affect the current process (your test) but instead, any remote process that runssetupServer
(your app).By fully extending the
SetupApi
, thesetupRemoteServer
API provides the user with full network-managing capabilities. This includes defining initial and runtime request handlers, as well as observing the outgoing traffic of a remote process using the Life-cycle API (remote.events.on(event, listener)
). I think this is a nice familiarity that also provides the user with more power when it comes to controlling the network.Implementation
I've considered multiple ways of implementing this feature. Listing them below.
(Chosen) WebSocket server
The
setupRemoteServer
API can establish an internal WebSocket server that can route the outgoing traffic from any server-side MSW instance anywhere and deliver it to the remote server to potentially resolve.Technically, the WebSocket server acts as a resolution point (i.e. your handlers) while the remote MSW process acts as a request supplier (similar to how the Service Worker acts in the browser).
Very roughly, this implies that the regular
setupServer
instances now have a fixed request handler that tries to check if any outgoing request is potentially handled by an existing remote WebSocket server:If no WebSocket server was found or establishing a connection with it fails within a sensible timeout period (~500ms), the
setupServer
instance of the app continues to operate as normal.IPC
The test process and the app process can utilize IPC (interprocess communication) to implement a messaging protocol. Using that protocol, the app can signal back any outgoing requests and the test can try resolving them against the request handlers you defined immediately in the test.
This approach is similar to the WebSocket approach above with the exception that it relies on IPC instead of a standalone running server. With that, it also gains its biggest disadvantage: the app process must be a child process of the test process. This is not easy to guarantee. Depending on the framework's internal implementation, the user may not achieve this parent/child relationship, and the IPC implementation will not work.
Given such a demanding requirement, I've decided not to use this implementation.
Limitations
useRemoteServer()
affects the network resolution for the entire app. This means that you cannot have multiple tests that override request handlers for the same app at the same time. I think this is more than reasonable since you know you're running 1 app instance that can only behave in a single way at a single point in time. Still, I expect users to be confused when they parallelize their E2E tests and suddenly see some network behaviors leaking across the test cases.Concerns
setupRemoteServer
only affects the server-side network behavior of any running application process with the server-side MSW integration? To affect the client-side network behavior from a test you have to 1) havesetupWorker
integration in the app; 2) set a globalwindow.worker
instance; 3) usewindow.worker.use()
to add runtime request handlers. This stays as it is right now, no changes here.The API is TBD and is subjected to change.
Roadmap
rest.all()
handler.response
insetupServer
.ReadableStream
from the remote request handler (may consider transferringReadableStream
over the WS messages instead ofArrayBuffer
, if that's allowed).ReadableStream
transfer over WebSockets that would be great.remotePort
andport
an implementation detail ofsetupRemoteServer
andsetupServer({ remote: true })
. The developer mustn't care about those.use()
test (may have something to do with the handlers management refactoring as a part of theserver.boundary()
).setupServer
(that it doesn't emit them for internal requests).setupWorker
support (see feat: support cross-process interception viasetupRemoteServer
#1617 (comment)).socket.io
in favor ofws
if we don't need anything socketio-specificforwardLifeCycleEvents()
).ListenOptions
onsetupRemoteServer()
, likeonUnhandledRequest
.spyOnLifeCycleEvents
test utility importingvitest
when it can also be run in the browser.setupRemoteServer
#1617 (comment)setupRemoteServer
#1617 (review)).Blockers
socket.io-parser
are broken for the CJS build).setupRemoteServer
#1617 (comment)).