feat: roll driver, add dnd tests (#540)
This commit is contained in:
@@ -24,21 +24,8 @@ import java.util.*;
|
||||
* ElementHandle represents an in-page DOM element. ElementHandles can be created with the {@link Page#querySelector
|
||||
* Page.querySelector()} method.
|
||||
* <pre>{@code
|
||||
* import com.microsoft.playwright.*;
|
||||
*
|
||||
* public class Example {
|
||||
* public static void main(String[] args) {
|
||||
* try (Playwright playwright = Playwright.create()) {
|
||||
* BrowserType chromium = playwright.chromium();
|
||||
* Browser browser = chromium.launch();
|
||||
* Page page = browser.newPage();
|
||||
* page.navigate("https://example.com");
|
||||
* ElementHandle hrefElement = page.querySelector("a");
|
||||
* hrefElement.click();
|
||||
* // ...
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ElementHandle hrefElement = page.querySelector("a");
|
||||
* hrefElement.click();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> ElementHandle prevents DOM element from garbage collection unless the handle is disposed with {@link JSHandle#dispose
|
||||
@@ -46,6 +33,30 @@ import java.util.*;
|
||||
*
|
||||
* <p> ElementHandle instances can be used as an argument in {@link Page#evalOnSelector Page.evalOnSelector()} and {@link
|
||||
* Page#evaluate Page.evaluate()} methods.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> In most cases, you would want to use the {@code Locator} object instead. You should only use {@code ElementHandle} if you want to
|
||||
* retain a handle to a particular DOM Node that you intend to pass into {@link Page#evaluate Page.evaluate()} as an
|
||||
* argument.
|
||||
*
|
||||
* <p> The difference between the {@code Locator} and ElementHandle is that the ElementHandle points to a particular element, while
|
||||
* {@code Locator} captures the logic of how to retrieve an element.
|
||||
*
|
||||
* <p> In the example below, handle points to a particular DOM element on page. If that element changes text or is used by
|
||||
* React to render an entirely different component, handle is still pointing to that very DOM element. This can lead to
|
||||
* unexpected behaviors.
|
||||
* <pre>{@code
|
||||
* ElementHandle handle = page.querySelector("text=Submit");
|
||||
* handle.hover();
|
||||
* handle.click();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> With the locator, every time the {@code element} is used, up-to-date DOM element is located in the page using the selector. So
|
||||
* in the snippet below, underlying DOM element is going to be located twice.
|
||||
* <pre>{@code
|
||||
* Locator locator = page.locator("text=Submit");
|
||||
* locator.hover();
|
||||
* locator.click();
|
||||
* }</pre>
|
||||
*/
|
||||
public interface ElementHandle extends JSHandle {
|
||||
class CheckOptions {
|
||||
@@ -1120,13 +1131,13 @@ public interface ElementHandle extends JSHandle {
|
||||
*/
|
||||
String innerText();
|
||||
/**
|
||||
* Returns {@code input.value} for {@code <input>} or {@code <textarea>} element. Throws for non-input elements.
|
||||
* Returns {@code input.value} for {@code <input>} or {@code <textarea>} or {@code <select>} element. Throws for non-input elements.
|
||||
*/
|
||||
default String inputValue() {
|
||||
return inputValue(null);
|
||||
}
|
||||
/**
|
||||
* Returns {@code input.value} for {@code <input>} or {@code <textarea>} element. Throws for non-input elements.
|
||||
* Returns {@code input.value} for {@code <input>} or {@code <textarea>} or {@code <select>} element. Throws for non-input elements.
|
||||
*/
|
||||
String inputValue(InputValueOptions options);
|
||||
/**
|
||||
|
||||
@@ -406,11 +406,21 @@ public interface Frame {
|
||||
* inaccessible pages. Defaults to {@code false}.
|
||||
*/
|
||||
public Boolean noWaitAfter;
|
||||
/**
|
||||
* Clicks on the source element at this point relative to the top-left corner of the element's padding box. If not
|
||||
* specified, some visible point of the element is used.
|
||||
*/
|
||||
public Position sourcePosition;
|
||||
/**
|
||||
* When true, the call requires selector to resolve to a single element. If given selector resolves to more then one
|
||||
* element, the call throws an exception.
|
||||
*/
|
||||
public Boolean strict;
|
||||
/**
|
||||
* Drops on the target element at this point relative to the top-left corner of the element's padding box. If not
|
||||
* specified, some visible point of the element is used.
|
||||
*/
|
||||
public Position targetPosition;
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
@@ -432,10 +442,24 @@ public interface Frame {
|
||||
this.noWaitAfter = noWaitAfter;
|
||||
return this;
|
||||
}
|
||||
public DragAndDropOptions setSourcePosition(double x, double y) {
|
||||
return setSourcePosition(new Position(x, y));
|
||||
}
|
||||
public DragAndDropOptions setSourcePosition(Position sourcePosition) {
|
||||
this.sourcePosition = sourcePosition;
|
||||
return this;
|
||||
}
|
||||
public DragAndDropOptions setStrict(boolean strict) {
|
||||
this.strict = strict;
|
||||
return this;
|
||||
}
|
||||
public DragAndDropOptions setTargetPosition(double x, double y) {
|
||||
return setTargetPosition(new Position(x, y));
|
||||
}
|
||||
public DragAndDropOptions setTargetPosition(Position targetPosition) {
|
||||
this.targetPosition = targetPosition;
|
||||
return this;
|
||||
}
|
||||
public DragAndDropOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
@@ -2079,7 +2103,7 @@ public interface Frame {
|
||||
*/
|
||||
String innerText(String selector, InnerTextOptions options);
|
||||
/**
|
||||
* Returns {@code input.value} for the selected {@code <input>} or {@code <textarea>} element. Throws for non-input elements.
|
||||
* Returns {@code input.value} for the selected {@code <input>} or {@code <textarea>} or {@code <select>} element. Throws for non-input elements.
|
||||
*
|
||||
* @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. See
|
||||
* <a href="https://playwright.dev/java/docs/selectors/">working with selectors</a> for more details.
|
||||
@@ -2088,7 +2112,7 @@ public interface Frame {
|
||||
return inputValue(selector, null);
|
||||
}
|
||||
/**
|
||||
* Returns {@code input.value} for the selected {@code <input>} or {@code <textarea>} element. Throws for non-input elements.
|
||||
* Returns {@code input.value} for the selected {@code <input>} or {@code <textarea>} or {@code <select>} element. Throws for non-input elements.
|
||||
*
|
||||
* @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. See
|
||||
* <a href="https://playwright.dev/java/docs/selectors/">working with selectors</a> for more details.
|
||||
|
||||
@@ -23,24 +23,29 @@ import java.util.*;
|
||||
/**
|
||||
* Locator represents a view to the element(s) on the page. It captures the logic sufficient to retrieve the element at any
|
||||
* given moment. Locator can be created with the {@link Page#locator Page.locator()} method.
|
||||
* <pre>{@code
|
||||
* Locator locator = page.locator("text=Submit");
|
||||
* locator.click();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> The difference between the Locator and {@code ElementHandle} is that the latter points to a particular element, while Locator
|
||||
* only captures the logic of how to retrieve an element at any given moment.
|
||||
* captures the logic of how to retrieve that element.
|
||||
*
|
||||
* <p> In the example below, handle points to a particular DOM element on page. If that element changes text or is used by
|
||||
* React to render an entirely different component, handle is still pointing to that very DOM element.
|
||||
* React to render an entirely different component, handle is still pointing to that very DOM element. This can lead to
|
||||
* unexpected behaviors.
|
||||
* <pre>{@code
|
||||
* ElementHandle handle = page.querySelector("text=Submit");
|
||||
* handle.hover();
|
||||
* handle.click();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> With the locator, every time the {@code element} is used, corresponding DOM element is located in the page using given
|
||||
* selector. So in the snippet below, underlying DOM element is going to be located twice, using the given selector.
|
||||
* <p> With the locator, every time the {@code element} is used, up-to-date DOM element is located in the page using the selector. So
|
||||
* in the snippet below, underlying DOM element is going to be located twice.
|
||||
* <pre>{@code
|
||||
* Locator element = page.locator("text=Submit");
|
||||
* element.hover();
|
||||
* element.click();
|
||||
* Locator locator = page.locator("text=Submit");
|
||||
* locator.hover();
|
||||
* locator.click();
|
||||
* }</pre>
|
||||
*/
|
||||
public interface Locator {
|
||||
@@ -887,6 +892,14 @@ public interface Locator {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns an array of {@code node.innerText} values for all matching nodes.
|
||||
*/
|
||||
List<String> allInnerTexts();
|
||||
/**
|
||||
* Returns an array of {@code node.textContent} values for all matching nodes.
|
||||
*/
|
||||
List<String> allTextContents();
|
||||
/**
|
||||
* This method returns the bounding box of the element, or {@code null} if the element is not visible. The bounding box is
|
||||
* calculated relative to the main frame viewport - which is usually the same as the browser window.
|
||||
@@ -1446,13 +1459,13 @@ public interface Locator {
|
||||
*/
|
||||
String innerText(InnerTextOptions options);
|
||||
/**
|
||||
* Returns {@code input.value} for {@code <input>} or {@code <textarea>} element. Throws for non-input elements.
|
||||
* Returns {@code input.value} for {@code <input>} or {@code <textarea>} or {@code <select>} element. Throws for non-input elements.
|
||||
*/
|
||||
default String inputValue() {
|
||||
return inputValue(null);
|
||||
}
|
||||
/**
|
||||
* Returns {@code input.value} for {@code <input>} or {@code <textarea>} element. Throws for non-input elements.
|
||||
* Returns {@code input.value} for {@code <input>} or {@code <textarea>} or {@code <select>} element. Throws for non-input elements.
|
||||
*/
|
||||
String inputValue(InputValueOptions options);
|
||||
/**
|
||||
|
||||
@@ -664,11 +664,21 @@ public interface Page extends AutoCloseable {
|
||||
* inaccessible pages. Defaults to {@code false}.
|
||||
*/
|
||||
public Boolean noWaitAfter;
|
||||
/**
|
||||
* Clicks on the source element at this point relative to the top-left corner of the element's padding box. If not
|
||||
* specified, some visible point of the element is used.
|
||||
*/
|
||||
public Position sourcePosition;
|
||||
/**
|
||||
* When true, the call requires selector to resolve to a single element. If given selector resolves to more then one
|
||||
* element, the call throws an exception.
|
||||
*/
|
||||
public Boolean strict;
|
||||
/**
|
||||
* Drops on the target element at this point relative to the top-left corner of the element's padding box. If not
|
||||
* specified, some visible point of the element is used.
|
||||
*/
|
||||
public Position targetPosition;
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
@@ -690,10 +700,24 @@ public interface Page extends AutoCloseable {
|
||||
this.noWaitAfter = noWaitAfter;
|
||||
return this;
|
||||
}
|
||||
public DragAndDropOptions setSourcePosition(double x, double y) {
|
||||
return setSourcePosition(new Position(x, y));
|
||||
}
|
||||
public DragAndDropOptions setSourcePosition(Position sourcePosition) {
|
||||
this.sourcePosition = sourcePosition;
|
||||
return this;
|
||||
}
|
||||
public DragAndDropOptions setStrict(boolean strict) {
|
||||
this.strict = strict;
|
||||
return this;
|
||||
}
|
||||
public DragAndDropOptions setTargetPosition(double x, double y) {
|
||||
return setTargetPosition(new Position(x, y));
|
||||
}
|
||||
public DragAndDropOptions setTargetPosition(Position targetPosition) {
|
||||
this.targetPosition = targetPosition;
|
||||
return this;
|
||||
}
|
||||
public DragAndDropOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
@@ -3225,7 +3249,7 @@ public interface Page extends AutoCloseable {
|
||||
*/
|
||||
String innerText(String selector, InnerTextOptions options);
|
||||
/**
|
||||
* Returns {@code input.value} for the selected {@code <input>} or {@code <textarea>} element. Throws for non-input elements.
|
||||
* Returns {@code input.value} for the selected {@code <input>} or {@code <textarea>} or {@code <select>} element. Throws for non-input elements.
|
||||
*
|
||||
* @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. See
|
||||
* <a href="https://playwright.dev/java/docs/selectors/">working with selectors</a> for more details.
|
||||
@@ -3234,7 +3258,7 @@ public interface Page extends AutoCloseable {
|
||||
return inputValue(selector, null);
|
||||
}
|
||||
/**
|
||||
* Returns {@code input.value} for the selected {@code <input>} or {@code <textarea>} element. Throws for non-input elements.
|
||||
* Returns {@code input.value} for the selected {@code <input>} or {@code <textarea>} or {@code <select>} element. Throws for non-input elements.
|
||||
*
|
||||
* @param selector A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. See
|
||||
* <a href="https://playwright.dev/java/docs/selectors/">working with selectors</a> for more details.
|
||||
|
||||
@@ -561,7 +561,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
@Override
|
||||
public Locator locator(String selector) {
|
||||
return new LoccatorImpl(this, selector);
|
||||
return new LocatorImpl(this, selector);
|
||||
}
|
||||
|
||||
boolean isVisibleImpl(String selector, IsVisibleOptions options) {
|
||||
|
||||
+17
-7
@@ -15,12 +15,12 @@ import java.util.function.BiFunction;
|
||||
|
||||
import static com.microsoft.playwright.impl.Utils.convertViaJson;
|
||||
|
||||
class LoccatorImpl implements Locator {
|
||||
class LocatorImpl implements Locator {
|
||||
private final FrameImpl frame;
|
||||
private final String selector;
|
||||
private final String visibleSelector;
|
||||
|
||||
public LoccatorImpl(FrameImpl frame, String selector) {
|
||||
public LocatorImpl(FrameImpl frame, String selector) {
|
||||
this.frame = frame;
|
||||
this.selector = selector;
|
||||
this.visibleSelector = selector + " >> _visible=true";
|
||||
@@ -45,6 +45,16 @@ class LoccatorImpl implements Locator {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> allInnerTexts() {
|
||||
return (List<String>) frame.evalOnSelectorAll(selector, "ee => ee.map(e => e.innerText)");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> allTextContents() {
|
||||
return (List<String>) frame.evalOnSelectorAll(selector, "ee => ee.map(e => e.textContent || '')");
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoundingBox boundingBox(BoundingBoxOptions options) {
|
||||
return withElement((h, o) -> h.boundingBox(), options);
|
||||
@@ -128,7 +138,7 @@ class LoccatorImpl implements Locator {
|
||||
|
||||
@Override
|
||||
public Locator first() {
|
||||
return new LoccatorImpl(frame, selector + " >> _nth=first");
|
||||
return new LocatorImpl(frame, selector + " >> _nth=first");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -229,17 +239,17 @@ class LoccatorImpl implements Locator {
|
||||
|
||||
@Override
|
||||
public Locator last() {
|
||||
return new LoccatorImpl(frame, selector + " >> _nth=last");
|
||||
return new LocatorImpl(frame, selector + " >> _nth=last");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator locator(String selector) {
|
||||
return new LoccatorImpl(frame, this.selector + " >> " + selector);
|
||||
return new LocatorImpl(frame, this.selector + " >> " + selector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locator nth(int index) {
|
||||
return new LoccatorImpl(frame, selector + " >> _nth=" + index);
|
||||
return new LocatorImpl(frame, selector + " >> _nth=" + index);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -385,6 +395,6 @@ class LoccatorImpl implements Locator {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Loccator@" + selector;
|
||||
return "Locator@" + selector;
|
||||
}
|
||||
}
|
||||
@@ -59,10 +59,10 @@ class TracingImpl implements Tracing {
|
||||
@Override
|
||||
public void stop(StopOptions options) {
|
||||
context.withLogging("Tracing.stop", () -> {
|
||||
context.sendMessage("tracingStop");
|
||||
if (options != null && options.path != null) {
|
||||
export(options.path);
|
||||
}
|
||||
context.sendMessage("tracingStop");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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 java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestLocatorConvenience extends TestBase {
|
||||
@Test
|
||||
void shouldHaveANicePreview() {
|
||||
page.navigate(server.PREFIX + "/dom.html");
|
||||
Locator outer = page.locator("#outer");
|
||||
Locator inner = outer.locator("#inner");
|
||||
Locator check = page.locator("#check");
|
||||
JSHandle text = inner.evaluateHandle("e => e.firstChild");
|
||||
page.evaluate("() => 1"); // Give them a chance to calculate the preview.
|
||||
assertEquals("Locator@#outer", outer.toString());
|
||||
assertEquals("Locator@#outer >> #inner", inner.toString());
|
||||
assertEquals("JSHandle@#text=Text,↵more text", text.toString());
|
||||
assertEquals("Locator@#check", check.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAttributeShouldWork() {
|
||||
page.navigate(server.PREFIX + "/dom.html");
|
||||
Locator locator = page.locator("#outer");
|
||||
assertEquals("value", locator.getAttribute("name"));
|
||||
assertNull(locator.getAttribute("foo"));
|
||||
assertEquals("value", page.getAttribute("#outer", "name"));
|
||||
assertNull(page.getAttribute("#outer", "foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void inputValueShouldWork() {
|
||||
page.navigate(server.PREFIX + "/dom.html");
|
||||
|
||||
page.selectOption("#select", "foo");
|
||||
assertEquals("foo", page.inputValue("#select"));
|
||||
|
||||
page.fill("#textarea", "text value");
|
||||
assertEquals("text value", page.inputValue("#textarea"));
|
||||
|
||||
page.fill("#input", "input value");
|
||||
assertEquals("input value", page.inputValue("#input"));
|
||||
Locator locator = page.locator("#input");
|
||||
assertEquals("input value", locator.inputValue());
|
||||
|
||||
try {
|
||||
page.inputValue("#inner");
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Node is not an HTMLInputElement or HTMLTextAreaElement or HTMLSelectElement"));
|
||||
}
|
||||
try {
|
||||
Locator locator2 = page.locator("#inner");
|
||||
locator2.inputValue();
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Node is not an HTMLInputElement or HTMLTextAreaElement or HTMLSelectElement"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void innerHTMLShouldWork() {
|
||||
page.navigate(server.PREFIX + "/dom.html");
|
||||
Locator locator = page.locator("#outer");
|
||||
assertEquals("<div id=\"inner\">Text,\nmore text</div>", locator.innerHTML());
|
||||
assertEquals("<div id=\"inner\">Text,\nmore text</div>", page.innerHTML("#outer"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void innerTextShouldWork() {
|
||||
page.navigate(server.PREFIX + "/dom.html");
|
||||
Locator locator = page.locator("#inner");
|
||||
assertEquals("Text, more text", locator.innerText());
|
||||
assertEquals("Text, more text", page.innerText("#inner"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void innerTextShouldThrow() {
|
||||
page.setContent("<svg>text</svg>");
|
||||
try {
|
||||
page.innerText("svg");
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Not an HTMLElement"));
|
||||
}
|
||||
Locator locator = page.locator("svg");
|
||||
try {
|
||||
locator.innerText();
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Not an HTMLElement"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void textContentShouldWork() {
|
||||
page.navigate(server.PREFIX + "/dom.html");
|
||||
Locator locator = page.locator("#inner");
|
||||
assertEquals("Text,\nmore text", locator.textContent());
|
||||
assertEquals("Text,\nmore text", page.textContent("#inner"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isVisibleAndIsHiddenShouldWork() {
|
||||
page.setContent("<div>Hi</div><span></span>");
|
||||
|
||||
Locator div = page.locator("div");
|
||||
assertTrue(div.isVisible());
|
||||
assertFalse(div.isHidden());
|
||||
assertTrue(page.isVisible("div"));
|
||||
assertFalse(page.isHidden("div"));
|
||||
|
||||
Locator span = page.locator("span");
|
||||
assertFalse(span.isVisible());
|
||||
assertTrue(span.isHidden());
|
||||
assertFalse(page.isVisible("span"));
|
||||
assertTrue(page.isHidden("span"));
|
||||
|
||||
assertFalse(page.isVisible("no-such-element"));
|
||||
assertTrue(page.isHidden("no-such-element"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEnabledAndIsDisabledShouldWork() {
|
||||
page.setContent("<button disabled>button1</button>\n" +
|
||||
"<button>button2</button>\n" +
|
||||
"<div>div</div>");
|
||||
Locator div = page.locator("div");
|
||||
assertTrue(div.isEnabled());
|
||||
assertFalse(div.isDisabled());
|
||||
assertTrue(page.isEnabled("div"));
|
||||
assertEquals(false, page.isDisabled("div"));
|
||||
Locator button1 = page.locator(":text('button1')");
|
||||
assertEquals(false, button1.isEnabled());
|
||||
assertTrue(button1.isDisabled());
|
||||
assertEquals(false, page.isEnabled(":text('button1')"));
|
||||
assertTrue(page.isDisabled(":text('button1')"));
|
||||
Locator button2 = page.locator(":text('button2')");
|
||||
assertTrue(button2.isEnabled());
|
||||
assertEquals(false, button2.isDisabled());
|
||||
assertTrue(page.isEnabled(":text('button2')"));
|
||||
assertEquals(false, page.isDisabled(":text('button2')"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isEditableShouldWork() {
|
||||
page.setContent("<input id=input1 disabled><textarea></textarea><input id=input2>");
|
||||
page.evalOnSelector("textarea", "t => t.readOnly = true");
|
||||
Locator input1 = page.locator("#input1");
|
||||
assertFalse(input1.isEditable());
|
||||
assertFalse(page.isEditable("#input1"));
|
||||
Locator input2 = page.locator("#input2");
|
||||
assertTrue(input2.isEditable());
|
||||
assertTrue(page.isEditable("#input2"));
|
||||
Locator textarea = page.locator("textarea");
|
||||
assertFalse(textarea.isEditable());
|
||||
assertFalse(page.isEditable("textarea"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isCheckedShouldWork() {
|
||||
page.setContent("<input type='checkbox' checked><div>Not a checkbox</div>");
|
||||
Locator element = page.locator("input");
|
||||
assertTrue(element.isChecked());
|
||||
assertTrue(page.isChecked("input"));
|
||||
element.evaluate("input => input.checked = false");
|
||||
assertFalse(element.isChecked());
|
||||
assertFalse(page.isChecked("input"));
|
||||
try {
|
||||
page.isChecked("div");
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Not a checkbox or radio button"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void allTextContentsShouldWork() {
|
||||
page.setContent("<div>A</div><div>B</div><div>C</div>");
|
||||
assertEquals(asList("A", "B", "C"), page.locator("div").allTextContents());
|
||||
}
|
||||
|
||||
@Test
|
||||
void allInnerTextsShouldWork() {
|
||||
page.setContent("<div>A</div><div>B</div><div>C</div>");
|
||||
assertEquals(asList("A", "B", "C"), page.locator("div").allInnerTexts());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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 com.google.gson.Gson;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static com.microsoft.playwright.Utils.assertJsonEquals;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class TestPageDrag extends TestBase {
|
||||
@Test
|
||||
void shouldWorkIfTheDragIsCanceled() {
|
||||
page.navigate(server.PREFIX + "/drag-n-drop.html");
|
||||
page.evaluate("() => {\n" +
|
||||
" document.body.addEventListener('dragstart', event => {\n" +
|
||||
" event.preventDefault();\n" +
|
||||
" }, false);\n" +
|
||||
" }");
|
||||
page.hover("#source");
|
||||
page.mouse().down();
|
||||
page.hover("#target");
|
||||
page.mouse().up();
|
||||
assertEquals(false, page.evalOnSelector("#target", "target => target.contains(document.querySelector('#source'))"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkIfTheDragEventIsCapturedButNotCanceled() {
|
||||
page.navigate(server.PREFIX + "/drag-n-drop.html");
|
||||
page.evaluate("() => {\n" +
|
||||
" document.body.addEventListener('dragstart', event => {\n" +
|
||||
" event.stopImmediatePropagation();\n" +
|
||||
" }, false);\n" +
|
||||
" }");
|
||||
page.hover("#source");
|
||||
page.mouse().down();
|
||||
page.hover("#target");
|
||||
page.mouse().up();
|
||||
assertEquals(true, page.evalOnSelector("#target", "target => target.contains(document.querySelector('#source'))"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBeAbleToDragTheMouseInAFrame() {
|
||||
page.navigate(server.PREFIX + "/frames/one-frame.html");
|
||||
JSHandle eventsHandle = trackEvents(page.frames().get(1).querySelector("html"));
|
||||
page.mouse().move(30, 30);
|
||||
page.mouse().down();
|
||||
page.mouse().move(60, 60);
|
||||
page.mouse().up();
|
||||
assertEquals(asList("mousemove", "mousedown", "mousemove", "mouseup"), eventsHandle.jsonValue());
|
||||
}
|
||||
|
||||
private static JSHandle trackEvents(ElementHandle target) {
|
||||
return target.evaluateHandle("target => {\n" +
|
||||
" const events = [];\n" +
|
||||
" for (const event of [\n" +
|
||||
" 'mousedown', 'mousemove', 'mouseup',\n" +
|
||||
" 'dragstart', 'dragend', 'dragover', 'dragenter', 'dragleave', 'dragexit',\n" +
|
||||
" 'drop'\n" +
|
||||
" ])\n" +
|
||||
" target.addEventListener(event, () => events.push(event), false);\n" +
|
||||
" return events;\n" +
|
||||
"}");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithTheHelperMethod() {
|
||||
page.navigate(server.PREFIX + "/drag-n-drop.html");
|
||||
page.dragAndDrop("#source", "#target");
|
||||
assertEquals(true, page.evalOnSelector("#target",
|
||||
"target => target.contains(document.querySelector('#source'))")); // could not find source in target
|
||||
}
|
||||
@Test
|
||||
void shouldAllowSpecifyingThePosition() {
|
||||
page.setContent("<div style='width:100px;height:100px;background:red;' id='red'>\n" +
|
||||
"</div>\n" +
|
||||
"<div style='width:100px;height:100px;background:blue;' id='blue'>\n" +
|
||||
"</div>");
|
||||
JSHandle eventsHandle = page.evaluateHandle("() => {\n" +
|
||||
" const events = [];\n" +
|
||||
" document.getElementById('red').addEventListener('mousedown', event => {\n" +
|
||||
" events.push({\n" +
|
||||
" type: 'mousedown',\n" +
|
||||
" x: event.offsetX,\n" +
|
||||
" y: event.offsetY,\n" +
|
||||
" });\n" +
|
||||
" });\n" +
|
||||
" document.getElementById('blue').addEventListener('mouseup', event => {\n" +
|
||||
" events.push({\n" +
|
||||
" type: 'mouseup',\n" +
|
||||
" x: event.offsetX,\n" +
|
||||
" y: event.offsetY,\n" +
|
||||
" });\n" +
|
||||
" });\n" +
|
||||
" return events;\n" +
|
||||
" }");
|
||||
page.dragAndDrop("#red", "#blue", new Page.DragAndDropOptions()
|
||||
.setSourcePosition(34, 7).setTargetPosition(10, 20));
|
||||
Object json = eventsHandle.jsonValue();
|
||||
assertJsonEquals("[{type: \"mousedown\", x: 34, y: 7},{type: \"mouseup\", x: 10, y: 20}]", json);
|
||||
}
|
||||
}
|
||||
@@ -66,7 +66,11 @@ public class TestWorkers extends TestBase {
|
||||
void shouldHaveJSHandlesForConsoleLogs() {
|
||||
ConsoleMessage log = page.waitForConsoleMessage(() -> page.evaluate(
|
||||
"() => new Worker(URL.createObjectURL(new Blob(['console.log(1,2,3,this)'], {type: 'application/javascript'})))"));
|
||||
assertEquals("1 2 3 JSHandle@object", log.text());
|
||||
if (isFirefox()) {
|
||||
assertEquals("1 2 3 JSHandle@object", log.text());
|
||||
} else {
|
||||
assertEquals("1 2 3 DedicatedWorkerGlobalScope", log.text());
|
||||
}
|
||||
assertEquals(4, log.args().size());
|
||||
assertEquals("null", (log.args().get(3).getProperty("origin")).jsonValue());
|
||||
}
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
more text</div></div><input id="check" type=checkbox checked foo="bar"">
|
||||
<input id="input"></input>
|
||||
<textarea id="textarea"></textarea>
|
||||
<select id="select"><option></option><option value="foo"></option></select>
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
1.14.0-next-1627483012000
|
||||
1.14.0-next-1628276501000
|
||||
|
||||
@@ -259,6 +259,11 @@ class TypeRef extends Element {
|
||||
customTypeNames.put("FileChooser.setFiles.files", "FilePayload");
|
||||
customTypeNames.put("Frame.setInputFiles.files", "FilePayload");
|
||||
customTypeNames.put("Page.setInputFiles.files", "FilePayload");
|
||||
|
||||
customTypeNames.put("Page.dragAndDrop.options.sourcePosition", "Position");
|
||||
customTypeNames.put("Frame.dragAndDrop.options.sourcePosition", "Position");
|
||||
customTypeNames.put("Page.dragAndDrop.options.targetPosition", "Position");
|
||||
customTypeNames.put("Frame.dragAndDrop.options.targetPosition", "Position");
|
||||
}
|
||||
|
||||
TypeRef(Element parent, JsonElement jsonElement) {
|
||||
|
||||
Reference in New Issue
Block a user