From 7842c1212ccf61e3dc8479f579aa33d12a79d881 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Wed, 12 Apr 2023 10:42:06 -0700 Subject: [PATCH] feat: roll driver to Apr 12, implement new APIs --- README.md | 2 +- .../com/microsoft/playwright/APIRequest.java | 9 +- .../com/microsoft/playwright/Browser.java | 18 ++-- .../com/microsoft/playwright/BrowserType.java | 9 +- .../java/com/microsoft/playwright/Frame.java | 78 ++++++++++---- .../microsoft/playwright/FrameLocator.java | 40 +++++++ .../com/microsoft/playwright/JSHandle.java | 2 +- .../com/microsoft/playwright/Locator.java | 100 +++++++++++++++++ .../java/com/microsoft/playwright/Page.java | 102 ++++++++++++------ .../assertions/LocatorAssertions.java | 49 ++++++++- .../impl/LocatorAssertionsImpl.java | 8 ++ .../playwright/impl/LocatorImpl.java | 17 +++ .../microsoft/playwright/impl/RouteImpl.java | 5 +- .../playwright/options/HttpCredentials.java | 11 ++ .../microsoft/playwright/options/Timing.java | 4 +- .../playwright/TestLocatorAssertions2.java | 102 ++++++++++++++++++ .../playwright/TestPageLocatorQuery.java | 21 ++++ .../playwright/TestSelectorsMisc.java | 25 +++++ scripts/CLI_VERSION | 2 +- 19 files changed, 536 insertions(+), 68 deletions(-) create mode 100644 playwright/src/test/java/com/microsoft/playwright/TestLocatorAssertions2.java diff --git a/README.md b/README.md index 43d6f57c0..727a21475 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Playwright is a Java library to automate [Chromium](https://www.chromium.org/Hom | | Linux | macOS | Windows | | :--- | :---: | :---: | :---: | -| Chromium 112.0.5615.29 | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Chromium 113.0.5672.24 | :white_check_mark: | :white_check_mark: | :white_check_mark: | | WebKit 16.4 | ✅ | ✅ | ✅ | | Firefox 111.0 | :white_check_mark: | :white_check_mark: | :white_check_mark: | diff --git a/playwright/src/main/java/com/microsoft/playwright/APIRequest.java b/playwright/src/main/java/com/microsoft/playwright/APIRequest.java index 88722473a..1f6509880 100644 --- a/playwright/src/main/java/com/microsoft/playwright/APIRequest.java +++ b/playwright/src/main/java/com/microsoft/playwright/APIRequest.java @@ -46,7 +46,8 @@ class NewContextOptions { */ public Map extraHTTPHeaders; /** - * Credentials for HTTP authentication. + * Credentials for HTTP authentication. If + * no origin is specified, the username and password are sent to any servers upon unauthorized responses. */ public HttpCredentials httpCredentials; /** @@ -106,13 +107,15 @@ public NewContextOptions setExtraHTTPHeaders(Map extraHTTPHeader return this; } /** - * Credentials for HTTP authentication. + * Credentials for HTTP authentication. If + * no origin is specified, the username and password are sent to any servers upon unauthorized responses. */ public NewContextOptions setHttpCredentials(String username, String password) { return setHttpCredentials(new HttpCredentials(username, password)); } /** - * Credentials for HTTP authentication. + * Credentials for HTTP authentication. If + * no origin is specified, the username and password are sent to any servers upon unauthorized responses. */ public NewContextOptions setHttpCredentials(HttpCredentials httpCredentials) { this.httpCredentials = httpCredentials; diff --git a/playwright/src/main/java/com/microsoft/playwright/Browser.java b/playwright/src/main/java/com/microsoft/playwright/Browser.java index cc4cb17e0..a67150196 100644 --- a/playwright/src/main/java/com/microsoft/playwright/Browser.java +++ b/playwright/src/main/java/com/microsoft/playwright/Browser.java @@ -107,7 +107,8 @@ class NewContextOptions { */ public Boolean hasTouch; /** - * Credentials for HTTP authentication. + * Credentials for HTTP authentication. If + * no origin is specified, the username and password are sent to any servers upon unauthorized responses. */ public HttpCredentials httpCredentials; /** @@ -315,13 +316,15 @@ public NewContextOptions setHasTouch(boolean hasTouch) { return this; } /** - * Credentials for HTTP authentication. + * Credentials for HTTP authentication. If + * no origin is specified, the username and password are sent to any servers upon unauthorized responses. */ public NewContextOptions setHttpCredentials(String username, String password) { return setHttpCredentials(new HttpCredentials(username, password)); } /** - * Credentials for HTTP authentication. + * Credentials for HTTP authentication. If + * no origin is specified, the username and password are sent to any servers upon unauthorized responses. */ public NewContextOptions setHttpCredentials(HttpCredentials httpCredentials) { this.httpCredentials = httpCredentials; @@ -611,7 +614,8 @@ class NewPageOptions { */ public Boolean hasTouch; /** - * Credentials for HTTP authentication. + * Credentials for HTTP authentication. If + * no origin is specified, the username and password are sent to any servers upon unauthorized responses. */ public HttpCredentials httpCredentials; /** @@ -819,13 +823,15 @@ public NewPageOptions setHasTouch(boolean hasTouch) { return this; } /** - * Credentials for HTTP authentication. + * Credentials for HTTP authentication. If + * no origin is specified, the username and password are sent to any servers upon unauthorized responses. */ public NewPageOptions setHttpCredentials(String username, String password) { return setHttpCredentials(new HttpCredentials(username, password)); } /** - * Credentials for HTTP authentication. + * Credentials for HTTP authentication. If + * no origin is specified, the username and password are sent to any servers upon unauthorized responses. */ public NewPageOptions setHttpCredentials(HttpCredentials httpCredentials) { this.httpCredentials = httpCredentials; diff --git a/playwright/src/main/java/com/microsoft/playwright/BrowserType.java b/playwright/src/main/java/com/microsoft/playwright/BrowserType.java index 644036e85..3da16016c 100644 --- a/playwright/src/main/java/com/microsoft/playwright/BrowserType.java +++ b/playwright/src/main/java/com/microsoft/playwright/BrowserType.java @@ -473,7 +473,8 @@ class LaunchPersistentContextOptions { */ public Boolean headless; /** - * Credentials for HTTP authentication. + * Credentials for HTTP authentication. If + * no origin is specified, the username and password are sent to any servers upon unauthorized responses. */ public HttpCredentials httpCredentials; /** @@ -787,13 +788,15 @@ public LaunchPersistentContextOptions setHeadless(boolean headless) { return this; } /** - * Credentials for HTTP authentication. + * Credentials for HTTP authentication. If + * no origin is specified, the username and password are sent to any servers upon unauthorized responses. */ public LaunchPersistentContextOptions setHttpCredentials(String username, String password) { return setHttpCredentials(new HttpCredentials(username, password)); } /** - * Credentials for HTTP authentication. + * Credentials for HTTP authentication. If + * no origin is specified, the username and password are sent to any servers upon unauthorized responses. */ public LaunchPersistentContextOptions setHttpCredentials(HttpCredentials httpCredentials) { this.httpCredentials = httpCredentials; diff --git a/playwright/src/main/java/com/microsoft/playwright/Frame.java b/playwright/src/main/java/com/microsoft/playwright/Frame.java index 2f677bd6c..bd3faada6 100644 --- a/playwright/src/main/java/com/microsoft/playwright/Frame.java +++ b/playwright/src/main/java/com/microsoft/playwright/Frame.java @@ -1046,8 +1046,8 @@ class NavigateOptions { *
    *
  • {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.
  • *
  • {@code "load"} - consider operation to be finished when the {@code load} event is fired.
  • - *
  • {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} - * ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** consider operation to be finished when there are no network connections for at + * least {@code 500} ms. Don't use this method for testing, rely on web assertions to assess readiness instead.
  • *
  • {@code "commit"} - consider operation to be finished when network response is received and the document started loading.
  • *
*/ @@ -1076,8 +1076,8 @@ public NavigateOptions setTimeout(double timeout) { *
    *
  • {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.
  • *
  • {@code "load"} - consider operation to be finished when the {@code load} event is fired.
  • - *
  • {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} - * ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** consider operation to be finished when there are no network connections for at + * least {@code 500} ms. Don't use this method for testing, rely on web assertions to assess readiness instead.
  • *
  • {@code "commit"} - consider operation to be finished when network response is received and the document started loading.
  • *
*/ @@ -1476,6 +1476,19 @@ class LocatorOptions { *

Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@code FrameLocator}s. */ public Locator has; + /** + * Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the + * outer one. For example, {@code article} that does not have {@code div} matches {@code + *

Playwright
}. + * + *

Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@code FrameLocator}s. + */ + public Locator hasNot; + /** + * Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element. When + * passed a [string], matching is case-insensitive and searches for a substring. + */ + public Object hasNotText; /** * Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When passed a * [string], matching is case-insensitive and searches for a substring. For example, {@code "Playwright"} matches {@code @@ -1493,6 +1506,33 @@ public LocatorOptions setHas(Locator has) { this.has = has; return this; } + /** + * Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the + * outer one. For example, {@code article} that does not have {@code div} matches {@code + *

Playwright
}. + * + *

Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@code FrameLocator}s. + */ + public LocatorOptions setHasNot(Locator hasNot) { + this.hasNot = hasNot; + return this; + } + /** + * Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element. When + * passed a [string], matching is case-insensitive and searches for a substring. + */ + public LocatorOptions setHasNotText(String hasNotText) { + this.hasNotText = hasNotText; + return this; + } + /** + * Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element. When + * passed a [string], matching is case-insensitive and searches for a substring. + */ + public LocatorOptions setHasNotText(Pattern hasNotText) { + this.hasNotText = hasNotText; + return this; + } /** * Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When passed a * [string], matching is case-insensitive and searches for a substring. For example, {@code "Playwright"} matches {@code @@ -1751,8 +1791,8 @@ class SetContentOptions { *

    *
  • {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.
  • *
  • {@code "load"} - consider operation to be finished when the {@code load} event is fired.
  • - *
  • {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} - * ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** consider operation to be finished when there are no network connections for at + * least {@code 500} ms. Don't use this method for testing, rely on web assertions to assess readiness instead.
  • *
  • {@code "commit"} - consider operation to be finished when network response is received and the document started loading.
  • *
*/ @@ -1773,8 +1813,8 @@ public SetContentOptions setTimeout(double timeout) { *
    *
  • {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.
  • *
  • {@code "load"} - consider operation to be finished when the {@code load} event is fired.
  • - *
  • {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} - * ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** consider operation to be finished when there are no network connections for at + * least {@code 500} ms. Don't use this method for testing, rely on web assertions to assess readiness instead.
  • *
  • {@code "commit"} - consider operation to be finished when network response is received and the document started loading.
  • *
*/ @@ -2188,8 +2228,8 @@ class WaitForNavigationOptions { *
    *
  • {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.
  • *
  • {@code "load"} - consider operation to be finished when the {@code load} event is fired.
  • - *
  • {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} - * ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** consider operation to be finished when there are no network connections for at + * least {@code 500} ms. Don't use this method for testing, rely on web assertions to assess readiness instead.
  • *
  • {@code "commit"} - consider operation to be finished when network response is received and the document started loading.
  • *
*/ @@ -2237,8 +2277,8 @@ public WaitForNavigationOptions setUrl(Predicate url) { *
    *
  • {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.
  • *
  • {@code "load"} - consider operation to be finished when the {@code load} event is fired.
  • - *
  • {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} - * ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** consider operation to be finished when there are no network connections for at + * least {@code 500} ms. Don't use this method for testing, rely on web assertions to assess readiness instead.
  • *
  • {@code "commit"} - consider operation to be finished when network response is received and the document started loading.
  • *
*/ @@ -2318,8 +2358,8 @@ class WaitForURLOptions { *
    *
  • {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.
  • *
  • {@code "load"} - consider operation to be finished when the {@code load} event is fired.
  • - *
  • {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} - * ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** consider operation to be finished when there are no network connections for at + * least {@code 500} ms. Don't use this method for testing, rely on web assertions to assess readiness instead.
  • *
  • {@code "commit"} - consider operation to be finished when network response is received and the document started loading.
  • *
*/ @@ -2340,8 +2380,8 @@ public WaitForURLOptions setTimeout(double timeout) { *
    *
  • {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.
  • *
  • {@code "load"} - consider operation to be finished when the {@code load} event is fired.
  • - *
  • {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} - * ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** consider operation to be finished when there are no network connections for at + * least {@code 500} ms. Don't use this method for testing, rely on web assertions to assess readiness instead.
  • *
  • {@code "commit"} - consider operation to be finished when network response is received and the document started loading.
  • *
*/ @@ -4849,7 +4889,8 @@ default JSHandle waitForFunction(String expression) { *
    *
  • {@code "load"} - wait for the {@code load} event to be fired.
  • *
  • {@code "domcontentloaded"} - wait for the {@code DOMContentLoaded} event to be fired.
  • - *
  • {@code "networkidle"} - wait until there are no network connections for at least {@code 500} ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** wait until there are no network connections for at least {@code 500} ms. Don't + * use this method for testing, rely on web assertions to assess readiness instead.
  • *
* @since v1.8 */ @@ -4890,7 +4931,8 @@ default void waitForLoadState() { *
    *
  • {@code "load"} - wait for the {@code load} event to be fired.
  • *
  • {@code "domcontentloaded"} - wait for the {@code DOMContentLoaded} event to be fired.
  • - *
  • {@code "networkidle"} - wait until there are no network connections for at least {@code 500} ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** wait until there are no network connections for at least {@code 500} ms. Don't + * use this method for testing, rely on web assertions to assess readiness instead.
  • *
* @since v1.8 */ diff --git a/playwright/src/main/java/com/microsoft/playwright/FrameLocator.java b/playwright/src/main/java/com/microsoft/playwright/FrameLocator.java index aad6e33e6..6f5088c66 100644 --- a/playwright/src/main/java/com/microsoft/playwright/FrameLocator.java +++ b/playwright/src/main/java/com/microsoft/playwright/FrameLocator.java @@ -291,6 +291,19 @@ class LocatorOptions { *

Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@code FrameLocator}s. */ public Locator has; + /** + * Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the + * outer one. For example, {@code article} that does not have {@code div} matches {@code + *

Playwright
}. + * + *

Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@code FrameLocator}s. + */ + public Locator hasNot; + /** + * Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element. When + * passed a [string], matching is case-insensitive and searches for a substring. + */ + public Object hasNotText; /** * Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When passed a * [string], matching is case-insensitive and searches for a substring. For example, {@code "Playwright"} matches {@code @@ -308,6 +321,33 @@ public LocatorOptions setHas(Locator has) { this.has = has; return this; } + /** + * Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the + * outer one. For example, {@code article} that does not have {@code div} matches {@code + *

Playwright
}. + * + *

Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@code FrameLocator}s. + */ + public LocatorOptions setHasNot(Locator hasNot) { + this.hasNot = hasNot; + return this; + } + /** + * Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element. When + * passed a [string], matching is case-insensitive and searches for a substring. + */ + public LocatorOptions setHasNotText(String hasNotText) { + this.hasNotText = hasNotText; + return this; + } + /** + * Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element. When + * passed a [string], matching is case-insensitive and searches for a substring. + */ + public LocatorOptions setHasNotText(Pattern hasNotText) { + this.hasNotText = hasNotText; + return this; + } /** * Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When passed a * [string], matching is case-insensitive and searches for a substring. For example, {@code "Playwright"} matches {@code diff --git a/playwright/src/main/java/com/microsoft/playwright/JSHandle.java b/playwright/src/main/java/com/microsoft/playwright/JSHandle.java index 28b12553a..65f473171 100644 --- a/playwright/src/main/java/com/microsoft/playwright/JSHandle.java +++ b/playwright/src/main/java/com/microsoft/playwright/JSHandle.java @@ -135,7 +135,7 @@ default JSHandle evaluateHandle(String expression) { * *

**Usage** *

{@code
-   * JSHandle handle = page.evaluateHandle("() => ({window, document}"););
+   * JSHandle handle = page.evaluateHandle("() => ({window, document})");
    * Map properties = handle.getProperties();
    * JSHandle windowHandle = properties.get("window");
    * JSHandle documentHandle = properties.get("document");
diff --git a/playwright/src/main/java/com/microsoft/playwright/Locator.java b/playwright/src/main/java/com/microsoft/playwright/Locator.java
index 776b5ad17..6a55b8ef8 100644
--- a/playwright/src/main/java/com/microsoft/playwright/Locator.java
+++ b/playwright/src/main/java/com/microsoft/playwright/Locator.java
@@ -662,6 +662,19 @@ class FilterOptions {
      * 

Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@code FrameLocator}s. */ public Locator has; + /** + * Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the + * outer one. For example, {@code article} that does not have {@code div} matches {@code + *

Playwright
}. + * + *

Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@code FrameLocator}s. + */ + public Locator hasNot; + /** + * Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element. When + * passed a [string], matching is case-insensitive and searches for a substring. + */ + public Object hasNotText; /** * Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When passed a * [string], matching is case-insensitive and searches for a substring. For example, {@code "Playwright"} matches {@code @@ -679,6 +692,33 @@ public FilterOptions setHas(Locator has) { this.has = has; return this; } + /** + * Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the + * outer one. For example, {@code article} that does not have {@code div} matches {@code + *

Playwright
}. + * + *

Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@code FrameLocator}s. + */ + public FilterOptions setHasNot(Locator hasNot) { + this.hasNot = hasNot; + return this; + } + /** + * Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element. When + * passed a [string], matching is case-insensitive and searches for a substring. + */ + public FilterOptions setHasNotText(String hasNotText) { + this.hasNotText = hasNotText; + return this; + } + /** + * Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element. When + * passed a [string], matching is case-insensitive and searches for a substring. + */ + public FilterOptions setHasNotText(Pattern hasNotText) { + this.hasNotText = hasNotText; + return this; + } /** * Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When passed a * [string], matching is case-insensitive and searches for a substring. For example, {@code "Playwright"} matches {@code @@ -1228,6 +1268,19 @@ class LocatorOptions { *

Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@code FrameLocator}s. */ public Locator has; + /** + * Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the + * outer one. For example, {@code article} that does not have {@code div} matches {@code + *

Playwright
}. + * + *

Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@code FrameLocator}s. + */ + public Locator hasNot; + /** + * Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element. When + * passed a [string], matching is case-insensitive and searches for a substring. + */ + public Object hasNotText; /** * Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When passed a * [string], matching is case-insensitive and searches for a substring. For example, {@code "Playwright"} matches {@code @@ -1245,6 +1298,33 @@ public LocatorOptions setHas(Locator has) { this.has = has; return this; } + /** + * Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the + * outer one. For example, {@code article} that does not have {@code div} matches {@code + *

Playwright
}. + * + *

Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@code FrameLocator}s. + */ + public LocatorOptions setHasNot(Locator hasNot) { + this.hasNot = hasNot; + return this; + } + /** + * Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element. When + * passed a [string], matching is case-insensitive and searches for a substring. + */ + public LocatorOptions setHasNotText(String hasNotText) { + this.hasNotText = hasNotText; + return this; + } + /** + * Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element. When + * passed a [string], matching is case-insensitive and searches for a substring. + */ + public LocatorOptions setHasNotText(Pattern hasNotText) { + this.hasNotText = hasNotText; + return this; + } /** * Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When passed a * [string], matching is case-insensitive and searches for a substring. For example, {@code "Playwright"} matches {@code @@ -3744,6 +3824,26 @@ default Locator locator(Locator selectorOrLocator) { * @since v1.14 */ Locator nth(int index); + /** + * Creates a locator that matches either of the two locators. + * + *

**Usage** + * + *

Consider a scenario where you'd like to click on a "New email" button, but sometimes a security settings dialog shows up + * instead. In this case, you can wait for either a "New email" button, or a dialog and act accordingly. + *

{@code
+   * Locator newEmail = page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("New"));
+   * Locator dialog = page.getByText("Confirm security settings");
+   * assertThat(newEmail.or(dialog)).isVisible();
+   * if (dialog.isVisible())
+   *   page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Dismiss")).click();
+   * newEmail.click();
+   * }
+ * + * @param locator Alternative locator to match. + * @since v1.33 + */ + Locator or(Locator locator); /** * A page this locator belongs to. * diff --git a/playwright/src/main/java/com/microsoft/playwright/Page.java b/playwright/src/main/java/com/microsoft/playwright/Page.java index febedcb0d..40a51b2d6 100644 --- a/playwright/src/main/java/com/microsoft/playwright/Page.java +++ b/playwright/src/main/java/com/microsoft/playwright/Page.java @@ -1391,8 +1391,8 @@ class GoBackOptions { *
    *
  • {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.
  • *
  • {@code "load"} - consider operation to be finished when the {@code load} event is fired.
  • - *
  • {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} - * ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** consider operation to be finished when there are no network connections for at + * least {@code 500} ms. Don't use this method for testing, rely on web assertions to assess readiness instead.
  • *
  • {@code "commit"} - consider operation to be finished when network response is received and the document started loading.
  • *
*/ @@ -1413,8 +1413,8 @@ public GoBackOptions setTimeout(double timeout) { *
    *
  • {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.
  • *
  • {@code "load"} - consider operation to be finished when the {@code load} event is fired.
  • - *
  • {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} - * ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** consider operation to be finished when there are no network connections for at + * least {@code 500} ms. Don't use this method for testing, rely on web assertions to assess readiness instead.
  • *
  • {@code "commit"} - consider operation to be finished when network response is received and the document started loading.
  • *
*/ @@ -1436,8 +1436,8 @@ class GoForwardOptions { *
    *
  • {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.
  • *
  • {@code "load"} - consider operation to be finished when the {@code load} event is fired.
  • - *
  • {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} - * ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** consider operation to be finished when there are no network connections for at + * least {@code 500} ms. Don't use this method for testing, rely on web assertions to assess readiness instead.
  • *
  • {@code "commit"} - consider operation to be finished when network response is received and the document started loading.
  • *
*/ @@ -1458,8 +1458,8 @@ public GoForwardOptions setTimeout(double timeout) { *
    *
  • {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.
  • *
  • {@code "load"} - consider operation to be finished when the {@code load} event is fired.
  • - *
  • {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} - * ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** consider operation to be finished when there are no network connections for at + * least {@code 500} ms. Don't use this method for testing, rely on web assertions to assess readiness instead.
  • *
  • {@code "commit"} - consider operation to be finished when network response is received and the document started loading.
  • *
*/ @@ -1486,8 +1486,8 @@ class NavigateOptions { *
    *
  • {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.
  • *
  • {@code "load"} - consider operation to be finished when the {@code load} event is fired.
  • - *
  • {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} - * ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** consider operation to be finished when there are no network connections for at + * least {@code 500} ms. Don't use this method for testing, rely on web assertions to assess readiness instead.
  • *
  • {@code "commit"} - consider operation to be finished when network response is received and the document started loading.
  • *
*/ @@ -1516,8 +1516,8 @@ public NavigateOptions setTimeout(double timeout) { *
    *
  • {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.
  • *
  • {@code "load"} - consider operation to be finished when the {@code load} event is fired.
  • - *
  • {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} - * ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** consider operation to be finished when there are no network connections for at + * least {@code 500} ms. Don't use this method for testing, rely on web assertions to assess readiness instead.
  • *
  • {@code "commit"} - consider operation to be finished when network response is received and the document started loading.
  • *
*/ @@ -1916,6 +1916,19 @@ class LocatorOptions { *

Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@code FrameLocator}s. */ public Locator has; + /** + * Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the + * outer one. For example, {@code article} that does not have {@code div} matches {@code + *

Playwright
}. + * + *

Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@code FrameLocator}s. + */ + public Locator hasNot; + /** + * Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element. When + * passed a [string], matching is case-insensitive and searches for a substring. + */ + public Object hasNotText; /** * Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When passed a * [string], matching is case-insensitive and searches for a substring. For example, {@code "Playwright"} matches {@code @@ -1933,6 +1946,33 @@ public LocatorOptions setHas(Locator has) { this.has = has; return this; } + /** + * Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the + * outer one. For example, {@code article} that does not have {@code div} matches {@code + *

Playwright
}. + * + *

Note that outer and inner locators must belong to the same frame. Inner locator must not contain {@code FrameLocator}s. + */ + public LocatorOptions setHasNot(Locator hasNot) { + this.hasNot = hasNot; + return this; + } + /** + * Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element. When + * passed a [string], matching is case-insensitive and searches for a substring. + */ + public LocatorOptions setHasNotText(String hasNotText) { + this.hasNotText = hasNotText; + return this; + } + /** + * Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element. When + * passed a [string], matching is case-insensitive and searches for a substring. + */ + public LocatorOptions setHasNotText(Pattern hasNotText) { + this.hasNotText = hasNotText; + return this; + } /** * Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When passed a * [string], matching is case-insensitive and searches for a substring. For example, {@code "Playwright"} matches {@code @@ -2204,8 +2244,8 @@ class ReloadOptions { *

    *
  • {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.
  • *
  • {@code "load"} - consider operation to be finished when the {@code load} event is fired.
  • - *
  • {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} - * ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** consider operation to be finished when there are no network connections for at + * least {@code 500} ms. Don't use this method for testing, rely on web assertions to assess readiness instead.
  • *
  • {@code "commit"} - consider operation to be finished when network response is received and the document started loading.
  • *
*/ @@ -2226,8 +2266,8 @@ public ReloadOptions setTimeout(double timeout) { *
    *
  • {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.
  • *
  • {@code "load"} - consider operation to be finished when the {@code load} event is fired.
  • - *
  • {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} - * ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** consider operation to be finished when there are no network connections for at + * least {@code 500} ms. Don't use this method for testing, rely on web assertions to assess readiness instead.
  • *
  • {@code "commit"} - consider operation to be finished when network response is received and the document started loading.
  • *
*/ @@ -2670,8 +2710,8 @@ class SetContentOptions { *
    *
  • {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.
  • *
  • {@code "load"} - consider operation to be finished when the {@code load} event is fired.
  • - *
  • {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} - * ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** consider operation to be finished when there are no network connections for at + * least {@code 500} ms. Don't use this method for testing, rely on web assertions to assess readiness instead.
  • *
  • {@code "commit"} - consider operation to be finished when network response is received and the document started loading.
  • *
*/ @@ -2692,8 +2732,8 @@ public SetContentOptions setTimeout(double timeout) { *
    *
  • {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.
  • *
  • {@code "load"} - consider operation to be finished when the {@code load} event is fired.
  • - *
  • {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} - * ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** consider operation to be finished when there are no network connections for at + * least {@code 500} ms. Don't use this method for testing, rely on web assertions to assess readiness instead.
  • *
  • {@code "commit"} - consider operation to be finished when network response is received and the document started loading.
  • *
*/ @@ -3204,8 +3244,8 @@ class WaitForNavigationOptions { *
    *
  • {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.
  • *
  • {@code "load"} - consider operation to be finished when the {@code load} event is fired.
  • - *
  • {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} - * ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** consider operation to be finished when there are no network connections for at + * least {@code 500} ms. Don't use this method for testing, rely on web assertions to assess readiness instead.
  • *
  • {@code "commit"} - consider operation to be finished when network response is received and the document started loading.
  • *
*/ @@ -3253,8 +3293,8 @@ public WaitForNavigationOptions setUrl(Predicate url) { *
    *
  • {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.
  • *
  • {@code "load"} - consider operation to be finished when the {@code load} event is fired.
  • - *
  • {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} - * ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** consider operation to be finished when there are no network connections for at + * least {@code 500} ms. Don't use this method for testing, rely on web assertions to assess readiness instead.
  • *
  • {@code "commit"} - consider operation to be finished when network response is received and the document started loading.
  • *
*/ @@ -3440,8 +3480,8 @@ class WaitForURLOptions { *
    *
  • {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.
  • *
  • {@code "load"} - consider operation to be finished when the {@code load} event is fired.
  • - *
  • {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} - * ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** consider operation to be finished when there are no network connections for at + * least {@code 500} ms. Don't use this method for testing, rely on web assertions to assess readiness instead.
  • *
  • {@code "commit"} - consider operation to be finished when network response is received and the document started loading.
  • *
*/ @@ -3462,8 +3502,8 @@ public WaitForURLOptions setTimeout(double timeout) { *
    *
  • {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.
  • *
  • {@code "load"} - consider operation to be finished when the {@code load} event is fired.
  • - *
  • {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} - * ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** consider operation to be finished when there are no network connections for at + * least {@code 500} ms. Don't use this method for testing, rely on web assertions to assess readiness instead.
  • *
  • {@code "commit"} - consider operation to be finished when network response is received and the document started loading.
  • *
*/ @@ -7251,7 +7291,8 @@ default JSHandle waitForFunction(String expression) { *
    *
  • {@code "load"} - wait for the {@code load} event to be fired.
  • *
  • {@code "domcontentloaded"} - wait for the {@code DOMContentLoaded} event to be fired.
  • - *
  • {@code "networkidle"} - wait until there are no network connections for at least {@code 500} ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** wait until there are no network connections for at least {@code 500} ms. Don't + * use this method for testing, rely on web assertions to assess readiness instead.
  • *
* @since v1.8 */ @@ -7308,7 +7349,8 @@ default void waitForLoadState() { *
    *
  • {@code "load"} - wait for the {@code load} event to be fired.
  • *
  • {@code "domcontentloaded"} - wait for the {@code DOMContentLoaded} event to be fired.
  • - *
  • {@code "networkidle"} - wait until there are no network connections for at least {@code 500} ms.
  • + *
  • {@code "networkidle"} - **DISCOURAGED** wait until there are no network connections for at least {@code 500} ms. Don't + * use this method for testing, rely on web assertions to assess readiness instead.
  • *
* @since v1.8 */ 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 bc2e1676f..d02b3526e 100644 --- a/playwright/src/main/java/com/microsoft/playwright/assertions/LocatorAssertions.java +++ b/playwright/src/main/java/com/microsoft/playwright/assertions/LocatorAssertions.java @@ -37,6 +37,25 @@ * }
*/ public interface LocatorAssertions { + class IsAttachedOptions { + public Boolean attached; + /** + * Time to retry the assertion for. + */ + public Double timeout; + + public IsAttachedOptions setAttached(boolean attached) { + this.attached = attached; + return this; + } + /** + * Time to retry the assertion for. + */ + public IsAttachedOptions setTimeout(double timeout) { + this.timeout = timeout; + return this; + } + } class IsCheckedOptions { public Boolean checked; /** @@ -394,6 +413,32 @@ public HasValuesOptions setTimeout(double timeout) { * @since v1.20 */ LocatorAssertions not(); + /** + * Ensures that {@code Locator} points to an attached + * DOM node. + * + *

**Usage** + *

{@code
+   * assertThat(page.getByText("Hidden text")).isAttached();
+   * }
+ * + * @since v1.33 + */ + default void isAttached() { + isAttached(null); + } + /** + * Ensures that {@code Locator} points to an attached + * DOM node. + * + *

**Usage** + *

{@code
+   * assertThat(page.getByText("Hidden text")).isAttached();
+   * }
+ * + * @since v1.33 + */ + void isAttached(IsAttachedOptions options); /** * Ensures the {@code Locator} points to a checked input. * @@ -618,7 +663,7 @@ default void isInViewport() { * *

**Usage** *

{@code
-   * assertThat(page.locator(".my-element")).isVisible();
+   * assertThat(page.getByText("Welcome")).isVisible();
    * }
* * @since v1.20 @@ -632,7 +677,7 @@ default void isVisible() { * *

**Usage** *

{@code
-   * assertThat(page.locator(".my-element")).isVisible();
+   * assertThat(page.getByText("Welcome")).isVisible();
    * }
* * @since v1.20 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 4e427e092..3b8213188 100644 --- a/playwright/src/main/java/com/microsoft/playwright/impl/LocatorAssertionsImpl.java +++ b/playwright/src/main/java/com/microsoft/playwright/impl/LocatorAssertionsImpl.java @@ -357,6 +357,14 @@ public LocatorAssertions not() { return new LocatorAssertionsImpl(actualLocator, !isNot); } + @Override + public void isAttached(IsAttachedOptions options) { + FrameExpectOptions frameOptions = convertType(options, FrameExpectOptions.class); + boolean attached = options == null || options.attached == null || options.attached == true; + String message = "Locator expected to be " + (attached ? "attached" : "detached"); + expectTrue(attached ? "to.be.attached" : "to.be.detached", message, frameOptions); + } + private static Boolean shouldIgnoreCase(Object options) { if (options == null) { return null; diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/LocatorImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/LocatorImpl.java index 210cd1e73..826581d61 100644 --- a/playwright/src/main/java/com/microsoft/playwright/impl/LocatorImpl.java +++ b/playwright/src/main/java/com/microsoft/playwright/impl/LocatorImpl.java @@ -29,12 +29,21 @@ public LocatorImpl(FrameImpl frame, String selector, LocatorOptions options) { if (options.hasText != null) { selector += " >> internal:has-text=" + escapeForTextSelector(options.hasText, false); } + if (options.hasNotText != null) { + selector += " >> internal:has-not-text=" + escapeForTextSelector(options.hasNotText, false); + } if (options.has != null) { LocatorImpl locator = (LocatorImpl) options.has; if (locator.frame != frame) throw new Error("Inner 'has' locator must belong to the same frame."); selector += " >> internal:has=" + gson().toJson(locator.selector); } + if (options.hasNot != null) { + LocatorImpl locator = (LocatorImpl) options.hasNot; + if (locator.frame != frame) + throw new Error("Inner 'hasNot' locator must belong to the same frame."); + selector += " >> internal:has-not=" + gson().toJson(locator.selector); + } } this.selector = selector; } @@ -398,6 +407,14 @@ public Locator nth(int index) { return new LocatorImpl(frame, selector + " >> nth=" + index, null); } + @Override + public Locator or(Locator locator) { + LocatorImpl other = (LocatorImpl) locator; + if (other.frame != frame) + throw new Error("Locators must belong to the same frame."); + return new LocatorImpl(frame, selector + " >> internal:or=" + gson().toJson(other.selector), null); + } + @Override public Page page() { return frame.page(); diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/RouteImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/RouteImpl.java index 92425389e..921244c7d 100644 --- a/playwright/src/main/java/com/microsoft/playwright/impl/RouteImpl.java +++ b/playwright/src/main/java/com/microsoft/playwright/impl/RouteImpl.java @@ -47,6 +47,7 @@ public void abort(String errorCode) { withLogging("Route.abort", () -> { JsonObject params = new JsonObject(); params.addProperty("errorCode", errorCode); + params.addProperty("requestUrl", request.initializer.get("url").getAsString()); sendMessageAsync("abort", params); }); } @@ -95,7 +96,7 @@ public APIResponse fetch(FetchOptions fetchOptions) { String url = (fetchOptions == null || fetchOptions.url == null) ? request().url() : fetchOptions.url; return apiRequest.fetch(url, options); } - + private void applyOverrides(FallbackOptions options) { if (options == null) { return; @@ -127,6 +128,7 @@ private void resumeImpl(RequestImpl.FallbackOverrides options) { params.addProperty("postData", base64); } } + params.addProperty("requestUrl", request.initializer.get("url").getAsString()); sendMessageAsync("continue", params); } @@ -221,6 +223,7 @@ private void fulfillImpl(FulfillOptions options) { if (fetchResponseUid != null) { params.addProperty("fetchResponseUid", fetchResponseUid); } + params.addProperty("requestUrl", request.initializer.get("url").getAsString()); sendMessageAsync("fulfill", params); } diff --git a/playwright/src/main/java/com/microsoft/playwright/options/HttpCredentials.java b/playwright/src/main/java/com/microsoft/playwright/options/HttpCredentials.java index 98ce13356..5aea74a0d 100644 --- a/playwright/src/main/java/com/microsoft/playwright/options/HttpCredentials.java +++ b/playwright/src/main/java/com/microsoft/playwright/options/HttpCredentials.java @@ -19,9 +19,20 @@ public class HttpCredentials { public String username; public String password; + /** + * Restrain sending http credentials on specific origin (scheme://host:port). + */ + public String origin; public HttpCredentials(String username, String password) { this.username = username; this.password = password; } + /** + * Restrain sending http credentials on specific origin (scheme://host:port). + */ + public HttpCredentials setOrigin(String origin) { + this.origin = origin; + return this; + } } \ No newline at end of file diff --git a/playwright/src/main/java/com/microsoft/playwright/options/Timing.java b/playwright/src/main/java/com/microsoft/playwright/options/Timing.java index ff975b3ff..0415468d1 100644 --- a/playwright/src/main/java/com/microsoft/playwright/options/Timing.java +++ b/playwright/src/main/java/com/microsoft/playwright/options/Timing.java @@ -52,8 +52,8 @@ public class Timing { */ public double requestStart; /** - * Time immediately after the browser starts requesting the resource from the server, cache, or local resource. The value - * is given in milliseconds relative to {@code startTime}, -1 if not available. + * Time immediately after the browser receives the first byte of the response from the server, cache, or local resource. + * The value is given in milliseconds relative to {@code startTime}, -1 if not available. */ public double responseStart; /** diff --git a/playwright/src/test/java/com/microsoft/playwright/TestLocatorAssertions2.java b/playwright/src/test/java/com/microsoft/playwright/TestLocatorAssertions2.java new file mode 100644 index 000000000..4f41102f2 --- /dev/null +++ b/playwright/src/test/java/com/microsoft/playwright/TestLocatorAssertions2.java @@ -0,0 +1,102 @@ +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.*; + +public class TestLocatorAssertions2 extends TestBase { + @Test + void isAttachedDefault() { + page.setContent(""); + Locator locator = page.locator("input"); + assertThat(locator).isAttached(); + } + + @Test + void isAttachedWithHiddenElement() { + page.setContent(""); + Locator locator = page.locator("button"); + assertThat(locator).isAttached(); + } + + @Test + void isAttachedWithNot() { + page.setContent(""); + Locator locator = page.locator("input"); + assertThat(locator).not().isAttached(); + } + + @Test + void isAttachedWithAttachedTrue() { + page.setContent(""); + Locator locator = page.locator("button"); + assertThat(locator).isAttached(new LocatorAssertions.IsAttachedOptions().setAttached(true)); + } + + @Test + void isAttachedWithAttachedFalse() { + page.setContent(""); + Locator locator = page.locator("input"); + assertThat(locator).isAttached(new LocatorAssertions.IsAttachedOptions().setAttached(false)); + } + + @Test + void isAttachedWithNotAndAttachedFalse() { + page.setContent(""); + Locator locator = page.locator("button"); + assertThat(locator).not().isAttached(new LocatorAssertions.IsAttachedOptions().setAttached(false)); + } + + @Test + void isAttachedEventually() { + page.setContent("
"); + Locator locator = page.locator("span"); + page.evalOnSelector("div", "div => setTimeout(() => {\n" + + " div.innerHTML = 'Hello'\n" + + " }, 100)"); + assertThat(locator).isAttached(); + } + + @Test + void isAttachedEventuallyWithNot() { + page.setContent("
Hello
"); + Locator locator = page.locator("span"); + page.evalOnSelector("div", "div => setTimeout(() => {\n" + + " div.textContent = '';\n" + + " }, 0)"); + assertThat(locator).not().isAttached(); + } + + @Test + void isAttachedFail() { + page.setContent(""); + Locator locator = page.locator("input"); + AssertionFailedError error = assertThrows(AssertionFailedError.class, + () -> assertThat(locator).isAttached(new LocatorAssertions.IsAttachedOptions().setTimeout(1000))); + assertFalse(error.getMessage().contains("locator resolved to"), error.getMessage()); + } + + @Test + void isAttachedFailWithNot() { + page.setContent(""); + Locator locator = page.locator("input"); + AssertionFailedError error = assertThrows(AssertionFailedError.class, + () -> assertThat(locator).not().isAttached(new LocatorAssertions.IsAttachedOptions().setTimeout(1000))); + assertTrue(error.getMessage().contains("locator resolved to "), error.getMessage()); + } + + @Test + void isAttachedWithImpossibleTimeout() { + page.setContent("
Text content
"); + assertThat(page.locator("#node")).isAttached(new LocatorAssertions.IsAttachedOptions().setTimeout(1)); + } + + @Test + void isAttachedWithImpossibleTimeoutNot() { + page.setContent("
Text content
"); + assertThat(page.locator("no-such-thing")).not().isAttached(new LocatorAssertions.IsAttachedOptions().setTimeout(1)); + } +} diff --git a/playwright/src/test/java/com/microsoft/playwright/TestPageLocatorQuery.java b/playwright/src/test/java/com/microsoft/playwright/TestPageLocatorQuery.java index 575fb3b91..37eb6c2ed 100644 --- a/playwright/src/test/java/com/microsoft/playwright/TestPageLocatorQuery.java +++ b/playwright/src/test/java/com/microsoft/playwright/TestPageLocatorQuery.java @@ -172,5 +172,26 @@ void shouldSupportLocatorFilter() { assertThat(page.locator("div").filter(new Locator.FilterOptions() .setHas(page.locator("span")) .setHasText("world"))).hasCount(1); + assertThat(page.locator("div").filter(new Locator.FilterOptions() + .setHasNot(page.locator("span", new Page.LocatorOptions().setHasText("world"))))).hasCount(1); + assertThat(page.locator("div").filter(new Locator.FilterOptions() + .setHasNot(page.locator("section")))).hasCount(2); + assertThat(page.locator("div").filter(new Locator.FilterOptions() + .setHasNot(page.locator("span")))).hasCount(0); + assertThat(page.locator("div").filter(new Locator.FilterOptions().setHasNotText("hello"))).hasCount(1); + assertThat(page.locator("div").filter(new Locator.FilterOptions().setHasNotText("foo"))).hasCount(2); + } + + @Test + void shouldSupportLocatorOr() { + page.setContent("
hello
world"); + assertThat(page.locator("div").or(page.locator("span"))).hasCount(2); + assertThat(page.locator("div").or(page.locator("span"))).hasText(new String[]{"hello", "world"}); + assertThat(page.locator("span").or(page.locator("article")).or(page.locator("div"))).hasText(new String[]{"hello", "world"}); + assertThat(page.locator("article").or(page.locator("someting"))).hasCount(0); + assertThat(page.locator("article").or(page.locator("div"))).hasText("hello"); + assertThat(page.locator("article").or(page.locator("span"))).hasText("world"); + assertThat(page.locator("div").or(page.locator("article"))).hasText("hello"); + assertThat(page.locator("span").or(page.locator("article"))).hasText("world"); } } diff --git a/playwright/src/test/java/com/microsoft/playwright/TestSelectorsMisc.java b/playwright/src/test/java/com/microsoft/playwright/TestSelectorsMisc.java index 7faaa045c..0235929c1 100644 --- a/playwright/src/test/java/com/microsoft/playwright/TestSelectorsMisc.java +++ b/playwright/src/test/java/com/microsoft/playwright/TestSelectorsMisc.java @@ -2,6 +2,7 @@ import org.junit.jupiter.api.Test; +import static java.util.Arrays.asList; import static org.junit.jupiter.api.Assertions.*; public class TestSelectorsMisc extends TestBase { @@ -194,4 +195,28 @@ void shouldWorkWithLayoutSelectors() { e = assertThrows(PlaywrightException.class, () -> page.querySelector("div >> left-of='span',3,4")); assertTrue(e.getMessage().contains("Malformed selector: left-of='span',3,4")); } + + @Test + void shouldWorkWithInternalHasNot() { + page.setContent("

"); + assertEquals(1, page.evalOnSelectorAll("section >> internal:has-not=\"span\"", "els => els.length")); + assertEquals(0, page.evalOnSelectorAll("section >> internal:has-not=\"span, div, br\"", "els => els.length")); + assertEquals(1, page.evalOnSelectorAll("section >> internal:has-not=\"br\"", "els => els.length")); + assertEquals(1, page.evalOnSelectorAll("section >> internal:has-not=\"span, div\"", "els => els.length")); + assertEquals(2, page.evalOnSelectorAll("section >> internal:has-not=\"article\"", "els => els.length")); + } + + + @Test + void shouldWorkWithInternalOr() { + page.setContent("
hello
\n" + + " world"); + assertEquals(asList("hello", "world"), page.evalOnSelectorAll("div >> internal:or=\"span\"", "els => els.map(e => e.textContent)")); + assertEquals(asList("hello", "world"), page.evalOnSelectorAll("span >> internal:or=\"div\"", "els => els.map(e => e.textContent)")); + assertEquals(0, page.evalOnSelectorAll("article >> internal:or=\"something\"", "els => els.length")); + assertEquals("hello", page.locator("article >> internal:or=\"div\"").textContent()); + assertEquals("world", page.locator("article >> internal:or=\"span\"").textContent()); + assertEquals("hello", page.locator("div >> internal:or=\"article\"").textContent()); + assertEquals("world", page.locator("span >> internal:or=\"article\"").textContent()); + } } diff --git a/scripts/CLI_VERSION b/scripts/CLI_VERSION index 96cd6ee1e..aa715a948 100644 --- a/scripts/CLI_VERSION +++ b/scripts/CLI_VERSION @@ -1 +1 @@ -1.32.1 +1.33.0-alpha-apr-12-2023