feat: support waitForPage, waitForLoadState
This commit is contained in:
@@ -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"));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user