jqUnit the jQuery Test Framework

My last project used jsUnit, but after some searching around, i found a great alternative: jqUnit!

It is a simple wrapper around the jQuery testrunner, so it is possible to run it with any library. And it is jsUnit compatible, so you can reuse all old tests.

Other features:

  • simple, just write test(‘it should find ids’,function(){ok(true)})
  • very light syntax ok(value,msg), equal(actual,expected,msg)…
  • DOM reset after every test
  • clean output, that can be opened/closed by clicking test-names
  • real user interaction ca be simulated with triggerEvent

Download Source and Demo
Demo Testsuite

Javascript Array.include and Array.index

Just needed them and poor JS lacks these basic functions…

Warning:
if(![‘hello’].index(‘hello’))alert(‘watch out because 0 is false‘);

Array.prototype.index = function(val) {
  for(var i = 0, l = this.length; i < l; i++) {
    if(this[i] == val) return i;
  }
  return null;
}

Array.prototype.include = function(val) {
  return this.index(val) !== null;
}

Clicking Links in UnitTests

Using click /change/i is a good way of testing you links/methods, since it will assure your views render the valid link and you do not have to repeat the link parameters as in get :edit, :id=>3

  1. get form_test_helper with
    script/plugin install http://form-test-helper.googlecode.com/svn/form_test_helper/
  2. patch vendor/plugins/form_test_helper/lib/form_test_helper.rb
    -  if self["onclick"] && self["onclick"] =~ /'_method'.*'value', '(\w+)'/
    -    $1.to_sym
    +  if self["onclick"] && self["onclick"] =~ /\.method = '(.*)'/
    +    $1.downcase.to_sym
  3. insert helper to test/test_helper.rb
      def click(text)
        select_link(text).click
      end
  4. click click click clickaround!
      def test_should_not_upload_from_ftp
        login_as :aaron
        assert_invalid_upload do
          `cp -f test/fixtures/videos/test.mov #{ftp_accounts(:aaron).dir}/wrong_name.flv`
    
          click /upload.*ftp/i
          assert_response :redirect
          assert flash[:alert]
        end
      end

Event delegation with almost any Selector

Always reapply the events to the DOM objects when they are inserted or loaded? NO MORE!

There are 3 solutions out there at the moment, jQuery listen, jQuery intercept both by event delegation addict Ariel Flesler and LiveQuery a plugin that reapplys all rules to every new DOM element loaded.

(Event delegation = every div.onclick will reach the parents of the div and can be captured there without binding directly to the div)

Listen and intercept both have a very limited selector choise, ‘a.b’ or ‘a#hello’. LiveQuery can be used rather thoughtfree but comes at a load-time-price for every existing/new element.

I like delegation more, but the selector choise is too limited. So here comes my
‘intercept any selector’ hack!

$.intercept(‘table td div.hello a#click :input’,’click’,fn) will bind your Event to the document, and run on every click of ‘table td…’. The lower you bind, the higher the performance cost(needless event checking)
$(‘table’).intercept(‘td div.hello a#click :input’,’click’,fn) will be faster…

As long as the DOM element you bound your events to is not replaced, all events will continue to work. This approach works best for few(and slow) rules that match many elements. There is no initial performance cost, so your page will load fast.

Only when a event is triggered the checking is done, and can be costly if you used a lot of events or a often triggered event (mouseover…).

The code you need to make it happen is here(insert into intercept.js):

  ...
    for( selector in handlers ){
      if( selector == 'self' && e.target == this || $.intercept.matches($target,selector))
        ret = handlers[selector].apply(this, arguments) !== false && ret;
    }
    return ret;
  };

  /**
   * Walk the element backwards and see if the selector matches (before reaching the document)
   * @see https://pragmatig.wordpress.com/2008/03/08/event-delegation-with-almost-any-selector/
   */
  $.intercept.matches = function(obj,selector){
    var root_reached = false;
    var words = selector.split(' ').reverse();
    for(i in words){
    	do{
    	  //match found?
    	  if(obj.is(words[i])){
    		  obj=$(obj.parent());
    		  break;
  	    }
    	  obj=$(obj.parent());
    	  root_reached = obj.get(0) == document;
      }while(!root_reached);
  	  if(root_reached)return false;
    }
    return !root_reached;
  };

Autoupdate jQuery with Rake

After installing jQuery on Rails (jRails) i noticed that the update task just kept overwriting my jquery.js with an older version. Effectively downgrading it…

So here is a real update task, execute it with rake update:jquery

  #lib/tasks/jquery.rake
  namespace :update do
    desc "Download recent jQuery javascripts to public/javascripts"
    task :jquery => :environment do
      puts "Downloading files..."
      files = {
        'jquery.js'=>'http://code.jquery.com/jquery-latest.min.js',
      }

      require 'open-uri'
      files.each do |local,remote|
        f = File.open "#{js_dir}/#{local}", 'w' do |f|
          f.write open(remote).read
        end
        puts "downloaded #{local} successfully."
      end
      puts "all files downloaded successfully."
    end

    def js_dir
      ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR
    end
  end

PS: If the bleeding edge breaks anything you can get your old version back by jrails:update:javascripts

The other jquery libaries(fx,ui) are missing atm, since i am just using jquery on its own. If you know an update site, please drop a comment.