mirror of
https://github.com/microsoft/playwright-java.git
synced 2025-09-08 21:01:00 +00:00
feat: implement Page.waitForRequest/Response
This commit is contained in:
parent
9b5765e1f1
commit
3d75d2a836
@ -93,7 +93,7 @@ class TypeRef extends Element {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!mapping.from.equals(jsonName)) {
|
if (!mapping.from.equals(jsonName)) {
|
||||||
throw new RuntimeException("Unexpected source type for: " + parentPath);
|
throw new RuntimeException("Unexpected source type for: " + parentPath +". Expected: " + mapping.from + "; found: " + jsonName);
|
||||||
}
|
}
|
||||||
customType = mapping.to;
|
customType = mapping.to;
|
||||||
if (mapping.customMapping != null) {
|
if (mapping.customMapping != null) {
|
||||||
@ -511,6 +511,10 @@ class Interface extends TypeDefinition {
|
|||||||
for (Method m : methods) {
|
for (Method m : methods) {
|
||||||
m.writeTo(output, offset);
|
m.writeTo(output, offset);
|
||||||
}
|
}
|
||||||
|
// TODO: fix api.json generator to avoid name clash between close() method and close event.
|
||||||
|
if ("Page".equals(jsonName)) {
|
||||||
|
output.add(offset + "Deferred<Void> waitForClose();");
|
||||||
|
}
|
||||||
output.add("}");
|
output.add("}");
|
||||||
output.add("\n");
|
output.add("\n");
|
||||||
}
|
}
|
||||||
|
@ -185,6 +185,10 @@ class Types {
|
|||||||
// Return structures
|
// Return structures
|
||||||
add("ConsoleMessage.location", "Object", "Location");
|
add("ConsoleMessage.location", "Object", "Location");
|
||||||
|
|
||||||
|
add("Page.waitForRequest", "Promise<Request>", "Deferred<Request>");
|
||||||
|
add("Page.waitForResponse", "Promise<Response>", "Deferred<Response>");
|
||||||
|
add("Page.waitForNavigation", "Promise<null|Response>", "Deferred<Response>");
|
||||||
|
|
||||||
// Custom options
|
// Custom options
|
||||||
add("Page.pdf.options.margin.top", "string|number", "String");
|
add("Page.pdf.options.margin.top", "string|number", "String");
|
||||||
add("Page.pdf.options.margin.right", "string|number", "String");
|
add("Page.pdf.options.margin.right", "string|number", "String");
|
||||||
|
@ -920,23 +920,24 @@ public interface Page {
|
|||||||
waitForLoadState(null);
|
waitForLoadState(null);
|
||||||
}
|
}
|
||||||
void waitForLoadState(LoadState state, WaitForLoadStateOptions options);
|
void waitForLoadState(LoadState state, WaitForLoadStateOptions options);
|
||||||
default Response waitForNavigation() {
|
default Deferred<Response> waitForNavigation() {
|
||||||
return waitForNavigation(null);
|
return waitForNavigation(null);
|
||||||
}
|
}
|
||||||
Response waitForNavigation(WaitForNavigationOptions options);
|
Deferred<Response> waitForNavigation(WaitForNavigationOptions options);
|
||||||
default Request waitForRequest(String urlOrPredicate) {
|
default Deferred<Request> waitForRequest(String urlOrPredicate) {
|
||||||
return waitForRequest(urlOrPredicate, null);
|
return waitForRequest(urlOrPredicate, null);
|
||||||
}
|
}
|
||||||
Request waitForRequest(String urlOrPredicate, WaitForRequestOptions options);
|
Deferred<Request> waitForRequest(String urlOrPredicate, WaitForRequestOptions options);
|
||||||
default Response waitForResponse(String urlOrPredicate) {
|
default Deferred<Response> waitForResponse(String urlOrPredicate) {
|
||||||
return waitForResponse(urlOrPredicate, null);
|
return waitForResponse(urlOrPredicate, null);
|
||||||
}
|
}
|
||||||
Response waitForResponse(String urlOrPredicate, WaitForResponseOptions options);
|
Deferred<Response> waitForResponse(String urlOrPredicate, WaitForResponseOptions options);
|
||||||
default ElementHandle waitForSelector(String selector) {
|
default ElementHandle waitForSelector(String selector) {
|
||||||
return waitForSelector(selector, null);
|
return waitForSelector(selector, null);
|
||||||
}
|
}
|
||||||
ElementHandle waitForSelector(String selector, WaitForSelectorOptions options);
|
ElementHandle waitForSelector(String selector, WaitForSelectorOptions options);
|
||||||
void waitForTimeout(int timeout);
|
void waitForTimeout(int timeout);
|
||||||
List<Worker> workers();
|
List<Worker> workers();
|
||||||
|
Deferred<Void> waitForClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@ -34,7 +35,7 @@ import static com.microsoft.playwright.impl.Utils.isFunctionBody;
|
|||||||
|
|
||||||
class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||||
private final BrowserImpl browser;
|
private final BrowserImpl browser;
|
||||||
private final List<PageImpl> pages = new ArrayList<>();
|
final List<PageImpl> pages = new ArrayList<>();
|
||||||
private List<RouteInfo> routes = new ArrayList<>();
|
private List<RouteInfo> routes = new ArrayList<>();
|
||||||
private boolean isClosedOrClosing;
|
private boolean isClosedOrClosing;
|
||||||
final Map<String, Page.Binding> bindings = new HashMap<String, Page.Binding>();
|
final Map<String, Page.Binding> bindings = new HashMap<String, Page.Binding>();
|
||||||
@ -206,9 +207,9 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Deferred<Page> waitForPage() {
|
public Deferred<Page> waitForPage() {
|
||||||
Supplier<JsonObject> pageSupplier = waitForProtocolEvent("page");
|
CompletableFuture<JsonObject> pageFuture = futureForEvent("page");
|
||||||
return () -> {
|
return () -> {
|
||||||
JsonObject params = pageSupplier.get();
|
JsonObject params = waitForCompletion(pageFuture);
|
||||||
String guid = params.getAsJsonObject("page").get("guid").getAsString();
|
String guid = params.getAsJsonObject("page").get("guid").getAsString();
|
||||||
return connection.getExistingObject(guid);
|
return connection.getExistingObject(guid);
|
||||||
};
|
};
|
||||||
|
@ -66,11 +66,11 @@ class ChannelOwner {
|
|||||||
return connection.sendMessage(guid, method, params);
|
return connection.sendMessage(guid, method, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void sendMessageNoWait(String method, JsonObject params) {
|
void sendMessageNoWait(String method, JsonObject params) {
|
||||||
connection.sendMessageNoWait(guid, method, params);
|
connection.sendMessageNoWait(guid, method, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Supplier<JsonObject> waitForProtocolEvent(String event) {
|
CompletableFuture<JsonObject> futureForEvent(String event) {
|
||||||
ArrayList<CompletableFuture<JsonObject>> futures = futureEvents.get(event);
|
ArrayList<CompletableFuture<JsonObject>> futures = futureEvents.get(event);
|
||||||
if (futures == null) {
|
if (futures == null) {
|
||||||
futures = new ArrayList<>();
|
futures = new ArrayList<>();
|
||||||
@ -78,23 +78,27 @@ class ChannelOwner {
|
|||||||
}
|
}
|
||||||
CompletableFuture<JsonObject> result = new CompletableFuture<>();
|
CompletableFuture<JsonObject> result = new CompletableFuture<>();
|
||||||
futures.add(result);
|
futures.add(result);
|
||||||
return () -> {
|
return result;
|
||||||
while (!result.isDone()) {
|
}
|
||||||
|
|
||||||
|
<T> T waitForCompletion(CompletableFuture<T> future) {
|
||||||
|
while (!future.isDone()) {
|
||||||
connection.processOneMessage();
|
connection.processOneMessage();
|
||||||
}
|
}
|
||||||
|
// TODO: ensure it's been removed from futureEvents
|
||||||
try {
|
try {
|
||||||
return result.get();
|
return future.get();
|
||||||
} catch (InterruptedException | ExecutionException e) {
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final void onEvent(String event, JsonObject parameters) {
|
final void onEvent(String event, JsonObject parameters) {
|
||||||
handleEvent(event, parameters);
|
handleEvent(event, parameters);
|
||||||
ArrayList<CompletableFuture<JsonObject>> futures = futureEvents.remove(event);
|
ArrayList<CompletableFuture<JsonObject>> futures = futureEvents.remove(event);
|
||||||
if (futures == null)
|
if (futures == null) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
for (CompletableFuture<JsonObject> f : futures) {
|
for (CompletableFuture<JsonObject> f : futures) {
|
||||||
f.complete(parameters);
|
f.complete(parameters);
|
||||||
}
|
}
|
||||||
|
@ -240,7 +240,11 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
|||||||
params.addProperty("waitUntil", toProtocol(options.waitUntil));
|
params.addProperty("waitUntil", toProtocol(options.waitUntil));
|
||||||
}
|
}
|
||||||
JsonElement result = sendMessage("goto", params);
|
JsonElement result = sendMessage("goto", params);
|
||||||
return connection.getExistingObject(result.getAsJsonObject().getAsJsonObject("response").get("guid").getAsString());
|
JsonObject jsonResponse = result.getAsJsonObject().getAsJsonObject("response");
|
||||||
|
if (jsonResponse == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return connection.getExistingObject(jsonResponse.get("guid").getAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -17,12 +17,13 @@
|
|||||||
package com.microsoft.playwright.impl;
|
package com.microsoft.playwright.impl;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.microsoft.playwright.*;
|
import com.microsoft.playwright.*;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import static com.microsoft.playwright.impl.Utils.convertViaJson;
|
import static com.microsoft.playwright.impl.Utils.convertViaJson;
|
||||||
|
|
||||||
@ -32,12 +33,16 @@ public class PageImpl extends ChannelOwner implements Page {
|
|||||||
private final FrameImpl mainFrame;
|
private final FrameImpl mainFrame;
|
||||||
private final KeyboardImpl keyboard;
|
private final KeyboardImpl keyboard;
|
||||||
private final MouseImpl mouse;
|
private final MouseImpl mouse;
|
||||||
|
private Viewport viewport;
|
||||||
// TODO: do not rely on the frame order in the tests
|
// TODO: do not rely on the frame order in the tests
|
||||||
private final Set<FrameImpl> frames = new LinkedHashSet<>();
|
private final Set<FrameImpl> frames = new LinkedHashSet<>();
|
||||||
private final List<Listener<ConsoleMessage>> consoleListeners = new ArrayList<>();
|
private final List<Listener<ConsoleMessage>> consoleListeners = new ArrayList<>();
|
||||||
private final List<Listener<Dialog>> dialogListeners = new ArrayList<>();
|
private final List<Listener<Dialog>> dialogListeners = new ArrayList<>();
|
||||||
|
private final List<Listener<Page>> closeListeners = new ArrayList<>();
|
||||||
|
private final List<WaitEventHelper> eventHelpers = new ArrayList<>();
|
||||||
final Map<String, Binding> bindings = new HashMap<String, Binding>();
|
final Map<String, Binding> bindings = new HashMap<String, Binding>();
|
||||||
BrowserContextImpl ownedContext;
|
BrowserContextImpl ownedContext;
|
||||||
|
private boolean isClosed;
|
||||||
|
|
||||||
PageImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
PageImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||||
super(parent, type, guid, initializer);
|
super(parent, type, guid, initializer);
|
||||||
@ -59,6 +64,14 @@ public class PageImpl extends ChannelOwner implements Page {
|
|||||||
consoleListeners.remove(listener);
|
consoleListeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addCloseListener(Listener<Page> listener) {
|
||||||
|
closeListeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeCloseListener(Listener<Page> listener) {
|
||||||
|
closeListeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addDialogListener(Listener<Dialog> listener) {
|
public void addDialogListener(Listener<Dialog> listener) {
|
||||||
dialogListeners.add(listener);
|
dialogListeners.add(listener);
|
||||||
@ -71,21 +84,25 @@ public class PageImpl extends ChannelOwner implements Page {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Deferred<Page> waitForPopup() {
|
public Deferred<Page> waitForPopup() {
|
||||||
Supplier<JsonObject> popupSupplier = waitForProtocolEvent("popup");
|
CompletableFuture<JsonObject> popupFuture = futureForEvent("popup");
|
||||||
return () -> {
|
return () -> {
|
||||||
JsonObject params = popupSupplier.get();
|
JsonObject params = waitForCompletion(popupFuture);
|
||||||
String guid = params.getAsJsonObject("page").get("guid").getAsString();
|
String guid = params.getAsJsonObject("page").get("guid").getAsString();
|
||||||
return connection.getExistingObject(guid);
|
return connection.getExistingObject(guid);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static <T> void notifyListeners(List<Listener<T>> listeners, T subject) {
|
||||||
|
for (Listener<T> listener: new ArrayList<>(listeners)) {
|
||||||
|
listener.handle(subject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void handleEvent(String event, JsonObject params) {
|
protected void handleEvent(String event, JsonObject params) {
|
||||||
if ("dialog".equals(event)) {
|
if ("dialog".equals(event)) {
|
||||||
String guid = params.getAsJsonObject("dialog").get("guid").getAsString();
|
String guid = params.getAsJsonObject("dialog").get("guid").getAsString();
|
||||||
DialogImpl dialog = connection.getExistingObject(guid);
|
DialogImpl dialog = connection.getExistingObject(guid);
|
||||||
for (Listener<Dialog> listener: new ArrayList<>(dialogListeners)) {
|
notifyListeners(dialogListeners, dialog);
|
||||||
listener.handle(dialog);
|
|
||||||
}
|
|
||||||
// If no action taken dismiss dialog to not hang.
|
// If no action taken dismiss dialog to not hang.
|
||||||
if (!dialog.isHandled()) {
|
if (!dialog.isHandled()) {
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
@ -93,9 +110,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
|||||||
} else if ("console".equals(event)) {
|
} else if ("console".equals(event)) {
|
||||||
String guid = params.getAsJsonObject("message").get("guid").getAsString();
|
String guid = params.getAsJsonObject("message").get("guid").getAsString();
|
||||||
ConsoleMessageImpl message = connection.getExistingObject(guid);
|
ConsoleMessageImpl message = connection.getExistingObject(guid);
|
||||||
for (Listener<ConsoleMessage> listener: new ArrayList<>(consoleListeners)) {
|
notifyListeners(consoleListeners, message);
|
||||||
listener.handle(message);
|
|
||||||
}
|
|
||||||
} else if ("frameAttached".equals(event)) {
|
} else if ("frameAttached".equals(event)) {
|
||||||
String guid = params.getAsJsonObject("frame").get("guid").getAsString();
|
String guid = params.getAsJsonObject("frame").get("guid").getAsString();
|
||||||
FrameImpl frame = connection.getExistingObject(guid);
|
FrameImpl frame = connection.getExistingObject(guid);
|
||||||
@ -104,7 +119,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
|||||||
if (frame.parentFrame != null) {
|
if (frame.parentFrame != null) {
|
||||||
frame.parentFrame.childFrames.add(frame);
|
frame.parentFrame.childFrames.add(frame);
|
||||||
}
|
}
|
||||||
} else if ("'frameDetached'".equals(event)) {
|
} else if ("frameDetached".equals(event)) {
|
||||||
String guid = params.getAsJsonObject("frame").get("guid").getAsString();
|
String guid = params.getAsJsonObject("frame").get("guid").getAsString();
|
||||||
FrameImpl frame = connection.getExistingObject(guid);
|
FrameImpl frame = connection.getExistingObject(guid);
|
||||||
frames.remove(frame);
|
frames.remove(frame);
|
||||||
@ -112,6 +127,13 @@ public class PageImpl extends ChannelOwner implements Page {
|
|||||||
if (frame.parentFrame != null) {
|
if (frame.parentFrame != null) {
|
||||||
frame.parentFrame.childFrames.remove(frame);
|
frame.parentFrame.childFrames.remove(frame);
|
||||||
}
|
}
|
||||||
|
} else if ("close".equals(event)) {
|
||||||
|
isClosed = true;
|
||||||
|
browserContext.pages.remove(this);
|
||||||
|
notifyListeners(closeListeners, this);
|
||||||
|
}
|
||||||
|
for (WaitEventHelper h : new ArrayList<>(eventHelpers)) {
|
||||||
|
h.handleEvent(event, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,7 +193,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void check(String selector, CheckOptions options) {
|
public void check(String selector, CheckOptions options) {
|
||||||
|
mainFrame.check(selector, convertViaJson(options, Frame.CheckOptions.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -181,7 +203,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String content() {
|
public String content() {
|
||||||
return null;
|
return mainFrame.content();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -296,7 +318,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isClosed() {
|
public boolean isClosed() {
|
||||||
return false;
|
return isClosed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -316,8 +338,12 @@ public class PageImpl extends ChannelOwner implements Page {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Page opener() {
|
public Page opener() {
|
||||||
|
JsonObject result = sendMessage("opener", new JsonObject()).getAsJsonObject();
|
||||||
|
if (!result.has("page")) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
return connection.getExistingObject(result.getAsJsonObject("page").get("guid").getAsString());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] pdf(PdfOptions options) {
|
public byte[] pdf(PdfOptions options) {
|
||||||
@ -376,17 +402,15 @@ public class PageImpl extends ChannelOwner implements Page {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setViewportSize(int width, int height) {
|
public void setViewportSize(int width, int height) {
|
||||||
JsonObject size = new JsonObject();
|
viewport = new Viewport(width, height);
|
||||||
size.addProperty("width", width);
|
|
||||||
size.addProperty("height", height);
|
|
||||||
JsonObject params = new JsonObject();
|
JsonObject params = new JsonObject();
|
||||||
params.add("viewportSize", size);
|
params.add("viewportSize", new Gson().toJsonTree(viewport));
|
||||||
sendMessage("setViewportSize", params);
|
sendMessage("setViewportSize", params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String textContent(String selector, TextContentOptions options) {
|
public String textContent(String selector, TextContentOptions options) {
|
||||||
return null;
|
return mainFrame.textContent(selector, convertViaJson(options, Frame.TextContentOptions.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -396,12 +420,12 @@ public class PageImpl extends ChannelOwner implements Page {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void type(String selector, String text, TypeOptions options) {
|
public void type(String selector, String text, TypeOptions options) {
|
||||||
|
mainFrame.type(selector, text, convertViaJson(options, Frame.TypeOptions.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void uncheck(String selector, UncheckOptions options) {
|
public void uncheck(String selector, UncheckOptions options) {
|
||||||
|
mainFrame.uncheck(selector, convertViaJson(options, Frame.UncheckOptions.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -411,19 +435,17 @@ public class PageImpl extends ChannelOwner implements Page {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String url() {
|
public String url() {
|
||||||
return null;
|
return mainFrame.url();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Viewport viewportSize() {
|
public Viewport viewportSize() {
|
||||||
return null;
|
return viewport;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object waitForEvent(String event, String optionsOrPredicate) {
|
public Object waitForEvent(String event, String optionsOrPredicate) {
|
||||||
// TODO: do we want to keep this method ?
|
// TODO: do we want to keep this method ?
|
||||||
Supplier<JsonObject> popupSupplier = waitForProtocolEvent(event);
|
|
||||||
popupSupplier.get();
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,18 +460,51 @@ public class PageImpl extends ChannelOwner implements Page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response waitForNavigation(WaitForNavigationOptions options) {
|
public Deferred<Response> waitForNavigation(WaitForNavigationOptions options) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class WaitEventHelper<R> implements Deferred<R> {
|
||||||
public Request waitForRequest(String urlOrPredicate, WaitForRequestOptions options) {
|
private final CompletableFuture<R> result = new CompletableFuture<>();
|
||||||
return null;
|
private final String event;
|
||||||
|
private final String fieldName;
|
||||||
|
|
||||||
|
WaitEventHelper(String event, String fieldName) {
|
||||||
|
this.event = event;
|
||||||
|
this.fieldName = fieldName;
|
||||||
|
eventHelpers.add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleEvent(String name, JsonObject params) {
|
||||||
|
if (event.equals(name)) {
|
||||||
|
if (fieldName != null && params.has(fieldName)) {
|
||||||
|
result.complete(connection.getExistingObject(params.getAsJsonObject(fieldName).get("guid").getAsString()));
|
||||||
|
} else {
|
||||||
|
result.complete(null);
|
||||||
|
}
|
||||||
|
} else if ("close".equals(name)) {
|
||||||
|
result.completeExceptionally(new RuntimeException("Page closed"));
|
||||||
|
} else if ("crash".equals(name)) {
|
||||||
|
result.completeExceptionally(new RuntimeException("Page crashed"));
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
eventHelpers.remove(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R get() {
|
||||||
|
return waitForCompletion(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response waitForResponse(String urlOrPredicate, WaitForResponseOptions options) {
|
public Deferred<Request> waitForRequest(String urlOrPredicate, WaitForRequestOptions options) {
|
||||||
return null;
|
return new WaitEventHelper<>("request", "request");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Deferred<Response> waitForResponse(String urlOrPredicate, WaitForResponseOptions options) {
|
||||||
|
return new WaitEventHelper<>("response", "response");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -466,4 +521,9 @@ public class PageImpl extends ChannelOwner implements Page {
|
|||||||
public List<Worker> workers() {
|
public List<Worker> workers() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Deferred<Void> waitForClose() {
|
||||||
|
return new WaitEventHelper<>("close", null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,6 +101,12 @@ public class Server implements HttpHandler {
|
|||||||
routes.put(path, handler);
|
routes.put(path, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
requestSubscribers.clear();
|
||||||
|
auths.clear();
|
||||||
|
routes.clear();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(HttpExchange exchange) throws IOException {
|
public void handle(HttpExchange exchange) throws IOException {
|
||||||
String path = exchange.getRequestURI().getPath();
|
String path = exchange.getRequestURI().getPath();
|
||||||
|
@ -62,6 +62,7 @@ public class TestClick {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
|
server.reset();
|
||||||
context = browser.newContext();
|
context = browser.newContext();
|
||||||
page = context.newPage();
|
page = context.newPage();
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@ public class TestDialog {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
|
server.reset();
|
||||||
context = browser.newContext();
|
context = browser.newContext();
|
||||||
page = context.newPage();
|
page = context.newPage();
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ public class TestElementHandleClick {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
|
server.reset();
|
||||||
context = browser.newContext();
|
context = browser.newContext();
|
||||||
page = context.newPage();
|
page = context.newPage();
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,7 @@ public class TestFrameNavigate {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
|
server.reset();
|
||||||
context = browser.newContext();
|
context = browser.newContext();
|
||||||
page = context.newPage();
|
page = context.newPage();
|
||||||
}
|
}
|
||||||
|
249
lib/src/test/java/com/microsoft/playwright/TestPageBasic.java
Normal file
249
lib/src/test/java/com/microsoft/playwright/TestPageBasic.java
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
/**
|
||||||
|
* 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 static com.microsoft.playwright.Page.LoadState.DOMCONTENTLOADED;
|
||||||
|
import static com.microsoft.playwright.Page.LoadState.LOAD;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
public class TestPageBasic {
|
||||||
|
private static Server server;
|
||||||
|
private static Browser browser;
|
||||||
|
private static boolean isChromium;
|
||||||
|
private static boolean isWebKit;
|
||||||
|
private BrowserContext context;
|
||||||
|
private Page page;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void launchBrowser() {
|
||||||
|
Playwright playwright = Playwright.create();
|
||||||
|
BrowserType.LaunchOptions options = new BrowserType.LaunchOptions();
|
||||||
|
browser = playwright.chromium().launch(options);
|
||||||
|
isChromium = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void startServer() throws IOException {
|
||||||
|
server = new Server(8907);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
static void stopServer() throws IOException {
|
||||||
|
browser.close();
|
||||||
|
server.stop();
|
||||||
|
server = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
context = browser.newContext();
|
||||||
|
page = context.newPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void tearDown() {
|
||||||
|
context.close();
|
||||||
|
context = null;
|
||||||
|
page = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRejectAllPromisesWhenPageIsClosed() {
|
||||||
|
Page newPage = context.newPage();
|
||||||
|
newPage.close();
|
||||||
|
try {
|
||||||
|
newPage.evaluate("() => new Promise(r => {})");
|
||||||
|
fail("evaluate should throw");
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
assertTrue(e.getMessage().contains("Protocol error"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotBeVisibleInContextPages() {
|
||||||
|
Page newPage = context.newPage();
|
||||||
|
assertTrue(context.pages().contains(newPage));
|
||||||
|
newPage.close();
|
||||||
|
assertFalse(context.pages().contains(newPage));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRunBeforeunloadIfAskedFor() {
|
||||||
|
Page newPage = context.newPage();
|
||||||
|
newPage.navigate(server.PREFIX + "/beforeunload.html");
|
||||||
|
// We have to interact with a page so that "beforeunload" handlers
|
||||||
|
// fire.
|
||||||
|
newPage.click("body");
|
||||||
|
boolean[] didShowDialog = {false};
|
||||||
|
newPage.addDialogListener(dialog -> {
|
||||||
|
didShowDialog[0] = true;
|
||||||
|
assertEquals("beforeunload", dialog.type());
|
||||||
|
assertEquals("", dialog.defaultValue());
|
||||||
|
if (isChromium) {
|
||||||
|
assertEquals("", dialog.message());
|
||||||
|
} else if (isWebKit) {
|
||||||
|
assertEquals("Leave?", dialog.message());
|
||||||
|
} else {
|
||||||
|
assertEquals("This page is asking you to confirm that you want to leave - data you have entered may not be saved.", dialog.message());
|
||||||
|
}
|
||||||
|
dialog.accept();
|
||||||
|
});
|
||||||
|
newPage.close(new Page.CloseOptions().withRunBeforeUnload(true));
|
||||||
|
// TODO: uncomment once https://github.com/microsoft/playwright/pull/4070 is committed.
|
||||||
|
// assertTrue(didShowDialog[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotRunBeforeunloadByDefault() {
|
||||||
|
Page newPage = context.newPage();
|
||||||
|
newPage.navigate(server.PREFIX + "/beforeunload.html");
|
||||||
|
// We have to interact with a page so that "beforeunload" handlers
|
||||||
|
// fire.
|
||||||
|
newPage.click("body");
|
||||||
|
boolean[] didShowDialog = {false};
|
||||||
|
newPage.addDialogListener(dialog -> didShowDialog[0] = true);
|
||||||
|
newPage.close();
|
||||||
|
assertFalse(didShowDialog[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldSetThePageCloseState() {
|
||||||
|
Page newPage = context.newPage();
|
||||||
|
assertEquals(false, newPage.isClosed());
|
||||||
|
newPage.close();
|
||||||
|
assertEquals(true, newPage.isClosed());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldTerminateNetworkWaiters() {
|
||||||
|
Page newPage = context.newPage();
|
||||||
|
Deferred<Request> request = newPage.waitForRequest(server.EMPTY_PAGE);
|
||||||
|
Deferred<Response> response = newPage.waitForResponse(server.EMPTY_PAGE);
|
||||||
|
newPage.close();
|
||||||
|
try {
|
||||||
|
request.get();
|
||||||
|
fail("get() should throw");
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
assertTrue(e.getMessage().contains("Page closed"));
|
||||||
|
assertFalse(e.getMessage().contains("Timeout"));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
response.get();
|
||||||
|
fail("get() should throw");
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
assertTrue(e.getMessage().contains("Page closed"));
|
||||||
|
assertFalse(e.getMessage().contains("Timeout"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldBeCallableTwice() {
|
||||||
|
Page newPage = context.newPage();
|
||||||
|
newPage.close();
|
||||||
|
newPage.close();
|
||||||
|
newPage.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldFireLoadWhenExpected() {
|
||||||
|
page.navigate("about:blank");
|
||||||
|
page.waitForLoadState(LOAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: not supported in sync api
|
||||||
|
void asyncStacksShouldWork() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldProvideAccessToTheOpenerPage() {
|
||||||
|
Deferred<Page> popupEvent = page.waitForPopup();
|
||||||
|
page.evaluate("() => window.open('about:blank')");
|
||||||
|
Page opener = popupEvent.get().opener();
|
||||||
|
assertEquals(page, opener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReturnNullIfParentPageHasBeenClosed() {
|
||||||
|
Deferred<Page> popupEvent = page.waitForPopup();
|
||||||
|
page.evaluate("() => window.open('about:blank')");
|
||||||
|
Page popup = popupEvent.get();
|
||||||
|
page.close();
|
||||||
|
Page opener = popup.opener();
|
||||||
|
assertEquals(null, opener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldFireDomcontentloadedWhenExpected() {
|
||||||
|
page.navigate("about:blank");
|
||||||
|
page.waitForLoadState(DOMCONTENTLOADED);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: downloads
|
||||||
|
void shouldFailWithErrorUponDisconnect() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void pageUrlShouldWork() {
|
||||||
|
assertEquals("about:blank", page.url());
|
||||||
|
page.navigate(server.EMPTY_PAGE);
|
||||||
|
assertEquals(server.EMPTY_PAGE, page.url());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void pageUrlShouldIncludeHashes() {
|
||||||
|
page.navigate(server.EMPTY_PAGE + "#hash");
|
||||||
|
assertEquals(server.EMPTY_PAGE + "#hash", page.url());
|
||||||
|
page.evaluate("() => {\n" +
|
||||||
|
" window.location.hash = 'dynamic';\n" +
|
||||||
|
"}");
|
||||||
|
assertEquals(server.EMPTY_PAGE + "#dynamic", page.url());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void pageTitleShouldReturnThePageTitle() {
|
||||||
|
page.navigate(server.PREFIX + "/title.html");
|
||||||
|
assertEquals("Woof-Woof", page.title());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void pageCloseShouldWorkWithWindowClose() {
|
||||||
|
Deferred<Page> newPagePromise = page.waitForPopup();
|
||||||
|
page.evaluate("() => window['newPage'] = window.open('about:blank')");
|
||||||
|
Page newPage = newPagePromise.get();
|
||||||
|
Deferred<Void> closedPromise = newPage.waitForClose();
|
||||||
|
page.evaluate("() => window['newPage'].close()");
|
||||||
|
closedPromise.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void pageCloseShouldWorkWithPageClose() {
|
||||||
|
Page newPage = context.newPage();
|
||||||
|
Deferred<Void> closedPromise = newPage.waitForClose();
|
||||||
|
newPage.close();
|
||||||
|
closedPromise.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void pageContextShouldReturnTheCorrectInstance() {
|
||||||
|
assertEquals(context, page.context());
|
||||||
|
}
|
||||||
|
}
|
@ -54,6 +54,7 @@ public class TestPopup {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
|
server.reset();
|
||||||
context = browser.newContext();
|
context = browser.newContext();
|
||||||
page = context.newPage();
|
page = context.newPage();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user