Rahul
Rahul

Reputation: 321

How to wait in Capybara when using #all

I am aware that wait_until was removed from Capybara for some reason. Capybara being good at waiting, it waits fine when using find, but when dealing with AJAX applications, there are times when I have to use the all method. For instance, I am dealing with a HTML table that I can sort by clicking column names. When I apply sort on a column name and then call all(td, text: column_name) it returns the values that were present before the sort as the all method doesn't wait like the find method does. I am currently using a bespoke helper wait_for_ajax to deal with such situations. I don't want to pass the extra :wait argument. Is there a better way to handle it?

Upvotes: 2

Views: 2064

Answers (3)

Weston Ganger
Weston Ganger

Reputation: 6712

This is one of the edge cases where a wait_for_ajax method is actually the correct choice. Heres my implementation which does not use Timeout which Tom suggested can cause errors.

def wait_for_ajax
  start = Time.now.tv_sec
  stop = false
  until stop do
    active = page.evaluate_script('jQuery.active')
    if active == 0
      stop = true
    elsif (Time.now.tv_sec - start) > Capybara.default_max_wait_time
      stop = true
      raise Exception.new("WaitForUrlError: Timed out waiting for url: #{url}")
    end
  end
end

Upvotes: 0

Chints Vadgama
Chints Vadgama

Reputation: 103

I am using this to load all the HTML contents before it triggers the event. You can call this method whenever you want to specify the wait

def wait_for_ajax
  Timeout.timeout(Capybara.default_wait_time) do
    loop do
      active = page.evaluate_script('jQuery.active')
      break if active == 0
    end
  end
end

Upvotes: 1

Thomas Walpole
Thomas Walpole

Reputation: 49870

If you are testing an app then this is relatively easy because you know the test data, so you can just do something like

expect(page).to have_css('table tr:first-child', text 'text first in order when sorted')

which would wait for the first row of the table to have the text that should appear first in the sort order once sorting has occurred.

If you are screen-scraping or testing an app where you somehow don't know the test data then it becomes more difficult. When you sort a column does anything change on the page other than the order of the rows? Is there a sort indicator, sorted column header highlight, etc? If so you may be able to wait for that, assuming it doesn't actually update until the columns are updated (if it updates as soon as it's clicked then this won't work, and is probably actually a bad UI since you can end up with data inconsistencies). For instance, if the column header gets a class of 'sorted' you could do something like

expect(page).to have_css('table th td.sorted', text: 'text of the column header you sorted by')

which would wait for the correct header to indicate it was sorted.

If there really are no changes to anything that indicates sorting is completed then you're out of luck and you've hit one of the very few cases where something like wait_for_ajax should be used.

Note: passing the :wait option wouldn't help you at all in this case. This is because it's the maximum amount of time that will be waited for matching elements to be found, and #all validly matches 0 elements - it would still return immediately (without any of the count options mentioned in another answer, which as you pointed out don't help you here anyway)

Upvotes: 1

Related Questions