1
0
mirror of synced 2026-05-23 03:03:15 +00:00

Compare commits

...

13 Commits

Author SHA1 Message Date
Yury Semikhatsky 01c72ac5d3 chore: set version to 1.50.0 (#1740) 2025-02-03 09:43:14 -08:00
Yury Semikhatsky dbc0478e40 chore: roll 1.50.1-beta (#1739) 2025-02-03 09:22:37 -08:00
dependabot[bot] 9eb1db9034 chore(deps): bump com.google.code.gson:gson from 2.11.0 to 2.12.1 in the all group (#1738) 2025-02-03 08:27:25 -08:00
Max Schmitt 4820088457 fix(urlMatcher): normalize URLs to align with Node.js parser behavior (#1734) 2025-01-27 10:26:39 -08:00
Max Schmitt fcd0444c57 fix(webSocketRoute): resolve URL against baseURL (#1722) 2025-01-27 15:43:47 +01:00
Yury Semikhatsky 067e69f339 chore: roll 1.50.0 (#1733) 2025-01-23 12:14:30 -08:00
Yury Semikhatsky 015939b150 feat: roll driver to 1.50 alpha (#1729) 2025-01-17 09:21:19 -08:00
Adam Gastineau eb8cf62d74 fix(tracing): Properly log Clock calls (#1727) 2025-01-16 07:04:53 -08:00
dependabot[bot] 308b9913e7 chore(deps): bump the all group with 5 updates (#1723) 2025-01-02 10:36:48 -08:00
Max Schmitt 6b621ce6f7 chore: refactor UrlMatcher (#1720) 2024-12-28 10:47:01 +01:00
Yury Semikhatsky 42d0203b49 fix: waitForCondition should not call predicate after it returned true (#1721) 2024-12-19 12:25:42 -08:00
Max Schmitt c591a1470a test: do not create stray files when running tests (#1718) 2024-12-18 11:49:18 +01:00
dependabot[bot] eb08046e94 chore(deps): bump the all group with 2 updates (#1710) 2024-12-02 10:49:48 -08:00
37 changed files with 761 additions and 204 deletions
+2 -2
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 -->133.0.6943.16<!-- 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.
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.50.0-SNAPSHOT</version>
<version>1.50.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.50.0-SNAPSHOT</version>
<version>1.50.0</version>
</parent>
<artifactId>driver</artifactId>
+1 -1
View File
@@ -6,7 +6,7 @@
<groupId>org.example</groupId>
<artifactId>examples</artifactId>
<version>1.50.0-SNAPSHOT</version>
<version>1.50.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.50.0-SNAPSHOT</version>
<version>1.50.0</version>
</parent>
<artifactId>playwright</artifactId>
@@ -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.
*
@@ -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);
@@ -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;
@@ -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
*/
@@ -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();
@@ -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);
@@ -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.
@@ -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>
@@ -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;
}
@@ -12,18 +12,24 @@ class ClockImpl implements Clock {
this.browserContext = browserContext;
}
private void sendMessageWithLogging(String method, JsonObject params) {
String capitalizedMethod = method.substring(0, 1).toUpperCase() + method.substring(1);
browserContext.withLogging("Clock." + method,
() -> browserContext.sendMessage("clock" + capitalizedMethod, params));
}
@Override
public void fastForward(long ticks) {
JsonObject params = new JsonObject();
params.addProperty("ticksNumber", ticks);
browserContext.sendMessage("clockFastForward", params);
sendMessageWithLogging("fastForward", params);
}
@Override
public void fastForward(String ticks) {
JsonObject params = new JsonObject();
params.addProperty("ticksString", ticks);
browserContext.sendMessage("clockFastForward", params);
sendMessageWithLogging("fastForward", params);
}
@Override
@@ -32,89 +38,89 @@ class ClockImpl implements Clock {
if (options != null) {
parseTime(options.time, params);
}
browserContext.sendMessage("clockInstall", params);
sendMessageWithLogging("install", params);
}
@Override
public void runFor(long ticks) {
JsonObject params = new JsonObject();
params.addProperty("ticksNumber", ticks);
browserContext.sendMessage("clockRunFor", params);
sendMessageWithLogging("runFor", params);
}
@Override
public void runFor(String ticks) {
JsonObject params = new JsonObject();
params.addProperty("ticksString", ticks);
browserContext.sendMessage("clockRunFor", params);
sendMessageWithLogging("runFor", params);
}
@Override
public void pauseAt(long time) {
JsonObject params = new JsonObject();
params.addProperty("timeNumber", time);
browserContext.sendMessage("clockPauseAt", params);
sendMessageWithLogging("pauseAt", params);
}
@Override
public void pauseAt(String time) {
JsonObject params = new JsonObject();
params.addProperty("timeString", time);
browserContext.sendMessage("clockPauseAt", params);
sendMessageWithLogging("pauseAt", params);
}
@Override
public void pauseAt(Date time) {
JsonObject params = new JsonObject();
params.addProperty("timeNumber", time.getTime());
browserContext.sendMessage("clockPauseAt", params);
sendMessageWithLogging("pauseAt", params);
}
@Override
public void resume() {
browserContext.sendMessage("clockResume");
sendMessageWithLogging("resume", new JsonObject());
}
@Override
public void setFixedTime(long time) {
JsonObject params = new JsonObject();
params.addProperty("timeNumber", time);
browserContext.sendMessage("clockSetFixedTime", params);
sendMessageWithLogging("setFixedTime", params);
}
@Override
public void setFixedTime(String time) {
JsonObject params = new JsonObject();
params.addProperty("timeString", time);
browserContext.sendMessage("clockSetFixedTime", params);
sendMessageWithLogging("setFixedTime", params);
}
@Override
public void setFixedTime(Date time) {
JsonObject params = new JsonObject();
params.addProperty("timeNumber", time.getTime());
browserContext.sendMessage("clockSetFixedTime", params);
sendMessageWithLogging("setFixedTime", params);
}
@Override
public void setSystemTime(long time) {
JsonObject params = new JsonObject();
params.addProperty("timeNumber", time);
browserContext.sendMessage("clockSetSystemTime", params);
sendMessageWithLogging("setSystemTime", params);
}
@Override
public void setSystemTime(String time) {
JsonObject params = new JsonObject();
params.addProperty("timeString", time);
browserContext.sendMessage("clockSetSystemTime", params);
sendMessageWithLogging("setSystemTime", params);
}
@Override
public void setSystemTime(Date time) {
JsonObject params = new JsonObject();
params.addProperty("timeNumber", time.getTime());
browserContext.sendMessage("clockSetSystemTime", params);
sendMessageWithLogging("setSystemTime", params);
}
private static void parseTime(Object time, JsonObject params) {
@@ -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
@@ -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());
@@ -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);
@@ -16,13 +16,12 @@
package com.microsoft.playwright.impl;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.microsoft.playwright.PlaywrightException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Objects;
import java.util.Arrays;
import java.util.function.Predicate;
import java.util.regex.Pattern;
@@ -30,20 +29,14 @@ import static com.microsoft.playwright.impl.Utils.globToRegex;
import static com.microsoft.playwright.impl.Utils.toJsRegexFlags;
class UrlMatcher {
final Object rawSource;
private final Predicate<String> predicate;
private static Predicate<String> toPredicate(Pattern pattern) {
return s -> pattern.matcher(s).find();
}
static UrlMatcher any() {
return new UrlMatcher((Object) null, null);
}
private final String baseURL;
public final String glob;
public final Pattern pattern;
public final Predicate<String> predicate;
static UrlMatcher forOneOf(URL baseUrl, Object object) {
if (object == null) {
return UrlMatcher.any();
return new UrlMatcher(null, null, null, null);
}
if (object instanceof String) {
return new UrlMatcher(baseUrl, (String) object);
@@ -58,34 +51,77 @@ class UrlMatcher {
}
static String resolveUrl(URL baseUrl, String spec) {
return resolveUrl(baseUrl.toString(), spec);
}
private static String resolveUrl(String baseUrl, String spec) {
if (baseUrl == null) {
return spec;
}
try {
return new URL(baseUrl, spec).toString();
} catch (MalformedURLException e) {
// Join using URI instead of URL since URL doesn't handle ws(s) protocols.
return new URI(baseUrl).resolve(spec).toString();
} catch (URISyntaxException e) {
return spec;
}
}
UrlMatcher(URL base, String url) {
this(url, toPredicate(Pattern.compile(globToRegex(resolveUrl(base, url)))).or(s -> url == null || url.equals(s)));
private static String normaliseUrl(String spec) {
try {
// Align with the Node.js URL parser which automatically adds a slash to the path if it is empty.
URI url = new URI(spec);
if (url.getScheme() != null &&
Arrays.asList("http", "https", "ws", "wss").contains(url.getScheme()) &&
url.getPath().isEmpty()) {
return new URI(url.getScheme(), url.getAuthority(), "/", url.getQuery(), url.getFragment()).toString();
}
return url.toString();
} catch (URISyntaxException e) {
return spec;
}
}
UrlMatcher(URL baseURL, String glob) {
this(baseURL, glob, null, null);
}
UrlMatcher(Pattern pattern) {
this(pattern, toPredicate(pattern));
}
UrlMatcher(Predicate<String> predicate) {
this(predicate, predicate);
this(null, null, pattern, null);
}
private UrlMatcher(Object rawSource, Predicate<String> predicate) {
this.rawSource = rawSource;
UrlMatcher(Predicate<String> predicate) {
this(null, null, null, predicate);
}
private UrlMatcher(URL baseURL, String glob, Pattern pattern, Predicate<String> predicate) {
this.baseURL = baseURL != null ? baseURL.toString() : null;
this.glob = glob;
this.pattern = pattern;
this.predicate = predicate;
}
boolean test(String value) {
return predicate == null || predicate.test(value);
return testImpl(baseURL, pattern, predicate, glob, value);
}
private static boolean testImpl(String baseURL, Pattern pattern, Predicate<String> predicate, String glob, String value) {
if (pattern != null) {
return pattern.matcher(value).find();
}
if (predicate != null) {
return predicate.test(value);
}
if (glob != null) {
if (!glob.startsWith("*")) {
// Allow http(s) baseURL to match ws(s) urls.
if (baseURL != null && Pattern.compile("^https?://").matcher(baseURL).find() && Pattern.compile("^wss?://").matcher(value).find()) {
baseURL = baseURL.replaceFirst("^http", "ws");
}
glob = normaliseUrl(resolveUrl(baseURL, glob));
}
return Pattern.compile(globToRegex(glob)).matcher(value).find();
}
return true;
}
@Override
@@ -93,25 +129,38 @@ class UrlMatcher {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UrlMatcher that = (UrlMatcher) o;
if (rawSource instanceof Pattern && that.rawSource instanceof Pattern) {
Pattern a = (Pattern) rawSource;
Pattern b = (Pattern) that.rawSource;
return a.pattern().equals(b.pattern()) && a.flags() == b.flags();
if (pattern != null) {
return that.pattern != null && pattern.pattern().equals(that.pattern.pattern()) && pattern.flags() == that.pattern.flags();
}
return Objects.equals(rawSource, that.rawSource);
if (predicate != null) {
return predicate.equals(that.predicate);
}
if (glob != null) {
return glob.equals(that.glob);
}
return that.pattern == null && that.predicate == null && that.glob == null;
}
@Override
public int hashCode() {
return Objects.hash(rawSource);
if (pattern != null) {
return pattern.hashCode();
}
if (predicate != null) {
return predicate.hashCode();
}
if (glob != null) {
return glob.hashCode();
}
return 0;
}
@Override
public String toString() {
if (rawSource == null)
return "<any>";
if (rawSource instanceof Predicate)
return "matching predicate";
return rawSource.toString();
if (pattern != null)
return String.format("<regex pattern=\"%s\" flags=\"%s\">", pattern.pattern(), toJsRegexFlags(pattern));
if (this.predicate != null)
return "<predicate>";
return String.format("<glob pattern=\"%s\">", glob);
}
}
@@ -462,13 +462,11 @@ public class Utils {
JsonArray jsonPatterns = new JsonArray();
for (UrlMatcher matcher: matchers) {
JsonObject jsonPattern = new JsonObject();
Object urlFilter = 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));
if (matcher.glob != null) {
jsonPattern.addProperty("glob", matcher.glob);
} else if (matcher.pattern != null) {
jsonPattern.addProperty("regexSource", matcher.pattern.pattern());
jsonPattern.addProperty("regexFlags", toJsRegexFlags(matcher.pattern));
} else {
// Match all requests.
jsonPattern.addProperty("glob", "**/*");
@@ -20,6 +20,7 @@ import java.util.Collection;
class WaitableRace<T> implements Waitable<T> {
private final Collection<Waitable<T>> waitables;
private Waitable<T> firstReady;
WaitableRace(Collection<Waitable<T>> waitables) {
this.waitables = waitables;
@@ -27,8 +28,12 @@ class WaitableRace<T> implements Waitable<T> {
@Override
public boolean isDone() {
for (Waitable w : waitables) {
if (firstReady != null) {
return true;
}
for (Waitable<T> w : waitables) {
if (w.isDone()) {
firstReady = w;
return true;
}
}
@@ -37,14 +42,11 @@ class WaitableRace<T> implements Waitable<T> {
@Override
public T get() {
assert isDone();
dispose();
for (Waitable<T> w : waitables) {
if (w.isDone()) {
return w.get();
}
try {
return firstReady.get();
} finally {
dispose();
}
throw new IllegalStateException("At least one element must be ready");
}
@Override
@@ -299,6 +299,13 @@ public class TestBrowserContextBasic {
assertTrue(e.getMessage().contains("Target page, context or browser has been closed"), e.getMessage());
}
@Test
void waitForConditionThatMayChangeToFalse(BrowserContext context) {
int[] var = {0};
context.waitForCondition(() -> ++var[0] == 1);
assertEquals(1, var[0], "The predicate should be called only once.");
}
@Test
void shouldPropagateCloseReasonToPendingActions(Browser browser) {
BrowserContext context = browser.newContext();
@@ -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>");
@@ -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());
}
}
@@ -97,6 +97,18 @@ public class TestPageRoute extends TestBase {
assertEquals(asList(1), intercepted);
}
@Test
void shouldUnrouteNonExistentPatternHandler() {
List<Integer> intercepted = new ArrayList<>();
page.route(Pattern.compile("empty.html"), route -> {
intercepted.add(1);
route.fallback();
});
page.unroute("**/*");
page.navigate(server.EMPTY_PAGE);
assertEquals(asList( 1), intercepted);
}
@Test
void shouldSupportQuestionMarkInGlobPattern() {
server.setRoute("/index", exchange -> {
@@ -471,10 +471,10 @@ public class TestPageSetInputFiles extends TestBase {
}
@Test
void shouldUploadAFolderAndThrowForMultipleDirectories() throws IOException {
void shouldUploadAFolderAndThrowForMultipleDirectories(@TempDir Path tmpDir) throws IOException {
page.navigate(server.PREFIX + "/input/folderupload.html");
Locator input = page.locator("input[name=\"file1\"]");
Path dir = Paths.get("file-upload-test"); // Adjust path as necessary
Path dir = tmpDir.resolve("file-upload-test");
Files.createDirectories(dir.resolve("folder1"));
writeFile(dir.resolve("folder1").resolve("file1.txt"), "file1 content");
Files.createDirectories(dir.resolve("folder2"));
@@ -485,11 +485,11 @@ public class TestPageSetInputFiles extends TestBase {
}
@Test
void shouldThrowIfADirectoryAndFilesArePassed() throws IOException {
void shouldThrowIfADirectoryAndFilesArePassed(@TempDir Path tmpDir) throws IOException {
// Skipping conditions based on environment not directly translatable to Java; needs custom implementation
page.navigate(server.PREFIX + "/input/folderupload.html");
Locator input = page.locator("input[name=\"file1\"]");
Path dir = Paths.get("file-upload-test"); // Adjust path as necessary
Path dir = tmpDir.resolve("file-upload-test");
Files.createDirectories(dir.resolve("folder1"));
writeFile(dir.resolve("folder1").resolve("file1.txt"), "file1 content");
PlaywrightException e = assertThrows(PlaywrightException.class,
@@ -498,10 +498,10 @@ public class TestPageSetInputFiles extends TestBase {
}
@Test
void shouldThrowWhenUploadingAFolderInANormalFileUploadInput() throws IOException {
void shouldThrowWhenUploadingAFolderInANormalFileUploadInput(@TempDir Path tmpDir) throws IOException {
page.navigate(server.PREFIX + "/input/fileupload.html");
Locator input = page.locator("input[name=\"file1\"]");
Path dir = Paths.get("file-upload-test"); // Adjust path as necessary
Path dir = tmpDir.resolve("file-upload-test");
Files.createDirectories(dir);
writeFile(dir.resolve("file1.txt"), "file1 content");
PlaywrightException e = assertThrows(PlaywrightException.class,
@@ -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");
@@ -9,6 +9,8 @@ import org.junit.jupiter.params.provider.ValueSource;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.regex.Pattern;
@@ -317,4 +319,76 @@ public class TestRouteWebSocket {
"close code=3008 reason=oops"),
page.evaluate("window.log"));
}
@Test
public void shouldWorkWithBaseURL(Browser browser) throws Exception {
BrowserContext context = browser.newContext(new Browser.NewContextOptions().setBaseURL("http://localhost:" + webSocketServer.getPort()));
Page newPage = context.newPage();
newPage.routeWebSocket("/ws", ws -> {
ws.onMessage(message -> {
if (message.text() != null) {
ws.send(message.text());
} else {
ws.send(message.binary());
}
});
});
setupWS(newPage, webSocketServer.getPort(), "blob");
newPage.evaluate("async () => {\n" +
" await window.wsOpened;\n" +
" window.ws.send('echo');\n" +
" }");
newPage.waitForCondition(() -> {
Boolean result = (Boolean) newPage.evaluate("() => window.log.length >= 2");
return result;
});
assertEquals(
asList("open", "message: data=echo origin=ws://localhost:" + webSocketServer.getPort() + " lastEventId="),
newPage.evaluate("window.log"));
}
@Test
public void shouldWorkWithNoTrailingSlash(Page page) throws Exception {
List<String> log = new ArrayList<>();
// No trailing slash in the route pattern
page.routeWebSocket("ws://localhost:" + webSocketServer.getPort(), ws -> {
ws.onMessage(message -> {
log.add(message.text());
ws.send("response");
});
});
page.navigate("about:blank");
page.evaluate("({ port }) => {\n" +
" window.log = [];\n" +
" // No trailing slash in WebSocket URL\n" +
" window.ws = new WebSocket('ws://localhost:' + port);\n" +
" window.ws.addEventListener('message', event => window.log.push(event.data));\n" +
"}", mapOf("port", webSocketServer.getPort()));
// Wait for WebSocket to be ready (readyState === 1)
page.waitForCondition(() -> {
Integer result = (Integer) page.evaluate("() => window.ws.readyState");
return result == 1;
});
page.evaluate("() => window.ws.send('query')");
// Wait and verify server received message
page.waitForCondition(() -> log.size() >= 1);
assertEquals(asList("query"), log);
// Wait and verify client received response
page.waitForCondition(() -> {
Boolean result = (Boolean) page.evaluate("() => window.log.length >= 1");
return result;
});
assertEquals(asList("response"), page.evaluate("window.log"));
}
}
@@ -18,7 +18,10 @@ package com.microsoft.playwright;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.microsoft.playwright.options.AriaRole;
import com.microsoft.playwright.options.Location;
import com.microsoft.playwright.options.MouseButton;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -204,6 +207,89 @@ public class TestTracing extends TestBase {
assertEquals(asList("outer group", "Page.navigate", "inner group 1", "Frame.click", "inner group 2", "Page.isVisible"), calls);
}
@Test
void shouldTraceVariousAPIs(@TempDir Path tempDir) throws Exception {
context.tracing().start(new Tracing.StartOptions());
page.clock().install();
page.setContent("<input type='text' />");
page.locator("input").click(new Locator.ClickOptions().setButton(MouseButton.RIGHT));
page.getByRole(AriaRole.TEXTBOX).click();
page.keyboard().type("Hello world this is a very long string what happens when it overflows?");
page.keyboard().press("Control+c");
page.keyboard().down("Shift");
page.keyboard().insertText("Hello world");
page.keyboard().up("Shift");
page.mouse().move(0, 0);
page.mouse().down();
page.mouse().move(100, 200);
page.mouse().wheel(5, 7);
page.mouse().up();
page.clock().fastForward(1000);
page.clock().fastForward("30:00");
page.clock().pauseAt("2050-02-02");
page.clock().runFor(10);
page.clock().setFixedTime("2050-02-02");
page.clock().setSystemTime("2050-02-02");
page.clock().resume();
page.locator("input").click(new Locator.ClickOptions().setButton(MouseButton.RIGHT));
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(
"Clock.install",
"Page.setContent",
"Frame.click",
"Frame.click",
"Keyboard.type",
"Keyboard.press",
"Keyboard.down",
"Keyboard.insertText",
"Keyboard.up",
"Mouse.move",
"Mouse.down",
"Mouse.move",
"Mouse.wheel",
"Mouse.up",
"Clock.fastForward",
"Clock.fastForward",
"Clock.pauseAt",
"Clock.runFor",
"Clock.setFixedTime",
"Clock.setSystemTime",
"Clock.resume",
"Frame.click"),
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;
+6 -6
View File
@@ -6,7 +6,7 @@
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.50.0-SNAPSHOT</version>
<version>1.50.0</version>
<packaging>pom</packaging>
<name>Playwright Parent Project</name>
<description>Java library to automate Chromium, Firefox and WebKit with a single API.
@@ -44,10 +44,10 @@
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<maven.compiler.parameters>true</maven.compiler.parameters>
<gson.version>2.11.0</gson.version>
<junit.version>5.11.3</junit.version>
<gson.version>2.12.1</gson.version>
<junit.version>5.11.4</junit.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<websocket.version>1.5.7</websocket.version>
<websocket.version>1.6.0</websocket.version>
<opentest4j.version>1.3.0</opentest4j.version>
</properties>
@@ -136,7 +136,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.10.1</version>
<version>3.11.2</version>
<configuration>
<additionalOptions>--allow-script-in-comments</additionalOptions>
<failOnError>false</failOnError>
@@ -147,7 +147,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.1</version>
<version>3.5.2</version>
<configuration>
<properties>
<configurationParameters>
+1 -1
View File
@@ -1 +1 @@
1.49.0
1.50.1-beta-1738592302000
+1 -1
View File
@@ -6,7 +6,7 @@
<groupId>com.microsoft.playwright</groupId>
<artifactId>api-generator</artifactId>
<version>1.50.0-SNAPSHOT</version>
<version>1.50.0</version>
<name>Playwright - API Generator</name>
<description>
This is an internal module used to generate Java API from the upstream Playwright
+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.50.0-SNAPSHOT</version>
<version>1.50.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.50.0-SNAPSHOT</version>
<version>1.50.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.50.0-SNAPSHOT</version>
<version>1.50.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.50.0-SNAPSHOT</version>
<version>1.50.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.50.0-SNAPSHOT</version>
<version>1.50.0</version>
<name>Playwright - Update Version in Documentation</name>
<description>
This is an internal module used to update versions in the documentation based on