diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 384d24d2..92ddefc6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,6 +30,8 @@ jobs: run: scripts/download_driver.sh - name: Build & Install run: mvn -B install -D skipTests --no-transfer-progress + - name: Install browsers + run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps" -f playwright/pom.xml - name: Run tests run: mvn test --no-transfer-progress --fail-at-end -D org.slf4j.simpleLogger.showDateTime=true -D org.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss env: @@ -78,6 +80,8 @@ jobs: run: scripts/download_driver.sh - name: Build & Install run: mvn -B install -D skipTests --no-transfer-progress + - name: Install browsers + run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps" -f playwright/pom.xml - name: Install MS Edge if: matrix.browser-channel == 'msedge' && matrix.os == 'macos-latest' shell: bash @@ -108,6 +112,8 @@ jobs: run: scripts/download_driver.sh - name: Build & Install run: mvn -B install -D skipTests --no-transfer-progress + - name: Install browsers + run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps" -f playwright/pom.xml - name: Run tests run: mvn test --no-transfer-progress --fail-at-end env: diff --git a/.github/workflows/verify_api.yml b/.github/workflows/verify_api.yml index 39834549..a692f84a 100644 --- a/.github/workflows/verify_api.yml +++ b/.github/workflows/verify_api.yml @@ -25,6 +25,10 @@ jobs: run: scripts/download_driver.sh - name: Regenerate APIs run: scripts/generate_api.sh + - name: Build & Install + run: mvn -B install -D skipTests --no-transfer-progress + - name: Install browsers + run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps" -f playwright/pom.xml - name: Update browser versions in README run: scripts/update_readme.sh - name: Verify API is up to date diff --git a/README.md b/README.md index b5a64ce7..af58b7c9 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,9 @@ Playwright is a Java library to automate [Chromium](https://www.chromium.org/Hom | | Linux | macOS | Windows | | :--- | :---: | :---: | :---: | -| Chromium 130.0.6723.31 | :white_check_mark: | :white_check_mark: | :white_check_mark: | -| WebKit 18.0 | ✅ | ✅ | ✅ | -| Firefox 131.0 | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Chromium 131.0.6778.33 | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| WebKit 18.2 | ✅ | ✅ | ✅ | +| Firefox 132.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/intro#system-requirements) for details. diff --git a/playwright/src/main/java/com/microsoft/playwright/Browser.java b/playwright/src/main/java/com/microsoft/playwright/Browser.java index 34f58217..2860249f 100644 --- a/playwright/src/main/java/com/microsoft/playwright/Browser.java +++ b/playwright/src/main/java/com/microsoft/playwright/Browser.java @@ -29,15 +29,15 @@ * import com.microsoft.playwright.*; * * public class Example { - * public static void main(String[] args) { - * try (Playwright playwright = Playwright.create()) { - * BrowserType firefox = playwright.firefox() - * Browser browser = firefox.launch(); - * Page page = browser.newPage(); - * page.navigate('https://example.com'); - * browser.close(); - * } - * } + * public static void main(String[] args) { + * try (Playwright playwright = Playwright.create()) { + * BrowserType firefox = playwright.firefox(); + * Browser browser = firefox.launch(); + * Page page = browser.newPage(); + * page.navigate("https://example.com"); + * browser.close(); + * } + * } * } * } */ @@ -111,9 +111,11 @@ class NewContextOptions { */ public List clientCertificates; /** - * Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code - * "no-preference"}. See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing - * {@code null} resets emulation to system defaults. Defaults to {@code "light"}. + * Emulates prefers-colors-scheme media + * feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia + * Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code + * "light"}. */ public Optional colorScheme; /** @@ -323,9 +325,11 @@ public NewContextOptions setClientCertificates(List clientCer return this; } /** - * Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code - * "no-preference"}. See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing - * {@code null} resets emulation to system defaults. Defaults to {@code "light"}. + * Emulates prefers-colors-scheme media + * feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia + * Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code + * "light"}. */ public NewContextOptions setColorScheme(ColorScheme colorScheme) { this.colorScheme = Optional.ofNullable(colorScheme); @@ -660,9 +664,11 @@ class NewPageOptions { */ public List clientCertificates; /** - * Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code - * "no-preference"}. See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing - * {@code null} resets emulation to system defaults. Defaults to {@code "light"}. + * Emulates prefers-colors-scheme media + * feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia + * Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code + * "light"}. */ public Optional colorScheme; /** @@ -872,9 +878,11 @@ public NewPageOptions setClientCertificates(List clientCertif return this; } /** - * Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code - * "no-preference"}. See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing - * {@code null} resets emulation to system defaults. Defaults to {@code "light"}. + * Emulates prefers-colors-scheme media + * feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia + * Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code + * "light"}. */ public NewPageOptions setColorScheme(ColorScheme colorScheme) { this.colorScheme = Optional.ofNullable(colorScheme); @@ -1289,7 +1297,7 @@ default void close() { * BrowserContext context = browser.newContext(); * // Create a new page in a pristine context. * Page page = context.newPage(); - * page.navigate('https://example.com'); + * page.navigate("https://example.com"); * * // Graceful close up everything * context.close(); @@ -1316,7 +1324,7 @@ default BrowserContext newContext() { * BrowserContext context = browser.newContext(); * // Create a new page in a pristine context. * Page page = context.newPage(); - * page.navigate('https://example.com'); + * page.navigate("https://example.com"); * * // Graceful close up everything * context.close(); @@ -1364,7 +1372,7 @@ default Page newPage() { *
{@code
    * browser.startTracing(page, new Browser.StartTracingOptions()
    *   .setPath(Paths.get("trace.json")));
-   * page.goto('https://www.google.com');
+   * page.navigate("https://www.google.com");
    * browser.stopTracing();
    * }
* @@ -1388,7 +1396,7 @@ default void startTracing(Page page) { *
{@code
    * browser.startTracing(page, new Browser.StartTracingOptions()
    *   .setPath(Paths.get("trace.json")));
-   * page.goto('https://www.google.com');
+   * page.navigate("https://www.google.com");
    * browser.stopTracing();
    * }
* @@ -1411,7 +1419,7 @@ default void startTracing() { *
{@code
    * browser.startTracing(page, new Browser.StartTracingOptions()
    *   .setPath(Paths.get("trace.json")));
-   * page.goto('https://www.google.com');
+   * page.navigate("https://www.google.com");
    * browser.stopTracing();
    * }
* diff --git a/playwright/src/main/java/com/microsoft/playwright/BrowserContext.java b/playwright/src/main/java/com/microsoft/playwright/BrowserContext.java index 005345c6..b0e7c9c9 100644 --- a/playwright/src/main/java/com/microsoft/playwright/BrowserContext.java +++ b/playwright/src/main/java/com/microsoft/playwright/BrowserContext.java @@ -701,7 +701,7 @@ default List cookies() { * public class Example { * public static void main(String[] args) { * try (Playwright playwright = Playwright.create()) { - * BrowserType webkit = playwright.webkit() + * BrowserType webkit = playwright.webkit(); * Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false)); * BrowserContext context = browser.newContext(); * context.exposeBinding("pageURL", (source, args) -> source.page().url()); @@ -748,7 +748,7 @@ default void exposeBinding(String name, BindingCallback callback) { * public class Example { * public static void main(String[] args) { * try (Playwright playwright = Playwright.create()) { - * BrowserType webkit = playwright.webkit() + * BrowserType webkit = playwright.webkit(); * Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false)); * BrowserContext context = browser.newContext(); * context.exposeBinding("pageURL", (source, args) -> source.page().url()); @@ -797,8 +797,9 @@ default void exposeBinding(String name, BindingCallback callback) { * public class Example { * public static void main(String[] args) { * try (Playwright playwright = Playwright.create()) { - * BrowserType webkit = playwright.webkit() + * BrowserType webkit = playwright.webkit(); * Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false)); + * BrowserContext context = browser.newContext(); * context.exposeFunction("sha256", args -> { * String text = (String) args[0]; * MessageDigest crypto; diff --git a/playwright/src/main/java/com/microsoft/playwright/BrowserType.java b/playwright/src/main/java/com/microsoft/playwright/BrowserType.java index 827d4868..21df1b68 100644 --- a/playwright/src/main/java/com/microsoft/playwright/BrowserType.java +++ b/playwright/src/main/java/com/microsoft/playwright/BrowserType.java @@ -470,9 +470,11 @@ class LaunchPersistentContextOptions { */ public List clientCertificates; /** - * Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code - * "no-preference"}. See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing - * {@code null} resets emulation to system defaults. Defaults to {@code "light"}. + * Emulates prefers-colors-scheme media + * feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia + * Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code + * "light"}. */ public Optional colorScheme; /** @@ -774,9 +776,11 @@ public LaunchPersistentContextOptions setClientCertificates(Listprefers-colors-scheme media + * feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia + * Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code + * "light"}. */ public LaunchPersistentContextOptions setColorScheme(ColorScheme colorScheme) { this.colorScheme = Optional.ofNullable(colorScheme); diff --git a/playwright/src/main/java/com/microsoft/playwright/Clock.java b/playwright/src/main/java/com/microsoft/playwright/Clock.java index 4cd6e454..52c64d34 100644 --- a/playwright/src/main/java/com/microsoft/playwright/Clock.java +++ b/playwright/src/main/java/com/microsoft/playwright/Clock.java @@ -227,6 +227,10 @@ default void install() { /** * Makes {@code Date.now} and {@code new Date()} return fixed fake time at all times, keeps all the timers running. * + *

Use this method for simple scenarios where you only need to test with a predefined time. For more advanced scenarios, + * use {@link com.microsoft.playwright.Clock#install Clock.install()} instead. Read docs on clock emulation to learn more. + * *

Usage *

{@code
    * page.clock().setFixedTime(new Date());
@@ -241,6 +245,10 @@ default void install() {
   /**
    * Makes {@code Date.now} and {@code new Date()} return fixed fake time at all times, keeps all the timers running.
    *
+   * 

Use this method for simple scenarios where you only need to test with a predefined time. For more advanced scenarios, + * use {@link com.microsoft.playwright.Clock#install Clock.install()} instead. Read docs on clock emulation to learn more. + * *

Usage *

{@code
    * page.clock().setFixedTime(new Date());
@@ -255,6 +263,10 @@ default void install() {
   /**
    * Makes {@code Date.now} and {@code new Date()} return fixed fake time at all times, keeps all the timers running.
    *
+   * 

Use this method for simple scenarios where you only need to test with a predefined time. For more advanced scenarios, + * use {@link com.microsoft.playwright.Clock#install Clock.install()} instead. Read docs on clock emulation to learn more. + * *

Usage *

{@code
    * page.clock().setFixedTime(new Date());
@@ -267,7 +279,8 @@ default void install() {
    */
   void setFixedTime(Date time);
   /**
-   * Sets current system time but does not trigger any timers.
+   * Sets system time, but does not trigger any timers. Use this to test how the web page reacts to a time shift, for example
+   * switching from summer to winter time, or changing time zones.
    *
    * 

Usage *

{@code
@@ -281,7 +294,8 @@ default void install() {
    */
   void setSystemTime(long time);
   /**
-   * Sets current system time but does not trigger any timers.
+   * Sets system time, but does not trigger any timers. Use this to test how the web page reacts to a time shift, for example
+   * switching from summer to winter time, or changing time zones.
    *
    * 

Usage *

{@code
@@ -295,7 +309,8 @@ default void install() {
    */
   void setSystemTime(String time);
   /**
-   * Sets current system time but does not trigger any timers.
+   * Sets system time, but does not trigger any timers. Use this to test how the web page reacts to a time shift, for example
+   * switching from summer to winter time, or changing time zones.
    *
    * 

Usage *

{@code
diff --git a/playwright/src/main/java/com/microsoft/playwright/ConsoleMessage.java b/playwright/src/main/java/com/microsoft/playwright/ConsoleMessage.java
index 4d1aea69..663092f3 100644
--- a/playwright/src/main/java/com/microsoft/playwright/ConsoleMessage.java
+++ b/playwright/src/main/java/com/microsoft/playwright/ConsoleMessage.java
@@ -39,8 +39,8 @@
  * });
  *
  * // Deconstruct console.log arguments
- * msg.args().get(0).jsonValue() // hello
- * msg.args().get(1).jsonValue() // 42
+ * msg.args().get(0).jsonValue(); // hello
+ * msg.args().get(1).jsonValue(); // 42
  * }
*/ public interface ConsoleMessage { diff --git a/playwright/src/main/java/com/microsoft/playwright/Frame.java b/playwright/src/main/java/com/microsoft/playwright/Frame.java index d732b829..4e6b35c2 100644 --- a/playwright/src/main/java/com/microsoft/playwright/Frame.java +++ b/playwright/src/main/java/com/microsoft/playwright/Frame.java @@ -3499,19 +3499,19 @@ default Locator getByRole(AriaRole role) { *

You can locate by text substring, exact string, or a regular expression: *

{@code
    * // Matches 
-   * page.getByText("world")
+   * page.getByText("world");
    *
    * // Matches first 
- * page.getByText("Hello world") + * page.getByText("Hello world"); * * // Matches second
- * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)) + * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)); * * // Matches both
s - * page.getByText(Pattern.compile("Hello")) + * page.getByText(Pattern.compile("Hello")); * * // Matches second
- * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)) + * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)); * }
* *

Details @@ -3541,19 +3541,19 @@ default Locator getByText(String text) { *

You can locate by text substring, exact string, or a regular expression: *

{@code
    * // Matches 
-   * page.getByText("world")
+   * page.getByText("world");
    *
    * // Matches first 
- * page.getByText("Hello world") + * page.getByText("Hello world"); * * // Matches second
- * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)) + * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)); * * // Matches both
s - * page.getByText(Pattern.compile("Hello")) + * page.getByText(Pattern.compile("Hello")); * * // Matches second
- * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)) + * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)); * }
* *

Details @@ -3581,19 +3581,19 @@ default Locator getByText(String text) { *

You can locate by text substring, exact string, or a regular expression: *

{@code
    * // Matches 
-   * page.getByText("world")
+   * page.getByText("world");
    *
    * // Matches first 
- * page.getByText("Hello world") + * page.getByText("Hello world"); * * // Matches second
- * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)) + * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)); * * // Matches both
s - * page.getByText(Pattern.compile("Hello")) + * page.getByText(Pattern.compile("Hello")); * * // Matches second
- * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)) + * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)); * }
* *

Details @@ -3623,19 +3623,19 @@ default Locator getByText(Pattern text) { *

You can locate by text substring, exact string, or a regular expression: *

{@code
    * // Matches 
-   * page.getByText("world")
+   * page.getByText("world");
    *
    * // Matches first 
- * page.getByText("Hello world") + * page.getByText("Hello world"); * * // Matches second
- * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)) + * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)); * * // Matches both
s - * page.getByText(Pattern.compile("Hello")) + * page.getByText(Pattern.compile("Hello")); * * // Matches second
- * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)) + * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)); * }
* *

Details diff --git a/playwright/src/main/java/com/microsoft/playwright/FrameLocator.java b/playwright/src/main/java/com/microsoft/playwright/FrameLocator.java index 0fe625a3..00746021 100644 --- a/playwright/src/main/java/com/microsoft/playwright/FrameLocator.java +++ b/playwright/src/main/java/com/microsoft/playwright/FrameLocator.java @@ -734,19 +734,19 @@ default Locator getByRole(AriaRole role) { *

You can locate by text substring, exact string, or a regular expression: *

{@code
    * // Matches 
-   * page.getByText("world")
+   * page.getByText("world");
    *
    * // Matches first 
- * page.getByText("Hello world") + * page.getByText("Hello world"); * * // Matches second
- * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)) + * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)); * * // Matches both
s - * page.getByText(Pattern.compile("Hello")) + * page.getByText(Pattern.compile("Hello")); * * // Matches second
- * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)) + * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)); * }
* *

Details @@ -776,19 +776,19 @@ default Locator getByText(String text) { *

You can locate by text substring, exact string, or a regular expression: *

{@code
    * // Matches 
-   * page.getByText("world")
+   * page.getByText("world");
    *
    * // Matches first 
- * page.getByText("Hello world") + * page.getByText("Hello world"); * * // Matches second
- * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)) + * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)); * * // Matches both
s - * page.getByText(Pattern.compile("Hello")) + * page.getByText(Pattern.compile("Hello")); * * // Matches second
- * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)) + * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)); * }
* *

Details @@ -816,19 +816,19 @@ default Locator getByText(String text) { *

You can locate by text substring, exact string, or a regular expression: *

{@code
    * // Matches 
-   * page.getByText("world")
+   * page.getByText("world");
    *
    * // Matches first 
- * page.getByText("Hello world") + * page.getByText("Hello world"); * * // Matches second
- * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)) + * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)); * * // Matches both
s - * page.getByText(Pattern.compile("Hello")) + * page.getByText(Pattern.compile("Hello")); * * // Matches second
- * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)) + * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)); * }
* *

Details @@ -858,19 +858,19 @@ default Locator getByText(Pattern text) { *

You can locate by text substring, exact string, or a regular expression: *

{@code
    * // Matches 
-   * page.getByText("world")
+   * page.getByText("world");
    *
    * // Matches first 
- * page.getByText("Hello world") + * page.getByText("Hello world"); * * // Matches second
- * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)) + * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)); * * // Matches both
s - * page.getByText(Pattern.compile("Hello")) + * page.getByText(Pattern.compile("Hello")); * * // Matches second
- * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)) + * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)); * }
* *

Details diff --git a/playwright/src/main/java/com/microsoft/playwright/Keyboard.java b/playwright/src/main/java/com/microsoft/playwright/Keyboard.java index 56cea175..6ef8c0b5 100644 --- a/playwright/src/main/java/com/microsoft/playwright/Keyboard.java +++ b/playwright/src/main/java/com/microsoft/playwright/Keyboard.java @@ -48,10 +48,7 @@ * *

An example to trigger select-all with the keyboard *

{@code
- * // on Windows and Linux
- * page.keyboard().press("Control+A");
- * // on macOS
- * page.keyboard().press("Meta+A");
+ * page.keyboard().press("ControlOrMeta+A");
  * }
*/ public interface Keyboard { @@ -164,7 +161,7 @@ public TypeOptions setDelay(double delay) { * Page page = browser.newPage(); * page.navigate("https://keycode.info"); * page.keyboard().press("A"); - * page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("A.png")); + * page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("A.png"))); * page.keyboard().press("ArrowLeft"); * page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("ArrowLeft.png"))); * page.keyboard().press("Shift+O"); @@ -211,7 +208,7 @@ default void press(String key) { * Page page = browser.newPage(); * page.navigate("https://keycode.info"); * page.keyboard().press("A"); - * page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("A.png")); + * page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("A.png"))); * page.keyboard().press("ArrowLeft"); * page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("ArrowLeft.png"))); * page.keyboard().press("Shift+O"); diff --git a/playwright/src/main/java/com/microsoft/playwright/Locator.java b/playwright/src/main/java/com/microsoft/playwright/Locator.java index 99161071..ad16fbf1 100644 --- a/playwright/src/main/java/com/microsoft/playwright/Locator.java +++ b/playwright/src/main/java/com/microsoft/playwright/Locator.java @@ -29,6 +29,26 @@ *

Learn more about locators. */ public interface Locator { + class AriaSnapshotOptions { + /** + * Maximum time in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default + * value can be changed by using the {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout + * BrowserContext.setDefaultTimeout()} or {@link com.microsoft.playwright.Page#setDefaultTimeout Page.setDefaultTimeout()} + * methods. + */ + public Double timeout; + + /** + * Maximum time in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default + * value can be changed by using the {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout + * BrowserContext.setDefaultTimeout()} or {@link com.microsoft.playwright.Page#setDefaultTimeout Page.setDefaultTimeout()} + * methods. + */ + public AriaSnapshotOptions setTimeout(double timeout) { + this.timeout = timeout; + return this; + } + } class BlurOptions { /** * Maximum time in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default @@ -2151,7 +2171,7 @@ public WaitForOptions setTimeout(double timeout) { * *

Usage *

{@code
-   * for (Locator li : page.getByRole('listitem').all())
+   * for (Locator li : page.getByRole("listitem").all())
    *   li.click();
    * }
* @@ -2202,6 +2222,66 @@ public WaitForOptions setTimeout(double timeout) { * @since v1.34 */ Locator and(Locator locator); + /** + * Captures the aria snapshot of the given element. Read more about aria snapshots and {@link + * com.microsoft.playwright.assertions.LocatorAssertions#matchesAriaSnapshot LocatorAssertions.matchesAriaSnapshot()} for + * the corresponding assertion. + * + *

Usage + *

{@code
+   * page.getByRole(AriaRole.LINK).ariaSnapshot();
+   * }
+ * + *

Details + * + *

This method captures the aria snapshot of the given element. The snapshot is a string that represents the state of the + * element and its children. The snapshot can be used to assert the state of the element in the test, or to compare it to + * state in the future. + * + *

The ARIA snapshot is represented using YAML markup language: + *

    + *
  • The keys of the objects are the roles and optional accessible names of the elements.
  • + *
  • The values are either text content or an array of child elements.
  • + *
  • Generic static text can be represented with the {@code text} key.
  • + *
+ * + *

Below is the HTML markup and the respective ARIA snapshot: + * + * @since v1.49 + */ + default String ariaSnapshot() { + return ariaSnapshot(null); + } + /** + * Captures the aria snapshot of the given element. Read more about aria snapshots and {@link + * com.microsoft.playwright.assertions.LocatorAssertions#matchesAriaSnapshot LocatorAssertions.matchesAriaSnapshot()} for + * the corresponding assertion. + * + *

Usage + *

{@code
+   * page.getByRole(AriaRole.LINK).ariaSnapshot();
+   * }
+ * + *

Details + * + *

This method captures the aria snapshot of the given element. The snapshot is a string that represents the state of the + * element and its children. The snapshot can be used to assert the state of the element in the test, or to compare it to + * state in the future. + * + *

The ARIA snapshot is represented using YAML markup language: + *

    + *
  • The keys of the objects are the roles and optional accessible names of the elements.
  • + *
  • The values are either text content or an array of child elements.
  • + *
  • Generic static text can be represented with the {@code text} key.
  • + *
+ * + *

Below is the HTML markup and the respective ARIA snapshot: + * + * @since v1.49 + */ + String ariaSnapshot(AriaSnapshotOptions options); /** * Calls blur on the element. * @@ -3455,19 +3535,19 @@ default Locator getByRole(AriaRole role) { *

You can locate by text substring, exact string, or a regular expression: *

{@code
    * // Matches 
-   * page.getByText("world")
+   * page.getByText("world");
    *
    * // Matches first 
- * page.getByText("Hello world") + * page.getByText("Hello world"); * * // Matches second
- * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)) + * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)); * * // Matches both
s - * page.getByText(Pattern.compile("Hello")) + * page.getByText(Pattern.compile("Hello")); * * // Matches second
- * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)) + * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)); * }
* *

Details @@ -3497,19 +3577,19 @@ default Locator getByText(String text) { *

You can locate by text substring, exact string, or a regular expression: *

{@code
    * // Matches 
-   * page.getByText("world")
+   * page.getByText("world");
    *
    * // Matches first 
- * page.getByText("Hello world") + * page.getByText("Hello world"); * * // Matches second
- * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)) + * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)); * * // Matches both
s - * page.getByText(Pattern.compile("Hello")) + * page.getByText(Pattern.compile("Hello")); * * // Matches second
- * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)) + * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)); * }
* *

Details @@ -3537,19 +3617,19 @@ default Locator getByText(String text) { *

You can locate by text substring, exact string, or a regular expression: *

{@code
    * // Matches 
-   * page.getByText("world")
+   * page.getByText("world");
    *
    * // Matches first 
- * page.getByText("Hello world") + * page.getByText("Hello world"); * * // Matches second
- * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)) + * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)); * * // Matches both
s - * page.getByText(Pattern.compile("Hello")) + * page.getByText(Pattern.compile("Hello")); * * // Matches second
- * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)) + * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)); * }
* *

Details @@ -3579,19 +3659,19 @@ default Locator getByText(Pattern text) { *

You can locate by text substring, exact string, or a regular expression: *

{@code
    * // Matches 
-   * page.getByText("world")
+   * page.getByText("world");
    *
    * // Matches first 
- * page.getByText("Hello world") + * page.getByText("Hello world"); * * // Matches second
- * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)) + * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)); * * // Matches both
s - * page.getByText(Pattern.compile("Hello")) + * page.getByText(Pattern.compile("Hello")); * * // Matches second
- * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)) + * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)); * }
* *

Details diff --git a/playwright/src/main/java/com/microsoft/playwright/Page.java b/playwright/src/main/java/com/microsoft/playwright/Page.java index 14e788a4..8901b592 100644 --- a/playwright/src/main/java/com/microsoft/playwright/Page.java +++ b/playwright/src/main/java/com/microsoft/playwright/Page.java @@ -959,8 +959,10 @@ public DragAndDropOptions setTrial(boolean trial) { } class EmulateMediaOptions { /** - * Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code - * "no-preference"}. Passing {@code null} disables color scheme emulation. + * Emulates prefers-colors-scheme media + * feature, supported values are {@code "light"} and {@code "dark"}. Passing {@code null} disables color scheme emulation. + * {@code "no-preference"} is deprecated. */ public Optional colorScheme; /** @@ -980,8 +982,10 @@ class EmulateMediaOptions { public Optional reducedMotion; /** - * Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code - * "no-preference"}. Passing {@code null} disables color scheme emulation. + * Emulates prefers-colors-scheme media + * feature, supported values are {@code "light"} and {@code "dark"}. Passing {@code null} disables color scheme emulation. + * {@code "no-preference"} is deprecated. */ public EmulateMediaOptions setColorScheme(ColorScheme colorScheme) { this.colorScheme = Optional.ofNullable(colorScheme); @@ -4151,9 +4155,9 @@ default void dispatchEvent(String selector, String type) { * *

Usage *

{@code
-   * page.dragAndDrop("#source", '#target');
+   * page.dragAndDrop("#source", "#target");
    * // or specify exact positions relative to the top-left corners of the elements:
-   * page.dragAndDrop("#source", '#target', new Page.DragAndDropOptions()
+   * page.dragAndDrop("#source", "#target", new Page.DragAndDropOptions()
    *   .setSourcePosition(34, 7).setTargetPosition(10, 20));
    * }
* @@ -4172,9 +4176,9 @@ default void dragAndDrop(String source, String target) { * *

Usage *

{@code
-   * page.dragAndDrop("#source", '#target');
+   * page.dragAndDrop("#source", "#target");
    * // or specify exact positions relative to the top-left corners of the elements:
-   * page.dragAndDrop("#source", '#target', new Page.DragAndDropOptions()
+   * page.dragAndDrop("#source", "#target", new Page.DragAndDropOptions()
    *   .setSourcePosition(34, 7).setTargetPosition(10, 20));
    * }
* @@ -4214,8 +4218,6 @@ default void dragAndDrop(String source, String target) { * // → true * page.evaluate("() => matchMedia('(prefers-color-scheme: light)').matches"); * // → false - * page.evaluate("() => matchMedia('(prefers-color-scheme: no-preference)').matches"); - * // → false * }
* * @since v1.8 @@ -4252,8 +4254,6 @@ default void emulateMedia() { * // → true * page.evaluate("() => matchMedia('(prefers-color-scheme: light)').matches"); * // → false - * page.evaluate("() => matchMedia('(prefers-color-scheme: no-preference)').matches"); - * // → false * }
* * @since v1.8 @@ -4560,7 +4560,7 @@ default JSHandle evaluateHandle(String expression) { * public static void main(String[] args) { * try (Playwright playwright = Playwright.create()) { * BrowserType webkit = playwright.webkit(); - * Browser browser = webkit.launch({ headless: false }); + * Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false)); * BrowserContext context = browser.newContext(); * Page page = context.newPage(); * page.exposeBinding("pageURL", (source, args) -> source.page().url()); @@ -4610,7 +4610,7 @@ default void exposeBinding(String name, BindingCallback callback) { * public static void main(String[] args) { * try (Playwright playwright = Playwright.create()) { * BrowserType webkit = playwright.webkit(); - * Browser browser = webkit.launch({ headless: false }); + * Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false)); * BrowserContext context = browser.newContext(); * Page page = context.newPage(); * page.exposeBinding("pageURL", (source, args) -> source.page().url()); @@ -4662,26 +4662,27 @@ default void exposeBinding(String name, BindingCallback callback) { * public static void main(String[] args) { * try (Playwright playwright = Playwright.create()) { * BrowserType webkit = playwright.webkit(); - * Browser browser = webkit.launch({ headless: false }); + * Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false)); * Page page = browser.newPage(); * page.exposeFunction("sha256", args -> { - * String text = (String) args[0]; - * MessageDigest crypto; * try { - * crypto = MessageDigest.getInstance("SHA-256"); + * String text = (String) args[0]; + * MessageDigest crypto = MessageDigest.getInstance("SHA-256"); + * byte[] token = crypto.digest(text.getBytes(StandardCharsets.UTF_8)); + * return Base64.getEncoder().encodeToString(token); * } catch (NoSuchAlgorithmException e) { * return null; * } - * byte[] token = crypto.digest(text.getBytes(StandardCharsets.UTF_8)); - * return Base64.getEncoder().encodeToString(token); * }); - * page.setContent("\n" + * "\n" + - * "
\n"); + * "
" + * ); * page.click("button"); * } * } @@ -4757,7 +4758,7 @@ default void focus(String selector) { * Frame frame = page.frame("frame-name"); * }
*
{@code
-   * Frame frame = page.frameByUrl(Pattern.compile(".*domain.*");
+   * Frame frame = page.frameByUrl(Pattern.compile(".*domain.*"));
    * }
* * @param name Frame name specified in the {@code iframe}'s {@code name} attribute. @@ -5163,19 +5164,19 @@ default Locator getByRole(AriaRole role) { *

You can locate by text substring, exact string, or a regular expression: *

{@code
    * // Matches 
-   * page.getByText("world")
+   * page.getByText("world");
    *
    * // Matches first 
- * page.getByText("Hello world") + * page.getByText("Hello world"); * * // Matches second
- * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)) + * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)); * * // Matches both
s - * page.getByText(Pattern.compile("Hello")) + * page.getByText(Pattern.compile("Hello")); * * // Matches second
- * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)) + * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)); * }
* *

Details @@ -5205,19 +5206,19 @@ default Locator getByText(String text) { *

You can locate by text substring, exact string, or a regular expression: *

{@code
    * // Matches 
-   * page.getByText("world")
+   * page.getByText("world");
    *
    * // Matches first 
- * page.getByText("Hello world") + * page.getByText("Hello world"); * * // Matches second
- * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)) + * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)); * * // Matches both
s - * page.getByText(Pattern.compile("Hello")) + * page.getByText(Pattern.compile("Hello")); * * // Matches second
- * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)) + * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)); * }
* *

Details @@ -5245,19 +5246,19 @@ default Locator getByText(String text) { *

You can locate by text substring, exact string, or a regular expression: *

{@code
    * // Matches 
-   * page.getByText("world")
+   * page.getByText("world");
    *
    * // Matches first 
- * page.getByText("Hello world") + * page.getByText("Hello world"); * * // Matches second
- * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)) + * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)); * * // Matches both
s - * page.getByText(Pattern.compile("Hello")) + * page.getByText(Pattern.compile("Hello")); * * // Matches second
- * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)) + * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)); * }
* *

Details @@ -5287,19 +5288,19 @@ default Locator getByText(Pattern text) { *

You can locate by text substring, exact string, or a regular expression: *

{@code
    * // Matches 
-   * page.getByText("world")
+   * page.getByText("world");
    *
    * // Matches first 
- * page.getByText("Hello world") + * page.getByText("Hello world"); * * // Matches second
- * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)) + * page.getByText("Hello", new Page.GetByTextOptions().setExact(true)); * * // Matches both
s - * page.getByText(Pattern.compile("Hello")) + * page.getByText(Pattern.compile("Hello")); * * // Matches second
- * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)) + * page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE)); * }
* *

Details @@ -6080,24 +6081,24 @@ default ElementHandle querySelector(String selector) { *

An example that closes a "Sign up to the newsletter" dialog when it appears: *

{@code
    * // Setup the handler.
-   * page.addLocatorHandler(page.getByText("Sign up to the newsletter"), () => {
+   * page.addLocatorHandler(page.getByText("Sign up to the newsletter"), () -> {
    *   page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("No thanks")).click();
    * });
    *
    * // Write the test as usual.
-   * page.goto("https://example.com");
+   * page.navigate("https://example.com");
    * page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
    * }
* *

An example that skips the "Confirm your security details" page when it is shown: *

{@code
    * // Setup the handler.
-   * page.addLocatorHandler(page.getByText("Confirm your security details")), () => {
+   * page.addLocatorHandler(page.getByText("Confirm your security details"), () -> {
    *   page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Remind me later")).click();
    * });
    *
    * // Write the test as usual.
-   * page.goto("https://example.com");
+   * page.navigate("https://example.com");
    * page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
    * }
* @@ -6106,19 +6107,19 @@ default ElementHandle querySelector(String selector) { * handler does not hide the {@code } element. *
{@code
    * // Setup the handler.
-   * page.addLocatorHandler(page.locator("body")), () => {
+   * page.addLocatorHandler(page.locator("body"), () -> {
    *   page.evaluate("window.removeObstructionsForTestIfNeeded()");
-   * }, new Page.AddLocatorHandlerOptions.setNoWaitAfter(true));
+   * }, new Page.AddLocatorHandlerOptions().setNoWaitAfter(true));
    *
    * // Write the test as usual.
-   * page.goto("https://example.com");
+   * page.navigate("https://example.com");
    * page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
    * }
* *

Handler takes the original locator as an argument. You can also automatically remove the handler after a number of * invocations by setting {@code times}: *

{@code
-   * page.addLocatorHandler(page.getByLabel("Close"), locator => {
+   * page.addLocatorHandler(page.getByLabel("Close"), locator -> {
    *   locator.click();
    * }, new Page.AddLocatorHandlerOptions().setTimes(1));
    * }
@@ -6172,24 +6173,24 @@ default void addLocatorHandler(Locator locator, Consumer handler) { *

An example that closes a "Sign up to the newsletter" dialog when it appears: *

{@code
    * // Setup the handler.
-   * page.addLocatorHandler(page.getByText("Sign up to the newsletter"), () => {
+   * page.addLocatorHandler(page.getByText("Sign up to the newsletter"), () -> {
    *   page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("No thanks")).click();
    * });
    *
    * // Write the test as usual.
-   * page.goto("https://example.com");
+   * page.navigate("https://example.com");
    * page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
    * }
* *

An example that skips the "Confirm your security details" page when it is shown: *

{@code
    * // Setup the handler.
-   * page.addLocatorHandler(page.getByText("Confirm your security details")), () => {
+   * page.addLocatorHandler(page.getByText("Confirm your security details"), () -> {
    *   page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Remind me later")).click();
    * });
    *
    * // Write the test as usual.
-   * page.goto("https://example.com");
+   * page.navigate("https://example.com");
    * page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
    * }
* @@ -6198,19 +6199,19 @@ default void addLocatorHandler(Locator locator, Consumer handler) { * handler does not hide the {@code } element. *
{@code
    * // Setup the handler.
-   * page.addLocatorHandler(page.locator("body")), () => {
+   * page.addLocatorHandler(page.locator("body"), () -> {
    *   page.evaluate("window.removeObstructionsForTestIfNeeded()");
-   * }, new Page.AddLocatorHandlerOptions.setNoWaitAfter(true));
+   * }, new Page.AddLocatorHandlerOptions().setNoWaitAfter(true));
    *
    * // Write the test as usual.
-   * page.goto("https://example.com");
+   * page.navigate("https://example.com");
    * page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
    * }
* *

Handler takes the original locator as an argument. You can also automatically remove the handler after a number of * invocations by setting {@code times}: *

{@code
-   * page.addLocatorHandler(page.getByLabel("Close"), locator => {
+   * page.addLocatorHandler(page.getByLabel("Close"), locator -> {
    *   locator.click();
    * }, new Page.AddLocatorHandlerOptions().setTimes(1));
    * }
@@ -6641,8 +6642,8 @@ default void routeFromHAR(Path har) { * examples. *
{@code
    * page.routeWebSocket("/ws", ws -> {
-   *   ws.onMessage(message -> {
-   *     if ("request".equals(message))
+   *   ws.onMessage(frame -> {
+   *     if ("request".equals(frame.text()))
    *       ws.send("response");
    *   });
    * });
@@ -6666,8 +6667,8 @@ default void routeFromHAR(Path har) {
    * examples.
    * 
{@code
    * page.routeWebSocket("/ws", ws -> {
-   *   ws.onMessage(message -> {
-   *     if ("request".equals(message))
+   *   ws.onMessage(frame -> {
+   *     if ("request".equals(frame.text()))
    *       ws.send("response");
    *   });
    * });
@@ -6691,8 +6692,8 @@ default void routeFromHAR(Path har) {
    * examples.
    * 
{@code
    * page.routeWebSocket("/ws", ws -> {
-   *   ws.onMessage(message -> {
-   *     if ("request".equals(message))
+   *   ws.onMessage(frame -> {
+   *     if ("request".equals(frame.text()))
    *       ws.send("response");
    *   });
    * });
diff --git a/playwright/src/main/java/com/microsoft/playwright/Route.java b/playwright/src/main/java/com/microsoft/playwright/Route.java
index abb43204..ae05c816 100644
--- a/playwright/src/main/java/com/microsoft/playwright/Route.java
+++ b/playwright/src/main/java/com/microsoft/playwright/Route.java
@@ -363,10 +363,8 @@ default void abort() {
    *
    * 

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 com.microsoft.playwright.Route#fetch Route.fetch()} and {@link - * com.microsoft.playwright.Route#fulfill Route.fulfill()} instead. + *

The {@code headers} option applies to both the routed request and any redirects it initiates. However, {@code url}, + * {@code method}, and {@code postData} only apply to the original request and are not carried over to redirected requests. * *

{@link com.microsoft.playwright.Route#resume Route.resume()} will immediately send the request to the network, other * matching handlers won't be invoked. Use {@link com.microsoft.playwright.Route#fallback Route.fallback()} If you want @@ -393,10 +391,8 @@ default void resume() { * *

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 com.microsoft.playwright.Route#fetch Route.fetch()} and {@link - * com.microsoft.playwright.Route#fulfill Route.fulfill()} instead. + *

The {@code headers} option applies to both the routed request and any redirects it initiates. However, {@code url}, + * {@code method}, and {@code postData} only apply to the original request and are not carried over to redirected requests. * *

{@link com.microsoft.playwright.Route#resume Route.resume()} will immediately send the request to the network, other * matching handlers won't be invoked. Use {@link com.microsoft.playwright.Route#fallback Route.fallback()} If you want diff --git a/playwright/src/main/java/com/microsoft/playwright/Tracing.java b/playwright/src/main/java/com/microsoft/playwright/Tracing.java index 82ea8d49..160b4d3f 100644 --- a/playwright/src/main/java/com/microsoft/playwright/Tracing.java +++ b/playwright/src/main/java/com/microsoft/playwright/Tracing.java @@ -16,6 +16,7 @@ package com.microsoft.playwright; +import com.microsoft.playwright.options.*; import java.nio.file.Path; /** @@ -143,6 +144,29 @@ public StartChunkOptions setTitle(String title) { return this; } } + class GroupOptions { + /** + * Specifies a custom location for the group to be shown in the trace viewer. Defaults to the location of the {@link + * com.microsoft.playwright.Tracing#group Tracing.group()} call. + */ + public Location location; + + /** + * Specifies a custom location for the group to be shown in the trace viewer. Defaults to the location of the {@link + * com.microsoft.playwright.Tracing#group Tracing.group()} call. + */ + public GroupOptions setLocation(String file) { + return setLocation(new Location(file)); + } + /** + * Specifies a custom location for the group to be shown in the trace viewer. Defaults to the location of the {@link + * com.microsoft.playwright.Tracing#group Tracing.group()} call. + */ + public GroupOptions setLocation(Location location) { + this.location = location; + return this; + } + } class StopOptions { /** * Export trace into the file with the given path. @@ -271,6 +295,56 @@ default void startChunk() { * @since v1.15 */ void startChunk(StartChunkOptions options); + /** + * NOTE: Use {@code test.step} instead when available. + * + *

Creates a new group within the trace, assigning any subsequent API calls to this group, until {@link + * com.microsoft.playwright.Tracing#groupEnd Tracing.groupEnd()} is called. Groups can be nested and will be visible in the + * trace viewer. + * + *

Usage + *

{@code
+   * // All actions between group and groupEnd
+   * // will be shown in the trace viewer as a group.
+   * page.context().tracing.group("Open Playwright.dev > API");
+   * page.navigate("https://playwright.dev/");
+   * page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("API")).click();
+   * page.context().tracing.groupEnd();
+   * }
+ * + * @param name Group name shown in the trace viewer. + * @since v1.49 + */ + default void group(String name) { + group(name, null); + } + /** + * NOTE: Use {@code test.step} instead when available. + * + *

Creates a new group within the trace, assigning any subsequent API calls to this group, until {@link + * com.microsoft.playwright.Tracing#groupEnd Tracing.groupEnd()} is called. Groups can be nested and will be visible in the + * trace viewer. + * + *

Usage + *

{@code
+   * // All actions between group and groupEnd
+   * // will be shown in the trace viewer as a group.
+   * page.context().tracing.group("Open Playwright.dev > API");
+   * page.navigate("https://playwright.dev/");
+   * page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("API")).click();
+   * page.context().tracing.groupEnd();
+   * }
+ * + * @param name Group name shown in the trace viewer. + * @since v1.49 + */ + void group(String name, GroupOptions options); + /** + * Closes the last group created by {@link com.microsoft.playwright.Tracing#group Tracing.group()}. + * + * @since v1.49 + */ + void groupEnd(); /** * Stop tracing. * diff --git a/playwright/src/main/java/com/microsoft/playwright/WebSocketRoute.java b/playwright/src/main/java/com/microsoft/playwright/WebSocketRoute.java index fe662582..e20fe41a 100644 --- a/playwright/src/main/java/com/microsoft/playwright/WebSocketRoute.java +++ b/playwright/src/main/java/com/microsoft/playwright/WebSocketRoute.java @@ -31,8 +31,8 @@ * WebSocket. Here is an example that responds to a {@code "request"} with a {@code "response"}. *
{@code
  * page.routeWebSocket("wss://example.com/ws", ws -> {
- *   ws.onMessage(message -> {
- *     if ("request".equals(message))
+ *   ws.onMessage(frame -> {
+ *     if ("request".equals(frame.text()))
  *       ws.send("response");
  *   });
  * });
@@ -45,8 +45,8 @@
  * 

Here is another example that handles JSON messages: *

{@code
  * page.routeWebSocket("wss://example.com/ws", ws -> {
- *   ws.onMessage(message -> {
- *     JsonObject json = new JsonParser().parse(message).getAsJsonObject();
+ *   ws.onMessage(frame -> {
+ *     JsonObject json = new JsonParser().parse(frame.text()).getAsJsonObject();
  *     if ("question".equals(json.get("request").getAsString())) {
  *       Map result = new HashMap();
  *       result.put("response", "answer");
@@ -67,11 +67,11 @@
  * 
{@code
  * page.routeWebSocket("/ws", ws -> {
  *   WebSocketRoute server = ws.connectToServer();
- *   ws.onMessage(message -> {
- *     if ("request".equals(message))
+ *   ws.onMessage(frame -> {
+ *     if ("request".equals(frame.text()))
  *       server.send("request2");
  *     else
- *       server.send(message);
+ *       server.send(frame.text());
  *   });
  * });
  * }
@@ -92,13 +92,13 @@ *
{@code
  * page.routeWebSocket("/ws", ws -> {
  *   WebSocketRoute server = ws.connectToServer();
- *   ws.onMessage(message -> {
- *     if (!"blocked-from-the-page".equals(message))
- *       server.send(message);
+ *   ws.onMessage(frame -> {
+ *     if (!"blocked-from-the-page".equals(frame.text()))
+ *       server.send(frame.text());
  *   });
- *   server.onMessage(message -> {
- *     if (!"blocked-from-the-server".equals(message))
- *       ws.send(message);
+ *   server.onMessage(frame -> {
+ *     if (!"blocked-from-the-server".equals(frame.text()))
+ *       ws.send(frame.text());
  *   });
  * });
  * }
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 4e12a68e..70cfd8e0 100644 --- a/playwright/src/main/java/com/microsoft/playwright/assertions/APIResponseAssertions.java +++ b/playwright/src/main/java/com/microsoft/playwright/assertions/APIResponseAssertions.java @@ -21,15 +21,15 @@ * The {@code APIResponseAssertions} class provides assertion methods that can be used to make assertions about the {@code * APIResponse} in the tests. *
{@code
- * ...
+ * // ...
  * import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
  *
  * public class TestPage {
- *   ...
+ *   // ...
  *   @Test
  *   void navigatesToLoginPage() {
- *     ...
- *     APIResponse response = page.request().get('https://playwright.dev');
+ *     // ...
+ *     APIResponse response = page.request().get("https://playwright.dev");
  *     assertThat(response).isOK();
  *   }
  * }
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 e2a81d47..ea34fc2f 100644
--- a/playwright/src/main/java/com/microsoft/playwright/assertions/LocatorAssertions.java
+++ b/playwright/src/main/java/com/microsoft/playwright/assertions/LocatorAssertions.java
@@ -23,14 +23,14 @@
  * The {@code LocatorAssertions} class provides assertion methods that can be used to make assertions about the {@code
  * Locator} state in the tests.
  * 
{@code
- * ...
+ * // ...
  * import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
  *
  * public class TestLocator {
- *   ...
+ *   // ...
  *   @Test
  *   void statusBecomesSubmitted() {
- *     ...
+ *     // ...
  *     page.getByRole(AriaRole.BUTTON).click();
  *     assertThat(page.locator(".status")).hasText("Submitted");
  *   }
@@ -485,6 +485,20 @@ public HasValuesOptions setTimeout(double timeout) {
       return this;
     }
   }
+  class MatchesAriaSnapshotOptions {
+    /**
+     * Time to retry the assertion for in milliseconds. Defaults to {@code 5000}.
+     */
+    public Double timeout;
+
+    /**
+     * Time to retry the assertion for in milliseconds. Defaults to {@code 5000}.
+     */
+    public MatchesAriaSnapshotOptions setTimeout(double timeout) {
+      this.timeout = timeout;
+      return this;
+    }
+  }
   /**
    * Makes the assertion check for the opposite condition. For example, this code tests that the Locator doesn't contain text
    * {@code "error"}:
@@ -2097,7 +2111,7 @@ default void hasValue(Pattern value) {
    *
    * 

For example, given the following element: *

{@code
-   * page.locator("id=favorite-colors").selectOption(["R", "G"]);
+   * page.locator("id=favorite-colors").selectOption(new String[]{"R", "G"});
    * assertThat(page.locator("id=favorite-colors")).hasValues(new Pattern[] { Pattern.compile("R"), Pattern.compile("G") });
    * }
* @@ -2115,7 +2129,7 @@ default void hasValues(String[] values) { * *

For example, given the following element: *

{@code
-   * page.locator("id=favorite-colors").selectOption(["R", "G"]);
+   * page.locator("id=favorite-colors").selectOption(new String[]{"R", "G"});
    * assertThat(page.locator("id=favorite-colors")).hasValues(new Pattern[] { Pattern.compile("R"), Pattern.compile("G") });
    * }
* @@ -2131,7 +2145,7 @@ default void hasValues(String[] values) { * *

For example, given the following element: *

{@code
-   * page.locator("id=favorite-colors").selectOption(["R", "G"]);
+   * page.locator("id=favorite-colors").selectOption(new String[]{"R", "G"});
    * assertThat(page.locator("id=favorite-colors")).hasValues(new Pattern[] { Pattern.compile("R"), Pattern.compile("G") });
    * }
* @@ -2149,7 +2163,7 @@ default void hasValues(Pattern[] values) { * *

For example, given the following element: *

{@code
-   * page.locator("id=favorite-colors").selectOption(["R", "G"]);
+   * page.locator("id=favorite-colors").selectOption(new String[]{"R", "G"});
    * assertThat(page.locator("id=favorite-colors")).hasValues(new Pattern[] { Pattern.compile("R"), Pattern.compile("G") });
    * }
* @@ -2157,5 +2171,39 @@ default void hasValues(Pattern[] values) { * @since v1.23 */ void hasValues(Pattern[] values, HasValuesOptions options); + /** + * Asserts that the target element matches the given accessibility snapshot. + * + *

Usage + *

{@code
+   * page.navigate("https://demo.playwright.dev/todomvc/");
+   * assertThat(page.locator("body")).matchesAriaSnapshot("""
+   *   - heading "todos"
+   *   - textbox "What needs to be done?"
+   * """);
+   * }
+ * + * @since v1.49 + */ + default void matchesAriaSnapshot(String expected) { + matchesAriaSnapshot(expected, null); + } + /** + * Asserts that the target element matches the given accessibility snapshot. + * + *

Usage + *

{@code
+   * page.navigate("https://demo.playwright.dev/todomvc/");
+   * assertThat(page.locator("body")).matchesAriaSnapshot("""
+   *   - heading "todos"
+   *   - textbox "What needs to be done?"
+   * """);
+   * }
+ * + * @since v1.49 + */ + void matchesAriaSnapshot(String expected, MatchesAriaSnapshotOptions options); } 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 2c8e7fa7..2bfbfa44 100644 --- a/playwright/src/main/java/com/microsoft/playwright/assertions/PageAssertions.java +++ b/playwright/src/main/java/com/microsoft/playwright/assertions/PageAssertions.java @@ -22,14 +22,14 @@ * The {@code PageAssertions} class provides assertion methods that can be used to make assertions about the {@code Page} * state in the tests. *
{@code
- * ...
+ * // ...
  * import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
  *
  * public class TestPage {
- *   ...
+ *   // ...
  *   @Test
  *   void navigatesToLoginPage() {
- *     ...
+ *     // ...
  *     page.getByText("Sign in").click();
  *     assertThat(page).hasURL(Pattern.compile(".*\/login"));
  *   }
diff --git a/playwright/src/main/java/com/microsoft/playwright/assertions/PlaywrightAssertions.java b/playwright/src/main/java/com/microsoft/playwright/assertions/PlaywrightAssertions.java
index f0fdeede..2e926d66 100644
--- a/playwright/src/main/java/com/microsoft/playwright/assertions/PlaywrightAssertions.java
+++ b/playwright/src/main/java/com/microsoft/playwright/assertions/PlaywrightAssertions.java
@@ -30,14 +30,13 @@
  *
  * 

Consider the following example: *

{@code
- * ...
  * import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
  *
  * public class TestExample {
- *   ...
+ *   // ...
  *   @Test
  *   void statusBecomesSubmitted() {
- *     ...
+ *     // ...
  *     page.locator("#submit-button").click();
  *     assertThat(page.locator(".status")).hasText("Submitted");
  *   }
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 83264ea3..d7e65421 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/LocatorAssertionsImpl.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/LocatorAssertionsImpl.java
@@ -326,6 +326,16 @@ public void hasValues(Pattern[] patterns, HasValuesOptions options) {
     expectImpl("to.have.values", list, patterns, "Locator expected to have values matching regex", convertType(options, FrameExpectOptions.class));
   }
 
+  @Override
+  public void matchesAriaSnapshot(String expected, MatchesAriaSnapshotOptions snapshotOptions) {
+    if (snapshotOptions == null) {
+      snapshotOptions = new MatchesAriaSnapshotOptions();
+    }
+    FrameExpectOptions options = convertType(snapshotOptions, FrameExpectOptions.class);
+    options.expectedValue = serializeArgument(expected);
+    expectImpl("to.match.aria", options, expected,"Locator expected to match Aria snapshot");
+  }
+
   @Override
   public void isChecked(IsCheckedOptions options) {
     boolean unchecked = options != null && options.checked != null && !options.checked;
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 b97a54fb..9bb3eb19 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/LocatorImpl.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/LocatorImpl.java
@@ -119,6 +119,21 @@ public Locator and(Locator locator) {
     return new LocatorImpl(frame, selector + " >> internal:and=" + gson().toJson(other.selector), null);
   }
 
+  @Override
+  public String ariaSnapshot(AriaSnapshotOptions options) {
+    return frame.withLogging("Locator.ariaSnapshot", () -> ariaSnapshotImpl(options));
+  }
+
+  private String ariaSnapshotImpl(AriaSnapshotOptions options) {
+    if (options == null) {
+      options = new AriaSnapshotOptions();
+    }
+    JsonObject params = gson().toJsonTree(options).getAsJsonObject();
+    params.addProperty("selector", selector);
+    JsonObject result = frame.sendMessage("ariaSnapshot", params).getAsJsonObject();
+    return result.get("snapshot").getAsString();
+  }
+
   @Override
   public void blur(BlurOptions options) {
     frame.withLogging("Locator.blur", () -> blurImpl(options));
@@ -650,9 +665,6 @@ JsonObject toProtocol() {
   }
 
   private FrameExpectResult expectImpl(String expression, FrameExpectOptions options) {
-    if (options == null) {
-      options = new FrameExpectOptions();
-    }
     JsonObject params = gson().toJsonTree(options).getAsJsonObject();
     params.addProperty("selector", selector);
     params.addProperty("expression", expression);
diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/TracingImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/TracingImpl.java
index f88d2130..de757576 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/TracingImpl.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/TracingImpl.java
@@ -86,6 +86,25 @@ public void startChunk(StartChunkOptions options) {
     tracingStartChunk(options.name, options.title);
   }
 
+  @Override
+  public void group(String name, GroupOptions options) {
+    withLogging("Tracing.group", () -> groupImpl(name, options));
+  }
+
+  private void groupImpl(String name, GroupOptions options) {
+    if (options == null) {
+      options = new GroupOptions();
+    }
+    JsonObject params = gson().toJsonTree(options).getAsJsonObject();
+    params.addProperty("name", name);
+    sendMessage("tracingGroup", params);
+  }
+
+  @Override
+  public void groupEnd() {
+    withLogging("Tracing.groupEnd", () -> sendMessage("tracingGroupEnd"));
+  }
+
   private void tracingStartChunk(String name, String title) {
     JsonObject params = new JsonObject();
     if (name != null) {
diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/WebSocketRouteImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/WebSocketRouteImpl.java
index 53479fcf..3fe7a72d 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/WebSocketRouteImpl.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/WebSocketRouteImpl.java
@@ -69,6 +69,7 @@ public String url() {
 
   WebSocketRouteImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
     super(parent, type, guid, initializer);
+    markAsInternalType();
   }
 
     @Override
diff --git a/playwright/src/main/java/com/microsoft/playwright/options/FormData.java b/playwright/src/main/java/com/microsoft/playwright/options/FormData.java
index 057758a0..a674b83f 100644
--- a/playwright/src/main/java/com/microsoft/playwright/options/FormData.java
+++ b/playwright/src/main/java/com/microsoft/playwright/options/FormData.java
@@ -23,7 +23,7 @@
  * The {@code FormData} is used create form data that is sent via {@code APIRequestContext}.
  * 
{@code
  * import com.microsoft.playwright.options.FormData;
- * ...
+ * // ...
  * FormData form = FormData.create()
  *     .set("firstName", "John")
  *     .set("lastName", "Doe")
@@ -43,7 +43,7 @@ public interface FormData {
    * existing set of values.
    * 
{@code
    * import com.microsoft.playwright.options.FormData;
-   * ...
+   * // ...
    * FormData form = FormData.create()
    *     // Only name and value are set.
    *     .append("firstName", "John")
@@ -70,7 +70,7 @@ public interface FormData {
    * existing set of values.
    * 
{@code
    * import com.microsoft.playwright.options.FormData;
-   * ...
+   * // ...
    * FormData form = FormData.create()
    *     // Only name and value are set.
    *     .append("firstName", "John")
@@ -97,7 +97,7 @@ public interface FormData {
    * existing set of values.
    * 
{@code
    * import com.microsoft.playwright.options.FormData;
-   * ...
+   * // ...
    * FormData form = FormData.create()
    *     // Only name and value are set.
    *     .append("firstName", "John")
@@ -124,7 +124,7 @@ public interface FormData {
    * existing set of values.
    * 
{@code
    * import com.microsoft.playwright.options.FormData;
-   * ...
+   * // ...
    * FormData form = FormData.create()
    *     // Only name and value are set.
    *     .append("firstName", "John")
@@ -151,7 +151,7 @@ public interface FormData {
    * existing set of values.
    * 
{@code
    * import com.microsoft.playwright.options.FormData;
-   * ...
+   * // ...
    * FormData form = FormData.create()
    *     // Only name and value are set.
    *     .append("firstName", "John")
@@ -179,7 +179,7 @@ static FormData create() {
    * Sets a field on the form. File values can be passed either as {@code Path} or as {@code FilePayload}.
    * 
{@code
    * import com.microsoft.playwright.options.FormData;
-   * ...
+   * // ...
    * FormData form = FormData.create()
    *     // Only name and value are set.
    *     .set("firstName", "John")
@@ -200,7 +200,7 @@ static FormData create() {
    * Sets a field on the form. File values can be passed either as {@code Path} or as {@code FilePayload}.
    * 
{@code
    * import com.microsoft.playwright.options.FormData;
-   * ...
+   * // ...
    * FormData form = FormData.create()
    *     // Only name and value are set.
    *     .set("firstName", "John")
@@ -221,7 +221,7 @@ static FormData create() {
    * Sets a field on the form. File values can be passed either as {@code Path} or as {@code FilePayload}.
    * 
{@code
    * import com.microsoft.playwright.options.FormData;
-   * ...
+   * // ...
    * FormData form = FormData.create()
    *     // Only name and value are set.
    *     .set("firstName", "John")
@@ -242,7 +242,7 @@ static FormData create() {
    * Sets a field on the form. File values can be passed either as {@code Path} or as {@code FilePayload}.
    * 
{@code
    * import com.microsoft.playwright.options.FormData;
-   * ...
+   * // ...
    * FormData form = FormData.create()
    *     // Only name and value are set.
    *     .set("firstName", "John")
@@ -263,7 +263,7 @@ static FormData create() {
    * Sets a field on the form. File values can be passed either as {@code Path} or as {@code FilePayload}.
    * 
{@code
    * import com.microsoft.playwright.options.FormData;
-   * ...
+   * // ...
    * FormData form = FormData.create()
    *     // Only name and value are set.
    *     .set("firstName", "John")
diff --git a/playwright/src/main/java/com/microsoft/playwright/options/Location.java b/playwright/src/main/java/com/microsoft/playwright/options/Location.java
new file mode 100644
index 00000000..4ce62f8e
--- /dev/null
+++ b/playwright/src/main/java/com/microsoft/playwright/options/Location.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) Microsoft Corporation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.microsoft.playwright.options;
+
+public class Location {
+  public String file;
+  public Integer line;
+  public Integer column;
+
+  public Location(String file) {
+    this.file = file;
+  }
+  public Location setLine(int line) {
+    this.line = line;
+    return this;
+  }
+  public Location setColumn(int column) {
+    this.column = column;
+    return this;
+  }
+}
\ No newline at end of file
diff --git a/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextCDPSession.java b/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextCDPSession.java
index eeba7b6d..8fafe50f 100644
--- a/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextCDPSession.java
+++ b/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextCDPSession.java
@@ -16,6 +16,7 @@
 
 package com.microsoft.playwright;
 
+import com.google.gson.Gson;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import org.junit.jupiter.api.Test;
@@ -49,7 +50,12 @@ void shouldSendEvents() {
     cdpSession.send("Network.enable");
 
     List events = new ArrayList<>();
-    cdpSession.on("Network.requestWillBeSent", events::add);
+    cdpSession.on("Network.requestWillBeSent", (JsonObject jsonObject) -> {
+      // Only register main request, ignore favicon requests.
+      if ("Document".equals(jsonObject.get("type").getAsString())) {
+        events.add(jsonObject);
+      }
+    });
     page.navigate(server.EMPTY_PAGE);
 
     assertEquals(1, events.size());
@@ -136,8 +142,14 @@ void shouldAddMultipleEventListeners() {
     cdpSession.send("Network.enable");
 
     List events = new ArrayList<>();
-    cdpSession.on("Network.requestWillBeSent", events::add);
-    cdpSession.on("Network.requestWillBeSent", events::add);
+    Consumer listener1 = (JsonObject jsonObject) -> {
+      // Only register main request, ignore favicon requests.
+      if ("Document".equals(jsonObject.get("type").getAsString())) {
+        events.add(jsonObject);
+      }
+    };
+    cdpSession.on("Network.requestWillBeSent", listener1);
+    cdpSession.on("Network.requestWillBeSent", listener1);
 
     page.navigate(server.EMPTY_PAGE);
     assertEquals(2, events.size());
@@ -149,9 +161,15 @@ void shouldRemoveEventListeners() {
     cdpSession.send("Network.enable");
 
     List events = new ArrayList<>();
-    Consumer listener1 = events::add;
+    Consumer listener1 = (JsonObject jsonObject) -> {
+      // Only register main request, ignore favicon requests.
+      if ("Document".equals(jsonObject.get("type").getAsString())) {
+        events.add(jsonObject);
+      }
+    };
+    Consumer listener2 = listener1::accept;
     cdpSession.on("Network.requestWillBeSent", listener1);
-    cdpSession.on("Network.requestWillBeSent", events::add);
+    cdpSession.on("Network.requestWillBeSent", listener2);
 
     page.navigate(server.EMPTY_PAGE);
     assertEquals(2, events.size());
@@ -160,6 +178,6 @@ void shouldRemoveEventListeners() {
     events.clear();
 
     page.navigate(server.EMPTY_PAGE);
-    assertEquals(1, events.size());
+    assertEquals(1, events.size(), new Gson().toJson(events));
   }
 }
diff --git a/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextCredentials.java b/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextCredentials.java
index e9ce80aa..ce556273 100644
--- a/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextCredentials.java
+++ b/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextCredentials.java
@@ -25,13 +25,15 @@
 
 public class TestBrowserContextCredentials extends TestBase {
 
-  static boolean isChromiumHeadful() {
-    return isChromium() && isHeadful();
+  static boolean isChromiumHeadedLike() {
+    // --headless=new, the default in all Chromium channels, is like headless.
+    return isChromium() && (isHeadful() || getBrowserChannelFromEnv() != null);
   }
 
   @Test
-  @DisabledIf(value="isChromiumHeadful", disabledReason="fail")
+  @DisabledIf(value="isChromiumHeadedLike", disabledReason="fail")
   void shouldFailWithoutCredentials() {
+    System.out.println("channel2 " + getBrowserChannelFromEnv());
     server.setAuth("/empty.html", "user", "pass");
     Response response = page.navigate(server.EMPTY_PAGE);
     assertEquals(401, response.status());
@@ -103,6 +105,7 @@ void shouldWorkWithCorrectCredentialsAndMatchingOriginCaseInsensitive() {
   }
 
   @Test
+  @DisabledIf(value="isChromiumHeadedLike", disabledReason="fail")
   void shouldFailWithCorrectCredentialsAndWrongOriginScheme() {
     server.setAuth("/empty.html", "user", "pass");
     final HttpCredentials httpCredentials = new HttpCredentials("user", "pass");
@@ -115,6 +118,7 @@ void shouldFailWithCorrectCredentialsAndWrongOriginScheme() {
   }
 
   @Test
+  @DisabledIf(value="isChromiumHeadedLike", disabledReason="fail")
   void shouldFailWithCorrectCredentialsAndWrongOriginHostname() {
     server.setAuth("/empty.html", "user", "pass");
     final HttpCredentials httpCredentials = new HttpCredentials("user", "pass");
@@ -127,6 +131,7 @@ void shouldFailWithCorrectCredentialsAndWrongOriginHostname() {
   }
 
   @Test
+  @DisabledIf(value="isChromiumHeadedLike", disabledReason="fail")
   void shouldFailWithCorrectCredentialsAndWrongOriginPort() {
     server.setAuth("/empty.html", "user", "pass");
     final HttpCredentials httpCredentials = new HttpCredentials("user", "pass");
diff --git a/playwright/src/test/java/com/microsoft/playwright/TestPageAriaSnapshot.java b/playwright/src/test/java/com/microsoft/playwright/TestPageAriaSnapshot.java
new file mode 100644
index 00000000..8dc68e16
--- /dev/null
+++ b/playwright/src/test/java/com/microsoft/playwright/TestPageAriaSnapshot.java
@@ -0,0 +1,86 @@
+package com.microsoft.playwright;
+
+import com.microsoft.playwright.junit.FixtureTest;
+import com.microsoft.playwright.junit.UsePlaywright;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@FixtureTest
+@UsePlaywright
+public class TestPageAriaSnapshot {
+  public static String unshift(String snapshot) {
+    List lines = Arrays.asList(snapshot.split("\n"));
+    int whitespacePrefixLength = 100;
+    Pattern pattern = Pattern.compile("^(\\s*).*");
+    for (String line : lines) {
+      if (line.trim().isEmpty())
+        continue;
+      Matcher matcher = pattern.matcher(line);
+      if (!matcher.matches()) {
+        continue;
+      }
+      String match = matcher.group(1);
+      if (match.length() < whitespacePrefixLength) {
+        whitespacePrefixLength = match.length();
+      }
+    }
+    final int prefixLength = whitespacePrefixLength;
+    return lines.stream()
+      .filter(line -> !line.trim().isEmpty())
+      .map(line -> line.substring(prefixLength))
+      .collect(Collectors.joining("\n"));
+  }
+
+  private static void checkAndMatchSnapshot(Locator locator, String snapshot) {
+    assertEquals(unshift(snapshot), locator.ariaSnapshot());
+    assertThat(locator).matchesAriaSnapshot(snapshot);
+  }
+
+  @Test
+  void shouldSnapshot(Page page) {
+    page.setContent("

title

"); + checkAndMatchSnapshot(page.locator("body"), "- heading \"title\" [level=1]"); + } + + @Test + void shouldSnapshotList(Page page) { + page.setContent("

title

title 2

"); + checkAndMatchSnapshot(page.locator("body"), "" + + " - heading \"title\" [level=1]\n" + + " - heading \"title 2\" [level=1]"); + } + + @Test + void shouldSnapshotListWithAccessibleName(Page page) { + page.setContent("
  • one
  • two
"); + checkAndMatchSnapshot(page.locator("body"), "- list \"my list\":\n - listitem: one\n - listitem: two"); + } + + @Test + void shouldSnapshotComplex(Page page) { + page.setContent(""); + checkAndMatchSnapshot(page.locator("body"), "- list:\n - listitem:\n - link \"link\""); + } + + @Test + void shouldAllowTextNodes(Page page) { + page.setContent("

Microsoft

Open source projects and samples from Microsoft
"); + checkAndMatchSnapshot(page.locator("body"), "" + + " - heading \"Microsoft\" [level=1]\n" + + " - text: Open source projects and samples from Microsoft"); + } + + @Test + void shouldSnapshotDetailsVisibility(Page page) { + page.setContent("
Summary
Details
"); + checkAndMatchSnapshot(page.locator("body"), "- group: Summary"); + } +} diff --git a/playwright/src/test/java/com/microsoft/playwright/TestTracing.java b/playwright/src/test/java/com/microsoft/playwright/TestTracing.java index 4a064038..2da398f9 100644 --- a/playwright/src/test/java/com/microsoft/playwright/TestTracing.java +++ b/playwright/src/test/java/com/microsoft/playwright/TestTracing.java @@ -16,6 +16,9 @@ package com.microsoft.playwright; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.microsoft.playwright.options.Location; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -26,12 +29,14 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.stream.Collectors; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.*; public class TestTracing extends TestBase { @Override @@ -154,4 +159,68 @@ void shouldRespectTracesDirAndName(@TempDir Path tempDir) { } } + @Test + void canCallTracingGroupGroupEndAtAnyTimeAndAutoClose(@TempDir Path tempDir) throws Exception { + context.tracing().group("ignored"); + context.tracing().groupEnd(); + context.tracing().group("ignored2"); + + context.tracing().start(new Tracing.StartOptions()); + context.tracing().group("actual"); + page.navigate(server.EMPTY_PAGE); + Path traceFile1 = tempDir.resolve("trace1.zip"); + context.tracing().stopChunk(new Tracing.StopChunkOptions().setPath(traceFile1)); + + context.tracing().group("ignored3"); + context.tracing().groupEnd(); + context.tracing().groupEnd(); + context.tracing().groupEnd(); + + List events = parseTraceEvents(traceFile1); + List groups = events.stream().filter(e -> "tracingGroup".equals(e.method)).collect(Collectors.toList()); + assertEquals(1, groups.size()); + assertEquals("actual", groups.get(0).apiName); + + } + + @Test + void traceGroupGroupEnd(@TempDir Path tempDir) throws Exception { + context.tracing().start(new Tracing.StartOptions()); + context.tracing().group("outer group"); + page.navigate("data:text/html,
Hello world
"); + context.tracing().group("inner group 1", new Tracing.GroupOptions().setLocation(new Location("foo.java").setLine(17).setColumn(1))); + page.locator("body").click(); + context.tracing().groupEnd(); + context.tracing().group("inner group 2"); + assertTrue(page.locator("text=Hello").isVisible()); + context.tracing().groupEnd(); + context.tracing().groupEnd(); + + Path traceFile1 = tempDir.resolve("trace1.zip"); + context.tracing().stop(new Tracing.StopOptions().setPath(traceFile1)); + + List events = parseTraceEvents(traceFile1); + List calls = events.stream().filter(e -> e.apiName != null).map(e -> e.apiName).collect(Collectors.toList()); + assertEquals(asList("outer group", "Page.navigate", "inner group 1", "Frame.click", "inner group 2", "Page.isVisible"), calls); + } + + private static class TraceEvent { + String type; + String name; + String apiName; + String method; + Double startTime; + Double endTime; + String callId; + } + + private static List parseTraceEvents(Path traceFile) throws IOException { + Map files = Utils.parseZip(traceFile); + Map traces = files.entrySet().stream().filter(e -> e.getKey().endsWith(".trace")).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + assertNotNull(traces.get("trace.trace")); + return Arrays.stream(new String(traces.get("trace.trace"), UTF_8) + .split("\n")) + .map(s -> new Gson().fromJson(s, TraceEvent.class)) + .collect(Collectors.toList()); + } } diff --git a/scripts/DRIVER_VERSION b/scripts/DRIVER_VERSION index 5525f03f..90269c3e 100644 --- a/scripts/DRIVER_VERSION +++ b/scripts/DRIVER_VERSION @@ -1 +1 @@ -1.48.1 +1.49.0-beta-1731616844000 diff --git a/tools/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java b/tools/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java index f6d81169..c0b29924 100644 --- a/tools/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java +++ b/tools/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java @@ -986,7 +986,7 @@ void writeTo(List output, String offset) { if (methods.stream().anyMatch(m -> "create".equals(m.jsonName))) { output.add("import com.microsoft.playwright.impl." + jsonName + "Impl;"); } - if (asList("Page", "Request", "Response", "APIRequestContext", "APIRequest", "APIResponse", "FileChooser", "Frame", "FrameLocator", "ElementHandle", "Locator", "Browser", "BrowserContext", "BrowserType", "Mouse", "Keyboard").contains(jsonName)) { + if (asList("Page", "Request", "Response", "APIRequestContext", "APIRequest", "APIResponse", "FileChooser", "Frame", "FrameLocator", "ElementHandle", "Locator", "Browser", "BrowserContext", "BrowserType", "Mouse", "Keyboard", "Tracing").contains(jsonName)) { output.add("import com.microsoft.playwright.options.*;"); } if ("Download".equals(jsonName)) {