import { describe, it, expect } from 'vitest'; import { createCelebrationEngine } from './client.js'; import type { Celebration } from './types.js'; describe('createCelebrationEngine', () => { it('should return default celebration for known triggers', () => { const engine = createCelebrationEngine(); const c = engine.getCelebration('level_up'); expect(c.id).toBe('level_up'); expect(c.title).toContain('Level Up'); expect(c.confetti).toBe(true); expect(c.sound).toBe('level_up'); }); it('should return fallback for unknown triggers', () => { const engine = createCelebrationEngine(); const c = engine.getCelebration('custom_unknown'); expect(c.id).toBe('custom_unknown'); expect(c.title).toBe('Nice!'); }); it('should use custom triggers when provided', () => { const custom: Celebration = { id: 'fasting_complete', title: 'Fast Complete!', body: 'You did it!', emoji: '🍃', hapticType: 'heavy', confetti: true, sound: 'success', }; const engine = createCelebrationEngine({ customTriggers: { fasting_complete: custom } }); const c = engine.getCelebration('fasting_complete'); expect(c).toEqual(custom); }); it('should prefer custom triggers over defaults', () => { const custom: Celebration = { id: 'level_up', title: 'Custom Level Up!', body: 'Custom body', emoji: '🎮', hapticType: 'light', confetti: false, sound: 'none', }; const engine = createCelebrationEngine({ customTriggers: { level_up: custom } }); const c = engine.getCelebration('level_up'); expect(c.title).toBe('Custom Level Up!'); }); it('should return timed celebrations based on progress', () => { const engine = createCelebrationEngine(); const shown = new Set(); const at25 = engine.getTimedCelebrations(250, 1000, shown); expect(at25).toHaveLength(1); expect(at25[0].id).toBe('timed_25'); shown.add('timed_25'); const at50 = engine.getTimedCelebrations(500, 1000, shown); expect(at50).toHaveLength(1); expect(at50[0].id).toBe('timed_50'); }); it('should not repeat shown timed celebrations', () => { const engine = createCelebrationEngine(); const shown = new Set(['timed_25', 'timed_50']); const results = engine.getTimedCelebrations(500, 1000, shown); expect(results).toHaveLength(0); }); it('should return empty for zero target', () => { const engine = createCelebrationEngine(); expect(engine.getTimedCelebrations(100, 0, new Set())).toHaveLength(0); }); it('should detect personal best', () => { const engine = createCelebrationEngine(); expect(engine.isPersonalBest(10, 5)).toBe(true); expect(engine.isPersonalBest(5, 10)).toBe(false); expect(engine.isPersonalBest(5, 5)).toBe(false); expect(engine.isPersonalBest(1, 0)).toBe(true); }); it('should return positive messages', () => { const engine = createCelebrationEngine(); const msg0 = engine.getPositiveMessage(0); const msg100 = engine.getPositiveMessage(100); expect(msg0.length).toBeGreaterThan(0); expect(msg100.length).toBeGreaterThan(0); expect(msg0).not.toBe(msg100); }); it('should return positive incomplete messages', () => { const engine = createCelebrationEngine(); const msg = engine.getPositiveIncompleteMessage(30); expect(msg.length).toBeGreaterThan(0); }); it('should clamp progress percent', () => { const engine = createCelebrationEngine(); const msgNeg = engine.getPositiveMessage(-10); const msg0 = engine.getPositiveMessage(0); expect(msgNeg).toBe(msg0); const msgOver = engine.getPositiveMessage(200); const msg100 = engine.getPositiveMessage(100); expect(msgOver).toBe(msg100); }); });