learning_ai_common_plat/packages/celebrations/src/client.test.ts
saravanakumardb1 be03efa111 feat(shared-packages): add 9 @bytelyst/* client packages with 100% API coverage
Packages added:
- @bytelyst/referral-client — referral API client + share helpers
- @bytelyst/subscription-client — subscription/plan API client + cache
- @bytelyst/celebrations — milestone triggers, confetti, positive messages
- @bytelyst/gentle-notifications — ND-friendly messaging, forbidden phrases
- @bytelyst/accessibility — VoiceOver/TalkBack label generators
- @bytelyst/quick-actions — progressive disclosure, smart defaults
- @bytelyst/time-references — familiar duration references
- @bytelyst/org-client — org/workspace/membership/license API client
- @bytelyst/marketplace-client — listing/review/install API client

All packages: pure TS, ESM, globalThis.fetch, no Node.js deps.
99 Vitest tests across 9 packages, 79/79 public methods covered.

Review fixes applied:
- time-references: fix module-level mutable state leak + add clearCustomReferences()
- accessibility: fix parameter reassignment in formatDurationForA11y/numberToWords
- subscription-client: fix flaky daysRemaining test (ms boundary race)
2026-03-19 13:10:09 -07:00

113 lines
3.7 KiB
TypeScript

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<string>();
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);
});
});