codeburn/app/renderer/lib/format.test.ts
Resham Joshi d81a86dd02
Desktop: measured polish pass, new frame, card system, motion, onboarding (#1459)
Measured design audit fixes, one type ladder and header, zero clipping at 900 to 1680, light-theme contrast, Lucide icons, shared Escape, plurals and provider names, motion tokens with zero refresh shift, black frame with rounded content card, grouped monochrome sidebar with collapse, two-layer card system on every screen, Overview gauge, sparkline cards and chart grid, wallpaper onboarding, Windows fixes. Every number diffed identical to main on a fixed range; 1,032 app tests passing.
2026-09-16 19:48:33 +02:00

83 lines
3.2 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { afterEach, describe, expect, it } from 'vitest'
import { formatCompact, formatConverted, formatCount, formatDayLong, formatDayShort, formatDuration, formatUsd, setActiveCurrency } from './format'
describe('currency-aware formatting', () => {
afterEach(() => setActiveCurrency({ code: 'USD', symbol: '$', rate: 1 }))
it('formats raw USD with the default USD currency', () => {
expect(formatUsd(12.34)).toBe('$12.34')
expect(formatUsd(1_234.5)).toBe('$1,234.50')
})
it('applies the active FX rate and symbol to raw-USD values exactly once', () => {
setActiveCurrency({ code: 'EUR', symbol: '€', rate: 0.9 })
// 100 USD × 0.9 = 90.00 EUR
expect(formatUsd(100)).toBe('€90.00')
expect(formatUsd(1_000)).toBe('€900.00')
})
it('formatConverted swaps the symbol without re-applying the rate (CLI-converted values)', () => {
setActiveCurrency({ code: 'EUR', symbol: '€', rate: 0.9 })
// Already-EUR input renders as-is, never multiplied by the rate again.
expect(formatConverted(90)).toBe('€90.00')
expect(formatConverted(20)).toBe('€20.00')
})
})
describe('formatCompact', () => {
it('formats zero, plain counts, thousands, and millions compactly', () => {
expect(formatCompact(0)).toBe('0')
expect(formatCompact(842)).toBe('842')
expect(formatCompact(1_842)).toBe('1.8K')
expect(formatCompact(184_000)).toBe('184K')
expect(formatCompact(1_200_000)).toBe('1.2M')
})
it('trims trailing decimals and rejects non-finite values', () => {
expect(formatCompact(2_000_000)).toBe('2M')
expect(formatCompact(Number.NaN)).toBe('—')
})
})
describe('date and duration formatters', () => {
it('formats short and long calendar dates and handles invalid input', () => {
const date = '2026-07-10T12:00:00'
expect(formatDayShort(date)).toBe('Jul 10')
expect(formatDayLong(date)).toBe('Jul 10, 2026')
expect(formatDayShort('not-a-date')).toBe('—')
expect(formatDayLong('not-a-date')).toBe('—')
})
it('formats seconds, minutes, hours, and invalid durations', () => {
expect(formatDuration(29_000)).toBe('29s')
expect(formatDuration(30_000)).toBe('30s')
expect(formatDuration(47 * 60_000)).toBe('47m')
expect(formatDuration(134 * 60_000)).toBe('2h 14m')
expect(formatDuration(286 * 3_600_000 + 39 * 60_000)).toBe('12 days')
expect(formatDuration(0)).toBe('—')
})
})
describe('formatCount', () => {
it('uses the singular noun for exactly one', () => {
expect(formatCount(1, 'session')).toBe('1 session')
expect(formatCount(1, 'finding')).toBe('1 finding')
expect(formatCount(1, 'distinct session')).toBe('1 distinct session')
})
it('uses the plural noun for zero and for more than one', () => {
expect(formatCount(0, 'session')).toBe('0 sessions')
expect(formatCount(2, 'session')).toBe('2 sessions')
})
it('groups thousands with the same separator everywhere', () => {
expect(formatCount(5084, 'session')).toBe('5,084 sessions')
expect(formatCount(6205, 'session')).toBe('6,205 sessions')
})
it('takes an irregular plural when the noun needs one', () => {
expect(formatCount(1, 'entry', 'entries')).toBe('1 entry')
expect(formatCount(3, 'entry', 'entries')).toBe('3 entries')
})
})