






















@@ -71,7 +71,12 @@ async function expectTtsPayloadResult(params: {
7171text: string;
7272target: "voice-note" | "audio-file";
7373audioAsVoice: true | undefined;
74+providerResult?: MockSpeechSynthesisResult;
75+mediaExtension?: string;
7476}) {
77+if (params.providerResult) {
78+synthesizeMock.mockResolvedValueOnce(params.providerResult);
79+}
7580const cfg = createTtsConfig(params.prefsName);
7681let mediaDir: string | undefined;
7782try {
@@ -84,7 +89,7 @@ async function expectTtsPayloadResult(params: {
84898590expect(synthesizeMock).toHaveBeenCalledWith(expect.objectContaining({ target: params.target }));
8691expect(result.audioAsVoice).toBe(params.audioAsVoice);
87-expect(result.mediaUrl).toMatch(/voice-\d+\.ogg$/);
92+expect(result.mediaUrl).toMatch(new RegExp(`voice-\\d+\\.${params.mediaExtension ?? "ogg"}$`));
88938994mediaDir = result.mediaUrl ? path.dirname(result.mediaUrl) : undefined;
9095} finally {
@@ -118,35 +123,26 @@ describe("speech-core native voice-note routing", () => {
118123});
119124});
120125121-it("marks Feishu voice-note TTS for channel-side transcoding when provider returns mp3", async () => {
122-synthesizeMock.mockResolvedValueOnce({
123-audioBuffer: Buffer.from("mp3"),
124-outputFormat: "mp3",
125-fileExtension: ".mp3",
126-voiceCompatible: false,
127-});
128-const cfg = createTtsConfig("openclaw-speech-core-tts-feishu-mp3-test");
129-let mediaDir: string | undefined;
130-try {
131-const result = await maybeApplyTtsToPayload({
132-payload: { text: "This Feishu reply should be transcoded by the channel." },
133- cfg,
134-channel: "feishu",
135-kind: "final",
126+it.each(["feishu", "whatsapp"] as const)(
127+"marks %s voice-note TTS for channel-side transcoding when provider returns mp3",
128+async (channel) => {
129+expect(_test.supportsTranscodedVoiceNoteTts(channel)).toBe(true);
130+await expectTtsPayloadResult({
131+ channel,
132+prefsName: `openclaw-speech-core-tts-${channel}-mp3-test`,
133+text: `This ${channel} reply should be transcoded by the channel.`,
134+target: "voice-note",
135+audioAsVoice: true,
136+mediaExtension: "mp3",
137+providerResult: {
138+audioBuffer: Buffer.from("mp3"),
139+outputFormat: "mp3",
140+fileExtension: ".mp3",
141+voiceCompatible: false,
142+},
136143});
137-138-expect(synthesizeMock).toHaveBeenCalledWith(
139-expect.objectContaining({ target: "voice-note" }),
140-);
141-expect(result.audioAsVoice).toBe(true);
142-expect(result.mediaUrl).toMatch(/voice-\d+\.mp3$/);
143-mediaDir = result.mediaUrl ? path.dirname(result.mediaUrl) : undefined;
144-} finally {
145-if (mediaDir) {
146-rmSync(mediaDir, { recursive: true, force: true });
147-}
148-}
149-});
144+},
145+);
150146151147it("keeps non-native voice-note channels as regular audio files", async () => {
152148await expectTtsPayloadResult({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。