User:Jminta/MozMill in TB

From MozillaWiki
Jump to: navigation, search

Some notes on my experience using MozMill with TB

Overall impressions

MozMill tests in Thunderbird require a fair amount of boilerplate, more than one would have in an ideal world. In part this is because MozMill is designed primarily for Firefox, but also in part because of the testing environment problems discussed below.

Nonetheless, the framework does provide a great way to exercise functionality that couldn't reasonably be addressed in xpcshell tests.

Testing environment

Many of the tests that I want to run are destructive in some sense. (They create, delete, or modify what would normally be user-data.) Thus, MozMill tests almost always require a new profile.

Thunderbird is almost useless on its first-run, however; there's very little you can test without an account and some folders. More robust testing usually requires messages in those folders too. One option is to manually pre-populate your profile before running your tests, but this runs counter to the goal of automation. Thus, I used the snippets below in the beginnings of my tests to get some data I could manipulate.

This also means that tests must be very careful to clean-up after themselves, otherwise running multiple tests in a row will cause later tests to fail. This adds further to the boilerplate problem.


Thunderbird is dialog intensive, and moreso than Firefox. The MozMill framework doesn't yet have a great set of functions for dealing with these dialogs (instead focusing more on manipulating elements in the main window). The code snippets below also include some helper wrappers for dealing with dialogs.

Next steps

Obviously the key first step is to get MozMill running on the Thunderbird tinderboxen. This shouldn't be terribly difficult. See here.

It might be helpful to have a tb-mozmill module that includes a lot of the boilerplate/common functions below, to reduce the overhead in writing new tests.

One of my next goals is to find a way to analyze just how much of our current code-base is being exercised. Sayrer has done something like this for the Firefox xpcshell tests, and I'm hoping the js-debug APIs will let me follow just what code MozMill is (and is not) hitting.

Code Snippets

Initialize MozMill for Thunderbird's main window.

 var controller = {};
 Components.utils.import('resource://mozmill/modules/controller.js', controller);
 var mainWindow = Components.classes[";1"]
 var  MC = new controller.MozMillController(mainWindow);

Create a local account to work with folders (Note this gives you an Outbox and Trash folder by default).

 const Ci = Components.interfaces;
 let am = Components.classes[";1"]

Add a new mail-message to a folder

function addToFolder(aSubject, aBody, aFolder) {
  var uuidGen = Components.classes[";1"]
  var source = "From - Sat Nov  1 12:39:54 2008\n" +
               "X-Mozilla-Status: 0001\n" +
               "X-Mozilla-Status2: 00000000\n" +
               "Message-ID: <" + uuidGen.generateUUID() + ">\n" +
               "Date: Wed, 11 Jun 2008 20:32:02 -0400\n" +
               "From: Tester <>\n" +
               "User-Agent: Thunderbird 3.0a2pre (Macintosh/2008052122)\n" +
               "MIME-Version: 1.0\n" +
               "To:\n" +
               "Subject: " + aSubject + "\n" +
               "Content-Type: text/plain; charset=ISO-8859-1; format=flowed\n" 
               "Content-Transfer-Encoding: 7bit\n" +
               "\n" + aBody + "\n";
  aFolder.gettingNewMessages = true;
  aFolder.gettingNewMessages = false;

Watch for a window to open, and once it's loaded execute a function with that window's document.

 function checkDialog(doc) {
   // do stuff
 let wm = Components.classes[';1']
 let listener = new windowListener(checkDialog, "serverMenu");
 // call whatever opens the window
/** Include this at the end of your test-file **/
function windowListener(aCall, aElement) {
  this._window = null;
  this._call = aCall;
  this._element = aElement;
windowListener.prototype = {
  onWindowTitleChange: function() {},
  onOpenWindow: function (aWindow) {
    this._window = aWindow;
    var callback = this._call;
    var element = this._element;
    function timerCallback() {
      var doc = aWindow.docShell.contentViewer.DOMDocument;
      if (doc.getElementById(element)) {
      } else {
        function onLoad() {
          doc.removeEventListener("load", onLoad, true);
        doc.addEventListener("load", onLoad, true);
    let timer = Components.classes[";1"]
    timer.initWithCallback({notify: function() { timerCallback(); }}, 500, timer.TYPE_ONE_SHOT);
  onCloseWindow: function (aWindow) {}