| Wenceslao Páez

18 Jan, 2015

Angular, Rails, Capybara, wait for ajax.

When dealing with ajax integrations tests using Capybara and Rails, most of the time we don’t know how long it will take to our backend to respond to that request. In this cases, Capybara is very good about handling ajax, waiting for elements to appear on the page,

click_link("Send an ajax request")

expect(page).to have_content("My expected ajax response message")

Capybara is smart enough to retry finding the content for a brief period of time before giving up and throwing an error. This period of time is defined by

Capybara.default_wait_time

So, to solve your problem you can adjust this period, by 5, by 6, by 7. Only when it reaches the default wait time, Capybara stops finding the content and your test fails. Other approach is setting a sleep, guessing how long it takes your request.

click_link("Send an ajax request")
sleep(1)
expect(page).to have_content("My expected ajax response message")

Wait for ajax!!!

In order to stop gueesing, there is a also a good solution in CODERWALL when you use JQuery for your ajax request.

But what happens if like me, you are using AngularJS in your frontend app, making the requests with the $http service. In this case you have to find a way to figure out if there are pending request with AngularJS. Here is a solution:

# spec/support/wait_for_ajax.rb

module WaitForAjax
  def wait_for_ajax(max_wait_time = 30)
    timeout.timeout(max_wait_time) do
      while pending_ajax_requests?
        sleep 0.1
      end
    end
  end

  def pending_ajax_requests?
    page.evaluate_script("angular.element(document.body).injector().get('$http').pendingrequests.length") > 0
  end
end

RSpec.configure do |config|
  config.include WaitForAjax
end

Usage

Just add this method in the place you want to make sure an ajax request has finished.

click_link("Send an ajax request")

wait_for_ajax #wait for ajax response

expect(page).to have_content("My expected ajax response message")

Poltergeist solution

In case you are using Poltergeist as your Capybara driver, you can inspect network traffic, helping know if there are pending response parts:

# spec/support/wait_for_ajax.rb

def pending_ajax_requests?
  page.driver.network_traffic.collect(&:response_parts).any?(&:empty?)
end

You can use this approach regardless of your JS frontend framework.