Skip to content

Commit

Permalink
test: more testeable code, multiple comments
Browse files Browse the repository at this point in the history
  • Loading branch information
Andres Adjimann committed Sep 11, 2023
1 parent 0a2dcdf commit 489014f
Show file tree
Hide file tree
Showing 9 changed files with 263 additions and 227 deletions.
3 changes: 3 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ inputs:
app-name:
description: App name to display on comment
required: false
multiple-comment:
description: Add multiple comments
required: false
max_lines:
description: Maximum numbers of line print
required: false
Expand Down
4 changes: 2 additions & 2 deletions dist/main.js

Large diffs are not rendered by default.

30 changes: 18 additions & 12 deletions src/badge.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import path from "path";
import fs from "fs";
import { badgen } from "badgen";
import path from "path";

Check failure on line 2 in src/badge.js

View workflow job for this annotation

GitHub Actions / Code quality

`path` import should occur before import of `badgen`
import { percentage } from "./lcov";

const badge = (option, pct) => {
export const badge = (option, pct) => {

Check failure on line 5 in src/badge.js

View workflow job for this annotation

GitHub Actions / Code quality

exported declaration 'badge' not used within other modules
const { label = "coverage", style = "classic" } = option || {};
const colorData = {
"49c31a": [100],
Expand All @@ -28,20 +27,27 @@ const badge = (option, pct) => {
return badgen(badgenArgs);
};

export const createBadges = (badgePath, toCheck, options) => {
const dirName = path.resolve(badgePath);
if (!fs.existsSync(dirName)) {
fs.mkdirSync(dirName);
} else if (!fs.statSync(dirName).isDirectory()) {
throw new Error("badge path is not a directory");
export const badges = ({ rootLcov, lcovArray, options, mkDir, writeBadge }) => {
const badgeOptions = {
label: options.badgeLabel,
style: options.badgeStyle,
};
const toBadge = lcovArray.map((x) => x);
if (rootLcov) {
toBadge.push({
packageName: "root_package",
lcov: rootLcov,
});
}
for (const lcovObj of toCheck) {
const dirName = path.resolve(options.badgePath);
mkDir(dirName);
for (const lcovObj of toBadge) {
const coverage = percentage(lcovObj.lcov);
const svgStr = badge(options, coverage.toFixed(2));
const svgStr = badge(badgeOptions, coverage.toFixed(2));
const fileName = path.join(dirName, `${lcovObj.packageName}.svg`);
console.log("writing badge", fileName);
try {
fs.writeFileSync(fileName, svgStr);
writeBadge(fileName, svgStr);
} catch (err) {
console.error("Error writing badge", fileName, err.toString());
}
Expand Down
38 changes: 35 additions & 3 deletions src/check.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,51 @@
import { percentage } from "./lcov";

export const checkCoverage = (minCoverage, toCheck) => {
const checkCoverage = (toCheck) => {
let accum = 0;
for (const lcovObj of toCheck) {
const coverage = percentage(lcovObj.lcov);
const isValidBuild = coverage >= minCoverage;
const isValidBuild = coverage >= toCheck.minCoverage;
if (!isValidBuild) {
return { isValidBuild, coverage, name: lcovObj.packageName };
}
accum += coverage;
}

return {
isValidBuild: true,
coverage: toCheck.length === 0 ? 0 : accum / toCheck.length,
name: "average",
};
};

export const checks = ({ rootLcov, lcovArray, options, setFailed, info }) => {
const excludedFiles = (options.excluded || "")
.split(" ")
.map((x) => x.trim())
.filter((x) => x.length > 0);
const toCheck = lcovArray
.filter((x) => !excludedFiles.some((y) => x.packageName === y))
.map((x) => ({ minCoverage: options.minCoverage, ...x }));
if (rootLcov && !options.excludeRoot) {
toCheck.unshift({
packageName: "root_package",
lcov: rootLcov,
minCoverage: options.minCoverage,
});
}
const { isValidBuild, coverage, name } = checkCoverage(toCheck);
if (!isValidBuild) {
setFailed(
`${coverage.toFixed(2)}% for ${name} is less than min_coverage ${
options.minCoverage
}.`,
);
} else {
info(
`Coverage: ${coverage.toFixed(
2,
)}% is greater than or equal to min_coverage ${
options.minCoverage
}.`,
);
}
};
90 changes: 40 additions & 50 deletions src/cli.js
Original file line number Diff line number Diff line change
@@ -1,63 +1,53 @@
import process from "process";
import fs from "fs";
import path from "path";
import { diff, diffForMonorepo } from "./comment";
import { getLcovArray, readLcov } from "./monorepo";
import { checkCoverage } from "./check";
import { createBadges } from "./badge";
import { codeCoverageAssistant } from "./codeCoverageAssistant";
import { getLcovArray } from "./monorepo";

const addPackageName = (x) => ({
...x,
...{ packageName: x.dir.split("/").slice(-2)[0] },
});
const main = async () => {
const file = process.argv[2];
const beforeFile = process.argv[3];

const prefix = `${path.dirname(path.dirname(path.resolve(file)))}/`;
const options = {
repository: "example/foo",
commit: "f9d42291812ed03bb197e48050ac38ac6befe4e5",
prefix,
head: "feat/test",
base: "master",
maxLines: "10",
const baseClient = {
upsert: (title, body) => {
console.log("--> COMMENT", "title:", title, "body:", body);
},
mkDir: (dirName) => console.log("Creating directory", dirName),
writeBadge: (fileName) => console.log("written", fileName),
setFailed: (...x) => console.error("ERROR", ...x),
info: (...x) => console.log("INFO:", ...x),
options: {
repository: "example/foo",
prefix,
pullRequest: {
commit: "f9d42291812ed03bb197e48050ac38ac6befe4e5",
head: "feat/test",
base: "master",
},
maxLines: 100,
badgePath: "./badges",
appName: "someAppName",
minCoverage: 80,
multipleComment: true,
},
};

if (fs.statSync(file).isDirectory()) {
const lcovArrayForMonorepo = (
await getLcovArray(file, "lcov.info")
).map(addPackageName);
console.log(
diffForMonorepo(
lcovArrayForMonorepo,
await getLcovArray(file, "lcov-base"),
options,
),
);
createBadges("./badges", lcovArrayForMonorepo, {});
console.log(checkCoverage(90, lcovArrayForMonorepo));
} else {
const lcov = await readLcov(file);
console.log(
diff(lcov, beforeFile && (await readLcov(beforeFile)), options),
);
createBadges(
"./build",
{
packageName: "root",
lcov,
await codeCoverageAssistant({
...baseClient,
readLCovs: async (fileFilter) => {
const arr = await getLcovArray(file, fileFilter);
return arr.map((x) => ({
...x,
packageName: x.dir.split("/").slice(-2)[0],
}));
},
{},
);
console.log(
checkCoverage(90, [
{
packageName: "root",
lcov,
},
]),
);
});
} else {
await codeCoverageAssistant({
...baseClient,
readLCovs: () => [],
lcovFile: file,
baseFile: "lcov-base",
});
}
};

Expand Down
40 changes: 40 additions & 0 deletions src/codeCoverageAssistant.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { readLcov } from "./monorepo";

Check failure on line 1 in src/codeCoverageAssistant.js

View workflow job for this annotation

GitHub Actions / Code quality

Filename 'codeCoverageAssistant.js' does not match the naming convention
import { comments } from "./comment";
import { checks } from "./check";
import { badges } from "./badge";

export const codeCoverageAssistant = async ({
readLCovs,
lcovFile,
baseFile,
options,
upsert,
mkDir,
writeBadge,
setFailed,
info,
}) => {
const lcovArray = await readLCovs("lcov.info");
const lcovBaseArray =
lcovArray.length > 0 && (await readLCovs("lcov-base"));
// Always process root file if exists.
const rootLcov = lcovFile && (await readLcov(lcovFile));
const rootBaseLcov = rootLcov && baseFile && (await readLcov(baseFile));
// We cannot comment if is not a pull_request
if (options.maxLines > 0) {
await comments({
rootLcov,
rootBaseLcov,
lcovArray,
lcovBaseArray,
options,
upsert,
});
}
if (options.badgePath) {
await badges({ rootLcov, lcovArray, options, mkDir, writeBadge });
}
if (options.minCoverage) {
await checks({ rootLcov, lcovArray, options, setFailed, info });
}
};
91 changes: 63 additions & 28 deletions src/comment.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { details, summary, b, fragment, table, tbody, tr, th, p } from "./html";
import { b, details, fragment, p, summary, table, tbody, th, tr } from "./html";
import { percentage } from "./lcov";
import { tabulate } from "./tabulate";

Expand Down Expand Up @@ -35,7 +35,6 @@ const comparer = (otherArray) => (current) =>
).length === 0;

const renderLcov = (lcov, base, appTitle, options) => {
const maxLines = options.maxLines || 15;
const pbefore = base ? percentage(base) : 0;
const pafter = base ? percentage(lcov) : 0;
const pdiff = pafter - pbefore;
Expand Down Expand Up @@ -71,7 +70,7 @@ const renderLcov = (lcov, base, appTitle, options) => {
}
return [
table(tbody(tr(...row))),
report.length > maxLines
report.length > options.maxLines
? p("Coverage Report too long to display")
: details(summary("Coverage Report"), tabulate(report, options)),
"<br/>",
Expand All @@ -81,10 +80,10 @@ const renderLcov = (lcov, base, appTitle, options) => {
/**
* Github comment for monorepo
* @param {Array<{packageName, lcovPath}>} lcovArrayForMonorepo
* @param {{Array<{packageName, lcovBasePath}>}} lcovBaseArrayForMonorepo
* @param {Array<{packageName, lcovPath}>} lcovBaseArrayForMonorepo
* @param {*} options
*/
const commentForMonorepo = (
const renderCommentArray = (
lcovArrayForMonorepo,
lcovBaseArrayForMonorepo,
options,
Expand All @@ -110,32 +109,68 @@ const commentForMonorepo = (

/**
* Github comment for single repo
* @param {raw lcov} lcov
* @param {raw} lcov
* @param {raw} baseLcov
* @param {string} appTitle
* @param {*} options
*/
const comment = (lcov, before, options) => {
const { appName, base } = options;
const renderCommentLine = (lcov, baseLcov, appTitle, options) => {
const inner = renderLcov(lcov, baseLcov, appTitle, options);
const { base } = options;
const title = `Coverage after merging into ${b(base)} <p></p>`;
return fragment(title, renderLcov(lcov, before, appName, options));
return fragment(title, inner);
};

/**
* Diff in coverage percentage for single repo
* @param {raw lcov} lcov
* @param {raw base lcov} before
* @param {*} options
*/
export const diff = (lcov, before, options) => comment(lcov, before, options);

/**
* Diff in coverage percentage for monorepo
* @param {Array<{packageName, lcovPath}>} lcovArrayForMonorepo
* @param {{Array<{packageName, lcovBasePath}>}} lcovBaseArrayForMonorepo
* @param {*} options
*/
export const diffForMonorepo = (
lcovArrayForMonorepo,
lcovBaseArrayForMonorepo,
export const comments = async ({
rootLcov,
rootBaseLcov,
lcovArray,
lcovBaseArray,
options,
) =>
commentForMonorepo(lcovArrayForMonorepo, lcovBaseArrayForMonorepo, options);
upsert,
}) => {
if (!options.pullRequest) {
return;
}
const renderOptions = {
maxLines: options.maxLines,
prefix: options.prefix,
repository: options.repository,
commit: options.pullRequest.head.sha,
head: options.pullRequest.head.ref,
base: options.pullRequest.base.ref,
};
// Comments
if (lcovArray.length > 0) {
if (options.multipleComment) {
for (const lcovObj of lcovArray) {
const baseLcov = lcovBaseArray.find(
(el) => el.packageName === lcovObj.packageName,
);
const body = renderCommentLine(
lcovObj.lcov,
baseLcov && baseLcov.lcov,
lcovObj.packageName,
renderOptions,
);
await upsert(`m-${lcovObj.packageName}`, body);
}
} else {
const body = renderCommentArray(
lcovArray,
lcovBaseArray,
renderOptions,
);
await upsert("single-comment", body);
}
}
if (rootLcov) {
const body = renderCommentLine(
rootLcov,
rootBaseLcov,
"root",
renderOptions,
);
await upsert(`root-comment`, body);
}
};
Loading

0 comments on commit 489014f

Please sign in to comment.