Как я могу заставить Selenium Web Driver ждать появления элемента, а не просто присутствовать?
Я пишу тесты для веб-приложения. Некоторые команды вытягивают диалоговые окна, у которых есть элементы управления, которые видны, но недоступны в течение нескольких мгновений. (Они выделены серым цветом, но webdriver все еще видит их видимыми).
Как я могу сказать, что Selenium ожидает, что элемент будет фактически доступен, а не только видимым?
try:
print "about to look for element"
element = WebDriverWait(driver, 10).until(lambda driver : driver.find_element_by_id("createFolderCreateBtn"))
print "still looking?"
finally: print 'yowp'
Вот код, который я пробовал, но он "видит" кнопку, прежде чем она будет использоваться, и в основном заряжается прямо после предполагаемого "ожидания".
Обратите внимание, что вместо этого я могу записать десять секунд в код вместо этого, и код будет работать правильно, но это уродливо, ненадежно и неэффективно. Но это доказывает, что проблема в том, что команда "click" бежит впереди доступности элементов управления.
Ответы
Ответ 1
print time.time()
try:
print "about to look for element"
def find(driver):
e = driver.find_element_by_id("createFolderCreateBtn")
if (e.get_attribute("disabled")=='true'):
return False
return e
element = WebDriverWait(driver, 10).until(find)
print "still looking?"
finally: print 'yowp'
print "ok, left the loop"
print time.time()
Вот что мы закончили. (Спасибо lukeis и RossPatterson.) Обратите внимание, что нам нужно было найти все элементы по id, а затем отфильтровать "отключено". Я бы предпочел один шаблон поиска, но что вы можете сделать?
Ответ 2
Я предполагаю, что временная шкала событий будет выглядеть следующим образом:
- на странице нет необходимых элементов.
- нужный элемент появляется, но отключен:
<input type="button" id="createFolderCreateBtn" disabled="disabled" />
- необходимый элемент становится включенным:
<input type="button" id="createFolderCreateBtn" />
В настоящее время вы ищете элемент по id, и вы найдете его на шаге 2, который раньше, чем вам нужно. Что вам нужно сделать, это поиск по xpath:
//input[@id="createFolderCreateBtn" and not(@disabled)]
Здесь разница:
from lxml import etree
html = """
<input type="button" id="createFolderCreateBtn" disabled="disabled" />
<input type="button" id="createFolderCreateBtn" />
"""
tree = etree.fromstring(html, parser=etree.HTMLParser())
tree.xpath('//input[@id="createFolderCreateBtn"]')
# returns both elements:
# [<Element input at 102a73680>, <Element input at 102a73578>]
tree.xpath('//input[@id="createFolderCreateBtn" and not(@disabled)]')
# returns single element:
# [<Element input at 102a73578>]
Чтобы обернуть его, вот ваш фиксированный код:
try:
print "about to look for element"
element_xpath = '//input[@id="createFolderCreateBtn" and not(@disabled)]'
element = WebDriverWait(driver, 10).until(
lambda driver : driver.find_element_by_xpath(element_xpath)
)
print "still looking?"
finally:
print 'yowp'
UPDATE:
Растут то же самое с фактическим webdriver.
Здесь example.html
код страницы:
<input type="button" id="createFolderCreateBtn" disabled="disabled" />
<input type="button" id="createFolderCreateBtn" />
Здесь сеанс ipython:
In [1]: from selenium.webdriver import Firefox
In [2]: browser = Firefox()
In [3]: browser.get('file:///tmp/example.html')
In [4]: browser.find_elements_by_xpath('//input[@id="createFolderCreateBtn"]')
Out[4]:
[<selenium.webdriver.remote.webelement.WebElement at 0x103f75110>,
<selenium.webdriver.remote.webelement.WebElement at 0x103f75150>]
In [5]: browser.find_elements_by_xpath('//input[@id="createFolderCreateBtn" and not(@disabled)]')
Out[5]:
[<selenium.webdriver.remote.webelement.WebElement at 0x103f75290>]
ОБНОВЛЕНИЕ 2:
Он также работает с этим:
<input type="button" id="createFolderCreateBtn" disabled />
Ответ 3
Здесь уже есть отличные ответы, но я подумал, что добавлю свое решение. Явное ожидание и т.д. - отличные функции для использования при тестировании с селеном. Однако явное ожидание просто выполняет функцию Thread.Sleep()
, которую вы можете установить только один раз. Функция ниже - это то, что я использовал для "Shave off" через несколько минут. Он ждет, пока элемент "доступен".
//ALTERNATIVE FOR THREAD.SLEEP
public static class Wait
{
//public static void wait(this IWebDriver driver, List<IWebElement> IWebElementLIst)
public static void wait(this IWebDriver driver, By bylocator)
{
bool elementPresent = IsPresent.isPresent(driver, bylocator);
while (elementPresent != true)
{
Thread.Sleep(1000);
elementPresent = IsPresent.isPresent(driver, bylocator);
}
}
}
Это в С#, но адаптировать его было бы не так сложно. Надеюсь это поможет.