1
0
mirror of synced 2026-08-06 07:26:55 +00:00

feat: roll driver 1.31.0-beta-1676591072000 (#1207)

This commit is contained in:
Yury Semikhatsky
2023-02-16 19:09:22 -08:00
committed by GitHub
parent a478acf6b2
commit c4b27febd4
25 changed files with 348 additions and 118 deletions
+2 -2
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 -->110.0.5481.38<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Chromium <!-- GEN:chromium-version -->111.0.5563.19<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->16.4<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Firefox <!-- GEN:firefox-version -->108.0.2<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Firefox <!-- GEN:firefox-version -->109.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
@@ -15,7 +15,7 @@
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<version>1.22.0</version>
<version>1.30.0</version>
</dependency>
</dependencies>
<build>
@@ -227,8 +227,11 @@ public interface Browser extends AutoCloseable {
*/
public String userAgent;
/**
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default
* viewport.
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent
* viewport emulation.
*
* <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
* operating system. It makes the execution of the tests non-deterministic.
*/
public Optional<ViewportSize> viewportSize;
@@ -536,15 +539,21 @@ public interface Browser extends AutoCloseable {
return this;
}
/**
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default
* viewport.
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent
* viewport emulation.
*
* <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
* operating system. It makes the execution of the tests non-deterministic.
*/
public NewContextOptions setViewportSize(int width, int height) {
return setViewportSize(new ViewportSize(width, height));
}
/**
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default
* viewport.
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent
* viewport emulation.
*
* <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
* operating system. It makes the execution of the tests non-deterministic.
*/
public NewContextOptions setViewportSize(ViewportSize viewportSize) {
this.viewportSize = Optional.ofNullable(viewportSize);
@@ -722,8 +731,11 @@ public interface Browser extends AutoCloseable {
*/
public String userAgent;
/**
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default
* viewport.
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent
* viewport emulation.
*
* <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
* operating system. It makes the execution of the tests non-deterministic.
*/
public Optional<ViewportSize> viewportSize;
@@ -1031,15 +1043,21 @@ public interface Browser extends AutoCloseable {
return this;
}
/**
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default
* viewport.
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent
* viewport emulation.
*
* <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
* operating system. It makes the execution of the tests non-deterministic.
*/
public NewPageOptions setViewportSize(int width, int height) {
return setViewportSize(new ViewportSize(width, height));
}
/**
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default
* viewport.
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent
* viewport emulation.
*
* <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
* operating system. It makes the execution of the tests non-deterministic.
*/
public NewPageOptions setViewportSize(ViewportSize viewportSize) {
this.viewportSize = Optional.ofNullable(viewportSize);
@@ -601,8 +601,11 @@ public interface BrowserType {
*/
public String userAgent;
/**
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default
* viewport.
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent
* viewport emulation.
*
* <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
* operating system. It makes the execution of the tests non-deterministic.
*/
public Optional<ViewportSize> viewportSize;
@@ -1021,15 +1024,21 @@ public interface BrowserType {
return this;
}
/**
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default
* viewport.
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent
* viewport emulation.
*
* <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
* operating system. It makes the execution of the tests non-deterministic.
*/
public LaunchPersistentContextOptions setViewportSize(int width, int height) {
return setViewportSize(new ViewportSize(width, height));
}
/**
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default
* viewport.
* Emulates consistent viewport for each page. Defaults to an 1280x720 viewport. Use {@code null} to disable the consistent
* viewport emulation.
*
* <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
* operating system. It makes the execution of the tests non-deterministic.
*/
public LaunchPersistentContextOptions setViewportSize(ViewportSize viewportSize) {
this.viewportSize = Optional.ofNullable(viewportSize);
@@ -2283,7 +2283,7 @@ public interface Locator {
*/
void dblclick(DblclickOptions options);
/**
* Programmaticaly dispatch an event on the matching element.
* Programmatically dispatch an event on the matching element.
*
* <p> **Usage**
* <pre>{@code
@@ -2328,7 +2328,7 @@ public interface Locator {
dispatchEvent(type, eventInit, null);
}
/**
* Programmaticaly dispatch an event on the matching element.
* Programmatically dispatch an event on the matching element.
*
* <p> **Usage**
* <pre>{@code
@@ -2372,7 +2372,7 @@ public interface Locator {
dispatchEvent(type, null);
}
/**
* Programmaticaly dispatch an event on the matching element.
* Programmatically dispatch an event on the matching element.
*
* <p> **Usage**
* <pre>{@code
@@ -3717,7 +3717,7 @@ public interface Locator {
*/
Page page();
/**
* Focuses the mathing element and presses a combintation of the keys.
* Focuses the matching element and presses a combination of the keys.
*
* <p> **Usage**
* <pre>{@code
@@ -3756,7 +3756,7 @@ public interface Locator {
press(key, null);
}
/**
* Focuses the mathing element and presses a combintation of the keys.
* Focuses the matching element and presses a combination of the keys.
*
* <p> **Usage**
* <pre>{@code
@@ -5441,6 +5441,45 @@ public interface Page extends AutoCloseable {
* @since v1.8
*/
Mouse mouse();
/**
* Adds one-off {@code Dialog} handler. The handler will be removed immediately after next {@code Dialog} is created.
* <pre>{@code
* page.onceDialog(dialog -> {
* dialog.accept("foo");
* });
*
* // prints 'foo'
* System.out.println(page.evaluate("prompt('Enter string:')"));
*
* // prints 'null' as the dialog will be auto-dismissed because there are no handlers.
* System.out.println(page.evaluate("prompt('Enter string:')"));
* }</pre>
*
* <p> This code above is equivalent to:
* <pre>{@code
* Consumer<Dialog> handler = new Consumer<Dialog>() {
* @Override
* public void accept(Dialog dialog) {
* dialog.accept("foo");
* page.offDialog(this);
* }
* };
* page.onDialog(handler);
*
* // prints 'foo'
* System.out.println(page.evaluate("prompt('Enter string:')"));
*
* // prints 'null' as the dialog will be auto-dismissed because there are no handlers.
* System.out.println(page.evaluate("prompt('Enter string:')"));
* }</pre>
*
* @param handler Receives the {@code Dialog} object, it **must** either {@link Dialog#accept Dialog.accept()} or {@link Dialog#dismiss
* Dialog.dismiss()} the dialog - otherwise the page will <a
* href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking">freeze</a> waiting for the
* dialog, and actions like click will never finish.
* @since v1.10
*/
void onceDialog(Consumer<Dialog> handler);
/**
* Returns the opener for popup pages and {@code null} for others. If the opener has been closed already the returns {@code
* null}.
@@ -6727,7 +6766,7 @@ public interface Page extends AutoCloseable {
* <p> When all steps combined have not finished during the specified {@code timeout}, this method throws a {@code
* TimeoutError}. Passing zero timeout disables this.
*
* <p> <strong>NOTE:</strong> {@link Page#tap Page.tap()} requires that the {@code hasTouch} option of the browser context be set to true.
* <p> <strong>NOTE:</strong> {@link Page#tap Page.tap()} the method will throw if {@code hasTouch} option of the browser context is false.
*
* @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used.
* @since v1.8
@@ -6749,7 +6788,7 @@ public interface Page extends AutoCloseable {
* <p> When all steps combined have not finished during the specified {@code timeout}, this method throws a {@code
* TimeoutError}. Passing zero timeout disables this.
*
* <p> <strong>NOTE:</strong> {@link Page#tap Page.tap()} requires that the {@code hasTouch} option of the browser context be set to true.
* <p> <strong>NOTE:</strong> {@link Page#tap Page.tap()} the method will throw if {@code hasTouch} option of the browser context is false.
*
* @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used.
* @since v1.8
@@ -7838,44 +7877,5 @@ public interface Page extends AutoCloseable {
* @since v1.8
*/
List<Worker> workers();
/**
* Adds one-off {@code Dialog} handler. The handler will be removed immediately after next {@code Dialog} is created.
* <pre>{@code
* page.onceDialog(dialog -> {
* dialog.accept("foo");
* });
*
* // prints 'foo'
* System.out.println(page.evaluate("prompt('Enter string:')"));
*
* // prints 'null' as the dialog will be auto-dismissed because there are no handlers.
* System.out.println(page.evaluate("prompt('Enter string:')"));
* }</pre>
*
* <p> This code above is equivalent to:
* <pre>{@code
* Consumer<Dialog> handler = new Consumer<Dialog>() {
* @Override
* public void accept(Dialog dialog) {
* dialog.accept("foo");
* page.offDialog(this);
* }
* };
* page.onDialog(handler);
*
* // prints 'foo'
* System.out.println(page.evaluate("prompt('Enter string:')"));
*
* // prints 'null' as the dialog will be auto-dismissed because there are no handlers.
* System.out.println(page.evaluate("prompt('Enter string:')"));
* }</pre>
*
* @param handler Receives the {@code Dialog} object, it **must** either {@link Dialog#accept Dialog.accept()} or {@link Dialog#dismiss
* Dialog.dismiss()} the dialog - otherwise the page will <a
* href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking">freeze</a> waiting for the
* dialog, and actions like click will never finish.
* @since v1.10
*/
void onceDialog(Consumer<Dialog> handler);
}
@@ -97,7 +97,7 @@ public interface Playwright extends AutoCloseable {
* Launches new Playwright driver process and connects to it. {@link Playwright#close Playwright.close()} should be called
* when the instance is no longer needed.
* <pre>{@code
* Playwright playwright = Playwright.create()) {
* Playwright playwright = Playwright.create();
* Browser browser = playwright.webkit().launch();
* Page page = browser.newPage();
* page.navigate("https://www.w3.org/");
@@ -141,6 +141,11 @@ public interface Route {
* If set changes the request HTTP headers. Header values will be converted to a string.
*/
public Map<String, String> headers;
/**
* Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is
* exceeded. Defaults to {@code 20}. Pass {@code 0} to not follow redirects.
*/
public Integer maxRedirects;
/**
* If set changes the request method (e.g. GET or POST).
*/
@@ -161,6 +166,14 @@ public interface Route {
this.headers = headers;
return this;
}
/**
* Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is
* exceeded. Defaults to {@code 20}. Pass {@code 0} to not follow redirects.
*/
public FetchOptions setMaxRedirects(int maxRedirects) {
this.maxRedirects = maxRedirects;
return this;
}
/**
* If set changes the request method (e.g. GET or POST).
*/
@@ -321,6 +334,13 @@ public interface Route {
* });
* }</pre>
*
* <p> **Details**
*
* <p> Note that any overrides such as {@code url} or {@code headers} only apply to the request being routed. If this request
* results in a redirect, overrides will not be applied to the new redirected request. If you want to propagate a header
* through redirects, use the combination of {@link Route#fetch Route.fetch()} and {@link Route#fulfill Route.fulfill()}
* instead.
*
* @since v1.8
*/
default void resume() {
@@ -340,6 +360,13 @@ public interface Route {
* });
* }</pre>
*
* <p> **Details**
*
* <p> Note that any overrides such as {@code url} or {@code headers} only apply to the request being routed. If this request
* results in a redirect, overrides will not be applied to the new redirected request. If you want to propagate a header
* through redirects, use the combination of {@link Route#fetch Route.fetch()} and {@link Route#fulfill Route.fulfill()}
* instead.
*
* @since v1.8
*/
void resume(ResumeOptions options);
@@ -488,6 +515,12 @@ public interface Route {
* });
* }</pre>
*
* <p> **Details**
*
* <p> Note that {@code headers} option will apply to the fetched request as well as any redirects initiated by it. If you want
* to only apply {@code headers} to the original request, but not to redirects, look into {@link Route#resume
* Route.resume()} instead.
*
* @since v1.29
*/
default APIResponse fetch() {
@@ -510,6 +543,12 @@ public interface Route {
* });
* }</pre>
*
* <p> **Details**
*
* <p> Note that {@code headers} option will apply to the fetched request as well as any redirects initiated by it. If you want
* to only apply {@code headers} to the original request, but not to redirects, look into {@link Route#resume
* Route.resume()} instead.
*
* @since v1.29
*/
APIResponse fetch(FetchOptions options);
@@ -42,7 +42,9 @@ public interface Selectors {
}
}
/**
* **Usage**
* Selectors must be registered before creating the page.
*
* <p> **Usage**
*
* <p> An example of registering selector engine that queries elements based on a tag name:
* <pre>{@code
@@ -64,8 +66,8 @@ public interface Selectors {
* page.setContent("<div><button>Click me</button></div>");
* // Use the selector prefixed with its name.
* Locator button = page.locator("tag=button");
* // Combine it with other selector engines.
* page.locator("tag=div >> text=\"Click me\"").click();
* // Combine it with built-in locators.
* page.locator("tag=div").getByText("Click me").click();
* // Can use it in any methods supporting selectors.
* int buttonCount = (int) page.locator("tag=button").count();
* browser.close();
@@ -80,7 +82,9 @@ public interface Selectors {
register(name, script, null);
}
/**
* **Usage**
* Selectors must be registered before creating the page.
*
* <p> **Usage**
*
* <p> An example of registering selector engine that queries elements based on a tag name:
* <pre>{@code
@@ -102,8 +106,8 @@ public interface Selectors {
* page.setContent("<div><button>Click me</button></div>");
* // Use the selector prefixed with its name.
* Locator button = page.locator("tag=button");
* // Combine it with other selector engines.
* page.locator("tag=div >> text=\"Click me\"").click();
* // Combine it with built-in locators.
* page.locator("tag=div").getByText("Click me").click();
* // Can use it in any methods supporting selectors.
* int buttonCount = (int) page.locator("tag=button").count();
* browser.close();
@@ -116,7 +120,9 @@ public interface Selectors {
*/
void register(String name, String script, RegisterOptions options);
/**
* **Usage**
* Selectors must be registered before creating the page.
*
* <p> **Usage**
*
* <p> An example of registering selector engine that queries elements based on a tag name:
* <pre>{@code
@@ -138,8 +144,8 @@ public interface Selectors {
* page.setContent("<div><button>Click me</button></div>");
* // Use the selector prefixed with its name.
* Locator button = page.locator("tag=button");
* // Combine it with other selector engines.
* page.locator("tag=div >> text=\"Click me\"").click();
* // Combine it with built-in locators.
* page.locator("tag=div").getByText("Click me").click();
* // Can use it in any methods supporting selectors.
* int buttonCount = (int) page.locator("tag=button").count();
* browser.close();
@@ -154,7 +160,9 @@ public interface Selectors {
register(name, script, null);
}
/**
* **Usage**
* Selectors must be registered before creating the page.
*
* <p> **Usage**
*
* <p> An example of registering selector engine that queries elements based on a tag name:
* <pre>{@code
@@ -176,8 +184,8 @@ public interface Selectors {
* page.setContent("<div><button>Click me</button></div>");
* // Use the selector prefixed with its name.
* Locator button = page.locator("tag=button");
* // Combine it with other selector engines.
* page.locator("tag=div >> text=\"Click me\"").click();
* // Combine it with built-in locators.
* page.locator("tag=div").getByText("Click me").click();
* // Can use it in any methods supporting selectors.
* int buttonCount = (int) page.locator("tag=button").count();
* browser.close();
@@ -25,6 +25,8 @@ public interface Touchscreen {
/**
* Dispatches a {@code touchstart} and {@code touchend} event with a single touch at the position ({@code x},{@code y}).
*
* <p> <strong>NOTE:</strong> {@link Page#tap Page.tap()} the method will throw if {@code hasTouch} option of the browser context is false.
*
* @since v1.8
*/
void tap(double x, double y);
@@ -19,8 +19,7 @@ package com.microsoft.playwright.assertions;
/**
* The {@code APIResponseAssertions} class provides assertion methods that can be used to make assertions about the {@code
* APIResponse} in the tests. A new instance of {@code APIResponseAssertions} is created by calling {@link
* PlaywrightAssertions#assertThat PlaywrightAssertions.assertThat()}:
* APIResponse} in the tests.
* <pre>{@code
* ...
* import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
@@ -20,8 +20,7 @@ import java.util.regex.Pattern;
/**
* The {@code LocatorAssertions} class provides assertion methods that can be used to make assertions about the {@code
* Locator} state in the tests. A new instance of {@code LocatorAssertions} is created by calling {@link
* PlaywrightAssertions#assertThat PlaywrightAssertions.assertThat()}:
* Locator} state in the tests.
* <pre>{@code
* ...
* import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
@@ -151,6 +150,33 @@ public interface LocatorAssertions {
return this;
}
}
class IsInViewportOptions {
/**
* The minimal ratio of the element to intersect viewport. If equals to {@code 0}, then element should intersect viewport
* at any positive ratio. Defaults to {@code 0}.
*/
public Double ratio;
/**
* Time to retry the assertion for.
*/
public Double timeout;
/**
* The minimal ratio of the element to intersect viewport. If equals to {@code 0}, then element should intersect viewport
* at any positive ratio. Defaults to {@code 0}.
*/
public IsInViewportOptions setRatio(double ratio) {
this.ratio = ratio;
return this;
}
/**
* Time to retry the assertion for.
*/
public IsInViewportOptions setTimeout(double timeout) {
this.timeout = timeout;
return this;
}
}
class IsVisibleOptions {
/**
* Time to retry the assertion for.
@@ -548,6 +574,44 @@ public interface LocatorAssertions {
* @since v1.20
*/
void isHidden(IsHiddenOptions options);
/**
* Ensures the {@code Locator} points to an element that intersects viewport, according to the <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API">intersection observer API</a>.
*
* <p> **Usage**
* <pre>{@code
* Locator locator = page.locator("button.submit");
* // Make sure at least some part of element intersects viewport.
* assertThat(locator).isInViewport();
* // Make sure element is fully outside of viewport.
* assertThat(locator).not().isInViewport();
* // Make sure that at least half of the element intersects viewport.
* assertThat(locator).isInViewport(new LocatorAssertions.IsInViewportOptions().setRatio(0.5));
* }</pre>
*
* @since v1.31
*/
default void isInViewport() {
isInViewport(null);
}
/**
* Ensures the {@code Locator} points to an element that intersects viewport, according to the <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API">intersection observer API</a>.
*
* <p> **Usage**
* <pre>{@code
* Locator locator = page.locator("button.submit");
* // Make sure at least some part of element intersects viewport.
* assertThat(locator).isInViewport();
* // Make sure element is fully outside of viewport.
* assertThat(locator).not().isInViewport();
* // Make sure that at least half of the element intersects viewport.
* assertThat(locator).isInViewport(new LocatorAssertions.IsInViewportOptions().setRatio(0.5));
* }</pre>
*
* @since v1.31
*/
void isInViewport(IsInViewportOptions options);
/**
* Ensures that {@code Locator} points to an <a href="https://playwright.dev/java/docs/actionability#attached">attached</a>
* and <a href="https://playwright.dev/java/docs/actionability#visible">visible</a> DOM node.
@@ -20,8 +20,7 @@ import java.util.regex.Pattern;
/**
* The {@code PageAssertions} class provides assertion methods that can be used to make assertions about the {@code Page}
* state in the tests. A new instance of {@code PageAssertions} is created by calling {@link
* PlaywrightAssertions#assertThat PlaywrightAssertions.assertThat()}:
* state in the tests.
* <pre>{@code
* ...
* import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
@@ -399,11 +399,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
private void route(UrlMatcher matcher, Consumer<Route> handler, RouteOptions options) {
withLogging("BrowserContext.route", () -> {
routes.add(matcher, handler, options == null ? null : options.times);
if (routes.size() == 1) {
JsonObject params = new JsonObject();
params.addProperty("enabled", true);
sendMessage("setNetworkInterceptionEnabled", params);
}
updateInterceptionPatterns();
});
}
@@ -525,22 +521,18 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
private void unroute(UrlMatcher matcher, Consumer<Route> handler) {
withLogging("BrowserContext.unroute", () -> {
routes.remove(matcher, handler);
maybeDisableNetworkInterception();
updateInterceptionPatterns();
});
}
private void maybeDisableNetworkInterception() {
if (routes.size() == 0) {
JsonObject params = new JsonObject();
params.addProperty("enabled", false);
sendMessage("setNetworkInterceptionEnabled", params);
}
private void updateInterceptionPatterns() {
sendMessage("setNetworkInterceptionPatterns", routes.interceptionPatterns());
}
void handleRoute(RouteImpl route) {
Router.HandleResult handled = routes.handle(route);
if (handled != Router.HandleResult.NoMatchingHandler) {
maybeDisableNetworkInterception();
updateInterceptionPatterns();
}
if (handled == Router.HandleResult.NoMatchingHandler || handled == Router.HandleResult.Fallback) {
route.resume();
@@ -148,7 +148,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
options = new HasCountOptions();
}
FrameExpectOptions commonOptions = convertType(options, FrameExpectOptions.class);
commonOptions.expectedNumber = count;
commonOptions.expectedNumber = (double) count;
List<ExpectedTextValue> expectedText = null;
expectImpl("to.have.count", expectedText, count, "Locator expected to have count", commonOptions);
}
@@ -326,6 +326,15 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
expectTrue("to.be.hidden", "Locator expected to be hidden", convertType(options, FrameExpectOptions.class));
}
@Override
public void isInViewport(IsInViewportOptions options) {
FrameExpectOptions expectOptions = convertType(options, FrameExpectOptions.class);
if (options != null && options.ratio != null) {
expectOptions.expectedNumber = options.ratio;
}
expectTrue("to.be.in.viewport", "Locator expected to be in viewport", expectOptions);
}
@Override
public void isVisible(IsVisibleOptions options) {
FrameExpectOptions frameOptions = convertType(options, FrameExpectOptions.class);
@@ -189,7 +189,7 @@ public class PageImpl extends ChannelOwner implements Page {
RouteImpl route = connection.getExistingObject(params.getAsJsonObject("route").get("guid").getAsString());
Router.HandleResult handled = routes.handle(route);
if (handled != Router.HandleResult.NoMatchingHandler) {
maybeDisableNetworkInterception();
updateInterceptionPatterns();
}
if (handled == Router.HandleResult.NoMatchingHandler || handled == Router.HandleResult.Fallback) {
browserContext.handleRoute(route);
@@ -1037,11 +1037,7 @@ public class PageImpl extends ChannelOwner implements Page {
private void route(UrlMatcher matcher, Consumer<Route> handler, RouteOptions options) {
withLogging("Page.route", () -> {
routes.add(matcher, handler, options == null ? null : options.times);
if (routes.size() == 1) {
JsonObject params = new JsonObject();
params.addProperty("enabled", true);
sendMessage("setNetworkInterceptionEnabled", params);
}
updateInterceptionPatterns();
});
}
@@ -1256,16 +1252,12 @@ public class PageImpl extends ChannelOwner implements Page {
private void unroute(UrlMatcher matcher, Consumer<Route> handler) {
withLogging("Page.unroute", () -> {
routes.remove(matcher, handler);
maybeDisableNetworkInterception();
updateInterceptionPatterns();
});
}
private void maybeDisableNetworkInterception() {
if (routes.size() == 0) {
JsonObject params = new JsonObject();
params.addProperty("enabled", false);
sendMessage("setNetworkInterceptionEnabled", params);
}
private void updateInterceptionPatterns() {
sendMessage("setNetworkInterceptionPatterns", routes.interceptionPatterns());
}
@Override
@@ -94,7 +94,7 @@ class ExpectedTextValue {
class FrameExpectOptions {
Object expressionArg;
List<ExpectedTextValue> expectedText;
Integer expectedNumber;
Double expectedNumber;
SerializedArgument expectedValue;
Boolean useInnerText;
boolean isNot;
@@ -16,14 +16,19 @@
package com.microsoft.playwright.impl;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.microsoft.playwright.Route;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static com.microsoft.playwright.impl.Utils.toJsRegexFlags;
class Router {
private List<RouteInfo> routes = new ArrayList<>();
@@ -92,4 +97,29 @@ class Router {
}
return result;
}
JsonObject interceptionPatterns() {
JsonArray jsonPatterns = new JsonArray();
for (RouteInfo route : routes) {
JsonObject jsonPattern = new JsonObject();
Object urlFilter = route.matcher.rawSource;
if (urlFilter instanceof String) {
jsonPattern.addProperty("glob", (String) urlFilter);
} else if (urlFilter instanceof Pattern) {
Pattern pattern = (Pattern) urlFilter;
jsonPattern.addProperty("regexSource", pattern.pattern());
jsonPattern.addProperty("regexFlags", toJsRegexFlags(pattern));
} else {
// Match all requests.
jsonPattern.addProperty("glob", "**/*");
jsonPatterns = new JsonArray();
jsonPatterns.add(jsonPattern);
break;
}
jsonPatterns.add(jsonPattern);
}
JsonObject result = new JsonObject();
result.add("patterns", jsonPatterns);
return result;
}
}
@@ -27,7 +27,7 @@ import java.util.regex.Pattern;
import static com.microsoft.playwright.impl.Utils.globToRegex;
class UrlMatcher {
private final Object rawSource;
final Object rawSource;
private final Predicate<String> predicate;
private static Predicate<String> toPredicate(Pattern pattern) {
@@ -110,7 +110,8 @@ public interface RequestOptions {
*/
RequestOptions setForm(FormData form);
/**
* Sets an HTTP header to the request.
* Sets an HTTP header to the request. This header will apply to the fetched request as well as any redirects initiated by
* it.
*
* @param name Header name.
* @param value Header value.
@@ -0,0 +1,54 @@
package com.microsoft.playwright;
import com.microsoft.playwright.assertions.LocatorAssertions;
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.*;
// Copied from expect-misc.spec.ts > toBeInViewport
public class TestAssertThatIsInViewport extends TestBase {
@Test
void shouldWork() {
page.setContent("<div id=big style=\"height: 10000px;\"></div>\n" +
" <div id=small>foo</div>");
assertThat(page.locator("#big")).isInViewport();
assertThat(page.locator("#small")).not().isInViewport();
page.locator("#small").scrollIntoViewIfNeeded();
assertThat(page.locator("#small")).isInViewport();
assertThat(page.locator("#small")).isInViewport(new LocatorAssertions.IsInViewportOptions().setRatio(1));
}
@Test
void shouldRespectRatioOption() {
page.setContent("<style>body, div, html { padding: 0; margin: 0; }</style>\n" +
" <div id=big style=\"height: 400vh;\"></div>");
assertThat(page.locator("div")).isInViewport();
assertThat(page.locator("div")).isInViewport(new LocatorAssertions.IsInViewportOptions().setRatio(0.1));
assertThat(page.locator("div")).isInViewport(new LocatorAssertions.IsInViewportOptions().setRatio(0.2));
assertThat(page.locator("div")).isInViewport(new LocatorAssertions.IsInViewportOptions().setRatio(0.24));
// In this test, element's ratio is 0.25.
assertThat(page.locator("div")).isInViewport(new LocatorAssertions.IsInViewportOptions().setRatio(0.25));
assertThat(page.locator("div")).not().isInViewport(new LocatorAssertions.IsInViewportOptions().setRatio(0.26));
assertThat(page.locator("div")).not().isInViewport(new LocatorAssertions.IsInViewportOptions().setRatio(0.3));
assertThat(page.locator("div")).not().isInViewport(new LocatorAssertions.IsInViewportOptions().setRatio(0.7));
assertThat(page.locator("div")).not().isInViewport(new LocatorAssertions.IsInViewportOptions().setRatio(0.8));
}
@Test
void shouldHaveGoodStack() {
AssertionFailedError error = assertThrows(AssertionFailedError.class, () -> assertThat(page.locator("body")).not().isInViewport(new LocatorAssertions.IsInViewportOptions().setTimeout(100)));
assertNotNull(error);
assertTrue(error.getMessage().contains("Locator expected not to be in viewport"), error.getMessage());
}
@Test
void shouldReportIntersectionEvenIfFullyCoveredByOtherElement() {
page.setContent("<h1>hello</h1>\n" +
" <div style=\"position: relative; height: 10000px; top: -5000px;></div>");
assertThat(page.locator("h1")).isInViewport();
}
}
@@ -17,6 +17,7 @@
package com.microsoft.playwright;
import com.microsoft.playwright.options.AriaRole;
import com.microsoft.playwright.options.WaitUntilState;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIf;
import org.junit.jupiter.api.condition.EnabledIf;
@@ -92,4 +92,17 @@ public class TestPageInterception extends TestBase {
page.navigate(server.PREFIX + "/empty.html");
assertEquals("{ \"foo\": \"bar\" }", new String(request.get().postBody));
}
@Test
void shouldNotFollowRedirectsWhenMaxRedirectsIsSetTo0InRouteFetch() {
server.setRedirect("/foo", "/empty.html");
page.route("**/*", route -> {
APIResponse response = route.fetch(new Route.FetchOptions().setMaxRedirects(0));
assertEquals("/empty.html", response.headers().get("location"));
assertEquals(302, response.status());
route.fulfill(new Route.FulfillOptions().setBody("hello"));
});
page.navigate(server.PREFIX + "/foo");
assertTrue(page.content().contains("hello"));
}
}
@@ -73,7 +73,7 @@ public class TestPageRequestContinue extends TestBase {
done[0] = true;
});
PlaywrightException e = assertThrows(PlaywrightException.class, () -> page.navigate(server.EMPTY_PAGE));
assertTrue(e.getMessage().contains("Navigation failed because page was closed") ||
assertTrue(e.getMessage().contains("Target page, context or browser has been closed") ||
e.getMessage().contains("frame was detached"), e.getMessage());
assertTrue(done[0]);
}
+1 -1
View File
@@ -1 +1 @@
1.30.0-beta-1674276599000
1.31.0-beta-1676596096000