mozilla / geckodriver

WebDriver for Firefox
https://firefox-source-docs.mozilla.org/testing/geckodriver/
Mozilla Public License 2.0
7.03k stars 1.51k forks source link

scroll_to_element raises MoveTargetOutOfBoundsException but shouldn't #2078

Closed alpatron closed 6 months ago

alpatron commented 1 year ago

Hello. I was trying to use the scroll_to_element method provided by the Actions API in the Python version of Selenium. The documentation says that this method should scroll elements into view, but if I try to use this method for this purpose I get MoveTargetOutOfBoundsException. Given this behaviour, at least in my limited testing, appears only in Geckodriver, I filled this issue here.

System

Testcase

HTML:

<html>
    <body>
        <button style="position:absolute;left:200vw;top:200vh;">
            I'm outside the viewport by default.
        </button>
    </body>
</html>

Selenium:

from selenium import webdriver
from selenium.webdriver.common.by import By
from pathlib import Path
from selenium.webdriver import ActionChains

driver = webdriver.Firefox()
driver.get(Path('bug.html').absolute().as_uri())
button = driver.find_element(By.TAG_NAME,'button')
ActionChains(driver).scroll_to_element(button).perform()
driver.close()

Stacktrace

Traceback (most recent call last):
  File "C:\Users\Vikto\Desktop\seltest\selbug.py", line 13, in <module>
    ActionChains(driver).scroll_to_element(button).perform()
  File "C:\Users\Vikto\Desktop\seltest\Lib\site-packages\selenium\webdriver\common\action_chains.py", line 73, in perform
    self.w3c_actions.perform()
  File "C:\Users\Vikto\Desktop\seltest\Lib\site-packages\selenium\webdriver\common\actions\action_builder.py", line 91, in perform
    self.driver.execute(Command.W3C_ACTIONS, enc)
  File "C:\Users\Vikto\Desktop\seltest\Lib\site-packages\selenium\webdriver\remote\webdriver.py", line 440, in execute
    self.error_handler.check_response(response)

(seltest) C:\Users\Vikto\Desktop\seltest>python selbug.py
Traceback (most recent call last):
  File "C:\Users\Vikto\Desktop\seltest\selbug.py", line 13, in <module>
    ActionChains(driver).scroll_to_element(button).perform()
  File "C:\Users\Vikto\Desktop\seltest\Lib\site-packages\selenium\webdriver\common\action_chains.py", line 73, in perform
    self.w3c_actions.perform()
  File "C:\Users\Vikto\Desktop\seltest\Lib\site-packages\selenium\webdriver\common\actions\action_builder.py", line 91, in perform
    self.driver.execute(Command.W3C_ACTIONS, enc)
  File "C:\Users\Vikto\Desktop\seltest\Lib\site-packages\selenium\webdriver\remote\webdriver.py", line 440, in execute
    self.error_handler.check_response(response)
  File "C:\Users\Vikto\Desktop\seltest\Lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 245, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.MoveTargetOutOfBoundsException: Message: (1926, 1324) is out of bounds of viewport width (1284) and height (883)
Stacktrace:
RemoteError@chrome://remote/content/shared/RemoteError.sys.mjs:8:8
WebDriverError@chrome://remote/content/shared/webdriver/Errors.sys.mjs:180:5
MoveTargetOutOfBoundsError@chrome://remote/content/shared/webdriver/Errors.sys.mjs:370:5
assertInViewPort@chrome://remote/content/marionette/action.sys.mjs:2113:11
dispatch@chrome://remote/content/marionette/action.sys.mjs:1149:21
dispatch/pendingEvents<@chrome://remote/content/marionette/action.sys.mjs:1827:14
dispatch@chrome://remote/content/marionette/action.sys.mjs:1826:39
dispatch/chainEvents<@chrome://remote/content/marionette/action.sys.mjs:1753:27
dispatch@chrome://remote/content/marionette/action.sys.mjs:1755:7
performActions@chrome://remote/content/marionette/actors/MarionetteCommandsChild.sys.mjs:472:23
receiveMessage@chrome://remote/content/marionette/actors/MarionetteCommandsChild.sys.mjs:139:31

Trace-level log

geckodriver.log

titusfortner commented 1 year ago

To add quick context (and get notifications on this issue)...

We discussed adding Scrolling to Actions endpoint at TPAC in Lyon because elements are not implicitly scrolled into view unlike with click endpoint. Not being able to scroll to an element that is outside of the viewport makes the scrolling feature mostly useless. We don't auto scroll in actions class because we need users to make intention clear, but that's what this does.

So, Chrome has the desired behavior, but does it match what is in the spec? Does the spec need to be changed to say if the origin is an element that is outside of the viewport it needs to be first scrolled into view?

pbratkowski commented 7 months ago

Edit: For anyone who stumbles upon this issue from googling the exception, this appears to solve it in a cross-browser manner:

driver.execute_script("arguments[0].scrollIntoView();", element)
ActionChains(driver)...

Consider this solved, so sorry again for the ping :)


We don't auto scroll in actions class because we need users to make intention clear, but that's what this does.

Sorry to ping you ~8 months later, but any chance this had any traction, or do you have any recommendations for some fix?

We are working on porting some tests to the Actions API as it makes many things easier in CI, but it breaks a few of them in Firefox, and I would rather not pepper them all with things like

if self.selenium.name == "firefox" and self.selenium.capabilities.get("moz:headless") is False:
    # hacks
else:
    # normal stuff

Thanks in advance :)

whimboo commented 6 months ago

So I had a look at the WebDriver classic spec and when reading steps 5 and 6 of the section dispatch a scroll action it's clear that our implementation handles it correctly. The scroll action of a wheel input device should actually raise a move target out of bounds error. If that is correct it's debatable. IMO it would be nice to allow scrolling to a given element whether if it's inside or outside the current viewport. @jgraham what do you think?

Here the Python implementation which doesn't take those checks into account: https://github.com/SeleniumHQ/selenium/blob/c1831460444ef70a6550a2983463e2618493f8c2/py/selenium/webdriver/common/action_chains.py#L323-L332

titusfortner commented 6 months ago

What is the point of scrolling, then?

The *whole point is to let you scroll to something out of screen in the Actions class, so you don't have to insert a "element once scrolled into view" equivalent command.

If this is spec compliant, we need to change the spec.

whimboo commented 6 months ago

After speaking to @jgraham it turns out that the scroll action of the wheel input source is actually never meant to be used to "scroll to an element". Instead it has to be used to "scroll at the location of the element". That means that at least the code in the Python bindings is broken and needs to be fixed.

In regards of Webdriver it would need an action primitive to allow something like that.

Also see https://github.com/w3c/webdriver/issues/1635 for probably the same underlying issue.