Compare commits
21 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
ceb756dad3 | |
![]() |
c3740d37af | |
![]() |
2ec0c86b93 | |
![]() |
8ef381fc5f | |
![]() |
c72a2538bc | |
![]() |
3d7ef3c062 | |
![]() |
78c43bc5d3 | |
![]() |
6dc9ec7fe9 | |
![]() |
e5bbd5effe | |
![]() |
daff1a9025 | |
![]() |
8d524e24ce | |
![]() |
0cdbb11068 | |
![]() |
ca368d43fa | |
![]() |
c329c5c1ec | |
![]() |
97aaa12be4 | |
![]() |
0c17732e91 | |
![]() |
530f04304e | |
![]() |
1054930edd | |
![]() |
e732f68eee | |
![]() |
dfa0e8bf35 | |
![]() |
7155356e3c |
|
@ -1,6 +1,6 @@
|
||||||
# 🎭 Playwright
|
# 🎭 Playwright
|
||||||
|
|
||||||
[](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[](https://webkit.org/)<!-- GEN:stop --> [](https://aka.ms/playwright/discord)
|
[](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[](https://webkit.org/)<!-- GEN:stop --> [](https://aka.ms/playwright/discord)
|
||||||
|
|
||||||
## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright)
|
## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright)
|
||||||
|
|
||||||
|
@ -8,9 +8,9 @@ Playwright is a framework for Web Testing and Automation. It allows testing [Chr
|
||||||
|
|
||||||
| | Linux | macOS | Windows |
|
| | Linux | macOS | Windows |
|
||||||
| :--- | :---: | :---: | :---: |
|
| :--- | :---: | :---: | :---: |
|
||||||
| Chromium <!-- GEN:chromium-version -->130.0.6723.19<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
| Chromium <!-- GEN:chromium-version -->130.0.6723.31<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||||
| WebKit <!-- GEN:webkit-version -->18.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
| WebKit <!-- GEN:webkit-version -->18.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||||
| Firefox <!-- GEN:firefox-version -->130.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
| Firefox <!-- GEN:firefox-version -->131.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||||
|
|
||||||
Headless execution is supported for all browsers on all platforms. Check out [system requirements](https://playwright.dev/docs/intro#system-requirements) for details.
|
Headless execution is supported for all browsers on all platforms. Check out [system requirements](https://playwright.dev/docs/intro#system-requirements) for details.
|
||||||
|
|
||||||
|
|
|
@ -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"`.
|
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
|
```js
|
||||||
await page.routeWebSocket('/ws', ws => {
|
await page.routeWebSocket('wss://example.com/ws', ws => {
|
||||||
ws.onMessage(message => {
|
ws.onMessage(message => {
|
||||||
if (message === 'request')
|
if (message === 'request')
|
||||||
ws.send('response');
|
ws.send('response');
|
||||||
|
@ -17,7 +17,7 @@ await page.routeWebSocket('/ws', ws => {
|
||||||
```
|
```
|
||||||
|
|
||||||
```java
|
```java
|
||||||
page.routeWebSocket("/ws", ws -> {
|
page.routeWebSocket("wss://example.com/ws", ws -> {
|
||||||
ws.onMessage(message -> {
|
ws.onMessage(message -> {
|
||||||
if ("request".equals(message))
|
if ("request".equals(message))
|
||||||
ws.send("response");
|
ws.send("response");
|
||||||
|
@ -30,7 +30,7 @@ def message_handler(ws: WebSocketRoute, message: Union[str, bytes]):
|
||||||
if message == "request":
|
if message == "request":
|
||||||
ws.send("response")
|
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)
|
lambda message: message_handler(ws, message)
|
||||||
))
|
))
|
||||||
```
|
```
|
||||||
|
@ -40,13 +40,13 @@ def message_handler(ws: WebSocketRoute, message: Union[str, bytes]):
|
||||||
if message == "request":
|
if message == "request":
|
||||||
ws.send("response")
|
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)
|
lambda message: message_handler(ws, message)
|
||||||
))
|
))
|
||||||
```
|
```
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
await page.RouteWebSocketAsync("/ws", ws => {
|
await page.RouteWebSocketAsync("wss://example.com/ws", ws => {
|
||||||
ws.OnMessage(message => {
|
ws.OnMessage(message => {
|
||||||
if (message == "request")
|
if (message == "request")
|
||||||
ws.Send("response");
|
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.
|
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**
|
**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.
|
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.
|
||||||
|
@ -255,13 +318,26 @@ By default, closing one side of the connection, either in the page or on the ser
|
||||||
|
|
||||||
### param: WebSocketRoute.onClose.handler
|
### param: WebSocketRoute.onClose.handler
|
||||||
* since: v1.48
|
* since: v1.48
|
||||||
- `handler` <[function]\([number]|[undefined], [string]|[undefined]\): [Promise<any>|any]>
|
* langs: js, python
|
||||||
|
- `handler` <[function]\([int]|[undefined], [string]|[undefined]\): [Promise<any>|any]>
|
||||||
|
|
||||||
Function that will handle WebSocket closure. Received an optional [close code](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#code) and an optional [close reason](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#reason).
|
Function that will handle WebSocket closure. Received an optional [close code](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#code) and an optional [close reason](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#reason).
|
||||||
|
|
||||||
|
### param: WebSocketRoute.onClose.handler
|
||||||
|
* since: v1.48
|
||||||
|
* langs: java
|
||||||
|
- `handler` <[function]\([null]|[int], [null]|[string]\)>
|
||||||
|
|
||||||
|
Function that will handle WebSocket closure. Received an optional [close code](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#code) and an optional [close reason](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#reason).
|
||||||
|
|
||||||
## async method: WebSocketRoute.onMessage
|
### param: WebSocketRoute.onClose.handler
|
||||||
|
* since: v1.48
|
||||||
|
* langs: csharp
|
||||||
|
- `handler` <[function]\([int?], [string]\)>
|
||||||
|
|
||||||
|
Function that will handle WebSocket closure. Received an optional [close code](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#code) and an optional [close reason](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#reason).
|
||||||
|
|
||||||
|
## method: WebSocketRoute.onMessage
|
||||||
* since: v1.48
|
* since: v1.48
|
||||||
|
|
||||||
This method allows to handle messages that are sent by the WebSocket, either from the page or from the server.
|
This method allows to handle messages that are sent by the WebSocket, either from the page or from the server.
|
||||||
|
|
|
@ -363,7 +363,7 @@ Target URL.
|
||||||
|
|
||||||
## js-fetch-option-params
|
## js-fetch-option-params
|
||||||
* langs: js
|
* langs: js
|
||||||
- `params` <[Object]<[string], [string]|[number]|[boolean]>|[URLSearchParams]|[string]>
|
- `params` <[Object]<[string], [string]|[float]|[boolean]>|[URLSearchParams]|[string]>
|
||||||
|
|
||||||
Query parameters to be sent with the URL.
|
Query parameters to be sent with the URL.
|
||||||
|
|
||||||
|
|
119
docs/src/mock.md
119
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).
|
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].
|
||||||
|
|
|
@ -10,7 +10,7 @@ Playwright provides APIs to **monitor** and **modify** browser network traffic,
|
||||||
|
|
||||||
## Mock APIs
|
## 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
|
- mock API requests and never hit the API
|
||||||
- perform the API request and modify the response
|
- perform the API request and modify the response
|
||||||
- use HAR files to mock network requests.
|
- use HAR files to mock network requests.
|
||||||
|
@ -723,7 +723,9 @@ Important notes:
|
||||||
|
|
||||||
## WebSockets
|
## 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
|
```js
|
||||||
page.on('websocket', ws => {
|
page.on('websocket', ws => {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "playwright-internal",
|
"name": "playwright-internal",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "playwright-internal",
|
"name": "playwright-internal",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
@ -7925,10 +7925,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/playwright": {
|
"packages/playwright": {
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.48.0-next"
|
"playwright-core": "1.48.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
|
@ -7942,11 +7942,11 @@
|
||||||
},
|
},
|
||||||
"packages/playwright-browser-chromium": {
|
"packages/playwright-browser-chromium": {
|
||||||
"name": "@playwright/browser-chromium",
|
"name": "@playwright/browser-chromium",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.48.0-next"
|
"playwright-core": "1.48.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
|
@ -7954,11 +7954,11 @@
|
||||||
},
|
},
|
||||||
"packages/playwright-browser-firefox": {
|
"packages/playwright-browser-firefox": {
|
||||||
"name": "@playwright/browser-firefox",
|
"name": "@playwright/browser-firefox",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.48.0-next"
|
"playwright-core": "1.48.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
|
@ -7966,22 +7966,22 @@
|
||||||
},
|
},
|
||||||
"packages/playwright-browser-webkit": {
|
"packages/playwright-browser-webkit": {
|
||||||
"name": "@playwright/browser-webkit",
|
"name": "@playwright/browser-webkit",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.48.0-next"
|
"playwright-core": "1.48.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/playwright-chromium": {
|
"packages/playwright-chromium": {
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.48.0-next"
|
"playwright-core": "1.48.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
|
@ -7991,7 +7991,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/playwright-core": {
|
"packages/playwright-core": {
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright-core": "cli.js"
|
"playwright-core": "cli.js"
|
||||||
|
@ -8002,11 +8002,11 @@
|
||||||
},
|
},
|
||||||
"packages/playwright-ct-core": {
|
"packages/playwright-ct-core": {
|
||||||
"name": "@playwright/experimental-ct-core",
|
"name": "@playwright/experimental-ct-core",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright": "1.48.0-next",
|
"playwright": "1.48.1",
|
||||||
"playwright-core": "1.48.0-next",
|
"playwright-core": "1.48.1",
|
||||||
"vite": "^5.2.8"
|
"vite": "^5.2.8"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -8015,10 +8015,10 @@
|
||||||
},
|
},
|
||||||
"packages/playwright-ct-react": {
|
"packages/playwright-ct-react": {
|
||||||
"name": "@playwright/experimental-ct-react",
|
"name": "@playwright/experimental-ct-react",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@playwright/experimental-ct-core": "1.48.0-next",
|
"@playwright/experimental-ct-core": "1.48.1",
|
||||||
"@vitejs/plugin-react": "^4.2.1"
|
"@vitejs/plugin-react": "^4.2.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -8030,10 +8030,10 @@
|
||||||
},
|
},
|
||||||
"packages/playwright-ct-react17": {
|
"packages/playwright-ct-react17": {
|
||||||
"name": "@playwright/experimental-ct-react17",
|
"name": "@playwright/experimental-ct-react17",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@playwright/experimental-ct-core": "1.48.0-next",
|
"@playwright/experimental-ct-core": "1.48.1",
|
||||||
"@vitejs/plugin-react": "^4.2.1"
|
"@vitejs/plugin-react": "^4.2.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -8045,10 +8045,10 @@
|
||||||
},
|
},
|
||||||
"packages/playwright-ct-solid": {
|
"packages/playwright-ct-solid": {
|
||||||
"name": "@playwright/experimental-ct-solid",
|
"name": "@playwright/experimental-ct-solid",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@playwright/experimental-ct-core": "1.48.0-next",
|
"@playwright/experimental-ct-core": "1.48.1",
|
||||||
"vite-plugin-solid": "^2.7.0"
|
"vite-plugin-solid": "^2.7.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -8063,10 +8063,10 @@
|
||||||
},
|
},
|
||||||
"packages/playwright-ct-svelte": {
|
"packages/playwright-ct-svelte": {
|
||||||
"name": "@playwright/experimental-ct-svelte",
|
"name": "@playwright/experimental-ct-svelte",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@playwright/experimental-ct-core": "1.48.0-next",
|
"@playwright/experimental-ct-core": "1.48.1",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.0.1"
|
"@sveltejs/vite-plugin-svelte": "^3.0.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -8081,10 +8081,10 @@
|
||||||
},
|
},
|
||||||
"packages/playwright-ct-vue": {
|
"packages/playwright-ct-vue": {
|
||||||
"name": "@playwright/experimental-ct-vue",
|
"name": "@playwright/experimental-ct-vue",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@playwright/experimental-ct-core": "1.48.0-next",
|
"@playwright/experimental-ct-core": "1.48.1",
|
||||||
"@vitejs/plugin-vue": "^4.2.1"
|
"@vitejs/plugin-vue": "^4.2.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -8096,10 +8096,10 @@
|
||||||
},
|
},
|
||||||
"packages/playwright-ct-vue2": {
|
"packages/playwright-ct-vue2": {
|
||||||
"name": "@playwright/experimental-ct-vue2",
|
"name": "@playwright/experimental-ct-vue2",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@playwright/experimental-ct-core": "1.48.0-next",
|
"@playwright/experimental-ct-core": "1.48.1",
|
||||||
"@vitejs/plugin-vue2": "^2.2.0"
|
"@vitejs/plugin-vue2": "^2.2.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -8148,11 +8148,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/playwright-firefox": {
|
"packages/playwright-firefox": {
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.48.0-next"
|
"playwright-core": "1.48.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
|
@ -8163,10 +8163,10 @@
|
||||||
},
|
},
|
||||||
"packages/playwright-test": {
|
"packages/playwright-test": {
|
||||||
"name": "@playwright/test",
|
"name": "@playwright/test",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright": "1.48.0-next"
|
"playwright": "1.48.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
|
@ -8176,11 +8176,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/playwright-webkit": {
|
"packages/playwright-webkit": {
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.48.0-next"
|
"playwright-core": "1.48.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "playwright-internal",
|
"name": "playwright-internal",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"description": "A high-level API to automate web browsers",
|
"description": "A high-level API to automate web browsers",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@playwright/browser-chromium",
|
"name": "@playwright/browser-chromium",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"description": "Playwright package that automatically installs Chromium",
|
"description": "Playwright package that automatically installs Chromium",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -27,6 +27,6 @@
|
||||||
"install": "node install.js"
|
"install": "node install.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.48.0-next"
|
"playwright-core": "1.48.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@playwright/browser-firefox",
|
"name": "@playwright/browser-firefox",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"description": "Playwright package that automatically installs Firefox",
|
"description": "Playwright package that automatically installs Firefox",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -27,6 +27,6 @@
|
||||||
"install": "node install.js"
|
"install": "node install.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.48.0-next"
|
"playwright-core": "1.48.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@playwright/browser-webkit",
|
"name": "@playwright/browser-webkit",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"description": "Playwright package that automatically installs WebKit",
|
"description": "Playwright package that automatically installs WebKit",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -27,6 +27,6 @@
|
||||||
"install": "node install.js"
|
"install": "node install.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.48.0-next"
|
"playwright-core": "1.48.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "playwright-chromium",
|
"name": "playwright-chromium",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"description": "A high-level API to automate Chromium",
|
"description": "A high-level API to automate Chromium",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -30,6 +30,6 @@
|
||||||
"install": "node install.js"
|
"install": "node install.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.48.0-next"
|
"playwright-core": "1.48.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
"browsers": [
|
"browsers": [
|
||||||
{
|
{
|
||||||
"name": "chromium",
|
"name": "chromium",
|
||||||
"revision": "1137",
|
"revision": "1140",
|
||||||
"installByDefault": true,
|
"installByDefault": true,
|
||||||
"browserVersion": "130.0.6723.19"
|
"browserVersion": "130.0.6723.31"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "chromium-tip-of-tree",
|
"name": "chromium-tip-of-tree",
|
||||||
|
@ -15,9 +15,9 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "firefox",
|
"name": "firefox",
|
||||||
"revision": "1464",
|
"revision": "1465",
|
||||||
"installByDefault": true,
|
"installByDefault": true,
|
||||||
"browserVersion": "130.0"
|
"browserVersion": "131.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "firefox-beta",
|
"name": "firefox-beta",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "playwright-core",
|
"name": "playwright-core",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"description": "A high-level API to automate web browsers",
|
"description": "A high-level API to automate web browsers",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
@ -462,6 +462,7 @@ export class WebSocketRoute extends ChannelOwner<channels.WebSocketRouteChannel>
|
||||||
|
|
||||||
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.WebSocketRouteInitializer) {
|
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.WebSocketRouteInitializer) {
|
||||||
super(parent, type, guid, initializer);
|
super(parent, type, guid, initializer);
|
||||||
|
this.markAsInternalType();
|
||||||
|
|
||||||
this._server = {
|
this._server = {
|
||||||
onMessage: (handler: (message: string | Buffer) => any) => {
|
onMessage: (handler: (message: string | Buffer) => any) => {
|
||||||
|
|
|
@ -37,7 +37,8 @@ export const chromiumSwitches = [
|
||||||
// PaintHolding - https://github.com/microsoft/playwright/issues/28023
|
// PaintHolding - https://github.com/microsoft/playwright/issues/28023
|
||||||
// ThirdPartyStoragePartitioning - https://github.com/microsoft/playwright/issues/32230
|
// ThirdPartyStoragePartitioning - https://github.com/microsoft/playwright/issues/32230
|
||||||
// LensOverlay - Hides the Lens feature in the URL address bar. Its not working in unofficial builds.
|
// LensOverlay - Hides the Lens feature in the URL address bar. Its not working in unofficial builds.
|
||||||
'--disable-features=ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate,HttpsUpgrades,PaintHolding,ThirdPartyStoragePartitioning,LensOverlay',
|
// PlzDedicatedWorker - https://github.com/microsoft/playwright/issues/31747
|
||||||
|
'--disable-features=ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate,HttpsUpgrades,PaintHolding,ThirdPartyStoragePartitioning,LensOverlay,PlzDedicatedWorker',
|
||||||
'--allow-pre-commit-input',
|
'--allow-pre-commit-input',
|
||||||
'--disable-hang-monitor',
|
'--disable-hang-monitor',
|
||||||
'--disable-ipc-flooding-protection',
|
'--disable-ipc-flooding-protection',
|
||||||
|
|
|
@ -110,7 +110,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"Galaxy S5": {
|
"Galaxy S5": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 360,
|
"width": 360,
|
||||||
"height": 640
|
"height": 640
|
||||||
|
@ -121,7 +121,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Galaxy S5 landscape": {
|
"Galaxy S5 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 640,
|
"width": 640,
|
||||||
"height": 360
|
"height": 360
|
||||||
|
@ -132,7 +132,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Galaxy S8": {
|
"Galaxy S8": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 360,
|
"width": 360,
|
||||||
"height": 740
|
"height": 740
|
||||||
|
@ -143,7 +143,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Galaxy S8 landscape": {
|
"Galaxy S8 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 740,
|
"width": 740,
|
||||||
"height": 360
|
"height": 360
|
||||||
|
@ -154,7 +154,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Galaxy S9+": {
|
"Galaxy S9+": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 320,
|
"width": 320,
|
||||||
"height": 658
|
"height": 658
|
||||||
|
@ -165,7 +165,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Galaxy S9+ landscape": {
|
"Galaxy S9+ landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 658,
|
"width": 658,
|
||||||
"height": 320
|
"height": 320
|
||||||
|
@ -176,7 +176,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Galaxy Tab S4": {
|
"Galaxy Tab S4": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 712,
|
"width": 712,
|
||||||
"height": 1138
|
"height": 1138
|
||||||
|
@ -187,7 +187,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Galaxy Tab S4 landscape": {
|
"Galaxy Tab S4 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 1138,
|
"width": 1138,
|
||||||
"height": 712
|
"height": 712
|
||||||
|
@ -1098,7 +1098,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"LG Optimus L70": {
|
"LG Optimus L70": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 384,
|
"width": 384,
|
||||||
"height": 640
|
"height": 640
|
||||||
|
@ -1109,7 +1109,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"LG Optimus L70 landscape": {
|
"LG Optimus L70 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 640,
|
"width": 640,
|
||||||
"height": 384
|
"height": 384
|
||||||
|
@ -1120,7 +1120,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Microsoft Lumia 550": {
|
"Microsoft Lumia 550": {
|
||||||
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36 Edge/14.14263",
|
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36 Edge/14.14263",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 640,
|
"width": 640,
|
||||||
"height": 360
|
"height": 360
|
||||||
|
@ -1131,7 +1131,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Microsoft Lumia 550 landscape": {
|
"Microsoft Lumia 550 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36 Edge/14.14263",
|
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36 Edge/14.14263",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 360,
|
"width": 360,
|
||||||
"height": 640
|
"height": 640
|
||||||
|
@ -1142,7 +1142,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Microsoft Lumia 950": {
|
"Microsoft Lumia 950": {
|
||||||
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36 Edge/14.14263",
|
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36 Edge/14.14263",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 360,
|
"width": 360,
|
||||||
"height": 640
|
"height": 640
|
||||||
|
@ -1153,7 +1153,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Microsoft Lumia 950 landscape": {
|
"Microsoft Lumia 950 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36 Edge/14.14263",
|
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36 Edge/14.14263",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 640,
|
"width": 640,
|
||||||
"height": 360
|
"height": 360
|
||||||
|
@ -1164,7 +1164,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 10": {
|
"Nexus 10": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 800,
|
"width": 800,
|
||||||
"height": 1280
|
"height": 1280
|
||||||
|
@ -1175,7 +1175,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 10 landscape": {
|
"Nexus 10 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 1280,
|
"width": 1280,
|
||||||
"height": 800
|
"height": 800
|
||||||
|
@ -1186,7 +1186,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 4": {
|
"Nexus 4": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 384,
|
"width": 384,
|
||||||
"height": 640
|
"height": 640
|
||||||
|
@ -1197,7 +1197,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 4 landscape": {
|
"Nexus 4 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 640,
|
"width": 640,
|
||||||
"height": 384
|
"height": 384
|
||||||
|
@ -1208,7 +1208,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 5": {
|
"Nexus 5": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 360,
|
"width": 360,
|
||||||
"height": 640
|
"height": 640
|
||||||
|
@ -1219,7 +1219,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 5 landscape": {
|
"Nexus 5 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 640,
|
"width": 640,
|
||||||
"height": 360
|
"height": 360
|
||||||
|
@ -1230,7 +1230,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 5X": {
|
"Nexus 5X": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 412,
|
"width": 412,
|
||||||
"height": 732
|
"height": 732
|
||||||
|
@ -1241,7 +1241,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 5X landscape": {
|
"Nexus 5X landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 732,
|
"width": 732,
|
||||||
"height": 412
|
"height": 412
|
||||||
|
@ -1252,7 +1252,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 6": {
|
"Nexus 6": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 412,
|
"width": 412,
|
||||||
"height": 732
|
"height": 732
|
||||||
|
@ -1263,7 +1263,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 6 landscape": {
|
"Nexus 6 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 732,
|
"width": 732,
|
||||||
"height": 412
|
"height": 412
|
||||||
|
@ -1274,7 +1274,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 6P": {
|
"Nexus 6P": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 412,
|
"width": 412,
|
||||||
"height": 732
|
"height": 732
|
||||||
|
@ -1285,7 +1285,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 6P landscape": {
|
"Nexus 6P landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 732,
|
"width": 732,
|
||||||
"height": 412
|
"height": 412
|
||||||
|
@ -1296,7 +1296,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 7": {
|
"Nexus 7": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 600,
|
"width": 600,
|
||||||
"height": 960
|
"height": 960
|
||||||
|
@ -1307,7 +1307,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Nexus 7 landscape": {
|
"Nexus 7 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 960,
|
"width": 960,
|
||||||
"height": 600
|
"height": 600
|
||||||
|
@ -1362,7 +1362,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"Pixel 2": {
|
"Pixel 2": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 411,
|
"width": 411,
|
||||||
"height": 731
|
"height": 731
|
||||||
|
@ -1373,7 +1373,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 2 landscape": {
|
"Pixel 2 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 731,
|
"width": 731,
|
||||||
"height": 411
|
"height": 411
|
||||||
|
@ -1384,7 +1384,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 2 XL": {
|
"Pixel 2 XL": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 411,
|
"width": 411,
|
||||||
"height": 823
|
"height": 823
|
||||||
|
@ -1395,7 +1395,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 2 XL landscape": {
|
"Pixel 2 XL landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 823,
|
"width": 823,
|
||||||
"height": 411
|
"height": 411
|
||||||
|
@ -1406,7 +1406,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 3": {
|
"Pixel 3": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 393,
|
"width": 393,
|
||||||
"height": 786
|
"height": 786
|
||||||
|
@ -1417,7 +1417,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 3 landscape": {
|
"Pixel 3 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 786,
|
"width": 786,
|
||||||
"height": 393
|
"height": 393
|
||||||
|
@ -1428,7 +1428,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 4": {
|
"Pixel 4": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 353,
|
"width": 353,
|
||||||
"height": 745
|
"height": 745
|
||||||
|
@ -1439,7 +1439,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 4 landscape": {
|
"Pixel 4 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 745,
|
"width": 745,
|
||||||
"height": 353
|
"height": 353
|
||||||
|
@ -1450,7 +1450,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 4a (5G)": {
|
"Pixel 4a (5G)": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 412,
|
"width": 412,
|
||||||
"height": 892
|
"height": 892
|
||||||
|
@ -1465,7 +1465,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 4a (5G) landscape": {
|
"Pixel 4a (5G) landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"screen": {
|
"screen": {
|
||||||
"height": 892,
|
"height": 892,
|
||||||
"width": 412
|
"width": 412
|
||||||
|
@ -1480,7 +1480,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 5": {
|
"Pixel 5": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 393,
|
"width": 393,
|
||||||
"height": 851
|
"height": 851
|
||||||
|
@ -1495,7 +1495,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 5 landscape": {
|
"Pixel 5 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 851,
|
"width": 851,
|
||||||
"height": 393
|
"height": 393
|
||||||
|
@ -1510,7 +1510,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 7": {
|
"Pixel 7": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 412,
|
"width": 412,
|
||||||
"height": 915
|
"height": 915
|
||||||
|
@ -1525,7 +1525,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Pixel 7 landscape": {
|
"Pixel 7 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 915,
|
"width": 915,
|
||||||
"height": 412
|
"height": 412
|
||||||
|
@ -1540,7 +1540,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Moto G4": {
|
"Moto G4": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 360,
|
"width": 360,
|
||||||
"height": 640
|
"height": 640
|
||||||
|
@ -1551,7 +1551,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Moto G4 landscape": {
|
"Moto G4 landscape": {
|
||||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Mobile Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Mobile Safari/537.36",
|
||||||
"viewport": {
|
"viewport": {
|
||||||
"width": 640,
|
"width": 640,
|
||||||
"height": 360
|
"height": 360
|
||||||
|
@ -1562,7 +1562,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Desktop Chrome HiDPI": {
|
"Desktop Chrome HiDPI": {
|
||||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Safari/537.36",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 1792,
|
"width": 1792,
|
||||||
"height": 1120
|
"height": 1120
|
||||||
|
@ -1577,7 +1577,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Desktop Edge HiDPI": {
|
"Desktop Edge HiDPI": {
|
||||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Safari/537.36 Edg/130.0.6723.19",
|
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Safari/537.36 Edg/130.0.6723.31",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 1792,
|
"width": 1792,
|
||||||
"height": 1120
|
"height": 1120
|
||||||
|
@ -1592,7 +1592,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Desktop Firefox HiDPI": {
|
"Desktop Firefox HiDPI": {
|
||||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0",
|
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:131.0) Gecko/20100101 Firefox/131.0",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 1792,
|
"width": 1792,
|
||||||
"height": 1120
|
"height": 1120
|
||||||
|
@ -1622,7 +1622,7 @@
|
||||||
"defaultBrowserType": "webkit"
|
"defaultBrowserType": "webkit"
|
||||||
},
|
},
|
||||||
"Desktop Chrome": {
|
"Desktop Chrome": {
|
||||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Safari/537.36",
|
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Safari/537.36",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 1920,
|
"width": 1920,
|
||||||
"height": 1080
|
"height": 1080
|
||||||
|
@ -1637,7 +1637,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Desktop Edge": {
|
"Desktop Edge": {
|
||||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.19 Safari/537.36 Edg/130.0.6723.19",
|
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Safari/537.36 Edg/130.0.6723.31",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 1920,
|
"width": 1920,
|
||||||
"height": 1080
|
"height": 1080
|
||||||
|
@ -1652,7 +1652,7 @@
|
||||||
"defaultBrowserType": "chromium"
|
"defaultBrowserType": "chromium"
|
||||||
},
|
},
|
||||||
"Desktop Firefox": {
|
"Desktop Firefox": {
|
||||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0",
|
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:131.0) Gecko/20100101 Firefox/131.0",
|
||||||
"screen": {
|
"screen": {
|
||||||
"width": 1920,
|
"width": 1920,
|
||||||
"height": 1080
|
"height": 1080
|
||||||
|
|
|
@ -25,7 +25,7 @@ import zlib from 'zlib';
|
||||||
import type { HTTPCredentials } from '../../types/types';
|
import type { HTTPCredentials } from '../../types/types';
|
||||||
import { TimeoutSettings } from '../common/timeoutSettings';
|
import { TimeoutSettings } from '../common/timeoutSettings';
|
||||||
import { getUserAgent } from '../utils/userAgent';
|
import { getUserAgent } from '../utils/userAgent';
|
||||||
import { assert, createGuid, monotonicTime } from '../utils';
|
import { assert, createGuid, eventsHelper, monotonicTime, type RegisteredListener } from '../utils';
|
||||||
import { HttpsProxyAgent, SocksProxyAgent } from '../utilsBundle';
|
import { HttpsProxyAgent, SocksProxyAgent } from '../utilsBundle';
|
||||||
import { BrowserContext, verifyClientCertificates } from './browserContext';
|
import { BrowserContext, verifyClientCertificates } from './browserContext';
|
||||||
import { CookieStore, domainMatches, parseRawCookie } from './cookieStore';
|
import { CookieStore, domainMatches, parseRawCookie } from './cookieStore';
|
||||||
|
@ -312,8 +312,11 @@ export abstract class APIRequestContext extends SdkObject {
|
||||||
|
|
||||||
let securityDetails: har.SecurityDetails | undefined;
|
let securityDetails: har.SecurityDetails | undefined;
|
||||||
|
|
||||||
|
const listeners: RegisteredListener[] = [];
|
||||||
|
|
||||||
const request = requestConstructor(url, requestOptions as any, async response => {
|
const request = requestConstructor(url, requestOptions as any, async response => {
|
||||||
const responseAt = monotonicTime();
|
const responseAt = monotonicTime();
|
||||||
|
|
||||||
const notifyRequestFinished = (body?: Buffer) => {
|
const notifyRequestFinished = (body?: Buffer) => {
|
||||||
const endAt = monotonicTime();
|
const endAt = monotonicTime();
|
||||||
// spec: http://www.softwareishard.com/blog/har-12-spec/#timings
|
// spec: http://www.softwareishard.com/blog/har-12-spec/#timings
|
||||||
|
@ -478,12 +481,13 @@ export abstract class APIRequestContext extends SdkObject {
|
||||||
});
|
});
|
||||||
request.on('error', reject);
|
request.on('error', reject);
|
||||||
|
|
||||||
const disposeListener = () => {
|
listeners.push(
|
||||||
reject(new Error('Request context disposed.'));
|
eventsHelper.addEventListener(this, APIRequestContext.Events.Dispose, () => {
|
||||||
request.destroy();
|
reject(new Error('Request context disposed.'));
|
||||||
};
|
request.destroy();
|
||||||
this.on(APIRequestContext.Events.Dispose, disposeListener);
|
})
|
||||||
request.on('close', () => this.off(APIRequestContext.Events.Dispose, disposeListener));
|
);
|
||||||
|
request.on('close', () => eventsHelper.removeEventListeners(listeners));
|
||||||
|
|
||||||
request.on('socket', socket => {
|
request.on('socket', socket => {
|
||||||
// happy eyeballs don't emit lookup and connect events, so we use our custom ones
|
// happy eyeballs don't emit lookup and connect events, so we use our custom ones
|
||||||
|
@ -492,22 +496,24 @@ export abstract class APIRequestContext extends SdkObject {
|
||||||
tcpConnectionAt = happyEyeBallsTimings.tcpConnectionAt;
|
tcpConnectionAt = happyEyeBallsTimings.tcpConnectionAt;
|
||||||
|
|
||||||
// non-happy-eyeballs sockets
|
// non-happy-eyeballs sockets
|
||||||
socket.on('lookup', () => { dnsLookupAt = monotonicTime(); });
|
listeners.push(
|
||||||
socket.on('connect', () => { tcpConnectionAt = monotonicTime(); });
|
eventsHelper.addEventListener(socket, 'lookup', () => { dnsLookupAt = monotonicTime(); }),
|
||||||
socket.on('secureConnect', () => {
|
eventsHelper.addEventListener(socket, 'connect', () => { tcpConnectionAt = monotonicTime(); }),
|
||||||
tlsHandshakeAt = monotonicTime();
|
eventsHelper.addEventListener(socket, 'secureConnect', () => {
|
||||||
|
tlsHandshakeAt = monotonicTime();
|
||||||
|
|
||||||
if (socket instanceof TLSSocket) {
|
if (socket instanceof TLSSocket) {
|
||||||
const peerCertificate = socket.getPeerCertificate();
|
const peerCertificate = socket.getPeerCertificate();
|
||||||
securityDetails = {
|
securityDetails = {
|
||||||
protocol: socket.getProtocol() ?? undefined,
|
protocol: socket.getProtocol() ?? undefined,
|
||||||
subjectName: peerCertificate.subject.CN,
|
subjectName: peerCertificate.subject.CN,
|
||||||
validFrom: new Date(peerCertificate.valid_from).getTime() / 1000,
|
validFrom: new Date(peerCertificate.valid_from).getTime() / 1000,
|
||||||
validTo: new Date(peerCertificate.valid_to).getTime() / 1000,
|
validTo: new Date(peerCertificate.valid_to).getTime() / 1000,
|
||||||
issuer: peerCertificate.issuer.CN
|
issuer: peerCertificate.issuer.CN
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
serverIPAddress = socket.remoteAddress;
|
serverIPAddress = socket.remoteAddress;
|
||||||
serverPort = socket.remotePort;
|
serverPort = socket.remotePort;
|
||||||
|
|
|
@ -143,6 +143,7 @@ export function inject(globalThis: GlobalThis) {
|
||||||
|
|
||||||
this.url = typeof url === 'string' ? url : url.href;
|
this.url = typeof url === 'string' ? url : url.href;
|
||||||
try {
|
try {
|
||||||
|
this.url = new URL(url).href;
|
||||||
this._origin = new URL(url).origin;
|
this._origin = new URL(url).origin;
|
||||||
} catch {
|
} catch {
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,6 +140,7 @@ export class Recorder implements InstrumentationListener, IRecorder {
|
||||||
this._contextRecorder.on(ContextRecorder.Events.Change, (data: { sources: Source[], actions: actions.ActionInContext[] }) => {
|
this._contextRecorder.on(ContextRecorder.Events.Change, (data: { sources: Source[], actions: actions.ActionInContext[] }) => {
|
||||||
this._recorderSources = data.sources;
|
this._recorderSources = data.sources;
|
||||||
recorderApp.setActions(data.actions, data.sources);
|
recorderApp.setActions(data.actions, data.sources);
|
||||||
|
recorderApp.setRunningFile(undefined);
|
||||||
this._pushAllSources();
|
this._pushAllSources();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -299,7 +300,7 @@ export class Recorder implements InstrumentationListener, IRecorder {
|
||||||
}
|
}
|
||||||
this._pushAllSources();
|
this._pushAllSources();
|
||||||
if (fileToSelect)
|
if (fileToSelect)
|
||||||
this._recorderApp?.setFile(fileToSelect);
|
this._recorderApp?.setRunningFile(fileToSelect);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _pushAllSources() {
|
private _pushAllSources() {
|
||||||
|
|
|
@ -34,7 +34,7 @@ export class EmptyRecorderApp extends EventEmitter implements IRecorderApp {
|
||||||
async close(): Promise<void> {}
|
async close(): Promise<void> {}
|
||||||
async setPaused(paused: boolean): Promise<void> {}
|
async setPaused(paused: boolean): Promise<void> {}
|
||||||
async setMode(mode: Mode): Promise<void> {}
|
async setMode(mode: Mode): Promise<void> {}
|
||||||
async setFile(file: string): Promise<void> {}
|
async setRunningFile(file: string | undefined): Promise<void> {}
|
||||||
async setSelector(selector: string, userGesture?: boolean): Promise<void> {}
|
async setSelector(selector: string, userGesture?: boolean): Promise<void> {}
|
||||||
async updateCallLogs(callLogs: CallLog[]): Promise<void> {}
|
async updateCallLogs(callLogs: CallLog[]): Promise<void> {}
|
||||||
async setSources(sources: Source[]): Promise<void> {}
|
async setSources(sources: Source[]): Promise<void> {}
|
||||||
|
@ -131,9 +131,9 @@ export class RecorderApp extends EventEmitter implements IRecorderApp {
|
||||||
}).toString(), { isFunction: true }, mode).catch(() => {});
|
}).toString(), { isFunction: true }, mode).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
async setFile(file: string): Promise<void> {
|
async setRunningFile(file: string | undefined): Promise<void> {
|
||||||
await this._page.mainFrame().evaluateExpression(((file: string) => {
|
await this._page.mainFrame().evaluateExpression(((file: string) => {
|
||||||
window.playwrightSetFile(file);
|
window.playwrightSetRunningFile(file);
|
||||||
}).toString(), { isFunction: true }, file).catch(() => {});
|
}).toString(), { isFunction: true }, file).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,8 @@ export class RecorderCollection extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _fireChange() {
|
private _fireChange() {
|
||||||
|
if (!this._enabled)
|
||||||
|
return;
|
||||||
this.emit('change', collapseActions(this._actions));
|
this.emit('change', collapseActions(this._actions));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ export interface IRecorderApp extends EventEmitter {
|
||||||
close(): Promise<void>;
|
close(): Promise<void>;
|
||||||
setPaused(paused: boolean): Promise<void>;
|
setPaused(paused: boolean): Promise<void>;
|
||||||
setMode(mode: Mode): Promise<void>;
|
setMode(mode: Mode): Promise<void>;
|
||||||
setFile(file: string): Promise<void>;
|
setRunningFile(file: string | undefined): Promise<void>;
|
||||||
setSelector(selector: string, userGesture?: boolean): Promise<void>;
|
setSelector(selector: string, userGesture?: boolean): Promise<void>;
|
||||||
updateCallLogs(callLogs: CallLog[]): Promise<void>;
|
updateCallLogs(callLogs: CallLog[]): Promise<void>;
|
||||||
setSources(sources: Source[]): Promise<void>;
|
setSources(sources: Source[]): Promise<void>;
|
||||||
|
|
|
@ -66,8 +66,8 @@ export class RecorderInTraceViewer extends EventEmitter implements IRecorderApp
|
||||||
this._transport.deliverEvent('setMode', { mode });
|
this._transport.deliverEvent('setMode', { mode });
|
||||||
}
|
}
|
||||||
|
|
||||||
async setFile(file: string): Promise<void> {
|
async setRunningFile(file: string | undefined): Promise<void> {
|
||||||
this._transport.deliverEvent('setFileIfNeeded', { file });
|
this._transport.deliverEvent('setRunningFile', { file });
|
||||||
}
|
}
|
||||||
|
|
||||||
async setSelector(selector: string, userGesture?: boolean): Promise<void> {
|
async setSelector(selector: string, userGesture?: boolean): Promise<void> {
|
||||||
|
|
|
@ -15355,7 +15355,7 @@ export interface CDPSession {
|
||||||
* the WebSocket. Here is an example that responds to a `"request"` with a `"response"`.
|
* the WebSocket. Here is an example that responds to a `"request"` with a `"response"`.
|
||||||
*
|
*
|
||||||
* ```js
|
* ```js
|
||||||
* await page.routeWebSocket('/ws', ws => {
|
* await page.routeWebSocket('wss://example.com/ws', ws => {
|
||||||
* ws.onMessage(message => {
|
* ws.onMessage(message => {
|
||||||
* if (message === 'request')
|
* if (message === 'request')
|
||||||
* ws.send('response');
|
* 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 WebSocket route handler, Playwright assumes that WebSocket will be mocked, and opens the WebSocket
|
||||||
* inside the page automatically.
|
* 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**
|
* **Intercepting**
|
||||||
*
|
*
|
||||||
* Alternatively, you may want to connect to the actual server, but intercept messages in-between and modify or block
|
* Alternatively, you may want to connect to the actual server, but intercept messages in-between and modify or block
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@playwright/experimental-ct-core",
|
"name": "@playwright/experimental-ct-core",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"description": "Playwright Component Testing Helpers",
|
"description": "Playwright Component Testing Helpers",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -26,8 +26,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.48.0-next",
|
"playwright-core": "1.48.1",
|
||||||
"vite": "^5.2.8",
|
"vite": "^5.2.8",
|
||||||
"playwright": "1.48.0-next"
|
"playwright": "1.48.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@playwright/experimental-ct-react",
|
"name": "@playwright/experimental-ct-react",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"description": "Playwright Component Testing for React",
|
"description": "Playwright Component Testing for React",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
"./package.json": "./package.json"
|
"./package.json": "./package.json"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@playwright/experimental-ct-core": "1.48.0-next",
|
"@playwright/experimental-ct-core": "1.48.1",
|
||||||
"@vitejs/plugin-react": "^4.2.1"
|
"@vitejs/plugin-react": "^4.2.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@playwright/experimental-ct-react17",
|
"name": "@playwright/experimental-ct-react17",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"description": "Playwright Component Testing for React",
|
"description": "Playwright Component Testing for React",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
"./package.json": "./package.json"
|
"./package.json": "./package.json"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@playwright/experimental-ct-core": "1.48.0-next",
|
"@playwright/experimental-ct-core": "1.48.1",
|
||||||
"@vitejs/plugin-react": "^4.2.1"
|
"@vitejs/plugin-react": "^4.2.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@playwright/experimental-ct-solid",
|
"name": "@playwright/experimental-ct-solid",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"description": "Playwright Component Testing for Solid",
|
"description": "Playwright Component Testing for Solid",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
"./package.json": "./package.json"
|
"./package.json": "./package.json"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@playwright/experimental-ct-core": "1.48.0-next",
|
"@playwright/experimental-ct-core": "1.48.1",
|
||||||
"vite-plugin-solid": "^2.7.0"
|
"vite-plugin-solid": "^2.7.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@playwright/experimental-ct-svelte",
|
"name": "@playwright/experimental-ct-svelte",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"description": "Playwright Component Testing for Svelte",
|
"description": "Playwright Component Testing for Svelte",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
"./package.json": "./package.json"
|
"./package.json": "./package.json"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@playwright/experimental-ct-core": "1.48.0-next",
|
"@playwright/experimental-ct-core": "1.48.1",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.0.1"
|
"@sveltejs/vite-plugin-svelte": "^3.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@playwright/experimental-ct-vue",
|
"name": "@playwright/experimental-ct-vue",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"description": "Playwright Component Testing for Vue",
|
"description": "Playwright Component Testing for Vue",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
"./package.json": "./package.json"
|
"./package.json": "./package.json"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@playwright/experimental-ct-core": "1.48.0-next",
|
"@playwright/experimental-ct-core": "1.48.1",
|
||||||
"@vitejs/plugin-vue": "^4.2.1"
|
"@vitejs/plugin-vue": "^4.2.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@playwright/experimental-ct-vue2",
|
"name": "@playwright/experimental-ct-vue2",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"description": "Playwright Component Testing for Vue2",
|
"description": "Playwright Component Testing for Vue2",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
"./package.json": "./package.json"
|
"./package.json": "./package.json"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@playwright/experimental-ct-core": "1.48.0-next",
|
"@playwright/experimental-ct-core": "1.48.1",
|
||||||
"@vitejs/plugin-vue2": "^2.2.0"
|
"@vitejs/plugin-vue2": "^2.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "playwright-firefox",
|
"name": "playwright-firefox",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"description": "A high-level API to automate Firefox",
|
"description": "A high-level API to automate Firefox",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -30,6 +30,6 @@
|
||||||
"install": "node install.js"
|
"install": "node install.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.48.0-next"
|
"playwright-core": "1.48.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@playwright/test",
|
"name": "@playwright/test",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"description": "A high-level API to automate web browsers",
|
"description": "A high-level API to automate web browsers",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -30,6 +30,6 @@
|
||||||
},
|
},
|
||||||
"scripts": {},
|
"scripts": {},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright": "1.48.0-next"
|
"playwright": "1.48.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "playwright-webkit",
|
"name": "playwright-webkit",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"description": "A high-level API to automate WebKit",
|
"description": "A high-level API to automate WebKit",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -30,6 +30,6 @@
|
||||||
"install": "node install.js"
|
"install": "node install.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.48.0-next"
|
"playwright-core": "1.48.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "playwright",
|
"name": "playwright",
|
||||||
"version": "1.48.0-next",
|
"version": "1.48.1",
|
||||||
"description": "A high-level API to automate web browsers",
|
"description": "A high-level API to automate web browsers",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -56,7 +56,7 @@
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.48.0-next"
|
"playwright-core": "1.48.1"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"fsevents": "2.3.2"
|
"fsevents": "2.3.2"
|
||||||
|
|
|
@ -302,10 +302,11 @@ export class TestServerDispatcher implements TestServerInterface {
|
||||||
preserveOutputDir: true,
|
preserveOutputDir: true,
|
||||||
reporter: params.reporters ? params.reporters.map(r => [r]) : undefined,
|
reporter: params.reporters ? params.reporters.map(r => [r]) : undefined,
|
||||||
use: {
|
use: {
|
||||||
...(this._configCLIOverrides.use || {}),
|
...this._configCLIOverrides.use,
|
||||||
trace: params.trace === 'on' ? { mode: 'on', sources: false, _live: true } : (params.trace === 'off' ? 'off' : undefined),
|
...(params.trace === 'on' ? { trace: { mode: 'on', sources: false, _live: true } } : {}),
|
||||||
video: params.video === 'on' ? 'on' : (params.video === 'off' ? 'off' : undefined),
|
...(params.trace === 'off' ? { trace: 'off' } : {}),
|
||||||
headless: params.headed ? false : undefined,
|
...(params.video === 'on' || params.video === 'off' ? { video: params.video } : {}),
|
||||||
|
...(params.headed !== undefined ? { headless: !params.headed } : {}),
|
||||||
_optionContextReuseMode: params.reuseContext ? 'when-possible' : undefined,
|
_optionContextReuseMode: params.reuseContext ? 'when-possible' : undefined,
|
||||||
_optionConnectOptions: params.connectWsEndpoint ? { wsEndpoint: params.connectWsEndpoint } : undefined,
|
_optionConnectOptions: params.connectWsEndpoint ? { wsEndpoint: params.connectWsEndpoint } : undefined,
|
||||||
},
|
},
|
||||||
|
|
|
@ -41,13 +41,11 @@ export const Recorder: React.FC<RecorderProps> = ({
|
||||||
log,
|
log,
|
||||||
mode,
|
mode,
|
||||||
}) => {
|
}) => {
|
||||||
const [fileId, setFileId] = React.useState<string | undefined>();
|
const [selectedFileId, setSelectedFileId] = React.useState<string | undefined>();
|
||||||
|
const [runningFileId, setRunningFileId] = React.useState<string | undefined>();
|
||||||
const [selectedTab, setSelectedTab] = React.useState<string>('log');
|
const [selectedTab, setSelectedTab] = React.useState<string>('log');
|
||||||
|
|
||||||
React.useEffect(() => {
|
const fileId = selectedFileId || runningFileId || sources[0]?.id;
|
||||||
if (!fileId && sources.length > 0)
|
|
||||||
setFileId(sources[0].id);
|
|
||||||
}, [fileId, sources]);
|
|
||||||
|
|
||||||
const source = React.useMemo(() => {
|
const source = React.useMemo(() => {
|
||||||
if (fileId) {
|
if (fileId) {
|
||||||
|
@ -66,7 +64,7 @@ export const Recorder: React.FC<RecorderProps> = ({
|
||||||
setLocator(asLocator(language, selector));
|
setLocator(asLocator(language, selector));
|
||||||
};
|
};
|
||||||
|
|
||||||
window.playwrightSetFile = setFileId;
|
window.playwrightSetRunningFile = setRunningFileId;
|
||||||
|
|
||||||
const messagesEndRef = React.useRef<HTMLDivElement>(null);
|
const messagesEndRef = React.useRef<HTMLDivElement>(null);
|
||||||
React.useLayoutEffect(() => {
|
React.useLayoutEffect(() => {
|
||||||
|
@ -134,19 +132,19 @@ export const Recorder: React.FC<RecorderProps> = ({
|
||||||
<ToolbarButton icon='files' title='Copy' disabled={!source || !source.text} onClick={() => {
|
<ToolbarButton icon='files' title='Copy' disabled={!source || !source.text} onClick={() => {
|
||||||
copy(source.text);
|
copy(source.text);
|
||||||
}}></ToolbarButton>
|
}}></ToolbarButton>
|
||||||
<ToolbarButton icon='debug-continue' title='Resume (F8)' disabled={!paused} onClick={() => {
|
<ToolbarButton icon='debug-continue' title='Resume (F8)' ariaLabel='Resume' disabled={!paused} onClick={() => {
|
||||||
window.dispatch({ event: 'resume' });
|
window.dispatch({ event: 'resume' });
|
||||||
}}></ToolbarButton>
|
}}></ToolbarButton>
|
||||||
<ToolbarButton icon='debug-pause' title='Pause (F8)' disabled={paused} onClick={() => {
|
<ToolbarButton icon='debug-pause' title='Pause (F8)' ariaLabel='Pause' disabled={paused} onClick={() => {
|
||||||
window.dispatch({ event: 'pause' });
|
window.dispatch({ event: 'pause' });
|
||||||
}}></ToolbarButton>
|
}}></ToolbarButton>
|
||||||
<ToolbarButton icon='debug-step-over' title='Step over (F10)' disabled={!paused} onClick={() => {
|
<ToolbarButton icon='debug-step-over' title='Step over (F10)' ariaLabel='Step over' disabled={!paused} onClick={() => {
|
||||||
window.dispatch({ event: 'step' });
|
window.dispatch({ event: 'step' });
|
||||||
}}></ToolbarButton>
|
}}></ToolbarButton>
|
||||||
<div style={{ flex: 'auto' }}></div>
|
<div style={{ flex: 'auto' }}></div>
|
||||||
<div>Target:</div>
|
<div>Target:</div>
|
||||||
<SourceChooser fileId={fileId} sources={sources} setFileId={fileId => {
|
<SourceChooser fileId={fileId} sources={sources} setFileId={fileId => {
|
||||||
setFileId(fileId);
|
setSelectedFileId(fileId);
|
||||||
window.dispatch({ event: 'fileChanged', params: { file: fileId } });
|
window.dispatch({ event: 'fileChanged', params: { file: fileId } });
|
||||||
}} />
|
}} />
|
||||||
<ToolbarButton icon='clear-all' title='Clear' disabled={!source || !source.text} onClick={() => {
|
<ToolbarButton icon='clear-all' title='Clear' disabled={!source || !source.text} onClick={() => {
|
||||||
|
|
|
@ -96,7 +96,7 @@ declare global {
|
||||||
playwrightSetSources: (sources: Source[]) => void;
|
playwrightSetSources: (sources: Source[]) => void;
|
||||||
playwrightSetOverlayVisible: (visible: boolean) => void;
|
playwrightSetOverlayVisible: (visible: boolean) => void;
|
||||||
playwrightUpdateLogs: (callLogs: CallLog[]) => void;
|
playwrightUpdateLogs: (callLogs: CallLog[]) => void;
|
||||||
playwrightSetFile: (file: string) => void;
|
playwrightSetRunningFile: (file: string | undefined) => void;
|
||||||
playwrightSetSelector: (selector: string, focus?: boolean) => void;
|
playwrightSetSelector: (selector: string, focus?: boolean) => void;
|
||||||
playwrightSourcesEchoForTest: Source[];
|
playwrightSourcesEchoForTest: Source[];
|
||||||
dispatch(data: any): Promise<void>;
|
dispatch(data: any): Promise<void>;
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { ImageDiffView } from '@web/shared/imageDiffView';
|
||||||
import type { MultiTraceModel } from './modelUtil';
|
import type { MultiTraceModel } from './modelUtil';
|
||||||
import { PlaceholderPanel } from './placeholderPanel';
|
import { PlaceholderPanel } from './placeholderPanel';
|
||||||
import type { AfterActionTraceEventAttachment } from '@trace/trace';
|
import type { AfterActionTraceEventAttachment } from '@trace/trace';
|
||||||
import { CodeMirrorWrapper } from '@web/components/codeMirrorWrapper';
|
import { CodeMirrorWrapper, lineHeight } from '@web/components/codeMirrorWrapper';
|
||||||
import { isTextualMimeType } from '@isomorphic/mimeType';
|
import { isTextualMimeType } from '@isomorphic/mimeType';
|
||||||
import { Expandable } from '@web/components/expandable';
|
import { Expandable } from '@web/components/expandable';
|
||||||
import { linkifyText } from '@web/renderUtils';
|
import { linkifyText } from '@web/renderUtils';
|
||||||
|
@ -51,6 +51,11 @@ const ExpandableAttachment: React.FunctionComponent<ExpandableAttachmentProps> =
|
||||||
}
|
}
|
||||||
}, [expanded, attachmentText, placeholder, attachment]);
|
}, [expanded, attachmentText, placeholder, attachment]);
|
||||||
|
|
||||||
|
const snippetHeight = React.useMemo(() => {
|
||||||
|
const lineCount = attachmentText ? attachmentText.split('\n').length : 0;
|
||||||
|
return Math.min(Math.max(5, lineCount), 20) * lineHeight;
|
||||||
|
}, [attachmentText]);
|
||||||
|
|
||||||
const title = <span style={{ marginLeft: 5 }}>
|
const title = <span style={{ marginLeft: 5 }}>
|
||||||
{linkifyText(attachment.name)} {hasContent && <a style={{ marginLeft: 5 }} href={downloadURL(attachment)}>download</a>}
|
{linkifyText(attachment.name)} {hasContent && <a style={{ marginLeft: 5 }} href={downloadURL(attachment)}>download</a>}
|
||||||
</span>;
|
</span>;
|
||||||
|
@ -62,14 +67,16 @@ const ExpandableAttachment: React.FunctionComponent<ExpandableAttachmentProps> =
|
||||||
<Expandable title={title} expanded={expanded} setExpanded={setExpanded} expandOnTitleClick={true}>
|
<Expandable title={title} expanded={expanded} setExpanded={setExpanded} expandOnTitleClick={true}>
|
||||||
{placeholder && <i>{placeholder}</i>}
|
{placeholder && <i>{placeholder}</i>}
|
||||||
</Expandable>
|
</Expandable>
|
||||||
{expanded && attachmentText !== null && <CodeMirrorWrapper
|
{expanded && attachmentText !== null && <div className='vbox' style={{ height: snippetHeight }}>
|
||||||
text={attachmentText}
|
<CodeMirrorWrapper
|
||||||
readOnly
|
text={attachmentText}
|
||||||
mimeType={attachment.contentType}
|
readOnly
|
||||||
linkify={true}
|
mimeType={attachment.contentType}
|
||||||
lineNumbers={true}
|
linkify={true}
|
||||||
wrapLines={false}>
|
lineNumbers={true}
|
||||||
</CodeMirrorWrapper>}
|
wrapLines={false}>
|
||||||
|
</CodeMirrorWrapper>
|
||||||
|
</div>}
|
||||||
</>;
|
</>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ import type { ActionTraceEvent } from '@trace/trace';
|
||||||
import { context, type MultiTraceModel, pageForAction, prevInList } from './modelUtil';
|
import { context, type MultiTraceModel, pageForAction, prevInList } from './modelUtil';
|
||||||
import { Toolbar } from '@web/components/toolbar';
|
import { Toolbar } from '@web/components/toolbar';
|
||||||
import { ToolbarButton } from '@web/components/toolbarButton';
|
import { ToolbarButton } from '@web/components/toolbarButton';
|
||||||
import { clsx, useMeasure, useSetting } from '@web/uiUtils';
|
import { clsx, useMeasure } from '@web/uiUtils';
|
||||||
import { InjectedScript } from '@injected/injectedScript';
|
import { InjectedScript } from '@injected/injectedScript';
|
||||||
import { Recorder } from '@injected/recorder/recorder';
|
import { Recorder } from '@injected/recorder/recorder';
|
||||||
import ConsoleAPI from '@injected/consoleApi';
|
import ConsoleAPI from '@injected/consoleApi';
|
||||||
|
@ -52,7 +52,7 @@ export const SnapshotTabsView: React.FunctionComponent<{
|
||||||
openPage?: (url: string, target?: string) => Window | any,
|
openPage?: (url: string, target?: string) => Window | any,
|
||||||
}> = ({ action, sdkLanguage, testIdAttributeName, isInspecting, setIsInspecting, highlightedLocator, setHighlightedLocator, openPage }) => {
|
}> = ({ action, sdkLanguage, testIdAttributeName, isInspecting, setIsInspecting, highlightedLocator, setHighlightedLocator, openPage }) => {
|
||||||
const [snapshotTab, setSnapshotTab] = React.useState<'action'|'before'|'after'>('action');
|
const [snapshotTab, setSnapshotTab] = React.useState<'action'|'before'|'after'>('action');
|
||||||
const [showScreenshotInsteadOfSnapshot] = useSetting('screenshot-instead-of-snapshot', false);
|
const showScreenshotInsteadOfSnapshot = false;
|
||||||
|
|
||||||
const snapshots = React.useMemo(() => {
|
const snapshots = React.useMemo(() => {
|
||||||
return collectSnapshots(action);
|
return collectSnapshots(action);
|
||||||
|
|
|
@ -41,7 +41,6 @@ import type { Entry } from '@trace/har';
|
||||||
import './workbench.css';
|
import './workbench.css';
|
||||||
import { testStatusIcon, testStatusText } from './testUtils';
|
import { testStatusIcon, testStatusText } from './testUtils';
|
||||||
import type { UITestStatus } from './testUtils';
|
import type { UITestStatus } from './testUtils';
|
||||||
import { SettingsView } from './settingsView';
|
|
||||||
|
|
||||||
export const Workbench: React.FunctionComponent<{
|
export const Workbench: React.FunctionComponent<{
|
||||||
model?: modelUtil.MultiTraceModel,
|
model?: modelUtil.MultiTraceModel,
|
||||||
|
@ -69,7 +68,7 @@ export const Workbench: React.FunctionComponent<{
|
||||||
const [highlightedLocator, setHighlightedLocator] = React.useState<string>('');
|
const [highlightedLocator, setHighlightedLocator] = React.useState<string>('');
|
||||||
const [selectedTime, setSelectedTime] = React.useState<Boundaries | undefined>();
|
const [selectedTime, setSelectedTime] = React.useState<Boundaries | undefined>();
|
||||||
const [sidebarLocation, setSidebarLocation] = useSetting<'bottom' | 'right'>('propertiesSidebarLocation', 'bottom');
|
const [sidebarLocation, setSidebarLocation] = useSetting<'bottom' | 'right'>('propertiesSidebarLocation', 'bottom');
|
||||||
const [showScreenshot, setShowScreenshot] = useSetting('screenshot-instead-of-snapshot', false);
|
const showScreenshot = false;
|
||||||
|
|
||||||
const setSelectedAction = React.useCallback((action: modelUtil.ActionTraceEventInContext | undefined) => {
|
const setSelectedAction = React.useCallback((action: modelUtil.ActionTraceEventInContext | undefined) => {
|
||||||
setSelectedCallId(action?.callId);
|
setSelectedCallId(action?.callId);
|
||||||
|
@ -310,13 +309,6 @@ export const Workbench: React.FunctionComponent<{
|
||||||
title: 'Metadata',
|
title: 'Metadata',
|
||||||
component: <MetadataView model={model}/>
|
component: <MetadataView model={model}/>
|
||||||
};
|
};
|
||||||
const settingsTab: TabbedPaneTabModel = {
|
|
||||||
id: 'settings',
|
|
||||||
title: 'Settings',
|
|
||||||
component: <SettingsView settings={[
|
|
||||||
{ value: showScreenshot, set: setShowScreenshot, title: 'Show screenshot instead of snapshot' }
|
|
||||||
]}/>,
|
|
||||||
};
|
|
||||||
|
|
||||||
return <div className='vbox workbench' {...(inert ? { inert: 'true' } : {})}>
|
return <div className='vbox workbench' {...(inert ? { inert: 'true' } : {})}>
|
||||||
{!hideTimeline && <Timeline
|
{!hideTimeline && <Timeline
|
||||||
|
@ -351,8 +343,7 @@ export const Workbench: React.FunctionComponent<{
|
||||||
openPage={openPage} />}
|
openPage={openPage} />}
|
||||||
sidebar={
|
sidebar={
|
||||||
<TabbedPane
|
<TabbedPane
|
||||||
// Hide settings tab for now, it only includes screenshots as snapshots option which is not ready yet.
|
tabs={[actionsTab, metadataTab]}
|
||||||
tabs={(showSettings && false) ? [actionsTab, metadataTab, settingsTab] : [actionsTab, metadataTab]}
|
|
||||||
selectedTab={selectedNavigatorTab}
|
selectedTab={selectedNavigatorTab}
|
||||||
setSelectedTab={setSelectedNavigatorTab}
|
setSelectedTab={setSelectedNavigatorTab}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -28,6 +28,8 @@ export type SourceHighlight = {
|
||||||
|
|
||||||
export type Language = 'javascript' | 'python' | 'java' | 'csharp' | 'jsonl' | 'html' | 'css' | 'markdown';
|
export type Language = 'javascript' | 'python' | 'java' | 'csharp' | 'jsonl' | 'html' | 'css' | 'markdown';
|
||||||
|
|
||||||
|
export const lineHeight = 20;
|
||||||
|
|
||||||
export interface SourceProps {
|
export interface SourceProps {
|
||||||
text: string;
|
text: string;
|
||||||
language?: Language;
|
language?: Language;
|
||||||
|
|
|
@ -22,7 +22,7 @@ export const SourceChooser: React.FC<{
|
||||||
fileId: string | undefined,
|
fileId: string | undefined,
|
||||||
setFileId: (fileId: string) => void,
|
setFileId: (fileId: string) => void,
|
||||||
}> = ({ sources, fileId, setFileId }) => {
|
}> = ({ sources, fileId, setFileId }) => {
|
||||||
return <select className='source-chooser' hidden={!sources.length} value={fileId} onChange={event => {
|
return <select className='source-chooser' hidden={!sources.length} title='Source chooser' value={fileId} onChange={event => {
|
||||||
setFileId(event.target.selectedOptions[0].value);
|
setFileId(event.target.selectedOptions[0].value);
|
||||||
}}>{renderSourceOptions(sources)}</select>;
|
}}>{renderSourceOptions(sources)}</select>;
|
||||||
};
|
};
|
||||||
|
@ -33,17 +33,21 @@ function renderSourceOptions(sources: Source[]): React.ReactNode {
|
||||||
<option key={source.id} value={source.id}>{transformTitle(source.label)}</option>
|
<option key={source.id} value={source.id}>{transformTitle(source.label)}</option>
|
||||||
);
|
);
|
||||||
|
|
||||||
const hasGroup = sources.some(s => s.group);
|
const sourcesByGroups = new Map<string, Source[]>();
|
||||||
if (hasGroup) {
|
for (const source of sources) {
|
||||||
const groups = new Set(sources.map(s => s.group));
|
let list = sourcesByGroups.get(source.group || 'Debugger');
|
||||||
return [...groups].filter(Boolean).map(group => (
|
if (!list) {
|
||||||
<optgroup label={group} key={group}>
|
list = [];
|
||||||
{sources.filter(s => s.group === group).map(source => renderOption(source))}
|
sourcesByGroups.set(source.group || 'Debugger', list);
|
||||||
</optgroup>
|
}
|
||||||
));
|
list.push(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
return sources.map(source => renderOption(source));
|
return [...sourcesByGroups.entries()].map(([group, sources]) => (
|
||||||
|
<optgroup label={group} key={group}>
|
||||||
|
{sources.filter(s => (s.group || 'Debugger') === group).map(source => renderOption(source))}
|
||||||
|
</optgroup>
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function emptySource(): Source {
|
export function emptySource(): Source {
|
||||||
|
|
|
@ -28,6 +28,7 @@ export interface ToolbarButtonProps {
|
||||||
style?: React.CSSProperties,
|
style?: React.CSSProperties,
|
||||||
testId?: string,
|
testId?: string,
|
||||||
className?: string,
|
className?: string,
|
||||||
|
ariaLabel?: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ToolbarButton: React.FC<React.PropsWithChildren<ToolbarButtonProps>> = ({
|
export const ToolbarButton: React.FC<React.PropsWithChildren<ToolbarButtonProps>> = ({
|
||||||
|
@ -40,6 +41,7 @@ export const ToolbarButton: React.FC<React.PropsWithChildren<ToolbarButtonProps>
|
||||||
style,
|
style,
|
||||||
testId,
|
testId,
|
||||||
className,
|
className,
|
||||||
|
ariaLabel,
|
||||||
}) => {
|
}) => {
|
||||||
return <button
|
return <button
|
||||||
className={clsx(className, 'toolbar-button', icon, toggled && 'toggled')}
|
className={clsx(className, 'toolbar-button', icon, toggled && 'toggled')}
|
||||||
|
@ -50,6 +52,7 @@ export const ToolbarButton: React.FC<React.PropsWithChildren<ToolbarButtonProps>
|
||||||
disabled={!!disabled}
|
disabled={!!disabled}
|
||||||
style={style}
|
style={style}
|
||||||
data-testid={testId}
|
data-testid={testId}
|
||||||
|
aria-label={ariaLabel}
|
||||||
>
|
>
|
||||||
{icon && <span className={`codicon codicon-${icon}`} style={children ? { marginRight: 5 } : {}}></span>}
|
{icon && <span className={`codicon codicon-${icon}`} style={children ? { marginRight: 5 } : {}}></span>}
|
||||||
{children}
|
{children}
|
||||||
|
|
|
@ -188,9 +188,9 @@ test('test', async ({ page }) => {
|
||||||
await page.getByRole('button', { name: 'Submit' }).click();
|
await page.getByRole('button', { name: 'Submit' }).click();
|
||||||
});`
|
});`
|
||||||
});
|
});
|
||||||
const length = events.length;
|
|
||||||
// No events after mode disabled
|
// No events after mode disabled
|
||||||
await backend.setRecorderMode({ mode: 'none' });
|
await backend.setRecorderMode({ mode: 'none' });
|
||||||
|
const length = events.length;
|
||||||
await page.getByRole('button').click();
|
await page.getByRole('button').click();
|
||||||
expect(events).toHaveLength(length);
|
expect(events).toHaveLength(length);
|
||||||
});
|
});
|
||||||
|
|
|
@ -171,6 +171,13 @@ export class Recorder {
|
||||||
return this.page.locator('x-pw-tooltip').textContent();
|
return this.page.locator('x-pw-tooltip').textContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async waitForHighlightNoTooltip(action: () => Promise<void>): Promise<string> {
|
||||||
|
await this.page.$$eval('x-pw-highlight', els => els.forEach(e => e.remove()));
|
||||||
|
await action();
|
||||||
|
await this.page.locator('x-pw-highlight').waitFor();
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
async waitForActionPerformed(): Promise<{ hovered: string | null, active: string | null }> {
|
async waitForActionPerformed(): Promise<{ hovered: string | null, active: string | null }> {
|
||||||
let callback;
|
let callback;
|
||||||
const listener = async msg => {
|
const listener = async msg => {
|
||||||
|
@ -185,8 +192,8 @@ export class Recorder {
|
||||||
return new Promise(f => callback = f);
|
return new Promise(f => callback = f);
|
||||||
}
|
}
|
||||||
|
|
||||||
async hoverOverElement(selector: string, options?: { position?: { x: number, y: number }}): Promise<string> {
|
async hoverOverElement(selector: string, options?: { position?: { x: number, y: number }, omitTooltip?: boolean }): Promise<string> {
|
||||||
return this.waitForHighlight(async () => {
|
return (options?.omitTooltip ? this.waitForHighlightNoTooltip : this.waitForHighlight).call(this, async () => {
|
||||||
const box = await this.page.locator(selector).first().boundingBox();
|
const box = await this.page.locator(selector).first().boundingBox();
|
||||||
const offset = options?.position || { x: box.width / 2, y: box.height / 2 };
|
const offset = options?.position || { x: box.width / 2, y: box.height / 2 };
|
||||||
await this.page.mouse.move(box.x + offset.x, box.y + offset.y);
|
await this.page.mouse.move(box.x + offset.x, box.y + offset.y);
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Page } from 'playwright-core';
|
import type { Page } from 'playwright-core';
|
||||||
import { test as it, expect } from './inspectorTest';
|
import { test as it, expect, Recorder } from './inspectorTest';
|
||||||
import { waitForTestLog } from '../../config/utils';
|
import { waitForTestLog } from '../../config/utils';
|
||||||
|
|
||||||
|
|
||||||
|
@ -103,6 +103,7 @@ it.describe('pause', () => {
|
||||||
await page.pause();
|
await page.pause();
|
||||||
})();
|
})();
|
||||||
const recorderPage = await recorderPageGetter();
|
const recorderPage = await recorderPageGetter();
|
||||||
|
await expect(recorderPage.getByRole('combobox', { name: 'Source chooser' })).toHaveValue(/pause\.spec\.ts/);
|
||||||
const source = await recorderPage.textContent('.source-line-paused');
|
const source = await recorderPage.textContent('.source-line-paused');
|
||||||
expect(source).toContain('page.pause()');
|
expect(source).toContain('page.pause()');
|
||||||
await recorderPage.click('[title="Resume (F8)"]');
|
await recorderPage.click('[title="Resume (F8)"]');
|
||||||
|
@ -480,6 +481,26 @@ it.describe('pause', () => {
|
||||||
await recorderPage.click('[title="Resume (F8)"]');
|
await recorderPage.click('[title="Resume (F8)"]');
|
||||||
await scriptPromise;
|
await scriptPromise;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should record from debugger', async ({ page, recorderPageGetter }) => {
|
||||||
|
await page.setContent('<body style="width: 100%; height: 100%"></body>');
|
||||||
|
const scriptPromise = (async () => {
|
||||||
|
await page.pause();
|
||||||
|
})();
|
||||||
|
const recorderPage = await recorderPageGetter();
|
||||||
|
await expect(recorderPage.getByRole('combobox', { name: 'Source chooser' })).toHaveValue(/pause\.spec\.ts/);
|
||||||
|
await expect(recorderPage.locator('.source-line-paused')).toHaveText(/await page\.pause\(\)/);
|
||||||
|
await recorderPage.getByRole('button', { name: 'Record' }).click();
|
||||||
|
|
||||||
|
const recorder = new Recorder(page, recorderPage);
|
||||||
|
await recorder.hoverOverElement('body', { omitTooltip: true });
|
||||||
|
await recorder.trustedClick();
|
||||||
|
|
||||||
|
await expect(recorderPage.getByRole('combobox', { name: 'Source chooser' })).toHaveValue('javascript');
|
||||||
|
await expect(recorderPage.locator('.cm-wrapper')).toContainText(`await page.locator('body').click();`);
|
||||||
|
await recorderPage.getByRole('button', { name: 'Resume' }).click();
|
||||||
|
await scriptPromise;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
async function sanitizeLog(recorderPage: Page): Promise<string[]> {
|
async function sanitizeLog(recorderPage: Page): Promise<string[]> {
|
||||||
|
|
|
@ -508,3 +508,27 @@ test('should throw when connecting twice', async ({ page, server }) => {
|
||||||
const error = await promise;
|
const error = await promise;
|
||||||
expect(error.message).toContain('Already connected to the server');
|
expect(error.message).toContain('Already connected to the server');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should work with no trailing slash', async ({ page, server }) => {
|
||||||
|
const log: string[] = [];
|
||||||
|
// No trailing slash!
|
||||||
|
await page.routeWebSocket('ws://localhost:' + server.PORT, ws => {
|
||||||
|
ws.onMessage(message => {
|
||||||
|
log.push(message as string);
|
||||||
|
ws.send('response');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.goto('about:blank');
|
||||||
|
await page.evaluate(({ port }) => {
|
||||||
|
window.log = [];
|
||||||
|
// No trailing slash!
|
||||||
|
window.ws = new WebSocket('ws://localhost:' + port);
|
||||||
|
window.ws.addEventListener('message', event => window.log.push(event.data));
|
||||||
|
}, { port: server.PORT });
|
||||||
|
|
||||||
|
await expect.poll(() => page.evaluate(() => window.ws.readyState)).toBe(1);
|
||||||
|
await page.evaluate(() => window.ws.send('query'));
|
||||||
|
await expect.poll(() => log).toEqual(['query']);
|
||||||
|
expect(await page.evaluate(() => window.log)).toEqual(['response']);
|
||||||
|
});
|
||||||
|
|
|
@ -123,9 +123,10 @@ it('should intercept network activity from worker', async function({ page, serve
|
||||||
|
|
||||||
it('should intercept worker requests when enabled after worker creation', {
|
it('should intercept worker requests when enabled after worker creation', {
|
||||||
annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/32355' }
|
annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/32355' }
|
||||||
}, async ({ page, server, isAndroid, browserName }) => {
|
}, async ({ page, server, isAndroid, browserName, browserMajorVersion }) => {
|
||||||
it.skip(isAndroid);
|
it.skip(isAndroid);
|
||||||
it.fixme(browserName === 'chromium');
|
it.skip(browserName === 'chromium' && browserMajorVersion < 130, 'fixed in Chromium 130');
|
||||||
|
it.fixme(browserName === 'chromium', 'requires PlzDedicatedWorker to be enabled');
|
||||||
|
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
server.setRoute('/data_for_worker', (req, res) => res.end('failed to intercept'));
|
server.setRoute('/data_for_worker', (req, res) => res.end('failed to intercept'));
|
||||||
|
|
|
@ -167,7 +167,6 @@ it('should report network activity', async function({ page, server, browserName,
|
||||||
|
|
||||||
it('should report network activity on worker creation', async function({ page, server, browserName, browserMajorVersion }) {
|
it('should report network activity on worker creation', async function({ page, server, browserName, browserMajorVersion }) {
|
||||||
it.skip(browserName === 'firefox' && browserMajorVersion < 114, 'https://github.com/microsoft/playwright/issues/21760');
|
it.skip(browserName === 'firefox' && browserMajorVersion < 114, 'https://github.com/microsoft/playwright/issues/21760');
|
||||||
// Chromium needs waitForDebugger enabled for this one.
|
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
const url = server.PREFIX + '/one-style.css';
|
const url = server.PREFIX + '/one-style.css';
|
||||||
const requestPromise = page.waitForRequest(url);
|
const requestPromise = page.waitForRequest(url);
|
||||||
|
@ -182,6 +181,19 @@ it('should report network activity on worker creation', async function({ page, s
|
||||||
expect(response.ok()).toBe(true);
|
expect(response.ok()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should report worker script as network request', {
|
||||||
|
annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/33107' },
|
||||||
|
}, async function({ page, server }) {
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
const [request1, request2] = await Promise.all([
|
||||||
|
page.waitForEvent('request', r => r.url().includes('worker.js')),
|
||||||
|
page.waitForEvent('requestfinished', r => r.url().includes('worker.js')),
|
||||||
|
page.evaluate(() => (window as any).w = new Worker('/worker/worker.js')),
|
||||||
|
]);
|
||||||
|
expect.soft(request1.url()).toBe(server.PREFIX + '/worker/worker.js');
|
||||||
|
expect.soft(request1).toBe(request2);
|
||||||
|
});
|
||||||
|
|
||||||
it('should dispatch console messages when page has workers', async function({ page, server }) {
|
it('should dispatch console messages when page has workers', async function({ page, server }) {
|
||||||
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/15550' });
|
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/15550' });
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
|
|
@ -4,7 +4,7 @@ set -x
|
||||||
|
|
||||||
trap "cd $(pwd -P)" EXIT
|
trap "cd $(pwd -P)" EXIT
|
||||||
SCRIPT_PATH="$(cd "$(dirname "$0")" ; pwd -P)"
|
SCRIPT_PATH="$(cd "$(dirname "$0")" ; pwd -P)"
|
||||||
NODE_VERSION="20.17.0" # autogenerated via ./update-playwright-driver-version.mjs
|
NODE_VERSION="20.18.0" # autogenerated via ./update-playwright-driver-version.mjs
|
||||||
|
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
PACKAGE_VERSION=$(node -p "require('../../package.json').version")
|
PACKAGE_VERSION=$(node -p "require('../../package.json').version")
|
||||||
|
|
|
@ -2,7 +2,7 @@ FROM ubuntu:noble
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
ARG TZ=America/Los_Angeles
|
ARG TZ=America/Los_Angeles
|
||||||
ARG DOCKER_IMAGE_NAME_TEMPLATE="mcr.microsoft.com/playwright:v%version%-jammy"
|
ARG DOCKER_IMAGE_NAME_TEMPLATE="mcr.microsoft.com/playwright:v%version%-noble"
|
||||||
|
|
||||||
ENV LANG=C.UTF-8
|
ENV LANG=C.UTF-8
|
||||||
ENV LC_ALL=C.UTF-8
|
ENV LC_ALL=C.UTF-8
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
// @ts-check
|
// @ts-check
|
||||||
const Documentation = require('./documentation');
|
const Documentation = require('./documentation');
|
||||||
const { visitAll } = require('../markdown');
|
const { visitAll, render } = require('../markdown');
|
||||||
/**
|
/**
|
||||||
* @param {Documentation.MarkdownNode[]} nodes
|
* @param {Documentation.MarkdownNode[]} nodes
|
||||||
* @param {number} maxColumns
|
* @param {number} maxColumns
|
||||||
|
@ -64,7 +64,10 @@ function _innerRenderNodes(nodes, maxColumns = 80, wrapParagraphs = true) {
|
||||||
} else if (node.type === 'li') {
|
} else if (node.type === 'li') {
|
||||||
_wrapInNode('item><description', _wrapAndEscape(node, maxColumns), summary, '/description></item');
|
_wrapInNode('item><description', _wrapAndEscape(node, maxColumns), summary, '/description></item');
|
||||||
} else if (node.type === 'note') {
|
} else if (node.type === 'note') {
|
||||||
_wrapInNode('para', _wrapAndEscape(node, maxColumns), remarks);
|
_wrapInNode('para', _wrapAndEscape({
|
||||||
|
type: 'text',
|
||||||
|
text: render(node.children ?? []).replaceAll('\n', '↵'),
|
||||||
|
}, maxColumns), remarks);
|
||||||
}
|
}
|
||||||
lastNode = node;
|
lastNode = node;
|
||||||
});
|
});
|
||||||
|
@ -75,11 +78,11 @@ function _innerRenderNodes(nodes, maxColumns = 80, wrapParagraphs = true) {
|
||||||
|
|
||||||
function _wrapCode(lines) {
|
function _wrapCode(lines) {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
let out = [];
|
const out = [];
|
||||||
for (let line of lines) {
|
for (let line of lines) {
|
||||||
line = line.replace(/[&]/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
line = line.replace(/[&]/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||||
if (i < lines.length - 1)
|
if (i < lines.length - 1)
|
||||||
line = line + "<br/>";
|
line = line + '<br/>';
|
||||||
out.push(line);
|
out.push(line);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
@ -163,4 +166,4 @@ function renderTextOnly(nodes, maxColumns = 80) {
|
||||||
return result.summary;
|
return result.summary;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { renderXmlDoc, renderTextOnly }
|
module.exports = { renderXmlDoc, renderTextOnly };
|
|
@ -520,7 +520,8 @@ function renderMethod(member, parent, name, options, out) {
|
||||||
&& !name.startsWith('Get')
|
&& !name.startsWith('Get')
|
||||||
&& name !== 'CreateFormData'
|
&& name !== 'CreateFormData'
|
||||||
&& !name.startsWith('PostDataJSON')
|
&& !name.startsWith('PostDataJSON')
|
||||||
&& !name.startsWith('As')) {
|
&& !name.startsWith('As')
|
||||||
|
&& name !== 'ConnectToServer') {
|
||||||
if (!member.async) {
|
if (!member.async) {
|
||||||
if (member.spec && !options.nodocs)
|
if (member.spec && !options.nodocs)
|
||||||
out.push(...XmlDoc.renderXmlDoc(member.spec, maxDocumentationColumnWidth));
|
out.push(...XmlDoc.renderXmlDoc(member.spec, maxDocumentationColumnWidth));
|
||||||
|
@ -718,7 +719,7 @@ function translateType(type, parent, generateNameCallback = t => t.name, optiona
|
||||||
if (type.expression === '[null]|[Error]')
|
if (type.expression === '[null]|[Error]')
|
||||||
return 'void';
|
return 'void';
|
||||||
|
|
||||||
if (type.name == 'Promise' && type.templates?.[0].name === 'any')
|
if (type.name === 'Promise' && type.templates?.[0].name === 'any')
|
||||||
return 'Task';
|
return 'Task';
|
||||||
|
|
||||||
if (type.union) {
|
if (type.union) {
|
||||||
|
|
Loading…
Reference in New Issue