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("Page.waitForTimeout", "Promise", "Deferred<Void>", new Empty());
|
||||||
add("Frame.waitForFunction", "Promise<JSHandle>", "Deferred<JSHandle>", new Empty());
|
add("Frame.waitForFunction", "Promise<JSHandle>", "Deferred<JSHandle>", new Empty());
|
||||||
add("Page.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
|
// Custom options
|
||||||
add("Page.pdf.options.margin.top", "string|number", "String");
|
add("Page.pdf.options.margin.top", "string|number", "String");
|
||||||
|
@ -434,10 +434,10 @@ public interface ElementHandle extends JSHandle {
|
|||||||
uncheck(null);
|
uncheck(null);
|
||||||
}
|
}
|
||||||
void uncheck(UncheckOptions options);
|
void uncheck(UncheckOptions options);
|
||||||
default void waitForElementState(ElementState state) {
|
default Deferred<Void> waitForElementState(ElementState state) {
|
||||||
waitForElementState(state, null);
|
return waitForElementState(state, null);
|
||||||
}
|
}
|
||||||
void waitForElementState(ElementState state, WaitForElementStateOptions options);
|
Deferred<Void> waitForElementState(ElementState state, WaitForElementStateOptions options);
|
||||||
default Deferred<ElementHandle> waitForSelector(String selector) {
|
default Deferred<ElementHandle> waitForSelector(String selector) {
|
||||||
return waitForSelector(selector, null);
|
return waitForSelector(selector, null);
|
||||||
}
|
}
|
||||||
|
@ -20,17 +20,16 @@ import com.google.gson.Gson;
|
|||||||
import com.google.gson.JsonArray;
|
import com.google.gson.JsonArray;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.microsoft.playwright.Deferred;
|
import com.microsoft.playwright.*;
|
||||||
import com.microsoft.playwright.ElementHandle;
|
|
||||||
import com.microsoft.playwright.Frame;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
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 {
|
class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||||
public ElementHandleImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
ElementHandleImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||||
super(parent, type, guid, initializer);
|
super(parent, type, guid, initializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,12 +68,26 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object evalOnSelector(String selector, String pageFunction, Object arg) {
|
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
|
@Override
|
||||||
public Object evalOnSelectorAll(String selector, String pageFunction, Object arg) {
|
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
|
@Override
|
||||||
@ -99,12 +112,12 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
|||||||
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
|
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
|
||||||
params.remove("button");
|
params.remove("button");
|
||||||
if (options.button != null) {
|
if (options.button != null) {
|
||||||
params.addProperty("button", toProtocol(options.button));
|
params.addProperty("button", Serialization.toProtocol(options.button));
|
||||||
}
|
}
|
||||||
|
|
||||||
params.remove("modifiers");
|
params.remove("modifiers");
|
||||||
if (options.modifiers != null) {
|
if (options.modifiers != null) {
|
||||||
params.add("modifiers", toProtocol(options.modifiers));
|
params.add("modifiers", Serialization.toProtocol(options.modifiers));
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessage("click", params);
|
sendMessage("click", params);
|
||||||
@ -124,12 +137,12 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
|||||||
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
|
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
|
||||||
params.remove("button");
|
params.remove("button");
|
||||||
if (options.button != null) {
|
if (options.button != null) {
|
||||||
params.addProperty("button", toProtocol(options.button));
|
params.addProperty("button", Serialization.toProtocol(options.button));
|
||||||
}
|
}
|
||||||
|
|
||||||
params.remove("modifiers");
|
params.remove("modifiers");
|
||||||
if (options.modifiers != null) {
|
if (options.modifiers != null) {
|
||||||
params.add("modifiers", toProtocol(options.modifiers));
|
params.add("modifiers", Serialization.toProtocol(options.modifiers));
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessage("dblclick", params);
|
sendMessage("dblclick", params);
|
||||||
@ -137,47 +150,71 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispatchEvent(String type, Object eventInit) {
|
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
|
@Override
|
||||||
public void fill(String value, FillOptions options) {
|
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
|
@Override
|
||||||
public void focus() {
|
public void focus() {
|
||||||
|
sendMessage("focus", new JsonObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getAttribute(String name) {
|
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
|
@Override
|
||||||
public void hover(HoverOptions options) {
|
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
|
@Override
|
||||||
public String innerHTML() {
|
public String innerHTML() {
|
||||||
return null;
|
JsonObject json = sendMessage("innerHTML", new JsonObject()).getAsJsonObject();
|
||||||
|
return json.get("value").getAsString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String innerText() {
|
public String innerText() {
|
||||||
return null;
|
JsonObject json = sendMessage("innerText", new JsonObject()).getAsJsonObject();
|
||||||
|
return json.get("value").getAsString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Frame ownerFrame() {
|
public Frame ownerFrame() {
|
||||||
return null;
|
JsonObject json = sendMessage("ownerFrame", new JsonObject()).getAsJsonObject();
|
||||||
|
return connection.getExistingObject(json.getAsJsonObject("frame").get("guid").getAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void press(String key, PressOptions options) {
|
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
|
@Override
|
||||||
@ -187,17 +224,26 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void scrollIntoViewIfNeeded(ScrollIntoViewIfNeededOptions options) {
|
public void scrollIntoViewIfNeeded(ScrollIntoViewIfNeededOptions options) {
|
||||||
|
if (options == null) {
|
||||||
|
options = new ScrollIntoViewIfNeededOptions();
|
||||||
|
}
|
||||||
|
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
|
||||||
|
sendMessage("scrollIntoViewIfNeeded", params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> selectOption(String values, SelectOptionOptions options) {
|
public List<String> selectOption(String values, SelectOptionOptions options) {
|
||||||
|
// TODO:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void selectText(SelectTextOptions options) {
|
public void selectText(SelectTextOptions options) {
|
||||||
|
if (options == null) {
|
||||||
|
options = new SelectTextOptions();
|
||||||
|
}
|
||||||
|
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
|
||||||
|
sendMessage("selectText", params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -207,12 +253,18 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String textContent() {
|
public String textContent() {
|
||||||
return null;
|
JsonObject json = sendMessage("textContent", new JsonObject()).getAsJsonObject();
|
||||||
|
return json.has("value") ? json.get("value").getAsString() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void type(String text, TypeOptions options) {
|
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
|
@Override
|
||||||
@ -225,12 +277,38 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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
|
@Override
|
||||||
public Deferred<ElementHandle> waitForSelector(String selector, WaitForSelectorOptions options) {
|
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