functional testing unobtrusive JavaScript to update a portion of a page

I recently updated an application to Rails 3.0, and I ran into a testing question while doing so. I had one bit of a view that looked like this with Rails 2.x:

<% form_remote_tag :url => { :action => :answer, :item => ask }, :update => “answer” %>

where the corresponding controller method ended with:

render :partial => ‘answer’, :object => @item

In the Rails 3 version, :update isn’t available in form_tag, so the controller method had the render line removed, and instead I added a file answer.js.erb containing the following:

$(‘answer’).update(“<%= escape_javascript(render :partial => ‘answer’, :object => @item) %>”);

That works great (and I like the HTML that results, much more stylish than the older version), but I can’t test it as well as I would have liked. Before, I could get at the HTML that was generated by that render :partial line by using assert_select, as follows:

def test_get_answer

get :index

post :answer, :item => @one.id

assert_select “p.qa”, “Answer 1”

end

The problem with the new version is that the return value is now JavaScript, which I can’t take apart via assert_select. So, for now, I’m doing this:

def test_get_answer

get :index

post :answer, :format => ‘js’, :item => @one.id

assert(response.body.include? “Answer 1”)

end

which is certainly better than nothing, but isn’t nearly as precise as I’d like.

Any recommendations for a better way to test this sort of thing?

Thanks, David Carlton carlton@bactrian.org

(Where “this sort of thing” is updating body elements via unobtrusive JavaScript.)

In case anybody else is curious, here’s what I ended up with:

The source .js.erb file:

$(‘answer’).update(“<%= escape_javascript(render :partial => ‘answer’, :object => @item) %>”);

The source partial .html.erb file:

(something that includes

text_that_I_want_to_assert_on

)

And the test:

def unescape_javascript(escaped)

escaped.gsub(“\'”, “'”).gsub(“\"”, “"”).gsub(“\n”, “\n”).

gsub(“\\”, “\”).gsub(“<\/”, “</”)

end

def assert_encoded_update(body, element_to_update)

assert_not_nil(/$('(.)').update("(.)");/ =~ body)

assert_equal(element_to_update, $1)

yield(HTML::Document.new(unescape_javascript($2)).root)

end

def test_get_answer

get :index

post :answer, :format => ‘js’, :item => @one.id

assert_encoded_update(response.body, “answer”) do |replacement|

assert_select(replacement, “p.qa”, “text_that_I_want_to_assert_on”)

end

end

Probably room for improvement (I certainly don’t claim that unescape_javascript handles all the cases that escape_javascript handles), but it works for my purposes.