Skip to content

Commit

Permalink
docs: improve docs for WebSocketRoute (#33097)
Browse files Browse the repository at this point in the history
  • Loading branch information
dgozman authored Oct 15, 2024
1 parent 2c05d29 commit 8a275e5
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 8 deletions.
73 changes: 68 additions & 5 deletions docs/src/api/class-websocketroute.md
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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");
Expand All @@ -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)
))
```
Expand All @@ -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");
Expand All @@ -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<String, String> 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<string, string> { ["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.
Expand Down
119 changes: 119 additions & 0 deletions docs/src/mock.md
Original file line number Diff line number Diff line change
Expand Up @@ -435,3 +435,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].
6 changes: 4 additions & 2 deletions docs/src/network.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 => {
Expand Down
14 changes: 13 additions & 1 deletion packages/playwright-core/types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15356,7 +15356,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');
Expand All @@ -15369,6 +15369,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
Expand Down

0 comments on commit 8a275e5

Please sign in to comment.