json-cov.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  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. * @param {Runner} runner
  13. * @param {Boolean} output
  14. * @api public
  15. */
  16. function JSONCov(runner, output) {
  17. var self = this
  18. , output = 1 == arguments.length ? true : output;
  19. Base.call(this, runner);
  20. var tests = []
  21. , failures = []
  22. , 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) return;
  40. process.stdout.write(JSON.stringify(result, null, 2 ));
  41. });
  42. }
  43. /**
  44. * Map jscoverage data to a JSON structure
  45. * suitable for reporting.
  46. *
  47. * @param {Object} cov
  48. * @return {Object}
  49. * @api private
  50. */
  51. function map(cov) {
  52. var ret = {
  53. instrumentation: 'node-jscoverage'
  54. , sloc: 0
  55. , hits: 0
  56. , misses: 0
  57. , coverage: 0
  58. , files: []
  59. };
  60. for (var filename in cov) {
  61. var data = coverage(filename, cov[filename]);
  62. ret.files.push(data);
  63. ret.hits += data.hits;
  64. ret.misses += data.misses;
  65. ret.sloc += data.sloc;
  66. }
  67. ret.files.sort(function(a, b) {
  68. return a.filename.localeCompare(b.filename);
  69. });
  70. if (ret.sloc > 0) {
  71. ret.coverage = (ret.hits / ret.sloc) * 100;
  72. }
  73. return ret;
  74. };
  75. /**
  76. * Map jscoverage data for a single source file
  77. * to a JSON structure suitable for reporting.
  78. *
  79. * @param {String} filename name of the source file
  80. * @param {Object} data jscoverage coverage data
  81. * @return {Object}
  82. * @api private
  83. */
  84. function coverage(filename, data) {
  85. var ret = {
  86. filename: filename,
  87. coverage: 0,
  88. hits: 0,
  89. misses: 0,
  90. sloc: 0,
  91. source: {}
  92. };
  93. data.source.forEach(function(line, num){
  94. num++;
  95. if (data[num] === 0) {
  96. ret.misses++;
  97. ret.sloc++;
  98. } else if (data[num] !== undefined) {
  99. ret.hits++;
  100. ret.sloc++;
  101. }
  102. ret.source[num] = {
  103. source: line
  104. , coverage: data[num] === undefined
  105. ? ''
  106. : data[num]
  107. };
  108. });
  109. ret.coverage = ret.hits / ret.sloc * 100;
  110. return ret;
  111. }
  112. /**
  113. * Return a plain-object representation of `test`
  114. * free of cyclic properties etc.
  115. *
  116. * @param {Object} test
  117. * @return {Object}
  118. * @api private
  119. */
  120. function clean(test) {
  121. return {
  122. title: test.title
  123. , fullTitle: test.fullTitle()
  124. , duration: test.duration
  125. }
  126. }