Experiences on the Front Lines of User Interfaces and Web Development

Capybara / Poltergeist tests for Twitter Typeahead search

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  
comments powered by Disqus