Python Selenium WebDriver. Написание собственного ожидаемого условия

Я пытаюсь написать свое собственное ожидаемое условие. Что мне нужно... У меня есть iframe. и у меня также есть изображение. Я хочу продолжить обработку, когда сглаживание изображения изменится. Что я сделал:

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  

а затем в коде, который у меня есть:

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  

Этот код работает, но возвращает один и тот же URL-адрес несколько раз. Проблема заключается в том, когда я раскомментирую url_changed_condition он падает с TimeoutException в
(current_url, self.driver.find_element(By.XPATH, u"//a/img").get_attribute(u"src"))
Строка под ним отлично работает... Я не понимаю.

Ответ 1

Похоже, в этом разделе отсутствует пример пользовательского ожидаемого условия.

На самом деле это довольно легко. Прежде всего, что такое Ожидаемое условие в привязках селена Python:

Существует большой набор встроенных классов ожидаемых условий.

Пусть работает пример. Скажем, мы хотим подождать, пока текст элемента начнется с нужного текста:

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

Использование:

WebDriverWait(driver, 10).until(wait_for_text_to_start_with((By.ID, 'myid'), "Hello, World!"))

Ответ 2

В соответствии с документацией:

WebDriverWait по умолчанию вызывает ExpectedCondition каждые 500 миллисекунды, пока он не вернется успешно. Успешное возвращение для Тип ExpectedCondition - это Boolean return true или not null return value для всех других типов ExpectedCondition.

Тот факт, что вы получаете один и тот же URL несколько раз, если вы закомментируете пользовательское ожидание, должен дать вам подсказку.

В __call__() вы всегда возвращаете False, так как URL-адреса никогда не меняются. Поскольку вы возвращаете False, ExpectedCondition никогда не выполняется, и вы получаете TimeoutException.

Так что либо переопределите логику ExpectedCondition, либо проверите для другого случая.

Ответ 3

Используя метод, описанный @alecxe, но слегка модифицированный, чтобы справиться с ожидаемыми условиями, которые принимают элемент вместо локатора: (В этом случае, поскольку нет selenium.webdriver.support.expected_conditions.invisibility_of(element), я жду, когда метод is_displayed() будет сигнализировать 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))

HTML-фрагмент с соответствующими битами DOM:

<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>`