---
title: Automatically Wait for AJAX with Capybara
teaser: How to use Capybara's `wait_for_ajax` helper.
tags: web,rails,testing
author: Gabe Berke-Williams
published_on: 2014-02-04
---

Capybara's very good about [waiting for AJAX][waiting]. For example, this code
will keep checking the page for the element for `Capybara.default_max_wait_time`
seconds, allowing AJAX calls to finish:

```ruby
expect(page).to have_css('.username', text: 'Gabe B-W')
```

But there are times when that's not enough. For example, in this code:

```ruby
visit users_path
click_link 'Add Gabe as friend via AJAX'
reload_page
expect(page).to have_css('.favorite', text: 'Gabe')
```

We have a race condition between `click_link` and `reload_page`. Sometimes the
AJAX call will go through before Capybara reloads the page, and sometimes it
won't.  This kind of nondeterministic test can be very difficult to debug, so I
added a little helper.

## Capybara's Little Helper

Here's the helper, via [Coderwall][coderwall]:

```ruby
# spec/support/wait_for_ajax.rb
module WaitForAjax
  def wait_for_ajax
    Timeout.timeout(Capybara.default_max_wait_time) do
      loop until finished_all_ajax_requests?
    end
  end

  def finished_all_ajax_requests?
    page.evaluate_script('jQuery.active').zero?
  end
end

RSpec.configure do |config|
  config.include WaitForAjax, type: :feature
end
```

We automatically include every file in `spec/support/**/*.rb` in our
`spec_helper.rb`, so this file is automatically `require`d. Since only feature
specs can interact with the page via JavaScript, I've scoped the `wait_for_ajax`
method to feature specs using the `type: :feature` option.

The helper uses the `jQuery.active` variable, which tracks the number of
active AJAX requests. When it's 0, there are no active AJAX requests, meaning
all of the requests have completed.

## Usage

Here's how I use it:

```ruby
visit users_path
click_link 'Add Gabe as friend via AJAX'
wait_for_ajax # This is new!
reload_page
expect(page).to have_css('.favorite', text: 'Gabe')
```

Now there's no race condition: Capybara will wait for the AJAX friend request to
complete before reloading the page.

## Change we can believe in (and see)

This solution can hide a bad user experience. We're not making any DOM changes
on AJAX success, meaning Capybara can't automatically detect when the AJAX
completes. If Capybara can't see it, neither can our users. Depending on your
application, this might be OK.

One solution might be to have an AJAX spinner in a standard location that gets
shown when AJAX requests start and hidden when AJAX requests complete. To do
this globally in jQuery:

```javascript
jQuery.ajaxSetup({
  beforeSend: function(xhr) {
    $('#spinner').show();
  },
  // runs after AJAX requests complete, successfully or not
  complete: function(xhr, status){
    $('#spinner').hide();
  }
});
```

## What's next

There is no official documentation on `jQuery.active`, since it's an internal
variable, but [this Stack Overflow answer][SO] is helpful. To see how we require
all files in `spec/support`, read through [our spec_helper
template][suspenders].

## Credits

Thanks to [Jorge Dias][jorge] and [Ancor Cruz][ancor] on Coderwall for the
original and refactored helper implementations.

[waiting]: https://github.com/jnicklas/capybara#asynchronous-javascript-ajax-and-friends
[coderwall]: https://coderwall.com/p/aklybw
[SO]: http://stackoverflow.com/questions/3148225/jquery-active-function
[suspenders]: https://github.com/thoughtbot/suspenders/blob/master/templates/spec_helper.rb#L11
[jorge]: https://coderwall.com/diasjorge
[ancor]: https://coderwall.com/ancorcruz
