Testing PURE javascript templates from RSpec

2010-05-2

The traditional Rails approach for AJAX has been using RJS templates. Following this approach, controllers respond to requests by rendering javascript code that is executed in the page on the fly, being the main benefit that you can use a powerful set of ruby helpers for specifying the javascript to be generated. Although this technique has shown to be a productive way of adding ajax to web interfaces, it also has serious drawbacks and has been explicitly discouraged by first-class experts on Rails and Javascript like Yehuda Katz: check these slides or these. It is worth to notice that Rails 3 has redefined its AJAX approach encouraging an unobtrusive use of javascript. This way you can still use the remote Rails helpers without being tied to a concrete Javascript library.

In my opinion, it just makes sense to have a server sending and receiving data in the form of JSON, and rich clients taking all the responsibility of rendering the UI and handling user interactions using HTML, CSS and Javascript. The key is that this approach enables you to architect the client part of your application. If some part of the client behavior is left to the server, it becomes more difficult to have a consistent architecture in the client part because concerns are mixed. And without a consistent architecture in the client, it is difficult to provide the user experience that is becoming more and more demanded in today web applications.

So, if you want to render everything in the client using Javascript, you need a good system for creating the HTML, and here it is where javascript templating systems appear. In this post I would like to explain how to test PURE Javascript templates using RSpec. I chose to use PURE because it is a production-ready system for rendering JSON data. The other solid approach seems to be JTemplates, but it doesn’t appear to be as actively maintained as PURE. There is also a proposal for including a templating system in JQuery that was initially submitted by Microsoft. There is already a demonstration implementation of the proposal but it is still in the incubation phase.

Tools

I am using RSpec and an amazing set of tools for running javascript from Ruby code:

  • Harmony: which wraps Johnson and env.js letting you run javascript code in a DOM environment from ruby.
  • Holy Grail: the Harmony plugin for Rails. It allows you to run javascript code in the context of your view tests.

Just follow the instructions in the sites or Harmony and Holy Grail for installing them. For the installation of Holy Grail with RSpec, read this article from Ken Mayer.

The example

For the sake of simplicity I will try to keep the PURE part as minimal as possible. I want to render the following person object.

{"name":"Jorge"}

And I will use the following PURE template:

<div id="person-template" class="name"/>

Although in practice you will be using PURE directives for sure, for the example I will use the PURE auto-rendering mechanism.

What I want is to write a RSpec view spec to test that the template renders JSon as expected:

describe "persons/_person_template.html.erb" do
  it "should render a proper container for the person" do
    person = {:name=>"Jorge"}
    render_javascript_template(person) do |rendered_text|
      rendered_text.should have_selector("div.name", :content=>"Jorge")
    end
  end
end

The helper method render_javascript_template receives a model object to render, and yields to a closure providing it with the result of the rendered template. The rendered text can be then checked using, in this case, Webrat matchers. The code of this helper method is shown bellow.

module ViewHelpers
 
  def render_javascript_template(data)
    render
    include_javascript_files
    yield do_render_javascript_template(data)    
  end
 
  def include_javascript_files
    %w{jquery.js pure.js}.each {|file| js("load('public/javascripts/#{file}');")}
  end
 
  def as_javascript_string(text)
    text.split("\n").collect{|line| "'#{line}'"}.join('+')
  end
 
  def template_id_from_path(template_path)
    template_path.gsub(/^(.+\/_)/, "").gsub(/_/, "-").gsub(/.html.erb$/, "")
  end
 
  def do_render_javascript_template(data)
    json_data = as_javascript_string(data.to_json)
    template_id = template_id_from_path(self.class.description_parts.first)
    js("var data=$.parseJSON(#{json_data});")
    js("var $template = $('##{template_id}').clone().appendTo($('<div></div>'));")
    js("var renderedElement = $template.autoRender(data);")
   
    return js("$('<div></div>').append(renderedElement).html()")
  end
 
end

The render_javascript_template method has to do basically two things before rendering the templates using PURE:

  • Rendering the template that contains the HTML of the template (in this example it is the ERB file itself).
  • Including the required javascript files of PURE and JQuery.

The order of these steps is important: if you don’t invoke render before requiring the javascript files, JQuery won’t work properly.

The method that does the work is do_render_javascript_template. It basically does three things:

  • It converts the json of the data to be rendered to a form suitable to be injected in the javascript runtime as a string literal (javascript doesn’t admit multi-lines literals).
  • It then obtains the template CSS id that is going to be used to locate the template node we want to use. It obtains it from the file path of the template, which is specified in the outer describe of the spec.
  • Finally it invokes the PURE auto-rendering process with the template. The only tip is that it appends an artificial container to the template because PURE fails if the template node hasn’t a parent. In the same way, before calling the JQuery function html(), an artificial root node is added because JQuery ignore root nodes with elements detached from the DOM.

Conclussions

While I test all my javascript code using JSpec, it didn’t fit well for testing my PURE templates. I had read about Harmony and I wondered if it would be possible to make the testing of PURE templates using RSpec and the powerful Webrat matchers. It happened to be not only possible, but a surprisingly fast and comfortable way of testing the templates. I am willing to see a templating system finally included with JQuery, but PURE is a powerful and fast choice that works today. I really think Javascript templates are here to stay.

The code of this post is availabe as a gist at github.

There are 3 comments in this article:

  1. 2010-05-3Tweets that mention Testing PURE javascript templates from RSpec - Jorge Manrubia -- Topsy.com say:

    [...] This post was mentioned on Twitter by Juan F. Valdés gayo, Jorge Manrubia. Jorge Manrubia said: I wrote a post on how to test javascript templates using RSpec: http://bit.ly/bhyF6f [...]

  2. 2010-05-26Mic say:

    Hi Jorge,

    PURE is actively maintained as it is the exclusive HTML renderer of our web app.

    Have a look at http://beebole.com , the “live demo” is the real app (since last week!!). I think it is a good showcase of the speed PURE can offer to a web app based on the data/server + rich/client concept you describe here.

    ¡Muchas gracias por usar PURE y escribir ese post! Saludos,

  3. 2010-05-27Jorge Manrubia say:

    Hi Mic,

    Thanks for the feedback, and for creating PURE. I love its approach for rendering json data and it also has an amazing performance. By the way, Beebole seems very nice!

Write a comment: