import { describe, it, expect } from 'vitest' import { formatCurrency, formatPercent, formatMinutes, formatSimulationTimestamp } from '@/lib/format' // ─── formatCurrency ─────────────────────────────────────────────────────────── describe('formatCurrency', () => { // USD it('USD: formatea 0 correctamente', () => { const result = formatCurrency(0, 'USD') expect(result).toMatch(/0/) expect(result).toMatch(/\$|USD/) }) it('USD: formatea 1 con 2 decimales', () => { const result = formatCurrency(1, 'USD') expect(result).toMatch(/1[.,]00/) }) it('USD: formatea 1000 con separador de miles', () => { const result = formatCurrency(1000, 'USD') // Debe tener separador (coma o punto) antes de los tres ceros expect(result).toMatch(/1[.,]000/) }) it('USD: formatea 1234567 con separadores', () => { const result = formatCurrency(1234567, 'USD') expect(result).toMatch(/1/) expect(result).toMatch(/234/) expect(result).toMatch(/567/) }) it('USD: formatea decimales correctamente', () => { const result = formatCurrency(1234.56, 'USD') expect(result).toMatch(/1[.,]234/) expect(result).toMatch(/56/) }) // PYG — el guaraní no tiene decimales centavos en la práctica it('PYG: devuelve una cadena con el valor', () => { const result = formatCurrency(1000000, 'PYG') expect(result).toBeTruthy() expect(typeof result).toBe('string') expect(result.length).toBeGreaterThan(0) }) it('PYG: 0 formatea sin crash', () => { expect(() => formatCurrency(0, 'PYG')).not.toThrow() }) it('PYG: número grande formatea sin crash', () => { expect(() => formatCurrency(1_234_567_890, 'PYG')).not.toThrow() const result = formatCurrency(1_234_567_890, 'PYG') expect(result).toMatch(/1/) }) // BRL it('BRL: formatea con símbolo correcto', () => { const result = formatCurrency(1000, 'BRL') expect(result).toBeTruthy() // El símbolo R$ puede aparecer o la abreviatura BRL expect(result).toMatch(/1/) }) // EUR it('EUR: formatea 1234.56 sin crash', () => { expect(() => formatCurrency(1234.56, 'EUR')).not.toThrow() const result = formatCurrency(1234.56, 'EUR') expect(result).toMatch(/1/) }) // Negativos — el reporte no debe tener valores negativos, pero la función no debe crashear it('No crashea con valores negativos', () => { expect(() => formatCurrency(-100, 'USD')).not.toThrow() const result = formatCurrency(-100, 'USD') expect(result).toMatch(/-|100/) }) // Múltiples monedas producen outputs distintos it('USD y PYG producen outputs distintos para el mismo número', () => { const usd = formatCurrency(1000, 'USD') const pyg = formatCurrency(1000, 'PYG') expect(usd).not.toBe(pyg) }) }) // ─── formatMinutes ──────────────────────────────────────────────────────────── describe('formatMinutes', () => { it('5 min → "5 min"', () => { expect(formatMinutes(5)).toBe('5 min') }) it('30 min → "30 min"', () => { expect(formatMinutes(30)).toBe('30 min') }) it('59 min → "59 min" (aún no llega a 1h)', () => { expect(formatMinutes(59)).toBe('59 min') }) it('60 min → "1h" (exactamente 1 hora)', () => { expect(formatMinutes(60)).toBe('1h') }) it('90 min → "1h 30min"', () => { expect(formatMinutes(90)).toBe('1h 30min') }) it('125 min → "2h 5min"', () => { expect(formatMinutes(125)).toBe('2h 5min') }) it('120 min → "2h" (horas exactas, sin fracción)', () => { expect(formatMinutes(120)).toBe('2h') }) it('1440 min → "24h" (un día completo)', () => { expect(formatMinutes(1440)).toBe('24h') }) it('0 min → "0 min"', () => { expect(formatMinutes(0)).toBe('0 min') }) it('No crashea con valores decimales', () => { expect(() => formatMinutes(90.7)).not.toThrow() }) }) // ─── formatPercent ──────────────────────────────────────────────────────────── // Nota: el locale es-PY usa coma como separador decimal → "23,4%" no "23.4%". // Los tests verifican estructura (contiene el valor y el símbolo) sin asumir separador. describe('formatPercent', () => { it('23.4 → contiene "23" y "4" y "%" con 1 decimal', () => { const result = formatPercent(23.4) expect(result).toContain('23') expect(result).toContain('4') expect(result).toContain('%') }) it('100 → contiene "100" y "%" con 1 decimal', () => { const result = formatPercent(100) expect(result).toContain('100') expect(result).toContain('%') }) it('0 → contiene "0" y "%" sin crash', () => { const result = formatPercent(0) expect(result).toContain('0') expect(result).toContain('%') }) it('el locale es-PY usa coma decimal: 23.4 → "23,4%"', () => { // Documenta el comportamiento real del locale configurado expect(formatPercent(23.4)).toBe('23,4%') }) it('100% con 0 decimales → "100%"', () => { expect(formatPercent(100, 0)).toBe('100%') }) it('20 con 0 decimales → "20%"', () => { expect(formatPercent(20, 0)).toBe('20%') }) }) // ─── formatSimulationTimestamp ──────────────────────────────────────────────── describe('formatSimulationTimestamp', () => { // Timestamp fijo para reproducibilidad: 13 de mayo de 2026, 09:45 UTC-4 (PY hora de verano) // Usamos una fecha conocida y verificamos la estructura del output sin depender de TZ del CI. const KNOWN_TS = new Date('2026-05-13T13:45:00Z').getTime() // UTC it('devuelve objeto con date y time strings no vacíos', () => { const { date, time } = formatSimulationTimestamp(KNOWN_TS) expect(date.length).toBeGreaterThan(0) expect(time.length).toBeGreaterThan(0) }) it('date contiene el año 2026', () => { const { date } = formatSimulationTimestamp(KNOWN_TS) expect(date).toContain('2026') }) it('date contiene el número 13 (día)', () => { const { date } = formatSimulationTimestamp(KNOWN_TS) expect(date).toContain('13') }) it('date contiene "mayo" (nombre del mes en es-PY)', () => { const { date } = formatSimulationTimestamp(KNOWN_TS) expect(date).toMatch(/mayo/i) }) it('time tiene formato HH:mm (dos pares de dígitos separados por ":")', () => { const { time } = formatSimulationTimestamp(KNOWN_TS) expect(time).toMatch(/\d{1,2}:\d{2}/) }) it('no crashea con timestamp 0 (epoch)', () => { expect(() => formatSimulationTimestamp(0)).not.toThrow() const { date, time } = formatSimulationTimestamp(0) expect(date.length).toBeGreaterThan(0) expect(time.length).toBeGreaterThan(0) }) it('timestamps distintos producen salidas distintas', () => { const ts1 = new Date('2026-01-01T10:00:00Z').getTime() const ts2 = new Date('2026-06-15T18:30:00Z').getTime() const r1 = formatSimulationTimestamp(ts1) const r2 = formatSimulationTimestamp(ts2) expect(r1.date).not.toBe(r2.date) }) })