tunnckoCore/gruu-api

View on GitHub
src/plugins/core-methods.js

Summary

Maintainability
B
5 hrs
Test Coverage
/*!
 * gruu-api <https://github.com/tunnckoCore/gruu-api>
 *
 * Copyright (c) Charlike Mike Reagent <@tunnckoCore> (https://i.am.charlike.online)
 * Released under the MIT license.
 */

'use strict'

import utils from '../utils.js'
import flow from 'each-promise'
import getName from 'get-fn-name'

export default function coreMethods () {
  return function coreMethods_ (app) {
    /**
     * > Add test function.
     *
     * @param {[type]}   title [description]
     * @param {Function} fn    [description]
     * @api public
     */

    app.define('add', function add (title, fn) {
      if (typeof title === 'function') {
        fn = title
        title = null
      }
      if (typeof fn !== 'function') {
        var err = new TypeError('.add: expect `fn` to be a function')
        app.emit('error', err)
        return app
      }

      title = typeof title === 'string' ? title : getName(fn)
      if (title === null) {
        app.stats.anonymous++
      }

      app.stats.count++
      app.titles.push(title)
      app.tests.push(utils.promisify(fn, app.options))
      return app
    })

    /**
     * > Extends (and clone) `app.options` with given `opts`.
     *
     * @param  {[type]} opts [description]
     * @return {[type]}      [description]
     * @api public
     */

    app.define('option', function option (opts) {
      app.options = utils.extend({}, app.options, opts)
      return app
    })

    /**
     * > Run test suite. Given `options` are merged
     * with `app.options`, using `app.option` method.
     *
     * @param  {[type]} options [description]
     * @return {[type]}         [description]
     * @api public
     */

    app.define('run', function run (options) {
      app.option(options)

      const reporter = typeof app.options.reporter === 'function'
        ? app.options.reporter
        : function noopPlugin () {}

      app.use(reporter)

      return utils.promisify((done) => {
        app.option(defineHooks(app, done))
        flow.each(app.tests, app.options)
      }, app.options)()
    })

    return app
  }
}

/**
 * Utilities / Helpers
 */

var defineHooks = (app, done) => ({
  start () {
    app.emit('start', app)
  },

  beforeEach (item) {
    const test = makeTest(app, item)
    app.emit('beforeEach', app, test)
  },

  afterEach (item) {
    const test = utils.extend(makeTest(app, item), reflect(item))
    app.emit('afterEach', app, test)

    if (test.isRejected) {
      app.stats.fail++
      app.emit('fail', app, test)
    } else {
      app.stats.pass++
      app.emit('pass', app, test)
    }

    if (test.isRejected && app.options.settle === false) {
      throw test.reason
    } else {
      return test
    }
  },
  finish (err, results) {
    app.reason = err
    app.value = results

    // merge all from `app` to err if `err` exists
    // so we can use it as `app` but in same time
    // follow the `error-first` style
    utils.extend(err, app)

    if (err) {
      // emit `finish` from the `error` if is need,
      // much better than emitting both events here
      app.emit('error', err)
      done(err, results)
      return
    }
    app.emit('finish', app)
    done(err, results)
  }
})

// helper
var makeTest = (app, item) => ({
  title: app.titles[item.index],
  index: item.index + 1,
  value: item.value
})

// helper
var reflect = (item) => (item.reason ? {
  isRejected: true,
  isFulfilled: false,
  reason: item.reason
} : {
  isRejected: false,
  isFulfilled: true,
  value: item.value
})