import { describe, it, expect } from 'vitest'; import { getSuggestedPrepTime, getTotalPrepMinutes, getPrepStartTime, formatPrepWarning, shouldShowPrepWarning, getPrepKeywords, } from './prep-time'; describe('getSuggestedPrepTime', () => { it('returns keyword-based config for "meeting"', () => { const config = getSuggestedPrepTime('Team Meeting'); expect(config.prepMinutes).toBe(10); expect(config.message).toContain('agenda'); }); it('returns keyword-based config for "flight"', () => { const config = getSuggestedPrepTime('Flight to NYC'); expect(config.prepMinutes).toBe(60); expect(config.travelMinutes).toBe(60); }); it('returns keyword-based config for "dentist"', () => { const config = getSuggestedPrepTime('Dentist appointment'); expect(config.prepMinutes).toBe(15); expect(config.travelMinutes).toBe(20); }); it('returns keyword-based config for "interview"', () => { const config = getSuggestedPrepTime('Job Interview'); expect(config.prepMinutes).toBe(30); }); it('returns keyword-based config for "gym"', () => { const config = getSuggestedPrepTime('Go to gym'); expect(config.prepMinutes).toBe(10); expect(config.travelMinutes).toBe(10); }); it('returns keyword-based config for "exam"', () => { const config = getSuggestedPrepTime('Math Exam'); expect(config.prepMinutes).toBe(30); }); it('falls back to category default when no keyword match', () => { const config = getSuggestedPrepTime('Random task', 'cooking'); expect(config.prepMinutes).toBe(15); expect(config.message).toContain('ingredients'); }); it('returns generic default when no keyword or category match', () => { const config = getSuggestedPrepTime('Something'); expect(config.prepMinutes).toBe(5); expect(config.travelMinutes).toBe(0); }); it('keyword takes priority over category', () => { const config = getSuggestedPrepTime('Flight to LA', 'personal'); expect(config.prepMinutes).toBe(60); // keyword "flight", not category "personal" (5) }); it('handles case-insensitive matching', () => { const config = getSuggestedPrepTime('PRESENTATION at work'); expect(config.prepMinutes).toBe(20); }); }); describe('getTotalPrepMinutes', () => { it('sums prep and travel', () => { expect(getTotalPrepMinutes({ prepMinutes: 10, travelMinutes: 20, message: '' })).toBe(30); }); it('handles zero travel', () => { expect(getTotalPrepMinutes({ prepMinutes: 15, travelMinutes: 0, message: '' })).toBe(15); }); }); describe('getPrepStartTime', () => { it('calculates correct start time', () => { const target = 1000 * 60 * 60; // 1 hour in ms const config = { prepMinutes: 10, travelMinutes: 5, message: '' }; const start = getPrepStartTime(target, config); expect(start).toBe(target - 15 * 60_000); }); }); describe('formatPrepWarning', () => { const config = { prepMinutes: 10, travelMinutes: 5, message: 'Review your agenda' }; it('returns "Time\'s up" when no time left', () => { expect(formatPrepWarning(config, 0)).toContain("Time's up"); }); it('returns "Start now" when within total prep window', () => { expect(formatPrepWarning(config, 10)).toContain('Start now'); }); it('returns "Prepare soon" when close to prep start', () => { expect(formatPrepWarning(config, 18)).toContain('Prepare soon'); }); it('returns prep start countdown when far away', () => { expect(formatPrepWarning(config, 60)).toContain('prep starts in 45m'); }); }); describe('shouldShowPrepWarning', () => { const config = { prepMinutes: 10, travelMinutes: 5, message: '' }; const target = Date.now() + 60 * 60_000; // 1 hour from now it('returns false when too early', () => { const earlyNow = target - 90 * 60_000; // 90 min before expect(shouldShowPrepWarning(target, config, earlyNow)).toBe(false); }); it('returns true within prep window', () => { const inPrepWindow = target - 10 * 60_000; // 10 min before (within 15m window) expect(shouldShowPrepWarning(target, config, inPrepWindow)).toBe(true); }); it('returns false after target time', () => { const afterTarget = target + 60_000; expect(shouldShowPrepWarning(target, config, afterTarget)).toBe(false); }); it('returns true exactly at prep start', () => { const prepStart = target - 15 * 60_000; expect(shouldShowPrepWarning(target, config, prepStart)).toBe(true); }); }); describe('getPrepKeywords', () => { it('returns an array of keyword strings', () => { const keywords = getPrepKeywords(); expect(keywords.length).toBeGreaterThan(10); expect(keywords).toContain('meeting'); expect(keywords).toContain('flight'); expect(keywords).toContain('dentist'); }); });