2

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?

Ratler
  • 431
  • 3
  • 14

1 Answers1

0

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

prime13
  • 145
  • 1
  • 9
  • Thanks, but the source code doesn't really improve my understanding. I suppose it's consistent with everything I said about the locator being some sort of tuple (which, btw, explains why it needs to be passed enclosed in a parenthesis; without the parenthesis, it would be interpreted as two arguments, as opposed to a single argument of type tuple), but we're still lacking a clear definition for a locator and also an explanation as to why the choice was made to be implemented in such a bizarre way (in comparison to the other languages). – Ratler Feb 08 '23 at 00:32