Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 50508ed2fa | |||
| e863a78755 | |||
| b1c5ec2083 | |||
| eca3491203 | |||
| 558df1fc60 | |||
| ba5bd2c9ac | |||
| 4be749f045 | |||
| f515d9f318 | |||
| 2f706012a7 | |||
| 853b5062e7 | |||
| 2d0d941e18 | |||
| 49a54d7ee4 | |||
| 44a85c1dc3 | |||
| 5d7ee12f4a | |||
| a0416459e1 | |||
| ddffc45e84 | |||
| e85258908e | |||
| c61d1da352 | |||
| a60b0a9b78 | |||
| 38bde7ad25 | |||
| a8e41b1ede | |||
| 45b141811b | |||
| bd6ed7bc88 | |||
| d0e7ab1e58 | |||
| 7ff7ee188b | |||
| d291a64e11 | |||
| 9f2b482084 | |||
| b7319c629d | |||
| 38c5dc28a4 | |||
| 1b9f7732fe | |||
| 1a4dec86cd | |||
| c802c87e52 | |||
| ab81b542b8 | |||
| 07e7cb85c7 | |||
| 7af5405d38 | |||
| 50ba2dacbb | |||
| 61f5e4dfdd | |||
| 84344c9ff9 | |||
| c0fd575fac | |||
| 81724c7c94 | |||
| 3eb88931bf | |||
| 948dd1515a | |||
| dd57d5248d |
@@ -3,7 +3,7 @@ on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
jobs:
|
||||
build:
|
||||
timeout-minutes: 30
|
||||
|
||||
@@ -3,7 +3,7 @@ name: "devrelease:docker"
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
jobs:
|
||||
publish-canary-docker:
|
||||
name: "publish to DockerHub"
|
||||
|
||||
@@ -2,11 +2,11 @@ name: Build & Test
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
- release-*
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
- release-*
|
||||
jobs:
|
||||
dev:
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
- name: Build with Maven
|
||||
run: mvn -B package -D skipTests --no-transfer-progress
|
||||
- name: Run tests
|
||||
run: mvn test --no-transfer-progress
|
||||
run: mvn test --no-transfer-progress --fail-at-end
|
||||
env:
|
||||
BROWSER: ${{ matrix.browser }}
|
||||
- name: Test Spring Boot Starter
|
||||
@@ -85,7 +85,7 @@ jobs:
|
||||
- name: Build with Maven
|
||||
run: mvn -B package -D skipTests --no-transfer-progress
|
||||
- name: Run tests
|
||||
run: mvn test --no-transfer-progress
|
||||
run: mvn test --no-transfer-progress --fail-at-end
|
||||
env:
|
||||
BROWSER: chromium
|
||||
BROWSER_CHANNEL: ${{ matrix.browser-channel }}
|
||||
|
||||
@@ -2,17 +2,15 @@ name: Test CLI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
- release-*
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
- release-*
|
||||
jobs:
|
||||
verify:
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: true
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
@@ -5,7 +5,7 @@ on:
|
||||
- '.github/workflows/test_docker.yml'
|
||||
- 'Dockerfile*'
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
- release-*
|
||||
pull_request:
|
||||
paths:
|
||||
@@ -14,7 +14,7 @@ on:
|
||||
- scripts/CLI_VERSION
|
||||
- '**/pom.xml'
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
- release-*
|
||||
jobs:
|
||||
test:
|
||||
|
||||
@@ -2,14 +2,14 @@ name: Verify API
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
- release-*
|
||||
paths:
|
||||
- 'scripts/*'
|
||||
- 'api-generator/*'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
- release-*
|
||||
paths:
|
||||
- 'scripts/**'
|
||||
@@ -17,8 +17,6 @@ on:
|
||||
jobs:
|
||||
verify:
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: true
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
@@ -11,9 +11,9 @@ Playwright is a Java library to automate [Chromium](https://www.chromium.org/Hom
|
||||
|
||||
| | Linux | macOS | Windows |
|
||||
| :--- | :---: | :---: | :---: |
|
||||
| Chromium <!-- GEN:chromium-version -->96.0.4641.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| WebKit <!-- GEN:webkit-version -->15.0<!-- GEN:stop --> | ✅ | ✅ | ✅ |
|
||||
| Firefox <!-- GEN:firefox-version -->92.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Chromium <!-- GEN:chromium-version -->98.0.4695.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| WebKit <!-- GEN:webkit-version -->15.4<!-- GEN:stop --> | ✅ | ✅ | ✅ |
|
||||
| Firefox <!-- GEN:firefox-version -->94.0.1<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
|
||||
Headless execution is supported for all the browsers on all platforms. Check out [system requirements](https://playwright.dev/java/docs/next/intro/#system-requirements) for details.
|
||||
|
||||
@@ -43,7 +43,7 @@ To run Playwright simply add following dependency to your Maven project:
|
||||
<dependency>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>playwright</artifactId>
|
||||
<version>1.14.1</version>
|
||||
<version>1.16.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
@@ -179,7 +179,7 @@ You can also browse [javadoc online](https://www.javadoc.io/doc/com.microsoft.pl
|
||||
|
||||
## Contributing
|
||||
|
||||
Follow [the instructions](https://github.com/microsoft/playwright-java/blob/master/CONTRIBUTING.md#getting-code) to build the project from source and install the driver.
|
||||
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?
|
||||
|
||||
|
||||
@@ -5,3 +5,9 @@
|
||||
* set new driver version in `scripts/CLI_VERSION`
|
||||
* regenerate API: `./scripts/download_driver_for_all_platforms.sh -f && ./scripts/generate_api.sh && ./scripts/update_readme.sh`
|
||||
* commit & send PR with the roll
|
||||
|
||||
# Updating Version
|
||||
|
||||
```bash
|
||||
./scripts/set_maven_version.sh 1.15.0
|
||||
```
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>parent-pom</artifactId>
|
||||
<version>1.17.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>assertions</artifactId>
|
||||
<name>Playwright - Assertions</name>
|
||||
<description>
|
||||
This module provides Playwright assertions that will wait until the expected condition is met.
|
||||
</description>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<configuration>
|
||||
<subpackages>com.microsoft.playwright.assertions</subpackages>
|
||||
<excludePackageNames>com.microsoft.playwright.impl</excludePackageNames>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.opentest4j</groupId>
|
||||
<artifactId>opentest4j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>playwright</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>playwright</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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.assertions;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
import com.microsoft.playwright.Page;
|
||||
|
||||
/**
|
||||
* The {@code PageAssertions} class provides assertion methods that can be used to make assertions about the {@code Page} state in the
|
||||
* tests. A new instance of {@code LocatorAssertions} is created by calling {@link PlaywrightAssertions#assertThat
|
||||
* PlaywrightAssertions.assertThat()}:
|
||||
* <pre>{@code
|
||||
* ...
|
||||
* import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
|
||||
*
|
||||
* public class TestPage {
|
||||
* ...
|
||||
* @Test
|
||||
* void navigatesToLoginPage() {
|
||||
* ...
|
||||
* page.click("#login");
|
||||
* assertThat(page).hasURL(Pattern.compile(".*\/login"));
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*/
|
||||
public interface PageAssertions {
|
||||
class HasTitleOptions {
|
||||
/**
|
||||
* Time to retry the assertion for.
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Time to retry the assertion for.
|
||||
*/
|
||||
public HasTitleOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class HasURLOptions {
|
||||
/**
|
||||
* Time to retry the assertion for.
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Time to retry the assertion for.
|
||||
*/
|
||||
public HasURLOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Ensures the page has the given title.
|
||||
* <pre>{@code
|
||||
* assertThat(page).hasTitle("Playwright");
|
||||
* }</pre>
|
||||
*
|
||||
* @param titleOrRegExp Expected title or RegExp.
|
||||
*/
|
||||
default void hasTitle(String titleOrRegExp) {
|
||||
hasTitle(titleOrRegExp, null);
|
||||
}
|
||||
/**
|
||||
* Ensures the page has the given title.
|
||||
* <pre>{@code
|
||||
* assertThat(page).hasTitle("Playwright");
|
||||
* }</pre>
|
||||
*
|
||||
* @param titleOrRegExp Expected title or RegExp.
|
||||
*/
|
||||
void hasTitle(String titleOrRegExp, HasTitleOptions options);
|
||||
/**
|
||||
* Ensures the page has the given title.
|
||||
* <pre>{@code
|
||||
* assertThat(page).hasTitle("Playwright");
|
||||
* }</pre>
|
||||
*
|
||||
* @param titleOrRegExp Expected title or RegExp.
|
||||
*/
|
||||
default void hasTitle(Pattern titleOrRegExp) {
|
||||
hasTitle(titleOrRegExp, null);
|
||||
}
|
||||
/**
|
||||
* Ensures the page has the given title.
|
||||
* <pre>{@code
|
||||
* assertThat(page).hasTitle("Playwright");
|
||||
* }</pre>
|
||||
*
|
||||
* @param titleOrRegExp Expected title or RegExp.
|
||||
*/
|
||||
void hasTitle(Pattern titleOrRegExp, HasTitleOptions options);
|
||||
/**
|
||||
* Ensures the page is navigated to the given URL.
|
||||
* <pre>{@code
|
||||
* assertThat(page).hasURL(".com");
|
||||
* }</pre>
|
||||
*
|
||||
* @param urlOrRegExp Expected substring or RegExp.
|
||||
*/
|
||||
default void hasURL(String urlOrRegExp) {
|
||||
hasURL(urlOrRegExp, null);
|
||||
}
|
||||
/**
|
||||
* Ensures the page is navigated to the given URL.
|
||||
* <pre>{@code
|
||||
* assertThat(page).hasURL(".com");
|
||||
* }</pre>
|
||||
*
|
||||
* @param urlOrRegExp Expected substring or RegExp.
|
||||
*/
|
||||
void hasURL(String urlOrRegExp, HasURLOptions options);
|
||||
/**
|
||||
* Ensures the page is navigated to the given URL.
|
||||
* <pre>{@code
|
||||
* assertThat(page).hasURL(".com");
|
||||
* }</pre>
|
||||
*
|
||||
* @param urlOrRegExp Expected substring or RegExp.
|
||||
*/
|
||||
default void hasURL(Pattern urlOrRegExp) {
|
||||
hasURL(urlOrRegExp, null);
|
||||
}
|
||||
/**
|
||||
* Ensures the page is navigated to the given URL.
|
||||
* <pre>{@code
|
||||
* assertThat(page).hasURL(".com");
|
||||
* }</pre>
|
||||
*
|
||||
* @param urlOrRegExp Expected substring or RegExp.
|
||||
*/
|
||||
void hasURL(Pattern urlOrRegExp, HasURLOptions options);
|
||||
/**
|
||||
* Makes the assertion check for the opposite condition. For example, this code tests that the page URL doesn't contain
|
||||
* {@code "error"}:
|
||||
* <pre>{@code
|
||||
* assertThat(page).not().hasURL("error");
|
||||
* }</pre>
|
||||
*/
|
||||
PageAssertions not();
|
||||
}
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.assertions;
|
||||
|
||||
import java.util.*;
|
||||
import com.microsoft.playwright.Locator;
|
||||
import com.microsoft.playwright.Page;
|
||||
import com.microsoft.playwright.impl.LocatorAssertionsImpl;
|
||||
import com.microsoft.playwright.impl.PageAssertionsImpl;
|
||||
|
||||
/**
|
||||
* The {@code PlaywrightAssertions} class provides convenience methods for creating assertions that will wait until the expected
|
||||
* condition is met.
|
||||
*
|
||||
* <p> Consider the following example:
|
||||
* <pre>{@code
|
||||
* ...
|
||||
* import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
|
||||
*
|
||||
* public class TestExample {
|
||||
* ...
|
||||
* @Test
|
||||
* void statusBecomesSubmitted() {
|
||||
* ...
|
||||
* page.click("#submit-button");
|
||||
* assertThat(page.locator(".status")).hasText("Submitted");
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <p> Playwright will be re-testing the node with the selector {@code .status} until fetched Node has the {@code "Submitted"} text. It
|
||||
* will be re-fetching the node and checking it over and over, until the condition is met or until the timeout is reached.
|
||||
* You can pass this timeout as an option.
|
||||
*
|
||||
* <p> By default, the timeout for assertions is set to 5 seconds.
|
||||
*
|
||||
* <p> To use Playwright assertions add the following dependency into the {@code pom.xml} of your Maven project:
|
||||
*/
|
||||
public interface PlaywrightAssertions {
|
||||
/**
|
||||
* Creates a {@code LocatorAssertions} object for the given {@code Locator}.
|
||||
* <pre>{@code
|
||||
* PlaywrightAssertions.assertThat(locator).isVisible();
|
||||
* }</pre>
|
||||
*
|
||||
* @param locator {@code Locator} object to use for assertions.
|
||||
*/
|
||||
static LocatorAssertions assertThat(Locator locator) {
|
||||
return new LocatorAssertionsImpl(locator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code PageAssertions} object for the given {@code Page}.
|
||||
* <pre>{@code
|
||||
* PlaywrightAssertions.assertThat(page).hasTitle("News");
|
||||
* }</pre>
|
||||
*
|
||||
* @param page {@code Page} object to use for assertions.
|
||||
*/
|
||||
static PageAssertions assertThat(Page page) {
|
||||
return new PageAssertionsImpl(page);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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 org.opentest4j.AssertionFailedError;
|
||||
import org.opentest4j.ValueWrapper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
class AssertionsBase {
|
||||
final LocatorImpl actualLocator;
|
||||
final 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) {
|
||||
expectImpl(expression, asList(textValue), expected, message, options);
|
||||
}
|
||||
|
||||
void expectImpl(String expression, List<ExpectedTextValue> expectedText, Object expected, String message, FrameExpectOptions options) {
|
||||
if (options == null) {
|
||||
options = new FrameExpectOptions();
|
||||
}
|
||||
options.expectedText = expectedText;
|
||||
options.isNot = isNot;
|
||||
expectImpl(expression, options, expected, message);
|
||||
}
|
||||
|
||||
void expectImpl(String expression, FrameExpectOptions expectOptions, Object expected, String message) {
|
||||
if (expectOptions.timeout == null) {
|
||||
expectOptions.timeout = 5_000.0;
|
||||
}
|
||||
if (expectOptions.isNot) {
|
||||
message = message.replace("expected to", "expected not to");
|
||||
}
|
||||
FrameExpectResult result = actualLocator.expect(expression, expectOptions);
|
||||
if (result.matches == isNot) {
|
||||
Object actual = result.received == null ? null : Serialization.deserialize(result.received);
|
||||
String log = String.join("\n", result.log);
|
||||
if (!log.isEmpty()) {
|
||||
log = "\nCall log:\n" + log;
|
||||
}
|
||||
if (expected == null) {
|
||||
throw new AssertionFailedError(message + log);
|
||||
}
|
||||
throw new AssertionFailedError(message + log, formatValue(expected), formatValue(actual));
|
||||
}
|
||||
}
|
||||
|
||||
private static ValueWrapper formatValue(Object value) {
|
||||
if (value == null || !value.getClass().isArray()) {
|
||||
return ValueWrapper.create(value);
|
||||
}
|
||||
Collection<String> values = asList((Object[]) value).stream().map(e -> e.toString()).collect(Collectors.toList());
|
||||
String stringRepresentation = "[" + String.join(", ", values) + "]";
|
||||
return ValueWrapper.create(value, stringRepresentation);
|
||||
}
|
||||
|
||||
static ExpectedTextValue expectedRegex(Pattern pattern) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.regexSource = pattern.pattern();
|
||||
if (pattern.flags() != 0) {
|
||||
expected.regexFlags = "";
|
||||
if ((pattern.flags() & Pattern.CASE_INSENSITIVE) != 0) {
|
||||
// Case-insensitive search.
|
||||
expected.regexFlags += "i";
|
||||
}
|
||||
if ((pattern.flags() & Pattern.DOTALL) != 0) {
|
||||
// Allows . to match newline characters.
|
||||
expected.regexFlags += "s";
|
||||
}
|
||||
if ((pattern.flags() & Pattern.MULTILINE) != 0) {
|
||||
// Multi-line search.
|
||||
expected.regexFlags += "m";
|
||||
}
|
||||
if ((pattern.flags() & ~(Pattern.MULTILINE | Pattern.CASE_INSENSITIVE | Pattern.DOTALL)) != 0) {
|
||||
throw new PlaywrightException("Unexpected RegEx flag, only CASE_INSENSITIVE, DOTALL and MULTILINE are supported.");
|
||||
}
|
||||
}
|
||||
return expected;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* 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.Locator;
|
||||
import com.microsoft.playwright.assertions.LocatorAssertions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.serializeArgument;
|
||||
import static com.microsoft.playwright.impl.Utils.convertViaJson;
|
||||
|
||||
public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAssertions {
|
||||
public LocatorAssertionsImpl(Locator locator) {
|
||||
this(locator, false);
|
||||
}
|
||||
|
||||
private LocatorAssertionsImpl(Locator locator, boolean isNot) {
|
||||
super((LocatorImpl) locator, isNot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsText(String text, ContainsTextOptions options) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = text;
|
||||
expected.matchSubstring = true;
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.text", expected, text, "Locator expected to contain text", convertViaJson(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsText(Pattern pattern, ContainsTextOptions options) {
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
expected.matchSubstring = true;
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.text", expected, pattern, "Locator expected to contain regex", convertViaJson(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsText(String[] strings, ContainsTextOptions options) {
|
||||
List<ExpectedTextValue> list = new ArrayList<>();
|
||||
for (String text : strings) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = text;
|
||||
expected.matchSubstring = true;
|
||||
expected.normalizeWhiteSpace = true;
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.contain.text.array", list, strings, "Locator expected to contain text", convertViaJson(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containsText(Pattern[] patterns, ContainsTextOptions options) {
|
||||
List<ExpectedTextValue> list = new ArrayList<>();
|
||||
for (Pattern pattern : patterns) {
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
expected.matchSubstring = true;
|
||||
expected.normalizeWhiteSpace = true;
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.contain.text.array", list, patterns, "Locator expected to contain text", convertViaJson(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasAttribute(String name, String text, HasAttributeOptions options) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = text;
|
||||
hasAttribute(name, expected, text, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasAttribute(String name, Pattern pattern, HasAttributeOptions options) {
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
hasAttribute(name, expected, pattern, options);
|
||||
}
|
||||
|
||||
private void hasAttribute(String name, ExpectedTextValue expectedText, Object expectedValue, HasAttributeOptions options) {
|
||||
if (options == null) {
|
||||
options = new HasAttributeOptions();
|
||||
}
|
||||
FrameExpectOptions commonOptions = convertViaJson(options, FrameExpectOptions.class);
|
||||
commonOptions.expressionArg = name;
|
||||
String message = "Locator expected to have attribute '" + name + "'";
|
||||
if (expectedValue instanceof Pattern) {
|
||||
message += " matching regex";
|
||||
}
|
||||
expectImpl("to.have.attribute", 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", convertViaJson(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", convertViaJson(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasClass(String[] strings, HasClassOptions options) {
|
||||
List<ExpectedTextValue> list = new ArrayList<>();
|
||||
for (String text : strings) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = text;
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.have.class.array", list, strings, "Locator expected to have class", convertViaJson(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasClass(Pattern[] patterns, HasClassOptions options) {
|
||||
List<ExpectedTextValue> list = new ArrayList<>();
|
||||
for (Pattern pattern : patterns) {
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.have.class.array", list, patterns, "Locator expected to have class matching regex", convertViaJson(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasCount(int count, HasCountOptions options) {
|
||||
if (options == null) {
|
||||
options = new HasCountOptions();
|
||||
}
|
||||
FrameExpectOptions commonOptions = convertViaJson(options, FrameExpectOptions.class);
|
||||
commonOptions.expectedNumber = count;
|
||||
List<ExpectedTextValue> expectedText = null;
|
||||
expectImpl("to.have.count", expectedText, count, "Locator expected to have count", commonOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasCSS(String name, String value, HasCSSOptions options) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = value;
|
||||
hasCSS(name, expected, value, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasCSS(String name, Pattern pattern, HasCSSOptions options) {
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
hasCSS(name, expected, pattern, options);
|
||||
}
|
||||
|
||||
private void hasCSS(String name, ExpectedTextValue expectedText, Object expectedValue, HasCSSOptions options) {
|
||||
if (options == null) {
|
||||
options = new HasCSSOptions();
|
||||
}
|
||||
FrameExpectOptions commonOptions = convertViaJson(options, FrameExpectOptions.class);
|
||||
commonOptions.expressionArg = name;
|
||||
String message = "Locator expected to have CSS property '" + name + "'";
|
||||
if (expectedValue instanceof Pattern) {
|
||||
message += " matching regex";
|
||||
}
|
||||
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", convertViaJson(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasJSProperty(String name, Object value, HasJSPropertyOptions options) {
|
||||
if (options == null) {
|
||||
options = new HasJSPropertyOptions();
|
||||
}
|
||||
FrameExpectOptions commonOptions = convertViaJson(options, FrameExpectOptions.class);
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasText(String text, HasTextOptions options) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = text;
|
||||
expected.matchSubstring = false;
|
||||
expected.normalizeWhiteSpace = true;
|
||||
expectImpl("to.have.text", expected, text, "Locator expected to have text", convertViaJson(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasText(Pattern pattern, HasTextOptions options) {
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
// 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", convertViaJson(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasText(String[] strings, HasTextOptions options) {
|
||||
List<ExpectedTextValue> list = new ArrayList<>();
|
||||
for (String text : strings) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = text;
|
||||
expected.matchSubstring = false;
|
||||
expected.normalizeWhiteSpace = true;
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.have.text.array", list, strings, "Locator expected to have text", convertViaJson(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasText(Pattern[] patterns, HasTextOptions options) {
|
||||
List<ExpectedTextValue> list = new ArrayList<>();
|
||||
for (Pattern pattern : patterns) {
|
||||
ExpectedTextValue expected = expectedRegex(pattern);
|
||||
expected.matchSubstring = true;
|
||||
expected.normalizeWhiteSpace = true;
|
||||
list.add(expected);
|
||||
}
|
||||
expectImpl("to.have.text.array", list, patterns, "Locator expected to have text matching regex", convertViaJson(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", convertViaJson(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", convertViaJson(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isChecked(IsCheckedOptions options) {
|
||||
expectTrue("to.be.checked", "Locator expected to be checked", convertViaJson(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isDisabled(IsDisabledOptions options) {
|
||||
expectTrue("to.be.disabled", "Locator expected to be disabled", convertViaJson(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isEditable(IsEditableOptions options) {
|
||||
expectTrue("to.be.editable", "Locator expected to be editable", convertViaJson(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isEmpty(IsEmptyOptions options) {
|
||||
expectTrue("to.be.empty", "Locator expected to be empty", convertViaJson(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isEnabled(IsEnabledOptions options) {
|
||||
expectTrue("to.be.enabled", "Locator expected to be enabled", convertViaJson(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isFocused(IsFocusedOptions options) {
|
||||
expectTrue("to.be.focused", "Locator expected to be focused", convertViaJson(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isHidden(IsHiddenOptions options) {
|
||||
expectTrue("to.be.hidden", "Locator expected to be hidden", convertViaJson(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isVisible(IsVisibleOptions options) {
|
||||
expectTrue("to.be.visible", "Locator expected to be visible", convertViaJson(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
private void expectTrue(String expression, String message, FrameExpectOptions options) {
|
||||
List<ExpectedTextValue> expectedText = null;
|
||||
expectImpl(expression, expectedText, null, message, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocatorAssertions not() {
|
||||
return new LocatorAssertionsImpl(actualLocator, !isNot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.Page;
|
||||
import com.microsoft.playwright.assertions.PageAssertions;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.microsoft.playwright.impl.UrlMatcher.resolveUrl;
|
||||
import static com.microsoft.playwright.impl.Utils.convertViaJson;
|
||||
|
||||
public class PageAssertionsImpl extends AssertionsBase implements PageAssertions {
|
||||
private final PageImpl actualPage;
|
||||
|
||||
public PageAssertionsImpl(Page page) {
|
||||
this(page, false);
|
||||
}
|
||||
|
||||
private PageAssertionsImpl(Page page, boolean isNot) {
|
||||
super((LocatorImpl) page.locator(":root"), isNot);
|
||||
this.actualPage = (PageImpl) page;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hasTitle(String title, HasTitleOptions options) {
|
||||
ExpectedTextValue expected = new ExpectedTextValue();
|
||||
expected.string = title;
|
||||
expectImpl("to.have.title", expected, title, "Page title expected to be", convertViaJson(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", convertViaJson(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);
|
||||
}
|
||||
expected.string = url;
|
||||
expectImpl("to.have.url", expected, url, "Page URL expected to be", convertViaJson(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", convertViaJson(options, FrameExpectOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageAssertions not() {
|
||||
return new PageAssertionsImpl(actualPage, !isNot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,826 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import com.microsoft.playwright.assertions.LocatorAssertions;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.opentest4j.AssertionFailedError;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.microsoft.playwright.Utils.mapOf;
|
||||
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestLocatorAssertions extends TestBase {
|
||||
@Test
|
||||
void containsTextWRegexPass() {
|
||||
page.setContent("<div id=node>Text content</div>");
|
||||
Locator locator = page.locator("#node");
|
||||
assertThat(locator).containsText(Pattern.compile("ex"));
|
||||
// Should not normalize whitespace.
|
||||
assertThat(locator).containsText(Pattern.compile("ext cont"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void containsTextWRegexCaseInsensitivePass() {
|
||||
page.setContent("<div id=node>Text content</div>");
|
||||
Locator locator = page.locator("#node");
|
||||
assertThat(locator).containsText(Pattern.compile("text", Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void containsTextWRegexMultilinePass() {
|
||||
page.setContent("<div id=node>Text \nContent</div>");
|
||||
Locator locator = page.locator("#node");
|
||||
assertThat(locator).containsText(Pattern.compile("^Content", Pattern.MULTILINE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void containsTextWRegexDotAllPass() {
|
||||
page.setContent("<div id=node>foo\nbar</div>");
|
||||
Locator locator = page.locator("#node");
|
||||
assertThat(locator).containsText(Pattern.compile("foo.bar", Pattern.DOTALL));
|
||||
}
|
||||
|
||||
@Test
|
||||
void containsTextWRegexFail() {
|
||||
page.setContent("<div id=node>Text content</div>");
|
||||
Locator locator = page.locator("#node");
|
||||
try {
|
||||
assertThat(locator).containsText(Pattern.compile("ex2"), new LocatorAssertions.ContainsTextOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("ex2", e.getExpected().getStringRepresentation());
|
||||
assertEquals("Text content", e.getActual().getValue());
|
||||
assertTrue(e.getMessage().contains("Locator expected to contain regex"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasTextWRegexPass() {
|
||||
page.setContent("<div id=node>Text content</div>");
|
||||
Locator locator = page.locator("#node");
|
||||
assertThat(locator).hasText(Pattern.compile("Te.t"));
|
||||
// Should not normalize whitespace.
|
||||
assertThat(locator).hasText(Pattern.compile("Text.+content"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasTextWRegexFail() {
|
||||
page.setContent("<div id=node>Text content</div>");
|
||||
Locator locator = page.locator("#node");
|
||||
try {
|
||||
assertThat(locator).hasText(Pattern.compile("Text 2"), new LocatorAssertions.HasTextOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("Text 2", e.getExpected().getStringRepresentation());
|
||||
assertEquals("Text content", e.getActual().getValue());
|
||||
assertTrue(e.getMessage().contains("Locator expected to have text matching regex"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasTextWTextPass() {
|
||||
page.setContent("<div id=node><span></span>Text \ncontent </div>");
|
||||
Locator locator = page.locator("#node");
|
||||
// Should normalize whitespace.
|
||||
assertThat(locator).hasText("Text content");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasTextWTextFail() {
|
||||
page.setContent("<div id=node>Text content</div>");
|
||||
Locator locator = page.locator("#node");
|
||||
// Should normalize whitespace.
|
||||
try {
|
||||
assertThat(locator).hasText("Text", new LocatorAssertions.HasTextOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("Text", e.getExpected().getStringRepresentation());
|
||||
assertEquals("Text content", e.getActual().getValue());
|
||||
assertTrue(e.getMessage().contains("Locator expected to have text"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasTextWTextArrayPass() {
|
||||
page.setContent("<div>Text \n1</div><div>Text 2a</div>");
|
||||
Locator locator = page.locator("div");
|
||||
// Should normalize whitespace.
|
||||
assertThat(locator).hasText(new String[] {"Text 1", "Text 2a"});
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasTextWTextArrayPassEmpty() {
|
||||
page.setContent("<div></div>");
|
||||
Locator locator = page.locator("p");
|
||||
// Should normalize whitespace.
|
||||
assertThat(locator).hasText(new String[] {});
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasTextWTextArrayPassNotEmpty() {
|
||||
page.setContent("<div><p>Test</p></div>");
|
||||
Locator locator = page.locator("div");
|
||||
// Should normalize whitespace.
|
||||
assertThat(locator).not().hasText(new String[] {});
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasTextWTextArrayPassOnEmpty() {
|
||||
page.setContent("<div></div>");
|
||||
Locator locator = page.locator("p");
|
||||
// Should normalize whitespace.
|
||||
assertThat(locator).not().hasText(new String[] {"Test"});
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasTextWTextArrayFailOnNotEmpty() {
|
||||
page.setContent("<div></div>");
|
||||
Locator locator = page.locator("p");
|
||||
// Should normalize whitespace.
|
||||
try {
|
||||
assertThat(locator).not().hasText(new String[] {}, new LocatorAssertions.HasTextOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("[]", e.getExpected().getStringRepresentation());
|
||||
assertEquals("null", e.getActual().getStringRepresentation());
|
||||
assertTrue(e.getMessage().contains("Locator expected not to have text"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasTextWTextArrayPassLazyPass() {
|
||||
page.setContent("<div id=div></div>");
|
||||
Locator locator = page.locator("p");
|
||||
page.evaluate("setTimeout(() => {\n" +
|
||||
" div.innerHTML = \"<p>Text 1</p><p>Text 2</p>\";\n" +
|
||||
"}, 100);");
|
||||
// Should normalize whitespace.
|
||||
assertThat(locator).hasText(new String[] {"Text 1", "Text 2"}, new LocatorAssertions.HasTextOptions().setTimeout(1000));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasTextWTextArrayFail() {
|
||||
page.setContent("<div>Text 1</div><div>Text 3</div>");
|
||||
Locator locator = page.locator("div");
|
||||
page.evaluate("setTimeout(() => {\n" +
|
||||
" div.innerHTML = \"<p>Text 1</p><p>Text 2</p>\";\n" +
|
||||
"}, 100);");
|
||||
try {
|
||||
// Should normalize whitespace.
|
||||
assertThat(locator).hasText(new String[] {"Text 1", "Text 3", "Extra"}, new LocatorAssertions.HasTextOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("[Text 1, Text 3, Extra]", e.getExpected().getStringRepresentation());
|
||||
assertEquals("[Text 1, Text 3]", e.getActual().getStringRepresentation());
|
||||
assertTrue(e.getMessage().contains("Locator expected to have text"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasTextWRegExArrayPass() {
|
||||
page.setContent("<div>Text \n1</div><div>Text 2a</div>");
|
||||
Locator locator = page.locator("div");
|
||||
// Should normalize whitespace.
|
||||
assertThat(locator).hasText(new Pattern[] {Pattern.compile( "Text \n1"), Pattern.compile("Text \\d+a")});
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasTextWRegExArrayFail() {
|
||||
page.setContent("<div>Text 1</div><div>Text 3</div>");
|
||||
Locator locator = page.locator("div");
|
||||
try {
|
||||
// Should normalize whitespace.
|
||||
assertThat(locator).hasText(new Pattern[] {Pattern.compile( "Text 1"), Pattern.compile("Text \\d"), Pattern.compile("Extra")}, new LocatorAssertions.HasTextOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("[Text 1, Text \\d, Extra]", e.getExpected().getStringRepresentation());
|
||||
assertEquals("[Text 1, Text 3]", e.getActual().getStringRepresentation());
|
||||
assertTrue(e.getMessage().contains("Locator expected to have text"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasAttributeTextPass() {
|
||||
page.setContent("<div id=node>Text content</div>");
|
||||
Locator locator = page.locator("#node");
|
||||
assertThat(locator).hasAttribute("id", "node");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasAttributeTextFail() {
|
||||
page.setContent("<div id=node>Text content</div>");
|
||||
Locator locator = page.locator("#node");
|
||||
try {
|
||||
assertThat(locator).hasAttribute("id", "foo", new LocatorAssertions.HasAttributeOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("foo", e.getExpected().getStringRepresentation());
|
||||
assertEquals("node", e.getActual().getStringRepresentation());
|
||||
assertTrue(e.getMessage().contains("Locator expected to have attribute 'id'"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasAttributeRegExpPass() {
|
||||
page.setContent("<div id=node>Text content</div>");
|
||||
Locator locator = page.locator("#node");
|
||||
assertThat(locator).hasAttribute("id", Pattern.compile("n..e"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasAttributeRegExpFail() {
|
||||
page.setContent("<div id=node>Text content</div>");
|
||||
Locator locator = page.locator("#node");
|
||||
try {
|
||||
assertThat(locator).hasAttribute("id", Pattern.compile(".Nod.."), new LocatorAssertions.HasAttributeOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals(".Nod..", e.getExpected().getStringRepresentation());
|
||||
assertEquals("node", e.getActual().getStringRepresentation());
|
||||
assertTrue(e.getMessage().contains("Locator expected to have attribute 'id' matching regex"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasClassTextPass() {
|
||||
page.setContent("<div class=\"foo bar baz\"></div>");
|
||||
Locator locator = page.locator("div");
|
||||
assertThat(locator).hasClass("foo bar baz");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasClassTextFail() {
|
||||
page.setContent("<div class=\"bar baz\"></div>");
|
||||
Locator locator = page.locator("div");
|
||||
try {
|
||||
assertThat(locator).hasClass("foo bar baz", new LocatorAssertions.HasClassOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("foo bar baz", e.getExpected().getStringRepresentation());
|
||||
assertEquals("bar baz", e.getActual().getStringRepresentation());
|
||||
assertTrue(e.getMessage().contains("Locator expected to have class"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasClassRegExpPass() {
|
||||
page.setContent("<div class=\"foo bar baz\"></div>");
|
||||
Locator locator = page.locator("div");
|
||||
assertThat(locator).hasClass(Pattern.compile("foo.* baz"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasClassRegExpFail() {
|
||||
page.setContent("<div class=\"bar baz\"></div>");
|
||||
Locator locator = page.locator("div");
|
||||
try {
|
||||
assertThat(locator).hasClass(Pattern.compile("foo Z.*"), new LocatorAssertions.HasClassOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("foo Z.*", e.getExpected().getStringRepresentation());
|
||||
assertEquals("bar baz", e.getActual().getStringRepresentation());
|
||||
assertTrue(e.getMessage().contains("Locator expected to have class matching regex"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasClassTextArrayPass() {
|
||||
page.setContent("<div class=\"foo\"></div><div class=\"bar\"></div><div class=\"baz\"></div>");
|
||||
Locator locator = page.locator("div");
|
||||
assertThat(locator).hasClass(new String[] {"foo", "bar", "baz"});
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasClassTextArrayFail() {
|
||||
page.setContent("<div class=\"foo\"></div><div class=\"bar\"></div><div class=\"baz\"></div>");
|
||||
Locator locator = page.locator("div");
|
||||
try {
|
||||
assertThat(locator).hasClass(new String[] {"foo", "bar", "missing"}, new LocatorAssertions.HasClassOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("[foo, bar, missing]", e.getExpected().getStringRepresentation());
|
||||
assertEquals("[foo, bar, baz]", e.getActual().getStringRepresentation());
|
||||
assertTrue(e.getMessage().contains("Locator expected to have class"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasClassRegExpArrayPass() {
|
||||
page.setContent("<div class=\"foo\"></div><div class=\"bar\"></div><div class=\"baz\"></div>");
|
||||
Locator locator = page.locator("div");
|
||||
assertThat(locator).hasClass(new Pattern[] {Pattern.compile("fo.*"), Pattern.compile(".ar"), Pattern.compile("baz")});
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasClassRegExpArrayFail() {
|
||||
page.setContent("<div class=\"foo\"></div><div class=\"bar\"></div><div class=\"baz\"></div>");
|
||||
Locator locator = page.locator("div");
|
||||
try {
|
||||
assertThat(locator).hasClass(new Pattern[] {Pattern.compile("fo.*"), Pattern.compile(".ar"), Pattern.compile("baz"), Pattern.compile("extra")}, new LocatorAssertions.HasClassOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("[fo.*, .ar, baz, extra]", e.getExpected().getStringRepresentation());
|
||||
assertEquals("[foo, bar, baz]", e.getActual().getStringRepresentation());
|
||||
assertTrue(e.getMessage().contains("Locator expected to have class matching regex"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasCountPass() {
|
||||
page.setContent("<select><option>One</option><option>Two</option></select>");
|
||||
Locator locator = page.locator("option");
|
||||
assertThat(locator).hasCount(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasCountFail() {
|
||||
page.setContent("<select><option>One</option><option>Two</option></select>");
|
||||
Locator locator = page.locator("option");
|
||||
try {
|
||||
assertThat(locator).hasCount(1, new LocatorAssertions.HasCountOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("1", e.getExpected().getStringRepresentation());
|
||||
assertEquals("2", e.getActual().getStringRepresentation());
|
||||
assertTrue(e.getMessage().contains("Locator expected to have count"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasCountPassZero() {
|
||||
page.setContent("<div></div>");
|
||||
Locator locator = page.locator("span");
|
||||
assertThat(locator).hasCount(0);
|
||||
assertThat(locator).not().hasCount(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasCSSPass() {
|
||||
page.setContent("<div id=node style='color: rgb(255, 0, 0)'>Text content</div>");
|
||||
Locator locator = page.locator("#node");
|
||||
assertThat(locator).hasCSS("color", "rgb(255, 0, 0)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasCSSFail() {
|
||||
page.setContent("<div id=node style='color: rgb(255, 0, 0)'>Text content</div>");
|
||||
Locator locator = page.locator("#node");
|
||||
try {
|
||||
assertThat(locator).hasCSS("color", "red", new LocatorAssertions.HasCSSOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("red", e.getExpected().getStringRepresentation());
|
||||
assertEquals("rgb(255, 0, 0)", e.getActual().getStringRepresentation());
|
||||
assertTrue(e.getMessage().contains("Locator expected to have CSS property 'color'"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasCSSRegExPass() {
|
||||
page.setContent("<div id=node style='color: rgb(255, 0, 0)'>Text content</div>");
|
||||
Locator locator = page.locator("#node");
|
||||
assertThat(locator).hasCSS("color", Pattern.compile("rgb.*"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasCSSRegExFail() {
|
||||
page.setContent("<div id=node style='color: rgb(255, 0, 0)'>Text content</div>");
|
||||
Locator locator = page.locator("#node");
|
||||
try {
|
||||
assertThat(locator).hasCSS("color", Pattern.compile("red"), new LocatorAssertions.HasCSSOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("red", e.getExpected().getStringRepresentation());
|
||||
assertEquals("rgb(255, 0, 0)", e.getActual().getStringRepresentation());
|
||||
assertTrue(e.getMessage().contains("Locator expected to have CSS property 'color' matching regex"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasIdPass() {
|
||||
page.setContent("<div id=node>Text content</div>");
|
||||
Locator locator = page.locator("#node");
|
||||
assertThat(locator).hasId("node");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasIdFail() {
|
||||
page.setContent("<div id=node>Text content</div>");
|
||||
Locator locator = page.locator("#node");
|
||||
try {
|
||||
assertThat(locator).hasId("foo", new LocatorAssertions.HasIdOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("foo", e.getExpected().getStringRepresentation());
|
||||
assertEquals("node", e.getActual().getStringRepresentation());
|
||||
assertTrue(e.getMessage().contains("Locator expected to have ID"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasJSPropertyPass() {
|
||||
page.setContent("<div></div>");
|
||||
page.evalOnSelector("div", "e => e.foo = { a: 1, b: 'string' }");
|
||||
Locator locator = page.locator("div");
|
||||
assertThat(locator).hasJSProperty("foo", mapOf("a", 1, "b", "string"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasJSPropertyNumberFail() {
|
||||
page.setContent("<div id=node>Text content</div>");
|
||||
Locator locator = page.locator("#node");
|
||||
page.evalOnSelector("div", "e => e.foo = 2021");
|
||||
try {
|
||||
assertThat(locator).hasJSProperty("foo", 1, new LocatorAssertions.HasJSPropertyOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("1", e.getExpected().getStringRepresentation());
|
||||
assertEquals("2021", e.getActual().getStringRepresentation());
|
||||
assertTrue(e.getMessage().contains("Locator expected to have JavaScript property 'foo'"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasJSPropertyObjectFail() {
|
||||
page.setContent("<div id=node>Text content</div>");
|
||||
Locator locator = page.locator("#node");
|
||||
page.evalOnSelector("div", "e => e.foo = { a: 1, b: 'string' }");
|
||||
try {
|
||||
assertThat(locator).hasJSProperty("foo", mapOf("a", 2), new LocatorAssertions.HasJSPropertyOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("{a=2}", e.getExpected().getStringRepresentation());
|
||||
assertEquals("{a=1, b=string}", e.getActual().getStringRepresentation());
|
||||
assertTrue(e.getMessage().contains("Locator expected to have JavaScript property 'foo'"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasJSPropertyStringFail() {
|
||||
page.setContent("<div id=node>Text content</div>");
|
||||
Locator locator = page.locator("#node");
|
||||
try {
|
||||
assertThat(locator).hasJSProperty("id", "foo", new LocatorAssertions.HasJSPropertyOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("foo", e.getExpected().getStringRepresentation());
|
||||
assertEquals("node", e.getActual().getStringRepresentation());
|
||||
assertTrue(e.getMessage().contains("Locator expected to have JavaScript property 'id'"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasValueTextPass() {
|
||||
page.setContent("<input id=node></input>");
|
||||
Locator locator = page.locator("#node");
|
||||
locator.fill("Text content");
|
||||
assertThat(locator).hasValue("Text content");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasValueTextFail() {
|
||||
page.setContent("<input id=node></input>");
|
||||
Locator locator = page.locator("#node");
|
||||
locator.fill("Text content");
|
||||
try {
|
||||
assertThat(locator).hasValue("Text2", new LocatorAssertions.HasValueOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("Text2", e.getExpected().getStringRepresentation());
|
||||
assertEquals("Text content", e.getActual().getStringRepresentation());
|
||||
assertTrue(e.getMessage().contains("Locator expected to have value"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasValueRegExpPass() {
|
||||
page.setContent("<input id=node></input>");
|
||||
Locator locator = page.locator("#node");
|
||||
locator.fill("Text content");
|
||||
assertThat(locator).hasValue(Pattern.compile("Text"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasValueRegExpPassWithNot() {
|
||||
page.setContent("<input id=node></input>");
|
||||
Locator locator = page.locator("#node");
|
||||
locator.fill("Text content");
|
||||
assertThat(locator).not().hasValue(Pattern.compile("Text2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasValueRegExpFail() {
|
||||
page.setContent("<input id=node></input>");
|
||||
Locator locator = page.locator("#node");
|
||||
locator.fill("Text content");
|
||||
try {
|
||||
assertThat(locator).hasValue(Pattern.compile("Text2"), new LocatorAssertions.HasValueOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("Text2", e.getExpected().getStringRepresentation());
|
||||
assertEquals("Text content", e.getActual().getStringRepresentation());
|
||||
assertTrue(e.getMessage().contains("Locator expected to have value matching regex"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void isCheckedPass() {
|
||||
page.setContent("<input type=checkbox checked></input>");
|
||||
Locator locator = page.locator("input");
|
||||
assertThat(locator).isChecked();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isCheckedFail() {
|
||||
page.setContent("<input type=checkbox></input>");
|
||||
Locator locator = page.locator("input");
|
||||
try {
|
||||
assertThat(locator).isChecked(new LocatorAssertions.IsCheckedOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertNull(e.getExpected());
|
||||
assertNull(e.getActual());
|
||||
assertTrue(e.getMessage().contains("Locator expected to be checked"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void notIsCheckedFail() {
|
||||
page.setContent("<input type=checkbox checked></input>");
|
||||
Locator locator = page.locator("input");
|
||||
try {
|
||||
assertThat(locator).not().isChecked(new LocatorAssertions.IsCheckedOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertNull(e.getExpected());
|
||||
assertNull(e.getActual());
|
||||
assertTrue(e.getMessage().contains("Locator expected not to be checked"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void isDisabledPass() {
|
||||
page.setContent("<button disabled>Text</button>");
|
||||
Locator locator = page.locator("button");
|
||||
assertThat(locator).isDisabled();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isDisabledFail() {
|
||||
page.setContent("<button>Text</button>");
|
||||
Locator locator = page.locator("button");
|
||||
try {
|
||||
assertThat(locator).isDisabled(new LocatorAssertions.IsDisabledOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertNull(e.getExpected());
|
||||
assertNull(e.getActual());
|
||||
assertTrue(e.getMessage().contains("Locator expected to be disabled"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void notIsDisabledFail() {
|
||||
page.setContent("<button disabled>Text</button>");
|
||||
Locator locator = page.locator("button");
|
||||
try {
|
||||
assertThat(locator).not().isDisabled(new LocatorAssertions.IsDisabledOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertNull(e.getExpected());
|
||||
assertNull(e.getActual());
|
||||
assertTrue(e.getMessage().contains("Locator expected not to be disabled"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEditablePass() {
|
||||
page.setContent("<input></input>");
|
||||
Locator locator = page.locator("input");
|
||||
assertThat(locator).isEditable();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEditableFail() {
|
||||
page.setContent("<input disabled></input>");
|
||||
Locator locator = page.locator("input");
|
||||
try {
|
||||
assertThat(locator).isEditable(new LocatorAssertions.IsEditableOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertNull(e.getExpected());
|
||||
assertNull(e.getActual());
|
||||
assertTrue(e.getMessage().contains("Locator expected to be editable"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void notIsEditableFail() {
|
||||
page.setContent("<input></input>");
|
||||
Locator locator = page.locator("input");
|
||||
try {
|
||||
assertThat(locator).not().isEditable(new LocatorAssertions.IsEditableOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertNull(e.getExpected());
|
||||
assertNull(e.getActual());
|
||||
assertTrue(e.getMessage().contains("Locator expected not to be editable"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void isEmptyPass() {
|
||||
page.setContent("<input></input>");
|
||||
Locator locator = page.locator("input");
|
||||
assertThat(locator).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEmptyFail() {
|
||||
page.setContent("<input value=text></input>");
|
||||
Locator locator = page.locator("input");
|
||||
try {
|
||||
assertThat(locator).isEmpty(new LocatorAssertions.IsEmptyOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertNull(e.getExpected());
|
||||
assertNull(e.getActual());
|
||||
assertTrue(e.getMessage().contains("Locator expected to be empty"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void notIsEmptyFail() {
|
||||
page.setContent("<input></input>");
|
||||
Locator locator = page.locator("input");
|
||||
try {
|
||||
assertThat(locator).not().isEmpty(new LocatorAssertions.IsEmptyOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertNull(e.getExpected());
|
||||
assertNull(e.getActual());
|
||||
assertTrue(e.getMessage().contains("Locator expected not to be empty"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEnabledPass() {
|
||||
page.setContent("<button>Text</button>");
|
||||
Locator locator = page.locator("button");
|
||||
assertThat(locator).isEnabled();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEnabledFail() {
|
||||
page.setContent("<button disabled>Text</button>");
|
||||
Locator locator = page.locator("button");
|
||||
try {
|
||||
assertThat(locator).isEnabled(new LocatorAssertions.IsEnabledOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertNull(e.getExpected());
|
||||
assertNull(e.getActual());
|
||||
assertTrue(e.getMessage().contains("Locator expected to be enabled"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void notIsEnabledFail() {
|
||||
page.setContent("<button>Text</button>");
|
||||
Locator locator = page.locator("button");
|
||||
try {
|
||||
assertThat(locator).not().isEnabled(new LocatorAssertions.IsEnabledOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertNull(e.getExpected());
|
||||
assertNull(e.getActual());
|
||||
assertTrue(e.getMessage().contains("Locator expected not to be enabled"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void isFocusedPass() {
|
||||
page.setContent("<input></input>");
|
||||
Locator locator = page.locator("input");
|
||||
locator.focus();
|
||||
assertThat(locator).isFocused();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isFocusedFail() {
|
||||
page.setContent("<input></input>");
|
||||
Locator locator = page.locator("input");
|
||||
try {
|
||||
assertThat(locator).isFocused(new LocatorAssertions.IsFocusedOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertNull(e.getExpected());
|
||||
assertNull(e.getActual());
|
||||
assertTrue(e.getMessage().contains("Locator expected to be focused"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void notIsFocusedFail() {
|
||||
page.setContent("<input></input>");
|
||||
Locator locator = page.locator("input");
|
||||
locator.focus();
|
||||
try {
|
||||
assertThat(locator).not().isFocused(new LocatorAssertions.IsFocusedOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertNull(e.getExpected());
|
||||
assertNull(e.getActual());
|
||||
assertTrue(e.getMessage().contains("Locator expected not to be focused"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void isHiddenPass() {
|
||||
page.setContent("<button style='display: none'></button>");
|
||||
Locator locator = page.locator("button");
|
||||
assertThat(locator).isHidden();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isHiddenFail() {
|
||||
page.setContent("<button></button>");
|
||||
Locator locator = page.locator("button");
|
||||
try {
|
||||
assertThat(locator).isHidden(new LocatorAssertions.IsHiddenOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertNull(e.getExpected());
|
||||
assertNull(e.getActual());
|
||||
assertTrue(e.getMessage().contains("Locator expected to be hidden"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void notIsHiddenFail() {
|
||||
page.setContent("<button style='display: none'></button>");
|
||||
Locator locator = page.locator("button");
|
||||
try {
|
||||
assertThat(locator).not().isHidden(new LocatorAssertions.IsHiddenOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertNull(e.getExpected());
|
||||
assertNull(e.getActual());
|
||||
assertTrue(e.getMessage().contains("Locator expected not to be hidden"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void isVisiblePass() {
|
||||
page.setContent("<input></input>");
|
||||
Locator locator = page.locator("input");
|
||||
assertThat(locator).isVisible();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isVisibleFail() {
|
||||
page.setContent("<input style='display: none'></input>");
|
||||
Locator locator = page.locator("input");
|
||||
try {
|
||||
assertThat(locator).isVisible(new LocatorAssertions.IsVisibleOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertNull(e.getExpected());
|
||||
assertNull(e.getActual());
|
||||
assertTrue(e.getMessage().contains("Locator expected to be visible"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void notIsVisibleFail() {
|
||||
page.setContent("<input></input>");
|
||||
Locator locator = page.locator("input");
|
||||
try {
|
||||
assertThat(locator).not().isVisible(new LocatorAssertions.IsVisibleOptions().setTimeout(1000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertNull(e.getExpected());
|
||||
assertNull(e.getActual());
|
||||
assertTrue(e.getMessage().contains("Locator expected not to be visible"), e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestLocatorFrame extends TestBase {
|
||||
private static void routeIframe(Page page) {
|
||||
page.route("**/empty.html", route -> route.fulfill(new Route.FulfillOptions()
|
||||
.setBody("<iframe src='iframe.html'></iframe>").setContentType("text/html")));
|
||||
page.route("**/iframe.html", route -> {
|
||||
route.fulfill(new Route.FulfillOptions().setBody("<html>\n" +
|
||||
" <div>\n" +
|
||||
" <button>Hello iframe</button>\n" +
|
||||
" <iframe src='iframe-2.html'></iframe>\n" +
|
||||
" </div>\n" +
|
||||
" <span>1</span>\n" +
|
||||
" <span>2</span>\n" +
|
||||
" </html>").setContentType("text/html"));
|
||||
});
|
||||
page.route("**/iframe-2.html", route -> {
|
||||
route.fulfill(new Route.FulfillOptions().setBody("<html><button>Hello nested iframe</button></html>").setContentType("text/html"));
|
||||
});
|
||||
}
|
||||
|
||||
private static void routeAmbiguous(Page page) {
|
||||
page.route("**/empty.html", route -> {
|
||||
route.fulfill(new Route.FulfillOptions()
|
||||
.setBody("<iframe src='iframe-1.html'></iframe>\n" +
|
||||
"<iframe src='iframe-2.html'></iframe>\n" +
|
||||
"<iframe src='iframe-3.html'></iframe>")
|
||||
.setContentType("text/html"));
|
||||
});
|
||||
page.route("**/iframe-*", route -> {
|
||||
try {
|
||||
String path = new URL(route.request().url()).getPath().substring(1);
|
||||
route.fulfill(new Route.FulfillOptions()
|
||||
.setBody("<html><button>Hello from " + path + "</button></html>")
|
||||
.setContentType("text/html"));
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkForIframe() {
|
||||
routeIframe(page);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
Locator button = page.frameLocator("iframe").locator("button");
|
||||
button.waitFor();
|
||||
assertEquals("Hello iframe", button.innerText());
|
||||
assertThat(button).hasText("Hello iframe");
|
||||
button.click();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkForNestedIframe() {
|
||||
routeIframe(page);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
Locator button = page.frameLocator("iframe").frameLocator("iframe").locator("button");
|
||||
button.waitFor();
|
||||
assertEquals("Hello nested iframe", button.innerText());
|
||||
assertThat(button).hasText("Hello nested iframe");
|
||||
button.click();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkForAnd() {
|
||||
routeIframe(page);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
Locator locator = page.frameLocator("iframe").locator("button");
|
||||
assertThat(locator).hasText("Hello iframe");
|
||||
assertEquals("Hello iframe", locator.innerText());
|
||||
Locator spans = page.frameLocator("iframe").locator("span");
|
||||
assertThat(spans).hasCount(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWaitForFrame() {
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
try {
|
||||
page.frameLocator("iframe").locator("span").click(new Locator.ClickOptions().setTimeout(300));
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("waiting for frame \"iframe\""), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWaitForFrame2() {
|
||||
routeIframe(page);
|
||||
page.evaluate("url => setTimeout(() => location.href = url, 300)", server.EMPTY_PAGE);
|
||||
page.frameLocator("iframe").locator("button").click();
|
||||
}
|
||||
|
||||
void shouldWaitForFrameToGo() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotWaitForFrame() {
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
assertThat(page.frameLocator("iframe").locator("span")).isHidden();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotWaitForFrame2() {
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
assertThat(page.frameLocator("iframe").locator("span")).not().isVisible();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotWaitForFrame3() {
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
assertThat(page.frameLocator("iframe").locator("span")).hasCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldClickInLazyIframe() {
|
||||
page.route("**/iframe.html", route -> {
|
||||
route.fulfill(new Route.FulfillOptions().setBody("<html><button>Hello iframe</button></html>").setContentType("text/html"));
|
||||
});
|
||||
// empty pge
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
|
||||
// add blank iframe
|
||||
page.evaluate("setTimeout(() => {\n" +
|
||||
" const iframe = document.createElement('iframe');\n" +
|
||||
" document.body.appendChild(iframe);\n" +
|
||||
" // navigate iframe\n" +
|
||||
" setTimeout(() => iframe.src = 'iframe.html', 500);\n" +
|
||||
" }, 500);");
|
||||
// Click in iframe
|
||||
Locator button = page.frameLocator("iframe").locator("button");
|
||||
button.click();
|
||||
assertThat(button).hasText("Hello iframe");
|
||||
assertEquals("Hello iframe", button.innerText());
|
||||
}
|
||||
|
||||
@Test
|
||||
void waitForShouldSurviveFrameReattach() {
|
||||
routeIframe(page);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
Locator button = page.frameLocator("iframe").locator("button:has-text('Hello nested iframe')");
|
||||
page.evaluate("setTimeout(() => {\n" +
|
||||
" document.querySelector('iframe').remove();\n" +
|
||||
" setTimeout(() => {\n" +
|
||||
" const iframe = document.createElement('iframe');\n" +
|
||||
" iframe.src = 'iframe-2.html';\n" +
|
||||
" document.body.appendChild(iframe);\n" +
|
||||
" }, 500);\n" +
|
||||
" }, 500);");
|
||||
button.waitFor();
|
||||
}
|
||||
|
||||
@Test
|
||||
void clickShouldSurviveFrameReattach() {
|
||||
routeIframe(page);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
Locator button = page.frameLocator("iframe").locator("button:has-text('Hello nested iframe')");
|
||||
|
||||
page.evaluate("setTimeout(() => {\n" +
|
||||
" document.querySelector('iframe').remove();\n" +
|
||||
" setTimeout(() => {\n" +
|
||||
" const iframe = document.createElement('iframe');\n" +
|
||||
" iframe.src = 'iframe-2.html';\n" +
|
||||
" document.body.appendChild(iframe);\n" +
|
||||
" }, 500);\n" +
|
||||
" }, 500);");
|
||||
button.click();
|
||||
}
|
||||
|
||||
@Test
|
||||
void clickShouldSurviveIframeNavigation() {
|
||||
routeIframe(page);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
Locator button = page.frameLocator("iframe").locator("button:has-text('Hello nested iframe')");
|
||||
page.evaluate("setTimeout(() => {\n" +
|
||||
" document.querySelector('iframe').src = 'iframe-2.html';\n" +
|
||||
" }, 500);");
|
||||
button.click();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNonWorkForNonFrame() {
|
||||
routeIframe(page);
|
||||
page.setContent("<div></div>");
|
||||
Locator button = page.frameLocator("div").locator("button");
|
||||
try {
|
||||
button.waitFor();
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("<div></div>"), e.getMessage());
|
||||
assertTrue(e.getMessage().contains("<iframe> was expected"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void locatorFrameLocatorShouldWorkForIframe() {
|
||||
routeIframe(page);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
Locator button = page.locator("body").frameLocator("iframe").locator("button");
|
||||
button.waitFor();
|
||||
assertThat(button).hasText("Hello iframe");
|
||||
assertEquals("Hello iframe", button.innerText());
|
||||
button.click();
|
||||
}
|
||||
|
||||
@Test
|
||||
void locatorFrameLocatorShouldThrowOnAmbiguity() {
|
||||
routeAmbiguous(page);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
Locator button = page.locator("body").frameLocator("iframe").locator("button");
|
||||
try {
|
||||
button.waitFor();
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Error: strict mode violation: \"body >> iframe\" resolved to 3 elements"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void locatorFrameLocatorShouldNotThrowOnFirstLastNth() {
|
||||
routeAmbiguous(page);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
Locator button1 = page.locator("body").frameLocator("iframe").first().locator("button");
|
||||
assertThat(button1).hasText("Hello from iframe-1.html");
|
||||
Locator button2 = page.locator("body").frameLocator("iframe").nth(1).locator("button");
|
||||
assertThat(button2).hasText("Hello from iframe-2.html");
|
||||
Locator button3 = page.locator("body").frameLocator("iframe").last().locator("button");
|
||||
assertThat(button3).hasText("Hello from iframe-3.html");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import com.microsoft.playwright.assertions.PageAssertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.opentest4j.AssertionFailedError;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestPageAssertions extends TestBase {
|
||||
@Test
|
||||
void hasURLTextPass() {
|
||||
page.navigate("data:text/html,<div>A</div>");
|
||||
assertThat(page).hasURL("data:text/html,<div>A</div>");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasURLTextFail() {
|
||||
page.navigate("data:text/html,<div>B</div>");
|
||||
try {
|
||||
assertThat(page).hasURL("foo", new PageAssertions.HasURLOptions().setTimeout(1_000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("foo", e.getExpected().getValue());
|
||||
assertEquals("data:text/html,<div>B</div>", e.getActual().getValue());
|
||||
assertTrue(e.getMessage().contains("Page URL expected to be"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSupportHasUrlWithBaseUrl() {
|
||||
try (BrowserContext context = browser.newContext(new Browser.NewContextOptions().setBaseURL(server.PREFIX))) {
|
||||
Page page = context.newPage();
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
assertThat(page).hasURL("/empty.html", new PageAssertions.HasURLOptions().setTimeout(1_000));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void notHasUrlText() {
|
||||
page.navigate("data:text/html,<div>B</div>");
|
||||
assertThat(page).not().hasURL("about:blank", new PageAssertions.HasURLOptions().setTimeout(1000));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasURLRegexPass() {
|
||||
page.navigate("data:text/html,<div>A</div>");
|
||||
assertThat(page).hasURL(Pattern.compile("text"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasURLRegexFail() {
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
try {
|
||||
assertThat(page).hasURL(Pattern.compile(".*foo.*"), new PageAssertions.HasURLOptions().setTimeout(1_000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals(".*foo.*", e.getExpected().getStringRepresentation());
|
||||
assertEquals(server.EMPTY_PAGE, e.getActual().getValue());
|
||||
assertTrue(e.getMessage().contains("Page URL expected to match regex"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void notHasUrlRegEx() {
|
||||
page.navigate("data:text/html,<div>B</div>");
|
||||
assertThat(page).not().hasURL(Pattern.compile("about"), new PageAssertions.HasURLOptions().setTimeout(1000));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasTitleTextPass() {
|
||||
page.navigate(server.PREFIX + "/title.html");
|
||||
assertThat(page).hasTitle("Woof-Woof", new PageAssertions.HasTitleOptions().setTimeout(1_000));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasTitleTextFail() {
|
||||
page.navigate(server.PREFIX + "/title.html");
|
||||
try {
|
||||
assertThat(page).hasTitle("foo", new PageAssertions.HasTitleOptions().setTimeout(1_000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("foo", e.getExpected().getValue());
|
||||
assertEquals("Woof-Woof", e.getActual().getValue());
|
||||
assertTrue(e.getMessage().contains("Page title expected to be"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasTitleRegexPass() {
|
||||
page.navigate(server.PREFIX + "/title.html");
|
||||
assertThat(page).hasTitle(Pattern.compile("^.oof.+oof$"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasTitleRegexFail() {
|
||||
page.navigate(server.PREFIX + "/title.html");
|
||||
try {
|
||||
assertThat(page).hasTitle(Pattern.compile("^foo[AB]"), new PageAssertions.HasTitleOptions().setTimeout(1_000));
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("^foo[AB]", e.getExpected().getStringRepresentation());
|
||||
assertEquals("Woof-Woof", e.getActual().getValue());
|
||||
assertTrue(e.getMessage().contains("Page title expected to match regex"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void notHasTitleRegEx() {
|
||||
page.navigate(server.PREFIX + "/title.html");
|
||||
assertThat(page).not().hasTitle(Pattern.compile("ab.ut"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasTitleRegExCaseInsensitivePass() {
|
||||
page.navigate(server.PREFIX + "/title.html");
|
||||
assertThat(page).hasTitle(Pattern.compile("woof-woof", Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
}
|
||||
+2
-17
@@ -6,13 +6,13 @@
|
||||
<parent>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>parent-pom</artifactId>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.17.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>driver-bundle</artifactId>
|
||||
<name>Playwright - Drivers For All Platforms</name>
|
||||
<description>
|
||||
This module includes playwright-cli binary and related utilities for all supported platforms.
|
||||
This module includes Playwright driver and related utilities for all supported platforms.
|
||||
It is intended to be used on the systems where Playwright driver is not preinstalled.
|
||||
</description>
|
||||
|
||||
@@ -28,25 +28,10 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>8</source>
|
||||
<failOnError>false</failOnError>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<redirectTestOutputToFile>true</redirectTestOutputToFile>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
@@ -131,7 +131,7 @@ public class DriverJar extends Driver {
|
||||
private static String platformDir() {
|
||||
String name = System.getProperty("os.name").toLowerCase();
|
||||
if (name.contains("windows")) {
|
||||
return System.getProperty("os.arch").equals("amd64") ? "win32_x64" : "win32";
|
||||
return "win32_x64";
|
||||
}
|
||||
if (name.contains("linux")) {
|
||||
return "linux";
|
||||
|
||||
+2
-17
@@ -6,13 +6,13 @@
|
||||
<parent>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>parent-pom</artifactId>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.17.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>driver</artifactId>
|
||||
<name>Playwright - Driver</name>
|
||||
<description>
|
||||
This module provides API for discovery and launching of playwright-cli binary.
|
||||
This module provides API for discovery and launching of Playwright driver.
|
||||
</description>
|
||||
|
||||
<build>
|
||||
@@ -24,25 +24,10 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>8</source>
|
||||
<failOnError>false</failOnError>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<redirectTestOutputToFile>true</redirectTestOutputToFile>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>org.example</groupId>
|
||||
<artifactId>examples</artifactId>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.17.2</version>
|
||||
<name>Playwright Client Examples</name>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
@@ -15,7 +15,7 @@
|
||||
<dependency>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>playwright</artifactId>
|
||||
<version>1.11.1</version>
|
||||
<version>1.16.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
|
||||
+19
-11
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>parent-pom</artifactId>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.17.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>playwright</artifactId>
|
||||
@@ -31,17 +31,7 @@
|
||||
<configuration>
|
||||
<subpackages>com.microsoft.playwright</subpackages>
|
||||
<excludePackageNames>com.microsoft.playwright.impl</excludePackageNames>
|
||||
<additionalOptions>--allow-script-in-comments</additionalOptions>
|
||||
<failOnError>false</failOnError>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
@@ -51,7 +41,25 @@
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>test-jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<testResources>
|
||||
<testResource>
|
||||
<directory>src/test/resources</directory>
|
||||
<targetPath>resources</targetPath>
|
||||
</testResource>
|
||||
</testResources>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
||||
@@ -86,7 +86,7 @@ public interface Browser extends AutoCloseable {
|
||||
*/
|
||||
public Double deviceScaleFactor;
|
||||
/**
|
||||
* An object containing additional HTTP headers to be sent with every request. All header values must be strings.
|
||||
* An object containing additional HTTP headers to be sent with every request.
|
||||
*/
|
||||
public Map<String, String> extraHTTPHeaders;
|
||||
/**
|
||||
@@ -107,7 +107,7 @@ public interface Browser extends AutoCloseable {
|
||||
*/
|
||||
public HttpCredentials httpCredentials;
|
||||
/**
|
||||
* Whether to ignore HTTPS errors during navigation. Defaults to {@code false}.
|
||||
* Whether to ignore HTTPS errors when sending network requests. Defaults to {@code false}.
|
||||
*/
|
||||
public Boolean ignoreHTTPSErrors;
|
||||
/**
|
||||
@@ -249,7 +249,7 @@ public interface Browser extends AutoCloseable {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* An object containing additional HTTP headers to be sent with every request. All header values must be strings.
|
||||
* An object containing additional HTTP headers to be sent with every request.
|
||||
*/
|
||||
public NewContextOptions setExtraHTTPHeaders(Map<String, String> extraHTTPHeaders) {
|
||||
this.extraHTTPHeaders = extraHTTPHeaders;
|
||||
@@ -294,7 +294,7 @@ public interface Browser extends AutoCloseable {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Whether to ignore HTTPS errors during navigation. Defaults to {@code false}.
|
||||
* Whether to ignore HTTPS errors when sending network requests. Defaults to {@code false}.
|
||||
*/
|
||||
public NewContextOptions setIgnoreHTTPSErrors(boolean ignoreHTTPSErrors) {
|
||||
this.ignoreHTTPSErrors = ignoreHTTPSErrors;
|
||||
@@ -510,7 +510,7 @@ public interface Browser extends AutoCloseable {
|
||||
*/
|
||||
public Double deviceScaleFactor;
|
||||
/**
|
||||
* An object containing additional HTTP headers to be sent with every request. All header values must be strings.
|
||||
* An object containing additional HTTP headers to be sent with every request.
|
||||
*/
|
||||
public Map<String, String> extraHTTPHeaders;
|
||||
/**
|
||||
@@ -531,7 +531,7 @@ public interface Browser extends AutoCloseable {
|
||||
*/
|
||||
public HttpCredentials httpCredentials;
|
||||
/**
|
||||
* Whether to ignore HTTPS errors during navigation. Defaults to {@code false}.
|
||||
* Whether to ignore HTTPS errors when sending network requests. Defaults to {@code false}.
|
||||
*/
|
||||
public Boolean ignoreHTTPSErrors;
|
||||
/**
|
||||
@@ -673,7 +673,7 @@ public interface Browser extends AutoCloseable {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* An object containing additional HTTP headers to be sent with every request. All header values must be strings.
|
||||
* An object containing additional HTTP headers to be sent with every request.
|
||||
*/
|
||||
public NewPageOptions setExtraHTTPHeaders(Map<String, String> extraHTTPHeaders) {
|
||||
this.extraHTTPHeaders = extraHTTPHeaders;
|
||||
@@ -718,7 +718,7 @@ public interface Browser extends AutoCloseable {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Whether to ignore HTTPS errors during navigation. Defaults to {@code false}.
|
||||
* Whether to ignore HTTPS errors when sending network requests. Defaults to {@code false}.
|
||||
*/
|
||||
public NewPageOptions setIgnoreHTTPSErrors(boolean ignoreHTTPSErrors) {
|
||||
this.ignoreHTTPSErrors = ignoreHTTPSErrors;
|
||||
|
||||
@@ -29,8 +29,8 @@ import java.util.regex.Pattern;
|
||||
* <p> If a page opens another page, e.g. with a {@code window.open} call, the popup will belong to the parent page's browser
|
||||
* context.
|
||||
*
|
||||
* <p> Playwright allows creation of "incognito" browser contexts with {@code browser.newContext()} method. "Incognito" browser
|
||||
* contexts don't write any browsing data to disk.
|
||||
* <p> Playwright allows creating "incognito" browser contexts with {@link Browser#newContext Browser.newContext()} method.
|
||||
* "Incognito" browser contexts don't write any browsing data to disk.
|
||||
* <pre>{@code
|
||||
* // Create a new incognito browser context
|
||||
* BrowserContext context = browser.newContext();
|
||||
|
||||
@@ -435,7 +435,7 @@ public interface BrowserType {
|
||||
*/
|
||||
public Path executablePath;
|
||||
/**
|
||||
* An object containing additional HTTP headers to be sent with every request. All header values must be strings.
|
||||
* An object containing additional HTTP headers to be sent with every request.
|
||||
*/
|
||||
public Map<String, String> extraHTTPHeaders;
|
||||
/**
|
||||
@@ -485,7 +485,7 @@ public interface BrowserType {
|
||||
*/
|
||||
public List<String> ignoreDefaultArgs;
|
||||
/**
|
||||
* Whether to ignore HTTPS errors during navigation. Defaults to {@code false}.
|
||||
* Whether to ignore HTTPS errors when sending network requests. Defaults to {@code false}.
|
||||
*/
|
||||
public Boolean ignoreHTTPSErrors;
|
||||
/**
|
||||
@@ -692,7 +692,7 @@ public interface BrowserType {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* An object containing additional HTTP headers to be sent with every request. All header values must be strings.
|
||||
* An object containing additional HTTP headers to be sent with every request.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setExtraHTTPHeaders(Map<String, String> extraHTTPHeaders) {
|
||||
this.extraHTTPHeaders = extraHTTPHeaders;
|
||||
@@ -784,7 +784,7 @@ public interface BrowserType {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Whether to ignore HTTPS errors during navigation. Defaults to {@code false}.
|
||||
* Whether to ignore HTTPS errors when sending network requests. Defaults to {@code false}.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setIgnoreHTTPSErrors(boolean ignoreHTTPSErrors) {
|
||||
this.ignoreHTTPSErrors = ignoreHTTPSErrors;
|
||||
@@ -1079,7 +1079,8 @@ public interface BrowserType {
|
||||
* @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://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}.
|
||||
* 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.
|
||||
*/
|
||||
default BrowserContext launchPersistentContext(Path userDataDir) {
|
||||
return launchPersistentContext(userDataDir, null);
|
||||
@@ -1093,7 +1094,8 @@ public interface BrowserType {
|
||||
* @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://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}.
|
||||
* 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.
|
||||
*/
|
||||
BrowserContext launchPersistentContext(Path userDataDir, LaunchPersistentContextOptions options);
|
||||
/**
|
||||
|
||||
@@ -23,6 +23,8 @@ import java.util.*;
|
||||
/**
|
||||
* ElementHandle represents an in-page DOM element. ElementHandles can be created with the {@link Page#querySelector
|
||||
* Page.querySelector()} method.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> The use of ElementHandle is discouraged, use {@code Locator} objects and web-first assertions instead.
|
||||
* <pre>{@code
|
||||
* ElementHandle hrefElement = page.querySelector("a");
|
||||
* hrefElement.click();
|
||||
@@ -34,10 +36,6 @@ import java.util.*;
|
||||
* <p> ElementHandle instances can be used as an argument in {@link Page#evalOnSelector Page.evalOnSelector()} and {@link
|
||||
* Page#evaluate Page.evaluate()} methods.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> In most cases, you would want to use the {@code Locator} object instead. You should only use {@code ElementHandle} if you want to
|
||||
* retain a handle to a particular DOM Node that you intend to pass into {@link Page#evaluate Page.evaluate()} as an
|
||||
* argument.
|
||||
*
|
||||
* <p> The difference between the {@code Locator} and ElementHandle is that the ElementHandle points to a particular element, while
|
||||
* {@code Locator} captures the logic of how to retrieve an element.
|
||||
*
|
||||
|
||||
@@ -813,6 +813,7 @@ public interface Frame {
|
||||
* <li> {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.</li>
|
||||
* <li> {@code "load"} - consider operation to be finished when the {@code load} event is fired.</li>
|
||||
* <li> {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} ms.</li>
|
||||
* <li> {@code "commit"} - consider operation to be finished when network response is received and the document started loading.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public WaitUntilState waitUntil;
|
||||
@@ -841,6 +842,7 @@ public interface Frame {
|
||||
* <li> {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.</li>
|
||||
* <li> {@code "load"} - consider operation to be finished when the {@code load} event is fired.</li>
|
||||
* <li> {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} ms.</li>
|
||||
* <li> {@code "commit"} - consider operation to be finished when network response is received and the document started loading.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public NavigateOptions setWaitUntil(WaitUntilState waitUntil) {
|
||||
@@ -1455,6 +1457,7 @@ public interface Frame {
|
||||
* <li> {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.</li>
|
||||
* <li> {@code "load"} - consider operation to be finished when the {@code load} event is fired.</li>
|
||||
* <li> {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} ms.</li>
|
||||
* <li> {@code "commit"} - consider operation to be finished when network response is received and the document started loading.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public WaitUntilState waitUntil;
|
||||
@@ -1475,6 +1478,7 @@ public interface Frame {
|
||||
* <li> {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.</li>
|
||||
* <li> {@code "load"} - consider operation to be finished when the {@code load} event is fired.</li>
|
||||
* <li> {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} ms.</li>
|
||||
* <li> {@code "commit"} - consider operation to be finished when network response is received and the document started loading.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public SetContentOptions setWaitUntil(WaitUntilState waitUntil) {
|
||||
@@ -1875,7 +1879,9 @@ public interface Frame {
|
||||
*/
|
||||
public Double timeout;
|
||||
/**
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
public Object url;
|
||||
/**
|
||||
@@ -1884,6 +1890,7 @@ public interface Frame {
|
||||
* <li> {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.</li>
|
||||
* <li> {@code "load"} - consider operation to be finished when the {@code load} event is fired.</li>
|
||||
* <li> {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} ms.</li>
|
||||
* <li> {@code "commit"} - consider operation to be finished when network response is received and the document started loading.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public WaitUntilState waitUntil;
|
||||
@@ -1899,21 +1906,27 @@ public interface Frame {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
public WaitForNavigationOptions setUrl(String url) {
|
||||
this.url = url;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
public WaitForNavigationOptions setUrl(Pattern url) {
|
||||
this.url = url;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
public WaitForNavigationOptions setUrl(Predicate<String> url) {
|
||||
this.url = url;
|
||||
@@ -1925,6 +1938,7 @@ public interface Frame {
|
||||
* <li> {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.</li>
|
||||
* <li> {@code "load"} - consider operation to be finished when the {@code load} event is fired.</li>
|
||||
* <li> {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} ms.</li>
|
||||
* <li> {@code "commit"} - consider operation to be finished when network response is received and the document started loading.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public WaitForNavigationOptions setWaitUntil(WaitUntilState waitUntil) {
|
||||
@@ -2004,6 +2018,7 @@ public interface Frame {
|
||||
* <li> {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.</li>
|
||||
* <li> {@code "load"} - consider operation to be finished when the {@code load} event is fired.</li>
|
||||
* <li> {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} ms.</li>
|
||||
* <li> {@code "commit"} - consider operation to be finished when network response is received and the document started loading.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public WaitUntilState waitUntil;
|
||||
@@ -2024,6 +2039,7 @@ public interface Frame {
|
||||
* <li> {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.</li>
|
||||
* <li> {@code "load"} - consider operation to be finished when the {@code load} event is fired.</li>
|
||||
* <li> {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} ms.</li>
|
||||
* <li> {@code "commit"} - consider operation to be finished when network response is received and the document started loading.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public WaitForURLOptions setWaitUntil(WaitUntilState waitUntil) {
|
||||
@@ -2313,6 +2329,9 @@ public interface Frame {
|
||||
/**
|
||||
* Returns the return value of {@code expression}.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> This method does not wait for the element to pass actionability checks and therefore can lead to the flaky tests. Use
|
||||
* {@link Locator#evaluate Locator.evaluate()}, other {@code Locator} helper methods or web-first assertions instead.
|
||||
*
|
||||
* <p> The method finds an element matching the specified selector within the frame and passes it as a first argument to
|
||||
* {@code expression}. See <a href="https://playwright.dev/java/docs/selectors/">Working with selectors</a> for more details. If
|
||||
* no elements match the selector, the method throws an error.
|
||||
@@ -2340,6 +2359,9 @@ public interface Frame {
|
||||
/**
|
||||
* Returns the return value of {@code expression}.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> This method does not wait for the element to pass actionability checks and therefore can lead to the flaky tests. Use
|
||||
* {@link Locator#evaluate Locator.evaluate()}, other {@code Locator} helper methods or web-first assertions instead.
|
||||
*
|
||||
* <p> The method finds an element matching the specified selector within the frame and passes it as a first argument to
|
||||
* {@code expression}. See <a href="https://playwright.dev/java/docs/selectors/">Working with selectors</a> for more details. If
|
||||
* no elements match the selector, the method throws an error.
|
||||
@@ -2366,6 +2388,9 @@ public interface Frame {
|
||||
/**
|
||||
* Returns the return value of {@code expression}.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> This method does not wait for the element to pass actionability checks and therefore can lead to the flaky tests. Use
|
||||
* {@link Locator#evaluate Locator.evaluate()}, other {@code Locator} helper methods or web-first assertions instead.
|
||||
*
|
||||
* <p> The method finds an element matching the specified selector within the frame and passes it as a first argument to
|
||||
* {@code expression}. See <a href="https://playwright.dev/java/docs/selectors/">Working with selectors</a> for more details. If
|
||||
* no elements match the selector, the method throws an error.
|
||||
@@ -2391,6 +2416,9 @@ public interface Frame {
|
||||
/**
|
||||
* Returns the return value of {@code expression}.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> In most cases, {@link Locator#evaluateAll Locator.evaluateAll()}, other {@code Locator} helper methods and web-first
|
||||
* assertions do a better job.
|
||||
*
|
||||
* <p> The method finds all elements matching the specified selector within the frame and passes an array of matched elements
|
||||
* as a first argument to {@code expression}. See <a href="https://playwright.dev/java/docs/selectors/">Working with
|
||||
* selectors</a> for more details.
|
||||
@@ -2415,6 +2443,9 @@ public interface Frame {
|
||||
/**
|
||||
* Returns the return value of {@code expression}.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> In most cases, {@link Locator#evaluateAll Locator.evaluateAll()}, other {@code Locator} helper methods and web-first
|
||||
* assertions do a better job.
|
||||
*
|
||||
* <p> The method finds all elements matching the specified selector within the frame and passes an array of matched elements
|
||||
* as a first argument to {@code expression}. See <a href="https://playwright.dev/java/docs/selectors/">Working with
|
||||
* selectors</a> for more details.
|
||||
@@ -2459,7 +2490,7 @@ public interface Frame {
|
||||
*
|
||||
* <p> {@code ElementHandle} instances can be passed as an argument to the {@link Frame#evaluate Frame.evaluate()}:
|
||||
* <pre>{@code
|
||||
* ElementHandle bodyHandle = frame.querySelector("body");
|
||||
* ElementHandle bodyHandle = frame.evaluate("document.body");
|
||||
* String html = (String) frame.evaluate("([body, suffix]) => body.innerHTML + suffix", Arrays.asList(bodyHandle, "hello"));
|
||||
* bodyHandle.dispose();
|
||||
* }</pre>
|
||||
@@ -2494,7 +2525,7 @@ public interface Frame {
|
||||
*
|
||||
* <p> {@code ElementHandle} instances can be passed as an argument to the {@link Frame#evaluate Frame.evaluate()}:
|
||||
* <pre>{@code
|
||||
* ElementHandle bodyHandle = frame.querySelector("body");
|
||||
* ElementHandle bodyHandle = frame.evaluate("document.body");
|
||||
* String html = (String) frame.evaluate("([body, suffix]) => body.innerHTML + suffix", Arrays.asList(bodyHandle, "hello"));
|
||||
* bodyHandle.dispose();
|
||||
* }</pre>
|
||||
@@ -2637,6 +2668,19 @@ public interface Frame {
|
||||
* }</pre>
|
||||
*/
|
||||
ElementHandle frameElement();
|
||||
/**
|
||||
* When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements in
|
||||
* that iframe. Following snippet locates element with text "Submit" in the iframe with id {@code my-frame}, like {@code <iframe
|
||||
* id="my-frame">}:
|
||||
* <pre>{@code
|
||||
* Locator locator = frame.frameLocator("#my-iframe").locator("text=Submit");
|
||||
* locator.click();
|
||||
* }</pre>
|
||||
*
|
||||
* @param selector A selector to use when resolving DOM element. See <a href="https://playwright.dev/java/docs/selectors/">working with
|
||||
* selectors</a> for more details.
|
||||
*/
|
||||
FrameLocator frameLocator(String selector);
|
||||
/**
|
||||
* Returns element attribute value.
|
||||
*
|
||||
@@ -2981,6 +3025,8 @@ public interface Frame {
|
||||
/**
|
||||
* Returns the ElementHandle pointing to the frame element.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> The use of {@code ElementHandle} is discouraged, use {@code Locator} objects and web-first assertions instead.
|
||||
*
|
||||
* <p> The method finds an element matching the specified selector within the frame. See <a
|
||||
* href="https://playwright.dev/java/docs/selectors/">Working with selectors</a> for more details. If no elements match the
|
||||
* selector, returns {@code null}.
|
||||
@@ -2994,6 +3040,8 @@ public interface Frame {
|
||||
/**
|
||||
* Returns the ElementHandle pointing to the frame element.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> The use of {@code ElementHandle} is discouraged, use {@code Locator} objects and web-first assertions instead.
|
||||
*
|
||||
* <p> The method finds an element matching the specified selector within the frame. See <a
|
||||
* href="https://playwright.dev/java/docs/selectors/">Working with selectors</a> for more details. If no elements match the
|
||||
* selector, returns {@code null}.
|
||||
@@ -3005,6 +3053,8 @@ public interface Frame {
|
||||
/**
|
||||
* Returns the ElementHandles pointing to the frame elements.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> The use of {@code ElementHandle} is discouraged, use {@code Locator} objects instead.
|
||||
*
|
||||
* <p> The method finds all elements matching the specified selector within the frame. See <a
|
||||
* href="https://playwright.dev/java/docs/selectors/">Working with selectors</a> for more details. If no elements match the
|
||||
* selector, returns empty array.
|
||||
@@ -3875,6 +3925,9 @@ public interface Frame {
|
||||
* Returns when element specified by selector satisfies {@code state} option. Returns {@code null} if waiting for {@code hidden} or
|
||||
* {@code detached}.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Playwright automatically waits for element to be ready before performing an action. Using {@code Locator} objects and
|
||||
* web-first assertions make the code wait-for-selector-free.
|
||||
*
|
||||
* <p> Wait for the {@code selector} to satisfy {@code state} option (either appear/disappear from dom, or become visible/hidden). If at
|
||||
* the moment of calling the method {@code selector} already satisfies the condition, the method will return immediately. If the
|
||||
* selector doesn't satisfy the condition for the {@code timeout} milliseconds, the function will throw.
|
||||
@@ -3910,6 +3963,9 @@ public interface Frame {
|
||||
* Returns when element specified by selector satisfies {@code state} option. Returns {@code null} if waiting for {@code hidden} or
|
||||
* {@code detached}.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Playwright automatically waits for element to be ready before performing an action. Using {@code Locator} objects and
|
||||
* web-first assertions make the code wait-for-selector-free.
|
||||
*
|
||||
* <p> Wait for the {@code selector} to satisfy {@code state} option (either appear/disappear from dom, or become visible/hidden). If at
|
||||
* the moment of calling the method {@code selector} already satisfies the condition, the method will return immediately. If the
|
||||
* selector doesn't satisfy the condition for the {@code timeout} milliseconds, the function will throw.
|
||||
@@ -3955,7 +4011,9 @@ public interface Frame {
|
||||
* frame.waitForURL("**\/target.html");
|
||||
* }</pre>
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
default void waitForURL(String url) {
|
||||
waitForURL(url, null);
|
||||
@@ -3967,7 +4025,9 @@ public interface Frame {
|
||||
* frame.waitForURL("**\/target.html");
|
||||
* }</pre>
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
void waitForURL(String url, WaitForURLOptions options);
|
||||
/**
|
||||
@@ -3977,7 +4037,9 @@ public interface Frame {
|
||||
* frame.waitForURL("**\/target.html");
|
||||
* }</pre>
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
default void waitForURL(Pattern url) {
|
||||
waitForURL(url, null);
|
||||
@@ -3989,7 +4051,9 @@ public interface Frame {
|
||||
* frame.waitForURL("**\/target.html");
|
||||
* }</pre>
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
void waitForURL(Pattern url, WaitForURLOptions options);
|
||||
/**
|
||||
@@ -3999,7 +4063,9 @@ public interface Frame {
|
||||
* frame.waitForURL("**\/target.html");
|
||||
* }</pre>
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
default void waitForURL(Predicate<String> url) {
|
||||
waitForURL(url, null);
|
||||
@@ -4011,7 +4077,9 @@ public interface Frame {
|
||||
* frame.waitForURL("**\/target.html");
|
||||
* }</pre>
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
void waitForURL(Predicate<String> url, WaitForURLOptions options);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* FrameLocator represents a view to the {@code iframe} on the page. It captures the logic sufficient to retrieve the {@code iframe}
|
||||
* and locate elements in that iframe. FrameLocator can be created with either {@link Page#frameLocator
|
||||
* Page.frameLocator()} or {@link Locator#frameLocator Locator.frameLocator()} method.
|
||||
* <pre>{@code
|
||||
* Locator locator = page.frameLocator("#my-frame").locator("text=Submit");
|
||||
* locator.click();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> **Strictness**
|
||||
*
|
||||
* <p> Frame locators are strict. This means that all operations on frame locators will throw if more than one element matches
|
||||
* given selector.
|
||||
* <pre>{@code
|
||||
* // Throws if there are several frames in DOM:
|
||||
* page.frame_locator(".result-frame").locator("button").click();
|
||||
*
|
||||
* // Works because we explicitly tell locator to pick the first frame:
|
||||
* page.frame_locator(".result-frame").first().locator("button").click();
|
||||
* }</pre>
|
||||
*/
|
||||
public interface FrameLocator {
|
||||
/**
|
||||
* Returns locator to the first matching frame.
|
||||
*/
|
||||
FrameLocator first();
|
||||
/**
|
||||
* When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements in
|
||||
* that iframe.
|
||||
*
|
||||
* @param selector A selector to use when resolving DOM element. See <a href="https://playwright.dev/java/docs/selectors/">working with
|
||||
* selectors</a> for more details.
|
||||
*/
|
||||
FrameLocator frameLocator(String selector);
|
||||
/**
|
||||
* Returns locator to the last matching frame.
|
||||
*/
|
||||
FrameLocator last();
|
||||
/**
|
||||
* The method finds an element matching the specified selector in the FrameLocator's subtree.
|
||||
*
|
||||
* @param selector A selector to use when resolving DOM element. See <a href="https://playwright.dev/java/docs/selectors/">working with
|
||||
* selectors</a> for more details.
|
||||
*/
|
||||
Locator locator(String selector);
|
||||
/**
|
||||
* Returns locator to the n-th matching frame.
|
||||
*/
|
||||
FrameLocator nth(int index);
|
||||
}
|
||||
|
||||
@@ -1355,6 +1355,51 @@ public interface Locator {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class WaitForOptions {
|
||||
/**
|
||||
* Defaults to {@code "visible"}. Can be either:
|
||||
* <ul>
|
||||
* <li> {@code "attached"} - wait for element to be present in DOM.</li>
|
||||
* <li> {@code "detached"} - wait for element to not be present in DOM.</li>
|
||||
* <li> {@code "visible"} - wait for element to have non-empty bounding box and no {@code visibility:hidden}. Note that element without any
|
||||
* content or with {@code display:none} has an empty bounding box and is not considered visible.</li>
|
||||
* <li> {@code "hidden"} - wait for element to be either detached from DOM, or have an empty bounding box or {@code visibility:hidden}. This
|
||||
* is opposite to the {@code "visible"} option.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public WaitForSelectorState state;
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
/**
|
||||
* Defaults to {@code "visible"}. Can be either:
|
||||
* <ul>
|
||||
* <li> {@code "attached"} - wait for element to be present in DOM.</li>
|
||||
* <li> {@code "detached"} - wait for element to not be present in DOM.</li>
|
||||
* <li> {@code "visible"} - wait for element to have non-empty bounding box and no {@code visibility:hidden}. Note that element without any
|
||||
* content or with {@code display:none} has an empty bounding box and is not considered visible.</li>
|
||||
* <li> {@code "hidden"} - wait for element to be either detached from DOM, or have an empty bounding box or {@code visibility:hidden}. This
|
||||
* is opposite to the {@code "visible"} option.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public WaitForOptions setState(WaitForSelectorState state) {
|
||||
this.state = state;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public WaitForOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns an array of {@code node.innerText} values for all matching nodes.
|
||||
*/
|
||||
@@ -1853,6 +1898,18 @@ public interface Locator {
|
||||
* Calls <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus">focus</a> on the element.
|
||||
*/
|
||||
void focus(FocusOptions options);
|
||||
/**
|
||||
* When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements in
|
||||
* that iframe:
|
||||
* <pre>{@code
|
||||
* Locator locator = page.frameLocator("iframe").locator("text=Submit");
|
||||
* locator.click();
|
||||
* }</pre>
|
||||
*
|
||||
* @param selector A selector to use when resolving DOM element. See <a href="https://playwright.dev/java/docs/selectors/">working with
|
||||
* selectors</a> for more details.
|
||||
*/
|
||||
FrameLocator frameLocator(String selector);
|
||||
/**
|
||||
* Returns element attribute value.
|
||||
*
|
||||
@@ -2000,8 +2057,7 @@ public interface Locator {
|
||||
*/
|
||||
Locator last();
|
||||
/**
|
||||
* The method finds an element matching the specified selector in the {@code Locator}'s subtree. See <a
|
||||
* href="https://playwright.dev/java/docs/selectors/">Working with selectors</a> for more details.
|
||||
* The method finds an element matching the specified selector in the {@code Locator}'s subtree.
|
||||
*
|
||||
* @param selector A selector to use when resolving DOM element. See <a href="https://playwright.dev/java/docs/selectors/">working with
|
||||
* selectors</a> for more details.
|
||||
@@ -2667,5 +2723,29 @@ public interface Locator {
|
||||
* zero timeout disables this.
|
||||
*/
|
||||
void uncheck(UncheckOptions options);
|
||||
/**
|
||||
* Returns when element specified by locator satisfies the {@code state} option.
|
||||
*
|
||||
* <p> If target element already satisfies the condition, the method returns immediately. Otherwise, waits for up to {@code timeout}
|
||||
* milliseconds until the condition is met.
|
||||
* <pre>{@code
|
||||
* Locator orderSent = page.locator("#order-sent");
|
||||
* orderSent.waitFor();
|
||||
* }</pre>
|
||||
*/
|
||||
default void waitFor() {
|
||||
waitFor(null);
|
||||
}
|
||||
/**
|
||||
* Returns when element specified by locator satisfies the {@code state} option.
|
||||
*
|
||||
* <p> If target element already satisfies the condition, the method returns immediately. Otherwise, waits for up to {@code timeout}
|
||||
* milliseconds until the condition is met.
|
||||
* <pre>{@code
|
||||
* Locator orderSent = page.locator("#order-sent");
|
||||
* orderSent.waitFor();
|
||||
* }</pre>
|
||||
*/
|
||||
void waitFor(WaitForOptions options);
|
||||
}
|
||||
|
||||
|
||||
@@ -1147,6 +1147,7 @@ public interface Page extends AutoCloseable {
|
||||
* <li> {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.</li>
|
||||
* <li> {@code "load"} - consider operation to be finished when the {@code load} event is fired.</li>
|
||||
* <li> {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} ms.</li>
|
||||
* <li> {@code "commit"} - consider operation to be finished when network response is received and the document started loading.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public WaitUntilState waitUntil;
|
||||
@@ -1167,6 +1168,7 @@ public interface Page extends AutoCloseable {
|
||||
* <li> {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.</li>
|
||||
* <li> {@code "load"} - consider operation to be finished when the {@code load} event is fired.</li>
|
||||
* <li> {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} ms.</li>
|
||||
* <li> {@code "commit"} - consider operation to be finished when network response is received and the document started loading.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public GoBackOptions setWaitUntil(WaitUntilState waitUntil) {
|
||||
@@ -1188,6 +1190,7 @@ public interface Page extends AutoCloseable {
|
||||
* <li> {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.</li>
|
||||
* <li> {@code "load"} - consider operation to be finished when the {@code load} event is fired.</li>
|
||||
* <li> {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} ms.</li>
|
||||
* <li> {@code "commit"} - consider operation to be finished when network response is received and the document started loading.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public WaitUntilState waitUntil;
|
||||
@@ -1208,6 +1211,7 @@ public interface Page extends AutoCloseable {
|
||||
* <li> {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.</li>
|
||||
* <li> {@code "load"} - consider operation to be finished when the {@code load} event is fired.</li>
|
||||
* <li> {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} ms.</li>
|
||||
* <li> {@code "commit"} - consider operation to be finished when network response is received and the document started loading.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public GoForwardOptions setWaitUntil(WaitUntilState waitUntil) {
|
||||
@@ -1234,6 +1238,7 @@ public interface Page extends AutoCloseable {
|
||||
* <li> {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.</li>
|
||||
* <li> {@code "load"} - consider operation to be finished when the {@code load} event is fired.</li>
|
||||
* <li> {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} ms.</li>
|
||||
* <li> {@code "commit"} - consider operation to be finished when network response is received and the document started loading.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public WaitUntilState waitUntil;
|
||||
@@ -1262,6 +1267,7 @@ public interface Page extends AutoCloseable {
|
||||
* <li> {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.</li>
|
||||
* <li> {@code "load"} - consider operation to be finished when the {@code load} event is fired.</li>
|
||||
* <li> {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} ms.</li>
|
||||
* <li> {@code "commit"} - consider operation to be finished when network response is received and the document started loading.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public NavigateOptions setWaitUntil(WaitUntilState waitUntil) {
|
||||
@@ -1889,6 +1895,7 @@ public interface Page extends AutoCloseable {
|
||||
* <li> {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.</li>
|
||||
* <li> {@code "load"} - consider operation to be finished when the {@code load} event is fired.</li>
|
||||
* <li> {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} ms.</li>
|
||||
* <li> {@code "commit"} - consider operation to be finished when network response is received and the document started loading.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public WaitUntilState waitUntil;
|
||||
@@ -1909,6 +1916,7 @@ public interface Page extends AutoCloseable {
|
||||
* <li> {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.</li>
|
||||
* <li> {@code "load"} - consider operation to be finished when the {@code load} event is fired.</li>
|
||||
* <li> {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} ms.</li>
|
||||
* <li> {@code "commit"} - consider operation to be finished when network response is received and the document started loading.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public ReloadOptions setWaitUntil(WaitUntilState waitUntil) {
|
||||
@@ -2195,6 +2203,7 @@ public interface Page extends AutoCloseable {
|
||||
* <li> {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.</li>
|
||||
* <li> {@code "load"} - consider operation to be finished when the {@code load} event is fired.</li>
|
||||
* <li> {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} ms.</li>
|
||||
* <li> {@code "commit"} - consider operation to be finished when network response is received and the document started loading.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public WaitUntilState waitUntil;
|
||||
@@ -2215,6 +2224,7 @@ public interface Page extends AutoCloseable {
|
||||
* <li> {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.</li>
|
||||
* <li> {@code "load"} - consider operation to be finished when the {@code load} event is fired.</li>
|
||||
* <li> {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} ms.</li>
|
||||
* <li> {@code "commit"} - consider operation to be finished when network response is received and the document started loading.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public SetContentOptions setWaitUntil(WaitUntilState waitUntil) {
|
||||
@@ -2712,7 +2722,9 @@ public interface Page extends AutoCloseable {
|
||||
*/
|
||||
public Double timeout;
|
||||
/**
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
public Object url;
|
||||
/**
|
||||
@@ -2721,6 +2733,7 @@ public interface Page extends AutoCloseable {
|
||||
* <li> {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.</li>
|
||||
* <li> {@code "load"} - consider operation to be finished when the {@code load} event is fired.</li>
|
||||
* <li> {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} ms.</li>
|
||||
* <li> {@code "commit"} - consider operation to be finished when network response is received and the document started loading.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public WaitUntilState waitUntil;
|
||||
@@ -2736,21 +2749,27 @@ public interface Page extends AutoCloseable {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
public WaitForNavigationOptions setUrl(String url) {
|
||||
this.url = url;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
public WaitForNavigationOptions setUrl(Pattern url) {
|
||||
this.url = url;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
* A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
public WaitForNavigationOptions setUrl(Predicate<String> url) {
|
||||
this.url = url;
|
||||
@@ -2762,6 +2781,7 @@ public interface Page extends AutoCloseable {
|
||||
* <li> {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.</li>
|
||||
* <li> {@code "load"} - consider operation to be finished when the {@code load} event is fired.</li>
|
||||
* <li> {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} ms.</li>
|
||||
* <li> {@code "commit"} - consider operation to be finished when network response is received and the document started loading.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public WaitForNavigationOptions setWaitUntil(WaitUntilState waitUntil) {
|
||||
@@ -2929,6 +2949,7 @@ public interface Page extends AutoCloseable {
|
||||
* <li> {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.</li>
|
||||
* <li> {@code "load"} - consider operation to be finished when the {@code load} event is fired.</li>
|
||||
* <li> {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} ms.</li>
|
||||
* <li> {@code "commit"} - consider operation to be finished when network response is received and the document started loading.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public WaitUntilState waitUntil;
|
||||
@@ -2949,6 +2970,7 @@ public interface Page extends AutoCloseable {
|
||||
* <li> {@code "domcontentloaded"} - consider operation to be finished when the {@code DOMContentLoaded} event is fired.</li>
|
||||
* <li> {@code "load"} - consider operation to be finished when the {@code load} event is fired.</li>
|
||||
* <li> {@code "networkidle"} - consider operation to be finished when there are no network connections for at least {@code 500} ms.</li>
|
||||
* <li> {@code "commit"} - consider operation to be finished when network response is received and the document started loading.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public WaitForURLOptions setWaitUntil(WaitUntilState waitUntil) {
|
||||
@@ -3445,7 +3467,10 @@ public interface Page extends AutoCloseable {
|
||||
*/
|
||||
void emulateMedia(EmulateMediaOptions options);
|
||||
/**
|
||||
* The method finds an element matching the specified selector within the page and passes it as a first argument to
|
||||
* <strong>NOTE:</strong> This method does not wait for the element to pass actionability checks and therefore can lead to the flaky tests. Use
|
||||
* {@link Locator#evaluate Locator.evaluate()}, other {@code Locator} helper methods or web-first assertions instead.
|
||||
*
|
||||
* <p> The method finds an element matching the specified selector within the page and passes it as a first argument to
|
||||
* {@code expression}. If no elements match the selector, the method throws an error. Returns the value of {@code expression}.
|
||||
*
|
||||
* <p> If {@code expression} returns a <a
|
||||
@@ -3471,7 +3496,10 @@ public interface Page extends AutoCloseable {
|
||||
return evalOnSelector(selector, expression, arg, null);
|
||||
}
|
||||
/**
|
||||
* The method finds an element matching the specified selector within the page and passes it as a first argument to
|
||||
* <strong>NOTE:</strong> This method does not wait for the element to pass actionability checks and therefore can lead to the flaky tests. Use
|
||||
* {@link Locator#evaluate Locator.evaluate()}, other {@code Locator} helper methods or web-first assertions instead.
|
||||
*
|
||||
* <p> The method finds an element matching the specified selector within the page and passes it as a first argument to
|
||||
* {@code expression}. If no elements match the selector, the method throws an error. Returns the value of {@code expression}.
|
||||
*
|
||||
* <p> If {@code expression} returns a <a
|
||||
@@ -3496,7 +3524,10 @@ public interface Page extends AutoCloseable {
|
||||
return evalOnSelector(selector, expression, null);
|
||||
}
|
||||
/**
|
||||
* The method finds an element matching the specified selector within the page and passes it as a first argument to
|
||||
* <strong>NOTE:</strong> This method does not wait for the element to pass actionability checks and therefore can lead to the flaky tests. Use
|
||||
* {@link Locator#evaluate Locator.evaluate()}, other {@code Locator} helper methods or web-first assertions instead.
|
||||
*
|
||||
* <p> The method finds an element matching the specified selector within the page and passes it as a first argument to
|
||||
* {@code expression}. If no elements match the selector, the method throws an error. Returns the value of {@code expression}.
|
||||
*
|
||||
* <p> If {@code expression} returns a <a
|
||||
@@ -3520,7 +3551,10 @@ public interface Page extends AutoCloseable {
|
||||
*/
|
||||
Object evalOnSelector(String selector, String expression, Object arg, EvalOnSelectorOptions options);
|
||||
/**
|
||||
* The method finds all elements matching the specified selector within the page and passes an array of matched elements as
|
||||
* <strong>NOTE:</strong> In most cases, {@link Locator#evaluateAll Locator.evaluateAll()}, other {@code Locator} helper methods and web-first
|
||||
* assertions do a better job.
|
||||
*
|
||||
* <p> The method finds all elements matching the specified selector within the page and passes an array of matched elements as
|
||||
* a first argument to {@code expression}. Returns the result of {@code expression} invocation.
|
||||
*
|
||||
* <p> If {@code expression} returns a <a
|
||||
@@ -3541,7 +3575,10 @@ public interface Page extends AutoCloseable {
|
||||
return evalOnSelectorAll(selector, expression, null);
|
||||
}
|
||||
/**
|
||||
* The method finds all elements matching the specified selector within the page and passes an array of matched elements as
|
||||
* <strong>NOTE:</strong> In most cases, {@link Locator#evaluateAll Locator.evaluateAll()}, other {@code Locator} helper methods and web-first
|
||||
* assertions do a better job.
|
||||
*
|
||||
* <p> The method finds all elements matching the specified selector within the page and passes an array of matched elements as
|
||||
* a first argument to {@code expression}. Returns the result of {@code expression} invocation.
|
||||
*
|
||||
* <p> If {@code expression} returns a <a
|
||||
@@ -3586,7 +3623,7 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> {@code ElementHandle} instances can be passed as an argument to the {@link Page#evaluate Page.evaluate()}:
|
||||
* <pre>{@code
|
||||
* ElementHandle bodyHandle = page.querySelector("body");
|
||||
* ElementHandle bodyHandle = page.evaluate("document.body");
|
||||
* String html = (String) page.evaluate("([body, suffix]) => body.innerHTML + suffix", Arrays.asList(bodyHandle, "hello"));
|
||||
* bodyHandle.dispose();
|
||||
* }</pre>
|
||||
@@ -3625,7 +3662,7 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> {@code ElementHandle} instances can be passed as an argument to the {@link Page#evaluate Page.evaluate()}:
|
||||
* <pre>{@code
|
||||
* ElementHandle bodyHandle = page.querySelector("body");
|
||||
* ElementHandle bodyHandle = page.evaluate("document.body");
|
||||
* String html = (String) page.evaluate("([body, suffix]) => body.innerHTML + suffix", Arrays.asList(bodyHandle, "hello"));
|
||||
* bodyHandle.dispose();
|
||||
* }</pre>
|
||||
@@ -3971,6 +4008,19 @@ public interface Page extends AutoCloseable {
|
||||
* @param url A glob pattern, regex pattern or predicate receiving frame's {@code url} as a [URL] object.
|
||||
*/
|
||||
Frame frameByUrl(Predicate<String> url);
|
||||
/**
|
||||
* When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements in
|
||||
* that iframe. Following snippet locates element with text "Submit" in the iframe with id {@code my-frame}, like {@code <iframe
|
||||
* id="my-frame">}:
|
||||
* <pre>{@code
|
||||
* Locator locator = page.frameLocator("#my-iframe").locator("text=Submit");
|
||||
* locator.click();
|
||||
* }</pre>
|
||||
*
|
||||
* @param selector A selector to use when resolving DOM element. See <a href="https://playwright.dev/java/docs/selectors/">working with
|
||||
* selectors</a> for more details.
|
||||
*/
|
||||
FrameLocator frameLocator(String selector);
|
||||
/**
|
||||
* An array of all frames attached to the page.
|
||||
*/
|
||||
@@ -4500,9 +4550,10 @@ public interface Page extends AutoCloseable {
|
||||
*/
|
||||
void press(String selector, String key, PressOptions options);
|
||||
/**
|
||||
* The method finds an element matching the specified selector within the page. If no elements match the selector, the
|
||||
* return value resolves to {@code null}. To wait for an element on the page, use {@link Page#waitForSelector
|
||||
* Page.waitForSelector()}.
|
||||
* <strong>NOTE:</strong> The use of {@code ElementHandle} is discouraged, use {@code Locator} objects and web-first assertions instead.
|
||||
*
|
||||
* <p> The method finds an element matching the specified selector within the page. If no elements match the selector, the
|
||||
* return value resolves to {@code null}. To wait for an element on the page, use {@link Locator#waitFor Locator.waitFor()}.
|
||||
*
|
||||
* <p> Shortcut for main frame's {@link Frame#querySelector Frame.querySelector()}.
|
||||
*
|
||||
@@ -4513,9 +4564,10 @@ public interface Page extends AutoCloseable {
|
||||
return querySelector(selector, null);
|
||||
}
|
||||
/**
|
||||
* The method finds an element matching the specified selector within the page. If no elements match the selector, the
|
||||
* return value resolves to {@code null}. To wait for an element on the page, use {@link Page#waitForSelector
|
||||
* Page.waitForSelector()}.
|
||||
* <strong>NOTE:</strong> The use of {@code ElementHandle} is discouraged, use {@code Locator} objects and web-first assertions instead.
|
||||
*
|
||||
* <p> The method finds an element matching the specified selector within the page. If no elements match the selector, the
|
||||
* return value resolves to {@code null}. To wait for an element on the page, use {@link Locator#waitFor Locator.waitFor()}.
|
||||
*
|
||||
* <p> Shortcut for main frame's {@link Frame#querySelector Frame.querySelector()}.
|
||||
*
|
||||
@@ -4524,7 +4576,9 @@ public interface Page extends AutoCloseable {
|
||||
*/
|
||||
ElementHandle querySelector(String selector, QuerySelectorOptions options);
|
||||
/**
|
||||
* The method finds all elements matching the specified selector within the page. If no elements match the selector, the
|
||||
* <strong>NOTE:</strong> The use of {@code ElementHandle} is discouraged, use {@code Locator} objects and web-first assertions instead.
|
||||
*
|
||||
* <p> The method finds all elements matching the specified selector within the page. If no elements match the selector, the
|
||||
* return value resolves to {@code []}.
|
||||
*
|
||||
* <p> Shortcut for main frame's {@link Frame#querySelectorAll Frame.querySelectorAll()}.
|
||||
@@ -4534,15 +4588,15 @@ public interface Page extends AutoCloseable {
|
||||
*/
|
||||
List<ElementHandle> querySelectorAll(String selector);
|
||||
/**
|
||||
* Returns the main resource response. In case of multiple redirects, the navigation will resolve with the response of the
|
||||
* last redirect.
|
||||
* This method reloads the current page, in the same way as if the user had triggered a browser refresh. Returns the main
|
||||
* resource response. In case of multiple redirects, the navigation will resolve with the response of the last redirect.
|
||||
*/
|
||||
default Response reload() {
|
||||
return reload(null);
|
||||
}
|
||||
/**
|
||||
* Returns the main resource response. In case of multiple redirects, the navigation will resolve with the response of the
|
||||
* last redirect.
|
||||
* This method reloads the current page, in the same way as if the user had triggered a browser refresh. Returns the main
|
||||
* resource response. In case of multiple redirects, the navigation will resolve with the response of the last redirect.
|
||||
*/
|
||||
Response reload(ReloadOptions options);
|
||||
/**
|
||||
@@ -5454,7 +5508,9 @@ public interface Page extends AutoCloseable {
|
||||
* Browser#newContext Browser.newContext()} allows to set viewport size (and more) for all pages in the context at once.
|
||||
*
|
||||
* <p> {@code page.setViewportSize} will resize the page. A lot of websites don't expect phones to change size, so you should set the
|
||||
* viewport size before navigating to the page.
|
||||
* viewport size before navigating to the page. {@link Page#setViewportSize Page.setViewportSize()} will also reset
|
||||
* {@code screen} size, use {@link Browser#newContext Browser.newContext()} with {@code screen} and {@code viewport} parameters if you need
|
||||
* better control of these properties.
|
||||
* <pre>{@code
|
||||
* Page page = browser.newPage();
|
||||
* page.setViewportSize(640, 480);
|
||||
@@ -6306,6 +6362,9 @@ public interface Page extends AutoCloseable {
|
||||
* Returns when element specified by selector satisfies {@code state} option. Returns {@code null} if waiting for {@code hidden} or
|
||||
* {@code detached}.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Playwright automatically waits for element to be ready before performing an action. Using {@code Locator} objects and
|
||||
* web-first assertions make the code wait-for-selector-free.
|
||||
*
|
||||
* <p> Wait for the {@code selector} to satisfy {@code state} option (either appear/disappear from dom, or become visible/hidden). If at
|
||||
* the moment of calling the method {@code selector} already satisfies the condition, the method will return immediately. If the
|
||||
* selector doesn't satisfy the condition for the {@code timeout} milliseconds, the function will throw.
|
||||
@@ -6341,6 +6400,9 @@ public interface Page extends AutoCloseable {
|
||||
* Returns when element specified by selector satisfies {@code state} option. Returns {@code null} if waiting for {@code hidden} or
|
||||
* {@code detached}.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Playwright automatically waits for element to be ready before performing an action. Using {@code Locator} objects and
|
||||
* web-first assertions make the code wait-for-selector-free.
|
||||
*
|
||||
* <p> Wait for the {@code selector} to satisfy {@code state} option (either appear/disappear from dom, or become visible/hidden). If at
|
||||
* the moment of calling the method {@code selector} already satisfies the condition, the method will return immediately. If the
|
||||
* selector doesn't satisfy the condition for the {@code timeout} milliseconds, the function will throw.
|
||||
@@ -6394,7 +6456,9 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> Shortcut for main frame's {@link Frame#waitForURL Frame.waitForURL()}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
default void waitForURL(String url) {
|
||||
waitForURL(url, null);
|
||||
@@ -6408,7 +6472,9 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> Shortcut for main frame's {@link Frame#waitForURL Frame.waitForURL()}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
void waitForURL(String url, WaitForURLOptions options);
|
||||
/**
|
||||
@@ -6420,7 +6486,9 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> Shortcut for main frame's {@link Frame#waitForURL Frame.waitForURL()}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
default void waitForURL(Pattern url) {
|
||||
waitForURL(url, null);
|
||||
@@ -6434,7 +6502,9 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> Shortcut for main frame's {@link Frame#waitForURL Frame.waitForURL()}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
void waitForURL(Pattern url, WaitForURLOptions options);
|
||||
/**
|
||||
@@ -6446,7 +6516,9 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> Shortcut for main frame's {@link Frame#waitForURL Frame.waitForURL()}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
default void waitForURL(Predicate<String> url) {
|
||||
waitForURL(url, null);
|
||||
@@ -6460,7 +6532,9 @@ public interface Page extends AutoCloseable {
|
||||
*
|
||||
* <p> Shortcut for main frame's {@link Frame#waitForURL Frame.waitForURL()}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation.
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if the
|
||||
* parameter is a string without wilcard characters, the method will wait for navigation to URL that is exactly equal to
|
||||
* the string.
|
||||
*/
|
||||
void waitForURL(Predicate<String> url, WaitForURLOptions options);
|
||||
/**
|
||||
|
||||
@@ -62,11 +62,11 @@ public interface Selectors {
|
||||
* Page page = browser.newPage();
|
||||
* page.setContent("<div><button>Click me</button></div>");
|
||||
* // Use the selector prefixed with its name.
|
||||
* ElementHandle button = page.querySelector("tag=button");
|
||||
* Locator button = page.locator("tag=button");
|
||||
* // Combine it with other selector engines.
|
||||
* page.click("tag=div >> text=\"Click me\"");
|
||||
* // Can use it in any methods supporting selectors.
|
||||
* int buttonCount = (int) page.evalOnSelectorAll("tag=button", "buttons => buttons.length");
|
||||
* int buttonCount = (int) page.locator("tag=button").count();
|
||||
* browser.close();
|
||||
* }</pre>
|
||||
*
|
||||
@@ -97,11 +97,11 @@ public interface Selectors {
|
||||
* Page page = browser.newPage();
|
||||
* page.setContent("<div><button>Click me</button></div>");
|
||||
* // Use the selector prefixed with its name.
|
||||
* ElementHandle button = page.querySelector("tag=button");
|
||||
* Locator button = page.locator("tag=button");
|
||||
* // Combine it with other selector engines.
|
||||
* page.click("tag=div >> text=\"Click me\"");
|
||||
* // Can use it in any methods supporting selectors.
|
||||
* int buttonCount = (int) page.evalOnSelectorAll("tag=button", "buttons => buttons.length");
|
||||
* int buttonCount = (int) page.locator("tag=button").count();
|
||||
* browser.close();
|
||||
* }</pre>
|
||||
*
|
||||
@@ -130,11 +130,11 @@ public interface Selectors {
|
||||
* Page page = browser.newPage();
|
||||
* page.setContent("<div><button>Click me</button></div>");
|
||||
* // Use the selector prefixed with its name.
|
||||
* ElementHandle button = page.querySelector("tag=button");
|
||||
* Locator button = page.locator("tag=button");
|
||||
* // Combine it with other selector engines.
|
||||
* page.click("tag=div >> text=\"Click me\"");
|
||||
* // Can use it in any methods supporting selectors.
|
||||
* int buttonCount = (int) page.evalOnSelectorAll("tag=button", "buttons => buttons.length");
|
||||
* int buttonCount = (int) page.locator("tag=button").count();
|
||||
* browser.close();
|
||||
* }</pre>
|
||||
*
|
||||
@@ -165,11 +165,11 @@ public interface Selectors {
|
||||
* Page page = browser.newPage();
|
||||
* page.setContent("<div><button>Click me</button></div>");
|
||||
* // Use the selector prefixed with its name.
|
||||
* ElementHandle button = page.querySelector("tag=button");
|
||||
* Locator button = page.locator("tag=button");
|
||||
* // Combine it with other selector engines.
|
||||
* page.click("tag=div >> text=\"Click me\"");
|
||||
* // Can use it in any methods supporting selectors.
|
||||
* int buttonCount = (int) page.evalOnSelectorAll("tag=button", "buttons => buttons.length");
|
||||
* int buttonCount = (int) page.locator("tag=button").count();
|
||||
* browser.close();
|
||||
* }</pre>
|
||||
*
|
||||
|
||||
@@ -51,6 +51,10 @@ public interface Tracing {
|
||||
* Whether to capture DOM snapshot on every action.
|
||||
*/
|
||||
public Boolean snapshots;
|
||||
/**
|
||||
* Trace name to be shown in the Trace Viewer.
|
||||
*/
|
||||
public String title;
|
||||
|
||||
/**
|
||||
* If specified, the trace is going to be saved into the file with the given name inside the {@code tracesDir} folder specified
|
||||
@@ -74,6 +78,27 @@ public interface Tracing {
|
||||
this.snapshots = snapshots;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Trace name to be shown in the Trace Viewer.
|
||||
*/
|
||||
public StartOptions setTitle(String title) {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class StartChunkOptions {
|
||||
/**
|
||||
* Trace name to be shown in the Trace Viewer.
|
||||
*/
|
||||
public String title;
|
||||
|
||||
/**
|
||||
* Trace name to be shown in the Trace Viewer.
|
||||
*/
|
||||
public StartChunkOptions setTitle(String title) {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class StopOptions {
|
||||
/**
|
||||
@@ -157,7 +182,34 @@ public interface Tracing {
|
||||
* .setPath(Paths.get("trace2.zip")));
|
||||
* }</pre>
|
||||
*/
|
||||
void startChunk();
|
||||
default void startChunk() {
|
||||
startChunk(null);
|
||||
}
|
||||
/**
|
||||
* Start a new trace chunk. If you'd like to record multiple traces on the same {@code BrowserContext}, use {@link Tracing#start
|
||||
* Tracing.start()} once, and then create multiple trace chunks with {@link Tracing#startChunk Tracing.startChunk()} and
|
||||
* {@link Tracing#stopChunk Tracing.stopChunk()}.
|
||||
* <pre>{@code
|
||||
* context.tracing().start(new Tracing.StartOptions()
|
||||
* .setScreenshots(true)
|
||||
* .setSnapshots(true));
|
||||
* Page page = context.newPage();
|
||||
* page.navigate("https://playwright.dev");
|
||||
*
|
||||
* context.tracing().startChunk();
|
||||
* page.click("text=Get Started");
|
||||
* // Everything between startChunk and stopChunk will be recorded in the trace.
|
||||
* context.tracing().stopChunk(new Tracing.StopChunkOptions()
|
||||
* .setPath(Paths.get("trace1.zip")));
|
||||
*
|
||||
* context.tracing().startChunk();
|
||||
* page.navigate("http://example.com");
|
||||
* // Save a second trace file with different actions.
|
||||
* context.tracing().stopChunk(new Tracing.StopChunkOptions()
|
||||
* .setPath(Paths.get("trace2.zip")));
|
||||
* }</pre>
|
||||
*/
|
||||
void startChunk(StartChunkOptions options);
|
||||
/**
|
||||
* Stop tracing.
|
||||
*/
|
||||
|
||||
@@ -457,14 +457,27 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
private void unroute(UrlMatcher matcher, Consumer<Route> handler) {
|
||||
withLogging("BrowserContext.unroute", () -> {
|
||||
routes.remove(matcher, handler);
|
||||
if (routes.size() == 0) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("enabled", false);
|
||||
sendMessage("setNetworkInterceptionEnabled", params);
|
||||
}
|
||||
maybeDisableNetworkInterception();
|
||||
});
|
||||
}
|
||||
|
||||
private void maybeDisableNetworkInterception() {
|
||||
if (routes.size() == 0) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("enabled", false);
|
||||
sendMessage("setNetworkInterceptionEnabled", params);
|
||||
}
|
||||
}
|
||||
|
||||
void handleRoute(Route route) {
|
||||
boolean handled = routes.handle(route);
|
||||
if (handled) {
|
||||
maybeDisableNetworkInterception();
|
||||
} else {
|
||||
route.resume();
|
||||
}
|
||||
}
|
||||
|
||||
void pause() {
|
||||
sendMessage("pause");
|
||||
}
|
||||
@@ -473,10 +486,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
protected void handleEvent(String event, JsonObject params) {
|
||||
if ("route".equals(event)) {
|
||||
Route route = connection.getExistingObject(params.getAsJsonObject("route").get("guid").getAsString());
|
||||
boolean handled = routes.handle(route);
|
||||
if (!handled) {
|
||||
route.resume();
|
||||
}
|
||||
handleRoute(route);
|
||||
} else if ("page".equals(event)) {
|
||||
PageImpl page = connection.getExistingObject(params.getAsJsonObject("page").get("guid").getAsString());
|
||||
pages.add(page);
|
||||
|
||||
@@ -71,7 +71,6 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to close browser connection", e);
|
||||
}
|
||||
notifyRemoteClosed();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Browser;
|
||||
@@ -23,12 +24,7 @@ import com.microsoft.playwright.BrowserType;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
@@ -58,51 +54,40 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
}
|
||||
|
||||
private Browser connectImpl(String wsEndpoint, ConnectOptions options) {
|
||||
try {
|
||||
Duration timeout = Duration.ofDays(1);
|
||||
Map<String, String> headers = Collections.emptyMap();
|
||||
Duration slowMo = null;
|
||||
if (options != null) {
|
||||
if (options.timeout != null) {
|
||||
timeout = Duration.ofMillis(Math.round(options.timeout));
|
||||
}
|
||||
if (options.headers != null) {
|
||||
headers = options.headers;
|
||||
}
|
||||
if (options.slowMo != null) {
|
||||
slowMo = Duration.ofMillis(options.slowMo.intValue());
|
||||
}
|
||||
}
|
||||
WebSocketTransport transport = new WebSocketTransport(new URI(wsEndpoint), headers, timeout, slowMo);
|
||||
Connection connection = new Connection(transport);
|
||||
PlaywrightImpl playwright = connection.initializePlaywright();
|
||||
if (!playwright.initializer.has("preLaunchedBrowser")) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
throw new PlaywrightException("Malformed endpoint. Did you use launchServer method?");
|
||||
}
|
||||
playwright.initSharedSelectors(this.connection.getExistingObject("Playwright"));
|
||||
BrowserImpl browser = connection.getExistingObject(playwright.initializer.getAsJsonObject("preLaunchedBrowser").get("guid").getAsString());
|
||||
browser.isRemote = true;
|
||||
browser.isConnectedOverWebSocket = true;
|
||||
Consumer<WebSocketTransport> connectionCloseListener = t -> browser.notifyRemoteClosed();
|
||||
transport.onClose(connectionCloseListener);
|
||||
browser.onDisconnected(b -> {
|
||||
playwright.unregisterSelectors();
|
||||
transport.offClose(connectionCloseListener);
|
||||
try {
|
||||
connection.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
});
|
||||
return browser;
|
||||
} catch (URISyntaxException e) {
|
||||
throw new PlaywrightException("Failed to connect", e);
|
||||
if (options == null) {
|
||||
options = new ConnectOptions();
|
||||
}
|
||||
// We don't use gson() here as the headers map should be serialized to a json object.
|
||||
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("wsEndpoint", wsEndpoint);
|
||||
JsonObject json = sendMessage("connect", params).getAsJsonObject();
|
||||
JsonPipe pipe = connection.getExistingObject(json.getAsJsonObject("pipe").get("guid").getAsString());
|
||||
Connection connection = new Connection(pipe);
|
||||
PlaywrightImpl playwright = connection.initializePlaywright();
|
||||
if (!playwright.initializer.has("preLaunchedBrowser")) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
throw new PlaywrightException("Malformed endpoint. Did you use launchServer method?");
|
||||
}
|
||||
playwright.initSharedSelectors(this.connection.getExistingObject("Playwright"));
|
||||
BrowserImpl browser = connection.getExistingObject(playwright.initializer.getAsJsonObject("preLaunchedBrowser").get("guid").getAsString());
|
||||
browser.isRemote = true;
|
||||
browser.isConnectedOverWebSocket = true;
|
||||
Consumer<JsonPipe> connectionCloseListener = t -> browser.notifyRemoteClosed();
|
||||
pipe.onClose(connectionCloseListener);
|
||||
browser.onDisconnected(b -> {
|
||||
playwright.unregisterSelectors();
|
||||
pipe.offClose(connectionCloseListener);
|
||||
try {
|
||||
connection.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
});
|
||||
return browser;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -85,6 +85,9 @@ public class Connection {
|
||||
}
|
||||
|
||||
Connection(Transport transport) {
|
||||
if (isLogging) {
|
||||
transport = new TransportLogger(transport);
|
||||
}
|
||||
this.transport = transport;
|
||||
root = new Root(this);
|
||||
String srcRoot = System.getenv("PLAYWRIGHT_JAVA_SRC");
|
||||
@@ -172,11 +175,7 @@ public class Connection {
|
||||
metadata.addProperty("apiName", apiName);
|
||||
}
|
||||
message.add("metadata", metadata);
|
||||
String messageString = gson().toJson(message);
|
||||
if (isLogging) {
|
||||
logWithTimestamp("SEND ► " + messageString);
|
||||
}
|
||||
transport.send(messageString);
|
||||
transport.send(message);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -200,16 +199,13 @@ public class Connection {
|
||||
}
|
||||
|
||||
void processOneMessage() {
|
||||
String messageString = transport.poll(Duration.ofMillis(10));
|
||||
if (messageString == null) {
|
||||
JsonObject message = transport.poll(Duration.ofMillis(10));
|
||||
if (message == null) {
|
||||
return;
|
||||
}
|
||||
if (isLogging) {
|
||||
logWithTimestamp("◀ RECV " + messageString);
|
||||
}
|
||||
Gson gson = gson();
|
||||
Message message = gson.fromJson(messageString, Message.class);
|
||||
dispatch(message);
|
||||
Message messageObj = gson.fromJson(message, Message.class);
|
||||
dispatch(messageObj);
|
||||
}
|
||||
|
||||
private void dispatch(Message message) {
|
||||
@@ -305,7 +301,7 @@ public class Connection {
|
||||
case "ElementHandle":
|
||||
result = new ElementHandleImpl(parent, type, guid, initializer);
|
||||
break;
|
||||
case "FetchRequest":
|
||||
case "APIRequestContext":
|
||||
// Create fake object as this API is experimental an only exposed in Node.js.
|
||||
result = new ChannelOwner(parent, type, guid, initializer);
|
||||
break;
|
||||
@@ -315,6 +311,9 @@ public class Connection {
|
||||
case "JSHandle":
|
||||
result = new JSHandleImpl(parent, type, guid, initializer);
|
||||
break;
|
||||
case "JsonPipe":
|
||||
result = new JsonPipe(parent, type, guid, initializer);
|
||||
break;
|
||||
case "Page":
|
||||
result = new PageImpl(parent, type, guid, initializer);
|
||||
break;
|
||||
|
||||
@@ -31,7 +31,7 @@ import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.microsoft.playwright.options.LoadState.*;
|
||||
import static com.microsoft.playwright.options.WaitUntilState.*;
|
||||
import static com.microsoft.playwright.impl.Serialization.*;
|
||||
import static com.microsoft.playwright.impl.Utils.convertViaJson;
|
||||
|
||||
@@ -40,7 +40,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
private String url;
|
||||
FrameImpl parentFrame;
|
||||
Set<FrameImpl> childFrames = new LinkedHashSet<>();
|
||||
private final Set<LoadState> loadStates = new HashSet<>();
|
||||
private final Set<WaitUntilState> loadStates = new HashSet<>();
|
||||
|
||||
enum InternalEventType { NAVIGATED, LOADSTATE }
|
||||
private final ListenerCollection<InternalEventType> internalListeners = new ListenerCollection<>();
|
||||
@@ -61,11 +61,12 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
}
|
||||
|
||||
private static LoadState loadStateFromProtocol(String value) {
|
||||
private static WaitUntilState loadStateFromProtocol(String value) {
|
||||
switch (value) {
|
||||
case "load": return LOAD;
|
||||
case "domcontentloaded": return DOMCONTENTLOADED;
|
||||
case "networkidle": return NETWORKIDLE;
|
||||
case "commit": return COMMIT;
|
||||
default: throw new PlaywrightException("Unexpected value: " + value);
|
||||
}
|
||||
}
|
||||
@@ -357,6 +358,11 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrameLocator frameLocator(String selector) {
|
||||
return new FrameLocatorImpl(this, selector);
|
||||
}
|
||||
|
||||
ElementHandle frameElementImpl() {
|
||||
JsonObject json = sendMessage("frameElement").getAsJsonObject();
|
||||
return connection.getExistingObject(json.getAsJsonObject("element").get("guid").getAsString());
|
||||
@@ -560,7 +566,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator locator(String selector) {
|
||||
public LocatorImpl locator(String selector) {
|
||||
return new LocatorImpl(this, selector);
|
||||
}
|
||||
|
||||
@@ -801,6 +807,10 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
|
||||
void waitForLoadStateImpl(LoadState state, WaitForLoadStateOptions options) {
|
||||
waitForLoadStateImpl(convertViaJson(state, WaitUntilState.class), options);
|
||||
}
|
||||
|
||||
private void waitForLoadStateImpl(WaitUntilState state, WaitForLoadStateOptions options) {
|
||||
if (options == null) {
|
||||
options = new WaitForLoadStateOptions();
|
||||
}
|
||||
@@ -815,11 +825,11 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
runUntil(() -> {}, new WaitableRace<>(waitables));
|
||||
}
|
||||
|
||||
private class WaitForLoadStateHelper implements Waitable<Void>, Consumer<LoadState> {
|
||||
private final LoadState expectedState;
|
||||
private class WaitForLoadStateHelper implements Waitable<Void>, Consumer<WaitUntilState> {
|
||||
private final WaitUntilState expectedState;
|
||||
private boolean isDone;
|
||||
|
||||
WaitForLoadStateHelper(LoadState state) {
|
||||
WaitForLoadStateHelper(WaitUntilState state) {
|
||||
expectedState = state;
|
||||
isDone = loadStates.contains(state);
|
||||
if (!isDone) {
|
||||
@@ -828,7 +838,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(LoadState state) {
|
||||
public void accept(WaitUntilState state) {
|
||||
if (expectedState.equals(state)) {
|
||||
isDone = true;
|
||||
dispose();
|
||||
@@ -851,13 +861,13 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
private class WaitForNavigationHelper implements Waitable<Response>, Consumer<JsonObject> {
|
||||
private final UrlMatcher matcher;
|
||||
private final LoadState expectedLoadState;
|
||||
private final WaitUntilState expectedLoadState;
|
||||
private WaitForLoadStateHelper loadStateHelper;
|
||||
|
||||
private RequestImpl request;
|
||||
private RuntimeException exception;
|
||||
|
||||
WaitForNavigationHelper(UrlMatcher matcher, LoadState expectedLoadState) {
|
||||
WaitForNavigationHelper(UrlMatcher matcher, WaitUntilState expectedLoadState) {
|
||||
this.matcher = matcher;
|
||||
this.expectedLoadState = expectedLoadState;
|
||||
internalListeners.add(InternalEventType.NAVIGATED, this);
|
||||
@@ -935,7 +945,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
if (matcher == null) {
|
||||
matcher = UrlMatcher.forOneOf(page.context().baseUrl, options.url);
|
||||
}
|
||||
waitables.add(new WaitForNavigationHelper(matcher, convertViaJson(options.waitUntil, LoadState.class)));
|
||||
waitables.add(new WaitForNavigationHelper(matcher, options.waitUntil));
|
||||
waitables.add(page.createWaitForCloseHelper());
|
||||
waitables.add(page.createWaitableFrameDetach(this));
|
||||
waitables.add(page.createWaitableNavigationTimeout(options.timeout));
|
||||
@@ -948,11 +958,16 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
|
||||
ElementHandle waitForSelectorImpl(String selector, WaitForSelectorOptions options) {
|
||||
return waitForSelectorImpl(selector, options, false);
|
||||
}
|
||||
|
||||
ElementHandle waitForSelectorImpl(String selector, WaitForSelectorOptions options, boolean omitReturnValue) {
|
||||
if (options == null) {
|
||||
options = new WaitForSelectorOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("omitReturnValue", omitReturnValue);
|
||||
JsonElement json = sendMessage("waitForSelector", params);
|
||||
JsonObject element = json.getAsJsonObject().getAsJsonObject("element");
|
||||
if (element == null) {
|
||||
@@ -967,13 +982,9 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
|
||||
void waitForTimeoutImpl(double timeout) {
|
||||
runUntil(() -> {}, new WaitableTimeout<Void>(timeout) {
|
||||
@Override
|
||||
public Void get() {
|
||||
// Override to not throw.
|
||||
return null;
|
||||
}
|
||||
});
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("timeout", timeout);
|
||||
sendMessage("waitForTimeout", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1000,8 +1011,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
options = new WaitForURLOptions();
|
||||
}
|
||||
if (matcher.test(url())) {
|
||||
waitForLoadStateImpl(convertViaJson(options.waitUntil, LoadState.class),
|
||||
convertViaJson(options, WaitForLoadStateOptions.class));
|
||||
waitForLoadStateImpl(options.waitUntil, convertViaJson(options, WaitForLoadStateOptions.class));
|
||||
return;
|
||||
}
|
||||
waitForNavigationImpl(() -> {}, convertViaJson(options, WaitForNavigationOptions.class), matcher);
|
||||
@@ -1011,7 +1021,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
if ("loadstate".equals(event)) {
|
||||
JsonElement add = params.get("add");
|
||||
if (add != null) {
|
||||
LoadState state = loadStateFromProtocol(add.getAsString());
|
||||
WaitUntilState state = loadStateFromProtocol(add.getAsString());
|
||||
loadStates.add(state);
|
||||
internalListeners.notify(InternalEventType.LOADSTATE, state);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.FrameLocator;
|
||||
|
||||
class FrameLocatorImpl implements FrameLocator {
|
||||
private final FrameImpl frame;
|
||||
private final String frameSelector;
|
||||
|
||||
FrameLocatorImpl(FrameImpl frame, String selector) {
|
||||
this.frame = frame;
|
||||
this.frameSelector = selector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrameLocator first() {
|
||||
return new FrameLocatorImpl(frame, frameSelector + " >> nth=0");
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrameLocatorImpl frameLocator(String selector) {
|
||||
return new FrameLocatorImpl(frame, frameSelector + " >> control=enter-frame >> " + selector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrameLocator last() {
|
||||
return new FrameLocatorImpl(frame, frameSelector + " >> nth=-1");
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocatorImpl locator(String selector) {
|
||||
return new LocatorImpl(frame, frameSelector + " >> control=enter-frame >> " + selector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrameLocator nth(int index) {
|
||||
return new FrameLocatorImpl(frame, frameSelector + " >> nth=" + index);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
|
||||
class JsonPipe extends ChannelOwner implements Transport {
|
||||
private final Queue<JsonObject> incoming = new LinkedList<>();
|
||||
private ListenerCollection<EventType> listeners = new ListenerCollection<>();
|
||||
private enum EventType { CLOSE }
|
||||
private boolean isClosed;
|
||||
|
||||
JsonPipe(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(JsonObject message) {
|
||||
checkIfClosed();
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("message", message);
|
||||
sendMessage("send", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject poll(Duration timeout) {
|
||||
Instant start = Instant.now();
|
||||
return runUntil(() -> {}, new Waitable<JsonObject>() {
|
||||
JsonObject message;
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
if (!incoming.isEmpty()) {
|
||||
message = incoming.remove();
|
||||
return true;
|
||||
}
|
||||
checkIfClosed();
|
||||
if (Duration.between(start, Instant.now()).compareTo(timeout) > 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject get() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (!isClosed) {
|
||||
sendMessage("close");
|
||||
}
|
||||
}
|
||||
|
||||
void onClose(Consumer<JsonPipe> handler) {
|
||||
listeners.add(EventType.CLOSE, handler);
|
||||
}
|
||||
|
||||
void offClose(Consumer<JsonPipe> handler) {
|
||||
listeners.remove(EventType.CLOSE, handler);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void handleEvent(String event, JsonObject params) {
|
||||
if ("message".equals(event)) {
|
||||
incoming.add(params.get("message").getAsJsonObject());
|
||||
} else if ("closed".equals(event)) {
|
||||
isClosed = true;
|
||||
listeners.notify(EventType.CLOSE, this);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkIfClosed() {
|
||||
if (isClosed) {
|
||||
throw new PlaywrightException("Browser has been closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.microsoft.playwright.ElementHandle;
|
||||
import com.microsoft.playwright.Frame;
|
||||
import com.microsoft.playwright.JSHandle;
|
||||
import com.microsoft.playwright.Locator;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.*;
|
||||
import com.microsoft.playwright.options.BoundingBox;
|
||||
import com.microsoft.playwright.options.FilePayload;
|
||||
import com.microsoft.playwright.options.SelectOption;
|
||||
@@ -13,6 +12,8 @@ import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
import static com.microsoft.playwright.impl.Serialization.serializeArgument;
|
||||
import static com.microsoft.playwright.impl.Utils.convertViaJson;
|
||||
|
||||
class LocatorImpl implements Locator {
|
||||
@@ -147,6 +148,11 @@ class LocatorImpl implements Locator {
|
||||
frame.focus(selector, convertViaJson(options, Frame.FocusOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrameLocatorImpl frameLocator(String selector) {
|
||||
return new FrameLocatorImpl(frame, this.selector + " >> " + selector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttribute(String name, GetAttributeOptions options) {
|
||||
if (options == null) {
|
||||
@@ -399,8 +405,36 @@ class LocatorImpl implements Locator {
|
||||
frame.uncheck(selector, convertViaJson(options, Frame.UncheckOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waitFor(WaitForOptions options) {
|
||||
if (options == null) {
|
||||
options = new WaitForOptions();
|
||||
}
|
||||
waitForImpl(options);
|
||||
}
|
||||
|
||||
private void waitForImpl(WaitForOptions options) {
|
||||
frame.withLogging("Locator.waitFor", () -> frame.waitForSelectorImpl(selector, convertViaJson(options, Frame.WaitForSelectorOptions.class).setStrict(true), true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Locator@" + selector;
|
||||
}
|
||||
|
||||
FrameExpectResult expect(String expression, FrameExpectOptions options) {
|
||||
return frame.withLogging("Locator.expect", () -> expectImpl(expression, options));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,11 +198,10 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
} else if ("route".equals(event)) {
|
||||
Route route = connection.getExistingObject(params.getAsJsonObject("route").get("guid").getAsString());
|
||||
boolean handled = routes.handle(route);
|
||||
if (!handled) {
|
||||
handled = browserContext.routes.handle(route);
|
||||
}
|
||||
if (!handled) {
|
||||
route.resume();
|
||||
if (handled) {
|
||||
maybeDisableNetworkInterception();
|
||||
} else {
|
||||
browserContext.handleRoute(route);
|
||||
}
|
||||
} else if ("video".equals(event)) {
|
||||
String artifactGuid = params.getAsJsonObject("artifact").get("guid").getAsString();
|
||||
@@ -738,6 +737,11 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
return frameFor(new UrlMatcher(predicate));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrameLocator frameLocator(String selector) {
|
||||
return mainFrame.frameLocator(selector);
|
||||
}
|
||||
|
||||
private Frame frameFor(UrlMatcher matcher) {
|
||||
for (Frame frame : frames) {
|
||||
if (matcher.test(frame.url())) {
|
||||
@@ -875,7 +879,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator locator(String selector) {
|
||||
public LocatorImpl locator(String selector) {
|
||||
return mainFrame.locator(selector);
|
||||
}
|
||||
|
||||
@@ -1181,14 +1185,18 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
private void unroute(UrlMatcher matcher, Consumer<Route> handler) {
|
||||
withLogging("Page.unroute", () -> {
|
||||
routes.remove(matcher, handler);
|
||||
if (routes.size() == 0) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("enabled", false);
|
||||
sendMessage("setNetworkInterceptionEnabled", params);
|
||||
}
|
||||
maybeDisableNetworkInterception();
|
||||
});
|
||||
}
|
||||
|
||||
private void maybeDisableNetworkInterception() {
|
||||
if (routes.size() == 0) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("enabled", false);
|
||||
sendMessage("setNetworkInterceptionEnabled", params);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String url() {
|
||||
return mainFrame.url();
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
|
||||
import java.io.*;
|
||||
@@ -24,8 +25,10 @@ import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
|
||||
public class PipeTransport implements Transport {
|
||||
private final BlockingQueue<String> incoming = new ArrayBlockingQueue<>(1000);
|
||||
private final BlockingQueue<JsonObject> incoming = new ArrayBlockingQueue<>(1000);
|
||||
private final BlockingQueue<String> outgoing= new ArrayBlockingQueue<>(1000);
|
||||
|
||||
private final ReaderThread readerThread;
|
||||
@@ -42,24 +45,27 @@ public class PipeTransport implements Transport {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(String message) {
|
||||
public void send(JsonObject message) {
|
||||
if (isClosed) {
|
||||
throw new PlaywrightException("Playwright connection closed");
|
||||
}
|
||||
try {
|
||||
outgoing.put(message);
|
||||
// We could serialize the message on the IO thread but there is no guarantee
|
||||
// that the message object won't be modified on this thread after it's added
|
||||
// to the queue.
|
||||
outgoing.put(gson().toJson(message));
|
||||
} catch (InterruptedException e) {
|
||||
throw new PlaywrightException("Failed to send message", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String poll(Duration timeout) {
|
||||
public JsonObject poll(Duration timeout) {
|
||||
if (isClosed) {
|
||||
throw new PlaywrightException("Playwright connection closed");
|
||||
}
|
||||
try {
|
||||
String message = incoming.poll(timeout.toMillis(), TimeUnit.MILLISECONDS);
|
||||
JsonObject message = incoming.poll(timeout.toMillis(), TimeUnit.MILLISECONDS);
|
||||
if (message == null && readerThread.exception != null) {
|
||||
try {
|
||||
close();
|
||||
@@ -91,7 +97,7 @@ public class PipeTransport implements Transport {
|
||||
|
||||
class ReaderThread extends Thread {
|
||||
private final DataInputStream in;
|
||||
private final BlockingQueue<String> queue;
|
||||
private final BlockingQueue<JsonObject> queue;
|
||||
volatile boolean isClosing;
|
||||
volatile Exception exception;
|
||||
|
||||
@@ -107,7 +113,7 @@ class ReaderThread extends Thread {
|
||||
}
|
||||
}
|
||||
|
||||
ReaderThread(DataInputStream in, BlockingQueue<String> queue) {
|
||||
ReaderThread(DataInputStream in, BlockingQueue<JsonObject> queue) {
|
||||
this.in = in;
|
||||
this.queue = queue;
|
||||
}
|
||||
@@ -116,7 +122,8 @@ class ReaderThread extends Thread {
|
||||
public void run() {
|
||||
while (!isInterrupted()) {
|
||||
try {
|
||||
queue.put(readMessage());
|
||||
JsonObject message = gson().fromJson(readMessage(), JsonObject.class);
|
||||
queue.put(message);
|
||||
} catch (IOException e) {
|
||||
if (!isInterrupted() && !isClosing) {
|
||||
exception = e;
|
||||
|
||||
@@ -18,18 +18,12 @@
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
class Binary {
|
||||
}
|
||||
import java.util.List;
|
||||
|
||||
class Channel {
|
||||
String guid;
|
||||
}
|
||||
|
||||
class Metadata{
|
||||
String stack;
|
||||
}
|
||||
|
||||
|
||||
class SerializedValue{
|
||||
Number n;
|
||||
Boolean b;
|
||||
@@ -51,45 +45,11 @@ class SerializedValue{
|
||||
Number h;
|
||||
}
|
||||
|
||||
|
||||
class SerializedArgument{
|
||||
SerializedValue value;
|
||||
Channel[] handles;
|
||||
}
|
||||
|
||||
class AXNode{
|
||||
String role;
|
||||
String name;
|
||||
String valueString;
|
||||
Number valueNumber;
|
||||
String description;
|
||||
String keyshortcuts;
|
||||
String roledescription;
|
||||
String valuetext;
|
||||
Boolean disabled;
|
||||
Boolean expanded;
|
||||
Boolean focused;
|
||||
Boolean modal;
|
||||
Boolean multiline;
|
||||
Boolean multiselectable;
|
||||
Boolean readonly;
|
||||
Boolean required;
|
||||
Boolean selected;
|
||||
// Possible values: { 'checked, 'unchecked, 'mixed }
|
||||
String checked;
|
||||
// Possible values: { 'pressed, 'released, 'mixed }
|
||||
String pressed;
|
||||
Number level;
|
||||
Number valuemin;
|
||||
Number valuemax;
|
||||
String autocomplete;
|
||||
String haspopup;
|
||||
String invalid;
|
||||
String orientation;
|
||||
AXNode[] children;
|
||||
}
|
||||
|
||||
|
||||
class SerializedError{
|
||||
public static class Error {
|
||||
String message;
|
||||
@@ -119,3 +79,28 @@ class SerializedError{
|
||||
}
|
||||
}
|
||||
|
||||
class ExpectedTextValue {
|
||||
String string;
|
||||
String regexSource;
|
||||
String regexFlags;
|
||||
Boolean matchSubstring;
|
||||
Boolean normalizeWhiteSpace;
|
||||
}
|
||||
|
||||
class FrameExpectOptions {
|
||||
Object expressionArg;
|
||||
List<ExpectedTextValue> expectedText;
|
||||
Integer expectedNumber;
|
||||
SerializedArgument expectedValue;
|
||||
Boolean useInnerText;
|
||||
boolean isNot;
|
||||
Double timeout;
|
||||
}
|
||||
|
||||
class FrameExpectResult {
|
||||
boolean matches;
|
||||
SerializedValue received;
|
||||
List<String> log;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -167,13 +167,8 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
if (rawHeaders != null) {
|
||||
return rawHeaders;
|
||||
}
|
||||
ResponseImpl response = response();
|
||||
// there is no response, so should we return the headers we have now?
|
||||
if (response == null) {
|
||||
return headers;
|
||||
}
|
||||
JsonArray rawHeadersJson = response.withLogging("Request.allHeaders", () -> {
|
||||
JsonObject result = response.sendMessage("rawRequestHeaders").getAsJsonObject();
|
||||
JsonArray rawHeadersJson = withLogging("Request.allHeaders", () -> {
|
||||
JsonObject result = sendMessage("rawRequestHeaders").getAsJsonObject();
|
||||
return result.getAsJsonArray("headers");
|
||||
});
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ public class RouteImpl extends ChannelOwner implements Route {
|
||||
withLogging("Route.abort", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("errorCode", errorCode);
|
||||
sendMessage("abort", params);
|
||||
sendMessageAsync("abort", params);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ public class RouteImpl extends ChannelOwner implements Route {
|
||||
String base64 = Base64.getEncoder().encodeToString(bytes);
|
||||
params.addProperty("postData", base64);
|
||||
}
|
||||
sendMessage("continue", params);
|
||||
sendMessageAsync("continue", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -128,7 +128,7 @@ public class RouteImpl extends ChannelOwner implements Route {
|
||||
params.add("headers", Serialization.toProtocol(headers));
|
||||
params.addProperty("isBase64", isBase64);
|
||||
params.addProperty("body", body);
|
||||
sendMessage("fulfill", params);
|
||||
sendMessageAsync("fulfill", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -50,6 +50,10 @@ class Router {
|
||||
handler.accept(route);
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean isDone() {
|
||||
return times != null && times <= 0;
|
||||
}
|
||||
}
|
||||
|
||||
void add(UrlMatcher matcher, Consumer<Route> handler, Integer times) {
|
||||
@@ -69,6 +73,9 @@ class Router {
|
||||
boolean handle(Route route) {
|
||||
for (RouteInfo info : routes) {
|
||||
if (info.handle(route)) {
|
||||
if (info.isDone()) {
|
||||
routes.remove(info);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,29 +33,26 @@ import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
class Serialization {
|
||||
private static Gson gson;
|
||||
private static Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(SameSiteAttribute.class, new SameSiteAdapter().nullSafe())
|
||||
.registerTypeAdapter(BrowserChannel.class, new ToLowerCaseAndDashSerializer<BrowserChannel>())
|
||||
.registerTypeAdapter(ColorScheme.class, new ToLowerCaseAndDashSerializer<ColorScheme>())
|
||||
.registerTypeAdapter(Media.class, new ToLowerCaseSerializer<Media>())
|
||||
.registerTypeAdapter(ForcedColors.class, new ToLowerCaseSerializer<ForcedColors>())
|
||||
.registerTypeAdapter(ReducedMotion.class, new ToLowerCaseAndDashSerializer<ReducedMotion>())
|
||||
.registerTypeAdapter(ScreenshotType.class, new ToLowerCaseSerializer<ScreenshotType>())
|
||||
.registerTypeAdapter(MouseButton.class, new ToLowerCaseSerializer<MouseButton>())
|
||||
.registerTypeAdapter(LoadState.class, new ToLowerCaseSerializer<LoadState>())
|
||||
.registerTypeAdapter(WaitUntilState.class, new ToLowerCaseSerializer<WaitUntilState>())
|
||||
.registerTypeAdapter(WaitForSelectorState.class, new ToLowerCaseSerializer<WaitForSelectorState>())
|
||||
.registerTypeAdapter((new TypeToken<List<KeyboardModifier>>(){}).getType(), new KeyboardModifiersSerializer())
|
||||
.registerTypeAdapter(Optional.class, new OptionalSerializer())
|
||||
.registerTypeHierarchyAdapter(JSHandleImpl.class, new HandleSerializer())
|
||||
.registerTypeAdapter((new TypeToken<Map<String, String>>(){}).getType(), new StringMapSerializer())
|
||||
.registerTypeAdapter((new TypeToken<Map<String, Object>>(){}).getType(), new FirefoxUserPrefsSerializer())
|
||||
.registerTypeHierarchyAdapter(Path.class, new PathSerializer()).create();;
|
||||
|
||||
static Gson gson() {
|
||||
if (gson == null) {
|
||||
gson = new GsonBuilder()
|
||||
.registerTypeAdapter(SameSiteAttribute.class, new SameSiteAdapter().nullSafe())
|
||||
.registerTypeAdapter(BrowserChannel.class, new ToLowerCaseAndDashSerializer<BrowserChannel>())
|
||||
.registerTypeAdapter(ColorScheme.class, new ToLowerCaseAndDashSerializer<ColorScheme>())
|
||||
.registerTypeAdapter(Media.class, new ToLowerCaseSerializer<Media>())
|
||||
.registerTypeAdapter(ForcedColors.class, new ToLowerCaseSerializer<ForcedColors>())
|
||||
.registerTypeAdapter(ReducedMotion.class, new ToLowerCaseAndDashSerializer<ReducedMotion>())
|
||||
.registerTypeAdapter(ScreenshotType.class, new ToLowerCaseSerializer<ScreenshotType>())
|
||||
.registerTypeAdapter(MouseButton.class, new ToLowerCaseSerializer<MouseButton>())
|
||||
.registerTypeAdapter(LoadState.class, new ToLowerCaseSerializer<LoadState>())
|
||||
.registerTypeAdapter(WaitUntilState.class, new ToLowerCaseSerializer<WaitUntilState>())
|
||||
.registerTypeAdapter(WaitForSelectorState.class, new ToLowerCaseSerializer<WaitForSelectorState>())
|
||||
.registerTypeAdapter((new TypeToken<List<KeyboardModifier>>(){}).getType(), new KeyboardModifiersSerializer())
|
||||
.registerTypeAdapter(Optional.class, new OptionalSerializer())
|
||||
.registerTypeHierarchyAdapter(JSHandleImpl.class, new HandleSerializer())
|
||||
.registerTypeAdapter((new TypeToken<Map<String, String>>(){}).getType(), new StringMapSerializer())
|
||||
.registerTypeAdapter((new TypeToken<Map<String, Object>>(){}).getType(), new FirefoxUserPrefsSerializer())
|
||||
.registerTypeHierarchyAdapter(Path.class, new PathSerializer()).create();
|
||||
}
|
||||
return gson;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ class TracingImpl implements Tracing {
|
||||
private void stopChunkImpl(Path path) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("save", path != null);
|
||||
params.addProperty("skipCompress", false);
|
||||
JsonObject json = context.sendMessage("tracingStopChunk", params).getAsJsonObject();
|
||||
if (!json.has("artifact")) {
|
||||
return;
|
||||
@@ -54,12 +55,20 @@ class TracingImpl implements Tracing {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startChunk() {
|
||||
public void startChunk(StartChunkOptions options) {
|
||||
context.withLogging("Tracing.startChunk", () -> {
|
||||
context.sendMessage("tracingStartChunk");
|
||||
startChunkImpl(options);
|
||||
});
|
||||
}
|
||||
|
||||
private void startChunkImpl(StartChunkOptions options) {
|
||||
if (options == null) {
|
||||
options = new StartChunkOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
context.sendMessage("tracingStartChunk", params);
|
||||
}
|
||||
|
||||
private void startImpl(StartOptions options) {
|
||||
if (options == null) {
|
||||
options = new StartOptions();
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
|
||||
public interface Transport {
|
||||
void send(String message);
|
||||
String poll(Duration timeout);
|
||||
void send(JsonObject message);
|
||||
JsonObject poll(Duration timeout);
|
||||
void close() throws IOException;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
|
||||
import static com.microsoft.playwright.impl.LoggingSupport.logWithTimestamp;
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
|
||||
class TransportLogger implements Transport {
|
||||
private final Transport transport;
|
||||
|
||||
TransportLogger(Transport transport) {
|
||||
this.transport = transport;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(JsonObject message) {
|
||||
String messageString = gson().toJson(message);
|
||||
logWithTimestamp("SEND ► " + messageString);
|
||||
transport.send(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject poll(Duration timeout) {
|
||||
JsonObject message = transport.poll(timeout);
|
||||
if (message != null) {
|
||||
String messageString = gson().toJson(message);
|
||||
logWithTimestamp("◀ RECV " + messageString);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
transport.close();
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ class UrlMatcher {
|
||||
throw new PlaywrightException("Url must be String, Pattern or Predicate<String>, found: " + object.getClass().getTypeName());
|
||||
}
|
||||
|
||||
private static String resolveUrl(URL baseUrl, String spec) {
|
||||
static String resolveUrl(URL baseUrl, String spec) {
|
||||
if (baseUrl == null) {
|
||||
return spec;
|
||||
}
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import org.java_websocket.client.WebSocketClient;
|
||||
import org.java_websocket.handshake.ServerHandshake;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
|
||||
class WebSocketTransport implements Transport {
|
||||
private final BlockingQueue<String> incoming = new LinkedBlockingQueue<>();
|
||||
private final ClientConnection clientConnection;
|
||||
private final Duration slowMo;
|
||||
private boolean isClosed;
|
||||
private volatile Exception lastError;
|
||||
ListenerCollection<EventType> listeners = new ListenerCollection<>();
|
||||
|
||||
private enum EventType { CLOSE }
|
||||
|
||||
private class ClientConnection extends WebSocketClient {
|
||||
ClientConnection(URI serverUri) {
|
||||
super(serverUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(ServerHandshake handshakedata) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(String message) {
|
||||
incoming.add(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(int code, String reason, boolean remote) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
lastError = ex;
|
||||
}
|
||||
}
|
||||
|
||||
WebSocketTransport(URI uri, Map<String, String> headers, Duration timeout, Duration slowMo) {
|
||||
clientConnection = new ClientConnection(uri);
|
||||
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
||||
clientConnection.addHeader(entry.getKey(), entry.getValue());
|
||||
}
|
||||
try {
|
||||
if (!clientConnection.connectBlocking(timeout.toMillis(), TimeUnit.MILLISECONDS)) {
|
||||
throw new PlaywrightException("Failed to connect", lastError);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new PlaywrightException("Failed to connect", e);
|
||||
}
|
||||
this.slowMo = slowMo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(String message) {
|
||||
checkIfClosed();
|
||||
clientConnection.send(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String poll(Duration timeout) {
|
||||
checkIfClosed();
|
||||
try {
|
||||
String message = incoming.poll(timeout.toMillis(), TimeUnit.MILLISECONDS);
|
||||
if (slowMo != null && message != null) {
|
||||
Thread.sleep(slowMo.toMillis());
|
||||
}
|
||||
return message;
|
||||
} catch (InterruptedException e) {
|
||||
throw new PlaywrightException("Failed to read message", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
isClosed = true;
|
||||
clientConnection.close();
|
||||
}
|
||||
|
||||
void onClose(Consumer<WebSocketTransport> handler) {
|
||||
listeners.add(EventType.CLOSE, handler);
|
||||
}
|
||||
|
||||
void offClose(Consumer<WebSocketTransport> handler) {
|
||||
listeners.remove(EventType.CLOSE, handler);
|
||||
}
|
||||
|
||||
private void checkIfClosed() {
|
||||
if (isClosed) {
|
||||
throw new PlaywrightException("Playwright connection closed");
|
||||
}
|
||||
if (clientConnection.isClosed()) {
|
||||
isClosed = true;
|
||||
listeners.notify(EventType.CLOSE, this);
|
||||
throw new PlaywrightException("Playwright connection closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ public class Proxy {
|
||||
*/
|
||||
public String server;
|
||||
/**
|
||||
* Optional coma-separated domains to bypass proxy, for example {@code ".com, chromium.org, .domain.com"}.
|
||||
* Optional comma-separated domains to bypass proxy, for example {@code ".com, chromium.org, .domain.com"}.
|
||||
*/
|
||||
public String bypass;
|
||||
/**
|
||||
@@ -39,7 +39,7 @@ public class Proxy {
|
||||
this.server = server;
|
||||
}
|
||||
/**
|
||||
* Optional coma-separated domains to bypass proxy, for example {@code ".com, chromium.org, .domain.com"}.
|
||||
* Optional comma-separated domains to bypass proxy, for example {@code ".com, chromium.org, .domain.com"}.
|
||||
*/
|
||||
public Proxy setBypass(String bypass) {
|
||||
this.bypass = bypass;
|
||||
|
||||
@@ -19,5 +19,6 @@ package com.microsoft.playwright.options;
|
||||
public enum WaitUntilState {
|
||||
LOAD,
|
||||
DOMCONTENTLOADED,
|
||||
NETWORKIDLE
|
||||
NETWORKIDLE,
|
||||
COMMIT
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import java.io.FileInputStream;
|
||||
import java.security.KeyStore;
|
||||
|
||||
class HttpsConfiguratorImpl extends HttpsConfigurator {
|
||||
@@ -52,8 +51,7 @@ class HttpsConfiguratorImpl extends HttpsConfigurator {
|
||||
String password = "password";
|
||||
// Generated via
|
||||
// keytool -genkey -keyalg RSA -validity 36500 -keysize 4096 -dname cn=Playwright,ou=Playwright,o=Playwright,c=US -keystore keystore.jks -storepass password -keypass password
|
||||
ks.load(new FileInputStream("src/test/resources/keys/keystore.jks"), password.toCharArray());
|
||||
|
||||
ks.load(HttpsConfiguratorImpl.class.getClassLoader().getResourceAsStream("resources/keys/keystore.jks"), password.toCharArray());
|
||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
|
||||
kmf.init(ks, password.toCharArray());
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ public class Server implements HttpHandler {
|
||||
public final String CROSS_PROCESS_PREFIX;
|
||||
public final int PORT;
|
||||
public final String EMPTY_PAGE;
|
||||
private final File resourcesDir;
|
||||
|
||||
private final Map<String, CompletableFuture<Request>> requestSubscribers = Collections.synchronizedMap(new HashMap<>());
|
||||
private final Map<String, Auth> auths = Collections.synchronizedMap(new HashMap<>());
|
||||
@@ -79,7 +78,6 @@ public class Server implements HttpHandler {
|
||||
server.setExecutor(null); // creates a default executor
|
||||
|
||||
File cwd = FileSystems.getDefault().getPath(".").toFile();
|
||||
resourcesDir = new File(cwd, "src/test/resources");
|
||||
server.start();
|
||||
}
|
||||
|
||||
@@ -192,32 +190,42 @@ public class Server implements HttpHandler {
|
||||
if ("/".equals(path)) {
|
||||
path = "/index.html";
|
||||
}
|
||||
File file = new File(resourcesDir, path.substring(1));
|
||||
if (!file.exists()) {
|
||||
|
||||
// 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.sendResponseHeaders(404, 0);
|
||||
try (Writer writer = new OutputStreamWriter(exchange.getResponseBody())) {
|
||||
writer.write("File not found: " + file.getCanonicalPath());
|
||||
writer.write("File not found: " + resourcePath);
|
||||
}
|
||||
return;
|
||||
}
|
||||
exchange.getResponseHeaders().add("Content-Type", mimeType(file));
|
||||
OutputStream output = exchange.getResponseBody();
|
||||
exchange.getResponseHeaders().add("Content-Type", mimeType(new File(resourcePath)));
|
||||
ByteArrayOutputStream body = new ByteArrayOutputStream();
|
||||
OutputStream output = body;
|
||||
if (gzipRoutes.contains(path)) {
|
||||
exchange.getResponseHeaders().add("Content-Encoding", "gzip");
|
||||
}
|
||||
try (FileInputStream input = new FileInputStream(file)) {
|
||||
exchange.sendResponseHeaders(200, 0);
|
||||
try (InputStream input = resource) {
|
||||
if (gzipRoutes.contains(path)) {
|
||||
output = new GZIPOutputStream(output);
|
||||
}
|
||||
copy(input, output);
|
||||
output.close();
|
||||
} catch (IOException e) {
|
||||
try (Writer writer = new OutputStreamWriter(exchange.getResponseBody())) {
|
||||
body.reset();
|
||||
try (Writer writer = new OutputStreamWriter(output)) {
|
||||
writer.write("Exception: " + e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
output.close();
|
||||
long contentLength = body.size();
|
||||
// -1 means no body, 0 means chunked encoding.
|
||||
exchange.sendResponseHeaders(200, contentLength == 0 ? -1 : contentLength);
|
||||
if (contentLength > 0) {
|
||||
exchange.getResponseBody().write(body.toByteArray());
|
||||
}
|
||||
exchange.getResponseBody().close();
|
||||
}
|
||||
|
||||
private static String mimeType(File file) {
|
||||
|
||||
@@ -226,11 +226,20 @@ public class TestBrowserTypeConnect extends TestBase {
|
||||
BrowserServer server = launchBrowserServer(browserType);
|
||||
Browser remote = browserType.connect(server.wsEndpoint);
|
||||
Page page = remote.newPage();
|
||||
boolean[] disconnected = {false};
|
||||
remote.onDisconnected(b -> disconnected[0] = true);
|
||||
server.kill();
|
||||
while (!disconnected[0]) {
|
||||
try {
|
||||
page.waitForTimeout(10);
|
||||
} catch (PlaywrightException e) {
|
||||
}
|
||||
}
|
||||
assertFalse(remote.isConnected());
|
||||
try {
|
||||
page.evaluate("1 + 1");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Playwright connection closed"));
|
||||
assertTrue(e.getMessage().contains("Browser has been closed"), e.getMessage());
|
||||
}
|
||||
assertFalse(remote.isConnected());
|
||||
}
|
||||
@@ -248,7 +257,6 @@ public class TestBrowserTypeConnect extends TestBase {
|
||||
try {
|
||||
page.waitForTimeout(10);
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Playwright connection closed"));
|
||||
}
|
||||
}
|
||||
assertFalse(browser.isConnected());
|
||||
@@ -256,7 +264,7 @@ public class TestBrowserTypeConnect extends TestBase {
|
||||
page.waitForNavigation(() -> {});
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Playwright connection closed"));
|
||||
assertTrue(e.getMessage().contains("Page closed") || e.getMessage().contains("Browser has been closed"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +279,7 @@ public class TestBrowserTypeConnect extends TestBase {
|
||||
page.navigate(server.PREFIX + "/one-style.html", new Page.NavigateOptions().setTimeout(60000));
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Playwright connection closed"));
|
||||
assertTrue(e.getMessage().contains("Browser has been closed"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,14 +61,14 @@ public class TestElementHandleConvenience extends TestBase {
|
||||
page.inputValue("#inner");
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Node is not an HTMLInputElement or HTMLTextAreaElement"), e.getMessage());
|
||||
assertTrue(e.getMessage().contains("Node is not an <input>, <textarea> or <select> element"), e.getMessage());
|
||||
}
|
||||
ElementHandle handle2 = page.querySelector("#inner");
|
||||
try {
|
||||
handle2.inputValue();
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Node is not an HTMLInputElement or HTMLTextAreaElement"), e.getMessage());
|
||||
assertTrue(e.getMessage().contains("Node is not an <input>, <textarea> or <select> element"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,14 +95,14 @@ public class TestElementHandleConvenience extends TestBase {
|
||||
page.innerText("svg");
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Not an HTMLElement"));
|
||||
assertTrue(e.getMessage().contains("Node is not an HTMLElement"), e.getMessage());
|
||||
}
|
||||
ElementHandle handle = page.querySelector("svg");
|
||||
try {
|
||||
handle.innerText();
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Not an HTMLElement"));
|
||||
assertTrue(e.getMessage().contains("Node is not an HTMLElement"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,14 +65,14 @@ public class TestLocatorConvenience extends TestBase {
|
||||
page.inputValue("#inner");
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Node is not an HTMLInputElement or HTMLTextAreaElement or HTMLSelectElement"));
|
||||
assertTrue(e.getMessage().contains("Node is not an <input>, <textarea> or <select> element"), e.getMessage());
|
||||
}
|
||||
try {
|
||||
Locator locator2 = page.locator("#inner");
|
||||
locator2.inputValue();
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Node is not an HTMLInputElement or HTMLTextAreaElement or HTMLSelectElement"));
|
||||
assertTrue(e.getMessage().contains("Node is not an <input>, <textarea> or <select> element"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,14 +99,14 @@ public class TestLocatorConvenience extends TestBase {
|
||||
page.innerText("svg");
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Not an HTMLElement"));
|
||||
assertTrue(e.getMessage().contains("Node is not an HTMLElement"), e.getMessage());
|
||||
}
|
||||
Locator locator = page.locator("svg");
|
||||
try {
|
||||
locator.innerText();
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Not an HTMLElement"));
|
||||
assertTrue(e.getMessage().contains("Node is not an HTMLElement"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,11 @@
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import com.microsoft.playwright.options.WaitForSelectorState;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class TestLocatorMisc extends TestBase{
|
||||
@Test
|
||||
@@ -30,4 +32,21 @@ public class TestLocatorMisc extends TestBase{
|
||||
input.setChecked(false);
|
||||
assertEquals(false, page.evaluate("checkbox.checked"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWaitFor() {
|
||||
page.setContent("<div></div>");
|
||||
Locator locator = page.locator("span");
|
||||
page.evalOnSelector("div", "div => setTimeout(() => div.innerHTML = '<span>target</span>', 500)");
|
||||
locator.waitFor();
|
||||
assertTrue(locator.textContent().contains("target"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWaitForHidden() {
|
||||
page.setContent("<div><span>target</span></div>");
|
||||
Locator locator = page.locator("span");
|
||||
page.evalOnSelector("div", "div => setTimeout(() => div.innerHTML = '', 500)");
|
||||
locator.waitFor(new Locator.WaitForOptions().setState(WaitForSelectorState.HIDDEN));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,18 @@ public class TestNetworkRequest extends TestBase {
|
||||
assertEquals(server.PREFIX + "/empty.html", requests.get(1).url());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkAllHeadersInsideRoute() {
|
||||
List<Request> requests = new ArrayList<>();
|
||||
page.route("**", route -> {
|
||||
assertTrue(route.request().allHeaders().get("accept").length() > 5);
|
||||
requests.add(route.request());
|
||||
route.resume();
|
||||
});
|
||||
page.navigate(server.PREFIX + "/empty.html");
|
||||
assertEquals(1, requests.size());
|
||||
}
|
||||
|
||||
// https://github.com/microsoft/playwright/issues/3993
|
||||
@Test
|
||||
void shouldNotWorkForARedirectAndInterception() {
|
||||
|
||||
@@ -53,13 +53,12 @@ public class TestPageNetworkSizes extends TestBase {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("responseBodySize == 16")
|
||||
void shouldSetBodySizeHeadersSizeAndTransferSize() throws ExecutionException, InterruptedException {
|
||||
server.setRoute("/get", exchange -> {
|
||||
// In Firefox, |fetch| will be hanging until it receives |Content-Type| header
|
||||
// from server.
|
||||
exchange.getResponseHeaders().add("Content-Type", "text/plain; charset=utf-8");
|
||||
exchange.sendResponseHeaders(200, 0);
|
||||
exchange.sendResponseHeaders(200, 6);
|
||||
try (OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody())) {
|
||||
writer.write("abc134");
|
||||
}
|
||||
@@ -70,21 +69,20 @@ public class TestPageNetworkSizes extends TestBase {
|
||||
() -> page.evaluate("async () => fetch('./get').then(r => r.text())"));
|
||||
request.get();
|
||||
Sizes sizes = response.request().sizes();
|
||||
assertEquals("abc134", response.text());
|
||||
assertEquals(6, sizes.responseBodySize);
|
||||
assertTrue(sizes.responseHeadersSize >= 100);
|
||||
assertTrue(sizes.responseHeadersSize > 10);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("responseBodySize == 5")
|
||||
void shouldSetBodySizeTo0WhenThereWasNoResponseBody() {
|
||||
Response response = page.navigate(server.EMPTY_PAGE);
|
||||
Sizes sizes = response.request().sizes();
|
||||
assertEquals(0, sizes.responseBodySize);
|
||||
assertTrue(sizes.responseHeadersSize >= 100, "" + sizes.responseHeadersSize);
|
||||
assertTrue(sizes.responseHeadersSize > 10, "" + sizes.responseHeadersSize);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("responseBodySize == 0")
|
||||
void shouldHaveTheCorrectResponseBodySize() throws IOException {
|
||||
Response response = page.navigate(server.PREFIX + "/simplezip.json");
|
||||
Sizes sizes = response.request().sizes();
|
||||
|
||||
@@ -141,7 +141,7 @@ public class TestPageSelectOption extends TestBase {
|
||||
page.selectOption("body", "");
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Element is not a <select> element."));
|
||||
assertTrue(e.getMessage().contains("Element is not a <select> element"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import com.microsoft.playwright.options.WaitUntilState;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static com.microsoft.playwright.options.WaitUntilState.COMMIT;
|
||||
import static com.microsoft.playwright.options.WaitUntilState.DOMCONTENTLOADED;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestPageSetContent extends TestBase {
|
||||
private static final String expectedOutput = "<html><head></head><body><div>hello</div></body></html>";
|
||||
|
||||
@Test
|
||||
void shouldWork() {
|
||||
page.setContent("<div>hello</div>");
|
||||
Object result = page.content();
|
||||
assertEquals(expectedOutput, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithDomcontentloaded() {
|
||||
page.setContent("<div>hello</div>", new Page.SetContentOptions().setWaitUntil(DOMCONTENTLOADED));
|
||||
Object result = page.content();
|
||||
assertEquals(expectedOutput, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithCommit() {
|
||||
page.setContent("<div>hello</div>", new Page.SetContentOptions().setWaitUntil(COMMIT));
|
||||
Object result = page.content();
|
||||
assertEquals(expectedOutput, result);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void shouldWorkWithDoctype() {
|
||||
String doctype = "<!DOCTYPE html>";
|
||||
page.setContent(doctype + "<div>hello</div>");
|
||||
Object result = page.content();
|
||||
assertEquals(doctype + expectedOutput, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithHTML4Doctype() {
|
||||
String doctype = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" " +
|
||||
"\"http://www.w3.org/TR/html4/strict.dtd\">";
|
||||
page.setContent(doctype + "<div>hello</div>");
|
||||
Object result = page.content();
|
||||
assertEquals(doctype + expectedOutput, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRespectTimeout() {
|
||||
String imgPath = "/img.png";
|
||||
// stall for image
|
||||
server.setRoute(imgPath, exchange -> {});
|
||||
try {
|
||||
page.setContent("<img src='" + server.PREFIX + imgPath + "'></img>", new Page.SetContentOptions().setTimeout(100));
|
||||
fail("did not throw");
|
||||
} catch (TimeoutError e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRespectDefaultNavigationTimeout() {
|
||||
page.setDefaultNavigationTimeout(100);
|
||||
String imgPath = "/img.png";
|
||||
// stall for image
|
||||
server.setRoute(imgPath, exchange -> {});
|
||||
try {
|
||||
page.setContent("<img src='" + server.PREFIX + imgPath + "'></img>");
|
||||
fail("did not throw");
|
||||
} catch (TimeoutError e) {
|
||||
assertTrue(e.getMessage().contains("Timeout 100ms exceeded."), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkFastEnough() {
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
page.setContent("<div>yo</div>");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithTrickyContent() {
|
||||
page.setContent("<div>hello world</div>" + "\\x7F");
|
||||
assertEquals("hello world", page.evalOnSelector("div", "div => div.textContent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithAccents() {
|
||||
page.setContent("<div>aberración</div>");
|
||||
assertEquals("aberración", page.evalOnSelector("div", "div => div.textContent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithEmojis() {
|
||||
page.setContent("<div>🐥</div>");
|
||||
assertEquals("🐥", page.evalOnSelector("div", "div => div.textContent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithNewline() {
|
||||
page.setContent("<div>\n</div>");
|
||||
assertEquals("\n", page.evalOnSelector("div", "div => div.textContent"));
|
||||
}
|
||||
}
|
||||
@@ -19,12 +19,14 @@ package com.microsoft.playwright;
|
||||
import com.microsoft.playwright.options.WaitUntilState;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.microsoft.playwright.Utils.expectedSSLError;
|
||||
import static java.util.Collections.nCopies;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestPageWaitForNavigation extends TestBase {
|
||||
@@ -47,8 +49,6 @@ public class TestPageWaitForNavigation extends TestBase {
|
||||
fail("did not throw");
|
||||
} catch (TimeoutError e) {
|
||||
assertTrue(e.getMessage().contains("Timeout 5000ms exceeded"));
|
||||
// assertTrue(e.getMessage().contains("waiting for navigation to '**/frame.html' until 'load'"));
|
||||
// assertTrue(e.getMessage().contains("navigated to '${server.EMPTY_PAGE}'"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,23 @@ public class TestPageWaitForNavigation extends TestBase {
|
||||
void shouldWorkWithBothDomcontentloadedAndLoad() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithCommit() {
|
||||
server.setRoute("/empty.html", exchange -> {
|
||||
exchange.getResponseHeaders().add("Content-Type", "text/html");
|
||||
exchange.sendResponseHeaders(200, 8192);
|
||||
OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody());
|
||||
writer.write("<title>" + String.join("", nCopies(4100, "A")));
|
||||
writer.flush();
|
||||
});
|
||||
page.waitForNavigation(new Page.WaitForNavigationOptions().setWaitUntil(WaitUntilState.COMMIT), () -> {
|
||||
try {
|
||||
page.navigate(server.EMPTY_PAGE, new Page.NavigateOptions().setTimeout(100));
|
||||
} catch (TimeoutError e) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithClickingOnAnchorLinks() {
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
|
||||
@@ -19,6 +19,11 @@ package com.microsoft.playwright;
|
||||
import com.microsoft.playwright.options.WaitUntilState;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import static java.util.Collections.nCopies;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestPageWaitForUrl extends TestBase {
|
||||
@@ -47,6 +52,27 @@ public class TestPageWaitForUrl extends TestBase {
|
||||
page.waitForURL("**/one-style.html", new Page.WaitForURLOptions().setWaitUntil(WaitUntilState.LOAD));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithCommit() {
|
||||
server.setRoute("/empty.html", exchange -> {
|
||||
exchange.getResponseHeaders().add("Content-Type", "text/html");
|
||||
exchange.sendResponseHeaders(200, 8192);
|
||||
OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody());
|
||||
writer.write("<title>" + String.join("", nCopies(4100, "A")));
|
||||
writer.flush();
|
||||
});
|
||||
try {
|
||||
page.navigate(server.EMPTY_PAGE, new Page.NavigateOptions().setTimeout(100));
|
||||
} catch (TimeoutError e) {
|
||||
}
|
||||
page.waitForURL("**/empty.html", new Page.WaitForURLOptions().setWaitUntil(WaitUntilState.COMMIT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithCommitAndAboutBlank() {
|
||||
page.waitForURL("about:blank", new Page.WaitForURLOptions().setWaitUntil(WaitUntilState.COMMIT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithClickingOnAnchorLinks() {
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -72,6 +73,7 @@ public class TestRequestContinue extends TestBase {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("resume() method is now asynchronous")
|
||||
void shouldNotAllowChangingProtocolWhenOverridingUrl() {
|
||||
PlaywrightException[] error = {null};
|
||||
page.route("**/*", route -> {
|
||||
|
||||
@@ -41,8 +41,8 @@ public class TestWheel extends TestBase {
|
||||
"shiftKey", false,
|
||||
"altKey", false,
|
||||
"metaKey", false);
|
||||
assertEquals(expected, page.evaluate("window.lastEvent"));
|
||||
page.waitForFunction("window.scrollY === 100");
|
||||
assertEquals(expected, page.evaluate("window.lastEvent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -51,7 +51,7 @@ public class TestWorkers extends TestBase {
|
||||
try {
|
||||
workerThisObj.getProperty("self");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Target closed"), e.getMessage());
|
||||
assertTrue(e.getMessage().contains("Target closed") || e.getMessage().contains("Worker was closed"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>parent-pom</artifactId>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.17.2</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Playwright Parent Project</name>
|
||||
<description>Java library to automate Chromium, Firefox and WebKit with a single API.
|
||||
@@ -39,6 +39,7 @@
|
||||
<module>driver</module>
|
||||
<module>driver-bundle</module>
|
||||
<module>playwright</module>
|
||||
<module>assertions</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
@@ -47,6 +48,7 @@
|
||||
<junit.version>5.7.0</junit.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<websocket.version>1.5.1</websocket.version>
|
||||
<opentest4j.version>1.2.0</opentest4j.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
@@ -66,6 +68,11 @@
|
||||
<artifactId>gson</artifactId>
|
||||
<version>${gson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.opentest4j</groupId>
|
||||
<artifactId>opentest4j</artifactId>
|
||||
<version>${opentest4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
@@ -76,6 +83,7 @@
|
||||
<groupId>org.java-websocket</groupId>
|
||||
<artifactId>Java-WebSocket</artifactId>
|
||||
<version>${websocket.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
@@ -108,6 +116,18 @@
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<configuration>
|
||||
<additionalOptions>--allow-script-in-comments</additionalOptions>
|
||||
<failOnError>false</failOnError>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
@@ -123,9 +143,7 @@
|
||||
junit.jupiter.execution.parallel.config.dynamic.factor=0.5
|
||||
</configurationParameters>
|
||||
</properties>
|
||||
<failIfNoTests>
|
||||
false
|
||||
</failIfNoTests>
|
||||
<failIfNoTests>false</failIfNoTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
1.15.0-1631797286000
|
||||
1.17.1
|
||||
|
||||
@@ -33,7 +33,7 @@ fi
|
||||
mkdir -p driver
|
||||
cd driver
|
||||
|
||||
for PLATFORM in mac linux win32 win32_x64
|
||||
for PLATFORM in mac linux win32_x64
|
||||
do
|
||||
FILE_NAME=$FILE_PREFIX-$PLATFORM.zip
|
||||
if [[ -d $PLATFORM ]]; then
|
||||
@@ -45,7 +45,7 @@ do
|
||||
echo "Downloading driver for $PLATFORM to $(pwd)"
|
||||
|
||||
URL=https://playwright.azureedge.net/builds/driver
|
||||
if ! [[ $CLI_VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
if [[ "$CLI_VERSION" == *-alpha* || "$CLI_VERSION" == *-beta* || "$CLI_VERSION" == *-next* ]]; then
|
||||
URL=$URL/next
|
||||
fi
|
||||
URL=$URL/$FILE_NAME
|
||||
|
||||
@@ -14,9 +14,6 @@ Darwin)
|
||||
Linux)
|
||||
PLAYWRIGHT_CLI=./driver-bundle/src/main/resources/driver/linux/playwright.sh
|
||||
;;
|
||||
MINGW32*)
|
||||
PLAYWRIGHT_CLI=./driver-bundle/src/main/resources/driver/win32/playwright.cmd
|
||||
;;
|
||||
MINGW64*)
|
||||
PLAYWRIGHT_CLI=./driver-bundle/src/main/resources/driver/win32_x64/playwright.cmd
|
||||
;;
|
||||
|
||||
@@ -26,10 +26,6 @@ Linux)
|
||||
PLATFORM=linux
|
||||
echo "Downloading driver for Linux"
|
||||
;;
|
||||
MINGW32*)
|
||||
PLATFORM=win32
|
||||
echo "Downloading driver for Win32"
|
||||
;;
|
||||
MINGW64*)
|
||||
PLATFORM=win32_x64
|
||||
echo "Downloading driver for Win64"
|
||||
|
||||
@@ -25,5 +25,5 @@ POM_FILES=(
|
||||
|
||||
for name in ${POM_FILES[*]};
|
||||
do
|
||||
mvn versions:set -D newVersion=$VERSION -f $name
|
||||
mvn versions:set -D generateBackupPoms=false -D newVersion=$VERSION -f $name
|
||||
done
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>api-generator</artifactId>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.17.2</version>
|
||||
<name>Playwright - API Generator</name>
|
||||
<description>
|
||||
This is an internal module used to generate Java API from the upstream Playwright
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.google.gson.JsonObject;
|
||||
import java.io.*;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -627,6 +628,24 @@ class Method extends Element {
|
||||
output.add(offset + "}");
|
||||
return;
|
||||
}
|
||||
if ("PlaywrightAssertions.assertThat".equals(jsonPath)) {
|
||||
writeJavadoc(params, output, offset);
|
||||
String originalName = jsonElement.getAsJsonObject().get("originalName").getAsString();
|
||||
if ("assertThatPage".equals(originalName)) {
|
||||
output.add(offset + "static PageAssertions assertThat(Page page) {");
|
||||
output.add(offset + " return new PageAssertionsImpl(page);");
|
||||
output.add(offset + "}");
|
||||
output.add("");
|
||||
} else if ("assertThatLocator".equals(originalName)) {
|
||||
output.add(offset + "static LocatorAssertions assertThat(Locator locator) {");
|
||||
output.add(offset + " return new LocatorAssertionsImpl(locator);");
|
||||
output.add(offset + "}");
|
||||
output.add("");
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected originalName: " + originalName);
|
||||
}
|
||||
return;
|
||||
}
|
||||
int numOverloads = 1;
|
||||
for (int i = 0; i < params.size(); i++) {
|
||||
if (params.get(i).type.isTypeUnion()) {
|
||||
@@ -844,9 +863,7 @@ class Interface extends TypeDefinition {
|
||||
" * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" +
|
||||
" * See the License for the specific language governing permissions and\n" +
|
||||
" * limitations under the License.\n" +
|
||||
" */\n" +
|
||||
"\n" +
|
||||
"package com.microsoft.playwright;\n";
|
||||
" */\n";
|
||||
|
||||
private static Set<String> allowedBaseInterfaces = new HashSet<>(asList("Browser", "JSHandle", "BrowserContext"));
|
||||
private static Set<String> autoCloseableInterfaces = new HashSet<>(asList("Playwright", "Browser", "BrowserContext", "Page"));
|
||||
@@ -877,7 +894,6 @@ class Interface extends TypeDefinition {
|
||||
}
|
||||
|
||||
void writeTo(List<String> output, String offset) {
|
||||
output.add(header);
|
||||
if ("Playwright".equals(jsonName)) {
|
||||
output.add("import com.microsoft.playwright.impl.PlaywrightImpl;");
|
||||
}
|
||||
@@ -900,9 +916,21 @@ class Interface extends TypeDefinition {
|
||||
if (asList("Page", "Frame", "BrowserContext", "WebSocket").contains(jsonName)) {
|
||||
output.add("import java.util.function.Predicate;");
|
||||
}
|
||||
if (asList("Page", "Frame", "BrowserContext").contains(jsonName)) {
|
||||
if (asList("Page", "Frame", "BrowserContext", "PageAssertions", "LocatorAssertions").contains(jsonName)) {
|
||||
output.add("import java.util.regex.Pattern;");
|
||||
}
|
||||
if ("PlaywrightAssertions".equals(jsonName)) {
|
||||
output.add("import com.microsoft.playwright.Locator;");
|
||||
output.add("import com.microsoft.playwright.Page;");
|
||||
output.add("import com.microsoft.playwright.impl.LocatorAssertionsImpl;");
|
||||
output.add("import com.microsoft.playwright.impl.PageAssertionsImpl;");
|
||||
}
|
||||
if ("PageAssertions".equals(jsonName)) {
|
||||
output.add("import com.microsoft.playwright.Page;");
|
||||
}
|
||||
if ("LocatorAssertions".equals(jsonName)) {
|
||||
output.add("import com.microsoft.playwright.Locator;");
|
||||
}
|
||||
output.add("");
|
||||
|
||||
List<String> superInterfaces = new ArrayList<>();
|
||||
@@ -1062,10 +1090,22 @@ public class ApiGenerator {
|
||||
ApiGenerator(Reader reader) throws IOException {
|
||||
JsonArray api = new Gson().fromJson(reader, JsonArray.class);
|
||||
File cwd = FileSystems.getDefault().getPath(".").toFile();
|
||||
filterOtherLangs(api, new Stack<>());
|
||||
|
||||
File dir = new File(cwd, "playwright/src/main/java/com/microsoft/playwright");
|
||||
System.out.println("Writing files to: " + dir.getCanonicalPath());
|
||||
Stack<String> path = new Stack<>();
|
||||
filterOtherLangs(api, path);
|
||||
generate(api, dir, "com.microsoft.playwright", isAssertion().negate());
|
||||
|
||||
File assertionsDir = new File(cwd,"assertions/src/main/java/com/microsoft/playwright/assertions");
|
||||
System.out.println("Writing assertion files to: " + dir.getCanonicalPath());
|
||||
generate(api, assertionsDir, "com.microsoft.playwright.assertions", isAssertion());
|
||||
}
|
||||
|
||||
private static Predicate<String> isAssertion() {
|
||||
return className -> className.toLowerCase().contains("assert");
|
||||
}
|
||||
|
||||
private void generate(JsonArray api, File dir, String packageName, Predicate<String> classFilter) throws IOException {
|
||||
Map<String, TypeDefinition> topLevelTypes = new HashMap<>();
|
||||
for (JsonElement entry: api) {
|
||||
String name = entry.getAsJsonObject().get("name").getAsString();
|
||||
@@ -1073,17 +1113,26 @@ public class ApiGenerator {
|
||||
if (asList("PlaywrightException", "TimeoutError").contains(name)) {
|
||||
continue;
|
||||
}
|
||||
if (!classFilter.test(name)) {
|
||||
continue;
|
||||
}
|
||||
List<String> lines = new ArrayList<>();
|
||||
lines.add(Interface.header);
|
||||
lines.add("package " + packageName + ";");
|
||||
lines.add("");
|
||||
new Interface(entry.getAsJsonObject(), topLevelTypes).writeTo(lines, "");
|
||||
String text = String.join("\n", lines);
|
||||
try (FileWriter writer = new FileWriter(new File(dir, name + ".java"))) {
|
||||
writer.write(text);
|
||||
}
|
||||
}
|
||||
|
||||
dir = new File(dir, "options");
|
||||
for (TypeDefinition e : topLevelTypes.values()) {
|
||||
List<String> lines = new ArrayList<>();
|
||||
lines.add(Interface.header.replace("package com.microsoft.playwright;", "package com.microsoft.playwright.options;"));
|
||||
lines.add(Interface.header);
|
||||
lines.add("package " + packageName + ".options;");
|
||||
lines.add("");
|
||||
e.writeTo(lines, "");
|
||||
String text = String.join("\n", lines);
|
||||
try (FileWriter writer = new FileWriter(new File(dir, e.name() + ".java"))) {
|
||||
@@ -1117,6 +1166,8 @@ public class ApiGenerator {
|
||||
List<String> aliasPath = new ArrayList<>(path);
|
||||
aliasPath.set(aliasPath.size() - 1, alias);
|
||||
aliases.put(String.join(".", path), String.join(".", aliasPath));
|
||||
// Save original name.
|
||||
object.addProperty("originalName", object.get("name").getAsString());
|
||||
// Rename in place.
|
||||
object.addProperty("name", alias);
|
||||
}
|
||||
@@ -1189,7 +1240,6 @@ public class ApiGenerator {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
File cwd = FileSystems.getDefault().getPath(".").toFile();
|
||||
System.out.println(cwd.getCanonicalPath());
|
||||
File file = new File(cwd, "tools/api-generator/src/main/resources/api.json");
|
||||
System.out.println("Reading from: " + file.getCanonicalPath());
|
||||
new ApiGenerator(new FileReader(file));
|
||||
|
||||
@@ -10,6 +10,7 @@ cd "$(dirname $0)"
|
||||
PROJECT_DIR=$(mktemp -d)
|
||||
echo "Creating project in $PROJECT_DIR"
|
||||
cp -R . $PROJECT_DIR
|
||||
cp -R ../../assertions/src/test/ $PROJECT_DIR/src/
|
||||
cp -R ../../driver-bundle/src/test/ $PROJECT_DIR/src/
|
||||
cp -R ../../playwright/src/test/ $PROJECT_DIR/src/
|
||||
cd $PROJECT_DIR
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>test-local-installation</artifactId>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.17.2</version>
|
||||
<name>Test local installation</name>
|
||||
<description>Runs Playwright test suite (copied from playwright module) against locally cached Playwright</description>
|
||||
<properties>
|
||||
@@ -20,6 +20,18 @@
|
||||
<artifactId>playwright</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>playwright</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>assertions</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
@@ -52,9 +64,6 @@
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.0.0-M5</version>
|
||||
<configuration>
|
||||
<redirectTestOutputToFile>true</redirectTestOutputToFile>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</parent>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>test-spring-boot-starter</artifactId>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.17.2</version>
|
||||
<name>Test Playwright With Spring Boot</name>
|
||||
<properties>
|
||||
<spring.version>2.4.3</spring.version>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>update-version</artifactId>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.17.2</version>
|
||||
<name>Playwright - Update Version in Documentation</name>
|
||||
<description>
|
||||
This is an internal module used to update versions in the documentation based on
|
||||
|
||||
Reference in New Issue
Block a user