feat: add web-first assertions for page (#657)

This commit is contained in:
Yury Semikhatsky 2021-10-21 18:22:00 -07:00 committed by GitHub
parent 38c5dc28a4
commit b7319c629d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 635 additions and 65 deletions

72
assertions/pom.xml Normal file
View File

@ -0,0 +1,72 @@
<?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.0-SNAPSHOT</version>
</parent>
<artifactId>assertions</artifactId>
<name>Playwright - Assertions</name>
<description>
This module provides AssertJ Matchers specific to Playwright.
</description>
<build>
<plugins>
<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>
<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>
<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>

View File

@ -0,0 +1,145 @@
/*
* 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.
*/
public interface PageAssertions {
class HasTitleOptions {
/**
* Time to retry assertion for.
*/
public Double timeout;
/**
* Time to retry 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 a 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 a 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 a 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 a 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();
}

View File

@ -0,0 +1,53 @@
/*
* 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.Page;
import com.microsoft.playwright.Locator;
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
* 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.
*/
public interface PlaywrightAssertions {
/**
* 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);
}
}

View File

@ -0,0 +1,100 @@
/*
* 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 org.opentest4j.AssertionFailedError;
import java.util.regex.Pattern;
import static com.microsoft.playwright.impl.UrlMatcher.resolveUrl;
import static java.util.Arrays.asList;
public class PageAssertionsImpl implements PageAssertions {
private final PageImpl actual;
private final boolean isNot;
public PageAssertionsImpl(Page page) {
this(page, false);
}
private PageAssertionsImpl(Page page, boolean isNot) {
this.actual = (PageImpl) page;
this.isNot = isNot;
}
@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", options == null ? null : options.timeout);
}
@Override
public void hasTitle(Pattern pattern, HasTitleOptions options) {
//urlOrRegExp.flags();
ExpectedTextValue expected = new ExpectedTextValue();
expected.regexSource = pattern.pattern();
// expected.regexFlags =
expectImpl("to.have.title", expected, pattern, "Page title expected to match regex", options == null ? null : options.timeout);
}
@Override
public void hasURL(String url, HasURLOptions options) {
ExpectedTextValue expected = new ExpectedTextValue();
if (actual.context().baseUrl != null) {
url = resolveUrl(actual.context().baseUrl, url);
}
expected.string = url;
expectImpl("to.have.url", expected, url, "Page URL expected to be", options == null ? null : options.timeout);
}
@Override
public void hasURL(Pattern pattern, HasURLOptions options) {
//urlOrRegExp.flags();
ExpectedTextValue expected = new ExpectedTextValue();
expected.regexSource = pattern.pattern();
// expected.regexFlags =
expectImpl("to.have.url", expected, pattern, "Page URL expected to match regex", options == null ? null : options.timeout);
}
private void expectImpl(String expression, ExpectedTextValue textValue, Object expected, String message, Double timeout) {
LocatorImpl locator = actual.locator(":root");
FrameExpectOptions expectOptions = new FrameExpectOptions();
expectOptions.expectedText = asList(textValue);
expectOptions.isNot = isNot;
expectOptions.timeout = timeout;
FrameExpectResult result = locator.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;
}
throw new AssertionFailedError(message + log, expected, actual);
}
}
@Override
public PageAssertions not() {
return new PageAssertionsImpl(actual, !isNot);
}
}

View File

@ -0,0 +1,131 @@
/*
* 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"));
}
}

View File

@ -51,7 +51,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>

View File

@ -560,7 +560,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
}
@Override
public Locator locator(String selector) {
public LocatorImpl locator(String selector) {
return new LocatorImpl(this, selector);
}

View File

@ -1,5 +1,7 @@
package com.microsoft.playwright.impl;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.microsoft.playwright.ElementHandle;
import com.microsoft.playwright.Frame;
import com.microsoft.playwright.JSHandle;
@ -13,6 +15,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 {
@ -415,4 +419,24 @@ class LocatorImpl implements Locator {
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);
if (options.expectedValue != null) {
params.remove("expectedValue");
params.add("expectedValue", gson().toJsonTree(serializeArgument(options.expectedValue)));
}
JsonElement json = frame.sendMessage("expect", params);
FrameExpectResult result = gson().fromJson(json, FrameExpectResult.class);
return result;
}
}

View File

@ -875,7 +875,7 @@ public class PageImpl extends ChannelOwner implements Page {
}
@Override
public Locator locator(String selector) {
public LocatorImpl locator(String selector) {
return mainFrame.locator(selector);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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());

View File

@ -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,21 +190,24 @@ 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));
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)) {
try (InputStream input = resource) {
if (gzipRoutes.contains(path)) {
output = new GZIPOutputStream(output);
}

View File

@ -47,8 +47,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}'"));
}
}

View File

@ -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>

View File

@ -1 +1 @@
1.16.0-1634781227000
1.17.0-next-1634863457000

View File

@ -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,14 @@ class Method extends Element {
output.add(offset + "}");
return;
}
if ("PlaywrightAssertions.assertThat".equals(jsonPath)) {
writeJavadoc(params, output, offset);
output.add(offset + "static PageAssertions assertThat(Page page) {");
output.add(offset + " return new PageAssertionsImpl(page);");
output.add(offset + "}");
output.add("");
return;
}
int numOverloads = 1;
for (int i = 0; i < params.size(); i++) {
if (params.get(i).type.isTypeUnion()) {
@ -844,9 +853,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 +884,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 +906,20 @@ 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").contains(jsonName)) {
output.add("import java.util.regex.Pattern;");
}
if ("PlaywrightAssertions".equals(jsonName)) {
output.add("import com.microsoft.playwright.Page;");
output.add("import com.microsoft.playwright.Locator;");
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 +1079,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 +1102,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"))) {