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)
98 lines
3.6 KiB
TypeScript
98 lines
3.6 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { createGentleNotificationEngine, FORBIDDEN_PHRASES } from './client.js';
|
|
|
|
describe('createGentleNotificationEngine', () => {
|
|
it('should return default config', () => {
|
|
const engine = createGentleNotificationEngine();
|
|
const config = engine.getDefaultConfig();
|
|
expect(config.maxPerHour).toBe(3);
|
|
expect(config.tone).toBe('encouraging');
|
|
expect(config.adaptiveFrequency).toBe(true);
|
|
expect(config.dismissCount).toBe(0);
|
|
expect(config.suppressThreshold).toBe(5);
|
|
});
|
|
|
|
it('should return a message for known type', () => {
|
|
const engine = createGentleNotificationEngine();
|
|
const msg = engine.getMessage('reminder');
|
|
expect(msg.title.length).toBeGreaterThan(0);
|
|
expect(msg.body.length).toBeGreaterThan(0);
|
|
expect(['encouraging', 'neutral', 'minimal']).toContain(msg.tone);
|
|
});
|
|
|
|
it('should return fallback for unknown type', () => {
|
|
const engine = createGentleNotificationEngine();
|
|
const msg = engine.getMessage('nonexistent_type');
|
|
expect(msg.title).toBe('Hey');
|
|
expect(msg.body.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('should suppress when dismiss threshold reached', () => {
|
|
const engine = createGentleNotificationEngine();
|
|
const config = { ...engine.getDefaultConfig(), dismissCount: 5 };
|
|
expect(engine.shouldSuppress(config)).toBe(true);
|
|
});
|
|
|
|
it('should not suppress when below threshold', () => {
|
|
const engine = createGentleNotificationEngine();
|
|
const config = { ...engine.getDefaultConfig(), dismissCount: 2 };
|
|
expect(engine.shouldSuppress(config)).toBe(false);
|
|
});
|
|
|
|
it('should record dismissal and reduce frequency', () => {
|
|
const engine = createGentleNotificationEngine();
|
|
let config = engine.getDefaultConfig();
|
|
expect(config.dismissCount).toBe(0);
|
|
|
|
config = engine.recordDismissal(config);
|
|
expect(config.dismissCount).toBe(1);
|
|
|
|
// Record enough to trigger suppression
|
|
for (let i = 0; i < 4; i++) {
|
|
config = engine.recordDismissal(config);
|
|
}
|
|
expect(config.dismissCount).toBe(5);
|
|
expect(config.maxPerHour).toBeLessThan(3);
|
|
});
|
|
|
|
it('should reset dismissals', () => {
|
|
const engine = createGentleNotificationEngine();
|
|
let config = engine.getDefaultConfig();
|
|
config = engine.recordDismissal(config);
|
|
config = engine.recordDismissal(config);
|
|
expect(config.dismissCount).toBe(2);
|
|
|
|
config = engine.resetDismissals(config);
|
|
expect(config.dismissCount).toBe(0);
|
|
});
|
|
|
|
it('should allow registering custom messages', () => {
|
|
const engine = createGentleNotificationEngine();
|
|
engine.registerMessages('fasting', [
|
|
{ title: 'Fasting Reminder', body: 'Your body is doing great things!', tone: 'encouraging' },
|
|
]);
|
|
const msg = engine.getMessage('fasting');
|
|
expect(msg.title).toBe('Fasting Reminder');
|
|
});
|
|
|
|
it('should export FORBIDDEN_PHRASES', () => {
|
|
expect(FORBIDDEN_PHRASES).toContain("You haven't");
|
|
expect(FORBIDDEN_PHRASES).toContain('You failed');
|
|
expect(FORBIDDEN_PHRASES.length).toBeGreaterThanOrEqual(8);
|
|
});
|
|
|
|
it('should detect forbidden phrases', () => {
|
|
const engine = createGentleNotificationEngine();
|
|
expect(engine.containsForbiddenPhrase("You haven't done this yet")).toBe(true);
|
|
expect(engine.containsForbiddenPhrase('Great job today!')).toBe(false);
|
|
expect(engine.containsForbiddenPhrase('you failed the test')).toBe(true);
|
|
});
|
|
|
|
it('should respect custom initial config', () => {
|
|
const engine = createGentleNotificationEngine({ maxPerHour: 10, tone: 'minimal' });
|
|
const config = engine.getDefaultConfig();
|
|
expect(config.maxPerHour).toBe(10);
|
|
expect(config.tone).toBe('minimal');
|
|
});
|
|
});
|