Search code examples
pythonseleniumselenium-chromedriverweb-testing

Selenium Python: Actions.move_to_element not working


I am currently working on a selenium test to test some pagination buttons

These buttons are outside the viewport of the screen...

In firefox I can execute this code just fine (obviously with driver = webdriver.Firefox)

from selenium.webdriver.common.action_chains import ActionChains
import selenium.common.exceptions
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager

driver = webdriver.Chrome(ChromeDriverManager().install())
url = 'https://stackoverflow.com'

driver.get(url)
driver.maximize_window()

footer_link = driver.find_element_by_css_selector("#footer > div > nav > div:nth-child(1) > h5 > a")
action = ActionChains(driver)
action.move_to_element(footer_link).click().perform()
driver.quit()

(btw this is a minimal production of my error)

Again, the code works just fine in firefox

However in Chrome (version 89), the code fails with this error

selenium.common.exceptions.MoveTargetOutOfBoundsException: Message: move target out of bounds

I have tried a couple things to make it work, and I have been able to make it work by scrolling to the element with execute_script() but I need to use a sleep(), implicit wait, or explicit wait.

I would like to use the established way to scroll to the element without using sleep, aka using the ActionChains object

From the little bit of digging on the internet, I would assume this code to work, but I keep getting the same error...

Any help would be greatly appreciated!


Solution

  • https://github.com/SeleniumHQ/selenium/issues/7315

    you have to scroll it into view first ,

    also isvisible doesn't check if element is in view port, so its not possible to use wait for visibility

    https://github.com/seleniumhq/selenium-google-code-issue-archive/issues/8054

    so use :

    from selenium.webdriver.support import expected_conditions as EC
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.common.by import By
    import selenium.common.exceptions
    from selenium import webdriver
    import time
    
    from selenium.webdriver.support.wait import WebDriverWait
    
    options = webdriver.ChromeOptions()
    
    driver = webdriver.Chrome()
    url = 'https://stackoverflow.com'
    
    driver.get(url)
    driver.maximize_window()
    
     
    footer_link=WebDriverWait(driver, 10).until(EC.presence_of_element_located(
       (By.CSS_SELECTOR, "#footer > div > nav > div:nth-child(1) > h5 > a")))
    
    driver.execute_script("arguments[0].scrollIntoView()", footer_link)
    
    time.sleep(3)
    
    
    footer_link.click()
    
    driver.quit()
    

    you can use custom waits also:

    from selenium.webdriver.support import expected_conditions as EC
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.common.by import By
    import selenium.common.exceptions
    from selenium import webdriver
    import time
    
    from selenium.webdriver.support.wait import WebDriverWait
    
    options = webdriver.ChromeOptions()
    
    driver = webdriver.Chrome()
    url = 'https://stackoverflow.com'
    
    driver.get(url)
    driver.maximize_window()
    
     
    footer_link=WebDriverWait(driver, 10).until(EC.presence_of_element_located(
       (By.CSS_SELECTOR, "#footer > div > nav > div:nth-child(1) > h5 > a")))
    
    driver.execute_script("$('footer').scrollIntoView(true)", footer_link)
    
    WebDriverWait(driver, 100).until(lambda a: driver.execute_script(
        "return parseInt($('html').scrollTop())>6500"))
    
    
    footer_link.click()
    
    driver.quit()
    

    here i am using jquery.scrollTop() to get current scroll position.