-
Notifications
You must be signed in to change notification settings - Fork 5
Plugins
- Plugin types
- Naming convention
- Structure of a plugin
- Custom plugin configuration
- Listening to events
- Dependency check
- Custom plugin files
Bender server part is based on broadway that introduces Inversion of control mechanism to increase modularity, allow easy application extensibility and better composition.
Using broadway's API, you can create custom plugins that have full access to application`s core and modules.
There are five basic types of plugins defined:
- [framework](https://github.com/benderjs/benderjs/wiki/Framework adapters) - adapter for a test framework
- reporter - server log reporter
- [testbuilder](https://github.com/benderjs/benderjs/wiki/Test builders) - test file builder
- [pagebuilder](https://github.com/benderjs/benderjs/wiki/Page builders) - test page builder
- preprocessor - code pre-processor
However, you can create a plugin that does not fit to any of the above groups.
You can also create a plugin that combines 2 or more of the above, benderjs-jquery plugin is a good example of that approach - it consists of a pagebuilder and a testbuilder.
All Bender plugins should be named according to the following convention:
bender-<plugin type>-<plugin name>
Exceptionally, for plugins that don't fit in any of the above types, you can skip plugin's type and name it in the following way:
bender-<plugin name>
Bender plugins are distributed as NPM packages. This means, they should consist of at least three files:
-
index.js- source code of a plugin -
package.json- metadata used by NPM -
README.md- plugin's description
Plugins utilize broadway API. For a valid plugin you need to specify at least the name and the attach function.
module.exports = {
name: 'bender-pluginType-pluginName',
attach: function () {
// plugin's core
},
init: function () {
// plugin's initialization
}
};Explanation:
-
name- name of the plugin, should follow naming convention. -
attach- function called whenbender.use()method is executed. It's being called in Bender's context, which means by usingthisyou can refer tobenderapplication, extend it and access it's modules, methods and properties.Example:
module.exports = { name: 'plugin-customPlugin', attach: function () { var bender = this; // extend bender with custom plugin's API bender.customPlugin = { foo: function () { // do some stuff }, bar: 12345 }; } };
-
init- function called whenbender.init()method is executed. At this stage all of the modules should be already attached tobenderapplication. This function is also executed in Bender's context. It accepts optionaldonecallback that can be used in case the initialization of the plugin is asynchronous.Example:
module.exports = { name: 'plugin-customPlugin', attach: function () { var bender = this; // extend bender app with custom plugin's API bender.customPlugin = { foo: function () {...}, fooAsync: function (callback) { // do some async stuff and execute the callback on some point callback(); } }; }, init: function (done) { var bender = this; bender.customPlugin.foo(); bender.customPlugin.fooAsync(done); // this will execute 'done' to indicate that the initialization process is complete } };
If the plugin requires some additional configuration from a user, it can access to Bender's configuration and options defined in bender.js file through bender.conf object.
Example:
In bender.js configuration file:
{
applications: {...},
customOption: 'foo',
tests: {...}
}In the plugin's code:
module.exports = {
name: 'benderjs-customPlugin',
attach: function () {
var bender = this;
console.log(bender.conf.customOption); // 'foo'
},
init: function () {
var bender = this;
console.log(bender.conf.customOption); // 'foo'
}
};Please note: some of the configuration options names are reserved for Bender core, so in case you really need to use them, please avoid modifying them.
Bender application inherits from EventEmitter2 and uses its API to manage events. There are several events you can listen to with your custom plugin.
Example:
module.exports = {
name: 'plugin-customPlugin',
attach: function () {
var bender = this;
// extend bender with custom plugin's API
bender.customPlugin = {
foo: function (arg1, arg2) {
// do some stuff
}
};
},
init: function () {
var bender = this;
// attach custom listener for 'eventName'
bender.on('eventName', bender.customPlugin.foo);
}
};Some of the plugins may depend on others. To make sure all of the dependencies are available, use bender.checkDeps API.
checkDeps(<current_plugins_name>, <dependency_1_name>, <dependency_2_name> ...)
Example:
module.exports = {
name: 'benderjs-customPlugin',
attach: function () {
var bender = this;
bender.checkDeps('customPlugin', 'benderjs-jquery', 'benderjs-otherCustomPlugin');
bender.otherCustomPlugin.method();
}
};If one or more dependencies are missing, following message will appear in the console and Bender will be stopped:
error: Missing module: <dependency_name>
error: Module <plugin_name> requires: <list_of_dependencies>
Some of the plugins may require loading additional files in the test context. To mark which files should be shared by Bender server, use files field in the plugin definition.
Example:
var path = require('path'),
files = [
path.join(__dirname, 'custom-script.js'),
path.join(__dirname, 'custom-styles.css')
];
module.exports = {
name: 'bender-testbuilder-custom',
files: files,
build: function (data) {
// add a custom script to the test context if a custom flag is truthy
if (data.customPlugin.foo) {
data.parts.push(
'<head><script src="' + files[0] + '"></script></head>'
);
}
return data;
}
};