Compare commits
53 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1cc79c4c41 | |||
| 43da8b23e1 | |||
| bcc240d5cb | |||
| 79668f877f | |||
| e5e9a7db99 | |||
| 0c8706f1eb | |||
| b000a8b6df | |||
| 16cc466622 | |||
| 958813201a | |||
| bc7a59d852 | |||
| a073eb07ae | |||
| 2dbc6194a3 | |||
| 1d69d924cf | |||
| a171c39601 | |||
| 46baa46e36 | |||
| cba51c5e96 | |||
| c17d5d8a9a | |||
| 89894e15d2 | |||
| a012836779 | |||
| 29c0df6443 | |||
| 33ec902eb9 | |||
| 104be4728f | |||
| 08776552da | |||
| 70b9e2e034 | |||
| ebf9b09c34 | |||
| cd3b45acd0 | |||
| 528d01b07a | |||
| ab2efd4d80 | |||
| 1eb3f3bb80 | |||
| fcac298050 | |||
| 476e222c93 | |||
| 696610de15 | |||
| 3e7f8017e0 | |||
| 3631357a35 | |||
| 79529a7239 | |||
| 04419c7e5f | |||
| fb508701ef | |||
| 654a4dc12f | |||
| 5eecbb5252 | |||
| 1b58892e58 | |||
| a1ef49cd03 | |||
| 2b0b50358b | |||
| 399de8e899 | |||
| c66b95afb3 | |||
| 1f0f1b06e1 | |||
| 5a0dd8595c | |||
| 518f117fb0 | |||
| 539b18f167 | |||
| 1958a2fa64 | |||
| 87ad579deb | |||
| e83ef2b1c0 | |||
| f23c5d4c01 | |||
| d3f06cefd8 |
@@ -0,0 +1,41 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Something doesn't work like it should? Tell us!
|
||||
title: "[BUG]"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Context:**
|
||||
- Playwright Version: [what Playwright version do you use?]
|
||||
- Operating System: [e.g. Windows, Linux or Mac]
|
||||
- Browser: [e.g. All, Chromium, Firefox, WebKit]
|
||||
- Extra: [any specific details about your environment]
|
||||
|
||||
<!-- CLI to auto-capture this info -->
|
||||
<!-- npx envinfo --preset playwright --markdown -->
|
||||
|
||||
**Code Snippet**
|
||||
|
||||
Help us help you! Put down a short code snippet that illustrates your bug and
|
||||
that we can run and debug locally. For example:
|
||||
|
||||
```java
|
||||
import com.microsoft.playwright.*;
|
||||
|
||||
public class ExampleReproducible {
|
||||
public static void main(String[] args) {
|
||||
try (Playwright playwright = Playwright.create()) {
|
||||
Browser browser = playwright.chromium().launch();
|
||||
BrowserContext context = browser.newContext();
|
||||
Page page = context.newPage();
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Describe the bug**
|
||||
|
||||
Add any other details about the problem here.
|
||||
@@ -0,0 +1,4 @@
|
||||
contact_links:
|
||||
- name: Join our Slack community
|
||||
url: https://aka.ms/playwright-slack
|
||||
about: Ask questions and discuss with other community members
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Request new features to be added
|
||||
title: "[Feature]"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Let us know what functionality you'd like to see in Playwright and what your use case is.
|
||||
Do you think others might benefit from this as well?
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: I have a question
|
||||
about: Feel free to ask us your questions!
|
||||
title: "[Question]"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
name: Report regression
|
||||
about: Functionality that used to work and does not any more
|
||||
title: "[REGRESSION]: "
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Context:**
|
||||
- GOOD Playwright Version: [what Playwright version worked nicely?]
|
||||
- BAD Playwright Version: [what Playwright version doesn't work any more?]
|
||||
- Operating System: [e.g. Windows, Linux or Mac]
|
||||
- Extra: [any specific details about your environment]
|
||||
|
||||
**Code Snippet**
|
||||
|
||||
Help us help you! Put down a short code snippet that illustrates your bug and
|
||||
that we can run and debug locally. For example:
|
||||
|
||||
```java
|
||||
import com.microsoft.playwright.*;
|
||||
|
||||
public class ExampleReproducible {
|
||||
public static void main(String[] args) {
|
||||
try (Playwright playwright = Playwright.create()) {
|
||||
Browser browser = playwright.chromium().launch();
|
||||
BrowserContext context = browser.newContext();
|
||||
Page page = context.newPage();
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Describe the bug**
|
||||
|
||||
Add any other details about the problem here.
|
||||
@@ -1,15 +1,16 @@
|
||||
name: Publish
|
||||
on:
|
||||
workflow_dispatch
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
build:
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: microsoft/playwright-github-action@v1
|
||||
- uses: microsoft/playwright-github-action@v1.5.0
|
||||
- name: Set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: microsoft/playwright-github-action@v1
|
||||
- uses: microsoft/playwright-github-action@v1.5.0
|
||||
- name: Set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: microsoft/playwright-github-action@v1
|
||||
- uses: microsoft/playwright-github-action@v1.5.0
|
||||
- name: Install Media Pack
|
||||
if: matrix.os == 'windows-latest'
|
||||
shell: powershell
|
||||
|
||||
@@ -22,6 +22,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: microsoft/playwright-github-action@v1.5.0
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
@@ -32,10 +33,12 @@ jobs:
|
||||
run: scripts/download_driver_for_all_platforms.sh
|
||||
- name: Regenerate APIs
|
||||
run: scripts/generate_api.sh
|
||||
- name: Update browser versions in README
|
||||
run: scripts/update_readme.sh
|
||||
- name: Verify API is up to date
|
||||
run: |
|
||||
if [[ -n $(git status -s) ]]; then
|
||||
echo "ERROR: generated interfaces differ from the current sources:"
|
||||
echo "ERROR: generated interfaces/docs differ from the current sources:"
|
||||
git diff
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -33,6 +33,10 @@ Names of published driver archives can be found at https://github.com/microsoft/
|
||||
```bash
|
||||
mvn compile
|
||||
mvn test
|
||||
# Executing a single test
|
||||
BROWSER=chromium mvn test -Dtest=TestPageNetworkSizes#shouldHaveTheCorrectResponseBodySize
|
||||
# Executing a single test class
|
||||
BROWSER=chromium mvn test -Dtest=TestPageNetworkSizes
|
||||
```
|
||||
|
||||
### Generating API
|
||||
|
||||
@@ -11,9 +11,9 @@ Playwright is a Java library to automate [Chromium](https://www.chromium.org/Hom
|
||||
|
||||
| | Linux | macOS | Windows |
|
||||
| :--- | :---: | :---: | :---: |
|
||||
| Chromium <!-- GEN:chromium-version -->92.0.4498.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| WebKit <!-- GEN:webkit-version -->14.2<!-- GEN:stop --> | ✅ | ✅ | ✅ |
|
||||
| Firefox <!-- GEN:firefox-version -->89.0b6<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Chromium <!-- GEN:chromium-version -->96.0.4641.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| WebKit <!-- GEN:webkit-version -->15.0<!-- GEN:stop --> | ✅ | ✅ | ✅ |
|
||||
| Firefox <!-- GEN:firefox-version -->92.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
|
||||
Headless execution is supported for all the browsers on all platforms. Check out [system requirements](https://playwright.dev/java/docs/next/intro/#system-requirements) for details.
|
||||
|
||||
@@ -43,7 +43,7 @@ To run Playwright simply add following dependency to your Maven project:
|
||||
<dependency>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>playwright</artifactId>
|
||||
<version>1.11.0</version>
|
||||
<version>1.14.1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# Rolling Playwright-Java to the latest Playwright driver
|
||||
|
||||
* make sure to have at least Java 8 and Maven 3.6.3
|
||||
* clone playwright for java: http://github.com/microsoft/playwright-java
|
||||
* set new driver version in `scripts/CLI_VERSION`
|
||||
* regenerate API: `./scripts/download_driver_for_all_platforms.sh -f && ./scripts/generate_api.sh && ./scripts/update_readme.sh`
|
||||
* commit & send PR with the roll
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>parent-pom</artifactId>
|
||||
<version>1.12.0-SNAPSHOT</version>
|
||||
<version>1.15.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>driver-bundle</artifactId>
|
||||
|
||||
@@ -21,25 +21,40 @@ import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.*;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class DriverJar extends Driver {
|
||||
private static final String PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD = "PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD";
|
||||
private final Path driverTempDir;
|
||||
|
||||
DriverJar() throws IOException, URISyntaxException, InterruptedException {
|
||||
driverTempDir = Files.createTempDirectory("playwright-java-");
|
||||
driverTempDir.toFile().deleteOnExit();
|
||||
extractDriverToTempDir();
|
||||
installBrowsers();
|
||||
}
|
||||
|
||||
private void installBrowsers() throws IOException, InterruptedException {
|
||||
@Override
|
||||
protected void initialize(Map<String, String> env) throws Exception {
|
||||
extractDriverToTempDir();
|
||||
installBrowsers(env);
|
||||
}
|
||||
|
||||
private void installBrowsers(Map<String, String> env) throws IOException, InterruptedException {
|
||||
String skip = env.get(PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD);
|
||||
if (skip == null) {
|
||||
skip = System.getenv(PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD);
|
||||
}
|
||||
if (skip != null && !"0".equals(skip) && !"false".equals(skip)) {
|
||||
System.out.println("Skipping browsers download because `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD` env variable is set");
|
||||
return;
|
||||
}
|
||||
String cliFileName = super.cliFileName();
|
||||
Path driver = driverTempDir.resolve(cliFileName);
|
||||
if (!Files.exists(driver)) {
|
||||
throw new RuntimeException("Failed to find " + cliFileName + " at " + driver);
|
||||
}
|
||||
ProcessBuilder pb = new ProcessBuilder(driver.toString(), "install");
|
||||
pb.environment().putAll(env);
|
||||
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
|
||||
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
|
||||
Process p = pb.start();
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
@@ -31,7 +32,7 @@ public class TestInstall {
|
||||
void playwrightCliInstalled() throws Exception {
|
||||
// Clear system property to ensure that the driver is loaded from jar.
|
||||
System.clearProperty("playwright.cli.dir");
|
||||
Path cli = Driver.ensureDriverInstalled();
|
||||
Path cli = Driver.ensureDriverInstalled(Collections.emptyMap());
|
||||
assertTrue(Files.exists(cli));
|
||||
|
||||
ProcessBuilder pb = new ProcessBuilder(cli.toString(), "install");
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>parent-pom</artifactId>
|
||||
<version>1.12.0-SNAPSHOT</version>
|
||||
<version>1.15.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>driver</artifactId>
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.microsoft.playwright.impl;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This class provides access to playwright-cli. It can be either preinstalled
|
||||
@@ -32,16 +33,23 @@ public abstract class Driver {
|
||||
PreinstalledDriver(Path driverDir) {
|
||||
this.driverDir = driverDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize(Map<String, String> env) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
Path driverDir() {
|
||||
return driverDir;
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized Path ensureDriverInstalled() {
|
||||
public static synchronized Path ensureDriverInstalled(Map<String, String> env) {
|
||||
if (instance == null) {
|
||||
try {
|
||||
instance = createDriver();
|
||||
instance.initialize(env);
|
||||
} catch (Exception exception) {
|
||||
throw new RuntimeException("Failed to create driver", exception);
|
||||
}
|
||||
@@ -50,6 +58,8 @@ public abstract class Driver {
|
||||
return instance.driverDir().resolve(name);
|
||||
}
|
||||
|
||||
protected abstract void initialize(Map<String, String> env) throws Exception;
|
||||
|
||||
protected String cliFileName() {
|
||||
return System.getProperty("os.name").toLowerCase().contains("windows") ?
|
||||
"playwright.cmd" : "playwright.sh";
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>org.example</groupId>
|
||||
<artifactId>examples</artifactId>
|
||||
<version>1.12.0-SNAPSHOT</version>
|
||||
<version>1.15.2</version>
|
||||
<name>Playwright Client Examples</name>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"catalogs": {},
|
||||
"aliases": {
|
||||
"playwright": {
|
||||
"script-ref": "scripts/playwright.java",
|
||||
"description": "Playwright lets you automate Chromium, Firefox and Webkit with a single API. \nWith this cli you can install, trace, generate pdf and screenshots and more.\nExample on how to record and run a script:\n```\n jbang playwright@microsoft/playwright-java codegen -o Example.java`\n jbang --deps com.microsoft.playwright:playwright:RELEASE Example.java\n```"
|
||||
}
|
||||
},
|
||||
"templates": {}
|
||||
}
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>parent-pom</artifactId>
|
||||
<version>1.12.0-SNAPSHOT</version>
|
||||
<version>1.15.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>playwright</artifactId>
|
||||
|
||||
@@ -60,6 +60,18 @@ public interface Browser extends AutoCloseable {
|
||||
* Whether to automatically download all the attachments. Defaults to {@code false} where all the downloads are canceled.
|
||||
*/
|
||||
public Boolean acceptDownloads;
|
||||
/**
|
||||
* When using {@link Page#navigate Page.navigate()}, {@link Page#route Page.route()}, {@link Page#waitForURL
|
||||
* Page.waitForURL()}, {@link Page#waitForRequest Page.waitForRequest()}, or {@link Page#waitForResponse
|
||||
* Page.waitForResponse()} it takes the base URL in consideration by using the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code URL()}</a> constructor for building the corresponding
|
||||
* URL. Examples:
|
||||
* <ul>
|
||||
* <li> baseURL: {@code http://localhost:3000} and navigating to {@code /bar.html} results in {@code http://localhost:3000/bar.html}</li>
|
||||
* <li> baseURL: {@code http://localhost:3000/foo/} and navigating to {@code ./bar.html} results in {@code http://localhost:3000/foo/bar.html}</li>
|
||||
* </ul>
|
||||
*/
|
||||
public String baseURL;
|
||||
/**
|
||||
* Toggles bypassing page's Content-Security-Policy.
|
||||
*/
|
||||
@@ -74,9 +86,17 @@ public interface Browser extends AutoCloseable {
|
||||
*/
|
||||
public Double deviceScaleFactor;
|
||||
/**
|
||||
* An object containing additional HTTP headers to be sent with every request. All header values must be strings.
|
||||
* An object containing additional HTTP headers to be sent with every request.
|
||||
*/
|
||||
public Map<String, String> extraHTTPHeaders;
|
||||
/**
|
||||
* Emulates {@code "forced-colors"} media feature, supported values are {@code "active"}, {@code "none"}. See {@link Page#emulateMedia
|
||||
* Page.emulateMedia()} for more details. Defaults to {@code "none"}.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> It's not supported in WebKit, see <a href="https://bugs.webkit.org/show_bug.cgi?id=225281">here</a> in their issue
|
||||
* tracker.
|
||||
*/
|
||||
public ForcedColors forcedColors;
|
||||
public Geolocation geolocation;
|
||||
/**
|
||||
* Specifies if viewport supports touch events. Defaults to false.
|
||||
@@ -87,7 +107,7 @@ public interface Browser extends AutoCloseable {
|
||||
*/
|
||||
public HttpCredentials httpCredentials;
|
||||
/**
|
||||
* Whether to ignore HTTPS errors during navigation. Defaults to {@code false}.
|
||||
* Whether to ignore HTTPS errors when sending network requests. Defaults to {@code false}.
|
||||
*/
|
||||
public Boolean ignoreHTTPSErrors;
|
||||
/**
|
||||
@@ -163,6 +183,12 @@ public interface Browser extends AutoCloseable {
|
||||
* state.
|
||||
*/
|
||||
public Path storageStatePath;
|
||||
/**
|
||||
* It specified, enables strict selectors mode for this context. In the strict selectors mode all operations on selectors
|
||||
* that imply single target DOM element will throw when more than one element matches the selector. See {@code Locator} to learn
|
||||
* more about the strict mode.
|
||||
*/
|
||||
public Boolean strictSelectors;
|
||||
/**
|
||||
* Changes the timezone of the context. See <a
|
||||
* href="https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1">ICU's
|
||||
@@ -178,26 +204,68 @@ public interface Browser extends AutoCloseable {
|
||||
*/
|
||||
public Optional<ViewportSize> viewportSize;
|
||||
|
||||
/**
|
||||
* Whether to automatically download all the attachments. Defaults to {@code false} where all the downloads are canceled.
|
||||
*/
|
||||
public NewContextOptions setAcceptDownloads(boolean acceptDownloads) {
|
||||
this.acceptDownloads = acceptDownloads;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* When using {@link Page#navigate Page.navigate()}, {@link Page#route Page.route()}, {@link Page#waitForURL
|
||||
* Page.waitForURL()}, {@link Page#waitForRequest Page.waitForRequest()}, or {@link Page#waitForResponse
|
||||
* Page.waitForResponse()} it takes the base URL in consideration by using the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code URL()}</a> constructor for building the corresponding
|
||||
* URL. Examples:
|
||||
* <ul>
|
||||
* <li> baseURL: {@code http://localhost:3000} and navigating to {@code /bar.html} results in {@code http://localhost:3000/bar.html}</li>
|
||||
* <li> baseURL: {@code http://localhost:3000/foo/} and navigating to {@code ./bar.html} results in {@code http://localhost:3000/foo/bar.html}</li>
|
||||
* </ul>
|
||||
*/
|
||||
public NewContextOptions setBaseURL(String baseURL) {
|
||||
this.baseURL = baseURL;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Toggles bypassing page's Content-Security-Policy.
|
||||
*/
|
||||
public NewContextOptions setBypassCSP(boolean bypassCSP) {
|
||||
this.bypassCSP = bypassCSP;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code "no-preference"}. See
|
||||
* {@link Page#emulateMedia Page.emulateMedia()} for more details. Defaults to {@code "light"}.
|
||||
*/
|
||||
public NewContextOptions setColorScheme(ColorScheme colorScheme) {
|
||||
this.colorScheme = colorScheme;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Specify device scale factor (can be thought of as dpr). Defaults to {@code 1}.
|
||||
*/
|
||||
public NewContextOptions setDeviceScaleFactor(double deviceScaleFactor) {
|
||||
this.deviceScaleFactor = deviceScaleFactor;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* An object containing additional HTTP headers to be sent with every request.
|
||||
*/
|
||||
public NewContextOptions setExtraHTTPHeaders(Map<String, String> extraHTTPHeaders) {
|
||||
this.extraHTTPHeaders = extraHTTPHeaders;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates {@code "forced-colors"} media feature, supported values are {@code "active"}, {@code "none"}. See {@link Page#emulateMedia
|
||||
* Page.emulateMedia()} for more details. Defaults to {@code "none"}.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> It's not supported in WebKit, see <a href="https://bugs.webkit.org/show_bug.cgi?id=225281">here</a> in their issue
|
||||
* tracker.
|
||||
*/
|
||||
public NewContextOptions setForcedColors(ForcedColors forcedColors) {
|
||||
this.forcedColors = forcedColors;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions setGeolocation(double latitude, double longitude) {
|
||||
return setGeolocation(new Geolocation(latitude, longitude));
|
||||
}
|
||||
@@ -205,97 +273,207 @@ public interface Browser extends AutoCloseable {
|
||||
this.geolocation = geolocation;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Specifies if viewport supports touch events. Defaults to false.
|
||||
*/
|
||||
public NewContextOptions setHasTouch(boolean hasTouch) {
|
||||
this.hasTouch = hasTouch;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Credentials for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication">HTTP authentication</a>.
|
||||
*/
|
||||
public NewContextOptions setHttpCredentials(String username, String password) {
|
||||
return setHttpCredentials(new HttpCredentials(username, password));
|
||||
}
|
||||
/**
|
||||
* Credentials for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication">HTTP authentication</a>.
|
||||
*/
|
||||
public NewContextOptions setHttpCredentials(HttpCredentials httpCredentials) {
|
||||
this.httpCredentials = httpCredentials;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Whether to ignore HTTPS errors when sending network requests. Defaults to {@code false}.
|
||||
*/
|
||||
public NewContextOptions setIgnoreHTTPSErrors(boolean ignoreHTTPSErrors) {
|
||||
this.ignoreHTTPSErrors = ignoreHTTPSErrors;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Whether the {@code meta viewport} tag is taken into account and touch events are enabled. Defaults to {@code false}. Not supported
|
||||
* in Firefox.
|
||||
*/
|
||||
public NewContextOptions setIsMobile(boolean isMobile) {
|
||||
this.isMobile = isMobile;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Whether or not to enable JavaScript in the context. Defaults to {@code true}.
|
||||
*/
|
||||
public NewContextOptions setJavaScriptEnabled(boolean javaScriptEnabled) {
|
||||
this.javaScriptEnabled = javaScriptEnabled;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Specify user locale, for example {@code en-GB}, {@code de-DE}, etc. Locale will affect {@code navigator.language} value, {@code Accept-Language}
|
||||
* request header value as well as number and date formatting rules.
|
||||
*/
|
||||
public NewContextOptions setLocale(String locale) {
|
||||
this.locale = locale;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Whether to emulate network being offline. Defaults to {@code false}.
|
||||
*/
|
||||
public NewContextOptions setOffline(boolean offline) {
|
||||
this.offline = offline;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A list of permissions to grant to all pages in this context. See {@link BrowserContext#grantPermissions
|
||||
* BrowserContext.grantPermissions()} for more details.
|
||||
*/
|
||||
public NewContextOptions setPermissions(List<String> permissions) {
|
||||
this.permissions = permissions;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Network proxy settings to use with this context.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> For Chromium on Windows the browser needs to be launched with the global proxy for this option to work. If all contexts
|
||||
* override the proxy, global proxy will be never used and can be any string, for example {@code launch({ proxy: { server:
|
||||
* 'http://per-context' } })}.
|
||||
*/
|
||||
public NewContextOptions setProxy(String server) {
|
||||
return setProxy(new Proxy(server));
|
||||
}
|
||||
/**
|
||||
* Network proxy settings to use with this context.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> For Chromium on Windows the browser needs to be launched with the global proxy for this option to work. If all contexts
|
||||
* override the proxy, global proxy will be never used and can be any string, for example {@code launch({ proxy: { server:
|
||||
* 'http://per-context' } })}.
|
||||
*/
|
||||
public NewContextOptions setProxy(Proxy proxy) {
|
||||
this.proxy = proxy;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Optional setting to control whether to omit request content from the HAR. Defaults to {@code false}.
|
||||
*/
|
||||
public NewContextOptions setRecordHarOmitContent(boolean recordHarOmitContent) {
|
||||
this.recordHarOmitContent = recordHarOmitContent;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Enables <a href="http://www.softwareishard.com/blog/har-12-spec">HAR</a> recording for all pages into the specified HAR
|
||||
* file on the filesystem. If not specified, the HAR is not recorded. Make sure to call {@link BrowserContext#close
|
||||
* BrowserContext.close()} for the HAR to be saved.
|
||||
*/
|
||||
public NewContextOptions setRecordHarPath(Path recordHarPath) {
|
||||
this.recordHarPath = recordHarPath;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Enables video recording for all pages into the specified directory. If not specified videos are not recorded. Make sure
|
||||
* to call {@link BrowserContext#close BrowserContext.close()} for videos to be saved.
|
||||
*/
|
||||
public NewContextOptions setRecordVideoDir(Path recordVideoDir) {
|
||||
this.recordVideoDir = recordVideoDir;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Dimensions of the recorded videos. If not specified the size will be equal to {@code viewport} scaled down to fit into
|
||||
* 800x800. If {@code viewport} is not configured explicitly the video size defaults to 800x450. Actual picture of each page will
|
||||
* be scaled down if necessary to fit the specified size.
|
||||
*/
|
||||
public NewContextOptions setRecordVideoSize(int width, int height) {
|
||||
return setRecordVideoSize(new RecordVideoSize(width, height));
|
||||
}
|
||||
/**
|
||||
* Dimensions of the recorded videos. If not specified the size will be equal to {@code viewport} scaled down to fit into
|
||||
* 800x800. If {@code viewport} is not configured explicitly the video size defaults to 800x450. Actual picture of each page will
|
||||
* be scaled down if necessary to fit the specified size.
|
||||
*/
|
||||
public NewContextOptions setRecordVideoSize(RecordVideoSize recordVideoSize) {
|
||||
this.recordVideoSize = recordVideoSize;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates {@code "prefers-reduced-motion"} media feature, supported values are {@code "reduce"}, {@code "no-preference"}. See {@link
|
||||
* Page#emulateMedia Page.emulateMedia()} for more details. Defaults to {@code "no-preference"}.
|
||||
*/
|
||||
public NewContextOptions setReducedMotion(ReducedMotion reducedMotion) {
|
||||
this.reducedMotion = reducedMotion;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates consistent window screen size available inside web page via {@code window.screen}. Is only used when the {@code viewport}
|
||||
* is set.
|
||||
*/
|
||||
public NewContextOptions setScreenSize(int width, int height) {
|
||||
return setScreenSize(new ScreenSize(width, height));
|
||||
}
|
||||
/**
|
||||
* Emulates consistent window screen size available inside web page via {@code window.screen}. Is only used when the {@code viewport}
|
||||
* is set.
|
||||
*/
|
||||
public NewContextOptions setScreenSize(ScreenSize screenSize) {
|
||||
this.screenSize = screenSize;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Populates context with given storage state. This option can be used to initialize context with logged-in information
|
||||
* obtained via {@link BrowserContext#storageState BrowserContext.storageState()}.
|
||||
*/
|
||||
public NewContextOptions setStorageState(String storageState) {
|
||||
this.storageState = storageState;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Populates context with given storage state. This option can be used to initialize context with logged-in information
|
||||
* obtained via {@link BrowserContext#storageState BrowserContext.storageState()}. Path to the file with saved storage
|
||||
* state.
|
||||
*/
|
||||
public NewContextOptions setStorageStatePath(Path storageStatePath) {
|
||||
this.storageStatePath = storageStatePath;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* It specified, enables strict selectors mode for this context. In the strict selectors mode all operations on selectors
|
||||
* that imply single target DOM element will throw when more than one element matches the selector. See {@code Locator} to learn
|
||||
* more about the strict mode.
|
||||
*/
|
||||
public NewContextOptions setStrictSelectors(boolean strictSelectors) {
|
||||
this.strictSelectors = strictSelectors;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Changes the timezone of the context. See <a
|
||||
* href="https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1">ICU's
|
||||
* metaZones.txt</a> for a list of supported timezone IDs.
|
||||
*/
|
||||
public NewContextOptions setTimezoneId(String timezoneId) {
|
||||
this.timezoneId = timezoneId;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Specific user agent to use in this context.
|
||||
*/
|
||||
public NewContextOptions setUserAgent(String userAgent) {
|
||||
this.userAgent = userAgent;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default viewport.
|
||||
*/
|
||||
public NewContextOptions setViewportSize(int width, int height) {
|
||||
return setViewportSize(new ViewportSize(width, height));
|
||||
}
|
||||
/**
|
||||
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default viewport.
|
||||
*/
|
||||
public NewContextOptions setViewportSize(ViewportSize viewportSize) {
|
||||
this.viewportSize = Optional.ofNullable(viewportSize);
|
||||
return this;
|
||||
@@ -306,6 +484,18 @@ public interface Browser extends AutoCloseable {
|
||||
* Whether to automatically download all the attachments. Defaults to {@code false} where all the downloads are canceled.
|
||||
*/
|
||||
public Boolean acceptDownloads;
|
||||
/**
|
||||
* When using {@link Page#navigate Page.navigate()}, {@link Page#route Page.route()}, {@link Page#waitForURL
|
||||
* Page.waitForURL()}, {@link Page#waitForRequest Page.waitForRequest()}, or {@link Page#waitForResponse
|
||||
* Page.waitForResponse()} it takes the base URL in consideration by using the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code URL()}</a> constructor for building the corresponding
|
||||
* URL. Examples:
|
||||
* <ul>
|
||||
* <li> baseURL: {@code http://localhost:3000} and navigating to {@code /bar.html} results in {@code http://localhost:3000/bar.html}</li>
|
||||
* <li> baseURL: {@code http://localhost:3000/foo/} and navigating to {@code ./bar.html} results in {@code http://localhost:3000/foo/bar.html}</li>
|
||||
* </ul>
|
||||
*/
|
||||
public String baseURL;
|
||||
/**
|
||||
* Toggles bypassing page's Content-Security-Policy.
|
||||
*/
|
||||
@@ -320,9 +510,17 @@ public interface Browser extends AutoCloseable {
|
||||
*/
|
||||
public Double deviceScaleFactor;
|
||||
/**
|
||||
* An object containing additional HTTP headers to be sent with every request. All header values must be strings.
|
||||
* An object containing additional HTTP headers to be sent with every request.
|
||||
*/
|
||||
public Map<String, String> extraHTTPHeaders;
|
||||
/**
|
||||
* Emulates {@code "forced-colors"} media feature, supported values are {@code "active"}, {@code "none"}. See {@link Page#emulateMedia
|
||||
* Page.emulateMedia()} for more details. Defaults to {@code "none"}.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> It's not supported in WebKit, see <a href="https://bugs.webkit.org/show_bug.cgi?id=225281">here</a> in their issue
|
||||
* tracker.
|
||||
*/
|
||||
public ForcedColors forcedColors;
|
||||
public Geolocation geolocation;
|
||||
/**
|
||||
* Specifies if viewport supports touch events. Defaults to false.
|
||||
@@ -333,7 +531,7 @@ public interface Browser extends AutoCloseable {
|
||||
*/
|
||||
public HttpCredentials httpCredentials;
|
||||
/**
|
||||
* Whether to ignore HTTPS errors during navigation. Defaults to {@code false}.
|
||||
* Whether to ignore HTTPS errors when sending network requests. Defaults to {@code false}.
|
||||
*/
|
||||
public Boolean ignoreHTTPSErrors;
|
||||
/**
|
||||
@@ -409,6 +607,12 @@ public interface Browser extends AutoCloseable {
|
||||
* state.
|
||||
*/
|
||||
public Path storageStatePath;
|
||||
/**
|
||||
* It specified, enables strict selectors mode for this context. In the strict selectors mode all operations on selectors
|
||||
* that imply single target DOM element will throw when more than one element matches the selector. See {@code Locator} to learn
|
||||
* more about the strict mode.
|
||||
*/
|
||||
public Boolean strictSelectors;
|
||||
/**
|
||||
* Changes the timezone of the context. See <a
|
||||
* href="https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1">ICU's
|
||||
@@ -424,26 +628,68 @@ public interface Browser extends AutoCloseable {
|
||||
*/
|
||||
public Optional<ViewportSize> viewportSize;
|
||||
|
||||
/**
|
||||
* Whether to automatically download all the attachments. Defaults to {@code false} where all the downloads are canceled.
|
||||
*/
|
||||
public NewPageOptions setAcceptDownloads(boolean acceptDownloads) {
|
||||
this.acceptDownloads = acceptDownloads;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* When using {@link Page#navigate Page.navigate()}, {@link Page#route Page.route()}, {@link Page#waitForURL
|
||||
* Page.waitForURL()}, {@link Page#waitForRequest Page.waitForRequest()}, or {@link Page#waitForResponse
|
||||
* Page.waitForResponse()} it takes the base URL in consideration by using the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code URL()}</a> constructor for building the corresponding
|
||||
* URL. Examples:
|
||||
* <ul>
|
||||
* <li> baseURL: {@code http://localhost:3000} and navigating to {@code /bar.html} results in {@code http://localhost:3000/bar.html}</li>
|
||||
* <li> baseURL: {@code http://localhost:3000/foo/} and navigating to {@code ./bar.html} results in {@code http://localhost:3000/foo/bar.html}</li>
|
||||
* </ul>
|
||||
*/
|
||||
public NewPageOptions setBaseURL(String baseURL) {
|
||||
this.baseURL = baseURL;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Toggles bypassing page's Content-Security-Policy.
|
||||
*/
|
||||
public NewPageOptions setBypassCSP(boolean bypassCSP) {
|
||||
this.bypassCSP = bypassCSP;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code "no-preference"}. See
|
||||
* {@link Page#emulateMedia Page.emulateMedia()} for more details. Defaults to {@code "light"}.
|
||||
*/
|
||||
public NewPageOptions setColorScheme(ColorScheme colorScheme) {
|
||||
this.colorScheme = colorScheme;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Specify device scale factor (can be thought of as dpr). Defaults to {@code 1}.
|
||||
*/
|
||||
public NewPageOptions setDeviceScaleFactor(double deviceScaleFactor) {
|
||||
this.deviceScaleFactor = deviceScaleFactor;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* An object containing additional HTTP headers to be sent with every request.
|
||||
*/
|
||||
public NewPageOptions setExtraHTTPHeaders(Map<String, String> extraHTTPHeaders) {
|
||||
this.extraHTTPHeaders = extraHTTPHeaders;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates {@code "forced-colors"} media feature, supported values are {@code "active"}, {@code "none"}. See {@link Page#emulateMedia
|
||||
* Page.emulateMedia()} for more details. Defaults to {@code "none"}.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> It's not supported in WebKit, see <a href="https://bugs.webkit.org/show_bug.cgi?id=225281">here</a> in their issue
|
||||
* tracker.
|
||||
*/
|
||||
public NewPageOptions setForcedColors(ForcedColors forcedColors) {
|
||||
this.forcedColors = forcedColors;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions setGeolocation(double latitude, double longitude) {
|
||||
return setGeolocation(new Geolocation(latitude, longitude));
|
||||
}
|
||||
@@ -451,97 +697,207 @@ public interface Browser extends AutoCloseable {
|
||||
this.geolocation = geolocation;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Specifies if viewport supports touch events. Defaults to false.
|
||||
*/
|
||||
public NewPageOptions setHasTouch(boolean hasTouch) {
|
||||
this.hasTouch = hasTouch;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Credentials for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication">HTTP authentication</a>.
|
||||
*/
|
||||
public NewPageOptions setHttpCredentials(String username, String password) {
|
||||
return setHttpCredentials(new HttpCredentials(username, password));
|
||||
}
|
||||
/**
|
||||
* Credentials for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication">HTTP authentication</a>.
|
||||
*/
|
||||
public NewPageOptions setHttpCredentials(HttpCredentials httpCredentials) {
|
||||
this.httpCredentials = httpCredentials;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Whether to ignore HTTPS errors when sending network requests. Defaults to {@code false}.
|
||||
*/
|
||||
public NewPageOptions setIgnoreHTTPSErrors(boolean ignoreHTTPSErrors) {
|
||||
this.ignoreHTTPSErrors = ignoreHTTPSErrors;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Whether the {@code meta viewport} tag is taken into account and touch events are enabled. Defaults to {@code false}. Not supported
|
||||
* in Firefox.
|
||||
*/
|
||||
public NewPageOptions setIsMobile(boolean isMobile) {
|
||||
this.isMobile = isMobile;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Whether or not to enable JavaScript in the context. Defaults to {@code true}.
|
||||
*/
|
||||
public NewPageOptions setJavaScriptEnabled(boolean javaScriptEnabled) {
|
||||
this.javaScriptEnabled = javaScriptEnabled;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Specify user locale, for example {@code en-GB}, {@code de-DE}, etc. Locale will affect {@code navigator.language} value, {@code Accept-Language}
|
||||
* request header value as well as number and date formatting rules.
|
||||
*/
|
||||
public NewPageOptions setLocale(String locale) {
|
||||
this.locale = locale;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Whether to emulate network being offline. Defaults to {@code false}.
|
||||
*/
|
||||
public NewPageOptions setOffline(boolean offline) {
|
||||
this.offline = offline;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A list of permissions to grant to all pages in this context. See {@link BrowserContext#grantPermissions
|
||||
* BrowserContext.grantPermissions()} for more details.
|
||||
*/
|
||||
public NewPageOptions setPermissions(List<String> permissions) {
|
||||
this.permissions = permissions;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Network proxy settings to use with this context.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> For Chromium on Windows the browser needs to be launched with the global proxy for this option to work. If all contexts
|
||||
* override the proxy, global proxy will be never used and can be any string, for example {@code launch({ proxy: { server:
|
||||
* 'http://per-context' } })}.
|
||||
*/
|
||||
public NewPageOptions setProxy(String server) {
|
||||
return setProxy(new Proxy(server));
|
||||
}
|
||||
/**
|
||||
* Network proxy settings to use with this context.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> For Chromium on Windows the browser needs to be launched with the global proxy for this option to work. If all contexts
|
||||
* override the proxy, global proxy will be never used and can be any string, for example {@code launch({ proxy: { server:
|
||||
* 'http://per-context' } })}.
|
||||
*/
|
||||
public NewPageOptions setProxy(Proxy proxy) {
|
||||
this.proxy = proxy;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Optional setting to control whether to omit request content from the HAR. Defaults to {@code false}.
|
||||
*/
|
||||
public NewPageOptions setRecordHarOmitContent(boolean recordHarOmitContent) {
|
||||
this.recordHarOmitContent = recordHarOmitContent;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Enables <a href="http://www.softwareishard.com/blog/har-12-spec">HAR</a> recording for all pages into the specified HAR
|
||||
* file on the filesystem. If not specified, the HAR is not recorded. Make sure to call {@link BrowserContext#close
|
||||
* BrowserContext.close()} for the HAR to be saved.
|
||||
*/
|
||||
public NewPageOptions setRecordHarPath(Path recordHarPath) {
|
||||
this.recordHarPath = recordHarPath;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Enables video recording for all pages into the specified directory. If not specified videos are not recorded. Make sure
|
||||
* to call {@link BrowserContext#close BrowserContext.close()} for videos to be saved.
|
||||
*/
|
||||
public NewPageOptions setRecordVideoDir(Path recordVideoDir) {
|
||||
this.recordVideoDir = recordVideoDir;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Dimensions of the recorded videos. If not specified the size will be equal to {@code viewport} scaled down to fit into
|
||||
* 800x800. If {@code viewport} is not configured explicitly the video size defaults to 800x450. Actual picture of each page will
|
||||
* be scaled down if necessary to fit the specified size.
|
||||
*/
|
||||
public NewPageOptions setRecordVideoSize(int width, int height) {
|
||||
return setRecordVideoSize(new RecordVideoSize(width, height));
|
||||
}
|
||||
/**
|
||||
* Dimensions of the recorded videos. If not specified the size will be equal to {@code viewport} scaled down to fit into
|
||||
* 800x800. If {@code viewport} is not configured explicitly the video size defaults to 800x450. Actual picture of each page will
|
||||
* be scaled down if necessary to fit the specified size.
|
||||
*/
|
||||
public NewPageOptions setRecordVideoSize(RecordVideoSize recordVideoSize) {
|
||||
this.recordVideoSize = recordVideoSize;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates {@code "prefers-reduced-motion"} media feature, supported values are {@code "reduce"}, {@code "no-preference"}. See {@link
|
||||
* Page#emulateMedia Page.emulateMedia()} for more details. Defaults to {@code "no-preference"}.
|
||||
*/
|
||||
public NewPageOptions setReducedMotion(ReducedMotion reducedMotion) {
|
||||
this.reducedMotion = reducedMotion;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates consistent window screen size available inside web page via {@code window.screen}. Is only used when the {@code viewport}
|
||||
* is set.
|
||||
*/
|
||||
public NewPageOptions setScreenSize(int width, int height) {
|
||||
return setScreenSize(new ScreenSize(width, height));
|
||||
}
|
||||
/**
|
||||
* Emulates consistent window screen size available inside web page via {@code window.screen}. Is only used when the {@code viewport}
|
||||
* is set.
|
||||
*/
|
||||
public NewPageOptions setScreenSize(ScreenSize screenSize) {
|
||||
this.screenSize = screenSize;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Populates context with given storage state. This option can be used to initialize context with logged-in information
|
||||
* obtained via {@link BrowserContext#storageState BrowserContext.storageState()}.
|
||||
*/
|
||||
public NewPageOptions setStorageState(String storageState) {
|
||||
this.storageState = storageState;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Populates context with given storage state. This option can be used to initialize context with logged-in information
|
||||
* obtained via {@link BrowserContext#storageState BrowserContext.storageState()}. Path to the file with saved storage
|
||||
* state.
|
||||
*/
|
||||
public NewPageOptions setStorageStatePath(Path storageStatePath) {
|
||||
this.storageStatePath = storageStatePath;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* It specified, enables strict selectors mode for this context. In the strict selectors mode all operations on selectors
|
||||
* that imply single target DOM element will throw when more than one element matches the selector. See {@code Locator} to learn
|
||||
* more about the strict mode.
|
||||
*/
|
||||
public NewPageOptions setStrictSelectors(boolean strictSelectors) {
|
||||
this.strictSelectors = strictSelectors;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Changes the timezone of the context. See <a
|
||||
* href="https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1">ICU's
|
||||
* metaZones.txt</a> for a list of supported timezone IDs.
|
||||
*/
|
||||
public NewPageOptions setTimezoneId(String timezoneId) {
|
||||
this.timezoneId = timezoneId;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Specific user agent to use in this context.
|
||||
*/
|
||||
public NewPageOptions setUserAgent(String userAgent) {
|
||||
this.userAgent = userAgent;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default viewport.
|
||||
*/
|
||||
public NewPageOptions setViewportSize(int width, int height) {
|
||||
return setViewportSize(new ViewportSize(width, height));
|
||||
}
|
||||
/**
|
||||
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default viewport.
|
||||
*/
|
||||
public NewPageOptions setViewportSize(ViewportSize viewportSize) {
|
||||
this.viewportSize = Optional.ofNullable(viewportSize);
|
||||
return this;
|
||||
@@ -561,14 +917,23 @@ public interface Browser extends AutoCloseable {
|
||||
*/
|
||||
public Boolean screenshots;
|
||||
|
||||
/**
|
||||
* specify custom categories to use instead of default.
|
||||
*/
|
||||
public StartTracingOptions setCategories(List<String> categories) {
|
||||
this.categories = categories;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A path to write the trace file to.
|
||||
*/
|
||||
public StartTracingOptions setPath(Path path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* captures screenshots in the trace.
|
||||
*/
|
||||
public StartTracingOptions setScreenshots(boolean screenshots) {
|
||||
this.screenshots = screenshots;
|
||||
return this;
|
||||
@@ -643,7 +1008,9 @@ public interface Browser extends AutoCloseable {
|
||||
*/
|
||||
Page newPage(NewPageOptions options);
|
||||
/**
|
||||
* <strong>NOTE:</strong> Tracing is only supported on Chromium-based browsers.
|
||||
* <strong>NOTE:</strong> This API controls <a href="https://www.chromium.org/developers/how-tos/trace-event-profiling-tool">Chromium Tracing</a>
|
||||
* which is a low-level chromium-specific debugging tool. API to control <a href="../trace-viewer">Playwright Tracing</a>
|
||||
* could be found <a href="https://playwright.dev/java/docs/class-tracing">here</a>.
|
||||
*
|
||||
* <p> You can use {@link Browser#startTracing Browser.startTracing()} and {@link Browser#stopTracing Browser.stopTracing()} to
|
||||
* create a trace file that can be opened in Chrome DevTools performance panel.
|
||||
@@ -660,7 +1027,9 @@ public interface Browser extends AutoCloseable {
|
||||
startTracing(page, null);
|
||||
}
|
||||
/**
|
||||
* <strong>NOTE:</strong> Tracing is only supported on Chromium-based browsers.
|
||||
* <strong>NOTE:</strong> This API controls <a href="https://www.chromium.org/developers/how-tos/trace-event-profiling-tool">Chromium Tracing</a>
|
||||
* which is a low-level chromium-specific debugging tool. API to control <a href="../trace-viewer">Playwright Tracing</a>
|
||||
* could be found <a href="https://playwright.dev/java/docs/class-tracing">here</a>.
|
||||
*
|
||||
* <p> You can use {@link Browser#startTracing Browser.startTracing()} and {@link Browser#stopTracing Browser.stopTracing()} to
|
||||
* create a trace file that can be opened in Chrome DevTools performance panel.
|
||||
@@ -675,7 +1044,9 @@ public interface Browser extends AutoCloseable {
|
||||
startTracing(null);
|
||||
}
|
||||
/**
|
||||
* <strong>NOTE:</strong> Tracing is only supported on Chromium-based browsers.
|
||||
* <strong>NOTE:</strong> This API controls <a href="https://www.chromium.org/developers/how-tos/trace-event-profiling-tool">Chromium Tracing</a>
|
||||
* which is a low-level chromium-specific debugging tool. API to control <a href="../trace-viewer">Playwright Tracing</a>
|
||||
* could be found <a href="https://playwright.dev/java/docs/class-tracing">here</a>.
|
||||
*
|
||||
* <p> You can use {@link Browser#startTracing Browser.startTracing()} and {@link Browser#stopTracing Browser.stopTracing()} to
|
||||
* create a trace file that can be opened in Chrome DevTools performance panel.
|
||||
@@ -690,7 +1061,9 @@ public interface Browser extends AutoCloseable {
|
||||
*/
|
||||
void startTracing(Page page, StartTracingOptions options);
|
||||
/**
|
||||
* <strong>NOTE:</strong> Tracing is only supported on Chromium-based browsers.
|
||||
* <strong>NOTE:</strong> This API controls <a href="https://www.chromium.org/developers/how-tos/trace-event-profiling-tool">Chromium Tracing</a>
|
||||
* which is a low-level chromium-specific debugging tool. API to control <a href="../trace-viewer">Playwright Tracing</a>
|
||||
* could be found <a href="https://playwright.dev/java/docs/class-tracing">here</a>.
|
||||
*
|
||||
* <p> Returns the buffer with trace data.
|
||||
*/
|
||||
|
||||
@@ -137,6 +137,10 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*/
|
||||
public Boolean handle;
|
||||
|
||||
/**
|
||||
* Whether to pass the argument as a handle, instead of passing by value. When passing a handle, only one argument is
|
||||
* supported. When passing by value, multiple arguments are supported.
|
||||
*/
|
||||
public ExposeBindingOptions setHandle(boolean handle) {
|
||||
this.handle = handle;
|
||||
return this;
|
||||
@@ -148,11 +152,28 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*/
|
||||
public String origin;
|
||||
|
||||
/**
|
||||
* The [origin] to grant permissions to, e.g. "https://example.com".
|
||||
*/
|
||||
public GrantPermissionsOptions setOrigin(String origin) {
|
||||
this.origin = origin;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class RouteOptions {
|
||||
/**
|
||||
* How often a route should be used. By default it will be used every time.
|
||||
*/
|
||||
public Integer times;
|
||||
|
||||
/**
|
||||
* How often a route should be used. By default it will be used every time.
|
||||
*/
|
||||
public RouteOptions setTimes(int times) {
|
||||
this.times = times;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class StorageStateOptions {
|
||||
/**
|
||||
* The file path to save the storage state to. If {@code path} is a relative path, then it is resolved relative to current
|
||||
@@ -160,6 +181,10 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*/
|
||||
public Path path;
|
||||
|
||||
/**
|
||||
* The file path to save the storage state to. If {@code path} is a relative path, then it is resolved relative to current
|
||||
* working directory. If no path is provided, storage state is still returned, but won't be saved to the disk.
|
||||
*/
|
||||
public StorageStateOptions setPath(Path path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
@@ -176,10 +201,17 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Receives the {@code Page} object and resolves to truthy value when the waiting should resolve.
|
||||
*/
|
||||
public WaitForPageOptions setPredicate(Predicate<Page> predicate) {
|
||||
this.predicate = predicate;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time to wait for in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
|
||||
* value can be changed by using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()}.
|
||||
*/
|
||||
public WaitForPageOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
@@ -519,6 +551,10 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* Routing provides the capability to modify network requests that are made by any page in the browser context. Once route
|
||||
* is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or aborted.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> {@link Page#route Page.route()} will not intercept requests intercepted by Service Worker. See <a
|
||||
* href="https://github.com/microsoft/playwright/issues/1090">this</a> issue. We recommend disabling Service Workers when
|
||||
* using request interception. Via {@code await context.addInitScript(() => delete window.navigator.serviceWorker);}
|
||||
*
|
||||
* <p> An example of a naive handler that aborts all image requests:
|
||||
* <pre>{@code
|
||||
* BrowserContext context = browser.newContext();
|
||||
@@ -555,14 +591,22 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing.
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the context
|
||||
* options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
*/
|
||||
void route(String url, Consumer<Route> handler);
|
||||
default void route(String url, Consumer<Route> handler) {
|
||||
route(url, handler, null);
|
||||
}
|
||||
/**
|
||||
* Routing provides the capability to modify network requests that are made by any page in the browser context. Once route
|
||||
* is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or aborted.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> {@link Page#route Page.route()} will not intercept requests intercepted by Service Worker. See <a
|
||||
* href="https://github.com/microsoft/playwright/issues/1090">this</a> issue. We recommend disabling Service Workers when
|
||||
* using request interception. Via {@code await context.addInitScript(() => delete window.navigator.serviceWorker);}
|
||||
*
|
||||
* <p> An example of a naive handler that aborts all image requests:
|
||||
* <pre>{@code
|
||||
* BrowserContext context = browser.newContext();
|
||||
@@ -599,14 +643,20 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing.
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the context
|
||||
* options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
*/
|
||||
void route(Pattern url, Consumer<Route> handler);
|
||||
void route(String url, Consumer<Route> handler, RouteOptions options);
|
||||
/**
|
||||
* Routing provides the capability to modify network requests that are made by any page in the browser context. Once route
|
||||
* is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or aborted.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> {@link Page#route Page.route()} will not intercept requests intercepted by Service Worker. See <a
|
||||
* href="https://github.com/microsoft/playwright/issues/1090">this</a> issue. We recommend disabling Service Workers when
|
||||
* using request interception. Via {@code await context.addInitScript(() => delete window.navigator.serviceWorker);}
|
||||
*
|
||||
* <p> An example of a naive handler that aborts all image requests:
|
||||
* <pre>{@code
|
||||
* BrowserContext context = browser.newContext();
|
||||
@@ -643,16 +693,172 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing.
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the context
|
||||
* options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
*/
|
||||
void route(Predicate<String> url, Consumer<Route> handler);
|
||||
default void route(Pattern url, Consumer<Route> handler) {
|
||||
route(url, handler, null);
|
||||
}
|
||||
/**
|
||||
* Routing provides the capability to modify network requests that are made by any page in the browser context. Once route
|
||||
* is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or aborted.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> {@link Page#route Page.route()} will not intercept requests intercepted by Service Worker. See <a
|
||||
* href="https://github.com/microsoft/playwright/issues/1090">this</a> issue. We recommend disabling Service Workers when
|
||||
* using request interception. Via {@code await context.addInitScript(() => delete window.navigator.serviceWorker);}
|
||||
*
|
||||
* <p> An example of a naive handler that aborts all image requests:
|
||||
* <pre>{@code
|
||||
* BrowserContext context = browser.newContext();
|
||||
* context.route("**\/*.{png,jpg,jpeg}", route -> route.abort());
|
||||
* Page page = context.newPage();
|
||||
* page.navigate("https://example.com");
|
||||
* browser.close();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> or the same snippet using a regex pattern instead:
|
||||
* <pre>{@code
|
||||
* BrowserContext context = browser.newContext();
|
||||
* context.route(Pattern.compile("(\\.png$)|(\\.jpg$)"), route -> route.abort());
|
||||
* Page page = context.newPage();
|
||||
* page.navigate("https://example.com");
|
||||
* browser.close();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> It is possible to examine the request to decide the route action. For example, mocking all requests that contain some
|
||||
* post data, and leaving all other requests as is:
|
||||
* <pre>{@code
|
||||
* context.route("/api/**", route -> {
|
||||
* if (route.request().postData().contains("my-string"))
|
||||
* route.fulfill(new Route.FulfillOptions().setBody("mocked-data"));
|
||||
* else
|
||||
* route.resume();
|
||||
* });
|
||||
* }</pre>
|
||||
*
|
||||
* <p> Page routes (set up with {@link Page#route Page.route()}) take precedence over browser context routes when request
|
||||
* matches both handlers.
|
||||
*
|
||||
* <p> To remove a route with its handler you can use {@link BrowserContext#unroute BrowserContext.unroute()}.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the context
|
||||
* options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
*/
|
||||
void route(Pattern url, Consumer<Route> handler, RouteOptions options);
|
||||
/**
|
||||
* Routing provides the capability to modify network requests that are made by any page in the browser context. Once route
|
||||
* is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or aborted.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> {@link Page#route Page.route()} will not intercept requests intercepted by Service Worker. See <a
|
||||
* href="https://github.com/microsoft/playwright/issues/1090">this</a> issue. We recommend disabling Service Workers when
|
||||
* using request interception. Via {@code await context.addInitScript(() => delete window.navigator.serviceWorker);}
|
||||
*
|
||||
* <p> An example of a naive handler that aborts all image requests:
|
||||
* <pre>{@code
|
||||
* BrowserContext context = browser.newContext();
|
||||
* context.route("**\/*.{png,jpg,jpeg}", route -> route.abort());
|
||||
* Page page = context.newPage();
|
||||
* page.navigate("https://example.com");
|
||||
* browser.close();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> or the same snippet using a regex pattern instead:
|
||||
* <pre>{@code
|
||||
* BrowserContext context = browser.newContext();
|
||||
* context.route(Pattern.compile("(\\.png$)|(\\.jpg$)"), route -> route.abort());
|
||||
* Page page = context.newPage();
|
||||
* page.navigate("https://example.com");
|
||||
* browser.close();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> It is possible to examine the request to decide the route action. For example, mocking all requests that contain some
|
||||
* post data, and leaving all other requests as is:
|
||||
* <pre>{@code
|
||||
* context.route("/api/**", route -> {
|
||||
* if (route.request().postData().contains("my-string"))
|
||||
* route.fulfill(new Route.FulfillOptions().setBody("mocked-data"));
|
||||
* else
|
||||
* route.resume();
|
||||
* });
|
||||
* }</pre>
|
||||
*
|
||||
* <p> Page routes (set up with {@link Page#route Page.route()}) take precedence over browser context routes when request
|
||||
* matches both handlers.
|
||||
*
|
||||
* <p> To remove a route with its handler you can use {@link BrowserContext#unroute BrowserContext.unroute()}.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the context
|
||||
* options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
*/
|
||||
default void route(Predicate<String> url, Consumer<Route> handler) {
|
||||
route(url, handler, null);
|
||||
}
|
||||
/**
|
||||
* Routing provides the capability to modify network requests that are made by any page in the browser context. Once route
|
||||
* is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or aborted.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> {@link Page#route Page.route()} will not intercept requests intercepted by Service Worker. See <a
|
||||
* href="https://github.com/microsoft/playwright/issues/1090">this</a> issue. We recommend disabling Service Workers when
|
||||
* using request interception. Via {@code await context.addInitScript(() => delete window.navigator.serviceWorker);}
|
||||
*
|
||||
* <p> An example of a naive handler that aborts all image requests:
|
||||
* <pre>{@code
|
||||
* BrowserContext context = browser.newContext();
|
||||
* context.route("**\/*.{png,jpg,jpeg}", route -> route.abort());
|
||||
* Page page = context.newPage();
|
||||
* page.navigate("https://example.com");
|
||||
* browser.close();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> or the same snippet using a regex pattern instead:
|
||||
* <pre>{@code
|
||||
* BrowserContext context = browser.newContext();
|
||||
* context.route(Pattern.compile("(\\.png$)|(\\.jpg$)"), route -> route.abort());
|
||||
* Page page = context.newPage();
|
||||
* page.navigate("https://example.com");
|
||||
* browser.close();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> It is possible to examine the request to decide the route action. For example, mocking all requests that contain some
|
||||
* post data, and leaving all other requests as is:
|
||||
* <pre>{@code
|
||||
* context.route("/api/**", route -> {
|
||||
* if (route.request().postData().contains("my-string"))
|
||||
* route.fulfill(new Route.FulfillOptions().setBody("mocked-data"));
|
||||
* else
|
||||
* route.resume();
|
||||
* });
|
||||
* }</pre>
|
||||
*
|
||||
* <p> Page routes (set up with {@link Page#route Page.route()}) take precedence over browser context routes when request
|
||||
* matches both handlers.
|
||||
*
|
||||
* <p> To remove a route with its handler you can use {@link BrowserContext#unroute BrowserContext.unroute()}.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the context
|
||||
* options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
*/
|
||||
void route(Predicate<String> url, Consumer<Route> handler, RouteOptions options);
|
||||
/**
|
||||
* This setting will change the default maximum navigation time for the following methods and related shortcuts:
|
||||
* <ul>
|
||||
* <li> {@link Page#goBack Page.goBack()}</li>
|
||||
* <li> {@link Page#goForward Page.goForward()}</li>
|
||||
* <li> {@link Page#goto Page.goto()}</li>
|
||||
* <li> {@link Page#navigate Page.navigate()}</li>
|
||||
* <li> {@link Page#reload Page.reload()}</li>
|
||||
* <li> {@link Page#setContent Page.setContent()}</li>
|
||||
* <li> {@link Page#waitForNavigation Page.waitForNavigation()}</li>
|
||||
|
||||
@@ -57,14 +57,25 @@ public interface BrowserType {
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Additional HTTP headers to be sent with web socket connect request. Optional.
|
||||
*/
|
||||
public ConnectOptions setHeaders(Map<String, String> headers) {
|
||||
this.headers = headers;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
|
||||
* Defaults to 0.
|
||||
*/
|
||||
public ConnectOptions setSlowMo(double slowMo) {
|
||||
this.slowMo = slowMo;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time in milliseconds to wait for the connection to be established. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to
|
||||
* disable timeout.
|
||||
*/
|
||||
public ConnectOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
@@ -86,14 +97,25 @@ public interface BrowserType {
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Additional HTTP headers to be sent with connect request. Optional.
|
||||
*/
|
||||
public ConnectOverCDPOptions setHeaders(Map<String, String> headers) {
|
||||
this.headers = headers;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
|
||||
* Defaults to 0.
|
||||
*/
|
||||
public ConnectOverCDPOptions setSlowMo(double slowMo) {
|
||||
this.slowMo = slowMo;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time in milliseconds to wait for the connection to be established. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to
|
||||
* disable timeout.
|
||||
*/
|
||||
public ConnectOverCDPOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
@@ -122,7 +144,8 @@ public interface BrowserType {
|
||||
public Boolean devtools;
|
||||
/**
|
||||
* If specified, accepted downloads are downloaded into this directory. Otherwise, temporary directory is created and is
|
||||
* deleted when browser is closed.
|
||||
* deleted when browser is closed. In either case, the downloads are deleted when the browser context they were created in
|
||||
* is closed.
|
||||
*/
|
||||
public Path downloadsPath;
|
||||
/**
|
||||
@@ -187,82 +210,159 @@ public interface BrowserType {
|
||||
*/
|
||||
public Path tracesDir;
|
||||
|
||||
/**
|
||||
* Additional arguments to pass to the browser instance. The list of Chromium flags can be found <a
|
||||
* href="http://peter.sh/experiments/chromium-command-line-switches/">here</a>.
|
||||
*/
|
||||
public LaunchOptions setArgs(List<String> args) {
|
||||
this.args = args;
|
||||
return this;
|
||||
}
|
||||
@Deprecated
|
||||
/**
|
||||
* Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge",
|
||||
* "msedge-beta", "msedge-dev", "msedge-canary". Read more about using <a
|
||||
* href="https://playwright.dev/java/docs/browsers/#google-chrome--microsoft-edge">Google Chrome and Microsoft Edge</a>.
|
||||
*/
|
||||
public LaunchOptions setChannel(BrowserChannel channel) {
|
||||
this.channel = channel;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge",
|
||||
* "msedge-beta", "msedge-dev", "msedge-canary". Read more about using <a
|
||||
* href="https://playwright.dev/java/docs/browsers/#google-chrome--microsoft-edge">Google Chrome and Microsoft Edge</a>.
|
||||
*/
|
||||
public LaunchOptions setChannel(String channel) {
|
||||
this.channel = channel;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Enable Chromium sandboxing. Defaults to {@code false}.
|
||||
*/
|
||||
public LaunchOptions setChromiumSandbox(boolean chromiumSandbox) {
|
||||
this.chromiumSandbox = chromiumSandbox;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is {@code true}, the {@code headless}
|
||||
* option will be set {@code false}.
|
||||
*/
|
||||
public LaunchOptions setDevtools(boolean devtools) {
|
||||
this.devtools = devtools;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* If specified, accepted downloads are downloaded into this directory. Otherwise, temporary directory is created and is
|
||||
* deleted when browser is closed. In either case, the downloads are deleted when the browser context they were created in
|
||||
* is closed.
|
||||
*/
|
||||
public LaunchOptions setDownloadsPath(Path downloadsPath) {
|
||||
this.downloadsPath = downloadsPath;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Specify environment variables that will be visible to the browser. Defaults to {@code process.env}.
|
||||
*/
|
||||
public LaunchOptions setEnv(Map<String, String> env) {
|
||||
this.env = env;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Path to a browser executable to run instead of the bundled one. If {@code executablePath} is a relative path, then it is
|
||||
* resolved relative to the current working directory. Note that Playwright only works with the bundled Chromium, Firefox
|
||||
* or WebKit, use at your own risk.
|
||||
*/
|
||||
public LaunchOptions setExecutablePath(Path executablePath) {
|
||||
this.executablePath = executablePath;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* 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>.
|
||||
*/
|
||||
public LaunchOptions setFirefoxUserPrefs(Map<String, Object> firefoxUserPrefs) {
|
||||
this.firefoxUserPrefs = firefoxUserPrefs;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Close the browser process on SIGHUP. Defaults to {@code true}.
|
||||
*/
|
||||
public LaunchOptions setHandleSIGHUP(boolean handleSIGHUP) {
|
||||
this.handleSIGHUP = handleSIGHUP;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Close the browser process on Ctrl-C. Defaults to {@code true}.
|
||||
*/
|
||||
public LaunchOptions setHandleSIGINT(boolean handleSIGINT) {
|
||||
this.handleSIGINT = handleSIGINT;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Close the browser process on SIGTERM. Defaults to {@code true}.
|
||||
*/
|
||||
public LaunchOptions setHandleSIGTERM(boolean handleSIGTERM) {
|
||||
this.handleSIGTERM = handleSIGTERM;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Whether to run browser in headless mode. More details for <a
|
||||
* href="https://developers.google.com/web/updates/2017/04/headless-chrome">Chromium</a> and <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode">Firefox</a>. Defaults to {@code true} unless the
|
||||
* {@code devtools} option is {@code true}.
|
||||
*/
|
||||
public LaunchOptions setHeadless(boolean headless) {
|
||||
this.headless = headless;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* If {@code true}, Playwright does not pass its own configurations args and only uses the ones from {@code args}. Dangerous option;
|
||||
* use with care. Defaults to {@code false}.
|
||||
*/
|
||||
public LaunchOptions setIgnoreAllDefaultArgs(boolean ignoreAllDefaultArgs) {
|
||||
this.ignoreAllDefaultArgs = ignoreAllDefaultArgs;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* If {@code true}, Playwright does not pass its own configurations args and only uses the ones from {@code args}. Dangerous option;
|
||||
* use with care.
|
||||
*/
|
||||
public LaunchOptions setIgnoreDefaultArgs(List<String> ignoreDefaultArgs) {
|
||||
this.ignoreDefaultArgs = ignoreDefaultArgs;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Network proxy settings.
|
||||
*/
|
||||
public LaunchOptions setProxy(String server) {
|
||||
return setProxy(new Proxy(server));
|
||||
}
|
||||
/**
|
||||
* Network proxy settings.
|
||||
*/
|
||||
public LaunchOptions setProxy(Proxy proxy) {
|
||||
this.proxy = proxy;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
|
||||
*/
|
||||
public LaunchOptions setSlowMo(double slowMo) {
|
||||
this.slowMo = slowMo;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time in milliseconds to wait for the browser instance to start. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to
|
||||
* disable timeout.
|
||||
*/
|
||||
public LaunchOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* If specified, traces are saved into this directory.
|
||||
*/
|
||||
public LaunchOptions setTracesDir(Path tracesDir) {
|
||||
this.tracesDir = tracesDir;
|
||||
return this;
|
||||
@@ -278,6 +378,18 @@ public interface BrowserType {
|
||||
* href="http://peter.sh/experiments/chromium-command-line-switches/">here</a>.
|
||||
*/
|
||||
public List<String> args;
|
||||
/**
|
||||
* When using {@link Page#navigate Page.navigate()}, {@link Page#route Page.route()}, {@link Page#waitForURL
|
||||
* Page.waitForURL()}, {@link Page#waitForRequest Page.waitForRequest()}, or {@link Page#waitForResponse
|
||||
* Page.waitForResponse()} it takes the base URL in consideration by using the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code URL()}</a> constructor for building the corresponding
|
||||
* URL. Examples:
|
||||
* <ul>
|
||||
* <li> baseURL: {@code http://localhost:3000} and navigating to {@code /bar.html} results in {@code http://localhost:3000/bar.html}</li>
|
||||
* <li> baseURL: {@code http://localhost:3000/foo/} and navigating to {@code ./bar.html} results in {@code http://localhost:3000/foo/bar.html}</li>
|
||||
* </ul>
|
||||
*/
|
||||
public String baseURL;
|
||||
/**
|
||||
* Toggles bypassing page's Content-Security-Policy.
|
||||
*/
|
||||
@@ -308,7 +420,8 @@ public interface BrowserType {
|
||||
public Boolean devtools;
|
||||
/**
|
||||
* If specified, accepted downloads are downloaded into this directory. Otherwise, temporary directory is created and is
|
||||
* deleted when browser is closed.
|
||||
* deleted when browser is closed. In either case, the downloads are deleted when the browser context they were created in
|
||||
* is closed.
|
||||
*/
|
||||
public Path downloadsPath;
|
||||
/**
|
||||
@@ -322,9 +435,17 @@ public interface BrowserType {
|
||||
*/
|
||||
public Path executablePath;
|
||||
/**
|
||||
* An object containing additional HTTP headers to be sent with every request. All header values must be strings.
|
||||
* An object containing additional HTTP headers to be sent with every request.
|
||||
*/
|
||||
public Map<String, String> extraHTTPHeaders;
|
||||
/**
|
||||
* Emulates {@code "forced-colors"} media feature, supported values are {@code "active"}, {@code "none"}. See {@link Page#emulateMedia
|
||||
* Page.emulateMedia()} for more details. Defaults to {@code "none"}.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> It's not supported in WebKit, see <a href="https://bugs.webkit.org/show_bug.cgi?id=225281">here</a> in their issue
|
||||
* tracker.
|
||||
*/
|
||||
public ForcedColors forcedColors;
|
||||
public Geolocation geolocation;
|
||||
/**
|
||||
* Close the browser process on SIGHUP. Defaults to {@code true}.
|
||||
@@ -364,7 +485,7 @@ public interface BrowserType {
|
||||
*/
|
||||
public List<String> ignoreDefaultArgs;
|
||||
/**
|
||||
* Whether to ignore HTTPS errors during navigation. Defaults to {@code false}.
|
||||
* Whether to ignore HTTPS errors when sending network requests. Defaults to {@code false}.
|
||||
*/
|
||||
public Boolean ignoreHTTPSErrors;
|
||||
/**
|
||||
@@ -429,6 +550,12 @@ public interface BrowserType {
|
||||
* Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
|
||||
*/
|
||||
public Double slowMo;
|
||||
/**
|
||||
* It specified, enables strict selectors mode for this context. In the strict selectors mode all operations on selectors
|
||||
* that imply single target DOM element will throw when more than one element matches the selector. See {@code Locator} to learn
|
||||
* more about the strict mode.
|
||||
*/
|
||||
public Boolean strictSelectors;
|
||||
/**
|
||||
* Maximum time in milliseconds to wait for the browser instance to start. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to
|
||||
* disable timeout.
|
||||
@@ -453,59 +580,135 @@ public interface BrowserType {
|
||||
*/
|
||||
public Optional<ViewportSize> viewportSize;
|
||||
|
||||
/**
|
||||
* Whether to automatically download all the attachments. Defaults to {@code false} where all the downloads are canceled.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setAcceptDownloads(boolean acceptDownloads) {
|
||||
this.acceptDownloads = acceptDownloads;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Additional arguments to pass to the browser instance. The list of Chromium flags can be found <a
|
||||
* href="http://peter.sh/experiments/chromium-command-line-switches/">here</a>.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setArgs(List<String> args) {
|
||||
this.args = args;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* When using {@link Page#navigate Page.navigate()}, {@link Page#route Page.route()}, {@link Page#waitForURL
|
||||
* Page.waitForURL()}, {@link Page#waitForRequest Page.waitForRequest()}, or {@link Page#waitForResponse
|
||||
* Page.waitForResponse()} it takes the base URL in consideration by using the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code URL()}</a> constructor for building the corresponding
|
||||
* URL. Examples:
|
||||
* <ul>
|
||||
* <li> baseURL: {@code http://localhost:3000} and navigating to {@code /bar.html} results in {@code http://localhost:3000/bar.html}</li>
|
||||
* <li> baseURL: {@code http://localhost:3000/foo/} and navigating to {@code ./bar.html} results in {@code http://localhost:3000/foo/bar.html}</li>
|
||||
* </ul>
|
||||
*/
|
||||
public LaunchPersistentContextOptions setBaseURL(String baseURL) {
|
||||
this.baseURL = baseURL;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Toggles bypassing page's Content-Security-Policy.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setBypassCSP(boolean bypassCSP) {
|
||||
this.bypassCSP = bypassCSP;
|
||||
return this;
|
||||
}
|
||||
@Deprecated
|
||||
/**
|
||||
* Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge",
|
||||
* "msedge-beta", "msedge-dev", "msedge-canary". Read more about using <a
|
||||
* href="https://playwright.dev/java/docs/browsers/#google-chrome--microsoft-edge">Google Chrome and Microsoft Edge</a>.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setChannel(BrowserChannel channel) {
|
||||
this.channel = channel;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge",
|
||||
* "msedge-beta", "msedge-dev", "msedge-canary". Read more about using <a
|
||||
* href="https://playwright.dev/java/docs/browsers/#google-chrome--microsoft-edge">Google Chrome and Microsoft Edge</a>.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setChannel(String channel) {
|
||||
this.channel = channel;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Enable Chromium sandboxing. Defaults to {@code false}.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setChromiumSandbox(boolean chromiumSandbox) {
|
||||
this.chromiumSandbox = chromiumSandbox;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code "no-preference"}. See
|
||||
* {@link Page#emulateMedia Page.emulateMedia()} for more details. Defaults to {@code "light"}.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setColorScheme(ColorScheme colorScheme) {
|
||||
this.colorScheme = colorScheme;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Specify device scale factor (can be thought of as dpr). Defaults to {@code 1}.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setDeviceScaleFactor(double deviceScaleFactor) {
|
||||
this.deviceScaleFactor = deviceScaleFactor;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is {@code true}, the {@code headless}
|
||||
* option will be set {@code false}.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setDevtools(boolean devtools) {
|
||||
this.devtools = devtools;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* If specified, accepted downloads are downloaded into this directory. Otherwise, temporary directory is created and is
|
||||
* deleted when browser is closed. In either case, the downloads are deleted when the browser context they were created in
|
||||
* is closed.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setDownloadsPath(Path downloadsPath) {
|
||||
this.downloadsPath = downloadsPath;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Specify environment variables that will be visible to the browser. Defaults to {@code process.env}.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setEnv(Map<String, String> env) {
|
||||
this.env = env;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Path to a browser executable to run instead of the bundled one. If {@code executablePath} is a relative path, then it is
|
||||
* resolved relative to the current working directory. Note that Playwright only works with the bundled Chromium, Firefox
|
||||
* or WebKit, use at your own risk.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setExecutablePath(Path executablePath) {
|
||||
this.executablePath = executablePath;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* An object containing additional HTTP headers to be sent with every request.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setExtraHTTPHeaders(Map<String, String> extraHTTPHeaders) {
|
||||
this.extraHTTPHeaders = extraHTTPHeaders;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates {@code "forced-colors"} media feature, supported values are {@code "active"}, {@code "none"}. See {@link Page#emulateMedia
|
||||
* Page.emulateMedia()} for more details. Defaults to {@code "none"}.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> It's not supported in WebKit, see <a href="https://bugs.webkit.org/show_bug.cgi?id=225281">here</a> in their issue
|
||||
* tracker.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setForcedColors(ForcedColors forcedColors) {
|
||||
this.forcedColors = forcedColors;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions setGeolocation(double latitude, double longitude) {
|
||||
return setGeolocation(new Geolocation(latitude, longitude));
|
||||
}
|
||||
@@ -513,125 +716,251 @@ public interface BrowserType {
|
||||
this.geolocation = geolocation;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Close the browser process on SIGHUP. Defaults to {@code true}.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setHandleSIGHUP(boolean handleSIGHUP) {
|
||||
this.handleSIGHUP = handleSIGHUP;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Close the browser process on Ctrl-C. Defaults to {@code true}.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setHandleSIGINT(boolean handleSIGINT) {
|
||||
this.handleSIGINT = handleSIGINT;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Close the browser process on SIGTERM. Defaults to {@code true}.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setHandleSIGTERM(boolean handleSIGTERM) {
|
||||
this.handleSIGTERM = handleSIGTERM;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Specifies if viewport supports touch events. Defaults to false.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setHasTouch(boolean hasTouch) {
|
||||
this.hasTouch = hasTouch;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Whether to run browser in headless mode. More details for <a
|
||||
* href="https://developers.google.com/web/updates/2017/04/headless-chrome">Chromium</a> and <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode">Firefox</a>. Defaults to {@code true} unless the
|
||||
* {@code devtools} option is {@code true}.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setHeadless(boolean headless) {
|
||||
this.headless = headless;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Credentials for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication">HTTP authentication</a>.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setHttpCredentials(String username, String password) {
|
||||
return setHttpCredentials(new HttpCredentials(username, password));
|
||||
}
|
||||
/**
|
||||
* Credentials for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication">HTTP authentication</a>.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setHttpCredentials(HttpCredentials httpCredentials) {
|
||||
this.httpCredentials = httpCredentials;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* If {@code true}, Playwright does not pass its own configurations args and only uses the ones from {@code args}. Dangerous option;
|
||||
* use with care. Defaults to {@code false}.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setIgnoreAllDefaultArgs(boolean ignoreAllDefaultArgs) {
|
||||
this.ignoreAllDefaultArgs = ignoreAllDefaultArgs;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* If {@code true}, Playwright does not pass its own configurations args and only uses the ones from {@code args}. Dangerous option;
|
||||
* use with care.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setIgnoreDefaultArgs(List<String> ignoreDefaultArgs) {
|
||||
this.ignoreDefaultArgs = ignoreDefaultArgs;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Whether to ignore HTTPS errors when sending network requests. Defaults to {@code false}.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setIgnoreHTTPSErrors(boolean ignoreHTTPSErrors) {
|
||||
this.ignoreHTTPSErrors = ignoreHTTPSErrors;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Whether the {@code meta viewport} tag is taken into account and touch events are enabled. Defaults to {@code false}. Not supported
|
||||
* in Firefox.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setIsMobile(boolean isMobile) {
|
||||
this.isMobile = isMobile;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Whether or not to enable JavaScript in the context. Defaults to {@code true}.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setJavaScriptEnabled(boolean javaScriptEnabled) {
|
||||
this.javaScriptEnabled = javaScriptEnabled;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Specify user locale, for example {@code en-GB}, {@code de-DE}, etc. Locale will affect {@code navigator.language} value, {@code Accept-Language}
|
||||
* request header value as well as number and date formatting rules.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setLocale(String locale) {
|
||||
this.locale = locale;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Whether to emulate network being offline. Defaults to {@code false}.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setOffline(boolean offline) {
|
||||
this.offline = offline;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A list of permissions to grant to all pages in this context. See {@link BrowserContext#grantPermissions
|
||||
* BrowserContext.grantPermissions()} for more details.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setPermissions(List<String> permissions) {
|
||||
this.permissions = permissions;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Network proxy settings.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setProxy(String server) {
|
||||
return setProxy(new Proxy(server));
|
||||
}
|
||||
/**
|
||||
* Network proxy settings.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setProxy(Proxy proxy) {
|
||||
this.proxy = proxy;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Optional setting to control whether to omit request content from the HAR. Defaults to {@code false}.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setRecordHarOmitContent(boolean recordHarOmitContent) {
|
||||
this.recordHarOmitContent = recordHarOmitContent;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Enables <a href="http://www.softwareishard.com/blog/har-12-spec">HAR</a> recording for all pages into the specified HAR
|
||||
* file on the filesystem. If not specified, the HAR is not recorded. Make sure to call {@link BrowserContext#close
|
||||
* BrowserContext.close()} for the HAR to be saved.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setRecordHarPath(Path recordHarPath) {
|
||||
this.recordHarPath = recordHarPath;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Enables video recording for all pages into the specified directory. If not specified videos are not recorded. Make sure
|
||||
* to call {@link BrowserContext#close BrowserContext.close()} for videos to be saved.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setRecordVideoDir(Path recordVideoDir) {
|
||||
this.recordVideoDir = recordVideoDir;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Dimensions of the recorded videos. If not specified the size will be equal to {@code viewport} scaled down to fit into
|
||||
* 800x800. If {@code viewport} is not configured explicitly the video size defaults to 800x450. Actual picture of each page will
|
||||
* be scaled down if necessary to fit the specified size.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setRecordVideoSize(int width, int height) {
|
||||
return setRecordVideoSize(new RecordVideoSize(width, height));
|
||||
}
|
||||
/**
|
||||
* Dimensions of the recorded videos. If not specified the size will be equal to {@code viewport} scaled down to fit into
|
||||
* 800x800. If {@code viewport} is not configured explicitly the video size defaults to 800x450. Actual picture of each page will
|
||||
* be scaled down if necessary to fit the specified size.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setRecordVideoSize(RecordVideoSize recordVideoSize) {
|
||||
this.recordVideoSize = recordVideoSize;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates {@code "prefers-reduced-motion"} media feature, supported values are {@code "reduce"}, {@code "no-preference"}. See {@link
|
||||
* Page#emulateMedia Page.emulateMedia()} for more details. Defaults to {@code "no-preference"}.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setReducedMotion(ReducedMotion reducedMotion) {
|
||||
this.reducedMotion = reducedMotion;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates consistent window screen size available inside web page via {@code window.screen}. Is only used when the {@code viewport}
|
||||
* is set.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setScreenSize(int width, int height) {
|
||||
return setScreenSize(new ScreenSize(width, height));
|
||||
}
|
||||
/**
|
||||
* Emulates consistent window screen size available inside web page via {@code window.screen}. Is only used when the {@code viewport}
|
||||
* is set.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setScreenSize(ScreenSize screenSize) {
|
||||
this.screenSize = screenSize;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setSlowMo(double slowMo) {
|
||||
this.slowMo = slowMo;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* It specified, enables strict selectors mode for this context. In the strict selectors mode all operations on selectors
|
||||
* that imply single target DOM element will throw when more than one element matches the selector. See {@code Locator} to learn
|
||||
* more about the strict mode.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setStrictSelectors(boolean strictSelectors) {
|
||||
this.strictSelectors = strictSelectors;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time in milliseconds to wait for the browser instance to start. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to
|
||||
* disable timeout.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Changes the timezone of the context. See <a
|
||||
* href="https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1">ICU's
|
||||
* metaZones.txt</a> for a list of supported timezone IDs.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setTimezoneId(String timezoneId) {
|
||||
this.timezoneId = timezoneId;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* If specified, traces are saved into this directory.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setTracesDir(Path tracesDir) {
|
||||
this.tracesDir = tracesDir;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Specific user agent to use in this context.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setUserAgent(String userAgent) {
|
||||
this.userAgent = userAgent;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default viewport.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setViewportSize(int width, int height) {
|
||||
return setViewportSize(new ViewportSize(width, height));
|
||||
}
|
||||
/**
|
||||
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default viewport.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setViewportSize(ViewportSize viewportSize) {
|
||||
this.viewportSize = Optional.ofNullable(viewportSize);
|
||||
return this;
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.microsoft.playwright.impl.Driver;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
@@ -28,7 +29,7 @@ import static java.util.Arrays.asList;
|
||||
*/
|
||||
public class CLI {
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
Path driver = Driver.ensureDriverInstalled();
|
||||
Path driver = Driver.ensureDriverInstalled(Collections.emptyMap());
|
||||
ProcessBuilder pb = new ProcessBuilder(driver.toString());
|
||||
pb.command().addAll(asList(args));
|
||||
if (!pb.environment().containsKey("PW_CLI_TARGET_LANG")) {
|
||||
|
||||
@@ -19,11 +19,11 @@ package com.microsoft.playwright;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* {@code ConsoleMessage} objects are dispatched by page via the {@link Page#onConsole Page.onConsole()} event.
|
||||
* {@code ConsoleMessage} objects are dispatched by page via the {@link Page#onConsoleMessage Page.onConsoleMessage()} event.
|
||||
*/
|
||||
public interface ConsoleMessage {
|
||||
/**
|
||||
* List of arguments passed to a {@code console} function call. See also {@link Page#onConsole Page.onConsole()}.
|
||||
* List of arguments passed to a {@code console} function call. See also {@link Page#onConsoleMessage Page.onConsoleMessage()}.
|
||||
*/
|
||||
List<JSHandle> args();
|
||||
/**
|
||||
|
||||
@@ -23,8 +23,7 @@ import java.util.*;
|
||||
/**
|
||||
* {@code Download} objects are dispatched by page via the {@link Page#onDownload Page.onDownload()} event.
|
||||
*
|
||||
* <p> If {@code downloadsPath} isn't specified, all the downloaded files belonging to the browser context are deleted when the
|
||||
* browser context is closed. And all downloaded files are deleted when the browser closes.
|
||||
* <p> All the downloaded files belonging to the browser context are deleted when the browser context is closed.
|
||||
*
|
||||
* <p> Download event is emitted once the download starts. Download path becomes available once download completes:
|
||||
* <pre>{@code
|
||||
@@ -47,6 +46,11 @@ import java.util.*;
|
||||
* has no access to the downloaded files.
|
||||
*/
|
||||
public interface Download {
|
||||
/**
|
||||
* Cancels a download. Will not fail if the download is already finished or canceled. Upon successful cancellations,
|
||||
* {@code download.failure()} would resolve to {@code "canceled"}.
|
||||
*/
|
||||
void cancel();
|
||||
/**
|
||||
* Returns readable stream for current download or {@code null} if download failed.
|
||||
*/
|
||||
@@ -66,6 +70,9 @@ public interface Download {
|
||||
/**
|
||||
* Returns path to the downloaded file in case of successful download. The method will wait for the download to finish if
|
||||
* necessary. The method throws when connected remotely.
|
||||
*
|
||||
* <p> Note that the download's file name is a random GUID, use {@link Download#suggestedFilename Download.suggestedFilename()}
|
||||
* to get suggested file name.
|
||||
*/
|
||||
Path path();
|
||||
/**
|
||||
|
||||
@@ -24,21 +24,8 @@ import java.util.*;
|
||||
* ElementHandle represents an in-page DOM element. ElementHandles can be created with the {@link Page#querySelector
|
||||
* Page.querySelector()} method.
|
||||
* <pre>{@code
|
||||
* import com.microsoft.playwright.*;
|
||||
*
|
||||
* public class Example {
|
||||
* public static void main(String[] args) {
|
||||
* try (Playwright playwright = Playwright.create()) {
|
||||
* BrowserType chromium = playwright.chromium();
|
||||
* Browser browser = chromium.launch();
|
||||
* Page page = browser.newPage();
|
||||
* page.navigate("https://example.com");
|
||||
* ElementHandle hrefElement = page.querySelector("a");
|
||||
* hrefElement.click();
|
||||
* // ...
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ElementHandle hrefElement = page.querySelector("a");
|
||||
* hrefElement.click();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> ElementHandle prevents DOM element from garbage collection unless the handle is disposed with {@link JSHandle#dispose
|
||||
@@ -46,6 +33,30 @@ import java.util.*;
|
||||
*
|
||||
* <p> ElementHandle instances can be used as an argument in {@link Page#evalOnSelector Page.evalOnSelector()} and {@link
|
||||
* Page#evaluate Page.evaluate()} methods.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> In most cases, you would want to use the {@code Locator} object instead. You should only use {@code ElementHandle} if you want to
|
||||
* retain a handle to a particular DOM Node that you intend to pass into {@link Page#evaluate Page.evaluate()} as an
|
||||
* argument.
|
||||
*
|
||||
* <p> The difference between the {@code Locator} and ElementHandle is that the ElementHandle points to a particular element, while
|
||||
* {@code Locator} captures the logic of how to retrieve an element.
|
||||
*
|
||||
* <p> In the example below, handle points to a particular DOM element on page. If that element changes text or is used by
|
||||
* React to render an entirely different component, handle is still pointing to that very DOM element. This can lead to
|
||||
* unexpected behaviors.
|
||||
* <pre>{@code
|
||||
* ElementHandle handle = page.querySelector("text=Submit");
|
||||
* handle.hover();
|
||||
* handle.click();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> With the locator, every time the {@code element} is used, up-to-date DOM element is located in the page using the selector. So
|
||||
* in the snippet below, underlying DOM element is going to be located twice.
|
||||
* <pre>{@code
|
||||
* Locator locator = page.locator("text=Submit");
|
||||
* locator.hover();
|
||||
* locator.click();
|
||||
* }</pre>
|
||||
*/
|
||||
public interface ElementHandle extends JSHandle {
|
||||
class CheckOptions {
|
||||
@@ -78,25 +89,52 @@ public interface ElementHandle extends JSHandle {
|
||||
*/
|
||||
public Boolean trial;
|
||||
|
||||
/**
|
||||
* Whether to bypass the <a href="https://playwright.dev/java/docs/actionability/">actionability</a> checks. Defaults to
|
||||
* {@code false}.
|
||||
*/
|
||||
public CheckOptions setForce(boolean force) {
|
||||
this.force = force;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
|
||||
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
|
||||
* inaccessible pages. Defaults to {@code false}.
|
||||
*/
|
||||
public CheckOptions setNoWaitAfter(boolean noWaitAfter) {
|
||||
this.noWaitAfter = noWaitAfter;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
||||
* element.
|
||||
*/
|
||||
public CheckOptions setPosition(double x, double y) {
|
||||
return setPosition(new Position(x, y));
|
||||
}
|
||||
/**
|
||||
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
||||
* element.
|
||||
*/
|
||||
public CheckOptions setPosition(Position position) {
|
||||
this.position = position;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public CheckOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* When set, this method only performs the <a href="https://playwright.dev/java/docs/actionability/">actionability</a>
|
||||
* checks and skips the action. Defaults to {@code false}. Useful to wait until the element is ready for the action without
|
||||
* performing it.
|
||||
*/
|
||||
public CheckOptions setTrial(boolean trial) {
|
||||
this.trial = trial;
|
||||
return this;
|
||||
@@ -149,41 +187,81 @@ public interface ElementHandle extends JSHandle {
|
||||
*/
|
||||
public Boolean trial;
|
||||
|
||||
/**
|
||||
* Defaults to {@code left}.
|
||||
*/
|
||||
public ClickOptions setButton(MouseButton button) {
|
||||
this.button = button;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* defaults to 1. See [UIEvent.detail].
|
||||
*/
|
||||
public ClickOptions setClickCount(int clickCount) {
|
||||
this.clickCount = clickCount;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Time to wait between {@code mousedown} and {@code mouseup} in milliseconds. Defaults to 0.
|
||||
*/
|
||||
public ClickOptions setDelay(double delay) {
|
||||
this.delay = delay;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Whether to bypass the <a href="https://playwright.dev/java/docs/actionability/">actionability</a> checks. Defaults to
|
||||
* {@code false}.
|
||||
*/
|
||||
public ClickOptions setForce(boolean force) {
|
||||
this.force = force;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores current
|
||||
* modifiers back. If not specified, currently pressed modifiers are used.
|
||||
*/
|
||||
public ClickOptions setModifiers(List<KeyboardModifier> modifiers) {
|
||||
this.modifiers = modifiers;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
|
||||
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
|
||||
* inaccessible pages. Defaults to {@code false}.
|
||||
*/
|
||||
public ClickOptions setNoWaitAfter(boolean noWaitAfter) {
|
||||
this.noWaitAfter = noWaitAfter;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
||||
* element.
|
||||
*/
|
||||
public ClickOptions setPosition(double x, double y) {
|
||||
return setPosition(new Position(x, y));
|
||||
}
|
||||
/**
|
||||
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
||||
* element.
|
||||
*/
|
||||
public ClickOptions setPosition(Position position) {
|
||||
this.position = position;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public ClickOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* When set, this method only performs the <a href="https://playwright.dev/java/docs/actionability/">actionability</a>
|
||||
* checks and skips the action. Defaults to {@code false}. Useful to wait until the element is ready for the action without
|
||||
* performing it.
|
||||
*/
|
||||
public ClickOptions setTrial(boolean trial) {
|
||||
this.trial = trial;
|
||||
return this;
|
||||
@@ -232,43 +310,85 @@ public interface ElementHandle extends JSHandle {
|
||||
*/
|
||||
public Boolean trial;
|
||||
|
||||
/**
|
||||
* Defaults to {@code left}.
|
||||
*/
|
||||
public DblclickOptions setButton(MouseButton button) {
|
||||
this.button = button;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Time to wait between {@code mousedown} and {@code mouseup} in milliseconds. Defaults to 0.
|
||||
*/
|
||||
public DblclickOptions setDelay(double delay) {
|
||||
this.delay = delay;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Whether to bypass the <a href="https://playwright.dev/java/docs/actionability/">actionability</a> checks. Defaults to
|
||||
* {@code false}.
|
||||
*/
|
||||
public DblclickOptions setForce(boolean force) {
|
||||
this.force = force;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores current
|
||||
* modifiers back. If not specified, currently pressed modifiers are used.
|
||||
*/
|
||||
public DblclickOptions setModifiers(List<KeyboardModifier> modifiers) {
|
||||
this.modifiers = modifiers;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
|
||||
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
|
||||
* inaccessible pages. Defaults to {@code false}.
|
||||
*/
|
||||
public DblclickOptions setNoWaitAfter(boolean noWaitAfter) {
|
||||
this.noWaitAfter = noWaitAfter;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
||||
* element.
|
||||
*/
|
||||
public DblclickOptions setPosition(double x, double y) {
|
||||
return setPosition(new Position(x, y));
|
||||
}
|
||||
/**
|
||||
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
||||
* element.
|
||||
*/
|
||||
public DblclickOptions setPosition(Position position) {
|
||||
this.position = position;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public DblclickOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* When set, this method only performs the <a href="https://playwright.dev/java/docs/actionability/">actionability</a>
|
||||
* checks and skips the action. Defaults to {@code false}. Useful to wait until the element is ready for the action without
|
||||
* performing it.
|
||||
*/
|
||||
public DblclickOptions setTrial(boolean trial) {
|
||||
this.trial = trial;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class FillOptions {
|
||||
/**
|
||||
* Whether to bypass the <a href="https://playwright.dev/java/docs/actionability/">actionability</a> checks. Defaults to
|
||||
* {@code false}.
|
||||
*/
|
||||
public Boolean force;
|
||||
/**
|
||||
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
|
||||
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
|
||||
@@ -282,10 +402,28 @@ public interface ElementHandle extends JSHandle {
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Whether to bypass the <a href="https://playwright.dev/java/docs/actionability/">actionability</a> checks. Defaults to
|
||||
* {@code false}.
|
||||
*/
|
||||
public FillOptions setForce(boolean force) {
|
||||
this.force = force;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
|
||||
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
|
||||
* inaccessible pages. Defaults to {@code false}.
|
||||
*/
|
||||
public FillOptions setNoWaitAfter(boolean noWaitAfter) {
|
||||
this.noWaitAfter = noWaitAfter;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public FillOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
@@ -320,30 +458,74 @@ public interface ElementHandle extends JSHandle {
|
||||
*/
|
||||
public Boolean trial;
|
||||
|
||||
/**
|
||||
* Whether to bypass the <a href="https://playwright.dev/java/docs/actionability/">actionability</a> checks. Defaults to
|
||||
* {@code false}.
|
||||
*/
|
||||
public HoverOptions setForce(boolean force) {
|
||||
this.force = force;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores current
|
||||
* modifiers back. If not specified, currently pressed modifiers are used.
|
||||
*/
|
||||
public HoverOptions setModifiers(List<KeyboardModifier> modifiers) {
|
||||
this.modifiers = modifiers;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
||||
* element.
|
||||
*/
|
||||
public HoverOptions setPosition(double x, double y) {
|
||||
return setPosition(new Position(x, y));
|
||||
}
|
||||
/**
|
||||
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
||||
* element.
|
||||
*/
|
||||
public HoverOptions setPosition(Position position) {
|
||||
this.position = position;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public HoverOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* When set, this method only performs the <a href="https://playwright.dev/java/docs/actionability/">actionability</a>
|
||||
* checks and skips the action. Defaults to {@code false}. Useful to wait until the element is ready for the action without
|
||||
* performing it.
|
||||
*/
|
||||
public HoverOptions setTrial(boolean trial) {
|
||||
this.trial = trial;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class InputValueOptions {
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public InputValueOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class PressOptions {
|
||||
/**
|
||||
* Time to wait between {@code keydown} and {@code keyup} in milliseconds. Defaults to 0.
|
||||
@@ -362,14 +544,27 @@ public interface ElementHandle extends JSHandle {
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Time to wait between {@code keydown} and {@code keyup} in milliseconds. Defaults to 0.
|
||||
*/
|
||||
public PressOptions setDelay(double delay) {
|
||||
this.delay = delay;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
|
||||
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
|
||||
* inaccessible pages. Defaults to {@code false}.
|
||||
*/
|
||||
public PressOptions setNoWaitAfter(boolean noWaitAfter) {
|
||||
this.noWaitAfter = noWaitAfter;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public PressOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
@@ -402,22 +597,42 @@ public interface ElementHandle extends JSHandle {
|
||||
*/
|
||||
public ScreenshotType type;
|
||||
|
||||
/**
|
||||
* Hides default white background and allows capturing screenshots with transparency. Not applicable to {@code jpeg} images.
|
||||
* Defaults to {@code false}.
|
||||
*/
|
||||
public ScreenshotOptions setOmitBackground(boolean omitBackground) {
|
||||
this.omitBackground = omitBackground;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* The file path to save the image to. The screenshot type will be inferred from file extension. If {@code path} is a relative
|
||||
* path, then it is resolved relative to the current working directory. If no path is provided, the image won't be saved to
|
||||
* the disk.
|
||||
*/
|
||||
public ScreenshotOptions setPath(Path path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* The quality of the image, between 0-100. Not applicable to {@code png} images.
|
||||
*/
|
||||
public ScreenshotOptions setQuality(int quality) {
|
||||
this.quality = quality;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public ScreenshotOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Specify screenshot type, defaults to {@code png}.
|
||||
*/
|
||||
public ScreenshotOptions setType(ScreenshotType type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
@@ -431,12 +646,22 @@ public interface ElementHandle extends JSHandle {
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public ScrollIntoViewIfNeededOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class SelectOptionOptions {
|
||||
/**
|
||||
* Whether to bypass the <a href="https://playwright.dev/java/docs/actionability/">actionability</a> checks. Defaults to
|
||||
* {@code false}.
|
||||
*/
|
||||
public Boolean force;
|
||||
/**
|
||||
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
|
||||
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
|
||||
@@ -450,16 +675,39 @@ public interface ElementHandle extends JSHandle {
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Whether to bypass the <a href="https://playwright.dev/java/docs/actionability/">actionability</a> checks. Defaults to
|
||||
* {@code false}.
|
||||
*/
|
||||
public SelectOptionOptions setForce(boolean force) {
|
||||
this.force = force;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
|
||||
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
|
||||
* inaccessible pages. Defaults to {@code false}.
|
||||
*/
|
||||
public SelectOptionOptions setNoWaitAfter(boolean noWaitAfter) {
|
||||
this.noWaitAfter = noWaitAfter;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public SelectOptionOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class SelectTextOptions {
|
||||
/**
|
||||
* Whether to bypass the <a href="https://playwright.dev/java/docs/actionability/">actionability</a> checks. Defaults to
|
||||
* {@code false}.
|
||||
*/
|
||||
public Boolean force;
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
@@ -467,11 +715,105 @@ public interface ElementHandle extends JSHandle {
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Whether to bypass the <a href="https://playwright.dev/java/docs/actionability/">actionability</a> checks. Defaults to
|
||||
* {@code false}.
|
||||
*/
|
||||
public SelectTextOptions setForce(boolean force) {
|
||||
this.force = force;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public SelectTextOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class SetCheckedOptions {
|
||||
/**
|
||||
* Whether to bypass the <a href="https://playwright.dev/java/docs/actionability/">actionability</a> checks. Defaults to
|
||||
* {@code false}.
|
||||
*/
|
||||
public Boolean force;
|
||||
/**
|
||||
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
|
||||
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
|
||||
* inaccessible pages. Defaults to {@code false}.
|
||||
*/
|
||||
public Boolean noWaitAfter;
|
||||
/**
|
||||
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
||||
* element.
|
||||
*/
|
||||
public Position position;
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public Double timeout;
|
||||
/**
|
||||
* When set, this method only performs the <a href="https://playwright.dev/java/docs/actionability/">actionability</a>
|
||||
* checks and skips the action. Defaults to {@code false}. Useful to wait until the element is ready for the action without
|
||||
* performing it.
|
||||
*/
|
||||
public Boolean trial;
|
||||
|
||||
/**
|
||||
* Whether to bypass the <a href="https://playwright.dev/java/docs/actionability/">actionability</a> checks. Defaults to
|
||||
* {@code false}.
|
||||
*/
|
||||
public SetCheckedOptions setForce(boolean force) {
|
||||
this.force = force;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
|
||||
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
|
||||
* inaccessible pages. Defaults to {@code false}.
|
||||
*/
|
||||
public SetCheckedOptions setNoWaitAfter(boolean noWaitAfter) {
|
||||
this.noWaitAfter = noWaitAfter;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
||||
* element.
|
||||
*/
|
||||
public SetCheckedOptions setPosition(double x, double y) {
|
||||
return setPosition(new Position(x, y));
|
||||
}
|
||||
/**
|
||||
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
||||
* element.
|
||||
*/
|
||||
public SetCheckedOptions setPosition(Position position) {
|
||||
this.position = position;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public SetCheckedOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* When set, this method only performs the <a href="https://playwright.dev/java/docs/actionability/">actionability</a>
|
||||
* checks and skips the action. Defaults to {@code false}. Useful to wait until the element is ready for the action without
|
||||
* performing it.
|
||||
*/
|
||||
public SetCheckedOptions setTrial(boolean trial) {
|
||||
this.trial = trial;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class SetInputFilesOptions {
|
||||
/**
|
||||
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
|
||||
@@ -486,10 +828,20 @@ public interface ElementHandle extends JSHandle {
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
|
||||
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
|
||||
* inaccessible pages. Defaults to {@code false}.
|
||||
*/
|
||||
public SetInputFilesOptions setNoWaitAfter(boolean noWaitAfter) {
|
||||
this.noWaitAfter = noWaitAfter;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public SetInputFilesOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
@@ -530,29 +882,60 @@ public interface ElementHandle extends JSHandle {
|
||||
*/
|
||||
public Boolean trial;
|
||||
|
||||
/**
|
||||
* Whether to bypass the <a href="https://playwright.dev/java/docs/actionability/">actionability</a> checks. Defaults to
|
||||
* {@code false}.
|
||||
*/
|
||||
public TapOptions setForce(boolean force) {
|
||||
this.force = force;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores current
|
||||
* modifiers back. If not specified, currently pressed modifiers are used.
|
||||
*/
|
||||
public TapOptions setModifiers(List<KeyboardModifier> modifiers) {
|
||||
this.modifiers = modifiers;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
|
||||
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
|
||||
* inaccessible pages. Defaults to {@code false}.
|
||||
*/
|
||||
public TapOptions setNoWaitAfter(boolean noWaitAfter) {
|
||||
this.noWaitAfter = noWaitAfter;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
||||
* element.
|
||||
*/
|
||||
public TapOptions setPosition(double x, double y) {
|
||||
return setPosition(new Position(x, y));
|
||||
}
|
||||
/**
|
||||
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
||||
* element.
|
||||
*/
|
||||
public TapOptions setPosition(Position position) {
|
||||
this.position = position;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public TapOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* When set, this method only performs the <a href="https://playwright.dev/java/docs/actionability/">actionability</a>
|
||||
* checks and skips the action. Defaults to {@code false}. Useful to wait until the element is ready for the action without
|
||||
* performing it.
|
||||
*/
|
||||
public TapOptions setTrial(boolean trial) {
|
||||
this.trial = trial;
|
||||
return this;
|
||||
@@ -576,14 +959,27 @@ public interface ElementHandle extends JSHandle {
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Time to wait between key presses in milliseconds. Defaults to 0.
|
||||
*/
|
||||
public TypeOptions setDelay(double delay) {
|
||||
this.delay = delay;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
|
||||
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
|
||||
* inaccessible pages. Defaults to {@code false}.
|
||||
*/
|
||||
public TypeOptions setNoWaitAfter(boolean noWaitAfter) {
|
||||
this.noWaitAfter = noWaitAfter;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public TypeOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
@@ -619,25 +1015,52 @@ public interface ElementHandle extends JSHandle {
|
||||
*/
|
||||
public Boolean trial;
|
||||
|
||||
/**
|
||||
* Whether to bypass the <a href="https://playwright.dev/java/docs/actionability/">actionability</a> checks. Defaults to
|
||||
* {@code false}.
|
||||
*/
|
||||
public UncheckOptions setForce(boolean force) {
|
||||
this.force = force;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
|
||||
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
|
||||
* inaccessible pages. Defaults to {@code false}.
|
||||
*/
|
||||
public UncheckOptions setNoWaitAfter(boolean noWaitAfter) {
|
||||
this.noWaitAfter = noWaitAfter;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
||||
* element.
|
||||
*/
|
||||
public UncheckOptions setPosition(double x, double y) {
|
||||
return setPosition(new Position(x, y));
|
||||
}
|
||||
/**
|
||||
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
||||
* element.
|
||||
*/
|
||||
public UncheckOptions setPosition(Position position) {
|
||||
this.position = position;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public UncheckOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* When set, this method only performs the <a href="https://playwright.dev/java/docs/actionability/">actionability</a>
|
||||
* checks and skips the action. Defaults to {@code false}. Useful to wait until the element is ready for the action without
|
||||
* performing it.
|
||||
*/
|
||||
public UncheckOptions setTrial(boolean trial) {
|
||||
this.trial = trial;
|
||||
return this;
|
||||
@@ -651,6 +1074,11 @@ public interface ElementHandle extends JSHandle {
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public WaitForElementStateOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
@@ -669,6 +1097,11 @@ public interface ElementHandle extends JSHandle {
|
||||
* </ul>
|
||||
*/
|
||||
public WaitForSelectorState state;
|
||||
/**
|
||||
* When true, the call requires selector to resolve to a single element. If given selector resolves to more then one
|
||||
* element, the call throws an exception.
|
||||
*/
|
||||
public Boolean strict;
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
@@ -676,10 +1109,34 @@ public interface ElementHandle extends JSHandle {
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Defaults to {@code "visible"}. Can be either:
|
||||
* <ul>
|
||||
* <li> {@code "attached"} - wait for element to be present in DOM.</li>
|
||||
* <li> {@code "detached"} - wait for element to not be present in DOM.</li>
|
||||
* <li> {@code "visible"} - wait for element to have non-empty bounding box and no {@code visibility:hidden}. Note that element without any
|
||||
* content or with {@code display:none} has an empty bounding box and is not considered visible.</li>
|
||||
* <li> {@code "hidden"} - wait for element to be either detached from DOM, or have an empty bounding box or {@code visibility:hidden}. This
|
||||
* is opposite to the {@code "visible"} option.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public WaitForSelectorOptions setState(WaitForSelectorState state) {
|
||||
this.state = state;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* When true, the call requires selector to resolve to a single element. If given selector resolves to more then one
|
||||
* element, the call throws an exception.
|
||||
*/
|
||||
public WaitForSelectorOptions setStrict(boolean strict) {
|
||||
this.strict = strict;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public WaitForSelectorOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
@@ -1079,6 +1536,16 @@ public interface ElementHandle extends JSHandle {
|
||||
* Returns the {@code element.innerText}.
|
||||
*/
|
||||
String innerText();
|
||||
/**
|
||||
* Returns {@code input.value} for {@code <input>} or {@code <textarea>} or {@code <select>} element. Throws for non-input elements.
|
||||
*/
|
||||
default String inputValue() {
|
||||
return inputValue(null);
|
||||
}
|
||||
/**
|
||||
* Returns {@code input.value} for {@code <input>} or {@code <textarea>} or {@code <select>} element. Throws for non-input elements.
|
||||
*/
|
||||
String inputValue(InputValueOptions options);
|
||||
/**
|
||||
* Returns whether the element is checked. Throws if the element is not a checkbox or radio input.
|
||||
*/
|
||||
@@ -1549,6 +2016,46 @@ public interface ElementHandle extends JSHandle {
|
||||
* the element and selects all its text content.
|
||||
*/
|
||||
void selectText(SelectTextOptions options);
|
||||
/**
|
||||
* This method checks or unchecks an element by performing the following steps:
|
||||
* <ol>
|
||||
* <li> Ensure that element is a checkbox or a radio input. If not, this method throws.</li>
|
||||
* <li> If the element already has the right checked state, this method returns immediately.</li>
|
||||
* <li> Wait for <a href="https://playwright.dev/java/docs/actionability/">actionability</a> checks on the matched element,
|
||||
* unless {@code force} option is set. If the element is detached during the checks, the whole action is retried.</li>
|
||||
* <li> Scroll the element into view if needed.</li>
|
||||
* <li> Use {@link Page#mouse Page.mouse()} to click in the center of the element.</li>
|
||||
* <li> Wait for initiated navigations to either succeed or fail, unless {@code noWaitAfter} option is set.</li>
|
||||
* <li> Ensure that the element is now checked or unchecked. If not, this method throws.</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p> When all steps combined have not finished during the specified {@code timeout}, this method throws a {@code TimeoutError}. Passing
|
||||
* zero timeout disables this.
|
||||
*
|
||||
* @param checked Whether to check or uncheck the checkbox.
|
||||
*/
|
||||
default void setChecked(boolean checked) {
|
||||
setChecked(checked, null);
|
||||
}
|
||||
/**
|
||||
* This method checks or unchecks an element by performing the following steps:
|
||||
* <ol>
|
||||
* <li> Ensure that element is a checkbox or a radio input. If not, this method throws.</li>
|
||||
* <li> If the element already has the right checked state, this method returns immediately.</li>
|
||||
* <li> Wait for <a href="https://playwright.dev/java/docs/actionability/">actionability</a> checks on the matched element,
|
||||
* unless {@code force} option is set. If the element is detached during the checks, the whole action is retried.</li>
|
||||
* <li> Scroll the element into view if needed.</li>
|
||||
* <li> Use {@link Page#mouse Page.mouse()} to click in the center of the element.</li>
|
||||
* <li> Wait for initiated navigations to either succeed or fail, unless {@code noWaitAfter} option is set.</li>
|
||||
* <li> Ensure that the element is now checked or unchecked. If not, this method throws.</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p> When all steps combined have not finished during the specified {@code timeout}, this method throws a {@code TimeoutError}. Passing
|
||||
* zero timeout disables this.
|
||||
*
|
||||
* @param checked Whether to check or uncheck the checkbox.
|
||||
*/
|
||||
void setChecked(boolean checked, SetCheckedOptions options);
|
||||
/**
|
||||
* This method expects {@code elementHandle} to point to an <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input">input element</a>.
|
||||
|
||||
@@ -42,10 +42,20 @@ public interface FileChooser {
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
|
||||
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
|
||||
* inaccessible pages. Defaults to {@code false}.
|
||||
*/
|
||||
public SetFilesOptions setNoWaitAfter(boolean noWaitAfter) {
|
||||
this.noWaitAfter = noWaitAfter;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public SetFilesOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -60,6 +60,9 @@ public interface Keyboard {
|
||||
*/
|
||||
public Double delay;
|
||||
|
||||
/**
|
||||
* Time to wait between {@code keydown} and {@code keyup} in milliseconds. Defaults to 0.
|
||||
*/
|
||||
public PressOptions setDelay(double delay) {
|
||||
this.delay = delay;
|
||||
return this;
|
||||
@@ -71,6 +74,9 @@ public interface Keyboard {
|
||||
*/
|
||||
public Double delay;
|
||||
|
||||
/**
|
||||
* Time to wait between key presses in milliseconds. Defaults to 0.
|
||||
*/
|
||||
public TypeOptions setDelay(double delay) {
|
||||
this.delay = delay;
|
||||
return this;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -49,14 +49,23 @@ public interface Mouse {
|
||||
*/
|
||||
public Double delay;
|
||||
|
||||
/**
|
||||
* Defaults to {@code left}.
|
||||
*/
|
||||
public ClickOptions setButton(MouseButton button) {
|
||||
this.button = button;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* defaults to 1. See [UIEvent.detail].
|
||||
*/
|
||||
public ClickOptions setClickCount(int clickCount) {
|
||||
this.clickCount = clickCount;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Time to wait between {@code mousedown} and {@code mouseup} in milliseconds. Defaults to 0.
|
||||
*/
|
||||
public ClickOptions setDelay(double delay) {
|
||||
this.delay = delay;
|
||||
return this;
|
||||
@@ -72,10 +81,16 @@ public interface Mouse {
|
||||
*/
|
||||
public Double delay;
|
||||
|
||||
/**
|
||||
* Defaults to {@code left}.
|
||||
*/
|
||||
public DblclickOptions setButton(MouseButton button) {
|
||||
this.button = button;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Time to wait between {@code mousedown} and {@code mouseup} in milliseconds. Defaults to 0.
|
||||
*/
|
||||
public DblclickOptions setDelay(double delay) {
|
||||
this.delay = delay;
|
||||
return this;
|
||||
@@ -91,10 +106,16 @@ public interface Mouse {
|
||||
*/
|
||||
public Integer clickCount;
|
||||
|
||||
/**
|
||||
* Defaults to {@code left}.
|
||||
*/
|
||||
public DownOptions setButton(MouseButton button) {
|
||||
this.button = button;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* defaults to 1. See [UIEvent.detail].
|
||||
*/
|
||||
public DownOptions setClickCount(int clickCount) {
|
||||
this.clickCount = clickCount;
|
||||
return this;
|
||||
@@ -106,6 +127,9 @@ public interface Mouse {
|
||||
*/
|
||||
public Integer steps;
|
||||
|
||||
/**
|
||||
* defaults to 1. Sends intermediate {@code mousemove} events.
|
||||
*/
|
||||
public MoveOptions setSteps(int steps) {
|
||||
this.steps = steps;
|
||||
return this;
|
||||
@@ -121,10 +145,16 @@ public interface Mouse {
|
||||
*/
|
||||
public Integer clickCount;
|
||||
|
||||
/**
|
||||
* Defaults to {@code left}.
|
||||
*/
|
||||
public UpOptions setButton(MouseButton button) {
|
||||
this.button = button;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* defaults to 1. See [UIEvent.detail].
|
||||
*/
|
||||
public UpOptions setClickCount(int clickCount) {
|
||||
this.clickCount = clickCount;
|
||||
return this;
|
||||
@@ -182,5 +212,15 @@ public interface Mouse {
|
||||
* Dispatches a {@code mouseup} event.
|
||||
*/
|
||||
void up(UpOptions options);
|
||||
/**
|
||||
* Dispatches a {@code wheel} event.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Wheel events may cause scrolling if they are not handled, and this method does not wait for the scrolling to finish
|
||||
* before returning.
|
||||
*
|
||||
* @param deltaX Pixels to scroll horizontally.
|
||||
* @param deltaY Pixels to scroll vertically.
|
||||
*/
|
||||
void wheel(double deltaX, double deltaY);
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -40,6 +40,22 @@ import java.util.*;
|
||||
* }</pre>
|
||||
*/
|
||||
public interface Playwright extends AutoCloseable {
|
||||
class CreateOptions {
|
||||
/**
|
||||
* Additional environment variables that will be passed to the driver process. By default driver process inherits
|
||||
* environment variables of the Playwright process.
|
||||
*/
|
||||
public Map<String, String> env;
|
||||
|
||||
/**
|
||||
* Additional environment variables that will be passed to the driver process. By default driver process inherits
|
||||
* environment variables of the Playwright process.
|
||||
*/
|
||||
public CreateOptions setEnv(Map<String, String> env) {
|
||||
this.env = env;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This object can be used to launch or connect to Chromium, returning instances of {@code Browser}.
|
||||
*/
|
||||
@@ -72,8 +88,12 @@ public interface Playwright extends AutoCloseable {
|
||||
* playwright.close();
|
||||
* }</pre>
|
||||
*/
|
||||
static Playwright create(CreateOptions options) {
|
||||
return PlaywrightImpl.create(options);
|
||||
}
|
||||
|
||||
static Playwright create() {
|
||||
return PlaywrightImpl.create();
|
||||
return create(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,10 @@ import java.util.*;
|
||||
* request is issued to a redirected url.
|
||||
*/
|
||||
public interface Request {
|
||||
/**
|
||||
* An object with all the request HTTP headers associated with this request. The header names are lower-cased.
|
||||
*/
|
||||
Map<String, String> allHeaders();
|
||||
/**
|
||||
* The method returns {@code null} unless this request has failed, as reported by {@code requestfailed} event.
|
||||
*
|
||||
@@ -54,9 +58,22 @@ public interface Request {
|
||||
*/
|
||||
Frame frame();
|
||||
/**
|
||||
* An object with HTTP headers associated with the request. All header names are lower-case.
|
||||
* **DEPRECATED** Incomplete list of headers as seen by the rendering engine. Use {@link Request#allHeaders
|
||||
* Request.allHeaders()} instead.
|
||||
*/
|
||||
Map<String, String> headers();
|
||||
/**
|
||||
* An array with all the request HTTP headers associated with this request. Unlike {@link Request#allHeaders
|
||||
* Request.allHeaders()}, header names are NOT lower-cased. Headers with multiple entries, such as {@code Set-Cookie}, appear in
|
||||
* the array multiple times.
|
||||
*/
|
||||
List<HttpHeader> headersArray();
|
||||
/**
|
||||
* Returns the value of the header matching the name. The name is case insensitive.
|
||||
*
|
||||
* @param name Name of the header.
|
||||
*/
|
||||
String headerValue(String name);
|
||||
/**
|
||||
* Whether this request is driving frame's navigation.
|
||||
*/
|
||||
@@ -112,6 +129,10 @@ public interface Request {
|
||||
* Returns the matching {@code Response} object, or {@code null} if the response was not received due to error.
|
||||
*/
|
||||
Response response();
|
||||
/**
|
||||
* Returns resource size information for given request.
|
||||
*/
|
||||
Sizes sizes();
|
||||
/**
|
||||
* Returns resource timing information for given request. Most of the timing values become available upon the response,
|
||||
* {@code responseEnd} becomes available when request finishes. Find more information at <a
|
||||
|
||||
@@ -16,18 +16,23 @@
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import com.microsoft.playwright.options.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* {@code Response} class represents responses which are received by page.
|
||||
*/
|
||||
public interface Response {
|
||||
/**
|
||||
* An object with all the response HTTP headers associated with this response.
|
||||
*/
|
||||
Map<String, String> allHeaders();
|
||||
/**
|
||||
* Returns the buffer with response body.
|
||||
*/
|
||||
byte[] body();
|
||||
/**
|
||||
* Waits for this response to finish, returns failure error if request failed.
|
||||
* Waits for this response to finish, returns always {@code null}.
|
||||
*/
|
||||
String finished();
|
||||
/**
|
||||
@@ -35,9 +40,30 @@ public interface Response {
|
||||
*/
|
||||
Frame frame();
|
||||
/**
|
||||
* Returns the object with HTTP headers associated with the response. All header names are lower-case.
|
||||
* **DEPRECATED** Incomplete list of headers as seen by the rendering engine. Use {@link Response#allHeaders
|
||||
* Response.allHeaders()} instead.
|
||||
*/
|
||||
Map<String, String> headers();
|
||||
/**
|
||||
* An array with all the request HTTP headers associated with this response. Unlike {@link Response#allHeaders
|
||||
* Response.allHeaders()}, header names are NOT lower-cased. Headers with multiple entries, such as {@code Set-Cookie}, appear in
|
||||
* the array multiple times.
|
||||
*/
|
||||
List<HttpHeader> headersArray();
|
||||
/**
|
||||
* Returns the value of the header matching the name. The name is case insensitive. If multiple headers have the same name
|
||||
* (except {@code set-cookie}), they are returned as a list separated by {@code , }. For {@code set-cookie}, the {@code \n} separator is used. If
|
||||
* no headers are found, {@code null} is returned.
|
||||
*
|
||||
* @param name Name of the header.
|
||||
*/
|
||||
String headerValue(String name);
|
||||
/**
|
||||
* Returns all values of the headers matching the name, for example {@code set-cookie}. The name is case insensitive.
|
||||
*
|
||||
* @param name Name of the header.
|
||||
*/
|
||||
List<String> headerValues(String name);
|
||||
/**
|
||||
* Contains a boolean stating whether the response was successful (status in the range 200-299) or not.
|
||||
*/
|
||||
@@ -46,6 +72,14 @@ public interface Response {
|
||||
* Returns the matching {@code Request} object.
|
||||
*/
|
||||
Request request();
|
||||
/**
|
||||
* Returns SSL and other security information.
|
||||
*/
|
||||
SecurityDetails securityDetails();
|
||||
/**
|
||||
* Returns the IP address and port of the server.
|
||||
*/
|
||||
ServerAddr serverAddr();
|
||||
/**
|
||||
* Contains the status code of the response (e.g., 200 for a success).
|
||||
*/
|
||||
|
||||
@@ -43,22 +43,37 @@ public interface Route {
|
||||
*/
|
||||
public String url;
|
||||
|
||||
/**
|
||||
* If set changes the request HTTP headers. Header values will be converted to a string.
|
||||
*/
|
||||
public ResumeOptions setHeaders(Map<String, String> headers) {
|
||||
this.headers = headers;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* If set changes the request method (e.g. GET or POST)
|
||||
*/
|
||||
public ResumeOptions setMethod(String method) {
|
||||
this.method = method;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* If set changes the post data of request
|
||||
*/
|
||||
public ResumeOptions setPostData(String postData) {
|
||||
this.postData = postData;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* If set changes the post data of request
|
||||
*/
|
||||
public ResumeOptions setPostData(byte[] postData) {
|
||||
this.postData = postData;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* If set changes the request URL. New URL must have same protocol as original one.
|
||||
*/
|
||||
public ResumeOptions setUrl(String url) {
|
||||
this.url = url;
|
||||
return this;
|
||||
@@ -91,26 +106,45 @@ public interface Route {
|
||||
*/
|
||||
public Integer status;
|
||||
|
||||
/**
|
||||
* Optional response body as text.
|
||||
*/
|
||||
public FulfillOptions setBody(String body) {
|
||||
this.body = body;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Optional response body as raw bytes.
|
||||
*/
|
||||
public FulfillOptions setBodyBytes(byte[] bodyBytes) {
|
||||
this.bodyBytes = bodyBytes;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* If set, equals to setting {@code Content-Type} response header.
|
||||
*/
|
||||
public FulfillOptions setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Response headers. Header values will be converted to a string.
|
||||
*/
|
||||
public FulfillOptions setHeaders(Map<String, String> headers) {
|
||||
this.headers = headers;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* File path to respond with. The content type will be inferred from file extension. If {@code path} is a relative path, then it
|
||||
* is resolved relative to the current working directory.
|
||||
*/
|
||||
public FulfillOptions setPath(Path path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Response status code, defaults to {@code 200}.
|
||||
*/
|
||||
public FulfillOptions setStatus(int status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
@@ -190,7 +224,7 @@ public interface Route {
|
||||
* <p> An example of serving static file:
|
||||
* <pre>{@code
|
||||
* page.route("**\/xhr_endpoint", route -> route.fulfill(
|
||||
* new Route.FulfillOptions().setPath(Paths.get("mock_data.json")));
|
||||
* new Route.FulfillOptions().setPath(Paths.get("mock_data.json"))));
|
||||
* }</pre>
|
||||
*/
|
||||
default void fulfill() {
|
||||
@@ -212,7 +246,7 @@ public interface Route {
|
||||
* <p> An example of serving static file:
|
||||
* <pre>{@code
|
||||
* page.route("**\/xhr_endpoint", route -> route.fulfill(
|
||||
* new Route.FulfillOptions().setPath(Paths.get("mock_data.json")));
|
||||
* new Route.FulfillOptions().setPath(Paths.get("mock_data.json"))));
|
||||
* }</pre>
|
||||
*/
|
||||
void fulfill(FulfillOptions options);
|
||||
|
||||
@@ -32,6 +32,11 @@ public interface Selectors {
|
||||
*/
|
||||
public Boolean contentScript;
|
||||
|
||||
/**
|
||||
* Whether to run this selector engine in isolated JavaScript environment. This environment has access to the same DOM, but
|
||||
* not any JavaScript objects from the frame's scripts. Defaults to {@code false}. Note that running as a content script is not
|
||||
* guaranteed when this engine is used together with other registered engines.
|
||||
*/
|
||||
public RegisterOptions setContentScript(boolean contentScript) {
|
||||
this.contentScript = contentScript;
|
||||
return this;
|
||||
|
||||
@@ -20,19 +20,19 @@ import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* API for collecting and saving Playwright traces. Playwright traces can be opened using the Playwright CLI after
|
||||
* Playwright script runs.
|
||||
* 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> Start with specifying the folder traces will be stored in:
|
||||
* <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();
|
||||
* BrowserContext context = browser.newContext();
|
||||
* context.tracing.start(new Tracing.StartOptions()
|
||||
* context.tracing().start(new Tracing.StartOptions()
|
||||
* .setScreenshots(true)
|
||||
* .setSnapshots(true);
|
||||
* .setSnapshots(true));
|
||||
* Page page = context.newPage();
|
||||
* page.goto("https://playwright.dev");
|
||||
* context.tracing.stop(new Tracing.StopOptions()
|
||||
* page.navigate("https://playwright.dev");
|
||||
* context.tracing().stop(new Tracing.StopOptions()
|
||||
* .setPath(Paths.get("trace.zip")));
|
||||
* }</pre>
|
||||
*/
|
||||
@@ -52,14 +52,24 @@ public interface Tracing {
|
||||
*/
|
||||
public Boolean snapshots;
|
||||
|
||||
/**
|
||||
* If specified, the trace is going to be saved into the file with the given name inside the {@code tracesDir} folder specified
|
||||
* in {@link BrowserType#launch BrowserType.launch()}.
|
||||
*/
|
||||
public StartOptions setName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Whether to capture screenshots during tracing. Screenshots are used to build a timeline preview.
|
||||
*/
|
||||
public StartOptions setScreenshots(boolean screenshots) {
|
||||
this.screenshots = screenshots;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Whether to capture DOM snapshot on every action.
|
||||
*/
|
||||
public StartOptions setSnapshots(boolean snapshots) {
|
||||
this.snapshots = snapshots;
|
||||
return this;
|
||||
@@ -67,24 +77,43 @@ public interface Tracing {
|
||||
}
|
||||
class StopOptions {
|
||||
/**
|
||||
* Export trace into the file with the given name.
|
||||
* Export trace into the file with the given path.
|
||||
*/
|
||||
public Path path;
|
||||
|
||||
/**
|
||||
* Export trace into the file with the given path.
|
||||
*/
|
||||
public StopOptions setPath(Path path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class StopChunkOptions {
|
||||
/**
|
||||
* Export trace collected since the last {@link Tracing#startChunk Tracing.startChunk()} call into the file with the given
|
||||
* path.
|
||||
*/
|
||||
public Path path;
|
||||
|
||||
/**
|
||||
* Export trace collected since the last {@link Tracing#startChunk Tracing.startChunk()} call into the file with the given
|
||||
* path.
|
||||
*/
|
||||
public StopChunkOptions setPath(Path path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Start tracing.
|
||||
* <pre>{@code
|
||||
* context.tracing.start(new Tracing.StartOptions()
|
||||
* context.tracing().start(new Tracing.StartOptions()
|
||||
* .setScreenshots(true)
|
||||
* .setSnapshots(true);
|
||||
* .setSnapshots(true));
|
||||
* Page page = context.newPage();
|
||||
* page.goto('https://playwright.dev');
|
||||
* context.tracing.stop(new Tracing.StopOptions()
|
||||
* page.navigate("https://playwright.dev");
|
||||
* context.tracing().stop(new Tracing.StopOptions()
|
||||
* .setPath(Paths.get("trace.zip")));
|
||||
* }</pre>
|
||||
*/
|
||||
@@ -94,16 +123,41 @@ public interface Tracing {
|
||||
/**
|
||||
* Start tracing.
|
||||
* <pre>{@code
|
||||
* context.tracing.start(new Tracing.StartOptions()
|
||||
* context.tracing().start(new Tracing.StartOptions()
|
||||
* .setScreenshots(true)
|
||||
* .setSnapshots(true);
|
||||
* .setSnapshots(true));
|
||||
* Page page = context.newPage();
|
||||
* page.goto('https://playwright.dev');
|
||||
* context.tracing.stop(new Tracing.StopOptions()
|
||||
* page.navigate("https://playwright.dev");
|
||||
* context.tracing().stop(new Tracing.StopOptions()
|
||||
* .setPath(Paths.get("trace.zip")));
|
||||
* }</pre>
|
||||
*/
|
||||
void start(StartOptions options);
|
||||
/**
|
||||
* Start a new trace chunk. If you'd like to record multiple traces on the same {@code BrowserContext}, use {@link Tracing#start
|
||||
* Tracing.start()} once, and then create multiple trace chunks with {@link Tracing#startChunk Tracing.startChunk()} and
|
||||
* {@link Tracing#stopChunk Tracing.stopChunk()}.
|
||||
* <pre>{@code
|
||||
* context.tracing().start(new Tracing.StartOptions()
|
||||
* .setScreenshots(true)
|
||||
* .setSnapshots(true));
|
||||
* Page page = context.newPage();
|
||||
* page.navigate("https://playwright.dev");
|
||||
*
|
||||
* context.tracing().startChunk();
|
||||
* page.click("text=Get Started");
|
||||
* // Everything between startChunk and stopChunk will be recorded in the trace.
|
||||
* context.tracing().stopChunk(new Tracing.StopChunkOptions()
|
||||
* .setPath(Paths.get("trace1.zip")));
|
||||
*
|
||||
* context.tracing().startChunk();
|
||||
* page.navigate("http://example.com");
|
||||
* // Save a second trace file with different actions.
|
||||
* context.tracing().stopChunk(new Tracing.StopChunkOptions()
|
||||
* .setPath(Paths.get("trace2.zip")));
|
||||
* }</pre>
|
||||
*/
|
||||
void startChunk();
|
||||
/**
|
||||
* Stop tracing.
|
||||
*/
|
||||
@@ -114,5 +168,15 @@ public interface Tracing {
|
||||
* Stop tracing.
|
||||
*/
|
||||
void stop(StopOptions options);
|
||||
/**
|
||||
* Stop the trace chunk. See {@link Tracing#startChunk Tracing.startChunk()} for more details about multiple trace chunks.
|
||||
*/
|
||||
default void stopChunk() {
|
||||
stopChunk(null);
|
||||
}
|
||||
/**
|
||||
* Stop the trace chunk. See {@link Tracing#startChunk Tracing.startChunk()} for more details about multiple trace chunks.
|
||||
*/
|
||||
void stopChunk(StopChunkOptions options);
|
||||
}
|
||||
|
||||
|
||||
@@ -72,10 +72,17 @@ public interface WebSocket {
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Receives the {@code WebSocketFrame} object and resolves to truthy value when the waiting should resolve.
|
||||
*/
|
||||
public WaitForFrameReceivedOptions setPredicate(Predicate<WebSocketFrame> predicate) {
|
||||
this.predicate = predicate;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time to wait for in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
|
||||
* value can be changed by using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()}.
|
||||
*/
|
||||
public WaitForFrameReceivedOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
@@ -92,10 +99,17 @@ public interface WebSocket {
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Receives the {@code WebSocketFrame} object and resolves to truthy value when the waiting should resolve.
|
||||
*/
|
||||
public WaitForFrameSentOptions setPredicate(Predicate<WebSocketFrame> predicate) {
|
||||
this.predicate = predicate;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time to wait for in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
|
||||
* value can be changed by using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()}.
|
||||
*/
|
||||
public WaitForFrameSentOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
|
||||
@@ -52,6 +52,10 @@ public interface Worker {
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Maximum time to wait for in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
|
||||
* value can be changed by using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()}.
|
||||
*/
|
||||
public WaitForCloseOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
|
||||
@@ -40,6 +40,10 @@ class ArtifactImpl extends ChannelOwner {
|
||||
return stream.stream();
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
sendMessage("cancel");
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
sendMessage("delete");
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ import com.microsoft.playwright.options.FunctionCallback;
|
||||
import com.microsoft.playwright.options.Geolocation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
@@ -53,6 +55,8 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
private final ListenerCollection<EventType> listeners = new ListenerCollection<>();
|
||||
final TimeoutSettings timeoutSettings = new TimeoutSettings();
|
||||
Path videosDir;
|
||||
URL baseUrl;
|
||||
Path recordHarPath;
|
||||
|
||||
enum EventType {
|
||||
CLOSE,
|
||||
@@ -73,6 +77,14 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
this.tracing = new TracingImpl(this);
|
||||
}
|
||||
|
||||
void setBaseUrl(String spec) {
|
||||
try {
|
||||
this.baseUrl = new URL(spec);
|
||||
} catch (MalformedURLException e) {
|
||||
this.baseUrl = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(Consumer<BrowserContext> handler) {
|
||||
listeners.add(EventType.CLOSE, handler);
|
||||
@@ -133,9 +145,9 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
listeners.remove(EventType.RESPONSE, handler);
|
||||
}
|
||||
|
||||
private <T> T waitForEventWithTimeout(EventType eventType, Runnable code, Double timeout) {
|
||||
private <T> T waitForEventWithTimeout(EventType eventType, Runnable code, Predicate<T> predicate, Double timeout) {
|
||||
List<Waitable<T>> waitables = new ArrayList<>();
|
||||
waitables.add(new WaitableEvent<>(listeners, eventType));
|
||||
waitables.add(new WaitableEvent<>(listeners, eventType, predicate));
|
||||
waitables.add(new WaitableContextClose<>());
|
||||
waitables.add(timeoutSettings.createWaitable(timeout));
|
||||
return runUntil(code, new WaitableRace<>(waitables));
|
||||
@@ -150,7 +162,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
if (options == null) {
|
||||
options = new WaitForPageOptions();
|
||||
}
|
||||
return waitForEventWithTimeout(EventType.PAGE, code, options.timeout);
|
||||
return waitForEventWithTimeout(EventType.PAGE, code, options.predicate, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -169,6 +181,18 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
}
|
||||
isClosedOrClosing = true;
|
||||
try {
|
||||
if (recordHarPath != null) {
|
||||
JsonObject json = sendMessage("harExport").getAsJsonObject();
|
||||
ArtifactImpl artifact = connection.getExistingObject(json.getAsJsonObject("artifact").get("guid").getAsString());
|
||||
// In case of CDP connection browser is null but since the connection is established by
|
||||
// the driver it is safe to consider the artifact local.
|
||||
if (browser() != null && browser().isRemote) {
|
||||
artifact.isRemote = true;
|
||||
}
|
||||
artifact.saveAs(recordHarPath);
|
||||
artifact.delete();
|
||||
}
|
||||
|
||||
sendMessage("close");
|
||||
} catch (PlaywrightException e) {
|
||||
if (!isSafeCloseError(e)) {
|
||||
@@ -306,23 +330,23 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void route(String url, Consumer<Route> handler) {
|
||||
route(new UrlMatcher(url), handler);
|
||||
public void route(String url, Consumer<Route> handler, RouteOptions options) {
|
||||
route(new UrlMatcher(this.baseUrl, url), handler, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void route(Pattern url, Consumer<Route> handler) {
|
||||
route(new UrlMatcher(url), handler);
|
||||
public void route(Pattern url, Consumer<Route> handler, RouteOptions options) {
|
||||
route(new UrlMatcher(url), handler, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void route(Predicate<String> url, Consumer<Route> handler) {
|
||||
route(new UrlMatcher(url), handler);
|
||||
public void route(Predicate<String> url, Consumer<Route> handler, RouteOptions options) {
|
||||
route(new UrlMatcher(url), handler, options);
|
||||
}
|
||||
|
||||
private void route(UrlMatcher matcher, Consumer<Route> handler) {
|
||||
private void route(UrlMatcher matcher, Consumer<Route> handler, RouteOptions options) {
|
||||
withLogging("BrowserContext.route", () -> {
|
||||
routes.add(matcher, handler);
|
||||
routes.add(matcher, handler, options == null ? null : options.times);
|
||||
if (routes.size() == 1) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("enabled", true);
|
||||
@@ -406,7 +430,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
|
||||
@Override
|
||||
public void unroute(String url, Consumer<Route> handler) {
|
||||
unroute(new UrlMatcher(url), handler);
|
||||
unroute(new UrlMatcher(this.baseUrl, url), handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -477,6 +501,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
} else if ("requestFailed".equals(event)) {
|
||||
String guid = params.getAsJsonObject("request").get("guid").getAsString();
|
||||
RequestImpl request = connection.getExistingObject(guid);
|
||||
request.didFailOrFinish = true;
|
||||
if (params.has("failureText")) {
|
||||
request.failure = params.get("failureText").getAsString();
|
||||
}
|
||||
@@ -491,6 +516,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
} else if ("requestFinished".equals(event)) {
|
||||
String guid = params.getAsJsonObject("request").get("guid").getAsString();
|
||||
RequestImpl request = connection.getExistingObject(guid);
|
||||
request.didFailOrFinish = true;
|
||||
if (request.timing != null) {
|
||||
request.timing.responseEnd = params.get("responseEndTiming").getAsDouble();
|
||||
}
|
||||
|
||||
@@ -165,12 +165,13 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
params.addProperty("noDefaultViewport", true);
|
||||
}
|
||||
}
|
||||
params.addProperty("sdkLanguage", "java");
|
||||
JsonElement result = sendMessage("newContext", params);
|
||||
BrowserContextImpl context = connection.getExistingObject(result.getAsJsonObject().getAsJsonObject("context").get("guid").getAsString());
|
||||
if (options.recordVideoDir != null) {
|
||||
context.videosDir = options.recordVideoDir;
|
||||
context.videosDir = options.recordVideoDir;
|
||||
if (options.baseURL != null) {
|
||||
context.setBaseUrl(options.baseURL);
|
||||
}
|
||||
context.recordHarPath = options.recordHarPath;
|
||||
contexts.add(context);
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
}
|
||||
WebSocketTransport transport = new WebSocketTransport(new URI(wsEndpoint), headers, timeout, slowMo);
|
||||
Connection connection = new Connection(transport);
|
||||
PlaywrightImpl playwright = (PlaywrightImpl) connection.waitForObjectWithKnownName("Playwright");
|
||||
PlaywrightImpl playwright = connection.initializePlaywright();
|
||||
if (!playwright.initializer.has("preLaunchedBrowser")) {
|
||||
try {
|
||||
connection.close();
|
||||
@@ -119,7 +119,6 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
}
|
||||
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("sdkLanguage", "java");
|
||||
params.addProperty("endpointURL", endpointURL);
|
||||
JsonObject json = sendMessage("connectOverCDP", params).getAsJsonObject();
|
||||
|
||||
@@ -183,12 +182,13 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
params.addProperty("noDefaultViewport", true);
|
||||
}
|
||||
}
|
||||
params.addProperty("sdkLanguage", "java");
|
||||
JsonObject json = sendMessage("launchPersistentContext", params).getAsJsonObject();
|
||||
BrowserContextImpl context = connection.getExistingObject(json.getAsJsonObject("context").get("guid").getAsString());
|
||||
if (options.recordVideoDir != null) {
|
||||
context.videosDir = options.recordVideoDir;
|
||||
context.videosDir = options.recordVideoDir;
|
||||
if (options.baseURL != null) {
|
||||
context.setBaseUrl(options.baseURL);
|
||||
}
|
||||
context.recordHarPath = options.recordHarPath;
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
@@ -69,9 +69,18 @@ class ChannelOwner extends LoggingSupport {
|
||||
}
|
||||
|
||||
<T> T withWaitLogging(String apiName, Supplier<T> code) {
|
||||
return super.withLogging(apiName, new WaitForEventLogger<>(this, apiName, code));
|
||||
return new WaitForEventLogger<>(this, apiName, code).get();
|
||||
}
|
||||
|
||||
@Override
|
||||
<T> T withLogging(String apiName, Supplier<T> code) {
|
||||
String previousApiName = connection.setApiName(apiName);
|
||||
try {
|
||||
return super.withLogging(apiName, code);
|
||||
} finally {
|
||||
connection.setApiName(previousApiName);
|
||||
}
|
||||
}
|
||||
|
||||
WaitableResult<JsonElement> sendMessageAsync(String method, JsonObject params) {
|
||||
return connection.sendMessageAsync(guid, method, params);
|
||||
|
||||
@@ -19,16 +19,12 @@ 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;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
@@ -68,6 +64,7 @@ public class Connection {
|
||||
private int lastId = 0;
|
||||
private final Path srcDir;
|
||||
private final Map<Integer, WaitableResult<JsonElement>> callbacks = new HashMap<>();
|
||||
private String apiName;
|
||||
private static final boolean isLogging;
|
||||
static {
|
||||
String debug = System.getenv("DEBUG");
|
||||
@@ -76,7 +73,14 @@ public class Connection {
|
||||
|
||||
class Root extends ChannelOwner {
|
||||
Root(Connection connection) {
|
||||
super(connection, "", "");
|
||||
super(connection, "Root", "");
|
||||
}
|
||||
|
||||
Playwright initialize() {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("sdkLanguage", "java");
|
||||
JsonElement result = sendMessage("initialize", params.getAsJsonObject());
|
||||
return this.connection.getExistingObject(result.getAsJsonObject().getAsJsonObject("playwright").get("guid").getAsString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +98,12 @@ public class Connection {
|
||||
}
|
||||
}
|
||||
|
||||
String setApiName(String name) {
|
||||
String previous = apiName;
|
||||
apiName = name;
|
||||
return previous;
|
||||
}
|
||||
|
||||
void close() throws IOException {
|
||||
transport.close();
|
||||
}
|
||||
@@ -154,11 +164,14 @@ public class Connection {
|
||||
message.addProperty("guid", guid);
|
||||
message.addProperty("method", method);
|
||||
message.add("params", params);
|
||||
JsonObject metadata = new JsonObject();
|
||||
if (srcDir != null) {
|
||||
JsonObject metadata = new JsonObject();
|
||||
metadata.add("stack", currentStackTrace());
|
||||
message.add("metadata", metadata);
|
||||
}
|
||||
if (apiName != null) {
|
||||
metadata.addProperty("apiName", apiName);
|
||||
}
|
||||
message.add("metadata", metadata);
|
||||
String messageString = gson().toJson(message);
|
||||
if (isLogging) {
|
||||
logWithTimestamp("SEND ► " + messageString);
|
||||
@@ -167,11 +180,8 @@ public class Connection {
|
||||
return result;
|
||||
}
|
||||
|
||||
public ChannelOwner waitForObjectWithKnownName(String guid) {
|
||||
while (!objects.containsKey(guid)) {
|
||||
processOneMessage();
|
||||
}
|
||||
return objects.get(guid);
|
||||
public PlaywrightImpl initializePlaywright() {
|
||||
return (PlaywrightImpl) this.root.initialize();
|
||||
}
|
||||
|
||||
public <T> T getExistingObject(String guid) {
|
||||
@@ -295,6 +305,10 @@ public class Connection {
|
||||
case "ElementHandle":
|
||||
result = new ElementHandleImpl(parent, type, guid, initializer);
|
||||
break;
|
||||
case "FetchRequest":
|
||||
// Create fake object as this API is experimental an only exposed in Node.js.
|
||||
result = new ChannelOwner(parent, type, guid, initializer);
|
||||
break;
|
||||
case "Frame":
|
||||
result = new FrameImpl(parent, type, guid, initializer);
|
||||
break;
|
||||
|
||||
@@ -23,7 +23,7 @@ import com.microsoft.playwright.Page;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
|
||||
class DownloadImpl extends LoggingSupport implements Download {
|
||||
class DownloadImpl implements Download {
|
||||
private final PageImpl page;
|
||||
private final ArtifactImpl artifact;
|
||||
private final JsonObject initializer;
|
||||
@@ -44,19 +44,24 @@ class DownloadImpl extends LoggingSupport implements Download {
|
||||
return initializer.get("suggestedFilename").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
page.withLogging("Download.cancel", () -> artifact.cancel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream createReadStream() {
|
||||
return withLogging("Download.createReadStream", () -> artifact.createReadStream());
|
||||
return page.withLogging("Download.createReadStream", () -> artifact.createReadStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
withLogging("Download.delete", () -> artifact.delete());
|
||||
page.withLogging("Download.delete", () -> artifact.delete());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String failure() {
|
||||
return withLogging("Download.failure", () -> artifact.failure());
|
||||
return page.withLogging("Download.failure", () -> artifact.failure());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,11 +71,11 @@ class DownloadImpl extends LoggingSupport implements Download {
|
||||
|
||||
@Override
|
||||
public Path path() {
|
||||
return withLogging("Download.path", () -> artifact.pathAfterFinished());
|
||||
return page.withLogging("Download.path", () -> artifact.pathAfterFinished());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAs(Path path) {
|
||||
withLogging("Download.saveAs", () -> artifact.saveAs(path));
|
||||
page.withLogging("Download.saveAs", () -> artifact.saveAs(path));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import java.util.Base64;
|
||||
import java.util.List;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.*;
|
||||
import static com.microsoft.playwright.impl.Utils.convertViaJson;
|
||||
import static com.microsoft.playwright.options.ScreenshotType.JPEG;
|
||||
import static com.microsoft.playwright.options.ScreenshotType.PNG;
|
||||
|
||||
@@ -209,10 +210,15 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
|
||||
@Override
|
||||
public void hover(HoverOptions options) {
|
||||
withLogging("ElementHandle.hover", () -> {
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("hover", params);
|
||||
});
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -231,6 +237,20 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
});
|
||||
}
|
||||
|
||||
@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();
|
||||
return json.get("value").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
return withLogging("ElementHandle.isChecked", () -> {
|
||||
@@ -412,6 +432,15 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
withLogging("ElementHandle.selectText", () -> selectTextImpl(options));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChecked(boolean checked, SetCheckedOptions options) {
|
||||
if (checked) {
|
||||
check(convertViaJson(options, CheckOptions.class));
|
||||
} else {
|
||||
uncheck(convertViaJson(options, UncheckOptions.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(Path files, SetInputFilesOptions options) {
|
||||
setInputFiles(new Path[]{files}, options);
|
||||
|
||||
@@ -41,6 +41,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
FrameImpl parentFrame;
|
||||
Set<FrameImpl> childFrames = new LinkedHashSet<>();
|
||||
private final Set<LoadState> loadStates = new HashSet<>();
|
||||
|
||||
enum InternalEventType { NAVIGATED, LOADSTATE }
|
||||
private final ListenerCollection<InternalEventType> internalListeners = new ListenerCollection<>();
|
||||
PageImpl page;
|
||||
@@ -70,12 +71,15 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementHandle querySelector(String selector) {
|
||||
return withLogging("Frame.querySelector", () -> querySelectorImpl(selector));
|
||||
public ElementHandle querySelector(String selector, QuerySelectorOptions options) {
|
||||
return withLogging("Frame.querySelector", () -> querySelectorImpl(selector, options));
|
||||
}
|
||||
|
||||
ElementHandleImpl querySelectorImpl(String selector) {
|
||||
JsonObject params = new JsonObject();
|
||||
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);
|
||||
JsonObject element = json.getAsJsonObject().getAsJsonObject("element");
|
||||
@@ -133,12 +137,15 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evalOnSelector(String selector, String pageFunction, Object arg) {
|
||||
return withLogging("Frame.evalOnSelector", () -> evalOnSelectorImpl(selector, pageFunction, arg));
|
||||
public Object evalOnSelector(String selector, String pageFunction, Object arg, EvalOnSelectorOptions options) {
|
||||
return withLogging("Frame.evalOnSelector", () -> evalOnSelectorImpl(selector, pageFunction, arg, options));
|
||||
}
|
||||
|
||||
Object evalOnSelectorImpl(String selector, String pageFunction, Object arg) {
|
||||
JsonObject params = new JsonObject();
|
||||
Object evalOnSelectorImpl(String selector, String pageFunction, Object arg, EvalOnSelectorOptions options) {
|
||||
if (options == null) {
|
||||
options = new EvalOnSelectorOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
@@ -407,6 +414,21 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
sendMessage("hover", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragAndDrop(String source, String target, DragAndDropOptions options) {
|
||||
withLogging("Frame.dragAndDrop", () -> dragAndDropImpl(source, target, options));
|
||||
}
|
||||
|
||||
void dragAndDropImpl(String source, String target, DragAndDropOptions options) {
|
||||
if (options == null) {
|
||||
options = new DragAndDropOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("source", source);
|
||||
params.addProperty("target", target);
|
||||
sendMessage("dragAndDrop", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String innerHTML(String selector, InnerHTMLOptions options) {
|
||||
return withLogging("Frame.innerHTML", () -> innerHTMLImpl(selector, options));
|
||||
@@ -437,6 +459,21 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
return json.get("value").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String inputValue(String selector, InputValueOptions options) {
|
||||
return withLogging("Frame.inputValue", () -> inputValueImpl(selector, options));
|
||||
}
|
||||
|
||||
String inputValueImpl(String selector, InputValueOptions options) {
|
||||
if (options == null) {
|
||||
options = new InputValueOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject json = sendMessage("inputValue", params).getAsJsonObject();
|
||||
return json.get("value").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked(String selector, IsCheckedOptions options) {
|
||||
return withLogging("Page.isChecked", () -> isCheckedImpl(selector, options));
|
||||
@@ -522,6 +559,11 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
return withLogging("Page.isVisible", () -> isVisibleImpl(selector, options));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator locator(String selector) {
|
||||
return new LocatorImpl(this, selector);
|
||||
}
|
||||
|
||||
boolean isVisibleImpl(String selector, IsVisibleOptions options) {
|
||||
if (options == null) {
|
||||
options = new IsVisibleOptions();
|
||||
@@ -601,6 +643,19 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
return parseStringList(json.getAsJsonArray("values"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChecked(String selector, boolean checked, SetCheckedOptions options) {
|
||||
withLogging("Frame.setChecked", () -> setCheckedImpl(selector, checked, options));
|
||||
}
|
||||
|
||||
void setCheckedImpl(String selector, boolean checked, SetCheckedOptions options) {
|
||||
if (checked) {
|
||||
checkImpl(selector, convertViaJson(options, CheckOptions.class));
|
||||
} else {
|
||||
uncheckImpl(selector, convertViaJson(options, UncheckOptions.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContent(String html, SetContentOptions options) {
|
||||
withLogging("Frame.setContent", () -> setContentImpl(html, options));
|
||||
@@ -739,7 +794,10 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public void waitForLoadState(LoadState state, WaitForLoadStateOptions options) {
|
||||
withLogging("Frame.waitForLoadState", () -> waitForLoadStateImpl(state, options));
|
||||
withWaitLogging("Frame.waitForLoadState", () -> {
|
||||
waitForLoadStateImpl(state, options);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
void waitForLoadStateImpl(LoadState state, WaitForLoadStateOptions options) {
|
||||
@@ -875,7 +933,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
List<Waitable<Response>> waitables = new ArrayList<>();
|
||||
if (matcher == null) {
|
||||
matcher = UrlMatcher.forOneOf(options.url);
|
||||
matcher = UrlMatcher.forOneOf(page.context().baseUrl, options.url);
|
||||
}
|
||||
waitables.add(new WaitForNavigationHelper(matcher, convertViaJson(options.waitUntil, LoadState.class)));
|
||||
waitables.add(page.createWaitForCloseHelper());
|
||||
@@ -920,7 +978,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public void waitForURL(String url, WaitForURLOptions options) {
|
||||
waitForURL(new UrlMatcher(url), options);
|
||||
waitForURL(new UrlMatcher(page.context().baseUrl, url), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,7 +21,7 @@ import com.microsoft.playwright.Keyboard;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
|
||||
class KeyboardImpl extends LoggingSupport implements Keyboard {
|
||||
class KeyboardImpl implements Keyboard {
|
||||
private final ChannelOwner page;
|
||||
|
||||
KeyboardImpl(ChannelOwner page) {
|
||||
@@ -30,7 +30,7 @@ class KeyboardImpl extends LoggingSupport implements Keyboard {
|
||||
|
||||
@Override
|
||||
public void down(String key) {
|
||||
withLogging("Keyboard.down", () -> {
|
||||
page.withLogging("Keyboard.down", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("key", key);
|
||||
page.sendMessage("keyboardDown", params);
|
||||
@@ -39,7 +39,7 @@ class KeyboardImpl extends LoggingSupport implements Keyboard {
|
||||
|
||||
@Override
|
||||
public void insertText(String text) {
|
||||
withLogging("Keyboard.insertText", () -> {
|
||||
page.withLogging("Keyboard.insertText", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("text", text);
|
||||
page.sendMessage("keyboardInsertText", params);
|
||||
@@ -48,7 +48,7 @@ class KeyboardImpl extends LoggingSupport implements Keyboard {
|
||||
|
||||
@Override
|
||||
public void press(String key, PressOptions options) {
|
||||
withLogging("Keyboard.press", () -> pressImpl(key, options));
|
||||
page.withLogging("Keyboard.press", () -> pressImpl(key, options));
|
||||
}
|
||||
|
||||
private void pressImpl(String key, PressOptions options) {
|
||||
@@ -62,7 +62,7 @@ class KeyboardImpl extends LoggingSupport implements Keyboard {
|
||||
|
||||
@Override
|
||||
public void type(String text, TypeOptions options) {
|
||||
withLogging("Keyboard.type", () -> typeImpl(text, options));
|
||||
page.withLogging("Keyboard.type", () -> typeImpl(text, options));
|
||||
}
|
||||
|
||||
private void typeImpl(String text, TypeOptions options) {
|
||||
@@ -76,7 +76,7 @@ class KeyboardImpl extends LoggingSupport implements Keyboard {
|
||||
|
||||
@Override
|
||||
public void up(String key) {
|
||||
withLogging("Keyboard.up", () -> {
|
||||
page.withLogging("Keyboard.up", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("key", key);
|
||||
page.sendMessage("keyboardUp", params);
|
||||
|
||||
@@ -0,0 +1,406 @@
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.microsoft.playwright.ElementHandle;
|
||||
import com.microsoft.playwright.Frame;
|
||||
import com.microsoft.playwright.JSHandle;
|
||||
import com.microsoft.playwright.Locator;
|
||||
import com.microsoft.playwright.options.BoundingBox;
|
||||
import com.microsoft.playwright.options.FilePayload;
|
||||
import com.microsoft.playwright.options.SelectOption;
|
||||
import com.microsoft.playwright.options.WaitForSelectorState;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import static com.microsoft.playwright.impl.Utils.convertViaJson;
|
||||
|
||||
class LocatorImpl implements Locator {
|
||||
private final FrameImpl frame;
|
||||
private final String selector;
|
||||
|
||||
public LocatorImpl(FrameImpl frame, String selector) {
|
||||
this.frame = frame;
|
||||
this.selector = selector;
|
||||
}
|
||||
|
||||
private <R, O> R withElement(BiFunction<ElementHandle, O, R> callback, O options) {
|
||||
ElementHandleOptions handleOptions = convertViaJson(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
|
||||
public List<String> allInnerTexts() {
|
||||
return (List<String>) frame.evalOnSelectorAll(selector, "ee => ee.map(e => e.innerText)");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> allTextContents() {
|
||||
return (List<String>) frame.evalOnSelectorAll(selector, "ee => ee.map(e => e.textContent || '')");
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoundingBox boundingBox(BoundingBoxOptions options) {
|
||||
return withElement((h, o) -> h.boundingBox(), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void check(CheckOptions options) {
|
||||
if (options == null) {
|
||||
options = new CheckOptions();
|
||||
}
|
||||
frame.check(selector, convertViaJson(options, Frame.CheckOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void click(ClickOptions options) {
|
||||
if (options == null) {
|
||||
options = new ClickOptions();
|
||||
}
|
||||
frame.click(selector, convertViaJson(options, Frame.ClickOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int count() {
|
||||
return ((Number) evaluateAll("ee => ee.length")).intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dblclick(DblclickOptions options) {
|
||||
if (options == null) {
|
||||
options = new DblclickOptions();
|
||||
}
|
||||
frame.dblclick(selector, convertViaJson(options, Frame.DblclickOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchEvent(String type, Object eventInit, DispatchEventOptions options) {
|
||||
if (options == null) {
|
||||
options = new DispatchEventOptions();
|
||||
}
|
||||
frame.dispatchEvent(selector, type, eventInit, convertViaJson(options, Frame.DispatchEventOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementHandle elementHandle(ElementHandleOptions options) {
|
||||
if (options == null) {
|
||||
options = new ElementHandleOptions();
|
||||
}
|
||||
Frame.WaitForSelectorOptions frameOptions = convertViaJson(options, Frame.WaitForSelectorOptions.class);
|
||||
frameOptions.setStrict(true);
|
||||
frameOptions.setState(WaitForSelectorState.ATTACHED);
|
||||
return frame.waitForSelector(selector, frameOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ElementHandle> elementHandles() {
|
||||
return frame.querySelectorAll(selector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evaluate(String expression, Object arg, EvaluateOptions options) {
|
||||
return withElement((h, o) -> h.evaluate(expression, arg), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evaluateAll(String expression, Object arg) {
|
||||
return frame.evalOnSelectorAll(selector, expression, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSHandle evaluateHandle(String expression, Object arg, EvaluateHandleOptions options) {
|
||||
return withElement((h, o) -> h.evaluateHandle(expression, arg), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fill(String value, FillOptions options) {
|
||||
if (options == null) {
|
||||
options = new FillOptions();
|
||||
}
|
||||
frame.fill(selector, value, convertViaJson(options, Frame.FillOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator first() {
|
||||
return new LocatorImpl(frame, selector + " >> nth=0");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focus(FocusOptions options) {
|
||||
if (options == null) {
|
||||
options = new FocusOptions();
|
||||
}
|
||||
frame.focus(selector, convertViaJson(options, Frame.FocusOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttribute(String name, GetAttributeOptions options) {
|
||||
if (options == null) {
|
||||
options = new GetAttributeOptions();
|
||||
}
|
||||
return frame.getAttribute(selector, name, convertViaJson(options, Frame.GetAttributeOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hover(HoverOptions options) {
|
||||
if (options == null) {
|
||||
options = new HoverOptions();
|
||||
}
|
||||
frame.hover(selector, convertViaJson(options, Frame.HoverOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String innerHTML(InnerHTMLOptions options) {
|
||||
if (options == null) {
|
||||
options = new InnerHTMLOptions();
|
||||
}
|
||||
return frame.innerHTML(selector, convertViaJson(options, Frame.InnerHTMLOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String innerText(InnerTextOptions options) {
|
||||
if (options == null) {
|
||||
options = new InnerTextOptions();
|
||||
}
|
||||
return frame.innerText(selector, convertViaJson(options, Frame.InnerTextOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String inputValue(InputValueOptions options) {
|
||||
if (options == null) {
|
||||
options = new InputValueOptions();
|
||||
}
|
||||
return frame.inputValue(selector, convertViaJson(options, Frame.InputValueOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked(IsCheckedOptions options) {
|
||||
if (options == null) {
|
||||
options = new IsCheckedOptions();
|
||||
}
|
||||
return frame.isChecked(selector, convertViaJson(options, Frame.IsCheckedOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisabled(IsDisabledOptions options) {
|
||||
if (options == null) {
|
||||
options = new IsDisabledOptions();
|
||||
}
|
||||
return frame.isDisabled(selector, convertViaJson(options, Frame.IsDisabledOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEditable(IsEditableOptions options) {
|
||||
if (options == null) {
|
||||
options = new IsEditableOptions();
|
||||
}
|
||||
return frame.isEditable(selector, convertViaJson(options, Frame.IsEditableOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(IsEnabledOptions options) {
|
||||
if (options == null) {
|
||||
options = new IsEnabledOptions();
|
||||
}
|
||||
return frame.isEnabled(selector, convertViaJson(options, Frame.IsEnabledOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden(IsHiddenOptions options) {
|
||||
if (options == null) {
|
||||
options = new IsHiddenOptions();
|
||||
}
|
||||
return frame.isHidden(selector, convertViaJson(options, Frame.IsHiddenOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible(IsVisibleOptions options) {
|
||||
if (options == null) {
|
||||
options = new IsVisibleOptions();
|
||||
}
|
||||
return frame.isVisible(selector, convertViaJson(options, Frame.IsVisibleOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator last() {
|
||||
return new LocatorImpl(frame, selector + " >> nth=-1");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator locator(String selector) {
|
||||
return new LocatorImpl(frame, this.selector + " >> " + selector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator nth(int index) {
|
||||
return new LocatorImpl(frame, selector + " >> nth=" + index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void press(String key, PressOptions options) {
|
||||
if (options == null) {
|
||||
options = new PressOptions();
|
||||
}
|
||||
frame.press(selector, key, convertViaJson(options, Frame.PressOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] screenshot(ScreenshotOptions options) {
|
||||
return withElement((h, o) -> h.screenshot(o), convertViaJson(options, ElementHandle.ScreenshotOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scrollIntoViewIfNeeded(ScrollIntoViewIfNeededOptions options) {
|
||||
withElement((h, o) -> {
|
||||
h.scrollIntoViewIfNeeded(o);
|
||||
return null;
|
||||
}, convertViaJson(options, ElementHandle.ScrollIntoViewIfNeededOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String values, SelectOptionOptions options) {
|
||||
if (options == null) {
|
||||
options = new SelectOptionOptions();
|
||||
}
|
||||
return frame.selectOption(selector, values, convertViaJson(options, Frame.SelectOptionOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(ElementHandle values, SelectOptionOptions options) {
|
||||
if (options == null) {
|
||||
options = new SelectOptionOptions();
|
||||
}
|
||||
return frame.selectOption(selector, values, convertViaJson(options, Frame.SelectOptionOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String[] values, SelectOptionOptions options) {
|
||||
if (options == null) {
|
||||
options = new SelectOptionOptions();
|
||||
}
|
||||
return frame.selectOption(selector, values, convertViaJson(options, Frame.SelectOptionOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(SelectOption values, SelectOptionOptions options) {
|
||||
if (options == null) {
|
||||
options = new SelectOptionOptions();
|
||||
}
|
||||
return frame.selectOption(selector, values, convertViaJson(options, Frame.SelectOptionOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(ElementHandle[] values, SelectOptionOptions options) {
|
||||
if (options == null) {
|
||||
options = new SelectOptionOptions();
|
||||
}
|
||||
return frame.selectOption(selector, values, convertViaJson(options, Frame.SelectOptionOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(SelectOption[] values, SelectOptionOptions options) {
|
||||
if (options == null) {
|
||||
options = new SelectOptionOptions();
|
||||
}
|
||||
return frame.selectOption(selector, values, convertViaJson(options, Frame.SelectOptionOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectText(SelectTextOptions options) {
|
||||
withElement((h, o) -> {
|
||||
h.selectText(o);
|
||||
return null;
|
||||
}, convertViaJson(options, ElementHandle.SelectTextOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChecked(boolean checked, SetCheckedOptions options) {
|
||||
if (options == null) {
|
||||
options = new SetCheckedOptions();
|
||||
}
|
||||
frame.setChecked(selector, checked, convertViaJson(options, Frame.SetCheckedOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(Path files, SetInputFilesOptions options) {
|
||||
if (options == null) {
|
||||
options = new SetInputFilesOptions();
|
||||
}
|
||||
frame.setInputFiles(selector, files, convertViaJson(options, Frame.SetInputFilesOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(Path[] files, SetInputFilesOptions options) {
|
||||
if (options == null) {
|
||||
options = new SetInputFilesOptions();
|
||||
}
|
||||
frame.setInputFiles(selector, files, convertViaJson(options, Frame.SetInputFilesOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(FilePayload files, SetInputFilesOptions options) {
|
||||
if (options == null) {
|
||||
options = new SetInputFilesOptions();
|
||||
}
|
||||
frame.setInputFiles(selector, files, convertViaJson(options, Frame.SetInputFilesOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(FilePayload[] files, SetInputFilesOptions options) {
|
||||
if (options == null) {
|
||||
options = new SetInputFilesOptions();
|
||||
}
|
||||
frame.setInputFiles(selector, files, convertViaJson(options, Frame.SetInputFilesOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tap(TapOptions options) {
|
||||
if (options == null) {
|
||||
options = new TapOptions();
|
||||
}
|
||||
frame.tap(selector, convertViaJson(options, Frame.TapOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String textContent(TextContentOptions options) {
|
||||
if (options == null) {
|
||||
options = new TextContentOptions();
|
||||
}
|
||||
return frame.textContent(selector, convertViaJson(options, Frame.TextContentOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void type(String text, TypeOptions options) {
|
||||
if (options == null) {
|
||||
options = new TypeOptions();
|
||||
}
|
||||
frame.type(selector, text, convertViaJson(options, Frame.TypeOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncheck(UncheckOptions options) {
|
||||
if (options == null) {
|
||||
options = new UncheckOptions();
|
||||
}
|
||||
frame.uncheck(selector, convertViaJson(options, Frame.UncheckOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Locator@" + selector;
|
||||
}
|
||||
}
|
||||
@@ -93,6 +93,16 @@ class MouseImpl implements Mouse {
|
||||
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();
|
||||
|
||||
@@ -125,7 +125,14 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
if (listeners.hasListeners(EventType.DIALOG)) {
|
||||
listeners.notify(EventType.DIALOG, dialog);
|
||||
} else {
|
||||
dialog.dismiss();
|
||||
if ("beforeunload".equals(dialog.type())) {
|
||||
try {
|
||||
dialog.accept();
|
||||
} catch (PlaywrightException e) {
|
||||
}
|
||||
} else {
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
||||
} else if ("worker".equals(event)) {
|
||||
String guid = params.getAsJsonObject("worker").get("guid").getAsString();
|
||||
@@ -445,7 +452,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
if (options == null) {
|
||||
options = new WaitForCloseOptions();
|
||||
}
|
||||
return waitForEventWithTimeout(EventType.CLOSE, code, options.timeout);
|
||||
return waitForEventWithTimeout(EventType.CLOSE, code, null, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -457,7 +464,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
if (options == null) {
|
||||
options = new WaitForConsoleMessageOptions();
|
||||
}
|
||||
return waitForEventWithTimeout(EventType.CONSOLE, code, options.timeout);
|
||||
return waitForEventWithTimeout(EventType.CONSOLE, code, options.predicate, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -469,7 +476,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
if (options == null) {
|
||||
options = new WaitForDownloadOptions();
|
||||
}
|
||||
return waitForEventWithTimeout(EventType.DOWNLOAD, code, options.timeout);
|
||||
return waitForEventWithTimeout(EventType.DOWNLOAD, code, options.predicate, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -482,7 +489,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
if (options == null) {
|
||||
options = new WaitForFileChooserOptions();
|
||||
}
|
||||
return waitForEventWithTimeout(EventType.FILECHOOSER, code, options.timeout);
|
||||
return waitForEventWithTimeout(EventType.FILECHOOSER, code, options.predicate, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -494,7 +501,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
if (options == null) {
|
||||
options = new WaitForPopupOptions();
|
||||
}
|
||||
return waitForEventWithTimeout(EventType.POPUP, code, options.timeout);
|
||||
return waitForEventWithTimeout(EventType.POPUP, code, options.predicate, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -506,7 +513,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
if (options == null) {
|
||||
options = new WaitForWebSocketOptions();
|
||||
}
|
||||
return waitForEventWithTimeout(EventType.WEBSOCKET, code, options.timeout);
|
||||
return waitForEventWithTimeout(EventType.WEBSOCKET, code, options.predicate, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -518,12 +525,12 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
if (options == null) {
|
||||
options = new WaitForWorkerOptions();
|
||||
}
|
||||
return waitForEventWithTimeout(EventType.WORKER, code, options.timeout);
|
||||
return waitForEventWithTimeout(EventType.WORKER, code, options.predicate, options.timeout);
|
||||
}
|
||||
|
||||
private <T> T waitForEventWithTimeout(EventType eventType, Runnable code, Double timeout) {
|
||||
private <T> T waitForEventWithTimeout(EventType eventType, Runnable code, Predicate<T> predicate, Double timeout) {
|
||||
List<Waitable<T>> waitables = new ArrayList<>();
|
||||
waitables.add(new WaitableEvent<>(listeners, eventType));
|
||||
waitables.add(new WaitableEvent<>(listeners, eventType, predicate));
|
||||
waitables.add(createWaitForCloseHelper());
|
||||
waitables.add(createWaitableTimeout(timeout));
|
||||
return runUntil(code, new WaitableRace<>(waitables));
|
||||
@@ -548,8 +555,9 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementHandle querySelector(String selector) {
|
||||
return withLogging("Page.querySelector", () -> mainFrame.querySelectorImpl(selector));
|
||||
public ElementHandle querySelector(String selector, QuerySelectorOptions options) {
|
||||
return withLogging("Page.querySelector", () -> mainFrame.querySelectorImpl(
|
||||
selector, convertViaJson(options, Frame.QuerySelectorOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -558,8 +566,9 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evalOnSelector(String selector, String pageFunction, Object arg) {
|
||||
return withLogging("Page.evalOnSelector", () -> mainFrame.evalOnSelectorImpl(selector, pageFunction, arg));
|
||||
public Object evalOnSelector(String selector, String pageFunction, Object arg, EvalOnSelectorOptions options) {
|
||||
return withLogging("Page.evalOnSelector", () -> mainFrame.evalOnSelectorImpl(
|
||||
selector, pageFunction, arg, convertViaJson(options, Frame.EvalOnSelectorOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -716,7 +725,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public Frame frameByUrl(String glob) {
|
||||
return frameFor(new UrlMatcher(glob));
|
||||
return frameFor(new UrlMatcher(browserContext.baseUrl, glob));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -795,6 +804,12 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
mainFrame.hoverImpl(selector, convertViaJson(options, Frame.HoverOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragAndDrop(String source, String target, DragAndDropOptions options) {
|
||||
withLogging("Page.dragAndDrop", () ->
|
||||
mainFrame.dragAndDropImpl(source, target, convertViaJson(options, Frame.DragAndDropOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String innerHTML(String selector, InnerHTMLOptions options) {
|
||||
return withLogging("Page.innerHTML",
|
||||
@@ -807,6 +822,12 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
() -> mainFrame.innerTextImpl(selector, convertViaJson(options, Frame.InnerTextOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String inputValue(String selector, InputValueOptions options) {
|
||||
return withLogging("Page.inputValue",
|
||||
() -> mainFrame.inputValueImpl(selector, convertViaJson(options, Frame.InputValueOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked(String selector, IsCheckedOptions options) {
|
||||
return withLogging("Page.isChecked",
|
||||
@@ -853,6 +874,11 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
return keyboard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator locator(String selector) {
|
||||
return mainFrame.locator(selector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Frame mainFrame() {
|
||||
return mainFrame;
|
||||
@@ -924,23 +950,23 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void route(String url, Consumer<Route> handler) {
|
||||
route(new UrlMatcher(url), handler);
|
||||
public void route(String url, Consumer<Route> handler, RouteOptions options) {
|
||||
route(new UrlMatcher(browserContext.baseUrl, url), handler, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void route(Pattern url, Consumer<Route> handler) {
|
||||
route(new UrlMatcher(url), handler);
|
||||
public void route(Pattern url, Consumer<Route> handler, RouteOptions options) {
|
||||
route(new UrlMatcher(url), handler, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void route(Predicate<String> url, Consumer<Route> handler) {
|
||||
route(new UrlMatcher(url), handler);
|
||||
public void route(Predicate<String> url, Consumer<Route> handler, RouteOptions options) {
|
||||
route(new UrlMatcher(url), handler, options);
|
||||
}
|
||||
|
||||
private void route(UrlMatcher matcher, Consumer<Route> handler) {
|
||||
private void route(UrlMatcher matcher, Consumer<Route> handler, RouteOptions options) {
|
||||
withLogging("Page.route", () -> {
|
||||
routes.add(matcher, handler);
|
||||
routes.add(matcher, handler, options == null ? null : options.times);
|
||||
if (routes.size() == 1) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("enabled", true);
|
||||
@@ -1023,6 +1049,12 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
() -> mainFrame.selectOptionImpl(selector, values, convertViaJson(options, Frame.SelectOptionOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChecked(String selector, boolean checked, SetCheckedOptions options) {
|
||||
withLogging("Page.setChecked",
|
||||
() -> mainFrame.setCheckedImpl(selector, checked, convertViaJson(options, Frame.SetCheckedOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContent(String html, SetContentOptions options) {
|
||||
withLogging("Page.setContent",
|
||||
@@ -1133,7 +1165,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void unroute(String url, Consumer<Route> handler) {
|
||||
unroute(new UrlMatcher(url), handler);
|
||||
unroute(new UrlMatcher(browserContext.baseUrl, url), handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1202,8 +1234,10 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void waitForLoadState(LoadState state, WaitForLoadStateOptions options) {
|
||||
withLogging("Page.waitForLoadState",
|
||||
() -> mainFrame.waitForLoadStateImpl(state, convertViaJson(options, Frame.WaitForLoadStateOptions.class)));
|
||||
withWaitLogging("Page.waitForLoadState", () -> {
|
||||
mainFrame.waitForLoadStateImpl(state, convertViaJson(options, Frame.WaitForLoadStateOptions.class));
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1270,7 +1304,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public Request waitForRequest(String urlGlob, WaitForRequestOptions options, Runnable code) {
|
||||
return waitForRequest(toRequestPredicate(new UrlMatcher(urlGlob)), options, code);
|
||||
return waitForRequest(toRequestPredicate(new UrlMatcher(browserContext.baseUrl, urlGlob)), options, code);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1291,12 +1325,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
if (options == null) {
|
||||
options = new WaitForRequestOptions();
|
||||
}
|
||||
List<Waitable<Request>> waitables = new ArrayList<>();
|
||||
waitables.add(new WaitableEvent<>(listeners, EventType.REQUEST,
|
||||
request -> predicate == null || predicate.test(request)));
|
||||
waitables.add(createWaitForCloseHelper());
|
||||
waitables.add(createWaitableTimeout(options.timeout));
|
||||
return runUntil(code, new WaitableRace<>(waitables));
|
||||
return waitForEventWithTimeout(EventType.REQUEST, code, predicate, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1308,18 +1337,12 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
if (options == null) {
|
||||
options = new WaitForRequestFinishedOptions();
|
||||
}
|
||||
List<Waitable<Request>> waitables = new ArrayList<>();
|
||||
Predicate<Request> predicate = options.predicate;
|
||||
waitables.add(new WaitableEvent<>(listeners, EventType.REQUESTFINISHED,
|
||||
request -> predicate == null || predicate.test(request)));
|
||||
waitables.add(createWaitForCloseHelper());
|
||||
waitables.add(createWaitableTimeout(options.timeout));
|
||||
return runUntil(code, new WaitableRace<>(waitables));
|
||||
return waitForEventWithTimeout(EventType.REQUESTFINISHED, code, options.predicate, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response waitForResponse(String urlGlob, WaitForResponseOptions options, Runnable code) {
|
||||
return waitForResponse(toResponsePredicate(new UrlMatcher(urlGlob)), options, code);
|
||||
return waitForResponse(toResponsePredicate(new UrlMatcher(browserContext.baseUrl, urlGlob)), options, code);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1340,12 +1363,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
if (options == null) {
|
||||
options = new WaitForResponseOptions();
|
||||
}
|
||||
List<Waitable<Response>> waitables = new ArrayList<>();
|
||||
waitables.add(new WaitableEvent<>(listeners, EventType.RESPONSE,
|
||||
response -> predicate == null || predicate.test(response)));
|
||||
waitables.add(createWaitForCloseHelper());
|
||||
waitables.add(createWaitableTimeout(options.timeout));
|
||||
return runUntil(code, new WaitableRace<>(waitables));
|
||||
return waitForEventWithTimeout(EventType.RESPONSE, code, predicate, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1361,7 +1379,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void waitForURL(String url, WaitForURLOptions options) {
|
||||
waitForURL(new UrlMatcher(url), options);
|
||||
waitForURL(new UrlMatcher(browserContext.baseUrl, url), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -23,20 +23,26 @@ import com.microsoft.playwright.Selectors;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class PlaywrightImpl extends ChannelOwner implements Playwright {
|
||||
private Process driverProcess;
|
||||
|
||||
public static PlaywrightImpl create() {
|
||||
public static PlaywrightImpl create(CreateOptions options) {
|
||||
try {
|
||||
Path driver = Driver.ensureDriverInstalled();
|
||||
Map<String, String> env = Collections.emptyMap();
|
||||
if (options != null && options.env != null) {
|
||||
env = options.env;
|
||||
}
|
||||
Path driver = Driver.ensureDriverInstalled(env);
|
||||
ProcessBuilder pb = new ProcessBuilder(driver.toString(), "run-driver");
|
||||
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
|
||||
// pb.environment().put("DEBUG", "pw:pro*");
|
||||
pb.environment().putAll(env);
|
||||
Process p = pb.start();
|
||||
Connection connection = new Connection(new PipeTransport(p.getInputStream(), p.getOutputStream()));
|
||||
PlaywrightImpl result = (PlaywrightImpl) connection.waitForObjectWithKnownName("Playwright");
|
||||
PlaywrightImpl result = connection.initializePlaywright();
|
||||
result.driverProcess = p;
|
||||
result.initSharedSelectors(null);
|
||||
return result;
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.options.HttpHeader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
class RawHeaders {
|
||||
private final List<HttpHeader> headersArray;
|
||||
private final Map<String, List<String>> headersMap = new LinkedHashMap<>();
|
||||
|
||||
RawHeaders(List<HttpHeader> headers) {
|
||||
headersArray = headers;
|
||||
for (HttpHeader h: headers) {
|
||||
String name = h.name.toLowerCase();
|
||||
List<String> values = headersMap.get(name);
|
||||
if (values == null) {
|
||||
values = new ArrayList<>();
|
||||
headersMap.put(name, values);
|
||||
}
|
||||
values.add(h.value);
|
||||
}
|
||||
}
|
||||
|
||||
String get(String name) {
|
||||
List<String> values = getAll(name);
|
||||
if (values == null) {
|
||||
return null;
|
||||
}
|
||||
return String.join("set-cookie".equals(name.toLowerCase()) ? "\n" : ", ", values);
|
||||
}
|
||||
|
||||
List<String> getAll(String name) {
|
||||
return headersMap.get(name.toLowerCase());
|
||||
}
|
||||
|
||||
Map<String, String> headers() {
|
||||
Map<String, String> result = new LinkedHashMap<>();
|
||||
for (String name: headersMap.keySet()) {
|
||||
result.put(name, get(name));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
List<HttpHeader> headersArray() {
|
||||
return headersArray;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,25 +16,32 @@
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Frame;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import com.microsoft.playwright.Request;
|
||||
import com.microsoft.playwright.Response;
|
||||
import com.microsoft.playwright.options.HttpHeader;
|
||||
import com.microsoft.playwright.options.Sizes;
|
||||
import com.microsoft.playwright.options.Timing;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
import static com.microsoft.playwright.impl.Utils.toHeadersMap;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
public class RequestImpl extends ChannelOwner implements Request {
|
||||
private final byte[] postData;
|
||||
private RequestImpl redirectedFrom;
|
||||
private RequestImpl redirectedTo;
|
||||
final Map<String, String> headers = new HashMap<>();
|
||||
private final RawHeaders headers;
|
||||
private RawHeaders rawHeaders;
|
||||
String failure;
|
||||
Timing timing;
|
||||
boolean didFailOrFinish;
|
||||
|
||||
RequestImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
@@ -43,10 +50,7 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
redirectedFrom = connection.getExistingObject(initializer.getAsJsonObject("redirectedFrom").get("guid").getAsString());
|
||||
redirectedFrom.redirectedTo = this;
|
||||
}
|
||||
for (JsonElement e : initializer.getAsJsonArray("headers")) {
|
||||
JsonObject item = e.getAsJsonObject();
|
||||
headers.put(item.get("name").getAsString().toLowerCase(), item.get("value").getAsString());
|
||||
}
|
||||
headers = new RawHeaders(asList(gson().fromJson(initializer.getAsJsonArray("headers"), HttpHeader[].class)));
|
||||
if (initializer.has("postData")) {
|
||||
postData = Base64.getDecoder().decode(initializer.get("postData").getAsString());
|
||||
} else {
|
||||
@@ -54,19 +58,34 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> allHeaders() {
|
||||
return withLogging("Request.allHeaders", () -> getRawHeaders().headers());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String failure() {
|
||||
return failure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Frame frame() {
|
||||
public FrameImpl frame() {
|
||||
return connection.getExistingObject(initializer.getAsJsonObject("frame").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> headers() {
|
||||
return headers;
|
||||
return headers.headers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HttpHeader> headersArray() {
|
||||
return withLogging("Request.headersArray", () -> getRawHeaders().headersArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String headerValue(String name) {
|
||||
return withLogging("Request.headerValue", () -> getRawHeaders().get(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -108,7 +127,7 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response response() {
|
||||
public ResponseImpl response() {
|
||||
return withLogging("Request.response", () -> {
|
||||
JsonObject result = sendMessage("response").getAsJsonObject();
|
||||
if (!result.has("response")) {
|
||||
@@ -118,6 +137,18 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
});
|
||||
}
|
||||
|
||||
@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);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timing timing() {
|
||||
return timing;
|
||||
@@ -132,4 +163,17 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
return redirectedTo != null ? redirectedTo.finalRequest() : this;
|
||||
}
|
||||
|
||||
private RawHeaders getRawHeaders() {
|
||||
if (rawHeaders != null) {
|
||||
return rawHeaders;
|
||||
}
|
||||
JsonArray rawHeadersJson = withLogging("Request.allHeaders", () -> {
|
||||
JsonObject result = sendMessage("rawRequestHeaders").getAsJsonObject();
|
||||
return 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)));
|
||||
return rawHeaders;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,37 +16,38 @@
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Frame;
|
||||
import com.microsoft.playwright.Request;
|
||||
import com.microsoft.playwright.Response;
|
||||
import com.microsoft.playwright.options.HttpHeader;
|
||||
import com.microsoft.playwright.options.SecurityDetails;
|
||||
import com.microsoft.playwright.options.ServerAddr;
|
||||
import com.microsoft.playwright.options.Timing;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
public class ResponseImpl extends ChannelOwner implements Response {
|
||||
private final Map<String, String> headers = new HashMap<>();
|
||||
private final RawHeaders headers;
|
||||
private RawHeaders rawHeaders;
|
||||
private final RequestImpl request;
|
||||
|
||||
ResponseImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
|
||||
for (JsonElement e : initializer.getAsJsonArray("headers")) {
|
||||
JsonObject item = e.getAsJsonObject();
|
||||
headers.put(item.get("name").getAsString().toLowerCase(), item.get("value").getAsString());
|
||||
}
|
||||
|
||||
headers = new RawHeaders(asList(gson().fromJson(initializer.getAsJsonArray("headers"), HttpHeader[].class)));
|
||||
request = connection.getExistingObject(initializer.getAsJsonObject("request").get("guid").getAsString());
|
||||
request.headers.clear();
|
||||
for (JsonElement e : initializer.getAsJsonArray("requestHeaders")) {
|
||||
JsonObject item = e.getAsJsonObject();
|
||||
request.headers.put(item.get("name").getAsString().toLowerCase(), item.get("value").getAsString());
|
||||
}
|
||||
request.timing = Serialization.gson().fromJson(initializer.get("timing"), Timing.class);
|
||||
request.timing = gson().fromJson(initializer.get("timing"), Timing.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> allHeaders() {
|
||||
return withLogging("Response.allHeaders", () -> getRawHeaders().headers());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,13 +60,22 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
||||
|
||||
@Override
|
||||
public String finished() {
|
||||
return withLogging("Response.finished", () -> {
|
||||
JsonObject json = sendMessage("finished").getAsJsonObject();
|
||||
if (json.has("error")) {
|
||||
return json.get("error").getAsString();
|
||||
List<Waitable<String>> waitables = new ArrayList<>();
|
||||
waitables.add(new WaitableNever<String>() {
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return request.didFailOrFinish;
|
||||
}
|
||||
@Override
|
||||
public String get() {
|
||||
return request.failure();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
PageImpl page = request.frame().page;
|
||||
waitables.add(page.createWaitForCloseHelper());
|
||||
waitables.add(page.createWaitableTimeout(null));
|
||||
runUntil(() -> {}, new WaitableRace<>(waitables));
|
||||
return request.failure();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,7 +85,22 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
||||
|
||||
@Override
|
||||
public Map<String, String> headers() {
|
||||
return headers;
|
||||
return headers.headers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HttpHeader> headersArray() {
|
||||
return withLogging("Response.headersArray", () -> getRawHeaders().headersArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String headerValue(String name) {
|
||||
return getRawHeaders().get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> headerValues(String name) {
|
||||
return getRawHeaders().getAll(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -88,6 +113,28 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
||||
return request;
|
||||
}
|
||||
|
||||
@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;
|
||||
});
|
||||
}
|
||||
|
||||
@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;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int status() {
|
||||
return initializer.get("status").getAsInt();
|
||||
@@ -107,4 +154,12 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
||||
public String url() {
|
||||
return initializer.get("url").getAsString();
|
||||
}
|
||||
|
||||
private RawHeaders getRawHeaders() {
|
||||
if (rawHeaders == null) {
|
||||
JsonObject json = sendMessage("rawResponseHeaders").getAsJsonObject();
|
||||
rawHeaders = new RawHeaders(asList(gson().fromJson(json.getAsJsonArray("headers"), HttpHeader[].class)));
|
||||
}
|
||||
return rawHeaders;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,15 +29,31 @@ class Router {
|
||||
private static class RouteInfo {
|
||||
final UrlMatcher matcher;
|
||||
final Consumer<Route> handler;
|
||||
Integer times;
|
||||
|
||||
RouteInfo(UrlMatcher matcher, Consumer<Route> handler) {
|
||||
RouteInfo(UrlMatcher matcher, Consumer<Route> handler, Integer times) {
|
||||
this.matcher = matcher;
|
||||
this.handler = handler;
|
||||
this.times = times;
|
||||
}
|
||||
|
||||
boolean handle(Route route) {
|
||||
if (times != null && times <= 0) {
|
||||
return false;
|
||||
}
|
||||
if (!matcher.test(route.request().url())) {
|
||||
return false;
|
||||
}
|
||||
if (times != null) {
|
||||
--times;
|
||||
}
|
||||
handler.accept(route);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void add(UrlMatcher matcher, Consumer<Route> handler) {
|
||||
routes.add(new RouteInfo(matcher, handler));
|
||||
void add(UrlMatcher matcher, Consumer<Route> handler, Integer times) {
|
||||
routes.add(0, new RouteInfo(matcher, handler, times));
|
||||
}
|
||||
|
||||
void remove(UrlMatcher matcher, Consumer<Route> handler) {
|
||||
@@ -52,8 +68,7 @@ class Router {
|
||||
|
||||
boolean handle(Route route) {
|
||||
for (RouteInfo info : routes) {
|
||||
if (info.matcher.test(route.request().url())) {
|
||||
info.handler.accept(route);
|
||||
if (info.handle(route)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,9 +39,11 @@ class Serialization {
|
||||
if (gson == null) {
|
||||
gson = new GsonBuilder()
|
||||
.registerTypeAdapter(SameSiteAttribute.class, new SameSiteAdapter().nullSafe())
|
||||
.registerTypeAdapter(BrowserChannel.class, new BrowserChannelSerializer())
|
||||
.registerTypeAdapter(ColorScheme.class, new ColorSchemeAdapter().nullSafe())
|
||||
.registerTypeAdapter(Media.class, new MediaSerializer())
|
||||
.registerTypeAdapter(BrowserChannel.class, new ToLowerCaseAndDashSerializer<BrowserChannel>())
|
||||
.registerTypeAdapter(ColorScheme.class, new ToLowerCaseAndDashSerializer<ColorScheme>())
|
||||
.registerTypeAdapter(Media.class, new ToLowerCaseSerializer<Media>())
|
||||
.registerTypeAdapter(ForcedColors.class, new ToLowerCaseSerializer<ForcedColors>())
|
||||
.registerTypeAdapter(ReducedMotion.class, new ToLowerCaseAndDashSerializer<ReducedMotion>())
|
||||
.registerTypeAdapter(ScreenshotType.class, new ToLowerCaseSerializer<ScreenshotType>())
|
||||
.registerTypeAdapter(MouseButton.class, new ToLowerCaseSerializer<MouseButton>())
|
||||
.registerTypeAdapter(LoadState.class, new ToLowerCaseSerializer<LoadState>())
|
||||
@@ -255,6 +257,8 @@ class Serialization {
|
||||
private static boolean isSupported(Type type) {
|
||||
return new TypeToken<Optional<Media>>() {}.getType().getTypeName().equals(type.getTypeName()) ||
|
||||
new TypeToken<Optional<ColorScheme>>() {}.getType().getTypeName().equals(type.getTypeName()) ||
|
||||
new TypeToken<Optional<ForcedColors>>() {}.getType().getTypeName().equals(type.getTypeName()) ||
|
||||
new TypeToken<Optional<ReducedMotion>>() {}.getType().getTypeName().equals(type.getTypeName()) ||
|
||||
new TypeToken<Optional<ViewportSize>>() {}.getType().getTypeName().equals(type.getTypeName());
|
||||
}
|
||||
|
||||
@@ -303,11 +307,10 @@ class Serialization {
|
||||
return new JsonPrimitive(src.toString().toLowerCase().replace('_', '-'));
|
||||
}
|
||||
}
|
||||
|
||||
private static class MediaSerializer implements JsonSerializer<Media> {
|
||||
private static class ToLowerCaseAndDashSerializer<E extends Enum<E>> implements JsonSerializer<E> {
|
||||
@Override
|
||||
public JsonElement serialize(Media src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src.toString().toLowerCase());
|
||||
public JsonElement serialize(E src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src.toString().toLowerCase().replace('_', '-'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,37 +354,5 @@ class Serialization {
|
||||
return SameSiteAttribute.valueOf(value.toUpperCase());
|
||||
}
|
||||
}
|
||||
|
||||
private static class ColorSchemeAdapter extends TypeAdapter<ColorScheme> {
|
||||
@Override
|
||||
public void write(JsonWriter out, ColorScheme value) throws IOException {
|
||||
String stringValue;
|
||||
switch (value) {
|
||||
case DARK:
|
||||
stringValue = "dark";
|
||||
break;
|
||||
case LIGHT:
|
||||
stringValue = "light";
|
||||
break;
|
||||
case NO_PREFERENCE:
|
||||
stringValue = "no-preference";
|
||||
break;
|
||||
default:
|
||||
throw new PlaywrightException("Unexpected value: " + value);
|
||||
}
|
||||
out.value(stringValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorScheme read(JsonReader in) throws IOException {
|
||||
String value = in.nextString();
|
||||
switch (value) {
|
||||
case "dark": return ColorScheme.DARK;
|
||||
case "light": return ColorScheme.LIGHT;
|
||||
case "no-preference": return ColorScheme.NO_PREFERENCE;
|
||||
default: throw new PlaywrightException("Unexpected value: " + value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,9 @@ public class Stream extends ChannelOwner {
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) {
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("size", len);
|
||||
JsonObject json = sendMessage("read", params).getAsJsonObject();
|
||||
|
||||
@@ -31,10 +31,17 @@ class TracingImpl implements Tracing {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
private void export(Path path) {
|
||||
JsonObject json = context.sendMessage("tracingExport").getAsJsonObject();
|
||||
private void stopChunkImpl(Path path) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("save", path != null);
|
||||
JsonObject json = context.sendMessage("tracingStopChunk", params).getAsJsonObject();
|
||||
if (!json.has("artifact")) {
|
||||
return;
|
||||
}
|
||||
ArtifactImpl artifact = context.connection.getExistingObject(json.getAsJsonObject("artifact").get("guid").getAsString());
|
||||
if (context.browser().isRemote) {
|
||||
// In case of CDP connection browser is null but since the connection is established by
|
||||
// the driver it is safe to consider the artifact local.
|
||||
if (context.browser() != null && context.browser().isRemote) {
|
||||
artifact.isRemote = true;
|
||||
}
|
||||
artifact.saveAs(path);
|
||||
@@ -46,21 +53,34 @@ class TracingImpl implements Tracing {
|
||||
context.withLogging("Tracing.start", () -> startImpl(options));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startChunk() {
|
||||
context.withLogging("Tracing.startChunk", () -> {
|
||||
context.sendMessage("tracingStartChunk");
|
||||
});
|
||||
}
|
||||
|
||||
private void startImpl(StartOptions options) {
|
||||
if (options == null) {
|
||||
options = new StartOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
context.sendMessage("tracingStart", params);
|
||||
context.sendMessage("tracingStartChunk");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(StopOptions options) {
|
||||
context.withLogging("Tracing.stop", () -> {
|
||||
stopChunkImpl(options == null ? null : options.path);
|
||||
context.sendMessage("tracingStop");
|
||||
if (options != null && options.path != null) {
|
||||
export(options.path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopChunk(StopChunkOptions options) {
|
||||
context.withLogging("Tracing.stopChunk", () -> {
|
||||
stopChunkImpl(options == null ? null : options.path);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package com.microsoft.playwright.impl;
|
||||
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -33,15 +35,15 @@ class UrlMatcher {
|
||||
}
|
||||
|
||||
static UrlMatcher any() {
|
||||
return new UrlMatcher(null, null);
|
||||
return new UrlMatcher((Object) null, null);
|
||||
}
|
||||
|
||||
static UrlMatcher forOneOf(Object object) {
|
||||
static UrlMatcher forOneOf(URL baseUrl, Object object) {
|
||||
if (object == null) {
|
||||
return UrlMatcher.any();
|
||||
}
|
||||
if (object instanceof String) {
|
||||
return new UrlMatcher((String) object);
|
||||
return new UrlMatcher(baseUrl, (String) object);
|
||||
}
|
||||
if (object instanceof Pattern) {
|
||||
return new UrlMatcher((Pattern) object);
|
||||
@@ -52,8 +54,19 @@ class UrlMatcher {
|
||||
throw new PlaywrightException("Url must be String, Pattern or Predicate<String>, found: " + object.getClass().getTypeName());
|
||||
}
|
||||
|
||||
UrlMatcher(String url) {
|
||||
this(url, toPredicate(Pattern.compile(globToRegex(url))).or(s -> url == null || url.equals(s)));
|
||||
private static String resolveUrl(URL baseUrl, String spec) {
|
||||
if (baseUrl == null) {
|
||||
return spec;
|
||||
}
|
||||
try {
|
||||
return new URL(baseUrl, spec).toString();
|
||||
} catch (MalformedURLException e) {
|
||||
return spec;
|
||||
}
|
||||
}
|
||||
|
||||
UrlMatcher(URL base, String url) {
|
||||
this(url, toPredicate(Pattern.compile(globToRegex(resolveUrl(base, url)))).or(s -> url == null || url.equals(s)));
|
||||
}
|
||||
|
||||
UrlMatcher(Pattern pattern) {
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.microsoft.playwright.impl;
|
||||
import com.google.gson.*;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import com.microsoft.playwright.options.FilePayload;
|
||||
import com.microsoft.playwright.options.HttpHeader;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -187,4 +188,12 @@ class Utils {
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
static Map<String, String> toHeadersMap(List<HttpHeader> headers) {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
for (HttpHeader header: headers) {
|
||||
map.put(header.name.toLowerCase(), header.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.nio.file.Paths;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
class VideoImpl extends LoggingSupport implements Video {
|
||||
class VideoImpl implements Video {
|
||||
private final PageImpl page;
|
||||
private final WaitableResult<ArtifactImpl> waitableArtifact = new WaitableResult<>();
|
||||
private final boolean isRemote;
|
||||
@@ -47,7 +47,7 @@ class VideoImpl extends LoggingSupport implements Video {
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
withLogging("Video.delete", () -> {
|
||||
page.withLogging("Video.delete", () -> {
|
||||
try {
|
||||
waitForArtifact().delete();
|
||||
} catch (PlaywrightException e) {
|
||||
@@ -57,7 +57,7 @@ class VideoImpl extends LoggingSupport implements Video {
|
||||
|
||||
@Override
|
||||
public Path path() {
|
||||
return withLogging("Video.path", () -> {
|
||||
return page.withLogging("Video.path", () -> {
|
||||
if (isRemote) {
|
||||
throw new PlaywrightException("Path is not available when using browserType.connect(). Use saveAs() to save a local copy.");
|
||||
}
|
||||
@@ -71,7 +71,7 @@ class VideoImpl extends LoggingSupport implements Video {
|
||||
|
||||
@Override
|
||||
public void saveAs(Path path) {
|
||||
withLogging("Video.saveAs", () -> {
|
||||
page.withLogging("Video.saveAs", () -> {
|
||||
try {
|
||||
waitForArtifact().saveAs(path);
|
||||
} catch (PlaywrightException e) {
|
||||
|
||||
@@ -53,10 +53,10 @@ public class WaitForEventLogger<T> implements Supplier<T> {
|
||||
}
|
||||
|
||||
private void sendWaitForEventInfo(JsonObject info) {
|
||||
info.addProperty("apiName", apiName);
|
||||
info.addProperty("event", "");
|
||||
info.addProperty("waitId", waitId);
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("info", info);
|
||||
channel.sendMessageAsync("waitForEventInfo", params);
|
||||
channel.withLogging(apiName, () -> channel.sendMessageAsync("waitForEventInfo", params));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
class WebSocketImpl extends ChannelOwner implements WebSocket {
|
||||
private final ListenerCollection<EventType> listeners = new ListenerCollection<>();
|
||||
@@ -93,7 +94,7 @@ class WebSocketImpl extends ChannelOwner implements WebSocket {
|
||||
if (options == null) {
|
||||
options = new WaitForFrameReceivedOptions();
|
||||
}
|
||||
return waitForEventWithTimeout(EventType.FRAMERECEIVED, code, options.timeout);
|
||||
return waitForEventWithTimeout(EventType.FRAMERECEIVED, code, options.predicate, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,7 +106,7 @@ class WebSocketImpl extends ChannelOwner implements WebSocket {
|
||||
if (options == null) {
|
||||
options = new WaitForFrameSentOptions();
|
||||
}
|
||||
return waitForEventWithTimeout(EventType.FRAMESENT, code, options.timeout);
|
||||
return waitForEventWithTimeout(EventType.FRAMESENT, code, options.predicate, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -140,9 +141,9 @@ class WebSocketImpl extends ChannelOwner implements WebSocket {
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T waitForEventWithTimeout(EventType eventType, Runnable code, Double timeout) {
|
||||
List<Waitable<T>> waitables = new ArrayList<>();
|
||||
waitables.add(new WaitableEvent<>(listeners, eventType));
|
||||
private WebSocketFrame waitForEventWithTimeout(EventType eventType, Runnable code, Predicate<WebSocketFrame> predicate, Double timeout) {
|
||||
List<Waitable<WebSocketFrame>> waitables = new ArrayList<>();
|
||||
waitables.add(new WaitableEvent<>(listeners, eventType, predicate));
|
||||
waitables.add(new WaitableWebSocketClose<>());
|
||||
waitables.add(new WaitableWebSocketError<>());
|
||||
waitables.add(page.createWaitForCloseHelper());
|
||||
@@ -176,14 +177,22 @@ class WebSocketImpl extends ChannelOwner implements WebSocket {
|
||||
void handleEvent(String event, JsonObject parameters) {
|
||||
switch (event) {
|
||||
case "frameSent": {
|
||||
int opCode = parameters.get("opcode").getAsInt();
|
||||
if (opCode != 1 && opCode != 2) {
|
||||
break;
|
||||
}
|
||||
WebSocketFrameImpl WebSocketFrame = new WebSocketFrameImpl(
|
||||
parameters.get("data").getAsString(), parameters.get("opcode").getAsInt() == 2);
|
||||
parameters.get("data").getAsString(), opCode == 2);
|
||||
listeners.notify(EventType.FRAMESENT, WebSocketFrame);
|
||||
break;
|
||||
}
|
||||
case "frameReceived": {
|
||||
int opCode = parameters.get("opcode").getAsInt();
|
||||
if (opCode != 1 && opCode != 2) {
|
||||
break;
|
||||
}
|
||||
WebSocketFrameImpl WebSocketFrame = new WebSocketFrameImpl(
|
||||
parameters.get("data").getAsString(), parameters.get("opcode").getAsInt() == 2);
|
||||
parameters.get("data").getAsString(), opCode == 2);
|
||||
listeners.notify(EventType.FRAMERECEIVED, WebSocketFrame);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -52,30 +52,51 @@ public class Cookie {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
/**
|
||||
* either url or domain / path are required. Optional.
|
||||
*/
|
||||
public Cookie setUrl(String url) {
|
||||
this.url = url;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* either url or domain / path are required Optional.
|
||||
*/
|
||||
public Cookie setDomain(String domain) {
|
||||
this.domain = domain;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* either url or domain / path are required Optional.
|
||||
*/
|
||||
public Cookie setPath(String path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Unix time in seconds. Optional.
|
||||
*/
|
||||
public Cookie setExpires(double expires) {
|
||||
this.expires = expires;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Optional.
|
||||
*/
|
||||
public Cookie setHttpOnly(boolean httpOnly) {
|
||||
this.httpOnly = httpOnly;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Optional.
|
||||
*/
|
||||
public Cookie setSecure(boolean secure) {
|
||||
this.secure = secure;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Optional.
|
||||
*/
|
||||
public Cookie setSameSite(SameSiteAttribute sameSite) {
|
||||
this.sameSite = sameSite;
|
||||
return this;
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public enum ForcedColors {
|
||||
ACTIVE,
|
||||
NONE
|
||||
}
|
||||
@@ -34,6 +34,9 @@ public class Geolocation {
|
||||
this.latitude = latitude;
|
||||
this.longitude = longitude;
|
||||
}
|
||||
/**
|
||||
* Non-negative accuracy value. Defaults to {@code 0}.
|
||||
*/
|
||||
public Geolocation setAccuracy(double accuracy) {
|
||||
this.accuracy = accuracy;
|
||||
return this;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public class HttpHeader {
|
||||
/**
|
||||
* Name of the header.
|
||||
*/
|
||||
public String name;
|
||||
/**
|
||||
* Value of the header.
|
||||
*/
|
||||
public String value;
|
||||
|
||||
}
|
||||
@@ -34,18 +34,30 @@ public class Margin {
|
||||
*/
|
||||
public String left;
|
||||
|
||||
/**
|
||||
* Top margin, accepts values labeled with units. Defaults to {@code 0}.
|
||||
*/
|
||||
public Margin setTop(String top) {
|
||||
this.top = top;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Right margin, accepts values labeled with units. Defaults to {@code 0}.
|
||||
*/
|
||||
public Margin setRight(String right) {
|
||||
this.right = right;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Bottom margin, accepts values labeled with units. Defaults to {@code 0}.
|
||||
*/
|
||||
public Margin setBottom(String bottom) {
|
||||
this.bottom = bottom;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Left margin, accepts values labeled with units. Defaults to {@code 0}.
|
||||
*/
|
||||
public Margin setLeft(String left) {
|
||||
this.left = left;
|
||||
return this;
|
||||
|
||||
@@ -38,14 +38,23 @@ public class Proxy {
|
||||
public Proxy(String server) {
|
||||
this.server = server;
|
||||
}
|
||||
/**
|
||||
* Optional coma-separated domains to bypass proxy, for example {@code ".com, chromium.org, .domain.com"}.
|
||||
*/
|
||||
public Proxy setBypass(String bypass) {
|
||||
this.bypass = bypass;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Optional username to use if HTTP proxy requires authentication.
|
||||
*/
|
||||
public Proxy setUsername(String username) {
|
||||
this.username = username;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Optional password to use if HTTP proxy requires authentication.
|
||||
*/
|
||||
public Proxy setPassword(String password) {
|
||||
this.password = password;
|
||||
return this;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public class SecurityDetails {
|
||||
/**
|
||||
* Common Name component of the Issuer field. from the certificate. This should only be used for informational purposes.
|
||||
* Optional.
|
||||
*/
|
||||
public String issuer;
|
||||
/**
|
||||
* The specific TLS protocol used. (e.g. {@code TLS 1.3}). Optional.
|
||||
*/
|
||||
public String protocol;
|
||||
/**
|
||||
* Common Name component of the Subject field from the certificate. This should only be used for informational purposes.
|
||||
* Optional.
|
||||
*/
|
||||
public String subjectName;
|
||||
/**
|
||||
* Unix timestamp (in seconds) specifying when this cert becomes valid. Optional.
|
||||
*/
|
||||
public Double validFrom;
|
||||
/**
|
||||
* Unix timestamp (in seconds) specifying when this cert becomes invalid. Optional.
|
||||
*/
|
||||
public Double validTo;
|
||||
|
||||
}
|
||||
@@ -30,14 +30,23 @@ public class SelectOption {
|
||||
*/
|
||||
public Integer index;
|
||||
|
||||
/**
|
||||
* Matches by {@code option.value}. Optional.
|
||||
*/
|
||||
public SelectOption setValue(String value) {
|
||||
this.value = value;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Matches by {@code option.label}. Optional.
|
||||
*/
|
||||
public SelectOption setLabel(String label) {
|
||||
this.label = label;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Matches by the index. Optional.
|
||||
*/
|
||||
public SelectOption setIndex(int index) {
|
||||
this.index = index;
|
||||
return this;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public class ServerAddr {
|
||||
/**
|
||||
* IPv4 or IPV6 address of the server.
|
||||
*/
|
||||
public String ipAddress;
|
||||
public int port;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public class Sizes {
|
||||
/**
|
||||
* Size of the request body (POST data payload) in bytes. Set to 0 if there was no body.
|
||||
*/
|
||||
public int requestBodySize;
|
||||
/**
|
||||
* Total number of bytes from the start of the HTTP request message until (and including) the double CRLF before the body.
|
||||
*/
|
||||
public int requestHeadersSize;
|
||||
/**
|
||||
* Size of the received response body (encoded) in bytes.
|
||||
*/
|
||||
public int responseBodySize;
|
||||
/**
|
||||
* Total number of bytes from the start of the HTTP response message until (and including) the double CRLF before the body.
|
||||
*/
|
||||
public int responseHeadersSize;
|
||||
|
||||
}
|
||||
@@ -189,6 +189,9 @@ public class Server implements HttpHandler {
|
||||
if (csp.containsKey(path)) {
|
||||
exchange.getResponseHeaders().add("Content-Security-Policy", csp.get(path));
|
||||
}
|
||||
if ("/".equals(path)) {
|
||||
path = "/index.html";
|
||||
}
|
||||
File file = new File(resourcesDir, path.substring(1));
|
||||
if (!file.exists()) {
|
||||
exchange.sendResponseHeaders(404, 0);
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import com.microsoft.playwright.options.BrowserChannel;
|
||||
import org.junit.jupiter.api.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.Test;
|
||||
|
||||
public class TestBeforeunload extends TestBase {
|
||||
@Test
|
||||
void shouldBeAbleToNavigateAwayFromPageWithBeforeunload() {
|
||||
page.navigate(server.PREFIX + "/beforeunload.html");
|
||||
// We have to interact with a page so that "beforeunload" handlers
|
||||
// fire.
|
||||
page.click("body");
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import static com.microsoft.playwright.Utils.assertJsonEquals;
|
||||
import static com.microsoft.playwright.Utils.getOS;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singletonList;
|
||||
@@ -217,7 +218,7 @@ public class TestBrowserContextAddCookies extends TestBase {
|
||||
" expires: -1,\n" +
|
||||
" httpOnly: false,\n" +
|
||||
" secure: false,\n" +
|
||||
" sameSite: 'NONE'\n" +
|
||||
" sameSite: '" + (isChromium() ? "LAX" : "NONE") +"'\n" +
|
||||
"}]", cookies);
|
||||
}
|
||||
|
||||
@@ -236,7 +237,7 @@ public class TestBrowserContextAddCookies extends TestBase {
|
||||
" expires: -1,\n" +
|
||||
" httpOnly: false,\n" +
|
||||
" secure: false,\n" +
|
||||
" sameSite: 'NONE'\n" +
|
||||
" sameSite: '" + (isChromium() ? "LAX" : "NONE") +"'\n" +
|
||||
"}]", cookies);
|
||||
assertEquals("gridcookie=GRID", page.evaluate("document.cookie"));
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
@@ -309,7 +310,7 @@ public class TestBrowserContextAddCookies extends TestBase {
|
||||
" expires: -1,\n" +
|
||||
" httpOnly: false,\n" +
|
||||
" secure: true,\n" +
|
||||
" sameSite: 'NONE'\n" +
|
||||
" sameSite: '" + (isChromium() ? "LAX" : "NONE") +"'\n" +
|
||||
"}]", context.cookies("https://www.example.com"));
|
||||
}
|
||||
|
||||
@@ -345,7 +346,7 @@ public class TestBrowserContextAddCookies extends TestBase {
|
||||
"}", server.CROSS_PROCESS_PREFIX + "/grid.html");
|
||||
page.frames().get(1).evaluate("document.cookie = 'username=John Doe'");
|
||||
page.waitForTimeout(2000);
|
||||
boolean allowsThirdParty = isChromium() || isFirefox();
|
||||
boolean allowsThirdParty = isFirefox();
|
||||
List<Cookie> cookies = context.cookies(server.CROSS_PROCESS_PREFIX + "/grid.html");
|
||||
if (allowsThirdParty) {
|
||||
assertJsonEquals("[{\n" +
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
public class TestBrowserContextBaseUrl extends TestBase {
|
||||
@Test
|
||||
void shouldConstructANewURLWhenABaseURLInBrowserNewContextIsPassedToPageGoto() throws MalformedURLException {
|
||||
try (BrowserContext context = browser.newContext(new Browser.NewContextOptions().setBaseURL(server.PREFIX))) {
|
||||
Page page = context.newPage();
|
||||
assertEquals(server.EMPTY_PAGE, page.navigate("/empty.html").url());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldConstructANewURLWhenABaseURLInBrowserNewPageIsPassedToPageGoto() {
|
||||
try (Page page = browser.newPage(new Browser.NewPageOptions().setBaseURL(server.PREFIX))) {
|
||||
assertEquals(server.EMPTY_PAGE, page.navigate("/empty.html").url());
|
||||
}
|
||||
}
|
||||
@Test
|
||||
void shouldConstructTheURLsCorrectlyWhenABaseURLWithoutATrailingSlashInBrowserNewPageIsPassedToPageGoto() {
|
||||
try (Page page = browser.newPage(new Browser.NewPageOptions().setBaseURL(server.PREFIX + "/url-construction"))) {
|
||||
assertEquals(server.PREFIX + "/mypage.html", page.navigate("mypage.html").url());
|
||||
assertEquals(server.PREFIX + "/mypage.html", page.navigate("./mypage.html").url());
|
||||
assertEquals(server.PREFIX + "/mypage.html", page.navigate("/mypage.html").url());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldConstructTheURLsCorrectlyWhenABaseURLWithATrailingSlashInBrowserNewPageIsPassedToPageGoto() {
|
||||
try (Page page = browser.newPage(new Browser.NewPageOptions().setBaseURL(server.PREFIX + "/url-construction/"))) {
|
||||
assertEquals(server.PREFIX + "/url-construction/mypage.html", page.navigate("mypage.html").url());
|
||||
assertEquals(server.PREFIX + "/url-construction/mypage.html", page.navigate("./mypage.html").url());
|
||||
assertEquals(server.PREFIX + "/mypage.html", page.navigate("/mypage.html").url());
|
||||
assertEquals(server.PREFIX + "/url-construction/", page.navigate(".").url());
|
||||
assertEquals(server.PREFIX + "/", page.navigate("/").url());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotConstructANewURLWhenValidURLsArePassed() {
|
||||
try (Page page = browser.newPage(new Browser.NewPageOptions().setBaseURL("http://microsoft.com"))) {
|
||||
assertEquals(server.EMPTY_PAGE, page.navigate(server.EMPTY_PAGE).url());
|
||||
|
||||
page.navigate("data:text/html,Hello world");
|
||||
assertEquals("data:text/html,Hello world", page.evaluate("window.location.href"));
|
||||
|
||||
page.navigate("about:blank");
|
||||
assertEquals("about:blank", page.evaluate("window.location.href"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBeAbleToMatchAURLRelativeToItsGivenURLWithUrlMatcher() {
|
||||
try (Page page = browser.newPage(new Browser.NewPageOptions().setBaseURL(server.PREFIX + "/foobar/"))) {
|
||||
page.navigate("/kek/index.html");
|
||||
page.waitForURL("/kek/index.html");
|
||||
assertEquals(server.PREFIX + "/kek/index.html", page.url());
|
||||
|
||||
page.route("./kek/index.html", route -> route.fulfill(new Route.FulfillOptions().setBody("base-url-matched-route")));
|
||||
Request[] request = {null};
|
||||
Response response = page.waitForResponse("./kek/index.html", () -> {
|
||||
request[0] = page.waitForRequest("./kek/index.html", () -> {
|
||||
page.navigate("./kek/index.html");
|
||||
});
|
||||
});
|
||||
assertNotNull(request[0]);
|
||||
assertNotNull(response);
|
||||
assertEquals(server.PREFIX + "/foobar/kek/index.html", request[0].url());
|
||||
assertEquals(server.PREFIX + "/foobar/kek/index.html", response.url());
|
||||
assertEquals("base-url-matched-route", response.text());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,10 +115,9 @@ public class TestBrowserContextBasic extends TestBase {
|
||||
|
||||
|
||||
@Test
|
||||
@Disabled("TODO: supported null viewport option")
|
||||
void shouldNotAllowDeviceScaleFactorWithNullViewport() {
|
||||
try {
|
||||
browser.newContext(new Browser.NewContextOptions().setDeviceScaleFactor(1.0));
|
||||
browser.newContext(new Browser.NewContextOptions().setDeviceScaleFactor(1.0).setViewportSize(null));
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("\"deviceScaleFactor\" option is not supported with null \"viewport\""));
|
||||
@@ -126,10 +125,9 @@ public class TestBrowserContextBasic extends TestBase {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("TODO: supported null viewport option")
|
||||
void shouldNotAllowIsMobileWithNullViewport() {
|
||||
try {
|
||||
browser.newContext(new Browser.NewContextOptions().setIsMobile(true));
|
||||
browser.newContext(new Browser.NewContextOptions().setIsMobile(true).setViewportSize(null));
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("\"isMobile\" option is not supported with null \"viewport\""));
|
||||
|
||||
@@ -50,7 +50,7 @@ public class TestBrowserContextCookies extends TestBase {
|
||||
" expires: -1,\n" +
|
||||
" httpOnly: false,\n" +
|
||||
" secure: false,\n" +
|
||||
" sameSite: 'NONE'\n" +
|
||||
" sameSite: '" + (isChromium() ? "LAX" : "NONE") +"'\n" +
|
||||
" }]", cookies);
|
||||
}
|
||||
|
||||
@@ -74,7 +74,11 @@ public class TestBrowserContextCookies extends TestBase {
|
||||
assertEquals(timestamp, cookie.expires);
|
||||
assertEquals(false, cookie.httpOnly);
|
||||
assertEquals(false, cookie.secure);
|
||||
assertEquals(SameSiteAttribute.NONE, cookie.sameSite);
|
||||
if (isChromium()) {
|
||||
assertEquals(SameSiteAttribute.LAX, cookie.sameSite);
|
||||
} else {
|
||||
assertEquals(SameSiteAttribute.NONE, cookie.sameSite);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -142,7 +146,7 @@ public class TestBrowserContextCookies extends TestBase {
|
||||
" expires: -1,\n" +
|
||||
" httpOnly: false,\n" +
|
||||
" secure: false,\n" +
|
||||
" sameSite: 'NONE'\n" +
|
||||
" sameSite: '" + (isChromium() ? "LAX" : "NONE") +"'\n" +
|
||||
" },\n" +
|
||||
" {\n" +
|
||||
" name: 'username',\n" +
|
||||
@@ -152,7 +156,7 @@ public class TestBrowserContextCookies extends TestBase {
|
||||
" expires: -1,\n" +
|
||||
" httpOnly: false,\n" +
|
||||
" secure: false,\n" +
|
||||
" sameSite: 'NONE'\n" +
|
||||
" sameSite: '" + (isChromium() ? "LAX" : "NONE") +"'\n" +
|
||||
" }\n" +
|
||||
"]", cookies);
|
||||
}
|
||||
@@ -170,19 +174,19 @@ public class TestBrowserContextCookies extends TestBase {
|
||||
" value: 'tweets',\n" +
|
||||
" domain: 'baz.com',\n" +
|
||||
" path: '/',\n" +
|
||||
" expires: -1,\n" +
|
||||
" expires: -1.0,\n" +
|
||||
" httpOnly: false,\n" +
|
||||
" secure: true,\n" +
|
||||
" sameSite: 'NONE'\n" +
|
||||
" sameSite: '" + (isChromium() ? "LAX" : "NONE") +"'\n" +
|
||||
"}, {\n" +
|
||||
" name: 'doggo',\n" +
|
||||
" value: 'woofs',\n" +
|
||||
" domain: 'foo.com',\n" +
|
||||
" path: '/',\n" +
|
||||
" expires: -1,\n" +
|
||||
" expires: -1.0,\n" +
|
||||
" httpOnly: false,\n" +
|
||||
" secure: true,\n" +
|
||||
" sameSite: 'NONE'\n" +
|
||||
" sameSite: '" + (isChromium() ? "LAX" : "NONE") +"'\n" +
|
||||
"}]", cookies);
|
||||
}
|
||||
|
||||
@@ -200,9 +204,17 @@ public class TestBrowserContextCookies extends TestBase {
|
||||
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
Object documentCookie = page.evaluate("document.cookie.split('; ').sort().join('; ')");
|
||||
assertEquals("one=uno; three=tres; two=dos", documentCookie);
|
||||
if (isChromium()) {
|
||||
assertEquals("one=uno; two=dos", documentCookie);
|
||||
} else {
|
||||
assertEquals("one=uno; three=tres; two=dos", documentCookie);
|
||||
}
|
||||
|
||||
List<SameSiteAttribute> list = context.cookies().stream().map(c -> c.sameSite).sorted().collect(Collectors.toList());
|
||||
assertEquals(asList( SameSiteAttribute.STRICT, SameSiteAttribute.LAX, SameSiteAttribute.NONE), list);
|
||||
if (isChromium()) {
|
||||
assertEquals(asList(SameSiteAttribute.STRICT, SameSiteAttribute.LAX), list);
|
||||
} else {
|
||||
assertEquals(asList(SameSiteAttribute.STRICT, SameSiteAttribute.LAX, SameSiteAttribute.NONE), list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,11 +57,10 @@ public class TestBrowserContextRoute extends TestBase {
|
||||
Page page = context.newPage();
|
||||
|
||||
List<Integer> intercepted = new ArrayList<>();
|
||||
Consumer<Route> handler1 = route -> {
|
||||
context.route("**/*", route -> {
|
||||
intercepted.add(1);
|
||||
route.resume();
|
||||
};
|
||||
context.route("**/empty.html", handler1);
|
||||
});
|
||||
context.route("**/empty.html", route -> {
|
||||
intercepted.add(2);
|
||||
route.resume();
|
||||
@@ -70,22 +69,23 @@ public class TestBrowserContextRoute extends TestBase {
|
||||
intercepted.add(3);
|
||||
route.resume();
|
||||
});
|
||||
context.route("**/*", route -> {
|
||||
Consumer<Route> handler4 = route -> {
|
||||
intercepted.add(4);
|
||||
route.resume();
|
||||
});
|
||||
};
|
||||
context.route("**/empty.html", handler4);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
assertEquals(asList(1), intercepted);
|
||||
assertEquals(asList(4), intercepted);
|
||||
|
||||
intercepted.clear();
|
||||
context.unroute("**/empty.html", handler1);
|
||||
context.unroute("**/empty.html", handler4);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
assertEquals(asList(2), intercepted);
|
||||
assertEquals(asList(3), intercepted);
|
||||
|
||||
intercepted.clear();
|
||||
context.unroute("**/empty.html");
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
assertEquals(asList(4), intercepted);
|
||||
assertEquals(asList(1), intercepted);
|
||||
|
||||
context.close();
|
||||
}
|
||||
@@ -121,4 +121,17 @@ public class TestBrowserContextRoute extends TestBase {
|
||||
assertEquals("context", response.text());
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSupportTheTimesParameterWithRouteMatching() {
|
||||
int[] intercepted = {0};
|
||||
context.route("**/empty.html", route -> {
|
||||
++intercepted[0];
|
||||
route.resume();
|
||||
}, new BrowserContext.RouteOptions().setTimes(2));
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
assertEquals(2, intercepted[0]);
|
||||
}
|
||||
}
|
||||
|
||||
+14
-14
@@ -100,24 +100,24 @@ public class TestBrowserContextStorageState extends TestBase {
|
||||
context.storageState(new BrowserContext.StorageStateOptions().setPath(path));
|
||||
JsonObject expected = new Gson().fromJson(
|
||||
"{\n" +
|
||||
" \"cookies\":[\n" +
|
||||
" 'cookies':[\n" +
|
||||
" { \n" +
|
||||
" \"name\":\"username\",\n" +
|
||||
" \"value\":\"John Doe\",\n" +
|
||||
" \"domain\":\"www.example.com\",\n" +
|
||||
" \"path\":\"/\",\n" +
|
||||
" \"expires\":-1,\n" +
|
||||
" \"httpOnly\":false,\n" +
|
||||
" \"secure\":false,\n" +
|
||||
" \"sameSite\":\"None\"\n" +
|
||||
" 'name':'username',\n" +
|
||||
" 'value':'John Doe',\n" +
|
||||
" 'domain':'www.example.com',\n" +
|
||||
" 'path':'/',\n" +
|
||||
" 'expires':-1,\n" +
|
||||
" 'httpOnly':false,\n" +
|
||||
" 'secure':false,\n" +
|
||||
" 'sameSite':'" + (isChromium() ? "Lax" : "None") + "'\n" +
|
||||
" }],\n" +
|
||||
" \"origins\":[\n" +
|
||||
" 'origins':[\n" +
|
||||
" {\n" +
|
||||
" \"origin\":\"https://www.example.com\",\n" +
|
||||
" \"localStorage\":[\n" +
|
||||
" 'origin':'https://www.example.com',\n" +
|
||||
" 'localStorage':[\n" +
|
||||
" {\n" +
|
||||
" \"name\":\"name1\",\n" +
|
||||
" \"value\":\"value1\"\n" +
|
||||
" 'name':'name1',\n" +
|
||||
" 'value':'value1'\n" +
|
||||
" }]\n" +
|
||||
" }]\n" +
|
||||
"}\n", JsonObject.class);
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestBrowserContextStrict extends TestBase {
|
||||
@Override
|
||||
BrowserContext createContext() {
|
||||
return browser.newContext(new Browser.NewContextOptions().setStrictSelectors(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotFailPageTextContentInNonStrictMode() {
|
||||
try (BrowserContext context = browser.newContext()) {
|
||||
Page page = context.newPage();
|
||||
page.setContent("<span>span1</span><div><span>target</span></div>");
|
||||
assertEquals("span1", page.textContent("span"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFailPageTextContentInStrictMode() {
|
||||
page.setContent("<span>span1</span><div><span>target</span></div>");
|
||||
try {
|
||||
page.textContent("span");
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("strict mode violation"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldOptOutOfStrictMode() {
|
||||
page.setContent("<span>span1</span><div><span>target</span></div>");
|
||||
assertEquals("span1", page.textContent("span", new Page.TextContentOptions().setStrict(false)));
|
||||
}
|
||||
}
|
||||
@@ -31,11 +31,10 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import static com.microsoft.playwright.Utils.mapOf;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestBrowserTypeConnect extends TestBase {
|
||||
@@ -60,7 +59,7 @@ public class TestBrowserTypeConnect extends TestBase {
|
||||
|
||||
private static BrowserServer launchBrowserServer(BrowserType browserType) {
|
||||
try {
|
||||
Path driver = Driver.ensureDriverInstalled();
|
||||
Path driver = Driver.ensureDriverInstalled(Collections.emptyMap());
|
||||
Path dir = driver.getParent();
|
||||
String node = dir.resolve(isWindows ? "node.exe" : "node").toString();
|
||||
String cliJs = dir.resolve("package/lib/cli/cli.js").toString();
|
||||
@@ -458,4 +457,24 @@ public class TestBrowserTypeConnect extends TestBase {
|
||||
}
|
||||
page.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSupportTracingOverWebSocket(@TempDir Path tempDir) throws IOException {
|
||||
List<BrowserContext> contexts = browser.contexts();
|
||||
assertEquals(1, contexts.size());
|
||||
BrowserContext context = contexts.get(0);
|
||||
|
||||
Page page = context.newPage();
|
||||
context.tracing().start(new Tracing.StartOptions().setName("test")
|
||||
.setScreenshots(true).setSnapshots(true));
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
page.setContent("<button>Click</button>");
|
||||
page.click("'Click'");
|
||||
page.close();
|
||||
Path traceFile = tempDir.resolve("trace.zip");
|
||||
context.tracing().stop(new Tracing.StopOptions().setPath(traceFile));
|
||||
|
||||
assertTrue(Files.exists(traceFile));
|
||||
assertTrue(Files.size(traceFile) > 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
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.io.InputStream;
|
||||
@@ -27,12 +28,13 @@ import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static com.microsoft.playwright.Utils.mapOf;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@EnabledIf(value="com.microsoft.playwright.TestBase#isChromium", disabledReason="Chromium-specific API")
|
||||
public class TestChromium extends TestBase {
|
||||
@@ -41,7 +43,7 @@ public class TestChromium extends TestBase {
|
||||
// Do not create anything.
|
||||
}
|
||||
|
||||
private static int nextPort = 9339;
|
||||
private int nextPort = 9339;
|
||||
|
||||
private static String wsEndpointFromUrl(String urlString) throws IOException {
|
||||
URL url = new URL(urlString);
|
||||
@@ -122,4 +124,30 @@ public class TestChromium extends TestBase {
|
||||
assertEquals("bar", webSocketServer.lastClientHandshake.getFieldValue("foo"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSupportTracingOverCDP(@TempDir Path tempDir) throws IOException {
|
||||
int port = nextPort++;
|
||||
try (Browser browserServer = browserType.launch(createLaunchOptions()
|
||||
.setArgs(asList("--remote-debugging-port=" + port)))) {
|
||||
try (Browser cdpBrowser = browserType.connectOverCDP("http://localhost:" + port)) {
|
||||
List<BrowserContext> contexts = cdpBrowser.contexts();
|
||||
assertEquals(1, contexts.size());
|
||||
BrowserContext context = contexts.get(0);
|
||||
|
||||
Page page = context.newPage();
|
||||
context.tracing().start(new Tracing.StartOptions().setName("test")
|
||||
.setScreenshots(true).setSnapshots(true));
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
page.setContent("<button>Click</button>");
|
||||
page.click("'Click'");
|
||||
page.close();
|
||||
Path traceFile = tempDir.resolve("trace.zip");
|
||||
context.tracing().stop(new Tracing.StopOptions().setPath(traceFile));
|
||||
|
||||
assertTrue(Files.exists(traceFile));
|
||||
assertTrue(Files.size(traceFile) > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
|
||||
import static com.microsoft.playwright.options.KeyboardModifier.ALT;
|
||||
import static com.microsoft.playwright.Utils.copy;
|
||||
@@ -58,6 +59,54 @@ public class TestDownload extends TestBase {
|
||||
writer.write("Hello world");
|
||||
}
|
||||
});
|
||||
|
||||
server.setRoute("/downloadWithDelay", exchange -> {
|
||||
exchange.getResponseHeaders().add("Content-Type", "application/octet-stream");
|
||||
exchange.getResponseHeaders().add("Content-Disposition", "attachment; filename=file.txt");
|
||||
exchange.sendResponseHeaders(200, 0);
|
||||
// Chromium requires a large enough payload to trigger the download event soon enough
|
||||
OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody());
|
||||
writer.write(String.join("", Collections.nCopies(4096, "a")));
|
||||
writer.write("foo");
|
||||
writer.flush();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReportDownloadWhenNavigationTurnsIntoDownload() throws IOException {
|
||||
Page page = browser.newPage(new Browser.NewPageOptions().setAcceptDownloads(true));
|
||||
|
||||
Response[] response = new Response[]{null};
|
||||
PlaywrightException[] error = new PlaywrightException[]{null};
|
||||
Download download = page.waitForDownload(() -> {
|
||||
try {
|
||||
response[0] = page.navigate(server.PREFIX + "/download");
|
||||
} catch (PlaywrightException e) {
|
||||
error[0] = e;
|
||||
}
|
||||
});
|
||||
|
||||
assertEquals(page, download.page());
|
||||
assertEquals(server.PREFIX + "/download", download.url());
|
||||
Path path = download.path();
|
||||
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(response[0]);
|
||||
assertEquals(200, response[0].status());
|
||||
assertEquals(server.PREFIX + "/download", page.url());
|
||||
}
|
||||
page.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -354,6 +403,19 @@ public class TestDownload extends TestBase {
|
||||
page.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void streamShouldSupportZeroSizeRead() throws IOException {
|
||||
Page page = browser.newPage(new Browser.NewPageOptions().setAcceptDownloads(true));
|
||||
page.setContent("<a href='" + server.PREFIX + "/download'>download</a>");
|
||||
Download download = page.waitForDownload(() -> page.click("a"));
|
||||
|
||||
InputStream stream = download.createReadStream();
|
||||
byte[] b = new byte[1];
|
||||
int read = stream.read(b, 0, 0);
|
||||
assertEquals(0, read);
|
||||
page.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDeleteDownloadsOnContextDestruction() {
|
||||
Page page = browser.newPage(new Browser.NewPageOptions().setAcceptDownloads(true));
|
||||
@@ -385,4 +447,30 @@ public class TestDownload extends TestBase {
|
||||
assertFalse(Files.exists(path2));
|
||||
assertFalse(Files.exists(path1.resolve("..")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBeAbleToCancelPendingDownloads() {
|
||||
try (Page page = browser.newPage(new Browser.NewPageOptions().setAcceptDownloads(true))) {
|
||||
page.setContent("<a href='" + server.PREFIX + "/downloadWithDelay'>download</a>");
|
||||
Download download = page.waitForDownload(() -> page.click("a"));
|
||||
download.cancel();
|
||||
String failure = download.failure();
|
||||
assertEquals("canceled", failure);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotFailExplicitlyToCancelADownloadEvenIfThatIsAlreadyFinished() throws IOException {
|
||||
try (Page page = browser.newPage(new Browser.NewPageOptions().setAcceptDownloads(true))) {
|
||||
page.setContent("<a href='" + server.PREFIX + "/download'>download</a>");
|
||||
Download download = page.waitForDownload(() -> page.click("a"));
|
||||
|
||||
Path path = download.path();
|
||||
assertTrue(Files.exists(path));
|
||||
byte[] bytes = readAllBytes(path);
|
||||
assertEquals("Hello world", new String(bytes, UTF_8));
|
||||
download.cancel();
|
||||
assertNull(download.failure());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,33 @@ public class TestElementHandleConvenience extends TestBase {
|
||||
assertNull(page.getAttribute("#outer", "foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void inputValueShouldWork() {
|
||||
page.navigate(server.PREFIX + "/dom.html");
|
||||
|
||||
page.fill("#textarea", "text value");
|
||||
assertEquals("text value", page.inputValue("#textarea"));
|
||||
|
||||
page.fill("#input", "input value");
|
||||
assertEquals("input value", page.inputValue("#input"));
|
||||
ElementHandle handle = page.querySelector("#input");
|
||||
assertEquals("input value", handle.inputValue());
|
||||
|
||||
try {
|
||||
page.inputValue("#inner");
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Node is not an HTMLInputElement or HTMLTextAreaElement"), e.getMessage());
|
||||
}
|
||||
ElementHandle handle2 = page.querySelector("#inner");
|
||||
try {
|
||||
handle2.inputValue();
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Node is not an HTMLInputElement or HTMLTextAreaElement"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void innerHTMLShouldWork() {
|
||||
page.navigate(server.PREFIX + "/dom.html");
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class TestElementHandleMisc extends TestBase {
|
||||
@Test
|
||||
void shouldHover() {
|
||||
page.navigate(server.PREFIX + "/input/scrollable.html");
|
||||
ElementHandle button = page.querySelector("#button-6");
|
||||
button.hover();
|
||||
assertEquals("button-6", page.evaluate("document.querySelector('button:hover').id"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHoverWhenNodeIsRemoved() {
|
||||
page.navigate(server.PREFIX + "/input/scrollable.html");
|
||||
page.evaluate("() => delete window['Node']");
|
||||
ElementHandle button = page.querySelector("#button-6");
|
||||
button.hover();
|
||||
assertEquals("button-6", page.evaluate("document.querySelector('button:hover').id"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFillInput() {
|
||||
page.navigate(server.PREFIX + "/input/textarea.html");
|
||||
ElementHandle handle = page.querySelector("input");
|
||||
handle.fill("some value");
|
||||
assertEquals("some value", page.evaluate("window['result']"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFillInputWhenNodeIsRemoved() {
|
||||
page.navigate(server.PREFIX + "/input/textarea.html");
|
||||
page.evaluate("() => delete window['Node']");
|
||||
ElementHandle handle = page.querySelector("input");
|
||||
handle.fill("some value");
|
||||
assertEquals("some value", page.evaluate("window['result']"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCheckTheBox() {
|
||||
page.setContent("<input id='checkbox' type='checkbox'></input>");
|
||||
ElementHandle input = page.querySelector("input");
|
||||
input.check();
|
||||
assertEquals(true, page.evaluate("checkbox.checked"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUncheckTheBox() {
|
||||
page.setContent("<input id='checkbox' type='checkbox' checked></input>");
|
||||
ElementHandle input = page.querySelector("input");
|
||||
input.uncheck();
|
||||
assertEquals(false, page.evaluate("checkbox.checked"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSelectSingleOption() {
|
||||
page.navigate(server.PREFIX + "/input/select.html");
|
||||
ElementHandle select = page.querySelector("select");
|
||||
select.selectOption("blue");
|
||||
assertEquals(asList("blue"), page.evaluate("window['result'].onInput"));
|
||||
assertEquals(asList("blue"), page.evaluate("window['result'].onChange"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFocusAButton() {
|
||||
page.navigate(server.PREFIX + "/input/button.html");
|
||||
ElementHandle button = page.querySelector("button");
|
||||
assertEquals(false, button.evaluate("button => document.activeElement === button"));
|
||||
button.focus();
|
||||
assertEquals(true, button.evaluate("button => document.activeElement === button"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCheckTheBoxUsingSetChecked() {
|
||||
page.setContent("<input id='checkbox' type='checkbox'></input>");
|
||||
ElementHandle input = page.querySelector("input");
|
||||
input.setChecked(true);
|
||||
assertEquals(true, page.evaluate("checkbox.checked"));
|
||||
input.setChecked(false);
|
||||
assertEquals(false, page.evaluate("checkbox.checked"));
|
||||
}
|
||||
}
|
||||
@@ -20,15 +20,20 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.DisabledIf;
|
||||
import org.junit.jupiter.api.condition.EnabledIf;
|
||||
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
|
||||
import static com.microsoft.playwright.Utils.getOS;
|
||||
import static com.microsoft.playwright.options.LoadState.DOMCONTENTLOADED;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@@ -99,7 +104,7 @@ public class TestHar extends TestBase {
|
||||
|
||||
assertEquals(1, log.getAsJsonArray("pages").size());
|
||||
JsonObject pageEntry = log.getAsJsonArray("pages").get(0).getAsJsonObject();
|
||||
assertEquals("page_0", pageEntry.get("id").getAsString());
|
||||
assertNotNull(pageEntry.get("id").getAsString());
|
||||
assertEquals("Hello", pageEntry.get("title").getAsString());
|
||||
// expect(new Date(pageEntry.startedDateTime).valueOf()).toBeGreaterThan(Date.now() - 3600 * 1000);
|
||||
assertTrue(pageEntry.getAsJsonObject("pageTimings").get("onContentLoad").getAsDouble() > 0);
|
||||
@@ -125,7 +130,7 @@ public class TestHar extends TestBase {
|
||||
}
|
||||
assertEquals(1, log.getAsJsonArray("pages").size());
|
||||
JsonObject pageEntry = log.getAsJsonArray("pages").get(0).getAsJsonObject();
|
||||
assertEquals("page_0", pageEntry.get("id").getAsString());
|
||||
assertNotNull(pageEntry.get("id").getAsString());
|
||||
assertEquals("Hello", pageEntry.get("title").getAsString());
|
||||
}
|
||||
|
||||
@@ -135,7 +140,8 @@ public class TestHar extends TestBase {
|
||||
JsonObject log = pageWithHar.log();
|
||||
assertEquals(1, log.getAsJsonArray("entries").size());
|
||||
JsonObject entry = log.getAsJsonArray("entries").get(0).getAsJsonObject();
|
||||
assertEquals("page_0", entry.get("pageref").getAsString());
|
||||
String id = log.getAsJsonArray("pages").get(0).getAsJsonObject().get("id").getAsString();
|
||||
assertEquals(id, entry.get("pageref").getAsString());
|
||||
assertEquals(server.EMPTY_PAGE, entry.getAsJsonObject("request").get("url").getAsString());
|
||||
assertEquals("GET", entry.getAsJsonObject("request").get("method").getAsString());
|
||||
assertEquals("HTTP/1.1", entry.getAsJsonObject("request").get("httpVersion").getAsString());
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestLocatorClick extends TestBase {
|
||||
|
||||
@Test
|
||||
void shouldWork() {
|
||||
page.navigate(server.PREFIX + "/input/button.html");
|
||||
Locator button = page.locator("button");
|
||||
button.click();
|
||||
assertEquals("Clicked", page.evaluate("() => window['result']"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithNodeRemoved() {
|
||||
page.navigate(server.PREFIX + "/input/button.html");
|
||||
page.evaluate("() => delete window['Node']");
|
||||
Locator button = page.locator("button");
|
||||
button.click();
|
||||
assertEquals("Clicked", page.evaluate("() => window['result']"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDoubleClickTheButton() {
|
||||
page.navigate(server.PREFIX + "/input/button.html");
|
||||
page.evaluate("() => {\n" +
|
||||
" window['double'] = false;\n" +
|
||||
" const button = document.querySelector('button');\n" +
|
||||
" button.addEventListener('dblclick', event => {\n" +
|
||||
" window['double'] = true;\n" +
|
||||
" });\n" +
|
||||
"}");
|
||||
Locator button = page.locator("button");
|
||||
button.dblclick();
|
||||
assertEquals(true, page.evaluate("double"));
|
||||
assertEquals("Clicked", page.evaluate("result"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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.Test;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestLocatorConvenience extends TestBase {
|
||||
@Test
|
||||
void shouldHaveANicePreview() {
|
||||
page.navigate(server.PREFIX + "/dom.html");
|
||||
Locator outer = page.locator("#outer");
|
||||
Locator inner = outer.locator("#inner");
|
||||
Locator check = page.locator("#check");
|
||||
JSHandle text = inner.evaluateHandle("e => e.firstChild");
|
||||
page.evaluate("() => 1"); // Give them a chance to calculate the preview.
|
||||
assertEquals("Locator@#outer", outer.toString());
|
||||
assertEquals("Locator@#outer >> #inner", inner.toString());
|
||||
assertEquals("JSHandle@#text=Text,↵more text", text.toString());
|
||||
assertEquals("Locator@#check", check.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAttributeShouldWork() {
|
||||
page.navigate(server.PREFIX + "/dom.html");
|
||||
Locator locator = page.locator("#outer");
|
||||
assertEquals("value", locator.getAttribute("name"));
|
||||
assertNull(locator.getAttribute("foo"));
|
||||
assertEquals("value", page.getAttribute("#outer", "name"));
|
||||
assertNull(page.getAttribute("#outer", "foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void inputValueShouldWork() {
|
||||
page.navigate(server.PREFIX + "/dom.html");
|
||||
|
||||
page.selectOption("#select", "foo");
|
||||
assertEquals("foo", page.inputValue("#select"));
|
||||
|
||||
page.fill("#textarea", "text value");
|
||||
assertEquals("text value", page.inputValue("#textarea"));
|
||||
|
||||
page.fill("#input", "input value");
|
||||
assertEquals("input value", page.inputValue("#input"));
|
||||
Locator locator = page.locator("#input");
|
||||
assertEquals("input value", locator.inputValue());
|
||||
|
||||
try {
|
||||
page.inputValue("#inner");
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Node is not an HTMLInputElement or HTMLTextAreaElement or HTMLSelectElement"));
|
||||
}
|
||||
try {
|
||||
Locator locator2 = page.locator("#inner");
|
||||
locator2.inputValue();
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Node is not an HTMLInputElement or HTMLTextAreaElement or HTMLSelectElement"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void innerHTMLShouldWork() {
|
||||
page.navigate(server.PREFIX + "/dom.html");
|
||||
Locator locator = page.locator("#outer");
|
||||
assertEquals("<div id=\"inner\">Text,\nmore text</div>", locator.innerHTML());
|
||||
assertEquals("<div id=\"inner\">Text,\nmore text</div>", page.innerHTML("#outer"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void innerTextShouldWork() {
|
||||
page.navigate(server.PREFIX + "/dom.html");
|
||||
Locator locator = page.locator("#inner");
|
||||
assertEquals("Text, more text", locator.innerText());
|
||||
assertEquals("Text, more text", page.innerText("#inner"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void innerTextShouldThrow() {
|
||||
page.setContent("<svg>text</svg>");
|
||||
try {
|
||||
page.innerText("svg");
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Not an HTMLElement"));
|
||||
}
|
||||
Locator locator = page.locator("svg");
|
||||
try {
|
||||
locator.innerText();
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Not an HTMLElement"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void textContentShouldWork() {
|
||||
page.navigate(server.PREFIX + "/dom.html");
|
||||
Locator locator = page.locator("#inner");
|
||||
assertEquals("Text,\nmore text", locator.textContent());
|
||||
assertEquals("Text,\nmore text", page.textContent("#inner"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isVisibleAndIsHiddenShouldWork() {
|
||||
page.setContent("<div>Hi</div><span></span>");
|
||||
|
||||
Locator div = page.locator("div");
|
||||
assertTrue(div.isVisible());
|
||||
assertFalse(div.isHidden());
|
||||
assertTrue(page.isVisible("div"));
|
||||
assertFalse(page.isHidden("div"));
|
||||
|
||||
Locator span = page.locator("span");
|
||||
assertFalse(span.isVisible());
|
||||
assertTrue(span.isHidden());
|
||||
assertFalse(page.isVisible("span"));
|
||||
assertTrue(page.isHidden("span"));
|
||||
|
||||
assertFalse(page.isVisible("no-such-element"));
|
||||
assertTrue(page.isHidden("no-such-element"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEnabledAndIsDisabledShouldWork() {
|
||||
page.setContent("<button disabled>button1</button>\n" +
|
||||
"<button>button2</button>\n" +
|
||||
"<div>div</div>");
|
||||
Locator div = page.locator("div");
|
||||
assertTrue(div.isEnabled());
|
||||
assertFalse(div.isDisabled());
|
||||
assertTrue(page.isEnabled("div"));
|
||||
assertEquals(false, page.isDisabled("div"));
|
||||
Locator button1 = page.locator(":text('button1')");
|
||||
assertEquals(false, button1.isEnabled());
|
||||
assertTrue(button1.isDisabled());
|
||||
assertEquals(false, page.isEnabled(":text('button1')"));
|
||||
assertTrue(page.isDisabled(":text('button1')"));
|
||||
Locator button2 = page.locator(":text('button2')");
|
||||
assertTrue(button2.isEnabled());
|
||||
assertEquals(false, button2.isDisabled());
|
||||
assertTrue(page.isEnabled(":text('button2')"));
|
||||
assertEquals(false, page.isDisabled(":text('button2')"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEditableShouldWork() {
|
||||
page.setContent("<input id=input1 disabled><textarea></textarea><input id=input2>");
|
||||
page.evalOnSelector("textarea", "t => t.readOnly = true");
|
||||
Locator input1 = page.locator("#input1");
|
||||
assertFalse(input1.isEditable());
|
||||
assertFalse(page.isEditable("#input1"));
|
||||
Locator input2 = page.locator("#input2");
|
||||
assertTrue(input2.isEditable());
|
||||
assertTrue(page.isEditable("#input2"));
|
||||
Locator textarea = page.locator("textarea");
|
||||
assertFalse(textarea.isEditable());
|
||||
assertFalse(page.isEditable("textarea"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isCheckedShouldWork() {
|
||||
page.setContent("<input type='checkbox' checked><div>Not a checkbox</div>");
|
||||
Locator element = page.locator("input");
|
||||
assertTrue(element.isChecked());
|
||||
assertTrue(page.isChecked("input"));
|
||||
element.evaluate("input => input.checked = false");
|
||||
assertFalse(element.isChecked());
|
||||
assertFalse(page.isChecked("input"));
|
||||
try {
|
||||
page.isChecked("div");
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Not a checkbox or radio button"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void allTextContentsShouldWork() {
|
||||
page.setContent("<div>A</div><div>B</div><div>C</div>");
|
||||
assertEquals(asList("A", "B", "C"), page.locator("div").allTextContents());
|
||||
}
|
||||
|
||||
@Test
|
||||
void allInnerTextsShouldWork() {
|
||||
page.setContent("<div>A</div><div>B</div><div>C</div>");
|
||||
assertEquals(asList("A", "B", "C"), page.locator("div").allInnerTexts());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class TestLocatorElementHandle extends TestBase {
|
||||
@Test
|
||||
void shouldQueryExistingElement() {
|
||||
page.navigate(server.PREFIX + "/playground.html");
|
||||
page.setContent("<html><body><div class='second'><div class='inner'>A</div></div></body></html>");
|
||||
Locator html = page.locator("html");
|
||||
Locator second = html.locator(".second");
|
||||
Locator inner = second.locator(".inner");
|
||||
Object content = page.evaluate("e => e.textContent", inner.elementHandle());
|
||||
assertEquals("A", content);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldQueryExistingElements() {
|
||||
page.setContent("<html><body><div>A</div><br/><div>B</div></body></html>");
|
||||
Locator html = page.locator("html");
|
||||
List<ElementHandle> elements = html.locator("div").elementHandles();
|
||||
assertEquals(2, elements.size());
|
||||
List<Object> texts = elements.stream().map(element -> page.evaluate("e => e.textContent", element))
|
||||
.collect(Collectors.toList());
|
||||
assertEquals(asList("A", "B"), texts);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnEmptyArrayForNonExistingElements() {
|
||||
page.setContent("<html><body><span>A</span><br/><span>B</span></body></html>");
|
||||
Locator html = page.locator("html");
|
||||
List<ElementHandle> elements = html.locator("div").elementHandles();
|
||||
assertEquals(0, elements.size());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void xpathShouldQueryExistingElement() {
|
||||
page.navigate(server.PREFIX + "/playground.html");
|
||||
page.setContent("<html><body><div class='second'><div class='inner'>A</div></div></body></html>");
|
||||
Locator html = page.locator("html");
|
||||
Locator second = html.locator("xpath=./body/div[contains(@class, 'second')]");
|
||||
Locator inner = second.locator("xpath=./div[contains(@class, 'inner')]");
|
||||
Object content = page.evaluate("e => e.textContent", inner.elementHandle());
|
||||
assertEquals("A", content);
|
||||
}
|
||||
|
||||
@Test
|
||||
void xpathShouldReturnNullForNonExistingElement() {
|
||||
page.setContent("<html><body><div class='second'><div class='inner'>B</div></div></body></html>");
|
||||
Locator html = page.locator("html");
|
||||
List<ElementHandle> second = html.locator("xpath=/div[contains(@class, 'third')]").elementHandles();
|
||||
assertEquals(asList(), second);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class TestLocatorEvaluate extends TestBase {
|
||||
@Test
|
||||
void shouldWork() {
|
||||
page.setContent("<html><body><div class='tweet'><div class='like'>100</div><div class='retweets'>10</div></div></body></html>");
|
||||
Locator tweet = page.locator(".tweet .like");
|
||||
Object content = tweet.evaluate("node => node.innerText");
|
||||
assertEquals("100", content);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRetrieveContentFromSubtree() {
|
||||
String htmlContent = "<div class='a'>not-a-child-div</div><div id='myId'><div class='a'>a-child-div</div></div>";
|
||||
page.setContent(htmlContent);
|
||||
Locator locator = page.locator("#myId .a");
|
||||
Object content = locator.evaluate("node => node.innerText");
|
||||
assertEquals("a-child-div", content);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkForAll() {
|
||||
page.setContent("<html><body><div class='tweet'><div class='like'>100</div><div class='like'>10</div></div></body></html>");
|
||||
Locator tweet = page.locator(".tweet .like");
|
||||
Object content = tweet.evaluateAll("nodes => nodes.map(n => n.innerText)");
|
||||
assertEquals(asList("100", "10"), content);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRetrieveContentFromSubtreeForAll() {
|
||||
String htmlContent = "<div class='a'>not-a-child-div</div><div id='myId'><div class='a'>a1-child-div</div><div class='a'>a2-child-div</div></div>";
|
||||
page.setContent(htmlContent);
|
||||
Locator element = page.locator("#myId .a");
|
||||
Object content = element.evaluateAll("nodes => nodes.map(n => n.innerText)");
|
||||
assertEquals(asList("a1-child-div", "a2-child-div"), content);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotThrowInCaseOfMissingSelectorForAll() {
|
||||
String htmlContent = "<div class='a'>not-a-child-div</div><div id='myId'></div>";
|
||||
page.setContent(htmlContent);
|
||||
Locator element = page.locator("#myId .a");
|
||||
Object nodesLength = element.evaluateAll("nodes => nodes.length");
|
||||
assertEquals(0, nodesLength);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class TestLocatorMisc extends TestBase{
|
||||
@Test
|
||||
void shouldCheckTheBoxUsingSetChecked() {
|
||||
page.setContent("<input id='checkbox' type='checkbox'></input>");
|
||||
Locator input = page.locator("input");
|
||||
input.setChecked(true);
|
||||
assertEquals(true, page.evaluate("checkbox.checked"));
|
||||
input.setChecked(false);
|
||||
assertEquals(false, page.evaluate("checkbox.checked"));
|
||||
}
|
||||
}
|
||||
@@ -73,6 +73,18 @@ public class TestNetworkRequest extends TestBase {
|
||||
assertEquals(server.PREFIX + "/empty.html", requests.get(1).url());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkAllHeadersInsideRoute() {
|
||||
List<Request> requests = new ArrayList<>();
|
||||
page.route("**", route -> {
|
||||
assertTrue(route.request().allHeaders().get("accept").length() > 5);
|
||||
requests.add(route.request());
|
||||
route.resume();
|
||||
});
|
||||
page.navigate(server.PREFIX + "/empty.html");
|
||||
assertEquals(1, requests.size());
|
||||
}
|
||||
|
||||
// https://github.com/microsoft/playwright/issues/3993
|
||||
@Test
|
||||
void shouldNotWorkForARedirectAndInterception() {
|
||||
@@ -106,8 +118,12 @@ public class TestNetworkRequest extends TestBase {
|
||||
return (isWebKit() && getOS() == Utils.OS.WINDOWS) || isChromium();
|
||||
}
|
||||
|
||||
static boolean isWebKitWindows() {
|
||||
return isWebKit() && getOS() == Utils.OS.WINDOWS;
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisabledIf(value="isWebKitWindowsOrChromium", disabledReason="Flaky, see https://github.com/microsoft/playwright/issues/6690")
|
||||
@DisabledIf(value="isWebKitWindows", disabledReason="Flaky, see https://github.com/microsoft/playwright/issues/6690")
|
||||
void shouldGetTheSameHeadersAsTheServer() throws ExecutionException, InterruptedException {
|
||||
Future<Server.Request> serverRequest = server.futureRequest("/empty.html");
|
||||
server.setRoute("/empty.html", exchange -> {
|
||||
@@ -119,7 +135,7 @@ public class TestNetworkRequest extends TestBase {
|
||||
Response response = page.navigate(server.PREFIX + "/empty.html");
|
||||
Map<String, String> expectedHeaders = serverRequest.get().headers.entrySet().stream().collect(
|
||||
Collectors.toMap(e -> e.getKey().toLowerCase(), e -> e.getValue().get(0)));
|
||||
assertEquals(expectedHeaders, response.request().headers());
|
||||
assertEquals(expectedHeaders, response.request().allHeaders());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -143,7 +159,7 @@ public class TestNetworkRequest extends TestBase {
|
||||
});
|
||||
Map<String, String> expectedHeaders = serverRequest.get().headers.entrySet().stream().collect(
|
||||
Collectors.toMap(e -> e.getKey().toLowerCase(), e -> e.getValue().get(0)));
|
||||
assertEquals(expectedHeaders, response.request().headers());
|
||||
assertEquals(expectedHeaders, response.request().allHeaders());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import com.microsoft.playwright.options.ServerAddr;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -26,6 +27,7 @@ import java.util.Arrays;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestNetworkResponse extends TestBase {
|
||||
@@ -141,4 +143,13 @@ public class TestNetworkResponse extends TestBase {
|
||||
Response response = page.navigate(server.PREFIX + "/cool");
|
||||
assertEquals("OK", response.statusText());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnServerAddress() {
|
||||
Response response = page.navigate(server.EMPTY_PAGE);
|
||||
ServerAddr address = response.serverAddr();
|
||||
assertNotNull(address);
|
||||
assertEquals(server.PORT, address.port);
|
||||
assertTrue(asList("127.0.0.1", "::1").contains(address.ipAddress), address.ipAddress);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public class TestPageBasic extends TestBase {
|
||||
newPage.evaluate("() => new Promise(r => {})");
|
||||
fail("evaluate should throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Protocol error"));
|
||||
assertTrue(e.getMessage().contains("Target closed"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,4 +291,10 @@ public class TestPageBasic extends TestBase {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void frameDragAndDropShouldWork() {
|
||||
page.navigate(server.PREFIX + "/drag-n-drop.html");
|
||||
page.dragAndDrop("#source", "#target");
|
||||
assertEquals(true, page.evalOnSelector("#target", "target => target.contains(document.querySelector('#source'))"));
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user