In fixing an escaping bug in a Twitter Typeahead search component we have, I put in some capybara tests. I wanted to not only assert the presence of the search box, but also input text and assert the contents of the suggestions (dropdown list).
The trick (at least with Poltergeist / PhantomJS) is to ensure you set focus on the textarea when inserting the desired text value
page.driver.execute_script %Q{ jQuery('#global_search').focus().typeahead('val', 'ABC Co') }
Another gotcha was that our simple wait_for_ajax
(which is a helper that does exactly that) was not sufficient in this case, because we also had some debouncing logic on the text field. So, I ended up putting a really short sleep in, followed by waiting for ajax to complete. That did the trick to ensure the suggestions had been populated in the DOM before going to the next part of the test.
def pause_for_auto_complete_dropdown
sleep(0.5)
wait_for_ajax
end
## in a helper
def wait_for_ajax
Timeout.timeout(Capybara.default_wait_time) do
loop until finished_all_ajax_requests?
end
end
Putting it all together, a simple test to enter a search and assert the hint text auto completes is:
within('#header') do
within('.global-search') do
enter_text_in_global_search('ABC Co')
pause_for_auto_complete_dropdown
assert_equal 'ABC Company', find('.tt-hint').value
end
end
And here is one slightly more complicated that also ensured the suggestion was correct and had the right HTML escaping
within('#header') do
within('.global-search') do
enter_text_in_global_search('XYZ <b')
pause_for_auto_complete_dropdown
# leave the html entities because this is inside a text input
assert_equal "XYZ <b>bold</b>", find(".tt-hint").value
# in the dropdown the html needs to be escaped (by getting the 'text' value and seeing <b>bold</b> that proves it was escaped in the DOM)
# if the output changes to just "XYZ bold" that would indicate the <b> was rendered as an HTML tag, which is bad!
within ".tt-suggestions" do
assert_match(/XYZ <b>bold<\/b>/, find(".tt-suggestion").text)
end
end
end