Reputation: 321
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
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
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
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