import test from "node:test"; import assert from "node:assert/strict"; import { announcePassingHelper, announceRaceStartedHelper, getSessionAnnouncementText, selectSpeechVoice, } from "../src/runtime_services.js"; test("speech voice selection prefers an explicit voice and then the current language", () => { const voices = [ { name: "English", lang: "en-US", voiceURI: "voice-en", localService: true }, { name: "Swedish", lang: "sv-SE", voiceURI: "voice-sv", localService: true }, ]; assert.equal(selectSpeechVoice(voices, "voice-en", "sv-SE")?.voiceURI, "voice-en"); assert.equal(selectSpeechVoice(voices, "", "sv-SE")?.voiceURI, "voice-sv"); assert.equal(selectSpeechVoice(voices, "missing", "de-DE"), null); }); test("session announcements distinguish practice from racing", () => { const t = (key) => key; assert.equal(getSessionAnnouncementText({ type: "open_practice" }, "start", { t }), "timing.practice_started_voice"); assert.equal(getSessionAnnouncementText({ type: "practice" }, "finish", { t }), "timing.practice_finished_voice"); assert.equal(getSessionAnnouncementText({ type: "final" }, "start", { t }), "timing.race_started_voice"); assert.equal(getSessionAnnouncementText({ type: "heat" }, "finish", { t }), "timing.race_finished_voice"); }); test("passing announcement uses the driver's spoken name and beeps while speech is busy", () => { const calls = []; announcePassingHelper( { displayName: "Anders", speechName: "Ann-ders" }, { state: { settings: { audioEnabled: true, passingSoundMode: "name" } }, t: () => "Unknown", speakText: (value, options) => { calls.push([value, options]); return false; }, playPassingBeep: () => calls.push(["beep"]), } ); assert.deepEqual(calls, [["Ann-ders", { interrupt: false }], ["beep"]]); }); test("race start creates an overlay event and plays enabled start cues", async () => { const calls = []; announceRaceStartedHelper( { id: "session_1", name: "A Final", type: "final" }, { state: { settings: { audioEnabled: true, startSoundEnabled: true, finishVoiceEnabled: true } }, t: (key) => key, pushOverlayEvent: (type, label, options) => calls.push([type, label, options]), playStartCue: () => calls.push(["tone"]), speakText: (value, options) => calls.push(["speech", value, options]), } ); assert.deepEqual(calls, [ ["start", "A Final • timing.start", { silentCue: true }], ["tone"], ]); await new Promise((resolve) => setTimeout(resolve, 700)); assert.deepEqual(calls[2], ["speech", "timing.race_started_voice", { interrupt: true }]); });