Jak sprawić, by Kapibara sprawdzała widoczność po uruchomieniu jakiegoś JS?

82

Po załadowaniu strony mam kod, który działa, ukrywa i pokazuje różne elementy na podstawie danych zwróconych przez xhr.

Mój test integracji wygląda mniej więcej tak:

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

Kiedy ręcznie przechodzę do strony w kontekście tego testu, #blah nie jest widoczny zgodnie z oczekiwaniami. Podejrzewam, że Kapibara patrzy na początkowy stan strony (w tym przypadku niewidoczny), oceniając stan DOM i nie zdając testu przed uruchomieniem JS.

Tak, ustawiłem :js => truena zawierający opis bloku :)

Wszelkie pomysły będą mile widziane! Mam nadzieję, że nie muszę tu wstawiać celowego opóźnienia, które wydaje się niestabilne i spowolni.

Kevin Davis
źródło
1
Jaki jest kierowca? Jaki jest oczekiwany kod HTML po zakończeniu XHR?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Odpowiedzi:

132

Myślę, że findtutaj stwierdzenie jest z ukrytym czekaniem, więc Kapibara będzie czekać, aż element znajdzie się na stronie, ale nie będzie czekać, aż stanie się widoczny.

Tutaj chciałbyś, aby Kapibara czekała na pojawienie się widocznego elementu, co powinno być osiągalne, określając visibleopcję:

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

Nie próbowałem tego, ale ignore_hidden_elementsopcja konfiguracji może się tu również przydać, jeśli chcesz findzawsze czekać na widoczne elementy.

Jon M
źródło
6
na wypadek, gdyby ktoś początkowo przegapił notatkę @ Kevina (jak ja), musisz mieć js:trueblok zawierający, aby to zadziałało.
Chris Beck
3
To jest niesamowite. Pomogło mi to w dotarciu do: expect(page).to have_selector('#flash-message', visible: false, text: "Signed in Successfully")- dzięki za wysłanie tej odpowiedzi
Chuck Bergeron
Widoczna opcja nie przyniosła mi żadnego efektu podczas testowania widoczności elementu zsuwanego jquery. Musiałem użyć oczekiwać (find ('# blah'). Visible?). To be_falsey.
trueinViso
Chcę coś takiego jak is_visible? następnie zrób to jeszcze raz zrób coś innego
user1735921
37

Jest to inny sposób, który działa idealnie dla mnie:

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

Szczególnie w przypadku bardziej złożonych znalezisk, takich jak

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

co potwierdzałoby, że element został ukryty.

Björn Grossmann
źródło
1
should_not be_visibleNie wygląda dobrze dla mnie jako metę Kapibara wait_until { base.visible? }kiedy Element # widoczne? przywoływane.
Phương Nguyễn
1
@ phng-nguyn, #wait_until został usunięty z ostatnich wersji Kapibary.
solidcell
3
W rzeczywistości jest to lepszy wariant niż „page.should have_selector ...”, ponieważ w przypadku błędnego tekstu można by powiedzieć, że element jest faktycznie niewidoczny, podczas gdy wspomniany wariant po prostu wywołuje błąd „nie było dopasowań” , to trochę frustrujące.
installero
To nie to samo, co przyjęte rozwiązanie. W przypadku tego kodu Capybara będzie tylko czekać, aż element znajdzie się w DOM, a następnie natychmiast sprawdzi widoczność. Jeśli spodziewasz się zmiany widoczności, może to dodać warunek wyścigu do specyfikacji.
johncip
1
Czy ktoś wie, czy .should_not be_visibleokładki display: noneustawione przez jQuery? Wydaje mi się, że to nie działa.
Antrikshy
20

Jeśli chcesz sprawdzić, czy element znajduje się na stronie, ale nie jest widoczny, visible: falsenie będzie działać zgodnie z oczekiwaniami. Trochę się potknąłem.

Oto jak to zrobić:

# 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)
Zubin
źródło
1
Nie możesz używać should_notz kapibarą, z powodu Ajaxa
Marc-André Lafortune,
@ Marc-AndréLafortune, aby przetestować Ajax z kapibarą, użyj Capybara.javascript_driver.
Zubin
1
z javascript_drive, have_cssbędzie czekał przez cały limit czasu, zanim się poddaje, ponieważ oczekuje, że znajdzie element. Np. Musisz użyć should have_no_contentzamiast should_not have_content.
Marc-André Lafortune
1
expect(page).not_to have_selector("#some_element", visible: true)działało dobrze dla mnie przy użyciu javascript_driver, bez czekania przez cały limit czasu.
Jason Swett
1
Właściwie od czasu mojego komentarza zostało to zmienione i możesz (i powinieneś) użyć should_notteraz, przepraszam!
Marc-André Lafortune
9

Za pomocą:

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

Mam aplikację Rails, w której znajduje się link, który po kliknięciu powinien wysłać żądanie wiadomości AJAX i zwrócić odpowiedź JS.

Kod łącza:

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

Odpowiedź JS (plik .js.haml) powinna przełączać następujące ukryte div na stronie, do której istnieje łącze:

 #notification_status(style='display:none')

Zawartość pliku js.haml:

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

Testowałem mój scenariusz wysyłania powiadomienia i wyświetlania komunikatu o statusie powiadomienia do użytkownika za pomocą Cucumber ( klejnot ogórków z wbudowaną obsługą Kapibary )

Próbowałem sprawdzić, czy element mający id: notification_status był widoczny po pomyślnej odpowiedzi w mojej definicji kroku, w tym celu wypróbowałem następujące stwierdzenia:

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)

Żaden z powyższych nie zadziałał dla mnie i nie wykonał mojego kroku. Z 5 wymienionych powyżej fragmentów ostatnie 4 zakończyły się niepowodzeniem z następującym błędem:

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

co było dziwne, ponieważ następujące stwierdzenie zostało przekazane poprawnie:

page.has_selector?('#notification_status')

I faktycznie sprawdziłem źródło strony używając

  print page.html

który się pojawił

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

co było oczekiwane.

Wreszcie znalazłem ten link kapibara assert atrybuty elementu, który pokazał, jak sprawdzić atrybut elementu w sposób surowy.

Znalazłem też w dokumentacji Kapibary widoczne? metoda ( http://rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Element#visible%3F-instance_method ) następujące informacje:

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

W ten sposób doszedłem do wniosku, że testując widoczność pierwiastka nie opierasz się na wynikach widzialności Kapibary? przy użyciu selektora CSS i rozwiązania sugerowanego w link capybara assert atrybuty elementu

Wymyśliłem:

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

 World(CustomMatchers)

Stosowanie:

should_be_visible('#notification_status')
Jignesh Gohel
źródło
8

Nie jest oczywiste, jakie są widoczne środki

Awaria może wynikać z niezrozumienia tego, co jest uważane za widoczne lub nie, ponieważ jest nieoczywiste, nieprzenośne dla kierowcy i niedostatecznie udokumentowane. Niektóre testy:

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>

Jedyną rzeczą, którą test Rack uważa za niewidoczną, jest inline display: none(nie wewnętrzny CSS, ponieważ nie obsługuje selektorów):

!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

Poltergeist zachowuje się podobnie, ale radzi sobie z wewnętrznymi style.displaymanipulacjami CSS i 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

Selen zachowuje się zupełnie inaczej: jeśli uważa pusty element za niewidoczny, a visibility-hiddentakże 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

Innym częstym haczykiem jest domyślna wartość visible:

  • kiedyś false(widzi zarówno widoczne, jak i niewidoczne elementy),
  • obecnie jest true
  • jest kontrolowany przez Capybara.ignore_hidden_elementsopcję.

Odniesienie .

Pełny test na moim GitHubie .

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
źródło
1
Świetna odpowiedź - dzięki za wysiłek, jaki w to włożyłeś. Testowanie widoczności elementów w pakiecie przy użyciu wielu sterowników jest nietrywialne.
Andy Triggs
1
"Kiedyś było fałszywe (widzi zarówno widoczne, jak i niewidoczne elementy), obecnie jest prawdziwe i jest kontrolowane przez opcję Capybara.ignore_hidden_elements." Zobacz ten post na blogu, aby uzyskać więcej informacji: elabs.se/blog/60-introducing-capybara-2-1
rizidoro
5

Możesz spojrzeć na ten post , który podaje przykładową metodę czekania, aż wszystkie żądania AJAX są kompletne:

def wait_for_ajax(timeout = Capybara.default_wait_time)
  page.wait_until(timeout) do
    page.evaluate_script 'jQuery.active == 0'
  end
end
smallsense
źródło
5
To rozwiązanie nie jest kompatybilne w przyszłości, ponieważ wait_untilzostało usunięte z Kapibary 2 .
parhamr
1
UżyjTimeout.timeout
Ian Vaughan
Lepsze wykorzystanieSelenium::WebDriver::Wait
Nakilon
jeśli używasz tylko selenu
Nickolay Kondratenko
2

Zaakceptowana odpowiedź jest teraz nieco nieaktualna, ponieważ składnia „powinna” jest przestarzała. W dzisiejszych czasach lepiej byłoby zrobić coś podobnego doexpect(page).not_to have_css('#blah', visible: :hidden)

Alex Foxleigh
źródło
1

Pozostałe odpowiedzi tutaj to najlepszy sposób „czekania” na element. Jednak okazało się, że to nie działa w przypadku witryny, nad którą pracuję. Zasadniczo element, który wymagał kliknięcia, był widoczny, zanim funkcja za nim została w pełni załadowana. Odbywa się to w ułamkach sekundy, ale okazjonalnie mój test przebiegł tak szybko, że klikał przycisk i nic się nie stało. Udało mi się to obejść, wykonując to wyrażenie logiczne typu make-shift:

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

Zasadniczo używa domyślnego czasu oczekiwania kapibary, aby wyszukać coś, co powinno się pojawić, jeśli go tam nie ma, spróbuje ponownie kliknąć.

Ponownie powiem, że should have_selectornajpierw należy wypróbować metodę, ale jeśli to po prostu nie zadziała, spróbuj tego

Dono
źródło