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.
Related
I am trying to get selenium set up to send out messages automatically and have not yet got around to check if the specific listing has already been sent a message. This causes selenium to give a NoSuchElementException because its looking for (By.XPATH, ('//span[contains(text(),"Message")]'))
How can I have it skip these pages where the element doesn't exist?
message = driver.find_element(By.XPATH, ('//span[contains(text(),"Message")]'))
message.click()
Very small snippet that shows the code where the issue is.
Instead of find_element you should use find_elements here.
find_elements returns a list of found matches. So, in case of match (such element exists) it will return non-empty list. It will be interpreted by Python as Boolean True. Otherwise, in case of no matches found the returned list is empty, it is interpreted by Python as Boolean False.
To perform click you can get the first element in the returned list, as following:
message = driver.find_elements(By.XPATH, ('//span[contains(text(),"Message")]'))
if message:
message[0].click()
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()
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)
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
I'm trying to wait for the innerHTML element to load. Here is the generic version of my code:
element = WebDriverWait(driver, 120).until(EC.presence_of_element_located((By.XPATH, XPATH)))
element = element.get_attribute('innerHTML')
Element is a tr tag inside of a table. This code is inside of a loop that is supposed to run 25x per page over thousands of ajax pages. After a certain amount of runs, I continue to receive this error:
selenium.common.exceptions.StaleElementReferenceException: Message: {"errorMessage":"Element is no longer attached to the DOM"...
Every time, this error stems from the second line of provided code. This leads me to believe the element is loading, but the innerHTML is not loading quickly enough, and this elicits the given error message. I've tried many ways to get around this without success.
How can I make my code wait for the innerHTML to load after the element's presence has been confirmed?
Good that you are using python, you could write the wait condition like this as well.
innerHTML = WebDriverWait(driver, 5).until(lambda driver: driver.find_element_by_xpath(XPATH).get_attribute("innerHTML"))
or maybe like this
WebDriverWait(driver, 5).until(lambda driver: driver.find_element_by_xpath(XPATH).get_attribute("innerHTML") == "expected text")