index.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. 'use strict';
  2. /*
  3. Copyright 2012-2015, Yahoo Inc.
  4. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
  5. */
  6. const path = require('path');
  7. const { escape } = require('html-escaper');
  8. const { ReportBase } = require('istanbul-lib-report');
  9. class CoberturaReport extends ReportBase {
  10. constructor(opts) {
  11. super();
  12. opts = opts || {};
  13. this.cw = null;
  14. this.xml = null;
  15. this.timestamp = opts.timestamp || Date.now().toString();
  16. this.projectRoot = opts.projectRoot || process.cwd();
  17. this.file = opts.file || 'cobertura-coverage.xml';
  18. }
  19. onStart(root, context) {
  20. this.cw = context.writer.writeFile(this.file);
  21. this.xml = context.getXMLWriter(this.cw);
  22. this.writeRootStats(root);
  23. }
  24. onEnd() {
  25. this.xml.closeAll();
  26. this.cw.close();
  27. }
  28. writeRootStats(node) {
  29. const metrics = node.getCoverageSummary();
  30. this.cw.println('<?xml version="1.0" ?>');
  31. this.cw.println(
  32. '<!DOCTYPE coverage SYSTEM "http://cobertura.sourceforge.net/xml/coverage-04.dtd">'
  33. );
  34. this.xml.openTag('coverage', {
  35. 'lines-valid': metrics.lines.total,
  36. 'lines-covered': metrics.lines.covered,
  37. 'line-rate': metrics.lines.pct / 100.0,
  38. 'branches-valid': metrics.branches.total,
  39. 'branches-covered': metrics.branches.covered,
  40. 'branch-rate': metrics.branches.pct / 100.0,
  41. timestamp: this.timestamp,
  42. complexity: '0',
  43. version: '0.1'
  44. });
  45. this.xml.openTag('sources');
  46. this.xml.inlineTag('source', null, this.projectRoot);
  47. this.xml.closeTag('sources');
  48. this.xml.openTag('packages');
  49. }
  50. onSummary(node) {
  51. const metrics = node.getCoverageSummary(true);
  52. if (!metrics) {
  53. return;
  54. }
  55. this.xml.openTag('package', {
  56. name: node.isRoot() ? 'main' : escape(asJavaPackage(node)),
  57. 'line-rate': metrics.lines.pct / 100.0,
  58. 'branch-rate': metrics.branches.pct / 100.0
  59. });
  60. this.xml.openTag('classes');
  61. }
  62. onSummaryEnd(node) {
  63. const metrics = node.getCoverageSummary(true);
  64. if (!metrics) {
  65. return;
  66. }
  67. this.xml.closeTag('classes');
  68. this.xml.closeTag('package');
  69. }
  70. onDetail(node) {
  71. const fileCoverage = node.getFileCoverage();
  72. const metrics = node.getCoverageSummary();
  73. const branchByLine = fileCoverage.getBranchCoverageByLine();
  74. this.xml.openTag('class', {
  75. name: escape(asClassName(node)),
  76. filename: path.relative(this.projectRoot, fileCoverage.path),
  77. 'line-rate': metrics.lines.pct / 100.0,
  78. 'branch-rate': metrics.branches.pct / 100.0
  79. });
  80. this.xml.openTag('methods');
  81. const fnMap = fileCoverage.fnMap;
  82. Object.entries(fnMap).forEach(([k, { name, decl }]) => {
  83. const hits = fileCoverage.f[k];
  84. this.xml.openTag('method', {
  85. name: escape(name),
  86. hits,
  87. signature: '()V' //fake out a no-args void return
  88. });
  89. this.xml.openTag('lines');
  90. //Add the function definition line and hits so that jenkins cobertura plugin records method hits
  91. this.xml.inlineTag('line', {
  92. number: decl.start.line,
  93. hits
  94. });
  95. this.xml.closeTag('lines');
  96. this.xml.closeTag('method');
  97. });
  98. this.xml.closeTag('methods');
  99. this.xml.openTag('lines');
  100. const lines = fileCoverage.getLineCoverage();
  101. Object.entries(lines).forEach(([k, hits]) => {
  102. const attrs = {
  103. number: k,
  104. hits,
  105. branch: 'false'
  106. };
  107. const branchDetail = branchByLine[k];
  108. if (branchDetail) {
  109. attrs.branch = true;
  110. attrs['condition-coverage'] =
  111. branchDetail.coverage +
  112. '% (' +
  113. branchDetail.covered +
  114. '/' +
  115. branchDetail.total +
  116. ')';
  117. }
  118. this.xml.inlineTag('line', attrs);
  119. });
  120. this.xml.closeTag('lines');
  121. this.xml.closeTag('class');
  122. }
  123. }
  124. function asJavaPackage(node) {
  125. return node
  126. .getRelativeName()
  127. .replace(/\//g, '.')
  128. .replace(/\\/g, '.')
  129. .replace(/\.$/, '');
  130. }
  131. function asClassName(node) {
  132. return node.getRelativeName().replace(/.*[\\/]/, '');
  133. }
  134. module.exports = CoberturaReport;