katrotz/splitify

View on GitHub
packages/meteor-jasmine/src/client/integration/ClientIntegrationTestFramework.js

Summary

Maintainability
B
6 hrs
Test Coverage
/* globals ClientIntegrationTestFramework: true */

ClientIntegrationTestFramework = function (options) {
  options = options || {}

  _.defaults(options, {
    name: 'jasmine-client-integration',
    regex: '^tests/jasmine/client/integration/.+\\.(js|es6|jsx|coffee|litcoffee|coffee\\.md)$',
    sampleTestGenerator: function () {
      return [
        {
          path: 'jasmine/client/integration/sample/spec/PlayerSpec.js',
          contents: Assets.getText('src/client/integration/sample-tests/sample/spec/PlayerSpec.js')
        },
        {
          path: 'jasmine/client/integration/sample/spec/SpecMatchers.js',
          contents: Assets.getText('src/client/integration/sample-tests/sample/spec/SpecMatchers.js')
        },
        {
          path: 'jasmine/client/integration/sample/src/Player.js',
          contents: Assets.getText('src/client/integration/sample-tests/sample/src/Player.js')
        },
        {
          path: 'jasmine/client/integration/sample/src/Song.js',
          contents: Assets.getText('src/client/integration/sample-tests/sample/src/Song.js')
        }
      ]
    },
    jasmineRequire: Meteor.isClient ? window.jasmineRequire : null
  })

  JasmineTestFramework.call(this, options)

  if (Meteor.isClient) {
    this._setup()
  }
}

ClientIntegrationTestFramework.prototype = Object.create(JasmineTestFramework.prototype)

_.extend(ClientIntegrationTestFramework.prototype, {

  _setup: function () {
    this.jasmine = this.jasmineRequire.core(this.jasmineRequire)
    this.jasmineInterface = new JasmineInterface({jasmine: this.jasmine})
    _.extend(window, this.jasmineInterface)
  },

  startMirror: function () {
    var self = this;
    var mirrorStarter = new MirrorStarter(this.name)
    var mirrorOptions = {}

    if (isTestPackagesMode()) {
      mirrorStarter.startSelfMirror(mirrorOptions)

      process.on('message', Meteor.bindEnvironment(function (message) {
        if (message && message.refresh === 'client') {
          // Listen for message 'on-listening' that signals that the application has been rebuild
          // and is ready to serve
          // * This callback *must* be registered here in 'on-message-refresh-client'
          // * because onListening is a short-lived registration that is removed after firing once
          WebApp.onListening(function () {
            Meteor.call('velocity/reports/reset', {framework: self.name})
          })
        }
      }))
    } else {
      _.extend(mirrorOptions, {
        port: this._getCustomPort(),
        testsPath: 'jasmine/client/integration'
      })

      if (process.env.JASMINE_CLIENT_MIRROR_APP_PATH) {
        mirrorOptions.args = [
          '--test-app-path', process.env.JASMINE_CLIENT_MIRROR_APP_PATH
        ]
      }

      mirrorStarter.lazyStartMirror(mirrorOptions)
    }
  },

  _getCustomPort: function () {
    var customPort = parseInt(process.env.JASMINE_MIRROR_PORT, 10)
    if (!_.isNaN(customPort)) {
      return customPort
    }
  },

  shouldRunTests: function (mirrorInfo) {
    return mirrorInfo.isTestPackagesMode ||
           (mirrorInfo.isMirror && mirrorInfo.framework === this.name)
  },

  runTests: function () {
    var self = this

    Meteor.call('jasmine/environmentInfo', function (error, mirrorInfo) {
      if (error) {
        throw error
      } else if (self.shouldRunTests(mirrorInfo)) {
        Meteor.defer(function() {
          log.info('Running Jasmine tests')

          var ddpConnection = mirrorInfo.isTestPackagesMode ?
            Meteor :
            DDP.connect(mirrorInfo.parentUrl)
          window.initJasmineJquery()
          self._executeClientTests(ddpConnection)
        })
      } else if (!mirrorInfo.isMirror && !self.inIframe()) {
        self.createMirrorIframe()
      }
    })
  },

  inIframe: function() {
    try {
      return window.self !== window.top;
    } catch (error) {
      return true;
    }
  },

  createMirrorIframe: function () {
    var self = this
    var iframeId = 'jasmine-mirror'

    var getMirrorUrl = function (mirrorInfo) {
      return mirrorInfo.rootUrl;
    }

    var insertMirrorIframe = _.once(function (mirrorInfo) {
      var iframe = document.createElement('iframe')
      iframe.id = iframeId
      iframe.src = getMirrorUrl(mirrorInfo);
      // Make the iFrame invisible
      iframe.style.display = 'block'
      iframe.style.position = 'absolute'
      iframe.style.width = 0
      iframe.style.height = 0
      iframe.style.border = 0
      document.body.appendChild(iframe)
    })

    var updateMirrorIframe = function (mirrorInfo) {
      var iframe = document.getElementById(iframeId)
      if (iframe) {
        iframe.src = getMirrorUrl(mirrorInfo)
      } else {
        insertMirrorIframe(mirrorInfo)
      }
    }

    Tracker.autorun(function () {
      var mirror = VelocityMirrors.findOne(
        {framework: self.name, state: 'ready'},
        {fields: {state: 1, rootUrl: 1, lastModified: 1}}
      )
      if (mirror) {
        updateMirrorIframe(mirror)
      }
    })
  },

  _executeClientTests: function (ddpConnection) {
    var self = this;

    window.ddpParentConnection = ddpConnection

    window.ddpParentConnection.call('velocity/reports/reset', {framework: self.name})

    /**
     * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference.
     */
    self.jasmineRequire.html(self.jasmine)

    /**
     * Create the Jasmine environment. This is used to run all specs in a project.
     */
    var env = self.jasmine.getEnv()

    /**
     * ## Runner Parameters
     *
     * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface.
     */

    var queryString = new self.jasmine.QueryString({
      getWindowLocation: function () {
        return window.location
      }
    })

    var catchingExceptions = queryString.getParam('catch')
    env.catchExceptions(typeof catchingExceptions === 'undefined' ? true : catchingExceptions)

    /**
     * ## Reporters
     */
    var velocityReporter = new VelocityTestReporter({
      mode: 'Client Integration',
      framework: self.name,
      env: env,
      timer: new self.jasmine.Timer(),
      ddpParentConnection: window.ddpParentConnection,
      isClient: true
    })

    /**
     * The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results  from JavaScript.
     */
    env.addReporter(self.jasmineInterface.jsApiReporter)
    env.addReporter(velocityReporter)

    /**
     * Filter which specs will be run by matching the start of the full name against the `spec` query param.
     */
    var specFilter = new self.jasmine.HtmlSpecFilter({
      filterString: function () {
        return queryString.getParam('spec')
      }
    })

    env.specFilter = function (spec) {
      return specFilter.matches(spec.getFullName())
    }

    /**
     * Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack.
     */
    window.setTimeout = window.setTimeout
    window.setInterval = window.setInterval
    window.clearTimeout = window.clearTimeout
    window.clearInterval = window.clearInterval

    env.execute()
  },

  _reportResults: function () {
    Meteor.call('velocity/reports/completed', {framework: this.name})
  }
})