import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { cn, formatDate, formatFullDate, getInitials, getRelativeTime, getDaysUntil } from './utils'; describe('cn', () => { it('merges class names', () => { expect(cn('foo', 'bar')).toBe('foo bar'); }); it('handles conditional classes', () => { expect(cn('base', false && 'hidden', 'visible')).toBe('base visible'); }); it('merges tailwind conflicts', () => { expect(cn('px-4', 'px-2')).toBe('px-2'); }); }); describe('formatDate', () => { beforeEach(() => { vi.useFakeTimers(); vi.setSystemTime(new Date('2026-01-28T12:00:00Z')); }); afterEach(() => { vi.useRealTimers(); }); it('returns empty string for undefined', () => { expect(formatDate(undefined)).toBe(''); }); it('returns "Today" for today\'s date', () => { expect(formatDate('2026-01-28')).toBe('Today'); }); it('returns "Tomorrow" for tomorrow\'s date', () => { expect(formatDate('2026-01-29')).toBe('Tomorrow'); }); it('returns "Yesterday" for yesterday\'s date', () => { expect(formatDate('2026-01-27')).toBe('Yesterday'); }); it('returns short format for same year', () => { const result = formatDate('2026-03-15'); expect(result).toBe('Mar 15'); }); it('returns full format for different year', () => { const result = formatDate('2025-03-15'); expect(result).toBe('Mar 15, 2025'); }); }); describe('formatFullDate', () => { it('returns empty string for undefined', () => { expect(formatFullDate(undefined)).toBe(''); }); it('returns full formatted date', () => { const result = formatFullDate('2026-01-28'); expect(result).toContain('January'); expect(result).toContain('28'); expect(result).toContain('2026'); }); }); describe('getInitials', () => { it('returns initials from first and last name', () => { expect(getInitials('John', 'Doe')).toBe('JD'); }); it('handles empty strings', () => { expect(getInitials('', '')).toBe(''); }); it('handles single name', () => { expect(getInitials('Alice', '')).toBe('A'); }); }); describe('getRelativeTime', () => { beforeEach(() => { vi.useFakeTimers(); vi.setSystemTime(new Date('2026-01-28T12:00:00Z')); }); afterEach(() => { vi.useRealTimers(); }); it('returns "Never" for undefined', () => { expect(getRelativeTime(undefined)).toBe('Never'); }); it('returns "Today" for today', () => { expect(getRelativeTime('2026-01-28T10:00:00Z')).toBe('Today'); }); it('returns "Yesterday" for yesterday', () => { expect(getRelativeTime('2026-01-27T10:00:00Z')).toBe('Yesterday'); }); it('returns days ago for recent dates', () => { expect(getRelativeTime('2026-01-25T10:00:00Z')).toBe('3 days ago'); }); it('returns weeks ago', () => { expect(getRelativeTime('2026-01-14T10:00:00Z')).toBe('2 weeks ago'); }); it('returns months ago', () => { expect(getRelativeTime('2025-11-28T10:00:00Z')).toBe('2 months ago'); }); }); describe('getDaysUntil', () => { beforeEach(() => { vi.useFakeTimers(); vi.setSystemTime(new Date('2026-01-28T12:00:00Z')); }); afterEach(() => { vi.useRealTimers(); }); it('returns 0 for today', () => { expect(getDaysUntil('2026-01-28')).toBe(0); }); it('returns positive for future dates', () => { expect(getDaysUntil('2026-02-01')).toBe(4); }); it('projects past dates to next occurrence', () => { // Jan 15 already passed, should project to next year const days = getDaysUntil('2025-01-15'); expect(days).toBeGreaterThan(300); // ~352 days to Jan 15 2027 }); });