Monday, September 29, 2008

Google Gears in Prism

I've been searching for a while to see if someone has gotten Google Gears working in Prism, but from what I've seen, it hasn't been done before. So I decided to see what it would take to get Prism to accept Gears as a extension.

My first stop was to Mozilla's Prism Extensions page: http://developer.mozilla.org/en/Prism/Extensions I ran into it a bunch of times before, but never paid much attention to it. There is one section that caught my attention "Porting an Existing Extension". This section tells you that you have to add this code to the install.rdf file to say that the extension works in prism:

  <em:targetApplication>
    <!– Prism –>
    <Description>
      <em:id>prism@developer.mozilla.org</em:id>
      <em:minVersion>0.8</em:minVersion>
      <em:maxVersion>1.0.0.*</em:maxVersion>
    </Description>
  </em:targetApplication>

So my next step was to get the actual Gears extension file: http://gears.google.com/ For some reason this was hard for me, but all you have to do is click on the link for your operating system below the big blue button, then the big "Install Gears" button.

Now you have everything you need to try to get Gears to work in Prism. Just open the .xpi, edit install.rdf to include the snippet above (I had problems adding in the snippet, but it worked for me when I substituted it with the targetApplication that was already in the install.rds). Now the extension should be good to add to Prism.

Here is a copy of the Gears.xpi that I created (it is Linux and Prism only). This technique should work with most Firefox extensions, but don't be surprised if it doesn't.

For more information on the install.rds file, check out developer.mozilla.org/en/Install_Manifests To learn more about where Prism came from and where it is going, check out wiki.mozilla.org/Prism

Saturday, September 6, 2008

Mocking in JavaScript

This post is kind of like a postmortem for my JSMock project. The project does not follow this post (but that may chane).

I ran into this issue repeatedly when I was writing tests in JavaScript: The function I want to test, makes calls to a function I don't want to be called (window.alert, Effect.BlindUp, new SomeObject, ... etc). Often I would write tests like:

testSomething: function() {
  window.setTimeout = function(){};
  var timer = setTimeout(function(){ alert('hi'); }, 100);
  assert(timer);
}

The problem with this is it leaves window.setTimeout as a empty function, and later calls to window.setTimeout don't do anything. What we need to do is store the original function before we override it, then restore it at the end of the test:

testSomething: function() {
  var Original_Window_SetTimeout = window.setTimeout;
  window.setTimeout = function(){};
  var timer = setTimeout(function(){ alert('hi'); }, 100);
  assert(timer);
  window.setTimeout = Original_Window_SetTimeout;
}

Cool! Now lets try to automate this:

Object.prototype.mock = function(funcName, block){
  var originalFunc = this[funcName];
  this[funcName] = function(){};
  block();
  this[funcName] = originalFunc;
}
...
testSomething: function() {
  window.mock('setTimeout', function(){
    var timer = setTimeout(function(){ alert('hi'); }, 100);
    assert(timer);
  });
}

Handy! But what if I want to test that window.setTimeout was called? We can do this by adding a variable to our new mock of window.setTimeout. See the wasCalled variable:

Object.prototype.mock = function(funcName, block){
  var originalFunc = this[funcName],
      that = this;
  that[funcName] = function(){ that[funcName].wasCalled = true; };
  that[funcName].wasCalled = false;
  block();
  that[funcName] = originalFunc;
}
...
testSomething: function() {
  window.mock('setTimeout', function(){
    var timer = setTimeout(function(){ alert('hi'); }, 100);
    assert(timer);
    assert(window.setTimeout.wasCalled);
  });
}

Of course you can set other variables if you would like, such as a callCount; but once the mock is finished, these variables are gone. Lastly I'm going to toss in a few things for fun. I'm going to add a reference to the original function, and have the mock return the mocked function.

Object.prototype.mock = function(funcName, block){
  var originalFunc = this[funcName],
      that = this,
      mock = that[funcName] = function(){
        that[funcName].callCount++;
      };
  that[funcName].callCount = 0;
  block(originalFunc);
  that[funcName] = originalFunc;
  return(mock);
}
...
testSomething: function() {
  var mock = window.mock('setTimeout', function(origSetTimeout){
    var timer = setTimeout(function(){ alert('hi'); }, 100);
    assert(timer);
    origSetTimeout(); // actually call window.setTimeout
  });
  assertEqual(1, mock.callCount);
}

If you would like further explanation, examples, or would like to suggest a topic, let me know.