feat: support FileChooser (#33)
This commit is contained in:
@@ -263,6 +263,38 @@ class Method extends Element {
|
||||
"void unroute(Pattern url, BiConsumer<Route, Request> handler);",
|
||||
"void unroute(Predicate<String> url, BiConsumer<Route, Request> handler);",
|
||||
});
|
||||
customSignature.put("FileChooser.setFiles", new String[]{
|
||||
"default void setFiles(File file) { setFiles(file, null); }",
|
||||
"default void setFiles(File file, SetFilesOptions options) { setFiles(new File[]{ file }, options); }",
|
||||
"default void setFiles(File[] files) { setFiles(files, null); }",
|
||||
"void setFiles(File[] files, SetFilesOptions options);",
|
||||
"default void setFiles(FileChooser.FilePayload file) { setFiles(file, null); }",
|
||||
"default void setFiles(FileChooser.FilePayload file, SetFilesOptions options) { setFiles(new FileChooser.FilePayload[]{ file }, options); }",
|
||||
"default void setFiles(FileChooser.FilePayload[] files) { setFiles(files, null); }",
|
||||
"void setFiles(FileChooser.FilePayload[] files, SetFilesOptions options);",
|
||||
});
|
||||
customSignature.put("ElementHandle.setInputFiles", new String[]{
|
||||
"default void setInputFiles(File file) { setInputFiles(file, null); }",
|
||||
"default void setInputFiles(File file, SetInputFilesOptions options) { setInputFiles(new File[]{ file }, options); }",
|
||||
"default void setInputFiles(File[] files) { setInputFiles(files, null); }",
|
||||
"void setInputFiles(File[] files, SetInputFilesOptions options);",
|
||||
"default void setInputFiles(FileChooser.FilePayload file) { setInputFiles(file, null); }",
|
||||
"default void setInputFiles(FileChooser.FilePayload file, SetInputFilesOptions options) { setInputFiles(new FileChooser.FilePayload[]{ file }, options); }",
|
||||
"default void setInputFiles(FileChooser.FilePayload[] files) { setInputFiles(files, null); }",
|
||||
"void setInputFiles(FileChooser.FilePayload[] files, SetInputFilesOptions options);",
|
||||
});
|
||||
String[] setInputFilesWithSelector = {
|
||||
"default void setInputFiles(String selector, File file) { setInputFiles(selector, file, null); }",
|
||||
"default void setInputFiles(String selector, File file, SetInputFilesOptions options) { setInputFiles(selector, new File[]{ file }, options); }",
|
||||
"default void setInputFiles(String selector, File[] files) { setInputFiles(selector, files, null); }",
|
||||
"void setInputFiles(String selector, File[] files, SetInputFilesOptions options);",
|
||||
"default void setInputFiles(String selector, FileChooser.FilePayload file) { setInputFiles(selector, file, null); }",
|
||||
"default void setInputFiles(String selector, FileChooser.FilePayload file, SetInputFilesOptions options) { setInputFiles(selector, new FileChooser.FilePayload[]{ file }, options); }",
|
||||
"default void setInputFiles(String selector, FileChooser.FilePayload[] files) { setInputFiles(selector, files, null); }",
|
||||
"void setInputFiles(String selector, FileChooser.FilePayload[] files, SetInputFilesOptions options);",
|
||||
};
|
||||
customSignature.put("Page.setInputFiles", setInputFilesWithSelector);
|
||||
customSignature.put("Frame.setInputFiles", setInputFilesWithSelector);
|
||||
}
|
||||
|
||||
Method(TypeDefinition parent, JsonObject jsonElement) {
|
||||
@@ -498,6 +530,9 @@ class Interface extends TypeDefinition {
|
||||
if (jsonName.equals("Route")) {
|
||||
output.add("import java.nio.charset.StandardCharsets;");
|
||||
}
|
||||
if (Arrays.asList("Page", "Frame", "ElementHandle", "FileChooser").contains(jsonName)) {
|
||||
output.add("import java.io.File;");
|
||||
}
|
||||
output.add("import java.util.*;");
|
||||
if (Arrays.asList("Page", "BrowserContext").contains(jsonName)) {
|
||||
output.add("import java.util.function.BiConsumer;");
|
||||
@@ -632,6 +667,20 @@ class Interface extends TypeDefinition {
|
||||
output.add(offset + "}");
|
||||
break;
|
||||
}
|
||||
case "FileChooser": {
|
||||
output.add(offset + "class FilePayload {");
|
||||
output.add(offset + " public final String name;");
|
||||
output.add(offset + " public final String mimeType;");
|
||||
output.add(offset + " public final byte[] buffer;");
|
||||
output.add("");
|
||||
output.add(offset + " public FilePayload(String name, String mimeType, byte[] buffer) {");
|
||||
output.add(offset + " this.name = name;");
|
||||
output.add(offset + " this.mimeType = mimeType;");
|
||||
output.add(offset + " this.buffer = buffer;");
|
||||
output.add(offset + " }");
|
||||
output.add(offset + "}");
|
||||
break;
|
||||
}
|
||||
default: return;
|
||||
}
|
||||
output.add("");
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
public interface ElementHandle extends JSHandle {
|
||||
@@ -427,10 +428,14 @@ public interface ElementHandle extends JSHandle {
|
||||
selectText(null);
|
||||
}
|
||||
void selectText(SelectTextOptions options);
|
||||
default void setInputFiles(String files) {
|
||||
setInputFiles(files, null);
|
||||
}
|
||||
void setInputFiles(String files, SetInputFilesOptions options);
|
||||
default void setInputFiles(File file) { setInputFiles(file, null); }
|
||||
default void setInputFiles(File file, SetInputFilesOptions options) { setInputFiles(new File[]{ file }, options); }
|
||||
default void setInputFiles(File[] files) { setInputFiles(files, null); }
|
||||
void setInputFiles(File[] files, SetInputFilesOptions options);
|
||||
default void setInputFiles(FileChooser.FilePayload file) { setInputFiles(file, null); }
|
||||
default void setInputFiles(FileChooser.FilePayload file, SetInputFilesOptions options) { setInputFiles(new FileChooser.FilePayload[]{ file }, options); }
|
||||
default void setInputFiles(FileChooser.FilePayload[] files) { setInputFiles(files, null); }
|
||||
void setInputFiles(FileChooser.FilePayload[] files, SetInputFilesOptions options);
|
||||
String textContent();
|
||||
String toString();
|
||||
default void type(String text) {
|
||||
|
||||
@@ -16,9 +16,22 @@
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
public interface FileChooser {
|
||||
class FilePayload {
|
||||
public final String name;
|
||||
public final String mimeType;
|
||||
public final byte[] buffer;
|
||||
|
||||
public FilePayload(String name, String mimeType, byte[] buffer) {
|
||||
this.name = name;
|
||||
this.mimeType = mimeType;
|
||||
this.buffer = buffer;
|
||||
}
|
||||
}
|
||||
|
||||
class SetFilesOptions {
|
||||
public Boolean noWaitAfter;
|
||||
public Integer timeout;
|
||||
@@ -35,9 +48,13 @@ public interface FileChooser {
|
||||
ElementHandle element();
|
||||
boolean isMultiple();
|
||||
Page page();
|
||||
default void setFiles(String files) {
|
||||
setFiles(files, null);
|
||||
}
|
||||
void setFiles(String files, SetFilesOptions options);
|
||||
default void setFiles(File file) { setFiles(file, null); }
|
||||
default void setFiles(File file, SetFilesOptions options) { setFiles(new File[]{ file }, options); }
|
||||
default void setFiles(File[] files) { setFiles(files, null); }
|
||||
void setFiles(File[] files, SetFilesOptions options);
|
||||
default void setFiles(FileChooser.FilePayload file) { setFiles(file, null); }
|
||||
default void setFiles(FileChooser.FilePayload file, SetFilesOptions options) { setFiles(new FileChooser.FilePayload[]{ file }, options); }
|
||||
default void setFiles(FileChooser.FilePayload[] files) { setFiles(files, null); }
|
||||
void setFiles(FileChooser.FilePayload[] files, SetFilesOptions options);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -567,10 +568,14 @@ public interface Frame {
|
||||
setContent(html, null);
|
||||
}
|
||||
void setContent(String html, SetContentOptions options);
|
||||
default void setInputFiles(String selector, String files) {
|
||||
setInputFiles(selector, files, null);
|
||||
}
|
||||
void setInputFiles(String selector, String files, SetInputFilesOptions options);
|
||||
default void setInputFiles(String selector, File file) { setInputFiles(selector, file, null); }
|
||||
default void setInputFiles(String selector, File file, SetInputFilesOptions options) { setInputFiles(selector, new File[]{ file }, options); }
|
||||
default void setInputFiles(String selector, File[] files) { setInputFiles(selector, files, null); }
|
||||
void setInputFiles(String selector, File[] files, SetInputFilesOptions options);
|
||||
default void setInputFiles(String selector, FileChooser.FilePayload file) { setInputFiles(selector, file, null); }
|
||||
default void setInputFiles(String selector, FileChooser.FilePayload file, SetInputFilesOptions options) { setInputFiles(selector, new FileChooser.FilePayload[]{ file }, options); }
|
||||
default void setInputFiles(String selector, FileChooser.FilePayload[] files) { setInputFiles(selector, files, null); }
|
||||
void setInputFiles(String selector, FileChooser.FilePayload[] files, SetInputFilesOptions options);
|
||||
default String textContent(String selector) {
|
||||
return textContent(selector, null);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
@@ -929,10 +930,14 @@ public interface Page {
|
||||
void setDefaultNavigationTimeout(int timeout);
|
||||
void setDefaultTimeout(int timeout);
|
||||
void setExtraHTTPHeaders(Map<String, String> headers);
|
||||
default void setInputFiles(String selector, String files) {
|
||||
setInputFiles(selector, files, null);
|
||||
}
|
||||
void setInputFiles(String selector, String files, SetInputFilesOptions options);
|
||||
default void setInputFiles(String selector, File file) { setInputFiles(selector, file, null); }
|
||||
default void setInputFiles(String selector, File file, SetInputFilesOptions options) { setInputFiles(selector, new File[]{ file }, options); }
|
||||
default void setInputFiles(String selector, File[] files) { setInputFiles(selector, files, null); }
|
||||
void setInputFiles(String selector, File[] files, SetInputFilesOptions options);
|
||||
default void setInputFiles(String selector, FileChooser.FilePayload file) { setInputFiles(selector, file, null); }
|
||||
default void setInputFiles(String selector, FileChooser.FilePayload file, SetInputFilesOptions options) { setInputFiles(selector, new FileChooser.FilePayload[]{ file }, options); }
|
||||
default void setInputFiles(String selector, FileChooser.FilePayload[] files) { setInputFiles(selector, files, null); }
|
||||
void setInputFiles(String selector, FileChooser.FilePayload[] files, SetInputFilesOptions options);
|
||||
void setViewportSize(int width, int height);
|
||||
default String textContent(String selector) {
|
||||
return textContent(selector, null);
|
||||
|
||||
@@ -218,7 +218,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
|
||||
@Override
|
||||
public Deferred<Event<EventType>> waitForEvent(EventType event, String optionsOrPredicate) {
|
||||
return listeners.waitForEvent(event, connection);
|
||||
return toDeferred(new WaitableEvent<>(listeners, event));
|
||||
}
|
||||
|
||||
private void unroute(UrlMatcher matcher, BiConsumer<Route, Request> handler) {
|
||||
|
||||
@@ -76,6 +76,7 @@ class ChannelOwner {
|
||||
connection.sendMessageNoWait(guid, method, params);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<T> Deferred<T> toDeferred(Waitable waitable) {
|
||||
return () -> {
|
||||
while (!waitable.isDone()) {
|
||||
|
||||
@@ -205,9 +205,6 @@ public class Connection {
|
||||
case "Frame":
|
||||
result = new FrameImpl(parent, type, guid, initializer);
|
||||
break;
|
||||
case "FileChooser":
|
||||
result = new FileChooserImpl(parent, type, guid, initializer);
|
||||
break;
|
||||
case "JSHandle":
|
||||
result = new JSHandleImpl(parent, type, guid, initializer);
|
||||
break;
|
||||
|
||||
@@ -20,12 +20,17 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.*;
|
||||
import com.microsoft.playwright.Deferred;
|
||||
import com.microsoft.playwright.ElementHandle;
|
||||
import com.microsoft.playwright.FileChooser;
|
||||
import com.microsoft.playwright.Frame;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.*;
|
||||
import static com.microsoft.playwright.impl.Serialization.deserialize;
|
||||
import static com.microsoft.playwright.impl.Serialization.serializeArgument;
|
||||
import static com.microsoft.playwright.impl.Utils.isFunctionBody;
|
||||
|
||||
class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
@@ -257,8 +262,18 @@ class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(String files, SetInputFilesOptions options) {
|
||||
public void setInputFiles(File[] files, SetInputFilesOptions options) {
|
||||
setInputFiles(Utils.toFilePayloads(files), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(FileChooser.FilePayload[] files, SetInputFilesOptions options) {
|
||||
if (options == null) {
|
||||
options = new SetInputFilesOptions();
|
||||
}
|
||||
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
|
||||
params.add("files", Serialization.toJsonArray(files));
|
||||
sendMessage("setInputFiles", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,33 +16,49 @@
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.ElementHandle;
|
||||
import com.microsoft.playwright.FileChooser;
|
||||
import com.microsoft.playwright.Page;
|
||||
|
||||
class FileChooserImpl extends ChannelOwner implements FileChooser {
|
||||
FileChooserImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
import java.io.File;
|
||||
|
||||
import static com.microsoft.playwright.impl.Utils.convertViaJson;
|
||||
|
||||
class FileChooserImpl implements FileChooser {
|
||||
private final PageImpl page;
|
||||
private final ElementHandle element;
|
||||
private final boolean isMultiple;
|
||||
|
||||
FileChooserImpl(PageImpl page, ElementHandle element, boolean isMultiple) {
|
||||
this.page = page;
|
||||
this.element = element;
|
||||
this.isMultiple = isMultiple;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementHandle element() {
|
||||
return null;
|
||||
return element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMultiple() {
|
||||
return false;
|
||||
return isMultiple;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page page() {
|
||||
return null;
|
||||
return page;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFiles(String files, SetFilesOptions options) {
|
||||
public void setFiles(File[] files, SetFilesOptions options) {
|
||||
setFiles(Utils.toFilePayloads(files), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFiles(FilePayload[] files, SetFilesOptions options) {
|
||||
element.setInputFiles(files, convertViaJson(options, ElementHandle.SetInputFilesOptions.class));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
@@ -29,8 +30,7 @@ import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
|
||||
import static com.microsoft.playwright.Frame.LoadState.*;
|
||||
import static com.microsoft.playwright.impl.Serialization.deserialize;
|
||||
import static com.microsoft.playwright.impl.Serialization.serializeArgument;
|
||||
import static com.microsoft.playwright.impl.Serialization.*;
|
||||
import static com.microsoft.playwright.impl.Utils.isFunctionBody;
|
||||
|
||||
public class FrameImpl extends ChannelOwner implements Frame {
|
||||
@@ -388,8 +388,19 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(String selector, String files, SetInputFilesOptions options) {
|
||||
public void setInputFiles(String selector, File[] files, SetInputFilesOptions options) {
|
||||
setInputFiles(selector, Utils.toFilePayloads(files), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(String selector, FileChooser.FilePayload[] files, SetInputFilesOptions options) {
|
||||
if (options == null) {
|
||||
options = new SetInputFilesOptions();
|
||||
}
|
||||
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.add("files", toJsonArray(files));
|
||||
sendMessage("setInputFiles", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -71,34 +71,7 @@ class ListenerCollection <EventType> {
|
||||
}
|
||||
}
|
||||
|
||||
private class DeferredEvent implements Listener<EventType>, Deferred<Event<EventType>> {
|
||||
private final EventType type;
|
||||
private final Connection connection;
|
||||
private Event<EventType> event;
|
||||
|
||||
DeferredEvent(EventType type, Connection connection) {
|
||||
add(type, this);
|
||||
this.type = type;
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Event<EventType> e) {
|
||||
event = e;
|
||||
remove(type, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Event<EventType> get() {
|
||||
while (event == null) {
|
||||
connection.processOneMessage();
|
||||
}
|
||||
return event;
|
||||
}
|
||||
boolean hasListeners(EventType type) {
|
||||
return listeners.containsKey(type);
|
||||
}
|
||||
|
||||
Deferred<Event<EventType>> waitForEvent(EventType event, Connection connection) {
|
||||
return new DeferredEvent(event, connection);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Watchable;
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
@@ -83,7 +85,8 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
listeners.notify(EventType.DOWNLOAD, download);
|
||||
} else if ("fileChooser".equals(event)) {
|
||||
String guid = params.getAsJsonObject("element").get("guid").getAsString();
|
||||
FileChooser fileChooser = connection.getExistingObject(guid);
|
||||
ElementHandle elementHandle = connection.getExistingObject(guid);
|
||||
FileChooser fileChooser = new FileChooserImpl(this, elementHandle, params.get("isMultiple").getAsBoolean());
|
||||
listeners.notify(EventType.FILECHOOSER, fileChooser);
|
||||
} else if ("bindingCall".equals(event)) {
|
||||
String guid = params.getAsJsonObject("binding").get("guid").getAsString();
|
||||
@@ -162,14 +165,34 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
}
|
||||
|
||||
private void willAddFileChooserListener() {
|
||||
if (!listeners.hasListeners(EventType.FILECHOOSER)) {
|
||||
updateFileChooserInterception(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void didRemoveFileChooserListener() {
|
||||
if (!listeners.hasListeners(EventType.FILECHOOSER)) {
|
||||
updateFileChooserInterception(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateFileChooserInterception(boolean enabled) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("intercepted", enabled);
|
||||
sendMessage("setFileChooserInterceptedNoReply", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(EventType type, Listener<EventType> listener) {
|
||||
willAddFileChooserListener();
|
||||
listeners.add(type, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(EventType type, Listener<EventType> listener) {
|
||||
listeners.remove(type, listener);
|
||||
didRemoveFileChooserListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -554,7 +577,12 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(String selector, String files, SetInputFilesOptions options) {
|
||||
public void setInputFiles(String selector, File[] files, SetInputFilesOptions options) {
|
||||
mainFrame.setInputFiles(selector, files, convertViaJson(options, Frame.SetInputFilesOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(String selector, FileChooser.FilePayload[] files, SetInputFilesOptions options) {
|
||||
mainFrame.setInputFiles(selector, files, convertViaJson(options, Frame.SetInputFilesOptions.class));
|
||||
}
|
||||
|
||||
@@ -627,7 +655,20 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public Deferred<Event<EventType>> waitForEvent(EventType event, String optionsOrPredicate) {
|
||||
return listeners.waitForEvent(event, connection);
|
||||
Waitable<Event<EventType>> waitable;
|
||||
if (event == EventType.FILECHOOSER) {
|
||||
willAddFileChooserListener();
|
||||
waitable = new WaitableEvent<EventType>(listeners, event) {
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
didRemoveFileChooserListener();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
waitable = new WaitableEvent<>(listeners, event);
|
||||
}
|
||||
return toDeferred(waitable);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -680,19 +721,21 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
}
|
||||
|
||||
private class WaitableFrameDetach<R> extends WaitableEvent<EventType, R> {
|
||||
private class WaitableFrameDetach extends WaitableEvent<EventType> {
|
||||
WaitableFrameDetach(Frame frame) {
|
||||
super(listeners, EventType.FRAMEDETACHED, event -> frame.equals(event.data()));
|
||||
super(PageImpl.this.listeners, EventType.FRAMEDETACHED, event -> frame.equals(event.data()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public R get() {
|
||||
public Event<EventType> get() {
|
||||
throw new RuntimeException("Navigating frame was detached");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<T> Waitable<T> createWaitableFrameDetach(Frame frame) {
|
||||
return new WaitableFrameDetach<T>(frame);
|
||||
// It is safe to cast as WaitableFrameDetach.get() always throws.
|
||||
return (Waitable<T>) new WaitableFrameDetach(frame);
|
||||
}
|
||||
|
||||
<T> Waitable<T> createWaitForCloseHelper() {
|
||||
@@ -748,7 +791,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
return true;
|
||||
}
|
||||
return urlOrPredicate.equals(((Request) e.data()).url());
|
||||
}));
|
||||
}).apply(event -> (Request) event.data()));
|
||||
waitables.add(createWaitForCloseHelper());
|
||||
if (options != null && options.timeout != null) {
|
||||
waitables.add(new WaitableTimeout<>(options.timeout));
|
||||
@@ -764,7 +807,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
return true;
|
||||
}
|
||||
return urlOrPredicate.equals(((Response) e.data()).url());
|
||||
}));
|
||||
}).apply(event -> (Response) event.data()));
|
||||
waitables.add(createWaitForCloseHelper());
|
||||
if (options != null && options.timeout != null) {
|
||||
waitables.add(new WaitableTimeout<>(options.timeout));
|
||||
|
||||
@@ -18,6 +18,8 @@ package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.FileChooser;
|
||||
import com.microsoft.playwright.JSHandle;
|
||||
import com.microsoft.playwright.Keyboard;
|
||||
import com.microsoft.playwright.Mouse;
|
||||
@@ -185,4 +187,16 @@ class Serialization {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static JsonArray toJsonArray(FileChooser.FilePayload[] files) {
|
||||
JsonArray jsonFiles = new JsonArray();
|
||||
for (FileChooser.FilePayload p : files) {
|
||||
JsonObject jsonFile = new JsonObject();
|
||||
jsonFile.addProperty("name", p.name);
|
||||
jsonFile.addProperty("mimeType", p.mimeType);
|
||||
jsonFile.addProperty("buffer", Base64.getEncoder().encodeToString(p.buffer));
|
||||
jsonFiles.add(jsonFile);
|
||||
}
|
||||
return jsonFiles;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,11 @@
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.microsoft.playwright.FileChooser;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
|
||||
class Utils {
|
||||
@@ -90,4 +94,27 @@ class Utils {
|
||||
tokens.append('$');
|
||||
return tokens.toString();
|
||||
}
|
||||
|
||||
static FileChooser.FilePayload[] toFilePayloads(File[] files) {
|
||||
List<FileChooser.FilePayload> payloads = new ArrayList<>();
|
||||
for (File file : files) {
|
||||
String mimeType;
|
||||
try {
|
||||
mimeType = Files.probeContentType(file.toPath());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to determine mime type", e);
|
||||
}
|
||||
if (mimeType == null) {
|
||||
mimeType = "application/octet-stream";
|
||||
}
|
||||
byte[] buffer;
|
||||
try {
|
||||
buffer = Files.readAllBytes(file.toPath());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to read from file", e);
|
||||
}
|
||||
payloads.add(new FileChooser.FilePayload(file.getName(), mimeType, buffer));
|
||||
}
|
||||
return payloads.toArray(new FileChooser.FilePayload[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,12 +22,16 @@ import com.microsoft.playwright.Page;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
class WaitableEvent<EventType, ResultType> implements Waitable<ResultType>, Listener<EventType> {
|
||||
private final ListenerCollection<EventType> listeners;
|
||||
class WaitableEvent<EventType> implements Waitable<Event<EventType>>, Listener<EventType> {
|
||||
final ListenerCollection<EventType> listeners;
|
||||
private final EventType type;
|
||||
private final Predicate<Event<EventType>> predicate;
|
||||
private Event<EventType> event;
|
||||
|
||||
WaitableEvent(ListenerCollection<EventType> listeners, EventType type) {
|
||||
this(listeners, type, null);
|
||||
}
|
||||
|
||||
WaitableEvent(ListenerCollection<EventType> listeners, EventType type, Predicate<Event<EventType>> predicate) {
|
||||
this.listeners = listeners;
|
||||
this.type = type;
|
||||
@@ -38,7 +42,7 @@ class WaitableEvent<EventType, ResultType> implements Waitable<ResultType>, List
|
||||
@Override
|
||||
public void handle(Event<EventType> event) {
|
||||
assert type.equals(event.type());
|
||||
if (!predicate.test(event)) {
|
||||
if (predicate != null && !predicate.test(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -56,9 +60,8 @@ class WaitableEvent<EventType, ResultType> implements Waitable<ResultType>, List
|
||||
listeners.remove(type, this);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public ResultType get() {
|
||||
return (ResultType) event.data();
|
||||
public Event<EventType> get() {
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ class WorkerImpl extends ChannelOwner implements Worker {
|
||||
|
||||
@Override
|
||||
public Deferred<Event<EventType>> waitForEvent(EventType event) {
|
||||
return listeners.waitForEvent(event, connection);
|
||||
return toDeferred(new WaitableEvent<>(listeners, event));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* 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.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
|
||||
public class TestPageSetInputFiles extends TestBase {
|
||||
static File FILE_TO_UPLOAD = new File("src/test/resources/file-to-upload.txt");
|
||||
|
||||
@Test
|
||||
void shouldUploadTheFile() {
|
||||
page.navigate(server.PREFIX + "/input/fileupload.html");
|
||||
ElementHandle input = page.querySelector("input");
|
||||
input.setInputFiles(FILE_TO_UPLOAD);
|
||||
assertEquals("file-to-upload.txt", page.evaluate("e => e.files[0].name", input));
|
||||
assertEquals("contents of the file", page.evaluate("e => {\n" +
|
||||
" const reader = new FileReader();\n" +
|
||||
" const promise = new Promise(fulfill => reader.onload = fulfill);\n" +
|
||||
" reader.readAsText(e.files[0]);\n" +
|
||||
" return promise.then(() => reader.result);\n" +
|
||||
"}", input));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWork() {
|
||||
page.setContent("<input type=file>");
|
||||
page.setInputFiles("input", FILE_TO_UPLOAD);
|
||||
assertEquals(1, page.evalOnSelector("input", "input => input.files.length"));
|
||||
assertEquals("file-to-upload.txt", page.evalOnSelector("input", "input => input.files[0].name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSetFromMemory() {
|
||||
page.setContent("<input type=file>");
|
||||
page.setInputFiles("input", new FileChooser.FilePayload("test.txt","text/plain","this is a test".getBytes()));
|
||||
assertEquals(1, page.evalOnSelector("input", "input => input.files.length"));
|
||||
assertEquals("test.txt", page.evalOnSelector("input", "input => input.files[0].name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldEmitEventOnce() {
|
||||
page.setContent("<input type=file>");
|
||||
Deferred<Event<Page.EventType>> event = page.waitForEvent(Page.EventType.FILECHOOSER);
|
||||
page.click("input");
|
||||
FileChooser chooser = (FileChooser) event.get().data();
|
||||
assertNotNull(chooser);
|
||||
}
|
||||
|
||||
void shouldEmitEventOnOff() {
|
||||
// Not applicable in Java.
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldEmitEventAddListenerRemoveListener() {
|
||||
page.setContent("<input type=file>");
|
||||
FileChooser[] chooser = { null };
|
||||
page.addListener(Page.EventType.FILECHOOSER, new Listener<Page.EventType>() {
|
||||
@Override
|
||||
public void handle(Event<Page.EventType> event) {
|
||||
chooser[0] = (FileChooser) event.data();
|
||||
page.removeListener(Page.EventType.FILECHOOSER, this);
|
||||
}
|
||||
});
|
||||
page.click("input");
|
||||
Instant start = Instant.now();
|
||||
while (chooser[0] == null && Duration.between(start, Instant.now()).toMillis() < 10_000) {
|
||||
page.waitForTimeout(100).get();
|
||||
}
|
||||
assertNotNull(chooser[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWhenFileInputIsAttachedToDOM() {
|
||||
page.setContent("<input type=file>");
|
||||
Deferred<Event<Page.EventType>> chooser = page.waitForEvent(Page.EventType.FILECHOOSER);
|
||||
page.click("input");
|
||||
assertNotNull(chooser.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWhenFileInputIsNotAttachedToDOM() {
|
||||
Deferred<Event<Page.EventType>> chooser = page.waitForEvent(Page.EventType.FILECHOOSER);
|
||||
page.evaluate("() => {\n" +
|
||||
" const el = document.createElement('input');\n" +
|
||||
" el.type = 'file';\n" +
|
||||
" el.click();\n" +
|
||||
"}");
|
||||
assertNotNull(chooser.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithCSP() {
|
||||
server.setCSP("/empty.html", "default-src 'none'");
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
page.setContent("<input type=file>");
|
||||
page.setInputFiles("input", FILE_TO_UPLOAD);
|
||||
assertEquals(1, page.evalOnSelector("input", "input => input.files.length"));
|
||||
assertEquals("file-to-upload.txt", page.evalOnSelector("input", "input => input.files[0].name"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user