Sunday, September 22, 2013

Capybara wait for ajax call to finish

TL;DR: Before capybara 2.0.0, wait_until method was available which can be used for wait for ajax call to finish. In capybara 2.0.0, wait_until method is removed as it is not needed and was creating confusion. Capybara automatically waits for elements to appear or disappear on the page. If you do something like find("#foo"), this will block until an element with id “foo” appears on the page, as will has_content?("bar"), click_link("baz") and most other things you can do with Capybara.

Since today morning I was facing very strange issue with capybara feature spec. One of my spec was failing with following error.
spec:
    scenario "can create comment" do
      user =  FactoryGirl.create(:user, :email => "p@abc.com")
      post = FactoryGirl.create(:published_post)

      login(user)

      visit post_path(post)

      page.find('#comment_body').set("This is my comment")
      keypress_script = "var e = $.Event('keypress', { keyCode: 13 }); $('#comment_body').trigger(e);"
      page.execute_script(keypress_script)
   
      page.should have_text("This is my comment")
      page.should have_link('Reply')
      page.should have_link('Edit')
      page.should have_link('Destroy')
    end

       expected to find text "This is my comment" in "BROWSE CATEGORIES FORMATS MY PREGNANCY VOICES SERVICES POST TAGS MyText About Author1 — A brief write up about the author will come here. This will not exceed more than 250 characters. Give us your opinion. Discuss it with people like you. Javier Huel - This is my comment 0 likes | 0 replies LIKE | Reply | Edit | Destroy SIMILAR POSTS about us | our team | our vision | give feedback | privacy policy | terms of use | best viewed in | careers | site map © 1998–2013 ABC, Inc. All rights reserved."
     # ./spec/features/commets_spec.rb:42:in `block (3 levels) in ' 

Now in above error, I am clearly able to see that the text is present in page, but still it is not able to match it.

To quickly understand what is happening, I have used pry. On pry prompt, just verified expectation again and this time it is evaluated true.
[1] pry(#)> page.should have_text("This is my comment")
=> true 

Means, here capybara was not waiting for ajax call to finish.

So I have googled for "capybara + wait for ajax call to finish" and found solution to use "wait_until" method.

When I have tried this method, I got into another error,
Failure/Error: wait_until do
     NoMethodError:
       undefined method `wait_until' for #
     # ./spec/features/commets_spec.rb:41:in `block (3 levels) in '
While googled more, I found strong discussion of Jonas Nicklas and found that wait_until is removed from capybara 2.0.0 ;(.

Alternatively, I found how to implement "wait_until" back into capybara.

But in same discussion Jonas Nicklas has posted a link of his blog post where he has explained about why this decision was reached. Also he has mentioned how we can wait for ajax call to modify DOM.

So, my spec changed to,
    scenario "can create comment" do
      user =  FactoryGirl.create(:user, :email => "p@abc.com")
      post = FactoryGirl.create(:published_post)

      login(user)

      visit post_path(post)

      page.find('#comment_body').set("This is my comment")
      keypress_script = "var e = $.Event('keypress', { keyCode: 13 }); $('#comment_body').trigger(e);"
      page.execute_script(keypress_script)
   
      page.should have_selector(".comment", :text => "This is my comment")
      page.should have_link('Reply')                         # Subsequent call also passed due to have_selector
      page.should have_link('Edit')
      page.should have_link('Destroy')
    end
 
and which is passing. ;)

References :
https://groups.google.com/forum/#!topic/ruby-capybara/qQYWpQb9FzY
http://www.elabs.se/blog/53-why-wait_until-was-removed-from-capybara
https://gist.github.com/KevinTriplett/5087744