askandconcern
askandconcern

Reputation: 73

There is error "Invalid locator values passed in" in case we use find_element instead of find_element_by

I'm using Python-Webdriver to automate a "click" action. Here is my code:

from selenium.webdriver.common.by import By
from selenium import webdriver
from selenium.common.exceptions import InvalidSelectorException


LOGIN_BUTTON = (By.XPATH, '//a[contains(@class,"aui-nav-link login-link")]')
NEWS_OPTION = (By.ID, 'blq-nav-news')

driver = webdriver.Chrome()
driver.implicitly_wait(30)
driver.get("http://bbc.co.uk/")
myDynamicElement = driver.find_element(NEWS_OPTION)
myDynamicElement.click()

The console generates an exception as below

    raise InvalidSelectorException("Invalid locator values passed in")
selenium.common.exceptions.InvalidSelectorException: Message: Invalid locator values passed in

But, if I change the line

"myDynamicElement = driver.find_element(NEWS_OPTION) "

to

"myDynamicElement = driver.find_element_by_id('blq-nav-news') "

, there is no exception and the script works as expected.

I figured out the root cause is we don't use

find_element_by_*

. So I'd like to know this is limitation on Python-Webdriver ? whether we have a solution to fix my issue without changing my code as I did.

Upvotes: 6

Views: 14540

Answers (2)

alecxe
alecxe

Reputation: 474191

According to the documentation, you can use either find_element_by_* shortcuts, or find_element() and find_elements() "private" methods directly:

Apart from the public methods given above, there are two private methods which might be useful with locators in page objects. These are the two private methods: find_element and find_elements.

Example usage:

from selenium.webdriver.common.by import By

driver.find_element(By.XPATH, '//button[text()="Some text"]')
driver.find_elements(By.XPATH, '//button')

But, in your case, instead of passing 2 parameters to find_element(), you are passing a single one - a tuple NEWS_OPTION. You just need to unpack the tuple into positional arguments:

NEWS_OPTION = (By.ID, 'blq-nav-news')
myDynamicElement = driver.find_element(*NEWS_OPTION)

Or, just as an alternative, you could use keyword arguments also:

NEWS_OPTION = {'by': By.ID, 'value': 'blq-nav-news'}
myDynamicElement = driver.find_element(**NEWS_OPTION)

And, also, whenever you have any doubts how things should work, just dig up the source code and clarify it for yourself. In this case, see how find_element_by_id() method is actually implemented:

def find_element_by_id(self, id_):
    """Finds an element by id.

    :Args:
     - id\_ - The id of the element to be found.

    :Usage:
        driver.find_element_by_id('foo')
    """
    return self.find_element(by=By.ID, value=id_)

Upvotes: 8

Saifur
Saifur

Reputation: 16201

According to the api doc private method find_elements and find_element takes two parameters each and you are passing one which is obviously wrong.

And,

myDynamicElement = driver.find_element(NEWS_OPTION)

and

myDynamicElement = driver.find_element_by_id('blq-nav-news')

are NOT same. driver.find_element_by_id takes one parameter and thus this work and find_element takes two so that fails.

So, literally you should be using

myDynamicElement = driver.find_element(By.ID,'blq-nav-news')

EDIT

NEWS_OPTION = (By.ID, 'blq-nav-news') will return you ('id', 'blq-nav-news') whereas find_element() expects the first parameter to be the implementation of By class mechanism to locate the element not simply a string.

A direct call of NEWS_OPTION = (By.ID, 'blq-nav-news') has returned me a tuple and the attribute of By.ID which is 'id' A simple print out of NEWS_OPTION = (By.ID, 'blq-nav-news') provided me

('id', 'blq-nav-news')

And, here is the source of By class

class By(object):
    """
    Set of supported locator strategies.
    """

    ID = "id"
    XPATH = "xpath"
    LINK_TEXT = "link text"
    PARTIAL_LINK_TEXT = "partial link text"
    NAME = "name"
    TAG_NAME = "tag name"
    CLASS_NAME = "class name"
    CSS_SELECTOR = "css selector"

    @classmethod
    def is_valid(cls, by):
        for attr in dir(cls):
            if by == getattr(cls, attr):
                return True
        return False

Upvotes: 2

Related Questions