Assert yourself, man! -- redirecting with RJS

Courtenay : February 19th, 2006

Got an ajax method that degrades but want to redirect? Ever tried sending a redirect with ajax? It either crashes, breaks, or just doesn't work. Here's the solution, and as a bonus, a new test method to test your redirects.


  def new
    if request.xhr?
      render :update { |page| page.redirect_to :action => 'create' }
    else
      redirect_to(:action => 'create') 
    end
  end
    
render :update is the infamous RJS call, wherein it returns a document with text/javascript content-type. If you look into prototype.js, it automagically evals such documents. The response body will be pure javascript, baby, and it'll look something like 'window.location.href = "/whatever/do/5"' Here's the test helper to test if your ajax response is actually called. I'd love for someone to rewrite this, write some tests, and submit it as a patch to rails.. Hi, lazyweb! In the mean time, you can just dump it in test/test_helper.rb

  def assert_js_redirected_to(options={}, message=nil)
    clean_backtrace do
      assert_response(:success, message)
      assert_equal 'text/javascript', @response.headers['Content-Type'], 'Response should be Javascript content-type';
      js_regexp = %r{(\w+://)?.*?(/|$|\\\?)(.*)}
      url_regexp = %r{^window\.location\.href [=] ['"]#{js_regexp}['"][;]$}
      redirected_to = @response.body.match(url_regexp)
      assert_not_nil(redirected_to, message)
      redirected_to = redirected_to[3]
      msg = build_message(message, "expected a JS redirect to , found one to ", options, redirected_to)

      if options.is_a?(String)
        assert_equal(options.gsub(/^\//, ''), redirected_to, message)
      else
        msg = build_message(message, "response is not a redirection to all of the options supplied (redirection is )", redirected_to)
        assert_equal(@controller.url_for(options).match(js_regexp)[3], redirected_to, msg)
      end
    end
  end
Strip the ^ and $ from that regex if you're returning more than just a URL in your RJS update call. *update* fixed the method so it actually takes a hash. testing is not just for sissies!

2 Responses to “Assert yourself, man! -- redirecting with RJS”

  1. Brian Lee Says:

    Not sure if I did something wrong with your example but here’s what works for me:

    if request.xhr? render :update do |page| page.redirectto loginurl end else redirect_to(:action => ‘create’) end

  2. Sergio Fierens Says:

    Hi,

    Thank you, man! I was just trying to implement this myself, but I though "Some very clever guy must have done this before me", and I googled for it. Just one minor modification to the fourth line:

    assert_match(/text\/javascript/, @response.headers['Content-Type'], 'Response should be Javascript content-type')

    That is because you can have some other things in the content type header, like "text/javascript; charset=utf-8"

    Regards, Sergio Fierens.

Leave a Reply

I am a human (check this)

Remember: escape your underscores \_ and indent code at least 4 spaces or incur the wrath of smartypants.