| 1 | // Run: tsx src/__tests__/sound.test.ts |
| 2 | |
| 3 | import { attentionChimeEventKey, clearAttentionChimeKeys, shouldPlayAttentionChimeForEvent } from "../lib/sound"; |
| 4 | |
| 5 | let passed = 0; |
| 6 | let failed = 0; |
| 7 | |
| 8 | function eq(a: unknown, b: unknown, label: string) { |
| 9 | if (a === b) { |
| 10 | process.stdout.write(` PASS ${label}\n`); |
| 11 | passed += 1; |
| 12 | } else { |
| 13 | process.stdout.write(` FAIL ${label}: expected ${JSON.stringify(b)}, got ${JSON.stringify(a)}\n`); |
| 14 | failed += 1; |
| 15 | } |
| 16 | } |
| 17 | |
| 18 | console.log("\nsound notifications"); |
| 19 | |
| 20 | { |
| 21 | eq(attentionChimeEventKey({ kind: "approval_request", tabId: "tab-a", approval: { id: "approval-1" } }), "approval:tab-a:approval-1", "approval request builds a tab-scoped chime key"); |
| 22 | eq(attentionChimeEventKey({ kind: "ask_request", tabId: "tab-a", ask: { id: "ask-1" } }), "ask:tab-a:ask-1", "ask request builds a tab-scoped chime key"); |
| 23 | eq(attentionChimeEventKey({ kind: "approval_request", approval: { id: "approval-1" } }), "approval::approval-1", "legacy approval events without a tab still build a stable key"); |
| 24 | eq(attentionChimeEventKey({ kind: "turn_done" }), undefined, "non-attention events do not build chime keys"); |
| 25 | } |
| 26 | |
| 27 | { |
| 28 | const seen = new Set<string>(); |
| 29 | eq(shouldPlayAttentionChimeForEvent({ kind: "approval_request", tabId: "tab-a", approval: { id: "1" } }, seen), true, "first approval event plays"); |
| 30 | eq(shouldPlayAttentionChimeForEvent({ kind: "approval_request", tabId: "tab-a", approval: { id: "1" } }, seen), false, "replayed approval event for the same tab is deduped"); |
| 31 | eq(shouldPlayAttentionChimeForEvent({ kind: "approval_request", tabId: "tab-b", approval: { id: "1" } }, seen), true, "same approval id from another tab still plays"); |
| 32 | eq(shouldPlayAttentionChimeForEvent({ kind: "ask_request", tabId: "tab-a", ask: { id: "1" } }, seen), true, "ask id sharing an approval id still plays"); |
| 33 | eq(shouldPlayAttentionChimeForEvent({ kind: "approval_request" }, seen), false, "malformed approval event does not play"); |
| 34 | } |
| 35 | |
| 36 | { |
| 37 | // The dedupe set stays bounded: after many unique prompts it self-prunes |
| 38 | // while still deduping recently seen ids. |
| 39 | const seen = new Set<string>(); |
| 40 | for (let i = 0; i < 2000; i++) { |
| 41 | shouldPlayAttentionChimeForEvent({ kind: "approval_request", tabId: "t", approval: { id: String(i) } }, seen); |
| 42 | } |
| 43 | eq(seen.size <= 1024, true, "dedupe set stays bounded after 2000 unique prompts"); |
| 44 | eq(shouldPlayAttentionChimeForEvent({ kind: "approval_request", tabId: "t", approval: { id: "1999" } }, seen), false, "most recent prompt id is still deduped after pruning"); |
| 45 | } |
| 46 | |
| 47 | { |
| 48 | // Runtime rebuild clears dedupe keys so reissued ids chime again. |
| 49 | const seen = new Set<string>(); |
| 50 | shouldPlayAttentionChimeForEvent({ kind: "approval_request", tabId: "tab-a", approval: { id: "1" } }, seen); |
| 51 | shouldPlayAttentionChimeForEvent({ kind: "ask_request", tabId: "tab-b", ask: { id: "1" } }, seen); |
| 52 | clearAttentionChimeKeys(seen, "tab-a"); |
| 53 | eq(shouldPlayAttentionChimeForEvent({ kind: "approval_request", tabId: "tab-a", approval: { id: "1" } }, seen), true, "rebuilt tab's reissued approval id chimes again"); |
| 54 | eq(shouldPlayAttentionChimeForEvent({ kind: "ask_request", tabId: "tab-b", ask: { id: "1" } }, seen), false, "other tab's keys survive a scoped clear"); |
| 55 | clearAttentionChimeKeys(seen); |
| 56 | eq(shouldPlayAttentionChimeForEvent({ kind: "ask_request", tabId: "tab-b", ask: { id: "1" } }, seen), true, "tab-less ready clears every key"); |
| 57 | } |
| 58 | |
| 59 | if (failed) { |
| 60 | console.error(`sound notifications: ${failed} failed, ${passed} passed`); |
| 61 | process.exit(1); |
| 62 | } |
| 63 | |
| 64 | console.log(`sound notifications: ${passed} passed`); |
| 65 |