web-dev-qa-db-fra.com

Comment faire en sorte que Capybara vérifie la visibilité après l'exécution de JS?

Après avoir chargé une page, j'ai un code qui s'exécute, qui se cache et affiche divers éléments en fonction des données renvoyées par une xhr.

Mon test d'intégration ressemble à ceci:

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

Quand je vais manuellement à la page dans le contexte où ce test est exécuté, #blah est not visible comme prévu. Je soupçonne que Capybara examine l'état initial de la page (invisible dans ce cas), évalue l'état du DOM et échoue au test avant l'exécution du JS.

Oui, je règle le :js => true sur le bloc décrit :)

Toutes les idées seraient grandement appréciées! J'espère ne pas avoir à mettre un délai intentionnel ici, cela semble floconneux et va ralentir les choses.

78
Kevin Davis

Je pense que la déclaration find est celle avec l'attente implicite. Capybara attendra donc que l'élément se trouve sur la page, mais n'attendra pas qu'il soit visible.

Ici, vous voudriez que Capybara attende que l’élément visible apparaisse, ce qui devrait être réalisable en spécifiant l’option visible:

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

Je ne l'ai pas essayée, mais l'option de configuration ignore_hidden_elements Pourrait également être utile si vous vouliez que find attende toujours les éléments visibles.

125
Jon M

C'est une autre façon de le faire qui fonctionne parfaitement pour moi:

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

Surtout pour les découvertes plus complexes, telles que

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

ce qui affirmerait qu'un élément a été caché.

35
Björn Grossmann

Si vous voulez vérifier qu'un élément est sur la page mais n'est pas visible, visible: false _ ne fonctionnera pas comme prévu. M'avait laissé perplexe un peu.

Voici comment le faire:

# 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)
20
Zubin

En utilisant:

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

J'ai une application Rails dans laquelle se trouve un lien qui, une fois cliqué, doit soumettre une demande AJAX et envoyer une réponse JS.

Code de lien:

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

La réponse JS (fichier .js.haml) doit basculer le div caché suivant sur la page où le lien existe:

 #notification_status(style='display:none')

contenu du fichier js.haml:

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

Je testais mon scénario d'envoi de notification et d'affichage du message d'état de notification à l'utilisateur à l'aide de Cucumber ( joyau de concombre-rails avec le support Capybara intégré)

J'essayais de tester que l'élément ayant l'id: notification_status était visible après une réponse positive dans ma définition d'étape. Pour cela, j'ai essayé les instructions suivantes:

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)

Aucune des actions ci-dessus n'a fonctionné pour moi et a échoué. Les cinq derniers extraits énumérés ci-dessus ont échoué avec l'erreur suivante:

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

ce qui était étrange parce que la déclaration suivante passait correctement:

page.has_selector?('#notification_status')

Et en fait, j'ai inspecté la source de la page en utilisant

  print page.html

qui est apparu

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

ce qui était attendu.

Enfin, j'ai trouvé ce lien capybara affirme les attributs d'un élément qui a montré comment inspecter l'attribut d'un élément de manière brute.

Aussi, j'ai trouvé dans la documentation Capybara des éléments visibles? méthode ( http://rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Element#visible%3F-instance_method ) informations suivantes:

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

Je suis donc parvenu à la conclusion que lors du test de la visibilité d'un élément, ne vous fiez pas aux résultats de la visibilité de Capybara? méthode en utilisant un sélecteur CSS et en utilisant la solution suggérée dans le lien capybara assert les attributs d'un élément

Je suis venu avec ce qui suit:

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

 World(CustomMatchers)

Usage:

should_be_visible('#notification_status')
9
Jignesh Gohel

Ce que signifie visible n'est pas évident

L'échec peut provenir d'une incompréhension de ce qui est considéré visible ou non, car ce n'est pas évident, il n'est pas portable et il est sous-documenté. Quelques 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>

La seule chose que le test de rack considère comme invisible est inline display: none _ (pas le CSS interne car il ne fait pas de sélecteurs):

!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 a un comportement similaire, mais il peut gérer les CSS internes et Js style.display _ manipulation:

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

Sélénium se comporte très différemment: si considère un élément vide invisible et visibility-hidden aussi bien que 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

Une autre erreur courante est la valeur par défaut de visible:

  • c'était false (voit les deux éléments visibles et invisibles),
  • est actuellement true
  • est contrôlé par le Capybara.ignore_hidden_elements option.

référence .

Test complet exécutable sur mon GitHub .

Vous voudrez peut-être regarder this post , qui donne un exemple de méthode pour attendre que toutes les demandes ajax soient terminées:

def wait_for_ajax(timeout = Capybara.default_wait_time)
  page.wait_until(timeout) do
    page.evaluate_script 'jQuery.active == 0'
  end
end
5
smallsense

La réponse acceptée est un peu obsolète à présent car "devrait" est une syntaxe déconseillée. De nos jours, vous feriez mieux de faire quelque chose comme expect(page).not_to have_css('#blah', visible: :hidden)

2
Alex Foxleigh

Les autres réponses ici sont la meilleure façon "d'attendre" l'élément. Cependant, j'ai constaté que cela ne fonctionnait pas pour le site sur lequel je travaille. En gros, l'élément sur lequel il fallait cliquer était visible avant que la fonction derrière ne soit entièrement chargée. C’est en quelques fractions de seconde, mais j’ai trouvé que mon test était tellement rapide qu’il a cliqué sur le bouton et qu’il ne s’est rien passé. J'ai réussi à contourner ce problème en faisant cette expression booléenne 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

Fondamentalement, il utilise le délai d’attente par défaut de capybara pour rechercher quelque chose qui devrait apparaître. Si ce n’est pas le cas, il réessayera votre clic.

Encore une fois, je dirai que le should have_selector la méthode devrait être essayée en premier mais si ça ne marche pas, essayez ceci

1
Dono