mirror of
https://github.com/microsoft/playwright-java.git
synced 2025-09-08 21:01:00 +00:00
feat: support timeout in waitForEvent, more FileChooser (#34)
This commit is contained in:
parent
6d5724f166
commit
a02e70ad84
@ -24,8 +24,11 @@ import java.io.*;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
abstract class Element {
|
||||
final String jsonName;
|
||||
final String jsonPath;
|
||||
@ -295,6 +298,20 @@ class Method extends Element {
|
||||
};
|
||||
customSignature.put("Page.setInputFiles", setInputFilesWithSelector);
|
||||
customSignature.put("Frame.setInputFiles", setInputFilesWithSelector);
|
||||
|
||||
String[] waitForEvent = {
|
||||
"default Deferred<Event<EventType>> waitForEvent(EventType event) {",
|
||||
" return waitForEvent(event, (WaitForEventOptions) null);",
|
||||
"}",
|
||||
"default Deferred<Event<EventType>> waitForEvent(EventType event, Predicate<Event<EventType>> predicate) {",
|
||||
" WaitForEventOptions options = new WaitForEventOptions();",
|
||||
" options.predicate = predicate;",
|
||||
" return waitForEvent(event, options);",
|
||||
"}",
|
||||
"Deferred<Event<EventType>> waitForEvent(EventType event, WaitForEventOptions options);",
|
||||
};
|
||||
customSignature.put("Page.waitForEvent", waitForEvent);
|
||||
customSignature.put("BrowserContext.waitForEvent", waitForEvent);
|
||||
}
|
||||
|
||||
Method(TypeDefinition parent, JsonObject jsonElement) {
|
||||
@ -503,7 +520,7 @@ class Interface extends TypeDefinition {
|
||||
"\n" +
|
||||
"package com.microsoft.playwright;\n";
|
||||
|
||||
private static Set<String> allowedBaseInterfaces = new HashSet<>(Arrays.asList("Browser", "JSHandle", "BrowserContext"));
|
||||
private static Set<String> allowedBaseInterfaces = new HashSet<>(asList("Browser", "JSHandle", "BrowserContext"));
|
||||
|
||||
Interface(JsonObject jsonElement) {
|
||||
super(null, jsonElement);
|
||||
@ -530,14 +547,14 @@ class Interface extends TypeDefinition {
|
||||
if (jsonName.equals("Route")) {
|
||||
output.add("import java.nio.charset.StandardCharsets;");
|
||||
}
|
||||
if (Arrays.asList("Page", "Frame", "ElementHandle", "FileChooser").contains(jsonName)) {
|
||||
if (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)) {
|
||||
if (asList("Page", "BrowserContext").contains(jsonName)) {
|
||||
output.add("import java.util.function.BiConsumer;");
|
||||
}
|
||||
if (Arrays.asList("Page", "Frame", "BrowserContext").contains(jsonName)) {
|
||||
if (asList("Page", "Frame", "BrowserContext").contains(jsonName)) {
|
||||
output.add("import java.util.function.Predicate;");
|
||||
output.add("import java.util.regex.Pattern;");
|
||||
}
|
||||
@ -589,10 +606,12 @@ class Interface extends TypeDefinition {
|
||||
switch (jsonName) {
|
||||
case "Mouse": {
|
||||
output.add(offset + "enum Button { LEFT, MIDDLE, RIGHT }");
|
||||
output.add("");
|
||||
break;
|
||||
}
|
||||
case "Keyboard": {
|
||||
output.add(offset + "enum Modifier { ALT, CONTROL, META, SHIFT }");
|
||||
output.add("");
|
||||
break;
|
||||
}
|
||||
case "Page": {
|
||||
@ -656,6 +675,7 @@ class Interface extends TypeDefinition {
|
||||
output.add(offset + " return password;");
|
||||
output.add(offset + " }");
|
||||
output.add(offset + "}");
|
||||
output.add("");
|
||||
break;
|
||||
}
|
||||
case "ElementHandle": {
|
||||
@ -665,6 +685,7 @@ class Interface extends TypeDefinition {
|
||||
output.add(offset + " public double width;");
|
||||
output.add(offset + " public double height;");
|
||||
output.add(offset + "}");
|
||||
output.add("");
|
||||
break;
|
||||
}
|
||||
case "FileChooser": {
|
||||
@ -679,12 +700,27 @@ class Interface extends TypeDefinition {
|
||||
output.add(offset + " this.buffer = buffer;");
|
||||
output.add(offset + " }");
|
||||
output.add(offset + "}");
|
||||
output.add("");
|
||||
break;
|
||||
}
|
||||
default: return;
|
||||
}
|
||||
if (asList("Page", "BrowserContext").contains(jsonName)){
|
||||
output.add(offset + "class WaitForEventOptions {");
|
||||
output.add(offset + " public Integer timeout;");
|
||||
output.add(offset + " public Predicate<Event<EventType>> predicate;");
|
||||
|
||||
output.add(offset + " public WaitForEventOptions withTimeout(int millis) {");
|
||||
output.add(offset + " timeout = millis;");
|
||||
output.add(offset + " return this;");
|
||||
output.add(offset + " }");
|
||||
output.add(offset + " public WaitForEventOptions withPredicate(Predicate<Event<EventType>> predicate) {");
|
||||
output.add(offset + " this.predicate = predicate;");
|
||||
output.add(offset + " return this;");
|
||||
output.add(offset + " }");
|
||||
output.add(offset + "}");
|
||||
output.add("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NestedClass extends TypeDefinition {
|
||||
|
@ -210,7 +210,7 @@ class Types {
|
||||
add("Page.setInputFiles.files", "string|Array<string>|Object|Array<Object>", "String");
|
||||
add("Page.unroute.url", "string|RegExp|function(URL):boolean", "String");
|
||||
add("Page.waitForEvent.event", "string", "EventType", new Empty());
|
||||
add("Page.waitForEvent.optionsOrPredicate", "Function|Object", "String");
|
||||
add("Page.waitForEvent.optionsOrPredicate", "Function|Object", "WaitForEventOptions");
|
||||
add("Page.waitForEvent", "Promise<Object>", "Deferred<Event<EventType>>", new Empty());
|
||||
add("Page.waitForRequest.urlOrPredicate", "string|RegExp|Function", "String");
|
||||
add("Page.waitForResponse.urlOrPredicate", "string|RegExp|Function", "String");
|
||||
|
@ -40,6 +40,19 @@ public interface BrowserContext {
|
||||
}
|
||||
}
|
||||
|
||||
class WaitForEventOptions {
|
||||
public Integer timeout;
|
||||
public Predicate<Event<EventType>> predicate;
|
||||
public WaitForEventOptions withTimeout(int millis) {
|
||||
timeout = millis;
|
||||
return this;
|
||||
}
|
||||
public WaitForEventOptions withPredicate(Predicate<Event<EventType>> predicate) {
|
||||
this.predicate = predicate;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
enum EventType {
|
||||
PAGE,
|
||||
}
|
||||
@ -120,8 +133,13 @@ public interface BrowserContext {
|
||||
void unroute(Pattern url, BiConsumer<Route, Request> handler);
|
||||
void unroute(Predicate<String> url, BiConsumer<Route, Request> handler);
|
||||
default Deferred<Event<EventType>> waitForEvent(EventType event) {
|
||||
return waitForEvent(event, null);
|
||||
return waitForEvent(event, (WaitForEventOptions) null);
|
||||
}
|
||||
Deferred<Event<EventType>> waitForEvent(EventType event, String optionsOrPredicate);
|
||||
default Deferred<Event<EventType>> waitForEvent(EventType event, Predicate<Event<EventType>> predicate) {
|
||||
WaitForEventOptions options = new WaitForEventOptions();
|
||||
options.predicate = predicate;
|
||||
return waitForEvent(event, options);
|
||||
}
|
||||
Deferred<Event<EventType>> waitForEvent(EventType event, WaitForEventOptions options);
|
||||
}
|
||||
|
||||
|
@ -61,6 +61,18 @@ public interface Page {
|
||||
String stack();
|
||||
}
|
||||
|
||||
class WaitForEventOptions {
|
||||
public Integer timeout;
|
||||
public Predicate<Event<EventType>> predicate;
|
||||
public WaitForEventOptions withTimeout(int millis) {
|
||||
timeout = millis;
|
||||
return this;
|
||||
}
|
||||
public WaitForEventOptions withPredicate(Predicate<Event<EventType>> predicate) {
|
||||
this.predicate = predicate;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
enum EventType {
|
||||
CLOSE,
|
||||
@ -962,9 +974,14 @@ public interface Page {
|
||||
Video video();
|
||||
Viewport viewportSize();
|
||||
default Deferred<Event<EventType>> waitForEvent(EventType event) {
|
||||
return waitForEvent(event, null);
|
||||
return waitForEvent(event, (WaitForEventOptions) null);
|
||||
}
|
||||
Deferred<Event<EventType>> waitForEvent(EventType event, String optionsOrPredicate);
|
||||
default Deferred<Event<EventType>> waitForEvent(EventType event, Predicate<Event<EventType>> predicate) {
|
||||
WaitForEventOptions options = new WaitForEventOptions();
|
||||
options.predicate = predicate;
|
||||
return waitForEvent(event, options);
|
||||
}
|
||||
Deferred<Event<EventType>> waitForEvent(EventType event, WaitForEventOptions options);
|
||||
default Deferred<JSHandle> waitForFunction(String pageFunction, Object arg) {
|
||||
return waitForFunction(pageFunction, arg, null);
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
final Map<String, Page.Binding> bindings = new HashMap<String, Page.Binding>();
|
||||
PageImpl ownerPage;
|
||||
private final ListenerCollection<EventType> listeners = new ListenerCollection<>();
|
||||
final TimeoutSettings timeoutSettings = new TimeoutSettings();
|
||||
|
||||
protected BrowserContextImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
@ -83,7 +84,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
|
||||
@Override
|
||||
public Browser browser() {
|
||||
return null;
|
||||
return browser;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -171,12 +172,18 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
|
||||
@Override
|
||||
public void setDefaultNavigationTimeout(int timeout) {
|
||||
|
||||
timeoutSettings.setDefaultNavigationTimeout(timeout);
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("timeout", timeout);
|
||||
sendMessage("setDefaultNavigationTimeoutNoReply", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultTimeout(int timeout) {
|
||||
|
||||
timeoutSettings.setDefaultTimeout(timeout);
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("timeout", timeout);
|
||||
sendMessage("setDefaultTimeoutNoReply", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -217,8 +224,14 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Deferred<Event<EventType>> waitForEvent(EventType event, String optionsOrPredicate) {
|
||||
return toDeferred(new WaitableEvent<>(listeners, event));
|
||||
public Deferred<Event<EventType>> waitForEvent(EventType event, WaitForEventOptions options) {
|
||||
if (options == null) {
|
||||
options = new WaitForEventOptions();
|
||||
}
|
||||
List<Waitable<Event<EventType>>> waitables = new ArrayList<>();
|
||||
waitables.add(new WaitableEvent<>(listeners, event, options.predicate));
|
||||
waitables.add(timeoutSettings.createWaitable(options.timeout));
|
||||
return toDeferred(new WaitableRace<>(waitables));
|
||||
}
|
||||
|
||||
private void unroute(UrlMatcher matcher, BiConsumer<Route, Request> handler) {
|
||||
|
@ -457,6 +457,9 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public Deferred<Void> waitForLoadState(LoadState state, WaitForLoadStateOptions options) {
|
||||
if (options == null) {
|
||||
options = new WaitForLoadStateOptions();
|
||||
}
|
||||
if (state == null) {
|
||||
state = LOAD;
|
||||
}
|
||||
@ -464,9 +467,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
List<Waitable<Void>> waitables = new ArrayList<>();
|
||||
waitables.add(new WaitForLoadStateHelper(state));
|
||||
waitables.add(page.createWaitForCloseHelper());
|
||||
if (options != null && options.timeout != null) {
|
||||
waitables.add(new WaitableTimeout<>(options.timeout));
|
||||
}
|
||||
waitables.add(page.createWaitableTimeout(options.timeout));
|
||||
return toDeferred(new WaitableRace<>(waitables));
|
||||
}
|
||||
|
||||
@ -586,9 +587,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
waitables.add(new WaitForNavigationHelper(matcher, options.waitUntil));
|
||||
waitables.add(page.createWaitForCloseHelper());
|
||||
waitables.add(page.createWaitableFrameDetach(this));
|
||||
if (options.timeout != null) {
|
||||
waitables.add(new WaitableTimeout<>(options.timeout));
|
||||
}
|
||||
waitables.add(page.createWaitableNavigationTimeout(options.timeout));
|
||||
return toDeferred(new WaitableRace<>(waitables));
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
BrowserContextImpl ownedContext;
|
||||
private boolean isClosed;
|
||||
final Set<Worker> workers = new HashSet<>();
|
||||
private final TimeoutSettings timeoutSettings;
|
||||
|
||||
PageImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
@ -53,6 +54,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
keyboard = new KeyboardImpl(this);
|
||||
mouse = new MouseImpl(this);
|
||||
frames.add(mainFrame);
|
||||
timeoutSettings = new TimeoutSettings(browserContext.timeoutSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -550,6 +552,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void setDefaultNavigationTimeout(int timeout) {
|
||||
timeoutSettings.setDefaultNavigationTimeout(timeout);
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("timeout", timeout);
|
||||
sendMessage("setDefaultNavigationTimeoutNoReply", params);
|
||||
@ -557,6 +560,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void setDefaultTimeout(int timeout) {
|
||||
timeoutSettings.setDefaultTimeout(timeout);
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("timeout", timeout);
|
||||
sendMessage("setDefaultTimeoutNoReply", params);
|
||||
@ -653,22 +657,34 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
return viewport;
|
||||
}
|
||||
|
||||
<T> Waitable<T> createWaitableNavigationTimeout(Integer timeout) {
|
||||
return new WaitableTimeout<>(timeoutSettings.navigationTimeout(timeout));
|
||||
}
|
||||
|
||||
<T> Waitable<T> createWaitableTimeout(Integer timeout) {
|
||||
return timeoutSettings.createWaitable(timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Deferred<Event<EventType>> waitForEvent(EventType event, String optionsOrPredicate) {
|
||||
Waitable<Event<EventType>> waitable;
|
||||
public Deferred<Event<EventType>> waitForEvent(EventType event, WaitForEventOptions options) {
|
||||
if (options == null) {
|
||||
options = new WaitForEventOptions();
|
||||
}
|
||||
List<Waitable<Event<EventType>>> waitables = new ArrayList<>();
|
||||
if (event == EventType.FILECHOOSER) {
|
||||
willAddFileChooserListener();
|
||||
waitable = new WaitableEvent<EventType>(listeners, event) {
|
||||
waitables.add(new WaitableEvent<EventType>(listeners, event, options.predicate) {
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
didRemoveFileChooserListener();
|
||||
}
|
||||
};
|
||||
});
|
||||
} else {
|
||||
waitable = new WaitableEvent<>(listeners, event);
|
||||
waitables.add(new WaitableEvent<>(listeners, event, options.predicate));
|
||||
}
|
||||
return toDeferred(waitable);
|
||||
waitables.add(createWaitableTimeout(options.timeout));
|
||||
return toDeferred(new WaitableRace<>(waitables));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -785,6 +801,9 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public Deferred<Request> waitForRequest(String urlOrPredicate, WaitForRequestOptions options) {
|
||||
if (options == null) {
|
||||
options = new WaitForRequestOptions();
|
||||
}
|
||||
List<Waitable<Request>> waitables = new ArrayList<>();
|
||||
waitables.add(new WaitableEvent<>(listeners, EventType.REQUEST,e -> {
|
||||
if (urlOrPredicate == null) {
|
||||
@ -793,14 +812,15 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
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));
|
||||
}
|
||||
waitables.add(createWaitableTimeout(options.timeout));
|
||||
return toDeferred(new WaitableRace<>(waitables));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Deferred<Response> waitForResponse(String urlOrPredicate, WaitForResponseOptions options) {
|
||||
if (options == null) {
|
||||
options = new WaitForResponseOptions();
|
||||
}
|
||||
List<Waitable<Response>> waitables = new ArrayList<>();
|
||||
waitables.add(new WaitableEvent<>(listeners, EventType.RESPONSE, e -> {
|
||||
if (urlOrPredicate == null) {
|
||||
@ -809,9 +829,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
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));
|
||||
}
|
||||
waitables.add(createWaitableTimeout(options.timeout));
|
||||
return toDeferred(new WaitableRace<>(waitables));
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* 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.impl;
|
||||
|
||||
class TimeoutSettings {
|
||||
private static final int DEFAULT_TIMEOUT_MS = 30_000;
|
||||
|
||||
private final TimeoutSettings parent;
|
||||
private Integer defaultTimeout ;
|
||||
private Integer defaultNavigationTimeout;
|
||||
|
||||
TimeoutSettings() {
|
||||
this(null);
|
||||
}
|
||||
TimeoutSettings(TimeoutSettings parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
void setDefaultTimeout(int timeout) {
|
||||
defaultTimeout = timeout;
|
||||
}
|
||||
|
||||
void setDefaultNavigationTimeout(int timeout) {
|
||||
defaultNavigationTimeout = timeout;
|
||||
}
|
||||
|
||||
int timeout(Integer timeout) {
|
||||
if (timeout != null) {
|
||||
return timeout;
|
||||
}
|
||||
if (defaultTimeout != null) {
|
||||
return defaultTimeout;
|
||||
}
|
||||
if (parent != null) {
|
||||
return parent.timeout(timeout);
|
||||
}
|
||||
return DEFAULT_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
int navigationTimeout(Integer timeout) {
|
||||
if (timeout != null) {
|
||||
return timeout;
|
||||
}
|
||||
if (defaultNavigationTimeout != null) {
|
||||
return defaultNavigationTimeout;
|
||||
}
|
||||
if (defaultTimeout != null) {
|
||||
return defaultTimeout;
|
||||
}
|
||||
if (parent != null) {
|
||||
return parent.navigationTimeout(timeout);
|
||||
}
|
||||
return DEFAULT_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
<T> Waitable<T> createWaitable(Integer timeout) {
|
||||
if (timeout != null && timeout == 0) {
|
||||
return new WaitableNever<>();
|
||||
}
|
||||
return new WaitableTimeout<>(timeout(timeout));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 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.impl;
|
||||
|
||||
public class WaitableNever<T> implements Waitable<T> {
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
throw new IllegalStateException("Should never be called");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
}
|
@ -22,7 +22,7 @@ class WaitableTimeout<T> implements Waitable<T> {
|
||||
|
||||
WaitableTimeout(int millis) {
|
||||
timeout = millis;
|
||||
deadline = System.nanoTime() + millis * 1_000_000;
|
||||
deadline = System.nanoTime() + (long) millis * 1_000_000;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -39,3 +39,4 @@ class WaitableTimeout<T> implements Waitable<T> {
|
||||
public void dispose() {
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,9 +21,12 @@ import org.junit.jupiter.api.Test;
|
||||
import java.io.File;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
|
||||
public class TestPageSetInputFiles extends TestBase {
|
||||
@ -119,4 +122,185 @@ public class TestPageSetInputFiles extends TestBase {
|
||||
assertEquals(1, page.evalOnSelector("input", "input => input.files.length"));
|
||||
assertEquals("file-to-upload.txt", page.evalOnSelector("input", "input => input.files[0].name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRespectTimeout() {
|
||||
try {
|
||||
Deferred<Event<Page.EventType>> event = page.waitForEvent(Page.EventType.FILECHOOSER, new Page.WaitForEventOptions().withTimeout(1));
|
||||
event.get();
|
||||
fail("did not throw");
|
||||
} catch (RuntimeException e) {
|
||||
assertTrue(e.getMessage().contains("Timeout 1ms exceeded"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRespectDefaultTimeoutWhenThereIsNoCustomTimeout() {
|
||||
page.setDefaultTimeout(1);
|
||||
try {
|
||||
Deferred<Event<Page.EventType>> event = page.waitForEvent(Page.EventType.FILECHOOSER);
|
||||
event.get();
|
||||
fail("did not throw");
|
||||
} catch (RuntimeException e) {
|
||||
assertTrue(e.getMessage().contains("Timeout 1ms exceeded"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPrioritizeExactTimeoutOverDefaultTimeout() {
|
||||
page.setDefaultTimeout(0);
|
||||
Deferred<Event<Page.EventType>> event = page.waitForEvent(Page.EventType.FILECHOOSER,
|
||||
new Page.WaitForEventOptions().withTimeout(1));
|
||||
try {
|
||||
event.get();
|
||||
fail("did not throw");
|
||||
} catch (RuntimeException e) {
|
||||
assertTrue(e.getMessage().contains("Timeout 1ms exceeded"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithNoTimeout() {
|
||||
Deferred<Event<Page.EventType>> event = page.waitForEvent(Page.EventType.FILECHOOSER,
|
||||
new Page.WaitForEventOptions().withTimeout(0));
|
||||
page.evaluate("() => setTimeout(() => {\n" +
|
||||
" const el = document.createElement('input');\n" +
|
||||
" el.type = 'file';\n" +
|
||||
" el.click();\n" +
|
||||
"}, 50)");
|
||||
assertNotNull(event.get().data());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnTheSameFileChooserWhenThereAreManyWatchdogsSimultaneously() {
|
||||
page.setContent("<input type=file>");
|
||||
Deferred<Event<Page.EventType>> fileChooser1 = page.waitForEvent(Page.EventType.FILECHOOSER);
|
||||
Deferred<Event<Page.EventType>> fileChooser2 = page.waitForEvent(Page.EventType.FILECHOOSER);
|
||||
page.evalOnSelector("input", "input => input.click()");
|
||||
assertEquals(fileChooser1.get().data(), fileChooser2.get().data());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAcceptSingleFile() {
|
||||
page.setContent("<input type=file oninput='javascript:console.timeStamp()'>");
|
||||
Deferred<Event<Page.EventType>> event = page.waitForEvent(Page.EventType.FILECHOOSER);
|
||||
page.click("input");
|
||||
FileChooser fileChooser = (FileChooser) event.get().data();
|
||||
assertEquals(page, fileChooser.page());
|
||||
assertNotNull(fileChooser.element());
|
||||
fileChooser.setFiles(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 shouldDetectMimeType() throws ExecutionException, InterruptedException {
|
||||
// TODO: Parse form fields on server
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBeAbleToReadSelectedFile() {
|
||||
page.setContent("<input type=file>");
|
||||
page.addListener(Page.EventType.FILECHOOSER, event -> {
|
||||
FileChooser fileChooser = (FileChooser) event.data();
|
||||
fileChooser.setFiles(FILE_TO_UPLOAD);
|
||||
});
|
||||
Object content = page.evalOnSelector("input", "async picker => {\n" +
|
||||
" picker.click();\n" +
|
||||
" await new Promise(x => picker.oninput = x);\n" +
|
||||
" const reader = new FileReader();\n" +
|
||||
" const promise = new Promise(fulfill => reader.onload = fulfill);\n" +
|
||||
" reader.readAsText(picker.files[0]);\n" +
|
||||
" return promise.then(() => reader.result);\n" +
|
||||
"}");
|
||||
assertEquals("contents of the file", content);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBeAbleToResetSelectedFilesWithEmptyFileList() {
|
||||
page.setContent("<input type=file>");
|
||||
page.addListener(Page.EventType.FILECHOOSER, new Listener<Page.EventType>() {
|
||||
@Override
|
||||
public void handle(Event<Page.EventType> event) {
|
||||
FileChooser fileChooser = (FileChooser) event.data();
|
||||
fileChooser.setFiles(FILE_TO_UPLOAD);
|
||||
page.removeListener(Page.EventType.FILECHOOSER, this);
|
||||
}
|
||||
});
|
||||
Object fileLength1 = page.evalOnSelector("input", "async picker => {\n" +
|
||||
" picker.click();\n" +
|
||||
" await new Promise(x => picker.oninput = x);\n" +
|
||||
" return picker.files.length;\n" +
|
||||
"}");
|
||||
assertEquals(1, fileLength1);
|
||||
|
||||
page.addListener(Page.EventType.FILECHOOSER, new Listener<Page.EventType>() {
|
||||
@Override
|
||||
public void handle(Event<Page.EventType> event) {
|
||||
FileChooser fileChooser = (FileChooser) event.data();
|
||||
fileChooser.setFiles(new File[0]);
|
||||
page.removeListener(Page.EventType.FILECHOOSER, this);
|
||||
}
|
||||
});
|
||||
Object fileLength2 = page.evalOnSelector("input", "async picker => {\n" +
|
||||
" picker.click();\n" +
|
||||
" await new Promise(x => picker.oninput = x);\n" +
|
||||
" return picker.files.length;\n" +
|
||||
"}");
|
||||
assertEquals(0, fileLength2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotAcceptMultipleFilesForSingleFileInput() {
|
||||
page.setContent("<input type=file>");
|
||||
Deferred<Event<Page.EventType>> event = page.waitForEvent(Page.EventType.FILECHOOSER);
|
||||
page.click("input");
|
||||
FileChooser fileChooser = (FileChooser) event.get().data();
|
||||
try {
|
||||
fileChooser.setFiles(new File[]{FILE_TO_UPLOAD, new File("src/test/resources/pptr.png")});
|
||||
fail("did not throw");
|
||||
} catch (RuntimeException e) {
|
||||
assertTrue(e.getMessage().contains("Non-multiple file input can only accept single file"));
|
||||
}
|
||||
}
|
||||
@Test
|
||||
void shouldEmitInputAndChangeEvents() {
|
||||
List<Object> events = new ArrayList<>();
|
||||
page.exposeFunction("eventHandled", args -> events.add(args[0]));
|
||||
page.setContent("<input id=input type=file></input>\n" +
|
||||
"<script>\n" +
|
||||
" input.addEventListener('input', e => eventHandled(e.type));\n" +
|
||||
" input.addEventListener('change', e => eventHandled(e.type));\n" +
|
||||
"</script>");
|
||||
page.querySelector("input").setInputFiles(FILE_TO_UPLOAD);
|
||||
assertEquals(asList("input", "change"), events);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkForSingleFilePick() {
|
||||
page.setContent("<input type=file>");
|
||||
Deferred<Event<Page.EventType>> event = page.waitForEvent(Page.EventType.FILECHOOSER);
|
||||
page.click("input");
|
||||
FileChooser fileChooser = (FileChooser) event.get().data();
|
||||
assertFalse(fileChooser.isMultiple());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkForMultiple() {
|
||||
page.setContent("<input multiple type=file>");
|
||||
Deferred<Event<Page.EventType>> event = page.waitForEvent(Page.EventType.FILECHOOSER);
|
||||
page.click("input");
|
||||
FileChooser fileChooser = (FileChooser) event.get().data();
|
||||
assertTrue(fileChooser.isMultiple());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkForWebkitdirectory() {
|
||||
page.setContent("<input multiple webkitdirectory type=file>");
|
||||
Deferred<Event<Page.EventType>> event = page.waitForEvent(Page.EventType.FILECHOOSER);
|
||||
page.click("input");
|
||||
FileChooser fileChooser = (FileChooser) event.get().data();
|
||||
assertTrue(fileChooser.isMultiple());
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user