utils.test.js
1 import { describe, it, expect, vi, beforeEach } from 'vitest'; 2 import { formatDuration, formatDate, itunesFetch } from './utils.js'; 3 describe('formatDuration', () => { 4 it('formats typical duration in ms', () => { 5 expect(formatDuration(3661000)).toBe('61:01'); 6 }); 7 it('pads single-digit seconds', () => { 8 expect(formatDuration(65000)).toBe('1:05'); 9 }); 10 it('formats exact minutes', () => { 11 expect(formatDuration(3600000)).toBe('60:00'); 12 }); 13 it('rounds fractional milliseconds', () => { 14 expect(formatDuration(3600500)).toBe('60:01'); 15 }); 16 it('returns dash for zero', () => { 17 expect(formatDuration(0)).toBe('-'); 18 }); 19 it('returns dash for NaN', () => { 20 expect(formatDuration(NaN)).toBe('-'); 21 }); 22 }); 23 describe('formatDate', () => { 24 it('extracts YYYY-MM-DD from ISO string', () => { 25 expect(formatDate('2026-03-19T12:00:00.000Z')).toBe('2026-03-19'); 26 }); 27 it('handles date-only string', () => { 28 expect(formatDate('2025-01-01')).toBe('2025-01-01'); 29 }); 30 it('returns dash for empty string', () => { 31 expect(formatDate('')).toBe('-'); 32 }); 33 it('returns dash for undefined', () => { 34 expect(formatDate(undefined)).toBe('-'); 35 }); 36 }); 37 describe('itunesFetch', () => { 38 beforeEach(() => { 39 vi.restoreAllMocks(); 40 }); 41 it('returns parsed JSON on success', async () => { 42 const mockData = { resultCount: 1, results: [{ collectionId: 123 }] }; 43 vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ 44 ok: true, 45 json: () => Promise.resolve(mockData), 46 })); 47 const result = await itunesFetch('/search?term=test&media=podcast&limit=1'); 48 expect(result).toEqual(mockData); 49 }); 50 it('throws CliError on HTTP error', async () => { 51 vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ 52 ok: false, 53 status: 403, 54 })); 55 await expect(itunesFetch('/search?term=test')).rejects.toThrow('iTunes API HTTP 403'); 56 }); 57 });