Task 15 of the slice plan. Strategy §4.1 immediate-reward toast. celebrate(noteId) deterministically picks one of the 4 reward copies via SHA-256(noteId)[0] % 4, then forwards to the injected send() callback (which Task 30 wires to a real electron Notification). Skips silently when isSupported() is false (denied OS permission), and swallows send() errors so that capture path never fails because of a notification quirk. Verification: `npx vitest run tests/unit/NotificationService.test.ts` 3 passed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
42 lines
1.3 KiB
TypeScript
42 lines
1.3 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { NotificationService, REWARD_COPIES } from '@main/services/NotificationService.js';
|
|
|
|
describe('NotificationService', () => {
|
|
it('rotates copy deterministically by noteId hash', () => {
|
|
const fired: string[] = [];
|
|
const svc = new NotificationService({
|
|
isSupported: () => true,
|
|
send: (body) => { fired.push(body); }
|
|
});
|
|
const id1 = '00000000-0000-7000-8000-000000000001';
|
|
svc.celebrate(id1);
|
|
svc.celebrate(id1);
|
|
expect(fired).toHaveLength(2);
|
|
expect(fired[0]).toBe(fired[1]);
|
|
expect(REWARD_COPIES).toContain(fired[0]);
|
|
});
|
|
|
|
it('different ids select different (eventually all 4)', () => {
|
|
const fired: string[] = [];
|
|
const svc = new NotificationService({
|
|
isSupported: () => true,
|
|
send: (body) => { fired.push(body); }
|
|
});
|
|
for (let i = 0; i < 32; i++) {
|
|
svc.celebrate(`id-${i}`);
|
|
}
|
|
const distinct = new Set(fired);
|
|
expect(distinct.size).toBe(REWARD_COPIES.length);
|
|
});
|
|
|
|
it('skips silently when notifications unsupported', () => {
|
|
const fired: string[] = [];
|
|
const svc = new NotificationService({
|
|
isSupported: () => false,
|
|
send: (body) => { fired.push(body); }
|
|
});
|
|
svc.celebrate('id-1');
|
|
expect(fired).toHaveLength(0);
|
|
});
|
|
});
|