diff --git a/vaadin-testbench-core-junit5/src/test/java/com/vaadin/testbench/ParametersTest.java b/vaadin-testbench-core-junit5/src/test/java/com/vaadin/testbench/ParametersTest.java index 766bb8757..beb0960c6 100644 --- a/vaadin-testbench-core-junit5/src/test/java/com/vaadin/testbench/ParametersTest.java +++ b/vaadin-testbench-core-junit5/src/test/java/com/vaadin/testbench/ParametersTest.java @@ -30,6 +30,7 @@ public void testDefaultValues() { Assertions.assertEquals(false, Parameters.isScreenshotComparisonCursorDetection()); Assertions.assertFalse(Parameters.isHeadless()); + Assertions.assertFalse(Parameters.isUseJavascriptClick()); Assertions.assertEquals( ClientConfig.defaultConfig().readTimeout().toSeconds(), Parameters.getReadTimeout()); diff --git a/vaadin-testbench-integration-tests-junit5/src/main/java/com/vaadin/testUI/ClickCountView.java b/vaadin-testbench-integration-tests-junit5/src/main/java/com/vaadin/testUI/ClickCountView.java new file mode 100644 index 000000000..cbbddc251 --- /dev/null +++ b/vaadin-testbench-integration-tests-junit5/src/main/java/com/vaadin/testUI/ClickCountView.java @@ -0,0 +1,32 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.testUI; + +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.html.NativeButton; +import com.vaadin.flow.router.Route; + +@Route("ClickCountView") +public class ClickCountView extends Div { + + public ClickCountView() { + NativeButton button = new NativeButton("Click me"); + button.setId("click-target"); + + Div log = new Div(); + log.setId("click-count"); + + button.getElement().addEventListener("click", e -> { + log.setText(String + .valueOf(e.getEventData().get("event.detail").intValue())); + }).addEventData("event.detail"); + + add(button, log); + } +} diff --git a/vaadin-testbench-integration-tests-junit5/src/test/java/com/vaadin/tests/elements/ClickCountIT.java b/vaadin-testbench-integration-tests-junit5/src/test/java/com/vaadin/tests/elements/ClickCountIT.java new file mode 100644 index 000000000..0dacd5364 --- /dev/null +++ b/vaadin-testbench-integration-tests-junit5/src/test/java/com/vaadin/tests/elements/ClickCountIT.java @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2000-2026 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.tests.elements; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; + +import com.vaadin.flow.component.Component; +import com.vaadin.testUI.ClickCountView; +import com.vaadin.testbench.BrowserTest; +import com.vaadin.testbench.TestBenchElement; +import com.vaadin.tests.AbstractBrowserTB9Test; + +public class ClickCountIT extends AbstractBrowserTB9Test { + + @Override + protected Class getTestView() { + return ClickCountView.class; + } + + @BeforeEach + public void open() { + openTestURL(); + } + + @BrowserTest + public void click_hasClickCountOne() { + TestBenchElement button = $(NativeButtonElement.class) + .id("click-target"); + button.click(); + Assertions.assertEquals("1", $("div").id("click-count").getText()); + } + + @BrowserTest + public void clickWithCoordinates_hasClickCountOne() { + TestBenchElement button = $(NativeButtonElement.class) + .id("click-target"); + button.click(0, 0); + Assertions.assertEquals("1", $("div").id("click-count").getText()); + } + + @BrowserTest + public void doubleClick_hasClickCountTwo() { + TestBenchElement button = $(NativeButtonElement.class) + .id("click-target"); + button.doubleClick(); + Assertions.assertEquals("2", $("div").id("click-count").getText()); + } +} diff --git a/vaadin-testbench-shared/src/main/java/com/vaadin/testbench/Parameters.java b/vaadin-testbench-shared/src/main/java/com/vaadin/testbench/Parameters.java index 46c48e5f0..56ab2cf95 100644 --- a/vaadin-testbench-shared/src/main/java/com/vaadin/testbench/Parameters.java +++ b/vaadin-testbench-shared/src/main/java/com/vaadin/testbench/Parameters.java @@ -37,6 +37,7 @@ public class Parameters { private static int readTimeout; private static int hubPort; private static String[] chromeOptions; + private static boolean useJavascriptClick; static { isDebug = getSystemPropertyBoolean("debug", false); @@ -68,6 +69,8 @@ public class Parameters { (int) ClientConfig.defaultConfig().readTimeout().toSeconds()); hubPort = getSystemPropertyInt("hubPort", 4444); setChromeOptions(getSystemPropertyString("chromeOptions", null)); + useJavascriptClick = getSystemPropertyBoolean("useJavascriptClick", + false); } /** @@ -564,4 +567,35 @@ public static String[] getChromeOptions() { public static int getReadTimeout() { return readTimeout; } + + /** + * Returns whether {@link TestBenchElement#click()} should use JavaScript + * instead of Selenium Actions. + *

+ * JavaScript click avoids "element not clickable at point" issues but does + * not correctly simulate a real user interaction in the browser (e.g., + * browser event properties such as click count may be missing or + * incorrect). + * + * @return {@code true} if JavaScript click is used by default, + * {@code false} otherwise + */ + public static boolean isUseJavascriptClick() { + return useJavascriptClick; + } + + /** + * Sets whether {@link TestBenchElement#click()} should use JavaScript + * instead of Selenium Actions. + *

+ * Can also be set with the system property + * {@code com.vaadin.testbench.Parameters.useJavascriptClick=true}. + * + * @param useJavascriptClick + * {@code true} to use JavaScript click, {@code false} to use + * Selenium Actions + */ + public static void setUseJavascriptClick(boolean useJavascriptClick) { + Parameters.useJavascriptClick = useJavascriptClick; + } } diff --git a/vaadin-testbench-shared/src/main/java/com/vaadin/testbench/TestBenchElement.java b/vaadin-testbench-shared/src/main/java/com/vaadin/testbench/TestBenchElement.java index b5de02c10..b5df03302 100644 --- a/vaadin-testbench-shared/src/main/java/com/vaadin/testbench/TestBenchElement.java +++ b/vaadin-testbench-shared/src/main/java/com/vaadin/testbench/TestBenchElement.java @@ -191,16 +191,39 @@ public boolean isFocused() { @Override public void click() { - // JS call to click does not focus element, hence ensure focus + if (Parameters.isUseJavascriptClick()) { + javascriptClick(); + } else { + autoScrollIntoView(); + waitForVaadin(); + new Actions(getDriver()).click(wrappedElement).build().perform(); + } + } + + /** + * Clicks the element using JavaScript rather than Selenium Actions. + *

+ * This avoids "element not clickable at point" issues that can occur when + * the element is behind an overlay or otherwise not directly reachable by + * Selenium, but it does not correctly simulate a real user interaction in + * the browser (e.g., browser event properties such as click count may be + * missing or incorrect). + *

+ * Prefer {@link #click()} for normal use. Use this method only when you + * need to click an element that Selenium cannot reach directly. + *

+ * To use this method globally as the default for {@link #click()}, set the + * system property + * {@code com.vaadin.testbench.Parameters.useJavascriptClick=true} or call + * {@link Parameters#setUseJavascriptClick(boolean)}. + */ + public void javascriptClick() { focus(); try { - // Avoid strange "element not clickable at point" problems callFunction("click"); } catch (Exception e) { - if (e.getMessage() + if (e.getMessage() != null && e.getMessage() .contains("Inspected target navigated or closed")) { - // This happens with chromedriver although e.g. navigation - // succeeds return; } // SVG elements and maybe others do not have a 'click' method