add unit test suite: 80 tests across utils, api, auth, clients, events, emails
- Vitest + React Testing Library + jsdom setup - utils.test.ts: cn, formatDate, formatFullDate, getInitials, getRelativeTime, getDaysUntil - api.test.ts: token management, auth, CRUD for clients/events/emails, admin, error handling - auth.test.ts: login, logout, checkSession, setUser - clients.test.ts: fetch, create, update, delete, markContacted, filters - events.test.ts: fetch, create, update, delete, syncAll - emails.test.ts: fetch, generate, update, send, delete
This commit is contained in:
140
src/lib/utils.test.ts
Normal file
140
src/lib/utils.test.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
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
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user