|
4 | 4 | getProviderTitle, |
5 | 5 | getReadableLanguageName, |
6 | 6 | toGigabytes, |
| 7 | + formatBytes, |
7 | 8 | formatMegaBytes, |
8 | 9 | formatDuration, |
9 | 10 | getModelDisplayName, |
@@ -149,6 +150,80 @@ describe('formatMegaBytes', () => { |
149 | 150 | }) |
150 | 151 | }) |
151 | 152 |
|
| 153 | +describe('formatBytes', () => { |
| 154 | + it('returns fallback for undefined or non-finite input', () => { |
| 155 | + expect(formatBytes(undefined)).toBe('') |
| 156 | + expect(formatBytes(undefined, { fallback: 'N/A' })).toBe('N/A') |
| 157 | + expect(formatBytes(Number.NaN, { fallback: '-' })).toBe('-') |
| 158 | + expect(formatBytes(Number.POSITIVE_INFINITY, { fallback: 'INF' })).toBe( |
| 159 | + 'INF' |
| 160 | + ) |
| 161 | + }) |
| 162 | + |
| 163 | + it('formats bytes with default options', () => { |
| 164 | + expect(formatBytes(0)).toBe('0.0 B') |
| 165 | + expect(formatBytes(512)).toBe('512.0 B') |
| 166 | + expect(formatBytes(1536)).toBe('1.5 KB') |
| 167 | + expect(formatBytes(1024 ** 2 * 2.25)).toBe('2.3 MB') |
| 168 | + expect(formatBytes(1024 ** 3 * 3)).toBe('3.0 GB') |
| 169 | + }) |
| 170 | + |
| 171 | + it('uses 1024 boundaries for unit transitions', () => { |
| 172 | + expect(formatBytes(1023)).toBe('1023.0 B') |
| 173 | + expect(formatBytes(1024)).toBe('1.0 KB') |
| 174 | + expect(formatBytes(1024 ** 2)).toBe('1.0 MB') |
| 175 | + expect(formatBytes(1024 ** 3)).toBe('1.0 GB') |
| 176 | + }) |
| 177 | + |
| 178 | + it('supports numeric decimals option', () => { |
| 179 | + expect(formatBytes(1536, { decimals: 2 })).toBe('1.50 KB') |
| 180 | + expect(formatBytes(1536, { decimals: 0 })).toBe('2 KB') |
| 181 | + expect(formatBytes(1536, { decimals: 3 })).toBe('1.500 KB') |
| 182 | + }) |
| 183 | + |
| 184 | + it('clamps and truncates decimal precision into toFixed range', () => { |
| 185 | + expect(formatBytes(1536, { decimals: -5 })).toBe('2 KB') |
| 186 | + expect(formatBytes(1536, { decimals: 2.9 })).toBe('1.50 KB') |
| 187 | + expect(formatBytes(1536, { decimals: 999 })).toBe( |
| 188 | + '1.50000000000000000000 KB' |
| 189 | + ) |
| 190 | + }) |
| 191 | + |
| 192 | + it('supports function-based decimals per unit', () => { |
| 193 | + const decimals = vi.fn((value: number, unit: 'B' | 'KB' | 'MB' | 'GB') => { |
| 194 | + if (unit === 'B') return 0 |
| 195 | + if (unit === 'KB') return value < 2 ? 3 : 1 |
| 196 | + if (unit === 'MB') return 2 |
| 197 | + return 4 |
| 198 | + }) |
| 199 | + |
| 200 | + expect(formatBytes(900, { decimals })).toBe('900 B') |
| 201 | + expect(formatBytes(1536, { decimals })).toBe('1.500 KB') |
| 202 | + expect(formatBytes(4096, { decimals })).toBe('4.0 KB') |
| 203 | + expect(formatBytes(1024 ** 2 * 1.25, { decimals })).toBe('1.25 MB') |
| 204 | + expect(formatBytes(1024 ** 3 * 1.5, { decimals })).toBe('1.5000 GB') |
| 205 | + expect(decimals).toHaveBeenCalledTimes(5) |
| 206 | + }) |
| 207 | + |
| 208 | + it('supports separator and hideUnit options', () => { |
| 209 | + expect(formatBytes(1536, { separator: '' })).toBe('1.5KB') |
| 210 | + expect(formatBytes(1536, { separator: ' - ' })).toBe('1.5 - KB') |
| 211 | + expect(formatBytes(1536, { hideUnit: true })).toBe('1.5') |
| 212 | + expect(formatBytes(1536, { hideUnit: true, separator: ' / ' })).toBe('1.5') |
| 213 | + }) |
| 214 | + |
| 215 | + it('honors minUnit floor', () => { |
| 216 | + expect(formatBytes(1, { minUnit: 'KB', decimals: 4 })).toBe('0.0010 KB') |
| 217 | + expect(formatBytes(1024, { minUnit: 'MB', decimals: 4 })).toBe('0.0010 MB') |
| 218 | + expect(formatBytes(1024 ** 2, { minUnit: 'GB', decimals: 4 })).toBe( |
| 219 | + '0.0010 GB' |
| 220 | + ) |
| 221 | + expect(formatBytes(1024 ** 2 * 2, { minUnit: 'KB', decimals: 2 })).toBe( |
| 222 | + '2.00 MB' |
| 223 | + ) |
| 224 | + }) |
| 225 | +}) |
| 226 | + |
152 | 227 | describe('formatDuration', () => { |
153 | 228 | it('formats milliseconds when duration is less than 1 second', () => { |
154 | 229 | const start = Date.now() |
|
0 commit comments