runner.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. var create = require('lodash.create');
  2. var utils = require('../utils');
  3. var filter = utils.filter;
  4. var indexOf = utils.indexOf;
  5. var some = utils.some;
  6. var NightwatchClient = require('../nightwatch/client');
  7. var MochaRunner = require('../runner');
  8. module.exports = Runner;
  9. function Runner(suite, delay) {
  10. MochaRunner.call(this, suite, delay);
  11. }
  12. Runner.prototype = create(MochaRunner.prototype, {
  13. constructor : Runner
  14. });
  15. Runner.prototype.run = function(nightwatch, test_settings, fn) {
  16. var self = this;
  17. var rootSuite = this.suite;
  18. var client = new NightwatchClient(nightwatch, this, test_settings);
  19. if (this.hasOnly) {
  20. filterOnly(rootSuite);
  21. }
  22. fn = fn || function() {};
  23. function uncaught(err) {
  24. self.uncaught(err);
  25. }
  26. function setClient(test) {
  27. test.setNightwatchClient(client.get());
  28. }
  29. function start() {
  30. self.emit('start');
  31. self.runSuite(rootSuite, function() {
  32. self.emit('end');
  33. });
  34. self.on('test', setClient);
  35. self.on('hook', setClient);
  36. }
  37. // callback
  38. this.on('end', function() {
  39. process.removeListener('uncaughtException', uncaught);
  40. fn(self.failures);
  41. });
  42. // uncaught exception
  43. process.on('uncaughtException', uncaught);
  44. if (this._delay) {
  45. // for reporters, I guess.
  46. // might be nice to debounce some dots while we wait.
  47. this.emit('waiting', rootSuite);
  48. rootSuite.once('run', start);
  49. } else {
  50. start();
  51. }
  52. return this;
  53. };
  54. Runner.prototype.failOnError = function(err) {
  55. var runnable = this.currentRunnable;
  56. if (!runnable) {
  57. return;
  58. }
  59. runnable.clearTimeout();
  60. // Ignore errors if complete
  61. if (runnable.state) {
  62. return;
  63. }
  64. this.fail(runnable, err);
  65. // recover from test
  66. if (runnable.type === 'test') {
  67. this.emit('test end', runnable);
  68. this.hookUp('afterEach', this.next);
  69. return;
  70. }
  71. // bail on hooks
  72. this.emit('end');
  73. };
  74. /**
  75. * Filter suites based on `isOnly` logic.
  76. *
  77. * @param {Array} suite
  78. * @returns {Boolean}
  79. * @api private
  80. */
  81. function filterOnly (suite) {
  82. if (suite._onlyTests.length) {
  83. // If the suite contains `only` tests, run those and ignore any nested suites.
  84. suite.tests = suite._onlyTests;
  85. suite.suites = [];
  86. } else {
  87. // Otherwise, do not run any of the tests in this suite.
  88. suite.tests = [];
  89. utils.forEach(suite._onlySuites, function (onlySuite) {
  90. // If there are other `only` tests/suites nested in the current `only` suite, then filter that `only` suite.
  91. // Otherwise, all of the tests on this `only` suite should be run, so don't filter it.
  92. if (hasOnly(onlySuite)) {
  93. filterOnly(onlySuite);
  94. }
  95. });
  96. // Run the `only` suites, as well as any other suites that have `only` tests/suites as descendants.
  97. suite.suites = filter(suite.suites, function (childSuite) {
  98. return indexOf(suite._onlySuites, childSuite) !== -1 || filterOnly(childSuite);
  99. });
  100. }
  101. // Keep the suite only if there is something to run
  102. return suite.tests.length || suite.suites.length;
  103. }
  104. /**
  105. * Determines whether a suite has an `only` test or suite as a descendant.
  106. *
  107. * @param {Array} suite
  108. * @returns {Boolean}
  109. * @api private
  110. */
  111. function hasOnly (suite) {
  112. return suite._onlyTests.length || suite._onlySuites.length || some(suite.suites, hasOnly);
  113. }