1
0
mirror of synced 2026-08-05 15:06:54 +00:00

feat: support waitForPage, waitForLoadState

This commit is contained in:
Yury Semikhatsky
2020-10-01 22:38:53 -07:00
parent fbcd6b9105
commit 1f2f27a166
7 changed files with 167 additions and 9 deletions
@@ -263,7 +263,8 @@ class Event extends Element {
void writeTo(List<String> output, String offset) {
// TODO: only whitelisted events are generated for now as the API may change.
if (!"Page.console".equals(jsonPath) &&
if (!"BrowserContext.page".equals(jsonPath) &&
!"Page.console".equals(jsonPath) &&
!"Page.popup".equals(jsonPath)) {
return;
}
@@ -59,6 +59,7 @@ public interface BrowserContext {
return this;
}
}
Deferred<Page> waitForPage();
void close();
void addCookies(List<Object> cookies);
default void addInitScript(String script) {
@@ -137,8 +137,8 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
return null;
}
public Supplier<PageImpl> waitForPage() {
@Override
public Deferred<Page> waitForPage() {
Supplier<JsonObject> pageSupplier = waitForProtocolEvent("page");
return () -> {
JsonObject params = pageSupplier.get();
@@ -146,5 +146,4 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
return connection.getExistingObject(guid);
};
}
}
@@ -23,14 +23,30 @@ import com.google.gson.JsonObject;
import com.microsoft.playwright.*;
import java.util.*;
import java.util.function.Supplier;
import static com.microsoft.playwright.Frame.LoadState.*;
import static com.microsoft.playwright.impl.Helpers.isFunctionBody;
public class FrameImpl extends ChannelOwner implements Frame {
PageImpl page;
private final Set<LoadState> loadStates = new HashSet<>();
FrameImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
super(parent, type, guid, initializer);
for (JsonElement item : initializer.get("loadStates").getAsJsonArray()) {
loadStates.add(loadStateFromProtocol(item.getAsString()));
}
}
private static LoadState loadStateFromProtocol(String value) {
switch (value) {
case "load": return LOAD;
case "domcontentloaded": return DOMCONTENTLOADED;
case "networkidle": return NETWORKIDLE;
default: throw new RuntimeException("Unexpected value: " + value);
}
}
private static SerializedValue serializeValue(Object value) {
@@ -425,7 +441,13 @@ public class FrameImpl extends ChannelOwner implements Frame {
@Override
public void waitForLoadState(LoadState state, WaitForLoadStateOptions options) {
if (state == null) {
state = LOAD;
}
while (!loadStates.contains(state)) {
// TODO: support timeout!
connection.processOneMessage();
}
}
@Override
@@ -442,4 +464,18 @@ public class FrameImpl extends ChannelOwner implements Frame {
public void waitForTimeout(int timeout) {
}
protected void handleEvent(String event, JsonObject params) {
if ("loadstate".equals(event)) {
JsonElement add = params.get("add");
if (add != null) {
loadStates.add(loadStateFromProtocol(add.getAsString()));
}
JsonElement remove = params.get("remove");
if (remove != null) {
loadStates.remove(loadStateFromProtocol(remove.getAsString()));
}
}
}
}
@@ -398,7 +398,7 @@ public class PageImpl extends ChannelOwner implements Page {
@Override
public void waitForLoadState(LoadState state, WaitForLoadStateOptions options) {
mainFrame.waitForLoadState(convertViaJson(state, Frame.LoadState.class), convertViaJson(options, Frame.WaitForLoadStateOptions.class));
}
@Override
@@ -16,17 +16,19 @@
package com.microsoft.playwright;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.io.*;
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.file.FileSystems;
import java.nio.file.spi.FileTypeDetector;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import static java.util.Collections.checkedCollection;
import static java.util.Collections.singletonList;
public class Server implements HttpHandler {
@@ -40,6 +42,8 @@ public class Server implements HttpHandler {
public final String EMPTY_PAGE;
private final File resourcesDir;
private final Map<String, CompletableFuture<Request>> requestSubscribers = Collections.synchronizedMap(new HashMap<>());
Server(int port) throws IOException {
PORT = port;
PREFIX = "http://localhost:" + PORT;
@@ -59,9 +63,29 @@ public class Server implements HttpHandler {
server.stop(0);
}
public static class Request {
// TODO: make a copy to ensure thread safety
public final Headers headers;
public Request(Headers headers) {
this.headers = headers;
}
}
Future<Request> waitForRequest(String path) {
CompletableFuture<Request> future = requestSubscribers.get(path);
if (future == null) {
future = new CompletableFuture<>();
requestSubscribers.put(path, future);
}
return future;
}
@Override
public void handle(HttpExchange exchange) throws IOException {
File file = new File(resourcesDir, exchange.getRequestURI().getPath().substring(1));
String path = exchange.getRequestURI().getPath();
File file = new File(resourcesDir, path.substring(1));
exchange.getResponseHeaders().put("Content-Type", singletonList(mimeType(file)));
try (FileInputStream input = new FileInputStream(file)) {
exchange.sendResponseHeaders(200, 0);
@@ -73,6 +97,14 @@ public class Server implements HttpHandler {
}
}
exchange.getResponseBody().close();
synchronized (requestSubscribers) {
CompletableFuture<Request> subscriber = requestSubscribers.get(path);
if (subscriber != null) {
requestSubscribers.remove(path);
subscriber.complete(new Request(exchange.getRequestHeaders()));
}
}
}
private static void copy(InputStream in, OutputStream out) throws IOException {
@@ -0,0 +1,89 @@
/**
* 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.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestPopup {
private static Playwright playwright;
private static Server server;
private Browser browser;
private boolean isChromium;
private boolean isWebKit;
private boolean headful;
private BrowserContext context;
private Page page;
@BeforeAll
static void createPlaywright() {
playwright = Playwright.create();
}
@BeforeAll
static void startServer() throws IOException {
server = new Server(8907);
}
@AfterAll
static void stopServer() throws IOException {
server.stop();
server = null;
}
@BeforeEach
void setUp() {
// BrowserType.LaunchOptions options = new BrowserType.LaunchOptions().withHeadless(false).withSlowMo(1000);
BrowserType.LaunchOptions options = new BrowserType.LaunchOptions();
browser = playwright.chromium().launch(options);
isChromium = true;
isWebKit = false;
headful = false;
context = browser.newContext();
page = context.newPage();
}
@AfterEach
void tearDown() {
browser.close();
}
@Test
void should_inherit_user_agent_from_browser_context() throws ExecutionException, InterruptedException {
BrowserContext context = browser.newContext(new Browser.NewContextOptions().withUserAgent("hey"));
Page page = context.newPage();
page.navigate(server.EMPTY_PAGE);
page.setContent("<a target=_blank rel=noopener href='/popup/popup.html'>link</a>");
Future<Server.Request> requestPromise = server.waitForRequest("/popup/popup.html");
Deferred<Page> popupPromise = context.waitForPage();
page.click("a");
Page popup = popupPromise.get();
popup.waitForLoadState(Page.LoadState.DOMCONTENTLOADED);
String userAgent = (String) popup.evaluate("() => window['initialUserAgent']");
Server.Request request = requestPromise.get();
context.close();
assertEquals("hey", userAgent);
assertEquals(Arrays.asList("hey"), request.headers.get("user-agent"));
}
}