@msw/playwright 0.4.3 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.d.ts +16 -3
- package/build/index.js +35 -13
- package/package.json +6 -4
- package/src/fixture.ts +65 -44
package/build/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { LifeCycleEventsMap, RequestHandler, SetupApi, WebSocketHandler } from "msw";
|
|
1
|
+
import { LifeCycleEventsMap, RequestHandler, SetupApi, UnhandledRequestStrategy, WebSocketHandler } from "msw";
|
|
2
2
|
import { Page, PlaywrightTestArgs, PlaywrightWorkerArgs, TestFixture } from "@playwright/test";
|
|
3
3
|
|
|
4
|
-
//#region src/
|
|
4
|
+
//#region src/fixture.d.ts
|
|
5
5
|
interface CreateNetworkFixtureArgs {
|
|
6
6
|
initialHandlers: Array<RequestHandler | WebSocketHandler>;
|
|
7
|
+
onUnhandledRequest?: UnhandledRequestStrategy;
|
|
7
8
|
}
|
|
8
9
|
/**
|
|
9
10
|
* Creates a fixture that controls the network in your tests.
|
|
@@ -27,11 +28,23 @@ interface CreateNetworkFixtureArgs {
|
|
|
27
28
|
declare function createNetworkFixture(args?: CreateNetworkFixtureArgs): [TestFixture<NetworkFixture, PlaywrightTestArgs & PlaywrightWorkerArgs>, {
|
|
28
29
|
auto: boolean;
|
|
29
30
|
}];
|
|
31
|
+
/**
|
|
32
|
+
* @note Use a match-all RegExp with an optional group as the predicate
|
|
33
|
+
* for the `page.route()`/`page.unroute()` calls. Playwright treats given RegExp
|
|
34
|
+
* as the handler ID, which allows us to remove only those handlers introduces by us
|
|
35
|
+
* without carrying the reference to the handler function around.
|
|
36
|
+
*/
|
|
37
|
+
|
|
30
38
|
declare class NetworkFixture extends SetupApi<LifeCycleEventsMap> {
|
|
31
|
-
|
|
39
|
+
protected args: {
|
|
40
|
+
page: Page;
|
|
41
|
+
initialHandlers: Array<RequestHandler | WebSocketHandler>;
|
|
42
|
+
onUnhandledRequest?: UnhandledRequestStrategy;
|
|
43
|
+
};
|
|
32
44
|
constructor(args: {
|
|
33
45
|
page: Page;
|
|
34
46
|
initialHandlers: Array<RequestHandler | WebSocketHandler>;
|
|
47
|
+
onUnhandledRequest?: UnhandledRequestStrategy;
|
|
35
48
|
});
|
|
36
49
|
start(): Promise<void>;
|
|
37
50
|
stop(): Promise<void>;
|
package/build/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { invariant } from "outvariant";
|
|
2
|
-
import { RequestHandler, SetupApi, WebSocketHandler,
|
|
2
|
+
import { RequestHandler, SetupApi, WebSocketHandler, handleRequest } from "msw";
|
|
3
3
|
import { CancelableCloseEvent, CancelableMessageEvent } from "@mswjs/interceptors/WebSocket";
|
|
4
4
|
|
|
5
|
-
//#region src/
|
|
5
|
+
//#region src/fixture.ts
|
|
6
6
|
/**
|
|
7
7
|
* Creates a fixture that controls the network in your tests.
|
|
8
8
|
*
|
|
@@ -26,29 +26,40 @@ function createNetworkFixture(args) {
|
|
|
26
26
|
return [async ({ page }, use) => {
|
|
27
27
|
const worker = new NetworkFixture({
|
|
28
28
|
page,
|
|
29
|
-
initialHandlers: args?.initialHandlers || []
|
|
29
|
+
initialHandlers: args?.initialHandlers || [],
|
|
30
|
+
onUnhandledRequest: args?.onUnhandledRequest
|
|
30
31
|
});
|
|
31
32
|
await worker.start();
|
|
32
33
|
await use(worker);
|
|
33
34
|
await worker.stop();
|
|
34
35
|
}, { auto: true }];
|
|
35
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* @note Use a match-all RegExp with an optional group as the predicate
|
|
39
|
+
* for the `page.route()`/`page.unroute()` calls. Playwright treats given RegExp
|
|
40
|
+
* as the handler ID, which allows us to remove only those handlers introduces by us
|
|
41
|
+
* without carrying the reference to the handler function around.
|
|
42
|
+
*/
|
|
43
|
+
const INTERNAL_MATCH_ALL_REG_EXP = /.+(__MSW_PLAYWRIGHT_PREDICATE__)?/;
|
|
36
44
|
var NetworkFixture = class extends SetupApi {
|
|
37
|
-
#page;
|
|
38
45
|
constructor(args) {
|
|
39
46
|
super(...args.initialHandlers);
|
|
40
|
-
this
|
|
47
|
+
this.args = args;
|
|
41
48
|
}
|
|
42
49
|
async start() {
|
|
43
|
-
|
|
50
|
+
await this.args.page.route(INTERNAL_MATCH_ALL_REG_EXP, async (route, request) => {
|
|
44
51
|
const fetchRequest = new Request(request.url(), {
|
|
45
52
|
method: request.method(),
|
|
46
53
|
headers: new Headers(await request.allHeaders()),
|
|
47
54
|
body: request.postDataBuffer()
|
|
48
55
|
});
|
|
49
|
-
const
|
|
56
|
+
const handlers = this.handlersController.currentHandlers().filter((handler) => {
|
|
50
57
|
return handler instanceof RequestHandler;
|
|
51
|
-
})
|
|
58
|
+
});
|
|
59
|
+
const response = await handleRequest(fetchRequest, crypto.randomUUID(), handlers, { onUnhandledRequest: this.args.onUnhandledRequest || "warn" }, this.emitter, { resolutionContext: {
|
|
60
|
+
quiet: true,
|
|
61
|
+
baseUrl: this.getPageUrl()
|
|
62
|
+
} });
|
|
52
63
|
if (response) {
|
|
53
64
|
if (response.status === 0) {
|
|
54
65
|
route.abort();
|
|
@@ -62,9 +73,8 @@ var NetworkFixture = class extends SetupApi {
|
|
|
62
73
|
return;
|
|
63
74
|
}
|
|
64
75
|
route.continue();
|
|
65
|
-
};
|
|
66
|
-
await this
|
|
67
|
-
await this.#page.routeWebSocket(/.+/, async (ws) => {
|
|
76
|
+
});
|
|
77
|
+
await this.args.page.routeWebSocket(INTERNAL_MATCH_ALL_REG_EXP, async (ws) => {
|
|
68
78
|
const allWebSocketHandlers = this.handlersController.currentHandlers().filter((handler) => {
|
|
69
79
|
return handler instanceof WebSocketHandler;
|
|
70
80
|
});
|
|
@@ -83,7 +93,8 @@ var NetworkFixture = class extends SetupApi {
|
|
|
83
93
|
}
|
|
84
94
|
async stop() {
|
|
85
95
|
super.dispose();
|
|
86
|
-
await this.#page.unroute(
|
|
96
|
+
await this.#page.unroute(INTERNAL_MATCH_ALL_REG_EXP);
|
|
97
|
+
await unrouteWebSocket(this.#page, INTERNAL_MATCH_ALL_REG_EXP);
|
|
87
98
|
}
|
|
88
99
|
getPageUrl() {
|
|
89
100
|
const url = this.#page.url();
|
|
@@ -229,6 +240,17 @@ var PlaywrightWebSocketServerConnection = class {
|
|
|
229
240
|
console.warn("@msw/playwright: WebSocketRoute does not support removing event listeners");
|
|
230
241
|
}
|
|
231
242
|
};
|
|
243
|
+
/**
|
|
244
|
+
* Custom implementation of the missing `page.unrouteWebSocket()` to remove
|
|
245
|
+
* WebSocket route handlers from the page. Loosely inspired by `page.unroute()`.
|
|
246
|
+
*/
|
|
247
|
+
async function unrouteWebSocket(page, url, handler) {
|
|
248
|
+
if (!("_webSocketRoutes" in page && Array.isArray(page._webSocketRoutes))) return;
|
|
249
|
+
for (let i = page._webSocketRoutes.length - 1; i >= 0; i--) {
|
|
250
|
+
const route = page._webSocketRoutes[i];
|
|
251
|
+
if (route.url === url && (handler != null ? route.handler === handler : true)) page._webSocketRoutes.splice(i, 1);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
232
254
|
|
|
233
255
|
//#endregion
|
|
234
|
-
export {
|
|
256
|
+
export { createNetworkFixture };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@msw/playwright",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.4",
|
|
5
5
|
"description": "Mock Service Worker binding for Playwright",
|
|
6
6
|
"main": "./build/index.js",
|
|
7
7
|
"types": "./build/index.d.ts",
|
|
@@ -40,13 +40,15 @@
|
|
|
40
40
|
"@ossjs/release": "^0.10.1",
|
|
41
41
|
"@playwright/test": "^1.57.0",
|
|
42
42
|
"@types/node": "^22.15.29",
|
|
43
|
-
"
|
|
43
|
+
"@types/sinon": "^21.0.0",
|
|
44
|
+
"msw": "^2.12.7",
|
|
45
|
+
"sinon": "^21.0.1",
|
|
44
46
|
"tsdown": "^0.12.7",
|
|
45
|
-
"typescript": "^5.
|
|
47
|
+
"typescript": "^5.9.3",
|
|
46
48
|
"vite": "^7.3.1"
|
|
47
49
|
},
|
|
48
50
|
"dependencies": {
|
|
49
|
-
"@mswjs/interceptors": "^0.
|
|
51
|
+
"@mswjs/interceptors": "^0.40.0",
|
|
50
52
|
"outvariant": "^1.4.3"
|
|
51
53
|
},
|
|
52
54
|
"scripts": {
|
package/src/fixture.ts
CHANGED
|
@@ -10,10 +10,11 @@ import type {
|
|
|
10
10
|
} from '@playwright/test'
|
|
11
11
|
import {
|
|
12
12
|
type LifeCycleEventsMap,
|
|
13
|
+
type UnhandledRequestStrategy,
|
|
13
14
|
SetupApi,
|
|
14
15
|
RequestHandler,
|
|
15
16
|
WebSocketHandler,
|
|
16
|
-
|
|
17
|
+
handleRequest,
|
|
17
18
|
} from 'msw'
|
|
18
19
|
import {
|
|
19
20
|
type WebSocketClientEventMap,
|
|
@@ -26,7 +27,8 @@ import {
|
|
|
26
27
|
} from '@mswjs/interceptors/WebSocket'
|
|
27
28
|
|
|
28
29
|
export interface CreateNetworkFixtureArgs {
|
|
29
|
-
initialHandlers
|
|
30
|
+
initialHandlers?: Array<RequestHandler | WebSocketHandler>
|
|
31
|
+
onUnhandledRequest?: UnhandledRequestStrategy
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
/**
|
|
@@ -50,7 +52,6 @@ export interface CreateNetworkFixtureArgs {
|
|
|
50
52
|
*/
|
|
51
53
|
export function createNetworkFixture(
|
|
52
54
|
args?: CreateNetworkFixtureArgs,
|
|
53
|
-
/** @todo `onUnhandledRequest`? */
|
|
54
55
|
): [
|
|
55
56
|
TestFixture<NetworkFixture, PlaywrightTestArgs & PlaywrightWorkerArgs>,
|
|
56
57
|
{ auto: boolean },
|
|
@@ -60,6 +61,7 @@ export function createNetworkFixture(
|
|
|
60
61
|
const worker = new NetworkFixture({
|
|
61
62
|
page,
|
|
62
63
|
initialHandlers: args?.initialHandlers || [],
|
|
64
|
+
onUnhandledRequest: args?.onUnhandledRequest,
|
|
63
65
|
})
|
|
64
66
|
|
|
65
67
|
await worker.start()
|
|
@@ -79,19 +81,19 @@ export function createNetworkFixture(
|
|
|
79
81
|
export const INTERNAL_MATCH_ALL_REG_EXP = /.+(__MSW_PLAYWRIGHT_PREDICATE__)?/
|
|
80
82
|
|
|
81
83
|
export class NetworkFixture extends SetupApi<LifeCycleEventsMap> {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
constructor(
|
|
85
|
+
protected args: {
|
|
86
|
+
page: Page
|
|
87
|
+
initialHandlers: Array<RequestHandler | WebSocketHandler>
|
|
88
|
+
onUnhandledRequest?: UnhandledRequestStrategy
|
|
89
|
+
},
|
|
90
|
+
) {
|
|
88
91
|
super(...args.initialHandlers)
|
|
89
|
-
this.#page = args.page
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
public async start(): Promise<void> {
|
|
93
95
|
// Handle HTTP requests.
|
|
94
|
-
await this
|
|
96
|
+
await this.args.page.route(
|
|
95
97
|
INTERNAL_MATCH_ALL_REG_EXP,
|
|
96
98
|
async (route: Route, request: Request) => {
|
|
97
99
|
const fetchRequest = new Request(request.url(), {
|
|
@@ -100,13 +102,29 @@ export class NetworkFixture extends SetupApi<LifeCycleEventsMap> {
|
|
|
100
102
|
body: request.postDataBuffer(),
|
|
101
103
|
})
|
|
102
104
|
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
+
const handlers = this.handlersController
|
|
106
|
+
.currentHandlers()
|
|
107
|
+
.filter((handler) => {
|
|
105
108
|
return handler instanceof RequestHandler
|
|
106
|
-
})
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @note Use `handleRequest` instead of `getResponse` so we can pass
|
|
113
|
+
* the `onUnhandledRequest` option as-is and benefit from MSW's default behaviors.
|
|
114
|
+
*/
|
|
115
|
+
const response = await handleRequest(
|
|
107
116
|
fetchRequest,
|
|
117
|
+
crypto.randomUUID(),
|
|
118
|
+
handlers,
|
|
108
119
|
{
|
|
109
|
-
|
|
120
|
+
onUnhandledRequest: this.args.onUnhandledRequest || 'bypass',
|
|
121
|
+
},
|
|
122
|
+
this.emitter,
|
|
123
|
+
{
|
|
124
|
+
resolutionContext: {
|
|
125
|
+
quiet: true,
|
|
126
|
+
baseUrl: this.getPageUrl(),
|
|
127
|
+
},
|
|
110
128
|
},
|
|
111
129
|
)
|
|
112
130
|
|
|
@@ -131,44 +149,47 @@ export class NetworkFixture extends SetupApi<LifeCycleEventsMap> {
|
|
|
131
149
|
)
|
|
132
150
|
|
|
133
151
|
// Handle WebSocket connections.
|
|
134
|
-
await this
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
ws.connectToServer()
|
|
143
|
-
return
|
|
144
|
-
}
|
|
152
|
+
await this.args.page.routeWebSocket(
|
|
153
|
+
INTERNAL_MATCH_ALL_REG_EXP,
|
|
154
|
+
async (ws) => {
|
|
155
|
+
const allWebSocketHandlers = this.handlersController
|
|
156
|
+
.currentHandlers()
|
|
157
|
+
.filter((handler) => {
|
|
158
|
+
return handler instanceof WebSocketHandler
|
|
159
|
+
})
|
|
145
160
|
|
|
146
|
-
|
|
147
|
-
|
|
161
|
+
if (allWebSocketHandlers.length === 0) {
|
|
162
|
+
ws.connectToServer()
|
|
163
|
+
return
|
|
164
|
+
}
|
|
148
165
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
166
|
+
const client = new PlaywrightWebSocketClientConnection(ws)
|
|
167
|
+
const server = new PlaywrightWebSocketServerConnection(ws)
|
|
168
|
+
|
|
169
|
+
for (const handler of allWebSocketHandlers) {
|
|
170
|
+
await handler.run(
|
|
171
|
+
{
|
|
172
|
+
client,
|
|
173
|
+
server,
|
|
174
|
+
info: { protocols: [] },
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
baseUrl: this.getPageUrl(),
|
|
178
|
+
},
|
|
179
|
+
)
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
)
|
|
162
183
|
}
|
|
163
184
|
|
|
164
185
|
public async stop(): Promise<void> {
|
|
165
186
|
super.dispose()
|
|
166
|
-
await this
|
|
167
|
-
await unrouteWebSocket(this
|
|
187
|
+
await this.args.page.unroute(INTERNAL_MATCH_ALL_REG_EXP)
|
|
188
|
+
await unrouteWebSocket(this.args.page, INTERNAL_MATCH_ALL_REG_EXP)
|
|
168
189
|
}
|
|
169
190
|
|
|
170
191
|
private getPageUrl(): string | undefined {
|
|
171
|
-
const url = this
|
|
192
|
+
const url = this.args.page.url()
|
|
172
193
|
return url !== 'about:blank' ? url : undefined
|
|
173
194
|
}
|
|
174
195
|
}
|