diff --git a/README.md b/README.md index cefdb62..75eb0cc 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,9 @@ schedule(istanbulCoverage({ // Set a custom failure message customFailureMessage: "Coverage is a little low, take a look", + // How to sort the entries in the table + entrySortMethod: "alphabetical" // || "least-coverage" || "most-coverage" || "largest-file-size" ||"smallest-file-size" || "uncovered-lines" + // Add a maximum number of entries to display numberOfEntries: 10, diff --git a/src/config.model.test.ts b/src/config.model.test.ts index e62ff2a..6c7a9cf 100644 --- a/src/config.model.test.ts +++ b/src/config.model.test.ts @@ -5,6 +5,7 @@ describe("makeCompleteConfiguration", () => { coveragePath: "./coverage/coverage-summary.json", reportFileSet: "all", reportMode: "message", + entrySortMethod: "alphabetically", numberOfEntries: 10, threshold: { statements: 100, diff --git a/src/config.model.ts b/src/config.model.ts index 9fa4f26..a8948d5 100644 --- a/src/config.model.ts +++ b/src/config.model.ts @@ -1,5 +1,12 @@ export type ReportFileSet = "created" | "modified" | "createdOrModified" | "all" export type ReportMode = "fail" | "warn" | "message" +export type SortMethod = + | "alphabetically" + | "least-coverage" + | "most-coverage" + | "largest-file-size" + | "smallest-file-size" + | "uncovered-lines" export interface CoverageThreshold { statements: number @@ -12,6 +19,7 @@ export interface Config { customSuccessMessage?: string customFailureMessage?: string numberOfEntries: number + entrySortMethod: SortMethod coveragePath: string reportFileSet: ReportFileSet threshold: CoverageThreshold @@ -28,6 +36,7 @@ export function makeCompleteConfiguration(config?: Partial): Config { coveragePath: "./coverage/coverage-summary.json", reportFileSet: "all", reportMode: "message", + entrySortMethod: "alphabetically", numberOfEntries: 10, threshold: { statements: 100, diff --git a/src/coverage.mode.test.ts b/src/coverage.mode.test.ts index 89c1aab..f552f87 100644 --- a/src/coverage.mode.test.ts +++ b/src/coverage.mode.test.ts @@ -7,6 +7,7 @@ import { makeCoverageModel, meetsThreshold, parseCoverageCollection, + sortFiles, } from "./coverage.model" describe("combineEntries()", () => { @@ -141,6 +142,54 @@ describe("makeCoverageModel", () => { }) }) -describe("parseCoverageCollection", () => { - it("") +describe("sortFiles", () => { + const coverage = { + file1: { + lines: { total: 50, covered: 25, skipped: 25, pct: 50 }, + functions: { total: 100, covered: 50, skipped: 50, pct: 50 }, + statements: { total: 100, covered: 50, skipped: 50, pct: 50 }, + branches: { total: 100, covered: 50, skipped: 50, pct: 50 }, + }, + file2: { + lines: { total: 150, covered: 60, skipped: 40, pct: 60 }, + functions: { total: 100, covered: 60, skipped: 40, pct: 60 }, + statements: { total: 100, covered: 60, skipped: 40, pct: 60 }, + branches: { total: 100, covered: 60, skipped: 40, pct: 60 }, + }, + file3: { + lines: { total: 100, covered: 700, skipped: 300, pct: 70 }, + functions: { total: 100, covered: 70, skipped: 30, pct: 70 }, + statements: { total: 100, covered: 70, skipped: 30, pct: 70 }, + branches: { total: 100, covered: 70, skipped: 30, pct: 70 }, + }, + } + it("sorts files by their line coverage percentage in descending order", () => { + const output = sortFiles(["file1", "file2", "file3"], coverage, "most-coverage") + expect(output).toEqual(["file3", "file2", "file1"]) + }) + it("sorts files by their line coverage percentage in ascending order", () => { + const output = sortFiles(["file1", "file2", "file3"], coverage, "least-coverage") + expect(output).toEqual(["file1", "file2", "file3"]) + }) + it("skips files not in input file list", () => { + const output = sortFiles(["file1", "file3"], coverage, "most-coverage") + expect(output).toEqual(["file3", "file1"]) + }) + it("sorts files by the number of lines in descending order", () => { + const output = sortFiles(["file1", "file2", "file3"], coverage, "largest-file-size") + expect(output).toEqual(["file2", "file3", "file1"]) + }) + it("sorts files the number of lines in ascending order", () => { + const output = sortFiles(["file1", "file2", "file3"], coverage, "smallest-file-size") + expect(output).toEqual(["file1", "file3", "file2"]) + }) + it("sorts files by the number of uncovered lines in descending order", () => { + const output = sortFiles(["file1", "file2", "file3"], coverage, "uncovered-lines") + expect(output).toEqual(["file3", "file2", "file1"]) + }) + + it("sorts files in alphabetical order", () => { + const output = sortFiles(["c", "b", "a"], coverage, "alphabetically") + expect(output).toEqual(["a", "b", "c"]) + }) }) diff --git a/src/coverage.model.ts b/src/coverage.model.ts index a275eca..6feb918 100644 --- a/src/coverage.model.ts +++ b/src/coverage.model.ts @@ -1,5 +1,6 @@ +import { METHODS } from "http" import * as path from "path" -import { Config, CoverageThreshold } from "./config.model" +import { Config, CoverageThreshold, SortMethod } from "./config.model" import FilesystemService from "./filesystem.service" export interface CoverageItem { @@ -79,8 +80,55 @@ export function parseCoverageCollection(coveragePath: string): CoverageCollectio } } -export function makeCoverageModel(numberOfEntries: number, files: string[], coverageCollection: CoverageCollection) { - const sortedFiles = files.sort((a, b) => a.localeCompare(b, "en-US")) +function sortFileByCoverageKey( + files: string[], + coverageCollection: CoverageCollection, + ascending: boolean, + key: string +) { + return files + .map(file => { + return { file, entry: coverageCollection[file] } + }) + .sort((a, b) => (ascending ? a.entry.lines[key] - b.entry.lines[key] : b.entry.lines[key] - a.entry.lines[key])) + .map(entry => entry.file) +} + +function sortFilesAlphabetically(files: string[]): string[] { + return files.sort((a, b) => a.localeCompare(b, "en-US")) +} + +/** + * Sorts a list of files by their total line coverage. + * @param files The files list + * @param coverageCollection The collection of file coverages. + * @param method The method to use while sorting + * @returns The sorted list of file names. + */ +export function sortFiles(files: string[], coverageCollection: CoverageCollection, method: SortMethod) { + switch (method) { + case "alphabetically": + return sortFilesAlphabetically(files) + case "least-coverage": + return sortFileByCoverageKey(files, coverageCollection, true, "pct") + case "most-coverage": + return sortFileByCoverageKey(files, coverageCollection, false, "pct") + case "largest-file-size": + return sortFileByCoverageKey(files, coverageCollection, false, "total") + case "smallest-file-size": + return sortFileByCoverageKey(files, coverageCollection, true, "total") + case "uncovered-lines": + return sortFileByCoverageKey(files, coverageCollection, false, "skipped") + } +} + +export function makeCoverageModel( + numberOfEntries: number, + files: string[], + coverageCollection: CoverageCollection, + sortMethod: SortMethod = "alphabetically" +) { + const sortedFiles = sortFiles(files, coverageCollection, sortMethod) const displayedFiles = sortedFiles.slice(0, Math.min(sortedFiles.length, numberOfEntries)) const displayedEntries = displayedFiles.map(file => coverageCollection[file]) diff --git a/src/index.ts b/src/index.ts index 4db141f..86e6b10 100644 --- a/src/index.ts +++ b/src/index.ts @@ -178,7 +178,12 @@ export function istanbulCoverage(config?: Partial): Promise { return } - const coverageModel = makeCoverageModel(combinedConfig.numberOfEntries, files, coverage) + const coverageModel = makeCoverageModel( + combinedConfig.numberOfEntries, + files, + coverage, + combinedConfig.entrySortMethod + ) sendPRComment(combinedConfig, coverageModel.total) const report = generateReport(gitRoot, gitBranch, coverageModel, combinedConfig.reportFileSet)