pact-foundation/pact-js-mocha

View on GitHub
src/index.js

Summary

Maintainability
A
0 mins
Test Coverage
'use strict'

var Pact = require('pact')
var Mocha = require('mocha')
var Test = require('mocha/lib/test')
var Suite = require('mocha/lib/suite')
var escapeRe = require('escape-string-regexp')
var wrapper = require('@pact-foundation/pact-node')
var Common = require('mocha/lib/interfaces/common')
var Promise = require('bluebird')

/**
 * BDD-style interface mixed with Pact:
 *
 *      describe('Array', function() {
 *        describe('#indexOf()', function() {
 *          it('should return -1 when not present', function() {
 *            // ...
 *          });
 *
 *          it('should return the index when present', function() {
 *            // ...
 *          });
 *        });
 *      });
 *
 *      Pact('Consumer', 'Provider', 'http://url', function() {
 *        add((interaction) => {
 *            interaction
 *              .given('a state')
 *              .uponReceiving('a request for...')
 *              .withRequest('METHOD', '/PATH', BODY, { 'HeaderName': 'HeaderValue' })
 *              .willRespondWith(STATUS, { 'HeaderName': 'HeaderValue' }, EXPECTED_BODY)
 *        });
 *
 *        verify('interaction', functionThatReturnsPromise, (result, done) => {
 *          expect(JSON.parse(result)).to.eql(EXPECTED_BODY)
 *          done()
 *        })
 *      });
 * @param {Suite} suite Root suite.
 */
module.exports = Mocha.interfaces['bdd'] = function (suite) {
  var suites = [suite]

  suite.on('pre-require', function (context, file, mocha) {
    var common = Common(suites, context)

    context.before = common.before
    context.after = common.after
    context.beforeEach = common.beforeEach
    context.afterEach = common.afterEach
    context.run = mocha.options.delay && common.runWithSuite(suite)

    /**
     * Describe a "suite" with the given `title`
     * and callback `fn` containing nested suites
     * and/or tests.
     */

    context.describe = context.context = function (title, fn) {
      var suite = Suite.create(suites[0], title)
      suite.file = file
      suites.unshift(suite)
      fn.call(suite)
      suites.shift()
      return suite
    }

    /**
     * Pending describe.
     */

    context.xdescribe = context.xcontext = context.describe.skip = function (title, fn) {
      var suite = Suite.create(suites[0], title)
      suite.pending = true
      suites.unshift(suite)
      fn.call(suite)
      suites.shift()
    }

    /**
     * Top level function like describe above
     */

    context.PactConsumer = function (opts, fn) {
      var pactSuite = Suite.create(suites[0], opts.consumer + ' has pact with ' + opts.provider)
      pactSuite.file = file

      pactSuite.pactConsumer = opts.consumer
      pactSuite.pactProvider = opts.provider

      pactSuite.pact = Pact({ consumer: opts.consumer, provider: opts.provider, port: opts.providerPort })

      suites.unshift(pactSuite)
      fn.call(pactSuite, {})
      suites.shift()

      return pactSuite
    }

    context.PactProvider = function (opts, fn) {
      var pactSuite = Suite.create(suites[0], opts.consumer + ' has pact with ' + opts.provider)
      pactSuite.file = file

      pactSuite.pactConsumer = opts.consumer
      pactSuite.pactProvider = opts.provider

      suites.unshift(pactSuite)
      fn.call(pactSuite, {})
      suites.shift()

      return pactSuite
    }

    /**
     * Pending Pact.
     */

    context.xPact = context.xPactProvider = function (consumer, provider, fn) {
      var pactSuite = Suite.create(suites[0], consumer + ' has pact with ' + provider)
      pactSuite.pending = true
      suites.unshift(pactSuite)
      fn.call(pactSuite, {})
      suites.shift()
    }

    /**
     * Exclusive suite.
     */

    context.describe.only = function (title, fn) {
      var suite = context.describe(title, fn)
      mocha.grep(suite.fullTitle())
      return suite
    }

    /**
     * Describe a specification or test-case
     * with the given `title` and callback `fn`
     * acting as a thunk.
     */

    var it = context.it = context.specify = function (title, fn) {
      var suite = suites[0]
      if (suite.pending) {
        fn = null
      }
      var test = new Test(title, fn)
      test.file = file
      suite.addTest(test)
      return test
    }

    /**
     * Exclusive test-case.
     */

    context.it.only = function (title, fn) {
      var test = it(title, fn)
      var reString = '^' + escapeRe(test.fullTitle()) + '$'
      mocha.grep(new RegExp(reString))
      return test
    }

    context.addInteractions = function (interactions) {
      var pactSuite = suites[0]
      context.before(function (done) {
        var interactionsPromise = interactions.map(function (interaction) {
          return pactSuite.pact.addInteraction(interaction)
        })

        Promise.all(interactionsPromise).then(function () {
          done()
        })
      })
    }

    context.finalizePact = function () {
      var pactSuite = suites[0]
      context.after(function (done) {
        pactSuite.pact.finalize().then(function () {
          done()
        })
      })
    }

    /**
     * Verify works just like an it block but it starts the mock server
     * and runs the Pact verification promise chain.
     */

    context.verify = function (title, clientRequestFn, fn) {
      var pactSuite = suites[0]

      if (pactSuite.pending) {
        fn = null
      }

      var test = new Test(title, function (done) {
        clientRequestFn()
          .then(pactSuite.pact.verify)
          .then(function (data) {
            fn(data, done)
          })
          .catch(done)
      })

      test.file = file
      pactSuite.addTest(test)
      return test
    }

    context.honourPact = function (opts, fn) {
      var pactSuite = suites[0]

      if (pactSuite.pending) {
        fn = null
      }

      var test = new Test(opts.name || 'honours interactions', function (done) {
        wrapper.verifyPacts(opts)
          .then(function (data) {
            fn(data, done)
          })
          .catch(function (err) {
            done(err)
          })
      })

      test.file = file
      pactSuite.addTest(test)
      return test
    }

    /**
     * Pending verify.
     */

    context.xverify = context.verify.skip = function (title, fn) {
      context.verify(title)
    }

    /**
     * Pending test case.
     */

    context.xit = context.xspecify = context.it.skip = function (title) {
      context.it(title)
    }

    /**
     * Number of attempts to retry.
     */
    context.it.retries = function (n) {
      context.retries(n)
    }
  })
}