evOo-local/apps/web/src/lib/oo-sound.test.ts
cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

101 lines
2.5 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { type OoSoundCue, ooSound } from "./oo-sound";
import { readOoSoundEnabled, writeOoSoundEnabled } from "./oo-sound-pref";
describe("oo-sound-pref", () => {
const storage = new Map<string, string>();
beforeEach(() => {
storage.clear();
const localStorage = {
getItem: (key: string) => storage.get(key) ?? null,
setItem: (key: string, value: string) => {
storage.set(key, value);
},
removeItem: (key: string) => {
storage.delete(key);
},
};
vi.stubGlobal("localStorage", localStorage);
vi.stubGlobal("window", { localStorage });
});
afterEach(() => {
vi.unstubAllGlobals();
});
it("defaults sound to enabled", () => {
expect(readOoSoundEnabled()).toBe(true);
});
it("persists disabled preference", () => {
writeOoSoundEnabled(false);
expect(readOoSoundEnabled()).toBe(false);
writeOoSoundEnabled(true);
expect(readOoSoundEnabled()).toBe(true);
});
});
describe("ooSound", () => {
class FakeContext {
state: AudioContextState = "running";
currentTime = 0;
destination = {};
createGain() {
return {
gain: { value: 0, setValueAtTime: vi.fn(), exponentialRampToValueAtTime: vi.fn() },
connect: vi.fn(),
};
}
createOscillator() {
return {
type: "sine",
frequency: { setValueAtTime: vi.fn() },
connect: vi.fn(),
start: vi.fn(),
stop: vi.fn(),
};
}
resume = vi.fn(async () => undefined);
}
beforeEach(() => {
vi.stubGlobal("AudioContext", FakeContext);
vi.stubGlobal("window", {
matchMedia: () => ({
matches: false,
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
}),
});
vi.stubGlobal("document", { visibilityState: "visible" });
writeOoSoundEnabled(true);
ooSound.setEnabled(true);
});
afterEach(() => {
vi.unstubAllGlobals();
});
it("plays procedural cues when enabled", () => {
const cues: OoSoundCue[] = [
"work-complete",
"listening-start",
"listening-stop",
"speaking-start",
"speaking-done",
"attention",
"message-sent",
"handoff",
];
for (const cue of cues) {
expect(() => ooSound.play(cue)).not.toThrow();
}
});
it("skips playback when disabled", () => {
ooSound.setEnabled(false);
expect(() => ooSound.play("work-complete")).not.toThrow();
});
});