Python Selenium WebDriver. Writing my own expected condition
I'm trying to write my own expected condition. What I need... I have an iframe. and I also have an image in it. I want to continue processing when image's scr will change. What I did:
class url_changed_condition(object):
'''
Checks whether url in iframe has changed or not
'''
def __init__(self, urls):
self._current_url, self._new_url = urls
def __call__(self, ignored):
return self._current_url != self._new_url
and later in the code I have:
def process_image(self, locator, current_url):
try:
WebDriverWait(self.driver, 10).until(ec.presence_of_element_located((By.TAG_NAME, u"iframe")))
iframe = self.driver.find_element(*locator)
if iframe:
print "Iframe found!"
self.driver.switch_to_frame(iframe)
WebDriverWait(self.driver, 10).until(ec.presence_of_element_located((By.XPATH, u"//div")))
# WebDriverWait(self.driver, 10).until(
# url_changed_condition(
# (current_url, self.driver.find_element(By.XPATH, u"//a/img").get_attribute(u"src"))))
img_url = self.driver.find_element(By.XPATH, u"//a/img").get_attribute(u"src")
print img_url
self.search_dict[self._search_item].append(img_url)
self.driver.switch_to_default_content()
except NoSuchElementException as NSE:
print "iframe not found! {0}".format(NSE.msg)
except:
print "something went wrong"
import traceback
import sys
type_, value_, trace_ = sys.exc_info()
print type_, value_
print traceback.format_tb(trace_)
finally:
return current_url
This code works, but returns the same url multiple times. The problem is when I uncomment url_changed_condition
it falls with TimeoutException
in(current_url, self.driver.find_element(By.XPATH, u"//a/img").get_attribute(u"src"))
The line below it works fine... I don't get it.
Solution 1:
Looks like this topic misses an example of a Custom Expected Condition.
It is actually pretty easy. First of all, what is an Expected Condition in Python selenium bindings:
- it is a new-style
class
(based onobject
) - it has
__call__()
magic method defined which returns a boolean
There is a big set of built-in expected condition classes.
Let's work through example. Let's say we want to wait until an element's text will start with a desired text:
from selenium.webdriver.support import expected_conditions as EC
class wait_for_text_to_start_with(object):
def __init__(self, locator, text_):
self.locator = locator
self.text = text_
def __call__(self, driver):
try:
element_text = EC._find_element(driver, self.locator).text
return element_text.startswith(self.text)
except StaleElementReferenceException:
return False
Usage:
WebDriverWait(driver, 10).until(wait_for_text_to_start_with((By.ID, 'myid'), "Hello, World!"))
Solution 2:
Using the technique described by @alecxe , but modified slightly to cope with the expected_conditions which accept an element instead of a locator:
(In this case, since there is no selenium.webdriver.support.expected_conditions.invisibility_of(element)
, I'm waiting for the is_displayed()
method to signal False)
class wait_for_element_to_be_invisible(object):
def __init__(self, element):
self.element = element
def __call__(self, driver):
return not(self.element.is_displayed())
def test_collapsible_blocks_expand_or_collapse(self):
self.browser.get(self.server_url+'/courses/1/')
shadables = self.browser.find_elements_by_class_name('shade')
for shadable in shadables:
## parent, then sibling element (*)
shady_bit = shadable.find_element_by_xpath('../following-sibling::*')
element = WebDriverWait(self.browser, 10).until(
EC.visibility_of(shady_bit))
shadable.click()
element = WebDriverWait(self.browser, 10).until(
self.wait_for_element_to_be_invisible(shady_bit))
The HTML fragment with the relevant bits of the DOM is:
<h4 class="shadable"><span class="shade" title="Show/Hide">↓</span>
<a href="/link/to/uri">Title of Section</a>
</h4>
<article class="abstract">
<p>Descriptive part which is shadable
</p>
</article>`
Solution 3:
According to the documentation:
WebDriverWait by default calls the ExpectedCondition every 500 milliseconds until it returns successfully. A successful return is for ExpectedCondition type is Boolean return true or not null return value for all other ExpectedCondition types.
The fact that you get the same URL multiple times if you comment out the custom wait should give you a hint.
In __call__()
you're always returning False
since the URLs never change. Since you're returning False
, the ExpectedCondition is never met and you get the TimeoutException
.
So either redefine the ExpectedCondition logic or test for a different case.