Как заставить Capybara проверять видимость после запуска JS?

82

После загрузки страницы у меня есть код, который запускается, скрывает и показывает различные элементы на основе данных, возвращаемых xhr.

Мой интеграционный тест выглядит примерно так:

it "should not show the blah" do
    page.find('#blah').visible?.should be_true
end 

Когда я вручную перехожу на страницу в контексте этого теста, #blah не отображается, как я ожидал. Я подозреваю, что Capybara смотрит на начальное состояние страницы (в данном случае невидимо), оценивает состояние DOM и не проходит тест до запуска JS.

Да, я установил :js => trueна содержащий блок описания :)

Благодарим за любую идею! Я надеюсь, что мне не придется делать здесь преднамеренную задержку, это кажется ненадежным и замедлит процесс.

Кевин Дэвис
источник
1
Какой драйвер? Каков ожидаемый HTML-код после завершения XHR?
Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功

Ответы:

132

Я думаю, что find утверждение с неявным ожиданием, поэтому Capybara будет ждать, пока элемент не появится на странице, но не будет ждать, пока он станет видимым.

Здесь вы хотите, чтобы Capybara ожидала появления видимого элемента, что должно быть достижимо, указав visibleпараметр:

expect(page).to have_selector('#blah', visible: true)

Я не пробовал, но ignore_hidden_elementsопция конфигурации может быть полезна и здесь, если вы хотите findвсегда ждать появления видимых элементов.

Джон М
источник
6
в случае, если кто-то изначально пропустил заметку @ Kevin (например, я), вы должны иметь js:trueв содержащем блоке, чтобы это сработало.
Крис Бек
3
Это круто. Это помогло мне добраться до: expect(page).to have_selector('#flash-message', visible: false, text: "Signed in Successfully")- Спасибо за публикацию этого ответа
Чака Бержерона
Опция visible не повлияла на меня при тестировании видимости элемента слайда вниз jquery. Мне пришлось использовать expect (find ('# blah'). Visible?). To be_falsey.
trueinViso
Я хочу что-то вроде is_visible? тогда сделай это еще сделай что-нибудь еще
user1735921
37

Это еще один способ, который мне подходит:

find(:css, "#some_element").should be_visible

Особенно для более сложных находок, таких как

find(:css, "#comment_stream_list li[data-id='#{@id3}']").should_not be_visible

который утверждал бы, что элемент был скрыт.

Бьорн Гроссманн
источник
1
should_not be_visibleНе выглядит хорошо для меня , как Capybara перспектива , wait_until { base.visible? }когда элемент # видимые? вызывается.
Phương Nguyễn
1
@ phng-nguyn, #wait_until был удален из последних версий Capybara.
solidcell
3
На самом деле это лучший вариант, чем "page.should have_selector ...", потому что в случае неудачного текста он будет явно говорить, что элемент фактически невидим, тогда как упомянутый вариант просто вызывает ошибку "не было совпадений" , это немного расстраивает.
installero
Это не то же самое, что принятое решение. Для этого кода Capybara будет только ждать, пока элемент не появится в DOM, а затем немедленно проверит видимость. Если вы ожидаете изменения видимости, это может добавить в спецификации состояние гонки.
johncip 01
1
Кто-нибудь знает, установлены ли .should_not be_visibleобложки с display: noneпомощью jQuery? Похоже, у меня это не работает.
Antrikshy 06
20

Если вы хотите проверить, что элемент находится на странице, но не отображается, visible: falseэто не сработает, как вы могли ожидать. Если бы я был немного в тупике.

Вот как это сделать:

# assert element is present, regardless of visibility
page.should have_css('#some_element', :visible => false)
# assert visible element is not present
page.should have_no_css('#some_element', :visible => true)
Зубин
источник
1
Вы не можете использовать should_notс капибарой из-за аякса
Марк-Андре Лафортюн
@ Marc-AndréLafortune, чтобы протестировать ajax с помощью capybara, используйте Capybara.javascript_driver.
Зубин
1
с javascript_drive, have_cssбудет ждать весь ваш тайм-аут, прежде чем сдаться, потому что он ожидает найти элемент. Например, вы должны использовать should have_no_contentвместо should_not have_content.
Marc-André Lafortune
1
expect(page).not_to have_selector("#some_element", visible: true)отлично работал у меня, используя javascript_driver, не дожидаясь всего таймаута.
Джейсон Светт,
1
На самом деле, после моего комментария это было изменено, и вы можете (и должны) использовать should_notсейчас, извините!
Marc-André Lafortune
9

С помощью:

 Ruby:     ruby 1.9.3dev (2011-09-23 revision 33323) [i686-linux]
 Rails:    3.2.9
 Capybara: 2.0.3

У меня есть приложение Rails, в котором есть ссылка, которая при нажатии должна отправить запрос на отправку AJAX и вернуть ответ JS.

Код ссылки:

 link_to("Send Notification", notification_path(user_id: user_id), remote: true, method: :post)

Ответ JS (файл .js.haml) должен переключить следующий скрытый div на странице, на которую существует ссылка:

 #notification_status(style='display:none')

js.haml содержимое файла:

:plain
  var notificationStatusContainer = $('#notification_status');
  notificationStatusContainer.val("#{@notification_status_msg}");
  notificationStatusContainer.show();

Я тестировал свой сценарий отправки уведомления и отображения сообщения о состоянии уведомления пользователю, используя Cucumber ( драгоценный камень огурцов-рельсов со встроенной поддержкой Capybara. )

Я пытался проверить, что элемент с идентификатором: notification_status был виден при успешном ответе в моем определении шага. Для этого я попробовал следующие утверждения:

page.find('#notification_status').should be_visible
page.should have_selector('#notification_status', visible: true)
page.should have_css('#notification_status', visible: true)
page.find('#notification_status', visible: true)
page.find(:css, 'div#notification_status', visible: true)

Ни одно из вышеперечисленных не сработало для меня и провалило мой шаг. Из перечисленных выше 5 фрагментов последние 4 завершились со следующей ошибкой:

'expected to find css "#notification_status" but there were no matches. Also found "", which matched the selector but not all filters. (Capybara::ExpectationNotMet)'

что было странно, потому что следующий оператор проходил правильно:

page.has_selector?('#notification_status')

И на самом деле я проверил источник страницы, используя

  print page.html

который появился

<div style='' id='notification_status'></div>

чего и ожидалось.

Наконец я нашел эту ссылку capybara assert attributes элемента, которая показала, как проверять атрибут элемента в необработанном виде.

Также я нашел в документации Capybara для видимых? метод ( http://rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Element#visible%3F-instance_method ) следующую информацию:

 Not all drivers support CSS, so the result may be inaccurate.

Таким образом, я пришел к выводу, что при тестировании видимости элемента не полагаться на результаты Capybara's visible? при использовании селектора CSS и использовании решения, предложенного в ссылке capybara assert атрибутов элемента

Я придумал следующее:

 module CustomMatchers
   def should_be_visible(css_selector)
    find(css_selector)['style'].should_not include('display:none', 'display: none')
   end
 end

 World(CustomMatchers)

Применение:

should_be_visible('#notification_status')
Джигнеш Гохель
источник
8

Что значит видимое не очевидно

Сбой может возникнуть из-за неправильного понимания того, что считается видимым, а что нет, поскольку это неочевидно, не переносимо для драйвера и недостаточно документировано. Некоторые тесты:

HTML:

<div id="visible-empty"                                                                   ></div>
<div id="visible-empty-background"      style="width:10px; height:10px; background:black;"></div>
<div id="visible-empty-background-same" style="width:10px; height:10px; background:white;"></div>
<div id="visible-visibility-hidden"     style="visibility:hidden;"                        >a</div>
<div id="visible-display-none"          style="display:none;"                             >a</div>

Единственное, что Rack test считает невидимым, - это встроенный display: none(не внутренний CSS, поскольку он не выполняет селекторов):

!all('#visible-empty',                 visible: true).empty? or raise
!all('#visible-empty-background',      visible: true).empty? or raise
!all('#visible-empty-background-same', visible: true).empty? or raise
!all('#visible-visibiility-hidden',    visible: true).empty? or raise
 all('#visible-display-none',          visible: true).empty? or raise

Полтергейст имеет аналогичное поведение, но он может иметь дело с внутренними style.displayманипуляциями CSS и Js :

Capybara.current_driver = :poltergeist
!all('#visible-empty',                 visible: true).empty? or raise
!all('#visible-empty-background',      visible: true).empty? or raise
!all('#visible-empty-background-same', visible: true).empty? or raise
!all('#visible-visibiility-hidden',    visible: true).empty? or raise
 all('#visible-display-none',          visible: true).empty? or raise

Селен ведет себя иначе: если считает пустой элемент невидимым, visibility-hiddenа также display: none:

Capybara.current_driver = :selenium
 all('#visible-empty',                 visible: true).empty? or raise
!all('#visible-empty-background',      visible: true).empty? or raise
!all('#visible-empty-background-same', visible: true).empty? or raise
 all('#visible-visibiility-hidden',    visible: true).empty? or raise
 all('#visible-display-none',          visible: true).empty? or raise

Еще одна распространенная проблема - это значение по умолчанию visible:

  • раньше было false(видит как видимые, так и невидимые элементы),
  • в настоящее время true
  • контролируется Capybara.ignore_hidden_elementsопцией.

Ссылка .

Полный рабочий тест на моем GitHub .

Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
источник
1
Отличный ответ - спасибо за приложенные вами усилия. Проверить видимость элементов в наборе с использованием нескольких драйверов нетривиально.
Энди Триггс,
1
"раньше он был ложным (видит и видимые, и невидимые элементы), в настоящее время истинно и управляется параметром Capybara.ignore_hidden_elements." См. Это сообщение в блоге для получения дополнительной информации: elabs.se/blog/60-introduction-capybara-2-1
rizidoro
5

Возможно, вы захотите посмотреть этот пост , который дает образец метода ожидания завершения всех запросов ajax:

def wait_for_ajax(timeout = Capybara.default_wait_time)
  page.wait_until(timeout) do
    page.evaluate_script 'jQuery.active == 0'
  end
end
smallsense
источник
5
Это решение несовместимо с будущим, поскольку wait_untilбыло удалено из Capybara 2 .
parhamr
1
UseTimeout.timeout
Ian Vaughan
Лучшее использованиеSelenium::WebDriver::Wait
Накилон 07
если вы используете только селен
Николай Кондратенко
2

Принятый ответ теперь немного устарел, поскольку синтаксис «следует» устарел. В наши дни вам лучше сделать что-нибудь вродеexpect(page).not_to have_css('#blah', visible: :hidden)

Алекс Фоксли
источник
1

Другие ответы здесь - лучший способ «дождаться» элемента. Однако я обнаружил, что это не работает для сайта, над которым я работаю. В основном элемент, на который нужно было щелкнуть, был виден до полной загрузки функции, стоящей за ним. Это доли секунды, но я обнаружил, что мой тест иногда запускался так быстро, что нажимал кнопку, и ничего не происходило. Мне удалось обойти это, выполнив это импровизированное логическое выражение:

if page.has_selector?('<css-that-appears-after-click>')
  puts ('<Some-message-you-want-printed-in-the-output>')
else
  find('<css-for-the-button-to-click-again>', :match == :first).trigger('click')
end

В основном он использует время ожидания по умолчанию для капибары, чтобы найти то, что должно появиться, если его нет, он будет повторять ваш щелчок.

Я снова скажу, что should have_selectorсначала следует попробовать этот метод, но если он просто не работает, попробуйте это

Доно
источник