mirror of
https://github.com/microsoft/playwright-java.git
synced 2025-09-08 21:01:00 +00:00
feat: ElementHandle basic functionality (#29)
This commit is contained in:
parent
3690079b4f
commit
5e02d0ae49
@ -183,6 +183,7 @@ class Types {
|
||||
add("Page.waitForTimeout", "Promise", "Deferred<Void>", new Empty());
|
||||
add("Frame.waitForFunction", "Promise<JSHandle>", "Deferred<JSHandle>", new Empty());
|
||||
add("Page.waitForFunction", "Promise<JSHandle>", "Deferred<JSHandle>", new Empty());
|
||||
add("ElementHandle.waitForElementState", "Promise", "Deferred<Void>", new Empty());
|
||||
|
||||
// Custom options
|
||||
add("Page.pdf.options.margin.top", "string|number", "String");
|
||||
|
@ -434,10 +434,10 @@ public interface ElementHandle extends JSHandle {
|
||||
uncheck(null);
|
||||
}
|
||||
void uncheck(UncheckOptions options);
|
||||
default void waitForElementState(ElementState state) {
|
||||
waitForElementState(state, null);
|
||||
default Deferred<Void> waitForElementState(ElementState state) {
|
||||
return waitForElementState(state, null);
|
||||
}
|
||||
void waitForElementState(ElementState state, WaitForElementStateOptions options);
|
||||
Deferred<Void> waitForElementState(ElementState state, WaitForElementStateOptions options);
|
||||
default Deferred<ElementHandle> waitForSelector(String selector) {
|
||||
return waitForSelector(selector, null);
|
||||
}
|
||||
|
@ -20,17 +20,16 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Deferred;
|
||||
import com.microsoft.playwright.ElementHandle;
|
||||
import com.microsoft.playwright.Frame;
|
||||
import com.microsoft.playwright.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.toProtocol;
|
||||
import static com.microsoft.playwright.impl.Serialization.*;
|
||||
import static com.microsoft.playwright.impl.Utils.isFunctionBody;
|
||||
|
||||
public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
public ElementHandleImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
ElementHandleImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
}
|
||||
|
||||
@ -69,12 +68,26 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
|
||||
@Override
|
||||
public Object evalOnSelector(String selector, String pageFunction, Object arg) {
|
||||
return null;
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.addProperty("isFunction", isFunctionBody(pageFunction));
|
||||
params.add("arg", new Gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evalOnSelector", params);
|
||||
SerializedValue value = new Gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evalOnSelectorAll(String selector, String pageFunction, Object arg) {
|
||||
return null;
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.addProperty("isFunction", isFunctionBody(pageFunction));
|
||||
params.add("arg", new Gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evalOnSelectorAll", params);
|
||||
SerializedValue value = new Gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
return deserialize(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -99,12 +112,12 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
|
||||
params.remove("button");
|
||||
if (options.button != null) {
|
||||
params.addProperty("button", toProtocol(options.button));
|
||||
params.addProperty("button", Serialization.toProtocol(options.button));
|
||||
}
|
||||
|
||||
params.remove("modifiers");
|
||||
if (options.modifiers != null) {
|
||||
params.add("modifiers", toProtocol(options.modifiers));
|
||||
params.add("modifiers", Serialization.toProtocol(options.modifiers));
|
||||
}
|
||||
|
||||
sendMessage("click", params);
|
||||
@ -124,12 +137,12 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
|
||||
params.remove("button");
|
||||
if (options.button != null) {
|
||||
params.addProperty("button", toProtocol(options.button));
|
||||
params.addProperty("button", Serialization.toProtocol(options.button));
|
||||
}
|
||||
|
||||
params.remove("modifiers");
|
||||
if (options.modifiers != null) {
|
||||
params.add("modifiers", toProtocol(options.modifiers));
|
||||
params.add("modifiers", Serialization.toProtocol(options.modifiers));
|
||||
}
|
||||
|
||||
sendMessage("dblclick", params);
|
||||
@ -137,47 +150,71 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
|
||||
@Override
|
||||
public void dispatchEvent(String type, Object eventInit) {
|
||||
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("type", type);
|
||||
params.add("eventInit", new Gson().toJsonTree(serializeArgument(eventInit)));
|
||||
sendMessage("dispatchEvent", params).getAsJsonObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fill(String value, FillOptions options) {
|
||||
|
||||
if (options == null) {
|
||||
options = new FillOptions();
|
||||
}
|
||||
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("value", value);
|
||||
sendMessage("fill", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focus() {
|
||||
|
||||
sendMessage("focus", new JsonObject());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttribute(String name) {
|
||||
return null;
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("name", name);
|
||||
JsonObject json = sendMessage("getAttribute", params).getAsJsonObject();
|
||||
return json.has("value") ? json.get("value").getAsString() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hover(HoverOptions options) {
|
||||
|
||||
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
|
||||
params.remove("modifiers");
|
||||
if (options.modifiers != null) {
|
||||
params.add("modifiers", Serialization.toProtocol(options.modifiers));
|
||||
}
|
||||
sendMessage("hover", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String innerHTML() {
|
||||
return null;
|
||||
JsonObject json = sendMessage("innerHTML", new JsonObject()).getAsJsonObject();
|
||||
return json.get("value").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String innerText() {
|
||||
return null;
|
||||
JsonObject json = sendMessage("innerText", new JsonObject()).getAsJsonObject();
|
||||
return json.get("value").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Frame ownerFrame() {
|
||||
return null;
|
||||
JsonObject json = sendMessage("ownerFrame", new JsonObject()).getAsJsonObject();
|
||||
return connection.getExistingObject(json.getAsJsonObject("frame").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void press(String key, PressOptions options) {
|
||||
|
||||
if (options == null) {
|
||||
options = new PressOptions();
|
||||
}
|
||||
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("key", key);
|
||||
sendMessage("press", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -187,17 +224,26 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
|
||||
@Override
|
||||
public void scrollIntoViewIfNeeded(ScrollIntoViewIfNeededOptions options) {
|
||||
|
||||
if (options == null) {
|
||||
options = new ScrollIntoViewIfNeededOptions();
|
||||
}
|
||||
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("scrollIntoViewIfNeeded", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String values, SelectOptionOptions options) {
|
||||
// TODO:
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectText(SelectTextOptions options) {
|
||||
|
||||
if (options == null) {
|
||||
options = new SelectTextOptions();
|
||||
}
|
||||
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("selectText", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -207,12 +253,18 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
|
||||
@Override
|
||||
public String textContent() {
|
||||
return null;
|
||||
JsonObject json = sendMessage("textContent", new JsonObject()).getAsJsonObject();
|
||||
return json.has("value") ? json.get("value").getAsString() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void type(String text, TypeOptions options) {
|
||||
|
||||
if (options == null) {
|
||||
options = new TypeOptions();
|
||||
}
|
||||
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("text", text);
|
||||
sendMessage("type", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -225,12 +277,38 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waitForElementState(ElementState state, WaitForElementStateOptions options) {
|
||||
public Deferred<Void> waitForElementState(ElementState state, WaitForElementStateOptions options) {
|
||||
if (options == null) {
|
||||
options = new WaitForElementStateOptions();
|
||||
}
|
||||
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("state", toProtocol(state));
|
||||
return toDeferred(sendMessageAsync("waitForElementState", params).apply(json -> null));
|
||||
}
|
||||
|
||||
private static String toProtocol(ElementState state) {
|
||||
if (state == null) {
|
||||
throw new IllegalArgumentException("State cannot by null");
|
||||
}
|
||||
return state.toString().toLowerCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Deferred<ElementHandle> waitForSelector(String selector, WaitForSelectorOptions options) {
|
||||
return null;
|
||||
if (options == null) {
|
||||
options = new WaitForSelectorOptions();
|
||||
}
|
||||
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
|
||||
params.remove("state");
|
||||
params.addProperty("state", toProtocol(options.state));
|
||||
params.addProperty("selector", selector);
|
||||
return toDeferred(sendMessageAsync("waitForElementState", params).apply(json -> null));
|
||||
}
|
||||
|
||||
private static String toProtocol(WaitForSelectorOptions.State state) {
|
||||
if (state == null) {
|
||||
state = WaitForSelectorOptions.State.VISIBLE;
|
||||
}
|
||||
return state.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,153 @@
|
||||
/**
|
||||
* 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.ElementHandle.ElementState.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class TestElementHandleWaitForElementState extends TestBase {
|
||||
|
||||
static void giveItAChanceToResolve(Page page) {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
page.evaluate("() => new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f)))");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWaitForVisible() {
|
||||
page.setContent("<div style='display:none'>content</div>");
|
||||
ElementHandle div = page.querySelector("div");
|
||||
Deferred<Void> promise = div.waitForElementState(VISIBLE);
|
||||
giveItAChanceToResolve(page);
|
||||
div.evaluate("div => div.style.display = 'block'");
|
||||
promise.get();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWaitForAlreadyVisible() {
|
||||
page.setContent("<div>content</div>");
|
||||
ElementHandle div = page.querySelector("div");
|
||||
div.waitForElementState(VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldTimeoutWaitingForVisible() {
|
||||
page.setContent("<div style='display:none'>content</div>");
|
||||
ElementHandle div = page.querySelector("div");
|
||||
Deferred<Void> result = div.waitForElementState(VISIBLE, new ElementHandle.WaitForElementStateOptions().withTimeout(1000));
|
||||
try {
|
||||
result.get();
|
||||
fail("did not throw");
|
||||
} catch (RuntimeException e) {
|
||||
assertTrue(e.getMessage().contains("Timeout 1000ms exceeded"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowWaitingForVisibleWhenDetached() {
|
||||
page.setContent("<div style='display:none'>content</div>");
|
||||
ElementHandle div = page.querySelector("div");
|
||||
Deferred<Void> promise = div.waitForElementState(VISIBLE);
|
||||
div.evaluate("div => div.remove()");
|
||||
try {
|
||||
promise.get();
|
||||
fail("did not throw");
|
||||
} catch (RuntimeException e) {
|
||||
assertTrue(e.getMessage().contains("Element is not attached to the DOM"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWaitForHidden() {
|
||||
page.setContent("<div>content</div>");
|
||||
ElementHandle div = page.querySelector("div");
|
||||
Deferred<Void> promise = div.waitForElementState(HIDDEN);
|
||||
giveItAChanceToResolve(page);
|
||||
div.evaluate("div => div.style.display = 'none'");
|
||||
promise.get();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWaitForAlreadyHidden() {
|
||||
page.setContent("<div></div>");
|
||||
ElementHandle div = page.querySelector("div");
|
||||
Deferred<Void> result = div.waitForElementState(HIDDEN);
|
||||
result.get();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWaitForHiddenWhenDetached() {
|
||||
page.setContent("<div>content</div>");
|
||||
ElementHandle div = page.querySelector("div");
|
||||
Deferred<Void> promise = div.waitForElementState(HIDDEN);
|
||||
giveItAChanceToResolve(page);
|
||||
div.evaluate("div => div.remove()");
|
||||
promise.get();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWaitForEnabledButton() {
|
||||
page.setContent("<button disabled><span>Target</span></button>");
|
||||
ElementHandle span = page.querySelector("text=Target");
|
||||
Deferred<Void> promise = span.waitForElementState(ENABLED);
|
||||
giveItAChanceToResolve(page);
|
||||
span.evaluate("span => span.parentElement.disabled = false");
|
||||
promise.get();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowWaitingForEnabledWhenDetached() {
|
||||
page.setContent("<button disabled>Target</button>");
|
||||
ElementHandle button = page.querySelector("button");
|
||||
Deferred<Void> promise = button.waitForElementState(ENABLED);
|
||||
button.evaluate("button => button.remove()");
|
||||
try {
|
||||
promise.get();
|
||||
fail("did not throw");
|
||||
} catch (RuntimeException e) {
|
||||
assertTrue(e.getMessage().contains("Element is not attached to the DOM"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWaitForDisabledButton() {
|
||||
page.setContent("<button><span>Target</span></button>");
|
||||
ElementHandle span = page.querySelector("text=Target");
|
||||
Deferred<Void> promise = span.waitForElementState(DISABLED);
|
||||
giveItAChanceToResolve(page);
|
||||
span.evaluate("span => span.parentElement.disabled = true");
|
||||
promise.get();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWaitForStablePosition() {
|
||||
// TODO: test.fixme(browserName === "firefox" && platform === "linux");
|
||||
page.navigate(server.PREFIX + "/input/button.html");
|
||||
ElementHandle button = page.querySelector("button");
|
||||
page.evalOnSelector("button", "button => {\n" +
|
||||
" button.style.transition = 'margin 10000ms linear 0s';\n" +
|
||||
" button.style.marginLeft = '20000px';\n" +
|
||||
"}");
|
||||
Deferred<Void> promise = button.waitForElementState(STABLE);
|
||||
giveItAChanceToResolve(page);
|
||||
button.evaluate("button => button.style.transition = ''");
|
||||
promise.get();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user