diff --git a/playwright/src/main/java/com/microsoft/playwright/BrowserContext.java b/playwright/src/main/java/com/microsoft/playwright/BrowserContext.java index 8820f8ba..d36d9c35 100644 --- a/playwright/src/main/java/com/microsoft/playwright/BrowserContext.java +++ b/playwright/src/main/java/com/microsoft/playwright/BrowserContext.java @@ -488,6 +488,17 @@ public interface BrowserContext extends AutoCloseable { * browser.close(); * } * + *

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: + *

{@code
+   * context.route("/api/**", route -> {
+   *   if (route.request().postData().contains("my-string"))
+   *     route.fulfill(new Route.FulfillOptions().setBody("mocked-data"));
+   *   else
+   *     route.resume();
+   * });
+   * }
+ * *

Page routes (set up with {@link Page#route Page.route()}) take precedence over browser context routes when request * matches both handlers. * @@ -521,6 +532,17 @@ public interface BrowserContext extends AutoCloseable { * browser.close(); * } * + *

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: + *

{@code
+   * context.route("/api/**", route -> {
+   *   if (route.request().postData().contains("my-string"))
+   *     route.fulfill(new Route.FulfillOptions().setBody("mocked-data"));
+   *   else
+   *     route.resume();
+   * });
+   * }
+ * *

Page routes (set up with {@link Page#route Page.route()}) take precedence over browser context routes when request * matches both handlers. * @@ -554,6 +576,17 @@ public interface BrowserContext extends AutoCloseable { * browser.close(); * } * + *

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: + *

{@code
+   * context.route("/api/**", route -> {
+   *   if (route.request().postData().contains("my-string"))
+   *     route.fulfill(new Route.FulfillOptions().setBody("mocked-data"));
+   *   else
+   *     route.resume();
+   * });
+   * }
+ * *

Page routes (set up with {@link Page#route Page.route()}) take precedence over browser context routes when request * matches both handlers. * diff --git a/playwright/src/main/java/com/microsoft/playwright/BrowserType.java b/playwright/src/main/java/com/microsoft/playwright/BrowserType.java index 69927ee0..235b7ea3 100644 --- a/playwright/src/main/java/com/microsoft/playwright/BrowserType.java +++ b/playwright/src/main/java/com/microsoft/playwright/BrowserType.java @@ -42,6 +42,10 @@ import java.util.*; */ public interface BrowserType { class ConnectOptions { + /** + * Additional HTTP headers to be sent with web socket connect request. Optional. + */ + public Map headers; /** * Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on. * Defaults to 0. @@ -53,6 +57,10 @@ public interface BrowserType { */ public Double timeout; + public ConnectOptions setHeaders(Map headers) { + this.headers = headers; + return this; + } public ConnectOptions setSlowMo(double slowMo) { this.slowMo = slowMo; return this; @@ -63,6 +71,10 @@ public interface BrowserType { } } class ConnectOverCDPOptions { + /** + * Additional HTTP headers to be sent with connect request. Optional. + */ + public Map headers; /** * Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on. * Defaults to 0. @@ -74,6 +86,10 @@ public interface BrowserType { */ public Double timeout; + public ConnectOverCDPOptions setHeaders(Map headers) { + this.headers = headers; + return this; + } public ConnectOverCDPOptions setSlowMo(double slowMo) { this.slowMo = slowMo; return this; @@ -253,7 +269,8 @@ public interface BrowserType { */ public Boolean bypassCSP; /** - * Browser distribution channel. + * Browser distribution channel. Read more about using Google Chrome and Microsoft Edge. */ public BrowserChannel channel; /** diff --git a/playwright/src/main/java/com/microsoft/playwright/ElementHandle.java b/playwright/src/main/java/com/microsoft/playwright/ElementHandle.java index f14a1689..160d922b 100644 --- a/playwright/src/main/java/com/microsoft/playwright/ElementHandle.java +++ b/playwright/src/main/java/com/microsoft/playwright/ElementHandle.java @@ -71,6 +71,12 @@ public interface ElementHandle extends JSHandle { * Page.setDefaultTimeout()} methods. */ public Double timeout; + /** + * When set, this method only performs the actionability + * 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; public CheckOptions setForce(boolean force) { this.force = force; @@ -91,6 +97,10 @@ public interface ElementHandle extends JSHandle { this.timeout = timeout; return this; } + public CheckOptions setTrial(boolean trial) { + this.trial = trial; + return this; + } } class ClickOptions { /** @@ -132,6 +142,12 @@ public interface ElementHandle extends JSHandle { * Page.setDefaultTimeout()} methods. */ public Double timeout; + /** + * When set, this method only performs the actionability + * 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; public ClickOptions setButton(MouseButton button) { this.button = button; @@ -168,6 +184,10 @@ public interface ElementHandle extends JSHandle { this.timeout = timeout; return this; } + public ClickOptions setTrial(boolean trial) { + this.trial = trial; + return this; + } } class DblclickOptions { /** @@ -205,6 +225,12 @@ public interface ElementHandle extends JSHandle { * Page.setDefaultTimeout()} methods. */ public Double timeout; + /** + * When set, this method only performs the actionability + * 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; public DblclickOptions setButton(MouseButton button) { this.button = button; @@ -237,6 +263,10 @@ public interface ElementHandle extends JSHandle { this.timeout = timeout; return this; } + public DblclickOptions setTrial(boolean trial) { + this.trial = trial; + return this; + } } class FillOptions { /** @@ -283,6 +313,12 @@ public interface ElementHandle extends JSHandle { * Page.setDefaultTimeout()} methods. */ public Double timeout; + /** + * When set, this method only performs the actionability + * 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; public HoverOptions setForce(boolean force) { this.force = force; @@ -303,6 +339,10 @@ public interface ElementHandle extends JSHandle { this.timeout = timeout; return this; } + public HoverOptions setTrial(boolean trial) { + this.trial = trial; + return this; + } } class PressOptions { /** @@ -483,6 +523,12 @@ public interface ElementHandle extends JSHandle { * Page.setDefaultTimeout()} methods. */ public Double timeout; + /** + * When set, this method only performs the actionability + * 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; public TapOptions setForce(boolean force) { this.force = force; @@ -507,6 +553,10 @@ public interface ElementHandle extends JSHandle { this.timeout = timeout; return this; } + public TapOptions setTrial(boolean trial) { + this.trial = trial; + return this; + } } class TypeOptions { /** @@ -562,6 +612,12 @@ public interface ElementHandle extends JSHandle { * Page.setDefaultTimeout()} methods. */ public Double timeout; + /** + * When set, this method only performs the actionability + * 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; public UncheckOptions setForce(boolean force) { this.force = force; @@ -582,6 +638,10 @@ public interface ElementHandle extends JSHandle { this.timeout = timeout; return this; } + public UncheckOptions setTrial(boolean trial) { + this.trial = trial; + return this; + } } class WaitForElementStateOptions { /** diff --git a/playwright/src/main/java/com/microsoft/playwright/Frame.java b/playwright/src/main/java/com/microsoft/playwright/Frame.java index 1f6446cf..f292047e 100644 --- a/playwright/src/main/java/com/microsoft/playwright/Frame.java +++ b/playwright/src/main/java/com/microsoft/playwright/Frame.java @@ -148,6 +148,12 @@ public interface Frame { * Page.setDefaultTimeout()} methods. */ public Double timeout; + /** + * When set, this method only performs the actionability + * 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; public CheckOptions setForce(boolean force) { this.force = force; @@ -168,6 +174,10 @@ public interface Frame { this.timeout = timeout; return this; } + public CheckOptions setTrial(boolean trial) { + this.trial = trial; + return this; + } } class ClickOptions { /** @@ -209,6 +219,12 @@ public interface Frame { * Page.setDefaultTimeout()} methods. */ public Double timeout; + /** + * When set, this method only performs the actionability + * 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; public ClickOptions setButton(MouseButton button) { this.button = button; @@ -245,6 +261,10 @@ public interface Frame { this.timeout = timeout; return this; } + public ClickOptions setTrial(boolean trial) { + this.trial = trial; + return this; + } } class DblclickOptions { /** @@ -282,6 +302,12 @@ public interface Frame { * Page.setDefaultTimeout()} methods. */ public Double timeout; + /** + * When set, this method only performs the actionability + * 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; public DblclickOptions setButton(MouseButton button) { this.button = button; @@ -314,6 +340,10 @@ public interface Frame { this.timeout = timeout; return this; } + public DblclickOptions setTrial(boolean trial) { + this.trial = trial; + return this; + } } class DispatchEventOptions { /** @@ -435,6 +465,12 @@ public interface Frame { * Page.setDefaultTimeout()} methods. */ public Double timeout; + /** + * When set, this method only performs the actionability + * 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; public HoverOptions setForce(boolean force) { this.force = force; @@ -455,6 +491,10 @@ public interface Frame { this.timeout = timeout; return this; } + public HoverOptions setTrial(boolean trial) { + this.trial = trial; + return this; + } } class InnerHTMLOptions { /** @@ -692,6 +732,12 @@ public interface Frame { * Page.setDefaultTimeout()} methods. */ public Double timeout; + /** + * When set, this method only performs the actionability + * 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; public TapOptions setForce(boolean force) { this.force = force; @@ -716,6 +762,10 @@ public interface Frame { this.timeout = timeout; return this; } + public TapOptions setTrial(boolean trial) { + this.trial = trial; + return this; + } } class TextContentOptions { /** @@ -784,6 +834,12 @@ public interface Frame { * Page.setDefaultTimeout()} methods. */ public Double timeout; + /** + * When set, this method only performs the actionability + * 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; public UncheckOptions setForce(boolean force) { this.force = force; @@ -804,6 +860,10 @@ public interface Frame { this.timeout = timeout; return this; } + public UncheckOptions setTrial(boolean trial) { + this.trial = trial; + return this; + } } class WaitForFunctionOptions { /** diff --git a/playwright/src/main/java/com/microsoft/playwright/Page.java b/playwright/src/main/java/com/microsoft/playwright/Page.java index 0bbddfaf..3c6ee7cc 100644 --- a/playwright/src/main/java/com/microsoft/playwright/Page.java +++ b/playwright/src/main/java/com/microsoft/playwright/Page.java @@ -393,6 +393,12 @@ public interface Page extends AutoCloseable { * Page.setDefaultTimeout()} methods. */ public Double timeout; + /** + * When set, this method only performs the actionability + * 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; public CheckOptions setForce(boolean force) { this.force = force; @@ -413,6 +419,10 @@ public interface Page extends AutoCloseable { this.timeout = timeout; return this; } + public CheckOptions setTrial(boolean trial) { + this.trial = trial; + return this; + } } class ClickOptions { /** @@ -454,6 +464,12 @@ public interface Page extends AutoCloseable { * Page.setDefaultTimeout()} methods. */ public Double timeout; + /** + * When set, this method only performs the actionability + * 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; public ClickOptions setButton(MouseButton button) { this.button = button; @@ -490,6 +506,10 @@ public interface Page extends AutoCloseable { this.timeout = timeout; return this; } + public ClickOptions setTrial(boolean trial) { + this.trial = trial; + return this; + } } class CloseOptions { /** @@ -539,6 +559,12 @@ public interface Page extends AutoCloseable { * Page.setDefaultTimeout()} methods. */ public Double timeout; + /** + * When set, this method only performs the actionability + * 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; public DblclickOptions setButton(MouseButton button) { this.button = button; @@ -571,6 +597,10 @@ public interface Page extends AutoCloseable { this.timeout = timeout; return this; } + public DblclickOptions setTrial(boolean trial) { + this.trial = trial; + return this; + } } class DispatchEventOptions { /** @@ -779,6 +809,12 @@ public interface Page extends AutoCloseable { * Page.setDefaultTimeout()} methods. */ public Double timeout; + /** + * When set, this method only performs the actionability + * 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; public HoverOptions setForce(boolean force) { this.force = force; @@ -799,6 +835,10 @@ public interface Page extends AutoCloseable { this.timeout = timeout; return this; } + public HoverOptions setTrial(boolean trial) { + this.trial = trial; + return this; + } } class InnerHTMLOptions { /** @@ -1248,6 +1288,12 @@ public interface Page extends AutoCloseable { * Page.setDefaultTimeout()} methods. */ public Double timeout; + /** + * When set, this method only performs the actionability + * 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; public TapOptions setForce(boolean force) { this.force = force; @@ -1272,6 +1318,10 @@ public interface Page extends AutoCloseable { this.timeout = timeout; return this; } + public TapOptions setTrial(boolean trial) { + this.trial = trial; + return this; + } } class TextContentOptions { /** @@ -1340,6 +1390,12 @@ public interface Page extends AutoCloseable { * Page.setDefaultTimeout()} methods. */ public Double timeout; + /** + * When set, this method only performs the actionability + * 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; public UncheckOptions setForce(boolean force) { this.force = force; @@ -1360,6 +1416,10 @@ public interface Page extends AutoCloseable { this.timeout = timeout; return this; } + public UncheckOptions setTrial(boolean trial) { + this.trial = trial; + return this; + } } class WaitForCloseOptions { /** @@ -3127,6 +3187,17 @@ public interface Page extends AutoCloseable { * browser.close(); * } * + *

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: + *

{@code
+   * page.route("/api/**", route -> {
+   *   if (route.request().postData().contains("my-string"))
+   *     route.fulfill(new Route.FulfillOptions().setBody("mocked-data"));
+   *   else
+   *     route.resume();
+   * });
+   * }
+ * *

Page routes take precedence over browser context routes (set up with {@link BrowserContext#route * BrowserContext.route()}) when request matches both handlers. * @@ -3161,6 +3232,17 @@ public interface Page extends AutoCloseable { * browser.close(); * } * + *

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: + *

{@code
+   * page.route("/api/**", route -> {
+   *   if (route.request().postData().contains("my-string"))
+   *     route.fulfill(new Route.FulfillOptions().setBody("mocked-data"));
+   *   else
+   *     route.resume();
+   * });
+   * }
+ * *

Page routes take precedence over browser context routes (set up with {@link BrowserContext#route * BrowserContext.route()}) when request matches both handlers. * @@ -3195,6 +3277,17 @@ public interface Page extends AutoCloseable { * browser.close(); * } * + *

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: + *

{@code
+   * page.route("/api/**", route -> {
+   *   if (route.request().postData().contains("my-string"))
+   *     route.fulfill(new Route.FulfillOptions().setBody("mocked-data"));
+   *   else
+   *     route.resume();
+   * });
+   * }
+ * *

Page routes take precedence over browser context routes (set up with {@link BrowserContext#route * BrowserContext.route()}) when request matches both handlers. * @@ -4224,12 +4317,20 @@ public interface Page extends AutoCloseable { */ Page waitForPopup(WaitForPopupOptions options, Runnable callback); /** - * Waits for the matching request and returns it. + * Waits for the matching request and returns it. See waiting for event for more details about events. *

{@code
-   * Request firstRequest = page.waitForRequest("http://example.com/resource");
-   * Object finalRequest = page.waitForRequest(
-   *   request -> "http://example.com".equals(request.url()) && "GET".equals(request.method()), () -> {});
-   * return firstRequest.url();
+   * // Waits for the next response with the specified url
+   * Request request = page.waitForRequest("https://example.com/resource", () -> {
+   *   // Triggers the request
+   *   page.click("button.triggers-request");
+   * });
+   *
+   * // Waits for the next request matching some conditions
+   * Request request = page.waitForRequest(request -> "https://example.com".equals(request.url()) && "GET".equals(request.method()), () -> {
+   *   // Triggers the request
+   *   page.click("button.triggers-request");
+   * });
    * }
* * @param urlOrPredicate Request URL string, regex or predicate receiving {@code Request} object. @@ -4239,12 +4340,20 @@ public interface Page extends AutoCloseable { return waitForRequest(urlOrPredicate, null, callback); } /** - * Waits for the matching request and returns it. + * Waits for the matching request and returns it. See waiting for event for more details about events. *
{@code
-   * Request firstRequest = page.waitForRequest("http://example.com/resource");
-   * Object finalRequest = page.waitForRequest(
-   *   request -> "http://example.com".equals(request.url()) && "GET".equals(request.method()), () -> {});
-   * return firstRequest.url();
+   * // Waits for the next response with the specified url
+   * Request request = page.waitForRequest("https://example.com/resource", () -> {
+   *   // Triggers the request
+   *   page.click("button.triggers-request");
+   * });
+   *
+   * // Waits for the next request matching some conditions
+   * Request request = page.waitForRequest(request -> "https://example.com".equals(request.url()) && "GET".equals(request.method()), () -> {
+   *   // Triggers the request
+   *   page.click("button.triggers-request");
+   * });
    * }
* * @param urlOrPredicate Request URL string, regex or predicate receiving {@code Request} object. @@ -4252,12 +4361,20 @@ public interface Page extends AutoCloseable { */ Request waitForRequest(String urlOrPredicate, WaitForRequestOptions options, Runnable callback); /** - * Waits for the matching request and returns it. + * Waits for the matching request and returns it. See waiting for event for more details about events. *
{@code
-   * Request firstRequest = page.waitForRequest("http://example.com/resource");
-   * Object finalRequest = page.waitForRequest(
-   *   request -> "http://example.com".equals(request.url()) && "GET".equals(request.method()), () -> {});
-   * return firstRequest.url();
+   * // Waits for the next response with the specified url
+   * Request request = page.waitForRequest("https://example.com/resource", () -> {
+   *   // Triggers the request
+   *   page.click("button.triggers-request");
+   * });
+   *
+   * // Waits for the next request matching some conditions
+   * Request request = page.waitForRequest(request -> "https://example.com".equals(request.url()) && "GET".equals(request.method()), () -> {
+   *   // Triggers the request
+   *   page.click("button.triggers-request");
+   * });
    * }
* * @param urlOrPredicate Request URL string, regex or predicate receiving {@code Request} object. @@ -4267,12 +4384,20 @@ public interface Page extends AutoCloseable { return waitForRequest(urlOrPredicate, null, callback); } /** - * Waits for the matching request and returns it. + * Waits for the matching request and returns it. See waiting for event for more details about events. *
{@code
-   * Request firstRequest = page.waitForRequest("http://example.com/resource");
-   * Object finalRequest = page.waitForRequest(
-   *   request -> "http://example.com".equals(request.url()) && "GET".equals(request.method()), () -> {});
-   * return firstRequest.url();
+   * // Waits for the next response with the specified url
+   * Request request = page.waitForRequest("https://example.com/resource", () -> {
+   *   // Triggers the request
+   *   page.click("button.triggers-request");
+   * });
+   *
+   * // Waits for the next request matching some conditions
+   * Request request = page.waitForRequest(request -> "https://example.com".equals(request.url()) && "GET".equals(request.method()), () -> {
+   *   // Triggers the request
+   *   page.click("button.triggers-request");
+   * });
    * }
* * @param urlOrPredicate Request URL string, regex or predicate receiving {@code Request} object. @@ -4280,12 +4405,20 @@ public interface Page extends AutoCloseable { */ Request waitForRequest(Pattern urlOrPredicate, WaitForRequestOptions options, Runnable callback); /** - * Waits for the matching request and returns it. + * Waits for the matching request and returns it. See waiting for event for more details about events. *
{@code
-   * Request firstRequest = page.waitForRequest("http://example.com/resource");
-   * Object finalRequest = page.waitForRequest(
-   *   request -> "http://example.com".equals(request.url()) && "GET".equals(request.method()), () -> {});
-   * return firstRequest.url();
+   * // Waits for the next response with the specified url
+   * Request request = page.waitForRequest("https://example.com/resource", () -> {
+   *   // Triggers the request
+   *   page.click("button.triggers-request");
+   * });
+   *
+   * // Waits for the next request matching some conditions
+   * Request request = page.waitForRequest(request -> "https://example.com".equals(request.url()) && "GET".equals(request.method()), () -> {
+   *   // Triggers the request
+   *   page.click("button.triggers-request");
+   * });
    * }
* * @param urlOrPredicate Request URL string, regex or predicate receiving {@code Request} object. @@ -4295,12 +4428,20 @@ public interface Page extends AutoCloseable { return waitForRequest(urlOrPredicate, null, callback); } /** - * Waits for the matching request and returns it. + * Waits for the matching request and returns it. See waiting for event for more details about events. *
{@code
-   * Request firstRequest = page.waitForRequest("http://example.com/resource");
-   * Object finalRequest = page.waitForRequest(
-   *   request -> "http://example.com".equals(request.url()) && "GET".equals(request.method()), () -> {});
-   * return firstRequest.url();
+   * // Waits for the next response with the specified url
+   * Request request = page.waitForRequest("https://example.com/resource", () -> {
+   *   // Triggers the request
+   *   page.click("button.triggers-request");
+   * });
+   *
+   * // Waits for the next request matching some conditions
+   * Request request = page.waitForRequest(request -> "https://example.com".equals(request.url()) && "GET".equals(request.method()), () -> {
+   *   // Triggers the request
+   *   page.click("button.triggers-request");
+   * });
    * }
* * @param urlOrPredicate Request URL string, regex or predicate receiving {@code Request} object. @@ -4308,11 +4449,20 @@ public interface Page extends AutoCloseable { */ Request waitForRequest(Predicate urlOrPredicate, WaitForRequestOptions options, Runnable callback); /** - * Returns the matched response. + * Returns the matched response. See waiting for + * event for more details about events. *
{@code
-   * Response firstResponse = page.waitForResponse("https://example.com/resource", () -> {});
-   * Response finalResponse = page.waitForResponse(response -> "https://example.com".equals(response.url()) && response.status() == 200, () -> {});
-   * return finalResponse.ok();
+   * // Waits for the next response with the specified url
+   * Response response = page.waitForResponse("https://example.com/resource", () -> {
+   *   // Triggers the response
+   *   page.click("button.triggers-response");
+   * });
+   *
+   * // Waits for the next response matching some conditions
+   * Response response = page.waitForResponse(response -> "https://example.com".equals(response.url()) && response.status() == 200, () -> {
+   *   // Triggers the response
+   *   page.click("button.triggers-response");
+   * });
    * }
* * @param urlOrPredicate Request URL string, regex or predicate receiving {@code Response} object. @@ -4322,11 +4472,20 @@ public interface Page extends AutoCloseable { return waitForResponse(urlOrPredicate, null, callback); } /** - * Returns the matched response. + * Returns the matched response. See waiting for + * event for more details about events. *
{@code
-   * Response firstResponse = page.waitForResponse("https://example.com/resource", () -> {});
-   * Response finalResponse = page.waitForResponse(response -> "https://example.com".equals(response.url()) && response.status() == 200, () -> {});
-   * return finalResponse.ok();
+   * // Waits for the next response with the specified url
+   * Response response = page.waitForResponse("https://example.com/resource", () -> {
+   *   // Triggers the response
+   *   page.click("button.triggers-response");
+   * });
+   *
+   * // Waits for the next response matching some conditions
+   * Response response = page.waitForResponse(response -> "https://example.com".equals(response.url()) && response.status() == 200, () -> {
+   *   // Triggers the response
+   *   page.click("button.triggers-response");
+   * });
    * }
* * @param urlOrPredicate Request URL string, regex or predicate receiving {@code Response} object. @@ -4334,11 +4493,20 @@ public interface Page extends AutoCloseable { */ Response waitForResponse(String urlOrPredicate, WaitForResponseOptions options, Runnable callback); /** - * Returns the matched response. + * Returns the matched response. See waiting for + * event for more details about events. *
{@code
-   * Response firstResponse = page.waitForResponse("https://example.com/resource", () -> {});
-   * Response finalResponse = page.waitForResponse(response -> "https://example.com".equals(response.url()) && response.status() == 200, () -> {});
-   * return finalResponse.ok();
+   * // Waits for the next response with the specified url
+   * Response response = page.waitForResponse("https://example.com/resource", () -> {
+   *   // Triggers the response
+   *   page.click("button.triggers-response");
+   * });
+   *
+   * // Waits for the next response matching some conditions
+   * Response response = page.waitForResponse(response -> "https://example.com".equals(response.url()) && response.status() == 200, () -> {
+   *   // Triggers the response
+   *   page.click("button.triggers-response");
+   * });
    * }
* * @param urlOrPredicate Request URL string, regex or predicate receiving {@code Response} object. @@ -4348,11 +4516,20 @@ public interface Page extends AutoCloseable { return waitForResponse(urlOrPredicate, null, callback); } /** - * Returns the matched response. + * Returns the matched response. See waiting for + * event for more details about events. *
{@code
-   * Response firstResponse = page.waitForResponse("https://example.com/resource", () -> {});
-   * Response finalResponse = page.waitForResponse(response -> "https://example.com".equals(response.url()) && response.status() == 200, () -> {});
-   * return finalResponse.ok();
+   * // Waits for the next response with the specified url
+   * Response response = page.waitForResponse("https://example.com/resource", () -> {
+   *   // Triggers the response
+   *   page.click("button.triggers-response");
+   * });
+   *
+   * // Waits for the next response matching some conditions
+   * Response response = page.waitForResponse(response -> "https://example.com".equals(response.url()) && response.status() == 200, () -> {
+   *   // Triggers the response
+   *   page.click("button.triggers-response");
+   * });
    * }
* * @param urlOrPredicate Request URL string, regex or predicate receiving {@code Response} object. @@ -4360,11 +4537,20 @@ public interface Page extends AutoCloseable { */ Response waitForResponse(Pattern urlOrPredicate, WaitForResponseOptions options, Runnable callback); /** - * Returns the matched response. + * Returns the matched response. See waiting for + * event for more details about events. *
{@code
-   * Response firstResponse = page.waitForResponse("https://example.com/resource", () -> {});
-   * Response finalResponse = page.waitForResponse(response -> "https://example.com".equals(response.url()) && response.status() == 200, () -> {});
-   * return finalResponse.ok();
+   * // Waits for the next response with the specified url
+   * Response response = page.waitForResponse("https://example.com/resource", () -> {
+   *   // Triggers the response
+   *   page.click("button.triggers-response");
+   * });
+   *
+   * // Waits for the next response matching some conditions
+   * Response response = page.waitForResponse(response -> "https://example.com".equals(response.url()) && response.status() == 200, () -> {
+   *   // Triggers the response
+   *   page.click("button.triggers-response");
+   * });
    * }
* * @param urlOrPredicate Request URL string, regex or predicate receiving {@code Response} object. @@ -4374,11 +4560,20 @@ public interface Page extends AutoCloseable { return waitForResponse(urlOrPredicate, null, callback); } /** - * Returns the matched response. + * Returns the matched response. See waiting for + * event for more details about events. *
{@code
-   * Response firstResponse = page.waitForResponse("https://example.com/resource", () -> {});
-   * Response finalResponse = page.waitForResponse(response -> "https://example.com".equals(response.url()) && response.status() == 200, () -> {});
-   * return finalResponse.ok();
+   * // Waits for the next response with the specified url
+   * Response response = page.waitForResponse("https://example.com/resource", () -> {
+   *   // Triggers the response
+   *   page.click("button.triggers-response");
+   * });
+   *
+   * // Waits for the next response matching some conditions
+   * Response response = page.waitForResponse(response -> "https://example.com".equals(response.url()) && response.status() == 200, () -> {
+   *   // Triggers the response
+   *   page.click("button.triggers-response");
+   * });
    * }
* * @param urlOrPredicate Request URL string, regex or predicate receiving {@code Response} object. diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/BrowserTypeImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/BrowserTypeImpl.java index 5da9be86..e84d3f37 100644 --- a/playwright/src/main/java/com/microsoft/playwright/impl/BrowserTypeImpl.java +++ b/playwright/src/main/java/com/microsoft/playwright/impl/BrowserTypeImpl.java @@ -27,6 +27,8 @@ 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,10 +60,16 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType { private Browser connectImpl(String wsEndpoint, ConnectOptions options) { try { Duration timeout = Duration.ofDays(1); - if (options != null && options.timeout != null) { - timeout = Duration.ofMillis(Math.round(options.timeout)); + Map headers = Collections.emptyMap(); + if (options != null) { + if (options.timeout != null) { + timeout = Duration.ofMillis(Math.round(options.timeout)); + } + if (options.headers != null) { + headers = options.headers; + } } - WebSocketTransport transport = new WebSocketTransport(new URI(wsEndpoint), timeout); + WebSocketTransport transport = new WebSocketTransport(new URI(wsEndpoint), headers, timeout); Connection connection = new Connection(transport); PlaywrightImpl playwright = (PlaywrightImpl) connection.waitForObjectWithKnownName("Playwright"); if (!playwright.initializer.has("preLaunchedBrowser")) { diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/WebSocketTransport.java b/playwright/src/main/java/com/microsoft/playwright/impl/WebSocketTransport.java index 89fc36d1..267543d9 100644 --- a/playwright/src/main/java/com/microsoft/playwright/impl/WebSocketTransport.java +++ b/playwright/src/main/java/com/microsoft/playwright/impl/WebSocketTransport.java @@ -23,6 +23,7 @@ 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; @@ -62,8 +63,11 @@ class WebSocketTransport implements Transport { } } - WebSocketTransport(URI uri, Duration timeout) { + WebSocketTransport(URI uri, Map headers, Duration timeout) { clientConnection = new ClientConnection(uri); + for (Map.Entry entry : headers.entrySet()) { + clientConnection.addHeader(entry.getKey(), entry.getValue()); + } try { if (!clientConnection.connectBlocking(timeout.toMillis(), TimeUnit.MILLISECONDS)) { throw new PlaywrightException("Failed to connect", lastError); diff --git a/playwright/src/test/java/com/microsoft/playwright/TestBrowserTypeConnect.java b/playwright/src/test/java/com/microsoft/playwright/TestBrowserTypeConnect.java index 290f439a..a6db747c 100644 --- a/playwright/src/test/java/com/microsoft/playwright/TestBrowserTypeConnect.java +++ b/playwright/src/test/java/com/microsoft/playwright/TestBrowserTypeConnect.java @@ -18,14 +18,17 @@ package com.microsoft.playwright; import com.microsoft.playwright.impl.Driver; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.net.InetSocketAddress; import java.nio.file.Path; +import static com.microsoft.playwright.Utils.mapOf; import static org.junit.jupiter.api.Assertions.*; public class TestBrowserTypeConnect extends TestBase { @@ -133,6 +136,20 @@ public class TestBrowserTypeConnect extends TestBase { browser2.close(); } + @Test + void shouldSendExtraHeadersWithConnectRequest() throws Exception { + try (WebSocketServerImpl webSocketServer = WebSocketServerImpl.create()) { + try { + browserType.connect("ws://localhost:" + webSocketServer.getPort() + "/ws", + new BrowserType.ConnectOptions().setHeaders(mapOf("User-Agent", "Playwright", "foo", "bar"))); + } catch (Exception e) { + } + assertNotNull(webSocketServer.lastClientHandshake); + assertEquals("Playwright", webSocketServer.lastClientHandshake.getFieldValue("User-Agent")); + assertEquals("bar", webSocketServer.lastClientHandshake.getFieldValue("foo")); + } + } + @Test void disconnectedEventShouldBeEmittedWhenBrowserIsClosedOrServerIsClosed() throws InterruptedException { // Launch another server to not affect other tests. diff --git a/playwright/src/test/java/com/microsoft/playwright/TestChromium.java b/playwright/src/test/java/com/microsoft/playwright/TestChromium.java index 5136ba60..9afdb343 100644 --- a/playwright/src/test/java/com/microsoft/playwright/TestChromium.java +++ b/playwright/src/test/java/com/microsoft/playwright/TestChromium.java @@ -29,8 +29,10 @@ import java.net.URL; import java.net.URLConnection; 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; @EnabledIf(value="com.microsoft.playwright.TestBase#isChromium", disabledReason="Chromium-specific API") public class TestChromium extends TestBase { @@ -104,4 +106,18 @@ public class TestChromium extends TestBase { cdpBrowser2.close(); } } + + @Test + void shouldSendExtraHeadersWithConnectRequest() throws Exception { + try (WebSocketServerImpl webSocketServer = WebSocketServerImpl.create()) { + try { + browserType.connectOverCDP("ws://localhost:" + webSocketServer.getPort() + "/ws", + new BrowserType.ConnectOverCDPOptions().setHeaders(mapOf("User-Agent", "Playwright", "foo", "bar"))); + } catch (Exception e) { + } + assertNotNull(webSocketServer.lastClientHandshake); + assertEquals("Playwright", webSocketServer.lastClientHandshake.getFieldValue("User-Agent")); + assertEquals("bar", webSocketServer.lastClientHandshake.getFieldValue("foo")); + } + } } diff --git a/playwright/src/test/java/com/microsoft/playwright/TestDownload.java b/playwright/src/test/java/com/microsoft/playwright/TestDownload.java index 0d79232f..510e21d4 100644 --- a/playwright/src/test/java/com/microsoft/playwright/TestDownload.java +++ b/playwright/src/test/java/com/microsoft/playwright/TestDownload.java @@ -213,7 +213,7 @@ public class TestDownload extends TestBase { download.saveAs(userPath); fail("did not throw"); } catch (PlaywrightException e) { - assertTrue(e.getMessage().contains("File already deleted. Save before deleting."), e.getMessage()); + assertTrue(e.getMessage().contains("Target page, context or browser has been closed"), e.getMessage()); } page.close(); } diff --git a/playwright/src/test/java/com/microsoft/playwright/TestWebSocket.java b/playwright/src/test/java/com/microsoft/playwright/TestWebSocket.java index 3618358d..99d0590c 100644 --- a/playwright/src/test/java/com/microsoft/playwright/TestWebSocket.java +++ b/playwright/src/test/java/com/microsoft/playwright/TestWebSocket.java @@ -16,9 +16,6 @@ package com.microsoft.playwright; -import org.java_websocket.WebSocket; -import org.java_websocket.handshake.ClientHandshake; -import org.java_websocket.server.WebSocketServer; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -35,35 +32,10 @@ import static org.junit.jupiter.api.Assertions.*; public class TestWebSocket extends TestBase { private static WebSocketServerImpl webSocketServer; - private static int WS_SERVER_PORT = 8910; - - private static class WebSocketServerImpl extends WebSocketServer { - WebSocketServerImpl(InetSocketAddress address) { - super(address, 1); - } - - @Override - public void onOpen(org.java_websocket.WebSocket webSocket, ClientHandshake clientHandshake) { - webSocket.send("incoming"); - } - - @Override - public void onClose(org.java_websocket.WebSocket webSocket, int i, String s, boolean b) { } - - @Override - public void onMessage(org.java_websocket.WebSocket webSocket, String s) { } - - @Override - public void onError(WebSocket webSocket, Exception e) { } - - @Override - public void onStart() { } - } @BeforeAll - static void startWebSockerServer() { - webSocketServer = new WebSocketServerImpl(new InetSocketAddress("localhost", WS_SERVER_PORT)); - new Thread(webSocketServer).start(); + static void startWebSockerServer() throws InterruptedException { + webSocketServer = WebSocketServerImpl.create(); } @AfterAll diff --git a/playwright/src/test/java/com/microsoft/playwright/WebSocketServerImpl.java b/playwright/src/test/java/com/microsoft/playwright/WebSocketServerImpl.java new file mode 100644 index 00000000..cd864ab9 --- /dev/null +++ b/playwright/src/test/java/com/microsoft/playwright/WebSocketServerImpl.java @@ -0,0 +1,70 @@ +/* + * 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.java_websocket.WebSocket; +import org.java_websocket.handshake.ClientHandshake; +import org.java_websocket.server.WebSocketServer; + +import java.net.InetSocketAddress; +import java.util.concurrent.Semaphore; + +class WebSocketServerImpl extends WebSocketServer implements AutoCloseable { + volatile ClientHandshake lastClientHandshake; + private final Semaphore startSemaphore = new Semaphore(0); + + static final int WS_SERVER_PORT = 8910; + + static WebSocketServerImpl create() throws InterruptedException { + WebSocketServerImpl result = new WebSocketServerImpl(new InetSocketAddress("localhost", WebSocketServerImpl.WS_SERVER_PORT)); + result.start(); + result.startSemaphore.acquire(); + return result; + } + + private WebSocketServerImpl(InetSocketAddress address) { + super(address, 1); + } + + @Override + public void close() throws Exception { + this.stop(); + } + + @Override + public void onOpen(org.java_websocket.WebSocket webSocket, ClientHandshake clientHandshake) { + lastClientHandshake = clientHandshake; + webSocket.send("incoming"); + } + + @Override + public void onClose(org.java_websocket.WebSocket webSocket, int i, String s, boolean b) { + } + + @Override + public void onMessage(org.java_websocket.WebSocket webSocket, String s) { + } + + @Override + public void onError(WebSocket webSocket, Exception e) { + } + + @Override + public void onStart() { + startSemaphore.release(); + } +} diff --git a/scripts/CLI_VERSION b/scripts/CLI_VERSION index ce392920..25738e33 100644 --- a/scripts/CLI_VERSION +++ b/scripts/CLI_VERSION @@ -1 +1 @@ -1.11.0-next-1618951606000 +1.11.0-next-1619459952000