1
0
mirror of synced 2026-05-23 19:23:20 +00:00

Compare commits

..

10 Commits

Author SHA1 Message Date
Yury Semikhatsky 0a5673b8bc chore: mark 1.37.0 (#1349) 2023-08-14 09:08:57 -07:00
Yury Semikhatsky d3495ca511 chore: roll driver to 1.37.0-beta (#1348) 2023-08-11 15:12:37 -07:00
Raphi 4b873ec3ad feat: Add support for Chrome DevTools Protocol (CDPSession) (#1329)
Add new methods BrowserContext.newCDPSession and
Browser.newBrowserCDPSession to create a Chrome
DevTools Protocol[1] session for the page and
browser respectively.

Fixes #823
[1] https://chromedevtools.github.io/devtools-protocol/
2023-08-10 16:42:57 -07:00
Yury Semikhatsky 463146ab11 chore: set dev version to 1.37 (#1337) 2023-07-14 14:50:36 -07:00
Yury Semikhatsky f6bc9a8b3d chore: roll driver to 1.36.1 (#1335) 2023-07-14 14:42:12 -07:00
Yury Semikhatsky 35e3c3653e fix: form data field encoding (#1333)
Fixes #1331
2023-07-11 09:52:21 -07:00
Yury Semikhatsky ed63ba4dcf chore: roll 1.36.0-beta-1689010164000 (#1332)
References #1311
2023-07-10 12:50:24 -07:00
Yury Semikhatsky 48a92d1a62 chore: roll to 1.36.0-alpha-jul-7-2023 (#1330) 2023-07-07 11:50:08 -07:00
Yury Semikhatsky d219fddc8b fix: NPE after pause (#1314) 2023-06-20 12:02:50 -07:00
Yury Semikhatsky 7a1bbe23b1 chore: bump dev version to 1.36.0-SNAPSHOT (#1305) 2023-06-12 15:57:36 -07:00
42 changed files with 684 additions and 70 deletions
+3 -3
View File
@@ -11,9 +11,9 @@ Playwright is a Java library to automate [Chromium](https://www.chromium.org/Hom
| | Linux | macOS | Windows |
| :--- | :---: | :---: | :---: |
| Chromium <!-- GEN:chromium-version -->115.0.5790.24<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->16.4<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Firefox <!-- GEN:firefox-version -->113.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Chromium <!-- GEN:chromium-version -->116.0.5845.82<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->17.0<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Firefox <!-- GEN:firefox-version -->115.0<!-- GEN:stop --> | :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/next/intro/#system-requirements) for details.
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.35.0</version>
<version>1.37.0</version>
</parent>
<artifactId>driver-bundle</artifactId>
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.35.0</version>
<version>1.37.0</version>
</parent>
<artifactId>driver</artifactId>
+1 -1
View File
@@ -6,7 +6,7 @@
<groupId>org.example</groupId>
<artifactId>examples</artifactId>
<version>1.35.0</version>
<version>1.37.0</version>
<name>Playwright Client Examples</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+1 -1
View File
@@ -7,7 +7,7 @@
<parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.35.0</version>
<version>1.37.0</version>
</parent>
<artifactId>playwright</artifactId>
@@ -120,7 +120,7 @@ public interface Browser extends AutoCloseable {
/**
* Whether the {@code meta viewport} tag is taken into account and touch events are enabled. isMobile is a part of device,
* so you don't actually need to set it manually. Defaults to {@code false} and is not supported in Firefox. Learn more
* about <a href="https://playwright.dev/java/docs/emulation#isMobile">mobile emulation</a>.
* about <a href="https://playwright.dev/java/docs/emulation#ismobile">mobile emulation</a>.
*/
public Boolean isMobile;
/**
@@ -236,8 +236,8 @@ public interface Browser extends AutoCloseable {
*/
public String userAgent;
/**
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the
* consistent viewport emulation. Learn more about <a href="https://playwright.dev/java/docs/emulation#viewport">viewport
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent
* viewport emulation. Learn more about <a href="https://playwright.dev/java/docs/emulation#viewport">viewport
* emulation</a>.
*
* <p> <strong>NOTE:</strong> The {@code null} value opts out from the default presets, makes viewport depend on the host window size defined by the
@@ -351,7 +351,7 @@ public interface Browser extends AutoCloseable {
/**
* Whether the {@code meta viewport} tag is taken into account and touch events are enabled. isMobile is a part of device,
* so you don't actually need to set it manually. Defaults to {@code false} and is not supported in Firefox. Learn more
* about <a href="https://playwright.dev/java/docs/emulation#isMobile">mobile emulation</a>.
* about <a href="https://playwright.dev/java/docs/emulation#ismobile">mobile emulation</a>.
*/
public NewContextOptions setIsMobile(boolean isMobile) {
this.isMobile = isMobile;
@@ -559,8 +559,8 @@ public interface Browser extends AutoCloseable {
return this;
}
/**
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the
* consistent viewport emulation. Learn more about <a href="https://playwright.dev/java/docs/emulation#viewport">viewport
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent
* viewport emulation. Learn more about <a href="https://playwright.dev/java/docs/emulation#viewport">viewport
* emulation</a>.
*
* <p> <strong>NOTE:</strong> The {@code null} value opts out from the default presets, makes viewport depend on the host window size defined by the
@@ -570,8 +570,8 @@ public interface Browser extends AutoCloseable {
return setViewportSize(new ViewportSize(width, height));
}
/**
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the
* consistent viewport emulation. Learn more about <a href="https://playwright.dev/java/docs/emulation#viewport">viewport
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent
* viewport emulation. Learn more about <a href="https://playwright.dev/java/docs/emulation#viewport">viewport
* emulation</a>.
*
* <p> <strong>NOTE:</strong> The {@code null} value opts out from the default presets, makes viewport depend on the host window size defined by the
@@ -646,7 +646,7 @@ public interface Browser extends AutoCloseable {
/**
* Whether the {@code meta viewport} tag is taken into account and touch events are enabled. isMobile is a part of device,
* so you don't actually need to set it manually. Defaults to {@code false} and is not supported in Firefox. Learn more
* about <a href="https://playwright.dev/java/docs/emulation#isMobile">mobile emulation</a>.
* about <a href="https://playwright.dev/java/docs/emulation#ismobile">mobile emulation</a>.
*/
public Boolean isMobile;
/**
@@ -762,8 +762,8 @@ public interface Browser extends AutoCloseable {
*/
public String userAgent;
/**
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the
* consistent viewport emulation. Learn more about <a href="https://playwright.dev/java/docs/emulation#viewport">viewport
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent
* viewport emulation. Learn more about <a href="https://playwright.dev/java/docs/emulation#viewport">viewport
* emulation</a>.
*
* <p> <strong>NOTE:</strong> The {@code null} value opts out from the default presets, makes viewport depend on the host window size defined by the
@@ -877,7 +877,7 @@ public interface Browser extends AutoCloseable {
/**
* Whether the {@code meta viewport} tag is taken into account and touch events are enabled. isMobile is a part of device,
* so you don't actually need to set it manually. Defaults to {@code false} and is not supported in Firefox. Learn more
* about <a href="https://playwright.dev/java/docs/emulation#isMobile">mobile emulation</a>.
* about <a href="https://playwright.dev/java/docs/emulation#ismobile">mobile emulation</a>.
*/
public NewPageOptions setIsMobile(boolean isMobile) {
this.isMobile = isMobile;
@@ -1085,8 +1085,8 @@ public interface Browser extends AutoCloseable {
return this;
}
/**
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the
* consistent viewport emulation. Learn more about <a href="https://playwright.dev/java/docs/emulation#viewport">viewport
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent
* viewport emulation. Learn more about <a href="https://playwright.dev/java/docs/emulation#viewport">viewport
* emulation</a>.
*
* <p> <strong>NOTE:</strong> The {@code null} value opts out from the default presets, makes viewport depend on the host window size defined by the
@@ -1096,8 +1096,8 @@ public interface Browser extends AutoCloseable {
return setViewportSize(new ViewportSize(width, height));
}
/**
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the
* consistent viewport emulation. Learn more about <a href="https://playwright.dev/java/docs/emulation#viewport">viewport
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent
* viewport emulation. Learn more about <a href="https://playwright.dev/java/docs/emulation#viewport">viewport
* emulation</a>.
*
* <p> <strong>NOTE:</strong> The {@code null} value opts out from the default presets, makes viewport depend on the host window size defined by the
@@ -1186,6 +1186,14 @@ public interface Browser extends AutoCloseable {
* @since v1.8
*/
boolean isConnected();
/**
* <strong>NOTE:</strong> CDP Sessions are only supported on Chromium-based browsers.
*
* <p> Returns the newly created browser session.
*
* @since v1.11
*/
CDPSession newBrowserCDPSession();
/**
* Creates a new browser context. It won't share cookies/cache with other browser contexts.
*
@@ -752,6 +752,26 @@ public interface BrowserContext extends AutoCloseable {
* @since v1.8
*/
void grantPermissions(List<String> permissions, GrantPermissionsOptions options);
/**
* <strong>NOTE:</strong> CDP sessions are only supported on Chromium-based browsers.
*
* <p> Returns the newly created session.
*
* @param page Target to create new session for. For backwards-compatibility, this parameter is named {@code page}, but it can be a
* {@code Page} or {@code Frame} type.
* @since v1.11
*/
CDPSession newCDPSession(Page page);
/**
* <strong>NOTE:</strong> CDP sessions are only supported on Chromium-based browsers.
*
* <p> Returns the newly created session.
*
* @param page Target to create new session for. For backwards-compatibility, this parameter is named {@code page}, but it can be a
* {@code Page} or {@code Frame} type.
* @since v1.11
*/
CDPSession newCDPSession(Frame page);
/**
* Creates a new page in the browser context.
*
@@ -1096,7 +1116,7 @@ public interface BrowserContext extends AutoCloseable {
void route(Predicate<String> url, Consumer<Route> handler, RouteOptions options);
/**
* If specified the network requests that are made in the context will be served from the HAR file. Read more about <a
* href="https://playwright.dev/java/docs/network#replaying-from-har">Replaying from HAR</a>.
* href="https://playwright.dev/java/docs/mock#replaying-from-har">Replaying from HAR</a>.
*
* <p> Playwright will not serve requests intercepted by Service Worker from the HAR file. See <a
* href="https://github.com/microsoft/playwright/issues/1090">this</a> issue. We recommend disabling Service Workers when
@@ -1111,7 +1131,7 @@ public interface BrowserContext extends AutoCloseable {
}
/**
* If specified the network requests that are made in the context will be served from the HAR file. Read more about <a
* href="https://playwright.dev/java/docs/network#replaying-from-har">Replaying from HAR</a>.
* href="https://playwright.dev/java/docs/mock#replaying-from-har">Replaying from HAR</a>.
*
* <p> Playwright will not serve requests intercepted by Service Worker from the HAR file. See <a
* href="https://github.com/microsoft/playwright/issues/1090">this</a> issue. We recommend disabling Service Workers when
@@ -43,6 +43,26 @@ import java.util.regex.Pattern;
*/
public interface BrowserType {
class ConnectOptions {
/**
* This option exposes network available on the connecting client to the browser being connected to. Consists of a list of
* rules separated by comma.
*
* <p> Available rules:
* <ol>
* <li> Hostname pattern, for example: {@code example.com}, {@code *.org:99}, {@code x.*.y.com}, {@code *foo.org}.</li>
* <li> IP literal, for example: {@code 127.0.0.1}, {@code 0.0.0.0:99}, {@code [::1]}, {@code [0:0::1]:99}.</li>
* <li> {@code <loopback>} that matches local loopback interfaces: {@code localhost}, {@code *.localhost}, {@code 127.0.0.1},
* {@code [::1]}.</li>
* </ol>
*
* <p> Some common examples:
* <ol>
* <li> {@code "*"} to expose all network.</li>
* <li> {@code "<loopback>"} to expose localhost network.</li>
* <li> {@code "*.test.internal-domain,*.staging.internal-domain,<loopback>"} to expose test/staging deployments and localhost.</li>
* </ol>
*/
public String exposeNetwork;
/**
* Additional HTTP headers to be sent with web socket connect request. Optional.
*/
@@ -57,6 +77,29 @@ public interface BrowserType {
*/
public Double timeout;
/**
* This option exposes network available on the connecting client to the browser being connected to. Consists of a list of
* rules separated by comma.
*
* <p> Available rules:
* <ol>
* <li> Hostname pattern, for example: {@code example.com}, {@code *.org:99}, {@code x.*.y.com}, {@code *foo.org}.</li>
* <li> IP literal, for example: {@code 127.0.0.1}, {@code 0.0.0.0:99}, {@code [::1]}, {@code [0:0::1]:99}.</li>
* <li> {@code <loopback>} that matches local loopback interfaces: {@code localhost}, {@code *.localhost}, {@code 127.0.0.1},
* {@code [::1]}.</li>
* </ol>
*
* <p> Some common examples:
* <ol>
* <li> {@code "*"} to expose all network.</li>
* <li> {@code "<loopback>"} to expose localhost network.</li>
* <li> {@code "*.test.internal-domain,*.staging.internal-domain,<loopback>"} to expose test/staging deployments and localhost.</li>
* </ol>
*/
public ConnectOptions setExposeNetwork(String exposeNetwork) {
this.exposeNetwork = exposeNetwork;
return this;
}
/**
* Additional HTTP headers to be sent with web socket connect request. Optional.
*/
@@ -496,7 +539,7 @@ public interface BrowserType {
/**
* Whether the {@code meta viewport} tag is taken into account and touch events are enabled. isMobile is a part of device,
* so you don't actually need to set it manually. Defaults to {@code false} and is not supported in Firefox. Learn more
* about <a href="https://playwright.dev/java/docs/emulation#isMobile">mobile emulation</a>.
* about <a href="https://playwright.dev/java/docs/emulation#ismobile">mobile emulation</a>.
*/
public Boolean isMobile;
/**
@@ -610,8 +653,8 @@ public interface BrowserType {
*/
public String userAgent;
/**
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the
* consistent viewport emulation. Learn more about <a href="https://playwright.dev/java/docs/emulation#viewport">viewport
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent
* viewport emulation. Learn more about <a href="https://playwright.dev/java/docs/emulation#viewport">viewport
* emulation</a>.
*
* <p> <strong>NOTE:</strong> The {@code null} value opts out from the default presets, makes viewport depend on the host window size defined by the
@@ -839,7 +882,7 @@ public interface BrowserType {
/**
* Whether the {@code meta viewport} tag is taken into account and touch events are enabled. isMobile is a part of device,
* so you don't actually need to set it manually. Defaults to {@code false} and is not supported in Firefox. Learn more
* about <a href="https://playwright.dev/java/docs/emulation#isMobile">mobile emulation</a>.
* about <a href="https://playwright.dev/java/docs/emulation#ismobile">mobile emulation</a>.
*/
public LaunchPersistentContextOptions setIsMobile(boolean isMobile) {
this.isMobile = isMobile;
@@ -1044,8 +1087,8 @@ public interface BrowserType {
return this;
}
/**
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the
* consistent viewport emulation. Learn more about <a href="https://playwright.dev/java/docs/emulation#viewport">viewport
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent
* viewport emulation. Learn more about <a href="https://playwright.dev/java/docs/emulation#viewport">viewport
* emulation</a>.
*
* <p> <strong>NOTE:</strong> The {@code null} value opts out from the default presets, makes viewport depend on the host window size defined by the
@@ -1055,8 +1098,8 @@ public interface BrowserType {
return setViewportSize(new ViewportSize(width, height));
}
/**
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the
* consistent viewport emulation. Learn more about <a href="https://playwright.dev/java/docs/emulation#viewport">viewport
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent
* viewport emulation. Learn more about <a href="https://playwright.dev/java/docs/emulation#viewport">viewport
* emulation</a>.
*
* <p> <strong>NOTE:</strong> The {@code null} value opts out from the default presets, makes viewport depend on the host window size defined by the
@@ -0,0 +1,94 @@
/*
* 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 java.util.function.Consumer;
import com.google.gson.JsonObject;
/**
* The {@code CDPSession} instances are used to talk raw Chrome Devtools Protocol:
* <ul>
* <li> protocol methods can be called with {@code session.send} method.</li>
* <li> protocol events can be subscribed to with {@code session.on} method.</li>
* </ul>
*
* <p> Useful links:
* <ul>
* <li> Documentation on DevTools Protocol can be found here: <a
* href="https://chromedevtools.github.io/devtools-protocol/">DevTools Protocol Viewer</a>.</li>
* <li> Getting Started with DevTools Protocol: https://github.com/aslushnikov/getting-started-with-cdp/blob/master/README.md</li>
* <pre>{@code
* CDPSession client = page.context().newCDPSession(page);
* client.send("Runtime.enable");
*
* client.on("Animation.animationCreated", (event) -> System.out.println("Animation created!"));
*
* JsonObject response = client.send("Animation.getPlaybackRate");
* double playbackRate = response.get("playbackRate").getAsDouble();
* System.out.println("playback rate is " + playbackRate);
*
* JsonObject params = new JsonObject();
* params.addProperty("playbackRate", playbackRate / 2);
* client.send("Animation.setPlaybackRate", params);
* }</pre>
* </ul>
*/
public interface CDPSession {
/**
* Detaches the CDPSession from the target. Once detached, the CDPSession object won't emit any events and can't be used to
* send messages.
*
* @since v1.8
*/
void detach();
/**
*
*
* @param method Protocol method name.
* @since v1.8
*/
default JsonObject send(String method) {
return send(method, null);
}
/**
*
*
* @param method Protocol method name.
* @param args Optional method parameters.
* @since v1.8
*/
JsonObject send(String method, JsonObject args);
/**
* Register an event handler for events with the specified event name. The given handler will be called for every event
* with the given name.
*
* @param eventName CDP event name.
* @param handler Event handler.
* @since v1.37
*/
void on(String eventName, Consumer<JsonObject> handler);
/**
* Unregister an event handler for events with the specified event name. The given handler will not be called anymore for
* events with the given name.
*
* @param eventName CDP event name.
* @param handler Event handler.
* @since v1.37
*/
void off(String eventName, Consumer<JsonObject> handler);
}
@@ -4473,7 +4473,9 @@ public interface Frame {
*/
void setChecked(String selector, boolean checked, SetCheckedOptions options);
/**
*
* This method internally calls <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/Document/write">document.write()</a>, inheriting all its specific
* characteristics and behaviors.
*
* @param html HTML markup to assign to the page.
* @since v1.8
@@ -4482,7 +4484,9 @@ public interface Frame {
setContent(html, null);
}
/**
*
* This method internally calls <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/Document/write">document.write()</a>, inheriting all its specific
* characteristics and behaviors.
*
* @param html HTML markup to assign to the page.
* @since v1.8
@@ -135,7 +135,7 @@ public interface JSHandle {
*
* <p> **Usage**
* <pre>{@code
* JSHandle handle = page.evaluateHandle("() => ({window, document})");
* JSHandle handle = page.evaluateHandle("() => ({ window, document })");
* Map<String, JSHandle> properties = handle.getProperties();
* JSHandle windowHandle = properties.get("window");
* JSHandle documentHandle = properties.get("document");
@@ -23,7 +23,7 @@ import java.util.regex.Pattern;
/**
* Locators are the central piece of Playwright's auto-waiting and retry-ability. In a nutshell, locators represent a way
* to find element(s) on the page at any moment. Locator can be created with the {@link Page#locator Page.locator()}
* to find element(s) on the page at any moment. A locator can be created with the {@link Page#locator Page.locator()}
* method.
*
* <p> <a href="https://playwright.dev/java/docs/locators">Learn more about locators</a>.
@@ -2033,7 +2033,7 @@ public interface Locator {
}
}
/**
* When locator points to a list of elements, returns array of locators, pointing to respective elements.
* When the locator points to a list of elements, this returns an array of locators, pointing to their respective elements.
*
* <p> <strong>NOTE:</strong> {@link Locator#all Locator.all()} does not wait for elements to match the locator, and instead immediately returns
* whatever is present in the page. When the list of elements changes dynamically, {@link Locator#all Locator.all()} will
@@ -4865,7 +4865,10 @@ public interface Locator {
*/
String textContent(TextContentOptions options);
/**
* Focuses the element, and then sends a {@code keydown}, {@code keypress}/{@code input}, and {@code keyup} event for each
* <strong>NOTE:</strong> In most cases, you should use {@link Locator#fill Locator.fill()} instead. You only need to type characters if there is
* special keyboard handling on the page.
*
* <p> Focuses the element, and then sends a {@code keydown}, {@code keypress}/{@code input}, and {@code keyup} event for each
* character in the text.
*
* <p> To press a special key, like {@code Control} or {@code ArrowDown}, use {@link Locator#press Locator.press()}.
@@ -4890,7 +4893,10 @@ public interface Locator {
type(text, null);
}
/**
* Focuses the element, and then sends a {@code keydown}, {@code keypress}/{@code input}, and {@code keyup} event for each
* <strong>NOTE:</strong> In most cases, you should use {@link Locator#fill Locator.fill()} instead. You only need to type characters if there is
* special keyboard handling on the page.
*
* <p> Focuses the element, and then sends a {@code keydown}, {@code keypress}/{@code input}, and {@code keyup} event for each
* character in the text.
*
* <p> To press a special key, like {@code Control} or {@code ArrowDown}, use {@link Locator#press Locator.press()}.
@@ -6201,7 +6201,7 @@ public interface Page extends AutoCloseable {
void route(Predicate<String> url, Consumer<Route> handler, RouteOptions options);
/**
* If specified the network requests that are made in the page will be served from the HAR file. Read more about <a
* href="https://playwright.dev/java/docs/network#replaying-from-har">Replaying from HAR</a>.
* href="https://playwright.dev/java/docs/mock#replaying-from-har">Replaying from HAR</a>.
*
* <p> Playwright will not serve requests intercepted by Service Worker from the HAR file. See <a
* href="https://github.com/microsoft/playwright/issues/1090">this</a> issue. We recommend disabling Service Workers when
@@ -6216,7 +6216,7 @@ public interface Page extends AutoCloseable {
}
/**
* If specified the network requests that are made in the page will be served from the HAR file. Read more about <a
* href="https://playwright.dev/java/docs/network#replaying-from-har">Replaying from HAR</a>.
* href="https://playwright.dev/java/docs/mock#replaying-from-har">Replaying from HAR</a>.
*
* <p> Playwright will not serve requests intercepted by Service Worker from the HAR file. See <a
* href="https://github.com/microsoft/playwright/issues/1090">this</a> issue. We recommend disabling Service Workers when
@@ -6672,7 +6672,9 @@ public interface Page extends AutoCloseable {
*/
void setChecked(String selector, boolean checked, SetCheckedOptions options);
/**
*
* This method internally calls <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/Document/write">document.write()</a>, inheriting all its specific
* characteristics and behaviors.
*
* @param html HTML markup to assign to the page.
* @since v1.8
@@ -6681,7 +6683,9 @@ public interface Page extends AutoCloseable {
setContent(html, null);
}
/**
*
* This method internally calls <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/Document/write">document.write()</a>, inheriting all its specific
* characteristics and behaviors.
*
* @param html HTML markup to assign to the page.
* @since v1.8
@@ -19,6 +19,8 @@ package com.microsoft.playwright.impl;
import com.google.gson.JsonObject;
import com.microsoft.playwright.PlaywrightException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileSystems;
import java.nio.file.Path;
@@ -39,6 +41,20 @@ class ArtifactImpl extends ChannelOwner {
return stream.stream();
}
byte[] readAllBytes() {
final int bufLen = 1024 * 1024;
byte[] buf = new byte[bufLen];
int readLen;
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); InputStream stream = createReadStream()) {
while ((readLen = stream.read(buf, 0, bufLen)) != -1) {
outputStream.write(buf, 0, readLen);
}
return outputStream.toByteArray();
} catch (IOException e) {
throw new PlaywrightException("Failed to read artifact", e);
}
}
public void cancel() {
sendMessage("cancel");
}
@@ -214,6 +214,22 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
return waitForEventWithTimeout(EventType.PAGE, code, options.predicate, options.timeout);
}
@Override
public CDPSession newCDPSession(Page page) {
JsonObject params = new JsonObject();
params.add("page", ((PageImpl) page).toProtocolRef());
JsonObject result = sendMessage("newCDPSession", params).getAsJsonObject();
return connection.getExistingObject(result.getAsJsonObject("session").get("guid").getAsString());
}
@Override
public CDPSession newCDPSession(Frame frame) {
JsonObject params = new JsonObject();
params.add("frame", ((FrameImpl) frame).toProtocolRef());
JsonObject result = sendMessage("newCDPSession", params).getAsJsonObject();
return connection.getExistingObject(result.getAsJsonObject("session").get("guid").getAsString());
}
@Override
public void close() {
withLogging("BrowserContext.close", () -> closeImpl());
@@ -445,6 +461,10 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
@Override
public void setDefaultNavigationTimeout(double timeout) {
setDefaultNavigationTimeoutImpl(timeout);
}
void setDefaultNavigationTimeoutImpl(Double timeout) {
withLogging("BrowserContext.setDefaultNavigationTimeout", () -> {
timeoutSettings.setDefaultNavigationTimeout(timeout);
JsonObject params = new JsonObject();
@@ -455,6 +475,10 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
@Override
public void setDefaultTimeout(double timeout) {
setDefaultTimeoutImpl(timeout);
}
void setDefaultTimeoutImpl(Double timeout) {
withLogging("BrowserContext.setDefaultTimeout", () -> {
timeoutSettings.setDefaultTimeout(timeout);
JsonObject params = new JsonObject();
@@ -28,7 +28,6 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import static com.microsoft.playwright.impl.Serialization.addHarUrlFilter;
import static com.microsoft.playwright.impl.Serialization.gson;
@@ -42,6 +41,7 @@ class BrowserImpl extends ChannelOwner implements Browser {
private boolean isConnected = true;
BrowserTypeImpl browserType;
BrowserType.LaunchOptions launchOptions;
private Path tracePath;
enum EventType {
DISCONNECTED,
@@ -231,6 +231,7 @@ class BrowserImpl extends ChannelOwner implements Browser {
if (options == null) {
options = new StartTracingOptions();
}
tracePath = options.path;
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
if (page != null) {
params.add("page", ((PageImpl) page).toProtocolRef());
@@ -245,7 +246,20 @@ class BrowserImpl extends ChannelOwner implements Browser {
private byte[] stopTracingImpl() {
JsonObject json = sendMessage("stopTracing").getAsJsonObject();
return Base64.getDecoder().decode(json.get("binary").getAsString());
ArtifactImpl artifact = connection.getExistingObject(json.getAsJsonObject().getAsJsonObject("artifact").get("guid").getAsString());
byte[] data = artifact.readAllBytes();
artifact.delete();
if (tracePath != null) {
try {
Files.createDirectories(tracePath.getParent());
Files.write(tracePath, data);
} catch (IOException e) {
throw new PlaywrightException("Failed to write trace file", e);
} finally {
tracePath = null;
}
}
return data;
}
private Page newPageImpl(NewPageOptions options) {
@@ -276,6 +290,13 @@ class BrowserImpl extends ChannelOwner implements Browser {
}
}
@Override
public CDPSession newBrowserCDPSession() {
JsonObject params = new JsonObject();
JsonObject result = sendMessage("newBrowserCDPSession", params).getAsJsonObject();
return connection.getExistingObject(result.getAsJsonObject("session").get("guid").getAsString());
}
private void didClose() {
isConnected = false;
listeners.notify(EventType.DISCONNECTED, this);
@@ -0,0 +1,56 @@
package com.microsoft.playwright.impl;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.microsoft.playwright.CDPSession;
import java.util.HashMap;
import java.util.function.Consumer;
public class CDPSessionImpl extends ChannelOwner implements CDPSession {
private final ListenerCollection<String> listeners = new ListenerCollection<>(new HashMap<>(), this);
protected CDPSessionImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
super(parent, type, guid, initializer);
}
@Override
void handleEvent(String event, JsonObject parameters) {
super.handleEvent(event, parameters);
if ("event".equals(event)) {
String method = parameters.get("method").getAsString();
JsonObject params = parameters.get("params").getAsJsonObject();
listeners.notify(method, params);
}
}
public JsonObject send(String method) {
return send(method, null);
}
public JsonObject send(String method, JsonObject params) {
JsonObject args = new JsonObject();
if (params != null) {
args.add("params", params);
}
args.addProperty("method", method);
JsonElement response = connection.sendMessage(guid, "send", args);
if (response == null) return null;
else return response.getAsJsonObject().get("result").getAsJsonObject();
}
@Override
public void on(String event, Consumer<JsonObject> handler) {
listeners.add(event, handler);
}
@Override
public void off(String event, Consumer<JsonObject> handler) {
listeners.remove(event, handler);
}
@Override
public void detach() {
sendMessage("detach");
}
}
@@ -359,6 +359,9 @@ public class Connection {
case "WritableStream":
result = new WritableStream(parent, type, guid, initializer);
break;
case "CDPSession":
result = new CDPSessionImpl(parent, type, guid, initializer);
break;
default:
throw new PlaywrightException("Unknown type " + type);
}
@@ -407,7 +407,7 @@ class LocatorImpl implements Locator {
if (other.frame != frame) {
throw new PlaywrightException("Locators must belong to the same frame.");
}
return locator(other.selector, options);
return new LocatorImpl(frame, this.selector + " >> internal:chain=" + gson().toJson(other.selector), options);
}
@Override
@@ -938,13 +938,13 @@ public class PageImpl extends ChannelOwner implements Page {
withLogging("Page.pause", () -> {
Double defaultNavigationTimeout = browserContext.timeoutSettings.defaultNavigationTimeout();
Double defaultTimeout = browserContext.timeoutSettings.defaultTimeout();
browserContext.setDefaultNavigationTimeout(0);
browserContext.setDefaultTimeout(0);
browserContext.setDefaultNavigationTimeoutImpl(0.0);
browserContext.setDefaultTimeoutImpl(0.0);
try {
runUntil(() -> {}, new WaitableRace<>(asList(context().pause(), (Waitable<JsonElement>) waitableClosedOrCrashed)));
} finally {
browserContext.setDefaultNavigationTimeout(defaultNavigationTimeout);
browserContext.setDefaultTimeout(defaultTimeout);
browserContext.setDefaultNavigationTimeoutImpl(defaultNavigationTimeout);
browserContext.setDefaultTimeoutImpl(defaultTimeout);
}
});
}
@@ -32,6 +32,7 @@ class SerializedValue{
String v;
String d;
String u;
String bi;
public static class R {
String p;
String f;
@@ -28,6 +28,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Type;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
@@ -162,6 +163,8 @@ class Serialization {
result.d = ((LocalDateTime)value).atZone(ZoneId.systemDefault()).toInstant().toString();
} else if (value instanceof URL) {
result.u = ((URL)value).toString();
} else if (value instanceof BigInteger) {
result.bi = ((BigInteger)value).toString();
} else if (value instanceof Pattern) {
result.r = new SerializedValue.R();
result.r.p = ((Pattern)value).pattern();
@@ -236,6 +239,9 @@ class Serialization {
throw new PlaywrightException("Unexpected value: " + value.u, e);
}
}
if (value.bi != null) {
return (T) new BigInteger(value.bi);
}
if (value.d != null)
return (T)(Date.from(Instant.parse(value.d)));
if (value.r != null)
@@ -330,6 +336,11 @@ class Serialization {
}
static JsonArray toProtocol(Map<String, String> map) {
for (String value : map.values()) {
if (value == null) {
throw new PlaywrightException("Value cannot be null");
}
}
return toNameValueArray(map);
}
@@ -348,7 +359,11 @@ class Serialization {
for (Map.Entry<String, ?> e : map.entrySet()) {
JsonObject item = new JsonObject();
item.addProperty("name", e.getKey());
item.add("value", gson().toJsonTree(e.getValue()));
if (e.getValue() instanceof FilePayload) {
item.add("value", gson().toJsonTree(e.getValue()));
} else {
item.addProperty("value", "" + e.getValue());
}
array.add(item);
}
return array;
@@ -37,11 +37,11 @@ class TimeoutSettings {
return defaultNavigationTimeout;
}
void setDefaultTimeout(double timeout) {
void setDefaultTimeout(Double timeout) {
defaultTimeout = timeout;
}
void setDefaultNavigationTimeout(double timeout) {
void setDefaultNavigationTimeout(Double timeout) {
defaultNavigationTimeout = timeout;
}
@@ -89,7 +89,8 @@ class Utils {
}
static Set<Character> escapeGlobChars = new HashSet<>(Arrays.asList('/', '$', '^', '+', '.', '(', ')', '=', '!', '|'));
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions#escaping
static Set<Character> escapeGlobChars = new HashSet<>(Arrays.asList('$', '^', '+', '.', '*', '(', ')', '|', '\\', '?', '{', '}', '[', ']'));
static String globToRegex(String glob) {
StringBuilder tokens = new StringBuilder();
@@ -97,8 +98,12 @@ class Utils {
boolean inGroup = false;
for (int i = 0; i < glob.length(); ++i) {
char c = glob.charAt(i);
if (escapeGlobChars.contains(c)) {
tokens.append("\\").append(c);
if (c == '\\' && i + 1 < glob.length()) {
char nextChar = glob.charAt(++i);
if (escapeGlobChars.contains(nextChar)) {
tokens.append('\\');
}
tokens.append(nextChar);
continue;
}
if (c == '*') {
@@ -123,6 +128,12 @@ class Utils {
case '?':
tokens.append('.');
break;
case '[':
tokens.append('[');
break;
case ']':
tokens.append(']');
break;
case '{':
inGroup = true;
tokens.append('(');
@@ -139,7 +150,11 @@ class Utils {
tokens.append("\\").append(c);
break;
default:
if (escapeGlobChars.contains(c)) {
tokens.append('\\');
}
tokens.append(c);
break;
}
}
tokens.append('$');
@@ -16,10 +16,14 @@
package com.microsoft.playwright;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.microsoft.playwright.options.BrowserChannel;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIf;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Pattern;
import static org.junit.jupiter.api.Assertions.*;
@@ -98,4 +102,29 @@ public class TestBrowser extends TestBase {
void shouldReturnBrowserType() {
assertEquals(browserType, browser.browserType());
}
@Test
@EnabledIf(value = "com.microsoft.playwright.TestBase#isChromium", disabledReason = "Chrome Devtools Protocol supported by chromium only")
void shouldWorkWithNewBrowserCDPSession() {
CDPSession session = browser.newBrowserCDPSession();
JsonElement response = session.send("Browser.getVersion");
assertNotNull(response.getAsJsonObject().get("userAgent").toString());
AtomicReference<Boolean> gotEvent = new AtomicReference<>(false);
session.on("Target.targetCreated", jsonElement -> {
gotEvent.set(true);
});
JsonObject params = new JsonObject();
params.addProperty("discover", true);
session.send("Target.setDiscoverTargets", params);
Page page = browser.newPage();
assertTrue(gotEvent.get());
page.close();
session.detach();
}
}
@@ -0,0 +1,165 @@
/*
* 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 com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIf;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import static org.junit.jupiter.api.Assertions.*;
@EnabledIf(value = "com.microsoft.playwright.TestBase#isChromium", disabledReason = "Chrome Devtools Protocol supported by chromium only")
public class TestBrowserContextCDPSession extends TestBase {
@Test
void shouldWork() {
CDPSession cdpSession = page.context().newCDPSession(page);
cdpSession.send("Runtime.enable");
JsonObject params = new JsonObject();
params.addProperty("expression", "window.foo = 'bar'");
cdpSession.send("Runtime.evaluate", params);
Object foo = page.evaluate("window['foo']");
assertEquals("bar", foo);
}
@Test
void shouldSendEvents() {
CDPSession cdpSession = page.context().newCDPSession(page);
cdpSession.send("Network.enable");
List<JsonElement> events = new ArrayList<>();
cdpSession.on("Network.requestWillBeSent", events::add);
page.navigate(server.EMPTY_PAGE);
assertEquals(1, events.size());
}
@Test
void shouldDetachSession() {
CDPSession cdpSession = page.context().newCDPSession(page);
cdpSession.send("Runtime.enable");
JsonObject params = new JsonObject();
params.addProperty("expression", "1 + 2");
params.addProperty("returnByValue", true);
JsonElement evaluateResult = cdpSession.send("Runtime.evaluate", params);
assertEquals(3, evaluateResult.getAsJsonObject().getAsJsonObject("result").get("value").getAsInt());
cdpSession.detach();
PlaywrightException exception = assertThrows(PlaywrightException.class, () -> {
cdpSession.send("Runtime.evaluate", params);
});
assertTrue(exception.getMessage().contains("Target page, context or browser has been closed"));
}
@Test
void shouldThrowNiceErrors() {
CDPSession cdpSession = page.context().newCDPSession(page);
PlaywrightException exception = assertThrows(PlaywrightException.class, () -> {
cdpSession.send("ThisCommand.DoesNotExist");
});
assertTrue(exception.getMessage().contains("'ThisCommand.DoesNotExist' wasn't found"));
}
@Test
void shouldWorkWithMainFrame() {
CDPSession cdpSession = page.context().newCDPSession(page.mainFrame());
JsonObject params = new JsonObject();
params.addProperty("expression", "window.foo = 'bar'");
cdpSession.send("Runtime.evaluate", params);
Object foo = page.evaluate("window['foo']");
assertEquals("bar", foo);
}
@Test
void shouldThrowIfTargetIsPartOfMain() {
page.navigate(server.PREFIX + "/frames/one-frame.html");
assertEquals(server.PREFIX + "/frames/one-frame.html", page.frames().get(0).url());
assertEquals(server.PREFIX + "/frames/frame.html", page.frames().get(1).url());
PlaywrightException exception = assertThrows(PlaywrightException.class, () -> {
page.context().newCDPSession(page.frames().get(1));
});
assertTrue(exception.getMessage().contains("This frame does not have a separate CDP session, it is a part of the parent frame's session"));
}
@Test
void shouldNotBreakPageClose() {
BrowserContext context = browser.newContext();
Page page = context.newPage();
CDPSession session = page.context().newCDPSession(page);
session.detach();
page.close();
context.close();
}
@Test
void shouldDetachWhenPageCloses() {
BrowserContext context = browser.newContext();
Page page = context.newPage();
CDPSession session = page.context().newCDPSession(page);
page.close();
PlaywrightException exception = assertThrows(PlaywrightException.class, session::detach);
assertTrue(exception.getMessage().contains("Target page, context or browser has been closed"));
context.close();
}
@Test
void shouldAddMultipleEventListeners() {
CDPSession cdpSession = page.context().newCDPSession(page);
cdpSession.send("Network.enable");
List<JsonObject> events = new ArrayList<>();
cdpSession.on("Network.requestWillBeSent", events::add);
cdpSession.on("Network.requestWillBeSent", events::add);
page.navigate(server.EMPTY_PAGE);
assertEquals(2, events.size());
}
@Test
void shouldRemoveEventListeners() {
CDPSession cdpSession = page.context().newCDPSession(page);
cdpSession.send("Network.enable");
List<JsonObject> events = new ArrayList<>();
Consumer<JsonObject> listener1 = events::add;
cdpSession.on("Network.requestWillBeSent", listener1);
cdpSession.on("Network.requestWillBeSent", events::add);
page.navigate(server.EMPTY_PAGE);
assertEquals(2, events.size());
cdpSession.off("Network.requestWillBeSent", listener1);
events.clear();
page.navigate(server.EMPTY_PAGE);
assertEquals(1, events.size());
}
}
@@ -511,14 +511,18 @@ public class TestBrowserContextFetch extends TestBase {
FormData.create()
.set("firstName", "John")
.set("lastName", "Doe")
.set("age", 30)
.set("isMale", true)
.set("file", "f.js")));
assertEquals("POST", req.get().method);
assertEquals(asList("application/x-www-form-urlencoded"), req.get().headers.get("content-type"));
String body = new String(req.get().postBody);
assertTrue(body.contains("firstName=John"));
assertTrue(body.contains("lastName=Doe"));
assertTrue(body.contains("file=f.js"));
assertTrue(body.contains("firstName=John"), body);
assertTrue(body.contains("lastName=Doe"), body);
assertTrue(body.contains("age=30"), body);
assertTrue(body.contains("isMale=true"), body);
assertTrue(body.contains("file=f.js"), body);
}
@Test
@@ -526,6 +530,8 @@ public class TestBrowserContextFetch extends TestBase {
Map<String, Object> data = mapOf(
"firstName", "John",
"lastName", "Doe",
"age", 30,
"isMale", true,
"file", mapOf("name", "f.js")
);
Future<Server.Request> req = server.futureRequest("/empty.html");
@@ -19,6 +19,7 @@ package com.microsoft.playwright;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIf;
import java.math.BigInteger;
import java.time.*;
import java.util.Map;
import java.util.regex.Pattern;
@@ -115,6 +116,12 @@ public class TestPageEvaluate extends TestBase {
assertEquals(true, result);
}
@Test
void shouldTransferBigint() {
assertEquals(new BigInteger("42", 10), page.evaluate("() => 42n"));
assertEquals(new BigInteger("17", 10), page.evaluate("a => a", new BigInteger("17", 10)));
}
// @Test
void shouldTransferMapsAsEmptyObjects() {
// Not applicable.
@@ -124,4 +124,15 @@ public class TestPageInterception extends TestBase {
page.navigate(server.PREFIX + "/foo");
assertTrue(page.content().contains("hello"));
}
@Test
void shouldProperlyHandleCharacterSetsInGlobs() {
page.route("**/[a-z]*.html", route -> {
APIResponse response = route.fetch(new Route.FetchOptions().setUrl(server.PREFIX + "/one-style.html"));
route.fulfill(new Route.FulfillOptions().setResponse(response));
});
Response response = page.navigate(server.PREFIX + "/empty.html");
assertEquals(200, response.status());
assertTrue(response.text().contains("one-style.css"), response.text());
}
}
@@ -16,6 +16,7 @@
package com.microsoft.playwright;
import com.microsoft.playwright.options.AriaRole;
import org.junit.jupiter.api.Test;
import java.util.regex.Pattern;
@@ -207,4 +208,21 @@ public class TestPageLocatorQuery extends TestBase {
assertThat(page.locator("div").or(page.locator("article"))).hasText("hello");
assertThat(page.locator("span").or(page.locator("article"))).hasText("world");
}
@Test
void shouldSupportLocatorLocatorWithAndOr() {
page.setContent("\n" +
" <div>one <span>two</span> <button>three</button> </div>\n" +
" <span>four</span>\n" +
" <button>five</button>\n" +
" ");
assertThat(page.locator("div").locator(page.locator("button"))).hasText(new String[] {"three"});
assertThat(page.locator("div").locator(page.locator("button").or(page.locator("span")))).hasText(new String[]{"two", "three"});
assertThat(page.locator("button").or(page.locator("span"))).hasText(new String[]{"two", "three", "four", "five"});
assertThat(page.locator("div").locator(page.locator("button").and(page.getByRole(AriaRole.BUTTON)))).hasText(new String[]{"three"});
assertThat(page.locator("button").and(page.getByRole(AriaRole.BUTTON))).hasText(new String[]{"three", "five"});
}
}
@@ -97,6 +97,42 @@ public class TestPageRoute extends TestBase {
assertEquals(asList(1), intercepted);
}
@Test
void shouldSupportQuestionMarkInGlobPattern() {
server.setRoute("/index", exchange -> {
exchange.sendResponseHeaders(200, 0);
try (OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody())) {
writer.write("index-no-hello");
}
});
server.setRoute("/index123hello", exchange -> {
exchange.sendResponseHeaders(200, 0);
try (OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody())) {
writer.write("index123hello");
}
});
page.route("**/index?hello", route -> {
route.fulfill(new Route.FulfillOptions().setBody("intercepted any character"));
});
page.route("**/index\\?hello", route -> {
route.fulfill(new Route.FulfillOptions().setBody("intercepted question mark"));
});
page.navigate(server.PREFIX + "/index?hello");
assertTrue(page.content().contains("intercepted question mark"), page.content());
page.navigate(server.PREFIX + "/index");
assertTrue(page.content().contains("index-no-hello"), page.content());
page.navigate(server.PREFIX + "/index1hello");
assertTrue(page.content().contains("intercepted any character"), page.content());
page.navigate(server.PREFIX + "/index123hello");
assertTrue(page.content().contains("index123hello"), page.content());
}
@Test
void shouldUnroutePredicate() {
List<Integer> intercepted = new ArrayList<>();
@@ -73,6 +73,6 @@ public class TestPageSetExtraHttpHeaders extends TestBase {
PlaywrightException e = assertThrows(PlaywrightException.class, () -> {
browser.newContext(new Browser.NewContextOptions().setExtraHTTPHeaders(mapOf("foo", null)));
});
assertTrue(e.getMessage().contains("expected string, got undefined"));
assertTrue(e.getMessage().contains("Value cannot be null"));
}
}
@@ -230,4 +230,13 @@ public class TestSelectorsMisc extends TestBase {
assertEquals("hello", page.locator("div >> internal:or=\"article\"").textContent());
assertEquals("world", page.locator("span >> internal:or=\"article\"").textContent());
}
@Test
void shouldWorkWithInternalChain() {
page.setContent("<div>one <span>two</span> <button>three</button> </div>\n" +
" <span>four</span>\n" +
" <button>five</button>");
assertEquals(asList("three"), page.evalOnSelectorAll("div >> internal:chain=\"button\"", "els => els.map(e => e.textContent)"));
assertEquals(asList("two", "three"), page.evalOnSelectorAll("div >> internal:chain=\"span >> internal:or=\\\"button\\\"\"", "els => els.map(e => e.textContent)"));
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.35.0</version>
<version>1.37.0</version>
<packaging>pom</packaging>
<name>Playwright Parent Project</name>
<description>Java library to automate Chromium, Firefox and WebKit with a single API.
+1 -1
View File
@@ -1 +1 @@
1.35.0
1.37.0-beta-1691701681000
+1 -1
View File
@@ -6,7 +6,7 @@
<groupId>com.microsoft.playwright</groupId>
<artifactId>api-generator</artifactId>
<version>1.35.0</version>
<version>1.37.0</version>
<name>Playwright - API Generator</name>
<description>
This is an internal module used to generate Java API from the upstream Playwright
@@ -964,7 +964,7 @@ class Interface extends TypeDefinition {
if (asList("Page", "Frame", "ElementHandle", "Locator", "APIRequest", "Browser", "BrowserContext", "BrowserType", "Route", "Request", "Response", "JSHandle", "ConsoleMessage", "APIResponse", "Playwright").contains(jsonName)) {
output.add("import java.util.*;");
}
if (asList("Page", "Browser", "BrowserContext", "WebSocket", "Worker").contains(jsonName)) {
if (asList("Page", "Browser", "BrowserContext", "WebSocket", "Worker", "CDPSession").contains(jsonName)) {
output.add("import java.util.function.Consumer;");
}
if (asList("Page", "BrowserContext").contains(jsonName)) {
@@ -977,6 +977,9 @@ class Interface extends TypeDefinition {
if (asList("Page", "Frame", "FrameLocator", "Locator", "Browser", "BrowserType", "BrowserContext", "PageAssertions", "LocatorAssertions").contains(jsonName)) {
output.add("import java.util.regex.Pattern;");
}
if ("CDPSession".equals(jsonName)) {
output.add("import com.google.gson.JsonObject;");
}
if ("PlaywrightAssertions".equals(jsonName)) {
output.add("import com.microsoft.playwright.APIResponse;");
output.add("import com.microsoft.playwright.Locator;");
+1 -1
View File
@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.microsoft.playwright</groupId>
<artifactId>test-cli-fatjar</artifactId>
<version>1.35.0</version>
<version>1.37.0</version>
<name>Test Playwright Command Line FatJar</name>
<properties>
<compiler.version>1.8</compiler.version>
+1 -1
View File
@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.microsoft.playwright</groupId>
<artifactId>test-cli-version</artifactId>
<version>1.35.0</version>
<version>1.37.0</version>
<name>Test Playwright Command Line Version</name>
<properties>
<compiler.version>1.8</compiler.version>
+1 -1
View File
@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.microsoft.playwright</groupId>
<artifactId>test-local-installation</artifactId>
<version>1.35.0</version>
<version>1.37.0</version>
<name>Test local installation</name>
<description>Runs Playwright test suite (copied from playwright module) against locally cached Playwright</description>
<properties>
+1 -1
View File
@@ -9,7 +9,7 @@
</parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>test-spring-boot-starter</artifactId>
<version>1.35.0</version>
<version>1.37.0</version>
<name>Test Playwright With Spring Boot</name>
<properties>
<spring.version>2.4.3</spring.version>
+1 -1
View File
@@ -6,7 +6,7 @@
<groupId>com.microsoft.playwright</groupId>
<artifactId>update-version</artifactId>
<version>1.35.0</version>
<version>1.37.0</version>
<name>Playwright - Update Version in Documentation</name>
<description>
This is an internal module used to update versions in the documentation based on