runnable.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. var create = require('lodash.create');
  2. var MochaRunnable = require('../runnable');
  3. var utils = require('../utils');
  4. module.exports = Runnable;
  5. function Runnable(title, fn) {
  6. this.title = title;
  7. this.fn = fn;
  8. this.async = 1;
  9. this.sync = false;
  10. this._timeout = 20000;
  11. this._slow = 75;
  12. this._enableTimeouts = true;
  13. this.timedOut = false;
  14. this._trace = new Error('done() called multiple times');
  15. this._retries = -1;
  16. this._currentRetry = 0;
  17. this.pending = false;
  18. }
  19. Runnable.prototype = create(MochaRunnable.prototype, {
  20. constructor: Runnable
  21. });
  22. Runnable.prototype.setNightwatchClient = function(client) {
  23. this._nightwatch = client;
  24. this._nightwatch.clearGlobalResult();
  25. return this;
  26. };
  27. /**
  28. * Run the test and invoke `fn(err)`.
  29. *
  30. * @api private
  31. * @param {Function} fn
  32. */
  33. Runnable.prototype.run = function(fn) {
  34. var self = this;
  35. var start = new Date();
  36. var ctx = this.ctx;
  37. var finished;
  38. var emitted;
  39. // Sometimes the ctx exists, but it is not runnable
  40. if (ctx && ctx.runnable) {
  41. ctx.runnable(this);
  42. }
  43. // called multiple times
  44. function multiple(err) {
  45. if (emitted) {
  46. return;
  47. }
  48. emitted = true;
  49. self.emit('error', err || new Error('done() called multiple times; stacktrace may be inaccurate'));
  50. }
  51. // finished
  52. function done(err) {
  53. if (self.timedOut) {
  54. return;
  55. }
  56. if (finished) {
  57. return multiple(err || self._trace);
  58. }
  59. self.clearTimeout();
  60. self.duration = new Date() - start;
  61. finished = true;
  62. fn(err);
  63. }
  64. // for .resetTimeout()
  65. this.callback = done;
  66. try {
  67. if (this.type == 'test') {
  68. var module = [];
  69. if (this.parent.parent.title) {
  70. module.push(this.parent.parent.title);
  71. }
  72. module.push(this.parent.title);
  73. var moduleTitle = module.join('/').toLocaleLowerCase().replace(/\s+/g,'-');
  74. this._nightwatch.api('currentTest', {
  75. name : this.title,
  76. module : moduleTitle
  77. });
  78. }
  79. var args = [this._nightwatch.api()];
  80. if (this.type == 'hook') {
  81. args.push(done);
  82. // explicit async with `done` argument
  83. this.resetTimeout();
  84. }
  85. // TODO: support sync hooks
  86. this.fn.apply(ctx, args);
  87. if (this.type == 'hook' && this.title == '"before each" hook') {
  88. // don't restart the queue for before each
  89. return;
  90. }
  91. if (this.type == 'test') {
  92. this._nightwatch.once('complete', function() {
  93. self.duration = new Date() - start;
  94. finished = true;
  95. var results = this.results();
  96. var err = null;
  97. if (results.failed > 0 || results.errors > 0) {
  98. err = results.lastError;
  99. }
  100. fn(err);
  101. });
  102. }
  103. if (this._nightwatch.shouldRestartQueue()) {
  104. this._nightwatch.start();
  105. }
  106. } catch (err) {
  107. done(utils.getError(err));
  108. }
  109. };