-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathcoverage.model.ts
142 lines (125 loc) · 4.5 KB
/
coverage.model.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import { METHODS } from "http"
import * as path from "path"
import { Config, CoverageThreshold, SortMethod } from "./config.model"
import FilesystemService from "./filesystem.service"
export interface CoverageItem {
total: number
covered: number
skipped: number
pct: number
}
export interface CoverageEntry {
lines: CoverageItem
functions: CoverageItem
statements: CoverageItem
branches: CoverageItem
}
export interface CoverageCollection {
[key: string]: CoverageEntry
}
export interface CoverageModel {
total: CoverageEntry
elided: CoverageEntry
elidedCount: number
displayed: CoverageCollection
}
function combineItems(first: CoverageItem, second: CoverageItem): CoverageItem {
const percentage =
second.covered + first.covered > 0 ? 100 * (first.covered + second.covered) / (second.total + first.total) : 100
return {
total: first.total + second.total,
covered: first.covered + second.covered,
skipped: first.skipped + second.skipped,
pct: percentage,
}
}
function reduceEntries(entries: CoverageEntry[]): CoverageEntry {
return entries.reduce((cumulativeEntry, entry) => combineEntries(cumulativeEntry, entry), createEmptyCoverageEntry())
}
function createEmptyCoverageEntry(): CoverageEntry {
return {
lines: { total: 0, covered: 0, skipped: 0, pct: 0 },
statements: { total: 0, covered: 0, skipped: 0, pct: 0 },
functions: { total: 0, covered: 0, skipped: 0, pct: 0 },
branches: { total: 0, covered: 0, skipped: 0, pct: 0 },
}
}
export function combineEntries(first: CoverageEntry, second: CoverageEntry): CoverageEntry {
return {
lines: combineItems(first.lines, second.lines),
statements: combineItems(first.statements, second.statements),
branches: combineItems(first.branches, second.branches),
functions: combineItems(first.functions, second.functions),
}
}
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])
const ellidedEntries = sortedFiles.slice(numberOfEntries).map(file => coverageCollection[file])
const ellidedSummary = reduceEntries(ellidedEntries)
const totalSummary = reduceEntries([...displayedEntries, ellidedSummary])
const coverageEntries = displayedFiles.reduce((current, file) => {
const copy = { ...current }
copy[file] = coverageCollection[file]
return copy
}, {})
return {
displayed: coverageEntries,
total: totalSummary,
elided: ellidedSummary,
elidedCount: ellidedEntries.length,
}
}
export function meetsThreshold(entry: CoverageEntry, threshold: CoverageThreshold) {
return (
entry.lines.pct >= threshold.lines &&
entry.functions.pct >= threshold.functions &&
entry.branches.pct >= threshold.branches &&
entry.statements.pct >= threshold.statements
)
}