API Docs for: 0.0.4
Show:

File: index.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 _ = require('lodash');
  29. var Q = require('q');
  30.  
  31. // Int. libs
  32. var actions = require('dalek-internal-actions');
  33. var assertions = require('dalek-internal-assertions');
  34.  
  35. /**
  36. * Prepares the test instance values
  37. *
  38. * @param {object} opts Options like the tests name, etc.
  39. * @constructor
  40. */
  41.  
  42. var Test = function (opts) {
  43. // prepare meta queue data
  44. this.actionPromiseQueue = [];
  45.  
  46. // prepare assertion data
  47. this.expactation = null;
  48. this.runnedExpactations = 0;
  49. this.failedAssertions = 0;
  50.  
  51. // prepare test specific data
  52. this.name = opts.name;
  53. this.lastChain = [];
  54. this.uuids = {};
  55. this.contextVars = {};
  56.  
  57. if (this.name) {
  58. this.timeoutForDone = setTimeout(function () {
  59. this.done();
  60. this.reporter.emit('warning', 'done not called!');
  61. }.bind(this), 2000);
  62. }
  63. };
  64.  
  65. /**
  66. * Generates an test instance
  67. *
  68. * @module DalekJS
  69. * @class Test
  70. * @namespace Dalek
  71. * @part test
  72. * @api
  73. */
  74.  
  75. Test.prototype = {
  76.  
  77. /**
  78. * Specify how many assertions are expected to run within a test.
  79. * Very useful for ensuring that all your callbacks and assertions are run.
  80. *
  81. * @method expect
  82. * @param {Integer} expecatation Number of assertions that should be run
  83. * @chainable
  84. */
  85.  
  86. expect: function (expectation) {
  87. this.expectation = parseInt(expectation, 10);
  88. return this;
  89. },
  90.  
  91. /**
  92. * Global data store (works between the node & browser envs)
  93. *
  94. * @method expect
  95. * @param {string|number} key Key to store or fetch data
  96. * @param {mixed} value *optional* Data that should be stored
  97. * @return {mixed} Data that has been stored
  98. * @chainable
  99. */
  100.  
  101. data: function (key, value) {
  102. if (value) {
  103. this.contextVars[key] = value;
  104. return this;
  105. }
  106.  
  107. return this.contextVars[key];
  108. },
  109.  
  110. /**
  111. * Increment the number of executed assertions
  112. *
  113. * @method incrementExpectations
  114. * @chainable
  115. */
  116.  
  117. incrementExpectations: function () {
  118. this.runnedExpactations++;
  119. return this;
  120. },
  121.  
  122. /**
  123. * Increment the number of failed assertions
  124. *
  125. * @method incrementFailedAssertions
  126. * @chainable
  127. */
  128.  
  129. incrementFailedAssertions: function () {
  130. this.failedAssertions++;
  131. return this;
  132. },
  133.  
  134. /**
  135. * Checks if the runned tests fullfill the set expectations
  136. * or if no expectations were raised
  137. *
  138. * @method checkExpectations
  139. * @return {bool} checkedExpectations Expectations match
  140. */
  141.  
  142. checkExpectations: function () {
  143. return (this.expectation === null || !this.expectation || (this.runnedExpactations === this.expectation));
  144. },
  145.  
  146. /**
  147. * Checks if all runned assertions passed
  148. *
  149. * @method checkAssertions
  150. * @return {bool} assertiosnFailed Any expectation failed
  151. */
  152.  
  153. checkAssertions: function () {
  154. return this.failedAssertions === 0;
  155. },
  156.  
  157. /**
  158. * Sets up all the bindings needed for a test to run
  159. *
  160. * @method done
  161. * @return {object} result A promise
  162. * @private
  163. */
  164.  
  165. done: function () {
  166. var result = Q.resolve();
  167. // clear the done error timeout
  168. clearTimeout(this.timeoutForDone);
  169. // remove all previously attached event listeners to clear the message queue
  170. this.driver.events.removeAllListeners('driver:message');
  171. // resolve the deferred when the test is finished
  172. Test.testStarted.fin(this._testFinished.bind(this, result));
  173. return result;
  174. },
  175.  
  176. /**
  177. * Emits the test finished events & resolves all promises
  178. * when its done
  179. *
  180. * @method _testFinished
  181. * @param {object} result Promised result var
  182. * @return {object} result Promised result var
  183. * @private
  184. */
  185.  
  186. _testFinished: function (result) {
  187. // add a last deferred function on the end of the action queue,
  188. // to tell that this test is finished
  189. this.actionPromiseQueue.push(this._testFin.bind(this));
  190.  
  191. // initialize all of the event receiver functions,
  192. // that later take the driver result
  193. this.actionPromiseQueue.forEach(function (f) {
  194. result = result.then(f).fail(function () {
  195. console.error(arguments);
  196. process.exit(0);
  197. });
  198. }.bind(this));
  199.  
  200. // run the driver when all actions are stored in the queue
  201. Q.allSettled(this.actionPromiseQueue)
  202. .then(this.driver.run.bind(this.driver));
  203.  
  204. return result;
  205. },
  206.  
  207. /**
  208. * Emits the test started event
  209. *
  210. * @method _reportTestStarted
  211. * @param {string} name Name of the test
  212. * @chainable
  213. * @private
  214. */
  215.  
  216. _reportTestStarted: function (name) {
  217. this.reporter.emit('report:test:started', {name: name});
  218. return this;
  219. },
  220.  
  221. /**
  222. * Checks if the test run is complete & emits/resolves
  223. * all the needed events/promises when the run is complete
  224. *
  225. * @method _onDriverMessage
  226. * @param {object} data Data that is returned by the driver:message event
  227. * @chainable
  228. * @private
  229. */
  230.  
  231. _onDriverMessage: function (data) {
  232. // check if the test run is complete
  233. if (data && data.key === 'run.complete') {
  234. // emit the test finish events & resolve the deferred
  235. this._emitConcreteTestFinished();
  236. this._emitAssertionStatus();
  237. this._emitTestFinished();
  238. this.deferred.resolve();
  239. }
  240.  
  241. return this;
  242. },
  243.  
  244. /**
  245. * Emits an event, that the current test run has been finished
  246. *
  247. * @method _emitConcreteTestFinished
  248. * @chainable
  249. * @private
  250. */
  251.  
  252. _emitConcreteTestFinished: function () {
  253. this.events.emit('test:' + this._uid + ':finished', 'test:finished', this);
  254. return this;
  255. },
  256.  
  257. /**
  258. * Emits an event that describes the current state of all assertions
  259. *
  260. * @method _emitAssertionStatus
  261. * @chainable
  262. * @private
  263. */
  264.  
  265. _emitAssertionStatus: function () {
  266. this.reporter.emit('report:assertion:status', {
  267. expected: (this.expectation ? this.expectation : this.runnedExpactations),
  268. run: this.runnedExpactations,
  269. status: this._testStatus()
  270. });
  271. return this;
  272. },
  273.  
  274. /**
  275. * Get the overall test status (assertions & expectation)
  276. *
  277. * @method _testStatus
  278. * @return {bool} status The test status
  279. * @chainable
  280. * @private
  281. */
  282.  
  283. _testStatus: function () {
  284. return this.checkExpectations() && this.checkAssertions();
  285. },
  286.  
  287. /**
  288. * Emits an event that describes the current state of all assertions.
  289. * The event should be fired when a test is finished
  290. *
  291. * @method _emitTestFinished
  292. * @chainable
  293. * @private
  294. */
  295.  
  296. _emitTestFinished: function () {
  297. this.reporter.emit('report:test:finished', {
  298. name: this.name,
  299. id: this._uid,
  300. passedAssertions: this.runnedExpactations - this.failedAssertions,
  301. failedAssertions: this.failedAssertions,
  302. runnedExpactations: this.runnedExpactations,
  303. status: this._testStatus(),
  304. nl: true
  305. });
  306.  
  307. return this;
  308. },
  309.  
  310. /**
  311. * Kicks off the test & binds all promises/events
  312. *
  313. * @method _testFin
  314. * @return {object} promise A promise
  315. * @private
  316. */
  317.  
  318. _testFin: function () {
  319. this.deferred = Q.defer();
  320.  
  321. if (_.isFunction(this.driver.end)) {
  322. this.driver.end();
  323. }
  324.  
  325. // emit report startet event
  326. this._reportTestStarted(this.name);
  327.  
  328. // listen to all the messages from the driver
  329. this.driver.events.on('driver:message', this._onDriverMessage.bind(this));
  330. return this.deferred.promise;
  331. },
  332.  
  333. /**
  334. * Copies assertion methods
  335. *
  336. * @method _inheritAssertions
  337. * @param {Test} test Instacne of test
  338. * @chainable
  339. * @private
  340. */
  341.  
  342. _inheritAssertions: function (test) {
  343. ['is'].forEach(function (method) {
  344. test[method] = test.assert[method].bind(test.assert);
  345. });
  346. return test;
  347. },
  348.  
  349. /**
  350. * Copies assertion helper methods
  351. *
  352. * @method _inheritAssertions
  353. * @param {Test} test Instacne of test
  354. * @chainable
  355. * @private
  356. */
  357.  
  358. _inheritAssertionHelpers: function (test) {
  359. ['not', 'between', 'gt', 'gte', 'lt', 'lte'].forEach(function (method) {
  360. test.is[method] = test.assert[method].bind(test.assert);
  361. test.assert.is[method] = test.assert[method].bind(test.assert);
  362. });
  363. ['contain', 'match'].forEach(function (method) {
  364. test.to = test.to || {};
  365. test.assert.to = test.assert.to || {};
  366.  
  367. test.to[method] = test.assert[method].bind(test.assert);
  368. test.assert.to[method] = test.assert[method].bind(test.assert);
  369. });
  370. return test;
  371. },
  372.  
  373. /**
  374. * Set up the instance
  375. *
  376. * @method _inheritAssertions
  377. * @param {Test} test Instacne of test
  378. * @param {object} opts Options
  379. * @chainable
  380. * @private
  381. */
  382.  
  383. _initialize: function (test, opts) {
  384. test._uid = _.uniqueId('test');
  385. test.events = opts.events;
  386. test.driver = opts.driver;
  387. test.reporter = opts.reporter;
  388. return test;
  389. }
  390.  
  391. };
  392.  
  393. // export a function that generates a new test instance
  394. module.exports = function (opts) {
  395. // mixin assertions, actions & getters
  396. Test.prototype = _.extend(Test.prototype, actions({reporter: opts.reporter}).prototype);
  397. var test = new Test(opts);
  398. test.assert = new (assertions())({test: test});
  399. test.assert.done = test.done.bind(this);
  400. test.assert.query = test.query.bind(test.assert);
  401. test.assert.$ = test.query.bind(test.assert);
  402. test.end = test.assert.end.bind(test.assert);
  403.  
  404. // copy log methods
  405. test.log = {};
  406. test.log.dom = test.logger.dom.bind(test);
  407. test.log.message = test.logger.message.bind(test);
  408.  
  409. // copy assertions methods
  410. test = test._inheritAssertions(test);
  411.  
  412. // copy assertion helper methods
  413. test = test._inheritAssertionHelpers(test);
  414.  
  415. // initialize the instance
  416. test = test._initialize(test, opts);
  417.  
  418. // TODO: Promise driver start
  419. // so that we can reexecute them and clean the env between tests
  420. Test.testStarted = test.driver.start(Q);
  421. return test;
  422. };
  423.