221 lines
5.8 KiB
TypeScript
221 lines
5.8 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import {
|
|
handleNotificationEvent,
|
|
playNotification,
|
|
shouldPlayNotification,
|
|
} from "../src/audio";
|
|
|
|
describe("shouldPlayNotification", () => {
|
|
it("returns false when the selected sound is unavailable", () => {
|
|
expect(
|
|
shouldPlayNotification({
|
|
selectedSound: "missing.wav",
|
|
soundExists: false,
|
|
})
|
|
).toBe(false);
|
|
});
|
|
|
|
it("returns true when a selected sound exists", () => {
|
|
expect(
|
|
shouldPlayNotification({
|
|
selectedSound: "ping-success.wav",
|
|
soundExists: true,
|
|
})
|
|
).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("playNotification", () => {
|
|
it("warns and plays nothing for an invalid selected sound", async () => {
|
|
const warn = vi.fn();
|
|
const createAudio = vi.fn();
|
|
|
|
const result = await playNotification({
|
|
audioUrl: "unused",
|
|
createAudio,
|
|
logger: { warn },
|
|
selectedSound: "missing.wav",
|
|
soundExists: false,
|
|
volume: 0.7,
|
|
});
|
|
|
|
expect(result.played).toBe(false);
|
|
expect(createAudio).not.toHaveBeenCalled();
|
|
expect(warn).toHaveBeenCalledWith(expect.stringContaining("Selected sound"));
|
|
});
|
|
|
|
it("clears stale current audio for an invalid selected sound", async () => {
|
|
const pause = vi.fn();
|
|
const warn = vi.fn();
|
|
const currentAudio = {
|
|
currentTime: 9,
|
|
pause,
|
|
play: vi.fn(),
|
|
volume: 0.2,
|
|
};
|
|
|
|
const result = await playNotification({
|
|
audioUrl: "unused",
|
|
createAudio: vi.fn(),
|
|
currentAudio,
|
|
logger: { warn },
|
|
selectedSound: "missing.wav",
|
|
soundExists: false,
|
|
volume: 0.7,
|
|
});
|
|
|
|
expect(pause).toHaveBeenCalledTimes(1);
|
|
expect(currentAudio.currentTime).toBe(0);
|
|
expect(result.played).toBe(false);
|
|
expect(result.audio).toBeNull();
|
|
expect(warn).toHaveBeenCalledWith(expect.stringContaining("Selected sound"));
|
|
});
|
|
|
|
it("stops the previous audio before playing a new sound", async () => {
|
|
const pause = vi.fn();
|
|
const play = vi.fn().mockResolvedValue(undefined);
|
|
const currentAudio = {
|
|
currentTime: 12,
|
|
pause,
|
|
play: vi.fn(),
|
|
volume: 0,
|
|
};
|
|
const nextAudio = {
|
|
currentTime: 0,
|
|
pause: vi.fn(),
|
|
play,
|
|
volume: 0,
|
|
};
|
|
const createAudio = vi.fn(() => nextAudio);
|
|
|
|
const result = await playNotification({
|
|
audioUrl: "memory://ping-success",
|
|
createAudio,
|
|
currentAudio,
|
|
selectedSound: "ping-success.wav",
|
|
soundExists: true,
|
|
stopPrevious: true,
|
|
volume: 0.4,
|
|
});
|
|
|
|
expect(pause).toHaveBeenCalledTimes(1);
|
|
expect(currentAudio.currentTime).toBe(0);
|
|
expect(nextAudio.volume).toBe(0.4);
|
|
expect(play).toHaveBeenCalledTimes(1);
|
|
expect(result.played).toBe(true);
|
|
expect(result.audio).toBe(nextAudio);
|
|
});
|
|
|
|
it("warns and returns a non-playing result when audio playback rejects", async () => {
|
|
const warn = vi.fn();
|
|
const pause = vi.fn();
|
|
const play = vi.fn().mockRejectedValue(new Error("autoplay blocked"));
|
|
const currentAudio = {
|
|
currentTime: 4,
|
|
pause,
|
|
play: vi.fn(),
|
|
volume: 0.3,
|
|
};
|
|
const nextAudio = {
|
|
currentTime: 0,
|
|
pause: vi.fn(),
|
|
play,
|
|
volume: 0,
|
|
};
|
|
|
|
const result = await playNotification({
|
|
audioUrl: "memory://ping-failure",
|
|
createAudio: vi.fn(() => nextAudio),
|
|
currentAudio,
|
|
logger: { warn },
|
|
selectedSound: "ping-failure.wav",
|
|
soundExists: true,
|
|
volume: 0.5,
|
|
});
|
|
|
|
expect(warn).toHaveBeenCalledWith(
|
|
expect.stringContaining("Unable to play notification audio")
|
|
);
|
|
expect(result.played).toBe(false);
|
|
expect(result.audio).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("handleNotificationEvent", () => {
|
|
it("plays flat sound ids through the real backend sound route", async () => {
|
|
const play = vi.fn().mockResolvedValue(undefined);
|
|
const audio = {
|
|
currentTime: 0,
|
|
pause: vi.fn(),
|
|
play,
|
|
volume: 0,
|
|
};
|
|
const createAudio = vi.fn(() => audio);
|
|
|
|
const result = await handleNotificationEvent({
|
|
catalog: [{ name: "uploaded.wav" }],
|
|
createAudio,
|
|
detail: {
|
|
event_kind: "global",
|
|
sound_id: "uploaded.wav",
|
|
source: "queue_drained",
|
|
status: "success",
|
|
volume: 0.8,
|
|
},
|
|
});
|
|
|
|
expect(createAudio).toHaveBeenCalledWith("/comfyui-ping/sounds/uploaded.wav");
|
|
expect(play).toHaveBeenCalledTimes(1);
|
|
expect(result.played).toBe(true);
|
|
});
|
|
|
|
it("normalizes legacy prefixed sound ids before playback", async () => {
|
|
const play = vi.fn().mockResolvedValue(undefined);
|
|
const audio = {
|
|
currentTime: 0,
|
|
pause: vi.fn(),
|
|
play,
|
|
volume: 0,
|
|
};
|
|
const createAudio = vi.fn(() => audio);
|
|
|
|
const result = await handleNotificationEvent({
|
|
catalog: [{ name: "uploaded.wav" }],
|
|
createAudio,
|
|
detail: {
|
|
event_kind: "global",
|
|
sound_id: "custom:uploaded.wav",
|
|
source: "queue_drained",
|
|
status: "success",
|
|
volume: 0.8,
|
|
},
|
|
});
|
|
|
|
expect(createAudio).toHaveBeenCalledWith("/comfyui-ping/sounds/uploaded.wav");
|
|
expect(play).toHaveBeenCalledTimes(1);
|
|
expect(result.played).toBe(true);
|
|
});
|
|
|
|
it("warns and stays silent when the selected sound is not in the catalog", async () => {
|
|
const warn = vi.fn();
|
|
const createAudio = vi.fn();
|
|
|
|
const result = await handleNotificationEvent({
|
|
catalog: [{ name: "ping-success.wav" }],
|
|
createAudio,
|
|
detail: {
|
|
event_kind: "global",
|
|
sound_id: "uploaded.wav",
|
|
source: "queue_drained",
|
|
status: "success",
|
|
volume: 0.8,
|
|
},
|
|
logger: { warn },
|
|
});
|
|
|
|
expect(createAudio).not.toHaveBeenCalled();
|
|
expect(warn).toHaveBeenCalledWith(expect.stringContaining("Selected sound"));
|
|
expect(result.played).toBe(false);
|
|
});
|
|
});
|