MrBoolean/http-test

View on GitHub
suite.js

Summary

Maintainability
A
2 hrs
Test Coverage
/**
 * @module http-test/suite
 */

var Promise = require('bluebird');
var EventEmitter = require('eventemitter2').EventEmitter2;
var inherit = require('util').inherits;
var isString = require('lodash.isstring');
var through2 = require('through2');
var Test = require('./test');
var Base = require('./base');

/**
 * Creates a new suite.
 *
 * @param {object} options
 */
function Suite(options) {
  var self = this;

  options = options || {};

  Suite.super_.apply(this, [options]);

  this._ee = new EventEmitter();
  this.name = options.name || null;
  this.description = options.description || null;

  this.tests = [];

  if (options.tests) {
    options.tests.forEach(function eachTest(test) {
      self.add(test);
    });
  }
}

inherit(Suite, Base);

/**
 * Adds a listener to the end of the listeners array for the specified event.
 *
 * @param  {string}   name
 * @param  {function} callback
 * @return {Suite}
 */
Suite.prototype.on = function on(name, callback) {
  this._ee.on(name, callback);
  return this;
};

Suite.prototype.emit = function emit(name, args) {
  this._ee.emit(name, args);
  return this;
};

Suite.prototype.off = function off(name, callback) {
  this._ee.off(name, callback);
  return this;
};

Suite.prototype.hasListener = function hasListener(name, callback) {
  var affected = this._ee._events[name];
  var index;

  if (!affected) {
    return false;
  }

  if (!callback) {
    return true;
  }

  if (Array.isArray(affected) && callback) {
    for (index = 0; index < affected.length; index++) {
      if (callback === affected[index]) {
        return true;
      }
    }
  }

  return affected === callback;
};

/**
 * An abstract method which accepts suites, tests and urls (strings).
 *
 * @param {mixed} item
 * @return {Suite}
 */
Suite.prototype.add = function add(item) {
  if (isString(item)) {
    item = new Test({ req: { url: item } }, this);
  }

  if (!(item instanceof Test) && !(item instanceof Suite)) {
    item = new Test(item);
  }

  item.suite = this;
  this.tests.push(item);
  return this;
};

Suite.prototype.run = function run() {
  var suite = this;
  var offAny = [];

  function broadcast(test) {
    suite.emit('test', test);
  }

  function walk(item) {
    if (!(item instanceof Suite)) {
      return;
    }

    if (!item.hasListener('broadcast.test')) {
      item.on('broadcast.test', broadcast);
      offAny.push(function off() {
        item.off('broadcast.test', broadcast);
      });
    }

    item.tests.forEach(walk);
  }

  this.tests.forEach(walk);

  return Promise
    .map(this.tests, function each(item) {
      return item.run();
    })
    .then(function summarize(result) {
      return {
        suite: {
          name: suite.name,
          description: suite.description
        },
        tests: result.map(function mapResult(item) {
          return item;
        })
      };
    })
    .finally(function unbindBroadcasts() {
      offAny.forEach(function eachOff(off) {
        off();
      });
    })
  ;
};

Suite.prototype.runAsStream = function runAsStream() {
  var self = this;
  var stream = through2.obj(function eachTest(test, encoding, next) {
    this.push(test);
    next();
  });

  function onTest(item) {
    stream.push(item);
  }

  self
    .on('test', onTest)
    .run()
    .finally(function unbindOnTest() {
      self.off('test', onTest);
      stream.end();
    })
  ;

  return stream;
};

module.exports = Suite;