Wie kann Capybara nach dem Ausführen von JS auf Sichtbarkeit überprüft werden?

81

Nach dem Laden einer Seite habe ich Code, der verschiedene Elemente basierend auf den von einem xhr zurückgegebenen Daten ausführt und ausblendet und anzeigt.

Mein Integrationstest sieht ungefähr so ​​aus:

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

Wenn ich in dem Kontext, in dem dieser Test ausgeführt wird, manuell zur Seite gehe, ist #blah nicht wie erwartet sichtbar. Ich vermute, dass Capybara den Anfangszustand der Seite (in diesem Fall unsichtbar) betrachtet, den Zustand des DOM bewertet und den Test nicht besteht, bevor der JS ausgeführt wird.

Ja, ich habe den :js => trueauf dem enthaltenden Beschreibungsblock gesetzt :)

Irgendwelche Ideen wären sehr dankbar! Ich hoffe, dass ich hier keine absichtliche Verzögerung einlegen muss, die sich schuppig anfühlt und die Dinge verlangsamt.

Kevin Davis
quelle
1
Was ist der Fahrer? Was ist der erwartete HTML-Code nach Abschluss des XHR?
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功

Antworten:

129

Ich denke, dass die findAnweisung hier die mit dem impliziten Warten ist, also wird Capybara warten, bis sich das Element auf der Seite befindet, aber nicht warten, bis es sichtbar wird.

Hier möchten Sie, dass Capybara auf das Erscheinen des sichtbaren Elements wartet. Dies sollte durch Angabe der folgenden visibleOption erreichbar sein:

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

Ich habe es nicht ausprobiert, aber die ignore_hidden_elementsKonfigurationsoption könnte auch hier nützlich sein, wenn Sie findimmer auf sichtbare Elemente warten möchten.

Jon M.
quelle
6
Falls jemand (wie ich) anfänglich die Notiz von @ Kevin verpasst, müssen Sie sie js:trueauf dem enthaltenen Block haben, damit dies funktioniert.
Chris Beck
3
Das ist fantastisch. Es hat mir geholfen zu: expect(page).to have_selector('#flash-message', visible: false, text: "Signed in Successfully")- Danke, dass Sie diese Antwort gepostet haben
Chuck Bergeron
Die sichtbare Option hatte für mich keine Auswirkung, als ich die Sichtbarkeit eines Abfrageelements für die Abfrage testete. Ich musste erwarten (find ('# blah'). Visuell?). Be_falsey verwenden.
TrueinViso
Ich möchte so etwas wie is_visible? dann mach das sonst mach noch was anderes
user1735921
36

Dies ist eine andere Möglichkeit, die für mich vollkommen funktioniert:

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

Besonders für komplexere Funde wie

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

was behaupten würde, dass ein Element versteckt wurde.

Björn Grossmann
quelle
1
Das should_not be_visiblesieht für mich nicht gut aus, wenn Capybara läuft, wait_until { base.visible? }wenn Element # sichtbar ist? aufgerufen werden.
Phương Nguyễn
1
@ phng-nguyn, #wait_until wurde aus neueren Versionen von Capybara entfernt.
Solidcell
3
Dies ist tatsächlich eine bessere Variante als die "page.should have_selector ...", da im Falle eines fehlerhaften Textes das Element tatsächlich explizit unsichtbar ist, während die erwähnte Variante nur den Fehler "Es gab keine Übereinstimmungen" hervorruft , das ist ein bisschen frustrierend.
Installero
Dies ist nicht dasselbe wie die akzeptierte Lösung. Für diesen Code wartet Capybara nur, bis sich das Element im DOM befindet, und überprüft dann sofort die Sichtbarkeit. Wenn Sie erwarten, dass sich die Sichtbarkeit ändert, kann dies den Spezifikationen eine Rennbedingung hinzufügen.
Johncip
1
Weiß jemand, ob .should_not be_visibleCover display: nonevon jQuery festgelegt wurden? Es scheint bei mir nicht zu funktionieren.
Antrikshy
20

Wenn Sie überprüfen möchten, ob sich ein Element auf der Seite befindet, aber nicht sichtbar ist, visible: falsefunktioniert es nicht wie erwartet. Hatte mich ein bisschen verblüfft.

So geht's:

# 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
quelle
1
Sie können nicht should_notmit Capybara verwenden, wegen Ajax
Marc-André Lafortune
@ Marc-AndréLafortune, um Ajax mit Capybara zu testen, benutze Capybara.javascript_driver.
Zubin
1
Mit javascript_drive have_csswird auf Ihre gesamte Timeout-Dauer gewartet, bevor Sie aufgeben, da erwartet wird, dass das Element gefunden wird. ZB muss man should have_no_contentstatt verwenden should_not have_content.
Marc-André Lafortune
1
expect(page).not_to have_selector("#some_element", visible: true)funktionierte gut für mich mit javascript_driver, ohne die gesamte Timeout-Dauer zu warten.
Jason Swett
1
Eigentlich wurde dies seit meinem Kommentar geändert und Sie können (und sollten) should_notjetzt verwenden, sorry!
Marc-André Lafortune
9

Verwenden von:

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

Ich habe eine Rails-Anwendung, in der es einen Link gibt, der beim Klicken eine AJAX-Post-Anfrage senden und eine JS-Antwort zurückgeben soll.

Linkcode:

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

Die JS-Antwort (.js.haml-Datei) sollte das folgende versteckte Div auf der Seite, auf der der Link vorhanden ist, umschalten:

 #notification_status(style='display:none')

Inhalt der js.haml-Datei:

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

Ich habe mein Szenario getestet, in dem eine Benachrichtigung gesendet und dem Benutzer die Benachrichtigungsstatusmeldung mithilfe von Cucumber angezeigt wurde ( Gurkenschienen-Juwel mit integrierter Capybara-Unterstützung ) angezeigt wurde.

Ich habe versucht zu testen, ob das Element mit der ID: notification_status bei erfolgreicher Antwort in meiner Schrittdefinition sichtbar war. Dazu habe ich folgende Anweisungen versucht:

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)

Keiner der oben genannten Punkte hat bei mir funktioniert und meinen Schritt nicht bestanden. Von den oben aufgeführten 5 Snippets sind die letzten 4 mit folgendem Fehler fehlgeschlagen:

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

Das war seltsam, weil die folgende Aussage richtig war:

page.has_selector?('#notification_status')

Und tatsächlich habe ich die Seitenquelle mit überprüft

  print page.html

was sich zeigte

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

was erwartet wurde.

Schließlich fand ich diesen Link Capybara assert Attribute eines Elements , das zeigte sich , wie ein Element des Attributs in roher Weise zu untersuchen.

Auch habe ich in Capybara Dokumentation für sichtbar gefunden? Methode ( http://rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Element#visible%3F-instance_method ) folgende Informationen:

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

So kam ich zu dem Schluss, dass man beim Testen der Sichtbarkeit eines Elements nicht auf Ergebnisse von Capybaras Sichtbarkeit angewiesen ist? Methode bei Verwendung eines CSS-Selektors und Verwendung der im Link vorgeschlagenen Lösung capybara Assert-Attribute eines Elements

Ich habe mir Folgendes ausgedacht:

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

 World(CustomMatchers)

Verwendung:

should_be_visible('#notification_status')
Jignesh Gohel
quelle
8

Was sichtbar bedeutet, ist nicht offensichtlich

Der Fehler kann auf ein Missverständnis dessen zurückzuführen sein, was als sichtbar oder nicht sichtbar angesehen wird, da es nicht offensichtlich, nicht tragbar und unterdokumentiert ist. Einige Tests:

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>

Das einzige, was der Rack-Test als unsichtbar betrachtet, ist Inline display: none(kein internes CSS, da es keine Selektoren ausführt):

!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 hat ein ähnliches Verhalten, kann jedoch mit internen CSS- und Js- style.displayManipulationen umgehen :

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 verhält sich ganz anders: wenn ein leeres Element unsichtbar ist und visibility-hiddensowie 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

Ein weiterer häufiger Haken ist der Standardwert von visible:

  • es war früher false(sieht beides sichtbare als auch unsichtbare Elemente),
  • Derzeit ist true
  • wird durch die Capybara.ignore_hidden_elementsOption gesteuert .

Referenz .

Vollständiger ausführbarer Test auf meinem GitHub .

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
quelle
1
Tolle Antwort - danke für die Mühe, die Sie in diese Sache gesteckt haben. Das Testen der Sichtbarkeit von Elementen in einer Suite mit mehreren Treibern ist nicht trivial.
Andy Triggs
1
"Früher war es falsch (sieht sowohl sichtbare als auch unsichtbare Elemente), ist derzeit wahr und wird von der Option Capybara.ignore_hidden_elements gesteuert." Weitere Informationen finden Sie in diesem Blogbeitrag: elabs.se/blog/60-introducing-capybara-2-1
rizidoro
5

Vielleicht möchten Sie sich diesen Beitrag ansehen , der eine Beispielmethode zum Warten auf den Abschluss aller Ajax-Anforderungen enthält:

def wait_for_ajax(timeout = Capybara.default_wait_time)
  page.wait_until(timeout) do
    page.evaluate_script 'jQuery.active == 0'
  end
end
Kleinigkeit
quelle
5
Diese Lösung ist nicht Zukunft kompatibel, wie wait_untilwurde aus Capybara 2 entfernt .
Parhamr
1
Verwenden SieTimeout.timeout
Ian Vaughan
Bessere VerwendungSelenium::WebDriver::Wait
Nakilon
wenn Sie nur Selen verwenden
Nickolay Kondratenko
2

Die akzeptierte Antwort ist jetzt etwas veraltet, da 'sollte' eine veraltete Syntax ist. Heutzutage ist es besser, wenn Sie etwas in der Art von tunexpect(page).not_to have_css('#blah', visible: :hidden)

Alex Foxleigh
quelle
1

Die anderen Antworten hier sind der beste Weg, um auf das Element zu "warten". Ich habe jedoch festgestellt, dass dies für die Site, an der ich arbeite, nicht funktioniert hat. Grundsätzlich war das Element, auf das geklickt werden musste, sichtbar, bevor die dahinter stehende Funktion vollständig geladen wurde. Dies ist in Bruchteilen von Sekunden, aber ich fand, dass mein Test gelegentlich so schnell lief, dass er auf die Schaltfläche klickte und nichts passierte. Ich habe es geschafft, das zu umgehen, indem ich diesen booleschen Make-Shift-Ausdruck gemacht habe:

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

Grundsätzlich wird die Standardwartezeit von Capybara verwendet, um nach etwas zu suchen, das angezeigt werden soll. Wenn es nicht vorhanden ist, wird Ihr Klick erneut versucht.

Wieder werde ich sagen, dass die should have_selectorMethode zuerst versucht werden sollte, aber wenn es einfach nicht funktioniert, versuchen Sie dies

Dono
quelle