1
0
mirror of synced 2026-08-07 16:06:55 +00:00

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
17 changed files with 635 additions and 65 deletions
+18
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>
@@ -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);
}
@@ -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;
}
}
@@ -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);
}
@@ -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;
}
@@ -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;
}
@@ -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,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);
}
@@ -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}'"));
}
}