In a recent Rails project we had some issues with flaky feature tests where we used Capybara. Some tests would fail every second time or so. When I investigated the problem I found that it was a form that was updated with AJAX when a checkbox was checked. The updated form would have some different content, and the next Capybara action - clicking a button with an updated text would fail if Capybara tried to click the button before the AJAX request and DOM update was completed.
When I knew what was causing the tests to sporadically fail I thought that I would surely find someone else with this problem. Shortly I found the blog post Automatically Wait for AJAX with Capybara. It was a bit old, but I soon found at that it worked...to some degree. The tests with this click button would still fail from time to time.
If we take a closer look at the following method:
def wait_for_ajax Timeout.timeout(Capybara.default_max_wait_time) do loop until finished_all_ajax_requests? end end
If there is an ongoing AJAX request, it will wait up to
Capybara.default_max_wait_time for it to finish and then return. At first glance, it seems right. But what if this method is entered before the AJAX request has fired? Then it will just return without waiting.
To wait for the AJAX request to fire it should work to just reverse the loop above. That is,
loop while finished_all_ajax_requests?. But what if the AJAX request is already finished? Then this loop would just go on forever. I decided to put a timeout of 0.2 seconds, just in case this would happen. I also wrapped the
Timeout.timeout calls in a
My improved version of the method look like this:
def wait_for_ajax # Wait for first request if not already done begin Timeout.timeout(0.2) do loop while finished_all_ajax_requests? end rescue end # Wait for all AJAX requests to complete begin Timeout.timeout(Capybara.default_max_wait_time) do loop until finished_all_ajax_requests? sleep AJAX_COMPLETE_WAIT end rescue end end
If the original solution did not work for you, then I hope that this will help you too getting a more robust test suite, as it did for me!