- /*!
- *
- * Copyright (c) 2013 Sebastian Golasch
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- */
-
- 'use strict';
-
- // ext. libs
- var async = require('async');
- var EventEmitter2 = require('eventemitter2').EventEmitter2;
-
- // int. libs
- var Driver = require('./dalek/driver');
- var Reporter = require('./dalek/reporter');
- var Timer = require('./dalek/timer');
- var Config = require('./dalek/config');
- var Host = require('./dalek/host');
-
- /**
- * Default options
- * @type {Object}
- */
-
- var defaults = {
- reporter: ['console'],
- driver: ['native'],
- browser: ['phantomjs'],
- viewport: {width: 1280, height: 1024},
- logLevel: 3
- };
-
- /**
- * Setup all the options needed to configure dalek
- *
- * @param {options} opts Configuration options
- * @constructor
- */
-
- var Dalek = function (opts) {
- // setup instance
- this._initialize();
-
- // register exception handler
- this._registerExceptionHandler();
-
- // normalize options
- this.options = this.normalizeOptions(opts);
-
- // getting advanced options
- if (opts && opts.advanced) {
- this.advancedOptions = opts.advanced;
- }
-
- // initiate config
- this.config = new Config(defaults, this.options, this.advancedOptions);
-
- // override tests if provided on the commandline
- if (this.options.tests) {
- this.config.config.tests = this.options.tests;
- }
-
- // prepare and load reporter(s)
- this._setupReporters();
-
- // count all passed & failed assertions
- this.reporterEvents.on('report:assertion', this._onReportAssertion.bind(this));
-
- // init the timer instance
- this.timer = new Timer();
-
- // prepare driver event emitter instance
- this._setupDriverEmitter();
-
- // check for file option, throw error if none is given
- // only bypasses if dalek runs in the remote mode
- if (!Array.isArray(this.config.get('tests')) && !this.options.remote) {
- this.reporterEvents.emit('error', 'No test files given!');
- this.driverEmitter.emit('killAll');
- process.exit(127);
- }
-
- // init the driver instance
- this._initDriver();
-
- // check if dalek runs as a remote browser launcher
- if (this.options.remote) {
- var host = new Host({reporterEvents: this.reporterEvents, config: this.config});
- host.run({
- port: !isNaN(parseFloat(this.options.remote)) && isFinite(this.options.remote) ? this.options.remote : false
- });
- }
- };
-
- /**
- * Daleks base module
- * Used to configure all the things
- * and to start off the tests
- *
- * @module DalekJS
- * @class Dalek
- */
-
- Dalek.prototype = {
-
- /**
- * Runs the configured testsuites
- *
- * @method run
- * @chainable
- */
-
- run: function () {
- // early return; in case of remote
- if (this.options.remote) {
- return this;
- }
-
- // start the timer to measure the execution time
- this.timer.start();
-
- // emit the runner started event
- this.reporterEvents.emit('report:runner:started');
-
- // execute all given drivers sequentially
- var drivers = this.driver.getDrivers();
- async.series(drivers, this.testsuitesFinished.bind(this));
- return this;
- },
-
- /**
- * Reports the all testsuites executed event
- *
- * @method testsuitesFinished
- * @chainable
- */
-
- testsuitesFinished: function () {
- this.driverEmitter.emit('tests:complete');
- setTimeout(this.reportRunFinished.bind(this), 0);
- return this;
- },
-
- /**
- * Reports the all testsuites executed event
- *
- * @method reportRunFinished
- * @chainable
- */
-
- reportRunFinished: function () {
- this.reporterEvents.emit('report:runner:finished', {
- elapsedTime: this.timer.stop().getElapsedTimeFormatted(),
- assertions: this.assertionsFailed + this.assertionsPassed,
- assertionsFailed: this.assertionsFailed,
- assertionsPassed: this.assertionsPassed,
- status: this.runnerStatus
- });
-
- //we want to exit process with code 1 to single that test did not pass
- if(this.runnerStatus !== true) {
- var processExitCaptured = false;
-
- process.on('exit', function() {
- if(processExitCaptured === false) {
- processExitCaptured = true;
- process.exit(1);
- }
- });
- }
-
- return this;
- },
-
- /**
- * Normalizes options
- *
- * @method normalizeOptions
- * @param {object} options Raw options
- * @return {object} Normalized options
- */
-
- normalizeOptions: function (options) {
- Object.keys(options).forEach(function (key) {
- if ({reporter: 1, driver: 1}[key]) {
- options[key] = options[key].map(function (input) { return input.trim(); });
- }
- });
-
- return options;
- },
-
- /**
- * Sets up system env properties
- *
- * @method _initialize
- * @chainable
- * @private
- */
-
- _initialize: function () {
- // prepare error data
- this.warnings = [];
- this.errors = [];
-
- // prepare state data for the complete test run
- this.runnerStatus = true;
- this.assertionsFailed = 0;
- this.assertionsPassed = 0;
-
- return this;
- },
-
- /**
- * Sets up all the reporters
- *
- * @method _setupReporters
- * @chainable
- * @private
- */
-
- _setupReporters: function () {
- this.reporters = [];
- this.reporterEvents = new EventEmitter2();
- this.reporterEvents.setMaxListeners(Infinity);
- this.options.reporter = this.config.verifyReporters(this.config.get('reporter'), Reporter);
- this.options.reporter.forEach(this._addReporter, this);
- return this;
- },
-
- /**
- * Adds a reporter
- *
- * @method _addReporter
- * @param {string} reporter Name of the reporter to add
- * @chainable
- * @private
- */
-
- _addReporter: function (reporter) {
- this.reporters.push(Reporter.loadReporter(reporter, {events: this.reporterEvents, config: this.config, logLevel: this.config.get('logLevel')}));
- return this;
- },
-
- /**
- * Updates the assertion state
- *
- * @method _onReportAssertion
- * @param {object} assertion Informations aout the runned assertions
- * @chainable
- * @private
- */
-
- _onReportAssertion: function (assertion) {
- if (assertion.success) {
- this.assertionsPassed++;
- } else {
- this.runnerStatus = false;
- this.assertionsFailed++;
- }
- return this;
- },
-
- /**
- * Initizializes the driver instances
- *
- * @method _initDriver
- * @chainable
- * @private
- */
-
- _initDriver: function () {
- this.driver = new Driver({
- config: this.config,
- driverEmitter: this.driverEmitter,
- reporterEvents: this.reporterEvents
- });
-
- this.options.driver = this.config.verifyDrivers(this.config.get('driver'), this.driver);
- return this;
- },
-
- /**
- * Sets up the event dispatcher for driver events
- *
- * @method _setupDriverEmitter
- * @chainable
- * @private
- */
-
- _setupDriverEmitter: function () {
- var driverEmitter = new EventEmitter2();
- driverEmitter.setMaxListeners(Infinity);
- this.driverEmitter = driverEmitter;
- return this;
- },
-
- /**
- * Make sure to shutdown dalek & its spawned
- * components, webservers gracefully if a
- * runtime error pops up
- *
- * @method _registerExceptionHandler
- * @private
- * @chainable
- */
-
- _registerExceptionHandler: function () {
- process.setMaxListeners(Infinity);
- process.on('uncaughtException', this._shutdown.bind(this));
- return this;
- },
-
- /**
- * Shutdown on uncaught exception
- *
- * @method _shutdown
- * @param {object} exception Runtime exception
- * @private
- */
-
- _shutdown: function (exception) {
- // ios emulator hack, needs to go in the future
- if (exception.message && exception.message.search('This socket has been ended by the other party') !== -1) {
- return false;
- }
-
- this.driverEmitter.emit('killAll');
- this.reporterEvents.emit('error', exception);
- }
-
- };
-
- // export dalek as a module
- module.exports = Dalek;
-
-