1
0
mirror of synced 2026-05-23 03:03:15 +00:00

Compare commits

...

23 Commits

Author SHA1 Message Date
Yury Semikhatsky 1c90bb65ad chore: set version to 1.55.0 (#1836) 2025-08-27 10:03:03 -07:00
Yury Semikhatsky aee298b293 chore: rename headful -> headed (#1835) 2025-08-27 09:51:30 -07:00
Max Schmitt fd2ab4708a devops: enable retries in Docker tests (#1834) 2025-08-26 21:50:28 +02:00
Max Schmitt 2a6cdff664 chore: migrate Trace Viewer tests to use real Trace viewer (#1830) 2025-08-26 10:43:18 +02:00
Max Schmitt 44161e0558 chore: fix Maven test commands (#1832) 2025-08-26 00:35:06 +02:00
Max Schmitt 954b1c43ef refactor: remove unused ImplUtils class (#1833) 2025-08-25 15:29:53 -07:00
Simon Knott f4c7b9734f chore: roll 1.55.0 (#1827) 2025-08-21 17:09:15 +02:00
Yury Semikhatsky dd87b300fb fix: npe in page.pause() (#1828) 2025-08-18 15:51:17 -07:00
Janne Hyötylä f83c03af68 fix: Fix masking in single element screenshots. (#1825) 2025-08-11 11:40:00 -07:00
JONGSHIN d26dd0b112 fix: Replaced classLoader in DriverJar (#1811) 2025-07-31 11:03:19 -07:00
Yury Semikhatsky 0cf8c4e17f chore: unflake tracing test (#1822) 2025-07-21 11:32:08 -07:00
Yury Semikhatsky 1fb593e1e2 chore: roll 1.54.1 (#1821) 2025-07-21 11:06:39 -07:00
Simon Knott 915ee8d64c chore: roll 1.54.0-alpha-2025-07-09 (#1817) 2025-07-09 10:29:18 +02:00
Yury Semikhatsky 9df2165e93 devops: fix release branch name check (#1813) 2025-06-24 13:20:20 -07:00
Yury Semikhatsky 859eb9b8b8 chore: require explicit timeout parameter in sendMessage() (#1807) 2025-06-23 10:37:40 -07:00
Yury Semikhatsky f28cb55795 chore: roll 1.53.1 (#1808) 2025-06-23 09:45:36 -07:00
Max Schmitt 07867c2db5 devops: ignore tests in CodeQL checks (#1805) 2025-06-20 16:25:15 -07:00
Simon Knott b4151b1231 chore: remove withLogging (#1804) 2025-06-13 15:33:22 -07:00
Simon Knott 4698b91d8e chore: roll 1.53.0 (#1801) 2025-06-12 12:35:29 +02:00
Simon Knott 8a89e36ce3 chore: roll to 1.53.0-alpha-2025-05-21 (#1798) 2025-06-10 18:08:32 +02:00
campersau f1e6100b33 fix(docker): set default shell encoding (#1793) 2025-05-30 16:13:42 +01:00
Yury Semikhatsky fddb146d73 devops: trigger publish on new tag (#1787) 2025-05-02 10:55:07 -07:00
Max Schmitt 9ad596ac75 devops: add linux-arm64 Docker tests (#1784) 2025-05-01 19:21:22 +02:00
131 changed files with 2093 additions and 2232 deletions
+7 -3
View File
@@ -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
+22 -5
View File
@@ -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
- 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"
@@ -1,27 +0,0 @@
name: "Internal Tests"
on:
push:
branches:
- main
- release-*
jobs:
trigger:
name: "trigger"
runs-on: ubuntu-24.04
steps:
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.PLAYWRIGHT_APP_ID }}
private-key: ${{ secrets.PLAYWRIGHT_PRIVATE_KEY }}
repositories: playwright-browsers
- run: |
curl -X POST --fail \
-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: ${{ steps.app-token.outputs.token }}
+2 -2
View File
@@ -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
View File
@@ -0,0 +1,3 @@
path_classifiers:
tests:
- "playwright/src/test/**"
+3 -3
View File
@@ -10,9 +10,9 @@ Playwright is a Java library to automate [Chromium](https://www.chromium.org/Hom
| | Linux | macOS | Windows |
| :--- | :---: | :---: | :---: |
| Chromium <!-- GEN:chromium-version -->136.0.7103.25<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->18.4<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Firefox <!-- GEN:firefox-version -->137.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
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.50.0-SNAPSHOT</version>
<version>1.55.0</version>
</parent>
<artifactId>driver-bundle</artifactId>
@@ -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();
}
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.50.0-SNAPSHOT</version>
<version>1.55.0</version>
</parent>
<artifactId>driver</artifactId>
+2 -2
View File
@@ -6,11 +6,11 @@
<groupId>org.example</groupId>
<artifactId>examples</artifactId>
<version>1.50.0-SNAPSHOT</version>
<version>1.55.0</version>
<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>
+1 -1
View File
@@ -7,7 +7,7 @@
<parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.50.0-SNAPSHOT</version>
<version>1.55.0</version>
</parent>
<artifactId>playwright</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}.
*/
@@ -134,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}.
*/
@@ -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}.
*/
@@ -596,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
*/
@@ -871,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
*/
@@ -903,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
*/
@@ -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;
/**
@@ -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;
@@ -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;
/**
@@ -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;
@@ -1374,6 +1394,11 @@ public interface BrowserType {
* 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) {
@@ -1394,6 +1419,11 @@ public interface BrowserType {
* 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);
@@ -30,11 +30,6 @@ import java.util.regex.Pattern;
*/
public interface Locator {
class AriaSnapshotOptions {
/**
* Generate symbolic reference for each element. One can use {@code aria-ref=<ref>} locator immediately after capturing the
* snapshot to perform actions on the element.
*/
public Boolean ref;
/**
* 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
@@ -43,14 +38,6 @@ public interface Locator {
*/
public Double timeout;
/**
* Generate symbolic reference for each element. One can use {@code aria-ref=<ref>} locator immediately after capturing the
* snapshot to perform actions on the element.
*/
public AriaSnapshotOptions setRef(boolean ref) {
this.ref = ref;
return this;
}
/**
* 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
@@ -2615,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.
*
@@ -2861,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}.
@@ -2885,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
@@ -2908,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.
@@ -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 -> {
@@ -885,7 +885,7 @@ public interface LocatorAssertions {
* <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"});
* 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.
@@ -909,7 +909,7 @@ public interface LocatorAssertions {
* <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"});
* 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.
@@ -931,7 +931,7 @@ public interface LocatorAssertions {
* <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"});
* 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.
@@ -955,7 +955,7 @@ public interface LocatorAssertions {
* <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"});
* 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.
@@ -1565,7 +1565,7 @@ public interface LocatorAssertions {
* 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.
@@ -1589,7 +1589,7 @@ public interface LocatorAssertions {
* 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.
@@ -1611,7 +1611,7 @@ public interface LocatorAssertions {
* 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.
@@ -1635,7 +1635,7 @@ public interface LocatorAssertions {
* 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.
@@ -1657,7 +1657,7 @@ public interface LocatorAssertions {
* 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.
@@ -1681,7 +1681,7 @@ public interface LocatorAssertions {
* 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.
@@ -1703,7 +1703,7 @@ public interface LocatorAssertions {
* 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.
@@ -1727,7 +1727,7 @@ public interface LocatorAssertions {
* 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(UrlMatcher.forGlob(baseUrl, url, this.connection.localUtils, false), 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, this.connection.localUtils, false);
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(UrlMatcher.forGlob(baseUrl, url, this.connection.localUtils, true), 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(UrlMatcher.forGlob(this.baseUrl, url, this.connection.localUtils, false), 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, this.connection.localUtils, false);
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(UrlMatcher.forGlob(page.context().baseUrl, url, this.connection.localUtils, false), 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);
}
}
@@ -28,7 +28,6 @@ import static com.microsoft.playwright.impl.Serialization.gson;
public class LocalUtils extends ChannelOwner {
LocalUtils(ChannelOwner parent, String type, String guid, JsonObject initializer) {
super(parent, type, guid, initializer);
markAsInternalType();
}
JsonArray deviceDescriptors() {
@@ -42,13 +41,13 @@ public 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) {
@@ -57,7 +56,7 @@ public 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();
}
@@ -68,7 +67,7 @@ public class LocalUtils extends ChannelOwner {
params.addProperty("baseURL", baseURL);
}
params.addProperty("webSocketUrl", webSocketUrl);
JsonObject json = connection.localUtils().sendMessage("globToRegex", params).getAsJsonObject();
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,20 @@ 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);
}
@@ -44,7 +51,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
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));
expectImpl("to.contain.class", expected, classname, "Locator expected to contain class", convertType(options, FrameExpectOptions.class), "Assert \"containsClass\"");
}
@Override
@@ -55,7 +62,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
expected.string = text;
list.add(expected);
}
expectImpl("to.contain.class.array", list, classnames, "Locator expected to contain classes", convertType(options, FrameExpectOptions.class));
expectImpl("to.contain.class.array", list, classnames, "Locator expected to contain classes", convertType(options, FrameExpectOptions.class), "Assert \"containsClass\"");
}
@Override
@@ -65,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
@@ -74,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
@@ -88,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
@@ -101,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
@@ -110,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
@@ -118,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
@@ -127,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
@@ -135,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
@@ -144,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
@@ -152,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
@@ -180,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
@@ -204,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
@@ -214,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
@@ -225,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
@@ -251,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
@@ -276,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
@@ -293,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
@@ -303,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
@@ -317,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
@@ -330,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
@@ -354,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
@@ -365,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
@@ -375,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
@@ -403,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
@@ -416,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
@@ -429,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
@@ -448,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
@@ -456,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
@@ -474,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(UrlMatcher.forGlob(browserContext.baseUrl, glob, this.connection.localUtils, false));
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(UrlMatcher.forGlob(browserContext.baseUrl, url, this.connection.localUtils, false), 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, this.connection.localUtils, false);
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(UrlMatcher.forGlob(browserContext.baseUrl, url, this.connection.localUtils, true), 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(UrlMatcher.forGlob(browserContext.baseUrl, url, this.connection.localUtils, false), 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,8 +1353,7 @@ 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
@@ -1508,7 +1428,7 @@ public class PageImpl extends ChannelOwner implements Page {
@Override
public Request waitForRequest(String urlGlob, WaitForRequestOptions options, Runnable code) {
return waitForRequest(UrlMatcher.forGlob(browserContext.baseUrl, urlGlob, this.connection.localUtils, false), null, options, code);
return waitForRequest(UrlMatcher.forGlob(browserContext.baseUrl(), urlGlob, this.connection.localUtils, false), null, options, code);
}
@Override
@@ -1553,7 +1473,7 @@ public class PageImpl extends ChannelOwner implements Page {
@Override
public Response waitForResponse(String urlGlob, WaitForResponseOptions options, Runnable code) {
return waitForResponse(UrlMatcher.forGlob(browserContext.baseUrl, urlGlob, this.connection.localUtils, false), null, options, code);
return waitForResponse(UrlMatcher.forGlob(browserContext.baseUrl(), urlGlob, this.connection.localUtils, false), null, options, code);
}
@Override
@@ -1586,8 +1506,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 +1520,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(UrlMatcher.forGlob(browserContext.baseUrl, url, this.connection.localUtils, false), 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,24 +70,14 @@ 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;
}
@@ -120,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);
}
@@ -35,6 +35,7 @@ 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;
@@ -200,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)) {
@@ -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,7 +27,7 @@ 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
@@ -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" +
@@ -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");
@@ -189,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());
}
@@ -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());
}
}
@@ -1070,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);
}
@@ -1119,7 +1119,7 @@ public class TestLocatorAssertions extends TestBase {
AssertionFailedError e = assertThrows(AssertionFailedError.class, () -> {
assertThat(page.locator("div")).containsClass("does-not-exist", new ContainsClassOptions().setTimeout(1000));
});
assertTrue(e.getMessage().contains("Locator.expect with timeout 1000ms"), e.getMessage());
assertTrue(e.getMessage().contains("Assert \"containsClass\" with timeout 1000ms"), e.getMessage());
}
@Test
@@ -1137,6 +1137,6 @@ public class TestLocatorAssertions extends TestBase {
AssertionFailedError e = assertThrows(AssertionFailedError.class, () -> {
assertThat(page.locator("div")).containsClass(asList("foo", "bar", "baz"), new ContainsClassOptions().setTimeout(1000));
});
assertTrue(e.getMessage().contains("Locator.expect with timeout 1000ms"), e.getMessage());
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());
}
@@ -1,9 +1,9 @@
package com.microsoft.playwright;
import com.microsoft.playwright.Locator.AriaSnapshotOptions;
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;
@@ -14,7 +14,6 @@ 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;
import static org.junit.jupiter.api.Assertions.assertTrue;
@FixtureTest
@UsePlaywright
@@ -73,12 +72,6 @@ public class TestPageAriaSnapshot {
checkAndMatchSnapshot(page.locator("body"), "- list:\n - listitem:\n - link \"link\":\n - /url: about:blank");
}
@Test
void shouldSnapshotRef(Page page) {
page.setContent("<ul><li>foo</li></ul>");
assertEquals(unshift("- list [ref=s1e3]:\n - listitem [ref=s1e4]: foo"), page.locator("body").ariaSnapshot(new AriaSnapshotOptions().setRef(true)));
}
@Test
void shouldAllowTextNodes(Page page) {
page.setContent("<h1>Microsoft</h1><div>Open source projects and samples from Microsoft</div>");
@@ -111,4 +104,27 @@ public class TestPageAriaSnapshot {
"- 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();
}
}
@@ -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
@@ -99,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)));
@@ -186,6 +186,13 @@ public class TestPageInterception extends TestBase {
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"));
@@ -198,6 +205,15 @@ public class TestPageInterception extends TestBase {
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) {
@@ -40,7 +40,7 @@ import static java.util.Arrays.asList;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
// TODO: suite.skip(browserName === "firefox" && headful");
// TODO: suite.skip(browserName === "firefox" && headed");
public class TestPageScreenshot extends TestBase {
@Test
void shouldWork() throws IOException {
@@ -136,7 +136,7 @@ public class TestPageScreenshot extends TestBase {
}
@Test
void maskShouldWork() {
void maskShouldWorkForPage() {
page.setViewportSize(500, 500);
page.navigate(server.PREFIX + "/grid.html");
byte[] screenshot = page.screenshot(new Page.ScreenshotOptions()
@@ -146,6 +146,17 @@ public class TestPageScreenshot extends TestBase {
assertThrows(AssertionFailedError.class, () -> assertArrayEquals(screenshot, originalScreenshot));
}
@Test
void maskShouldWorkForLocator() {
page.navigate(server.PREFIX + "/grid.html");
Locator locatorToScreenshot = page.locator("div").first();
byte[] screenshot = locatorToScreenshot.screenshot(new Locator.ScreenshotOptions()
.setMask(asList(page.locator("img"))));
// TODO: toMatchSnapshot is not present in java, so we only checks that masked screenshot is different.
byte[] originalScreenshot = locatorToScreenshot.screenshot();
assertThrows(AssertionFailedError.class, () -> assertArrayEquals(screenshot, originalScreenshot));
}
@Test
void shouldWorkWithDeviceScaleFactorAndClip() {
try (BrowserContext context = browser.newContext(new Browser.NewContextOptions()
@@ -451,7 +451,7 @@ public class TestPageSetInputFiles extends TestBase {
List<String> relativePathsSorted = new ArrayList<>(webkitRelativePaths);
relativePathsSorted.sort(String::compareTo);
// https://issues.chromium.org/issues/345393164
if (isChromium() && !isHeadful() && chromiumVersionLessThan(browser.version(), "127.0.6533.0")) {
if (isChromium() && !isHeaded() && chromiumVersionLessThan(browser.version(), "127.0.6533.0")) {
assertEquals(asList("file-upload-test/file1.txt", "file-upload-test/file2"), relativePathsSorted);
} else {
assertEquals(asList("file-upload-test/file1.txt", "file-upload-test/file2", "file-upload-test/sub-dir/really.txt"), relativePathsSorted);
@@ -60,13 +60,6 @@ public class TestPdf extends TestBase {
assertTrue(outlineSize > noOutlineSize, "Unexpected sizes: " + outlineSize + " noOutline: " + noOutlineSize);
}
@Test
@DisabledIf(value="com.microsoft.playwright.TestBase#isChromium", disabledReason="skip")
void shouldThrowInNonChromium() {
PlaywrightException e = assertThrows(PlaywrightException.class, () -> page.pdf());
assertTrue(e.getMessage().contains("PDF generation is only supported for Headless Chromium"), e.getMessage());
}
@Test
@DisabledIf(value="com.microsoft.playwright.TestBase#isChromium", disabledReason="skip")
@@ -17,12 +17,10 @@
package com.microsoft.playwright;
import com.microsoft.playwright.impl.PlaywrightImpl;
import com.microsoft.playwright.impl.driver.Driver;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -55,12 +55,12 @@ public class TestRequestFulfill extends TestBase {
assertEquals("Yo, page!", page.evaluate("document.body.textContent"));
}
static boolean isFirefoxHeadful() {
return isFirefox() && isHeadful();
static boolean isFirefoxHeaded() {
return isFirefox() && isHeaded();
}
@Test
@DisabledIf(value="isFirefoxHeadful", disabledReason="skip")
@DisabledIf(value="isFirefoxHeaded", disabledReason="skip")
void shouldAllowMockingBinaryResponses() {
page.route("**/*", route -> {
byte[] imageBuffer;
@@ -85,9 +85,9 @@ public class TestRequestFulfill extends TestBase {
}
@Test
@DisabledIf(value="isFirefoxHeadful", disabledReason="skip")
@DisabledIf(value="isFirefoxHeaded", disabledReason="skip")
void shouldAllowMockingSvgWithCharset() {
// Firefox headful produces a different image.
// Firefox headed produces a different image.
page.route("**/*", route -> {
route.fulfill(new Route.FulfillOptions()
.setContentType("image/svg+xml ; charset=utf-8")
@@ -23,25 +23,26 @@ import java.nio.file.Paths;
import static org.junit.jupiter.api.Assertions.*;
public class TestSelectorsRegister extends TestBase {
private static final String TAG_SELECTOR_SCRIPT = "{\n" +
" create(root, target) {\n" +
" return target.nodeName;\n" +
" },\n" +
" query(root, selector) {\n" +
" return root.querySelector(selector);\n" +
" },\n" +
" queryAll(root, selector) {\n" +
" return Array.from(root.querySelectorAll(selector));\n" +
" }\n" +
"}";
@Test
void shouldWork() {
String selectorScript = "{\n" +
" create(root, target) {\n" +
" return target.nodeName;\n" +
" },\n" +
" query(root, selector) {\n" +
" return root.querySelector(selector);\n" +
" },\n" +
" queryAll(root, selector) {\n" +
" return Array.from(root.querySelectorAll(selector));\n" +
" }\n" +
"}";
// Register one engine before creating context.
playwright.selectors().register("tag", selectorScript);
playwright.selectors().register("tag", TAG_SELECTOR_SCRIPT);
BrowserContext context = browser.newContext();
// Register another engine after creating context.
playwright.selectors().register("tag2", selectorScript);
playwright.selectors().register("tag2", TAG_SELECTOR_SCRIPT);
Page page = context.newPage();
page.setContent("<div><span></span></div><div></div>");
@@ -134,4 +135,21 @@ public class TestSelectorsRegister extends TestBase {
});
assertTrue(e.getMessage().contains("\"css\" is a predefined selector engine"));
}
@Test
void shouldThrowAlreadyRegisteredErrorWhenRegistering() {
// https://github.com/microsoft/playwright/issues/36467
// this test is about the exception *before* there's a context created
context.close();
// Register the selector engine first
playwright.selectors().register("alreadyRegistered", TAG_SELECTOR_SCRIPT);
// Attempt to register the same selector engine again should throw an error
PlaywrightException e = assertThrows(PlaywrightException.class, () -> {
playwright.selectors().register("alreadyRegistered", TAG_SELECTOR_SCRIPT);
});
assertTrue(e.getMessage().contains("\"alreadyRegistered\" selector engine has been already registered"));
}
}
@@ -16,8 +16,6 @@
package com.microsoft.playwright;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.microsoft.playwright.options.AriaRole;
import com.microsoft.playwright.options.Location;
import com.microsoft.playwright.options.MouseButton;
@@ -27,18 +25,12 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.regex.Pattern;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Arrays.asList;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
public class TestTracing extends TestBase {
@@ -57,7 +49,7 @@ public class TestTracing extends TestBase {
}
@Test
void shouldCollectTrace1(@TempDir Path tempDir) {
void shouldCollectTrace1(@TempDir Path tempDir) throws Exception {
context.tracing().start(new Tracing.StartOptions().setName("test")
.setScreenshots(true).setSnapshots(true));
page.navigate(server.EMPTY_PAGE);
@@ -68,10 +60,18 @@ public class TestTracing extends TestBase {
context.tracing().stop(new Tracing.StopOptions().setPath(traceFile));
assertTrue(Files.exists(traceFile));
TraceViewerPage.showTraceViewer(this.browserType, traceFile, traceViewer -> {
assertThat(traceViewer.actionTitles()).hasText(new Pattern[] {
Pattern.compile("Navigate to \"/empty.html\""),
Pattern.compile("Set content"),
Pattern.compile("Click"),
Pattern.compile("Close")
});
});
}
@Test
void shouldCollectTwoTraces(@TempDir Path tempDir) {
void shouldCollectTwoTraces(@TempDir Path tempDir) throws Exception {
context.tracing().start(new Tracing.StartOptions().setName("test1")
.setScreenshots(true).setSnapshots(true));
page.navigate(server.EMPTY_PAGE);
@@ -89,10 +89,25 @@ public class TestTracing extends TestBase {
assertTrue(Files.exists(traceFile1));
assertTrue(Files.exists(traceFile2));
TraceViewerPage.showTraceViewer(this.browserType, traceFile1, traceViewer -> {
assertThat(traceViewer.actionTitles()).hasText(new Pattern[] {
Pattern.compile("Navigate to \"/empty.html\""),
Pattern.compile("Set content"),
Pattern.compile("Click")
});
});
TraceViewerPage.showTraceViewer(this.browserType, traceFile2, traceViewer -> {
assertThat(traceViewer.actionTitles()).hasText(new Pattern[] {
Pattern.compile("Double click"),
Pattern.compile("Close")
});
});
}
@Test
void shouldWorkWithMultipleChunks(@TempDir Path tempDir) {
void shouldWorkWithMultipleChunks(@TempDir Path tempDir) throws Exception {
context.tracing().start(new Tracing.StartOptions().setScreenshots(true).setSnapshots(true));
page.navigate(server.PREFIX + "/frames/frame.html");
@@ -109,28 +124,60 @@ public class TestTracing extends TestBase {
assertTrue(Files.exists(traceFile1));
assertTrue(Files.exists(traceFile2));
TraceViewerPage.showTraceViewer(this.browserType, traceFile1, traceViewer -> {
assertThat(traceViewer.actionTitles()).hasText(new Pattern[] {
Pattern.compile("Set content"),
Pattern.compile("Click")
});
traceViewer.selectSnapshot("After");
FrameLocator frame = traceViewer.snapshotFrame("Set content", 0, false);
assertThat(frame.locator("button")).hasText("Click");
});
TraceViewerPage.showTraceViewer(this.browserType, traceFile2, traceViewer -> {
assertThat(traceViewer.actionTitles()).containsText(new String[] {"Hover"});
FrameLocator frame = traceViewer.snapshotFrame("Hover", 0, false);
assertThat(frame.locator("button")).hasText("Click");
});
}
@Test
void shouldCollectSources(@TempDir Path tmpDir) throws IOException {
void shouldCollectSources(@TempDir Path tmpDir) throws Exception {
Assumptions.assumeTrue(System.getenv("PLAYWRIGHT_JAVA_SRC") != null, "PLAYWRIGHT_JAVA_SRC must point to the directory containing this test source.");
context.tracing().start(new Tracing.StartOptions().setSources(true));
page.navigate(server.EMPTY_PAGE);
page.setContent("<button>Click</button>");
page.click("'Click'");
myMethodOuter();
Path trace = tmpDir.resolve("trace1.zip");
context.tracing().stop(new Tracing.StopOptions().setPath(trace));
Map<String, byte[]> entries = Utils.parseZip(trace);
Map<String, byte[]> sources = entries.entrySet().stream().filter(e -> e.getKey().endsWith(".txt")).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
assertEquals(1, sources.size());
TraceViewerPage.showTraceViewer(this.browserType, trace, traceViewer -> {
assertThat(traceViewer.actionTitles()).hasText(new Pattern[] {
Pattern.compile("Navigate to \"/empty.html\""),
Pattern.compile("Set content"),
Pattern.compile("Click")
});
traceViewer.showSourceTab();
assertThat(traceViewer.stackFrames()).containsText(new Pattern[] {
Pattern.compile("myMethodInner"),
Pattern.compile("myMethodOuter"),
Pattern.compile("shouldCollectSources")
});
traceViewer.selectAction("Set content");
assertThat(traceViewer.page().locator(".source-tab-file-name"))
.hasAttribute("title", Pattern.compile(".*TestTracing\\.java"));
assertThat(traceViewer.page().locator(".source-line-running"))
.containsText("page.setContent(\"<button>Click</button>\");");
});
}
String path = getClass().getName().replace('.', File.separatorChar);
String[] srcRoots = System.getenv("PLAYWRIGHT_JAVA_SRC").split(File.pathSeparator);
// Resolve in the last specified source dir.
Path sourceFile = Paths.get(srcRoots[srcRoots.length - 1], path + ".java");
byte[] thisFile = Files.readAllBytes(sourceFile);
assertEquals(new String(thisFile, UTF_8), new String(sources.values().iterator().next(), UTF_8));
private void myMethodOuter() {
myMethodInner();
}
private void myMethodInner() {
page.getByText("Click").click();
}
@Test
@@ -140,7 +187,7 @@ public class TestTracing extends TestBase {
}
@Test
void shouldRespectTracesDirAndName(@TempDir Path tempDir) {
void shouldRespectTracesDirAndName(@TempDir Path tempDir) throws Exception {
Path tracesDir = tempDir.resolve("trace-dir");
BrowserType.LaunchOptions options = createLaunchOptions();
options.setTracesDir(tracesDir);
@@ -159,6 +206,24 @@ public class TestTracing extends TestBase {
context.tracing().stop(new Tracing.StopOptions().setPath(tempDir.resolve("trace2.zip")));
assertTrue(Files.exists(tracesDir.resolve("name2.trace")));
assertTrue(Files.exists(tracesDir.resolve("name2.network")));
TraceViewerPage.showTraceViewer(this.browserType, tempDir.resolve("trace1.zip"), traceViewer -> {
assertThat(traceViewer.actionTitles()).hasText(new Pattern[] {
Pattern.compile("Navigate to \"/one-style.html\"")
});
FrameLocator frame = traceViewer.snapshotFrame("Navigate", 0, false);
assertThat(frame.locator("body")).hasCSS("background-color", "rgb(255, 192, 203)");
assertThat(frame.locator("body")).hasText("hello, world!");
});
TraceViewerPage.showTraceViewer(this.browserType, tempDir.resolve("trace2.zip"), traceViewer -> {
assertThat(traceViewer.actionTitles()).hasText(new Pattern[] {
Pattern.compile("Navigate to \"/har.html\"")
});
FrameLocator frame = traceViewer.snapshotFrame("Navigate", 0, false);
assertThat(frame.locator("body")).hasCSS("background-color", "rgb(255, 192, 203)");
assertThat(frame.locator("body")).hasText("hello, world!");
});
}
}
@@ -179,11 +244,9 @@ public class TestTracing extends TestBase {
context.tracing().groupEnd();
context.tracing().groupEnd();
List<TraceEvent> events = parseTraceEvents(traceFile1);
List<TraceEvent> groups = events.stream().filter(e -> "tracingGroup".equals(e.method)).collect(Collectors.toList());
assertEquals(1, groups.size());
assertEquals("actual", groups.get(0).apiName);
TraceViewerPage.showTraceViewer(this.browserType, traceFile1, traceViewer -> {
assertThat(traceViewer.actionTitles()).containsText(new String[] {"actual", "Navigate to \"/empty.html\""});
});
}
@Test
@@ -202,9 +265,16 @@ public class TestTracing extends TestBase {
Path traceFile1 = tempDir.resolve("trace1.zip");
context.tracing().stop(new Tracing.StopOptions().setPath(traceFile1));
List<TraceEvent> events = parseTraceEvents(traceFile1);
List<String> calls = events.stream().filter(e -> e.apiName != null).map(e -> e.apiName).collect(Collectors.toList());
assertEquals(asList("outer group", "Page.navigate", "inner group 1", "Frame.click", "inner group 2", "Page.isVisible"), calls);
TraceViewerPage.showTraceViewer(this.browserType, traceFile1, traceViewer -> {
traceViewer.expandAction("inner group 1");
assertThat(traceViewer.actionTitles()).hasText(new Pattern[] {
Pattern.compile("outer group"),
Pattern.compile("Navigate to \"data:"),
Pattern.compile("inner group 1"),
Pattern.compile("Click"),
Pattern.compile("inner group 2"),
});
});
}
@Test
@@ -240,37 +310,36 @@ public class TestTracing extends TestBase {
Path traceFile1 = tempDir.resolve("trace1.zip");
context.tracing().stop(new Tracing.StopOptions().setPath(traceFile1));
List<TraceEvent> events = parseTraceEvents(traceFile1);
List<String> calls = events.stream().filter(e -> e.apiName != null).map(e -> e.apiName)
.collect(Collectors.toList());
assertEquals(asList(
"Clock.install",
"Page.setContent",
"Frame.click",
"Frame.click",
"Keyboard.type",
"Keyboard.press",
"Keyboard.down",
"Keyboard.insertText",
"Keyboard.up",
"Mouse.move",
"Mouse.down",
"Mouse.move",
"Mouse.wheel",
"Mouse.up",
"Clock.fastForward",
"Clock.fastForward",
"Clock.pauseAt",
"Clock.runFor",
"Clock.setFixedTime",
"Clock.setSystemTime",
"Clock.resume",
"Frame.click"),
calls);
TraceViewerPage.showTraceViewer(this.browserType, traceFile1, traceViewer -> {
assertThat(traceViewer.actionTitles()).hasText(new Pattern[] {
Pattern.compile("Install clock"),
Pattern.compile("Set content"),
Pattern.compile("Click"),
Pattern.compile("Click"),
Pattern.compile("Type"),
Pattern.compile("Press"),
Pattern.compile("Key down"),
Pattern.compile("Insert"),
Pattern.compile("Key up"),
Pattern.compile("Mouse move"),
Pattern.compile("Mouse down"),
Pattern.compile("Mouse move"),
Pattern.compile("Mouse wheel"),
Pattern.compile("Mouse up"),
Pattern.compile("Fast forward clock"),
Pattern.compile("Fast forward clock"),
Pattern.compile("Pause clock"),
Pattern.compile("Run clock"),
Pattern.compile("Set fixed time"),
Pattern.compile("Set system time"),
Pattern.compile("Resume clock"),
Pattern.compile("Click")
});
});
}
@Test
public void shouldNotRecordNetworkActions(@TempDir Path tempDir) throws IOException {
public void shouldNotRecordNetworkActions(@TempDir Path tempDir) throws Exception {
context.tracing().start(new Tracing.StartOptions());
page.onRequest(request -> {
@@ -284,29 +353,10 @@ public class TestTracing extends TestBase {
Path traceFile1 = tempDir.resolve("trace1.zip");
context.tracing().stop(new Tracing.StopOptions().setPath(traceFile1));
List<TraceEvent> events = parseTraceEvents(traceFile1);
List<String> calls = events.stream().filter(e -> e.apiName != null).map(e -> e.apiName)
.collect(Collectors.toList());
assertEquals(asList("Page.navigate"), calls);
}
private static class TraceEvent {
String type;
String name;
String apiName;
String method;
Double startTime;
Double endTime;
String callId;
}
private static List<TraceEvent> parseTraceEvents(Path traceFile) throws IOException {
Map<String, byte[]> files = Utils.parseZip(traceFile);
Map<String, byte[]> traces = files.entrySet().stream().filter(e -> e.getKey().endsWith(".trace")).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
assertNotNull(traces.get("trace.trace"));
return Arrays.stream(new String(traces.get("trace.trace"), UTF_8)
.split("\n"))
.map(s -> new Gson().fromJson(s, TraceEvent.class))
.collect(Collectors.toList());
TraceViewerPage.showTraceViewer(this.browserType, traceFile1, traceViewer -> {
assertThat(traceViewer.actionTitles()).hasText(new Pattern[] {
Pattern.compile("Navigate to \"/empty.html\"")
});
});
}
}

Some files were not shown because too many files have changed in this diff Show More