/**
 * External dependencies.
 */

var extend = require('super').extend;

/**
 * Store `Date` locally since modules like `Timekeeper`
 * can modify it.
 */

var D = Date;

/**
 * Base Test.
 *
 * @param {String} title
 * @param {Function} fn
 * @param {Array} meta
 * @constructor
 */

function Base(title, fn, meta) {
  this.title = title;
  this.meta = meta || [];
  this.fn = fn;
  this.status = null;
  this.error = null;
  this.time = null;
  this.context = {};
  this.meta = Array.isArray(this.meta) ? this.meta : [this.meta];
  this.events = {
    pre: 'pre:test',
    post: 'post:test',
  };

  if (!this.fn) {
    this.pending();
  }
}

/**
 * Extend.
 */

Base.extend = extend;

/**
 * Configure test timeout.
 *
 * @param {Number} ms
 * @api public
 */

Base.prototype.timeout = function(ms) {
  this._timeout = ms;
  return this;
};

/**
 * Mark the test as skipped.
 *
 * @api public
 */

Base.prototype.skip = function(condition) {
  if (arguments.length && !condition) return;
  this.status = 'skipped';
  this.time = 0;
};

/**
 * Mark the test as pending.
 *
 * @returns {Base} self
 * @api public
 */

Base.prototype.pending = function() {
  this.status = 'pending';
  this.time = 0;
  return this;
};

/**
 * Run the test.
 *
 * @param {Object} emitter
 * @param {Function} done
 * @api public
 */

Base.prototype.run = function(emitter, done) {
  var self = this;
  var events = this.events;
  var disabled = this.status === 'pending' || this.status === 'skipped';

  emitter.emit(events.pre, this, function() {
    if (disabled) return emitter.emit(events.post, self, done);
    var start = (new D).getTime();
    self.exec(function(err) {
      self.time = (new D).getTime() - start;
      if (err) self.fail(err);
      else self.pass();
      emitter.emit(events.post, self, done);
    });
  });
};

/**
 * Mark the test as failed.
 *
 * @param {Object} error
 * @returns {Base} self
 * @api private
 */

Base.prototype.fail = function(err) {
  this.status = 'failed';
  this.error = err;
  return this;
};

/**
 * Mark the test as passed.
 *
 * @returns {Base} self
 * @api private
 */

Base.prototype.pass = function() {
  this.status = 'passed';
  return this;
};

/**
 * Primary export.
 */

module.exports = Base;

/**
 * External dependencies.
 */

var extend = require('super').extend;

/**
 * Store `Date` locally since modules like `Timekeeper`
 * can modify it.
 */

var D = Date;

/**
 * Base Test.
 *
 * @param {String} title
 * @param {Function} fn
 * @param {Array} meta
 * @constructor
 */

function Base(title, fn, meta) {
  this.title = title;
  this.meta = meta || [];
  this.fn = fn;
  this.status = null;
  this.error = null;
  this.time = null;
  this.context = {};
  this.meta = Array.isArray(this.meta) ? this.meta : [this.meta];
  this.events = {
    pre: 'pre:test',
    post: 'post:test',
  };

  if (!this.fn) {
    this.pending();
  }
}

/**
 * Extend.
 */

Base.extend = extend;

/**
 * Configure test timeout.
 *
 * @param {Number} ms
 * @api public
 */

Base.prototype.timeout = function(ms) {
  this._timeout = ms;
  return this;
};

/**
 * Mark the test as skipped.
 *
 * @api public
 */

Base.prototype.skip = function(condition) {
  if (arguments.length && !condition) return;
  this.status = 'skipped';
  this.time = 0;
};

/**
 * Mark the test as pending.
 *
 * @returns {Base} self
 * @api public
 */

Base.prototype.pending = function() {
  this.status = 'pending';
  this.time = 0;
  return this;
};

/**
 * Run the test.
 *
 * @param {Object} emitter
 * @param {Function} done
 * @api public
 */

Base.prototype.run = function(emitter, done) {
  var self = this;
  var events = this.events;
  var disabled = this.status === 'pending' || this.status === 'skipped';

  emitter.emit(events.pre, this, function() {
    var disabled = self.status === 'pending' || self.status === 'skipped';
    if (disabled) return emitter.emit(events.post, self, done);
    var start = (new D).getTime();
    self.exec(function(err) {
      self.time = (new D).getTime() - start;
      if (err) self.fail(err);
      else self.pass();
      emitter.emit(events.post, self, done);
    });
  });
};

/**
 * Mark the test as failed.
 *
 * @param {Object} error
 * @returns {Base} self
 * @api private
 */

Base.prototype.fail = function(err) {
  this.status = 'failed';
  this.error = err;
  return this;
};

/**
 * Mark the test as passed.
 *
 * @returns {Base} self
 * @api private
 */

Base.prototype.pass = function() {
  this.status = 'passed';
  return this;
};

/**
 * Primary export.
 */

module.exports = Base;

Allow tests to be skipped from a plugin