mirror of
https://github.com/microsoft/playwright-java.git
synced 2025-09-08 21:01:00 +00:00
Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
eb1fea9907 | ||
|
dd99ce8b34 | ||
|
ed8e9c434f | ||
|
aee298b293 | ||
|
fd2ab4708a | ||
|
2a6cdff664 | ||
|
44161e0558 | ||
|
954b1c43ef | ||
|
f4c7b9734f | ||
|
dd87b300fb | ||
|
f83c03af68 | ||
|
d26dd0b112 | ||
|
0cf8c4e17f | ||
|
1fb593e1e2 | ||
|
915ee8d64c | ||
|
9df2165e93 | ||
|
859eb9b8b8 | ||
|
f28cb55795 | ||
|
07867c2db5 | ||
|
b4151b1231 | ||
|
4698b91d8e | ||
|
8a89e36ce3 | ||
|
f1e6100b33 | ||
|
fddb146d73 | ||
|
9ad596ac75 | ||
|
8cca01851a | ||
|
478417bb56 | ||
|
739202fddf | ||
|
8593941005 | ||
|
b2852f5d57 |
@ -1,6 +1,10 @@
|
||||
trigger: none
|
||||
pr: none
|
||||
|
||||
trigger:
|
||||
tags:
|
||||
include:
|
||||
- '*'
|
||||
|
||||
resources:
|
||||
repositories:
|
||||
- repository: 1esPipelines
|
||||
@ -33,8 +37,8 @@ extends:
|
||||
artifact: esrp-build
|
||||
steps:
|
||||
- bash: |
|
||||
if [[ ! "$CURRENT_BRANCH" =~ ^release-.* ]]; then
|
||||
echo "Can only publish from a release branch."
|
||||
if [[ ! "$CURRENT_BRANCH" =~ ^v1\..* ]]; then
|
||||
echo "Can only publish from a release tag branch (v1.*)."
|
||||
echo "Unexpected branch name: $CURRENT_BRANCH"
|
||||
exit 1
|
||||
fi
|
||||
|
4
.github/workflows/publish_docker.yml
vendored
4
.github/workflows/publish_docker.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
||||
environment: Docker
|
||||
if: github.repository == 'microsoft/playwright-java'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Azure login
|
||||
uses: azure/login@v2
|
||||
with:
|
||||
@ -26,5 +26,5 @@ jobs:
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
platforms: arm64
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- run: ./utils/docker/publish_docker.sh stable
|
||||
|
12
.github/workflows/test.yml
vendored
12
.github/workflows/test.yml
vendored
@ -20,9 +20,9 @@ jobs:
|
||||
browser: [chromium, firefox, webkit]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up JDK 1.8
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: zulu
|
||||
java-version: 8
|
||||
@ -65,13 +65,13 @@ jobs:
|
||||
browser-channel: msedge
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Install Media Pack
|
||||
if: matrix.os == 'windows-latest'
|
||||
shell: powershell
|
||||
run: Install-WindowsFeature Server-Media-Foundation
|
||||
- name: Set up JDK 1.8
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: zulu
|
||||
java-version: 8
|
||||
@ -100,9 +100,9 @@ jobs:
|
||||
browser: [chromium, firefox, webkit]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: adopt
|
||||
java-version: 21
|
||||
|
2
.github/workflows/test_cli.yml
vendored
2
.github/workflows/test_cli.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
|
29
.github/workflows/test_docker.yml
vendored
29
.github/workflows/test_docker.yml
vendored
@ -20,16 +20,33 @@ jobs:
|
||||
test:
|
||||
name: Test
|
||||
timeout-minutes: 120
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
env:
|
||||
PW_MAX_RETRIES: 3
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
flavor: [jammy, noble]
|
||||
runs-on: [ubuntu-24.04, ubuntu-24.04-arm]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Build Docker image
|
||||
run: bash utils/docker/build.sh --amd64 ${{ matrix.flavor }} playwright-java:localbuild-${{ matrix.flavor }}
|
||||
- name: Test
|
||||
run: |
|
||||
CONTAINER_ID="$(docker run --rm --ipc=host -v $(pwd):/root/playwright --name playwright-docker-test -d -t playwright-java:localbuild-${{ matrix.flavor }} /bin/bash)"
|
||||
docker exec "${CONTAINER_ID}" /root/playwright/tools/test-local-installation/create_project_and_run_tests.sh
|
||||
ARCH="${{ matrix.runs-on == 'ubuntu-24.04-arm' && 'arm64' || 'amd64' }}"
|
||||
bash utils/docker/build.sh --$ARCH ${{ matrix.flavor }} playwright-java:localbuild-${{ matrix.flavor }}
|
||||
- name: Start container
|
||||
run: |
|
||||
CONTAINER_ID=$(docker run --rm -e CI -e PW_MAX_RETRIES --ipc=host -v "$(pwd)":/root/playwright --name playwright-docker-test -d -t playwright-java:localbuild-${{ matrix.flavor }} /bin/bash)
|
||||
echo "CONTAINER_ID=$CONTAINER_ID" >> $GITHUB_ENV
|
||||
|
||||
- name: Run test in container
|
||||
run: |
|
||||
docker exec "$CONTAINER_ID" /root/playwright/tools/test-local-installation/create_project_and_run_tests.sh
|
||||
|
||||
- name: Test ClassLoader
|
||||
run: |
|
||||
docker exec "${CONTAINER_ID}" /root/playwright/tools/test-spring-boot-starter/package_and_run_async_test.sh
|
||||
|
||||
- name: Stop container
|
||||
run: |
|
||||
docker stop "$CONTAINER_ID"
|
||||
|
21
.github/workflows/trigger_internal_tests.yml
vendored
21
.github/workflows/trigger_internal_tests.yml
vendored
@ -1,21 +0,0 @@
|
||||
name: "Internal Tests"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release-*
|
||||
|
||||
jobs:
|
||||
trigger:
|
||||
name: "trigger"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- run: |
|
||||
curl -X POST \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-H "Authorization: token ${GH_TOKEN}" \
|
||||
--data "{\"event_type\": \"playwright_tests_java\", \"client_payload\": {\"ref\": \"${GITHUB_SHA}\"}}" \
|
||||
https://api.github.com/repos/microsoft/playwright-browsers/dispatches
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.REPOSITORY_DISPATCH_PERSONAL_ACCESS_TOKEN }}
|
2
.github/workflows/verify_api.yml
vendored
2
.github/workflows/verify_api.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Download drivers
|
||||
run: scripts/download_driver.sh
|
||||
- name: Regenerate APIs
|
||||
|
@ -32,9 +32,9 @@ scripts/download_driver.sh
|
||||
mvn compile
|
||||
mvn test
|
||||
# Executing a single test
|
||||
BROWSER=chromium mvn test --projects=playwright -Dtest=TestPageNetworkSizes#shouldHaveTheCorrectResponseBodySize
|
||||
BROWSER=chromium mvn test -Dtest=TestPageNetworkSizes#shouldHaveTheCorrectResponseBodySize
|
||||
# Executing a single test class
|
||||
BROWSER=chromium mvn test --projects=playwright -Dtest=TestPageNetworkSizes
|
||||
BROWSER=chromium mvn test -Dtest=TestPageNetworkSizes
|
||||
```
|
||||
|
||||
### Generating API
|
||||
|
3
CodeQL.yml
Normal file
3
CodeQL.yml
Normal file
@ -0,0 +1,3 @@
|
||||
path_classifiers:
|
||||
tests:
|
||||
- "playwright/src/test/**"
|
@ -10,9 +10,9 @@ Playwright is a Java library to automate [Chromium](https://www.chromium.org/Hom
|
||||
|
||||
| | Linux | macOS | Windows |
|
||||
| :--- | :---: | :---: | :---: |
|
||||
| Chromium <!-- GEN:chromium-version -->134.0.6998.35<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| WebKit <!-- GEN:webkit-version -->18.4<!-- GEN:stop --> | ✅ | ✅ | ✅ |
|
||||
| Firefox <!-- GEN:firefox-version -->135.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Chromium <!-- GEN:chromium-version -->140.0.7339.16<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| WebKit <!-- GEN:webkit-version -->26.0<!-- GEN:stop --> | ✅ | ✅ | ✅ |
|
||||
| Firefox <!-- GEN:firefox-version -->141.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
|
||||
## Documentation
|
||||
|
||||
|
@ -114,7 +114,7 @@ public class DriverJar extends Driver {
|
||||
}
|
||||
|
||||
public static URI getDriverResourceURI() throws URISyntaxException {
|
||||
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
|
||||
ClassLoader classloader = DriverJar.class.getClassLoader();
|
||||
return classloader.getResource("driver/" + platformDir()).toURI();
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
<name>Playwright Client Examples</name>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<playwright.version>1.51.0</playwright.version>
|
||||
<playwright.version>1.55.0</playwright.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
@ -57,6 +57,15 @@
|
||||
<groupId>org.java-websocket</groupId>
|
||||
<artifactId>Java-WebSocket</artifactId>
|
||||
</dependency>
|
||||
<!--
|
||||
The following slf4j-simple dependency resolves the warning:
|
||||
'SLF4J(W): No SLF4J providers were found.'
|
||||
This warning is produced by the org.java-websocket library.
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
|
@ -51,6 +51,10 @@ public interface APIRequest {
|
||||
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
|
||||
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
|
||||
*
|
||||
* <p> Client certificate authentication is only active when at least one client certificate is provided. If you want to reject
|
||||
* all client certificates sent by the server, you need to provide a client certificate with an {@code origin} that does
|
||||
* not match any of the domains you plan to visit.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
|
||||
* replacing {@code localhost} with {@code local.playwright}.
|
||||
*/
|
||||
@ -72,6 +76,12 @@ public interface APIRequest {
|
||||
* Whether to ignore HTTPS errors when sending network requests. Defaults to {@code false}.
|
||||
*/
|
||||
public Boolean ignoreHTTPSErrors;
|
||||
/**
|
||||
* Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is
|
||||
* exceeded. Defaults to {@code 20}. Pass {@code 0} to not follow redirects. This can be overwritten for each request
|
||||
* individually.
|
||||
*/
|
||||
public Integer maxRedirects;
|
||||
/**
|
||||
* Network proxy settings.
|
||||
*/
|
||||
@ -128,6 +138,10 @@ public interface APIRequest {
|
||||
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
|
||||
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
|
||||
*
|
||||
* <p> Client certificate authentication is only active when at least one client certificate is provided. If you want to reject
|
||||
* all client certificates sent by the server, you need to provide a client certificate with an {@code origin} that does
|
||||
* not match any of the domains you plan to visit.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
|
||||
* replacing {@code localhost} with {@code local.playwright}.
|
||||
*/
|
||||
@ -171,6 +185,15 @@ public interface APIRequest {
|
||||
this.ignoreHTTPSErrors = ignoreHTTPSErrors;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is
|
||||
* exceeded. Defaults to {@code 20}. Pass {@code 0} to not follow redirects. This can be overwritten for each request
|
||||
* individually.
|
||||
*/
|
||||
public NewContextOptions setMaxRedirects(int maxRedirects) {
|
||||
this.maxRedirects = maxRedirects;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Network proxy settings.
|
||||
*/
|
||||
|
@ -106,6 +106,10 @@ public interface Browser extends AutoCloseable {
|
||||
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
|
||||
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
|
||||
*
|
||||
* <p> Client certificate authentication is only active when at least one client certificate is provided. If you want to reject
|
||||
* all client certificates sent by the server, you need to provide a client certificate with an {@code origin} that does
|
||||
* not match any of the domains you plan to visit.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
|
||||
* replacing {@code localhost} with {@code local.playwright}.
|
||||
*/
|
||||
@ -323,6 +327,10 @@ public interface Browser extends AutoCloseable {
|
||||
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
|
||||
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
|
||||
*
|
||||
* <p> Client certificate authentication is only active when at least one client certificate is provided. If you want to reject
|
||||
* all client certificates sent by the server, you need to provide a client certificate with an {@code origin} that does
|
||||
* not match any of the domains you plan to visit.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
|
||||
* replacing {@code localhost} with {@code local.playwright}.
|
||||
*/
|
||||
@ -674,6 +682,10 @@ public interface Browser extends AutoCloseable {
|
||||
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
|
||||
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
|
||||
*
|
||||
* <p> Client certificate authentication is only active when at least one client certificate is provided. If you want to reject
|
||||
* all client certificates sent by the server, you need to provide a client certificate with an {@code origin} that does
|
||||
* not match any of the domains you plan to visit.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
|
||||
* replacing {@code localhost} with {@code local.playwright}.
|
||||
*/
|
||||
@ -891,6 +903,10 @@ public interface Browser extends AutoCloseable {
|
||||
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
|
||||
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
|
||||
*
|
||||
* <p> Client certificate authentication is only active when at least one client certificate is provided. If you want to reject
|
||||
* all client certificates sent by the server, you need to provide a client certificate with an {@code origin} that does
|
||||
* not match any of the domains you plan to visit.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
|
||||
* replacing {@code localhost} with {@code local.playwright}.
|
||||
*/
|
||||
|
@ -411,8 +411,6 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* Set to {@code true} to include <a href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API">IndexedDB</a> in
|
||||
* the storage state snapshot. If your application uses IndexedDB to store authentication tokens, like Firebase
|
||||
* Authentication, enable this.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> IndexedDBs with typed arrays are currently not supported.
|
||||
*/
|
||||
public Boolean indexedDB;
|
||||
/**
|
||||
@ -425,8 +423,6 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* Set to {@code true} to include <a href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API">IndexedDB</a> in
|
||||
* the storage state snapshot. If your application uses IndexedDB to store authentication tokens, like Firebase
|
||||
* Authentication, enable this.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> IndexedDBs with typed arrays are currently not supported.
|
||||
*/
|
||||
public StorageStateOptions setIndexedDB(boolean indexedDB) {
|
||||
this.indexedDB = indexedDB;
|
||||
@ -600,7 +596,8 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*/
|
||||
List<Page> backgroundPages();
|
||||
/**
|
||||
* Returns the browser instance of the context. If it was launched as a persistent context null gets returned.
|
||||
* Gets the browser instance that owns the context. Returns {@code null} if the context is created outside of normal
|
||||
* browser, e.g. Android or Electron.
|
||||
*
|
||||
* @since v1.8
|
||||
*/
|
||||
@ -875,6 +872,7 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* <li> {@code "notifications"}</li>
|
||||
* <li> {@code "payment-handler"}</li>
|
||||
* <li> {@code "storage-access"}</li>
|
||||
* <li> {@code "local-fonts"}</li>
|
||||
* </ul>
|
||||
* @since v1.8
|
||||
*/
|
||||
@ -907,6 +905,7 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* <li> {@code "notifications"}</li>
|
||||
* <li> {@code "payment-handler"}</li>
|
||||
* <li> {@code "storage-access"}</li>
|
||||
* <li> {@code "local-fonts"}</li>
|
||||
* </ul>
|
||||
* @since v1.8
|
||||
*/
|
||||
@ -996,8 +995,8 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
@ -1052,8 +1051,8 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
@ -1106,8 +1105,8 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
@ -1162,8 +1161,8 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
@ -1216,8 +1215,8 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
@ -1272,8 +1271,8 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
|
@ -209,6 +209,9 @@ public interface BrowserType {
|
||||
/**
|
||||
* Firefox user preferences. Learn more about the Firefox user preferences at <a
|
||||
* href="https://support.mozilla.org/en-US/kb/about-config-editor-firefox">{@code about:config}</a>.
|
||||
*
|
||||
* <p> You can also provide a path to a custom <a href="https://mozilla.github.io/policy-templates/">{@code policies.json}
|
||||
* file</a> via {@code PLAYWRIGHT_FIREFOX_POLICIES_JSON} environment variable.
|
||||
*/
|
||||
public Map<String, Object> firefoxUserPrefs;
|
||||
/**
|
||||
@ -226,8 +229,8 @@ public interface BrowserType {
|
||||
/**
|
||||
* Whether to run browser in headless mode. More details for <a
|
||||
* href="https://developers.google.com/web/updates/2017/04/headless-chrome">Chromium</a> and <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode">Firefox</a>. Defaults to {@code true}
|
||||
* unless the {@code devtools} option is {@code true}.
|
||||
* href="https://hacks.mozilla.org/2017/12/using-headless-mode-in-firefox/">Firefox</a>. Defaults to {@code true} unless
|
||||
* the {@code devtools} option is {@code true}.
|
||||
*/
|
||||
public Boolean headless;
|
||||
/**
|
||||
@ -339,6 +342,9 @@ public interface BrowserType {
|
||||
/**
|
||||
* Firefox user preferences. Learn more about the Firefox user preferences at <a
|
||||
* href="https://support.mozilla.org/en-US/kb/about-config-editor-firefox">{@code about:config}</a>.
|
||||
*
|
||||
* <p> You can also provide a path to a custom <a href="https://mozilla.github.io/policy-templates/">{@code policies.json}
|
||||
* file</a> via {@code PLAYWRIGHT_FIREFOX_POLICIES_JSON} environment variable.
|
||||
*/
|
||||
public LaunchOptions setFirefoxUserPrefs(Map<String, Object> firefoxUserPrefs) {
|
||||
this.firefoxUserPrefs = firefoxUserPrefs;
|
||||
@ -368,8 +374,8 @@ public interface BrowserType {
|
||||
/**
|
||||
* Whether to run browser in headless mode. More details for <a
|
||||
* href="https://developers.google.com/web/updates/2017/04/headless-chrome">Chromium</a> and <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode">Firefox</a>. Defaults to {@code true}
|
||||
* unless the {@code devtools} option is {@code true}.
|
||||
* href="https://hacks.mozilla.org/2017/12/using-headless-mode-in-firefox/">Firefox</a>. Defaults to {@code true} unless
|
||||
* the {@code devtools} option is {@code true}.
|
||||
*/
|
||||
public LaunchOptions setHeadless(boolean headless) {
|
||||
this.headless = headless;
|
||||
@ -485,6 +491,10 @@ public interface BrowserType {
|
||||
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
|
||||
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
|
||||
*
|
||||
* <p> Client certificate authentication is only active when at least one client certificate is provided. If you want to reject
|
||||
* all client certificates sent by the server, you need to provide a client certificate with an {@code origin} that does
|
||||
* not match any of the domains you plan to visit.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
|
||||
* replacing {@code localhost} with {@code local.playwright}.
|
||||
*/
|
||||
@ -535,6 +545,9 @@ public interface BrowserType {
|
||||
/**
|
||||
* Firefox user preferences. Learn more about the Firefox user preferences at <a
|
||||
* href="https://support.mozilla.org/en-US/kb/about-config-editor-firefox">{@code about:config}</a>.
|
||||
*
|
||||
* <p> You can also provide a path to a custom <a href="https://mozilla.github.io/policy-templates/">{@code policies.json}
|
||||
* file</a> via {@code PLAYWRIGHT_FIREFOX_POLICIES_JSON} environment variable.
|
||||
*/
|
||||
public Map<String, Object> firefoxUserPrefs;
|
||||
/**
|
||||
@ -564,8 +577,8 @@ public interface BrowserType {
|
||||
/**
|
||||
* Whether to run browser in headless mode. More details for <a
|
||||
* href="https://developers.google.com/web/updates/2017/04/headless-chrome">Chromium</a> and <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode">Firefox</a>. Defaults to {@code true}
|
||||
* unless the {@code devtools} option is {@code true}.
|
||||
* href="https://hacks.mozilla.org/2017/12/using-headless-mode-in-firefox/">Firefox</a>. Defaults to {@code true} unless
|
||||
* the {@code devtools} option is {@code true}.
|
||||
*/
|
||||
public Boolean headless;
|
||||
/**
|
||||
@ -804,6 +817,10 @@ public interface BrowserType {
|
||||
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
|
||||
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
|
||||
*
|
||||
* <p> Client certificate authentication is only active when at least one client certificate is provided. If you want to reject
|
||||
* all client certificates sent by the server, you need to provide a client certificate with an {@code origin} that does
|
||||
* not match any of the domains you plan to visit.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
|
||||
* replacing {@code localhost} with {@code local.playwright}.
|
||||
*/
|
||||
@ -881,6 +898,9 @@ public interface BrowserType {
|
||||
/**
|
||||
* Firefox user preferences. Learn more about the Firefox user preferences at <a
|
||||
* href="https://support.mozilla.org/en-US/kb/about-config-editor-firefox">{@code about:config}</a>.
|
||||
*
|
||||
* <p> You can also provide a path to a custom <a href="https://mozilla.github.io/policy-templates/">{@code policies.json}
|
||||
* file</a> via {@code PLAYWRIGHT_FIREFOX_POLICIES_JSON} environment variable.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setFirefoxUserPrefs(Map<String, Object> firefoxUserPrefs) {
|
||||
this.firefoxUserPrefs = firefoxUserPrefs;
|
||||
@ -934,8 +954,8 @@ public interface BrowserType {
|
||||
/**
|
||||
* Whether to run browser in headless mode. More details for <a
|
||||
* href="https://developers.google.com/web/updates/2017/04/headless-chrome">Chromium</a> and <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode">Firefox</a>. Defaults to {@code true}
|
||||
* unless the {@code devtools} option is {@code true}.
|
||||
* href="https://hacks.mozilla.org/2017/12/using-headless-mode-in-firefox/">Firefox</a>. Defaults to {@code true} unless
|
||||
* the {@code devtools} option is {@code true}.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setHeadless(boolean headless) {
|
||||
this.headless = headless;
|
||||
@ -1365,11 +1385,20 @@ public interface BrowserType {
|
||||
* <p> Launches browser that uses persistent storage located at {@code userDataDir} and returns the only context. Closing this
|
||||
* context will automatically close the browser.
|
||||
*
|
||||
* @param userDataDir Path to a User Data Directory, which stores browser session data like cookies and local storage. More details for <a
|
||||
* @param userDataDir Path to a User Data Directory, which stores browser session data like cookies and local storage. Pass an empty string to
|
||||
* create a temporary directory.
|
||||
*
|
||||
* <p> More details for <a
|
||||
* href="https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md#introduction">Chromium</a> and <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile">Firefox</a>. Note that
|
||||
* Chromium's user data directory is the **parent** directory of the "Profile Path" seen at {@code chrome://version}. Pass
|
||||
* an empty string to use a temporary directory instead.
|
||||
* href="https://wiki.mozilla.org/Firefox/CommandLineOptions#User_profile">Firefox</a>. Chromium's user data directory is
|
||||
* the **parent** directory of the "Profile Path" seen at {@code chrome://version}.
|
||||
*
|
||||
* <p> Note that browsers do not allow launching multiple instances with the same User Data Directory.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Chromium/Chrome: Due to recent Chrome policy changes, automating the default Chrome user profile is not supported.
|
||||
* Pointing {@code userDataDir} to Chrome's main "User Data" directory (the profile used for your regular browsing) may
|
||||
* result in pages not loading or the browser exiting. Create and use a separate directory (for example, an empty folder)
|
||||
* as your automation profile instead. See https://developer.chrome.com/blog/remote-debugging-port for details.
|
||||
* @since v1.8
|
||||
*/
|
||||
default BrowserContext launchPersistentContext(Path userDataDir) {
|
||||
@ -1381,11 +1410,20 @@ public interface BrowserType {
|
||||
* <p> Launches browser that uses persistent storage located at {@code userDataDir} and returns the only context. Closing this
|
||||
* context will automatically close the browser.
|
||||
*
|
||||
* @param userDataDir Path to a User Data Directory, which stores browser session data like cookies and local storage. More details for <a
|
||||
* @param userDataDir Path to a User Data Directory, which stores browser session data like cookies and local storage. Pass an empty string to
|
||||
* create a temporary directory.
|
||||
*
|
||||
* <p> More details for <a
|
||||
* href="https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md#introduction">Chromium</a> and <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile">Firefox</a>. Note that
|
||||
* Chromium's user data directory is the **parent** directory of the "Profile Path" seen at {@code chrome://version}. Pass
|
||||
* an empty string to use a temporary directory instead.
|
||||
* href="https://wiki.mozilla.org/Firefox/CommandLineOptions#User_profile">Firefox</a>. Chromium's user data directory is
|
||||
* the **parent** directory of the "Profile Path" seen at {@code chrome://version}.
|
||||
*
|
||||
* <p> Note that browsers do not allow launching multiple instances with the same User Data Directory.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Chromium/Chrome: Due to recent Chrome policy changes, automating the default Chrome user profile is not supported.
|
||||
* Pointing {@code userDataDir} to Chrome's main "User Data" directory (the profile used for your regular browsing) may
|
||||
* result in pages not loading or the browser exiting. Create and use a separate directory (for example, an empty folder)
|
||||
* as your automation profile instead. See https://developer.chrome.com/blog/remote-debugging-port for details.
|
||||
* @since v1.8
|
||||
*/
|
||||
BrowserContext launchPersistentContext(Path userDataDir, LaunchPersistentContextOptions options);
|
||||
|
@ -600,18 +600,14 @@ public interface Locator {
|
||||
}
|
||||
class EvaluateOptions {
|
||||
/**
|
||||
* Maximum time in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
|
||||
* value can be changed by using the {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout
|
||||
* BrowserContext.setDefaultTimeout()} or {@link com.microsoft.playwright.Page#setDefaultTimeout Page.setDefaultTimeout()}
|
||||
* methods.
|
||||
* Maximum time in milliseconds to wait for the locator before evaluating. Note that after locator is resolved, evaluation
|
||||
* itself is not limited by the timeout. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout.
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Maximum time in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
|
||||
* value can be changed by using the {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout
|
||||
* BrowserContext.setDefaultTimeout()} or {@link com.microsoft.playwright.Page#setDefaultTimeout Page.setDefaultTimeout()}
|
||||
* methods.
|
||||
* Maximum time in milliseconds to wait for the locator before evaluating. Note that after locator is resolved, evaluation
|
||||
* itself is not limited by the timeout. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout.
|
||||
*/
|
||||
public EvaluateOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
@ -620,18 +616,14 @@ public interface Locator {
|
||||
}
|
||||
class EvaluateHandleOptions {
|
||||
/**
|
||||
* Maximum time in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
|
||||
* value can be changed by using the {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout
|
||||
* BrowserContext.setDefaultTimeout()} or {@link com.microsoft.playwright.Page#setDefaultTimeout Page.setDefaultTimeout()}
|
||||
* methods.
|
||||
* Maximum time in milliseconds to wait for the locator before evaluating. Note that after locator is resolved, evaluation
|
||||
* itself is not limited by the timeout. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout.
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Maximum time in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
|
||||
* value can be changed by using the {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout
|
||||
* BrowserContext.setDefaultTimeout()} or {@link com.microsoft.playwright.Page#setDefaultTimeout Page.setDefaultTimeout()}
|
||||
* methods.
|
||||
* Maximum time in milliseconds to wait for the locator before evaluating. Note that after locator is resolved, evaluation
|
||||
* itself is not limited by the timeout. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout.
|
||||
*/
|
||||
public EvaluateHandleOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
@ -2610,6 +2602,20 @@ public interface Locator {
|
||||
* @since v1.14
|
||||
*/
|
||||
void dblclick(DblclickOptions options);
|
||||
/**
|
||||
* Describes the locator, description is used in the trace viewer and reports. Returns the locator pointing to the same
|
||||
* element.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* Locator button = page.getByTestId("btn-sub").describe("Subscribe button");
|
||||
* button.click();
|
||||
* }</pre>
|
||||
*
|
||||
* @param description Locator description.
|
||||
* @since v1.53
|
||||
*/
|
||||
Locator describe(String description);
|
||||
/**
|
||||
* Programmatically dispatch an event on the matching element.
|
||||
*
|
||||
@ -2856,6 +2862,14 @@ public interface Locator {
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
*
|
||||
* <p> Passing argument to {@code expression}:
|
||||
* <pre>{@code
|
||||
* Object result = page.getByTestId("myId").evaluate("(element, [x, y]) => {\n" +
|
||||
* " return element.textContent + ' ' + x * y;\n" +
|
||||
* "}", Arrays.asList(7, 8));
|
||||
* System.out.println(result); // prints "myId text 56"
|
||||
* }</pre>
|
||||
*
|
||||
* @param expression JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the function is
|
||||
* automatically invoked.
|
||||
* @param arg Optional argument to pass to {@code expression}.
|
||||
@ -2880,6 +2894,14 @@ public interface Locator {
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
*
|
||||
* <p> Passing argument to {@code expression}:
|
||||
* <pre>{@code
|
||||
* Object result = page.getByTestId("myId").evaluate("(element, [x, y]) => {\n" +
|
||||
* " return element.textContent + ' ' + x * y;\n" +
|
||||
* "}", Arrays.asList(7, 8));
|
||||
* System.out.println(result); // prints "myId text 56"
|
||||
* }</pre>
|
||||
*
|
||||
* @param expression JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the function is
|
||||
* automatically invoked.
|
||||
* @since v1.14
|
||||
@ -2903,6 +2925,14 @@ public interface Locator {
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
*
|
||||
* <p> Passing argument to {@code expression}:
|
||||
* <pre>{@code
|
||||
* Object result = page.getByTestId("myId").evaluate("(element, [x, y]) => {\n" +
|
||||
* " return element.textContent + ' ' + x * y;\n" +
|
||||
* "}", Arrays.asList(7, 8));
|
||||
* System.out.println(result); // prints "myId text 56"
|
||||
* }</pre>
|
||||
*
|
||||
* @param expression JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the function is
|
||||
* automatically invoked.
|
||||
* @param arg Optional argument to pass to {@code expression}.
|
||||
|
@ -21,6 +21,11 @@ import com.microsoft.playwright.options.*;
|
||||
/**
|
||||
* The Mouse class operates in main-frame CSS pixels relative to the top-left corner of the viewport.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> If you want to debug where the mouse moved, you can use the <a
|
||||
* href="https://playwright.dev/java/docs/trace-viewer-intro">Trace viewer</a> or <a
|
||||
* href="https://playwright.dev/java/docs/running-tests">Playwright Inspector</a>. A red dot showing the location of the
|
||||
* mouse will be shown for every mouse action.
|
||||
*
|
||||
* <p> Every {@code page} object has its own Mouse, accessible with {@link com.microsoft.playwright.Page#mouse Page.mouse()}.
|
||||
* <pre>{@code
|
||||
* // Using ‘page.mouse’ to trace a 100x100 square.
|
||||
|
@ -5812,8 +5812,8 @@ public interface Page extends AutoCloseable {
|
||||
*/
|
||||
Page opener();
|
||||
/**
|
||||
* Pauses script execution. Playwright will stop executing the script and wait for the user to either press 'Resume' button
|
||||
* in the page overlay or to call {@code playwright.resume()} in the DevTools console.
|
||||
* Pauses script execution. Playwright will stop executing the script and wait for the user to either press the 'Resume'
|
||||
* button in the page overlay or to call {@code playwright.resume()} in the DevTools console.
|
||||
*
|
||||
* <p> User can inspect selectors or perform manual steps while paused. Resume will continue running the original script from
|
||||
* the place it was paused.
|
||||
@ -6317,8 +6317,8 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
@ -6376,8 +6376,8 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
@ -6433,8 +6433,8 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
@ -6492,8 +6492,8 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
@ -6549,8 +6549,8 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
@ -6608,8 +6608,8 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
|
@ -370,6 +370,10 @@ public interface Route {
|
||||
* matching handlers won't be invoked. Use {@link com.microsoft.playwright.Route#fallback Route.fallback()} If you want
|
||||
* next matching handler in the chain to be invoked.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> The {@code Cookie} header cannot be overridden using this method. If a value is provided, it will be ignored, and the
|
||||
* cookie will be loaded from the browser's cookie store. To set custom cookies, use {@link
|
||||
* com.microsoft.playwright.BrowserContext#addCookies BrowserContext.addCookies()}.
|
||||
*
|
||||
* @since v1.8
|
||||
*/
|
||||
default void resume() {
|
||||
@ -398,6 +402,10 @@ public interface Route {
|
||||
* matching handlers won't be invoked. Use {@link com.microsoft.playwright.Route#fallback Route.fallback()} If you want
|
||||
* next matching handler in the chain to be invoked.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> The {@code Cookie} header cannot be overridden using this method. If a value is provided, it will be ignored, and the
|
||||
* cookie will be loaded from the browser's cookie store. To set custom cookies, use {@link
|
||||
* com.microsoft.playwright.BrowserContext#addCookies BrowserContext.addCookies()}.
|
||||
*
|
||||
* @since v1.8
|
||||
*/
|
||||
void resume(ResumeOptions options);
|
||||
|
@ -23,6 +23,12 @@ import java.nio.file.Path;
|
||||
* API for collecting and saving Playwright traces. Playwright traces can be opened in <a
|
||||
* href="https://playwright.dev/java/docs/trace-viewer">Trace Viewer</a> after Playwright script runs.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> You probably want to <a href="https://playwright.dev/docs/api/class-testoptions#test-options-trace">enable tracing in
|
||||
* your config file</a> instead of using {@code context.tracing}.The {@code context.tracing} API captures browser operations and network activity, but it doesn't record test assertions
|
||||
* (like {@code expect} calls). We recommend <a
|
||||
* href="https://playwright.dev/docs/api/class-testoptions#test-options-trace">enabling tracing through Playwright Test
|
||||
* configuration</a>, which includes those assertions and provides a more complete trace for debugging test failures.
|
||||
*
|
||||
* <p> Start recording a trace before performing actions. At the end, stop tracing and save it to a file.
|
||||
* <pre>{@code
|
||||
* Browser browser = chromium.launch();
|
||||
@ -200,6 +206,12 @@ public interface Tracing {
|
||||
/**
|
||||
* Start tracing.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> You probably want to <a href="https://playwright.dev/docs/api/class-testoptions#test-options-trace">enable tracing in
|
||||
* your config file</a> instead of using {@code Tracing.start}.The {@code context.tracing} API captures browser operations and network activity, but it doesn't record test assertions
|
||||
* (like {@code expect} calls). We recommend <a
|
||||
* href="https://playwright.dev/docs/api/class-testoptions#test-options-trace">enabling tracing through Playwright Test
|
||||
* configuration</a>, which includes those assertions and provides a more complete trace for debugging test failures.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* context.tracing().start(new Tracing.StartOptions()
|
||||
@ -219,6 +231,12 @@ public interface Tracing {
|
||||
/**
|
||||
* Start tracing.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> You probably want to <a href="https://playwright.dev/docs/api/class-testoptions#test-options-trace">enable tracing in
|
||||
* your config file</a> instead of using {@code Tracing.start}.The {@code context.tracing} API captures browser operations and network activity, but it doesn't record test assertions
|
||||
* (like {@code expect} calls). We recommend <a
|
||||
* href="https://playwright.dev/docs/api/class-testoptions#test-options-trace">enabling tracing through Playwright Test
|
||||
* configuration</a>, which includes those assertions and provides a more complete trace for debugging test failures.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* context.tracing().start(new Tracing.StartOptions()
|
||||
|
@ -27,7 +27,7 @@ import java.util.function.Consumer;
|
||||
*
|
||||
* <p> <strong>Mocking</strong>
|
||||
*
|
||||
* <p> By default, the routed WebSocket will not connect to the server. This way, you can mock entire communcation over the
|
||||
* <p> By default, the routed WebSocket will not connect to the server. This way, you can mock entire communication over the
|
||||
* WebSocket. Here is an example that responds to a {@code "request"} with a {@code "response"}.
|
||||
* <pre>{@code
|
||||
* page.routeWebSocket("wss://example.com/ws", ws -> {
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package com.microsoft.playwright.assertions;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
import com.microsoft.playwright.options.AriaRole;
|
||||
|
||||
@ -237,6 +238,20 @@ public interface LocatorAssertions {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class ContainsClassOptions {
|
||||
/**
|
||||
* Time to retry the assertion for in milliseconds. Defaults to {@code 5000}.
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Time to retry the assertion for in milliseconds. Defaults to {@code 5000}.
|
||||
*/
|
||||
public ContainsClassOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class ContainsTextOptions {
|
||||
/**
|
||||
* Whether to perform case-insensitive match. {@code ignoreCase} option takes precedence over the corresponding regular
|
||||
@ -855,6 +870,98 @@ public interface LocatorAssertions {
|
||||
* @since v1.20
|
||||
*/
|
||||
void isVisible(IsVisibleOptions options);
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. All classes from the asserted value, separated
|
||||
* by spaces, must be present in the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/Element/classList">Element.classList</a> in any order.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).containsClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).containsClass("selected");
|
||||
* assertThat(page.locator("#component")).containsClass("row middle");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class lists. Each element's class attribute is matched against the corresponding class in the array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).containsClass(new String[] {"inactive", "active", "inactive"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected A string containing expected class names, separated by spaces, or a list of such strings to assert multiple elements.
|
||||
* @since v1.52
|
||||
*/
|
||||
default void containsClass(String expected) {
|
||||
containsClass(expected, null);
|
||||
}
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. All classes from the asserted value, separated
|
||||
* by spaces, must be present in the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/Element/classList">Element.classList</a> in any order.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).containsClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).containsClass("selected");
|
||||
* assertThat(page.locator("#component")).containsClass("row middle");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class lists. Each element's class attribute is matched against the corresponding class in the array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).containsClass(new String[] {"inactive", "active", "inactive"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected A string containing expected class names, separated by spaces, or a list of such strings to assert multiple elements.
|
||||
* @since v1.52
|
||||
*/
|
||||
void containsClass(String expected, ContainsClassOptions options);
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. All classes from the asserted value, separated
|
||||
* by spaces, must be present in the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/Element/classList">Element.classList</a> in any order.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).containsClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).containsClass("selected");
|
||||
* assertThat(page.locator("#component")).containsClass("row middle");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class lists. Each element's class attribute is matched against the corresponding class in the array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).containsClass(new String[] {"inactive", "active", "inactive"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected A string containing expected class names, separated by spaces, or a list of such strings to assert multiple elements.
|
||||
* @since v1.52
|
||||
*/
|
||||
default void containsClass(List<String> expected) {
|
||||
containsClass(expected, null);
|
||||
}
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. All classes from the asserted value, separated
|
||||
* by spaces, must be present in the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/Element/classList">Element.classList</a> in any order.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).containsClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).containsClass("selected");
|
||||
* assertThat(page.locator("#component")).containsClass("row middle");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class lists. Each element's class attribute is matched against the corresponding class in the array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).containsClass(new String[] {"inactive", "active", "inactive"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected A string containing expected class names, separated by spaces, or a list of such strings to assert multiple elements.
|
||||
* @since v1.52
|
||||
*/
|
||||
void containsClass(List<String> expected, ContainsClassOptions options);
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element that contains the given text. All nested elements will be considered
|
||||
* when computing the text content of the element. You can use regular expressions for the value as well.
|
||||
@ -1445,19 +1552,20 @@ public interface LocatorAssertions {
|
||||
void hasAttribute(String name, Pattern value, HasAttributeOptions options);
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
|
||||
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
|
||||
* the element's {@code class} attribute. To match individual classes use {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#containsClass LocatorAssertions.containsClass()}.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@ -1468,19 +1576,20 @@ public interface LocatorAssertions {
|
||||
}
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
|
||||
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
|
||||
* the element's {@code class} attribute. To match individual classes use {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#containsClass LocatorAssertions.containsClass()}.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@ -1489,19 +1598,20 @@ public interface LocatorAssertions {
|
||||
void hasClass(String expected, HasClassOptions options);
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
|
||||
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
|
||||
* the element's {@code class} attribute. To match individual classes use {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#containsClass LocatorAssertions.containsClass()}.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@ -1512,19 +1622,20 @@ public interface LocatorAssertions {
|
||||
}
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
|
||||
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
|
||||
* the element's {@code class} attribute. To match individual classes use {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#containsClass LocatorAssertions.containsClass()}.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@ -1533,19 +1644,20 @@ public interface LocatorAssertions {
|
||||
void hasClass(Pattern expected, HasClassOptions options);
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
|
||||
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
|
||||
* the element's {@code class} attribute. To match individual classes use {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#containsClass LocatorAssertions.containsClass()}.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@ -1556,19 +1668,20 @@ public interface LocatorAssertions {
|
||||
}
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
|
||||
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
|
||||
* the element's {@code class} attribute. To match individual classes use {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#containsClass LocatorAssertions.containsClass()}.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@ -1577,19 +1690,20 @@ public interface LocatorAssertions {
|
||||
void hasClass(String[] expected, HasClassOptions options);
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
|
||||
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
|
||||
* the element's {@code class} attribute. To match individual classes use {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#containsClass LocatorAssertions.containsClass()}.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@ -1600,19 +1714,20 @@ public interface LocatorAssertions {
|
||||
}
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
|
||||
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
|
||||
* the element's {@code class} attribute. To match individual classes use {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#containsClass LocatorAssertions.containsClass()}.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
|
@ -39,6 +39,8 @@ class APIRequestContextImpl extends ChannelOwner implements APIRequestContext {
|
||||
private final TracingImpl tracing;
|
||||
private String disposeReason;
|
||||
|
||||
protected TimeoutSettings timeoutSettings = new TimeoutSettings();
|
||||
|
||||
APIRequestContextImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
this.tracing = connection.getExistingObject(initializer.getAsJsonObject("tracing").get("guid").getAsString());
|
||||
@ -51,21 +53,17 @@ class APIRequestContextImpl extends ChannelOwner implements APIRequestContext {
|
||||
|
||||
@Override
|
||||
public void dispose(DisposeOptions options) {
|
||||
withLogging("APIRequestContext.dispose", () -> disposeImpl(options));
|
||||
}
|
||||
|
||||
private void disposeImpl(DisposeOptions options) {
|
||||
if (options == null) {
|
||||
options = new DisposeOptions();
|
||||
}
|
||||
disposeReason = options.reason;
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("dispose", params);
|
||||
sendMessage("dispose", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public APIResponse fetch(String urlOrRequest, RequestOptions options) {
|
||||
return withLogging("APIRequestContext.fetch", () -> fetchImpl(urlOrRequest, (RequestOptionsImpl) options));
|
||||
return fetchImpl(urlOrRequest, (RequestOptionsImpl) options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -93,6 +91,7 @@ class APIRequestContextImpl extends ChannelOwner implements APIRequestContext {
|
||||
if (options == null) {
|
||||
options = new RequestOptionsImpl();
|
||||
}
|
||||
options.timeout = timeoutSettings.timeout(options.timeout);
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("url", url);
|
||||
if (options.params != null) {
|
||||
@ -132,9 +131,6 @@ class APIRequestContextImpl extends ChannelOwner implements APIRequestContext {
|
||||
if (options.multipart != null) {
|
||||
params.add("multipartData", serializeMultipartData(options.multipart.fields));
|
||||
}
|
||||
if (options.timeout != null) {
|
||||
params.addProperty("timeout", options.timeout);
|
||||
}
|
||||
if (options.failOnStatusCode != null) {
|
||||
params.addProperty("failOnStatusCode", options.failOnStatusCode);
|
||||
}
|
||||
@ -153,7 +149,7 @@ class APIRequestContextImpl extends ChannelOwner implements APIRequestContext {
|
||||
}
|
||||
params.addProperty("maxRetries", options.maxRetries);
|
||||
}
|
||||
JsonObject json = sendMessage("fetch", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("fetch", params, timeoutSettings.timeout(options.timeout)).getAsJsonObject();
|
||||
return new APIResponseImpl(this, json.getAsJsonObject("response"));
|
||||
}
|
||||
|
||||
@ -219,14 +215,12 @@ class APIRequestContextImpl extends ChannelOwner implements APIRequestContext {
|
||||
|
||||
@Override
|
||||
public String storageState(StorageStateOptions options) {
|
||||
return withLogging("APIRequestContext.storageState", () -> {
|
||||
JsonElement json = sendMessage("storageState");
|
||||
String storageState = json.toString();
|
||||
if (options != null && options.path != null) {
|
||||
Utils.writeToFile(storageState.getBytes(StandardCharsets.UTF_8), options.path);
|
||||
}
|
||||
return storageState;
|
||||
});
|
||||
JsonElement json = sendMessage("storageState");
|
||||
String storageState = json.toString();
|
||||
if (options != null && options.path != null) {
|
||||
Utils.writeToFile(storageState.getBytes(StandardCharsets.UTF_8), options.path);
|
||||
}
|
||||
return storageState;
|
||||
}
|
||||
|
||||
private static RequestOptionsImpl ensureOptions(RequestOptions options, String method) {
|
||||
|
@ -17,7 +17,6 @@
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.APIRequest;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
@ -26,12 +25,11 @@ import com.microsoft.playwright.options.ClientCertificate;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
|
||||
import static com.microsoft.playwright.impl.ChannelOwner.NO_TIMEOUT;
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
import static com.microsoft.playwright.impl.Utils.addToProtocol;
|
||||
import static java.nio.file.Files.readAllBytes;
|
||||
|
||||
class APIRequestImpl implements APIRequest {
|
||||
private final PlaywrightImpl playwright;
|
||||
@ -42,10 +40,6 @@ class APIRequestImpl implements APIRequest {
|
||||
|
||||
@Override
|
||||
public APIRequestContextImpl newContext(NewContextOptions options) {
|
||||
return playwright.withLogging("APIRequest.newContext", () -> newContextImpl(options));
|
||||
}
|
||||
|
||||
private APIRequestContextImpl newContextImpl(NewContextOptions options) {
|
||||
if (options == null) {
|
||||
options = new NewContextOptions();
|
||||
} else {
|
||||
@ -67,13 +61,17 @@ class APIRequestImpl implements APIRequest {
|
||||
}
|
||||
List<ClientCertificate> clientCertificateList = options.clientCertificates;
|
||||
options.clientCertificates = null;
|
||||
Double timeout = options.timeout;
|
||||
// Timeout is handled on the client.
|
||||
options.timeout = null;
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
if (storageState != null) {
|
||||
params.add("storageState", storageState);
|
||||
}
|
||||
addToProtocol(params, clientCertificateList);
|
||||
JsonObject result = playwright.sendMessage("newRequest", params).getAsJsonObject();
|
||||
JsonObject result = playwright.sendMessage("newRequest", params, NO_TIMEOUT).getAsJsonObject();
|
||||
APIRequestContextImpl context = playwright.connection.getExistingObject(result.getAsJsonObject("request").get("guid").getAsString());
|
||||
context.timeoutSettings.setDefaultTimeout(timeout);
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.microsoft.playwright.APIResponse;
|
||||
@ -29,6 +28,7 @@ import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.microsoft.playwright.impl.ChannelOwner.NO_TIMEOUT;
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
import static com.microsoft.playwright.impl.Utils.isSafeCloseError;
|
||||
import static java.util.Arrays.asList;
|
||||
@ -49,7 +49,7 @@ class APIResponseImpl implements APIResponse {
|
||||
try {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("fetchUid", fetchUid());
|
||||
JsonObject json = context.sendMessage("fetchResponseBody", params).getAsJsonObject();
|
||||
JsonObject json = context.sendMessage("fetchResponseBody", params, NO_TIMEOUT).getAsJsonObject();
|
||||
if (!json.has("binary")) {
|
||||
throw new PlaywrightException("Response has been disposed");
|
||||
}
|
||||
@ -64,11 +64,9 @@ class APIResponseImpl implements APIResponse {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
context.withLogging("APIResponse.dispose", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("fetchUid", fetchUid());
|
||||
context.sendMessage("disposeAPIResponse", params);
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("fetchUid", fetchUid());
|
||||
context.sendMessage("disposeAPIResponse", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -114,7 +112,7 @@ class APIResponseImpl implements APIResponse {
|
||||
List<String> fetchLog() {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("fetchUid", fetchUid());
|
||||
JsonObject json = context.sendMessage("fetchLog", params).getAsJsonObject();
|
||||
JsonObject json = context.sendMessage("fetchLog", params, NO_TIMEOUT).getAsJsonObject();
|
||||
JsonArray log = json.get("log").getAsJsonArray();
|
||||
return gson().fromJson(log, new TypeToken<List<String>>() {}.getType());
|
||||
}
|
||||
|
@ -86,6 +86,6 @@ class ArtifactImpl extends ChannelOwner {
|
||||
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("path", path.toString());
|
||||
sendMessage("saveAs", params);
|
||||
sendMessage("saveAs", params, NO_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import org.opentest4j.AssertionFailedError;
|
||||
import org.opentest4j.ValueWrapper;
|
||||
|
||||
@ -29,28 +28,26 @@ import java.util.stream.Collectors;
|
||||
import static com.microsoft.playwright.impl.Utils.toJsRegexFlags;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
class AssertionsBase {
|
||||
final LocatorImpl actualLocator;
|
||||
abstract class AssertionsBase {
|
||||
final boolean isNot;
|
||||
|
||||
AssertionsBase(LocatorImpl actual, boolean isNot) {
|
||||
this.actualLocator = actual;
|
||||
AssertionsBase(boolean isNot) {
|
||||
this.isNot = isNot;
|
||||
}
|
||||
|
||||
void expectImpl(String expression, ExpectedTextValue textValue, Object expected, String message, FrameExpectOptions options) {
|
||||
expectImpl(expression, asList(textValue), expected, message, options);
|
||||
void expectImpl(String expression, ExpectedTextValue textValue, Object expected, String message, FrameExpectOptions options, String title) {
|
||||
expectImpl(expression, asList(textValue), expected, message, options, title);
|
||||
}
|
||||
|
||||
void expectImpl(String expression, List<ExpectedTextValue> expectedText, Object expected, String message, FrameExpectOptions options) {
|
||||
void expectImpl(String expression, List<ExpectedTextValue> expectedText, Object expected, String message, FrameExpectOptions options, String title) {
|
||||
if (options == null) {
|
||||
options = new FrameExpectOptions();
|
||||
}
|
||||
options.expectedText = expectedText;
|
||||
expectImpl(expression, options, expected, message);
|
||||
expectImpl(expression, options, expected, message, title);
|
||||
}
|
||||
|
||||
void expectImpl(String expression, FrameExpectOptions expectOptions, Object expected, String message) {
|
||||
void expectImpl(String expression, FrameExpectOptions expectOptions, Object expected, String message, String title) {
|
||||
if (expectOptions.timeout == null) {
|
||||
expectOptions.timeout = AssertionsTimeout.defaultTimeout;
|
||||
}
|
||||
@ -58,7 +55,7 @@ class AssertionsBase {
|
||||
if (isNot) {
|
||||
message = message.replace("expected to", "expected not to");
|
||||
}
|
||||
FrameExpectResult result = actualLocator.expect(expression, expectOptions);
|
||||
FrameExpectResult result = doExpect(expression, expectOptions, title);
|
||||
if (result.matches == isNot) {
|
||||
Object actual = result.received == null ? null : Serialization.deserialize(result.received);
|
||||
String log = (result.log == null) ? "" : String.join("\n", result.log);
|
||||
@ -75,7 +72,9 @@ class AssertionsBase {
|
||||
}
|
||||
}
|
||||
|
||||
private static ValueWrapper formatValue(Object value) {
|
||||
abstract FrameExpectResult doExpect(String expression, FrameExpectOptions expectOptions, String title);
|
||||
|
||||
protected static ValueWrapper formatValue(Object value) {
|
||||
if (value == null || !value.getClass().isArray()) {
|
||||
return ValueWrapper.create(value);
|
||||
}
|
||||
|
@ -78,11 +78,11 @@ class BindingCall extends ChannelOwner {
|
||||
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("result", gson().toJsonTree(serializeArgument(result)));
|
||||
sendMessage("resolve", params);
|
||||
sendMessage("resolve", params, NO_TIMEOUT);
|
||||
} catch (RuntimeException exception) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("error", gson().toJsonTree(serializeError(exception)));
|
||||
sendMessage("reject", params);
|
||||
sendMessage("reject", params, NO_TIMEOUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ import static java.nio.file.Files.readAllBytes;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
private final BrowserImpl browser;
|
||||
protected BrowserImpl browser;
|
||||
private final TracingImpl tracing;
|
||||
private final APIRequestContextImpl request;
|
||||
private final ClockImpl clock;
|
||||
@ -51,7 +51,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
|
||||
final Router routes = new Router();
|
||||
final WebSocketRouter webSocketRoutes = new WebSocketRouter();
|
||||
private boolean closeWasCalled;
|
||||
private boolean closingOrClosed;
|
||||
private final WaitableEvent<EventType, ?> closePromise;
|
||||
final Map<String, BindingCallback> bindings = new HashMap<>();
|
||||
PageImpl ownerPage;
|
||||
@ -69,8 +69,6 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
}
|
||||
private final ListenerCollection<EventType> listeners = new ListenerCollection<>(eventSubscriptions(), this);
|
||||
final TimeoutSettings timeoutSettings = new TimeoutSettings();
|
||||
Path videosDir;
|
||||
URL baseUrl;
|
||||
final Map<String, HarRecorder> harRecorders = new HashMap<>();
|
||||
|
||||
static class HarRecorder {
|
||||
@ -98,29 +96,30 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
|
||||
BrowserContextImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
if (parent instanceof BrowserImpl) {
|
||||
browser = (BrowserImpl) parent;
|
||||
} else {
|
||||
browser = null;
|
||||
}
|
||||
tracing = connection.getExistingObject(initializer.getAsJsonObject("tracing").get("guid").getAsString());
|
||||
request = connection.getExistingObject(initializer.getAsJsonObject("requestContext").get("guid").getAsString());
|
||||
request.timeoutSettings = timeoutSettings;
|
||||
clock = new ClockImpl(this);
|
||||
closePromise = new WaitableEvent<>(listeners, EventType.CLOSE);
|
||||
}
|
||||
|
||||
void setRecordHar(Path path, HarContentPolicy policy) {
|
||||
if (path != null) {
|
||||
harRecorders.put("", new HarRecorder(path, policy));
|
||||
Path videosDir() {
|
||||
JsonObject recordVideo = initializer.getAsJsonObject("options").getAsJsonObject("recordVideo");
|
||||
if (recordVideo == null) {
|
||||
return null;
|
||||
}
|
||||
return Paths.get(recordVideo.get("dir").getAsString());
|
||||
}
|
||||
|
||||
void setBaseUrl(String spec) {
|
||||
try {
|
||||
this.baseUrl = new URL(spec);
|
||||
} catch (MalformedURLException e) {
|
||||
this.baseUrl = null;
|
||||
URL baseUrl() {
|
||||
JsonElement url = initializer.getAsJsonObject("options").get("baseURL");
|
||||
if (url != null) {
|
||||
try {
|
||||
return new URL(url.getAsString());
|
||||
} catch (MalformedURLException e) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String effectiveCloseReason() {
|
||||
@ -262,7 +261,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
public CDPSession newCDPSession(Page page) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("page", ((PageImpl) page).toProtocolRef());
|
||||
JsonObject result = sendMessage("newCDPSession", params).getAsJsonObject();
|
||||
JsonObject result = sendMessage("newCDPSession", params, NO_TIMEOUT).getAsJsonObject();
|
||||
return connection.getExistingObject(result.getAsJsonObject("session").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@ -270,23 +269,14 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
public CDPSession newCDPSession(Frame frame) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("frame", ((FrameImpl) frame).toProtocolRef());
|
||||
JsonObject result = sendMessage("newCDPSession", params).getAsJsonObject();
|
||||
JsonObject result = sendMessage("newCDPSession", params, NO_TIMEOUT).getAsJsonObject();
|
||||
return connection.getExistingObject(result.getAsJsonObject("session").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(CloseOptions options) {
|
||||
withLogging("BrowserContext.close", () -> closeImpl(options));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Cookie> cookies(String url) {
|
||||
return cookies(url == null ? new ArrayList<>() : Collections.singletonList(url));
|
||||
}
|
||||
|
||||
private void closeImpl(CloseOptions options) {
|
||||
if (!closeWasCalled) {
|
||||
closeWasCalled = true;
|
||||
if (!closingOrClosed) {
|
||||
closingOrClosed = true;
|
||||
if (options == null) {
|
||||
options = new CloseOptions();
|
||||
}
|
||||
@ -295,7 +285,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
for (Map.Entry<String, HarRecorder> entry : harRecorders.entrySet()) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("harId", entry.getKey());
|
||||
JsonObject json = sendMessage("harExport", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("harExport", params, NO_TIMEOUT).getAsJsonObject();
|
||||
ArtifactImpl artifact = connection.getExistingObject(json.getAsJsonObject("artifact").get("guid").getAsString());
|
||||
// Server side will compress artifact if content is attach or if file is .zip.
|
||||
HarRecorder harParams = entry.getValue();
|
||||
@ -307,42 +297,46 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
JsonObject unzipParams = new JsonObject();
|
||||
unzipParams.addProperty("zipFile", tmpPath);
|
||||
unzipParams.addProperty("harFile", harParams.path.toString());
|
||||
connection.localUtils.sendMessage("harUnzip", unzipParams);
|
||||
connection.localUtils.sendMessage("harUnzip", unzipParams, NO_TIMEOUT);
|
||||
} else {
|
||||
artifact.saveAs(harParams.path);
|
||||
}
|
||||
artifact.delete();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("close", params);
|
||||
sendMessage("close", params, NO_TIMEOUT);
|
||||
}
|
||||
runUntil(() -> {}, closePromise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Cookie> cookies(String url) {
|
||||
return cookies(url == null ? new ArrayList<>() : Collections.singletonList(url));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addCookies(List<Cookie> cookies) {
|
||||
withLogging("BrowserContext.addCookies", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("cookies", gson().toJsonTree(cookies));
|
||||
sendMessage("addCookies", params);
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("cookies", gson().toJsonTree(cookies));
|
||||
sendMessage("addCookies", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInitScript(String script) {
|
||||
withLogging("BrowserContext.addInitScript", () -> addInitScriptImpl(script));
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("source", script);
|
||||
sendMessage("addInitScript", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInitScript(Path path) {
|
||||
withLogging("BrowserContext.addInitScript", () -> {
|
||||
try {
|
||||
byte[] bytes = readAllBytes(path);
|
||||
addInitScriptImpl(new String(bytes, UTF_8));
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to read script from file", e);
|
||||
}
|
||||
});
|
||||
try {
|
||||
byte[] bytes = readAllBytes(path);
|
||||
addInitScript(new String(bytes, UTF_8));
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to read script from file", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -350,12 +344,6 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
return new ArrayList<>(backgroundPages);
|
||||
}
|
||||
|
||||
private void addInitScriptImpl(String script) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("source", script);
|
||||
sendMessage("addInitScript", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BrowserImpl browser() {
|
||||
return browser;
|
||||
@ -363,10 +351,6 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
|
||||
@Override
|
||||
public void clearCookies(ClearCookiesOptions options) {
|
||||
withLogging("BrowserContext.clearCookies", () -> clearCookiesImpl(options));
|
||||
}
|
||||
|
||||
private void clearCookiesImpl(ClearCookiesOptions options) {
|
||||
if (options == null) {
|
||||
options = new ClearCookiesOptions();
|
||||
}
|
||||
@ -374,7 +358,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
setStringOrRegex(params, "name", options.name);
|
||||
setStringOrRegex(params, "domain", options.domain);
|
||||
setStringOrRegex(params, "path", options.path);
|
||||
sendMessage("clearCookies", params);
|
||||
sendMessage("clearCookies", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
private static void setStringOrRegex(JsonObject params, String name, Object value) {
|
||||
@ -389,28 +373,24 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
|
||||
@Override
|
||||
public void clearPermissions() {
|
||||
withLogging("BrowserContext.clearPermissions", () -> sendMessage("clearPermissions"));
|
||||
sendMessage("clearPermissions");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Cookie> cookies(List<String> urls) {
|
||||
return withLogging("BrowserContext.cookies", () -> cookiesImpl(urls));
|
||||
}
|
||||
|
||||
private List<Cookie> cookiesImpl(List<String> urls) {
|
||||
JsonObject params = new JsonObject();
|
||||
if (urls == null) {
|
||||
urls = new ArrayList<>();
|
||||
}
|
||||
params.add("urls", gson().toJsonTree(urls));
|
||||
JsonObject json = sendMessage("cookies", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("cookies", params, NO_TIMEOUT).getAsJsonObject();
|
||||
Cookie[] cookies = gson().fromJson(json.getAsJsonArray("cookies"), Cookie[].class);
|
||||
return asList(cookies);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exposeBinding(String name, BindingCallback playwrightBinding, ExposeBindingOptions options) {
|
||||
withLogging("BrowserContext.exposeBinding", () -> exposeBindingImpl(name, playwrightBinding, options));
|
||||
exposeBindingImpl(name, playwrightBinding, options);
|
||||
}
|
||||
|
||||
private void exposeBindingImpl(String name, BindingCallback playwrightBinding, ExposeBindingOptions options) {
|
||||
@ -429,21 +409,16 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
if (options != null && options.handle != null && options.handle) {
|
||||
params.addProperty("needsHandle", true);
|
||||
}
|
||||
sendMessage("exposeBinding", params);
|
||||
sendMessage("exposeBinding", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exposeFunction(String name, FunctionCallback playwrightFunction) {
|
||||
withLogging("BrowserContext.exposeFunction",
|
||||
() -> exposeBindingImpl(name, (BindingCallback.Source source, Object... args) -> playwrightFunction.call(args), null));
|
||||
exposeBindingImpl(name, (BindingCallback.Source source, Object... args) -> playwrightFunction.call(args), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantPermissions(List<String> permissions, GrantPermissionsOptions options) {
|
||||
withLogging("BrowserContext.grantPermissions", () -> grantPermissionsImpl(permissions, options));
|
||||
}
|
||||
|
||||
private void grantPermissionsImpl(List<String> permissions, GrantPermissionsOptions options) {
|
||||
if (options == null) {
|
||||
options = new GrantPermissionsOptions();
|
||||
}
|
||||
@ -452,15 +427,11 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.add("permissions", gson().toJsonTree(permissions));
|
||||
sendMessage("grantPermissions", params);
|
||||
sendMessage("grantPermissions", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageImpl newPage() {
|
||||
return withLogging("BrowserContext.newPage", () -> newPageImpl());
|
||||
}
|
||||
|
||||
private PageImpl newPageImpl() {
|
||||
if (ownerPage != null) {
|
||||
throw new PlaywrightException("Please use browser.newContext()");
|
||||
}
|
||||
@ -480,7 +451,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
|
||||
@Override
|
||||
public void route(String url, Consumer<Route> handler, RouteOptions options) {
|
||||
route(new UrlMatcher(baseUrl, url), handler, options);
|
||||
route(UrlMatcher.forGlob(baseUrl(), url, this.connection.localUtils, false), handler, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -499,25 +470,23 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
options = new RouteFromHAROptions();
|
||||
}
|
||||
if (options.update != null && options.update) {
|
||||
recordIntoHar(null, har, options);
|
||||
recordIntoHar(null, har, options, null);
|
||||
return;
|
||||
}
|
||||
UrlMatcher matcher = UrlMatcher.forOneOf(baseUrl, options.url);
|
||||
UrlMatcher matcher = UrlMatcher.forOneOf(baseUrl(), options.url, this.connection.localUtils, false);
|
||||
HARRouter harRouter = new HARRouter(connection.localUtils, har, options.notFound);
|
||||
onClose(context -> harRouter.dispose());
|
||||
route(matcher, route -> harRouter.handle(route), null);
|
||||
}
|
||||
|
||||
private void route(UrlMatcher matcher, Consumer<Route> handler, RouteOptions options) {
|
||||
withLogging("BrowserContext.route", () -> {
|
||||
routes.add(matcher, handler, options == null ? null : options.times);
|
||||
updateInterceptionPatterns();
|
||||
});
|
||||
routes.add(matcher, handler, options == null ? null : options.times);
|
||||
updateInterceptionPatterns();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void routeWebSocket(String url, Consumer<WebSocketRoute> handler) {
|
||||
routeWebSocketImpl(new UrlMatcher(baseUrl, url), handler);
|
||||
routeWebSocketImpl(UrlMatcher.forGlob(baseUrl(), url, this.connection.localUtils, true), handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -531,108 +500,82 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
}
|
||||
|
||||
private void routeWebSocketImpl(UrlMatcher matcher, Consumer<WebSocketRoute> handler) {
|
||||
withLogging("BrowserContext.routeWebSocket", () -> {
|
||||
webSocketRoutes.add(matcher, handler);
|
||||
updateWebSocketInterceptionPatterns();
|
||||
});
|
||||
webSocketRoutes.add(matcher, handler);
|
||||
updateWebSocketInterceptionPatterns();
|
||||
}
|
||||
|
||||
void recordIntoHar(PageImpl page, Path har, RouteFromHAROptions options) {
|
||||
void recordIntoHar(PageImpl page, Path har, RouteFromHAROptions options, HarContentPolicy contentPolicy) {
|
||||
if (contentPolicy == null) {
|
||||
contentPolicy = Utils.convertType(options.updateContent, HarContentPolicy.class);
|
||||
}
|
||||
if (contentPolicy == null) {
|
||||
contentPolicy = HarContentPolicy.ATTACH;
|
||||
}
|
||||
|
||||
JsonObject params = new JsonObject();
|
||||
if (page != null) {
|
||||
params.add("page", page.toProtocolRef());
|
||||
}
|
||||
JsonObject jsonOptions = new JsonObject();
|
||||
jsonOptions.addProperty("path", har.toAbsolutePath().toString());
|
||||
jsonOptions.addProperty("content", options.updateContent == null ?
|
||||
HarContentPolicy.ATTACH.name().toLowerCase() :
|
||||
options.updateContent.name().toLowerCase());
|
||||
jsonOptions.addProperty("mode", options.updateMode == null ?
|
||||
HarMode.MINIMAL.name().toLowerCase() :
|
||||
options.updateMode.name().toLowerCase());
|
||||
addHarUrlFilter(jsonOptions, options.url);
|
||||
params.add("options", jsonOptions);
|
||||
JsonObject json = sendMessage("harStart", params).getAsJsonObject();
|
||||
JsonObject recordHarArgs = new JsonObject();
|
||||
recordHarArgs.addProperty("zip", har.toString().endsWith(".zip"));
|
||||
recordHarArgs.addProperty("content", contentPolicy.name().toLowerCase());
|
||||
recordHarArgs.addProperty("mode", (options.updateMode == null ? HarMode.MINIMAL : options.updateMode).name().toLowerCase());
|
||||
addHarUrlFilter(recordHarArgs, options.url);
|
||||
|
||||
params.add("options", recordHarArgs);
|
||||
JsonObject json = sendMessage("harStart", params, NO_TIMEOUT).getAsJsonObject();
|
||||
String harId = json.get("harId").getAsString();
|
||||
harRecorders.put(harId, new HarRecorder(har, HarContentPolicy.ATTACH));
|
||||
harRecorders.put(harId, new HarRecorder(har, contentPolicy));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultNavigationTimeout(double timeout) {
|
||||
setDefaultNavigationTimeoutImpl(timeout);
|
||||
}
|
||||
|
||||
void setDefaultNavigationTimeoutImpl(Double timeout) {
|
||||
withLogging("BrowserContext.setDefaultNavigationTimeout", () -> {
|
||||
timeoutSettings.setDefaultNavigationTimeout(timeout);
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("timeout", timeout);
|
||||
sendMessage("setDefaultNavigationTimeoutNoReply", params);
|
||||
});
|
||||
timeoutSettings.setDefaultNavigationTimeout(timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultTimeout(double timeout) {
|
||||
setDefaultTimeoutImpl(timeout);
|
||||
}
|
||||
|
||||
void setDefaultTimeoutImpl(Double timeout) {
|
||||
withLogging("BrowserContext.setDefaultTimeout", () -> {
|
||||
timeoutSettings.setDefaultTimeout(timeout);
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("timeout", timeout);
|
||||
sendMessage("setDefaultTimeoutNoReply", params);
|
||||
});
|
||||
timeoutSettings.setDefaultTimeout(timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExtraHTTPHeaders(Map<String, String> headers) {
|
||||
withLogging("BrowserContext.setExtraHTTPHeaders", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
JsonArray jsonHeaders = new JsonArray();
|
||||
for (Map.Entry<String, String> e : headers.entrySet()) {
|
||||
JsonObject header = new JsonObject();
|
||||
header.addProperty("name", e.getKey());
|
||||
header.addProperty("value", e.getValue());
|
||||
jsonHeaders.add(header);
|
||||
}
|
||||
params.add("headers", jsonHeaders);
|
||||
sendMessage("setExtraHTTPHeaders", params);
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
JsonArray jsonHeaders = new JsonArray();
|
||||
for (Map.Entry<String, String> e : headers.entrySet()) {
|
||||
JsonObject header = new JsonObject();
|
||||
header.addProperty("name", e.getKey());
|
||||
header.addProperty("value", e.getValue());
|
||||
jsonHeaders.add(header);
|
||||
}
|
||||
params.add("headers", jsonHeaders);
|
||||
sendMessage("setExtraHTTPHeaders", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGeolocation(Geolocation geolocation) {
|
||||
withLogging("BrowserContext.setGeolocation", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
if (geolocation != null) {
|
||||
params.add("geolocation", gson().toJsonTree(geolocation));
|
||||
}
|
||||
sendMessage("setGeolocation", params);
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
if (geolocation != null) {
|
||||
params.add("geolocation", gson().toJsonTree(geolocation));
|
||||
}
|
||||
sendMessage("setGeolocation", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOffline(boolean offline) {
|
||||
withLogging("BrowserContext.setOffline", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("offline", offline);
|
||||
sendMessage("setOffline", params);
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("offline", offline);
|
||||
sendMessage("setOffline", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String storageState(StorageStateOptions options) {
|
||||
return withLogging("BrowserContext.storageState", () -> storageStateImpl(options));
|
||||
}
|
||||
|
||||
private String storageStateImpl(StorageStateOptions options) {
|
||||
if (options == null) {
|
||||
options = new StorageStateOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.remove("path");
|
||||
JsonElement json = sendMessage("storageState", params);
|
||||
JsonElement json = sendMessage("storageState", params, NO_TIMEOUT);
|
||||
|
||||
String storageState = json.toString();
|
||||
if (options.path != null) {
|
||||
@ -648,15 +591,13 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
|
||||
@Override
|
||||
public void unrouteAll() {
|
||||
withLogging("BrowserContext.unrouteAll", () -> {
|
||||
routes.removeAll();
|
||||
updateInterceptionPatterns();
|
||||
});
|
||||
routes.removeAll();
|
||||
updateInterceptionPatterns();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unroute(String url, Consumer<Route> handler) {
|
||||
unroute(new UrlMatcher(this.baseUrl, url), handler);
|
||||
unroute(UrlMatcher.forGlob(this.baseUrl(), url, this.connection.localUtils, false), handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -702,18 +643,16 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
}
|
||||
|
||||
private void unroute(UrlMatcher matcher, Consumer<Route> handler) {
|
||||
withLogging("BrowserContext.unroute", () -> {
|
||||
routes.remove(matcher, handler);
|
||||
updateInterceptionPatterns();
|
||||
});
|
||||
routes.remove(matcher, handler);
|
||||
updateInterceptionPatterns();
|
||||
}
|
||||
|
||||
private void updateInterceptionPatterns() {
|
||||
sendMessage("setNetworkInterceptionPatterns", routes.interceptionPatterns());
|
||||
sendMessage("setNetworkInterceptionPatterns", routes.interceptionPatterns(), NO_TIMEOUT);
|
||||
}
|
||||
|
||||
private void updateWebSocketInterceptionPatterns() {
|
||||
sendMessage("setWebSocketInterceptionPatterns", webSocketRoutes.interceptionPatterns());
|
||||
sendMessage("setWebSocketInterceptionPatterns", webSocketRoutes.interceptionPatterns(), NO_TIMEOUT);
|
||||
}
|
||||
|
||||
void handleRoute(RouteImpl route) {
|
||||
@ -866,8 +805,10 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
}
|
||||
|
||||
void didClose() {
|
||||
closingOrClosed = true;
|
||||
if (browser != null) {
|
||||
browser.contexts.remove(this);
|
||||
browser.browserType.playwright.selectors.contextsForSelectors.remove(this);
|
||||
}
|
||||
listeners.notify(EventType.CLOSE, this);
|
||||
}
|
||||
@ -876,7 +817,49 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("name", name);
|
||||
params.addProperty("lastModifiedMs", lastModifiedMs);
|
||||
JsonObject json = sendMessage("createTempFile", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("createTempFile", params, NO_TIMEOUT).getAsJsonObject();
|
||||
return connection.getExistingObject(json.getAsJsonObject("writableStream").get("guid").getAsString());
|
||||
}
|
||||
|
||||
protected void initializeHarFromOptions(Browser.NewContextOptions options) {
|
||||
if (options.recordHarPath == null) {
|
||||
if (options.recordHarOmitContent != null) {
|
||||
throw new PlaywrightException("recordHarOmitContent is set but recordHarPath is null");
|
||||
}
|
||||
if (options.recordHarUrlFilter != null) {
|
||||
throw new PlaywrightException("recordHarUrlFilter is set but recordHarPath is null");
|
||||
}
|
||||
if (options.recordHarMode != null) {
|
||||
throw new PlaywrightException("recordHarMode is set but recordHarPath is null");
|
||||
}
|
||||
if (options.recordHarContent != null) {
|
||||
throw new PlaywrightException("recordHarContent is set but recordHarPath is null");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
HarContentPolicy contentPolicy = options.recordHarContent;
|
||||
if (contentPolicy == null && options.recordHarOmitContent != null && options.recordHarOmitContent == true) {
|
||||
contentPolicy = HarContentPolicy.OMIT;
|
||||
}
|
||||
if (contentPolicy == null) {
|
||||
contentPolicy = options.recordHarPath.endsWith(".zip") ? HarContentPolicy.ATTACH : HarContentPolicy.EMBED;
|
||||
}
|
||||
RouteFromHAROptions routeFromHAROptions = new RouteFromHAROptions();
|
||||
|
||||
if (options.recordHarUrlFilter instanceof String) {
|
||||
routeFromHAROptions.setUrl((String) options.recordHarUrlFilter);
|
||||
} else if (options.recordHarUrlFilter instanceof Pattern) {
|
||||
routeFromHAROptions.setUrl((Pattern) options.recordHarUrlFilter);
|
||||
}
|
||||
|
||||
if (options.recordHarMode != null) {
|
||||
routeFromHAROptions.updateMode = options.recordHarMode;
|
||||
} else {
|
||||
routeFromHAROptions.updateMode = HarMode.FULL;
|
||||
}
|
||||
routeFromHAROptions.url = options.recordHarUrlFilter;
|
||||
|
||||
recordIntoHar(null, options.recordHarPath, routeFromHAROptions, contentPolicy);
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.*;
|
||||
import com.microsoft.playwright.options.HarContentPolicy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@ -29,10 +28,8 @@ import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.addHarUrlFilter;
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
import static com.microsoft.playwright.impl.Utils.*;
|
||||
import static com.microsoft.playwright.impl.Utils.convertType;
|
||||
|
||||
class BrowserImpl extends ChannelOwner implements Browser {
|
||||
final Set<BrowserContextImpl> contexts = new HashSet<>();
|
||||
@ -69,10 +66,6 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
|
||||
@Override
|
||||
public void close(CloseOptions options) {
|
||||
withLogging("Browser.close", () -> closeImpl(options));
|
||||
}
|
||||
|
||||
private void closeImpl(CloseOptions options) {
|
||||
if (options == null) {
|
||||
options = new CloseOptions();
|
||||
}
|
||||
@ -117,16 +110,20 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
|
||||
@Override
|
||||
public BrowserContextImpl newContext(NewContextOptions options) {
|
||||
return withLogging("Browser.newContext", () -> newContextImpl(options));
|
||||
}
|
||||
|
||||
private BrowserContextImpl newContextImpl(NewContextOptions options) {
|
||||
if (options == null) {
|
||||
options = new NewContextOptions();
|
||||
} else {
|
||||
// Make a copy so that we can nullify some fields below.
|
||||
options = convertType(options, NewContextOptions.class);
|
||||
}
|
||||
|
||||
NewContextOptions harOptions = Utils.clone(options);
|
||||
options.recordHarContent = null;
|
||||
options.recordHarMode = null;
|
||||
options.recordHarPath = null;
|
||||
options.recordHarOmitContent = null;
|
||||
options.recordHarUrlFilter = null;
|
||||
|
||||
if (options.storageStatePath != null) {
|
||||
try {
|
||||
byte[] bytes = Files.readAllBytes(options.storageStatePath);
|
||||
@ -141,51 +138,10 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
storageState = new Gson().fromJson(options.storageState, JsonObject.class);
|
||||
options.storageState = null;
|
||||
}
|
||||
JsonObject recordHar = null;
|
||||
Path recordHarPath = options.recordHarPath;
|
||||
HarContentPolicy harContentPolicy = null;
|
||||
if (options.recordHarPath != null) {
|
||||
recordHar = new JsonObject();
|
||||
recordHar.addProperty("path", options.recordHarPath.toString());
|
||||
if (options.recordHarContent != null) {
|
||||
harContentPolicy = options.recordHarContent;
|
||||
} else if (options.recordHarOmitContent != null && options.recordHarOmitContent) {
|
||||
harContentPolicy = HarContentPolicy.OMIT;
|
||||
}
|
||||
if (harContentPolicy != null) {
|
||||
recordHar.addProperty("content", harContentPolicy.name().toLowerCase());
|
||||
}
|
||||
if (options.recordHarMode != null) {
|
||||
recordHar.addProperty("mode", options.recordHarMode.name().toLowerCase());
|
||||
}
|
||||
addHarUrlFilter(recordHar, options.recordHarUrlFilter);
|
||||
options.recordHarPath = null;
|
||||
options.recordHarMode = null;
|
||||
options.recordHarOmitContent = null;
|
||||
options.recordHarContent = null;
|
||||
options.recordHarUrlFilter = null;
|
||||
} else {
|
||||
if (options.recordHarOmitContent != null) {
|
||||
throw new PlaywrightException("recordHarOmitContent is set but recordHarPath is null");
|
||||
}
|
||||
if (options.recordHarUrlFilter != null) {
|
||||
throw new PlaywrightException("recordHarUrlFilter is set but recordHarPath is null");
|
||||
}
|
||||
if (options.recordHarMode != null) {
|
||||
throw new PlaywrightException("recordHarMode is set but recordHarPath is null");
|
||||
}
|
||||
if (options.recordHarContent != null) {
|
||||
throw new PlaywrightException("recordHarContent is set but recordHarPath is null");
|
||||
}
|
||||
}
|
||||
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
if (storageState != null) {
|
||||
params.add("storageState", storageState);
|
||||
}
|
||||
if (recordHar != null) {
|
||||
params.add("recordHar", recordHar);
|
||||
}
|
||||
if (options.recordVideoDir != null) {
|
||||
JsonObject recordVideo = new JsonObject();
|
||||
recordVideo.addProperty("dir", options.recordVideoDir.toAbsolutePath().toString());
|
||||
@ -213,31 +169,21 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
if (options.acceptDownloads != null) {
|
||||
params.addProperty("acceptDownloads", options.acceptDownloads ? "accept" : "deny");
|
||||
}
|
||||
JsonElement result = sendMessage("newContext", params);
|
||||
params.add("selectorEngines", gson().toJsonTree(browserType.playwright.selectors.selectorEngines));
|
||||
params.addProperty("testIdAttributeName", browserType.playwright.selectors.testIdAttributeName);
|
||||
JsonElement result = sendMessage("newContext", params, NO_TIMEOUT);
|
||||
BrowserContextImpl context = connection.getExistingObject(result.getAsJsonObject().getAsJsonObject("context").get("guid").getAsString());
|
||||
context.videosDir = options.recordVideoDir;
|
||||
if (options.baseURL != null) {
|
||||
context.setBaseUrl(options.baseURL);
|
||||
}
|
||||
context.setRecordHar(recordHarPath, harContentPolicy);
|
||||
if (launchOptions != null) {
|
||||
context.tracing().setTracesDir(launchOptions.tracesDir);
|
||||
}
|
||||
contexts.add(context);
|
||||
context.initializeHarFromOptions(harOptions);
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page newPage(NewPageOptions options) {
|
||||
return withLogging("Browser.newPage", () -> newPageImpl(options));
|
||||
return withTitle("Create Page", () -> newPageImpl(options));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startTracing(Page page, StartTracingOptions options) {
|
||||
withLogging("Browser.startTracing", () -> startTracingImpl(page, options));
|
||||
}
|
||||
|
||||
private void startTracingImpl(Page page, StartTracingOptions options) {
|
||||
if (options == null) {
|
||||
options = new StartTracingOptions();
|
||||
}
|
||||
@ -246,15 +192,11 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
if (page != null) {
|
||||
params.add("page", ((PageImpl) page).toProtocolRef());
|
||||
}
|
||||
sendMessage("startTracing", params);
|
||||
sendMessage("startTracing", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] stopTracing() {
|
||||
return withLogging("Browser.stopTracing", () -> stopTracingImpl());
|
||||
}
|
||||
|
||||
private byte[] stopTracingImpl() {
|
||||
JsonObject json = sendMessage("stopTracing").getAsJsonObject();
|
||||
ArtifactImpl artifact = connection.getExistingObject(json.getAsJsonObject().getAsJsonObject("artifact").get("guid").getAsString());
|
||||
byte[] data = artifact.readAllBytes();
|
||||
@ -295,18 +237,46 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
|
||||
@Override
|
||||
void handleEvent(String event, JsonObject parameters) {
|
||||
if ("close".equals(event)) {
|
||||
didClose();
|
||||
switch (event) {
|
||||
case "context":
|
||||
didCreateContext(connection.getExistingObject(parameters.getAsJsonObject("context").get("guid").getAsString()));
|
||||
break;
|
||||
case "close":
|
||||
didClose();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CDPSession newBrowserCDPSession() {
|
||||
JsonObject params = new JsonObject();
|
||||
JsonObject result = sendMessage("newBrowserCDPSession", params).getAsJsonObject();
|
||||
JsonObject result = sendMessage("newBrowserCDPSession", params, NO_TIMEOUT).getAsJsonObject();
|
||||
return connection.getExistingObject(result.getAsJsonObject("session").get("guid").getAsString());
|
||||
}
|
||||
|
||||
protected void connectToBrowserType(BrowserTypeImpl browserType, Path tracesDir){
|
||||
// Note: when using connect(), `browserType` is different from `this.parent`.
|
||||
// This is why browser type is not wired up in the constructor, and instead this separate method is called later on.
|
||||
this.browserType = browserType;
|
||||
this.tracePath = tracesDir;
|
||||
|
||||
for (BrowserContextImpl context : contexts) {
|
||||
context.tracing().setTracesDir(tracesDir);
|
||||
browserType.playwright.selectors.contextsForSelectors.add(context);
|
||||
}
|
||||
}
|
||||
|
||||
private void didCreateContext(BrowserContextImpl context) {
|
||||
context.browser = this;
|
||||
contexts.add(context);
|
||||
// Note: when connecting to a browser, initial contexts arrive before `_browserType` is set,
|
||||
// and will be configured later in `ConnectToBrowserType`.
|
||||
if (browserType != null) {
|
||||
context.tracing().setTracesDir(tracePath);
|
||||
browserType.playwright.selectors.contextsForSelectors.add(context);
|
||||
}
|
||||
}
|
||||
|
||||
private void didClose() {
|
||||
isConnected = false;
|
||||
listeners.notify(EventType.DISCONNECTED, this);
|
||||
|
@ -22,34 +22,30 @@ import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Browser;
|
||||
import com.microsoft.playwright.BrowserType;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import com.microsoft.playwright.options.HarContentPolicy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.addHarUrlFilter;
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
import static com.microsoft.playwright.impl.Utils.addToProtocol;
|
||||
import static com.microsoft.playwright.impl.Utils.convertType;
|
||||
|
||||
class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
protected PlaywrightImpl playwright;
|
||||
|
||||
BrowserTypeImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BrowserImpl launch(LaunchOptions options) {
|
||||
return withLogging("BrowserType.launch", () -> launchImpl(options));
|
||||
}
|
||||
|
||||
private BrowserImpl launchImpl(LaunchOptions options) {
|
||||
if (options == null) {
|
||||
options = new LaunchOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
JsonElement result = sendMessage("launch", params);
|
||||
JsonElement result = sendMessage("launch", params, TimeoutSettings.launchTimeout(options.timeout));
|
||||
BrowserImpl browser = connection.getExistingObject(result.getAsJsonObject().getAsJsonObject("browser").get("guid").getAsString());
|
||||
browser.browserType = this;
|
||||
browser.launchOptions = options;
|
||||
@ -58,10 +54,6 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
|
||||
@Override
|
||||
public Browser connect(String wsEndpoint, ConnectOptions options) {
|
||||
return withLogging("BrowserType.connect", () -> connectImpl(wsEndpoint, options));
|
||||
}
|
||||
|
||||
private Browser connectImpl(String wsEndpoint, ConnectOptions options) {
|
||||
if (options == null) {
|
||||
options = new ConnectOptions();
|
||||
}
|
||||
@ -84,7 +76,12 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
headers.addProperty("x-playwright-browser", name());
|
||||
}
|
||||
|
||||
JsonObject json = connection.localUtils().sendMessage("connect", params).getAsJsonObject();
|
||||
Double timeout = options.timeout;
|
||||
if (timeout == null) {
|
||||
timeout = 0.0;
|
||||
}
|
||||
|
||||
JsonObject json = connection.localUtils().sendMessage("connect", params, timeout).getAsJsonObject();
|
||||
JsonPipe pipe = connection.getExistingObject(json.getAsJsonObject("pipe").get("guid").getAsString());
|
||||
Connection connection = new Connection(pipe, this.connection.env, this.connection.localUtils);
|
||||
PlaywrightImpl playwright = connection.initializePlaywright();
|
||||
@ -96,14 +93,13 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
}
|
||||
throw new PlaywrightException("Malformed endpoint. Did you use launchServer method?");
|
||||
}
|
||||
playwright.initSharedSelectors(this.connection.getExistingObject("Playwright"));
|
||||
playwright.selectors = this.playwright.selectors;
|
||||
BrowserImpl browser = connection.getExistingObject(playwright.initializer.getAsJsonObject("preLaunchedBrowser").get("guid").getAsString());
|
||||
browser.isConnectedOverWebSocket = true;
|
||||
browser.browserType = this;
|
||||
browser.connectToBrowserType(this, null);
|
||||
Consumer<JsonPipe> connectionCloseListener = t -> browser.notifyRemoteClosed();
|
||||
pipe.onClose(connectionCloseListener);
|
||||
browser.onDisconnected(b -> {
|
||||
playwright.unregisterSelectors();
|
||||
pipe.offClose(connectionCloseListener);
|
||||
try {
|
||||
connection.close();
|
||||
@ -119,25 +115,16 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
if (!"chromium".equals(name())) {
|
||||
throw new PlaywrightException("Connecting over CDP is only supported in Chromium.");
|
||||
}
|
||||
return withLogging("BrowserType.connectOverCDP", () -> connectOverCDPImpl(endpointURL, options));
|
||||
}
|
||||
|
||||
private Browser connectOverCDPImpl(String endpointURL, ConnectOverCDPOptions options) {
|
||||
if (options == null) {
|
||||
options = new ConnectOverCDPOptions();
|
||||
}
|
||||
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("endpointURL", endpointURL);
|
||||
JsonObject json = sendMessage("connectOverCDP", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("connectOverCDP", params, TimeoutSettings.launchTimeout(options.timeout)).getAsJsonObject();
|
||||
|
||||
BrowserImpl browser = connection.getExistingObject(json.getAsJsonObject("browser").get("guid").getAsString());
|
||||
browser.browserType = this;
|
||||
if (json.has("defaultContext")) {
|
||||
String contextId = json.getAsJsonObject("defaultContext").get("guid").getAsString();
|
||||
BrowserContextImpl defaultContext = connection.getExistingObject(contextId);
|
||||
browser.contexts.add(defaultContext);
|
||||
}
|
||||
browser.connectToBrowserType(this, null);
|
||||
return browser;
|
||||
}
|
||||
|
||||
@ -147,54 +134,19 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
|
||||
@Override
|
||||
public BrowserContextImpl launchPersistentContext(Path userDataDir, LaunchPersistentContextOptions options) {
|
||||
return withLogging("BrowserType.launchPersistentContext",
|
||||
() -> launchPersistentContextImpl(userDataDir, options));
|
||||
}
|
||||
|
||||
private BrowserContextImpl launchPersistentContextImpl(Path userDataDir, LaunchPersistentContextOptions options) {
|
||||
if (options == null) {
|
||||
options = new LaunchPersistentContextOptions();
|
||||
} else {
|
||||
// Make a copy so that we can nullify some fields below.
|
||||
options = convertType(options, LaunchPersistentContextOptions.class);
|
||||
}
|
||||
JsonObject recordHar = null;
|
||||
Path recordHarPath = options.recordHarPath;
|
||||
HarContentPolicy harContentPolicy = null;
|
||||
if (options.recordHarPath != null) {
|
||||
recordHar = new JsonObject();
|
||||
recordHar.addProperty("path", options.recordHarPath.toString());
|
||||
if (options.recordHarContent != null) {
|
||||
harContentPolicy = options.recordHarContent;
|
||||
} else if (options.recordHarOmitContent != null && options.recordHarOmitContent) {
|
||||
harContentPolicy = HarContentPolicy.OMIT;
|
||||
}
|
||||
if (harContentPolicy != null) {
|
||||
recordHar.addProperty("content", harContentPolicy.name().toLowerCase());
|
||||
}
|
||||
if (options.recordHarMode != null) {
|
||||
recordHar.addProperty("mode", options.recordHarMode.toString().toLowerCase());
|
||||
}
|
||||
addHarUrlFilter(recordHar, options.recordHarUrlFilter);
|
||||
options.recordHarPath = null;
|
||||
options.recordHarMode = null;
|
||||
options.recordHarOmitContent = null;
|
||||
options.recordHarContent = null;
|
||||
options.recordHarUrlFilter = null;
|
||||
} else {
|
||||
if (options.recordHarOmitContent != null) {
|
||||
throw new PlaywrightException("recordHarOmitContent is set but recordHarPath is null");
|
||||
}
|
||||
if (options.recordHarUrlFilter != null) {
|
||||
throw new PlaywrightException("recordHarUrlFilter is set but recordHarPath is null");
|
||||
}
|
||||
if (options.recordHarMode != null) {
|
||||
throw new PlaywrightException("recordHarMode is set but recordHarPath is null");
|
||||
}
|
||||
if (options.recordHarContent != null) {
|
||||
throw new PlaywrightException("recordHarContent is set but recordHarPath is null");
|
||||
}
|
||||
}
|
||||
|
||||
Browser.NewContextOptions harOptions = convertType(options, Browser.NewContextOptions.class);
|
||||
options.recordHarContent = null;
|
||||
options.recordHarMode = null;
|
||||
options.recordHarPath = null;
|
||||
options.recordHarOmitContent = null;
|
||||
options.recordHarUrlFilter = null;
|
||||
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
if (!userDataDir.isAbsolute() && !userDataDir.toString().isEmpty()) {
|
||||
@ -202,9 +154,6 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
userDataDir = cwd.resolve(userDataDir);
|
||||
}
|
||||
params.addProperty("userDataDir", userDataDir.toString());
|
||||
if (recordHar != null) {
|
||||
params.add("recordHar", recordHar);
|
||||
}
|
||||
if (options.recordVideoDir != null) {
|
||||
JsonObject recordVideo = new JsonObject();
|
||||
recordVideo.addProperty("dir", options.recordVideoDir.toAbsolutePath().toString());
|
||||
@ -232,13 +181,13 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
if (options.acceptDownloads != null) {
|
||||
params.addProperty("acceptDownloads", options.acceptDownloads ? "accept" : "deny");
|
||||
}
|
||||
JsonObject json = sendMessage("launchPersistentContext", params).getAsJsonObject();
|
||||
params.add("selectorEngines", gson().toJsonTree(playwright.selectors.selectorEngines));
|
||||
params.addProperty("testIdAttributeName", playwright.selectors.testIdAttributeName);
|
||||
JsonObject json = sendMessage("launchPersistentContext", params, TimeoutSettings.launchTimeout(options.timeout)).getAsJsonObject();
|
||||
BrowserImpl browser = connection.getExistingObject(json.getAsJsonObject("browser").get("guid").getAsString());
|
||||
browser.connectToBrowserType(this, options.tracesDir);
|
||||
BrowserContextImpl context = connection.getExistingObject(json.getAsJsonObject("context").get("guid").getAsString());
|
||||
context.videosDir = options.recordVideoDir;
|
||||
if (options.baseURL != null) {
|
||||
context.setBaseUrl(options.baseURL);
|
||||
}
|
||||
context.setRecordHar(recordHarPath, harContentPolicy);
|
||||
context.initializeHarFromOptions(harOptions);
|
||||
context.tracing().setTracesDir(options.tracesDir);
|
||||
return context;
|
||||
}
|
||||
|
@ -35,7 +35,8 @@ class ChannelOwner extends LoggingSupport {
|
||||
final String guid;
|
||||
final JsonObject initializer;
|
||||
private boolean wasCollected;
|
||||
private boolean isInternalType;
|
||||
|
||||
static Double NO_TIMEOUT = null;
|
||||
|
||||
protected ChannelOwner(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
this(parent.connection, parent, type, guid, initializer);
|
||||
@ -59,10 +60,6 @@ class ChannelOwner extends LoggingSupport {
|
||||
}
|
||||
}
|
||||
|
||||
void markAsInternalType() {
|
||||
isInternalType = true;
|
||||
}
|
||||
|
||||
void disposeChannelOwner(boolean wasGarbageCollected) {
|
||||
// Clean up from parent and connection.
|
||||
if (parent != null) {
|
||||
@ -87,16 +84,20 @@ class ChannelOwner extends LoggingSupport {
|
||||
return new WaitForEventLogger<>(this, apiName, code).get();
|
||||
}
|
||||
|
||||
@Override
|
||||
<T> T withLogging(String apiName, Supplier<T> code) {
|
||||
if (isInternalType) {
|
||||
apiName = null;
|
||||
}
|
||||
String previousApiName = connection.setApiName(apiName);
|
||||
|
||||
void withTitle(String title, Runnable code) {
|
||||
withTitle(title, () -> {
|
||||
code.run();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
<T> T withTitle(String title, Supplier<T> code) {
|
||||
String previousTitle = connection.setTitle(title);
|
||||
try {
|
||||
return super.withLogging(apiName, code);
|
||||
return code.get();
|
||||
} finally {
|
||||
connection.setApiName(previousApiName);
|
||||
connection.setTitle(previousTitle);
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,11 +111,16 @@ class ChannelOwner extends LoggingSupport {
|
||||
}
|
||||
|
||||
JsonElement sendMessage(String method) {
|
||||
return sendMessage(method, new JsonObject());
|
||||
return sendMessage(method, new JsonObject(), NO_TIMEOUT);
|
||||
}
|
||||
|
||||
JsonElement sendMessage(String method, JsonObject params) {
|
||||
JsonElement sendMessage(String method, JsonObject params, Double timeout) {
|
||||
checkNotCollected();
|
||||
if (timeout != null) {
|
||||
params.addProperty("timeout", timeout);
|
||||
} else if (params.has("timeout")) {
|
||||
throw new PlaywrightException("Internal error: timeout must be passed explicitly.");
|
||||
}
|
||||
return connection.sendMessage(guid, method, params);
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,8 @@ import com.microsoft.playwright.Clock;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import static com.microsoft.playwright.impl.ChannelOwner.NO_TIMEOUT;
|
||||
|
||||
class ClockImpl implements Clock {
|
||||
private final ChannelOwner browserContext;
|
||||
|
||||
@ -14,8 +16,7 @@ class ClockImpl implements Clock {
|
||||
|
||||
private void sendMessageWithLogging(String method, JsonObject params) {
|
||||
String capitalizedMethod = method.substring(0, 1).toUpperCase() + method.substring(1);
|
||||
browserContext.withLogging("Clock." + method,
|
||||
() -> browserContext.sendMessage("clock" + capitalizedMethod, params));
|
||||
browserContext.sendMessage("clock" + capitalizedMethod, params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -19,7 +19,6 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Playwright;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import com.microsoft.playwright.TimeoutError;
|
||||
|
||||
@ -64,7 +63,8 @@ public class Connection {
|
||||
private int lastId = 0;
|
||||
private final StackTraceCollector stackTraceCollector;
|
||||
private final Map<Integer, WaitableResult<JsonElement>> callbacks = new HashMap<>();
|
||||
private String apiName;
|
||||
private String title;
|
||||
private boolean titleReported = false;
|
||||
private static final boolean isLogging;
|
||||
static {
|
||||
String debug = System.getenv("DEBUG");
|
||||
@ -83,7 +83,7 @@ public class Connection {
|
||||
PlaywrightImpl initialize() {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("sdkLanguage", "java");
|
||||
JsonElement result = sendMessage("initialize", params.getAsJsonObject());
|
||||
JsonElement result = sendMessage("initialize", params.getAsJsonObject(), NO_TIMEOUT);
|
||||
return this.connection.getExistingObject(result.getAsJsonObject().getAsJsonObject("playwright").get("guid").getAsString());
|
||||
}
|
||||
}
|
||||
@ -116,9 +116,10 @@ public class Connection {
|
||||
}
|
||||
}
|
||||
|
||||
String setApiName(String name) {
|
||||
String previous = apiName;
|
||||
apiName = name;
|
||||
String setTitle(String newTitle) {
|
||||
String previous = title;
|
||||
titleReported = false;
|
||||
title = newTitle;
|
||||
return previous;
|
||||
}
|
||||
|
||||
@ -146,12 +147,14 @@ public class Connection {
|
||||
JsonObject metadata = new JsonObject();
|
||||
metadata.addProperty("wallTime", currentTimeMillis());
|
||||
JsonArray stack = null;
|
||||
if (apiName == null) {
|
||||
if (titleReported) {
|
||||
metadata.addProperty("internal", true);
|
||||
} else {
|
||||
metadata.addProperty("apiName", apiName);
|
||||
// All but first message in an API call are considered internal and will be hidden from the inspector.
|
||||
apiName = null;
|
||||
if (title != null) {
|
||||
metadata.addProperty("title", title);
|
||||
// All but first message in a custom-titled API call are considered internal and will be hidden from the inspector.
|
||||
titleReported = true;
|
||||
}
|
||||
if (stackTraceCollector != null) {
|
||||
stack = stackTraceCollector.currentStackTrace();
|
||||
if (!stack.isEmpty()) {
|
||||
@ -373,9 +376,6 @@ public class Connection {
|
||||
case "Stream":
|
||||
result = new Stream(parent, type, guid, initializer);
|
||||
break;
|
||||
case "Selectors":
|
||||
result = new SelectorsImpl(parent, type, guid, initializer);
|
||||
break;
|
||||
case "SocksSupport":
|
||||
break;
|
||||
case "Tracing":
|
||||
|
@ -20,13 +20,10 @@ import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.ConsoleMessage;
|
||||
import com.microsoft.playwright.JSHandle;
|
||||
import com.microsoft.playwright.Page;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
|
||||
public class ConsoleMessageImpl implements ConsoleMessage {
|
||||
private final Connection connection;
|
||||
private PageImpl page;
|
||||
|
@ -34,18 +34,16 @@ class DialogImpl extends ChannelOwner implements Dialog {
|
||||
|
||||
@Override
|
||||
public void accept(String promptText) {
|
||||
withLogging("Dialog.accept", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
if (promptText != null) {
|
||||
params.addProperty("promptText", promptText);
|
||||
}
|
||||
sendMessage("accept", params);
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
if (promptText != null) {
|
||||
params.addProperty("promptText", promptText);
|
||||
}
|
||||
sendMessage("accept", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismiss() {
|
||||
withLogging("Dialog.dismiss", () -> sendMessage("dismiss"));
|
||||
sendMessage("dismiss");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -46,22 +46,22 @@ class DownloadImpl implements Download {
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
page.withLogging("Download.cancel", () -> artifact.cancel());
|
||||
artifact.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream createReadStream() {
|
||||
return page.withLogging("Download.createReadStream", () -> artifact.createReadStream());
|
||||
return artifact.createReadStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
page.withLogging("Download.delete", () -> artifact.delete());
|
||||
artifact.delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String failure() {
|
||||
return page.withLogging("Download.failure", () -> artifact.failure());
|
||||
return artifact.failure();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -71,11 +71,11 @@ class DownloadImpl implements Download {
|
||||
|
||||
@Override
|
||||
public Path path() {
|
||||
return page.withLogging("Download.path", () -> artifact.pathAfterFinished());
|
||||
return artifact.pathAfterFinished();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAs(Path path) {
|
||||
page.withLogging("Download.saveAs", () -> artifact.saveAs(path));
|
||||
artifact.saveAs(path);
|
||||
}
|
||||
}
|
||||
|
@ -40,8 +40,11 @@ import static com.microsoft.playwright.options.ScreenshotType.JPEG;
|
||||
import static com.microsoft.playwright.options.ScreenshotType.PNG;
|
||||
|
||||
public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
private final FrameImpl frame;
|
||||
|
||||
ElementHandleImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
this.frame = (FrameImpl)parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -51,105 +54,83 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
|
||||
@Override
|
||||
public ElementHandle querySelector(String selector) {
|
||||
return withLogging("ElementHandle.querySelector", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonElement json = sendMessage("querySelector", params);
|
||||
JsonObject element = json.getAsJsonObject().getAsJsonObject("element");
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
return connection.getExistingObject(element.get("guid").getAsString());
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonElement json = sendMessage("querySelector", params, NO_TIMEOUT);
|
||||
JsonObject element = json.getAsJsonObject().getAsJsonObject("element");
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
return connection.getExistingObject(element.get("guid").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ElementHandle> querySelectorAll(String selector) {
|
||||
return withLogging("ElementHandle.<", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonElement json = sendMessage("querySelectorAll", params);
|
||||
JsonArray elements = json.getAsJsonObject().getAsJsonArray("elements");
|
||||
if (elements == null) {
|
||||
return null;
|
||||
}
|
||||
List<ElementHandle> handles = new ArrayList<>();
|
||||
for (JsonElement item : elements) {
|
||||
handles.add(connection.getExistingObject(item.getAsJsonObject().get("guid").getAsString()));
|
||||
}
|
||||
return handles;
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonElement json = sendMessage("querySelectorAll", params, NO_TIMEOUT);
|
||||
JsonArray elements = json.getAsJsonObject().getAsJsonArray("elements");
|
||||
if (elements == null) {
|
||||
return null;
|
||||
}
|
||||
List<ElementHandle> handles = new ArrayList<>();
|
||||
for (JsonElement item : elements) {
|
||||
handles.add(connection.getExistingObject(item.getAsJsonObject().get("guid").getAsString()));
|
||||
}
|
||||
return handles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evalOnSelector(String selector, String pageFunction, Object arg) {
|
||||
return withLogging("ElementHandle.evalOnSelector", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evalOnSelector", params);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evalOnSelector", params, NO_TIMEOUT);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evalOnSelectorAll(String selector, String pageFunction, Object arg) {
|
||||
return withLogging("ElementHandle.evalOnSelectorAll", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evalOnSelectorAll", params);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evalOnSelectorAll", params, NO_TIMEOUT);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoundingBox boundingBox() {
|
||||
return withLogging("ElementHandle.boundingBox", () -> {
|
||||
JsonObject json = sendMessage("boundingBox").getAsJsonObject();
|
||||
if (!json.has("value")) {
|
||||
return null;
|
||||
}
|
||||
return gson().fromJson(json.get("value"), BoundingBox.class);
|
||||
});
|
||||
JsonObject json = sendMessage("boundingBox").getAsJsonObject();
|
||||
if (!json.has("value")) {
|
||||
return null;
|
||||
}
|
||||
return gson().fromJson(json.get("value"), BoundingBox.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void check(CheckOptions options) {
|
||||
withLogging("ElementHandle.check", () -> checkImpl(options));
|
||||
}
|
||||
|
||||
private void checkImpl(CheckOptions options) {
|
||||
if (options == null) {
|
||||
options = new CheckOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("check", params);
|
||||
sendMessage("check", params, frame.timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void click(ClickOptions options) {
|
||||
withLogging("ElementHandle.click", () -> clickImpl(options));
|
||||
}
|
||||
|
||||
private void clickImpl(ClickOptions options) {
|
||||
if (options == null) {
|
||||
options = new ClickOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("click", params);
|
||||
sendMessage("click", params, frame.timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Frame contentFrame() {
|
||||
return withLogging("ElementHandle.contentFrame", () -> contentFrameImpl());
|
||||
}
|
||||
|
||||
private Frame contentFrameImpl() {
|
||||
JsonObject json = sendMessage("contentFrame").getAsJsonObject();
|
||||
if (!json.has("frame")) {
|
||||
return null;
|
||||
@ -159,177 +140,132 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
|
||||
@Override
|
||||
public void dblclick(DblclickOptions options) {
|
||||
withLogging("ElementHandle.dblclick", () -> dblclickImpl(options));
|
||||
}
|
||||
|
||||
private void dblclickImpl(DblclickOptions options) {
|
||||
if (options == null) {
|
||||
options = new DblclickOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("dblclick", params);
|
||||
sendMessage("dblclick", params, frame.timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchEvent(String type, Object eventInit) {
|
||||
withLogging("ElementHandle.dispatchEvent", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("type", type);
|
||||
params.add("eventInit", gson().toJsonTree(serializeArgument(eventInit)));
|
||||
sendMessage("dispatchEvent", params);
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("type", type);
|
||||
params.add("eventInit", gson().toJsonTree(serializeArgument(eventInit)));
|
||||
sendMessage("dispatchEvent", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fill(String value, FillOptions options) {
|
||||
withLogging("ElementHandle.fill", () -> fillImpl(value, options));
|
||||
}
|
||||
|
||||
private void fillImpl(String value, FillOptions options) {
|
||||
if (options == null) {
|
||||
options = new FillOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("value", value);
|
||||
sendMessage("fill", params);
|
||||
sendMessage("fill", params, frame.timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focus() {
|
||||
withLogging("ElementHandle.focus", () -> sendMessage("focus"));
|
||||
sendMessage("focus");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttribute(String name) {
|
||||
return withLogging("ElementHandle.getAttribute", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("name", name);
|
||||
JsonObject json = sendMessage("getAttribute", params).getAsJsonObject();
|
||||
return json.has("value") ? json.get("value").getAsString() : null;
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("name", name);
|
||||
JsonObject json = sendMessage("getAttribute", params, NO_TIMEOUT).getAsJsonObject();
|
||||
return json.has("value") ? json.get("value").getAsString() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hover(HoverOptions options) {
|
||||
withLogging("ElementHandle.hover", () -> hoverImpl(options));
|
||||
}
|
||||
|
||||
private void hoverImpl(HoverOptions options) {
|
||||
if (options == null) {
|
||||
options = new HoverOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("hover", params);
|
||||
sendMessage("hover", params, frame.timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String innerHTML() {
|
||||
return withLogging("ElementHandle.innerHTML", () -> {
|
||||
JsonObject json = sendMessage("innerHTML").getAsJsonObject();
|
||||
return json.get("value").getAsString();
|
||||
});
|
||||
JsonObject json = sendMessage("innerHTML").getAsJsonObject();
|
||||
return json.get("value").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String innerText() {
|
||||
return withLogging("ElementHandle.innerText", () -> {
|
||||
JsonObject json = sendMessage("innerText").getAsJsonObject();
|
||||
return json.get("value").getAsString();
|
||||
});
|
||||
JsonObject json = sendMessage("innerText").getAsJsonObject();
|
||||
return json.get("value").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String inputValue(InputValueOptions options) {
|
||||
return withLogging("ElementHandle.inputValue", () -> inputValueImpl(options));
|
||||
}
|
||||
|
||||
private String inputValueImpl(InputValueOptions options) {
|
||||
if (options == null) {
|
||||
options = new InputValueOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
JsonObject json = sendMessage("inputValue", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("inputValue", params, NO_TIMEOUT).getAsJsonObject();
|
||||
return json.get("value").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
return withLogging("ElementHandle.isChecked", () -> {
|
||||
JsonObject json = sendMessage("isChecked").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
});
|
||||
JsonObject json = sendMessage("isChecked").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisabled() {
|
||||
return withLogging("ElementHandle.isDisabled", () -> {
|
||||
JsonObject json = sendMessage("isDisabled").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
});
|
||||
JsonObject json = sendMessage("isDisabled").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEditable() {
|
||||
return withLogging("ElementHandle.isEditable", () -> {
|
||||
JsonObject json = sendMessage("isEditable").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
});
|
||||
JsonObject json = sendMessage("isEditable").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return withLogging("ElementHandle.isEnabled", () -> {
|
||||
JsonObject json = sendMessage("isEnabled").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
});
|
||||
JsonObject json = sendMessage("isEnabled").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden() {
|
||||
return withLogging("ElementHandle.isHidden", () -> {
|
||||
JsonObject json = sendMessage("isHidden").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
});
|
||||
JsonObject json = sendMessage("isHidden").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return withLogging("ElementHandle.isVisible", () -> {
|
||||
JsonObject json = sendMessage("isVisible").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
});
|
||||
JsonObject json = sendMessage("isVisible").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrameImpl ownerFrame() {
|
||||
return withLogging("ElementHandle.ownerFrame", () -> {
|
||||
JsonObject json = sendMessage("ownerFrame").getAsJsonObject();
|
||||
if (!json.has("frame")) {
|
||||
return null;
|
||||
}
|
||||
return connection.getExistingObject(json.getAsJsonObject("frame").get("guid").getAsString());
|
||||
});
|
||||
JsonObject json = sendMessage("ownerFrame").getAsJsonObject();
|
||||
if (!json.has("frame")) {
|
||||
return null;
|
||||
}
|
||||
return connection.getExistingObject(json.getAsJsonObject("frame").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void press(String key, PressOptions options) {
|
||||
withLogging("ElementHandle.press", () -> pressImpl(key, options));
|
||||
}
|
||||
private void pressImpl(String key, PressOptions options) {
|
||||
if (options == null) {
|
||||
options = new PressOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("key", key);
|
||||
sendMessage("press", params);
|
||||
sendMessage("press", params, frame.timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] screenshot(ScreenshotOptions options) {
|
||||
return withLogging("ElementHandle.screenshot", () -> screenshotImpl(options));
|
||||
}
|
||||
|
||||
private byte[] screenshotImpl(ScreenshotOptions options) {
|
||||
if (options == null) {
|
||||
options = new ScreenshotOptions();
|
||||
}
|
||||
@ -348,7 +284,7 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.remove("path");
|
||||
JsonObject json = sendMessage("screenshot", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("screenshot", params, frame.timeout(options.timeout)).getAsJsonObject();
|
||||
|
||||
byte[] buffer = Base64.getDecoder().decode(json.get("binary").getAsString());
|
||||
if (options.path != null) {
|
||||
@ -359,7 +295,11 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
|
||||
@Override
|
||||
public void scrollIntoViewIfNeeded(ScrollIntoViewIfNeededOptions options) {
|
||||
withLogging("ElementHandle.scrollIntoViewIfNeeded", () -> scrollIntoViewIfNeededImpl(options));
|
||||
if (options == null) {
|
||||
options = new ScrollIntoViewIfNeededOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("scrollIntoViewIfNeeded", params, frame.timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -383,7 +323,7 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
if (values != null) {
|
||||
params.add("options", toSelectValueOrLabel(values));
|
||||
}
|
||||
return selectOption(params);
|
||||
return selectOption(params, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -392,13 +332,6 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
return selectOption(values, options);
|
||||
}
|
||||
|
||||
private void scrollIntoViewIfNeededImpl(ScrollIntoViewIfNeededOptions options) {
|
||||
if (options == null) {
|
||||
options = new ScrollIntoViewIfNeededOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("scrollIntoViewIfNeeded", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(SelectOption[] values, SelectOptionOptions options) {
|
||||
@ -409,7 +342,7 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
if (values != null) {
|
||||
params.add("options", gson().toJsonTree(values));
|
||||
}
|
||||
return selectOption(params);
|
||||
return selectOption(params, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -421,19 +354,21 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
if (values != null) {
|
||||
params.add("elements", Serialization.toProtocol(values));
|
||||
}
|
||||
return selectOption(params);
|
||||
return selectOption(params, options.timeout);
|
||||
}
|
||||
|
||||
private List<String> selectOption(JsonObject params) {
|
||||
return withLogging("SelectOption", () -> {
|
||||
JsonObject json = sendMessage("selectOption", params).getAsJsonObject();
|
||||
return parseStringList(json.getAsJsonArray("values"));
|
||||
});
|
||||
private List<String> selectOption(JsonObject params, Double timeout) {
|
||||
JsonObject json = sendMessage("selectOption", params, frame.timeout(timeout)).getAsJsonObject();
|
||||
return parseStringList(json.getAsJsonArray("values"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectText(SelectTextOptions options) {
|
||||
withLogging("ElementHandle.selectText", () -> selectTextImpl(options));
|
||||
if (options == null) {
|
||||
options = new SelectTextOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("selectText", params, frame.timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -450,20 +385,9 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
setInputFiles(new Path[]{files}, options);
|
||||
}
|
||||
|
||||
private void selectTextImpl(SelectTextOptions options) {
|
||||
if (options == null) {
|
||||
options = new SelectTextOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("selectText", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(Path[] files, SetInputFilesOptions options) {
|
||||
withLogging("ElementHandle.setInputFiles", () -> setInputFilesImpl(files, options));
|
||||
}
|
||||
|
||||
void setInputFilesImpl(Path[] files, SetInputFilesOptions options) {
|
||||
FrameImpl frame = ownerFrame();
|
||||
if (frame == null) {
|
||||
throw new Error("Cannot set input files to detached element");
|
||||
@ -473,7 +397,7 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
addFilePathUploadParams(files, params, frame.page().context());
|
||||
sendMessage("setInputFiles", params);
|
||||
sendMessage("setInputFiles", params, frame.timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -483,77 +407,51 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
|
||||
@Override
|
||||
public void setInputFiles(FilePayload[] files, SetInputFilesOptions options) {
|
||||
withLogging("ElementHandle.setInputFiles", () -> setInputFilesImpl(files, options));
|
||||
}
|
||||
|
||||
void setInputFilesImpl(FilePayload[] files, SetInputFilesOptions options) {
|
||||
checkFilePayloadSize(files);
|
||||
if (options == null) {
|
||||
options = new SetInputFilesOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.add("payloads", Serialization.toJsonArray(files));
|
||||
sendMessage("setInputFiles", params);
|
||||
sendMessage("setInputFiles", params, frame.timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tap(TapOptions options) {
|
||||
withLogging("ElementHandle.tap", () -> tapImpl(options));
|
||||
}
|
||||
|
||||
private void tapImpl(TapOptions options) {
|
||||
if (options == null) {
|
||||
options = new TapOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("tap", params);
|
||||
sendMessage("tap", params, frame.timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String textContent() {
|
||||
return withLogging("ElementHandle.textContent", () -> textContentImpl());
|
||||
}
|
||||
|
||||
private String textContentImpl() {
|
||||
return withLogging("ElementHandle.textContent", () -> {
|
||||
JsonObject json = sendMessage("textContent").getAsJsonObject();
|
||||
return json.has("value") ? json.get("value").getAsString() : null;
|
||||
});
|
||||
JsonObject json = sendMessage("textContent").getAsJsonObject();
|
||||
return json.has("value") ? json.get("value").getAsString() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void type(String text, TypeOptions options) {
|
||||
withLogging("ElementHandle.type", () -> typeImpl(text, options));
|
||||
}
|
||||
|
||||
private void typeImpl(String text, TypeOptions options) {
|
||||
if (options == null) {
|
||||
options = new TypeOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("text", text);
|
||||
sendMessage("type", params);
|
||||
sendMessage("type", params, frame.timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncheck(UncheckOptions options) {
|
||||
withLogging("ElementHandle.uncheck", () -> uncheckImpl(options));
|
||||
}
|
||||
|
||||
private void uncheckImpl(UncheckOptions options) {
|
||||
if (options == null) {
|
||||
options = new UncheckOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("uncheck", params);
|
||||
sendMessage("uncheck", params, frame.timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waitForElementState(ElementState state, WaitForElementStateOptions options) {
|
||||
withLogging("ElementHandle.waitForElementState", () -> waitForElementStateImpl(state, options));
|
||||
}
|
||||
|
||||
private void waitForElementStateImpl(ElementState state, WaitForElementStateOptions options) {
|
||||
if (options == null) {
|
||||
options = new WaitForElementStateOptions();
|
||||
}
|
||||
@ -562,7 +460,7 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("state", toProtocol(state));
|
||||
sendMessage("waitForElementState", params);
|
||||
sendMessage("waitForElementState", params, frame.timeout(options.timeout));
|
||||
}
|
||||
|
||||
private static String toProtocol(ElementState state) {
|
||||
@ -571,16 +469,12 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
|
||||
@Override
|
||||
public ElementHandle waitForSelector(String selector, WaitForSelectorOptions options) {
|
||||
return withLogging("ElementHandle.waitForSelector", () -> waitForSelectorImpl(selector, options));
|
||||
}
|
||||
|
||||
private ElementHandle waitForSelectorImpl(String selector, WaitForSelectorOptions options) {
|
||||
if (options == null) {
|
||||
options = new WaitForSelectorOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonElement json = sendMessage("waitForSelector", params);
|
||||
JsonElement json = sendMessage("waitForSelector", params, frame.timeout(options.timeout)).getAsJsonObject();
|
||||
JsonObject element = json.getAsJsonObject().getAsJsonObject("element");
|
||||
if (element == null) {
|
||||
return null;
|
||||
|
@ -58,8 +58,7 @@ class FileChooserImpl implements FileChooser {
|
||||
|
||||
@Override
|
||||
public void setFiles(Path[] files, SetFilesOptions options) {
|
||||
page.withLogging("FileChooser.setInputFiles",
|
||||
() -> element.setInputFilesImpl(files, convertType(options, ElementHandle.SetInputFilesOptions.class)));
|
||||
element.setInputFiles(files, convertType(options, ElementHandle.SetInputFilesOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -69,7 +68,6 @@ class FileChooserImpl implements FileChooser {
|
||||
|
||||
@Override
|
||||
public void setFiles(FilePayload[] files, SetFilesOptions options) {
|
||||
page.withLogging("FileChooser.setInputFiles",
|
||||
() -> element.setInputFilesImpl(files, convertType(options, ElementHandle.SetInputFilesOptions.class)));
|
||||
element.setInputFiles(files, convertType(options, ElementHandle.SetInputFilesOptions.class));
|
||||
}
|
||||
}
|
||||
|
@ -74,16 +74,12 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public ElementHandle querySelector(String selector, QuerySelectorOptions options) {
|
||||
return withLogging("Frame.querySelector", () -> querySelectorImpl(selector, options));
|
||||
}
|
||||
|
||||
ElementHandleImpl querySelectorImpl(String selector, QuerySelectorOptions options) {
|
||||
if (options == null) {
|
||||
options = new QuerySelectorOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonElement json = sendMessage("querySelector", params);
|
||||
JsonElement json = sendMessage("querySelector", params, NO_TIMEOUT);
|
||||
JsonObject element = json.getAsJsonObject().getAsJsonObject("element");
|
||||
if (element == null) {
|
||||
return null;
|
||||
@ -93,7 +89,18 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public List<ElementHandle> querySelectorAll(String selector) {
|
||||
return withLogging("Frame.querySelectorAll", () -> querySelectorAllImpl(selector));
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonElement json = sendMessage("querySelectorAll", params, NO_TIMEOUT);
|
||||
JsonArray elements = json.getAsJsonObject().getAsJsonArray("elements");
|
||||
if (elements == null) {
|
||||
return null;
|
||||
}
|
||||
List<ElementHandle> handles = new ArrayList<>();
|
||||
for (JsonElement item : elements) {
|
||||
handles.add(connection.getExistingObject(item.getAsJsonObject().get("guid").getAsString()));
|
||||
}
|
||||
return handles;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -110,7 +117,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String selector, String[] values, SelectOptionOptions options) {
|
||||
return withLogging("Frame.selectOption", () -> selectOptionImpl(selector, values, options));
|
||||
return selectOptionImpl(selector, values, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -119,24 +126,10 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
return selectOption(selector, values, options);
|
||||
}
|
||||
|
||||
List<ElementHandle> querySelectorAllImpl(String selector) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonElement json = sendMessage("querySelectorAll", params);
|
||||
JsonArray elements = json.getAsJsonObject().getAsJsonArray("elements");
|
||||
if (elements == null) {
|
||||
return null;
|
||||
}
|
||||
List<ElementHandle> handles = new ArrayList<>();
|
||||
for (JsonElement item : elements) {
|
||||
handles.add(connection.getExistingObject(item.getAsJsonObject().get("guid").getAsString()));
|
||||
}
|
||||
return handles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evalOnSelector(String selector, String pageFunction, Object arg, EvalOnSelectorOptions options) {
|
||||
return withLogging("Frame.evalOnSelector", () -> evalOnSelectorImpl(selector, pageFunction, arg, options));
|
||||
return evalOnSelectorImpl(selector, pageFunction, arg, options);
|
||||
}
|
||||
|
||||
Object evalOnSelectorImpl(String selector, String pageFunction, Object arg, EvalOnSelectorOptions options) {
|
||||
@ -147,14 +140,14 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evalOnSelector", params);
|
||||
JsonElement json = sendMessage("evalOnSelector", params, NO_TIMEOUT);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evalOnSelectorAll(String selector, String pageFunction, Object arg) {
|
||||
return withLogging("Frame.evalOnSelectorAll", () -> evalOnSelectorAllImpl(selector, pageFunction, arg));
|
||||
return evalOnSelectorAllImpl(selector, pageFunction, arg);
|
||||
}
|
||||
|
||||
Object evalOnSelectorAllImpl(String selector, String pageFunction, Object arg) {
|
||||
@ -162,14 +155,14 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evalOnSelectorAll", params);
|
||||
JsonElement json = sendMessage("evalOnSelectorAll", params, NO_TIMEOUT);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementHandle addScriptTag(AddScriptTagOptions options){
|
||||
return withLogging("Frame.addScriptTag", () -> addScriptTagImpl(options));
|
||||
return addScriptTagImpl(options);
|
||||
}
|
||||
|
||||
ElementHandle addScriptTagImpl(AddScriptTagOptions options) {
|
||||
@ -189,13 +182,13 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
content = addSourceUrlToScript(content, options.path);
|
||||
jsonOptions.addProperty("content", content);
|
||||
}
|
||||
JsonElement json = sendMessage("addScriptTag", jsonOptions);
|
||||
JsonElement json = sendMessage("addScriptTag", jsonOptions, NO_TIMEOUT);
|
||||
return connection.getExistingObject(json.getAsJsonObject().getAsJsonObject("element").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementHandle addStyleTag(AddStyleTagOptions options){
|
||||
return withLogging("Frame.addStyleTag", () -> addStyleTagImpl(options));
|
||||
return addStyleTagImpl(options);
|
||||
}
|
||||
|
||||
ElementHandle addStyleTagImpl(AddStyleTagOptions options) {
|
||||
@ -215,22 +208,18 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
content += "/*# sourceURL=" + options.path.toString().replace("\n", "") + "*/";
|
||||
jsonOptions.addProperty("content", content);
|
||||
}
|
||||
JsonElement json = sendMessage("addStyleTag", jsonOptions);
|
||||
JsonElement json = sendMessage("addStyleTag", jsonOptions, NO_TIMEOUT);
|
||||
return connection.getExistingObject(json.getAsJsonObject().getAsJsonObject("element").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void check(String selector, CheckOptions options){
|
||||
withLogging("Frame.check", () -> checkImpl(selector, options));
|
||||
}
|
||||
|
||||
void checkImpl(String selector, CheckOptions options) {
|
||||
if (options == null) {
|
||||
options = new CheckOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
sendMessage("check", params);
|
||||
sendMessage("check", params, timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -240,7 +229,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public void click(String selector, ClickOptions options) {
|
||||
withLogging("Frame.click", () -> clickImpl(selector, options));
|
||||
clickImpl(selector, options);
|
||||
}
|
||||
|
||||
void clickImpl(String selector, ClickOptions options) {
|
||||
@ -249,38 +238,26 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
sendMessage("click", params);
|
||||
sendMessage("click", params, timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String content() {
|
||||
return withLogging("Frame.content", () -> contentImpl());
|
||||
}
|
||||
|
||||
String contentImpl() {
|
||||
return sendMessage("content").getAsJsonObject().get("value").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dblclick(String selector, DblclickOptions options) {
|
||||
withLogging("Frame.dblclick", () -> dblclickImpl(selector, options));
|
||||
}
|
||||
|
||||
void dblclickImpl(String selector, DblclickOptions options) {
|
||||
if (options == null) {
|
||||
options = new DblclickOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
sendMessage("dblclick", params);
|
||||
sendMessage("dblclick", params, timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchEvent(String selector, String type, Object eventInit, DispatchEventOptions options) {
|
||||
withLogging("Frame.dispatchEvent", () -> dispatchEventImpl(selector, type, eventInit, options));
|
||||
}
|
||||
|
||||
void dispatchEventImpl(String selector, String type, Object eventInit, DispatchEventOptions options) {
|
||||
if (options == null) {
|
||||
options = new DispatchEventOptions();
|
||||
}
|
||||
@ -288,71 +265,55 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("type", type);
|
||||
params.add("eventInit", gson().toJsonTree(serializeArgument(eventInit)));
|
||||
sendMessage("dispatchEvent", params);
|
||||
sendMessage("dispatchEvent", params, timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evaluate(String expression, Object arg) {
|
||||
return withLogging("Frame.evaluate", () -> evaluateImpl(expression, arg));
|
||||
}
|
||||
|
||||
Object evaluateImpl(String expression, Object arg) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", expression);
|
||||
params.addProperty("world", "main");
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpression", params);
|
||||
JsonElement json = sendMessage("evaluateExpression", params, NO_TIMEOUT);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSHandle evaluateHandle(String pageFunction, Object arg) {
|
||||
return withLogging("Frame.evaluateHandle", () -> evaluateHandleImpl(pageFunction, arg));
|
||||
}
|
||||
|
||||
JSHandle evaluateHandleImpl(String pageFunction, Object arg) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.addProperty("world", "main");
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpressionHandle", params);
|
||||
JsonElement json = sendMessage("evaluateExpressionHandle", params, NO_TIMEOUT);
|
||||
return connection.getExistingObject(json.getAsJsonObject().getAsJsonObject("handle").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fill(String selector, String value, FillOptions options) {
|
||||
withLogging("Frame.fill", () -> fillImpl(selector, value, options));
|
||||
}
|
||||
|
||||
void fillImpl(String selector, String value, FillOptions options) {
|
||||
if (options == null) {
|
||||
options = new FillOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("value", value);
|
||||
sendMessage("fill", params);
|
||||
sendMessage("fill", params, timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focus(String selector, FocusOptions options) {
|
||||
withLogging("Frame.focus", () -> focusImpl(selector, options));
|
||||
}
|
||||
|
||||
void focusImpl(String selector, FocusOptions options) {
|
||||
if (options == null) {
|
||||
options = new FocusOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
sendMessage("focus", params);
|
||||
sendMessage("focus", params, timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementHandle frameElement() {
|
||||
return withLogging("Frame.frameElement", () -> frameElementImpl());
|
||||
|
||||
JsonObject json = sendMessage("frameElement").getAsJsonObject();
|
||||
return connection.getExistingObject(json.getAsJsonObject("element").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -360,14 +321,9 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
return new FrameLocatorImpl(this, selector);
|
||||
}
|
||||
|
||||
ElementHandle frameElementImpl() {
|
||||
JsonObject json = sendMessage("frameElement").getAsJsonObject();
|
||||
return connection.getExistingObject(json.getAsJsonObject("element").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttribute(String selector, String name, GetAttributeOptions options) {
|
||||
return withLogging("Frame.getAttribute", () -> getAttributeImpl(selector, name, options));
|
||||
return getAttributeImpl(selector, name, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -442,7 +398,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("name", name);
|
||||
JsonObject json = sendMessage("getAttribute", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("getAttribute", params, timeout(options.timeout)).getAsJsonObject();
|
||||
if (json.has("value")) {
|
||||
return json.get("value").getAsString();
|
||||
}
|
||||
@ -451,7 +407,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public ResponseImpl navigate(String url, NavigateOptions options) {
|
||||
return withLogging("Page.navigate", () -> navigateImpl(url, options));
|
||||
return navigateImpl(url, options);
|
||||
}
|
||||
|
||||
ResponseImpl navigateImpl(String url, NavigateOptions options) {
|
||||
@ -460,7 +416,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("url", url);
|
||||
JsonElement result = sendMessage("goto", params);
|
||||
JsonElement result = sendMessage("goto", params, navigationTimeout(options.timeout));
|
||||
JsonObject jsonResponse = result.getAsJsonObject().getAsJsonObject("response");
|
||||
if (jsonResponse == null) {
|
||||
return null;
|
||||
@ -470,7 +426,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public void hover(String selector, HoverOptions options) {
|
||||
withLogging("Frame.hover", () -> hoverImpl(selector, options));
|
||||
hoverImpl(selector, options);
|
||||
}
|
||||
|
||||
void hoverImpl(String selector, HoverOptions options) {
|
||||
@ -479,12 +435,12 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
sendMessage("hover", params);
|
||||
sendMessage("hover", params, timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragAndDrop(String source, String target, DragAndDropOptions options) {
|
||||
withLogging("Frame.dragAndDrop", () -> dragAndDropImpl(source, target, options));
|
||||
dragAndDropImpl(source, target, options);
|
||||
}
|
||||
|
||||
void dragAndDropImpl(String source, String target, DragAndDropOptions options) {
|
||||
@ -494,12 +450,12 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("source", source);
|
||||
params.addProperty("target", target);
|
||||
sendMessage("dragAndDrop", params);
|
||||
sendMessage("dragAndDrop", params, timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String innerHTML(String selector, InnerHTMLOptions options) {
|
||||
return withLogging("Frame.innerHTML", () -> innerHTMLImpl(selector, options));
|
||||
return innerHTMLImpl(selector, options);
|
||||
}
|
||||
|
||||
String innerHTMLImpl(String selector, InnerHTMLOptions options) {
|
||||
@ -508,13 +464,13 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject json = sendMessage("innerHTML", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("innerHTML", params, timeout(options.timeout)).getAsJsonObject();
|
||||
return json.get("value").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String innerText(String selector, InnerTextOptions options) {
|
||||
return withLogging("Frame.innerText", () -> innerTextImpl(selector, options));
|
||||
return innerTextImpl(selector, options);
|
||||
}
|
||||
|
||||
String innerTextImpl(String selector, InnerTextOptions options) {
|
||||
@ -523,13 +479,13 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject json = sendMessage("innerText", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("innerText", params, timeout(options.timeout)).getAsJsonObject();
|
||||
return json.get("value").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String inputValue(String selector, InputValueOptions options) {
|
||||
return withLogging("Frame.inputValue", () -> inputValueImpl(selector, options));
|
||||
return inputValueImpl(selector, options);
|
||||
}
|
||||
|
||||
String inputValueImpl(String selector, InputValueOptions options) {
|
||||
@ -538,13 +494,13 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject json = sendMessage("inputValue", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("inputValue", params, timeout(options.timeout)).getAsJsonObject();
|
||||
return json.get("value").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked(String selector, IsCheckedOptions options) {
|
||||
return withLogging("Page.isChecked", () -> isCheckedImpl(selector, options));
|
||||
return isCheckedImpl(selector, options);
|
||||
}
|
||||
|
||||
boolean isCheckedImpl(String selector, IsCheckedOptions options) {
|
||||
@ -553,7 +509,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject json = sendMessage("isChecked", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("isChecked", params, timeout(options.timeout)).getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
}
|
||||
|
||||
@ -564,7 +520,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public boolean isDisabled(String selector, IsDisabledOptions options) {
|
||||
return withLogging("Page.isDisabled", () -> isDisabledImpl(selector, options));
|
||||
return isDisabledImpl(selector, options);
|
||||
}
|
||||
|
||||
boolean isDisabledImpl(String selector, IsDisabledOptions options) {
|
||||
@ -573,13 +529,13 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject json = sendMessage("isDisabled", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("isDisabled", params, timeout(options.timeout)).getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEditable(String selector, IsEditableOptions options) {
|
||||
return withLogging("Page.isEditable", () -> isEditableImpl(selector, options));
|
||||
return isEditableImpl(selector, options);
|
||||
}
|
||||
|
||||
boolean isEditableImpl(String selector, IsEditableOptions options) {
|
||||
@ -588,13 +544,13 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject json = sendMessage("isEditable", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("isEditable", params, timeout(options.timeout)).getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(String selector, IsEnabledOptions options) {
|
||||
return withLogging("Page.isEnabled", () -> isEnabledImpl(selector, options));
|
||||
return isEnabledImpl(selector, options);
|
||||
}
|
||||
|
||||
boolean isEnabledImpl(String selector, IsEnabledOptions options) {
|
||||
@ -603,13 +559,13 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject json = sendMessage("isEnabled", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("isEnabled", params, timeout(options.timeout)).getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden(String selector, IsHiddenOptions options) {
|
||||
return withLogging("Page.isHidden", () -> isHiddenImpl(selector, options));
|
||||
return isHiddenImpl(selector, options);
|
||||
}
|
||||
|
||||
boolean isHiddenImpl(String selector, IsHiddenOptions options) {
|
||||
@ -618,13 +574,13 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject json = sendMessage("isHidden", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("isHidden", params, timeout(options.timeout)).getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible(String selector, IsVisibleOptions options) {
|
||||
return withLogging("Page.isVisible", () -> isVisibleImpl(selector, options));
|
||||
return isVisibleImpl(selector, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -638,7 +594,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject json = sendMessage("isVisible", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("isVisible", params, timeout(options.timeout)).getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
}
|
||||
|
||||
@ -659,7 +615,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public void press(String selector, String key, PressOptions options) {
|
||||
withLogging("Frame.press", () -> pressImpl(selector, key, options));
|
||||
pressImpl(selector, key, options);
|
||||
}
|
||||
|
||||
void pressImpl(String selector, String key, PressOptions options) {
|
||||
@ -669,12 +625,12 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("key", key);
|
||||
sendMessage("press", params);
|
||||
sendMessage("press", params, timeout(options.timeout));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String selector, SelectOption[] values, SelectOptionOptions options) {
|
||||
return withLogging("Frame.selectOption", () -> selectOptionImpl(selector, values, options));
|
||||
return selectOptionImpl(selector, values, options);
|
||||
}
|
||||
|
||||
List<String> selectOptionImpl(String selector, SelectOption[] values, SelectOptionOptions options) {
|
||||
@ -686,7 +642,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
if (values != null) {
|
||||
params.add("options", gson().toJsonTree(values));
|
||||
}
|
||||
return selectOption(params);
|
||||
return selectOption(params, options.timeout);
|
||||
}
|
||||
|
||||
List<String> selectOptionImpl(String selector, String[] values, SelectOptionOptions options) {
|
||||
@ -698,12 +654,12 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
if (values != null) {
|
||||
params.add("options", toSelectValueOrLabel(values));
|
||||
}
|
||||
return selectOption(params);
|
||||
return selectOption(params, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String selector, ElementHandle[] values, SelectOptionOptions options) {
|
||||
return withLogging("Frame.selectOption", () -> selectOptionImpl(selector, values, options));
|
||||
return selectOptionImpl(selector, values, options);
|
||||
}
|
||||
|
||||
List<String> selectOptionImpl(String selector, ElementHandle[] values, SelectOptionOptions options) {
|
||||
@ -715,30 +671,35 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
if (values != null) {
|
||||
params.add("elements", Serialization.toProtocol(values));
|
||||
}
|
||||
return selectOption(params);
|
||||
return selectOption(params, options.timeout);
|
||||
}
|
||||
|
||||
private List<String> selectOption(JsonObject params) {
|
||||
JsonObject json = sendMessage("selectOption", params).getAsJsonObject();
|
||||
private List<String> selectOption(JsonObject params, Double timeout) {
|
||||
JsonObject json = sendMessage("selectOption", params, timeout(timeout)).getAsJsonObject();
|
||||
return parseStringList(json.getAsJsonArray("values"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChecked(String selector, boolean checked, SetCheckedOptions options) {
|
||||
withLogging("Frame.setChecked", () -> setCheckedImpl(selector, checked, options));
|
||||
setCheckedImpl(selector, checked, options);
|
||||
}
|
||||
|
||||
void setCheckedImpl(String selector, boolean checked, SetCheckedOptions options) {
|
||||
if (checked) {
|
||||
checkImpl(selector, convertType(options, CheckOptions.class));
|
||||
check(selector, convertType(options, CheckOptions.class));
|
||||
} else {
|
||||
uncheckImpl(selector, convertType(options, UncheckOptions.class));
|
||||
uncheck(selector, convertType(options, UncheckOptions.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContent(String html, SetContentOptions options) {
|
||||
withLogging("Frame.setContent", () -> setContentImpl(html, options));
|
||||
if (options == null) {
|
||||
options = new SetContentOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("html", html);
|
||||
sendMessage("setContent", params, navigationTimeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -746,18 +707,9 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
setInputFiles(selector, new Path[] {files}, options);
|
||||
}
|
||||
|
||||
void setContentImpl(String html, SetContentOptions options) {
|
||||
if (options == null) {
|
||||
options = new SetContentOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("html", html);
|
||||
sendMessage("setContent", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(String selector, Path[] files, SetInputFilesOptions options) {
|
||||
withLogging("Frame.setInputFiles", () -> setInputFilesImpl(selector, files, options));
|
||||
setInputFilesImpl(selector, files, options);
|
||||
}
|
||||
|
||||
void setInputFilesImpl(String selector, Path[] files, SetInputFilesOptions options) {
|
||||
@ -767,7 +719,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
addFilePathUploadParams(files, params, page.context());
|
||||
params.addProperty("selector", selector);
|
||||
sendMessage("setInputFiles", params);
|
||||
sendMessage("setInputFiles", params, timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -777,7 +729,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public void setInputFiles(String selector, FilePayload[] files, SetInputFilesOptions options) {
|
||||
withLogging("Frame.setInputFiles", () -> setInputFilesImpl(selector, files, options));
|
||||
setInputFilesImpl(selector, files, options);
|
||||
}
|
||||
|
||||
void setInputFilesImpl(String selector, FilePayload[] files, SetInputFilesOptions options) {
|
||||
@ -788,73 +740,54 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.add("payloads", toJsonArray(files));
|
||||
sendMessage("setInputFiles", params);
|
||||
sendMessage("setInputFiles", params, timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tap(String selector, TapOptions options) {
|
||||
withLogging("Frame.tap", () -> tapImpl(selector, options));
|
||||
}
|
||||
void tapImpl(String selector, TapOptions options) {
|
||||
if (options == null) {
|
||||
options = new TapOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
sendMessage("tap", params);
|
||||
sendMessage("tap", params, timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String textContent(String selector, TextContentOptions options) {
|
||||
return withLogging("Frame.textContent", () -> textContentImpl(selector, options));
|
||||
}
|
||||
|
||||
String textContentImpl(String selector, TextContentOptions options) {
|
||||
if (options == null) {
|
||||
options = new TextContentOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
return sendMessage("textContent", params).getAsJsonObject().get("value").getAsString();
|
||||
return sendMessage("textContent", params, timeout(options.timeout)).getAsJsonObject().get("value").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String title() {
|
||||
return withLogging("Frame.title", () -> titleImpl());
|
||||
}
|
||||
|
||||
String titleImpl() {
|
||||
JsonElement json = sendMessage("title");
|
||||
return json.getAsJsonObject().get("value").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void type(String selector, String text, TypeOptions options) {
|
||||
withLogging("Frame.type", () -> typeImpl(selector, text, options));
|
||||
}
|
||||
|
||||
void typeImpl(String selector, String text, TypeOptions options) {
|
||||
if (options == null) {
|
||||
options = new TypeOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("text", text);
|
||||
sendMessage("type", params);
|
||||
sendMessage("type", params, timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncheck(String selector, UncheckOptions options) {
|
||||
withLogging("Frame.uncheck", () -> uncheckImpl(selector, options));
|
||||
}
|
||||
|
||||
void uncheckImpl(String selector, UncheckOptions options) {
|
||||
if (options == null) {
|
||||
options = new UncheckOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
sendMessage("uncheck", params);
|
||||
sendMessage("uncheck", params, timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -864,17 +797,13 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public JSHandle waitForFunction(String pageFunction, Object arg, WaitForFunctionOptions options) {
|
||||
return withLogging("Frame.waitForFunction", () -> waitForFunctionImpl(pageFunction, arg, options));
|
||||
}
|
||||
|
||||
JSHandle waitForFunctionImpl(String pageFunction, Object arg, WaitForFunctionOptions options) {
|
||||
if (options == null) {
|
||||
options = new WaitForFunctionOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("waitForFunction", params);
|
||||
JsonElement json = sendMessage("waitForFunction", params, timeout(options.timeout));
|
||||
JsonObject element = json.getAsJsonObject().getAsJsonObject("handle");
|
||||
return connection.getExistingObject(element.get("guid").getAsString());
|
||||
}
|
||||
@ -1031,7 +960,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
List<Waitable<Response>> waitables = new ArrayList<>();
|
||||
if (matcher == null) {
|
||||
matcher = UrlMatcher.forOneOf(page.context().baseUrl, options.url);
|
||||
matcher = UrlMatcher.forOneOf(page.context().baseUrl(), options.url, this.connection.localUtils, false);
|
||||
}
|
||||
logger.log("waiting for navigation " + matcher);
|
||||
waitables.add(new WaitForNavigationHelper(matcher, options.waitUntil, logger));
|
||||
@ -1043,10 +972,6 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public ElementHandle waitForSelector(String selector, WaitForSelectorOptions options) {
|
||||
return withLogging("Frame.waitForSelector", () -> waitForSelectorImpl(selector, options));
|
||||
}
|
||||
|
||||
ElementHandle waitForSelectorImpl(String selector, WaitForSelectorOptions options) {
|
||||
return waitForSelectorImpl(selector, options, false);
|
||||
}
|
||||
|
||||
@ -1057,7 +982,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("omitReturnValue", omitReturnValue);
|
||||
JsonElement json = sendMessage("waitForSelector", params);
|
||||
JsonElement json = sendMessage("waitForSelector", params, timeout(options.timeout));
|
||||
JsonObject element = json.getAsJsonObject().getAsJsonObject("element");
|
||||
if (element == null) {
|
||||
return null;
|
||||
@ -1067,18 +992,14 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public void waitForTimeout(double timeout) {
|
||||
withLogging("Frame.waitForTimeout", () -> waitForTimeoutImpl(timeout));
|
||||
}
|
||||
|
||||
void waitForTimeoutImpl(double timeout) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("timeout", timeout);
|
||||
sendMessage("waitForTimeout", params);
|
||||
params.addProperty("waitTimeout", timeout);
|
||||
sendMessage("waitForTimeout", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waitForURL(String url, WaitForURLOptions options) {
|
||||
waitForURL(new UrlMatcher(page.context().baseUrl, url), options);
|
||||
waitForURL(UrlMatcher.forGlob(page.context().baseUrl(), url, this.connection.localUtils, false), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1113,14 +1034,14 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
int queryCount(String selector) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject result = sendMessage("queryCount", params).getAsJsonObject();
|
||||
JsonObject result = sendMessage("queryCount", params, NO_TIMEOUT).getAsJsonObject();
|
||||
return result.get("value").getAsInt();
|
||||
}
|
||||
|
||||
void highlightImpl(String selector) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
sendMessage("highlight", params);
|
||||
sendMessage("highlight", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
protected void handleEvent(String event, JsonObject params) {
|
||||
@ -1151,4 +1072,30 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
internalListeners.notify(InternalEventType.NAVIGATED, params);
|
||||
}
|
||||
}
|
||||
|
||||
protected double timeout(Double timeout) {
|
||||
if (page != null) {
|
||||
return page.timeoutSettings.timeout(timeout);
|
||||
}
|
||||
return new TimeoutSettings().timeout(timeout);
|
||||
}
|
||||
|
||||
protected double navigationTimeout(Double timeout) {
|
||||
if (page != null) {
|
||||
return page.timeoutSettings.navigationTimeout(timeout);
|
||||
}
|
||||
return new TimeoutSettings().navigationTimeout(timeout);
|
||||
}
|
||||
|
||||
FrameExpectResult expect(String expression, FrameExpectOptions options, String title) {
|
||||
return withTitle(title, () -> expect(expression, options));
|
||||
}
|
||||
|
||||
FrameExpectResult expect(String expression, FrameExpectOptions options) {
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("expression", expression);
|
||||
JsonElement json = sendMessage("expect", params, options.timeout);
|
||||
FrameExpectResult result = gson().fromJson(json, FrameExpectResult.class);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import java.nio.file.Path;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.microsoft.playwright.impl.ChannelOwner.NO_TIMEOUT;
|
||||
import static com.microsoft.playwright.impl.LoggingSupport.*;
|
||||
import static com.microsoft.playwright.impl.Serialization.fromNameValues;
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
@ -41,7 +42,7 @@ public class HARRouter {
|
||||
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("file", harFile.toString());
|
||||
JsonObject json = localUtils.sendMessage("harOpen", params).getAsJsonObject();
|
||||
JsonObject json = localUtils.sendMessage("harOpen", params, NO_TIMEOUT).getAsJsonObject();
|
||||
if (json.has("error")) {
|
||||
throw new PlaywrightException(json.get("error").getAsString());
|
||||
}
|
||||
@ -61,7 +62,7 @@ public class HARRouter {
|
||||
params.addProperty("postData", base64);
|
||||
}
|
||||
params.addProperty("isNavigationRequest", request.isNavigationRequest());
|
||||
JsonObject response = localUtils.sendMessage("harLookup", params).getAsJsonObject();
|
||||
JsonObject response = localUtils.sendMessage("harLookup", params, NO_TIMEOUT).getAsJsonObject();
|
||||
|
||||
String action = response.get("action").getAsString();
|
||||
if ("redirect".equals(action)) {
|
||||
|
@ -41,70 +41,58 @@ public class JSHandleImpl extends ChannelOwner implements JSHandle {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
withLogging("JSHandle.dispose", () -> {
|
||||
try {
|
||||
sendMessage("dispose");
|
||||
} catch (TargetClosedError e) {
|
||||
}
|
||||
});
|
||||
try {
|
||||
sendMessage("dispose");
|
||||
} catch (TargetClosedError e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evaluate(String pageFunction, Object arg) {
|
||||
return withLogging("JSHandle.evaluate", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.addProperty("world", "main");
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpression", params);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.addProperty("world", "main");
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpression", params, NO_TIMEOUT);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSHandle evaluateHandle(String pageFunction, Object arg) {
|
||||
return withLogging("JSHandle.evaluateHandle", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.addProperty("world", "main");
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpressionHandle", params);
|
||||
return connection.getExistingObject(json.getAsJsonObject().getAsJsonObject("handle").get("guid").getAsString());
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.addProperty("world", "main");
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpressionHandle", params, NO_TIMEOUT);
|
||||
return connection.getExistingObject(json.getAsJsonObject().getAsJsonObject("handle").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, JSHandle> getProperties() {
|
||||
return withLogging("JSHandle.getProperties", () -> {
|
||||
JsonObject json = sendMessage("getPropertyList").getAsJsonObject();
|
||||
Map<String, JSHandle> result = new HashMap<>();
|
||||
for (JsonElement e : json.getAsJsonArray("properties")) {
|
||||
JsonObject item = e.getAsJsonObject();
|
||||
JSHandle value = connection.getExistingObject(item.getAsJsonObject("value").get("guid").getAsString());
|
||||
result.put(item.get("name").getAsString(), value);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
JsonObject json = sendMessage("getPropertyList").getAsJsonObject();
|
||||
Map<String, JSHandle> result = new HashMap<>();
|
||||
for (JsonElement e : json.getAsJsonArray("properties")) {
|
||||
JsonObject item = e.getAsJsonObject();
|
||||
JSHandle value = connection.getExistingObject(item.getAsJsonObject("value").get("guid").getAsString());
|
||||
result.put(item.get("name").getAsString(), value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSHandle getProperty(String propertyName) {
|
||||
return withLogging("JSHandle.getProperty", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("name", propertyName);
|
||||
JsonObject json = sendMessage("getProperty", params).getAsJsonObject();
|
||||
return connection.getExistingObject(json.getAsJsonObject("handle").get("guid").getAsString());
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("name", propertyName);
|
||||
JsonObject json = sendMessage("getProperty", params, NO_TIMEOUT).getAsJsonObject();
|
||||
return connection.getExistingObject(json.getAsJsonObject("handle").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object jsonValue() {
|
||||
return withLogging("JSHandle.jsonValue", () -> {
|
||||
JsonObject json = sendMessage("jsonValue").getAsJsonObject();
|
||||
SerializedValue value = gson().fromJson(json.get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
});
|
||||
JsonObject json = sendMessage("jsonValue").getAsJsonObject();
|
||||
SerializedValue value = gson().fromJson(json.get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -44,7 +44,7 @@ class JsonPipe extends ChannelOwner implements Transport {
|
||||
checkIfClosed();
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("message", message);
|
||||
sendMessage("send", params);
|
||||
sendMessage("send", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -19,6 +19,7 @@ package com.microsoft.playwright.impl;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Keyboard;
|
||||
|
||||
import static com.microsoft.playwright.impl.ChannelOwner.NO_TIMEOUT;
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
|
||||
class KeyboardImpl implements Keyboard {
|
||||
@ -30,25 +31,21 @@ class KeyboardImpl implements Keyboard {
|
||||
|
||||
@Override
|
||||
public void down(String key) {
|
||||
page.withLogging("Keyboard.down", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("key", key);
|
||||
page.sendMessage("keyboardDown", params);
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("key", key);
|
||||
page.sendMessage("keyboardDown", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertText(String text) {
|
||||
page.withLogging("Keyboard.insertText", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("text", text);
|
||||
page.sendMessage("keyboardInsertText", params);
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("text", text);
|
||||
page.sendMessage("keyboardInsertText", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void press(String key, PressOptions options) {
|
||||
page.withLogging("Keyboard.press", () -> pressImpl(key, options));
|
||||
pressImpl(key, options);
|
||||
}
|
||||
|
||||
private void pressImpl(String key, PressOptions options) {
|
||||
@ -57,12 +54,12 @@ class KeyboardImpl implements Keyboard {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("key", key);
|
||||
page.sendMessage("keyboardPress", params);
|
||||
page.sendMessage("keyboardPress", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void type(String text, TypeOptions options) {
|
||||
page.withLogging("Keyboard.type", () -> typeImpl(text, options));
|
||||
typeImpl(text, options);
|
||||
}
|
||||
|
||||
private void typeImpl(String text, TypeOptions options) {
|
||||
@ -71,15 +68,13 @@ class KeyboardImpl implements Keyboard {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("text", text);
|
||||
page.sendMessage("keyboardType", params);
|
||||
page.sendMessage("keyboardType", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void up(String key) {
|
||||
page.withLogging("Keyboard.up", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("key", key);
|
||||
page.sendMessage("keyboardUp", params);
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("key", key);
|
||||
page.sendMessage("keyboardUp", params, NO_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
@ -21,13 +21,13 @@ import com.google.gson.JsonObject;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
|
||||
class LocalUtils extends ChannelOwner {
|
||||
public class LocalUtils extends ChannelOwner {
|
||||
LocalUtils(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
markAsInternalType();
|
||||
}
|
||||
|
||||
JsonArray deviceDescriptors() {
|
||||
@ -41,13 +41,13 @@ class LocalUtils extends ChannelOwner {
|
||||
params.addProperty("mode", appendMode ? "append" : "write");
|
||||
params.addProperty("stacksId", stacksId);
|
||||
params.addProperty("includeSources", includeSources);
|
||||
sendMessage("zip", params);
|
||||
sendMessage("zip", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
void traceDiscarded(String stacksId) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("stacksId", stacksId);
|
||||
sendMessage("traceDiscarded", params);
|
||||
sendMessage("traceDiscarded", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
String tracingStarted(String tracesDir, String traceName) {
|
||||
@ -56,7 +56,19 @@ class LocalUtils extends ChannelOwner {
|
||||
params.addProperty("tracesDir", "");
|
||||
}
|
||||
params.addProperty("traceName", traceName);
|
||||
JsonObject json = connection.localUtils().sendMessage("tracingStarted", params).getAsJsonObject();
|
||||
JsonObject json = connection.localUtils().sendMessage("tracingStarted", params, NO_TIMEOUT).getAsJsonObject();
|
||||
return json.get("stacksId").getAsString();
|
||||
}
|
||||
|
||||
public Pattern globToRegex(String glob, String baseURL, boolean webSocketUrl) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("glob", glob);
|
||||
if (baseURL != null) {
|
||||
params.addProperty("baseURL", baseURL);
|
||||
}
|
||||
params.addProperty("webSocketUrl", webSocketUrl);
|
||||
JsonObject json = connection.localUtils().sendMessage("globToRegex", params, NO_TIMEOUT).getAsJsonObject();
|
||||
String regex = json.get("regex").getAsString();
|
||||
return Pattern.compile(regex);
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ import com.microsoft.playwright.Locator;
|
||||
import com.microsoft.playwright.assertions.LocatorAssertions;
|
||||
import com.microsoft.playwright.options.AriaRole;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -31,12 +30,39 @@ import static com.microsoft.playwright.impl.Serialization.serializeArgument;
|
||||
import static com.microsoft.playwright.impl.Utils.convertType;
|
||||
|
||||
public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAssertions {
|
||||
LocatorImpl actualLocator;
|
||||
|
||||
public LocatorAssertionsImpl(Locator locator) {
|
||||
this(locator, false);
|
||||
}
|
||||
|
||||
private LocatorAssertionsImpl(Locator locator, boolean isNot) {
|
||||
super((LocatorImpl) locator, isNot);
|
||||
super(isNot);
|
||||
this.actualLocator = (LocatorImpl) locator;
|
||||
}
|
||||
|
||||
@Override
|
||||
FrameExpectResult doExpect(String expression, FrameExpectOptions expectOptions, String title) {
|
||||
return actualLocator.expect(expression, expectOptions, title);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void containsClass(String classname, ContainsClassOptions options) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = classname;
|
||||
expectImpl("to.contain.class", expected, classname, "Locator expected to contain class", convertType(options, FrameExpectOptions.class), "Assert \"containsClass\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsClass(List<String> classnames, ContainsClassOptions options) {
|
||||
List<ExpectedTextValue> list = new ArrayList<>();
|
||||
for (String text : classnames) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = text;
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.contain.class.array", list, classnames, "Locator expected to contain classes", convertType(options, FrameExpectOptions.class), "Assert \"containsClass\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -46,7 +72,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.matchSubstring = true;
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.text", expected, text, "Locator expected to contain text", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.text", expected, text, "Locator expected to contain text", convertType(options, FrameExpectOptions.class), "Assert \"containsText\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -55,7 +81,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.matchSubstring = true;
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.text", expected, pattern, "Locator expected to contain regex", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.text", expected, pattern, "Locator expected to contain regex", convertType(options, FrameExpectOptions.class), "Assert \"containsText\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -69,7 +95,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.normalizeWhiteSpace = true;
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.contain.text.array", list, strings, "Locator expected to contain text", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.contain.text.array", list, strings, "Locator expected to contain text", convertType(options, FrameExpectOptions.class), "Assert \"containsText\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -82,7 +108,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.normalizeWhiteSpace = true;
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.contain.text.array", list, patterns, "Locator expected to contain text", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.contain.text.array", list, patterns, "Locator expected to contain text", convertType(options, FrameExpectOptions.class), "Assert \"containsText\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -91,7 +117,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.string = description;
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.accessible.description", expected, description, "Locator expected to have accessible description", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.accessible.description", expected, description, "Locator expected to have accessible description", convertType(options, FrameExpectOptions.class), "Assert \"hasAccessibleDescription\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -99,7 +125,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.accessible.description", expected, pattern, "Locator expected to have accessible description", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.accessible.description", expected, pattern, "Locator expected to have accessible description", convertType(options, FrameExpectOptions.class), "Assert \"hasAccessibleDescription\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -108,7 +134,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.string = errorMessage;
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.accessible.error.message", expected, errorMessage, "Locator expected to have accessible error message", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.accessible.error.message", expected, errorMessage, "Locator expected to have accessible error message", convertType(options, FrameExpectOptions.class), "Assert \"hasAccessibleErrorMessage\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -116,7 +142,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.accessible.error.message", expected, pattern, "Locator expected to have accessible error message", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.accessible.error.message", expected, pattern, "Locator expected to have accessible error message", convertType(options, FrameExpectOptions.class), "Assert \"hasAccessibleErrorMessage\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -125,7 +151,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.string = name;
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.accessible.name", expected, name, "Locator expected to have accessible name", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.accessible.name", expected, name, "Locator expected to have accessible name", convertType(options, FrameExpectOptions.class), "Assert \"hasAccessibleName\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -133,7 +159,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.accessible.name", expected, pattern, "Locator expected to have accessible name", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.accessible.name", expected, pattern, "Locator expected to have accessible name", convertType(options, FrameExpectOptions.class), "Assert \"hasAccessibleName\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -161,20 +187,20 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
if (expectedValue instanceof Pattern) {
|
||||
message += " matching regex";
|
||||
}
|
||||
expectImpl("to.have.attribute.value", expectedText, expectedValue, message, commonOptions);
|
||||
expectImpl("to.have.attribute.value", expectedText, expectedValue, message, commonOptions, "Assert \"hasAttribute\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasClass(String text, HasClassOptions options) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = text;
|
||||
expectImpl("to.have.class", expected, text, "Locator expected to have class", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.class", expected, text, "Locator expected to have class", convertType(options, FrameExpectOptions.class), "Assert \"hasClass\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasClass(Pattern pattern, HasClassOptions options) {
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
expectImpl("to.have.class", expected, pattern, "Locator expected to have class matching regex", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.class", expected, pattern, "Locator expected to have class matching regex", convertType(options, FrameExpectOptions.class), "Assert \"hasClass\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -185,7 +211,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.string = text;
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.have.class.array", list, strings, "Locator expected to have class", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.class.array", list, strings, "Locator expected to have class", convertType(options, FrameExpectOptions.class), "Assert \"hasClass\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -195,7 +221,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.have.class.array", list, patterns, "Locator expected to have class matching regex", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.class.array", list, patterns, "Locator expected to have class matching regex", convertType(options, FrameExpectOptions.class), "Assert \"hasClass\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -206,7 +232,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
FrameExpectOptions commonOptions = convertType(options, FrameExpectOptions.class);
|
||||
commonOptions.expectedNumber = (double) count;
|
||||
List<ExpectedTextValue> expectedText = null;
|
||||
expectImpl("to.have.count", expectedText, count, "Locator expected to have count", commonOptions);
|
||||
expectImpl("to.have.count", expectedText, count, "Locator expected to have count", commonOptions, "Assert \"hasCount\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -232,20 +258,20 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
if (expectedValue instanceof Pattern) {
|
||||
message += " matching regex";
|
||||
}
|
||||
expectImpl("to.have.css", expectedText, expectedValue, message, commonOptions);
|
||||
expectImpl("to.have.css", expectedText, expectedValue, message, commonOptions, "Assert \"hasCSS\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasId(String id, HasIdOptions options) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = id;
|
||||
expectImpl("to.have.id", expected, id, "Locator expected to have ID", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.id", expected, id, "Locator expected to have ID", convertType(options, FrameExpectOptions.class), "Assert \"hasId\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasId(Pattern pattern, HasIdOptions options) {
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
expectImpl("to.have.id", expected, pattern, "Locator expected to have ID matching regex", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.id", expected, pattern, "Locator expected to have ID matching regex", convertType(options, FrameExpectOptions.class), "Assert \"hasId\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -257,14 +283,14 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
commonOptions.expressionArg = name;
|
||||
commonOptions.expectedValue = serializeArgument(value);
|
||||
List<ExpectedTextValue> list = null;
|
||||
expectImpl("to.have.property", list, value, "Locator expected to have JavaScript property '" + name + "'", commonOptions);
|
||||
expectImpl("to.have.property", list, value, "Locator expected to have JavaScript property '" + name + "'", commonOptions, "Assert \"hasJSProperty\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasRole(AriaRole role, HasRoleOptions options) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = role.toString().toLowerCase();
|
||||
expectImpl("to.have.role", expected, expected.string, "Locator expected to have role", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.role", expected, expected.string, "Locator expected to have role", convertType(options, FrameExpectOptions.class), "Assert \"hasRole\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -274,7 +300,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.matchSubstring = false;
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.text", expected, text, "Locator expected to have text", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.text", expected, text, "Locator expected to have text", convertType(options, FrameExpectOptions.class), "Assert \"hasText\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -284,7 +310,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
// Just match substring, same as containsText.
|
||||
expected.matchSubstring = true;
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.text", expected, pattern, "Locator expected to have text matching regex", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.text", expected, pattern, "Locator expected to have text matching regex", convertType(options, FrameExpectOptions.class), "Assert \"hasText\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -298,7 +324,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.normalizeWhiteSpace = true;
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.have.text.array", list, strings, "Locator expected to have text", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.text.array", list, strings, "Locator expected to have text", convertType(options, FrameExpectOptions.class), "Assert \"hasText\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -311,20 +337,20 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.normalizeWhiteSpace = true;
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.have.text.array", list, patterns, "Locator expected to have text matching regex", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.text.array", list, patterns, "Locator expected to have text matching regex", convertType(options, FrameExpectOptions.class), "Assert \"hasText\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasValue(String value, HasValueOptions options) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = value;
|
||||
expectImpl("to.have.value", expected, value, "Locator expected to have value", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.value", expected, value, "Locator expected to have value", convertType(options, FrameExpectOptions.class), "Assert \"hasValue\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasValue(Pattern pattern, HasValueOptions options) {
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
expectImpl("to.have.value", expected, pattern, "Locator expected to have value matching regex", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.value", expected, pattern, "Locator expected to have value matching regex", convertType(options, FrameExpectOptions.class), "Assert \"hasValue\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -335,7 +361,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.string = text;
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.have.values", list, values, "Locator expected to have values", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.values", list, values, "Locator expected to have values", convertType(options, FrameExpectOptions.class), "Assert \"hasValues\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -346,7 +372,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.matchSubstring = true;
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.have.values", list, patterns, "Locator expected to have values matching regex", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.values", list, patterns, "Locator expected to have values matching regex", convertType(options, FrameExpectOptions.class), "Assert \"hasValues\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -356,7 +382,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
}
|
||||
FrameExpectOptions options = convertType(snapshotOptions, FrameExpectOptions.class);
|
||||
options.expectedValue = serializeArgument(expected);
|
||||
expectImpl("to.match.aria", options, expected,"Locator expected to match Aria snapshot");
|
||||
expectImpl("to.match.aria", options, expected,"Locator expected to match Aria snapshot", "Assert \"matchesAriaSnapshot\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -384,12 +410,12 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
String message = "Locator expected to be";
|
||||
FrameExpectOptions expectOptions = convertType(options, FrameExpectOptions.class);
|
||||
expectOptions.expectedValue = serializeArgument(expectedValue);
|
||||
expectImpl("to.be.checked", expectOptions, expected, message);
|
||||
expectImpl("to.be.checked", expectOptions, expected, message, "Assert \"isChecked\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isDisabled(IsDisabledOptions options) {
|
||||
expectTrue("to.be.disabled", "Locator expected to be disabled", convertType(options, FrameExpectOptions.class));
|
||||
expectTrue("to.be.disabled", "Locator expected to be disabled", convertType(options, FrameExpectOptions.class), "Assert \"isDisabled\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -397,12 +423,12 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
FrameExpectOptions frameOptions = convertType(options, FrameExpectOptions.class);
|
||||
boolean editable = options == null || options.editable == null || options.editable == true;
|
||||
String message = "Locator expected to be " + (editable ? "editable" : "readonly");
|
||||
expectTrue(editable ? "to.be.editable" : "to.be.readonly", message, frameOptions);
|
||||
expectTrue(editable ? "to.be.editable" : "to.be.readonly", message, frameOptions, "Assert \"isEditable\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isEmpty(IsEmptyOptions options) {
|
||||
expectTrue("to.be.empty", "Locator expected to be empty", convertType(options, FrameExpectOptions.class));
|
||||
expectTrue("to.be.empty", "Locator expected to be empty", convertType(options, FrameExpectOptions.class), "Assert \"isEmpty\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -410,17 +436,17 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
FrameExpectOptions frameOptions = convertType(options, FrameExpectOptions.class);
|
||||
boolean enabled = options == null || options.enabled == null || options.enabled == true;
|
||||
String message = "Locator expected to be " + (enabled ? "enabled" : "disabled");
|
||||
expectTrue(enabled ? "to.be.enabled" : "to.be.disabled", message, frameOptions);
|
||||
expectTrue(enabled ? "to.be.enabled" : "to.be.disabled", message, frameOptions, "Assert \"isEnabled\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isFocused(IsFocusedOptions options) {
|
||||
expectTrue("to.be.focused", "Locator expected to be focused", convertType(options, FrameExpectOptions.class));
|
||||
expectTrue("to.be.focused", "Locator expected to be focused", convertType(options, FrameExpectOptions.class), "Assert \"isFocused\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isHidden(IsHiddenOptions options) {
|
||||
expectTrue("to.be.hidden", "Locator expected to be hidden", convertType(options, FrameExpectOptions.class));
|
||||
expectTrue("to.be.hidden", "Locator expected to be hidden", convertType(options, FrameExpectOptions.class), "Assert \"isHidden\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -429,7 +455,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
if (options != null && options.ratio != null) {
|
||||
expectOptions.expectedNumber = options.ratio;
|
||||
}
|
||||
expectTrue("to.be.in.viewport", "Locator expected to be in viewport", expectOptions);
|
||||
expectTrue("to.be.in.viewport", "Locator expected to be in viewport", expectOptions, "Assert \"isInViewport\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -437,12 +463,12 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
FrameExpectOptions frameOptions = convertType(options, FrameExpectOptions.class);
|
||||
boolean visible = options == null || options.visible == null || options.visible == true;
|
||||
String message = "Locator expected to be " + (visible ? "visible" : "hidden");
|
||||
expectTrue(visible ? "to.be.visible" : "to.be.hidden", message, frameOptions);
|
||||
expectTrue(visible ? "to.be.visible" : "to.be.hidden", message, frameOptions, "Assert \"isVisible\"");
|
||||
}
|
||||
|
||||
private void expectTrue(String expression, String message, FrameExpectOptions options) {
|
||||
private void expectTrue(String expression, String message, FrameExpectOptions options, String title) {
|
||||
List<ExpectedTextValue> expectedText = null;
|
||||
expectImpl(expression, expectedText, null, message, options);
|
||||
expectImpl(expression, expectedText, null, message, options, title);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -455,6 +481,6 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
FrameExpectOptions frameOptions = convertType(options, FrameExpectOptions.class);
|
||||
boolean attached = options == null || options.attached == null || options.attached == true;
|
||||
String message = "Locator expected to be " + (attached ? "attached" : "detached");
|
||||
expectTrue(attached ? "to.be.attached" : "to.be.detached", message, frameOptions);
|
||||
expectTrue(attached ? "to.be.attached" : "to.be.detached", message, frameOptions, "Assert \"isAttached\"");
|
||||
}
|
||||
}
|
||||
|
@ -67,27 +67,25 @@ class LocatorImpl implements Locator {
|
||||
this.selector = selector;
|
||||
}
|
||||
|
||||
private static String escapeWithQuotes(String text) {
|
||||
return gson().toJson(text);
|
||||
}
|
||||
|
||||
private <R, O> R withElement(BiFunction<ElementHandle, O, R> callback, O options) {
|
||||
ElementHandleOptions handleOptions = convertType(options, ElementHandleOptions.class);
|
||||
// TODO: support deadline based timeout
|
||||
// Double timeout = null;
|
||||
// if (handleOptions != null) {
|
||||
// timeout = handleOptions.timeout;
|
||||
// }
|
||||
// timeout = frame.page.timeoutSettings.timeout(timeout);
|
||||
// long deadline = System.nanoTime() + (long) timeout.doubleValue() * 1_000_000;
|
||||
ElementHandle handle = elementHandle(handleOptions);
|
||||
try {
|
||||
return callback.apply(handle, options);
|
||||
} finally {
|
||||
if (handle != null) {
|
||||
handle.dispose();
|
||||
private <R, O> R withElement(BiFunction<ElementHandle, O, R> callback, O options, String title) {
|
||||
return frame.withTitle(title, () -> {
|
||||
ElementHandleOptions handleOptions = convertType(options, ElementHandleOptions.class);
|
||||
// TODO: support deadline based timeout
|
||||
// Double timeout = null;
|
||||
// if (handleOptions != null) {
|
||||
// timeout = handleOptions.timeout;
|
||||
// }
|
||||
// timeout = frame.page.timeoutSettings.timeout(timeout);
|
||||
// long deadline = System.nanoTime() + (long) timeout.doubleValue() * 1_000_000;
|
||||
ElementHandle handle = elementHandle(handleOptions);
|
||||
try {
|
||||
return callback.apply(handle, options);
|
||||
} finally {
|
||||
if (handle != null) {
|
||||
handle.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -120,37 +118,29 @@ class LocatorImpl implements Locator {
|
||||
|
||||
@Override
|
||||
public String ariaSnapshot(AriaSnapshotOptions options) {
|
||||
return frame.withLogging("Locator.ariaSnapshot", () -> ariaSnapshotImpl(options));
|
||||
}
|
||||
|
||||
private String ariaSnapshotImpl(AriaSnapshotOptions options) {
|
||||
if (options == null) {
|
||||
options = new AriaSnapshotOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject result = frame.sendMessage("ariaSnapshot", params).getAsJsonObject();
|
||||
JsonObject result = frame.sendMessage("ariaSnapshot", params, frame.timeout(options.timeout)).getAsJsonObject();
|
||||
return result.get("snapshot").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blur(BlurOptions options) {
|
||||
frame.withLogging("Locator.blur", () -> blurImpl(options));
|
||||
}
|
||||
|
||||
private void blurImpl(BlurOptions options) {
|
||||
if (options == null) {
|
||||
options = new BlurOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("strict", true);
|
||||
frame.sendMessage("blur", params);
|
||||
frame.sendMessage("blur", params, frame.timeout(options.timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoundingBox boundingBox(BoundingBoxOptions options) {
|
||||
return withElement((h, o) -> h.boundingBox(), options);
|
||||
return withElement((h, o) -> h.boundingBox(), options, "Bounding Box");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -163,7 +153,7 @@ class LocatorImpl implements Locator {
|
||||
|
||||
@Override
|
||||
public void clear(ClearOptions options) {
|
||||
fill("", convertType(options, FillOptions.class));
|
||||
frame.withTitle("Clear", () -> fill("", convertType(options, FillOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -179,6 +169,11 @@ class LocatorImpl implements Locator {
|
||||
return frame.queryCount(selector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator describe(String description) {
|
||||
return locator(describeSelector(description));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dblclick(DblclickOptions options) {
|
||||
if (options == null) {
|
||||
@ -228,7 +223,7 @@ class LocatorImpl implements Locator {
|
||||
|
||||
@Override
|
||||
public Object evaluate(String expression, Object arg, EvaluateOptions options) {
|
||||
return withElement((h, o) -> h.evaluate(expression, arg), options);
|
||||
return withElement((h, o) -> h.evaluate(expression, arg), options, "Evaluate");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -238,7 +233,7 @@ class LocatorImpl implements Locator {
|
||||
|
||||
@Override
|
||||
public JSHandle evaluateHandle(String expression, Object arg, EvaluateHandleOptions options) {
|
||||
return withElement((h, o) -> h.evaluateHandle(expression, arg), options);
|
||||
return withElement((h, o) -> h.evaluateHandle(expression, arg), options, "Evaluate");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -483,7 +478,7 @@ class LocatorImpl implements Locator {
|
||||
|
||||
@Override
|
||||
public byte[] screenshot(ScreenshotOptions options) {
|
||||
return withElement((h, o) -> h.screenshot(o), convertType(options, ElementHandle.ScreenshotOptions.class));
|
||||
return withElement((h, o) -> h.screenshot(o), convertType(options, ElementHandle.ScreenshotOptions.class), "Screenshot");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -491,7 +486,7 @@ class LocatorImpl implements Locator {
|
||||
withElement((h, o) -> {
|
||||
h.scrollIntoViewIfNeeded(o);
|
||||
return null;
|
||||
}, convertType(options, ElementHandle.ScrollIntoViewIfNeededOptions.class));
|
||||
}, convertType(options, ElementHandle.ScrollIntoViewIfNeededOptions.class), "Scroll into view");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -547,7 +542,7 @@ class LocatorImpl implements Locator {
|
||||
withElement((h, o) -> {
|
||||
h.selectText(o);
|
||||
return null;
|
||||
}, convertType(options, ElementHandle.SelectTextOptions.class));
|
||||
}, convertType(options, ElementHandle.SelectTextOptions.class), "Select text");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -627,11 +622,7 @@ class LocatorImpl implements Locator {
|
||||
if (options == null) {
|
||||
options = new WaitForOptions();
|
||||
}
|
||||
waitForImpl(options);
|
||||
}
|
||||
|
||||
private void waitForImpl(WaitForOptions options) {
|
||||
frame.withLogging("Locator.waitFor", () -> frame.waitForSelectorImpl(selector, convertType(options, Frame.WaitForSelectorOptions.class).setStrict(true), true));
|
||||
frame.waitForSelectorImpl(selector, convertType(options, Frame.WaitForSelectorOptions.class).setStrict(true), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -653,8 +644,9 @@ class LocatorImpl implements Locator {
|
||||
return frame.hashCode() ^ selector.hashCode();
|
||||
}
|
||||
|
||||
FrameExpectResult expect(String expression, FrameExpectOptions options) {
|
||||
return frame.withLogging("Locator.expect", () -> expectImpl(expression, options));
|
||||
FrameExpectResult expect(String expression, FrameExpectOptions options, String title) {
|
||||
options.selector = selector;
|
||||
return frame.expect(expression, options, title);
|
||||
}
|
||||
|
||||
JsonObject toProtocol() {
|
||||
@ -663,13 +655,4 @@ class LocatorImpl implements Locator {
|
||||
result.addProperty("selector", selector);
|
||||
return result;
|
||||
}
|
||||
|
||||
private FrameExpectResult expectImpl(String expression, FrameExpectOptions options) {
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("expression", expression);
|
||||
JsonElement json = frame.sendMessage("expect", params);
|
||||
FrameExpectResult result = gson().fromJson(json, FrameExpectResult.class);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -39,9 +39,12 @@ public class LocatorUtils {
|
||||
return "internal:attr=[" + attrName + "=" + escapeForAttributeSelector(value, exact) + "]";
|
||||
}
|
||||
|
||||
static String describeSelector(String description) {
|
||||
return "internal:describe=" + gson().toJson(description);
|
||||
}
|
||||
|
||||
static String getByTestIdSelector(Object testId, PlaywrightImpl playwright) {
|
||||
String testIdAttributeName = ((SharedSelectors) playwright.selectors()).testIdAttributeName;
|
||||
return getByAttributeTextSelector(testIdAttributeName, testId, true);
|
||||
return getByAttributeTextSelector(playwright.selectors.testIdAttributeName, testId, true);
|
||||
}
|
||||
|
||||
static String getByAltTextSelector(Object text, Locator.GetByAltTextOptions options) {
|
||||
|
@ -19,7 +19,6 @@ package com.microsoft.playwright.impl;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
class LoggingSupport {
|
||||
private static final boolean isEnabled;
|
||||
@ -31,29 +30,6 @@ class LoggingSupport {
|
||||
private static final DateTimeFormatter timestampFormat = DateTimeFormatter.ofPattern(
|
||||
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX").withZone(ZoneId.of("UTC"));
|
||||
|
||||
void withLogging(String apiName, Runnable code) {
|
||||
withLogging(apiName, () -> {
|
||||
code.run();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
<T> T withLogging(String apiName, Supplier<T> code) {
|
||||
if (isEnabled) {
|
||||
logApi("=> " + apiName + " started");
|
||||
}
|
||||
boolean success = false;
|
||||
try {
|
||||
T result = code.get();
|
||||
success = true;
|
||||
return result;
|
||||
} finally {
|
||||
if (isEnabled) {
|
||||
logApi("<= " + apiName + (success ? " succeeded" : " failed"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void logWithTimestamp(String message) {
|
||||
// This matches log format produced by the server.
|
||||
String timestamp = ZonedDateTime.now().format(timestampFormat);
|
||||
|
@ -19,6 +19,7 @@ package com.microsoft.playwright.impl;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Mouse;
|
||||
|
||||
import static com.microsoft.playwright.impl.ChannelOwner.NO_TIMEOUT;
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
import static com.microsoft.playwright.impl.Utils.convertType;
|
||||
|
||||
@ -31,22 +32,18 @@ class MouseImpl implements Mouse {
|
||||
|
||||
@Override
|
||||
public void click(double x, double y, ClickOptions options) {
|
||||
page.withLogging("Mouse.click", () -> clickImpl(x, y, options));
|
||||
}
|
||||
|
||||
private void clickImpl(double x, double y, ClickOptions options) {
|
||||
if (options == null) {
|
||||
options = new ClickOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("x", x);
|
||||
params.addProperty("y", y);
|
||||
page.sendMessage("mouseClick", params);
|
||||
page.sendMessage("mouseClick", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dblclick(double x, double y, DblclickOptions options) {
|
||||
page.withLogging("Mouse.dblclick", () -> dblclickImpl(x, y, options));
|
||||
page.withTitle("Double click", () -> dblclickImpl(x, y, options));
|
||||
}
|
||||
|
||||
private void dblclickImpl(double x, double y, DblclickOptions options) {
|
||||
@ -62,52 +59,38 @@ class MouseImpl implements Mouse {
|
||||
|
||||
@Override
|
||||
public void down(DownOptions options) {
|
||||
page.withLogging("Mouse.down", () -> downImpl(options));
|
||||
}
|
||||
|
||||
private void downImpl(DownOptions options) {
|
||||
if (options == null) {
|
||||
options = new DownOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
page.sendMessage("mouseDown", params);
|
||||
page.sendMessage("mouseDown", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void move(double x, double y, MoveOptions options) {
|
||||
page.withLogging("Mouse.move", () -> moveImpl(x, y, options));
|
||||
}
|
||||
|
||||
private void moveImpl(double x, double y, MoveOptions options) {
|
||||
if (options == null) {
|
||||
options = new MoveOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("x", x);
|
||||
params.addProperty("y", y);
|
||||
page.sendMessage("mouseMove", params);
|
||||
page.sendMessage("mouseMove", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void up(UpOptions options) {
|
||||
page.withLogging("Mouse.up", () -> upImpl(options));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void wheel(double deltaX, double deltaY) {
|
||||
page.withLogging("Mouse.wheel", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("deltaX", deltaX);
|
||||
params.addProperty("deltaY", deltaY);
|
||||
page.sendMessage("mouseWheel", params);
|
||||
});
|
||||
}
|
||||
|
||||
private void upImpl(UpOptions options) {
|
||||
if (options == null) {
|
||||
options = new UpOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
page.sendMessage("mouseUp", params);
|
||||
page.sendMessage("mouseUp", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void wheel(double deltaX, double deltaY) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("deltaX", deltaX);
|
||||
params.addProperty("deltaY", deltaY);
|
||||
page.sendMessage("mouseWheel", params, NO_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ import com.microsoft.playwright.assertions.PageAssertions;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.microsoft.playwright.impl.LocatorAssertionsImpl.shouldIgnoreCase;
|
||||
import static com.microsoft.playwright.impl.UrlMatcher.resolveUrl;
|
||||
import static com.microsoft.playwright.impl.Utils.convertType;
|
||||
|
||||
@ -33,39 +32,45 @@ public class PageAssertionsImpl extends AssertionsBase implements PageAssertions
|
||||
}
|
||||
|
||||
private PageAssertionsImpl(Page page, boolean isNot) {
|
||||
super((LocatorImpl) page.locator(":root"), isNot);
|
||||
super(isNot);
|
||||
this.actualPage = (PageImpl) page;
|
||||
}
|
||||
|
||||
@Override
|
||||
FrameExpectResult doExpect(String expression, FrameExpectOptions expectOptions, String title) {
|
||||
FrameImpl frame = (FrameImpl) actualPage.mainFrame();
|
||||
return frame.expect(expression, expectOptions, title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasTitle(String title, HasTitleOptions options) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = title;
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.title", expected, title, "Page title expected to be", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.title", expected, title, "Page title expected to be", convertType(options, FrameExpectOptions.class), "Assert \"hasTitle\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasTitle(Pattern pattern, HasTitleOptions options) {
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
expectImpl("to.have.title", expected, pattern, "Page title expected to match regex", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.title", expected, pattern, "Page title expected to match regex", convertType(options, FrameExpectOptions.class), "Assert \"hasTitle\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasURL(String url, HasURLOptions options) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
if (actualPage.context().baseUrl != null) {
|
||||
url = resolveUrl(actualPage.context().baseUrl, url);
|
||||
if (actualPage.context().baseUrl() != null) {
|
||||
url = resolveUrl(actualPage.context().baseUrl(), url);
|
||||
}
|
||||
expected.string = url;
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expectImpl("to.have.url", expected, url, "Page URL expected to be", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.url", expected, url, "Page URL expected to be", convertType(options, FrameExpectOptions.class), "Assert \"hasURL\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasURL(Pattern pattern, HasURLOptions options) {
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
expectImpl("to.have.url", expected, pattern, "Page URL expected to match regex", convertType(options, FrameExpectOptions.class));
|
||||
expectImpl("to.have.url", expected, pattern, "Page URL expected to match regex", convertType(options, FrameExpectOptions.class), "Assert \"hasURL\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -95,7 +95,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
BrowserContextImpl ownedContext;
|
||||
private boolean isClosed;
|
||||
final Set<Worker> workers = new HashSet<>();
|
||||
private final TimeoutSettings timeoutSettings;
|
||||
protected final TimeoutSettings timeoutSettings;
|
||||
private VideoImpl video;
|
||||
private final PageImpl opener;
|
||||
private String closeReason;
|
||||
@ -546,7 +546,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
ownedContext.close();
|
||||
} else {
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("close", params);
|
||||
sendMessage("close", params, NO_TIMEOUT);
|
||||
}
|
||||
} catch (PlaywrightException exception) {
|
||||
if (isSafeCloseError(exception) && (options.runBeforeUnload == null || !options.runBeforeUnload)) {
|
||||
@ -558,13 +558,13 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public ElementHandle querySelector(String selector, QuerySelectorOptions options) {
|
||||
return withLogging("Page.querySelector", () -> mainFrame.querySelectorImpl(
|
||||
selector, convertType(options, Frame.QuerySelectorOptions.class)));
|
||||
return mainFrame.querySelector(
|
||||
selector, convertType(options, Frame.QuerySelectorOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ElementHandle> querySelectorAll(String selector) {
|
||||
return withLogging("Page.querySelectorAll", () -> mainFrame.querySelectorAllImpl(selector));
|
||||
return mainFrame.querySelectorAll(selector);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -580,17 +580,15 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
return;
|
||||
}
|
||||
AddLocatorHandlerOptions finalOptions = options;
|
||||
withLogging("Page.addLocatorHandler", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", locatorImpl.selector);
|
||||
if (finalOptions.noWaitAfter != null && finalOptions.noWaitAfter) {
|
||||
params.addProperty("noWaitAfter", true);
|
||||
}
|
||||
params.addProperty("selector", locatorImpl.selector);
|
||||
JsonObject json = (JsonObject) sendMessage("registerLocatorHandler", params);
|
||||
int uid = json.get("uid").getAsInt();
|
||||
locatorHandlers.put(uid, new LocatorHandler(locator, handler, finalOptions.times));
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", locatorImpl.selector);
|
||||
if (finalOptions.noWaitAfter != null && finalOptions.noWaitAfter) {
|
||||
params.addProperty("noWaitAfter", true);
|
||||
}
|
||||
params.addProperty("selector", locatorImpl.selector);
|
||||
JsonObject json = (JsonObject) sendMessage("registerLocatorHandler", params, NO_TIMEOUT);
|
||||
int uid = json.get("uid").getAsInt();
|
||||
locatorHandlers.put(uid, new LocatorHandler(locator, handler, finalOptions.times));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -601,7 +599,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("uid", entry.getKey());
|
||||
try {
|
||||
sendMessage("unregisterLocatorHandler", params);
|
||||
sendMessage("unregisterLocatorHandler", params, NO_TIMEOUT);
|
||||
} catch (PlaywrightException e) {
|
||||
}
|
||||
}
|
||||
@ -626,71 +624,65 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public Object evalOnSelector(String selector, String pageFunction, Object arg, EvalOnSelectorOptions options) {
|
||||
return withLogging("Page.evalOnSelector", () -> mainFrame.evalOnSelectorImpl(
|
||||
selector, pageFunction, arg, convertType(options, Frame.EvalOnSelectorOptions.class)));
|
||||
return mainFrame.evalOnSelectorImpl(
|
||||
selector, pageFunction, arg, convertType(options, Frame.EvalOnSelectorOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evalOnSelectorAll(String selector, String pageFunction, Object arg) {
|
||||
return withLogging("Page.evalOnSelectorAll", () -> mainFrame.evalOnSelectorAllImpl(selector, pageFunction, arg));
|
||||
return mainFrame.evalOnSelectorAllImpl(selector, pageFunction, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInitScript(String script) {
|
||||
withLogging("Page.addInitScript", () -> addInitScriptImpl(script));
|
||||
addInitScriptImpl(script);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInitScript(Path path) {
|
||||
withLogging("Page.addInitScript", () -> {
|
||||
try {
|
||||
byte[] bytes = readAllBytes(path);
|
||||
String script = addSourceUrlToScript(new String(bytes, UTF_8), path);
|
||||
addInitScriptImpl(script);
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to read script from file", e);
|
||||
}
|
||||
});
|
||||
try {
|
||||
byte[] bytes = readAllBytes(path);
|
||||
String script = addSourceUrlToScript(new String(bytes, UTF_8), path);
|
||||
addInitScriptImpl(script);
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to read script from file", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void addInitScriptImpl(String script) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("source", script);
|
||||
sendMessage("addInitScript", params);
|
||||
sendMessage("addInitScript", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementHandle addScriptTag(AddScriptTagOptions options) {
|
||||
return withLogging("Page.addScriptTag",
|
||||
() -> mainFrame.addScriptTagImpl(convertType(options, Frame.AddScriptTagOptions.class)));
|
||||
return mainFrame.addScriptTagImpl(convertType(options, Frame.AddScriptTagOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementHandle addStyleTag(AddStyleTagOptions options) {
|
||||
return withLogging("Page.addStyleTag",
|
||||
() -> mainFrame.addStyleTagImpl(convertType(options, Frame.AddStyleTagOptions.class)));
|
||||
return mainFrame.addStyleTagImpl(convertType(options, Frame.AddStyleTagOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bringToFront() {
|
||||
withLogging("Page.bringToFront", () -> sendMessage("bringToFront"));
|
||||
sendMessage("bringToFront");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void check(String selector, CheckOptions options) {
|
||||
withLogging("Page.check",
|
||||
() -> mainFrame.checkImpl(selector, convertType(options, Frame.CheckOptions.class)));
|
||||
mainFrame.check(selector, convertType(options, Frame.CheckOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void click(String selector, ClickOptions options) {
|
||||
withLogging("Page.click",
|
||||
() -> mainFrame.clickImpl(selector, convertType(options, Frame.ClickOptions.class)));
|
||||
mainFrame.clickImpl(selector, convertType(options, Frame.ClickOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String content() {
|
||||
return withLogging("Page.content", () -> mainFrame.contentImpl());
|
||||
return mainFrame.content();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -700,19 +692,17 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void dblclick(String selector, DblclickOptions options) {
|
||||
withLogging("Page.dblclick",
|
||||
() -> mainFrame.dblclickImpl(selector, convertType(options, Frame.DblclickOptions.class)));
|
||||
mainFrame.dblclick(selector, convertType(options, Frame.DblclickOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchEvent(String selector, String type, Object eventInit, DispatchEventOptions options) {
|
||||
withLogging("Page.dispatchEvent",
|
||||
() -> mainFrame.dispatchEventImpl(selector, type, eventInit, convertType(options, Frame.DispatchEventOptions.class)));
|
||||
mainFrame.dispatchEvent(selector, type, eventInit, convertType(options, Frame.DispatchEventOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emulateMedia(EmulateMediaOptions options) {
|
||||
withLogging("Page.emulateMedia", () -> emulateMediaImpl(options));
|
||||
emulateMediaImpl(options);
|
||||
}
|
||||
|
||||
private void emulateMediaImpl(EmulateMediaOptions options) {
|
||||
@ -720,22 +710,22 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
options = new EmulateMediaOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("emulateMedia", params);
|
||||
sendMessage("emulateMedia", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evaluate(String expression, Object arg) {
|
||||
return withLogging("Page.evaluate", () -> mainFrame.evaluateImpl(expression, arg));
|
||||
return mainFrame.evaluate(expression, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSHandle evaluateHandle(String pageFunction, Object arg) {
|
||||
return withLogging("Page.evaluateHandle", () -> mainFrame.evaluateHandleImpl(pageFunction, arg));
|
||||
return mainFrame.evaluateHandle(pageFunction, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exposeBinding(String name, BindingCallback playwrightBinding, ExposeBindingOptions options) {
|
||||
withLogging("Page.exposeBinding", () -> exposeBindingImpl(name, playwrightBinding, options));
|
||||
exposeBindingImpl(name, playwrightBinding, options);
|
||||
}
|
||||
|
||||
private void exposeBindingImpl(String name, BindingCallback playwrightBinding, ExposeBindingOptions options) {
|
||||
@ -752,25 +742,22 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
if (options != null && options.handle != null && options.handle) {
|
||||
params.addProperty("needsHandle", true);
|
||||
}
|
||||
sendMessage("exposeBinding", params);
|
||||
sendMessage("exposeBinding", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exposeFunction(String name, FunctionCallback playwrightFunction) {
|
||||
withLogging("Page.exposeFunction",
|
||||
() -> exposeBindingImpl(name, (BindingCallback.Source source, Object... args) -> playwrightFunction.call(args), null));
|
||||
exposeBindingImpl(name, (BindingCallback.Source source, Object... args) -> playwrightFunction.call(args), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fill(String selector, String value, FillOptions options) {
|
||||
withLogging("Page.fill",
|
||||
() -> mainFrame.fillImpl(selector, value, convertType(options, Frame.FillOptions.class)));
|
||||
mainFrame.fill(selector, value, convertType(options, Frame.FillOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focus(String selector, FocusOptions options) {
|
||||
withLogging("Page.focus",
|
||||
() -> mainFrame.focusImpl(selector, convertType(options, Frame.FocusOptions.class)));
|
||||
mainFrame.focus(selector, convertType(options, Frame.FocusOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -785,7 +772,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public Frame frameByUrl(String glob) {
|
||||
return frameFor(new UrlMatcher(browserContext.baseUrl, glob));
|
||||
return frameFor(UrlMatcher.forGlob(browserContext.baseUrl(), glob, this.connection.localUtils, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -819,89 +806,77 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public String getAttribute(String selector, String name, GetAttributeOptions options) {
|
||||
return withLogging("Page.getAttribute",
|
||||
() -> mainFrame.getAttributeImpl(selector, name, convertType(options, Frame.GetAttributeOptions.class)));
|
||||
return mainFrame.getAttributeImpl(selector, name, convertType(options, Frame.GetAttributeOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByAltText(String text, GetByAltTextOptions options) {
|
||||
return withLogging("Page.getAttribute",
|
||||
() -> mainFrame.getByAltText(text, convertType(options, Frame.GetByAltTextOptions.class)));
|
||||
return mainFrame.getByAltText(text, convertType(options, Frame.GetByAltTextOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByAltText(Pattern text, GetByAltTextOptions options) {
|
||||
return withLogging("Page.getByAltText",
|
||||
() -> mainFrame.getByAltText(text, convertType(options, Frame.GetByAltTextOptions.class)));
|
||||
return mainFrame.getByAltText(text, convertType(options, Frame.GetByAltTextOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByLabel(String text, GetByLabelOptions options) {
|
||||
return withLogging("Page.getByLabel",
|
||||
() -> mainFrame.getByLabel(text, convertType(options, Frame.GetByLabelOptions.class)));
|
||||
return mainFrame.getByLabel(text, convertType(options, Frame.GetByLabelOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByLabel(Pattern text, GetByLabelOptions options) {
|
||||
return withLogging("Page.getByLabel",
|
||||
() -> mainFrame.getByLabel(text, convertType(options, Frame.GetByLabelOptions.class)));
|
||||
return mainFrame.getByLabel(text, convertType(options, Frame.GetByLabelOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByPlaceholder(String text, GetByPlaceholderOptions options) {
|
||||
return withLogging("Page.getByPlaceholder",
|
||||
() -> mainFrame.getByPlaceholder(text, convertType(options, Frame.GetByPlaceholderOptions.class)));
|
||||
return mainFrame.getByPlaceholder(text, convertType(options, Frame.GetByPlaceholderOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByPlaceholder(Pattern text, GetByPlaceholderOptions options) {
|
||||
return withLogging("Page.getByPlaceholder",
|
||||
() -> mainFrame.getByPlaceholder(text, convertType(options, Frame.GetByPlaceholderOptions.class)));
|
||||
return mainFrame.getByPlaceholder(text, convertType(options, Frame.GetByPlaceholderOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByRole(AriaRole role, GetByRoleOptions options) {
|
||||
return withLogging("Page.getByRole",
|
||||
() -> mainFrame.getByRole(role, convertType(options, Frame.GetByRoleOptions.class)));
|
||||
return mainFrame.getByRole(role, convertType(options, Frame.GetByRoleOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByTestId(String testId) {
|
||||
return withLogging("Page.getByTestId", () -> mainFrame.getByTestId(testId));
|
||||
return mainFrame.getByTestId(testId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByTestId(Pattern testId) {
|
||||
return withLogging("Page.getByTestId", () -> mainFrame.getByTestId(testId));
|
||||
return mainFrame.getByTestId(testId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByText(String text, GetByTextOptions options) {
|
||||
return withLogging("Page.getByText",
|
||||
() -> mainFrame.getByText(text, convertType(options, Frame.GetByTextOptions.class)));
|
||||
return mainFrame.getByText(text, convertType(options, Frame.GetByTextOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByText(Pattern text, GetByTextOptions options) {
|
||||
return withLogging("Page.getByText",
|
||||
() -> mainFrame.getByText(text, convertType(options, Frame.GetByTextOptions.class)));
|
||||
return mainFrame.getByText(text, convertType(options, Frame.GetByTextOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByTitle(String text, GetByTitleOptions options) {
|
||||
return withLogging("Page.getByTitle",
|
||||
() -> mainFrame.getByTitle(text, convertType(options, Frame.GetByTitleOptions.class)));
|
||||
return mainFrame.getByTitle(text, convertType(options, Frame.GetByTitleOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByTitle(Pattern text, GetByTitleOptions options) {
|
||||
return withLogging("Page.getByTitle",
|
||||
() -> mainFrame.getByTitle(text, convertType(options, Frame.GetByTitleOptions.class)));
|
||||
return mainFrame.getByTitle(text, convertType(options, Frame.GetByTitleOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response goBack(GoBackOptions options) {
|
||||
return withLogging("Page.goBack", () -> goBackImpl(options));
|
||||
return goBackImpl(options);
|
||||
}
|
||||
|
||||
Response goBackImpl(GoBackOptions options) {
|
||||
@ -909,7 +884,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
options = new GoBackOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
JsonObject json = sendMessage("goBack", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("goBack", params, timeoutSettings.navigationTimeout(options.timeout)).getAsJsonObject();
|
||||
if (json.has("response")) {
|
||||
return connection.getExistingObject(json.getAsJsonObject("response").get("guid").getAsString());
|
||||
}
|
||||
@ -918,7 +893,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public Response goForward(GoForwardOptions options) {
|
||||
return withLogging("Page.goForward", () -> goForwardImpl(options));
|
||||
return goForwardImpl(options);
|
||||
}
|
||||
|
||||
Response goForwardImpl(GoForwardOptions options) {
|
||||
@ -926,7 +901,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
options = new GoForwardOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
JsonObject json = sendMessage("goForward", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("goForward", params, timeoutSettings.navigationTimeout(options.timeout)).getAsJsonObject();
|
||||
if (json.has("response")) {
|
||||
return connection.getExistingObject(json.getAsJsonObject("response").get("guid").getAsString());
|
||||
}
|
||||
@ -935,46 +910,42 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void requestGC() {
|
||||
withLogging("Page.requestGC", () -> sendMessage("requestGC"));
|
||||
sendMessage("requestGC");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseImpl navigate(String url, NavigateOptions options) {
|
||||
return withLogging("Page.navigate", () -> mainFrame.navigateImpl(url, convertType(options, Frame.NavigateOptions.class)));
|
||||
return mainFrame.navigateImpl(url, convertType(options, Frame.NavigateOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hover(String selector, HoverOptions options) {
|
||||
withLogging("Page.hover", () -> mainFrame.hoverImpl(selector, convertType(options, Frame.HoverOptions.class)));
|
||||
mainFrame.hoverImpl(selector, convertType(options, Frame.HoverOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragAndDrop(String source, String target, DragAndDropOptions options) {
|
||||
withLogging("Page.dragAndDrop", () -> mainFrame.dragAndDropImpl(source, target, convertType(options, Frame.DragAndDropOptions.class)));
|
||||
mainFrame.dragAndDropImpl(source, target, convertType(options, Frame.DragAndDropOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String innerHTML(String selector, InnerHTMLOptions options) {
|
||||
return withLogging("Page.innerHTML",
|
||||
() -> mainFrame.innerHTMLImpl(selector, convertType(options, Frame.InnerHTMLOptions.class)));
|
||||
return mainFrame.innerHTMLImpl(selector, convertType(options, Frame.InnerHTMLOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String innerText(String selector, InnerTextOptions options) {
|
||||
return withLogging("Page.innerText",
|
||||
() -> mainFrame.innerTextImpl(selector, convertType(options, Frame.InnerTextOptions.class)));
|
||||
return mainFrame.innerTextImpl(selector, convertType(options, Frame.InnerTextOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String inputValue(String selector, InputValueOptions options) {
|
||||
return withLogging("Page.inputValue",
|
||||
() -> mainFrame.inputValueImpl(selector, convertType(options, Frame.InputValueOptions.class)));
|
||||
return mainFrame.inputValueImpl(selector, convertType(options, Frame.InputValueOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked(String selector, IsCheckedOptions options) {
|
||||
return withLogging("Page.isChecked",
|
||||
() -> mainFrame.isCheckedImpl(selector, convertType(options, Frame.IsCheckedOptions.class)));
|
||||
return mainFrame.isCheckedImpl(selector, convertType(options, Frame.IsCheckedOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -984,32 +955,27 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public boolean isDisabled(String selector, IsDisabledOptions options) {
|
||||
return withLogging("Page.isDisabled",
|
||||
() -> mainFrame.isDisabledImpl(selector, convertType(options, Frame.IsDisabledOptions.class)));
|
||||
return mainFrame.isDisabledImpl(selector, convertType(options, Frame.IsDisabledOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEditable(String selector, IsEditableOptions options) {
|
||||
return withLogging("Page.isEditable",
|
||||
() -> mainFrame.isEditableImpl(selector, convertType(options, Frame.IsEditableOptions.class)));
|
||||
return mainFrame.isEditableImpl(selector, convertType(options, Frame.IsEditableOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(String selector, IsEnabledOptions options) {
|
||||
return withLogging("Page.isEnabled",
|
||||
() -> mainFrame.isEnabledImpl(selector, convertType(options, Frame.IsEnabledOptions.class)));
|
||||
return mainFrame.isEnabledImpl(selector, convertType(options, Frame.IsEnabledOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden(String selector, IsHiddenOptions options) {
|
||||
return withLogging("Page.isHidden",
|
||||
() -> mainFrame.isHiddenImpl(selector, convertType(options, Frame.IsHiddenOptions.class)));
|
||||
return mainFrame.isHiddenImpl(selector, convertType(options, Frame.IsHiddenOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible(String selector, IsVisibleOptions options) {
|
||||
return withLogging("Page.isVisible",
|
||||
() -> mainFrame.isVisibleImpl(selector, convertType(options, Frame.IsVisibleOptions.class)));
|
||||
return mainFrame.isVisibleImpl(selector, convertType(options, Frame.IsVisibleOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1042,23 +1008,22 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void pause() {
|
||||
withLogging("Page.pause", () -> {
|
||||
Double defaultNavigationTimeout = browserContext.timeoutSettings.defaultNavigationTimeout();
|
||||
Double defaultTimeout = browserContext.timeoutSettings.defaultTimeout();
|
||||
browserContext.setDefaultNavigationTimeoutImpl(0.0);
|
||||
browserContext.setDefaultTimeoutImpl(0.0);
|
||||
try {
|
||||
runUntil(() -> {}, new WaitableRace<>(asList(context().pause(), (Waitable<JsonElement>) waitableClosedOrCrashed)));
|
||||
} finally {
|
||||
browserContext.setDefaultNavigationTimeoutImpl(defaultNavigationTimeout);
|
||||
browserContext.setDefaultTimeoutImpl(defaultTimeout);
|
||||
}
|
||||
});
|
||||
TimeoutSettings settings = browserContext.timeoutSettings;
|
||||
Double defaultNavigationTimeout = settings.defaultNavigationTimeout();
|
||||
Double defaultTimeout = settings.defaultTimeout();
|
||||
settings.setDefaultNavigationTimeout(0.0);
|
||||
settings.setDefaultTimeout(0.0);
|
||||
try {
|
||||
runUntil(() -> {}, new WaitableRace<>(asList(context().pause(), (Waitable<JsonElement>) waitableClosedOrCrashed)));
|
||||
} finally {
|
||||
settings.setDefaultNavigationTimeout(defaultNavigationTimeout);
|
||||
settings.setDefaultTimeout(defaultTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] pdf(PdfOptions options) {
|
||||
return withLogging("Page.pdf", () -> pdfImpl(options));
|
||||
return pdfImpl(options);
|
||||
}
|
||||
|
||||
private byte[] pdfImpl(PdfOptions options) {
|
||||
@ -1067,7 +1032,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.remove("path");
|
||||
JsonObject json = sendMessage("pdf", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("pdf", params, NO_TIMEOUT).getAsJsonObject();
|
||||
byte[] buffer = Base64.getDecoder().decode(json.get("pdf").getAsString());
|
||||
if (options.path != null) {
|
||||
Utils.writeToFile(buffer, options.path);
|
||||
@ -1077,13 +1042,12 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void press(String selector, String key, PressOptions options) {
|
||||
withLogging("Page.press",
|
||||
() -> mainFrame.pressImpl(selector, key, convertType(options, Frame.PressOptions.class)));
|
||||
mainFrame.pressImpl(selector, key, convertType(options, Frame.PressOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response reload(ReloadOptions options) {
|
||||
return withLogging("Page.reload", () -> reloadImpl(options));
|
||||
return reloadImpl(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1096,7 +1060,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
options = new ReloadOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
JsonObject json = sendMessage("reload", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("reload", params, timeoutSettings.navigationTimeout(options.timeout)).getAsJsonObject();
|
||||
if (json.has("response")) {
|
||||
return connection.getExistingObject(json.getAsJsonObject("response").get("guid").getAsString());
|
||||
}
|
||||
@ -1105,7 +1069,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void route(String url, Consumer<Route> handler, RouteOptions options) {
|
||||
route(new UrlMatcher(browserContext.baseUrl, url), handler, options);
|
||||
route(UrlMatcher.forGlob(browserContext.baseUrl(), url, this.connection.localUtils, false), handler, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1124,25 +1088,23 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
options = new RouteFromHAROptions();
|
||||
}
|
||||
if (options.update != null && options.update) {
|
||||
browserContext.recordIntoHar(this, har, convertType(options, BrowserContext.RouteFromHAROptions.class));
|
||||
browserContext.recordIntoHar(this, har, convertType(options, BrowserContext.RouteFromHAROptions.class), null);
|
||||
return;
|
||||
}
|
||||
UrlMatcher matcher = UrlMatcher.forOneOf(browserContext.baseUrl, options.url);
|
||||
UrlMatcher matcher = UrlMatcher.forOneOf(browserContext.baseUrl(), options.url, this.connection.localUtils, false);
|
||||
HARRouter harRouter = new HARRouter(connection.localUtils, har, options.notFound);
|
||||
onClose(context -> harRouter.dispose());
|
||||
route(matcher, route -> harRouter.handle(route), null);
|
||||
}
|
||||
|
||||
private void route(UrlMatcher matcher, Consumer<Route> handler, RouteOptions options) {
|
||||
withLogging("Page.route", () -> {
|
||||
routes.add(matcher, handler, options == null ? null : options.times);
|
||||
updateInterceptionPatterns();
|
||||
});
|
||||
routes.add(matcher, handler, options == null ? null : options.times);
|
||||
updateInterceptionPatterns();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void routeWebSocket(String url, Consumer<WebSocketRoute> handler) {
|
||||
routeWebSocketImpl(new UrlMatcher(browserContext.baseUrl, url), handler);
|
||||
routeWebSocketImpl(UrlMatcher.forGlob(browserContext.baseUrl(), url, this.connection.localUtils, true), handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1156,15 +1118,13 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
private void routeWebSocketImpl(UrlMatcher matcher, Consumer<WebSocketRoute> handler) {
|
||||
withLogging("Page.routeWebSocket", () -> {
|
||||
webSocketRoutes.add(matcher, handler);
|
||||
updateWebSocketInterceptionPatterns();
|
||||
});
|
||||
webSocketRoutes.add(matcher, handler);
|
||||
updateWebSocketInterceptionPatterns();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] screenshot(ScreenshotOptions options) {
|
||||
return withLogging("Page.screenshot", () -> screenshotImpl(options));
|
||||
return screenshotImpl(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1181,8 +1141,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String selector, String[] values, SelectOptionOptions options) {
|
||||
return withLogging("Page.selectOption",
|
||||
() -> mainFrame.selectOptionImpl(selector, values, convertType(options, Frame.SelectOptionOptions.class)));
|
||||
return mainFrame.selectOptionImpl(selector, values, convertType(options, Frame.SelectOptionOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1208,19 +1167,9 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
}
|
||||
}
|
||||
List<Locator> mask = options.mask;
|
||||
options.mask = null;
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
options.mask = mask;
|
||||
params.remove("path");
|
||||
if (mask != null) {
|
||||
JsonArray maskArray = new JsonArray();
|
||||
for (Locator locator: mask) {
|
||||
maskArray.add(((LocatorImpl) locator).toProtocol());
|
||||
}
|
||||
params.add("mask", maskArray);
|
||||
}
|
||||
JsonObject json = sendMessage("screenshot", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("screenshot", params, timeoutSettings.timeout(options.timeout)).getAsJsonObject();
|
||||
|
||||
byte[] buffer = Base64.getDecoder().decode(json.get("binary").getAsString());
|
||||
if (options.path != null) {
|
||||
@ -1231,62 +1180,46 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String selector, SelectOption[] values, SelectOptionOptions options) {
|
||||
return withLogging("Page.selectOption",
|
||||
() -> mainFrame.selectOptionImpl(selector, values, convertType(options, Frame.SelectOptionOptions.class)));
|
||||
return mainFrame.selectOptionImpl(selector, values, convertType(options, Frame.SelectOptionOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String selector, ElementHandle[] values, SelectOptionOptions options) {
|
||||
return withLogging("Page.selectOption",
|
||||
() -> mainFrame.selectOptionImpl(selector, values, convertType(options, Frame.SelectOptionOptions.class)));
|
||||
return mainFrame.selectOptionImpl(selector, values, convertType(options, Frame.SelectOptionOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChecked(String selector, boolean checked, SetCheckedOptions options) {
|
||||
withLogging("Page.setChecked",
|
||||
() -> mainFrame.setCheckedImpl(selector, checked, convertType(options, Frame.SetCheckedOptions.class)));
|
||||
mainFrame.setCheckedImpl(selector, checked, convertType(options, Frame.SetCheckedOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContent(String html, SetContentOptions options) {
|
||||
withLogging("Page.setContent",
|
||||
() -> mainFrame.setContentImpl(html, convertType(options, Frame.SetContentOptions.class)));
|
||||
mainFrame.setContent(html, convertType(options, Frame.SetContentOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultNavigationTimeout(double timeout) {
|
||||
withLogging("Page.setDefaultNavigationTimeout", () -> {
|
||||
timeoutSettings.setDefaultNavigationTimeout(timeout);
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("timeout", timeout);
|
||||
sendMessage("setDefaultNavigationTimeoutNoReply", params);
|
||||
});
|
||||
timeoutSettings.setDefaultNavigationTimeout(timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultTimeout(double timeout) {
|
||||
withLogging("Page.setDefaultTimeout", () -> {
|
||||
timeoutSettings.setDefaultTimeout(timeout);
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("timeout", timeout);
|
||||
sendMessage("setDefaultTimeoutNoReply", params);
|
||||
});
|
||||
timeoutSettings.setDefaultTimeout(timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExtraHTTPHeaders(Map<String, String> headers) {
|
||||
withLogging("Page.setExtraHTTPHeaders", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
JsonArray jsonHeaders = new JsonArray();
|
||||
for (Map.Entry<String, String> e : headers.entrySet()) {
|
||||
JsonObject header = new JsonObject();
|
||||
header.addProperty("name", e.getKey());
|
||||
header.addProperty("value", e.getValue());
|
||||
jsonHeaders.add(header);
|
||||
}
|
||||
params.add("headers", jsonHeaders);
|
||||
sendMessage("setExtraHTTPHeaders", params);
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
JsonArray jsonHeaders = new JsonArray();
|
||||
for (Map.Entry<String, String> e : headers.entrySet()) {
|
||||
JsonObject header = new JsonObject();
|
||||
header.addProperty("name", e.getKey());
|
||||
header.addProperty("value", e.getValue());
|
||||
jsonHeaders.add(header);
|
||||
}
|
||||
params.add("headers", jsonHeaders);
|
||||
sendMessage("setExtraHTTPHeaders", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1296,8 +1229,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void setInputFiles(String selector, Path[] files, SetInputFilesOptions options) {
|
||||
withLogging("Page.setInputFiles",
|
||||
() -> mainFrame.setInputFilesImpl(selector, files, convertType(options, Frame.SetInputFilesOptions.class)));
|
||||
mainFrame.setInputFilesImpl(selector, files, convertType(options, Frame.SetInputFilesOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1307,35 +1239,30 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void setInputFiles(String selector, FilePayload[] files, SetInputFilesOptions options) {
|
||||
withLogging("Page.setInputFiles",
|
||||
() -> mainFrame.setInputFilesImpl(selector, files, convertType(options, Frame.SetInputFilesOptions.class)));
|
||||
mainFrame.setInputFilesImpl(selector, files, convertType(options, Frame.SetInputFilesOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setViewportSize(int width, int height) {
|
||||
withLogging("Page.setViewportSize", () -> {
|
||||
viewport = new ViewportSize(width, height);
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("viewportSize", gson().toJsonTree(viewport));
|
||||
sendMessage("setViewportSize", params);
|
||||
});
|
||||
viewport = new ViewportSize(width, height);
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("viewportSize", gson().toJsonTree(viewport));
|
||||
sendMessage("setViewportSize", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tap(String selector, TapOptions options) {
|
||||
withLogging("Page.tap",
|
||||
() -> mainFrame.tapImpl(selector, convertType(options, Frame.TapOptions.class)));
|
||||
mainFrame.tap(selector, convertType(options, Frame.TapOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String textContent(String selector, TextContentOptions options) {
|
||||
return withLogging("Page.textContent",
|
||||
() -> mainFrame.textContentImpl(selector, convertType(options, Frame.TextContentOptions.class)));
|
||||
return mainFrame.textContent(selector, convertType(options, Frame.TextContentOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String title() {
|
||||
return withLogging("Page.title", () -> mainFrame.titleImpl());
|
||||
return mainFrame.title();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1345,27 +1272,23 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void type(String selector, String text, TypeOptions options) {
|
||||
withLogging("Page.type",
|
||||
() -> mainFrame.typeImpl(selector, text, convertType(options, Frame.TypeOptions.class)));
|
||||
mainFrame.type(selector, text, convertType(options, Frame.TypeOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncheck(String selector, UncheckOptions options) {
|
||||
withLogging("Page.uncheck",
|
||||
() -> mainFrame.uncheckImpl(selector, convertType(options, Frame.UncheckOptions.class)));
|
||||
mainFrame.uncheck(selector, convertType(options, Frame.UncheckOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unrouteAll() {
|
||||
withLogging("Page.unrouteAll", () -> {
|
||||
routes.removeAll();
|
||||
updateInterceptionPatterns();
|
||||
});
|
||||
routes.removeAll();
|
||||
updateInterceptionPatterns();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unroute(String url, Consumer<Route> handler) {
|
||||
unroute(new UrlMatcher(browserContext.baseUrl, url), handler);
|
||||
unroute(UrlMatcher.forGlob(browserContext.baseUrl(), url, this.connection.localUtils, false), handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1379,18 +1302,16 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
private void unroute(UrlMatcher matcher, Consumer<Route> handler) {
|
||||
withLogging("Page.unroute", () -> {
|
||||
routes.remove(matcher, handler);
|
||||
updateInterceptionPatterns();
|
||||
});
|
||||
routes.remove(matcher, handler);
|
||||
updateInterceptionPatterns();
|
||||
}
|
||||
|
||||
private void updateInterceptionPatterns() {
|
||||
sendMessage("setNetworkInterceptionPatterns", routes.interceptionPatterns());
|
||||
sendMessage("setNetworkInterceptionPatterns", routes.interceptionPatterns(), NO_TIMEOUT);
|
||||
}
|
||||
|
||||
private void updateWebSocketInterceptionPatterns() {
|
||||
sendMessage("setWebSocketInterceptionPatterns", webSocketRoutes.interceptionPatterns());
|
||||
sendMessage("setWebSocketInterceptionPatterns", webSocketRoutes.interceptionPatterns(), NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1411,7 +1332,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
// Note: we are creating Video object lazily, because we do not know
|
||||
// BrowserContextOptions when constructing the page - it is assigned
|
||||
// too late during launchPersistentContext.
|
||||
if (browserContext.videosDir == null) {
|
||||
if (browserContext.videosDir() == null) {
|
||||
return null;
|
||||
}
|
||||
return forceVideo();
|
||||
@ -1432,15 +1353,17 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public JSHandle waitForFunction(String pageFunction, Object arg, WaitForFunctionOptions options) {
|
||||
return withLogging("Page.waitForFunction",
|
||||
() -> mainFrame.waitForFunctionImpl(pageFunction, arg, convertType(options, Frame.WaitForFunctionOptions.class)));
|
||||
return mainFrame.waitForFunction(pageFunction, arg, convertType(options, Frame.WaitForFunctionOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waitForLoadState(LoadState state, WaitForLoadStateOptions options) {
|
||||
withWaitLogging("Page.waitForLoadState", logger -> {
|
||||
mainFrame.waitForLoadStateImpl(state, convertType(options, Frame.WaitForLoadStateOptions.class), logger);
|
||||
return null;
|
||||
final LoadState loadState = state == null ? LoadState.LOAD : state;
|
||||
withTitle("Wait for load state \"" + loadState.toString().toLowerCase() + "\"", () -> {
|
||||
withWaitLogging("Page.waitForLoadState", logger -> {
|
||||
mainFrame.waitForLoadStateImpl(loadState, convertType(options, Frame.WaitForLoadStateOptions.class), logger);
|
||||
return null;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -1508,7 +1431,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public Request waitForRequest(String urlGlob, WaitForRequestOptions options, Runnable code) {
|
||||
return waitForRequest(new UrlMatcher(browserContext.baseUrl, urlGlob), null, options, code);
|
||||
return waitForRequest(UrlMatcher.forGlob(browserContext.baseUrl(), urlGlob, this.connection.localUtils, false), null, options, code);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1553,7 +1476,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public Response waitForResponse(String urlGlob, WaitForResponseOptions options, Runnable code) {
|
||||
return waitForResponse(new UrlMatcher(browserContext.baseUrl, urlGlob), null, options, code);
|
||||
return waitForResponse(UrlMatcher.forGlob(browserContext.baseUrl(), urlGlob, this.connection.localUtils, false), null, options, code);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1586,8 +1509,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public ElementHandle waitForSelector(String selector, WaitForSelectorOptions options) {
|
||||
return withLogging("Page.waitForSelector",
|
||||
() -> mainFrame.waitForSelectorImpl(selector, convertType(options, Frame.WaitForSelectorOptions.class)));
|
||||
return mainFrame.waitForSelector(selector, convertType(options, Frame.WaitForSelectorOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1601,12 +1523,12 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void waitForTimeout(double timeout) {
|
||||
withLogging("Page.waitForTimeout", () -> mainFrame.waitForTimeoutImpl(timeout));
|
||||
mainFrame.waitForTimeout(timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waitForURL(String url, WaitForURLOptions options) {
|
||||
waitForURL(new UrlMatcher(browserContext.baseUrl, url), options);
|
||||
waitForURL(UrlMatcher.forGlob(browserContext.baseUrl(), url, this.connection.localUtils, false), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -52,7 +52,6 @@ public class PlaywrightImpl extends ChannelOwner implements Playwright {
|
||||
Connection connection = new Connection(new PipeTransport(p.getInputStream(), p.getOutputStream()), env);
|
||||
PlaywrightImpl result = connection.initializePlaywright();
|
||||
result.driverProcess = p;
|
||||
result.initSharedSelectors(null);
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to launch driver", e);
|
||||
@ -62,9 +61,8 @@ public class PlaywrightImpl extends ChannelOwner implements Playwright {
|
||||
private final BrowserTypeImpl chromium;
|
||||
private final BrowserTypeImpl firefox;
|
||||
private final BrowserTypeImpl webkit;
|
||||
private final SelectorsImpl selectors;
|
||||
private final APIRequestImpl apiRequest;
|
||||
private SharedSelectors sharedSelectors;
|
||||
protected SelectorsImpl selectors;
|
||||
|
||||
PlaywrightImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
@ -72,26 +70,20 @@ public class PlaywrightImpl extends ChannelOwner implements Playwright {
|
||||
firefox = parent.connection.getExistingObject(initializer.getAsJsonObject("firefox").get("guid").getAsString());
|
||||
webkit = parent.connection.getExistingObject(initializer.getAsJsonObject("webkit").get("guid").getAsString());
|
||||
|
||||
selectors = connection.getExistingObject(initializer.getAsJsonObject("selectors").get("guid").getAsString());
|
||||
chromium.playwright = this;
|
||||
firefox.playwright = this;
|
||||
webkit.playwright = this;
|
||||
|
||||
selectors = new SelectorsImpl();
|
||||
apiRequest = new APIRequestImpl(this);
|
||||
}
|
||||
|
||||
void initSharedSelectors(PlaywrightImpl parent) {
|
||||
assert sharedSelectors == null;
|
||||
if (parent == null) {
|
||||
sharedSelectors = new SharedSelectors();
|
||||
} else {
|
||||
sharedSelectors = parent.sharedSelectors;
|
||||
}
|
||||
sharedSelectors.addChannel(selectors);
|
||||
}
|
||||
|
||||
void unregisterSelectors() {
|
||||
sharedSelectors.removeChannel(selectors);
|
||||
public LocalUtils localUtils() {
|
||||
return connection.localUtils;
|
||||
}
|
||||
|
||||
public JsonArray deviceDescriptors() {
|
||||
return connection.localUtils.deviceDescriptors();
|
||||
return localUtils().deviceDescriptors();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -116,7 +108,7 @@ public class PlaywrightImpl extends ChannelOwner implements Playwright {
|
||||
|
||||
@Override
|
||||
public Selectors selectors() {
|
||||
return sharedSelectors;
|
||||
return selectors;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -14,8 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// This file is generated by generate_java_rpc.js, do not edit manually.
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import java.util.List;
|
||||
@ -105,6 +103,7 @@ class ExpectedTextValue {
|
||||
class FrameExpectOptions {
|
||||
Object expressionArg;
|
||||
List<ExpectedTextValue> expectedText;
|
||||
String selector;
|
||||
Double expectedNumber;
|
||||
SerializedArgument expectedValue;
|
||||
Boolean useInnerText;
|
||||
|
@ -26,8 +26,4 @@ public class RemoteBrowser extends ChannelOwner {
|
||||
BrowserImpl browser() {
|
||||
return connection.getExistingObject(initializer.getAsJsonObject("browser").get("guid").getAsString());
|
||||
}
|
||||
|
||||
SelectorsImpl selectors() {
|
||||
return connection.getExistingObject(initializer.getAsJsonObject("selectors").get("guid").getAsString());
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,6 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
|
||||
RequestImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
markAsInternalType();
|
||||
|
||||
if (initializer.has("redirectedFrom")) {
|
||||
redirectedFrom = connection.getExistingObject(initializer.getAsJsonObject("redirectedFrom").get("guid").getAsString());
|
||||
@ -69,7 +68,7 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
|
||||
@Override
|
||||
public Map<String, String> allHeaders() {
|
||||
return withLogging("Request.allHeaders", () -> getRawHeaders().headers());
|
||||
return getRawHeaders().headers();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -98,12 +97,12 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
|
||||
@Override
|
||||
public List<HttpHeader> headersArray() {
|
||||
return withLogging("Request.headersArray", () -> getRawHeaders().headersArray());
|
||||
return getRawHeaders().headersArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String headerValue(String name) {
|
||||
return withLogging("Request.headerValue", () -> getRawHeaders().get(name));
|
||||
return getRawHeaders().get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -153,25 +152,21 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
|
||||
@Override
|
||||
public ResponseImpl response() {
|
||||
return withLogging("Request.response", () -> {
|
||||
JsonObject result = sendMessage("response").getAsJsonObject();
|
||||
if (!result.has("response")) {
|
||||
return null;
|
||||
}
|
||||
return connection.getExistingObject(result.getAsJsonObject("response").get("guid").getAsString());
|
||||
});
|
||||
JsonObject result = sendMessage("response").getAsJsonObject();
|
||||
if (!result.has("response")) {
|
||||
return null;
|
||||
}
|
||||
return connection.getExistingObject(result.getAsJsonObject("response").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sizes sizes() {
|
||||
return withLogging("Request.sizes", () -> {
|
||||
ResponseImpl response = response();
|
||||
if (response == null) {
|
||||
throw new PlaywrightException("Unable to fetch sizes for failed request");
|
||||
}
|
||||
JsonObject json = response.sendMessage("sizes").getAsJsonObject();
|
||||
return gson().fromJson(json.getAsJsonObject("sizes"), Sizes.class);
|
||||
});
|
||||
ResponseImpl response = response();
|
||||
if (response == null) {
|
||||
throw new PlaywrightException("Unable to fetch sizes for failed request");
|
||||
}
|
||||
JsonObject json = response.sendMessage("sizes").getAsJsonObject();
|
||||
return gson().fromJson(json.getAsJsonObject("sizes"), Sizes.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -198,10 +193,8 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
if (rawHeaders != null) {
|
||||
return rawHeaders;
|
||||
}
|
||||
JsonArray rawHeadersJson = withLogging("Request.allHeaders", () -> {
|
||||
JsonObject result = sendMessage("rawRequestHeaders").getAsJsonObject();
|
||||
return result.getAsJsonArray("headers");
|
||||
});
|
||||
JsonObject result = sendMessage("rawRequestHeaders").getAsJsonObject();
|
||||
JsonArray rawHeadersJson = result.getAsJsonArray("headers");
|
||||
|
||||
// The field may have been initialized in a nested call but it is ok.
|
||||
rawHeaders = new RawHeaders(asList(gson().fromJson(rawHeadersJson, HttpHeader[].class)));
|
||||
|
@ -40,7 +40,6 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
||||
|
||||
ResponseImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
markAsInternalType();
|
||||
headers = new RawHeaders(asList(gson().fromJson(initializer.getAsJsonArray("headers"), HttpHeader[].class)));
|
||||
request = connection.getExistingObject(initializer.getAsJsonObject("request").get("guid").getAsString());
|
||||
request.timing = gson().fromJson(initializer.get("timing"), Timing.class);
|
||||
@ -48,15 +47,13 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
||||
|
||||
@Override
|
||||
public Map<String, String> allHeaders() {
|
||||
return withLogging("Response.allHeaders", () -> getRawHeaders().headers());
|
||||
return getRawHeaders().headers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] body() {
|
||||
return withLogging("Response.body", () -> {
|
||||
JsonObject json = sendMessage("body").getAsJsonObject();
|
||||
return Base64.getDecoder().decode(json.get("binary").getAsString());
|
||||
});
|
||||
JsonObject json = sendMessage("body").getAsJsonObject();
|
||||
return Base64.getDecoder().decode(json.get("binary").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -96,7 +93,7 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
||||
|
||||
@Override
|
||||
public List<HttpHeader> headersArray() {
|
||||
return withLogging("Response.headersArray", () -> getRawHeaders().headersArray());
|
||||
return getRawHeaders().headersArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -121,24 +118,20 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
||||
|
||||
@Override
|
||||
public SecurityDetails securityDetails() {
|
||||
return withLogging("Response.securityDetails", () -> {
|
||||
JsonObject json = sendMessage("securityDetails").getAsJsonObject();
|
||||
if (json.has("value")) {
|
||||
return gson().fromJson(json.get("value"), SecurityDetails.class);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
JsonObject json = sendMessage("securityDetails").getAsJsonObject();
|
||||
if (json.has("value")) {
|
||||
return gson().fromJson(json.get("value"), SecurityDetails.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerAddr serverAddr() {
|
||||
return withLogging("Response.serverAddr", () -> {
|
||||
JsonObject json = sendMessage("serverAddr").getAsJsonObject();
|
||||
if (json.has("value")) {
|
||||
return gson().fromJson(json.get("value"), ServerAddr.class);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
JsonObject json = sendMessage("serverAddr").getAsJsonObject();
|
||||
if (json.has("value")) {
|
||||
return gson().fromJson(json.get("value"), ServerAddr.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -38,18 +38,15 @@ public class RouteImpl extends ChannelOwner implements Route {
|
||||
|
||||
public RouteImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
markAsInternalType();
|
||||
request = connection.getExistingObject(initializer.getAsJsonObject("request").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abort(String errorCode) {
|
||||
startHandling();
|
||||
withLogging("Route.abort", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("errorCode", errorCode);
|
||||
sendMessageAsync("abort", params);
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("errorCode", errorCode);
|
||||
sendMessageAsync("abort", params);
|
||||
}
|
||||
|
||||
boolean isHandled() {
|
||||
@ -64,7 +61,7 @@ public class RouteImpl extends ChannelOwner implements Route {
|
||||
void resume(ResumeOptions options, boolean isFallback) {
|
||||
startHandling();
|
||||
applyOverrides(convertType(options, FallbackOptions.class));
|
||||
withLogging("Route.resume", () -> resumeImpl(request().fallbackOverridesForResume(), isFallback));
|
||||
resumeImpl(request().fallbackOverridesForResume(), isFallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -152,7 +149,7 @@ public class RouteImpl extends ChannelOwner implements Route {
|
||||
@Override
|
||||
public void fulfill(FulfillOptions options) {
|
||||
startHandling();
|
||||
withLogging("Route.fulfill", () -> fulfillImpl(options));
|
||||
fulfillImpl(options);
|
||||
}
|
||||
|
||||
private void fulfillImpl(FulfillOptions options) {
|
||||
|
@ -17,28 +17,72 @@
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import com.microsoft.playwright.Selectors;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class SelectorsImpl extends ChannelOwner {
|
||||
SelectorsImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
}
|
||||
import static com.microsoft.playwright.impl.ChannelOwner.NO_TIMEOUT;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
void register(String name, String script, Selectors.RegisterOptions options) {
|
||||
if (options == null) {
|
||||
options = new Selectors.RegisterOptions();
|
||||
public class SelectorsImpl extends LoggingSupport implements Selectors {
|
||||
protected final List<BrowserContextImpl> contextsForSelectors = new ArrayList<>();
|
||||
protected final List<JsonObject> selectorEngines = new ArrayList<>();
|
||||
|
||||
String testIdAttributeName = "data-testid";
|
||||
|
||||
@Override
|
||||
public void setTestIdAttribute(String attributeName) {
|
||||
if (attributeName == null) {
|
||||
throw new PlaywrightException("Test id attribute cannot be null");
|
||||
}
|
||||
testIdAttributeName = attributeName;
|
||||
for (BrowserContextImpl context : contextsForSelectors) {
|
||||
try {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("testIdAttributeName", attributeName);
|
||||
context.sendMessageAsync("setTestIdAttributeName", params);
|
||||
} catch (PlaywrightException e) {
|
||||
}
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("name", name);
|
||||
params.addProperty("source", script);
|
||||
sendMessage("register", params);
|
||||
}
|
||||
|
||||
void setTestIdAttributeName(String name) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("testIdAttributeName", name);
|
||||
sendMessageAsync("setTestIdAttributeName", params);
|
||||
@Override
|
||||
public void register(String name, String script, RegisterOptions options) {
|
||||
registerImpl(name, script, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(String name, Path path, RegisterOptions options) {
|
||||
byte[] buffer;
|
||||
try {
|
||||
buffer = Files.readAllBytes(path);
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to read selector from file: " + path, e);
|
||||
}
|
||||
registerImpl(name, new String(buffer, UTF_8), options);
|
||||
}
|
||||
|
||||
private void registerImpl(String name, String script, RegisterOptions options) {
|
||||
if (selectorEngines.stream().anyMatch(engine -> name.equals(engine.get("name").getAsString()))) {
|
||||
throw new PlaywrightException("selectors.register: \"" + name + "\" selector engine has been already registered");
|
||||
}
|
||||
|
||||
JsonObject engine = new JsonObject();
|
||||
engine.addProperty("name", name);
|
||||
engine.addProperty("source", script);
|
||||
if (options != null && options.contentScript != null) {
|
||||
engine.addProperty("contentScript", options.contentScript);
|
||||
}
|
||||
for (BrowserContextImpl context : contextsForSelectors) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("selectorEngine", engine);
|
||||
context.sendMessage("registerSelectorEngine", params, NO_TIMEOUT);
|
||||
}
|
||||
selectorEngines.add(engine);
|
||||
}
|
||||
}
|
||||
|
@ -66,6 +66,7 @@ class Serialization {
|
||||
.registerTypeHierarchyAdapter(JSHandleImpl.class, new HandleSerializer())
|
||||
.registerTypeAdapter((new TypeToken<Map<String, String>>(){}).getType(), new StringMapSerializer())
|
||||
.registerTypeAdapter((new TypeToken<Map<String, Object>>(){}).getType(), new FirefoxUserPrefsSerializer())
|
||||
.registerTypeAdapter(LocatorImpl.class, new LocatorImplSerializer())
|
||||
.registerTypeHierarchyAdapter(Path.class, new PathSerializer()).create();
|
||||
|
||||
static Gson gson() {
|
||||
@ -490,6 +491,13 @@ class Serialization {
|
||||
}
|
||||
}
|
||||
|
||||
private static class LocatorImplSerializer implements JsonSerializer<LocatorImpl> {
|
||||
@Override
|
||||
public JsonElement serialize(LocatorImpl src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return src.toProtocol();
|
||||
}
|
||||
}
|
||||
|
||||
private static class SameSiteAdapter extends TypeAdapter<SameSiteAttribute> {
|
||||
@Override
|
||||
public void write(JsonWriter out, SameSiteAttribute value) throws IOException {
|
||||
|
@ -1,95 +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.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import com.microsoft.playwright.Selectors;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
public class SharedSelectors extends LoggingSupport implements Selectors {
|
||||
private final List<SelectorsImpl> channels = new ArrayList<>();
|
||||
private final List<Registration> registrations = new ArrayList<>();
|
||||
|
||||
String testIdAttributeName = "data-testid";
|
||||
|
||||
private static class Registration {
|
||||
final String name;
|
||||
final String script;
|
||||
final RegisterOptions options;
|
||||
|
||||
Registration(String name, String script, RegisterOptions options) {
|
||||
this.name = name;
|
||||
this.script = script;
|
||||
this.options = options;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(String name, String script, RegisterOptions options) {
|
||||
withLogging("Selectors.register", () -> registerImpl(name, script, options));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(String name, Path path, RegisterOptions options) {
|
||||
withLogging("Selectors.register", () -> {
|
||||
byte[] buffer;
|
||||
try {
|
||||
buffer = Files.readAllBytes(path);
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to read selector from file: " + path, e);
|
||||
}
|
||||
registerImpl(name, new String(buffer, UTF_8), options);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTestIdAttribute(String attributeName) {
|
||||
if (attributeName == null) {
|
||||
throw new PlaywrightException("Test id attribute cannot be null");
|
||||
}
|
||||
testIdAttributeName = attributeName;
|
||||
channels.forEach(channel -> channel.setTestIdAttributeName(testIdAttributeName));
|
||||
}
|
||||
|
||||
void addChannel(SelectorsImpl channel) {
|
||||
registrations.forEach(r -> {
|
||||
try {
|
||||
channel.register(r.name, r.script, r.options);
|
||||
} catch (PlaywrightException e) {
|
||||
// This should not fail except for connection closure, but just in case we catch.
|
||||
}
|
||||
channel.setTestIdAttributeName(testIdAttributeName);
|
||||
});
|
||||
channels.add(channel);
|
||||
}
|
||||
|
||||
void removeChannel(SelectorsImpl channel) {
|
||||
channels.remove(channel);
|
||||
}
|
||||
|
||||
private void registerImpl(String name, String script, RegisterOptions options) {
|
||||
channels.forEach(impl -> impl.register(name, script, options));
|
||||
registrations.add(new Registration(name, script, options));
|
||||
}
|
||||
}
|
@ -50,7 +50,7 @@ public class Stream extends ChannelOwner {
|
||||
}
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("size", len);
|
||||
JsonObject json = sendMessage("read", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("read", params, NO_TIMEOUT).getAsJsonObject();
|
||||
String encoded = json.get("binary").getAsString();
|
||||
if (encoded.isEmpty()) {
|
||||
return -1;
|
||||
|
@ -18,6 +18,7 @@ package com.microsoft.playwright.impl;
|
||||
|
||||
class TimeoutSettings {
|
||||
private static final int DEFAULT_TIMEOUT_MS = 30_000;
|
||||
private static final int DEFAULT_LAUNCH_TIMEOUT_MS = 180_000;
|
||||
|
||||
private final TimeoutSettings parent;
|
||||
private Double defaultTimeout ;
|
||||
@ -80,4 +81,11 @@ class TimeoutSettings {
|
||||
}
|
||||
return new WaitableTimeout<>(timeout(timeout));
|
||||
}
|
||||
|
||||
static double launchTimeout(Double timeout) {
|
||||
if (timeout != null) {
|
||||
return timeout;
|
||||
}
|
||||
return DEFAULT_LAUNCH_TIMEOUT_MS;
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ package com.microsoft.playwright.impl;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Touchscreen;
|
||||
|
||||
import static com.microsoft.playwright.impl.ChannelOwner.NO_TIMEOUT;
|
||||
|
||||
class TouchscreenImpl implements Touchscreen {
|
||||
private final PageImpl page;
|
||||
|
||||
@ -28,11 +30,9 @@ class TouchscreenImpl implements Touchscreen {
|
||||
|
||||
@Override
|
||||
public void tap(double x, double y) {
|
||||
page.withLogging("Touchscreen.tap", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("x", x);
|
||||
params.addProperty("y", y);
|
||||
page.sendMessage("touchscreenTap", params);
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("x", x);
|
||||
params.addProperty("y", y);
|
||||
page.sendMessage("touchscreenTap", params, NO_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,6 @@ class TracingImpl extends ChannelOwner implements Tracing {
|
||||
|
||||
TracingImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
markAsInternalType();
|
||||
}
|
||||
|
||||
private void stopChunkImpl(Path path) {
|
||||
@ -46,7 +45,7 @@ class TracingImpl extends ChannelOwner implements Tracing {
|
||||
// Not interested in artifacts.
|
||||
if (path == null) {
|
||||
params.addProperty("mode", "discard");
|
||||
sendMessage("tracingStopChunk", params);
|
||||
sendMessage("tracingStopChunk", params, NO_TIMEOUT);
|
||||
if (stacksId != null) {
|
||||
connection.localUtils().traceDiscarded(stacksId);
|
||||
}
|
||||
@ -56,14 +55,14 @@ class TracingImpl extends ChannelOwner implements Tracing {
|
||||
boolean isLocal = !connection.isRemote;
|
||||
if (isLocal) {
|
||||
params.addProperty("mode", "entries");
|
||||
JsonObject json = sendMessage("tracingStopChunk", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("tracingStopChunk", params, NO_TIMEOUT).getAsJsonObject();
|
||||
JsonArray entries = json.getAsJsonArray("entries");
|
||||
connection.localUtils.zip(path, entries, stacksId, false, includeSources);
|
||||
return;
|
||||
}
|
||||
|
||||
params.addProperty("mode", "archive");
|
||||
JsonObject json = sendMessage("tracingStopChunk", params).getAsJsonObject();
|
||||
JsonObject json = sendMessage("tracingStopChunk", params, NO_TIMEOUT).getAsJsonObject();
|
||||
// The artifact may be missing if the browser closed while stopping tracing.
|
||||
if (!json.has("artifact")) {
|
||||
if (stacksId != null) {
|
||||
@ -88,7 +87,7 @@ class TracingImpl extends ChannelOwner implements Tracing {
|
||||
|
||||
@Override
|
||||
public void group(String name, GroupOptions options) {
|
||||
withLogging("Tracing.group", () -> groupImpl(name, options));
|
||||
groupImpl(name, options);
|
||||
}
|
||||
|
||||
private void groupImpl(String name, GroupOptions options) {
|
||||
@ -97,12 +96,12 @@ class TracingImpl extends ChannelOwner implements Tracing {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("name", name);
|
||||
sendMessage("tracingGroup", params);
|
||||
sendMessage("tracingGroup", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void groupEnd() {
|
||||
withLogging("Tracing.groupEnd", () -> sendMessage("tracingGroupEnd"));
|
||||
sendMessage("tracingGroupEnd");
|
||||
}
|
||||
|
||||
private void tracingStartChunk(String name, String title) {
|
||||
@ -113,7 +112,7 @@ class TracingImpl extends ChannelOwner implements Tracing {
|
||||
if (title != null) {
|
||||
params.addProperty("title", title);
|
||||
}
|
||||
JsonObject result = sendMessage("tracingStartChunk", params).getAsJsonObject();
|
||||
JsonObject result = sendMessage("tracingStartChunk", params, NO_TIMEOUT).getAsJsonObject();
|
||||
startCollectingStacks(result.get("traceName").getAsString());
|
||||
}
|
||||
|
||||
@ -135,7 +134,7 @@ class TracingImpl extends ChannelOwner implements Tracing {
|
||||
if (includeSources) {
|
||||
params.addProperty("sources", true);
|
||||
}
|
||||
sendMessage("tracingStart", params);
|
||||
sendMessage("tracingStart", params, NO_TIMEOUT);
|
||||
tracingStartChunk(options.name, options.title);
|
||||
}
|
||||
|
||||
|
@ -21,25 +21,22 @@ import com.microsoft.playwright.PlaywrightException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.microsoft.playwright.impl.Utils.globToRegex;
|
||||
import static com.microsoft.playwright.impl.Utils.toJsRegexFlags;
|
||||
|
||||
class UrlMatcher {
|
||||
private final String baseURL;
|
||||
public final String glob;
|
||||
public final Pattern pattern;
|
||||
public final Predicate<String> predicate;
|
||||
|
||||
static UrlMatcher forOneOf(URL baseUrl, Object object) {
|
||||
static UrlMatcher forOneOf(URL baseUrl, Object object, LocalUtils localUtils, boolean isWebSocketUrl) {
|
||||
if (object == null) {
|
||||
return new UrlMatcher(null, null, null, null);
|
||||
return new UrlMatcher(null, null, null);
|
||||
}
|
||||
if (object instanceof String) {
|
||||
return new UrlMatcher(baseUrl, (String) object);
|
||||
return UrlMatcher.forGlob(baseUrl, (String) object, localUtils, isWebSocketUrl);
|
||||
}
|
||||
if (object instanceof Pattern) {
|
||||
return new UrlMatcher((Pattern) object);
|
||||
@ -66,61 +63,32 @@ class UrlMatcher {
|
||||
}
|
||||
}
|
||||
|
||||
private static String normaliseUrl(String spec) {
|
||||
try {
|
||||
// Align with the Node.js URL parser which automatically adds a slash to the path if it is empty.
|
||||
URI url = new URI(spec);
|
||||
if (url.getScheme() != null &&
|
||||
Arrays.asList("http", "https", "ws", "wss").contains(url.getScheme()) &&
|
||||
url.getPath().isEmpty()) {
|
||||
return new URI(url.getScheme(), url.getAuthority(), "/", url.getQuery(), url.getFragment()).toString();
|
||||
}
|
||||
return url.toString();
|
||||
} catch (URISyntaxException e) {
|
||||
return spec;
|
||||
}
|
||||
}
|
||||
|
||||
UrlMatcher(URL baseURL, String glob) {
|
||||
this(baseURL, glob, null, null);
|
||||
static UrlMatcher forGlob(URL baseURL, String glob, LocalUtils localUtils, boolean isWebSocketUrl) {
|
||||
Pattern pattern = localUtils.globToRegex(glob, baseURL != null ? baseURL.toString() : null, isWebSocketUrl);
|
||||
return new UrlMatcher(glob, pattern, null);
|
||||
}
|
||||
|
||||
UrlMatcher(Pattern pattern) {
|
||||
this(null, null, pattern, null);
|
||||
this(null, pattern, null);
|
||||
}
|
||||
|
||||
UrlMatcher(Predicate<String> predicate) {
|
||||
this(null, null, null, predicate);
|
||||
this(null, null, predicate);
|
||||
}
|
||||
|
||||
private UrlMatcher(URL baseURL, String glob, Pattern pattern, Predicate<String> predicate) {
|
||||
this.baseURL = baseURL != null ? baseURL.toString() : null;
|
||||
private UrlMatcher(String glob, Pattern pattern, Predicate<String> predicate) {
|
||||
this.glob = glob;
|
||||
this.pattern = pattern;
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
boolean test(String value) {
|
||||
return testImpl(baseURL, pattern, predicate, glob, value);
|
||||
}
|
||||
|
||||
private static boolean testImpl(String baseURL, Pattern pattern, Predicate<String> predicate, String glob, String value) {
|
||||
if (pattern != null) {
|
||||
return pattern.matcher(value).find();
|
||||
}
|
||||
if (predicate != null) {
|
||||
return predicate.test(value);
|
||||
}
|
||||
if (glob != null) {
|
||||
if (!glob.startsWith("*")) {
|
||||
// Allow http(s) baseURL to match ws(s) urls.
|
||||
if (baseURL != null && Pattern.compile("^https?://").matcher(baseURL).find() && Pattern.compile("^wss?://").matcher(value).find()) {
|
||||
baseURL = baseURL.replaceFirst("^http", "ws");
|
||||
}
|
||||
glob = normaliseUrl(resolveUrl(baseURL, glob));
|
||||
}
|
||||
return Pattern.compile(globToRegex(glob)).matcher(value).find();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -157,10 +125,12 @@ class UrlMatcher {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (glob != null)
|
||||
return String.format("<glob pattern=\"%s\">", glob);
|
||||
if (pattern != null)
|
||||
return String.format("<regex pattern=\"%s\" flags=\"%s\">", pattern.pattern(), toJsRegexFlags(pattern));
|
||||
if (this.predicate != null)
|
||||
return "<predicate>";
|
||||
return String.format("<glob pattern=\"%s\">", glob);
|
||||
return "<true>";
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import com.microsoft.playwright.options.ClientCertificate;
|
||||
@ -32,11 +31,11 @@ import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.microsoft.playwright.impl.ChannelOwner.NO_TIMEOUT;
|
||||
import static com.microsoft.playwright.impl.Serialization.toJsonArray;
|
||||
import static java.nio.file.Files.readAllBytes;
|
||||
|
||||
@ -91,79 +90,6 @@ public class Utils {
|
||||
return convertType(f, (Class<T>) f.getClass());
|
||||
}
|
||||
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions#escaping
|
||||
static Set<Character> escapeGlobChars = new HashSet<>(Arrays.asList('$', '^', '+', '.', '*', '(', ')', '|', '\\', '?', '{', '}', '[', ']'));
|
||||
|
||||
static String globToRegex(String glob) {
|
||||
StringBuilder tokens = new StringBuilder();
|
||||
tokens.append('^');
|
||||
boolean inGroup = false;
|
||||
for (int i = 0; i < glob.length(); ++i) {
|
||||
char c = glob.charAt(i);
|
||||
if (c == '\\' && i + 1 < glob.length()) {
|
||||
char nextChar = glob.charAt(++i);
|
||||
if (escapeGlobChars.contains(nextChar)) {
|
||||
tokens.append('\\');
|
||||
}
|
||||
tokens.append(nextChar);
|
||||
continue;
|
||||
}
|
||||
if (c == '*') {
|
||||
boolean beforeDeep = i < 1 || glob.charAt(i - 1) == '/';
|
||||
int starCount = 1;
|
||||
while (i + 1 < glob.length() && glob.charAt(i + 1) == '*') {
|
||||
starCount++;
|
||||
i++;
|
||||
}
|
||||
boolean afterDeep = i + 1 >= glob.length() || glob.charAt(i + 1) == '/';
|
||||
boolean isDeep = starCount > 1 && beforeDeep && afterDeep;
|
||||
if (isDeep) {
|
||||
tokens.append("((?:[^/]*(?:\\/|$))*)");
|
||||
i++;
|
||||
} else {
|
||||
tokens.append("([^/]*)");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case '?':
|
||||
tokens.append('.');
|
||||
break;
|
||||
case '[':
|
||||
tokens.append('[');
|
||||
break;
|
||||
case ']':
|
||||
tokens.append(']');
|
||||
break;
|
||||
case '{':
|
||||
inGroup = true;
|
||||
tokens.append('(');
|
||||
break;
|
||||
case '}':
|
||||
inGroup = false;
|
||||
tokens.append(')');
|
||||
break;
|
||||
case ',':
|
||||
if (inGroup) {
|
||||
tokens.append('|');
|
||||
break;
|
||||
}
|
||||
tokens.append("\\").append(c);
|
||||
break;
|
||||
default:
|
||||
if (escapeGlobChars.contains(c)) {
|
||||
tokens.append('\\');
|
||||
}
|
||||
tokens.append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
tokens.append('$');
|
||||
return tokens.toString();
|
||||
}
|
||||
|
||||
static String mimeType(Path path) {
|
||||
String mimeType;
|
||||
try {
|
||||
@ -275,7 +201,7 @@ public class Utils {
|
||||
items.add(item);
|
||||
}
|
||||
tempFilesParams.add("items", items);
|
||||
return context.sendMessage("createTempFiles", tempFilesParams).getAsJsonObject();
|
||||
return context.sendMessage("createTempFiles", tempFilesParams, NO_TIMEOUT).getAsJsonObject();
|
||||
}
|
||||
|
||||
static void checkFilePayloadSize(FilePayload[] files) {
|
||||
|
@ -30,7 +30,6 @@ class VideoImpl implements Video {
|
||||
|
||||
VideoImpl(PageImpl page) {
|
||||
this.page = page;
|
||||
BrowserImpl browser = page.context().browser();
|
||||
}
|
||||
|
||||
void setArtifact(ArtifactImpl artifact) {
|
||||
@ -44,39 +43,33 @@ class VideoImpl implements Video {
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
page.withLogging("Video.delete", () -> {
|
||||
try {
|
||||
waitForArtifact().delete();
|
||||
} catch (PlaywrightException e) {
|
||||
}
|
||||
});
|
||||
try {
|
||||
waitForArtifact().delete();
|
||||
} catch (PlaywrightException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path path() {
|
||||
return page.withLogging("Video.path", () -> {
|
||||
if (page.connection.isRemote) {
|
||||
throw new PlaywrightException("Path is not available when using browserType.connect(). Use saveAs() to save a local copy.");
|
||||
}
|
||||
try {
|
||||
return Paths.get(waitForArtifact().initializer.get("absolutePath").getAsString());
|
||||
} catch (PlaywrightException e) {
|
||||
throw new PlaywrightException("Page did not produce any video frames", e);
|
||||
}
|
||||
});
|
||||
if (page.connection.isRemote) {
|
||||
throw new PlaywrightException("Path is not available when using browserType.connect(). Use saveAs() to save a local copy.");
|
||||
}
|
||||
try {
|
||||
return Paths.get(waitForArtifact().initializer.get("absolutePath").getAsString());
|
||||
} catch (PlaywrightException e) {
|
||||
throw new PlaywrightException("Page did not produce any video frames", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAs(Path path) {
|
||||
page.withLogging("Video.saveAs", () -> {
|
||||
if (!page.isClosed()) {
|
||||
throw new PlaywrightException("Page is not yet closed. Close the page prior to calling saveAs");
|
||||
}
|
||||
try {
|
||||
waitForArtifact().saveAs(path);
|
||||
} catch (PlaywrightException e) {
|
||||
throw new PlaywrightException("Page did not produce any video frames", e);
|
||||
}
|
||||
});
|
||||
if (!page.isClosed()) {
|
||||
throw new PlaywrightException("Page is not yet closed. Close the page prior to calling saveAs");
|
||||
}
|
||||
try {
|
||||
waitForArtifact().saveAs(path);
|
||||
} catch (PlaywrightException e) {
|
||||
throw new PlaywrightException("Page did not produce any video frames", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,23 +38,21 @@ public class WaitForEventLogger<T> implements Supplier<T>, Logger {
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
return channel.withLogging(apiName, () -> {
|
||||
{
|
||||
JsonObject info = new JsonObject();
|
||||
info.addProperty("phase", "before");
|
||||
sendWaitForEventInfo(info);
|
||||
}
|
||||
{
|
||||
JsonObject info = new JsonObject();
|
||||
info.addProperty("phase", "after");
|
||||
try {
|
||||
return supplier.apply(this);
|
||||
} catch (RuntimeException e) {
|
||||
info.addProperty("error", e.getMessage());
|
||||
throw e;
|
||||
} finally {
|
||||
sendWaitForEventInfo(info);
|
||||
}
|
||||
});
|
||||
info.addProperty("phase", "before");
|
||||
sendWaitForEventInfo(info);
|
||||
}
|
||||
JsonObject info = new JsonObject();
|
||||
info.addProperty("phase", "after");
|
||||
try {
|
||||
return supplier.apply(this);
|
||||
} catch (RuntimeException e) {
|
||||
info.addProperty("error", e.getMessage());
|
||||
throw e;
|
||||
} finally {
|
||||
sendWaitForEventInfo(info);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -69,7 +69,6 @@ class WebSocketRouteImpl extends ChannelOwner implements WebSocketRoute {
|
||||
|
||||
WebSocketRouteImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
markAsInternalType();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -129,7 +128,11 @@ class WebSocketRouteImpl extends ChannelOwner implements WebSocketRoute {
|
||||
return;
|
||||
}
|
||||
// Ensure that websocket is "open" and can send messages without an actual server connection.
|
||||
sendMessageAsync("ensureOpened");
|
||||
try {
|
||||
sendMessageAsync("ensureOpened");
|
||||
} catch (PlaywrightException e) {
|
||||
// If this happens after the page has been closed, ignore the error.
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -71,25 +71,21 @@ class WorkerImpl extends ChannelOwner implements Worker {
|
||||
|
||||
@Override
|
||||
public Object evaluate(String pageFunction, Object arg) {
|
||||
return withLogging("Worker.evaluate", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpression", params);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpression", params, NO_TIMEOUT);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSHandle evaluateHandle(String pageFunction, Object arg) {
|
||||
return withLogging("Worker.evaluateHandle", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpressionHandle", params);
|
||||
return connection.getExistingObject(json.getAsJsonObject().getAsJsonObject("handle").get("guid").getAsString());
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpressionHandle", params, NO_TIMEOUT);
|
||||
return connection.getExistingObject(json.getAsJsonObject().getAsJsonObject("handle").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -42,7 +42,7 @@ class WritableStream extends ChannelOwner {
|
||||
ByteBuffer buffer = ByteBuffer.wrap(b, off, len);
|
||||
ByteBuffer encoded = Base64.getEncoder().encode(buffer);
|
||||
params.addProperty("binary", new String(encoded.array(), StandardCharsets.UTF_8));
|
||||
sendMessage("write", params);
|
||||
sendMessage("write", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -48,6 +48,12 @@ public class Cookie {
|
||||
* Optional.
|
||||
*/
|
||||
public SameSiteAttribute sameSite;
|
||||
/**
|
||||
* For partitioned third-party cookies (aka <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/Privacy/Guides/Privacy_sandbox/Partitioned_cookies">CHIPS</a>), the
|
||||
* partition key. Optional.
|
||||
*/
|
||||
public String partitionKey;
|
||||
|
||||
public Cookie(String name, String value) {
|
||||
this.name = name;
|
||||
@ -103,4 +109,13 @@ public class Cookie {
|
||||
this.sameSite = sameSite;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* For partitioned third-party cookies (aka <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/Privacy/Guides/Privacy_sandbox/Partitioned_cookies">CHIPS</a>), the
|
||||
* partition key. Optional.
|
||||
*/
|
||||
public Cookie setPartitionKey(String partitionKey) {
|
||||
this.partitionKey = partitionKey;
|
||||
return this;
|
||||
}
|
||||
}
|
@ -23,6 +23,7 @@ import java.net.InetSocketAddress;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Function;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import static com.microsoft.playwright.Utils.copy;
|
||||
@ -40,6 +41,7 @@ public class Server implements HttpHandler {
|
||||
private final Map<String, String> csp = Collections.synchronizedMap(new HashMap<>());
|
||||
private final Map<String, HttpHandler> routes = Collections.synchronizedMap(new HashMap<>());
|
||||
private final Set<String> gzipRoutes = Collections.synchronizedSet(new HashSet<>());
|
||||
private Function<String, InputStream> resourceProvider;
|
||||
|
||||
private static class Auth {
|
||||
public final String user;
|
||||
@ -75,6 +77,8 @@ public class Server implements HttpHandler {
|
||||
server.createContext("/", this);
|
||||
server.setExecutor(null); // creates a default executor
|
||||
server.start();
|
||||
// Resources from "src/test/resources/" are copied to "resources/" directory in the jar.
|
||||
resourceProvider = path -> Server.class.getClassLoader().getResourceAsStream("resources" + path);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
@ -93,6 +97,10 @@ public class Server implements HttpHandler {
|
||||
gzipRoutes.add(path);
|
||||
}
|
||||
|
||||
void setResourceProvider(Function<String, InputStream> resourceProvider) {
|
||||
this.resourceProvider = resourceProvider;
|
||||
}
|
||||
|
||||
static class Request {
|
||||
public final String url;
|
||||
public final String method;
|
||||
@ -187,18 +195,16 @@ public class Server implements HttpHandler {
|
||||
path = "/index.html";
|
||||
}
|
||||
|
||||
// Resources from "src/test/resources/" are copied to "resources/" directory in the jar.
|
||||
String resourcePath = "resources" + path;
|
||||
InputStream resource = getClass().getClassLoader().getResourceAsStream(resourcePath);
|
||||
InputStream resource = resourceProvider.apply(path);
|
||||
if (resource == null) {
|
||||
exchange.getResponseHeaders().add("Content-Type", "text/plain");
|
||||
exchange.sendResponseHeaders(404, 0);
|
||||
try (Writer writer = new OutputStreamWriter(exchange.getResponseBody())) {
|
||||
writer.write("File not found: " + resourcePath);
|
||||
writer.write("File not found: " + path);
|
||||
}
|
||||
return;
|
||||
}
|
||||
exchange.getResponseHeaders().add("Content-Type", mimeType(new File(resourcePath)));
|
||||
exchange.getResponseHeaders().add("Content-Type", mimeType(new File(path)));
|
||||
ByteArrayOutputStream body = new ByteArrayOutputStream();
|
||||
OutputStream output = body;
|
||||
if (gzipRoutes.contains(path)) {
|
||||
@ -218,7 +224,7 @@ public class Server implements HttpHandler {
|
||||
}
|
||||
long contentLength = body.size();
|
||||
// -1 means no body, 0 means chunked encoding.
|
||||
exchange.sendResponseHeaders(200, contentLength == 0 ? -1 : contentLength);
|
||||
exchange.sendResponseHeaders(200, (contentLength == 0 || exchange.getRequestMethod().equals("HEAD")) ? -1 : contentLength);
|
||||
if (contentLength > 0) {
|
||||
exchange.getResponseBody().write(body.toByteArray());
|
||||
}
|
||||
|
@ -45,12 +45,12 @@ public class TestBase {
|
||||
static final boolean isMac = Utils.getOS() == Utils.OS.MAC;
|
||||
static final boolean isLinux = Utils.getOS() == Utils.OS.LINUX;
|
||||
static final boolean isWindows = Utils.getOS() == Utils.OS.WINDOWS;
|
||||
static final boolean headful;
|
||||
static final boolean headed;
|
||||
static final SameSiteAttribute defaultSameSiteCookieValue;
|
||||
|
||||
static {
|
||||
String headfulEnv = System.getenv("HEADFUL");
|
||||
headful = headfulEnv != null && !"0".equals(headfulEnv) && !"false".equals(headfulEnv);
|
||||
String headedEnv = System.getenv("HEADED");
|
||||
headed = headedEnv != null && !"0".equals(headedEnv) && !"false".equals(headedEnv);
|
||||
defaultSameSiteCookieValue = initSameSiteAttribute();
|
||||
}
|
||||
|
||||
@ -58,8 +58,8 @@ public class TestBase {
|
||||
Page page;
|
||||
BrowserContext context;
|
||||
|
||||
static boolean isHeadful() {
|
||||
return headful;
|
||||
static boolean isHeaded() {
|
||||
return headed;
|
||||
}
|
||||
|
||||
static boolean isChromium() {
|
||||
@ -81,7 +81,7 @@ public class TestBase {
|
||||
static BrowserType.LaunchOptions createLaunchOptions() {
|
||||
BrowserType.LaunchOptions options;
|
||||
options = new BrowserType.LaunchOptions();
|
||||
options.headless = !headful;
|
||||
options.headless = !headed;
|
||||
options.channel = getBrowserChannelFromEnv();
|
||||
return options;
|
||||
}
|
||||
|
@ -27,13 +27,12 @@ public class TestBrowserContextCredentials extends TestBase {
|
||||
|
||||
static boolean isChromiumHeadedLike() {
|
||||
// --headless=new, the default in all Chromium channels, is like headless.
|
||||
return isChromium() && (isHeadful() || getBrowserChannelFromEnv() != null);
|
||||
return isChromium() && (isHeaded() || getBrowserChannelFromEnv() != null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisabledIf(value="isChromiumHeadedLike", disabledReason="fail")
|
||||
void shouldFailWithoutCredentials() {
|
||||
System.out.println("channel2 " + getBrowserChannelFromEnv());
|
||||
server.setAuth("/empty.html", "user", "pass");
|
||||
Response response = page.navigate(server.EMPTY_PAGE);
|
||||
assertEquals(401, response.status());
|
||||
|
@ -436,7 +436,7 @@ public class TestBrowserContextFetch extends TestBase {
|
||||
PlaywrightException e = assertThrows(PlaywrightException.class, () -> {
|
||||
context.request().get(server.PREFIX + "/slow", RequestOptions.create().setTimeout(100));
|
||||
});
|
||||
assertTrue(e.getMessage().contains("Request timed out after 100ms"), e.getMessage());
|
||||
assertTrue(e.getMessage().contains("Timeout 100ms exceeded"), e.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -468,7 +468,7 @@ public class TestBrowserContextFetch extends TestBase {
|
||||
|
||||
context.setDefaultTimeout(100);
|
||||
PlaywrightException e = assertThrows(PlaywrightException.class, () -> context.request().get(server.PREFIX + "/redirect"));
|
||||
assertTrue(e.getMessage().contains("Request timed out after 100ms"), e.getMessage());
|
||||
assertTrue(e.getMessage().contains("Timeout 100ms exceeded"), e.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import com.microsoft.playwright.options.HarContentPolicy;
|
||||
import com.microsoft.playwright.options.HarMode;
|
||||
import com.microsoft.playwright.options.HarNotFound;
|
||||
import com.microsoft.playwright.options.RouteFromHarUpdateContentPolicy;
|
||||
@ -40,7 +39,6 @@ import static com.microsoft.playwright.Utils.copy;
|
||||
import static com.microsoft.playwright.Utils.extractZip;
|
||||
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
|
||||
import static com.microsoft.playwright.options.HarContentPolicy.ATTACH;
|
||||
import static com.microsoft.playwright.options.HarContentPolicy.EMBED;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestBrowserContextHar extends TestBase {
|
||||
|
@ -129,12 +129,12 @@ public class TestBrowserContextProxy extends TestBase {
|
||||
context.close();
|
||||
}
|
||||
|
||||
static boolean isChromiumHeadful() {
|
||||
return isChromium() && isHeadful();
|
||||
static boolean isChromiumHeaded() {
|
||||
return isChromium() && isHeaded();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisabledIf(value="isChromiumHeadful", disabledReason="fixme")
|
||||
@DisabledIf(value="isChromiumHeaded", disabledReason="fixme")
|
||||
void shouldExcludePatterns() {
|
||||
server.setRoute("/target.html", exchange -> {
|
||||
exchange.sendResponseHeaders(200, 0);
|
||||
|
@ -174,10 +174,19 @@ public class TestBrowserContextStorageState extends TestBase {
|
||||
@Test
|
||||
void shouldSupportIndexedDB() {
|
||||
page.navigate(server.PREFIX + "/to-do-notifications/index.html");
|
||||
|
||||
assertThat(page.locator("#notifications")).matchesAriaSnapshot(
|
||||
" - list:\n" +
|
||||
" - listitem: Database initialised."
|
||||
);
|
||||
page.locator("label:has-text('Task title')").fill("Pet the cat");
|
||||
page.locator("label:has-text('Hours')").fill("1");
|
||||
page.locator("label:has-text('Mins')").fill("1");
|
||||
page.locator("text=Add Task").click();
|
||||
assertThat(page.locator("#notifications")).matchesAriaSnapshot(
|
||||
" - list:\n" +
|
||||
" - listitem: \"Transaction completed: database modification finished.\""
|
||||
);
|
||||
|
||||
String storageState = page.context().storageState(new BrowserContext.StorageStateOptions().setIndexedDB(true));
|
||||
assertJsonEquals("{\"cookies\":[],\"origins\":[\n" +
|
||||
@ -195,14 +204,18 @@ public class TestBrowserContextStorageState extends TestBase {
|
||||
" \"keyPath\": \"taskTitle\",\n" +
|
||||
" \"records\": [\n" +
|
||||
" {\n" +
|
||||
" \"value\": {\n" +
|
||||
" \"day\": \"01\",\n" +
|
||||
" \"hours\": \"1\",\n" +
|
||||
" \"minutes\": \"1\",\n" +
|
||||
" \"month\": \"January\",\n" +
|
||||
" \"notified\": \"no\",\n" +
|
||||
" \"taskTitle\": \"Pet the cat\",\n" +
|
||||
" \"year\": \"2025\"\n" +
|
||||
" \"valueEncoded\": {\n" +
|
||||
" \"id\": 1,\n" +
|
||||
" \"o\": [\n" +
|
||||
" {\"k\": \"taskTitle\", \"v\": \"Pet the cat\"},\n" +
|
||||
" {\"k\": \"hours\", \"v\": \"1\"},\n" +
|
||||
" {\"k\": \"minutes\", \"v\": \"1\"},\n" +
|
||||
" {\"k\": \"day\", \"v\": \"01\"},\n" +
|
||||
" {\"k\": \"month\", \"v\": \"January\"},\n" +
|
||||
" {\"k\": \"year\", \"v\": \"2025\"},\n" +
|
||||
" {\"k\": \"notified\", \"v\": \"no\"},\n" +
|
||||
" {\"k\": \"signature\", \"v\": { \"ta\": {\"b\":\"c2lnbmVkIGJ5IHNpbW9u\",\"k\":\"ui8\"}}}\n" +
|
||||
" ]\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
" ],\n" +
|
||||
|
@ -57,18 +57,35 @@ public class TestChromiumTracing extends TestBase {
|
||||
}
|
||||
}
|
||||
|
||||
private static void rafraf(Page page) {
|
||||
int count = 2;
|
||||
for (int i = 0; i < count; i++) {
|
||||
page.evaluate("() => new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f)))");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRunWithCustomCategoriesIfProvided(@TempDir Path tempDir) throws IOException {
|
||||
try (Page page = browser.newPage()) {
|
||||
Path outputTraceFile = tempDir.resolve("trace.json");
|
||||
browser.startTracing(page, new Browser.StartTracingOptions()
|
||||
.setPath(outputTraceFile)
|
||||
.setCategories(asList("disabled-by-default-v8.cpu_profiler.hires")));
|
||||
.setCategories(asList("disabled-by-default-cc.debug")));
|
||||
rafraf(page);
|
||||
browser.stopTracing();
|
||||
try (FileReader fileReader = new FileReader(outputTraceFile.toFile())) {
|
||||
JsonObject traceJson = new Gson().fromJson(fileReader, JsonObject.class);
|
||||
assertTrue(traceJson.getAsJsonObject("metadata").get("trace-config")
|
||||
.getAsString().contains("disabled-by-default-v8.cpu_profiler.hires"));
|
||||
// NOTE: trace-config is deprecated as per http://crrev.com/c/6628182
|
||||
boolean hasTraceConfig =
|
||||
traceJson.getAsJsonObject("metadata").get("trace-config") != null
|
||||
&& traceJson.getAsJsonObject("metadata").get("trace-config").getAsString().contains("disabled-by-default-cc.debug");
|
||||
boolean hasTraceEvents = traceJson.getAsJsonArray("traceEvents").asList().stream()
|
||||
.anyMatch(event -> {
|
||||
JsonObject eventObj = (JsonObject) event;
|
||||
return eventObj.has("cat") &&
|
||||
eventObj.get("cat").getAsString().equals("disabled-by-default-cc.debug");
|
||||
});
|
||||
assertTrue(hasTraceConfig || hasTraceEvents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -125,8 +125,8 @@ public class TestClientCertificates extends TestBase {
|
||||
.setIgnoreHTTPSErrors(true) // TODO: remove once we can pass a custom CA.
|
||||
.setClientCertificates(asList(
|
||||
new ClientCertificate(customServer.origin)
|
||||
.setPfxPath(asset("client-certificates/client/trusted/client_keystore.p12"))
|
||||
.setPassphrase("passphrase")));
|
||||
.setPfxPath(asset("client-certificates/client/trusted/keystore.p12"))
|
||||
.setPassphrase("")));
|
||||
|
||||
APIRequestContext request = playwright.request().newContext(requestOptions);
|
||||
APIResponse response = request.get(customServer.url);
|
||||
|
@ -128,7 +128,7 @@ public class TestDefaultBrowserContext2 extends TestBase {
|
||||
|
||||
@Test
|
||||
void shouldSupportExtraHTTPHeadersOption() throws ExecutionException, InterruptedException {
|
||||
// TODO: test.flaky(browserName === "firefox" && headful && platform === "linux", "Intermittent timeout on bots");
|
||||
// TODO: test.flaky(browserName === "firefox" && headed && platform === "linux", "Intermittent timeout on bots");
|
||||
Page page = launchPersistent(new BrowserType.LaunchPersistentContextOptions().setExtraHTTPHeaders(mapOf("foo", "bar")));
|
||||
Future<Server.Request> request = server.futureRequest("/empty.html");
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
@ -307,4 +307,19 @@ public class TestDefaultBrowserContext2 extends TestBase {
|
||||
assertTrue(Files.list(userDataDir).count() > 0);
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExposeBrowser() {
|
||||
Page page = launchPersistent();
|
||||
BrowserContext context = page.context();
|
||||
Browser browser = context.browser();
|
||||
assertFalse(browser.version().isEmpty());
|
||||
Page page2 = browser.newPage();
|
||||
page2.navigate("data:text/html,<html><title>Title</title></html>");
|
||||
assertEquals("Title", page2.title());
|
||||
browser.close();
|
||||
assertEquals(0, context.pages().size());
|
||||
// Next line should not throw.
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
@ -93,18 +93,12 @@ public class TestDownload extends TestBase {
|
||||
assertTrue(Files.exists(path));
|
||||
byte[] bytes = readAllBytes(path);
|
||||
assertEquals("Hello world", new String(bytes, UTF_8));
|
||||
if (isChromium()) {
|
||||
assertNotNull(error[0]);
|
||||
assertTrue(error[0].getMessage().contains("net::ERR_ABORTED"));
|
||||
assertEquals("about:blank", page.url());
|
||||
} else if (isWebKit()) {
|
||||
assertNotNull(error[0]);
|
||||
assertTrue(error[0].getMessage().contains("Download is starting"));
|
||||
assertEquals("about:blank", page.url());
|
||||
} else {
|
||||
assertNotNull(error[0]);
|
||||
assertNotNull(error[0]);
|
||||
if (!chromiumVersionLessThan(browser.version(), "140.0.0.0")) {
|
||||
assertTrue(error[0].getMessage().contains("Download is starting"));
|
||||
}
|
||||
if (!isFirefox())
|
||||
assertEquals("about:blank", page.url());
|
||||
page.close();
|
||||
}
|
||||
|
||||
@ -347,14 +341,14 @@ public class TestDownload extends TestBase {
|
||||
}
|
||||
|
||||
|
||||
static boolean isChromiumHeadful() {
|
||||
return isChromium() && isHeadful();
|
||||
static boolean isChromiumHeaded() {
|
||||
return isChromium() && isHeaded();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisabledIf(value="isChromiumHeadful", disabledReason="fixme")
|
||||
@DisabledIf(value="isChromiumHeaded", disabledReason="fixme")
|
||||
void shouldReportNewWindowDownloads() throws IOException {
|
||||
// TODO: - the test fails in headful Chromium as the popup page gets closed along
|
||||
// TODO: - the test fails in headed Chromium as the popup page gets closed along
|
||||
// with the session before download completed event arrives.
|
||||
// - WebKit doesn't close the popup page
|
||||
Page page = browser.newPage(new Browser.NewPageOptions().setAcceptDownloads(true));
|
||||
|
@ -26,12 +26,12 @@ import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestElementHandleBoundingBox extends TestBase {
|
||||
|
||||
static boolean isFirefoxHeadful() {
|
||||
return isFirefox() && isHeadful();
|
||||
static boolean isFirefoxHeaded() {
|
||||
return isFirefox() && isHeaded();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisabledIf(value="isFirefoxHeadful", disabledReason="fail")
|
||||
@DisabledIf(value="isFirefoxHeaded", disabledReason="fail")
|
||||
void shouldWork() {
|
||||
page.setViewportSize(500, 500);
|
||||
page.navigate(server.PREFIX + "/grid.html");
|
||||
|
@ -28,7 +28,7 @@ public class TestElementHandleSelectText extends TestBase {
|
||||
ElementHandle textarea = page.querySelector("textarea");
|
||||
textarea.evaluate("textarea => textarea.value = 'some value'");
|
||||
textarea.selectText();
|
||||
if (isFirefox() || isWebKit()) {
|
||||
if (isFirefox()) {
|
||||
assertEquals(0, textarea.evaluate("el => el.selectionStart"));
|
||||
assertEquals(10, textarea.evaluate("el => el.selectionEnd"));
|
||||
} else {
|
||||
@ -42,7 +42,7 @@ public class TestElementHandleSelectText extends TestBase {
|
||||
ElementHandle input = page.querySelector("input");
|
||||
input.evaluate("input => input.value = 'some value'");
|
||||
input.selectText();
|
||||
if (isFirefox() || isWebKit()) {
|
||||
if (isFirefox()) {
|
||||
assertEquals(0, input.evaluate("el => el.selectionStart"));
|
||||
assertEquals(10, input.evaluate("el => el.selectionEnd"));
|
||||
} else {
|
||||
|
@ -17,6 +17,7 @@
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.microsoft.playwright.APIRequest.NewContextOptions;
|
||||
import com.microsoft.playwright.options.HttpCredentials;
|
||||
import com.microsoft.playwright.options.HttpCredentialsSend;
|
||||
import com.microsoft.playwright.options.HttpHeader;
|
||||
@ -37,6 +38,8 @@ import static java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestGlobalFetch extends TestBase {
|
||||
private static final List<String> HTTP_METHODS = asList("GET", "PUT", "POST", "OPTIONS", "HEAD", "PATCH");
|
||||
|
||||
@Test
|
||||
void shouldHaveJavaInDefaultUesrAgent() throws ExecutionException, InterruptedException {
|
||||
APIRequestContext request = playwright.request().newContext(new APIRequest.NewContextOptions());
|
||||
@ -186,7 +189,7 @@ public class TestGlobalFetch extends TestBase {
|
||||
APIRequestContext request = playwright.request().newContext(new APIRequest.NewContextOptions().setTimeout(100));
|
||||
server.setRoute("/empty.html", exchange -> {});
|
||||
PlaywrightException e = assertThrows(PlaywrightException.class, () -> request.get(server.EMPTY_PAGE));
|
||||
assertTrue(e.getMessage().contains("Request timed out after 100ms"), e.getMessage());
|
||||
assertTrue(e.getMessage().contains("Timeout 100ms exceeded"), e.getMessage());
|
||||
}
|
||||
|
||||
|
||||
@ -337,7 +340,7 @@ public class TestGlobalFetch extends TestBase {
|
||||
for (String method : new String[] {"head", "put", "trace"}) {
|
||||
server.setRoute("/empty.html", exchange -> {
|
||||
exchange.getResponseHeaders().set("Content-type", "text/plain");
|
||||
exchange.sendResponseHeaders(404, 10);
|
||||
exchange.sendResponseHeaders(404, exchange.getRequestMethod().equals("HEAD") ? -1 : 10);
|
||||
try (Writer writer = new OutputStreamWriter(exchange.getResponseBody())) {
|
||||
writer.write("Not found.");
|
||||
}
|
||||
@ -358,7 +361,7 @@ public class TestGlobalFetch extends TestBase {
|
||||
server.setRedirect("/b/c/redirect4", "/simple.json");
|
||||
|
||||
APIRequestContext request = playwright.request().newContext();
|
||||
for (String method : new String[] {"GET", "PUT", "POST", "OPTIONS", "HEAD", "PATCH"}) {
|
||||
for (String method : HTTP_METHODS) {
|
||||
for (int maxRedirects = 1; maxRedirects < 4; maxRedirects++) {
|
||||
int currMaxRedirects = maxRedirects;
|
||||
PlaywrightException exception = assertThrows(PlaywrightException.class,
|
||||
@ -370,13 +373,69 @@ public class TestGlobalFetch extends TestBase {
|
||||
request.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUseMaxRedirectsFromFetchWhenProvidedOverridingNewContext() {
|
||||
server.setRedirect("/a/redirect1", "/b/c/redirect2");
|
||||
server.setRedirect("/b/c/redirect2", "/b/c/redirect3");
|
||||
server.setRedirect("/b/c/redirect3", "/b/c/redirect4");
|
||||
server.setRedirect("/b/c/redirect4", "/simple.json");
|
||||
|
||||
APIRequestContext request = playwright.request().newContext(new NewContextOptions().setMaxRedirects(1));
|
||||
for (String method : HTTP_METHODS) {
|
||||
APIResponse response = request.fetch(server.PREFIX + "/a/redirect1",
|
||||
RequestOptions.create().setMethod(method).setMaxRedirects(4));
|
||||
assertEquals(200, response.status());
|
||||
}
|
||||
request.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFollowRedirectsUpToMaxRedirectsLimitSetInNewContext() {
|
||||
server.setRedirect("/a/redirect1", "/b/c/redirect2");
|
||||
server.setRedirect("/b/c/redirect2", "/b/c/redirect3");
|
||||
server.setRedirect("/b/c/redirect3", "/b/c/redirect4");
|
||||
server.setRedirect("/b/c/redirect4", "/simple.json");
|
||||
|
||||
for (String method : HTTP_METHODS) {
|
||||
for (int maxRedirects = 1; maxRedirects <= 4; maxRedirects++) {
|
||||
int currMaxRedirects = maxRedirects;
|
||||
APIRequestContext request = playwright.request().newContext(new NewContextOptions().setMaxRedirects(currMaxRedirects));
|
||||
if (maxRedirects < 4) {
|
||||
PlaywrightException exception = assertThrows(PlaywrightException.class,
|
||||
() -> request.fetch(server.PREFIX + "/a/redirect1",
|
||||
RequestOptions.create().setMethod(method)));
|
||||
assertTrue(exception.getMessage().contains("Max redirect count exceeded"), exception.getMessage());
|
||||
} else {
|
||||
APIResponse response = request.fetch(server.PREFIX + "/a/redirect1", RequestOptions.create().setMethod(method));
|
||||
assertEquals(200, response.status());
|
||||
}
|
||||
request.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotFollowRedirectsWhenMaxRedirectsIsSetTo0InNewContext() {
|
||||
server.setRedirect("/a/redirect1", "/b/c/redirect2");
|
||||
server.setRedirect("/b/c/redirect2", "/simple.json");
|
||||
|
||||
APIRequestContext request = playwright.request().newContext(new NewContextOptions().setMaxRedirects(0));
|
||||
for (String method : HTTP_METHODS) {
|
||||
APIResponse response = request.fetch(server.PREFIX + "/a/redirect1",
|
||||
RequestOptions.create().setMethod(method));
|
||||
assertEquals("/b/c/redirect2", response.headers().get("location"));
|
||||
assertEquals(302, response.status());
|
||||
}
|
||||
request.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotFollowRedirectsWhenMaxRedirectsIsSetTo0() {
|
||||
server.setRedirect("/a/redirect1", "/b/c/redirect2");
|
||||
server.setRedirect("/b/c/redirect2", "/simple.json");
|
||||
|
||||
APIRequestContext request = playwright.request().newContext();
|
||||
for (String method : new String[] {"GET", "PUT", "POST", "OPTIONS", "HEAD", "PATCH"}) {
|
||||
for (String method : HTTP_METHODS) {
|
||||
APIResponse response = request.fetch(server.PREFIX + "/a/redirect1",
|
||||
RequestOptions.create().setMethod(method).setMaxRedirects(0));
|
||||
assertEquals("/b/c/redirect2", response.headers().get("location"));
|
||||
@ -391,7 +450,7 @@ public class TestGlobalFetch extends TestBase {
|
||||
server.setRedirect("/b/c/redirect2", "/simple.json");
|
||||
|
||||
APIRequestContext request = playwright.request().newContext();
|
||||
for (String method : new String[] {"GET", "PUT", "POST", "OPTIONS", "HEAD", "PATCH"}) {
|
||||
for (String method : HTTP_METHODS) {
|
||||
PlaywrightException exception = assertThrows(PlaywrightException.class,
|
||||
() -> request.fetch(server.PREFIX + "/a/redirect1",
|
||||
RequestOptions.create().setMethod(method).setMaxRedirects(-1)));
|
||||
|
@ -1,88 +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.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledIf;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
|
||||
import static com.microsoft.playwright.Utils.mapOf;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestLaunch extends TestBase {
|
||||
|
||||
@Override
|
||||
@BeforeAll
|
||||
// Hide base class method to not launch browser.
|
||||
void launchBrowser() {
|
||||
}
|
||||
|
||||
@Override
|
||||
void createContextAndPage() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Test
|
||||
void passEnvVar() {
|
||||
BrowserType.LaunchOptions options = new BrowserType.LaunchOptions();
|
||||
options.setEnv(mapOf("DEBUG", "pw:protocol"));
|
||||
launchBrowser(options);
|
||||
}
|
||||
|
||||
public static boolean canRunHeaded() {
|
||||
// On linux headed browser requires xvfb.
|
||||
return isHeadful() || isMac || isWindows;
|
||||
}
|
||||
|
||||
public static boolean canRunExtensionTest() {
|
||||
return canRunHeaded() && isChromium();
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledIf(value="com.microsoft.playwright.TestLaunch#canRunExtensionTest", disabledReason="Only Chromium Headed")
|
||||
void shouldReturnBackgroundPages(@TempDir Path tmpDir) throws IOException {
|
||||
Path profileDir = tmpDir.resolve("profile");
|
||||
Files.createDirectories(profileDir);
|
||||
String extensionPath = Paths.get("src/test/resources/simple-extension").toAbsolutePath().toString();
|
||||
initBrowserType();
|
||||
BrowserContext context = browserType.launchPersistentContext(profileDir, new BrowserType.LaunchPersistentContextOptions()
|
||||
.setHeadless(false)
|
||||
.setArgs(asList(
|
||||
"--disable-extensions-except=" + extensionPath,
|
||||
"--load-extension=" + extensionPath
|
||||
)));
|
||||
List<Page> backgroundPages = context.backgroundPages();
|
||||
context.onBackgroundPage(page1 -> backgroundPages.add(page1));
|
||||
context.waitForCondition(() -> !backgroundPages.isEmpty(),
|
||||
new BrowserContext.WaitForConditionOptions().setTimeout(10_000));
|
||||
Page backgroundPage = backgroundPages.get(0);
|
||||
assertNotNull(backgroundPage);
|
||||
assertTrue(context.backgroundPages().contains(backgroundPage));
|
||||
assertFalse(context.pages().contains(backgroundPage));
|
||||
context.close();
|
||||
assertEquals(0, context.pages().size());
|
||||
assertEquals(0, context.backgroundPages().size());
|
||||
}
|
||||
}
|
@ -18,10 +18,12 @@ package com.microsoft.playwright;
|
||||
|
||||
import com.microsoft.playwright.assertions.LocatorAssertions;
|
||||
import com.microsoft.playwright.assertions.PlaywrightAssertions;
|
||||
import com.microsoft.playwright.assertions.LocatorAssertions.ContainsClassOptions;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.opentest4j.AssertionFailedError;
|
||||
import org.opentest4j.ValueWrapper;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.microsoft.playwright.Utils.mapOf;
|
||||
@ -1068,7 +1070,7 @@ public class TestLocatorAssertions extends TestBase {
|
||||
Locator locator = page.locator("div");
|
||||
PlaywrightAssertions.setDefaultAssertionTimeout(1000);
|
||||
AssertionFailedError exception = assertThrows(AssertionFailedError.class, () -> assertThat(locator).hasText("foo"));
|
||||
assertTrue(exception.getMessage().contains("Locator.expect with timeout 1000ms"), exception.getMessage());
|
||||
assertTrue(exception.getMessage().contains("Assert \"hasText\" with timeout 1000ms"), exception.getMessage());
|
||||
// Restore default.
|
||||
PlaywrightAssertions.setDefaultAssertionTimeout(5_000);
|
||||
}
|
||||
@ -1092,4 +1094,49 @@ public class TestLocatorAssertions extends TestBase {
|
||||
// Restore default.
|
||||
PlaywrightAssertions.setDefaultAssertionTimeout(5_000);
|
||||
}
|
||||
|
||||
@Test
|
||||
void containsClassPass() {
|
||||
page.setContent("<div class='foo bar baz'></div>");
|
||||
Locator locator = page.locator("div");
|
||||
assertThat(locator).containsClass("");
|
||||
assertThat(locator).containsClass("bar");
|
||||
assertThat(locator).containsClass("baz bar");
|
||||
assertThat(locator).containsClass(" bar foo ");
|
||||
assertThat(locator).not().containsClass(" baz not-matching");
|
||||
}
|
||||
|
||||
@Test
|
||||
void containsClassPassWithSvgs() {
|
||||
page.setContent("<svg class='c1 c2' role='img' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'></svg>");
|
||||
assertThat(page.locator("svg")).containsClass("c1");
|
||||
assertThat(page.locator("svg")).containsClass("c2 c1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void containsClassFail() {
|
||||
page.setContent("<div class='bar baz'></div>");
|
||||
AssertionFailedError e = assertThrows(AssertionFailedError.class, () -> {
|
||||
assertThat(page.locator("div")).containsClass("does-not-exist", new ContainsClassOptions().setTimeout(1000));
|
||||
});
|
||||
assertTrue(e.getMessage().contains("Assert \"containsClass\" with timeout 1000ms"), e.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void containsClassPassWithArray() {
|
||||
page.setContent("<div class='foo'></div><div class='hello bar'></div><div class='baz'></div>");
|
||||
Locator locator = page.locator("div");
|
||||
assertThat(locator).containsClass(asList("foo", "hello", "baz"));
|
||||
assertThat(locator).not().hasClass(new String[]{"not-there", "hello", "baz"}); // Class not there
|
||||
assertThat(locator).not().hasClass(new String[]{"foo", "hello"}); // length mismatch
|
||||
}
|
||||
|
||||
@Test
|
||||
void containsClassFailWithArray() {
|
||||
page.setContent("<div class='foo'></div><div class='bar'></div><div class='bar'></div>");
|
||||
AssertionFailedError e = assertThrows(AssertionFailedError.class, () -> {
|
||||
assertThat(page.locator("div")).containsClass(asList("foo", "bar", "baz"), new ContainsClassOptions().setTimeout(1000));
|
||||
});
|
||||
assertTrue(e.getMessage().contains("Assert \"containsClass\" with timeout 1000ms"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
@ -218,6 +218,6 @@ public class TestLocatorAssertions2 extends TestBase {
|
||||
AssertionFailedError e = assertThrows(AssertionFailedError.class, () ->
|
||||
assertThat(locator).isChecked(new LocatorAssertions.IsCheckedOptions().setIndeterminate(true).setTimeout(1000)));
|
||||
// TODO: should be "assertThat().isChecked() with timeout 1000ms"
|
||||
assertTrue(e.getMessage().contains("Locator.expect with timeout 1000ms"), e.getMessage());
|
||||
assertTrue(e.getMessage().contains("Assert \"isChecked\" with timeout 1000ms"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
@ -35,13 +35,13 @@ public class TestOptionsFactories {
|
||||
public static BrowserType.LaunchOptions createLaunchOptions() {
|
||||
BrowserType.LaunchOptions options;
|
||||
options = new BrowserType.LaunchOptions();
|
||||
options.headless = !getHeadful();
|
||||
options.headless = !getHeaded();
|
||||
return options;
|
||||
}
|
||||
|
||||
private static boolean getHeadful() {
|
||||
String headfulEnv = System.getenv("HEADFUL");
|
||||
return headfulEnv != null && !"0".equals(headfulEnv) && !"false".equals(headfulEnv);
|
||||
private static boolean getHeaded() {
|
||||
String headedEnv = System.getenv("HEADED");
|
||||
return headedEnv != null && !"0".equals(headedEnv) && !"false".equals(headedEnv);
|
||||
}
|
||||
|
||||
public static String getBrowserName() {
|
||||
|
@ -271,7 +271,7 @@ public class TestPageAddLocatorHandler extends TestBase {
|
||||
|
||||
PlaywrightException e = assertThrows(PlaywrightException.class, () -> page.locator("#target").click(new Locator.ClickOptions().setTimeout(3_000)));
|
||||
assertEquals(0, (int) page.evaluate("window.clicked"));
|
||||
assertThat(page.locator("#interstitial")).isVisible();
|
||||
assertTrue(page.locator("#interstitial").isVisible());
|
||||
assertEquals(1, called[0]);
|
||||
assertTrue(e.getMessage().contains("locator handler has finished, waiting for getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName(\"close\")) to be hidden"), e.getMessage());
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package com.microsoft.playwright;
|
||||
import com.microsoft.playwright.junit.FixtureTest;
|
||||
import com.microsoft.playwright.junit.UsePlaywright;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.opentest4j.AssertionFailedError;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@ -12,6 +13,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
@FixtureTest
|
||||
@UsePlaywright
|
||||
@ -67,7 +69,7 @@ public class TestPageAriaSnapshot {
|
||||
@Test
|
||||
void shouldSnapshotComplex(Page page) {
|
||||
page.setContent("<ul><li><a href='about:blank'>link</a></li></ul>");
|
||||
checkAndMatchSnapshot(page.locator("body"), "- list:\n - listitem:\n - link \"link\"");
|
||||
checkAndMatchSnapshot(page.locator("body"), "- list:\n - listitem:\n - link \"link\":\n - /url: about:blank");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -83,4 +85,46 @@ public class TestPageAriaSnapshot {
|
||||
page.setContent("<details><summary>Summary</summary><div>Details</div></details>");
|
||||
checkAndMatchSnapshot(page.locator("body"), "- group: Summary");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSnapshotChildren(Page page) {
|
||||
page.setContent("<ul><li><img />One</li><li>Two</li><li>Three</li></ul>");
|
||||
assertThat(page.locator("body")).matchesAriaSnapshot("- list:\n - /children: equal\n - listitem\n - listitem: Two\n - listitem: Three");
|
||||
assertThat(page.locator("body")).not().matchesAriaSnapshot("- list:\n - /children: equal\n - listitem\n - listitem: Two");
|
||||
|
||||
assertThat(page.locator("body")).matchesAriaSnapshot("- list:\n - /children: deep-equal\n - listitem:\n - img\n - text: One\n - listitem: Two\n - listitem: Three");
|
||||
assertThat(page.locator("body")).not().matchesAriaSnapshot("- list:\n - /children: deep-equal\n - listitem:\n - text: One\n - listitem: Two\n - listitem: Three");
|
||||
assertThat(page.locator("body")).matchesAriaSnapshot("- list:\n - /children: deep-equal\n - listitem:\n - /children: contain\n - text: One\n - listitem: Two\n - listitem: Three");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMatchUrl(Page page) {
|
||||
page.setContent("<a href='https://example.com'>Link</a>");
|
||||
assertThat(page.locator("body")).matchesAriaSnapshot("" +
|
||||
"- link:\n" +
|
||||
" - /url: /.*example.com/");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleTopLevelDeepEqual(Page page) {
|
||||
// https://github.com/microsoft/playwright/issues/36456
|
||||
page.setContent("" +
|
||||
"<ul>\n" +
|
||||
" <li>\n" +
|
||||
" <ul>\n" +
|
||||
" <li>1.1</li>\n" +
|
||||
" <li>1.2</li>\n" +
|
||||
" </ul>\n" +
|
||||
" </li>\n" +
|
||||
"</ul>");
|
||||
|
||||
assertThrows(AssertionFailedError.class, () -> {
|
||||
assertThat(page.locator("body")).matchesAriaSnapshot("" +
|
||||
"- /children: deep-equal\n" +
|
||||
"- list:\n" +
|
||||
" - listitem:\n" +
|
||||
" - listitem: \"1.1\"\n" +
|
||||
" - listitem: \"1.2\"");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -356,4 +356,10 @@ public class TestPageBasic extends TestBase {
|
||||
|
||||
assertTrue(e.getMessage().contains("Can't add a null listener"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void pagePauseShouldNotThrow() {
|
||||
page.pause();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -391,8 +391,8 @@ public class TestPageClock {
|
||||
page.clock().install(new Clock.InstallOptions().setTime(0));
|
||||
page.navigate("data:text/html,");
|
||||
page.clock().pauseAt(1000);
|
||||
page.waitForTimeout(1000);
|
||||
page.clock().resume();
|
||||
// Internally wait to make sure the clock is paused and not running.
|
||||
page.waitForTimeout(1111);
|
||||
int now = (int) page.evaluate("() => Date.now()");
|
||||
assertTrue(now >= 0 && now <= 1000);
|
||||
}
|
||||
|
@ -29,7 +29,6 @@ import static com.microsoft.playwright.options.ColorScheme.LIGHT;
|
||||
import static com.microsoft.playwright.options.Media.PRINT;
|
||||
import static com.microsoft.playwright.Utils.attachFrame;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class TestPageEmulateMedia extends TestBase {
|
||||
@Test
|
||||
|
@ -18,11 +18,12 @@ package com.microsoft.playwright;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import com.microsoft.playwright.impl.PlaywrightImpl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@ -98,7 +99,7 @@ public class TestPageInterception extends TestBase {
|
||||
page.route("**/*", route -> {
|
||||
PlaywrightException error = assertThrows(PlaywrightException.class,
|
||||
() -> route.fetch(new Route.FetchOptions().setTimeout(1000)));
|
||||
assertTrue(error.getMessage().contains("Request timed out after 1000ms"), error.getMessage());
|
||||
assertTrue(error.getMessage().contains("Timeout 1000ms exceeded"), error.getMessage());
|
||||
});
|
||||
PlaywrightException error = assertThrows(PlaywrightException.class,
|
||||
() -> page.navigate(server.PREFIX + "/slow", new Page.NavigateOptions().setTimeout(2000)));
|
||||
@ -141,13 +142,98 @@ public class TestPageInterception extends TestBase {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldProperlyHandleCharacterSetsInGlobs() {
|
||||
page.route("**/[a-z]*.html", route -> {
|
||||
APIResponse response = route.fetch(new Route.FetchOptions().setUrl(server.PREFIX + "/one-style.html"));
|
||||
route.fulfill(new Route.FulfillOptions().setResponse(response));
|
||||
});
|
||||
Response response = page.navigate(server.PREFIX + "/empty.html");
|
||||
assertEquals(200, response.status());
|
||||
assertTrue(response.text().contains("one-style.css"), response.text());
|
||||
void shouldWorkWithGlob() {
|
||||
assertTrue(globToRegex("**/*.js").matcher("https://localhost:8080/foo.js").find());
|
||||
assertFalse(globToRegex("**/*.css").matcher("https://localhost:8080/foo.js").find());
|
||||
assertFalse(globToRegex("*.js").matcher("https://localhost:8080/foo.js").find());
|
||||
assertTrue(globToRegex("https://**/*.js").matcher("https://localhost:8080/foo.js").find());
|
||||
assertTrue(globToRegex("http://localhost:8080/simple/path.js").matcher("http://localhost:8080/simple/path.js").find());
|
||||
assertTrue(globToRegex("**/{a,b}.js").matcher("https://localhost:8080/a.js").find());
|
||||
assertTrue(globToRegex("**/{a,b}.js").matcher("https://localhost:8080/b.js").find());
|
||||
assertFalse(globToRegex("**/{a,b}.js").matcher("https://localhost:8080/c.js").find());
|
||||
|
||||
assertTrue(globToRegex("**/*.{png,jpg,jpeg}").matcher("https://localhost:8080/c.jpg").find());
|
||||
assertTrue(globToRegex("**/*.{png,jpg,jpeg}").matcher("https://localhost:8080/c.jpeg").find());
|
||||
assertTrue(globToRegex("**/*.{png,jpg,jpeg}").matcher("https://localhost:8080/c.png").find());
|
||||
assertFalse(globToRegex("**/*.{png,jpg,jpeg}").matcher("https://localhost:8080/c.css").find());
|
||||
assertTrue(globToRegex("foo*").matcher("foo.js").find());
|
||||
assertFalse(globToRegex("foo*").matcher("foo/bar.js").find());
|
||||
assertFalse(globToRegex("http://localhost:3000/signin-oidc*").matcher("http://localhost:3000/signin-oidc/foo").find());
|
||||
assertTrue(globToRegex("http://localhost:3000/signin-oidc*").matcher("http://localhost:3000/signin-oidcnice").find());
|
||||
|
||||
// range [] is NOT supported
|
||||
assertTrue(globToRegex("**/api/v[0-9]").matcher("http://example.com/api/v[0-9]").find());
|
||||
assertFalse(globToRegex("**/api/v[0-9]").matcher("http://example.com/api/version").find());
|
||||
|
||||
// query params
|
||||
assertTrue(globToRegex("**/api\\?param").matcher("http://example.com/api?param").find());
|
||||
assertFalse(globToRegex("**/api\\?param").matcher("http://example.com/api-param").find());
|
||||
assertTrue(globToRegex("**/three-columns/settings.html\\?**id=settings-**").matcher("http://mydomain:8080/blah/blah/three-columns/settings.html?id=settings-e3c58efe-02e9-44b0-97ac-dd138100cf7c&blah").find());
|
||||
|
||||
assertEquals("^\\?$", globToRegex("\\?").pattern());
|
||||
assertEquals("^\\\\$", globToRegex("\\").pattern());
|
||||
assertEquals("^\\\\$", globToRegex("\\\\").pattern());
|
||||
assertEquals("^\\[$", globToRegex("\\[").pattern());
|
||||
assertEquals("^\\[a-z\\]$", globToRegex("[a-z]").pattern());
|
||||
assertEquals("^\\$\\^\\+\\.\\*\\(\\)\\|\\?\\{\\}\\[\\]$", globToRegex("$^+.\\*()|\\?\\{\\}\\[\\]").pattern());
|
||||
|
||||
|
||||
assertTrue(urlMatches(null, "http://playwright.dev/", "http://playwright.dev"));
|
||||
assertTrue(urlMatches(null, "http://playwright.dev/?a=b", "http://playwright.dev?a=b"));
|
||||
assertTrue(urlMatches(null, "http://playwright.dev/", "h*://playwright.dev"));
|
||||
assertTrue(urlMatches(null, "http://api.playwright.dev/?x=y", "http://*.playwright.dev?x=y"));
|
||||
assertTrue(urlMatches(null, "http://playwright.dev/foo/bar", "**/foo/**"));
|
||||
assertTrue(urlMatches("http://playwright.dev", "http://playwright.dev/?x=y", "?x=y"));
|
||||
assertTrue(urlMatches("http://playwright.dev/foo/", "http://playwright.dev/foo/bar?x=y", "./bar?x=y"));
|
||||
|
||||
// Case insensitive matching
|
||||
assertTrue(urlMatches(null, "https://playwright.dev/fooBAR", "HtTpS://pLaYwRiGhT.dEv/fooBAR"));
|
||||
assertTrue(urlMatches("http://ignored", "https://playwright.dev/fooBAR", "HtTpS://pLaYwRiGhT.dEv/fooBAR"));
|
||||
// Path and search query are case-sensitive
|
||||
assertFalse(urlMatches(null, "https://playwright.dev/foobar", "https://playwright.dev/fooBAR"));
|
||||
assertFalse(urlMatches(null, "https://playwright.dev/foobar?a=b", "https://playwright.dev/foobar?A=B"));
|
||||
|
||||
// This is not supported, we treat ? as a query separator.
|
||||
assertFalse(urlMatches(null, "http://localhost:8080/Simple/path.js", "http://localhost:8080/?imple/path.js"));
|
||||
assertFalse(urlMatches(null, "http://playwright.dev/", "http://playwright.?ev"));
|
||||
assertTrue(urlMatches(null, "http://playwright./?ev", "http://playwright.?ev"));
|
||||
assertFalse(urlMatches(null, "http://playwright.dev/foo", "http://playwright.dev/f??"));
|
||||
assertTrue(urlMatches(null, "http://playwright.dev/f??", "http://playwright.dev/f??"));
|
||||
assertTrue(urlMatches(null, "http://playwright.dev/?x=y", "http://playwright.dev\\\\?x=y"));
|
||||
assertTrue(urlMatches(null, "http://playwright.dev/?x=y", "http://playwright.dev/\\\\?x=y"));
|
||||
assertTrue(urlMatches("http://playwright.dev/foo", "http://playwright.dev/foo?bar", "?bar"));
|
||||
assertTrue(urlMatches("http://playwright.dev/foo", "http://playwright.dev/foo?bar", "\\\\?bar"));
|
||||
assertTrue(urlMatches("http://first.host/", "http://second.host/foo", "**/foo"));
|
||||
assertTrue(urlMatches("http://playwright.dev/", "http://localhost/", "*//localhost/"));
|
||||
|
||||
String[] customPrefixes = {"about", "data", "chrome", "edge", "file"};
|
||||
for (String prefix : customPrefixes) {
|
||||
assertTrue(urlMatches("http://playwright.dev/", prefix + ":blank", prefix + ":blank"));
|
||||
assertFalse(urlMatches("http://playwright.dev/", prefix + ":blank", "http://playwright.dev/"));
|
||||
assertTrue(urlMatches(null, prefix + ":blank", prefix + ":blank"));
|
||||
assertTrue(urlMatches(null, prefix + ":blank", prefix + ":*"));
|
||||
assertFalse(urlMatches(null, "not" + prefix + ":blank", prefix + ":*"));
|
||||
}
|
||||
}
|
||||
|
||||
Pattern globToRegex(String glob) {
|
||||
return globToRegex(glob, null, false);
|
||||
}
|
||||
|
||||
Pattern globToRegex(String glob, String baseURL, boolean webSocketUrl) {
|
||||
return ((PlaywrightImpl) playwright).localUtils().globToRegex(glob, baseURL, webSocketUrl);
|
||||
}
|
||||
|
||||
boolean urlMatches(String baseURL, String urlString, String match) {
|
||||
if (match == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String glob = (String) match;
|
||||
if (glob.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return globToRegex(glob, baseURL, false).matcher(urlString).find();
|
||||
}
|
||||
}
|
||||
|
@ -16,9 +16,12 @@
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import com.microsoft.playwright.options.HttpHeader;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.DisabledIf;
|
||||
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
@ -77,4 +80,83 @@ public class TestPageRequestContinue extends TestBase {
|
||||
e.getMessage().contains("frame was detached"), e.getMessage());
|
||||
assertTrue(done[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisabledIf(value = "com.microsoft.playwright.TestBase#isFirefox", disabledReason = "We currently clear all headers during interception in firefox")
|
||||
void continueShouldNotPropagateCookieOverrideToRedirects() throws ExecutionException, InterruptedException {
|
||||
// https://github.com/microsoft/playwright/issues/35168
|
||||
server.setRoute("/set-cookie", exchange -> {
|
||||
exchange.getResponseHeaders().add("Set-Cookie", "foo=bar;");
|
||||
exchange.sendResponseHeaders(200, 0);
|
||||
exchange.getResponseBody().close();
|
||||
});
|
||||
page.navigate(server.PREFIX + "/set-cookie");
|
||||
assertEquals("foo=bar", page.evaluate("() => document.cookie"));
|
||||
|
||||
server.setRedirect("/redirect", server.PREFIX + "/empty.html");
|
||||
page.route("**/redirect", route -> {
|
||||
Map<String, String> headers = new HashMap<>(route.request().allHeaders());
|
||||
headers.put("cookie", "override");
|
||||
route.resume(new Route.ResumeOptions().setHeaders(headers));
|
||||
});
|
||||
|
||||
Future<Server.Request> serverRequest = server.futureRequest("/empty.html");
|
||||
page.navigate(server.PREFIX + "/redirect");
|
||||
assertEquals(asList("foo=bar"), serverRequest.get().headers.get("cookie"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisabledIf(value = "com.microsoft.playwright.TestBase#isFirefox", disabledReason = "We currently clear all headers during interception in firefox")
|
||||
void continueShouldNotOverrideCookie() throws ExecutionException, InterruptedException {
|
||||
// https://github.com/microsoft/playwright/issues/35168
|
||||
server.setRoute("/set-cookie", exchange -> {
|
||||
exchange.getResponseHeaders().add("Set-Cookie", "foo=bar;");
|
||||
exchange.sendResponseHeaders(200, 0);
|
||||
exchange.getResponseBody().close();
|
||||
});
|
||||
page.navigate(server.PREFIX + "/set-cookie");
|
||||
assertEquals("foo=bar", page.evaluate("() => document.cookie"));
|
||||
|
||||
page.route("**", route -> {
|
||||
Map<String, String> headers = new HashMap<>(route.request().allHeaders());
|
||||
headers.put("cookie", "override");
|
||||
headers.put("custom", "value");
|
||||
route.resume(new Route.ResumeOptions().setHeaders(headers));
|
||||
});
|
||||
|
||||
Future<Server.Request> serverRequest = server.futureRequest("/empty.html");
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
|
||||
// Original cookie from the browser's cookie jar should be sent.
|
||||
assertEquals(asList("foo=bar"), serverRequest.get().headers.get("cookie"));
|
||||
assertEquals(asList("value"), serverRequest.get().headers.get("custom"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void redirectAfterContinueShouldBeAbleToDeleteCookie() throws ExecutionException, InterruptedException {
|
||||
// https://github.com/microsoft/playwright/issues/35168
|
||||
server.setRoute("/set-cookie", exchange -> {
|
||||
exchange.getResponseHeaders().add("Set-Cookie", "foo=bar;");
|
||||
exchange.sendResponseHeaders(200, 0);
|
||||
exchange.getResponseBody().close();
|
||||
});
|
||||
page.navigate(server.PREFIX + "/set-cookie");
|
||||
assertEquals("foo=bar", page.evaluate("() => document.cookie"));
|
||||
|
||||
server.setRoute("/delete-cookie", exchange -> {
|
||||
exchange.getResponseHeaders().add("Set-Cookie", "foo=bar; expires=Thu, 01 Jan 1970 00:00:00 GMT");
|
||||
exchange.sendResponseHeaders(200, 0);
|
||||
exchange.getResponseBody().close();
|
||||
});
|
||||
server.setRedirect("/redirect", "/delete-cookie");
|
||||
page.route("**/redirect", route -> {
|
||||
// Pass original headers explicitly when continuing.
|
||||
route.resume(new Route.ResumeOptions().setHeaders(route.request().allHeaders()));
|
||||
});
|
||||
page.navigate(server.PREFIX + "/redirect");
|
||||
|
||||
Future<Server.Request> serverRequest = server.futureRequest("/empty.html");
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
assertNull(serverRequest.get().headers.get("cookie"));
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user