feat: roll driver to 1.50 alpha (#1729)

This commit is contained in:
Yury Semikhatsky 2025-01-17 09:21:19 -08:00 committed by GitHub
parent eb8cf62d74
commit 015939b150
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 459 additions and 115 deletions

View File

@ -10,9 +10,9 @@ Playwright is a Java library to automate [Chromium](https://www.chromium.org/Hom
| | Linux | macOS | Windows |
| :--- | :---: | :---: | :---: |
| Chromium <!-- GEN:chromium-version -->131.0.6778.33<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Chromium <!-- GEN:chromium-version -->132.0.6834.57<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->18.2<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Firefox <!-- GEN:firefox-version -->132.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Firefox <!-- GEN:firefox-version -->134.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/intro#system-requirements) for details.

View File

@ -1225,10 +1225,10 @@ public interface Browser extends AutoCloseable {
* <p> In case this browser is connected to, clears all created contexts belonging to this browser and disconnects from the
* browser server.
*
* <p> <strong>NOTE:</strong> This is similar to force quitting the browser. Therefore, you should call {@link
* com.microsoft.playwright.BrowserContext#close BrowserContext.close()} on any {@code BrowserContext}'s you explicitly
* created earlier with {@link com.microsoft.playwright.Browser#newContext Browser.newContext()} **before** calling {@link
* com.microsoft.playwright.Browser#close Browser.close()}.
* <p> <strong>NOTE:</strong> This is similar to force-quitting the browser. To close pages gracefully and ensure you receive page close events, call
* {@link com.microsoft.playwright.BrowserContext#close BrowserContext.close()} on any {@code BrowserContext} instances you
* explicitly created earlier using {@link com.microsoft.playwright.Browser#newContext Browser.newContext()} **before**
* calling {@link com.microsoft.playwright.Browser#close Browser.close()}.
*
* <p> The {@code Browser} object itself is considered to be disposed and cannot be used anymore.
*
@ -1244,10 +1244,10 @@ public interface Browser extends AutoCloseable {
* <p> In case this browser is connected to, clears all created contexts belonging to this browser and disconnects from the
* browser server.
*
* <p> <strong>NOTE:</strong> This is similar to force quitting the browser. Therefore, you should call {@link
* com.microsoft.playwright.BrowserContext#close BrowserContext.close()} on any {@code BrowserContext}'s you explicitly
* created earlier with {@link com.microsoft.playwright.Browser#newContext Browser.newContext()} **before** calling {@link
* com.microsoft.playwright.Browser#close Browser.close()}.
* <p> <strong>NOTE:</strong> This is similar to force-quitting the browser. To close pages gracefully and ensure you receive page close events, call
* {@link com.microsoft.playwright.BrowserContext#close BrowserContext.close()} on any {@code BrowserContext} instances you
* explicitly created earlier using {@link com.microsoft.playwright.Browser#newContext Browser.newContext()} **before**
* calling {@link com.microsoft.playwright.Browser#close Browser.close()}.
*
* <p> The {@code Browser} object itself is considered to be disposed and cannot be used anymore.
*

View File

@ -834,10 +834,14 @@ public interface BrowserContext extends AutoCloseable {
* Grants specified permissions to the browser context. Only grants corresponding permissions to the given origin if
* specified.
*
* @param permissions A permission or an array of permissions to grant. Permissions can be one of the following values:
* @param permissions A list of permissions to grant.
*
* <p> <strong>NOTE:</strong> Supported permissions differ between browsers, and even between different versions of the same browser. Any permission
* may stop working after an update.
*
* <p> Here are some permissions that may be supported by some browsers:
* <ul>
* <li> {@code "accelerometer"}</li>
* <li> {@code "accessibility-events"}</li>
* <li> {@code "ambient-light-sensor"}</li>
* <li> {@code "background-sync"}</li>
* <li> {@code "camera"}</li>
@ -862,10 +866,14 @@ public interface BrowserContext extends AutoCloseable {
* Grants specified permissions to the browser context. Only grants corresponding permissions to the given origin if
* specified.
*
* @param permissions A permission or an array of permissions to grant. Permissions can be one of the following values:
* @param permissions A list of permissions to grant.
*
* <p> <strong>NOTE:</strong> Supported permissions differ between browsers, and even between different versions of the same browser. Any permission
* may stop working after an update.
*
* <p> Here are some permissions that may be supported by some browsers:
* <ul>
* <li> {@code "accelerometer"}</li>
* <li> {@code "accessibility-events"}</li>
* <li> {@code "ambient-light-sensor"}</li>
* <li> {@code "background-sync"}</li>
* <li> {@code "camera"}</li>
@ -1388,7 +1396,7 @@ public interface BrowserContext extends AutoCloseable {
* com.microsoft.playwright.BrowserContext#setDefaultNavigationTimeout BrowserContext.setDefaultNavigationTimeout()} take
* priority over {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()}.
*
* @param timeout Maximum time in milliseconds
* @param timeout Maximum time in milliseconds. Pass {@code 0} to disable timeout.
* @since v1.8
*/
void setDefaultTimeout(double timeout);

View File

@ -172,9 +172,14 @@ public interface BrowserType {
*/
public List<String> args;
/**
* Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge",
* "msedge-beta", "msedge-dev", "msedge-canary". Read more about using <a
* href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and Microsoft Edge</a>.
* Browser distribution channel.
*
* <p> Use "chromium" to <a href="https://playwright.dev/java/docs/browsers#chromium-new-headless-mode">opt in to new headless
* mode</a>.
*
* <p> Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or "msedge-canary" to
* use branded <a href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and
* Microsoft Edge</a>.
*/
public Object channel;
/**
@ -265,18 +270,28 @@ public interface BrowserType {
}
@Deprecated
/**
* Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge",
* "msedge-beta", "msedge-dev", "msedge-canary". Read more about using <a
* href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and Microsoft Edge</a>.
* Browser distribution channel.
*
* <p> Use "chromium" to <a href="https://playwright.dev/java/docs/browsers#chromium-new-headless-mode">opt in to new headless
* mode</a>.
*
* <p> Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or "msedge-canary" to
* use branded <a href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and
* Microsoft Edge</a>.
*/
public LaunchOptions setChannel(BrowserChannel channel) {
this.channel = channel;
return this;
}
/**
* Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge",
* "msedge-beta", "msedge-dev", "msedge-canary". Read more about using <a
* href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and Microsoft Edge</a>.
* Browser distribution channel.
*
* <p> Use "chromium" to <a href="https://playwright.dev/java/docs/browsers#chromium-new-headless-mode">opt in to new headless
* mode</a>.
*
* <p> Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or "msedge-canary" to
* use branded <a href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and
* Microsoft Edge</a>.
*/
public LaunchOptions setChannel(String channel) {
this.channel = channel;
@ -446,9 +461,14 @@ public interface BrowserType {
*/
public Boolean bypassCSP;
/**
* Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge",
* "msedge-beta", "msedge-dev", "msedge-canary". Read more about using <a
* href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and Microsoft Edge</a>.
* Browser distribution channel.
*
* <p> Use "chromium" to <a href="https://playwright.dev/java/docs/browsers#chromium-new-headless-mode">opt in to new headless
* mode</a>.
*
* <p> Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or "msedge-canary" to
* use branded <a href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and
* Microsoft Edge</a>.
*/
public Object channel;
/**
@ -734,18 +754,28 @@ public interface BrowserType {
}
@Deprecated
/**
* Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge",
* "msedge-beta", "msedge-dev", "msedge-canary". Read more about using <a
* href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and Microsoft Edge</a>.
* Browser distribution channel.
*
* <p> Use "chromium" to <a href="https://playwright.dev/java/docs/browsers#chromium-new-headless-mode">opt in to new headless
* mode</a>.
*
* <p> Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or "msedge-canary" to
* use branded <a href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and
* Microsoft Edge</a>.
*/
public LaunchPersistentContextOptions setChannel(BrowserChannel channel) {
this.channel = channel;
return this;
}
/**
* Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge",
* "msedge-beta", "msedge-dev", "msedge-canary". Read more about using <a
* href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and Microsoft Edge</a>.
* Browser distribution channel.
*
* <p> Use "chromium" to <a href="https://playwright.dev/java/docs/browsers#chromium-new-headless-mode">opt in to new headless
* mode</a>.
*
* <p> Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or "msedge-canary" to
* use branded <a href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and
* Microsoft Edge</a>.
*/
public LaunchPersistentContextOptions setChannel(String channel) {
this.channel = channel;

View File

@ -174,6 +174,19 @@ public interface Clock {
* page.clock().pauseAt("2020-02-02");
* }</pre>
*
* <p> For best results, install the clock before navigating the page and set it to a time slightly before the intended test
* time. This ensures that all timers run normally during page loading, preventing the page from getting stuck. Once the
* page has fully loaded, you can safely use {@link com.microsoft.playwright.Clock#pauseAt Clock.pauseAt()} to pause the
* clock.
* <pre>{@code
* // Initialize clock with some time before the test time and let the page load
* // naturally. `Date.now` will progress as the timers fire.
* SimpleDateFormat format = new SimpleDateFormat("yyy-MM-dd'T'HH:mm:ss");
* page.clock().install(new Clock.InstallOptions().setTime(format.parse("2024-12-10T08:00:00")));
* page.navigate("http://localhost:3333");
* page.clock().pauseAt(format.parse("2024-12-10T10:00:00"));
* }</pre>
*
* @param time Time to pause at.
* @since v1.45
*/
@ -194,6 +207,19 @@ public interface Clock {
* page.clock().pauseAt("2020-02-02");
* }</pre>
*
* <p> For best results, install the clock before navigating the page and set it to a time slightly before the intended test
* time. This ensures that all timers run normally during page loading, preventing the page from getting stuck. Once the
* page has fully loaded, you can safely use {@link com.microsoft.playwright.Clock#pauseAt Clock.pauseAt()} to pause the
* clock.
* <pre>{@code
* // Initialize clock with some time before the test time and let the page load
* // naturally. `Date.now` will progress as the timers fire.
* SimpleDateFormat format = new SimpleDateFormat("yyy-MM-dd'T'HH:mm:ss");
* page.clock().install(new Clock.InstallOptions().setTime(format.parse("2024-12-10T08:00:00")));
* page.navigate("http://localhost:3333");
* page.clock().pauseAt(format.parse("2024-12-10T10:00:00"));
* }</pre>
*
* @param time Time to pause at.
* @since v1.45
*/
@ -214,6 +240,19 @@ public interface Clock {
* page.clock().pauseAt("2020-02-02");
* }</pre>
*
* <p> For best results, install the clock before navigating the page and set it to a time slightly before the intended test
* time. This ensures that all timers run normally during page loading, preventing the page from getting stuck. Once the
* page has fully loaded, you can safely use {@link com.microsoft.playwright.Clock#pauseAt Clock.pauseAt()} to pause the
* clock.
* <pre>{@code
* // Initialize clock with some time before the test time and let the page load
* // naturally. `Date.now` will progress as the timers fire.
* SimpleDateFormat format = new SimpleDateFormat("yyy-MM-dd'T'HH:mm:ss");
* page.clock().install(new Clock.InstallOptions().setTime(format.parse("2024-12-10T08:00:00")));
* page.navigate("http://localhost:3333");
* page.clock().pauseAt(format.parse("2024-12-10T10:00:00"));
* }</pre>
*
* @param time Time to pause at.
* @since v1.45
*/

View File

@ -2630,7 +2630,6 @@ public interface Locator {
*
* <p> You can also specify {@code JSHandle} as the property value if you want live objects to be passed into the event:
* <pre>{@code
* // Note you can only create DataTransfer in Chromium and Firefox
* JSHandle dataTransfer = page.evaluateHandle("() => new DataTransfer()");
* Map<String, Object> arg = new HashMap<>();
* arg.put("dataTransfer", dataTransfer);
@ -2679,7 +2678,6 @@ public interface Locator {
*
* <p> You can also specify {@code JSHandle} as the property value if you want live objects to be passed into the event:
* <pre>{@code
* // Note you can only create DataTransfer in Chromium and Firefox
* JSHandle dataTransfer = page.evaluateHandle("() => new DataTransfer()");
* Map<String, Object> arg = new HashMap<>();
* arg.put("dataTransfer", dataTransfer);
@ -2727,7 +2725,6 @@ public interface Locator {
*
* <p> You can also specify {@code JSHandle} as the property value if you want live objects to be passed into the event:
* <pre>{@code
* // Note you can only create DataTransfer in Chromium and Firefox
* JSHandle dataTransfer = page.evaluateHandle("() => new DataTransfer()");
* Map<String, Object> arg = new HashMap<>();
* arg.put("dataTransfer", dataTransfer);
@ -3968,7 +3965,9 @@ public interface Locator {
*/
boolean isDisabled(IsDisabledOptions options);
/**
* Returns whether the element is <a href="https://playwright.dev/java/docs/actionability#editable">editable</a>.
* Returns whether the element is <a href="https://playwright.dev/java/docs/actionability#editable">editable</a>. If the
* target element is not an {@code <input>}, {@code <textarea>}, {@code <select>}, {@code [contenteditable]} and does not
* have a role allowing {@code [aria-readonly]}, this method throws an error.
*
* <p> <strong>NOTE:</strong> If you need to assert that an element is editable, prefer {@link
* com.microsoft.playwright.assertions.LocatorAssertions#isEditable LocatorAssertions.isEditable()} to avoid flakiness. See
@ -3985,7 +3984,9 @@ public interface Locator {
return isEditable(null);
}
/**
* Returns whether the element is <a href="https://playwright.dev/java/docs/actionability#editable">editable</a>.
* Returns whether the element is <a href="https://playwright.dev/java/docs/actionability#editable">editable</a>. If the
* target element is not an {@code <input>}, {@code <textarea>}, {@code <select>}, {@code [contenteditable]} and does not
* have a role allowing {@code [aria-readonly]}, this method throws an error.
*
* <p> <strong>NOTE:</strong> If you need to assert that an element is editable, prefer {@link
* com.microsoft.playwright.assertions.LocatorAssertions#isEditable LocatorAssertions.isEditable()} to avoid flakiness. See
@ -4166,17 +4167,21 @@ public interface Locator {
/**
* Creates a locator matching all elements that match one or both of the two locators.
*
* <p> Note that when both locators match something, the resulting locator will have multiple matches and violate <a
* href="https://playwright.dev/java/docs/locators#strictness">locator strictness</a> guidelines.
* <p> Note that when both locators match something, the resulting locator will have multiple matches, potentially causing a <a
* href="https://playwright.dev/java/docs/locators#strictness">locator strictness</a> violation.
*
* <p> <strong>Usage</strong>
*
* <p> Consider a scenario where you'd like to click on a "New email" button, but sometimes a security settings dialog shows up
* instead. In this case, you can wait for either a "New email" button, or a dialog and act accordingly.
*
* <p> <strong>NOTE:</strong> If both "New email" button and security dialog appear on screen, the "or" locator will match both of them, possibly
* throwing the <a href="https://playwright.dev/java/docs/locators#strictness">"strict mode violation" error</a>. In this
* case, you can use {@link com.microsoft.playwright.Locator#first Locator.first()} to only match one of them.
* <pre>{@code
* Locator newEmail = page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("New"));
* Locator dialog = page.getByText("Confirm security settings");
* assertThat(newEmail.or(dialog)).isVisible();
* assertThat(newEmail.or(dialog).first()).isVisible();
* if (dialog.isVisible())
* page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Dismiss")).click();
* newEmail.click();

View File

@ -5809,8 +5809,6 @@ public interface Page extends AutoCloseable {
/**
* Returns the PDF buffer.
*
* <p> <strong>NOTE:</strong> Generating a pdf is currently only supported in Chromium headless.
*
* <p> {@code page.pdf()} generates a pdf of the page with {@code print} css media. To generate a pdf with {@code screen}
* media, call {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} before calling {@code page.pdf()}:
*
@ -5869,8 +5867,6 @@ public interface Page extends AutoCloseable {
/**
* Returns the PDF buffer.
*
* <p> <strong>NOTE:</strong> Generating a pdf is currently only supported in Chromium headless.
*
* <p> {@code page.pdf()} generates a pdf of the page with {@code print} css media. To generate a pdf with {@code screen}
* media, call {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} before calling {@code page.pdf()}:
*
@ -7194,7 +7190,7 @@ public interface Page extends AutoCloseable {
* <p> <strong>NOTE:</strong> {@link com.microsoft.playwright.Page#setDefaultNavigationTimeout Page.setDefaultNavigationTimeout()} takes priority over
* {@link com.microsoft.playwright.Page#setDefaultTimeout Page.setDefaultTimeout()}.
*
* @param timeout Maximum time in milliseconds
* @param timeout Maximum time in milliseconds. Pass {@code 0} to disable timeout.
* @since v1.8
*/
void setDefaultTimeout(double timeout);

View File

@ -306,10 +306,10 @@ public interface Tracing {
* <pre>{@code
* // All actions between group and groupEnd
* // will be shown in the trace viewer as a group.
* page.context().tracing.group("Open Playwright.dev > API");
* page.context().tracing().group("Open Playwright.dev > API");
* page.navigate("https://playwright.dev/");
* page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("API")).click();
* page.context().tracing.groupEnd();
* page.context().tracing().groupEnd();
* }</pre>
*
* @param name Group name shown in the trace viewer.
@ -329,10 +329,10 @@ public interface Tracing {
* <pre>{@code
* // All actions between group and groupEnd
* // will be shown in the trace viewer as a group.
* page.context().tracing.group("Open Playwright.dev > API");
* page.context().tracing().group("Open Playwright.dev > API");
* page.navigate("https://playwright.dev/");
* page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("API")).click();
* page.context().tracing.groupEnd();
* page.context().tracing().groupEnd();
* }</pre>
*
* @param name Group name shown in the trace viewer.

View File

@ -58,16 +58,37 @@ public interface LocatorAssertions {
}
}
class IsCheckedOptions {
/**
* Provides state to assert for. Asserts for input to be checked by default. This option can't be used when {@code
* indeterminate} is set to true.
*/
public Boolean checked;
/**
* Asserts that the element is in the indeterminate (mixed) state. Only supported for checkboxes and radio buttons. This
* option can't be true when {@code checked} is provided.
*/
public Boolean indeterminate;
/**
* Time to retry the assertion for in milliseconds. Defaults to {@code 5000}.
*/
public Double timeout;
/**
* Provides state to assert for. Asserts for input to be checked by default. This option can't be used when {@code
* indeterminate} is set to true.
*/
public IsCheckedOptions setChecked(boolean checked) {
this.checked = checked;
return this;
}
/**
* Asserts that the element is in the indeterminate (mixed) state. Only supported for checkboxes and radio buttons. This
* option can't be true when {@code checked} is provided.
*/
public IsCheckedOptions setIndeterminate(boolean indeterminate) {
this.indeterminate = indeterminate;
return this;
}
/**
* Time to retry the assertion for in milliseconds. Defaults to {@code 5000}.
*/
@ -281,6 +302,33 @@ public interface LocatorAssertions {
return this;
}
}
class HasAccessibleErrorMessageOptions {
/**
* Whether to perform case-insensitive match. {@code ignoreCase} option takes precedence over the corresponding regular
* expression flag if specified.
*/
public Boolean ignoreCase;
/**
* Time to retry the assertion for in milliseconds. Defaults to {@code 5000}.
*/
public Double timeout;
/**
* Whether to perform case-insensitive match. {@code ignoreCase} option takes precedence over the corresponding regular
* expression flag if specified.
*/
public HasAccessibleErrorMessageOptions setIgnoreCase(boolean ignoreCase) {
this.ignoreCase = ignoreCase;
return this;
}
/**
* Time to retry the assertion for in milliseconds. Defaults to {@code 5000}.
*/
public HasAccessibleErrorMessageOptions setTimeout(double timeout) {
this.timeout = timeout;
return this;
}
}
class HasAccessibleNameOptions {
/**
* Whether to perform case-insensitive match. {@code ignoreCase} option takes precedence over the corresponding regular
@ -1219,6 +1267,66 @@ public interface LocatorAssertions {
* @since v1.44
*/
void hasAccessibleDescription(Pattern description, HasAccessibleDescriptionOptions options);
/**
* Ensures the {@code Locator} points to an element with a given <a
* href="https://w3c.github.io/aria/#aria-errormessage">aria errormessage</a>.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* Locator locator = page.getByTestId("username-input");
* assertThat(locator).hasAccessibleErrorMessage("Username is required.");
* }</pre>
*
* @param errorMessage Expected accessible error message.
* @since v1.50
*/
default void hasAccessibleErrorMessage(String errorMessage) {
hasAccessibleErrorMessage(errorMessage, null);
}
/**
* Ensures the {@code Locator} points to an element with a given <a
* href="https://w3c.github.io/aria/#aria-errormessage">aria errormessage</a>.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* Locator locator = page.getByTestId("username-input");
* assertThat(locator).hasAccessibleErrorMessage("Username is required.");
* }</pre>
*
* @param errorMessage Expected accessible error message.
* @since v1.50
*/
void hasAccessibleErrorMessage(String errorMessage, HasAccessibleErrorMessageOptions options);
/**
* Ensures the {@code Locator} points to an element with a given <a
* href="https://w3c.github.io/aria/#aria-errormessage">aria errormessage</a>.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* Locator locator = page.getByTestId("username-input");
* assertThat(locator).hasAccessibleErrorMessage("Username is required.");
* }</pre>
*
* @param errorMessage Expected accessible error message.
* @since v1.50
*/
default void hasAccessibleErrorMessage(Pattern errorMessage) {
hasAccessibleErrorMessage(errorMessage, null);
}
/**
* Ensures the {@code Locator} points to an element with a given <a
* href="https://w3c.github.io/aria/#aria-errormessage">aria errormessage</a>.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* Locator locator = page.getByTestId("username-input");
* assertThat(locator).hasAccessibleErrorMessage("Username is required.");
* }</pre>
*
* @param errorMessage Expected accessible error message.
* @since v1.50
*/
void hasAccessibleErrorMessage(Pattern errorMessage, HasAccessibleErrorMessageOptions options);
/**
* Ensures the {@code Locator} points to an element with a given <a
* href="https://w3c.github.io/accname/#dfn-accessible-name">accessible name</a>.
@ -1336,16 +1444,18 @@ public interface LocatorAssertions {
*/
void hasAttribute(String name, Pattern value, HasAttributeOptions options);
/**
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a
* relaxed regular expression.
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
*
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
* assertThat(page.locator("#component")).hasClass("middle selected row");
* }</pre>
*
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
* array:
* <pre>{@code
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
* }</pre>
@ -1357,16 +1467,18 @@ public interface LocatorAssertions {
hasClass(expected, null);
}
/**
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a
* relaxed regular expression.
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
*
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
* assertThat(page.locator("#component")).hasClass("middle selected row");
* }</pre>
*
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
* array:
* <pre>{@code
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
* }</pre>
@ -1376,16 +1488,18 @@ public interface LocatorAssertions {
*/
void hasClass(String expected, HasClassOptions options);
/**
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a
* relaxed regular expression.
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
*
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
* assertThat(page.locator("#component")).hasClass("middle selected row");
* }</pre>
*
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
* array:
* <pre>{@code
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
* }</pre>
@ -1397,16 +1511,18 @@ public interface LocatorAssertions {
hasClass(expected, null);
}
/**
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a
* relaxed regular expression.
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
*
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
* assertThat(page.locator("#component")).hasClass("middle selected row");
* }</pre>
*
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
* array:
* <pre>{@code
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
* }</pre>
@ -1416,16 +1532,18 @@ public interface LocatorAssertions {
*/
void hasClass(Pattern expected, HasClassOptions options);
/**
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a
* relaxed regular expression.
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
*
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
* assertThat(page.locator("#component")).hasClass("middle selected row");
* }</pre>
*
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
* array:
* <pre>{@code
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
* }</pre>
@ -1437,16 +1555,18 @@ public interface LocatorAssertions {
hasClass(expected, null);
}
/**
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a
* relaxed regular expression.
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
*
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
* assertThat(page.locator("#component")).hasClass("middle selected row");
* }</pre>
*
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
* array:
* <pre>{@code
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
* }</pre>
@ -1456,16 +1576,18 @@ public interface LocatorAssertions {
*/
void hasClass(String[] expected, HasClassOptions options);
/**
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a
* relaxed regular expression.
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
*
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
* assertThat(page.locator("#component")).hasClass("middle selected row");
* }</pre>
*
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
* array:
* <pre>{@code
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
* }</pre>
@ -1477,16 +1599,18 @@ public interface LocatorAssertions {
hasClass(expected, null);
}
/**
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a
* relaxed regular expression.
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
*
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
* assertThat(page.locator("#component")).hasClass("middle selected row");
* }</pre>
*
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
* array:
* <pre>{@code
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
* }</pre>

View File

@ -47,7 +47,6 @@ class AssertionsBase {
options = new FrameExpectOptions();
}
options.expectedText = expectedText;
options.isNot = isNot;
expectImpl(expression, options, expected, message);
}
@ -55,13 +54,14 @@ class AssertionsBase {
if (expectOptions.timeout == null) {
expectOptions.timeout = AssertionsTimeout.defaultTimeout;
}
if (expectOptions.isNot) {
expectOptions.isNot = isNot;
if (isNot) {
message = message.replace("expected to", "expected not to");
}
FrameExpectResult result = actualLocator.expect(expression, expectOptions);
if (result.matches == isNot) {
Object actual = result.received == null ? null : Serialization.deserialize(result.received);
String log = String.join("\n", result.log);
String log = (result.log == null) ? "" : String.join("\n", result.log);
if (!log.isEmpty()) {
log = "\nCall log:\n" + log;
}

View File

@ -22,7 +22,9 @@ import com.microsoft.playwright.options.AriaRole;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import static com.microsoft.playwright.impl.Serialization.serializeArgument;
@ -88,6 +90,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
ExpectedTextValue expected = new ExpectedTextValue();
expected.string = description;
expected.ignoreCase = shouldIgnoreCase(options);
expected.normalizeWhiteSpace = true;
expectImpl("to.have.accessible.description", expected, description, "Locator expected to have accessible description", convertType(options, FrameExpectOptions.class));
}
@ -95,14 +98,33 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
public void hasAccessibleDescription(Pattern pattern, HasAccessibleDescriptionOptions options) {
ExpectedTextValue expected = expectedRegex(pattern);
expected.ignoreCase = shouldIgnoreCase(options);
expected.normalizeWhiteSpace = true;
expectImpl("to.have.accessible.description", expected, pattern, "Locator expected to have accessible description", convertType(options, FrameExpectOptions.class));
}
@Override
public void hasAccessibleErrorMessage(String errorMessage, HasAccessibleErrorMessageOptions options) {
ExpectedTextValue expected = new ExpectedTextValue();
expected.string = errorMessage;
expected.ignoreCase = shouldIgnoreCase(options);
expected.normalizeWhiteSpace = true;
expectImpl("to.have.accessible.error.message", expected, errorMessage, "Locator expected to have accessible error message", convertType(options, FrameExpectOptions.class));
}
@Override
public void hasAccessibleErrorMessage(Pattern pattern, HasAccessibleErrorMessageOptions options) {
ExpectedTextValue expected = expectedRegex(pattern);
expected.ignoreCase = shouldIgnoreCase(options);
expected.normalizeWhiteSpace = true;
expectImpl("to.have.accessible.error.message", expected, pattern, "Locator expected to have accessible error message", convertType(options, FrameExpectOptions.class));
}
@Override
public void hasAccessibleName(String name, HasAccessibleNameOptions options) {
ExpectedTextValue expected = new ExpectedTextValue();
expected.string = name;
expected.ignoreCase = shouldIgnoreCase(options);
expected.normalizeWhiteSpace = true;
expectImpl("to.have.accessible.name", expected, name, "Locator expected to have accessible name", convertType(options, FrameExpectOptions.class));
}
@ -110,6 +132,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
public void hasAccessibleName(Pattern pattern, HasAccessibleNameOptions options) {
ExpectedTextValue expected = expectedRegex(pattern);
expected.ignoreCase = shouldIgnoreCase(options);
expected.normalizeWhiteSpace = true;
expectImpl("to.have.accessible.name", expected, pattern, "Locator expected to have accessible name", convertType(options, FrameExpectOptions.class));
}
@ -338,10 +361,30 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
@Override
public void isChecked(IsCheckedOptions options) {
boolean unchecked = options != null && options.checked != null && !options.checked;
String expression = unchecked ? "to.be.unchecked" : "to.be.checked";
String message = "Locator expected to be " + (unchecked ? "un" : "") + "checked";
expectTrue(expression, message, convertType(options, FrameExpectOptions.class));
if (options == null) {
options = new IsCheckedOptions();
}
Map<String, Boolean> expectedValue = new HashMap<>();
if (options.indeterminate != null) {
expectedValue.put("indeterminate", options.indeterminate);
}
if (options.checked != null) {
expectedValue.put("checked", options.checked);
}
String expected;
if (options.indeterminate != null && options.indeterminate) {
expected = "indeterminate";
} else {
boolean unchecked = options.checked != null && !options.checked;
expected = unchecked ? "unchecked" : "checked";
}
String message = "Locator expected to be";
FrameExpectOptions expectOptions = convertType(options, FrameExpectOptions.class);
expectOptions.expectedValue = serializeArgument(expectedValue);
expectImpl("to.be.checked", expectOptions, expected, message);
}
@Override

View File

@ -53,6 +53,7 @@ public class RequestImpl extends ChannelOwner implements Request {
RequestImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
super(parent, type, guid, initializer);
markAsInternalType();
if (initializer.has("redirectedFrom")) {
redirectedFrom = connection.getExistingObject(initializer.getAsJsonObject("redirectedFrom").get("guid").getAsString());

View File

@ -40,6 +40,7 @@ public class ResponseImpl extends ChannelOwner implements Response {
ResponseImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
super(parent, type, guid, initializer);
markAsInternalType();
headers = new RawHeaders(asList(gson().fromJson(initializer.getAsJsonArray("headers"), HttpHeader[].class)));
request = connection.getExistingObject(initializer.getAsJsonObject("request").get("guid").getAsString());
request.timing = gson().fromJson(initializer.get("timing"), Timing.class);

View File

@ -18,9 +18,9 @@ package com.microsoft.playwright;
import com.microsoft.playwright.assertions.LocatorAssertions;
import com.microsoft.playwright.assertions.PlaywrightAssertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.opentest4j.AssertionFailedError;
import org.opentest4j.ValueWrapper;
import java.util.regex.Pattern;
@ -657,9 +657,9 @@ public class TestLocatorAssertions extends TestBase {
AssertionFailedError e = assertThrows(AssertionFailedError.class, () -> {
assertThat(locator).isChecked(new LocatorAssertions.IsCheckedOptions().setTimeout(1000));
});
assertNull(e.getExpected());
assertNull(e.getActual());
assertTrue(e.getMessage().contains("Locator expected to be checked"), e.getMessage());
assertEquals("checked", e.getExpected().getStringRepresentation());
assertEquals("unchecked", e.getActual().getStringRepresentation());
assertTrue(e.getMessage().contains("Locator expected to be: checked"), e.getMessage());
}
@Test
@ -669,9 +669,10 @@ public class TestLocatorAssertions extends TestBase {
AssertionFailedError e = assertThrows(AssertionFailedError.class, () -> {
assertThat(locator).not().isChecked(new LocatorAssertions.IsCheckedOptions().setTimeout(1000));
});
assertNull(e.getExpected());
assertNull(e.getActual());
assertTrue(e.getMessage().contains("Locator expected not to be checked"), e.getMessage());
assertEquals("checked", e.getExpected().getStringRepresentation());
assertEquals("checked", e.getActual().getStringRepresentation());
assertTrue(e.getMessage().contains("Locator expected not to be: checked"), e.getMessage());
}
@Test
@ -687,7 +688,7 @@ public class TestLocatorAssertions extends TestBase {
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: unchecked"), error.getMessage());
}
@Test
@ -789,6 +790,14 @@ public class TestLocatorAssertions extends TestBase {
assertThat(locator).not().isEditable(new LocatorAssertions.IsEditableOptions().setEditable(false));
}
@Test
void isEditableThrowsOnNonInputElement() {
page.setContent("<button>");
Locator locator = page.locator("button");
PlaywrightException e = assertThrows(PlaywrightException.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());
}
@Test
void isEmptyPass() {
page.setContent("<input></input>");

View File

@ -73,9 +73,9 @@ public class TestLocatorAssertions2 extends TestBase {
void isAttachedEventually() {
page.setContent("<div></div>");
Locator locator = page.locator("span");
page.evalOnSelector("div", "div => setTimeout(() => {\n" +
" div.innerHTML = '<span>Hello</span>'\n" +
" }, 100)");
page.evalOnSelector("div", "div => setTimeout(() => {\n" +
" div.innerHTML = '<span>Hello</span>'\n" +
" }, 100)");
assertThat(locator).isAttached();
}
@ -83,9 +83,9 @@ public class TestLocatorAssertions2 extends TestBase {
void isAttachedEventuallyWithNot() {
page.setContent("<div><span>Hello</span></div>");
Locator locator = page.locator("span");
page.evalOnSelector("div", "div => setTimeout(() => {\n" +
" div.textContent = '';\n" +
" }, 0)");
page.evalOnSelector("div", "div => setTimeout(() => {\n" +
" div.textContent = '';\n" +
" }, 0)");
assertThat(locator).not().isAttached();
}
@ -129,6 +129,9 @@ public class TestLocatorAssertions2 extends TestBase {
assertThat(page.locator("div")).hasAccessibleName(Pattern.compile("ell\\w"));
assertThat(page.locator("div")).not().hasAccessibleName(Pattern.compile("hello"));
assertThat(page.locator("div")).hasAccessibleName(Pattern.compile("hello"), new LocatorAssertions.HasAccessibleNameOptions().setIgnoreCase(true));
page.setContent("<button>foo&nbsp;bar\nbaz</button>");
assertThat(page.locator("button")).hasAccessibleName("foo bar baz");
}
@Test
@ -141,6 +144,10 @@ public class TestLocatorAssertions2 extends TestBase {
assertThat(page.locator("div")).hasAccessibleDescription(Pattern.compile("ell\\w"));
assertThat(page.locator("div")).not().hasAccessibleDescription(Pattern.compile("hello"));
assertThat(page.locator("div")).hasAccessibleDescription(Pattern.compile("hello"), new LocatorAssertions.HasAccessibleDescriptionOptions().setIgnoreCase(true));
page.setContent("<div role=\"button\" aria-describedby=\"desc\"></div>\n" +
" <span id=\"desc\">foo&nbsp;bar\nbaz</span>");
assertThat(page.locator("div")).hasAccessibleDescription("foo bar baz");
}
@Test
@ -150,4 +157,67 @@ public class TestLocatorAssertions2 extends TestBase {
assertThat(page.locator("div")).hasRole(AriaRole.BUTTON);
assertThat(page.locator("div")).not().hasRole(AriaRole.CHECKBOX);
}
@Test
void toHaveAccessibleErrorMessage() {
page.setContent("<form>" +
"<input role=\"textbox\" aria-invalid=\"true\" aria-errormessage=\"error-message\" />" +
"<div id=\"error-message\">Hello</div>" +
"<div id=\"irrelevant-error\">This should not be considered.</div>" +
"</form>");
Locator locator = page.locator("input[role=\"textbox\"]");
assertThat(locator).hasAccessibleErrorMessage("Hello");
assertThat(locator).not().hasAccessibleErrorMessage("hello");
assertThat(locator).hasAccessibleErrorMessage("hello", new LocatorAssertions.HasAccessibleErrorMessageOptions().setIgnoreCase(true));
assertThat(locator).hasAccessibleErrorMessage(Pattern.compile("ell\\w"));
assertThat(locator).not().hasAccessibleErrorMessage(Pattern.compile("hello"));
assertThat(locator).hasAccessibleErrorMessage(Pattern.compile("hello"), new LocatorAssertions.HasAccessibleErrorMessageOptions().setIgnoreCase(true));
assertThat(locator).not().hasAccessibleErrorMessage("This should not be considered.");
}
@Test
void toHaveAccessibleErrorMessageShouldHandleMultipleAriaErrorMessageReferences() {
page.setContent("<form>\n" +
" <input role=\"textbox\" aria-invalid=\"true\" aria-errormessage=\"error1 error2\" />\n" +
" <div id=\"error1\">First error message.</div>\n" +
" <div id=\"error2\">Second error message.</div>\n" +
" <div id=\"irrelevant-error\">This should not be considered.</div>\n" +
"</form>");
Locator locator = page.locator("input[role=\"textbox\"]");
assertThat(locator).hasAccessibleErrorMessage("First error message. Second error message.");
assertThat(locator).hasAccessibleErrorMessage(Pattern.compile("first error message.", Pattern.CASE_INSENSITIVE));
assertThat(locator).hasAccessibleErrorMessage(Pattern.compile("second error message.", Pattern.CASE_INSENSITIVE));
assertThat(locator).not().hasAccessibleErrorMessage(Pattern.compile("This should not be considered.", Pattern.CASE_INSENSITIVE));
}
@Test
void toBeEditableWithIndeterminateTrue() {
page.setContent("<input type=checkbox></input>");
page.locator("input").evaluate("e => e.indeterminate = true");
Locator locator = page.locator("input");
assertThat(locator).isChecked(new LocatorAssertions.IsCheckedOptions().setIndeterminate(true));
}
@Test
void toBeEditableWithIndeterminateTrueAndChecked() {
page.setContent("<input type=checkbox></input>");
page.locator("input").evaluate("e => e.indeterminate = true");
Locator locator = page.locator("input");
PlaywrightException e = assertThrows(PlaywrightException.class, () ->
assertThat(locator).isChecked(new LocatorAssertions.IsCheckedOptions().setIndeterminate(true).setChecked(false)));
assertTrue(e.getMessage().contains("Can't assert indeterminate and checked at the same time"), e.getMessage());
}
@Test
void toBeEditableFailWithIndeterminateTrue() {
page.setContent("<input type=checkbox></input>");
Locator locator = page.locator("input");
AssertionFailedError e = assertThrows(AssertionFailedError.class, () ->
assertThat(locator).isChecked(new LocatorAssertions.IsCheckedOptions().setIndeterminate(true).setTimeout(1000)));
// TODO: should be "assertThat().isChecked() with timeout 1000ms"
assertTrue(e.getMessage().contains("Locator.expect with timeout 1000ms"), e.getMessage());
}
}

View File

@ -30,7 +30,6 @@ import static org.junit.jupiter.api.Assertions.*;
public class TestPdf extends TestBase {
@Test
@EnabledIf(value="com.microsoft.playwright.TestBase#isChromium", disabledReason="skip")
@DisabledIf(value="com.microsoft.playwright.TestBase#isHeadful", disabledReason="skip")
void shouldBeAbleToSaveFile(@TempDir Path tempDir) throws IOException {
Path path = tempDir.resolve("output.pdf");
page.pdf(new Page.PdfOptions().setPath(path));
@ -40,7 +39,6 @@ public class TestPdf extends TestBase {
@Test
@EnabledIf(value="com.microsoft.playwright.TestBase#isChromium", disabledReason="skip")
@DisabledIf(value="com.microsoft.playwright.TestBase#isHeadful", disabledReason="skip")
void shouldSupportFractionalScaleValue(@TempDir Path tempDir) throws IOException {
Path path = tempDir.resolve("output.pdf");
page.pdf(new Page.PdfOptions().setPath(path).setScale(0.5));
@ -51,7 +49,6 @@ public class TestPdf extends TestBase {
@Test
@EnabledIf(value="com.microsoft.playwright.TestBase#isChromium", disabledReason="Printing to pdf is currently only supported in headless chromium.")
@DisabledIf(value="com.microsoft.playwright.TestBase#isHeadful", disabledReason="Printing to pdf is currently only supported in headless chromium.")
void shouldBeAbleToGenerateOutline(@TempDir Path tempDir) throws IOException {
page.navigate(server.PREFIX + "/headings.html");
Path outputFileNoOutline = tempDir.resolve("outputNoOutline.pdf");

View File

@ -269,6 +269,27 @@ public class TestTracing extends TestBase {
calls);
}
@Test
public void shouldNotRecordNetworkActions(@TempDir Path tempDir) throws IOException {
context.tracing().start(new Tracing.StartOptions());
page.onRequest(request -> {
request.allHeaders();
});
page.onResponse(response -> {
response.text();
});
page.navigate(server.EMPTY_PAGE);
Path traceFile1 = tempDir.resolve("trace1.zip");
context.tracing().stop(new Tracing.StopOptions().setPath(traceFile1));
List<TraceEvent> events = parseTraceEvents(traceFile1);
List<String> calls = events.stream().filter(e -> e.apiName != null).map(e -> e.apiName)
.collect(Collectors.toList());
assertEquals(asList("Page.navigate"), calls);
}
private static class TraceEvent {
String type;
String name;

View File

@ -1 +1 @@
1.49.0
1.50.0-alpha-1737075373000