diff --git a/.gitignore b/.gitignore index 7e874de..a778bf7 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,5 @@ typings/ # next.js build output .next + +exports/ \ No newline at end of file diff --git a/index.js b/index.js index 05972a0..aea0265 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ const core = require("@actions/core"); const minimatch = require("minimatch"); const parse = require("lcov-parse"); +const reportgen = require('./report-generator/generate-report'); function run() { const lcovPath = core.getInput("path"); @@ -13,15 +14,50 @@ function run() { core.setFailed("parsing error!"); return; } - let totalFinds = 0; - let totalHits = 0; + let totalLinesFound = 0; + let totalLinesHit = 0; + let totalFunctionsFound = 0; + let totalFunctionsHit = 0; + let totalBranchesFound = 0; + let totalBranchesHit = 0; + + const filesTemplateData = []; + data.forEach(element => { if (shouldCalculateCoverageForFile(element["file"], excludedFiles)) { - totalHits += element['lines']['hit']; - totalFinds += element['lines']['found']; + totalLinesHit += element['lines']['hit']; + totalLinesFound += element['lines']['found']; + totalFunctionsFound += element['functions']['hit']; + totalFunctionsHit += element['functions']['found']; + totalBranchesHit += element['branches']['hit']; + totalBranchesFound += element['branches']['found']; } + + filesTemplateData.push({ + path: element['file'], + linesHit: element['lines']['hit'], + linesFound: element['lines']['found'], + functionsHit: element['functions']['hit'], + functionsFound: element['functions']['found'], + branchesHit: element['branches']['hit'], + branchesFound: element['branches']['found'], + }); }); - const coverage = (totalHits / totalFinds) * 100; + + const templateData = { + totalLinesHit, + totalLinesFound, + totalFunctionsHit, + totalFunctionsFound, + totalBranchesHit, + totalBranchesFound, + files: filesTemplateData, + date: new Date(Date.now()), + }; + + reportgen.generateHtml(templateData); + + const coverage = (totalLinesHit / totalLinesFound) * 100; const isValidBuild = coverage >= minCoverage; if (!isValidBuild) { core.setFailed(`Coverage ${coverage} is below the minimum ${minCoverage} expected`); diff --git a/index.test.js b/index.test.js index 37beff4..6774f4c 100644 --- a/index.test.js +++ b/index.test.js @@ -78,4 +78,5 @@ test("fails when the coverage is below the given min_threshold", () => { } catch (err) { expect(err).toBeDefined(); } -}); \ No newline at end of file +}); + diff --git a/package-lock.json b/package-lock.json index 70b1f87..3134cd1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2314,6 +2314,18 @@ "dev": true, "optional": true }, + "handlebars": { + "version": "4.7.6", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", + "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + } + }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -3531,8 +3543,7 @@ "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "mixin-deep": { "version": "1.3.2", @@ -3595,6 +3606,11 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -4590,8 +4606,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "source-map-resolve": { "version": "0.5.3", @@ -5006,6 +5021,12 @@ "is-typedarray": "^1.0.0" } }, + "uglify-js": { + "version": "3.11.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.11.4.tgz", + "integrity": "sha512-FyYnoxVL1D6+jDGQpbK5jW6y/2JlVfRfEeQ67BPCUg5wfCjaKOpr2XeceE4QL+MkhxliLtf5EbrMDZgzpt2CNw==", + "optional": true + }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -5212,6 +5233,11 @@ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", diff --git a/package.json b/package.json index c2769e2..65ce0f7 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "dependencies": { "@actions/core": "^1.2.5", "lcov-parse": "^1.0.0", - "minimatch": "^3.0.4" + "minimatch": "^3.0.4", + "handlebars": "^4.7.6" }, "devDependencies": { "@vercel/ncc": "^0.24.1", diff --git a/report-generator/generate-report.js b/report-generator/generate-report.js new file mode 100644 index 0000000..a0a9f0f --- /dev/null +++ b/report-generator/generate-report.js @@ -0,0 +1,42 @@ +const fs = require('fs'); +const Handlebars = require("handlebars"); + +const roundToTwoDecimals = (num) => Math.round((num + Number.EPSILON) * 10000) / 100 + +function generateHtml(templateData) { + Handlebars.registerHelper('coverage', function (hits, found) { + if (found === 0) { + // assume 0 found means 100% coverage + return '100.00%'; + } + return `${roundToTwoDecimals(hits / found)}%`; + }); + + Handlebars.registerHelper('coverageColor', function (hits, found) { + if (found === 0) { + // assume 0 found means 100% coverage + return 'green'; + } + if ((hits / found) <= 0.75) { + return 'red'; + } else if ((hits / found) > 0.75 && (hits / found) < 1) { + return 'yellow'; + } else if ((hits / found) === 1) { + return 'green' + } + }); + + Handlebars.registerHelper('prettyDate', function (date) { + return date.toLocaleString(); + }); + + const templateString = fs.readFileSync('./report-generator/template.handlebars', 'utf-8'); + + const template = Handlebars.compile(templateString); + const page = template(templateData); + fs.writeFileSync('./exports/index.html', page); +} + +module.exports = { + generateHtml +} \ No newline at end of file diff --git a/report-generator/template.handlebars b/report-generator/template.handlebars new file mode 100644 index 0000000..a2d0131 --- /dev/null +++ b/report-generator/template.handlebars @@ -0,0 +1,140 @@ + + + + + Coverage Report + + + + +

Code Coverage Report

+
+
+
+

Current View: top level

+

Date: {{prettyDate date}}

+

Legend: + low: < 75% + medium: >= 75% < 100% + perfect: 100%

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
HitTotalCoverage
Lines:{{totalLinesHit}}{{totalLinesFound}} + {{coverage totalLinesHit totalLinesFound}}
Functions:{{totalFunctionsHit}}{{totalFunctionsFound}} + {{coverage totalFunctionsHit totalFunctionsFound}}
Branches:{{totalBranchesHit}}{{totalBranchesFound}} + {{coverage totalBranchesHit totalBranchesFound}}
+
+
+
+ + + + + + + + {{#each files as |file|}} + + + + + + + + + + {{/each}} +
DirectoryLine CoverageFunctionsBranches
{{file.path}} + {{coverage file.linesHit file.linesFound}} + + {{file.linesHit}} / {{file.linesFound}} + + {{coverage file.functionsHit file.functionsFound}} + + {{file.functionsHit}} / {{file.functionsFound}} + + {{coverage file.branchesHit file.branchesFound}} + + {{file.branchesHit}} / {{file.branchesFound}} +
+ + + \ No newline at end of file