Any built-in way for branching waits with OR conditions? - python

After I click a button on a webpage, one of two things can happen. Normally, I would use a wait until when there's a single event outcome, but is there any built in methodology where I can wait until 1 of two things happens i.e. one of two elements exists?

To wait until either of two elements you can induce WebDriverWait for either of the two elements through the OR option and you can use either of the following approaches:
Using CssSelector you can pass the expressions seperated by comma as follows:
element = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, ".classA, .classB"))
Using CssSelector and lambda you can pass the expressions through OR condition as follows:
element = WebDriverWait(driver,20).until(lambda driver: driver.find_element(By.CSS_SELECTOR, "tagname.classname") or driver.find_element(By.CSS_SELECTOR, "tagname#elementID"))
Using XPath you can pass the expressions through OR condition as follows:
element = WebDriverWait(driver,20).until(EC.visibility_of_element_located((By.XPATH, "//tag_name[#class='elementA' or #id='elementB']"))
Using XPath and lambda you can pass the expressions through OR condition as follows:
element = WebDriverWait(driver,20).until(lambda driver: driver.find_element(By.XPATH,"xpathA") or driver.find_element(By.XPATH,"xpathB"))
Reference
You can find a couple of relevant discussions in:
WebDriverWait for multiple conditions (OR logical evaluation)
selenium two xpath tests in one
Python / Selenium: Logic Operators in WebDriverWait Expected Conditions

you can do it tbh this is in java i dont know about python but this is at least an idea if you have two condition to be met for example input 1 to be present and input 2
I would create two boolean variabels that i will put in them driver.findelements(by....).size()>0;
then i will just add an if so in case not both of them show it will crash or do what ever i want.
Code example:
Boolean AccesViaLogin = driver.findElements(By.id("username_login")).size() > 0;
Boolean AccesViaHomepage = driver.findElements(By.xpath("//button[contains(text(),\"Connexion\")]")).size() > 0;
if (AccesViaLogin == true && AccesViaHomepage == true) {
}

Related

click through list elements with selenium python

The given list contains the references which will paste into the xpath ids (please find it below in my code) where x are the indexes of the elements.
I want to go through on all elements and click one by one by referring with its indexes, 'like so'
m_list = ['message0', 'message1', 'message2', 'message3', 'message4']
for x in range(0, len(m_list)):
WebDriverWait(driver, 10).until(EC.element_to_be_clickable(
(By.XPATH, f'//*[#id="{str(m_list[int(x)])}"]'))).click()
time.sleep(2)
This exception is common when you use Explicit wait which is WebDriverWait. This is expected since you wait for a fixed time for the element to be clickable. If the element was not found within that time, this exception is thrown. You might want to increase that time. The explicit wait can only be applied for specified elements, so if you are trying to click a paragraph, it won't work. If your elements appear after your initial click, that sleep command should also be in the loop, or you can use Implicit Wait.
Also if you want to iterate your list, you can use;
for i in m_list:
WebDriverWait(driver, 100).until(EC.element_to_be_clickable((By.XPATH, f'//*[#id="{i}"]'))).click()

How to get all elements with a class within wait

I am scraping the xbox website with selenium but I encountered a problem when extracting someone's followers and friends: both elements have the same class, with no other property setting them apart, so I need to find all elements with that class and append them to a list and get the first, second value. I just need to know how to find all elements with a class whilst using wait until as seen below
followers = WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.CSS_SELECTOR, ".item-value-data"))).text
#this currently only gets the first element
I am aware of how to do this without wait; just putting elements, but I couldn't find anything regarding using this in wait.
WebDriverWait waits until at least 1 element matching the passed condition is found.
There is no expected condition supported by Selenium with Python to wait for predefined amount of elements matching or something like this.
What you can do is to put a small sleep after the wait to make the page fully loaded and then get the list of desired elements.
Like this:
WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.CSS_SELECTOR, ".item-value-data")))
time.sleep(1)
followers = []
followers_els = driver.find_elements_by_css_selector(".item-value-data")
for el in followers_els:
followers.append(el.text)

Select the focused element in Selenium

What I'm dealing with
In one of my tests I need to interact with a pop-up input field that is very hard to select using a regular css selector or xpath. I know, however, that this pop-up box will have the focus.
How can I use the fact that it's focused to properly assert that the input field contains some text?
Pseudocode of what I'm looking for:
element = driver.find_element_by_FIND_THE_ELEMENT_WITH_FOCUS
assert elem.text == "foobar"
Partial solutions I've come across:
Possibly a working solution in Ruby:
element = #driver.find_element :css, 'input:focus'
sleep(3)
element.send_keys "Hello WebDriver!"
assert_equal(element.attribute('value'),"Hello WebDriver!")
Getting the focused element with jQuery:
var focus = $(document.activeElement);
Could you provide a solution in python please?
EDIT:
Here's the HTML of the element:
<div class="css-1492t68">Select a speaker...</div>
I realized my question could've lead to some confusion as the element itself is does not have the semantic <input> tag but is rather a <div>. I suppose this is handled by the quill-editor library, which our dev team used when designing this app.
Here's a screenshot of the pop-up box:
Since the element does not have a consistent CSS class, using an XPath expression is probably going to be your best bet.
You need to wait for the element to be clickable (which means intractable) by matching a <div> that contains the expected text, and has a CSS class that begins with css-. Doing this should allow enough time for JavaScript to do its thing in the browser to initialize the quill-editor and display it:
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ExpectedConditions
xpath = "//div[starts-with(#class, 'css-')][contains(., 'The text you expect to find')]"
expected_condition = ExpectedConditions.element_to_be_clickable((By.XPATH, xpath))
element = WebDriverWait(driver, 20).until(expected_condition)
If this pop up field is directly inside a particular parent element, you can further limit your xpath expression accordingly.
If this succeeds, no error will be thrown. If it does not succeed, it should throw a WebDriverTimeoutException (or whatever this error is called in Python).
use:
elem = driver.switch_to.active_element

How can I create a list of elements with the same xpath using selenium with python?

I need to click on several elements from the same table on the same webpage. I was thinking to do so with a for loop but in order to perform that action I first need to create a list of these elements.
//table[#border='1']//a
This is the xpath which selects all the elements from the table, how can I create a list of all these?
Use find_elements instead of find_element:
links = driver.find_elements_by_xpath("//table[#border='1']//a")
for values in links:
values.click()
While #SergiyKonoplyaniy answer was in the right direction, addressing your queries one by one:
How can I create a list of elements with the same xpath : To create a list of elements you need to use find_elements_by_xpath(xpath) which will create a List of elements matching the xpath you have specified.
Example:
my_links = driver.find_elements_by_xpath("//table[#border='1']//a")
Need to click on several elements: As you need to click() on several elements you have to iterate through all the elements you have captured in the List as follows:
for link in my_links:
link.click()
Now the most important aspect is, as per your xpath //table[#border='1']//a each and every element:
Has 3 distinct stages interms of presence, visibility and interactibility (i.e. clickability)
To collect the elements in a List you should always invoke a waiter with expected-conditions as visibility_of_all_elements_located(locator) as follows:
my_list = WebDriverWait(driver, 20).until(expected_conditions.visibility_of_all_elements_located((By.XPATH, "//table[#border='1']//a")))
The pseudo code as a solution for your question will be:
my_links = WebDriverWait(driver, 20).until(expected_conditions.visibility_of_all_elements_located((By.XPATH, "//table[#border='1']//a")))
for link in my_links:
link.click()
For your future reference, if you intend to invoke click() on any particular element always invoke a waiter with expected-conditions as element_to_be_clickable(locator) as follows:
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "desired_element_xpath"))).click()

WebDriverWait on find_elements_by_xpath

I am trying to figure out how WebDriverWait works with find_elements_by_xpath. How does it know that all related elements have loaded or does it just wait until page is loaded.
I can understand if we have a specific element using find_element_by_xpath, but not sure with find_elements_by_xpath.
For example:
elements = WebDriverWait(driver, 5).until(lambda driver: driver.find_elements_by_xpath("//table[#id='%s']/tbody/tr" % myid))
The expected condition you've presented would actually evaluate to True once there is at least one element matching the XPath expression. In other words, it is equivalent to:
expression = "//table[#id='%s']/tbody/tr" % myid
wait.until(EC.presence_of_element_located((By.XPATH, expression)))
webdriver isn't waiting for the page to be loaded -- it can't since the page's contents could be continually changing. Instead it simply executes the find_elements_* command and if successful, the WebDriverWait(...).until call returns the elements found. It is no different than find_element_by_xpath, except that more than one element may be returned.

Categories

Resources