reporter.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. var fs = require('fs');
  2. var path = require('path');
  3. var util = require('util');
  4. var mkpath = require('mkpath');
  5. var Logger = require('../util/logger.js');
  6. var colors = Logger.colors;
  7. var format = util.format;
  8. var Utils = require('../util/utils.js');
  9. var Q = require('q');
  10. var Reporter = module.exports = function(globalResults, testResults, globalStartTime, options) {
  11. this.globalResults = globalResults;
  12. this.testResults = testResults;
  13. this.globalStartTime = globalStartTime;
  14. this.options = options;
  15. this.reporter = options.reporter || 'junit';
  16. };
  17. var printf = function() {
  18. console.log(format.apply(null, arguments));
  19. };
  20. /**
  21. * @static
  22. * @param err
  23. * @param testname
  24. * @param time
  25. * @return {string}
  26. */
  27. Reporter.getTestOutput = function (err, testname, time) {
  28. var symbol;
  29. if (Utils.isErrorObject(err)) {
  30. symbol = Logger.colors.red(Utils.symbols.fail);
  31. testname = Logger.colors.red(testname);
  32. } else {
  33. symbol = Logger.colors.green(Utils.symbols.ok);
  34. }
  35. var args = [
  36. '%s %s', symbol, testname
  37. ];
  38. if (time > 20) {
  39. args[0] += ' %s';
  40. args.push(Logger.colors.yellow(Utils.format('(%s)', Utils.formatElapsedTime(time))));
  41. }
  42. return Utils.format.apply(null, args);
  43. };
  44. Reporter.printAssertions = function(test) {
  45. test.assertions.forEach(function(a) {
  46. if (a.failure !== false) {
  47. var message = a.stackTrace.split('\n');
  48. message.unshift(a.fullMsg);
  49. Utils.showStackTrace(message.join('\n'));
  50. }
  51. });
  52. if (test.stackTrace) {
  53. Utils.showStackTrace(test.stackTrace);
  54. }
  55. };
  56. Reporter.prototype.get = function() {
  57. var fileName = __dirname + '/reporters/' + this.reporter + '.js';
  58. if (fs.existsSync(fileName)) {
  59. return require(fileName);
  60. }
  61. fileName = path.resolve(this.reporter);
  62. if (fs.existsSync(fileName)) {
  63. var reporter = require(fileName);
  64. if (typeof reporter.write == 'function') {
  65. return reporter;
  66. }
  67. throw new Error('The reporter module must have a public `write` method defined.');
  68. }
  69. throw new Error('The reporter file name cannot be resolved. Using path: ' + fileName);
  70. };
  71. /**
  72. * @param {object} globals
  73. * @returns {function}
  74. */
  75. Reporter.prototype.globalReporter = function(globals) {
  76. var reporterFn = Utils.checkFunction('reporter', globals) || function() {};
  77. return Utils.makeFnAsync(2, reporterFn, globals);
  78. };
  79. Reporter.prototype.isDisabled = function() {
  80. return this.options.output_folder === false;
  81. };
  82. Reporter.prototype.createFolder = function(cb) {
  83. if (this.isDisabled()) {
  84. cb(null);
  85. } else {
  86. mkpath(this.options.output_folder, cb);
  87. }
  88. };
  89. Reporter.prototype.save = function() {
  90. var self = this;
  91. var deferred = Q.defer();
  92. this.createFolder(function(err) {
  93. if (self.isDisabled()) {
  94. deferred.resolve();
  95. } else {
  96. if (err) {
  97. deferred.reject(err);
  98. return;
  99. }
  100. var reporter = self.get();
  101. reporter.write(self.globalResults, self.options, function(err) {
  102. if (err) {
  103. console.log(colors.yellow(format('Warning: Failed to save report file to folder: %s', self.options.output_folder)));
  104. console.log(err.stack);
  105. }
  106. deferred.resolve(err);
  107. });
  108. }
  109. });
  110. return deferred.promise;
  111. };
  112. Reporter.prototype.printTotalResults = function() {
  113. var elapsedTime = new Date().getTime() - this.globalStartTime;
  114. process.stdout.write('\n');
  115. if (this.testsFailed()) {
  116. var countMessage = this.getTestsFailedMessage();
  117. console.log(colors.light_red(' _________________________________________________'));
  118. console.log(
  119. format('\n %s', colors.light_red('TEST FAILURE:')),
  120. countMessage,
  121. format('(%s)', Utils.formatElapsedTime(elapsedTime))
  122. );
  123. this.printFailureSummary();
  124. console.log('');
  125. } else {
  126. if (!this.shouldShowSummary()) {
  127. return;
  128. }
  129. var message = this.getTestsPassedMessage();
  130. printf('%s (%s)', message, Utils.formatElapsedTime(elapsedTime));
  131. }
  132. };
  133. Reporter.prototype.testsFailed = function() {
  134. return Object.keys(this.globalResults.modules).some(function(moduleKey) {
  135. return this.globalResults.modules[moduleKey].failures > 0 || this.globalResults.modules[moduleKey].errors > 0;
  136. }.bind(this));
  137. };
  138. Reporter.prototype.shouldShowSummary = function() {
  139. var modules = Object.keys(this.globalResults.modules);
  140. if (modules.length > 1) {
  141. return true;
  142. }
  143. if (modules.length <= 0) {
  144. return false;
  145. }
  146. return Object.keys(this.globalResults.modules[modules[0]].completed).length > 1;
  147. };
  148. Reporter.prototype.hasAssertionCount = function() {
  149. return Object.keys(this.globalResults.modules).length > 0 &&
  150. (this.globalResults.failed > 0 || this.globalResults.passed > 0);
  151. };
  152. Reporter.prototype.getTestsPassedMessage = function() {
  153. var hasCount = this.hasAssertionCount();
  154. var message;
  155. var count;
  156. if (hasCount) {
  157. count = this.globalResults.passed;
  158. message = colors.green(format('OK. %s %s passed.', count, (count > 1 ? ' total assertions' : ' assertion')));
  159. } else {
  160. count = this.getTotalTestsCount();
  161. message = format('%s tests passed.', colors.green('OK. ' + count));
  162. }
  163. return message;
  164. };
  165. Reporter.prototype.getTotalTestsCount = function() {
  166. var module;
  167. return Object.keys(this.globalResults.modules).reduce(function(count, moduleKey) {
  168. module = this.globalResults.modules[moduleKey];
  169. return count + module.tests - module.skipped.length;
  170. }.bind(this), 0);
  171. };
  172. Reporter.prototype.getTestsFailedMessage = function() {
  173. var hasCount = this.hasAssertionCount();
  174. if (!hasCount && this.testResults.errmessages === 0) {
  175. return '';
  176. }
  177. var errorsMsg = '';
  178. var failedMsg = 'assertions';
  179. var passedMsg = format('%s passed', colors.green(this.globalResults.passed));
  180. if (!this.options.start_session) {
  181. failedMsg = 'tests';
  182. var passedCount = Math.max(0, this.getTotalTestsCount() - this.globalResults.failed);
  183. passedMsg = format('%s passed', colors.green(passedCount));
  184. }
  185. var skipped = '';
  186. if (this.testResults.skipped) {
  187. skipped = format(' and %s skipped', colors.cyan(this.testResults.skipped));
  188. }
  189. var globalErrors = this.globalResults.errors || this.testResults.errors;
  190. if (globalErrors) {
  191. var suffix = globalErrors > 1 ? 's' : '';
  192. errorsMsg += format('%s error%s during execution, ', colors.red(globalErrors), suffix);
  193. }
  194. return format('%s %s %s failed, %s%s.', errorsMsg, colors.red(this.globalResults.failed), failedMsg, passedMsg, skipped);
  195. };
  196. Reporter.prototype.printFailureSummary = function() {
  197. Object.keys(this.globalResults.modules).forEach(function(moduleKey) {
  198. var testSuite = this.globalResults.modules[moduleKey];
  199. if (testSuite.failures > 0 || testSuite.errors > 0) {
  200. console.log('\n' + colors.red(format(' %s %s', Utils.symbols.fail, moduleKey)));
  201. Object.keys(testSuite.completed).forEach(function(testcase) {
  202. var test = testSuite.completed[testcase];
  203. if (test.failed > 0 || test.errors > 0) {
  204. printf('\n - %s %s', testcase, colors.yellow('(' + Utils.formatElapsedTime(test.timeMs) + ')'));
  205. if (test.assertions.length > 0 && this.options.start_session) {
  206. Reporter.printAssertions(test);
  207. } else if (test.stackTrace) {
  208. Utils.showStackTrace(test.stackTrace);
  209. }
  210. }
  211. }.bind(this));
  212. if (Array.isArray(testSuite.errmessages)) {
  213. testSuite.errmessages.forEach(function(err) {
  214. console.log('');
  215. Utils.showStackTrace(err);
  216. console.log('');
  217. });
  218. }
  219. if (testSuite.skipped.length > 0) {
  220. console.log(colors.cyan(' SKIPPED:'));
  221. testSuite.skipped.forEach(function(testcase) {
  222. printf(' - %s', testcase);
  223. });
  224. }
  225. }
  226. }.bind(this));
  227. if (Array.isArray(this.globalResults.errmessages) && Object.keys(this.globalResults.modules).length === 0) {
  228. this.globalResults.errmessages.forEach(function(err) {
  229. console.log('');
  230. Utils.showStackTrace(err);
  231. console.log('');
  232. }.bind(this));
  233. }
  234. };