Skip to content

Commit

Permalink
Merge pull request #594 from bhofmei/feat-unit-test
Browse files Browse the repository at this point in the history
feat(app): add unit test coverage summary page
  • Loading branch information
vogloblinsky committed Jul 25, 2018
2 parents 4a44505 + 0ec2c13 commit 0a1e248
Show file tree
Hide file tree
Showing 15 changed files with 370 additions and 12 deletions.
114 changes: 114 additions & 0 deletions src/app/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,12 @@ export class Application {
});
}

if (this.configuration.mainData.unitTestCoverage !== ''){
actions.push(()=>{
return this.prepareUnitTestCoverage();
});
}

if (this.configuration.mainData.includes !== '') {
actions.push(() => {
return this.prepareExternalIncludes();
Expand Down Expand Up @@ -1994,6 +2000,7 @@ at least one config for the 'info' or 'source' tab in --navTabConfig.`);
if (this.configuration.mainData.exportFormat === COMPODOC_DEFAULTS.exportFormat) {
this.htmlEngine.generateCoverageBadge(
this.configuration.mainData.output,
'documentation',
coverageData
);
}
Expand Down Expand Up @@ -2149,6 +2156,113 @@ at least one config for the 'info' or 'source' tab in --navTabConfig.`);
}
});
}
public prepareUnitTestCoverage() {
logger.info('Process unit test coverage report');
return new Promise((resolve, reject)=>{
let covDat, covFileNames;

if (!this.configuration.mainData.coverageData['files']){
logger.warn('Missing documentation coverage data');
} else {
covDat = {};
covFileNames = _.map(this.configuration.mainData.coverageData['files'], (el) => {
let fileName = el.filePath;
covDat[fileName] = {type: el.type, linktype: el.linktype, linksubtype: el.linksubtype, name: el.name};
return fileName;
});
}
// read coverage summary file and data
let unitTestSummary = {};
let fileDat = this.fileEngine.getSync(this.configuration.mainData.unitTestCoverage);
if(fileDat){
unitTestSummary = JSON.parse(fileDat);
} else {
return Promise.reject('Error reading unit test coverage file');
}
let getCovStatus = function(percent, totalLines){
let status;
if(totalLines === 0){
status = 'uncovered'
} else if (percent <= 25){
status = 'low';
} else if (percent > 25 && percent <= 50){
status = 'medium';
} else if (percent > 50 && percent <= 75){
status = 'good'
} else {
status = 'very-good';
}
return status;
}
let getCoverageData = function(data, fileName) {
let out = {};
if (fileName !== 'total'){
if(covDat === undefined){
// need a name to include in output but this isn't visible
out = {name: fileName, filePath: fileName};
} else { //if (covDat[fileName]){
let findMatch = _.filter(covFileNames, (el)=>{
return (el.includes(fileName) || fileName.includes(el))
});
if(findMatch.length > 0){
out = _.clone(covDat[findMatch[0]]);
out['filePath'] = fileName;
} //else {
//out = {name: fileName, filePath: fileName};
//}
}
}
let keysToGet = ['statements', 'branches', 'functions', 'lines'];
_.forEach(keysToGet, (key)=>{
if(data[key]){
let t = data[key];
out[key] = {coveragePercent: Math.round(t.pct),
coverageCount: '' + t.covered + '/' + t.total,
status: getCovStatus(t.pct, t.total)};
}
});
return out;
}

let unitTestData = {};
let files = [];
for(let file in unitTestSummary){
let dat = getCoverageData(unitTestSummary[file], file);
if (file === 'total'){
unitTestData['total'] = dat;
} else {
files.push(dat);
}
}
unitTestData['files'] = files;
unitTestData['idColumn'] = (covDat !== undefined); // should we include the id column
this.configuration.mainData.unitTestData = unitTestData;
this.configuration.addPage({
name: 'unit-test',
id: 'unit-test',
context: 'unit-test',
files: files,
data: unitTestData,
depth: 0,
pageType: COMPODOC_DEFAULTS.PAGE_TYPES.ROOT
});

if(this.configuration.mainData.exportFormat === COMPODOC_DEFAULTS.exportFormat){
let keysToGet = ['statements', 'branches', 'functions', 'lines'];
_.forEach(keysToGet, (key)=>{
if(unitTestData['total'][key]){
this.htmlEngine.generateCoverageBadge(
this.configuration.mainData.output,
key,
{count: unitTestData['total'][key]['coveragePercent'],
status: unitTestData['total'][key]['status']}
)
}
});
}
resolve();
});
}

private processPage(page): Promise<void> {
logger.info('Process page', page.name);
Expand Down
2 changes: 2 additions & 0 deletions src/app/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ export class Configuration implements ConfigurationInterface {
coverageTestThresholdFail: COMPODOC_DEFAULTS.coverageTestThresholdFail,
coverageTestPerFile: false,
coverageMinimumPerFile: COMPODOC_DEFAULTS.defaultCoverageMinimumPerFile,
unitTestCoverage: '',
unitTestData: {},
routesLength: 0,
angularVersion: '',
exportFormat: COMPODOC_DEFAULTS.exportFormat,
Expand Down
8 changes: 5 additions & 3 deletions src/app/engines/html.engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export class HtmlEngine {
'block-input',
'block-output',
'coverage-report',
'unit-test-report',
'miscellaneous-functions',
'miscellaneous-variables',
'miscellaneous-typealiases',
Expand Down Expand Up @@ -122,12 +123,13 @@ export class HtmlEngine {
});
}

public generateCoverageBadge(outputFolder, coverageData) {
public generateCoverageBadge(outputFolder, label, coverageData) {
return this.fileEngine
.get(path.resolve(__dirname + '/../src/templates/partials/coverage-badge.hbs'))
.then(
data => {
let template: any = Handlebars.compile(data);
coverageData.label = label;
let result = template({
data: coverageData
});
Expand All @@ -137,9 +139,9 @@ export class HtmlEngine {
}

return this.fileEngine
.write(outputFolder + path.sep + '/images/coverage-badge.svg', result)
.write(outputFolder + path.sep + '/images/coverage-badge-' + label + '.svg', result)
.catch(err => {
logger.error('Error during coverage badge file generation ', err);
logger.error('Error during coverage badge ' + label + ' file generation ', err);
return Promise.reject(err);
});
},
Expand Down
2 changes: 2 additions & 0 deletions src/app/interfaces/main-data.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ export interface MainDataInterface {
coverageTestThresholdFail: boolean;
coverageTestPerFile: boolean;
coverageMinimumPerFile: number;
unitTestCoverage: string;
unitTestData: Object;
routesLength: number;
angularVersion: string;
exportFormat: string;
Expand Down
9 changes: 9 additions & 0 deletions src/index-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ Note: Certain tabs will only be shown if applicable to a given dependency`,
'Test command of documentation coverage (global or per file) will fail with error or just warn user (true: error, false: warn)',
COMPODOC_DEFAULTS.coverageTestThresholdFail
)
.option('--unitTestCoverage [json-summary]',
'To include unit test coverage, specify istanbul JSON coverage summary file')
.option(
'--disableSourceCode',
'Do not add source code tab and links to source code',
Expand Down Expand Up @@ -339,6 +341,13 @@ Note: Certain tabs will only be shown if applicable to a given dependency`,
program.coverageTestThresholdFail === 'false' ? false : true;
}

if (configFile.unitTestCoverage) {
this.configuration.mainData.unitTestCoverage = configFile.unitTestCoverage;
}
if (program.unitTestCoverage) {
this.configuration.mainData.unitTestCoverage = program.unitTestCoverage;
}


if (configFile.disableSourceCode) {
this.configuration.mainData.disableSourceCode = configFile.disableSourceCode;
Expand Down
18 changes: 13 additions & 5 deletions src/resources/styles/compodoc.css
Original file line number Diff line number Diff line change
Expand Up @@ -581,19 +581,27 @@ i {
text-decoration: underline;
}

.coverage tr.low {
.coverage tr.low,
.coverage td.low {
background: rgba(216, 96, 75, 0.75);
}
.coverage tr.medium {
.coverage tr.medium,
.coverage td.medium {
background: rgba(218, 178, 38, 0.75);
}
.coverage tr.good {
.coverage tr.good,
.coverage td.good {
background: rgba(143, 189, 8, 0.75);
}
.coverage tr.very-good {
.coverage tr.very-good,
.coverage td.very-good {
background: rgba(77, 199, 31, 0.75);
}

.coverage td.uncovered {
background: rgba(221, 221, 221, 0.75);
}

.coverage-header {
background: #fafafa;
}
Expand Down Expand Up @@ -879,4 +887,4 @@ ul.index-list {
}
.package-dependencies li {
margin: 5px 0;
}
}
4 changes: 4 additions & 0 deletions src/templates/page.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@
{{~> coverage-report data ~}}
{{/compare}}

{{#compare data.context "===" 'unit-test'}}
{{~> unit-test-report data ~}}
{{/compare}}

{{#compare data.context "===" 'additional-page'}}
{{~> additional-page data ~}}
{{/compare}}
Expand Down
2 changes: 1 addition & 1 deletion src/templates/partials/coverage-badge.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<rect id="svg_1" height="20" width="130" y="0" x="0" stroke-width="1.5" stroke="#5d5d5d" fill="#5d5d5d" rx="7" ry="7"/>
<rect id="svg_2" height="20" width="40" y="0" x="92" stroke-width="1.5" stroke="{{#compare data.status "===" 'low'}}#d8604b{{/compare}}{{#compare data.status "===" 'medium'}}#dab226{{/compare}}{{#compare data.status "===" 'good'}}#8fbd08{{/compare}}{{#compare data.status "===" 'very-good'}}#4dc71f{{/compare}}" fill="{{#compare data.status "===" 'low'}}#d8604b{{/compare}}{{#compare data.status "===" 'medium'}}#dab226{{/compare}}{{#compare data.status "===" 'good'}}#8fbd08{{/compare}}{{#compare data.status "===" 'very-good'}}#4dc71f{{/compare}}" rx="7" ry="7"/>
<rect id="svg_3" height="20" width="22" y="0" x="92" stroke-width="1.5" stroke="{{#compare data.status "===" 'low'}}#d8604b{{/compare}}{{#compare data.status "===" 'medium'}}#dab226{{/compare}}{{#compare data.status "===" 'good'}}#8fbd08{{/compare}}{{#compare data.status "===" 'very-good'}}#4dc71f{{/compare}}" fill="{{#compare data.status "===" 'low'}}#d8604b{{/compare}}{{#compare data.status "===" 'medium'}}#dab226{{/compare}}{{#compare data.status "===" 'good'}}#8fbd08{{/compare}}{{#compare data.status "===" 'very-good'}}#4dc71f{{/compare}}"/>
<text xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="12" id="svg_4" y="14" x="6" stroke-width="0" stroke="#5d5d5d" fill="#ffffff">documentation</text>
<text xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="12" id="svg_4" y="14" x="6" stroke-width="0" stroke="#5d5d5d" fill="#ffffff">{{data.label}}</text>
<text xml:space="preserve" text-anchor="middle" font-family="Helvetica, Arial, sans-serif" font-size="12" id="svg_5" y="14" x="112" stroke-width="0" stroke="#5d5d5d" fill="#ffffff" style="text-anchor: middle">{{data.count}}%</text>
</g>
</svg>
2 changes: 1 addition & 1 deletion src/templates/partials/coverage-report.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
</ol>

<div>
<img src="./images/coverage-badge.svg">
<img src="./images/coverage-badge-documentation.svg">
</div>

<table class="table table-bordered coverage" id="coverage-table">
Expand Down
7 changes: 6 additions & 1 deletion src/templates/partials/menu.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,11 @@ customElements.define('compodoc-menu', class extends HTMLElement {
<a data-type="chapter-link" href="coverage.html"><span class="icon ion-ios-stats"></span>Documentation coverage</a>
</li>
{{/unless}}
{{#if unitTestData}}
<li class="chapter">
<a data-type="chapter-link" href="unit-test.html"><span class="icon ion-ios-podium"></span>Unit test coverage</a>
</li>
{{/if}}
{{#unless hideGenerator}}
<li class="divider"></li>
<li class="copyright">
Expand All @@ -388,4 +393,4 @@ customElements.define('compodoc-menu', class extends HTMLElement {
</nav>`);
this.innerHTML = tp.strings;
}
});
});
72 changes: 72 additions & 0 deletions src/templates/partials/unit-test-report.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<ol class="breadcrumb">
<li>Unit test coverage</li>
</ol>

<div>
<img src="./images/coverage-badge-statements.svg">
<img src="./images/coverage-badge-branches.svg">
<img src="./images/coverage-badge-functions.svg">
<img src="./images/coverage-badge-lines.svg">
</div>

<table class="table table-bordered coverage" id="coverage-table">
<thead class="coverage-header">
<tr>
<th>File</th>
{{#if idColumn}}
<th>Identifier</th>
{{/if}}
<th style="text-align:right" class="statements" data-sort-default>Statements</th>
<th style="text-align:right" class="statements" data-sort-default>Branches</th>
<th style="text-align:right" class="statements" data-sort-default>Functions</th>
<th style="text-align:right" class="statements" data-sort-default>Lines</th>
</tr>
</thead>
<tbody>
{{#each files}}
{{#if name}}
<tr>
<td>
<!-- miscellaneous -->
{{#if linktype}}
{{#if linksubtype}}
<a href="./{{linktype}}/{{linksubtype}}s.html#{{name}}">{{filePath}}</a>
{{else}}
<a href="./{{linktype}}s/{{name}}.html">{{filePath}}</a>
{{/if}}
{{else}}
{{filePath}}
{{/if}}
</td>
{{#if idColumn}}
<td>
{{name}}
</td>
{{/if}}
<td align="right" data-sort="{{statements.coveragePercent}}" class="{{statements.status}}">
<span class="coverage-percent">{{statements.coveragePercent}} %</span>
<span class="coverage-count">({{statements.coverageCount}})</span>
</td>
<td align="right" data-sort="{{branches.coveragePercent}}" class="{{branches.status}}">
<span class="coverage-percent">{{branches.coveragePercent}} %</span>
<span class="coverage-count">({{branches.coverageCount}})</span>
</td>
<td align="right" data-sort="{{functions.coveragePercent}}" class="{{functions.status}}">
<span class="coverage-percent">{{functions.coveragePercent}} %</span>
<span class="coverage-count">({{functions.coverageCount}})</span>
</td>
<td align="right" data-sort="{{lines.coveragePercent}}" class="{{lines.status}}">
<span class="coverage-percent">{{lines.coveragePercent}} %</span>
<span class="coverage-count">({{lines.coverageCount}})</span>
</td>
</tr>
{{/if}}
{{/each}}
</tbody>
</table>

<script src="{{relativeURL data.depth }}js/libs/tablesort.min.js"></script>
<script src="{{relativeURL data.depth }}js/libs/tablesort.number.min.js"></script>
<script>
new Tablesort(document.getElementById('coverage-table'));
</script>
2 changes: 1 addition & 1 deletion test/src/cli/cli-coverage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ describe('CLI coverage report', () => {

it('it should have coverage page', () => {
expect(coverageFile).to.contain('Documentation coverage');
expect(coverageFile).to.contain('img src="./images/coverage-badge.svg"');
expect(coverageFile).to.contain('img src="./images/coverage-badge-documentation.svg"');
expect(coverageFile).to.contain('5/5');
});

Expand Down
Loading

0 comments on commit 0a1e248

Please sign in to comment.