1
0
mirror of synced 2026-05-25 04:03:17 +00:00

Compare commits

...

14 Commits

Author SHA1 Message Date
Andrey Lushnikov ea16db9143 chore: mark v1.16.0 (#654) 2021-10-21 08:53:00 -07:00
Yury Semikhatsky 1a4dec86cd chore: remove route handlers with times=0 (#652) 2021-10-20 18:47:52 -07:00
Yury Semikhatsky c802c87e52 chore: roll driver to 1.16.0-next-1634661437000 (#650) 2021-10-19 11:37:44 -07:00
Yury Semikhatsky ab81b542b8 chore: roll driver (#643) 2021-10-12 16:03:11 -07:00
Yury Semikhatsky 07e7cb85c7 chore: roll driver (#642) 2021-10-12 11:25:51 -07:00
Yury Semikhatsky 7af5405d38 feat: roll driver, implement locator.waitFor (#639) 2021-10-05 19:00:19 -07:00
Yury Semikhatsky 50ba2dacbb chore: bump example dependency version (#632) 2021-09-30 08:40:18 -07:00
Yury Semikhatsky 61f5e4dfdd chore: roll driver to 1.16.0-next-1632766475000 (#629) 2021-09-27 13:21:10 -07:00
Andrey Lushnikov 84344c9ff9 docs: put in instructions on how to update package version (#626) 2021-09-23 08:11:51 -07:00
Max Schmitt c0fd575fac chore(set_maven_version): do not generate version backups (#620) 2021-09-21 09:11:18 -07:00
Max Schmitt 81724c7c94 chore: mark 1.16.0-SNAPSHOT (#621) 2021-09-21 15:11:27 +02:00
Yury Semikhatsky 3eb88931bf test: stop using chunked encoding for responses (#617) 2021-09-20 17:13:39 -07:00
Max Schmitt 948dd1515a test: fix sizes test by using no chunked requests (#616) 2021-09-20 13:14:45 -07:00
Yury Semikhatsky dd57d5248d chore: switch to connect implementation in driver (#615) 2021-09-20 13:10:50 -07:00
41 changed files with 498 additions and 306 deletions
+2 -2
View File
@@ -11,8 +11,8 @@ Playwright is a Java library to automate [Chromium](https://www.chromium.org/Hom
| | Linux | macOS | Windows |
| :--- | :---: | :---: | :---: |
| 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 --> | ✅ | ✅ | ✅ |
| Chromium <!-- GEN:chromium-version -->97.0.4666.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->15.4<!-- 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.
+6
View File
@@ -5,3 +5,9 @@
* 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
# Updating Version
```bash
./scripts/set_maven_version.sh 1.15.0
```
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.15.0-SNAPSHOT</version>
<version>1.16.0</version>
</parent>
<artifactId>driver-bundle</artifactId>
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.15.0-SNAPSHOT</version>
<version>1.16.0</version>
</parent>
<artifactId>driver</artifactId>
+2 -2
View File
@@ -6,7 +6,7 @@
<groupId>org.example</groupId>
<artifactId>examples</artifactId>
<version>1.15.0-SNAPSHOT</version>
<version>1.16.0</version>
<name>Playwright Client Examples</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -15,7 +15,7 @@
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<version>1.11.1</version>
<version>1.15.0</version>
</dependency>
</dependencies>
<build>
+1 -1
View File
@@ -7,7 +7,7 @@
<parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.15.0-SNAPSHOT</version>
<version>1.16.0</version>
</parent>
<artifactId>playwright</artifactId>
@@ -86,7 +86,7 @@ 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;
/**
@@ -107,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;
/**
@@ -249,7 +249,7 @@ public interface Browser extends AutoCloseable {
return this;
}
/**
* 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 NewContextOptions setExtraHTTPHeaders(Map<String, String> extraHTTPHeaders) {
this.extraHTTPHeaders = extraHTTPHeaders;
@@ -294,7 +294,7 @@ public interface Browser extends AutoCloseable {
return this;
}
/**
* 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 NewContextOptions setIgnoreHTTPSErrors(boolean ignoreHTTPSErrors) {
this.ignoreHTTPSErrors = ignoreHTTPSErrors;
@@ -510,7 +510,7 @@ 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;
/**
@@ -531,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;
/**
@@ -673,7 +673,7 @@ public interface Browser extends AutoCloseable {
return this;
}
/**
* 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 NewPageOptions setExtraHTTPHeaders(Map<String, String> extraHTTPHeaders) {
this.extraHTTPHeaders = extraHTTPHeaders;
@@ -718,7 +718,7 @@ public interface Browser extends AutoCloseable {
return this;
}
/**
* 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 NewPageOptions setIgnoreHTTPSErrors(boolean ignoreHTTPSErrors) {
this.ignoreHTTPSErrors = ignoreHTTPSErrors;
@@ -29,8 +29,8 @@ import java.util.regex.Pattern;
* <p> If a page opens another page, e.g. with a {@code window.open} call, the popup will belong to the parent page's browser
* context.
*
* <p> Playwright allows creation of "incognito" browser contexts with {@code browser.newContext()} method. "Incognito" browser
* contexts don't write any browsing data to disk.
* <p> Playwright allows creating "incognito" browser contexts with {@link Browser#newContext Browser.newContext()} method.
* "Incognito" browser contexts don't write any browsing data to disk.
* <pre>{@code
* // Create a new incognito browser context
* BrowserContext context = browser.newContext();
@@ -435,7 +435,7 @@ 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;
/**
@@ -485,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;
/**
@@ -692,7 +692,7 @@ public interface BrowserType {
return this;
}
/**
* 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 LaunchPersistentContextOptions setExtraHTTPHeaders(Map<String, String> extraHTTPHeaders) {
this.extraHTTPHeaders = extraHTTPHeaders;
@@ -784,7 +784,7 @@ public interface BrowserType {
return this;
}
/**
* 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 LaunchPersistentContextOptions setIgnoreHTTPSErrors(boolean ignoreHTTPSErrors) {
this.ignoreHTTPSErrors = ignoreHTTPSErrors;
@@ -1079,7 +1079,8 @@ public interface BrowserType {
* @param userDataDir Path to a User Data Directory, which stores browser session data like cookies and local storage. More details for <a
* href="https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md#introduction">Chromium</a> and <a
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile">Firefox</a>. Note that
* Chromium's user data directory is the **parent** directory of the "Profile Path" seen at {@code chrome://version}.
* Chromium's user data directory is the **parent** directory of the "Profile Path" seen at {@code chrome://version}. Pass an
* empty string to use a temporary directory instead.
*/
default BrowserContext launchPersistentContext(Path userDataDir) {
return launchPersistentContext(userDataDir, null);
@@ -1093,7 +1094,8 @@ public interface BrowserType {
* @param userDataDir Path to a User Data Directory, which stores browser session data like cookies and local storage. More details for <a
* href="https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md#introduction">Chromium</a> and <a
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile">Firefox</a>. Note that
* Chromium's user data directory is the **parent** directory of the "Profile Path" seen at {@code chrome://version}.
* Chromium's user data directory is the **parent** directory of the "Profile Path" seen at {@code chrome://version}. Pass an
* empty string to use a temporary directory instead.
*/
BrowserContext launchPersistentContext(Path userDataDir, LaunchPersistentContextOptions options);
/**
@@ -1875,7 +1875,9 @@ public interface Frame {
*/
public Double timeout;
/**
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
* the string.
*/
public Object url;
/**
@@ -1899,21 +1901,27 @@ public interface Frame {
return this;
}
/**
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
* the string.
*/
public WaitForNavigationOptions setUrl(String url) {
this.url = url;
return this;
}
/**
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
* the string.
*/
public WaitForNavigationOptions setUrl(Pattern url) {
this.url = url;
return this;
}
/**
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
* the string.
*/
public WaitForNavigationOptions setUrl(Predicate<String> url) {
this.url = url;
@@ -3955,7 +3963,9 @@ public interface Frame {
* frame.waitForURL("**\/target.html");
* }</pre>
*
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
* the string.
*/
default void waitForURL(String url) {
waitForURL(url, null);
@@ -3967,7 +3977,9 @@ public interface Frame {
* frame.waitForURL("**\/target.html");
* }</pre>
*
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
* the string.
*/
void waitForURL(String url, WaitForURLOptions options);
/**
@@ -3977,7 +3989,9 @@ public interface Frame {
* frame.waitForURL("**\/target.html");
* }</pre>
*
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
* the string.
*/
default void waitForURL(Pattern url) {
waitForURL(url, null);
@@ -3989,7 +4003,9 @@ public interface Frame {
* frame.waitForURL("**\/target.html");
* }</pre>
*
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
* the string.
*/
void waitForURL(Pattern url, WaitForURLOptions options);
/**
@@ -3999,7 +4015,9 @@ public interface Frame {
* frame.waitForURL("**\/target.html");
* }</pre>
*
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
* the string.
*/
default void waitForURL(Predicate<String> url) {
waitForURL(url, null);
@@ -4011,7 +4029,9 @@ public interface Frame {
* frame.waitForURL("**\/target.html");
* }</pre>
*
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
* the string.
*/
void waitForURL(Predicate<String> url, WaitForURLOptions options);
}
@@ -1355,6 +1355,51 @@ public interface Locator {
return this;
}
}
class WaitForOptions {
/**
* 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 WaitForSelectorState state;
/**
* 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;
/**
* 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 WaitForOptions setState(WaitForSelectorState state) {
this.state = state;
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 WaitForOptions setTimeout(double timeout) {
this.timeout = timeout;
return this;
}
}
/**
* Returns an array of {@code node.innerText} values for all matching nodes.
*/
@@ -2667,5 +2712,29 @@ public interface Locator {
* zero timeout disables this.
*/
void uncheck(UncheckOptions options);
/**
* Returns when element specified by locator satisfies the {@code state} option.
*
* <p> If target element already satisfies the condition, the method returns immediately. Otherwise, waits for up to {@code timeout}
* milliseconds until the condition is met.
* <pre>{@code
* Locator orderSent = page.locator("#order-sent");
* orderSent.waitFor();
* }</pre>
*/
default void waitFor() {
waitFor(null);
}
/**
* Returns when element specified by locator satisfies the {@code state} option.
*
* <p> If target element already satisfies the condition, the method returns immediately. Otherwise, waits for up to {@code timeout}
* milliseconds until the condition is met.
* <pre>{@code
* Locator orderSent = page.locator("#order-sent");
* orderSent.waitFor();
* }</pre>
*/
void waitFor(WaitForOptions options);
}
@@ -2712,7 +2712,9 @@ public interface Page extends AutoCloseable {
*/
public Double timeout;
/**
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
* the string.
*/
public Object url;
/**
@@ -2736,21 +2738,27 @@ public interface Page extends AutoCloseable {
return this;
}
/**
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
* the string.
*/
public WaitForNavigationOptions setUrl(String url) {
this.url = url;
return this;
}
/**
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
* the string.
*/
public WaitForNavigationOptions setUrl(Pattern url) {
this.url = url;
return this;
}
/**
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
* the string.
*/
public WaitForNavigationOptions setUrl(Predicate<String> url) {
this.url = url;
@@ -5454,7 +5462,9 @@ public interface Page extends AutoCloseable {
* Browser#newContext Browser.newContext()} allows to set viewport size (and more) for all pages in the context at once.
*
* <p> {@code page.setViewportSize} will resize the page. A lot of websites don't expect phones to change size, so you should set the
* viewport size before navigating to the page.
* viewport size before navigating to the page. {@link Page#setViewportSize Page.setViewportSize()} will also reset
* {@code screen} size, use {@link Browser#newContext Browser.newContext()} with {@code screen} and {@code viewport} parameters if you need
* better control of these properties.
* <pre>{@code
* Page page = browser.newPage();
* page.setViewportSize(640, 480);
@@ -6394,7 +6404,9 @@ public interface Page extends AutoCloseable {
*
* <p> Shortcut for main frame's {@link Frame#waitForURL Frame.waitForURL()}.
*
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
* the string.
*/
default void waitForURL(String url) {
waitForURL(url, null);
@@ -6408,7 +6420,9 @@ public interface Page extends AutoCloseable {
*
* <p> Shortcut for main frame's {@link Frame#waitForURL Frame.waitForURL()}.
*
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
* the string.
*/
void waitForURL(String url, WaitForURLOptions options);
/**
@@ -6420,7 +6434,9 @@ public interface Page extends AutoCloseable {
*
* <p> Shortcut for main frame's {@link Frame#waitForURL Frame.waitForURL()}.
*
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
* the string.
*/
default void waitForURL(Pattern url) {
waitForURL(url, null);
@@ -6434,7 +6450,9 @@ public interface Page extends AutoCloseable {
*
* <p> Shortcut for main frame's {@link Frame#waitForURL Frame.waitForURL()}.
*
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
* the string.
*/
void waitForURL(Pattern url, WaitForURLOptions options);
/**
@@ -6446,7 +6464,9 @@ public interface Page extends AutoCloseable {
*
* <p> Shortcut for main frame's {@link Frame#waitForURL Frame.waitForURL()}.
*
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
* the string.
*/
default void waitForURL(Predicate<String> url) {
waitForURL(url, null);
@@ -6460,7 +6480,9 @@ public interface Page extends AutoCloseable {
*
* <p> Shortcut for main frame's {@link Frame#waitForURL Frame.waitForURL()}.
*
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
* the string.
*/
void waitForURL(Predicate<String> url, WaitForURLOptions options);
/**
@@ -71,7 +71,6 @@ class BrowserImpl extends ChannelOwner implements Browser {
} catch (IOException e) {
throw new PlaywrightException("Failed to close browser connection", e);
}
notifyRemoteClosed();
return;
}
try {
@@ -16,6 +16,7 @@
package com.microsoft.playwright.impl;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.microsoft.playwright.Browser;
@@ -23,12 +24,7 @@ import com.microsoft.playwright.BrowserType;
import com.microsoft.playwright.PlaywrightException;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Collections;
import java.util.Map;
import java.util.function.Consumer;
import static com.microsoft.playwright.impl.Serialization.gson;
@@ -58,51 +54,40 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
}
private Browser connectImpl(String wsEndpoint, ConnectOptions options) {
try {
Duration timeout = Duration.ofDays(1);
Map<String, String> headers = Collections.emptyMap();
Duration slowMo = null;
if (options != null) {
if (options.timeout != null) {
timeout = Duration.ofMillis(Math.round(options.timeout));
}
if (options.headers != null) {
headers = options.headers;
}
if (options.slowMo != null) {
slowMo = Duration.ofMillis(options.slowMo.intValue());
}
}
WebSocketTransport transport = new WebSocketTransport(new URI(wsEndpoint), headers, timeout, slowMo);
Connection connection = new Connection(transport);
PlaywrightImpl playwright = connection.initializePlaywright();
if (!playwright.initializer.has("preLaunchedBrowser")) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace(System.err);
}
throw new PlaywrightException("Malformed endpoint. Did you use launchServer method?");
}
playwright.initSharedSelectors(this.connection.getExistingObject("Playwright"));
BrowserImpl browser = connection.getExistingObject(playwright.initializer.getAsJsonObject("preLaunchedBrowser").get("guid").getAsString());
browser.isRemote = true;
browser.isConnectedOverWebSocket = true;
Consumer<WebSocketTransport> connectionCloseListener = t -> browser.notifyRemoteClosed();
transport.onClose(connectionCloseListener);
browser.onDisconnected(b -> {
playwright.unregisterSelectors();
transport.offClose(connectionCloseListener);
try {
connection.close();
} catch (IOException e) {
e.printStackTrace(System.err);
}
});
return browser;
} catch (URISyntaxException e) {
throw new PlaywrightException("Failed to connect", e);
if (options == null) {
options = new ConnectOptions();
}
// We don't use gson() here as the headers map should be serialized to a json object.
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
params.addProperty("wsEndpoint", wsEndpoint);
JsonObject json = sendMessage("connect", params).getAsJsonObject();
JsonPipe pipe = connection.getExistingObject(json.getAsJsonObject("pipe").get("guid").getAsString());
Connection connection = new Connection(pipe);
PlaywrightImpl playwright = connection.initializePlaywright();
if (!playwright.initializer.has("preLaunchedBrowser")) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace(System.err);
}
throw new PlaywrightException("Malformed endpoint. Did you use launchServer method?");
}
playwright.initSharedSelectors(this.connection.getExistingObject("Playwright"));
BrowserImpl browser = connection.getExistingObject(playwright.initializer.getAsJsonObject("preLaunchedBrowser").get("guid").getAsString());
browser.isRemote = true;
browser.isConnectedOverWebSocket = true;
Consumer<JsonPipe> connectionCloseListener = t -> browser.notifyRemoteClosed();
pipe.onClose(connectionCloseListener);
browser.onDisconnected(b -> {
playwright.unregisterSelectors();
pipe.offClose(connectionCloseListener);
try {
connection.close();
} catch (IOException e) {
e.printStackTrace(System.err);
}
});
return browser;
}
@Override
@@ -85,6 +85,9 @@ public class Connection {
}
Connection(Transport transport) {
if (isLogging) {
transport = new TransportLogger(transport);
}
this.transport = transport;
root = new Root(this);
String srcRoot = System.getenv("PLAYWRIGHT_JAVA_SRC");
@@ -172,11 +175,7 @@ public class Connection {
metadata.addProperty("apiName", apiName);
}
message.add("metadata", metadata);
String messageString = gson().toJson(message);
if (isLogging) {
logWithTimestamp("SEND ► " + messageString);
}
transport.send(messageString);
transport.send(message);
return result;
}
@@ -200,16 +199,13 @@ public class Connection {
}
void processOneMessage() {
String messageString = transport.poll(Duration.ofMillis(10));
if (messageString == null) {
JsonObject message = transport.poll(Duration.ofMillis(10));
if (message == null) {
return;
}
if (isLogging) {
logWithTimestamp("◀ RECV " + messageString);
}
Gson gson = gson();
Message message = gson.fromJson(messageString, Message.class);
dispatch(message);
Message messageObj = gson.fromJson(message, Message.class);
dispatch(messageObj);
}
private void dispatch(Message message) {
@@ -315,6 +311,9 @@ public class Connection {
case "JSHandle":
result = new JSHandleImpl(parent, type, guid, initializer);
break;
case "JsonPipe":
result = new JsonPipe(parent, type, guid, initializer);
break;
case "Page":
result = new PageImpl(parent, type, guid, initializer);
break;
@@ -948,11 +948,16 @@ public class FrameImpl extends ChannelOwner implements Frame {
}
ElementHandle waitForSelectorImpl(String selector, WaitForSelectorOptions options) {
return waitForSelectorImpl(selector, options, false);
}
ElementHandle waitForSelectorImpl(String selector, WaitForSelectorOptions options, boolean omitReturnValue) {
if (options == null) {
options = new WaitForSelectorOptions();
}
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
params.addProperty("selector", selector);
params.addProperty("omitReturnValue", omitReturnValue);
JsonElement json = sendMessage("waitForSelector", params);
JsonObject element = json.getAsJsonObject().getAsJsonObject("element");
if (element == null) {
@@ -0,0 +1,109 @@
/*
* 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.google.gson.JsonObject;
import com.microsoft.playwright.PlaywrightException;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.LinkedList;
import java.util.Queue;
import java.util.function.Consumer;
import static com.microsoft.playwright.impl.Serialization.gson;
class JsonPipe extends ChannelOwner implements Transport {
private final Queue<JsonObject> incoming = new LinkedList<>();
private ListenerCollection<EventType> listeners = new ListenerCollection<>();
private enum EventType { CLOSE }
private boolean isClosed;
JsonPipe(ChannelOwner parent, String type, String guid, JsonObject initializer) {
super(parent, type, guid, initializer);
}
@Override
public void send(JsonObject message) {
checkIfClosed();
JsonObject params = new JsonObject();
params.add("message", message);
sendMessage("send", params);
}
@Override
public JsonObject poll(Duration timeout) {
Instant start = Instant.now();
return runUntil(() -> {}, new Waitable<JsonObject>() {
JsonObject message;
@Override
public boolean isDone() {
if (!incoming.isEmpty()) {
message = incoming.remove();
return true;
}
checkIfClosed();
if (Duration.between(start, Instant.now()).compareTo(timeout) > 0) {
return true;
}
return false;
}
@Override
public JsonObject get() {
return message;
}
@Override
public void dispose() {
}
});
}
@Override
public void close() throws IOException {
if (!isClosed) {
sendMessage("close");
}
}
void onClose(Consumer<JsonPipe> handler) {
listeners.add(EventType.CLOSE, handler);
}
void offClose(Consumer<JsonPipe> handler) {
listeners.remove(EventType.CLOSE, handler);
}
@Override
protected void handleEvent(String event, JsonObject params) {
if ("message".equals(event)) {
incoming.add(params.get("message").getAsJsonObject());
} else if ("closed".equals(event)) {
isClosed = true;
listeners.notify(EventType.CLOSE, this);
}
}
private void checkIfClosed() {
if (isClosed) {
throw new PlaywrightException("Browser has been closed");
}
}
}
@@ -399,6 +399,18 @@ class LocatorImpl implements Locator {
frame.uncheck(selector, convertViaJson(options, Frame.UncheckOptions.class).setStrict(true));
}
@Override
public void waitFor(WaitForOptions options) {
if (options == null) {
options = new WaitForOptions();
}
waitForImpl(options);
}
private void waitForImpl(WaitForOptions options) {
frame.withLogging("Locator.waitFor", () -> frame.waitForSelectorImpl(selector, convertViaJson(options, Frame.WaitForSelectorOptions.class).setStrict(true), true));
}
@Override
public String toString() {
return "Locator@" + selector;
@@ -15,6 +15,7 @@
*/
package com.microsoft.playwright.impl;
import com.google.gson.JsonObject;
import com.microsoft.playwright.PlaywrightException;
import java.io.*;
@@ -24,8 +25,10 @@ import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import static com.microsoft.playwright.impl.Serialization.gson;
public class PipeTransport implements Transport {
private final BlockingQueue<String> incoming = new ArrayBlockingQueue<>(1000);
private final BlockingQueue<JsonObject> incoming = new ArrayBlockingQueue<>(1000);
private final BlockingQueue<String> outgoing= new ArrayBlockingQueue<>(1000);
private final ReaderThread readerThread;
@@ -42,24 +45,27 @@ public class PipeTransport implements Transport {
}
@Override
public void send(String message) {
public void send(JsonObject message) {
if (isClosed) {
throw new PlaywrightException("Playwright connection closed");
}
try {
outgoing.put(message);
// We could serialize the message on the IO thread but there is no guarantee
// that the message object won't be modified on this thread after it's added
// to the queue.
outgoing.put(gson().toJson(message));
} catch (InterruptedException e) {
throw new PlaywrightException("Failed to send message", e);
}
}
@Override
public String poll(Duration timeout) {
public JsonObject poll(Duration timeout) {
if (isClosed) {
throw new PlaywrightException("Playwright connection closed");
}
try {
String message = incoming.poll(timeout.toMillis(), TimeUnit.MILLISECONDS);
JsonObject message = incoming.poll(timeout.toMillis(), TimeUnit.MILLISECONDS);
if (message == null && readerThread.exception != null) {
try {
close();
@@ -91,7 +97,7 @@ public class PipeTransport implements Transport {
class ReaderThread extends Thread {
private final DataInputStream in;
private final BlockingQueue<String> queue;
private final BlockingQueue<JsonObject> queue;
volatile boolean isClosing;
volatile Exception exception;
@@ -107,7 +113,7 @@ class ReaderThread extends Thread {
}
}
ReaderThread(DataInputStream in, BlockingQueue<String> queue) {
ReaderThread(DataInputStream in, BlockingQueue<JsonObject> queue) {
this.in = in;
this.queue = queue;
}
@@ -116,7 +122,8 @@ class ReaderThread extends Thread {
public void run() {
while (!isInterrupted()) {
try {
queue.put(readMessage());
JsonObject message = gson().fromJson(readMessage(), JsonObject.class);
queue.put(message);
} catch (IOException e) {
if (!isInterrupted() && !isClosing) {
exception = e;
@@ -167,13 +167,8 @@ public class RequestImpl extends ChannelOwner implements Request {
if (rawHeaders != null) {
return rawHeaders;
}
ResponseImpl response = response();
// there is no response, so should we return the headers we have now?
if (response == null) {
return headers;
}
JsonArray rawHeadersJson = response.withLogging("Request.allHeaders", () -> {
JsonObject result = response.sendMessage("rawRequestHeaders").getAsJsonObject();
JsonArray rawHeadersJson = withLogging("Request.allHeaders", () -> {
JsonObject result = sendMessage("rawRequestHeaders").getAsJsonObject();
return result.getAsJsonArray("headers");
});
@@ -50,6 +50,10 @@ class Router {
handler.accept(route);
return true;
}
boolean isDone() {
return times != null && times <= 0;
}
}
void add(UrlMatcher matcher, Consumer<Route> handler, Integer times) {
@@ -69,6 +73,9 @@ class Router {
boolean handle(Route route) {
for (RouteInfo info : routes) {
if (info.handle(route)) {
if (info.isDone()) {
routes.remove(info);
}
return true;
}
}
@@ -33,29 +33,26 @@ import java.nio.file.Path;
import java.util.*;
class Serialization {
private static Gson gson;
private static Gson gson = new GsonBuilder()
.registerTypeAdapter(SameSiteAttribute.class, new SameSiteAdapter().nullSafe())
.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>())
.registerTypeAdapter(WaitUntilState.class, new ToLowerCaseSerializer<WaitUntilState>())
.registerTypeAdapter(WaitForSelectorState.class, new ToLowerCaseSerializer<WaitForSelectorState>())
.registerTypeAdapter((new TypeToken<List<KeyboardModifier>>(){}).getType(), new KeyboardModifiersSerializer())
.registerTypeAdapter(Optional.class, new OptionalSerializer())
.registerTypeHierarchyAdapter(JSHandleImpl.class, new HandleSerializer())
.registerTypeAdapter((new TypeToken<Map<String, String>>(){}).getType(), new StringMapSerializer())
.registerTypeAdapter((new TypeToken<Map<String, Object>>(){}).getType(), new FirefoxUserPrefsSerializer())
.registerTypeHierarchyAdapter(Path.class, new PathSerializer()).create();;
static Gson gson() {
if (gson == null) {
gson = new GsonBuilder()
.registerTypeAdapter(SameSiteAttribute.class, new SameSiteAdapter().nullSafe())
.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>())
.registerTypeAdapter(WaitUntilState.class, new ToLowerCaseSerializer<WaitUntilState>())
.registerTypeAdapter(WaitForSelectorState.class, new ToLowerCaseSerializer<WaitForSelectorState>())
.registerTypeAdapter((new TypeToken<List<KeyboardModifier>>(){}).getType(), new KeyboardModifiersSerializer())
.registerTypeAdapter(Optional.class, new OptionalSerializer())
.registerTypeHierarchyAdapter(JSHandleImpl.class, new HandleSerializer())
.registerTypeAdapter((new TypeToken<Map<String, String>>(){}).getType(), new StringMapSerializer())
.registerTypeAdapter((new TypeToken<Map<String, Object>>(){}).getType(), new FirefoxUserPrefsSerializer())
.registerTypeHierarchyAdapter(Path.class, new PathSerializer()).create();
}
return gson;
}
@@ -16,11 +16,13 @@
package com.microsoft.playwright.impl;
import com.google.gson.JsonObject;
import java.io.IOException;
import java.time.Duration;
public interface Transport {
void send(String message);
String poll(Duration timeout);
void send(JsonObject message);
JsonObject poll(Duration timeout);
void close() throws IOException;
}
@@ -0,0 +1,39 @@
package com.microsoft.playwright.impl;
import com.google.gson.JsonObject;
import java.io.IOException;
import java.time.Duration;
import static com.microsoft.playwright.impl.LoggingSupport.logWithTimestamp;
import static com.microsoft.playwright.impl.Serialization.gson;
class TransportLogger implements Transport {
private final Transport transport;
TransportLogger(Transport transport) {
this.transport = transport;
}
@Override
public void send(JsonObject message) {
String messageString = gson().toJson(message);
logWithTimestamp("SEND ► " + messageString);
transport.send(message);
}
@Override
public JsonObject poll(Duration timeout) {
JsonObject message = transport.poll(timeout);
if (message != null) {
String messageString = gson().toJson(message);
logWithTimestamp("◀ RECV " + messageString);
}
return message;
}
@Override
public void close() throws IOException {
transport.close();
}
}
@@ -1,129 +0,0 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.impl;
import com.microsoft.playwright.PlaywrightException;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import java.io.IOException;
import java.net.URI;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
class WebSocketTransport implements Transport {
private final BlockingQueue<String> incoming = new LinkedBlockingQueue<>();
private final ClientConnection clientConnection;
private final Duration slowMo;
private boolean isClosed;
private volatile Exception lastError;
ListenerCollection<EventType> listeners = new ListenerCollection<>();
private enum EventType { CLOSE }
private class ClientConnection extends WebSocketClient {
ClientConnection(URI serverUri) {
super(serverUri);
}
@Override
public void onOpen(ServerHandshake handshakedata) {
}
@Override
public void onMessage(String message) {
incoming.add(message);
}
@Override
public void onClose(int code, String reason, boolean remote) {
}
@Override
public void onError(Exception ex) {
lastError = ex;
}
}
WebSocketTransport(URI uri, Map<String, String> headers, Duration timeout, Duration slowMo) {
clientConnection = new ClientConnection(uri);
for (Map.Entry<String, String> entry : headers.entrySet()) {
clientConnection.addHeader(entry.getKey(), entry.getValue());
}
try {
if (!clientConnection.connectBlocking(timeout.toMillis(), TimeUnit.MILLISECONDS)) {
throw new PlaywrightException("Failed to connect", lastError);
}
} catch (InterruptedException e) {
throw new PlaywrightException("Failed to connect", e);
}
this.slowMo = slowMo;
}
@Override
public void send(String message) {
checkIfClosed();
clientConnection.send(message);
}
@Override
public String poll(Duration timeout) {
checkIfClosed();
try {
String message = incoming.poll(timeout.toMillis(), TimeUnit.MILLISECONDS);
if (slowMo != null && message != null) {
Thread.sleep(slowMo.toMillis());
}
return message;
} catch (InterruptedException e) {
throw new PlaywrightException("Failed to read message", e);
}
}
@Override
public void close() throws IOException {
if (isClosed) {
return;
}
isClosed = true;
clientConnection.close();
}
void onClose(Consumer<WebSocketTransport> handler) {
listeners.add(EventType.CLOSE, handler);
}
void offClose(Consumer<WebSocketTransport> handler) {
listeners.remove(EventType.CLOSE, handler);
}
private void checkIfClosed() {
if (isClosed) {
throw new PlaywrightException("Playwright connection closed");
}
if (clientConnection.isClosed()) {
isClosed = true;
listeners.notify(EventType.CLOSE, this);
throw new PlaywrightException("Playwright connection closed");
}
}
}
@@ -23,7 +23,7 @@ public class Proxy {
*/
public String server;
/**
* Optional coma-separated domains to bypass proxy, for example {@code ".com, chromium.org, .domain.com"}.
* Optional comma-separated domains to bypass proxy, for example {@code ".com, chromium.org, .domain.com"}.
*/
public String bypass;
/**
@@ -39,7 +39,7 @@ public class Proxy {
this.server = server;
}
/**
* Optional coma-separated domains to bypass proxy, for example {@code ".com, chromium.org, .domain.com"}.
* Optional comma-separated domains to bypass proxy, for example {@code ".com, chromium.org, .domain.com"}.
*/
public Proxy setBypass(String bypass) {
this.bypass = bypass;
@@ -201,23 +201,30 @@ public class Server implements HttpHandler {
return;
}
exchange.getResponseHeaders().add("Content-Type", mimeType(file));
OutputStream output = exchange.getResponseBody();
ByteArrayOutputStream body = new ByteArrayOutputStream();
OutputStream output = body;
if (gzipRoutes.contains(path)) {
exchange.getResponseHeaders().add("Content-Encoding", "gzip");
}
try (FileInputStream input = new FileInputStream(file)) {
exchange.sendResponseHeaders(200, 0);
if (gzipRoutes.contains(path)) {
output = new GZIPOutputStream(output);
}
copy(input, output);
output.close();
} catch (IOException e) {
try (Writer writer = new OutputStreamWriter(exchange.getResponseBody())) {
body.reset();
try (Writer writer = new OutputStreamWriter(output)) {
writer.write("Exception: " + e);
}
return;
}
output.close();
long contentLength = body.size();
// -1 means no body, 0 means chunked encoding.
exchange.sendResponseHeaders(200, contentLength == 0 ? -1 : contentLength);
if (contentLength > 0) {
exchange.getResponseBody().write(body.toByteArray());
}
exchange.getResponseBody().close();
}
private static String mimeType(File file) {
@@ -226,11 +226,21 @@ public class TestBrowserTypeConnect extends TestBase {
BrowserServer server = launchBrowserServer(browserType);
Browser remote = browserType.connect(server.wsEndpoint);
Page page = remote.newPage();
boolean[] disconnected = {false};
remote.onDisconnected(b -> disconnected[0] = true);
server.kill();
while (!disconnected[0]) {
try {
page.waitForTimeout(10);
} catch (PlaywrightException e) {
assertTrue(e.getMessage().contains("Browser has been closed"));
}
}
assertFalse(remote.isConnected());
try {
page.evaluate("1 + 1");
} catch (PlaywrightException e) {
assertTrue(e.getMessage().contains("Playwright connection closed"));
assertTrue(e.getMessage().contains("Browser has been closed"), e.getMessage());
}
assertFalse(remote.isConnected());
}
@@ -248,7 +258,7 @@ public class TestBrowserTypeConnect extends TestBase {
try {
page.waitForTimeout(10);
} catch (PlaywrightException e) {
assertTrue(e.getMessage().contains("Playwright connection closed"));
assertTrue(e.getMessage().contains("Browser has been closed"));
}
}
assertFalse(browser.isConnected());
@@ -256,7 +266,7 @@ public class TestBrowserTypeConnect extends TestBase {
page.waitForNavigation(() -> {});
fail("did not throw");
} catch (PlaywrightException e) {
assertTrue(e.getMessage().contains("Playwright connection closed"));
assertTrue(e.getMessage().contains("Browser has been closed"));
}
}
@@ -271,7 +281,7 @@ public class TestBrowserTypeConnect extends TestBase {
page.navigate(server.PREFIX + "/one-style.html", new Page.NavigateOptions().setTimeout(60000));
fail("did not throw");
} catch (PlaywrightException e) {
assertTrue(e.getMessage().contains("Playwright connection closed"));
assertTrue(e.getMessage().contains("Browser has been closed"));
}
}
@@ -61,14 +61,14 @@ public class TestElementHandleConvenience extends TestBase {
page.inputValue("#inner");
fail("did not throw");
} catch (PlaywrightException e) {
assertTrue(e.getMessage().contains("Node is not an HTMLInputElement or HTMLTextAreaElement"), e.getMessage());
assertTrue(e.getMessage().contains("Node is not an <input>, <textarea> or <select> element"), 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());
assertTrue(e.getMessage().contains("Node is not an <input>, <textarea> or <select> element"), e.getMessage());
}
}
@@ -95,14 +95,14 @@ public class TestElementHandleConvenience extends TestBase {
page.innerText("svg");
fail("did not throw");
} catch (PlaywrightException e) {
assertTrue(e.getMessage().contains("Not an HTMLElement"));
assertTrue(e.getMessage().contains("Node is not an HTMLElement"), e.getMessage());
}
ElementHandle handle = page.querySelector("svg");
try {
handle.innerText();
fail("did not throw");
} catch (PlaywrightException e) {
assertTrue(e.getMessage().contains("Not an HTMLElement"));
assertTrue(e.getMessage().contains("Node is not an HTMLElement"), e.getMessage());
}
}
@@ -65,14 +65,14 @@ public class TestLocatorConvenience extends TestBase {
page.inputValue("#inner");
fail("did not throw");
} catch (PlaywrightException e) {
assertTrue(e.getMessage().contains("Node is not an HTMLInputElement or HTMLTextAreaElement or HTMLSelectElement"));
assertTrue(e.getMessage().contains("Node is not an <input>, <textarea> or <select> element"), e.getMessage());
}
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"));
assertTrue(e.getMessage().contains("Node is not an <input>, <textarea> or <select> element"), e.getMessage());
}
}
@@ -99,14 +99,14 @@ public class TestLocatorConvenience extends TestBase {
page.innerText("svg");
fail("did not throw");
} catch (PlaywrightException e) {
assertTrue(e.getMessage().contains("Not an HTMLElement"));
assertTrue(e.getMessage().contains("Node is not an HTMLElement"), e.getMessage());
}
Locator locator = page.locator("svg");
try {
locator.innerText();
fail("did not throw");
} catch (PlaywrightException e) {
assertTrue(e.getMessage().contains("Not an HTMLElement"));
assertTrue(e.getMessage().contains("Node is not an HTMLElement"), e.getMessage());
}
}
@@ -16,9 +16,11 @@
package com.microsoft.playwright;
import com.microsoft.playwright.options.WaitForSelectorState;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestLocatorMisc extends TestBase{
@Test
@@ -30,4 +32,21 @@ public class TestLocatorMisc extends TestBase{
input.setChecked(false);
assertEquals(false, page.evaluate("checkbox.checked"));
}
@Test
void shouldWaitFor() {
page.setContent("<div></div>");
Locator locator = page.locator("span");
page.evalOnSelector("div", "div => setTimeout(() => div.innerHTML = '<span>target</span>', 500)");
locator.waitFor();
assertTrue(locator.textContent().contains("target"));
}
@Test
void shouldWaitForHidden() {
page.setContent("<div><span>target</span></div>");
Locator locator = page.locator("span");
page.evalOnSelector("div", "div => setTimeout(() => div.innerHTML = '', 500)");
locator.waitFor(new Locator.WaitForOptions().setState(WaitForSelectorState.HIDDEN));
}
}
@@ -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() {
@@ -53,13 +53,12 @@ public class TestPageNetworkSizes extends TestBase {
}
@Test
@Disabled("responseBodySize == 16")
void shouldSetBodySizeHeadersSizeAndTransferSize() throws ExecutionException, InterruptedException {
server.setRoute("/get", exchange -> {
// In Firefox, |fetch| will be hanging until it receives |Content-Type| header
// from server.
exchange.getResponseHeaders().add("Content-Type", "text/plain; charset=utf-8");
exchange.sendResponseHeaders(200, 0);
exchange.sendResponseHeaders(200, 6);
try (OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody())) {
writer.write("abc134");
}
@@ -71,20 +70,18 @@ public class TestPageNetworkSizes extends TestBase {
request.get();
Sizes sizes = response.request().sizes();
assertEquals(6, sizes.responseBodySize);
assertTrue(sizes.responseHeadersSize >= 100);
assertTrue(sizes.responseHeadersSize > 10);
}
@Test
@Disabled("responseBodySize == 5")
void shouldSetBodySizeTo0WhenThereWasNoResponseBody() {
Response response = page.navigate(server.EMPTY_PAGE);
Sizes sizes = response.request().sizes();
assertEquals(0, sizes.responseBodySize);
assertTrue(sizes.responseHeadersSize >= 100, "" + sizes.responseHeadersSize);
assertTrue(sizes.responseHeadersSize > 10, "" + sizes.responseHeadersSize);
}
@Test
@Disabled("responseBodySize == 0")
void shouldHaveTheCorrectResponseBodySize() throws IOException {
Response response = page.navigate(server.PREFIX + "/simplezip.json");
Sizes sizes = response.request().sizes();
@@ -141,7 +141,7 @@ public class TestPageSelectOption extends TestBase {
page.selectOption("body", "");
fail("did not throw");
} catch (PlaywrightException e) {
assertTrue(e.getMessage().contains("Element is not a <select> element."));
assertTrue(e.getMessage().contains("Element is not a <select> element"), e.getMessage());
}
}
+2 -1
View File
@@ -6,7 +6,7 @@
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.15.0-SNAPSHOT</version>
<version>1.16.0</version>
<packaging>pom</packaging>
<name>Playwright Parent Project</name>
<description>Java library to automate Chromium, Firefox and WebKit with a single API.
@@ -76,6 +76,7 @@
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>${websocket.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
+1 -1
View File
@@ -1 +1 @@
1.15.0-1631797286000
1.16.0-next-1634661437000
+1 -1
View File
@@ -25,5 +25,5 @@ POM_FILES=(
for name in ${POM_FILES[*]};
do
mvn versions:set -D newVersion=$VERSION -f $name
mvn versions:set -D generateBackupPoms=false -D newVersion=$VERSION -f $name
done
+1 -1
View File
@@ -6,7 +6,7 @@
<groupId>com.microsoft.playwright</groupId>
<artifactId>api-generator</artifactId>
<version>1.15.0-SNAPSHOT</version>
<version>1.16.0</version>
<name>Playwright - API Generator</name>
<description>
This is an internal module used to generate Java API from the upstream Playwright
+1 -1
View File
@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.microsoft.playwright</groupId>
<artifactId>test-local-installation</artifactId>
<version>1.15.0-SNAPSHOT</version>
<version>1.16.0</version>
<name>Test local installation</name>
<description>Runs Playwright test suite (copied from playwright module) against locally cached Playwright</description>
<properties>
+1 -1
View File
@@ -9,7 +9,7 @@
</parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>test-spring-boot-starter</artifactId>
<version>1.15.0-SNAPSHOT</version>
<version>1.16.0</version>
<name>Test Playwright With Spring Boot</name>
<properties>
<spring.version>2.4.3</spring.version>
+1 -1
View File
@@ -6,7 +6,7 @@
<groupId>com.microsoft.playwright</groupId>
<artifactId>update-version</artifactId>
<version>1.15.0-SNAPSHOT</version>
<version>1.16.0</version>
<name>Playwright - Update Version in Documentation</name>
<description>
This is an internal module used to update versions in the documentation based on