Как определить кликабельность элемента на странице с помощью WebDriver?

Поддерживаю @joemast.
@romanua, создайте отдельный тред с вашим вопросом.
Возможно, у меня есть решение вашей проблемы, но нужны будут подробности, и лучше обсудить их в отдельном месте и не оффтопить тут.

Я задал вам четыре вопроса, мы мне ответили только на один. :slight_smile:

У меня была похожая проблема.
Сперва я задал себе вопрос: “А как браузер понимает, что элемент надо отобразить именно так, а не как иначе?”. Пару минут ковыряния в коде страницы дало в конечном итоге понять, что элементу присваивается тег @disabled="", который в дальнейшем влияет на его “кликабельность” (такой финт во фреймворке). Возможно, задача автора топика решается также просто.

Почему-то все уведомления в спам легли. Думал тишина здесь.

Это первое, что пришло в голову и мне. Но я подумал может есть другие варианты.

Это ваше предположение или это “необходимо и достаточно”?

А вы попробуйте и напишите. Вам тут уже много чего насоветовали, а вы продолжаете вопрошать. Сами что пробовали? Что не получается? Как ругается? Подробности давайте. В угадайку некогда играть

Увидел ваше предложение и Михаила. Все остальное я так понимаю к моему вопросу отношения не имело
А я разумеется попробую и ваш вариант тоже.
Спасибо.

Других методов проверки на кликабельность, кроме как перечислены выше, нет. Пробуйте что работает для вас и используйте его. А потом если будут какие-то конкретные проблемы, будем думать дальше.

Проблема у меня была в том, что слишком много слипов для ожидания возможности кликнуть по элементам.
И с этим надо было что-то делать.

Связка isDisplayed и isEnabled практически везде прокатила.

Слипы остались только в двух местах:

  1. После определенных действий пользователя сверху на страницу наезжала форма.
    Связка isDisplayed & isEnabled у элемента на этой форме выдает клик как положено без ругани.
    Проблема только в том, что клик похоже происходит еще до того как форма полностью выкатится и вне формы, т.к. форма не выехав до конца убирается обратно (так по сценарию заложено). Короче тут слип я оставил.
  2. При обращении к БД. Необходимо просто дождаться когда до БД дойдут данные.

по п.1 - если форма ajax’овская, то возможно поможет, перед поиском элемента и совершения действий с ним, проверить “return jQuery.active == 0”

2 лайка

Sleep плох только тогда, когда он не оправдан. Увлекаться этим методом не стоит, потому как он сложен в поддержке (например, нужно изменить время ожидания), лучше использовать динамические способы, которые предоставляет инструмент (AjaxElementLocator, WebDriver Wait).

Но бывает, что такое решение оказывается гораздо эффективнее (результат / трудозатраты) и точечное его использование вполне нормально (когда работает стабильно).

Так что в случае с отправкой данных в БД, sleep, на мой взгляд, использовать нормально.

А с выезжающей формой нужно разобраться как определить её окончательную загрузку. Если это Ajax, то @5am вам подсказал возможное решение. Если это связано с JS-анимацией, тогда возможно стоит поискать локатор, который наверняка определяет окончательное положение формы. И после этого уже обращаться к элементу на форме

Все-таки стали попадаться загадочные ситуации, когда isDisplayed & isEnabled выдают таки эксепшн.
Переделал под вариант “кликать пока не кликнется” - все встало на свои места.

В этой области я чайник. Можно пару строчек реального кода, от которого я могу оттолкнуться?
Спасибо

Вот пример на python

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import WebDriverException
 
def ajax_complete(driver):
    try:
        return 0 == driver.execute_script("return jQuery.active")
    except WebDriverException:
        pass
 
def my_automation_test():
    ff_driver = webdriver.Firefox()
 
    ff_driver.get("http://domain.tld")
 
    #wait for ajax items to load
    WebDriverWait(ff_driver, 10).until(
             ajax_complete,  "Timeout waiting for page to load")
 
    assert "ajax loaded string" in ff_driver.page_source

На java тоже не проблема найти c# - Wait for an Ajax call to complete with Selenium 2 WebDriver - Stack Overflow

public void WaitForAjax()
{
    while (true) // Handle timeout somewhere
    {
        var ajaxIsComplete = (bool)(driver as IJavaScriptExecutor).ExecuteScript("return jQuery.active == 0");
        if (ajaxIsComplete)
            break;
        Thread.Sleep(100);
    }
}

Вроде понятно куда двигаться. Спасибо

@polusok а как ты вызываешь метод ajax_complete не указав в аргументах объект driver?

Так это не мой код, я нашел примерочный код в интернете, чтобы @Raynor смог сориентироваться.

Но по поводу твоего вопроса. В методе until в WebDriverWait обычно передается callable object в виде lambda выражения, в данном случае это простая функция, которая будет вызываться. Сам класс WebDriverWait при запуске метода until будет передавать инстанс объекта в callable object как первый аргумент.

Обычно это выглядит где-то так

from selenium.webdriver.support.ui import WebDriverWait
element = WebDriverWait(driver, 10).until(lambda x: x.find_element_by_id("someId"))

Если посмотреть внутри реализации, то все становиться понятным http://selenium.googlecode.com/git/docs/api/py/_modules/selenium/webdriver/support/wait.html#WebDriverWait.until

Я оставил только код, который это объясняет.

class WebDriverWait(object):
    def __init__(self, driver, ...):
        ...
        self._driver = driver
        ...

    def until(self, method, message=''):
        ...
        while(True):
            try:
                value = method(self._driver)
                ...
            except self._ignored_exceptions:
                pass
            ....

@polusok спасибо, понял. В Ruby Wait работает капельку иначе.

require 'selenium-webdriver'

driver = Selenium::WebDriver.for :chrome
wait = Selenium::WebDriver::Wait.new timeout: 10

driver.get 'http://example.com'

wait.until do
  driver.find_element :xpath, '/html/body/div/a' 
end
element = driver.find_element :xpath, '/html/body/div/a' 
element.click

driver.close

Ну ты ведь в until тоже ничего не передаешь, хотя ты просто в одном скрипте все написал без функций и классов и инстанс driver виден из метода until или может быть я конечно не понимаю ruby (что вполне возможно :smile: )

Я написал для примера работающий путём copy/path скрипт.
Т. к. я хотел показать работу wait, то не стал перегружать скрипт ненужными в данном случае классами и методами.
И да, объект driver виден внутри wait.

Ну ок, что ты не хочешь усложнять, но что если инициализация драйвера и реалиализация вейтера в разных функциях, ты что явно передаешь инстанс вебрайвера?

[quote=“polusok, post:33, topic:3719, full:true”]
но что если инициализация драйвера и реалиализация вейтера в разных функциях, ты что явно передаешь инстанс вебрайвера?
[/quote]Не совсем так. driver инициализируется как public переменная и доступна из любого места теста. Я же не просто скрипт пишу, а всё внутри rspec обёртки.
Посмотри мой пример из Проверка правильности сортировки блоков c помощью WebDriver, пример на Ruby