diff --git a/action.yml b/action.yml index 963c3d36..b3ad25a6 100644 --- a/action.yml +++ b/action.yml @@ -17,6 +17,9 @@ inputs: lcov-base: description: The location of the lcov file for the base branch. Applicable for single repo setup. required: false + app-name: + description: App name to display on comment + required: false runs: using: node12 main: dist/main.js diff --git a/dist/main.js b/dist/main.js index a1cd21b6..ec7eeddc 100644 --- a/dist/main.js +++ b/dist/main.js @@ -6024,6 +6024,28 @@ function uncovered(file, options) { .join(", "); } +/** + * Compares two arrays of objects and returns with unique lines update + * @param {number} pdiff value from diff percentage + * @returns {string} emoji string for negative/positive pdiff + */ +const renderEmoji = pdiff => { + if (pdiff.toFixed(2) < 0) return "❌"; + return "✅"; +}; + +/** + * Compares two arrays of objects and returns with unique lines update + * @param {Array} otherArray + * @returns {Function} function with filtering non original lines + */ +const comparer = otherArray => current => + otherArray.filter( + other => + other.lines.found === current.lines.found && + other.lines.hit === current.lines.hit, + ).length === 0; + /** * Github comment for monorepo * @param {Array<{packageName, lcovPath}>} lcovArrayForMonorepo @@ -6035,6 +6057,7 @@ function commentForMonorepo( lcovBaseArrayForMonorepo, options, ) { + const { base } = options; const html = lcovArrayForMonorepo.map(lcovObj => { const baseLcov = lcovBaseArrayForMonorepo.find( el => el.packageName === lcovObj.packageName, @@ -6046,9 +6069,14 @@ function commentForMonorepo( const plus = pdiff > 0 ? "+" : ""; const arrow = pdiff === 0 ? "" : pdiff < 0 ? "▾" : "▴"; - const pdiffHtml = baseLcov - ? th(arrow, " ", plus, pdiff.toFixed(2), "%") - : ""; + const pdiffHtml = baseLcov ? th(renderEmoji(pdiff), " ", arrow, " ", plus, pdiff.toFixed(2), "%") : ""; + let report = lcovObj.lcov; + + if (baseLcov) { + const onlyInLcov = lcovObj.lcov.filter(comparer(baseLcov)); + const onlyInBefore = baseLcov.filter(comparer(lcovObj.lcov)); + report = onlyInBefore.concat(onlyInLcov); + } return `${table( tbody( @@ -6060,14 +6088,13 @@ function commentForMonorepo( ), )} \n\n ${details( summary("Coverage Report"), - tabulate(lcovObj.lcov, options), + tabulate(report, options), )}
`; }); - return fragment( - `Coverage after merging into ${b(options.base)}

`, - html.join(""), - ); + const title = `Coverage after merging into ${b(base)}

`; + + return fragment(title, html.join("")); } /** @@ -6076,21 +6103,31 @@ function commentForMonorepo( * @param {*} options */ function comment(lcov, before, options) { + const { appName, base } = options; const pbefore = before ? percentage(before) : 0; const pafter = before ? percentage(lcov) : 0; const pdiff = pafter - pbefore; const plus = pdiff > 0 ? "+" : ""; const arrow = pdiff === 0 ? "" : pdiff < 0 ? "▾" : "▴"; - const pdiffHtml = before ? th(arrow, " ", plus, pdiff.toFixed(2), "%") : ""; + const pdiffHtml = before ? th(renderEmoji(pdiff), " ", arrow, " ", plus, pdiff.toFixed(2), "%") : ""; + + let report = lcov; + + if (before) { + const onlyInLcov = lcov.filter(comparer(before)); + const onlyInBefore = before.filter(comparer(lcov)); + report = onlyInBefore.concat(onlyInLcov); + } + + const title = `Coverage after merging into ${b(base)}

`; + const header = appName ? tbody(tr(th(appName), th(percentage(lcov).toFixed(2), '%'), pdiffHtml)) : tbody(tr(th(percentage(lcov).toFixed(2), '%'), pdiffHtml)); return fragment( - `Coverage after merging ${b(options.head)} into ${b( - options.base, - )}

`, - table(tbody(tr(th(percentage(lcov).toFixed(2), "%"), pdiffHtml))), + title, + table(header), "\n\n", - details(summary("Coverage Report"), tabulate(lcov, options)), + details(summary("Coverage Report"), tabulate(report, options)), ); } @@ -6135,11 +6172,10 @@ function diffForMonorepo( // Every comment written by our action will have this hidden // header on top, and will be used to identify which comments // to update/delete etc -const hiddenHeader = ``; -const appendHiddenHeaderToComment = body => hiddenHeader + body; +const appendHiddenHeaderToComment = (body, hiddenHeader) => hiddenHeader + body; -const listComments = async ({ client, context, prNumber, commentHeader }) => { +const listComments = async ({ client, context, prNumber, commentHeader, hiddenHeader }) => { const { data: existingComments } = await client.issues.listComments({ ...context.repo, issue_number: prNumber, @@ -6148,18 +6184,18 @@ const listComments = async ({ client, context, prNumber, commentHeader }) => { return existingComments.filter(({ body }) => body.startsWith(hiddenHeader)); }; -const insertComment = async ({ client, context, prNumber, body }) => +const insertComment = async ({ client, context, prNumber, body }, hiddenHeader) => client.issues.createComment({ ...context.repo, issue_number: prNumber, - body: appendHiddenHeaderToComment(body), + body: appendHiddenHeaderToComment(body, hiddenHeader), }); -const updateComment = async ({ client, context, body, commentId }) => +const updateComment = async ({ client, context, body, commentId }, hiddenHeader) => client.issues.updateComment({ ...context.repo, comment_id: commentId, - body: appendHiddenHeaderToComment(body), + body: appendHiddenHeaderToComment(body, hiddenHeader), }); const deleteComments = async ({ client, context, comments }) => @@ -6172,11 +6208,12 @@ const deleteComments = async ({ client, context, comments }) => ), ); -const upsertComment = async ({ client, context, prNumber, body }) => { +const upsertComment = async ({ client, context, prNumber, body, hiddenHeader }) => { const existingComments = await listComments({ client, context, prNumber, + hiddenHeader, }); const last = existingComments.pop(); @@ -6192,13 +6229,13 @@ const upsertComment = async ({ client, context, prNumber, body }) => { context, body, commentId: last.id, - }) + }, hiddenHeader) : insertComment({ client, context, prNumber, body, - }); + }, hiddenHeader); }; var github$2 = { @@ -6256,6 +6293,7 @@ async function main() { const token = core$1.getInput("github-token"); const lcovFile = core$1.getInput("lcov-file") || "./coverage/lcov.info"; const baseFile = core$1.getInput("lcov-base"); + const appName = core$1.getInput("app-name"); // Add base path for monorepo const monorepoBasePath = core$1.getInput("monorepo-base-path"); @@ -6309,6 +6347,7 @@ async function main() { prefix: `${process.env.GITHUB_WORKSPACE}/`, head: context.payload.pull_request.head.ref, base: context.payload.pull_request.base.ref, + appName, }; const lcov = !monorepoBasePath && (await parse$1(raw)); @@ -6327,6 +6366,7 @@ async function main() { lcovBaseArrayForMonorepo, options, ), + hiddenHeader: appName ? `` : `` }); } diff --git a/src/comment.js b/src/comment.js index 96ed1da0..5cc594b0 100644 --- a/src/comment.js +++ b/src/comment.js @@ -2,6 +2,28 @@ import { details, summary, b, fragment, table, tbody, tr, th } from "./html"; import { percentage } from "./lcov"; import { tabulate } from "./tabulate"; +/** + * Compares two arrays of objects and returns with unique lines update + * @param {number} pdiff value from diff percentage + * @returns {string} emoji string for negative/positive pdiff + */ +const renderEmoji = pdiff => { + if (pdiff.toFixed(2) < 0) return "❌"; + return "✅"; +}; + +/** + * Compares two arrays of objects and returns with unique lines update + * @param {Array} otherArray + * @returns {Function} function with filtering non original lines + */ +const comparer = otherArray => current => + otherArray.filter( + other => + other.lines.found === current.lines.found && + other.lines.hit === current.lines.hit, + ).length === 0; + /** * Github comment for monorepo * @param {Array<{packageName, lcovPath}>} lcovArrayForMonorepo @@ -13,6 +35,7 @@ export function commentForMonorepo( lcovBaseArrayForMonorepo, options, ) { + const { base } = options; const html = lcovArrayForMonorepo.map(lcovObj => { const baseLcov = lcovBaseArrayForMonorepo.find( el => el.packageName === lcovObj.packageName, @@ -24,9 +47,14 @@ export function commentForMonorepo( const plus = pdiff > 0 ? "+" : ""; const arrow = pdiff === 0 ? "" : pdiff < 0 ? "▾" : "▴"; - const pdiffHtml = baseLcov - ? th(arrow, " ", plus, pdiff.toFixed(2), "%") - : ""; + const pdiffHtml = baseLcov ? th(renderEmoji(pdiff), " ", arrow, " ", plus, pdiff.toFixed(2), "%") : ""; + let report = lcovObj.lcov; + + if (baseLcov) { + const onlyInLcov = lcovObj.lcov.filter(comparer(baseLcov)); + const onlyInBefore = baseLcov.filter(comparer(lcovObj.lcov)); + report = onlyInBefore.concat(onlyInLcov); + } return `${table( tbody( @@ -38,14 +66,13 @@ export function commentForMonorepo( ), )} \n\n ${details( summary("Coverage Report"), - tabulate(lcovObj.lcov, options), + tabulate(report, options), )}
`; }); - return fragment( - `Coverage after merging into ${b(options.base)}

`, - html.join(""), - ); + const title = `Coverage after merging into ${b(base)}

`; + + return fragment(title, html.join("")); } /** @@ -54,21 +81,31 @@ export function commentForMonorepo( * @param {*} options */ export function comment(lcov, before, options) { + const { appName, base } = options; const pbefore = before ? percentage(before) : 0; const pafter = before ? percentage(lcov) : 0; const pdiff = pafter - pbefore; const plus = pdiff > 0 ? "+" : ""; const arrow = pdiff === 0 ? "" : pdiff < 0 ? "▾" : "▴"; - const pdiffHtml = before ? th(arrow, " ", plus, pdiff.toFixed(2), "%") : ""; + const pdiffHtml = before ? th(renderEmoji(pdiff), " ", arrow, " ", plus, pdiff.toFixed(2), "%") : ""; + + let report = lcov; + + if (before) { + const onlyInLcov = lcov.filter(comparer(before)); + const onlyInBefore = before.filter(comparer(lcov)); + report = onlyInBefore.concat(onlyInLcov); + } + + const title = `Coverage after merging into ${b(base)}

`; + const header = appName ? tbody(tr(th(appName), th(percentage(lcov).toFixed(2), '%'), pdiffHtml)) : tbody(tr(th(percentage(lcov).toFixed(2), '%'), pdiffHtml)); return fragment( - `Coverage after merging ${b(options.head)} into ${b( - options.base, - )}

`, - table(tbody(tr(th(percentage(lcov).toFixed(2), "%"), pdiffHtml))), + title, + table(header), "\n\n", - details(summary("Coverage Report"), tabulate(lcov, options)), + details(summary("Coverage Report"), tabulate(report, options)), ); } diff --git a/src/github.js b/src/github.js index 0783802a..ca6d3406 100644 --- a/src/github.js +++ b/src/github.js @@ -11,11 +11,10 @@ // Every comment written by our action will have this hidden // header on top, and will be used to identify which comments // to update/delete etc -const hiddenHeader = ``; -const appendHiddenHeaderToComment = body => hiddenHeader + body; +const appendHiddenHeaderToComment = (body, hiddenHeader) => hiddenHeader + body; -const listComments = async ({ client, context, prNumber, commentHeader }) => { +const listComments = async ({ client, context, prNumber, commentHeader, hiddenHeader }) => { const { data: existingComments } = await client.issues.listComments({ ...context.repo, issue_number: prNumber, @@ -24,18 +23,18 @@ const listComments = async ({ client, context, prNumber, commentHeader }) => { return existingComments.filter(({ body }) => body.startsWith(hiddenHeader)); }; -const insertComment = async ({ client, context, prNumber, body }) => +const insertComment = async ({ client, context, prNumber, body }, hiddenHeader) => client.issues.createComment({ ...context.repo, issue_number: prNumber, - body: appendHiddenHeaderToComment(body), + body: appendHiddenHeaderToComment(body, hiddenHeader), }); -const updateComment = async ({ client, context, body, commentId }) => +const updateComment = async ({ client, context, body, commentId }, hiddenHeader) => client.issues.updateComment({ ...context.repo, comment_id: commentId, - body: appendHiddenHeaderToComment(body), + body: appendHiddenHeaderToComment(body, hiddenHeader), }); const deleteComments = async ({ client, context, comments }) => @@ -48,11 +47,12 @@ const deleteComments = async ({ client, context, comments }) => ), ); -const upsertComment = async ({ client, context, prNumber, body }) => { +const upsertComment = async ({ client, context, prNumber, body, hiddenHeader }) => { const existingComments = await listComments({ client, context, prNumber, + hiddenHeader, }); const last = existingComments.pop(); @@ -68,13 +68,13 @@ const upsertComment = async ({ client, context, prNumber, body }) => { context, body, commentId: last.id, - }) + }, hiddenHeader) : insertComment({ client, context, prNumber, body, - }); + }, hiddenHeader); }; module.exports = { diff --git a/src/index.js b/src/index.js index 6e1cc294..42027d7e 100644 --- a/src/index.js +++ b/src/index.js @@ -56,6 +56,7 @@ async function main() { const token = core.getInput("github-token"); const lcovFile = core.getInput("lcov-file") || "./coverage/lcov.info"; const baseFile = core.getInput("lcov-base"); + const appName = core.getInput("app-name"); // Add base path for monorepo const monorepoBasePath = core.getInput("monorepo-base-path"); @@ -109,6 +110,7 @@ async function main() { prefix: `${process.env.GITHUB_WORKSPACE}/`, head: context.payload.pull_request.head.ref, base: context.payload.pull_request.base.ref, + appName, }; const lcov = !monorepoBasePath && (await parse(raw)); @@ -127,6 +129,7 @@ async function main() { lcovBaseArrayForMonorepo, options, ), + hiddenHeader: appName ? `` : `` }); }