feat: implement remaining frame methods (#49)

This commit is contained in:
Yury Semikhatsky 2020-10-27 16:33:41 -07:00 committed by GitHub
parent efa1a11c0f
commit ec49645c16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 216 additions and 28 deletions

View File

@ -165,7 +165,7 @@ class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
JsonObject params = new JsonObject(); JsonObject params = new JsonObject();
params.addProperty("type", type); params.addProperty("type", type);
params.add("eventInit", new Gson().toJsonTree(serializeArgument(eventInit))); params.add("eventInit", new Gson().toJsonTree(serializeArgument(eventInit)));
sendMessage("dispatchEvent", params).getAsJsonObject(); sendMessage("dispatchEvent", params);
} }
@Override @Override

View File

@ -38,7 +38,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
FrameImpl parentFrame; FrameImpl parentFrame;
Set<FrameImpl> childFrames = new LinkedHashSet<>(); Set<FrameImpl> childFrames = new LinkedHashSet<>();
private final Set<LoadState> loadStates = new HashSet<>(); private final Set<LoadState> loadStates = new HashSet<>();
enum InternalEventType { NAVIGATED, LOADSTATE }; enum InternalEventType { NAVIGATED, LOADSTATE }
private final ListenerCollection<InternalEventType> internalListeners = new ListenerCollection<>(); private final ListenerCollection<InternalEventType> internalListeners = new ListenerCollection<>();
PageImpl page; PageImpl page;
boolean isDetached; boolean isDetached;
@ -66,20 +66,6 @@ public class FrameImpl extends ChannelOwner implements Frame {
} }
} }
private Object evaluate(String expression, Object arg, boolean forceExpression) {
JsonObject params = new JsonObject();
params.addProperty("expression", expression);
params.addProperty("world", "main");
if (!isFunctionBody(expression)) {
forceExpression = true;
}
params.addProperty("isFunction", !forceExpression);
params.add("arg", new Gson().toJsonTree(serializeArgument(arg)));
JsonElement json = sendMessage("evaluateExpression", params);
SerializedValue value = new Gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
return deserialize(value);
}
@Override @Override
public ElementHandle querySelector(String selector) { public ElementHandle querySelector(String selector) {
JsonObject params = new JsonObject(); JsonObject params = new JsonObject();
@ -140,7 +126,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject(); JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
if (options.path != null) { if (options.path != null) {
params.remove("path"); params.remove("path");
byte[] encoded = new byte[0]; byte[] encoded;
try { try {
encoded = Files.readAllBytes(options.path.toPath()); encoded = Files.readAllBytes(options.path.toPath());
} catch (IOException e) { } catch (IOException e) {
@ -162,7 +148,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject(); JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
if (options.path != null) { if (options.path != null) {
params.remove("path"); params.remove("path");
byte[] encoded = new byte[0]; byte[] encoded;
try { try {
encoded = Files.readAllBytes(options.path.toPath()); encoded = Files.readAllBytes(options.path.toPath());
} catch (IOException e) { } catch (IOException e) {
@ -240,12 +226,26 @@ public class FrameImpl extends ChannelOwner implements Frame {
@Override @Override
public void dispatchEvent(String selector, String type, Object eventInit, DispatchEventOptions options) { public void dispatchEvent(String selector, String type, Object eventInit, DispatchEventOptions options) {
if (options == null) {
options = new DispatchEventOptions();
}
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
params.addProperty("selector", selector);
params.addProperty("type", type);
params.add("eventInit", new Gson().toJsonTree(serializeArgument(eventInit)));
sendMessage("dispatchEvent", params);
} }
@Override @Override
public Object evaluate(String pageFunction, Object arg) { public Object evaluate(String expression, Object arg) {
return evaluate(pageFunction, arg, false); JsonObject params = new JsonObject();
params.addProperty("expression", expression);
params.addProperty("world", "main");
params.addProperty("isFunction", isFunctionBody(expression));
params.add("arg", new Gson().toJsonTree(serializeArgument(arg)));
JsonElement json = sendMessage("evaluateExpression", params);
SerializedValue value = new Gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
return deserialize(value);
} }
@Override @Override
@ -282,15 +282,25 @@ public class FrameImpl extends ChannelOwner implements Frame {
@Override @Override
public ElementHandle frameElement() { public ElementHandle frameElement() {
return null; JsonObject json = sendMessage("frameElement").getAsJsonObject();
return connection.getExistingObject(json.getAsJsonObject("element").get("guid").getAsString());
} }
@Override @Override
public String getAttribute(String selector, String name, GetAttributeOptions options) { public String getAttribute(String selector, String name, GetAttributeOptions options) {
if (options == null) {
options = new GetAttributeOptions();
}
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
params.addProperty("selector", selector);
params.addProperty("name", name);
JsonObject json = sendMessage("getAttribute", params).getAsJsonObject();
if (json.has("value")) {
return json.get("value").getAsString();
}
return null; return null;
} }
@Override @Override
public ResponseImpl navigate(String url, NavigateOptions options) { public ResponseImpl navigate(String url, NavigateOptions options) {
if (options == null) { if (options == null) {
@ -312,17 +322,34 @@ public class FrameImpl extends ChannelOwner implements Frame {
@Override @Override
public void hover(String selector, HoverOptions options) { public void hover(String selector, HoverOptions options) {
if (options == null) {
options = new HoverOptions();
}
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
params.addProperty("selector", selector);
sendMessage("hover", params);
} }
@Override @Override
public String innerHTML(String selector, InnerHTMLOptions options) { public String innerHTML(String selector, InnerHTMLOptions options) {
return null; if (options == null) {
options = new InnerHTMLOptions();
}
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
params.addProperty("selector", selector);
JsonObject json = sendMessage("innerHTML", params).getAsJsonObject();
return json.get("value").getAsString();
} }
@Override @Override
public String innerText(String selector, InnerTextOptions options) { public String innerText(String selector, InnerTextOptions options) {
return null; if (options == null) {
options = new InnerTextOptions();
}
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
params.addProperty("selector", selector);
JsonObject json = sendMessage("innerText", params).getAsJsonObject();
return json.get("value").getAsString();
} }
@Override @Override
@ -445,7 +472,13 @@ public class FrameImpl extends ChannelOwner implements Frame {
@Override @Override
public void type(String selector, String text, TypeOptions options) { public void type(String selector, String text, TypeOptions options) {
if (options == null) {
options = new TypeOptions();
}
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
params.addProperty("selector", selector);
params.addProperty("text", text);
sendMessage("type", params);
} }
@Override @Override
@ -642,7 +675,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
@Override @Override
public Deferred<Void> waitForTimeout(int timeout) { public Deferred<Void> waitForTimeout(int timeout) {
return toDeferred(new WaitableTimeout(timeout) { return toDeferred(new WaitableTimeout<Void>(timeout) {
@Override @Override
public Void get() { public Void get() {
// Override to not throw. // Override to not throw.

View File

@ -0,0 +1,155 @@
/*
* 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 static com.microsoft.playwright.Utils.mapOf;
import static org.junit.jupiter.api.Assertions.*;
public class TestDispatchEvent extends TestBase {
@Test
void shouldDispatchClickEvent() {
page.navigate(server.PREFIX + "/input/button.html");
page.dispatchEvent("button", "click");
assertEquals("Clicked", page.evaluate("() => window['result']"));
}
@Test
void shouldDispatchClickEventProperties() {
page.navigate(server.PREFIX + "/input/button.html");
page.dispatchEvent("button", "click");
assertNotNull(page.evaluate("bubbles"));
assertNotNull(page.evaluate("cancelable"));
assertNotNull(page.evaluate("composed"));
}
@Test
void shouldDispatchClickSvg() {
page.setContent("<svg height='100' width='100'>\n" +
" <circle onclick='javascript:window.__CLICKED=42' cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red' />\n" +
"</svg>");
page.dispatchEvent("circle", "click");
assertEquals(42, page.evaluate("() => window['__CLICKED']"));
}
@Test
void shouldDispatchClickOnASpanWithAnInlineElementInside() {
page.setContent("<style>\n" +
"span::before {\n" +
" content: 'q';\n" +
"}\n" +
"</style>\n" +
"<span onclick='javascript:window.CLICKED=42'></span>");
page.dispatchEvent("span", "click");
assertEquals(42, page.evaluate("() => window['CLICKED']"));
}
@Test
void shouldDispatchClickAfterNavigation() {
page.navigate(server.PREFIX + "/input/button.html");
page.dispatchEvent("button", "click");
page.navigate(server.PREFIX + "/input/button.html");
page.dispatchEvent("button", "click");
assertEquals("Clicked", page.evaluate("() => window['result']"));
}
@Test
void shouldDispatchClickAfterACrossOriginNavigation() {
page.navigate(server.PREFIX + "/input/button.html");
page.dispatchEvent("button", "click");
page.navigate(server.CROSS_PROCESS_PREFIX + "/input/button.html");
page.dispatchEvent("button", "click");
assertEquals("Clicked", page.evaluate("() => window['result']"));
}
@Test
void shouldNotFailWhenElementIsBlockedOnHover() {
page.setContent("<style>\n" +
" container { display: block; position: relative; width: 200px; height: 50px; }\n" +
" div, button { position: absolute; left: 0; top: 0; bottom: 0; right: 0; }\n" +
" div { pointer-events: none; }\n" +
" container:hover div { pointer-events: auto; background: red; }\n" +
"</style>\n" +
"<container>\n" +
" <button onclick='window.clicked=true'>Click me</button>\n" +
" <div></div>\n" +
"</container>");
page.dispatchEvent("button", "click");
assertNotNull(page.evaluate("() => window['clicked']"));
}
@Test
void shouldDispatchClickWhenNodeIsAddedInShadowDom() {
page.navigate(server.EMPTY_PAGE);
page.evaluate("() => {\n" +
" const div = document.createElement('div');\n" +
" div.attachShadow({mode: 'open'});\n" +
" document.body.appendChild(div);\n" +
"}");
page.evaluate("() => new Promise(f => setTimeout(f, 100))");
page.evaluate("() => {\n" +
" const span = document.createElement('span');\n" +
" span.textContent = 'Hello from shadow';\n" +
" span.addEventListener('click', () => window['clicked'] = true);\n" +
" document.querySelector('div').shadowRoot.appendChild(span);\n" +
"}");
// TODO: do it asynchronously before evals?
page.dispatchEvent("span", "click");
assertEquals(true, page.evaluate("() => window['clicked']"));
}
// @Test
void shouldBeAtomic() {
// TODO: playwright.selectors.register
}
@Test
void shouldDispatchDragDropEvents() {
page.navigate(server.PREFIX + "/drag-n-drop.html");
JSHandle dataTransfer = page.evaluateHandle("() => new DataTransfer()");
page.dispatchEvent("#source", "dragstart", mapOf("dataTransfer", dataTransfer));
page.dispatchEvent("#target", "drop", mapOf("dataTransfer", dataTransfer));
ElementHandle source = page.querySelector("#source");
ElementHandle target = page.querySelector("#target");
assertEquals(true, page.evaluate("({source, target}) => {\n" +
" return source.parentElement === target;\n" +
"}", mapOf("source", source,"target", target)));
}
@Test
void shouldDispatchDragDropEventsOnHandle() {
page.navigate(server.PREFIX + "/drag-n-drop.html");
JSHandle dataTransfer = page.evaluateHandle("() => new DataTransfer()");
ElementHandle source = page.querySelector("#source");
source.dispatchEvent("dragstart", mapOf("dataTransfer", dataTransfer));
ElementHandle target = page.querySelector("#target");
target.dispatchEvent("drop", mapOf("dataTransfer", dataTransfer));
assertEquals(true, page.evaluate("({source, target}) => {\n" +
" return source.parentElement === target;\n" +
"}", mapOf("source", source,"target", target)));
}
@Test
void shouldDispatchClickEventOnHandle() {
page.navigate(server.PREFIX + "/input/button.html");
ElementHandle button = page.querySelector("button");
button.dispatchEvent("click");
assertEquals("Clicked", page.evaluate("() => window['result']"));
}
}