Skip to content

Commit

Permalink
feat: Only applies covarge to unchanged files
Browse files Browse the repository at this point in the history
  • Loading branch information
davelosert committed Nov 2, 2024
1 parent 2a741d6 commit 65fa212
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 95 deletions.
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
parseVitestJsonFinal,
parseVitestJsonSummary,
} from "./inputs/parseJsonReports.js";
import { createOctokit, type Octokit } from "./octokit.js";
import { type Octokit, createOctokit } from "./octokit.js";
import { generateCommitSHAUrl } from "./report/generateCommitSHAUrl.js";
import { generateFileCoverageHtml } from "./report/generateFileCoverageHtml.js";
import { generateHeadline } from "./report/generateHeadline.js";
Expand Down
4 changes: 2 additions & 2 deletions src/inputs/getCommentOn.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import * as core from "@actions/core";
import { getCommentOn, type CommentOn } from "./getCommentOn";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { type CommentOn, getCommentOn } from "./getCommentOn";

vi.mock("@actions/core");

Expand Down
76 changes: 38 additions & 38 deletions src/inputs/getPullChanges.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { RequestError } from "@octokit/request-error";
import { Mock, beforeEach, describe, expect, it, vi } from "vitest";
import type { Octokit } from "../octokit";
import { RequestError } from "@octokit/request-error";
import { FileCoverageMode } from "./FileCoverageMode";
import { getPullChanges } from "./getPullChanges";

Expand Down Expand Up @@ -68,48 +68,48 @@ describe("getPullChanges()", () => {
expect(result).toEqual(["file1.ts", "file2.ts"]);
});

it("handles RequestError with status 404 gracefully", async () => {
mockOctokit.paginate.iterator = vi.fn().mockImplementation(async function* () {
throw new RequestError("Not Found", 404, {
request: { headers: {}, method: "GET", url: "" },
});
it("handles RequestError with status 404 gracefully", async () => {
mockOctokit.paginate.iterator = vi.fn().mockImplementation(async () => {
throw new RequestError("Not Found", 404, {
request: { headers: {}, method: "GET", url: "" },
});

const result = await getPullChanges({
fileCoverageMode: FileCoverageMode.Changes,
prNumber: 123,
octokit: mockOctokit,
});

expect(result).toEqual([]);
});

it("handles RequestError with status 403 gracefully", async () => {
mockOctokit.paginate.iterator = vi.fn().mockImplementation(async function* () {
throw new RequestError("Forbidden", 403, {
request: { headers: {}, method: "GET", url: "" },
});
});

const result = await getPullChanges({
fileCoverageMode: FileCoverageMode.Changes,
prNumber: 123,
octokit: mockOctokit,
});

expect(result).toEqual([]);
const result = await getPullChanges({
fileCoverageMode: FileCoverageMode.Changes,
prNumber: 123,
octokit: mockOctokit,
});

it("throws an error for other exceptions", async () => {
mockOctokit.paginate.iterator = vi.fn().mockImplementation(async function* () {
throw new Error("Unexpected error");
expect(result).toEqual([]);
});

it("handles RequestError with status 403 gracefully", async () => {
mockOctokit.paginate.iterator = vi.fn().mockImplementation(async () => {
throw new RequestError("Forbidden", 403, {
request: { headers: {}, method: "GET", url: "" },
});
await expect(
getPullChanges({
fileCoverageMode: FileCoverageMode.Changes,
prNumber: 123,
octokit: mockOctokit,
}),
).rejects.toThrow("Unexpected error");
});

const result = await getPullChanges({
fileCoverageMode: FileCoverageMode.Changes,
prNumber: 123,
octokit: mockOctokit,
});

expect(result).toEqual([]);
});

it("throws an error for other exceptions", async () => {
mockOctokit.paginate.iterator = vi.fn().mockImplementation(async () => {
throw new Error("Unexpected error");
});
await expect(

Check failure on line 107 in src/inputs/getPullChanges.test.ts

View workflow job for this annotation

GitHub Actions / test (feat-add-file-compare)

AssertionError: expected [Function] to throw error including 'Unexpected error' but got 'iterator is not async iterable' Expected: "Unexpected error" Received: "iterator is not async iterable" ❯ src/inputs/getPullChanges.test.ts:107:3
getPullChanges({
fileCoverageMode: FileCoverageMode.Changes,
prNumber: 123,
octokit: mockOctokit,
}),
).rejects.toThrow("Unexpected error");
});
});
2 changes: 1 addition & 1 deletion src/inputs/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import * as core from "@actions/core";
import type { Octokit } from "../octokit";
import type { Thresholds } from "../types/Threshold";
import { type FileCoverageMode, getCoverageModeFrom } from "./FileCoverageMode";
import { type CommentOn, getCommentOn } from "./getCommentOn";
import { getCommitSHA } from "./getCommitSHA";
import { getPullRequestNumber } from "./getPullRequestNumber";
import { getViteConfigPath } from "./getViteConfigPath";
import { parseCoverageThresholds } from "./parseCoverageThresholds";
import { getCommentOn, type CommentOn } from "./getCommentOn";

type Options = {
fileCoverageMode: FileCoverageMode;
Expand Down
2 changes: 1 addition & 1 deletion src/report/generateFileCoverageHtml.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { getTableLine } from "../../test/queryHelper";
import { icons } from "../icons";
import { FileCoverageMode } from "../inputs/FileCoverageMode";
import type { JsonFinal } from "../types/JsonFinal";
import { createJsonFinalEntry } from "../types/JsonFinalMockFactory";
Expand All @@ -11,7 +12,6 @@ import {
createMockReportNumbers,
} from "../types/JsonSummaryMockFactory";
import { generateFileCoverageHtml } from "./generateFileCoverageHtml";
import { icons } from "../icons";

const workspacePath = process.cwd();
describe("generateFileCoverageHtml()", () => {
Expand Down
130 changes: 78 additions & 52 deletions src/report/generateFileCoverageHtml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ import { FileCoverageMode } from "../inputs/FileCoverageMode";
import type { JsonFinal } from "../types/JsonFinal";
import type { CoverageReport, JsonSummary } from "../types/JsonSummary";
import { generateBlobFileUrl } from "./generateFileUrl";
import { getCompareString } from "./getCompareString";
import {
type LineRange,
getUncoveredLinesFromStatements,
} from "./getUncoveredLinesFromStatements";
import { icons } from "../icons";
import { getCompareString } from "./getCompareString";

type FileCoverageInputs = {
jsonSummary: JsonSummary;
Expand All @@ -21,6 +20,7 @@ type FileCoverageInputs = {
};

const workspacePath = process.cwd();

const generateFileCoverageHtml = ({
jsonSummary,
jsonSummaryCompare,
Expand All @@ -31,31 +31,6 @@ const generateFileCoverageHtml = ({
}: FileCoverageInputs) => {
const filePaths = Object.keys(jsonSummary).filter((key) => key !== "total");

const formatFileLine = (filePath: string) => {
const coverageSummary = jsonSummary[filePath];
const coverageSummaryCompare = jsonSummaryCompare
? jsonSummaryCompare[filePath]
: undefined;
const lineCoverage = jsonFinal[filePath];

// LineCoverage might be empty if coverage-final.json was not provided.
const uncoveredLines = lineCoverage
? getUncoveredLinesFromStatements(jsonFinal[filePath])
: [];
const relativeFilePath = path.relative(workspacePath, filePath);
const url = generateBlobFileUrl(relativeFilePath, commitSHA);

return `
<tr>
<td align="left"><a href="${url}">${relativeFilePath}</a></td>
${generateCoverageCell(coverageSummary, coverageSummaryCompare, "statements")}
${generateCoverageCell(coverageSummary, coverageSummaryCompare, "branches")}
${generateCoverageCell(coverageSummary, coverageSummaryCompare, "functions")}
${generateCoverageCell(coverageSummary, coverageSummaryCompare, "lines")}
<td align="left">${createRangeURLs(uncoveredLines, url)}</td>
</tr>`;
};

let reportData = "";

const [changedFiles, unchangedFiles] = splitFilesByChangeStatus(
Expand All @@ -72,37 +47,88 @@ const generateFileCoverageHtml = ({

if (changedFiles.length > 0) {
reportData += `
${formatGroupLine("Changed Files")}
${changedFiles.map(formatFileLine).join("")}
`;
${formatGroupLine("Changed Files")}
${changedFiles
.map((filePath) =>
generateRow(
filePath,
jsonSummary,
jsonSummaryCompare,
jsonFinal,
commitSHA,
),
)
.join("")}
`;
}

if (fileCoverageMode === FileCoverageMode.All && unchangedFiles.length > 0) {
reportData += `
${formatGroupLine("Unchanged Files")}
${unchangedFiles.map(formatFileLine).join("")}
`;
${formatGroupLine("Unchanged Files")}
${unchangedFiles
.map((filePath) =>
generateRow(
filePath,
jsonSummary,
undefined,
jsonFinal,
commitSHA,
),
)
.join("")}
`;
}

return oneLine`
<table>
<thead>
<tr>
<th align="left">File</th>
<th align="right">Stmts</th>
<th align="right">% Branch</th>
<th align="right">% Funcs</th>
<th align="right">% Lines</th>
<th align="left">Uncovered Lines</th>
</tr>
</thead>
<tbody>
${reportData}
</tbody>
</table>
`;
<table>
<thead>
<tr>
<th align="left">File</th>
<th align="right">Stmts</th>
<th align="right">% Branch</th>
<th align="right">% Funcs</th>
<th align="right">% Lines</th>
<th align="left">Uncovered Lines</th>
</tr>
</thead>
<tbody>
${reportData}
</tbody>
</table>
`;
};

function generateRow(
filePath: string,
jsonSummary: JsonSummary,
jsonSummaryCompare: JsonSummary | undefined,
jsonFinal: JsonFinal,
commitSHA: string,
): string {
const coverageSummary = jsonSummary[filePath];
const coverageSummaryCompare = jsonSummaryCompare
? jsonSummaryCompare[filePath]
: undefined;
const lineCoverage = jsonFinal[filePath];

// LineCoverage might be empty if coverage-final.json was not provided.
const uncoveredLines = lineCoverage
? getUncoveredLinesFromStatements(jsonFinal[filePath])
: [];
const relativeFilePath = path.relative(workspacePath, filePath);
const url = generateBlobFileUrl(relativeFilePath, commitSHA);

return `
<tr>
<td align="left"><a href="${url}">${relativeFilePath}</a></td>
${generateCoverageCell(coverageSummary, coverageSummaryCompare, "statements")}
${generateCoverageCell(coverageSummary, coverageSummaryCompare, "branches")}
${generateCoverageCell(coverageSummary, coverageSummaryCompare, "functions")}
${generateCoverageCell(coverageSummary, coverageSummaryCompare, "lines")}
<td align="left">${createRangeURLs(uncoveredLines, url)}</td>
</tr>`;
}

function generateCoverageCell(
summary: CoverageReport,
summaryCompare: CoverageReport | undefined,
Expand All @@ -118,9 +144,9 @@ function generateCoverageCell(

function formatGroupLine(caption: string): string {
return `
<tr>
<td align="left" colspan="6"><b>${caption}</b></td>
</tr>
<tr>
<td align="left" colspan="6"><b>${caption}</b></td>
</tr>
`;
}

Expand Down

0 comments on commit 65fa212

Please sign in to comment.