The defineNetwork()
API
#2488
Replies: 2 comments 4 replies
-
I love the idea and the initiative to expose APIs for advanced cases. Nowadays I rarely use
I usually use the exposed
Yes please!!! With the release of On a side-note: I sometimes use |
Beta Was this translation helpful? Give feedback.
-
Thinking about this a bit, I start to wonder about the usefulness of In my head I am mostly focusing on the Playwright example. One thing that can really help here is a import {
test as testBase,
type Page,
type Request as PWRequest,
type Route,
} from "@playwright/test";
import { getResponse, type RequestHandler } from "msw";
import type { SetupServer } from "msw/node";
// Imagine such API exposed by MSW. Of course it should be more refined and nuanced, e.g. be Iterable etc...
type HandlerCollection = Pick<
SetupServer,
"use" | "resetHandlers" | "restoreHandlers" | "listHandlers"
>;
export const test = testBase.extend<{ handlers: HandlerCollection }>({
handlers: [
async ({ page }, use) => {
const collection = new HandlerCollection(initialHandlers);
const cleanup = await setupRequestInterception(page, collection);
await use(collection);
await cleanup();
},
{
auto: true,
scope: "test",
},
],
});
async function setupRequestInterception(
page: Page,
collection: HandlerCollection,
): Promise<() => Promise<void>> {
async function handler(route: Route, request: PWRequest) {
const nativeRequest = new Request(request.url(), {
method: request.method(),
headers: new Headers(await request.allHeaders()),
body: request.postDataBuffer(),
});
const response = await getResponse(
collection.listHandlers() as RequestHandler[], // Conflict with WebSocketHandler...
nativeRequest,
);
return !response
? route.continue()
: route.fulfill({
status: response.status,
headers: Object.fromEntries(response.headers),
body: Buffer.from(await response.arrayBuffer()),
});
}
await page.route(/.+/, handler);
return () => page.unroute(/.+/);
} |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
MSW is a combination of two things: request interception algorithm and the handler resolution API to handle outgoing requests. Right now, the recommended way is to use it as a whole or implement custom resolution logic using the exposed utilities like
handleRequest()
.That might not be enough for some advanced cases. For once,
handleRequest()
is quite implementation detail heavy and doesn't feel like a public API. It would be great if MSW could be used as a function of network source:MSW has always been architectured around the concept of a request stream coming from somewhere and then getting handled by the user's handlers. I think it's time we exposed that as an actual public API.
Proposal
This proposal comes down to implementing network handling primitives that accept any source of network, either a request stream from a Service Worker or requests from
page.route()
in Playwright, and produce a response given a list of handlers.Important
Conceptually, the proposed
defineNetwork()
API replacessetupWorker
andsetupServer
and introduces a single congruent way to create thenetwork
controller.setupWorker
andsetupServer
function are left intact as they provide a better default experience.defineNetwork()
The object returned from the
defineNetwork()
function allows you to control the network as you would with the similar control objects fromsetupWorker
/setupServer
:Example
Here's a contrived example of using the proposed API to integrate MSW into Playwright tests and utilize
page.route()
as the request interception algorithm:Prerequisites
Handler
type that's maintained by us. Right now, it's clunky to create manual unions ofRequestHandler | WebSocketHandler
to correctly annotate "any" handler type.handleRequest()
and replace it with a nicer API.Questions
interceptors: [...]
instead?Interceptor
class is an established pattern that doesn't behave as the proposed network source, in a way that it's not tightly integrated with MSW's internal request resolution logic. I think we should leave interceptors be interceptors and have a new concept of a "source" that hooks into the library.NetworkSource.fromInterceptors(...interceptors)
to map the interceptor events to the request/response stream expected by the network source. In fact, I would refactor the internals to be just that.NetworkSource
expose side effect-based methods likethis.requestStream.push()
or actually expose a method that returns a mocked response, if any:this.handleRequest(): Response | undefined
? I feel like the second would be more explicit and powerful.Beta Was this translation helpful? Give feedback.
All reactions