diff --git a/java/src/org/openqa/selenium/interactions/Actions.java b/java/src/org/openqa/selenium/interactions/Actions.java index bdec7bf3b94e3..ac6671e75d978 100644 --- a/java/src/org/openqa/selenium/interactions/Actions.java +++ b/java/src/org/openqa/selenium/interactions/Actions.java @@ -54,9 +54,15 @@ public class Actions { private PointerInput activePointer; private KeyInput activeKeyboard; private WheelInput activeWheel; + private Duration actionDuration; public Actions(WebDriver driver) { + this(driver, Duration.ofMillis(250)); + } + + public Actions(WebDriver driver, Duration duration) { this.driver = Require.nonNull("Driver", driver); + this.actionDuration = duration; } /** @@ -215,7 +221,7 @@ public Actions release(WebElement target) { */ public Actions scrollToElement(WebElement element) { WheelInput.ScrollOrigin scrollOrigin = WheelInput.ScrollOrigin.fromElement(element); - return tick(getActiveWheel().createScroll(0, 0, 0, 0, Duration.ofMillis(250), scrollOrigin)); + return tick(getActiveWheel().createScroll(0, 0, 0, 0, this.actionDuration, scrollOrigin)); } /** @@ -229,7 +235,7 @@ public Actions scrollToElement(WebElement element) { public Actions scrollByAmount(int deltaX, int deltaY) { WheelInput.ScrollOrigin scrollOrigin = WheelInput.ScrollOrigin.fromViewport(); return tick( - getActiveWheel().createScroll(0, 0, deltaX, deltaY, Duration.ofMillis(250), scrollOrigin)); + getActiveWheel().createScroll(0, 0, deltaX, deltaY, this.actionDuration, scrollOrigin)); } /** @@ -249,7 +255,7 @@ public Actions scrollFromOrigin(WheelInput.ScrollOrigin scrollOrigin, int deltaX int x = scrollOrigin.getxOffset(); int y = scrollOrigin.getyOffset(); return tick( - getActiveWheel().createScroll(x, y, deltaX, deltaY, Duration.ofMillis(250), scrollOrigin)); + getActiveWheel().createScroll(x, y, deltaX, deltaY, this.actionDuration, scrollOrigin)); } /** @@ -548,6 +554,10 @@ public WheelInput getActiveWheel() { return this.activeWheel; } + public Duration getActionDuration() { + return this.actionDuration; + } + /** * Generates a composite action containing all actions so far, ready to be performed (and resets * the internal builder state, so subsequent calls to this method will contain fresh sequences). diff --git a/java/test/org/openqa/selenium/interactions/ActionDurationTest.java b/java/test/org/openqa/selenium/interactions/ActionDurationTest.java new file mode 100644 index 0000000000000..f9d603390eedf --- /dev/null +++ b/java/test/org/openqa/selenium/interactions/ActionDurationTest.java @@ -0,0 +1,82 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 org.openqa.selenium.interactions; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.Duration; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.testing.JupiterTestBase; + +@Tag("UnitTests") +class ActionDurationTest extends JupiterTestBase { + @Test + void shouldScrollToElementWithCustomDuration() { + driver.get( + appServer.whereIs("scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html")); + WebElement iframe = driver.findElement(By.tagName("iframe")); + + assertFalse(inViewport(iframe)); + + new Actions(driver, Duration.ofMillis(111)).scrollToElement(iframe).perform(); + + assertTrue(inViewport(iframe)); + } + + @Test + void shouldScrollFromViewportByGivenAmountWithCustomDuration() { + driver.get( + appServer.whereIs("scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html")); + WebElement footer = driver.findElement(By.tagName("footer")); + int deltaY = footer.getRect().y; + + new Actions(driver, Duration.ofMillis(111)).scrollByAmount(0, deltaY).perform(); + + assertTrue(inViewport(footer)); + } + + @Test + void shouldBeDefaultActionDuration250ms() { + Actions actions = new Actions(driver); + assertEquals(Duration.ofMillis(250), actions.getActionDuration()); + } + + @Test + void shouldBeCustomDuration110ms() { + Actions actions = new Actions(driver, Duration.ofMillis(110)); + assertEquals(Duration.ofMillis(110), actions.getActionDuration()); + } + + private boolean inViewport(WebElement element) { + + String script = + "for(var e=arguments[0],f=e.offsetTop,t=e.offsetLeft,o=e.offsetWidth,n=e.offsetHeight;\n" + + "e.offsetParent;)f+=(e=e.offsetParent).offsetTop,t+=e.offsetLeft;\n" + + "return" + + " f\n" + + "window.pageYOffset&&t+o>window.pageXOffset"; + + return (boolean) ((JavascriptExecutor) driver).executeScript(script, element); + } +}