Jasmine, RequireJS and Rails

741 Views Asked by At

I'm starting to make the move over to requireJS for a project I'm building. I'm currently using jasminerice, rails 3.2 and the require-rails gem.

I've tried to implement http://ryantownsend.co.uk/post/31662285280/jasminerice-and-requirejs-rails-fix with little success, the specs don't run at all.

I am starting to think it maybe I might be better to use requirejs on it's own or maybe the jasmine gem?

I'm not sold on either jasminerice or require-rails gems, so does anyone have any advice on the best tools, and any tips on how to get it up and running/good tutorials?

2

There are 2 best solutions below

1
On BEST ANSWER

Ok as I didn't get any response I managed to find a slightly hacky way of making it work.

If you create a file in your view folder jasminerice/spec/index.html.erb (or haml) and copy the html from the jasminerice gem. Replace the spec.js call with:

%script{"data-main"=>"/assets/#{@specenv}", src:"/assets/require.js"}

Then write your spec file like require template like so:

require.config {
  paths:{
    'jquery':'/assets/jquery'
    'underscore': '/assets/underscore-min'
    'sinon':'sinon-1.6.0'
    'jasmine-sinon':'jasmine-sinon'
    'my_js':'my_js'
    'my_spec':'my_spec'
  }
}


require ['sinon', 'jasmine-sinon', 'jquery', 'underscore', 'my_js', 'my_spec'], () ->
  jasmine.getEnv().execute()

This will prevent jasminerice triggering the tests

jasmine.rice.autoExecute = false

Set up your tests with a beforeFilter similar to this(taken from http://kilon.org/blog/2012/08/testing-backbone-requirejs-applications-with-jasmine/)

describe "MySpec", ->

  beforeEach ->
    flag = false

    @thing = ""
    that = @

    require ['myjs'], (Myjs) ->
      flag = true
      that.thing = new Myjs()

    waitsFor ->
      flag


  it 'It should exsist', ->
    expect(@thing).toBeDefined()

Hope that helps anyone with a similar issue and if anyone has a better solution please post! :)

0
On

I have the same setup, here's what I did (starting from the blog post mentioned in the original question):

1. Create a helper to load all spec files

In a file lib/jasminerice/spec_helper.rb, put the following code:

require "requirejs-rails"

module Jasminerice
  module SpecHelper
    include RequirejsHelper

    def spec_files
      Rails.application.assets.each_logical_path.select { |lp| lp =~ %r{^spec/.*\.js$} }
    end
  end
end

This will create a helper method spec_files which you can call in the Jasminerice runner view to automatically get all your specs, so you don't need to update the list of specs every time you add a new one.

2. Override default Jasminerice index view

Create a view named app/views/jasminerice/spec/index.html.erb with the following:

<!doctype html>
<head>
  <title>Jasmine Spec Runner</title>
  <%= stylesheet_link_tag "jasmine", "spec" %>
  <%= requirejs_include_tag 'application' %>
  <%= javascript_include_tag "jasminerice", "spec", :debug => true %>
  <script>
    jasmine.rice.autoExecute = false;
    require([<%= spec_files.map { |f| "'#{f.sub(/\.js$/,'')}'" }.join(',').html_safe %>],
      function() { jasmine.getEnv().execute() },
      function(err) {
        var failedId = err.requireModules && err.requireModules[0];
        requirejs.undef(failedId);
        define(failedId, function() { return function() { console.debug(failedId + ': ' + err); null }; });
        require([ failedId ], function() {} );
      });
  </script>
  <%= csrf_meta_tags %>
</head>
<body>
</body>
</html>

This will require all the specs before running Jasmine (with jasmine.getEnv().execute()). I have an ugly hack in there to take the array of spec paths and generate an array of module names in quotes to pass to require.

I've also included an error callback in case there's a problem loading a module -- if you don't do this, your specs will hang when a module load fails. That's especially a problem when you're running them on the command line through guard-jasmine, which is what I do.

Unfortunately I haven't found a very good way to handle such errors -- here I write some info to console.debug and then required the failed module, returning an anonymous function in its place. This allows the specs to run but produces unpredictable results (which is better than no results). I've been struggling to find a better way to deal with this situation, suggestions would be much appreciated.

3. Write some specs

My Jasmine specs take the form:

define (require) ->
  MyModule = require 'my-module'
  # any other dependencies needed to test

  describe 'MyModule', ->

    it 'exists', ->
      expect(MyModule).toBeDefined()

etc. Note that all my testing dependencies (jasmine, sinon, jasmine-sinon, etc.) I load outside of require, in spec.js.coffee:

#=require sinon
#=require jasmine-sinon
#=require_tree ./helpers/

I put any other helper functions I need in the helpers directory.

4. Bonus

One other tip: if you have problems because your browser won't reload modules even when they change, I use a trick of adding a dummy argument with a timestamp so that the browser will always see a new file and correctly load it.

I created this function in ApplicationController which I load in a before filter:

before_filter :set_requirejs_config

def set_requirejs_config
  opts = { :urlArgs => "bust=#{Time.now.to_i}" }) if Rails.env == "development"
  Requirejs::Rails::Engine.config.requirejs.run_config.merge!(opts)
end

This adds a query param bust=... to the end of each module name if we're in development mode, so that we always reload modules and get the most up-to-date version. Somewhere there's a post on SO explaining how to do this in RequireJS, but to get it to work with requirejs-rails you have to put it into ApplicationController (and not config/requirejs.yml) so that it is loaded every time you load the page.

Hope that might provide some hints to anyone else using this configuration!