Go back to the main page

Never use wait_until again with Capybara

This article is over 2 years old. Proceed with caution.

Regards ♨ – Minimul

 

Problem

This is not new news, Jonas Nicklas, Capybara's chief, has been saying over and over not to use wait_until and has removed it from Capy 2.0. Here is a comment from Jonas that gives a little insight onto why he removed wait_until.

Your Ajax requests are probably going to have some kind of impact on the DOM, and Capybara can wait for that to finish just fine.

Many times it is not the case that your ajax responses will impact the DOM. Or if even if it does change the DOM, like doing the "yellow fade" technique, it can be hard to determine how the DOM was effected exactly. See my post on how to detect how to detect jqueryui's highlight effect within Capybara.

Solution

Therefore, in situations where an ajax response is not modifying the DOM or scenarios where it is not clear how the DOM is being altered after a ajax repsonse, define for yourself a simple non-intrusive way of demarcating the DOM.

First, specify a global utility method called Util.flashClass().


class @Util
  # Adds and removes a class name after a second to element or body if no element given
  # Intended to be used with headless testing as Capybara properly waits when detecting
  # for the appearance of changed content
  @flashClass: (name, duration = 1000, element = null) ->
    element ||= $(document.body)
    element.addClass(name)
    setTimeout (-> element.removeClass(name)), duration

Next, sprinkle its usage within ajax responses like below. Note that in the code below I am removing a row from the DOM so I could detect that in Capybara using something like page.should have_selector('.line-item', :count => 2) but even then I prefer the simplicity of the "flashClass" technique.


# ... omitted

$('#accounting').on 'ajax:success', 'a[data-method="delete"]', ->
  Util.flashClass 'line-removed'
  $(this).closest('.line-item').remove()


# ... omitted

$.ajax
  type: "PUT",
  data:
    no_redirect: true
  success: (result) ->
    Util.flashClass 'line-added'
 

Lastly, in you specs simply do this to detect.



it "updates via ajax", :js do
  # .....
  click_on 'Update'
  page.should have_css('.line-added')
  # .....
end
 

Explanation

What we are doing is playing nicely with Capybara's detection by using the Util.flashClass() to harmlessly add a class name of your choosing to the body tag and then automatically removing it in one second. You don't need to use this technique much because the majority of the time the DOM is being impacted in an obvious and easy to test manner. However, by applying this pattern where needed you will never have to use wait_until again in Capybara 1.x projects and will help you greatly when leveraging Capybara 2.x and beyond.

Comment on this article?