|
5 | 5 | formatDate, |
6 | 6 | formatRatio, |
7 | 7 | formatPercent, |
| 8 | + floorTo, |
| 9 | + formatProgress, |
| 10 | + formatAvailability, |
8 | 11 | } from '@/utils/format'; |
9 | 12 |
|
10 | 13 | describe('formatSize', () => { |
@@ -240,3 +243,75 @@ describe('formatPercent', () => { |
240 | 243 | expect(formatPercent(-0.1)).toBe('-10.0%'); |
241 | 244 | }); |
242 | 245 | }); |
| 246 | + |
| 247 | +describe('floorTo', () => { |
| 248 | + it('floors without rounding up', () => { |
| 249 | + expect(floorTo(99.99, 1)).toBe(99.9); |
| 250 | + expect(floorTo(99.95, 0)).toBe(99); |
| 251 | + }); |
| 252 | + |
| 253 | + it('does not truncate exact decimals that binary floats under-represent', () => { |
| 254 | + // 0.29 * 100 === 28.999999999999996 — naive Math.floor gives 28 |
| 255 | + expect(floorTo(0.29 * 100, 0)).toBe(29); |
| 256 | + expect(floorTo(0.58 * 100, 0)).toBe(58); |
| 257 | + expect(floorTo(1.005, 3)).toBe(1.005); |
| 258 | + }); |
| 259 | + |
| 260 | + it('never pushes a genuinely-below-boundary value across it', () => { |
| 261 | + expect(floorTo(99.9999, 0)).toBe(99); |
| 262 | + expect(floorTo(0.9999, 3)).toBe(0.999); |
| 263 | + }); |
| 264 | +}); |
| 265 | + |
| 266 | +describe('formatProgress', () => { |
| 267 | + it('returns zero for null, undefined, and NaN', () => { |
| 268 | + expect(formatProgress(null)).toBe('0.0%'); |
| 269 | + expect(formatProgress(undefined)).toBe('0.0%'); |
| 270 | + expect(formatProgress(NaN)).toBe('0.0%'); |
| 271 | + expect(formatProgress(null, 0)).toBe('0%'); |
| 272 | + }); |
| 273 | + |
| 274 | + it('truncates instead of rounding up near completion', () => { |
| 275 | + expect(formatProgress(0.9995)).toBe('99.9%'); |
| 276 | + expect(formatProgress(0.999999)).toBe('99.9%'); |
| 277 | + expect(formatProgress(0.995, 0)).toBe('99%'); |
| 278 | + }); |
| 279 | + |
| 280 | + it('shows 100% only at exactly complete', () => { |
| 281 | + expect(formatProgress(1)).toBe('100.0%'); |
| 282 | + expect(formatProgress(1, 0)).toBe('100%'); |
| 283 | + }); |
| 284 | + |
| 285 | + it('does not understate exact percentages (float one-ULP guard)', () => { |
| 286 | + expect(formatProgress(0.29, 0)).toBe('29%'); |
| 287 | + expect(formatProgress(0.58, 0)).toBe('58%'); |
| 288 | + expect(formatProgress(0.723)).toBe('72.3%'); |
| 289 | + }); |
| 290 | + |
| 291 | + it('truncates mid-range extra precision', () => { |
| 292 | + expect(formatProgress(0.8615)).toBe('86.1%'); |
| 293 | + }); |
| 294 | +}); |
| 295 | + |
| 296 | +describe('formatAvailability', () => { |
| 297 | + it('returns "0.000" for null, undefined, and NaN', () => { |
| 298 | + expect(formatAvailability(null)).toBe('0.000'); |
| 299 | + expect(formatAvailability(undefined)).toBe('0.000'); |
| 300 | + expect(formatAvailability(NaN)).toBe('0.000'); |
| 301 | + }); |
| 302 | + |
| 303 | + it('truncates instead of rounding up to 1.000', () => { |
| 304 | + expect(formatAvailability(0.9999)).toBe('0.999'); |
| 305 | + expect(formatAvailability(0.99999)).toBe('0.999'); |
| 306 | + }); |
| 307 | + |
| 308 | + it('shows 1.000 only at exactly 1', () => { |
| 309 | + expect(formatAvailability(1)).toBe('1.000'); |
| 310 | + }); |
| 311 | + |
| 312 | + it('handles ratios above 1 and float one-ULP values', () => { |
| 313 | + expect(formatAvailability(2.5)).toBe('2.500'); |
| 314 | + // 1.005 * 1000 === 1004.9999999999999 — naive Math.floor gives 1.004 |
| 315 | + expect(formatAvailability(1.005)).toBe('1.005'); |
| 316 | + }); |
| 317 | +}); |
0 commit comments