seleniumbase / SeleniumBase

📊 Python's all-in-one framework for web crawling, scraping, testing, and reporting. Supports pytest. UC Mode provides stealth. Includes many tools.
https://seleniumbase.io
MIT License
5.31k stars 974 forks source link

element not found using XPath #612

Closed jbnjohnathan closed 4 years ago

jbnjohnathan commented 4 years ago

I am trying to click on a drop down box created in React in order to select an item in it

This is my SeleniumBase code which is not working (running with pytest --browser=firefox)

from seleniumbase import BaseCase
class MyTestClass(BaseCase):
    def test_ant_dropdown(self):
        url = "https://ant.design/components/select/"
        self.open(url)
        self.assert_title("Select - Ant Design")
        self.click('//*[@id="rc_select_13"]')

Error message is

exception = <class 'selenium.common.exceptions.ElementNotVisibleException'>
message = '\n Element {//*[@id="rc_select_13"]} was not visible after 6 seconds!'

Trying the same with Selenium Webdriver (this works)

# Generated by Selenium IDE
import pytest
import time
import json
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

class TestAdddevice:
    def setup_method(self, method):
        # self.driver = webdriver.Firefox()
        self.driver = webdriver.Firefox(
            executable_path=r"/usr/local/lib/python3.7/dist-packages/seleniumbase-1.42.4-py3.7.egg/seleniumbase/drivers/geckodriver"
        )
        self.vars = {}

    def teardown_method(self, method):
        self.driver.quit()

    def test_adddevice(self):
        self.driver.get("https://ant.design/components/select/")
        self.driver.find_element(By.XPATH, "//*[@id='rc_select_13']").click()

This works. The XPATH is exactly the same, but for some reason SeleniumBase cannot click it.

As I workaround I use this code which works

from seleniumbase import BaseCase
from selenium.webdriver.common.by import By
class MyTestClass(BaseCase):
    def test_ant_dropdown(self):
        url = "https://ant.design/components/select/"
        self.open(url)
        self.assert_title("Select - Ant Design")
        self.driver.find_element(By.XPATH, "//*[@id='rc_select_13']").click()
mdmintz commented 4 years ago

Hi @jbnjohnathan , I found the issue. Before interacting with elements, SeleniumBase first waits for the element to be visible. One of the ways it checks is by looking at the opacity value. Based on the CSS, it was in fact at opacity: 0. In most cases, the element isn't seen at all in this case, but here, the outer parent element is visible (even though the "input" itself is not yet).

Select_-_Ant_Design

In this case, there are a few options for getting around this:

self.get_element("#rc_select_13").click()

OR

self.set_attribute("#rc_select_13", "style", "opacity: 1;")
self.click("#rc_select_13")

I usually use CSS Selectors, but you can use your XPath ("//*[@id='rc_select_13']") in place of ("#rc_select_13") if you desire.

As for down the road, if you have control over the website itself, you may not want to set the opacity of elements you want to interact with to "0", as this can have unintended side effects. But it's really no big deal, as there's a workaround for all such issues with SeleniumBase.

mdmintz commented 4 years ago

Also, a reason why SeleniumBase checks for opacity > 0 before doing a regular self.click(SELECTOR) is because in the past web developers have made important buttons completely invisible by accident by setting the opacity to "0". Other frameworks would click through the invisible buttons and not catch the bug there, but this would be caught by SeleniumBase.

In this case, the input is invisible, but you still see the parent element's bounding box, so you know where the input is going to be, which becomes visible after clicking on the area. The workarounds I listed above allow you to get past this.

jbnjohnathan commented 4 years ago

Ah great detective work! Yeah I think it makes sense to make sure that things you want to interact with are visible. Unfortunately this is a well used React framework, so I have little input on it, but I guess I could file a bug on their github for it. Thanks again : )

jbnjohnathan commented 4 years ago

One thing that would help on SeleniumBase side would be if the error message could be more clear that it found the element I wanted to click, but opacity is set to zero. Now I get "ElementNotVisible" which is the same error I got when I tried changing to an XPath which pointed to a non-existent resource.

mdmintz commented 4 years ago

@jbnjohnathan You're correct. I'll update the error messages to better reflect whether the element didn't exist (NoSuchElementException) versus whether the element exists but isn't visible (ElementNotVisibleException). There will be a new release soon.

mdmintz commented 4 years ago

@jbnjohnathan I released https://github.com/seleniumbase/SeleniumBase/releases/tag/v1.42.6 to improve error messages.

hiqqs commented 4 years ago

what an improvement, very few frameworks have that logging

jbnjohnathan commented 4 years ago

Wow already fixed and released. That is mind-boggling fast! Thanks!

mdmintz commented 4 years ago

Things move very fast in my world. :) I'll close out this ticket.