Repository: microsoft/playwright-java (2025-07-08T10:38:20Z)

Issue:
[Ports]: Backport client side changes for 1.54
Please backport client side changes:
  - [x] https://github.com/microsoft/playwright/pull/36180
  - [x] https://github.com/microsoft/playwright/pull/36208
  - [x] https://github.com/microsoft/playwright/pull/36211
  - [x] https://github.com/microsoft/playwright/pull/36216
  - [x] https://github.com/microsoft/playwright/pull/36224
  - [x] https://github.com/microsoft/playwright/pull/36168
  - [x] https://github.com/microsoft/playwright/pull/35857
  - [x] https://github.com/microsoft/playwright/pull/36282
  - [x] https://github.com/microsoft/playwright/pull/36186
  - [x] https://github.com/microsoft/playwright/pull/36213
  - [x] https://github.com/microsoft/playwright/pull/36350
  - [x] https://github.com/microsoft/playwright/pull/36363
  - [x] https://github.com/microsoft/playwright/pull/36382
  - [x] https://github.com/microsoft/playwright/pull/36381
  - [x] https://github.com/microsoft/playwright/pull/36429
  - [x] https://github.com/microsoft/playwright/pull/36439
  - [x] https://github.com/microsoft/playwright/pull/36438
  - [x] https://github.com/microsoft/playwright/pull/36463
  - [x] https://github.com/microsoft/playwright/pull/36471
  - [x] https://github.com/microsoft/playwright/pull/36455
  - [x] https://github.com/microsoft/playwright/pull/36469
  - [x] https://github.com/microsoft/playwright/pull/36497
  - [x] https://github.com/microsoft/playwright/pull/36506
  - [x] https://github.com/microsoft/playwright/pull/36562
  - [x] https://github.com/microsoft/playwright/pull/36496
  - [x] https://github.com/microsoft/playwright/pull/36036
  - [x] https://github.com/microsoft/playwright/pull/36517
  - [x] https://github.com/microsoft/playwright/pull/36579
  - [x] https://github.com/microsoft/playwright/pull/36588
  - [ ] https://github.com/microsoft/playwright/pull/36598
  - [ ] https://github.com/microsoft/playwright/pull/36599
  - [ ] https://github.com/microsoft/playwright/pull/36604


Discussion:

Patch:
diff --git a/README.md b/README.md
index 7ba2b89e0..c59818785 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 <!-- GEN:chromium-version -->138.0.7204.23<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
+| Chromium <!-- GEN:chromium-version -->139.0.7258.5<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
 | WebKit <!-- GEN:webkit-version -->18.5<!-- GEN:stop --> | ✅ | ✅ | ✅ |
-| Firefox <!-- GEN:firefox-version -->139.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
+| Firefox <!-- GEN:firefox-version -->140.0.2<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
 
 ## Documentation
 
diff --git a/examples/pom.xml b/examples/pom.xml
index 85e64f832..7474e88fa 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -10,7 +10,7 @@
   <name>Playwright Client Examples</name>
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-    <playwright.version>1.53.0</playwright.version>
+    <playwright.version>1.54.0</playwright.version>
   </properties>
   <dependencies>
     <dependency>
diff --git a/playwright/src/main/java/com/microsoft/playwright/BrowserContext.java b/playwright/src/main/java/com/microsoft/playwright/BrowserContext.java
index 97b46508b..695a38a6f 100644
--- a/playwright/src/main/java/com/microsoft/playwright/BrowserContext.java
+++ b/playwright/src/main/java/com/microsoft/playwright/BrowserContext.java
@@ -596,7 +596,8 @@ public WaitForPageOptions setTimeout(double timeout) {
    */
   List<Page> backgroundPages();
   /**
-   * Returns the browser instance of the context. If it was launched as a persistent context null gets returned.
+   * Gets the browser instance that owns the context. Returns {@code null} if the context is created outside of normal
+   * browser, e.g. Android or Electron.
    *
    * @since v1.8
    */
@@ -871,6 +872,7 @@ default void exposeBinding(String name, BindingCallback callback) {
    * <li> {@code "notifications"}</li>
    * <li> {@code "payment-handler"}</li>
    * <li> {@code "storage-access"}</li>
+   * <li> {@code "local-fonts"}</li>
    * </ul>
    * @since v1.8
    */
@@ -903,6 +905,7 @@ default void grantPermissions(List<String> permissions) {
    * <li> {@code "notifications"}</li>
    * <li> {@code "payment-handler"}</li>
    * <li> {@code "storage-access"}</li>
+   * <li> {@code "local-fonts"}</li>
    * </ul>
    * @since v1.8
    */
diff --git a/playwright/src/main/java/com/microsoft/playwright/Mouse.java b/playwright/src/main/java/com/microsoft/playwright/Mouse.java
index 83c6d7c7f..dbb9fb781 100644
--- a/playwright/src/main/java/com/microsoft/playwright/Mouse.java
+++ b/playwright/src/main/java/com/microsoft/playwright/Mouse.java
@@ -21,6 +21,11 @@
 /**
  * The Mouse class operates in main-frame CSS pixels relative to the top-left corner of the viewport.
  *
+ * <p> <strong>NOTE:</strong> If you want to debug where the mouse moved, you can use the <a
+ * href="https://playwright.dev/java/docs/trace-viewer-intro">Trace viewer</a> or <a
+ * href="https://playwright.dev/java/docs/running-tests">Playwright Inspector</a>. A red dot showing the location of the
+ * mouse will be shown for every mouse action.
+ *
  * <p> Every {@code page} object has its own Mouse, accessible with {@link com.microsoft.playwright.Page#mouse Page.mouse()}.
  * <pre>{@code
  * // Using ‘page.mouse’ to trace a 100x100 square.
diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/AssertionsBase.java b/playwright/src/main/java/com/microsoft/playwright/impl/AssertionsBase.java
index 92ad7ef3c..c97796032 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/AssertionsBase.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/AssertionsBase.java
@@ -16,7 +16,6 @@
 
 package com.microsoft.playwright.impl;
 
-import com.microsoft.playwright.PlaywrightException;
 import org.opentest4j.AssertionFailedError;
 import org.opentest4j.ValueWrapper;
 
@@ -29,12 +28,10 @@
 import static com.microsoft.playwright.impl.Utils.toJsRegexFlags;
 import static java.util.Arrays.asList;
 
-class AssertionsBase {
-  final LocatorImpl actualLocator;
+abstract class AssertionsBase {
   final boolean isNot;
 
-  AssertionsBase(LocatorImpl actual, boolean isNot) {
-    this.actualLocator = actual;
+  AssertionsBase(boolean isNot) {
     this.isNot = isNot;
   }
 
@@ -58,7 +55,7 @@ void expectImpl(String expression, FrameExpectOptions expectOptions, Object expe
     if (isNot) {
       message = message.replace("expected to", "expected not to");
     }
-    FrameExpectResult result = actualLocator.expect(expression, expectOptions, title);
+    FrameExpectResult result = doExpect(expression, expectOptions, title);
     if (result.matches == isNot) {
       Object actual = result.received == null ? null : Serialization.deserialize(result.received);
       String log = (result.log == null) ? "" : String.join("\n", result.log);
@@ -75,7 +72,9 @@ void expectImpl(String expression, FrameExpectOptions expectOptions, Object expe
     }
   }
 
-  private static ValueWrapper formatValue(Object value) {
+  abstract FrameExpectResult doExpect(String expression, FrameExpectOptions expectOptions, String title);
+
+  protected static ValueWrapper formatValue(Object value) {
     if (value == null || !value.getClass().isArray()) {
       return ValueWrapper.create(value);
     }
diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/BrowserContextImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/BrowserContextImpl.java
index b1f21d880..55a1aec42 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/BrowserContextImpl.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/BrowserContextImpl.java
@@ -506,7 +506,7 @@ private void routeWebSocketImpl(UrlMatcher matcher, Consumer<WebSocketRoute> han
 
   void recordIntoHar(PageImpl page, Path har, RouteFromHAROptions options, HarContentPolicy contentPolicy) {
     if (contentPolicy == null) {
-      contentPolicy = Utils.convertType(options.updateContent, HarContentPolicy.class);;
+      contentPolicy = Utils.convertType(options.updateContent, HarContentPolicy.class);
     }
     if (contentPolicy == null) {
       contentPolicy = HarContentPolicy.ATTACH;
diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/ConsoleMessageImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/ConsoleMessageImpl.java
index e5aa1af1f..f199c26f8 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/ConsoleMessageImpl.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/ConsoleMessageImpl.java
@@ -20,13 +20,10 @@
 import com.google.gson.JsonObject;
 import com.microsoft.playwright.ConsoleMessage;
 import com.microsoft.playwright.JSHandle;
-import com.microsoft.playwright.Page;
 
 import java.util.ArrayList;
 import java.util.List;
 
-import static com.microsoft.playwright.impl.Serialization.gson;
-
 public class ConsoleMessageImpl implements ConsoleMessage {
   private final Connection connection;
   private PageImpl page;
diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/FrameImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/FrameImpl.java
index 5f1a0a53d..ac04719ab 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/FrameImpl.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/FrameImpl.java
@@ -993,7 +993,8 @@ ElementHandle waitForSelectorImpl(String selector, WaitForSelectorOptions option
   @Override
   public void waitForTimeout(double timeout) {
     JsonObject params = new JsonObject();
-    sendMessage("waitForTimeout", params, timeout);
+    params.addProperty("waitTimeout", timeout);
+    sendMessage("waitForTimeout", params, NO_TIMEOUT);
   }
 
   @Override
@@ -1085,4 +1086,16 @@ protected double navigationTimeout(Double timeout) {
     }
     return new TimeoutSettings().navigationTimeout(timeout);
   }
+
+  FrameExpectResult expect(String expression, FrameExpectOptions options, String title) {
+    return withTitle(title, () -> expect(expression, options));
+  }
+
+  FrameExpectResult expect(String expression, FrameExpectOptions options) {
+    JsonObject params = gson().toJsonTree(options).getAsJsonObject();
+    params.addProperty("expression", expression);
+    JsonElement json = sendMessage("expect", params, options.timeout);
+    FrameExpectResult result = gson().fromJson(json, FrameExpectResult.class);
+    return result;
+  }
 }
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 616366b06..088910c98 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/LocatorAssertionsImpl.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/LocatorAssertionsImpl.java
@@ -30,12 +30,20 @@
 import static com.microsoft.playwright.impl.Utils.convertType;
 
 public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAssertions {
+  LocatorImpl actualLocator;
+
   public LocatorAssertionsImpl(Locator locator) {
     this(locator, false);
   }
 
   private LocatorAssertionsImpl(Locator locator, boolean isNot) {
-    super((LocatorImpl) locator, isNot);
+    super(isNot);
+    this.actualLocator = (LocatorImpl) locator;
+  }
+
+  @Override
+  FrameExpectResult doExpect(String expression, FrameExpectOptions expectOptions, String title) {
+    return actualLocator.expect(expression, expectOptions, title);
   }
 
 
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 f27a3d43f..7c182ad3c 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/LocatorImpl.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/LocatorImpl.java
@@ -645,7 +645,8 @@ public int hashCode() {
   }
 
   FrameExpectResult expect(String expression, FrameExpectOptions options, String title) {
-    return frame.withTitle(title, () -> expectImpl(expression, options));
+    options.selector = selector;
+    return frame.expect(expression, options, title);
   }
 
   JsonObject toProtocol() {
@@ -654,13 +655,4 @@ JsonObject toProtocol() {
     result.addProperty("selector", selector);
     return result;
   }
-
-  private FrameExpectResult expectImpl(String expression, FrameExpectOptions options) {
-    JsonObject params = gson().toJsonTree(options).getAsJsonObject();
-    params.addProperty("selector", selector);
-    params.addProperty("expression", expression);
-    JsonElement json = frame.sendMessage("expect", params, options.timeout);
-    FrameExpectResult result = gson().fromJson(json, FrameExpectResult.class);
-    return result;
-  }
 }
diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/PageAssertionsImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/PageAssertionsImpl.java
index e4c113834..9a114f249 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/PageAssertionsImpl.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/PageAssertionsImpl.java
@@ -32,10 +32,16 @@ public PageAssertionsImpl(Page page) {
   }
 
   private PageAssertionsImpl(Page page, boolean isNot) {
-    super((LocatorImpl) page.locator(":root"), isNot);
+    super(isNot);
     this.actualPage = (PageImpl) page;
   }
 
+  @Override
+  FrameExpectResult doExpect(String expression, FrameExpectOptions expectOptions, String title) {
+    FrameImpl frame = (FrameImpl) actualPage.mainFrame();
+    return frame.expect(expression, expectOptions, title);
+  }
+
   @Override
   public void hasTitle(String title, HasTitleOptions options) {
     ExpectedTextValue expected = new ExpectedTextValue();
diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/Protocol.java b/playwright/src/main/java/com/microsoft/playwright/impl/Protocol.java
index 2caef1409..c1a1768b1 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/Protocol.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/Protocol.java
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-// This file is generated by generate_java_rpc.js, do not edit manually.
-
 package com.microsoft.playwright.impl;
 
 import java.util.List;
@@ -105,6 +103,7 @@ class ExpectedTextValue {
 class FrameExpectOptions {
   Object expressionArg;
   List<ExpectedTextValue> expectedText;
+  String selector;
   Double expectedNumber;
   SerializedArgument expectedValue;
   Boolean useInnerText;
diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/SelectorsImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/SelectorsImpl.java
index 62f5779be..878542ef1 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/SelectorsImpl.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/SelectorsImpl.java
@@ -68,6 +68,10 @@ public void register(String name, Path path, RegisterOptions options) {
   }
 
   private void registerImpl(String name, String script, RegisterOptions options) {
+    if (selectorEngines.stream().anyMatch(engine -> name.equals(engine.get("name").getAsString()))) {
+      throw new PlaywrightException("selectors.register: \"" + name + "\" selector engine has been already registered");
+    }
+
     JsonObject engine = new JsonObject();
     engine.addProperty("name", name);
     engine.addProperty("source", script);
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 53479fcf2..ab9ed40a0 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/WebSocketRouteImpl.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/WebSocketRouteImpl.java
@@ -128,7 +128,11 @@ void afterHandle() {
       return;
     }
     // Ensure that websocket is "open" and can send messages without an actual server connection.
-    sendMessageAsync("ensureOpened");
+    try {
+      sendMessageAsync("ensureOpened");
+    } catch (PlaywrightException e) {
+      // If this happens after the page has been closed, ignore the error.
+    }
   }
 
   @Override
diff --git a/playwright/src/main/java/com/microsoft/playwright/options/Cookie.java b/playwright/src/main/java/com/microsoft/playwright/options/Cookie.java
index ae3881329..ee7888536 100644
--- a/playwright/src/main/java/com/microsoft/playwright/options/Cookie.java
+++ b/playwright/src/main/java/com/microsoft/playwright/options/Cookie.java
@@ -48,6 +48,12 @@ public class Cookie {
    * Optional.
    */
   public SameSiteAttribute sameSite;
+  /**
+   * For partitioned third-party cookies (aka <a
+   * href="https://developer.mozilla.org/en-US/docs/Web/Privacy/Guides/Privacy_sandbox/Partitioned_cookies">CHIPS</a>), the
+   * partition key. Optional.
+   */
+  public String partitionKey;
 
   public Cookie(String name, String value) {
     this.name = name;
@@ -103,4 +109,13 @@ public Cookie setSameSite(SameSiteAttribute sameSite) {
     this.sameSite = sameSite;
     return this;
   }
+  /**
+   * For partitioned third-party cookies (aka <a
+   * href="https://developer.mozilla.org/en-US/docs/Web/Privacy/Guides/Privacy_sandbox/Partitioned_cookies">CHIPS</a>), the
+   * partition key. Optional.
+   */
+  public Cookie setPartitionKey(String partitionKey) {
+    this.partitionKey = partitionKey;
+    return this;
+  }
 }
\ No newline at end of file
diff --git a/scripts/DRIVER_VERSION b/scripts/DRIVER_VERSION
index 9adccc2f2..8ba9de6a3 100644
--- a/scripts/DRIVER_VERSION
+++ b/scripts/DRIVER_VERSION
@@ -1,1 +1,1 @@
-1.53.1
+1.54.0-alpha-2025-07-09


Test patch:
diff --git a/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextFetch.java b/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextFetch.java
index f8592f82e..d7352c748 100644
--- a/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextFetch.java
+++ b/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextFetch.java
@@ -436,7 +436,7 @@ void shouldSupportTimeoutOption() {
     PlaywrightException e = assertThrows(PlaywrightException.class, () -> {
       context.request().get(server.PREFIX + "/slow", RequestOptions.create().setTimeout(100));
     });
-    assertTrue(e.getMessage().contains("Request timed out after 100ms"), e.getMessage());
+    assertTrue(e.getMessage().contains("Timeout 100ms exceeded"), e.getMessage());
   }
 
   @Test
@@ -468,7 +468,7 @@ void shouldRespectTimeoutAfterRedirects() {
 
     context.setDefaultTimeout(100);
     PlaywrightException e = assertThrows(PlaywrightException.class, () -> context.request().get(server.PREFIX + "/redirect"));
-    assertTrue(e.getMessage().contains("Request timed out after 100ms"), e.getMessage());
+    assertTrue(e.getMessage().contains("Timeout 100ms exceeded"), e.getMessage());
   }
 
   @Test
diff --git a/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextStorageState.java b/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextStorageState.java
index 4d62fec4f..a398aaba0 100644
--- a/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextStorageState.java
+++ b/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextStorageState.java
@@ -174,10 +174,19 @@ void shouldSerialiseStorageStateWithLoneSurrogates() {
   @Test
   void shouldSupportIndexedDB() {
     page.navigate(server.PREFIX + "/to-do-notifications/index.html");
+
+    assertThat(page.locator("#notifications")).matchesAriaSnapshot(
+      "    - list:\n" +
+      "      - listitem: Database initialised."
+    );
     page.locator("label:has-text('Task title')").fill("Pet the cat");
     page.locator("label:has-text('Hours')").fill("1");
     page.locator("label:has-text('Mins')").fill("1");
     page.locator("text=Add Task").click();
+    assertThat(page.locator("#notifications")).matchesAriaSnapshot(
+      "    - list:\n" +
+      "      - listitem: \"Transaction completed: database modification finished.\""
+    );
 
     String storageState = page.context().storageState(new BrowserContext.StorageStateOptions().setIndexedDB(true));
     assertJsonEquals("{\"cookies\":[],\"origins\":[\n" +
diff --git a/playwright/src/test/java/com/microsoft/playwright/TestChromiumTracing.java b/playwright/src/test/java/com/microsoft/playwright/TestChromiumTracing.java
index 034d464c4..93d1a220c 100644
--- a/playwright/src/test/java/com/microsoft/playwright/TestChromiumTracing.java
+++ b/playwright/src/test/java/com/microsoft/playwright/TestChromiumTracing.java
@@ -63,12 +63,22 @@ void shouldRunWithCustomCategoriesIfProvided(@TempDir Path tempDir) throws IOExc
       Path outputTraceFile = tempDir.resolve("trace.json");
       browser.startTracing(page, new Browser.StartTracingOptions()
         .setPath(outputTraceFile)
-        .setCategories(asList("disabled-by-default-v8.cpu_profiler.hires")));
+        .setCategories(asList("disabled-by-default-cc.debug")));
+      page.evaluate("() => 1 + 1");
       browser.stopTracing();
       try (FileReader fileReader = new FileReader(outputTraceFile.toFile())) {
         JsonObject traceJson = new Gson().fromJson(fileReader, JsonObject.class);
-        assertTrue(traceJson.getAsJsonObject("metadata").get("trace-config")
-          .getAsString().contains("disabled-by-default-v8.cpu_profiler.hires"));
+        // NOTE: trace-config is deprecated as per http://crrev.com/c/6628182
+        boolean hasTraceConfig =
+              traceJson.getAsJsonObject("metadata").get("trace-config") != null
+          && traceJson.getAsJsonObject("metadata").get("trace-config").getAsString().contains("disabled-by-default-cc.debug");
+        boolean hasTraceEvents = traceJson.getAsJsonArray("traceEvents").asList().stream()
+            .anyMatch(event -> {
+              JsonObject eventObj = (JsonObject) event;
+              return eventObj.has("cat") && 
+                eventObj.get("cat").getAsString().equals("disabled-by-default-cc.debug");
+            });
+        assertTrue(hasTraceConfig || hasTraceEvents);
       }
     }
   }
diff --git a/playwright/src/test/java/com/microsoft/playwright/TestGlobalFetch.java b/playwright/src/test/java/com/microsoft/playwright/TestGlobalFetch.java
index f11e39f22..ce9e21f48 100644
--- a/playwright/src/test/java/com/microsoft/playwright/TestGlobalFetch.java
+++ b/playwright/src/test/java/com/microsoft/playwright/TestGlobalFetch.java
@@ -189,7 +189,7 @@ void shouldSupportGlobalTimeoutOption() {
     APIRequestContext request = playwright.request().newContext(new APIRequest.NewContextOptions().setTimeout(100));
     server.setRoute("/empty.html", exchange -> {});
     PlaywrightException e = assertThrows(PlaywrightException.class, () -> request.get(server.EMPTY_PAGE));
-    assertTrue(e.getMessage().contains("Request timed out after 100ms"), e.getMessage());
+    assertTrue(e.getMessage().contains("Timeout 100ms exceeded"), e.getMessage());
   }
 
 
diff --git a/playwright/src/test/java/com/microsoft/playwright/TestPageAddLocatorHandler.java b/playwright/src/test/java/com/microsoft/playwright/TestPageAddLocatorHandler.java
index 49a615f4f..5b5cab3e2 100644
--- a/playwright/src/test/java/com/microsoft/playwright/TestPageAddLocatorHandler.java
+++ b/playwright/src/test/java/com/microsoft/playwright/TestPageAddLocatorHandler.java
@@ -271,7 +271,7 @@ public void shouldWaitForHiddenByDefault2() {
 
     PlaywrightException e = assertThrows(PlaywrightException.class, () -> page.locator("#target").click(new Locator.ClickOptions().setTimeout(3_000)));
     assertEquals(0, (int) page.evaluate("window.clicked"));
-    assertThat(page.locator("#interstitial")).isVisible();
+    assertTrue(page.locator("#interstitial").isVisible());
     assertEquals(1, called[0]);
     assertTrue(e.getMessage().contains("locator handler has finished, waiting for getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName(\"close\")) to be hidden"), e.getMessage());
   }
diff --git a/playwright/src/test/java/com/microsoft/playwright/TestPageAriaSnapshot.java b/playwright/src/test/java/com/microsoft/playwright/TestPageAriaSnapshot.java
index 527e0d7b7..7dd9c8dfa 100644
--- a/playwright/src/test/java/com/microsoft/playwright/TestPageAriaSnapshot.java
+++ b/playwright/src/test/java/com/microsoft/playwright/TestPageAriaSnapshot.java
@@ -3,6 +3,7 @@
 import com.microsoft.playwright.junit.FixtureTest;
 import com.microsoft.playwright.junit.UsePlaywright;
 import org.junit.jupiter.api.Test;
+import org.opentest4j.AssertionFailedError;
 
 import java.util.Arrays;
 import java.util.List;
@@ -12,6 +13,7 @@
 
 import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 @FixtureTest
 @UsePlaywright
@@ -102,4 +104,27 @@ void shouldMatchUrl(Page page) {
       "- link:\n" +
       "  - /url: /.*example.com/");
   }
+
+  @Test
+  void shouldHandleTopLevelDeepEqual(Page page) {
+    // https://github.com/microsoft/playwright/issues/36456
+    page.setContent("" +
+      "<ul>\n" +
+      "  <li>\n" +
+      "    <ul>\n" +
+      "      <li>1.1</li>\n" +
+      "      <li>1.2</li>\n" +
+      "    </ul>\n" +
+      "  </li>\n" +
+      "</ul>");
+
+    assertThrows(AssertionFailedError.class, () -> {
+      assertThat(page.locator("body")).matchesAriaSnapshot("" +
+        "- /children: deep-equal\n" +
+        "- list:\n" +
+        "  - listitem:\n" +
+        "    - listitem: \"1.1\"\n" +
+        "    - listitem: \"1.2\"");
+    });
+  }
 }
diff --git a/playwright/src/test/java/com/microsoft/playwright/TestPageInterception.java b/playwright/src/test/java/com/microsoft/playwright/TestPageInterception.java
index a6462ff22..907d37bbc 100644
--- a/playwright/src/test/java/com/microsoft/playwright/TestPageInterception.java
+++ b/playwright/src/test/java/com/microsoft/playwright/TestPageInterception.java
@@ -99,7 +99,7 @@ void shouldSupportTimeoutOptionInRouteFetch() {
     page.route("**/*", route -> {
       PlaywrightException error = assertThrows(PlaywrightException.class,
         () -> route.fetch(new Route.FetchOptions().setTimeout(1000)));
-      assertTrue(error.getMessage().contains("Request timed out after 1000ms"), error.getMessage());
+      assertTrue(error.getMessage().contains("Timeout 1000ms exceeded"), error.getMessage());
     });
     PlaywrightException error = assertThrows(PlaywrightException.class,
       () -> page.navigate(server.PREFIX + "/slow", new Page.NavigateOptions().setTimeout(2000)));
@@ -198,6 +198,15 @@ void shouldWorkWithGlob() {
     assertTrue(urlMatches("http://playwright.dev/foo", "http://playwright.dev/foo?bar", "\\\\?bar"));
     assertTrue(urlMatches("http://first.host/", "http://second.host/foo", "**/foo"));
     assertTrue(urlMatches("http://playwright.dev/", "http://localhost/", "*//localhost/"));
+
+    String[] customPrefixes = {"about", "data", "chrome", "edge", "file"};
+    for (String prefix : customPrefixes) {
+      assertTrue(urlMatches("http://playwright.dev/", prefix + ":blank", prefix + ":blank"));
+      assertFalse(urlMatches("http://playwright.dev/", prefix + ":blank", "http://playwright.dev/"));
+      assertTrue(urlMatches(null, prefix + ":blank", prefix + ":blank"));
+      assertTrue(urlMatches(null, prefix + ":blank", prefix + ":*"));
+      assertFalse(urlMatches(null, "not" + prefix + ":blank", prefix + ":*"));
+    }
   }
 
   Pattern globToRegex(String glob) {
diff --git a/playwright/src/test/java/com/microsoft/playwright/TestSelectorsRegister.java b/playwright/src/test/java/com/microsoft/playwright/TestSelectorsRegister.java
index 4d50891ce..1c6cbcc0c 100644
--- a/playwright/src/test/java/com/microsoft/playwright/TestSelectorsRegister.java
+++ b/playwright/src/test/java/com/microsoft/playwright/TestSelectorsRegister.java
@@ -23,25 +23,26 @@
 import static org.junit.jupiter.api.Assertions.*;
 
 public class TestSelectorsRegister extends TestBase {
+  private static final String TAG_SELECTOR_SCRIPT = "{\n" +
+    "  create(root, target) {\n" +
+    "    return target.nodeName;\n" +
+    "  },\n" +
+    "  query(root, selector) {\n" +
+    "    return root.querySelector(selector);\n" +
+    "  },\n" +
+    "  queryAll(root, selector) {\n" +
+    "    return Array.from(root.querySelectorAll(selector));\n" +
+    "  }\n" +
+    "}";
+
   @Test
   void shouldWork() {
-    String selectorScript = "{\n" +
-      "  create(root, target) {\n" +
-      "    return target.nodeName;\n" +
-      "  },\n" +
-      "  query(root, selector) {\n" +
-      "    return root.querySelector(selector);\n" +
-      "  },\n" +
-      "  queryAll(root, selector) {\n" +
-      "    return Array.from(root.querySelectorAll(selector));\n" +
-      "  }\n" +
-      "}";
     // Register one engine before creating context.
-    playwright.selectors().register("tag", selectorScript);
+    playwright.selectors().register("tag", TAG_SELECTOR_SCRIPT);
 
     BrowserContext context = browser.newContext();
     // Register another engine after creating context.
-    playwright.selectors().register("tag2", selectorScript);
+    playwright.selectors().register("tag2", TAG_SELECTOR_SCRIPT);
 
     Page page = context.newPage();
     page.setContent("<div><span></span></div><div></div>");
@@ -134,4 +135,21 @@ void shouldHandleErrors() {
     });
     assertTrue(e.getMessage().contains("\"css\" is a predefined selector engine"));
   }
+
+  @Test
+  void shouldThrowAlreadyRegisteredErrorWhenRegistering() {
+    // https://github.com/microsoft/playwright/issues/36467
+
+    // this test is about the exception *before* there's a context created
+    context.close();
+
+    // Register the selector engine first
+    playwright.selectors().register("alreadyRegistered", TAG_SELECTOR_SCRIPT);
+    
+    // Attempt to register the same selector engine again should throw an error
+    PlaywrightException e = assertThrows(PlaywrightException.class, () -> {
+      playwright.selectors().register("alreadyRegistered", TAG_SELECTOR_SCRIPT);
+    });
+    assertTrue(e.getMessage().contains("\"alreadyRegistered\" selector engine has been already registered"));
+  }
 }