'use strict';

var Suite = require('../suite'); var errors = require('../errors'); var createMissingArgumentError = errors.createMissingArgumentError;

/**

* Functions common to more than one interface.
*
* @param {Suite[]} suites
* @param {Context} context
* @param {Mocha} mocha
* @return {Object} An object containing common functions.
*/

module.exports = function(suites, context, mocha) {

/**
 * Check if the suite should be tested.
 *
 * @private
 * @param {Suite} suite - suite to check
 * @returns {boolean}
 */
function shouldBeTested(suite) {
  return (
    !mocha.options.grep ||
    (mocha.options.grep &&
      mocha.options.grep.test(suite.fullTitle()) &&
      !mocha.options.invert)
  );
}

return {
  /**
   * This is only present if flag --delay is passed into Mocha. It triggers
   * root suite execution.
   *
   * @param {Suite} suite The root suite.
   * @return {Function} A function which runs the root suite
   */
  runWithSuite: function runWithSuite(suite) {
    return function run() {
      suite.run();
    };
  },

  /**
   * Execute before running tests.
   *
   * @param {string} name
   * @param {Function} fn
   */
  before: function(name, fn) {
    suites[0].beforeAll(name, fn);
  },

  /**
   * Execute after running tests.
   *
   * @param {string} name
   * @param {Function} fn
   */
  after: function(name, fn) {
    suites[0].afterAll(name, fn);
  },

  /**
   * Execute before each test case.
   *
   * @param {string} name
   * @param {Function} fn
   */
  beforeEach: function(name, fn) {
    suites[0].beforeEach(name, fn);
  },

  /**
   * Execute after each test case.
   *
   * @param {string} name
   * @param {Function} fn
   */
  afterEach: function(name, fn) {
    suites[0].afterEach(name, fn);
  },

  suite: {
    /**
     * Create an exclusive Suite; convenience function
     * See docstring for create() below.
     *
     * @param {Object} opts
     * @returns {Suite}
     */
    only: function only(opts) {
      opts.isOnly = true;
      return this.create(opts);
    },

    /**
     * Create a Suite, but skip it; convenience function
     * See docstring for create() below.
     *
     * @param {Object} opts
     * @returns {Suite}
     */
    skip: function skip(opts) {
      opts.pending = true;
      return this.create(opts);
    },

    /**
     * Creates a suite.
     *
     * @param {Object} opts Options
     * @param {string} opts.title Title of Suite
     * @param {Function} [opts.fn] Suite Function (not always applicable)
     * @param {boolean} [opts.pending] Is Suite pending?
     * @param {string} [opts.file] Filepath where this Suite resides
     * @param {boolean} [opts.isOnly] Is Suite exclusive?
     * @returns {Suite}
     */
    create: function create(opts) {
      var suite = Suite.create(suites[0], opts.title);
      suite.pending = Boolean(opts.pending);
      suite.file = opts.file;
      suites.unshift(suite);
      if (opts.isOnly) {
        if (mocha.options.forbidOnly && shouldBeTested(suite)) {
          throw new Error('`.only` forbidden');
        }

        suite.parent.appendOnlySuite(suite);
      }
      if (suite.pending) {
        if (mocha.options.forbidPending && shouldBeTested(suite)) {
          throw new Error('Pending test forbidden');
        }
      }
      if (typeof opts.fn === 'function') {
        opts.fn.call(suite);
        suites.shift();
      } else if (typeof opts.fn === 'undefined' && !suite.pending) {
        throw createMissingArgumentError(
          'Suite "' +
            suite.fullTitle() +
            '" was defined but no callback was supplied. ' +
            'Supply a callback or explicitly skip the suite.',
          'callback',
          'function'
        );
      } else if (!opts.fn && suite.pending) {
        suites.shift();
      }

      return suite;
    }
  },

  test: {
    /**
     * Exclusive test-case.
     *
     * @param {Object} mocha
     * @param {Function} test
     * @returns {*}
     */
    only: function(mocha, test) {
      test.parent.appendOnlyTest(test);
      return test;
    },

    /**
     * Pending test case.
     *
     * @param {string} title
     */
    skip: function(title) {
      context.test(title);
    },

    /**
     * Number of retry attempts
     *
     * @param {number} n
     */
    retries: function(n) {
      context.retries(n);
    }
  }
};

};