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

Compare commits

..

1 Commits

Author SHA1 Message Date
Yury Semikhatsky c433c1d6e5 chore: mark 1.59.0 (#1909) 2026-04-09 14:14:15 -07:00
128 changed files with 612 additions and 4316 deletions
+1 -1
View File
@@ -75,7 +75,7 @@ extends:
targetPath: $(Build.ArtifactStagingDirectory)/esrp-build
steps:
- checkout: none
- task: EsrpRelease@11
- task: EsrpRelease@9
inputs:
connectedservicename: 'Playwright-ESRP-PME'
usemanagedidentity: true
@@ -1,76 +0,0 @@
---
name: playwright-java-release
description: Prepare a Playwright Java release after the rolling PR has merged — cut the release branch, mark the Maven version, draft the GitHub release, and tick the Java boxes in the internal checklist.
---
Use this skill once the `chore: roll driver to 1.X.0` PR has merged into `main` and the upstream JS `v1.X.0` is published. The rolling work itself is covered by the [[playwright-roll]] skill.
Throughout this doc, replace `X` with the minor version (e.g. `60` for `1.60.0`) and `<user>` with the fork owner (`gh api user --jq .login`).
The full release checklist lives in the private `microsoft/playwright-internal` repo as the `v1.X checklist` issue. Find its number once:
```bash
unset GITHUB_TOKEN
ISSUE=$(gh search issues --repo microsoft/playwright-internal "v1.X checklist" --json number --jq '.[0].number')
```
Tick each Java box incrementally (one PATCH per item) so the issue reflects accurate state if the flow is interrupted:
```bash
gh api repos/microsoft/playwright-internal/issues/$ISSUE --jq '.body' > /tmp/body.md
# edit /tmp/body.md to flip "- [ ]" → "- [x]" on the relevant Java item
gh api repos/microsoft/playwright-internal/issues/$ISSUE -X PATCH --field body=@/tmp/body.md
```
## 1. Cut the release branch
Push `release-1.X` from current `upstream/main` (which now contains the merged roll commit):
```bash
git fetch upstream main
git push upstream upstream/main:refs/heads/release-1.X
```
## 2. Draft the GitHub release
Generate the release notes from the upstream docs:
```bash
cd ~/playwright
node utils/render_release_notes.mjs java 1.X > /tmp/v1.X.0-release-notes.md
```
The renderer leaves JS-isms that need fixing for Java. Apply these substitutions — the list is not exhaustive, eyeball the diff before publishing:
- `toMatchAriaSnapshot()``matchesAriaSnapshot()`
- `toHaveCSS()``hasCSS()` (and other `toHaveX` matchers → `hasX`)
- `browser.on('context')``browser.onContext()`
- `browserContext.on('download' | 'frameattached' | ...)``browserContext.onDownload()` / `onFrameAttached()` / …
Create the draft directly against `release-1.X` — drafting against `main` and retargeting later is fragile because every `gh release edit` rotates the `untagged-<hash>` ID:
```bash
gh release create v1.X.0 --repo microsoft/playwright-java --draft \
--title "v1.X.0" --notes-file /tmp/v1.X.0-release-notes.md --target release-1.X
```
## 3. Bump the Maven version on the release branch
Cut `mark-v-1.X.0` off `upstream/release-1.X`, run `set_maven_version.sh`, and PR back to the release branch:
```bash
git checkout -b mark-v-1.X.0 upstream/release-1.X
./scripts/set_maven_version.sh 1.X.0
git add -u
git commit -m "chore: mark 1.X.0"
git push -u origin mark-v-1.X.0
gh pr create --repo microsoft/playwright-java --head <user>:mark-v-1.X.0 --base release-1.X \
--title "chore: mark 1.X.0" \
--body "Updates Maven version in all modules to \`1.X.0\` for the v1.X release."
```
`set_maven_version.sh` only invokes `mvn versions:set` on `pom.xml`, `tools/*/pom.xml`, and `examples/pom.xml`, but the root invocation cascades through the reactor, so the expected diff is 11 poms: root + `driver/` + `driver-bundle/` + `playwright/` (from the reactor cascade) + 6 under `tools/` + `examples/`, all flipping `1.<prev>.0-SNAPSHOT``1.X.0`. Any other file in the diff is a red flag.
## 4. Publish
The user publishes the draft release manually once the `mark-v-1.X.0` PR is merged. After publishing, CI pushes the artifacts to Maven Central and runs the Docker workflow automatically: https://github.com/microsoft/playwright-java/actions.
+41 -39
View File
@@ -7,45 +7,10 @@ Help the user roll to a new version of Playwright.
ROLLING.md contains general instructions and scripts.
Start with running ./scripts/roll_driver.sh to update the version and generate the API to see the state of things.
Afterwards, walk through the upstream changes that affect the Java client and port the relevant ones.
## Determining what to port
List the upstream commits that touched a client-relevant path since the last release. The paths cover everything that can change the public Java surface or the wire protocol:
- `docs/src/api/` — the source of truth for `api.json`. Method/option additions, removals, and `langs:` filter changes flow from here.
- `packages/playwright-core/src/client/` — the JS client implementation that the Java client mirrors.
- `packages/isomorphic/` — selector engines, locator generation/parsing, and aria-snapshot logic shared between client and server. Changes here can affect client-side helpers like `getByRoleSelector`.
- `packages/playwright/src/matchers/matchers.ts` — assertion-method definitions. Changes here usually correspond to new options on `LocatorAssertions` / `PageAssertions`.
- `packages/protocol/src/protocol.yml` — the wire protocol schema. Method/event additions, parameter renames, and result-shape changes affect what the Java `*Impl` classes need to send/receive.
```bash
cd ~/playwright
PREV_TAG=$(git tag | grep -E '^v1\.[0-9]+\.[0-9]+$' | sort -V | tail -1) # e.g. v1.59.1
git log "$PREV_TAG"..HEAD --oneline -- \
'docs/src/api/' \
'packages/playwright-core/src/client/' \
'packages/isomorphic/' \
'packages/playwright/src/matchers/matchers.ts' \
'packages/protocol/src/protocol.yml'
```
Walk that list top-to-bottom (oldest-first is easier — newest is at top, so reverse). For each commit:
1. Read the commit (`git show <sha>`) to see what client/protocol/docs changed.
2. If it's JS-internal (bundling, dispatcher conventions, electron, mcp, dashboard, trace-viewer, test-runner) — skip.
3. If it touches `docs/src/api/` or types, check `langs:` annotations — features marked `langs: js`/`langs: js, python` don't apply to Java.
4. If it adds/changes a public API method or option that applies to Java, port it. The api.json regenerated by `roll_driver.sh` already contains the new types/options, so the generated Java interfaces usually pick them up automatically — what's typically missing is the `*Impl` wiring.
5. Watch for follow-up reverts — a "feat: X" commit might be undone by a later "Revert X". Check whether the change still exists in HEAD before porting.
6. Maintain a running notes file (e.g. `/tmp/roll-notes.md`) listing each upstream PR as ported / skipped / verified-already-supported, with a one-line reason. This file becomes the body of the eventual PR.
## What to include in the rolling PR
- Driver version bump
- Generated interface diffs from `roll_driver.sh`
- `*Impl` wiring for each ported feature
- Generator updates (import lists, special-cases) if new types appeared
- A small test per new public API surface — listener for new events, basic call for new methods, regression for changed return types
- PR description: list each upstream PR ported, each skipped (with reason), and each verified-already-supported
Afterwards, work through the list of changes that need to be backported.
You can find a list of pull requests that might need to be taking into account in the issue titled "Backport changes".
Work through them one-by-one and check off the items that you have handled.
Not all of them will be relevant, some might have partially been reverted, etc. - so feel free to check with the upstream release branch.
Rolling includes:
- updating client implementation to match changes in the upstream JS implementation (see ../playwright/packages/playwright-core/src/client)
@@ -164,3 +129,40 @@ When you've identified a hanging test:
1. Run it in isolation: `mvn -f playwright/pom.xml test -Dtest='TestClass#testMethod'`. If it passes alone, it's a parallel-load flake — note it but move on.
2. If it still hangs in isolation, look for a recent fix in the upstream repo for the *same* test name. Use `git log --oneline tests/library/<spec>.spec.ts` in `~/playwright`. Upstream fixes for client-side hangs are often small and portable (e.g. `about:blank``server.EMPTY_PAGE` from microsoft/playwright#39840 fixed `route-web-socket.spec.ts` arraybuffer hangs — apparently some browser changed the WebSocket origin policy on `about:blank`).
3. When porting an upstream fix, mirror the helper signature change rather than hard-coding workarounds. E.g. if upstream added a `server` parameter to `setupWS`, do the same in Java by injecting `Server server` via the JUnit fixture (`@FixtureTest` already wires up `ServerLifecycle`, so adding `Server server` to the test method signature is enough — no class-level boilerplate). Watch for local-variable shadowing when you add a `Server server` parameter to a method that already has a `WebSocketRoute server` local; rename the local.
## Commit Convention
Semantic commit messages: `label(scope): description`
Labels: `fix`, `feat`, `chore`, `docs`, `test`, `devops`
```bash
git checkout -b fix-39562
# ... make changes ...
git add <changed-files>
git commit -m "$(cat <<'EOF'
fix(proxy): handle SOCKS proxy authentication
Fixes: https://github.com/microsoft/playwright-java/issues/39562
EOF
)"
git push origin fix-39562
gh pr create --repo microsoft/playwright-java --head username:fix-39562 \
--title "fix(proxy): handle SOCKS proxy authentication" \
--body "$(cat <<'EOF'
## Summary
- <describe the change very! briefly>
Fixes https://github.com/microsoft/playwright-java/issues/39562
EOF
)"
```
Never add Co-Authored-By agents in commit message.
Never add "Generated with" in commit message.
Branch naming for issue fixes: `fix-<issue-number>`
## Tips & Tricks
- Project checkouts are in the parent directory (`../`).
- When updating checkboxes, store the issue content into /tmp and edit it there, then update the issue based on the file
- use the "gh" cli to interact with GitHub
+2 -2
View File
@@ -13,7 +13,7 @@ jobs:
environment: Docker
if: github.repository == 'microsoft/playwright-java'
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
- name: Azure login
uses: azure/login@v3
with:
@@ -26,5 +26,5 @@ jobs:
uses: docker/setup-qemu-action@v4
with:
platforms: arm64
- uses: actions/checkout@v7
- uses: actions/checkout@v6
- run: ./utils/docker/publish_docker.sh stable
+3 -11
View File
@@ -18,17 +18,9 @@ jobs:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
browser: [chromium, firefox, webkit]
exclude:
# macos-latest is the free M1 runner (3 vCPU / 7 GB); WebKit needs more headroom.
# Upstream's webkit matrix runs on macos-15-xlarge for the same reason.
- os: macos-latest
browser: webkit
include:
- os: macos-15-xlarge
browser: webkit
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
- name: Set up JDK 1.8
uses: actions/setup-java@v5
with:
@@ -73,7 +65,7 @@ jobs:
browser-channel: msedge
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
- name: Install Media Pack
if: matrix.os == 'windows-latest'
shell: powershell
@@ -108,7 +100,7 @@ jobs:
browser: [chromium, firefox, webkit]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
- name: Set up JDK 21
uses: actions/setup-java@v5
with:
+2 -2
View File
@@ -13,9 +13,9 @@ jobs:
timeout-minutes: 30
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
- name: Cache Maven packages
uses: actions/cache@v6
uses: actions/cache@v5
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
+8 -33
View File
@@ -26,51 +26,26 @@ jobs:
strategy:
fail-fast: false
matrix:
flavor: [jammy, noble, resolute]
flavor: [jammy, noble]
runs-on: [ubuntu-24.04, ubuntu-24.04-arm]
include:
- runs-on: ubuntu-24.04
arch: amd64
- runs-on: ubuntu-24.04-arm
arch: arm64
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
- name: Build Docker image
run: |
bash utils/docker/build.sh --${{ matrix.arch }} ${{ matrix.flavor }} playwright-java:localbuild-${{ matrix.flavor }}
ARCH="${{ matrix.runs-on == 'ubuntu-24.04-arm' && 'arm64' || 'amd64' }}"
bash utils/docker/build.sh --$ARCH ${{ matrix.flavor }} playwright-java:localbuild-${{ matrix.flavor }}
- name: Start container
run: |
CONTAINER_ID=$(docker run \
--rm \
--name playwright-docker-test \
--platform linux/${{ matrix.arch }} \
--user=pwuser \
--workdir /home/pwuser \
--shm-size=2g \
-e CI \
-e PW_MAX_RETRIES \
-d -t \
playwright-java:localbuild-${{ matrix.flavor }} /bin/bash)
CONTAINER_ID=$(docker run --rm -e CI -e PW_MAX_RETRIES --ipc=host -v "$(pwd)":/root/playwright --name playwright-docker-test -d -t playwright-java:localbuild-${{ matrix.flavor }} /bin/bash)
echo "CONTAINER_ID=$CONTAINER_ID" >> $GITHUB_ENV
- name: Copy repository inside docker container
- name: Run test in container
run: |
docker cp . "$CONTAINER_ID":/home/pwuser/playwright
# /root/.m2 was populated as root during image build; move it to
# pwuser so the locally-installed SNAPSHOT artifacts resolve.
docker exec --user root "$CONTAINER_ID" bash -c '
chown -R pwuser /home/pwuser/playwright
mv /root/.m2 /home/pwuser/.m2
chown -R pwuser /home/pwuser/.m2
'
- name: Run smoke tests in container
run: |
docker exec "$CONTAINER_ID" /home/pwuser/playwright/tools/test-local-installation/create_project_and_run_tests.sh -Dgroups=smoke
docker exec "$CONTAINER_ID" /root/playwright/tools/test-local-installation/create_project_and_run_tests.sh
- name: Test ClassLoader
run: |
docker exec "${CONTAINER_ID}" /home/pwuser/playwright/tools/test-spring-boot-starter/package_and_run_async_test.sh
docker exec "${CONTAINER_ID}" /root/playwright/tools/test-spring-boot-starter/package_and_run_async_test.sh
- name: Stop container
run: |
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
timeout-minutes: 30
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
- name: Download drivers
run: scripts/download_driver.sh
- name: Regenerate APIs
-48
View File
@@ -1,48 +0,0 @@
# Playwright Java
The Java client is a port of the JavaScript client in `../playwright/packages/playwright-core/src/client/`. When implementing or changing a method, read the corresponding JS file first and mirror its logic.
Project checkouts (including the upstream `playwright` repo) live in the parent directory (`../`). Use the `gh` cli to interact with GitHub.
## Commit Convention
Semantic commit messages: `label(scope): description`
Labels: `fix`, `feat`, `chore`, `docs`, `test`, `devops`
```bash
git checkout -b fix-39562
# ... make changes ...
git add <changed-files>
git commit -m "$(cat <<'EOF'
fix(proxy): handle SOCKS proxy authentication
Fixes: https://github.com/microsoft/playwright-java/issues/39562
EOF
)"
# **Never `git push` without an explicit instruction to push.**
git push origin fix-39562
gh pr create --repo microsoft/playwright-java --head <user>:fix-39562 \
--title "fix(proxy): handle SOCKS proxy authentication" \
--body "$(cat <<'EOF'
## Summary
- <describe the change very! briefly>
Fixes https://github.com/microsoft/playwright-java/issues/39562
EOF
)"
```
Never add Co-Authored-By agents in commit message.
Never add "Generated with" in commit message.
Never add test plan to PR description. Keep PR description short — a few bullet points at most.
Branch naming for issue fixes: `fix-<issue-number>`.
**Never amend commits.** Always create a new commit for follow-up changes, even when iterating on an open PR. Amending rewrites history and forces a force-push, losing the incremental review trail. Only amend if the user explicitly says so.
**Never `git push` without an explicit instruction to push.** Applies even when a PR is already open for the branch — additional commits are immediately visible to reviewers. Commit locally, report what was committed, and wait. Only push when the user's message contains "push", "upload", "create PR", "ship it", or equivalent.
## Skills
- **playwright-roll** (`.claude/skills/playwright-roll/SKILL.md`) — roll Playwright Java to a new upstream version: bump the driver, regenerate the API, and port relevant upstream changes.
- **playwright-java-release** (`.claude/skills/playwright-java-release/SKILL.md`) — prepare a release after the rolling PR merges: cut the release branch, mark the Maven version, and draft the GitHub release.
+3 -4
View File
@@ -20,14 +20,12 @@ git clone https://github.com/microsoft/playwright-java
cd playwright-java
```
2. Run the following script to download and assemble the Playwright driver. The platform-independent `playwright-core` package is assembled once into `driver/src/main/resources/driver/package/`, and the Node.js binary for each platform into `driver-bundle/src/main/resources/driver/<platform>/` (browser binaries for Chromium, Firefox and WebKit will be automatically downloaded later on first Playwright run).
2. Run the following script to download Playwright driver for all platforms into `driver-bundle/src/main/resources/driver/` directory (browser binaries for Chromium, Firefox and WebKit will be automatically downloaded later on first Playwright run).
```bash
scripts/download_driver.sh
```
Each driver is assembled from the [`playwright-core`](https://www.npmjs.com/package/playwright-core) npm package (version pinned in [scripts/DRIVER_VERSION](scripts/DRIVER_VERSION)) and the matching Node.js binary from https://nodejs.org, the same way the upstream Playwright build does it.
### Building and running the tests with Maven
```bash
@@ -41,9 +39,10 @@ BROWSER=chromium mvn test -Dtest=TestPageNetworkSizes
### Generating API
Public Java API is generated from api.json, which is generated from the upstream Playwright source at the exact commit that produced the driver version in [scripts/DRIVER_VERSION](scripts/DRIVER_VERSION) (resolved via `npm view playwright@<version> gitHead`). `scripts/generate_api.sh` fetches a minimal upstream checkout automatically; set `PW_SRC_DIR` to reuse an existing `microsoft/playwright` checkout instead. To regenerate Java interfaces for the current driver run:
Public Java API is generated from api.json which is produced by `print-api-json` command of playwright CLI. To regenerate Java interfaces for the current driver run the following commands:
```bash
./scripts/download_driver.sh
./scripts/generate_api.sh
```
+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 -->151.0.7922.34<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->26.5<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Firefox <!-- GEN:firefox-version -->153.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Chromium <!-- GEN:chromium-version -->147.0.7727.15<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->26.4<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Firefox <!-- GEN:firefox-version -->148.0.2<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
## Documentation
+16 -17
View File
@@ -6,27 +6,26 @@
<parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.62.0</version>
<version>1.59.0</version>
</parent>
<artifactId>driver-bundle</artifactId>
<name>Playwright - Node.js For All Platforms</name>
<name>Playwright - Drivers For All Platforms</name>
<description>
Node.js binaries for the Playwright driver on every supported platform. Can be excluded when
Node.js is preinstalled on the host (see PLAYWRIGHT_NODEJS_PATH).
This module includes Playwright driver and related utilities for all supported platforms.
It is intended to be used on the systems where Playwright driver is not preinstalled.
</description>
<build>
<plugins>
<!-- The Node.js binaries for all platforms live in src/main/resources and must not
be packaged into the sources JAR (see issue #1913). -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<configuration>
<excludeResources>true</excludeResources>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>driver</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
</dependency>
</dependencies>
</project>
@@ -30,11 +30,17 @@ public class DriverJar extends Driver {
private static final String PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD = "PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD";
private static final String SELENIUM_REMOTE_URL = "SELENIUM_REMOTE_URL";
private final Path driverTempDir;
private final boolean deleteOnExit;
private Path preinstalledNodePath;
public DriverJar() throws IOException {
this(createTempDriverDir(), true);
// Allow specifying custom path for the driver installation
// See https://github.com/microsoft/playwright-java/issues/728
String alternativeTmpdir = System.getProperty("playwright.driver.tmpdir");
String prefix = "playwright-java-";
driverTempDir = alternativeTmpdir == null
? Files.createTempDirectory(prefix)
: Files.createTempDirectory(Paths.get(alternativeTmpdir), prefix);
driverTempDir.toFile().deleteOnExit();
String nodePath = System.getProperty("playwright.nodejs.path");
if (nodePath != null) {
preinstalledNodePath = Paths.get(nodePath);
@@ -45,32 +51,6 @@ public class DriverJar extends Driver {
logMessage("created DriverJar: " + driverTempDir);
}
private DriverJar(Path driverDir, boolean deleteOnExit) {
this.driverTempDir = driverDir;
this.deleteOnExit = deleteOnExit;
if (deleteOnExit) {
driverTempDir.toFile().deleteOnExit();
}
}
private static Path createTempDriverDir() throws IOException {
// Allow specifying custom path for the driver installation
// See https://github.com/microsoft/playwright-java/issues/728
String alternativeTmpdir = System.getProperty("playwright.driver.tmpdir");
String prefix = "playwright-java-";
return alternativeTmpdir == null
? Files.createTempDirectory(prefix)
: Files.createTempDirectory(Paths.get(alternativeTmpdir), prefix);
}
// Extracts the driver (playwright-core package and the Node.js binary for the current platform)
// into the given directory, persistently. Point playwright.cli.dir / PLAYWRIGHT_DRIVER_DIR at it
// to run without extracting to a temp directory on every launch. See issue #1268.
public static void installDriverTo(Path driverDir) throws IOException, URISyntaxException {
Files.createDirectories(driverDir);
new DriverJar(driverDir, false).extractDriverToTempDir();
}
@Override
protected void initialize(Boolean installBrowsers) throws Exception {
if (preinstalledNodePath == null && env.containsKey(PLAYWRIGHT_NODEJS_PATH)) {
@@ -139,21 +119,7 @@ public class DriverJar extends Driver {
}
void extractDriverToTempDir() throws URISyntaxException, IOException {
extractResourceToDir("driver/package", driverTempDir.resolve("package"));
if (preinstalledNodePath == null) {
String platformResource = "driver/" + platformDir();
if (DriverJar.class.getClassLoader().getResource(platformResource) == null) {
throw new RuntimeException("Failed to find the bundled Node.js for platform '" + platformDir()
+ "'. Add the com.microsoft.playwright:driver-bundle dependency, or set the "
+ PLAYWRIGHT_NODEJS_PATH + " environment variable (or the playwright.nodejs.path system "
+ "property) to point at a preinstalled Node.js.");
}
extractResourceToDir(platformResource, driverTempDir);
}
}
private void extractResourceToDir(String resourcePath, Path destDir) throws URISyntaxException, IOException {
URI originalUri = DriverJar.class.getClassLoader().getResource(resourcePath).toURI();
URI originalUri = getDriverResourceURI();
URI uri = maybeExtractNestedJar(originalUri);
// Create zip filesystem if loading from jar.
@@ -165,8 +131,14 @@ public class DriverJar extends Driver {
// See https://github.com/microsoft/playwright-java/issues/306
Path srcRootDefaultFs = Paths.get(srcRoot.toString());
Files.walk(srcRoot).forEach(fromPath -> {
if (preinstalledNodePath != null) {
String fileName = fromPath.getFileName().toString();
if ("node.exe".equals(fileName) || "node".equals(fileName)) {
return;
}
}
Path relative = srcRootDefaultFs.relativize(Paths.get(fromPath.toString()));
Path toPath = destDir.resolve(relative.toString());
Path toPath = driverTempDir.resolve(relative.toString());
try {
if (Files.isDirectory(fromPath)) {
Files.createDirectories(toPath);
@@ -176,9 +148,7 @@ public class DriverJar extends Driver {
toPath.toFile().setExecutable(true, true);
}
}
if (deleteOnExit) {
toPath.toFile().deleteOnExit();
}
toPath.toFile().deleteOnExit();
} catch (IOException e) {
throw new RuntimeException("Failed to extract driver from " + uri + ", full uri: " + originalUri, e);
}
@@ -201,9 +171,7 @@ public class DriverJar extends Driver {
Path fromPath = Paths.get(jarUri);
Path toPath = driverTempDir.resolve(fromPath.getFileName().toString());
Files.copy(fromPath, toPath);
if (deleteOnExit) {
toPath.toFile().deleteOnExit();
}
toPath.toFile().deleteOnExit();
return new URI("jar:" + toPath.toUri() + JAR_URL_SEPARATOR + parts[2]);
} catch (IOException e) {
throw new RuntimeException("Failed to extract driver's nested .jar from " + jarUri + "; full uri: " + uri, e);
@@ -132,30 +132,6 @@ public class TestInstall {
}
@Test
void canInstallDriverToDirectoryAndReuseIt(@TempDir Path tmpDir) throws Exception {
Path driverDir = tmpDir.resolve("driver");
DriverJar.installDriverTo(driverDir);
// The directory is self-contained: the playwright-core package and the Node.js binary.
assertTrue(Files.exists(driverDir.resolve("package").resolve("cli.js")));
assertTrue(Files.exists(driverDir.resolve(isWindows() ? "node.exe" : "node")));
// Pointing playwright.cli.dir at it must reuse it as-is, without extracting to a temp directory.
System.setProperty("playwright.cli.dir", driverDir.toString());
Driver driver = Driver.createAndInstall(Collections.emptyMap(), false);
assertEquals(driverDir, driver.driverDir());
ProcessBuilder pb = driver.createProcessBuilder();
pb.command().add("--version");
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
Path out = tmpDir.resolve("out.txt");
pb.redirectOutput(out.toFile());
Process p = pb.start();
assertTrue(p.waitFor(1, TimeUnit.MINUTES), "Timed out waiting for version to be printed");
String stdout = new String(Files.readAllBytes(out), StandardCharsets.UTF_8);
assertTrue(stdout.contains("Version "), stdout);
}
private static String extractNodeJsToTemp() throws URISyntaxException, IOException {
DriverJar auxDriver = new DriverJar();
auxDriver.extractDriverToTempDir();
+2 -17
View File
@@ -6,14 +6,13 @@
<parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.62.0</version>
<version>1.59.0</version>
</parent>
<artifactId>driver</artifactId>
<name>Playwright - Driver</name>
<description>
API for launching the Playwright driver. Bundles the platform-independent playwright-core
package; the Node.js binary comes from the driver-bundle module or a preinstalled Node.js.
This module provides API for discovery and launching of Playwright driver.
</description>
<dependencies>
@@ -22,18 +21,4 @@
<artifactId>junit-jupiter-engine</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!-- The playwright-core package lives in src/main/resources and must not be packaged
into the sources JAR (see issue #1913). -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<configuration>
<excludeResources>true</excludeResources>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -25,14 +25,12 @@ import static com.microsoft.playwright.impl.driver.DriverLogging.logWithTimestam
/**
* This class provides access to playwright-cli. It can be either preinstalled
* in the host system and its path is passed as a system property, or it can be
* loaded from the classpath: the platform-independent driver code ships in the
* driver module and the Node.js binary in the optional driver-bundle module.
* in the host system and its path is passed as a system property or it can be
* loaded from the driver-bundle module if that module is in the classpath.
*/
public abstract class Driver {
protected final Map<String, String> env = new LinkedHashMap<>(System.getenv());
public static final String PLAYWRIGHT_NODEJS_PATH = "PLAYWRIGHT_NODEJS_PATH";
public static final String PLAYWRIGHT_DRIVER_DIR = "PLAYWRIGHT_DRIVER_DIR";
private static Driver instance;
@@ -109,12 +107,9 @@ public abstract class Driver {
}
private static Driver newInstance() throws Exception {
String driverDir = System.getProperty("playwright.cli.dir");
if (driverDir == null) {
driverDir = System.getenv(PLAYWRIGHT_DRIVER_DIR);
}
if (driverDir != null) {
return new PreinstalledDriver(Paths.get(driverDir));
String pathFromProperty = System.getProperty("playwright.cli.dir");
if (pathFromProperty != null) {
return new PreinstalledDriver(Paths.get(pathFromProperty));
}
String driverImpl =
-2
View File
@@ -1,2 +0,0 @@
driver/
local-driver/
+2 -2
View File
@@ -6,11 +6,11 @@
<groupId>org.example</groupId>
<artifactId>examples</artifactId>
<version>1.62.0</version>
<version>1.59.0</version>
<name>Playwright Client Examples</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<playwright.version>1.62.0</playwright.version>
<playwright.version>1.59.0</playwright.version>
</properties>
<dependencies>
<dependency>
+1 -1
View File
@@ -7,7 +7,7 @@
<parent>
<groupId>com.microsoft.playwright</groupId>
<artifactId>parent-pom</artifactId>
<version>1.62.0</version>
<version>1.59.0</version>
</parent>
<artifactId>playwright</artifactId>
@@ -23,23 +23,24 @@ import java.nio.file.Path;
* This API is used for the Web API testing. You can use it to trigger API endpoints, configure micro-services, prepare
* environment or the service to your e2e test.
*
* <p> Each Playwright browser context has an associated {@code APIRequestContext}, accessible via {@link
* com.microsoft.playwright.BrowserContext#request BrowserContext.request()} or {@link
* com.microsoft.playwright.Page#request Page.request()} (these return the
*
* <p> **same instance** — {@code page.request} is a shortcut for {@code page.context().request}). You can also create a
* standalone, isolated instance with {@link com.microsoft.playwright.APIRequest#newContext APIRequest.newContext()}.
* <p> Each Playwright browser context has associated with it {@code APIRequestContext} instance which shares cookie storage
* with the browser context and can be accessed via {@link com.microsoft.playwright.BrowserContext#request
* BrowserContext.request()} or {@link com.microsoft.playwright.Page#request Page.request()}. It is also possible to create
* a new APIRequestContext instance manually by calling {@link com.microsoft.playwright.APIRequest#newContext
* APIRequest.newContext()}.
*
* <p> <strong>Cookie management</strong>
*
* <p> The {@code APIRequestContext} returned by {@link com.microsoft.playwright.BrowserContext#request
* BrowserContext.request()} and
* <p> {@code APIRequestContext} returned by {@link com.microsoft.playwright.BrowserContext#request BrowserContext.request()}
* and {@link com.microsoft.playwright.Page#request Page.request()} shares cookie storage with the corresponding {@code
* BrowserContext}. Each API request will have {@code Cookie} header populated with the values from the browser context. If
* the API response contains {@code Set-Cookie} header it will automatically update {@code BrowserContext} cookies and
* requests made from the page will pick them up. This means that if you log in using this API, your e2e test will be
* logged in and vice versa.
*
* <p> {@link com.microsoft.playwright.Page#request Page.request()} uses the same cookie jar as its {@code BrowserContext}:
*
* <p> If you want API requests that do **not** share cookies with the browser, create an isolated context via {@link
* com.microsoft.playwright.APIRequest#newContext APIRequest.newContext()}. Such {@code APIRequestContext} object will have
* its own isolated cookie storage.
* <p> If you want API requests to not interfere with the browser cookies you should create a new {@code APIRequestContext} by
* calling {@link com.microsoft.playwright.APIRequest#newContext APIRequest.newContext()}. Such {@code APIRequestContext}
* object will have its own isolated cookie storage.
*/
public interface APIRequestContext {
class DisposeOptions {
@@ -483,11 +484,5 @@ public interface APIRequestContext {
* @since v1.16
*/
String storageState(StorageStateOptions options);
/**
*
*
* @since v1.60
*/
Tracing tracing();
}
@@ -55,20 +55,6 @@ public interface APIResponse {
* @since v1.16
*/
boolean ok();
/**
* Returns SSL and other security information. Resolves to {@code null} for non-HTTPS responses. For redirected requests,
* returns the information for the last request in the redirect chain.
*
* @since v1.61
*/
SecurityDetails securityDetails();
/**
* Returns the IP address and port of the server. Resolves to {@code null} if the server address is not available. For
* redirected requests, returns the information for the last request in the redirect chain.
*
* @since v1.61
*/
ServerAddr serverAddr();
/**
* Contains the status code of the response (e.g., 200 for a success).
*
@@ -87,16 +73,6 @@ public interface APIResponse {
* @since v1.16
*/
String text();
/**
* Returns resource timing information for given response. For redirected requests, returns the information for the last
* request in the redirect chain. When the response is served <a
* href="https://playwright.dev/java/docs/mock#replaying-from-har">from the HAR file</a>, timing information is not
* available and all the values are -1. Find more information at <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming">Resource Timing API</a>.
*
* @since v1.62
*/
Timing timing();
/**
* Contains the URL of the response.
*
@@ -43,15 +43,6 @@ import java.util.regex.Pattern;
*/
public interface Browser extends AutoCloseable {
/**
* Emitted when a new browser context is created.
*/
void onContext(Consumer<BrowserContext> handler);
/**
* Removes handler that was previously added with {@link #onContext onContext(handler)}.
*/
void offContext(Consumer<BrowserContext> handler);
/**
* Emitted when Browser gets disconnected from the browser application. This might happen because of one of the following:
* <ul>
@@ -114,48 +114,6 @@ public interface BrowserContext extends AutoCloseable {
*/
void offDialog(Consumer<Dialog> handler);
/**
* Emitted when attachment download started in any page belonging to this context. User can access basic file operations on
* downloaded content via the passed {@code Download} instance. See also {@link com.microsoft.playwright.Page#onDownload
* Page.onDownload()} to receive events about a specific page.
*/
void onDownload(Consumer<Download> handler);
/**
* Removes handler that was previously added with {@link #onDownload onDownload(handler)}.
*/
void offDownload(Consumer<Download> handler);
/**
* Emitted when a frame is attached in any page belonging to this context. See also {@link
* com.microsoft.playwright.Page#onFrameAttached Page.onFrameAttached()} to receive events about a specific page.
*/
void onFrameAttached(Consumer<Frame> handler);
/**
* Removes handler that was previously added with {@link #onFrameAttached onFrameAttached(handler)}.
*/
void offFrameAttached(Consumer<Frame> handler);
/**
* Emitted when a frame is detached in any page belonging to this context. See also {@link
* com.microsoft.playwright.Page#onFrameDetached Page.onFrameDetached()} to receive events about a specific page.
*/
void onFrameDetached(Consumer<Frame> handler);
/**
* Removes handler that was previously added with {@link #onFrameDetached onFrameDetached(handler)}.
*/
void offFrameDetached(Consumer<Frame> handler);
/**
* Emitted when a frame is navigated to a new url in any page belonging to this context. See also {@link
* com.microsoft.playwright.Page#onFrameNavigated Page.onFrameNavigated()} to receive events about navigations in a
* specific page.
*/
void onFrameNavigated(Consumer<Frame> handler);
/**
* Removes handler that was previously added with {@link #onFrameNavigated onFrameNavigated(handler)}.
*/
void offFrameNavigated(Consumer<Frame> handler);
/**
* The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event will
* also fire for popup pages. See also {@link com.microsoft.playwright.Page#onPopup Page.onPopup()} to receive events about
@@ -183,27 +141,6 @@ public interface BrowserContext extends AutoCloseable {
*/
void offPage(Consumer<Page> handler);
/**
* Emitted when a page in this context is closed. See also {@link com.microsoft.playwright.Page#onClose Page.onClose()} to
* receive events about a specific page.
*/
void onPageClose(Consumer<Page> handler);
/**
* Removes handler that was previously added with {@link #onPageClose onPageClose(handler)}.
*/
void offPageClose(Consumer<Page> handler);
/**
* Emitted when the JavaScript <a href="https://developer.mozilla.org/en-US/docs/Web/Events/load">{@code load}</a> event is
* dispatched in any page belonging to this context. See also {@link com.microsoft.playwright.Page#onLoad Page.onLoad()} to
* receive events about a specific page.
*/
void onPageLoad(Consumer<Page> handler);
/**
* Removes handler that was previously added with {@link #onPageLoad onPageLoad(handler)}.
*/
void offPageLoad(Consumer<Page> handler);
/**
* Emitted when exception is unhandled in any of the pages in this context. To listen for errors from a particular page,
* use {@link com.microsoft.playwright.Page#onPageError Page.onPageError()} instead.
@@ -334,6 +271,20 @@ public interface BrowserContext extends AutoCloseable {
return this;
}
}
class ExposeBindingOptions {
/**
* @deprecated This option will be removed in the future.
*/
public Boolean handle;
/**
* @deprecated This option will be removed in the future.
*/
public ExposeBindingOptions setHandle(boolean handle) {
this.handle = handle;
return this;
}
}
class GrantPermissionsOptions {
/**
* The [origin] to grant permissions to, e.g. "https://example.com".
@@ -449,16 +400,6 @@ public interface BrowserContext extends AutoCloseable {
}
}
class StorageStateOptions {
/**
* Set to {@code true} to include the context's virtual WebAuthn {@link com.microsoft.playwright.BrowserContext#credentials
* BrowserContext.credentials()} (passkeys) in the storage state snapshot. The captured credentials carry their private
* keys, so they can be re-seeded into a later context via the {@code storageState} option or {@link
* com.microsoft.playwright.BrowserContext#setStorageState BrowserContext.setStorageState()}. Note that restoring the
* storage state that contains credentials will automatically install the virtual WebAuthn authenticator (see {@link
* com.microsoft.playwright.Credentials#install Credentials.install()}), and prevent all real authenticators from working
* in this context.
*/
public Boolean credentials;
/**
* Set to {@code true} to include <a href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API">IndexedDB</a> in
* the storage state snapshot. If your application uses IndexedDB to store authentication tokens, like Firebase
@@ -471,19 +412,6 @@ public interface BrowserContext extends AutoCloseable {
*/
public Path path;
/**
* Set to {@code true} to include the context's virtual WebAuthn {@link com.microsoft.playwright.BrowserContext#credentials
* BrowserContext.credentials()} (passkeys) in the storage state snapshot. The captured credentials carry their private
* keys, so they can be re-seeded into a later context via the {@code storageState} option or {@link
* com.microsoft.playwright.BrowserContext#setStorageState BrowserContext.setStorageState()}. Note that restoring the
* storage state that contains credentials will automatically install the virtual WebAuthn authenticator (see {@link
* com.microsoft.playwright.Credentials#install Credentials.install()}), and prevent all real authenticators from working
* in this context.
*/
public StorageStateOptions setCredentials(boolean credentials) {
this.credentials = credentials;
return this;
}
/**
* Set to {@code true} to include <a href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API">IndexedDB</a> in
* the storage state snapshot. If your application uses IndexedDB to store authentication tokens, like Firebase
@@ -586,13 +514,6 @@ public interface BrowserContext extends AutoCloseable {
* @since v1.45
*/
Clock clock();
/**
* Virtual WebAuthn authenticator for this context. Lets tests seed credentials and intercept {@code
* navigator.credentials.create()} / {@code navigator.credentials.get()} ceremonies.
*
* @since v1.61
*/
Credentials credentials();
/**
* Debugger allows to pause and resume the execution.
*
@@ -815,7 +736,54 @@ public interface BrowserContext extends AutoCloseable {
* @param callback Callback function that will be called in the Playwright's context.
* @since v1.8
*/
AutoCloseable exposeBinding(String name, BindingCallback callback);
default AutoCloseable exposeBinding(String name, BindingCallback callback) {
return exposeBinding(name, callback, null);
}
/**
* The method adds a function called {@code name} on the {@code window} object of every frame in every page in the context.
* When called, the function executes {@code callback} and returns a <a
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a> which
* resolves to the return value of {@code callback}. If the {@code callback} returns a <a
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, it will be
* awaited.
*
* <p> The first argument of the {@code callback} function contains information about the caller: {@code { browserContext:
* BrowserContext, page: Page, frame: Frame }}.
*
* <p> See {@link com.microsoft.playwright.Page#exposeBinding Page.exposeBinding()} for page-only version.
*
* <p> <strong>Usage</strong>
*
* <p> An example of exposing page URL to all frames in all pages in the context:
* <pre>{@code
* import com.microsoft.playwright.*;
*
* public class Example {
* public static void main(String[] args) {
* try (Playwright playwright = Playwright.create()) {
* 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());
* Page page = context.newPage();
* page.setContent("<script>\n" +
* " async function onClick() {\n" +
* " document.querySelector('div').textContent = await window.pageURL();\n" +
* " }\n" +
* "</script>\n" +
* "<button onclick=\"onClick()\">Click me</button>\n" +
* "<div></div>");
* page.getByRole(AriaRole.BUTTON).click();
* }
* }
* }
* }</pre>
*
* @param name Name of the function on the window object.
* @param callback Callback function that will be called in the Playwright's context.
* @since v1.8
*/
AutoCloseable exposeBinding(String name, BindingCallback callback, ExposeBindingOptions options);
/**
* The method adds a function called {@code name} on the {@code window} object of every frame in every page in the context.
* When called, the function executes {@code callback} and returns a <a
@@ -1492,8 +1460,7 @@ public interface BrowserContext extends AutoCloseable {
*/
void setOffline(boolean offline);
/**
* Returns storage state for this browser context, contains current cookies, local storage snapshot, IndexedDB snapshot and
* virtual WebAuthn credentials.
* Returns storage state for this browser context, contains current cookies, local storage snapshot and IndexedDB snapshot.
*
* @since v1.8
*/
@@ -1501,17 +1468,13 @@ public interface BrowserContext extends AutoCloseable {
return storageState(null);
}
/**
* Returns storage state for this browser context, contains current cookies, local storage snapshot, IndexedDB snapshot and
* virtual WebAuthn credentials.
* Returns storage state for this browser context, contains current cookies, local storage snapshot and IndexedDB snapshot.
*
* @since v1.8
*/
String storageState(StorageStateOptions options);
/**
* Clears the existing cookies, local storage, IndexedDB entries and virtual WebAuthn credentials, and sets the new storage
* state. When the storage state contains credentials, the virtual WebAuthn authenticator is installed (equivalent to
* {@link com.microsoft.playwright.Credentials#install Credentials.install()}), preventing all real authenticators from
* working in this context.
* Clears the existing cookies, local storage and IndexedDB entries for all origins and sets the new storage state.
*
* <p> <strong>Usage</strong>
* <pre>{@code
@@ -124,10 +124,6 @@ public interface BrowserType {
}
}
class ConnectOverCDPOptions {
/**
* If specified, browser artifacts (such as traces and downloads) are saved into this directory.
*/
public Path artifactsDir;
/**
* Additional HTTP headers to be sent with connect request. Optional.
*/
@@ -137,15 +133,6 @@ public interface BrowserType {
* the file system being the same between Playwright and the Browser.
*/
public Boolean isLocal;
/**
* When true, Playwright will not apply its default overrides to the existing default browser context. Specifically, {@code
* acceptDownloads} is left at the browser's setting, focus emulation is not enabled, and media emulation options (such as
* {@code colorScheme}, {@code reducedMotion}, {@code forcedColors}, and {@code contrast}) are not applied. Useful when
* attaching to a user's daily-driver browser where these overrides would interfere with existing browser state. New
* contexts created via {@link com.microsoft.playwright.Browser#newContext Browser.newContext()} are not affected. Defaults
* to {@code false}.
*/
public Boolean noDefaults;
/**
* Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
* Defaults to 0.
@@ -157,13 +144,6 @@ public interface BrowserType {
*/
public Double timeout;
/**
* If specified, browser artifacts (such as traces and downloads) are saved into this directory.
*/
public ConnectOverCDPOptions setArtifactsDir(Path artifactsDir) {
this.artifactsDir = artifactsDir;
return this;
}
/**
* Additional HTTP headers to be sent with connect request. Optional.
*/
@@ -179,18 +159,6 @@ public interface BrowserType {
this.isLocal = isLocal;
return this;
}
/**
* When true, Playwright will not apply its default overrides to the existing default browser context. Specifically, {@code
* acceptDownloads} is left at the browser's setting, focus emulation is not enabled, and media emulation options (such as
* {@code colorScheme}, {@code reducedMotion}, {@code forcedColors}, and {@code contrast}) are not applied. Useful when
* attaching to a user's daily-driver browser where these overrides would interfere with existing browser state. New
* contexts created via {@link com.microsoft.playwright.Browser#newContext Browser.newContext()} are not affected. Defaults
* to {@code false}.
*/
public ConnectOverCDPOptions setNoDefaults(boolean noDefaults) {
this.noDefaults = noDefaults;
return this;
}
/**
* Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
* Defaults to 0.
@@ -1314,9 +1282,6 @@ public interface BrowserType {
* advanced functionality, you probably want to use {@link com.microsoft.playwright.BrowserType#connect
* BrowserType.connect()}.
*
* <p> <strong>NOTE:</strong> Playwright maintains a curated list of arguments for launching the browser. If you launch the browser without Playwright
* and do not pass the exact same arguments, some of Playwright functionality may be broken upon connecting to the browser.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* Browser browser = playwright.chromium().connectOverCDP("http://localhost:9222");
@@ -1343,9 +1308,6 @@ public interface BrowserType {
* advanced functionality, you probably want to use {@link com.microsoft.playwright.BrowserType#connect
* BrowserType.connect()}.
*
* <p> <strong>NOTE:</strong> Playwright maintains a curated list of arguments for launching the browser. If you launch the browser without Playwright
* and do not pass the exact same arguments, some of Playwright functionality may be broken upon connecting to the browser.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* Browser browser = playwright.chromium().connectOverCDP("http://localhost:9222");
@@ -17,12 +17,9 @@
package com.microsoft.playwright;
import com.microsoft.playwright.impl.driver.Driver;
import com.microsoft.playwright.impl.driver.jar.DriverJar;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import static java.util.Arrays.asList;
@@ -31,13 +28,7 @@ import static java.util.Arrays.asList;
* Use this class to launch playwright cli.
*/
public class CLI {
public static void main(String[] args) throws IOException, InterruptedException, URISyntaxException {
// Extract the driver into a fixed directory instead of running the playwright CLI. This is
// handled in Java because it must not require an already-extracted driver. See issue #1268.
if (args.length > 0 && "install-driver".equals(args[0])) {
installDriver(args);
return;
}
public static void main(String[] args) throws IOException, InterruptedException {
Driver driver = Driver.ensureDriverInstalled(Collections.emptyMap(), false);
ProcessBuilder pb = driver.createProcessBuilder();
pb.command().addAll(asList(args));
@@ -49,17 +40,4 @@ public class CLI {
Process process = pb.start();
System.exit(process.waitFor());
}
private static void installDriver(String[] args) throws IOException, URISyntaxException {
String dir = args.length > 1 ? args[1] : System.getenv(Driver.PLAYWRIGHT_DRIVER_DIR);
if (dir == null) {
System.err.println("Usage: install-driver <dir> (or set the " + Driver.PLAYWRIGHT_DRIVER_DIR
+ " environment variable)");
System.exit(1);
return;
}
Path driverDir = Paths.get(dir);
DriverJar.installDriverTo(driverDir);
System.out.println("Installed Playwright driver into " + driverDir.toAbsolutePath());
}
}
@@ -1,243 +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;
import com.microsoft.playwright.options.*;
import java.util.*;
/**
* {@code Credentials} is a virtual WebAuthn authenticator scoped to a {@code BrowserContext}. It lets tests register
* passkeys and answer {@code navigator.credentials.create()} / {@code navigator.credentials.get()} ceremonies in the page,
* without a real authenticator or hardware security key.
*
* <p> There are three common ways to use it:
*
* <p> <strong>Usage: seed a known credential</strong>
* <pre>{@code
* BrowserContext context = browser.newContext();
*
* // A passkey your backend already provisioned for a test user.
* context.credentials().create("example.com", new Credentials.CreateOptions()
* .setId(knownCredentialId) // base64url
* .setUserHandle(knownUserHandle) // base64url
* .setPrivateKey(knownPrivateKey) // base64url PKCS#8 (DER)
* .setPublicKey(knownPublicKey)); // base64url SPKI (DER)
* context.credentials().install();
*
* Page page = context.newPage();
* page.navigate("https://example.com/login");
* // The page's navigator.credentials.get() is answered with the seeded passkey.
* }</pre>
*
* <p> <strong>Usage: capture a credential, then reuse it</strong>
* <pre>{@code
* // setup test: let the app register a passkey, then save it.
* BrowserContext context = browser.newContext();
* context.credentials().install();
*
* Page page = context.newPage();
* page.navigate("https://example.com/register");
* page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Create a passkey")).click();
*
* // Read back the passkey the page registered — it includes the private key.
* VirtualCredential credential = context.credentials().get(
* new Credentials.GetOptions().setRpId("example.com")).get(0);
* Files.writeString(Paths.get("playwright/.auth/passkey.json"), new Gson().toJson(credential));
* }</pre>
* <pre>{@code
* // later test: seed the captured passkey so the app starts already enrolled.
* VirtualCredential credential = new Gson().fromJson(
* Files.readString(Paths.get("playwright/.auth/passkey.json")), VirtualCredential.class);
* BrowserContext context = browser.newContext();
* context.credentials().create(credential.rpId, new Credentials.CreateOptions()
* .setId(credential.id)
* .setUserHandle(credential.userHandle)
* .setPrivateKey(credential.privateKey)
* .setPublicKey(credential.publicKey));
* context.credentials().install();
*
* Page page = context.newPage();
* page.navigate("https://example.com/login");
* // navigator.credentials.get() resolves the captured passkey — already signed in.
* }</pre>
*
* <p> <strong>Usage: save credentials in the storage state, restore later</strong>
*
* <p> See <a href="https://playwright.dev/java/docs/auth">authentication guide</a> for examples of using saving and resotring
* the storage state.
*
* <p> <strong>Defaults</strong>
*/
public interface Credentials {
class CreateOptions {
/**
* Base64url-encoded credential id. Auto-generated if omitted.
*/
public String id;
/**
* Base64url-encoded PKCS#8 (DER) private key. Auto-generated if omitted.
*/
public String privateKey;
/**
* Base64url-encoded SPKI (DER) public key. Auto-generated if omitted.
*/
public String publicKey;
/**
* Base64url-encoded user handle. Auto-generated if omitted.
*/
public String userHandle;
/**
* Base64url-encoded credential id. Auto-generated if omitted.
*/
public CreateOptions setId(String id) {
this.id = id;
return this;
}
/**
* Base64url-encoded PKCS#8 (DER) private key. Auto-generated if omitted.
*/
public CreateOptions setPrivateKey(String privateKey) {
this.privateKey = privateKey;
return this;
}
/**
* Base64url-encoded SPKI (DER) public key. Auto-generated if omitted.
*/
public CreateOptions setPublicKey(String publicKey) {
this.publicKey = publicKey;
return this;
}
/**
* Base64url-encoded user handle. Auto-generated if omitted.
*/
public CreateOptions setUserHandle(String userHandle) {
this.userHandle = userHandle;
return this;
}
}
class GetOptions {
/**
* Only return the credential with this base64url-encoded id.
*/
public String id;
/**
* Only return credentials for this relying party id.
*/
public String rpId;
/**
* Only return the credential with this base64url-encoded id.
*/
public GetOptions setId(String id) {
this.id = id;
return this;
}
/**
* Only return credentials for this relying party id.
*/
public GetOptions setRpId(String rpId) {
this.rpId = rpId;
return this;
}
}
/**
* Installs the virtual WebAuthn authenticator into the context, overriding {@code navigator.credentials.create()} and
* {@code navigator.credentials.get()} in all current and future pages. Call this before the page first touches {@code
* navigator.credentials}.
*
* <p> Required: until {@link com.microsoft.playwright.Credentials#install Credentials.install()} is called, no interception is
* in place and the page sees the platform's native (or absent) WebAuthn behaviour. Seeding credentials with {@link
* com.microsoft.playwright.Credentials#create Credentials.create()} without installing populates the authenticator, but
* the page will never see those credentials.
*
* @since v1.61
*/
void install();
/**
* Seeds a virtual WebAuthn credential and returns it.
*
* <p> With only {@code rpId}, generates a fresh **ECDSA P-256** keypair, credential id and user handle. The seeded credential
* is discoverable (resident), so the page can resolve it from both username-then-passkey and usernameless passkey flows.
* The returned object carries the private and public keys, so it can be persisted to disk and re-seeded in a later test.
*
* <p> To **import a known credential**, supply all four of {@code id}, {@code userHandle}, {@code privateKey} and {@code
* publicKey} together.
*
* <p> Call {@link com.microsoft.playwright.Credentials#install Credentials.install()} before navigating to a page that uses
* WebAuthn.
*
* @param rpId Relying party id (typically the site's effective domain).
* @since v1.61
*/
default VirtualCredential create(String rpId) {
return create(rpId, null);
}
/**
* Seeds a virtual WebAuthn credential and returns it.
*
* <p> With only {@code rpId}, generates a fresh **ECDSA P-256** keypair, credential id and user handle. The seeded credential
* is discoverable (resident), so the page can resolve it from both username-then-passkey and usernameless passkey flows.
* The returned object carries the private and public keys, so it can be persisted to disk and re-seeded in a later test.
*
* <p> To **import a known credential**, supply all four of {@code id}, {@code userHandle}, {@code privateKey} and {@code
* publicKey} together.
*
* <p> Call {@link com.microsoft.playwright.Credentials#install Credentials.install()} before navigating to a page that uses
* WebAuthn.
*
* @param rpId Relying party id (typically the site's effective domain).
* @since v1.61
*/
VirtualCredential create(String rpId, CreateOptions options);
/**
* Removes a credential from the authenticator by its id. Works for any credential currently held — both those seeded with
* {@link com.microsoft.playwright.Credentials#create Credentials.create()} and those the page registered itself by calling
* {@code navigator.credentials.create()}.
*
* @param id Base64url-encoded credential id.
* @since v1.61
*/
void delete(String id);
/**
* Returns every credential currently held by the authenticator, optionally filtered by {@code rpId} or {@code id}. This
* includes both credentials seeded with {@link com.microsoft.playwright.Credentials#create Credentials.create()} and
* credentials the page registered itself by calling {@code navigator.credentials.create()}.
*
* <p> Each returned credential includes its private and public keys, so a passkey the app just registered can be saved and
* re-seeded into a later test with {@link com.microsoft.playwright.Credentials#create Credentials.create()} — see the
* second example in the class overview.
*
* @since v1.61
*/
default List<VirtualCredential> get() {
return get(null);
}
/**
* Returns every credential currently held by the authenticator, optionally filtered by {@code rpId} or {@code id}. This
* includes both credentials seeded with {@link com.microsoft.playwright.Credentials#create Credentials.create()} and
* credentials the page registered itself by calling {@code navigator.credentials.create()}.
*
* <p> Each returned credential includes its private and public keys, so a passkey the app just registered can be saved and
* re-seeded into a later test with {@link com.microsoft.playwright.Credentials#create Credentials.create()} — see the
* second example in the class overview.
*
* @since v1.61
*/
List<VirtualCredential> get(GetOptions options);
}
@@ -73,13 +73,6 @@ public interface ElementHandle extends JSHandle {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* 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
@@ -124,16 +117,6 @@ public interface ElementHandle extends JSHandle {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public CheckOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* 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
@@ -187,13 +170,6 @@ public interface ElementHandle extends JSHandle {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* Defaults to 1. Sends {@code n} interpolated {@code mousemove} events to represent travel between Playwright's current
* cursor position and the provided destination. When set to 1, emits a single {@code mousemove} event at the destination
@@ -274,16 +250,6 @@ public interface ElementHandle extends JSHandle {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ClickOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* Defaults to 1. Sends {@code n} interpolated {@code mousemove} events to represent travel between Playwright's current
* cursor position and the provided destination. When set to 1, emits a single {@code mousemove} event at the destination
@@ -342,13 +308,6 @@ public interface ElementHandle extends JSHandle {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* Defaults to 1. Sends {@code n} interpolated {@code mousemove} events to represent travel between Playwright's current
* cursor position and the provided destination. When set to 1, emits a single {@code mousemove} event at the destination
@@ -422,16 +381,6 @@ public interface ElementHandle extends JSHandle {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public DblclickOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* Defaults to 1. Sends {@code n} interpolated {@code mousemove} events to represent travel between Playwright's current
* cursor position and the provided destination. When set to 1, emits a single {@code mousemove} event at the destination
@@ -526,13 +475,6 @@ public interface ElementHandle extends JSHandle {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* 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
@@ -586,16 +528,6 @@ public interface ElementHandle extends JSHandle {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public HoverOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* 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
@@ -618,12 +550,18 @@ public interface ElementHandle extends JSHandle {
}
class InputValueOptions {
/**
* @deprecated This option is ignored. The value is returned immediately.
* 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;
/**
* @deprecated This option is ignored. The value is returned immediately.
* 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 InputValueOptions setTimeout(double timeout) {
this.timeout = timeout;
@@ -714,9 +652,7 @@ public interface ElementHandle extends JSHandle {
*/
public Path path;
/**
* The quality of the image, between 0-100. Not applicable to {@code png} images. For {@code jpeg} the default is {@code
* 80}. For {@code webp}, a quality of {@code 100} (the default) produces a lossless image, while lower values use lossy
* compression.
* The quality of the image, between 0-100. Not applicable to {@code png} images.
*/
public Integer quality;
/**
@@ -804,9 +740,7 @@ public interface ElementHandle extends JSHandle {
return this;
}
/**
* The quality of the image, between 0-100. Not applicable to {@code png} images. For {@code jpeg} the default is {@code
* 80}. For {@code webp}, a quality of {@code 100} (the default) produces a lossless image, while lower values use lossy
* compression.
* The quality of the image, between 0-100. Not applicable to {@code png} images.
*/
public ScreenshotOptions setQuality(int quality) {
this.quality = quality;
@@ -962,13 +896,6 @@ public interface ElementHandle extends JSHandle {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* 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
@@ -1013,16 +940,6 @@ public interface ElementHandle extends JSHandle {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public SetCheckedOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* 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
@@ -1095,13 +1012,6 @@ public interface ElementHandle extends JSHandle {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* 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
@@ -1155,16 +1065,6 @@ public interface ElementHandle extends JSHandle {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public TapOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* 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
@@ -1242,13 +1142,6 @@ public interface ElementHandle extends JSHandle {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* 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
@@ -1293,16 +1186,6 @@ public interface ElementHandle extends JSHandle {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public UncheckOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* 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
@@ -165,13 +165,6 @@ public interface Frame {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -221,16 +214,6 @@ public interface Frame {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public CheckOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -292,13 +275,6 @@ public interface Frame {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -379,16 +355,6 @@ public interface Frame {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ClickOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -447,13 +413,6 @@ public interface Frame {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -527,16 +486,6 @@ public interface Frame {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public DblclickOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -609,13 +558,6 @@ public interface Frame {
* @deprecated This option has no effect.
*/
public Boolean noWaitAfter;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* Clicks on the source element at this point relative to the top-left corner of the element's padding box. If not
* specified, some visible point of the element is used.
@@ -665,16 +607,6 @@ public interface Frame {
this.noWaitAfter = noWaitAfter;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public DragAndDropOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* Clicks on the source element at this point relative to the top-left corner of the element's padding box. If not
* specified, some visible point of the element is used.
@@ -935,13 +867,6 @@ public interface Frame {
* <p> Learn more about <a href="https://www.w3.org/TR/wai-aria-1.2/#aria-checked">{@code aria-checked}</a>.
*/
public Boolean checked;
/**
* Option to match the <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>. By
* default, matching is case-insensitive and searches for a substring, use {@code exact} to control this behavior.
*
* <p> Learn more about <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>.
*/
public Object description;
/**
* An attribute that is usually set by {@code aria-disabled} or {@code disabled}.
*
@@ -950,8 +875,8 @@ public interface Frame {
*/
public Boolean disabled;
/**
* Whether {@code name} and {@code description} are matched exactly: case-sensitive and whole-string. Defaults to false.
* Ignored when the value is a regular expression. Note that exact match still trims whitespace.
* Whether {@code name} is matched exactly: case-sensitive and whole-string. Defaults to false. Ignored when {@code name}
* is a regular expression. Note that exact match still trims whitespace.
*/
public Boolean exact;
/**
@@ -1003,26 +928,6 @@ public interface Frame {
this.checked = checked;
return this;
}
/**
* Option to match the <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>. By
* default, matching is case-insensitive and searches for a substring, use {@code exact} to control this behavior.
*
* <p> Learn more about <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>.
*/
public GetByRoleOptions setDescription(String description) {
this.description = description;
return this;
}
/**
* Option to match the <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>. By
* default, matching is case-insensitive and searches for a substring, use {@code exact} to control this behavior.
*
* <p> Learn more about <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>.
*/
public GetByRoleOptions setDescription(Pattern description) {
this.description = description;
return this;
}
/**
* An attribute that is usually set by {@code aria-disabled} or {@code disabled}.
*
@@ -1034,8 +939,8 @@ public interface Frame {
return this;
}
/**
* Whether {@code name} and {@code description} are matched exactly: case-sensitive and whole-string. Defaults to false.
* Ignored when the value is a regular expression. Note that exact match still trims whitespace.
* Whether {@code name} is matched exactly: case-sensitive and whole-string. Defaults to false. Ignored when {@code name}
* is a regular expression. Note that exact match still trims whitespace.
*/
public GetByRoleOptions setExact(boolean exact) {
this.exact = exact;
@@ -1224,13 +1129,6 @@ public interface Frame {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -1290,16 +1188,6 @@ public interface Frame {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public HoverOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -1857,13 +1745,6 @@ public interface Frame {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -1913,16 +1794,6 @@ public interface Frame {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public SetCheckedOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -2065,13 +1936,6 @@ public interface Frame {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -2131,16 +1995,6 @@ public interface Frame {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public TapOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -2273,13 +2127,6 @@ public interface Frame {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -2329,16 +2176,6 @@ public interface Frame {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public UncheckOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -3101,7 +2938,7 @@ public interface Frame {
* <p> {@code ElementHandle} instances can be passed as an argument to the {@link com.microsoft.playwright.Frame#evaluate
* Frame.evaluate()}:
* <pre>{@code
* ElementHandle bodyHandle = frame.evaluateHandle("document.body");
* ElementHandle bodyHandle = frame.evaluate("document.body");
* String html = (String) frame.evaluate("([body, suffix]) => body.innerHTML + suffix", Arrays.asList(bodyHandle, "hello"));
* bodyHandle.dispose();
* }</pre>
@@ -3141,7 +2978,7 @@ public interface Frame {
* <p> {@code ElementHandle} instances can be passed as an argument to the {@link com.microsoft.playwright.Frame#evaluate
* Frame.evaluate()}:
* <pre>{@code
* ElementHandle bodyHandle = frame.evaluateHandle("document.body");
* ElementHandle bodyHandle = frame.evaluate("document.body");
* String html = (String) frame.evaluate("([body, suffix]) => body.innerHTML + suffix", Arrays.asList(bodyHandle, "hello"));
* bodyHandle.dispose();
* }</pre>
@@ -107,13 +107,6 @@ public interface FrameLocator {
* <p> Learn more about <a href="https://www.w3.org/TR/wai-aria-1.2/#aria-checked">{@code aria-checked}</a>.
*/
public Boolean checked;
/**
* Option to match the <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>. By
* default, matching is case-insensitive and searches for a substring, use {@code exact} to control this behavior.
*
* <p> Learn more about <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>.
*/
public Object description;
/**
* An attribute that is usually set by {@code aria-disabled} or {@code disabled}.
*
@@ -122,8 +115,8 @@ public interface FrameLocator {
*/
public Boolean disabled;
/**
* Whether {@code name} and {@code description} are matched exactly: case-sensitive and whole-string. Defaults to false.
* Ignored when the value is a regular expression. Note that exact match still trims whitespace.
* Whether {@code name} is matched exactly: case-sensitive and whole-string. Defaults to false. Ignored when {@code name}
* is a regular expression. Note that exact match still trims whitespace.
*/
public Boolean exact;
/**
@@ -175,26 +168,6 @@ public interface FrameLocator {
this.checked = checked;
return this;
}
/**
* Option to match the <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>. By
* default, matching is case-insensitive and searches for a substring, use {@code exact} to control this behavior.
*
* <p> Learn more about <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>.
*/
public GetByRoleOptions setDescription(String description) {
this.description = description;
return this;
}
/**
* Option to match the <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>. By
* default, matching is case-insensitive and searches for a substring, use {@code exact} to control this behavior.
*
* <p> Learn more about <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>.
*/
public GetByRoleOptions setDescription(Pattern description) {
this.description = description;
return this;
}
/**
* An attribute that is usually set by {@code aria-disabled} or {@code disabled}.
*
@@ -206,8 +179,8 @@ public interface FrameLocator {
return this;
}
/**
* Whether {@code name} and {@code description} are matched exactly: case-sensitive and whole-string. Defaults to false.
* Ignored when the value is a regular expression. Note that exact match still trims whitespace.
* Whether {@code name} is matched exactly: case-sensitive and whole-string. Defaults to false. Ignored when {@code name}
* is a regular expression. Note that exact match still trims whitespace.
*/
public GetByRoleOptions setExact(boolean exact) {
this.exact = exact;
@@ -30,13 +30,6 @@ import java.util.regex.Pattern;
*/
public interface Locator {
class AriaSnapshotOptions {
/**
* When {@code true}, appends each element's bounding box as {@code [box=x,y,width,height]} to the snapshot. Coordinates
* are relative to the viewport, in CSS pixels, as returned by <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect">{@code
* Element.getBoundingClientRect()}</a>. Defaults to {@code false}.
*/
public Boolean boxes;
/**
* When specified, limits the depth of the snapshot.
*/
@@ -54,16 +47,6 @@ public interface Locator {
*/
public Double timeout;
/**
* When {@code true}, appends each element's bounding box as {@code [box=x,y,width,height]} to the snapshot. Coordinates
* are relative to the viewport, in CSS pixels, as returned by <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect">{@code
* Element.getBoundingClientRect()}</a>. Defaults to {@code false}.
*/
public AriaSnapshotOptions setBoxes(boolean boxes) {
this.boxes = boxes;
return this;
}
/**
* When specified, limits the depth of the snapshot.
*/
@@ -145,13 +128,6 @@ public interface Locator {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* 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
@@ -196,16 +172,6 @@ public interface Locator {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public CheckOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* 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
@@ -303,13 +269,6 @@ public interface Locator {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* Defaults to 1. Sends {@code n} interpolated {@code mousemove} events to represent travel between Playwright's current
* cursor position and the provided destination. When set to 1, emits a single {@code mousemove} event at the destination
@@ -391,16 +350,6 @@ public interface Locator {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ClickOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* Defaults to 1. Sends {@code n} interpolated {@code mousemove} events to represent travel between Playwright's current
* cursor position and the provided destination. When set to 1, emits a single {@code mousemove} event at the destination
@@ -460,13 +409,6 @@ public interface Locator {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* Defaults to 1. Sends {@code n} interpolated {@code mousemove} events to represent travel between Playwright's current
* cursor position and the provided destination. When set to 1, emits a single {@code mousemove} event at the destination
@@ -541,16 +483,6 @@ public interface Locator {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public DblclickOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* Defaults to 1. Sends {@code n} interpolated {@code mousemove} events to represent travel between Playwright's current
* cursor position and the provided destination. When set to 1, emits a single {@code mousemove} event at the destination
@@ -611,13 +543,6 @@ public interface Locator {
* @deprecated This option has no effect.
*/
public Boolean noWaitAfter;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* Clicks on the source element at this point relative to the top-left corner of the element's padding box. If not
* specified, some visible point of the element is used.
@@ -662,16 +587,6 @@ public interface Locator {
this.noWaitAfter = noWaitAfter;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public DragToOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* Clicks on the source element at this point relative to the top-left corner of the element's padding box. If not
* specified, some visible point of the element is used.
@@ -730,46 +645,6 @@ public interface Locator {
return this;
}
}
class DropOptions {
/**
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
* element.
*/
public Position position;
/**
* 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;
/**
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
* element.
*/
public DropOptions setPosition(double x, double y) {
return setPosition(new Position(x, y));
}
/**
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
* element.
*/
public DropOptions setPosition(Position position) {
this.position = position;
return this;
}
/**
* 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 DropOptions setTimeout(double timeout) {
this.timeout = timeout;
return this;
}
}
class ElementHandleOptions {
/**
* Maximum time in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
@@ -1068,13 +943,6 @@ public interface Locator {
* <p> Learn more about <a href="https://www.w3.org/TR/wai-aria-1.2/#aria-checked">{@code aria-checked}</a>.
*/
public Boolean checked;
/**
* Option to match the <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>. By
* default, matching is case-insensitive and searches for a substring, use {@code exact} to control this behavior.
*
* <p> Learn more about <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>.
*/
public Object description;
/**
* An attribute that is usually set by {@code aria-disabled} or {@code disabled}.
*
@@ -1083,8 +951,8 @@ public interface Locator {
*/
public Boolean disabled;
/**
* Whether {@code name} and {@code description} are matched exactly: case-sensitive and whole-string. Defaults to false.
* Ignored when the value is a regular expression. Note that exact match still trims whitespace.
* Whether {@code name} is matched exactly: case-sensitive and whole-string. Defaults to false. Ignored when {@code name}
* is a regular expression. Note that exact match still trims whitespace.
*/
public Boolean exact;
/**
@@ -1136,26 +1004,6 @@ public interface Locator {
this.checked = checked;
return this;
}
/**
* Option to match the <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>. By
* default, matching is case-insensitive and searches for a substring, use {@code exact} to control this behavior.
*
* <p> Learn more about <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>.
*/
public GetByRoleOptions setDescription(String description) {
this.description = description;
return this;
}
/**
* Option to match the <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>. By
* default, matching is case-insensitive and searches for a substring, use {@code exact} to control this behavior.
*
* <p> Learn more about <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>.
*/
public GetByRoleOptions setDescription(Pattern description) {
this.description = description;
return this;
}
/**
* An attribute that is usually set by {@code aria-disabled} or {@code disabled}.
*
@@ -1167,8 +1015,8 @@ public interface Locator {
return this;
}
/**
* Whether {@code name} and {@code description} are matched exactly: case-sensitive and whole-string. Defaults to false.
* Ignored when the value is a regular expression. Note that exact match still trims whitespace.
* Whether {@code name} is matched exactly: case-sensitive and whole-string. Defaults to false. Ignored when {@code name}
* is a regular expression. Note that exact match still trims whitespace.
*/
public GetByRoleOptions setExact(boolean exact) {
this.exact = exact;
@@ -1274,20 +1122,6 @@ public interface Locator {
return this;
}
}
class HighlightOptions {
/**
* Additional inline CSS applied to the highlight overlay, e.g. {@code "outline: 2px dashed red"}.
*/
public String style;
/**
* Additional inline CSS applied to the highlight overlay, e.g. {@code "outline: 2px dashed red"}.
*/
public HighlightOptions setStyle(String style) {
this.style = style;
return this;
}
}
class HoverOptions {
/**
* Whether to bypass the <a href="https://playwright.dev/java/docs/actionability">actionability</a> checks. Defaults to
@@ -1309,13 +1143,6 @@ public interface Locator {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* 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
@@ -1370,16 +1197,6 @@ public interface Locator {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public HoverOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* 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
@@ -1795,9 +1612,7 @@ public interface Locator {
*/
public Path path;
/**
* The quality of the image, between 0-100. Not applicable to {@code png} images. For {@code jpeg} the default is {@code
* 80}. For {@code webp}, a quality of {@code 100} (the default) produces a lossless image, while lower values use lossy
* compression.
* The quality of the image, between 0-100. Not applicable to {@code png} images.
*/
public Integer quality;
/**
@@ -1885,9 +1700,7 @@ public interface Locator {
return this;
}
/**
* The quality of the image, between 0-100. Not applicable to {@code png} images. For {@code jpeg} the default is {@code
* 80}. For {@code webp}, a quality of {@code 100} (the default) produces a lossless image, while lower values use lossy
* compression.
* The quality of the image, between 0-100. Not applicable to {@code png} images.
*/
public ScreenshotOptions setQuality(int quality) {
this.quality = quality;
@@ -2043,13 +1856,6 @@ public interface Locator {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* 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
@@ -2094,16 +1900,6 @@ public interface Locator {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public SetCheckedOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* 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
@@ -2176,13 +1972,6 @@ public interface Locator {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* 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
@@ -2237,16 +2026,6 @@ public interface Locator {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public TapOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* 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
@@ -2345,13 +2124,6 @@ public interface Locator {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* 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
@@ -2396,16 +2168,6 @@ public interface Locator {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public UncheckOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* 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
@@ -2473,26 +2235,6 @@ public interface Locator {
return this;
}
}
class WaitForFunctionOptions {
/**
* Maximum time to wait for 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 to wait for 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 WaitForFunctionOptions setTimeout(double timeout) {
this.timeout = timeout;
return this;
}
}
/**
* When the locator points to a list of elements, this returns an array of locators, pointing to their respective elements.
*
@@ -3158,54 +2900,6 @@ public interface Locator {
* @since v1.18
*/
void dragTo(Locator target, DragToOptions options);
/**
* Simulate an external drag-and-drop of files or clipboard-like data onto this locator.
*
* <p> <strong>Details</strong>
*
* <p> Dispatches the native {@code dragenter}, {@code dragover}, and {@code drop} events at the center of the target element
* with a synthetic [DataTransfer] carrying the provided files and/or data entries. Works cross-browser by constructing the
* [DataTransfer] in the page context.
*
* <p> If the target element's {@code dragover} listener does not call {@code preventDefault()}, the target is considered to
* have rejected the drop: Playwright dispatches {@code dragleave} and this method throws.
*
* <p> <strong>Usage</strong>
*
* <p> Drop a file buffer onto an upload area:
*
* <p> Drop plain text and a URL together:
*
* @param payload Data to drop onto the target. Provide {@code files} (file paths or in-memory buffers), {@code data} (a mime-type →
* string map for clipboard-like content such as {@code text/plain}, {@code text/html}, {@code text/uri-list}), or both.
* @since v1.60
*/
default void drop(DropPayload payload) {
drop(payload, null);
}
/**
* Simulate an external drag-and-drop of files or clipboard-like data onto this locator.
*
* <p> <strong>Details</strong>
*
* <p> Dispatches the native {@code dragenter}, {@code dragover}, and {@code drop} events at the center of the target element
* with a synthetic [DataTransfer] carrying the provided files and/or data entries. Works cross-browser by constructing the
* [DataTransfer] in the page context.
*
* <p> If the target element's {@code dragover} listener does not call {@code preventDefault()}, the target is considered to
* have rejected the drop: Playwright dispatches {@code dragleave} and this method throws.
*
* <p> <strong>Usage</strong>
*
* <p> Drop a file buffer onto an upload area:
*
* <p> Drop plain text and a URL together:
*
* @param payload Data to drop onto the target. Provide {@code files} (file paths or in-memory buffers), {@code data} (a mime-type →
* string map for clipboard-like content such as {@code text/plain}, {@code text/html}, {@code text/uri-list}), or both.
* @since v1.60
*/
void drop(DropPayload payload, DropOptions options);
/**
* Resolves given locator to the first matching DOM element. If there are no matching elements, waits for one. If multiple
* elements match the locator, throws.
@@ -4185,28 +3879,13 @@ public interface Locator {
* @since v1.27
*/
Locator getByTitle(Pattern text, GetByTitleOptions options);
/**
* Hides the element highlight previously added by {@link com.microsoft.playwright.Locator#highlight Locator.highlight()}.
*
* @since v1.60
*/
void hideHighlight();
/**
* Highlight the corresponding element(s) on the screen. Useful for debugging, don't commit the code that uses {@link
* com.microsoft.playwright.Locator#highlight Locator.highlight()}.
*
* @since v1.20
*/
default AutoCloseable highlight() {
return highlight(null);
}
/**
* Highlight the corresponding element(s) on the screen. Useful for debugging, don't commit the code that uses {@link
* com.microsoft.playwright.Locator#highlight Locator.highlight()}.
*
* @since v1.20
*/
AutoCloseable highlight(HighlightOptions options);
void highlight();
/**
* Hover over the matching element.
*
@@ -5856,83 +5535,5 @@ public interface Locator {
* @since v1.16
*/
void waitFor(WaitForOptions options);
/**
* Returns when {@code expression} returns a truthy value, called with the matching element as a first argument, and {@code
* arg} as a second argument.
*
* <p> This is a generic way to wait for an element to reach a custom condition without asserting it. The locator is
* re-resolved on each retry, so it tolerates the element being re-rendered while waiting.
*
* <p> If {@code expression} returns a <a
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, this method
* will wait for the promise to resolve before checking its value.
*
* <p> If {@code expression} throws or rejects, this method throws.
*
* <p> <strong>Usage</strong>
*
* <p> Wait for an attribute to appear:
*
* <p> Passing argument to {@code expression}:
*
* @param expression JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the function is
* automatically invoked.
* @param arg Optional argument to pass to {@code expression}.
* @since v1.62
*/
default void waitForFunction(String expression, Object arg) {
waitForFunction(expression, arg, null);
}
/**
* Returns when {@code expression} returns a truthy value, called with the matching element as a first argument, and {@code
* arg} as a second argument.
*
* <p> This is a generic way to wait for an element to reach a custom condition without asserting it. The locator is
* re-resolved on each retry, so it tolerates the element being re-rendered while waiting.
*
* <p> If {@code expression} returns a <a
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, this method
* will wait for the promise to resolve before checking its value.
*
* <p> If {@code expression} throws or rejects, this method throws.
*
* <p> <strong>Usage</strong>
*
* <p> Wait for an attribute to appear:
*
* <p> Passing argument to {@code expression}:
*
* @param expression JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the function is
* automatically invoked.
* @since v1.62
*/
default void waitForFunction(String expression) {
waitForFunction(expression, null);
}
/**
* Returns when {@code expression} returns a truthy value, called with the matching element as a first argument, and {@code
* arg} as a second argument.
*
* <p> This is a generic way to wait for an element to reach a custom condition without asserting it. The locator is
* re-resolved on each retry, so it tolerates the element being re-rendered while waiting.
*
* <p> If {@code expression} returns a <a
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, this method
* will wait for the promise to resolve before checking its value.
*
* <p> If {@code expression} throws or rejects, this method throws.
*
* <p> <strong>Usage</strong>
*
* <p> Wait for an attribute to appear:
*
* <p> Passing argument to {@code expression}:
*
* @param expression JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the function is
* automatically invoked.
* @param arg Optional argument to pass to {@code expression}.
* @since v1.62
*/
void waitForFunction(String expression, Object arg, WaitForFunctionOptions options);
}
@@ -435,13 +435,6 @@ public interface Page extends AutoCloseable {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -491,16 +484,6 @@ public interface Page extends AutoCloseable {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public CheckOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -562,13 +545,6 @@ public interface Page extends AutoCloseable {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -649,16 +625,6 @@ public interface Page extends AutoCloseable {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ClickOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -744,13 +710,6 @@ public interface Page extends AutoCloseable {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -824,16 +783,6 @@ public interface Page extends AutoCloseable {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public DblclickOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -906,13 +855,6 @@ public interface Page extends AutoCloseable {
* @deprecated This option has no effect.
*/
public Boolean noWaitAfter;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* Clicks on the source element at this point relative to the top-left corner of the element's padding box. If not
* specified, some visible point of the element is used.
@@ -962,16 +904,6 @@ public interface Page extends AutoCloseable {
this.noWaitAfter = noWaitAfter;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public DragAndDropOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* Clicks on the source element at this point relative to the top-left corner of the element's padding box. If not
* specified, some visible point of the element is used.
@@ -1126,6 +1058,20 @@ public interface Page extends AutoCloseable {
return this;
}
}
class ExposeBindingOptions {
/**
* @deprecated This option will be removed in the future.
*/
public Boolean handle;
/**
* @deprecated This option will be removed in the future.
*/
public ExposeBindingOptions setHandle(boolean handle) {
this.handle = handle;
return this;
}
}
class FillOptions {
/**
* Whether to bypass the <a href="https://playwright.dev/java/docs/actionability">actionability</a> checks. Defaults to
@@ -1304,13 +1250,6 @@ public interface Page extends AutoCloseable {
* <p> Learn more about <a href="https://www.w3.org/TR/wai-aria-1.2/#aria-checked">{@code aria-checked}</a>.
*/
public Boolean checked;
/**
* Option to match the <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>. By
* default, matching is case-insensitive and searches for a substring, use {@code exact} to control this behavior.
*
* <p> Learn more about <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>.
*/
public Object description;
/**
* An attribute that is usually set by {@code aria-disabled} or {@code disabled}.
*
@@ -1319,8 +1258,8 @@ public interface Page extends AutoCloseable {
*/
public Boolean disabled;
/**
* Whether {@code name} and {@code description} are matched exactly: case-sensitive and whole-string. Defaults to false.
* Ignored when the value is a regular expression. Note that exact match still trims whitespace.
* Whether {@code name} is matched exactly: case-sensitive and whole-string. Defaults to false. Ignored when {@code name}
* is a regular expression. Note that exact match still trims whitespace.
*/
public Boolean exact;
/**
@@ -1372,26 +1311,6 @@ public interface Page extends AutoCloseable {
this.checked = checked;
return this;
}
/**
* Option to match the <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>. By
* default, matching is case-insensitive and searches for a substring, use {@code exact} to control this behavior.
*
* <p> Learn more about <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>.
*/
public GetByRoleOptions setDescription(String description) {
this.description = description;
return this;
}
/**
* Option to match the <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>. By
* default, matching is case-insensitive and searches for a substring, use {@code exact} to control this behavior.
*
* <p> Learn more about <a href="https://w3c.github.io/accname/#dfn-accessible-description">accessible description</a>.
*/
public GetByRoleOptions setDescription(Pattern description) {
this.description = description;
return this;
}
/**
* An attribute that is usually set by {@code aria-disabled} or {@code disabled}.
*
@@ -1403,8 +1322,8 @@ public interface Page extends AutoCloseable {
return this;
}
/**
* Whether {@code name} and {@code description} are matched exactly: case-sensitive and whole-string. Defaults to false.
* Ignored when the value is a regular expression. Note that exact match still trims whitespace.
* Whether {@code name} is matched exactly: case-sensitive and whole-string. Defaults to false. Ignored when {@code name}
* is a regular expression. Note that exact match still trims whitespace.
*/
public GetByRoleOptions setExact(boolean exact) {
this.exact = exact;
@@ -1691,13 +1610,6 @@ public interface Page extends AutoCloseable {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -1757,16 +1669,6 @@ public interface Page extends AutoCloseable {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public HoverOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -2683,9 +2585,7 @@ public interface Page extends AutoCloseable {
*/
public Path path;
/**
* The quality of the image, between 0-100. Not applicable to {@code png} images. For {@code jpeg} the default is {@code
* 80}. For {@code webp}, a quality of {@code 100} (the default) produces a lossless image, while lower values use lossy
* compression.
* The quality of the image, between 0-100. Not applicable to {@code png} images.
*/
public Integer quality;
/**
@@ -2794,9 +2694,7 @@ public interface Page extends AutoCloseable {
return this;
}
/**
* The quality of the image, between 0-100. Not applicable to {@code png} images. For {@code jpeg} the default is {@code
* 80}. For {@code webp}, a quality of {@code 100} (the default) produces a lossless image, while lower values use lossy
* compression.
* The quality of the image, between 0-100. Not applicable to {@code png} images.
*/
public ScreenshotOptions setQuality(int quality) {
this.quality = quality;
@@ -2912,13 +2810,6 @@ public interface Page extends AutoCloseable {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -2968,16 +2859,6 @@ public interface Page extends AutoCloseable {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public SetCheckedOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -3100,13 +2981,6 @@ public interface Page extends AutoCloseable {
}
}
class AriaSnapshotOptions {
/**
* When {@code true}, appends each element's bounding box as {@code [box=x,y,width,height]} to the snapshot. Coordinates
* are relative to the viewport, in CSS pixels, as returned by <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect">{@code
* Element.getBoundingClientRect()}</a>. Defaults to {@code false}.
*/
public Boolean boxes;
/**
* When specified, limits the depth of the snapshot.
*/
@@ -3124,16 +2998,6 @@ public interface Page extends AutoCloseable {
*/
public Double timeout;
/**
* When {@code true}, appends each element's bounding box as {@code [box=x,y,width,height]} to the snapshot. Coordinates
* are relative to the viewport, in CSS pixels, as returned by <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect">{@code
* Element.getBoundingClientRect()}</a>. Defaults to {@code false}.
*/
public AriaSnapshotOptions setBoxes(boolean boxes) {
this.boxes = boxes;
return this;
}
/**
* When specified, limits the depth of the snapshot.
*/
@@ -3181,13 +3045,6 @@ public interface Page extends AutoCloseable {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -3247,16 +3104,6 @@ public interface Page extends AutoCloseable {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public TapOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -3389,13 +3236,6 @@ public interface Page extends AutoCloseable {
* element.
*/
public Position position;
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public ScrollMode scroll;
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -3445,16 +3285,6 @@ public interface Page extends AutoCloseable {
this.position = position;
return this;
}
/**
* Controls whether Playwright scrolls the element into view before performing the action. Defaults to {@code "auto"},
* which scrolls the element into view when necessary, including scrolling nested scrollable containers. When set to {@code
* "none"}, Playwright does not scroll the element and the action fails if the element is not already in the viewport. This
* is useful to assert that an element is reachable by the user without additional scrolling.
*/
public UncheckOptions setScroll(ScrollMode scroll) {
this.scroll = scroll;
return this;
}
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
* element, the call throws an exception.
@@ -4669,7 +4499,7 @@ public interface Page extends AutoCloseable {
* <p> {@code ElementHandle} instances can be passed as an argument to the {@link com.microsoft.playwright.Page#evaluate
* Page.evaluate()}:
* <pre>{@code
* ElementHandle bodyHandle = page.evaluateHandle("document.body");
* ElementHandle bodyHandle = page.evaluate("document.body");
* String html = (String) page.evaluate("([body, suffix]) => body.innerHTML + suffix", Arrays.asList(bodyHandle, "hello"));
* bodyHandle.dispose();
* }</pre>
@@ -4711,7 +4541,7 @@ public interface Page extends AutoCloseable {
* <p> {@code ElementHandle} instances can be passed as an argument to the {@link com.microsoft.playwright.Page#evaluate
* Page.evaluate()}:
* <pre>{@code
* ElementHandle bodyHandle = page.evaluateHandle("document.body");
* ElementHandle bodyHandle = page.evaluate("document.body");
* String html = (String) page.evaluate("([body, suffix]) => body.innerHTML + suffix", Arrays.asList(bodyHandle, "hello"));
* bodyHandle.dispose();
* }</pre>
@@ -4846,7 +4676,57 @@ public interface Page extends AutoCloseable {
* @param callback Callback function that will be called in the Playwright's context.
* @since v1.8
*/
AutoCloseable exposeBinding(String name, BindingCallback callback);
default AutoCloseable exposeBinding(String name, BindingCallback callback) {
return exposeBinding(name, callback, null);
}
/**
* The method adds a function called {@code name} on the {@code window} object of every frame in this page. When called,
* the function executes {@code callback} and returns a <a
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a> which
* resolves to the return value of {@code callback}. If the {@code callback} returns a <a
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, it will be
* awaited.
*
* <p> The first argument of the {@code callback} function contains information about the caller: {@code { browserContext:
* BrowserContext, page: Page, frame: Frame }}.
*
* <p> See {@link com.microsoft.playwright.BrowserContext#exposeBinding BrowserContext.exposeBinding()} for the context-wide
* version.
*
* <p> <strong>NOTE:</strong> Functions installed via {@link com.microsoft.playwright.Page#exposeBinding Page.exposeBinding()} survive navigations.
*
* <p> <strong>Usage</strong>
*
* <p> An example of exposing page URL to all frames in a page:
* <pre>{@code
* import com.microsoft.playwright.*;
*
* public class Example {
* public static void main(String[] args) {
* try (Playwright playwright = Playwright.create()) {
* BrowserType webkit = playwright.webkit();
* Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false));
* BrowserContext context = browser.newContext();
* Page page = context.newPage();
* page.exposeBinding("pageURL", (source, args) -> source.page().url());
* page.setContent("<script>\n" +
* " async function onClick() {\n" +
* " document.querySelector('div').textContent = await window.pageURL();\n" +
* " }\n" +
* "</script>\n" +
* "<button onclick=\"onClick()\">Click me</button>\n" +
* "<div></div>");
* page.click("button");
* }
* }
* }
* }</pre>
*
* @param name Name of the function on the window object.
* @param callback Callback function that will be called in the Playwright's context.
* @since v1.8
*/
AutoCloseable exposeBinding(String name, BindingCallback callback, ExposeBindingOptions options);
/**
* The method adds a function called {@code name} on the {@code window} object of every frame in the page. When called, the
* function executes {@code callback} and returns a <a
@@ -5604,11 +5484,6 @@ public interface Page extends AutoCloseable {
*
* <p> Navigate to the previous page in history.
*
* <p> <strong>NOTE:</strong> **Testing Back/Forward Cache (BFCache) is not supported.** By default, Playwright disables the Back/Forward Cache
* across all browsers. Even if explicitly enabled, Playwright's internal state relies on network-level navigation events.
* Because BFCache restores unfreeze the DOM without firing these events, using {@code page.goBack()} or {@code
* page.goForward()} to trigger a BFCache restore will result in timeouts and a desynchronized {@code Page} state.
*
* @since v1.8
*/
default Response goBack() {
@@ -5620,11 +5495,6 @@ public interface Page extends AutoCloseable {
*
* <p> Navigate to the previous page in history.
*
* <p> <strong>NOTE:</strong> **Testing Back/Forward Cache (BFCache) is not supported.** By default, Playwright disables the Back/Forward Cache
* across all browsers. Even if explicitly enabled, Playwright's internal state relies on network-level navigation events.
* Because BFCache restores unfreeze the DOM without firing these events, using {@code page.goBack()} or {@code
* page.goForward()} to trigger a BFCache restore will result in timeouts and a desynchronized {@code Page} state.
*
* @since v1.8
*/
Response goBack(GoBackOptions options);
@@ -5634,11 +5504,6 @@ public interface Page extends AutoCloseable {
*
* <p> Navigate to the next page in history.
*
* <p> <strong>NOTE:</strong> **Testing Back/Forward Cache (BFCache) is not supported.** By default, Playwright disables the Back/Forward Cache
* across all browsers. Even if explicitly enabled, Playwright's internal state relies on network-level navigation events.
* Because BFCache restores unfreeze the DOM without firing these events, using {@code page.goBack()} or {@code
* page.goForward()} to trigger a BFCache restore will result in timeouts and a desynchronized {@code Page} state.
*
* @since v1.8
*/
default Response goForward() {
@@ -5650,11 +5515,6 @@ public interface Page extends AutoCloseable {
*
* <p> Navigate to the next page in history.
*
* <p> <strong>NOTE:</strong> **Testing Back/Forward Cache (BFCache) is not supported.** By default, Playwright disables the Back/Forward Cache
* across all browsers. Even if explicitly enabled, Playwright's internal state relies on network-level navigation events.
* Because BFCache restores unfreeze the DOM without firing these events, using {@code page.goBack()} or {@code
* page.goForward()} to trigger a BFCache restore will result in timeouts and a desynchronized {@code Page} state.
*
* @since v1.8
*/
Response goForward(GoForwardOptions options);
@@ -5737,13 +5597,6 @@ public interface Page extends AutoCloseable {
* @since v1.8
*/
Response navigate(String url, NavigateOptions options);
/**
* Hide all locator highlight overlays previously added by {@link com.microsoft.playwright.Locator#highlight
* Locator.highlight()} on this page.
*
* @since v1.60
*/
void hideHighlight();
/**
* This method hovers over an element matching {@code selector} by performing the following steps:
* <ol>
@@ -5968,18 +5821,6 @@ public interface Page extends AutoCloseable {
* @since v1.59
*/
void clearPageErrors();
/**
* Provides access to the page's {@code localStorage} for the current origin. See {@code WebStorage}.
*
* @since v1.61
*/
WebStorage localStorage();
/**
* Provides access to the page's {@code sessionStorage} for the current origin. See {@code WebStorage}.
*
* @since v1.61
*/
WebStorage sessionStorage();
/**
* Returns up to (currently) 200 last console messages from this page. See {@link
* com.microsoft.playwright.Page#onConsoleMessage Page.onConsoleMessage()} for more details.
@@ -7724,8 +7565,8 @@ public interface Page extends AutoCloseable {
* <p> When all steps combined have not finished during the specified {@code timeout}, this method throws a {@code
* TimeoutError}. Passing zero timeout disables this.
*
* <p> <strong>NOTE:</strong> {@link com.microsoft.playwright.Page#tap Page.tap()} will throw if the {@code hasTouch} option of the browser context is
* false.
* <p> <strong>NOTE:</strong> {@link com.microsoft.playwright.Page#tap Page.tap()} the method will throw if {@code hasTouch} option of the browser
* context is false.
*
* @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used.
* @since v1.8
@@ -7747,8 +7588,8 @@ public interface Page extends AutoCloseable {
* <p> When all steps combined have not finished during the specified {@code timeout}, this method throws a {@code
* TimeoutError}. Passing zero timeout disables this.
*
* <p> <strong>NOTE:</strong> {@link com.microsoft.playwright.Page#tap Page.tap()} will throw if the {@code hasTouch} option of the browser context is
* false.
* <p> <strong>NOTE:</strong> {@link com.microsoft.playwright.Page#tap Page.tap()} the method will throw if {@code hasTouch} option of the browser
* context is false.
*
* @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used.
* @since v1.8
@@ -27,7 +27,7 @@ import java.util.function.Consumer;
public interface Screencast {
class StartOptions {
/**
* Callback that receives JPEG-encoded frame data along with the page viewport size at the time of capture.
* Callback that receives JPEG-encoded frame data.
*/
public Consumer<ScreencastFrame> onFrame;
/**
@@ -38,16 +38,9 @@ public interface Screencast {
* The quality of the image, between 0-100.
*/
public Integer quality;
/**
* Specifies the dimensions of screencast frames. The actual frame is scaled to preserve the page's aspect ratio and may be
* smaller than these bounds. If a screencast is already active (e.g. started by tracing or video recording), the existing
* configuration takes precedence and the frame size may exceed these bounds or this option may be ignored. If not
* specified the size will be equal to page viewport scaled down to fit into 800×800.
*/
public Size size;
/**
* Callback that receives JPEG-encoded frame data along with the page viewport size at the time of capture.
* Callback that receives JPEG-encoded frame data.
*/
public StartOptions setOnFrame(Consumer<ScreencastFrame> onFrame) {
this.onFrame = onFrame;
@@ -67,25 +60,6 @@ public interface Screencast {
this.quality = quality;
return this;
}
/**
* Specifies the dimensions of screencast frames. The actual frame is scaled to preserve the page's aspect ratio and may be
* smaller than these bounds. If a screencast is already active (e.g. started by tracing or video recording), the existing
* configuration takes precedence and the frame size may exceed these bounds or this option may be ignored. If not
* specified the size will be equal to page viewport scaled down to fit into 800×800.
*/
public StartOptions setSize(int width, int height) {
return setSize(new Size(width, height));
}
/**
* Specifies the dimensions of screencast frames. The actual frame is scaled to preserve the page's aspect ratio and may be
* smaller than these bounds. If a screencast is already active (e.g. started by tracing or video recording), the existing
* configuration takes precedence and the frame size may exceed these bounds or this option may be ignored. If not
* specified the size will be equal to page viewport scaled down to fit into 800×800.
*/
public StartOptions setSize(Size size) {
this.size = size;
return this;
}
}
class ShowOverlayOptions {
/**
@@ -129,11 +103,6 @@ public interface Screencast {
}
}
class ShowActionsOptions {
/**
* Cursor decoration shown for pointer actions. {@code "pointer"} (the default) renders a mouse pointer that animates from
* the previous action point to the next one. {@code "none"} disables the cursor decoration.
*/
public ScreencastCursor cursor;
/**
* How long each annotation is displayed in milliseconds. Defaults to {@code 500}.
*/
@@ -147,14 +116,6 @@ public interface Screencast {
*/
public AnnotatePosition position;
/**
* Cursor decoration shown for pointer actions. {@code "pointer"} (the default) renders a mouse pointer that animates from
* the previous action point to the next one. {@code "none"} disables the cursor decoration.
*/
public ShowActionsOptions setCursor(ScreencastCursor cursor) {
this.cursor = cursor;
return this;
}
/**
* How long each annotation is displayed in milliseconds. Defaults to {@code 500}.
*/
@@ -1,39 +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;
public interface ScreencastFrame {
/**
* JPEG-encoded frame data.
*/
byte[] data();
/**
* The timestamp of when the frame was presented by the browser, in milliseconds since the Unix epoch.
*/
double timestamp();
/**
* Width of the page viewport at the time the frame was captured.
*/
int viewportWidth();
/**
* Height of the page viewport at the time the frame was captured.
*/
int viewportHeight();
}
@@ -201,8 +201,7 @@ public interface Selectors {
* Defines custom attribute name to be used in {@link com.microsoft.playwright.Page#getByTestId Page.getByTestId()}. {@code
* data-testid} is used by default.
*
* @param attributeName Test id attribute name. To match elements with any of several attributes, pass them as a comma-separated list, e.g.
* {@code "data-pw,data-ti"}.
* @param attributeName Test id attribute name.
* @since v1.27
*/
void setTestIdAttribute(String attributeName);
@@ -28,8 +28,8 @@ public interface Touchscreen {
/**
* Dispatches a {@code touchstart} and {@code touchend} event with a single touch at the position ({@code x},{@code y}).
*
* <p> <strong>NOTE:</strong> {@link com.microsoft.playwright.Touchscreen#tap Touchscreen.tap()} will throw if the {@code hasTouch} option of the
* browser context is false.
* <p> <strong>NOTE:</strong> {@link com.microsoft.playwright.Page#tap Page.tap()} the method will throw if {@code hasTouch} option of the browser
* context is false.
*
* @param x X coordinate relative to the main frame's viewport in CSS pixels.
* @param y Y coordinate relative to the main frame's viewport in CSS pixels.
@@ -18,7 +18,6 @@ package com.microsoft.playwright;
import com.microsoft.playwright.options.*;
import java.nio.file.Path;
import java.util.regex.Pattern;
/**
* API for collecting and saving Playwright traces. Playwright traces can be opened in <a
@@ -166,59 +165,6 @@ public interface Tracing {
return this;
}
}
class StartHarOptions {
/**
* Optional setting to control resource content management. If {@code omit} is specified, content is not persisted. If
* {@code attach} is specified, resources are persisted as separate files or entries in the ZIP archive. If {@code embed}
* is specified, content is stored inline the HAR file as per HAR specification. Defaults to {@code attach} for {@code
* .zip} output files and to {@code embed} for all other file extensions.
*/
public HarContentPolicy content;
/**
* When set to {@code minimal}, only record information necessary for routing from HAR. This omits sizes, timing, page,
* cookies, security and other types of HAR information that are not used when replaying from HAR. Defaults to {@code
* full}.
*/
public HarMode mode;
/**
* A glob or regex pattern to filter requests that are stored in the HAR. Defaults to none.
*/
public Object urlFilter;
/**
* Optional setting to control resource content management. If {@code omit} is specified, content is not persisted. If
* {@code attach} is specified, resources are persisted as separate files or entries in the ZIP archive. If {@code embed}
* is specified, content is stored inline the HAR file as per HAR specification. Defaults to {@code attach} for {@code
* .zip} output files and to {@code embed} for all other file extensions.
*/
public StartHarOptions setContent(HarContentPolicy content) {
this.content = content;
return this;
}
/**
* When set to {@code minimal}, only record information necessary for routing from HAR. This omits sizes, timing, page,
* cookies, security and other types of HAR information that are not used when replaying from HAR. Defaults to {@code
* full}.
*/
public StartHarOptions setMode(HarMode mode) {
this.mode = mode;
return this;
}
/**
* A glob or regex pattern to filter requests that are stored in the HAR. Defaults to none.
*/
public StartHarOptions setUrlFilter(String urlFilter) {
this.urlFilter = urlFilter;
return this;
}
/**
* A glob or regex pattern to filter requests that are stored in the HAR. Defaults to none.
*/
public StartHarOptions setUrlFilter(Pattern urlFilter) {
this.urlFilter = urlFilter;
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
@@ -382,48 +328,6 @@ public interface Tracing {
* @since v1.15
*/
void startChunk(StartChunkOptions options);
/**
* Start recording a HAR (HTTP Archive) of network activity in this context. The HAR file is written to disk when {@link
* com.microsoft.playwright.Tracing#stopHar Tracing.stopHar()} is called, or when the returned {@code Disposable} is
* disposed.
*
* <p> Only one HAR recording can be active at a time per {@code BrowserContext}.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* context.tracing().startHar(Paths.get("trace.har"));
* Page page = context.newPage();
* page.navigate("https://playwright.dev");
* context.tracing().stopHar();
* }</pre>
*
* @param path Path on the filesystem to write the HAR file to. If the file name ends with {@code .zip}, the HAR is saved as a zip
* archive with response bodies attached as separate files.
* @since v1.60
*/
default AutoCloseable startHar(Path path) {
return startHar(path, null);
}
/**
* Start recording a HAR (HTTP Archive) of network activity in this context. The HAR file is written to disk when {@link
* com.microsoft.playwright.Tracing#stopHar Tracing.stopHar()} is called, or when the returned {@code Disposable} is
* disposed.
*
* <p> Only one HAR recording can be active at a time per {@code BrowserContext}.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* context.tracing().startHar(Paths.get("trace.har"));
* Page page = context.newPage();
* page.navigate("https://playwright.dev");
* context.tracing().stopHar();
* }</pre>
*
* @param path Path on the filesystem to write the HAR file to. If the file name ends with {@code .zip}, the HAR is saved as a zip
* archive with response bodies attached as separate files.
* @since v1.60
*/
AutoCloseable startHar(Path path, StartHarOptions options);
/**
* <strong>NOTE:</strong> Use {@code test.step} instead when available.
*
@@ -504,12 +408,5 @@ public interface Tracing {
* @since v1.15
*/
void stopChunk(StopChunkOptions options);
/**
* Stop HAR recording and save the HAR file to the path given to {@link com.microsoft.playwright.Tracing#startHar
* Tracing.startHar()}.
*
* @since v1.60
*/
void stopHar();
}
@@ -16,7 +16,6 @@
package com.microsoft.playwright;
import com.microsoft.playwright.options.*;
/**
* {@code WebError} class represents an unhandled exception thrown in the page. It is dispatched via the {@link
@@ -44,11 +43,5 @@ public interface WebError {
* @since v1.38
*/
String error();
/**
*
*
* @since v1.60
*/
WebErrorLocation location();
}
@@ -16,7 +16,6 @@
package com.microsoft.playwright;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@@ -214,27 +213,6 @@ public interface WebSocketRoute {
* @since v1.48
*/
void send(byte[] message);
/**
* The list of WebSocket subprotocols requested by the page, as passed via the second argument to the <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/WebSocket">{@code WebSocket} constructor</a>.
* Corresponds to the {@code Sec-WebSocket-Protocol} request header.
*
* <p> Returns an empty array if no protocols were specified.
*
* <p> <strong>Usage</strong>
* <pre>{@code
* page.routeWebSocket("wss://example.com/ws", ws -> {
* if (ws.protocols().contains("chat.v2")) {
* ws.onMessage(frame -> ws.send("v2:" + frame.text()));
* } else {
* ws.close(1002, "Unsupported protocol");
* }
* });
* }</pre>
*
* @since v1.60
*/
List<String> protocols();
/**
* URL of the WebSocket created in the page.
*
@@ -1,73 +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;
import com.microsoft.playwright.options.*;
import java.util.*;
/**
* WebStorage exposes the page's {@code localStorage} or {@code sessionStorage} for the current origin via an async, <a
* href="https://developer.mozilla.org/en-US/docs/Web/API/Storage">browser-consistent</a> API.
*
* <p> Instances are accessed through {@link com.microsoft.playwright.Page#localStorage Page.localStorage()} and {@link
* com.microsoft.playwright.Page#sessionStorage Page.sessionStorage()}.
* <pre>{@code
* page.navigate("https://example.com");
* page.localStorage().setItem("token", "abc");
* String token = page.localStorage().getItem("token");
* List<WebStorageItem> all = page.localStorage().items();
* page.localStorage().removeItem("token");
* page.localStorage().clear();
* }</pre>
*/
public interface WebStorage {
/**
* Returns all items in the storage as name/value pairs.
*
* @since v1.61
*/
List<WebStorageItem> items();
/**
* Returns the value for the given {@code name} if present.
*
* @param name Name of the item to retrieve.
* @since v1.61
*/
String getItem(String name);
/**
* Sets the value for the given {@code name}. Overwrites any existing value for that name.
*
* @param name Name of the item to set.
* @param value New value for the item.
* @since v1.61
*/
void setItem(String name, String value);
/**
* Removes the item with the given {@code name}. No-op if the item is absent.
*
* @param name Name of the item to remove.
* @since v1.61
*/
void removeItem(String name);
/**
* Removes all items from the storage.
*
* @since v1.61
*/
void clear();
}
@@ -19,7 +19,6 @@ package com.microsoft.playwright.assertions;
import java.util.*;
import java.util.regex.Pattern;
import com.microsoft.playwright.options.AriaRole;
import com.microsoft.playwright.options.PseudoElement;
/**
* The {@code LocatorAssertions} class provides assertion methods that can be used to make assertions about the {@code
@@ -428,22 +427,11 @@ public interface LocatorAssertions {
}
}
class HasCSSOptions {
/**
* Pseudo-element to read computed styles from.
*/
public PseudoElement pseudo;
/**
* Time to retry the assertion for in milliseconds. Defaults to {@code 5000}.
*/
public Double timeout;
/**
* Pseudo-element to read computed styles from.
*/
public HasCSSOptions setPseudo(PseudoElement pseudo) {
this.pseudo = pseudo;
return this;
}
/**
* Time to retry the assertion for in milliseconds. Defaults to {@code 5000}.
*/
@@ -37,20 +37,6 @@ import java.util.regex.Pattern;
* }</pre>
*/
public interface PageAssertions {
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;
}
}
class HasTitleOptions {
/**
* Time to retry the assertion for in milliseconds. Defaults to {@code 5000}.
@@ -105,40 +91,6 @@ public interface PageAssertions {
* @since v1.20
*/
PageAssertions not();
/**
* Asserts that the page body 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).matchesAriaSnapshot("""
* - heading "todos"
* - textbox "What needs to be done?"
* """);
* }</pre>
*
* @since v1.60
*/
default void matchesAriaSnapshot(String expected) {
matchesAriaSnapshot(expected, null);
}
/**
* Asserts that the page body 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).matchesAriaSnapshot("""
* - heading "todos"
* - textbox "What needs to be done?"
* """);
* }</pre>
*
* @since v1.60
*/
void matchesAriaSnapshot(String expected, MatchesAriaSnapshotOptions options);
/**
* Ensures the page has the given title.
*
@@ -46,11 +46,6 @@ class APIRequestContextImpl extends ChannelOwner implements APIRequestContext {
this.tracing = connection.getExistingObject(initializer.getAsJsonObject("tracing").get("guid").getAsString());
}
@Override
public com.microsoft.playwright.Tracing tracing() {
return tracing;
}
@Override
public APIResponse delete(String url, RequestOptions options) {
return fetch(url, ensureOptions(options, "DELETE"));
@@ -22,9 +22,6 @@ import com.google.gson.reflect.TypeToken;
import com.microsoft.playwright.APIResponse;
import com.microsoft.playwright.PlaywrightException;
import com.microsoft.playwright.options.HttpHeader;
import com.microsoft.playwright.options.SecurityDetails;
import com.microsoft.playwright.options.ServerAddr;
import com.microsoft.playwright.options.Timing;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
@@ -88,22 +85,6 @@ class APIResponseImpl implements APIResponse {
return status == 0 || (status >= 200 && status <= 299);
}
@Override
public SecurityDetails securityDetails() {
if (!initializer.has("securityDetails")) {
return null;
}
return gson().fromJson(initializer.get("securityDetails"), SecurityDetails.class);
}
@Override
public ServerAddr serverAddr() {
if (!initializer.has("serverAddr")) {
return null;
}
return gson().fromJson(initializer.get("serverAddr"), ServerAddr.class);
}
@Override
public int status() {
return initializer.get("status").getAsInt();
@@ -119,26 +100,6 @@ class APIResponseImpl implements APIResponse {
return new String(body(), StandardCharsets.UTF_8);
}
@Override
public Timing timing() {
Timing timing;
if (initializer.has("timing")) {
timing = gson().fromJson(initializer.get("timing"), Timing.class);
} else {
timing = new Timing();
timing.startTime = -1;
timing.domainLookupStart = -1;
timing.domainLookupEnd = -1;
timing.connectStart = -1;
timing.secureConnectionStart = -1;
timing.connectEnd = -1;
timing.requestStart = -1;
timing.responseStart = -1;
}
timing.responseEnd = initializer.has("responseEndTiming") ? initializer.get("responseEndTiming").getAsDouble() : -1;
return timing;
}
@Override
public String url() {
return initializer.get("url").getAsString();
@@ -57,14 +57,7 @@ abstract class AssertionsBase {
}
FrameExpectResult result = doExpect(expression, expectOptions, title);
if (result.matches == isNot) {
Object actual;
if (result.received == null) {
actual = null;
} else if (result.received.value != null) {
actual = Serialization.deserialize(result.received.value);
} else {
actual = result.received.ariaSnapshot;
}
Object actual = result.received == null ? null : Serialization.deserialize(result.received);
String log = (result.log == null) ? "" : String.join("\n", result.log);
if (!log.isEmpty()) {
log = "\nCall log:\n" + log;
@@ -46,7 +46,6 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
private final DebuggerImpl debugger;
private final APIRequestContextImpl request;
private final ClockImpl clock;
private final CredentialsImpl credentials;
final List<PageImpl> pages = new ArrayList<>();
final Router routes = new Router();
@@ -69,18 +68,23 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
}
private final ListenerCollection<EventType> listeners = new ListenerCollection<>(eventSubscriptions(), this);
final TimeoutSettings timeoutSettings = new TimeoutSettings();
final Map<String, HarRecorder> harRecorders = new HashMap<>();
static class HarRecorder {
final Path path;
final HarContentPolicy contentPolicy;
HarRecorder(Path har, HarContentPolicy policy) {
path = har;
contentPolicy = policy;
}
}
enum EventType {
CLOSE,
CONSOLE,
DIALOG,
DOWNLOAD,
FRAMEATTACHED,
FRAMEDETACHED,
FRAMENAVIGATED,
PAGE,
PAGECLOSE,
PAGELOAD,
WEBERROR,
REQUEST,
REQUESTFAILED,
@@ -95,7 +99,6 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
request = connection.getExistingObject(initializer.getAsJsonObject("requestContext").get("guid").getAsString());
request.timeoutSettings = timeoutSettings;
clock = new ClockImpl(this);
credentials = new CredentialsImpl(this);
closePromise = new WaitableEvent<>(listeners, EventType.CLOSE);
}
@@ -136,20 +139,6 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
public void offBackgroundPage(Consumer<Page> handler) {
}
@Override
public void onDownload(Consumer<Download> handler) {
listeners.add(EventType.DOWNLOAD, handler);
}
@Override
public void offDownload(Consumer<Download> handler) {
listeners.remove(EventType.DOWNLOAD, handler);
}
void notifyDownload(Download download) {
listeners.notify(EventType.DOWNLOAD, download);
}
@Override
public void onClose(Consumer<BrowserContext> handler) {
listeners.add(EventType.CLOSE, handler);
@@ -190,76 +179,6 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
listeners.remove(EventType.PAGE, handler);
}
@Override
public void onFrameAttached(Consumer<Frame> handler) {
listeners.add(EventType.FRAMEATTACHED, handler);
}
@Override
public void offFrameAttached(Consumer<Frame> handler) {
listeners.remove(EventType.FRAMEATTACHED, handler);
}
void notifyFrameAttached(FrameImpl frame) {
listeners.notify(EventType.FRAMEATTACHED, frame);
}
@Override
public void onFrameDetached(Consumer<Frame> handler) {
listeners.add(EventType.FRAMEDETACHED, handler);
}
@Override
public void offFrameDetached(Consumer<Frame> handler) {
listeners.remove(EventType.FRAMEDETACHED, handler);
}
void notifyFrameDetached(FrameImpl frame) {
listeners.notify(EventType.FRAMEDETACHED, frame);
}
@Override
public void onFrameNavigated(Consumer<Frame> handler) {
listeners.add(EventType.FRAMENAVIGATED, handler);
}
@Override
public void offFrameNavigated(Consumer<Frame> handler) {
listeners.remove(EventType.FRAMENAVIGATED, handler);
}
void notifyFrameNavigated(FrameImpl frame) {
listeners.notify(EventType.FRAMENAVIGATED, frame);
}
@Override
public void onPageClose(Consumer<Page> handler) {
listeners.add(EventType.PAGECLOSE, handler);
}
@Override
public void offPageClose(Consumer<Page> handler) {
listeners.remove(EventType.PAGECLOSE, handler);
}
void notifyPageClose(PageImpl page) {
listeners.notify(EventType.PAGECLOSE, page);
}
@Override
public void onPageLoad(Consumer<Page> handler) {
listeners.add(EventType.PAGELOAD, handler);
}
@Override
public void offPageLoad(Consumer<Page> handler) {
listeners.remove(EventType.PAGELOAD, handler);
}
void notifyPageLoad(PageImpl page) {
listeners.notify(EventType.PAGELOAD, page);
}
@Override
public void onWebError(Consumer<WebError> handler) {
listeners.add(EventType.WEBERROR, handler);
@@ -315,11 +234,6 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
return clock;
}
@Override
public Credentials credentials() {
return credentials;
}
private <T> T waitForEventWithTimeout(EventType eventType, Runnable code, Predicate<T> predicate, Double timeout) {
List<Waitable<T>> waitables = new ArrayList<>();
waitables.add(new WaitableEvent<>(listeners, eventType, predicate));
@@ -370,7 +284,27 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
}
closeReason = options.reason;
request.dispose(convertType(options, APIRequestContext.DisposeOptions.class));
tracing.exportAllHars();
for (Map.Entry<String, HarRecorder> entry : harRecorders.entrySet()) {
JsonObject params = new JsonObject();
params.addProperty("harId", entry.getKey());
JsonObject json = sendMessage("harExport", params, NO_TIMEOUT).getAsJsonObject();
ArtifactImpl artifact = connection.getExistingObject(json.getAsJsonObject("artifact").get("guid").getAsString());
// Server side will compress artifact if content is attach or if file is .zip.
HarRecorder harParams = entry.getValue();
boolean isCompressed = harParams.contentPolicy == HarContentPolicy.ATTACH || harParams.path.toString().endsWith(".zip");
boolean needCompressed = harParams.path.toString().endsWith(".zip");
if (isCompressed && !needCompressed) {
String tmpPath = harParams.path + ".tmp";
artifact.saveAs(Paths.get(tmpPath));
JsonObject unzipParams = new JsonObject();
unzipParams.addProperty("zipFile", tmpPath);
unzipParams.addProperty("harFile", harParams.path.toString());
connection.localUtils.sendMessage("harUnzip", unzipParams, NO_TIMEOUT);
} else {
artifact.saveAs(harParams.path);
}
artifact.delete();
}
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
sendMessage("close", params, NO_TIMEOUT);
}
@@ -458,11 +392,11 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
}
@Override
public AutoCloseable exposeBinding(String name, BindingCallback playwrightBinding) {
return exposeBindingImpl(name, playwrightBinding);
public AutoCloseable exposeBinding(String name, BindingCallback playwrightBinding, ExposeBindingOptions options) {
return exposeBindingImpl(name, playwrightBinding, options);
}
private AutoCloseable exposeBindingImpl(String name, BindingCallback playwrightBinding) {
private AutoCloseable exposeBindingImpl(String name, BindingCallback playwrightBinding, ExposeBindingOptions options) {
if (bindings.containsKey(name)) {
throw new PlaywrightException("Function \"" + name + "\" has been already registered");
}
@@ -475,13 +409,16 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
JsonObject params = new JsonObject();
params.addProperty("name", name);
if (options != null && options.handle != null && options.handle) {
params.addProperty("needsHandle", true);
}
JsonObject result = sendMessage("exposeBinding", params, NO_TIMEOUT).getAsJsonObject();
return connection.getExistingObject(result.getAsJsonObject("disposable").get("guid").getAsString());
}
@Override
public AutoCloseable exposeFunction(String name, FunctionCallback playwrightFunction) {
return exposeBindingImpl(name, (BindingCallback.Source source, Object... args) -> playwrightFunction.call(args));
return exposeBindingImpl(name, (BindingCallback.Source source, Object... args) -> playwrightFunction.call(args), null);
}
@Override
@@ -578,7 +515,24 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
if (contentPolicy == null) {
contentPolicy = Utils.convertType(options.updateContent, HarContentPolicy.class);
}
tracing.recordIntoHar(page, har, options.url, contentPolicy, options.updateMode, null);
if (contentPolicy == null) {
contentPolicy = HarContentPolicy.ATTACH;
}
JsonObject params = new JsonObject();
if (page != null) {
params.add("page", page.toProtocolRef());
}
JsonObject recordHarArgs = new JsonObject();
recordHarArgs.addProperty("zip", har.toString().endsWith(".zip"));
recordHarArgs.addProperty("content", contentPolicy.name().toLowerCase());
recordHarArgs.addProperty("mode", (options.updateMode == null ? HarMode.MINIMAL : options.updateMode).name().toLowerCase());
addHarUrlFilter(recordHarArgs, options.url);
params.add("options", recordHarArgs);
JsonObject json = sendMessage("harStart", params, NO_TIMEOUT).getAsJsonObject();
String harId = json.get("harId").getAsString();
harRecorders.put(harId, new HarRecorder(har, contentPolicy));
}
@Override
@@ -864,11 +818,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
} catch (PlaywrightException e) {
page = null;
}
WebErrorLocation location = null;
if (params.has("location")) {
location = gson().fromJson(params.getAsJsonObject("location"), WebErrorLocation.class);
}
listeners.notify(BrowserContextImpl.EventType.WEBERROR, new WebErrorImpl(page, errorStr, location));
listeners.notify(BrowserContextImpl.EventType.WEBERROR, new WebErrorImpl(page, errorStr));
if (page != null) {
page.listeners.notify(PageImpl.EventType.PAGEERROR, errorStr);
}
@@ -43,7 +43,6 @@ class BrowserImpl extends ChannelOwner implements Browser {
String closeReason;
enum EventType {
CONTEXT,
DISCONNECTED,
}
@@ -51,16 +50,6 @@ class BrowserImpl extends ChannelOwner implements Browser {
super(parent, type, guid, initializer);
}
@Override
public void onContext(Consumer<BrowserContext> handler) {
listeners.add(EventType.CONTEXT, handler);
}
@Override
public void offContext(Consumer<BrowserContext> handler) {
listeners.remove(EventType.CONTEXT, handler);
}
@Override
public void onDisconnected(Consumer<Browser> handler) {
listeners.add(EventType.DISCONNECTED, handler);
@@ -313,7 +302,6 @@ class BrowserImpl extends ChannelOwner implements Browser {
context.tracing().setTracesDir(tracePath);
browserType.playwright.selectors.contextsForSelectors.add(context);
}
listeners.notify(EventType.CONTEXT, context);
}
private void didClose() {
@@ -112,8 +112,8 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
@Override
public Browser connectOverCDP(String endpointURL, ConnectOverCDPOptions options) {
if (!"chromium".equals(name()) && !"webkit".equals(name())) {
throw new PlaywrightException("Connecting over CDP is only supported in Chromium and WebKit.");
if (!"chromium".equals(name())) {
throw new PlaywrightException("Connecting over CDP is only supported in Chromium.");
}
if (options == null) {
options = new ConnectOverCDPOptions();
@@ -110,14 +110,6 @@ class ChannelOwner extends LoggingSupport {
return connection.sendMessageAsync(guid, method, params);
}
// Fire-and-forget: silently drop if the object was collected.
void sendMessageNoReply(String method, JsonObject params) {
if (wasCollected) {
return;
}
connection.sendMessageNoReply(guid, method, params);
}
JsonElement sendMessage(String method) {
return sendMessage(method, new JsonObject(), NO_TIMEOUT);
}
@@ -125,12 +117,11 @@ class ChannelOwner extends LoggingSupport {
JsonElement sendMessage(String method, JsonObject params, Double timeout) {
checkNotCollected();
if (timeout != null) {
// Timeout is passed in the message metadata, remove potential leftover from serialized options.
params.remove("timeout");
params.addProperty("timeout", timeout);
} else if (params.has("timeout")) {
throw new PlaywrightException("Internal error: timeout must be passed explicitly.");
}
return connection.sendMessage(guid, method, params, timeout);
return connection.sendMessage(guid, method, params);
}
private void checkNotCollected() {
@@ -39,7 +39,6 @@ class Message {
JsonObject params;
JsonElement result;
SerializedError error;
JsonObject errorDetails;
JsonArray log;
@Override
@@ -129,28 +128,17 @@ public class Connection {
}
public JsonElement sendMessage(String guid, String method, JsonObject params) {
return sendMessage(guid, method, params, null);
}
public JsonElement sendMessage(String guid, String method, JsonObject params, Double timeout) {
return root.runUntil(() -> {}, internalSendMessage(guid, method, params, timeout, true, true));
return root.runUntil(() -> {}, sendMessageAsync(guid, method, params));
}
public WaitableResult<JsonElement> sendMessageAsync(String guid, String method, JsonObject params) {
return internalSendMessage(guid, method, params, null, true, true);
return internalSendMessage(guid, method, params, true);
}
// Fire-and-forget: the server never replies.
public void sendMessageNoReply(String guid, String method, JsonObject params) {
internalSendMessage(guid, method, params, null, false, false);
}
private WaitableResult<JsonElement> internalSendMessage(String guid, String method, JsonObject params, Double timeout, boolean sendStack, boolean expectsReply) {
private WaitableResult<JsonElement> internalSendMessage(String guid, String method, JsonObject params, boolean sendStack) {
int id = ++lastId;
WaitableResult<JsonElement> result = new WaitableResult<>();
if (expectsReply) {
callbacks.put(id, result);
}
callbacks.put(id, result);
JsonObject message = new JsonObject();
message.addProperty("id", id);
message.addProperty("guid", guid);
@@ -158,9 +146,6 @@ public class Connection {
message.add("params", params);
JsonObject metadata = new JsonObject();
metadata.addProperty("wallTime", currentTimeMillis());
if (timeout != null) {
metadata.addProperty("timeout", timeout);
}
JsonArray stack = null;
if (titleReported) {
metadata.addProperty("internal", true);
@@ -190,7 +175,7 @@ public class Connection {
callData.add("stack", stack);
JsonObject stackParams = new JsonObject();
stackParams.add("callData", callData);
internalSendMessage(localUtils.guid,"addStackToTracingNoReply", stackParams, null, false, true);
internalSendMessage(localUtils.guid,"addStackToTracingNoReply", stackParams, false);
}
return result;
}
@@ -266,20 +251,16 @@ public class Connection {
callback.complete(message.result);
} else {
String callLog = formatCallLog(message.log);
PlaywrightException exception;
if (message.error.error == null) {
exception = new PlaywrightException(message.error + callLog);
callback.completeExceptionally(new PlaywrightException(message.error + callLog));
} else if ("TimeoutError".equals(message.error.error.name)) {
exception = new TimeoutError(message.error.error + callLog);
callback.completeExceptionally(new TimeoutError(message.error.error + callLog));
} else if ("TargetClosedError".equals(message.error.error.name)) {
exception = new TargetClosedError(message.error.error + callLog);
callback.completeExceptionally(new TargetClosedError(message.error.error + callLog));
} else {
exception = new DriverException(message.error.error + callLog);
callback.completeExceptionally(new DriverException(message.error.error + callLog));
}
if (message.errorDetails != null) {
exception = new ServerErrorWithDetails(exception, message.errorDetails, message.log);
}
callback.completeExceptionally(exception);
}
return;
}
@@ -1,62 +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.impl;
import com.google.gson.JsonObject;
import com.microsoft.playwright.Credentials;
import com.microsoft.playwright.options.VirtualCredential;
import java.util.List;
import static com.microsoft.playwright.impl.ChannelOwner.NO_TIMEOUT;
import static com.microsoft.playwright.impl.Serialization.gson;
import static java.util.Arrays.asList;
class CredentialsImpl implements Credentials {
private final BrowserContextImpl context;
CredentialsImpl(BrowserContextImpl context) {
this.context = context;
}
@Override
public void install() {
context.sendMessage("credentialsInstall", new JsonObject(), NO_TIMEOUT);
}
@Override
public VirtualCredential create(String rpId, CreateOptions options) {
JsonObject params = options == null ? new JsonObject() : gson().toJsonTree(options).getAsJsonObject();
params.addProperty("rpId", rpId);
JsonObject json = context.sendMessage("credentialsCreate", params, NO_TIMEOUT).getAsJsonObject();
return gson().fromJson(json.get("credential"), VirtualCredential.class);
}
@Override
public void delete(String id) {
JsonObject params = new JsonObject();
params.addProperty("id", id);
context.sendMessage("credentialsDelete", params, NO_TIMEOUT);
}
@Override
public List<VirtualCredential> get(GetOptions options) {
JsonObject params = options == null ? new JsonObject() : gson().toJsonTree(options).getAsJsonObject();
JsonObject json = context.sendMessage("credentialsGet", params, NO_TIMEOUT).getAsJsonObject();
return asList(gson().fromJson(json.getAsJsonArray("credentials"), VirtualCredential[].class));
}
}
@@ -38,7 +38,6 @@ import static com.microsoft.playwright.impl.Utils.*;
import static com.microsoft.playwright.impl.Utils.addFilePathUploadParams;
import static com.microsoft.playwright.options.ScreenshotType.JPEG;
import static com.microsoft.playwright.options.ScreenshotType.PNG;
import static com.microsoft.playwright.options.ScreenshotType.WEBP;
public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
private final FrameImpl frame;
@@ -279,8 +278,6 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
String extension = fileName.substring(extStart).toLowerCase();
if (".jpeg".equals(extension) || ".jpg".equals(extension)) {
options.type = JPEG;
} else if (".webp".equals(extension)) {
options.type = WEBP;
}
}
}
@@ -1051,58 +1051,12 @@ public class FrameImpl extends ChannelOwner implements Frame {
return result.get("value").getAsInt();
}
void dropImpl(String selector, DropPayload payload, com.microsoft.playwright.Locator.DropOptions options) {
if (options == null) {
options = new com.microsoft.playwright.Locator.DropOptions();
}
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
params.addProperty("selector", selector);
params.addProperty("strict", true);
if (payload != null) {
if (payload.files != null) {
if (payload.files instanceof Path) {
addFilePathUploadParams(new Path[] { (Path) payload.files }, params, page.context());
} else if (payload.files instanceof Path[]) {
addFilePathUploadParams((Path[]) payload.files, params, page.context());
} else if (payload.files instanceof com.microsoft.playwright.options.FilePayload) {
checkFilePayloadSize(new com.microsoft.playwright.options.FilePayload[] { (com.microsoft.playwright.options.FilePayload) payload.files });
params.add("payloads", toJsonArray(new com.microsoft.playwright.options.FilePayload[] { (com.microsoft.playwright.options.FilePayload) payload.files }));
} else if (payload.files instanceof com.microsoft.playwright.options.FilePayload[]) {
checkFilePayloadSize((com.microsoft.playwright.options.FilePayload[]) payload.files);
params.add("payloads", toJsonArray((com.microsoft.playwright.options.FilePayload[]) payload.files));
} else {
throw new com.microsoft.playwright.PlaywrightException("Unsupported files type: " + payload.files.getClass());
}
}
if (payload.data != null) {
com.google.gson.JsonArray dataArray = new com.google.gson.JsonArray();
for (java.util.Map.Entry<String, String> entry : payload.data.entrySet()) {
JsonObject e = new JsonObject();
e.addProperty("mimeType", entry.getKey());
e.addProperty("value", entry.getValue());
dataArray.add(e);
}
params.add("data", dataArray);
}
}
sendMessage("drop", params, timeout(options.timeout));
}
void highlightImpl(String selector, String style) {
void highlightImpl(String selector) {
JsonObject params = new JsonObject();
params.addProperty("selector", selector);
if (style != null) {
params.addProperty("style", style);
}
sendMessage("highlight", params, NO_TIMEOUT);
}
void hideHighlightImpl(String selector) {
JsonObject params = new JsonObject();
params.addProperty("selector", selector);
sendMessage("hideHighlight", params, NO_TIMEOUT);
}
protected void handleEvent(String event, JsonObject params) {
if ("loadstate".equals(event)) {
JsonElement add = params.get("add");
@@ -1112,7 +1066,6 @@ public class FrameImpl extends ChannelOwner implements Frame {
if (parentFrame == null && page != null) {
if (state == LOAD) {
page.listeners.notify(PageImpl.EventType.LOAD, page);
page.browserContext.notifyPageLoad(page);
} else if (state == DOMCONTENTLOADED) {
page.listeners.notify(PageImpl.EventType.DOMCONTENTLOADED, page);
}
@@ -1154,17 +1107,8 @@ public class FrameImpl extends ChannelOwner implements Frame {
FrameExpectResult expect(String expression, FrameExpectOptions options) {
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
params.addProperty("expression", expression);
FrameExpectResult result = new FrameExpectResult();
try {
sendMessage("expect", params, options.timeout);
result.matches = !options.isNot;
} catch (ServerErrorWithDetails e) {
FrameExpectErrorDetails details = gson().fromJson(e.errorDetails(), FrameExpectErrorDetails.class);
result.matches = options.isNot;
result.received = details.received;
result.errorMessage = details.customErrorMessage == null ? null : "Error: " + details.customErrorMessage;
result.log = e.log();
}
JsonElement json = sendMessage("expect", params, options.timeout);
FrameExpectResult result = gson().fromJson(json, FrameExpectResult.class);
return result;
}
}
@@ -29,7 +29,6 @@ import java.util.regex.Pattern;
import static com.microsoft.playwright.impl.LocatorUtils.*;
import static com.microsoft.playwright.impl.Serialization.gson;
import static com.microsoft.playwright.impl.Serialization.serializeArgument;
import static com.microsoft.playwright.impl.Utils.convertType;
class LocatorImpl implements Locator {
@@ -372,20 +371,8 @@ class LocatorImpl implements Locator {
}
@Override
public void drop(DropPayload payload, DropOptions options) {
frame.dropImpl(selector, payload, options);
}
@Override
public AutoCloseable highlight(HighlightOptions options) {
String style = options == null ? null : options.style;
frame.highlightImpl(selector, style);
return new DisposableStub(this::hideHighlight);
}
@Override
public void hideHighlight() {
frame.hideHighlightImpl(selector);
public void highlight() {
frame.highlightImpl(selector);
}
@Override
@@ -667,16 +654,6 @@ class LocatorImpl implements Locator {
frame.waitForSelectorImpl(selector, convertType(options, Frame.WaitForSelectorOptions.class).setStrict(true), true);
}
@Override
public void waitForFunction(String expression, Object arg, WaitForFunctionOptions options) {
JsonObject params = new JsonObject();
params.addProperty("selector", selector);
params.addProperty("strict", true);
params.addProperty("expression", expression);
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
frame.sendMessage("waitForFunction", params, frame.timeout(options == null ? null : options.timeout));
}
@Override
public String toString() {
String description = description();
@@ -43,14 +43,8 @@ public class LocatorUtils {
return "internal:describe=" + gson().toJson(description);
}
// Multiple test id attribute names can be joined with a comma. Attribute names cannot contain commas.
private static String encodeTestIdAttributeName(String testIdAttributeName) {
return testIdAttributeName.contains(",") ? gson().toJson(testIdAttributeName) : testIdAttributeName;
}
static String getByTestIdSelector(Object testId, PlaywrightImpl playwright) {
String attributeName = encodeTestIdAttributeName(playwright.selectors.testIdAttributeName);
return "internal:testid=[" + attributeName + "=" + escapeForAttributeSelector(testId, true) + "]";
return getByAttributeTextSelector(playwright.selectors.testIdAttributeName, testId, true);
}
static String getByAltTextSelector(Object text, Locator.GetByAltTextOptions options) {
@@ -92,10 +86,6 @@ public class LocatorUtils {
String name = escapeForAttributeSelector(options.name, options.exact != null && options.exact);
addAttr(result, "name", name);
}
if (options.description != null) {
String description = escapeForAttributeSelector(options.description, options.exact != null && options.exact);
addAttr(result, "description", description);
}
if (options.pressed != null)
addAttr(result, "pressed", options.pressed.toString());
}
@@ -73,16 +73,6 @@ public class PageAssertionsImpl extends AssertionsBase implements PageAssertions
expectImpl("to.have.url", expected, pattern, "Page URL expected to match regex", convertType(options, FrameExpectOptions.class), "Assert \"hasURL\"");
}
@Override
public void matchesAriaSnapshot(String expected, MatchesAriaSnapshotOptions snapshotOptions) {
if (snapshotOptions == null) {
snapshotOptions = new MatchesAriaSnapshotOptions();
}
FrameExpectOptions options = convertType(snapshotOptions, FrameExpectOptions.class);
options.expectedValue = Serialization.serializeArgument(expected);
expectImpl("to.match.aria", options, expected, "Page expected to match Aria snapshot", "Assert \"matchesAriaSnapshot\"");
}
@Override
public PageAssertions not() {
return new PageAssertionsImpl(actualPage, !isNot);
@@ -35,21 +35,18 @@ import static com.microsoft.playwright.impl.Serialization.parseError;
import static com.microsoft.playwright.impl.Utils.*;
import static com.microsoft.playwright.options.ScreenshotType.JPEG;
import static com.microsoft.playwright.options.ScreenshotType.PNG;
import static com.microsoft.playwright.options.ScreenshotType.WEBP;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.Files.readAllBytes;
import static java.util.Arrays.asList;
public class PageImpl extends ChannelOwner implements Page {
final BrowserContextImpl browserContext;
private final BrowserContextImpl browserContext;
private final FrameImpl mainFrame;
private final KeyboardImpl keyboard;
private final MouseImpl mouse;
private final TouchscreenImpl touchscreen;
private final ScreencastImpl screencast;
private final WebStorageImpl localStorage;
private final WebStorageImpl sessionStorage;
final Waitable<?> waitableClosedOrCrashed;
private ViewportSize viewport;
private final Router routes = new Router();
@@ -140,8 +137,6 @@ public class PageImpl extends ChannelOwner implements Page {
mouse = new MouseImpl(this);
touchscreen = new TouchscreenImpl(this);
screencast = new ScreencastImpl(this);
localStorage = new WebStorageImpl(this, "local");
sessionStorage = new WebStorageImpl(this, "session");
frames.add(mainFrame);
timeoutSettings = new TimeoutSettings(browserContext.timeoutSettings);
waitableClosedOrCrashed = createWaitForCloseHelper();
@@ -176,7 +171,6 @@ public class PageImpl extends ChannelOwner implements Page {
ArtifactImpl artifact = connection.getExistingObject(artifactGuid);
DownloadImpl download = new DownloadImpl(this, artifact, params);
listeners.notify(EventType.DOWNLOAD, download);
browserContext.notifyDownload(download);
} else if ("fileChooser".equals(event)) {
String guid = params.getAsJsonObject("element").get("guid").getAsString();
ElementHandleImpl elementHandle = connection.getExistingObject(guid);
@@ -207,7 +201,6 @@ public class PageImpl extends ChannelOwner implements Page {
frame.parentFrame.childFrames.add(frame);
}
listeners.notify(EventType.FRAMEATTACHED, frame);
browserContext.notifyFrameAttached(frame);
} else if ("frameDetached".equals(event)) {
String guid = params.getAsJsonObject("frame").get("guid").getAsString();
FrameImpl frame = connection.getExistingObject(guid);
@@ -217,7 +210,6 @@ public class PageImpl extends ChannelOwner implements Page {
frame.parentFrame.childFrames.remove(frame);
}
listeners.notify(EventType.FRAMEDETACHED, frame);
browserContext.notifyFrameDetached(frame);
} else if ("locatorHandlerTriggered".equals(event)) {
int uid = params.get("uid").getAsInt();
onLocatorHandlerTriggered(uid);
@@ -253,7 +245,6 @@ public class PageImpl extends ChannelOwner implements Page {
isClosed = true;
browserContext.pages.remove(this);
listeners.notify(EventType.CLOSE, this);
browserContext.notifyPageClose(this);
}
private String effectiveCloseReason() {
@@ -560,13 +551,8 @@ public class PageImpl extends ChannelOwner implements Page {
try {
if (ownedContext != null) {
ownedContext.close();
} else if (options.runBeforeUnload != null && options.runBeforeUnload) {
sendMessage("runBeforeUnload", new JsonObject(), NO_TIMEOUT);
} else {
JsonObject params = new JsonObject();
if (options.reason != null) {
params.addProperty("reason", options.reason);
}
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
sendMessage("close", params, NO_TIMEOUT);
}
} catch (PlaywrightException exception) {
@@ -767,11 +753,11 @@ public class PageImpl extends ChannelOwner implements Page {
}
@Override
public AutoCloseable exposeBinding(String name, BindingCallback playwrightBinding) {
return exposeBindingImpl(name, playwrightBinding);
public AutoCloseable exposeBinding(String name, BindingCallback playwrightBinding, ExposeBindingOptions options) {
return exposeBindingImpl(name, playwrightBinding, options);
}
private AutoCloseable exposeBindingImpl(String name, BindingCallback playwrightBinding) {
private AutoCloseable exposeBindingImpl(String name, BindingCallback playwrightBinding, ExposeBindingOptions options) {
if (bindings.containsKey(name)) {
throw new PlaywrightException("Function \"" + name + "\" has been already registered");
}
@@ -782,13 +768,16 @@ public class PageImpl extends ChannelOwner implements Page {
JsonObject params = new JsonObject();
params.addProperty("name", name);
if (options != null && options.handle != null && options.handle) {
params.addProperty("needsHandle", true);
}
JsonObject result = sendMessage("exposeBinding", params, NO_TIMEOUT).getAsJsonObject();
return connection.getExistingObject(result.getAsJsonObject("disposable").get("guid").getAsString());
}
@Override
public AutoCloseable exposeFunction(String name, FunctionCallback playwrightFunction) {
return exposeBindingImpl(name, (BindingCallback.Source source, Object... args) -> playwrightFunction.call(args));
return exposeBindingImpl(name, (BindingCallback.Source source, Object... args) -> playwrightFunction.call(args), null);
}
@Override
@@ -1071,11 +1060,6 @@ public class PageImpl extends ChannelOwner implements Page {
return mainFrame;
}
@Override
public void hideHighlight() {
sendMessage("hideHighlight", new JsonObject(), NO_TIMEOUT);
}
@Override
public Mouse mouse() {
return mouse;
@@ -1260,8 +1244,6 @@ public class PageImpl extends ChannelOwner implements Page {
String extension = fileName.substring(extStart).toLowerCase();
if (".jpeg".equals(extension) || ".jpg".equals(extension)) {
options.type = JPEG;
} else if (".webp".equals(extension)) {
options.type = WEBP;
}
}
}
@@ -1374,16 +1356,6 @@ public class PageImpl extends ChannelOwner implements Page {
return screencast;
}
@Override
public WebStorage localStorage() {
return localStorage;
}
@Override
public WebStorage sessionStorage() {
return sessionStorage;
}
@Override
public void type(String selector, String text, TypeOptions options) {
mainFrame.type(selector, text, convertType(options, Frame.TypeOptions.class));
@@ -1485,7 +1457,6 @@ public class PageImpl extends ChannelOwner implements Page {
void frameNavigated(FrameImpl frame) {
listeners.notify(EventType.FRAMENAVIGATED, frame);
browserContext.notifyFrameNavigated(frame);
}
private class WaitableFrameDetach extends WaitableEvent<EventType, Frame> {
@@ -112,20 +112,10 @@ class FrameExpectOptions {
}
class FrameExpectResult {
static class Received {
SerializedValue value;
String ariaSnapshot;
}
boolean matches;
Received received;
SerializedValue received;
String errorMessage;
List<String> log;
}
class FrameExpectErrorDetails {
FrameExpectResult.Received received;
Boolean timedOut;
String customErrorMessage;
}
@@ -1,53 +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.impl;
import com.microsoft.playwright.ScreencastFrame;
class ScreencastFrameImpl implements ScreencastFrame {
private final byte[] data;
private final double timestamp;
private final int viewportWidth;
private final int viewportHeight;
ScreencastFrameImpl(byte[] data, double timestamp, int viewportWidth, int viewportHeight) {
this.data = data;
this.timestamp = timestamp;
this.viewportWidth = viewportWidth;
this.viewportHeight = viewportHeight;
}
@Override
public byte[] data() {
return data;
}
@Override
public double timestamp() {
return timestamp;
}
@Override
public int viewportWidth() {
return viewportWidth;
}
@Override
public int viewportHeight() {
return viewportHeight;
}
}
@@ -19,7 +19,7 @@ package com.microsoft.playwright.impl;
import com.google.gson.JsonObject;
import com.microsoft.playwright.PlaywrightException;
import com.microsoft.playwright.Screencast;
import com.microsoft.playwright.ScreencastFrame;
import com.microsoft.playwright.options.ScreencastFrame;
import java.nio.file.Path;
import java.util.function.Consumer;
@@ -39,21 +39,12 @@ class ScreencastImpl implements Screencast {
}
void handleScreencastFrame(JsonObject params) {
try {
if (onFrame != null) {
String dataBase64 = params.get("data").getAsString();
byte[] data = java.util.Base64.getDecoder().decode(dataBase64);
double timestamp = params.get("timestamp").getAsDouble();
int viewportWidth = params.get("viewportWidth").getAsInt();
int viewportHeight = params.get("viewportHeight").getAsInt();
onFrame.accept(new ScreencastFrameImpl(data, timestamp, viewportWidth, viewportHeight));
}
} finally {
// The server sends the next frame only after the previous one is acknowledged.
JsonObject ackParams = new JsonObject();
ackParams.add("frameId", params.get("frameId"));
page.sendMessageAsync("screencastFrameAck", ackParams);
if (onFrame == null) {
return;
}
String dataBase64 = params.get("data").getAsString();
byte[] data = java.util.Base64.getDecoder().decode(dataBase64);
onFrame.accept(new ScreencastFrame(data));
}
@Override
@@ -58,7 +58,6 @@ class Serialization {
.registerTypeAdapter(ScreenshotCaret.class, new ToLowerCaseSerializer<ScreenshotCaret>())
.registerTypeAdapter(ServiceWorkerPolicy.class, new ToLowerCaseAndDashSerializer<ServiceWorkerPolicy>())
.registerTypeAdapter(MouseButton.class, new ToLowerCaseSerializer<MouseButton>())
.registerTypeAdapter(ScrollMode.class, new ToLowerCaseSerializer<ScrollMode>())
.registerTypeAdapter(ConsoleMessagesFilter.class, new ConsoleMessagesFilterSerializer())
.registerTypeAdapter(AriaSnapshotMode.class, new ToLowerCaseSerializer<AriaSnapshotMode>())
.registerTypeAdapter(LoadState.class, new ToLowerCaseSerializer<LoadState>())
@@ -78,7 +77,6 @@ class Serialization {
static final Gson jsonDataSerializer = new GsonBuilder().disableHtmlEscaping()
.registerTypeAdapter(Date.class, new DateSerializer())
.registerTypeAdapter(LocalDate.class, new LocalDateSerializer())
.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeSerializer())
.registerTypeAdapter(OffsetDateTime.class, new OffsetDateTimeSerializer())
.serializeNulls().create();
@@ -573,15 +571,6 @@ class Serialization {
}
}
private static class LocalDateSerializer implements JsonSerializer<LocalDate> {
@Override
public JsonElement serialize(LocalDate src, Type typeOfSrc, JsonSerializationContext context) {
// LocalDate has no time or zone, so emit the ISO-8601 date (yyyy-MM-dd) as-is to
// avoid shifting the calendar date when converting through a time zone.
return new JsonPrimitive(src.toString());
}
}
private static class LocalDateTimeSerializer implements JsonSerializer<LocalDateTime> {
@Override
public JsonElement serialize(LocalDateTime src, Type typeOfSrc, JsonSerializationContext context) {
@@ -1,57 +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.impl;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.microsoft.playwright.PlaywrightException;
import java.util.ArrayList;
import java.util.List;
class ServerErrorWithDetails extends PlaywrightException {
private final JsonObject errorDetails;
private final JsonArray log;
ServerErrorWithDetails(PlaywrightException cause, JsonObject errorDetails, JsonArray log) {
super(cause.getMessage(), cause);
this.errorDetails = errorDetails;
this.log = log;
}
// Rethrown with the calling thread's stack trace, see WaitableResult.get().
ServerErrorWithDetails(ServerErrorWithDetails cause) {
super(cause.getMessage(), cause);
this.errorDetails = cause.errorDetails;
this.log = cause.log;
}
JsonObject errorDetails() {
return errorDetails;
}
List<String> log() {
List<String> result = new ArrayList<>();
if (log != null) {
for (JsonElement e : log) {
result.add(e.getAsString());
}
}
return result;
}
}
@@ -18,21 +18,14 @@ package com.microsoft.playwright.impl;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.microsoft.playwright.PlaywrightException;
import com.microsoft.playwright.Tracing;
import com.microsoft.playwright.options.HarContentPolicy;
import com.microsoft.playwright.options.HarMode;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static com.microsoft.playwright.impl.Serialization.addHarUrlFilter;
import static com.microsoft.playwright.impl.Serialization.gson;
class TracingImpl extends ChannelOwner implements Tracing {
@@ -41,17 +34,6 @@ class TracingImpl extends ChannelOwner implements Tracing {
private boolean isTracing;
private String stacksId;
private final Set<String> additionalSources = new HashSet<>();
final Map<String, HarRecorder> harRecorders = new HashMap<>();
static class HarRecorder {
final Path path;
final HarContentPolicy contentPolicy;
HarRecorder(Path har, HarContentPolicy policy) {
this.path = har;
this.contentPolicy = policy;
}
}
TracingImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
@@ -179,110 +161,6 @@ class TracingImpl extends ChannelOwner implements Tracing {
stopChunkImpl(options == null ? null : options.path);
}
private String currentHarId;
@Override
public AutoCloseable startHar(Path path, StartHarOptions options) {
if (currentHarId != null) {
throw new PlaywrightException("HAR recording has already been started");
}
if (options == null) {
options = new StartHarOptions();
}
boolean isZip = path.toString().endsWith(".zip");
HarContentPolicy contentPolicy = options.content != null
? options.content
: (isZip ? HarContentPolicy.ATTACH : HarContentPolicy.EMBED);
HarMode mode = options.mode != null ? options.mode : HarMode.FULL;
currentHarId = recordIntoHar(null, path, options.urlFilter, contentPolicy, mode, null);
return new DisposableStub(this::stopHar);
}
@Override
public void stopHar() {
if (currentHarId == null) {
throw new PlaywrightException("HAR recording has not been started");
}
String harId = currentHarId;
currentHarId = null;
exportHar(harId);
}
String recordIntoHar(PageImpl page, Path har, Object urlFilter, HarContentPolicy contentPolicy, HarMode mode, Path resourcesDir) {
if (contentPolicy == null) {
contentPolicy = HarContentPolicy.ATTACH;
}
if (mode == null) {
mode = HarMode.MINIMAL;
}
JsonObject params = new JsonObject();
if (page != null) {
params.add("page", page.toProtocolRef());
}
JsonObject recordHarArgs = new JsonObject();
recordHarArgs.addProperty("zip", har.toString().endsWith(".zip"));
recordHarArgs.addProperty("content", contentPolicy.name().toLowerCase());
recordHarArgs.addProperty("mode", mode.name().toLowerCase());
addHarUrlFilter(recordHarArgs, urlFilter);
if (resourcesDir != null) {
recordHarArgs.addProperty("resourcesDir", resourcesDir.toString());
}
if (!har.toString().endsWith(".zip")) {
recordHarArgs.addProperty("harPath", har.toString());
}
params.add("options", recordHarArgs);
JsonObject json = sendMessage("harStart", params, NO_TIMEOUT).getAsJsonObject();
String harId = json.get("harId").getAsString();
harRecorders.put(harId, new HarRecorder(har, contentPolicy));
return harId;
}
void exportHar(String harId) {
HarRecorder harParams = harRecorders.remove(harId);
if (harParams == null) {
return;
}
boolean isLocal = !connection.isRemote;
boolean isZip = harParams.path.toString().endsWith(".zip");
JsonObject params = new JsonObject();
params.addProperty("harId", harId);
if (isLocal) {
params.addProperty("mode", "entries");
JsonObject json = sendMessage("harExport", params, NO_TIMEOUT).getAsJsonObject();
if (!isZip) {
return;
}
JsonArray entries = json.getAsJsonArray("entries");
connection.localUtils.zip(harParams.path, entries, null, false, false, java.util.Collections.emptyList());
return;
}
params.addProperty("mode", "archive");
JsonObject json = sendMessage("harExport", params, NO_TIMEOUT).getAsJsonObject();
ArtifactImpl artifact = connection.getExistingObject(json.getAsJsonObject("artifact").get("guid").getAsString());
if (isZip) {
artifact.saveAs(harParams.path);
artifact.delete();
return;
}
String tmpPath = harParams.path + ".tmp";
artifact.saveAs(Paths.get(tmpPath));
JsonObject unzipParams = new JsonObject();
unzipParams.addProperty("zipFile", tmpPath);
unzipParams.addProperty("harFile", harParams.path.toString());
connection.localUtils.sendMessage("harUnzip", unzipParams, NO_TIMEOUT);
artifact.delete();
}
void exportAllHars() {
for (String harId : new ArrayList<>(harRecorders.keySet())) {
exportHar(harId);
}
}
void setTracesDir(Path tracesDir) {
this.tracesDir = tracesDir;
}
@@ -41,8 +41,7 @@ public class WaitForEventLogger<T> implements Supplier<T>, Logger {
{
JsonObject info = new JsonObject();
info.addProperty("phase", "before");
info.addProperty("event", "");
sendWaitInfo(info);
sendWaitForEventInfo(info);
}
JsonObject info = new JsonObject();
info.addProperty("phase", "after");
@@ -52,7 +51,7 @@ public class WaitForEventLogger<T> implements Supplier<T>, Logger {
info.addProperty("error", e.getMessage());
throw e;
} finally {
sendWaitInfo(info);
sendWaitForEventInfo(info);
}
}
@@ -62,15 +61,14 @@ public class WaitForEventLogger<T> implements Supplier<T>, Logger {
JsonObject info = new JsonObject();
info.addProperty("phase", "log");
info.addProperty("message", message);
sendWaitInfo(info);
sendWaitForEventInfo(info);
}
private void sendWaitInfo(JsonObject info) {
private void sendWaitForEventInfo(JsonObject info) {
info.addProperty("event", "");
info.addProperty("waitId", waitId);
try {
channel.sendMessageNoReply("__waitInfo__", info);
} catch (RuntimeException e) {
// Fire-and-forget: never throw to the caller.
}
JsonObject params = new JsonObject();
params.add("info", info);
channel.sendMessageAsync("waitForEventInfo", params);
}
}
@@ -52,8 +52,6 @@ class WaitableResult<T> implements Waitable<T> {
throw new TimeoutError(exception.getMessage(), exception);
} if (exception instanceof TargetClosedError) {
throw new TargetClosedError(exception.getMessage(), exception);
} if (exception instanceof ServerErrorWithDetails) {
throw new ServerErrorWithDetails((ServerErrorWithDetails) exception);
}
throw new PlaywrightException(exception.getMessage(), exception);
}
@@ -17,17 +17,14 @@
package com.microsoft.playwright.impl;
import com.microsoft.playwright.WebError;
import com.microsoft.playwright.options.WebErrorLocation;
public class WebErrorImpl implements WebError {
private final PageImpl page;
private final String error;
private final WebErrorLocation location;
WebErrorImpl(PageImpl page, String error, WebErrorLocation location) {
WebErrorImpl(PageImpl page, String error) {
this.page = page;
this.error = error;
this.location = location;
}
@Override
@@ -39,9 +36,4 @@ public class WebErrorImpl implements WebError {
public String error() {
return error;
}
@Override
public WebErrorLocation location() {
return location;
}
}
@@ -1,14 +1,11 @@
package com.microsoft.playwright.impl;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.microsoft.playwright.PlaywrightException;
import com.microsoft.playwright.WebSocketFrame;
import com.microsoft.playwright.WebSocketRoute;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@@ -68,11 +65,6 @@ class WebSocketRouteImpl extends ChannelOwner implements WebSocketRoute {
public String url() {
return initializer.get("url").getAsString();
}
@Override
public List<String> protocols() {
return readProtocols();
}
};
WebSocketRouteImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
@@ -131,22 +123,6 @@ class WebSocketRouteImpl extends ChannelOwner implements WebSocketRoute {
return initializer.get("url").getAsString();
}
@Override
public List<String> protocols() {
return readProtocols();
}
private List<String> readProtocols() {
List<String> result = new ArrayList<>();
if (!initializer.has("protocols")) {
return result;
}
for (JsonElement element : initializer.getAsJsonArray("protocols")) {
result.add(element.getAsString());
}
return result;
}
void afterHandle() {
if (this.connected) {
return;
@@ -1,77 +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.impl;
import com.google.gson.JsonObject;
import com.microsoft.playwright.WebStorage;
import com.microsoft.playwright.options.WebStorageItem;
import java.util.List;
import static com.microsoft.playwright.impl.ChannelOwner.NO_TIMEOUT;
import static com.microsoft.playwright.impl.Serialization.gson;
import static java.util.Arrays.asList;
class WebStorageImpl implements WebStorage {
private final PageImpl page;
private final String kind;
WebStorageImpl(PageImpl page, String kind) {
this.page = page;
this.kind = kind;
}
private JsonObject createParams() {
JsonObject params = new JsonObject();
params.addProperty("kind", kind);
return params;
}
@Override
public List<WebStorageItem> items() {
JsonObject json = page.sendMessage("webStorageItems", createParams(), NO_TIMEOUT).getAsJsonObject();
return asList(gson().fromJson(json.getAsJsonArray("items"), WebStorageItem[].class));
}
@Override
public String getItem(String name) {
JsonObject params = createParams();
params.addProperty("name", name);
JsonObject json = page.sendMessage("webStorageGetItem", params, NO_TIMEOUT).getAsJsonObject();
return json.has("value") ? json.get("value").getAsString() : null;
}
@Override
public void setItem(String name, String value) {
JsonObject params = createParams();
params.addProperty("name", name);
params.addProperty("value", value);
page.sendMessage("webStorageSetItem", params, NO_TIMEOUT);
}
@Override
public void removeItem(String name) {
JsonObject params = createParams();
params.addProperty("name", name);
page.sendMessage("webStorageRemoveItem", params, NO_TIMEOUT);
}
@Override
public void clear() {
page.sendMessage("webStorageClear", createParams(), NO_TIMEOUT);
}
}
@@ -1,22 +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 enum PseudoElement {
BEFORE,
AFTER
}
@@ -1,22 +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 enum ScreencastCursor {
NONE,
POINTER
}
@@ -16,18 +16,17 @@
package com.microsoft.playwright.options;
import java.util.Map;
/**
* A single screencast frame delivered to {@link com.microsoft.playwright.Screencast#start Screencast.start()}'s
* {@code onFrame} callback.
*/
public class ScreencastFrame {
/**
* JPEG-encoded frame data.
*/
public byte[] data;
public class DropPayload {
public Object files;
public Map<String, String> data;
public DropPayload setFiles(Object files) {
this.files = files;
return this;
}
public DropPayload setData(Map<String, String> data) {
public ScreencastFrame(byte[] data) {
this.data = data;
return this;
}
}
}
@@ -18,6 +18,5 @@ package com.microsoft.playwright.options;
public enum ScreenshotType {
PNG,
JPEG,
WEBP
JPEG
}
@@ -1,22 +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 enum ScrollMode {
AUTO,
NONE
}
@@ -18,11 +18,11 @@ package com.microsoft.playwright.options;
public class Size {
/**
* Max frame width in pixels.
* Video frame width.
*/
public int width;
/**
* Max frame height in pixels.
* Video frame height.
*/
public int height;
@@ -22,42 +22,42 @@ public class Timing {
*/
public double startTime;
/**
* Time immediately before the client starts the domain name lookup for the resource. The value is given in milliseconds
* Time immediately before the browser starts the domain name lookup for the resource. The value is given in milliseconds
* relative to {@code startTime}, -1 if not available.
*/
public double domainLookupStart;
/**
* Time immediately after the client ends the domain name lookup for the resource. The value is given in milliseconds
* Time immediately after the browser starts the domain name lookup for the resource. The value is given in milliseconds
* relative to {@code startTime}, -1 if not available.
*/
public double domainLookupEnd;
/**
* Time immediately before the client starts establishing the connection to the server to retrieve the resource. The value
* is given in milliseconds relative to {@code startTime}, -1 if not available.
* Time immediately before the user agent starts establishing the connection to the server to retrieve the resource. The
* value is given in milliseconds relative to {@code startTime}, -1 if not available.
*/
public double connectStart;
/**
* Time immediately before the client starts the handshake process to secure the current connection. The value is given in
* Time immediately before the browser starts the handshake process to secure the current connection. The value is given in
* milliseconds relative to {@code startTime}, -1 if not available.
*/
public double secureConnectionStart;
/**
* Time immediately after the client establishes the connection to the server to retrieve the resource. The value is given
* in milliseconds relative to {@code startTime}, -1 if not available.
* Time immediately before the user agent starts establishing the connection to the server to retrieve the resource. The
* value is given in milliseconds relative to {@code startTime}, -1 if not available.
*/
public double connectEnd;
/**
* Time immediately before the client starts requesting the resource from the server, cache, or local resource. The value
* Time immediately before the browser starts requesting the resource from the server, cache, or local resource. The value
* is given in milliseconds relative to {@code startTime}, -1 if not available.
*/
public double requestStart;
/**
* Time immediately after the client receives the first byte of the response from the server, cache, or local resource. The
* value is given in milliseconds relative to {@code startTime}, -1 if not available.
* Time immediately after the browser receives the first byte of the response from the server, cache, or local resource.
* The value is given in milliseconds relative to {@code startTime}, -1 if not available.
*/
public double responseStart;
/**
* Time immediately after the client receives the last byte of the resource or immediately before the transport connection
* Time immediately after the browser receives the last byte of the resource or immediately before the transport connection
* is closed, whichever comes first. The value is given in milliseconds relative to {@code startTime}, -1 if not available.
*/
public double responseEnd;
@@ -1,41 +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 VirtualCredential {
/**
* Base64url-encoded credential id.
*/
public String id;
/**
* Relying party id.
*/
public String rpId;
/**
* Base64url-encoded user handle.
*/
public String userHandle;
/**
* Base64url-encoded PKCS#8 (DER) private key.
*/
public String privateKey;
/**
* Base64url-encoded SPKI (DER) public key.
*/
public String publicKey;
}
@@ -1,33 +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 WebErrorLocation {
/**
* URL of the resource.
*/
public String url;
/**
* 0-based line number in the resource.
*/
public int line;
/**
* 0-based column number in the resource.
*/
public int column;
}
@@ -1,23 +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 WebStorageItem {
public String name;
public String value;
}
@@ -20,7 +20,6 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.microsoft.playwright.junit.FixtureTest;
import com.microsoft.playwright.junit.UsePlaywright;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIf;
@@ -31,7 +30,6 @@ import static org.junit.jupiter.api.Assertions.*;
@FixtureTest
@UsePlaywright(TestOptionsFactories.BasicOptionsFactory.class)
@Tag("smoke")
public class TestBrowser1 {
@Test
@@ -115,13 +113,4 @@ public class TestBrowser1 {
assertTrue(e.getMessage().contains("The reason."), e.getMessage());
}
@Test
void shouldFireContextEvent(Browser browser) {
BrowserContext[] contextEvent = { null };
browser.onContext(c -> contextEvent[0] = c);
BrowserContext context = browser.newContext();
assertEquals(context, contextEvent[0]);
context.close();
}
}
@@ -18,7 +18,6 @@ package com.microsoft.playwright;
import com.microsoft.playwright.junit.FixtureTest;
import com.microsoft.playwright.junit.UsePlaywright;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import java.io.OutputStreamWriter;
@@ -33,7 +32,6 @@ import static org.junit.jupiter.api.Assertions.*;
@FixtureTest
@UsePlaywright(TestOptionsFactories.BasicOptionsFactory.class)
@Tag("smoke")
public class TestBrowserContextBasic {
@Test
void shouldCreateNewContext(Browser browser) {
@@ -131,9 +131,8 @@ public class TestBrowserContextCDPSession extends TestBase {
CDPSession session = page.context().newCDPSession(page);
page.close();
// Like the upstream test, only check that detach fails the error depends on
// whether the session detached before or after the page closed.
assertThrows(PlaywrightException.class, session::detach);
PlaywrightException exception = assertThrows(PlaywrightException.class, session::detach);
assertTrue(exception.getMessage().contains("Target page, context or browser has been closed"), exception.getMessage());
context.close();
}
@@ -186,90 +186,4 @@ public class TestBrowserContextEvents extends TestBase {
assertTrue(webError[0].error().contains("boom"), webError[0].error());
}
@Test
void weberrorEventShouldIncludeLocation() {
server.setRoute("/error.js", exchange -> {
exchange.getResponseHeaders().add("content-type", "application/javascript");
exchange.sendResponseHeaders(200, 0);
try (Writer writer = new OutputStreamWriter(exchange.getResponseBody())) {
writer.write("\nfunction foo() {\n throw new Error('boom');\n}\nfoo();\n");
}
});
server.setRoute("/error.html", exchange -> {
exchange.getResponseHeaders().add("content-type", "text/html");
exchange.sendResponseHeaders(200, 0);
try (Writer writer = new OutputStreamWriter(exchange.getResponseBody())) {
writer.write("<script src=\"/error.js\"></script>");
}
});
WebError[] webError = { null };
context.onWebError(e -> webError[0] = e);
page.navigate(server.PREFIX + "/error.html");
waitForCondition(() -> webError[0] != null);
com.microsoft.playwright.options.WebErrorLocation location = webError[0].location();
assertEquals(server.PREFIX + "/error.js", location.url);
assertEquals(2, location.line);
assertTrue(location.column > 0, "expected column > 0, got " + location.column);
}
@Test
void pageLoadEventShouldWork() {
Page[] loaded = { null };
context.onPageLoad(p -> loaded[0] = p);
page.navigate(server.EMPTY_PAGE);
waitForCondition(() -> loaded[0] != null);
assertEquals(page, loaded[0]);
}
@Test
void frameNavigatedEventShouldWork() {
Frame[] navigated = { null };
context.onFrameNavigated(f -> navigated[0] = f);
page.navigate(server.EMPTY_PAGE);
waitForCondition(() -> navigated[0] != null);
assertEquals(page.mainFrame(), navigated[0]);
assertEquals(server.EMPTY_PAGE, navigated[0].url());
}
@Test
void pageCloseEventShouldWork() {
Page newPage = context.newPage();
Page[] closed = { null };
context.onPageClose(p -> closed[0] = p);
newPage.close();
waitForCondition(() -> closed[0] != null);
assertEquals(newPage, closed[0]);
}
@Test
void frameAttachedEventShouldWork() {
page.navigate(server.EMPTY_PAGE);
Frame[] attached = { null };
context.onFrameAttached(f -> attached[0] = f);
page.evaluate("() => {\n" +
" const iframe = document.createElement('iframe');\n" +
" iframe.src = 'about:blank';\n" +
" document.body.appendChild(iframe);\n" +
"}");
waitForCondition(() -> attached[0] != null);
assertEquals(page.mainFrame(), attached[0].parentFrame());
}
@Test
void frameDetachedEventShouldWork() {
page.navigate(server.EMPTY_PAGE);
page.evaluate("() => {\n" +
" const iframe = document.createElement('iframe');\n" +
" iframe.id = 'x';\n" +
" iframe.src = 'about:blank';\n" +
" document.body.appendChild(iframe);\n" +
"}");
page.waitForSelector("iframe");
Frame[] detached = { null };
context.onFrameDetached(f -> detached[0] = f);
page.evaluate("() => document.getElementById('x').remove()");
waitForCondition(() -> detached[0] != null);
assertEquals(page.mainFrame(), detached[0].parentFrame());
}
}
@@ -84,4 +84,19 @@ public class TestBrowserContextExposeFunction extends TestBase {
assertEquals(asList("context", "page"), actualArgs);
}
@Test
void exposeBindingHandleShouldWork() {
JSHandle[] target = { null };
context.exposeBinding("logme", (source, args) -> {
target[0] = (JSHandle) args[0];
return 17;
}, new BrowserContext.ExposeBindingOptions().setHandle(true));
Page page = context.newPage();
Object result = page.evaluate("async function() {\n" +
" return window['logme']({ foo: 42 });\n" +
"}");
assertNotNull(target[0]);
assertEquals(42, target[0].evaluate("x => x.foo"));
assertEquals(17, result);
}
}
@@ -28,7 +28,6 @@ import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.text.ParseException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
@@ -56,22 +55,6 @@ public class TestBrowserContextFetch extends TestBase {
assertEquals("{\"foo\": \"bar\"}\n", response.text());
}
@Test
void getShouldReturnTiming() {
APIResponse response = context.request().get(server.PREFIX + "/simple.json");
assertTrue(response.ok());
Timing timing = response.timing();
assertTrue(timing.startTime > 0, "startTime = " + timing.startTime);
assertTrue(timing.domainLookupEnd >= timing.domainLookupStart);
assertTrue(timing.connectStart >= timing.domainLookupEnd);
assertEquals(-1, timing.secureConnectionStart);
assertTrue(timing.connectEnd >= timing.connectStart);
assertTrue(timing.requestStart >= timing.connectEnd);
assertTrue(timing.responseStart >= timing.requestStart);
assertTrue(timing.responseEnd >= timing.responseStart);
assertTrue(timing.responseEnd < 60_000, "responseEnd = " + timing.responseEnd);
}
@Test
void fetchShouldWork() {
APIResponse response = context.request().fetch(server.PREFIX + "/simple.json");
@@ -550,23 +533,6 @@ public class TestBrowserContextFetch extends TestBase {
assertEquals("{\"date\":\"2024-07-10T18:15:30.000Z\"}", new String(body));
}
public static class LocalDateData {
public String name;
public LocalDate date;
}
@Test
void shouldSupportLocalDateInData() throws ExecutionException, InterruptedException {
APIRequestContext request = playwright.request().newContext();
LocalDateData testData = new LocalDateData();
testData.name = "foo";
testData.date = LocalDate.of(2022, 12, 23);
Future<Server.Request> serverRequest = server.futureRequest("/empty.html");
request.post(server.EMPTY_PAGE, RequestOptions.create().setData(testData));
byte[] body = serverRequest.get().postBody;
assertEquals("{\"name\":\"foo\",\"date\":\"2022-12-23\"}", new String(body));
}
@Test
void shouldSupportApplicationXWwwFormUrlencoded() throws ExecutionException, InterruptedException {
Future<Server.Request> req = server.futureRequest("/empty.html");
@@ -775,12 +741,10 @@ public class TestBrowserContextFetch extends TestBase {
});
page.evaluate("() => setTimeout(closeContext, 1000);");
PlaywrightException e = assertThrows(PlaywrightException.class, () -> context.request().get(server.EMPTY_PAGE));
assertTrue(e.getMessage().contains("Request context disposed") ||
e.getMessage().contains("Target page, context or browser has been closed"), e.getMessage());
assertTrue(e.getMessage().contains("Target page, context or browser has been closed"), e.getMessage());
e = assertThrows(PlaywrightException.class, () -> context.request().post(server.EMPTY_PAGE));
assertTrue(e.getMessage().contains("Request context disposed") ||
e.getMessage().contains("Target page, context or browser has been closed"), e.getMessage());
assertTrue(e.getMessage().contains("Target page, context or browser has been closed"), e.getMessage());
}
@Test
@@ -19,7 +19,6 @@ package com.microsoft.playwright;
import com.microsoft.playwright.options.HarMode;
import com.microsoft.playwright.options.HarNotFound;
import com.microsoft.playwright.options.RouteFromHarUpdateContentPolicy;
import com.microsoft.playwright.options.Timing;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIf;
import org.junit.jupiter.api.io.TempDir;
@@ -502,36 +501,4 @@ public class TestBrowserContextHar extends TestBase {
assertNull(page.evaluate("window.result"));
}
}
private void setJsonRoute(String path, String json) {
server.setRoute(path, exchange -> {
exchange.getResponseHeaders().add("Content-Type", "application/json");
byte[] body = json.getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(200, body.length);
try (OutputStream out = exchange.getResponseBody()) {
out.write(body);
}
});
}
@Test
void shouldNotInterceptAPIRequestContextRequestsByDefault(@TempDir Path tmpDir) {
setJsonRoute("/api/data", "{\"hello\": \"live\"}");
Path harPath = tmpDir.resolve("api.har");
try (BrowserContext context1 = browser.newContext()) {
context1.routeFromHAR(harPath, new BrowserContext.RouteFromHAROptions().setUpdate(true));
Page page1 = context1.newPage();
page1.navigate(server.EMPTY_PAGE);
page1.request().get(server.PREFIX + "/api/data");
}
// Without the option, the live network is hit.
setJsonRoute("/api/data", "{\"hello\": \"fresh\"}");
try (BrowserContext context2 = browser.newContext()) {
context2.routeFromHAR(harPath, new BrowserContext.RouteFromHAROptions().setNotFound(HarNotFound.FALLBACK));
Page page2 = context2.newPage();
APIResponse replayed = page2.request().get(server.PREFIX + "/api/data");
assertEquals("{\"hello\": \"fresh\"}", replayed.text());
}
}
}
@@ -1,179 +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;
import com.microsoft.playwright.options.VirtualCredential;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import static com.microsoft.playwright.Utils.mapOf;
import static org.junit.jupiter.api.Assertions.*;
public class TestBrowserContextWebAuthn extends TestBase {
private static final String B64URL_TO_BYTES_JS =
" const b64UrlToBytes = s => {\n" +
" let str = s.replace(/-/g, '+').replace(/_/g, '/');\n" +
" while (str.length % 4)\n" +
" str += '=';\n" +
" const bin = atob(str);\n" +
" const u8 = new Uint8Array(bin.length);\n" +
" for (let i = 0; i < bin.length; i++)\n" +
" u8[i] = bin.charCodeAt(i);\n" +
" return u8;\n" +
" };\n";
@Test
void shouldNotInterceptNavigatorCredentialsWithoutInstall() {
// Seed a credential, but do not install the interceptor.
context.credentials().create("localhost");
page.navigate(server.EMPTY_PAGE);
Object intercepted = page.evaluate("() => globalThis.__pwWebAuthnInstalled === true");
assertEquals(false, intercepted);
}
@Test
void shouldSeedKnownCredentialAndAuthenticate() {
// This is the easiest way to create credentials. In practice, this
// probably comes from environment.
VirtualCredential known;
try (BrowserContext source = browser.newContext()) {
known = source.credentials().create("localhost");
}
// A fresh context imports the known credential and signs in with it.
context.credentials().create(known.rpId, new Credentials.CreateOptions()
.setId(known.id)
.setUserHandle(known.userHandle)
.setPrivateKey(known.privateKey)
.setPublicKey(known.publicKey));
context.credentials().install();
page.navigate(server.EMPTY_PAGE);
Map<String, Object> result = (Map<String, Object>) page.evaluate(
"async ({ rpId, credentialId }) => {\n" +
B64URL_TO_BYTES_JS +
" const challenge = crypto.getRandomValues(new Uint8Array(32));\n" +
" const cred = await navigator.credentials.get({\n" +
" publicKey: {\n" +
" challenge,\n" +
" rpId,\n" +
" allowCredentials: [{ type: 'public-key', id: b64UrlToBytes(credentialId) }],\n" +
" userVerification: 'preferred',\n" +
" },\n" +
" });\n" +
" const resp = cred.response;\n" +
" return {\n" +
" id: cred.id,\n" +
" type: cred.type,\n" +
" hasClientData: resp.clientDataJSON.byteLength > 0,\n" +
" hasAuthData: resp.authenticatorData.byteLength > 0,\n" +
" hasSignature: resp.signature.byteLength > 0,\n" +
" authDataFlags: new Uint8Array(resp.authenticatorData)[32],\n" +
" };\n" +
"}", mapOf("rpId", "localhost", "credentialId", known.id));
assertEquals(known.id, result.get("id"));
assertEquals("public-key", result.get("type"));
assertEquals(true, result.get("hasClientData"));
assertEquals(true, result.get("hasAuthData"));
assertEquals(true, result.get("hasSignature"));
// UP (0x01) | UV (0x04) = 0x05
assertEquals(0x05, ((Number) result.get("authDataFlags")).intValue() & 0x05);
// After the credential is deleted, the page can no longer authenticate with it.
context.credentials().delete(known.id);
assertEquals(0, context.credentials().get().size());
Object error = page.evaluate(
"async ({ rpId, credentialId }) => {\n" +
B64URL_TO_BYTES_JS +
" const challenge = crypto.getRandomValues(new Uint8Array(32));\n" +
" try {\n" +
" await navigator.credentials.get({\n" +
" publicKey: {\n" +
" challenge,\n" +
" rpId,\n" +
" allowCredentials: [{ type: 'public-key', id: b64UrlToBytes(credentialId) }],\n" +
" },\n" +
" });\n" +
" return 'no-error';\n" +
" } catch (e) {\n" +
" return e.name;\n" +
" }\n" +
"}", mapOf("rpId", "localhost", "credentialId", known.id));
assertEquals("NotAllowedError", error);
}
@Test
void shouldCapturePageCreatedCredentialAndReuseItInAnotherContext() {
// Setup context: the app registers a passkey via navigator.credentials.create().
String createdId;
VirtualCredential captured;
try (BrowserContext setupContext = browser.newContext()) {
setupContext.credentials().install();
Page setupPage = setupContext.newPage();
setupPage.navigate(server.EMPTY_PAGE);
createdId = (String) setupPage.evaluate(
"async ({ rpId }) => {\n" +
" const challenge = crypto.getRandomValues(new Uint8Array(32));\n" +
" const created = await navigator.credentials.create({\n" +
" publicKey: {\n" +
" challenge,\n" +
" rp: { id: rpId, name: 'Test RP' },\n" +
" user: { id: new Uint8Array([1, 2, 3, 4]), name: 'u', displayName: 'User' },\n" +
" pubKeyCredParams: [{ type: 'public-key', alg: -7 }],\n" +
" authenticatorSelection: { residentKey: 'required', userVerification: 'preferred' },\n" +
" },\n" +
" });\n" +
" return created.id;\n" +
"}", mapOf("rpId", "localhost"));
List<VirtualCredential> credentials = setupContext.credentials().get(
new Credentials.GetOptions().setRpId("localhost"));
assertEquals(1, credentials.size());
captured = credentials.get(0);
assertEquals(createdId, captured.id);
assertTrue(captured.privateKey.matches("^[A-Za-z0-9_-]+$"), captured.privateKey);
assertTrue(captured.publicKey.matches("^[A-Za-z0-9_-]+$"), captured.publicKey);
}
// Reuse the captured passkey in a fresh context and sign in with it.
context.credentials().create(captured.rpId, new Credentials.CreateOptions()
.setId(captured.id)
.setUserHandle(captured.userHandle)
.setPrivateKey(captured.privateKey)
.setPublicKey(captured.publicKey));
context.credentials().install();
page.navigate(server.EMPTY_PAGE);
Object gotId = page.evaluate(
"async ({ rpId }) => {\n" +
" const challenge = crypto.getRandomValues(new Uint8Array(32));\n" +
" // No allowCredentials — relies on the re-seeded credential being discoverable.\n" +
" const cred = await navigator.credentials.get({\n" +
" publicKey: { challenge, rpId, userVerification: 'preferred' },\n" +
" });\n" +
" return cred.id;\n" +
"}", mapOf("rpId", "localhost"));
assertEquals(createdId, gotId);
}
}
@@ -40,14 +40,10 @@ public class TestBrowserTypeBasic extends TestBase {
assertEquals(getBrowserNameFromEnv(), browserType.name());
}
static boolean isChromiumOrWebKit() {
return isChromium() || isWebKit();
}
@Test
@DisabledIf(value="isChromiumOrWebKit", disabledReason="Connecting over CDP is supported in Chromium and WebKit")
@DisabledIf(value="com.microsoft.playwright.TestBase#isChromium", disabledReason="Non-chromium behavior")
void shouldThrowWhenTryingToConnectWithNotChromium() {
PlaywrightException e = assertThrows(PlaywrightException.class, () -> browserType.connectOverCDP("foo"));
assertTrue(e.getMessage().contains("Connecting over CDP is only supported in Chromium and WebKit."));
assertTrue(e.getMessage().contains("Connecting over CDP is only supported in Chromium."));
}
}
@@ -259,11 +259,7 @@ public class TestBrowserTypeConnect extends TestBase {
}
assertFalse(browser.isConnected());
PlaywrightException e = assertThrows(PlaywrightException.class, () -> page.waitForNavigation(() -> {}));
// The surfaced message depends on which call hits the closed connection first.
assertTrue(e.getMessage().contains("Browser closed") ||
e.getMessage().contains("Page closed") ||
e.getMessage().contains("Browser has been closed") ||
e.getMessage().contains("Target page, context or browser has been closed"), e.getMessage());
assertTrue(e.getMessage().contains("Browser closed") || e.getMessage().contains("Page closed") || e.getMessage().contains("Browser has been closed"), e.getMessage());
}
@Test
@@ -5,7 +5,6 @@ import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIf;
import org.junit.jupiter.api.io.CleanupMode;
import org.junit.jupiter.api.io.TempDir;
import java.io.IOException;
@@ -224,9 +223,7 @@ public class TestClientCertificates extends TestBase {
@Test
@DisabledIf(value="com.microsoft.playwright.TestClientCertificates#isWebKitMacOS", disabledReason="The network connection was lost.")
// No cleanup: on Windows Chromium may keep chrome_debug.log in the user data dir
// locked briefly after close, failing the deletion.
public void shouldWorkWithBrowserLaunchPersistentContext(@TempDir(cleanup = CleanupMode.NEVER) Path tmpDir) {
public void shouldWorkWithBrowserLaunchPersistentContext(@TempDir Path tmpDir) {
BrowserType.LaunchPersistentContextOptions options = new BrowserType.LaunchPersistentContextOptions()
.setIgnoreHTTPSErrors(true) // TODO: remove once we can pass a custom CA.
.setClientCertificates(asList(
@@ -22,7 +22,6 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIf;
import org.junit.jupiter.api.io.CleanupMode;
import org.junit.jupiter.api.io.TempDir;
import java.io.IOException;
@@ -47,9 +46,7 @@ public class TestDefaultBrowserContext2 extends TestBase {
private BrowserContext persistentContext;
// No cleanup: on Windows Chromium may keep chrome_debug.log in the user data dir
// locked briefly after close, failing the deletion.
@TempDir(cleanup = CleanupMode.NEVER) Path tempDir;
@TempDir Path tempDir;
@AfterEach
void closePersistentContext() {
@@ -22,7 +22,6 @@ import com.microsoft.playwright.options.HttpCredentials;
import com.microsoft.playwright.options.HttpCredentialsSend;
import com.microsoft.playwright.options.HttpHeader;
import com.microsoft.playwright.options.RequestOptions;
import com.microsoft.playwright.options.ServerAddr;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -253,25 +252,6 @@ public class TestGlobalFetch extends TestBase {
assertEquals(200, response.status());
}
@Test
void shouldReturnServerAddressFromResponse() {
APIRequestContext request = playwright.request().newContext();
APIResponse response = request.get(server.EMPTY_PAGE);
ServerAddr address = response.serverAddr();
assertNotNull(address);
assertEquals(server.PORT, address.port);
assertTrue(asList("127.0.0.1", "::1").contains(address.ipAddress), address.ipAddress);
request.dispose();
}
@Test
void shouldReturnNullSecurityDetailsForHttpResponse() {
APIRequestContext request = playwright.request().newContext();
APIResponse response = request.get(server.EMPTY_PAGE);
assertNull(response.securityDetails());
request.dispose();
}
@Test
void shouldResolveUrlRelativeToGobalBaseURLOption() {
APIRequestContext request = playwright.request().newContext(new APIRequest.NewContextOptions().setBaseURL(server.PREFIX));
@@ -17,15 +17,12 @@
package com.microsoft.playwright;
import com.microsoft.playwright.options.KeyboardModifier;
import com.microsoft.playwright.options.ScrollMode;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
import static java.util.Arrays.asList;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
@Tag("smoke")
public class TestLocatorClick extends TestBase {
@Test
@@ -105,24 +102,4 @@ public class TestLocatorClick extends TestBase {
page.evaluate("result")
);
}
@Test
void shouldNotScrollWhenScrollIsNone() {
page.setContent("<div style='height: 2000px;'>filler</div>\n" +
"<button onclick='window._clicked=true'>click me</button>");
PlaywrightException e = assertThrows(PlaywrightException.class,
() -> page.locator("button").click(new Locator.ClickOptions().setScroll(ScrollMode.NONE).setTimeout(2000)));
assertTrue(e.getMessage().contains("element is outside of the viewport"), e.getMessage());
assertNull(page.evaluate("window._clicked"));
assertEquals(0, page.evaluate("() => window.scrollY"));
}
@Test
void shouldClickInViewportElementWhenScrollIsNone() {
page.setContent("<button onclick='window._clicked=true'>click me</button>\n" +
"<div style='height: 2000px;'></div>");
page.locator("button").click(new Locator.ClickOptions().setScroll(ScrollMode.NONE).setTimeout(2000));
assertEquals(true, page.evaluate("window._clicked"));
assertEquals(0, page.evaluate("() => window.scrollY"));
}
}
@@ -36,18 +36,4 @@ public class TestLocatorHighlight extends TestBase {
BoundingBox box2 = page.locator("x-pw-highlight").boundingBox();
assertEquals(new Gson().toJson(box2), new Gson().toJson(box1));
}
@Test
void highlightAndHideHighlightShouldNotThrow() {
page.setContent("<input type='text' />");
AutoCloseable disposable = page.locator("input").highlight(new Locator.HighlightOptions().setStyle("outline: 2px dashed red"));
try {
disposable.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
page.locator("input").highlight();
page.locator("input").hideHighlight();
page.hideHighlight();
}
}
@@ -140,49 +140,4 @@ public class TestLocatorMisc extends TestBase{
assertThat(page.locator(".item").filter(new Locator.FilterOptions().setVisible(true)).getByText("data3")).hasText("visible data3");
assertThat(page.locator(".item").filter(new Locator.FilterOptions().setVisible(false)).getByText("data1")).hasText("Hidden data1");
}
@Test
void waitForFunctionShouldWaitForAnAttributeToAppear() {
page.setContent("<button id=toggle>Menu</button>");
page.evaluate("() => setTimeout(() => document.querySelector('#toggle').setAttribute('aria-expanded', 'true'), 500)");
page.locator("#toggle").waitForFunction("element => element.hasAttribute('aria-expanded')");
}
@Test
void waitForFunctionShouldReturnImmediatelyWhenAlreadyTruthy() {
page.setContent("<div id=target>yes</div>");
page.locator("#target").waitForFunction("element => element.textContent === 'yes'");
}
@Test
void waitForFunctionShouldAcceptElementHandleArguments() {
page.setContent("<div id=a></div><div id=b>value</div>");
ElementHandle handle = page.querySelector("#b");
page.locator("#a").waitForFunction("(element, other) => other.textContent === 'value'", handle);
}
@Test
void waitForFunctionShouldThrowWhenPredicateThrows() {
page.setContent("<div id=target>no</div>");
PlaywrightException e = assertThrows(PlaywrightException.class,
() -> page.locator("#target").waitForFunction("() => { throw new Error('oh my'); }"));
assertTrue(e.getMessage().contains("oh my"), e.getMessage());
}
@Test
void waitForFunctionShouldThrowOnStrictModeViolation() {
page.setContent("<div class=x>1</div><div class=x>2</div>");
PlaywrightException e = assertThrows(PlaywrightException.class,
() -> page.locator("div.x").waitForFunction("() => true"));
assertTrue(e.getMessage().contains("strict mode violation"), e.getMessage());
}
@Test
void waitForFunctionShouldRespectTimeout() {
page.setContent("<div id=target>no</div>");
PlaywrightException e = assertThrows(PlaywrightException.class,
() -> page.locator("#target").waitForFunction("element => element.textContent === 'yes'", null,
new Locator.WaitForFunctionOptions().setTimeout(500)));
assertTrue(e.getMessage().contains("Timeout 500ms exceeded"), e.getMessage());
}
}
@@ -341,14 +341,4 @@ public class TestPageAriaSnapshot {
" - /placeholder: Placeholder");
}
@Test
void pageMatchesAriaSnapshot(Page page) {
page.setContent("<h1>hello</h1>");
assertThat(page).matchesAriaSnapshot("- heading \"hello\" [level=1]");
AssertionFailedError e = assertThrows(AssertionFailedError.class,
() -> assertThat(page).matchesAriaSnapshot("- heading \"world\"",
new com.microsoft.playwright.assertions.PageAssertions.MatchesAriaSnapshotOptions().setTimeout(1000)));
org.junit.jupiter.api.Assertions.assertTrue(e.getMessage().contains("Page expected to match Aria snapshot"), e.getMessage());
}
}
@@ -82,10 +82,7 @@ public class TestPageAriaSnapshotAI {
"Link with a button <button style=\"cursor: pointer\">Button</button>" +
"</a>");
String snapshot = aiSnapshot(page);
// The link's name is redundant - "Link with a button" prints as text and "Button" as the button -
// so it is dropped even though the node is clickable.
assertTrue(snapshot.contains("link [ref=e2] [cursor=pointer]"), snapshot);
assertTrue(snapshot.contains("text: Link with a button"), snapshot);
assertTrue(snapshot.contains("link \"Link with a button Button\" [ref=e2] [cursor=pointer]"), snapshot);
// The button inside a cursor-pointer link should not get a redundant [cursor=pointer]
assertTrue(snapshot.contains("button \"Button\" [ref=e3]"), snapshot);
assertFalse(snapshot.contains("button \"Button\" [ref=e3] [cursor=pointer]"), snapshot);
@@ -16,7 +16,6 @@
package com.microsoft.playwright;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIf;
@@ -31,7 +30,6 @@ import static com.microsoft.playwright.options.LoadState.LOAD;
import static java.util.Arrays.asList;
import static org.junit.jupiter.api.Assertions.*;
@Tag("smoke")
public class TestPageBasic extends TestBase {
@Test
@@ -1,125 +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;
import com.microsoft.playwright.options.FilePayload;
import com.microsoft.playwright.options.DropPayload;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.microsoft.playwright.Utils.mapOf;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestPageDrop extends TestBase {
private void setupDropzone() {
page.setContent("<style>#dropzone { width: 300px; height: 200px; border: 2px dashed #888; }</style>\n" +
"<div id=\"dropzone\"></div>\n" +
"<script>\n" +
" window.__dropInfo = null;\n" +
" const zone = document.getElementById('dropzone');\n" +
" zone.addEventListener('dragenter', e => e.preventDefault());\n" +
" zone.addEventListener('dragover', e => e.preventDefault());\n" +
" zone.addEventListener('drop', async e => {\n" +
" e.preventDefault();\n" +
" const files = [];\n" +
" for (const file of e.dataTransfer.files)\n" +
" files.push({ name: file.name, type: file.type, size: file.size, text: await file.text() });\n" +
" const data = {};\n" +
" for (const t of e.dataTransfer.types) {\n" +
" if (t !== 'Files')\n" +
" data[t] = e.dataTransfer.getData(t);\n" +
" }\n" +
" window.__dropInfo = { files, data };\n" +
" });\n" +
"</script>");
}
@SuppressWarnings("unchecked")
private Map<String, Object> waitForDropInfo() {
page.waitForCondition(() -> page.evaluate("window.__dropInfo") != null);
return (Map<String, Object>) page.evaluate("window.__dropInfo");
}
@Test
void shouldDropFilePayload() {
setupDropzone();
page.locator("#dropzone").drop(new DropPayload().setFiles(new FilePayload("note.txt", "text/plain", "hello".getBytes(StandardCharsets.UTF_8))));
Map<String, Object> info = waitForDropInfo();
@SuppressWarnings("unchecked")
List<Map<String, Object>> files = (List<Map<String, Object>>) info.get("files");
assertEquals(1, files.size());
assertEquals("note.txt", files.get(0).get("name"));
assertEquals("text/plain", files.get(0).get("type"));
assertEquals("hello", files.get(0).get("text"));
}
@Test
void shouldDropMultipleFilePayloads() {
setupDropzone();
page.locator("#dropzone").drop(new DropPayload().setFiles(new FilePayload[] {
new FilePayload("a.txt", "text/plain", "AAA".getBytes(StandardCharsets.UTF_8)),
new FilePayload("b.txt", "text/plain", "BB".getBytes(StandardCharsets.UTF_8)),
}));
Map<String, Object> info = waitForDropInfo();
@SuppressWarnings("unchecked")
List<Map<String, Object>> files = (List<Map<String, Object>>) info.get("files");
assertEquals(2, files.size());
assertEquals("a.txt", files.get(0).get("name"));
assertEquals("AAA", files.get(0).get("text"));
assertEquals("b.txt", files.get(1).get("name"));
assertEquals("BB", files.get(1).get("text"));
}
@Test
void shouldDropClipboardLikeData() {
setupDropzone();
Map<String, String> data = new HashMap<>();
data.put("text/plain", "hello world");
data.put("text/uri-list", "https://example.com");
page.locator("#dropzone").drop(new DropPayload().setData(data));
Map<String, Object> info = waitForDropInfo();
@SuppressWarnings("unchecked")
List<?> files = (List<?>) info.get("files");
assertTrue(files.isEmpty(), "expected no files");
@SuppressWarnings("unchecked")
Map<String, String> droppedData = (Map<String, String>) info.get("data");
assertEquals("hello world", droppedData.get("text/plain"));
assertEquals("https://example.com", droppedData.get("text/uri-list"));
}
@Test
void shouldDropFileByLocalPath(@org.junit.jupiter.api.io.TempDir Path dir) throws Exception {
setupDropzone();
Path filePath = dir.resolve("hello.txt");
Files.write(filePath, "path-content".getBytes(StandardCharsets.UTF_8));
page.locator("#dropzone").drop(new DropPayload().setFiles(filePath));
Map<String, Object> info = waitForDropInfo();
@SuppressWarnings("unchecked")
List<Map<String, Object>> files = (List<Map<String, Object>>) info.get("files");
assertEquals(1, files.size());
assertEquals("hello.txt", files.get(0).get("name"));
assertEquals("path-content", files.get(0).get("text"));
}
}
@@ -30,7 +30,7 @@ public class TestPageEventPageError extends TestBase {
page.evaluate("async () => {\n" +
" for (let i = 0; i < 301; i++)\n" +
" window.setTimeout(() => { throw new Error('error' + i); }, 0);\n" +
" await new Promise(f => window.setTimeout(f, 2000));\n" +
" await new Promise(f => window.setTimeout(f, 100));\n" +
" }");
List<String> errors = page.pageErrors();
@@ -164,6 +164,35 @@ public class TestPageExposeFunction extends TestBase {
assertEquals( 7, ((Map) result).get("x"));
}
@Test
void exposeBindingHandleShouldWork() {
JSHandle[] target = { null };
page.exposeBinding("logme", (source, args) -> {
target[0] = (JSHandle) args[0];
return 17;
}, new Page.ExposeBindingOptions().setHandle(true));
Object result = page.evaluate("async function() {\n" +
" return window['logme']({ foo: 42 });\n" +
"}");
assertEquals(42, target[0].evaluate("x => x.foo"));
assertEquals(17, result);
}
@Test
void exposeBindingHandleShouldNotThrowDuringNavigation() {
page.exposeBinding("logme", (source, args) -> {
return 17;
}, new Page.ExposeBindingOptions().setHandle(true));
page.navigate(server.EMPTY_PAGE);
page.waitForNavigation(new Page.WaitForNavigationOptions().setWaitUntil(LOAD), () -> {
page.evaluate("async url => {\n" +
" window['logme']({ foo: 42 });\n" +
" window.location.href = url;\n" +
"}", server.PREFIX + "/one-style.html");
});
}
@Test
void shouldThrowForDuplicateRegistrations() {
page.exposeFunction("foo", args -> null);
@@ -173,6 +202,28 @@ public class TestPageExposeFunction extends TestBase {
assertTrue(e.getMessage().contains("Function \"foo\" has been already registered"));
}
@Test
void exposeBindingHandleShouldThrowForMultipleArguments() {
page.exposeBinding("logme", (source, args) -> {
return 17;
}, new Page.ExposeBindingOptions().setHandle(true));
assertEquals(17, page.evaluate("async function() {\n" +
" return window['logme']({ foo: 42 });\n" +
"}"));
assertEquals(17, page.evaluate("async function() {\n" +
" return window['logme']({ foo: 42 }, undefined, undefined);\n" +
"}"));
assertEquals(17, page.evaluate("async function() {\n" +
" return window['logme'](undefined, undefined, undefined);\n" +
"}"));
PlaywrightException e = assertThrows(PlaywrightException.class, () -> {
page.evaluate("async function() {\n" +
" return window['logme'](1, 2);\n" +
"}");
});
assertTrue(e.getMessage().contains("exposeBindingHandle supports a single argument, 2 received"));
}
@Test
void shouldSerializeCycles() {
Object[] object = { null };
@@ -20,11 +20,9 @@ import com.microsoft.playwright.options.Clip;
import com.microsoft.playwright.options.ScreenshotAnimations;
import com.microsoft.playwright.options.ScreenshotCaret;
import com.microsoft.playwright.options.ScreenshotScale;
import com.microsoft.playwright.options.ScreenshotType;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIf;
import org.junit.jupiter.api.io.TempDir;
import org.opentest4j.AssertionFailedError;
import javax.imageio.ImageIO;
@@ -67,39 +65,6 @@ public class TestPageScreenshot extends TestBase {
// expect(screenshot).toMatchSnapshot("screenshot-clip-rect.png");
}
private static void assertWebp(byte[] screenshot) {
// WebP magic: "RIFF" at offset 0, "WEBP" at offset 8.
assertTrue(screenshot.length > 12);
assertEquals("RIFF", new String(screenshot, 0, 4, java.nio.charset.StandardCharsets.US_ASCII));
assertEquals("WEBP", new String(screenshot, 8, 4, java.nio.charset.StandardCharsets.US_ASCII));
}
@Test
void shouldProduceAValidWebpScreenshot() {
page.setViewportSize(300, 300);
page.navigate(server.EMPTY_PAGE);
byte[] screenshot = page.screenshot(new Page.ScreenshotOptions().setType(ScreenshotType.WEBP));
assertWebp(screenshot);
}
@Test
void pathOptionShouldDetectWebp(@TempDir Path tmpDir) throws IOException {
page.setViewportSize(300, 300);
page.navigate(server.EMPTY_PAGE);
Path outputPath = tmpDir.resolve("screenshot.webp");
byte[] screenshot = page.screenshot(new Page.ScreenshotOptions().setPath(outputPath));
assertWebp(screenshot);
assertWebp(Files.readAllBytes(outputPath));
}
@Test
void qualityOptionShouldWorkForWebp() {
page.navigate(server.PREFIX + "/grid.html");
byte[] lowQuality = page.screenshot(new Page.ScreenshotOptions().setType(ScreenshotType.WEBP).setQuality(0));
byte[] highQuality = page.screenshot(new Page.ScreenshotOptions().setType(ScreenshotType.WEBP).setQuality(100));
assertTrue(lowQuality.length < highQuality.length);
}
static private void rafraf(Page page) {
// Do a double raf since single raf does not
// actually guarantee a new animation frame.

Some files were not shown because too many files have changed in this diff Show More