Python Selenium WebDriver. Writing my own expected condition
Asked Answered
W

3

22

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.

Wooden answered 15/10, 2013 at 9:16 Comment(0)
A
22

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:

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!"))
Achaea answered 31/3, 2015 at 20:19 Comment(2)
For people coming in Selenium 4, change EC._find_element ... to: element_text = driver.find_element(*self.locator).textMccully
what is the object parameter, can some one describe it and do i need import it?Publicness
F
4

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>`
Forsooth answered 9/5, 2015 at 21:36 Comment(0)
M
1

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.

Mesothorax answered 29/10, 2013 at 14:5 Comment(3)
you are telling me exactly what I've known. Your answer is exactly what I've coded here. I need self.driver.find_element(By.XPATH, u"//a/img").get_attribute(u"src") to be called every 500 milliseconds to compare it's return value with one I've already got. And when they are not equal my __call__() will return True.Wooden
I was just explaining why you got the TimeoutException, since in your question you appeared to not understand why. Like I said, your problem is that you're either checking for the wrong element (since the URL apparently never changes), or the timeout period is too low. Try increasing it to more than 10 seconds. Another option would be to check for another case in the iframe. I'm not familiar with the site you're testing, so can't help you further.Mesothorax
I got here because "A successful return is for ExpectedCondition type is Boolean return true or not null return value for all other ExpectedCondition types" Is horribly written and makes absolutely no sense....Clairvoyant

© 2022 - 2024 — McMap. All rights reserved.