Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ Playwright is a Java library to automate [Chromium](https://www.chromium.org/Hom

| | Linux | macOS | Windows |
| :--- | :---: | :---: | :---: |
| Chromium <!-- GEN:chromium-version -->140.0.7339.16<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Chromium <!-- GEN:chromium-version -->141.0.7390.37<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->26.0<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Firefox <!-- GEN:firefox-version -->141.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Firefox <!-- GEN:firefox-version -->142.0.1<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |

## Documentation

Expand Down
2 changes: 1 addition & 1 deletion examples/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<name>Playwright Client Examples</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<playwright.version>1.55.0</playwright.version>
<playwright.version>1.56.0</playwright.version>
</properties>
<dependencies>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,7 @@
public interface BrowserContext extends AutoCloseable {

/**
* <strong>NOTE:</strong> Only works with Chromium browser's persistent context.
*
* <p> Emitted when new background page is created in the context.
* <pre>{@code
* context.onBackgroundPage(backgroundPage -> {
* System.out.println(backgroundPage.url());
* });
* }</pre>
* @deprecated Background pages have been removed from Chromium together with Manifest V2 extensions.
*/
void onBackgroundPage(Consumer<Page> handler);
/**
Expand Down Expand Up @@ -588,9 +581,7 @@ public WaitForPageOptions setTimeout(double timeout) {
*/
void addInitScript(Path script);
/**
* <strong>NOTE:</strong> Background pages are only supported on Chromium-based browsers.
*
* <p> All existing background pages in the context.
* @deprecated Background pages have been removed from Chromium together with Manifest V2 extensions.
*
* @since v1.11
*/
Expand Down
29 changes: 29 additions & 0 deletions playwright/src/main/java/com/microsoft/playwright/Page.java
Original file line number Diff line number Diff line change
Expand Up @@ -5729,6 +5729,20 @@ default boolean isVisible(String selector) {
* @since v1.8
*/
Keyboard keyboard();
/**
* Returns up to (currently) 200 last console messages from this page. See {@link
* com.microsoft.playwright.Page#onConsoleMessage Page.onConsoleMessage()} for more details.
*
* @since v1.56
*/
List<ConsoleMessage> consoleMessages();
/**
* Returns up to (currently) 200 last page errors from this page. See {@link com.microsoft.playwright.Page#onPageError
* Page.onPageError()} for more details.
*
* @since v1.56
*/
List<String> pageErrors();
/**
* The method returns an element locator that can be used to perform actions on this page / frame. Locator is resolved to
* the element immediately before performing an action, so a series of actions on the same locator can in fact be performed
Expand Down Expand Up @@ -6053,6 +6067,21 @@ default ElementHandle querySelector(String selector) {
* @since v1.9
*/
List<ElementHandle> querySelectorAll(String selector);
/**
* Returns up to (currently) 100 last network request from this page. See {@link com.microsoft.playwright.Page#onRequest
* Page.onRequest()} for more details.
*
* <p> Returned requests should be accessed immediately, otherwise they might be collected to prevent unbounded memory growth
* as new requests come in. Once collected, retrieving most information about the request is impossible.
*
* <p> Note that requests reported through the {@link com.microsoft.playwright.Page#onRequest Page.onRequest()} request are not
* collected, so there is a trade off between efficient memory usage with {@link com.microsoft.playwright.Page#requests
* Page.requests()} and the amount of available information reported through {@link com.microsoft.playwright.Page#onRequest
* Page.onRequest()}.
*
* @since v1.56
*/
List<Request> requests();
/**
* When testing a web page, sometimes unexpected overlays like a "Sign up" dialog appear and block actions you want to
* automate, e.g. clicking a button. These overlays don't always show up in the same way or at the same time, making them
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,15 @@ void expectImpl(String expression, FrameExpectOptions expectOptions, Object expe
if (!log.isEmpty()) {
log = "\nCall log:\n" + log;
}
if (result.errorMessage != null) {
message += "\n" + result.errorMessage;
}
if (expected == null) {
throw new AssertionFailedError(message + log);
}
ValueWrapper expectedValue = formatValue(expected);
ValueWrapper actualValue = formatValue(actual);
message += ": " + expectedValue.getStringRepresentation() + "\nReceived: " + actualValue.getStringRepresentation() + "\n";
message += "\nExpected: " + expectedValue.getStringRepresentation() + "\nReceived: " + actualValue.getStringRepresentation() + "\n";
throw new AssertionFailedError(message + log, expectedValue, actualValue);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@
import java.util.function.Predicate;
import java.util.regex.Pattern;

import static com.microsoft.playwright.impl.Serialization.addHarUrlFilter;
import static com.microsoft.playwright.impl.Serialization.gson;
import static com.microsoft.playwright.impl.Serialization.*;
import static com.microsoft.playwright.impl.Utils.*;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.Files.readAllBytes;
Expand Down Expand Up @@ -731,9 +730,12 @@ protected void handleEvent(String event, JsonObject params) {
bindingCall.call(binding);
}
} else if ("console".equals(event)) {
ConsoleMessageImpl message = new ConsoleMessageImpl(connection, params);
PageImpl page = null;
if (params.has("page")) {
page = connection.getExistingObject(params.getAsJsonObject("page").get("guid").getAsString());
}
ConsoleMessageImpl message = new ConsoleMessageImpl(connection, params, page);
listeners.notify(BrowserContextImpl.EventType.CONSOLE, message);
PageImpl page = message.page();
if (page != null) {
page.listeners.notify(PageImpl.EventType.CONSOLE, message);
}
Expand Down Expand Up @@ -781,14 +783,7 @@ protected void handleEvent(String event, JsonObject params) {
page.listeners.notify(PageImpl.EventType.RESPONSE, response);
}
} else if ("pageError".equals(event)) {
SerializedError error = gson().fromJson(params.getAsJsonObject("error"), SerializedError.class);
String errorStr = "";
if (error.error != null) {
errorStr = error.error.name + ": " + error.error.message;
if (error.error.stack != null && !error.error.stack.isEmpty()) {
errorStr += "\n" + error.error.stack;
}
}
String errorStr = parseError(params.getAsJsonObject("error"));
PageImpl page;
try {
page = connection.getExistingObject(params.getAsJsonObject("page").get("guid").getAsString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,12 @@

public class ConsoleMessageImpl implements ConsoleMessage {
private final Connection connection;
private PageImpl page;
private final PageImpl page;
private final JsonObject initializer;

public ConsoleMessageImpl(Connection connection, JsonObject initializer) {
public ConsoleMessageImpl(Connection connection, JsonObject initializer, PageImpl page) {
this.connection = connection;
// Note: currently, we only report console messages for pages and they always have a page.
// However, in the future we might report console messages for service workers or something else,
// where page() would be null.
if (initializer.has("page")) {
page = connection.getExistingObject(initializer.getAsJsonObject("page").get("guid").getAsString());
}
this.page = page;
this.initializer = initializer;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.regex.Pattern;

import static com.microsoft.playwright.impl.Serialization.gson;
import static com.microsoft.playwright.impl.Serialization.parseError;
import static com.microsoft.playwright.impl.Utils.*;
import static com.microsoft.playwright.options.ScreenshotType.JPEG;
import static com.microsoft.playwright.options.ScreenshotType.PNG;
Expand Down Expand Up @@ -567,6 +568,17 @@ public List<ElementHandle> querySelectorAll(String selector) {
return mainFrame.querySelectorAll(selector);
}

@Override
public List<Request> requests() {
JsonObject json = sendMessage("requests", new JsonObject(), NO_TIMEOUT).getAsJsonObject();
JsonArray requests = json.getAsJsonArray("requests");
List<Request> result = new ArrayList<>();
for (JsonElement item : requests) {
result.add(connection.getExistingObject(item.getAsJsonObject().get("guid").getAsString()));
}
return result;
}

@Override
public void addLocatorHandler(Locator locator, Consumer<Locator> handler, AddLocatorHandlerOptions options) {
LocatorImpl locatorImpl = (LocatorImpl) locator;
Expand Down Expand Up @@ -983,6 +995,29 @@ public Keyboard keyboard() {
return keyboard;
}

@Override
public List<ConsoleMessage> consoleMessages() {
JsonObject json = sendMessage("consoleMessages", new JsonObject(), NO_TIMEOUT).getAsJsonObject();
JsonArray messages = json.getAsJsonArray("messages");
List<ConsoleMessage> result = new ArrayList<>();
for (JsonElement item : messages) {
result.add(new ConsoleMessageImpl(connection, item.getAsJsonObject(), this));
}
return result;
}

@Override
public List<String> pageErrors() {
JsonObject json = sendMessage("pageErrors", new JsonObject(), NO_TIMEOUT).getAsJsonObject();
JsonArray errors = json.getAsJsonArray("errors");
List<String> result = new ArrayList<>();
for (JsonElement item : errors) {
String errorStr = parseError(item.getAsJsonObject());
result.add(errorStr);
}
return result;
}

@Override
public Locator locator(String selector, LocatorOptions options) {
return mainFrame.locator(selector, convertType(options, Frame.LocatorOptions.class));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ class FrameExpectOptions {
class FrameExpectResult {
boolean matches;
SerializedValue received;
String errorMessage;
List<String> log;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,18 @@ static List<String> parseStringList(JsonArray array) {
return result;
}

static String parseError(JsonObject object) {
SerializedError error = gson().fromJson(object, SerializedError.class);
String errorStr = "";
if (error.error != null) {
errorStr = error.error.name + ": " + error.error.message;
if (error.error.stack != null && !error.error.stack.isEmpty()) {
errorStr += "\n" + error.error.stack;
}
}
return errorStr;
}

private static class OptionalSerializer implements JsonSerializer<Optional<?>> {
private static boolean isSupported(Type type) {
return new TypeToken<Optional<Media>>() {}.getType().getTypeName().equals(type.getTypeName()) ||
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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;

import org.junit.jupiter.api.Test;
import org.opentest4j.AssertionFailedError;

import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class TestExpectMisc extends TestBase {
@Test
void strictModeViolationErrorFormat() {
page.setContent(" <div>hello</div><div>hi</div>");
AssertionFailedError error = assertThrows(AssertionFailedError.class, () ->
assertThat(page.locator("div")).isVisible());
assertTrue(error.getMessage().contains("Locator expected to be visible"), error.getMessage());
assertTrue(error.getMessage().contains("Error: strict mode violation: locator(\"div\") resolved to 2 elements:"), error.getMessage());
}

@Test
void invalidSelectorErrorFormat() {
page.setContent("<div>hello</div><div>hi</div>");
AssertionFailedError error = assertThrows(AssertionFailedError.class, () ->
assertThat(page.locator("##")).isVisible());
assertTrue(error.getMessage().contains("Locator expected to be visible"), error.getMessage());
assertTrue(error.getMessage().contains("Error: Unexpected token \"#\" while parsing css selector \"##\"."), error.getMessage());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ void hasTextWTextArrayFail() {
});
assertEquals("[Text 1, Text 3, Extra]", e.getExpected().getStringRepresentation());
assertEquals("[Text 1, Text 3]", e.getActual().getStringRepresentation());
assertTrue(e.getMessage().contains("Locator expected to have text: [Text 1, Text 3, Extra]"), e.getMessage());
assertTrue(e.getMessage().contains("Locator expected to have text\nExpected: [Text 1, Text 3, Extra]"), e.getMessage());
assertTrue(e.getMessage().contains("Received: [Text 1, Text 3]"), e.getMessage());
}

Expand Down Expand Up @@ -272,7 +272,7 @@ void hasAttributeTextFail() {
});
assertEquals("foo", e.getExpected().getStringRepresentation());
assertEquals("node", e.getActual().getStringRepresentation());
assertTrue(e.getMessage().contains("Locator expected to have attribute 'id': foo\nReceived: node"), e.getMessage());
assertTrue(e.getMessage().contains("Locator expected to have attribute 'id'\nExpected: foo\nReceived: node"), e.getMessage());
}

@Test
Expand All @@ -291,7 +291,7 @@ void hasAttributeRegExpFail() {
});
assertEquals(".Nod..", e.getExpected().getStringRepresentation());
assertEquals("node", e.getActual().getStringRepresentation());
assertTrue(e.getMessage().contains("Locator expected to have attribute 'id' matching regex: .Nod..\nReceived: node"), e.getMessage());
assertTrue(e.getMessage().contains("Locator expected to have attribute 'id' matching regex\nExpected: .Nod..\nReceived: node"), e.getMessage());
}

@Test
Expand Down Expand Up @@ -629,7 +629,7 @@ void hasValuesFailsWhenMultipleNotSpecified() {
" </select>");
Locator locator = page.locator("select");
locator.selectOption(new String[] {"B"});
PlaywrightException e = assertThrows(PlaywrightException.class, () -> {
AssertionFailedError e = assertThrows(AssertionFailedError.class, () -> {
assertThat(locator).hasValues(new Pattern[]{ Pattern.compile("R"), Pattern.compile("G")});
});
assertTrue(e.getMessage().contains("Not a select element with a multiple attribute"), e.getMessage());
Expand All @@ -639,7 +639,7 @@ void hasValuesFailsWhenMultipleNotSpecified() {
void hasValuesFailsWhenNotASelectElement() {
page.setContent("<input value=\"foo\" />");
Locator locator = page.locator("input");
PlaywrightException e = assertThrows(PlaywrightException.class, () -> {
AssertionFailedError e = assertThrows(AssertionFailedError.class, () -> {
assertThat(locator).hasValues(new Pattern[]{ Pattern.compile("R"), Pattern.compile("G")}, new LocatorAssertions.HasValuesOptions().setTimeout(1000));
});
assertTrue(e.getMessage().contains("Not a select element with a multiple attribute"), e.getMessage());
Expand All @@ -661,7 +661,7 @@ void isCheckedFail() {
});
assertEquals("checked", e.getExpected().getStringRepresentation());
assertEquals("unchecked", e.getActual().getStringRepresentation());
assertTrue(e.getMessage().contains("Locator expected to be: checked"), e.getMessage());
assertTrue(e.getMessage().contains("Locator expected to be\nExpected: checked"), e.getMessage());
}

@Test
Expand All @@ -674,7 +674,7 @@ void notIsCheckedFail() {

assertEquals("checked", e.getExpected().getStringRepresentation());
assertEquals("checked", e.getActual().getStringRepresentation());
assertTrue(e.getMessage().contains("Locator expected not to be: checked"), e.getMessage());
assertTrue(e.getMessage().contains("Locator expected not to be\nExpected: checked"), e.getMessage());
}

@Test
Expand All @@ -690,7 +690,7 @@ void isCheckedFalseFail() {
Locator locator = page.locator("input");
AssertionFailedError error = assertThrows(AssertionFailedError.class,
() -> assertThat(locator).isChecked(new LocatorAssertions.IsCheckedOptions().setChecked(false).setTimeout(1000)));
assertTrue(error.getMessage().contains("Locator expected to be: unchecked"), error.getMessage());
assertTrue(error.getMessage().contains("Locator expected to be\nExpected: unchecked"), error.getMessage());
}

@Test
Expand Down Expand Up @@ -796,7 +796,7 @@ void isEditableWithNotAndEditableFalse() {
void isEditableThrowsOnNonInputElement() {
page.setContent("<button>");
Locator locator = page.locator("button");
PlaywrightException e = assertThrows(PlaywrightException.class, () -> assertThat(locator).isEditable());
AssertionFailedError e = assertThrows(AssertionFailedError.class, () -> assertThat(locator).isEditable());
assertTrue(e.getMessage().contains("Element is not an <input>, <textarea>, <select> or [contenteditable] and does not have a role allowing [aria-readonly]"), e.getMessage());
}

Expand Down
Loading
Loading