diff --git a/docs/src/api/class-websocketroute.md b/docs/src/api/class-websocketroute.md index f977050481120..b827db25dd6e7 100644 --- a/docs/src/api/class-websocketroute.md +++ b/docs/src/api/class-websocketroute.md @@ -8,7 +8,7 @@ Whenever a [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSoc By default, the routed WebSocket will not connect to the server. This way, you can mock entire communcation over the WebSocket. Here is an example that responds to a `"request"` with a `"response"`. ```js -await page.routeWebSocket('/ws', ws => { +await page.routeWebSocket('wss://example.com/ws', ws => { ws.onMessage(message => { if (message === 'request') ws.send('response'); @@ -17,7 +17,7 @@ await page.routeWebSocket('/ws', ws => { ``` ```java -page.routeWebSocket("/ws", ws -> { +page.routeWebSocket("wss://example.com/ws", ws -> { ws.onMessage(message -> { if ("request".equals(message)) ws.send("response"); @@ -30,7 +30,7 @@ def message_handler(ws: WebSocketRoute, message: Union[str, bytes]): if message == "request": ws.send("response") -await page.route_web_socket("/ws", lambda ws: ws.on_message( +await page.route_web_socket("wss://example.com/ws", lambda ws: ws.on_message( lambda message: message_handler(ws, message) )) ``` @@ -40,13 +40,13 @@ def message_handler(ws: WebSocketRoute, message: Union[str, bytes]): if message == "request": ws.send("response") -page.route_web_socket("/ws", lambda ws: ws.on_message( +page.route_web_socket("wss://example.com/ws", lambda ws: ws.on_message( lambda message: message_handler(ws, message) )) ``` ```csharp -await page.RouteWebSocketAsync("/ws", ws => { +await page.RouteWebSocketAsync("wss://example.com/ws", ws => { ws.OnMessage(message => { if (message == "request") ws.Send("response"); @@ -56,6 +56,69 @@ await page.RouteWebSocketAsync("/ws", ws => { Since we do not call [`method: WebSocketRoute.connectToServer`] inside the WebSocket route handler, Playwright assumes that WebSocket will be mocked, and opens the WebSocket inside the page automatically. +Here is another example that handles JSON messages: + +```js +await page.routeWebSocket('wss://example.com/ws', ws => { + ws.onMessage(message => { + const json = JSON.parse(message); + if (json.request === 'question') + ws.send(JSON.stringify({ response: 'answer' })); + }); +}); +``` + +```java +page.routeWebSocket("wss://example.com/ws", ws -> { + ws.onMessage(message -> { + JsonObject json = new JsonParser().parse(message).getAsJsonObject(); + if ("question".equals(json.get("request").getAsString())) { + Map result = new HashMap(); + result.put("response", "answer"); + ws.send(gson.toJson(result)); + } + }); +}); +``` + +```python async +def message_handler(ws: WebSocketRoute, message: Union[str, bytes]): + json_message = json.loads(message) + if json_message["request"] == "question": + ws.send(json.dumps({ "response": "answer" })) + +await page.route_web_socket("wss://example.com/ws", lambda ws: ws.on_message( + lambda message: message_handler(ws, message) +)) +``` + +```python sync +def message_handler(ws: WebSocketRoute, message: Union[str, bytes]): + json_message = json.loads(message) + if json_message["request"] == "question": + ws.send(json.dumps({ "response": "answer" })) + +page.route_web_socket("wss://example.com/ws", lambda ws: ws.on_message( + lambda message: message_handler(ws, message) +)) +``` + +```csharp +await page.RouteWebSocketAsync("wss://example.com/ws", ws => { + ws.OnMessage(message => { + using var jsonDoc = JsonDocument.Parse(message); + JsonElement root = jsonDoc.RootElement; + if (root.TryGetProperty("request", out JsonElement requestElement) && requestElement.GetString() == "question") + { + var response = new Dictionary { ["response"] = "answer" }; + string jsonResponse = JsonSerializer.Serialize(response); + ws.Send(jsonResponse); + } + }); +}); +``` + + **Intercepting** Alternatively, you may want to connect to the actual server, but intercept messages in-between and modify or block them. Calling [`method: WebSocketRoute.connectToServer`] returns a server-side `WebSocketRoute` instance that you can send messages to, or handle incoming messages. diff --git a/docs/src/mock.md b/docs/src/mock.md index 87ddf2ec96c3a..a061efaff143e 100644 --- a/docs/src/mock.md +++ b/docs/src/mock.md @@ -434,3 +434,122 @@ pwsh bin/Debug/netX/playwright.ps1 open --save-har=example.har --save-har-glob=" ``` Read more about [advanced networking](./network.md). + +## Mock WebSockets + +The following code will intercept WebSocket connections and mock entire communcation over the WebSocket, instead of connecting to the server. This example responds to a `"request"` with a `"response"`. + +```js +await page.routeWebSocket('wss://example.com/ws', ws => { + ws.onMessage(message => { + if (message === 'request') + ws.send('response'); + }); +}); +``` + +```java +page.routeWebSocket("wss://example.com/ws", ws -> { + ws.onMessage(message -> { + if ("request".equals(message)) + ws.send("response"); + }); +}); +``` + +```python async +def message_handler(ws: WebSocketRoute, message: Union[str, bytes]): + if message == "request": + ws.send("response") + +await page.route_web_socket("wss://example.com/ws", lambda ws: ws.on_message( + lambda message: message_handler(ws, message) +)) +``` + +```python sync +def message_handler(ws: WebSocketRoute, message: Union[str, bytes]): + if message == "request": + ws.send("response") + +page.route_web_socket("wss://example.com/ws", lambda ws: ws.on_message( + lambda message: message_handler(ws, message) +)) +``` + +```csharp +await page.RouteWebSocketAsync("wss://example.com/ws", ws => { + ws.OnMessage(message => { + if (message == "request") + ws.Send("response"); + }); +}); +``` + +Alternatively, you may want to connect to the actual server, but intercept messages in-between and modify or block them. Here is an example that modifies some of the messages sent by the page to the server, and leaves the rest unmodified. + +```js +await page.routeWebSocket('wss://example.com/ws', ws => { + const server = ws.connectToServer(); + ws.onMessage(message => { + if (message === 'request') + server.send('request2'); + else + server.send(message); + }); +}); +``` + +```java +page.routeWebSocket("wss://example.com/ws", ws -> { + WebSocketRoute server = ws.connectToServer(); + ws.onMessage(message -> { + if ("request".equals(message)) + server.send("request2"); + else + server.send(message); + }); +}); +``` + +```python async +def message_handler(server: WebSocketRoute, message: Union[str, bytes]): + if message == "request": + server.send("request2") + else: + server.send(message) + +def handler(ws: WebSocketRoute): + server = ws.connect_to_server() + ws.on_message(lambda message: message_handler(server, message)) + +await page.route_web_socket("wss://example.com/ws", handler) +``` + +```python sync +def message_handler(server: WebSocketRoute, message: Union[str, bytes]): + if message == "request": + server.send("request2") + else: + server.send(message) + +def handler(ws: WebSocketRoute): + server = ws.connect_to_server() + ws.on_message(lambda message: message_handler(server, message)) + +page.route_web_socket("wss://example.com/ws", handler) +``` + +```csharp +await page.RouteWebSocketAsync("wss://example.com/ws", ws => { + var server = ws.ConnectToServer(); + ws.OnMessage(message => { + if (message == "request") + server.Send("request2"); + else + server.Send(message); + }); +}); +``` + +For more details, see [WebSocketRoute]. diff --git a/docs/src/network.md b/docs/src/network.md index 4d6f229678b84..9b9b341a8dba7 100644 --- a/docs/src/network.md +++ b/docs/src/network.md @@ -10,7 +10,7 @@ Playwright provides APIs to **monitor** and **modify** browser network traffic, ## Mock APIs -Check out our [API mocking guide](./mock.md) to learn more on how to +Check out our [API mocking guide](./mock.md) to learn more on how to - mock API requests and never hit the API - perform the API request and modify the response - use HAR files to mock network requests. @@ -723,7 +723,9 @@ Important notes: ## WebSockets -Playwright supports [WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) inspection out of the box. Every time a WebSocket is created, the [`event: Page.webSocket`] event is fired. This event contains the [WebSocket] instance for further web socket frames inspection: +Playwright supports [WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) inspection, mocking and modifying out of the box. See our [API mocking guide](./mock.md#mock-websockets) to learn how to mock WebSockets. + +Every time a WebSocket is created, the [`event: Page.webSocket`] event is fired. This event contains the [WebSocket] instance for further web socket frames inspection: ```js page.on('websocket', ws => { diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 1f165c3d93fdb..6bd8d2bc1cb7b 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -15355,7 +15355,7 @@ export interface CDPSession { * the WebSocket. Here is an example that responds to a `"request"` with a `"response"`. * * ```js - * await page.routeWebSocket('/ws', ws => { + * await page.routeWebSocket('wss://example.com/ws', ws => { * ws.onMessage(message => { * if (message === 'request') * ws.send('response'); @@ -15368,6 +15368,18 @@ export interface CDPSession { * inside the WebSocket route handler, Playwright assumes that WebSocket will be mocked, and opens the WebSocket * inside the page automatically. * + * Here is another example that handles JSON messages: + * + * ```js + * await page.routeWebSocket('wss://example.com/ws', ws => { + * ws.onMessage(message => { + * const json = JSON.parse(message); + * if (json.request === 'question') + * ws.send(JSON.stringify({ response: 'answer' })); + * }); + * }); + * ``` + * * **Intercepting** * * Alternatively, you may want to connect to the actual server, but intercept messages in-between and modify or block