API Docs for: 0.9.1
Show:

File: lib/manager.js

// Copyright 2014, Yahoo! Inc.
// Copyrights licensed under the Mit License. See the accompanying LICENSE file for terms.

var Base = require('preceptor-core').Base;
var path = require('path');
var _ = require('underscore');

var ReportContainer = require('./container');

var EventReporter = require('./reporter/event');

var AbstractListener = require('./abstractListener');
var AbstractMessenger = require('./abstractMessenger');
var AbstractReporter = require('./abstractReporter');
var AbstractLoader = require('./abstractLoader');

/**
 * @class ReportManager
 * @extends Base
 *
 * @property {object} _options Options supplied when created
 * @property {ReportContainer} _container Report container
 *
 * @property {object} _reporterList Registered reporter
 * @property {AbstractReporter[]} _activeReporter Active reporter list
 *
 * @property {object} _listenerList Registered listener
 * @property {AbstractListener[]} _activeListener Active listener list
 *
 * @property {Event} _messageEvent Convenience reporter to trigger messages
 */
var ReportManager = Base.extend(

	/**
	 * Reporter manager constructor
	 *
	 * @param {object} [options]
	 * @param {object} [options.collect=true]
	 * @param {ReportContainer} [options.container]
	 * @constructor
	 */
	function (options) {
		this.__super();

		this._options = options || {};

		if (this._options.collect === undefined) {
			this._options.collect = true;
		}

		if (this._options.collect) {
			this._container = this._options.container || new ReportContainer();
		}

		this._reporterList = {};
		this._activeReporter = [];

		this._listenerList = {};
		this._activeListener = [];

		this._messageEvent = new EventReporter(null, { type:'Event' });

		this.initialize();
	},

	{
		/**
		 * Initializes the instance
		 *
		 * @method initialize
		 */
		initialize: function () {
			this._initializeReporterRegistry();
			this._initializeListenerRegistry();

			this._messageEvent.on('message', function (areaType, messageType, params) {
				this.processMessage(messageType, params);
			}.bind(this));
		},


		/**
		 * Initializes the reporter registry
		 *
		 * @method _initializeReporterRegistry
		 * @private
		 */
		_initializeReporterRegistry: function () {
			var defaultElements = [
				{ name: 'dot', fileName: 'dot' },
				{ name: 'duration', fileName: 'duration' },
				{ name: 'event', fileName: 'event' },
				{ name: 'jenkinssaucelabs', fileName: 'jenkinsSauceLabs' },
				{ name: 'json', fileName: 'json' },
				{ name: 'junit', fileName: 'junit' },
				{ name: 'linesummary', fileName: 'lineSummary' },
				{ name: 'list', fileName: 'list' },
				{ name: 'plain', fileName: 'plain' },
				{ name: 'preceptor', fileName: 'preceptor' },
				{ name: 'spec', fileName: 'spec' },
				{ name: 'summary', fileName: 'summary' },
				{ name: 'tap', fileName: 'tap' },
				{ name: 'teamcity', fileName: 'teamCity' },
				{ name: 'intellij', fileName: 'teamCity' }
			];

			_.each(defaultElements, function (entry) {
				entry.path = path.join(__dirname, 'reporter', entry.fileName);
				entry.fn = require(entry.path);
			}, this);

			this.registerReporterRange(defaultElements);
		},

		/**
		 * Initializes the listener registry
		 *
		 * @method _initializeListenerRegistry
		 * @private
		 */
		_initializeListenerRegistry: function () {
			var defaultElements = [
				{ name: 'preceptor', fileName: 'preceptor' },
				{ name: 'teamcity', fileName: 'teamCity' },
				{ name: 'intellij', fileName: 'teamCity' }
			];

			_.each(defaultElements, function (entry) {
				entry.path = path.join(__dirname, 'listener', entry.fileName);
				entry.fn = require(entry.path);
			}, this);

			this.registerListenerRange(defaultElements);
		},


		/**
		 * Should report-manager collect events?
		 *
		 * @method shouldCollect
		 * @return {boolean}
		 */
		shouldCollect: function () {
			return this.getOptions().collect;
		},


		/**
		 * Gets the options
		 *
		 * @method getOptions
		 * @return {object}
		 */
		getOptions: function () {
			return this._options;
		},

		/**
		 * Gets the container of messages
		 *
		 * @method getContainer
		 * @return {ReportContainer}
		 */
		getContainer: function () {
			return this._container;
		},


		/**
		 * Gets all the active reporter
		 *
		 * @method getActiveReporter
		 * @return {AbstractReporter[]}
		 */
		getActiveReporter: function () {
			return this._activeReporter;
		},

		/**
		 * Gets a dictionary of registered reporter
		 *
		 * @method getReporterList
		 * @return {object}
		 */
		getReporterList: function () {
			return this._reporterList;
		},


		/**
		 * Checks if a reporter is registered
		 *
		 * @method hasReporter
		 * @param {string} name
		 * @return {boolean}
		 */
		hasReporter: function (name) {
			return !!this._reporterList[name.toLowerCase()];
		},

		/**
		 * Gets a specific registered reporter
		 *
		 * @method getReporter
		 * @param {string} name
		 * @return {function}
		 */
		getReporter: function (name) {
			return this._reporterList[name.toLowerCase()];
		},

		/**
		 * Registers a reporter
		 *
		 * @method registerReporter
		 * @param {string} name
		 * @param {function} contr
		 */
		registerReporter: function (name, contr) {
			this._reporterList[name.toLowerCase()] = contr;
		},

		/**
		 * Registers a list of reporters
		 *
		 * @method registerReporterRange
		 * @param {object[]} list Of `{ name: <string>, fn: <function> }`
		 */
		registerReporterRange: function (list) {

			_.each(list, function (entry) {
				this.registerReporter(entry.name, entry.fn);
			}, this);
		},


		/**
		 * Adds a new reporter to the list of active reporters
		 *
		 * @method addReporter
		 * @param {string} name
		 * @param {object} [options]
		 * @return {AbstractReporter}
		 */
		addReporter: function (name, options) {
			var Class = this.getReporter(name),
				instance;

			if (!Class) {
				throw new Error('Unknown reporter "' + name + '"');
			}

			options = options || {};
			if (!options.type) {
				options.type = name;
			}

			instance = new Class(this.getContainer(), options);
			this.getActiveReporter().push(instance);
			return instance;
		},

		/**
		 * Adds a new reporter to the list of active reporters
		 *
		 * @method addReporterRange
		 * @param {string|string[]|object|object[]} reporter
		 * @return {AbstractReporter[]}
		 */
		addReporterRange: function (reporter) {
			var result = [];

			reporter = this._normalizeReporter(reporter);

			_.each(reporter, function (entry) {
				result.push(this.addReporter(entry.type, entry));
			}, this);

			return result;
		},

		/**
		 * Normalizes the reporter
		 *
		 * @method _normalizeReporter
		 * @param {string|string[]|object|object[]} reporter
		 * @return {object[]}
		 * @private
		 */
		_normalizeReporter: function (reporter) {
			var newReporter,
				localReporter = reporter;

			// Convert to array
			if (_.isString(localReporter)) {
				localReporter = [localReporter];
			} else if (!_.isArray(localReporter) && _.isObject(localReporter)) {
				localReporter = [localReporter];
			}

			// Format array as needed by the function
			if (_.isArray(localReporter)) {

				newReporter = [];
				_.each(localReporter, function (currentReporter) {

					if (_.isString(currentReporter)) {
						newReporter.push({ type: currentReporter });

					} else if (_.isObject(currentReporter)) {
						newReporter.push(currentReporter);
					}
				});
				localReporter = newReporter;
			}

			return localReporter;
		},



		/**
		 * Gets all the active listener
		 *
		 * @method getActiveListener
		 * @return {AbstractListener[]}
		 */
		getActiveListener: function () {
			return this._activeListener;
		},

		/**
		 * Gets a dictionary of registered listener
		 *
		 * @method getListenerList
		 * @return {object}
		 */
		getListenerList: function () {
			return this._listenerList;
		},


		/**
		 * Checks if a listener is registered
		 *
		 * @method hasListener
		 * @param {string} name
		 * @return {boolean}
		 */
		hasListener: function (name) {
			return !!this._listenerList[name.toLowerCase()];
		},

		/**
		 * Gets a specific registered listener
		 *
		 * @method getListener
		 * @param {string} name
		 * @return {function}
		 */
		getListener: function (name) {
			return this._listenerList[name.toLowerCase()];
		},

		/**
		 * Registers a listener
		 *
		 * @method registerListener
		 * @param {string} name
		 * @param {function} contr
		 */
		registerListener: function (name, contr) {
			this._listenerList[name.toLowerCase()] = contr;
		},

		/**
		 * Registers a list of listeners
		 *
		 * @method registerListenerRange
		 * @param {object[]} list `{ name: <string>, fn: <function> }`
		 */
		registerListenerRange: function (list) {

			_.each(list, function (entry) {
				this.registerListener(entry.name, entry.fn);
			}, this);
		},


		/**
		 * Adds a new listener to the list of active listeners
		 *
		 * @method addListener
		 * @param {string} name
		 * @param {object} [options]
		 * @return {AbstractListener}
		 */
		addListener: function (name, options) {
			var Class = this.getListener(name),
				instance;

			if (!Class) {
				throw new Error('Unknown listener "' + name + '"');
			}

			options = options || {};
			if (!options.type) {
				options.type = name;
			}

			instance = new Class(this.getContainer(), options);
			instance.on('message', function (messageType, data) {
				this.processMessage(messageType, data);
			}.bind(this));
			this.getActiveListener().push(instance);
			return instance;
		},

		/**
		 * Adds a new listener to the list of active listeners
		 *
		 * @method addListenerRange
		 * @param {string|string[]|object|object[]} listener
		 * @return {AbstractListener[]}
		 */
		addListenerRange: function (listener) {
			var result = [];

			listener = this._normalizeListener(listener);

			_.each(listener, function (entry) {
				result.push(this.addListener(entry.type, entry));
			}, this);

			return result;
		},

		/**
		 * Normalizes the listener
		 *
		 * @method _normalizeListener
		 * @param {string|string[]|object|object[]} listener
		 * @return {object[]}
		 * @private
		 */
		_normalizeListener: function (listener) {
			var newListener,
				localListener = listener;

			// Convert to array
			if (_.isString(localListener)) {
				localListener = [localListener];
			} else if (!_.isArray(localListener) && _.isObject(localListener)) {
				localListener = [localListener];
			}

			// Format array as needed by the function
			if (_.isArray(localListener)) {

				newListener = [];
				_.each(localListener, function (currentListener) {

					if (_.isString(currentListener)) {
						newListener.push({ type: currentListener });

					} else if (_.isObject(currentListener)) {
						newListener.push(currentListener);
					}
				});
				localListener = newListener;
			}

			return localListener;
		},


		/**
		 * Gets a list of allowed messages
		 *
		 * @method getAllowedMessages
		 * @return {string[]}
		 */
		getAllowedMessages: function () {
			return [
				'start',
				'stop',
				'complete',
				'itemData',
				'itemMessage',
				'suiteStart',
				'suiteEnd',
				'testStart',
				'testFailed',
				'testUndefined',
				'testError',
				'testPassed',
				'testSkipped',
				'testIncomplete'
			];
		},

		/**
		 * Processes a message by sending it to all attached reporter
		 *
		 * @method processMessage
		 * @param {string} messageType
		 * @param {*[]} data
		 */
		processMessage: function (messageType, data) {

			var container = this.getContainer();

			if (this.getAllowedMessages().indexOf(messageType) === -1) {
				throw new Error('The message is not allowed to be relayed to the reporter. "' + messageType + '"');
			}

			// Send to container
			if (this.shouldCollect()) {
				container[messageType].apply(container, data);
			}

			// Send to reporter
			_.each(this.getActiveReporter(), function (reporter) {
				reporter[messageType].apply(reporter, data);
			});
		},

		/**
		 * Returns a convenience object to trigger messages
		 *
		 * @method message
		 * @return {Event}
		 */
		message: function () {
			return this._messageEvent;
		},


		/**
		 * Parses a string to see if any listener can receive messages from it
		 *
		 * @method parse
		 * @param {string} text
		 * @param {object} [placeholder]
		 * @return {string}
		 */
		parse: function (text, placeholder) {
			var localText = text;

			_.each(this.getActiveListener(), function (listener) {
				localText = listener.parse(localText, placeholder);
			});

			return localText;
		},

		/**
		 * Loads a reporter hook and returns the system specific function to be supplied to the system
		 *
		 * @method loadHook
		 * @param {string} type
		 * @param {string|undefined} reportId
		 * @param {function} [finishedCallback]
		 * @return {function}
		 */
		loadHook: function (type, reportId, finishedCallback) {
			var hooks = ReportManager.getReporterHooks();

			if (hooks.hasOwnProperty(type)) {
				return hooks[type](this, reportId, finishedCallback);
			} else {
				throw new Error('Unknown reporter hook ' + type);
			}
		},


		/**
		 * Clears all run-data
		 *
		 * @method clear
		 */
		clear: function () {
			if (this.shouldCollect()) {
				this._container = new ReportContainer();
			}
			this._activeReporter = [];
			this._activeListener = [];
		}
	},

	{
		/**
		 * @var string
		 */
		TYPE: 'ReportManager'
	});

// Make abstract classes available to external project
/**
 * @property AbstractListener
 * @type {AbstractListener}
 * @static
 */
ReportManager.AbstractListener = AbstractListener;

/**
 * @property AbstractMessenger
 * @type {AbstractMessenger}
 * @static
 */
ReportManager.AbstractMessenger = AbstractMessenger;

/**
 * @property AbstractReporter
 * @type {AbstractReporter}
 * @static
 */
ReportManager.AbstractReporter = AbstractReporter;

/**
 * @property AbstractLoader
 * @type {AbstractLoader}
 * @static
 */
ReportManager.AbstractLoader = AbstractLoader;


/**
 * Gets a list of messengers
 *
 * @static
 * @method getMessengers
 * @return {object}
 */
ReportManager.getMessengers = function () {
	return {
		"JenkinsSauceLabs": require('./messenger/jenkinsSauceLabs'),
		"TeamCity": require('./messenger/teamCity'),
		"Preceptor": require('./messenger/preceptor')
	};
};

/**
 * Gets a list of loaders
 *
 * @static
 * @method getLoaders
 * @return {object}
 */
ReportManager.getLoaders = function () {
	return {
		"junit": require('./loader/junit'),
		"tap": require('./loader/tap')
	};
};

/**
 * Gets a list of reporter-hooks
 *
 * @static
 * @method getReporterHooks
 * @return {object}
 */
ReportManager.getReporterHooks = function () {
	return {
		"cucumber": require('./reporterHooks/cucumber'),
		"mocha": require('./reporterHooks/mocha')
	};
};

/**
 * Gets the module version
 *
 * @static
 * @method version
 * @return {string}
 */
ReportManager.version = require('../package.json').version;

module.exports = ReportManager;