619 lines
22 KiB
Java
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"));
|
|
}
|
|
}
|