json-cov.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. /**
  2. * Module dependencies.
  3. */
  4. var Base = require('./base');
  5. /**
  6. * Expose `JSONCov`.
  7. */
  8. exports = module.exports = JSONCov;
  9. /**
  10. * Initialize a new `JsCoverage` reporter.
  11. *
  12. * @api public
  13. * @param {Runner} runner
  14. * @param {boolean} output
  15. */
  16. function JSONCov(runner, output) {
  17. Base.call(this, runner);
  18. output = arguments.length === 1 || output;
  19. var self = this;
  20. var tests = [];
  21. var failures = [];
  22. var passes = [];
  23. runner.on('test end', function(test) {
  24. tests.push(test);
  25. });
  26. runner.on('pass', function(test) {
  27. passes.push(test);
  28. });
  29. runner.on('fail', function(test) {
  30. failures.push(test);
  31. });
  32. runner.on('end', function() {
  33. var cov = global._$jscoverage || {};
  34. var result = self.cov = map(cov);
  35. result.stats = self.stats;
  36. result.tests = tests.map(clean);
  37. result.failures = failures.map(clean);
  38. result.passes = passes.map(clean);
  39. if (!output) {
  40. return;
  41. }
  42. process.stdout.write(JSON.stringify(result, null, 2));
  43. });
  44. }
  45. /**
  46. * Map jscoverage data to a JSON structure
  47. * suitable for reporting.
  48. *
  49. * @api private
  50. * @param {Object} cov
  51. * @return {Object}
  52. */
  53. function map(cov) {
  54. var ret = {
  55. instrumentation: 'node-jscoverage',
  56. sloc: 0,
  57. hits: 0,
  58. misses: 0,
  59. coverage: 0,
  60. files: []
  61. };
  62. for (var filename in cov) {
  63. if (Object.prototype.hasOwnProperty.call(cov, filename)) {
  64. var data = coverage(filename, cov[filename]);
  65. ret.files.push(data);
  66. ret.hits += data.hits;
  67. ret.misses += data.misses;
  68. ret.sloc += data.sloc;
  69. }
  70. }
  71. ret.files.sort(function(a, b) {
  72. return a.filename.localeCompare(b.filename);
  73. });
  74. if (ret.sloc > 0) {
  75. ret.coverage = (ret.hits / ret.sloc) * 100;
  76. }
  77. return ret;
  78. }
  79. /**
  80. * Map jscoverage data for a single source file
  81. * to a JSON structure suitable for reporting.
  82. *
  83. * @api private
  84. * @param {string} filename name of the source file
  85. * @param {Object} data jscoverage coverage data
  86. * @return {Object}
  87. */
  88. function coverage(filename, data) {
  89. var ret = {
  90. filename: filename,
  91. coverage: 0,
  92. hits: 0,
  93. misses: 0,
  94. sloc: 0,
  95. source: {}
  96. };
  97. data.source.forEach(function(line, num) {
  98. num++;
  99. if (data[num] === 0) {
  100. ret.misses++;
  101. ret.sloc++;
  102. } else if (data[num] !== undefined) {
  103. ret.hits++;
  104. ret.sloc++;
  105. }
  106. ret.source[num] = {
  107. source: line,
  108. coverage: data[num] === undefined ? '' : data[num]
  109. };
  110. });
  111. ret.coverage = ret.hits / ret.sloc * 100;
  112. return ret;
  113. }
  114. /**
  115. * Return a plain-object representation of `test`
  116. * free of cyclic properties etc.
  117. *
  118. * @api private
  119. * @param {Object} test
  120. * @return {Object}
  121. */
  122. function clean(test) {
  123. return {
  124. duration: test.duration,
  125. currentRetry: test.currentRetry(),
  126. fullTitle: test.fullTitle(),
  127. title: test.title
  128. };
  129. }