Skip to content

Plugin Developer Documentation

Zarkonnen edited this page Oct 8, 2012 · 9 revisions

Builder supports a system of plugins that can extend its functionality. Plugins work by supplying additional Javascript code that is executed when Builder is loaded. The plugin system is separate from the Firefox addon system: plugins are not addons.

Since Builder's GUI is based on HTML and Javascript, not XUL, you can use these technologies to add new user interface elements as needed.

Plugin Structure

ID

Each plugin has a unique identifier, which is distinct from its name. The unique identifier must be between 3 and 32 letters long, and be composed of the uppercase and lowercase letters A-Z, numbers, and the underscore. Specifically it must match the regular expression

/^[0-9a-zA-Z_]{3,32}$/

This unique ID is used by the plugin system to refer to a particular plugin, so it's important that it's unique and consistent.

Plugin Directories

A plugin consists of a directory whose name is its identifier. Installed plugins reside in <Firefox profile directory>/SeBuilder/plugins/. The SeBuilder directory also contains other files and directories. Of special relevance is the plugins.sqlite file, which stores plugin metadata.

Note that whether or not a plugin is installed depends on its physical presence in the plugins directory. The metadata only stores whether the plugin is enabled and whether it's scheduled for update/install/uninstall.

The directory for a plugin contains at least two files: the header.json file, and a Javascript file that contains the plugin's code. In addition, it may also contain resources for the plugin such as images.

<profile directory> -+
                     +- SeBuilder -+
                                   +- plugins -+
                                               +- myplugin -+
                                                            +- header.json
                                                            +- code.js
                                                            +- logo.png

Header file format

The header file is a JSON file. Its top level is a JSON map with the following keys:

  • identifier (string) The plugin's unique identifier. Must match the name of the plugin directory exactly.
  • headerVersion (int) Version of the header format. Should be 1.
  • pluginVersion (string) Version of this plugin. Dot-delimited numbers. Should match /^[0-9]+([.][0-9]+)*$/. Valid versions include 2, 2.1, 2.3.5, 1.0 and 2012. Invalid versions include the empty string, 2a, 3bca and XP.
  • builderMinVersion (string, optional) Minimum version of Se Builder this plugin supports. As of the time of this writing, Se Builder is at version 2.0.0. The most up to date version can be found in loader.js. The format here is as above but additionally supports * as a wildcard. Valid versions include 1, 1.3, 1.3.*. Invalid versions include 1.*.8, 1b and 1.1*. Recommended value: 2.0.0.
  • builderMaxVersion (string, optional) Maximum version of Se Builder this plugin supports. Recommended value: 2.0.* or 2.*.
  • name (string) The display name of the plugin. Unlike the unique identifier, this may be anything within reason, and may change between versions of the same plugin.
  • description (string) Short description of the plugin. Should not exceed ~200 letters.
  • siteUrl (string, optional) URL for the plugin's website. Not its download link.
  • shutdownFunction (string, optional) The name of the function to call when Builder is shutting down. This allows the plugin to perform any cleanup (closing database connections, etc) in time before Builder shuts down. Since this function may not be called if eg Firefox crashes, try not to rely on it too much!
  • load (list of strings) The list of Javascript files to be executed during loading. Each element is a slash-delimited path relative to the plugin's directory.

Example header file

{
  "headerVersion": 1,
  "pluginVersion": "1.0",
  "builderMinVersion": "2.0.1",
  "builderMaxVersion": "2.0.*",
  "identifier": "myplugin",
  "name": "My Plugin",
  "description": "Example plugin.",
  "siteUrl": "http://www.example.com",
  "shutdownFunction": "myplugin.shutdown",
  "load": [
    "plugin.js"
  ]
}

Installing and supplying plugins

Plugins can be installed simply by dropping them into the <your Firefox profile>/SeBuilder/plugins directory.

There is also a central list of available plugins. These plugins can be installed with a single click from the "Plugins" view in Builder. To prepare your plugin for submission to the repository, create a zip of the plugin directory and make it available for download. (Note: The zip should be of the directory, not of its contents.) Then contact Adam Christian or David Stark to have it included.

Plugin API

jQuery

jQuery is loaded at startup by Se Builder, and is hence available in your plugin too.

Creating HTML

Since Builder's UI is composed from HTML elements, there is an easy function for creating them: newNode(tagname, ...). The function's first argument is the tag name of the HTML element you want to create, such as div. It then takes a number of additional arguments and treats them as follows:

  • If the argument is text, it will be inserted into the element.
  • If the argument is another HTML element (created by newNode or otherwise), it will be inserted into the element.
  • If it's a map, its key/value mappings are used as attributes, including class and style. If a value is a function, it is attached as a listener for the event the key names.

Example:

newNode('div', {'class': 'dialog'},
  newNode('h3', "Squirrels"),
  newNode('p', "Squirrels are furry")
);

becomes

<div class="dialog"><h3>Squirrels</h3><p>Squirrels are furry</p></div>

Getting resources

You can get the file:// URL for a file in the directory of your plugin by calling builder.plugins.getResourcePath(plugin_id, relative_url). For example, if you call the function on a Mac with the parameters "myplugin", "logo.png", it might return file:///Users/zar/Library/Application%20Support/Firefox/Profiles/k3xrgtby.dev/SeBuilder/plugins/myplugin/logo.png.

Supporting multiple languages

Selenium Builder now supports displaying the UI in languages other than English. To allow your plugin to match this, you can store all the language strings for your plugin like this:

// Strings
// en
var m = builder.translate.locales['en'].mapping;
m.__example_plugin_cat = "cat";
m.__example_plugin_mouse = "mouse";

// de
m = {};
if (builder.translate.locales['de']) {
    m = builder.translate.locales['de'].mapping;
} else {
    m = {};
    builder.translate.addLocale({'name':'de', 'title': "Deutsch", 'mapping': m});
}
m.__example_plugin_cat = "Katze";
m.__example_plugin_mouse = "Maus";

Then, you can use _t(string_key) to retrieve the strings. For example _t('__example_plugin_cat') will evaluate to "cat" if Builder is set to English or "Katze" if it's set to German. See the Translation Documentation for more details.

You can also substitute text into a translation string. For example:

m.__example_plugin_noexists = "The file {0} does not exist.";

can then be called with _t('__example_plugin_noexists', file_path) to produce "The file /home/fish/stuff.txt does not exist.".

You can specify any number of arguments to _t, and they will be substituted in for {0}, {1}, {2}, etc. If you have a lot of strings to substitute in, you can also use named substitutions to make things clearer. To do that, supply a single JS object as a parameter, whose mappings will be substituted in. For example:

m.__example_plugin_parse_error = "The file {path} could not be parsed due to a {error} error. Please select a {ext} file.";

_t('__example_plugin_parse_error', {'path': file_path, 'error': error_type, 'ext': expected_extension})

Adding startup entries

You can add a new entry to the list of options shown at startup (such as "Open a script or suite" or "Manage plugins") by calling builder.gui.addStartupEntry(title, id, callback).

Adding menu items

You can add new menu items to the GUI by calling builder.gui.menu.addItem(menu_name, item_title, item_id, callback). The available standard menu_names are file, run and suite.

Adding new menus

You can add whole new menus to the GUI by calling builder.gui.menu.addMenu(menu_name, id). Use this sparingly: there is only limited space for menus in the GUI.

Adding new single-button menus

The Record menu in Builder doesn't have items and simply acts as a button. To add such a menu, call builder.gui.menu.addSingleItemMenu(title, id, callback). Again, use this sparingly.

Showing dialogs

You can display an HTML element as a dialog by passing it to builder.dialogs.show(node). Use jQuery(node).remove() to get rid of the dialog again. Dialogs should consist of a div of class dialog. This will display them with the correct border.

Playback result clear notification

You can register a listener that is notified when playback results are cleared by calling builder.views.script.addClearResultsListener(callback). Listeners can be removed again by calling builder.views.script.removeClearResultsListener(callback).

Creating new export formats

See Documentation

Example plugin

Download here: https://github.com/downloads/sebuilder/se-builder/Example%20Plugin.zip

Getting more information

You can read the Builder code on GitHub. Also, do contact David Stark or the mailing list for queries. As questions arise and are answered, the answers will be added to this document.

Planned API functionality

The above API is obviously lacking a lot of features. As time goes on, more API calls will be made available. The following is a list of planned features, with the implemented ones in bold. If you need one of them for a plugin, please don't hesitate to ask and we will put it at the top of the to-do list.

General

  • Post code load hook that executes once all code is loaded
  • Pre-shutdown hook that executes before Builder is shut down

Script editing

  • Entire test script changed (new script, script switched, script discarded)
  • Step added/deleted
  • Step parameter changed
  • Step type changed
  • Parameter edit started/ended
  • Type edit started/ended
  • "Find a different target" started [1]
  • "Find a different target" ended
  • Script saved/exported to location
  • Script converted to different selenium version

Suites

  • Script added/removed
  • Suite discarded
  • Suite saved to location

Recording

  • Recording started [1]
  • User action captured [2]
  • Step modified [3]
  • Step recorded [4]
  • Recording ended

GUI attachment API

  • Add/remove new menu item to existing menu
  • Add/remove whole new top-level menu
  • Add/remove step menu item
  • Show dialog
  • Hide dialog
  • Add/remove element at top/bottom of script
  • Add/remove element in step
  • Add/remove element in text/locator edit
  • Add/remove element in step type editor
  • Add/remove element in startup view

Notes

  • [1]: Can supply a visitor function that is used to attach listeners to the page.
  • [2]: Listener is supplied raw event and can manipulate its data or prevent the recording of the event.
  • [3]: Listener is supplied data structure describing the change and can manipulate its data or discard it. This data structure should probably be a general "step change" event that is also used when listening to user edits of the script.
  • [4]: Listener is supplied generated step and can manipulate its data or discard it.