-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathformatters.ts
More file actions
126 lines (109 loc) · 3.12 KB
/
formatters.ts
File metadata and controls
126 lines (109 loc) · 3.12 KB
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
/**
* @fileoverview Coverage output formatters.
*/
import { indentString } from '../strings'
import type { FormatCoverageOptions } from './types'
/**
* Coverage emoji thresholds for visual feedback.
*/
const COVERAGE_EMOJI_THRESHOLDS = [
{ emoji: ' 🚀', threshold: 99 },
{ emoji: ' 🎯', threshold: 95 },
{ emoji: ' ✨', threshold: 90 },
{ emoji: ' 💚', threshold: 85 },
{ emoji: ' ✅', threshold: 80 },
{ emoji: ' 🟢', threshold: 70 },
{ emoji: ' 🟡', threshold: 60 },
{ emoji: ' 🔨', threshold: 50 },
{ emoji: ' ⚠️', threshold: 0 },
]
/**
* Get emoji for coverage percentage.
*
* @example
* ```typescript
* getCoverageEmoji(95) // ' \u{1F3AF}'
* getCoverageEmoji(50) // ' \u{1F528}'
* ```
*/
export function getCoverageEmoji(percent: number): string {
const entry = COVERAGE_EMOJI_THRESHOLDS.find(
({ threshold }) => percent >= threshold,
)
return entry?.emoji || ''
}
/**
* Format coverage data for console output.
*
* @example
* ```typescript
* const output = formatCoverage({
* code: {
* statements: { percent: '85.00' },
* branches: { percent: '80.00' },
* functions: { percent: '90.00' },
* lines: { percent: '88.00' },
* },
* })
* ```
*/
export function formatCoverage(options: FormatCoverageOptions): string {
const opts = {
__proto__: null,
format: 'default' as const,
...options,
} as Required<FormatCoverageOptions>
const { code, format, type } = opts
if (format === 'json') {
return JSON.stringify({ code, type }, null, 2)
}
const overall = calculateOverall(code, type)
if (format === 'simple') {
return overall
}
// Default format with emoji and details.
let output = ''
// Code coverage section.
output += 'Code Coverage:\n'
output += indentString(`Statements: ${code.statements.percent}%\n`, {
count: 2,
})
output += indentString(`Branches: ${code.branches.percent}%\n`, { count: 2 })
output += indentString(`Functions: ${code.functions.percent}%\n`, {
count: 2,
})
output += indentString(`Lines: ${code.lines.percent}%\n`, { count: 2 })
// Type coverage section.
if (type) {
output += '\nType Coverage:\n'
output += indentString(
`${type.percent}% (${type.covered}/${type.total})\n`,
{ count: 2 },
)
}
// Overall.
const overallValue = Number.parseFloat(overall)
const emoji = getCoverageEmoji(Number.isNaN(overallValue) ? 0 : overallValue)
output += `\nOverall: ${overall}%${emoji}\n`
return output
}
/**
* Calculate overall coverage percentage.
*/
function calculateOverall(
code: FormatCoverageOptions['code'],
type: FormatCoverageOptions['type'],
): string {
const metrics = [
Number.parseFloat(code.statements.percent),
Number.parseFloat(code.branches.percent),
Number.parseFloat(code.functions.percent),
Number.parseFloat(code.lines.percent),
].map(val => (Number.isNaN(val) ? 0 : val))
if (type) {
const typePercent = Number.parseFloat(type.percent)
metrics.push(Number.isNaN(typePercent) ? 0 : typePercent)
}
const average = metrics.reduce((sum, val) => sum + val, 0) / metrics.length
return average.toFixed(2)
}