Comment faire en sorte que Capybara vérifie la visibilité après que certains JS aient fonctionné?

Après le chargement d’une page, j’ai un code qui s’exécute et masque et affiche divers éléments en fonction des données renvoyées par un fichier xhr.

Mon test d’intégration ressemble à ceci:

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

Lorsque je vais manuellement à la page dans le contexte où ce test s’exécute, #blah n’est pas 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 mets le :js => true sur le bloc de description contenant 🙂

Toutes les idées seraient grandement appréciées! J’espère que je n’ai pas à mettre un retard intentionnel ici, cela me semble flou et ralentira les choses.

Je pense que la requête find est celle avec l’attente implicite, donc Capybara attendra que l’élément soit sur la page, mais n’attendra pas qu’il devienne visible.

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

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

Je ne l’ai pas essayé, mais l’option de configuration ignore_hidden_elements peut être utile ici aussi, si vous voulez que find attende toujours les éléments visibles.

C’est une autre façon de faire qui fonctionne parfaitement bien pour moi:

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

Surtout pour les trouvailles plus complexes, telles que

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

qui affirmerait qu’un élément a été caché.

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. Me suis-je arrêté un peu?

Voici comment procéder:

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

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 il existe un lien qui, une fois cliqué, doit soumettre une demande de publication AJAX et renvoyer 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 sur la div cachée suivante sur la page du lien:

  #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 ( gemme concombre-rails avec prise en charge intégrée de Capybara )

J’essayais de tester que l’élément ayant id: notification_status était visible lors d’une réponse réussie dans ma définition d’étape. Pour cela, j’ai essayé les déclarations 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 réponses ci-dessus n’a fonctionné pour moi et mon échec a échoué.

 '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 car 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

 

ce qui était attendu

Enfin, j’ai trouvé ce lien capybara assert des atsortingbuts d’un élément qui montrait comment inspecter l’atsortingbut d’un élément de manière brute.

J’ai aussi trouvé dans la documentation de Capybara pour visible? 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 du visible de Capybara? méthode lors de l’utilisation d’un sélecteur CSS et en utilisant la solution suggérée dans le lien capybara assert les atsortingbuts d’un élément

Je suis venu avec les suivants:

  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') 

Quel moyen visible n’est pas évident

L’échec peut provenir d’une mauvaise compréhension de ce qui est considéré comme visible ou non, car il n’est pas évident, n’est pas portable et sous-documenté. Quelques tests:

HTML:

 

La seule chose que Rack test considère comme invisible est l’ display: none ligne display: none (pas de 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 manipulations internes CSS et Js style.display :

 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 

Le selenium se comporte différemment: si considère un élément vide invisible et invisible visibility-hidden ainsi que l’ 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 capture commune est la valeur par défaut de visible :

  • c’était false (voit à la fois les éléments visibles et invisibles),
  • est actuellement true
  • est contrôlé par l’option Capybara.ignore_hidden_elements .

Référence

Test complet sur mon GitHub .

Vous pourriez vouloir regarder ce post , qui donne un exemple de méthode pour attendre que toutes les requêtes 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 

La réponse acceptée est un peu obsolète maintenant que “should” est une syntaxe obsolète. Ces jours-ci vous feriez mieux de faire quelque chose dans le sens de expect(page).not_to have_css('#blah', visible: :hidden)

Les autres réponses sont le meilleur moyen d’attendre l’élément. Cependant, j’ai trouvé que cela ne fonctionnait pas pour le site sur lequel je travaille. Fondamentalement, l’élément qui nécessitait un clic était visible avant la fonction qui était complètement chargée. C’est en quelques fractions de seconde mais j’ai trouvé que mon test avait été si rapide à l’occasion qu’il cliquait sur le bouton et que rien ne s’était passé. J’ai réussi à contourner ce problème en faisant cette expression booléenne:

 if page.has_selector?('') puts ('') else find('', :match == :first).sortinggger('click') end 

Fondamentalement, il utilise le temps 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 la méthode should have_selector devrait être essayée en premier mais si elle ne fonctionne pas, essayez ceci