Skip to content

Commit 1ddac4c

Browse files
authored
Support multiple reports (#36)
1 parent c85756a commit 1ddac4c

File tree

6 files changed

+400
-198
lines changed

6 files changed

+400
-198
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ The GITHUB_TOKEN. See [details](https://help.github.com/en/articles/virtual-envi
2929

3030
### `path`
3131

32-
The to the cobertura report. Defaults to `coverage.xml`. If a glob pattern is provided it will take the first match.
32+
The path to the cobertura report. Defaults to `coverage.xml`. Glob pattern is supported, for example `coverage/*.xml`.
3333

3434
### `skip_covered`
3535

dist/index.js

+150-79
Original file line numberDiff line numberDiff line change
@@ -13542,14 +13542,14 @@ const util = __webpack_require__(669);
1354213542
const glob = __webpack_require__(294);
1354313543
const parseString = util.promisify(xml2js.parseString);
1354413544

13545-
async function processCoverage(path, options) {
13546-
options = options || { skipCovered: false };
13547-
13548-
if (glob.hasMagic(path)) {
13549-
const paths = await glob(path);
13550-
path = paths[0];
13551-
}
13552-
13545+
/**
13546+
* generate the report for the given file
13547+
*
13548+
* @param path: string
13549+
* @param options: object
13550+
* @return {Promise<{total: number, line: number, files: T[], branch: number}>}
13551+
*/
13552+
async function readCoverageFromFile(path, options) {
1355313553
const xml = await fs.readFile(path, "utf-8");
1355413554
const { coverage } = await parseString(xml, {
1355513555
explicitArray: false,
@@ -13574,6 +13574,40 @@ async function processCoverage(path, options) {
1357413574
};
1357513575
}
1357613576

13577+
function trimFolder(path, positionOfFirstDiff) {
13578+
const lastFolder = path.lastIndexOf("/") + 1;
13579+
if (positionOfFirstDiff >= lastFolder) {
13580+
return path.substr(lastFolder);
13581+
} else {
13582+
const startOffset = Math.min(positionOfFirstDiff - 1, lastFolder);
13583+
const length = path.length - startOffset - lastFolder - 2; // remove filename
13584+
return path.substr(startOffset, length);
13585+
}
13586+
}
13587+
13588+
/**
13589+
*
13590+
* @param path: string
13591+
* @param options: {}
13592+
* @returns {Promise<{total: number, folder: string, line: number, files: T[], branch: number}[]>}
13593+
*/
13594+
async function processCoverage(path, options) {
13595+
options = options || { skipCovered: false };
13596+
13597+
const paths = glob.hasMagic(path) ? await glob(path) : [path];
13598+
const positionOfFirstDiff = longestCommonPrefix(paths);
13599+
return await Promise.all(
13600+
paths.map(async (path) => {
13601+
const report = await readCoverageFromFile(path, options);
13602+
const folder = trimFolder(path, positionOfFirstDiff);
13603+
return {
13604+
...report,
13605+
folder,
13606+
};
13607+
})
13608+
);
13609+
}
13610+
1357713611
function processPackages(packages) {
1357813612
if (packages.package instanceof Array) {
1357913613
return packages.package.map((p) => processPackage(p)).flat();
@@ -13596,6 +13630,12 @@ function processPackage(packageObj) {
1359613630
}
1359713631
}
1359813632

13633+
/**
13634+
* returns coverage rates
13635+
*
13636+
* @param element: object
13637+
* @returns {{total: number, line: number, branch: number}}
13638+
*/
1359913639
function calculateRates(element) {
1360013640
const line = parseFloat(element["line-rate"]) * 100;
1360113641
const branch = parseFloat(element["branch-rate"]) * 100;
@@ -13645,7 +13685,7 @@ function formatLines(statements, lines) {
1364513685
const ranges = [];
1364613686
let start = null;
1364713687
let linesCursor = 0;
13648-
let end = null;
13688+
let end;
1364913689
for (const statement of statements) {
1365013690
if (linesCursor >= lines.length) break;
1365113691

@@ -13674,8 +13714,32 @@ function formatLines(statements, lines) {
1367413714
.join(", ");
1367513715
}
1367613716

13717+
/**
13718+
*
13719+
* @param paths: [string]
13720+
* @returns number
13721+
*/
13722+
function longestCommonPrefix(paths) {
13723+
let prefix = "";
13724+
if (paths === null || paths.length === 0) return 0;
13725+
13726+
for (let i = 0; i < paths[0].length; i++) {
13727+
const char = paths[0][i]; // loop through all characters of the very first string.
13728+
13729+
for (let j = 1; j < paths.length; j++) {
13730+
// loop through all other strings in the array
13731+
if (paths[j][i] !== char) return prefix.length;
13732+
}
13733+
prefix = prefix + char;
13734+
}
13735+
13736+
return prefix.length;
13737+
}
13738+
1367713739
module.exports = {
1367813740
processCoverage,
13741+
trimFolder,
13742+
longestCommonPrefix,
1367913743
};
1368013744

1368113745

@@ -15906,8 +15970,8 @@ async function action(payload) {
1590615970
? await listChangedFiles(pullRequestNumber)
1590715971
: null;
1590815972

15909-
const report = await processCoverage(path, { skipCovered });
15910-
const comment = markdownReport(report, commit, {
15973+
const reports = await processCoverage(path, { skipCovered });
15974+
const comment = markdownReport(reports, commit, {
1591115975
minimumCoverage,
1591215976
showLine,
1591315977
showBranch,
@@ -15920,7 +15984,7 @@ async function action(payload) {
1592015984
await addComment(pullRequestNumber, comment, reportName);
1592115985
}
1592215986

15923-
function markdownReport(report, commit, options) {
15987+
function markdownReport(reports, commit, options) {
1592415988
const {
1592515989
minimumCoverage = 100,
1592615990
showLine = false,
@@ -15937,74 +16001,81 @@ function markdownReport(report, commit, options) {
1593716001
str.length > at ? str.slice(0, at).concat("...") : str;
1593816002
// Setup files
1593916003
const files = [];
15940-
for (const file of report.files.filter(
15941-
(file) => filteredFiles == null || filteredFiles.includes(file.filename)
15942-
)) {
15943-
const fileTotal = Math.round(file.total);
15944-
const fileLines = Math.round(file.line);
15945-
const fileBranch = Math.round(file.branch);
15946-
const fileMissing =
15947-
showMissingMaxLength > 0
15948-
? crop(file.missing, showMissingMaxLength)
15949-
: file.missing;
15950-
files.push([
15951-
escapeMarkdown(showClassNames ? file.name : file.filename),
15952-
`\`${fileTotal}%\``,
15953-
showLine ? `\`${fileLines}%\`` : undefined,
15954-
showBranch ? `\`${fileBranch}%\`` : undefined,
15955-
status(fileTotal),
15956-
showMissing ? (fileMissing ? `\`${fileMissing}\`` : " ") : undefined,
15957-
]);
15958-
}
15959-
// Construct table
15960-
/*
15961-
| File | Coverage | |
15962-
|---------------|:--------:|:------------------:|
15963-
| **All files** | `78%` | :x: |
15964-
| foo.py | `80%` | :white_check_mark: |
15965-
| bar.py | `75%` | :x: |
15966-
15967-
_Minimum allowed coverage is `80%`_
15968-
*/
15969-
15970-
const total = Math.round(report.total);
15971-
const linesTotal = Math.round(report.line);
15972-
const branchTotal = Math.round(report.branch);
15973-
const table = [
15974-
[
15975-
"File",
15976-
"Coverage",
15977-
showLine ? "Lines" : undefined,
15978-
showBranch ? "Branches" : undefined,
15979-
" ",
15980-
showMissing ? "Missing" : undefined,
15981-
],
15982-
[
15983-
"-",
15984-
":-:",
15985-
showLine ? ":-:" : undefined,
15986-
showBranch ? ":-:" : undefined,
15987-
":-:",
15988-
showMissing ? ":-:" : undefined,
15989-
],
15990-
[
15991-
"**All files**",
15992-
`\`${total}%\``,
15993-
showLine ? `\`${linesTotal}%\`` : undefined,
15994-
showBranch ? `\`${branchTotal}%\`` : undefined,
15995-
status(total),
15996-
showMissing ? " " : undefined,
15997-
],
15998-
...files,
15999-
]
16000-
.map((row) => {
16001-
return `| ${row.filter(Boolean).join(" | ")} |`;
16002-
})
16003-
.join("\n");
16004+
let output = "";
16005+
for (const report of reports) {
16006+
const folder = reports.length <= 1 ? "" : ` ${report.folder}`;
16007+
for (const file of report.files.filter(
16008+
(file) => filteredFiles == null || filteredFiles.includes(file.filename)
16009+
)) {
16010+
const fileTotal = Math.round(file.total);
16011+
const fileLines = Math.round(file.line);
16012+
const fileBranch = Math.round(file.branch);
16013+
const fileMissing =
16014+
showMissingMaxLength > 0
16015+
? crop(file.missing, showMissingMaxLength)
16016+
: file.missing;
16017+
files.push([
16018+
escapeMarkdown(showClassNames ? file.name : file.filename),
16019+
`\`${fileTotal}%\``,
16020+
showLine ? `\`${fileLines}%\`` : undefined,
16021+
showBranch ? `\`${fileBranch}%\`` : undefined,
16022+
status(fileTotal),
16023+
showMissing ? (fileMissing ? `\`${fileMissing}\`` : " ") : undefined,
16024+
]);
16025+
}
16026+
16027+
// Construct table
16028+
/*
16029+
| File | Coverage | |
16030+
|---------------|:--------:|:------------------:|
16031+
| **All files** | `78%` | :x: |
16032+
| foo.py | `80%` | :white_check_mark: |
16033+
| bar.py | `75%` | :x: |
16034+
16035+
_Minimum allowed coverage is `80%`_
16036+
*/
16037+
16038+
const total = Math.round(report.total);
16039+
const linesTotal = Math.round(report.line);
16040+
const branchTotal = Math.round(report.branch);
16041+
const table = [
16042+
[
16043+
"File",
16044+
"Coverage",
16045+
showLine ? "Lines" : undefined,
16046+
showBranch ? "Branches" : undefined,
16047+
" ",
16048+
showMissing ? "Missing" : undefined,
16049+
],
16050+
[
16051+
"-",
16052+
":-:",
16053+
showLine ? ":-:" : undefined,
16054+
showBranch ? ":-:" : undefined,
16055+
":-:",
16056+
showMissing ? ":-:" : undefined,
16057+
],
16058+
[
16059+
"**All files**",
16060+
`\`${total}%\``,
16061+
showLine ? `\`${linesTotal}%\`` : undefined,
16062+
showBranch ? `\`${branchTotal}%\`` : undefined,
16063+
status(total),
16064+
showMissing ? " " : undefined,
16065+
],
16066+
...files,
16067+
]
16068+
.map((row) => {
16069+
return `| ${row.filter(Boolean).join(" | ")} |`;
16070+
})
16071+
.join("\n");
16072+
const titleText = `<strong>${reportName}${folder}</strong>`;
16073+
output += `${titleText}\n\n${table}\n\n`;
16074+
}
1600416075
const minimumCoverageText = `_Minimum allowed coverage is \`${minimumCoverage}%\`_`;
1600516076
const footerText = `<p align="right">${credits} against ${commit} </p>`;
16006-
const titleText = `<strong>${reportName}</strong>`;
16007-
return `${titleText}\n\n${table}\n\n${minimumCoverageText}\n\n${footerText}`;
16077+
output += `${minimumCoverageText}\n\n${footerText}`;
16078+
return output;
1600816079
}
1600916080

1601016081
async function addComment(pullRequestNumber, body, reportName) {
@@ -16066,7 +16137,7 @@ async function pullRequestInfo(payload = {}) {
1606616137
state: "open",
1606716138
});
1606816139
pullRequestNumber = data
16069-
.filter((d) => d.head.sha == commit)
16140+
.filter((d) => d.head.sha === commit)
1607016141
.reduce((n, d) => d.number, "");
1607116142
} else if (payload.pull_request) {
1607216143
// try to find the PR from payload

0 commit comments

Comments
 (0)