Selenium + Python expect specific already found element - python

I'd like to use expect on an object already located but I'm getting an error.
I am able to locate the parent only before it is clicked -- afterwards it mutates into "one-of-many-alike" input elements and only way to distinguish it is "value" property (not attribute thus cannot be accessed from XPath).
My code is (something like):
parent_element = self.driver.find_element_by_xpath("//div/div/input[position()=10]/div")
parent_element.click() # After this I cannot locate parent_element any more
child_element = WebDriverWait(self.driver,3).until(
expect.element_to_be_clickable(
parent_element.find_element_by_xpath("./div")
)
)
child_element.click()
Which leads to:
TypeError: find_element() argument after * must be an iterable, not function

Try this line:
child_element = WebDriverWait(parent_element, 3).until(
expect.element_to_be_clickable((By.XPATH, "./div")))
Note that element_to_be_clickable() should receive tuple of "by" and "value" as the only argument, but not WebElement
P.S. I assume that expect is ExpectedConditions

Related

What is a Selenium locator in Python specifically?

My question comes from trying to understand the following code (which is meant to wait for a particular element to be loaded on the page before proceeding):
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
# ... start chromium_driver
wait_timeout = 10
wait = WebDriverWait(chromium_driver, wait_timeout)
target_id = "CookiePopup"
target_element = wait.until(EC.presence_of_element_located((By.ID, target_id)))
I can understand what a locator is conceptually ("a way to identify elements on a page"), but I'm trying to wrap my head around its structure and specification as an object in this context (namely, the signature of EC.presence_of_element_located(locator)). N.B., that the (By.ID, target_id) part in the code above needs to be enclosed in parenthesis; i.e.,
EC.presence_of_element_located(By.ID, target_id)
causes
TypeError: __init__() takes 2 positional arguments but 3 were given
The documentation explains that "[a locator] is the argument passed to the Finding element methods".
The Finding element methods page shows that the find_element() method in Python takes two arguments, which is the part that I find somewhat confusing:
vegetable = driver.find_element(By.CLASS_NAME, "tomatoes")
In addition, By.CLASS_NAME, By.ID etc. are actually properties that contain strings ("class name" and "id" respectively).
Compare this to the Java (or any of the other languages) code:
WebElement vegetable = driver.findElement(By.className("tomatoes"));
which makes more sense: By.className() is a method, which takes the (HTML) class name as an argument and returns a locator object that matches elements with that class name.
Given the above, would it be accurate to describe the locator as a tuple of two str, with the first string being the type of identifier used and the second string being the value of that identifier? And as a follow-up question, why is Python different in this way than the other languages?
The documentation doesn't make it clear (to me anyway) why you have to pass the arguments enclosed in parenthesis, but have a look at the source code for that function here:
https://www.selenium.dev/selenium/docs/api/py/_modules/selenium/webdriver/support/expected_conditions.html#presence_of_element_located
You can see that in the implementation of presence_of_element_located(), it takes whatever you pass as a locator and passes that argument on in a call to find_element(*locator)
Note that asterisk (*) is used in Python to unpack the contents of a tuple (or any iterable). For more info on that, see this answer:
https://stackoverflow.com/a/1993732

Unpacked operator in Selenium explicit wait

In my code I faced an issue, when I try to use an unpacking operator in an explicit wait, there is a problem: "Can't use starred expression here"
self.xpath = (locator, string)
WebDriverWait(self.sin.get_driver(),wait).until(EC.presence_of_element_located((*self.xpath)))
In this case is locator something like By.XPATH, and string - "//div[]//..." - xpath of an element on a page.
Can such problem be solved? Or is it impossible to use starred impressions in the explicit wait anyway?
In Selenium EC.presence_of_element_located(()) method is expecting to receive a By class object.
It is not intended to iterate over any kind of iteratable like list, tuple etc.

How to get the 'state' of an element in python-selenium?

In python-selenium there are two ways to find an element.
First, you can use the actual method to find an element, e.g.
element = find_element_by_xpath(myxpath)
Second, you can use the WebDriverWait to make sure the webdriver waits for some timeout until the element has been found in some given state:
element = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, myxpath)))
For the latter method you can define several 'expected conditions' (see here):
element_located_to_be_selected, element_to_be_clickable, element_to_be_selected etc.
My question: Using only the first method of finding an element, how can I check which state this element is in (in case I found the element). How can I check if is 'clickable', or 'selectable' etc.? Is there an attribute of the element object that I can use to determine the state of the element?
No, there is no direct method to retrieve the exact state of a WebElement through Selenium.
find_element_by_xpath(xpath)
find_element_by_xpath() would simply find the first matching WebElement using the xpath. If no matching element is found it will raise NoSuchElementException
But Selenium offers multiple methods to validate the state of a WebElement as follows:
is_displayed(): Returns a Boolean value (true / false) whether the element is visible to a user or not.
is_enabled(): Returns a Boolean value (true / false) whether the element is enabled or not.
is_selected(): Returns a Boolean value (true / false) whether the element is selected or not.
WebDriverWait() in-conjunction with expected_conditions class
The canned expected_conditions are generally useful to access the elements when they achieves a definite state as follows:
presence_of_element_located(locator) mentions :
class selenium.webdriver.support.expected_conditions.presence_of_element_located(locator)
An expectation for checking that an element is present on the DOM of a page. This does not necessarily mean that the element is visible.
visibility_of_element_located(locator) mentions :
class selenium.webdriver.support.expected_conditions.visibility_of_element_located(locator)
An expectation for checking that an element is present on the DOM of a page and visible. Visibility means that the element is not only displayed but also has a height and width that is greater than 0.
element_to_be_clickable(locator) mentions:
class selenium.webdriver.support.expected_conditions.element_to_be_clickable(locator)
An Expectation for checking an element is visible and enabled such that you can click it.
There isn't any is_clickable() function or attribute. Looking at the source code
class element_to_be_clickable(object):
""" An Expectation for checking an element is visible and enabled such that
you can click it."""
def __init__(self, locator):
self.locator = locator
def __call__(self, driver):
element = visibility_of_element_located(self.locator)(driver)
if element and element.is_enabled():
return element
else:
return False
you can see element_to_be_clickable uses visibility_of_element_located, which waits for visibility using element.is_displayed(), and checks if the element is enabled using element.is_enabled(). You can check the combination of those two.
For element_located_to_be_selected you do have a function, element.is_selected().
Regarding the "click()", it doesn't appear in the Python WebElement API, but in Java's API it stats "There are some preconditions for an element to be clicked. The element must be visible and it must have a height and width greater then 0", checking those parameters should suffice.
Something like this:
button = driver.find_element_by_class_name('?')
href_data = button.get_attribute('href')
if href_data is None:
is_clickable = False

How does starred expression works when using with find_element() method

def enter_text_textbox(self, locator, text):
ele = self.driver.find_element(*locator)
try:
logging.info("# Entering text in Textbox.")
ele.clear()
ele.send_keys(text)
return True
except NoSuchElementException:
return False
Here is a method enter_text_textbox which accepts locator and some text as parameters.
I passed locator as something like this:
self.email_field = (By.XPATH, "//input[#id='email']")
When I tried to get into this(self.driver.find_element(*locator)) method, it displayed
As from the image the methods contains parameters of ID only.
By running the test script(not displayed here), it worked perfectly fine.
I want to know how does this method know if this locator is made by XPATH or ID or CSS i.e. How the starred expression works?
self.driver.find_element(*locator)
unpacks into positional arguments. So locator[0] has to be xpath, and locator[1] has to be the other text argument (ID or CSS apparently)
It's equivalent (when locator has the proper number of arguments) to:
self.driver.find_element(locator[0],locator[1])
note:
Passing parameters in the wrong order will fail.
Passing not enough or too many parameters will also fail.
Since this isn't a variable argument function, this is bad practice & very unclear.
The image you are showing shows PyCharm telling you what the method uses by default. If you were to just call find_element() it would use By.ID as the by value and None as the value. As Jean-François Fabre stated, the * just unpacks your tuple into positional arguments

Using Selenium and Python, how to check whether the button is still clickable?

so I am doing some web scraping using Selenium with Python and I am having a problem. I am clicking a Next button to move to the next page on a certain website, but I need to stop clicking it when I reach the last page. Now, my idea of doing it would be just to use some_element.click() in a try/except statement and wait until it gives me an error while the button is not clickable anymore. It seems though, .click() doesn't emit any signal of any kind, it doesn't throw an error when the button can't be clicked and it doesn't emit any true or false signal.
a code snippet I tried using:
while True:
try:
button = driver.find_element_by_class_name('Next_button')
button.click()
except:
break
Is there any other way? Thanks and cheers.
Without knowing more about your example target, the minimal that can be said is that a clickable attribute would have an 'href' attribute.
You can use the get_attribute property of an element:
button = driver.find_element_by_class_name('Next_button')
href_data = button.get_attribute('href')
if href_data is None:
is_clickable = False
Gets the given attribute or property of the element.
This method will first try to return the value of a property with the given name. If a property with that name doesn’t exist, it returns the value of the attribute with the same name. If there’s no attribute with that name, None is returned.
Values which are considered truthy, that is equals “true” or “false”, are returned as booleans. All other non-None values are returned as strings. For attributes or properties which do not exist, None is returned.
More on Using get_attribute
You could also try is_displayed or is_enabled
Use this to get classes
element.get_attribute("class")
and check if list of classes contains characteristic class (for example "disable") from your html framework which is used to describe unclickable buttons

Categories

Resources