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 138.0.7204.23 | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Chromium 139.0.7258.5 | :white_check_mark: | :white_check_mark: | :white_check_mark: | | WebKit 18.5 | ✅ | ✅ | ✅ | -| Firefox 139.0 | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Firefox 140.0.2 | :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 @@ Playwright Client Examples UTF-8 - 1.53.0 + 1.54.0 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 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) { *
  • {@code "notifications"}
  • *
  • {@code "payment-handler"}
  • *
  • {@code "storage-access"}
  • + *
  • {@code "local-fonts"}
  • * * @since v1.8 */ @@ -903,6 +905,7 @@ default void grantPermissions(List permissions) { *
  • {@code "notifications"}
  • *
  • {@code "payment-handler"}
  • *
  • {@code "storage-access"}
  • + *
  • {@code "local-fonts"}
  • * * @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. * + *

    NOTE: If you want to debug where the mouse moved, you can use the Trace viewer or Playwright Inspector. A red dot showing the location of the + * mouse will be shown for every mouse action. + * *

    Every {@code page} object has its own Mouse, accessible with {@link com.microsoft.playwright.Page#mouse Page.mouse()}. *

    {@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 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 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 CHIPS), 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 CHIPS), the
    +   * partition key. Optional.
    +   */
    +  public Cookie setPartitionKey(String partitionKey) {
    +    this.partitionKey = partitionKey;
    +    return this;
    +  }
     }
    \ No newline at end of file
    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("" +
    +      "
      \n" + + "
    • \n" + + "
        \n" + + "
      • 1.1
      • \n" + + "
      • 1.2
      • \n" + + "
      \n" + + "
    • \n" + + "
    "); + + 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("
    "); @@ -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")); + } } 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.53.1 +1.54.0-alpha-2025-07-09