From c4b27febd4f7e4160dd1dd3169c23833df3a1de9 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Thu, 16 Feb 2023 19:09:22 -0800 Subject: [PATCH] feat: roll driver 1.31.0-beta-1676591072000 (#1207) --- README.md | 4 +- examples/pom.xml | 2 +- .../com/microsoft/playwright/Browser.java | 42 +++++++--- .../com/microsoft/playwright/BrowserType.java | 21 +++-- .../com/microsoft/playwright/Locator.java | 10 +-- .../java/com/microsoft/playwright/Page.java | 82 +++++++++---------- .../com/microsoft/playwright/Playwright.java | 2 +- .../java/com/microsoft/playwright/Route.java | 39 +++++++++ .../com/microsoft/playwright/Selectors.java | 32 +++++--- .../com/microsoft/playwright/Touchscreen.java | 2 + .../assertions/APIResponseAssertions.java | 3 +- .../assertions/LocatorAssertions.java | 68 ++++++++++++++- .../playwright/assertions/PageAssertions.java | 3 +- .../playwright/impl/BrowserContextImpl.java | 18 ++-- .../impl/LocatorAssertionsImpl.java | 11 ++- .../microsoft/playwright/impl/PageImpl.java | 18 ++-- .../microsoft/playwright/impl/Protocol.java | 2 +- .../com/microsoft/playwright/impl/Router.java | 30 +++++++ .../microsoft/playwright/impl/UrlMatcher.java | 2 +- .../playwright/options/RequestOptions.java | 3 +- .../TestAssertThatIsInViewport.java | 54 ++++++++++++ .../com/microsoft/playwright/TestClick.java | 1 + .../playwright/TestPageInterception.java | 13 +++ .../playwright/TestPageRequestContinue.java | 2 +- scripts/CLI_VERSION | 2 +- 25 files changed, 348 insertions(+), 118 deletions(-) create mode 100644 playwright/src/test/java/com/microsoft/playwright/TestAssertThatIsInViewport.java diff --git a/README.md b/README.md index 6b39080b..8df5008d 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,9 @@ Playwright is a Java library to automate [Chromium](https://www.chromium.org/Hom | | Linux | macOS | Windows | | :--- | :---: | :---: | :---: | -| Chromium 110.0.5481.38 | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Chromium 111.0.5563.19 | :white_check_mark: | :white_check_mark: | :white_check_mark: | | WebKit 16.4 | ✅ | ✅ | ✅ | -| Firefox 108.0.2 | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Firefox 109.0 | :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. diff --git a/examples/pom.xml b/examples/pom.xml index ed0a80bf..19e4f0a9 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -15,7 +15,7 @@ com.microsoft.playwright playwright - 1.22.0 + 1.30.0 diff --git a/playwright/src/main/java/com/microsoft/playwright/Browser.java b/playwright/src/main/java/com/microsoft/playwright/Browser.java index e5086b2b..cc4cb17e 100644 --- a/playwright/src/main/java/com/microsoft/playwright/Browser.java +++ b/playwright/src/main/java/com/microsoft/playwright/Browser.java @@ -227,8 +227,11 @@ public interface Browser extends AutoCloseable { */ public String userAgent; /** - * Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default - * viewport. + * Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent + * viewport emulation. + * + *

NOTE: The {@code null} value opts out from the default presets, makes viewport depend on the host window size defined by the + * operating system. It makes the execution of the tests non-deterministic. */ public Optional viewportSize; @@ -536,15 +539,21 @@ public interface Browser extends AutoCloseable { return this; } /** - * Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default - * viewport. + * Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent + * viewport emulation. + * + *

NOTE: The {@code null} value opts out from the default presets, makes viewport depend on the host window size defined by the + * operating system. It makes the execution of the tests non-deterministic. */ public NewContextOptions setViewportSize(int width, int height) { return setViewportSize(new ViewportSize(width, height)); } /** - * Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default - * viewport. + * Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent + * viewport emulation. + * + *

NOTE: The {@code null} value opts out from the default presets, makes viewport depend on the host window size defined by the + * operating system. It makes the execution of the tests non-deterministic. */ public NewContextOptions setViewportSize(ViewportSize viewportSize) { this.viewportSize = Optional.ofNullable(viewportSize); @@ -722,8 +731,11 @@ public interface Browser extends AutoCloseable { */ public String userAgent; /** - * Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default - * viewport. + * Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent + * viewport emulation. + * + *

NOTE: The {@code null} value opts out from the default presets, makes viewport depend on the host window size defined by the + * operating system. It makes the execution of the tests non-deterministic. */ public Optional viewportSize; @@ -1031,15 +1043,21 @@ public interface Browser extends AutoCloseable { return this; } /** - * Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default - * viewport. + * Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent + * viewport emulation. + * + *

NOTE: The {@code null} value opts out from the default presets, makes viewport depend on the host window size defined by the + * operating system. It makes the execution of the tests non-deterministic. */ public NewPageOptions setViewportSize(int width, int height) { return setViewportSize(new ViewportSize(width, height)); } /** - * Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default - * viewport. + * Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent + * viewport emulation. + * + *

NOTE: The {@code null} value opts out from the default presets, makes viewport depend on the host window size defined by the + * operating system. It makes the execution of the tests non-deterministic. */ public NewPageOptions setViewportSize(ViewportSize viewportSize) { this.viewportSize = Optional.ofNullable(viewportSize); diff --git a/playwright/src/main/java/com/microsoft/playwright/BrowserType.java b/playwright/src/main/java/com/microsoft/playwright/BrowserType.java index 3fb82fab..644036e8 100644 --- a/playwright/src/main/java/com/microsoft/playwright/BrowserType.java +++ b/playwright/src/main/java/com/microsoft/playwright/BrowserType.java @@ -601,8 +601,11 @@ public interface BrowserType { */ public String userAgent; /** - * Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default - * viewport. + * Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent + * viewport emulation. + * + *

NOTE: The {@code null} value opts out from the default presets, makes viewport depend on the host window size defined by the + * operating system. It makes the execution of the tests non-deterministic. */ public Optional viewportSize; @@ -1021,15 +1024,21 @@ public interface BrowserType { return this; } /** - * Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default - * viewport. + * Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent + * viewport emulation. + * + *

NOTE: The {@code null} value opts out from the default presets, makes viewport depend on the host window size defined by the + * operating system. It makes the execution of the tests non-deterministic. */ public LaunchPersistentContextOptions setViewportSize(int width, int height) { return setViewportSize(new ViewportSize(width, height)); } /** - * Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default - * viewport. + * Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent + * viewport emulation. + * + *

NOTE: The {@code null} value opts out from the default presets, makes viewport depend on the host window size defined by the + * operating system. It makes the execution of the tests non-deterministic. */ public LaunchPersistentContextOptions setViewportSize(ViewportSize viewportSize) { this.viewportSize = Optional.ofNullable(viewportSize); diff --git a/playwright/src/main/java/com/microsoft/playwright/Locator.java b/playwright/src/main/java/com/microsoft/playwright/Locator.java index 093631ef..e2574530 100644 --- a/playwright/src/main/java/com/microsoft/playwright/Locator.java +++ b/playwright/src/main/java/com/microsoft/playwright/Locator.java @@ -2283,7 +2283,7 @@ public interface Locator { */ void dblclick(DblclickOptions options); /** - * Programmaticaly dispatch an event on the matching element. + * Programmatically dispatch an event on the matching element. * *

**Usage** *

{@code
@@ -2328,7 +2328,7 @@ public interface Locator {
     dispatchEvent(type, eventInit, null);
   }
   /**
-   * Programmaticaly dispatch an event on the matching element.
+   * Programmatically dispatch an event on the matching element.
    *
    * 

**Usage** *

{@code
@@ -2372,7 +2372,7 @@ public interface Locator {
     dispatchEvent(type, null);
   }
   /**
-   * Programmaticaly dispatch an event on the matching element.
+   * Programmatically dispatch an event on the matching element.
    *
    * 

**Usage** *

{@code
@@ -3717,7 +3717,7 @@ public interface Locator {
    */
   Page page();
   /**
-   * Focuses the mathing element and presses a combintation of the keys.
+   * Focuses the matching element and presses a combination of the keys.
    *
    * 

**Usage** *

{@code
@@ -3756,7 +3756,7 @@ public interface Locator {
     press(key, null);
   }
   /**
-   * Focuses the mathing element and presses a combintation of the keys.
+   * Focuses the matching element and presses a combination of the keys.
    *
    * 

**Usage** *

{@code
diff --git a/playwright/src/main/java/com/microsoft/playwright/Page.java b/playwright/src/main/java/com/microsoft/playwright/Page.java
index 3321171d..6d149101 100644
--- a/playwright/src/main/java/com/microsoft/playwright/Page.java
+++ b/playwright/src/main/java/com/microsoft/playwright/Page.java
@@ -5441,6 +5441,45 @@ public interface Page extends AutoCloseable {
    * @since v1.8
    */
   Mouse mouse();
+  /**
+   * Adds one-off {@code Dialog} handler. The handler will be removed immediately after next {@code Dialog} is created.
+   * 
{@code
+   * page.onceDialog(dialog -> {
+   *   dialog.accept("foo");
+   * });
+   *
+   * // prints 'foo'
+   * System.out.println(page.evaluate("prompt('Enter string:')"));
+   *
+   * // prints 'null' as the dialog will be auto-dismissed because there are no handlers.
+   * System.out.println(page.evaluate("prompt('Enter string:')"));
+   * }
+ * + *

This code above is equivalent to: + *

{@code
+   * Consumer handler = new Consumer() {
+   *   @Override
+   *   public void accept(Dialog dialog) {
+   *     dialog.accept("foo");
+   *     page.offDialog(this);
+   *   }
+   * };
+   * page.onDialog(handler);
+   *
+   * // prints 'foo'
+   * System.out.println(page.evaluate("prompt('Enter string:')"));
+   *
+   * // prints 'null' as the dialog will be auto-dismissed because there are no handlers.
+   * System.out.println(page.evaluate("prompt('Enter string:')"));
+   * }
+ * + * @param handler Receives the {@code Dialog} object, it **must** either {@link Dialog#accept Dialog.accept()} or {@link Dialog#dismiss + * Dialog.dismiss()} the dialog - otherwise the page will freeze waiting for the + * dialog, and actions like click will never finish. + * @since v1.10 + */ + void onceDialog(Consumer handler); /** * Returns the opener for popup pages and {@code null} for others. If the opener has been closed already the returns {@code * null}. @@ -6727,7 +6766,7 @@ public interface Page extends AutoCloseable { *

When all steps combined have not finished during the specified {@code timeout}, this method throws a {@code * TimeoutError}. Passing zero timeout disables this. * - *

NOTE: {@link Page#tap Page.tap()} requires that the {@code hasTouch} option of the browser context be set to true. + *

NOTE: {@link Page#tap Page.tap()} the method will throw if {@code hasTouch} option of the browser context is false. * * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. * @since v1.8 @@ -6749,7 +6788,7 @@ public interface Page extends AutoCloseable { *

When all steps combined have not finished during the specified {@code timeout}, this method throws a {@code * TimeoutError}. Passing zero timeout disables this. * - *

NOTE: {@link Page#tap Page.tap()} requires that the {@code hasTouch} option of the browser context be set to true. + *

NOTE: {@link Page#tap Page.tap()} the method will throw if {@code hasTouch} option of the browser context is false. * * @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. * @since v1.8 @@ -7838,44 +7877,5 @@ public interface Page extends AutoCloseable { * @since v1.8 */ List workers(); - /** - * Adds one-off {@code Dialog} handler. The handler will be removed immediately after next {@code Dialog} is created. - *

{@code
-   * page.onceDialog(dialog -> {
-   *   dialog.accept("foo");
-   * });
-   *
-   * // prints 'foo'
-   * System.out.println(page.evaluate("prompt('Enter string:')"));
-   *
-   * // prints 'null' as the dialog will be auto-dismissed because there are no handlers.
-   * System.out.println(page.evaluate("prompt('Enter string:')"));
-   * }
- * - *

This code above is equivalent to: - *

{@code
-   * Consumer handler = new Consumer() {
-   *   @Override
-   *   public void accept(Dialog dialog) {
-   *     dialog.accept("foo");
-   *     page.offDialog(this);
-   *   }
-   * };
-   * page.onDialog(handler);
-   *
-   * // prints 'foo'
-   * System.out.println(page.evaluate("prompt('Enter string:')"));
-   *
-   * // prints 'null' as the dialog will be auto-dismissed because there are no handlers.
-   * System.out.println(page.evaluate("prompt('Enter string:')"));
-   * }
- * - * @param handler Receives the {@code Dialog} object, it **must** either {@link Dialog#accept Dialog.accept()} or {@link Dialog#dismiss - * Dialog.dismiss()} the dialog - otherwise the page will freeze waiting for the - * dialog, and actions like click will never finish. - * @since v1.10 - */ - void onceDialog(Consumer handler); } diff --git a/playwright/src/main/java/com/microsoft/playwright/Playwright.java b/playwright/src/main/java/com/microsoft/playwright/Playwright.java index 659d81b4..18fc0cf3 100644 --- a/playwright/src/main/java/com/microsoft/playwright/Playwright.java +++ b/playwright/src/main/java/com/microsoft/playwright/Playwright.java @@ -97,7 +97,7 @@ public interface Playwright extends AutoCloseable { * Launches new Playwright driver process and connects to it. {@link Playwright#close Playwright.close()} should be called * when the instance is no longer needed. *
{@code
-   * Playwright playwright = Playwright.create()) {
+   * Playwright playwright = Playwright.create();
    * Browser browser = playwright.webkit().launch();
    * Page page = browser.newPage();
    * page.navigate("https://www.w3.org/");
diff --git a/playwright/src/main/java/com/microsoft/playwright/Route.java b/playwright/src/main/java/com/microsoft/playwright/Route.java
index 0042eb61..4e0e8285 100644
--- a/playwright/src/main/java/com/microsoft/playwright/Route.java
+++ b/playwright/src/main/java/com/microsoft/playwright/Route.java
@@ -141,6 +141,11 @@ public interface Route {
      * If set changes the request HTTP headers. Header values will be converted to a string.
      */
     public Map headers;
+    /**
+     * Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is
+     * exceeded. Defaults to {@code 20}. Pass {@code 0} to not follow redirects.
+     */
+    public Integer maxRedirects;
     /**
      * If set changes the request method (e.g. GET or POST).
      */
@@ -161,6 +166,14 @@ public interface Route {
       this.headers = headers;
       return this;
     }
+    /**
+     * Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is
+     * exceeded. Defaults to {@code 20}. Pass {@code 0} to not follow redirects.
+     */
+    public FetchOptions setMaxRedirects(int maxRedirects) {
+      this.maxRedirects = maxRedirects;
+      return this;
+    }
     /**
      * If set changes the request method (e.g. GET or POST).
      */
@@ -321,6 +334,13 @@ public interface Route {
    * });
    * }
* + *

**Details** + * + *

Note that any overrides such as {@code url} or {@code headers} only apply to the request being routed. If this request + * results in a redirect, overrides will not be applied to the new redirected request. If you want to propagate a header + * through redirects, use the combination of {@link Route#fetch Route.fetch()} and {@link Route#fulfill Route.fulfill()} + * instead. + * * @since v1.8 */ default void resume() { @@ -340,6 +360,13 @@ public interface Route { * }); * }

* + *

**Details** + * + *

Note that any overrides such as {@code url} or {@code headers} only apply to the request being routed. If this request + * results in a redirect, overrides will not be applied to the new redirected request. If you want to propagate a header + * through redirects, use the combination of {@link Route#fetch Route.fetch()} and {@link Route#fulfill Route.fulfill()} + * instead. + * * @since v1.8 */ void resume(ResumeOptions options); @@ -488,6 +515,12 @@ public interface Route { * }); * }

* + *

**Details** + * + *

Note that {@code headers} option will apply to the fetched request as well as any redirects initiated by it. If you want + * to only apply {@code headers} to the original request, but not to redirects, look into {@link Route#resume + * Route.resume()} instead. + * * @since v1.29 */ default APIResponse fetch() { @@ -510,6 +543,12 @@ public interface Route { * }); * }

* + *

**Details** + * + *

Note that {@code headers} option will apply to the fetched request as well as any redirects initiated by it. If you want + * to only apply {@code headers} to the original request, but not to redirects, look into {@link Route#resume + * Route.resume()} instead. + * * @since v1.29 */ APIResponse fetch(FetchOptions options); diff --git a/playwright/src/main/java/com/microsoft/playwright/Selectors.java b/playwright/src/main/java/com/microsoft/playwright/Selectors.java index 99b8960a..b3dddf89 100644 --- a/playwright/src/main/java/com/microsoft/playwright/Selectors.java +++ b/playwright/src/main/java/com/microsoft/playwright/Selectors.java @@ -42,7 +42,9 @@ public interface Selectors { } } /** - * **Usage** + * Selectors must be registered before creating the page. + * + *

**Usage** * *

An example of registering selector engine that queries elements based on a tag name: *

{@code
@@ -64,8 +66,8 @@ public interface Selectors {
    * page.setContent("
"); * // Use the selector prefixed with its name. * Locator button = page.locator("tag=button"); - * // Combine it with other selector engines. - * page.locator("tag=div >> text=\"Click me\"").click(); + * // Combine it with built-in locators. + * page.locator("tag=div").getByText("Click me").click(); * // Can use it in any methods supporting selectors. * int buttonCount = (int) page.locator("tag=button").count(); * browser.close(); @@ -80,7 +82,9 @@ public interface Selectors { register(name, script, null); } /** - * **Usage** + * Selectors must be registered before creating the page. + * + *

**Usage** * *

An example of registering selector engine that queries elements based on a tag name: *

{@code
@@ -102,8 +106,8 @@ public interface Selectors {
    * page.setContent("
"); * // Use the selector prefixed with its name. * Locator button = page.locator("tag=button"); - * // Combine it with other selector engines. - * page.locator("tag=div >> text=\"Click me\"").click(); + * // Combine it with built-in locators. + * page.locator("tag=div").getByText("Click me").click(); * // Can use it in any methods supporting selectors. * int buttonCount = (int) page.locator("tag=button").count(); * browser.close(); @@ -116,7 +120,9 @@ public interface Selectors { */ void register(String name, String script, RegisterOptions options); /** - * **Usage** + * Selectors must be registered before creating the page. + * + *

**Usage** * *

An example of registering selector engine that queries elements based on a tag name: *

{@code
@@ -138,8 +144,8 @@ public interface Selectors {
    * page.setContent("
"); * // Use the selector prefixed with its name. * Locator button = page.locator("tag=button"); - * // Combine it with other selector engines. - * page.locator("tag=div >> text=\"Click me\"").click(); + * // Combine it with built-in locators. + * page.locator("tag=div").getByText("Click me").click(); * // Can use it in any methods supporting selectors. * int buttonCount = (int) page.locator("tag=button").count(); * browser.close(); @@ -154,7 +160,9 @@ public interface Selectors { register(name, script, null); } /** - * **Usage** + * Selectors must be registered before creating the page. + * + *

**Usage** * *

An example of registering selector engine that queries elements based on a tag name: *

{@code
@@ -176,8 +184,8 @@ public interface Selectors {
    * page.setContent("
"); * // Use the selector prefixed with its name. * Locator button = page.locator("tag=button"); - * // Combine it with other selector engines. - * page.locator("tag=div >> text=\"Click me\"").click(); + * // Combine it with built-in locators. + * page.locator("tag=div").getByText("Click me").click(); * // Can use it in any methods supporting selectors. * int buttonCount = (int) page.locator("tag=button").count(); * browser.close(); diff --git a/playwright/src/main/java/com/microsoft/playwright/Touchscreen.java b/playwright/src/main/java/com/microsoft/playwright/Touchscreen.java index 38d4c909..b93f1b4a 100644 --- a/playwright/src/main/java/com/microsoft/playwright/Touchscreen.java +++ b/playwright/src/main/java/com/microsoft/playwright/Touchscreen.java @@ -25,6 +25,8 @@ public interface Touchscreen { /** * Dispatches a {@code touchstart} and {@code touchend} event with a single touch at the position ({@code x},{@code y}). * + *

NOTE: {@link Page#tap Page.tap()} the method will throw if {@code hasTouch} option of the browser context is false. + * * @since v1.8 */ void tap(double x, double y); diff --git a/playwright/src/main/java/com/microsoft/playwright/assertions/APIResponseAssertions.java b/playwright/src/main/java/com/microsoft/playwright/assertions/APIResponseAssertions.java index 9258a26c..fe62f1b6 100644 --- a/playwright/src/main/java/com/microsoft/playwright/assertions/APIResponseAssertions.java +++ b/playwright/src/main/java/com/microsoft/playwright/assertions/APIResponseAssertions.java @@ -19,8 +19,7 @@ package com.microsoft.playwright.assertions; /** * The {@code APIResponseAssertions} class provides assertion methods that can be used to make assertions about the {@code - * APIResponse} in the tests. A new instance of {@code APIResponseAssertions} is created by calling {@link - * PlaywrightAssertions#assertThat PlaywrightAssertions.assertThat()}: + * APIResponse} in the tests. *

{@code
  * ...
  * import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
diff --git a/playwright/src/main/java/com/microsoft/playwright/assertions/LocatorAssertions.java b/playwright/src/main/java/com/microsoft/playwright/assertions/LocatorAssertions.java
index 38411c96..81bf2055 100644
--- a/playwright/src/main/java/com/microsoft/playwright/assertions/LocatorAssertions.java
+++ b/playwright/src/main/java/com/microsoft/playwright/assertions/LocatorAssertions.java
@@ -20,8 +20,7 @@ import java.util.regex.Pattern;
 
 /**
  * The {@code LocatorAssertions} class provides assertion methods that can be used to make assertions about the {@code
- * Locator} state in the tests. A new instance of {@code LocatorAssertions} is created by calling {@link
- * PlaywrightAssertions#assertThat PlaywrightAssertions.assertThat()}:
+ * Locator} state in the tests.
  * 
{@code
  * ...
  * import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
@@ -151,6 +150,33 @@ public interface LocatorAssertions {
       return this;
     }
   }
+  class IsInViewportOptions {
+    /**
+     * The minimal ratio of the element to intersect viewport. If equals to {@code 0}, then element should intersect viewport
+     * at any positive ratio. Defaults to {@code 0}.
+     */
+    public Double ratio;
+    /**
+     * Time to retry the assertion for.
+     */
+    public Double timeout;
+
+    /**
+     * The minimal ratio of the element to intersect viewport. If equals to {@code 0}, then element should intersect viewport
+     * at any positive ratio. Defaults to {@code 0}.
+     */
+    public IsInViewportOptions setRatio(double ratio) {
+      this.ratio = ratio;
+      return this;
+    }
+    /**
+     * Time to retry the assertion for.
+     */
+    public IsInViewportOptions setTimeout(double timeout) {
+      this.timeout = timeout;
+      return this;
+    }
+  }
   class IsVisibleOptions {
     /**
      * Time to retry the assertion for.
@@ -548,6 +574,44 @@ public interface LocatorAssertions {
    * @since v1.20
    */
   void isHidden(IsHiddenOptions options);
+  /**
+   * Ensures the {@code Locator} points to an element that intersects viewport, according to the intersection observer API.
+   *
+   * 

**Usage** + *

{@code
+   * Locator locator = page.locator("button.submit");
+   * // Make sure at least some part of element intersects viewport.
+   * assertThat(locator).isInViewport();
+   * // Make sure element is fully outside of viewport.
+   * assertThat(locator).not().isInViewport();
+   * // Make sure that at least half of the element intersects viewport.
+   * assertThat(locator).isInViewport(new LocatorAssertions.IsInViewportOptions().setRatio(0.5));
+   * }
+ * + * @since v1.31 + */ + default void isInViewport() { + isInViewport(null); + } + /** + * Ensures the {@code Locator} points to an element that intersects viewport, according to the intersection observer API. + * + *

**Usage** + *

{@code
+   * Locator locator = page.locator("button.submit");
+   * // Make sure at least some part of element intersects viewport.
+   * assertThat(locator).isInViewport();
+   * // Make sure element is fully outside of viewport.
+   * assertThat(locator).not().isInViewport();
+   * // Make sure that at least half of the element intersects viewport.
+   * assertThat(locator).isInViewport(new LocatorAssertions.IsInViewportOptions().setRatio(0.5));
+   * }
+ * + * @since v1.31 + */ + void isInViewport(IsInViewportOptions options); /** * Ensures that {@code Locator} points to an attached * and visible DOM node. diff --git a/playwright/src/main/java/com/microsoft/playwright/assertions/PageAssertions.java b/playwright/src/main/java/com/microsoft/playwright/assertions/PageAssertions.java index 09215b4d..9d46459e 100644 --- a/playwright/src/main/java/com/microsoft/playwright/assertions/PageAssertions.java +++ b/playwright/src/main/java/com/microsoft/playwright/assertions/PageAssertions.java @@ -20,8 +20,7 @@ import java.util.regex.Pattern; /** * The {@code PageAssertions} class provides assertion methods that can be used to make assertions about the {@code Page} - * state in the tests. A new instance of {@code PageAssertions} is created by calling {@link - * PlaywrightAssertions#assertThat PlaywrightAssertions.assertThat()}: + * state in the tests. *
{@code
  * ...
  * import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/BrowserContextImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/BrowserContextImpl.java
index f3e930ed..50359995 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/BrowserContextImpl.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/BrowserContextImpl.java
@@ -399,11 +399,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
   private void route(UrlMatcher matcher, Consumer handler, RouteOptions options) {
     withLogging("BrowserContext.route", () -> {
       routes.add(matcher, handler, options == null ? null : options.times);
-      if (routes.size() == 1) {
-        JsonObject params = new JsonObject();
-        params.addProperty("enabled", true);
-        sendMessage("setNetworkInterceptionEnabled", params);
-      }
+      updateInterceptionPatterns();
     });
   }
 
@@ -525,22 +521,18 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
   private void unroute(UrlMatcher matcher, Consumer handler) {
     withLogging("BrowserContext.unroute", () -> {
       routes.remove(matcher, handler);
-      maybeDisableNetworkInterception();
+      updateInterceptionPatterns();
     });
   }
 
-  private void maybeDisableNetworkInterception() {
-    if (routes.size() == 0) {
-      JsonObject params = new JsonObject();
-      params.addProperty("enabled", false);
-      sendMessage("setNetworkInterceptionEnabled", params);
-    }
+  private void updateInterceptionPatterns() {
+    sendMessage("setNetworkInterceptionPatterns", routes.interceptionPatterns());
   }
 
   void handleRoute(RouteImpl route) {
     Router.HandleResult handled = routes.handle(route);
     if (handled != Router.HandleResult.NoMatchingHandler) {
-      maybeDisableNetworkInterception();
+      updateInterceptionPatterns();
     }
     if (handled == Router.HandleResult.NoMatchingHandler || handled == Router.HandleResult.Fallback) {
       route.resume();
diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/LocatorAssertionsImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/LocatorAssertionsImpl.java
index b7dfb22d..2c76b8f1 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/LocatorAssertionsImpl.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/LocatorAssertionsImpl.java
@@ -148,7 +148,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
       options = new HasCountOptions();
     }
     FrameExpectOptions commonOptions = convertType(options, FrameExpectOptions.class);
-    commonOptions.expectedNumber = count;
+    commonOptions.expectedNumber = (double) count;
     List expectedText = null;
     expectImpl("to.have.count", expectedText, count, "Locator expected to have count", commonOptions);
   }
@@ -326,6 +326,15 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
     expectTrue("to.be.hidden", "Locator expected to be hidden", convertType(options, FrameExpectOptions.class));
   }
 
+  @Override
+  public void isInViewport(IsInViewportOptions options) {
+    FrameExpectOptions expectOptions = convertType(options, FrameExpectOptions.class);
+    if (options != null && options.ratio != null) {
+      expectOptions.expectedNumber = options.ratio;
+    }
+    expectTrue("to.be.in.viewport", "Locator expected to be in viewport",  expectOptions);
+  }
+
   @Override
   public void isVisible(IsVisibleOptions options) {
     FrameExpectOptions frameOptions = convertType(options, FrameExpectOptions.class);
diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/PageImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/PageImpl.java
index 8abb8f78..328ceadd 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/PageImpl.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/PageImpl.java
@@ -189,7 +189,7 @@ public class PageImpl extends ChannelOwner implements Page {
       RouteImpl route = connection.getExistingObject(params.getAsJsonObject("route").get("guid").getAsString());
       Router.HandleResult handled = routes.handle(route);
       if (handled != Router.HandleResult.NoMatchingHandler) {
-        maybeDisableNetworkInterception();
+        updateInterceptionPatterns();
       }
       if (handled == Router.HandleResult.NoMatchingHandler || handled == Router.HandleResult.Fallback) {
         browserContext.handleRoute(route);
@@ -1037,11 +1037,7 @@ public class PageImpl extends ChannelOwner implements Page {
   private void route(UrlMatcher matcher, Consumer handler, RouteOptions options) {
     withLogging("Page.route", () -> {
       routes.add(matcher, handler, options == null ? null : options.times);
-      if (routes.size() == 1) {
-        JsonObject params = new JsonObject();
-        params.addProperty("enabled", true);
-        sendMessage("setNetworkInterceptionEnabled", params);
-      }
+      updateInterceptionPatterns();
     });
   }
 
@@ -1256,16 +1252,12 @@ public class PageImpl extends ChannelOwner implements Page {
   private void unroute(UrlMatcher matcher, Consumer handler) {
     withLogging("Page.unroute", () -> {
       routes.remove(matcher, handler);
-      maybeDisableNetworkInterception();
+      updateInterceptionPatterns();
     });
   }
 
-  private void maybeDisableNetworkInterception() {
-    if (routes.size() == 0) {
-      JsonObject params = new JsonObject();
-      params.addProperty("enabled", false);
-      sendMessage("setNetworkInterceptionEnabled", params);
-    }
+  private void updateInterceptionPatterns() {
+    sendMessage("setNetworkInterceptionPatterns", routes.interceptionPatterns());
   }
 
   @Override
diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/Protocol.java b/playwright/src/main/java/com/microsoft/playwright/impl/Protocol.java
index 29daf590..be18b7f4 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/Protocol.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/Protocol.java
@@ -94,7 +94,7 @@ class ExpectedTextValue {
 class FrameExpectOptions {
   Object expressionArg;
   List expectedText;
-  Integer expectedNumber;
+  Double expectedNumber;
   SerializedArgument expectedValue;
   Boolean useInnerText;
   boolean isNot;
diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/Router.java b/playwright/src/main/java/com/microsoft/playwright/impl/Router.java
index 683d72eb..80506215 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/Router.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/Router.java
@@ -16,14 +16,19 @@
 
 package com.microsoft.playwright.impl;
 
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
 import com.microsoft.playwright.Route;
 
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.function.Consumer;
+import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
+import static com.microsoft.playwright.impl.Utils.toJsRegexFlags;
+
 class Router {
   private List routes = new ArrayList<>();
 
@@ -92,4 +97,29 @@ class Router {
     }
     return result;
   }
+
+  JsonObject interceptionPatterns() {
+    JsonArray jsonPatterns = new JsonArray();
+    for (RouteInfo route : routes) {
+      JsonObject jsonPattern = new JsonObject();
+      Object urlFilter = route.matcher.rawSource;
+      if (urlFilter instanceof String) {
+        jsonPattern.addProperty("glob", (String) urlFilter);
+      } else if (urlFilter instanceof Pattern) {
+        Pattern pattern = (Pattern) urlFilter;
+        jsonPattern.addProperty("regexSource", pattern.pattern());
+        jsonPattern.addProperty("regexFlags", toJsRegexFlags(pattern));
+      } else {
+        // Match all requests.
+        jsonPattern.addProperty("glob", "**/*");
+        jsonPatterns = new JsonArray();
+        jsonPatterns.add(jsonPattern);
+        break;
+      }
+      jsonPatterns.add(jsonPattern);
+    }
+    JsonObject result = new JsonObject();
+    result.add("patterns", jsonPatterns);
+    return result;
+  }
 }
diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/UrlMatcher.java b/playwright/src/main/java/com/microsoft/playwright/impl/UrlMatcher.java
index c89bf9c4..7039e6a9 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/UrlMatcher.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/UrlMatcher.java
@@ -27,7 +27,7 @@ import java.util.regex.Pattern;
 import static com.microsoft.playwright.impl.Utils.globToRegex;
 
 class UrlMatcher {
-  private final Object rawSource;
+  final Object rawSource;
   private final Predicate predicate;
 
   private static Predicate toPredicate(Pattern pattern) {
diff --git a/playwright/src/main/java/com/microsoft/playwright/options/RequestOptions.java b/playwright/src/main/java/com/microsoft/playwright/options/RequestOptions.java
index 3a19a31d..0cb5dd4f 100644
--- a/playwright/src/main/java/com/microsoft/playwright/options/RequestOptions.java
+++ b/playwright/src/main/java/com/microsoft/playwright/options/RequestOptions.java
@@ -110,7 +110,8 @@ public interface RequestOptions {
    */
   RequestOptions setForm(FormData form);
   /**
-   * Sets an HTTP header to the request.
+   * Sets an HTTP header to the request. This header will apply to the fetched request as well as any redirects initiated by
+   * it.
    *
    * @param name Header name.
    * @param value Header value.
diff --git a/playwright/src/test/java/com/microsoft/playwright/TestAssertThatIsInViewport.java b/playwright/src/test/java/com/microsoft/playwright/TestAssertThatIsInViewport.java
new file mode 100644
index 00000000..1cc658ad
--- /dev/null
+++ b/playwright/src/test/java/com/microsoft/playwright/TestAssertThatIsInViewport.java
@@ -0,0 +1,54 @@
+package com.microsoft.playwright;
+
+import com.microsoft.playwright.assertions.LocatorAssertions;
+import org.junit.jupiter.api.Test;
+import org.opentest4j.AssertionFailedError;
+
+import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
+import static org.junit.jupiter.api.Assertions.*;
+
+// Copied from expect-misc.spec.ts > toBeInViewport
+public class TestAssertThatIsInViewport extends TestBase {
+  @Test
+  void shouldWork() {
+    page.setContent("
\n" + + "
foo
"); + assertThat(page.locator("#big")).isInViewport(); + assertThat(page.locator("#small")).not().isInViewport(); + page.locator("#small").scrollIntoViewIfNeeded(); + assertThat(page.locator("#small")).isInViewport(); + assertThat(page.locator("#small")).isInViewport(new LocatorAssertions.IsInViewportOptions().setRatio(1)); + } + + @Test + void shouldRespectRatioOption() { + page.setContent("\n" + + "
"); + assertThat(page.locator("div")).isInViewport(); + assertThat(page.locator("div")).isInViewport(new LocatorAssertions.IsInViewportOptions().setRatio(0.1)); + assertThat(page.locator("div")).isInViewport(new LocatorAssertions.IsInViewportOptions().setRatio(0.2)); + + assertThat(page.locator("div")).isInViewport(new LocatorAssertions.IsInViewportOptions().setRatio(0.24)); + // In this test, element's ratio is 0.25. + assertThat(page.locator("div")).isInViewport(new LocatorAssertions.IsInViewportOptions().setRatio(0.25)); + assertThat(page.locator("div")).not().isInViewport(new LocatorAssertions.IsInViewportOptions().setRatio(0.26)); + + assertThat(page.locator("div")).not().isInViewport(new LocatorAssertions.IsInViewportOptions().setRatio(0.3)); + assertThat(page.locator("div")).not().isInViewport(new LocatorAssertions.IsInViewportOptions().setRatio(0.7)); + assertThat(page.locator("div")).not().isInViewport(new LocatorAssertions.IsInViewportOptions().setRatio(0.8)); + } + + @Test + void shouldHaveGoodStack() { + AssertionFailedError error = assertThrows(AssertionFailedError.class, () -> assertThat(page.locator("body")).not().isInViewport(new LocatorAssertions.IsInViewportOptions().setTimeout(100))); + assertNotNull(error); + assertTrue(error.getMessage().contains("Locator expected not to be in viewport"), error.getMessage()); + } + + @Test + void shouldReportIntersectionEvenIfFullyCoveredByOtherElement() { + page.setContent("

hello

\n" + + "
"); + assertThat(page.locator("h1")).isInViewport(); + } +} diff --git a/playwright/src/test/java/com/microsoft/playwright/TestClick.java b/playwright/src/test/java/com/microsoft/playwright/TestClick.java index a155e66c..74f24e49 100644 --- a/playwright/src/test/java/com/microsoft/playwright/TestClick.java +++ b/playwright/src/test/java/com/microsoft/playwright/TestClick.java @@ -17,6 +17,7 @@ package com.microsoft.playwright; import com.microsoft.playwright.options.AriaRole; +import com.microsoft.playwright.options.WaitUntilState; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledIf; import org.junit.jupiter.api.condition.EnabledIf; diff --git a/playwright/src/test/java/com/microsoft/playwright/TestPageInterception.java b/playwright/src/test/java/com/microsoft/playwright/TestPageInterception.java index 57197a64..61ab82c1 100644 --- a/playwright/src/test/java/com/microsoft/playwright/TestPageInterception.java +++ b/playwright/src/test/java/com/microsoft/playwright/TestPageInterception.java @@ -92,4 +92,17 @@ public class TestPageInterception extends TestBase { page.navigate(server.PREFIX + "/empty.html"); assertEquals("{ \"foo\": \"bar\" }", new String(request.get().postBody)); } + + @Test + void shouldNotFollowRedirectsWhenMaxRedirectsIsSetTo0InRouteFetch() { + server.setRedirect("/foo", "/empty.html"); + page.route("**/*", route -> { + APIResponse response = route.fetch(new Route.FetchOptions().setMaxRedirects(0)); + assertEquals("/empty.html", response.headers().get("location")); + assertEquals(302, response.status()); + route.fulfill(new Route.FulfillOptions().setBody("hello")); + }); + page.navigate(server.PREFIX + "/foo"); + assertTrue(page.content().contains("hello")); + } } diff --git a/playwright/src/test/java/com/microsoft/playwright/TestPageRequestContinue.java b/playwright/src/test/java/com/microsoft/playwright/TestPageRequestContinue.java index a2eab766..40437110 100644 --- a/playwright/src/test/java/com/microsoft/playwright/TestPageRequestContinue.java +++ b/playwright/src/test/java/com/microsoft/playwright/TestPageRequestContinue.java @@ -73,7 +73,7 @@ public class TestPageRequestContinue extends TestBase { done[0] = true; }); PlaywrightException e = assertThrows(PlaywrightException.class, () -> page.navigate(server.EMPTY_PAGE)); - assertTrue(e.getMessage().contains("Navigation failed because page was closed") || + assertTrue(e.getMessage().contains("Target page, context or browser has been closed") || e.getMessage().contains("frame was detached"), e.getMessage()); assertTrue(done[0]); } diff --git a/scripts/CLI_VERSION b/scripts/CLI_VERSION index 231d458d..b78197a0 100644 --- a/scripts/CLI_VERSION +++ b/scripts/CLI_VERSION @@ -1 +1 @@ -1.30.0-beta-1674276599000 +1.31.0-beta-1676596096000