Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 10 additions & 17 deletions __tests__/abstract-tts-gender.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,11 @@
* Tests for AbstractTTSClient.getVoicesByGender() (issue #44)
*/

import { AbstractTTSClient } from "../src/core/abstract-tts";
import type { UnifiedVoice } from "../src/types";

// Minimal stub so we can instantiate a concrete subclass
jest.mock("../src/core/abstract-tts", () => {
const actual = jest.requireActual("../src/core/abstract-tts");
return actual;
});

// Build a concrete subclass with a fixed voice list
async function makeClient(voices: UnifiedVoice[]) {
const { AbstractTTSClient } = await import("../src/core/abstract-tts");

function makeClient(voices: UnifiedVoice[]) {
class TestTTSClient extends AbstractTTSClient {
constructor() {
super({ lang: "en-US" } as any);
Expand Down Expand Up @@ -68,29 +61,29 @@ const VOICES: UnifiedVoice[] = [

describe("AbstractTTSClient.getVoicesByGender()", () => {
it("returns only Female voices when asked for Female", async () => {
const client = await makeClient(VOICES);
const result = await (client as any).getVoicesByGender("Female");
const client = makeClient(VOICES);
const result = await client.getVoicesByGender("Female");
expect(result).toHaveLength(2);
expect(result.every((v: UnifiedVoice) => v.gender === "Female")).toBe(true);
});

it("returns only Male voices when asked for Male", async () => {
const client = await makeClient(VOICES);
const result = await (client as any).getVoicesByGender("Male");
const client = makeClient(VOICES);
const result = await client.getVoicesByGender("Male");
expect(result).toHaveLength(1);
expect(result[0].id).toBe("voice-male-1");
});

it("returns only Unknown voices when asked for Unknown", async () => {
const client = await makeClient(VOICES);
const result = await (client as any).getVoicesByGender("Unknown");
const client = makeClient(VOICES);
const result = await client.getVoicesByGender("Unknown");
expect(result).toHaveLength(1);
expect(result[0].id).toBe("voice-unknown-1");
});

it("returns an empty array when no voices match the gender", async () => {
const client = await makeClient([VOICES[0]]); // only Female
const result = await (client as any).getVoicesByGender("Male");
const client = makeClient([VOICES[0]]); // only Female
const result = await client.getVoicesByGender("Male");
expect(result).toHaveLength(0);
});
});
20 changes: 3 additions & 17 deletions __tests__/azure-ssml.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,10 @@
* Tests for Azure SSML generation correctness (issue #42)
*/

import { jest } from "@jest/globals";
import { AzureTTSClient } from "../src/engines/azure";
import * as SSMLUtils from "../src/core/ssml-utils";

// Minimal stub so we can import AzureTTSClient without real credentials
jest.mock("../src/core/abstract-tts", () => {
return {
AbstractTTSClient: class {
voiceId = "en-US-AriaNeural";
lang = "en-US";
properties: Record<string, unknown> = { rate: "medium", pitch: "medium", volume: 100 };
timings: unknown[] = [];
on() {}
emit() {}
},
};
});

// We test the SSML utilities directly — no network calls needed.

describe("createProsodyTag — volume format", () => {
Expand All @@ -43,7 +31,7 @@ describe("createProsodyTag — volume format", () => {
});

describe("Azure prepareSSML — no spurious xmlns/version warnings", () => {
let warnSpy: jest.SpyInstance;
let warnSpy: ReturnType<typeof jest.spyOn>;

beforeEach(() => {
warnSpy = jest.spyOn(console, "warn").mockImplementation(() => {});
Expand All @@ -54,8 +42,6 @@ describe("Azure prepareSSML — no spurious xmlns/version warnings", () => {
});

it("does not warn about missing xmlns or version when synthesising plain text", async () => {
// Import lazily so mock is in place
const { AzureTTSClient } = await import("../src/engines/azure");
const client = new AzureTTSClient({ subscriptionKey: "key", region: "eastus" });

// Access the private method via type cast
Expand Down
14 changes: 2 additions & 12 deletions __tests__/elevenlabs-gender.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,12 @@
* The bulk voice list response includes labels.gender as "female" / "male"
*/

jest.mock("../src/core/abstract-tts", () => ({
AbstractTTSClient: class {
voiceId = "some-voice-id";
lang = "en-US";
properties: Record<string, unknown> = { rate: "medium", pitch: "medium", volume: 100 };
timings: unknown[] = [];
on() {}
emit() {}
},
}));
import { ElevenLabsTTSClient } from "../src/engines/elevenlabs";

describe("ElevenLabs _mapVoicesToUnified — gender mapping", () => {
let client: any;

beforeEach(async () => {
const { ElevenLabsTTSClient } = await import("../src/engines/elevenlabs");
beforeEach(() => {
client = new ElevenLabsTTSClient({ apiKey: "fake" });
});

Expand Down
14 changes: 2 additions & 12 deletions __tests__/google-gender.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,12 @@
* These must map to "Male", "Female", "Unknown" in UnifiedVoice
*/

jest.mock("../src/core/abstract-tts", () => ({
AbstractTTSClient: class {
voiceId = "en-US-Standard-A";
lang = "en-US";
properties: Record<string, unknown> = { rate: "medium", pitch: "medium", volume: 100 };
timings: unknown[] = [];
on() {}
emit() {}
},
}));
import { GoogleTTSClient } from "../src/engines/google";

describe("Google _mapVoicesToUnified — gender casing", () => {
let client: any;

beforeEach(async () => {
const { GoogleTTSClient } = await import("../src/engines/google");
beforeEach(() => {
client = new GoogleTTSClient({ keyFilename: "fake.json" });
});

Expand Down
Loading