xunit.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. /**
  2. * Module dependencies.
  3. */
  4. var Base = require('./base');
  5. var utils = require('../utils');
  6. var inherits = utils.inherits;
  7. var fs = require('fs');
  8. var escape = utils.escape;
  9. var mkdirp = require('mkdirp');
  10. var path = require('path');
  11. /**
  12. * Save timer references to avoid Sinon interfering (see GH-237).
  13. */
  14. /* eslint-disable no-unused-vars, no-native-reassign */
  15. var Date = global.Date;
  16. var setTimeout = global.setTimeout;
  17. var setInterval = global.setInterval;
  18. var clearTimeout = global.clearTimeout;
  19. var clearInterval = global.clearInterval;
  20. /* eslint-enable no-unused-vars, no-native-reassign */
  21. /**
  22. * Expose `XUnit`.
  23. */
  24. exports = module.exports = XUnit;
  25. /**
  26. * Initialize a new `XUnit` reporter.
  27. *
  28. * @api public
  29. * @param {Runner} runner
  30. */
  31. function XUnit(runner, options) {
  32. Base.call(this, runner);
  33. var stats = this.stats;
  34. var tests = [];
  35. var self = this;
  36. if (options.reporterOptions && options.reporterOptions.output) {
  37. if (!fs.createWriteStream) {
  38. throw new Error('file output not supported in browser');
  39. }
  40. mkdirp.sync(path.dirname(options.reporterOptions.output));
  41. self.fileStream = fs.createWriteStream(options.reporterOptions.output);
  42. }
  43. runner.on('pending', function(test) {
  44. tests.push(test);
  45. });
  46. runner.on('pass', function(test) {
  47. tests.push(test);
  48. });
  49. runner.on('fail', function(test) {
  50. tests.push(test);
  51. });
  52. runner.on('end', function() {
  53. self.write(tag('testsuite', {
  54. name: 'Mocha Tests',
  55. tests: stats.tests,
  56. failures: stats.failures,
  57. errors: stats.failures,
  58. skipped: stats.tests - stats.failures - stats.passes,
  59. timestamp: (new Date()).toUTCString(),
  60. time: (stats.duration / 1000) || 0
  61. }, false));
  62. tests.forEach(function(t) {
  63. self.test(t);
  64. });
  65. self.write('</testsuite>');
  66. });
  67. }
  68. /**
  69. * Inherit from `Base.prototype`.
  70. */
  71. inherits(XUnit, Base);
  72. /**
  73. * Override done to close the stream (if it's a file).
  74. *
  75. * @param failures
  76. * @param {Function} fn
  77. */
  78. XUnit.prototype.done = function(failures, fn) {
  79. if (this.fileStream) {
  80. this.fileStream.end(function() {
  81. fn(failures);
  82. });
  83. } else {
  84. fn(failures);
  85. }
  86. };
  87. /**
  88. * Write out the given line.
  89. *
  90. * @param {string} line
  91. */
  92. XUnit.prototype.write = function(line) {
  93. if (this.fileStream) {
  94. this.fileStream.write(line + '\n');
  95. } else if (typeof process === 'object' && process.stdout) {
  96. process.stdout.write(line + '\n');
  97. } else {
  98. console.log(line);
  99. }
  100. };
  101. /**
  102. * Output tag for the given `test.`
  103. *
  104. * @param {Test} test
  105. */
  106. XUnit.prototype.test = function(test) {
  107. var attrs = {
  108. classname: test.parent.fullTitle(),
  109. name: test.title,
  110. time: (test.duration / 1000) || 0
  111. };
  112. if (test.state === 'failed') {
  113. var err = test.err;
  114. this.write(tag('testcase', attrs, false, tag('failure', {}, false, escape(err.message) + '\n' + escape(err.stack))));
  115. } else if (test.isPending()) {
  116. this.write(tag('testcase', attrs, false, tag('skipped', {}, true)));
  117. } else {
  118. this.write(tag('testcase', attrs, true));
  119. }
  120. };
  121. /**
  122. * HTML tag helper.
  123. *
  124. * @param name
  125. * @param attrs
  126. * @param close
  127. * @param content
  128. * @return {string}
  129. */
  130. function tag(name, attrs, close, content) {
  131. var end = close ? '/>' : '>';
  132. var pairs = [];
  133. var tag;
  134. for (var key in attrs) {
  135. if (Object.prototype.hasOwnProperty.call(attrs, key)) {
  136. pairs.push(key + '="' + escape(attrs[key]) + '"');
  137. }
  138. }
  139. tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end;
  140. if (content) {
  141. tag += content + '</' + name + end;
  142. }
  143. return tag;
  144. }