From 1ddac4c65c4aed2582446619e19c7720405814e0 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 16 Mar 2021 21:48:02 +0100 Subject: [PATCH] Support multiple reports (#36) --- README.md | 2 +- dist/index.js | 229 +++++++++++++++++++++++++++--------------- src/action.js | 143 +++++++++++++------------- src/action.test.js | 61 ++++++++--- src/cobertura.js | 82 +++++++++++++-- src/cobertura.test.js | 81 +++++++++------ 6 files changed, 400 insertions(+), 198 deletions(-) diff --git a/README.md b/README.md index dc338f67..56b9d3f1 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ The GITHUB_TOKEN. See [details](https://help.github.com/en/articles/virtual-envi ### `path` -The to the cobertura report. Defaults to `coverage.xml`. If a glob pattern is provided it will take the first match. +The path to the cobertura report. Defaults to `coverage.xml`. Glob pattern is supported, for example `coverage/*.xml`. ### `skip_covered` diff --git a/dist/index.js b/dist/index.js index 2efa8ba4..20659a58 100644 --- a/dist/index.js +++ b/dist/index.js @@ -13542,14 +13542,14 @@ const util = __webpack_require__(669); const glob = __webpack_require__(294); const parseString = util.promisify(xml2js.parseString); -async function processCoverage(path, options) { - options = options || { skipCovered: false }; - - if (glob.hasMagic(path)) { - const paths = await glob(path); - path = paths[0]; - } - +/** + * generate the report for the given file + * + * @param path: string + * @param options: object + * @return {Promise<{total: number, line: number, files: T[], branch: number}>} + */ +async function readCoverageFromFile(path, options) { const xml = await fs.readFile(path, "utf-8"); const { coverage } = await parseString(xml, { explicitArray: false, @@ -13574,6 +13574,40 @@ async function processCoverage(path, options) { }; } +function trimFolder(path, positionOfFirstDiff) { + const lastFolder = path.lastIndexOf("/") + 1; + if (positionOfFirstDiff >= lastFolder) { + return path.substr(lastFolder); + } else { + const startOffset = Math.min(positionOfFirstDiff - 1, lastFolder); + const length = path.length - startOffset - lastFolder - 2; // remove filename + return path.substr(startOffset, length); + } +} + +/** + * + * @param path: string + * @param options: {} + * @returns {Promise<{total: number, folder: string, line: number, files: T[], branch: number}[]>} + */ +async function processCoverage(path, options) { + options = options || { skipCovered: false }; + + const paths = glob.hasMagic(path) ? await glob(path) : [path]; + const positionOfFirstDiff = longestCommonPrefix(paths); + return await Promise.all( + paths.map(async (path) => { + const report = await readCoverageFromFile(path, options); + const folder = trimFolder(path, positionOfFirstDiff); + return { + ...report, + folder, + }; + }) + ); +} + function processPackages(packages) { if (packages.package instanceof Array) { return packages.package.map((p) => processPackage(p)).flat(); @@ -13596,6 +13630,12 @@ function processPackage(packageObj) { } } +/** + * returns coverage rates + * + * @param element: object + * @returns {{total: number, line: number, branch: number}} + */ function calculateRates(element) { const line = parseFloat(element["line-rate"]) * 100; const branch = parseFloat(element["branch-rate"]) * 100; @@ -13645,7 +13685,7 @@ function formatLines(statements, lines) { const ranges = []; let start = null; let linesCursor = 0; - let end = null; + let end; for (const statement of statements) { if (linesCursor >= lines.length) break; @@ -13674,8 +13714,32 @@ function formatLines(statements, lines) { .join(", "); } +/** + * + * @param paths: [string] + * @returns number + */ +function longestCommonPrefix(paths) { + let prefix = ""; + if (paths === null || paths.length === 0) return 0; + + for (let i = 0; i < paths[0].length; i++) { + const char = paths[0][i]; // loop through all characters of the very first string. + + for (let j = 1; j < paths.length; j++) { + // loop through all other strings in the array + if (paths[j][i] !== char) return prefix.length; + } + prefix = prefix + char; + } + + return prefix.length; +} + module.exports = { processCoverage, + trimFolder, + longestCommonPrefix, }; @@ -15906,8 +15970,8 @@ async function action(payload) { ? await listChangedFiles(pullRequestNumber) : null; - const report = await processCoverage(path, { skipCovered }); - const comment = markdownReport(report, commit, { + const reports = await processCoverage(path, { skipCovered }); + const comment = markdownReport(reports, commit, { minimumCoverage, showLine, showBranch, @@ -15920,7 +15984,7 @@ async function action(payload) { await addComment(pullRequestNumber, comment, reportName); } -function markdownReport(report, commit, options) { +function markdownReport(reports, commit, options) { const { minimumCoverage = 100, showLine = false, @@ -15937,74 +16001,81 @@ function markdownReport(report, commit, options) { str.length > at ? str.slice(0, at).concat("...") : str; // Setup files const files = []; - for (const file of report.files.filter( - (file) => filteredFiles == null || filteredFiles.includes(file.filename) - )) { - const fileTotal = Math.round(file.total); - const fileLines = Math.round(file.line); - const fileBranch = Math.round(file.branch); - const fileMissing = - showMissingMaxLength > 0 - ? crop(file.missing, showMissingMaxLength) - : file.missing; - files.push([ - escapeMarkdown(showClassNames ? file.name : file.filename), - `\`${fileTotal}%\``, - showLine ? `\`${fileLines}%\`` : undefined, - showBranch ? `\`${fileBranch}%\`` : undefined, - status(fileTotal), - showMissing ? (fileMissing ? `\`${fileMissing}\`` : " ") : undefined, - ]); - } - // Construct table - /* - | File | Coverage | | - |---------------|:--------:|:------------------:| - | **All files** | `78%` | :x: | - | foo.py | `80%` | :white_check_mark: | - | bar.py | `75%` | :x: | - - _Minimum allowed coverage is `80%`_ - */ - - const total = Math.round(report.total); - const linesTotal = Math.round(report.line); - const branchTotal = Math.round(report.branch); - const table = [ - [ - "File", - "Coverage", - showLine ? "Lines" : undefined, - showBranch ? "Branches" : undefined, - " ", - showMissing ? "Missing" : undefined, - ], - [ - "-", - ":-:", - showLine ? ":-:" : undefined, - showBranch ? ":-:" : undefined, - ":-:", - showMissing ? ":-:" : undefined, - ], - [ - "**All files**", - `\`${total}%\``, - showLine ? `\`${linesTotal}%\`` : undefined, - showBranch ? `\`${branchTotal}%\`` : undefined, - status(total), - showMissing ? " " : undefined, - ], - ...files, - ] - .map((row) => { - return `| ${row.filter(Boolean).join(" | ")} |`; - }) - .join("\n"); + let output = ""; + for (const report of reports) { + const folder = reports.length <= 1 ? "" : ` ${report.folder}`; + for (const file of report.files.filter( + (file) => filteredFiles == null || filteredFiles.includes(file.filename) + )) { + const fileTotal = Math.round(file.total); + const fileLines = Math.round(file.line); + const fileBranch = Math.round(file.branch); + const fileMissing = + showMissingMaxLength > 0 + ? crop(file.missing, showMissingMaxLength) + : file.missing; + files.push([ + escapeMarkdown(showClassNames ? file.name : file.filename), + `\`${fileTotal}%\``, + showLine ? `\`${fileLines}%\`` : undefined, + showBranch ? `\`${fileBranch}%\`` : undefined, + status(fileTotal), + showMissing ? (fileMissing ? `\`${fileMissing}\`` : " ") : undefined, + ]); + } + + // Construct table + /* + | File | Coverage | | + |---------------|:--------:|:------------------:| + | **All files** | `78%` | :x: | + | foo.py | `80%` | :white_check_mark: | + | bar.py | `75%` | :x: | + + _Minimum allowed coverage is `80%`_ + */ + + const total = Math.round(report.total); + const linesTotal = Math.round(report.line); + const branchTotal = Math.round(report.branch); + const table = [ + [ + "File", + "Coverage", + showLine ? "Lines" : undefined, + showBranch ? "Branches" : undefined, + " ", + showMissing ? "Missing" : undefined, + ], + [ + "-", + ":-:", + showLine ? ":-:" : undefined, + showBranch ? ":-:" : undefined, + ":-:", + showMissing ? ":-:" : undefined, + ], + [ + "**All files**", + `\`${total}%\``, + showLine ? `\`${linesTotal}%\`` : undefined, + showBranch ? `\`${branchTotal}%\`` : undefined, + status(total), + showMissing ? " " : undefined, + ], + ...files, + ] + .map((row) => { + return `| ${row.filter(Boolean).join(" | ")} |`; + }) + .join("\n"); + const titleText = `${reportName}${folder}`; + output += `${titleText}\n\n${table}\n\n`; + } const minimumCoverageText = `_Minimum allowed coverage is \`${minimumCoverage}%\`_`; const footerText = `

${credits} against ${commit}

`; - const titleText = `${reportName}`; - return `${titleText}\n\n${table}\n\n${minimumCoverageText}\n\n${footerText}`; + output += `${minimumCoverageText}\n\n${footerText}`; + return output; } async function addComment(pullRequestNumber, body, reportName) { @@ -16066,7 +16137,7 @@ async function pullRequestInfo(payload = {}) { state: "open", }); pullRequestNumber = data - .filter((d) => d.head.sha == commit) + .filter((d) => d.head.sha === commit) .reduce((n, d) => d.number, ""); } else if (payload.pull_request) { // try to find the PR from payload diff --git a/src/action.js b/src/action.js index 50ba0bb7..5f37b708 100644 --- a/src/action.js +++ b/src/action.js @@ -47,8 +47,8 @@ async function action(payload) { ? await listChangedFiles(pullRequestNumber) : null; - const report = await processCoverage(path, { skipCovered }); - const comment = markdownReport(report, commit, { + const reports = await processCoverage(path, { skipCovered }); + const comment = markdownReport(reports, commit, { minimumCoverage, showLine, showBranch, @@ -61,7 +61,7 @@ async function action(payload) { await addComment(pullRequestNumber, comment, reportName); } -function markdownReport(report, commit, options) { +function markdownReport(reports, commit, options) { const { minimumCoverage = 100, showLine = false, @@ -78,74 +78,81 @@ function markdownReport(report, commit, options) { str.length > at ? str.slice(0, at).concat("...") : str; // Setup files const files = []; - for (const file of report.files.filter( - (file) => filteredFiles == null || filteredFiles.includes(file.filename) - )) { - const fileTotal = Math.round(file.total); - const fileLines = Math.round(file.line); - const fileBranch = Math.round(file.branch); - const fileMissing = - showMissingMaxLength > 0 - ? crop(file.missing, showMissingMaxLength) - : file.missing; - files.push([ - escapeMarkdown(showClassNames ? file.name : file.filename), - `\`${fileTotal}%\``, - showLine ? `\`${fileLines}%\`` : undefined, - showBranch ? `\`${fileBranch}%\`` : undefined, - status(fileTotal), - showMissing ? (fileMissing ? `\`${fileMissing}\`` : " ") : undefined, - ]); - } - // Construct table - /* - | File | Coverage | | - |---------------|:--------:|:------------------:| - | **All files** | `78%` | :x: | - | foo.py | `80%` | :white_check_mark: | - | bar.py | `75%` | :x: | + let output = ""; + for (const report of reports) { + const folder = reports.length <= 1 ? "" : ` ${report.folder}`; + for (const file of report.files.filter( + (file) => filteredFiles == null || filteredFiles.includes(file.filename) + )) { + const fileTotal = Math.round(file.total); + const fileLines = Math.round(file.line); + const fileBranch = Math.round(file.branch); + const fileMissing = + showMissingMaxLength > 0 + ? crop(file.missing, showMissingMaxLength) + : file.missing; + files.push([ + escapeMarkdown(showClassNames ? file.name : file.filename), + `\`${fileTotal}%\``, + showLine ? `\`${fileLines}%\`` : undefined, + showBranch ? `\`${fileBranch}%\`` : undefined, + status(fileTotal), + showMissing ? (fileMissing ? `\`${fileMissing}\`` : " ") : undefined, + ]); + } + + // Construct table + /* + | File | Coverage | | + |---------------|:--------:|:------------------:| + | **All files** | `78%` | :x: | + | foo.py | `80%` | :white_check_mark: | + | bar.py | `75%` | :x: | - _Minimum allowed coverage is `80%`_ - */ + _Minimum allowed coverage is `80%`_ + */ - const total = Math.round(report.total); - const linesTotal = Math.round(report.line); - const branchTotal = Math.round(report.branch); - const table = [ - [ - "File", - "Coverage", - showLine ? "Lines" : undefined, - showBranch ? "Branches" : undefined, - " ", - showMissing ? "Missing" : undefined, - ], - [ - "-", - ":-:", - showLine ? ":-:" : undefined, - showBranch ? ":-:" : undefined, - ":-:", - showMissing ? ":-:" : undefined, - ], - [ - "**All files**", - `\`${total}%\``, - showLine ? `\`${linesTotal}%\`` : undefined, - showBranch ? `\`${branchTotal}%\`` : undefined, - status(total), - showMissing ? " " : undefined, - ], - ...files, - ] - .map((row) => { - return `| ${row.filter(Boolean).join(" | ")} |`; - }) - .join("\n"); + const total = Math.round(report.total); + const linesTotal = Math.round(report.line); + const branchTotal = Math.round(report.branch); + const table = [ + [ + "File", + "Coverage", + showLine ? "Lines" : undefined, + showBranch ? "Branches" : undefined, + " ", + showMissing ? "Missing" : undefined, + ], + [ + "-", + ":-:", + showLine ? ":-:" : undefined, + showBranch ? ":-:" : undefined, + ":-:", + showMissing ? ":-:" : undefined, + ], + [ + "**All files**", + `\`${total}%\``, + showLine ? `\`${linesTotal}%\`` : undefined, + showBranch ? `\`${branchTotal}%\`` : undefined, + status(total), + showMissing ? " " : undefined, + ], + ...files, + ] + .map((row) => { + return `| ${row.filter(Boolean).join(" | ")} |`; + }) + .join("\n"); + const titleText = `${reportName}${folder}`; + output += `${titleText}\n\n${table}\n\n`; + } const minimumCoverageText = `_Minimum allowed coverage is \`${minimumCoverage}%\`_`; const footerText = `

${credits} against ${commit}

`; - const titleText = `${reportName}`; - return `${titleText}\n\n${table}\n\n${minimumCoverageText}\n\n${footerText}`; + output += `${minimumCoverageText}\n\n${footerText}`; + return output; } async function addComment(pullRequestNumber, body, reportName) { @@ -207,7 +214,7 @@ async function pullRequestInfo(payload = {}) { state: "open", }); pullRequestNumber = data - .filter((d) => d.head.sha == commit) + .filter((d) => d.head.sha === commit) .reduce((n, d) => d.number, ""); } else if (payload.pull_request) { // try to find the PR from payload diff --git a/src/action.test.js b/src/action.test.js index ffbbf0c6..00a9035d 100644 --- a/src/action.test.js +++ b/src/action.test.js @@ -208,7 +208,7 @@ test("markdownReport", () => { const reportName = "TestReport"; const defaultReportName = "Coverage Report"; expect( - markdownReport(dummyReport, commit, { + markdownReport([dummyReport], commit, { minimumCoverage: 70, reportName: reportName, }) @@ -225,7 +225,7 @@ _Minimum allowed coverage is \`70%\`_

Generated by :monkey: cobertura-action against deadbeef

`); - expect(markdownReport(dummyReport, commit)) + expect(markdownReport([dummyReport], commit)) .toBe(`${defaultReportName} | File | Coverage | | @@ -240,7 +240,10 @@ _Minimum allowed coverage is \`100%\`_

Generated by :monkey: cobertura-action against deadbeef

`); expect( - markdownReport(dummyReport, commit, { minimumCoverage: 70, showLine: true }) + markdownReport([dummyReport], commit, { + minimumCoverage: 70, + showLine: true, + }) ).toBe(`${defaultReportName} | File | Coverage | Lines | | @@ -255,7 +258,7 @@ _Minimum allowed coverage is \`70%\`_

Generated by :monkey: cobertura-action against deadbeef

`); expect( - markdownReport(dummyReport, commit, { + markdownReport([dummyReport], commit, { minimumCoverage: 70, showBranch: true, }) @@ -273,7 +276,7 @@ _Minimum allowed coverage is \`70%\`_

Generated by :monkey: cobertura-action against deadbeef

`); expect( - markdownReport(dummyReport, commit, { + markdownReport([dummyReport], commit, { minimumCoverage: 70, showLine: true, showBranch: true, @@ -292,7 +295,7 @@ _Minimum allowed coverage is \`70%\`_

Generated by :monkey: cobertura-action against deadbeef

`); expect( - markdownReport(dummyReport, commit, { + markdownReport([dummyReport], commit, { minimumCoverage: 70, showLine: true, showBranch: true, @@ -312,7 +315,7 @@ _Minimum allowed coverage is \`70%\`_

Generated by :monkey: cobertura-action against deadbeef

`); expect( - markdownReport(dummyReport, commit, { + markdownReport([dummyReport], commit, { minimumCoverage: 70, showLine: true, showBranch: true, @@ -332,7 +335,7 @@ _Minimum allowed coverage is \`70%\`_

Generated by :monkey: cobertura-action against deadbeef

`); - expect(markdownReport(dummyReport, commit, { minimumCoverage: 80 })) + expect(markdownReport([dummyReport], commit, { minimumCoverage: 80 })) .toBe(`${defaultReportName} | File | Coverage | | @@ -346,7 +349,7 @@ _Minimum allowed coverage is \`80%\`_

Generated by :monkey: cobertura-action against deadbeef

`); - expect(markdownReport(dummyReport, commit, { showClassNames: true })) + expect(markdownReport([dummyReport], commit, { showClassNames: true })) .toBe(`${defaultReportName} | File | Coverage | | @@ -360,7 +363,7 @@ _Minimum allowed coverage is \`100%\`_

Generated by :monkey: cobertura-action against deadbeef

`); - expect(markdownReport(dummyReport, commit, { filteredFiles: ["bar.py"] })) + expect(markdownReport([dummyReport], commit, { filteredFiles: ["bar.py"] })) .toBe(`${defaultReportName} | File | Coverage | | @@ -372,8 +375,9 @@ _Minimum allowed coverage is \`100%\`_

Generated by :monkey: cobertura-action against deadbeef

`); - expect(markdownReport(dummyReport, commit, { filteredFiles: ["README.md"] })) - .toBe(`${defaultReportName} + expect( + markdownReport([dummyReport], commit, { filteredFiles: ["README.md"] }) + ).toBe(`${defaultReportName} | File | Coverage | | | - | :-: | :-: | @@ -383,7 +387,7 @@ _Minimum allowed coverage is \`100%\`_

Generated by :monkey: cobertura-action against deadbeef

`); - expect(markdownReport(dummyReport, commit, { filteredFiles: [] })) + expect(markdownReport([dummyReport], commit, { filteredFiles: [] })) .toBe(`${defaultReportName} | File | Coverage | | @@ -392,6 +396,37 @@ _Minimum allowed coverage is \`100%\`_ _Minimum allowed coverage is \`100%\`_ +

Generated by :monkey: cobertura-action against deadbeef

`); + + expect( + markdownReport( + [ + { + folder: "foo.xml", + ...dummyReport, + }, + { + folder: "bar.xml", + ...dummyReport, + }, + ], + commit, + { filteredFiles: [] } + ) + ).toBe(`${defaultReportName} foo.xml + +| File | Coverage | | +| - | :-: | :-: | +| **All files** | \`78%\` | :x: | + +${defaultReportName} bar.xml + +| File | Coverage | | +| - | :-: | :-: | +| **All files** | \`78%\` | :x: | + +_Minimum allowed coverage is \`100%\`_ +

Generated by :monkey: cobertura-action against deadbeef

`); }); diff --git a/src/cobertura.js b/src/cobertura.js index 78b3aa6b..2b3d3d29 100644 --- a/src/cobertura.js +++ b/src/cobertura.js @@ -4,14 +4,14 @@ const util = require("util"); const glob = require("glob-promise"); const parseString = util.promisify(xml2js.parseString); -async function processCoverage(path, options) { - options = options || { skipCovered: false }; - - if (glob.hasMagic(path)) { - const paths = await glob(path); - path = paths[0]; - } - +/** + * generate the report for the given file + * + * @param path: string + * @param options: object + * @return {Promise<{total: number, line: number, files: T[], branch: number}>} + */ +async function readCoverageFromFile(path, options) { const xml = await fs.readFile(path, "utf-8"); const { coverage } = await parseString(xml, { explicitArray: false, @@ -36,6 +36,40 @@ async function processCoverage(path, options) { }; } +function trimFolder(path, positionOfFirstDiff) { + const lastFolder = path.lastIndexOf("/") + 1; + if (positionOfFirstDiff >= lastFolder) { + return path.substr(lastFolder); + } else { + const startOffset = Math.min(positionOfFirstDiff - 1, lastFolder); + const length = path.length - startOffset - lastFolder - 2; // remove filename + return path.substr(startOffset, length); + } +} + +/** + * + * @param path: string + * @param options: {} + * @returns {Promise<{total: number, folder: string, line: number, files: T[], branch: number}[]>} + */ +async function processCoverage(path, options) { + options = options || { skipCovered: false }; + + const paths = glob.hasMagic(path) ? await glob(path) : [path]; + const positionOfFirstDiff = longestCommonPrefix(paths); + return await Promise.all( + paths.map(async (path) => { + const report = await readCoverageFromFile(path, options); + const folder = trimFolder(path, positionOfFirstDiff); + return { + ...report, + folder, + }; + }) + ); +} + function processPackages(packages) { if (packages.package instanceof Array) { return packages.package.map((p) => processPackage(p)).flat(); @@ -58,6 +92,12 @@ function processPackage(packageObj) { } } +/** + * returns coverage rates + * + * @param element: object + * @returns {{total: number, line: number, branch: number}} + */ function calculateRates(element) { const line = parseFloat(element["line-rate"]) * 100; const branch = parseFloat(element["branch-rate"]) * 100; @@ -107,7 +147,7 @@ function formatLines(statements, lines) { const ranges = []; let start = null; let linesCursor = 0; - let end = null; + let end; for (const statement of statements) { if (linesCursor >= lines.length) break; @@ -136,6 +176,30 @@ function formatLines(statements, lines) { .join(", "); } +/** + * + * @param paths: [string] + * @returns number + */ +function longestCommonPrefix(paths) { + let prefix = ""; + if (paths === null || paths.length === 0) return 0; + + for (let i = 0; i < paths[0].length; i++) { + const char = paths[0][i]; // loop through all characters of the very first string. + + for (let j = 1; j < paths.length; j++) { + // loop through all other strings in the array + if (paths[j][i] !== char) return prefix.length; + } + prefix = prefix + char; + } + + return prefix.length; +} + module.exports = { processCoverage, + trimFolder, + longestCommonPrefix, }; diff --git a/src/cobertura.test.js b/src/cobertura.test.js index 15799bae..0c523f06 100644 --- a/src/cobertura.test.js +++ b/src/cobertura.test.js @@ -1,9 +1,21 @@ -const { processCoverage } = require("./cobertura"); +const { + processCoverage, + trimFolder, + longestCommonPrefix, +} = require("./cobertura"); + +test("multiple files", async () => { + const reports = await processCoverage("./src/fixtures/*-branch.xml"); + expect(reports.length).toBe(2); + expect(reports[0].folder).toBe("test-branch.xml"); + expect(reports[1].folder).toBe("test-no-branch.xml"); +}); test("processCoverage(test-branch.xml, {skipCovered: false})", async () => { - const report = await processCoverage("./src/fixtures/test-branch.xml"); - expect(report.total).toBe(82.5); - const files = report.files; + const reports = await processCoverage("./src/fixtures/test-branch.xml"); + expect(reports.length).toBe(1); + expect(reports[0].total).toBe(82.5); + const files = reports[0].files; expect(files.length).toBe(4); expect(files[0].total).toBe(100); @@ -32,11 +44,12 @@ test("processCoverage(test-branch.xml, {skipCovered: false})", async () => { }); test("processCoverage({skipCovered: true})", async () => { - const report = await processCoverage("./src/fixtures/test-branch.xml", { + const reports = await processCoverage("./src/fixtures/test-branch.xml", { skipCovered: true, }); - expect(report.total).toBe(82.5); - const files = report.files; + expect(reports.length).toBe(1); + expect(reports[0].total).toBe(82.5); + const files = reports[0].files; expect(files.length).toBe(2); expect(files[0].total).toBe(87.5); @@ -53,11 +66,11 @@ test("processCoverage({skipCovered: true})", async () => { }); test("processCoverage(test-branch.xml, {skipCovered: true})", async () => { - const report = await processCoverage("./src/fixtures/test-branch.xml", { + const reports = await processCoverage("./src/fixtures/test-branch.xml", { skipCovered: true, }); - expect(report.total).toBe(82.5); - const files = report.files; + expect(reports[0].total).toBe(82.5); + const files = reports[0].files; expect(files.length).toBe(2); expect(files[0].total).toBe(87.5); @@ -74,11 +87,11 @@ test("processCoverage(test-branch.xml, {skipCovered: true})", async () => { }); test("processCoverage(test-no-branch.xml, {skipCovered: true})", async () => { - const report = await processCoverage("./src/fixtures/test-no-branch.xml", { + const reports = await processCoverage("./src/fixtures/test-no-branch.xml", { skipCovered: true, }); - expect(report.total).toBe(90); - const files = report.files; + expect(reports[0].total).toBe(90); + const files = reports[0].files; expect(files.length).toBe(2); expect(files[0].total).toBe(91.66666666666666); @@ -95,11 +108,11 @@ test("processCoverage(test-no-branch.xml, {skipCovered: true})", async () => { }); test("processCoverage(test-istanbul.xml, {skipCovered: false})", async () => { - const report = await processCoverage("./src/fixtures/test-istanbul.xml", { + const reports = await processCoverage("./src/fixtures/test-istanbul.xml", { skipCovered: false, }); - expect(report.total).toBe(100); - const files = report.files; + expect(reports[0].total).toBe(100); + const files = reports[0].files; expect(files.length).toBe(2); expect(files[0].total).toBe(100); @@ -116,14 +129,14 @@ test("processCoverage(test-istanbul.xml, {skipCovered: false})", async () => { }); test("processCoverage(test-istanbul-single.xml, {skipCovered: false})", async () => { - const report = await processCoverage( + const reports = await processCoverage( "./src/fixtures/test-istanbul-single.xml", { skipCovered: false, } ); - expect(report.total).toBe(100); - const files = report.files; + expect(reports[0].total).toBe(100); + const files = reports[0].files; expect(files.length).toBe(1); expect(files[0].total).toBe(100); @@ -134,11 +147,11 @@ test("processCoverage(test-istanbul-single.xml, {skipCovered: false})", async () }); test("processCoverage(test-python.xml, {skipCovered: false})", async () => { - const report = await processCoverage("./src/fixtures/test-python.xml", { + const reports = await processCoverage("./src/fixtures/test-python.xml", { skipCovered: false, }); - expect(report.total).toBe(90); - const files = report.files; + expect(reports[0].total).toBe(90); + const files = reports[0].files; expect(files.length).toBe(1); expect(files[0].total).toBe(90); @@ -149,9 +162,10 @@ test("processCoverage(test-python.xml, {skipCovered: false})", async () => { }); test("processCoverage(glob-test-branch.xml, {skipCovered: false})", async () => { - const report = await processCoverage("./src/**/test-branch.xml"); - expect(report.total).toBe(82.5); - const files = report.files; + const reports = await processCoverage("./src/**/test-branch.xml"); + expect(reports.length).toBe(1); + expect(reports[0].total).toBe(82.5); + const files = reports[0].files; expect(files.length).toBe(4); expect(files[0].total).toBe(100); @@ -180,9 +194,10 @@ test("processCoverage(glob-test-branch.xml, {skipCovered: false})", async () => }); test("processCoverage(test-missing-lines.xml, {skipCovered: true})", async () => { - const report = await processCoverage("./src/**/test-missing-lines.xml"); - expect(report.total).toBe(51.24999999999999); - const files = report.files; + const reports = await processCoverage("./src/**/test-missing-lines.xml"); + expect(reports.length).toBe(1); + expect(reports[0].total).toBe(51.24999999999999); + const files = reports[0].files; expect(files.length).toBe(8); expect(files[0].filename).toBe("all_lines_covered.py"); @@ -234,3 +249,13 @@ test("processCoverage(test-missing-lines.xml, {skipCovered: true})", async () => expect(files[7].line).toBe(0); expect(files[7].missing).toBe("4"); }); + +test("trimFolder", () => { + expect(trimFolder("/a/b/c/file.xml", 7)).toBe("file.xml"); + expect(trimFolder("/a/b/c/file.xml", 3)).toBe("/b/c"); +}); + +test("longestCommonPrefix", () => { + expect(longestCommonPrefix(null)).toBe(0); + expect(longestCommonPrefix([])).toBe(0); +});