Compare commits
31 commits
main
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba2035b031 | ||
|
|
47f428a00b | ||
|
|
6365c0fe6c | ||
|
|
0dade6ef0e | ||
|
|
56e04df8aa | ||
|
|
a2acf769eb | ||
|
|
741e338fb1 | ||
|
|
19790a0e70 | ||
|
|
2b64bca12b | ||
|
|
354f6ea337 | ||
|
|
e9c42a16d8 | ||
|
|
a126a2f220 | ||
|
|
a19117aa6b | ||
|
|
e9200c5f9f | ||
|
|
389aa59470 | ||
|
|
140f8e45f9 | ||
|
|
a8cf285df6 | ||
|
|
25cd4c9a30 | ||
|
|
7db695ffb5 | ||
|
|
5c6335edda | ||
|
|
29ec22b9c9 | ||
|
|
8f292e09bf | ||
|
|
270675a89f | ||
|
|
7b78e71433 | ||
|
|
c74c9a3690 | ||
|
|
c175d6fa8e | ||
|
|
7f74063549 | ||
|
|
d3e80664e8 | ||
|
|
c840a28946 | ||
|
|
82769c23de | ||
|
|
0b30dd2537 |
|
|
@ -1,6 +1,6 @@
|
|||
# 🎭 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://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 -->
|
||||
|
||||
## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright)
|
||||
|
||||
|
|
@ -8,7 +8,7 @@ Playwright is a framework for Web Testing and Automation. It allows testing [Chr
|
|||
|
||||
| | Linux | macOS | Windows |
|
||||
| :--- | :---: | :---: | :---: |
|
||||
| Chromium <!-- GEN:chromium-version -->103.0.5060.53<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Chromium <!-- GEN:chromium-version -->104.0.5112.20<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| WebKit <!-- GEN:webkit-version -->15.4<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Firefox <!-- GEN:firefox-version -->100.0.2<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
|
||||
|
|
|
|||
|
|
@ -1025,6 +1025,35 @@ handler function to route the request.
|
|||
|
||||
How often a route should be used. By default it will be used every time.
|
||||
|
||||
## async method: BrowserContext.routeFromHAR
|
||||
|
||||
If specified the network requests that are made in the context will be served from the HAR file. Read more about [Replaying from HAR](../network.md#replaying-from-har).
|
||||
|
||||
Playwright will not serve requests intercepted by Service Worker from the HAR file. See [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using request interception by setting [`option: Browser.newContext.serviceWorkers`] to `'block'`.
|
||||
|
||||
### param: BrowserContext.routeFromHAR.har
|
||||
- `har` <[path]>
|
||||
|
||||
Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a relative path, then it is resolved relative to the current working directory.
|
||||
|
||||
### option: BrowserContext.routeFromHAR.notFound
|
||||
- `notFound` ?<[HarNotFound]<"abort"|"fallback">>
|
||||
|
||||
* If set to 'abort' any request not found in the HAR file will be aborted.
|
||||
* If set to 'fallback' falls through to the next route handler in the handler chain.
|
||||
|
||||
Defaults to abort.
|
||||
|
||||
### option: BrowserContext.routeFromHAR.update
|
||||
- `update` ?<boolean>
|
||||
|
||||
If specified, updates the given HAR with the actual network information instead of serving from file.
|
||||
|
||||
### option: BrowserContext.routeFromHAR.url
|
||||
- `url` <[string]|[RegExp]>
|
||||
|
||||
A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern will be surved from the HAR file. If not specified, all requests are served from the HAR file.
|
||||
|
||||
## method: BrowserContext.serviceWorkers
|
||||
* langs: js, python
|
||||
- returns: <[Array]<[Worker]>>
|
||||
|
|
|
|||
|
|
@ -94,7 +94,6 @@ Maximum time in milliseconds to wait for the application to start. Defaults to `
|
|||
### option: Electron.launch.recordhar = %%-context-option-recordhar-%%
|
||||
### option: Electron.launch.recordharpath = %%-context-option-recordhar-path-%%
|
||||
### option: Electron.launch.recordHarOmitContent = %%-context-option-recordhar-omit-content-%%
|
||||
### option: Electron.launch.har = %%-js-context-option-har-%%
|
||||
### option: Electron.launch.recordvideo = %%-context-option-recordvideo-%%
|
||||
### option: Electron.launch.recordvideodir = %%-context-option-recordvideo-dir-%%
|
||||
### option: Electron.launch.recordvideosize = %%-context-option-recordvideo-size-%%
|
||||
|
|
|
|||
|
|
@ -2732,6 +2732,35 @@ handler function to route the request.
|
|||
|
||||
How often a route should be used. By default it will be used every time.
|
||||
|
||||
## async method: Page.routeFromHAR
|
||||
|
||||
If specified the network requests that are made in the page will be served from the HAR file. Read more about [Replaying from HAR](../network.md#replaying-from-har).
|
||||
|
||||
Playwright will not serve requests intercepted by Service Worker from the HAR file. See [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using request interception by setting [`option: Browser.newContext.serviceWorkers`] to `'block'`.
|
||||
|
||||
### param: Page.routeFromHAR.har
|
||||
- `har` <[path]>
|
||||
|
||||
Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a relative path, then it is resolved relative to the current working directory.
|
||||
|
||||
### option: Page.routeFromHAR.notFound
|
||||
- `notFound` ?<[HarNotFound]<"abort"|"fallback">>
|
||||
|
||||
* If set to 'abort' any request not found in the HAR file will be aborted.
|
||||
* If set to 'fallback' missing requests will be sent to the network.
|
||||
|
||||
Defaults to abort.
|
||||
|
||||
### option: Page.routeFromHAR.update
|
||||
- `update` ?<boolean>
|
||||
|
||||
If specified, updates the given HAR with the actual network information instead of serving from file.
|
||||
|
||||
### option: Page.routeFromHAR.url
|
||||
- `url` <[string]|[RegExp]>
|
||||
|
||||
A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern will be surved from the HAR file. If not specified, all requests are served from the HAR file.
|
||||
|
||||
## async method: Page.screenshot
|
||||
- returns: <[Buffer]>
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,9 @@ Returns the [Frame] that initiated this request.
|
|||
## method: Request.headers
|
||||
- returns: <[Object]<[string], [string]>>
|
||||
|
||||
**DEPRECATED** Incomplete list of headers as seen by the rendering engine. Use [`method: Request.allHeaders`] instead.
|
||||
An object with the request HTTP headers. The header names are lower-cased.
|
||||
Note that this method does not return security-related headers, including cookie-related ones.
|
||||
You can use [`method: Request.allHeaders`] for complete list of headers that include `cookie` information.
|
||||
|
||||
## async method: Request.headersArray
|
||||
- returns: <[Array]<[Object]>>
|
||||
|
|
|
|||
|
|
@ -30,7 +30,9 @@ Indicates whether this Response was fullfilled by a Service Worker's Fetch Handl
|
|||
## method: Response.headers
|
||||
- returns: <[Object]<[string], [string]>>
|
||||
|
||||
**DEPRECATED** Incomplete list of headers as seen by the rendering engine. Use [`method: Response.allHeaders`] instead.
|
||||
An object with the response HTTP headers. The header names are lower-cased.
|
||||
Note that this method does not return security-related headers, including cookie-related ones.
|
||||
You can use [`method: Response.allHeaders`] for complete list of headers that include `cookie` information.
|
||||
|
||||
## async method: Response.headersArray
|
||||
- returns: <[Array]<[Object]>>
|
||||
|
|
|
|||
|
|
@ -247,37 +247,6 @@ The file path to save the storage state to. If [`option: path`] is a relative pa
|
|||
current working directory. If no path is provided, storage
|
||||
state is still returned, but won't be saved to the disk.
|
||||
|
||||
## js-context-option-har
|
||||
* langs: js, python
|
||||
- `har` <[Object]>
|
||||
- `path` <[path]> Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a relative path, then it is resolved relative to the current working directory.
|
||||
- `fallback` ?<[HarFallback]<"abort"|"continue">> If set to 'abort' any request not found in the HAR file will be aborted. If set to'continue' missing requests will be sent to the network. Defaults to 'abort'.
|
||||
- `urlFilter` ?<[string]|[RegExp]> A glob pattern or regular expression to match request URL while routing. Only requests with URL matching the pattern will be surved from the HAR file. If not specified, all requests are served from the HAR file.
|
||||
|
||||
If specified the network requests that are made in the context will be served from the HAR file. Read more about [Replaying from HAR](../network.md#replaying-from-har).
|
||||
|
||||
:::note
|
||||
Playwright will not serve requests intercepted by Service Worker from the HAR file. See [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using request interception by setting [`option: Browser.newContext.serviceWorkers`] to `'block'`.
|
||||
:::
|
||||
|
||||
## csharp-java-python-context-option-har-path
|
||||
* langs: csharp, java, python
|
||||
- `harPath` <[path]>
|
||||
|
||||
Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If the HAR file contains an entry with the matching URL and HTTP method, then the entry's headers, status and body will be used to fulfill the network request. An entry resulting in a redirect will be followed automatically. If `path` is a relative path, then it is resolved relative to the current working directory.
|
||||
|
||||
## csharp-java-python-context-option-har-fallback
|
||||
* langs: csharp, java, python
|
||||
- `harFallback` ?<[HarFallback]<"abort"|"continue">>
|
||||
|
||||
If set to 'abort' any request not found in the HAR file will be aborted. If set to'continue' missing requests will be sent to the network. Defaults to 'abort'.
|
||||
|
||||
## csharp-java-python-context-option-har-urlfilter
|
||||
* langs: csharp, java, python
|
||||
- `harUrlFilter` ?<[string]|[RegExp]>
|
||||
|
||||
A glob pattern or regular expression to match request URL while routing. Only requests with URL matching the pattern will be surved from the HAR file. If not specified, all requests are served from the HAR file.
|
||||
|
||||
## context-option-acceptdownloads
|
||||
- `acceptDownloads` <[boolean]>
|
||||
|
||||
|
|
@ -592,8 +561,9 @@ Logger sink for Playwright logging.
|
|||
- `recordHar` <[Object]>
|
||||
- `omitContent` ?<[boolean]> Optional setting to control whether to omit request content from the HAR. Defaults to
|
||||
`false`. Deprecated, use `content` policy instead.
|
||||
- `content` ?<[HarContentPolicy]<"omit"|"embed"|"attach">> Optional setting to control resource content management. If `omit` is specified, content is not persisted. If `attach` is specified, resources are persistet as separate files and all of these files are archived along with the HAR file. Defaults to `embed`, which stores content inline the HAR file as per HAR specification.
|
||||
- `path` <[path]> Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, `attach` mode is used by default.
|
||||
- `content` ?<[HarContentPolicy]<"omit"|"embed"|"attach">> Optional setting to control resource content management. If `omit` is specified, content is not persisted. If `attach` is specified, resources are persistet as separate files or entries in the ZIP archive. If `embed` is specified, content is stored inline the HAR file as per HAR specification. Defaults to `attach` for `.zip` output files and to `embed` for all other file extensions.
|
||||
- `path` <[path]> Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, `content: 'attach'` is used by default.
|
||||
- `mode` ?<[HarMode]<"full"|"minimal">> When set to `minimal`, only record information necessary for routing from HAR. This omits sizes, timing, page, cookies, security and other types of HAR information that are not used when replaying from HAR. Defaults to `full`.
|
||||
- `urlFilter` ?<[string]|[RegExp]> A glob or regex pattern to filter requests that are stored in the HAR. When a [`option: baseURL`] via the context options was provided and the passed URL is a path, it gets merged via the [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor.
|
||||
|
||||
Enables [HAR](http://www.softwareishard.com/blog/har-12-spec) recording for all pages into `recordHar.path` file. If not
|
||||
|
|
@ -616,6 +586,20 @@ call [`method: BrowserContext.close`] for the HAR to be saved.
|
|||
|
||||
Optional setting to control whether to omit request content from the HAR. Defaults to `false`.
|
||||
|
||||
## context-option-recordhar-content
|
||||
* langs: csharp, java, python
|
||||
- alias-python: record_har_content
|
||||
- `recordHarContent` ?<[HarContentPolicy]<"omit"|"embed"|"attach">>
|
||||
|
||||
Optional setting to control resource content management. If `omit` is specified, content is not persisted. If `attach` is specified, resources are persistet as separate files and all of these files are archived along with the HAR file. Defaults to `embed`, which stores content inline the HAR file as per HAR specification.
|
||||
|
||||
## context-option-recordhar-mode
|
||||
* langs: csharp, java, python
|
||||
- alias-python: record_har_mode
|
||||
- `recordHarMode` ?<[HarMode]<"full"|"minimal">>
|
||||
|
||||
When set to `minimal`, only record information necessary for routing from HAR. This omits sizes, timing, page, cookies, security and other types of HAR information that are not used when replaying from HAR. Defaults to `full`.
|
||||
|
||||
## context-option-recordhar-url-filter
|
||||
* langs: csharp, java, python
|
||||
- alias-python: record_har_url_filter
|
||||
|
|
@ -834,13 +818,11 @@ An acceptable perceived color difference in the [YIQ color space](https://en.wik
|
|||
- %%-context-option-logger-%%
|
||||
- %%-context-option-videospath-%%
|
||||
- %%-context-option-videosize-%%
|
||||
- %%-js-context-option-har-%%
|
||||
- %%-csharp-java-python-context-option-har-path-%%
|
||||
- %%-csharp-java-python-context-option-har-fallback-%%
|
||||
- %%-csharp-java-python-context-option-har-urlfilter-%%
|
||||
- %%-context-option-recordhar-%%
|
||||
- %%-context-option-recordhar-path-%%
|
||||
- %%-context-option-recordhar-omit-content-%%
|
||||
- %%-context-option-recordhar-content-%%
|
||||
- %%-context-option-recordhar-mode-%%
|
||||
- %%-context-option-recordhar-url-filter-%%
|
||||
- %%-context-option-recordvideo-%%
|
||||
- %%-context-option-recordvideo-dir-%%
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ Alternatively, you can use [Command line tools](./cli.md#install-system-dependen
|
|||
pool:
|
||||
vmImage: 'ubuntu-20.04'
|
||||
|
||||
container: mcr.microsoft.com/playwright:v1.23.0-focal
|
||||
container: mcr.microsoft.com/playwright:v1.23.4-focal
|
||||
|
||||
steps:
|
||||
...
|
||||
|
|
@ -157,7 +157,7 @@ Running Playwright on CircleCI requires the following steps:
|
|||
|
||||
```yml
|
||||
docker:
|
||||
- image: mcr.microsoft.com/playwright:v1.23.0-focal
|
||||
- image: mcr.microsoft.com/playwright:v1.23.4-focal
|
||||
environment:
|
||||
NODE_ENV: development # Needed if playwright is in `devDependencies`
|
||||
```
|
||||
|
|
@ -179,7 +179,7 @@ to run tests on Jenkins.
|
|||
|
||||
```groovy
|
||||
pipeline {
|
||||
agent { docker { image 'mcr.microsoft.com/playwright:v1.23.0-focal' } }
|
||||
agent { docker { image 'mcr.microsoft.com/playwright:v1.23.4-focal' } }
|
||||
stages {
|
||||
stage('e2e-tests') {
|
||||
steps {
|
||||
|
|
@ -196,7 +196,7 @@ pipeline {
|
|||
Bitbucket Pipelines can use public [Docker images as build environments](https://confluence.atlassian.com/bitbucket/use-docker-images-as-build-environments-792298897.html). To run Playwright tests on Bitbucket, use our public Docker image ([see Dockerfile](./docker.md)).
|
||||
|
||||
```yml
|
||||
image: mcr.microsoft.com/playwright:v1.23.0-focal
|
||||
image: mcr.microsoft.com/playwright:v1.23.4-focal
|
||||
```
|
||||
|
||||
### GitLab CI
|
||||
|
|
@ -209,7 +209,7 @@ stages:
|
|||
|
||||
tests:
|
||||
stage: test
|
||||
image: mcr.microsoft.com/playwright:v1.23.0-focal
|
||||
image: mcr.microsoft.com/playwright:v1.23.4-focal
|
||||
script:
|
||||
...
|
||||
```
|
||||
|
|
|
|||
|
|
@ -14,19 +14,19 @@ This image is published on [Docker Hub].
|
|||
### Pull the image
|
||||
|
||||
```bash js
|
||||
docker pull mcr.microsoft.com/playwright:v1.23.0-focal
|
||||
docker pull mcr.microsoft.com/playwright:v1.23.4-focal
|
||||
```
|
||||
|
||||
```bash python
|
||||
docker pull mcr.microsoft.com/playwright/python:v1.23.0-focal
|
||||
docker pull mcr.microsoft.com/playwright/python:v1.23.4-focal
|
||||
```
|
||||
|
||||
```bash csharp
|
||||
docker pull mcr.microsoft.com/playwright/dotnet:v1.23.0-focal
|
||||
docker pull mcr.microsoft.com/playwright/dotnet:v1.23.4-focal
|
||||
```
|
||||
|
||||
```bash java
|
||||
docker pull mcr.microsoft.com/playwright/java:v1.23.0-focal
|
||||
docker pull mcr.microsoft.com/playwright/java:v1.23.4-focal
|
||||
```
|
||||
|
||||
### Run the image
|
||||
|
|
@ -38,19 +38,19 @@ By default, the Docker image will use the `root` user to run the browsers. This
|
|||
On trusted websites, you can avoid creating a separate user and use root for it since you trust the code which will run on the browsers.
|
||||
|
||||
```bash js
|
||||
docker run -it --rm --ipc=host mcr.microsoft.com/playwright:v1.23.0-focal /bin/bash
|
||||
docker run -it --rm --ipc=host mcr.microsoft.com/playwright:v1.23.4-focal /bin/bash
|
||||
```
|
||||
|
||||
```bash python
|
||||
docker run -it --rm --ipc=host mcr.microsoft.com/playwright/python:v1.23.0-focal /bin/bash
|
||||
docker run -it --rm --ipc=host mcr.microsoft.com/playwright/python:v1.23.4-focal /bin/bash
|
||||
```
|
||||
|
||||
```bash csharp
|
||||
docker run -it --rm --ipc=host mcr.microsoft.com/playwright/dotnet:v1.23.0-focal /bin/bash
|
||||
docker run -it --rm --ipc=host mcr.microsoft.com/playwright/dotnet:v1.23.4-focal /bin/bash
|
||||
```
|
||||
|
||||
```bash java
|
||||
docker run -it --rm --ipc=host mcr.microsoft.com/playwright/java:v1.23.0-focal /bin/bash
|
||||
docker run -it --rm --ipc=host mcr.microsoft.com/playwright/java:v1.23.4-focal /bin/bash
|
||||
```
|
||||
|
||||
#### Crawling and scraping
|
||||
|
|
@ -58,19 +58,19 @@ docker run -it --rm --ipc=host mcr.microsoft.com/playwright/java:v1.23.0-focal /
|
|||
On untrusted websites, it's recommended to use a separate user for launching the browsers in combination with the seccomp profile. Inside the container or if you are using the Docker image as a base image you have to use `adduser` for it.
|
||||
|
||||
```bash js
|
||||
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright:v1.23.0-focal /bin/bash
|
||||
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright:v1.23.4-focal /bin/bash
|
||||
```
|
||||
|
||||
```bash python
|
||||
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/python:v1.23.0-focal /bin/bash
|
||||
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/python:v1.23.4-focal /bin/bash
|
||||
```
|
||||
|
||||
```bash csharp
|
||||
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/dotnet:v1.23.0-focal /bin/bash
|
||||
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/dotnet:v1.23.4-focal /bin/bash
|
||||
```
|
||||
|
||||
```bash java
|
||||
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/java:v1.23.0-focal /bin/bash
|
||||
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/java:v1.23.4-focal /bin/bash
|
||||
```
|
||||
|
||||
[`seccomp_profile.json`](https://github.com/microsoft/playwright/blob/main/utils/docker/seccomp_profile.json) is needed to run Chromium with sandbox. This is a [default Docker seccomp profile](https://github.com/docker/engine/blob/d0d99b04cf6e00ed3fc27e81fc3d94e7eda70af3/profiles/seccomp/default.json) with extra user namespace cloning permissions:
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public class Example {
|
|||
<dependency>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>playwright</artifactId>
|
||||
<version>1.17.1</version>
|
||||
<version>1.23.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
|
|
|
|||
|
|
@ -740,7 +740,7 @@ await context.close();
|
|||
```java
|
||||
BrowserContext context = browser.newContext(new Browser.NewContextOptions()
|
||||
.setRecordHarPath(Paths.get("example.har"))
|
||||
.setRecordHarUrlFilter("**/api/**");
|
||||
.setRecordHarUrlFilter("**/api/**"));
|
||||
|
||||
// ... Perform actions ...
|
||||
|
||||
|
|
@ -780,61 +780,43 @@ await context.CloseAsync();
|
|||
|
||||
### Replaying from HAR
|
||||
|
||||
Pass [`option: har`] option to the [`method: Browser.newContext`] method to use matching responses from the [HAR](http://www.softwareishard.com/blog/har-12-spec/) file.
|
||||
Use [`method: Page.routeFromHAR`] or [`method: BrowserContext.routeFromHAR`] to serve matching responses from the [HAR](http://www.softwareishard.com/blog/har-12-spec/) file.
|
||||
|
||||
```js
|
||||
// Replay API requests from HAR.
|
||||
// Either use a matching response from the HAR,
|
||||
// or abort the request if nothing matches.
|
||||
const context = await browser.newContext({ har: { path: 'example.har' } });
|
||||
await page.routeFromHAR('example.har');
|
||||
```
|
||||
|
||||
```java
|
||||
// Either use a matching response from the HAR,
|
||||
// or abort the request if nothing matches.
|
||||
BrowserContext context = browser.newContext(new Browser.NewContextOptions().setHarPath(Paths.get("example.har")));
|
||||
Page page = context.newPage();
|
||||
page.navigate("https://example.com");
|
||||
page.routeFromHAR(Paths.get("example.har"));
|
||||
```
|
||||
|
||||
```python async
|
||||
# Either use a matching response from the HAR,
|
||||
# or abort the request if nothing matches.
|
||||
context = await browser.new_context(
|
||||
har_path = "example.har"
|
||||
)
|
||||
page = await context.new_page()
|
||||
await page.goto("https://example.com")
|
||||
await page.route_from_har("example.har")
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Either use a matching response from the HAR,
|
||||
# or abort the request if nothing matches.
|
||||
context = browser.new_context(
|
||||
har_path="example.har"
|
||||
)
|
||||
page = context.new_page()
|
||||
page.goto("https://example.com")
|
||||
page.route_from_har("example.har")
|
||||
```
|
||||
|
||||
```csharp
|
||||
// Either use a matching response from the HAR,
|
||||
// or abort the request if nothing matches.
|
||||
var context = await Browser.NewContextAsync(new () {
|
||||
HarPath = "example.har"
|
||||
});
|
||||
var page = await context.NewPageAsync();
|
||||
await page.GotoAsync("https://example.com");
|
||||
await context.RouteFromHARAsync("example.har");
|
||||
```
|
||||
|
||||
HAR replay matches URL and HTTP method strictly. For POST requests, it also matches POST payloads strictly. If multiple recordings match a request, the one with the most matching headers is picked. An entry resulting in a redirect will be followed automatically.
|
||||
|
||||
Similar to when recording, if given HAR file name ends with `.zip`, it is considered an archive containing the HAR file along with network payloads stored as separate entries. You can also extract this archive, edit payloads or HAR log manually and point to the extracted har file. All the payloads will be resolved relative to the extracted har file on the file system.
|
||||
|
||||
### API reference
|
||||
- [`method: Browser.newContext`]
|
||||
- [`method: Route.fulfill`]
|
||||
|
||||
<br/>
|
||||
|
||||
## WebSockets
|
||||
|
|
|
|||
|
|
@ -5,6 +5,124 @@ title: "Release notes"
|
|||
|
||||
<!-- TOC -->
|
||||
|
||||
## Version 1.23
|
||||
|
||||
### API Testing
|
||||
|
||||
Playwright for .NET 1.23 introduces new [API Testing](./api/class-apirequestcontext) that lets you send requests to the server directly from .NET!
|
||||
Now you can:
|
||||
|
||||
- test your server API
|
||||
- prepare server side state before visiting the web application in a test
|
||||
- validate server side post-conditions after running some actions in the browser
|
||||
|
||||
To do a request on behalf of Playwright's Page, use **new [`property: Page.request`] API**:
|
||||
|
||||
```csharp
|
||||
// Do a GET request on behalf of page
|
||||
var response = await Page.APIRequest.GetAsync("http://example.com/foo.json");
|
||||
Console.WriteLine(response.Status);
|
||||
Console.WriteLine(response.StatusText);
|
||||
Console.WriteLine(response.Ok);
|
||||
Console.WriteLine(response.Headers["Content-Type"]);
|
||||
Console.WriteLine(await response.TextAsync());
|
||||
Console.WriteLine((await response.JsonAsync())?.GetProperty("foo").GetString());
|
||||
```
|
||||
|
||||
Read more about it in our [API testing guide](./api-testing).
|
||||
|
||||
### Network Replay
|
||||
|
||||
Now you can record network traffic into a HAR file and re-use this traffic in your tests.
|
||||
|
||||
To record network into HAR file:
|
||||
|
||||
```bash
|
||||
pwsh bin\Debug\netX\playwright.ps1 open --save-har=example.har --save-har-glob="**/api/**" https://example.com
|
||||
```
|
||||
|
||||
Alternatively, you can record HAR programmatically:
|
||||
|
||||
```csharp
|
||||
var context = await browser.NewContextAsync(new ()
|
||||
{
|
||||
RecordHarPath = harPath,
|
||||
RecordHarUrlFilterString = "**/api/**",
|
||||
});
|
||||
|
||||
// ... Perform actions ...
|
||||
|
||||
// Close context to ensure HAR is saved to disk.
|
||||
context.CloseAsync();
|
||||
```
|
||||
|
||||
Use the new methods [`method: Page.routeFromHAR`] or [`method: BrowserContext.routeFromHAR`] to serve matching responses from the [HAR](http://www.softwareishard.com/blog/har-12-spec/) file:
|
||||
|
||||
|
||||
```csharp
|
||||
await context.RouteFromHARAsync("example.har");
|
||||
```
|
||||
|
||||
Read more in [our documentation](./network#record-and-replay-requests).
|
||||
|
||||
|
||||
### Advanced Routing
|
||||
|
||||
You can now use [`method: Route.fallback`] to defer routing to other handlers.
|
||||
|
||||
Consider the following example:
|
||||
|
||||
```csharp
|
||||
// Remove a header from all requests.
|
||||
await page.RouteAsync("**/*", async route =>
|
||||
{
|
||||
var headers = route.Request.Headers;
|
||||
headers.Remove("X-Secret");
|
||||
await route.ContinueAsync(new () { Headers = headers });
|
||||
});
|
||||
|
||||
// Abort all images.
|
||||
await page.RouteAsync("**/*", async route =>
|
||||
{
|
||||
if (route.Request.ResourceType == "image")
|
||||
{
|
||||
await route.AbortAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
await route.FallbackAsync();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Note that the new methods [`method: Page.routeFromHAR`] and [`method: BrowserContext.routeFromHAR`] also participate in routing and could be deferred to.
|
||||
|
||||
### Web-First Assertions Update
|
||||
|
||||
* New method [`method: LocatorAssertions.toHaveValues`] that asserts all selected values of `<select multiple>` element.
|
||||
* Methods [`method: LocatorAssertions.toContainText`] and [`method: LocatorAssertions.toHaveText`] now accept `ignoreCase` option.
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
* If there's a service worker that's in your way, you can now easily disable it with a new context option `serviceWorkers`:
|
||||
```csharp
|
||||
var context = await Browser.NewContextAsync(new()
|
||||
{
|
||||
ServiceWorkers = ServiceWorkerPolicy.Block
|
||||
});
|
||||
```
|
||||
* Using `.zip` path for `recordHar` context option automatically zips the resulting HAR:
|
||||
```csharp
|
||||
var context = await Browser.NewContextAsync(new() { RecordHarPath = "example.har.zip" });
|
||||
```
|
||||
* If you intend to edit HAR by hand, consider using the `"minimal"` HAR recording mode
|
||||
that only records information that is essential for replaying:
|
||||
```csharp
|
||||
var context = await Browser.NewContextAsync(new() { RecordHarPath = "example.har", RecordHarMode = HarMode.Minimal });
|
||||
```
|
||||
* Playwright now runs on Ubuntu 22 amd64 and Ubuntu 22 arm64.
|
||||
* Playwright for .NET now supports **linux-arm64** and provides a **arm64 Ubuntu 20.04 Docker image** for it.
|
||||
|
||||
## Version 1.22
|
||||
|
||||
### Highlights
|
||||
|
|
|
|||
|
|
@ -5,6 +5,93 @@ title: "Release notes"
|
|||
|
||||
<!-- TOC -->
|
||||
|
||||
## Version 1.23
|
||||
|
||||
### Network Replay
|
||||
|
||||
Now you can record network traffic into a HAR file and re-use this traffic in your tests.
|
||||
|
||||
To record network into HAR file:
|
||||
|
||||
```bash
|
||||
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="open --save-har=example.har --save-har-glob='**/api/**' https://example.com"
|
||||
```
|
||||
|
||||
Alternatively, you can record HAR programmatically:
|
||||
|
||||
```java
|
||||
BrowserContext context = browser.newContext(new Browser.NewContextOptions()
|
||||
.setRecordHarPath(Paths.get("example.har"))
|
||||
.setRecordHarUrlFilter("**/api/**"));
|
||||
|
||||
// ... Perform actions ...
|
||||
|
||||
// Close context to ensure HAR is saved to disk.
|
||||
context.close();
|
||||
```
|
||||
|
||||
Use the new methods [`method: Page.routeFromHAR`] or [`method: BrowserContext.routeFromHAR`] to serve matching responses from the [HAR](http://www.softwareishard.com/blog/har-12-spec/) file:
|
||||
|
||||
|
||||
```java
|
||||
context.routeFromHAR(Paths.get("example.har"));
|
||||
```
|
||||
|
||||
Read more in [our documentation](./network#record-and-replay-requests).
|
||||
|
||||
|
||||
### Advanced Routing
|
||||
|
||||
You can now use [`method: Route.fallback`] to defer routing to other handlers.
|
||||
|
||||
Consider the following example:
|
||||
|
||||
```java
|
||||
// Remove a header from all requests.
|
||||
page.route("**/*", route -> {
|
||||
Map<String, String> headers = new HashMap<>(route.request().headers());
|
||||
headers.remove("X-Secret");
|
||||
route.resume(new Route.ResumeOptions().setHeaders(headers));
|
||||
});
|
||||
|
||||
// Abort all images.
|
||||
page.route("**/*", route -> {
|
||||
if ("image".equals(route.request().resourceType()))
|
||||
route.abort();
|
||||
else
|
||||
route.fallback();
|
||||
});
|
||||
```
|
||||
|
||||
Note that the new methods [`method: Page.routeFromHAR`] and [`method: BrowserContext.routeFromHAR`] also participate in routing and could be deferred to.
|
||||
|
||||
### Web-First Assertions Update
|
||||
|
||||
* New method [`method: LocatorAssertions.toHaveValues`] that asserts all selected values of `<select multiple>` element.
|
||||
* Methods [`method: LocatorAssertions.toContainText`] and [`method: LocatorAssertions.toHaveText`] now accept `ignoreCase` option.
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
* If there's a service worker that's in your way, you can now easily disable it with a new context option `serviceWorkers`:
|
||||
```java
|
||||
BrowserContext context = browser.newContext(new Browser.NewContextOptions()
|
||||
.setServiceWorkers(ServiceWorkerPolicy.BLOCK));
|
||||
```
|
||||
* Using `.zip` path for `recordHar` context option automatically zips the resulting HAR:
|
||||
```java
|
||||
BrowserContext context = browser.newContext(new Browser.NewContextOptions()
|
||||
.setRecordHarPath(Paths.get("example.har.zip")));
|
||||
```
|
||||
* If you intend to edit HAR by hand, consider using the `"minimal"` HAR recording mode
|
||||
that only records information that is essential for replaying:
|
||||
```java
|
||||
BrowserContext context = browser.newContext(new Browser.NewContextOptions()
|
||||
.setRecordHarPath(Paths.get("example.har"))
|
||||
.setRecordHarMode(HarMode.MINIMAL));
|
||||
```
|
||||
* Playwright now runs on Ubuntu 22 amd64 and Ubuntu 22 arm64.
|
||||
|
||||
|
||||
## Version 1.22
|
||||
|
||||
### Highlights
|
||||
|
|
|
|||
|
|
@ -5,6 +5,118 @@ title: "Release notes"
|
|||
|
||||
<!-- TOC -->
|
||||
|
||||
## Version 1.23
|
||||
|
||||
### Network Replay
|
||||
|
||||
Now you can record network traffic into a HAR file and re-use this traffic in your tests.
|
||||
|
||||
To record network into HAR file:
|
||||
|
||||
```bash
|
||||
npx playwright open --save-har=github.har.zip https://github.com/microsoft
|
||||
```
|
||||
|
||||
Alternatively, you can record HAR programmatically:
|
||||
|
||||
```ts
|
||||
const context = await browser.newContext({
|
||||
recordHar: { path: 'github.har.zip' }
|
||||
});
|
||||
// ... do stuff ...
|
||||
await context.close();
|
||||
```
|
||||
|
||||
Use the new methods [`method: Page.routeFromHAR`] or [`method: BrowserContext.routeFromHAR`] to serve matching responses from the [HAR](http://www.softwareishard.com/blog/har-12-spec/) file:
|
||||
|
||||
|
||||
```ts
|
||||
await context.routeFromHAR('github.har.zip');
|
||||
```
|
||||
|
||||
Read more in [our documentation](./network#record-and-replay-requests).
|
||||
|
||||
|
||||
### Advanced Routing
|
||||
|
||||
You can now use [`method: Route.fallback`] to defer routing to other handlers.
|
||||
|
||||
Consider the following example:
|
||||
|
||||
```ts
|
||||
// Remove a header from all requests.
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.route('**/*', async route => {
|
||||
const headers = await route.request().allHeaders();
|
||||
delete headers['if-none-match'];
|
||||
route.fallback({ headers });
|
||||
});
|
||||
});
|
||||
|
||||
test('should work', async ({ page }) => {
|
||||
await page.route('**/*', route => {
|
||||
if (route.request().resourceType() === 'image')
|
||||
route.abort();
|
||||
else
|
||||
route.fallback();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Note that the new methods [`method: Page.routeFromHAR`] and [`method: BrowserContext.routeFromHAR`] also participate in routing and could be deferred to.
|
||||
|
||||
### Web-First Assertions Update
|
||||
|
||||
* New method [`method: LocatorAssertions.toHaveValues`] that asserts all selected values of `<select multiple>` element.
|
||||
* Methods [`method: LocatorAssertions.toContainText`] and [`method: LocatorAssertions.toHaveText`] now accept `ignoreCase` option.
|
||||
|
||||
### Component Tests Update
|
||||
|
||||
* Support for Vue2 via the [`@playwright/experimental-ct-vue2`](https://www.npmjs.com/package/@playwright/experimental-ct-vue2) package.
|
||||
* Support for component tests for [create-react-app](https://www.npmjs.com/package/create-react-app) with components in `.js` files.
|
||||
|
||||
Read more about [component testing with Playwright](./test-components).
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
* If there's a service worker that's in your way, you can now easily disable it with a new context option `serviceWorkers`:
|
||||
```ts
|
||||
// playwright.config.ts
|
||||
export default {
|
||||
use: {
|
||||
serviceWorkers: 'block',
|
||||
}
|
||||
}
|
||||
```
|
||||
* Using `.zip` path for `recordHar` context option automatically zips the resulting HAR:
|
||||
```ts
|
||||
const context = await browser.newContext({
|
||||
recordHar: {
|
||||
path: 'github.har.zip',
|
||||
}
|
||||
});
|
||||
```
|
||||
* If you intend to edit HAR by hand, consider using the `"minimal"` HAR recording mode
|
||||
that only records information that is essential for replaying:
|
||||
```ts
|
||||
const context = await browser.newContext({
|
||||
recordHar: {
|
||||
path: 'github.har',
|
||||
mode: 'minimal',
|
||||
}
|
||||
});
|
||||
```
|
||||
* Playwright now runs on Ubuntu 22 amd64 and Ubuntu 22 arm64. We also publish new docker image `mcr.microsoft.com/playwright:v1.23.4-jammy`.
|
||||
|
||||
### ⚠️ Breaking Changes ⚠️
|
||||
|
||||
WebServer is now considered "ready" if request to the specified port has any of the following HTTP status codes:
|
||||
|
||||
* `200-299`
|
||||
* `300-399` (new)
|
||||
* `400`, `401`, `402`, `403` (new)
|
||||
|
||||
|
||||
## Version 1.22
|
||||
|
||||
### Highlights
|
||||
|
|
|
|||
|
|
@ -5,6 +5,137 @@ title: "Release notes"
|
|||
|
||||
<!-- TOC -->
|
||||
|
||||
## Version 1.23
|
||||
|
||||
### Network Replay
|
||||
|
||||
Now you can record network traffic into a HAR file and re-use this traffic in your tests.
|
||||
|
||||
To record network into HAR file:
|
||||
|
||||
```bash
|
||||
npx playwright open --save-har=github.har.zip https://github.com/microsoft
|
||||
```
|
||||
|
||||
Alternatively, you can record HAR programmatically:
|
||||
|
||||
```python async
|
||||
context = await browser.new_context(record_har_path="github.har.zip")
|
||||
# ... do stuff ...
|
||||
await context.close()
|
||||
```
|
||||
|
||||
```python sync
|
||||
context = browser.new_context(record_har_path="github.har.zip")
|
||||
# ... do stuff ...
|
||||
context.close()
|
||||
```
|
||||
|
||||
Use the new methods [`method: Page.routeFromHAR`] or [`method: BrowserContext.routeFromHAR`] to serve matching responses from the [HAR](http://www.softwareishard.com/blog/har-12-spec/) file:
|
||||
|
||||
|
||||
```python async
|
||||
await context.route_from_har("github.har.zip")
|
||||
```
|
||||
|
||||
```python sync
|
||||
context.route_from_har("github.har.zip")
|
||||
```
|
||||
|
||||
Read more in [our documentation](./network#record-and-replay-requests).
|
||||
|
||||
|
||||
### Advanced Routing
|
||||
|
||||
You can now use [`method: Route.fallback`] to defer routing to other handlers.
|
||||
|
||||
Consider the following example:
|
||||
|
||||
```python async
|
||||
# Remove a header from all requests
|
||||
async def remove_header_handler(route: Route) -> None:
|
||||
headers = await route.request.all_headers()
|
||||
if "if-none-match" in headers:
|
||||
del headers["if-none-match"]
|
||||
await route.fallback(headers=headers)
|
||||
|
||||
await page.route("**/*", remove_header_handler)
|
||||
|
||||
# Abort all images
|
||||
async def abort_images_handler(route: Route) -> None:
|
||||
if route.request.resource_type == "image":
|
||||
await route.abort()
|
||||
else:
|
||||
await route.fallback()
|
||||
|
||||
await page.route("**/*", abort_images_handler)
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Remove a header from all requests
|
||||
def remove_header_handler(route: Route) -> None:
|
||||
headers = route.request.all_headers()
|
||||
if "if-none-match" in headers:
|
||||
del headers["if-none-match"]
|
||||
route.fallback(headers=headers)
|
||||
|
||||
page.route("**/*", remove_header_handler)
|
||||
|
||||
# Abort all images
|
||||
def abort_images_handler(route: Route) -> None:
|
||||
if route.request.resource_type == "image":
|
||||
route.abort()
|
||||
else:
|
||||
route.fallback()
|
||||
|
||||
page.route("**/*", abort_images_handler)
|
||||
```
|
||||
|
||||
Note that the new methods [`method: Page.routeFromHAR`] and [`method: BrowserContext.routeFromHAR`] also participate in routing and could be deferred to.
|
||||
|
||||
### Web-First Assertions Update
|
||||
|
||||
* New method [`method: LocatorAssertions.toHaveValues`] that asserts all selected values of `<select multiple>` element.
|
||||
* Methods [`method: LocatorAssertions.toContainText`] and [`method: LocatorAssertions.toHaveText`] now accept `ignore_case` option.
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
* If there's a service worker that's in your way, you can now easily disable it with a new context option `service_workers`:
|
||||
|
||||
```python async
|
||||
context = await browser.new_context(service_workers="block")
|
||||
page = await context.new_page()
|
||||
```
|
||||
|
||||
```python sync
|
||||
context = browser.new_context(service_workers="block")
|
||||
page = context.new_page()
|
||||
```
|
||||
|
||||
* Using `.zip` path for `recordHar` context option automatically zips the resulting HAR:
|
||||
|
||||
```python async
|
||||
context = await browser.new_context(record_har_path="github.har.zip")
|
||||
```
|
||||
|
||||
```python sync
|
||||
context = browser.new_context(record_har_path="github.har.zip")
|
||||
```
|
||||
|
||||
* If you intend to edit HAR by hand, consider using the `"minimal"` HAR recording mode
|
||||
that only records information that is essential for replaying:
|
||||
|
||||
```python async
|
||||
context = await browser.new_context(record_har_mode="minimal", record_har_path="har.har")
|
||||
```
|
||||
|
||||
```python sync
|
||||
context = browser.new_context(record_har_mode="minimal", record_har_path="har.har")
|
||||
```
|
||||
|
||||
* Playwright now runs on Ubuntu 22 amd64 and Ubuntu 22 arm64.
|
||||
|
||||
|
||||
## Version 1.22
|
||||
|
||||
### Highlights
|
||||
|
|
|
|||
|
|
@ -129,8 +129,6 @@ Options used to create the context, as passed to [`method: Browser.newContext`].
|
|||
|
||||
## property: TestOptions.geolocation = %%-context-option-geolocation-%%
|
||||
|
||||
## property: TestOptions.har = %%-js-context-option-har-%%
|
||||
|
||||
## property: TestOptions.hasTouch = %%-context-option-hastouch-%%
|
||||
|
||||
## property: TestOptions.headless = %%-browser-option-headless-%%
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ The snapshot name `example-test-1-chromium-darwin.png` consists of a few parts:
|
|||
If you are not on the same operating system as your CI system, you can use Docker to generate/update the screenshots:
|
||||
|
||||
```bash
|
||||
docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.23.0-focal /bin/bash
|
||||
docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.23.4-focal /bin/bash
|
||||
npm install
|
||||
npx playwright test --update-snapshots
|
||||
```
|
||||
|
|
|
|||
60
package-lock.json
generated
60
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "playwright-internal",
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "playwright-internal",
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"license": "Apache-2.0",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
|
|
@ -6459,11 +6459,11 @@
|
|||
"version": "0.0.0"
|
||||
},
|
||||
"packages/playwright": {
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.23.0-next"
|
||||
"playwright-core": "1.23.4"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
|
|
@ -6473,11 +6473,11 @@
|
|||
}
|
||||
},
|
||||
"packages/playwright-chromium": {
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.23.0-next"
|
||||
"playwright-core": "1.23.4"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
|
|
@ -6487,7 +6487,7 @@
|
|||
}
|
||||
},
|
||||
"packages/playwright-core": {
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
|
|
@ -6498,10 +6498,10 @@
|
|||
},
|
||||
"packages/playwright-ct-react": {
|
||||
"name": "@playwright/experimental-ct-react",
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@playwright/test": "1.23.0-next",
|
||||
"@playwright/test": "1.23.4",
|
||||
"@vitejs/plugin-react": "^1.0.7",
|
||||
"vite": "^2.9.5"
|
||||
},
|
||||
|
|
@ -6511,10 +6511,10 @@
|
|||
},
|
||||
"packages/playwright-ct-svelte": {
|
||||
"name": "@playwright/experimental-ct-svelte",
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@playwright/test": "1.23.0-next",
|
||||
"@playwright/test": "1.23.4",
|
||||
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.30",
|
||||
"vite": "^2.9.5"
|
||||
},
|
||||
|
|
@ -6524,10 +6524,10 @@
|
|||
},
|
||||
"packages/playwright-ct-vue": {
|
||||
"name": "@playwright/experimental-ct-vue",
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@playwright/test": "1.23.0-next",
|
||||
"@playwright/test": "1.23.4",
|
||||
"@vitejs/plugin-vue": "^2.3.1",
|
||||
"vite": "^2.9.5"
|
||||
},
|
||||
|
|
@ -6575,10 +6575,10 @@
|
|||
},
|
||||
"packages/playwright-ct-vue2": {
|
||||
"name": "@playwright/experimental-ct-vue2",
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@playwright/test": "1.23.0-next",
|
||||
"@playwright/test": "1.23.4",
|
||||
"vite": "^2.9.5",
|
||||
"vite-plugin-vue2": "^2.0.1"
|
||||
},
|
||||
|
|
@ -6590,11 +6590,11 @@
|
|||
}
|
||||
},
|
||||
"packages/playwright-firefox": {
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.23.0-next"
|
||||
"playwright-core": "1.23.4"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
|
|
@ -6605,11 +6605,11 @@
|
|||
},
|
||||
"packages/playwright-test": {
|
||||
"name": "@playwright/test",
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"playwright-core": "1.23.0-next"
|
||||
"playwright-core": "1.23.4"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
|
|
@ -6619,11 +6619,11 @@
|
|||
}
|
||||
},
|
||||
"packages/playwright-webkit": {
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.23.0-next"
|
||||
"playwright-core": "1.23.4"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
|
|
@ -7437,7 +7437,7 @@
|
|||
"@playwright/experimental-ct-react": {
|
||||
"version": "file:packages/playwright-ct-react",
|
||||
"requires": {
|
||||
"@playwright/test": "1.23.0-next",
|
||||
"@playwright/test": "1.23.4",
|
||||
"@vitejs/plugin-react": "^1.0.7",
|
||||
"vite": "^2.9.5"
|
||||
}
|
||||
|
|
@ -7445,7 +7445,7 @@
|
|||
"@playwright/experimental-ct-svelte": {
|
||||
"version": "file:packages/playwright-ct-svelte",
|
||||
"requires": {
|
||||
"@playwright/test": "1.23.0-next",
|
||||
"@playwright/test": "1.23.4",
|
||||
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.30",
|
||||
"vite": "^2.9.5"
|
||||
}
|
||||
|
|
@ -7453,7 +7453,7 @@
|
|||
"@playwright/experimental-ct-vue": {
|
||||
"version": "file:packages/playwright-ct-vue",
|
||||
"requires": {
|
||||
"@playwright/test": "1.23.0-next",
|
||||
"@playwright/test": "1.23.4",
|
||||
"@vitejs/plugin-vue": "^2.3.1",
|
||||
"vite": "^2.9.5"
|
||||
},
|
||||
|
|
@ -7492,7 +7492,7 @@
|
|||
"@playwright/experimental-ct-vue2": {
|
||||
"version": "file:packages/playwright-ct-vue2",
|
||||
"requires": {
|
||||
"@playwright/test": "1.23.0-next",
|
||||
"@playwright/test": "1.23.4",
|
||||
"vite": "^2.9.5",
|
||||
"vite-plugin-vue2": "^2.0.1",
|
||||
"vue": "^2.6.14"
|
||||
|
|
@ -7502,7 +7502,7 @@
|
|||
"version": "file:packages/playwright-test",
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"playwright-core": "1.23.0-next"
|
||||
"playwright-core": "1.23.4"
|
||||
}
|
||||
},
|
||||
"@rollup/pluginutils": {
|
||||
|
|
@ -10311,13 +10311,13 @@
|
|||
"playwright": {
|
||||
"version": "file:packages/playwright",
|
||||
"requires": {
|
||||
"playwright-core": "1.23.0-next"
|
||||
"playwright-core": "1.23.4"
|
||||
}
|
||||
},
|
||||
"playwright-chromium": {
|
||||
"version": "file:packages/playwright-chromium",
|
||||
"requires": {
|
||||
"playwright-core": "1.23.0-next"
|
||||
"playwright-core": "1.23.4"
|
||||
}
|
||||
},
|
||||
"playwright-core": {
|
||||
|
|
@ -10326,13 +10326,13 @@
|
|||
"playwright-firefox": {
|
||||
"version": "file:packages/playwright-firefox",
|
||||
"requires": {
|
||||
"playwright-core": "1.23.0-next"
|
||||
"playwright-core": "1.23.4"
|
||||
}
|
||||
},
|
||||
"playwright-webkit": {
|
||||
"version": "file:packages/playwright-webkit",
|
||||
"requires": {
|
||||
"playwright-core": "1.23.0-next"
|
||||
"playwright-core": "1.23.4"
|
||||
}
|
||||
},
|
||||
"postcss": {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "playwright-internal",
|
||||
"private": true,
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"description": "A high-level API to automate web browsers",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "playwright-chromium",
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"description": "A high-level API to automate Chromium",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
@ -27,6 +27,6 @@
|
|||
"install": "node install.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"playwright-core": "1.23.0-next"
|
||||
"playwright-core": "1.23.4"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@
|
|||
"browsers": [
|
||||
{
|
||||
"name": "chromium",
|
||||
"revision": "1011",
|
||||
"revision": "1012",
|
||||
"installByDefault": true,
|
||||
"browserVersion": "103.0.5060.53"
|
||||
"browserVersion": "104.0.5112.20"
|
||||
},
|
||||
{
|
||||
"name": "chromium-with-symbols",
|
||||
"revision": "1011",
|
||||
"revision": "1012",
|
||||
"installByDefault": false,
|
||||
"browserVersion": "103.0.5060.53"
|
||||
"browserVersion": "104.0.5112.20"
|
||||
},
|
||||
{
|
||||
"name": "chromium-tip-of-tree",
|
||||
|
|
@ -33,7 +33,7 @@
|
|||
},
|
||||
{
|
||||
"name": "webkit",
|
||||
"revision": "1666",
|
||||
"revision": "1668",
|
||||
"installByDefault": true,
|
||||
"revisionOverrides": {
|
||||
"mac10.14": "1446",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "playwright-core",
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"description": "A high-level API to automate web browsers",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
@ -38,7 +38,6 @@
|
|||
"./lib/server": "./lib/server/index.js",
|
||||
"./lib/utilsBundle": "./lib/utilsBundle.js",
|
||||
"./lib/zipBundle": "./lib/zipBundle.js",
|
||||
"./types/har": "./types/har.d.ts",
|
||||
"./types/protocol": "./types/protocol.d.ts",
|
||||
"./types/structs": "./types/structs.d.ts"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -468,7 +468,7 @@ async function launchContext(options: Options, headless: boolean, executablePath
|
|||
// HAR
|
||||
|
||||
if (options.saveHar) {
|
||||
contextOptions.recordHar = { path: path.resolve(process.cwd(), options.saveHar) };
|
||||
contextOptions.recordHar = { path: path.resolve(process.cwd(), options.saveHar), mode: 'minimal' };
|
||||
if (options.saveHarGlob)
|
||||
contextOptions.recordHar.urlFilter = options.saveHarGlob;
|
||||
contextOptions.serviceWorkers = 'block';
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import { isSafeCloseError, kBrowserClosedError } from '../common/errors';
|
|||
import type * as api from '../../types/types';
|
||||
import { CDPSession } from './cdpSession';
|
||||
import type { BrowserType } from './browserType';
|
||||
import { HarRouter } from './harRouter';
|
||||
|
||||
export class Browser extends ChannelOwner<channels.BrowserChannel> implements api.Browser {
|
||||
readonly _contexts = new Set<BrowserContext>();
|
||||
|
|
@ -61,14 +60,12 @@ export class Browser extends ChannelOwner<channels.BrowserChannel> implements ap
|
|||
|
||||
async newContext(options: BrowserContextOptions = {}): Promise<BrowserContext> {
|
||||
options = { ...this._browserType._defaultContextOptions, ...options };
|
||||
const harRouter = options.har ? await HarRouter.create(this._connection.localUtils(), options.har) : null;
|
||||
const contextOptions = await prepareBrowserContextParams(options);
|
||||
const context = BrowserContext.from((await this._channel.newContext(contextOptions)).context);
|
||||
context._options = contextOptions;
|
||||
this._contexts.add(context);
|
||||
context._logger = options.logger || this._logger;
|
||||
context._setBrowserType(this._browserType);
|
||||
harRouter?.addRoute(context);
|
||||
await this._browserType._onDidCreateContext?.(context);
|
||||
return context;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import { Artifact } from './artifact';
|
|||
import { APIRequestContext } from './fetch';
|
||||
import { createInstrumentation } from './clientInstrumentation';
|
||||
import { rewriteErrorMessage } from '../utils/stackTrace';
|
||||
import { HarRouter } from './harRouter';
|
||||
|
||||
export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel> implements api.BrowserContext {
|
||||
_pages = new Set<Page>();
|
||||
|
|
@ -57,6 +58,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
|||
readonly _backgroundPages = new Set<Page>();
|
||||
readonly _serviceWorkers = new Set<Worker>();
|
||||
readonly _isChromium: boolean;
|
||||
private _harRecorders = new Map<string, { path: string, content: 'embed' | 'attach' | 'omit' | undefined }>();
|
||||
|
||||
static from(context: channels.BrowserContextChannel): BrowserContext {
|
||||
return (context as any)._object;
|
||||
|
|
@ -99,6 +101,8 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
|||
_setBrowserType(browserType: BrowserType) {
|
||||
this._browserType = browserType;
|
||||
browserType._contexts.add(this);
|
||||
if (this._options.recordHar)
|
||||
this._harRecorders.set('', { path: this._options.recordHar.path, content: this._options.recordHar.content });
|
||||
}
|
||||
|
||||
private _onPage(page: Page): void {
|
||||
|
|
@ -144,8 +148,10 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
|||
}
|
||||
|
||||
async _onRoute(route: network.Route, request: network.Request) {
|
||||
const routeHandlers = this._routes.filter(r => r.matches(request.url()));
|
||||
const routeHandlers = this._routes.slice();
|
||||
for (const routeHandler of routeHandlers) {
|
||||
if (!routeHandler.matches(request.url()))
|
||||
continue;
|
||||
if (routeHandler.willExpire())
|
||||
this._routes.splice(this._routes.indexOf(routeHandler), 1);
|
||||
const handled = await routeHandler.handle(route, request);
|
||||
|
|
@ -267,6 +273,28 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
|||
await this._channel.setNetworkInterceptionEnabled({ enabled: true });
|
||||
}
|
||||
|
||||
async _recordIntoHAR(har: string, page: Page | null, options: { url?: string | RegExp, notFound?: 'abort' | 'fallback', update?: boolean } = {}): Promise<void> {
|
||||
const { harId } = await this._channel.harStart({
|
||||
page: page?._channel,
|
||||
options: prepareRecordHarOptions({
|
||||
path: har,
|
||||
content: 'attach',
|
||||
mode: 'minimal',
|
||||
urlFilter: options.url
|
||||
})!
|
||||
});
|
||||
this._harRecorders.set(harId, { path: har, content: 'attach' });
|
||||
}
|
||||
|
||||
async routeFromHAR(har: string, options: { url?: string | RegExp, notFound?: 'abort' | 'fallback', update?: boolean } = {}): Promise<void> {
|
||||
if (options.update) {
|
||||
await this._recordIntoHAR(har, null, options);
|
||||
return;
|
||||
}
|
||||
const harRouter = await HarRouter.create(this._connection.localUtils(), har, options.notFound || 'abort', { urlMatch: options.url });
|
||||
harRouter.addContextRoute(this);
|
||||
}
|
||||
|
||||
async unroute(url: URLMatch, handler?: network.RouteHandlerCallback): Promise<void> {
|
||||
this._routes = this._routes.filter(route => route.url !== url || (handler && route.handler !== handler));
|
||||
if (!this._routes.length)
|
||||
|
|
@ -332,10 +360,18 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
|
|||
try {
|
||||
await this._wrapApiCall(async () => {
|
||||
await this._browserType?._onWillCloseContext?.(this);
|
||||
if (this._options.recordHar) {
|
||||
const har = await this._channel.harExport();
|
||||
for (const [harId, harParams] of this._harRecorders) {
|
||||
const har = await this._channel.harExport({ harId });
|
||||
const artifact = Artifact.from(har.artifact);
|
||||
await artifact.saveAs(this._options.recordHar.path);
|
||||
// Server side will compress artifact if content is attach or if file is .zip.
|
||||
const isCompressed = harParams.content === 'attach' || harParams.path.endsWith('.zip');
|
||||
const needCompressed = harParams.path.endsWith('.zip');
|
||||
if (isCompressed && !needCompressed) {
|
||||
await artifact.saveAs(harParams.path + '.tmp');
|
||||
await this._connection.localUtils()._channel.harUnzip({ zipFile: harParams.path + '.tmp', harFile: harParams.path });
|
||||
} else {
|
||||
await artifact.saveAs(harParams.path);
|
||||
}
|
||||
await artifact.delete();
|
||||
}
|
||||
}, true);
|
||||
|
|
@ -387,6 +423,7 @@ function prepareRecordHarOptions(options: BrowserContextOptions['recordHar']): c
|
|||
urlGlob: isString(options.urlFilter) ? options.urlFilter : undefined,
|
||||
urlRegexSource: isRegExp(options.urlFilter) ? options.urlFilter.source : undefined,
|
||||
urlRegexFlags: isRegExp(options.urlFilter) ? options.urlFilter.flags : undefined,
|
||||
mode: options.mode
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ import type * as api from '../../types/types';
|
|||
import { kBrowserClosedError } from '../common/errors';
|
||||
import { raceAgainstTimeout } from '../utils/timeoutRunner';
|
||||
import type { Playwright } from './playwright';
|
||||
import { HarRouter } from './harRouter';
|
||||
|
||||
export interface BrowserServerLauncher {
|
||||
launchServer(options?: LaunchServerOptions): Promise<api.BrowserServer>;
|
||||
|
|
@ -95,7 +94,6 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
|
|||
const logger = options.logger || this._defaultLaunchOptions?.logger;
|
||||
assert(!(options as any).port, 'Cannot specify a port without launching as a server.');
|
||||
options = { ...this._defaultLaunchOptions, ...this._defaultContextOptions, ...options };
|
||||
const harRouter = options.har ? await HarRouter.create(this._connection.localUtils(), options.har) : null;
|
||||
const contextParams = await prepareBrowserContextParams(options);
|
||||
const persistentParams: channels.BrowserTypeLaunchPersistentContextParams = {
|
||||
...contextParams,
|
||||
|
|
@ -110,7 +108,6 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
|
|||
context._options = contextParams;
|
||||
context._logger = logger;
|
||||
context._setBrowserType(this);
|
||||
harRouter?.addRoute(context);
|
||||
await this._onDidCreateContext?.(context);
|
||||
return context;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,9 +26,8 @@ import { zones } from '../utils/zones';
|
|||
import type { ClientInstrumentation } from './clientInstrumentation';
|
||||
import type { Connection } from './connection';
|
||||
import type { Logger } from './types';
|
||||
import { JoiningEventEmitter } from './joiningEventEmitter';
|
||||
|
||||
export abstract class ChannelOwner<T extends channels.Channel = channels.Channel> extends JoiningEventEmitter {
|
||||
export abstract class ChannelOwner<T extends channels.Channel = channels.Channel> extends EventEmitter {
|
||||
readonly _connection: Connection;
|
||||
private _parent: ChannelOwner | undefined;
|
||||
private _objects = new Map<string, ChannelOwner>();
|
||||
|
|
|
|||
|
|
@ -28,12 +28,10 @@ import { JSHandle, parseResult, serializeArgument } from './jsHandle';
|
|||
import type { Page } from './page';
|
||||
import type { Env, WaitForEventOptions, Headers, BrowserContextOptions } from './types';
|
||||
import { Waiter } from './waiter';
|
||||
import { HarRouter } from './harRouter';
|
||||
|
||||
type ElectronOptions = Omit<channels.ElectronLaunchOptions, 'env'|'extraHTTPHeaders'|'recordHar'> & {
|
||||
env?: Env,
|
||||
extraHTTPHeaders?: Headers,
|
||||
har?: BrowserContextOptions['har'],
|
||||
recordHar?: BrowserContextOptions['recordHar'],
|
||||
};
|
||||
|
||||
|
|
@ -53,10 +51,8 @@ export class Electron extends ChannelOwner<channels.ElectronChannel> implements
|
|||
...await prepareBrowserContextParams(options),
|
||||
env: envObjectToArray(options.env ? options.env : process.env),
|
||||
};
|
||||
const harRouter = options.har ? await HarRouter.create(this._connection.localUtils(), options.har) : null;
|
||||
const app = ElectronApplication.from((await this._channel.launch(params)).electronApplication);
|
||||
app._context._options = params;
|
||||
harRouter?.addRoute(app._context);
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,32 +19,34 @@ import type { BrowserContext } from './browserContext';
|
|||
import { Events } from './events';
|
||||
import type { LocalUtils } from './localUtils';
|
||||
import type { Route } from './network';
|
||||
import type { BrowserContextOptions } from './types';
|
||||
import type { URLMatch } from './types';
|
||||
import type { Page } from './page';
|
||||
|
||||
type HarOptions = NonNullable<BrowserContextOptions['har']>;
|
||||
type HarNotFoundAction = 'abort' | 'fallback';
|
||||
|
||||
export class HarRouter {
|
||||
private _pattern: string | RegExp;
|
||||
private _options: HarOptions | undefined;
|
||||
private _localUtils: LocalUtils;
|
||||
private _harId: string;
|
||||
private _notFoundAction: HarNotFoundAction;
|
||||
private _options: { urlMatch?: URLMatch; baseURL?: string; };
|
||||
|
||||
static async create(localUtils: LocalUtils, options: HarOptions): Promise<HarRouter> {
|
||||
const { harId, error } = await localUtils._channel.harOpen({ file: options.path });
|
||||
static async create(localUtils: LocalUtils, file: string, notFoundAction: HarNotFoundAction, options: { urlMatch?: URLMatch }): Promise<HarRouter> {
|
||||
const { harId, error } = await localUtils._channel.harOpen({ file });
|
||||
if (error)
|
||||
throw new Error(error);
|
||||
return new HarRouter(localUtils, harId!, options);
|
||||
return new HarRouter(localUtils, harId!, notFoundAction, options);
|
||||
}
|
||||
|
||||
constructor(localUtils: LocalUtils, harId: string, options?: HarOptions) {
|
||||
constructor(localUtils: LocalUtils, harId: string, notFoundAction: HarNotFoundAction, options: { urlMatch?: URLMatch }) {
|
||||
this._localUtils = localUtils;
|
||||
this._harId = harId;
|
||||
this._pattern = options?.urlFilter ?? /.*/;
|
||||
this._options = options;
|
||||
this._notFoundAction = notFoundAction;
|
||||
}
|
||||
|
||||
private async _handle(route: Route) {
|
||||
const request = route.request();
|
||||
|
||||
const response = await this._localUtils._channel.harLookup({
|
||||
harId: this._harId,
|
||||
url: request.url(),
|
||||
|
|
@ -56,7 +58,7 @@ export class HarRouter {
|
|||
|
||||
if (response.action === 'redirect') {
|
||||
debugLogger.log('api', `HAR: ${route.request().url()} redirected to ${response.redirectURL}`);
|
||||
await route._abort(undefined, response.redirectURL);
|
||||
await route._redirectNavigationRequest(response.redirectURL!);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -73,20 +75,24 @@ export class HarRouter {
|
|||
debugLogger.log('api', 'HAR: ' + response.message!);
|
||||
// Report the error, but fall through to the default handler.
|
||||
|
||||
if (this._options?.fallback === 'continue') {
|
||||
await route.fallback();
|
||||
if (this._notFoundAction === 'abort') {
|
||||
await route.abort();
|
||||
return;
|
||||
}
|
||||
|
||||
debugLogger.log('api', `HAR: ${route.request().method()} ${route.request().url()} aborted - no such entry in HAR file`);
|
||||
await route.abort();
|
||||
await route.fallback();
|
||||
}
|
||||
|
||||
async addRoute(context: BrowserContext) {
|
||||
await context.route(this._pattern, route => this._handle(route));
|
||||
async addContextRoute(context: BrowserContext) {
|
||||
await context.route(this._options.urlMatch || '**/*', route => this._handle(route));
|
||||
context.once(Events.BrowserContext.Close, () => this.dispose());
|
||||
}
|
||||
|
||||
async addPageRoute(page: Page) {
|
||||
await page.route(this._options.urlMatch || '**/*', route => this._handle(route));
|
||||
page.once(Events.Page.Close, () => this.dispose());
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._localUtils._channel.harClose({ harId: this._harId }).catch(() => {});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ export class JoiningEventEmitter implements EventEmitter {
|
|||
}
|
||||
|
||||
removeAllListeners(event?: string | symbol | undefined): this {
|
||||
this._emitterDelegate.removeAllListeners();
|
||||
this._emitterDelegate.removeAllListeners(event);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -119,7 +119,8 @@ export class JoiningEventEmitter implements EventEmitter {
|
|||
}
|
||||
|
||||
private _wrapper(listener: (...args: any[]) => void) {
|
||||
return (listener as any)[wrapperListener];
|
||||
// Fallback to original listener if not wrapped to ensure backwards compatibility Node.js's event emitter
|
||||
return (listener as any)[wrapperListener] ?? listener;
|
||||
}
|
||||
|
||||
private _original(wrapper: Function): Function {
|
||||
|
|
|
|||
|
|
@ -282,12 +282,14 @@ export class Route extends ChannelOwner<channels.RouteChannel> implements api.Ro
|
|||
}
|
||||
|
||||
async abort(errorCode?: string) {
|
||||
await this._abort(errorCode);
|
||||
this._checkNotHandled();
|
||||
await this._raceWithPageClose(this._channel.abort({ errorCode }));
|
||||
this._reportHandled(true);
|
||||
}
|
||||
|
||||
async _abort(errorCode?: string, redirectAbortedNavigationToUrl?: string) {
|
||||
async _redirectNavigationRequest(url: string) {
|
||||
this._checkNotHandled();
|
||||
await this._raceWithPageClose(this._channel.abort({ errorCode, redirectAbortedNavigationToUrl }));
|
||||
await this._raceWithPageClose(this._channel.redirectNavigationRequest({ url }));
|
||||
this._reportHandled(true);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ import type { APIRequestContext } from './fetch';
|
|||
import { FileChooser } from './fileChooser';
|
||||
import type { WaitForNavigationOptions } from './frame';
|
||||
import { Frame, verifyLoadState } from './frame';
|
||||
import { HarRouter } from './harRouter';
|
||||
import { Keyboard, Mouse, Touchscreen } from './input';
|
||||
import { assertMaxArguments, JSHandle, parseResult, serializeArgument } from './jsHandle';
|
||||
import type { FrameLocator, Locator, LocatorOptions } from './locator';
|
||||
|
|
@ -178,8 +179,10 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
|
|||
}
|
||||
|
||||
private async _onRoute(route: Route, request: Request) {
|
||||
const routeHandlers = this._routes.filter(r => r.matches(request.url()));
|
||||
const routeHandlers = this._routes.slice();
|
||||
for (const routeHandler of routeHandlers) {
|
||||
if (!routeHandler.matches(request.url()))
|
||||
continue;
|
||||
if (routeHandler.willExpire())
|
||||
this._routes.splice(this._routes.indexOf(routeHandler), 1);
|
||||
const handled = await routeHandler.handle(route, request);
|
||||
|
|
@ -465,6 +468,15 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
|
|||
await this._channel.setNetworkInterceptionEnabled({ enabled: true });
|
||||
}
|
||||
|
||||
async routeFromHAR(har: string, options: { url?: string | RegExp, notFound?: 'abort' | 'fallback', update?: boolean } = {}): Promise<void> {
|
||||
if (options.update) {
|
||||
await this._browserContext._recordIntoHAR(har, this, options);
|
||||
return;
|
||||
}
|
||||
const harRouter = await HarRouter.create(this._connection.localUtils(), har, options.notFound || 'abort', { urlMatch: options.url });
|
||||
harRouter.addPageRoute(this);
|
||||
}
|
||||
|
||||
async unroute(url: URLMatch, handler?: RouteHandlerCallback): Promise<void> {
|
||||
this._routes = this._routes.filter(route => route.url !== url || (handler && route.handler !== handler));
|
||||
if (!this._routes.length)
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ export type BrowserContextOptions = Omit<channels.BrowserNewContextOptions, 'vie
|
|||
path: string,
|
||||
omitContent?: boolean,
|
||||
content?: 'omit' | 'embed' | 'attach',
|
||||
mode?: 'full' | 'minimal',
|
||||
urlFilter?: string | RegExp,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -266,6 +266,7 @@ export type SerializedError = {
|
|||
export type RecordHarOptions = {
|
||||
path: string,
|
||||
content?: 'embed' | 'attach' | 'omit',
|
||||
mode?: 'full' | 'minimal',
|
||||
urlGlob?: string,
|
||||
urlRegexSource?: string,
|
||||
urlRegexFlags?: string,
|
||||
|
|
@ -381,6 +382,7 @@ export interface LocalUtilsChannel extends LocalUtilsEventTarget, Channel {
|
|||
harOpen(params: LocalUtilsHarOpenParams, metadata?: Metadata): Promise<LocalUtilsHarOpenResult>;
|
||||
harLookup(params: LocalUtilsHarLookupParams, metadata?: Metadata): Promise<LocalUtilsHarLookupResult>;
|
||||
harClose(params: LocalUtilsHarCloseParams, metadata?: Metadata): Promise<LocalUtilsHarCloseResult>;
|
||||
harUnzip(params: LocalUtilsHarUnzipParams, metadata?: Metadata): Promise<LocalUtilsHarUnzipResult>;
|
||||
}
|
||||
export type LocalUtilsZipParams = {
|
||||
zipFile: string,
|
||||
|
|
@ -426,6 +428,14 @@ export type LocalUtilsHarCloseOptions = {
|
|||
|
||||
};
|
||||
export type LocalUtilsHarCloseResult = void;
|
||||
export type LocalUtilsHarUnzipParams = {
|
||||
zipFile: string,
|
||||
harFile: string,
|
||||
};
|
||||
export type LocalUtilsHarUnzipOptions = {
|
||||
|
||||
};
|
||||
export type LocalUtilsHarUnzipResult = void;
|
||||
|
||||
export interface LocalUtilsEvents {
|
||||
}
|
||||
|
|
@ -1118,7 +1128,8 @@ export interface BrowserContextChannel extends BrowserContextEventTarget, EventT
|
|||
pause(params?: BrowserContextPauseParams, metadata?: Metadata): Promise<BrowserContextPauseResult>;
|
||||
recorderSupplementEnable(params: BrowserContextRecorderSupplementEnableParams, metadata?: Metadata): Promise<BrowserContextRecorderSupplementEnableResult>;
|
||||
newCDPSession(params: BrowserContextNewCDPSessionParams, metadata?: Metadata): Promise<BrowserContextNewCDPSessionResult>;
|
||||
harExport(params?: BrowserContextHarExportParams, metadata?: Metadata): Promise<BrowserContextHarExportResult>;
|
||||
harStart(params: BrowserContextHarStartParams, metadata?: Metadata): Promise<BrowserContextHarStartResult>;
|
||||
harExport(params: BrowserContextHarExportParams, metadata?: Metadata): Promise<BrowserContextHarExportResult>;
|
||||
createTempFile(params: BrowserContextCreateTempFileParams, metadata?: Metadata): Promise<BrowserContextCreateTempFileResult>;
|
||||
}
|
||||
export type BrowserContextBindingCallEvent = {
|
||||
|
|
@ -1324,8 +1335,22 @@ export type BrowserContextNewCDPSessionOptions = {
|
|||
export type BrowserContextNewCDPSessionResult = {
|
||||
session: CDPSessionChannel,
|
||||
};
|
||||
export type BrowserContextHarExportParams = {};
|
||||
export type BrowserContextHarExportOptions = {};
|
||||
export type BrowserContextHarStartParams = {
|
||||
page?: PageChannel,
|
||||
options: RecordHarOptions,
|
||||
};
|
||||
export type BrowserContextHarStartOptions = {
|
||||
page?: PageChannel,
|
||||
};
|
||||
export type BrowserContextHarStartResult = {
|
||||
harId: string,
|
||||
};
|
||||
export type BrowserContextHarExportParams = {
|
||||
harId?: string,
|
||||
};
|
||||
export type BrowserContextHarExportOptions = {
|
||||
harId?: string,
|
||||
};
|
||||
export type BrowserContextHarExportResult = {
|
||||
artifact: ArtifactChannel,
|
||||
};
|
||||
|
|
@ -3158,17 +3183,23 @@ export interface RouteEventTarget {
|
|||
}
|
||||
export interface RouteChannel extends RouteEventTarget, Channel {
|
||||
_type_Route: boolean;
|
||||
redirectNavigationRequest(params: RouteRedirectNavigationRequestParams, metadata?: Metadata): Promise<RouteRedirectNavigationRequestResult>;
|
||||
abort(params: RouteAbortParams, metadata?: Metadata): Promise<RouteAbortResult>;
|
||||
continue(params: RouteContinueParams, metadata?: Metadata): Promise<RouteContinueResult>;
|
||||
fulfill(params: RouteFulfillParams, metadata?: Metadata): Promise<RouteFulfillResult>;
|
||||
}
|
||||
export type RouteRedirectNavigationRequestParams = {
|
||||
url: string,
|
||||
};
|
||||
export type RouteRedirectNavigationRequestOptions = {
|
||||
|
||||
};
|
||||
export type RouteRedirectNavigationRequestResult = void;
|
||||
export type RouteAbortParams = {
|
||||
errorCode?: string,
|
||||
redirectAbortedNavigationToUrl?: string,
|
||||
};
|
||||
export type RouteAbortOptions = {
|
||||
errorCode?: string,
|
||||
redirectAbortedNavigationToUrl?: string,
|
||||
};
|
||||
export type RouteAbortResult = void;
|
||||
export type RouteContinueParams = {
|
||||
|
|
|
|||
|
|
@ -231,6 +231,11 @@ RecordHarOptions:
|
|||
- embed
|
||||
- attach
|
||||
- omit
|
||||
mode:
|
||||
type: enum?
|
||||
literals:
|
||||
- full
|
||||
- minimal
|
||||
urlGlob: string?
|
||||
urlRegexSource: string?
|
||||
urlRegexFlags: string?
|
||||
|
|
@ -515,6 +520,11 @@ LocalUtils:
|
|||
parameters:
|
||||
harId: string
|
||||
|
||||
harUnzip:
|
||||
parameters:
|
||||
zipFile: string
|
||||
harFile: string
|
||||
|
||||
Root:
|
||||
type: interface
|
||||
|
||||
|
|
@ -921,7 +931,16 @@ BrowserContext:
|
|||
returns:
|
||||
session: CDPSession
|
||||
|
||||
harStart:
|
||||
parameters:
|
||||
page: Page?
|
||||
options: RecordHarOptions
|
||||
returns:
|
||||
harId: string
|
||||
|
||||
harExport:
|
||||
parameters:
|
||||
harId: string?
|
||||
returns:
|
||||
artifact: Artifact
|
||||
|
||||
|
|
@ -2491,10 +2510,13 @@ Route:
|
|||
|
||||
commands:
|
||||
|
||||
redirectNavigationRequest:
|
||||
parameters:
|
||||
url: string
|
||||
|
||||
abort:
|
||||
parameters:
|
||||
errorCode: string?
|
||||
redirectAbortedNavigationToUrl: string?
|
||||
|
||||
continue:
|
||||
parameters:
|
||||
|
|
|
|||
|
|
@ -156,6 +156,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
|||
scheme.RecordHarOptions = tObject({
|
||||
path: tString,
|
||||
content: tOptional(tEnum(['embed', 'attach', 'omit'])),
|
||||
mode: tOptional(tEnum(['full', 'minimal'])),
|
||||
urlGlob: tOptional(tString),
|
||||
urlRegexSource: tOptional(tString),
|
||||
urlRegexFlags: tOptional(tString),
|
||||
|
|
@ -219,6 +220,10 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
|||
scheme.LocalUtilsHarCloseParams = tObject({
|
||||
harId: tString,
|
||||
});
|
||||
scheme.LocalUtilsHarUnzipParams = tObject({
|
||||
zipFile: tString,
|
||||
harFile: tString,
|
||||
});
|
||||
scheme.RootInitializeParams = tObject({
|
||||
sdkLanguage: tString,
|
||||
});
|
||||
|
|
@ -526,7 +531,13 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
|||
page: tOptional(tChannel('Page')),
|
||||
frame: tOptional(tChannel('Frame')),
|
||||
});
|
||||
scheme.BrowserContextHarExportParams = tOptional(tObject({}));
|
||||
scheme.BrowserContextHarStartParams = tObject({
|
||||
page: tOptional(tChannel('Page')),
|
||||
options: tType('RecordHarOptions'),
|
||||
});
|
||||
scheme.BrowserContextHarExportParams = tObject({
|
||||
harId: tOptional(tString),
|
||||
});
|
||||
scheme.BrowserContextCreateTempFileParams = tObject({
|
||||
name: tString,
|
||||
});
|
||||
|
|
@ -1181,9 +1192,11 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
|||
});
|
||||
scheme.RequestResponseParams = tOptional(tObject({}));
|
||||
scheme.RequestRawRequestHeadersParams = tOptional(tObject({}));
|
||||
scheme.RouteRedirectNavigationRequestParams = tObject({
|
||||
url: tString,
|
||||
});
|
||||
scheme.RouteAbortParams = tObject({
|
||||
errorCode: tOptional(tString),
|
||||
redirectAbortedNavigationToUrl: tOptional(tString),
|
||||
});
|
||||
scheme.RouteContinueParams = tObject({
|
||||
url: tOptional(tString),
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
import * as os from 'os';
|
||||
import { TimeoutSettings } from '../common/timeoutSettings';
|
||||
import { debugMode } from '../utils';
|
||||
import { createGuid, debugMode } from '../utils';
|
||||
import { mkdirIfNeeded } from '../utils/fileUtils';
|
||||
import type { Browser, BrowserOptions } from './browser';
|
||||
import type { Download } from './download';
|
||||
|
|
@ -40,6 +40,7 @@ import { HarRecorder } from './har/harRecorder';
|
|||
import { Recorder } from './recorder';
|
||||
import * as consoleApiSource from '../generated/consoleApiSource';
|
||||
import { BrowserContextAPIRequestContext } from './fetch';
|
||||
import type { Artifact } from './artifact';
|
||||
|
||||
export abstract class BrowserContext extends SdkObject {
|
||||
static Events = {
|
||||
|
|
@ -67,7 +68,7 @@ export abstract class BrowserContext extends SdkObject {
|
|||
readonly _browserContextId: string | undefined;
|
||||
private _selectors?: Selectors;
|
||||
private _origins = new Set<string>();
|
||||
readonly _harRecorder: HarRecorder | undefined;
|
||||
readonly _harRecorders = new Map<string, HarRecorder>();
|
||||
readonly tracing: Tracing;
|
||||
readonly fetchRequest: BrowserContextAPIRequestContext;
|
||||
private _customCloseHandler?: () => Promise<any>;
|
||||
|
|
@ -87,7 +88,7 @@ export abstract class BrowserContext extends SdkObject {
|
|||
this.fetchRequest = new BrowserContextAPIRequestContext(this);
|
||||
|
||||
if (this._options.recordHar)
|
||||
this._harRecorder = new HarRecorder(this, this._options.recordHar);
|
||||
this._harRecorders.set('', new HarRecorder(this, null, this._options.recordHar));
|
||||
|
||||
this.tracing = new Tracing(this, browser.options.tracesDir);
|
||||
}
|
||||
|
|
@ -316,7 +317,8 @@ export abstract class BrowserContext extends SdkObject {
|
|||
this.emit(BrowserContext.Events.BeforeClose);
|
||||
this._closedStatus = 'closing';
|
||||
|
||||
await this._harRecorder?.flush();
|
||||
for (const harRecorder of this._harRecorders.values())
|
||||
await harRecorder.flush();
|
||||
await this.tracing.flush();
|
||||
|
||||
// Cleanup.
|
||||
|
|
@ -442,6 +444,17 @@ export abstract class BrowserContext extends SdkObject {
|
|||
this.on(BrowserContext.Events.Page, installInPage);
|
||||
return Promise.all(this.pages().map(installInPage));
|
||||
}
|
||||
|
||||
async _harStart(page: Page | null, options: channels.RecordHarOptions): Promise<string> {
|
||||
const harId = createGuid();
|
||||
this._harRecorders.set(harId, new HarRecorder(this, page, options));
|
||||
return harId;
|
||||
}
|
||||
|
||||
async _harExport(harId: string | undefined): Promise<Artifact> {
|
||||
const recorder = this._harRecorders.get(harId || '')!;
|
||||
return recorder.export();
|
||||
}
|
||||
}
|
||||
|
||||
export function assertBrowserContextIsNotOwned(context: BrowserContext) {
|
||||
|
|
|
|||
|
|
@ -810,7 +810,7 @@ CORS RFC1918 enforcement.
|
|||
export type AttributionReportingIssueType = "PermissionPolicyDisabled"|"AttributionSourceUntrustworthyOrigin"|"AttributionUntrustworthyOrigin"|"InvalidHeader";
|
||||
/**
|
||||
* Details for issues around "Attribution Reporting API" usage.
|
||||
Explainer: https://github.com/WICG/conversion-measurement-api
|
||||
Explainer: https://github.com/WICG/attribution-reporting-api
|
||||
*/
|
||||
export interface AttributionReportingIssueDetails {
|
||||
violationType: AttributionReportingIssueType;
|
||||
|
|
@ -849,7 +849,7 @@ instead of "limited-quirks".
|
|||
errorType: GenericIssueErrorType;
|
||||
frameId?: Page.FrameId;
|
||||
}
|
||||
export type DeprecationIssueType = "AuthorizationCoveredByWildcard"|"CanRequestURLHTTPContainingNewline"|"ChromeLoadTimesConnectionInfo"|"ChromeLoadTimesFirstPaintAfterLoadTime"|"ChromeLoadTimesWasAlternateProtocolAvailable"|"CookieWithTruncatingChar"|"CrossOriginAccessBasedOnDocumentDomain"|"CrossOriginWindowAlert"|"CrossOriginWindowConfirm"|"CSSSelectorInternalMediaControlsOverlayCastButton"|"CustomCursorIntersectsViewport"|"DeprecationExample"|"DocumentDomainSettingWithoutOriginAgentClusterHeader"|"EventPath"|"GeolocationInsecureOrigin"|"GeolocationInsecureOriginDeprecatedNotRemoved"|"GetUserMediaInsecureOrigin"|"HostCandidateAttributeGetter"|"InsecurePrivateNetworkSubresourceRequest"|"LegacyConstraintGoogIPv6"|"LocalCSSFileExtensionRejected"|"MediaElementAudioSourceNode"|"MediaSourceAbortRemove"|"MediaSourceDurationTruncatingBuffered"|"NoSysexWebMIDIWithoutPermission"|"NotificationInsecureOrigin"|"NotificationPermissionRequestedIframe"|"ObsoleteWebRtcCipherSuite"|"PaymentRequestBasicCard"|"PaymentRequestShowWithoutGesture"|"PictureSourceSrc"|"PrefixedCancelAnimationFrame"|"PrefixedRequestAnimationFrame"|"PrefixedStorageInfo"|"PrefixedVideoDisplayingFullscreen"|"PrefixedVideoEnterFullscreen"|"PrefixedVideoEnterFullScreen"|"PrefixedVideoExitFullscreen"|"PrefixedVideoExitFullScreen"|"PrefixedVideoSupportsFullscreen"|"RangeExpand"|"RequestedSubresourceWithEmbeddedCredentials"|"RTCConstraintEnableDtlsSrtpFalse"|"RTCConstraintEnableDtlsSrtpTrue"|"RTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics"|"RTCPeerConnectionSdpSemanticsPlanB"|"RtcpMuxPolicyNegotiate"|"RTPDataChannel"|"SharedArrayBufferConstructedWithoutIsolation"|"TextToSpeech_DisallowedByAutoplay"|"V8SharedArrayBufferConstructedInExtensionWithoutIsolation"|"XHRJSONEncodingDetection"|"XMLHttpRequestSynchronousInNonWorkerOutsideBeforeUnload"|"XRSupportsSession";
|
||||
export type DeprecationIssueType = "AuthorizationCoveredByWildcard"|"CanRequestURLHTTPContainingNewline"|"ChromeLoadTimesConnectionInfo"|"ChromeLoadTimesFirstPaintAfterLoadTime"|"ChromeLoadTimesWasAlternateProtocolAvailable"|"CookieWithTruncatingChar"|"CrossOriginAccessBasedOnDocumentDomain"|"CrossOriginWindowAlert"|"CrossOriginWindowConfirm"|"CSSSelectorInternalMediaControlsOverlayCastButton"|"DeprecationExample"|"DocumentDomainSettingWithoutOriginAgentClusterHeader"|"EventPath"|"GeolocationInsecureOrigin"|"GeolocationInsecureOriginDeprecatedNotRemoved"|"GetUserMediaInsecureOrigin"|"HostCandidateAttributeGetter"|"IdentityInCanMakePaymentEvent"|"InsecurePrivateNetworkSubresourceRequest"|"LegacyConstraintGoogIPv6"|"LocalCSSFileExtensionRejected"|"MediaSourceAbortRemove"|"MediaSourceDurationTruncatingBuffered"|"NoSysexWebMIDIWithoutPermission"|"NotificationInsecureOrigin"|"NotificationPermissionRequestedIframe"|"ObsoleteWebRtcCipherSuite"|"OpenWebDatabaseInsecureContext"|"PictureSourceSrc"|"PrefixedCancelAnimationFrame"|"PrefixedRequestAnimationFrame"|"PrefixedStorageInfo"|"PrefixedVideoDisplayingFullscreen"|"PrefixedVideoEnterFullscreen"|"PrefixedVideoEnterFullScreen"|"PrefixedVideoExitFullscreen"|"PrefixedVideoExitFullScreen"|"PrefixedVideoSupportsFullscreen"|"RangeExpand"|"RequestedSubresourceWithEmbeddedCredentials"|"RTCConstraintEnableDtlsSrtpFalse"|"RTCConstraintEnableDtlsSrtpTrue"|"RTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics"|"RTCPeerConnectionSdpSemanticsPlanB"|"RtcpMuxPolicyNegotiate"|"SharedArrayBufferConstructedWithoutIsolation"|"TextToSpeech_DisallowedByAutoplay"|"V8SharedArrayBufferConstructedInExtensionWithoutIsolation"|"XHRJSONEncodingDetection"|"XMLHttpRequestSynchronousInNonWorkerOutsideBeforeUnload"|"XRSupportsSession";
|
||||
/**
|
||||
* This issue tracks information needed to print a deprecation message.
|
||||
https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/frame/third_party/blink/renderer/core/frame/deprecation/README.md
|
||||
|
|
@ -1532,6 +1532,10 @@ inspector" rules), "regular" for regular stylesheets.
|
|||
* Pseudo element type.
|
||||
*/
|
||||
pseudoType: DOM.PseudoType;
|
||||
/**
|
||||
* Pseudo element custom ident.
|
||||
*/
|
||||
pseudoIdentifier?: string;
|
||||
/**
|
||||
* Matches of CSS rules applicable to the pseudo style.
|
||||
*/
|
||||
|
|
@ -2068,6 +2072,10 @@ and additional information such as platformFontFamily and fontVariationAxes.
|
|||
* The font-stretch.
|
||||
*/
|
||||
fontStretch: string;
|
||||
/**
|
||||
* The font-display.
|
||||
*/
|
||||
fontDisplay: string;
|
||||
/**
|
||||
* The unicode-range.
|
||||
*/
|
||||
|
|
@ -2958,6 +2966,11 @@ fire DOM events for nodes known to the client.
|
|||
* Pseudo element type for this node.
|
||||
*/
|
||||
pseudoType?: PseudoType;
|
||||
/**
|
||||
* Pseudo element identifier for this node. Only present if there is a
|
||||
valid pseudoType.
|
||||
*/
|
||||
pseudoIdentifier?: string;
|
||||
/**
|
||||
* Shadow root type.
|
||||
*/
|
||||
|
|
@ -2997,6 +3010,7 @@ The property is always undefined now.
|
|||
*/
|
||||
isSVG?: boolean;
|
||||
compatibilityMode?: CompatibilityMode;
|
||||
assignedSlot?: BackendNode;
|
||||
}
|
||||
/**
|
||||
* A structure holding an RGBA color.
|
||||
|
|
@ -4706,6 +4720,11 @@ getSnapshot was true.
|
|||
* Type of a pseudo element node.
|
||||
*/
|
||||
pseudoType?: RareStringData;
|
||||
/**
|
||||
* Pseudo element identifier for this node. Only present if there is a
|
||||
valid pseudoType.
|
||||
*/
|
||||
pseudoIdentifier?: RareStringData;
|
||||
/**
|
||||
* Whether this DOM node responds to mouse clicks. This includes nodes that have had click
|
||||
event listeners attached via JavaScript as well as anchor tags that naturally navigate when
|
||||
|
|
@ -4978,12 +4997,6 @@ The final text color opacity is computed based on the opacity of all overlapping
|
|||
}
|
||||
export type setDOMStorageItemReturnValue = {
|
||||
}
|
||||
export type getStorageKeyForFrameParameters = {
|
||||
frameId: Page.FrameId;
|
||||
}
|
||||
export type getStorageKeyForFrameReturnValue = {
|
||||
storageKey: SerializedStorageKey;
|
||||
}
|
||||
}
|
||||
|
||||
export module Database {
|
||||
|
|
@ -5533,6 +5546,14 @@ on Android.
|
|||
}
|
||||
export type setDisabledImageTypesReturnValue = {
|
||||
}
|
||||
export type setHardwareConcurrencyOverrideParameters = {
|
||||
/**
|
||||
* Hardware concurrency to report
|
||||
*/
|
||||
hardwareConcurrency: number;
|
||||
}
|
||||
export type setHardwareConcurrencyOverrideReturnValue = {
|
||||
}
|
||||
/**
|
||||
* Allows overriding user agent with the given string.
|
||||
*/
|
||||
|
|
@ -8063,7 +8084,7 @@ the same request (but not for redirected requests).
|
|||
initiatorIPAddressSpace: IPAddressSpace;
|
||||
privateNetworkRequestPolicy: PrivateNetworkRequestPolicy;
|
||||
}
|
||||
export type CrossOriginOpenerPolicyValue = "SameOrigin"|"SameOriginAllowPopups"|"UnsafeNone"|"SameOriginPlusCoep"|"SameOriginAllowPopupsPlusCoep";
|
||||
export type CrossOriginOpenerPolicyValue = "SameOrigin"|"SameOriginAllowPopups"|"RestrictProperties"|"UnsafeNone"|"SameOriginPlusCoep"|"RestrictPropertiesPlusCoep";
|
||||
export interface CrossOriginOpenerPolicyStatus {
|
||||
value: CrossOriginOpenerPolicyValue;
|
||||
reportOnlyValue: CrossOriginOpenerPolicyValue;
|
||||
|
|
@ -10166,6 +10187,21 @@ Backend then generates 'inspectNodeRequested' event upon element selection.
|
|||
adFrameType: AdFrameType;
|
||||
explanations?: AdFrameExplanation[];
|
||||
}
|
||||
/**
|
||||
* Identifies the bottom-most script which caused the frame to be labelled
|
||||
as an ad.
|
||||
*/
|
||||
export interface AdScriptId {
|
||||
/**
|
||||
* Script Id of the bottom-most script which caused the frame to be labelled
|
||||
as an ad.
|
||||
*/
|
||||
scriptId: Runtime.ScriptId;
|
||||
/**
|
||||
* Id of adScriptId's debugger.
|
||||
*/
|
||||
debuggerId: Runtime.UniqueDebuggerId;
|
||||
}
|
||||
/**
|
||||
* Indicates whether the frame is a secure context and why it is the case.
|
||||
*/
|
||||
|
|
@ -10179,7 +10215,7 @@ Backend then generates 'inspectNodeRequested' event upon element selection.
|
|||
* All Permissions Policy features. This enum should match the one defined
|
||||
in third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5.
|
||||
*/
|
||||
export type PermissionsPolicyFeature = "accelerometer"|"ambient-light-sensor"|"attribution-reporting"|"autoplay"|"browsing-topics"|"camera"|"ch-dpr"|"ch-device-memory"|"ch-downlink"|"ch-ect"|"ch-prefers-color-scheme"|"ch-rtt"|"ch-save-data"|"ch-ua"|"ch-ua-arch"|"ch-ua-bitness"|"ch-ua-platform"|"ch-ua-model"|"ch-ua-mobile"|"ch-ua-full"|"ch-ua-full-version"|"ch-ua-full-version-list"|"ch-ua-platform-version"|"ch-ua-reduced"|"ch-ua-wow64"|"ch-viewport-height"|"ch-viewport-width"|"ch-width"|"clipboard-read"|"clipboard-write"|"cross-origin-isolated"|"direct-sockets"|"display-capture"|"document-domain"|"encrypted-media"|"execution-while-out-of-viewport"|"execution-while-not-rendered"|"focus-without-user-activation"|"fullscreen"|"frobulate"|"gamepad"|"geolocation"|"gyroscope"|"hid"|"idle-detection"|"interest-cohort"|"join-ad-interest-group"|"keyboard-map"|"local-fonts"|"magnetometer"|"microphone"|"midi"|"otp-credentials"|"payment"|"picture-in-picture"|"publickey-credentials-get"|"run-ad-auction"|"screen-wake-lock"|"serial"|"shared-autofill"|"storage-access-api"|"sync-xhr"|"trust-token-redemption"|"usb"|"vertical-scroll"|"web-share"|"window-placement"|"xr-spatial-tracking";
|
||||
export type PermissionsPolicyFeature = "accelerometer"|"ambient-light-sensor"|"attribution-reporting"|"autoplay"|"bluetooth"|"browsing-topics"|"camera"|"ch-dpr"|"ch-device-memory"|"ch-downlink"|"ch-ect"|"ch-prefers-color-scheme"|"ch-rtt"|"ch-save-data"|"ch-ua"|"ch-ua-arch"|"ch-ua-bitness"|"ch-ua-platform"|"ch-ua-model"|"ch-ua-mobile"|"ch-ua-full"|"ch-ua-full-version"|"ch-ua-full-version-list"|"ch-ua-platform-version"|"ch-ua-reduced"|"ch-ua-wow64"|"ch-viewport-height"|"ch-viewport-width"|"ch-width"|"clipboard-read"|"clipboard-write"|"cross-origin-isolated"|"direct-sockets"|"display-capture"|"document-domain"|"encrypted-media"|"execution-while-out-of-viewport"|"execution-while-not-rendered"|"focus-without-user-activation"|"fullscreen"|"frobulate"|"gamepad"|"geolocation"|"gyroscope"|"hid"|"idle-detection"|"interest-cohort"|"join-ad-interest-group"|"keyboard-map"|"local-fonts"|"magnetometer"|"microphone"|"midi"|"otp-credentials"|"payment"|"picture-in-picture"|"publickey-credentials-get"|"run-ad-auction"|"screen-wake-lock"|"serial"|"shared-autofill"|"storage-access-api"|"sync-xhr"|"trust-token-redemption"|"usb"|"vertical-scroll"|"web-share"|"window-placement"|"xr-spatial-tracking";
|
||||
/**
|
||||
* Reason for a permissions policy feature to be disabled.
|
||||
*/
|
||||
|
|
@ -10644,7 +10680,7 @@ Example URLs: http://www.google.com/file.html -> "google.com"
|
|||
/**
|
||||
* List of not restored reasons for back-forward cache.
|
||||
*/
|
||||
export type BackForwardCacheNotRestoredReason = "NotPrimaryMainFrame"|"BackForwardCacheDisabled"|"RelatedActiveContentsExist"|"HTTPStatusNotOK"|"SchemeNotHTTPOrHTTPS"|"Loading"|"WasGrantedMediaAccess"|"DisableForRenderFrameHostCalled"|"DomainNotAllowed"|"HTTPMethodNotGET"|"SubframeIsNavigating"|"Timeout"|"CacheLimit"|"JavaScriptExecution"|"RendererProcessKilled"|"RendererProcessCrashed"|"SchedulerTrackedFeatureUsed"|"ConflictingBrowsingInstance"|"CacheFlushed"|"ServiceWorkerVersionActivation"|"SessionRestored"|"ServiceWorkerPostMessage"|"EnteredBackForwardCacheBeforeServiceWorkerHostAdded"|"RenderFrameHostReused_SameSite"|"RenderFrameHostReused_CrossSite"|"ServiceWorkerClaim"|"IgnoreEventAndEvict"|"HaveInnerContents"|"TimeoutPuttingInCache"|"BackForwardCacheDisabledByLowMemory"|"BackForwardCacheDisabledByCommandLine"|"NetworkRequestDatapipeDrainedAsBytesConsumer"|"NetworkRequestRedirected"|"NetworkRequestTimeout"|"NetworkExceedsBufferLimit"|"NavigationCancelledWhileRestoring"|"NotMostRecentNavigationEntry"|"BackForwardCacheDisabledForPrerender"|"UserAgentOverrideDiffers"|"ForegroundCacheLimit"|"BrowsingInstanceNotSwapped"|"BackForwardCacheDisabledForDelegate"|"UnloadHandlerExistsInMainFrame"|"UnloadHandlerExistsInSubFrame"|"ServiceWorkerUnregistration"|"CacheControlNoStore"|"CacheControlNoStoreCookieModified"|"CacheControlNoStoreHTTPOnlyCookieModified"|"NoResponseHead"|"Unknown"|"ActivationNavigationsDisallowedForBug1234857"|"ErrorDocument"|"FencedFramesEmbedder"|"WebSocket"|"WebTransport"|"WebRTC"|"MainResourceHasCacheControlNoStore"|"MainResourceHasCacheControlNoCache"|"SubresourceHasCacheControlNoStore"|"SubresourceHasCacheControlNoCache"|"ContainsPlugins"|"DocumentLoaded"|"DedicatedWorkerOrWorklet"|"OutstandingNetworkRequestOthers"|"OutstandingIndexedDBTransaction"|"RequestedNotificationsPermission"|"RequestedMIDIPermission"|"RequestedAudioCapturePermission"|"RequestedVideoCapturePermission"|"RequestedBackForwardCacheBlockedSensors"|"RequestedBackgroundWorkPermission"|"BroadcastChannel"|"IndexedDBConnection"|"WebXR"|"SharedWorker"|"WebLocks"|"WebHID"|"WebShare"|"RequestedStorageAccessGrant"|"WebNfc"|"OutstandingNetworkRequestFetch"|"OutstandingNetworkRequestXHR"|"AppBanner"|"Printing"|"WebDatabase"|"PictureInPicture"|"Portal"|"SpeechRecognizer"|"IdleManager"|"PaymentManager"|"SpeechSynthesis"|"KeyboardLock"|"WebOTPService"|"OutstandingNetworkRequestDirectSocket"|"InjectedJavascript"|"InjectedStyleSheet"|"Dummy"|"ContentSecurityHandler"|"ContentWebAuthenticationAPI"|"ContentFileChooser"|"ContentSerial"|"ContentFileSystemAccess"|"ContentMediaDevicesDispatcherHost"|"ContentWebBluetooth"|"ContentWebUSB"|"ContentMediaSession"|"ContentMediaSessionService"|"ContentScreenReader"|"EmbedderPopupBlockerTabHelper"|"EmbedderSafeBrowsingTriggeredPopupBlocker"|"EmbedderSafeBrowsingThreatDetails"|"EmbedderAppBannerManager"|"EmbedderDomDistillerViewerSource"|"EmbedderDomDistillerSelfDeletingRequestDelegate"|"EmbedderOomInterventionTabHelper"|"EmbedderOfflinePage"|"EmbedderChromePasswordManagerClientBindCredentialManager"|"EmbedderPermissionRequestManager"|"EmbedderModalDialog"|"EmbedderExtensions"|"EmbedderExtensionMessaging"|"EmbedderExtensionMessagingForOpenPort"|"EmbedderExtensionSentMessageToCachedFrame";
|
||||
export type BackForwardCacheNotRestoredReason = "NotPrimaryMainFrame"|"BackForwardCacheDisabled"|"RelatedActiveContentsExist"|"HTTPStatusNotOK"|"SchemeNotHTTPOrHTTPS"|"Loading"|"WasGrantedMediaAccess"|"DisableForRenderFrameHostCalled"|"DomainNotAllowed"|"HTTPMethodNotGET"|"SubframeIsNavigating"|"Timeout"|"CacheLimit"|"JavaScriptExecution"|"RendererProcessKilled"|"RendererProcessCrashed"|"SchedulerTrackedFeatureUsed"|"ConflictingBrowsingInstance"|"CacheFlushed"|"ServiceWorkerVersionActivation"|"SessionRestored"|"ServiceWorkerPostMessage"|"EnteredBackForwardCacheBeforeServiceWorkerHostAdded"|"RenderFrameHostReused_SameSite"|"RenderFrameHostReused_CrossSite"|"ServiceWorkerClaim"|"IgnoreEventAndEvict"|"HaveInnerContents"|"TimeoutPuttingInCache"|"BackForwardCacheDisabledByLowMemory"|"BackForwardCacheDisabledByCommandLine"|"NetworkRequestDatapipeDrainedAsBytesConsumer"|"NetworkRequestRedirected"|"NetworkRequestTimeout"|"NetworkExceedsBufferLimit"|"NavigationCancelledWhileRestoring"|"NotMostRecentNavigationEntry"|"BackForwardCacheDisabledForPrerender"|"UserAgentOverrideDiffers"|"ForegroundCacheLimit"|"BrowsingInstanceNotSwapped"|"BackForwardCacheDisabledForDelegate"|"UnloadHandlerExistsInMainFrame"|"UnloadHandlerExistsInSubFrame"|"ServiceWorkerUnregistration"|"CacheControlNoStore"|"CacheControlNoStoreCookieModified"|"CacheControlNoStoreHTTPOnlyCookieModified"|"NoResponseHead"|"Unknown"|"ActivationNavigationsDisallowedForBug1234857"|"ErrorDocument"|"FencedFramesEmbedder"|"WebSocket"|"WebTransport"|"WebRTC"|"MainResourceHasCacheControlNoStore"|"MainResourceHasCacheControlNoCache"|"SubresourceHasCacheControlNoStore"|"SubresourceHasCacheControlNoCache"|"ContainsPlugins"|"DocumentLoaded"|"DedicatedWorkerOrWorklet"|"OutstandingNetworkRequestOthers"|"OutstandingIndexedDBTransaction"|"RequestedNotificationsPermission"|"RequestedMIDIPermission"|"RequestedAudioCapturePermission"|"RequestedVideoCapturePermission"|"RequestedBackForwardCacheBlockedSensors"|"RequestedBackgroundWorkPermission"|"BroadcastChannel"|"IndexedDBConnection"|"WebXR"|"SharedWorker"|"WebLocks"|"WebHID"|"WebShare"|"RequestedStorageAccessGrant"|"WebNfc"|"OutstandingNetworkRequestFetch"|"OutstandingNetworkRequestXHR"|"AppBanner"|"Printing"|"WebDatabase"|"PictureInPicture"|"Portal"|"SpeechRecognizer"|"IdleManager"|"PaymentManager"|"SpeechSynthesis"|"KeyboardLock"|"WebOTPService"|"OutstandingNetworkRequestDirectSocket"|"InjectedJavascript"|"InjectedStyleSheet"|"Dummy"|"ContentSecurityHandler"|"ContentWebAuthenticationAPI"|"ContentFileChooser"|"ContentSerial"|"ContentFileSystemAccess"|"ContentMediaDevicesDispatcherHost"|"ContentWebBluetooth"|"ContentWebUSB"|"ContentMediaSessionService"|"ContentScreenReader"|"EmbedderPopupBlockerTabHelper"|"EmbedderSafeBrowsingTriggeredPopupBlocker"|"EmbedderSafeBrowsingThreatDetails"|"EmbedderAppBannerManager"|"EmbedderDomDistillerViewerSource"|"EmbedderDomDistillerSelfDeletingRequestDelegate"|"EmbedderOomInterventionTabHelper"|"EmbedderOfflinePage"|"EmbedderChromePasswordManagerClientBindCredentialManager"|"EmbedderPermissionRequestManager"|"EmbedderModalDialog"|"EmbedderExtensions"|"EmbedderExtensionMessaging"|"EmbedderExtensionMessagingForOpenPort"|"EmbedderExtensionSentMessageToCachedFrame";
|
||||
/**
|
||||
* Types of not restored reasons for back-forward cache.
|
||||
*/
|
||||
|
|
@ -10720,6 +10756,11 @@ dependent on the reason:
|
|||
* JavaScript stack trace of when frame was attached, only set if frame initiated from script.
|
||||
*/
|
||||
stack?: Runtime.StackTrace;
|
||||
/**
|
||||
* Identifies the bottom-most script which caused the frame to be labelled
|
||||
as an ad. Only sent if frame is labelled as an ad and id is available.
|
||||
*/
|
||||
adScriptId?: AdScriptId;
|
||||
}
|
||||
/**
|
||||
* Fired when frame no longer has a scheduled navigation.
|
||||
|
|
@ -12668,6 +12709,15 @@ Tokens from that issuer.
|
|||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a storage key given a frame id.
|
||||
*/
|
||||
export type getStorageKeyForFrameParameters = {
|
||||
frameId: Page.FrameId;
|
||||
}
|
||||
export type getStorageKeyForFrameReturnValue = {
|
||||
storageKey: SerializedStorageKey;
|
||||
}
|
||||
/**
|
||||
* Clears storage for origin.
|
||||
*/
|
||||
|
|
@ -15248,13 +15298,30 @@ of scripts is used as end of range.
|
|||
export type removeBreakpointReturnValue = {
|
||||
}
|
||||
/**
|
||||
* Restarts particular call frame from the beginning.
|
||||
* Restarts particular call frame from the beginning. The old, deprecated
|
||||
behavior of `restartFrame` is to stay paused and allow further CDP commands
|
||||
after a restart was scheduled. This can cause problems with restarting, so
|
||||
we now continue execution immediatly after it has been scheduled until we
|
||||
reach the beginning of the restarted frame.
|
||||
|
||||
To stay back-wards compatible, `restartFrame` now expects a `mode`
|
||||
parameter to be present. If the `mode` parameter is missing, `restartFrame`
|
||||
errors out.
|
||||
|
||||
The various return values are deprecated and `callFrames` is always empty.
|
||||
Use the call frames from the `Debugger#paused` events instead, that fires
|
||||
once V8 pauses at the beginning of the restarted function.
|
||||
*/
|
||||
export type restartFrameParameters = {
|
||||
/**
|
||||
* Call frame identifier to evaluate on.
|
||||
*/
|
||||
callFrameId: CallFrameId;
|
||||
/**
|
||||
* The `mode` parameter must be present and set to 'StepInto', otherwise
|
||||
`restartFrame` will error out.
|
||||
*/
|
||||
mode?: "StepInto";
|
||||
}
|
||||
export type restartFrameReturnValue = {
|
||||
/**
|
||||
|
|
@ -17514,7 +17581,6 @@ Error was thrown.
|
|||
"DOMStorage.getDOMStorageItems": DOMStorage.getDOMStorageItemsParameters;
|
||||
"DOMStorage.removeDOMStorageItem": DOMStorage.removeDOMStorageItemParameters;
|
||||
"DOMStorage.setDOMStorageItem": DOMStorage.setDOMStorageItemParameters;
|
||||
"DOMStorage.getStorageKeyForFrame": DOMStorage.getStorageKeyForFrameParameters;
|
||||
"Database.disable": Database.disableParameters;
|
||||
"Database.enable": Database.enableParameters;
|
||||
"Database.executeSQL": Database.executeSQLParameters;
|
||||
|
|
@ -17547,6 +17613,7 @@ Error was thrown.
|
|||
"Emulation.setTimezoneOverride": Emulation.setTimezoneOverrideParameters;
|
||||
"Emulation.setVisibleSize": Emulation.setVisibleSizeParameters;
|
||||
"Emulation.setDisabledImageTypes": Emulation.setDisabledImageTypesParameters;
|
||||
"Emulation.setHardwareConcurrencyOverride": Emulation.setHardwareConcurrencyOverrideParameters;
|
||||
"Emulation.setUserAgentOverride": Emulation.setUserAgentOverrideParameters;
|
||||
"Emulation.setAutomationOverride": Emulation.setAutomationOverrideParameters;
|
||||
"HeadlessExperimental.beginFrame": HeadlessExperimental.beginFrameParameters;
|
||||
|
|
@ -17744,6 +17811,7 @@ Error was thrown.
|
|||
"ServiceWorker.stopWorker": ServiceWorker.stopWorkerParameters;
|
||||
"ServiceWorker.unregister": ServiceWorker.unregisterParameters;
|
||||
"ServiceWorker.updateRegistration": ServiceWorker.updateRegistrationParameters;
|
||||
"Storage.getStorageKeyForFrame": Storage.getStorageKeyForFrameParameters;
|
||||
"Storage.clearDataForOrigin": Storage.clearDataForOriginParameters;
|
||||
"Storage.getCookies": Storage.getCookiesParameters;
|
||||
"Storage.setCookies": Storage.setCookiesParameters;
|
||||
|
|
@ -18044,7 +18112,6 @@ Error was thrown.
|
|||
"DOMStorage.getDOMStorageItems": DOMStorage.getDOMStorageItemsReturnValue;
|
||||
"DOMStorage.removeDOMStorageItem": DOMStorage.removeDOMStorageItemReturnValue;
|
||||
"DOMStorage.setDOMStorageItem": DOMStorage.setDOMStorageItemReturnValue;
|
||||
"DOMStorage.getStorageKeyForFrame": DOMStorage.getStorageKeyForFrameReturnValue;
|
||||
"Database.disable": Database.disableReturnValue;
|
||||
"Database.enable": Database.enableReturnValue;
|
||||
"Database.executeSQL": Database.executeSQLReturnValue;
|
||||
|
|
@ -18077,6 +18144,7 @@ Error was thrown.
|
|||
"Emulation.setTimezoneOverride": Emulation.setTimezoneOverrideReturnValue;
|
||||
"Emulation.setVisibleSize": Emulation.setVisibleSizeReturnValue;
|
||||
"Emulation.setDisabledImageTypes": Emulation.setDisabledImageTypesReturnValue;
|
||||
"Emulation.setHardwareConcurrencyOverride": Emulation.setHardwareConcurrencyOverrideReturnValue;
|
||||
"Emulation.setUserAgentOverride": Emulation.setUserAgentOverrideReturnValue;
|
||||
"Emulation.setAutomationOverride": Emulation.setAutomationOverrideReturnValue;
|
||||
"HeadlessExperimental.beginFrame": HeadlessExperimental.beginFrameReturnValue;
|
||||
|
|
@ -18274,6 +18342,7 @@ Error was thrown.
|
|||
"ServiceWorker.stopWorker": ServiceWorker.stopWorkerReturnValue;
|
||||
"ServiceWorker.unregister": ServiceWorker.unregisterReturnValue;
|
||||
"ServiceWorker.updateRegistration": ServiceWorker.updateRegistrationReturnValue;
|
||||
"Storage.getStorageKeyForFrame": Storage.getStorageKeyForFrameReturnValue;
|
||||
"Storage.clearDataForOrigin": Storage.clearDataForOriginReturnValue;
|
||||
"Storage.getCookies": Storage.getCookiesReturnValue;
|
||||
"Storage.setCookies": Storage.setCookiesReturnValue;
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@
|
|||
"defaultBrowserType": "webkit"
|
||||
},
|
||||
"Galaxy S5": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 360,
|
||||
"height": 640
|
||||
|
|
@ -121,7 +121,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Galaxy S5 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 640,
|
||||
"height": 360
|
||||
|
|
@ -132,7 +132,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Galaxy S8": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 360,
|
||||
"height": 740
|
||||
|
|
@ -143,7 +143,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Galaxy S8 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 740,
|
||||
"height": 360
|
||||
|
|
@ -154,7 +154,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Galaxy S9+": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 320,
|
||||
"height": 658
|
||||
|
|
@ -165,7 +165,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Galaxy S9+ landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 658,
|
||||
"height": 320
|
||||
|
|
@ -176,7 +176,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Galaxy Tab S4": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 712,
|
||||
"height": 1138
|
||||
|
|
@ -187,7 +187,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Galaxy Tab S4 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 1138,
|
||||
"height": 712
|
||||
|
|
@ -858,7 +858,7 @@
|
|||
"defaultBrowserType": "webkit"
|
||||
},
|
||||
"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/103.0.5060.53 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/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 384,
|
||||
"height": 640
|
||||
|
|
@ -869,7 +869,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"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/103.0.5060.53 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/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 640,
|
||||
"height": 384
|
||||
|
|
@ -880,7 +880,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"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/103.0.5060.53 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/104.0.5112.20 Mobile Safari/537.36 Edge/14.14263",
|
||||
"viewport": {
|
||||
"width": 640,
|
||||
"height": 360
|
||||
|
|
@ -891,7 +891,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"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/103.0.5060.53 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/104.0.5112.20 Mobile Safari/537.36 Edge/14.14263",
|
||||
"viewport": {
|
||||
"width": 360,
|
||||
"height": 640
|
||||
|
|
@ -902,7 +902,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"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/103.0.5060.53 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/104.0.5112.20 Mobile Safari/537.36 Edge/14.14263",
|
||||
"viewport": {
|
||||
"width": 360,
|
||||
"height": 640
|
||||
|
|
@ -913,7 +913,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"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/103.0.5060.53 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/104.0.5112.20 Mobile Safari/537.36 Edge/14.14263",
|
||||
"viewport": {
|
||||
"width": 640,
|
||||
"height": 360
|
||||
|
|
@ -924,7 +924,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 10": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 800,
|
||||
"height": 1280
|
||||
|
|
@ -935,7 +935,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 10 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 1280,
|
||||
"height": 800
|
||||
|
|
@ -946,7 +946,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 4": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 384,
|
||||
"height": 640
|
||||
|
|
@ -957,7 +957,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 4 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 640,
|
||||
"height": 384
|
||||
|
|
@ -968,7 +968,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 5": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 360,
|
||||
"height": 640
|
||||
|
|
@ -979,7 +979,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 5 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 640,
|
||||
"height": 360
|
||||
|
|
@ -990,7 +990,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 5X": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 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/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 412,
|
||||
"height": 732
|
||||
|
|
@ -1001,7 +1001,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"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/103.0.5060.53 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/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 732,
|
||||
"height": 412
|
||||
|
|
@ -1012,7 +1012,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 6": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 412,
|
||||
"height": 732
|
||||
|
|
@ -1023,7 +1023,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 6 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 732,
|
||||
"height": 412
|
||||
|
|
@ -1034,7 +1034,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 6P": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 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/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 412,
|
||||
"height": 732
|
||||
|
|
@ -1045,7 +1045,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"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/103.0.5060.53 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/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 732,
|
||||
"height": 412
|
||||
|
|
@ -1056,7 +1056,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 7": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 600,
|
||||
"height": 960
|
||||
|
|
@ -1067,7 +1067,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Nexus 7 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 960,
|
||||
"height": 600
|
||||
|
|
@ -1122,7 +1122,7 @@
|
|||
"defaultBrowserType": "webkit"
|
||||
},
|
||||
"Pixel 2": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 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/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 411,
|
||||
"height": 731
|
||||
|
|
@ -1133,7 +1133,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 2 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 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/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 731,
|
||||
"height": 411
|
||||
|
|
@ -1144,7 +1144,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"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/103.0.5060.53 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/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 411,
|
||||
"height": 823
|
||||
|
|
@ -1155,7 +1155,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"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/103.0.5060.53 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/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 823,
|
||||
"height": 411
|
||||
|
|
@ -1166,7 +1166,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 3": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 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/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 393,
|
||||
"height": 786
|
||||
|
|
@ -1177,7 +1177,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 3 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 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/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 786,
|
||||
"height": 393
|
||||
|
|
@ -1188,7 +1188,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 4": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 353,
|
||||
"height": 745
|
||||
|
|
@ -1199,7 +1199,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 4 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 745,
|
||||
"height": 353
|
||||
|
|
@ -1210,7 +1210,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 4a (5G)": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Mobile Safari/537.36",
|
||||
"screen": {
|
||||
"width": 412,
|
||||
"height": 892
|
||||
|
|
@ -1225,7 +1225,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 4a (5G) landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Mobile Safari/537.36",
|
||||
"screen": {
|
||||
"height": 892,
|
||||
"width": 412
|
||||
|
|
@ -1240,7 +1240,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 5": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Mobile Safari/537.36",
|
||||
"screen": {
|
||||
"width": 393,
|
||||
"height": 851
|
||||
|
|
@ -1255,7 +1255,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Pixel 5 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Mobile Safari/537.36",
|
||||
"screen": {
|
||||
"width": 851,
|
||||
"height": 393
|
||||
|
|
@ -1270,7 +1270,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Moto G4": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 360,
|
||||
"height": 640
|
||||
|
|
@ -1281,7 +1281,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Moto G4 landscape": {
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Mobile Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Mobile Safari/537.36",
|
||||
"viewport": {
|
||||
"width": 640,
|
||||
"height": 360
|
||||
|
|
@ -1292,7 +1292,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Desktop Chrome HiDPI": {
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Safari/537.36",
|
||||
"screen": {
|
||||
"width": 1792,
|
||||
"height": 1120
|
||||
|
|
@ -1307,7 +1307,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Desktop Edge HiDPI": {
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36 Edg/103.0.5060.53",
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Safari/537.36 Edg/104.0.5112.20",
|
||||
"screen": {
|
||||
"width": 1792,
|
||||
"height": 1120
|
||||
|
|
@ -1352,7 +1352,7 @@
|
|||
"defaultBrowserType": "webkit"
|
||||
},
|
||||
"Desktop Chrome": {
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36",
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Safari/537.36",
|
||||
"screen": {
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
|
|
@ -1367,7 +1367,7 @@
|
|||
"defaultBrowserType": "chromium"
|
||||
},
|
||||
"Desktop Edge": {
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36 Edg/103.0.5060.53",
|
||||
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.20 Safari/537.36 Edg/104.0.5112.20",
|
||||
"screen": {
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
|
|
|
|||
|
|
@ -213,8 +213,13 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
|||
return { session: new CDPSessionDispatcher(this._scope, await crBrowserContext.newCDPSession((params.page ? params.page as PageDispatcher : params.frame as FrameDispatcher)._object)) };
|
||||
}
|
||||
|
||||
async harStart(params: channels.BrowserContextHarStartParams): Promise<channels.BrowserContextHarStartResult> {
|
||||
const harId = await this._context._harStart(params.page ? (params.page as PageDispatcher)._object : null, params.options);
|
||||
return { harId };
|
||||
}
|
||||
|
||||
async harExport(params: channels.BrowserContextHarExportParams): Promise<channels.BrowserContextHarExportResult> {
|
||||
const artifact = await this._context._harRecorder?.export();
|
||||
const artifact = await this._context._harExport(params.harId);
|
||||
if (!artifact)
|
||||
throw new Error('No HAR artifact. Ensure record.harPath is set.');
|
||||
return { artifact: new ArtifactDispatcher(this._scope, artifact) };
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import type { DispatcherScope } from './dispatcher';
|
|||
import { Dispatcher } from './dispatcher';
|
||||
import { yazl, yauzl } from '../../zipBundle';
|
||||
import { ZipFile } from '../../utils/zipFile';
|
||||
import type { HAREntry, HARFile, HARHeader } from '../../../types/har';
|
||||
import type * as har from '../har/har';
|
||||
import type { HeadersArray } from '../types';
|
||||
|
||||
export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels.LocalUtilsChannel> implements channels.LocalUtilsChannel {
|
||||
|
|
@ -100,10 +100,10 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels.
|
|||
if (!harEntryName)
|
||||
return { error: 'Specified archive does not have a .har file' };
|
||||
const har = await zipFile.read(harEntryName);
|
||||
const harFile = JSON.parse(har.toString()) as HARFile;
|
||||
const harFile = JSON.parse(har.toString()) as har.HARFile;
|
||||
harBackend = new HarBackend(harFile, null, zipFile);
|
||||
} else {
|
||||
const harFile = JSON.parse(await fs.promises.readFile(params.file, 'utf-8')) as HARFile;
|
||||
const harFile = JSON.parse(await fs.promises.readFile(params.file, 'utf-8')) as har.HARFile;
|
||||
harBackend = new HarBackend(harFile, path.dirname(params.file), null);
|
||||
}
|
||||
this._harBakends.set(harBackend.id, harBackend);
|
||||
|
|
@ -124,17 +124,31 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels.
|
|||
harBackend.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
async harUnzip(params: channels.LocalUtilsHarUnzipParams, metadata?: channels.Metadata): Promise<void> {
|
||||
const dir = path.dirname(params.zipFile);
|
||||
const zipFile = new ZipFile(params.zipFile);
|
||||
for (const entry of await zipFile.entries()) {
|
||||
const buffer = await zipFile.read(entry);
|
||||
if (entry === 'har.har')
|
||||
await fs.promises.writeFile(params.harFile, buffer);
|
||||
else
|
||||
await fs.promises.writeFile(path.join(dir, entry), buffer);
|
||||
}
|
||||
zipFile.close();
|
||||
await fs.promises.unlink(params.zipFile);
|
||||
}
|
||||
}
|
||||
|
||||
const redirectStatus = [301, 302, 303, 307, 308];
|
||||
|
||||
class HarBackend {
|
||||
readonly id = createGuid();
|
||||
private _harFile: HARFile;
|
||||
private _harFile: har.HARFile;
|
||||
private _zipFile: ZipFile | null;
|
||||
private _baseDir: string | null;
|
||||
|
||||
constructor(harFile: HARFile, baseDir: string | null, zipFile: ZipFile | null) {
|
||||
constructor(harFile: har.HARFile, baseDir: string | null, zipFile: ZipFile | null) {
|
||||
this._harFile = harFile;
|
||||
this._baseDir = baseDir;
|
||||
this._zipFile = zipFile;
|
||||
|
|
@ -176,25 +190,25 @@ class HarBackend {
|
|||
}
|
||||
}
|
||||
|
||||
private async _loadContent(content: { text?: string, encoding?: string, _sha1?: string }): Promise<Buffer> {
|
||||
const sha1 = content._sha1;
|
||||
private async _loadContent(content: { text?: string, encoding?: string, _file?: string }): Promise<Buffer> {
|
||||
const file = content._file;
|
||||
let buffer: Buffer;
|
||||
if (sha1) {
|
||||
if (file) {
|
||||
if (this._zipFile)
|
||||
buffer = await this._zipFile.read(sha1);
|
||||
buffer = await this._zipFile.read(file);
|
||||
else
|
||||
buffer = await fs.promises.readFile(path.resolve(this._baseDir!, sha1));
|
||||
buffer = await fs.promises.readFile(path.resolve(this._baseDir!, file));
|
||||
} else {
|
||||
buffer = Buffer.from(content.text || '', content.encoding === 'base64' ? 'base64' : 'utf-8');
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private async _harFindResponse(url: string, method: string, headers: HeadersArray, postData: Buffer | undefined): Promise<HAREntry | undefined> {
|
||||
private async _harFindResponse(url: string, method: string, headers: HeadersArray, postData: Buffer | undefined): Promise<har.Entry | undefined> {
|
||||
const harLog = this._harFile.log;
|
||||
const visited = new Set<HAREntry>();
|
||||
const visited = new Set<har.Entry>();
|
||||
while (true) {
|
||||
const entries: HAREntry[] = [];
|
||||
const entries: har.Entry[] = [];
|
||||
for (const candidate of harLog.entries) {
|
||||
if (candidate.request.url !== url || candidate.request.method !== method)
|
||||
continue;
|
||||
|
|
@ -213,7 +227,7 @@ class HarBackend {
|
|||
|
||||
// Disambiguate using headers - then one with most matching headers wins.
|
||||
if (entries.length > 1) {
|
||||
const list: { candidate: HAREntry, matchingHeaders: number }[] = [];
|
||||
const list: { candidate: har.Entry, matchingHeaders: number }[] = [];
|
||||
for (const candidate of entries) {
|
||||
const matchingHeaders = countMatchingHeaders(candidate.request.headers, headers);
|
||||
list.push({ candidate, matchingHeaders });
|
||||
|
|
@ -249,7 +263,7 @@ class HarBackend {
|
|||
}
|
||||
}
|
||||
|
||||
function countMatchingHeaders(harHeaders: HARHeader[], headers: HeadersArray): number {
|
||||
function countMatchingHeaders(harHeaders: har.Header[], headers: HeadersArray): number {
|
||||
const set = new Set(headers.map(h => h.name.toLowerCase() + ':' + h.value));
|
||||
let matches = 0;
|
||||
for (const h of harHeaders) {
|
||||
|
|
|
|||
|
|
@ -135,7 +135,11 @@ export class RouteDispatcher extends Dispatcher<Route, channels.RouteChannel> im
|
|||
}
|
||||
|
||||
async abort(params: channels.RouteAbortParams): Promise<void> {
|
||||
await this._object.abort(params.errorCode || 'failed', params.redirectAbortedNavigationToUrl);
|
||||
await this._object.abort(params.errorCode || 'failed');
|
||||
}
|
||||
|
||||
async redirectNavigationRequest(params: channels.RouteRedirectNavigationRequestParams): Promise<void> {
|
||||
await this._object.redirectNavigationRequest(params.url);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -269,7 +269,7 @@ export class FrameManager {
|
|||
name: frame._name,
|
||||
newDocument: frame.pendingDocument(),
|
||||
error: new NavigationAbortedError(documentId, errorText),
|
||||
isPublic: !frame._pendingNavigationRedirectAfterAbort
|
||||
isPublic: !(documentId && frame._redirectedNavigations.has(documentId)),
|
||||
};
|
||||
frame.setPendingDocument(undefined);
|
||||
frame.emit(Frame.Events.InternalNavigation, navigationEvent);
|
||||
|
|
@ -467,7 +467,7 @@ export class Frame extends SdkObject {
|
|||
readonly _detachedPromise: Promise<void>;
|
||||
private _detachedCallback = () => {};
|
||||
private _raceAgainstEvaluationStallingEventsPromises = new Set<ManualPromise<any>>();
|
||||
_pendingNavigationRedirectAfterAbort: { url: string, documentId: string } | undefined;
|
||||
readonly _redirectedNavigations = new Map<string, { url: string, gotoPromise: Promise<network.Response | null> }>(); // documentId -> data
|
||||
|
||||
constructor(page: Page, id: string, parentFrame: Frame | null) {
|
||||
super(page, 'frame');
|
||||
|
|
@ -604,12 +604,11 @@ export class Frame extends SdkObject {
|
|||
this._page._crashedPromise.then(() => { throw new Error('Navigation failed because page crashed!'); }),
|
||||
this._detachedPromise.then(() => { throw new Error('Navigating frame was detached!'); }),
|
||||
action().catch(e => {
|
||||
if (this._pendingNavigationRedirectAfterAbort && e instanceof NavigationAbortedError) {
|
||||
const { url, documentId } = this._pendingNavigationRedirectAfterAbort;
|
||||
this._pendingNavigationRedirectAfterAbort = undefined;
|
||||
if (e.documentId === documentId) {
|
||||
progress.log(`redirecting navigation to "${url}"`);
|
||||
return this._gotoAction(progress, url, options);
|
||||
if (e instanceof NavigationAbortedError && e.documentId) {
|
||||
const data = this._redirectedNavigations.get(e.documentId);
|
||||
if (data) {
|
||||
progress.log(`waiting for redirected navigation to "${data.url}"`);
|
||||
return data.gotoPromise;
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
|
|
@ -617,8 +616,14 @@ export class Frame extends SdkObject {
|
|||
]);
|
||||
}
|
||||
|
||||
redirectNavigationAfterAbort(url: string, documentId: string) {
|
||||
this._pendingNavigationRedirectAfterAbort = { url, documentId };
|
||||
redirectNavigation(url: string, documentId: string, referer: string | undefined) {
|
||||
const controller = new ProgressController(serverSideCallMetadata(), this);
|
||||
const data = {
|
||||
url,
|
||||
gotoPromise: controller.run(progress => this._gotoAction(progress, url, { referer }), 0),
|
||||
};
|
||||
this._redirectedNavigations.set(documentId, data);
|
||||
data.gotoPromise.finally(() => this._redirectedNavigations.delete(documentId));
|
||||
}
|
||||
|
||||
async goto(metadata: CallMetadata, url: string, options: types.GotoOptions = {}): Promise<network.Response | null> {
|
||||
|
|
@ -659,7 +664,7 @@ export class Frame extends SdkObject {
|
|||
if (event.newDocument!.documentId !== navigateResult.newDocumentId) {
|
||||
// This is just a sanity check. In practice, new navigation should
|
||||
// cancel the previous one and report "request cancelled"-like error.
|
||||
throw new Error('Navigation interrupted by another one');
|
||||
throw new NavigationAbortedError(navigateResult.newDocumentId, 'Navigation interrupted by another one');
|
||||
}
|
||||
if (event.error)
|
||||
throw event.error;
|
||||
|
|
|
|||
|
|
@ -22,19 +22,22 @@ export type HARFile = {
|
|||
export type Log = {
|
||||
version: string;
|
||||
creator: Creator;
|
||||
browser: Browser;
|
||||
pages: Page[];
|
||||
browser?: Browser;
|
||||
pages?: Page[];
|
||||
entries: Entry[];
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type Creator = {
|
||||
name: string;
|
||||
version: string;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type Browser = {
|
||||
name: string;
|
||||
version: string;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type Page = {
|
||||
|
|
@ -42,11 +45,13 @@ export type Page = {
|
|||
id: string;
|
||||
title: string;
|
||||
pageTimings: PageTimings;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type PageTimings = {
|
||||
onContentLoad: number;
|
||||
onLoad: number;
|
||||
onContentLoad?: number;
|
||||
onLoad?: number;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type Entry = {
|
||||
|
|
@ -59,9 +64,8 @@ export type Entry = {
|
|||
timings: Timings;
|
||||
serverIPAddress?: string;
|
||||
connection?: string;
|
||||
_requestref: string;
|
||||
_frameref: string;
|
||||
_monotonicTime: number;
|
||||
_frameref?: string;
|
||||
_monotonicTime?: number;
|
||||
_serverPort?: number;
|
||||
_securityDetails?: SecurityDetails;
|
||||
};
|
||||
|
|
@ -76,6 +80,7 @@ export type Request = {
|
|||
postData?: PostData;
|
||||
headersSize: number;
|
||||
bodySize: number;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type Response = {
|
||||
|
|
@ -88,7 +93,8 @@ export type Response = {
|
|||
redirectURL: string;
|
||||
headersSize: number;
|
||||
bodySize: number;
|
||||
_transferSize: number;
|
||||
comment?: string;
|
||||
_transferSize?: number;
|
||||
_failureText?: string
|
||||
};
|
||||
|
||||
|
|
@ -101,23 +107,28 @@ export type Cookie = {
|
|||
httpOnly?: boolean;
|
||||
secure?: boolean;
|
||||
sameSite?: string;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type Header = {
|
||||
name: string;
|
||||
value: string;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type QueryParameter = {
|
||||
name: string;
|
||||
value: string;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type PostData = {
|
||||
mimeType: string;
|
||||
params: Param[];
|
||||
text: string;
|
||||
comment?: string;
|
||||
_sha1?: string;
|
||||
_file?: string;
|
||||
};
|
||||
|
||||
export type Param = {
|
||||
|
|
@ -125,6 +136,7 @@ export type Param = {
|
|||
value?: string;
|
||||
fileName?: string;
|
||||
contentType?: string;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type Content = {
|
||||
|
|
@ -133,12 +145,15 @@ export type Content = {
|
|||
mimeType: string;
|
||||
text?: string;
|
||||
encoding?: string;
|
||||
comment?: string;
|
||||
_sha1?: string;
|
||||
_file?: string;
|
||||
};
|
||||
|
||||
export type Cache = {
|
||||
beforeRequest: CacheState | null;
|
||||
afterRequest: CacheState | null;
|
||||
beforeRequest?: CacheState | null;
|
||||
afterRequest?: CacheState | null;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type CacheState = {
|
||||
|
|
@ -146,6 +161,7 @@ export type CacheState = {
|
|||
lastAccess: string;
|
||||
eTag: string;
|
||||
hitCount: number;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type Timings = {
|
||||
|
|
@ -156,6 +172,7 @@ export type Timings = {
|
|||
wait: number;
|
||||
receive: number;
|
||||
ssl?: number;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type SecurityDetails = {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import type { ZipFile } from '../../zipBundle';
|
|||
import { ManualPromise } from '../../utils/manualPromise';
|
||||
import type EventEmitter from 'events';
|
||||
import { createGuid } from '../../utils';
|
||||
import type { Page } from '../page';
|
||||
|
||||
export class HarRecorder {
|
||||
private _artifact: Artifact;
|
||||
|
|
@ -33,14 +34,17 @@ export class HarRecorder {
|
|||
private _tracer: HarTracer;
|
||||
private _entries: har.Entry[] = [];
|
||||
private _zipFile: ZipFile | null = null;
|
||||
private _writtenZipEntries = new Set<string>();
|
||||
|
||||
constructor(context: BrowserContext, options: channels.RecordHarOptions) {
|
||||
constructor(context: BrowserContext, page: Page | null, options: channels.RecordHarOptions) {
|
||||
this._artifact = new Artifact(context, path.join(context._browser.options.artifactsDir, `${createGuid()}.har`));
|
||||
const urlFilterRe = options.urlRegexSource !== undefined && options.urlRegexFlags !== undefined ? new RegExp(options.urlRegexSource, options.urlRegexFlags) : undefined;
|
||||
const expectsZip = options.path.endsWith('.zip');
|
||||
const content = options.content || (expectsZip ? 'attach' : 'embed');
|
||||
this._tracer = new HarTracer(context, this, {
|
||||
this._tracer = new HarTracer(context, page, this, {
|
||||
content,
|
||||
slimMode: options.mode === 'minimal',
|
||||
includeTraceInfo: false,
|
||||
waitForContentOnStop: true,
|
||||
skipScripts: false,
|
||||
urlFilter: urlFilterRe ?? options.urlGlob,
|
||||
|
|
@ -57,8 +61,10 @@ export class HarRecorder {
|
|||
}
|
||||
|
||||
onContentBlob(sha1: string, buffer: Buffer) {
|
||||
if (this._zipFile)
|
||||
this._zipFile!.addBuffer(buffer, sha1);
|
||||
if (!this._zipFile || this._writtenZipEntries.has(sha1))
|
||||
return;
|
||||
this._writtenZipEntries.add(sha1);
|
||||
this._zipFile!.addBuffer(buffer, sha1);
|
||||
}
|
||||
|
||||
async flush() {
|
||||
|
|
@ -70,7 +76,7 @@ export class HarRecorder {
|
|||
const log = this._tracer.stop();
|
||||
log.entries = this._entries;
|
||||
|
||||
const harFileContent = JSON.stringify({ log }, undefined, 2);
|
||||
const harFileContent = jsonStringify({ log });
|
||||
|
||||
if (this._zipFile) {
|
||||
const result = new ManualPromise<void>();
|
||||
|
|
@ -92,3 +98,50 @@ export class HarRecorder {
|
|||
return this._artifact;
|
||||
}
|
||||
}
|
||||
|
||||
function jsonStringify(object: any): string {
|
||||
const tokens: string[] = [];
|
||||
innerJsonStringify(object, tokens, '', false, undefined);
|
||||
return tokens.join('');
|
||||
}
|
||||
|
||||
function innerJsonStringify(object: any, tokens: string[], indent: string, flat: boolean, parentKey: string | undefined) {
|
||||
if (typeof object !== 'object' || object === null) {
|
||||
tokens.push(JSON.stringify(object));
|
||||
return;
|
||||
}
|
||||
|
||||
const isArray = Array.isArray(object);
|
||||
if (!isArray && object.constructor.name !== 'Object') {
|
||||
tokens.push(JSON.stringify(object));
|
||||
return;
|
||||
}
|
||||
|
||||
const entries = isArray ? object : Object.entries(object).filter(e => e[1] !== undefined);
|
||||
if (!entries.length) {
|
||||
tokens.push(isArray ? `[]` : `{}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const childIndent = `${indent} `;
|
||||
let brackets: { open: string, close: string };
|
||||
if (isArray)
|
||||
brackets = flat ? { open: '[', close: ']' } : { open: `[\n${childIndent}`, close: `\n${indent}]` };
|
||||
else
|
||||
brackets = flat ? { open: '{ ', close: ' }' } : { open: `{\n${childIndent}`, close: `\n${indent}}` };
|
||||
|
||||
tokens.push(brackets.open);
|
||||
|
||||
for (let i = 0; i < entries.length; ++i) {
|
||||
const entry = entries[i];
|
||||
if (i)
|
||||
tokens.push(flat ? `, ` : `,\n${childIndent}`);
|
||||
if (!isArray)
|
||||
tokens.push(`${JSON.stringify(entry[0])}: `);
|
||||
const key = isArray ? undefined : entry[0];
|
||||
const flatten = flat || key === 'timings' || parentKey === 'headers';
|
||||
innerJsonStringify(isArray ? entry : entry[1], tokens, childIndent, flatten, key);
|
||||
}
|
||||
|
||||
tokens.push(brackets.close);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,8 +42,16 @@ export interface HarTracerDelegate {
|
|||
type HarTracerOptions = {
|
||||
content: 'omit' | 'attach' | 'embed';
|
||||
skipScripts: boolean;
|
||||
includeTraceInfo: boolean;
|
||||
waitForContentOnStop: boolean;
|
||||
urlFilter?: string | RegExp;
|
||||
slimMode?: boolean;
|
||||
omitSecurityDetails?: boolean;
|
||||
omitCookies?: boolean;
|
||||
omitTiming?: boolean;
|
||||
omitServerIP?: boolean;
|
||||
omitPages?: boolean;
|
||||
omitSizes?: boolean;
|
||||
};
|
||||
|
||||
export class HarTracer {
|
||||
|
|
@ -56,11 +64,21 @@ export class HarTracer {
|
|||
private _started = false;
|
||||
private _entrySymbol: symbol;
|
||||
private _baseURL: string | undefined;
|
||||
private _page: Page | null;
|
||||
|
||||
constructor(context: BrowserContext | APIRequestContext, delegate: HarTracerDelegate, options: HarTracerOptions) {
|
||||
constructor(context: BrowserContext | APIRequestContext, page: Page | null, delegate: HarTracerDelegate, options: HarTracerOptions) {
|
||||
this._context = context;
|
||||
this._page = page;
|
||||
this._delegate = delegate;
|
||||
this._options = options;
|
||||
if (options.slimMode) {
|
||||
options.omitSecurityDetails = true;
|
||||
options.omitCookies = true;
|
||||
options.omitTiming = true;
|
||||
options.omitServerIP = true;
|
||||
options.omitSizes = true;
|
||||
options.omitPages = true;
|
||||
}
|
||||
this._entrySymbol = Symbol('requestHarEntry');
|
||||
this._baseURL = context instanceof APIRequestContext ? context._defaultOptions().baseURL : context._options.baseURL;
|
||||
}
|
||||
|
|
@ -76,7 +94,7 @@ export class HarTracer {
|
|||
];
|
||||
if (this._context instanceof BrowserContext) {
|
||||
this._eventListeners.push(
|
||||
eventsHelper.addEventListener(this._context, BrowserContext.Events.Page, (page: Page) => this._ensurePageEntry(page)),
|
||||
eventsHelper.addEventListener(this._context, BrowserContext.Events.Page, (page: Page) => this._createPageEntryIfNeeded(page)),
|
||||
eventsHelper.addEventListener(this._context, BrowserContext.Events.Request, (request: network.Request) => this._onRequest(request)),
|
||||
eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestFinished, ({ request, response }) => this._onRequestFinished(request, response).catch(() => {})),
|
||||
eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestFailed, request => this._onRequestFailed(request)),
|
||||
|
|
@ -92,32 +110,36 @@ export class HarTracer {
|
|||
return (request as any)[this._entrySymbol];
|
||||
}
|
||||
|
||||
private _ensurePageEntry(page: Page) {
|
||||
private _createPageEntryIfNeeded(page: Page): har.Page | undefined {
|
||||
if (this._options.omitPages)
|
||||
return;
|
||||
if (this._page && page !== this._page)
|
||||
return;
|
||||
let pageEntry = this._pageEntries.get(page);
|
||||
if (!pageEntry) {
|
||||
page.mainFrame().on(Frame.Events.AddLifecycle, (event: LifecycleEvent) => {
|
||||
if (event === 'load')
|
||||
this._onLoad(page);
|
||||
if (event === 'domcontentloaded')
|
||||
this._onDOMContentLoaded(page);
|
||||
});
|
||||
|
||||
pageEntry = {
|
||||
startedDateTime: new Date(),
|
||||
id: page.guid,
|
||||
title: '',
|
||||
pageTimings: {
|
||||
pageTimings: this._options.omitTiming ? {} : {
|
||||
onContentLoad: -1,
|
||||
onLoad: -1,
|
||||
},
|
||||
};
|
||||
|
||||
page.mainFrame().on(Frame.Events.AddLifecycle, (event: LifecycleEvent) => {
|
||||
if (event === 'load')
|
||||
this._onLoad(page, pageEntry!);
|
||||
if (event === 'domcontentloaded')
|
||||
this._onDOMContentLoaded(page, pageEntry!);
|
||||
});
|
||||
|
||||
this._pageEntries.set(page, pageEntry);
|
||||
}
|
||||
return pageEntry;
|
||||
}
|
||||
|
||||
private _onDOMContentLoaded(page: Page) {
|
||||
const pageEntry = this._ensurePageEntry(page);
|
||||
private _onDOMContentLoaded(page: Page, pageEntry: har.Page) {
|
||||
const promise = page.mainFrame().evaluateExpression(String(() => {
|
||||
return {
|
||||
title: document.title,
|
||||
|
|
@ -125,13 +147,13 @@ export class HarTracer {
|
|||
};
|
||||
}), true, undefined, 'utility').then(result => {
|
||||
pageEntry.title = result.title;
|
||||
pageEntry.pageTimings.onContentLoad = result.domContentLoaded;
|
||||
if (!this._options.omitTiming)
|
||||
pageEntry.pageTimings.onContentLoad = result.domContentLoaded;
|
||||
}).catch(() => {});
|
||||
this._addBarrier(page, promise);
|
||||
}
|
||||
|
||||
private _onLoad(page: Page) {
|
||||
const pageEntry = this._ensurePageEntry(page);
|
||||
private _onLoad(page: Page, pageEntry: har.Page) {
|
||||
const promise = page.mainFrame().evaluateExpression(String(() => {
|
||||
return {
|
||||
title: document.title,
|
||||
|
|
@ -139,7 +161,8 @@ export class HarTracer {
|
|||
};
|
||||
}), true, undefined, 'utility').then(result => {
|
||||
pageEntry.title = result.title;
|
||||
pageEntry.pageTimings.onLoad = result.loaded;
|
||||
if (!this._options.omitTiming)
|
||||
pageEntry.pageTimings.onLoad = result.loaded;
|
||||
}).catch(() => {});
|
||||
this._addBarrier(page, promise);
|
||||
}
|
||||
|
|
@ -161,11 +184,13 @@ export class HarTracer {
|
|||
private _onAPIRequest(event: APIRequestEvent) {
|
||||
if (!this._shouldIncludeEntryWithUrl(event.url.toString()))
|
||||
return;
|
||||
const harEntry = createHarEntry(event.method, event.url, '', '');
|
||||
harEntry.request.cookies = event.cookies;
|
||||
const harEntry = createHarEntry(event.method, event.url, undefined, this._options);
|
||||
if (!this._options.omitCookies)
|
||||
harEntry.request.cookies = event.cookies;
|
||||
harEntry.request.headers = Object.entries(event.headers).map(([name, value]) => ({ name, value }));
|
||||
harEntry.request.postData = this._postDataForBuffer(event.postData || null, event.headers['content-type'], this._options.content);
|
||||
harEntry.request.bodySize = event.postData?.length || 0;
|
||||
if (!this._options.omitSizes)
|
||||
harEntry.request.bodySize = event.postData?.length || 0;
|
||||
(event as any)[this._entrySymbol] = harEntry;
|
||||
if (this._started)
|
||||
this._delegate.onEntryStarted(harEntry);
|
||||
|
|
@ -186,7 +211,7 @@ export class HarTracer {
|
|||
value: event.rawHeaders[i + 1]
|
||||
});
|
||||
}
|
||||
harEntry.response.cookies = event.cookies.map(c => {
|
||||
harEntry.response.cookies = this._options.omitCookies ? [] : event.cookies.map(c => {
|
||||
return {
|
||||
...c,
|
||||
expires: c.expires === -1 ? undefined : new Date(c.expires)
|
||||
|
|
@ -207,15 +232,19 @@ export class HarTracer {
|
|||
if (!this._shouldIncludeEntryWithUrl(request.url()))
|
||||
return;
|
||||
const page = request.frame()._page;
|
||||
if (this._page && page !== this._page)
|
||||
return;
|
||||
const url = network.parsedURL(request.url());
|
||||
if (!url)
|
||||
return;
|
||||
|
||||
const pageEntry = this._ensurePageEntry(page);
|
||||
const harEntry = createHarEntry(request.method(), url, request.guid, request.frame().guid);
|
||||
harEntry.pageref = pageEntry.id;
|
||||
const pageEntry = this._createPageEntryIfNeeded(page);
|
||||
const harEntry = createHarEntry(request.method(), url, request.frame().guid, this._options);
|
||||
if (pageEntry)
|
||||
harEntry.pageref = pageEntry.id;
|
||||
harEntry.request.postData = this._postDataForRequest(request, this._options.content);
|
||||
harEntry.request.bodySize = request.bodySize();
|
||||
if (!this._options.omitSizes)
|
||||
harEntry.request.bodySize = request.bodySize();
|
||||
if (request.redirectedFrom()) {
|
||||
const fromEntry = this._entryForRequest(request.redirectedFrom()!);
|
||||
if (fromEntry)
|
||||
|
|
@ -229,16 +258,16 @@ export class HarTracer {
|
|||
private async _onRequestFinished(request: network.Request, response: network.Response | null) {
|
||||
if (!response)
|
||||
return;
|
||||
const page = request.frame()._page;
|
||||
const harEntry = this._entryForRequest(request);
|
||||
if (!harEntry)
|
||||
return;
|
||||
const page = request.frame()._page;
|
||||
|
||||
const httpVersion = response.httpVersion();
|
||||
harEntry.request.httpVersion = httpVersion;
|
||||
harEntry.response.httpVersion = httpVersion;
|
||||
|
||||
const compressionCalculationBarrier = {
|
||||
const compressionCalculationBarrier = this._options.omitSizes ? undefined : {
|
||||
_encodedBodySize: -1,
|
||||
_decodedBodySize: -1,
|
||||
barrier: new ManualPromise<void>(),
|
||||
|
|
@ -257,32 +286,36 @@ export class HarTracer {
|
|||
this._check();
|
||||
}
|
||||
};
|
||||
this._addBarrier(page, compressionCalculationBarrier.barrier);
|
||||
if (compressionCalculationBarrier)
|
||||
this._addBarrier(page, compressionCalculationBarrier.barrier);
|
||||
|
||||
const promise = response.body().then(buffer => {
|
||||
if (this._options.skipScripts && request.resourceType() === 'script') {
|
||||
compressionCalculationBarrier.setDecodedBodySize(0);
|
||||
compressionCalculationBarrier?.setDecodedBodySize(0);
|
||||
return;
|
||||
}
|
||||
|
||||
const content = harEntry.response.content;
|
||||
compressionCalculationBarrier.setDecodedBodySize(buffer.length);
|
||||
compressionCalculationBarrier?.setDecodedBodySize(buffer.length);
|
||||
this._storeResponseContent(buffer, content, request.resourceType());
|
||||
}).catch(() => {
|
||||
compressionCalculationBarrier.setDecodedBodySize(0);
|
||||
compressionCalculationBarrier?.setDecodedBodySize(0);
|
||||
}).then(() => {
|
||||
if (this._started)
|
||||
this._delegate.onEntryFinished(harEntry);
|
||||
});
|
||||
this._addBarrier(page, promise);
|
||||
this._addBarrier(page, response.sizes().then(sizes => {
|
||||
harEntry.response.bodySize = sizes.responseBodySize;
|
||||
harEntry.response.headersSize = sizes.responseHeadersSize;
|
||||
// Fallback for WebKit by calculating it manually
|
||||
harEntry.response._transferSize = response.request().responseSize.transferSize || (sizes.responseHeadersSize + sizes.responseBodySize);
|
||||
harEntry.request.headersSize = sizes.requestHeadersSize;
|
||||
compressionCalculationBarrier.setEncodedBodySize(sizes.responseBodySize);
|
||||
}));
|
||||
|
||||
if (!this._options.omitSizes) {
|
||||
this._addBarrier(page, response.sizes().then(sizes => {
|
||||
harEntry.response.bodySize = sizes.responseBodySize;
|
||||
harEntry.response.headersSize = sizes.responseHeadersSize;
|
||||
// Fallback for WebKit by calculating it manually
|
||||
harEntry.response._transferSize = response.request().responseSize.transferSize || (sizes.responseHeadersSize + sizes.responseBodySize);
|
||||
harEntry.request.headersSize = sizes.requestHeadersSize;
|
||||
compressionCalculationBarrier?.setEncodedBodySize(sizes.responseBodySize);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
private async _onRequestFailed(request: network.Request) {
|
||||
|
|
@ -301,7 +334,10 @@ export class HarTracer {
|
|||
content.size = 0;
|
||||
return;
|
||||
}
|
||||
content.size = buffer.length;
|
||||
|
||||
if (!this._options.omitSizes)
|
||||
content.size = buffer.length;
|
||||
|
||||
if (this._options.content === 'embed') {
|
||||
// Sometimes, we can receive a font/media file with textual mime type. Browser
|
||||
// still interprets them correctly, but the 'content-type' header is obviously wrong.
|
||||
|
|
@ -312,18 +348,22 @@ export class HarTracer {
|
|||
content.encoding = 'base64';
|
||||
}
|
||||
} else if (this._options.content === 'attach') {
|
||||
content._sha1 = calculateSha1(buffer) + '.' + (mime.getExtension(content.mimeType) || 'dat');
|
||||
const sha1 = calculateSha1(buffer) + '.' + (mime.getExtension(content.mimeType) || 'dat');
|
||||
if (this._options.includeTraceInfo)
|
||||
content._sha1 = sha1;
|
||||
else
|
||||
content._file = sha1;
|
||||
if (this._started)
|
||||
this._delegate.onContentBlob(content._sha1, buffer);
|
||||
this._delegate.onContentBlob(sha1, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
private _onResponse(response: network.Response) {
|
||||
const page = response.frame()._page;
|
||||
const pageEntry = this._ensurePageEntry(page);
|
||||
const harEntry = this._entryForRequest(response.request());
|
||||
if (!harEntry)
|
||||
return;
|
||||
const page = response.frame()._page;
|
||||
const pageEntry = this._createPageEntryIfNeeded(page);
|
||||
const request = response.request();
|
||||
|
||||
harEntry.response = {
|
||||
|
|
@ -340,43 +380,56 @@ export class HarTracer {
|
|||
headersSize: -1,
|
||||
bodySize: -1,
|
||||
redirectURL: '',
|
||||
_transferSize: -1
|
||||
_transferSize: this._options.omitSizes ? undefined : -1
|
||||
};
|
||||
const timing = response.timing();
|
||||
if (pageEntry.startedDateTime.valueOf() > timing.startTime)
|
||||
pageEntry.startedDateTime = new Date(timing.startTime);
|
||||
const dns = timing.domainLookupEnd !== -1 ? helper.millisToRoundishMillis(timing.domainLookupEnd - timing.domainLookupStart) : -1;
|
||||
const connect = timing.connectEnd !== -1 ? helper.millisToRoundishMillis(timing.connectEnd - timing.connectStart) : -1;
|
||||
const ssl = timing.connectEnd !== -1 ? helper.millisToRoundishMillis(timing.connectEnd - timing.secureConnectionStart) : -1;
|
||||
const wait = timing.responseStart !== -1 ? helper.millisToRoundishMillis(timing.responseStart - timing.requestStart) : -1;
|
||||
const receive = response.request()._responseEndTiming !== -1 ? helper.millisToRoundishMillis(response.request()._responseEndTiming - timing.responseStart) : -1;
|
||||
harEntry.timings = {
|
||||
dns,
|
||||
connect,
|
||||
ssl,
|
||||
send: 0,
|
||||
wait,
|
||||
receive,
|
||||
};
|
||||
harEntry.time = [dns, connect, ssl, wait, receive].reduce((pre, cur) => cur > 0 ? cur + pre : pre, 0);
|
||||
this._addBarrier(page, response.serverAddr().then(server => {
|
||||
if (server?.ipAddress)
|
||||
harEntry.serverIPAddress = server.ipAddress;
|
||||
if (server?.port)
|
||||
harEntry._serverPort = server.port;
|
||||
}));
|
||||
this._addBarrier(page, response.securityDetails().then(details => {
|
||||
if (details)
|
||||
harEntry._securityDetails = details;
|
||||
}));
|
||||
|
||||
if (!this._options.omitTiming) {
|
||||
const timing = response.timing();
|
||||
if (pageEntry && pageEntry.startedDateTime.valueOf() > timing.startTime)
|
||||
pageEntry.startedDateTime = new Date(timing.startTime);
|
||||
const dns = timing.domainLookupEnd !== -1 ? helper.millisToRoundishMillis(timing.domainLookupEnd - timing.domainLookupStart) : -1;
|
||||
const connect = timing.connectEnd !== -1 ? helper.millisToRoundishMillis(timing.connectEnd - timing.connectStart) : -1;
|
||||
const ssl = timing.connectEnd !== -1 ? helper.millisToRoundishMillis(timing.connectEnd - timing.secureConnectionStart) : -1;
|
||||
const wait = timing.responseStart !== -1 ? helper.millisToRoundishMillis(timing.responseStart - timing.requestStart) : -1;
|
||||
const receive = response.request()._responseEndTiming !== -1 ? helper.millisToRoundishMillis(response.request()._responseEndTiming - timing.responseStart) : -1;
|
||||
|
||||
harEntry.timings = {
|
||||
dns,
|
||||
connect,
|
||||
ssl,
|
||||
send: 0,
|
||||
wait,
|
||||
receive,
|
||||
};
|
||||
harEntry.time = [dns, connect, ssl, wait, receive].reduce((pre, cur) => cur > 0 ? cur + pre : pre, 0);
|
||||
}
|
||||
|
||||
if (!this._options.omitServerIP) {
|
||||
this._addBarrier(page, response.serverAddr().then(server => {
|
||||
if (server?.ipAddress)
|
||||
harEntry.serverIPAddress = server.ipAddress;
|
||||
if (server?.port)
|
||||
harEntry._serverPort = server.port;
|
||||
}));
|
||||
}
|
||||
if (!this._options.omitSecurityDetails) {
|
||||
this._addBarrier(page, response.securityDetails().then(details => {
|
||||
if (details)
|
||||
harEntry._securityDetails = details;
|
||||
}));
|
||||
}
|
||||
this._addBarrier(page, request.rawRequestHeaders().then(headers => {
|
||||
for (const header of headers.filter(header => header.name.toLowerCase() === 'cookie'))
|
||||
harEntry.request.cookies.push(...header.value.split(';').map(parseCookie));
|
||||
if (!this._options.omitCookies) {
|
||||
for (const header of headers.filter(header => header.name.toLowerCase() === 'cookie'))
|
||||
harEntry.request.cookies.push(...header.value.split(';').map(parseCookie));
|
||||
}
|
||||
harEntry.request.headers = headers;
|
||||
}));
|
||||
this._addBarrier(page, response.rawResponseHeaders().then(headers => {
|
||||
for (const header of headers.filter(header => header.name.toLowerCase() === 'set-cookie'))
|
||||
harEntry.response.cookies.push(parseCookie(header.value));
|
||||
if (!this._options.omitCookies) {
|
||||
for (const header of headers.filter(header => header.name.toLowerCase() === 'set-cookie'))
|
||||
harEntry.response.cookies.push(parseCookie(header.value));
|
||||
}
|
||||
harEntry.response.headers = headers;
|
||||
const contentType = headers.find(header => header.name.toLowerCase() === 'content-type');
|
||||
if (contentType)
|
||||
|
|
@ -404,18 +457,20 @@ export class HarTracer {
|
|||
name: context?._browser.options.name || '',
|
||||
version: context?._browser.version() || ''
|
||||
},
|
||||
pages: Array.from(this._pageEntries.values()),
|
||||
pages: this._pageEntries.size ? Array.from(this._pageEntries.values()) : undefined,
|
||||
entries: [],
|
||||
};
|
||||
for (const pageEntry of log.pages) {
|
||||
if (pageEntry.pageTimings.onContentLoad >= 0)
|
||||
pageEntry.pageTimings.onContentLoad -= pageEntry.startedDateTime.valueOf();
|
||||
else
|
||||
pageEntry.pageTimings.onContentLoad = -1;
|
||||
if (pageEntry.pageTimings.onLoad >= 0)
|
||||
pageEntry.pageTimings.onLoad -= pageEntry.startedDateTime.valueOf();
|
||||
else
|
||||
pageEntry.pageTimings.onLoad = -1;
|
||||
if (!this._options.omitTiming) {
|
||||
for (const pageEntry of log.pages || []) {
|
||||
if (typeof pageEntry.pageTimings.onContentLoad === 'number' && pageEntry.pageTimings.onContentLoad >= 0)
|
||||
pageEntry.pageTimings.onContentLoad -= pageEntry.startedDateTime.valueOf();
|
||||
else
|
||||
pageEntry.pageTimings.onContentLoad = -1;
|
||||
if (typeof pageEntry.pageTimings.onLoad === 'number' && pageEntry.pageTimings.onLoad >= 0)
|
||||
pageEntry.pageTimings.onLoad -= pageEntry.startedDateTime.valueOf();
|
||||
else
|
||||
pageEntry.pageTimings.onLoad = -1;
|
||||
}
|
||||
}
|
||||
this._pageEntries.clear();
|
||||
return log;
|
||||
|
|
@ -446,8 +501,12 @@ export class HarTracer {
|
|||
result.text = postData.toString();
|
||||
|
||||
if (content === 'attach') {
|
||||
result._sha1 = calculateSha1(postData) + '.' + (mime.getExtension(contentType) || 'dat');
|
||||
this._delegate.onContentBlob(result._sha1, postData);
|
||||
const sha1 = calculateSha1(postData) + '.' + (mime.getExtension(contentType) || 'dat');
|
||||
if (this._options.includeTraceInfo)
|
||||
result._sha1 = sha1;
|
||||
else
|
||||
result._file = sha1;
|
||||
this._delegate.onContentBlob(sha1, postData);
|
||||
}
|
||||
|
||||
if (contentType === 'application/x-www-form-urlencoded') {
|
||||
|
|
@ -461,11 +520,10 @@ export class HarTracer {
|
|||
|
||||
}
|
||||
|
||||
function createHarEntry(method: string, url: URL, requestref: string, frameref: string): har.Entry {
|
||||
function createHarEntry(method: string, url: URL, frameref: string | undefined, options: HarTracerOptions): har.Entry {
|
||||
const harEntry: har.Entry = {
|
||||
_requestref: requestref,
|
||||
_frameref: frameref,
|
||||
_monotonicTime: monotonicTime(),
|
||||
_frameref: options.includeTraceInfo ? frameref : undefined,
|
||||
_monotonicTime: options.includeTraceInfo ? monotonicTime() : undefined,
|
||||
startedDateTime: new Date(),
|
||||
time: -1,
|
||||
request: {
|
||||
|
|
@ -476,7 +534,7 @@ function createHarEntry(method: string, url: URL, requestref: string, frameref:
|
|||
headers: [],
|
||||
queryString: [...url.searchParams].map(e => ({ name: e[0], value: e[1] })),
|
||||
headersSize: -1,
|
||||
bodySize: 0,
|
||||
bodySize: -1,
|
||||
},
|
||||
response: {
|
||||
status: -1,
|
||||
|
|
@ -491,12 +549,9 @@ function createHarEntry(method: string, url: URL, requestref: string, frameref:
|
|||
headersSize: -1,
|
||||
bodySize: -1,
|
||||
redirectURL: '',
|
||||
_transferSize: -1
|
||||
},
|
||||
cache: {
|
||||
beforeRequest: null,
|
||||
afterRequest: null,
|
||||
_transferSize: options.omitSizes ? undefined : -1
|
||||
},
|
||||
cache: {},
|
||||
timings: {
|
||||
send: -1,
|
||||
wait: -1,
|
||||
|
|
|
|||
|
|
@ -244,13 +244,17 @@ export class Route extends SdkObject {
|
|||
return this._request;
|
||||
}
|
||||
|
||||
async abort(errorCode: string = 'failed', redirectAbortedNavigationToUrl?: string) {
|
||||
async abort(errorCode: string = 'failed') {
|
||||
this._startHandling();
|
||||
if (redirectAbortedNavigationToUrl)
|
||||
this._request.frame().redirectNavigationAfterAbort(redirectAbortedNavigationToUrl, this._request._documentId!);
|
||||
await this._delegate.abort(errorCode);
|
||||
}
|
||||
|
||||
async redirectNavigationRequest(url: string) {
|
||||
this._startHandling();
|
||||
assert(this._request.isNavigationRequest());
|
||||
this._request.frame().redirectNavigation(url, this._request._documentId!, this._request.headerValue('referer'));
|
||||
}
|
||||
|
||||
async fulfill(overrides: channels.RouteFulfillParams) {
|
||||
this._startHandling();
|
||||
let body = overrides.body;
|
||||
|
|
|
|||
|
|
@ -88,8 +88,9 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
|
|||
super(context, 'Tracing');
|
||||
this._context = context;
|
||||
this._precreatedTracesDir = tracesDir;
|
||||
this._harTracer = new HarTracer(context, this, {
|
||||
this._harTracer = new HarTracer(context, null, this, {
|
||||
content: 'attach',
|
||||
includeTraceInfo: true,
|
||||
waitForContentOnStop: false,
|
||||
skipScripts: true,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export class InMemorySnapshotter extends BaseSnapshotStorage implements Snapshot
|
|||
constructor(context: BrowserContext) {
|
||||
super();
|
||||
this._snapshotter = new Snapshotter(context, this);
|
||||
this._harTracer = new HarTracer(context, this, { content: 'attach', waitForContentOnStop: false, skipScripts: true });
|
||||
this._harTracer = new HarTracer(context, null, this, { content: 'attach', includeTraceInfo: true, waitForContentOnStop: false, skipScripts: true });
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
|
|
|
|||
167
packages/playwright-core/types/har.d.ts
vendored
167
packages/playwright-core/types/har.d.ts
vendored
|
|
@ -1,167 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// see http://www.softwareishard.com/blog/har-12-spec/
|
||||
export type HARFile = {
|
||||
log: HARLog;
|
||||
}
|
||||
|
||||
export type HARLog = {
|
||||
version: string;
|
||||
creator: HARCreator;
|
||||
browser?: HARBrowser;
|
||||
pages?: HARPage[];
|
||||
entries: HAREntry[];
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type HARCreator = {
|
||||
name: string;
|
||||
version: string;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type HARBrowser = {
|
||||
name: string;
|
||||
version: string;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type HARPage = {
|
||||
startedDateTime: string;
|
||||
id: string;
|
||||
title: string;
|
||||
pageTimings: HARPageTimings;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type HARPageTimings = {
|
||||
onContentLoad?: number;
|
||||
onLoad?: number;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type HAREntry = {
|
||||
pageref?: string;
|
||||
startedDateTime: string;
|
||||
time: number;
|
||||
request: HARRequest;
|
||||
response: HARResponse;
|
||||
cache: HARCache;
|
||||
timings: HARTimings;
|
||||
serverIPAddress?: string;
|
||||
connection?: string;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type HARRequest = {
|
||||
method: string;
|
||||
url: string;
|
||||
httpVersion: string;
|
||||
cookies: HARCookie[];
|
||||
headers: HARHeader[];
|
||||
queryString: HARQueryParameter[];
|
||||
postData?: HARPostData;
|
||||
headersSize: number;
|
||||
bodySize: number;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type HARResponse = {
|
||||
status: number;
|
||||
statusText: string;
|
||||
httpVersion: string;
|
||||
cookies: HARCookie[];
|
||||
headers: HARHeader[];
|
||||
content: HARContent;
|
||||
redirectURL: string;
|
||||
headersSize: number;
|
||||
bodySize: number;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type HARCookie = {
|
||||
name: string;
|
||||
value: string;
|
||||
path?: string;
|
||||
domain?: string;
|
||||
expires?: string;
|
||||
httpOnly?: boolean;
|
||||
secure?: boolean;
|
||||
sameSite?: string;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type HARHeader = {
|
||||
name: string;
|
||||
value: string;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type HARQueryParameter = {
|
||||
name: string;
|
||||
value: string;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type HARPostData = {
|
||||
mimeType: string;
|
||||
params: HARParam[];
|
||||
text: string;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type HARParam = {
|
||||
name: string;
|
||||
value?: string;
|
||||
fileName?: string;
|
||||
contentType?: string;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type HARContent = {
|
||||
size: number;
|
||||
compression?: number;
|
||||
mimeType: string;
|
||||
text?: string;
|
||||
encoding?: string;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type HARCache = {
|
||||
beforeRequest?: HARCacheState;
|
||||
afterRequest?: HARCacheState;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type HARCacheState = {
|
||||
expires?: string;
|
||||
lastAccess: string;
|
||||
eTag: string;
|
||||
hitCount: number;
|
||||
comment?: string;
|
||||
};
|
||||
|
||||
export type HARTimings = {
|
||||
blocked?: number;
|
||||
dns?: number;
|
||||
connect?: number;
|
||||
send: number;
|
||||
wait: number;
|
||||
receive: number;
|
||||
ssl?: number;
|
||||
comment?: string;
|
||||
};
|
||||
97
packages/playwright-core/types/protocol.d.ts
vendored
97
packages/playwright-core/types/protocol.d.ts
vendored
|
|
@ -810,7 +810,7 @@ CORS RFC1918 enforcement.
|
|||
export type AttributionReportingIssueType = "PermissionPolicyDisabled"|"AttributionSourceUntrustworthyOrigin"|"AttributionUntrustworthyOrigin"|"InvalidHeader";
|
||||
/**
|
||||
* Details for issues around "Attribution Reporting API" usage.
|
||||
Explainer: https://github.com/WICG/conversion-measurement-api
|
||||
Explainer: https://github.com/WICG/attribution-reporting-api
|
||||
*/
|
||||
export interface AttributionReportingIssueDetails {
|
||||
violationType: AttributionReportingIssueType;
|
||||
|
|
@ -849,7 +849,7 @@ instead of "limited-quirks".
|
|||
errorType: GenericIssueErrorType;
|
||||
frameId?: Page.FrameId;
|
||||
}
|
||||
export type DeprecationIssueType = "AuthorizationCoveredByWildcard"|"CanRequestURLHTTPContainingNewline"|"ChromeLoadTimesConnectionInfo"|"ChromeLoadTimesFirstPaintAfterLoadTime"|"ChromeLoadTimesWasAlternateProtocolAvailable"|"CookieWithTruncatingChar"|"CrossOriginAccessBasedOnDocumentDomain"|"CrossOriginWindowAlert"|"CrossOriginWindowConfirm"|"CSSSelectorInternalMediaControlsOverlayCastButton"|"CustomCursorIntersectsViewport"|"DeprecationExample"|"DocumentDomainSettingWithoutOriginAgentClusterHeader"|"EventPath"|"GeolocationInsecureOrigin"|"GeolocationInsecureOriginDeprecatedNotRemoved"|"GetUserMediaInsecureOrigin"|"HostCandidateAttributeGetter"|"InsecurePrivateNetworkSubresourceRequest"|"LegacyConstraintGoogIPv6"|"LocalCSSFileExtensionRejected"|"MediaElementAudioSourceNode"|"MediaSourceAbortRemove"|"MediaSourceDurationTruncatingBuffered"|"NoSysexWebMIDIWithoutPermission"|"NotificationInsecureOrigin"|"NotificationPermissionRequestedIframe"|"ObsoleteWebRtcCipherSuite"|"PaymentRequestBasicCard"|"PaymentRequestShowWithoutGesture"|"PictureSourceSrc"|"PrefixedCancelAnimationFrame"|"PrefixedRequestAnimationFrame"|"PrefixedStorageInfo"|"PrefixedVideoDisplayingFullscreen"|"PrefixedVideoEnterFullscreen"|"PrefixedVideoEnterFullScreen"|"PrefixedVideoExitFullscreen"|"PrefixedVideoExitFullScreen"|"PrefixedVideoSupportsFullscreen"|"RangeExpand"|"RequestedSubresourceWithEmbeddedCredentials"|"RTCConstraintEnableDtlsSrtpFalse"|"RTCConstraintEnableDtlsSrtpTrue"|"RTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics"|"RTCPeerConnectionSdpSemanticsPlanB"|"RtcpMuxPolicyNegotiate"|"RTPDataChannel"|"SharedArrayBufferConstructedWithoutIsolation"|"TextToSpeech_DisallowedByAutoplay"|"V8SharedArrayBufferConstructedInExtensionWithoutIsolation"|"XHRJSONEncodingDetection"|"XMLHttpRequestSynchronousInNonWorkerOutsideBeforeUnload"|"XRSupportsSession";
|
||||
export type DeprecationIssueType = "AuthorizationCoveredByWildcard"|"CanRequestURLHTTPContainingNewline"|"ChromeLoadTimesConnectionInfo"|"ChromeLoadTimesFirstPaintAfterLoadTime"|"ChromeLoadTimesWasAlternateProtocolAvailable"|"CookieWithTruncatingChar"|"CrossOriginAccessBasedOnDocumentDomain"|"CrossOriginWindowAlert"|"CrossOriginWindowConfirm"|"CSSSelectorInternalMediaControlsOverlayCastButton"|"DeprecationExample"|"DocumentDomainSettingWithoutOriginAgentClusterHeader"|"EventPath"|"GeolocationInsecureOrigin"|"GeolocationInsecureOriginDeprecatedNotRemoved"|"GetUserMediaInsecureOrigin"|"HostCandidateAttributeGetter"|"IdentityInCanMakePaymentEvent"|"InsecurePrivateNetworkSubresourceRequest"|"LegacyConstraintGoogIPv6"|"LocalCSSFileExtensionRejected"|"MediaSourceAbortRemove"|"MediaSourceDurationTruncatingBuffered"|"NoSysexWebMIDIWithoutPermission"|"NotificationInsecureOrigin"|"NotificationPermissionRequestedIframe"|"ObsoleteWebRtcCipherSuite"|"OpenWebDatabaseInsecureContext"|"PictureSourceSrc"|"PrefixedCancelAnimationFrame"|"PrefixedRequestAnimationFrame"|"PrefixedStorageInfo"|"PrefixedVideoDisplayingFullscreen"|"PrefixedVideoEnterFullscreen"|"PrefixedVideoEnterFullScreen"|"PrefixedVideoExitFullscreen"|"PrefixedVideoExitFullScreen"|"PrefixedVideoSupportsFullscreen"|"RangeExpand"|"RequestedSubresourceWithEmbeddedCredentials"|"RTCConstraintEnableDtlsSrtpFalse"|"RTCConstraintEnableDtlsSrtpTrue"|"RTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics"|"RTCPeerConnectionSdpSemanticsPlanB"|"RtcpMuxPolicyNegotiate"|"SharedArrayBufferConstructedWithoutIsolation"|"TextToSpeech_DisallowedByAutoplay"|"V8SharedArrayBufferConstructedInExtensionWithoutIsolation"|"XHRJSONEncodingDetection"|"XMLHttpRequestSynchronousInNonWorkerOutsideBeforeUnload"|"XRSupportsSession";
|
||||
/**
|
||||
* This issue tracks information needed to print a deprecation message.
|
||||
https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/frame/third_party/blink/renderer/core/frame/deprecation/README.md
|
||||
|
|
@ -1532,6 +1532,10 @@ inspector" rules), "regular" for regular stylesheets.
|
|||
* Pseudo element type.
|
||||
*/
|
||||
pseudoType: DOM.PseudoType;
|
||||
/**
|
||||
* Pseudo element custom ident.
|
||||
*/
|
||||
pseudoIdentifier?: string;
|
||||
/**
|
||||
* Matches of CSS rules applicable to the pseudo style.
|
||||
*/
|
||||
|
|
@ -2068,6 +2072,10 @@ and additional information such as platformFontFamily and fontVariationAxes.
|
|||
* The font-stretch.
|
||||
*/
|
||||
fontStretch: string;
|
||||
/**
|
||||
* The font-display.
|
||||
*/
|
||||
fontDisplay: string;
|
||||
/**
|
||||
* The unicode-range.
|
||||
*/
|
||||
|
|
@ -2958,6 +2966,11 @@ fire DOM events for nodes known to the client.
|
|||
* Pseudo element type for this node.
|
||||
*/
|
||||
pseudoType?: PseudoType;
|
||||
/**
|
||||
* Pseudo element identifier for this node. Only present if there is a
|
||||
valid pseudoType.
|
||||
*/
|
||||
pseudoIdentifier?: string;
|
||||
/**
|
||||
* Shadow root type.
|
||||
*/
|
||||
|
|
@ -2997,6 +3010,7 @@ The property is always undefined now.
|
|||
*/
|
||||
isSVG?: boolean;
|
||||
compatibilityMode?: CompatibilityMode;
|
||||
assignedSlot?: BackendNode;
|
||||
}
|
||||
/**
|
||||
* A structure holding an RGBA color.
|
||||
|
|
@ -4706,6 +4720,11 @@ getSnapshot was true.
|
|||
* Type of a pseudo element node.
|
||||
*/
|
||||
pseudoType?: RareStringData;
|
||||
/**
|
||||
* Pseudo element identifier for this node. Only present if there is a
|
||||
valid pseudoType.
|
||||
*/
|
||||
pseudoIdentifier?: RareStringData;
|
||||
/**
|
||||
* Whether this DOM node responds to mouse clicks. This includes nodes that have had click
|
||||
event listeners attached via JavaScript as well as anchor tags that naturally navigate when
|
||||
|
|
@ -4978,12 +4997,6 @@ The final text color opacity is computed based on the opacity of all overlapping
|
|||
}
|
||||
export type setDOMStorageItemReturnValue = {
|
||||
}
|
||||
export type getStorageKeyForFrameParameters = {
|
||||
frameId: Page.FrameId;
|
||||
}
|
||||
export type getStorageKeyForFrameReturnValue = {
|
||||
storageKey: SerializedStorageKey;
|
||||
}
|
||||
}
|
||||
|
||||
export module Database {
|
||||
|
|
@ -5533,6 +5546,14 @@ on Android.
|
|||
}
|
||||
export type setDisabledImageTypesReturnValue = {
|
||||
}
|
||||
export type setHardwareConcurrencyOverrideParameters = {
|
||||
/**
|
||||
* Hardware concurrency to report
|
||||
*/
|
||||
hardwareConcurrency: number;
|
||||
}
|
||||
export type setHardwareConcurrencyOverrideReturnValue = {
|
||||
}
|
||||
/**
|
||||
* Allows overriding user agent with the given string.
|
||||
*/
|
||||
|
|
@ -8063,7 +8084,7 @@ the same request (but not for redirected requests).
|
|||
initiatorIPAddressSpace: IPAddressSpace;
|
||||
privateNetworkRequestPolicy: PrivateNetworkRequestPolicy;
|
||||
}
|
||||
export type CrossOriginOpenerPolicyValue = "SameOrigin"|"SameOriginAllowPopups"|"UnsafeNone"|"SameOriginPlusCoep"|"SameOriginAllowPopupsPlusCoep";
|
||||
export type CrossOriginOpenerPolicyValue = "SameOrigin"|"SameOriginAllowPopups"|"RestrictProperties"|"UnsafeNone"|"SameOriginPlusCoep"|"RestrictPropertiesPlusCoep";
|
||||
export interface CrossOriginOpenerPolicyStatus {
|
||||
value: CrossOriginOpenerPolicyValue;
|
||||
reportOnlyValue: CrossOriginOpenerPolicyValue;
|
||||
|
|
@ -10166,6 +10187,21 @@ Backend then generates 'inspectNodeRequested' event upon element selection.
|
|||
adFrameType: AdFrameType;
|
||||
explanations?: AdFrameExplanation[];
|
||||
}
|
||||
/**
|
||||
* Identifies the bottom-most script which caused the frame to be labelled
|
||||
as an ad.
|
||||
*/
|
||||
export interface AdScriptId {
|
||||
/**
|
||||
* Script Id of the bottom-most script which caused the frame to be labelled
|
||||
as an ad.
|
||||
*/
|
||||
scriptId: Runtime.ScriptId;
|
||||
/**
|
||||
* Id of adScriptId's debugger.
|
||||
*/
|
||||
debuggerId: Runtime.UniqueDebuggerId;
|
||||
}
|
||||
/**
|
||||
* Indicates whether the frame is a secure context and why it is the case.
|
||||
*/
|
||||
|
|
@ -10179,7 +10215,7 @@ Backend then generates 'inspectNodeRequested' event upon element selection.
|
|||
* All Permissions Policy features. This enum should match the one defined
|
||||
in third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5.
|
||||
*/
|
||||
export type PermissionsPolicyFeature = "accelerometer"|"ambient-light-sensor"|"attribution-reporting"|"autoplay"|"browsing-topics"|"camera"|"ch-dpr"|"ch-device-memory"|"ch-downlink"|"ch-ect"|"ch-prefers-color-scheme"|"ch-rtt"|"ch-save-data"|"ch-ua"|"ch-ua-arch"|"ch-ua-bitness"|"ch-ua-platform"|"ch-ua-model"|"ch-ua-mobile"|"ch-ua-full"|"ch-ua-full-version"|"ch-ua-full-version-list"|"ch-ua-platform-version"|"ch-ua-reduced"|"ch-ua-wow64"|"ch-viewport-height"|"ch-viewport-width"|"ch-width"|"clipboard-read"|"clipboard-write"|"cross-origin-isolated"|"direct-sockets"|"display-capture"|"document-domain"|"encrypted-media"|"execution-while-out-of-viewport"|"execution-while-not-rendered"|"focus-without-user-activation"|"fullscreen"|"frobulate"|"gamepad"|"geolocation"|"gyroscope"|"hid"|"idle-detection"|"interest-cohort"|"join-ad-interest-group"|"keyboard-map"|"local-fonts"|"magnetometer"|"microphone"|"midi"|"otp-credentials"|"payment"|"picture-in-picture"|"publickey-credentials-get"|"run-ad-auction"|"screen-wake-lock"|"serial"|"shared-autofill"|"storage-access-api"|"sync-xhr"|"trust-token-redemption"|"usb"|"vertical-scroll"|"web-share"|"window-placement"|"xr-spatial-tracking";
|
||||
export type PermissionsPolicyFeature = "accelerometer"|"ambient-light-sensor"|"attribution-reporting"|"autoplay"|"bluetooth"|"browsing-topics"|"camera"|"ch-dpr"|"ch-device-memory"|"ch-downlink"|"ch-ect"|"ch-prefers-color-scheme"|"ch-rtt"|"ch-save-data"|"ch-ua"|"ch-ua-arch"|"ch-ua-bitness"|"ch-ua-platform"|"ch-ua-model"|"ch-ua-mobile"|"ch-ua-full"|"ch-ua-full-version"|"ch-ua-full-version-list"|"ch-ua-platform-version"|"ch-ua-reduced"|"ch-ua-wow64"|"ch-viewport-height"|"ch-viewport-width"|"ch-width"|"clipboard-read"|"clipboard-write"|"cross-origin-isolated"|"direct-sockets"|"display-capture"|"document-domain"|"encrypted-media"|"execution-while-out-of-viewport"|"execution-while-not-rendered"|"focus-without-user-activation"|"fullscreen"|"frobulate"|"gamepad"|"geolocation"|"gyroscope"|"hid"|"idle-detection"|"interest-cohort"|"join-ad-interest-group"|"keyboard-map"|"local-fonts"|"magnetometer"|"microphone"|"midi"|"otp-credentials"|"payment"|"picture-in-picture"|"publickey-credentials-get"|"run-ad-auction"|"screen-wake-lock"|"serial"|"shared-autofill"|"storage-access-api"|"sync-xhr"|"trust-token-redemption"|"usb"|"vertical-scroll"|"web-share"|"window-placement"|"xr-spatial-tracking";
|
||||
/**
|
||||
* Reason for a permissions policy feature to be disabled.
|
||||
*/
|
||||
|
|
@ -10644,7 +10680,7 @@ Example URLs: http://www.google.com/file.html -> "google.com"
|
|||
/**
|
||||
* List of not restored reasons for back-forward cache.
|
||||
*/
|
||||
export type BackForwardCacheNotRestoredReason = "NotPrimaryMainFrame"|"BackForwardCacheDisabled"|"RelatedActiveContentsExist"|"HTTPStatusNotOK"|"SchemeNotHTTPOrHTTPS"|"Loading"|"WasGrantedMediaAccess"|"DisableForRenderFrameHostCalled"|"DomainNotAllowed"|"HTTPMethodNotGET"|"SubframeIsNavigating"|"Timeout"|"CacheLimit"|"JavaScriptExecution"|"RendererProcessKilled"|"RendererProcessCrashed"|"SchedulerTrackedFeatureUsed"|"ConflictingBrowsingInstance"|"CacheFlushed"|"ServiceWorkerVersionActivation"|"SessionRestored"|"ServiceWorkerPostMessage"|"EnteredBackForwardCacheBeforeServiceWorkerHostAdded"|"RenderFrameHostReused_SameSite"|"RenderFrameHostReused_CrossSite"|"ServiceWorkerClaim"|"IgnoreEventAndEvict"|"HaveInnerContents"|"TimeoutPuttingInCache"|"BackForwardCacheDisabledByLowMemory"|"BackForwardCacheDisabledByCommandLine"|"NetworkRequestDatapipeDrainedAsBytesConsumer"|"NetworkRequestRedirected"|"NetworkRequestTimeout"|"NetworkExceedsBufferLimit"|"NavigationCancelledWhileRestoring"|"NotMostRecentNavigationEntry"|"BackForwardCacheDisabledForPrerender"|"UserAgentOverrideDiffers"|"ForegroundCacheLimit"|"BrowsingInstanceNotSwapped"|"BackForwardCacheDisabledForDelegate"|"UnloadHandlerExistsInMainFrame"|"UnloadHandlerExistsInSubFrame"|"ServiceWorkerUnregistration"|"CacheControlNoStore"|"CacheControlNoStoreCookieModified"|"CacheControlNoStoreHTTPOnlyCookieModified"|"NoResponseHead"|"Unknown"|"ActivationNavigationsDisallowedForBug1234857"|"ErrorDocument"|"FencedFramesEmbedder"|"WebSocket"|"WebTransport"|"WebRTC"|"MainResourceHasCacheControlNoStore"|"MainResourceHasCacheControlNoCache"|"SubresourceHasCacheControlNoStore"|"SubresourceHasCacheControlNoCache"|"ContainsPlugins"|"DocumentLoaded"|"DedicatedWorkerOrWorklet"|"OutstandingNetworkRequestOthers"|"OutstandingIndexedDBTransaction"|"RequestedNotificationsPermission"|"RequestedMIDIPermission"|"RequestedAudioCapturePermission"|"RequestedVideoCapturePermission"|"RequestedBackForwardCacheBlockedSensors"|"RequestedBackgroundWorkPermission"|"BroadcastChannel"|"IndexedDBConnection"|"WebXR"|"SharedWorker"|"WebLocks"|"WebHID"|"WebShare"|"RequestedStorageAccessGrant"|"WebNfc"|"OutstandingNetworkRequestFetch"|"OutstandingNetworkRequestXHR"|"AppBanner"|"Printing"|"WebDatabase"|"PictureInPicture"|"Portal"|"SpeechRecognizer"|"IdleManager"|"PaymentManager"|"SpeechSynthesis"|"KeyboardLock"|"WebOTPService"|"OutstandingNetworkRequestDirectSocket"|"InjectedJavascript"|"InjectedStyleSheet"|"Dummy"|"ContentSecurityHandler"|"ContentWebAuthenticationAPI"|"ContentFileChooser"|"ContentSerial"|"ContentFileSystemAccess"|"ContentMediaDevicesDispatcherHost"|"ContentWebBluetooth"|"ContentWebUSB"|"ContentMediaSession"|"ContentMediaSessionService"|"ContentScreenReader"|"EmbedderPopupBlockerTabHelper"|"EmbedderSafeBrowsingTriggeredPopupBlocker"|"EmbedderSafeBrowsingThreatDetails"|"EmbedderAppBannerManager"|"EmbedderDomDistillerViewerSource"|"EmbedderDomDistillerSelfDeletingRequestDelegate"|"EmbedderOomInterventionTabHelper"|"EmbedderOfflinePage"|"EmbedderChromePasswordManagerClientBindCredentialManager"|"EmbedderPermissionRequestManager"|"EmbedderModalDialog"|"EmbedderExtensions"|"EmbedderExtensionMessaging"|"EmbedderExtensionMessagingForOpenPort"|"EmbedderExtensionSentMessageToCachedFrame";
|
||||
export type BackForwardCacheNotRestoredReason = "NotPrimaryMainFrame"|"BackForwardCacheDisabled"|"RelatedActiveContentsExist"|"HTTPStatusNotOK"|"SchemeNotHTTPOrHTTPS"|"Loading"|"WasGrantedMediaAccess"|"DisableForRenderFrameHostCalled"|"DomainNotAllowed"|"HTTPMethodNotGET"|"SubframeIsNavigating"|"Timeout"|"CacheLimit"|"JavaScriptExecution"|"RendererProcessKilled"|"RendererProcessCrashed"|"SchedulerTrackedFeatureUsed"|"ConflictingBrowsingInstance"|"CacheFlushed"|"ServiceWorkerVersionActivation"|"SessionRestored"|"ServiceWorkerPostMessage"|"EnteredBackForwardCacheBeforeServiceWorkerHostAdded"|"RenderFrameHostReused_SameSite"|"RenderFrameHostReused_CrossSite"|"ServiceWorkerClaim"|"IgnoreEventAndEvict"|"HaveInnerContents"|"TimeoutPuttingInCache"|"BackForwardCacheDisabledByLowMemory"|"BackForwardCacheDisabledByCommandLine"|"NetworkRequestDatapipeDrainedAsBytesConsumer"|"NetworkRequestRedirected"|"NetworkRequestTimeout"|"NetworkExceedsBufferLimit"|"NavigationCancelledWhileRestoring"|"NotMostRecentNavigationEntry"|"BackForwardCacheDisabledForPrerender"|"UserAgentOverrideDiffers"|"ForegroundCacheLimit"|"BrowsingInstanceNotSwapped"|"BackForwardCacheDisabledForDelegate"|"UnloadHandlerExistsInMainFrame"|"UnloadHandlerExistsInSubFrame"|"ServiceWorkerUnregistration"|"CacheControlNoStore"|"CacheControlNoStoreCookieModified"|"CacheControlNoStoreHTTPOnlyCookieModified"|"NoResponseHead"|"Unknown"|"ActivationNavigationsDisallowedForBug1234857"|"ErrorDocument"|"FencedFramesEmbedder"|"WebSocket"|"WebTransport"|"WebRTC"|"MainResourceHasCacheControlNoStore"|"MainResourceHasCacheControlNoCache"|"SubresourceHasCacheControlNoStore"|"SubresourceHasCacheControlNoCache"|"ContainsPlugins"|"DocumentLoaded"|"DedicatedWorkerOrWorklet"|"OutstandingNetworkRequestOthers"|"OutstandingIndexedDBTransaction"|"RequestedNotificationsPermission"|"RequestedMIDIPermission"|"RequestedAudioCapturePermission"|"RequestedVideoCapturePermission"|"RequestedBackForwardCacheBlockedSensors"|"RequestedBackgroundWorkPermission"|"BroadcastChannel"|"IndexedDBConnection"|"WebXR"|"SharedWorker"|"WebLocks"|"WebHID"|"WebShare"|"RequestedStorageAccessGrant"|"WebNfc"|"OutstandingNetworkRequestFetch"|"OutstandingNetworkRequestXHR"|"AppBanner"|"Printing"|"WebDatabase"|"PictureInPicture"|"Portal"|"SpeechRecognizer"|"IdleManager"|"PaymentManager"|"SpeechSynthesis"|"KeyboardLock"|"WebOTPService"|"OutstandingNetworkRequestDirectSocket"|"InjectedJavascript"|"InjectedStyleSheet"|"Dummy"|"ContentSecurityHandler"|"ContentWebAuthenticationAPI"|"ContentFileChooser"|"ContentSerial"|"ContentFileSystemAccess"|"ContentMediaDevicesDispatcherHost"|"ContentWebBluetooth"|"ContentWebUSB"|"ContentMediaSessionService"|"ContentScreenReader"|"EmbedderPopupBlockerTabHelper"|"EmbedderSafeBrowsingTriggeredPopupBlocker"|"EmbedderSafeBrowsingThreatDetails"|"EmbedderAppBannerManager"|"EmbedderDomDistillerViewerSource"|"EmbedderDomDistillerSelfDeletingRequestDelegate"|"EmbedderOomInterventionTabHelper"|"EmbedderOfflinePage"|"EmbedderChromePasswordManagerClientBindCredentialManager"|"EmbedderPermissionRequestManager"|"EmbedderModalDialog"|"EmbedderExtensions"|"EmbedderExtensionMessaging"|"EmbedderExtensionMessagingForOpenPort"|"EmbedderExtensionSentMessageToCachedFrame";
|
||||
/**
|
||||
* Types of not restored reasons for back-forward cache.
|
||||
*/
|
||||
|
|
@ -10720,6 +10756,11 @@ dependent on the reason:
|
|||
* JavaScript stack trace of when frame was attached, only set if frame initiated from script.
|
||||
*/
|
||||
stack?: Runtime.StackTrace;
|
||||
/**
|
||||
* Identifies the bottom-most script which caused the frame to be labelled
|
||||
as an ad. Only sent if frame is labelled as an ad and id is available.
|
||||
*/
|
||||
adScriptId?: AdScriptId;
|
||||
}
|
||||
/**
|
||||
* Fired when frame no longer has a scheduled navigation.
|
||||
|
|
@ -12668,6 +12709,15 @@ Tokens from that issuer.
|
|||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a storage key given a frame id.
|
||||
*/
|
||||
export type getStorageKeyForFrameParameters = {
|
||||
frameId: Page.FrameId;
|
||||
}
|
||||
export type getStorageKeyForFrameReturnValue = {
|
||||
storageKey: SerializedStorageKey;
|
||||
}
|
||||
/**
|
||||
* Clears storage for origin.
|
||||
*/
|
||||
|
|
@ -15248,13 +15298,30 @@ of scripts is used as end of range.
|
|||
export type removeBreakpointReturnValue = {
|
||||
}
|
||||
/**
|
||||
* Restarts particular call frame from the beginning.
|
||||
* Restarts particular call frame from the beginning. The old, deprecated
|
||||
behavior of `restartFrame` is to stay paused and allow further CDP commands
|
||||
after a restart was scheduled. This can cause problems with restarting, so
|
||||
we now continue execution immediatly after it has been scheduled until we
|
||||
reach the beginning of the restarted frame.
|
||||
|
||||
To stay back-wards compatible, `restartFrame` now expects a `mode`
|
||||
parameter to be present. If the `mode` parameter is missing, `restartFrame`
|
||||
errors out.
|
||||
|
||||
The various return values are deprecated and `callFrames` is always empty.
|
||||
Use the call frames from the `Debugger#paused` events instead, that fires
|
||||
once V8 pauses at the beginning of the restarted function.
|
||||
*/
|
||||
export type restartFrameParameters = {
|
||||
/**
|
||||
* Call frame identifier to evaluate on.
|
||||
*/
|
||||
callFrameId: CallFrameId;
|
||||
/**
|
||||
* The `mode` parameter must be present and set to 'StepInto', otherwise
|
||||
`restartFrame` will error out.
|
||||
*/
|
||||
mode?: "StepInto";
|
||||
}
|
||||
export type restartFrameReturnValue = {
|
||||
/**
|
||||
|
|
@ -17514,7 +17581,6 @@ Error was thrown.
|
|||
"DOMStorage.getDOMStorageItems": DOMStorage.getDOMStorageItemsParameters;
|
||||
"DOMStorage.removeDOMStorageItem": DOMStorage.removeDOMStorageItemParameters;
|
||||
"DOMStorage.setDOMStorageItem": DOMStorage.setDOMStorageItemParameters;
|
||||
"DOMStorage.getStorageKeyForFrame": DOMStorage.getStorageKeyForFrameParameters;
|
||||
"Database.disable": Database.disableParameters;
|
||||
"Database.enable": Database.enableParameters;
|
||||
"Database.executeSQL": Database.executeSQLParameters;
|
||||
|
|
@ -17547,6 +17613,7 @@ Error was thrown.
|
|||
"Emulation.setTimezoneOverride": Emulation.setTimezoneOverrideParameters;
|
||||
"Emulation.setVisibleSize": Emulation.setVisibleSizeParameters;
|
||||
"Emulation.setDisabledImageTypes": Emulation.setDisabledImageTypesParameters;
|
||||
"Emulation.setHardwareConcurrencyOverride": Emulation.setHardwareConcurrencyOverrideParameters;
|
||||
"Emulation.setUserAgentOverride": Emulation.setUserAgentOverrideParameters;
|
||||
"Emulation.setAutomationOverride": Emulation.setAutomationOverrideParameters;
|
||||
"HeadlessExperimental.beginFrame": HeadlessExperimental.beginFrameParameters;
|
||||
|
|
@ -17744,6 +17811,7 @@ Error was thrown.
|
|||
"ServiceWorker.stopWorker": ServiceWorker.stopWorkerParameters;
|
||||
"ServiceWorker.unregister": ServiceWorker.unregisterParameters;
|
||||
"ServiceWorker.updateRegistration": ServiceWorker.updateRegistrationParameters;
|
||||
"Storage.getStorageKeyForFrame": Storage.getStorageKeyForFrameParameters;
|
||||
"Storage.clearDataForOrigin": Storage.clearDataForOriginParameters;
|
||||
"Storage.getCookies": Storage.getCookiesParameters;
|
||||
"Storage.setCookies": Storage.setCookiesParameters;
|
||||
|
|
@ -18044,7 +18112,6 @@ Error was thrown.
|
|||
"DOMStorage.getDOMStorageItems": DOMStorage.getDOMStorageItemsReturnValue;
|
||||
"DOMStorage.removeDOMStorageItem": DOMStorage.removeDOMStorageItemReturnValue;
|
||||
"DOMStorage.setDOMStorageItem": DOMStorage.setDOMStorageItemReturnValue;
|
||||
"DOMStorage.getStorageKeyForFrame": DOMStorage.getStorageKeyForFrameReturnValue;
|
||||
"Database.disable": Database.disableReturnValue;
|
||||
"Database.enable": Database.enableReturnValue;
|
||||
"Database.executeSQL": Database.executeSQLReturnValue;
|
||||
|
|
@ -18077,6 +18144,7 @@ Error was thrown.
|
|||
"Emulation.setTimezoneOverride": Emulation.setTimezoneOverrideReturnValue;
|
||||
"Emulation.setVisibleSize": Emulation.setVisibleSizeReturnValue;
|
||||
"Emulation.setDisabledImageTypes": Emulation.setDisabledImageTypesReturnValue;
|
||||
"Emulation.setHardwareConcurrencyOverride": Emulation.setHardwareConcurrencyOverrideReturnValue;
|
||||
"Emulation.setUserAgentOverride": Emulation.setUserAgentOverrideReturnValue;
|
||||
"Emulation.setAutomationOverride": Emulation.setAutomationOverrideReturnValue;
|
||||
"HeadlessExperimental.beginFrame": HeadlessExperimental.beginFrameReturnValue;
|
||||
|
|
@ -18274,6 +18342,7 @@ Error was thrown.
|
|||
"ServiceWorker.stopWorker": ServiceWorker.stopWorkerReturnValue;
|
||||
"ServiceWorker.unregister": ServiceWorker.unregisterReturnValue;
|
||||
"ServiceWorker.updateRegistration": ServiceWorker.updateRegistrationReturnValue;
|
||||
"Storage.getStorageKeyForFrame": Storage.getStorageKeyForFrameReturnValue;
|
||||
"Storage.clearDataForOrigin": Storage.clearDataForOriginReturnValue;
|
||||
"Storage.getCookies": Storage.getCookiesReturnValue;
|
||||
"Storage.setCookies": Storage.setCookiesReturnValue;
|
||||
|
|
|
|||
287
packages/playwright-core/types/types.d.ts
vendored
287
packages/playwright-core/types/types.d.ts
vendored
|
|
@ -17,7 +17,6 @@
|
|||
import { Protocol } from 'playwright-core/types/protocol';
|
||||
import { ChildProcess } from 'child_process';
|
||||
import { EventEmitter } from 'events';
|
||||
import { HARResponse } from 'playwright-core/types/har';
|
||||
import { Readable } from 'stream';
|
||||
import { ReadStream } from 'fs';
|
||||
import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from 'playwright-core/types/structs';
|
||||
|
|
@ -3167,6 +3166,37 @@ export interface Page {
|
|||
times?: number;
|
||||
}): Promise<void>;
|
||||
|
||||
/**
|
||||
* If specified the network requests that are made in the page will be served from the HAR file. Read more about
|
||||
* [Replaying from HAR](https://playwright.dev/docs/network#replaying-from-har).
|
||||
*
|
||||
* Playwright will not serve requests intercepted by Service Worker from the HAR file. See
|
||||
* [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using
|
||||
* request interception by setting `Browser.newContext.serviceWorkers` to `'block'`.
|
||||
* @param har Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a relative path, then it is resolved relative to the current working directory.
|
||||
* @param options
|
||||
*/
|
||||
routeFromHAR(har: string, options?: {
|
||||
/**
|
||||
* - If set to 'abort' any request not found in the HAR file will be aborted.
|
||||
* - If set to 'fallback' missing requests will be sent to the network.
|
||||
*
|
||||
* Defaults to abort.
|
||||
*/
|
||||
notFound?: "abort"|"fallback";
|
||||
|
||||
/**
|
||||
* If specified, updates the given HAR with the actual network information instead of serving from file.
|
||||
*/
|
||||
update?: boolean;
|
||||
|
||||
/**
|
||||
* A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern
|
||||
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
|
||||
*/
|
||||
url?: string|RegExp;
|
||||
}): Promise<void>;
|
||||
|
||||
/**
|
||||
* Returns the buffer with the captured screenshot.
|
||||
* @param options
|
||||
|
|
@ -7093,6 +7123,37 @@ export interface BrowserContext {
|
|||
times?: number;
|
||||
}): Promise<void>;
|
||||
|
||||
/**
|
||||
* If specified the network requests that are made in the context will be served from the HAR file. Read more about
|
||||
* [Replaying from HAR](https://playwright.dev/docs/network#replaying-from-har).
|
||||
*
|
||||
* Playwright will not serve requests intercepted by Service Worker from the HAR file. See
|
||||
* [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using
|
||||
* request interception by setting `Browser.newContext.serviceWorkers` to `'block'`.
|
||||
* @param har Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a relative path, then it is resolved relative to the current working directory.
|
||||
* @param options
|
||||
*/
|
||||
routeFromHAR(har: string, options?: {
|
||||
/**
|
||||
* - If set to 'abort' any request not found in the HAR file will be aborted.
|
||||
* - If set to 'fallback' falls through to the next route handler in the handler chain.
|
||||
*
|
||||
* Defaults to abort.
|
||||
*/
|
||||
notFound?: "abort"|"fallback";
|
||||
|
||||
/**
|
||||
* If specified, updates the given HAR with the actual network information instead of serving from file.
|
||||
*/
|
||||
update?: boolean;
|
||||
|
||||
/**
|
||||
* A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern
|
||||
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
|
||||
*/
|
||||
url?: string|RegExp;
|
||||
}): Promise<void>;
|
||||
|
||||
/**
|
||||
* > NOTE: Service workers are only supported on Chromium-based browsers.
|
||||
*
|
||||
|
|
@ -10507,34 +10568,6 @@ export interface BrowserType<Unused = {}> {
|
|||
*/
|
||||
handleSIGTERM?: boolean;
|
||||
|
||||
/**
|
||||
* If specified the network requests that are made in the context will be served from the HAR file. Read more about
|
||||
* [Replaying from HAR](https://playwright.dev/docs/network#replaying-from-har).
|
||||
*
|
||||
* > NOTE: Playwright will not serve requests intercepted by Service Worker from the HAR file. See
|
||||
* [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using
|
||||
* request interception by setting `Browser.newContext.serviceWorkers` to `'block'`.
|
||||
*/
|
||||
har?: {
|
||||
/**
|
||||
* Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a
|
||||
* relative path, then it is resolved relative to the current working directory.
|
||||
*/
|
||||
path: string;
|
||||
|
||||
/**
|
||||
* If set to 'abort' any request not found in the HAR file will be aborted. If set to'continue' missing requests will be
|
||||
* sent to the network. Defaults to 'abort'.
|
||||
*/
|
||||
fallback?: "abort"|"continue";
|
||||
|
||||
/**
|
||||
* A glob pattern or regular expression to match request URL while routing. Only requests with URL matching the pattern
|
||||
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
|
||||
*/
|
||||
urlFilter?: string|RegExp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Specifies if viewport supports touch events. Defaults to false.
|
||||
*/
|
||||
|
|
@ -10643,16 +10676,24 @@ export interface BrowserType<Unused = {}> {
|
|||
|
||||
/**
|
||||
* Optional setting to control resource content management. If `omit` is specified, content is not persisted. If `attach`
|
||||
* is specified, resources are persistet as separate files and all of these files are archived along with the HAR file.
|
||||
* Defaults to `embed`, which stores content inline the HAR file as per HAR specification.
|
||||
* is specified, resources are persistet as separate files or entries in the ZIP archive. If `embed` is specified, content
|
||||
* is stored inline the HAR file as per HAR specification. Defaults to `attach` for `.zip` output files and to `embed` for
|
||||
* all other file extensions.
|
||||
*/
|
||||
content?: "omit"|"embed"|"attach";
|
||||
|
||||
/**
|
||||
* Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, `attach` mode is used by default.
|
||||
* Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, `content: 'attach'` is used by
|
||||
* default.
|
||||
*/
|
||||
path: string;
|
||||
|
||||
/**
|
||||
* When set to `minimal`, only record information necessary for routing from HAR. This omits sizes, timing, page, cookies,
|
||||
* security and other types of HAR information that are not used when replaying from HAR. Defaults to `full`.
|
||||
*/
|
||||
mode?: "full"|"minimal";
|
||||
|
||||
/**
|
||||
* A glob or regex pattern to filter requests that are stored in the HAR. When a `baseURL` via the context options was
|
||||
* provided and the passed URL is a path, it gets merged via the
|
||||
|
|
@ -11762,34 +11803,6 @@ export interface AndroidDevice {
|
|||
accuracy?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* If specified the network requests that are made in the context will be served from the HAR file. Read more about
|
||||
* [Replaying from HAR](https://playwright.dev/docs/network#replaying-from-har).
|
||||
*
|
||||
* > NOTE: Playwright will not serve requests intercepted by Service Worker from the HAR file. See
|
||||
* [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using
|
||||
* request interception by setting `Browser.newContext.serviceWorkers` to `'block'`.
|
||||
*/
|
||||
har?: {
|
||||
/**
|
||||
* Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a
|
||||
* relative path, then it is resolved relative to the current working directory.
|
||||
*/
|
||||
path: string;
|
||||
|
||||
/**
|
||||
* If set to 'abort' any request not found in the HAR file will be aborted. If set to'continue' missing requests will be
|
||||
* sent to the network. Defaults to 'abort'.
|
||||
*/
|
||||
fallback?: "abort"|"continue";
|
||||
|
||||
/**
|
||||
* A glob pattern or regular expression to match request URL while routing. Only requests with URL matching the pattern
|
||||
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
|
||||
*/
|
||||
urlFilter?: string|RegExp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Specifies if viewport supports touch events. Defaults to false.
|
||||
*/
|
||||
|
|
@ -11858,16 +11871,24 @@ export interface AndroidDevice {
|
|||
|
||||
/**
|
||||
* Optional setting to control resource content management. If `omit` is specified, content is not persisted. If `attach`
|
||||
* is specified, resources are persistet as separate files and all of these files are archived along with the HAR file.
|
||||
* Defaults to `embed`, which stores content inline the HAR file as per HAR specification.
|
||||
* is specified, resources are persistet as separate files or entries in the ZIP archive. If `embed` is specified, content
|
||||
* is stored inline the HAR file as per HAR specification. Defaults to `attach` for `.zip` output files and to `embed` for
|
||||
* all other file extensions.
|
||||
*/
|
||||
content?: "omit"|"embed"|"attach";
|
||||
|
||||
/**
|
||||
* Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, `attach` mode is used by default.
|
||||
* Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, `content: 'attach'` is used by
|
||||
* default.
|
||||
*/
|
||||
path: string;
|
||||
|
||||
/**
|
||||
* When set to `minimal`, only record information necessary for routing from HAR. This omits sizes, timing, page, cookies,
|
||||
* security and other types of HAR information that are not used when replaying from HAR. Defaults to `full`.
|
||||
*/
|
||||
mode?: "full"|"minimal";
|
||||
|
||||
/**
|
||||
* A glob or regex pattern to filter requests that are stored in the HAR. When a `baseURL` via the context options was
|
||||
* provided and the passed URL is a path, it gets merged via the
|
||||
|
|
@ -13330,34 +13351,6 @@ export interface Browser extends EventEmitter {
|
|||
accuracy?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* If specified the network requests that are made in the context will be served from the HAR file. Read more about
|
||||
* [Replaying from HAR](https://playwright.dev/docs/network#replaying-from-har).
|
||||
*
|
||||
* > NOTE: Playwright will not serve requests intercepted by Service Worker from the HAR file. See
|
||||
* [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using
|
||||
* request interception by setting `Browser.newContext.serviceWorkers` to `'block'`.
|
||||
*/
|
||||
har?: {
|
||||
/**
|
||||
* Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a
|
||||
* relative path, then it is resolved relative to the current working directory.
|
||||
*/
|
||||
path: string;
|
||||
|
||||
/**
|
||||
* If set to 'abort' any request not found in the HAR file will be aborted. If set to'continue' missing requests will be
|
||||
* sent to the network. Defaults to 'abort'.
|
||||
*/
|
||||
fallback?: "abort"|"continue";
|
||||
|
||||
/**
|
||||
* A glob pattern or regular expression to match request URL while routing. Only requests with URL matching the pattern
|
||||
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
|
||||
*/
|
||||
urlFilter?: string|RegExp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Specifies if viewport supports touch events. Defaults to false.
|
||||
*/
|
||||
|
|
@ -13456,16 +13449,24 @@ export interface Browser extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Optional setting to control resource content management. If `omit` is specified, content is not persisted. If `attach`
|
||||
* is specified, resources are persistet as separate files and all of these files are archived along with the HAR file.
|
||||
* Defaults to `embed`, which stores content inline the HAR file as per HAR specification.
|
||||
* is specified, resources are persistet as separate files or entries in the ZIP archive. If `embed` is specified, content
|
||||
* is stored inline the HAR file as per HAR specification. Defaults to `attach` for `.zip` output files and to `embed` for
|
||||
* all other file extensions.
|
||||
*/
|
||||
content?: "omit"|"embed"|"attach";
|
||||
|
||||
/**
|
||||
* Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, `attach` mode is used by default.
|
||||
* Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, `content: 'attach'` is used by
|
||||
* default.
|
||||
*/
|
||||
path: string;
|
||||
|
||||
/**
|
||||
* When set to `minimal`, only record information necessary for routing from HAR. This omits sizes, timing, page, cookies,
|
||||
* security and other types of HAR information that are not used when replaying from HAR. Defaults to `full`.
|
||||
*/
|
||||
mode?: "full"|"minimal";
|
||||
|
||||
/**
|
||||
* A glob or regex pattern to filter requests that are stored in the HAR. When a `baseURL` via the context options was
|
||||
* provided and the passed URL is a path, it gets merged via the
|
||||
|
|
@ -14202,34 +14203,6 @@ export interface Electron {
|
|||
accuracy?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* If specified the network requests that are made in the context will be served from the HAR file. Read more about
|
||||
* [Replaying from HAR](https://playwright.dev/docs/network#replaying-from-har).
|
||||
*
|
||||
* > NOTE: Playwright will not serve requests intercepted by Service Worker from the HAR file. See
|
||||
* [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using
|
||||
* request interception by setting `Browser.newContext.serviceWorkers` to `'block'`.
|
||||
*/
|
||||
har?: {
|
||||
/**
|
||||
* Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a
|
||||
* relative path, then it is resolved relative to the current working directory.
|
||||
*/
|
||||
path: string;
|
||||
|
||||
/**
|
||||
* If set to 'abort' any request not found in the HAR file will be aborted. If set to'continue' missing requests will be
|
||||
* sent to the network. Defaults to 'abort'.
|
||||
*/
|
||||
fallback?: "abort"|"continue";
|
||||
|
||||
/**
|
||||
* A glob pattern or regular expression to match request URL while routing. Only requests with URL matching the pattern
|
||||
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
|
||||
*/
|
||||
urlFilter?: string|RegExp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
|
||||
*/
|
||||
|
|
@ -14270,16 +14243,24 @@ export interface Electron {
|
|||
|
||||
/**
|
||||
* Optional setting to control resource content management. If `omit` is specified, content is not persisted. If `attach`
|
||||
* is specified, resources are persistet as separate files and all of these files are archived along with the HAR file.
|
||||
* Defaults to `embed`, which stores content inline the HAR file as per HAR specification.
|
||||
* is specified, resources are persistet as separate files or entries in the ZIP archive. If `embed` is specified, content
|
||||
* is stored inline the HAR file as per HAR specification. Defaults to `attach` for `.zip` output files and to `embed` for
|
||||
* all other file extensions.
|
||||
*/
|
||||
content?: "omit"|"embed"|"attach";
|
||||
|
||||
/**
|
||||
* Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, `attach` mode is used by default.
|
||||
* Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, `content: 'attach'` is used by
|
||||
* default.
|
||||
*/
|
||||
path: string;
|
||||
|
||||
/**
|
||||
* When set to `minimal`, only record information necessary for routing from HAR. This omits sizes, timing, page, cookies,
|
||||
* security and other types of HAR information that are not used when replaying from HAR. Defaults to `full`.
|
||||
*/
|
||||
mode?: "full"|"minimal";
|
||||
|
||||
/**
|
||||
* A glob or regex pattern to filter requests that are stored in the HAR. When a `baseURL` via the context options was
|
||||
* provided and the passed URL is a path, it gets merged via the
|
||||
|
|
@ -14895,9 +14876,10 @@ export interface Request {
|
|||
frame(): Frame;
|
||||
|
||||
/**
|
||||
* **DEPRECATED** Incomplete list of headers as seen by the rendering engine. Use
|
||||
* [request.allHeaders()](https://playwright.dev/docs/api/class-request#request-all-headers) instead.
|
||||
* @deprecated
|
||||
* An object with the request HTTP headers. The header names are lower-cased. Note that this method does not return
|
||||
* security-related headers, including cookie-related ones. You can use
|
||||
* [request.allHeaders()](https://playwright.dev/docs/api/class-request#request-all-headers) for complete list of headers
|
||||
* that include `cookie` information.
|
||||
*/
|
||||
headers(): { [key: string]: string; };
|
||||
|
||||
|
|
@ -15132,9 +15114,10 @@ export interface Response {
|
|||
fromServiceWorker(): boolean;
|
||||
|
||||
/**
|
||||
* **DEPRECATED** Incomplete list of headers as seen by the rendering engine. Use
|
||||
* [response.allHeaders()](https://playwright.dev/docs/api/class-response#response-all-headers) instead.
|
||||
* @deprecated
|
||||
* An object with the response HTTP headers. The header names are lower-cased. Note that this method does not return
|
||||
* security-related headers, including cookie-related ones. You can use
|
||||
* [response.allHeaders()](https://playwright.dev/docs/api/class-response#response-all-headers) for complete list of
|
||||
* headers that include `cookie` information.
|
||||
*/
|
||||
headers(): { [key: string]: string; };
|
||||
|
||||
|
|
@ -15995,34 +15978,6 @@ export interface BrowserContextOptions {
|
|||
|
||||
geolocation?: Geolocation;
|
||||
|
||||
/**
|
||||
* If specified the network requests that are made in the context will be served from the HAR file. Read more about
|
||||
* [Replaying from HAR](https://playwright.dev/docs/network#replaying-from-har).
|
||||
*
|
||||
* > NOTE: Playwright will not serve requests intercepted by Service Worker from the HAR file. See
|
||||
* [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using
|
||||
* request interception by setting `Browser.newContext.serviceWorkers` to `'block'`.
|
||||
*/
|
||||
har?: {
|
||||
/**
|
||||
* Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a
|
||||
* relative path, then it is resolved relative to the current working directory.
|
||||
*/
|
||||
path: string;
|
||||
|
||||
/**
|
||||
* If set to 'abort' any request not found in the HAR file will be aborted. If set to'continue' missing requests will be
|
||||
* sent to the network. Defaults to 'abort'.
|
||||
*/
|
||||
fallback?: "abort"|"continue";
|
||||
|
||||
/**
|
||||
* A glob pattern or regular expression to match request URL while routing. Only requests with URL matching the pattern
|
||||
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
|
||||
*/
|
||||
urlFilter?: string|RegExp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Specifies if viewport supports touch events. Defaults to false.
|
||||
*/
|
||||
|
|
@ -16117,16 +16072,24 @@ export interface BrowserContextOptions {
|
|||
|
||||
/**
|
||||
* Optional setting to control resource content management. If `omit` is specified, content is not persisted. If `attach`
|
||||
* is specified, resources are persistet as separate files and all of these files are archived along with the HAR file.
|
||||
* Defaults to `embed`, which stores content inline the HAR file as per HAR specification.
|
||||
* is specified, resources are persistet as separate files or entries in the ZIP archive. If `embed` is specified, content
|
||||
* is stored inline the HAR file as per HAR specification. Defaults to `attach` for `.zip` output files and to `embed` for
|
||||
* all other file extensions.
|
||||
*/
|
||||
content?: "omit"|"embed"|"attach";
|
||||
|
||||
/**
|
||||
* Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, `attach` mode is used by default.
|
||||
* Path on the filesystem to write the HAR file to. If the file name ends with `.zip`, `content: 'attach'` is used by
|
||||
* default.
|
||||
*/
|
||||
path: string;
|
||||
|
||||
/**
|
||||
* When set to `minimal`, only record information necessary for routing from HAR. This omits sizes, timing, page, cookies,
|
||||
* security and other types of HAR information that are not used when replaying from HAR. Defaults to `full`.
|
||||
*/
|
||||
mode?: "full"|"minimal";
|
||||
|
||||
/**
|
||||
* A glob or regex pattern to filter requests that are stored in the HAR. When a `baseURL` via the context options was
|
||||
* provided and the passed URL is a path, it gets merged via the
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@playwright/experimental-ct-react",
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"description": "Playwright Component Testing for React",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@vitejs/plugin-react": "^1.0.7",
|
||||
"@playwright/test": "1.23.0-next",
|
||||
"@playwright/test": "1.23.4",
|
||||
"vite": "^2.9.5"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@playwright/experimental-ct-svelte",
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"description": "Playwright Component Testing for Svelte",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.30",
|
||||
"@playwright/test": "1.23.0-next",
|
||||
"@playwright/test": "1.23.4",
|
||||
"vite": "^2.9.5"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@playwright/experimental-ct-vue",
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"description": "Playwright Component Testing for Vue",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@vitejs/plugin-vue": "^2.3.1",
|
||||
"@playwright/test": "1.23.0-next",
|
||||
"@playwright/test": "1.23.4",
|
||||
"vite": "^2.9.5"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@playwright/experimental-ct-vue2",
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"description": "Playwright Component Testing for Vue2",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@playwright/test": "1.23.0-next",
|
||||
"@playwright/test": "1.23.4",
|
||||
"vite": "^2.9.5",
|
||||
"vite-plugin-vue2": "^2.0.1"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "playwright-firefox",
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"description": "A high-level API to automate Firefox",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
@ -27,6 +27,6 @@
|
|||
"install": "node install.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"playwright-core": "1.23.0-next"
|
||||
"playwright-core": "1.23.4"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ This project incorporates components from the projects listed below. The origina
|
|||
- babel-plugin-dynamic-import-node@2.3.3 (https://github.com/airbnb/babel-plugin-dynamic-import-node)
|
||||
- braces@3.0.2 (https://github.com/micromatch/braces)
|
||||
- browserslist@4.20.3 (https://github.com/browserslist/browserslist)
|
||||
- buffer-from@1.1.2 (https://github.com/LinusU/buffer-from)
|
||||
- call-bind@1.0.2 (https://github.com/ljharb/call-bind)
|
||||
- caniuse-lite@1.0.30001346 (https://github.com/browserslist/caniuse-lite)
|
||||
- chalk@2.4.2 (https://github.com/chalk/chalk)
|
||||
|
|
@ -133,8 +134,8 @@ This project incorporates components from the projects listed below. The origina
|
|||
- safe-buffer@5.1.2 (https://github.com/feross/safe-buffer)
|
||||
- semver@6.3.0 (https://github.com/npm/node-semver)
|
||||
- slash@3.0.0 (https://github.com/sindresorhus/slash)
|
||||
- source-map-support@0.4.18 (https://github.com/evanw/node-source-map-support)
|
||||
- source-map@0.5.7 (https://github.com/mozilla/source-map)
|
||||
- source-map-support@0.5.21 (https://github.com/evanw/node-source-map-support)
|
||||
- source-map@0.6.1 (https://github.com/mozilla/source-map)
|
||||
- stack-utils@2.0.5 (https://github.com/tapjs/stack-utils)
|
||||
- supports-color@5.5.0 (https://github.com/chalk/supports-color)
|
||||
- supports-color@7.2.0 (https://github.com/chalk/supports-color)
|
||||
|
|
@ -2262,6 +2263,32 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
=========================================
|
||||
END OF browserslist@4.20.3 AND INFORMATION
|
||||
|
||||
%% buffer-from@1.1.2 NOTICES AND INFORMATION BEGIN HERE
|
||||
=========================================
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016, 2018 Linus Unnebäck
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
=========================================
|
||||
END OF buffer-from@1.1.2 AND INFORMATION
|
||||
|
||||
%% call-bind@1.0.2 NOTICES AND INFORMATION BEGIN HERE
|
||||
=========================================
|
||||
MIT License
|
||||
|
|
@ -3820,7 +3847,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
|||
=========================================
|
||||
END OF slash@3.0.0 AND INFORMATION
|
||||
|
||||
%% source-map-support@0.4.18 NOTICES AND INFORMATION BEGIN HERE
|
||||
%% source-map-support@0.5.21 NOTICES AND INFORMATION BEGIN HERE
|
||||
=========================================
|
||||
The MIT License (MIT)
|
||||
|
||||
|
|
@ -3844,9 +3871,9 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
=========================================
|
||||
END OF source-map-support@0.4.18 AND INFORMATION
|
||||
END OF source-map-support@0.5.21 AND INFORMATION
|
||||
|
||||
%% source-map@0.5.7 NOTICES AND INFORMATION BEGIN HERE
|
||||
%% source-map@0.6.1 NOTICES AND INFORMATION BEGIN HERE
|
||||
=========================================
|
||||
Copyright (c) 2009-2011, Mozilla Foundation and contributors
|
||||
All rights reserved.
|
||||
|
|
@ -3876,7 +3903,7 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
=========================================
|
||||
END OF source-map@0.5.7 AND INFORMATION
|
||||
END OF source-map@0.6.1 AND INFORMATION
|
||||
|
||||
%% stack-utils@2.0.5 NOTICES AND INFORMATION BEGIN HERE
|
||||
=========================================
|
||||
|
|
@ -3975,6 +4002,6 @@ END OF to-regex-range@5.0.1 AND INFORMATION
|
|||
|
||||
SUMMARY BEGIN HERE
|
||||
=========================================
|
||||
Total Packages: 136
|
||||
Total Packages: 137
|
||||
=========================================
|
||||
END OF SUMMARY
|
||||
|
|
@ -21,14 +21,17 @@ const esbuild = require('esbuild');
|
|||
|
||||
// Can be removed once source-map-support was is fixed.
|
||||
/** @type{import('esbuild').Plugin} */
|
||||
let patchSourceMapSupportHideBufferDeprecationWarning = {
|
||||
let patchSource = {
|
||||
name: 'patch-source-map-support-deprecation',
|
||||
setup(build) {
|
||||
build.onResolve({ filter: /^source-map-support$/ }, () => {
|
||||
const originalPath = require.resolve('source-map-support');
|
||||
const patchedPath = path.join(path.dirname(originalPath), path.basename(originalPath, '.js') + '.pw-patched.js');
|
||||
let sourceFileContent = fs.readFileSync(originalPath, 'utf8')
|
||||
sourceFileContent = sourceFileContent.replace(/new Buffer\(rawData/g, 'Buffer.from(rawData');
|
||||
let sourceFileContent = fs.readFileSync(originalPath, 'utf8');
|
||||
// source-map-support is overwriting __PW_ZONE__ with func in core if source maps are present.
|
||||
const original = `return state.nextPosition.name || originalFunctionName();`;
|
||||
const insertedLine = `if (state.nextPosition.name === 'func') return originalFunctionName() || 'func';`;
|
||||
sourceFileContent = sourceFileContent.replace(original, insertedLine + original);
|
||||
fs.writeFileSync(patchedPath, sourceFileContent);
|
||||
return { path: patchedPath }
|
||||
});
|
||||
|
|
@ -39,7 +42,7 @@ esbuild.build({
|
|||
entryPoints: [path.join(__dirname, 'src/utilsBundleImpl.ts')],
|
||||
bundle: true,
|
||||
outdir: path.join(__dirname, '../../lib'),
|
||||
plugins: [patchSourceMapSupportHideBufferDeprecationWarning],
|
||||
plugins: [patchSource],
|
||||
format: 'cjs',
|
||||
platform: 'node',
|
||||
target: 'ES2019',
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
"json5": "2.2.1",
|
||||
"open": "8.4.0",
|
||||
"pirates": "4.0.4",
|
||||
"source-map-support": "0.4.18"
|
||||
"source-map-support": "0.5.21"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/source-map-support": "^0.5.4"
|
||||
|
|
@ -26,6 +26,11 @@
|
|||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
|
||||
},
|
||||
"node_modules/define-lazy-prop": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
|
||||
|
|
@ -98,25 +103,17 @@
|
|||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-support": {
|
||||
"version": "0.4.18",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
|
||||
"integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
|
||||
"version": "0.5.21",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
||||
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
||||
"dependencies": {
|
||||
"source-map": "^0.5.6"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-support/node_modules/source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -130,6 +127,11 @@
|
|||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
|
||||
},
|
||||
"define-lazy-prop": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
|
||||
|
|
@ -171,22 +173,15 @@
|
|||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||
},
|
||||
"source-map-support": {
|
||||
"version": "0.4.18",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
|
||||
"integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
|
||||
"version": "0.5.21",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
||||
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
||||
"requires": {
|
||||
"source-map": "^0.5.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
|
||||
}
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
"json5": "2.2.1",
|
||||
"open": "8.4.0",
|
||||
"pirates": "4.0.4",
|
||||
"source-map-support": "0.4.18"
|
||||
"source-map-support": "0.5.21"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/source-map-support": "^0.5.4"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@playwright/test",
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"description": "A high-level API to automate web browsers",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
@ -33,6 +33,6 @@
|
|||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"playwright-core": "1.23.0-next"
|
||||
"playwright-core": "1.23.4"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@ import fs from 'fs';
|
|||
import url from 'url';
|
||||
import { transformHook, resolveHook } from './transform';
|
||||
|
||||
async function resolve(specifier: string, context: { parentURL: string }, defaultResolve: any) {
|
||||
// Node < 18.6: defaultResolve takes 3 arguments.
|
||||
// Node >= 18.6: nextResolve from the chain takes 2 arguments.
|
||||
async function resolve(specifier: string, context: { parentURL?: string }, defaultResolve: Function) {
|
||||
if (context.parentURL && context.parentURL.startsWith('file://')) {
|
||||
const filename = url.fileURLToPath(context.parentURL);
|
||||
const resolved = resolveHook(filename, specifier);
|
||||
|
|
@ -28,12 +30,15 @@ async function resolve(specifier: string, context: { parentURL: string }, defaul
|
|||
return defaultResolve(specifier, context, defaultResolve);
|
||||
}
|
||||
|
||||
async function load(moduleUrl: string, context: any, defaultLoad: any) {
|
||||
// Node < 18.6: defaultLoad takes 3 arguments.
|
||||
// Node >= 18.6: nextLoad from the chain takes 2 arguments.
|
||||
async function load(moduleUrl: string, context: any, defaultLoad: Function) {
|
||||
if (moduleUrl.startsWith('file://') && (moduleUrl.endsWith('.ts') || moduleUrl.endsWith('.tsx'))) {
|
||||
const filename = url.fileURLToPath(moduleUrl);
|
||||
const code = fs.readFileSync(filename, 'utf-8');
|
||||
const source = transformHook(code, filename, moduleUrl);
|
||||
return { format: 'module', source };
|
||||
// shortCurcuit is required by Node >= 18.6 to designate no more loaders should be called.
|
||||
return { format: 'module', source, shortCircuit: true };
|
||||
}
|
||||
return defaultLoad(moduleUrl, context, defaultLoad);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,8 +64,8 @@ export const test = _baseTest.extend<TestFixtures, WorkerFixtures>({
|
|||
await use(require('playwright-core'));
|
||||
}
|
||||
}, { scope: 'worker' } ],
|
||||
headless: [ true, { scope: 'worker', option: true } ],
|
||||
channel: [ undefined, { scope: 'worker', option: true } ],
|
||||
headless: [ ({ launchOptions }, use) => use(launchOptions.headless ?? true), { scope: 'worker', option: true } ],
|
||||
channel: [ ({ launchOptions }, use) => use(launchOptions.channel), { scope: 'worker', option: true } ],
|
||||
launchOptions: [ {}, { scope: 'worker', option: true } ],
|
||||
connectOptions: [ undefined, { scope: 'worker', option: true } ],
|
||||
screenshot: [ 'off', { scope: 'worker', option: true } ],
|
||||
|
|
@ -135,32 +135,31 @@ export const test = _baseTest.extend<TestFixtures, WorkerFixtures>({
|
|||
await browser.close();
|
||||
}, { scope: 'worker' } ],
|
||||
|
||||
acceptDownloads: [ true, { option: true } ],
|
||||
bypassCSP: [ undefined, { option: true } ],
|
||||
colorScheme: [ undefined, { option: true } ],
|
||||
deviceScaleFactor: [ undefined, { option: true } ],
|
||||
extraHTTPHeaders: [ undefined, { option: true } ],
|
||||
geolocation: [ undefined, { option: true } ],
|
||||
har: [undefined, { option: true }],
|
||||
hasTouch: [ undefined, { option: true } ],
|
||||
httpCredentials: [ undefined, { option: true } ],
|
||||
ignoreHTTPSErrors: [ undefined, { option: true } ],
|
||||
isMobile: [ undefined, { option: true } ],
|
||||
javaScriptEnabled: [ true, { option: true } ],
|
||||
locale: [ 'en-US', { option: true } ],
|
||||
offline: [ undefined, { option: true } ],
|
||||
permissions: [ undefined, { option: true } ],
|
||||
proxy: [ undefined, { option: true } ],
|
||||
storageState: [ undefined, { option: true } ],
|
||||
timezoneId: [ undefined, { option: true } ],
|
||||
userAgent: [ undefined, { option: true } ],
|
||||
viewport: [ { width: 1280, height: 720 }, { option: true } ],
|
||||
acceptDownloads: [ ({ contextOptions }, use) => use(contextOptions.acceptDownloads ?? true), { option: true } ],
|
||||
bypassCSP: [ ({ contextOptions }, use) => use(contextOptions.bypassCSP), { option: true } ],
|
||||
colorScheme: [ ({ contextOptions }, use) => use(contextOptions.colorScheme), { option: true } ],
|
||||
deviceScaleFactor: [ ({ contextOptions }, use) => use(contextOptions.deviceScaleFactor), { option: true } ],
|
||||
extraHTTPHeaders: [ ({ contextOptions }, use) => use(contextOptions.extraHTTPHeaders), { option: true } ],
|
||||
geolocation: [ ({ contextOptions }, use) => use(contextOptions.geolocation), { option: true } ],
|
||||
hasTouch: [ ({ contextOptions }, use) => use(contextOptions.hasTouch), { option: true } ],
|
||||
httpCredentials: [ ({ contextOptions }, use) => use(contextOptions.httpCredentials), { option: true } ],
|
||||
ignoreHTTPSErrors: [ ({ contextOptions }, use) => use(contextOptions.ignoreHTTPSErrors), { option: true } ],
|
||||
isMobile: [ ({ contextOptions }, use) => use(contextOptions.isMobile), { option: true } ],
|
||||
javaScriptEnabled: [ ({ contextOptions }, use) => use(contextOptions.javaScriptEnabled ?? true), { option: true } ],
|
||||
locale: [ ({ contextOptions }, use) => use(contextOptions.locale ?? 'en-US'), { option: true } ],
|
||||
offline: [ ({ contextOptions }, use) => use(contextOptions.offline), { option: true } ],
|
||||
permissions: [ ({ contextOptions }, use) => use(contextOptions.permissions), { option: true } ],
|
||||
proxy: [ ({ contextOptions }, use) => use(contextOptions.proxy), { option: true } ],
|
||||
storageState: [ ({ contextOptions }, use) => use(contextOptions.storageState), { option: true } ],
|
||||
timezoneId: [ ({ contextOptions }, use) => use(contextOptions.timezoneId), { option: true } ],
|
||||
userAgent: [ ({ contextOptions }, use) => use(contextOptions.userAgent), { option: true } ],
|
||||
viewport: [({ contextOptions }, use) => use(contextOptions.viewport === undefined ? { width: 1280, height: 720 } : contextOptions.viewport), { option: true }],
|
||||
actionTimeout: [ 0, { option: true } ],
|
||||
navigationTimeout: [ 0, { option: true } ],
|
||||
baseURL: [ async ({ }, use) => {
|
||||
await use(process.env.PLAYWRIGHT_TEST_BASE_URL);
|
||||
}, { option: true } ],
|
||||
serviceWorkers: [ 'allow', { option: true } ],
|
||||
serviceWorkers: [ ({ contextOptions }, use) => use(contextOptions.serviceWorkers ?? 'allow'), { option: true } ],
|
||||
contextOptions: [ {}, { option: true } ],
|
||||
|
||||
_combinedContextOptions: async ({
|
||||
|
|
@ -169,7 +168,6 @@ export const test = _baseTest.extend<TestFixtures, WorkerFixtures>({
|
|||
colorScheme,
|
||||
deviceScaleFactor,
|
||||
extraHTTPHeaders,
|
||||
har,
|
||||
hasTouch,
|
||||
geolocation,
|
||||
httpCredentials,
|
||||
|
|
@ -201,8 +199,6 @@ export const test = _baseTest.extend<TestFixtures, WorkerFixtures>({
|
|||
options.extraHTTPHeaders = extraHTTPHeaders;
|
||||
if (geolocation !== undefined)
|
||||
options.geolocation = geolocation;
|
||||
if (har !== undefined)
|
||||
options.har = har;
|
||||
if (hasTouch !== undefined)
|
||||
options.hasTouch = hasTouch;
|
||||
if (httpCredentials !== undefined)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import { assert, calculateSha1 } from 'playwright-core/lib/utils';
|
|||
import type { AddressInfo } from 'net';
|
||||
|
||||
let previewServer: PreviewServer;
|
||||
const VERSION = 4;
|
||||
const VERSION = 5;
|
||||
|
||||
type CtConfig = {
|
||||
ctPort?: number;
|
||||
|
|
@ -37,6 +37,7 @@ type CtConfig = {
|
|||
};
|
||||
|
||||
const importReactRE = /(^|\n)import\s+(\*\s+as\s+)?React(,|\s+)/;
|
||||
const compiledReactRE = /(const|var)\s+React\s*=/;
|
||||
|
||||
export function createPlugin(
|
||||
registerSourceFile: string,
|
||||
|
|
@ -275,7 +276,7 @@ function vitePlugin(registerSource: string, relativeTemplateDir: string, buildIn
|
|||
}
|
||||
|
||||
// Vite React plugin will do this for .jsx files, but not .js files.
|
||||
if (id.endsWith('.js') && content.includes('React.createElement') && !content.match(importReactRE)) {
|
||||
if (id.endsWith('.js') && content.includes('React.createElement') && !content.match(importReactRE) && !content.match(compiledReactRE)) {
|
||||
const code = `import React from 'react';\n${content}`;
|
||||
return { code, map: { mappings: '' } };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import { tsConfigLoader } from './third_party/tsconfig-loader';
|
|||
import Module from 'module';
|
||||
import type { BabelTransformFunction } from './babelBundle';
|
||||
|
||||
const version = 12;
|
||||
const version = 13;
|
||||
const cacheDir = process.env.PWTEST_CACHE_DIR || path.join(os.tmpdir(), 'playwright-transform-cache');
|
||||
const sourceMaps: Map<string, string> = new Map();
|
||||
|
||||
|
|
|
|||
10
packages/playwright-test/types/test.d.ts
vendored
10
packages/playwright-test/types/test.d.ts
vendored
|
|
@ -2492,7 +2492,6 @@ type BrowserName = 'chromium' | 'firefox' | 'webkit';
|
|||
type BrowserChannel = Exclude<LaunchOptions['channel'], undefined>;
|
||||
type ColorScheme = Exclude<BrowserContextOptions['colorScheme'], undefined>;
|
||||
type ExtraHTTPHeaders = Exclude<BrowserContextOptions['extraHTTPHeaders'], undefined>;
|
||||
type HAROptions = Exclude<BrowserContextOptions['har'], undefined>;
|
||||
type Proxy = Exclude<BrowserContextOptions['proxy'], undefined>;
|
||||
type StorageState = Exclude<BrowserContextOptions['storageState'], undefined>;
|
||||
type ServiceWorkerPolicy = Exclude<BrowserContextOptions['serviceWorkers'], undefined>;
|
||||
|
|
@ -2700,15 +2699,6 @@ export interface PlaywrightTestOptions {
|
|||
*/
|
||||
extraHTTPHeaders: ExtraHTTPHeaders | undefined;
|
||||
geolocation: Geolocation | undefined;
|
||||
/**
|
||||
* If specified the network requests that are made in the context will be served from the HAR file. Read more about
|
||||
* [Replaying from HAR](https://playwright.dev/docs/network#replaying-from-har).
|
||||
*
|
||||
* > NOTE: Playwright will not serve requests intercepted by Service Worker from the HAR file. See
|
||||
* [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using
|
||||
* request interception by setting `Browser.newContext.serviceWorkers` to `'block'`.
|
||||
*/
|
||||
har: HAROptions | undefined;
|
||||
/**
|
||||
* Specifies if viewport supports touch events. Defaults to false.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "playwright-webkit",
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"description": "A high-level API to automate WebKit",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
@ -27,6 +27,6 @@
|
|||
"install": "node install.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"playwright-core": "1.23.0-next"
|
||||
"playwright-core": "1.23.4"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "playwright",
|
||||
"version": "1.23.0-next",
|
||||
"version": "1.23.4",
|
||||
"description": "A high-level API to automate web browsers",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
|
|
@ -27,6 +27,6 @@
|
|||
"install": "node install.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"playwright-core": "1.23.0-next"
|
||||
"playwright-core": "1.23.4"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ export class SnapshotRenderer {
|
|||
|
||||
// First try locating exact resource belonging to this frame.
|
||||
for (const resource of this._resources) {
|
||||
if (resource._monotonicTime >= snapshot.timestamp)
|
||||
if (typeof resource._monotonicTime === 'number' && resource._monotonicTime >= snapshot.timestamp)
|
||||
break;
|
||||
if (resource._frameref !== snapshot.frameId)
|
||||
continue;
|
||||
|
|
@ -121,7 +121,7 @@ export class SnapshotRenderer {
|
|||
if (!result) {
|
||||
// Then fall back to resource with this URL to account for memory cache.
|
||||
for (const resource of this._resources) {
|
||||
if (resource._monotonicTime >= snapshot.timestamp)
|
||||
if (typeof resource._monotonicTime === 'number' && resource._monotonicTime >= snapshot.timestamp)
|
||||
break;
|
||||
if (resource.request.url === url)
|
||||
return resource;
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ export function resourcesForAction(action: ActionTraceEvent): ResourceSnapshot[]
|
|||
|
||||
const nextAction = next(action);
|
||||
result = context(action).resources.filter(resource => {
|
||||
return resource._monotonicTime > action.metadata.startTime && (!nextAction || resource._monotonicTime < nextAction.metadata.startTime);
|
||||
return typeof resource._monotonicTime === 'number' && resource._monotonicTime > action.metadata.startTime && (!nextAction || resource._monotonicTime < nextAction.metadata.startTime);
|
||||
});
|
||||
(action as any)[resourcesSymbol] = result;
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
],
|
||||
"entries": [
|
||||
{
|
||||
"_requestref": "request@ee2a0dc164935fcd4d9432d37b245f3c",
|
||||
"_frameref": "frame@c7467fc0f1f86f09fc3b0d727a3862ea",
|
||||
"_monotonicTime": 270572145.898,
|
||||
"startedDateTime": "2022-06-10T04:27:32.146Z",
|
||||
|
|
@ -92,7 +91,6 @@
|
|||
"_securityDetails": {}
|
||||
},
|
||||
{
|
||||
"_requestref": "request@f2ff0fd79321ff90d0bc1b5d6fc13bad",
|
||||
"_frameref": "frame@c7467fc0f1f86f09fc3b0d727a3862ea",
|
||||
"_monotonicTime": 270572174.683,
|
||||
"startedDateTime": "2022-06-10T04:27:32.172Z",
|
||||
|
|
@ -162,7 +160,6 @@
|
|||
"_securityDetails": {}
|
||||
},
|
||||
{
|
||||
"_requestref": "request@f2ff0fd79321ff90d0bc1b5d6fc13bac",
|
||||
"_frameref": "frame@c7467fc0f1f86f09fc3b0d727a3862ea",
|
||||
"_monotonicTime": 270572174.683,
|
||||
"startedDateTime": "2022-06-10T04:27:32.174Z",
|
||||
|
|
@ -232,7 +229,6 @@
|
|||
"_securityDetails": {}
|
||||
},
|
||||
{
|
||||
"_requestref": "request@9626f59acb1f4a95f25112d32e9f7f60",
|
||||
"_frameref": "frame@c7467fc0f1f86f09fc3b0d727a3862ea",
|
||||
"_monotonicTime": 270572175.042,
|
||||
"startedDateTime": "2022-06-10T04:27:32.175Z",
|
||||
|
|
@ -297,7 +293,6 @@
|
|||
"_securityDetails": {}
|
||||
},
|
||||
{
|
||||
"_requestref": "request@d7ee53396148a663b819c348c53b03fb",
|
||||
"_frameref": "frame@c7467fc0f1f86f09fc3b0d727a3862ea",
|
||||
"_monotonicTime": 270572181.822,
|
||||
"startedDateTime": "2022-06-10T04:27:32.182Z",
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
],
|
||||
"entries": [
|
||||
{
|
||||
"_requestref": "request@7d6e0ddb1e1e25f6e5c4a7c943c0bae1",
|
||||
"_frameref": "frame@3767e074ecde4cb8372abba2f6f9bb4f",
|
||||
"_monotonicTime": 110928357.437,
|
||||
"startedDateTime": "2022-06-16T21:41:23.951Z",
|
||||
|
|
@ -201,7 +200,6 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"_requestref": "request@5c7a316ee46a095bda80c23ddc8c740d",
|
||||
"_frameref": "frame@3767e074ecde4cb8372abba2f6f9bb4f",
|
||||
"_monotonicTime": 110928427.603,
|
||||
"startedDateTime": "2022-06-16T21:41:24.022Z",
|
||||
|
|
@ -358,7 +356,6 @@
|
|||
"_securityDetails": {}
|
||||
},
|
||||
{
|
||||
"_requestref": "request@17664a6093c12c97d41efbff3a502adb",
|
||||
"_frameref": "frame@3767e074ecde4cb8372abba2f6f9bb4f",
|
||||
"_monotonicTime": 110928455.901,
|
||||
"startedDateTime": "2022-06-16T21:41:24.050Z",
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
],
|
||||
"entries": [
|
||||
{
|
||||
"_requestref": "request@ee2a0dc164935fcd4d9432d37b245f3c",
|
||||
"_frameref": "frame@c7467fc0f1f86f09fc3b0d727a3862ea",
|
||||
"_monotonicTime": 270572145.898,
|
||||
"startedDateTime": "2022-06-10T04:27:32.146Z",
|
||||
|
|
@ -69,7 +68,7 @@
|
|||
"size": 12,
|
||||
"mimeType": "text/html",
|
||||
"compression": 0,
|
||||
"_sha1": "har-sha1-main-response.txt"
|
||||
"_file": "har-sha1-main-response.txt"
|
||||
},
|
||||
"headersSize": 64,
|
||||
"bodySize": 71,
|
||||
|
|
|
|||
|
|
@ -292,7 +292,7 @@
|
|||
"cssresize": true,
|
||||
"scrollsnappoints": true,
|
||||
"shapes": true,
|
||||
"textalignlast": false,
|
||||
"textalignlast": true,
|
||||
"csstransforms": true,
|
||||
"csstransforms3d": true,
|
||||
"csstransformslevel2": true,
|
||||
|
|
|
|||
|
|
@ -292,7 +292,7 @@
|
|||
"cssresize": true,
|
||||
"scrollsnappoints": true,
|
||||
"shapes": true,
|
||||
"textalignlast": false,
|
||||
"textalignlast": true,
|
||||
"csstransforms": true,
|
||||
"csstransforms3d": true,
|
||||
"csstransformslevel2": true,
|
||||
|
|
|
|||
|
|
@ -196,8 +196,8 @@ test('should serve from HAR', async ({ playwright, asset }) => {
|
|||
const harPath = asset('har-fulfill.har');
|
||||
const app = await playwright._electron.launch({
|
||||
args: [path.join(__dirname, 'electron-window-app.js')],
|
||||
har: { path: harPath },
|
||||
});
|
||||
app.context().routeFromHAR(harPath);
|
||||
const page = await app.firstWindow();
|
||||
// await page.goto('https://playwright.dev/');
|
||||
await page.goto('http://no.playwright/');
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@ import fs from 'fs';
|
|||
import path from 'path';
|
||||
import extractZip from '../../packages/playwright-core/bundles/zip/node_modules/extract-zip';
|
||||
|
||||
it('should fulfill from har, matching the method and following redirects', async ({ contextFactory, isAndroid, asset }) => {
|
||||
it('should context.routeFromHAR, matching the method and following redirects', async ({ context, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-fulfill.har');
|
||||
const context = await contextFactory({ har: { path } });
|
||||
await context.routeFromHAR(path);
|
||||
const page = await context.newPage();
|
||||
await page.goto('http://no.playwright/');
|
||||
// HAR contains a redirect for the script that should be followed automatically.
|
||||
|
|
@ -32,42 +32,55 @@ it('should fulfill from har, matching the method and following redirects', async
|
|||
await expect(page.locator('body')).toHaveCSS('background-color', 'rgb(255, 0, 0)');
|
||||
});
|
||||
|
||||
it('fallback:continue should continue when not found in har', async ({ contextFactory, server, isAndroid, asset }) => {
|
||||
it('should page.routeFromHAR, matching the method and following redirects', async ({ context, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-fulfill.har');
|
||||
const context = await contextFactory({ har: { path, fallback: 'continue' } });
|
||||
const page = await context.newPage();
|
||||
await page.routeFromHAR(path);
|
||||
await page.goto('http://no.playwright/');
|
||||
// HAR contains a redirect for the script that should be followed automatically.
|
||||
expect(await page.evaluate('window.value')).toBe('foo');
|
||||
// HAR contains a POST for the css file that should not be used.
|
||||
await expect(page.locator('body')).toHaveCSS('background-color', 'rgb(255, 0, 0)');
|
||||
});
|
||||
|
||||
it('fallback:continue should continue when not found in har', async ({ context, server, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-fulfill.har');
|
||||
await context.routeFromHAR(path, { notFound: 'fallback' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/one-style.html');
|
||||
await expect(page.locator('body')).toHaveCSS('background-color', 'rgb(255, 192, 203)');
|
||||
});
|
||||
|
||||
it('by default should abort requests not found in har', async ({ contextFactory, server, isAndroid, asset }) => {
|
||||
it('by default should abort requests not found in har', async ({ context, server, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-fulfill.har');
|
||||
const context = await contextFactory({ har: { path } });
|
||||
await context.routeFromHAR(path);
|
||||
const page = await context.newPage();
|
||||
const error = await page.goto(server.EMPTY_PAGE).catch(e => e);
|
||||
expect(error instanceof Error).toBe(true);
|
||||
});
|
||||
|
||||
it('fallback:continue should continue requests on bad har', async ({ contextFactory, server, isAndroid }, testInfo) => {
|
||||
it('fallback:continue should continue requests on bad har', async ({ context, server, isAndroid }, testInfo) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = testInfo.outputPath('test.har');
|
||||
fs.writeFileSync(path, JSON.stringify({ log: {} }), 'utf-8');
|
||||
const context = await contextFactory({ har: { path, fallback: 'continue' } });
|
||||
await context.routeFromHAR(path, { notFound: 'fallback' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/one-style.html');
|
||||
await expect(page.locator('body')).toHaveCSS('background-color', 'rgb(255, 192, 203)');
|
||||
});
|
||||
|
||||
it('should only handle requests matching url filter', async ({ contextFactory, isAndroid, asset }) => {
|
||||
it('should only handle requests matching url filter', async ({ context, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-fulfill.har');
|
||||
const context = await contextFactory({ har: { path, urlFilter: '**/*.js' } });
|
||||
await context.routeFromHAR(path, { notFound: 'fallback', url: '**/*.js' });
|
||||
const page = await context.newPage();
|
||||
await context.route('http://no.playwright/', async route => {
|
||||
expect(route.request().url()).toBe('http://no.playwright/');
|
||||
|
|
@ -83,11 +96,51 @@ it('should only handle requests matching url filter', async ({ contextFactory, i
|
|||
await expect(page.locator('body')).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)');
|
||||
});
|
||||
|
||||
it('should support regex filter', async ({ contextFactory, isAndroid, asset }) => {
|
||||
it('should only context.routeFromHAR requests matching url filter', async ({ context, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-fulfill.har');
|
||||
const context = await contextFactory({ har: { path, urlFilter: /.*(\.js|.*\.css|no.playwright\/)$/ } });
|
||||
await context.routeFromHAR(path, { url: '**/*.js' });
|
||||
const page = await context.newPage();
|
||||
await context.route('http://no.playwright/', async route => {
|
||||
expect(route.request().url()).toBe('http://no.playwright/');
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'text/html',
|
||||
body: '<script src="./script.js"></script><div>hello</div>',
|
||||
});
|
||||
});
|
||||
await page.goto('http://no.playwright/');
|
||||
// HAR contains a redirect for the script that should be followed automatically.
|
||||
expect(await page.evaluate('window.value')).toBe('foo');
|
||||
await expect(page.locator('body')).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)');
|
||||
});
|
||||
|
||||
it('should only page.routeFromHAR requests matching url filter', async ({ context, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-fulfill.har');
|
||||
const page = await context.newPage();
|
||||
await page.routeFromHAR(path, { url: '**/*.js' });
|
||||
await context.route('http://no.playwright/', async route => {
|
||||
expect(route.request().url()).toBe('http://no.playwright/');
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'text/html',
|
||||
body: '<script src="./script.js"></script><div>hello</div>',
|
||||
});
|
||||
});
|
||||
await page.goto('http://no.playwright/');
|
||||
// HAR contains a redirect for the script that should be followed automatically.
|
||||
expect(await page.evaluate('window.value')).toBe('foo');
|
||||
await expect(page.locator('body')).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)');
|
||||
});
|
||||
|
||||
it('should support regex filter', async ({ context, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-fulfill.har');
|
||||
await context.routeFromHAR(path, { url: /.*(\.js|.*\.css|no.playwright\/)$/ });
|
||||
const page = await context.newPage();
|
||||
await page.goto('http://no.playwright/');
|
||||
expect(await page.evaluate('window.value')).toBe('foo');
|
||||
|
|
@ -98,7 +151,8 @@ it('newPage should fulfill from har, matching the method and following redirects
|
|||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-fulfill.har');
|
||||
const page = await browser.newPage({ har: { path } });
|
||||
const page = await browser.newPage();
|
||||
await page.routeFromHAR(path);
|
||||
await page.goto('http://no.playwright/');
|
||||
// HAR contains a redirect for the script that should be followed automatically.
|
||||
expect(await page.evaluate('window.value')).toBe('foo');
|
||||
|
|
@ -107,14 +161,15 @@ it('newPage should fulfill from har, matching the method and following redirects
|
|||
await page.close();
|
||||
});
|
||||
|
||||
it('should change document URL after redirected navigation', async ({ contextFactory, isAndroid, asset }) => {
|
||||
it('should change document URL after redirected navigation', async ({ context, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-redirect.har');
|
||||
const context = await contextFactory({ har: { path } });
|
||||
await context.routeFromHAR(path);
|
||||
const page = await context.newPage();
|
||||
const [response] = await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.waitForURL('https://www.theverge.com/'),
|
||||
page.goto('https://theverge.com/')
|
||||
]);
|
||||
await expect(page).toHaveURL('https://www.theverge.com/');
|
||||
|
|
@ -122,11 +177,28 @@ it('should change document URL after redirected navigation', async ({ contextFac
|
|||
expect(await page.evaluate(() => location.href)).toBe('https://www.theverge.com/');
|
||||
});
|
||||
|
||||
it('should goBack to redirected navigation', async ({ contextFactory, isAndroid, asset, server }) => {
|
||||
it('should change document URL after redirected navigation on click', async ({ server, context, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-redirect.har');
|
||||
const context = await contextFactory({ har: { path, urlFilter: /.*theverge.*/ } });
|
||||
await context.routeFromHAR(path, { url: /.*theverge.*/ });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`<a href="https://theverge.com/">click me</a>`);
|
||||
const [response] = await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.click('text=click me'),
|
||||
]);
|
||||
await expect(page).toHaveURL('https://www.theverge.com/');
|
||||
expect(response.request().url()).toBe('https://www.theverge.com/');
|
||||
expect(await page.evaluate(() => location.href)).toBe('https://www.theverge.com/');
|
||||
});
|
||||
|
||||
it('should goBack to redirected navigation', async ({ context, isAndroid, asset, server }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-redirect.har');
|
||||
await context.routeFromHAR(path, { url: /.*theverge.*/ });
|
||||
const page = await context.newPage();
|
||||
await page.goto('https://theverge.com/');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
|
|
@ -137,11 +209,11 @@ it('should goBack to redirected navigation', async ({ contextFactory, isAndroid,
|
|||
expect(await page.evaluate(() => location.href)).toBe('https://www.theverge.com/');
|
||||
});
|
||||
|
||||
it('should goForward to redirected navigation', async ({ contextFactory, isAndroid, asset, server }) => {
|
||||
it('should goForward to redirected navigation', async ({ context, isAndroid, asset, server }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-redirect.har');
|
||||
const context = await contextFactory({ har: { path, urlFilter: /.*theverge.*/ } });
|
||||
await context.routeFromHAR(path, { url: /.*theverge.*/ });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await expect(page).toHaveURL(server.EMPTY_PAGE);
|
||||
|
|
@ -155,11 +227,11 @@ it('should goForward to redirected navigation', async ({ contextFactory, isAndro
|
|||
expect(await page.evaluate(() => location.href)).toBe('https://www.theverge.com/');
|
||||
});
|
||||
|
||||
it('should reload redirected navigation', async ({ contextFactory, isAndroid, asset, server }) => {
|
||||
it('should reload redirected navigation', async ({ context, isAndroid, asset, server }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-redirect.har');
|
||||
const context = await contextFactory({ har: { path, urlFilter: /.*theverge.*/ } });
|
||||
await context.routeFromHAR(path, { url: /.*theverge.*/ });
|
||||
const page = await context.newPage();
|
||||
await page.goto('https://theverge.com/');
|
||||
await expect(page).toHaveURL('https://www.theverge.com/');
|
||||
|
|
@ -169,11 +241,11 @@ it('should reload redirected navigation', async ({ contextFactory, isAndroid, as
|
|||
expect(await page.evaluate(() => location.href)).toBe('https://www.theverge.com/');
|
||||
});
|
||||
|
||||
it('should fulfill from har with content in a file', async ({ contextFactory, isAndroid, asset }) => {
|
||||
it('should fulfill from har with content in a file', async ({ context, isAndroid, asset }) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-sha1.har');
|
||||
const context = await contextFactory({ har: { path } });
|
||||
await context.routeFromHAR(path);
|
||||
const page = await context.newPage();
|
||||
await page.goto('http://no.playwright/');
|
||||
expect(await page.content()).toBe('<html><head></head><body>Hello, world</body></html>');
|
||||
|
|
@ -183,12 +255,34 @@ it('should round-trip har.zip', async ({ contextFactory, isAndroid, server }, te
|
|||
it.fixme(isAndroid);
|
||||
|
||||
const harPath = testInfo.outputPath('har.zip');
|
||||
const context1 = await contextFactory({ recordHar: { path: harPath } });
|
||||
const context1 = await contextFactory({ recordHar: { mode: 'minimal', path: harPath } });
|
||||
const page1 = await context1.newPage();
|
||||
await page1.goto(server.PREFIX + '/one-style.html');
|
||||
await context1.close();
|
||||
|
||||
const context2 = await contextFactory({ har: { path: harPath, fallback: 'abort' } });
|
||||
const context2 = await contextFactory();
|
||||
await context2.routeFromHAR(harPath, { notFound: 'abort' });
|
||||
const page2 = await context2.newPage();
|
||||
await page2.goto(server.PREFIX + '/one-style.html');
|
||||
expect(await page2.content()).toContain('hello, world!');
|
||||
await expect(page2.locator('body')).toHaveCSS('background-color', 'rgb(255, 192, 203)');
|
||||
});
|
||||
|
||||
it('should produce extracted zip', async ({ contextFactory, isAndroid, server }, testInfo) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const harPath = testInfo.outputPath('har.har');
|
||||
const context1 = await contextFactory({ recordHar: { mode: 'minimal', path: harPath, content: 'attach' } });
|
||||
const page1 = await context1.newPage();
|
||||
await page1.goto(server.PREFIX + '/one-style.html');
|
||||
await context1.close();
|
||||
|
||||
expect(fs.existsSync(harPath)).toBeTruthy();
|
||||
const har = fs.readFileSync(harPath, 'utf-8');
|
||||
expect(har).not.toContain('background-color');
|
||||
|
||||
const context2 = await contextFactory();
|
||||
await context2.routeFromHAR(harPath, { notFound: 'abort' });
|
||||
const page2 = await context2.newPage();
|
||||
await page2.goto(server.PREFIX + '/one-style.html');
|
||||
expect(await page2.content()).toContain('hello, world!');
|
||||
|
|
@ -199,7 +293,7 @@ it('should round-trip extracted har.zip', async ({ contextFactory, isAndroid, se
|
|||
it.fixme(isAndroid);
|
||||
|
||||
const harPath = testInfo.outputPath('har.zip');
|
||||
const context1 = await contextFactory({ recordHar: { path: harPath } });
|
||||
const context1 = await contextFactory({ recordHar: { mode: 'minimal', path: harPath } });
|
||||
const page1 = await context1.newPage();
|
||||
await page1.goto(server.PREFIX + '/one-style.html');
|
||||
await context1.close();
|
||||
|
|
@ -207,7 +301,8 @@ it('should round-trip extracted har.zip', async ({ contextFactory, isAndroid, se
|
|||
const harDir = testInfo.outputPath('hardir');
|
||||
await extractZip(harPath, { dir: harDir });
|
||||
|
||||
const context2 = await contextFactory({ har: { path: path.join(harDir, 'har.har') } });
|
||||
const context2 = await contextFactory();
|
||||
await context2.routeFromHAR(path.join(harDir, 'har.har'));
|
||||
const page2 = await context2.newPage();
|
||||
await page2.goto(server.PREFIX + '/one-style.html');
|
||||
expect(await page2.content()).toContain('hello, world!');
|
||||
|
|
@ -222,7 +317,7 @@ it('should round-trip har with postData', async ({ contextFactory, isAndroid, se
|
|||
});
|
||||
|
||||
const harPath = testInfo.outputPath('har.zip');
|
||||
const context1 = await contextFactory({ recordHar: { path: harPath } });
|
||||
const context1 = await contextFactory({ recordHar: { mode: 'minimal', path: harPath } });
|
||||
const page1 = await context1.newPage();
|
||||
await page1.goto(server.EMPTY_PAGE);
|
||||
const fetchFunction = async (body: string) => {
|
||||
|
|
@ -235,7 +330,8 @@ it('should round-trip har with postData', async ({ contextFactory, isAndroid, se
|
|||
expect(await page1.evaluate(fetchFunction, '3')).toBe('3');
|
||||
await context1.close();
|
||||
|
||||
const context2 = await contextFactory({ har: { path: harPath } });
|
||||
const context2 = await contextFactory();
|
||||
await context2.routeFromHAR(harPath);
|
||||
const page2 = await context2.newPage();
|
||||
await page2.goto(server.EMPTY_PAGE);
|
||||
expect(await page2.evaluate(fetchFunction, '1')).toBe('1');
|
||||
|
|
@ -252,7 +348,7 @@ it('should disambiguate by header', async ({ contextFactory, isAndroid, server }
|
|||
});
|
||||
|
||||
const harPath = testInfo.outputPath('har.zip');
|
||||
const context1 = await contextFactory({ recordHar: { path: harPath } });
|
||||
const context1 = await contextFactory({ recordHar: { mode: 'minimal', path: harPath } });
|
||||
const page1 = await context1.newPage();
|
||||
await page1.goto(server.EMPTY_PAGE);
|
||||
|
||||
|
|
@ -274,7 +370,8 @@ it('should disambiguate by header', async ({ contextFactory, isAndroid, server }
|
|||
expect(await page1.evaluate(fetchFunction, 'baz3')).toBe('baz3');
|
||||
await context1.close();
|
||||
|
||||
const context2 = await contextFactory({ har: { path: harPath } });
|
||||
const context2 = await contextFactory();
|
||||
await context2.routeFromHAR(harPath);
|
||||
const page2 = await context2.newPage();
|
||||
await page2.goto(server.EMPTY_PAGE);
|
||||
expect(await page2.evaluate(fetchFunction, 'baz1')).toBe('baz1');
|
||||
|
|
@ -282,3 +379,57 @@ it('should disambiguate by header', async ({ contextFactory, isAndroid, server }
|
|||
expect(await page2.evaluate(fetchFunction, 'baz3')).toBe('baz3');
|
||||
expect(await page2.evaluate(fetchFunction, 'baz4')).toBe('baz1');
|
||||
});
|
||||
|
||||
it('should update har.zip for context', async ({ contextFactory, isAndroid, server }, testInfo) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const harPath = testInfo.outputPath('har.zip');
|
||||
const context1 = await contextFactory();
|
||||
await context1.routeFromHAR(harPath, { update: true });
|
||||
const page1 = await context1.newPage();
|
||||
await page1.goto(server.PREFIX + '/one-style.html');
|
||||
await context1.close();
|
||||
|
||||
const context2 = await contextFactory();
|
||||
await context2.routeFromHAR(harPath, { notFound: 'abort' });
|
||||
const page2 = await context2.newPage();
|
||||
await page2.goto(server.PREFIX + '/one-style.html');
|
||||
expect(await page2.content()).toContain('hello, world!');
|
||||
await expect(page2.locator('body')).toHaveCSS('background-color', 'rgb(255, 192, 203)');
|
||||
});
|
||||
|
||||
it('should update har.zip for page', async ({ contextFactory, isAndroid, server }, testInfo) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const harPath = testInfo.outputPath('har.zip');
|
||||
const context1 = await contextFactory();
|
||||
const page1 = await context1.newPage();
|
||||
await page1.routeFromHAR(harPath, { update: true });
|
||||
await page1.goto(server.PREFIX + '/one-style.html');
|
||||
await context1.close();
|
||||
|
||||
const context2 = await contextFactory();
|
||||
const page2 = await context2.newPage();
|
||||
await page2.routeFromHAR(harPath, { notFound: 'abort' });
|
||||
await page2.goto(server.PREFIX + '/one-style.html');
|
||||
expect(await page2.content()).toContain('hello, world!');
|
||||
await expect(page2.locator('body')).toHaveCSS('background-color', 'rgb(255, 192, 203)');
|
||||
});
|
||||
|
||||
it('should update extracted har.zip for page', async ({ contextFactory, isAndroid, server }, testInfo) => {
|
||||
it.fixme(isAndroid);
|
||||
|
||||
const harPath = testInfo.outputPath('har.har');
|
||||
const context1 = await contextFactory();
|
||||
const page1 = await context1.newPage();
|
||||
await page1.routeFromHAR(harPath, { update: true });
|
||||
await page1.goto(server.PREFIX + '/one-style.html');
|
||||
await context1.close();
|
||||
|
||||
const context2 = await contextFactory();
|
||||
const page2 = await context2.newPage();
|
||||
await page2.routeFromHAR(harPath, { notFound: 'abort' });
|
||||
await page2.goto(server.PREFIX + '/one-style.html');
|
||||
expect(await page2.content()).toContain('hello, world!');
|
||||
await expect(page2.locator('body')).toHaveCSS('background-color', 'rgb(255, 192, 203)');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -268,6 +268,26 @@ it('should chain fallback', async ({ context, page, server }) => {
|
|||
expect(intercepted).toEqual([3, 2, 1]);
|
||||
});
|
||||
|
||||
it('should chain fallback w/ dynamic URL', async ({ context, page, server }) => {
|
||||
const intercepted = [];
|
||||
await context.route('**/bar', route => {
|
||||
intercepted.push(1);
|
||||
route.fallback({ url: server.EMPTY_PAGE });
|
||||
});
|
||||
await context.route('**/foo', route => {
|
||||
intercepted.push(2);
|
||||
route.fallback({ url: 'http://localhost/bar' });
|
||||
});
|
||||
|
||||
await context.route('**/empty.html', route => {
|
||||
intercepted.push(3);
|
||||
route.fallback({ url: 'http://localhost/foo' });
|
||||
});
|
||||
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(intercepted).toEqual([3, 2, 1]);
|
||||
});
|
||||
|
||||
it('should not chain fulfill', async ({ context, page, server }) => {
|
||||
let failed = false;
|
||||
await context.route('**/empty.html', route => {
|
||||
|
|
|
|||
|
|
@ -228,7 +228,8 @@ it('should support har option', async ({ isAndroid, launchPersistent, asset }) =
|
|||
it.fixme(isAndroid);
|
||||
|
||||
const path = asset('har-fulfill.har');
|
||||
const { page } = await launchPersistent({ har: { path } });
|
||||
const { page } = await launchPersistent();
|
||||
await page.routeFromHAR(path);
|
||||
await page.goto('http://no.playwright/');
|
||||
// HAR contains a redirect for the script that should be followed automatically.
|
||||
expect(await page.evaluate('window.value')).toBe('foo');
|
||||
|
|
|
|||
|
|
@ -291,7 +291,7 @@ it('should omit content', async ({ contextFactory, server }, testInfo) => {
|
|||
await page.evaluate(() => fetch('/pptr.png').then(r => r.arrayBuffer()));
|
||||
const log = await getLog();
|
||||
expect(log.entries[0].response.content.text).toBe(undefined);
|
||||
expect(log.entries[0].response.content._sha1).toBe(undefined);
|
||||
expect(log.entries[0].response.content._file).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should omit content legacy', async ({ contextFactory, server }, testInfo) => {
|
||||
|
|
@ -300,7 +300,7 @@ it('should omit content legacy', async ({ contextFactory, server }, testInfo) =>
|
|||
await page.evaluate(() => fetch('/pptr.png').then(r => r.arrayBuffer()));
|
||||
const log = await getLog();
|
||||
expect(log.entries[0].response.content.text).toBe(undefined);
|
||||
expect(log.entries[0].response.content._sha1).toBe(undefined);
|
||||
expect(log.entries[0].response.content._file).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should attach content', async ({ contextFactory, server }, testInfo) => {
|
||||
|
|
@ -312,19 +312,19 @@ it('should attach content', async ({ contextFactory, server }, testInfo) => {
|
|||
|
||||
expect(log.entries[0].response.content.encoding).toBe(undefined);
|
||||
expect(log.entries[0].response.content.mimeType).toBe('text/html; charset=utf-8');
|
||||
expect(log.entries[0].response.content._sha1).toContain('75841480e2606c03389077304342fac2c58ccb1b');
|
||||
expect(log.entries[0].response.content._file).toContain('75841480e2606c03389077304342fac2c58ccb1b');
|
||||
expect(log.entries[0].response.content.size).toBeGreaterThanOrEqual(96);
|
||||
expect(log.entries[0].response.content.compression).toBe(0);
|
||||
|
||||
expect(log.entries[1].response.content.encoding).toBe(undefined);
|
||||
expect(log.entries[1].response.content.mimeType).toBe('text/css; charset=utf-8');
|
||||
expect(log.entries[1].response.content._sha1).toContain('79f739d7bc88e80f55b9891a22bf13a2b4e18adb');
|
||||
expect(log.entries[1].response.content._file).toContain('79f739d7bc88e80f55b9891a22bf13a2b4e18adb');
|
||||
expect(log.entries[1].response.content.size).toBeGreaterThanOrEqual(37);
|
||||
expect(log.entries[1].response.content.compression).toBe(0);
|
||||
|
||||
expect(log.entries[2].response.content.encoding).toBe(undefined);
|
||||
expect(log.entries[2].response.content.mimeType).toBe('image/png');
|
||||
expect(log.entries[2].response.content._sha1).toContain('a4c3a18f0bb83f5d9fe7ce561e065c36205762fa');
|
||||
expect(log.entries[2].response.content._file).toContain('a4c3a18f0bb83f5d9fe7ce561e065c36205762fa');
|
||||
expect(log.entries[2].response.content.size).toBeGreaterThanOrEqual(6000);
|
||||
expect(log.entries[2].response.content.compression).toBe(0);
|
||||
|
||||
|
|
@ -689,45 +689,6 @@ it('should have different hars for concurrent contexts', async ({ contextFactory
|
|||
}
|
||||
});
|
||||
|
||||
it('should include _requestref', async ({ contextFactory, server }, testInfo) => {
|
||||
const { page, getLog } = await pageWithHar(contextFactory, testInfo);
|
||||
const resp = await page.goto(server.EMPTY_PAGE);
|
||||
const log = await getLog();
|
||||
expect(log.entries.length).toBe(1);
|
||||
const entry = log.entries[0];
|
||||
expect(entry._requestref).toMatch(/^request@[a-f0-9]{32}$/);
|
||||
expect(entry._requestref).toBe((resp.request() as any)._guid);
|
||||
});
|
||||
|
||||
it('should include _requestref for redirects', async ({ contextFactory, server }, testInfo) => {
|
||||
server.setRedirect('/start', '/one-more');
|
||||
server.setRedirect('/one-more', server.EMPTY_PAGE);
|
||||
|
||||
const { page, getLog, context } = await pageWithHar(contextFactory, testInfo);
|
||||
|
||||
const requests = new Map<string, string>();
|
||||
context.on('request', request => {
|
||||
requests.set(request.url(), (request as any)._guid);
|
||||
});
|
||||
|
||||
await page.goto(server.PREFIX + '/start');
|
||||
|
||||
const log = await getLog();
|
||||
expect(log.entries.length).toBe(3);
|
||||
|
||||
const entryStart = log.entries[0];
|
||||
expect(entryStart.request.url).toBe(server.PREFIX + '/start');
|
||||
expect(entryStart._requestref).toBe(requests.get(entryStart.request.url));
|
||||
|
||||
const entryOneMore = log.entries[1];
|
||||
expect(entryOneMore.request.url).toBe(server.PREFIX + '/one-more');
|
||||
expect(entryOneMore._requestref).toBe(requests.get(entryOneMore.request.url));
|
||||
|
||||
const entryEmptyPage = log.entries[2];
|
||||
expect(entryEmptyPage.request.url).toBe(server.EMPTY_PAGE);
|
||||
expect(entryEmptyPage._requestref).toBe(requests.get(entryEmptyPage.request.url));
|
||||
});
|
||||
|
||||
it('should include API request', async ({ contextFactory, server }, testInfo) => {
|
||||
const { page, getLog } = await pageWithHar(contextFactory, testInfo);
|
||||
const url = server.PREFIX + '/simple.json';
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
import { browserTest as it, expect } from '../config/browserTest';
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
|
||||
async function checkFeatures(name: string, context: any, server: any) {
|
||||
try {
|
||||
|
|
@ -31,6 +32,7 @@ async function checkFeatures(name: string, context: any, server: any) {
|
|||
|
||||
it('safari-14-1', async ({ browser, browserName, platform, server, headless }) => {
|
||||
it.skip(browserName !== 'webkit');
|
||||
it.skip(browserName === 'webkit' && parseInt(os.release(), 10) < 20, 'WebKit for macOS 10.15 is frozen.');
|
||||
const context = await browser.newContext({
|
||||
deviceScaleFactor: 2
|
||||
});
|
||||
|
|
@ -73,6 +75,7 @@ it('safari-14-1', async ({ browser, browserName, platform, server, headless }) =
|
|||
|
||||
it('mobile-safari-14-1', async ({ playwright, browser, browserName, platform, server, headless }) => {
|
||||
it.skip(browserName !== 'webkit');
|
||||
it.skip(browserName === 'webkit' && parseInt(os.release(), 10) < 20, 'WebKit for macOS 10.15 is frozen.');
|
||||
const iPhone = playwright.devices['iPhone 12'];
|
||||
const context = await browser.newContext(iPhone);
|
||||
const { actual, expected } = await checkFeatures('mobile-safari-14-1', context, server);
|
||||
|
|
|
|||
|
|
@ -126,8 +126,8 @@ it('should inherit viewport size from browser context', async function({ browser
|
|||
expect(size).toEqual({ width: 400, height: 500 });
|
||||
});
|
||||
|
||||
it('should use viewport size from window features', async function({ browser, server, channel }) {
|
||||
it.fixme(channel === 'chromium-tip-of-tree', 'https://github.com/microsoft/playwright/issues/14787');
|
||||
it('should use viewport size from window features', async function({ browser, server, browserName }) {
|
||||
it.fixme(browserName === 'chromium', 'https://github.com/microsoft/playwright/issues/14787');
|
||||
const context = await browser.newContext({
|
||||
viewport: { width: 700, height: 700 }
|
||||
});
|
||||
|
|
|
|||
|
|
@ -128,3 +128,7 @@ it('should handle window', async ({ page, browserName, isElectron }) => {
|
|||
]);
|
||||
expect(error.message).toBe(browserName === 'chromium' ? 'Window' : '[object Window]');
|
||||
});
|
||||
|
||||
it('should remove a listener of a non-existing event handler', async ({ page }) => {
|
||||
page.removeListener('pageerror', () => {});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ it('should override request url', async ({ page, server }) => {
|
|||
const request = server.waitForRequest('/global-var.html');
|
||||
|
||||
let url: string;
|
||||
await page.route('**/foo', route => {
|
||||
await page.route('**/global-var.html', route => {
|
||||
url = route.request().url();
|
||||
route.continue();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
import { test as base, expect } from './pageTest';
|
||||
import fs from 'fs';
|
||||
import type { HARFile, HARResponse } from 'playwright-core/types/har';
|
||||
import type * as har from 'playwright-core/lib/server/har/har';
|
||||
|
||||
const it = base.extend<{
|
||||
// We access test servers at 10.0.2.2 from inside the browser on Android,
|
||||
|
|
@ -327,7 +327,7 @@ it('should fulfill with har response', async ({ page, isAndroid, asset }) => {
|
|||
it.fixme(isAndroid);
|
||||
|
||||
const harPath = asset('har-fulfill.har');
|
||||
const har = JSON.parse(await fs.promises.readFile(harPath, 'utf-8')) as HARFile;
|
||||
const har = JSON.parse(await fs.promises.readFile(harPath, 'utf-8')) as har.HARFile;
|
||||
await page.route('**/*', async route => {
|
||||
const response = findResponse(har, route.request().url());
|
||||
const headers = {};
|
||||
|
|
@ -346,7 +346,7 @@ it('should fulfill with har response', async ({ page, isAndroid, asset }) => {
|
|||
await expect(page.locator('body')).toHaveCSS('background-color', 'rgb(0, 255, 255)');
|
||||
});
|
||||
|
||||
function findResponse(har: HARFile, url: string): HARResponse {
|
||||
function findResponse(har: har.HARFile, url: string): har.Response {
|
||||
let entry;
|
||||
const originalUrl = url;
|
||||
while (url.trim()) {
|
||||
|
|
|
|||
|
|
@ -321,6 +321,26 @@ it('should not work with redirects', async ({ page, server }) => {
|
|||
expect(chain[i].redirectedTo()).toBe(i ? chain[i - 1] : null);
|
||||
});
|
||||
|
||||
it('should chain fallback w/ dynamic URL', async ({ page, server }) => {
|
||||
const intercepted = [];
|
||||
await page.route('**/bar', route => {
|
||||
intercepted.push(1);
|
||||
route.fallback({ url: server.EMPTY_PAGE });
|
||||
});
|
||||
await page.route('**/foo', route => {
|
||||
intercepted.push(2);
|
||||
route.fallback({ url: 'http://localhost/bar' });
|
||||
});
|
||||
|
||||
await page.route('**/empty.html', route => {
|
||||
intercepted.push(3);
|
||||
route.fallback({ url: 'http://localhost/foo' });
|
||||
});
|
||||
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(intercepted).toEqual([3, 2, 1]);
|
||||
});
|
||||
|
||||
it('should work with redirects for subresources', async ({ page, server }) => {
|
||||
const intercepted = [];
|
||||
await page.route('**/*', route => {
|
||||
|
|
|
|||
186
tests/playwright-test/playwright.config.spec.ts
Normal file
186
tests/playwright-test/playwright.config.spec.ts
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { test, expect } from './playwright-test-fixtures';
|
||||
|
||||
test('should fall back to launchOptions', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
use: {
|
||||
launchOptions: {
|
||||
headless: false,
|
||||
channel: 'chrome',
|
||||
}
|
||||
}
|
||||
};
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('pass', async ({ headless, channel }) => {
|
||||
expect.soft(headless).toBe(false);
|
||||
expect.soft(channel).toBe('chrome');
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(1);
|
||||
});
|
||||
|
||||
test('should override launchOptions', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
use: {
|
||||
headless: false,
|
||||
channel: 'chrome',
|
||||
launchOptions: {
|
||||
headless: true,
|
||||
channel: 'msedge',
|
||||
}
|
||||
}
|
||||
};
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('pass', async ({ headless, channel }) => {
|
||||
expect.soft(headless).toBe(false);
|
||||
expect.soft(channel).toBe('chrome');
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(1);
|
||||
});
|
||||
|
||||
test('should respect contextOptions', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
use: {
|
||||
contextOptions: {
|
||||
acceptDownloads: false,
|
||||
bypassCSP: true,
|
||||
colorScheme: 'dark',
|
||||
deviceScaleFactor: 2,
|
||||
extraHTTPHeaders: {'foo': 'bar'},
|
||||
hasTouch: true,
|
||||
ignoreHTTPSErrors: true,
|
||||
isMobile: true,
|
||||
javaScriptEnabled: true,
|
||||
locale: 'fr-FR',
|
||||
offline: true,
|
||||
permissions: ['geolocation'],
|
||||
timezoneId: 'TIMEZONE',
|
||||
userAgent: 'UA',
|
||||
viewport: null
|
||||
}
|
||||
}
|
||||
};
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('pass', async ({ acceptDownloads, bypassCSP, colorScheme, deviceScaleFactor, extraHTTPHeaders, hasTouch, ignoreHTTPSErrors, isMobile, javaScriptEnabled, locale, offline, permissions, timezoneId, userAgent, viewport }) => {
|
||||
expect.soft(acceptDownloads).toBe(false);
|
||||
expect.soft(bypassCSP).toBe(true);
|
||||
expect.soft(colorScheme).toBe('dark');
|
||||
expect.soft(deviceScaleFactor).toBe(2);
|
||||
expect.soft(extraHTTPHeaders).toEqual({'foo': 'bar'});
|
||||
expect.soft(hasTouch).toBe(true);
|
||||
expect.soft(ignoreHTTPSErrors).toBe(true);
|
||||
expect.soft(isMobile).toBe(true);
|
||||
expect.soft(javaScriptEnabled).toBe(true);
|
||||
expect.soft(locale).toBe('fr-FR');
|
||||
expect.soft(offline).toBe(true);
|
||||
expect.soft(permissions).toEqual(['geolocation']);
|
||||
expect.soft(timezoneId).toBe('TIMEZONE');
|
||||
expect.soft(userAgent).toBe('UA');
|
||||
expect.soft(viewport).toBe(null);
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(1);
|
||||
});
|
||||
|
||||
test('should override contextOptions', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
use: {
|
||||
acceptDownloads: false,
|
||||
bypassCSP: true,
|
||||
colorScheme: 'dark',
|
||||
deviceScaleFactor: 2,
|
||||
extraHTTPHeaders: {'foo': 'bar'},
|
||||
hasTouch: true,
|
||||
ignoreHTTPSErrors: true,
|
||||
isMobile: true,
|
||||
javaScriptEnabled: true,
|
||||
locale: 'fr-FR',
|
||||
offline: true,
|
||||
permissions: ['geolocation'],
|
||||
timezoneId: 'TIMEZONE',
|
||||
userAgent: 'UA',
|
||||
viewport: null,
|
||||
contextOptions: {
|
||||
acceptDownloads: true,
|
||||
bypassCSP: false,
|
||||
colorScheme: 'light',
|
||||
deviceScaleFactor: 1,
|
||||
extraHTTPHeaders: {'foo': 'bar2'},
|
||||
hasTouch: false,
|
||||
ignoreHTTPSErrors: false,
|
||||
isMobile: false,
|
||||
javaScriptEnabled: false,
|
||||
locale: 'en-US',
|
||||
offline: false,
|
||||
permissions: [],
|
||||
timezoneId: 'TIMEZONE 2',
|
||||
userAgent: 'UA 2',
|
||||
viewport: { width: 500, height: 500 }
|
||||
}
|
||||
}
|
||||
};
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('pass', async ({ acceptDownloads, bypassCSP, colorScheme, deviceScaleFactor, extraHTTPHeaders, hasTouch, ignoreHTTPSErrors, isMobile, javaScriptEnabled, locale, offline, permissions, timezoneId, userAgent, viewport }) => {
|
||||
expect.soft(acceptDownloads).toBe(false);
|
||||
expect.soft(bypassCSP).toBe(true);
|
||||
expect.soft(colorScheme).toBe('dark');
|
||||
expect.soft(deviceScaleFactor).toBe(2);
|
||||
expect.soft(extraHTTPHeaders).toEqual({'foo': 'bar'});
|
||||
expect.soft(hasTouch).toBe(true);
|
||||
expect.soft(ignoreHTTPSErrors).toBe(true);
|
||||
expect.soft(isMobile).toBe(true);
|
||||
expect.soft(javaScriptEnabled).toBe(true);
|
||||
expect.soft(locale).toBe('fr-FR');
|
||||
expect.soft(offline).toBe(true);
|
||||
expect.soft(permissions).toEqual(['geolocation']);
|
||||
expect.soft(timezoneId).toBe('TIMEZONE');
|
||||
expect.soft(userAgent).toBe('UA');
|
||||
expect.soft(viewport).toBe(null);
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(1);
|
||||
});
|
||||
|
|
@ -602,18 +602,3 @@ test('should pass fixture defaults to tests', async ({ runInlineTest }) => {
|
|||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(1);
|
||||
});
|
||||
|
||||
test('should support har option', async ({ runInlineTest, asset }) => {
|
||||
const result = await runInlineTest({
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
test.use({ har: { path: ${JSON.stringify(asset('har-fulfill.har'))} }});
|
||||
test('pass', async ({ page }) => {
|
||||
await page.goto('http://no.playwright/');
|
||||
expect(await page.evaluate('window.value')).toBe('foo');
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(1);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ echo "Building playwright-core package"
|
|||
node ../../utils/pack_package.js playwright-core ./output/playwright-core.tgz
|
||||
|
||||
echo "Building api.json and protocol.yml"
|
||||
node ../../utils/doclint/generateApiJson.js > ./output/api.json
|
||||
API_JSON_MODE=1 node ../../utils/doclint/generateApiJson.js > ./output/api.json
|
||||
cp ../../packages/playwright-core/src/protocol/protocol.yml ./output/
|
||||
|
||||
function build {
|
||||
|
|
|
|||
|
|
@ -70,19 +70,31 @@ async function run() {
|
|||
writeAssumeNoop(path.join(PROJECT_DIR, 'README.md'), content, dirtyFiles);
|
||||
}
|
||||
|
||||
let playwrightVersion = require(path.join(PROJECT_DIR, 'package.json')).version;
|
||||
if (playwrightVersion.endsWith('-next'))
|
||||
playwrightVersion = playwrightVersion.substring(0, playwrightVersion.indexOf('-next'));
|
||||
|
||||
// Patch docker version in docs
|
||||
{
|
||||
let playwrightVersion = require(path.join(PROJECT_DIR, 'package.json')).version;
|
||||
if (playwrightVersion.endsWith('-next'))
|
||||
playwrightVersion = playwrightVersion.substring(0, playwrightVersion.indexOf('-next'));
|
||||
const regex = new RegExp("(mcr.microsoft.com/playwright[^: ]*):?([^ ]*)");
|
||||
for (const filePath of getAllMarkdownFiles(path.join(PROJECT_DIR, 'docs'))) {
|
||||
let content = fs.readFileSync(filePath).toString();
|
||||
content = content.replace(new RegExp('(mcr.microsoft.com/playwright[^:]*):([\\w\\d-.]+)', 'ig'), (match, imageName, imageVersion) => {
|
||||
return `${imageName}:v${playwrightVersion}-focal`;
|
||||
const [version, distroName] = imageVersion.split('-');
|
||||
return `${imageName}:v${playwrightVersion}-${distroName ?? 'focal'}`;
|
||||
});
|
||||
writeAssumeNoop(filePath, content, dirtyFiles);
|
||||
}
|
||||
|
||||
// Patch pom.xml
|
||||
{
|
||||
const introPath = path.join(PROJECT_DIR, 'docs', 'src', 'intro-java.md');
|
||||
const pomVersionRe = new RegExp('^(\\s*<artifactId>playwright<\\/artifactId>\\n\\s*<version>)(.*)(<\\/version>)$', 'gm');
|
||||
let content = fs.readFileSync(introPath).toString();
|
||||
const majorVersion = playwrightVersion.replace(new RegExp('((\\d+\\.){2})(\\d+)'), '$10')
|
||||
content = content.replace(pomVersionRe, '$1' + majorVersion + '$3');
|
||||
writeAssumeNoop(introPath, content, dirtyFiles);
|
||||
}
|
||||
}
|
||||
|
||||
// Update device descriptors
|
||||
|
|
|
|||
2
utils/generate_types/overrides-test.d.ts
vendored
2
utils/generate_types/overrides-test.d.ts
vendored
|
|
@ -173,7 +173,6 @@ type BrowserName = 'chromium' | 'firefox' | 'webkit';
|
|||
type BrowserChannel = Exclude<LaunchOptions['channel'], undefined>;
|
||||
type ColorScheme = Exclude<BrowserContextOptions['colorScheme'], undefined>;
|
||||
type ExtraHTTPHeaders = Exclude<BrowserContextOptions['extraHTTPHeaders'], undefined>;
|
||||
type HAROptions = Exclude<BrowserContextOptions['har'], undefined>;
|
||||
type Proxy = Exclude<BrowserContextOptions['proxy'], undefined>;
|
||||
type StorageState = Exclude<BrowserContextOptions['storageState'], undefined>;
|
||||
type ServiceWorkerPolicy = Exclude<BrowserContextOptions['serviceWorkers'], undefined>;
|
||||
|
|
@ -216,7 +215,6 @@ export interface PlaywrightTestOptions {
|
|||
deviceScaleFactor: number | undefined;
|
||||
extraHTTPHeaders: ExtraHTTPHeaders | undefined;
|
||||
geolocation: Geolocation | undefined;
|
||||
har: HAROptions | undefined;
|
||||
hasTouch: boolean | undefined;
|
||||
httpCredentials: HTTPCredentials | undefined;
|
||||
ignoreHTTPSErrors: boolean | undefined;
|
||||
|
|
|
|||
1
utils/generate_types/overrides.d.ts
vendored
1
utils/generate_types/overrides.d.ts
vendored
|
|
@ -16,7 +16,6 @@
|
|||
import { Protocol } from 'playwright-core/types/protocol';
|
||||
import { ChildProcess } from 'child_process';
|
||||
import { EventEmitter } from 'events';
|
||||
import { HARResponse } from 'playwright-core/types/har';
|
||||
import { Readable } from 'stream';
|
||||
import { ReadStream } from 'fs';
|
||||
import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from 'playwright-core/types/structs';
|
||||
|
|
|
|||
|
|
@ -264,7 +264,10 @@ function innerRenderMdNode(indent, node, lastNode, result, maxColumns) {
|
|||
|
||||
if (node.type === 'code') {
|
||||
newLine();
|
||||
result.push(`${indent}\`\`\`${codeLangToHighlighter(node.codeLang)}`);
|
||||
if (process.env.API_JSON_MODE)
|
||||
result.push(`${indent}\`\`\`${node.codeLang}`);
|
||||
else
|
||||
result.push(`${indent}\`\`\`${codeLangToHighlighter(node.codeLang)}`);
|
||||
for (const line of node.lines)
|
||||
result.push(indent + line);
|
||||
result.push(`${indent}\`\`\``);
|
||||
|
|
|
|||
Loading…
Reference in a new issue