Ожидаемые условия селена - можно использовать "или"?

Я использую Selenium 2/WebDriver с API Python, а именно:

from selenium.webdriver.support import expected_conditions as EC

# code that causes an ajax query to be run

WebDriverWait(driver, 10).until( EC.presence_of_element_located( \
    (By.CSS_SELECTOR, "div.some_result")));

Я хочу, чтобы ждать, пока либо результат должен быть возвращен (div.some_result) или "Не найдено" строка. Это возможно? Вид:

WebDriverWait(driver, 10).until( \
    EC.presence_of_element_located( \
         (By.CSS_SELECTOR, "div.some_result")) \
    or 
    EC.presence_of_element_located( \
         (By.CSS_SELECTOR, "div.no_result")) \
);

Я понимаю, что могу сделать это с помощью селектора CSS (div.no_result, div.some_result), но есть ли способ сделать это, используя метод ожидаемых условий Selenium?

Ответ 1

Я сделал это так:

class AnyEc:
    """ Use with WebDriverWait to combine expected_conditions
        in an OR.
    """
    def __init__(self, *args):
        self.ecs = args
    def __call__(self, driver):
        for fn in self.ecs:
            try:
                if fn(driver): return True
            except:
                pass

Тогда назовите это как...

from selenium.webdriver.support import expected_conditions as EC
# ...
WebDriverWait(driver, 10).until( AnyEc(
    EC.presence_of_element_located(
         (By.CSS_SELECTOR, "div.some_result")),
    EC.presence_of_element_located(
         (By.CSS_SELECTOR, "div.no_result")) ))

Очевидно, было бы тривиально также реализовать класс AllEc.

В северном направлении блок try: нечетный. Я был смущен, потому что некоторые EC возвращают true/false, в то время как другие будут генерировать исключения для False. Исключения пойманы WebDriverWait, поэтому моя вещь AnyEc создавала нечетные результаты, потому что первая, которая выбрала исключение, означала, что AnyEc не перешел к следующему тесту.

Ответ 2

Древний вопрос, но,

Рассмотрим, как работает WedDriverWait, на примере, независимом от селена:

def is_even(n):
    return n % 2 == 0

x = 10

WebDriverWait(x, 5).until(is_even)

Это будет ждать до 5 секунд, пока is_even(x) вернет True

теперь WebDriverWait(7, 5).until(is_even) займет 5 секунд, и они вызовут исключение TimeoutException

Оказывается, вы можете вернуть любое не ложное значение и захватить его:

def return_if_even(n):
    if n % 2 == 0:
        return n
    else:
        return False

x = 10
y = WebDriverWait(x, 5).until(return_if_even)
print(y) # >> 10

Теперь рассмотрим, как работают методы EC:

print(By.CSS_SELECTOR) # first note this is only a string
>> 'css selector'

cond = EC.presence_of_element_located( ('css selector', 'div.some_result') )
# this is only a function(*ish), and you can call it right away:

cond(driver)
# if element is in page, returns the element, raise an exception otherwise

Возможно, вы захотите попробовать что-то вроде:

def presence_of_any_element_located(parent, *selectors):
    ecs = []
    for selector in selectors:
        ecs.append(
            EC.presence_of_element_located( ('css selector', selector) )
        )

     # Execute the 'EC' functions agains 'parent'
     ecs = [ec(parent) for ec in ecs]

     return any(ecs)

это БУДЕТ работать, если EC.presence_of_element_located вернул False когда selector не найден в parent EC.presence_of_element_located, но возникает исключение, простой для понимания обходной путь:

def element_in_parent(parent, selector):
    matches = parent.find_elements_by_css_selector(selector)
    if len(matches) == 0:
        return False
    else:
        return matches

def any_element_in_parent(parent, *selectors):
    for selector in selectors:
        matches = element_in_parent(parent, selector)
        # if there is a match, return right away
        if matches:
            return matches
    # If list was exhausted
    return False

# let try 
any_element_in_parent(driver, 'div.some_result', 'div.no_result')
# if found in driver, will return matches, else, return False

# For convenience, let make a version wich takes a tuple containing the arguments (either one works):
cond = lambda args: any_element_in_parent(*args)
cond( (driver, 'div.some_result', 'div.no_result') )
# exactly same result as above

# At last, wait up until 5 seconds for it 
WebDriverWait((driver, 'div.some_result', 'div.no_result'), 5).until(cond)

Моя цель состояла в том, чтобы объяснить, artfulrobot уже дал фрагмент для общего использования реальных методов EC, просто отметьте, что

class A(object):
    def __init__(...): pass
    def __call__(...): pass

Это просто более гибкий способ определения функций (на самом деле "подобный функции", но не имеющий отношения к данному контексту)

Ответ 3

Попробуйте использовать выражение лямбда:

WebDriverWait(driver, 10).until(lambda a:
a.presence_of_element_located(By.CSS_SELECTOR, "div.some_result") OR a.presence_of_element_located(By.CSS_SELECTOR, "div.no_result"))