diff --git a/assertions/pom.xml b/assertions/pom.xml
new file mode 100644
index 00000000..a0b16dee
--- /dev/null
+++ b/assertions/pom.xml
@@ -0,0 +1,72 @@
+
+
+ 4.0.0
+
+ com.microsoft.playwright
+ parent-pom
+ 1.17.0-SNAPSHOT
+
+
+ assertions
+ Playwright - Assertions
+
+ This module provides AssertJ Matchers specific to Playwright.
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+ 8
+ false
+
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ true
+
+
+
+
+
+
+ org.opentest4j
+ opentest4j
+
+
+ com.microsoft.playwright
+ playwright
+ ${project.version}
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+
+
+ com.microsoft.playwright
+ playwright
+ ${project.version}
+ test-jar
+ test
+
+
+
+
diff --git a/assertions/src/main/java/com/microsoft/playwright/assertions/PageAssertions.java b/assertions/src/main/java/com/microsoft/playwright/assertions/PageAssertions.java
new file mode 100644
index 00000000..070a4690
--- /dev/null
+++ b/assertions/src/main/java/com/microsoft/playwright/assertions/PageAssertions.java
@@ -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.
+ *
{@code
+ * assertThat(page).hasTitle("Playwright");
+ * }
+ *
+ * @param titleOrRegExp Expected title or RegExp.
+ */
+ default void hasTitle(String titleOrRegExp) {
+ hasTitle(titleOrRegExp, null);
+ }
+ /**
+ * Ensures the page has a given title.
+ * {@code
+ * assertThat(page).hasTitle("Playwright");
+ * }
+ *
+ * @param titleOrRegExp Expected title or RegExp.
+ */
+ void hasTitle(String titleOrRegExp, HasTitleOptions options);
+ /**
+ * Ensures the page has a given title.
+ * {@code
+ * assertThat(page).hasTitle("Playwright");
+ * }
+ *
+ * @param titleOrRegExp Expected title or RegExp.
+ */
+ default void hasTitle(Pattern titleOrRegExp) {
+ hasTitle(titleOrRegExp, null);
+ }
+ /**
+ * Ensures the page has a given title.
+ * {@code
+ * assertThat(page).hasTitle("Playwright");
+ * }
+ *
+ * @param titleOrRegExp Expected title or RegExp.
+ */
+ void hasTitle(Pattern titleOrRegExp, HasTitleOptions options);
+ /**
+ * Ensures the page is navigated to the given URL.
+ * {@code
+ * assertThat(page).hasURL('.com');
+ * }
+ *
+ * @param urlOrRegExp Expected substring or RegExp.
+ */
+ default void hasURL(String urlOrRegExp) {
+ hasURL(urlOrRegExp, null);
+ }
+ /**
+ * Ensures the page is navigated to the given URL.
+ * {@code
+ * assertThat(page).hasURL('.com');
+ * }
+ *
+ * @param urlOrRegExp Expected substring or RegExp.
+ */
+ void hasURL(String urlOrRegExp, HasURLOptions options);
+ /**
+ * Ensures the page is navigated to the given URL.
+ * {@code
+ * assertThat(page).hasURL('.com');
+ * }
+ *
+ * @param urlOrRegExp Expected substring or RegExp.
+ */
+ default void hasURL(Pattern urlOrRegExp) {
+ hasURL(urlOrRegExp, null);
+ }
+ /**
+ * Ensures the page is navigated to the given URL.
+ * {@code
+ * assertThat(page).hasURL('.com');
+ * }
+ *
+ * @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"}:
+ * {@code
+ * assertThat(page).not().hasURL('error');
+ * }
+ */
+ PageAssertions not();
+}
+
diff --git a/assertions/src/main/java/com/microsoft/playwright/assertions/PlaywrightAssertions.java b/assertions/src/main/java/com/microsoft/playwright/assertions/PlaywrightAssertions.java
new file mode 100644
index 00000000..880bb2f0
--- /dev/null
+++ b/assertions/src/main/java/com/microsoft/playwright/assertions/PlaywrightAssertions.java
@@ -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.
+ *
+ * Consider the following example:
+ *
{@code
+ * assertThat(page.locator('.status')).hasText('Submitted');
+ * }
+ *
+ * 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.
+ *
+ *
By default, the timeout for assertions is set to 5 seconds.
+ */
+public interface PlaywrightAssertions {
+ /**
+ * Creates a {@code PageAssertions} object for the given {@code Page}.
+ *
{@code
+ * PlaywrightAssertions.assertThat(page).hasTitle("News");
+ * }
+ *
+ * @param page {@code Page} object to use for assertions.
+ */
+ static PageAssertions assertThat(Page page) {
+ return new PageAssertionsImpl(page);
+ }
+
+}
+
diff --git a/assertions/src/main/java/com/microsoft/playwright/impl/PageAssertionsImpl.java b/assertions/src/main/java/com/microsoft/playwright/impl/PageAssertionsImpl.java
new file mode 100644
index 00000000..7a1bfd26
--- /dev/null
+++ b/assertions/src/main/java/com/microsoft/playwright/impl/PageAssertionsImpl.java
@@ -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);
+ }
+
+
+}
+
diff --git a/assertions/src/test/java/com/microsoft/playwright/TestPageAssertions.java b/assertions/src/test/java/com/microsoft/playwright/TestPageAssertions.java
new file mode 100644
index 00000000..925a0752
--- /dev/null
+++ b/assertions/src/test/java/com/microsoft/playwright/TestPageAssertions.java
@@ -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,A
");
+ assertThat(page).hasURL("data:text/html,A
");
+ }
+
+ @Test
+ void hasURLTextFail() {
+ page.navigate("data:text/html,B
");
+ 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,B
", 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,B
");
+ assertThat(page).not().hasURL("about:blank", new PageAssertions.HasURLOptions().setTimeout(1000));
+ }
+
+ @Test
+ void hasURLRegexPass() {
+ page.navigate("data:text/html,A
");
+ 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,B
");
+ 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"));
+ }
+}
diff --git a/playwright/pom.xml b/playwright/pom.xml
index 32da0891..0a123aac 100644
--- a/playwright/pom.xml
+++ b/playwright/pom.xml
@@ -51,7 +51,25 @@
org.apache.maven.plugins
maven-surefire-plugin
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 2.4
+
+
+
+ test-jar
+
+
+
+
+
+
+ src/test/resources
+ resources
+
+
diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/FrameImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/FrameImpl.java
index f201822b..ab6e1feb 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/FrameImpl.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/FrameImpl.java
@@ -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);
}
diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/LocatorImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/LocatorImpl.java
index b5f35ee1..7136f908 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/LocatorImpl.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/LocatorImpl.java
@@ -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;
+ }
}
diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/PageImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/PageImpl.java
index ab1bc235..bb364110 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/PageImpl.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/PageImpl.java
@@ -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);
}
diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/Protocol.java b/playwright/src/main/java/com/microsoft/playwright/impl/Protocol.java
index fdfccf0b..48162f69 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/Protocol.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/Protocol.java
@@ -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 expectedText;
+ Integer expectedNumber;
+ SerializedArgument expectedValue;
+ Boolean useInnerText;
+ boolean isNot;
+ Double timeout;
+}
+
+class FrameExpectResult {
+ boolean matches;
+ SerializedValue received;
+ List log;
+}
+
+
diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/UrlMatcher.java b/playwright/src/main/java/com/microsoft/playwright/impl/UrlMatcher.java
index c0c599e9..de865195 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/UrlMatcher.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/UrlMatcher.java
@@ -54,7 +54,7 @@ class UrlMatcher {
throw new PlaywrightException("Url must be String, Pattern or Predicate, found: " + object.getClass().getTypeName());
}
- private static String resolveUrl(URL baseUrl, String spec) {
+ static String resolveUrl(URL baseUrl, String spec) {
if (baseUrl == null) {
return spec;
}
diff --git a/playwright/src/test/java/com/microsoft/playwright/HttpsConfiguratorImpl.java b/playwright/src/test/java/com/microsoft/playwright/HttpsConfiguratorImpl.java
index ab7e6ba0..d96d05b3 100644
--- a/playwright/src/test/java/com/microsoft/playwright/HttpsConfiguratorImpl.java
+++ b/playwright/src/test/java/com/microsoft/playwright/HttpsConfiguratorImpl.java
@@ -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());
diff --git a/playwright/src/test/java/com/microsoft/playwright/Server.java b/playwright/src/test/java/com/microsoft/playwright/Server.java
index 6010f103..1a7e2c51 100644
--- a/playwright/src/test/java/com/microsoft/playwright/Server.java
+++ b/playwright/src/test/java/com/microsoft/playwright/Server.java
@@ -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> requestSubscribers = Collections.synchronizedMap(new HashMap<>());
private final Map 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);
}
diff --git a/playwright/src/test/java/com/microsoft/playwright/TestPageWaitForNavigation.java b/playwright/src/test/java/com/microsoft/playwright/TestPageWaitForNavigation.java
index 9f8a96f4..e5de4a32 100644
--- a/playwright/src/test/java/com/microsoft/playwright/TestPageWaitForNavigation.java
+++ b/playwright/src/test/java/com/microsoft/playwright/TestPageWaitForNavigation.java
@@ -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}'"));
}
}
diff --git a/pom.xml b/pom.xml
index 0f6aac65..8162e586 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,6 +39,7 @@
driver
driver-bundle
playwright
+ assertions
@@ -47,6 +48,7 @@
5.7.0
UTF-8
1.5.1
+ 1.2.0
@@ -66,6 +68,11 @@
gson
${gson.version}
+
+ org.opentest4j
+ opentest4j
+ ${opentest4j.version}
+
org.junit.jupiter
junit-jupiter-engine
diff --git a/scripts/CLI_VERSION b/scripts/CLI_VERSION
index dac6511f..fcb14299 100644
--- a/scripts/CLI_VERSION
+++ b/scripts/CLI_VERSION
@@ -1 +1 @@
-1.16.0-1634781227000
+1.17.0-next-1634863457000
diff --git a/tools/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java b/tools/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java
index 2c702184..c3e53bd4 100644
--- a/tools/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java
+++ b/tools/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java
@@ -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 allowedBaseInterfaces = new HashSet<>(asList("Browser", "JSHandle", "BrowserContext"));
private static Set autoCloseableInterfaces = new HashSet<>(asList("Playwright", "Browser", "BrowserContext", "Page"));
@@ -877,7 +884,6 @@ class Interface extends TypeDefinition {
}
void writeTo(List 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 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 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 isAssertion() {
+ return className -> className.toLowerCase().contains("assert");
+ }
+
+ private void generate(JsonArray api, File dir, String packageName, Predicate classFilter) throws IOException {
Map 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 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 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"))) {