Skip to content

Commit 42d0a9b

Browse files
author
Andres Adjimann
committed
test: more testeable code, multiple comments
1 parent 0a2dcdf commit 42d0a9b

File tree

9 files changed

+260
-227
lines changed

9 files changed

+260
-227
lines changed

action.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ inputs:
2020
app-name:
2121
description: App name to display on comment
2222
required: false
23+
multiple-comment:
24+
description: Add multiple comments
25+
required: false
2326
max_lines:
2427
description: Maximum numbers of line print
2528
required: false

dist/main.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/badge.js

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import path from "path";
2-
import fs from "fs";
31
import { badgen } from "badgen";
2+
import path from "path";
43
import { percentage } from "./lcov";
54

6-
const badge = (option, pct) => {
5+
export const badge = (option, pct) => {
76
const { label = "coverage", style = "classic" } = option || {};
87
const colorData = {
98
"49c31a": [100],
@@ -28,20 +27,27 @@ const badge = (option, pct) => {
2827
return badgen(badgenArgs);
2928
};
3029

31-
export const createBadges = (badgePath, toCheck, options) => {
32-
const dirName = path.resolve(badgePath);
33-
if (!fs.existsSync(dirName)) {
34-
fs.mkdirSync(dirName);
35-
} else if (!fs.statSync(dirName).isDirectory()) {
36-
throw new Error("badge path is not a directory");
30+
export const badges = ({ rootLcov, lcovArray, options, mkDir, writeBadge }) => {
31+
const badgeOptions = {
32+
label: options.badgeLabel,
33+
style: options.badgeStyle,
34+
};
35+
const toBadge = lcovArray.map((x) => x);
36+
if (rootLcov) {
37+
toBadge.push({
38+
packageName: "root_package",
39+
lcov: rootLcov,
40+
});
3741
}
38-
for (const lcovObj of toCheck) {
42+
const dirName = path.resolve(options.badgePath);
43+
mkDir(dirName);
44+
for (const lcovObj of toBadge) {
3945
const coverage = percentage(lcovObj.lcov);
40-
const svgStr = badge(options, coverage.toFixed(2));
46+
const svgStr = badge(badgeOptions, coverage.toFixed(2));
4147
const fileName = path.join(dirName, `${lcovObj.packageName}.svg`);
4248
console.log("writing badge", fileName);
4349
try {
44-
fs.writeFileSync(fileName, svgStr);
50+
writeBadge(fileName, svgStr);
4551
} catch (err) {
4652
console.error("Error writing badge", fileName, err.toString());
4753
}

src/check.js

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,51 @@
11
import { percentage } from "./lcov";
22

3-
export const checkCoverage = (minCoverage, toCheck) => {
3+
const checkCoverage = (toCheck) => {
44
let accum = 0;
55
for (const lcovObj of toCheck) {
66
const coverage = percentage(lcovObj.lcov);
7-
const isValidBuild = coverage >= minCoverage;
7+
const isValidBuild = coverage >= toCheck.minCoverage;
88
if (!isValidBuild) {
99
return { isValidBuild, coverage, name: lcovObj.packageName };
1010
}
1111
accum += coverage;
1212
}
13-
1413
return {
1514
isValidBuild: true,
1615
coverage: toCheck.length === 0 ? 0 : accum / toCheck.length,
1716
name: "average",
1817
};
1918
};
19+
20+
export const checks = ({ rootLcov, lcovArray, options, setFailed, info }) => {
21+
const excludedFiles = (options.excluded || "")
22+
.split(" ")
23+
.map((x) => x.trim())
24+
.filter((x) => x.length > 0);
25+
const toCheck = lcovArray
26+
.filter((x) => !excludedFiles.some((y) => x.packageName === y))
27+
.map((x) => ({ minCoverage: options.minCoverage, ...x }));
28+
if (rootLcov && !options.excludeRoot) {
29+
toCheck.unshift({
30+
packageName: "root_package",
31+
lcov: rootLcov,
32+
minCoverage: options.minCoverage,
33+
});
34+
}
35+
const { isValidBuild, coverage, name } = checkCoverage(toCheck);
36+
if (!isValidBuild) {
37+
setFailed(
38+
`${coverage.toFixed(2)}% for ${name} is less than min_coverage ${
39+
options.minCoverage
40+
}.`,
41+
);
42+
} else {
43+
info(
44+
`Coverage: ${coverage.toFixed(
45+
2,
46+
)}% is greater than or equal to min_coverage ${
47+
options.minCoverage
48+
}.`,
49+
);
50+
}
51+
};

src/cli.js

Lines changed: 40 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,53 @@
11
import process from "process";
22
import fs from "fs";
33
import path from "path";
4-
import { diff, diffForMonorepo } from "./comment";
5-
import { getLcovArray, readLcov } from "./monorepo";
6-
import { checkCoverage } from "./check";
7-
import { createBadges } from "./badge";
4+
import { codeCoverageAssistant } from "./codeCoverageAssistant";
5+
import { getLcovArray } from "./monorepo";
86

9-
const addPackageName = (x) => ({
10-
...x,
11-
...{ packageName: x.dir.split("/").slice(-2)[0] },
12-
});
137
const main = async () => {
148
const file = process.argv[2];
15-
const beforeFile = process.argv[3];
16-
179
const prefix = `${path.dirname(path.dirname(path.resolve(file)))}/`;
18-
const options = {
19-
repository: "example/foo",
20-
commit: "f9d42291812ed03bb197e48050ac38ac6befe4e5",
21-
prefix,
22-
head: "feat/test",
23-
base: "master",
24-
maxLines: "10",
10+
const baseClient = {
11+
upsert: (title, body) => {
12+
console.log("--> COMMENT", "title:", title, "body:", body);
13+
},
14+
mkDir: (dirName) => console.log("Creating directory", dirName),
15+
writeBadge: (fileName) => console.log("written", fileName),
16+
setFailed: (...x) => console.error("ERROR", ...x),
17+
info: (...x) => console.log("INFO:", ...x),
18+
options: {
19+
repository: "example/foo",
20+
prefix,
21+
pullRequest: {
22+
commit: "f9d42291812ed03bb197e48050ac38ac6befe4e5",
23+
head: "feat/test",
24+
base: "master",
25+
},
26+
maxLines: 100,
27+
badgePath: "./badges",
28+
appName: "someAppName",
29+
minCoverage: 80,
30+
multipleComment: true,
31+
},
2532
};
26-
2733
if (fs.statSync(file).isDirectory()) {
28-
const lcovArrayForMonorepo = (
29-
await getLcovArray(file, "lcov.info")
30-
).map(addPackageName);
31-
console.log(
32-
diffForMonorepo(
33-
lcovArrayForMonorepo,
34-
await getLcovArray(file, "lcov-base"),
35-
options,
36-
),
37-
);
38-
createBadges("./badges", lcovArrayForMonorepo, {});
39-
console.log(checkCoverage(90, lcovArrayForMonorepo));
40-
} else {
41-
const lcov = await readLcov(file);
42-
console.log(
43-
diff(lcov, beforeFile && (await readLcov(beforeFile)), options),
44-
);
45-
createBadges(
46-
"./build",
47-
{
48-
packageName: "root",
49-
lcov,
34+
await codeCoverageAssistant({
35+
...baseClient,
36+
readLCovs: async (fileFilter) => {
37+
const arr = await getLcovArray(file, fileFilter);
38+
return arr.map((x) => ({
39+
...x,
40+
packageName: x.dir.split("/").slice(-2)[0],
41+
}));
5042
},
51-
{},
52-
);
53-
console.log(
54-
checkCoverage(90, [
55-
{
56-
packageName: "root",
57-
lcov,
58-
},
59-
]),
60-
);
43+
});
44+
} else {
45+
await codeCoverageAssistant({
46+
...baseClient,
47+
readLCovs: () => [],
48+
lcovFile: file,
49+
baseFile: "lcov-base",
50+
});
6151
}
6252
};
6353

src/codeCoverageAssistant.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { readLcov } from "./monorepo";
2+
import { comments } from "./comment";
3+
import { checks } from "./check";
4+
import { badges } from "./badge";
5+
6+
export const codeCoverageAssistant = async ({
7+
readLCovs,
8+
lcovFile,
9+
baseFile,
10+
options,
11+
upsert,
12+
mkDir,
13+
writeBadge,
14+
setFailed,
15+
info,
16+
}) => {
17+
const lcovArray = await readLCovs("lcov.info");
18+
const lcovBaseArray =
19+
lcovArray.length > 0 && (await readLCovs("lcov-base"));
20+
// Always process root file if exists.
21+
const rootLcov = lcovFile && (await readLcov(lcovFile));
22+
const rootBaseLcov = rootLcov && baseFile && (await readLcov(baseFile));
23+
// We cannot comment if is not a pull_request
24+
if (options.pullRequest && options.maxLines > 0) {
25+
await comments({
26+
rootLcov,
27+
rootBaseLcov,
28+
lcovArray,
29+
lcovBaseArray,
30+
options,
31+
upsert,
32+
});
33+
}
34+
if (options.badgePath) {
35+
await badges({ rootLcov, lcovArray, options, mkDir, writeBadge });
36+
}
37+
if (options.minCoverage) {
38+
await checks({ rootLcov, lcovArray, options, setFailed, info });
39+
}
40+
};

src/comment.js

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { details, summary, b, fragment, table, tbody, tr, th, p } from "./html";
1+
import { b, details, fragment, p, summary, table, tbody, th, tr } from "./html";
22
import { percentage } from "./lcov";
33
import { tabulate } from "./tabulate";
44

@@ -35,7 +35,6 @@ const comparer = (otherArray) => (current) =>
3535
).length === 0;
3636

3737
const renderLcov = (lcov, base, appTitle, options) => {
38-
const maxLines = options.maxLines || 15;
3938
const pbefore = base ? percentage(base) : 0;
4039
const pafter = base ? percentage(lcov) : 0;
4140
const pdiff = pafter - pbefore;
@@ -71,7 +70,7 @@ const renderLcov = (lcov, base, appTitle, options) => {
7170
}
7271
return [
7372
table(tbody(tr(...row))),
74-
report.length > maxLines
73+
report.length > options.maxLines
7574
? p("Coverage Report too long to display")
7675
: details(summary("Coverage Report"), tabulate(report, options)),
7776
"<br/>",
@@ -81,10 +80,10 @@ const renderLcov = (lcov, base, appTitle, options) => {
8180
/**
8281
* Github comment for monorepo
8382
* @param {Array<{packageName, lcovPath}>} lcovArrayForMonorepo
84-
* @param {{Array<{packageName, lcovBasePath}>}} lcovBaseArrayForMonorepo
83+
* @param {Array<{packageName, lcovPath}>} lcovBaseArrayForMonorepo
8584
* @param {*} options
8685
*/
87-
const commentForMonorepo = (
86+
const renderCommentArray = (
8887
lcovArrayForMonorepo,
8988
lcovBaseArrayForMonorepo,
9089
options,
@@ -110,32 +109,65 @@ const commentForMonorepo = (
110109

111110
/**
112111
* Github comment for single repo
113-
* @param {raw lcov} lcov
112+
* @param {raw} lcov
113+
* @param {raw} baseLcov
114+
* @param {string} appTitle
114115
* @param {*} options
115116
*/
116-
const comment = (lcov, before, options) => {
117-
const { appName, base } = options;
117+
const renderCommentLine = (lcov, baseLcov, appTitle, options) => {
118+
const inner = renderLcov(lcov, baseLcov, appTitle, options);
119+
const { base } = options;
118120
const title = `Coverage after merging into ${b(base)} <p></p>`;
119-
return fragment(title, renderLcov(lcov, before, appName, options));
121+
return fragment(title, inner);
120122
};
121123

122-
/**
123-
* Diff in coverage percentage for single repo
124-
* @param {raw lcov} lcov
125-
* @param {raw base lcov} before
126-
* @param {*} options
127-
*/
128-
export const diff = (lcov, before, options) => comment(lcov, before, options);
129-
130-
/**
131-
* Diff in coverage percentage for monorepo
132-
* @param {Array<{packageName, lcovPath}>} lcovArrayForMonorepo
133-
* @param {{Array<{packageName, lcovBasePath}>}} lcovBaseArrayForMonorepo
134-
* @param {*} options
135-
*/
136-
export const diffForMonorepo = (
137-
lcovArrayForMonorepo,
138-
lcovBaseArrayForMonorepo,
124+
export const comments = async ({
125+
rootLcov,
126+
rootBaseLcov,
127+
lcovArray,
128+
lcovBaseArray,
139129
options,
140-
) =>
141-
commentForMonorepo(lcovArrayForMonorepo, lcovBaseArrayForMonorepo, options);
130+
upsert,
131+
}) => {
132+
const renderOptions = {
133+
maxLines: options.maxLines,
134+
prefix: options.prefix,
135+
repository: options.repository,
136+
commit: options.pullRequest.head.sha,
137+
head: options.pullRequest.head.ref,
138+
base: options.pullRequest.base.ref,
139+
};
140+
// Comments
141+
if (lcovArray.length > 0) {
142+
if (options.multipleComment) {
143+
for (const lcovObj of lcovArray) {
144+
const baseLcov = lcovBaseArray.find(
145+
(el) => el.packageName === lcovObj.packageName,
146+
);
147+
const body = renderCommentLine(
148+
lcovObj.lcov,
149+
baseLcov && baseLcov.lcov,
150+
lcovObj.packageName,
151+
renderOptions,
152+
);
153+
await upsert(`m-${lcovObj.packageName}`, body);
154+
}
155+
} else {
156+
const body = renderCommentArray(
157+
lcovArray,
158+
lcovBaseArray,
159+
renderOptions,
160+
);
161+
await upsert("single-comment", body);
162+
}
163+
}
164+
if (rootLcov) {
165+
const body = renderCommentLine(
166+
rootLcov,
167+
rootBaseLcov,
168+
"root",
169+
renderOptions,
170+
);
171+
await upsert(`root-comment`, body);
172+
}
173+
};

0 commit comments

Comments
 (0)