Posted on January 5, 2010 in Uncategorized by adamNo Comments »

One of the nicer aspects of Firefox plugins is their ability to check for updates and prompt the user when it finds one. By default, it looks to https://addons.mozilla.org which is fine, if not desireable for a lot of projects. But what if you are a commercial entity who wants to control when things get made available in a much tighter manner?

Thankfully, Mozilla thought of this exact scenario and has provided a option in the install.rdf to handle it.

<em:updateURL>https://adam.goucher.ca/preflight/update.rdf</em:updateURL>

The update system is pretty slick and well documented, so I’m not going to redocument it. But these are the links you need if you are hosting your own update notification.

Also, note that I used https. Looking at the documentation around how FF handles the checking, things get a lot easier if you do it over a secure channel. If you don’t you have to provide a bunch of integrity checks, which is a pain.

And that’s all that is needed to avoid the queues that sometimes form on the official addons site.

Posted on January 5, 2010 in Uncategorized by adam1 Comment »

This part of the tutorial is one of the two major purposes of the plugin system for Selenium-IDE; how to add custom methods to the drop-down. (The other major task is adding a custom formatter. That is next on the docket.) This aspect of the plugin I expect will be used the most. Imagine the Selenium-Flex API as just a plugin for Se-IDE which magically extends the API on install. Or a commercial entity’s custom izations automatically installed. This will, I hope, hope the flood gates for adoption (and extension) of Se-IDE the way Se-Core/Se-RC has been.

Before you try this at home though, this relies on functionality that literally was only checked in 10 minutes ago so trying it with the current public release is not going to work. You have to check it out from the Google Code project and you should modify your install.rdf to require (at least) version 1.0.4-SNAPSHOT of Se-IDE.

<em:requires>   
     <Description>
         <em:id>{a6fd85ed-e919-4a43-a5af-8da18bda539f}</em:id>
         <em:minVersion>1.0.4-SNAPSHOT</em:minVersion>
         <em:maxVersion>1.*</em:maxVersion>
     </Description>  
</em:requires>

Now, to the actual extension of the API.

Like everything with the Se-IDE plugin system, you have to overlay something in the existing Se-IDE. In this case we are going to overlay one of the main xul files with one that will load our extension so add this to the chrome.manifest.

# custom extension(s)
overlay chrome://selenium-ide/content/selenium-ide-overlay.xul chrome://preflight/content/extensions/extension-loader.xul

For this example, I’m going to add the RandomString functionality that was contributed to the OpenQA wiki. Random data is super important to well designed automation. I’ve copied that whole file into my project under content/extensions/extension-random.js. I’m not going to outline it here, but trust me, it is the same as on the wiki.

What does need to be shown here is the overlay that loads the extension.

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 
<overlay id="preflight_extension_loader_overlay"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         xmlns:html="http://www.w3.org/1999/xhtml">
    <script type="application/x-javascript" src="chrome://selenium-ide/content/api.js"/>
    <script type="application/x-javascript" src="chrome://preflight/content/preflight.js"/>
    <html:script type="application/javascript">
        ide_api = new API();
        ide_api.addPluginProvidedUserExtension("chrome://preflight/content/extensions/extension-random.js");
    </html:script>
</overlay>

I’m not sure that I like the <html:script> part, but it works, which is good enough for now. And is pretty clear about its intent which is something I like as well. So what does it do? Well, two distinct things, but we’ll worry about loading it first and come back to the second.

  • Includes the Se-IDE API into this XUL’s scope
  • Creates an instance of the API
  • Informs Se-IDE of the extension we want to load using the addPluginProvidedUserExtension method. (If you have more than one extension, just make multiple calls to it.)

Recompile your plugin and restart Firefox and marvel in your new methods being in the Se-IDE command dropdown.

But we’re not done yet. A lot of people might say this is mission accomplished, but I come from the testing space where I notice shortcuts and incompletions. This means the plugin needs to clean up after itself. Which of course can’t be easy, now can it? A plugin can have multiple states: installed and enabled, installed and disabled, and of course, uninstalled. And since we are adding our extension into Se-IDE, when we remove or disable our plugin we need to tell Se-IDE that its added ‘stuff’ isn’t available.

To accomplish this we need to tap deep into Firefox magic and install ‘observers’ for events that happen within the browser such as ‘uninstall’ and ‘disable’. I won’t go into detail on this, but you can read about it more here. I also can’t lay claim to thinking out this next chunk of code. It was taken more-or-less line-for-line from here (credit given when credit due).

Back to cleaning up after ourselves.

Notice in the overlay the inclusion of a preflight.js? This is where I decided to put my observer code.

const PREFLIGHT_ID = "preflight@adam.goucher";
 
function initializePreflightObserver() {
    preflightObserver.register();
}
 
var preflightObserver = {
    _uninstall : false,
    observe : function(subject, topic, data) {
        if (topic == "em-action-requested") {
            subject.QueryInterface(Components.interfaces.nsIUpdateItem);
            if (subject.id == PREFLIGHT_ID) {
                if (data == "item-uninstalled") {
                    this._uninstall = true;
                } else if (data == "item-disabled") {
                    this._uninstall = true;
                } else if (data == "item-cancel-action") {
                    this._uninstall = false;
                }
            }
        } else if (topic == "quit-application-granted") {
            if (this._uninstall) {
            	// your uninstall stuff goes here
 
            	// this section removes the extension we added
                var branch = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch("extensions.selenium-ide.");
                var current_ppue = branch.getCharPref("pluginProvidedUserExtensions");
                if (typeof current_ppue != "undefined") {
                	// need one 'if' block like below for each one you added
                    if (current_ppue.search("chrome://preflight/content/extensions/extension-random.js") != -1) {
                        branch.setCharPref("pluginProvidedUserExtensions", current_ppue.replace("chrome://preflight/content/extensions/extension-random.js", ""));
                    }
                }
            }
            this.unregister();
        }
    },
    register : function() {
        var observerService = Components.classes["@mozilla.org/observer-service;1"].     getService(Components.interfaces.nsIObserverService);
        observerService.addObserver(this, "em-action-requested", false);
        observerService.addObserver(this, "quit-application-granted", false);
    },
    unregister : function() {
        var observerService = Components.classes["@mozilla.org/observer-service;1"].      getService(Components.interfaces.nsIObserverService);
        observerService.removeObserver(this,"em-action-requested");
        observerService.removeObserver(this, "quit-application-granted");
    }
}
 
window.addEventListener("load", initializePreflightObserver, false);

You can pretty much copy-and-paste the above code changing only things that say ‘preflight’ and have it work. Well, except for the section in the middle that I somewhat annotated. If you registered an extension, you need to remove it. If you have any other bits to cleanup as well, they should go there too.

(I couldn’t put this logic in the Se-IDE itself as there was some wierd circular dependencies around when observers get added / called so you just need to do it or you will give your users an annoying pop-up.)

From here, its just a matter of recompile and restart and you have successfully extended the API that ships with Se-IDE without user intervention.

It’s not as elegant as I had hoped (regarding the removal) but it works and is still relatively simple. Now lets start getting those ‘just add this to the user extension box’ additions to Se-IDE wrapped in plugins as registered somewhere. (I guess one of the next steps will be to make a page on Selenium HQ somewhere for these.)

Posted on January 4, 2010 in Uncategorized by adam1 Comment »

Macleans has a brief interview with Mark Remy about his book The Runner’s

Rule Book in the December 28, 2009 edition. It is rather light hearted and could be

ignored by non-runners, except for the section on stretching which is impact on a wider

audience.

On the subject of stretching, Remy tells Maclean’s, “I’m always too lazy to stretch.” Rule 1.42 is “Stretch If You Want To.” “If you’re looking for hard evidence of stretching’s benefits, good luck,” he writes. “Fact is, it doesn’t exist. And if you want to ask other runners or doctors or physical therapists or high school track coaches, go for it. Just be prepared to hear a different opinion from each one of them. If stretching seems to help you run better and feel better, then stretch. If not, then don’t.”

Now substitute in any of the following for stretching:

  • TDD
  • Pair Programming
  • SBTM
  • Agile
  • Waterfall
  • Metrics
  • UI Automation

Or any other buzzword or sacred cow of industry.

The last line is critically important: If X seems to help you Y better and feel better, then X. If not, then don’t.

« Previous Page