import { describe, it, expect } from 'vitest'; import { formatCustom, formatDate, formatIso, formatSmart, type DateFormatMode, } from './date-format'; /** A fixed local reference "now" for relative-date tests. */ const NOW = new Date(2026, 5, 7, 12, 0, 0); // 2026-06-07 12:00 local const at = (y: number, mo: number, d: number, h = 9, mi = 0, s = 0) => new Date(y, mo, d, h, mi, s); describe('formatSmart', () => { it('renders the same calendar day as Today', () => { expect(formatSmart(at(2026, 5, 7, 0, 1), NOW)).toBe('Today'); expect(formatSmart(at(2026, 5, 7, 23, 59), NOW)).toBe('Today'); }); it('renders the previous calendar day as Yesterday', () => { expect(formatSmart(at(2026, 5, 6), NOW)).toBe('Yesterday'); }); it('renders under a week as Nd ago', () => { expect(formatSmart(at(2026, 5, 4), NOW)).toBe('3d ago'); expect(formatSmart(at(2026, 5, 1), NOW)).toBe('6d ago'); }); it('falls back to a full locale date at a week or older', () => { const old = at(2026, 4, 1); expect(formatSmart(old, NOW)).toBe(old.toLocaleDateString()); }); it('does not flip to Yesterday on the hour within the same day', () => { // 1 minute ago but same calendar day → still Today. expect(formatSmart(at(2026, 5, 7, 11, 59), NOW)).toBe('Today'); }); }); describe('formatIso', () => { it('returns a full ISO 8601 timestamp', () => { const d = new Date('2026-06-07T10:30:00.000Z'); expect(formatIso(d)).toBe('2026-06-07T10:30:00.000Z'); }); }); describe('formatCustom', () => { it('formats the placeholder pattern yyyy-MM-dd HH:mm', () => { expect(formatCustom(at(2026, 0, 5, 8, 4), 'yyyy-MM-dd HH:mm')).toBe('2026-01-05 08:04'); }); it('supports month names and short year', () => { expect(formatCustom(at(2026, 11, 25), 'd MMMM yy')).toBe('25 December 26'); expect(formatCustom(at(2026, 2, 9), 'MMM d, yyyy')).toBe('Mar 9, 2026'); }); it('supports 12-hour clock with meridiem', () => { expect(formatCustom(at(2026, 5, 7, 0, 5), 'h:mm a')).toBe('12:05 AM'); expect(formatCustom(at(2026, 5, 7, 13, 5), 'h:mm a')).toBe('1:05 PM'); expect(formatCustom(at(2026, 5, 7, 12, 0), 'hh:mm a')).toBe('12:00 PM'); }); it('preserves non-token literals verbatim', () => { expect(formatCustom(at(2026, 0, 1), 'yyyy/MM/dd')).toBe('2026/01/01'); }); }); describe('formatDate', () => { it('dispatches by mode', () => { const iso = at(2026, 5, 6, 9, 0).toISOString(); expect(formatDate(iso, 'smart', '', NOW)).toBe('Yesterday'); expect(formatDate(iso, 'iso')).toBe(new Date(iso).toISOString()); expect(formatDate(iso, 'custom', 'yyyy-MM-dd', NOW)).toBe('2026-06-06'); }); it('falls back to ISO when a custom pattern is blank', () => { const iso = '2026-06-07T10:30:00.000Z'; expect(formatDate(iso, 'custom', ' ')).toBe(new Date(iso).toISOString()); expect(formatDate(iso, 'custom', '')).toBe(new Date(iso).toISOString()); }); it('returns an unparseable timestamp verbatim rather than "Invalid Date"', () => { expect(formatDate('not-a-date', 'iso')).toBe('not-a-date'); expect(formatDate('not-a-date', 'smart', '', NOW)).toBe('not-a-date'); }); it('defaults unknown modes to smart', () => { const iso = at(2026, 5, 7).toISOString(); expect(formatDate(iso, 'bogus' as DateFormatMode, '', NOW)).toBe('Today'); }); });