API Docs for: 0.0.9
Show:

File: lib/dalek.js

  1. /*!
  2. *
  3. * Copyright (c) 2013 Sebastian Golasch
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a
  6. * copy of this software and associated documentation files (the "Software"),
  7. * to deal in the Software without restriction, including without limitation
  8. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  9. * and/or sell copies of the Software, and to permit persons to whom the
  10. * Software is furnished to do so, subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be included
  13. * in all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  16. * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  18. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  21. * DEALINGS IN THE SOFTWARE.
  22. *
  23. */
  24.  
  25. 'use strict';
  26.  
  27. // ext. libs
  28. var async = require('async');
  29. var EventEmitter2 = require('eventemitter2').EventEmitter2;
  30.  
  31. // int. libs
  32. var Driver = require('./dalek/driver');
  33. var Reporter = require('./dalek/reporter');
  34. var Timer = require('./dalek/timer');
  35. var Config = require('./dalek/config');
  36. var Host = require('./dalek/host');
  37.  
  38. /**
  39. * Default options
  40. * @type {Object}
  41. */
  42.  
  43. var defaults = {
  44. reporter: ['console'],
  45. driver: ['native'],
  46. browser: ['phantomjs'],
  47. viewport: {width: 1280, height: 1024},
  48. logLevel: 3
  49. };
  50.  
  51. /**
  52. * Setup all the options needed to configure dalek
  53. *
  54. * @param {options} opts Configuration options
  55. * @constructor
  56. */
  57.  
  58. var Dalek = function (opts) {
  59. // setup instance
  60. this._initialize();
  61.  
  62. // register exception handler
  63. this._registerExceptionHandler();
  64.  
  65. // normalize options
  66. this.options = this.normalizeOptions(opts);
  67.  
  68. // getting advanced options
  69. if (opts && opts.advanced) {
  70. this.advancedOptions = opts.advanced;
  71. }
  72.  
  73. // initiate config
  74. this.config = new Config(defaults, this.options, this.advancedOptions);
  75.  
  76. // override tests if provided on the commandline
  77. if (this.options.tests) {
  78. this.config.config.tests = this.options.tests;
  79. }
  80.  
  81. // prepare and load reporter(s)
  82. this._setupReporters();
  83.  
  84. // count all passed & failed assertions
  85. this.reporterEvents.on('report:assertion', this._onReportAssertion.bind(this));
  86.  
  87. // init the timer instance
  88. this.timer = new Timer();
  89.  
  90. // prepare driver event emitter instance
  91. this._setupDriverEmitter();
  92.  
  93. // check for file option, throw error if none is given
  94. // only bypasses if dalek runs in the remote mode
  95. if (!Array.isArray(this.config.get('tests')) && !this.options.remote) {
  96. this.reporterEvents.emit('error', 'No test files given!');
  97. this.driverEmitter.emit('killAll');
  98. process.exit(127);
  99. }
  100.  
  101. // init the driver instance
  102. this._initDriver();
  103.  
  104. // check if dalek runs as a remote browser launcher
  105. if (this.options.remote) {
  106. var host = new Host({reporterEvents: this.reporterEvents, config: this.config});
  107. host.run({
  108. port: !isNaN(parseFloat(this.options.remote)) && isFinite(this.options.remote) ? this.options.remote : false
  109. });
  110. }
  111. };
  112.  
  113. /**
  114. * Daleks base module
  115. * Used to configure all the things
  116. * and to start off the tests
  117. *
  118. * @module DalekJS
  119. * @class Dalek
  120. */
  121.  
  122. Dalek.prototype = {
  123.  
  124. /**
  125. * Runs the configured testsuites
  126. *
  127. * @method run
  128. * @chainable
  129. */
  130.  
  131. run: function () {
  132. // early return; in case of remote
  133. if (this.options.remote) {
  134. return this;
  135. }
  136.  
  137. // start the timer to measure the execution time
  138. this.timer.start();
  139.  
  140. // emit the runner started event
  141. this.reporterEvents.emit('report:runner:started');
  142.  
  143. // execute all given drivers sequentially
  144. var drivers = this.driver.getDrivers();
  145. async.series(drivers, this.testsuitesFinished.bind(this));
  146. return this;
  147. },
  148.  
  149. /**
  150. * Reports the all testsuites executed event
  151. *
  152. * @method testsuitesFinished
  153. * @chainable
  154. */
  155.  
  156. testsuitesFinished: function () {
  157. this.driverEmitter.emit('tests:complete');
  158. setTimeout(this.reportRunFinished.bind(this), 0);
  159. return this;
  160. },
  161.  
  162. /**
  163. * Reports the all testsuites executed event
  164. *
  165. * @method reportRunFinished
  166. * @chainable
  167. */
  168.  
  169. reportRunFinished: function () {
  170. this.reporterEvents.emit('report:runner:finished', {
  171. elapsedTime: this.timer.stop().getElapsedTimeFormatted(),
  172. assertions: this.assertionsFailed + this.assertionsPassed,
  173. assertionsFailed: this.assertionsFailed,
  174. assertionsPassed: this.assertionsPassed,
  175. status: this.runnerStatus
  176. });
  177.  
  178. //we want to exit process with code 1 to single that test did not pass
  179. if(this.runnerStatus !== true) {
  180. var processExitCaptured = false;
  181.  
  182. process.on('exit', function() {
  183. if(processExitCaptured === false) {
  184. processExitCaptured = true;
  185. process.exit(1);
  186. }
  187. });
  188. }
  189.  
  190. return this;
  191. },
  192.  
  193. /**
  194. * Normalizes options
  195. *
  196. * @method normalizeOptions
  197. * @param {object} options Raw options
  198. * @return {object} Normalized options
  199. */
  200.  
  201. normalizeOptions: function (options) {
  202. Object.keys(options).forEach(function (key) {
  203. if ({reporter: 1, driver: 1}[key]) {
  204. options[key] = options[key].map(function (input) { return input.trim(); });
  205. }
  206. });
  207.  
  208. return options;
  209. },
  210.  
  211. /**
  212. * Sets up system env properties
  213. *
  214. * @method _initialize
  215. * @chainable
  216. * @private
  217. */
  218.  
  219. _initialize: function () {
  220. // prepare error data
  221. this.warnings = [];
  222. this.errors = [];
  223.  
  224. // prepare state data for the complete test run
  225. this.runnerStatus = true;
  226. this.assertionsFailed = 0;
  227. this.assertionsPassed = 0;
  228.  
  229. return this;
  230. },
  231.  
  232. /**
  233. * Sets up all the reporters
  234. *
  235. * @method _setupReporters
  236. * @chainable
  237. * @private
  238. */
  239.  
  240. _setupReporters: function () {
  241. this.reporters = [];
  242. this.reporterEvents = new EventEmitter2();
  243. this.reporterEvents.setMaxListeners(Infinity);
  244. this.options.reporter = this.config.verifyReporters(this.config.get('reporter'), Reporter);
  245. this.options.reporter.forEach(this._addReporter, this);
  246. return this;
  247. },
  248.  
  249. /**
  250. * Adds a reporter
  251. *
  252. * @method _addReporter
  253. * @param {string} reporter Name of the reporter to add
  254. * @chainable
  255. * @private
  256. */
  257.  
  258. _addReporter: function (reporter) {
  259. this.reporters.push(Reporter.loadReporter(reporter, {events: this.reporterEvents, config: this.config, logLevel: this.config.get('logLevel')}));
  260. return this;
  261. },
  262.  
  263. /**
  264. * Updates the assertion state
  265. *
  266. * @method _onReportAssertion
  267. * @param {object} assertion Informations aout the runned assertions
  268. * @chainable
  269. * @private
  270. */
  271.  
  272. _onReportAssertion: function (assertion) {
  273. if (assertion.success) {
  274. this.assertionsPassed++;
  275. } else {
  276. this.runnerStatus = false;
  277. this.assertionsFailed++;
  278. }
  279. return this;
  280. },
  281.  
  282. /**
  283. * Initizializes the driver instances
  284. *
  285. * @method _initDriver
  286. * @chainable
  287. * @private
  288. */
  289.  
  290. _initDriver: function () {
  291. this.driver = new Driver({
  292. config: this.config,
  293. driverEmitter: this.driverEmitter,
  294. reporterEvents: this.reporterEvents
  295. });
  296.  
  297. this.options.driver = this.config.verifyDrivers(this.config.get('driver'), this.driver);
  298. return this;
  299. },
  300.  
  301. /**
  302. * Sets up the event dispatcher for driver events
  303. *
  304. * @method _setupDriverEmitter
  305. * @chainable
  306. * @private
  307. */
  308.  
  309. _setupDriverEmitter: function () {
  310. var driverEmitter = new EventEmitter2();
  311. driverEmitter.setMaxListeners(Infinity);
  312. this.driverEmitter = driverEmitter;
  313. return this;
  314. },
  315.  
  316. /**
  317. * Make sure to shutdown dalek & its spawned
  318. * components, webservers gracefully if a
  319. * runtime error pops up
  320. *
  321. * @method _registerExceptionHandler
  322. * @private
  323. * @chainable
  324. */
  325.  
  326. _registerExceptionHandler: function () {
  327. process.setMaxListeners(Infinity);
  328. process.on('uncaughtException', this._shutdown.bind(this));
  329. return this;
  330. },
  331.  
  332. /**
  333. * Shutdown on uncaught exception
  334. *
  335. * @method _shutdown
  336. * @param {object} exception Runtime exception
  337. * @private
  338. */
  339.  
  340. _shutdown: function (exception) {
  341. // ios emulator hack, needs to go in the future
  342. if (exception.message && exception.message.search('This socket has been ended by the other party') !== -1) {
  343. return false;
  344. }
  345.  
  346. this.driverEmitter.emit('killAll');
  347. this.reporterEvents.emit('error', exception);
  348. }
  349.  
  350. };
  351.  
  352. // export dalek as a module
  353. module.exports = Dalek;
  354.