-
-
Notifications
You must be signed in to change notification settings - Fork 535
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 cross-process interception via setupRemoteServer
#1617
base: main
Are you sure you want to change the base?
Conversation
Test failures
This is caused by migrating to HTTP for the internal server. Since Vitest likely runs some of these tests in parallel, it attempts to call This is a good precursor into figuring out whether the internal server port is fixed or random. I likely lean toward making it random and passing it along with SolutionSolved by (1) spawning the internal server at a random port; (2) propagating the entire server URL through environment variable ( |
687e58b
to
172dbd7
Compare
Task: Ignore internal requests in life-cycle reportingInternal MSW requests is a new thing with this pull request, such as a request to the remote HTTP server, or internal requests that forward life-cycle events. Those internal requests bubble to the life-cycle event emitting, and result in entries there. Need to add some flag to ignore certain requests from the life-cycle API altogether. It may be an ✅ Solved by introducing a check for the
|
172dbd7
to
786c46b
Compare
786c46b
to
69a26dc
Compare
🐞 ECONNREFUSED on life-cycle event forwardingSometimes the following error is thrown while using the new API:
|
d6236aa
to
3826cad
Compare
commit: |
79ae842
to
e3f4c44
Compare
e3f4c44
to
b0b0381
Compare
0eb583f
to
bc0e0a7
Compare
🐞 Life-cycle event forwarding order sensitivityLCE are sensitive to order. But when forwarded, some events may arrive faster over HTTP, breaking that order: FAIL test/node/msw-api/setup-remote-server/life-cycle-event-forwarding.node.test.ts > emits correct events for the request handled in the test process
AssertionError: expected [ …(4) ] to deeply equal [ …(4) ]
- Expected
+ Received
Array [
Array [
"[request:start] GET https://example.com/resource ade0140740d15",
],
Array [
- "[request:match] GET https://example.com/resource ade0140740d15",
+ "[request:end] GET https://example.com/resource ade0140740d15",
],
Array [
- "[request:end] GET https://example.com/resource ade0140740d15",
+ "[request:match] GET https://example.com/resource ade0140740d15",
],
Array [
"[response:mocked] GET https://example.com/resource ade0140740d15 200 {\"mocked\":true}",
],
] Need to solve this. |
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.
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)Blockers
socket.io-parser
are broken for the CJS build).setupRemoteServer
#1617 (comment)).