Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@
"@ossjs/release": "^0.10.1",
"@playwright/test": "^1.57.0",
"@types/node": "^22.15.29",
"@types/sinon": "^21.0.0",
"msw": "^2.12.7",
"sinon": "^21.0.1",
"tsdown": "^0.12.7",
"typescript": "^5.9.3",
"vite": "^7.3.1"
Expand Down
77 changes: 77 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

109 changes: 65 additions & 44 deletions src/fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import type {
} from '@playwright/test'
import {
type LifeCycleEventsMap,
type UnhandledRequestStrategy,
SetupApi,
RequestHandler,
WebSocketHandler,
getResponse,
handleRequest,
} from 'msw'
import {
type WebSocketClientEventMap,
Expand All @@ -26,7 +27,8 @@ import {
} from '@mswjs/interceptors/WebSocket'

export interface CreateNetworkFixtureArgs {
initialHandlers: Array<RequestHandler | WebSocketHandler>
initialHandlers?: Array<RequestHandler | WebSocketHandler>
onUnhandledRequest?: UnhandledRequestStrategy
}

/**
Expand All @@ -50,7 +52,6 @@ export interface CreateNetworkFixtureArgs {
*/
export function createNetworkFixture(
args?: CreateNetworkFixtureArgs,
/** @todo `onUnhandledRequest`? */
): [
TestFixture<NetworkFixture, PlaywrightTestArgs & PlaywrightWorkerArgs>,
{ auto: boolean },
Expand All @@ -60,6 +61,7 @@ export function createNetworkFixture(
const worker = new NetworkFixture({
page,
initialHandlers: args?.initialHandlers || [],
onUnhandledRequest: args?.onUnhandledRequest,
})

await worker.start()
Expand All @@ -79,19 +81,19 @@ export function createNetworkFixture(
export const INTERNAL_MATCH_ALL_REG_EXP = /.+(__MSW_PLAYWRIGHT_PREDICATE__)?/

export class NetworkFixture extends SetupApi<LifeCycleEventsMap> {
#page: Page

constructor(args: {
page: Page
initialHandlers: Array<RequestHandler | WebSocketHandler>
}) {
constructor(
protected args: {
page: Page
initialHandlers: Array<RequestHandler | WebSocketHandler>
onUnhandledRequest?: UnhandledRequestStrategy
},
) {
super(...args.initialHandlers)
this.#page = args.page
}

public async start(): Promise<void> {
// Handle HTTP requests.
await this.#page.route(
await this.args.page.route(
INTERNAL_MATCH_ALL_REG_EXP,
async (route: Route, request: Request) => {
const fetchRequest = new Request(request.url(), {
Expand All @@ -100,13 +102,29 @@ export class NetworkFixture extends SetupApi<LifeCycleEventsMap> {
body: request.postDataBuffer(),
})

const response = await getResponse(
this.handlersController.currentHandlers().filter((handler) => {
const handlers = this.handlersController
.currentHandlers()
.filter((handler) => {
return handler instanceof RequestHandler
}),
})

/**
* @note Use `handleRequest` instead of `getResponse` so we can pass
* the `onUnhandledRequest` option as-is and benefit from MSW's default behaviors.
*/
const response = await handleRequest(
fetchRequest,
crypto.randomUUID(),
handlers,
{
baseUrl: this.getPageUrl(),
onUnhandledRequest: this.args.onUnhandledRequest || 'bypass',
},
this.emitter,
{
resolutionContext: {
quiet: true,
baseUrl: this.getPageUrl(),
},
},
)

Expand All @@ -131,44 +149,47 @@ export class NetworkFixture extends SetupApi<LifeCycleEventsMap> {
)

// Handle WebSocket connections.
await this.#page.routeWebSocket(INTERNAL_MATCH_ALL_REG_EXP, async (ws) => {
const allWebSocketHandlers = this.handlersController
.currentHandlers()
.filter((handler) => {
return handler instanceof WebSocketHandler
})

if (allWebSocketHandlers.length === 0) {
ws.connectToServer()
return
}
await this.args.page.routeWebSocket(
INTERNAL_MATCH_ALL_REG_EXP,
async (ws) => {
const allWebSocketHandlers = this.handlersController
.currentHandlers()
.filter((handler) => {
return handler instanceof WebSocketHandler
})

const client = new PlaywrightWebSocketClientConnection(ws)
const server = new PlaywrightWebSocketServerConnection(ws)
if (allWebSocketHandlers.length === 0) {
ws.connectToServer()
return
}

for (const handler of allWebSocketHandlers) {
await handler.run(
{
client,
server,
info: { protocols: [] },
},
{
baseUrl: this.getPageUrl(),
},
)
}
})
const client = new PlaywrightWebSocketClientConnection(ws)
const server = new PlaywrightWebSocketServerConnection(ws)

for (const handler of allWebSocketHandlers) {
await handler.run(
{
client,
server,
info: { protocols: [] },
},
{
baseUrl: this.getPageUrl(),
},
)
}
},
)
}

public async stop(): Promise<void> {
super.dispose()
await this.#page.unroute(INTERNAL_MATCH_ALL_REG_EXP)
await unrouteWebSocket(this.#page, INTERNAL_MATCH_ALL_REG_EXP)
await this.args.page.unroute(INTERNAL_MATCH_ALL_REG_EXP)
await unrouteWebSocket(this.args.page, INTERNAL_MATCH_ALL_REG_EXP)
}

private getPageUrl(): string | undefined {
const url = this.#page.url()
const url = this.args.page.url()
return url !== 'about:blank' ? url : undefined
}
}
Expand Down
29 changes: 0 additions & 29 deletions tests/auto.test.ts

This file was deleted.

Loading