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

Compare commits

..

1 Commits

Author SHA1 Message Date
Yury Semikhatsky ea48985e21 chore: set version to 1.48.0 (#1688) 2024-10-17 12:35:16 -07:00
63 changed files with 516 additions and 1493 deletions
+3 -6
View File
@@ -19,6 +19,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: microsoft/playwright-github-action@v1
- name: Set up JDK 1.8
uses: actions/setup-java@v2
with:
@@ -29,8 +30,6 @@ jobs:
run: scripts/download_driver.sh
- name: Build & Install
run: mvn -B install -D skipTests --no-transfer-progress
- name: Install browsers
run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps" -f playwright/pom.xml --no-transfer-progress
- name: Run tests
run: mvn test --no-transfer-progress --fail-at-end -D org.slf4j.simpleLogger.showDateTime=true -D org.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss
env:
@@ -64,6 +63,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: microsoft/playwright-github-action@v1
- name: Install Media Pack
if: matrix.os == 'windows-latest'
shell: powershell
@@ -78,8 +78,6 @@ jobs:
run: scripts/download_driver.sh
- name: Build & Install
run: mvn -B install -D skipTests --no-transfer-progress
- name: Install browsers
run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps" -f playwright/pom.xml --no-transfer-progress
- name: Install MS Edge
if: matrix.browser-channel == 'msedge' && matrix.os == 'macos-latest'
shell: bash
@@ -99,6 +97,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: microsoft/playwright-github-action@v1
- name: Set up JDK 21
uses: actions/setup-java@v2
with:
@@ -109,8 +108,6 @@ jobs:
run: scripts/download_driver.sh
- name: Build & Install
run: mvn -B install -D skipTests --no-transfer-progress
- name: Install browsers
run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps" -f playwright/pom.xml --no-transfer-progress
- name: Run tests
run: mvn test --no-transfer-progress --fail-at-end
env:
+1 -1
View File
@@ -24,7 +24,7 @@ jobs:
strategy:
fail-fast: false
matrix:
flavor: [jammy, noble]
flavor: [focal, jammy, noble]
steps:
- uses: actions/checkout@v3
- name: Build Docker image
+1 -1
View File
@@ -9,7 +9,7 @@ on:
jobs:
trigger:
name: "trigger"
runs-on: ubuntu-24.04
runs-on: ubuntu-20.04
steps:
- run: |
curl -X POST \
+1 -4
View File
@@ -20,14 +20,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: microsoft/playwright-github-action@v1
- name: Download drivers
run: scripts/download_driver.sh
- name: Regenerate APIs
run: scripts/generate_api.sh
- name: Build & Install
run: mvn -B install -D skipTests --no-transfer-progress
- name: Install browsers
run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps" -f playwright/pom.xml --no-transfer-progress
- name: Update browser versions in README
run: scripts/update_readme.sh
- name: Verify API is up to date
+3 -3
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 -->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 -->134.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Chromium <!-- GEN:chromium-version -->130.0.6723.31<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->18.0<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Firefox <!-- GEN:firefox-version -->131.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</version>
<version>1.48.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</version>
<version>1.48.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</version>
<version>1.48.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</version>
<version>1.48.0</version>
</parent>
<artifactId>playwright</artifactId>
@@ -29,15 +29,15 @@ import java.util.regex.Pattern;
* import com.microsoft.playwright.*;
*
* public class Example {
* public static void main(String[] args) {
* try (Playwright playwright = Playwright.create()) {
* BrowserType firefox = playwright.firefox();
* Browser browser = firefox.launch();
* Page page = browser.newPage();
* page.navigate("https://example.com");
* browser.close();
* }
* }
* public static void main(String[] args) {
* try (Playwright playwright = Playwright.create()) {
* BrowserType firefox = playwright.firefox()
* Browser browser = firefox.launch();
* Page page = browser.newPage();
* page.navigate('https://example.com');
* browser.close();
* }
* }
* }
* }</pre>
*/
@@ -111,11 +111,9 @@ public interface Browser extends AutoCloseable {
*/
public List<ClientCertificate> clientCertificates;
/**
* Emulates <a
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-colors-scheme</a> media
* feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia
* Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code
* "light"}.
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code
* "no-preference"}. See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing
* {@code null} resets emulation to system defaults. Defaults to {@code "light"}.
*/
public Optional<ColorScheme> colorScheme;
/**
@@ -325,11 +323,9 @@ public interface Browser extends AutoCloseable {
return this;
}
/**
* Emulates <a
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-colors-scheme</a> media
* feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia
* Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code
* "light"}.
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code
* "no-preference"}. See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing
* {@code null} resets emulation to system defaults. Defaults to {@code "light"}.
*/
public NewContextOptions setColorScheme(ColorScheme colorScheme) {
this.colorScheme = Optional.ofNullable(colorScheme);
@@ -664,11 +660,9 @@ public interface Browser extends AutoCloseable {
*/
public List<ClientCertificate> clientCertificates;
/**
* Emulates <a
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-colors-scheme</a> media
* feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia
* Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code
* "light"}.
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code
* "no-preference"}. See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing
* {@code null} resets emulation to system defaults. Defaults to {@code "light"}.
*/
public Optional<ColorScheme> colorScheme;
/**
@@ -878,11 +872,9 @@ public interface Browser extends AutoCloseable {
return this;
}
/**
* Emulates <a
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-colors-scheme</a> media
* feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia
* Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code
* "light"}.
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code
* "no-preference"}. See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing
* {@code null} resets emulation to system defaults. Defaults to {@code "light"}.
*/
public NewPageOptions setColorScheme(ColorScheme colorScheme) {
this.colorScheme = Optional.ofNullable(colorScheme);
@@ -1225,10 +1217,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. 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> <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> The {@code Browser} object itself is considered to be disposed and cannot be used anymore.
*
@@ -1244,10 +1236,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. 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> <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> The {@code Browser} object itself is considered to be disposed and cannot be used anymore.
*
@@ -1297,7 +1289,7 @@ public interface Browser extends AutoCloseable {
* BrowserContext context = browser.newContext();
* // Create a new page in a pristine context.
* Page page = context.newPage();
* page.navigate("https://example.com");
* page.navigate('https://example.com');
*
* // Graceful close up everything
* context.close();
@@ -1324,7 +1316,7 @@ public interface Browser extends AutoCloseable {
* BrowserContext context = browser.newContext();
* // Create a new page in a pristine context.
* Page page = context.newPage();
* page.navigate("https://example.com");
* page.navigate('https://example.com');
*
* // Graceful close up everything
* context.close();
@@ -1372,7 +1364,7 @@ public interface Browser extends AutoCloseable {
* <pre>{@code
* browser.startTracing(page, new Browser.StartTracingOptions()
* .setPath(Paths.get("trace.json")));
* page.navigate("https://www.google.com");
* page.goto('https://www.google.com');
* browser.stopTracing();
* }</pre>
*
@@ -1396,7 +1388,7 @@ public interface Browser extends AutoCloseable {
* <pre>{@code
* browser.startTracing(page, new Browser.StartTracingOptions()
* .setPath(Paths.get("trace.json")));
* page.navigate("https://www.google.com");
* page.goto('https://www.google.com');
* browser.stopTracing();
* }</pre>
*
@@ -1419,7 +1411,7 @@ public interface Browser extends AutoCloseable {
* <pre>{@code
* browser.startTracing(page, new Browser.StartTracingOptions()
* .setPath(Paths.get("trace.json")));
* page.navigate("https://www.google.com");
* page.goto('https://www.google.com');
* browser.stopTracing();
* }</pre>
*
@@ -701,7 +701,7 @@ public interface BrowserContext extends AutoCloseable {
* public class Example {
* public static void main(String[] args) {
* try (Playwright playwright = Playwright.create()) {
* BrowserType webkit = playwright.webkit();
* BrowserType webkit = playwright.webkit()
* Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false));
* BrowserContext context = browser.newContext();
* context.exposeBinding("pageURL", (source, args) -> source.page().url());
@@ -748,7 +748,7 @@ public interface BrowserContext extends AutoCloseable {
* public class Example {
* public static void main(String[] args) {
* try (Playwright playwright = Playwright.create()) {
* BrowserType webkit = playwright.webkit();
* BrowserType webkit = playwright.webkit()
* Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false));
* BrowserContext context = browser.newContext();
* context.exposeBinding("pageURL", (source, args) -> source.page().url());
@@ -797,9 +797,8 @@ public interface BrowserContext extends AutoCloseable {
* public class Example {
* public static void main(String[] args) {
* try (Playwright playwright = Playwright.create()) {
* BrowserType webkit = playwright.webkit();
* BrowserType webkit = playwright.webkit()
* Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false));
* BrowserContext context = browser.newContext();
* context.exposeFunction("sha256", args -> {
* String text = (String) args[0];
* MessageDigest crypto;
@@ -834,14 +833,10 @@ 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 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:
* @param permissions A permission or an array of permissions to grant. Permissions can be one of the following values:
* <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>
@@ -866,14 +861,10 @@ 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 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:
* @param permissions A permission or an array of permissions to grant. Permissions can be one of the following values:
* <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>
@@ -1396,7 +1387,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. Pass {@code 0} to disable timeout.
* @param timeout Maximum time in milliseconds
* @since v1.8
*/
void setDefaultTimeout(double timeout);
@@ -172,14 +172,9 @@ public interface BrowserType {
*/
public List<String> args;
/**
* 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>.
* 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>.
*/
public Object channel;
/**
@@ -270,28 +265,18 @@ public interface BrowserType {
}
@Deprecated
/**
* 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>.
* 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>.
*/
public LaunchOptions setChannel(BrowserChannel channel) {
this.channel = channel;
return this;
}
/**
* 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>.
* 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>.
*/
public LaunchOptions setChannel(String channel) {
this.channel = channel;
@@ -461,14 +446,9 @@ public interface BrowserType {
*/
public Boolean bypassCSP;
/**
* 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>.
* 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>.
*/
public Object channel;
/**
@@ -490,11 +470,9 @@ public interface BrowserType {
*/
public List<ClientCertificate> clientCertificates;
/**
* Emulates <a
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-colors-scheme</a> media
* feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia
* Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code
* "light"}.
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code
* "no-preference"}. See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing
* {@code null} resets emulation to system defaults. Defaults to {@code "light"}.
*/
public Optional<ColorScheme> colorScheme;
/**
@@ -754,28 +732,18 @@ public interface BrowserType {
}
@Deprecated
/**
* 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>.
* 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>.
*/
public LaunchPersistentContextOptions setChannel(BrowserChannel channel) {
this.channel = channel;
return this;
}
/**
* 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>.
* 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>.
*/
public LaunchPersistentContextOptions setChannel(String channel) {
this.channel = channel;
@@ -806,11 +774,9 @@ public interface BrowserType {
return this;
}
/**
* Emulates <a
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-colors-scheme</a> media
* feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia
* Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code
* "light"}.
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code
* "no-preference"}. See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing
* {@code null} resets emulation to system defaults. Defaults to {@code "light"}.
*/
public LaunchPersistentContextOptions setColorScheme(ColorScheme colorScheme) {
this.colorScheme = Optional.ofNullable(colorScheme);
@@ -174,19 +174,6 @@ 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
*/
@@ -207,19 +194,6 @@ 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
*/
@@ -240,19 +214,6 @@ 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
*/
@@ -266,10 +227,6 @@ public interface Clock {
/**
* Makes {@code Date.now} and {@code new Date()} return fixed fake time at all times, keeps all the timers running.
*
* <p> Use this method for simple scenarios where you only need to test with a predefined time. For more advanced scenarios,
* use {@link com.microsoft.playwright.Clock#install Clock.install()} instead. Read docs on <a
* href="https://playwright.dev/java/docs/clock">clock emulation</a> to learn more.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* page.clock().setFixedTime(new Date());
@@ -284,10 +241,6 @@ public interface Clock {
/**
* Makes {@code Date.now} and {@code new Date()} return fixed fake time at all times, keeps all the timers running.
*
* <p> Use this method for simple scenarios where you only need to test with a predefined time. For more advanced scenarios,
* use {@link com.microsoft.playwright.Clock#install Clock.install()} instead. Read docs on <a
* href="https://playwright.dev/java/docs/clock">clock emulation</a> to learn more.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* page.clock().setFixedTime(new Date());
@@ -302,10 +255,6 @@ public interface Clock {
/**
* Makes {@code Date.now} and {@code new Date()} return fixed fake time at all times, keeps all the timers running.
*
* <p> Use this method for simple scenarios where you only need to test with a predefined time. For more advanced scenarios,
* use {@link com.microsoft.playwright.Clock#install Clock.install()} instead. Read docs on <a
* href="https://playwright.dev/java/docs/clock">clock emulation</a> to learn more.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* page.clock().setFixedTime(new Date());
@@ -318,8 +267,7 @@ public interface Clock {
*/
void setFixedTime(Date time);
/**
* Sets system time, but does not trigger any timers. Use this to test how the web page reacts to a time shift, for example
* switching from summer to winter time, or changing time zones.
* Sets current system time but does not trigger any timers.
*
* <p> <strong>Usage</strong>
* <pre>{@code
@@ -333,8 +281,7 @@ public interface Clock {
*/
void setSystemTime(long time);
/**
* Sets system time, but does not trigger any timers. Use this to test how the web page reacts to a time shift, for example
* switching from summer to winter time, or changing time zones.
* Sets current system time but does not trigger any timers.
*
* <p> <strong>Usage</strong>
* <pre>{@code
@@ -348,8 +295,7 @@ public interface Clock {
*/
void setSystemTime(String time);
/**
* Sets system time, but does not trigger any timers. Use this to test how the web page reacts to a time shift, for example
* switching from summer to winter time, or changing time zones.
* Sets current system time but does not trigger any timers.
*
* <p> <strong>Usage</strong>
* <pre>{@code
@@ -39,8 +39,8 @@ import java.util.*;
* });
*
* // Deconstruct console.log arguments
* msg.args().get(0).jsonValue(); // hello
* msg.args().get(1).jsonValue(); // 42
* msg.args().get(0).jsonValue() // hello
* msg.args().get(1).jsonValue() // 42
* }</pre>
*/
public interface ConsoleMessage {
@@ -3499,19 +3499,19 @@ public interface Frame {
* <p> You can locate by text substring, exact string, or a regular expression:
* <pre>{@code
* // Matches <span>
* page.getByText("world");
* page.getByText("world")
*
* // Matches first <div>
* page.getByText("Hello world");
* page.getByText("Hello world")
*
* // Matches second <div>
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
*
* // Matches both <div>s
* page.getByText(Pattern.compile("Hello"));
* page.getByText(Pattern.compile("Hello"))
*
* // Matches second <div>
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
* }</pre>
*
* <p> <strong>Details</strong>
@@ -3541,19 +3541,19 @@ public interface Frame {
* <p> You can locate by text substring, exact string, or a regular expression:
* <pre>{@code
* // Matches <span>
* page.getByText("world");
* page.getByText("world")
*
* // Matches first <div>
* page.getByText("Hello world");
* page.getByText("Hello world")
*
* // Matches second <div>
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
*
* // Matches both <div>s
* page.getByText(Pattern.compile("Hello"));
* page.getByText(Pattern.compile("Hello"))
*
* // Matches second <div>
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
* }</pre>
*
* <p> <strong>Details</strong>
@@ -3581,19 +3581,19 @@ public interface Frame {
* <p> You can locate by text substring, exact string, or a regular expression:
* <pre>{@code
* // Matches <span>
* page.getByText("world");
* page.getByText("world")
*
* // Matches first <div>
* page.getByText("Hello world");
* page.getByText("Hello world")
*
* // Matches second <div>
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
*
* // Matches both <div>s
* page.getByText(Pattern.compile("Hello"));
* page.getByText(Pattern.compile("Hello"))
*
* // Matches second <div>
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
* }</pre>
*
* <p> <strong>Details</strong>
@@ -3623,19 +3623,19 @@ public interface Frame {
* <p> You can locate by text substring, exact string, or a regular expression:
* <pre>{@code
* // Matches <span>
* page.getByText("world");
* page.getByText("world")
*
* // Matches first <div>
* page.getByText("Hello world");
* page.getByText("Hello world")
*
* // Matches second <div>
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
*
* // Matches both <div>s
* page.getByText(Pattern.compile("Hello"));
* page.getByText(Pattern.compile("Hello"))
*
* // Matches second <div>
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
* }</pre>
*
* <p> <strong>Details</strong>
@@ -734,19 +734,19 @@ public interface FrameLocator {
* <p> You can locate by text substring, exact string, or a regular expression:
* <pre>{@code
* // Matches <span>
* page.getByText("world");
* page.getByText("world")
*
* // Matches first <div>
* page.getByText("Hello world");
* page.getByText("Hello world")
*
* // Matches second <div>
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
*
* // Matches both <div>s
* page.getByText(Pattern.compile("Hello"));
* page.getByText(Pattern.compile("Hello"))
*
* // Matches second <div>
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
* }</pre>
*
* <p> <strong>Details</strong>
@@ -776,19 +776,19 @@ public interface FrameLocator {
* <p> You can locate by text substring, exact string, or a regular expression:
* <pre>{@code
* // Matches <span>
* page.getByText("world");
* page.getByText("world")
*
* // Matches first <div>
* page.getByText("Hello world");
* page.getByText("Hello world")
*
* // Matches second <div>
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
*
* // Matches both <div>s
* page.getByText(Pattern.compile("Hello"));
* page.getByText(Pattern.compile("Hello"))
*
* // Matches second <div>
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
* }</pre>
*
* <p> <strong>Details</strong>
@@ -816,19 +816,19 @@ public interface FrameLocator {
* <p> You can locate by text substring, exact string, or a regular expression:
* <pre>{@code
* // Matches <span>
* page.getByText("world");
* page.getByText("world")
*
* // Matches first <div>
* page.getByText("Hello world");
* page.getByText("Hello world")
*
* // Matches second <div>
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
*
* // Matches both <div>s
* page.getByText(Pattern.compile("Hello"));
* page.getByText(Pattern.compile("Hello"))
*
* // Matches second <div>
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
* }</pre>
*
* <p> <strong>Details</strong>
@@ -858,19 +858,19 @@ public interface FrameLocator {
* <p> You can locate by text substring, exact string, or a regular expression:
* <pre>{@code
* // Matches <span>
* page.getByText("world");
* page.getByText("world")
*
* // Matches first <div>
* page.getByText("Hello world");
* page.getByText("Hello world")
*
* // Matches second <div>
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
*
* // Matches both <div>s
* page.getByText(Pattern.compile("Hello"));
* page.getByText(Pattern.compile("Hello"))
*
* // Matches second <div>
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
* }</pre>
*
* <p> <strong>Details</strong>
@@ -48,7 +48,10 @@ import com.microsoft.playwright.options.*;
*
* <p> An example to trigger select-all with the keyboard
* <pre>{@code
* page.keyboard().press("ControlOrMeta+A");
* // on Windows and Linux
* page.keyboard().press("Control+A");
* // on macOS
* page.keyboard().press("Meta+A");
* }</pre>
*/
public interface Keyboard {
@@ -161,7 +164,7 @@ public interface Keyboard {
* Page page = browser.newPage();
* page.navigate("https://keycode.info");
* page.keyboard().press("A");
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("A.png")));
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("A.png"));
* page.keyboard().press("ArrowLeft");
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("ArrowLeft.png")));
* page.keyboard().press("Shift+O");
@@ -208,7 +211,7 @@ public interface Keyboard {
* Page page = browser.newPage();
* page.navigate("https://keycode.info");
* page.keyboard().press("A");
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("A.png")));
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("A.png"));
* page.keyboard().press("ArrowLeft");
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("ArrowLeft.png")));
* page.keyboard().press("Shift+O");
@@ -29,26 +29,6 @@ import java.util.regex.Pattern;
* <p> <a href="https://playwright.dev/java/docs/locators">Learn more about locators</a>.
*/
public interface Locator {
class AriaSnapshotOptions {
/**
* Maximum time in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
* value can be changed by using the {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout
* BrowserContext.setDefaultTimeout()} or {@link com.microsoft.playwright.Page#setDefaultTimeout Page.setDefaultTimeout()}
* methods.
*/
public Double timeout;
/**
* Maximum time in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
* value can be changed by using the {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout
* BrowserContext.setDefaultTimeout()} or {@link com.microsoft.playwright.Page#setDefaultTimeout Page.setDefaultTimeout()}
* methods.
*/
public AriaSnapshotOptions setTimeout(double timeout) {
this.timeout = timeout;
return this;
}
}
class BlurOptions {
/**
* Maximum time in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
@@ -2171,7 +2151,7 @@ public interface Locator {
*
* <p> <strong>Usage</strong>
* <pre>{@code
* for (Locator li : page.getByRole("listitem").all())
* for (Locator li : page.getByRole('listitem').all())
* li.click();
* }</pre>
*
@@ -2222,66 +2202,6 @@ public interface Locator {
* @since v1.34
*/
Locator and(Locator locator);
/**
* Captures the aria snapshot of the given element. Read more about <a
* href="https://playwright.dev/java/docs/aria-snapshots">aria snapshots</a> and {@link
* com.microsoft.playwright.assertions.LocatorAssertions#matchesAriaSnapshot LocatorAssertions.matchesAriaSnapshot()} for
* the corresponding assertion.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* page.getByRole(AriaRole.LINK).ariaSnapshot();
* }</pre>
*
* <p> <strong>Details</strong>
*
* <p> This method captures the aria snapshot of the given element. The snapshot is a string that represents the state of the
* element and its children. The snapshot can be used to assert the state of the element in the test, or to compare it to
* state in the future.
*
* <p> The ARIA snapshot is represented using <a href="https://yaml.org/spec/1.2.2/">YAML</a> markup language:
* <ul>
* <li> The keys of the objects are the roles and optional accessible names of the elements.</li>
* <li> The values are either text content or an array of child elements.</li>
* <li> Generic static text can be represented with the {@code text} key.</li>
* </ul>
*
* <p> Below is the HTML markup and the respective ARIA snapshot:
*
* @since v1.49
*/
default String ariaSnapshot() {
return ariaSnapshot(null);
}
/**
* Captures the aria snapshot of the given element. Read more about <a
* href="https://playwright.dev/java/docs/aria-snapshots">aria snapshots</a> and {@link
* com.microsoft.playwright.assertions.LocatorAssertions#matchesAriaSnapshot LocatorAssertions.matchesAriaSnapshot()} for
* the corresponding assertion.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* page.getByRole(AriaRole.LINK).ariaSnapshot();
* }</pre>
*
* <p> <strong>Details</strong>
*
* <p> This method captures the aria snapshot of the given element. The snapshot is a string that represents the state of the
* element and its children. The snapshot can be used to assert the state of the element in the test, or to compare it to
* state in the future.
*
* <p> The ARIA snapshot is represented using <a href="https://yaml.org/spec/1.2.2/">YAML</a> markup language:
* <ul>
* <li> The keys of the objects are the roles and optional accessible names of the elements.</li>
* <li> The values are either text content or an array of child elements.</li>
* <li> Generic static text can be represented with the {@code text} key.</li>
* </ul>
*
* <p> Below is the HTML markup and the respective ARIA snapshot:
*
* @since v1.49
*/
String ariaSnapshot(AriaSnapshotOptions options);
/**
* Calls <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/blur">blur</a> on the element.
*
@@ -2630,6 +2550,7 @@ 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);
@@ -2678,6 +2599,7 @@ 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);
@@ -2725,6 +2647,7 @@ 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);
@@ -3532,19 +3455,19 @@ public interface Locator {
* <p> You can locate by text substring, exact string, or a regular expression:
* <pre>{@code
* // Matches <span>
* page.getByText("world");
* page.getByText("world")
*
* // Matches first <div>
* page.getByText("Hello world");
* page.getByText("Hello world")
*
* // Matches second <div>
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
*
* // Matches both <div>s
* page.getByText(Pattern.compile("Hello"));
* page.getByText(Pattern.compile("Hello"))
*
* // Matches second <div>
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
* }</pre>
*
* <p> <strong>Details</strong>
@@ -3574,19 +3497,19 @@ public interface Locator {
* <p> You can locate by text substring, exact string, or a regular expression:
* <pre>{@code
* // Matches <span>
* page.getByText("world");
* page.getByText("world")
*
* // Matches first <div>
* page.getByText("Hello world");
* page.getByText("Hello world")
*
* // Matches second <div>
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
*
* // Matches both <div>s
* page.getByText(Pattern.compile("Hello"));
* page.getByText(Pattern.compile("Hello"))
*
* // Matches second <div>
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
* }</pre>
*
* <p> <strong>Details</strong>
@@ -3614,19 +3537,19 @@ public interface Locator {
* <p> You can locate by text substring, exact string, or a regular expression:
* <pre>{@code
* // Matches <span>
* page.getByText("world");
* page.getByText("world")
*
* // Matches first <div>
* page.getByText("Hello world");
* page.getByText("Hello world")
*
* // Matches second <div>
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
*
* // Matches both <div>s
* page.getByText(Pattern.compile("Hello"));
* page.getByText(Pattern.compile("Hello"))
*
* // Matches second <div>
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
* }</pre>
*
* <p> <strong>Details</strong>
@@ -3656,19 +3579,19 @@ public interface Locator {
* <p> You can locate by text substring, exact string, or a regular expression:
* <pre>{@code
* // Matches <span>
* page.getByText("world");
* page.getByText("world")
*
* // Matches first <div>
* page.getByText("Hello world");
* page.getByText("Hello world")
*
* // Matches second <div>
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
*
* // Matches both <div>s
* page.getByText(Pattern.compile("Hello"));
* page.getByText(Pattern.compile("Hello"))
*
* // Matches second <div>
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
* }</pre>
*
* <p> <strong>Details</strong>
@@ -3965,9 +3888,7 @@ public interface Locator {
*/
boolean isDisabled(IsDisabledOptions options);
/**
* 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.
* Returns whether the element is <a href="https://playwright.dev/java/docs/actionability#editable">editable</a>.
*
* <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
@@ -3984,9 +3905,7 @@ public interface Locator {
return isEditable(null);
}
/**
* 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.
* Returns whether the element is <a href="https://playwright.dev/java/docs/actionability#editable">editable</a>.
*
* <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
@@ -4167,21 +4086,17 @@ 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, potentially causing a <a
* href="https://playwright.dev/java/docs/locators#strictness">locator strictness</a> violation.
* <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> <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).first()).isVisible();
* assertThat(newEmail.or(dialog)).isVisible();
* if (dialog.isVisible())
* page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Dismiss")).click();
* newEmail.click();
@@ -959,10 +959,8 @@ public interface Page extends AutoCloseable {
}
class EmulateMediaOptions {
/**
* Emulates <a
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-colors-scheme</a> media
* feature, supported values are {@code "light"} and {@code "dark"}. Passing {@code null} disables color scheme emulation.
* {@code "no-preference"} is deprecated.
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code
* "no-preference"}. Passing {@code null} disables color scheme emulation.
*/
public Optional<ColorScheme> colorScheme;
/**
@@ -982,10 +980,8 @@ public interface Page extends AutoCloseable {
public Optional<ReducedMotion> reducedMotion;
/**
* Emulates <a
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-colors-scheme</a> media
* feature, supported values are {@code "light"} and {@code "dark"}. Passing {@code null} disables color scheme emulation.
* {@code "no-preference"} is deprecated.
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code
* "no-preference"}. Passing {@code null} disables color scheme emulation.
*/
public EmulateMediaOptions setColorScheme(ColorScheme colorScheme) {
this.colorScheme = Optional.ofNullable(colorScheme);
@@ -4155,9 +4151,9 @@ public interface Page extends AutoCloseable {
*
* <p> <strong>Usage</strong>
* <pre>{@code
* page.dragAndDrop("#source", "#target");
* page.dragAndDrop("#source", '#target');
* // or specify exact positions relative to the top-left corners of the elements:
* page.dragAndDrop("#source", "#target", new Page.DragAndDropOptions()
* page.dragAndDrop("#source", '#target', new Page.DragAndDropOptions()
* .setSourcePosition(34, 7).setTargetPosition(10, 20));
* }</pre>
*
@@ -4176,9 +4172,9 @@ public interface Page extends AutoCloseable {
*
* <p> <strong>Usage</strong>
* <pre>{@code
* page.dragAndDrop("#source", "#target");
* page.dragAndDrop("#source", '#target');
* // or specify exact positions relative to the top-left corners of the elements:
* page.dragAndDrop("#source", "#target", new Page.DragAndDropOptions()
* page.dragAndDrop("#source", '#target', new Page.DragAndDropOptions()
* .setSourcePosition(34, 7).setTargetPosition(10, 20));
* }</pre>
*
@@ -4218,6 +4214,8 @@ public interface Page extends AutoCloseable {
* // true
* page.evaluate("() => matchMedia('(prefers-color-scheme: light)').matches");
* // false
* page.evaluate("() => matchMedia('(prefers-color-scheme: no-preference)').matches");
* // false
* }</pre>
*
* @since v1.8
@@ -4254,6 +4252,8 @@ public interface Page extends AutoCloseable {
* // true
* page.evaluate("() => matchMedia('(prefers-color-scheme: light)').matches");
* // false
* page.evaluate("() => matchMedia('(prefers-color-scheme: no-preference)').matches");
* // false
* }</pre>
*
* @since v1.8
@@ -4560,7 +4560,7 @@ public interface Page extends AutoCloseable {
* public static void main(String[] args) {
* try (Playwright playwright = Playwright.create()) {
* BrowserType webkit = playwright.webkit();
* Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false));
* Browser browser = webkit.launch({ headless: false });
* BrowserContext context = browser.newContext();
* Page page = context.newPage();
* page.exposeBinding("pageURL", (source, args) -> source.page().url());
@@ -4610,7 +4610,7 @@ public interface Page extends AutoCloseable {
* public static void main(String[] args) {
* try (Playwright playwright = Playwright.create()) {
* BrowserType webkit = playwright.webkit();
* Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false));
* Browser browser = webkit.launch({ headless: false });
* BrowserContext context = browser.newContext();
* Page page = context.newPage();
* page.exposeBinding("pageURL", (source, args) -> source.page().url());
@@ -4662,27 +4662,26 @@ public interface Page extends AutoCloseable {
* public static void main(String[] args) {
* try (Playwright playwright = Playwright.create()) {
* BrowserType webkit = playwright.webkit();
* Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false));
* Browser browser = webkit.launch({ headless: false });
* Page page = browser.newPage();
* page.exposeFunction("sha256", args -> {
* String text = (String) args[0];
* MessageDigest crypto;
* try {
* String text = (String) args[0];
* MessageDigest crypto = MessageDigest.getInstance("SHA-256");
* byte[] token = crypto.digest(text.getBytes(StandardCharsets.UTF_8));
* return Base64.getEncoder().encodeToString(token);
* crypto = MessageDigest.getInstance("SHA-256");
* } catch (NoSuchAlgorithmException e) {
* return null;
* }
* byte[] token = crypto.digest(text.getBytes(StandardCharsets.UTF_8));
* return Base64.getEncoder().encodeToString(token);
* });
* page.setContent(
* "<script>\n" +
* page.setContent("<script>\n" +
* " async function onClick() {\n" +
* " document.querySelector('div').textContent = await window.sha256('PLAYWRIGHT');\n" +
* " }\n" +
* "</script>\n" +
* "<button onclick=\"onClick()\">Click me</button>\n" +
* "<div></div>"
* );
* "<div></div>\n");
* page.click("button");
* }
* }
@@ -4758,7 +4757,7 @@ public interface Page extends AutoCloseable {
* Frame frame = page.frame("frame-name");
* }</pre>
* <pre>{@code
* Frame frame = page.frameByUrl(Pattern.compile(".*domain.*"));
* Frame frame = page.frameByUrl(Pattern.compile(".*domain.*");
* }</pre>
*
* @param name Frame name specified in the {@code iframe}'s {@code name} attribute.
@@ -5164,19 +5163,19 @@ public interface Page extends AutoCloseable {
* <p> You can locate by text substring, exact string, or a regular expression:
* <pre>{@code
* // Matches <span>
* page.getByText("world");
* page.getByText("world")
*
* // Matches first <div>
* page.getByText("Hello world");
* page.getByText("Hello world")
*
* // Matches second <div>
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
*
* // Matches both <div>s
* page.getByText(Pattern.compile("Hello"));
* page.getByText(Pattern.compile("Hello"))
*
* // Matches second <div>
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
* }</pre>
*
* <p> <strong>Details</strong>
@@ -5206,19 +5205,19 @@ public interface Page extends AutoCloseable {
* <p> You can locate by text substring, exact string, or a regular expression:
* <pre>{@code
* // Matches <span>
* page.getByText("world");
* page.getByText("world")
*
* // Matches first <div>
* page.getByText("Hello world");
* page.getByText("Hello world")
*
* // Matches second <div>
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
*
* // Matches both <div>s
* page.getByText(Pattern.compile("Hello"));
* page.getByText(Pattern.compile("Hello"))
*
* // Matches second <div>
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
* }</pre>
*
* <p> <strong>Details</strong>
@@ -5246,19 +5245,19 @@ public interface Page extends AutoCloseable {
* <p> You can locate by text substring, exact string, or a regular expression:
* <pre>{@code
* // Matches <span>
* page.getByText("world");
* page.getByText("world")
*
* // Matches first <div>
* page.getByText("Hello world");
* page.getByText("Hello world")
*
* // Matches second <div>
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
*
* // Matches both <div>s
* page.getByText(Pattern.compile("Hello"));
* page.getByText(Pattern.compile("Hello"))
*
* // Matches second <div>
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
* }</pre>
*
* <p> <strong>Details</strong>
@@ -5288,19 +5287,19 @@ public interface Page extends AutoCloseable {
* <p> You can locate by text substring, exact string, or a regular expression:
* <pre>{@code
* // Matches <span>
* page.getByText("world");
* page.getByText("world")
*
* // Matches first <div>
* page.getByText("Hello world");
* page.getByText("Hello world")
*
* // Matches second <div>
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
*
* // Matches both <div>s
* page.getByText(Pattern.compile("Hello"));
* page.getByText(Pattern.compile("Hello"))
*
* // Matches second <div>
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
* }</pre>
*
* <p> <strong>Details</strong>
@@ -5809,6 +5808,8 @@ 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()}:
*
@@ -5867,6 +5868,8 @@ 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()}:
*
@@ -6077,24 +6080,24 @@ public interface Page extends AutoCloseable {
* <p> An example that closes a "Sign up to the newsletter" dialog when it appears:
* <pre>{@code
* // Setup the handler.
* page.addLocatorHandler(page.getByText("Sign up to the newsletter"), () -> {
* page.addLocatorHandler(page.getByText("Sign up to the newsletter"), () => {
* page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("No thanks")).click();
* });
*
* // Write the test as usual.
* page.navigate("https://example.com");
* page.goto("https://example.com");
* page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
* }</pre>
*
* <p> An example that skips the "Confirm your security details" page when it is shown:
* <pre>{@code
* // Setup the handler.
* page.addLocatorHandler(page.getByText("Confirm your security details"), () -> {
* page.addLocatorHandler(page.getByText("Confirm your security details")), () => {
* page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Remind me later")).click();
* });
*
* // Write the test as usual.
* page.navigate("https://example.com");
* page.goto("https://example.com");
* page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
* }</pre>
*
@@ -6103,19 +6106,19 @@ public interface Page extends AutoCloseable {
* handler does not hide the {@code <body>} element.
* <pre>{@code
* // Setup the handler.
* page.addLocatorHandler(page.locator("body"), () -> {
* page.addLocatorHandler(page.locator("body")), () => {
* page.evaluate("window.removeObstructionsForTestIfNeeded()");
* }, new Page.AddLocatorHandlerOptions().setNoWaitAfter(true));
* }, new Page.AddLocatorHandlerOptions.setNoWaitAfter(true));
*
* // Write the test as usual.
* page.navigate("https://example.com");
* page.goto("https://example.com");
* page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
* }</pre>
*
* <p> Handler takes the original locator as an argument. You can also automatically remove the handler after a number of
* invocations by setting {@code times}:
* <pre>{@code
* page.addLocatorHandler(page.getByLabel("Close"), locator -> {
* page.addLocatorHandler(page.getByLabel("Close"), locator => {
* locator.click();
* }, new Page.AddLocatorHandlerOptions().setTimes(1));
* }</pre>
@@ -6169,24 +6172,24 @@ public interface Page extends AutoCloseable {
* <p> An example that closes a "Sign up to the newsletter" dialog when it appears:
* <pre>{@code
* // Setup the handler.
* page.addLocatorHandler(page.getByText("Sign up to the newsletter"), () -> {
* page.addLocatorHandler(page.getByText("Sign up to the newsletter"), () => {
* page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("No thanks")).click();
* });
*
* // Write the test as usual.
* page.navigate("https://example.com");
* page.goto("https://example.com");
* page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
* }</pre>
*
* <p> An example that skips the "Confirm your security details" page when it is shown:
* <pre>{@code
* // Setup the handler.
* page.addLocatorHandler(page.getByText("Confirm your security details"), () -> {
* page.addLocatorHandler(page.getByText("Confirm your security details")), () => {
* page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Remind me later")).click();
* });
*
* // Write the test as usual.
* page.navigate("https://example.com");
* page.goto("https://example.com");
* page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
* }</pre>
*
@@ -6195,19 +6198,19 @@ public interface Page extends AutoCloseable {
* handler does not hide the {@code <body>} element.
* <pre>{@code
* // Setup the handler.
* page.addLocatorHandler(page.locator("body"), () -> {
* page.addLocatorHandler(page.locator("body")), () => {
* page.evaluate("window.removeObstructionsForTestIfNeeded()");
* }, new Page.AddLocatorHandlerOptions().setNoWaitAfter(true));
* }, new Page.AddLocatorHandlerOptions.setNoWaitAfter(true));
*
* // Write the test as usual.
* page.navigate("https://example.com");
* page.goto("https://example.com");
* page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
* }</pre>
*
* <p> Handler takes the original locator as an argument. You can also automatically remove the handler after a number of
* invocations by setting {@code times}:
* <pre>{@code
* page.addLocatorHandler(page.getByLabel("Close"), locator -> {
* page.addLocatorHandler(page.getByLabel("Close"), locator => {
* locator.click();
* }, new Page.AddLocatorHandlerOptions().setTimes(1));
* }</pre>
@@ -6638,8 +6641,8 @@ public interface Page extends AutoCloseable {
* examples.
* <pre>{@code
* page.routeWebSocket("/ws", ws -> {
* ws.onMessage(frame -> {
* if ("request".equals(frame.text()))
* ws.onMessage(message -> {
* if ("request".equals(message))
* ws.send("response");
* });
* });
@@ -6663,8 +6666,8 @@ public interface Page extends AutoCloseable {
* examples.
* <pre>{@code
* page.routeWebSocket("/ws", ws -> {
* ws.onMessage(frame -> {
* if ("request".equals(frame.text()))
* ws.onMessage(message -> {
* if ("request".equals(message))
* ws.send("response");
* });
* });
@@ -6688,8 +6691,8 @@ public interface Page extends AutoCloseable {
* examples.
* <pre>{@code
* page.routeWebSocket("/ws", ws -> {
* ws.onMessage(frame -> {
* if ("request".equals(frame.text()))
* ws.onMessage(message -> {
* if ("request".equals(message))
* ws.send("response");
* });
* });
@@ -7190,7 +7193,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. Pass {@code 0} to disable timeout.
* @param timeout Maximum time in milliseconds
* @since v1.8
*/
void setDefaultTimeout(double timeout);
@@ -363,8 +363,10 @@ public interface Route {
*
* <p> <strong>Details</strong>
*
* <p> The {@code headers} option applies to both the routed request and any redirects it initiates. However, {@code url},
* {@code method}, and {@code postData} only apply to the original request and are not carried over to redirected requests.
* <p> Note that any overrides such as {@code url} or {@code headers} only apply to the request being routed. If this request
* results in a redirect, overrides will not be applied to the new redirected request. If you want to propagate a header
* through redirects, use the combination of {@link com.microsoft.playwright.Route#fetch Route.fetch()} and {@link
* com.microsoft.playwright.Route#fulfill Route.fulfill()} instead.
*
* <p> {@link com.microsoft.playwright.Route#resume Route.resume()} will immediately send the request to the network, other
* matching handlers won't be invoked. Use {@link com.microsoft.playwright.Route#fallback Route.fallback()} If you want
@@ -391,8 +393,10 @@ public interface Route {
*
* <p> <strong>Details</strong>
*
* <p> The {@code headers} option applies to both the routed request and any redirects it initiates. However, {@code url},
* {@code method}, and {@code postData} only apply to the original request and are not carried over to redirected requests.
* <p> Note that any overrides such as {@code url} or {@code headers} only apply to the request being routed. If this request
* results in a redirect, overrides will not be applied to the new redirected request. If you want to propagate a header
* through redirects, use the combination of {@link com.microsoft.playwright.Route#fetch Route.fetch()} and {@link
* com.microsoft.playwright.Route#fulfill Route.fulfill()} instead.
*
* <p> {@link com.microsoft.playwright.Route#resume Route.resume()} will immediately send the request to the network, other
* matching handlers won't be invoked. Use {@link com.microsoft.playwright.Route#fallback Route.fallback()} If you want
@@ -16,7 +16,6 @@
package com.microsoft.playwright;
import com.microsoft.playwright.options.*;
import java.nio.file.Path;
/**
@@ -144,29 +143,6 @@ public interface Tracing {
return this;
}
}
class GroupOptions {
/**
* Specifies a custom location for the group to be shown in the trace viewer. Defaults to the location of the {@link
* com.microsoft.playwright.Tracing#group Tracing.group()} call.
*/
public Location location;
/**
* Specifies a custom location for the group to be shown in the trace viewer. Defaults to the location of the {@link
* com.microsoft.playwright.Tracing#group Tracing.group()} call.
*/
public GroupOptions setLocation(String file) {
return setLocation(new Location(file));
}
/**
* Specifies a custom location for the group to be shown in the trace viewer. Defaults to the location of the {@link
* com.microsoft.playwright.Tracing#group Tracing.group()} call.
*/
public GroupOptions setLocation(Location location) {
this.location = location;
return this;
}
}
class StopOptions {
/**
* Export trace into the file with the given path.
@@ -295,56 +271,6 @@ public interface Tracing {
* @since v1.15
*/
void startChunk(StartChunkOptions options);
/**
* <strong>NOTE:</strong> Use {@code test.step} instead when available.
*
* <p> Creates a new group within the trace, assigning any subsequent API calls to this group, until {@link
* com.microsoft.playwright.Tracing#groupEnd Tracing.groupEnd()} is called. Groups can be nested and will be visible in the
* trace viewer.
*
* <p> <strong>Usage</strong>
* <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.navigate("https://playwright.dev/");
* page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("API")).click();
* page.context().tracing().groupEnd();
* }</pre>
*
* @param name Group name shown in the trace viewer.
* @since v1.49
*/
default void group(String name) {
group(name, null);
}
/**
* <strong>NOTE:</strong> Use {@code test.step} instead when available.
*
* <p> Creates a new group within the trace, assigning any subsequent API calls to this group, until {@link
* com.microsoft.playwright.Tracing#groupEnd Tracing.groupEnd()} is called. Groups can be nested and will be visible in the
* trace viewer.
*
* <p> <strong>Usage</strong>
* <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.navigate("https://playwright.dev/");
* page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("API")).click();
* page.context().tracing().groupEnd();
* }</pre>
*
* @param name Group name shown in the trace viewer.
* @since v1.49
*/
void group(String name, GroupOptions options);
/**
* Closes the last group created by {@link com.microsoft.playwright.Tracing#group Tracing.group()}.
*
* @since v1.49
*/
void groupEnd();
/**
* Stop tracing.
*
@@ -31,8 +31,8 @@ import java.util.function.Consumer;
* WebSocket. Here is an example that responds to a {@code "request"} with a {@code "response"}.
* <pre>{@code
* page.routeWebSocket("wss://example.com/ws", ws -> {
* ws.onMessage(frame -> {
* if ("request".equals(frame.text()))
* ws.onMessage(message -> {
* if ("request".equals(message))
* ws.send("response");
* });
* });
@@ -45,8 +45,8 @@ import java.util.function.Consumer;
* <p> Here is another example that handles JSON messages:
* <pre>{@code
* page.routeWebSocket("wss://example.com/ws", ws -> {
* ws.onMessage(frame -> {
* JsonObject json = new JsonParser().parse(frame.text()).getAsJsonObject();
* ws.onMessage(message -> {
* JsonObject json = new JsonParser().parse(message).getAsJsonObject();
* if ("question".equals(json.get("request").getAsString())) {
* Map<String, String> result = new HashMap();
* result.put("response", "answer");
@@ -67,11 +67,11 @@ import java.util.function.Consumer;
* <pre>{@code
* page.routeWebSocket("/ws", ws -> {
* WebSocketRoute server = ws.connectToServer();
* ws.onMessage(frame -> {
* if ("request".equals(frame.text()))
* ws.onMessage(message -> {
* if ("request".equals(message))
* server.send("request2");
* else
* server.send(frame.text());
* server.send(message);
* });
* });
* }</pre>
@@ -92,13 +92,13 @@ import java.util.function.Consumer;
* <pre>{@code
* page.routeWebSocket("/ws", ws -> {
* WebSocketRoute server = ws.connectToServer();
* ws.onMessage(frame -> {
* if (!"blocked-from-the-page".equals(frame.text()))
* server.send(frame.text());
* ws.onMessage(message -> {
* if (!"blocked-from-the-page".equals(message))
* server.send(message);
* });
* server.onMessage(frame -> {
* if (!"blocked-from-the-server".equals(frame.text()))
* ws.send(frame.text());
* server.onMessage(message -> {
* if (!"blocked-from-the-server".equals(message))
* ws.send(message);
* });
* });
* }</pre>
@@ -21,15 +21,15 @@ package com.microsoft.playwright.assertions;
* The {@code APIResponseAssertions} class provides assertion methods that can be used to make assertions about the {@code
* APIResponse} in the tests.
* <pre>{@code
* // ...
* ...
* import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
*
* public class TestPage {
* // ...
* ...
* @Test
* void navigatesToLoginPage() {
* // ...
* APIResponse response = page.request().get("https://playwright.dev");
* ...
* APIResponse response = page.request().get('https://playwright.dev');
* assertThat(response).isOK();
* }
* }
@@ -23,14 +23,14 @@ import com.microsoft.playwright.options.AriaRole;
* The {@code LocatorAssertions} class provides assertion methods that can be used to make assertions about the {@code
* Locator} state in the tests.
* <pre>{@code
* // ...
* ...
* import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
*
* public class TestLocator {
* // ...
* ...
* @Test
* void statusBecomesSubmitted() {
* // ...
* ...
* page.getByRole(AriaRole.BUTTON).click();
* assertThat(page.locator(".status")).hasText("Submitted");
* }
@@ -58,37 +58,16 @@ 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}.
*/
@@ -302,33 +281,6 @@ 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
@@ -533,20 +485,6 @@ public interface LocatorAssertions {
return this;
}
}
class MatchesAriaSnapshotOptions {
/**
* Time to retry the assertion for in milliseconds. Defaults to {@code 5000}.
*/
public Double timeout;
/**
* Time to retry the assertion for in milliseconds. Defaults to {@code 5000}.
*/
public MatchesAriaSnapshotOptions setTimeout(double timeout) {
this.timeout = timeout;
return this;
}
}
/**
* Makes the assertion check for the opposite condition. For example, this code tests that the Locator doesn't contain text
* {@code "error"}:
@@ -1267,66 +1205,6 @@ 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>.
@@ -1444,18 +1322,16 @@ public interface LocatorAssertions {
*/
void hasAttribute(String name, Pattern value, HasAttributeOptions options);
/**
* 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:
* 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.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
* assertThat(page.locator("#component")).hasClass("middle selected row");
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* }</pre>
*
* <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:
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
* <pre>{@code
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
* }</pre>
@@ -1467,18 +1343,16 @@ public interface LocatorAssertions {
hasClass(expected, null);
}
/**
* 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:
* 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.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
* assertThat(page.locator("#component")).hasClass("middle selected row");
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* }</pre>
*
* <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:
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
* <pre>{@code
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
* }</pre>
@@ -1488,18 +1362,16 @@ public interface LocatorAssertions {
*/
void hasClass(String expected, HasClassOptions options);
/**
* 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:
* 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.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
* assertThat(page.locator("#component")).hasClass("middle selected row");
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* }</pre>
*
* <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:
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
* <pre>{@code
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
* }</pre>
@@ -1511,18 +1383,16 @@ public interface LocatorAssertions {
hasClass(expected, null);
}
/**
* 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:
* 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.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
* assertThat(page.locator("#component")).hasClass("middle selected row");
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* }</pre>
*
* <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:
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
* <pre>{@code
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
* }</pre>
@@ -1532,18 +1402,16 @@ public interface LocatorAssertions {
*/
void hasClass(Pattern expected, HasClassOptions options);
/**
* 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:
* 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.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
* assertThat(page.locator("#component")).hasClass("middle selected row");
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* }</pre>
*
* <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:
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
* <pre>{@code
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
* }</pre>
@@ -1555,18 +1423,16 @@ public interface LocatorAssertions {
hasClass(expected, null);
}
/**
* 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:
* 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.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
* assertThat(page.locator("#component")).hasClass("middle selected row");
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* }</pre>
*
* <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:
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
* <pre>{@code
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
* }</pre>
@@ -1576,18 +1442,16 @@ public interface LocatorAssertions {
*/
void hasClass(String[] expected, HasClassOptions options);
/**
* 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:
* 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.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
* assertThat(page.locator("#component")).hasClass("middle selected row");
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* }</pre>
*
* <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:
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
* <pre>{@code
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
* }</pre>
@@ -1599,18 +1463,16 @@ public interface LocatorAssertions {
hasClass(expected, null);
}
/**
* 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:
* 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.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
* assertThat(page.locator("#component")).hasClass("middle selected row");
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
* assertThat(page.locator("#component")).hasClass("selected row");
* }</pre>
*
* <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:
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
* <pre>{@code
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
* }</pre>
@@ -2235,7 +2097,7 @@ public interface LocatorAssertions {
*
* <p> For example, given the following element:
* <pre>{@code
* page.locator("id=favorite-colors").selectOption(new String[]{"R", "G"});
* page.locator("id=favorite-colors").selectOption(["R", "G"]);
* assertThat(page.locator("id=favorite-colors")).hasValues(new Pattern[] { Pattern.compile("R"), Pattern.compile("G") });
* }</pre>
*
@@ -2253,7 +2115,7 @@ public interface LocatorAssertions {
*
* <p> For example, given the following element:
* <pre>{@code
* page.locator("id=favorite-colors").selectOption(new String[]{"R", "G"});
* page.locator("id=favorite-colors").selectOption(["R", "G"]);
* assertThat(page.locator("id=favorite-colors")).hasValues(new Pattern[] { Pattern.compile("R"), Pattern.compile("G") });
* }</pre>
*
@@ -2269,7 +2131,7 @@ public interface LocatorAssertions {
*
* <p> For example, given the following element:
* <pre>{@code
* page.locator("id=favorite-colors").selectOption(new String[]{"R", "G"});
* page.locator("id=favorite-colors").selectOption(["R", "G"]);
* assertThat(page.locator("id=favorite-colors")).hasValues(new Pattern[] { Pattern.compile("R"), Pattern.compile("G") });
* }</pre>
*
@@ -2287,7 +2149,7 @@ public interface LocatorAssertions {
*
* <p> For example, given the following element:
* <pre>{@code
* page.locator("id=favorite-colors").selectOption(new String[]{"R", "G"});
* page.locator("id=favorite-colors").selectOption(["R", "G"]);
* assertThat(page.locator("id=favorite-colors")).hasValues(new Pattern[] { Pattern.compile("R"), Pattern.compile("G") });
* }</pre>
*
@@ -2295,39 +2157,5 @@ public interface LocatorAssertions {
* @since v1.23
*/
void hasValues(Pattern[] values, HasValuesOptions options);
/**
* Asserts that the target element matches the given <a
* href="https://playwright.dev/java/docs/aria-snapshots">accessibility snapshot</a>.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* page.navigate("https://demo.playwright.dev/todomvc/");
* assertThat(page.locator("body")).matchesAriaSnapshot("""
* - heading "todos"
* - textbox "What needs to be done?"
* """);
* }</pre>
*
* @since v1.49
*/
default void matchesAriaSnapshot(String expected) {
matchesAriaSnapshot(expected, null);
}
/**
* Asserts that the target element matches the given <a
* href="https://playwright.dev/java/docs/aria-snapshots">accessibility snapshot</a>.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* page.navigate("https://demo.playwright.dev/todomvc/");
* assertThat(page.locator("body")).matchesAriaSnapshot("""
* - heading "todos"
* - textbox "What needs to be done?"
* """);
* }</pre>
*
* @since v1.49
*/
void matchesAriaSnapshot(String expected, MatchesAriaSnapshotOptions options);
}
@@ -22,14 +22,14 @@ import java.util.regex.Pattern;
* The {@code PageAssertions} class provides assertion methods that can be used to make assertions about the {@code Page}
* state in the tests.
* <pre>{@code
* // ...
* ...
* import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
*
* public class TestPage {
* // ...
* ...
* @Test
* void navigatesToLoginPage() {
* // ...
* ...
* page.getByText("Sign in").click();
* assertThat(page).hasURL(Pattern.compile(".*\/login"));
* }
@@ -30,13 +30,14 @@ import com.microsoft.playwright.impl.PageAssertionsImpl;
*
* <p> Consider the following example:
* <pre>{@code
* ...
* import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
*
* public class TestExample {
* // ...
* ...
* @Test
* void statusBecomesSubmitted() {
* // ...
* ...
* page.locator("#submit-button").click();
* assertThat(page.locator(".status")).hasText("Submitted");
* }
@@ -47,6 +47,7 @@ class AssertionsBase {
options = new FrameExpectOptions();
}
options.expectedText = expectedText;
options.isNot = isNot;
expectImpl(expression, options, expected, message);
}
@@ -54,14 +55,13 @@ class AssertionsBase {
if (expectOptions.timeout == null) {
expectOptions.timeout = AssertionsTimeout.defaultTimeout;
}
expectOptions.isNot = isNot;
if (isNot) {
if (expectOptions.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 = (result.log == null) ? "" : String.join("\n", result.log);
String log = String.join("\n", result.log);
if (!log.isEmpty()) {
log = "\nCall log:\n" + log;
}
@@ -12,24 +12,18 @@ 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);
sendMessageWithLogging("fastForward", params);
browserContext.sendMessage("clockFastForward", params);
}
@Override
public void fastForward(String ticks) {
JsonObject params = new JsonObject();
params.addProperty("ticksString", ticks);
sendMessageWithLogging("fastForward", params);
browserContext.sendMessage("clockFastForward", params);
}
@Override
@@ -38,89 +32,89 @@ class ClockImpl implements Clock {
if (options != null) {
parseTime(options.time, params);
}
sendMessageWithLogging("install", params);
browserContext.sendMessage("clockInstall", params);
}
@Override
public void runFor(long ticks) {
JsonObject params = new JsonObject();
params.addProperty("ticksNumber", ticks);
sendMessageWithLogging("runFor", params);
browserContext.sendMessage("clockRunFor", params);
}
@Override
public void runFor(String ticks) {
JsonObject params = new JsonObject();
params.addProperty("ticksString", ticks);
sendMessageWithLogging("runFor", params);
browserContext.sendMessage("clockRunFor", params);
}
@Override
public void pauseAt(long time) {
JsonObject params = new JsonObject();
params.addProperty("timeNumber", time);
sendMessageWithLogging("pauseAt", params);
browserContext.sendMessage("clockPauseAt", params);
}
@Override
public void pauseAt(String time) {
JsonObject params = new JsonObject();
params.addProperty("timeString", time);
sendMessageWithLogging("pauseAt", params);
browserContext.sendMessage("clockPauseAt", params);
}
@Override
public void pauseAt(Date time) {
JsonObject params = new JsonObject();
params.addProperty("timeNumber", time.getTime());
sendMessageWithLogging("pauseAt", params);
browserContext.sendMessage("clockPauseAt", params);
}
@Override
public void resume() {
sendMessageWithLogging("resume", new JsonObject());
browserContext.sendMessage("clockResume");
}
@Override
public void setFixedTime(long time) {
JsonObject params = new JsonObject();
params.addProperty("timeNumber", time);
sendMessageWithLogging("setFixedTime", params);
browserContext.sendMessage("clockSetFixedTime", params);
}
@Override
public void setFixedTime(String time) {
JsonObject params = new JsonObject();
params.addProperty("timeString", time);
sendMessageWithLogging("setFixedTime", params);
browserContext.sendMessage("clockSetFixedTime", params);
}
@Override
public void setFixedTime(Date time) {
JsonObject params = new JsonObject();
params.addProperty("timeNumber", time.getTime());
sendMessageWithLogging("setFixedTime", params);
browserContext.sendMessage("clockSetFixedTime", params);
}
@Override
public void setSystemTime(long time) {
JsonObject params = new JsonObject();
params.addProperty("timeNumber", time);
sendMessageWithLogging("setSystemTime", params);
browserContext.sendMessage("clockSetSystemTime", params);
}
@Override
public void setSystemTime(String time) {
JsonObject params = new JsonObject();
params.addProperty("timeString", time);
sendMessageWithLogging("setSystemTime", params);
browserContext.sendMessage("clockSetSystemTime", params);
}
@Override
public void setSystemTime(Date time) {
JsonObject params = new JsonObject();
params.addProperty("timeNumber", time.getTime());
sendMessageWithLogging("setSystemTime", params);
browserContext.sendMessage("clockSetSystemTime", params);
}
private static void parseTime(Object time, JsonObject params) {
@@ -22,9 +22,7 @@ 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;
@@ -90,7 +88,6 @@ 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));
}
@@ -98,33 +95,14 @@ 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));
}
@@ -132,7 +110,6 @@ 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));
}
@@ -349,42 +326,12 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
expectImpl("to.have.values", list, patterns, "Locator expected to have values matching regex", convertType(options, FrameExpectOptions.class));
}
@Override
public void matchesAriaSnapshot(String expected, MatchesAriaSnapshotOptions snapshotOptions) {
if (snapshotOptions == null) {
snapshotOptions = new MatchesAriaSnapshotOptions();
}
FrameExpectOptions options = convertType(snapshotOptions, FrameExpectOptions.class);
options.expectedValue = serializeArgument(expected);
expectImpl("to.match.aria", options, expected,"Locator expected to match Aria snapshot");
}
@Override
public void isChecked(IsCheckedOptions options) {
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);
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));
}
@Override
@@ -119,21 +119,6 @@ class LocatorImpl implements Locator {
return new LocatorImpl(frame, selector + " >> internal:and=" + gson().toJson(other.selector), null);
}
@Override
public String ariaSnapshot(AriaSnapshotOptions options) {
return frame.withLogging("Locator.ariaSnapshot", () -> ariaSnapshotImpl(options));
}
private String ariaSnapshotImpl(AriaSnapshotOptions options) {
if (options == null) {
options = new AriaSnapshotOptions();
}
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
params.addProperty("selector", selector);
JsonObject result = frame.sendMessage("ariaSnapshot", params).getAsJsonObject();
return result.get("snapshot").getAsString();
}
@Override
public void blur(BlurOptions options) {
frame.withLogging("Locator.blur", () -> blurImpl(options));
@@ -665,6 +650,9 @@ class LocatorImpl implements Locator {
}
private FrameExpectResult expectImpl(String expression, FrameExpectOptions options) {
if (options == null) {
options = new FrameExpectOptions();
}
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
params.addProperty("selector", selector);
params.addProperty("expression", expression);
@@ -53,7 +53,6 @@ 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,7 +40,6 @@ 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);
@@ -86,25 +86,6 @@ class TracingImpl extends ChannelOwner implements Tracing {
tracingStartChunk(options.name, options.title);
}
@Override
public void group(String name, GroupOptions options) {
withLogging("Tracing.group", () -> groupImpl(name, options));
}
private void groupImpl(String name, GroupOptions options) {
if (options == null) {
options = new GroupOptions();
}
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
params.addProperty("name", name);
sendMessage("tracingGroup", params);
}
@Override
public void groupEnd() {
withLogging("Tracing.groupEnd", () -> sendMessage("tracingGroupEnd"));
}
private void tracingStartChunk(String name, String title) {
JsonObject params = new JsonObject();
if (name != null) {
@@ -16,12 +16,13 @@
package com.microsoft.playwright.impl;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.microsoft.playwright.PlaywrightException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.regex.Pattern;
@@ -29,14 +30,20 @@ import static com.microsoft.playwright.impl.Utils.globToRegex;
import static com.microsoft.playwright.impl.Utils.toJsRegexFlags;
class UrlMatcher {
private final String baseURL;
public final String glob;
public final Pattern pattern;
public final Predicate<String> predicate;
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);
}
static UrlMatcher forOneOf(URL baseUrl, Object object) {
if (object == null) {
return new UrlMatcher(null, null, null, null);
return UrlMatcher.any();
}
if (object instanceof String) {
return new UrlMatcher(baseUrl, (String) object);
@@ -51,77 +58,34 @@ 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 {
// 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 new URL(baseUrl, spec).toString();
} catch (MalformedURLException e) {
return spec;
}
}
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(URL base, String url) {
this(url, toPredicate(Pattern.compile(globToRegex(resolveUrl(base, url)))).or(s -> url == null || url.equals(s)));
}
UrlMatcher(Pattern pattern) {
this(null, null, pattern, null);
this(pattern, toPredicate(pattern));
}
UrlMatcher(Predicate<String> predicate) {
this(null, null, null, predicate);
this(predicate, 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;
private UrlMatcher(Object rawSource, Predicate<String> predicate) {
this.rawSource = rawSource;
this.predicate = predicate;
}
boolean test(String 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;
return predicate == null || predicate.test(value);
}
@Override
@@ -129,38 +93,25 @@ class UrlMatcher {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UrlMatcher that = (UrlMatcher) o;
if (pattern != null) {
return that.pattern != null && pattern.pattern().equals(that.pattern.pattern()) && pattern.flags() == that.pattern.flags();
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 (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;
return Objects.equals(rawSource, that.rawSource);
}
@Override
public int hashCode() {
if (pattern != null) {
return pattern.hashCode();
}
if (predicate != null) {
return predicate.hashCode();
}
if (glob != null) {
return glob.hashCode();
}
return 0;
return Objects.hash(rawSource);
}
@Override
public String 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);
if (rawSource == null)
return "<any>";
if (rawSource instanceof Predicate)
return "matching predicate";
return rawSource.toString();
}
}
@@ -462,11 +462,13 @@ public class Utils {
JsonArray jsonPatterns = new JsonArray();
for (UrlMatcher matcher: matchers) {
JsonObject jsonPattern = new JsonObject();
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));
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));
} else {
// Match all requests.
jsonPattern.addProperty("glob", "**/*");
@@ -20,7 +20,6 @@ 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;
@@ -28,12 +27,8 @@ class WaitableRace<T> implements Waitable<T> {
@Override
public boolean isDone() {
if (firstReady != null) {
return true;
}
for (Waitable<T> w : waitables) {
for (Waitable w : waitables) {
if (w.isDone()) {
firstReady = w;
return true;
}
}
@@ -42,11 +37,14 @@ class WaitableRace<T> implements Waitable<T> {
@Override
public T get() {
try {
return firstReady.get();
} finally {
dispose();
assert isDone();
dispose();
for (Waitable<T> w : waitables) {
if (w.isDone()) {
return w.get();
}
}
throw new IllegalStateException("At least one element must be ready");
}
@Override
@@ -69,7 +69,6 @@ class WebSocketRouteImpl extends ChannelOwner implements WebSocketRoute {
WebSocketRouteImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
super(parent, type, guid, initializer);
markAsInternalType();
}
@Override
@@ -23,7 +23,7 @@ import java.nio.file.Path;
* The {@code FormData} is used create form data that is sent via {@code APIRequestContext}.
* <pre>{@code
* import com.microsoft.playwright.options.FormData;
* // ...
* ...
* FormData form = FormData.create()
* .set("firstName", "John")
* .set("lastName", "Doe")
@@ -43,7 +43,7 @@ public interface FormData {
* existing set of values.
* <pre>{@code
* import com.microsoft.playwright.options.FormData;
* // ...
* ...
* FormData form = FormData.create()
* // Only name and value are set.
* .append("firstName", "John")
@@ -70,7 +70,7 @@ public interface FormData {
* existing set of values.
* <pre>{@code
* import com.microsoft.playwright.options.FormData;
* // ...
* ...
* FormData form = FormData.create()
* // Only name and value are set.
* .append("firstName", "John")
@@ -97,7 +97,7 @@ public interface FormData {
* existing set of values.
* <pre>{@code
* import com.microsoft.playwright.options.FormData;
* // ...
* ...
* FormData form = FormData.create()
* // Only name and value are set.
* .append("firstName", "John")
@@ -124,7 +124,7 @@ public interface FormData {
* existing set of values.
* <pre>{@code
* import com.microsoft.playwright.options.FormData;
* // ...
* ...
* FormData form = FormData.create()
* // Only name and value are set.
* .append("firstName", "John")
@@ -151,7 +151,7 @@ public interface FormData {
* existing set of values.
* <pre>{@code
* import com.microsoft.playwright.options.FormData;
* // ...
* ...
* FormData form = FormData.create()
* // Only name and value are set.
* .append("firstName", "John")
@@ -179,7 +179,7 @@ public interface FormData {
* Sets a field on the form. File values can be passed either as {@code Path} or as {@code FilePayload}.
* <pre>{@code
* import com.microsoft.playwright.options.FormData;
* // ...
* ...
* FormData form = FormData.create()
* // Only name and value are set.
* .set("firstName", "John")
@@ -200,7 +200,7 @@ public interface FormData {
* Sets a field on the form. File values can be passed either as {@code Path} or as {@code FilePayload}.
* <pre>{@code
* import com.microsoft.playwright.options.FormData;
* // ...
* ...
* FormData form = FormData.create()
* // Only name and value are set.
* .set("firstName", "John")
@@ -221,7 +221,7 @@ public interface FormData {
* Sets a field on the form. File values can be passed either as {@code Path} or as {@code FilePayload}.
* <pre>{@code
* import com.microsoft.playwright.options.FormData;
* // ...
* ...
* FormData form = FormData.create()
* // Only name and value are set.
* .set("firstName", "John")
@@ -242,7 +242,7 @@ public interface FormData {
* Sets a field on the form. File values can be passed either as {@code Path} or as {@code FilePayload}.
* <pre>{@code
* import com.microsoft.playwright.options.FormData;
* // ...
* ...
* FormData form = FormData.create()
* // Only name and value are set.
* .set("firstName", "John")
@@ -263,7 +263,7 @@ public interface FormData {
* Sets a field on the form. File values can be passed either as {@code Path} or as {@code FilePayload}.
* <pre>{@code
* import com.microsoft.playwright.options.FormData;
* // ...
* ...
* FormData form = FormData.create()
* // Only name and value are set.
* .set("firstName", "John")
@@ -1,35 +0,0 @@
/*
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.playwright.options;
public class Location {
public String file;
public Integer line;
public Integer column;
public Location(String file) {
this.file = file;
}
public Location setLine(int line) {
this.line = line;
return this;
}
public Location setColumn(int column) {
this.column = column;
return this;
}
}
@@ -299,13 +299,6 @@ 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();
@@ -16,7 +16,6 @@
package com.microsoft.playwright;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.junit.jupiter.api.Test;
@@ -50,12 +49,7 @@ public class TestBrowserContextCDPSession extends TestBase {
cdpSession.send("Network.enable");
List<JsonElement> events = new ArrayList<>();
cdpSession.on("Network.requestWillBeSent", (JsonObject jsonObject) -> {
// Only register main request, ignore favicon requests.
if ("Document".equals(jsonObject.get("type").getAsString())) {
events.add(jsonObject);
}
});
cdpSession.on("Network.requestWillBeSent", events::add);
page.navigate(server.EMPTY_PAGE);
assertEquals(1, events.size());
@@ -132,7 +126,7 @@ public class TestBrowserContextCDPSession extends TestBase {
page.close();
PlaywrightException exception = assertThrows(PlaywrightException.class, session::detach);
assertTrue(exception.getMessage().contains("Target page, context or browser has been closed"), exception.getMessage());
assertTrue(exception.getMessage().contains("Target page, context or browser has been closed"));
context.close();
}
@@ -142,14 +136,8 @@ public class TestBrowserContextCDPSession extends TestBase {
cdpSession.send("Network.enable");
List<JsonObject> events = new ArrayList<>();
Consumer<JsonObject> listener1 = (JsonObject jsonObject) -> {
// Only register main request, ignore favicon requests.
if ("Document".equals(jsonObject.get("type").getAsString())) {
events.add(jsonObject);
}
};
cdpSession.on("Network.requestWillBeSent", listener1);
cdpSession.on("Network.requestWillBeSent", listener1);
cdpSession.on("Network.requestWillBeSent", events::add);
cdpSession.on("Network.requestWillBeSent", events::add);
page.navigate(server.EMPTY_PAGE);
assertEquals(2, events.size());
@@ -161,15 +149,9 @@ public class TestBrowserContextCDPSession extends TestBase {
cdpSession.send("Network.enable");
List<JsonObject> events = new ArrayList<>();
Consumer<JsonObject> listener1 = (JsonObject jsonObject) -> {
// Only register main request, ignore favicon requests.
if ("Document".equals(jsonObject.get("type").getAsString())) {
events.add(jsonObject);
}
};
Consumer<JsonObject> listener2 = listener1::accept;
Consumer<JsonObject> listener1 = events::add;
cdpSession.on("Network.requestWillBeSent", listener1);
cdpSession.on("Network.requestWillBeSent", listener2);
cdpSession.on("Network.requestWillBeSent", events::add);
page.navigate(server.EMPTY_PAGE);
assertEquals(2, events.size());
@@ -178,6 +160,6 @@ public class TestBrowserContextCDPSession extends TestBase {
events.clear();
page.navigate(server.EMPTY_PAGE);
assertEquals(1, events.size(), new Gson().toJson(events));
assertEquals(1, events.size());
}
}
@@ -25,15 +25,13 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestBrowserContextCredentials extends TestBase {
static boolean isChromiumHeadedLike() {
// --headless=new, the default in all Chromium channels, is like headless.
return isChromium() && (isHeadful() || getBrowserChannelFromEnv() != null);
static boolean isChromiumHeadful() {
return isChromium() && isHeadful();
}
@Test
@DisabledIf(value="isChromiumHeadedLike", disabledReason="fail")
@DisabledIf(value="isChromiumHeadful", disabledReason="fail")
void shouldFailWithoutCredentials() {
System.out.println("channel2 " + getBrowserChannelFromEnv());
server.setAuth("/empty.html", "user", "pass");
Response response = page.navigate(server.EMPTY_PAGE);
assertEquals(401, response.status());
@@ -105,7 +103,6 @@ public class TestBrowserContextCredentials extends TestBase {
}
@Test
@DisabledIf(value="isChromiumHeadedLike", disabledReason="fail")
void shouldFailWithCorrectCredentialsAndWrongOriginScheme() {
server.setAuth("/empty.html", "user", "pass");
final HttpCredentials httpCredentials = new HttpCredentials("user", "pass");
@@ -118,7 +115,6 @@ public class TestBrowserContextCredentials extends TestBase {
}
@Test
@DisabledIf(value="isChromiumHeadedLike", disabledReason="fail")
void shouldFailWithCorrectCredentialsAndWrongOriginHostname() {
server.setAuth("/empty.html", "user", "pass");
final HttpCredentials httpCredentials = new HttpCredentials("user", "pass");
@@ -131,7 +127,6 @@ public class TestBrowserContextCredentials extends TestBase {
}
@Test
@DisabledIf(value="isChromiumHeadedLike", disabledReason="fail")
void shouldFailWithCorrectCredentialsAndWrongOriginPort() {
server.setAuth("/empty.html", "user", "pass");
final HttpCredentials httpCredentials = new HttpCredentials("user", "pass");
@@ -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));
});
assertEquals("checked", e.getExpected().getStringRepresentation());
assertEquals("unchecked", e.getActual().getStringRepresentation());
assertTrue(e.getMessage().contains("Locator expected to be: checked"), e.getMessage());
assertNull(e.getExpected());
assertNull(e.getActual());
assertTrue(e.getMessage().contains("Locator expected to be checked"), e.getMessage());
}
@Test
@@ -669,10 +669,9 @@ public class TestLocatorAssertions extends TestBase {
AssertionFailedError e = assertThrows(AssertionFailedError.class, () -> {
assertThat(locator).not().isChecked(new LocatorAssertions.IsCheckedOptions().setTimeout(1000));
});
assertEquals("checked", e.getExpected().getStringRepresentation());
assertEquals("checked", e.getActual().getStringRepresentation());
assertTrue(e.getMessage().contains("Locator expected not to be: checked"), e.getMessage());
assertNull(e.getExpected());
assertNull(e.getActual());
assertTrue(e.getMessage().contains("Locator expected not to be checked"), e.getMessage());
}
@Test
@@ -688,7 +687,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
@@ -790,14 +789,6 @@ 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,9 +129,6 @@ 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
@@ -144,10 +141,6 @@ 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
@@ -157,67 +150,4 @@ 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());
}
}
@@ -1,86 +0,0 @@
package com.microsoft.playwright;
import com.microsoft.playwright.junit.FixtureTest;
import com.microsoft.playwright.junit.UsePlaywright;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
@FixtureTest
@UsePlaywright
public class TestPageAriaSnapshot {
public static String unshift(String snapshot) {
List<String> lines = Arrays.asList(snapshot.split("\n"));
int whitespacePrefixLength = 100;
Pattern pattern = Pattern.compile("^(\\s*).*");
for (String line : lines) {
if (line.trim().isEmpty())
continue;
Matcher matcher = pattern.matcher(line);
if (!matcher.matches()) {
continue;
}
String match = matcher.group(1);
if (match.length() < whitespacePrefixLength) {
whitespacePrefixLength = match.length();
}
}
final int prefixLength = whitespacePrefixLength;
return lines.stream()
.filter(line -> !line.trim().isEmpty())
.map(line -> line.substring(prefixLength))
.collect(Collectors.joining("\n"));
}
private static void checkAndMatchSnapshot(Locator locator, String snapshot) {
assertEquals(unshift(snapshot), locator.ariaSnapshot());
assertThat(locator).matchesAriaSnapshot(snapshot);
}
@Test
void shouldSnapshot(Page page) {
page.setContent("<h1>title</h1>");
checkAndMatchSnapshot(page.locator("body"), "- heading \"title\" [level=1]");
}
@Test
void shouldSnapshotList(Page page) {
page.setContent("<h1>title</h1><h1>title 2</h1>");
checkAndMatchSnapshot(page.locator("body"), "" +
" - heading \"title\" [level=1]\n" +
" - heading \"title 2\" [level=1]");
}
@Test
void shouldSnapshotListWithAccessibleName(Page page) {
page.setContent("<ul aria-label=\"my list\"><li>one</li><li>two</li></ul>");
checkAndMatchSnapshot(page.locator("body"), "- list \"my list\":\n - listitem: one\n - listitem: two");
}
@Test
void shouldSnapshotComplex(Page page) {
page.setContent("<ul><li><a href='about:blank'>link</a></li></ul>");
checkAndMatchSnapshot(page.locator("body"), "- list:\n - listitem:\n - link \"link\"");
}
@Test
void shouldAllowTextNodes(Page page) {
page.setContent("<h1>Microsoft</h1><div>Open source projects and samples from Microsoft</div>");
checkAndMatchSnapshot(page.locator("body"), "" +
" - heading \"Microsoft\" [level=1]\n" +
" - text: Open source projects and samples from Microsoft");
}
@Test
void shouldSnapshotDetailsVisibility(Page page) {
page.setContent("<details><summary>Summary</summary><div>Details</div></details>");
checkAndMatchSnapshot(page.locator("body"), "- group: Summary");
}
}
@@ -290,9 +290,8 @@ public class TestPageClock {
Page popup = page.waitForPopup(() -> {
page.evaluate("url => window.open(url)", server.PREFIX + "/popup.html");
});
popup.waitForURL(server.PREFIX + "/popup.html");
Double popupTime = (Double) popup.evaluate("time");
assertTrue(popupTime >= 2000, "popupTime = " + popupTime);
assertTrue(popupTime >= 2000);
}
@Test
@@ -311,7 +310,6 @@ public class TestPageClock {
Page popup = page.waitForPopup(() -> {
page.evaluate("url => window.open(url)", server.PREFIX + "/popup.html");
});
popup.waitForURL(server.PREFIX + "/popup.html");
Object popupTime = popup.evaluate("time");
assertEquals(1000, popupTime);
}
@@ -97,18 +97,6 @@ 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(@TempDir Path tmpDir) throws IOException {
void shouldUploadAFolderAndThrowForMultipleDirectories() throws IOException {
page.navigate(server.PREFIX + "/input/folderupload.html");
Locator input = page.locator("input[name=\"file1\"]");
Path dir = tmpDir.resolve("file-upload-test");
Path dir = Paths.get("file-upload-test"); // Adjust path as necessary
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(@TempDir Path tmpDir) throws IOException {
void shouldThrowIfADirectoryAndFilesArePassed() 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 = tmpDir.resolve("file-upload-test");
Path dir = Paths.get("file-upload-test"); // Adjust path as necessary
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(@TempDir Path tmpDir) throws IOException {
void shouldThrowWhenUploadingAFolderInANormalFileUploadInput() throws IOException {
page.navigate(server.PREFIX + "/input/fileupload.html");
Locator input = page.locator("input[name=\"file1\"]");
Path dir = tmpDir.resolve("file-upload-test");
Path dir = Paths.get("file-upload-test"); // Adjust path as necessary
Files.createDirectories(dir);
writeFile(dir.resolve("file1.txt"), "file1 content");
PlaywrightException e = assertThrows(PlaywrightException.class,
@@ -30,6 +30,7 @@ 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));
@@ -39,6 +40,7 @@ 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));
@@ -49,6 +51,7 @@ 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,8 +9,6 @@ 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;
@@ -319,76 +317,4 @@ 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"));
}
}
@@ -16,12 +16,6 @@
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;
@@ -32,14 +26,12 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Arrays.asList;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestTracing extends TestBase {
@Override
@@ -162,151 +154,4 @@ public class TestTracing extends TestBase {
}
}
@Test
void canCallTracingGroupGroupEndAtAnyTimeAndAutoClose(@TempDir Path tempDir) throws Exception {
context.tracing().group("ignored");
context.tracing().groupEnd();
context.tracing().group("ignored2");
context.tracing().start(new Tracing.StartOptions());
context.tracing().group("actual");
page.navigate(server.EMPTY_PAGE);
Path traceFile1 = tempDir.resolve("trace1.zip");
context.tracing().stopChunk(new Tracing.StopChunkOptions().setPath(traceFile1));
context.tracing().group("ignored3");
context.tracing().groupEnd();
context.tracing().groupEnd();
context.tracing().groupEnd();
List<TraceEvent> events = parseTraceEvents(traceFile1);
List<TraceEvent> groups = events.stream().filter(e -> "tracingGroup".equals(e.method)).collect(Collectors.toList());
assertEquals(1, groups.size());
assertEquals("actual", groups.get(0).apiName);
}
@Test
void traceGroupGroupEnd(@TempDir Path tempDir) throws Exception {
context.tracing().start(new Tracing.StartOptions());
context.tracing().group("outer group");
page.navigate("data:text/html,<!DOCTYPE html><body><div>Hello world</div></body>");
context.tracing().group("inner group 1", new Tracing.GroupOptions().setLocation(new Location("foo.java").setLine(17).setColumn(1)));
page.locator("body").click();
context.tracing().groupEnd();
context.tracing().group("inner group 2");
assertTrue(page.locator("text=Hello").isVisible());
context.tracing().groupEnd();
context.tracing().groupEnd();
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("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;
String apiName;
String method;
Double startTime;
Double endTime;
String callId;
}
private static List<TraceEvent> parseTraceEvents(Path traceFile) throws IOException {
Map<String, byte[]> files = Utils.parseZip(traceFile);
Map<String, byte[]> traces = files.entrySet().stream().filter(e -> e.getKey().endsWith(".trace")).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
assertNotNull(traces.get("trace.trace"));
return Arrays.stream(new String(traces.get("trace.trace"), UTF_8)
.split("\n"))
.map(s -> new Gson().fromJson(s, TraceEvent.class))
.collect(Collectors.toList());
}
}
+6 -6
View File
@@ -6,7 +6,7 @@
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.50.0</version>
<version>1.48.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.12.1</gson.version>
<junit.version>5.11.4</junit.version>
<gson.version>2.11.0</gson.version>
<junit.version>5.11.1</junit.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<websocket.version>1.6.0</websocket.version>
<websocket.version>1.5.7</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.11.2</version>
<version>3.10.1</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.2</version>
<version>3.5.0</version>
<configuration>
<properties>
<configurationParameters>
+1 -1
View File
@@ -1 +1 @@
1.50.1-beta-1738592302000
1.48.1
+1 -1
View File
@@ -6,7 +6,7 @@
<groupId>com.microsoft.playwright</groupId>
<artifactId>api-generator</artifactId>
<version>1.50.0</version>
<version>1.48.0</version>
<name>Playwright - API Generator</name>
<description>
This is an internal module used to generate Java API from the upstream Playwright
@@ -986,7 +986,7 @@ class Interface extends TypeDefinition {
if (methods.stream().anyMatch(m -> "create".equals(m.jsonName))) {
output.add("import com.microsoft.playwright.impl." + jsonName + "Impl;");
}
if (asList("Page", "Request", "Response", "APIRequestContext", "APIRequest", "APIResponse", "FileChooser", "Frame", "FrameLocator", "ElementHandle", "Locator", "Browser", "BrowserContext", "BrowserType", "Mouse", "Keyboard", "Tracing").contains(jsonName)) {
if (asList("Page", "Request", "Response", "APIRequestContext", "APIRequest", "APIResponse", "FileChooser", "Frame", "FrameLocator", "ElementHandle", "Locator", "Browser", "BrowserContext", "BrowserType", "Mouse", "Keyboard").contains(jsonName)) {
output.add("import com.microsoft.playwright.options.*;");
}
if ("Download".equals(jsonName)) {
+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</version>
<version>1.48.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</version>
<version>1.48.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</version>
<version>1.48.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</version>
<version>1.48.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</version>
<version>1.48.0</version>
<name>Playwright - Update Version in Documentation</name>
<description>
This is an internal module used to update versions in the documentation based on
+53
View File
@@ -0,0 +1,53 @@
FROM ubuntu:focal
ARG DEBIAN_FRONTEND=noninteractive
ARG TZ=America/Los_Angeles
ARG DOCKER_IMAGE_NAME_TEMPLATE="mcr.microsoft.com/playwright/java:v%version%-focal"
# === INSTALL JDK and Maven ===
RUN apt-get update && \
apt-get install -y --no-install-recommends openjdk-21-jdk \
# Install utilities required for downloading browsers
wget \
# Install utilities required for downloading driver
unzip \
# For the MSEdge install script
gpg && \
rm -rf /var/lib/apt/lists/* && \
# Create the pwuser
adduser pwuser
# Ubuntu 22.04 and earlier come with Maven 3.6.3 which fails with
# Java 21, so we install latest Maven from Apache instead.
RUN VERSION=3.9.6 && \
wget -O - https://archive.apache.org/dist/maven/maven-3/$VERSION/binaries/apache-maven-$VERSION-bin.tar.gz | tar zxfv - -C /opt/ && \
ln -s /opt/apache-maven-$VERSION/bin/mvn /usr/local/bin/
ARG PW_TARGET_ARCH
ENV JAVA_HOME=/usr/lib/jvm/java-21-openjdk-${PW_TARGET_ARCH}
# === BAKE BROWSERS INTO IMAGE ===
# Browsers will remain downloaded in `/ms-playwright`.
# Note: make sure to set 777 to the registry so that any user can access
# registry.
ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright
RUN mkdir /ms-playwright && \
mkdir /tmp/pw-java
COPY . /tmp/pw-java
RUN cd /tmp/pw-java && \
./scripts/download_driver.sh && \
mvn install -D skipTests --no-transfer-progress && \
DEBIAN_FRONTEND=noninteractive mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI \
-D exec.args="install-deps" -f playwright/pom.xml --no-transfer-progress && \
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI \
-D exec.args="install" -f playwright/pom.xml --no-transfer-progress && \
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI \
-D exec.args="mark-docker-image '${DOCKER_IMAGE_NAME_TEMPLATE}'" -f playwright/pom.xml --no-transfer-progress && \
rm -rf /tmp/pw-java && \
chmod -R 777 $PLAYWRIGHT_BROWSERS_PATH
+3 -3
View File
@@ -3,12 +3,12 @@ set -e
set +x
if [[ ($1 == '--help') || ($1 == '-h') || ($1 == '') || ($2 == '') ]]; then
echo "usage: $(basename $0) {--arm64,--amd64} {jammy,noble} playwright:localbuild-noble"
echo "usage: $(basename $0) {--arm64,--amd64} {focal,jammy} playwright:localbuild-focal"
echo
echo "Build Playwright docker image and tag it as 'playwright:localbuild-noble'."
echo "Build Playwright docker image and tag it as 'playwright:localbuild-focal'."
echo "Once image is built, you can run it with"
echo ""
echo " docker run --rm -it playwright:localbuild-noble /bin/bash"
echo " docker run --rm -it playwright:localbuild-focal /bin/bash"
echo ""
echo "NOTE: this requires on Playwright PIP dependencies to be installed"
echo ""
+17 -4
View File
@@ -27,6 +27,11 @@ else
exit 1
fi
# Ubuntu 20.04
FOCAL_TAGS=(
"v${PW_VERSION}-focal"
)
# Ubuntu 22.04
JAMMY_TAGS=(
"v${PW_VERSION}-jammy"
@@ -70,12 +75,14 @@ install_oras_if_needed() {
publish_docker_images_with_arch_suffix() {
local FLAVOR="$1"
local TAGS=()
if [[ "$FLAVOR" == "jammy" ]]; then
if [[ "$FLAVOR" == "focal" ]]; then
TAGS=("${FOCAL_TAGS[@]}")
elif [[ "$FLAVOR" == "jammy" ]]; then
TAGS=("${JAMMY_TAGS[@]}")
elif [[ "$FLAVOR" == "noble" ]]; then
TAGS=("${NOBLE_TAGS[@]}")
else
echo "ERROR: unknown flavor - $FLAVOR. Must be either 'jammy', or 'noble'"
echo "ERROR: unknown flavor - $FLAVOR. Must be either 'focal', 'jammy', or 'noble'"
exit 1
fi
local ARCH="$2"
@@ -96,12 +103,14 @@ publish_docker_images_with_arch_suffix() {
publish_docker_manifest () {
local FLAVOR="$1"
local TAGS=()
if [[ "$FLAVOR" == "jammy" ]]; then
if [[ "$FLAVOR" == "focal" ]]; then
TAGS=("${FOCAL_TAGS[@]}")
elif [[ "$FLAVOR" == "jammy" ]]; then
TAGS=("${JAMMY_TAGS[@]}")
elif [[ "$FLAVOR" == "noble" ]]; then
TAGS=("${NOBLE_TAGS[@]}")
else
echo "ERROR: unknown flavor - $FLAVOR. Must be either 'jammy', 'noble'"
echo "ERROR: unknown flavor - $FLAVOR. Must be either 'focal', 'jammy', 'noble'"
exit 1
fi
@@ -120,6 +129,10 @@ publish_docker_manifest () {
done
}
publish_docker_images_with_arch_suffix focal amd64
publish_docker_images_with_arch_suffix focal arm64
publish_docker_manifest focal amd64 arm64
publish_docker_images_with_arch_suffix jammy amd64
publish_docker_images_with_arch_suffix jammy arm64
publish_docker_manifest jammy amd64 arm64