1
0
mirror of synced 2026-09-11 00:49:39 +00:00
Files
playwright-java/playwright/src/test/java/com/microsoft/playwright/TestClick.java
T
2021-02-11 12:53:18 -08:00

619 lines
22 KiB
Java

/*
* 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 org.junit.jupiter.api.condition.DisabledIf;
import org.junit.jupiter.api.condition.EnabledIf;
import java.util.ArrayList;
import java.util.List;
import static com.microsoft.playwright.options.KeyboardModifier.SHIFT;
import static com.microsoft.playwright.options.MouseButton.RIGHT;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static org.junit.jupiter.api.Assertions.*;
public class TestClick extends TestBase {
@Test
void shouldClickTheButton() {
page.navigate(server.PREFIX + "/input/button.html");
page.click("button");
assertEquals("Clicked", page.evaluate("result"));
}
@Test
void shouldClickSvg() {
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>\n");
page.click("circle");
assertEquals(42, page.evaluate("__CLICKED"));
}
@Test
void shouldClickTheButtonIfWindowNodeIsRemoved() {
page.navigate(server.PREFIX + "/input/button.html");
page.evaluate("() => delete window.Node");
page.click("button");
assertEquals("Clicked", page.evaluate("result"));
}
// @see https://github.com/GoogleChrome/puppeteer/issues/4281
@Test
void shouldClickOnASpanWithAnInlineElementInside() {
page.setContent(
"<style>\n" +
" span::before {\n" +
" content: 'q';\n" +
" }\n" +
"</style>\n" +
"<span onclick='javascript:window.CLICKED=42'></span>\n");
page.click("span");
assertEquals(42, page.evaluate("CLICKED"));
}
void shouldNotThrowUnhandledPromiseRejectionWhenPageCloses() {
// not supported in sync api
}
@Test
void shouldClickThe1x1Div() {
page.setContent("<div style='width: 1px; height: 1px;' onclick='window.__clicked = true'></div>");
page.click("div");
assertTrue((Boolean) page.evaluate("window.__clicked"));
}
@Test
void shouldClickTheButtonAfterNavigation() {
page.navigate(server.PREFIX + "/input/button.html");
page.click("button");
page.navigate(server.PREFIX + "/input/button.html");
page.click("button");
assertEquals("Clicked", page.evaluate("result"));
}
@Test
void shouldClickTheButtonAfterACrossOriginNavigation() {
page.navigate(server.PREFIX + "/input/button.html");
page.click("button");
page.navigate(server.CROSS_PROCESS_PREFIX + "/input/button.html");
page.click("button");
assertEquals("Clicked", page.evaluate("result"));
}
@Test
void shouldClickWithDisabledJavascript() {
BrowserContext context = browser.newContext(new Browser.NewContextOptions().withJavaScriptEnabled(false));
Page page = context.newPage();
page.navigate(server.PREFIX + "/wrappedlink.html");
page.waitForNavigation(() -> page.click("a"));
assertEquals(server.PREFIX + "/wrappedlink.html#clicked", page.url());
context.close();
}
@Test
void shouldClickWhenOneOfInlineBoxChildrenIsOutsideOfViewport() {
page.setContent(
"<style>\n" +
"i {\n" +
" position: absolute;\n" +
" top: -1000px;\n" +
"}\n" +
"</style>\n" +
"<span onclick='javascript:window.CLICKED = 42;'><i>woof</i><b>doggo</b></span>\n");
page.click("span");
assertEquals(42, page.evaluate("CLICKED"));
}
@Test
void shouldSelectTheTextByTripleClicking() {
page.navigate(server.PREFIX + "/input/textarea.html");
String text = "This is the text that we are going to try to select. Let's see how it goes.";
page.fill("textarea", text);
page.click("textarea", new Page.ClickOptions().withClickCount(3));
assertEquals(text, page.evaluate("() => {\n" +
" const textarea = document.querySelector('textarea');\n" +
" return textarea.value.substring(textarea.selectionStart, textarea.selectionEnd);\n" +
"}"));
}
@Test
void shouldClickOffscreenButtons() {
page.navigate(server.PREFIX + "/offscreenbuttons.html");
List<String> messages = new ArrayList<>();
page.onConsole(message -> messages.add(message.text()));
for (int i = 0; i < 11; ++i) {
// We might've scrolled to click a button - reset to (0, 0).
page.evaluate("() => window.scrollTo(0, 0)");
page.click("#btn" + i);
}
assertEquals(asList(
"button #0 clicked",
"button #1 clicked",
"button #2 clicked",
"button #3 clicked",
"button #4 clicked",
"button #5 clicked",
"button #6 clicked",
"button #7 clicked",
"button #8 clicked",
"button #9 clicked",
"button #10 clicked"
), messages);
}
@Test
void shouldWaitForVisibleWhenAlreadyVisible() {
page.navigate(server.PREFIX + "/input/button.html");
page.click("button");
assertEquals("Clicked", page.evaluate("result"));
}
@Test
void shouldNotWaitWithForce() {
page.navigate(server.PREFIX + "/input/button.html");
page.evalOnSelector("button", "b => b.style.display = 'none'");
Exception exception = null;
try {
page.click("button", new Page.ClickOptions().withForce(true));
} catch (PlaywrightException e) {
exception = e;
}
assertNotNull(exception);
assertTrue(exception.getMessage().contains("Element is not visible"));
assertEquals("Was not clicked", page.evaluate("result"));
}
void shouldWaitForDisplayNoneToBeGone() {
// not supported in sync api
}
void shouldWaitForVisibilityHiddenToBeGone() {
// not supported in sync api
}
void shouldWaitForVisibleWhenParentIsHidden() {
// not supported in sync api
}
@Test
void shouldClickWrappedLinks() {
page.navigate(server.PREFIX + "/wrappedlink.html");
page.click("a");
assertTrue((Boolean) page.evaluate("__clicked"));
}
@Test
void shouldClickOnCheckboxInputAndToggle() {
page.navigate(server.PREFIX + "/input/checkbox.html");
assertNull(page.evaluate("() => window['result'].check"));
page.click("input#agree");
assertTrue((Boolean) page.evaluate("() => window['result'].check"));
assertEquals(asList(
"mouseover",
"mouseenter",
"mousemove",
"mousedown",
"mouseup",
"click",
"input",
"change"),
page.evaluate("() => window['result'].events"));
page.click("input#agree");
assertFalse((Boolean) page.evaluate("() => window['result'].check"));
}
@Test
void shouldClickOnCheckboxLabelAndToggle() {
page.navigate(server.PREFIX + "/input/checkbox.html");
assertNull(page.evaluate("() => window['result'].check"));
page.click("label[for='agree']");
assertTrue((Boolean) page.evaluate("() => window['result'].check"));
assertEquals(asList(
"click",
"input",
"change"),
page.evaluate("() => window['result'].events"));
page.click("label[for='agree']");
assertFalse((Boolean) page.evaluate("() => window['result'].check"));
}
@Test
void shouldNotHangWithTouchEnabledViewports() {
// @see https://github.com/GoogleChrome/puppeteer/issues/161
BrowserContext context = browser.newContext(new Browser.NewContextOptions()
.withViewportSize(375, 667)
.withHasTouch(true));
Page page = context.newPage();
page.mouse().down();
page.mouse().move(100, 10);
page.mouse().up();
context.close();
}
@Test
void shouldScrollAndClickTheButton() {
page.navigate(server.PREFIX + "/input/scrollable.html");
page.click("#button-5");
assertEquals("clicked", page.evaluate("() => document.querySelector('#button-5').textContent"));
page.click("#button-80");
assertEquals("clicked", page.evaluate("() => document.querySelector('#button-80').textContent"));
}
@Test
void shouldDoubleClickTheButton() {
page.navigate(server.PREFIX + "/input/button.html");
page.evaluate("() => {\n" +
" window['double'] = false;\n" +
" const button = document.querySelector('button');\n" +
" button.addEventListener('dblclick', event => {\n" +
" window['double'] = true;\n" +
" });\n" +
"}");
page.dblclick("button");
assertEquals(true, page.evaluate("double"));
assertEquals("Clicked", page.evaluate("result"));
}
@Test
void shouldClickAPartiallyObscuredButton() {
page.navigate(server.PREFIX + "/input/button.html");
page.evaluate("() => {\n" +
" const button = document.querySelector('button');\n" +
" button.textContent = 'Some really long text that will go offscreen';\n" +
" button.style.position = 'absolute';\n" +
" button.style.left = '368px';\n" +
"}");
page.click("button");
assertEquals("Clicked", page.evaluate("() => window['result']"));
}
@Test
void shouldClickARotatedButton() {
page.navigate(server.PREFIX + "/input/rotatedButton.html");
page.click("button");
assertEquals("Clicked", page.evaluate("result"));
}
@Test
void shouldFireContextmenuEventOnRightClick() {
page.navigate(server.PREFIX + "/input/scrollable.html");
page.click("#button-8", new Page.ClickOptions().withButton(RIGHT));
assertEquals("context menu", page.evaluate("() => document.querySelector('#button-8').textContent"));
}
@Test
void shouldClickLinksWhichCauseNavigation() {
// @see https://github.com/GoogleChrome/puppeteer/issues/206
page.setContent("<a href=" + server.EMPTY_PAGE + ">empty.html</a>");
// This should not hang.
page.click("a");
}
@Test
void shouldClickTheButtonInsideAnIframe() {
page.navigate(server.EMPTY_PAGE);
page.setContent("<div style='width:100px;height:100px'>spacer</div>");
Utils.attachFrame(page, "button-test", server.PREFIX + "/input/button.html");
Frame frame = page.frames().get(1);
ElementHandle button = frame.querySelector("button");
button.click();
assertEquals("Clicked", frame.evaluate("() => window['result']"));
}
@Test
@EnabledIf(value="com.microsoft.playwright.TestBase#isFirefox", disabledReason="fixme")
void shouldClickTheButtonWithFixedPositionInsideAnIframe() {
// @see https://github.com/GoogleChrome/puppeteer/issues/4110
// @see https://bugs.chromium.org/p/chromium/issues/detail?id=986390
// @see https://chromium-review.googlesource.com/c/chromium/src/+/1742784
page.navigate(server.EMPTY_PAGE);
page.setViewportSize(500, 500);
page.setContent("<div style='width:100px;height:2000px'>spacer</div>");
Utils.attachFrame(page, "button-test", server.CROSS_PROCESS_PREFIX + "/input/button.html");
Frame frame = page.frames().get(1);
frame.evalOnSelector("button", "button => button.style.setProperty('position', 'fixed')");
frame.click("button");
assertEquals("Clicked", frame.evaluate("() => window['result']"));
}
@Test
void shouldClickTheButtonWithDeviceScaleFactorSet() {
BrowserContext context = browser.newContext(new Browser.NewContextOptions()
.withViewportSize(400, 400)
.withDeviceScaleFactor(5.0));
Page page = context.newPage();
assertEquals(5, page.evaluate("() => window.devicePixelRatio"));
page.setContent("<div style='width:100px;height:100px'>spacer</div>");
Utils.attachFrame(page, "button-test", server.PREFIX + "/input/button.html");
Frame frame = page.frames().get(1);
ElementHandle button = frame.querySelector("button");
button.click();
assertEquals("Clicked", frame.evaluate("window['result']"));
context.close();
}
@Test
void shouldClickTheButtonWithPxBorderWithOffset() {
page.navigate(server.PREFIX + "/input/button.html");
page.evalOnSelector("button", "button => button.style.borderWidth = '8px'");
page.click("button", new Page.ClickOptions().withPosition(20, 10));
assertEquals(page.evaluate("result"), "Clicked");
// Safari reports border-relative offsetX/offsetY.
assertEquals(isWebKit() ? 20 + 8 : 20, page.evaluate("offsetX"));
assertEquals(isWebKit() ? 10 + 8 : 10, page.evaluate("offsetY"));
}
@Test
void shouldClickTheButtonWithEmBorderWithOffset() {
page.navigate(server.PREFIX + "/input/button.html");
page.evalOnSelector("button", "button => button.style.borderWidth = '2em'");
page.evalOnSelector("button", "button => button.style.fontSize = '12px'");
page.click("button", new Page.ClickOptions().withPosition(20, 10));
assertEquals("Clicked", page.evaluate("result"));
// Safari reports border-relative offsetX/offsetY.
assertEquals(isWebKit() ? 12 * 2 + 20 : 20, page.evaluate("offsetX"));
assertEquals(isWebKit() ? 12 * 2 + 10 : 10, page.evaluate("offsetY"));
}
@Test
void shouldClickAVeryLargeButtonWithOffset() {
page.navigate(server.PREFIX + "/input/button.html");
page.evalOnSelector("button", "button => button.style.borderWidth = '8px'");
page.evalOnSelector("button", "button => button.style.height = button.style.width = '2000px'");
page.click("button", new Page.ClickOptions().withPosition(1900, 1910));
assertEquals("Clicked", page.evaluate("() => window['result']"));
// Safari reports border-relative offsetX/offsetY.
assertEquals(isWebKit() ? 1900 + 8 : 1900, page.evaluate("offsetX"));
assertEquals(isWebKit() ? 1910 + 8 : 1910, page.evaluate("offsetY"));
}
@Test
void shouldClickAButtonInScrollingContainerWithOffset() {
page.navigate(server.PREFIX + "/input/button.html");
page.evalOnSelector("button", "button => {\n" +
" const container = document.createElement('div');\n" +
" container.style.overflow = 'auto';\n" +
" container.style.width = '200px';\n" +
" container.style.height = '200px';\n" +
" button.parentElement.insertBefore(container, button);\n" +
" container.appendChild(button);\n" +
" button.style.height = '2000px';\n" +
" button.style.width = '2000px';\n" +
" button.style.borderWidth = '8px';\n" +
"}");
page.click("button", new Page.ClickOptions().withPosition(1900, 1910));
assertEquals("Clicked", page.evaluate("() => window['result']"));
// Safari reports border-relative offsetX/offsetY.
assertEquals(isWebKit() ? 1900 + 8 : 1900, page.evaluate("offsetX"));
assertEquals(isWebKit() ? 1910 + 8 : 1910, page.evaluate("offsetY"));
}
@Test
@DisabledIf(value="com.microsoft.playwright.TestBase#isFirefox", disabledReason="skip")
void shouldClickTheButtonWithOffsetWithPageScale() {
BrowserContext context = browser.newContext(new Browser.NewContextOptions()
.withViewportSize(400, 400)
.withIsMobile(true));
Page page = context.newPage();
page.navigate(server.PREFIX + "/input/button.html");
page.evalOnSelector("button", "button => {\n" +
" button.style.borderWidth = '8px';\n" +
" document.body.style.margin = '0';\n" +
"}");
page.click("button", new Page.ClickOptions().withPosition(20, 10));
assertEquals("Clicked", page.evaluate("result"));
// 20;10 + 8px of border in each direction
int expectedX = 28;
int expectedY = 18;
if (isWebKit()) {
// WebKit rounds up during css -> dip -> css conversion.
expectedX = 29;
expectedY = 19;
} else if (isChromium() && !headful) {
// Headless Chromium rounds down during css -> dip -> css conversion.
expectedX = 27;
expectedY = 18;
}
assertEquals(expectedX, Math.round((Integer) page.evaluate("pageX") + 0.01));
assertEquals(expectedY, Math.round((Integer) page.evaluate("pageY") + 0.01));
context.close();
}
@Test
void shouldWaitForStablePosition() {
page.navigate(server.PREFIX + "/input/button.html");
page.evalOnSelector("button", "button => {\n" +
" button.style.transition = 'margin 500ms linear 0s';\n" +
" button.style.marginLeft = '200px';\n" +
" button.style.borderWidth = '0';\n" +
" button.style.width = '200px';\n" +
" button.style.height = '20px';\n" +
" // Set display to 'block' - otherwise Firefox layouts with non-even\n" +
" // values on Linux.\n" +
" button.style.display = 'block';\n" +
" document.body.style.margin = '0';\n" +
"}");
page.click("button");
assertEquals("Clicked", page.evaluate("result"));
assertEquals(300, page.evaluate("pageX"));
assertEquals(10, page.evaluate("pageY"));
}
void shouldWaitForBecomingHitTarget() {
// not supported in sync api
}
@Test
void shouldFailWhenObscuredAndNotWaitingForHitTarget() {
page.navigate(server.PREFIX + "/input/button.html");
ElementHandle button = page.querySelector("button");
page.evaluate("() => {\n" +
" document.body.style.position = 'relative';\n" +
" const blocker = document.createElement('div');\n" +
" blocker.style.position = 'absolute';\n" +
" blocker.style.width = '400px';\n" +
" blocker.style.height = '20px';\n" +
" blocker.style.left = '0';\n" +
" blocker.style.top = '0';\n" +
" document.body.appendChild(blocker);\n" +
"}");
button.click(new ElementHandle.ClickOptions().withForce(true));
assertEquals("Was not clicked", page.evaluate("window['result']"));
}
void shouldWaitForButtonToBeEnabled() {
// not supported in sync api
}
void shouldWaitForInputToBeEnabled() {
// not supported in sync api
}
void shouldWaitForSelectToBeEnabled() {
// not supported in sync api
}
@Test
void shouldClickDisabledDiv() {
page.setContent("<div onclick='javascript:window.__CLICKED=true;' disabled>Click target</div>");
page.click("text=Click target");
assertEquals(true, page.evaluate("__CLICKED"));
}
@Test
void shouldClimbDomForInnerLabelWithPointerEventsNone() {
page.setContent("<button onclick='javascript:window.__CLICKED=true;'><label style='pointer-events:none'>Click target</label></button>");
page.click("text=Click target");
assertEquals(true, page.evaluate("__CLICKED"));
}
@Test
void shouldWorkWithUnicodeSelectors() {
page.setContent("<button onclick='javascript:window.__CLICKED=true;'><label style='pointer-events:none'>Найти</label></button>");
page.click("text=Найти");
assertEquals(true, page.evaluate("__CLICKED"));
}
@Test
void shouldClimbUpTo_roleButton_() {
page.setContent("<div role=button onclick='javascript:window.__CLICKED=true;'><div style='pointer-events:none'><span><div>Click target</div></span></div>");
page.click("text=Click target");
assertEquals(true, page.evaluate("__CLICKED"));
}
void shouldWaitForBUTTONToBeClickableWhenItHasPointerEventsNone() {
// not supported in sync api
}
void shouldWaitForLABELToBeClickableWhenItHasPointerEventsNone() {
// not supported in sync api
}
@Test
void shouldUpdateModifiersCorrectly() {
page.navigate(server.PREFIX + "/input/button.html");
page.click("button", new Page.ClickOptions().withModifiers(asList(SHIFT)));
assertEquals(true, page.evaluate("shiftKey"));
page.click("button", new Page.ClickOptions().withModifiers(emptyList()));
assertEquals(false, page.evaluate("shiftKey"));
page.keyboard().down("Shift");
page.click("button", new Page.ClickOptions().withModifiers(emptyList()));
assertEquals(false, page.evaluate("shiftKey"));
page.click("button");
assertEquals(true, page.evaluate("shiftKey"));
page.keyboard().up("Shift");
page.click("button");
assertEquals(false, page.evaluate("shiftKey"));
}
@Test
void shouldClickAnOffscreenElementWhenScrollBehaviorIsSmooth() {
page.setContent(
"<div style='border: 1px solid black; height: 500px; overflow: auto; width: 500px; scroll-behavior: smooth'>\n" +
" <button style='margin-top: 2000px' onClick='window.clicked = true'>hi</button>\n" +
" </div>");
page.click("button");
assertEquals(true, page.evaluate("window.clicked"));
}
@Test
void shouldReportNiceErrorWhenElementIsDetachedAndForceClicked() {
page.navigate(server.PREFIX + "/input/animating-button.html");
page.evaluate("addButton()");
ElementHandle handle = page.querySelector("button");
page.evaluate("stopButton(true)");
try {
handle.click(new ElementHandle.ClickOptions().withForce(true));
fail("did not throw");
} catch (PlaywrightException e) {
assertTrue(e.getMessage().contains("Element is not attached to the DOM"));
}
assertEquals(null, page.evaluate("window.clicked"));
}
void shouldFailWhenElementDetachesAfterAnimation() {
// not supported in sync api
}
void shouldRetryWhenElementDetachesAfterAnimation() {
// not supported in sync api
}
void shouldRetryWhenElementIsAnimatingFromOutsideTheViewport() {
// not supported in sync api
}
void shouldFailWhenElementIsAnimatingFromOutsideTheViewportWithForce() {
// not supported in sync api
}
@Test
void shouldDispatchMicrotasksInOrder() {
page.setContent(
"<button id=button>Click me</button>\n" +
"<script>\n" +
" let mutationCount = 0;\n" +
" const observer = new MutationObserver((mutationsList, observer) => {\n" +
" for(let mutation of mutationsList)\n" +
" ++mutationCount;\n" +
" });\n" +
" observer.observe(document.body, { attributes: true, childList: true, subtree: true });\n" +
" button.addEventListener('mousedown', () => {\n" +
" mutationCount = 0;\n" +
" document.body.appendChild(document.createElement('div'));\n" +
" });\n" +
" button.addEventListener('mouseup', () => {\n" +
" window['result'] = mutationCount;\n" +
" });\n" +
"</script>");
page.click("button");
assertEquals(1, page.evaluate("() => window['result']"));
}
@Test
void shouldClickTheButtonWhenWindowInnerWidthIsCorrupted() {
page.navigate(server.PREFIX + "/input/button.html");
page.evaluate("() => Object.defineProperty(window, 'innerWidth', {value: 0})");
page.click("button");
assertEquals("Clicked", page.evaluate("result"));
}
}