Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Capybara integration testing with asynchronous JavaScript

I have a Rails integration test that's failing, and I can't figure out why. I'm using Capybara with Selenium as the driver.

The test checks that page content has been removed after an AJAX call takes place. The relevant action is that a button is clicked, and that button click causes a section of the page to be removed via a jQuery remove() call. Here's an approximation of the integration testing code:

click_button("Remove stuff")
assert has_no_link?("This should be removed")

The assertion fails, implying that the link still exists.

I've been reading up on Capybara, and I know that you can extend the default wait time. I've extended it to a ridiculous value (20 seconds), and still the assertion fails.

When I follow the test process myself manually, the source of the page still shows the content, but the DOM does not (by viewing Firefox's DOM Inspector and looking for the element). Is this the issue? I've even tried inspecting the DOM while the tests are running in Firefox to check if the content was there, and it doesn't appear to be.

I have no idea how Capybara is still finding this link that no longer exists in the DOM. Is Capybara examining the source instead of the DOM and finding the link there? If so, I have no idea how to fix this test to make sure that the test passes. Refreshing the page would fix the issue, but that's not exactly what a user would do, so I hesitate to change the page just to make the test pass...

Would love any recommendations on how to approach this problem.

Thanks!

like image 935
aardvarkk Avatar asked Sep 23 '12 20:09

aardvarkk


2 Answers

Thoughtbot has a great blog post on waiting for AJAX, which you can read here, though it is based on Rspec, and it looks like you are using TestUnit.

It works great for situations when Capybara doesn't quite wait long enough, but doesn't add unnecessarily long timeouts. I work mostly in Rspec now, but I think you can modify it by doing this:

# Create this file in test/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

You can either include it when needed in the individual test file, or use one of the strategies provided in this SO post for automatically including it every time.

Then, whenever you have a test that is not properly waiting for AJAX to finish, just insert the line wait_for_ajax. Using your code as an example:

click_button("Remove stuff")
wait_for_ajax
assert has_no_link?("This should be removed")
like image 100
Sia Avatar answered Oct 15 '22 05:10

Sia


there was some method called wait_until, but it was deprecated recently and changed with synchronize method.

http://www.elabs.se/blog/53-why-wait_until-was-removed-from-capybara

https://github.com/jnicklas/capybara/blob/master/lib/capybara/node/base.rb#L44

For now I don't know how to use it exactly, but I'm waiting for answer for my question from the author, so I hope to resolve this problem soonly

like image 1
Dmitriy Konovalov Avatar answered Oct 15 '22 06:10

Dmitriy Konovalov