1
0
mirror of synced 2026-05-22 18:53:15 +00:00

feat: support FileChooser (#33)

This commit is contained in:
Yury Semikhatsky
2020-10-22 10:57:36 -07:00
committed by GitHub
parent 7b4cf7276b
commit 6d5724f166
18 changed files with 381 additions and 78 deletions
@@ -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"));
}
}