mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-02 02:31:39 +00:00
test(ui): cover reentrant Talk meter cleanup
This commit is contained in:
@@ -95,6 +95,44 @@ describe("RealtimeTalkMediaStreamMeter", () => {
|
||||
expect(close).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("reclaims its interval when the initial level callback stops it", () => {
|
||||
vi.useFakeTimers();
|
||||
const close = vi.fn(async () => undefined);
|
||||
const disconnectSource = vi.fn();
|
||||
const disconnectAnalyser = vi.fn();
|
||||
class MockAudioContext {
|
||||
readonly close = close;
|
||||
createMediaStreamSource() {
|
||||
return { connect: vi.fn(), disconnect: disconnectSource };
|
||||
}
|
||||
createAnalyser() {
|
||||
return {
|
||||
fftSize: 0,
|
||||
smoothingTimeConstant: 0,
|
||||
disconnect: disconnectAnalyser,
|
||||
getFloatTimeDomainData: (samples: Float32Array) => samples.fill(0.25),
|
||||
};
|
||||
}
|
||||
}
|
||||
vi.stubGlobal("AudioContext", MockAudioContext);
|
||||
const onLevel = vi.fn((level: number) => {
|
||||
if (level > 0) {
|
||||
meter.stop();
|
||||
}
|
||||
});
|
||||
const meter = new RealtimeTalkMediaStreamMeter(onLevel);
|
||||
|
||||
meter.start({} as MediaStream);
|
||||
meter.stop();
|
||||
meter.stop();
|
||||
vi.advanceTimersByTime(1_000);
|
||||
|
||||
expect(vi.getTimerCount()).toBe(0);
|
||||
expect(disconnectSource).toHaveBeenCalledOnce();
|
||||
expect(disconnectAnalyser).toHaveBeenCalledOnce();
|
||||
expect(close).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("closes an owned AudioContext when analyser setup fails", () => {
|
||||
const close = vi.fn(async () => undefined);
|
||||
class MockAudioContext {
|
||||
|
||||
@@ -614,6 +614,25 @@ describe("GatewayRelayRealtimeTalkTransport", () => {
|
||||
expect(onInputLevel).toHaveBeenLastCalledWith(0);
|
||||
});
|
||||
|
||||
it("reclaims the input meter when its first level update stops the transport", async () => {
|
||||
vi.useFakeTimers();
|
||||
const client = createClient();
|
||||
const onInputLevel = vi.fn((level: number) => {
|
||||
if (level > 0) {
|
||||
transport.stop();
|
||||
}
|
||||
});
|
||||
const transport = createTransport({ client, callbacks: { onInputLevel } });
|
||||
|
||||
await expect(transport.start()).resolves.toBe("ready");
|
||||
transport.stop();
|
||||
transport.stop();
|
||||
vi.advanceTimersByTime(1_000);
|
||||
|
||||
expect(vi.getTimerCount()).toBe(0);
|
||||
expect(requestCallsFor(client, "talk.session.close")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("bounds stalled microphone appends and aborts every owner on stop", async () => {
|
||||
const onStatus = vi.fn();
|
||||
const client = createClient();
|
||||
|
||||
@@ -223,6 +223,42 @@ describe("WebRtcSdpRealtimeTalkTransport", () => {
|
||||
expect(close).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("reclaims the input meter when its first level update stops the transport", async () => {
|
||||
vi.useFakeTimers();
|
||||
stubAnswerSdpFetch();
|
||||
const close = vi.fn(async () => undefined);
|
||||
class MockAudioContext {
|
||||
readonly close = close;
|
||||
createMediaStreamSource() {
|
||||
return { connect: vi.fn(), disconnect: vi.fn() };
|
||||
}
|
||||
createAnalyser() {
|
||||
return {
|
||||
fftSize: 0,
|
||||
smoothingTimeConstant: 0,
|
||||
disconnect: vi.fn(),
|
||||
getFloatTimeDomainData: (samples: Float32Array) => samples.fill(0.25),
|
||||
};
|
||||
}
|
||||
}
|
||||
vi.stubGlobal("AudioContext", MockAudioContext);
|
||||
const onInputLevel = vi.fn((level: number) => {
|
||||
if (level > 0) {
|
||||
transport.stop();
|
||||
}
|
||||
});
|
||||
const transport = createOpenAiTransport({}, { onInputLevel });
|
||||
|
||||
await expect(transport.start()).resolves.toBe("cancelled");
|
||||
transport.stop();
|
||||
transport.stop();
|
||||
vi.advanceTimersByTime(1_000);
|
||||
|
||||
expect(vi.getTimerCount()).toBe(0);
|
||||
expect(stopInputTrack).toHaveBeenCalledOnce();
|
||||
expect(close).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("does not continue WebRTC setup when stopped while microphone access is pending", async () => {
|
||||
const fetchMock = vi.fn(async () => new Response("answer-sdp"));
|
||||
vi.stubGlobal("fetch", fetchMock as unknown as typeof fetch);
|
||||
|
||||
Reference in New Issue
Block a user