Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ea48985e21 |
@@ -1,10 +1,6 @@
|
||||
trigger: none
|
||||
pr: none
|
||||
|
||||
trigger:
|
||||
tags:
|
||||
include:
|
||||
- '*'
|
||||
|
||||
resources:
|
||||
repositories:
|
||||
- repository: 1esPipelines
|
||||
@@ -29,16 +25,11 @@ extends:
|
||||
stages:
|
||||
- stage: Stage
|
||||
jobs:
|
||||
- job: Build
|
||||
templateContext:
|
||||
outputs:
|
||||
- output: pipelineArtifact
|
||||
path: $(Build.ArtifactStagingDirectory)/esrp-build
|
||||
artifact: esrp-build
|
||||
- job: HostJob
|
||||
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
|
||||
@@ -59,39 +50,28 @@ extends:
|
||||
- bash: ./scripts/download_driver.sh
|
||||
displayName: 'Download driver'
|
||||
|
||||
- bash: mvn -B deploy -D skipTests --no-transfer-progress --activate-profiles release -D gpg.passphrase=$GPG_PASSPHRASE -DaltDeploymentRepository=snapshot-repo::default::file:$(Build.ArtifactStagingDirectory)/esrp-build
|
||||
- bash: mvn -B deploy -D skipTests --no-transfer-progress --activate-profiles release -D gpg.passphrase=$GPG_PASSPHRASE -DaltDeploymentRepository=snapshot-repo::default::file:$(pwd)/local-build
|
||||
displayName: 'Build and deploy to a local directory'
|
||||
env:
|
||||
GPG_PASSPHRASE: $(GPG_PASSPHRASE) # secret variable has to be mapped to an env variable
|
||||
|
||||
- job: Publish
|
||||
dependsOn: Build
|
||||
templateContext:
|
||||
type: releaseJob
|
||||
isProduction: true
|
||||
|
||||
- task: EsrpRelease@7
|
||||
inputs:
|
||||
- input: pipelineArtifact
|
||||
artifactName: esrp-build
|
||||
targetPath: $(Build.ArtifactStagingDirectory)/esrp-build
|
||||
steps:
|
||||
- checkout: none
|
||||
- task: EsrpRelease@9
|
||||
inputs:
|
||||
connectedservicename: 'Playwright-ESRP-PME'
|
||||
usemanagedidentity: true
|
||||
keyvaultname: 'playwright-esrp-pme'
|
||||
signcertname: 'ESRP-Release-Sign'
|
||||
clientid: '13434a40-7de4-4c23-81a3-d843dc81c2c5'
|
||||
intent: 'PackageDistribution'
|
||||
contenttype: 'Maven'
|
||||
# Keeping it commented out as a workaround for:
|
||||
# https://portal.microsofticm.com/imp/v3/incidents/incident/499972482/summary
|
||||
# contentsource: 'folder'
|
||||
folderlocation: '$(Build.ArtifactStagingDirectory)/esrp-build'
|
||||
waitforreleasecompletion: true
|
||||
owners: 'yurys@microsoft.com'
|
||||
approvers: 'maxschmitt@microsoft.com'
|
||||
serviceendpointurl: 'https://api.esrp.microsoft.com'
|
||||
mainpublisher: 'Playwright'
|
||||
domaintenantid: '975f013f-7f24-47e8-a7d3-abc4752bf346'
|
||||
displayName: 'ESRP Release to Maven'
|
||||
connectedservicename: 'Playwright-ESRP-Azure'
|
||||
keyvaultname: 'pw-publishing-secrets'
|
||||
authcertname: 'ESRP-Release-Auth'
|
||||
signcertname: 'ESRP-Release-Sign'
|
||||
clientid: '13434a40-7de4-4c23-81a3-d843dc81c2c5'
|
||||
intent: 'PackageDistribution'
|
||||
contenttype: 'Maven'
|
||||
# Keeping it commented out as a workaround for:
|
||||
# https://portal.microsofticm.com/imp/v3/incidents/incident/499972482/summary
|
||||
# contentsource: 'folder'
|
||||
folderlocation: './local-build'
|
||||
waitforreleasecompletion: true
|
||||
owners: 'yurys@microsoft.com'
|
||||
approvers: 'maxschmitt@microsoft.com'
|
||||
serviceendpointurl: 'https://api.esrp.microsoft.com'
|
||||
mainpublisher: 'Playwright'
|
||||
domaintenantid: '72f988bf-86f1-41af-91ab-2d7cd011db47'
|
||||
displayName: 'ESRP Release to Maven'
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||
// README at: https://github.com/devcontainers/templates/tree/main/src/java
|
||||
{
|
||||
"name": "Java",
|
||||
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||
"image": "mcr.microsoft.com/devcontainers/java:1-21-bookworm",
|
||||
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/java:1": {
|
||||
"version": "none",
|
||||
"installGradle": "false",
|
||||
"installMaven": "true"
|
||||
},
|
||||
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}
|
||||
}
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
// "postCreateCommand": "java -version",
|
||||
|
||||
// Configure tool-specific properties.
|
||||
// "customizations": {},
|
||||
|
||||
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||
// "remoteUser": "root"
|
||||
}
|
||||
@@ -17,11 +17,3 @@ updates:
|
||||
allow:
|
||||
- dependency-type: "direct" # Optional: Only update direct dependencies
|
||||
- dependency-type: "indirect" # Optional: Only update indirect (transitive) dependencies
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
groups:
|
||||
actions:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
@@ -8,8 +8,6 @@ on:
|
||||
branches:
|
||||
- main
|
||||
- release-*
|
||||
env:
|
||||
PW_MAX_RETRIES: 3
|
||||
jobs:
|
||||
dev:
|
||||
timeout-minutes: 30
|
||||
@@ -20,9 +18,10 @@ jobs:
|
||||
browser: [chromium, firefox, webkit]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: microsoft/playwright-github-action@v1
|
||||
- name: Set up JDK 1.8
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: zulu
|
||||
java-version: 8
|
||||
@@ -31,8 +30,6 @@ jobs:
|
||||
run: scripts/download_driver.sh
|
||||
- name: Build & Install
|
||||
run: mvn -B install -D skipTests --no-transfer-progress
|
||||
- name: Install browsers
|
||||
run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps" -f playwright/pom.xml --no-transfer-progress
|
||||
- name: Run tests
|
||||
run: mvn test --no-transfer-progress --fail-at-end -D org.slf4j.simpleLogger.showDateTime=true -D org.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss
|
||||
env:
|
||||
@@ -65,13 +62,14 @@ jobs:
|
||||
browser-channel: msedge
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: microsoft/playwright-github-action@v1
|
||||
- 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@v4
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: zulu
|
||||
java-version: 8
|
||||
@@ -80,8 +78,6 @@ jobs:
|
||||
run: scripts/download_driver.sh
|
||||
- name: Build & Install
|
||||
run: mvn -B install -D skipTests --no-transfer-progress
|
||||
- name: Install browsers
|
||||
run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps" -f playwright/pom.xml --no-transfer-progress
|
||||
- name: Install MS Edge
|
||||
if: matrix.browser-channel == 'msedge' && matrix.os == 'macos-latest'
|
||||
shell: bash
|
||||
@@ -100,9 +96,10 @@ jobs:
|
||||
browser: [chromium, firefox, webkit]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: microsoft/playwright-github-action@v1
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: adopt
|
||||
java-version: 21
|
||||
@@ -111,8 +108,6 @@ jobs:
|
||||
run: scripts/download_driver.sh
|
||||
- name: Build & Install
|
||||
run: mvn -B install -D skipTests --no-transfer-progress
|
||||
- name: Install browsers
|
||||
run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps" -f playwright/pom.xml --no-transfer-progress
|
||||
- name: Run tests
|
||||
run: mvn test --no-transfer-progress --fail-at-end
|
||||
env:
|
||||
|
||||
@@ -13,9 +13,9 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v2
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.m2
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
|
||||
@@ -20,33 +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]
|
||||
flavor: [focal, jammy, noble]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build Docker image
|
||||
run: bash utils/docker/build.sh --amd64 ${{ matrix.flavor }} playwright-java:localbuild-${{ matrix.flavor }}
|
||||
- name: Test
|
||||
run: |
|
||||
ARCH="${{ matrix.runs-on == 'ubuntu-24.04-arm' && 'arm64' || 'amd64' }}"
|
||||
bash utils/docker/build.sh --$ARCH ${{ matrix.flavor }} playwright-java:localbuild-${{ matrix.flavor }}
|
||||
- name: Start container
|
||||
run: |
|
||||
CONTAINER_ID=$(docker run --rm -e CI -e PW_MAX_RETRIES --ipc=host -v "$(pwd)":/root/playwright --name playwright-docker-test -d -t playwright-java:localbuild-${{ matrix.flavor }} /bin/bash)
|
||||
echo "CONTAINER_ID=$CONTAINER_ID" >> $GITHUB_ENV
|
||||
|
||||
- name: Run test in container
|
||||
run: |
|
||||
docker exec "$CONTAINER_ID" /root/playwright/tools/test-local-installation/create_project_and_run_tests.sh
|
||||
|
||||
- name: Test ClassLoader
|
||||
run: |
|
||||
docker exec "${CONTAINER_ID}" /root/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-20.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,15 +19,12 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v2
|
||||
- uses: microsoft/playwright-github-action@v1
|
||||
- name: Download drivers
|
||||
run: scripts/download_driver.sh
|
||||
- name: Regenerate APIs
|
||||
run: scripts/generate_api.sh
|
||||
- name: Build & Install
|
||||
run: mvn -B install -D skipTests --no-transfer-progress
|
||||
- name: Install browsers
|
||||
run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps" -f playwright/pom.xml --no-transfer-progress
|
||||
- name: Update browser versions in README
|
||||
run: scripts/update_readme.sh
|
||||
- name: Verify API is up to date
|
||||
|
||||
+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,19 +10,59 @@ Playwright is a Java library to automate [Chromium](https://www.chromium.org/Hom
|
||||
|
||||
| | Linux | macOS | Windows |
|
||||
| :--- | :---: | :---: | :---: |
|
||||
| Chromium <!-- GEN:chromium-version -->140.0.7339.16<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| WebKit <!-- GEN:webkit-version -->26.0<!-- GEN:stop --> | ✅ | ✅ | ✅ |
|
||||
| Firefox <!-- GEN:firefox-version -->141.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Chromium <!-- GEN:chromium-version -->130.0.6723.31<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| WebKit <!-- GEN:webkit-version -->18.0<!-- GEN:stop --> | ✅ | ✅ | ✅ |
|
||||
| Firefox <!-- GEN:firefox-version -->131.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
|
||||
## Documentation
|
||||
Headless execution is supported for all the browsers on all platforms. Check out [system requirements](https://playwright.dev/java/docs/intro#system-requirements) for details.
|
||||
|
||||
[https://playwright.dev/java/docs/intro](https://playwright.dev/java/docs/intro)
|
||||
* [Usage](#usage)
|
||||
- [Add Maven dependency](#add-maven-dependency)
|
||||
- [Is Playwright thread-safe?](#is-playwright-thread-safe)
|
||||
* [Examples](#examples)
|
||||
- [Page screenshot](#page-screenshot)
|
||||
- [Mobile and geolocation](#mobile-and-geolocation)
|
||||
- [Evaluate JavaScript in browser](#evaluate-javascript-in-browser)
|
||||
- [Intercept network requests](#intercept-network-requests)
|
||||
* [Documentation](#documentation)
|
||||
* [Contributing](#contributing)
|
||||
* [Is Playwright for Java ready?](#is-playwright-for-java-ready)
|
||||
|
||||
## API Reference
|
||||
## Usage
|
||||
|
||||
[https://playwright.dev/java/docs/api/class-playwright](https://playwright.dev/java/docs/api/class-playwright)
|
||||
Playwright requires **Java 8** or newer.
|
||||
|
||||
## Example
|
||||
#### Add Maven dependency
|
||||
|
||||
Playwright is distributed as a set of [Maven](https://maven.apache.org/what-is-maven.html) modules. The easiest way to use it is to add one dependency to your Maven `pom.xml` file as described below. If you're not familiar with Maven please refer to its [documentation](https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html).
|
||||
|
||||
To run Playwright simply add following dependency to your Maven project:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>playwright</artifactId>
|
||||
<version>1.41.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
To run Playwright using Gradle add following dependency to your build.gradle file:
|
||||
|
||||
```gradle
|
||||
dependencies {
|
||||
implementation group: 'com.microsoft.playwright', name: 'playwright', version: '1.41.0'
|
||||
}
|
||||
```
|
||||
|
||||
#### Is Playwright thread-safe?
|
||||
|
||||
No, Playwright is not thread safe, i.e. all its methods as well as methods on all objects created by it (such as BrowserContext, Browser, Page etc.) are expected to be called on the same thread where Playwright object was created or proper synchronization should be implemented to ensure only one thread calls Playwright methods at any given time. Having said that it's okay to create multiple Playwright instances each on its own thread.
|
||||
|
||||
## Examples
|
||||
|
||||
You can find Maven project with the examples [here](./examples).
|
||||
|
||||
#### Page screenshot
|
||||
|
||||
This code snippet navigates to Playwright homepage in Chromium, Firefox and WebKit, and saves 3 screenshots.
|
||||
|
||||
@@ -54,9 +94,100 @@ public class PageScreenshot {
|
||||
}
|
||||
```
|
||||
|
||||
## Other languages
|
||||
#### Mobile and geolocation
|
||||
|
||||
More comfortable in another programming language? [Playwright](https://playwright.dev) is also available in
|
||||
- [Node.js (JavaScript / TypeScript)](https://playwright.dev/docs/intro),
|
||||
- [Python](https://playwright.dev/python/docs/intro).
|
||||
- [.NET](https://playwright.dev/dotnet/docs/intro),
|
||||
This snippet emulates Mobile Chromium on a device at a given geolocation, navigates to openstreetmap.org, performs action and takes a screenshot.
|
||||
|
||||
```java
|
||||
import com.microsoft.playwright.options.*;
|
||||
import com.microsoft.playwright.*;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
public class MobileAndGeolocation {
|
||||
public static void main(String[] args) {
|
||||
try (Playwright playwright = Playwright.create()) {
|
||||
Browser browser = playwright.chromium().launch();
|
||||
BrowserContext context = browser.newContext(new Browser.NewContextOptions()
|
||||
.setUserAgent("Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Mobile Safari/537.36")
|
||||
.setViewportSize(411, 731)
|
||||
.setDeviceScaleFactor(2.625)
|
||||
.setIsMobile(true)
|
||||
.setHasTouch(true)
|
||||
.setLocale("en-US")
|
||||
.setGeolocation(41.889938, 12.492507)
|
||||
.setPermissions(asList("geolocation")));
|
||||
Page page = context.newPage();
|
||||
page.navigate("https://www.openstreetmap.org/");
|
||||
page.click("a[data-bs-original-title=\"Show My Location\"]");
|
||||
page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("colosseum-pixel2.png")));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Evaluate JavaScript in browser
|
||||
|
||||
This code snippet navigates to example.com in Firefox, and executes a script in the page context.
|
||||
|
||||
```java
|
||||
import com.microsoft.playwright.*;
|
||||
|
||||
public class EvaluateInBrowserContext {
|
||||
public static void main(String[] args) {
|
||||
try (Playwright playwright = Playwright.create()) {
|
||||
Browser browser = playwright.firefox().launch();
|
||||
BrowserContext context = browser.newContext();
|
||||
Page page = context.newPage();
|
||||
page.navigate("https://www.example.com/");
|
||||
Object dimensions = page.evaluate("() => {\n" +
|
||||
" return {\n" +
|
||||
" width: document.documentElement.clientWidth,\n" +
|
||||
" height: document.documentElement.clientHeight,\n" +
|
||||
" deviceScaleFactor: window.devicePixelRatio\n" +
|
||||
" }\n" +
|
||||
"}");
|
||||
System.out.println(dimensions);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Intercept network requests
|
||||
|
||||
This code snippet sets up request routing for a WebKit page to log all network requests.
|
||||
|
||||
```java
|
||||
import com.microsoft.playwright.*;
|
||||
|
||||
public class InterceptNetworkRequests {
|
||||
public static void main(String[] args) {
|
||||
try (Playwright playwright = Playwright.create()) {
|
||||
Browser browser = playwright.webkit().launch();
|
||||
BrowserContext context = browser.newContext();
|
||||
Page page = context.newPage();
|
||||
page.route("**", route -> {
|
||||
System.out.println(route.request().url());
|
||||
route.resume();
|
||||
});
|
||||
page.navigate("http://todomvc.com");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Check out our official [documentation site](https://playwright.dev/java).
|
||||
|
||||
You can also browse [javadoc online](https://www.javadoc.io/doc/com.microsoft.playwright/playwright/latest/index.html).
|
||||
|
||||
## Contributing
|
||||
|
||||
Follow [the instructions](https://github.com/microsoft/playwright-java/blob/main/CONTRIBUTING.md#getting-code) to build the project from source and install the driver.
|
||||
|
||||
## Is Playwright for Java ready?
|
||||
|
||||
Yes, Playwright for Java is ready. v1.10.0 is the first stable release. Going forward we will adhere to [semantic versioning](https://semver.org/) of the API.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>parent-pom</artifactId>
|
||||
<version>1.55.0</version>
|
||||
<version>1.48.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.55.0</version>
|
||||
<version>1.48.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>driver</artifactId>
|
||||
|
||||
+2
-3
@@ -6,17 +6,16 @@
|
||||
|
||||
<groupId>org.example</groupId>
|
||||
<artifactId>examples</artifactId>
|
||||
<version>1.55.0</version>
|
||||
<version>1.48.0</version>
|
||||
<name>Playwright Client Examples</name>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<playwright.version>1.55.0</playwright.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>playwright</artifactId>
|
||||
<version>${playwright.version}</version>
|
||||
<version>1.41.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
|
||||
+1
-10
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>parent-pom</artifactId>
|
||||
<version>1.55.0</version>
|
||||
<version>1.48.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}.
|
||||
*/
|
||||
@@ -63,10 +59,6 @@ public interface APIRequest {
|
||||
* An object containing additional HTTP headers to be sent with every request. Defaults to none.
|
||||
*/
|
||||
public Map<String, String> extraHTTPHeaders;
|
||||
/**
|
||||
* Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status codes.
|
||||
*/
|
||||
public Boolean failOnStatusCode;
|
||||
/**
|
||||
* Credentials for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication">HTTP authentication</a>. If
|
||||
* no origin is specified, the username and password are sent to any servers upon unauthorized responses.
|
||||
@@ -76,12 +68,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 +124,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}.
|
||||
*/
|
||||
@@ -156,13 +138,6 @@ public interface APIRequest {
|
||||
this.extraHTTPHeaders = extraHTTPHeaders;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status codes.
|
||||
*/
|
||||
public NewContextOptions setFailOnStatusCode(boolean failOnStatusCode) {
|
||||
this.failOnStatusCode = failOnStatusCode;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Credentials for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication">HTTP authentication</a>. If
|
||||
* no origin is specified, the username and password are sent to any servers upon unauthorized responses.
|
||||
@@ -185,15 +160,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.
|
||||
*/
|
||||
|
||||
@@ -58,23 +58,12 @@ public interface APIRequestContext {
|
||||
}
|
||||
}
|
||||
class StorageStateOptions {
|
||||
/**
|
||||
* Set to {@code true} to include IndexedDB in the storage state snapshot.
|
||||
*/
|
||||
public Boolean indexedDB;
|
||||
/**
|
||||
* The file path to save the storage state to. If {@code path} is a relative path, then it is resolved relative to current
|
||||
* working directory. If no path is provided, storage state is still returned, but won't be saved to the disk.
|
||||
*/
|
||||
public Path path;
|
||||
|
||||
/**
|
||||
* Set to {@code true} to include IndexedDB in the storage state snapshot.
|
||||
*/
|
||||
public StorageStateOptions setIndexedDB(boolean indexedDB) {
|
||||
this.indexedDB = indexedDB;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* The file path to save the storage state to. If {@code path} is a relative path, then it is resolved relative to current
|
||||
* working directory. If no path is provided, storage state is still returned, but won't be saved to the disk.
|
||||
|
||||
@@ -29,15 +29,15 @@ import java.util.regex.Pattern;
|
||||
* import com.microsoft.playwright.*;
|
||||
*
|
||||
* public class Example {
|
||||
* public static void main(String[] args) {
|
||||
* try (Playwright playwright = Playwright.create()) {
|
||||
* BrowserType firefox = playwright.firefox();
|
||||
* Browser browser = firefox.launch();
|
||||
* Page page = browser.newPage();
|
||||
* page.navigate("https://example.com");
|
||||
* browser.close();
|
||||
* }
|
||||
* }
|
||||
* public static void main(String[] args) {
|
||||
* try (Playwright playwright = Playwright.create()) {
|
||||
* BrowserType firefox = playwright.firefox()
|
||||
* Browser browser = firefox.launch();
|
||||
* Page page = browser.newPage();
|
||||
* page.navigate('https://example.com');
|
||||
* browser.close();
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*/
|
||||
@@ -106,28 +106,16 @@ 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}.
|
||||
*/
|
||||
public List<ClientCertificate> clientCertificates;
|
||||
/**
|
||||
* Emulates <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-colors-scheme</a> media
|
||||
* feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia
|
||||
* Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code
|
||||
* "light"}.
|
||||
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code
|
||||
* "no-preference"}. See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing
|
||||
* {@code null} resets emulation to system defaults. Defaults to {@code "light"}.
|
||||
*/
|
||||
public Optional<ColorScheme> colorScheme;
|
||||
/**
|
||||
* Emulates {@code "prefers-contrast"} media feature, supported values are {@code "no-preference"}, {@code "more"}. See
|
||||
* {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
|
||||
* emulation to system defaults. Defaults to {@code "no-preference"}.
|
||||
*/
|
||||
public Optional<Contrast> contrast;
|
||||
/**
|
||||
* Specify device scale factor (can be thought of as dpr). Defaults to {@code 1}. Learn more about <a
|
||||
* href="https://playwright.dev/java/docs/emulation#devices">emulating devices with device scale factor</a>.
|
||||
@@ -327,10 +315,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}.
|
||||
*/
|
||||
@@ -339,25 +323,14 @@ public interface Browser extends AutoCloseable {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-colors-scheme</a> media
|
||||
* feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia
|
||||
* Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code
|
||||
* "light"}.
|
||||
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code
|
||||
* "no-preference"}. See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing
|
||||
* {@code null} resets emulation to system defaults. Defaults to {@code "light"}.
|
||||
*/
|
||||
public NewContextOptions setColorScheme(ColorScheme colorScheme) {
|
||||
this.colorScheme = Optional.ofNullable(colorScheme);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates {@code "prefers-contrast"} media feature, supported values are {@code "no-preference"}, {@code "more"}. See
|
||||
* {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
|
||||
* emulation to system defaults. Defaults to {@code "no-preference"}.
|
||||
*/
|
||||
public NewContextOptions setContrast(Contrast contrast) {
|
||||
this.contrast = Optional.ofNullable(contrast);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Specify device scale factor (can be thought of as dpr). Defaults to {@code 1}. Learn more about <a
|
||||
* href="https://playwright.dev/java/docs/emulation#devices">emulating devices with device scale factor</a>.
|
||||
@@ -682,28 +655,16 @@ 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}.
|
||||
*/
|
||||
public List<ClientCertificate> clientCertificates;
|
||||
/**
|
||||
* Emulates <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-colors-scheme</a> media
|
||||
* feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia
|
||||
* Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code
|
||||
* "light"}.
|
||||
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code
|
||||
* "no-preference"}. See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing
|
||||
* {@code null} resets emulation to system defaults. Defaults to {@code "light"}.
|
||||
*/
|
||||
public Optional<ColorScheme> colorScheme;
|
||||
/**
|
||||
* Emulates {@code "prefers-contrast"} media feature, supported values are {@code "no-preference"}, {@code "more"}. See
|
||||
* {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
|
||||
* emulation to system defaults. Defaults to {@code "no-preference"}.
|
||||
*/
|
||||
public Optional<Contrast> contrast;
|
||||
/**
|
||||
* Specify device scale factor (can be thought of as dpr). Defaults to {@code 1}. Learn more about <a
|
||||
* href="https://playwright.dev/java/docs/emulation#devices">emulating devices with device scale factor</a>.
|
||||
@@ -903,10 +864,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}.
|
||||
*/
|
||||
@@ -915,25 +872,14 @@ public interface Browser extends AutoCloseable {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-colors-scheme</a> media
|
||||
* feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia
|
||||
* Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code
|
||||
* "light"}.
|
||||
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code
|
||||
* "no-preference"}. See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing
|
||||
* {@code null} resets emulation to system defaults. Defaults to {@code "light"}.
|
||||
*/
|
||||
public NewPageOptions setColorScheme(ColorScheme colorScheme) {
|
||||
this.colorScheme = Optional.ofNullable(colorScheme);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates {@code "prefers-contrast"} media feature, supported values are {@code "no-preference"}, {@code "more"}. See
|
||||
* {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
|
||||
* emulation to system defaults. Defaults to {@code "no-preference"}.
|
||||
*/
|
||||
public NewPageOptions setContrast(Contrast contrast) {
|
||||
this.contrast = Optional.ofNullable(contrast);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Specify device scale factor (can be thought of as dpr). Defaults to {@code 1}. Learn more about <a
|
||||
* href="https://playwright.dev/java/docs/emulation#devices">emulating devices with device scale factor</a>.
|
||||
@@ -1271,10 +1217,10 @@ public interface Browser extends AutoCloseable {
|
||||
* <p> In case this browser is connected to, clears all created contexts belonging to this browser and disconnects from the
|
||||
* browser server.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> This is similar to force-quitting the browser. To close pages gracefully and ensure you receive page close events, call
|
||||
* {@link com.microsoft.playwright.BrowserContext#close BrowserContext.close()} on any {@code BrowserContext} instances you
|
||||
* explicitly created earlier using {@link com.microsoft.playwright.Browser#newContext Browser.newContext()} **before**
|
||||
* calling {@link com.microsoft.playwright.Browser#close Browser.close()}.
|
||||
* <p> <strong>NOTE:</strong> This is similar to force quitting the browser. Therefore, you should call {@link
|
||||
* com.microsoft.playwright.BrowserContext#close BrowserContext.close()} on any {@code BrowserContext}'s you explicitly
|
||||
* created earlier with {@link com.microsoft.playwright.Browser#newContext Browser.newContext()} **before** calling {@link
|
||||
* com.microsoft.playwright.Browser#close Browser.close()}.
|
||||
*
|
||||
* <p> The {@code Browser} object itself is considered to be disposed and cannot be used anymore.
|
||||
*
|
||||
@@ -1290,10 +1236,10 @@ public interface Browser extends AutoCloseable {
|
||||
* <p> In case this browser is connected to, clears all created contexts belonging to this browser and disconnects from the
|
||||
* browser server.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> This is similar to force-quitting the browser. To close pages gracefully and ensure you receive page close events, call
|
||||
* {@link com.microsoft.playwright.BrowserContext#close BrowserContext.close()} on any {@code BrowserContext} instances you
|
||||
* explicitly created earlier using {@link com.microsoft.playwright.Browser#newContext Browser.newContext()} **before**
|
||||
* calling {@link com.microsoft.playwright.Browser#close Browser.close()}.
|
||||
* <p> <strong>NOTE:</strong> This is similar to force quitting the browser. Therefore, you should call {@link
|
||||
* com.microsoft.playwright.BrowserContext#close BrowserContext.close()} on any {@code BrowserContext}'s you explicitly
|
||||
* created earlier with {@link com.microsoft.playwright.Browser#newContext Browser.newContext()} **before** calling {@link
|
||||
* com.microsoft.playwright.Browser#close Browser.close()}.
|
||||
*
|
||||
* <p> The {@code Browser} object itself is considered to be disposed and cannot be used anymore.
|
||||
*
|
||||
@@ -1343,7 +1289,7 @@ public interface Browser extends AutoCloseable {
|
||||
* BrowserContext context = browser.newContext();
|
||||
* // Create a new page in a pristine context.
|
||||
* Page page = context.newPage();
|
||||
* page.navigate("https://example.com");
|
||||
* page.navigate('https://example.com');
|
||||
*
|
||||
* // Graceful close up everything
|
||||
* context.close();
|
||||
@@ -1370,7 +1316,7 @@ public interface Browser extends AutoCloseable {
|
||||
* BrowserContext context = browser.newContext();
|
||||
* // Create a new page in a pristine context.
|
||||
* Page page = context.newPage();
|
||||
* page.navigate("https://example.com");
|
||||
* page.navigate('https://example.com');
|
||||
*
|
||||
* // Graceful close up everything
|
||||
* context.close();
|
||||
@@ -1418,7 +1364,7 @@ public interface Browser extends AutoCloseable {
|
||||
* <pre>{@code
|
||||
* browser.startTracing(page, new Browser.StartTracingOptions()
|
||||
* .setPath(Paths.get("trace.json")));
|
||||
* page.navigate("https://www.google.com");
|
||||
* page.goto('https://www.google.com');
|
||||
* browser.stopTracing();
|
||||
* }</pre>
|
||||
*
|
||||
@@ -1442,7 +1388,7 @@ public interface Browser extends AutoCloseable {
|
||||
* <pre>{@code
|
||||
* browser.startTracing(page, new Browser.StartTracingOptions()
|
||||
* .setPath(Paths.get("trace.json")));
|
||||
* page.navigate("https://www.google.com");
|
||||
* page.goto('https://www.google.com');
|
||||
* browser.stopTracing();
|
||||
* }</pre>
|
||||
*
|
||||
@@ -1465,7 +1411,7 @@ public interface Browser extends AutoCloseable {
|
||||
* <pre>{@code
|
||||
* browser.startTracing(page, new Browser.StartTracingOptions()
|
||||
* .setPath(Paths.get("trace.json")));
|
||||
* page.navigate("https://www.google.com");
|
||||
* page.goto('https://www.google.com');
|
||||
* browser.stopTracing();
|
||||
* }</pre>
|
||||
*
|
||||
|
||||
@@ -407,27 +407,12 @@ public interface BrowserContext extends AutoCloseable {
|
||||
}
|
||||
}
|
||||
class StorageStateOptions {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public Boolean indexedDB;
|
||||
/**
|
||||
* The file path to save the storage state to. If {@code path} is a relative path, then it is resolved relative to current
|
||||
* working directory. If no path is provided, storage state is still returned, but won't be saved to the disk.
|
||||
*/
|
||||
public Path path;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public StorageStateOptions setIndexedDB(boolean indexedDB) {
|
||||
this.indexedDB = indexedDB;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* The file path to save the storage state to. If {@code path} is a relative path, then it is resolved relative to current
|
||||
* working directory. If no path is provided, storage state is still returned, but won't be saved to the disk.
|
||||
@@ -596,8 +581,7 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@@ -717,7 +701,7 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* public class Example {
|
||||
* public static void main(String[] args) {
|
||||
* try (Playwright playwright = Playwright.create()) {
|
||||
* BrowserType webkit = playwright.webkit();
|
||||
* BrowserType webkit = playwright.webkit()
|
||||
* Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false));
|
||||
* BrowserContext context = browser.newContext();
|
||||
* context.exposeBinding("pageURL", (source, args) -> source.page().url());
|
||||
@@ -764,7 +748,7 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* public class Example {
|
||||
* public static void main(String[] args) {
|
||||
* try (Playwright playwright = Playwright.create()) {
|
||||
* BrowserType webkit = playwright.webkit();
|
||||
* BrowserType webkit = playwright.webkit()
|
||||
* Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false));
|
||||
* BrowserContext context = browser.newContext();
|
||||
* context.exposeBinding("pageURL", (source, args) -> source.page().url());
|
||||
@@ -813,9 +797,8 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* public class Example {
|
||||
* public static void main(String[] args) {
|
||||
* try (Playwright playwright = Playwright.create()) {
|
||||
* BrowserType webkit = playwright.webkit();
|
||||
* BrowserType webkit = playwright.webkit()
|
||||
* Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false));
|
||||
* BrowserContext context = browser.newContext();
|
||||
* context.exposeFunction("sha256", args -> {
|
||||
* String text = (String) args[0];
|
||||
* MessageDigest crypto;
|
||||
@@ -850,14 +833,10 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* Grants specified permissions to the browser context. Only grants corresponding permissions to the given origin if
|
||||
* specified.
|
||||
*
|
||||
* @param permissions A list of permissions to grant.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Supported permissions differ between browsers, and even between different versions of the same browser. Any permission
|
||||
* may stop working after an update.
|
||||
*
|
||||
* <p> Here are some permissions that may be supported by some browsers:
|
||||
* @param permissions A permission or an array of permissions to grant. Permissions can be one of the following values:
|
||||
* <ul>
|
||||
* <li> {@code "accelerometer"}</li>
|
||||
* <li> {@code "accessibility-events"}</li>
|
||||
* <li> {@code "ambient-light-sensor"}</li>
|
||||
* <li> {@code "background-sync"}</li>
|
||||
* <li> {@code "camera"}</li>
|
||||
@@ -872,7 +851,6 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* <li> {@code "notifications"}</li>
|
||||
* <li> {@code "payment-handler"}</li>
|
||||
* <li> {@code "storage-access"}</li>
|
||||
* <li> {@code "local-fonts"}</li>
|
||||
* </ul>
|
||||
* @since v1.8
|
||||
*/
|
||||
@@ -883,14 +861,10 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* Grants specified permissions to the browser context. Only grants corresponding permissions to the given origin if
|
||||
* specified.
|
||||
*
|
||||
* @param permissions A list of permissions to grant.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Supported permissions differ between browsers, and even between different versions of the same browser. Any permission
|
||||
* may stop working after an update.
|
||||
*
|
||||
* <p> Here are some permissions that may be supported by some browsers:
|
||||
* @param permissions A permission or an array of permissions to grant. Permissions can be one of the following values:
|
||||
* <ul>
|
||||
* <li> {@code "accelerometer"}</li>
|
||||
* <li> {@code "accessibility-events"}</li>
|
||||
* <li> {@code "ambient-light-sensor"}</li>
|
||||
* <li> {@code "background-sync"}</li>
|
||||
* <li> {@code "camera"}</li>
|
||||
@@ -905,7 +879,6 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* <li> {@code "notifications"}</li>
|
||||
* <li> {@code "payment-handler"}</li>
|
||||
* <li> {@code "storage-access"}</li>
|
||||
* <li> {@code "local-fonts"}</li>
|
||||
* </ul>
|
||||
* @since v1.8
|
||||
*/
|
||||
@@ -995,8 +968,8 @@ 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
|
||||
@@ -1051,8 +1024,8 @@ 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
|
||||
@@ -1105,8 +1078,8 @@ 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
|
||||
@@ -1161,8 +1134,8 @@ 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
|
||||
@@ -1215,8 +1188,8 @@ 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
|
||||
@@ -1271,8 +1244,8 @@ 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
|
||||
@@ -1414,7 +1387,7 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* com.microsoft.playwright.BrowserContext#setDefaultNavigationTimeout BrowserContext.setDefaultNavigationTimeout()} take
|
||||
* priority over {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()}.
|
||||
*
|
||||
* @param timeout Maximum time in milliseconds. Pass {@code 0} to disable timeout.
|
||||
* @param timeout Maximum time in milliseconds
|
||||
* @since v1.8
|
||||
*/
|
||||
void setDefaultTimeout(double timeout);
|
||||
@@ -1453,7 +1426,7 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*/
|
||||
void setOffline(boolean offline);
|
||||
/**
|
||||
* Returns storage state for this browser context, contains current cookies, local storage snapshot and IndexedDB snapshot.
|
||||
* Returns storage state for this browser context, contains current cookies and local storage snapshot.
|
||||
*
|
||||
* @since v1.8
|
||||
*/
|
||||
@@ -1461,7 +1434,7 @@ public interface BrowserContext extends AutoCloseable {
|
||||
return storageState(null);
|
||||
}
|
||||
/**
|
||||
* Returns storage state for this browser context, contains current cookies, local storage snapshot and IndexedDB snapshot.
|
||||
* Returns storage state for this browser context, contains current cookies and local storage snapshot.
|
||||
*
|
||||
* @since v1.8
|
||||
*/
|
||||
|
||||
@@ -172,14 +172,9 @@ public interface BrowserType {
|
||||
*/
|
||||
public List<String> args;
|
||||
/**
|
||||
* Browser distribution channel.
|
||||
*
|
||||
* <p> Use "chromium" to <a href="https://playwright.dev/java/docs/browsers#chromium-new-headless-mode">opt in to new headless
|
||||
* mode</a>.
|
||||
*
|
||||
* <p> Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or "msedge-canary" to
|
||||
* use branded <a href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and
|
||||
* Microsoft Edge</a>.
|
||||
* Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge",
|
||||
* "msedge-beta", "msedge-dev", "msedge-canary". Read more about using <a
|
||||
* href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and Microsoft Edge</a>.
|
||||
*/
|
||||
public Object channel;
|
||||
/**
|
||||
@@ -209,9 +204,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;
|
||||
/**
|
||||
@@ -229,8 +221,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} unless
|
||||
* the {@code devtools} option is {@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;
|
||||
/**
|
||||
@@ -273,28 +265,18 @@ public interface BrowserType {
|
||||
}
|
||||
@Deprecated
|
||||
/**
|
||||
* Browser distribution channel.
|
||||
*
|
||||
* <p> Use "chromium" to <a href="https://playwright.dev/java/docs/browsers#chromium-new-headless-mode">opt in to new headless
|
||||
* mode</a>.
|
||||
*
|
||||
* <p> Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or "msedge-canary" to
|
||||
* use branded <a href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and
|
||||
* Microsoft Edge</a>.
|
||||
* Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge",
|
||||
* "msedge-beta", "msedge-dev", "msedge-canary". Read more about using <a
|
||||
* href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and Microsoft Edge</a>.
|
||||
*/
|
||||
public LaunchOptions setChannel(BrowserChannel channel) {
|
||||
this.channel = channel;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Browser distribution channel.
|
||||
*
|
||||
* <p> Use "chromium" to <a href="https://playwright.dev/java/docs/browsers#chromium-new-headless-mode">opt in to new headless
|
||||
* mode</a>.
|
||||
*
|
||||
* <p> Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or "msedge-canary" to
|
||||
* use branded <a href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and
|
||||
* Microsoft Edge</a>.
|
||||
* Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge",
|
||||
* "msedge-beta", "msedge-dev", "msedge-canary". Read more about using <a
|
||||
* href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and Microsoft Edge</a>.
|
||||
*/
|
||||
public LaunchOptions setChannel(String channel) {
|
||||
this.channel = channel;
|
||||
@@ -342,9 +324,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;
|
||||
@@ -374,8 +353,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} unless
|
||||
* the {@code devtools} option is {@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;
|
||||
@@ -467,14 +446,9 @@ public interface BrowserType {
|
||||
*/
|
||||
public Boolean bypassCSP;
|
||||
/**
|
||||
* Browser distribution channel.
|
||||
*
|
||||
* <p> Use "chromium" to <a href="https://playwright.dev/java/docs/browsers#chromium-new-headless-mode">opt in to new headless
|
||||
* mode</a>.
|
||||
*
|
||||
* <p> Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or "msedge-canary" to
|
||||
* use branded <a href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and
|
||||
* Microsoft Edge</a>.
|
||||
* Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge",
|
||||
* "msedge-beta", "msedge-dev", "msedge-canary". Read more about using <a
|
||||
* href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and Microsoft Edge</a>.
|
||||
*/
|
||||
public Object channel;
|
||||
/**
|
||||
@@ -491,28 +465,16 @@ 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}.
|
||||
*/
|
||||
public List<ClientCertificate> clientCertificates;
|
||||
/**
|
||||
* Emulates <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-colors-scheme</a> media
|
||||
* feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia
|
||||
* Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code
|
||||
* "light"}.
|
||||
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code
|
||||
* "no-preference"}. See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing
|
||||
* {@code null} resets emulation to system defaults. Defaults to {@code "light"}.
|
||||
*/
|
||||
public Optional<ColorScheme> colorScheme;
|
||||
/**
|
||||
* Emulates {@code "prefers-contrast"} media feature, supported values are {@code "no-preference"}, {@code "more"}. See
|
||||
* {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
|
||||
* emulation to system defaults. Defaults to {@code "no-preference"}.
|
||||
*/
|
||||
public Optional<Contrast> contrast;
|
||||
/**
|
||||
* Specify device scale factor (can be thought of as dpr). Defaults to {@code 1}. Learn more about <a
|
||||
* href="https://playwright.dev/java/docs/emulation#devices">emulating devices with device scale factor</a>.
|
||||
@@ -545,9 +507,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;
|
||||
/**
|
||||
@@ -577,8 +536,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} unless
|
||||
* the {@code devtools} option is {@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;
|
||||
/**
|
||||
@@ -773,28 +732,18 @@ public interface BrowserType {
|
||||
}
|
||||
@Deprecated
|
||||
/**
|
||||
* Browser distribution channel.
|
||||
*
|
||||
* <p> Use "chromium" to <a href="https://playwright.dev/java/docs/browsers#chromium-new-headless-mode">opt in to new headless
|
||||
* mode</a>.
|
||||
*
|
||||
* <p> Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or "msedge-canary" to
|
||||
* use branded <a href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and
|
||||
* Microsoft Edge</a>.
|
||||
* Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge",
|
||||
* "msedge-beta", "msedge-dev", "msedge-canary". Read more about using <a
|
||||
* href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and Microsoft Edge</a>.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setChannel(BrowserChannel channel) {
|
||||
this.channel = channel;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Browser distribution channel.
|
||||
*
|
||||
* <p> Use "chromium" to <a href="https://playwright.dev/java/docs/browsers#chromium-new-headless-mode">opt in to new headless
|
||||
* mode</a>.
|
||||
*
|
||||
* <p> Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or "msedge-canary" to
|
||||
* use branded <a href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and
|
||||
* Microsoft Edge</a>.
|
||||
* Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge",
|
||||
* "msedge-beta", "msedge-dev", "msedge-canary". Read more about using <a
|
||||
* href="https://playwright.dev/java/docs/browsers#google-chrome--microsoft-edge">Google Chrome and Microsoft Edge</a>.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setChannel(String channel) {
|
||||
this.channel = channel;
|
||||
@@ -817,10 +766,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}.
|
||||
*/
|
||||
@@ -829,25 +774,14 @@ public interface BrowserType {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-colors-scheme</a> media
|
||||
* feature, supported values are {@code "light"} and {@code "dark"}. See {@link com.microsoft.playwright.Page#emulateMedia
|
||||
* Page.emulateMedia()} for more details. Passing {@code null} resets emulation to system defaults. Defaults to {@code
|
||||
* "light"}.
|
||||
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code
|
||||
* "no-preference"}. See {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing
|
||||
* {@code null} resets emulation to system defaults. Defaults to {@code "light"}.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setColorScheme(ColorScheme colorScheme) {
|
||||
this.colorScheme = Optional.ofNullable(colorScheme);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates {@code "prefers-contrast"} media feature, supported values are {@code "no-preference"}, {@code "more"}. See
|
||||
* {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} for more details. Passing {@code null} resets
|
||||
* emulation to system defaults. Defaults to {@code "no-preference"}.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setContrast(Contrast contrast) {
|
||||
this.contrast = Optional.ofNullable(contrast);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Specify device scale factor (can be thought of as dpr). Defaults to {@code 1}. Learn more about <a
|
||||
* href="https://playwright.dev/java/docs/emulation#devices">emulating devices with device scale factor</a>.
|
||||
@@ -898,9 +832,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;
|
||||
@@ -954,8 +885,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} unless
|
||||
* the {@code devtools} option is {@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;
|
||||
@@ -1232,24 +1163,22 @@ public interface BrowserType {
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This method attaches Playwright to an existing browser instance created via {@code BrowserType.launchServer} in Node.js.
|
||||
* This method attaches Playwright to an existing browser instance. When connecting to another browser launched via {@code
|
||||
* BrowserType.launchServer} in Node.js, the major and minor version needs to match the client version (1.2.3 → is
|
||||
* compatible with 1.2.x).
|
||||
*
|
||||
* <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 wsEndpoint A Playwright browser websocket endpoint to connect to. You obtain this endpoint via {@code BrowserServer.wsEndpoint}.
|
||||
* @param wsEndpoint A browser websocket endpoint to connect to.
|
||||
* @since v1.8
|
||||
*/
|
||||
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.
|
||||
* This method attaches Playwright to an existing browser instance. When connecting to another browser launched via {@code
|
||||
* BrowserType.launchServer} in Node.js, the major and minor version needs to match the client version (1.2.3 → is
|
||||
* compatible with 1.2.x).
|
||||
*
|
||||
* <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 wsEndpoint A Playwright browser websocket endpoint to connect to. You obtain this endpoint via {@code BrowserServer.wsEndpoint}.
|
||||
* @param wsEndpoint A browser websocket endpoint to connect to.
|
||||
* @since v1.8
|
||||
*/
|
||||
Browser connect(String wsEndpoint, ConnectOptions options);
|
||||
@@ -1260,11 +1189,6 @@ public interface BrowserType {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Connecting over the Chrome DevTools Protocol is only supported for Chromium-based browsers.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> This connection is significantly lower fidelity than the Playwright protocol connection via {@link
|
||||
* com.microsoft.playwright.BrowserType#connect BrowserType.connect()}. If you are experiencing issues or attempting to use
|
||||
* advanced functionality, you probably want to use {@link com.microsoft.playwright.BrowserType#connect
|
||||
* BrowserType.connect()}.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* Browser browser = playwright.chromium().connectOverCDP("http://localhost:9222");
|
||||
@@ -1286,11 +1210,6 @@ public interface BrowserType {
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Connecting over the Chrome DevTools Protocol is only supported for Chromium-based browsers.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> This connection is significantly lower fidelity than the Playwright protocol connection via {@link
|
||||
* com.microsoft.playwright.BrowserType#connect BrowserType.connect()}. If you are experiencing issues or attempting to use
|
||||
* advanced functionality, you probably want to use {@link com.microsoft.playwright.BrowserType#connect
|
||||
* BrowserType.connect()}.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* Browser browser = playwright.chromium().connectOverCDP("http://localhost:9222");
|
||||
@@ -1385,20 +1304,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) {
|
||||
@@ -1410,20 +1320,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);
|
||||
|
||||
@@ -174,19 +174,6 @@ public interface Clock {
|
||||
* page.clock().pauseAt("2020-02-02");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> For best results, install the clock before navigating the page and set it to a time slightly before the intended test
|
||||
* time. This ensures that all timers run normally during page loading, preventing the page from getting stuck. Once the
|
||||
* page has fully loaded, you can safely use {@link com.microsoft.playwright.Clock#pauseAt Clock.pauseAt()} to pause the
|
||||
* clock.
|
||||
* <pre>{@code
|
||||
* // Initialize clock with some time before the test time and let the page load
|
||||
* // naturally. `Date.now` will progress as the timers fire.
|
||||
* SimpleDateFormat format = new SimpleDateFormat("yyy-MM-dd'T'HH:mm:ss");
|
||||
* page.clock().install(new Clock.InstallOptions().setTime(format.parse("2024-12-10T08:00:00")));
|
||||
* page.navigate("http://localhost:3333");
|
||||
* page.clock().pauseAt(format.parse("2024-12-10T10:00:00"));
|
||||
* }</pre>
|
||||
*
|
||||
* @param time Time to pause at.
|
||||
* @since v1.45
|
||||
*/
|
||||
@@ -207,19 +194,6 @@ public interface Clock {
|
||||
* page.clock().pauseAt("2020-02-02");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> For best results, install the clock before navigating the page and set it to a time slightly before the intended test
|
||||
* time. This ensures that all timers run normally during page loading, preventing the page from getting stuck. Once the
|
||||
* page has fully loaded, you can safely use {@link com.microsoft.playwright.Clock#pauseAt Clock.pauseAt()} to pause the
|
||||
* clock.
|
||||
* <pre>{@code
|
||||
* // Initialize clock with some time before the test time and let the page load
|
||||
* // naturally. `Date.now` will progress as the timers fire.
|
||||
* SimpleDateFormat format = new SimpleDateFormat("yyy-MM-dd'T'HH:mm:ss");
|
||||
* page.clock().install(new Clock.InstallOptions().setTime(format.parse("2024-12-10T08:00:00")));
|
||||
* page.navigate("http://localhost:3333");
|
||||
* page.clock().pauseAt(format.parse("2024-12-10T10:00:00"));
|
||||
* }</pre>
|
||||
*
|
||||
* @param time Time to pause at.
|
||||
* @since v1.45
|
||||
*/
|
||||
@@ -240,19 +214,6 @@ public interface Clock {
|
||||
* page.clock().pauseAt("2020-02-02");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> For best results, install the clock before navigating the page and set it to a time slightly before the intended test
|
||||
* time. This ensures that all timers run normally during page loading, preventing the page from getting stuck. Once the
|
||||
* page has fully loaded, you can safely use {@link com.microsoft.playwright.Clock#pauseAt Clock.pauseAt()} to pause the
|
||||
* clock.
|
||||
* <pre>{@code
|
||||
* // Initialize clock with some time before the test time and let the page load
|
||||
* // naturally. `Date.now` will progress as the timers fire.
|
||||
* SimpleDateFormat format = new SimpleDateFormat("yyy-MM-dd'T'HH:mm:ss");
|
||||
* page.clock().install(new Clock.InstallOptions().setTime(format.parse("2024-12-10T08:00:00")));
|
||||
* page.navigate("http://localhost:3333");
|
||||
* page.clock().pauseAt(format.parse("2024-12-10T10:00:00"));
|
||||
* }</pre>
|
||||
*
|
||||
* @param time Time to pause at.
|
||||
* @since v1.45
|
||||
*/
|
||||
@@ -266,10 +227,6 @@ public interface Clock {
|
||||
/**
|
||||
* Makes {@code Date.now} and {@code new Date()} return fixed fake time at all times, keeps all the timers running.
|
||||
*
|
||||
* <p> Use this method for simple scenarios where you only need to test with a predefined time. For more advanced scenarios,
|
||||
* use {@link com.microsoft.playwright.Clock#install Clock.install()} instead. Read docs on <a
|
||||
* href="https://playwright.dev/java/docs/clock">clock emulation</a> to learn more.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* page.clock().setFixedTime(new Date());
|
||||
@@ -284,10 +241,6 @@ public interface Clock {
|
||||
/**
|
||||
* Makes {@code Date.now} and {@code new Date()} return fixed fake time at all times, keeps all the timers running.
|
||||
*
|
||||
* <p> Use this method for simple scenarios where you only need to test with a predefined time. For more advanced scenarios,
|
||||
* use {@link com.microsoft.playwright.Clock#install Clock.install()} instead. Read docs on <a
|
||||
* href="https://playwright.dev/java/docs/clock">clock emulation</a> to learn more.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* page.clock().setFixedTime(new Date());
|
||||
@@ -302,10 +255,6 @@ public interface Clock {
|
||||
/**
|
||||
* Makes {@code Date.now} and {@code new Date()} return fixed fake time at all times, keeps all the timers running.
|
||||
*
|
||||
* <p> Use this method for simple scenarios where you only need to test with a predefined time. For more advanced scenarios,
|
||||
* use {@link com.microsoft.playwright.Clock#install Clock.install()} instead. Read docs on <a
|
||||
* href="https://playwright.dev/java/docs/clock">clock emulation</a> to learn more.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* page.clock().setFixedTime(new Date());
|
||||
@@ -318,8 +267,7 @@ public interface Clock {
|
||||
*/
|
||||
void setFixedTime(Date time);
|
||||
/**
|
||||
* Sets system time, but does not trigger any timers. Use this to test how the web page reacts to a time shift, for example
|
||||
* switching from summer to winter time, or changing time zones.
|
||||
* Sets current system time but does not trigger any timers.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
@@ -333,8 +281,7 @@ public interface Clock {
|
||||
*/
|
||||
void setSystemTime(long time);
|
||||
/**
|
||||
* Sets system time, but does not trigger any timers. Use this to test how the web page reacts to a time shift, for example
|
||||
* switching from summer to winter time, or changing time zones.
|
||||
* Sets current system time but does not trigger any timers.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
@@ -348,8 +295,7 @@ public interface Clock {
|
||||
*/
|
||||
void setSystemTime(String time);
|
||||
/**
|
||||
* Sets system time, but does not trigger any timers. Use this to test how the web page reacts to a time shift, for example
|
||||
* switching from summer to winter time, or changing time zones.
|
||||
* Sets current system time but does not trigger any timers.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
|
||||
@@ -39,8 +39,8 @@ import java.util.*;
|
||||
* });
|
||||
*
|
||||
* // Deconstruct console.log arguments
|
||||
* msg.args().get(0).jsonValue(); // hello
|
||||
* msg.args().get(1).jsonValue(); // 42
|
||||
* msg.args().get(0).jsonValue() // hello
|
||||
* msg.args().get(1).jsonValue() // 42
|
||||
* }</pre>
|
||||
*/
|
||||
public interface ConsoleMessage {
|
||||
|
||||
@@ -599,9 +599,7 @@ public interface ElementHandle extends JSHandle {
|
||||
public ScreenshotCaret caret;
|
||||
/**
|
||||
* Specify locators that should be masked when the screenshot is taken. Masked elements will be overlaid with a pink box
|
||||
* {@code #FF00FF} (customized by {@code maskColor}) that completely covers its bounding box. The mask is also applied to
|
||||
* invisible elements, see <a href="https://playwright.dev/java/docs/locators#matching-only-visible-elements">Matching only
|
||||
* visible elements</a> to disable that.
|
||||
* {@code #FF00FF} (customized by {@code maskColor}) that completely covers its bounding box.
|
||||
*/
|
||||
public List<Locator> mask;
|
||||
/**
|
||||
@@ -675,9 +673,7 @@ public interface ElementHandle extends JSHandle {
|
||||
}
|
||||
/**
|
||||
* Specify locators that should be masked when the screenshot is taken. Masked elements will be overlaid with a pink box
|
||||
* {@code #FF00FF} (customized by {@code maskColor}) that completely covers its bounding box. The mask is also applied to
|
||||
* invisible elements, see <a href="https://playwright.dev/java/docs/locators#matching-only-visible-elements">Matching only
|
||||
* visible elements</a> to disable that.
|
||||
* {@code #FF00FF} (customized by {@code maskColor}) that completely covers its bounding box.
|
||||
*/
|
||||
public ScreenshotOptions setMask(List<Locator> mask) {
|
||||
this.mask = mask;
|
||||
|
||||
@@ -3499,19 +3499,19 @@ public interface Frame {
|
||||
* <p> You can locate by text substring, exact string, or a regular expression:
|
||||
* <pre>{@code
|
||||
* // Matches <span>
|
||||
* page.getByText("world");
|
||||
* page.getByText("world")
|
||||
*
|
||||
* // Matches first <div>
|
||||
* page.getByText("Hello world");
|
||||
* page.getByText("Hello world")
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
|
||||
*
|
||||
* // Matches both <div>s
|
||||
* page.getByText(Pattern.compile("Hello"));
|
||||
* page.getByText(Pattern.compile("Hello"))
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>Details</strong>
|
||||
@@ -3541,19 +3541,19 @@ public interface Frame {
|
||||
* <p> You can locate by text substring, exact string, or a regular expression:
|
||||
* <pre>{@code
|
||||
* // Matches <span>
|
||||
* page.getByText("world");
|
||||
* page.getByText("world")
|
||||
*
|
||||
* // Matches first <div>
|
||||
* page.getByText("Hello world");
|
||||
* page.getByText("Hello world")
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
|
||||
*
|
||||
* // Matches both <div>s
|
||||
* page.getByText(Pattern.compile("Hello"));
|
||||
* page.getByText(Pattern.compile("Hello"))
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>Details</strong>
|
||||
@@ -3581,19 +3581,19 @@ public interface Frame {
|
||||
* <p> You can locate by text substring, exact string, or a regular expression:
|
||||
* <pre>{@code
|
||||
* // Matches <span>
|
||||
* page.getByText("world");
|
||||
* page.getByText("world")
|
||||
*
|
||||
* // Matches first <div>
|
||||
* page.getByText("Hello world");
|
||||
* page.getByText("Hello world")
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
|
||||
*
|
||||
* // Matches both <div>s
|
||||
* page.getByText(Pattern.compile("Hello"));
|
||||
* page.getByText(Pattern.compile("Hello"))
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>Details</strong>
|
||||
@@ -3623,19 +3623,19 @@ public interface Frame {
|
||||
* <p> You can locate by text substring, exact string, or a regular expression:
|
||||
* <pre>{@code
|
||||
* // Matches <span>
|
||||
* page.getByText("world");
|
||||
* page.getByText("world")
|
||||
*
|
||||
* // Matches first <div>
|
||||
* page.getByText("Hello world");
|
||||
* page.getByText("Hello world")
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
|
||||
*
|
||||
* // Matches both <div>s
|
||||
* page.getByText(Pattern.compile("Hello"));
|
||||
* page.getByText(Pattern.compile("Hello"))
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>Details</strong>
|
||||
|
||||
@@ -734,19 +734,19 @@ public interface FrameLocator {
|
||||
* <p> You can locate by text substring, exact string, or a regular expression:
|
||||
* <pre>{@code
|
||||
* // Matches <span>
|
||||
* page.getByText("world");
|
||||
* page.getByText("world")
|
||||
*
|
||||
* // Matches first <div>
|
||||
* page.getByText("Hello world");
|
||||
* page.getByText("Hello world")
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
|
||||
*
|
||||
* // Matches both <div>s
|
||||
* page.getByText(Pattern.compile("Hello"));
|
||||
* page.getByText(Pattern.compile("Hello"))
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>Details</strong>
|
||||
@@ -776,19 +776,19 @@ public interface FrameLocator {
|
||||
* <p> You can locate by text substring, exact string, or a regular expression:
|
||||
* <pre>{@code
|
||||
* // Matches <span>
|
||||
* page.getByText("world");
|
||||
* page.getByText("world")
|
||||
*
|
||||
* // Matches first <div>
|
||||
* page.getByText("Hello world");
|
||||
* page.getByText("Hello world")
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
|
||||
*
|
||||
* // Matches both <div>s
|
||||
* page.getByText(Pattern.compile("Hello"));
|
||||
* page.getByText(Pattern.compile("Hello"))
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>Details</strong>
|
||||
@@ -816,19 +816,19 @@ public interface FrameLocator {
|
||||
* <p> You can locate by text substring, exact string, or a regular expression:
|
||||
* <pre>{@code
|
||||
* // Matches <span>
|
||||
* page.getByText("world");
|
||||
* page.getByText("world")
|
||||
*
|
||||
* // Matches first <div>
|
||||
* page.getByText("Hello world");
|
||||
* page.getByText("Hello world")
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
|
||||
*
|
||||
* // Matches both <div>s
|
||||
* page.getByText(Pattern.compile("Hello"));
|
||||
* page.getByText(Pattern.compile("Hello"))
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>Details</strong>
|
||||
@@ -858,19 +858,19 @@ public interface FrameLocator {
|
||||
* <p> You can locate by text substring, exact string, or a regular expression:
|
||||
* <pre>{@code
|
||||
* // Matches <span>
|
||||
* page.getByText("world");
|
||||
* page.getByText("world")
|
||||
*
|
||||
* // Matches first <div>
|
||||
* page.getByText("Hello world");
|
||||
* page.getByText("Hello world")
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
|
||||
*
|
||||
* // Matches both <div>s
|
||||
* page.getByText(Pattern.compile("Hello"));
|
||||
* page.getByText(Pattern.compile("Hello"))
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>Details</strong>
|
||||
|
||||
@@ -48,7 +48,10 @@ import com.microsoft.playwright.options.*;
|
||||
*
|
||||
* <p> An example to trigger select-all with the keyboard
|
||||
* <pre>{@code
|
||||
* page.keyboard().press("ControlOrMeta+A");
|
||||
* // on Windows and Linux
|
||||
* page.keyboard().press("Control+A");
|
||||
* // on macOS
|
||||
* page.keyboard().press("Meta+A");
|
||||
* }</pre>
|
||||
*/
|
||||
public interface Keyboard {
|
||||
@@ -161,7 +164,7 @@ public interface Keyboard {
|
||||
* Page page = browser.newPage();
|
||||
* page.navigate("https://keycode.info");
|
||||
* page.keyboard().press("A");
|
||||
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("A.png")));
|
||||
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("A.png"));
|
||||
* page.keyboard().press("ArrowLeft");
|
||||
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("ArrowLeft.png")));
|
||||
* page.keyboard().press("Shift+O");
|
||||
@@ -208,7 +211,7 @@ public interface Keyboard {
|
||||
* Page page = browser.newPage();
|
||||
* page.navigate("https://keycode.info");
|
||||
* page.keyboard().press("A");
|
||||
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("A.png")));
|
||||
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("A.png"));
|
||||
* page.keyboard().press("ArrowLeft");
|
||||
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("ArrowLeft.png")));
|
||||
* page.keyboard().press("Shift+O");
|
||||
|
||||
@@ -29,26 +29,6 @@ import java.util.regex.Pattern;
|
||||
* <p> <a href="https://playwright.dev/java/docs/locators">Learn more about locators</a>.
|
||||
*/
|
||||
public interface Locator {
|
||||
class AriaSnapshotOptions {
|
||||
/**
|
||||
* Maximum time in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
|
||||
* value can be changed by using the {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout
|
||||
* BrowserContext.setDefaultTimeout()} or {@link com.microsoft.playwright.Page#setDefaultTimeout Page.setDefaultTimeout()}
|
||||
* methods.
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Maximum time in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
|
||||
* value can be changed by using the {@link com.microsoft.playwright.BrowserContext#setDefaultTimeout
|
||||
* BrowserContext.setDefaultTimeout()} or {@link com.microsoft.playwright.Page#setDefaultTimeout Page.setDefaultTimeout()}
|
||||
* methods.
|
||||
*/
|
||||
public AriaSnapshotOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class BlurOptions {
|
||||
/**
|
||||
* Maximum time in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
|
||||
@@ -600,14 +580,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;
|
||||
@@ -616,14 +600,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;
|
||||
@@ -707,10 +695,6 @@ public interface Locator {
|
||||
* <article><div>Playwright</div></article>}.
|
||||
*/
|
||||
public Object hasText;
|
||||
/**
|
||||
* Only matches visible or invisible elements.
|
||||
*/
|
||||
public Boolean visible;
|
||||
|
||||
/**
|
||||
* Narrows down the results of the method to those which contain elements matching this relative locator. For example,
|
||||
@@ -773,13 +757,6 @@ public interface Locator {
|
||||
this.hasText = hasText;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Only matches visible or invisible elements.
|
||||
*/
|
||||
public FilterOptions setVisible(boolean visible) {
|
||||
this.visible = visible;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class FocusOptions {
|
||||
/**
|
||||
@@ -1522,9 +1499,7 @@ public interface Locator {
|
||||
public ScreenshotCaret caret;
|
||||
/**
|
||||
* Specify locators that should be masked when the screenshot is taken. Masked elements will be overlaid with a pink box
|
||||
* {@code #FF00FF} (customized by {@code maskColor}) that completely covers its bounding box. The mask is also applied to
|
||||
* invisible elements, see <a href="https://playwright.dev/java/docs/locators#matching-only-visible-elements">Matching only
|
||||
* visible elements</a> to disable that.
|
||||
* {@code #FF00FF} (customized by {@code maskColor}) that completely covers its bounding box.
|
||||
*/
|
||||
public List<Locator> mask;
|
||||
/**
|
||||
@@ -1598,9 +1573,7 @@ public interface Locator {
|
||||
}
|
||||
/**
|
||||
* Specify locators that should be masked when the screenshot is taken. Masked elements will be overlaid with a pink box
|
||||
* {@code #FF00FF} (customized by {@code maskColor}) that completely covers its bounding box. The mask is also applied to
|
||||
* invisible elements, see <a href="https://playwright.dev/java/docs/locators#matching-only-visible-elements">Matching only
|
||||
* visible elements</a> to disable that.
|
||||
* {@code #FF00FF} (customized by {@code maskColor}) that completely covers its bounding box.
|
||||
*/
|
||||
public ScreenshotOptions setMask(List<Locator> mask) {
|
||||
this.mask = mask;
|
||||
@@ -2178,7 +2151,7 @@ public interface Locator {
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* for (Locator li : page.getByRole("listitem").all())
|
||||
* for (Locator li : page.getByRole('listitem').all())
|
||||
* li.click();
|
||||
* }</pre>
|
||||
*
|
||||
@@ -2229,66 +2202,6 @@ public interface Locator {
|
||||
* @since v1.34
|
||||
*/
|
||||
Locator and(Locator locator);
|
||||
/**
|
||||
* Captures the aria snapshot of the given element. Read more about <a
|
||||
* href="https://playwright.dev/java/docs/aria-snapshots">aria snapshots</a> and {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#matchesAriaSnapshot LocatorAssertions.matchesAriaSnapshot()} for
|
||||
* the corresponding assertion.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* page.getByRole(AriaRole.LINK).ariaSnapshot();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>Details</strong>
|
||||
*
|
||||
* <p> This method captures the aria snapshot of the given element. The snapshot is a string that represents the state of the
|
||||
* element and its children. The snapshot can be used to assert the state of the element in the test, or to compare it to
|
||||
* state in the future.
|
||||
*
|
||||
* <p> The ARIA snapshot is represented using <a href="https://yaml.org/spec/1.2.2/">YAML</a> markup language:
|
||||
* <ul>
|
||||
* <li> The keys of the objects are the roles and optional accessible names of the elements.</li>
|
||||
* <li> The values are either text content or an array of child elements.</li>
|
||||
* <li> Generic static text can be represented with the {@code text} key.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p> Below is the HTML markup and the respective ARIA snapshot:
|
||||
*
|
||||
* @since v1.49
|
||||
*/
|
||||
default String ariaSnapshot() {
|
||||
return ariaSnapshot(null);
|
||||
}
|
||||
/**
|
||||
* Captures the aria snapshot of the given element. Read more about <a
|
||||
* href="https://playwright.dev/java/docs/aria-snapshots">aria snapshots</a> and {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#matchesAriaSnapshot LocatorAssertions.matchesAriaSnapshot()} for
|
||||
* the corresponding assertion.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* page.getByRole(AriaRole.LINK).ariaSnapshot();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>Details</strong>
|
||||
*
|
||||
* <p> This method captures the aria snapshot of the given element. The snapshot is a string that represents the state of the
|
||||
* element and its children. The snapshot can be used to assert the state of the element in the test, or to compare it to
|
||||
* state in the future.
|
||||
*
|
||||
* <p> The ARIA snapshot is represented using <a href="https://yaml.org/spec/1.2.2/">YAML</a> markup language:
|
||||
* <ul>
|
||||
* <li> The keys of the objects are the roles and optional accessible names of the elements.</li>
|
||||
* <li> The values are either text content or an array of child elements.</li>
|
||||
* <li> Generic static text can be represented with the {@code text} key.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p> Below is the HTML markup and the respective ARIA snapshot:
|
||||
*
|
||||
* @since v1.49
|
||||
*/
|
||||
String ariaSnapshot(AriaSnapshotOptions options);
|
||||
/**
|
||||
* Calls <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/blur">blur</a> on the element.
|
||||
*
|
||||
@@ -2602,20 +2515,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);
|
||||
/**
|
||||
* Programmatically dispatch an event on the matching element.
|
||||
*
|
||||
@@ -2651,6 +2550,7 @@ public interface Locator {
|
||||
*
|
||||
* <p> You can also specify {@code JSHandle} as the property value if you want live objects to be passed into the event:
|
||||
* <pre>{@code
|
||||
* // Note you can only create DataTransfer in Chromium and Firefox
|
||||
* JSHandle dataTransfer = page.evaluateHandle("() => new DataTransfer()");
|
||||
* Map<String, Object> arg = new HashMap<>();
|
||||
* arg.put("dataTransfer", dataTransfer);
|
||||
@@ -2699,6 +2599,7 @@ public interface Locator {
|
||||
*
|
||||
* <p> You can also specify {@code JSHandle} as the property value if you want live objects to be passed into the event:
|
||||
* <pre>{@code
|
||||
* // Note you can only create DataTransfer in Chromium and Firefox
|
||||
* JSHandle dataTransfer = page.evaluateHandle("() => new DataTransfer()");
|
||||
* Map<String, Object> arg = new HashMap<>();
|
||||
* arg.put("dataTransfer", dataTransfer);
|
||||
@@ -2746,6 +2647,7 @@ public interface Locator {
|
||||
*
|
||||
* <p> You can also specify {@code JSHandle} as the property value if you want live objects to be passed into the event:
|
||||
* <pre>{@code
|
||||
* // Note you can only create DataTransfer in Chromium and Firefox
|
||||
* JSHandle dataTransfer = page.evaluateHandle("() => new DataTransfer()");
|
||||
* Map<String, Object> arg = new HashMap<>();
|
||||
* arg.put("dataTransfer", dataTransfer);
|
||||
@@ -2861,13 +2763,9 @@ public interface Locator {
|
||||
* <p> If {@code expression} throws or rejects, this method throws.
|
||||
*
|
||||
* <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"
|
||||
* Locator tweets = page.locator(".tweet .retweets");
|
||||
* assertEquals("10 retweets", tweets.evaluate("node => node.innerText"));
|
||||
* }</pre>
|
||||
*
|
||||
* @param expression JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the function is
|
||||
@@ -2893,13 +2791,9 @@ public interface Locator {
|
||||
* <p> If {@code expression} throws or rejects, this method throws.
|
||||
*
|
||||
* <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"
|
||||
* Locator tweets = page.locator(".tweet .retweets");
|
||||
* assertEquals("10 retweets", tweets.evaluate("node => node.innerText"));
|
||||
* }</pre>
|
||||
*
|
||||
* @param expression JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the function is
|
||||
@@ -2924,13 +2818,9 @@ public interface Locator {
|
||||
* <p> If {@code expression} throws or rejects, this method throws.
|
||||
*
|
||||
* <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"
|
||||
* Locator tweets = page.locator(".tweet .retweets");
|
||||
* assertEquals("10 retweets", tweets.evaluate("node => node.innerText"));
|
||||
* }</pre>
|
||||
*
|
||||
* @param expression JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the function is
|
||||
@@ -3565,19 +3455,19 @@ public interface Locator {
|
||||
* <p> You can locate by text substring, exact string, or a regular expression:
|
||||
* <pre>{@code
|
||||
* // Matches <span>
|
||||
* page.getByText("world");
|
||||
* page.getByText("world")
|
||||
*
|
||||
* // Matches first <div>
|
||||
* page.getByText("Hello world");
|
||||
* page.getByText("Hello world")
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
|
||||
*
|
||||
* // Matches both <div>s
|
||||
* page.getByText(Pattern.compile("Hello"));
|
||||
* page.getByText(Pattern.compile("Hello"))
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>Details</strong>
|
||||
@@ -3607,19 +3497,19 @@ public interface Locator {
|
||||
* <p> You can locate by text substring, exact string, or a regular expression:
|
||||
* <pre>{@code
|
||||
* // Matches <span>
|
||||
* page.getByText("world");
|
||||
* page.getByText("world")
|
||||
*
|
||||
* // Matches first <div>
|
||||
* page.getByText("Hello world");
|
||||
* page.getByText("Hello world")
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
|
||||
*
|
||||
* // Matches both <div>s
|
||||
* page.getByText(Pattern.compile("Hello"));
|
||||
* page.getByText(Pattern.compile("Hello"))
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>Details</strong>
|
||||
@@ -3647,19 +3537,19 @@ public interface Locator {
|
||||
* <p> You can locate by text substring, exact string, or a regular expression:
|
||||
* <pre>{@code
|
||||
* // Matches <span>
|
||||
* page.getByText("world");
|
||||
* page.getByText("world")
|
||||
*
|
||||
* // Matches first <div>
|
||||
* page.getByText("Hello world");
|
||||
* page.getByText("Hello world")
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
|
||||
*
|
||||
* // Matches both <div>s
|
||||
* page.getByText(Pattern.compile("Hello"));
|
||||
* page.getByText(Pattern.compile("Hello"))
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>Details</strong>
|
||||
@@ -3689,19 +3579,19 @@ public interface Locator {
|
||||
* <p> You can locate by text substring, exact string, or a regular expression:
|
||||
* <pre>{@code
|
||||
* // Matches <span>
|
||||
* page.getByText("world");
|
||||
* page.getByText("world")
|
||||
*
|
||||
* // Matches first <div>
|
||||
* page.getByText("Hello world");
|
||||
* page.getByText("Hello world")
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
|
||||
*
|
||||
* // Matches both <div>s
|
||||
* page.getByText(Pattern.compile("Hello"));
|
||||
* page.getByText(Pattern.compile("Hello"))
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>Details</strong>
|
||||
@@ -3998,9 +3888,7 @@ public interface Locator {
|
||||
*/
|
||||
boolean isDisabled(IsDisabledOptions options);
|
||||
/**
|
||||
* Returns whether the element is <a href="https://playwright.dev/java/docs/actionability#editable">editable</a>. If the
|
||||
* target element is not an {@code <input>}, {@code <textarea>}, {@code <select>}, {@code [contenteditable]} and does not
|
||||
* have a role allowing {@code [aria-readonly]}, this method throws an error.
|
||||
* Returns whether the element is <a href="https://playwright.dev/java/docs/actionability#editable">editable</a>.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> If you need to assert that an element is editable, prefer {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#isEditable LocatorAssertions.isEditable()} to avoid flakiness. See
|
||||
@@ -4017,9 +3905,7 @@ public interface Locator {
|
||||
return isEditable(null);
|
||||
}
|
||||
/**
|
||||
* Returns whether the element is <a href="https://playwright.dev/java/docs/actionability#editable">editable</a>. If the
|
||||
* target element is not an {@code <input>}, {@code <textarea>}, {@code <select>}, {@code [contenteditable]} and does not
|
||||
* have a role allowing {@code [aria-readonly]}, this method throws an error.
|
||||
* Returns whether the element is <a href="https://playwright.dev/java/docs/actionability#editable">editable</a>.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> If you need to assert that an element is editable, prefer {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#isEditable LocatorAssertions.isEditable()} to avoid flakiness. See
|
||||
@@ -4200,21 +4086,17 @@ public interface Locator {
|
||||
/**
|
||||
* Creates a locator matching all elements that match one or both of the two locators.
|
||||
*
|
||||
* <p> Note that when both locators match something, the resulting locator will have multiple matches, potentially causing a <a
|
||||
* href="https://playwright.dev/java/docs/locators#strictness">locator strictness</a> violation.
|
||||
* <p> Note that when both locators match something, the resulting locator will have multiple matches and violate <a
|
||||
* href="https://playwright.dev/java/docs/locators#strictness">locator strictness</a> guidelines.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
*
|
||||
* <p> Consider a scenario where you'd like to click on a "New email" button, but sometimes a security settings dialog shows up
|
||||
* instead. In this case, you can wait for either a "New email" button, or a dialog and act accordingly.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> If both "New email" button and security dialog appear on screen, the "or" locator will match both of them, possibly
|
||||
* throwing the <a href="https://playwright.dev/java/docs/locators#strictness">"strict mode violation" error</a>. In this
|
||||
* case, you can use {@link com.microsoft.playwright.Locator#first Locator.first()} to only match one of them.
|
||||
* <pre>{@code
|
||||
* Locator newEmail = page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("New"));
|
||||
* Locator dialog = page.getByText("Confirm security settings");
|
||||
* assertThat(newEmail.or(dialog).first()).isVisible();
|
||||
* assertThat(newEmail.or(dialog)).isVisible();
|
||||
* if (dialog.isVisible())
|
||||
* page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Dismiss")).click();
|
||||
* newEmail.click();
|
||||
@@ -5245,9 +5127,7 @@ public interface Locator {
|
||||
*/
|
||||
void setInputFiles(FilePayload[] files, SetInputFilesOptions options);
|
||||
/**
|
||||
* Perform a tap gesture on the element matching the locator. For examples of emulating other gestures by manually
|
||||
* dispatching touch events, see the <a href="https://playwright.dev/java/docs/touch-events">emulating legacy touch
|
||||
* events</a> page.
|
||||
* Perform a tap gesture on the element matching the locator.
|
||||
*
|
||||
* <p> <strong>Details</strong>
|
||||
*
|
||||
@@ -5273,9 +5153,7 @@ public interface Locator {
|
||||
tap(null);
|
||||
}
|
||||
/**
|
||||
* Perform a tap gesture on the element matching the locator. For examples of emulating other gestures by manually
|
||||
* dispatching touch events, see the <a href="https://playwright.dev/java/docs/touch-events">emulating legacy touch
|
||||
* events</a> page.
|
||||
* Perform a tap gesture on the element matching the locator.
|
||||
*
|
||||
* <p> <strong>Details</strong>
|
||||
*
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -959,17 +959,10 @@ public interface Page extends AutoCloseable {
|
||||
}
|
||||
class EmulateMediaOptions {
|
||||
/**
|
||||
* Emulates <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-colors-scheme</a> media
|
||||
* feature, supported values are {@code "light"} and {@code "dark"}. Passing {@code null} disables color scheme emulation.
|
||||
* {@code "no-preference"} is deprecated.
|
||||
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code
|
||||
* "no-preference"}. Passing {@code null} disables color scheme emulation.
|
||||
*/
|
||||
public Optional<ColorScheme> colorScheme;
|
||||
/**
|
||||
* Emulates {@code "prefers-contrast"} media feature, supported values are {@code "no-preference"}, {@code "more"}. Passing
|
||||
* {@code null} disables contrast emulation.
|
||||
*/
|
||||
public Optional<Contrast> contrast;
|
||||
/**
|
||||
* Emulates {@code "forced-colors"} media feature, supported values are {@code "active"} and {@code "none"}. Passing {@code
|
||||
* null} disables forced colors emulation.
|
||||
@@ -987,23 +980,13 @@ public interface Page extends AutoCloseable {
|
||||
public Optional<ReducedMotion> reducedMotion;
|
||||
|
||||
/**
|
||||
* Emulates <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">prefers-colors-scheme</a> media
|
||||
* feature, supported values are {@code "light"} and {@code "dark"}. Passing {@code null} disables color scheme emulation.
|
||||
* {@code "no-preference"} is deprecated.
|
||||
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code
|
||||
* "no-preference"}. Passing {@code null} disables color scheme emulation.
|
||||
*/
|
||||
public EmulateMediaOptions setColorScheme(ColorScheme colorScheme) {
|
||||
this.colorScheme = Optional.ofNullable(colorScheme);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates {@code "prefers-contrast"} media feature, supported values are {@code "no-preference"}, {@code "more"}. Passing
|
||||
* {@code null} disables contrast emulation.
|
||||
*/
|
||||
public EmulateMediaOptions setContrast(Contrast contrast) {
|
||||
this.contrast = Optional.ofNullable(contrast);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Emulates {@code "forced-colors"} media feature, supported values are {@code "active"} and {@code "none"}. Passing {@code
|
||||
* null} disables forced colors emulation.
|
||||
@@ -2535,9 +2518,7 @@ public interface Page extends AutoCloseable {
|
||||
public Boolean fullPage;
|
||||
/**
|
||||
* Specify locators that should be masked when the screenshot is taken. Masked elements will be overlaid with a pink box
|
||||
* {@code #FF00FF} (customized by {@code maskColor}) that completely covers its bounding box. The mask is also applied to
|
||||
* invisible elements, see <a href="https://playwright.dev/java/docs/locators#matching-only-visible-elements">Matching only
|
||||
* visible elements</a> to disable that.
|
||||
* {@code #FF00FF} (customized by {@code maskColor}) that completely covers its bounding box.
|
||||
*/
|
||||
public List<Locator> mask;
|
||||
/**
|
||||
@@ -2632,9 +2613,7 @@ public interface Page extends AutoCloseable {
|
||||
}
|
||||
/**
|
||||
* Specify locators that should be masked when the screenshot is taken. Masked elements will be overlaid with a pink box
|
||||
* {@code #FF00FF} (customized by {@code maskColor}) that completely covers its bounding box. The mask is also applied to
|
||||
* invisible elements, see <a href="https://playwright.dev/java/docs/locators#matching-only-visible-elements">Matching only
|
||||
* visible elements</a> to disable that.
|
||||
* {@code #FF00FF} (customized by {@code maskColor}) that completely covers its bounding box.
|
||||
*/
|
||||
public ScreenshotOptions setMask(List<Locator> mask) {
|
||||
this.mask = mask;
|
||||
@@ -4172,9 +4151,9 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* page.dragAndDrop("#source", "#target");
|
||||
* page.dragAndDrop("#source", '#target');
|
||||
* // or specify exact positions relative to the top-left corners of the elements:
|
||||
* page.dragAndDrop("#source", "#target", new Page.DragAndDropOptions()
|
||||
* page.dragAndDrop("#source", '#target', new Page.DragAndDropOptions()
|
||||
* .setSourcePosition(34, 7).setTargetPosition(10, 20));
|
||||
* }</pre>
|
||||
*
|
||||
@@ -4193,9 +4172,9 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* page.dragAndDrop("#source", "#target");
|
||||
* page.dragAndDrop("#source", '#target');
|
||||
* // or specify exact positions relative to the top-left corners of the elements:
|
||||
* page.dragAndDrop("#source", "#target", new Page.DragAndDropOptions()
|
||||
* page.dragAndDrop("#source", '#target', new Page.DragAndDropOptions()
|
||||
* .setSourcePosition(34, 7).setTargetPosition(10, 20));
|
||||
* }</pre>
|
||||
*
|
||||
@@ -4235,6 +4214,8 @@ public interface Page extends AutoCloseable {
|
||||
* // → true
|
||||
* page.evaluate("() => matchMedia('(prefers-color-scheme: light)').matches");
|
||||
* // → false
|
||||
* page.evaluate("() => matchMedia('(prefers-color-scheme: no-preference)').matches");
|
||||
* // → false
|
||||
* }</pre>
|
||||
*
|
||||
* @since v1.8
|
||||
@@ -4271,6 +4252,8 @@ public interface Page extends AutoCloseable {
|
||||
* // → true
|
||||
* page.evaluate("() => matchMedia('(prefers-color-scheme: light)').matches");
|
||||
* // → false
|
||||
* page.evaluate("() => matchMedia('(prefers-color-scheme: no-preference)').matches");
|
||||
* // → false
|
||||
* }</pre>
|
||||
*
|
||||
* @since v1.8
|
||||
@@ -4577,7 +4560,7 @@ public interface Page extends AutoCloseable {
|
||||
* public static void main(String[] args) {
|
||||
* try (Playwright playwright = Playwright.create()) {
|
||||
* BrowserType webkit = playwright.webkit();
|
||||
* Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false));
|
||||
* Browser browser = webkit.launch({ headless: false });
|
||||
* BrowserContext context = browser.newContext();
|
||||
* Page page = context.newPage();
|
||||
* page.exposeBinding("pageURL", (source, args) -> source.page().url());
|
||||
@@ -4627,7 +4610,7 @@ public interface Page extends AutoCloseable {
|
||||
* public static void main(String[] args) {
|
||||
* try (Playwright playwright = Playwright.create()) {
|
||||
* BrowserType webkit = playwright.webkit();
|
||||
* Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false));
|
||||
* Browser browser = webkit.launch({ headless: false });
|
||||
* BrowserContext context = browser.newContext();
|
||||
* Page page = context.newPage();
|
||||
* page.exposeBinding("pageURL", (source, args) -> source.page().url());
|
||||
@@ -4679,27 +4662,26 @@ public interface Page extends AutoCloseable {
|
||||
* public static void main(String[] args) {
|
||||
* try (Playwright playwright = Playwright.create()) {
|
||||
* BrowserType webkit = playwright.webkit();
|
||||
* Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false));
|
||||
* Browser browser = webkit.launch({ headless: false });
|
||||
* Page page = browser.newPage();
|
||||
* page.exposeFunction("sha256", args -> {
|
||||
* String text = (String) args[0];
|
||||
* MessageDigest crypto;
|
||||
* try {
|
||||
* String text = (String) args[0];
|
||||
* MessageDigest crypto = MessageDigest.getInstance("SHA-256");
|
||||
* byte[] token = crypto.digest(text.getBytes(StandardCharsets.UTF_8));
|
||||
* return Base64.getEncoder().encodeToString(token);
|
||||
* crypto = MessageDigest.getInstance("SHA-256");
|
||||
* } catch (NoSuchAlgorithmException e) {
|
||||
* return null;
|
||||
* }
|
||||
* byte[] token = crypto.digest(text.getBytes(StandardCharsets.UTF_8));
|
||||
* return Base64.getEncoder().encodeToString(token);
|
||||
* });
|
||||
* page.setContent(
|
||||
* "<script>\n" +
|
||||
* page.setContent("<script>\n" +
|
||||
* " async function onClick() {\n" +
|
||||
* " document.querySelector('div').textContent = await window.sha256('PLAYWRIGHT');\n" +
|
||||
* " }\n" +
|
||||
* "</script>\n" +
|
||||
* "<button onclick=\"onClick()\">Click me</button>\n" +
|
||||
* "<div></div>"
|
||||
* );
|
||||
* "<div></div>\n");
|
||||
* page.click("button");
|
||||
* }
|
||||
* }
|
||||
@@ -4775,7 +4757,7 @@ public interface Page extends AutoCloseable {
|
||||
* Frame frame = page.frame("frame-name");
|
||||
* }</pre>
|
||||
* <pre>{@code
|
||||
* Frame frame = page.frameByUrl(Pattern.compile(".*domain.*"));
|
||||
* Frame frame = page.frameByUrl(Pattern.compile(".*domain.*");
|
||||
* }</pre>
|
||||
*
|
||||
* @param name Frame name specified in the {@code iframe}'s {@code name} attribute.
|
||||
@@ -5181,19 +5163,19 @@ public interface Page extends AutoCloseable {
|
||||
* <p> You can locate by text substring, exact string, or a regular expression:
|
||||
* <pre>{@code
|
||||
* // Matches <span>
|
||||
* page.getByText("world");
|
||||
* page.getByText("world")
|
||||
*
|
||||
* // Matches first <div>
|
||||
* page.getByText("Hello world");
|
||||
* page.getByText("Hello world")
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
|
||||
*
|
||||
* // Matches both <div>s
|
||||
* page.getByText(Pattern.compile("Hello"));
|
||||
* page.getByText(Pattern.compile("Hello"))
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>Details</strong>
|
||||
@@ -5223,19 +5205,19 @@ public interface Page extends AutoCloseable {
|
||||
* <p> You can locate by text substring, exact string, or a regular expression:
|
||||
* <pre>{@code
|
||||
* // Matches <span>
|
||||
* page.getByText("world");
|
||||
* page.getByText("world")
|
||||
*
|
||||
* // Matches first <div>
|
||||
* page.getByText("Hello world");
|
||||
* page.getByText("Hello world")
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
|
||||
*
|
||||
* // Matches both <div>s
|
||||
* page.getByText(Pattern.compile("Hello"));
|
||||
* page.getByText(Pattern.compile("Hello"))
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>Details</strong>
|
||||
@@ -5263,19 +5245,19 @@ public interface Page extends AutoCloseable {
|
||||
* <p> You can locate by text substring, exact string, or a regular expression:
|
||||
* <pre>{@code
|
||||
* // Matches <span>
|
||||
* page.getByText("world");
|
||||
* page.getByText("world")
|
||||
*
|
||||
* // Matches first <div>
|
||||
* page.getByText("Hello world");
|
||||
* page.getByText("Hello world")
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
|
||||
*
|
||||
* // Matches both <div>s
|
||||
* page.getByText(Pattern.compile("Hello"));
|
||||
* page.getByText(Pattern.compile("Hello"))
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>Details</strong>
|
||||
@@ -5305,19 +5287,19 @@ public interface Page extends AutoCloseable {
|
||||
* <p> You can locate by text substring, exact string, or a regular expression:
|
||||
* <pre>{@code
|
||||
* // Matches <span>
|
||||
* page.getByText("world");
|
||||
* page.getByText("world")
|
||||
*
|
||||
* // Matches first <div>
|
||||
* page.getByText("Hello world");
|
||||
* page.getByText("Hello world")
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true));
|
||||
* page.getByText("Hello", new Page.GetByTextOptions().setExact(true))
|
||||
*
|
||||
* // Matches both <div>s
|
||||
* page.getByText(Pattern.compile("Hello"));
|
||||
* page.getByText(Pattern.compile("Hello"))
|
||||
*
|
||||
* // Matches second <div>
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE));
|
||||
* page.getByText(Pattern.compile("^hello$", Pattern.CASE_INSENSITIVE))
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>Details</strong>
|
||||
@@ -5812,8 +5794,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.
|
||||
@@ -5826,6 +5808,8 @@ public interface Page extends AutoCloseable {
|
||||
/**
|
||||
* Returns the PDF buffer.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Generating a pdf is currently only supported in Chromium headless.
|
||||
*
|
||||
* <p> {@code page.pdf()} generates a pdf of the page with {@code print} css media. To generate a pdf with {@code screen}
|
||||
* media, call {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} before calling {@code page.pdf()}:
|
||||
*
|
||||
@@ -5884,6 +5868,8 @@ public interface Page extends AutoCloseable {
|
||||
/**
|
||||
* Returns the PDF buffer.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Generating a pdf is currently only supported in Chromium headless.
|
||||
*
|
||||
* <p> {@code page.pdf()} generates a pdf of the page with {@code print} css media. To generate a pdf with {@code screen}
|
||||
* media, call {@link com.microsoft.playwright.Page#emulateMedia Page.emulateMedia()} before calling {@code page.pdf()}:
|
||||
*
|
||||
@@ -6094,24 +6080,24 @@ public interface Page extends AutoCloseable {
|
||||
* <p> An example that closes a "Sign up to the newsletter" dialog when it appears:
|
||||
* <pre>{@code
|
||||
* // Setup the handler.
|
||||
* page.addLocatorHandler(page.getByText("Sign up to the newsletter"), () -> {
|
||||
* page.addLocatorHandler(page.getByText("Sign up to the newsletter"), () => {
|
||||
* page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("No thanks")).click();
|
||||
* });
|
||||
*
|
||||
* // Write the test as usual.
|
||||
* page.navigate("https://example.com");
|
||||
* page.goto("https://example.com");
|
||||
* page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> An example that skips the "Confirm your security details" page when it is shown:
|
||||
* <pre>{@code
|
||||
* // Setup the handler.
|
||||
* page.addLocatorHandler(page.getByText("Confirm your security details"), () -> {
|
||||
* page.addLocatorHandler(page.getByText("Confirm your security details")), () => {
|
||||
* page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Remind me later")).click();
|
||||
* });
|
||||
*
|
||||
* // Write the test as usual.
|
||||
* page.navigate("https://example.com");
|
||||
* page.goto("https://example.com");
|
||||
* page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
|
||||
* }</pre>
|
||||
*
|
||||
@@ -6120,19 +6106,19 @@ public interface Page extends AutoCloseable {
|
||||
* handler does not hide the {@code <body>} element.
|
||||
* <pre>{@code
|
||||
* // Setup the handler.
|
||||
* page.addLocatorHandler(page.locator("body"), () -> {
|
||||
* page.addLocatorHandler(page.locator("body")), () => {
|
||||
* page.evaluate("window.removeObstructionsForTestIfNeeded()");
|
||||
* }, new Page.AddLocatorHandlerOptions().setNoWaitAfter(true));
|
||||
* }, new Page.AddLocatorHandlerOptions.setNoWaitAfter(true));
|
||||
*
|
||||
* // Write the test as usual.
|
||||
* page.navigate("https://example.com");
|
||||
* page.goto("https://example.com");
|
||||
* page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> Handler takes the original locator as an argument. You can also automatically remove the handler after a number of
|
||||
* invocations by setting {@code times}:
|
||||
* <pre>{@code
|
||||
* page.addLocatorHandler(page.getByLabel("Close"), locator -> {
|
||||
* page.addLocatorHandler(page.getByLabel("Close"), locator => {
|
||||
* locator.click();
|
||||
* }, new Page.AddLocatorHandlerOptions().setTimes(1));
|
||||
* }</pre>
|
||||
@@ -6186,24 +6172,24 @@ public interface Page extends AutoCloseable {
|
||||
* <p> An example that closes a "Sign up to the newsletter" dialog when it appears:
|
||||
* <pre>{@code
|
||||
* // Setup the handler.
|
||||
* page.addLocatorHandler(page.getByText("Sign up to the newsletter"), () -> {
|
||||
* page.addLocatorHandler(page.getByText("Sign up to the newsletter"), () => {
|
||||
* page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("No thanks")).click();
|
||||
* });
|
||||
*
|
||||
* // Write the test as usual.
|
||||
* page.navigate("https://example.com");
|
||||
* page.goto("https://example.com");
|
||||
* page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> An example that skips the "Confirm your security details" page when it is shown:
|
||||
* <pre>{@code
|
||||
* // Setup the handler.
|
||||
* page.addLocatorHandler(page.getByText("Confirm your security details"), () -> {
|
||||
* page.addLocatorHandler(page.getByText("Confirm your security details")), () => {
|
||||
* page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Remind me later")).click();
|
||||
* });
|
||||
*
|
||||
* // Write the test as usual.
|
||||
* page.navigate("https://example.com");
|
||||
* page.goto("https://example.com");
|
||||
* page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
|
||||
* }</pre>
|
||||
*
|
||||
@@ -6212,19 +6198,19 @@ public interface Page extends AutoCloseable {
|
||||
* handler does not hide the {@code <body>} element.
|
||||
* <pre>{@code
|
||||
* // Setup the handler.
|
||||
* page.addLocatorHandler(page.locator("body"), () -> {
|
||||
* page.addLocatorHandler(page.locator("body")), () => {
|
||||
* page.evaluate("window.removeObstructionsForTestIfNeeded()");
|
||||
* }, new Page.AddLocatorHandlerOptions().setNoWaitAfter(true));
|
||||
* }, new Page.AddLocatorHandlerOptions.setNoWaitAfter(true));
|
||||
*
|
||||
* // Write the test as usual.
|
||||
* page.navigate("https://example.com");
|
||||
* page.goto("https://example.com");
|
||||
* page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> Handler takes the original locator as an argument. You can also automatically remove the handler after a number of
|
||||
* invocations by setting {@code times}:
|
||||
* <pre>{@code
|
||||
* page.addLocatorHandler(page.getByLabel("Close"), locator -> {
|
||||
* page.addLocatorHandler(page.getByLabel("Close"), locator => {
|
||||
* locator.click();
|
||||
* }, new Page.AddLocatorHandlerOptions().setTimes(1));
|
||||
* }</pre>
|
||||
@@ -6317,8 +6303,8 @@ 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
|
||||
@@ -6376,8 +6362,8 @@ 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
|
||||
@@ -6433,8 +6419,8 @@ 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
|
||||
@@ -6492,8 +6478,8 @@ 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
|
||||
@@ -6549,8 +6535,8 @@ 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
|
||||
@@ -6608,8 +6594,8 @@ 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
|
||||
@@ -6655,8 +6641,8 @@ public interface Page extends AutoCloseable {
|
||||
* examples.
|
||||
* <pre>{@code
|
||||
* page.routeWebSocket("/ws", ws -> {
|
||||
* ws.onMessage(frame -> {
|
||||
* if ("request".equals(frame.text()))
|
||||
* ws.onMessage(message -> {
|
||||
* if ("request".equals(message))
|
||||
* ws.send("response");
|
||||
* });
|
||||
* });
|
||||
@@ -6680,8 +6666,8 @@ public interface Page extends AutoCloseable {
|
||||
* examples.
|
||||
* <pre>{@code
|
||||
* page.routeWebSocket("/ws", ws -> {
|
||||
* ws.onMessage(frame -> {
|
||||
* if ("request".equals(frame.text()))
|
||||
* ws.onMessage(message -> {
|
||||
* if ("request".equals(message))
|
||||
* ws.send("response");
|
||||
* });
|
||||
* });
|
||||
@@ -6705,8 +6691,8 @@ public interface Page extends AutoCloseable {
|
||||
* examples.
|
||||
* <pre>{@code
|
||||
* page.routeWebSocket("/ws", ws -> {
|
||||
* ws.onMessage(frame -> {
|
||||
* if ("request".equals(frame.text()))
|
||||
* ws.onMessage(message -> {
|
||||
* if ("request".equals(message))
|
||||
* ws.send("response");
|
||||
* });
|
||||
* });
|
||||
@@ -7207,7 +7193,7 @@ public interface Page extends AutoCloseable {
|
||||
* <p> <strong>NOTE:</strong> {@link com.microsoft.playwright.Page#setDefaultNavigationTimeout Page.setDefaultNavigationTimeout()} takes priority over
|
||||
* {@link com.microsoft.playwright.Page#setDefaultTimeout Page.setDefaultTimeout()}.
|
||||
*
|
||||
* @param timeout Maximum time in milliseconds. Pass {@code 0} to disable timeout.
|
||||
* @param timeout Maximum time in milliseconds
|
||||
* @since v1.8
|
||||
*/
|
||||
void setDefaultTimeout(double timeout);
|
||||
|
||||
@@ -363,17 +363,15 @@ public interface Route {
|
||||
*
|
||||
* <p> <strong>Details</strong>
|
||||
*
|
||||
* <p> The {@code headers} option applies to both the routed request and any redirects it initiates. However, {@code url},
|
||||
* {@code method}, and {@code postData} only apply to the original request and are not carried over to redirected requests.
|
||||
* <p> Note that any overrides such as {@code url} or {@code headers} only apply to the request being routed. If this request
|
||||
* results in a redirect, overrides will not be applied to the new redirected request. If you want to propagate a header
|
||||
* through redirects, use the combination of {@link com.microsoft.playwright.Route#fetch Route.fetch()} and {@link
|
||||
* com.microsoft.playwright.Route#fulfill Route.fulfill()} instead.
|
||||
*
|
||||
* <p> {@link com.microsoft.playwright.Route#resume Route.resume()} will immediately send the request to the network, other
|
||||
* matching handlers won't be invoked. Use {@link com.microsoft.playwright.Route#fallback Route.fallback()} If you want
|
||||
* next matching handler in the chain to be invoked.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> The {@code Cookie} header cannot be overridden using this method. If a value is provided, it will be ignored, and the
|
||||
* cookie will be loaded from the browser's cookie store. To set custom cookies, use {@link
|
||||
* com.microsoft.playwright.BrowserContext#addCookies BrowserContext.addCookies()}.
|
||||
*
|
||||
* @since v1.8
|
||||
*/
|
||||
default void resume() {
|
||||
@@ -395,17 +393,15 @@ public interface Route {
|
||||
*
|
||||
* <p> <strong>Details</strong>
|
||||
*
|
||||
* <p> The {@code headers} option applies to both the routed request and any redirects it initiates. However, {@code url},
|
||||
* {@code method}, and {@code postData} only apply to the original request and are not carried over to redirected requests.
|
||||
* <p> Note that any overrides such as {@code url} or {@code headers} only apply to the request being routed. If this request
|
||||
* results in a redirect, overrides will not be applied to the new redirected request. If you want to propagate a header
|
||||
* through redirects, use the combination of {@link com.microsoft.playwright.Route#fetch Route.fetch()} and {@link
|
||||
* com.microsoft.playwright.Route#fulfill Route.fulfill()} instead.
|
||||
*
|
||||
* <p> {@link com.microsoft.playwright.Route#resume Route.resume()} will immediately send the request to the network, other
|
||||
* matching handlers won't be invoked. Use {@link com.microsoft.playwright.Route#fallback Route.fallback()} If you want
|
||||
* next matching handler in the chain to be invoked.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> The {@code Cookie} header cannot be overridden using this method. If a value is provided, it will be ignored, and the
|
||||
* cookie will be loaded from the browser's cookie store. To set custom cookies, use {@link
|
||||
* com.microsoft.playwright.BrowserContext#addCookies BrowserContext.addCookies()}.
|
||||
*
|
||||
* @since v1.8
|
||||
*/
|
||||
void resume(ResumeOptions options);
|
||||
|
||||
@@ -20,9 +20,6 @@ package com.microsoft.playwright;
|
||||
/**
|
||||
* The Touchscreen class operates in main-frame CSS pixels relative to the top-left corner of the viewport. Methods on the
|
||||
* touchscreen can only be used in browser contexts that have been initialized with {@code hasTouch} set to true.
|
||||
*
|
||||
* <p> This class is limited to emulating tap gestures. For examples of other gestures simulated by manually dispatching touch
|
||||
* events, see the <a href="https://playwright.dev/java/docs/touch-events">emulating legacy touch events</a> page.
|
||||
*/
|
||||
public interface Touchscreen {
|
||||
/**
|
||||
|
||||
@@ -16,19 +16,12 @@
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import com.microsoft.playwright.options.*;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
@@ -150,29 +143,6 @@ public interface Tracing {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class GroupOptions {
|
||||
/**
|
||||
* Specifies a custom location for the group to be shown in the trace viewer. Defaults to the location of the {@link
|
||||
* com.microsoft.playwright.Tracing#group Tracing.group()} call.
|
||||
*/
|
||||
public Location location;
|
||||
|
||||
/**
|
||||
* Specifies a custom location for the group to be shown in the trace viewer. Defaults to the location of the {@link
|
||||
* com.microsoft.playwright.Tracing#group Tracing.group()} call.
|
||||
*/
|
||||
public GroupOptions setLocation(String file) {
|
||||
return setLocation(new Location(file));
|
||||
}
|
||||
/**
|
||||
* Specifies a custom location for the group to be shown in the trace viewer. Defaults to the location of the {@link
|
||||
* com.microsoft.playwright.Tracing#group Tracing.group()} call.
|
||||
*/
|
||||
public GroupOptions setLocation(Location location) {
|
||||
this.location = location;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class StopOptions {
|
||||
/**
|
||||
* Export trace into the file with the given path.
|
||||
@@ -206,12 +176,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()
|
||||
@@ -231,12 +195,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()
|
||||
@@ -313,56 +271,6 @@ public interface Tracing {
|
||||
* @since v1.15
|
||||
*/
|
||||
void startChunk(StartChunkOptions options);
|
||||
/**
|
||||
* <strong>NOTE:</strong> Use {@code test.step} instead when available.
|
||||
*
|
||||
* <p> Creates a new group within the trace, assigning any subsequent API calls to this group, until {@link
|
||||
* com.microsoft.playwright.Tracing#groupEnd Tracing.groupEnd()} is called. Groups can be nested and will be visible in the
|
||||
* trace viewer.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* // All actions between group and groupEnd
|
||||
* // will be shown in the trace viewer as a group.
|
||||
* page.context().tracing().group("Open Playwright.dev > API");
|
||||
* page.navigate("https://playwright.dev/");
|
||||
* page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("API")).click();
|
||||
* page.context().tracing().groupEnd();
|
||||
* }</pre>
|
||||
*
|
||||
* @param name Group name shown in the trace viewer.
|
||||
* @since v1.49
|
||||
*/
|
||||
default void group(String name) {
|
||||
group(name, null);
|
||||
}
|
||||
/**
|
||||
* <strong>NOTE:</strong> Use {@code test.step} instead when available.
|
||||
*
|
||||
* <p> Creates a new group within the trace, assigning any subsequent API calls to this group, until {@link
|
||||
* com.microsoft.playwright.Tracing#groupEnd Tracing.groupEnd()} is called. Groups can be nested and will be visible in the
|
||||
* trace viewer.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* // All actions between group and groupEnd
|
||||
* // will be shown in the trace viewer as a group.
|
||||
* page.context().tracing().group("Open Playwright.dev > API");
|
||||
* page.navigate("https://playwright.dev/");
|
||||
* page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("API")).click();
|
||||
* page.context().tracing().groupEnd();
|
||||
* }</pre>
|
||||
*
|
||||
* @param name Group name shown in the trace viewer.
|
||||
* @since v1.49
|
||||
*/
|
||||
void group(String name, GroupOptions options);
|
||||
/**
|
||||
* Closes the last group created by {@link com.microsoft.playwright.Tracing#group Tracing.group()}.
|
||||
*
|
||||
* @since v1.49
|
||||
*/
|
||||
void groupEnd();
|
||||
/**
|
||||
* Stop tracing.
|
||||
*
|
||||
|
||||
@@ -20,10 +20,7 @@ import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* The {@code WebSocket} class represents WebSocket connections within a page. It provides the ability to inspect and
|
||||
* manipulate the data being transmitted and received.
|
||||
*
|
||||
* <p> If you want to intercept or modify WebSocket frames, consider using {@code WebSocketRoute}.
|
||||
* The {@code WebSocket} class represents websocket connections in the page.
|
||||
*/
|
||||
public interface WebSocket {
|
||||
|
||||
|
||||
@@ -27,12 +27,12 @@ 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 -> {
|
||||
* ws.onMessage(frame -> {
|
||||
* if ("request".equals(frame.text()))
|
||||
* ws.onMessage(message -> {
|
||||
* if ("request".equals(message))
|
||||
* ws.send("response");
|
||||
* });
|
||||
* });
|
||||
@@ -45,8 +45,8 @@ import java.util.function.Consumer;
|
||||
* <p> Here is another example that handles JSON messages:
|
||||
* <pre>{@code
|
||||
* page.routeWebSocket("wss://example.com/ws", ws -> {
|
||||
* ws.onMessage(frame -> {
|
||||
* JsonObject json = new JsonParser().parse(frame.text()).getAsJsonObject();
|
||||
* ws.onMessage(message -> {
|
||||
* JsonObject json = new JsonParser().parse(message).getAsJsonObject();
|
||||
* if ("question".equals(json.get("request").getAsString())) {
|
||||
* Map<String, String> result = new HashMap();
|
||||
* result.put("response", "answer");
|
||||
@@ -67,11 +67,11 @@ import java.util.function.Consumer;
|
||||
* <pre>{@code
|
||||
* page.routeWebSocket("/ws", ws -> {
|
||||
* WebSocketRoute server = ws.connectToServer();
|
||||
* ws.onMessage(frame -> {
|
||||
* if ("request".equals(frame.text()))
|
||||
* ws.onMessage(message -> {
|
||||
* if ("request".equals(message))
|
||||
* server.send("request2");
|
||||
* else
|
||||
* server.send(frame.text());
|
||||
* server.send(message);
|
||||
* });
|
||||
* });
|
||||
* }</pre>
|
||||
@@ -92,13 +92,13 @@ import java.util.function.Consumer;
|
||||
* <pre>{@code
|
||||
* page.routeWebSocket("/ws", ws -> {
|
||||
* WebSocketRoute server = ws.connectToServer();
|
||||
* ws.onMessage(frame -> {
|
||||
* if (!"blocked-from-the-page".equals(frame.text()))
|
||||
* server.send(frame.text());
|
||||
* ws.onMessage(message -> {
|
||||
* if (!"blocked-from-the-page".equals(message))
|
||||
* server.send(message);
|
||||
* });
|
||||
* server.onMessage(frame -> {
|
||||
* if (!"blocked-from-the-server".equals(frame.text()))
|
||||
* ws.send(frame.text());
|
||||
* server.onMessage(message -> {
|
||||
* if (!"blocked-from-the-server".equals(message))
|
||||
* ws.send(message);
|
||||
* });
|
||||
* });
|
||||
* }</pre>
|
||||
|
||||
+4
-4
@@ -21,15 +21,15 @@ package com.microsoft.playwright.assertions;
|
||||
* The {@code APIResponseAssertions} class provides assertion methods that can be used to make assertions about the {@code
|
||||
* APIResponse} in the tests.
|
||||
* <pre>{@code
|
||||
* // ...
|
||||
* ...
|
||||
* import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
|
||||
*
|
||||
* public class TestPage {
|
||||
* // ...
|
||||
* ...
|
||||
* @Test
|
||||
* void navigatesToLoginPage() {
|
||||
* // ...
|
||||
* APIResponse response = page.request().get("https://playwright.dev");
|
||||
* ...
|
||||
* APIResponse response = page.request().get('https://playwright.dev');
|
||||
* assertThat(response).isOK();
|
||||
* }
|
||||
* }
|
||||
|
||||
+55
-342
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.microsoft.playwright.assertions;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
import com.microsoft.playwright.options.AriaRole;
|
||||
|
||||
@@ -24,14 +23,14 @@ import com.microsoft.playwright.options.AriaRole;
|
||||
* The {@code LocatorAssertions} class provides assertion methods that can be used to make assertions about the {@code
|
||||
* Locator} state in the tests.
|
||||
* <pre>{@code
|
||||
* // ...
|
||||
* ...
|
||||
* import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
|
||||
*
|
||||
* public class TestLocator {
|
||||
* // ...
|
||||
* ...
|
||||
* @Test
|
||||
* void statusBecomesSubmitted() {
|
||||
* // ...
|
||||
* ...
|
||||
* page.getByRole(AriaRole.BUTTON).click();
|
||||
* assertThat(page.locator(".status")).hasText("Submitted");
|
||||
* }
|
||||
@@ -59,37 +58,16 @@ public interface LocatorAssertions {
|
||||
}
|
||||
}
|
||||
class IsCheckedOptions {
|
||||
/**
|
||||
* Provides state to assert for. Asserts for input to be checked by default. This option can't be used when {@code
|
||||
* indeterminate} is set to true.
|
||||
*/
|
||||
public Boolean checked;
|
||||
/**
|
||||
* Asserts that the element is in the indeterminate (mixed) state. Only supported for checkboxes and radio buttons. This
|
||||
* option can't be true when {@code checked} is provided.
|
||||
*/
|
||||
public Boolean indeterminate;
|
||||
/**
|
||||
* Time to retry the assertion for in milliseconds. Defaults to {@code 5000}.
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Provides state to assert for. Asserts for input to be checked by default. This option can't be used when {@code
|
||||
* indeterminate} is set to true.
|
||||
*/
|
||||
public IsCheckedOptions setChecked(boolean checked) {
|
||||
this.checked = checked;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Asserts that the element is in the indeterminate (mixed) state. Only supported for checkboxes and radio buttons. This
|
||||
* option can't be true when {@code checked} is provided.
|
||||
*/
|
||||
public IsCheckedOptions setIndeterminate(boolean indeterminate) {
|
||||
this.indeterminate = indeterminate;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Time to retry the assertion for in milliseconds. Defaults to {@code 5000}.
|
||||
*/
|
||||
@@ -238,20 +216,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
|
||||
@@ -317,33 +281,6 @@ public interface LocatorAssertions {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class HasAccessibleErrorMessageOptions {
|
||||
/**
|
||||
* Whether to perform case-insensitive match. {@code ignoreCase} option takes precedence over the corresponding regular
|
||||
* expression flag if specified.
|
||||
*/
|
||||
public Boolean ignoreCase;
|
||||
/**
|
||||
* Time to retry the assertion for in milliseconds. Defaults to {@code 5000}.
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Whether to perform case-insensitive match. {@code ignoreCase} option takes precedence over the corresponding regular
|
||||
* expression flag if specified.
|
||||
*/
|
||||
public HasAccessibleErrorMessageOptions setIgnoreCase(boolean ignoreCase) {
|
||||
this.ignoreCase = ignoreCase;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Time to retry the assertion for in milliseconds. Defaults to {@code 5000}.
|
||||
*/
|
||||
public HasAccessibleErrorMessageOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class HasAccessibleNameOptions {
|
||||
/**
|
||||
* Whether to perform case-insensitive match. {@code ignoreCase} option takes precedence over the corresponding regular
|
||||
@@ -548,20 +485,6 @@ public interface LocatorAssertions {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class MatchesAriaSnapshotOptions {
|
||||
/**
|
||||
* Time to retry the assertion for in milliseconds. Defaults to {@code 5000}.
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Time to retry the assertion for in milliseconds. Defaults to {@code 5000}.
|
||||
*/
|
||||
public MatchesAriaSnapshotOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Makes the assertion check for the opposite condition. For example, this code tests that the Locator doesn't contain text
|
||||
* {@code "error"}:
|
||||
@@ -870,98 +793,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(new String[] {"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(new String[] {"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(new String[] {"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(new String[] {"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.
|
||||
@@ -1374,66 +1205,6 @@ public interface LocatorAssertions {
|
||||
* @since v1.44
|
||||
*/
|
||||
void hasAccessibleDescription(Pattern description, HasAccessibleDescriptionOptions options);
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with a given <a
|
||||
* href="https://w3c.github.io/aria/#aria-errormessage">aria errormessage</a>.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* Locator locator = page.getByTestId("username-input");
|
||||
* assertThat(locator).hasAccessibleErrorMessage("Username is required.");
|
||||
* }</pre>
|
||||
*
|
||||
* @param errorMessage Expected accessible error message.
|
||||
* @since v1.50
|
||||
*/
|
||||
default void hasAccessibleErrorMessage(String errorMessage) {
|
||||
hasAccessibleErrorMessage(errorMessage, null);
|
||||
}
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with a given <a
|
||||
* href="https://w3c.github.io/aria/#aria-errormessage">aria errormessage</a>.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* Locator locator = page.getByTestId("username-input");
|
||||
* assertThat(locator).hasAccessibleErrorMessage("Username is required.");
|
||||
* }</pre>
|
||||
*
|
||||
* @param errorMessage Expected accessible error message.
|
||||
* @since v1.50
|
||||
*/
|
||||
void hasAccessibleErrorMessage(String errorMessage, HasAccessibleErrorMessageOptions options);
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with a given <a
|
||||
* href="https://w3c.github.io/aria/#aria-errormessage">aria errormessage</a>.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* Locator locator = page.getByTestId("username-input");
|
||||
* assertThat(locator).hasAccessibleErrorMessage("Username is required.");
|
||||
* }</pre>
|
||||
*
|
||||
* @param errorMessage Expected accessible error message.
|
||||
* @since v1.50
|
||||
*/
|
||||
default void hasAccessibleErrorMessage(Pattern errorMessage) {
|
||||
hasAccessibleErrorMessage(errorMessage, null);
|
||||
}
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with a given <a
|
||||
* href="https://w3c.github.io/aria/#aria-errormessage">aria errormessage</a>.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* Locator locator = page.getByTestId("username-input");
|
||||
* assertThat(locator).hasAccessibleErrorMessage("Username is required.");
|
||||
* }</pre>
|
||||
*
|
||||
* @param errorMessage Expected accessible error message.
|
||||
* @since v1.50
|
||||
*/
|
||||
void hasAccessibleErrorMessage(Pattern errorMessage, HasAccessibleErrorMessageOptions options);
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with a given <a
|
||||
* href="https://w3c.github.io/accname/#dfn-accessible-name">accessible name</a>.
|
||||
@@ -1551,21 +1322,18 @@ 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()}.
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a
|
||||
* relaxed regular expression.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
|
||||
* assertThat(page.locator("#component")).hasClass("selected row");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -1575,21 +1343,18 @@ public interface LocatorAssertions {
|
||||
hasClass(expected, null);
|
||||
}
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
|
||||
* the element's {@code class} attribute. To match individual classes use {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#containsClass LocatorAssertions.containsClass()}.
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a
|
||||
* relaxed regular expression.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
|
||||
* assertThat(page.locator("#component")).hasClass("selected row");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -1597,21 +1362,18 @@ 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()}.
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a
|
||||
* relaxed regular expression.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
|
||||
* assertThat(page.locator("#component")).hasClass("selected row");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -1621,21 +1383,18 @@ public interface LocatorAssertions {
|
||||
hasClass(expected, null);
|
||||
}
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
|
||||
* the element's {@code class} attribute. To match individual classes use {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#containsClass LocatorAssertions.containsClass()}.
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a
|
||||
* relaxed regular expression.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
|
||||
* assertThat(page.locator("#component")).hasClass("selected row");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -1643,21 +1402,18 @@ 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()}.
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a
|
||||
* relaxed regular expression.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
|
||||
* assertThat(page.locator("#component")).hasClass("selected row");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -1667,21 +1423,18 @@ public interface LocatorAssertions {
|
||||
hasClass(expected, null);
|
||||
}
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
|
||||
* the element's {@code class} attribute. To match individual classes use {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#containsClass LocatorAssertions.containsClass()}.
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a
|
||||
* relaxed regular expression.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
|
||||
* assertThat(page.locator("#component")).hasClass("selected row");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -1689,21 +1442,18 @@ 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()}.
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a
|
||||
* relaxed regular expression.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
|
||||
* assertThat(page.locator("#component")).hasClass("selected row");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -1713,21 +1463,18 @@ public interface LocatorAssertions {
|
||||
hasClass(expected, null);
|
||||
}
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. When a string is provided, it must fully match
|
||||
* the element's {@code class} attribute. To match individual classes use {@link
|
||||
* com.microsoft.playwright.assertions.LocatorAssertions#containsClass LocatorAssertions.containsClass()}.
|
||||
* Ensures the {@code Locator} points to an element with given CSS classes. This needs to be a full match or using a
|
||||
* relaxed regular expression.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("#component")).hasClass("middle selected row");
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("(^|\\s)selected(\\s|$)"));
|
||||
* assertThat(page.locator("#component")).hasClass(Pattern.compile("selected"));
|
||||
* assertThat(page.locator("#component")).hasClass("selected row");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> When an array is passed, the method asserts that the list of elements located matches the corresponding list of expected
|
||||
* class values. Each element's class attribute is matched against the corresponding string or regular expression in the
|
||||
* array:
|
||||
* <p> Note that if array is passed as an expected value, entire lists of elements can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -2350,7 +2097,7 @@ public interface LocatorAssertions {
|
||||
*
|
||||
* <p> For example, given the following element:
|
||||
* <pre>{@code
|
||||
* page.locator("id=favorite-colors").selectOption(new String[]{"R", "G"});
|
||||
* page.locator("id=favorite-colors").selectOption(["R", "G"]);
|
||||
* assertThat(page.locator("id=favorite-colors")).hasValues(new Pattern[] { Pattern.compile("R"), Pattern.compile("G") });
|
||||
* }</pre>
|
||||
*
|
||||
@@ -2368,7 +2115,7 @@ public interface LocatorAssertions {
|
||||
*
|
||||
* <p> For example, given the following element:
|
||||
* <pre>{@code
|
||||
* page.locator("id=favorite-colors").selectOption(new String[]{"R", "G"});
|
||||
* page.locator("id=favorite-colors").selectOption(["R", "G"]);
|
||||
* assertThat(page.locator("id=favorite-colors")).hasValues(new Pattern[] { Pattern.compile("R"), Pattern.compile("G") });
|
||||
* }</pre>
|
||||
*
|
||||
@@ -2384,7 +2131,7 @@ public interface LocatorAssertions {
|
||||
*
|
||||
* <p> For example, given the following element:
|
||||
* <pre>{@code
|
||||
* page.locator("id=favorite-colors").selectOption(new String[]{"R", "G"});
|
||||
* page.locator("id=favorite-colors").selectOption(["R", "G"]);
|
||||
* assertThat(page.locator("id=favorite-colors")).hasValues(new Pattern[] { Pattern.compile("R"), Pattern.compile("G") });
|
||||
* }</pre>
|
||||
*
|
||||
@@ -2402,7 +2149,7 @@ public interface LocatorAssertions {
|
||||
*
|
||||
* <p> For example, given the following element:
|
||||
* <pre>{@code
|
||||
* page.locator("id=favorite-colors").selectOption(new String[]{"R", "G"});
|
||||
* page.locator("id=favorite-colors").selectOption(["R", "G"]);
|
||||
* assertThat(page.locator("id=favorite-colors")).hasValues(new Pattern[] { Pattern.compile("R"), Pattern.compile("G") });
|
||||
* }</pre>
|
||||
*
|
||||
@@ -2410,39 +2157,5 @@ public interface LocatorAssertions {
|
||||
* @since v1.23
|
||||
*/
|
||||
void hasValues(Pattern[] values, HasValuesOptions options);
|
||||
/**
|
||||
* Asserts that the target element matches the given <a
|
||||
* href="https://playwright.dev/java/docs/aria-snapshots">accessibility snapshot</a>.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* page.navigate("https://demo.playwright.dev/todomvc/");
|
||||
* assertThat(page.locator("body")).matchesAriaSnapshot("""
|
||||
* - heading "todos"
|
||||
* - textbox "What needs to be done?"
|
||||
* """);
|
||||
* }</pre>
|
||||
*
|
||||
* @since v1.49
|
||||
*/
|
||||
default void matchesAriaSnapshot(String expected) {
|
||||
matchesAriaSnapshot(expected, null);
|
||||
}
|
||||
/**
|
||||
* Asserts that the target element matches the given <a
|
||||
* href="https://playwright.dev/java/docs/aria-snapshots">accessibility snapshot</a>.
|
||||
*
|
||||
* <p> <strong>Usage</strong>
|
||||
* <pre>{@code
|
||||
* page.navigate("https://demo.playwright.dev/todomvc/");
|
||||
* assertThat(page.locator("body")).matchesAriaSnapshot("""
|
||||
* - heading "todos"
|
||||
* - textbox "What needs to be done?"
|
||||
* """);
|
||||
* }</pre>
|
||||
*
|
||||
* @since v1.49
|
||||
*/
|
||||
void matchesAriaSnapshot(String expected, MatchesAriaSnapshotOptions options);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,14 +22,14 @@ import java.util.regex.Pattern;
|
||||
* The {@code PageAssertions} class provides assertion methods that can be used to make assertions about the {@code Page}
|
||||
* state in the tests.
|
||||
* <pre>{@code
|
||||
* // ...
|
||||
* ...
|
||||
* import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
|
||||
*
|
||||
* public class TestPage {
|
||||
* // ...
|
||||
* ...
|
||||
* @Test
|
||||
* void navigatesToLoginPage() {
|
||||
* // ...
|
||||
* ...
|
||||
* page.getByText("Sign in").click();
|
||||
* assertThat(page).hasURL(Pattern.compile(".*\/login"));
|
||||
* }
|
||||
@@ -54,7 +54,7 @@ public interface PageAssertions {
|
||||
class HasURLOptions {
|
||||
/**
|
||||
* Whether to perform case-insensitive match. {@code ignoreCase} option takes precedence over the corresponding regular
|
||||
* expression parameter if specified. A provided predicate ignores this flag.
|
||||
* expression flag if specified.
|
||||
*/
|
||||
public Boolean ignoreCase;
|
||||
/**
|
||||
@@ -64,7 +64,7 @@ public interface PageAssertions {
|
||||
|
||||
/**
|
||||
* Whether to perform case-insensitive match. {@code ignoreCase} option takes precedence over the corresponding regular
|
||||
* expression parameter if specified. A provided predicate ignores this flag.
|
||||
* expression flag if specified.
|
||||
*/
|
||||
public HasURLOptions setIgnoreCase(boolean ignoreCase) {
|
||||
this.ignoreCase = ignoreCase;
|
||||
|
||||
+3
-2
@@ -30,13 +30,14 @@ import com.microsoft.playwright.impl.PageAssertionsImpl;
|
||||
*
|
||||
* <p> Consider the following example:
|
||||
* <pre>{@code
|
||||
* ...
|
||||
* import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
|
||||
*
|
||||
* public class TestExample {
|
||||
* // ...
|
||||
* ...
|
||||
* @Test
|
||||
* void statusBecomesSubmitted() {
|
||||
* // ...
|
||||
* ...
|
||||
* page.locator("#submit-button").click();
|
||||
* assertThat(page.locator(".status")).hasText("Submitted");
|
||||
* }
|
||||
|
||||
@@ -39,8 +39,6 @@ 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());
|
||||
@@ -53,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
|
||||
@@ -91,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) {
|
||||
@@ -131,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);
|
||||
}
|
||||
@@ -149,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"));
|
||||
}
|
||||
|
||||
@@ -215,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;
|
||||
@@ -46,27 +46,31 @@ class APIResponseImpl implements APIResponse {
|
||||
|
||||
@Override
|
||||
public byte[] body() {
|
||||
try {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("fetchUid", fetchUid());
|
||||
JsonObject json = context.sendMessage("fetchResponseBody", params, NO_TIMEOUT).getAsJsonObject();
|
||||
if (!json.has("binary")) {
|
||||
throw new PlaywrightException("Response has been disposed");
|
||||
return context.withLogging("APIResponse.body", () -> {
|
||||
try {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("fetchUid", fetchUid());
|
||||
JsonObject json = context.sendMessage("fetchResponseBody", params).getAsJsonObject();
|
||||
if (!json.has("binary")) {
|
||||
throw new PlaywrightException("Response has been disposed");
|
||||
}
|
||||
return Base64.getDecoder().decode(json.get("binary").getAsString());
|
||||
} catch (PlaywrightException e) {
|
||||
if (isSafeCloseError(e)) {
|
||||
throw new PlaywrightException("Response has been disposed");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
return Base64.getDecoder().decode(json.get("binary").getAsString());
|
||||
} catch (PlaywrightException e) {
|
||||
if (isSafeCloseError(e)) {
|
||||
throw new PlaywrightException("Response has been disposed");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@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 +116,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,37 +29,39 @@ 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);
|
||||
options.isNot = isNot;
|
||||
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;
|
||||
}
|
||||
expectOptions.isNot = isNot;
|
||||
if (isNot) {
|
||||
if (expectOptions.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 = result.received == null ? null : Serialization.deserialize(result.received);
|
||||
String log = (result.log == null) ? "" : String.join("\n", result.log);
|
||||
String log = String.join("\n", result.log);
|
||||
if (!log.isEmpty()) {
|
||||
log = "\nCall log:\n" + log;
|
||||
}
|
||||
@@ -72,9 +75,7 @@ abstract class AssertionsBase {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ 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 APIRequestContextImpl request;
|
||||
private final ClockImpl clock;
|
||||
@@ -51,7 +51,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
|
||||
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;
|
||||
@@ -69,6 +69,8 @@ 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 {
|
||||
@@ -96,30 +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());
|
||||
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() {
|
||||
@@ -261,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());
|
||||
}
|
||||
|
||||
@@ -269,14 +270,23 @@ 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 void close(CloseOptions options) {
|
||||
if (!closingOrClosed) {
|
||||
closingOrClosed = true;
|
||||
withLogging("BrowserContext.close", () -> closeImpl(options));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Cookie> cookies(String url) {
|
||||
return cookies(url == null ? new ArrayList<>() : Collections.singletonList(url));
|
||||
}
|
||||
|
||||
private void closeImpl(CloseOptions options) {
|
||||
if (!closeWasCalled) {
|
||||
closeWasCalled = true;
|
||||
if (options == null) {
|
||||
options = new CloseOptions();
|
||||
}
|
||||
@@ -285,7 +295,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
for (Map.Entry<String, HarRecorder> entry : harRecorders.entrySet()) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("harId", entry.getKey());
|
||||
JsonObject json = sendMessage("harExport", params, NO_TIMEOUT).getAsJsonObject();
|
||||
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();
|
||||
@@ -297,46 +307,42 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
JsonObject unzipParams = new JsonObject();
|
||||
unzipParams.addProperty("zipFile", tmpPath);
|
||||
unzipParams.addProperty("harFile", harParams.path.toString());
|
||||
connection.localUtils.sendMessage("harUnzip", unzipParams, NO_TIMEOUT);
|
||||
connection.localUtils.sendMessage("harUnzip", unzipParams);
|
||||
} else {
|
||||
artifact.saveAs(harParams.path);
|
||||
}
|
||||
artifact.delete();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("close", params, NO_TIMEOUT);
|
||||
sendMessage("close", params);
|
||||
}
|
||||
runUntil(() -> {}, closePromise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Cookie> cookies(String url) {
|
||||
return cookies(url == null ? new ArrayList<>() : Collections.singletonList(url));
|
||||
}
|
||||
|
||||
|
||||
@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 void addInitScript(String script) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("source", script);
|
||||
sendMessage("addInitScript", params, NO_TIMEOUT);
|
||||
withLogging("BrowserContext.addInitScript", () -> addInitScriptImpl(script));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInitScript(Path path) {
|
||||
try {
|
||||
byte[] bytes = readAllBytes(path);
|
||||
addInitScript(new String(bytes, UTF_8));
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to read script from file", e);
|
||||
}
|
||||
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
|
||||
@@ -344,6 +350,12 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
return new ArrayList<>(backgroundPages);
|
||||
}
|
||||
|
||||
private void addInitScriptImpl(String script) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("source", script);
|
||||
sendMessage("addInitScript", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BrowserImpl browser() {
|
||||
return browser;
|
||||
@@ -351,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();
|
||||
}
|
||||
@@ -358,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) {
|
||||
@@ -373,24 +389,28 @@ 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 void exposeBinding(String name, BindingCallback playwrightBinding, ExposeBindingOptions options) {
|
||||
exposeBindingImpl(name, playwrightBinding, options);
|
||||
withLogging("BrowserContext.exposeBinding", () -> exposeBindingImpl(name, playwrightBinding, options));
|
||||
}
|
||||
|
||||
private void exposeBindingImpl(String name, BindingCallback playwrightBinding, ExposeBindingOptions options) {
|
||||
@@ -409,16 +429,21 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
if (options != null && options.handle != null && options.handle) {
|
||||
params.addProperty("needsHandle", true);
|
||||
}
|
||||
sendMessage("exposeBinding", params, NO_TIMEOUT);
|
||||
sendMessage("exposeBinding", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exposeFunction(String name, FunctionCallback playwrightFunction) {
|
||||
exposeBindingImpl(name, (BindingCallback.Source source, Object... args) -> playwrightFunction.call(args), null);
|
||||
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();
|
||||
}
|
||||
@@ -427,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()");
|
||||
}
|
||||
@@ -451,7 +480,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
|
||||
@Override
|
||||
public void route(String url, Consumer<Route> handler, RouteOptions options) {
|
||||
route(UrlMatcher.forGlob(baseUrl(), url, this.connection.localUtils, false), handler, options);
|
||||
route(new UrlMatcher(baseUrl, url), handler, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -470,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
|
||||
@@ -500,88 +531,106 @@ 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);
|
||||
}
|
||||
if (contentPolicy == null) {
|
||||
contentPolicy = HarContentPolicy.ATTACH;
|
||||
}
|
||||
|
||||
void recordIntoHar(PageImpl page, Path har, RouteFromHAROptions options) {
|
||||
JsonObject params = new JsonObject();
|
||||
if (page != null) {
|
||||
params.add("page", page.toProtocolRef());
|
||||
}
|
||||
JsonObject recordHarArgs = new JsonObject();
|
||||
recordHarArgs.addProperty("zip", har.toString().endsWith(".zip"));
|
||||
recordHarArgs.addProperty("content", contentPolicy.name().toLowerCase());
|
||||
recordHarArgs.addProperty("mode", (options.updateMode == null ? HarMode.MINIMAL : options.updateMode).name().toLowerCase());
|
||||
addHarUrlFilter(recordHarArgs, options.url);
|
||||
|
||||
params.add("options", recordHarArgs);
|
||||
JsonObject json = sendMessage("harStart", params, NO_TIMEOUT).getAsJsonObject();
|
||||
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, contentPolicy));
|
||||
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);
|
||||
withLogging("BrowserContext.setOffline", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("offline", offline);
|
||||
sendMessage("setOffline", params);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String storageState(StorageStateOptions options) {
|
||||
if (options == null) {
|
||||
options = new StorageStateOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.remove("path");
|
||||
JsonElement json = sendMessage("storageState", params, NO_TIMEOUT);
|
||||
|
||||
String storageState = json.toString();
|
||||
if (options.path != null) {
|
||||
Utils.writeToFile(storageState.getBytes(StandardCharsets.UTF_8), options.path);
|
||||
}
|
||||
return storageState;
|
||||
return withLogging("BrowserContext.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;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -591,13 +640,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
|
||||
@@ -643,16 +694,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) {
|
||||
@@ -805,10 +858,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);
|
||||
}
|
||||
@@ -817,49 +868,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,6 +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.HarContentPolicy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -28,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<>();
|
||||
@@ -66,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();
|
||||
}
|
||||
@@ -110,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);
|
||||
@@ -138,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());
|
||||
@@ -169,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();
|
||||
}
|
||||
@@ -192,11 +246,15 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
if (page != null) {
|
||||
params.add("page", ((PageImpl) page).toProtocolRef());
|
||||
}
|
||||
sendMessage("startTracing", params, 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();
|
||||
@@ -237,46 +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);
|
||||
}
|
||||
}
|
||||
|
||||
private void didClose() {
|
||||
isConnected = false;
|
||||
listeners.notify(EventType.DISCONNECTED, this);
|
||||
|
||||
@@ -22,30 +22,33 @@ 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,6 +57,10 @@ 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();
|
||||
}
|
||||
@@ -76,12 +83,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 +95,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 +118,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,26 +146,60 @@ 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()) {
|
||||
Path cwd = Paths.get("").toAbsolutePath();
|
||||
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 +227,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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -14,23 +12,18 @@ class ClockImpl implements Clock {
|
||||
this.browserContext = browserContext;
|
||||
}
|
||||
|
||||
private void sendMessageWithLogging(String method, JsonObject params) {
|
||||
String capitalizedMethod = method.substring(0, 1).toUpperCase() + method.substring(1);
|
||||
browserContext.sendMessage("clock" + capitalizedMethod, params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fastForward(long ticks) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("ticksNumber", ticks);
|
||||
sendMessageWithLogging("fastForward", params);
|
||||
browserContext.sendMessage("clockFastForward", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fastForward(String ticks) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("ticksString", ticks);
|
||||
sendMessageWithLogging("fastForward", params);
|
||||
browserContext.sendMessage("clockFastForward", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -39,89 +32,89 @@ class ClockImpl implements Clock {
|
||||
if (options != null) {
|
||||
parseTime(options.time, params);
|
||||
}
|
||||
sendMessageWithLogging("install", params);
|
||||
browserContext.sendMessage("clockInstall", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runFor(long ticks) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("ticksNumber", ticks);
|
||||
sendMessageWithLogging("runFor", params);
|
||||
browserContext.sendMessage("clockRunFor", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runFor(String ticks) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("ticksString", ticks);
|
||||
sendMessageWithLogging("runFor", params);
|
||||
browserContext.sendMessage("clockRunFor", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pauseAt(long time) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("timeNumber", time);
|
||||
sendMessageWithLogging("pauseAt", params);
|
||||
browserContext.sendMessage("clockPauseAt", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pauseAt(String time) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("timeString", time);
|
||||
sendMessageWithLogging("pauseAt", params);
|
||||
browserContext.sendMessage("clockPauseAt", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pauseAt(Date time) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("timeNumber", time.getTime());
|
||||
sendMessageWithLogging("pauseAt", params);
|
||||
browserContext.sendMessage("clockPauseAt", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume() {
|
||||
sendMessageWithLogging("resume", new JsonObject());
|
||||
browserContext.sendMessage("clockResume");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFixedTime(long time) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("timeNumber", time);
|
||||
sendMessageWithLogging("setFixedTime", params);
|
||||
browserContext.sendMessage("clockSetFixedTime", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFixedTime(String time) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("timeString", time);
|
||||
sendMessageWithLogging("setFixedTime", params);
|
||||
browserContext.sendMessage("clockSetFixedTime", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFixedTime(Date time) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("timeNumber", time.getTime());
|
||||
sendMessageWithLogging("setFixedTime", params);
|
||||
browserContext.sendMessage("clockSetFixedTime", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSystemTime(long time) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("timeNumber", time);
|
||||
sendMessageWithLogging("setSystemTime", params);
|
||||
browserContext.sendMessage("clockSetSystemTime", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSystemTime(String time) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("timeString", time);
|
||||
sendMessageWithLogging("setSystemTime", params);
|
||||
browserContext.sendMessage("clockSetSystemTime", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSystemTime(Date time) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("timeNumber", time.getTime());
|
||||
sendMessageWithLogging("setSystemTime", params);
|
||||
browserContext.sendMessage("clockSetSystemTime", params);
|
||||
}
|
||||
|
||||
private static void parseTime(Object time, JsonObject params) {
|
||||
|
||||
@@ -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()) {
|
||||
@@ -376,6 +373,9 @@ public class Connection {
|
||||
case "Stream":
|
||||
result = new Stream(parent, type, guid, initializer);
|
||||
break;
|
||||
case "Selectors":
|
||||
result = new SelectorsImpl(parent, type, guid, initializer);
|
||||
break;
|
||||
case "SocksSupport":
|
||||
break;
|
||||
case "Tracing":
|
||||
|
||||
@@ -20,10 +20,13 @@ import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.ConsoleMessage;
|
||||
import com.microsoft.playwright.JSHandle;
|
||||
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 PageImpl page;
|
||||
|
||||
@@ -34,16 +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() {
|
||||
sendMessage("dismiss");
|
||||
withLogging("Dialog.dismiss", () -> sendMessage("dismiss"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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,7 +240,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public void click(String selector, ClickOptions options) {
|
||||
clickImpl(selector, options);
|
||||
withLogging("Frame.click", () -> clickImpl(selector, options));
|
||||
}
|
||||
|
||||
void clickImpl(String selector, ClickOptions options) {
|
||||
@@ -238,26 +249,38 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
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) {
|
||||
withLogging("Frame.dblclick", () -> dblclickImpl(selector, options));
|
||||
}
|
||||
|
||||
void dblclickImpl(String selector, DblclickOptions options) {
|
||||
if (options == null) {
|
||||
options = new DblclickOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
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();
|
||||
}
|
||||
@@ -265,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
|
||||
@@ -321,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
|
||||
@@ -398,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();
|
||||
}
|
||||
@@ -407,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) {
|
||||
@@ -416,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;
|
||||
@@ -426,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) {
|
||||
@@ -435,12 +479,12 @@ 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);
|
||||
withLogging("Frame.dragAndDrop", () -> dragAndDropImpl(source, target, options));
|
||||
}
|
||||
|
||||
void dragAndDropImpl(String source, String target, DragAndDropOptions options) {
|
||||
@@ -450,12 +494,12 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("source", source);
|
||||
params.addProperty("target", target);
|
||||
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) {
|
||||
@@ -464,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) {
|
||||
@@ -479,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) {
|
||||
@@ -494,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) {
|
||||
@@ -509,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();
|
||||
}
|
||||
|
||||
@@ -520,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) {
|
||||
@@ -529,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) {
|
||||
@@ -544,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) {
|
||||
@@ -559,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) {
|
||||
@@ -574,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
|
||||
@@ -594,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();
|
||||
}
|
||||
|
||||
@@ -615,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) {
|
||||
@@ -625,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) {
|
||||
@@ -642,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) {
|
||||
@@ -654,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) {
|
||||
@@ -671,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
|
||||
@@ -707,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) {
|
||||
@@ -719,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
|
||||
@@ -729,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) {
|
||||
@@ -740,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
|
||||
@@ -797,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());
|
||||
}
|
||||
@@ -960,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));
|
||||
@@ -972,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);
|
||||
}
|
||||
|
||||
@@ -982,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;
|
||||
@@ -992,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
|
||||
@@ -1034,14 +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 highlightImpl(String selector) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
sendMessage("highlight", params, NO_TIMEOUT);
|
||||
sendMessage("highlight", params);
|
||||
}
|
||||
|
||||
protected void handleEvent(String event, JsonObject params) {
|
||||
@@ -1072,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,6 +136,6 @@ class FrameLocatorImpl implements FrameLocator {
|
||||
|
||||
@Override
|
||||
public Locator owner() {
|
||||
return new LocatorImpl(frame, frameSelector, null);
|
||||
return new LocatorImpl(frame, frameSelector);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,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;
|
||||
@@ -42,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());
|
||||
}
|
||||
@@ -62,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)) {
|
||||
|
||||
@@ -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,13 +21,13 @@ 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() {
|
||||
@@ -41,13 +41,13 @@ public class LocalUtils extends ChannelOwner {
|
||||
params.addProperty("mode", appendMode ? "append" : "write");
|
||||
params.addProperty("stacksId", stacksId);
|
||||
params.addProperty("includeSources", includeSources);
|
||||
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) {
|
||||
@@ -56,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);
|
||||
}
|
||||
}
|
||||
|
||||
+44
-123
@@ -20,49 +20,21 @@ 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;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
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 +44,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 +53,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 +67,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 +80,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
|
||||
@@ -116,33 +88,14 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = description;
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.accessible.description", expected, description, "Locator expected to have accessible description", convertType(options, FrameExpectOptions.class), "Assert \"hasAccessibleDescription\"");
|
||||
expectImpl("to.have.accessible.description", expected, description, "Locator expected to have accessible description", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasAccessibleDescription(Pattern pattern, HasAccessibleDescriptionOptions options) {
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.accessible.description", expected, pattern, "Locator expected to have accessible description", convertType(options, FrameExpectOptions.class), "Assert \"hasAccessibleDescription\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasAccessibleErrorMessage(String errorMessage, HasAccessibleErrorMessageOptions options) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = errorMessage;
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.accessible.error.message", expected, errorMessage, "Locator expected to have accessible error message", convertType(options, FrameExpectOptions.class), "Assert \"hasAccessibleErrorMessage\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasAccessibleErrorMessage(Pattern pattern, HasAccessibleErrorMessageOptions options) {
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.accessible.error.message", expected, pattern, "Locator expected to have accessible error message", convertType(options, FrameExpectOptions.class), "Assert \"hasAccessibleErrorMessage\"");
|
||||
expectImpl("to.have.accessible.description", expected, pattern, "Locator expected to have accessible description", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -150,16 +103,14 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = name;
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.accessible.name", expected, name, "Locator expected to have accessible name", convertType(options, FrameExpectOptions.class), "Assert \"hasAccessibleName\"");
|
||||
expectImpl("to.have.accessible.name", expected, name, "Locator expected to have accessible name", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasAccessibleName(Pattern pattern, HasAccessibleNameOptions options) {
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
expected.ignoreCase = shouldIgnoreCase(options);
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.accessible.name", expected, pattern, "Locator expected to have accessible name", convertType(options, FrameExpectOptions.class), "Assert \"hasAccessibleName\"");
|
||||
expectImpl("to.have.accessible.name", expected, pattern, "Locator expected to have accessible name", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -187,20 +138,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 +162,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 +172,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 +183,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 +209,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 +234,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 +251,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 +261,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 +275,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 +288,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 +312,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,50 +323,20 @@ 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\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void matchesAriaSnapshot(String expected, MatchesAriaSnapshotOptions snapshotOptions) {
|
||||
if (snapshotOptions == null) {
|
||||
snapshotOptions = new MatchesAriaSnapshotOptions();
|
||||
}
|
||||
FrameExpectOptions options = convertType(snapshotOptions, FrameExpectOptions.class);
|
||||
options.expectedValue = serializeArgument(expected);
|
||||
expectImpl("to.match.aria", options, expected,"Locator expected to match Aria snapshot", "Assert \"matchesAriaSnapshot\"");
|
||||
expectImpl("to.have.values", list, patterns, "Locator expected to have values matching regex", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isChecked(IsCheckedOptions options) {
|
||||
if (options == null) {
|
||||
options = new IsCheckedOptions();
|
||||
}
|
||||
|
||||
Map<String, Boolean> expectedValue = new HashMap<>();
|
||||
if (options.indeterminate != null) {
|
||||
expectedValue.put("indeterminate", options.indeterminate);
|
||||
}
|
||||
if (options.checked != null) {
|
||||
expectedValue.put("checked", options.checked);
|
||||
}
|
||||
|
||||
String expected;
|
||||
if (options.indeterminate != null && options.indeterminate) {
|
||||
expected = "indeterminate";
|
||||
} else {
|
||||
boolean unchecked = options.checked != null && !options.checked;
|
||||
expected = unchecked ? "unchecked" : "checked";
|
||||
}
|
||||
|
||||
String message = "Locator expected to be";
|
||||
FrameExpectOptions expectOptions = convertType(options, FrameExpectOptions.class);
|
||||
expectOptions.expectedValue = serializeArgument(expectedValue);
|
||||
expectImpl("to.be.checked", expectOptions, expected, message, "Assert \"isChecked\"");
|
||||
boolean unchecked = options != null && options.checked != null && !options.checked;
|
||||
String expression = unchecked ? "to.be.unchecked" : "to.be.checked";
|
||||
String message = "Locator expected to be " + (unchecked ? "un" : "") + "checked";
|
||||
expectTrue(expression, message, convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
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 +344,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 +357,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 +376,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 +384,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 +402,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,25 +21,29 @@ import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.*;
|
||||
import com.microsoft.playwright.options.*;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.microsoft.playwright.impl.LocatorUtils.*;
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
import static com.microsoft.playwright.impl.Utils.convertType;
|
||||
import static com.microsoft.playwright.impl.Utils.toJsRegexFlags;
|
||||
|
||||
class LocatorImpl implements Locator {
|
||||
final FrameImpl frame;
|
||||
final String selector;
|
||||
|
||||
LocatorImpl(FrameImpl frame, String selector, LocatorOptions options) {
|
||||
this(frame, selector, options, null);
|
||||
LocatorImpl(FrameImpl frame, String frameSelector) {
|
||||
this(frame, frameSelector, null);
|
||||
}
|
||||
|
||||
private LocatorImpl(FrameImpl frame, String selector, LocatorOptions options, Boolean visible) {
|
||||
public LocatorImpl(FrameImpl frame, String selector, LocatorOptions options) {
|
||||
this.frame = frame;
|
||||
if (options != null) {
|
||||
if (options.hasText != null) {
|
||||
@@ -61,31 +65,30 @@ class LocatorImpl implements Locator {
|
||||
selector += " >> internal:has-not=" + gson().toJson(locator.selector);
|
||||
}
|
||||
}
|
||||
if (visible != null) {
|
||||
selector += " >> visible=" + visible;
|
||||
}
|
||||
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
|
||||
@@ -117,30 +120,23 @@ class LocatorImpl implements Locator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String ariaSnapshot(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();
|
||||
return result.get("snapshot").getAsString();
|
||||
public void blur(BlurOptions options) {
|
||||
frame.withLogging("Locator.blur", () -> blurImpl(options));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blur(BlurOptions 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
|
||||
@@ -153,7 +149,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,11 +165,6 @@ class LocatorImpl implements Locator {
|
||||
return frame.queryCount(selector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator describe(String description) {
|
||||
return locator(describeSelector(description));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dblclick(DblclickOptions options) {
|
||||
if (options == null) {
|
||||
@@ -223,7 +214,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
|
||||
@@ -233,7 +224,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
|
||||
@@ -246,8 +237,7 @@ class LocatorImpl implements Locator {
|
||||
|
||||
@Override
|
||||
public Locator filter(FilterOptions options) {
|
||||
Boolean visible = (options == null) ? null : options.visible;
|
||||
return new LocatorImpl(frame, selector, convertType(options, LocatorOptions.class), visible);
|
||||
return new LocatorImpl(frame, selector, convertType(options,LocatorOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -478,7 +468,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
|
||||
@@ -486,7 +476,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
|
||||
@@ -542,7 +532,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
|
||||
@@ -622,7 +612,11 @@ 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
|
||||
@@ -644,9 +638,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() {
|
||||
@@ -655,4 +648,16 @@ class LocatorImpl implements Locator {
|
||||
result.addProperty("selector", selector);
|
||||
return result;
|
||||
}
|
||||
|
||||
private FrameExpectResult expectImpl(String expression, FrameExpectOptions options) {
|
||||
if (options == null) {
|
||||
options = new FrameExpectOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("expression", expression);
|
||||
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) {
|
||||
|
||||
@@ -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,45 +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\"");
|
||||
expectImpl("to.have.url", expected, pattern, "Page URL expected to match regex", convertType(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -95,7 +95,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
BrowserContextImpl ownedContext;
|
||||
private boolean isClosed;
|
||||
final Set<Worker> workers = new HashSet<>();
|
||||
protected final TimeoutSettings timeoutSettings;
|
||||
private final TimeoutSettings timeoutSettings;
|
||||
private VideoImpl video;
|
||||
private final PageImpl opener;
|
||||
private String closeReason;
|
||||
@@ -546,7 +546,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
ownedContext.close();
|
||||
} else {
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("close", params, NO_TIMEOUT);
|
||||
sendMessage("close", params);
|
||||
}
|
||||
} catch (PlaywrightException exception) {
|
||||
if (isSafeCloseError(exception) && (options.runBeforeUnload == null || !options.runBeforeUnload)) {
|
||||
@@ -558,13 +558,13 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public ElementHandle querySelector(String selector, QuerySelectorOptions options) {
|
||||
return mainFrame.querySelector(
|
||||
selector, convertType(options, Frame.QuerySelectorOptions.class));
|
||||
return withLogging("Page.querySelector", () -> mainFrame.querySelectorImpl(
|
||||
selector, convertType(options, Frame.QuerySelectorOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ElementHandle> querySelectorAll(String selector) {
|
||||
return mainFrame.querySelectorAll(selector);
|
||||
return withLogging("Page.querySelectorAll", () -> mainFrame.querySelectorAllImpl(selector));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -580,15 +580,17 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
return;
|
||||
}
|
||||
AddLocatorHandlerOptions finalOptions = options;
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", locatorImpl.selector);
|
||||
if (finalOptions.noWaitAfter != null && finalOptions.noWaitAfter) {
|
||||
params.addProperty("noWaitAfter", true);
|
||||
}
|
||||
params.addProperty("selector", locatorImpl.selector);
|
||||
JsonObject json = (JsonObject) sendMessage("registerLocatorHandler", params, NO_TIMEOUT);
|
||||
int uid = json.get("uid").getAsInt();
|
||||
locatorHandlers.put(uid, new LocatorHandler(locator, handler, finalOptions.times));
|
||||
withLogging("Page.addLocatorHandler", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", locatorImpl.selector);
|
||||
if (finalOptions.noWaitAfter != null && finalOptions.noWaitAfter) {
|
||||
params.addProperty("noWaitAfter", true);
|
||||
}
|
||||
params.addProperty("selector", locatorImpl.selector);
|
||||
JsonObject json = (JsonObject) sendMessage("registerLocatorHandler", params);
|
||||
int uid = json.get("uid").getAsInt();
|
||||
locatorHandlers.put(uid, new LocatorHandler(locator, handler, finalOptions.times));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -599,7 +601,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("uid", entry.getKey());
|
||||
try {
|
||||
sendMessage("unregisterLocatorHandler", params, NO_TIMEOUT);
|
||||
sendMessage("unregisterLocatorHandler", params);
|
||||
} catch (PlaywrightException e) {
|
||||
}
|
||||
}
|
||||
@@ -624,65 +626,71 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public Object evalOnSelector(String selector, String pageFunction, Object arg, EvalOnSelectorOptions options) {
|
||||
return mainFrame.evalOnSelectorImpl(
|
||||
selector, pageFunction, arg, convertType(options, Frame.EvalOnSelectorOptions.class));
|
||||
return withLogging("Page.evalOnSelector", () -> mainFrame.evalOnSelectorImpl(
|
||||
selector, pageFunction, arg, convertType(options, Frame.EvalOnSelectorOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evalOnSelectorAll(String selector, String pageFunction, Object arg) {
|
||||
return mainFrame.evalOnSelectorAllImpl(selector, pageFunction, arg);
|
||||
return withLogging("Page.evalOnSelectorAll", () -> mainFrame.evalOnSelectorAllImpl(selector, pageFunction, arg));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInitScript(String script) {
|
||||
addInitScriptImpl(script);
|
||||
withLogging("Page.addInitScript", () -> addInitScriptImpl(script));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInitScript(Path path) {
|
||||
try {
|
||||
byte[] bytes = readAllBytes(path);
|
||||
String script = addSourceUrlToScript(new String(bytes, UTF_8), path);
|
||||
addInitScriptImpl(script);
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to read script from file", e);
|
||||
}
|
||||
withLogging("Page.addInitScript", () -> {
|
||||
try {
|
||||
byte[] bytes = readAllBytes(path);
|
||||
String script = addSourceUrlToScript(new String(bytes, UTF_8), path);
|
||||
addInitScriptImpl(script);
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to read script from file", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addInitScriptImpl(String script) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("source", script);
|
||||
sendMessage("addInitScript", params, NO_TIMEOUT);
|
||||
sendMessage("addInitScript", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementHandle addScriptTag(AddScriptTagOptions options) {
|
||||
return mainFrame.addScriptTagImpl(convertType(options, Frame.AddScriptTagOptions.class));
|
||||
return withLogging("Page.addScriptTag",
|
||||
() -> mainFrame.addScriptTagImpl(convertType(options, Frame.AddScriptTagOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementHandle addStyleTag(AddStyleTagOptions options) {
|
||||
return mainFrame.addStyleTagImpl(convertType(options, Frame.AddStyleTagOptions.class));
|
||||
return withLogging("Page.addStyleTag",
|
||||
() -> mainFrame.addStyleTagImpl(convertType(options, Frame.AddStyleTagOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bringToFront() {
|
||||
sendMessage("bringToFront");
|
||||
withLogging("Page.bringToFront", () -> sendMessage("bringToFront"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void check(String selector, CheckOptions options) {
|
||||
mainFrame.check(selector, convertType(options, Frame.CheckOptions.class));
|
||||
withLogging("Page.check",
|
||||
() -> mainFrame.checkImpl(selector, convertType(options, Frame.CheckOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void click(String selector, ClickOptions options) {
|
||||
mainFrame.clickImpl(selector, convertType(options, Frame.ClickOptions.class));
|
||||
withLogging("Page.click",
|
||||
() -> mainFrame.clickImpl(selector, convertType(options, Frame.ClickOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String content() {
|
||||
return mainFrame.content();
|
||||
return withLogging("Page.content", () -> mainFrame.contentImpl());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -692,17 +700,19 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void dblclick(String selector, DblclickOptions options) {
|
||||
mainFrame.dblclick(selector, convertType(options, Frame.DblclickOptions.class));
|
||||
withLogging("Page.dblclick",
|
||||
() -> mainFrame.dblclickImpl(selector, convertType(options, Frame.DblclickOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchEvent(String selector, String type, Object eventInit, DispatchEventOptions options) {
|
||||
mainFrame.dispatchEvent(selector, type, eventInit, convertType(options, Frame.DispatchEventOptions.class));
|
||||
withLogging("Page.dispatchEvent",
|
||||
() -> mainFrame.dispatchEventImpl(selector, type, eventInit, convertType(options, Frame.DispatchEventOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emulateMedia(EmulateMediaOptions options) {
|
||||
emulateMediaImpl(options);
|
||||
withLogging("Page.emulateMedia", () -> emulateMediaImpl(options));
|
||||
}
|
||||
|
||||
private void emulateMediaImpl(EmulateMediaOptions options) {
|
||||
@@ -710,22 +720,22 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
options = new EmulateMediaOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("emulateMedia", params, NO_TIMEOUT);
|
||||
sendMessage("emulateMedia", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evaluate(String expression, Object arg) {
|
||||
return mainFrame.evaluate(expression, arg);
|
||||
return withLogging("Page.evaluate", () -> mainFrame.evaluateImpl(expression, arg));
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSHandle evaluateHandle(String pageFunction, Object arg) {
|
||||
return mainFrame.evaluateHandle(pageFunction, arg);
|
||||
return withLogging("Page.evaluateHandle", () -> mainFrame.evaluateHandleImpl(pageFunction, arg));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exposeBinding(String name, BindingCallback playwrightBinding, ExposeBindingOptions options) {
|
||||
exposeBindingImpl(name, playwrightBinding, options);
|
||||
withLogging("Page.exposeBinding", () -> exposeBindingImpl(name, playwrightBinding, options));
|
||||
}
|
||||
|
||||
private void exposeBindingImpl(String name, BindingCallback playwrightBinding, ExposeBindingOptions options) {
|
||||
@@ -742,22 +752,25 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
if (options != null && options.handle != null && options.handle) {
|
||||
params.addProperty("needsHandle", true);
|
||||
}
|
||||
sendMessage("exposeBinding", params, NO_TIMEOUT);
|
||||
sendMessage("exposeBinding", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exposeFunction(String name, FunctionCallback playwrightFunction) {
|
||||
exposeBindingImpl(name, (BindingCallback.Source source, Object... args) -> playwrightFunction.call(args), null);
|
||||
withLogging("Page.exposeFunction",
|
||||
() -> exposeBindingImpl(name, (BindingCallback.Source source, Object... args) -> playwrightFunction.call(args), null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fill(String selector, String value, FillOptions options) {
|
||||
mainFrame.fill(selector, value, convertType(options, Frame.FillOptions.class));
|
||||
withLogging("Page.fill",
|
||||
() -> mainFrame.fillImpl(selector, value, convertType(options, Frame.FillOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focus(String selector, FocusOptions options) {
|
||||
mainFrame.focus(selector, convertType(options, Frame.FocusOptions.class));
|
||||
withLogging("Page.focus",
|
||||
() -> mainFrame.focusImpl(selector, convertType(options, Frame.FocusOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -772,7 +785,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public Frame frameByUrl(String glob) {
|
||||
return frameFor(UrlMatcher.forGlob(browserContext.baseUrl(), glob, this.connection.localUtils, false));
|
||||
return frameFor(new UrlMatcher(browserContext.baseUrl, glob));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -806,77 +819,89 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public String getAttribute(String selector, String name, GetAttributeOptions options) {
|
||||
return mainFrame.getAttributeImpl(selector, name, convertType(options, Frame.GetAttributeOptions.class));
|
||||
return withLogging("Page.getAttribute",
|
||||
() -> mainFrame.getAttributeImpl(selector, name, convertType(options, Frame.GetAttributeOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByAltText(String text, GetByAltTextOptions options) {
|
||||
return mainFrame.getByAltText(text, convertType(options, Frame.GetByAltTextOptions.class));
|
||||
return withLogging("Page.getAttribute",
|
||||
() -> mainFrame.getByAltText(text, convertType(options, Frame.GetByAltTextOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByAltText(Pattern text, GetByAltTextOptions options) {
|
||||
return mainFrame.getByAltText(text, convertType(options, Frame.GetByAltTextOptions.class));
|
||||
return withLogging("Page.getByAltText",
|
||||
() -> mainFrame.getByAltText(text, convertType(options, Frame.GetByAltTextOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByLabel(String text, GetByLabelOptions options) {
|
||||
return mainFrame.getByLabel(text, convertType(options, Frame.GetByLabelOptions.class));
|
||||
return withLogging("Page.getByLabel",
|
||||
() -> mainFrame.getByLabel(text, convertType(options, Frame.GetByLabelOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByLabel(Pattern text, GetByLabelOptions options) {
|
||||
return mainFrame.getByLabel(text, convertType(options, Frame.GetByLabelOptions.class));
|
||||
return withLogging("Page.getByLabel",
|
||||
() -> mainFrame.getByLabel(text, convertType(options, Frame.GetByLabelOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByPlaceholder(String text, GetByPlaceholderOptions options) {
|
||||
return mainFrame.getByPlaceholder(text, convertType(options, Frame.GetByPlaceholderOptions.class));
|
||||
return withLogging("Page.getByPlaceholder",
|
||||
() -> mainFrame.getByPlaceholder(text, convertType(options, Frame.GetByPlaceholderOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByPlaceholder(Pattern text, GetByPlaceholderOptions options) {
|
||||
return mainFrame.getByPlaceholder(text, convertType(options, Frame.GetByPlaceholderOptions.class));
|
||||
return withLogging("Page.getByPlaceholder",
|
||||
() -> mainFrame.getByPlaceholder(text, convertType(options, Frame.GetByPlaceholderOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByRole(AriaRole role, GetByRoleOptions options) {
|
||||
return mainFrame.getByRole(role, convertType(options, Frame.GetByRoleOptions.class));
|
||||
return withLogging("Page.getByRole",
|
||||
() -> mainFrame.getByRole(role, convertType(options, Frame.GetByRoleOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByTestId(String testId) {
|
||||
return mainFrame.getByTestId(testId);
|
||||
return withLogging("Page.getByTestId", () -> mainFrame.getByTestId(testId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByTestId(Pattern testId) {
|
||||
return mainFrame.getByTestId(testId);
|
||||
return withLogging("Page.getByTestId", () -> mainFrame.getByTestId(testId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByText(String text, GetByTextOptions options) {
|
||||
return mainFrame.getByText(text, convertType(options, Frame.GetByTextOptions.class));
|
||||
return withLogging("Page.getByText",
|
||||
() -> mainFrame.getByText(text, convertType(options, Frame.GetByTextOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByText(Pattern text, GetByTextOptions options) {
|
||||
return mainFrame.getByText(text, convertType(options, Frame.GetByTextOptions.class));
|
||||
return withLogging("Page.getByText",
|
||||
() -> mainFrame.getByText(text, convertType(options, Frame.GetByTextOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByTitle(String text, GetByTitleOptions options) {
|
||||
return mainFrame.getByTitle(text, convertType(options, Frame.GetByTitleOptions.class));
|
||||
return withLogging("Page.getByTitle",
|
||||
() -> mainFrame.getByTitle(text, convertType(options, Frame.GetByTitleOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator getByTitle(Pattern text, GetByTitleOptions options) {
|
||||
return mainFrame.getByTitle(text, convertType(options, Frame.GetByTitleOptions.class));
|
||||
return withLogging("Page.getByTitle",
|
||||
() -> mainFrame.getByTitle(text, convertType(options, Frame.GetByTitleOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response goBack(GoBackOptions options) {
|
||||
return goBackImpl(options);
|
||||
return withLogging("Page.goBack", () -> goBackImpl(options));
|
||||
}
|
||||
|
||||
Response goBackImpl(GoBackOptions options) {
|
||||
@@ -884,7 +909,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
options = new GoBackOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
JsonObject json = sendMessage("goBack", params, timeoutSettings.navigationTimeout(options.timeout)).getAsJsonObject();
|
||||
JsonObject json = sendMessage("goBack", params).getAsJsonObject();
|
||||
if (json.has("response")) {
|
||||
return connection.getExistingObject(json.getAsJsonObject("response").get("guid").getAsString());
|
||||
}
|
||||
@@ -893,7 +918,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public Response goForward(GoForwardOptions options) {
|
||||
return goForwardImpl(options);
|
||||
return withLogging("Page.goForward", () -> goForwardImpl(options));
|
||||
}
|
||||
|
||||
Response goForwardImpl(GoForwardOptions options) {
|
||||
@@ -901,7 +926,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
options = new GoForwardOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
JsonObject json = sendMessage("goForward", params, timeoutSettings.navigationTimeout(options.timeout)).getAsJsonObject();
|
||||
JsonObject json = sendMessage("goForward", params).getAsJsonObject();
|
||||
if (json.has("response")) {
|
||||
return connection.getExistingObject(json.getAsJsonObject("response").get("guid").getAsString());
|
||||
}
|
||||
@@ -910,42 +935,46 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void requestGC() {
|
||||
sendMessage("requestGC");
|
||||
withLogging("Page.requestGC", () -> sendMessage("requestGC"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseImpl navigate(String url, NavigateOptions options) {
|
||||
return mainFrame.navigateImpl(url, convertType(options, Frame.NavigateOptions.class));
|
||||
return withLogging("Page.navigate", () -> mainFrame.navigateImpl(url, convertType(options, Frame.NavigateOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hover(String selector, HoverOptions options) {
|
||||
mainFrame.hoverImpl(selector, convertType(options, Frame.HoverOptions.class));
|
||||
withLogging("Page.hover", () -> mainFrame.hoverImpl(selector, convertType(options, Frame.HoverOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragAndDrop(String source, String target, DragAndDropOptions options) {
|
||||
mainFrame.dragAndDropImpl(source, target, convertType(options, Frame.DragAndDropOptions.class));
|
||||
withLogging("Page.dragAndDrop", () -> mainFrame.dragAndDropImpl(source, target, convertType(options, Frame.DragAndDropOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String innerHTML(String selector, InnerHTMLOptions options) {
|
||||
return mainFrame.innerHTMLImpl(selector, convertType(options, Frame.InnerHTMLOptions.class));
|
||||
return withLogging("Page.innerHTML",
|
||||
() -> mainFrame.innerHTMLImpl(selector, convertType(options, Frame.InnerHTMLOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String innerText(String selector, InnerTextOptions options) {
|
||||
return mainFrame.innerTextImpl(selector, convertType(options, Frame.InnerTextOptions.class));
|
||||
return withLogging("Page.innerText",
|
||||
() -> mainFrame.innerTextImpl(selector, convertType(options, Frame.InnerTextOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String inputValue(String selector, InputValueOptions options) {
|
||||
return mainFrame.inputValueImpl(selector, convertType(options, Frame.InputValueOptions.class));
|
||||
return withLogging("Page.inputValue",
|
||||
() -> mainFrame.inputValueImpl(selector, convertType(options, Frame.InputValueOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked(String selector, IsCheckedOptions options) {
|
||||
return mainFrame.isCheckedImpl(selector, convertType(options, Frame.IsCheckedOptions.class));
|
||||
return withLogging("Page.isChecked",
|
||||
() -> mainFrame.isCheckedImpl(selector, convertType(options, Frame.IsCheckedOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -955,27 +984,32 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public boolean isDisabled(String selector, IsDisabledOptions options) {
|
||||
return mainFrame.isDisabledImpl(selector, convertType(options, Frame.IsDisabledOptions.class));
|
||||
return withLogging("Page.isDisabled",
|
||||
() -> mainFrame.isDisabledImpl(selector, convertType(options, Frame.IsDisabledOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEditable(String selector, IsEditableOptions options) {
|
||||
return mainFrame.isEditableImpl(selector, convertType(options, Frame.IsEditableOptions.class));
|
||||
return withLogging("Page.isEditable",
|
||||
() -> mainFrame.isEditableImpl(selector, convertType(options, Frame.IsEditableOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(String selector, IsEnabledOptions options) {
|
||||
return mainFrame.isEnabledImpl(selector, convertType(options, Frame.IsEnabledOptions.class));
|
||||
return withLogging("Page.isEnabled",
|
||||
() -> mainFrame.isEnabledImpl(selector, convertType(options, Frame.IsEnabledOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden(String selector, IsHiddenOptions options) {
|
||||
return mainFrame.isHiddenImpl(selector, convertType(options, Frame.IsHiddenOptions.class));
|
||||
return withLogging("Page.isHidden",
|
||||
() -> mainFrame.isHiddenImpl(selector, convertType(options, Frame.IsHiddenOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible(String selector, IsVisibleOptions options) {
|
||||
return mainFrame.isVisibleImpl(selector, convertType(options, Frame.IsVisibleOptions.class));
|
||||
return withLogging("Page.isVisible",
|
||||
() -> mainFrame.isVisibleImpl(selector, convertType(options, Frame.IsVisibleOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1008,22 +1042,23 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void pause() {
|
||||
TimeoutSettings settings = browserContext.timeoutSettings;
|
||||
Double defaultNavigationTimeout = settings.defaultNavigationTimeout();
|
||||
Double defaultTimeout = settings.defaultTimeout();
|
||||
settings.setDefaultNavigationTimeout(0.0);
|
||||
settings.setDefaultTimeout(0.0);
|
||||
try {
|
||||
runUntil(() -> {}, new WaitableRace<>(asList(context().pause(), (Waitable<JsonElement>) waitableClosedOrCrashed)));
|
||||
} finally {
|
||||
settings.setDefaultNavigationTimeout(defaultNavigationTimeout);
|
||||
settings.setDefaultTimeout(defaultTimeout);
|
||||
}
|
||||
withLogging("Page.pause", () -> {
|
||||
Double defaultNavigationTimeout = browserContext.timeoutSettings.defaultNavigationTimeout();
|
||||
Double defaultTimeout = browserContext.timeoutSettings.defaultTimeout();
|
||||
browserContext.setDefaultNavigationTimeoutImpl(0.0);
|
||||
browserContext.setDefaultTimeoutImpl(0.0);
|
||||
try {
|
||||
runUntil(() -> {}, new WaitableRace<>(asList(context().pause(), (Waitable<JsonElement>) waitableClosedOrCrashed)));
|
||||
} finally {
|
||||
browserContext.setDefaultNavigationTimeoutImpl(defaultNavigationTimeout);
|
||||
browserContext.setDefaultTimeoutImpl(defaultTimeout);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] pdf(PdfOptions options) {
|
||||
return pdfImpl(options);
|
||||
return withLogging("Page.pdf", () -> pdfImpl(options));
|
||||
}
|
||||
|
||||
private byte[] pdfImpl(PdfOptions options) {
|
||||
@@ -1032,7 +1067,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.remove("path");
|
||||
JsonObject json = sendMessage("pdf", params, NO_TIMEOUT).getAsJsonObject();
|
||||
JsonObject json = sendMessage("pdf", params).getAsJsonObject();
|
||||
byte[] buffer = Base64.getDecoder().decode(json.get("pdf").getAsString());
|
||||
if (options.path != null) {
|
||||
Utils.writeToFile(buffer, options.path);
|
||||
@@ -1042,12 +1077,13 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void press(String selector, String key, PressOptions options) {
|
||||
mainFrame.pressImpl(selector, key, convertType(options, Frame.PressOptions.class));
|
||||
withLogging("Page.press",
|
||||
() -> mainFrame.pressImpl(selector, key, convertType(options, Frame.PressOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response reload(ReloadOptions options) {
|
||||
return reloadImpl(options);
|
||||
return withLogging("Page.reload", () -> reloadImpl(options));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1060,7 +1096,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
options = new ReloadOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
JsonObject json = sendMessage("reload", params, timeoutSettings.navigationTimeout(options.timeout)).getAsJsonObject();
|
||||
JsonObject json = sendMessage("reload", params).getAsJsonObject();
|
||||
if (json.has("response")) {
|
||||
return connection.getExistingObject(json.getAsJsonObject("response").get("guid").getAsString());
|
||||
}
|
||||
@@ -1069,7 +1105,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void route(String url, Consumer<Route> handler, RouteOptions options) {
|
||||
route(UrlMatcher.forGlob(browserContext.baseUrl(), url, this.connection.localUtils, false), handler, options);
|
||||
route(new UrlMatcher(browserContext.baseUrl, url), handler, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1088,23 +1124,25 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
options = new RouteFromHAROptions();
|
||||
}
|
||||
if (options.update != null && options.update) {
|
||||
browserContext.recordIntoHar(this, har, convertType(options, BrowserContext.RouteFromHAROptions.class), null);
|
||||
browserContext.recordIntoHar(this, har, convertType(options, BrowserContext.RouteFromHAROptions.class));
|
||||
return;
|
||||
}
|
||||
UrlMatcher matcher = UrlMatcher.forOneOf(browserContext.baseUrl(), options.url, this.connection.localUtils, false);
|
||||
UrlMatcher matcher = UrlMatcher.forOneOf(browserContext.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("Page.route", () -> {
|
||||
routes.add(matcher, handler, options == null ? null : options.times);
|
||||
updateInterceptionPatterns();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void routeWebSocket(String url, Consumer<WebSocketRoute> handler) {
|
||||
routeWebSocketImpl(UrlMatcher.forGlob(browserContext.baseUrl(), url, this.connection.localUtils, true), handler);
|
||||
routeWebSocketImpl(new UrlMatcher(browserContext.baseUrl, url), handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1118,13 +1156,15 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
private void routeWebSocketImpl(UrlMatcher matcher, Consumer<WebSocketRoute> handler) {
|
||||
webSocketRoutes.add(matcher, handler);
|
||||
updateWebSocketInterceptionPatterns();
|
||||
withLogging("Page.routeWebSocket", () -> {
|
||||
webSocketRoutes.add(matcher, handler);
|
||||
updateWebSocketInterceptionPatterns();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] screenshot(ScreenshotOptions options) {
|
||||
return screenshotImpl(options);
|
||||
return withLogging("Page.screenshot", () -> screenshotImpl(options));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1141,7 +1181,8 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String selector, String[] values, SelectOptionOptions options) {
|
||||
return mainFrame.selectOptionImpl(selector, values, convertType(options, Frame.SelectOptionOptions.class));
|
||||
return withLogging("Page.selectOption",
|
||||
() -> mainFrame.selectOptionImpl(selector, values, convertType(options, Frame.SelectOptionOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1167,9 +1208,19 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
}
|
||||
}
|
||||
List<Locator> mask = options.mask;
|
||||
options.mask = null;
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
options.mask = mask;
|
||||
params.remove("path");
|
||||
JsonObject json = sendMessage("screenshot", params, timeoutSettings.timeout(options.timeout)).getAsJsonObject();
|
||||
if (mask != null) {
|
||||
JsonArray maskArray = new JsonArray();
|
||||
for (Locator locator: mask) {
|
||||
maskArray.add(((LocatorImpl) locator).toProtocol());
|
||||
}
|
||||
params.add("mask", maskArray);
|
||||
}
|
||||
JsonObject json = sendMessage("screenshot", params).getAsJsonObject();
|
||||
|
||||
byte[] buffer = Base64.getDecoder().decode(json.get("binary").getAsString());
|
||||
if (options.path != null) {
|
||||
@@ -1180,46 +1231,62 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String selector, SelectOption[] values, SelectOptionOptions options) {
|
||||
return mainFrame.selectOptionImpl(selector, values, convertType(options, Frame.SelectOptionOptions.class));
|
||||
return withLogging("Page.selectOption",
|
||||
() -> mainFrame.selectOptionImpl(selector, values, convertType(options, Frame.SelectOptionOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String selector, ElementHandle[] values, SelectOptionOptions options) {
|
||||
return mainFrame.selectOptionImpl(selector, values, convertType(options, Frame.SelectOptionOptions.class));
|
||||
return withLogging("Page.selectOption",
|
||||
() -> mainFrame.selectOptionImpl(selector, values, convertType(options, Frame.SelectOptionOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChecked(String selector, boolean checked, SetCheckedOptions options) {
|
||||
mainFrame.setCheckedImpl(selector, checked, convertType(options, Frame.SetCheckedOptions.class));
|
||||
withLogging("Page.setChecked",
|
||||
() -> mainFrame.setCheckedImpl(selector, checked, convertType(options, Frame.SetCheckedOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContent(String html, SetContentOptions options) {
|
||||
mainFrame.setContent(html, convertType(options, Frame.SetContentOptions.class));
|
||||
withLogging("Page.setContent",
|
||||
() -> mainFrame.setContentImpl(html, convertType(options, Frame.SetContentOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultNavigationTimeout(double timeout) {
|
||||
timeoutSettings.setDefaultNavigationTimeout(timeout);
|
||||
withLogging("Page.setDefaultNavigationTimeout", () -> {
|
||||
timeoutSettings.setDefaultNavigationTimeout(timeout);
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("timeout", timeout);
|
||||
sendMessage("setDefaultNavigationTimeoutNoReply", params);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultTimeout(double timeout) {
|
||||
timeoutSettings.setDefaultTimeout(timeout);
|
||||
withLogging("Page.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("Page.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
|
||||
@@ -1229,7 +1296,8 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void setInputFiles(String selector, Path[] files, SetInputFilesOptions options) {
|
||||
mainFrame.setInputFilesImpl(selector, files, convertType(options, Frame.SetInputFilesOptions.class));
|
||||
withLogging("Page.setInputFiles",
|
||||
() -> mainFrame.setInputFilesImpl(selector, files, convertType(options, Frame.SetInputFilesOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1239,30 +1307,35 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void setInputFiles(String selector, FilePayload[] files, SetInputFilesOptions options) {
|
||||
mainFrame.setInputFilesImpl(selector, files, convertType(options, Frame.SetInputFilesOptions.class));
|
||||
withLogging("Page.setInputFiles",
|
||||
() -> mainFrame.setInputFilesImpl(selector, files, convertType(options, Frame.SetInputFilesOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setViewportSize(int width, int height) {
|
||||
viewport = new ViewportSize(width, height);
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("viewportSize", gson().toJsonTree(viewport));
|
||||
sendMessage("setViewportSize", params, NO_TIMEOUT);
|
||||
withLogging("Page.setViewportSize", () -> {
|
||||
viewport = new ViewportSize(width, height);
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("viewportSize", gson().toJsonTree(viewport));
|
||||
sendMessage("setViewportSize", params);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tap(String selector, TapOptions options) {
|
||||
mainFrame.tap(selector, convertType(options, Frame.TapOptions.class));
|
||||
withLogging("Page.tap",
|
||||
() -> mainFrame.tapImpl(selector, convertType(options, Frame.TapOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String textContent(String selector, TextContentOptions options) {
|
||||
return mainFrame.textContent(selector, convertType(options, Frame.TextContentOptions.class));
|
||||
return withLogging("Page.textContent",
|
||||
() -> mainFrame.textContentImpl(selector, convertType(options, Frame.TextContentOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String title() {
|
||||
return mainFrame.title();
|
||||
return withLogging("Page.title", () -> mainFrame.titleImpl());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1272,23 +1345,27 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void type(String selector, String text, TypeOptions options) {
|
||||
mainFrame.type(selector, text, convertType(options, Frame.TypeOptions.class));
|
||||
withLogging("Page.type",
|
||||
() -> mainFrame.typeImpl(selector, text, convertType(options, Frame.TypeOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncheck(String selector, UncheckOptions options) {
|
||||
mainFrame.uncheck(selector, convertType(options, Frame.UncheckOptions.class));
|
||||
withLogging("Page.uncheck",
|
||||
() -> mainFrame.uncheckImpl(selector, convertType(options, Frame.UncheckOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unrouteAll() {
|
||||
routes.removeAll();
|
||||
updateInterceptionPatterns();
|
||||
withLogging("Page.unrouteAll", () -> {
|
||||
routes.removeAll();
|
||||
updateInterceptionPatterns();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unroute(String url, Consumer<Route> handler) {
|
||||
unroute(UrlMatcher.forGlob(browserContext.baseUrl(), url, this.connection.localUtils, false), handler);
|
||||
unroute(new UrlMatcher(browserContext.baseUrl, url), handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1302,16 +1379,18 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
private void unroute(UrlMatcher matcher, Consumer<Route> handler) {
|
||||
routes.remove(matcher, handler);
|
||||
updateInterceptionPatterns();
|
||||
withLogging("Page.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());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1332,7 +1411,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
// Note: we are creating Video object lazily, because we do not know
|
||||
// BrowserContextOptions when constructing the page - it is assigned
|
||||
// too late during launchPersistentContext.
|
||||
if (browserContext.videosDir() == null) {
|
||||
if (browserContext.videosDir == null) {
|
||||
return null;
|
||||
}
|
||||
return forceVideo();
|
||||
@@ -1353,7 +1432,8 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public JSHandle waitForFunction(String pageFunction, Object arg, WaitForFunctionOptions options) {
|
||||
return mainFrame.waitForFunction(pageFunction, arg, convertType(options, Frame.WaitForFunctionOptions.class));
|
||||
return withLogging("Page.waitForFunction",
|
||||
() -> mainFrame.waitForFunctionImpl(pageFunction, arg, convertType(options, Frame.WaitForFunctionOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1428,7 +1508,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public Request waitForRequest(String urlGlob, WaitForRequestOptions options, Runnable code) {
|
||||
return waitForRequest(UrlMatcher.forGlob(browserContext.baseUrl(), urlGlob, this.connection.localUtils, false), null, options, code);
|
||||
return waitForRequest(new UrlMatcher(browserContext.baseUrl, urlGlob), null, options, code);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1473,7 +1553,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public Response waitForResponse(String urlGlob, WaitForResponseOptions options, Runnable code) {
|
||||
return waitForResponse(UrlMatcher.forGlob(browserContext.baseUrl(), urlGlob, this.connection.localUtils, false), null, options, code);
|
||||
return waitForResponse(new UrlMatcher(browserContext.baseUrl, urlGlob), null, options, code);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1506,7 +1586,8 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public ElementHandle waitForSelector(String selector, WaitForSelectorOptions options) {
|
||||
return mainFrame.waitForSelector(selector, convertType(options, Frame.WaitForSelectorOptions.class));
|
||||
return withLogging("Page.waitForSelector",
|
||||
() -> mainFrame.waitForSelectorImpl(selector, convertType(options, Frame.WaitForSelectorOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1520,12 +1601,12 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void waitForTimeout(double timeout) {
|
||||
mainFrame.waitForTimeout(timeout);
|
||||
withLogging("Page.waitForTimeout", () -> mainFrame.waitForTimeoutImpl(timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waitForURL(String url, WaitForURLOptions options) {
|
||||
waitForURL(UrlMatcher.forGlob(browserContext.baseUrl(), url, this.connection.localUtils, false), options);
|
||||
waitForURL(new UrlMatcher(browserContext.baseUrl, url), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
|
||||
@Override
|
||||
public Map<String, String> allHeaders() {
|
||||
return getRawHeaders().headers();
|
||||
return withLogging("Request.allHeaders", () -> getRawHeaders().headers());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -97,12 +97,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
|
||||
@@ -152,21 +152,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
|
||||
@@ -193,8 +197,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)));
|
||||
|
||||
@@ -47,13 +47,15 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
||||
|
||||
@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
|
||||
@@ -93,7 +95,7 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
||||
|
||||
@Override
|
||||
public List<HttpHeader> headersArray() {
|
||||
return getRawHeaders().headersArray();
|
||||
return withLogging("Response.headersArray", () -> getRawHeaders().headersArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -118,20 +120,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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ class Serialization {
|
||||
.registerTypeAdapter(SameSiteAttribute.class, new SameSiteAdapter().nullSafe())
|
||||
.registerTypeAdapter(BrowserChannel.class, new ToLowerCaseAndDashSerializer<BrowserChannel>())
|
||||
.registerTypeAdapter(ColorScheme.class, new ToLowerCaseAndDashSerializer<ColorScheme>())
|
||||
.registerTypeAdapter(Contrast.class, new ToLowerCaseAndDashSerializer<Contrast>())
|
||||
.registerTypeAdapter(Media.class, new ToLowerCaseSerializer<Media>())
|
||||
.registerTypeAdapter(ForcedColors.class, new ToLowerCaseSerializer<ForcedColors>())
|
||||
.registerTypeAdapter(HttpCredentialsSend.class, new ToLowerCaseSerializer<HttpCredentialsSend>())
|
||||
@@ -66,7 +65,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() {
|
||||
@@ -427,7 +425,6 @@ class Serialization {
|
||||
private static boolean isSupported(Type type) {
|
||||
return new TypeToken<Optional<Media>>() {}.getType().getTypeName().equals(type.getTypeName()) ||
|
||||
new TypeToken<Optional<ColorScheme>>() {}.getType().getTypeName().equals(type.getTypeName()) ||
|
||||
new TypeToken<Optional<Contrast>>() {}.getType().getTypeName().equals(type.getTypeName()) ||
|
||||
new TypeToken<Optional<ForcedColors>>() {}.getType().getTypeName().equals(type.getTypeName()) ||
|
||||
new TypeToken<Optional<ReducedMotion>>() {}.getType().getTypeName().equals(type.getTypeName()) ||
|
||||
new TypeToken<Optional<ViewportSize>>() {}.getType().getTypeName().equals(type.getTypeName());
|
||||
@@ -491,13 +488,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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ class TracingImpl extends ChannelOwner implements Tracing {
|
||||
|
||||
TracingImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
markAsInternalType();
|
||||
}
|
||||
|
||||
private void stopChunkImpl(Path path) {
|
||||
@@ -45,7 +46,7 @@ class TracingImpl extends ChannelOwner implements Tracing {
|
||||
// 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);
|
||||
}
|
||||
@@ -55,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);
|
||||
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) {
|
||||
@@ -85,25 +86,6 @@ class TracingImpl extends ChannelOwner implements Tracing {
|
||||
tracingStartChunk(options.name, options.title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void group(String name, GroupOptions options) {
|
||||
groupImpl(name, options);
|
||||
}
|
||||
|
||||
private void groupImpl(String name, GroupOptions options) {
|
||||
if (options == null) {
|
||||
options = new GroupOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("name", name);
|
||||
sendMessage("tracingGroup", params, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void groupEnd() {
|
||||
sendMessage("tracingGroupEnd");
|
||||
}
|
||||
|
||||
private void tracingStartChunk(String name, String title) {
|
||||
JsonObject params = new JsonObject();
|
||||
if (name != null) {
|
||||
@@ -112,7 +94,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());
|
||||
}
|
||||
|
||||
@@ -134,7 +116,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,27 +16,37 @@
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Objects;
|
||||
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 {
|
||||
public final String glob;
|
||||
public final Pattern pattern;
|
||||
public final Predicate<String> predicate;
|
||||
final Object rawSource;
|
||||
private final Predicate<String> predicate;
|
||||
|
||||
static UrlMatcher forOneOf(URL baseUrl, Object object, LocalUtils localUtils, boolean isWebSocketUrl) {
|
||||
private static Predicate<String> toPredicate(Pattern pattern) {
|
||||
return s -> pattern.matcher(s).find();
|
||||
}
|
||||
|
||||
static UrlMatcher any() {
|
||||
return new UrlMatcher((Object) null, null);
|
||||
}
|
||||
|
||||
static UrlMatcher forOneOf(URL baseUrl, Object object) {
|
||||
if (object == null) {
|
||||
return new UrlMatcher(null, null, null);
|
||||
return UrlMatcher.any();
|
||||
}
|
||||
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);
|
||||
@@ -48,48 +58,34 @@ class UrlMatcher {
|
||||
}
|
||||
|
||||
static String resolveUrl(URL baseUrl, String spec) {
|
||||
return resolveUrl(baseUrl.toString(), spec);
|
||||
}
|
||||
|
||||
private static String resolveUrl(String baseUrl, String spec) {
|
||||
if (baseUrl == null) {
|
||||
return spec;
|
||||
}
|
||||
try {
|
||||
// Join using URI instead of URL since URL doesn't handle ws(s) protocols.
|
||||
return new URI(baseUrl).resolve(spec).toString();
|
||||
} catch (URISyntaxException e) {
|
||||
return new URL(baseUrl, spec).toString();
|
||||
} catch (MalformedURLException e) {
|
||||
return spec;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
UrlMatcher(URL base, String url) {
|
||||
this(url, toPredicate(Pattern.compile(globToRegex(resolveUrl(base, url)))).or(s -> url == null || url.equals(s)));
|
||||
}
|
||||
|
||||
UrlMatcher(Pattern pattern) {
|
||||
this(null, pattern, null);
|
||||
this(pattern, toPredicate(pattern));
|
||||
}
|
||||
|
||||
UrlMatcher(Predicate<String> predicate) {
|
||||
this(null, null, predicate);
|
||||
this(predicate, predicate);
|
||||
}
|
||||
|
||||
private UrlMatcher(String glob, Pattern pattern, Predicate<String> predicate) {
|
||||
this.glob = glob;
|
||||
this.pattern = pattern;
|
||||
private UrlMatcher(Object rawSource, Predicate<String> predicate) {
|
||||
this.rawSource = rawSource;
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
boolean test(String value) {
|
||||
if (pattern != null) {
|
||||
return pattern.matcher(value).find();
|
||||
}
|
||||
if (predicate != null) {
|
||||
return predicate.test(value);
|
||||
}
|
||||
return true;
|
||||
return predicate == null || predicate.test(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -97,40 +93,25 @@ class UrlMatcher {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
UrlMatcher that = (UrlMatcher) o;
|
||||
if (pattern != null) {
|
||||
return that.pattern != null && pattern.pattern().equals(that.pattern.pattern()) && pattern.flags() == that.pattern.flags();
|
||||
if (rawSource instanceof Pattern && that.rawSource instanceof Pattern) {
|
||||
Pattern a = (Pattern) rawSource;
|
||||
Pattern b = (Pattern) that.rawSource;
|
||||
return a.pattern().equals(b.pattern()) && a.flags() == b.flags();
|
||||
}
|
||||
if (predicate != null) {
|
||||
return predicate.equals(that.predicate);
|
||||
}
|
||||
if (glob != null) {
|
||||
return glob.equals(that.glob);
|
||||
}
|
||||
return that.pattern == null && that.predicate == null && that.glob == null;
|
||||
return Objects.equals(rawSource, that.rawSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (pattern != null) {
|
||||
return pattern.hashCode();
|
||||
}
|
||||
if (predicate != null) {
|
||||
return predicate.hashCode();
|
||||
}
|
||||
if (glob != null) {
|
||||
return glob.hashCode();
|
||||
}
|
||||
return 0;
|
||||
return Objects.hash(rawSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (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>";
|
||||
if (rawSource == null)
|
||||
return "<any>";
|
||||
if (rawSource instanceof Predicate)
|
||||
return "matching predicate";
|
||||
return rawSource.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
@@ -360,7 +434,7 @@ public class Utils {
|
||||
}
|
||||
String pfxBase64 = base64Buffer(cert.pfx, cert.pfxPath);
|
||||
if (pfxBase64 != null) {
|
||||
jsonCert.addProperty("pfx", pfxBase64);
|
||||
params.addProperty("pfx", pfxBase64);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to read from file", e);
|
||||
@@ -388,11 +462,13 @@ public class Utils {
|
||||
JsonArray jsonPatterns = new JsonArray();
|
||||
for (UrlMatcher matcher: matchers) {
|
||||
JsonObject jsonPattern = new JsonObject();
|
||||
if (matcher.glob != null) {
|
||||
jsonPattern.addProperty("glob", matcher.glob);
|
||||
} else if (matcher.pattern != null) {
|
||||
jsonPattern.addProperty("regexSource", matcher.pattern.pattern());
|
||||
jsonPattern.addProperty("regexFlags", toJsRegexFlags(matcher.pattern));
|
||||
Object urlFilter = matcher.rawSource;
|
||||
if (urlFilter instanceof String) {
|
||||
jsonPattern.addProperty("glob", (String) urlFilter);
|
||||
} else if (urlFilter instanceof Pattern) {
|
||||
Pattern pattern = (Pattern) urlFilter;
|
||||
jsonPattern.addProperty("regexSource", pattern.pattern());
|
||||
jsonPattern.addProperty("regexFlags", toJsRegexFlags(pattern));
|
||||
} else {
|
||||
// Match all requests.
|
||||
jsonPattern.addProperty("glob", "**/*");
|
||||
|
||||
@@ -30,6 +30,7 @@ class VideoImpl implements Video {
|
||||
|
||||
VideoImpl(PageImpl page) {
|
||||
this.page = page;
|
||||
BrowserImpl browser = page.context().browser();
|
||||
}
|
||||
|
||||
void setArtifact(ArtifactImpl artifact) {
|
||||
@@ -43,33 +44,39 @@ class VideoImpl implements Video {
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
try {
|
||||
waitForArtifact().delete();
|
||||
} catch (PlaywrightException e) {
|
||||
}
|
||||
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.");
|
||||
}
|
||||
try {
|
||||
return Paths.get(waitForArtifact().initializer.get("absolutePath").getAsString());
|
||||
} catch (PlaywrightException e) {
|
||||
throw new PlaywrightException("Page did not produce any video frames", e);
|
||||
}
|
||||
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 (!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);
|
||||
}
|
||||
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
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.util.Collection;
|
||||
|
||||
class WaitableRace<T> implements Waitable<T> {
|
||||
private final Collection<Waitable<T>> waitables;
|
||||
private Waitable<T> firstReady;
|
||||
|
||||
WaitableRace(Collection<Waitable<T>> waitables) {
|
||||
this.waitables = waitables;
|
||||
@@ -28,12 +27,8 @@ class WaitableRace<T> implements Waitable<T> {
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
if (firstReady != null) {
|
||||
return true;
|
||||
}
|
||||
for (Waitable<T> w : waitables) {
|
||||
for (Waitable w : waitables) {
|
||||
if (w.isDone()) {
|
||||
firstReady = w;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -42,11 +37,14 @@ class WaitableRace<T> implements Waitable<T> {
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
try {
|
||||
return firstReady.get();
|
||||
} finally {
|
||||
dispose();
|
||||
assert isDone();
|
||||
dispose();
|
||||
for (Waitable<T> w : waitables) {
|
||||
if (w.isDone()) {
|
||||
return w.get();
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("At least one element must be ready");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -128,11 +128,7 @@ class WebSocketRouteImpl extends ChannelOwner implements WebSocketRoute {
|
||||
return;
|
||||
}
|
||||
// Ensure that websocket is "open" and can send messages without an actual server connection.
|
||||
try {
|
||||
sendMessageAsync("ensureOpened");
|
||||
} catch (PlaywrightException e) {
|
||||
// If this happens after the page has been closed, ignore the error.
|
||||
}
|
||||
sendMessageAsync("ensureOpened");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -71,21 +71,25 @@ class WorkerImpl extends ChannelOwner implements Worker {
|
||||
|
||||
@Override
|
||||
public Object evaluate(String pageFunction, Object arg) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
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("Worker.evaluate", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
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.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("Worker.evaluateHandle", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpressionHandle", params);
|
||||
return connection.getExistingObject(json.getAsJsonObject().getAsJsonObject("handle").get("guid").getAsString());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -42,7 +42,7 @@ class WritableStream extends ChannelOwner {
|
||||
ByteBuffer buffer = ByteBuffer.wrap(b, off, len);
|
||||
ByteBuffer encoded = Base64.getEncoder().encode(buffer);
|
||||
params.addProperty("binary", new String(encoded.array(), StandardCharsets.UTF_8));
|
||||
sendMessage("write", params, NO_TIMEOUT);
|
||||
sendMessage("write", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright.options;
|
||||
|
||||
public enum Contrast {
|
||||
NO_PREFERENCE,
|
||||
MORE
|
||||
}
|
||||
@@ -48,12 +48,6 @@ public class Cookie {
|
||||
* Optional.
|
||||
*/
|
||||
public SameSiteAttribute sameSite;
|
||||
/**
|
||||
* For partitioned third-party cookies (aka <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/Privacy/Guides/Privacy_sandbox/Partitioned_cookies">CHIPS</a>), the
|
||||
* partition key. Optional.
|
||||
*/
|
||||
public String partitionKey;
|
||||
|
||||
public Cookie(String name, String value) {
|
||||
this.name = name;
|
||||
@@ -109,13 +103,4 @@ public class Cookie {
|
||||
this.sameSite = sameSite;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* For partitioned third-party cookies (aka <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/Privacy/Guides/Privacy_sandbox/Partitioned_cookies">CHIPS</a>), the
|
||||
* partition key. Optional.
|
||||
*/
|
||||
public Cookie setPartitionKey(String partitionKey) {
|
||||
this.partitionKey = partitionKey;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import java.nio.file.Path;
|
||||
* The {@code FormData} is used create form data that is sent via {@code APIRequestContext}.
|
||||
* <pre>{@code
|
||||
* import com.microsoft.playwright.options.FormData;
|
||||
* // ...
|
||||
* ...
|
||||
* FormData form = FormData.create()
|
||||
* .set("firstName", "John")
|
||||
* .set("lastName", "Doe")
|
||||
@@ -43,7 +43,7 @@ public interface FormData {
|
||||
* existing set of values.
|
||||
* <pre>{@code
|
||||
* import com.microsoft.playwright.options.FormData;
|
||||
* // ...
|
||||
* ...
|
||||
* FormData form = FormData.create()
|
||||
* // Only name and value are set.
|
||||
* .append("firstName", "John")
|
||||
@@ -70,7 +70,7 @@ public interface FormData {
|
||||
* existing set of values.
|
||||
* <pre>{@code
|
||||
* import com.microsoft.playwright.options.FormData;
|
||||
* // ...
|
||||
* ...
|
||||
* FormData form = FormData.create()
|
||||
* // Only name and value are set.
|
||||
* .append("firstName", "John")
|
||||
@@ -97,7 +97,7 @@ public interface FormData {
|
||||
* existing set of values.
|
||||
* <pre>{@code
|
||||
* import com.microsoft.playwright.options.FormData;
|
||||
* // ...
|
||||
* ...
|
||||
* FormData form = FormData.create()
|
||||
* // Only name and value are set.
|
||||
* .append("firstName", "John")
|
||||
@@ -124,7 +124,7 @@ public interface FormData {
|
||||
* existing set of values.
|
||||
* <pre>{@code
|
||||
* import com.microsoft.playwright.options.FormData;
|
||||
* // ...
|
||||
* ...
|
||||
* FormData form = FormData.create()
|
||||
* // Only name and value are set.
|
||||
* .append("firstName", "John")
|
||||
@@ -151,7 +151,7 @@ public interface FormData {
|
||||
* existing set of values.
|
||||
* <pre>{@code
|
||||
* import com.microsoft.playwright.options.FormData;
|
||||
* // ...
|
||||
* ...
|
||||
* FormData form = FormData.create()
|
||||
* // Only name and value are set.
|
||||
* .append("firstName", "John")
|
||||
@@ -179,7 +179,7 @@ public interface FormData {
|
||||
* Sets a field on the form. File values can be passed either as {@code Path} or as {@code FilePayload}.
|
||||
* <pre>{@code
|
||||
* import com.microsoft.playwright.options.FormData;
|
||||
* // ...
|
||||
* ...
|
||||
* FormData form = FormData.create()
|
||||
* // Only name and value are set.
|
||||
* .set("firstName", "John")
|
||||
@@ -200,7 +200,7 @@ public interface FormData {
|
||||
* Sets a field on the form. File values can be passed either as {@code Path} or as {@code FilePayload}.
|
||||
* <pre>{@code
|
||||
* import com.microsoft.playwright.options.FormData;
|
||||
* // ...
|
||||
* ...
|
||||
* FormData form = FormData.create()
|
||||
* // Only name and value are set.
|
||||
* .set("firstName", "John")
|
||||
@@ -221,7 +221,7 @@ public interface FormData {
|
||||
* Sets a field on the form. File values can be passed either as {@code Path} or as {@code FilePayload}.
|
||||
* <pre>{@code
|
||||
* import com.microsoft.playwright.options.FormData;
|
||||
* // ...
|
||||
* ...
|
||||
* FormData form = FormData.create()
|
||||
* // Only name and value are set.
|
||||
* .set("firstName", "John")
|
||||
@@ -242,7 +242,7 @@ public interface FormData {
|
||||
* Sets a field on the form. File values can be passed either as {@code Path} or as {@code FilePayload}.
|
||||
* <pre>{@code
|
||||
* import com.microsoft.playwright.options.FormData;
|
||||
* // ...
|
||||
* ...
|
||||
* FormData form = FormData.create()
|
||||
* // Only name and value are set.
|
||||
* .set("firstName", "John")
|
||||
@@ -263,7 +263,7 @@ public interface FormData {
|
||||
* Sets a field on the form. File values can be passed either as {@code Path} or as {@code FilePayload}.
|
||||
* <pre>{@code
|
||||
* import com.microsoft.playwright.options.FormData;
|
||||
* // ...
|
||||
* ...
|
||||
* FormData form = FormData.create()
|
||||
* // Only name and value are set.
|
||||
* .set("firstName", "John")
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright.options;
|
||||
|
||||
public class Location {
|
||||
public String file;
|
||||
public Integer line;
|
||||
public Integer column;
|
||||
|
||||
public Location(String file) {
|
||||
this.file = file;
|
||||
}
|
||||
public Location setLine(int line) {
|
||||
this.line = line;
|
||||
return this;
|
||||
}
|
||||
public Location setColumn(int column) {
|
||||
this.column = column;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import java.net.InetSocketAddress;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Function;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import static com.microsoft.playwright.Utils.copy;
|
||||
@@ -41,7 +40,6 @@ public class Server implements HttpHandler {
|
||||
private final Map<String, String> csp = Collections.synchronizedMap(new HashMap<>());
|
||||
private final Map<String, HttpHandler> routes = Collections.synchronizedMap(new HashMap<>());
|
||||
private final Set<String> gzipRoutes = Collections.synchronizedSet(new HashSet<>());
|
||||
private Function<String, InputStream> resourceProvider;
|
||||
|
||||
private static class Auth {
|
||||
public final String user;
|
||||
@@ -77,8 +75,6 @@ public class Server implements HttpHandler {
|
||||
server.createContext("/", this);
|
||||
server.setExecutor(null); // creates a default executor
|
||||
server.start();
|
||||
// Resources from "src/test/resources/" are copied to "resources/" directory in the jar.
|
||||
resourceProvider = path -> Server.class.getClassLoader().getResourceAsStream("resources" + path);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
@@ -97,10 +93,6 @@ public class Server implements HttpHandler {
|
||||
gzipRoutes.add(path);
|
||||
}
|
||||
|
||||
void setResourceProvider(Function<String, InputStream> resourceProvider) {
|
||||
this.resourceProvider = resourceProvider;
|
||||
}
|
||||
|
||||
static class Request {
|
||||
public final String url;
|
||||
public final String method;
|
||||
@@ -195,16 +187,18 @@ public class Server implements HttpHandler {
|
||||
path = "/index.html";
|
||||
}
|
||||
|
||||
InputStream resource = resourceProvider.apply(path);
|
||||
// Resources from "src/test/resources/" are copied to "resources/" directory in the jar.
|
||||
String resourcePath = "resources" + path;
|
||||
InputStream resource = getClass().getClassLoader().getResourceAsStream(resourcePath);
|
||||
if (resource == null) {
|
||||
exchange.getResponseHeaders().add("Content-Type", "text/plain");
|
||||
exchange.sendResponseHeaders(404, 0);
|
||||
try (Writer writer = new OutputStreamWriter(exchange.getResponseBody())) {
|
||||
writer.write("File not found: " + path);
|
||||
writer.write("File not found: " + resourcePath);
|
||||
}
|
||||
return;
|
||||
}
|
||||
exchange.getResponseHeaders().add("Content-Type", mimeType(new File(path)));
|
||||
exchange.getResponseHeaders().add("Content-Type", mimeType(new File(resourcePath)));
|
||||
ByteArrayOutputStream body = new ByteArrayOutputStream();
|
||||
OutputStream output = body;
|
||||
if (gzipRoutes.contains(path)) {
|
||||
@@ -224,7 +218,7 @@ public class Server implements HttpHandler {
|
||||
}
|
||||
long contentLength = body.size();
|
||||
// -1 means no body, 0 means chunked encoding.
|
||||
exchange.sendResponseHeaders(200, (contentLength == 0 || exchange.getRequestMethod().equals("HEAD")) ? -1 : contentLength);
|
||||
exchange.sendResponseHeaders(200, contentLength == 0 ? -1 : contentLength);
|
||||
if (contentLength > 0) {
|
||||
exchange.getResponseBody().write(body.toByteArray());
|
||||
}
|
||||
|
||||
@@ -45,12 +45,12 @@ public class TestBase {
|
||||
static final boolean isMac = Utils.getOS() == Utils.OS.MAC;
|
||||
static final boolean isLinux = Utils.getOS() == Utils.OS.LINUX;
|
||||
static final boolean isWindows = Utils.getOS() == Utils.OS.WINDOWS;
|
||||
static final boolean headed;
|
||||
static final boolean headful;
|
||||
static final SameSiteAttribute defaultSameSiteCookieValue;
|
||||
|
||||
static {
|
||||
String headedEnv = System.getenv("HEADED");
|
||||
headed = headedEnv != null && !"0".equals(headedEnv) && !"false".equals(headedEnv);
|
||||
String headfulEnv = System.getenv("HEADFUL");
|
||||
headful = headfulEnv != null && !"0".equals(headfulEnv) && !"false".equals(headfulEnv);
|
||||
defaultSameSiteCookieValue = initSameSiteAttribute();
|
||||
}
|
||||
|
||||
@@ -58,8 +58,8 @@ public class TestBase {
|
||||
Page page;
|
||||
BrowserContext context;
|
||||
|
||||
static boolean isHeaded() {
|
||||
return headed;
|
||||
static boolean isHeadful() {
|
||||
return headful;
|
||||
}
|
||||
|
||||
static boolean isChromium() {
|
||||
@@ -81,7 +81,7 @@ public class TestBase {
|
||||
static BrowserType.LaunchOptions createLaunchOptions() {
|
||||
BrowserType.LaunchOptions options;
|
||||
options = new BrowserType.LaunchOptions();
|
||||
options.headless = !headed;
|
||||
options.headless = !headful;
|
||||
options.channel = getBrowserChannelFromEnv();
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -299,13 +299,6 @@ public class TestBrowserContextBasic {
|
||||
assertTrue(e.getMessage().contains("Target page, context or browser has been closed"), e.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void waitForConditionThatMayChangeToFalse(BrowserContext context) {
|
||||
int[] var = {0};
|
||||
context.waitForCondition(() -> ++var[0] == 1);
|
||||
assertEquals(1, var[0], "The predicate should be called only once.");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPropagateCloseReasonToPendingActions(Browser browser) {
|
||||
BrowserContext context = browser.newContext();
|
||||
|
||||
+7
-25
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -50,12 +49,7 @@ public class TestBrowserContextCDPSession extends TestBase {
|
||||
cdpSession.send("Network.enable");
|
||||
|
||||
List<JsonElement> events = new ArrayList<>();
|
||||
cdpSession.on("Network.requestWillBeSent", (JsonObject jsonObject) -> {
|
||||
// Only register main request, ignore favicon requests.
|
||||
if ("Document".equals(jsonObject.get("type").getAsString())) {
|
||||
events.add(jsonObject);
|
||||
}
|
||||
});
|
||||
cdpSession.on("Network.requestWillBeSent", events::add);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
|
||||
assertEquals(1, events.size());
|
||||
@@ -132,7 +126,7 @@ public class TestBrowserContextCDPSession extends TestBase {
|
||||
page.close();
|
||||
|
||||
PlaywrightException exception = assertThrows(PlaywrightException.class, session::detach);
|
||||
assertTrue(exception.getMessage().contains("Target page, context or browser has been closed"), exception.getMessage());
|
||||
assertTrue(exception.getMessage().contains("Target page, context or browser has been closed"));
|
||||
context.close();
|
||||
}
|
||||
|
||||
@@ -142,14 +136,8 @@ public class TestBrowserContextCDPSession extends TestBase {
|
||||
cdpSession.send("Network.enable");
|
||||
|
||||
List<JsonObject> events = new ArrayList<>();
|
||||
Consumer<JsonObject> listener1 = (JsonObject jsonObject) -> {
|
||||
// Only register main request, ignore favicon requests.
|
||||
if ("Document".equals(jsonObject.get("type").getAsString())) {
|
||||
events.add(jsonObject);
|
||||
}
|
||||
};
|
||||
cdpSession.on("Network.requestWillBeSent", listener1);
|
||||
cdpSession.on("Network.requestWillBeSent", listener1);
|
||||
cdpSession.on("Network.requestWillBeSent", events::add);
|
||||
cdpSession.on("Network.requestWillBeSent", events::add);
|
||||
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
assertEquals(2, events.size());
|
||||
@@ -161,15 +149,9 @@ public class TestBrowserContextCDPSession extends TestBase {
|
||||
cdpSession.send("Network.enable");
|
||||
|
||||
List<JsonObject> events = new ArrayList<>();
|
||||
Consumer<JsonObject> listener1 = (JsonObject jsonObject) -> {
|
||||
// Only register main request, ignore favicon requests.
|
||||
if ("Document".equals(jsonObject.get("type").getAsString())) {
|
||||
events.add(jsonObject);
|
||||
}
|
||||
};
|
||||
Consumer<JsonObject> listener2 = listener1::accept;
|
||||
Consumer<JsonObject> listener1 = events::add;
|
||||
cdpSession.on("Network.requestWillBeSent", listener1);
|
||||
cdpSession.on("Network.requestWillBeSent", listener2);
|
||||
cdpSession.on("Network.requestWillBeSent", events::add);
|
||||
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
assertEquals(2, events.size());
|
||||
@@ -178,6 +160,6 @@ public class TestBrowserContextCDPSession extends TestBase {
|
||||
events.clear();
|
||||
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
assertEquals(1, events.size(), new Gson().toJson(events));
|
||||
assertEquals(1, events.size());
|
||||
}
|
||||
}
|
||||
|
||||
+3
-7
@@ -25,13 +25,12 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class TestBrowserContextCredentials extends TestBase {
|
||||
|
||||
static boolean isChromiumHeadedLike() {
|
||||
// --headless=new, the default in all Chromium channels, is like headless.
|
||||
return isChromium() && (isHeaded() || getBrowserChannelFromEnv() != null);
|
||||
static boolean isChromiumHeadful() {
|
||||
return isChromium() && isHeadful();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisabledIf(value="isChromiumHeadedLike", disabledReason="fail")
|
||||
@DisabledIf(value="isChromiumHeadful", disabledReason="fail")
|
||||
void shouldFailWithoutCredentials() {
|
||||
server.setAuth("/empty.html", "user", "pass");
|
||||
Response response = page.navigate(server.EMPTY_PAGE);
|
||||
@@ -104,7 +103,6 @@ public class TestBrowserContextCredentials extends TestBase {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisabledIf(value="isChromiumHeadedLike", disabledReason="fail")
|
||||
void shouldFailWithCorrectCredentialsAndWrongOriginScheme() {
|
||||
server.setAuth("/empty.html", "user", "pass");
|
||||
final HttpCredentials httpCredentials = new HttpCredentials("user", "pass");
|
||||
@@ -117,7 +115,6 @@ public class TestBrowserContextCredentials extends TestBase {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisabledIf(value="isChromiumHeadedLike", disabledReason="fail")
|
||||
void shouldFailWithCorrectCredentialsAndWrongOriginHostname() {
|
||||
server.setAuth("/empty.html", "user", "pass");
|
||||
final HttpCredentials httpCredentials = new HttpCredentials("user", "pass");
|
||||
@@ -130,7 +127,6 @@ public class TestBrowserContextCredentials extends TestBase {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisabledIf(value="isChromiumHeadedLike", disabledReason="fail")
|
||||
void shouldFailWithCorrectCredentialsAndWrongOriginPort() {
|
||||
server.setAuth("/empty.html", "user", "pass");
|
||||
final HttpCredentials httpCredentials = new HttpCredentials("user", "pass");
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user