Compare commits
1 Commits
main
..
release-1.51
| Author | SHA1 | Date | |
|---|---|---|---|
| 4307548995 |
@@ -1,10 +1,6 @@
|
||||
trigger: none
|
||||
pr: none
|
||||
|
||||
trigger:
|
||||
tags:
|
||||
include:
|
||||
- '*'
|
||||
|
||||
resources:
|
||||
repositories:
|
||||
- repository: 1esPipelines
|
||||
@@ -37,8 +33,8 @@ extends:
|
||||
artifact: esrp-build
|
||||
steps:
|
||||
- bash: |
|
||||
if [[ ! "$CURRENT_BRANCH" =~ ^v1\..* ]]; then
|
||||
echo "Can only publish from a release tag branch (v1.*)."
|
||||
if [[ ! "$CURRENT_BRANCH" =~ ^release-.* ]]; then
|
||||
echo "Can only publish from a release branch."
|
||||
echo "Unexpected branch name: $CURRENT_BRANCH"
|
||||
exit 1
|
||||
fi
|
||||
@@ -90,7 +86,7 @@ extends:
|
||||
folderlocation: '$(Build.ArtifactStagingDirectory)/esrp-build'
|
||||
waitforreleasecompletion: true
|
||||
owners: 'yurys@microsoft.com'
|
||||
approvers: 'yurys@microsoft.com'
|
||||
approvers: 'maxschmitt@microsoft.com'
|
||||
serviceendpointurl: 'https://api.esrp.microsoft.com'
|
||||
mainpublisher: 'Playwright'
|
||||
domaintenantid: '975f013f-7f24-47e8-a7d3-abc4752bf346'
|
||||
|
||||
@@ -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.
|
||||
@@ -1,202 +0,0 @@
|
||||
---
|
||||
name: playwright-roll
|
||||
description: Roll Playwright Java to a new version
|
||||
---
|
||||
|
||||
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
|
||||
|
||||
Rolling includes:
|
||||
- updating client implementation to match changes in the upstream JS implementation (see ../playwright/packages/playwright-core/src/client)
|
||||
- adding a couple of new tests to verify new/changed functionality
|
||||
|
||||
## Mimicking the JavaScript implementation
|
||||
|
||||
The Java client is a port of the JS client in `../playwright/packages/playwright-core/src/client/`. When implementing a new or changed method, always read the corresponding JS file first and mirror its logic:
|
||||
|
||||
```
|
||||
../playwright/packages/playwright-core/src/client/browserContext.ts
|
||||
../playwright/packages/playwright-core/src/client/page.ts
|
||||
../playwright/packages/playwright-core/src/client/tracing.ts
|
||||
../playwright/packages/playwright-core/src/client/video.ts
|
||||
../playwright/packages/playwright-core/src/client/locator.ts
|
||||
../playwright/packages/playwright-core/src/client/network.ts
|
||||
...
|
||||
```
|
||||
|
||||
Key translation rules:
|
||||
|
||||
**Protocol calls** — `await this._channel.methodName(params)` → `sendMessage("methodName", params, NO_TIMEOUT)`
|
||||
|
||||
**Extracting a returned channel object from a result** — JS uses `SomeClass.from(result.foo)` which resolves the JS-side object for a channel reference. In Java, the object was already created when the server sent `__create__`, so extract it from the connection: `connection.getExistingObject(result.getAsJsonObject("foo").get("guid").getAsString())`
|
||||
|
||||
**Async/await** — all `await` calls become synchronous `sendMessage(...)` calls since the Java client is synchronous.
|
||||
|
||||
**`undefined` / optional params** — JS `options?.foo` checks translate to `if (options != null && options.foo != null)` null checks before adding to the params `JsonObject`.
|
||||
|
||||
**`_channel` fields** — the JS `this._channel.foo` maps to calling `sendMessage("foo", ...)` on `this` in the Impl class.
|
||||
|
||||
**Channel object references in params** — when a JS call passes a channel object as a param (e.g. `{ frame: frame._channel }`), in Java pass the guid: `params.addProperty("frame", ((FrameImpl) frame).guid)`.
|
||||
|
||||
## Fixing generator and compilation errors
|
||||
|
||||
After running `./scripts/roll_driver.sh`, the build often fails because the generated Java interfaces reference new types or methods that the generator doesn't know how to handle yet, and the `*Impl` classes don't implement new interface methods.
|
||||
|
||||
### ApiGenerator.java fixes (tools/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java)
|
||||
|
||||
The generator has hardcoded lists that control which imports are added to each generated file. When new classes appear in the API, add them to the relevant lists in `Interface.writeTo`:
|
||||
- `options.*` import list — add new classes that use types from the options package
|
||||
- `java.util.*` import list — add new classes that use `List`, `Map`, etc.
|
||||
- `java.util.function.Consumer` list — add new classes with `Consumer`-typed event handlers
|
||||
|
||||
Type mapping: when JS-only types (like `Disposable`) are used as return types in Java-compatible methods, add a mapping in `convertBuiltinType`. For example, `Disposable` → `AutoCloseable`.
|
||||
|
||||
Event handler generation: events with `void` type generate invalid `Consumer<void>`. Handle this case in `Event.writeListenerMethods` by emitting `Runnable` instead.
|
||||
|
||||
After editing the generator, recompile and re-run it:
|
||||
```
|
||||
mvn -f tools/api-generator/pom.xml compile -q
|
||||
mvn -f tools/api-generator/pom.xml exec:java -Dexec.mainClass=com.microsoft.playwright.tools.ApiGenerator
|
||||
```
|
||||
|
||||
### Impl class fixes (playwright/src/main/java/com/microsoft/playwright/impl/)
|
||||
|
||||
After regenerating, compile `playwright/` to find what's missing:
|
||||
```
|
||||
mvn -f playwright/pom.xml compile 2>&1 | grep "ERROR"
|
||||
```
|
||||
|
||||
Common patterns:
|
||||
|
||||
**Return type changed (e.g. `void` → `AutoCloseable`):** Update the method signature in the Impl class and return an appropriate `AutoCloseable`. Check the JS client to see what kind of disposable is used:
|
||||
- If JS returns `DisposableObject.from(result.disposable)` — the server created a disposable channel object. Extract its guid from the protocol result and return `connection.getExistingObject(guid)` (a `DisposableObject`).
|
||||
- If JS returns `new DisposableStub(() => this.someCleanup())` — it's a local callback. Return `new DisposableStub(this::someCleanup)` in Java.
|
||||
- Examples: `addInitScript`/`exposeBinding`/`exposeFunction` → `DisposableObject`; `route(...)` → `DisposableStub(() -> unroute(...))`; `Tracing.group` → `DisposableStub(this::groupEnd)`; `Video.start` → `DisposableStub(this::stop)`.
|
||||
|
||||
**New method missing:** Add a stub implementation. Common patterns:
|
||||
- Simple protocol message: `sendMessage("methodName", params, NO_TIMEOUT)`
|
||||
- New property accessor (e.g. from initializer): `return initializer.get("fieldName").getAsString()`
|
||||
- Delegation to mainFrame (for Page methods): `return mainFrame.locator(":root").method(...)`
|
||||
|
||||
**New interface entirely (e.g. `Debugger`):** Create a new `*Impl` class extending `ChannelOwner`, implement the interface, and register the type in `Connection.java`'s switch statement. Initialize the field from the parent's initializer in the parent's constructor (e.g. `connection.getExistingObject(initializer.getAsJsonObject("debugger").get("guid").getAsString())`).
|
||||
|
||||
**Field visibility:** If a field needs to be accessed from a sibling Impl class (e.g. setting `existingResponse` on `RequestImpl` from `BrowserContextImpl`), change it from `private` to package-private.
|
||||
|
||||
**`ListenerCollection` only supports `Consumer<T>`, not `Runnable`.** For void events that use `Runnable` handlers, maintain a plain `List<Runnable>` instead.
|
||||
|
||||
**Protocol changes that remove events** — when a method's response now returns an object directly instead of via a subsequent event, update the Impl to capture it from the `sendMessage` result and remove the old event handler. Example: `videoStart` used to fire a `"video"` page event to deliver the artifact; it now returns the artifact directly in the response. Check git history of the upstream JS client when tests hang unexpectedly.
|
||||
|
||||
**Protocol parameter renames** — protocol parameter names can change between versions (e.g. `wsEndpoint` → `endpoint` in `BrowserType.connect`). When a test fails with `expected string, got undefined` or similar validation errors from the driver, check `packages/protocol/src/protocol.yml` for the current parameter names and update the corresponding `params.addProperty(...)` call in the Impl class. Also check the JS client (`src/client/`) to see how it builds the params object.
|
||||
|
||||
## Rebuilding the driver-bundle after a roll
|
||||
|
||||
`./scripts/roll_driver.sh` does the whole roll pipeline end-to-end: bumps `DRIVER_VERSION`, downloads new driver files into `driver-bundle/src/main/resources/driver/<platform>/`, regenerates `api.json` and the Java interfaces, and updates the README. When all of that succeeds, the next `mvn` invocation that touches `driver-bundle` will pick up the new files and you don't need to think about it.
|
||||
|
||||
But if any step in the pipeline fails (the very common case is the API generator throwing on a new type — see *Fixing generator and compilation errors*), the run aborts before `driver-bundle/target/classes/` has been refreshed. From that point on, until you manually rebuild `driver-bundle`, the test JVM will load the **old** driver from the cached `target/classes`/installed jar even though the source resources have already been swapped to the new version.
|
||||
|
||||
Fix — rebuild `driver-bundle` once before re-running tests:
|
||||
```
|
||||
mvn -f driver-bundle/pom.xml install -DskipTests
|
||||
```
|
||||
|
||||
## Porting and verifying tests
|
||||
|
||||
**Before porting an upstream test file, check the API exists in Java.** The upstream repo may have test files for brand-new APIs that haven't been added to the Java interface yet (e.g., `screencast.spec.ts` tests `page.screencast` which may not be in the generated `Page.java`). Check `git diff main --name-only` to see what interfaces were added this roll, and verify the method exists in the generated Java interface before porting.
|
||||
|
||||
**Java test file names don't always match upstream spec names.** `TestScreencast.java` tests `recordVideo` video-file recording (which corresponds to `video.spec.ts`), not the newer `page.screencast` streaming API (`screencast.spec.ts`). When comparing coverage, check test *content*, not just file names.
|
||||
|
||||
**Remove tests for behavior that was removed upstream.** When the JS client drops a client-side error check (e.g., "Page is not yet closed before saveAs", "Page did not produce any video frames"), delete the corresponding Java tests rather than trying to keep them passing. Check the upstream `tests/library/` spec to confirm the behavior is gone.
|
||||
|
||||
**Run the full suite to catch regressions, re-run flaky failures in isolation.** Some tests (e.g., `TestClientCertificates#shouldKeepSupportingHttp`) time out only under heavy parallel load. Run the failing test alone to confirm it's flaky before investigating further.
|
||||
|
||||
## Diagnosing hanging tests
|
||||
|
||||
When `mvn test` hangs and surefire eventually times the JVM out, it writes thread dumps to `playwright/target/surefire-reports/<timestamp>-jvmRun*.dump`. To find the stuck test:
|
||||
|
||||
```
|
||||
grep "com.microsoft.playwright.Test" playwright/target/surefire-reports/*-jvmRun1.dump | sort -u
|
||||
```
|
||||
|
||||
Each line is a stack frame inside a test method — typically you'll see one or two test methods blocked on a `Future.get()`, `waitForCondition`, or similar. That's the hanging test.
|
||||
|
||||
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 (`../`).
|
||||
- use the "gh" cli to interact with GitHub
|
||||
@@ -13,9 +13,9 @@ jobs:
|
||||
environment: Docker
|
||||
if: github.repository == 'microsoft/playwright-java'
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: Azure login
|
||||
uses: azure/login@v3
|
||||
uses: azure/login@v2
|
||||
with:
|
||||
client-id: ${{ secrets.AZURE_DOCKER_CLIENT_ID }}
|
||||
tenant-id: ${{ secrets.AZURE_DOCKER_TENANT_ID }}
|
||||
@@ -23,8 +23,8 @@ jobs:
|
||||
- name: Login to ACR via OIDC
|
||||
run: az acr login --name playwright
|
||||
- name: Set up Docker QEMU for arm64 docker builds
|
||||
uses: docker/setup-qemu-action@v4
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
platforms: arm64
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- run: ./utils/docker/publish_docker.sh stable
|
||||
|
||||
@@ -18,19 +18,11 @@ 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@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK 1.8
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: zulu
|
||||
java-version: 8
|
||||
@@ -73,13 +65,13 @@ jobs:
|
||||
browser-channel: msedge
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Media Pack
|
||||
if: matrix.os == 'windows-latest'
|
||||
shell: powershell
|
||||
run: Install-WindowsFeature Server-Media-Foundation
|
||||
- name: Set up JDK 1.8
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: zulu
|
||||
java-version: 8
|
||||
@@ -108,9 +100,9 @@ jobs:
|
||||
browser: [chromium, firefox, webkit]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: adopt
|
||||
java-version: 21
|
||||
|
||||
@@ -13,9 +13,9 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.m2
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
|
||||
@@ -20,58 +20,16 @@ jobs:
|
||||
test:
|
||||
name: Test
|
||||
timeout-minutes: 120
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
env:
|
||||
PW_MAX_RETRIES: 3
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
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@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build Docker image
|
||||
run: bash utils/docker/build.sh --amd64 ${{ matrix.flavor }} playwright-java:localbuild-${{ matrix.flavor }}
|
||||
- name: Test
|
||||
run: |
|
||||
bash utils/docker/build.sh --${{ matrix.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)
|
||||
echo "CONTAINER_ID=$CONTAINER_ID" >> $GITHUB_ENV
|
||||
|
||||
- name: Copy repository inside docker 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
|
||||
|
||||
- name: Test ClassLoader
|
||||
run: |
|
||||
docker exec "${CONTAINER_ID}" /home/pwuser/playwright/tools/test-spring-boot-starter/package_and_run_async_test.sh
|
||||
|
||||
- name: Stop container
|
||||
run: |
|
||||
docker stop "$CONTAINER_ID"
|
||||
CONTAINER_ID="$(docker run --rm --ipc=host -v $(pwd):/root/playwright --name playwright-docker-test -d -t playwright-java:localbuild-${{ matrix.flavor }} /bin/bash)"
|
||||
docker exec "${CONTAINER_ID}" /root/playwright/tools/test-local-installation/create_project_and_run_tests.sh
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
name: "Internal Tests"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release-*
|
||||
|
||||
jobs:
|
||||
trigger:
|
||||
name: "trigger"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- run: |
|
||||
curl -X POST \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-H "Authorization: token ${GH_TOKEN}" \
|
||||
--data "{\"event_type\": \"playwright_tests_java\", \"client_payload\": {\"ref\": \"${GITHUB_SHA}\"}}" \
|
||||
https://api.github.com/repos/microsoft/playwright-browsers/dispatches
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.REPOSITORY_DISPATCH_PERSONAL_ACCESS_TOKEN }}
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download drivers
|
||||
run: scripts/download_driver.sh
|
||||
- name: Regenerate APIs
|
||||
|
||||
+2
-2
@@ -32,9 +32,9 @@ scripts/download_driver.sh
|
||||
mvn compile
|
||||
mvn test
|
||||
# Executing a single test
|
||||
BROWSER=chromium mvn test -Dtest=TestPageNetworkSizes#shouldHaveTheCorrectResponseBodySize
|
||||
BROWSER=chromium mvn test --projects=playwright -Dtest=TestPageNetworkSizes#shouldHaveTheCorrectResponseBodySize
|
||||
# Executing a single test class
|
||||
BROWSER=chromium mvn test -Dtest=TestPageNetworkSizes
|
||||
BROWSER=chromium mvn test --projects=playwright -Dtest=TestPageNetworkSizes
|
||||
```
|
||||
|
||||
### Generating API
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
path_classifiers:
|
||||
tests:
|
||||
- "playwright/src/test/**"
|
||||
@@ -10,9 +10,9 @@ Playwright is a Java library to automate [Chromium](https://www.chromium.org/Hom
|
||||
|
||||
| | Linux | macOS | Windows |
|
||||
| :--- | :---: | :---: | :---: |
|
||||
| Chromium <!-- GEN:chromium-version -->148.0.7778.96<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| WebKit <!-- GEN:webkit-version -->26.4<!-- GEN:stop --> | ✅ | ✅ | ✅ |
|
||||
| Firefox <!-- GEN:firefox-version -->150.0.2<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Chromium <!-- GEN:chromium-version -->134.0.6998.35<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| WebKit <!-- GEN:webkit-version -->18.4<!-- GEN:stop --> | ✅ | ✅ | ✅ |
|
||||
| Firefox <!-- GEN:firefox-version -->135.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
|
||||
## Documentation
|
||||
|
||||
|
||||
+14
-2
@@ -2,6 +2,18 @@
|
||||
|
||||
* make sure to have at least Java 8 and Maven 3.6.3
|
||||
* clone playwright for java: http://github.com/microsoft/playwright-java
|
||||
* roll the driver and update generated sources: `./scripts/roll_driver.sh next`
|
||||
* fix any errors
|
||||
* `./scripts/roll_driver.sh 1.47.0-beta-1726138322000`
|
||||
* commit & send PR with the roll
|
||||
|
||||
## Finding driver version
|
||||
|
||||
For development versions of Playwright, you can find the latest version by looking at [publish_canary](https://github.com/microsoft/playwright/actions/workflows/publish_canary.yml) workflow -> `publish canary NPM & Publish canary Docker` -> `build & publish driver` step -> `PACKAGE_VERSION`
|
||||
<img width="960" alt="image" src="https://github.com/microsoft/playwright-java/assets/9798949/4f33a7f1-b39a-4179-8ae7-fb1d84094c75">
|
||||
|
||||
|
||||
# Updating Version
|
||||
|
||||
```bash
|
||||
./scripts/set_maven_version.sh 1.15.0
|
||||
```
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>parent-pom</artifactId>
|
||||
<version>1.50.0-SNAPSHOT</version>
|
||||
<version>1.51.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>driver-bundle</artifactId>
|
||||
|
||||
@@ -114,7 +114,7 @@ public class DriverJar extends Driver {
|
||||
}
|
||||
|
||||
public static URI getDriverResourceURI() throws URISyntaxException {
|
||||
ClassLoader classloader = DriverJar.class.getClassLoader();
|
||||
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
|
||||
return classloader.getResource("driver/" + platformDir()).toURI();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>parent-pom</artifactId>
|
||||
<version>1.50.0-SNAPSHOT</version>
|
||||
<version>1.51.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>driver</artifactId>
|
||||
|
||||
+2
-2
@@ -6,11 +6,11 @@
|
||||
|
||||
<groupId>org.example</groupId>
|
||||
<artifactId>examples</artifactId>
|
||||
<version>1.50.0-SNAPSHOT</version>
|
||||
<version>1.51.0</version>
|
||||
<name>Playwright Client Examples</name>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<playwright.version>1.60.0</playwright.version>
|
||||
<playwright.version>1.51.0</playwright.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
||||
+1
-10
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>parent-pom</artifactId>
|
||||
<version>1.50.0-SNAPSHOT</version>
|
||||
<version>1.51.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>playwright</artifactId>
|
||||
@@ -57,15 +57,6 @@
|
||||
<groupId>org.java-websocket</groupId>
|
||||
<artifactId>Java-WebSocket</artifactId>
|
||||
</dependency>
|
||||
<!--
|
||||
The following slf4j-simple dependency resolves the warning:
|
||||
'SLF4J(W): No SLF4J providers were found.'
|
||||
This warning is produced by the org.java-websocket library.
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
|
||||
@@ -51,10 +51,6 @@ public interface APIRequest {
|
||||
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
|
||||
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
|
||||
*
|
||||
* <p> Client certificate authentication is only active when at least one client certificate is provided. If you want to reject
|
||||
* all client certificates sent by the server, you need to provide a client certificate with an {@code origin} that does
|
||||
* not match any of the domains you plan to visit.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
|
||||
* replacing {@code localhost} with {@code local.playwright}.
|
||||
*/
|
||||
@@ -76,12 +72,6 @@ public interface APIRequest {
|
||||
* Whether to ignore HTTPS errors when sending network requests. Defaults to {@code false}.
|
||||
*/
|
||||
public Boolean ignoreHTTPSErrors;
|
||||
/**
|
||||
* Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is
|
||||
* exceeded. Defaults to {@code 20}. Pass {@code 0} to not follow redirects. This can be overwritten for each request
|
||||
* individually.
|
||||
*/
|
||||
public Integer maxRedirects;
|
||||
/**
|
||||
* Network proxy settings.
|
||||
*/
|
||||
@@ -138,10 +128,6 @@ public interface APIRequest {
|
||||
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
|
||||
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
|
||||
*
|
||||
* <p> Client certificate authentication is only active when at least one client certificate is provided. If you want to reject
|
||||
* all client certificates sent by the server, you need to provide a client certificate with an {@code origin} that does
|
||||
* not match any of the domains you plan to visit.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
|
||||
* replacing {@code localhost} with {@code local.playwright}.
|
||||
*/
|
||||
@@ -185,15 +171,6 @@ public interface APIRequest {
|
||||
this.ignoreHTTPSErrors = ignoreHTTPSErrors;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is
|
||||
* exceeded. Defaults to {@code 20}. Pass {@code 0} to not follow redirects. This can be overwritten for each request
|
||||
* individually.
|
||||
*/
|
||||
public NewContextOptions setMaxRedirects(int maxRedirects) {
|
||||
this.maxRedirects = maxRedirects;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Network proxy settings.
|
||||
*/
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -115,10 +106,6 @@ public interface Browser extends AutoCloseable {
|
||||
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
|
||||
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
|
||||
*
|
||||
* <p> Client certificate authentication is only active when at least one client certificate is provided. If you want to reject
|
||||
* all client certificates sent by the server, you need to provide a client certificate with an {@code origin} that does
|
||||
* not match any of the domains you plan to visit.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
|
||||
* replacing {@code localhost} with {@code local.playwright}.
|
||||
*/
|
||||
@@ -336,10 +323,6 @@ public interface Browser extends AutoCloseable {
|
||||
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
|
||||
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
|
||||
*
|
||||
* <p> Client certificate authentication is only active when at least one client certificate is provided. If you want to reject
|
||||
* all client certificates sent by the server, you need to provide a client certificate with an {@code origin} that does
|
||||
* not match any of the domains you plan to visit.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
|
||||
* replacing {@code localhost} with {@code local.playwright}.
|
||||
*/
|
||||
@@ -691,10 +674,6 @@ public interface Browser extends AutoCloseable {
|
||||
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
|
||||
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
|
||||
*
|
||||
* <p> Client certificate authentication is only active when at least one client certificate is provided. If you want to reject
|
||||
* all client certificates sent by the server, you need to provide a client certificate with an {@code origin} that does
|
||||
* not match any of the domains you plan to visit.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
|
||||
* replacing {@code localhost} with {@code local.playwright}.
|
||||
*/
|
||||
@@ -912,10 +891,6 @@ public interface Browser extends AutoCloseable {
|
||||
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
|
||||
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
|
||||
*
|
||||
* <p> Client certificate authentication is only active when at least one client certificate is provided. If you want to reject
|
||||
* all client certificates sent by the server, you need to provide a client certificate with an {@code origin} that does
|
||||
* not match any of the domains you plan to visit.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
|
||||
* replacing {@code localhost} with {@code local.playwright}.
|
||||
*/
|
||||
@@ -1231,44 +1206,6 @@ public interface Browser extends AutoCloseable {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class BindOptions {
|
||||
/**
|
||||
* Host to bind the web socket server to. When specified, a web socket server is created instead of a named pipe.
|
||||
*/
|
||||
public String host;
|
||||
/**
|
||||
* Port to bind the web socket server to. When specified, a web socket server is created instead of a named pipe. Use
|
||||
* {@code 0} to let the OS pick an available port.
|
||||
*/
|
||||
public Integer port;
|
||||
/**
|
||||
* Working directory associated with this browser server.
|
||||
*/
|
||||
public String workspaceDir;
|
||||
|
||||
/**
|
||||
* Host to bind the web socket server to. When specified, a web socket server is created instead of a named pipe.
|
||||
*/
|
||||
public BindOptions setHost(String host) {
|
||||
this.host = host;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Port to bind the web socket server to. When specified, a web socket server is created instead of a named pipe. Use
|
||||
* {@code 0} to let the OS pick an available port.
|
||||
*/
|
||||
public BindOptions setPort(int port) {
|
||||
this.port = port;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Working directory associated with this browser server.
|
||||
*/
|
||||
public BindOptions setWorkspaceDir(String workspaceDir) {
|
||||
this.workspaceDir = workspaceDir;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class StartTracingOptions {
|
||||
/**
|
||||
* specify custom categories to use instead of default.
|
||||
@@ -1451,22 +1388,6 @@ public interface Browser extends AutoCloseable {
|
||||
* @since v1.8
|
||||
*/
|
||||
Page newPage(NewPageOptions options);
|
||||
/**
|
||||
* Binds the browser to a named pipe or web socket, making it available for other clients to connect to.
|
||||
*
|
||||
* @param title Title of the browser server, used for identification.
|
||||
* @since v1.59
|
||||
*/
|
||||
default BindResult bind(String title) {
|
||||
return bind(title, null);
|
||||
}
|
||||
/**
|
||||
* Binds the browser to a named pipe or web socket, making it available for other clients to connect to.
|
||||
*
|
||||
* @param title Title of the browser server, used for identification.
|
||||
* @since v1.59
|
||||
*/
|
||||
BindResult bind(String title, BindOptions options);
|
||||
/**
|
||||
* <strong>NOTE:</strong> This API controls <a href="https://www.chromium.org/developers/how-tos/trace-event-profiling-tool">Chromium Tracing</a>
|
||||
* which is a low-level chromium-specific debugging tool. API to control <a
|
||||
@@ -1547,12 +1468,6 @@ public interface Browser extends AutoCloseable {
|
||||
* @since v1.11
|
||||
*/
|
||||
byte[] stopTracing();
|
||||
/**
|
||||
* Unbinds the browser server previously bound with {@link com.microsoft.playwright.Browser#bind Browser.bind()}.
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
void unbind();
|
||||
/**
|
||||
* Returns the browser version.
|
||||
*
|
||||
|
||||
@@ -46,7 +46,14 @@ import java.util.regex.Pattern;
|
||||
public interface BrowserContext extends AutoCloseable {
|
||||
|
||||
/**
|
||||
* @deprecated Background pages have been removed from Chromium together with Manifest V2 extensions.
|
||||
* <strong>NOTE:</strong> Only works with Chromium browser's persistent context.
|
||||
*
|
||||
* <p> Emitted when new background page is created in the context.
|
||||
* <pre>{@code
|
||||
* context.onBackgroundPage(backgroundPage -> {
|
||||
* System.out.println(backgroundPage.url());
|
||||
* });
|
||||
* }</pre>
|
||||
*/
|
||||
void onBackgroundPage(Consumer<Page> handler);
|
||||
/**
|
||||
@@ -114,48 +121,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 +148,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 +278,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".
|
||||
@@ -453,6 +411,8 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* 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
|
||||
* Authentication, enable this.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> IndexedDBs with typed arrays are currently not supported.
|
||||
*/
|
||||
public Boolean indexedDB;
|
||||
/**
|
||||
@@ -465,6 +425,8 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* 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
|
||||
* Authentication, enable this.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> IndexedDBs with typed arrays are currently not supported.
|
||||
*/
|
||||
public StorageStateOptions setIndexedDB(boolean indexedDB) {
|
||||
this.indexedDB = indexedDB;
|
||||
@@ -563,12 +525,6 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* @since v1.45
|
||||
*/
|
||||
Clock clock();
|
||||
/**
|
||||
* Debugger allows to pause and resume the execution.
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
Debugger debugger();
|
||||
/**
|
||||
* Adds cookies into this browser context. All pages within this context will have these cookies installed. Cookies can be
|
||||
* obtained via {@link com.microsoft.playwright.BrowserContext#cookies BrowserContext.cookies()}.
|
||||
@@ -607,7 +563,7 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* @param script Script to be evaluated in all pages in the browser context.
|
||||
* @since v1.8
|
||||
*/
|
||||
AutoCloseable addInitScript(String script);
|
||||
void addInitScript(String script);
|
||||
/**
|
||||
* Adds a script which would be evaluated in one of the following scenarios:
|
||||
* <ul>
|
||||
@@ -634,16 +590,17 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* @param script Script to be evaluated in all pages in the browser context.
|
||||
* @since v1.8
|
||||
*/
|
||||
AutoCloseable addInitScript(Path script);
|
||||
void addInitScript(Path script);
|
||||
/**
|
||||
* @deprecated Background pages have been removed from Chromium together with Manifest V2 extensions.
|
||||
* <strong>NOTE:</strong> Background pages are only supported on Chromium-based browsers.
|
||||
*
|
||||
* <p> All existing background pages in the context.
|
||||
*
|
||||
* @since v1.11
|
||||
*/
|
||||
List<Page> backgroundPages();
|
||||
/**
|
||||
* Gets the browser instance that owns the context. Returns {@code null} if the context is created outside of normal
|
||||
* browser, e.g. Android or Electron.
|
||||
* Returns the browser instance of the context. If it was launched as a persistent context null gets returned.
|
||||
*
|
||||
* @since v1.8
|
||||
*/
|
||||
@@ -785,7 +742,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 void exposeBinding(String name, BindingCallback callback) {
|
||||
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
|
||||
*/
|
||||
void 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
|
||||
@@ -844,7 +848,7 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* @param callback Callback function that will be called in the Playwright's context.
|
||||
* @since v1.8
|
||||
*/
|
||||
AutoCloseable exposeFunction(String name, FunctionCallback callback);
|
||||
void exposeFunction(String name, FunctionCallback callback);
|
||||
/**
|
||||
* Grants specified permissions to the browser context. Only grants corresponding permissions to the given origin if
|
||||
* specified.
|
||||
@@ -864,8 +868,6 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* <li> {@code "clipboard-write"}</li>
|
||||
* <li> {@code "geolocation"}</li>
|
||||
* <li> {@code "gyroscope"}</li>
|
||||
* <li> {@code "local-fonts"}</li>
|
||||
* <li> {@code "local-network-access"}</li>
|
||||
* <li> {@code "magnetometer"}</li>
|
||||
* <li> {@code "microphone"}</li>
|
||||
* <li> {@code "midi-sysex"} (system-exclusive midi)</li>
|
||||
@@ -873,7 +875,6 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* <li> {@code "notifications"}</li>
|
||||
* <li> {@code "payment-handler"}</li>
|
||||
* <li> {@code "storage-access"}</li>
|
||||
* <li> {@code "screen-wake-lock"}</li>
|
||||
* </ul>
|
||||
* @since v1.8
|
||||
*/
|
||||
@@ -899,8 +900,6 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* <li> {@code "clipboard-write"}</li>
|
||||
* <li> {@code "geolocation"}</li>
|
||||
* <li> {@code "gyroscope"}</li>
|
||||
* <li> {@code "local-fonts"}</li>
|
||||
* <li> {@code "local-network-access"}</li>
|
||||
* <li> {@code "magnetometer"}</li>
|
||||
* <li> {@code "microphone"}</li>
|
||||
* <li> {@code "midi-sysex"} (system-exclusive midi)</li>
|
||||
@@ -908,17 +907,10 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* <li> {@code "notifications"}</li>
|
||||
* <li> {@code "payment-handler"}</li>
|
||||
* <li> {@code "storage-access"}</li>
|
||||
* <li> {@code "screen-wake-lock"}</li>
|
||||
* </ul>
|
||||
* @since v1.8
|
||||
*/
|
||||
void grantPermissions(List<String> permissions, GrantPermissionsOptions options);
|
||||
/**
|
||||
* Indicates that the browser context is in the process of closing or has already been closed.
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
boolean isClosed();
|
||||
/**
|
||||
* <strong>NOTE:</strong> CDP sessions are only supported on Chromium-based browsers.
|
||||
*
|
||||
@@ -1004,14 +996,14 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
*/
|
||||
default AutoCloseable route(String url, Consumer<Route> handler) {
|
||||
return route(url, handler, null);
|
||||
default void route(String url, Consumer<Route> handler) {
|
||||
route(url, handler, null);
|
||||
}
|
||||
/**
|
||||
* Routing provides the capability to modify network requests that are made by any page in the browser context. Once route
|
||||
@@ -1060,13 +1052,13 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
*/
|
||||
AutoCloseable route(String url, Consumer<Route> handler, RouteOptions options);
|
||||
void route(String url, Consumer<Route> handler, RouteOptions options);
|
||||
/**
|
||||
* Routing provides the capability to modify network requests that are made by any page in the browser context. Once route
|
||||
* is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or aborted.
|
||||
@@ -1114,14 +1106,14 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
*/
|
||||
default AutoCloseable route(Pattern url, Consumer<Route> handler) {
|
||||
return route(url, handler, null);
|
||||
default void route(Pattern url, Consumer<Route> handler) {
|
||||
route(url, handler, null);
|
||||
}
|
||||
/**
|
||||
* Routing provides the capability to modify network requests that are made by any page in the browser context. Once route
|
||||
@@ -1170,13 +1162,13 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
*/
|
||||
AutoCloseable route(Pattern url, Consumer<Route> handler, RouteOptions options);
|
||||
void route(Pattern url, Consumer<Route> handler, RouteOptions options);
|
||||
/**
|
||||
* Routing provides the capability to modify network requests that are made by any page in the browser context. Once route
|
||||
* is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or aborted.
|
||||
@@ -1224,14 +1216,14 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
*/
|
||||
default AutoCloseable route(Predicate<String> url, Consumer<Route> handler) {
|
||||
return route(url, handler, null);
|
||||
default void route(Predicate<String> url, Consumer<Route> handler) {
|
||||
route(url, handler, null);
|
||||
}
|
||||
/**
|
||||
* Routing provides the capability to modify network requests that are made by any page in the browser context. Once route
|
||||
@@ -1280,13 +1272,13 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
*/
|
||||
AutoCloseable route(Predicate<String> url, Consumer<Route> handler, RouteOptions options);
|
||||
void route(Predicate<String> url, Consumer<Route> handler, RouteOptions options);
|
||||
/**
|
||||
* If specified the network requests that are made in the context will be served from the HAR file. Read more about <a
|
||||
* href="https://playwright.dev/java/docs/mock#replaying-from-har">Replaying from HAR</a>.
|
||||
@@ -1475,21 +1467,6 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* @since v1.8
|
||||
*/
|
||||
String storageState(StorageStateOptions options);
|
||||
/**
|
||||
* Clears the existing cookies, local storage and IndexedDB entries for all origins and sets the new storage state.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* // Load storage state from a file and apply it to the context.
|
||||
* context.setStorageState(Paths.get("state.json"));
|
||||
* }</pre>
|
||||
*
|
||||
* @param storageState Populates context with given storage state. This option can be used to initialize context with logged-in information
|
||||
* obtained via {@link com.microsoft.playwright.BrowserContext#storageState BrowserContext.storageState()}. Path to the
|
||||
* file with saved storage state.
|
||||
* @since v1.59
|
||||
*/
|
||||
void setStorageState(Path storageState);
|
||||
/**
|
||||
*
|
||||
*
|
||||
@@ -1507,7 +1484,7 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* Removes a route created with {@link com.microsoft.playwright.BrowserContext#route BrowserContext.route()}. When {@code
|
||||
* handler} is not specified, removes all routes for the {@code url}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] used to register a routing with {@link
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] used to register a routing with {@link
|
||||
* com.microsoft.playwright.BrowserContext#route BrowserContext.route()}.
|
||||
* @since v1.8
|
||||
*/
|
||||
@@ -1518,7 +1495,7 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* Removes a route created with {@link com.microsoft.playwright.BrowserContext#route BrowserContext.route()}. When {@code
|
||||
* handler} is not specified, removes all routes for the {@code url}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] used to register a routing with {@link
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] used to register a routing with {@link
|
||||
* com.microsoft.playwright.BrowserContext#route BrowserContext.route()}.
|
||||
* @param handler Optional handler function used to register a routing with {@link com.microsoft.playwright.BrowserContext#route
|
||||
* BrowserContext.route()}.
|
||||
@@ -1529,7 +1506,7 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* Removes a route created with {@link com.microsoft.playwright.BrowserContext#route BrowserContext.route()}. When {@code
|
||||
* handler} is not specified, removes all routes for the {@code url}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] used to register a routing with {@link
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] used to register a routing with {@link
|
||||
* com.microsoft.playwright.BrowserContext#route BrowserContext.route()}.
|
||||
* @since v1.8
|
||||
*/
|
||||
@@ -1540,7 +1517,7 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* Removes a route created with {@link com.microsoft.playwright.BrowserContext#route BrowserContext.route()}. When {@code
|
||||
* handler} is not specified, removes all routes for the {@code url}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] used to register a routing with {@link
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] used to register a routing with {@link
|
||||
* com.microsoft.playwright.BrowserContext#route BrowserContext.route()}.
|
||||
* @param handler Optional handler function used to register a routing with {@link com.microsoft.playwright.BrowserContext#route
|
||||
* BrowserContext.route()}.
|
||||
@@ -1551,7 +1528,7 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* Removes a route created with {@link com.microsoft.playwright.BrowserContext#route BrowserContext.route()}. When {@code
|
||||
* handler} is not specified, removes all routes for the {@code url}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] used to register a routing with {@link
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] used to register a routing with {@link
|
||||
* com.microsoft.playwright.BrowserContext#route BrowserContext.route()}.
|
||||
* @since v1.8
|
||||
*/
|
||||
@@ -1562,7 +1539,7 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* Removes a route created with {@link com.microsoft.playwright.BrowserContext#route BrowserContext.route()}. When {@code
|
||||
* handler} is not specified, removes all routes for the {@code url}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] used to register a routing with {@link
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] used to register a routing with {@link
|
||||
* com.microsoft.playwright.BrowserContext#route BrowserContext.route()}.
|
||||
* @param handler Optional handler function used to register a routing with {@link com.microsoft.playwright.BrowserContext#route
|
||||
* BrowserContext.route()}.
|
||||
|
||||
@@ -128,20 +128,6 @@ public interface BrowserType {
|
||||
* Additional HTTP headers to be sent with connect request. Optional.
|
||||
*/
|
||||
public Map<String, String> headers;
|
||||
/**
|
||||
* Tells Playwright that it runs on the same host as the CDP server. It will enable certain optimizations that rely upon
|
||||
* 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.
|
||||
@@ -160,26 +146,6 @@ public interface BrowserType {
|
||||
this.headers = headers;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Tells Playwright that it runs on the same host as the CDP server. It will enable certain optimizations that rely upon
|
||||
* the file system being the same between Playwright and the Browser.
|
||||
*/
|
||||
public ConnectOverCDPOptions setIsLocal(boolean isLocal) {
|
||||
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.
|
||||
@@ -205,12 +171,6 @@ public interface BrowserType {
|
||||
* href="https://peter.sh/experiments/chromium-command-line-switches/">here</a>.
|
||||
*/
|
||||
public List<String> args;
|
||||
/**
|
||||
* If specified, artifacts (traces, videos, downloads, HAR files, etc.) are saved into this directory. The directory is not
|
||||
* cleaned up when the browser closes. If not specified, a temporary directory is used and cleaned up when the browser
|
||||
* closes.
|
||||
*/
|
||||
public Path artifactsDir;
|
||||
/**
|
||||
* Browser distribution channel.
|
||||
*
|
||||
@@ -226,6 +186,10 @@ public interface BrowserType {
|
||||
* Enable Chromium sandboxing. Defaults to {@code false}.
|
||||
*/
|
||||
public Boolean chromiumSandbox;
|
||||
/**
|
||||
* @deprecated Use <a href="https://playwright.dev/java/docs/debug">debugging tools</a> instead.
|
||||
*/
|
||||
public Boolean devtools;
|
||||
/**
|
||||
* If specified, accepted downloads are downloaded into this directory. Otherwise, temporary directory is created and is
|
||||
* deleted when browser is closed. In either case, the downloads are deleted when the browser context they were created in
|
||||
@@ -245,9 +209,6 @@ public interface BrowserType {
|
||||
/**
|
||||
* Firefox user preferences. Learn more about the Firefox user preferences at <a
|
||||
* href="https://support.mozilla.org/en-US/kb/about-config-editor-firefox">{@code about:config}</a>.
|
||||
*
|
||||
* <p> You can also provide a path to a custom <a href="https://mozilla.github.io/policy-templates/">{@code policies.json}
|
||||
* file</a> via {@code PLAYWRIGHT_FIREFOX_POLICIES_JSON} environment variable.
|
||||
*/
|
||||
public Map<String, Object> firefoxUserPrefs;
|
||||
/**
|
||||
@@ -265,7 +226,8 @@ public interface BrowserType {
|
||||
/**
|
||||
* Whether to run browser in headless mode. More details for <a
|
||||
* href="https://developers.google.com/web/updates/2017/04/headless-chrome">Chromium</a> and <a
|
||||
* href="https://hacks.mozilla.org/2017/12/using-headless-mode-in-firefox/">Firefox</a>. Defaults to {@code true}.
|
||||
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode">Firefox</a>. Defaults to {@code true}
|
||||
* unless the {@code devtools} option is {@code true}.
|
||||
*/
|
||||
public Boolean headless;
|
||||
/**
|
||||
@@ -306,15 +268,6 @@ public interface BrowserType {
|
||||
this.args = args;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* If specified, artifacts (traces, videos, downloads, HAR files, etc.) are saved into this directory. The directory is not
|
||||
* cleaned up when the browser closes. If not specified, a temporary directory is used and cleaned up when the browser
|
||||
* closes.
|
||||
*/
|
||||
public LaunchOptions setArtifactsDir(Path artifactsDir) {
|
||||
this.artifactsDir = artifactsDir;
|
||||
return this;
|
||||
}
|
||||
@Deprecated
|
||||
/**
|
||||
* Browser distribution channel.
|
||||
@@ -351,6 +304,13 @@ public interface BrowserType {
|
||||
this.chromiumSandbox = chromiumSandbox;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* @deprecated Use <a href="https://playwright.dev/java/docs/debug">debugging tools</a> instead.
|
||||
*/
|
||||
public LaunchOptions setDevtools(boolean devtools) {
|
||||
this.devtools = devtools;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* If specified, accepted downloads are downloaded into this directory. Otherwise, temporary directory is created and is
|
||||
* deleted when browser is closed. In either case, the downloads are deleted when the browser context they were created in
|
||||
@@ -379,9 +339,6 @@ public interface BrowserType {
|
||||
/**
|
||||
* Firefox user preferences. Learn more about the Firefox user preferences at <a
|
||||
* href="https://support.mozilla.org/en-US/kb/about-config-editor-firefox">{@code about:config}</a>.
|
||||
*
|
||||
* <p> You can also provide a path to a custom <a href="https://mozilla.github.io/policy-templates/">{@code policies.json}
|
||||
* file</a> via {@code PLAYWRIGHT_FIREFOX_POLICIES_JSON} environment variable.
|
||||
*/
|
||||
public LaunchOptions setFirefoxUserPrefs(Map<String, Object> firefoxUserPrefs) {
|
||||
this.firefoxUserPrefs = firefoxUserPrefs;
|
||||
@@ -411,7 +368,8 @@ public interface BrowserType {
|
||||
/**
|
||||
* Whether to run browser in headless mode. More details for <a
|
||||
* href="https://developers.google.com/web/updates/2017/04/headless-chrome">Chromium</a> and <a
|
||||
* href="https://hacks.mozilla.org/2017/12/using-headless-mode-in-firefox/">Firefox</a>. Defaults to {@code true}.
|
||||
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode">Firefox</a>. Defaults to {@code true}
|
||||
* unless the {@code devtools} option is {@code true}.
|
||||
*/
|
||||
public LaunchOptions setHeadless(boolean headless) {
|
||||
this.headless = headless;
|
||||
@@ -481,12 +439,6 @@ public interface BrowserType {
|
||||
* href="https://peter.sh/experiments/chromium-command-line-switches/">here</a>.
|
||||
*/
|
||||
public List<String> args;
|
||||
/**
|
||||
* If specified, artifacts (traces, videos, downloads, HAR files, etc.) are saved into this directory. The directory is not
|
||||
* cleaned up when the browser closes. If not specified, a temporary directory is used and cleaned up when the browser
|
||||
* closes.
|
||||
*/
|
||||
public Path artifactsDir;
|
||||
/**
|
||||
* When using {@link com.microsoft.playwright.Page#navigate Page.navigate()}, {@link com.microsoft.playwright.Page#route
|
||||
* Page.route()}, {@link com.microsoft.playwright.Page#waitForURL Page.waitForURL()}, {@link
|
||||
@@ -533,10 +485,6 @@ public interface BrowserType {
|
||||
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
|
||||
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
|
||||
*
|
||||
* <p> Client certificate authentication is only active when at least one client certificate is provided. If you want to reject
|
||||
* all client certificates sent by the server, you need to provide a client certificate with an {@code origin} that does
|
||||
* not match any of the domains you plan to visit.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
|
||||
* replacing {@code localhost} with {@code local.playwright}.
|
||||
*/
|
||||
@@ -560,6 +508,10 @@ public interface BrowserType {
|
||||
* href="https://playwright.dev/java/docs/emulation#devices">emulating devices with device scale factor</a>.
|
||||
*/
|
||||
public Double deviceScaleFactor;
|
||||
/**
|
||||
* @deprecated Use <a href="https://playwright.dev/java/docs/debug">debugging tools</a> instead.
|
||||
*/
|
||||
public Boolean devtools;
|
||||
/**
|
||||
* If specified, accepted downloads are downloaded into this directory. Otherwise, temporary directory is created and is
|
||||
* deleted when browser is closed. In either case, the downloads are deleted when the browser context they were created in
|
||||
@@ -583,9 +535,6 @@ public interface BrowserType {
|
||||
/**
|
||||
* Firefox user preferences. Learn more about the Firefox user preferences at <a
|
||||
* href="https://support.mozilla.org/en-US/kb/about-config-editor-firefox">{@code about:config}</a>.
|
||||
*
|
||||
* <p> You can also provide a path to a custom <a href="https://mozilla.github.io/policy-templates/">{@code policies.json}
|
||||
* file</a> via {@code PLAYWRIGHT_FIREFOX_POLICIES_JSON} environment variable.
|
||||
*/
|
||||
public Map<String, Object> firefoxUserPrefs;
|
||||
/**
|
||||
@@ -615,7 +564,8 @@ public interface BrowserType {
|
||||
/**
|
||||
* Whether to run browser in headless mode. More details for <a
|
||||
* href="https://developers.google.com/web/updates/2017/04/headless-chrome">Chromium</a> and <a
|
||||
* href="https://hacks.mozilla.org/2017/12/using-headless-mode-in-firefox/">Firefox</a>. Defaults to {@code true}.
|
||||
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode">Firefox</a>. Defaults to {@code true}
|
||||
* unless the {@code devtools} option is {@code true}.
|
||||
*/
|
||||
public Boolean headless;
|
||||
/**
|
||||
@@ -781,15 +731,6 @@ public interface BrowserType {
|
||||
this.args = args;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* If specified, artifacts (traces, videos, downloads, HAR files, etc.) are saved into this directory. The directory is not
|
||||
* cleaned up when the browser closes. If not specified, a temporary directory is used and cleaned up when the browser
|
||||
* closes.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setArtifactsDir(Path artifactsDir) {
|
||||
this.artifactsDir = artifactsDir;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* When using {@link com.microsoft.playwright.Page#navigate Page.navigate()}, {@link com.microsoft.playwright.Page#route
|
||||
* Page.route()}, {@link com.microsoft.playwright.Page#waitForURL Page.waitForURL()}, {@link
|
||||
@@ -863,10 +804,6 @@ public interface BrowserType {
|
||||
* {@code pfx}). Optionally, {@code passphrase} property should be provided if the certificate is encrypted. The {@code
|
||||
* origin} property should be provided with an exact match to the request origin that the certificate is valid for.
|
||||
*
|
||||
* <p> Client certificate authentication is only active when at least one client certificate is provided. If you want to reject
|
||||
* all client certificates sent by the server, you need to provide a client certificate with an {@code origin} that does
|
||||
* not match any of the domains you plan to visit.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> When using WebKit on macOS, accessing {@code localhost} will not pick up client certificates. You can make it work by
|
||||
* replacing {@code localhost} with {@code local.playwright}.
|
||||
*/
|
||||
@@ -902,6 +839,13 @@ public interface BrowserType {
|
||||
this.deviceScaleFactor = deviceScaleFactor;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* @deprecated Use <a href="https://playwright.dev/java/docs/debug">debugging tools</a> instead.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setDevtools(boolean devtools) {
|
||||
this.devtools = devtools;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* If specified, accepted downloads are downloaded into this directory. Otherwise, temporary directory is created and is
|
||||
* deleted when browser is closed. In either case, the downloads are deleted when the browser context they were created in
|
||||
@@ -937,9 +881,6 @@ public interface BrowserType {
|
||||
/**
|
||||
* Firefox user preferences. Learn more about the Firefox user preferences at <a
|
||||
* href="https://support.mozilla.org/en-US/kb/about-config-editor-firefox">{@code about:config}</a>.
|
||||
*
|
||||
* <p> You can also provide a path to a custom <a href="https://mozilla.github.io/policy-templates/">{@code policies.json}
|
||||
* file</a> via {@code PLAYWRIGHT_FIREFOX_POLICIES_JSON} environment variable.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setFirefoxUserPrefs(Map<String, Object> firefoxUserPrefs) {
|
||||
this.firefoxUserPrefs = firefoxUserPrefs;
|
||||
@@ -993,7 +934,8 @@ public interface BrowserType {
|
||||
/**
|
||||
* Whether to run browser in headless mode. More details for <a
|
||||
* href="https://developers.google.com/web/updates/2017/04/headless-chrome">Chromium</a> and <a
|
||||
* href="https://hacks.mozilla.org/2017/12/using-headless-mode-in-firefox/">Firefox</a>. Defaults to {@code true}.
|
||||
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode">Firefox</a>. Defaults to {@code true}
|
||||
* unless the {@code devtools} option is {@code true}.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setHeadless(boolean headless) {
|
||||
this.headless = headless;
|
||||
@@ -1275,11 +1217,11 @@ public interface BrowserType {
|
||||
* <p> <strong>NOTE:</strong> The major and minor version of the Playwright instance that connects needs to match the version of Playwright that
|
||||
* launches the browser (1.2.3 → is compatible with 1.2.x).
|
||||
*
|
||||
* @param endpoint A Playwright browser websocket endpoint to connect to. You obtain this endpoint via {@code BrowserServer.wsEndpoint}.
|
||||
* @param wsEndpoint A Playwright browser websocket endpoint to connect to. You obtain this endpoint via {@code BrowserServer.wsEndpoint}.
|
||||
* @since v1.8
|
||||
*/
|
||||
default Browser connect(String endpoint) {
|
||||
return connect(endpoint, null);
|
||||
default Browser connect(String wsEndpoint) {
|
||||
return connect(wsEndpoint, null);
|
||||
}
|
||||
/**
|
||||
* This method attaches Playwright to an existing browser instance created via {@code BrowserType.launchServer} in Node.js.
|
||||
@@ -1287,10 +1229,10 @@ public interface BrowserType {
|
||||
* <p> <strong>NOTE:</strong> The major and minor version of the Playwright instance that connects needs to match the version of Playwright that
|
||||
* launches the browser (1.2.3 → is compatible with 1.2.x).
|
||||
*
|
||||
* @param endpoint A Playwright browser websocket endpoint to connect to. You obtain this endpoint via {@code BrowserServer.wsEndpoint}.
|
||||
* @param wsEndpoint A Playwright browser websocket endpoint to connect to. You obtain this endpoint via {@code BrowserServer.wsEndpoint}.
|
||||
* @since v1.8
|
||||
*/
|
||||
Browser connect(String endpoint, ConnectOptions options);
|
||||
Browser connect(String wsEndpoint, ConnectOptions options);
|
||||
/**
|
||||
* This method attaches Playwright to an existing browser instance using the Chrome DevTools Protocol.
|
||||
*
|
||||
@@ -1423,20 +1365,11 @@ public interface BrowserType {
|
||||
* <p> Launches browser that uses persistent storage located at {@code userDataDir} and returns the only context. Closing this
|
||||
* context will automatically close the browser.
|
||||
*
|
||||
* @param userDataDir Path to a User Data Directory, which stores browser session data like cookies and local storage. Pass an empty string to
|
||||
* create a temporary directory.
|
||||
*
|
||||
* <p> More details for <a
|
||||
* @param userDataDir Path to a User Data Directory, which stores browser session data like cookies and local storage. More details for <a
|
||||
* href="https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md#introduction">Chromium</a> and <a
|
||||
* href="https://wiki.mozilla.org/Firefox/CommandLineOptions#User_profile">Firefox</a>. Chromium's user data directory is
|
||||
* the **parent** directory of the "Profile Path" seen at {@code chrome://version}.
|
||||
*
|
||||
* <p> Note that browsers do not allow launching multiple instances with the same User Data Directory.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Chromium/Chrome: Due to recent Chrome policy changes, automating the default Chrome user profile is not supported.
|
||||
* Pointing {@code userDataDir} to Chrome's main "User Data" directory (the profile used for your regular browsing) may
|
||||
* result in pages not loading or the browser exiting. Create and use a separate directory (for example, an empty folder)
|
||||
* as your automation profile instead. See https://developer.chrome.com/blog/remote-debugging-port for details.
|
||||
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile">Firefox</a>. Note that
|
||||
* Chromium's user data directory is the **parent** directory of the "Profile Path" seen at {@code chrome://version}. Pass
|
||||
* an empty string to use a temporary directory instead.
|
||||
* @since v1.8
|
||||
*/
|
||||
default BrowserContext launchPersistentContext(Path userDataDir) {
|
||||
@@ -1448,20 +1381,11 @@ public interface BrowserType {
|
||||
* <p> Launches browser that uses persistent storage located at {@code userDataDir} and returns the only context. Closing this
|
||||
* context will automatically close the browser.
|
||||
*
|
||||
* @param userDataDir Path to a User Data Directory, which stores browser session data like cookies and local storage. Pass an empty string to
|
||||
* create a temporary directory.
|
||||
*
|
||||
* <p> More details for <a
|
||||
* @param userDataDir Path to a User Data Directory, which stores browser session data like cookies and local storage. More details for <a
|
||||
* href="https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md#introduction">Chromium</a> and <a
|
||||
* href="https://wiki.mozilla.org/Firefox/CommandLineOptions#User_profile">Firefox</a>. Chromium's user data directory is
|
||||
* the **parent** directory of the "Profile Path" seen at {@code chrome://version}.
|
||||
*
|
||||
* <p> Note that browsers do not allow launching multiple instances with the same User Data Directory.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Chromium/Chrome: Due to recent Chrome policy changes, automating the default Chrome user profile is not supported.
|
||||
* Pointing {@code userDataDir} to Chrome's main "User Data" directory (the profile used for your regular browsing) may
|
||||
* result in pages not loading or the browser exiting. Create and use a separate directory (for example, an empty folder)
|
||||
* as your automation profile instead. See https://developer.chrome.com/blog/remote-debugging-port for details.
|
||||
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile">Firefox</a>. Note that
|
||||
* Chromium's user data directory is the **parent** directory of the "Profile Path" seen at {@code chrome://version}. Pass
|
||||
* an empty string to use a temporary directory instead.
|
||||
* @since v1.8
|
||||
*/
|
||||
BrowserContext launchPersistentContext(Path userDataDir, LaunchPersistentContextOptions options);
|
||||
|
||||
@@ -48,16 +48,6 @@ import com.google.gson.JsonObject;
|
||||
* }</pre>
|
||||
*/
|
||||
public interface CDPSession {
|
||||
|
||||
/**
|
||||
* Emitted when the session is closed, either because the target was closed or {@code session.detach()} was called.
|
||||
*/
|
||||
void onClose(Consumer<CDPSession> handler);
|
||||
/**
|
||||
* Removes handler that was previously added with {@link #onClose onClose(handler)}.
|
||||
*/
|
||||
void offClose(Consumer<CDPSession> handler);
|
||||
|
||||
/**
|
||||
* Detaches the CDPSession from the target. Once detached, the CDPSession object won't emit any events and can't be used to
|
||||
* send messages.
|
||||
|
||||
@@ -69,12 +69,6 @@ public interface ConsoleMessage {
|
||||
* @since v1.8
|
||||
*/
|
||||
String text();
|
||||
/**
|
||||
* The timestamp of the console message in milliseconds since the Unix epoch.
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
double timestamp();
|
||||
/**
|
||||
* One of the following values: {@code "log"}, {@code "debug"}, {@code "info"}, {@code "error"}, {@code "warning"}, {@code
|
||||
* "dir"}, {@code "dirxml"}, {@code "table"}, {@code "trace"}, {@code "clear"}, {@code "startGroup"}, {@code
|
||||
@@ -84,12 +78,5 @@ public interface ConsoleMessage {
|
||||
* @since v1.8
|
||||
*/
|
||||
String type();
|
||||
/**
|
||||
* The web worker or service worker that produced this console message, if any. Note that console messages from web workers
|
||||
* also have non-null {@link com.microsoft.playwright.ConsoleMessage#page ConsoleMessage.page()}.
|
||||
*
|
||||
* @since v1.57
|
||||
*/
|
||||
Worker worker();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,78 +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.*;
|
||||
|
||||
/**
|
||||
* API for controlling the Playwright debugger. The debugger allows pausing script execution and inspecting the page.
|
||||
* Obtain the debugger instance via {@link com.microsoft.playwright.BrowserContext#debugger BrowserContext.debugger()}.
|
||||
*/
|
||||
public interface Debugger {
|
||||
|
||||
/**
|
||||
* Emitted when the debugger pauses or resumes.
|
||||
*/
|
||||
void onPausedStateChanged(Runnable handler);
|
||||
/**
|
||||
* Removes handler that was previously added with {@link #onPausedStateChanged onPausedStateChanged(handler)}.
|
||||
*/
|
||||
void offPausedStateChanged(Runnable handler);
|
||||
|
||||
/**
|
||||
* Returns details about the currently paused call. Returns {@code null} if the debugger is not paused.
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
DebuggerPausedDetails pausedDetails();
|
||||
/**
|
||||
* Configures the debugger to pause before the next action is executed.
|
||||
*
|
||||
* <p> Throws if the debugger is already paused. Use {@link com.microsoft.playwright.Debugger#next Debugger.next()} or {@link
|
||||
* com.microsoft.playwright.Debugger#runTo Debugger.runTo()} to step while paused.
|
||||
*
|
||||
* <p> Note that {@link com.microsoft.playwright.Page#pause Page.pause()} is equivalent to a "debugger" statement — it pauses
|
||||
* execution at the call site immediately. On the contrary, {@link com.microsoft.playwright.Debugger#requestPause
|
||||
* Debugger.requestPause()} is equivalent to "pause on next statement" — it configures the debugger to pause before the
|
||||
* next action is executed.
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
void requestPause();
|
||||
/**
|
||||
* Resumes script execution. Throws if the debugger is not paused.
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
void resume();
|
||||
/**
|
||||
* Resumes script execution and pauses again before the next action. Throws if the debugger is not paused.
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
void next();
|
||||
/**
|
||||
* Resumes script execution and pauses when an action originates from the given source location. Throws if the debugger is
|
||||
* not paused.
|
||||
*
|
||||
* @param location The source location to pause at.
|
||||
* @since v1.59
|
||||
*/
|
||||
void runTo(Location location);
|
||||
}
|
||||
|
||||
@@ -48,9 +48,6 @@ public interface Download {
|
||||
/**
|
||||
* Returns a readable stream for a successful download, or throws for a failed/canceled download.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> If you don't need a readable stream, it's usually simpler to read the file from disk after the download completed. See
|
||||
* {@link com.microsoft.playwright.Download#path Download.path()}.
|
||||
*
|
||||
* @since v1.8
|
||||
*/
|
||||
InputStream createReadStream();
|
||||
|
||||
@@ -170,12 +170,6 @@ public interface ElementHandle extends JSHandle {
|
||||
* element.
|
||||
*/
|
||||
public Position position;
|
||||
/**
|
||||
* 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
|
||||
* location.
|
||||
*/
|
||||
public Integer steps;
|
||||
/**
|
||||
* 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
|
||||
@@ -250,15 +244,6 @@ public interface ElementHandle extends JSHandle {
|
||||
this.position = position;
|
||||
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
|
||||
* location.
|
||||
*/
|
||||
public ClickOptions setSteps(int steps) {
|
||||
this.steps = steps;
|
||||
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
|
||||
@@ -308,12 +293,6 @@ public interface ElementHandle extends JSHandle {
|
||||
* element.
|
||||
*/
|
||||
public Position position;
|
||||
/**
|
||||
* 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
|
||||
* location.
|
||||
*/
|
||||
public Integer steps;
|
||||
/**
|
||||
* 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
|
||||
@@ -381,15 +360,6 @@ public interface ElementHandle extends JSHandle {
|
||||
this.position = position;
|
||||
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
|
||||
* location.
|
||||
*/
|
||||
public DblclickOptions setSteps(int steps) {
|
||||
this.steps = steps;
|
||||
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
|
||||
|
||||
@@ -563,11 +563,6 @@ public interface Frame {
|
||||
* specified, some visible point of the element is used.
|
||||
*/
|
||||
public Position sourcePosition;
|
||||
/**
|
||||
* Defaults to 1. Sends {@code n} interpolated {@code mousemove} events to represent travel between the {@code mousedown}
|
||||
* and {@code mouseup} of the drag. When set to 1, emits a single {@code mousemove} event at the destination location.
|
||||
*/
|
||||
public Integer steps;
|
||||
/**
|
||||
* 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.
|
||||
@@ -622,14 +617,6 @@ public interface Frame {
|
||||
this.sourcePosition = sourcePosition;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Defaults to 1. Sends {@code n} interpolated {@code mousemove} events to represent travel between the {@code mousedown}
|
||||
* and {@code mouseup} of the drag. When set to 1, emits a single {@code mousemove} event at the destination location.
|
||||
*/
|
||||
public DragAndDropOptions setSteps(int steps) {
|
||||
this.steps = steps;
|
||||
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.
|
||||
@@ -867,13 +854,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}.
|
||||
*
|
||||
@@ -882,8 +862,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;
|
||||
/**
|
||||
@@ -935,26 +915,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}.
|
||||
*
|
||||
@@ -966,8 +926,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;
|
||||
@@ -2299,7 +2259,7 @@ public interface Frame {
|
||||
*/
|
||||
public Double timeout;
|
||||
/**
|
||||
* A glob pattern, regex pattern, or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
@@ -2329,7 +2289,7 @@ public interface Frame {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A glob pattern, regex pattern, or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
@@ -2338,7 +2298,7 @@ public interface Frame {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A glob pattern, regex pattern, or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
@@ -2347,7 +2307,7 @@ public interface Frame {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A glob pattern, regex pattern, or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
@@ -3407,7 +3367,7 @@ public interface Frame {
|
||||
*
|
||||
* <p> Consider the following DOM structure.
|
||||
*
|
||||
* <p> You can locate each element by its implicit role:
|
||||
* <p> You can locate each element by it's implicit role:
|
||||
* <pre>{@code
|
||||
* assertThat(page
|
||||
* .getByRole(AriaRole.HEADING,
|
||||
@@ -3450,7 +3410,7 @@ public interface Frame {
|
||||
*
|
||||
* <p> Consider the following DOM structure.
|
||||
*
|
||||
* <p> You can locate each element by its implicit role:
|
||||
* <p> You can locate each element by it's implicit role:
|
||||
* <pre>{@code
|
||||
* assertThat(page
|
||||
* .getByRole(AriaRole.HEADING,
|
||||
@@ -3489,7 +3449,7 @@ public interface Frame {
|
||||
*
|
||||
* <p> Consider the following DOM structure.
|
||||
*
|
||||
* <p> You can locate the element by its test id:
|
||||
* <p> You can locate the element by it's test id:
|
||||
* <pre>{@code
|
||||
* page.getByTestId("directions").click();
|
||||
* }</pre>
|
||||
@@ -3511,7 +3471,7 @@ public interface Frame {
|
||||
*
|
||||
* <p> Consider the following DOM structure.
|
||||
*
|
||||
* <p> You can locate the element by its test id:
|
||||
* <p> You can locate the element by it's test id:
|
||||
* <pre>{@code
|
||||
* page.getByTestId("directions").click();
|
||||
* }</pre>
|
||||
@@ -5166,7 +5126,7 @@ public interface Frame {
|
||||
* frame.waitForURL("**\/target.html");
|
||||
* }</pre>
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
* @since v1.11
|
||||
@@ -5183,7 +5143,7 @@ public interface Frame {
|
||||
* frame.waitForURL("**\/target.html");
|
||||
* }</pre>
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
* @since v1.11
|
||||
@@ -5198,7 +5158,7 @@ public interface Frame {
|
||||
* frame.waitForURL("**\/target.html");
|
||||
* }</pre>
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
* @since v1.11
|
||||
@@ -5215,7 +5175,7 @@ public interface Frame {
|
||||
* frame.waitForURL("**\/target.html");
|
||||
* }</pre>
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
* @since v1.11
|
||||
@@ -5230,7 +5190,7 @@ public interface Frame {
|
||||
* frame.waitForURL("**\/target.html");
|
||||
* }</pre>
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
* @since v1.11
|
||||
@@ -5247,7 +5207,7 @@ public interface Frame {
|
||||
* frame.waitForURL("**\/target.html");
|
||||
* }</pre>
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
* @since v1.11
|
||||
|
||||
@@ -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;
|
||||
@@ -629,7 +602,7 @@ public interface FrameLocator {
|
||||
*
|
||||
* <p> Consider the following DOM structure.
|
||||
*
|
||||
* <p> You can locate each element by its implicit role:
|
||||
* <p> You can locate each element by it's implicit role:
|
||||
* <pre>{@code
|
||||
* assertThat(page
|
||||
* .getByRole(AriaRole.HEADING,
|
||||
@@ -672,7 +645,7 @@ public interface FrameLocator {
|
||||
*
|
||||
* <p> Consider the following DOM structure.
|
||||
*
|
||||
* <p> You can locate each element by its implicit role:
|
||||
* <p> You can locate each element by it's implicit role:
|
||||
* <pre>{@code
|
||||
* assertThat(page
|
||||
* .getByRole(AriaRole.HEADING,
|
||||
@@ -711,7 +684,7 @@ public interface FrameLocator {
|
||||
*
|
||||
* <p> Consider the following DOM structure.
|
||||
*
|
||||
* <p> You can locate the element by its test id:
|
||||
* <p> You can locate the element by it's test id:
|
||||
* <pre>{@code
|
||||
* page.getByTestId("directions").click();
|
||||
* }</pre>
|
||||
@@ -733,7 +706,7 @@ public interface FrameLocator {
|
||||
*
|
||||
* <p> Consider the following DOM structure.
|
||||
*
|
||||
* <p> You can locate the element by its test id:
|
||||
* <p> You can locate the element by it's test id:
|
||||
* <pre>{@code
|
||||
* page.getByTestId("directions").click();
|
||||
* }</pre>
|
||||
|
||||
@@ -30,22 +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.
|
||||
*/
|
||||
public Integer depth;
|
||||
/**
|
||||
* When set to {@code "ai"}, returns a snapshot optimized for AI consumption. Defaults to {@code "default"}. See details
|
||||
* for more information.
|
||||
*/
|
||||
public AriaSnapshotMode mode;
|
||||
/**
|
||||
* 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
|
||||
@@ -54,31 +38,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.
|
||||
*/
|
||||
public AriaSnapshotOptions setDepth(int depth) {
|
||||
this.depth = depth;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* When set to {@code "ai"}, returns a snapshot optimized for AI consumption. Defaults to {@code "default"}. See details
|
||||
* for more information.
|
||||
*/
|
||||
public AriaSnapshotOptions setMode(AriaSnapshotMode mode) {
|
||||
this.mode = mode;
|
||||
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
|
||||
@@ -286,12 +245,6 @@ public interface Locator {
|
||||
* element.
|
||||
*/
|
||||
public Position position;
|
||||
/**
|
||||
* 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
|
||||
* location.
|
||||
*/
|
||||
public Integer steps;
|
||||
/**
|
||||
* 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
|
||||
@@ -367,15 +320,6 @@ public interface Locator {
|
||||
this.position = position;
|
||||
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
|
||||
* location.
|
||||
*/
|
||||
public ClickOptions setSteps(int steps) {
|
||||
this.steps = steps;
|
||||
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
|
||||
@@ -426,12 +370,6 @@ public interface Locator {
|
||||
* element.
|
||||
*/
|
||||
public Position position;
|
||||
/**
|
||||
* 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
|
||||
* location.
|
||||
*/
|
||||
public Integer steps;
|
||||
/**
|
||||
* 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
|
||||
@@ -500,15 +438,6 @@ public interface Locator {
|
||||
this.position = position;
|
||||
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
|
||||
* location.
|
||||
*/
|
||||
public DblclickOptions setSteps(int steps) {
|
||||
this.steps = steps;
|
||||
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
|
||||
@@ -565,11 +494,6 @@ public interface Locator {
|
||||
* specified, some visible point of the element is used.
|
||||
*/
|
||||
public Position sourcePosition;
|
||||
/**
|
||||
* Defaults to 1. Sends {@code n} interpolated {@code mousemove} events to represent travel between the {@code mousedown}
|
||||
* and {@code mouseup} of the drag. When set to 1, emits a single {@code mousemove} event at the destination location.
|
||||
*/
|
||||
public Integer steps;
|
||||
/**
|
||||
* Drops on the target 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.
|
||||
@@ -619,14 +543,6 @@ public interface Locator {
|
||||
this.sourcePosition = sourcePosition;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Defaults to 1. Sends {@code n} interpolated {@code mousemove} events to represent travel between the {@code mousedown}
|
||||
* and {@code mouseup} of the drag. When set to 1, emits a single {@code mousemove} event at the destination location.
|
||||
*/
|
||||
public DragToOptions setSteps(int steps) {
|
||||
this.steps = steps;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Drops on the target 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,46 +578,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
|
||||
@@ -724,14 +600,18 @@ public interface Locator {
|
||||
}
|
||||
class EvaluateOptions {
|
||||
/**
|
||||
* Maximum time in milliseconds to wait for the locator before evaluating. Note that after locator is resolved, evaluation
|
||||
* itself is not limited by the timeout. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout.
|
||||
* Maximum time in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
|
||||
* value can be changed by using the {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout
|
||||
* BrowserContext.setDefaultTimeout()} or {@link com.microsoft.playwright.Page#setDefaultTimeout Page.setDefaultTimeout()}
|
||||
* methods.
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Maximum time in milliseconds to wait for the locator before evaluating. Note that after locator is resolved, evaluation
|
||||
* itself is not limited by the timeout. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout.
|
||||
* Maximum time in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
|
||||
* value can be changed by using the {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout
|
||||
* BrowserContext.setDefaultTimeout()} or {@link com.microsoft.playwright.Page#setDefaultTimeout Page.setDefaultTimeout()}
|
||||
* methods.
|
||||
*/
|
||||
public EvaluateOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
@@ -740,14 +620,18 @@ public interface Locator {
|
||||
}
|
||||
class EvaluateHandleOptions {
|
||||
/**
|
||||
* Maximum time in milliseconds to wait for the locator before evaluating. Note that after locator is resolved, evaluation
|
||||
* itself is not limited by the timeout. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout.
|
||||
* Maximum time in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
|
||||
* value can be changed by using the {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout
|
||||
* BrowserContext.setDefaultTimeout()} or {@link com.microsoft.playwright.Page#setDefaultTimeout Page.setDefaultTimeout()}
|
||||
* methods.
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Maximum time in milliseconds to wait for the locator before evaluating. Note that after locator is resolved, evaluation
|
||||
* itself is not limited by the timeout. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout.
|
||||
* Maximum time in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
|
||||
* value can be changed by using the {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout
|
||||
* BrowserContext.setDefaultTimeout()} or {@link com.microsoft.playwright.Page#setDefaultTimeout Page.setDefaultTimeout()}
|
||||
* methods.
|
||||
*/
|
||||
public EvaluateHandleOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
@@ -1000,13 +884,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}.
|
||||
*
|
||||
@@ -1015,8 +892,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;
|
||||
/**
|
||||
@@ -1068,26 +945,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}.
|
||||
*
|
||||
@@ -1099,8 +956,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;
|
||||
@@ -1206,20 +1063,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
|
||||
@@ -2359,7 +2202,7 @@ public interface Locator {
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* List<String> texts = page.getByRole(AriaRole.LINK).allInnerTexts();
|
||||
* String[] texts = page.getByRole(AriaRole.LINK).allInnerTexts();
|
||||
* }</pre>
|
||||
*
|
||||
* @since v1.14
|
||||
@@ -2374,7 +2217,7 @@ public interface Locator {
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* List<String> texts = page.getByRole(AriaRole.LINK).allTextContents();
|
||||
* String[] texts = page.getByRole(AriaRole.LINK).allTextContents();
|
||||
* }</pre>
|
||||
*
|
||||
* @since v1.14
|
||||
@@ -2420,12 +2263,6 @@ public interface Locator {
|
||||
*
|
||||
* <p> Below is the HTML markup and the respective ARIA snapshot:
|
||||
*
|
||||
* <p> An AI-optimized snapshot, controlled by {@code mode}, is different from a default snapshot:
|
||||
* <ol>
|
||||
* <li> Includes element references {@code [ref=e2]}. 2. Does not wait for an element matching the locator, and throws when no
|
||||
* elements match. 3. Includes snapshots of {@code <iframe>}s inside the target.</li>
|
||||
* </ol>
|
||||
*
|
||||
* @since v1.49
|
||||
*/
|
||||
default String ariaSnapshot() {
|
||||
@@ -2457,12 +2294,6 @@ public interface Locator {
|
||||
*
|
||||
* <p> Below is the HTML markup and the respective ARIA snapshot:
|
||||
*
|
||||
* <p> An AI-optimized snapshot, controlled by {@code mode}, is different from a default snapshot:
|
||||
* <ol>
|
||||
* <li> Includes element references {@code [ref=e2]}. 2. Does not wait for an element matching the locator, and throws when no
|
||||
* elements match. 3. Includes snapshots of {@code <iframe>}s inside the target.</li>
|
||||
* </ol>
|
||||
*
|
||||
* @since v1.49
|
||||
*/
|
||||
String ariaSnapshot(AriaSnapshotOptions options);
|
||||
@@ -2779,36 +2610,6 @@ public interface Locator {
|
||||
* @since v1.14
|
||||
*/
|
||||
void dblclick(DblclickOptions options);
|
||||
/**
|
||||
* Describes the locator, description is used in the trace viewer and reports. Returns the locator pointing to the same
|
||||
* element.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* Locator button = page.getByTestId("btn-sub").describe("Subscribe button");
|
||||
* button.click();
|
||||
* }</pre>
|
||||
*
|
||||
* @param description Locator description.
|
||||
* @since v1.53
|
||||
*/
|
||||
Locator describe(String description);
|
||||
/**
|
||||
* Returns locator description previously set with {@link com.microsoft.playwright.Locator#describe Locator.describe()}.
|
||||
* Returns {@code null} if no custom description has been set.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* Locator button = page.getByRole(AriaRole.BUTTON).describe("Subscribe button");
|
||||
* System.out.println(button.description()); // "Subscribe button"
|
||||
*
|
||||
* Locator input = page.getByRole(AriaRole.TEXTBOX);
|
||||
* System.out.println(input.description()); // null
|
||||
* }</pre>
|
||||
*
|
||||
* @since v1.57
|
||||
*/
|
||||
String description();
|
||||
/**
|
||||
* Programmatically dispatch an event on the matching element.
|
||||
*
|
||||
@@ -2998,54 +2799,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.
|
||||
@@ -3103,14 +2856,6 @@ public interface Locator {
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
*
|
||||
* <p> Passing argument to {@code expression}:
|
||||
* <pre>{@code
|
||||
* Object result = page.getByTestId("myId").evaluate("(element, [x, y]) => {\n" +
|
||||
* " return element.textContent + ' ' + x * y;\n" +
|
||||
* "}", Arrays.asList(7, 8));
|
||||
* System.out.println(result); // prints "myId text 56"
|
||||
* }</pre>
|
||||
*
|
||||
* @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}.
|
||||
@@ -3135,14 +2880,6 @@ public interface Locator {
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
*
|
||||
* <p> Passing argument to {@code expression}:
|
||||
* <pre>{@code
|
||||
* Object result = page.getByTestId("myId").evaluate("(element, [x, y]) => {\n" +
|
||||
* " return element.textContent + ' ' + x * y;\n" +
|
||||
* "}", Arrays.asList(7, 8));
|
||||
* System.out.println(result); // prints "myId text 56"
|
||||
* }</pre>
|
||||
*
|
||||
* @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.14
|
||||
@@ -3166,14 +2903,6 @@ public interface Locator {
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
*
|
||||
* <p> Passing argument to {@code expression}:
|
||||
* <pre>{@code
|
||||
* Object result = page.getByTestId("myId").evaluate("(element, [x, y]) => {\n" +
|
||||
* " return element.textContent + ' ' + x * y;\n" +
|
||||
* "}", Arrays.asList(7, 8));
|
||||
* System.out.println(result); // prints "myId text 56"
|
||||
* }</pre>
|
||||
*
|
||||
* @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}.
|
||||
@@ -3674,7 +3403,7 @@ public interface Locator {
|
||||
*
|
||||
* <p> Consider the following DOM structure.
|
||||
*
|
||||
* <p> You can locate each element by its implicit role:
|
||||
* <p> You can locate each element by it's implicit role:
|
||||
* <pre>{@code
|
||||
* assertThat(page
|
||||
* .getByRole(AriaRole.HEADING,
|
||||
@@ -3717,7 +3446,7 @@ public interface Locator {
|
||||
*
|
||||
* <p> Consider the following DOM structure.
|
||||
*
|
||||
* <p> You can locate each element by its implicit role:
|
||||
* <p> You can locate each element by it's implicit role:
|
||||
* <pre>{@code
|
||||
* assertThat(page
|
||||
* .getByRole(AriaRole.HEADING,
|
||||
@@ -3756,7 +3485,7 @@ public interface Locator {
|
||||
*
|
||||
* <p> Consider the following DOM structure.
|
||||
*
|
||||
* <p> You can locate the element by its test id:
|
||||
* <p> You can locate the element by it's test id:
|
||||
* <pre>{@code
|
||||
* page.getByTestId("directions").click();
|
||||
* }</pre>
|
||||
@@ -3778,7 +3507,7 @@ public interface Locator {
|
||||
*
|
||||
* <p> Consider the following DOM structure.
|
||||
*
|
||||
* <p> You can locate the element by its test id:
|
||||
* <p> You can locate the element by it's test id:
|
||||
* <pre>{@code
|
||||
* page.getByTestId("directions").click();
|
||||
* }</pre>
|
||||
@@ -4025,28 +3754,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.
|
||||
*
|
||||
@@ -4442,14 +4156,6 @@ public interface Locator {
|
||||
* @since v1.14
|
||||
*/
|
||||
Locator locator(Locator selectorOrLocator, LocatorOptions options);
|
||||
/**
|
||||
* Returns a new locator that uses best practices for referencing the matched element, prioritizing test ids, aria roles,
|
||||
* and other user-facing attributes over CSS selectors. This is useful for converting implementation-detail selectors into
|
||||
* more resilient, human-readable locators.
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
Locator normalize();
|
||||
/**
|
||||
* Returns locator to the n-th matching element. It's zero based, {@code nth(0)} selects the first element.
|
||||
*
|
||||
|
||||
@@ -21,11 +21,6 @@ import com.microsoft.playwright.options.*;
|
||||
/**
|
||||
* The Mouse class operates in main-frame CSS pixels relative to the top-left corner of the viewport.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> If you want to debug where the mouse moved, you can use the <a
|
||||
* href="https://playwright.dev/java/docs/trace-viewer-intro">Trace viewer</a> or <a
|
||||
* href="https://playwright.dev/java/docs/running-tests">Playwright Inspector</a>. A red dot showing the location of the
|
||||
* mouse will be shown for every mouse action.
|
||||
*
|
||||
* <p> Every {@code page} object has its own Mouse, accessible with {@link com.microsoft.playwright.Page#mouse Page.mouse()}.
|
||||
* <pre>{@code
|
||||
* // Using ‘page.mouse’ to trace a 100x100 square.
|
||||
@@ -127,16 +122,12 @@ public interface Mouse {
|
||||
}
|
||||
class MoveOptions {
|
||||
/**
|
||||
* 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
|
||||
* location.
|
||||
* Defaults to 1. Sends intermediate {@code mousemove} events.
|
||||
*/
|
||||
public Integer steps;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* location.
|
||||
* Defaults to 1. Sends intermediate {@code mousemove} events.
|
||||
*/
|
||||
public MoveOptions setSteps(int steps) {
|
||||
this.steps = steps;
|
||||
|
||||
@@ -860,11 +860,6 @@ public interface Page extends AutoCloseable {
|
||||
* specified, some visible point of the element is used.
|
||||
*/
|
||||
public Position sourcePosition;
|
||||
/**
|
||||
* Defaults to 1. Sends {@code n} interpolated {@code mousemove} events to represent travel between the {@code mousedown}
|
||||
* and {@code mouseup} of the drag. When set to 1, emits a single {@code mousemove} event at the destination location.
|
||||
*/
|
||||
public Integer steps;
|
||||
/**
|
||||
* 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.
|
||||
@@ -919,14 +914,6 @@ public interface Page extends AutoCloseable {
|
||||
this.sourcePosition = sourcePosition;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Defaults to 1. Sends {@code n} interpolated {@code mousemove} events to represent travel between the {@code mousedown}
|
||||
* and {@code mouseup} of the drag. When set to 1, emits a single {@code mousemove} event at the destination location.
|
||||
*/
|
||||
public DragAndDropOptions setSteps(int steps) {
|
||||
this.steps = steps;
|
||||
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.
|
||||
@@ -1058,6 +1045,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
|
||||
@@ -1236,13 +1237,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}.
|
||||
*
|
||||
@@ -1251,8 +1245,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;
|
||||
/**
|
||||
@@ -1304,26 +1298,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}.
|
||||
*
|
||||
@@ -1335,8 +1309,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;
|
||||
@@ -2000,20 +1974,6 @@ public interface Page extends AutoCloseable {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class ConsoleMessagesOptions {
|
||||
/**
|
||||
* Controls which messages are returned:
|
||||
*/
|
||||
public ConsoleMessagesFilter filter;
|
||||
|
||||
/**
|
||||
* Controls which messages are returned:
|
||||
*/
|
||||
public ConsoleMessagesOptions setFilter(ConsoleMessagesFilter filter) {
|
||||
this.filter = filter;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class LocatorOptions {
|
||||
/**
|
||||
* Narrows down the results of the method to those which contain elements matching this relative locator. For example,
|
||||
@@ -2993,67 +2953,6 @@ public interface Page extends AutoCloseable {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
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.
|
||||
*/
|
||||
public Integer depth;
|
||||
/**
|
||||
* When set to {@code "ai"}, returns a snapshot optimized for AI consumption: including element references like {@code
|
||||
* [ref=e2]} and snapshots of {@code <iframe>}s. Defaults to {@code "default"}.
|
||||
*/
|
||||
public AriaSnapshotMode mode;
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public AriaSnapshotOptions setDepth(int depth) {
|
||||
this.depth = depth;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* When set to {@code "ai"}, returns a snapshot optimized for AI consumption: including element references like {@code
|
||||
* [ref=e2]} and snapshots of {@code <iframe>}s. Defaults to {@code "default"}.
|
||||
*/
|
||||
public AriaSnapshotOptions setMode(AriaSnapshotMode mode) {
|
||||
this.mode = mode;
|
||||
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 AriaSnapshotOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class TapOptions {
|
||||
/**
|
||||
* Whether to bypass the <a href="https://playwright.dev/java/docs/actionability">actionability</a> checks. Defaults to
|
||||
@@ -3516,7 +3415,7 @@ public interface Page extends AutoCloseable {
|
||||
*/
|
||||
public Double timeout;
|
||||
/**
|
||||
* A glob pattern, regex pattern, or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
@@ -3546,7 +3445,7 @@ public interface Page extends AutoCloseable {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A glob pattern, regex pattern, or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
@@ -3555,7 +3454,7 @@ public interface Page extends AutoCloseable {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A glob pattern, regex pattern, or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
@@ -3564,7 +3463,7 @@ public interface Page extends AutoCloseable {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A glob pattern, regex pattern, or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
@@ -3900,7 +3799,7 @@ public interface Page extends AutoCloseable {
|
||||
* @param script Script to be evaluated in all pages in the browser context.
|
||||
* @since v1.8
|
||||
*/
|
||||
AutoCloseable addInitScript(String script);
|
||||
void addInitScript(String script);
|
||||
/**
|
||||
* Adds a script which would be evaluated in one of the following scenarios:
|
||||
* <ul>
|
||||
@@ -3927,7 +3826,7 @@ public interface Page extends AutoCloseable {
|
||||
* @param script Script to be evaluated in all pages in the browser context.
|
||||
* @since v1.8
|
||||
*/
|
||||
AutoCloseable addInitScript(Path script);
|
||||
void addInitScript(Path script);
|
||||
/**
|
||||
* Adds a {@code <script>} tag into the page with the desired url or content. Returns the added tag when the script's
|
||||
* onload fires or when the script content was injected into frame.
|
||||
@@ -3968,13 +3867,6 @@ public interface Page extends AutoCloseable {
|
||||
* @since v1.8
|
||||
*/
|
||||
void bringToFront();
|
||||
/**
|
||||
* Cancels an ongoing {@link com.microsoft.playwright.Page#pickLocator Page.pickLocator()} call by deactivating pick
|
||||
* locator mode. If no pick locator mode is active, this method is a no-op.
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
void cancelPickLocator();
|
||||
/**
|
||||
* This method checks an element matching {@code selector} by performing the following steps:
|
||||
* <ol>
|
||||
@@ -4706,7 +4598,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 void exposeBinding(String name, BindingCallback callback) {
|
||||
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
|
||||
*/
|
||||
void 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
|
||||
@@ -4768,7 +4710,7 @@ public interface Page extends AutoCloseable {
|
||||
* @param callback Callback function which will be called in Playwright's context.
|
||||
* @since v1.8
|
||||
*/
|
||||
AutoCloseable exposeFunction(String name, FunctionCallback callback);
|
||||
void exposeFunction(String name, FunctionCallback callback);
|
||||
/**
|
||||
* This method waits for an element matching {@code selector}, waits for <a
|
||||
* href="https://playwright.dev/java/docs/actionability">actionability</a> checks, focuses the element, fills it and
|
||||
@@ -5107,7 +5049,7 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> Consider the following DOM structure.
|
||||
*
|
||||
* <p> You can locate each element by its implicit role:
|
||||
* <p> You can locate each element by it's implicit role:
|
||||
* <pre>{@code
|
||||
* assertThat(page
|
||||
* .getByRole(AriaRole.HEADING,
|
||||
@@ -5150,7 +5092,7 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> Consider the following DOM structure.
|
||||
*
|
||||
* <p> You can locate each element by its implicit role:
|
||||
* <p> You can locate each element by it's implicit role:
|
||||
* <pre>{@code
|
||||
* assertThat(page
|
||||
* .getByRole(AriaRole.HEADING,
|
||||
@@ -5189,7 +5131,7 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> Consider the following DOM structure.
|
||||
*
|
||||
* <p> You can locate the element by its test id:
|
||||
* <p> You can locate the element by it's test id:
|
||||
* <pre>{@code
|
||||
* page.getByTestId("directions").click();
|
||||
* }</pre>
|
||||
@@ -5211,7 +5153,7 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> Consider the following DOM structure.
|
||||
*
|
||||
* <p> You can locate the element by its test id:
|
||||
* <p> You can locate the element by it's test id:
|
||||
* <pre>{@code
|
||||
* page.getByTestId("directions").click();
|
||||
* }</pre>
|
||||
@@ -5577,13 +5519,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>
|
||||
@@ -5794,43 +5729,6 @@ public interface Page extends AutoCloseable {
|
||||
* @since v1.8
|
||||
*/
|
||||
Keyboard keyboard();
|
||||
/**
|
||||
* Clears all stored console messages from this page. Subsequent calls to {@link
|
||||
* com.microsoft.playwright.Page#consoleMessages Page.consoleMessages()} will only return messages logged after the clear.
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
void clearConsoleMessages();
|
||||
/**
|
||||
* Clears all stored page errors from this page. Subsequent calls to {@link com.microsoft.playwright.Page#pageErrors
|
||||
* Page.pageErrors()} will only return errors thrown after the clear.
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
void clearPageErrors();
|
||||
/**
|
||||
* Returns up to (currently) 200 last console messages from this page. See {@link
|
||||
* com.microsoft.playwright.Page#onConsoleMessage Page.onConsoleMessage()} for more details.
|
||||
*
|
||||
* @since v1.56
|
||||
*/
|
||||
default List<ConsoleMessage> consoleMessages() {
|
||||
return consoleMessages(null);
|
||||
}
|
||||
/**
|
||||
* Returns up to (currently) 200 last console messages from this page. See {@link
|
||||
* com.microsoft.playwright.Page#onConsoleMessage Page.onConsoleMessage()} for more details.
|
||||
*
|
||||
* @since v1.56
|
||||
*/
|
||||
List<ConsoleMessage> consoleMessages(ConsoleMessagesOptions options);
|
||||
/**
|
||||
* Returns up to (currently) 200 last page errors from this page. See {@link com.microsoft.playwright.Page#onPageError
|
||||
* Page.onPageError()} for more details.
|
||||
*
|
||||
* @since v1.56
|
||||
*/
|
||||
List<String> pageErrors();
|
||||
/**
|
||||
* The method returns an element locator that can be used to perform actions on this page / frame. Locator is resolved to
|
||||
* the element immediately before performing an action, so a series of actions on the same locator can in fact be performed
|
||||
@@ -5914,8 +5812,8 @@ public interface Page extends AutoCloseable {
|
||||
*/
|
||||
Page opener();
|
||||
/**
|
||||
* Pauses script execution. Playwright will stop executing the script and wait for the user to either press the 'Resume'
|
||||
* button in the page overlay or to call {@code playwright.resume()} in the DevTools console.
|
||||
* Pauses script execution. Playwright will stop executing the script and wait for the user to either press 'Resume' button
|
||||
* in the page overlay or to call {@code playwright.resume()} in the DevTools console.
|
||||
*
|
||||
* <p> User can inspect selectors or perform manual steps while paused. Resume will continue running the original script from
|
||||
* the place it was paused.
|
||||
@@ -6039,19 +5937,6 @@ public interface Page extends AutoCloseable {
|
||||
* @since v1.8
|
||||
*/
|
||||
byte[] pdf(PdfOptions options);
|
||||
/**
|
||||
* Enters pick locator mode where hovering over page elements highlights them and shows the corresponding locator. Once the
|
||||
* user clicks an element, the mode is deactivated and the {@code Locator} for the picked element is returned.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* Locator locator = page.pickLocator();
|
||||
* System.out.println(locator);
|
||||
* }</pre>
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
Locator pickLocator();
|
||||
/**
|
||||
* Focuses the element, and then uses {@link com.microsoft.playwright.Keyboard#down Keyboard.down()} and {@link
|
||||
* com.microsoft.playwright.Keyboard#up Keyboard.up()}.
|
||||
@@ -6168,21 +6053,6 @@ public interface Page extends AutoCloseable {
|
||||
* @since v1.9
|
||||
*/
|
||||
List<ElementHandle> querySelectorAll(String selector);
|
||||
/**
|
||||
* Returns up to (currently) 100 last network request from this page. See {@link com.microsoft.playwright.Page#onRequest
|
||||
* Page.onRequest()} for more details.
|
||||
*
|
||||
* <p> Returned requests should be accessed immediately, otherwise they might be collected to prevent unbounded memory growth
|
||||
* as new requests come in. Once collected, retrieving most information about the request is impossible.
|
||||
*
|
||||
* <p> Note that requests reported through the {@link com.microsoft.playwright.Page#onRequest Page.onRequest()} request are not
|
||||
* collected, so there is a trade off between efficient memory usage with {@link com.microsoft.playwright.Page#requests
|
||||
* Page.requests()} and the amount of available information reported through {@link com.microsoft.playwright.Page#onRequest
|
||||
* Page.onRequest()}.
|
||||
*
|
||||
* @since v1.56
|
||||
*/
|
||||
List<Request> requests();
|
||||
/**
|
||||
* When testing a web page, sometimes unexpected overlays like a "Sign up" dialog appear and block actions you want to
|
||||
* automate, e.g. clicking a button. These overlays don't always show up in the same way or at the same time, making them
|
||||
@@ -6440,8 +6310,6 @@ public interface Page extends AutoCloseable {
|
||||
* });
|
||||
* }</pre>
|
||||
*
|
||||
* <p> If a request matches multiple registered routes, the most recently registered route takes precedence.
|
||||
*
|
||||
* <p> Page routes take precedence over browser context routes (set up with {@link
|
||||
* com.microsoft.playwright.BrowserContext#route BrowserContext.route()}) when request matches both handlers.
|
||||
*
|
||||
@@ -6449,14 +6317,14 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
*/
|
||||
default AutoCloseable route(String url, Consumer<Route> handler) {
|
||||
return route(url, handler, null);
|
||||
default void route(String url, Consumer<Route> handler) {
|
||||
route(url, handler, null);
|
||||
}
|
||||
/**
|
||||
* Routing provides the capability to modify network requests that are made by a page.
|
||||
@@ -6501,8 +6369,6 @@ public interface Page extends AutoCloseable {
|
||||
* });
|
||||
* }</pre>
|
||||
*
|
||||
* <p> If a request matches multiple registered routes, the most recently registered route takes precedence.
|
||||
*
|
||||
* <p> Page routes take precedence over browser context routes (set up with {@link
|
||||
* com.microsoft.playwright.BrowserContext#route BrowserContext.route()}) when request matches both handlers.
|
||||
*
|
||||
@@ -6510,13 +6376,13 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
*/
|
||||
AutoCloseable route(String url, Consumer<Route> handler, RouteOptions options);
|
||||
void route(String url, Consumer<Route> handler, RouteOptions options);
|
||||
/**
|
||||
* Routing provides the capability to modify network requests that are made by a page.
|
||||
*
|
||||
@@ -6560,8 +6426,6 @@ public interface Page extends AutoCloseable {
|
||||
* });
|
||||
* }</pre>
|
||||
*
|
||||
* <p> If a request matches multiple registered routes, the most recently registered route takes precedence.
|
||||
*
|
||||
* <p> Page routes take precedence over browser context routes (set up with {@link
|
||||
* com.microsoft.playwright.BrowserContext#route BrowserContext.route()}) when request matches both handlers.
|
||||
*
|
||||
@@ -6569,14 +6433,14 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
*/
|
||||
default AutoCloseable route(Pattern url, Consumer<Route> handler) {
|
||||
return route(url, handler, null);
|
||||
default void route(Pattern url, Consumer<Route> handler) {
|
||||
route(url, handler, null);
|
||||
}
|
||||
/**
|
||||
* Routing provides the capability to modify network requests that are made by a page.
|
||||
@@ -6621,8 +6485,6 @@ public interface Page extends AutoCloseable {
|
||||
* });
|
||||
* }</pre>
|
||||
*
|
||||
* <p> If a request matches multiple registered routes, the most recently registered route takes precedence.
|
||||
*
|
||||
* <p> Page routes take precedence over browser context routes (set up with {@link
|
||||
* com.microsoft.playwright.BrowserContext#route BrowserContext.route()}) when request matches both handlers.
|
||||
*
|
||||
@@ -6630,13 +6492,13 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
*/
|
||||
AutoCloseable route(Pattern url, Consumer<Route> handler, RouteOptions options);
|
||||
void route(Pattern url, Consumer<Route> handler, RouteOptions options);
|
||||
/**
|
||||
* Routing provides the capability to modify network requests that are made by a page.
|
||||
*
|
||||
@@ -6680,8 +6542,6 @@ public interface Page extends AutoCloseable {
|
||||
* });
|
||||
* }</pre>
|
||||
*
|
||||
* <p> If a request matches multiple registered routes, the most recently registered route takes precedence.
|
||||
*
|
||||
* <p> Page routes take precedence over browser context routes (set up with {@link
|
||||
* com.microsoft.playwright.BrowserContext#route BrowserContext.route()}) when request matches both handlers.
|
||||
*
|
||||
@@ -6689,14 +6549,14 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
*/
|
||||
default AutoCloseable route(Predicate<String> url, Consumer<Route> handler) {
|
||||
return route(url, handler, null);
|
||||
default void route(Predicate<String> url, Consumer<Route> handler) {
|
||||
route(url, handler, null);
|
||||
}
|
||||
/**
|
||||
* Routing provides the capability to modify network requests that are made by a page.
|
||||
@@ -6741,8 +6601,6 @@ public interface Page extends AutoCloseable {
|
||||
* });
|
||||
* }</pre>
|
||||
*
|
||||
* <p> If a request matches multiple registered routes, the most recently registered route takes precedence.
|
||||
*
|
||||
* <p> Page routes take precedence over browser context routes (set up with {@link
|
||||
* com.microsoft.playwright.BrowserContext#route BrowserContext.route()}) when request matches both handlers.
|
||||
*
|
||||
@@ -6750,13 +6608,13 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If {@code baseURL} is set in
|
||||
* the context options and the provided URL is a string that does not start with {@code *}, it is resolved using the <a
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing. When a {@code baseURL} via the
|
||||
* context options was provided and the passed URL is a path, it gets merged via the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/URL/URL">{@code new URL()}</a> constructor.
|
||||
* @param handler handler function to route the request.
|
||||
* @since v1.8
|
||||
*/
|
||||
AutoCloseable route(Predicate<String> url, Consumer<Route> handler, RouteOptions options);
|
||||
void route(Predicate<String> url, Consumer<Route> handler, RouteOptions options);
|
||||
/**
|
||||
* If specified the network requests that are made in the page will be served from the HAR file. Read more about <a
|
||||
* href="https://playwright.dev/java/docs/mock#replaying-from-har">Replaying from HAR</a>.
|
||||
@@ -6860,14 +6718,6 @@ public interface Page extends AutoCloseable {
|
||||
* @since v1.48
|
||||
*/
|
||||
void routeWebSocket(Predicate<String> url, Consumer<WebSocketRoute> handler);
|
||||
/**
|
||||
* {@code Screencast} object associated with this page.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
Screencast screencast();
|
||||
/**
|
||||
* Returns the buffer with the captured screenshot.
|
||||
*
|
||||
@@ -7522,22 +7372,6 @@ public interface Page extends AutoCloseable {
|
||||
* @since v1.8
|
||||
*/
|
||||
void setViewportSize(int width, int height);
|
||||
/**
|
||||
* Captures the aria snapshot of the page. Read more about <a href="https://playwright.dev/java/docs/aria-snapshots">aria
|
||||
* snapshots</a>.
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
default String ariaSnapshot() {
|
||||
return ariaSnapshot(null);
|
||||
}
|
||||
/**
|
||||
* Captures the aria snapshot of the page. Read more about <a href="https://playwright.dev/java/docs/aria-snapshots">aria
|
||||
* snapshots</a>.
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
String ariaSnapshot(AriaSnapshotOptions options);
|
||||
/**
|
||||
* This method taps an element matching {@code selector} by performing the following steps:
|
||||
* <ol>
|
||||
@@ -7685,7 +7519,7 @@ public interface Page extends AutoCloseable {
|
||||
* Removes a route created with {@link com.microsoft.playwright.Page#route Page.route()}. When {@code handler} is not
|
||||
* specified, removes all routes for the {@code url}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] to match while routing.
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing.
|
||||
* @since v1.8
|
||||
*/
|
||||
default void unroute(String url) {
|
||||
@@ -7695,7 +7529,7 @@ public interface Page extends AutoCloseable {
|
||||
* Removes a route created with {@link com.microsoft.playwright.Page#route Page.route()}. When {@code handler} is not
|
||||
* specified, removes all routes for the {@code url}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] to match while routing.
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing.
|
||||
* @param handler Optional handler function to route the request.
|
||||
* @since v1.8
|
||||
*/
|
||||
@@ -7704,7 +7538,7 @@ public interface Page extends AutoCloseable {
|
||||
* Removes a route created with {@link com.microsoft.playwright.Page#route Page.route()}. When {@code handler} is not
|
||||
* specified, removes all routes for the {@code url}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] to match while routing.
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing.
|
||||
* @since v1.8
|
||||
*/
|
||||
default void unroute(Pattern url) {
|
||||
@@ -7714,7 +7548,7 @@ public interface Page extends AutoCloseable {
|
||||
* Removes a route created with {@link com.microsoft.playwright.Page#route Page.route()}. When {@code handler} is not
|
||||
* specified, removes all routes for the {@code url}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] to match while routing.
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing.
|
||||
* @param handler Optional handler function to route the request.
|
||||
* @since v1.8
|
||||
*/
|
||||
@@ -7723,7 +7557,7 @@ public interface Page extends AutoCloseable {
|
||||
* Removes a route created with {@link com.microsoft.playwright.Page#route Page.route()}. When {@code handler} is not
|
||||
* specified, removes all routes for the {@code url}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] to match while routing.
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing.
|
||||
* @since v1.8
|
||||
*/
|
||||
default void unroute(Predicate<String> url) {
|
||||
@@ -7733,7 +7567,7 @@ public interface Page extends AutoCloseable {
|
||||
* Removes a route created with {@link com.microsoft.playwright.Page#route Page.route()}. When {@code handler} is not
|
||||
* specified, removes all routes for the {@code url}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] to match while routing.
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing.
|
||||
* @param handler Optional handler function to route the request.
|
||||
* @since v1.8
|
||||
*/
|
||||
@@ -7745,8 +7579,7 @@ public interface Page extends AutoCloseable {
|
||||
*/
|
||||
String url();
|
||||
/**
|
||||
* Video object associated with this page. Can be used to access the video file when using the {@code recordVideo} context
|
||||
* option.
|
||||
* Video object associated with this page.
|
||||
*
|
||||
* @since v1.8
|
||||
*/
|
||||
@@ -8572,7 +8405,7 @@ public interface Page extends AutoCloseable {
|
||||
* page.waitForURL("**\/target.html");
|
||||
* }</pre>
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
* @since v1.11
|
||||
@@ -8589,7 +8422,7 @@ public interface Page extends AutoCloseable {
|
||||
* page.waitForURL("**\/target.html");
|
||||
* }</pre>
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
* @since v1.11
|
||||
@@ -8604,7 +8437,7 @@ public interface Page extends AutoCloseable {
|
||||
* page.waitForURL("**\/target.html");
|
||||
* }</pre>
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
* @since v1.11
|
||||
@@ -8621,7 +8454,7 @@ public interface Page extends AutoCloseable {
|
||||
* page.waitForURL("**\/target.html");
|
||||
* }</pre>
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
* @since v1.11
|
||||
@@ -8636,7 +8469,7 @@ public interface Page extends AutoCloseable {
|
||||
* page.waitForURL("**\/target.html");
|
||||
* }</pre>
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
* @since v1.11
|
||||
@@ -8653,7 +8486,7 @@ public interface Page extends AutoCloseable {
|
||||
* page.waitForURL("**\/target.html");
|
||||
* }</pre>
|
||||
*
|
||||
* @param url A glob pattern, regex pattern, or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
* @since v1.11
|
||||
|
||||
@@ -183,16 +183,6 @@ public interface Request {
|
||||
* @since v1.8
|
||||
*/
|
||||
Response response();
|
||||
/**
|
||||
* Returns the {@code Response} object if the response has already been received, {@code null} otherwise.
|
||||
*
|
||||
* <p> Unlike {@link com.microsoft.playwright.Request#response Request.response()}, this method does not wait for the response
|
||||
* to arrive. It returns immediately with the response object if the response has been received, or {@code null} if the
|
||||
* response has not been received yet.
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
Response existingResponse();
|
||||
/**
|
||||
* Returns resource size information for given request.
|
||||
*
|
||||
|
||||
@@ -86,12 +86,6 @@ public interface Response {
|
||||
* @since v1.15
|
||||
*/
|
||||
List<String> headerValues(String name);
|
||||
/**
|
||||
* Returns the http version used by the response.
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
String httpVersion();
|
||||
/**
|
||||
* Contains a boolean stating whether the response was successful (status in the range 200-299) or not.
|
||||
*
|
||||
|
||||
@@ -370,11 +370,6 @@ public interface Route {
|
||||
* matching handlers won't be invoked. Use {@link com.microsoft.playwright.Route#fallback Route.fallback()} If you want
|
||||
* next matching handler in the chain to be invoked.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Some request headers are **forbidden** and cannot be overridden (for example, {@code Cookie}, {@code Host}, {@code
|
||||
* Content-Length} and others, see <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_request_header">this MDN page</a> for full list). If
|
||||
* an override is provided for a forbidden header, it will be ignored and the original request header will be used.To set custom cookies, use {@link com.microsoft.playwright.BrowserContext#addCookies BrowserContext.addCookies()}.
|
||||
*
|
||||
* @since v1.8
|
||||
*/
|
||||
default void resume() {
|
||||
@@ -403,11 +398,6 @@ public interface Route {
|
||||
* matching handlers won't be invoked. Use {@link com.microsoft.playwright.Route#fallback Route.fallback()} If you want
|
||||
* next matching handler in the chain to be invoked.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Some request headers are **forbidden** and cannot be overridden (for example, {@code Cookie}, {@code Host}, {@code
|
||||
* Content-Length} and others, see <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_request_header">this MDN page</a> for full list). If
|
||||
* an override is provided for a forbidden header, it will be ignored and the original request header will be used.To set custom cookies, use {@link com.microsoft.playwright.BrowserContext#addCookies BrowserContext.addCookies()}.
|
||||
*
|
||||
* @since v1.8
|
||||
*/
|
||||
void resume(ResumeOptions options);
|
||||
|
||||
@@ -1,237 +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.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Interface for capturing screencast frames from a page.
|
||||
*/
|
||||
public interface Screencast {
|
||||
class StartOptions {
|
||||
/**
|
||||
* Callback that receives JPEG-encoded frame data along with the page viewport size at the time of capture.
|
||||
*/
|
||||
public Consumer<ScreencastFrame> onFrame;
|
||||
/**
|
||||
* Path where the video should be saved when the screencast is stopped. When provided, video recording is started.
|
||||
*/
|
||||
public Path path;
|
||||
/**
|
||||
* The quality of the image, between 0-100.
|
||||
*/
|
||||
public Integer quality;
|
||||
|
||||
/**
|
||||
* Callback that receives JPEG-encoded frame data along with the page viewport size at the time of capture.
|
||||
*/
|
||||
public StartOptions setOnFrame(Consumer<ScreencastFrame> onFrame) {
|
||||
this.onFrame = onFrame;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Path where the video should be saved when the screencast is stopped. When provided, video recording is started.
|
||||
*/
|
||||
public StartOptions setPath(Path path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* The quality of the image, between 0-100.
|
||||
*/
|
||||
public StartOptions setQuality(int quality) {
|
||||
this.quality = quality;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class ShowOverlayOptions {
|
||||
/**
|
||||
* Duration in milliseconds after which the overlay is automatically removed. Overlay stays until dismissed if not
|
||||
* provided.
|
||||
*/
|
||||
public Double duration;
|
||||
|
||||
/**
|
||||
* Duration in milliseconds after which the overlay is automatically removed. Overlay stays until dismissed if not
|
||||
* provided.
|
||||
*/
|
||||
public ShowOverlayOptions setDuration(double duration) {
|
||||
this.duration = duration;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class ShowChapterOptions {
|
||||
/**
|
||||
* Optional description text displayed below the title.
|
||||
*/
|
||||
public String description;
|
||||
/**
|
||||
* Duration in milliseconds after which the overlay is automatically removed. Defaults to {@code 2000}.
|
||||
*/
|
||||
public Double duration;
|
||||
|
||||
/**
|
||||
* Optional description text displayed below the title.
|
||||
*/
|
||||
public ShowChapterOptions setDescription(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Duration in milliseconds after which the overlay is automatically removed. Defaults to {@code 2000}.
|
||||
*/
|
||||
public ShowChapterOptions setDuration(double duration) {
|
||||
this.duration = duration;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class ShowActionsOptions {
|
||||
/**
|
||||
* How long each annotation is displayed in milliseconds. Defaults to {@code 500}.
|
||||
*/
|
||||
public Double duration;
|
||||
/**
|
||||
* Font size of the action title in pixels. Defaults to {@code 24}.
|
||||
*/
|
||||
public Integer fontSize;
|
||||
/**
|
||||
* Position of the action title overlay. Defaults to {@code "top-right"}.
|
||||
*/
|
||||
public AnnotatePosition position;
|
||||
|
||||
/**
|
||||
* How long each annotation is displayed in milliseconds. Defaults to {@code 500}.
|
||||
*/
|
||||
public ShowActionsOptions setDuration(double duration) {
|
||||
this.duration = duration;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Font size of the action title in pixels. Defaults to {@code 24}.
|
||||
*/
|
||||
public ShowActionsOptions setFontSize(int fontSize) {
|
||||
this.fontSize = fontSize;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Position of the action title overlay. Defaults to {@code "top-right"}.
|
||||
*/
|
||||
public ShowActionsOptions setPosition(AnnotatePosition position) {
|
||||
this.position = position;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Starts the screencast. When {@code path} is provided, it saves video recording to the specified file. When {@code
|
||||
* onFrame} is provided, delivers JPEG-encoded frames to the callback. Both can be used together.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
default AutoCloseable start() {
|
||||
return start(null);
|
||||
}
|
||||
/**
|
||||
* Starts the screencast. When {@code path} is provided, it saves video recording to the specified file. When {@code
|
||||
* onFrame} is provided, delivers JPEG-encoded frames to the callback. Both can be used together.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
AutoCloseable start(StartOptions options);
|
||||
/**
|
||||
* Stops the screencast and video recording if active. If a video was being recorded, saves it to the path specified in
|
||||
* {@link com.microsoft.playwright.Screencast#start Screencast.start()}.
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
void stop();
|
||||
/**
|
||||
* Adds an overlay with the given HTML content. The overlay is displayed on top of the page until removed. Returns a
|
||||
* disposable that removes the overlay when disposed.
|
||||
*
|
||||
* @param html HTML content for the overlay.
|
||||
* @since v1.59
|
||||
*/
|
||||
default AutoCloseable showOverlay(String html) {
|
||||
return showOverlay(html, null);
|
||||
}
|
||||
/**
|
||||
* Adds an overlay with the given HTML content. The overlay is displayed on top of the page until removed. Returns a
|
||||
* disposable that removes the overlay when disposed.
|
||||
*
|
||||
* @param html HTML content for the overlay.
|
||||
* @since v1.59
|
||||
*/
|
||||
AutoCloseable showOverlay(String html, ShowOverlayOptions options);
|
||||
/**
|
||||
* Shows a chapter overlay with a title and optional description, centered on the page with a blurred backdrop. Useful for
|
||||
* narrating video recordings. The overlay is removed after the specified duration, or 2000ms.
|
||||
*
|
||||
* @param title Title text displayed prominently in the overlay.
|
||||
* @since v1.59
|
||||
*/
|
||||
default void showChapter(String title) {
|
||||
showChapter(title, null);
|
||||
}
|
||||
/**
|
||||
* Shows a chapter overlay with a title and optional description, centered on the page with a blurred backdrop. Useful for
|
||||
* narrating video recordings. The overlay is removed after the specified duration, or 2000ms.
|
||||
*
|
||||
* @param title Title text displayed prominently in the overlay.
|
||||
* @since v1.59
|
||||
*/
|
||||
void showChapter(String title, ShowChapterOptions options);
|
||||
/**
|
||||
* Enables visual annotations on interacted elements. Returns a disposable that stops showing actions when disposed.
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
default AutoCloseable showActions() {
|
||||
return showActions(null);
|
||||
}
|
||||
/**
|
||||
* Enables visual annotations on interacted elements. Returns a disposable that stops showing actions when disposed.
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
AutoCloseable showActions(ShowActionsOptions options);
|
||||
/**
|
||||
* Shows overlays.
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
void showOverlays();
|
||||
/**
|
||||
* Removes action decorations.
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
void hideActions();
|
||||
/**
|
||||
* Hides overlays without removing them.
|
||||
*
|
||||
* @since v1.59
|
||||
*/
|
||||
void hideOverlays();
|
||||
}
|
||||
|
||||
@@ -1,34 +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();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
@@ -18,18 +18,11 @@ 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
|
||||
* href="https://playwright.dev/java/docs/trace-viewer">Trace Viewer</a> after Playwright script runs.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> You probably want to <a href="https://playwright.dev/docs/api/class-testoptions#test-options-trace">enable tracing in
|
||||
* your config file</a> instead of using {@code context.tracing}.The {@code context.tracing} API captures browser operations and network activity, but it doesn't record test assertions
|
||||
* (like {@code expect} calls). We recommend <a
|
||||
* href="https://playwright.dev/docs/api/class-testoptions#test-options-trace">enabling tracing through Playwright Test
|
||||
* configuration</a>, which includes those assertions and provides a more complete trace for debugging test failures.
|
||||
*
|
||||
* <p> Start recording a trace before performing actions. At the end, stop tracing and save it to a file.
|
||||
* <pre>{@code
|
||||
* Browser browser = chromium.launch();
|
||||
@@ -45,12 +38,6 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public interface Tracing {
|
||||
class StartOptions {
|
||||
/**
|
||||
* When enabled, the trace is written to an unarchived file that is updated in real time as actions occur, instead of
|
||||
* caching changes and archiving them into a zip file at the end. This is useful for live trace viewing during test
|
||||
* execution.
|
||||
*/
|
||||
public Boolean live;
|
||||
/**
|
||||
* If specified, intermediate trace files are going to be saved into the files with the given name prefix inside the {@code
|
||||
* tracesDir} directory specified in {@link com.microsoft.playwright.BrowserType#launch BrowserType.launch()}. To specify
|
||||
@@ -81,15 +68,6 @@ public interface Tracing {
|
||||
*/
|
||||
public String title;
|
||||
|
||||
/**
|
||||
* When enabled, the trace is written to an unarchived file that is updated in real time as actions occur, instead of
|
||||
* caching changes and archiving them into a zip file at the end. This is useful for live trace viewing during test
|
||||
* execution.
|
||||
*/
|
||||
public StartOptions setLive(boolean live) {
|
||||
this.live = live;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* If specified, intermediate trace files are going to be saved into the files with the given name prefix inside the {@code
|
||||
* tracesDir} directory specified in {@link com.microsoft.playwright.BrowserType#launch BrowserType.launch()}. To specify
|
||||
@@ -166,59 +144,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
|
||||
@@ -275,12 +200,6 @@ public interface Tracing {
|
||||
/**
|
||||
* Start tracing.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> You probably want to <a href="https://playwright.dev/docs/api/class-testoptions#test-options-trace">enable tracing in
|
||||
* your config file</a> instead of using {@code Tracing.start}.The {@code context.tracing} API captures browser operations and network activity, but it doesn't record test assertions
|
||||
* (like {@code expect} calls). We recommend <a
|
||||
* href="https://playwright.dev/docs/api/class-testoptions#test-options-trace">enabling tracing through Playwright Test
|
||||
* configuration</a>, which includes those assertions and provides a more complete trace for debugging test failures.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* context.tracing().start(new Tracing.StartOptions()
|
||||
@@ -300,12 +219,6 @@ public interface Tracing {
|
||||
/**
|
||||
* Start tracing.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> You probably want to <a href="https://playwright.dev/docs/api/class-testoptions#test-options-trace">enable tracing in
|
||||
* your config file</a> instead of using {@code Tracing.start}.The {@code context.tracing} API captures browser operations and network activity, but it doesn't record test assertions
|
||||
* (like {@code expect} calls). We recommend <a
|
||||
* href="https://playwright.dev/docs/api/class-testoptions#test-options-trace">enabling tracing through Playwright Test
|
||||
* configuration</a>, which includes those assertions and provides a more complete trace for debugging test failures.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* context.tracing().start(new Tracing.StartOptions()
|
||||
@@ -382,48 +295,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.
|
||||
*
|
||||
@@ -444,8 +315,8 @@ public interface Tracing {
|
||||
* @param name Group name shown in the trace viewer.
|
||||
* @since v1.49
|
||||
*/
|
||||
default AutoCloseable group(String name) {
|
||||
return group(name, null);
|
||||
default void group(String name) {
|
||||
group(name, null);
|
||||
}
|
||||
/**
|
||||
* <strong>NOTE:</strong> Use {@code test.step} instead when available.
|
||||
@@ -467,7 +338,7 @@ public interface Tracing {
|
||||
* @param name Group name shown in the trace viewer.
|
||||
* @since v1.49
|
||||
*/
|
||||
AutoCloseable group(String name, GroupOptions options);
|
||||
void group(String name, GroupOptions options);
|
||||
/**
|
||||
* Closes the last group created by {@link com.microsoft.playwright.Tracing#group Tracing.group()}.
|
||||
*
|
||||
@@ -504,12 +375,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.*;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -28,7 +27,7 @@ import java.util.function.Consumer;
|
||||
*
|
||||
* <p> <strong>Mocking</strong>
|
||||
*
|
||||
* <p> By default, the routed WebSocket will not connect to the server. This way, you can mock entire communication over the
|
||||
* <p> By default, the routed WebSocket will not connect to the server. This way, you can mock entire communcation over the
|
||||
* WebSocket. Here is an example that responds to a {@code "request"} with a {@code "response"}.
|
||||
* <pre>{@code
|
||||
* page.routeWebSocket("wss://example.com/ws", ws -> {
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* The Worker class represents a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API">WebWorker</a>.
|
||||
@@ -45,16 +44,6 @@ public interface Worker {
|
||||
*/
|
||||
void offClose(Consumer<Worker> handler);
|
||||
|
||||
/**
|
||||
* Emitted when JavaScript within the worker calls one of console API methods, e.g. {@code console.log} or {@code
|
||||
* console.dir}.
|
||||
*/
|
||||
void onConsole(Consumer<ConsoleMessage> handler);
|
||||
/**
|
||||
* Removes handler that was previously added with {@link #onConsole onConsole(handler)}.
|
||||
*/
|
||||
void offConsole(Consumer<ConsoleMessage> handler);
|
||||
|
||||
class WaitForCloseOptions {
|
||||
/**
|
||||
* Maximum time to wait for in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The
|
||||
@@ -73,35 +62,6 @@ public interface Worker {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class WaitForConsoleMessageOptions {
|
||||
/**
|
||||
* Receives the {@code ConsoleMessage} object and resolves to true when the waiting should resolve.
|
||||
*/
|
||||
public Predicate<ConsoleMessage> predicate;
|
||||
/**
|
||||
* 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()}.
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Receives the {@code ConsoleMessage} object and resolves to true when the waiting should resolve.
|
||||
*/
|
||||
public WaitForConsoleMessageOptions setPredicate(Predicate<ConsoleMessage> predicate) {
|
||||
this.predicate = predicate;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* 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()}.
|
||||
*/
|
||||
public WaitForConsoleMessageOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns the return value of {@code expression}.
|
||||
*
|
||||
@@ -198,21 +158,5 @@ public interface Worker {
|
||||
* @since v1.10
|
||||
*/
|
||||
Worker waitForClose(WaitForCloseOptions options, Runnable callback);
|
||||
/**
|
||||
* Performs action and waits for a console message.
|
||||
*
|
||||
* @param callback Callback that performs the action triggering the event.
|
||||
* @since v1.57
|
||||
*/
|
||||
default ConsoleMessage waitForConsoleMessage(Runnable callback) {
|
||||
return waitForConsoleMessage(null, callback);
|
||||
}
|
||||
/**
|
||||
* Performs action and waits for a console message.
|
||||
*
|
||||
* @param callback Callback that performs the action triggering the event.
|
||||
* @since v1.57
|
||||
*/
|
||||
ConsoleMessage waitForConsoleMessage(WaitForConsoleMessageOptions options, Runnable callback);
|
||||
}
|
||||
|
||||
|
||||
+2
-5
@@ -37,11 +37,8 @@ package com.microsoft.playwright.assertions;
|
||||
*/
|
||||
public interface APIResponseAssertions {
|
||||
/**
|
||||
* Makes the assertion check for the opposite condition.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
*
|
||||
* <p> For example, this code tests that the response status is not successful:
|
||||
* Makes the assertion check for the opposite condition. For example, this code tests that the response status is not
|
||||
* successful:
|
||||
* <pre>{@code
|
||||
* assertThat(response).not().isOK();
|
||||
* }</pre>
|
||||
|
||||
+34
-164
@@ -16,10 +16,8 @@
|
||||
|
||||
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
|
||||
@@ -239,20 +237,6 @@ public interface LocatorAssertions {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class ContainsClassOptions {
|
||||
/**
|
||||
* 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 ContainsClassOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class ContainsTextOptions {
|
||||
/**
|
||||
* Whether to perform case-insensitive match. {@code ignoreCase} option takes precedence over the corresponding regular
|
||||
@@ -428,22 +412,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}.
|
||||
*/
|
||||
@@ -575,11 +548,8 @@ public interface LocatorAssertions {
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Makes the assertion check for the opposite condition.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
*
|
||||
* <p> For example, this code tests that the Locator doesn't contain text {@code "error"}:
|
||||
* Makes the assertion check for the opposite condition. For example, this code tests that the Locator doesn't contain text
|
||||
* {@code "error"}:
|
||||
* <pre>{@code
|
||||
* assertThat(locator).not().containsText("error");
|
||||
* }</pre>
|
||||
@@ -885,98 +855,6 @@ public interface LocatorAssertions {
|
||||
* @since v1.20
|
||||
*/
|
||||
void isVisible(IsVisibleOptions options);
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. All classes from the asserted value, separated
|
||||
* by spaces, must be present in the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/Element/classList">Element.classList</a> in any order.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).containsClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).containsClass("selected");
|
||||
* assertThat(page.locator("#component")).containsClass("row middle");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class lists. Each element's class attribute is matched against the corresponding class in the array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).containsClass(Arrays.asList("inactive", "active", "inactive"));
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected A string containing expected class names, separated by spaces, or a list of such strings to assert multiple elements.
|
||||
* @since v1.52
|
||||
*/
|
||||
default void containsClass(String expected) {
|
||||
containsClass(expected, null);
|
||||
}
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. All classes from the asserted value, separated
|
||||
* by spaces, must be present in the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/Element/classList">Element.classList</a> in any order.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).containsClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).containsClass("selected");
|
||||
* assertThat(page.locator("#component")).containsClass("row middle");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class lists. Each element's class attribute is matched against the corresponding class in the array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).containsClass(Arrays.asList("inactive", "active", "inactive"));
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected A string containing expected class names, separated by spaces, or a list of such strings to assert multiple elements.
|
||||
* @since v1.52
|
||||
*/
|
||||
void containsClass(String expected, ContainsClassOptions options);
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. All classes from the asserted value, separated
|
||||
* by spaces, must be present in the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/Element/classList">Element.classList</a> in any order.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).containsClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).containsClass("selected");
|
||||
* assertThat(page.locator("#component")).containsClass("row middle");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class lists. Each element's class attribute is matched against the corresponding class in the array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).containsClass(Arrays.asList("inactive", "active", "inactive"));
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected A string containing expected class names, separated by spaces, or a list of such strings to assert multiple elements.
|
||||
* @since v1.52
|
||||
*/
|
||||
default void containsClass(List<String> expected) {
|
||||
containsClass(expected, null);
|
||||
}
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. All classes from the asserted value, separated
|
||||
* by spaces, must be present in the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/Element/classList">Element.classList</a> in any order.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).containsClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).containsClass("selected");
|
||||
* assertThat(page.locator("#component")).containsClass("row middle");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class lists. Each element's class attribute is matched against the corresponding class in the array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).containsClass(Arrays.asList("inactive", "active", "inactive"));
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected A string containing expected class names, separated by spaces, or a list of such strings to assert multiple elements.
|
||||
* @since v1.52
|
||||
*/
|
||||
void containsClass(List<String> expected, ContainsClassOptions options);
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element that contains the given text. All nested elements will be considered
|
||||
* when computing the text content of the element. You can use regular expressions for the value as well.
|
||||
@@ -1004,7 +882,7 @@ public interface LocatorAssertions {
|
||||
* <p> Let's see how we can use the assertion:
|
||||
* <pre>{@code
|
||||
* // ✓ Contains the right items in the right order
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 1", "Text 3"});
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 1", "Text 3", "Text 4"});
|
||||
*
|
||||
* // ✖ Wrong order
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 3", "Text 2"});
|
||||
@@ -1049,7 +927,7 @@ public interface LocatorAssertions {
|
||||
* <p> Let's see how we can use the assertion:
|
||||
* <pre>{@code
|
||||
* // ✓ Contains the right items in the right order
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 1", "Text 3"});
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 1", "Text 3", "Text 4"});
|
||||
*
|
||||
* // ✖ Wrong order
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 3", "Text 2"});
|
||||
@@ -1092,7 +970,7 @@ public interface LocatorAssertions {
|
||||
* <p> Let's see how we can use the assertion:
|
||||
* <pre>{@code
|
||||
* // ✓ Contains the right items in the right order
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 1", "Text 3"});
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 1", "Text 3", "Text 4"});
|
||||
*
|
||||
* // ✖ Wrong order
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 3", "Text 2"});
|
||||
@@ -1137,7 +1015,7 @@ public interface LocatorAssertions {
|
||||
* <p> Let's see how we can use the assertion:
|
||||
* <pre>{@code
|
||||
* // ✓ Contains the right items in the right order
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 1", "Text 3"});
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 1", "Text 3", "Text 4"});
|
||||
*
|
||||
* // ✖ Wrong order
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 3", "Text 2"});
|
||||
@@ -1180,7 +1058,7 @@ public interface LocatorAssertions {
|
||||
* <p> Let's see how we can use the assertion:
|
||||
* <pre>{@code
|
||||
* // ✓ Contains the right items in the right order
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 1", "Text 3"});
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 1", "Text 3", "Text 4"});
|
||||
*
|
||||
* // ✖ Wrong order
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 3", "Text 2"});
|
||||
@@ -1225,7 +1103,7 @@ public interface LocatorAssertions {
|
||||
* <p> Let's see how we can use the assertion:
|
||||
* <pre>{@code
|
||||
* // ✓ Contains the right items in the right order
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 1", "Text 3"});
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 1", "Text 3", "Text 4"});
|
||||
*
|
||||
* // ✖ Wrong order
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 3", "Text 2"});
|
||||
@@ -1268,7 +1146,7 @@ public interface LocatorAssertions {
|
||||
* <p> Let's see how we can use the assertion:
|
||||
* <pre>{@code
|
||||
* // ✓ Contains the right items in the right order
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 1", "Text 3"});
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 1", "Text 3", "Text 4"});
|
||||
*
|
||||
* // ✖ Wrong order
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 3", "Text 2"});
|
||||
@@ -1313,7 +1191,7 @@ public interface LocatorAssertions {
|
||||
* <p> Let's see how we can use the assertion:
|
||||
* <pre>{@code
|
||||
* // ✓ Contains the right items in the right order
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 1", "Text 3"});
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 1", "Text 3", "Text 4"});
|
||||
*
|
||||
* // ✖ Wrong order
|
||||
* assertThat(page.locator("ul > li")).containsText(new String[] {"Text 3", "Text 2"});
|
||||
@@ -1567,20 +1445,19 @@ public interface LocatorAssertions {
|
||||
void hasAttribute(String name, Pattern value, HasAttributeOptions options);
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
|
||||
* the element's {@code class} attribute. To match individual classes use {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#containsClass LocatorAssertions.containsClass()}.
|
||||
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -1591,20 +1468,19 @@ public interface LocatorAssertions {
|
||||
}
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
|
||||
* the element's {@code class} attribute. To match individual classes use {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#containsClass LocatorAssertions.containsClass()}.
|
||||
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -1613,20 +1489,19 @@ public interface LocatorAssertions {
|
||||
void hasClass(String expected, HasClassOptions options);
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
|
||||
* the element's {@code class} attribute. To match individual classes use {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#containsClass LocatorAssertions.containsClass()}.
|
||||
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -1637,20 +1512,19 @@ public interface LocatorAssertions {
|
||||
}
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
|
||||
* the element's {@code class} attribute. To match individual classes use {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#containsClass LocatorAssertions.containsClass()}.
|
||||
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -1659,20 +1533,19 @@ public interface LocatorAssertions {
|
||||
void hasClass(Pattern expected, HasClassOptions options);
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
|
||||
* the element's {@code class} attribute. To match individual classes use {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#containsClass LocatorAssertions.containsClass()}.
|
||||
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -1683,20 +1556,19 @@ public interface LocatorAssertions {
|
||||
}
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
|
||||
* the element's {@code class} attribute. To match individual classes use {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#containsClass LocatorAssertions.containsClass()}.
|
||||
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -1705,20 +1577,19 @@ public interface LocatorAssertions {
|
||||
void hasClass(String[] expected, HasClassOptions options);
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
|
||||
* the element's {@code class} attribute. To match individual classes use {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#containsClass LocatorAssertions.containsClass()}.
|
||||
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -1729,20 +1600,19 @@ public interface LocatorAssertions {
|
||||
}
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
|
||||
* the element's {@code class} attribute. To match individual classes use {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#containsClass LocatorAssertions.containsClass()}.
|
||||
* the element's {@code class} attribute. To match individual classes or perform partial matches, use a regular expression:
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
|
||||
@@ -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}.
|
||||
@@ -93,11 +79,8 @@ public interface PageAssertions {
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Makes the assertion check for the opposite condition.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
*
|
||||
* <p> For example, this code tests that the page URL doesn't contain {@code "error"}:
|
||||
* Makes the assertion check for the opposite condition. For example, this code tests that the page URL doesn't contain
|
||||
* {@code "error"}:
|
||||
* <pre>{@code
|
||||
* assertThat(page).not().hasURL("error");
|
||||
* }</pre>
|
||||
@@ -105,40 +88,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.
|
||||
*
|
||||
|
||||
@@ -39,18 +39,11 @@ class APIRequestContextImpl extends ChannelOwner implements APIRequestContext {
|
||||
private final TracingImpl tracing;
|
||||
private String disposeReason;
|
||||
|
||||
protected TimeoutSettings timeoutSettings = new TimeoutSettings();
|
||||
|
||||
APIRequestContextImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
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"));
|
||||
@@ -58,17 +51,21 @@ class APIRequestContextImpl extends ChannelOwner implements APIRequestContext {
|
||||
|
||||
@Override
|
||||
public void dispose(DisposeOptions options) {
|
||||
withLogging("APIRequestContext.dispose", () -> disposeImpl(options));
|
||||
}
|
||||
|
||||
private void disposeImpl(DisposeOptions options) {
|
||||
if (options == null) {
|
||||
options = new DisposeOptions();
|
||||
}
|
||||
disposeReason = options.reason;
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("dispose", params, NO_TIMEOUT);
|
||||
sendMessage("dispose", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public APIResponse fetch(String urlOrRequest, RequestOptions options) {
|
||||
return fetchImpl(urlOrRequest, (RequestOptionsImpl) options);
|
||||
return withLogging("APIRequestContext.fetch", () -> fetchImpl(urlOrRequest, (RequestOptionsImpl) options));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -96,7 +93,6 @@ class APIRequestContextImpl extends ChannelOwner implements APIRequestContext {
|
||||
if (options == null) {
|
||||
options = new RequestOptionsImpl();
|
||||
}
|
||||
options.timeout = timeoutSettings.timeout(options.timeout);
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("url", url);
|
||||
if (options.params != null) {
|
||||
@@ -136,6 +132,9 @@ class APIRequestContextImpl extends ChannelOwner implements APIRequestContext {
|
||||
if (options.multipart != null) {
|
||||
params.add("multipartData", serializeMultipartData(options.multipart.fields));
|
||||
}
|
||||
if (options.timeout != null) {
|
||||
params.addProperty("timeout", options.timeout);
|
||||
}
|
||||
if (options.failOnStatusCode != null) {
|
||||
params.addProperty("failOnStatusCode", options.failOnStatusCode);
|
||||
}
|
||||
@@ -154,7 +153,7 @@ class APIRequestContextImpl extends ChannelOwner implements APIRequestContext {
|
||||
}
|
||||
params.addProperty("maxRetries", options.maxRetries);
|
||||
}
|
||||
JsonObject json = sendMessage("fetch", params, timeoutSettings.timeout(options.timeout)).getAsJsonObject();
|
||||
JsonObject json = sendMessage("fetch", params).getAsJsonObject();
|
||||
return new APIResponseImpl(this, json.getAsJsonObject("response"));
|
||||
}
|
||||
|
||||
@@ -220,12 +219,14 @@ class APIRequestContextImpl extends ChannelOwner implements APIRequestContext {
|
||||
|
||||
@Override
|
||||
public String storageState(StorageStateOptions options) {
|
||||
JsonElement json = sendMessage("storageState");
|
||||
String storageState = json.toString();
|
||||
if (options != null && options.path != null) {
|
||||
Utils.writeToFile(storageState.getBytes(StandardCharsets.UTF_8), options.path);
|
||||
}
|
||||
return storageState;
|
||||
return withLogging("APIRequestContext.storageState", () -> {
|
||||
JsonElement json = sendMessage("storageState");
|
||||
String storageState = json.toString();
|
||||
if (options != null && options.path != null) {
|
||||
Utils.writeToFile(storageState.getBytes(StandardCharsets.UTF_8), options.path);
|
||||
}
|
||||
return storageState;
|
||||
});
|
||||
}
|
||||
|
||||
private static RequestOptionsImpl ensureOptions(RequestOptions options, String method) {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.APIRequest;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
@@ -25,11 +26,12 @@ import com.microsoft.playwright.options.ClientCertificate;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
|
||||
import static com.microsoft.playwright.impl.ChannelOwner.NO_TIMEOUT;
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
import static com.microsoft.playwright.impl.Utils.addToProtocol;
|
||||
import static java.nio.file.Files.readAllBytes;
|
||||
|
||||
class APIRequestImpl implements APIRequest {
|
||||
private final PlaywrightImpl playwright;
|
||||
@@ -40,6 +42,10 @@ class APIRequestImpl implements APIRequest {
|
||||
|
||||
@Override
|
||||
public APIRequestContextImpl newContext(NewContextOptions options) {
|
||||
return playwright.withLogging("APIRequest.newContext", () -> newContextImpl(options));
|
||||
}
|
||||
|
||||
private APIRequestContextImpl newContextImpl(NewContextOptions options) {
|
||||
if (options == null) {
|
||||
options = new NewContextOptions();
|
||||
} else {
|
||||
@@ -61,17 +67,13 @@ class APIRequestImpl implements APIRequest {
|
||||
}
|
||||
List<ClientCertificate> clientCertificateList = options.clientCertificates;
|
||||
options.clientCertificates = null;
|
||||
Double timeout = options.timeout;
|
||||
// Timeout is handled on the client.
|
||||
options.timeout = null;
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
if (storageState != null) {
|
||||
params.add("storageState", storageState);
|
||||
}
|
||||
addToProtocol(params, clientCertificateList);
|
||||
JsonObject result = playwright.sendMessage("newRequest", params, NO_TIMEOUT).getAsJsonObject();
|
||||
JsonObject result = playwright.sendMessage("newRequest", params).getAsJsonObject();
|
||||
APIRequestContextImpl context = playwright.connection.getExistingObject(result.getAsJsonObject("request").get("guid").getAsString());
|
||||
context.timeoutSettings.setDefaultTimeout(timeout);
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.microsoft.playwright.APIResponse;
|
||||
@@ -28,7 +29,6 @@ import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.microsoft.playwright.impl.ChannelOwner.NO_TIMEOUT;
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
import static com.microsoft.playwright.impl.Utils.isSafeCloseError;
|
||||
import static java.util.Arrays.asList;
|
||||
@@ -49,7 +49,7 @@ class APIResponseImpl implements APIResponse {
|
||||
try {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("fetchUid", fetchUid());
|
||||
JsonObject json = context.sendMessage("fetchResponseBody", params, NO_TIMEOUT).getAsJsonObject();
|
||||
JsonObject json = context.sendMessage("fetchResponseBody", params).getAsJsonObject();
|
||||
if (!json.has("binary")) {
|
||||
throw new PlaywrightException("Response has been disposed");
|
||||
}
|
||||
@@ -64,9 +64,11 @@ class APIResponseImpl implements APIResponse {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("fetchUid", fetchUid());
|
||||
context.sendMessage("disposeAPIResponse", params, NO_TIMEOUT);
|
||||
context.withLogging("APIResponse.dispose", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("fetchUid", fetchUid());
|
||||
context.sendMessage("disposeAPIResponse", params);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -112,7 +114,7 @@ class APIResponseImpl implements APIResponse {
|
||||
List<String> fetchLog() {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("fetchUid", fetchUid());
|
||||
JsonObject json = context.sendMessage("fetchLog", params, NO_TIMEOUT).getAsJsonObject();
|
||||
JsonObject json = context.sendMessage("fetchLog", params).getAsJsonObject();
|
||||
JsonArray log = json.get("log").getAsJsonArray();
|
||||
return gson().fromJson(log, new TypeToken<List<String>>() {}.getType());
|
||||
}
|
||||
|
||||
@@ -86,6 +86,6 @@ class ArtifactImpl extends ChannelOwner {
|
||||
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("path", path.toString());
|
||||
sendMessage("saveAs", params, NO_TIMEOUT);
|
||||
sendMessage("saveAs", params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import org.opentest4j.AssertionFailedError;
|
||||
import org.opentest4j.ValueWrapper;
|
||||
|
||||
@@ -28,26 +29,28 @@ import java.util.stream.Collectors;
|
||||
import static com.microsoft.playwright.impl.Utils.toJsRegexFlags;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
abstract class AssertionsBase {
|
||||
class AssertionsBase {
|
||||
final LocatorImpl actualLocator;
|
||||
final boolean isNot;
|
||||
|
||||
AssertionsBase(boolean isNot) {
|
||||
AssertionsBase(LocatorImpl actual, boolean isNot) {
|
||||
this.actualLocator = actual;
|
||||
this.isNot = isNot;
|
||||
}
|
||||
|
||||
void expectImpl(String expression, ExpectedTextValue textValue, Object expected, String message, FrameExpectOptions options, String title) {
|
||||
expectImpl(expression, asList(textValue), expected, message, options, title);
|
||||
void expectImpl(String expression, ExpectedTextValue textValue, Object expected, String message, FrameExpectOptions options) {
|
||||
expectImpl(expression, asList(textValue), expected, message, options);
|
||||
}
|
||||
|
||||
void expectImpl(String expression, List<ExpectedTextValue> expectedText, Object expected, String message, FrameExpectOptions options, String title) {
|
||||
void expectImpl(String expression, List<ExpectedTextValue> expectedText, Object expected, String message, FrameExpectOptions options) {
|
||||
if (options == null) {
|
||||
options = new FrameExpectOptions();
|
||||
}
|
||||
options.expectedText = expectedText;
|
||||
expectImpl(expression, options, expected, message, title);
|
||||
expectImpl(expression, options, expected, message);
|
||||
}
|
||||
|
||||
void expectImpl(String expression, FrameExpectOptions expectOptions, Object expected, String message, String title) {
|
||||
void expectImpl(String expression, FrameExpectOptions expectOptions, Object expected, String message) {
|
||||
if (expectOptions.timeout == null) {
|
||||
expectOptions.timeout = AssertionsTimeout.defaultTimeout;
|
||||
}
|
||||
@@ -55,36 +58,24 @@ abstract class AssertionsBase {
|
||||
if (isNot) {
|
||||
message = message.replace("expected to", "expected not to");
|
||||
}
|
||||
FrameExpectResult result = doExpect(expression, expectOptions, title);
|
||||
FrameExpectResult result = actualLocator.expect(expression, expectOptions);
|
||||
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;
|
||||
}
|
||||
if (result.errorMessage != null) {
|
||||
message += "\n" + result.errorMessage;
|
||||
}
|
||||
if (expected == null) {
|
||||
throw new AssertionFailedError(message + log);
|
||||
}
|
||||
ValueWrapper expectedValue = formatValue(expected);
|
||||
ValueWrapper actualValue = formatValue(actual);
|
||||
message += "\nExpected: " + expectedValue.getStringRepresentation() + "\nReceived: " + actualValue.getStringRepresentation() + "\n";
|
||||
message += ": " + expectedValue.getStringRepresentation() + "\nReceived: " + actualValue.getStringRepresentation() + "\n";
|
||||
throw new AssertionFailedError(message + log, expectedValue, actualValue);
|
||||
}
|
||||
}
|
||||
|
||||
abstract FrameExpectResult doExpect(String expression, FrameExpectOptions expectOptions, String title);
|
||||
|
||||
protected static ValueWrapper formatValue(Object value) {
|
||||
private static ValueWrapper formatValue(Object value) {
|
||||
if (value == null || !value.getClass().isArray()) {
|
||||
return ValueWrapper.create(value);
|
||||
}
|
||||
|
||||
@@ -78,11 +78,11 @@ class BindingCall extends ChannelOwner {
|
||||
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("result", gson().toJsonTree(serializeArgument(result)));
|
||||
sendMessage("resolve", params, NO_TIMEOUT);
|
||||
sendMessage("resolve", params);
|
||||
} catch (RuntimeException exception) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("error", gson().toJsonTree(serializeError(exception)));
|
||||
sendMessage("reject", params, NO_TIMEOUT);
|
||||
sendMessage("reject", params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,23 +34,24 @@ import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.*;
|
||||
import static com.microsoft.playwright.impl.Serialization.addHarUrlFilter;
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
import static com.microsoft.playwright.impl.Utils.*;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.nio.file.Files.readAllBytes;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
protected BrowserImpl browser;
|
||||
private final BrowserImpl browser;
|
||||
private final TracingImpl tracing;
|
||||
private final DebuggerImpl debugger;
|
||||
private final APIRequestContextImpl request;
|
||||
private final ClockImpl clock;
|
||||
final List<PageImpl> pages = new ArrayList<>();
|
||||
final List<PageImpl> backgroundPages = new ArrayList<>();
|
||||
|
||||
final Router routes = new Router();
|
||||
final WebSocketRouter webSocketRoutes = new WebSocketRouter();
|
||||
private boolean closingOrClosed;
|
||||
private boolean closeWasCalled;
|
||||
private final WaitableEvent<EventType, ?> closePromise;
|
||||
final Map<String, BindingCallback> bindings = new HashMap<>();
|
||||
PageImpl ownerPage;
|
||||
@@ -68,18 +69,26 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
}
|
||||
private final ListenerCollection<EventType> listeners = new ListenerCollection<>(eventSubscriptions(), this);
|
||||
final TimeoutSettings timeoutSettings = new TimeoutSettings();
|
||||
Path videosDir;
|
||||
URL baseUrl;
|
||||
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 {
|
||||
BACKGROUNDPAGE,
|
||||
CLOSE,
|
||||
CONSOLE,
|
||||
DIALOG,
|
||||
DOWNLOAD,
|
||||
FRAMEATTACHED,
|
||||
FRAMEDETACHED,
|
||||
FRAMENAVIGATED,
|
||||
PAGE,
|
||||
PAGECLOSE,
|
||||
PAGELOAD,
|
||||
WEBERROR,
|
||||
REQUEST,
|
||||
REQUESTFAILED,
|
||||
@@ -89,31 +98,29 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
|
||||
BrowserContextImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
if (parent instanceof BrowserImpl) {
|
||||
browser = (BrowserImpl) parent;
|
||||
} else {
|
||||
browser = null;
|
||||
}
|
||||
tracing = connection.getExistingObject(initializer.getAsJsonObject("tracing").get("guid").getAsString());
|
||||
debugger = connection.getExistingObject(initializer.getAsJsonObject("debugger").get("guid").getAsString());
|
||||
request = connection.getExistingObject(initializer.getAsJsonObject("requestContext").get("guid").getAsString());
|
||||
request.timeoutSettings = timeoutSettings;
|
||||
clock = new ClockImpl(this);
|
||||
closePromise = new WaitableEvent<>(listeners, EventType.CLOSE);
|
||||
}
|
||||
|
||||
Path videosDir() {
|
||||
JsonObject recordVideo = initializer.getAsJsonObject("options").getAsJsonObject("recordVideo");
|
||||
if (recordVideo == null) {
|
||||
return null;
|
||||
void setRecordHar(Path path, HarContentPolicy policy) {
|
||||
if (path != null) {
|
||||
harRecorders.put("", new HarRecorder(path, policy));
|
||||
}
|
||||
return Paths.get(recordVideo.get("dir").getAsString());
|
||||
}
|
||||
|
||||
URL baseUrl() {
|
||||
JsonElement url = initializer.getAsJsonObject("options").get("baseURL");
|
||||
if (url != null) {
|
||||
try {
|
||||
return new URL(url.getAsString());
|
||||
} catch (MalformedURLException e) {
|
||||
}
|
||||
void setBaseUrl(String spec) {
|
||||
try {
|
||||
this.baseUrl = new URL(spec);
|
||||
} catch (MalformedURLException e) {
|
||||
this.baseUrl = null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String effectiveCloseReason() {
|
||||
@@ -128,24 +135,12 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
|
||||
@Override
|
||||
public void onBackgroundPage(Consumer<Page> handler) {
|
||||
listeners.add(EventType.BACKGROUNDPAGE, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
listeners.remove(EventType.BACKGROUNDPAGE, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -188,76 +183,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);
|
||||
@@ -337,7 +262,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
public CDPSession newCDPSession(Page page) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("page", ((PageImpl) page).toProtocolRef());
|
||||
JsonObject result = sendMessage("newCDPSession", params, NO_TIMEOUT).getAsJsonObject();
|
||||
JsonObject result = sendMessage("newCDPSession", params).getAsJsonObject();
|
||||
return connection.getExistingObject(result.getAsJsonObject("session").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@@ -345,29 +270,13 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
public CDPSession newCDPSession(Frame frame) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("frame", ((FrameImpl) frame).toProtocolRef());
|
||||
JsonObject result = sendMessage("newCDPSession", params, NO_TIMEOUT).getAsJsonObject();
|
||||
JsonObject result = sendMessage("newCDPSession", params).getAsJsonObject();
|
||||
return connection.getExistingObject(result.getAsJsonObject("session").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return closingOrClosed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(CloseOptions options) {
|
||||
if (!closingOrClosed) {
|
||||
closingOrClosed = true;
|
||||
if (options == null) {
|
||||
options = new CloseOptions();
|
||||
}
|
||||
closeReason = options.reason;
|
||||
request.dispose(convertType(options, APIRequestContext.DisposeOptions.class));
|
||||
tracing.exportAllHars();
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("close", params, NO_TIMEOUT);
|
||||
}
|
||||
runUntil(() -> {}, closePromise);
|
||||
withLogging("BrowserContext.close", () -> closeImpl(options));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -375,35 +284,76 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
return cookies(url == null ? new ArrayList<>() : Collections.singletonList(url));
|
||||
}
|
||||
|
||||
private void closeImpl(CloseOptions options) {
|
||||
if (!closeWasCalled) {
|
||||
closeWasCalled = true;
|
||||
if (options == null) {
|
||||
options = new CloseOptions();
|
||||
}
|
||||
closeReason = options.reason;
|
||||
request.dispose(convertType(options, APIRequestContext.DisposeOptions.class));
|
||||
for (Map.Entry<String, HarRecorder> entry : harRecorders.entrySet()) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("harId", entry.getKey());
|
||||
JsonObject json = sendMessage("harExport", params).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);
|
||||
} else {
|
||||
artifact.saveAs(harParams.path);
|
||||
}
|
||||
artifact.delete();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("close", params);
|
||||
}
|
||||
runUntil(() -> {}, closePromise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCookies(List<Cookie> cookies) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("cookies", gson().toJsonTree(cookies));
|
||||
sendMessage("addCookies", params, NO_TIMEOUT);
|
||||
withLogging("BrowserContext.addCookies", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("cookies", gson().toJsonTree(cookies));
|
||||
sendMessage("addCookies", params);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoCloseable addInitScript(String script) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("source", script);
|
||||
JsonObject result = sendMessage("addInitScript", params, NO_TIMEOUT).getAsJsonObject();
|
||||
return connection.getExistingObject(result.getAsJsonObject("disposable").get("guid").getAsString());
|
||||
public void addInitScript(String script) {
|
||||
withLogging("BrowserContext.addInitScript", () -> addInitScriptImpl(script));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoCloseable addInitScript(Path path) {
|
||||
try {
|
||||
byte[] bytes = readAllBytes(path);
|
||||
return addInitScript(new String(bytes, UTF_8));
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to read script from file", e);
|
||||
}
|
||||
public void addInitScript(Path path) {
|
||||
withLogging("BrowserContext.addInitScript", () -> {
|
||||
try {
|
||||
byte[] bytes = readAllBytes(path);
|
||||
addInitScriptImpl(new String(bytes, UTF_8));
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to read script from file", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Page> backgroundPages() {
|
||||
return Collections.emptyList();
|
||||
return new ArrayList<>(backgroundPages);
|
||||
}
|
||||
|
||||
private void addInitScriptImpl(String script) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("source", script);
|
||||
sendMessage("addInitScript", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -413,6 +363,10 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
|
||||
@Override
|
||||
public void clearCookies(ClearCookiesOptions options) {
|
||||
withLogging("BrowserContext.clearCookies", () -> clearCookiesImpl(options));
|
||||
}
|
||||
|
||||
private void clearCookiesImpl(ClearCookiesOptions options) {
|
||||
if (options == null) {
|
||||
options = new ClearCookiesOptions();
|
||||
}
|
||||
@@ -420,7 +374,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
setStringOrRegex(params, "name", options.name);
|
||||
setStringOrRegex(params, "domain", options.domain);
|
||||
setStringOrRegex(params, "path", options.path);
|
||||
sendMessage("clearCookies", params, NO_TIMEOUT);
|
||||
sendMessage("clearCookies", params);
|
||||
}
|
||||
|
||||
private static void setStringOrRegex(JsonObject params, String name, Object value) {
|
||||
@@ -435,27 +389,31 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
|
||||
@Override
|
||||
public void clearPermissions() {
|
||||
sendMessage("clearPermissions");
|
||||
withLogging("BrowserContext.clearPermissions", () -> sendMessage("clearPermissions"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Cookie> cookies(List<String> urls) {
|
||||
return withLogging("BrowserContext.cookies", () -> cookiesImpl(urls));
|
||||
}
|
||||
|
||||
private List<Cookie> cookiesImpl(List<String> urls) {
|
||||
JsonObject params = new JsonObject();
|
||||
if (urls == null) {
|
||||
urls = new ArrayList<>();
|
||||
}
|
||||
params.add("urls", gson().toJsonTree(urls));
|
||||
JsonObject json = sendMessage("cookies", params, NO_TIMEOUT).getAsJsonObject();
|
||||
JsonObject json = sendMessage("cookies", params).getAsJsonObject();
|
||||
Cookie[] cookies = gson().fromJson(json.getAsJsonArray("cookies"), Cookie[].class);
|
||||
return asList(cookies);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoCloseable exposeBinding(String name, BindingCallback playwrightBinding) {
|
||||
return exposeBindingImpl(name, playwrightBinding);
|
||||
public void exposeBinding(String name, BindingCallback playwrightBinding, ExposeBindingOptions options) {
|
||||
withLogging("BrowserContext.exposeBinding", () -> exposeBindingImpl(name, playwrightBinding, options));
|
||||
}
|
||||
|
||||
private AutoCloseable exposeBindingImpl(String name, BindingCallback playwrightBinding) {
|
||||
private void exposeBindingImpl(String name, BindingCallback playwrightBinding, ExposeBindingOptions options) {
|
||||
if (bindings.containsKey(name)) {
|
||||
throw new PlaywrightException("Function \"" + name + "\" has been already registered");
|
||||
}
|
||||
@@ -468,17 +426,24 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("name", name);
|
||||
JsonObject result = sendMessage("exposeBinding", params, NO_TIMEOUT).getAsJsonObject();
|
||||
return connection.getExistingObject(result.getAsJsonObject("disposable").get("guid").getAsString());
|
||||
if (options != null && options.handle != null && options.handle) {
|
||||
params.addProperty("needsHandle", true);
|
||||
}
|
||||
sendMessage("exposeBinding", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoCloseable exposeFunction(String name, FunctionCallback playwrightFunction) {
|
||||
return exposeBindingImpl(name, (BindingCallback.Source source, Object... args) -> playwrightFunction.call(args));
|
||||
public void exposeFunction(String name, FunctionCallback playwrightFunction) {
|
||||
withLogging("BrowserContext.exposeFunction",
|
||||
() -> exposeBindingImpl(name, (BindingCallback.Source source, Object... args) -> playwrightFunction.call(args), null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantPermissions(List<String> permissions, GrantPermissionsOptions options) {
|
||||
withLogging("BrowserContext.grantPermissions", () -> grantPermissionsImpl(permissions, options));
|
||||
}
|
||||
|
||||
private void grantPermissionsImpl(List<String> permissions, GrantPermissionsOptions options) {
|
||||
if (options == null) {
|
||||
options = new GrantPermissionsOptions();
|
||||
}
|
||||
@@ -487,11 +452,15 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.add("permissions", gson().toJsonTree(permissions));
|
||||
sendMessage("grantPermissions", params, NO_TIMEOUT);
|
||||
sendMessage("grantPermissions", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageImpl newPage() {
|
||||
return withLogging("BrowserContext.newPage", () -> newPageImpl());
|
||||
}
|
||||
|
||||
private PageImpl newPageImpl() {
|
||||
if (ownerPage != null) {
|
||||
throw new PlaywrightException("Please use browser.newContext()");
|
||||
}
|
||||
@@ -510,21 +479,18 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoCloseable route(String url, Consumer<Route> handler, RouteOptions options) {
|
||||
route(UrlMatcher.forGlob(baseUrl(), url, this.connection.localUtils, false), handler, options);
|
||||
return new DisposableStub(() -> unroute(url, handler));
|
||||
public void route(String url, Consumer<Route> handler, RouteOptions options) {
|
||||
route(new UrlMatcher(baseUrl, url), handler, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoCloseable route(Pattern url, Consumer<Route> handler, RouteOptions options) {
|
||||
public void route(Pattern url, Consumer<Route> handler, RouteOptions options) {
|
||||
route(new UrlMatcher(url), handler, options);
|
||||
return new DisposableStub(() -> unroute(url, handler));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoCloseable route(Predicate<String> url, Consumer<Route> handler, RouteOptions options) {
|
||||
public void route(Predicate<String> url, Consumer<Route> handler, RouteOptions options) {
|
||||
route(new UrlMatcher(url), handler, options);
|
||||
return new DisposableStub(() -> unroute(url, handler));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -533,23 +499,25 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
options = new RouteFromHAROptions();
|
||||
}
|
||||
if (options.update != null && options.update) {
|
||||
recordIntoHar(null, har, options, null);
|
||||
recordIntoHar(null, har, options);
|
||||
return;
|
||||
}
|
||||
UrlMatcher matcher = UrlMatcher.forOneOf(baseUrl(), options.url, this.connection.localUtils, false);
|
||||
UrlMatcher matcher = UrlMatcher.forOneOf(baseUrl, options.url);
|
||||
HARRouter harRouter = new HARRouter(connection.localUtils, har, options.notFound);
|
||||
onClose(context -> harRouter.dispose());
|
||||
route(matcher, route -> harRouter.handle(route), null);
|
||||
}
|
||||
|
||||
private void route(UrlMatcher matcher, Consumer<Route> handler, RouteOptions options) {
|
||||
routes.add(matcher, handler, options == null ? null : options.times);
|
||||
updateInterceptionPatterns();
|
||||
withLogging("BrowserContext.route", () -> {
|
||||
routes.add(matcher, handler, options == null ? null : options.times);
|
||||
updateInterceptionPatterns();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void routeWebSocket(String url, Consumer<WebSocketRoute> handler) {
|
||||
routeWebSocketImpl(UrlMatcher.forGlob(baseUrl(), url, this.connection.localUtils, true), handler);
|
||||
routeWebSocketImpl(new UrlMatcher(baseUrl, url), handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -563,77 +531,108 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
}
|
||||
|
||||
private void routeWebSocketImpl(UrlMatcher matcher, Consumer<WebSocketRoute> handler) {
|
||||
webSocketRoutes.add(matcher, handler);
|
||||
updateWebSocketInterceptionPatterns();
|
||||
withLogging("BrowserContext.routeWebSocket", () -> {
|
||||
webSocketRoutes.add(matcher, handler);
|
||||
updateWebSocketInterceptionPatterns();
|
||||
});
|
||||
}
|
||||
|
||||
void recordIntoHar(PageImpl page, Path har, RouteFromHAROptions options, HarContentPolicy contentPolicy) {
|
||||
if (contentPolicy == null) {
|
||||
contentPolicy = Utils.convertType(options.updateContent, HarContentPolicy.class);
|
||||
void recordIntoHar(PageImpl page, Path har, RouteFromHAROptions options) {
|
||||
JsonObject params = new JsonObject();
|
||||
if (page != null) {
|
||||
params.add("page", page.toProtocolRef());
|
||||
}
|
||||
tracing.recordIntoHar(page, har, options.url, contentPolicy, options.updateMode, null);
|
||||
JsonObject jsonOptions = new JsonObject();
|
||||
jsonOptions.addProperty("path", har.toAbsolutePath().toString());
|
||||
jsonOptions.addProperty("content", options.updateContent == null ?
|
||||
HarContentPolicy.ATTACH.name().toLowerCase() :
|
||||
options.updateContent.name().toLowerCase());
|
||||
jsonOptions.addProperty("mode", options.updateMode == null ?
|
||||
HarMode.MINIMAL.name().toLowerCase() :
|
||||
options.updateMode.name().toLowerCase());
|
||||
addHarUrlFilter(jsonOptions, options.url);
|
||||
params.add("options", jsonOptions);
|
||||
JsonObject json = sendMessage("harStart", params).getAsJsonObject();
|
||||
String harId = json.get("harId").getAsString();
|
||||
harRecorders.put(harId, new HarRecorder(har, HarContentPolicy.ATTACH));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultNavigationTimeout(double timeout) {
|
||||
timeoutSettings.setDefaultNavigationTimeout(timeout);
|
||||
setDefaultNavigationTimeoutImpl(timeout);
|
||||
}
|
||||
|
||||
void setDefaultNavigationTimeoutImpl(Double timeout) {
|
||||
withLogging("BrowserContext.setDefaultNavigationTimeout", () -> {
|
||||
timeoutSettings.setDefaultNavigationTimeout(timeout);
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("timeout", timeout);
|
||||
sendMessage("setDefaultNavigationTimeoutNoReply", params);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultTimeout(double timeout) {
|
||||
timeoutSettings.setDefaultTimeout(timeout);
|
||||
setDefaultTimeoutImpl(timeout);
|
||||
}
|
||||
|
||||
void setDefaultTimeoutImpl(Double timeout) {
|
||||
withLogging("BrowserContext.setDefaultTimeout", () -> {
|
||||
timeoutSettings.setDefaultTimeout(timeout);
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("timeout", timeout);
|
||||
sendMessage("setDefaultTimeoutNoReply", params);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExtraHTTPHeaders(Map<String, String> headers) {
|
||||
JsonObject params = new JsonObject();
|
||||
JsonArray jsonHeaders = new JsonArray();
|
||||
for (Map.Entry<String, String> e : headers.entrySet()) {
|
||||
JsonObject header = new JsonObject();
|
||||
header.addProperty("name", e.getKey());
|
||||
header.addProperty("value", e.getValue());
|
||||
jsonHeaders.add(header);
|
||||
}
|
||||
params.add("headers", jsonHeaders);
|
||||
sendMessage("setExtraHTTPHeaders", params, NO_TIMEOUT);
|
||||
withLogging("BrowserContext.setExtraHTTPHeaders", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
JsonArray jsonHeaders = new JsonArray();
|
||||
for (Map.Entry<String, String> e : headers.entrySet()) {
|
||||
JsonObject header = new JsonObject();
|
||||
header.addProperty("name", e.getKey());
|
||||
header.addProperty("value", e.getValue());
|
||||
jsonHeaders.add(header);
|
||||
}
|
||||
params.add("headers", jsonHeaders);
|
||||
sendMessage("setExtraHTTPHeaders", params);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGeolocation(Geolocation geolocation) {
|
||||
JsonObject params = new JsonObject();
|
||||
if (geolocation != null) {
|
||||
params.add("geolocation", gson().toJsonTree(geolocation));
|
||||
}
|
||||
sendMessage("setGeolocation", params, NO_TIMEOUT);
|
||||
withLogging("BrowserContext.setGeolocation", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
if (geolocation != null) {
|
||||
params.add("geolocation", gson().toJsonTree(geolocation));
|
||||
}
|
||||
sendMessage("setGeolocation", params);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOffline(boolean offline) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("offline", offline);
|
||||
sendMessage("setOffline", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStorageState(Path storageState) {
|
||||
try {
|
||||
String state = new String(readAllBytes(storageState), UTF_8);
|
||||
withLogging("BrowserContext.setOffline", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("storageState", state);
|
||||
sendMessage("setStorageState", params, NO_TIMEOUT);
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to read storage state from file", e);
|
||||
}
|
||||
params.addProperty("offline", offline);
|
||||
sendMessage("setOffline", params);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String storageState(StorageStateOptions options) {
|
||||
return withLogging("BrowserContext.storageState", () -> storageStateImpl(options));
|
||||
}
|
||||
|
||||
private String storageStateImpl(StorageStateOptions options) {
|
||||
if (options == null) {
|
||||
options = new StorageStateOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.remove("path");
|
||||
JsonElement json = sendMessage("storageState", params, NO_TIMEOUT);
|
||||
JsonElement json = sendMessage("storageState", params);
|
||||
|
||||
String storageState = json.toString();
|
||||
if (options.path != null) {
|
||||
@@ -642,11 +641,6 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
return storageState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggerImpl debugger() {
|
||||
return debugger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TracingImpl tracing() {
|
||||
return tracing;
|
||||
@@ -654,13 +648,15 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
|
||||
@Override
|
||||
public void unrouteAll() {
|
||||
routes.removeAll();
|
||||
updateInterceptionPatterns();
|
||||
withLogging("BrowserContext.unrouteAll", () -> {
|
||||
routes.removeAll();
|
||||
updateInterceptionPatterns();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unroute(String url, Consumer<Route> handler) {
|
||||
unroute(UrlMatcher.forGlob(this.baseUrl(), url, this.connection.localUtils, false), handler);
|
||||
unroute(new UrlMatcher(this.baseUrl, url), handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -706,16 +702,18 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
}
|
||||
|
||||
private void unroute(UrlMatcher matcher, Consumer<Route> handler) {
|
||||
routes.remove(matcher, handler);
|
||||
updateInterceptionPatterns();
|
||||
withLogging("BrowserContext.unroute", () -> {
|
||||
routes.remove(matcher, handler);
|
||||
updateInterceptionPatterns();
|
||||
});
|
||||
}
|
||||
|
||||
private void updateInterceptionPatterns() {
|
||||
sendMessage("setNetworkInterceptionPatterns", routes.interceptionPatterns(), NO_TIMEOUT);
|
||||
sendMessage("setNetworkInterceptionPatterns", routes.interceptionPatterns());
|
||||
}
|
||||
|
||||
private void updateWebSocketInterceptionPatterns() {
|
||||
sendMessage("setWebSocketInterceptionPatterns", webSocketRoutes.interceptionPatterns(), NO_TIMEOUT);
|
||||
sendMessage("setWebSocketInterceptionPatterns", webSocketRoutes.interceptionPatterns());
|
||||
}
|
||||
|
||||
void handleRoute(RouteImpl route) {
|
||||
@@ -783,6 +781,10 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
if (page.opener() != null && !page.opener().isClosed()) {
|
||||
page.opener().notifyPopup(page);
|
||||
}
|
||||
} else if ("backgroundPage".equals(event)) {
|
||||
PageImpl page = connection.getExistingObject(params.getAsJsonObject("page").get("guid").getAsString());
|
||||
backgroundPages.add(page);
|
||||
listeners.notify(EventType.BACKGROUNDPAGE, page);
|
||||
} else if ("bindingCall".equals(event)) {
|
||||
BindingCall bindingCall = connection.getExistingObject(params.getAsJsonObject("binding").get("guid").getAsString());
|
||||
BindingCallback binding = bindings.get(bindingCall.name());
|
||||
@@ -790,19 +792,9 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
bindingCall.call(binding);
|
||||
}
|
||||
} else if ("console".equals(event)) {
|
||||
PageImpl page = null;
|
||||
if (params.has("page")) {
|
||||
page = connection.getExistingObject(params.getAsJsonObject("page").get("guid").getAsString());
|
||||
}
|
||||
WorkerImpl worker = null;
|
||||
if (params.has("worker")) {
|
||||
worker = connection.getExistingObject(params.getAsJsonObject("worker").get("guid").getAsString());
|
||||
}
|
||||
ConsoleMessageImpl message = new ConsoleMessageImpl(connection, params, page, worker);
|
||||
ConsoleMessageImpl message = new ConsoleMessageImpl(connection, params);
|
||||
listeners.notify(BrowserContextImpl.EventType.CONSOLE, message);
|
||||
if (worker != null) {
|
||||
worker.listeners.notify(WorkerImpl.EventType.CONSOLE, message);
|
||||
}
|
||||
PageImpl page = message.page();
|
||||
if (page != null) {
|
||||
page.listeners.notify(PageImpl.EventType.CONSOLE, message);
|
||||
}
|
||||
@@ -843,25 +835,28 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
}
|
||||
} else if ("response".equals(event)) {
|
||||
String guid = params.getAsJsonObject("response").get("guid").getAsString();
|
||||
ResponseImpl response = connection.getExistingObject(guid);
|
||||
Response response = connection.getExistingObject(guid);
|
||||
listeners.notify(EventType.RESPONSE, response);
|
||||
if (params.has("page")) {
|
||||
PageImpl page = connection.getExistingObject(params.getAsJsonObject("page").get("guid").getAsString());
|
||||
page.listeners.notify(PageImpl.EventType.RESPONSE, response);
|
||||
}
|
||||
} else if ("pageError".equals(event)) {
|
||||
String errorStr = parseError(params.getAsJsonObject("error"));
|
||||
SerializedError error = gson().fromJson(params.getAsJsonObject("error"), SerializedError.class);
|
||||
String errorStr = "";
|
||||
if (error.error != null) {
|
||||
errorStr = error.error.name + ": " + error.error.message;
|
||||
if (error.error.stack != null && !error.error.stack.isEmpty()) {
|
||||
errorStr += "\n" + error.error.stack;
|
||||
}
|
||||
}
|
||||
PageImpl page;
|
||||
try {
|
||||
page = connection.getExistingObject(params.getAsJsonObject("page").get("guid").getAsString());
|
||||
} 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);
|
||||
}
|
||||
@@ -871,10 +866,8 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
}
|
||||
|
||||
void didClose() {
|
||||
closingOrClosed = true;
|
||||
if (browser != null) {
|
||||
browser.contexts.remove(this);
|
||||
browser.browserType.playwright.selectors.contextsForSelectors.remove(this);
|
||||
}
|
||||
listeners.notify(EventType.CLOSE, this);
|
||||
}
|
||||
@@ -883,49 +876,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("name", name);
|
||||
params.addProperty("lastModifiedMs", lastModifiedMs);
|
||||
JsonObject json = sendMessage("createTempFile", params, NO_TIMEOUT).getAsJsonObject();
|
||||
JsonObject json = sendMessage("createTempFile", params).getAsJsonObject();
|
||||
return connection.getExistingObject(json.getAsJsonObject("writableStream").get("guid").getAsString());
|
||||
}
|
||||
|
||||
protected void initializeHarFromOptions(Browser.NewContextOptions options) {
|
||||
if (options.recordHarPath == null) {
|
||||
if (options.recordHarOmitContent != null) {
|
||||
throw new PlaywrightException("recordHarOmitContent is set but recordHarPath is null");
|
||||
}
|
||||
if (options.recordHarUrlFilter != null) {
|
||||
throw new PlaywrightException("recordHarUrlFilter is set but recordHarPath is null");
|
||||
}
|
||||
if (options.recordHarMode != null) {
|
||||
throw new PlaywrightException("recordHarMode is set but recordHarPath is null");
|
||||
}
|
||||
if (options.recordHarContent != null) {
|
||||
throw new PlaywrightException("recordHarContent is set but recordHarPath is null");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
HarContentPolicy contentPolicy = options.recordHarContent;
|
||||
if (contentPolicy == null && options.recordHarOmitContent != null && options.recordHarOmitContent == true) {
|
||||
contentPolicy = HarContentPolicy.OMIT;
|
||||
}
|
||||
if (contentPolicy == null) {
|
||||
contentPolicy = options.recordHarPath.endsWith(".zip") ? HarContentPolicy.ATTACH : HarContentPolicy.EMBED;
|
||||
}
|
||||
RouteFromHAROptions routeFromHAROptions = new RouteFromHAROptions();
|
||||
|
||||
if (options.recordHarUrlFilter instanceof String) {
|
||||
routeFromHAROptions.setUrl((String) options.recordHarUrlFilter);
|
||||
} else if (options.recordHarUrlFilter instanceof Pattern) {
|
||||
routeFromHAROptions.setUrl((Pattern) options.recordHarUrlFilter);
|
||||
}
|
||||
|
||||
if (options.recordHarMode != null) {
|
||||
routeFromHAROptions.updateMode = options.recordHarMode;
|
||||
} else {
|
||||
routeFromHAROptions.updateMode = HarMode.FULL;
|
||||
}
|
||||
routeFromHAROptions.url = options.recordHarUrlFilter;
|
||||
|
||||
recordIntoHar(null, options.recordHarPath, routeFromHAROptions, contentPolicy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.*;
|
||||
import com.microsoft.playwright.options.BindResult;
|
||||
import com.microsoft.playwright.options.HarContentPolicy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -29,8 +29,10 @@ import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.addHarUrlFilter;
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
import static com.microsoft.playwright.impl.Utils.*;
|
||||
import static com.microsoft.playwright.impl.Utils.convertType;
|
||||
|
||||
class BrowserImpl extends ChannelOwner implements Browser {
|
||||
final Set<BrowserContextImpl> contexts = new HashSet<>();
|
||||
@@ -43,7 +45,6 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
String closeReason;
|
||||
|
||||
enum EventType {
|
||||
CONTEXT,
|
||||
DISCONNECTED,
|
||||
}
|
||||
|
||||
@@ -51,16 +52,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);
|
||||
@@ -78,6 +69,10 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
|
||||
@Override
|
||||
public void close(CloseOptions options) {
|
||||
withLogging("Browser.close", () -> closeImpl(options));
|
||||
}
|
||||
|
||||
private void closeImpl(CloseOptions options) {
|
||||
if (options == null) {
|
||||
options = new CloseOptions();
|
||||
}
|
||||
@@ -122,20 +117,16 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
|
||||
@Override
|
||||
public BrowserContextImpl newContext(NewContextOptions options) {
|
||||
return withLogging("Browser.newContext", () -> newContextImpl(options));
|
||||
}
|
||||
|
||||
private BrowserContextImpl newContextImpl(NewContextOptions options) {
|
||||
if (options == null) {
|
||||
options = new NewContextOptions();
|
||||
} else {
|
||||
// Make a copy so that we can nullify some fields below.
|
||||
options = convertType(options, NewContextOptions.class);
|
||||
}
|
||||
|
||||
NewContextOptions harOptions = Utils.clone(options);
|
||||
options.recordHarContent = null;
|
||||
options.recordHarMode = null;
|
||||
options.recordHarPath = null;
|
||||
options.recordHarOmitContent = null;
|
||||
options.recordHarUrlFilter = null;
|
||||
|
||||
if (options.storageStatePath != null) {
|
||||
try {
|
||||
byte[] bytes = Files.readAllBytes(options.storageStatePath);
|
||||
@@ -150,10 +141,51 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
storageState = new Gson().fromJson(options.storageState, JsonObject.class);
|
||||
options.storageState = null;
|
||||
}
|
||||
JsonObject recordHar = null;
|
||||
Path recordHarPath = options.recordHarPath;
|
||||
HarContentPolicy harContentPolicy = null;
|
||||
if (options.recordHarPath != null) {
|
||||
recordHar = new JsonObject();
|
||||
recordHar.addProperty("path", options.recordHarPath.toString());
|
||||
if (options.recordHarContent != null) {
|
||||
harContentPolicy = options.recordHarContent;
|
||||
} else if (options.recordHarOmitContent != null && options.recordHarOmitContent) {
|
||||
harContentPolicy = HarContentPolicy.OMIT;
|
||||
}
|
||||
if (harContentPolicy != null) {
|
||||
recordHar.addProperty("content", harContentPolicy.name().toLowerCase());
|
||||
}
|
||||
if (options.recordHarMode != null) {
|
||||
recordHar.addProperty("mode", options.recordHarMode.name().toLowerCase());
|
||||
}
|
||||
addHarUrlFilter(recordHar, options.recordHarUrlFilter);
|
||||
options.recordHarPath = null;
|
||||
options.recordHarMode = null;
|
||||
options.recordHarOmitContent = null;
|
||||
options.recordHarContent = null;
|
||||
options.recordHarUrlFilter = null;
|
||||
} else {
|
||||
if (options.recordHarOmitContent != null) {
|
||||
throw new PlaywrightException("recordHarOmitContent is set but recordHarPath is null");
|
||||
}
|
||||
if (options.recordHarUrlFilter != null) {
|
||||
throw new PlaywrightException("recordHarUrlFilter is set but recordHarPath is null");
|
||||
}
|
||||
if (options.recordHarMode != null) {
|
||||
throw new PlaywrightException("recordHarMode is set but recordHarPath is null");
|
||||
}
|
||||
if (options.recordHarContent != null) {
|
||||
throw new PlaywrightException("recordHarContent is set but recordHarPath is null");
|
||||
}
|
||||
}
|
||||
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
if (storageState != null) {
|
||||
params.add("storageState", storageState);
|
||||
}
|
||||
if (recordHar != null) {
|
||||
params.add("recordHar", recordHar);
|
||||
}
|
||||
if (options.recordVideoDir != null) {
|
||||
JsonObject recordVideo = new JsonObject();
|
||||
recordVideo.addProperty("dir", options.recordVideoDir.toAbsolutePath().toString());
|
||||
@@ -181,21 +213,31 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
if (options.acceptDownloads != null) {
|
||||
params.addProperty("acceptDownloads", options.acceptDownloads ? "accept" : "deny");
|
||||
}
|
||||
params.add("selectorEngines", gson().toJsonTree(browserType.playwright.selectors.selectorEngines));
|
||||
params.addProperty("testIdAttributeName", browserType.playwright.selectors.testIdAttributeName);
|
||||
JsonElement result = sendMessage("newContext", params, NO_TIMEOUT);
|
||||
JsonElement result = sendMessage("newContext", params);
|
||||
BrowserContextImpl context = connection.getExistingObject(result.getAsJsonObject().getAsJsonObject("context").get("guid").getAsString());
|
||||
context.initializeHarFromOptions(harOptions);
|
||||
context.videosDir = options.recordVideoDir;
|
||||
if (options.baseURL != null) {
|
||||
context.setBaseUrl(options.baseURL);
|
||||
}
|
||||
context.setRecordHar(recordHarPath, harContentPolicy);
|
||||
if (launchOptions != null) {
|
||||
context.tracing().setTracesDir(launchOptions.tracesDir);
|
||||
}
|
||||
contexts.add(context);
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page newPage(NewPageOptions options) {
|
||||
return withTitle("Create Page", () -> newPageImpl(options));
|
||||
return withLogging("Browser.newPage", () -> newPageImpl(options));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startTracing(Page page, StartTracingOptions options) {
|
||||
withLogging("Browser.startTracing", () -> startTracingImpl(page, options));
|
||||
}
|
||||
|
||||
private void startTracingImpl(Page page, StartTracingOptions options) {
|
||||
if (options == null) {
|
||||
options = new StartTracingOptions();
|
||||
}
|
||||
@@ -204,37 +246,15 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
if (page != null) {
|
||||
params.add("page", ((PageImpl) page).toProtocolRef());
|
||||
}
|
||||
sendMessage("startTracing", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindResult bind(String title, BindOptions options) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("title", title);
|
||||
if (options != null) {
|
||||
if (options.host != null) {
|
||||
params.addProperty("host", options.host);
|
||||
}
|
||||
if (options.port != null) {
|
||||
params.addProperty("port", options.port);
|
||||
}
|
||||
if (options.workspaceDir != null) {
|
||||
params.addProperty("workspaceDir", options.workspaceDir);
|
||||
}
|
||||
}
|
||||
JsonObject result = sendMessage("startServer", params, NO_TIMEOUT).getAsJsonObject();
|
||||
BindResult bindResult = new BindResult();
|
||||
bindResult.endpoint = result.get("endpoint").getAsString();
|
||||
return bindResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbind() {
|
||||
sendMessage("stopServer", new JsonObject(), NO_TIMEOUT);
|
||||
sendMessage("startTracing", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] stopTracing() {
|
||||
return withLogging("Browser.stopTracing", () -> stopTracingImpl());
|
||||
}
|
||||
|
||||
private byte[] stopTracingImpl() {
|
||||
JsonObject json = sendMessage("stopTracing").getAsJsonObject();
|
||||
ArtifactImpl artifact = connection.getExistingObject(json.getAsJsonObject().getAsJsonObject("artifact").get("guid").getAsString());
|
||||
byte[] data = artifact.readAllBytes();
|
||||
@@ -275,47 +295,18 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
|
||||
@Override
|
||||
void handleEvent(String event, JsonObject parameters) {
|
||||
switch (event) {
|
||||
case "context":
|
||||
didCreateContext(connection.getExistingObject(parameters.getAsJsonObject("context").get("guid").getAsString()));
|
||||
break;
|
||||
case "close":
|
||||
didClose();
|
||||
break;
|
||||
if ("close".equals(event)) {
|
||||
didClose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CDPSession newBrowserCDPSession() {
|
||||
JsonObject params = new JsonObject();
|
||||
JsonObject result = sendMessage("newBrowserCDPSession", params, NO_TIMEOUT).getAsJsonObject();
|
||||
JsonObject result = sendMessage("newBrowserCDPSession", params).getAsJsonObject();
|
||||
return connection.getExistingObject(result.getAsJsonObject("session").get("guid").getAsString());
|
||||
}
|
||||
|
||||
protected void connectToBrowserType(BrowserTypeImpl browserType, Path tracesDir){
|
||||
// Note: when using connect(), `browserType` is different from `this.parent`.
|
||||
// This is why browser type is not wired up in the constructor, and instead this separate method is called later on.
|
||||
this.browserType = browserType;
|
||||
this.tracePath = tracesDir;
|
||||
|
||||
for (BrowserContextImpl context : contexts) {
|
||||
context.tracing().setTracesDir(tracesDir);
|
||||
browserType.playwright.selectors.contextsForSelectors.add(context);
|
||||
}
|
||||
}
|
||||
|
||||
private void didCreateContext(BrowserContextImpl context) {
|
||||
context.browser = this;
|
||||
contexts.add(context);
|
||||
// Note: when connecting to a browser, initial contexts arrive before `_browserType` is set,
|
||||
// and will be configured later in `ConnectToBrowserType`.
|
||||
if (browserType != null) {
|
||||
context.tracing().setTracesDir(tracePath);
|
||||
browserType.playwright.selectors.contextsForSelectors.add(context);
|
||||
}
|
||||
listeners.notify(EventType.CONTEXT, context);
|
||||
}
|
||||
|
||||
private void didClose() {
|
||||
isConnected = false;
|
||||
listeners.notify(EventType.DISCONNECTED, this);
|
||||
|
||||
@@ -22,30 +22,34 @@ import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Browser;
|
||||
import com.microsoft.playwright.BrowserType;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import com.microsoft.playwright.options.HarContentPolicy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.addHarUrlFilter;
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
import static com.microsoft.playwright.impl.Utils.addToProtocol;
|
||||
import static com.microsoft.playwright.impl.Utils.convertType;
|
||||
|
||||
class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
protected PlaywrightImpl playwright;
|
||||
|
||||
BrowserTypeImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BrowserImpl launch(LaunchOptions options) {
|
||||
return withLogging("BrowserType.launch", () -> launchImpl(options));
|
||||
}
|
||||
|
||||
private BrowserImpl launchImpl(LaunchOptions options) {
|
||||
if (options == null) {
|
||||
options = new LaunchOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
JsonElement result = sendMessage("launch", params, TimeoutSettings.launchTimeout(options.timeout));
|
||||
JsonElement result = sendMessage("launch", params);
|
||||
BrowserImpl browser = connection.getExistingObject(result.getAsJsonObject().getAsJsonObject("browser").get("guid").getAsString());
|
||||
browser.browserType = this;
|
||||
browser.launchOptions = options;
|
||||
@@ -54,12 +58,16 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
|
||||
@Override
|
||||
public Browser connect(String wsEndpoint, ConnectOptions options) {
|
||||
return withLogging("BrowserType.connect", () -> connectImpl(wsEndpoint, options));
|
||||
}
|
||||
|
||||
private Browser connectImpl(String wsEndpoint, ConnectOptions options) {
|
||||
if (options == null) {
|
||||
options = new ConnectOptions();
|
||||
}
|
||||
// We don't use gson() here as the headers map should be serialized to a json object.
|
||||
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("endpoint", wsEndpoint);
|
||||
params.addProperty("wsEndpoint", wsEndpoint);
|
||||
|
||||
if (!params.has("headers")) {
|
||||
params.add("headers", new JsonObject());
|
||||
@@ -76,12 +84,7 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
headers.addProperty("x-playwright-browser", name());
|
||||
}
|
||||
|
||||
Double timeout = options.timeout;
|
||||
if (timeout == null) {
|
||||
timeout = 0.0;
|
||||
}
|
||||
|
||||
JsonObject json = connection.localUtils().sendMessage("connect", params, timeout).getAsJsonObject();
|
||||
JsonObject json = connection.localUtils().sendMessage("connect", params).getAsJsonObject();
|
||||
JsonPipe pipe = connection.getExistingObject(json.getAsJsonObject("pipe").get("guid").getAsString());
|
||||
Connection connection = new Connection(pipe, this.connection.env, this.connection.localUtils);
|
||||
PlaywrightImpl playwright = connection.initializePlaywright();
|
||||
@@ -93,13 +96,14 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
}
|
||||
throw new PlaywrightException("Malformed endpoint. Did you use launchServer method?");
|
||||
}
|
||||
playwright.selectors = this.playwright.selectors;
|
||||
playwright.initSharedSelectors(this.connection.getExistingObject("Playwright"));
|
||||
BrowserImpl browser = connection.getExistingObject(playwright.initializer.getAsJsonObject("preLaunchedBrowser").get("guid").getAsString());
|
||||
browser.isConnectedOverWebSocket = true;
|
||||
browser.connectToBrowserType(this, null);
|
||||
browser.browserType = this;
|
||||
Consumer<JsonPipe> connectionCloseListener = t -> browser.notifyRemoteClosed();
|
||||
pipe.onClose(connectionCloseListener);
|
||||
browser.onDisconnected(b -> {
|
||||
playwright.unregisterSelectors();
|
||||
pipe.offClose(connectionCloseListener);
|
||||
try {
|
||||
connection.close();
|
||||
@@ -115,16 +119,25 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
if (!"chromium".equals(name())) {
|
||||
throw new PlaywrightException("Connecting over CDP is only supported in Chromium.");
|
||||
}
|
||||
return withLogging("BrowserType.connectOverCDP", () -> connectOverCDPImpl(endpointURL, options));
|
||||
}
|
||||
|
||||
private Browser connectOverCDPImpl(String endpointURL, ConnectOverCDPOptions options) {
|
||||
if (options == null) {
|
||||
options = new ConnectOverCDPOptions();
|
||||
}
|
||||
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("endpointURL", endpointURL);
|
||||
JsonObject json = sendMessage("connectOverCDP", params, TimeoutSettings.launchTimeout(options.timeout)).getAsJsonObject();
|
||||
JsonObject json = sendMessage("connectOverCDP", params).getAsJsonObject();
|
||||
|
||||
BrowserImpl browser = connection.getExistingObject(json.getAsJsonObject("browser").get("guid").getAsString());
|
||||
browser.connectToBrowserType(this, null);
|
||||
browser.browserType = this;
|
||||
if (json.has("defaultContext")) {
|
||||
String contextId = json.getAsJsonObject("defaultContext").get("guid").getAsString();
|
||||
BrowserContextImpl defaultContext = connection.getExistingObject(contextId);
|
||||
browser.contexts.add(defaultContext);
|
||||
}
|
||||
return browser;
|
||||
}
|
||||
|
||||
@@ -134,19 +147,54 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
|
||||
@Override
|
||||
public BrowserContextImpl launchPersistentContext(Path userDataDir, LaunchPersistentContextOptions options) {
|
||||
return withLogging("BrowserType.launchPersistentContext",
|
||||
() -> launchPersistentContextImpl(userDataDir, options));
|
||||
}
|
||||
|
||||
private BrowserContextImpl launchPersistentContextImpl(Path userDataDir, LaunchPersistentContextOptions options) {
|
||||
if (options == null) {
|
||||
options = new LaunchPersistentContextOptions();
|
||||
} else {
|
||||
// Make a copy so that we can nullify some fields below.
|
||||
options = convertType(options, LaunchPersistentContextOptions.class);
|
||||
}
|
||||
|
||||
Browser.NewContextOptions harOptions = convertType(options, Browser.NewContextOptions.class);
|
||||
options.recordHarContent = null;
|
||||
options.recordHarMode = null;
|
||||
options.recordHarPath = null;
|
||||
options.recordHarOmitContent = null;
|
||||
options.recordHarUrlFilter = null;
|
||||
JsonObject recordHar = null;
|
||||
Path recordHarPath = options.recordHarPath;
|
||||
HarContentPolicy harContentPolicy = null;
|
||||
if (options.recordHarPath != null) {
|
||||
recordHar = new JsonObject();
|
||||
recordHar.addProperty("path", options.recordHarPath.toString());
|
||||
if (options.recordHarContent != null) {
|
||||
harContentPolicy = options.recordHarContent;
|
||||
} else if (options.recordHarOmitContent != null && options.recordHarOmitContent) {
|
||||
harContentPolicy = HarContentPolicy.OMIT;
|
||||
}
|
||||
if (harContentPolicy != null) {
|
||||
recordHar.addProperty("content", harContentPolicy.name().toLowerCase());
|
||||
}
|
||||
if (options.recordHarMode != null) {
|
||||
recordHar.addProperty("mode", options.recordHarMode.toString().toLowerCase());
|
||||
}
|
||||
addHarUrlFilter(recordHar, options.recordHarUrlFilter);
|
||||
options.recordHarPath = null;
|
||||
options.recordHarMode = null;
|
||||
options.recordHarOmitContent = null;
|
||||
options.recordHarContent = null;
|
||||
options.recordHarUrlFilter = null;
|
||||
} else {
|
||||
if (options.recordHarOmitContent != null) {
|
||||
throw new PlaywrightException("recordHarOmitContent is set but recordHarPath is null");
|
||||
}
|
||||
if (options.recordHarUrlFilter != null) {
|
||||
throw new PlaywrightException("recordHarUrlFilter is set but recordHarPath is null");
|
||||
}
|
||||
if (options.recordHarMode != null) {
|
||||
throw new PlaywrightException("recordHarMode is set but recordHarPath is null");
|
||||
}
|
||||
if (options.recordHarContent != null) {
|
||||
throw new PlaywrightException("recordHarContent is set but recordHarPath is null");
|
||||
}
|
||||
}
|
||||
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
if (!userDataDir.isAbsolute() && !userDataDir.toString().isEmpty()) {
|
||||
@@ -154,6 +202,9 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
userDataDir = cwd.resolve(userDataDir);
|
||||
}
|
||||
params.addProperty("userDataDir", userDataDir.toString());
|
||||
if (recordHar != null) {
|
||||
params.add("recordHar", recordHar);
|
||||
}
|
||||
if (options.recordVideoDir != null) {
|
||||
JsonObject recordVideo = new JsonObject();
|
||||
recordVideo.addProperty("dir", options.recordVideoDir.toAbsolutePath().toString());
|
||||
@@ -181,13 +232,13 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
if (options.acceptDownloads != null) {
|
||||
params.addProperty("acceptDownloads", options.acceptDownloads ? "accept" : "deny");
|
||||
}
|
||||
params.add("selectorEngines", gson().toJsonTree(playwright.selectors.selectorEngines));
|
||||
params.addProperty("testIdAttributeName", playwright.selectors.testIdAttributeName);
|
||||
JsonObject json = sendMessage("launchPersistentContext", params, TimeoutSettings.launchTimeout(options.timeout)).getAsJsonObject();
|
||||
BrowserImpl browser = connection.getExistingObject(json.getAsJsonObject("browser").get("guid").getAsString());
|
||||
browser.connectToBrowserType(this, options.tracesDir);
|
||||
JsonObject json = sendMessage("launchPersistentContext", params).getAsJsonObject();
|
||||
BrowserContextImpl context = connection.getExistingObject(json.getAsJsonObject("context").get("guid").getAsString());
|
||||
context.initializeHarFromOptions(harOptions);
|
||||
context.videosDir = options.recordVideoDir;
|
||||
if (options.baseURL != null) {
|
||||
context.setBaseUrl(options.baseURL);
|
||||
}
|
||||
context.setRecordHar(recordHarPath, harContentPolicy);
|
||||
context.tracing().setTracesDir(options.tracesDir);
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -25,11 +25,6 @@ import java.util.function.Consumer;
|
||||
|
||||
public class CDPSessionImpl extends ChannelOwner implements CDPSession {
|
||||
private final ListenerCollection<String> listeners = new ListenerCollection<>(new HashMap<>(), this);
|
||||
private final ListenerCollection<EventType> typedListeners = new ListenerCollection<>(new HashMap<>(), this);
|
||||
|
||||
enum EventType {
|
||||
CLOSE,
|
||||
}
|
||||
|
||||
protected CDPSessionImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
@@ -45,22 +40,9 @@ public class CDPSessionImpl extends ChannelOwner implements CDPSession {
|
||||
params = parameters.get("params").getAsJsonObject();
|
||||
}
|
||||
listeners.notify(method, params);
|
||||
listeners.notify("event", parameters);
|
||||
} else if ("close".equals(event)) {
|
||||
typedListeners.notify(EventType.CLOSE, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(Consumer<CDPSession> handler) {
|
||||
typedListeners.add(EventType.CLOSE, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void offClose(Consumer<CDPSession> handler) {
|
||||
typedListeners.remove(EventType.CLOSE, handler);
|
||||
}
|
||||
|
||||
public JsonObject send(String method) {
|
||||
return send(method, null);
|
||||
}
|
||||
|
||||
@@ -35,8 +35,7 @@ class ChannelOwner extends LoggingSupport {
|
||||
final String guid;
|
||||
final JsonObject initializer;
|
||||
private boolean wasCollected;
|
||||
|
||||
static Double NO_TIMEOUT = null;
|
||||
private boolean isInternalType;
|
||||
|
||||
protected ChannelOwner(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
this(parent.connection, parent, type, guid, initializer);
|
||||
@@ -60,6 +59,10 @@ class ChannelOwner extends LoggingSupport {
|
||||
}
|
||||
}
|
||||
|
||||
void markAsInternalType() {
|
||||
isInternalType = true;
|
||||
}
|
||||
|
||||
void disposeChannelOwner(boolean wasGarbageCollected) {
|
||||
// Clean up from parent and connection.
|
||||
if (parent != null) {
|
||||
@@ -84,20 +87,16 @@ class ChannelOwner extends LoggingSupport {
|
||||
return new WaitForEventLogger<>(this, apiName, code).get();
|
||||
}
|
||||
|
||||
|
||||
void withTitle(String title, Runnable code) {
|
||||
withTitle(title, () -> {
|
||||
code.run();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
<T> T withTitle(String title, Supplier<T> code) {
|
||||
String previousTitle = connection.setTitle(title);
|
||||
@Override
|
||||
<T> T withLogging(String apiName, Supplier<T> code) {
|
||||
if (isInternalType) {
|
||||
apiName = null;
|
||||
}
|
||||
String previousApiName = connection.setApiName(apiName);
|
||||
try {
|
||||
return code.get();
|
||||
return super.withLogging(apiName, code);
|
||||
} finally {
|
||||
connection.setTitle(previousTitle);
|
||||
connection.setApiName(previousApiName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,16 +110,11 @@ class ChannelOwner extends LoggingSupport {
|
||||
}
|
||||
|
||||
JsonElement sendMessage(String method) {
|
||||
return sendMessage(method, new JsonObject(), NO_TIMEOUT);
|
||||
return sendMessage(method, new JsonObject());
|
||||
}
|
||||
|
||||
JsonElement sendMessage(String method, JsonObject params, Double timeout) {
|
||||
JsonElement sendMessage(String method, JsonObject params) {
|
||||
checkNotCollected();
|
||||
if (timeout != null) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@ import com.microsoft.playwright.Clock;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import static com.microsoft.playwright.impl.ChannelOwner.NO_TIMEOUT;
|
||||
|
||||
class ClockImpl implements Clock {
|
||||
private final ChannelOwner browserContext;
|
||||
|
||||
@@ -16,7 +14,8 @@ class ClockImpl implements Clock {
|
||||
|
||||
private void sendMessageWithLogging(String method, JsonObject params) {
|
||||
String capitalizedMethod = method.substring(0, 1).toUpperCase() + method.substring(1);
|
||||
browserContext.sendMessage("clock" + capitalizedMethod, params, NO_TIMEOUT);
|
||||
browserContext.withLogging("Clock." + method,
|
||||
() -> browserContext.sendMessage("clock" + capitalizedMethod, params));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Playwright;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import com.microsoft.playwright.TimeoutError;
|
||||
|
||||
@@ -63,8 +64,7 @@ public class Connection {
|
||||
private int lastId = 0;
|
||||
private final StackTraceCollector stackTraceCollector;
|
||||
private final Map<Integer, WaitableResult<JsonElement>> callbacks = new HashMap<>();
|
||||
private String title;
|
||||
private boolean titleReported = false;
|
||||
private String apiName;
|
||||
private static final boolean isLogging;
|
||||
static {
|
||||
String debug = System.getenv("DEBUG");
|
||||
@@ -83,7 +83,7 @@ public class Connection {
|
||||
PlaywrightImpl initialize() {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("sdkLanguage", "java");
|
||||
JsonElement result = sendMessage("initialize", params.getAsJsonObject(), NO_TIMEOUT);
|
||||
JsonElement result = sendMessage("initialize", params.getAsJsonObject());
|
||||
return this.connection.getExistingObject(result.getAsJsonObject().getAsJsonObject("playwright").get("guid").getAsString());
|
||||
}
|
||||
}
|
||||
@@ -116,10 +116,9 @@ public class Connection {
|
||||
}
|
||||
}
|
||||
|
||||
String setTitle(String newTitle) {
|
||||
String previous = title;
|
||||
titleReported = false;
|
||||
title = newTitle;
|
||||
String setApiName(String name) {
|
||||
String previous = apiName;
|
||||
apiName = name;
|
||||
return previous;
|
||||
}
|
||||
|
||||
@@ -147,14 +146,12 @@ public class Connection {
|
||||
JsonObject metadata = new JsonObject();
|
||||
metadata.addProperty("wallTime", currentTimeMillis());
|
||||
JsonArray stack = null;
|
||||
if (titleReported) {
|
||||
if (apiName == null) {
|
||||
metadata.addProperty("internal", true);
|
||||
} else {
|
||||
if (title != null) {
|
||||
metadata.addProperty("title", title);
|
||||
// All but first message in a custom-titled API call are considered internal and will be hidden from the inspector.
|
||||
titleReported = true;
|
||||
}
|
||||
metadata.addProperty("apiName", apiName);
|
||||
// All but first message in an API call are considered internal and will be hidden from the inspector.
|
||||
apiName = null;
|
||||
if (stackTraceCollector != null) {
|
||||
stack = stackTraceCollector.currentStackTrace();
|
||||
if (!stack.isEmpty()) {
|
||||
@@ -333,9 +330,6 @@ public class Connection {
|
||||
case "Dialog":
|
||||
result = new DialogImpl(parent, type, guid, initializer);
|
||||
break;
|
||||
case "Disposable":
|
||||
result = new DisposableObject(parent, type, guid, initializer);
|
||||
break;
|
||||
case "Electron":
|
||||
// result = new Playwright(parent, type, guid, initializer);
|
||||
break;
|
||||
@@ -379,10 +373,10 @@ public class Connection {
|
||||
case "Stream":
|
||||
result = new Stream(parent, type, guid, initializer);
|
||||
break;
|
||||
case "SocksSupport":
|
||||
case "Selectors":
|
||||
result = new SelectorsImpl(parent, type, guid, initializer);
|
||||
break;
|
||||
case "Debugger":
|
||||
result = new DebuggerImpl(parent, type, guid, initializer);
|
||||
case "SocksSupport":
|
||||
break;
|
||||
case "Tracing":
|
||||
result = new TracingImpl(parent, type, guid, initializer);
|
||||
|
||||
@@ -20,38 +20,33 @@ import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.ConsoleMessage;
|
||||
import com.microsoft.playwright.JSHandle;
|
||||
import com.microsoft.playwright.Worker;
|
||||
import com.microsoft.playwright.Page;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
|
||||
public class ConsoleMessageImpl implements ConsoleMessage {
|
||||
private final Connection connection;
|
||||
private final PageImpl page;
|
||||
private final WorkerImpl worker;
|
||||
private PageImpl page;
|
||||
private final JsonObject initializer;
|
||||
|
||||
public ConsoleMessageImpl(Connection connection, JsonObject initializer, PageImpl page, WorkerImpl worker) {
|
||||
public ConsoleMessageImpl(Connection connection, JsonObject initializer) {
|
||||
this.connection = connection;
|
||||
this.page = page;
|
||||
this.worker = worker;
|
||||
// Note: currently, we only report console messages for pages and they always have a page.
|
||||
// However, in the future we might report console messages for service workers or something else,
|
||||
// where page() would be null.
|
||||
if (initializer.has("page")) {
|
||||
page = connection.getExistingObject(initializer.getAsJsonObject("page").get("guid").getAsString());
|
||||
}
|
||||
this.initializer = initializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double timestamp() {
|
||||
return initializer.get("timestamp").getAsDouble();
|
||||
}
|
||||
|
||||
public String type() {
|
||||
return initializer.get("type").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Worker worker() {
|
||||
return worker;
|
||||
}
|
||||
|
||||
public String text() {
|
||||
return initializer.get("text").getAsString();
|
||||
}
|
||||
|
||||
@@ -1,86 +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.Debugger;
|
||||
import com.microsoft.playwright.options.Location;
|
||||
import com.microsoft.playwright.options.DebuggerPausedDetails;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
|
||||
class DebuggerImpl extends ChannelOwner implements Debugger {
|
||||
private final List<Runnable> pausedStateChangedHandlers = new ArrayList<>();
|
||||
private DebuggerPausedDetails pausedDetails;
|
||||
|
||||
DebuggerImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleEvent(String event, JsonObject params) {
|
||||
if ("pausedStateChanged".equals(event)) {
|
||||
if (params.has("pausedDetails") && !params.get("pausedDetails").isJsonNull()) {
|
||||
pausedDetails = gson().fromJson(params.get("pausedDetails"), DebuggerPausedDetails.class);
|
||||
} else {
|
||||
pausedDetails = null;
|
||||
}
|
||||
for (Runnable handler : new ArrayList<>(pausedStateChangedHandlers)) {
|
||||
handler.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPausedStateChanged(Runnable handler) {
|
||||
pausedStateChangedHandlers.add(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void offPausedStateChanged(Runnable handler) {
|
||||
pausedStateChangedHandlers.remove(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggerPausedDetails pausedDetails() {
|
||||
return pausedDetails;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestPause() {
|
||||
sendMessage("requestPause", new JsonObject(), NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume() {
|
||||
sendMessage("resume", new JsonObject(), NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void next() {
|
||||
sendMessage("next", new JsonObject(), NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runTo(Location location) {
|
||||
JsonObject params = gson().toJsonTree(location).getAsJsonObject();
|
||||
sendMessage("runTo", params, NO_TIMEOUT);
|
||||
}
|
||||
}
|
||||
@@ -34,20 +34,18 @@ class DialogImpl extends ChannelOwner implements Dialog {
|
||||
|
||||
@Override
|
||||
public void accept(String promptText) {
|
||||
JsonObject params = new JsonObject();
|
||||
if (promptText != null) {
|
||||
params.addProperty("promptText", promptText);
|
||||
}
|
||||
sendMessage("accept", params, NO_TIMEOUT);
|
||||
withLogging("Dialog.accept", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
if (promptText != null) {
|
||||
params.addProperty("promptText", promptText);
|
||||
}
|
||||
sendMessage("accept", params);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismiss() {
|
||||
try {
|
||||
sendMessage("dismiss");
|
||||
} catch (TargetClosedError e) {
|
||||
// Swallow TargetClosedErrors for beforeunload dialogs.
|
||||
}
|
||||
withLogging("Dialog.dismiss", () -> sendMessage("dismiss"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,30 +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;
|
||||
|
||||
class DisposableObject extends ChannelOwner implements AutoCloseable {
|
||||
DisposableObject(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
sendMessage("dispose", new JsonObject(), NO_TIMEOUT);
|
||||
}
|
||||
}
|
||||
@@ -1,34 +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;
|
||||
|
||||
class DisposableStub implements AutoCloseable {
|
||||
private Runnable dispose;
|
||||
|
||||
DisposableStub(Runnable dispose) {
|
||||
this.dispose = dispose;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (dispose == null)
|
||||
return;
|
||||
Runnable d = dispose;
|
||||
dispose = null;
|
||||
d.run();
|
||||
}
|
||||
}
|
||||
@@ -46,22 +46,22 @@ class DownloadImpl implements Download {
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
artifact.cancel();
|
||||
page.withLogging("Download.cancel", () -> artifact.cancel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream createReadStream() {
|
||||
return artifact.createReadStream();
|
||||
return page.withLogging("Download.createReadStream", () -> artifact.createReadStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
artifact.delete();
|
||||
page.withLogging("Download.delete", () -> artifact.delete());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String failure() {
|
||||
return artifact.failure();
|
||||
return page.withLogging("Download.failure", () -> artifact.failure());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -71,11 +71,11 @@ class DownloadImpl implements Download {
|
||||
|
||||
@Override
|
||||
public Path path() {
|
||||
return artifact.pathAfterFinished();
|
||||
return page.withLogging("Download.path", () -> artifact.pathAfterFinished());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAs(Path path) {
|
||||
artifact.saveAs(path);
|
||||
page.withLogging("Download.saveAs", () -> artifact.saveAs(path));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,11 +40,8 @@ import static com.microsoft.playwright.options.ScreenshotType.JPEG;
|
||||
import static com.microsoft.playwright.options.ScreenshotType.PNG;
|
||||
|
||||
public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
private final FrameImpl frame;
|
||||
|
||||
ElementHandleImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
this.frame = (FrameImpl)parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,83 +51,105 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
|
||||
@Override
|
||||
public ElementHandle querySelector(String selector) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonElement json = sendMessage("querySelector", params, NO_TIMEOUT);
|
||||
JsonObject element = json.getAsJsonObject().getAsJsonObject("element");
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
return connection.getExistingObject(element.get("guid").getAsString());
|
||||
return withLogging("ElementHandle.querySelector", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonElement json = sendMessage("querySelector", params);
|
||||
JsonObject element = json.getAsJsonObject().getAsJsonObject("element");
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
return connection.getExistingObject(element.get("guid").getAsString());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ElementHandle> querySelectorAll(String selector) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonElement json = sendMessage("querySelectorAll", params, NO_TIMEOUT);
|
||||
JsonArray elements = json.getAsJsonObject().getAsJsonArray("elements");
|
||||
if (elements == null) {
|
||||
return null;
|
||||
}
|
||||
List<ElementHandle> handles = new ArrayList<>();
|
||||
for (JsonElement item : elements) {
|
||||
handles.add(connection.getExistingObject(item.getAsJsonObject().get("guid").getAsString()));
|
||||
}
|
||||
return handles;
|
||||
return withLogging("ElementHandle.<", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonElement json = sendMessage("querySelectorAll", params);
|
||||
JsonArray elements = json.getAsJsonObject().getAsJsonArray("elements");
|
||||
if (elements == null) {
|
||||
return null;
|
||||
}
|
||||
List<ElementHandle> handles = new ArrayList<>();
|
||||
for (JsonElement item : elements) {
|
||||
handles.add(connection.getExistingObject(item.getAsJsonObject().get("guid").getAsString()));
|
||||
}
|
||||
return handles;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evalOnSelector(String selector, String pageFunction, Object arg) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evalOnSelector", params, NO_TIMEOUT);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
return withLogging("ElementHandle.evalOnSelector", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evalOnSelector", params);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evalOnSelectorAll(String selector, String pageFunction, Object arg) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evalOnSelectorAll", params, NO_TIMEOUT);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
return withLogging("ElementHandle.evalOnSelectorAll", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evalOnSelectorAll", params);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoundingBox boundingBox() {
|
||||
JsonObject json = sendMessage("boundingBox").getAsJsonObject();
|
||||
if (!json.has("value")) {
|
||||
return null;
|
||||
}
|
||||
return gson().fromJson(json.get("value"), BoundingBox.class);
|
||||
return withLogging("ElementHandle.boundingBox", () -> {
|
||||
JsonObject json = sendMessage("boundingBox").getAsJsonObject();
|
||||
if (!json.has("value")) {
|
||||
return null;
|
||||
}
|
||||
return gson().fromJson(json.get("value"), BoundingBox.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void check(CheckOptions options) {
|
||||
withLogging("ElementHandle.check", () -> checkImpl(options));
|
||||
}
|
||||
|
||||
private void checkImpl(CheckOptions options) {
|
||||
if (options == null) {
|
||||
options = new CheckOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("check", params, frame.timeout(options.timeout));
|
||||
sendMessage("check", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void click(ClickOptions options) {
|
||||
withLogging("ElementHandle.click", () -> clickImpl(options));
|
||||
}
|
||||
|
||||
private void clickImpl(ClickOptions options) {
|
||||
if (options == null) {
|
||||
options = new ClickOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("click", params, frame.timeout(options.timeout));
|
||||
sendMessage("click", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Frame contentFrame() {
|
||||
return withLogging("ElementHandle.contentFrame", () -> contentFrameImpl());
|
||||
}
|
||||
|
||||
private Frame contentFrameImpl() {
|
||||
JsonObject json = sendMessage("contentFrame").getAsJsonObject();
|
||||
if (!json.has("frame")) {
|
||||
return null;
|
||||
@@ -140,132 +159,177 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
|
||||
@Override
|
||||
public void dblclick(DblclickOptions options) {
|
||||
withLogging("ElementHandle.dblclick", () -> dblclickImpl(options));
|
||||
}
|
||||
|
||||
private void dblclickImpl(DblclickOptions options) {
|
||||
if (options == null) {
|
||||
options = new DblclickOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("dblclick", params, frame.timeout(options.timeout));
|
||||
sendMessage("dblclick", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchEvent(String type, Object eventInit) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("type", type);
|
||||
params.add("eventInit", gson().toJsonTree(serializeArgument(eventInit)));
|
||||
sendMessage("dispatchEvent", params, NO_TIMEOUT);
|
||||
withLogging("ElementHandle.dispatchEvent", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("type", type);
|
||||
params.add("eventInit", gson().toJsonTree(serializeArgument(eventInit)));
|
||||
sendMessage("dispatchEvent", params);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fill(String value, FillOptions options) {
|
||||
withLogging("ElementHandle.fill", () -> fillImpl(value, options));
|
||||
}
|
||||
|
||||
private void fillImpl(String value, FillOptions options) {
|
||||
if (options == null) {
|
||||
options = new FillOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("value", value);
|
||||
sendMessage("fill", params, frame.timeout(options.timeout));
|
||||
sendMessage("fill", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focus() {
|
||||
sendMessage("focus");
|
||||
withLogging("ElementHandle.focus", () -> sendMessage("focus"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttribute(String name) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("name", name);
|
||||
JsonObject json = sendMessage("getAttribute", params, NO_TIMEOUT).getAsJsonObject();
|
||||
return json.has("value") ? json.get("value").getAsString() : null;
|
||||
return withLogging("ElementHandle.getAttribute", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("name", name);
|
||||
JsonObject json = sendMessage("getAttribute", params).getAsJsonObject();
|
||||
return json.has("value") ? json.get("value").getAsString() : null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hover(HoverOptions options) {
|
||||
withLogging("ElementHandle.hover", () -> hoverImpl(options));
|
||||
}
|
||||
|
||||
private void hoverImpl(HoverOptions options) {
|
||||
if (options == null) {
|
||||
options = new HoverOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("hover", params, frame.timeout(options.timeout));
|
||||
sendMessage("hover", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String innerHTML() {
|
||||
JsonObject json = sendMessage("innerHTML").getAsJsonObject();
|
||||
return json.get("value").getAsString();
|
||||
return withLogging("ElementHandle.innerHTML", () -> {
|
||||
JsonObject json = sendMessage("innerHTML").getAsJsonObject();
|
||||
return json.get("value").getAsString();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String innerText() {
|
||||
JsonObject json = sendMessage("innerText").getAsJsonObject();
|
||||
return json.get("value").getAsString();
|
||||
return withLogging("ElementHandle.innerText", () -> {
|
||||
JsonObject json = sendMessage("innerText").getAsJsonObject();
|
||||
return json.get("value").getAsString();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String inputValue(InputValueOptions options) {
|
||||
return withLogging("ElementHandle.inputValue", () -> inputValueImpl(options));
|
||||
}
|
||||
|
||||
private String inputValueImpl(InputValueOptions options) {
|
||||
if (options == null) {
|
||||
options = new InputValueOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
JsonObject json = sendMessage("inputValue", params, NO_TIMEOUT).getAsJsonObject();
|
||||
JsonObject json = sendMessage("inputValue", params).getAsJsonObject();
|
||||
return json.get("value").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
JsonObject json = sendMessage("isChecked").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
return withLogging("ElementHandle.isChecked", () -> {
|
||||
JsonObject json = sendMessage("isChecked").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisabled() {
|
||||
JsonObject json = sendMessage("isDisabled").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
return withLogging("ElementHandle.isDisabled", () -> {
|
||||
JsonObject json = sendMessage("isDisabled").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEditable() {
|
||||
JsonObject json = sendMessage("isEditable").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
return withLogging("ElementHandle.isEditable", () -> {
|
||||
JsonObject json = sendMessage("isEditable").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
JsonObject json = sendMessage("isEnabled").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
return withLogging("ElementHandle.isEnabled", () -> {
|
||||
JsonObject json = sendMessage("isEnabled").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden() {
|
||||
JsonObject json = sendMessage("isHidden").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
return withLogging("ElementHandle.isHidden", () -> {
|
||||
JsonObject json = sendMessage("isHidden").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
JsonObject json = sendMessage("isVisible").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
return withLogging("ElementHandle.isVisible", () -> {
|
||||
JsonObject json = sendMessage("isVisible").getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrameImpl ownerFrame() {
|
||||
JsonObject json = sendMessage("ownerFrame").getAsJsonObject();
|
||||
if (!json.has("frame")) {
|
||||
return null;
|
||||
}
|
||||
return connection.getExistingObject(json.getAsJsonObject("frame").get("guid").getAsString());
|
||||
return withLogging("ElementHandle.ownerFrame", () -> {
|
||||
JsonObject json = sendMessage("ownerFrame").getAsJsonObject();
|
||||
if (!json.has("frame")) {
|
||||
return null;
|
||||
}
|
||||
return connection.getExistingObject(json.getAsJsonObject("frame").get("guid").getAsString());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void press(String key, PressOptions options) {
|
||||
withLogging("ElementHandle.press", () -> pressImpl(key, options));
|
||||
}
|
||||
private void pressImpl(String key, PressOptions options) {
|
||||
if (options == null) {
|
||||
options = new PressOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("key", key);
|
||||
sendMessage("press", params, frame.timeout(options.timeout));
|
||||
sendMessage("press", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] screenshot(ScreenshotOptions options) {
|
||||
return withLogging("ElementHandle.screenshot", () -> screenshotImpl(options));
|
||||
}
|
||||
|
||||
private byte[] screenshotImpl(ScreenshotOptions options) {
|
||||
if (options == null) {
|
||||
options = new ScreenshotOptions();
|
||||
}
|
||||
@@ -284,7 +348,7 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.remove("path");
|
||||
JsonObject json = sendMessage("screenshot", params, frame.timeout(options.timeout)).getAsJsonObject();
|
||||
JsonObject json = sendMessage("screenshot", params).getAsJsonObject();
|
||||
|
||||
byte[] buffer = Base64.getDecoder().decode(json.get("binary").getAsString());
|
||||
if (options.path != null) {
|
||||
@@ -295,11 +359,7 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
|
||||
@Override
|
||||
public void scrollIntoViewIfNeeded(ScrollIntoViewIfNeededOptions options) {
|
||||
if (options == null) {
|
||||
options = new ScrollIntoViewIfNeededOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("scrollIntoViewIfNeeded", params, frame.timeout(options.timeout));
|
||||
withLogging("ElementHandle.scrollIntoViewIfNeeded", () -> scrollIntoViewIfNeededImpl(options));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -323,7 +383,7 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
if (values != null) {
|
||||
params.add("options", toSelectValueOrLabel(values));
|
||||
}
|
||||
return selectOption(params, options.timeout);
|
||||
return selectOption(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -332,6 +392,13 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
return selectOption(values, options);
|
||||
}
|
||||
|
||||
private void scrollIntoViewIfNeededImpl(ScrollIntoViewIfNeededOptions options) {
|
||||
if (options == null) {
|
||||
options = new ScrollIntoViewIfNeededOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("scrollIntoViewIfNeeded", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(SelectOption[] values, SelectOptionOptions options) {
|
||||
@@ -342,7 +409,7 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
if (values != null) {
|
||||
params.add("options", gson().toJsonTree(values));
|
||||
}
|
||||
return selectOption(params, options.timeout);
|
||||
return selectOption(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -354,21 +421,19 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
if (values != null) {
|
||||
params.add("elements", Serialization.toProtocol(values));
|
||||
}
|
||||
return selectOption(params, options.timeout);
|
||||
return selectOption(params);
|
||||
}
|
||||
|
||||
private List<String> selectOption(JsonObject params, Double timeout) {
|
||||
JsonObject json = sendMessage("selectOption", params, frame.timeout(timeout)).getAsJsonObject();
|
||||
return parseStringList(json.getAsJsonArray("values"));
|
||||
private List<String> selectOption(JsonObject params) {
|
||||
return withLogging("SelectOption", () -> {
|
||||
JsonObject json = sendMessage("selectOption", params).getAsJsonObject();
|
||||
return parseStringList(json.getAsJsonArray("values"));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectText(SelectTextOptions options) {
|
||||
if (options == null) {
|
||||
options = new SelectTextOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("selectText", params, frame.timeout(options.timeout));
|
||||
withLogging("ElementHandle.selectText", () -> selectTextImpl(options));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -385,9 +450,20 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
setInputFiles(new Path[]{files}, options);
|
||||
}
|
||||
|
||||
private void selectTextImpl(SelectTextOptions options) {
|
||||
if (options == null) {
|
||||
options = new SelectTextOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("selectText", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(Path[] files, SetInputFilesOptions options) {
|
||||
withLogging("ElementHandle.setInputFiles", () -> setInputFilesImpl(files, options));
|
||||
}
|
||||
|
||||
void setInputFilesImpl(Path[] files, SetInputFilesOptions options) {
|
||||
FrameImpl frame = ownerFrame();
|
||||
if (frame == null) {
|
||||
throw new Error("Cannot set input files to detached element");
|
||||
@@ -397,7 +473,7 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
addFilePathUploadParams(files, params, frame.page().context());
|
||||
sendMessage("setInputFiles", params, frame.timeout(options.timeout));
|
||||
sendMessage("setInputFiles", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -407,51 +483,77 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
|
||||
@Override
|
||||
public void setInputFiles(FilePayload[] files, SetInputFilesOptions options) {
|
||||
withLogging("ElementHandle.setInputFiles", () -> setInputFilesImpl(files, options));
|
||||
}
|
||||
|
||||
void setInputFilesImpl(FilePayload[] files, SetInputFilesOptions options) {
|
||||
checkFilePayloadSize(files);
|
||||
if (options == null) {
|
||||
options = new SetInputFilesOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.add("payloads", Serialization.toJsonArray(files));
|
||||
sendMessage("setInputFiles", params, frame.timeout(options.timeout));
|
||||
sendMessage("setInputFiles", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tap(TapOptions options) {
|
||||
withLogging("ElementHandle.tap", () -> tapImpl(options));
|
||||
}
|
||||
|
||||
private void tapImpl(TapOptions options) {
|
||||
if (options == null) {
|
||||
options = new TapOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("tap", params, frame.timeout(options.timeout));
|
||||
sendMessage("tap", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String textContent() {
|
||||
JsonObject json = sendMessage("textContent").getAsJsonObject();
|
||||
return json.has("value") ? json.get("value").getAsString() : null;
|
||||
return withLogging("ElementHandle.textContent", () -> textContentImpl());
|
||||
}
|
||||
|
||||
private String textContentImpl() {
|
||||
return withLogging("ElementHandle.textContent", () -> {
|
||||
JsonObject json = sendMessage("textContent").getAsJsonObject();
|
||||
return json.has("value") ? json.get("value").getAsString() : null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void type(String text, TypeOptions options) {
|
||||
withLogging("ElementHandle.type", () -> typeImpl(text, options));
|
||||
}
|
||||
|
||||
private void typeImpl(String text, TypeOptions options) {
|
||||
if (options == null) {
|
||||
options = new TypeOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("text", text);
|
||||
sendMessage("type", params, frame.timeout(options.timeout));
|
||||
sendMessage("type", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncheck(UncheckOptions options) {
|
||||
withLogging("ElementHandle.uncheck", () -> uncheckImpl(options));
|
||||
}
|
||||
|
||||
private void uncheckImpl(UncheckOptions options) {
|
||||
if (options == null) {
|
||||
options = new UncheckOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("uncheck", params, frame.timeout(options.timeout));
|
||||
sendMessage("uncheck", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waitForElementState(ElementState state, WaitForElementStateOptions options) {
|
||||
withLogging("ElementHandle.waitForElementState", () -> waitForElementStateImpl(state, options));
|
||||
}
|
||||
|
||||
private void waitForElementStateImpl(ElementState state, WaitForElementStateOptions options) {
|
||||
if (options == null) {
|
||||
options = new WaitForElementStateOptions();
|
||||
}
|
||||
@@ -460,7 +562,7 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("state", toProtocol(state));
|
||||
sendMessage("waitForElementState", params, frame.timeout(options.timeout));
|
||||
sendMessage("waitForElementState", params);
|
||||
}
|
||||
|
||||
private static String toProtocol(ElementState state) {
|
||||
@@ -469,12 +571,16 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
|
||||
@Override
|
||||
public ElementHandle waitForSelector(String selector, WaitForSelectorOptions options) {
|
||||
return withLogging("ElementHandle.waitForSelector", () -> waitForSelectorImpl(selector, options));
|
||||
}
|
||||
|
||||
private ElementHandle waitForSelectorImpl(String selector, WaitForSelectorOptions options) {
|
||||
if (options == null) {
|
||||
options = new WaitForSelectorOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonElement json = sendMessage("waitForSelector", params, frame.timeout(options.timeout)).getAsJsonObject();
|
||||
JsonElement json = sendMessage("waitForSelector", params);
|
||||
JsonObject element = json.getAsJsonObject().getAsJsonObject("element");
|
||||
if (element == null) {
|
||||
return null;
|
||||
|
||||
@@ -58,7 +58,8 @@ class FileChooserImpl implements FileChooser {
|
||||
|
||||
@Override
|
||||
public void setFiles(Path[] files, SetFilesOptions options) {
|
||||
element.setInputFiles(files, convertType(options, ElementHandle.SetInputFilesOptions.class));
|
||||
page.withLogging("FileChooser.setInputFiles",
|
||||
() -> element.setInputFilesImpl(files, convertType(options, ElementHandle.SetInputFilesOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -68,6 +69,7 @@ class FileChooserImpl implements FileChooser {
|
||||
|
||||
@Override
|
||||
public void setFiles(FilePayload[] files, SetFilesOptions options) {
|
||||
element.setInputFiles(files, convertType(options, ElementHandle.SetInputFilesOptions.class));
|
||||
page.withLogging("FileChooser.setInputFiles",
|
||||
() -> element.setInputFilesImpl(files, convertType(options, ElementHandle.SetInputFilesOptions.class)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,12 +74,16 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public ElementHandle querySelector(String selector, QuerySelectorOptions options) {
|
||||
return withLogging("Frame.querySelector", () -> querySelectorImpl(selector, options));
|
||||
}
|
||||
|
||||
ElementHandleImpl querySelectorImpl(String selector, QuerySelectorOptions options) {
|
||||
if (options == null) {
|
||||
options = new QuerySelectorOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonElement json = sendMessage("querySelector", params, NO_TIMEOUT);
|
||||
JsonElement json = sendMessage("querySelector", params);
|
||||
JsonObject element = json.getAsJsonObject().getAsJsonObject("element");
|
||||
if (element == null) {
|
||||
return null;
|
||||
@@ -89,18 +93,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public List<ElementHandle> querySelectorAll(String selector) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonElement json = sendMessage("querySelectorAll", params, NO_TIMEOUT);
|
||||
JsonArray elements = json.getAsJsonObject().getAsJsonArray("elements");
|
||||
if (elements == null) {
|
||||
return null;
|
||||
}
|
||||
List<ElementHandle> handles = new ArrayList<>();
|
||||
for (JsonElement item : elements) {
|
||||
handles.add(connection.getExistingObject(item.getAsJsonObject().get("guid").getAsString()));
|
||||
}
|
||||
return handles;
|
||||
return withLogging("Frame.querySelectorAll", () -> querySelectorAllImpl(selector));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -117,7 +110,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String selector, String[] values, SelectOptionOptions options) {
|
||||
return selectOptionImpl(selector, values, options);
|
||||
return withLogging("Frame.selectOption", () -> selectOptionImpl(selector, values, options));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -126,10 +119,24 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
return selectOption(selector, values, options);
|
||||
}
|
||||
|
||||
List<ElementHandle> querySelectorAllImpl(String selector) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonElement json = sendMessage("querySelectorAll", params);
|
||||
JsonArray elements = json.getAsJsonObject().getAsJsonArray("elements");
|
||||
if (elements == null) {
|
||||
return null;
|
||||
}
|
||||
List<ElementHandle> handles = new ArrayList<>();
|
||||
for (JsonElement item : elements) {
|
||||
handles.add(connection.getExistingObject(item.getAsJsonObject().get("guid").getAsString()));
|
||||
}
|
||||
return handles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evalOnSelector(String selector, String pageFunction, Object arg, EvalOnSelectorOptions options) {
|
||||
return evalOnSelectorImpl(selector, pageFunction, arg, options);
|
||||
return withLogging("Frame.evalOnSelector", () -> evalOnSelectorImpl(selector, pageFunction, arg, options));
|
||||
}
|
||||
|
||||
Object evalOnSelectorImpl(String selector, String pageFunction, Object arg, EvalOnSelectorOptions options) {
|
||||
@@ -140,14 +147,14 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evalOnSelector", params, NO_TIMEOUT);
|
||||
JsonElement json = sendMessage("evalOnSelector", params);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evalOnSelectorAll(String selector, String pageFunction, Object arg) {
|
||||
return evalOnSelectorAllImpl(selector, pageFunction, arg);
|
||||
return withLogging("Frame.evalOnSelectorAll", () -> evalOnSelectorAllImpl(selector, pageFunction, arg));
|
||||
}
|
||||
|
||||
Object evalOnSelectorAllImpl(String selector, String pageFunction, Object arg) {
|
||||
@@ -155,14 +162,14 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evalOnSelectorAll", params, NO_TIMEOUT);
|
||||
JsonElement json = sendMessage("evalOnSelectorAll", params);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementHandle addScriptTag(AddScriptTagOptions options){
|
||||
return addScriptTagImpl(options);
|
||||
return withLogging("Frame.addScriptTag", () -> addScriptTagImpl(options));
|
||||
}
|
||||
|
||||
ElementHandle addScriptTagImpl(AddScriptTagOptions options) {
|
||||
@@ -182,13 +189,13 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
content = addSourceUrlToScript(content, options.path);
|
||||
jsonOptions.addProperty("content", content);
|
||||
}
|
||||
JsonElement json = sendMessage("addScriptTag", jsonOptions, NO_TIMEOUT);
|
||||
JsonElement json = sendMessage("addScriptTag", jsonOptions);
|
||||
return connection.getExistingObject(json.getAsJsonObject().getAsJsonObject("element").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementHandle addStyleTag(AddStyleTagOptions options){
|
||||
return addStyleTagImpl(options);
|
||||
return withLogging("Frame.addStyleTag", () -> addStyleTagImpl(options));
|
||||
}
|
||||
|
||||
ElementHandle addStyleTagImpl(AddStyleTagOptions options) {
|
||||
@@ -208,18 +215,22 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
content += "/*# sourceURL=" + options.path.toString().replace("\n", "") + "*/";
|
||||
jsonOptions.addProperty("content", content);
|
||||
}
|
||||
JsonElement json = sendMessage("addStyleTag", jsonOptions, NO_TIMEOUT);
|
||||
JsonElement json = sendMessage("addStyleTag", jsonOptions);
|
||||
return connection.getExistingObject(json.getAsJsonObject().getAsJsonObject("element").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void check(String selector, CheckOptions options){
|
||||
withLogging("Frame.check", () -> checkImpl(selector, options));
|
||||
}
|
||||
|
||||
void checkImpl(String selector, CheckOptions options) {
|
||||
if (options == null) {
|
||||
options = new CheckOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
sendMessage("check", params, timeout(options.timeout));
|
||||
sendMessage("check", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -229,45 +240,47 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public void click(String selector, ClickOptions options) {
|
||||
clickImpl(selector, options, null);
|
||||
withLogging("Frame.click", () -> clickImpl(selector, options));
|
||||
}
|
||||
|
||||
void clickImpl(String selector, ClickOptions options, Integer steps) {
|
||||
void clickImpl(String selector, ClickOptions options) {
|
||||
if (options == null) {
|
||||
options = new ClickOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
if (steps != null) {
|
||||
params.addProperty("steps", steps);
|
||||
}
|
||||
sendMessage("click", params, timeout(options.timeout));
|
||||
sendMessage("click", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String content() {
|
||||
return withLogging("Frame.content", () -> contentImpl());
|
||||
}
|
||||
|
||||
String contentImpl() {
|
||||
return sendMessage("content").getAsJsonObject().get("value").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dblclick(String selector, DblclickOptions options) {
|
||||
dblclickImpl(selector, options, null);
|
||||
withLogging("Frame.dblclick", () -> dblclickImpl(selector, options));
|
||||
}
|
||||
|
||||
void dblclickImpl(String selector, DblclickOptions options, Integer steps) {
|
||||
void dblclickImpl(String selector, DblclickOptions options) {
|
||||
if (options == null) {
|
||||
options = new DblclickOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
if (steps != null) {
|
||||
params.addProperty("steps", steps);
|
||||
}
|
||||
sendMessage("dblclick", params, timeout(options.timeout));
|
||||
sendMessage("dblclick", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchEvent(String selector, String type, Object eventInit, DispatchEventOptions options) {
|
||||
withLogging("Frame.dispatchEvent", () -> dispatchEventImpl(selector, type, eventInit, options));
|
||||
}
|
||||
|
||||
void dispatchEventImpl(String selector, String type, Object eventInit, DispatchEventOptions options) {
|
||||
if (options == null) {
|
||||
options = new DispatchEventOptions();
|
||||
}
|
||||
@@ -275,55 +288,71 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("type", type);
|
||||
params.add("eventInit", gson().toJsonTree(serializeArgument(eventInit)));
|
||||
sendMessage("dispatchEvent", params, timeout(options.timeout));
|
||||
sendMessage("dispatchEvent", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evaluate(String expression, Object arg) {
|
||||
return withLogging("Frame.evaluate", () -> evaluateImpl(expression, arg));
|
||||
}
|
||||
|
||||
Object evaluateImpl(String expression, Object arg) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", expression);
|
||||
params.addProperty("world", "main");
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpression", params, NO_TIMEOUT);
|
||||
JsonElement json = sendMessage("evaluateExpression", params);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSHandle evaluateHandle(String pageFunction, Object arg) {
|
||||
return withLogging("Frame.evaluateHandle", () -> evaluateHandleImpl(pageFunction, arg));
|
||||
}
|
||||
|
||||
JSHandle evaluateHandleImpl(String pageFunction, Object arg) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.addProperty("world", "main");
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpressionHandle", params, NO_TIMEOUT);
|
||||
JsonElement json = sendMessage("evaluateExpressionHandle", params);
|
||||
return connection.getExistingObject(json.getAsJsonObject().getAsJsonObject("handle").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fill(String selector, String value, FillOptions options) {
|
||||
withLogging("Frame.fill", () -> fillImpl(selector, value, options));
|
||||
}
|
||||
|
||||
void fillImpl(String selector, String value, FillOptions options) {
|
||||
if (options == null) {
|
||||
options = new FillOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("value", value);
|
||||
sendMessage("fill", params, timeout(options.timeout));
|
||||
sendMessage("fill", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focus(String selector, FocusOptions options) {
|
||||
withLogging("Frame.focus", () -> focusImpl(selector, options));
|
||||
}
|
||||
|
||||
void focusImpl(String selector, FocusOptions options) {
|
||||
if (options == null) {
|
||||
options = new FocusOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
sendMessage("focus", params, timeout(options.timeout));
|
||||
sendMessage("focus", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementHandle frameElement() {
|
||||
JsonObject json = sendMessage("frameElement").getAsJsonObject();
|
||||
return connection.getExistingObject(json.getAsJsonObject("element").get("guid").getAsString());
|
||||
return withLogging("Frame.frameElement", () -> frameElementImpl());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -331,9 +360,14 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
return new FrameLocatorImpl(this, selector);
|
||||
}
|
||||
|
||||
ElementHandle frameElementImpl() {
|
||||
JsonObject json = sendMessage("frameElement").getAsJsonObject();
|
||||
return connection.getExistingObject(json.getAsJsonObject("element").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttribute(String selector, String name, GetAttributeOptions options) {
|
||||
return getAttributeImpl(selector, name, options);
|
||||
return withLogging("Frame.getAttribute", () -> getAttributeImpl(selector, name, options));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -408,7 +442,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("name", name);
|
||||
JsonObject json = sendMessage("getAttribute", params, timeout(options.timeout)).getAsJsonObject();
|
||||
JsonObject json = sendMessage("getAttribute", params).getAsJsonObject();
|
||||
if (json.has("value")) {
|
||||
return json.get("value").getAsString();
|
||||
}
|
||||
@@ -417,7 +451,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public ResponseImpl navigate(String url, NavigateOptions options) {
|
||||
return navigateImpl(url, options);
|
||||
return withLogging("Page.navigate", () -> navigateImpl(url, options));
|
||||
}
|
||||
|
||||
ResponseImpl navigateImpl(String url, NavigateOptions options) {
|
||||
@@ -426,7 +460,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("url", url);
|
||||
JsonElement result = sendMessage("goto", params, navigationTimeout(options.timeout));
|
||||
JsonElement result = sendMessage("goto", params);
|
||||
JsonObject jsonResponse = result.getAsJsonObject().getAsJsonObject("response");
|
||||
if (jsonResponse == null) {
|
||||
return null;
|
||||
@@ -436,7 +470,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public void hover(String selector, HoverOptions options) {
|
||||
hoverImpl(selector, options);
|
||||
withLogging("Frame.hover", () -> hoverImpl(selector, options));
|
||||
}
|
||||
|
||||
void hoverImpl(String selector, HoverOptions options) {
|
||||
@@ -445,30 +479,27 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
sendMessage("hover", params, timeout(options.timeout));
|
||||
sendMessage("hover", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragAndDrop(String source, String target, DragAndDropOptions options) {
|
||||
dragAndDropImpl(source, target, options, null);
|
||||
withLogging("Frame.dragAndDrop", () -> dragAndDropImpl(source, target, options));
|
||||
}
|
||||
|
||||
void dragAndDropImpl(String source, String target, DragAndDropOptions options, Integer steps) {
|
||||
void dragAndDropImpl(String source, String target, DragAndDropOptions options) {
|
||||
if (options == null) {
|
||||
options = new DragAndDropOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("source", source);
|
||||
params.addProperty("target", target);
|
||||
if (steps != null) {
|
||||
params.addProperty("steps", steps);
|
||||
}
|
||||
sendMessage("dragAndDrop", params, timeout(options.timeout));
|
||||
sendMessage("dragAndDrop", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String innerHTML(String selector, InnerHTMLOptions options) {
|
||||
return innerHTMLImpl(selector, options);
|
||||
return withLogging("Frame.innerHTML", () -> innerHTMLImpl(selector, options));
|
||||
}
|
||||
|
||||
String innerHTMLImpl(String selector, InnerHTMLOptions options) {
|
||||
@@ -477,13 +508,13 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject json = sendMessage("innerHTML", params, timeout(options.timeout)).getAsJsonObject();
|
||||
JsonObject json = sendMessage("innerHTML", params).getAsJsonObject();
|
||||
return json.get("value").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String innerText(String selector, InnerTextOptions options) {
|
||||
return innerTextImpl(selector, options);
|
||||
return withLogging("Frame.innerText", () -> innerTextImpl(selector, options));
|
||||
}
|
||||
|
||||
String innerTextImpl(String selector, InnerTextOptions options) {
|
||||
@@ -492,13 +523,13 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject json = sendMessage("innerText", params, timeout(options.timeout)).getAsJsonObject();
|
||||
JsonObject json = sendMessage("innerText", params).getAsJsonObject();
|
||||
return json.get("value").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String inputValue(String selector, InputValueOptions options) {
|
||||
return inputValueImpl(selector, options);
|
||||
return withLogging("Frame.inputValue", () -> inputValueImpl(selector, options));
|
||||
}
|
||||
|
||||
String inputValueImpl(String selector, InputValueOptions options) {
|
||||
@@ -507,13 +538,13 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject json = sendMessage("inputValue", params, timeout(options.timeout)).getAsJsonObject();
|
||||
JsonObject json = sendMessage("inputValue", params).getAsJsonObject();
|
||||
return json.get("value").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked(String selector, IsCheckedOptions options) {
|
||||
return isCheckedImpl(selector, options);
|
||||
return withLogging("Page.isChecked", () -> isCheckedImpl(selector, options));
|
||||
}
|
||||
|
||||
boolean isCheckedImpl(String selector, IsCheckedOptions options) {
|
||||
@@ -522,7 +553,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject json = sendMessage("isChecked", params, timeout(options.timeout)).getAsJsonObject();
|
||||
JsonObject json = sendMessage("isChecked", params).getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
}
|
||||
|
||||
@@ -533,7 +564,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public boolean isDisabled(String selector, IsDisabledOptions options) {
|
||||
return isDisabledImpl(selector, options);
|
||||
return withLogging("Page.isDisabled", () -> isDisabledImpl(selector, options));
|
||||
}
|
||||
|
||||
boolean isDisabledImpl(String selector, IsDisabledOptions options) {
|
||||
@@ -542,13 +573,13 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject json = sendMessage("isDisabled", params, timeout(options.timeout)).getAsJsonObject();
|
||||
JsonObject json = sendMessage("isDisabled", params).getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEditable(String selector, IsEditableOptions options) {
|
||||
return isEditableImpl(selector, options);
|
||||
return withLogging("Page.isEditable", () -> isEditableImpl(selector, options));
|
||||
}
|
||||
|
||||
boolean isEditableImpl(String selector, IsEditableOptions options) {
|
||||
@@ -557,13 +588,13 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject json = sendMessage("isEditable", params, timeout(options.timeout)).getAsJsonObject();
|
||||
JsonObject json = sendMessage("isEditable", params).getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(String selector, IsEnabledOptions options) {
|
||||
return isEnabledImpl(selector, options);
|
||||
return withLogging("Page.isEnabled", () -> isEnabledImpl(selector, options));
|
||||
}
|
||||
|
||||
boolean isEnabledImpl(String selector, IsEnabledOptions options) {
|
||||
@@ -572,13 +603,13 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject json = sendMessage("isEnabled", params, timeout(options.timeout)).getAsJsonObject();
|
||||
JsonObject json = sendMessage("isEnabled", params).getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden(String selector, IsHiddenOptions options) {
|
||||
return isHiddenImpl(selector, options);
|
||||
return withLogging("Page.isHidden", () -> isHiddenImpl(selector, options));
|
||||
}
|
||||
|
||||
boolean isHiddenImpl(String selector, IsHiddenOptions options) {
|
||||
@@ -587,13 +618,13 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject json = sendMessage("isHidden", params, timeout(options.timeout)).getAsJsonObject();
|
||||
JsonObject json = sendMessage("isHidden", params).getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible(String selector, IsVisibleOptions options) {
|
||||
return isVisibleImpl(selector, options);
|
||||
return withLogging("Page.isVisible", () -> isVisibleImpl(selector, options));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -607,7 +638,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject json = sendMessage("isVisible", params, timeout(options.timeout)).getAsJsonObject();
|
||||
JsonObject json = sendMessage("isVisible", params).getAsJsonObject();
|
||||
return json.get("value").getAsBoolean();
|
||||
}
|
||||
|
||||
@@ -628,7 +659,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public void press(String selector, String key, PressOptions options) {
|
||||
pressImpl(selector, key, options);
|
||||
withLogging("Frame.press", () -> pressImpl(selector, key, options));
|
||||
}
|
||||
|
||||
void pressImpl(String selector, String key, PressOptions options) {
|
||||
@@ -638,12 +669,12 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("key", key);
|
||||
sendMessage("press", params, timeout(options.timeout));
|
||||
sendMessage("press", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String selector, SelectOption[] values, SelectOptionOptions options) {
|
||||
return selectOptionImpl(selector, values, options);
|
||||
return withLogging("Frame.selectOption", () -> selectOptionImpl(selector, values, options));
|
||||
}
|
||||
|
||||
List<String> selectOptionImpl(String selector, SelectOption[] values, SelectOptionOptions options) {
|
||||
@@ -655,7 +686,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
if (values != null) {
|
||||
params.add("options", gson().toJsonTree(values));
|
||||
}
|
||||
return selectOption(params, options.timeout);
|
||||
return selectOption(params);
|
||||
}
|
||||
|
||||
List<String> selectOptionImpl(String selector, String[] values, SelectOptionOptions options) {
|
||||
@@ -667,12 +698,12 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
if (values != null) {
|
||||
params.add("options", toSelectValueOrLabel(values));
|
||||
}
|
||||
return selectOption(params, options.timeout);
|
||||
return selectOption(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String selector, ElementHandle[] values, SelectOptionOptions options) {
|
||||
return selectOptionImpl(selector, values, options);
|
||||
return withLogging("Frame.selectOption", () -> selectOptionImpl(selector, values, options));
|
||||
}
|
||||
|
||||
List<String> selectOptionImpl(String selector, ElementHandle[] values, SelectOptionOptions options) {
|
||||
@@ -684,35 +715,30 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
if (values != null) {
|
||||
params.add("elements", Serialization.toProtocol(values));
|
||||
}
|
||||
return selectOption(params, options.timeout);
|
||||
return selectOption(params);
|
||||
}
|
||||
|
||||
private List<String> selectOption(JsonObject params, Double timeout) {
|
||||
JsonObject json = sendMessage("selectOption", params, timeout(timeout)).getAsJsonObject();
|
||||
private List<String> selectOption(JsonObject params) {
|
||||
JsonObject json = sendMessage("selectOption", params).getAsJsonObject();
|
||||
return parseStringList(json.getAsJsonArray("values"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChecked(String selector, boolean checked, SetCheckedOptions options) {
|
||||
setCheckedImpl(selector, checked, options);
|
||||
withLogging("Frame.setChecked", () -> setCheckedImpl(selector, checked, options));
|
||||
}
|
||||
|
||||
void setCheckedImpl(String selector, boolean checked, SetCheckedOptions options) {
|
||||
if (checked) {
|
||||
check(selector, convertType(options, CheckOptions.class));
|
||||
checkImpl(selector, convertType(options, CheckOptions.class));
|
||||
} else {
|
||||
uncheck(selector, convertType(options, UncheckOptions.class));
|
||||
uncheckImpl(selector, convertType(options, UncheckOptions.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContent(String html, SetContentOptions options) {
|
||||
if (options == null) {
|
||||
options = new SetContentOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("html", html);
|
||||
sendMessage("setContent", params, navigationTimeout(options.timeout));
|
||||
withLogging("Frame.setContent", () -> setContentImpl(html, options));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -720,9 +746,18 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
setInputFiles(selector, new Path[] {files}, options);
|
||||
}
|
||||
|
||||
void setContentImpl(String html, SetContentOptions options) {
|
||||
if (options == null) {
|
||||
options = new SetContentOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("html", html);
|
||||
sendMessage("setContent", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(String selector, Path[] files, SetInputFilesOptions options) {
|
||||
setInputFilesImpl(selector, files, options);
|
||||
withLogging("Frame.setInputFiles", () -> setInputFilesImpl(selector, files, options));
|
||||
}
|
||||
|
||||
void setInputFilesImpl(String selector, Path[] files, SetInputFilesOptions options) {
|
||||
@@ -732,7 +767,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
addFilePathUploadParams(files, params, page.context());
|
||||
params.addProperty("selector", selector);
|
||||
sendMessage("setInputFiles", params, timeout(options.timeout));
|
||||
sendMessage("setInputFiles", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -742,7 +777,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public void setInputFiles(String selector, FilePayload[] files, SetInputFilesOptions options) {
|
||||
setInputFilesImpl(selector, files, options);
|
||||
withLogging("Frame.setInputFiles", () -> setInputFilesImpl(selector, files, options));
|
||||
}
|
||||
|
||||
void setInputFilesImpl(String selector, FilePayload[] files, SetInputFilesOptions options) {
|
||||
@@ -753,54 +788,73 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.add("payloads", toJsonArray(files));
|
||||
sendMessage("setInputFiles", params, timeout(options.timeout));
|
||||
sendMessage("setInputFiles", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tap(String selector, TapOptions options) {
|
||||
withLogging("Frame.tap", () -> tapImpl(selector, options));
|
||||
}
|
||||
void tapImpl(String selector, TapOptions options) {
|
||||
if (options == null) {
|
||||
options = new TapOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
sendMessage("tap", params, timeout(options.timeout));
|
||||
sendMessage("tap", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String textContent(String selector, TextContentOptions options) {
|
||||
return withLogging("Frame.textContent", () -> textContentImpl(selector, options));
|
||||
}
|
||||
|
||||
String textContentImpl(String selector, TextContentOptions options) {
|
||||
if (options == null) {
|
||||
options = new TextContentOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
return sendMessage("textContent", params, timeout(options.timeout)).getAsJsonObject().get("value").getAsString();
|
||||
return sendMessage("textContent", params).getAsJsonObject().get("value").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String title() {
|
||||
return withLogging("Frame.title", () -> titleImpl());
|
||||
}
|
||||
|
||||
String titleImpl() {
|
||||
JsonElement json = sendMessage("title");
|
||||
return json.getAsJsonObject().get("value").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void type(String selector, String text, TypeOptions options) {
|
||||
withLogging("Frame.type", () -> typeImpl(selector, text, options));
|
||||
}
|
||||
|
||||
void typeImpl(String selector, String text, TypeOptions options) {
|
||||
if (options == null) {
|
||||
options = new TypeOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("text", text);
|
||||
sendMessage("type", params, timeout(options.timeout));
|
||||
sendMessage("type", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncheck(String selector, UncheckOptions options) {
|
||||
withLogging("Frame.uncheck", () -> uncheckImpl(selector, options));
|
||||
}
|
||||
|
||||
void uncheckImpl(String selector, UncheckOptions options) {
|
||||
if (options == null) {
|
||||
options = new UncheckOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
sendMessage("uncheck", params, timeout(options.timeout));
|
||||
sendMessage("uncheck", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -810,13 +864,17 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public JSHandle waitForFunction(String pageFunction, Object arg, WaitForFunctionOptions options) {
|
||||
return withLogging("Frame.waitForFunction", () -> waitForFunctionImpl(pageFunction, arg, options));
|
||||
}
|
||||
|
||||
JSHandle waitForFunctionImpl(String pageFunction, Object arg, WaitForFunctionOptions options) {
|
||||
if (options == null) {
|
||||
options = new WaitForFunctionOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("waitForFunction", params, timeout(options.timeout));
|
||||
JsonElement json = sendMessage("waitForFunction", params);
|
||||
JsonObject element = json.getAsJsonObject().getAsJsonObject("handle");
|
||||
return connection.getExistingObject(element.get("guid").getAsString());
|
||||
}
|
||||
@@ -973,7 +1031,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
List<Waitable<Response>> waitables = new ArrayList<>();
|
||||
if (matcher == null) {
|
||||
matcher = UrlMatcher.forOneOf(page.context().baseUrl(), options.url, this.connection.localUtils, false);
|
||||
matcher = UrlMatcher.forOneOf(page.context().baseUrl, options.url);
|
||||
}
|
||||
logger.log("waiting for navigation " + matcher);
|
||||
waitables.add(new WaitForNavigationHelper(matcher, options.waitUntil, logger));
|
||||
@@ -985,6 +1043,10 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public ElementHandle waitForSelector(String selector, WaitForSelectorOptions options) {
|
||||
return withLogging("Frame.waitForSelector", () -> waitForSelectorImpl(selector, options));
|
||||
}
|
||||
|
||||
ElementHandle waitForSelectorImpl(String selector, WaitForSelectorOptions options) {
|
||||
return waitForSelectorImpl(selector, options, false);
|
||||
}
|
||||
|
||||
@@ -995,7 +1057,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("omitReturnValue", omitReturnValue);
|
||||
JsonElement json = sendMessage("waitForSelector", params, timeout(options.timeout));
|
||||
JsonElement json = sendMessage("waitForSelector", params);
|
||||
JsonObject element = json.getAsJsonObject().getAsJsonObject("element");
|
||||
if (element == null) {
|
||||
return null;
|
||||
@@ -1005,14 +1067,18 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public void waitForTimeout(double timeout) {
|
||||
withLogging("Frame.waitForTimeout", () -> waitForTimeoutImpl(timeout));
|
||||
}
|
||||
|
||||
void waitForTimeoutImpl(double timeout) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("waitTimeout", timeout);
|
||||
sendMessage("waitForTimeout", params, NO_TIMEOUT);
|
||||
params.addProperty("timeout", timeout);
|
||||
sendMessage("waitForTimeout", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waitForURL(String url, WaitForURLOptions options) {
|
||||
waitForURL(UrlMatcher.forGlob(page.context().baseUrl(), url, this.connection.localUtils, false), options);
|
||||
waitForURL(new UrlMatcher(page.context().baseUrl, url), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1047,60 +1113,14 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
int queryCount(String selector) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject result = sendMessage("queryCount", params, NO_TIMEOUT).getAsJsonObject();
|
||||
JsonObject result = sendMessage("queryCount", params).getAsJsonObject();
|
||||
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);
|
||||
sendMessage("highlight", params);
|
||||
}
|
||||
|
||||
protected void handleEvent(String event, JsonObject params) {
|
||||
@@ -1112,7 +1132,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);
|
||||
}
|
||||
@@ -1132,30 +1151,4 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
internalListeners.notify(InternalEventType.NAVIGATED, params);
|
||||
}
|
||||
}
|
||||
|
||||
protected double timeout(Double timeout) {
|
||||
if (page != null) {
|
||||
return page.timeoutSettings.timeout(timeout);
|
||||
}
|
||||
return new TimeoutSettings().timeout(timeout);
|
||||
}
|
||||
|
||||
protected double navigationTimeout(Double timeout) {
|
||||
if (page != null) {
|
||||
return page.timeoutSettings.navigationTimeout(timeout);
|
||||
}
|
||||
return new TimeoutSettings().navigationTimeout(timeout);
|
||||
}
|
||||
|
||||
FrameExpectResult expect(String expression, FrameExpectOptions options, String title) {
|
||||
return withTitle(title, () -> expect(expression, options));
|
||||
}
|
||||
|
||||
FrameExpectResult expect(String expression, FrameExpectOptions options) {
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("expression", expression);
|
||||
JsonElement json = sendMessage("expect", params, options.timeout);
|
||||
FrameExpectResult result = gson().fromJson(json, FrameExpectResult.class);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
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 com.microsoft.playwright.Request;
|
||||
@@ -28,7 +26,6 @@ import java.nio.file.Path;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.microsoft.playwright.impl.ChannelOwner.NO_TIMEOUT;
|
||||
import static com.microsoft.playwright.impl.LoggingSupport.*;
|
||||
import static com.microsoft.playwright.impl.Serialization.fromNameValues;
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
@@ -44,7 +41,7 @@ public class HARRouter {
|
||||
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("file", harFile.toString());
|
||||
JsonObject json = localUtils.sendMessage("harOpen", params, NO_TIMEOUT).getAsJsonObject();
|
||||
JsonObject json = localUtils.sendMessage("harOpen", params).getAsJsonObject();
|
||||
if (json.has("error")) {
|
||||
throw new PlaywrightException(json.get("error").getAsString());
|
||||
}
|
||||
@@ -64,7 +61,7 @@ public class HARRouter {
|
||||
params.addProperty("postData", base64);
|
||||
}
|
||||
params.addProperty("isNavigationRequest", request.isNavigationRequest());
|
||||
JsonObject response = localUtils.sendMessage("harLookup", params, NO_TIMEOUT).getAsJsonObject();
|
||||
JsonObject response = localUtils.sendMessage("harLookup", params).getAsJsonObject();
|
||||
|
||||
String action = response.get("action").getAsString();
|
||||
if ("redirect".equals(action)) {
|
||||
@@ -84,7 +81,7 @@ public class HARRouter {
|
||||
if (status == -1) {
|
||||
return;
|
||||
}
|
||||
Map<String, String> headers = mergeSetCookieHeaders(response.getAsJsonArray("headers"));
|
||||
Map<String, String> headers = fromNameValues(response.getAsJsonArray("headers"));
|
||||
byte[] buffer = Base64.getDecoder().decode(response.get("body").getAsString());
|
||||
route.fulfill(new Route.FulfillOptions()
|
||||
.setStatus(status)
|
||||
@@ -107,25 +104,6 @@ public class HARRouter {
|
||||
route.abort();
|
||||
}
|
||||
|
||||
private static Map<String, String> mergeSetCookieHeaders(JsonArray headersArray) {
|
||||
Map<String, String> result = new java.util.LinkedHashMap<>();
|
||||
for (JsonElement element : headersArray) {
|
||||
JsonObject pair = element.getAsJsonObject();
|
||||
String name = pair.get("name").getAsString();
|
||||
String value = pair.get("value").getAsString();
|
||||
if ("set-cookie".equalsIgnoreCase(name)) {
|
||||
if (!result.containsKey("set-cookie")) {
|
||||
result.put("set-cookie", value);
|
||||
} else {
|
||||
result.put("set-cookie", result.get("set-cookie") + "\n" + value);
|
||||
}
|
||||
} else {
|
||||
result.put(name, value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("harId", harId);
|
||||
|
||||
@@ -41,58 +41,70 @@ public class JSHandleImpl extends ChannelOwner implements JSHandle {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
try {
|
||||
sendMessage("dispose");
|
||||
} catch (TargetClosedError e) {
|
||||
}
|
||||
withLogging("JSHandle.dispose", () -> {
|
||||
try {
|
||||
sendMessage("dispose");
|
||||
} catch (TargetClosedError e) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evaluate(String pageFunction, Object arg) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.addProperty("world", "main");
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpression", params, NO_TIMEOUT);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
return withLogging("JSHandle.evaluate", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.addProperty("world", "main");
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpression", params);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSHandle evaluateHandle(String pageFunction, Object arg) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.addProperty("world", "main");
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpressionHandle", params, NO_TIMEOUT);
|
||||
return connection.getExistingObject(json.getAsJsonObject().getAsJsonObject("handle").get("guid").getAsString());
|
||||
return withLogging("JSHandle.evaluateHandle", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.addProperty("world", "main");
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpressionHandle", params);
|
||||
return connection.getExistingObject(json.getAsJsonObject().getAsJsonObject("handle").get("guid").getAsString());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, JSHandle> getProperties() {
|
||||
JsonObject json = sendMessage("getPropertyList").getAsJsonObject();
|
||||
Map<String, JSHandle> result = new HashMap<>();
|
||||
for (JsonElement e : json.getAsJsonArray("properties")) {
|
||||
JsonObject item = e.getAsJsonObject();
|
||||
JSHandle value = connection.getExistingObject(item.getAsJsonObject("value").get("guid").getAsString());
|
||||
result.put(item.get("name").getAsString(), value);
|
||||
}
|
||||
return result;
|
||||
return withLogging("JSHandle.getProperties", () -> {
|
||||
JsonObject json = sendMessage("getPropertyList").getAsJsonObject();
|
||||
Map<String, JSHandle> result = new HashMap<>();
|
||||
for (JsonElement e : json.getAsJsonArray("properties")) {
|
||||
JsonObject item = e.getAsJsonObject();
|
||||
JSHandle value = connection.getExistingObject(item.getAsJsonObject("value").get("guid").getAsString());
|
||||
result.put(item.get("name").getAsString(), value);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSHandle getProperty(String propertyName) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("name", propertyName);
|
||||
JsonObject json = sendMessage("getProperty", params, NO_TIMEOUT).getAsJsonObject();
|
||||
return connection.getExistingObject(json.getAsJsonObject("handle").get("guid").getAsString());
|
||||
return withLogging("JSHandle.getProperty", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("name", propertyName);
|
||||
JsonObject json = sendMessage("getProperty", params).getAsJsonObject();
|
||||
return connection.getExistingObject(json.getAsJsonObject("handle").get("guid").getAsString());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object jsonValue() {
|
||||
JsonObject json = sendMessage("jsonValue").getAsJsonObject();
|
||||
SerializedValue value = gson().fromJson(json.get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
return withLogging("JSHandle.jsonValue", () -> {
|
||||
JsonObject json = sendMessage("jsonValue").getAsJsonObject();
|
||||
SerializedValue value = gson().fromJson(json.get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -44,7 +44,7 @@ class JsonPipe extends ChannelOwner implements Transport {
|
||||
checkIfClosed();
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("message", message);
|
||||
sendMessage("send", params, NO_TIMEOUT);
|
||||
sendMessage("send", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,7 +19,6 @@ package com.microsoft.playwright.impl;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Keyboard;
|
||||
|
||||
import static com.microsoft.playwright.impl.ChannelOwner.NO_TIMEOUT;
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
|
||||
class KeyboardImpl implements Keyboard {
|
||||
@@ -31,21 +30,25 @@ class KeyboardImpl implements Keyboard {
|
||||
|
||||
@Override
|
||||
public void down(String key) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("key", key);
|
||||
page.sendMessage("keyboardDown", params, NO_TIMEOUT);
|
||||
page.withLogging("Keyboard.down", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("key", key);
|
||||
page.sendMessage("keyboardDown", params);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertText(String text) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("text", text);
|
||||
page.sendMessage("keyboardInsertText", params, NO_TIMEOUT);
|
||||
page.withLogging("Keyboard.insertText", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("text", text);
|
||||
page.sendMessage("keyboardInsertText", params);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void press(String key, PressOptions options) {
|
||||
pressImpl(key, options);
|
||||
page.withLogging("Keyboard.press", () -> pressImpl(key, options));
|
||||
}
|
||||
|
||||
private void pressImpl(String key, PressOptions options) {
|
||||
@@ -54,12 +57,12 @@ class KeyboardImpl implements Keyboard {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("key", key);
|
||||
page.sendMessage("keyboardPress", params, NO_TIMEOUT);
|
||||
page.sendMessage("keyboardPress", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void type(String text, TypeOptions options) {
|
||||
typeImpl(text, options);
|
||||
page.withLogging("Keyboard.type", () -> typeImpl(text, options));
|
||||
}
|
||||
|
||||
private void typeImpl(String text, TypeOptions options) {
|
||||
@@ -68,13 +71,15 @@ class KeyboardImpl implements Keyboard {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("text", text);
|
||||
page.sendMessage("keyboardType", params, NO_TIMEOUT);
|
||||
page.sendMessage("keyboardType", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void up(String key) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("key", key);
|
||||
page.sendMessage("keyboardUp", params, NO_TIMEOUT);
|
||||
page.withLogging("Keyboard.up", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("key", key);
|
||||
page.sendMessage("keyboardUp", params);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,40 +21,33 @@ import com.google.gson.JsonObject;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
|
||||
public class LocalUtils extends ChannelOwner {
|
||||
class LocalUtils extends ChannelOwner {
|
||||
LocalUtils(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
markAsInternalType();
|
||||
}
|
||||
|
||||
JsonArray deviceDescriptors() {
|
||||
return initializer.getAsJsonArray("deviceDescriptors");
|
||||
}
|
||||
|
||||
void zip(Path zipFile, JsonArray entries, String stacksId, boolean appendMode, boolean includeSources, List<String> additionalSources) {
|
||||
void zip(Path zipFile, JsonArray entries, String stacksId, boolean appendMode, boolean includeSources) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("zipFile", zipFile.toString());
|
||||
params.add("entries", entries);
|
||||
params.addProperty("mode", appendMode ? "append" : "write");
|
||||
params.addProperty("stacksId", stacksId);
|
||||
params.addProperty("includeSources", includeSources);
|
||||
if (!additionalSources.isEmpty()) {
|
||||
JsonArray sourcesArray = new JsonArray();
|
||||
for (String source : additionalSources) {
|
||||
sourcesArray.add(source);
|
||||
}
|
||||
params.add("additionalSources", sourcesArray);
|
||||
}
|
||||
sendMessage("zip", params, NO_TIMEOUT);
|
||||
sendMessage("zip", params);
|
||||
}
|
||||
|
||||
void traceDiscarded(String stacksId) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("stacksId", stacksId);
|
||||
sendMessage("traceDiscarded", params, NO_TIMEOUT);
|
||||
sendMessage("traceDiscarded", params);
|
||||
}
|
||||
|
||||
String tracingStarted(String tracesDir, String traceName) {
|
||||
@@ -63,19 +56,7 @@ public class LocalUtils extends ChannelOwner {
|
||||
params.addProperty("tracesDir", "");
|
||||
}
|
||||
params.addProperty("traceName", traceName);
|
||||
JsonObject json = connection.localUtils().sendMessage("tracingStarted", params, NO_TIMEOUT).getAsJsonObject();
|
||||
JsonObject json = connection.localUtils().sendMessage("tracingStarted", params).getAsJsonObject();
|
||||
return json.get("stacksId").getAsString();
|
||||
}
|
||||
|
||||
public Pattern globToRegex(String glob, String baseURL, boolean webSocketUrl) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("glob", glob);
|
||||
if (baseURL != null) {
|
||||
params.addProperty("baseURL", baseURL);
|
||||
}
|
||||
params.addProperty("webSocketUrl", webSocketUrl);
|
||||
JsonObject json = connection.localUtils().sendMessage("globToRegex", params, NO_TIMEOUT).getAsJsonObject();
|
||||
String regex = json.get("regex").getAsString();
|
||||
return Pattern.compile(regex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.microsoft.playwright.Locator;
|
||||
import com.microsoft.playwright.assertions.LocatorAssertions;
|
||||
import com.microsoft.playwright.options.AriaRole;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -30,39 +31,12 @@ import static com.microsoft.playwright.impl.Serialization.serializeArgument;
|
||||
import static com.microsoft.playwright.impl.Utils.convertType;
|
||||
|
||||
public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAssertions {
|
||||
LocatorImpl actualLocator;
|
||||
|
||||
public LocatorAssertionsImpl(Locator locator) {
|
||||
this(locator, false);
|
||||
}
|
||||
|
||||
private LocatorAssertionsImpl(Locator locator, boolean isNot) {
|
||||
super(isNot);
|
||||
this.actualLocator = (LocatorImpl) locator;
|
||||
}
|
||||
|
||||
@Override
|
||||
FrameExpectResult doExpect(String expression, FrameExpectOptions expectOptions, String title) {
|
||||
return actualLocator.expect(expression, expectOptions, title);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void containsClass(String classname, ContainsClassOptions options) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = classname;
|
||||
expectImpl("to.contain.class", expected, classname, "Locator expected to contain class", convertType(options, FrameExpectOptions.class), "Assert \"containsClass\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsClass(List<String> classnames, ContainsClassOptions options) {
|
||||
List<ExpectedTextValue> list = new ArrayList<>();
|
||||
for (String text : classnames) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = text;
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.contain.class.array", list, classnames, "Locator expected to contain classes", convertType(options, FrameExpectOptions.class), "Assert \"containsClass\"");
|
||||
super((LocatorImpl) locator, isNot);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,7 +46,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.matchSubstring = true;
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.text", expected, text, "Locator expected to contain text", convertType(options, FrameExpectOptions.class), "Assert \"containsText\"");
|
||||
expectImpl("to.have.text", expected, text, "Locator expected to contain text", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -81,7 +55,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.matchSubstring = true;
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.text", expected, pattern, "Locator expected to contain regex", convertType(options, FrameExpectOptions.class), "Assert \"containsText\"");
|
||||
expectImpl("to.have.text", expected, pattern, "Locator expected to contain regex", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,7 +69,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.normalizeWhiteSpace = true;
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.contain.text.array", list, strings, "Locator expected to contain text", convertType(options, FrameExpectOptions.class), "Assert \"containsText\"");
|
||||
expectImpl("to.contain.text.array", list, strings, "Locator expected to contain text", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -108,7 +82,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.normalizeWhiteSpace = true;
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.contain.text.array", list, patterns, "Locator expected to contain text", convertType(options, FrameExpectOptions.class), "Assert \"containsText\"");
|
||||
expectImpl("to.contain.text.array", list, patterns, "Locator expected to contain text", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -117,7 +91,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.string = description;
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.accessible.description", expected, description, "Locator expected to have accessible description", convertType(options, FrameExpectOptions.class), "Assert \"hasAccessibleDescription\"");
|
||||
expectImpl("to.have.accessible.description", expected, description, "Locator expected to have accessible description", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -125,7 +99,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.accessible.description", expected, pattern, "Locator expected to have accessible description", convertType(options, FrameExpectOptions.class), "Assert \"hasAccessibleDescription\"");
|
||||
expectImpl("to.have.accessible.description", expected, pattern, "Locator expected to have accessible description", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -134,7 +108,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.string = errorMessage;
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.accessible.error.message", expected, errorMessage, "Locator expected to have accessible error message", convertType(options, FrameExpectOptions.class), "Assert \"hasAccessibleErrorMessage\"");
|
||||
expectImpl("to.have.accessible.error.message", expected, errorMessage, "Locator expected to have accessible error message", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -142,7 +116,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.accessible.error.message", expected, pattern, "Locator expected to have accessible error message", convertType(options, FrameExpectOptions.class), "Assert \"hasAccessibleErrorMessage\"");
|
||||
expectImpl("to.have.accessible.error.message", expected, pattern, "Locator expected to have accessible error message", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -151,7 +125,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.string = name;
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.accessible.name", expected, name, "Locator expected to have accessible name", convertType(options, FrameExpectOptions.class), "Assert \"hasAccessibleName\"");
|
||||
expectImpl("to.have.accessible.name", expected, name, "Locator expected to have accessible name", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -159,7 +133,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.accessible.name", expected, pattern, "Locator expected to have accessible name", convertType(options, FrameExpectOptions.class), "Assert \"hasAccessibleName\"");
|
||||
expectImpl("to.have.accessible.name", expected, pattern, "Locator expected to have accessible name", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -187,20 +161,20 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
if (expectedValue instanceof Pattern) {
|
||||
message += " matching regex";
|
||||
}
|
||||
expectImpl("to.have.attribute.value", expectedText, expectedValue, message, commonOptions, "Assert \"hasAttribute\"");
|
||||
expectImpl("to.have.attribute.value", expectedText, expectedValue, message, commonOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasClass(String text, HasClassOptions options) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = text;
|
||||
expectImpl("to.have.class", expected, text, "Locator expected to have class", convertType(options, FrameExpectOptions.class), "Assert \"hasClass\"");
|
||||
expectImpl("to.have.class", expected, text, "Locator expected to have class", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasClass(Pattern pattern, HasClassOptions options) {
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
expectImpl("to.have.class", expected, pattern, "Locator expected to have class matching regex", convertType(options, FrameExpectOptions.class), "Assert \"hasClass\"");
|
||||
expectImpl("to.have.class", expected, pattern, "Locator expected to have class matching regex", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -211,7 +185,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.string = text;
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.have.class.array", list, strings, "Locator expected to have class", convertType(options, FrameExpectOptions.class), "Assert \"hasClass\"");
|
||||
expectImpl("to.have.class.array", list, strings, "Locator expected to have class", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -221,7 +195,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.have.class.array", list, patterns, "Locator expected to have class matching regex", convertType(options, FrameExpectOptions.class), "Assert \"hasClass\"");
|
||||
expectImpl("to.have.class.array", list, patterns, "Locator expected to have class matching regex", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -232,7 +206,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
FrameExpectOptions commonOptions = convertType(options, FrameExpectOptions.class);
|
||||
commonOptions.expectedNumber = (double) count;
|
||||
List<ExpectedTextValue> expectedText = null;
|
||||
expectImpl("to.have.count", expectedText, count, "Locator expected to have count", commonOptions, "Assert \"hasCount\"");
|
||||
expectImpl("to.have.count", expectedText, count, "Locator expected to have count", commonOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -258,20 +232,20 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
if (expectedValue instanceof Pattern) {
|
||||
message += " matching regex";
|
||||
}
|
||||
expectImpl("to.have.css", expectedText, expectedValue, message, commonOptions, "Assert \"hasCSS\"");
|
||||
expectImpl("to.have.css", expectedText, expectedValue, message, commonOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasId(String id, HasIdOptions options) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = id;
|
||||
expectImpl("to.have.id", expected, id, "Locator expected to have ID", convertType(options, FrameExpectOptions.class), "Assert \"hasId\"");
|
||||
expectImpl("to.have.id", expected, id, "Locator expected to have ID", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasId(Pattern pattern, HasIdOptions options) {
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
expectImpl("to.have.id", expected, pattern, "Locator expected to have ID matching regex", convertType(options, FrameExpectOptions.class), "Assert \"hasId\"");
|
||||
expectImpl("to.have.id", expected, pattern, "Locator expected to have ID matching regex", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -283,14 +257,14 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
commonOptions.expressionArg = name;
|
||||
commonOptions.expectedValue = serializeArgument(value);
|
||||
List<ExpectedTextValue> list = null;
|
||||
expectImpl("to.have.property", list, value, "Locator expected to have JavaScript property '" + name + "'", commonOptions, "Assert \"hasJSProperty\"");
|
||||
expectImpl("to.have.property", list, value, "Locator expected to have JavaScript property '" + name + "'", commonOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasRole(AriaRole role, HasRoleOptions options) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = role.toString().toLowerCase();
|
||||
expectImpl("to.have.role", expected, expected.string, "Locator expected to have role", convertType(options, FrameExpectOptions.class), "Assert \"hasRole\"");
|
||||
expectImpl("to.have.role", expected, expected.string, "Locator expected to have role", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -300,7 +274,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.matchSubstring = false;
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.text", expected, text, "Locator expected to have text", convertType(options, FrameExpectOptions.class), "Assert \"hasText\"");
|
||||
expectImpl("to.have.text", expected, text, "Locator expected to have text", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -310,7 +284,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
// Just match substring, same as containsText.
|
||||
expected.matchSubstring = true;
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.text", expected, pattern, "Locator expected to have text matching regex", convertType(options, FrameExpectOptions.class), "Assert \"hasText\"");
|
||||
expectImpl("to.have.text", expected, pattern, "Locator expected to have text matching regex", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -324,7 +298,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.normalizeWhiteSpace = true;
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.have.text.array", list, strings, "Locator expected to have text", convertType(options, FrameExpectOptions.class), "Assert \"hasText\"");
|
||||
expectImpl("to.have.text.array", list, strings, "Locator expected to have text", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -337,20 +311,20 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.normalizeWhiteSpace = true;
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.have.text.array", list, patterns, "Locator expected to have text matching regex", convertType(options, FrameExpectOptions.class), "Assert \"hasText\"");
|
||||
expectImpl("to.have.text.array", list, patterns, "Locator expected to have text matching regex", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasValue(String value, HasValueOptions options) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = value;
|
||||
expectImpl("to.have.value", expected, value, "Locator expected to have value", convertType(options, FrameExpectOptions.class), "Assert \"hasValue\"");
|
||||
expectImpl("to.have.value", expected, value, "Locator expected to have value", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasValue(Pattern pattern, HasValueOptions options) {
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
expectImpl("to.have.value", expected, pattern, "Locator expected to have value matching regex", convertType(options, FrameExpectOptions.class), "Assert \"hasValue\"");
|
||||
expectImpl("to.have.value", expected, pattern, "Locator expected to have value matching regex", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -361,7 +335,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.string = text;
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.have.values", list, values, "Locator expected to have values", convertType(options, FrameExpectOptions.class), "Assert \"hasValues\"");
|
||||
expectImpl("to.have.values", list, values, "Locator expected to have values", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -372,7 +346,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
expected.matchSubstring = true;
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.have.values", list, patterns, "Locator expected to have values matching regex", convertType(options, FrameExpectOptions.class), "Assert \"hasValues\"");
|
||||
expectImpl("to.have.values", list, patterns, "Locator expected to have values matching regex", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -382,7 +356,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
}
|
||||
FrameExpectOptions options = convertType(snapshotOptions, FrameExpectOptions.class);
|
||||
options.expectedValue = serializeArgument(expected);
|
||||
expectImpl("to.match.aria", options, expected,"Locator expected to match Aria snapshot", "Assert \"matchesAriaSnapshot\"");
|
||||
expectImpl("to.match.aria", options, expected,"Locator expected to match Aria snapshot");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -410,12 +384,12 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
String message = "Locator expected to be";
|
||||
FrameExpectOptions expectOptions = convertType(options, FrameExpectOptions.class);
|
||||
expectOptions.expectedValue = serializeArgument(expectedValue);
|
||||
expectImpl("to.be.checked", expectOptions, expected, message, "Assert \"isChecked\"");
|
||||
expectImpl("to.be.checked", expectOptions, expected, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isDisabled(IsDisabledOptions options) {
|
||||
expectTrue("to.be.disabled", "Locator expected to be disabled", convertType(options, FrameExpectOptions.class), "Assert \"isDisabled\"");
|
||||
expectTrue("to.be.disabled", "Locator expected to be disabled", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -423,12 +397,12 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
FrameExpectOptions frameOptions = convertType(options, FrameExpectOptions.class);
|
||||
boolean editable = options == null || options.editable == null || options.editable == true;
|
||||
String message = "Locator expected to be " + (editable ? "editable" : "readonly");
|
||||
expectTrue(editable ? "to.be.editable" : "to.be.readonly", message, frameOptions, "Assert \"isEditable\"");
|
||||
expectTrue(editable ? "to.be.editable" : "to.be.readonly", message, frameOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isEmpty(IsEmptyOptions options) {
|
||||
expectTrue("to.be.empty", "Locator expected to be empty", convertType(options, FrameExpectOptions.class), "Assert \"isEmpty\"");
|
||||
expectTrue("to.be.empty", "Locator expected to be empty", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -436,17 +410,17 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
FrameExpectOptions frameOptions = convertType(options, FrameExpectOptions.class);
|
||||
boolean enabled = options == null || options.enabled == null || options.enabled == true;
|
||||
String message = "Locator expected to be " + (enabled ? "enabled" : "disabled");
|
||||
expectTrue(enabled ? "to.be.enabled" : "to.be.disabled", message, frameOptions, "Assert \"isEnabled\"");
|
||||
expectTrue(enabled ? "to.be.enabled" : "to.be.disabled", message, frameOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isFocused(IsFocusedOptions options) {
|
||||
expectTrue("to.be.focused", "Locator expected to be focused", convertType(options, FrameExpectOptions.class), "Assert \"isFocused\"");
|
||||
expectTrue("to.be.focused", "Locator expected to be focused", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isHidden(IsHiddenOptions options) {
|
||||
expectTrue("to.be.hidden", "Locator expected to be hidden", convertType(options, FrameExpectOptions.class), "Assert \"isHidden\"");
|
||||
expectTrue("to.be.hidden", "Locator expected to be hidden", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -455,7 +429,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
if (options != null && options.ratio != null) {
|
||||
expectOptions.expectedNumber = options.ratio;
|
||||
}
|
||||
expectTrue("to.be.in.viewport", "Locator expected to be in viewport", expectOptions, "Assert \"isInViewport\"");
|
||||
expectTrue("to.be.in.viewport", "Locator expected to be in viewport", expectOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -463,12 +437,12 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
FrameExpectOptions frameOptions = convertType(options, FrameExpectOptions.class);
|
||||
boolean visible = options == null || options.visible == null || options.visible == true;
|
||||
String message = "Locator expected to be " + (visible ? "visible" : "hidden");
|
||||
expectTrue(visible ? "to.be.visible" : "to.be.hidden", message, frameOptions, "Assert \"isVisible\"");
|
||||
expectTrue(visible ? "to.be.visible" : "to.be.hidden", message, frameOptions);
|
||||
}
|
||||
|
||||
private void expectTrue(String expression, String message, FrameExpectOptions options, String title) {
|
||||
private void expectTrue(String expression, String message, FrameExpectOptions options) {
|
||||
List<ExpectedTextValue> expectedText = null;
|
||||
expectImpl(expression, expectedText, null, message, options, title);
|
||||
expectImpl(expression, expectedText, null, message, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -481,6 +455,6 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
FrameExpectOptions frameOptions = convertType(options, FrameExpectOptions.class);
|
||||
boolean attached = options == null || options.attached == null || options.attached == true;
|
||||
String message = "Locator expected to be " + (attached ? "attached" : "detached");
|
||||
expectTrue(attached ? "to.be.attached" : "to.be.detached", message, frameOptions, "Assert \"isAttached\"");
|
||||
expectTrue(attached ? "to.be.attached" : "to.be.detached", message, frameOptions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.*;
|
||||
import com.microsoft.playwright.options.*;
|
||||
@@ -24,7 +25,6 @@ import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.microsoft.playwright.impl.LocatorUtils.*;
|
||||
@@ -67,25 +67,27 @@ class LocatorImpl implements Locator {
|
||||
this.selector = selector;
|
||||
}
|
||||
|
||||
private <R, O> R withElement(BiFunction<ElementHandle, O, R> callback, O options, String title) {
|
||||
return frame.withTitle(title, () -> {
|
||||
ElementHandleOptions handleOptions = convertType(options, ElementHandleOptions.class);
|
||||
// TODO: support deadline based timeout
|
||||
// Double timeout = null;
|
||||
// if (handleOptions != null) {
|
||||
// timeout = handleOptions.timeout;
|
||||
// }
|
||||
// timeout = frame.page.timeoutSettings.timeout(timeout);
|
||||
// long deadline = System.nanoTime() + (long) timeout.doubleValue() * 1_000_000;
|
||||
ElementHandle handle = elementHandle(handleOptions);
|
||||
try {
|
||||
return callback.apply(handle, options);
|
||||
} finally {
|
||||
if (handle != null) {
|
||||
handle.dispose();
|
||||
}
|
||||
private static String escapeWithQuotes(String text) {
|
||||
return gson().toJson(text);
|
||||
}
|
||||
|
||||
private <R, O> R withElement(BiFunction<ElementHandle, O, R> callback, O options) {
|
||||
ElementHandleOptions handleOptions = convertType(options, ElementHandleOptions.class);
|
||||
// TODO: support deadline based timeout
|
||||
// Double timeout = null;
|
||||
// if (handleOptions != null) {
|
||||
// timeout = handleOptions.timeout;
|
||||
// }
|
||||
// timeout = frame.page.timeoutSettings.timeout(timeout);
|
||||
// long deadline = System.nanoTime() + (long) timeout.doubleValue() * 1_000_000;
|
||||
ElementHandle handle = elementHandle(handleOptions);
|
||||
try {
|
||||
return callback.apply(handle, options);
|
||||
} finally {
|
||||
if (handle != null) {
|
||||
handle.dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -108,14 +110,6 @@ class LocatorImpl implements Locator {
|
||||
return (List<String>) frame.evalOnSelectorAll(selector, "ee => ee.map(e => e.textContent || '')");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator normalize() {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject result = frame.sendMessage("resolveSelector", params, ChannelOwner.NO_TIMEOUT).getAsJsonObject();
|
||||
return new LocatorImpl(frame, result.get("resolvedSelector").getAsString(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator and(Locator locator) {
|
||||
LocatorImpl other = (LocatorImpl) locator;
|
||||
@@ -126,29 +120,37 @@ class LocatorImpl implements Locator {
|
||||
|
||||
@Override
|
||||
public String ariaSnapshot(AriaSnapshotOptions options) {
|
||||
return frame.withLogging("Locator.ariaSnapshot", () -> ariaSnapshotImpl(options));
|
||||
}
|
||||
|
||||
private String ariaSnapshotImpl(AriaSnapshotOptions options) {
|
||||
if (options == null) {
|
||||
options = new AriaSnapshotOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
JsonObject result = frame.sendMessage("ariaSnapshot", params, frame.timeout(options.timeout)).getAsJsonObject();
|
||||
JsonObject result = frame.sendMessage("ariaSnapshot", params).getAsJsonObject();
|
||||
return result.get("snapshot").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blur(BlurOptions options) {
|
||||
frame.withLogging("Locator.blur", () -> blurImpl(options));
|
||||
}
|
||||
|
||||
private void blurImpl(BlurOptions options) {
|
||||
if (options == null) {
|
||||
options = new BlurOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("strict", true);
|
||||
frame.sendMessage("blur", params, frame.timeout(options.timeout));
|
||||
frame.sendMessage("blur", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoundingBox boundingBox(BoundingBoxOptions options) {
|
||||
return withElement((h, o) -> h.boundingBox(), options, "Bounding Box");
|
||||
return withElement((h, o) -> h.boundingBox(), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -161,7 +163,7 @@ class LocatorImpl implements Locator {
|
||||
|
||||
@Override
|
||||
public void clear(ClearOptions options) {
|
||||
frame.withTitle("Clear", () -> fill("", convertType(options, FillOptions.class)));
|
||||
fill("", convertType(options, FillOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -169,7 +171,7 @@ class LocatorImpl implements Locator {
|
||||
if (options == null) {
|
||||
options = new ClickOptions();
|
||||
}
|
||||
frame.clickImpl(selector, convertType(options, Frame.ClickOptions.class).setStrict(true), options.steps);
|
||||
frame.click(selector, convertType(options, Frame.ClickOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -177,38 +179,12 @@ class LocatorImpl implements Locator {
|
||||
return frame.queryCount(selector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator describe(String description) {
|
||||
return locator(describeSelector(description));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
// Match internal:describe= at the end of the selector with a JSON string
|
||||
// Pattern matches: >> internal:describe="..." where ... is a JSON-encoded string
|
||||
Pattern pattern = Pattern.compile(" >> internal:describe=(\"(?:[^\"\\\\]|\\\\.)*\")$");
|
||||
Matcher matcher = pattern.matcher(selector);
|
||||
|
||||
if (matcher.find()) {
|
||||
String jsonString = matcher.group(1);
|
||||
try {
|
||||
// Deserialize the JSON string
|
||||
return gson().fromJson(jsonString, String.class);
|
||||
} catch (Exception e) {
|
||||
// If we can't parse it, return null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dblclick(DblclickOptions options) {
|
||||
if (options == null) {
|
||||
options = new DblclickOptions();
|
||||
}
|
||||
frame.dblclickImpl(selector, convertType(options, Frame.DblclickOptions.class).setStrict(true), options.steps);
|
||||
frame.dblclick(selector, convertType(options, Frame.DblclickOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -226,7 +202,7 @@ class LocatorImpl implements Locator {
|
||||
}
|
||||
Frame.DragAndDropOptions frameOptions = convertType(options, Frame.DragAndDropOptions.class);
|
||||
frameOptions.setStrict(true);
|
||||
frame.dragAndDropImpl(selector, ((LocatorImpl) target).selector, frameOptions, options.steps);
|
||||
frame.dragAndDrop(selector, ((LocatorImpl) target).selector, frameOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -252,7 +228,7 @@ class LocatorImpl implements Locator {
|
||||
|
||||
@Override
|
||||
public Object evaluate(String expression, Object arg, EvaluateOptions options) {
|
||||
return withElement((h, o) -> h.evaluate(expression, arg), options, "Evaluate");
|
||||
return withElement((h, o) -> h.evaluate(expression, arg), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -262,7 +238,7 @@ class LocatorImpl implements Locator {
|
||||
|
||||
@Override
|
||||
public JSHandle evaluateHandle(String expression, Object arg, EvaluateHandleOptions options) {
|
||||
return withElement((h, o) -> h.evaluateHandle(expression, arg), options, "Evaluate");
|
||||
return withElement((h, o) -> h.evaluateHandle(expression, arg), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -371,20 +347,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
|
||||
@@ -519,7 +483,7 @@ class LocatorImpl implements Locator {
|
||||
|
||||
@Override
|
||||
public byte[] screenshot(ScreenshotOptions options) {
|
||||
return withElement((h, o) -> h.screenshot(o), convertType(options, ElementHandle.ScreenshotOptions.class), "Screenshot");
|
||||
return withElement((h, o) -> h.screenshot(o), convertType(options, ElementHandle.ScreenshotOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -527,7 +491,7 @@ class LocatorImpl implements Locator {
|
||||
withElement((h, o) -> {
|
||||
h.scrollIntoViewIfNeeded(o);
|
||||
return null;
|
||||
}, convertType(options, ElementHandle.ScrollIntoViewIfNeededOptions.class), "Scroll into view");
|
||||
}, convertType(options, ElementHandle.ScrollIntoViewIfNeededOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -583,7 +547,7 @@ class LocatorImpl implements Locator {
|
||||
withElement((h, o) -> {
|
||||
h.selectText(o);
|
||||
return null;
|
||||
}, convertType(options, ElementHandle.SelectTextOptions.class), "Select text");
|
||||
}, convertType(options, ElementHandle.SelectTextOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -663,15 +627,15 @@ class LocatorImpl implements Locator {
|
||||
if (options == null) {
|
||||
options = new WaitForOptions();
|
||||
}
|
||||
frame.waitForSelectorImpl(selector, convertType(options, Frame.WaitForSelectorOptions.class).setStrict(true), true);
|
||||
waitForImpl(options);
|
||||
}
|
||||
|
||||
private void waitForImpl(WaitForOptions options) {
|
||||
frame.withLogging("Locator.waitFor", () -> frame.waitForSelectorImpl(selector, convertType(options, Frame.WaitForSelectorOptions.class).setStrict(true), true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String description = description();
|
||||
if (description != null) {
|
||||
return description;
|
||||
}
|
||||
return "Locator@" + selector;
|
||||
}
|
||||
|
||||
@@ -689,9 +653,8 @@ class LocatorImpl implements Locator {
|
||||
return frame.hashCode() ^ selector.hashCode();
|
||||
}
|
||||
|
||||
FrameExpectResult expect(String expression, FrameExpectOptions options, String title) {
|
||||
options.selector = selector;
|
||||
return frame.expect(expression, options, title);
|
||||
FrameExpectResult expect(String expression, FrameExpectOptions options) {
|
||||
return frame.withLogging("Locator.expect", () -> expectImpl(expression, options));
|
||||
}
|
||||
|
||||
JsonObject toProtocol() {
|
||||
@@ -700,4 +663,13 @@ class LocatorImpl implements Locator {
|
||||
result.addProperty("selector", selector);
|
||||
return result;
|
||||
}
|
||||
|
||||
private FrameExpectResult expectImpl(String expression, FrameExpectOptions options) {
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("expression", expression);
|
||||
JsonElement json = frame.sendMessage("expect", params);
|
||||
FrameExpectResult result = gson().fromJson(json, FrameExpectResult.class);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,12 +39,9 @@ public class LocatorUtils {
|
||||
return "internal:attr=[" + attrName + "=" + escapeForAttributeSelector(value, exact) + "]";
|
||||
}
|
||||
|
||||
static String describeSelector(String description) {
|
||||
return "internal:describe=" + gson().toJson(description);
|
||||
}
|
||||
|
||||
static String getByTestIdSelector(Object testId, PlaywrightImpl playwright) {
|
||||
return getByAttributeTextSelector(playwright.selectors.testIdAttributeName, testId, true);
|
||||
String testIdAttributeName = ((SharedSelectors) playwright.selectors()).testIdAttributeName;
|
||||
return getByAttributeTextSelector(testIdAttributeName, testId, true);
|
||||
}
|
||||
|
||||
static String getByAltTextSelector(Object text, Locator.GetByAltTextOptions options) {
|
||||
@@ -86,10 +83,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());
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.microsoft.playwright.impl;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
class LoggingSupport {
|
||||
private static final boolean isEnabled;
|
||||
@@ -30,6 +31,29 @@ class LoggingSupport {
|
||||
private static final DateTimeFormatter timestampFormat = DateTimeFormatter.ofPattern(
|
||||
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX").withZone(ZoneId.of("UTC"));
|
||||
|
||||
void withLogging(String apiName, Runnable code) {
|
||||
withLogging(apiName, () -> {
|
||||
code.run();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
<T> T withLogging(String apiName, Supplier<T> code) {
|
||||
if (isEnabled) {
|
||||
logApi("=> " + apiName + " started");
|
||||
}
|
||||
boolean success = false;
|
||||
try {
|
||||
T result = code.get();
|
||||
success = true;
|
||||
return result;
|
||||
} finally {
|
||||
if (isEnabled) {
|
||||
logApi("<= " + apiName + (success ? " succeeded" : " failed"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void logWithTimestamp(String message) {
|
||||
// This matches log format produced by the server.
|
||||
String timestamp = ZonedDateTime.now().format(timestampFormat);
|
||||
|
||||
@@ -19,7 +19,6 @@ package com.microsoft.playwright.impl;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Mouse;
|
||||
|
||||
import static com.microsoft.playwright.impl.ChannelOwner.NO_TIMEOUT;
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
import static com.microsoft.playwright.impl.Utils.convertType;
|
||||
|
||||
@@ -32,18 +31,22 @@ class MouseImpl implements Mouse {
|
||||
|
||||
@Override
|
||||
public void click(double x, double y, ClickOptions options) {
|
||||
page.withLogging("Mouse.click", () -> clickImpl(x, y, options));
|
||||
}
|
||||
|
||||
private void clickImpl(double x, double y, ClickOptions options) {
|
||||
if (options == null) {
|
||||
options = new ClickOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("x", x);
|
||||
params.addProperty("y", y);
|
||||
page.sendMessage("mouseClick", params, NO_TIMEOUT);
|
||||
page.sendMessage("mouseClick", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dblclick(double x, double y, DblclickOptions options) {
|
||||
page.withTitle("Double click", () -> dblclickImpl(x, y, options));
|
||||
page.withLogging("Mouse.dblclick", () -> dblclickImpl(x, y, options));
|
||||
}
|
||||
|
||||
private void dblclickImpl(double x, double y, DblclickOptions options) {
|
||||
@@ -59,38 +62,52 @@ class MouseImpl implements Mouse {
|
||||
|
||||
@Override
|
||||
public void down(DownOptions options) {
|
||||
page.withLogging("Mouse.down", () -> downImpl(options));
|
||||
}
|
||||
|
||||
private void downImpl(DownOptions options) {
|
||||
if (options == null) {
|
||||
options = new DownOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
page.sendMessage("mouseDown", params, NO_TIMEOUT);
|
||||
page.sendMessage("mouseDown", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void move(double x, double y, MoveOptions options) {
|
||||
page.withLogging("Mouse.move", () -> moveImpl(x, y, options));
|
||||
}
|
||||
|
||||
private void moveImpl(double x, double y, MoveOptions options) {
|
||||
if (options == null) {
|
||||
options = new MoveOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("x", x);
|
||||
params.addProperty("y", y);
|
||||
page.sendMessage("mouseMove", params, NO_TIMEOUT);
|
||||
page.sendMessage("mouseMove", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void up(UpOptions options) {
|
||||
if (options == null) {
|
||||
options = new UpOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
page.sendMessage("mouseUp", params, NO_TIMEOUT);
|
||||
page.withLogging("Mouse.up", () -> upImpl(options));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void wheel(double deltaX, double deltaY) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("deltaX", deltaX);
|
||||
params.addProperty("deltaY", deltaY);
|
||||
page.sendMessage("mouseWheel", params, NO_TIMEOUT);
|
||||
page.withLogging("Mouse.wheel", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("deltaX", deltaX);
|
||||
params.addProperty("deltaY", deltaY);
|
||||
page.sendMessage("mouseWheel", params);
|
||||
});
|
||||
}
|
||||
|
||||
private void upImpl(UpOptions options) {
|
||||
if (options == null) {
|
||||
options = new UpOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
page.sendMessage("mouseUp", params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.microsoft.playwright.assertions.PageAssertions;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.microsoft.playwright.impl.LocatorAssertionsImpl.shouldIgnoreCase;
|
||||
import static com.microsoft.playwright.impl.UrlMatcher.resolveUrl;
|
||||
import static com.microsoft.playwright.impl.Utils.convertType;
|
||||
|
||||
@@ -32,55 +33,39 @@ public class PageAssertionsImpl extends AssertionsBase implements PageAssertions
|
||||
}
|
||||
|
||||
private PageAssertionsImpl(Page page, boolean isNot) {
|
||||
super(isNot);
|
||||
super((LocatorImpl) page.locator(":root"), isNot);
|
||||
this.actualPage = (PageImpl) page;
|
||||
}
|
||||
|
||||
@Override
|
||||
FrameExpectResult doExpect(String expression, FrameExpectOptions expectOptions, String title) {
|
||||
FrameImpl frame = (FrameImpl) actualPage.mainFrame();
|
||||
return frame.expect(expression, expectOptions, title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasTitle(String title, HasTitleOptions options) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = title;
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.title", expected, title, "Page title expected to be", convertType(options, FrameExpectOptions.class), "Assert \"hasTitle\"");
|
||||
expectImpl("to.have.title", expected, title, "Page title expected to be", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasTitle(Pattern pattern, HasTitleOptions options) {
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
expectImpl("to.have.title", expected, pattern, "Page title expected to match regex", convertType(options, FrameExpectOptions.class), "Assert \"hasTitle\"");
|
||||
expectImpl("to.have.title", expected, pattern, "Page title expected to match regex", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasURL(String url, HasURLOptions options) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
if (actualPage.context().baseUrl() != null) {
|
||||
url = resolveUrl(actualPage.context().baseUrl(), url);
|
||||
if (actualPage.context().baseUrl != null) {
|
||||
url = resolveUrl(actualPage.context().baseUrl, url);
|
||||
}
|
||||
expected.string = url;
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expectImpl("to.have.url", expected, url, "Page URL expected to be", convertType(options, FrameExpectOptions.class), "Assert \"hasURL\"");
|
||||
expectImpl("to.have.url", expected, url, "Page URL expected to be", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasURL(Pattern pattern, HasURLOptions options) {
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
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\"");
|
||||
expectImpl("to.have.url", expected, pattern, "Page URL expected to match regex", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -52,6 +52,7 @@ public class PlaywrightImpl extends ChannelOwner implements Playwright {
|
||||
Connection connection = new Connection(new PipeTransport(p.getInputStream(), p.getOutputStream()), env);
|
||||
PlaywrightImpl result = connection.initializePlaywright();
|
||||
result.driverProcess = p;
|
||||
result.initSharedSelectors(null);
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to launch driver", e);
|
||||
@@ -61,8 +62,9 @@ public class PlaywrightImpl extends ChannelOwner implements Playwright {
|
||||
private final BrowserTypeImpl chromium;
|
||||
private final BrowserTypeImpl firefox;
|
||||
private final BrowserTypeImpl webkit;
|
||||
private final SelectorsImpl selectors;
|
||||
private final APIRequestImpl apiRequest;
|
||||
protected SelectorsImpl selectors;
|
||||
private SharedSelectors sharedSelectors;
|
||||
|
||||
PlaywrightImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
@@ -70,20 +72,26 @@ public class PlaywrightImpl extends ChannelOwner implements Playwright {
|
||||
firefox = parent.connection.getExistingObject(initializer.getAsJsonObject("firefox").get("guid").getAsString());
|
||||
webkit = parent.connection.getExistingObject(initializer.getAsJsonObject("webkit").get("guid").getAsString());
|
||||
|
||||
chromium.playwright = this;
|
||||
firefox.playwright = this;
|
||||
webkit.playwright = this;
|
||||
|
||||
selectors = new SelectorsImpl();
|
||||
selectors = connection.getExistingObject(initializer.getAsJsonObject("selectors").get("guid").getAsString());
|
||||
apiRequest = new APIRequestImpl(this);
|
||||
}
|
||||
|
||||
public LocalUtils localUtils() {
|
||||
return connection.localUtils;
|
||||
void initSharedSelectors(PlaywrightImpl parent) {
|
||||
assert sharedSelectors == null;
|
||||
if (parent == null) {
|
||||
sharedSelectors = new SharedSelectors();
|
||||
} else {
|
||||
sharedSelectors = parent.sharedSelectors;
|
||||
}
|
||||
sharedSelectors.addChannel(selectors);
|
||||
}
|
||||
|
||||
void unregisterSelectors() {
|
||||
sharedSelectors.removeChannel(selectors);
|
||||
}
|
||||
|
||||
public JsonArray deviceDescriptors() {
|
||||
return localUtils().deviceDescriptors();
|
||||
return connection.localUtils.deviceDescriptors();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -108,7 +116,7 @@ public class PlaywrightImpl extends ChannelOwner implements Playwright {
|
||||
|
||||
@Override
|
||||
public Selectors selectors() {
|
||||
return selectors;
|
||||
return sharedSelectors;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// This file is generated by generate_java_rpc.js, do not edit manually.
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import java.util.List;
|
||||
@@ -103,7 +105,6 @@ class ExpectedTextValue {
|
||||
class FrameExpectOptions {
|
||||
Object expressionArg;
|
||||
List<ExpectedTextValue> expectedText;
|
||||
String selector;
|
||||
Double expectedNumber;
|
||||
SerializedArgument expectedValue;
|
||||
Boolean useInnerText;
|
||||
@@ -112,13 +113,8 @@ class FrameExpectOptions {
|
||||
}
|
||||
|
||||
class FrameExpectResult {
|
||||
static class Received {
|
||||
SerializedValue value;
|
||||
String ariaSnapshot;
|
||||
}
|
||||
boolean matches;
|
||||
Received received;
|
||||
String errorMessage;
|
||||
SerializedValue received;
|
||||
List<String> log;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,4 +26,8 @@ public class RemoteBrowser extends ChannelOwner {
|
||||
BrowserImpl browser() {
|
||||
return connection.getExistingObject(initializer.getAsJsonObject("browser").get("guid").getAsString());
|
||||
}
|
||||
|
||||
SelectorsImpl selectors() {
|
||||
return connection.getExistingObject(initializer.getAsJsonObject("selectors").get("guid").getAsString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,6 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
String failure;
|
||||
Timing timing;
|
||||
boolean didFailOrFinish;
|
||||
ResponseImpl existingResponse;
|
||||
private FallbackOverrides fallbackOverrides;
|
||||
|
||||
static class FallbackOverrides {
|
||||
@@ -54,6 +53,7 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
|
||||
RequestImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
markAsInternalType();
|
||||
|
||||
if (initializer.has("redirectedFrom")) {
|
||||
redirectedFrom = connection.getExistingObject(initializer.getAsJsonObject("redirectedFrom").get("guid").getAsString());
|
||||
@@ -67,14 +67,9 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseImpl existingResponse() {
|
||||
return existingResponse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> allHeaders() {
|
||||
return getRawHeaders().headers();
|
||||
return withLogging("Request.allHeaders", () -> getRawHeaders().headers());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -103,12 +98,12 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
|
||||
@Override
|
||||
public List<HttpHeader> headersArray() {
|
||||
return getRawHeaders().headersArray();
|
||||
return withLogging("Request.headersArray", () -> getRawHeaders().headersArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String headerValue(String name) {
|
||||
return getRawHeaders().get(name);
|
||||
return withLogging("Request.headerValue", () -> getRawHeaders().get(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -158,21 +153,25 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
|
||||
@Override
|
||||
public ResponseImpl response() {
|
||||
JsonObject result = sendMessage("response").getAsJsonObject();
|
||||
if (!result.has("response")) {
|
||||
return null;
|
||||
}
|
||||
return connection.getExistingObject(result.getAsJsonObject("response").get("guid").getAsString());
|
||||
return withLogging("Request.response", () -> {
|
||||
JsonObject result = sendMessage("response").getAsJsonObject();
|
||||
if (!result.has("response")) {
|
||||
return null;
|
||||
}
|
||||
return connection.getExistingObject(result.getAsJsonObject("response").get("guid").getAsString());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sizes sizes() {
|
||||
ResponseImpl response = response();
|
||||
if (response == null) {
|
||||
throw new PlaywrightException("Unable to fetch sizes for failed request");
|
||||
}
|
||||
JsonObject json = response.sendMessage("sizes").getAsJsonObject();
|
||||
return gson().fromJson(json.getAsJsonObject("sizes"), Sizes.class);
|
||||
return withLogging("Request.sizes", () -> {
|
||||
ResponseImpl response = response();
|
||||
if (response == null) {
|
||||
throw new PlaywrightException("Unable to fetch sizes for failed request");
|
||||
}
|
||||
JsonObject json = response.sendMessage("sizes").getAsJsonObject();
|
||||
return gson().fromJson(json.getAsJsonObject("sizes"), Sizes.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -199,8 +198,10 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
if (rawHeaders != null) {
|
||||
return rawHeaders;
|
||||
}
|
||||
JsonObject result = sendMessage("rawRequestHeaders").getAsJsonObject();
|
||||
JsonArray rawHeadersJson = result.getAsJsonArray("headers");
|
||||
JsonArray rawHeadersJson = withLogging("Request.allHeaders", () -> {
|
||||
JsonObject result = sendMessage("rawRequestHeaders").getAsJsonObject();
|
||||
return result.getAsJsonArray("headers");
|
||||
});
|
||||
|
||||
// The field may have been initialized in a nested call but it is ok.
|
||||
rawHeaders = new RawHeaders(asList(gson().fromJson(rawHeadersJson, HttpHeader[].class)));
|
||||
|
||||
@@ -36,25 +36,27 @@ import static java.util.Arrays.asList;
|
||||
public class ResponseImpl extends ChannelOwner implements Response {
|
||||
private final RawHeaders headers;
|
||||
private RawHeaders rawHeaders;
|
||||
final RequestImpl request;
|
||||
private final RequestImpl request;
|
||||
|
||||
ResponseImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
markAsInternalType();
|
||||
headers = new RawHeaders(asList(gson().fromJson(initializer.getAsJsonArray("headers"), HttpHeader[].class)));
|
||||
request = connection.getExistingObject(initializer.getAsJsonObject("request").get("guid").getAsString());
|
||||
request.existingResponse = this;
|
||||
request.timing = gson().fromJson(initializer.get("timing"), Timing.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> allHeaders() {
|
||||
return getRawHeaders().headers();
|
||||
return withLogging("Response.allHeaders", () -> getRawHeaders().headers());
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] body() {
|
||||
JsonObject json = sendMessage("body").getAsJsonObject();
|
||||
return Base64.getDecoder().decode(json.get("binary").getAsString());
|
||||
return withLogging("Response.body", () -> {
|
||||
JsonObject json = sendMessage("body").getAsJsonObject();
|
||||
return Base64.getDecoder().decode(json.get("binary").getAsString());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -87,12 +89,6 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
||||
return initializer.get("fromServiceWorker").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String httpVersion() {
|
||||
JsonObject result = sendMessage("httpVersion").getAsJsonObject();
|
||||
return result.get("value").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> headers() {
|
||||
return headers.headers();
|
||||
@@ -100,7 +96,7 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
||||
|
||||
@Override
|
||||
public List<HttpHeader> headersArray() {
|
||||
return getRawHeaders().headersArray();
|
||||
return withLogging("Response.headersArray", () -> getRawHeaders().headersArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -125,20 +121,24 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
||||
|
||||
@Override
|
||||
public SecurityDetails securityDetails() {
|
||||
JsonObject json = sendMessage("securityDetails").getAsJsonObject();
|
||||
if (json.has("value")) {
|
||||
return gson().fromJson(json.get("value"), SecurityDetails.class);
|
||||
}
|
||||
return null;
|
||||
return withLogging("Response.securityDetails", () -> {
|
||||
JsonObject json = sendMessage("securityDetails").getAsJsonObject();
|
||||
if (json.has("value")) {
|
||||
return gson().fromJson(json.get("value"), SecurityDetails.class);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerAddr serverAddr() {
|
||||
JsonObject json = sendMessage("serverAddr").getAsJsonObject();
|
||||
if (json.has("value")) {
|
||||
return gson().fromJson(json.get("value"), ServerAddr.class);
|
||||
}
|
||||
return null;
|
||||
return withLogging("Response.serverAddr", () -> {
|
||||
JsonObject json = sendMessage("serverAddr").getAsJsonObject();
|
||||
if (json.has("value")) {
|
||||
return gson().fromJson(json.get("value"), ServerAddr.class);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -38,15 +38,18 @@ public class RouteImpl extends ChannelOwner implements Route {
|
||||
|
||||
public RouteImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
markAsInternalType();
|
||||
request = connection.getExistingObject(initializer.getAsJsonObject("request").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abort(String errorCode) {
|
||||
startHandling();
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("errorCode", errorCode);
|
||||
sendMessageAsync("abort", params);
|
||||
withLogging("Route.abort", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("errorCode", errorCode);
|
||||
sendMessageAsync("abort", params);
|
||||
});
|
||||
}
|
||||
|
||||
boolean isHandled() {
|
||||
@@ -61,7 +64,7 @@ public class RouteImpl extends ChannelOwner implements Route {
|
||||
void resume(ResumeOptions options, boolean isFallback) {
|
||||
startHandling();
|
||||
applyOverrides(convertType(options, FallbackOptions.class));
|
||||
resumeImpl(request().fallbackOverridesForResume(), isFallback);
|
||||
withLogging("Route.resume", () -> resumeImpl(request().fallbackOverridesForResume(), isFallback));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -149,7 +152,7 @@ public class RouteImpl extends ChannelOwner implements Route {
|
||||
@Override
|
||||
public void fulfill(FulfillOptions options) {
|
||||
startHandling();
|
||||
fulfillImpl(options);
|
||||
withLogging("Route.fulfill", () -> fulfillImpl(options));
|
||||
}
|
||||
|
||||
private void fulfillImpl(FulfillOptions options) {
|
||||
|
||||
@@ -1,46 +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 int viewportWidth;
|
||||
private final int viewportHeight;
|
||||
|
||||
ScreencastFrameImpl(byte[] data, int viewportWidth, int viewportHeight) {
|
||||
this.data = data;
|
||||
this.viewportWidth = viewportWidth;
|
||||
this.viewportHeight = viewportHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] data() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int viewportWidth() {
|
||||
return viewportWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int viewportHeight() {
|
||||
return viewportHeight;
|
||||
}
|
||||
}
|
||||
@@ -1,160 +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.PlaywrightException;
|
||||
import com.microsoft.playwright.Screencast;
|
||||
import com.microsoft.playwright.ScreencastFrame;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.microsoft.playwright.impl.ChannelOwner.NO_TIMEOUT;
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
|
||||
class ScreencastImpl implements Screencast {
|
||||
private final PageImpl page;
|
||||
private boolean started;
|
||||
private Path savePath;
|
||||
private Consumer<ScreencastFrame> onFrame;
|
||||
private ArtifactImpl artifact;
|
||||
|
||||
ScreencastImpl(PageImpl page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
void handleScreencastFrame(JsonObject params) {
|
||||
if (onFrame == null) {
|
||||
return;
|
||||
}
|
||||
String dataBase64 = params.get("data").getAsString();
|
||||
byte[] data = java.util.Base64.getDecoder().decode(dataBase64);
|
||||
int viewportWidth = params.get("viewportWidth").getAsInt();
|
||||
int viewportHeight = params.get("viewportHeight").getAsInt();
|
||||
onFrame.accept(new ScreencastFrameImpl(data, viewportWidth, viewportHeight));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoCloseable start(StartOptions options) {
|
||||
if (started) {
|
||||
throw new PlaywrightException("Screencast is already started");
|
||||
}
|
||||
started = true;
|
||||
JsonObject params = new JsonObject();
|
||||
if (options != null) {
|
||||
if (options.onFrame != null) {
|
||||
onFrame = options.onFrame;
|
||||
}
|
||||
if (options.quality != null) {
|
||||
params.addProperty("quality", options.quality);
|
||||
}
|
||||
params.addProperty("sendFrames", options.onFrame != null);
|
||||
params.addProperty("record", options.path != null);
|
||||
savePath = options.path;
|
||||
} else {
|
||||
params.addProperty("sendFrames", false);
|
||||
params.addProperty("record", false);
|
||||
}
|
||||
JsonObject result = page.sendMessage("screencastStart", params, NO_TIMEOUT).getAsJsonObject();
|
||||
if (result.has("artifact")) {
|
||||
String artifactGuid = result.getAsJsonObject("artifact").get("guid").getAsString();
|
||||
artifact = page.connection.getExistingObject(artifactGuid);
|
||||
}
|
||||
return new DisposableStub(this::stop);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
started = false;
|
||||
onFrame = null;
|
||||
page.sendMessage("screencastStop", new JsonObject(), NO_TIMEOUT);
|
||||
if (savePath != null && artifact != null) {
|
||||
artifact.saveAs(savePath);
|
||||
}
|
||||
artifact = null;
|
||||
savePath = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoCloseable showOverlay(String html, ShowOverlayOptions options) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("html", html);
|
||||
if (options != null && options.duration != null) {
|
||||
params.addProperty("duration", options.duration);
|
||||
}
|
||||
JsonObject result = (JsonObject) page.sendMessage("screencastShowOverlay", params, NO_TIMEOUT);
|
||||
String id = result.get("id").getAsString();
|
||||
return () -> {
|
||||
JsonObject removeParams = new JsonObject();
|
||||
removeParams.addProperty("id", id);
|
||||
page.sendMessage("screencastRemoveOverlay", removeParams, NO_TIMEOUT);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showChapter(String title, ShowChapterOptions options) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("title", title);
|
||||
if (options != null) {
|
||||
if (options.description != null) {
|
||||
params.addProperty("description", options.description);
|
||||
}
|
||||
if (options.duration != null) {
|
||||
params.addProperty("duration", options.duration);
|
||||
}
|
||||
}
|
||||
page.sendMessage("screencastChapter", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoCloseable showActions(ShowActionsOptions options) {
|
||||
JsonObject params = new JsonObject();
|
||||
if (options != null) {
|
||||
if (options.duration != null) {
|
||||
params.addProperty("duration", options.duration);
|
||||
}
|
||||
if (options.fontSize != null) {
|
||||
params.addProperty("fontSize", options.fontSize);
|
||||
}
|
||||
if (options.position != null) {
|
||||
params.add("position", gson().toJsonTree(options.position));
|
||||
}
|
||||
}
|
||||
page.sendMessage("screencastShowActions", params, NO_TIMEOUT);
|
||||
return new DisposableStub(this::hideActions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showOverlays() {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("visible", true);
|
||||
page.sendMessage("screencastSetOverlayVisible", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideActions() {
|
||||
page.sendMessage("screencastHideActions", new JsonObject(), NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideOverlays() {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("visible", false);
|
||||
page.sendMessage("screencastSetOverlayVisible", params, NO_TIMEOUT);
|
||||
}
|
||||
}
|
||||
@@ -17,72 +17,28 @@
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import com.microsoft.playwright.Selectors;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
|
||||
import static com.microsoft.playwright.impl.ChannelOwner.NO_TIMEOUT;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
public class SelectorsImpl extends LoggingSupport implements Selectors {
|
||||
protected final List<BrowserContextImpl> contextsForSelectors = new ArrayList<>();
|
||||
protected final List<JsonObject> selectorEngines = new ArrayList<>();
|
||||
|
||||
String testIdAttributeName = "data-testid";
|
||||
|
||||
@Override
|
||||
public void setTestIdAttribute(String attributeName) {
|
||||
if (attributeName == null) {
|
||||
throw new PlaywrightException("Test id attribute cannot be null");
|
||||
}
|
||||
testIdAttributeName = attributeName;
|
||||
for (BrowserContextImpl context : contextsForSelectors) {
|
||||
try {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("testIdAttributeName", attributeName);
|
||||
context.sendMessageAsync("setTestIdAttributeName", params);
|
||||
} catch (PlaywrightException e) {
|
||||
}
|
||||
}
|
||||
class SelectorsImpl extends ChannelOwner {
|
||||
SelectorsImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(String name, String script, RegisterOptions options) {
|
||||
registerImpl(name, script, options);
|
||||
void register(String name, String script, Selectors.RegisterOptions options) {
|
||||
if (options == null) {
|
||||
options = new Selectors.RegisterOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("name", name);
|
||||
params.addProperty("source", script);
|
||||
sendMessage("register", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(String name, Path path, RegisterOptions options) {
|
||||
byte[] buffer;
|
||||
try {
|
||||
buffer = Files.readAllBytes(path);
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to read selector from file: " + path, e);
|
||||
}
|
||||
registerImpl(name, new String(buffer, UTF_8), options);
|
||||
}
|
||||
|
||||
private void registerImpl(String name, String script, RegisterOptions options) {
|
||||
if (selectorEngines.stream().anyMatch(engine -> name.equals(engine.get("name").getAsString()))) {
|
||||
throw new PlaywrightException("selectors.register: \"" + name + "\" selector engine has been already registered");
|
||||
}
|
||||
|
||||
JsonObject engine = new JsonObject();
|
||||
engine.addProperty("name", name);
|
||||
engine.addProperty("source", script);
|
||||
if (options != null && options.contentScript != null) {
|
||||
engine.addProperty("contentScript", options.contentScript);
|
||||
}
|
||||
for (BrowserContextImpl context : contextsForSelectors) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("selectorEngine", engine);
|
||||
context.sendMessage("registerSelectorEngine", params, NO_TIMEOUT);
|
||||
}
|
||||
selectorEngines.add(engine);
|
||||
void setTestIdAttributeName(String name) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("testIdAttributeName", name);
|
||||
sendMessageAsync("setTestIdAttributeName", params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,8 +58,6 @@ class Serialization {
|
||||
.registerTypeAdapter(ScreenshotCaret.class, new ToLowerCaseSerializer<ScreenshotCaret>())
|
||||
.registerTypeAdapter(ServiceWorkerPolicy.class, new ToLowerCaseAndDashSerializer<ServiceWorkerPolicy>())
|
||||
.registerTypeAdapter(MouseButton.class, new ToLowerCaseSerializer<MouseButton>())
|
||||
.registerTypeAdapter(ConsoleMessagesFilter.class, new ConsoleMessagesFilterSerializer())
|
||||
.registerTypeAdapter(AriaSnapshotMode.class, new ToLowerCaseSerializer<AriaSnapshotMode>())
|
||||
.registerTypeAdapter(LoadState.class, new ToLowerCaseSerializer<LoadState>())
|
||||
.registerTypeAdapter(WaitUntilState.class, new ToLowerCaseSerializer<WaitUntilState>())
|
||||
.registerTypeAdapter(WaitForSelectorState.class, new ToLowerCaseSerializer<WaitForSelectorState>())
|
||||
@@ -68,7 +66,6 @@ class Serialization {
|
||||
.registerTypeHierarchyAdapter(JSHandleImpl.class, new HandleSerializer())
|
||||
.registerTypeAdapter((new TypeToken<Map<String, String>>(){}).getType(), new StringMapSerializer())
|
||||
.registerTypeAdapter((new TypeToken<Map<String, Object>>(){}).getType(), new FirefoxUserPrefsSerializer())
|
||||
.registerTypeAdapter(LocatorImpl.class, new LocatorImplSerializer())
|
||||
.registerTypeHierarchyAdapter(Path.class, new PathSerializer()).create();
|
||||
|
||||
static Gson gson() {
|
||||
@@ -425,18 +422,6 @@ class Serialization {
|
||||
return result;
|
||||
}
|
||||
|
||||
static String parseError(JsonObject object) {
|
||||
SerializedError error = gson().fromJson(object, SerializedError.class);
|
||||
String errorStr = "";
|
||||
if (error.error != null) {
|
||||
errorStr = error.error.name + ": " + error.error.message;
|
||||
if (error.error.stack != null && !error.error.stack.isEmpty()) {
|
||||
errorStr += "\n" + error.error.stack;
|
||||
}
|
||||
}
|
||||
return errorStr;
|
||||
}
|
||||
|
||||
private static class OptionalSerializer implements JsonSerializer<Optional<?>> {
|
||||
private static boolean isSupported(Type type) {
|
||||
return new TypeToken<Optional<Media>>() {}.getType().getTypeName().equals(type.getTypeName()) ||
|
||||
@@ -484,17 +469,6 @@ class Serialization {
|
||||
}
|
||||
}
|
||||
|
||||
private static class ConsoleMessagesFilterSerializer implements JsonSerializer<ConsoleMessagesFilter> {
|
||||
@Override
|
||||
public JsonElement serialize(ConsoleMessagesFilter src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
switch (src) {
|
||||
case ALL: return new JsonPrimitive("all");
|
||||
case SINCE_NAVIGATION: return new JsonPrimitive("since-navigation");
|
||||
default: throw new PlaywrightException("Unknown ConsoleMessagesFilter: " + src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ToLowerCaseAndDashSerializer<E extends Enum<E>> implements JsonSerializer<E> {
|
||||
@Override
|
||||
public JsonElement serialize(E src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
@@ -516,13 +490,6 @@ class Serialization {
|
||||
}
|
||||
}
|
||||
|
||||
private static class LocatorImplSerializer implements JsonSerializer<LocatorImpl> {
|
||||
@Override
|
||||
public JsonElement serialize(LocatorImpl src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return src.toProtocol();
|
||||
}
|
||||
}
|
||||
|
||||
private static class SameSiteAdapter extends TypeAdapter<SameSiteAttribute> {
|
||||
@Override
|
||||
public void write(JsonWriter out, SameSiteAttribute value) throws IOException {
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.PlaywrightException;
|
||||
import com.microsoft.playwright.Selectors;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
public class SharedSelectors extends LoggingSupport implements Selectors {
|
||||
private final List<SelectorsImpl> channels = new ArrayList<>();
|
||||
private final List<Registration> registrations = new ArrayList<>();
|
||||
|
||||
String testIdAttributeName = "data-testid";
|
||||
|
||||
private static class Registration {
|
||||
final String name;
|
||||
final String script;
|
||||
final RegisterOptions options;
|
||||
|
||||
Registration(String name, String script, RegisterOptions options) {
|
||||
this.name = name;
|
||||
this.script = script;
|
||||
this.options = options;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(String name, String script, RegisterOptions options) {
|
||||
withLogging("Selectors.register", () -> registerImpl(name, script, options));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(String name, Path path, RegisterOptions options) {
|
||||
withLogging("Selectors.register", () -> {
|
||||
byte[] buffer;
|
||||
try {
|
||||
buffer = Files.readAllBytes(path);
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to read selector from file: " + path, e);
|
||||
}
|
||||
registerImpl(name, new String(buffer, UTF_8), options);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTestIdAttribute(String attributeName) {
|
||||
if (attributeName == null) {
|
||||
throw new PlaywrightException("Test id attribute cannot be null");
|
||||
}
|
||||
testIdAttributeName = attributeName;
|
||||
channels.forEach(channel -> channel.setTestIdAttributeName(testIdAttributeName));
|
||||
}
|
||||
|
||||
void addChannel(SelectorsImpl channel) {
|
||||
registrations.forEach(r -> {
|
||||
try {
|
||||
channel.register(r.name, r.script, r.options);
|
||||
} catch (PlaywrightException e) {
|
||||
// This should not fail except for connection closure, but just in case we catch.
|
||||
}
|
||||
channel.setTestIdAttributeName(testIdAttributeName);
|
||||
});
|
||||
channels.add(channel);
|
||||
}
|
||||
|
||||
void removeChannel(SelectorsImpl channel) {
|
||||
channels.remove(channel);
|
||||
}
|
||||
|
||||
private void registerImpl(String name, String script, RegisterOptions options) {
|
||||
channels.forEach(impl -> impl.register(name, script, options));
|
||||
registrations.add(new Registration(name, script, options));
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ public class Stream extends ChannelOwner {
|
||||
}
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("size", len);
|
||||
JsonObject json = sendMessage("read", params, NO_TIMEOUT).getAsJsonObject();
|
||||
JsonObject json = sendMessage("read", params).getAsJsonObject();
|
||||
String encoded = json.get("binary").getAsString();
|
||||
if (encoded.isEmpty()) {
|
||||
return -1;
|
||||
|
||||
@@ -18,7 +18,6 @@ package com.microsoft.playwright.impl;
|
||||
|
||||
class TimeoutSettings {
|
||||
private static final int DEFAULT_TIMEOUT_MS = 30_000;
|
||||
private static final int DEFAULT_LAUNCH_TIMEOUT_MS = 180_000;
|
||||
|
||||
private final TimeoutSettings parent;
|
||||
private Double defaultTimeout ;
|
||||
@@ -81,11 +80,4 @@ class TimeoutSettings {
|
||||
}
|
||||
return new WaitableTimeout<>(timeout(timeout));
|
||||
}
|
||||
|
||||
static double launchTimeout(Double timeout) {
|
||||
if (timeout != null) {
|
||||
return timeout;
|
||||
}
|
||||
return DEFAULT_LAUNCH_TIMEOUT_MS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,6 @@ package com.microsoft.playwright.impl;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Touchscreen;
|
||||
|
||||
import static com.microsoft.playwright.impl.ChannelOwner.NO_TIMEOUT;
|
||||
|
||||
class TouchscreenImpl implements Touchscreen {
|
||||
private final PageImpl page;
|
||||
|
||||
@@ -30,9 +28,11 @@ class TouchscreenImpl implements Touchscreen {
|
||||
|
||||
@Override
|
||||
public void tap(double x, double y) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("x", x);
|
||||
params.addProperty("y", y);
|
||||
page.sendMessage("touchscreenTap", params, NO_TIMEOUT);
|
||||
page.withLogging("Touchscreen.tap", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("x", x);
|
||||
params.addProperty("y", y);
|
||||
page.sendMessage("touchscreenTap", params);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,21 +18,10 @@ 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 {
|
||||
@@ -40,22 +29,11 @@ class TracingImpl extends ChannelOwner implements Tracing {
|
||||
private Path tracesDir;
|
||||
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) {
|
||||
super(parent, type, guid, initializer);
|
||||
markAsInternalType();
|
||||
}
|
||||
|
||||
private void stopChunkImpl(Path path) {
|
||||
@@ -65,13 +43,10 @@ class TracingImpl extends ChannelOwner implements Tracing {
|
||||
}
|
||||
JsonObject params = new JsonObject();
|
||||
|
||||
List<String> capturedAdditionalSources = new ArrayList<>(additionalSources);
|
||||
additionalSources.clear();
|
||||
|
||||
// Not interested in artifacts.
|
||||
if (path == null) {
|
||||
params.addProperty("mode", "discard");
|
||||
sendMessage("tracingStopChunk", params, NO_TIMEOUT);
|
||||
sendMessage("tracingStopChunk", params);
|
||||
if (stacksId != null) {
|
||||
connection.localUtils().traceDiscarded(stacksId);
|
||||
}
|
||||
@@ -81,14 +56,14 @@ class TracingImpl extends ChannelOwner implements Tracing {
|
||||
boolean isLocal = !connection.isRemote;
|
||||
if (isLocal) {
|
||||
params.addProperty("mode", "entries");
|
||||
JsonObject json = sendMessage("tracingStopChunk", params, NO_TIMEOUT).getAsJsonObject();
|
||||
JsonObject json = sendMessage("tracingStopChunk", params).getAsJsonObject();
|
||||
JsonArray entries = json.getAsJsonArray("entries");
|
||||
connection.localUtils.zip(path, entries, stacksId, false, includeSources, capturedAdditionalSources);
|
||||
connection.localUtils.zip(path, entries, stacksId, false, includeSources);
|
||||
return;
|
||||
}
|
||||
|
||||
params.addProperty("mode", "archive");
|
||||
JsonObject json = sendMessage("tracingStopChunk", params, NO_TIMEOUT).getAsJsonObject();
|
||||
JsonObject json = sendMessage("tracingStopChunk", params).getAsJsonObject();
|
||||
// The artifact may be missing if the browser closed while stopping tracing.
|
||||
if (!json.has("artifact")) {
|
||||
if (stacksId != null) {
|
||||
@@ -100,7 +75,7 @@ class TracingImpl extends ChannelOwner implements Tracing {
|
||||
artifact.saveAs(path);
|
||||
artifact.delete();
|
||||
|
||||
connection.localUtils.zip(path, new JsonArray(), stacksId, true, includeSources, capturedAdditionalSources);
|
||||
connection.localUtils.zip(path, new JsonArray(), stacksId, true, includeSources);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -112,26 +87,22 @@ class TracingImpl extends ChannelOwner implements Tracing {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoCloseable group(String name, GroupOptions options) {
|
||||
groupImpl(name, options);
|
||||
return new DisposableStub(this::groupEnd);
|
||||
public void group(String name, GroupOptions options) {
|
||||
withLogging("Tracing.group", () -> groupImpl(name, options));
|
||||
}
|
||||
|
||||
private void groupImpl(String name, GroupOptions options) {
|
||||
if (options == null) {
|
||||
options = new GroupOptions();
|
||||
}
|
||||
if (options.location != null) {
|
||||
additionalSources.add(options.location.file);
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("name", name);
|
||||
sendMessage("tracingGroup", params, NO_TIMEOUT);
|
||||
sendMessage("tracingGroup", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void groupEnd() {
|
||||
sendMessage("tracingGroupEnd");
|
||||
withLogging("Tracing.groupEnd", () -> sendMessage("tracingGroupEnd"));
|
||||
}
|
||||
|
||||
private void tracingStartChunk(String name, String title) {
|
||||
@@ -142,7 +113,7 @@ class TracingImpl extends ChannelOwner implements Tracing {
|
||||
if (title != null) {
|
||||
params.addProperty("title", title);
|
||||
}
|
||||
JsonObject result = sendMessage("tracingStartChunk", params, NO_TIMEOUT).getAsJsonObject();
|
||||
JsonObject result = sendMessage("tracingStartChunk", params).getAsJsonObject();
|
||||
startCollectingStacks(result.get("traceName").getAsString());
|
||||
}
|
||||
|
||||
@@ -164,7 +135,7 @@ class TracingImpl extends ChannelOwner implements Tracing {
|
||||
if (includeSources) {
|
||||
params.addProperty("sources", true);
|
||||
}
|
||||
sendMessage("tracingStart", params, NO_TIMEOUT);
|
||||
sendMessage("tracingStart", params);
|
||||
tracingStartChunk(options.name, options.title);
|
||||
}
|
||||
|
||||
@@ -179,110 +150,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;
|
||||
}
|
||||
|
||||
@@ -21,22 +21,25 @@ import com.microsoft.playwright.PlaywrightException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.microsoft.playwright.impl.Utils.globToRegex;
|
||||
import static com.microsoft.playwright.impl.Utils.toJsRegexFlags;
|
||||
|
||||
class UrlMatcher {
|
||||
private final String baseURL;
|
||||
public final String glob;
|
||||
public final Pattern pattern;
|
||||
public final Predicate<String> predicate;
|
||||
|
||||
static UrlMatcher forOneOf(URL baseUrl, Object object, LocalUtils localUtils, boolean isWebSocketUrl) {
|
||||
static UrlMatcher forOneOf(URL baseUrl, Object object) {
|
||||
if (object == null) {
|
||||
return new UrlMatcher(null, null, null);
|
||||
return new UrlMatcher(null, null, null, null);
|
||||
}
|
||||
if (object instanceof String) {
|
||||
return UrlMatcher.forGlob(baseUrl, (String) object, localUtils, isWebSocketUrl);
|
||||
return new UrlMatcher(baseUrl, (String) object);
|
||||
}
|
||||
if (object instanceof Pattern) {
|
||||
return new UrlMatcher((Pattern) object);
|
||||
@@ -63,32 +66,61 @@ class UrlMatcher {
|
||||
}
|
||||
}
|
||||
|
||||
static UrlMatcher forGlob(URL baseURL, String glob, LocalUtils localUtils, boolean isWebSocketUrl) {
|
||||
Pattern pattern = localUtils.globToRegex(glob, baseURL != null ? baseURL.toString() : null, isWebSocketUrl);
|
||||
return new UrlMatcher(glob, pattern, null);
|
||||
private static String normaliseUrl(String spec) {
|
||||
try {
|
||||
// Align with the Node.js URL parser which automatically adds a slash to the path if it is empty.
|
||||
URI url = new URI(spec);
|
||||
if (url.getScheme() != null &&
|
||||
Arrays.asList("http", "https", "ws", "wss").contains(url.getScheme()) &&
|
||||
url.getPath().isEmpty()) {
|
||||
return new URI(url.getScheme(), url.getAuthority(), "/", url.getQuery(), url.getFragment()).toString();
|
||||
}
|
||||
return url.toString();
|
||||
} catch (URISyntaxException e) {
|
||||
return spec;
|
||||
}
|
||||
}
|
||||
|
||||
UrlMatcher(URL baseURL, String glob) {
|
||||
this(baseURL, glob, null, null);
|
||||
}
|
||||
|
||||
UrlMatcher(Pattern pattern) {
|
||||
this(null, pattern, null);
|
||||
this(null, null, pattern, null);
|
||||
}
|
||||
|
||||
UrlMatcher(Predicate<String> predicate) {
|
||||
this(null, null, predicate);
|
||||
this(null, null, null, predicate);
|
||||
}
|
||||
|
||||
private UrlMatcher(String glob, Pattern pattern, Predicate<String> predicate) {
|
||||
private UrlMatcher(URL baseURL, String glob, Pattern pattern, Predicate<String> predicate) {
|
||||
this.baseURL = baseURL != null ? baseURL.toString() : null;
|
||||
this.glob = glob;
|
||||
this.pattern = pattern;
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
boolean test(String value) {
|
||||
return testImpl(baseURL, pattern, predicate, glob, value);
|
||||
}
|
||||
|
||||
private static boolean testImpl(String baseURL, Pattern pattern, Predicate<String> predicate, String glob, String value) {
|
||||
if (pattern != null) {
|
||||
return pattern.matcher(value).find();
|
||||
}
|
||||
if (predicate != null) {
|
||||
return predicate.test(value);
|
||||
}
|
||||
if (glob != null) {
|
||||
if (!glob.startsWith("*")) {
|
||||
// Allow http(s) baseURL to match ws(s) urls.
|
||||
if (baseURL != null && Pattern.compile("^https?://").matcher(baseURL).find() && Pattern.compile("^wss?://").matcher(value).find()) {
|
||||
baseURL = baseURL.replaceFirst("^http", "ws");
|
||||
}
|
||||
glob = normaliseUrl(resolveUrl(baseURL, glob));
|
||||
}
|
||||
return Pattern.compile(globToRegex(glob)).matcher(value).find();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -125,12 +157,10 @@ class UrlMatcher {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (glob != null)
|
||||
return String.format("<glob pattern=\"%s\">", glob);
|
||||
if (pattern != null)
|
||||
return String.format("<regex pattern=\"%s\" flags=\"%s\">", pattern.pattern(), toJsRegexFlags(pattern));
|
||||
if (this.predicate != null)
|
||||
return "<predicate>";
|
||||
return "<true>";
|
||||
return String.format("<glob pattern=\"%s\">", glob);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
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 com.microsoft.playwright.options.ClientCertificate;
|
||||
@@ -31,11 +32,11 @@ import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.microsoft.playwright.impl.ChannelOwner.NO_TIMEOUT;
|
||||
import static com.microsoft.playwright.impl.Serialization.toJsonArray;
|
||||
import static java.nio.file.Files.readAllBytes;
|
||||
|
||||
@@ -90,6 +91,79 @@ public class Utils {
|
||||
return convertType(f, (Class<T>) f.getClass());
|
||||
}
|
||||
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions#escaping
|
||||
static Set<Character> escapeGlobChars = new HashSet<>(Arrays.asList('$', '^', '+', '.', '*', '(', ')', '|', '\\', '?', '{', '}', '[', ']'));
|
||||
|
||||
static String globToRegex(String glob) {
|
||||
StringBuilder tokens = new StringBuilder();
|
||||
tokens.append('^');
|
||||
boolean inGroup = false;
|
||||
for (int i = 0; i < glob.length(); ++i) {
|
||||
char c = glob.charAt(i);
|
||||
if (c == '\\' && i + 1 < glob.length()) {
|
||||
char nextChar = glob.charAt(++i);
|
||||
if (escapeGlobChars.contains(nextChar)) {
|
||||
tokens.append('\\');
|
||||
}
|
||||
tokens.append(nextChar);
|
||||
continue;
|
||||
}
|
||||
if (c == '*') {
|
||||
boolean beforeDeep = i < 1 || glob.charAt(i - 1) == '/';
|
||||
int starCount = 1;
|
||||
while (i + 1 < glob.length() && glob.charAt(i + 1) == '*') {
|
||||
starCount++;
|
||||
i++;
|
||||
}
|
||||
boolean afterDeep = i + 1 >= glob.length() || glob.charAt(i + 1) == '/';
|
||||
boolean isDeep = starCount > 1 && beforeDeep && afterDeep;
|
||||
if (isDeep) {
|
||||
tokens.append("((?:[^/]*(?:\\/|$))*)");
|
||||
i++;
|
||||
} else {
|
||||
tokens.append("([^/]*)");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case '?':
|
||||
tokens.append('.');
|
||||
break;
|
||||
case '[':
|
||||
tokens.append('[');
|
||||
break;
|
||||
case ']':
|
||||
tokens.append(']');
|
||||
break;
|
||||
case '{':
|
||||
inGroup = true;
|
||||
tokens.append('(');
|
||||
break;
|
||||
case '}':
|
||||
inGroup = false;
|
||||
tokens.append(')');
|
||||
break;
|
||||
case ',':
|
||||
if (inGroup) {
|
||||
tokens.append('|');
|
||||
break;
|
||||
}
|
||||
tokens.append("\\").append(c);
|
||||
break;
|
||||
default:
|
||||
if (escapeGlobChars.contains(c)) {
|
||||
tokens.append('\\');
|
||||
}
|
||||
tokens.append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
tokens.append('$');
|
||||
return tokens.toString();
|
||||
}
|
||||
|
||||
static String mimeType(Path path) {
|
||||
String mimeType;
|
||||
try {
|
||||
@@ -201,7 +275,7 @@ public class Utils {
|
||||
items.add(item);
|
||||
}
|
||||
tempFilesParams.add("items", items);
|
||||
return context.sendMessage("createTempFiles", tempFilesParams, NO_TIMEOUT).getAsJsonObject();
|
||||
return context.sendMessage("createTempFiles", tempFilesParams).getAsJsonObject();
|
||||
}
|
||||
|
||||
static void checkFilePayloadSize(FilePayload[] files) {
|
||||
|
||||
@@ -22,39 +22,61 @@ import com.microsoft.playwright.Video;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
class VideoImpl implements Video {
|
||||
private final PageImpl page;
|
||||
ArtifactImpl artifact;
|
||||
private final WaitableResult<ArtifactImpl> waitableArtifact = new WaitableResult<>();
|
||||
|
||||
VideoImpl(PageImpl page) {
|
||||
this.page = page;
|
||||
BrowserImpl browser = page.context().browser();
|
||||
}
|
||||
|
||||
VideoImpl(PageImpl page, ArtifactImpl artifact) {
|
||||
this.page = page;
|
||||
this.artifact = artifact;
|
||||
void setArtifact(ArtifactImpl artifact) {
|
||||
waitableArtifact.complete(artifact);
|
||||
}
|
||||
|
||||
private ArtifactImpl waitForArtifact() {
|
||||
Waitable<ArtifactImpl> waitable = new WaitableRace<>(asList(waitableArtifact, (Waitable<ArtifactImpl>) page.waitableClosedOrCrashed));
|
||||
return page.runUntil(() -> {}, waitable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
if (artifact != null)
|
||||
artifact.delete();
|
||||
page.withLogging("Video.delete", () -> {
|
||||
try {
|
||||
waitForArtifact().delete();
|
||||
} catch (PlaywrightException e) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path path() {
|
||||
if (page.connection.isRemote) {
|
||||
throw new PlaywrightException("Path is not available when using browserType.connect(). Use saveAs() to save a local copy.");
|
||||
}
|
||||
if (artifact == null)
|
||||
throw new PlaywrightException("Video recording has not been started.");
|
||||
return Paths.get(artifact.initializer.get("absolutePath").getAsString());
|
||||
return page.withLogging("Video.path", () -> {
|
||||
if (page.connection.isRemote) {
|
||||
throw new PlaywrightException("Path is not available when using browserType.connect(). Use saveAs() to save a local copy.");
|
||||
}
|
||||
try {
|
||||
return Paths.get(waitForArtifact().initializer.get("absolutePath").getAsString());
|
||||
} catch (PlaywrightException e) {
|
||||
throw new PlaywrightException("Page did not produce any video frames", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAs(Path path) {
|
||||
if (artifact == null)
|
||||
throw new PlaywrightException("Video recording has not been started.");
|
||||
artifact.saveAs(path);
|
||||
page.withLogging("Video.saveAs", () -> {
|
||||
if (!page.isClosed()) {
|
||||
throw new PlaywrightException("Page is not yet closed. Close the page prior to calling saveAs");
|
||||
}
|
||||
try {
|
||||
waitForArtifact().saveAs(path);
|
||||
} catch (PlaywrightException e) {
|
||||
throw new PlaywrightException("Page did not produce any video frames", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,21 +38,23 @@ public class WaitForEventLogger<T> implements Supplier<T>, Logger {
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
{
|
||||
return channel.withLogging(apiName, () -> {
|
||||
{
|
||||
JsonObject info = new JsonObject();
|
||||
info.addProperty("phase", "before");
|
||||
sendWaitForEventInfo(info);
|
||||
}
|
||||
JsonObject info = new JsonObject();
|
||||
info.addProperty("phase", "before");
|
||||
sendWaitForEventInfo(info);
|
||||
}
|
||||
JsonObject info = new JsonObject();
|
||||
info.addProperty("phase", "after");
|
||||
try {
|
||||
return supplier.apply(this);
|
||||
} catch (RuntimeException e) {
|
||||
info.addProperty("error", e.getMessage());
|
||||
throw e;
|
||||
} finally {
|
||||
sendWaitForEventInfo(info);
|
||||
}
|
||||
info.addProperty("phase", "after");
|
||||
try {
|
||||
return supplier.apply(this);
|
||||
} catch (RuntimeException e) {
|
||||
info.addProperty("error", e.getMessage());
|
||||
throw e;
|
||||
} finally {
|
||||
sendWaitForEventInfo(info);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user