import { act, renderHook } from "@testing-library/react"; import { beforeEach, expect, it, vi } from "vitest"; import { getSelectedSpeechVoice, getSpeechVoiceDescription, getSpeechVoiceName, setSelectedSpeechVoice, SPEECH_VOICE_STORAGE_KEY, useSelectedSpeechVoice } from "./speech-voices"; beforeEach(() => setSelectedSpeechVoice("")); it("shares a saved voice across selectors and restores it from browser storage", () => { const first = renderHook(useSelectedSpeechVoice); const second = renderHook(useSelectedSpeechVoice); act(() => setSelectedSpeechVoice("zh-CN-YunyangNeural")); expect(first.result.current).toBe("zh-CN-YunyangNeural"); expect(second.result.current).toBe("zh-CN-YunyangNeural"); expect(localStorage.getItem(SPEECH_VOICE_STORAGE_KEY)).toBe("zh-CN-YunyangNeural"); act(() => { localStorage.setItem(SPEECH_VOICE_STORAGE_KEY, "zh-CN-XiaoyiNeural"); window.dispatchEvent(new StorageEvent("storage", { key: SPEECH_VOICE_STORAGE_KEY })); }); expect(first.result.current).toBe("zh-CN-XiaoyiNeural"); first.unmount(); second.unmount(); }); it("ignores malformed stored values and resets to the server default", () => { localStorage.setItem(SPEECH_VOICE_STORAGE_KEY, "unsafe"); expect(getSelectedSpeechVoice()).toBe(""); localStorage.setItem(SPEECH_VOICE_STORAGE_KEY, "en-US-AriaNeural"); expect(getSelectedSpeechVoice()).toBe(""); setSelectedSpeechVoice("zh-CN-YunyangNeural"); setSelectedSpeechVoice(""); expect(getSelectedSpeechVoice()).toBe(""); expect(localStorage.getItem(SPEECH_VOICE_STORAGE_KEY)).toBeNull(); }); it("provides Chinese labels and gender descriptions for Mandarin voices", () => { const voice = { id: "zh-CN-YunyangNeural", name: "Yunyang", locale: "zh-CN" as const, gender: "Male" as const }; expect(getSpeechVoiceName(voice)).toBe("云扬"); expect(getSpeechVoiceDescription(voice)).toContain("男声"); expect(getSpeechVoiceDescription(voice)).toContain("普通话"); }); it("keeps the choice for this page if browser storage cannot be written", () => { const write = vi.spyOn(Storage.prototype, "setItem").mockImplementation(() => { throw new Error("quota exceeded"); }); try { setSelectedSpeechVoice("zh-CN-YunyangNeural"); expect(getSelectedSpeechVoice()).toBe("zh-CN-YunyangNeural"); } finally { write.mockRestore(); setSelectedSpeechVoice(""); } });