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

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

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

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.

Here’s the helper, via Coderwall:

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

Here’s how I use it:

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.

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:

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

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

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