131 lines
4.0 KiB
TypeScript
131 lines
4.0 KiB
TypeScript
/*
|
|||
|
|
Copyright 2026 Lotus Guild
|
||
|
|
|
||
|
|
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||
|
|
Please see LICENSE in the repository root for full details.
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
||
|
|
import { Room as LivekitRoom } from "livekit-client";
|
||
|
|
import { BehaviorSubject } from "rxjs";
|
||
|
|
import { logger } from "matrix-js-sdk/lib/logger";
|
||
|
|
import EventEmitter from "events";
|
||
|
|
|
||
|
|
import { ObservableScope } from "../state/ObservableScope.ts";
|
||
|
|
import { ECConnectionFactory } from "../state/CallViewModel/remoteMembers/ConnectionFactory.ts";
|
||
|
|
import type { OpenIDClientParts } from "../livekit/openIDSFU.ts";
|
||
|
|
import {
|
||
|
|
exampleTransport,
|
||
|
|
mockMediaDevices,
|
||
|
|
ownMemberMock,
|
||
|
|
} from "../utils/test.ts";
|
||
|
|
import type { ProcessorState } from "../livekit/TrackProcessorContext.tsx";
|
||
|
|
import {
|
||
|
|
autoGainControlSetting,
|
||
|
|
echoCancellationSetting,
|
||
|
|
noiseSuppressionSetting,
|
||
|
|
} from "../settings/settings.ts";
|
||
|
|
|
||
|
|
// [lotus] Upstream v0.25.0 moved the audio-capture constraints to Settings.
|
||
|
|
// The Lotus host still drives them per-call via URL params (it turns browser
|
||
|
|
// noiseSuppression/AGC OFF for the in-source ML denoise tier), so
|
||
|
|
// ConnectionFactory ANDs the Setting with the URL param. These tests pin that
|
||
|
|
// contract.
|
||
|
|
|
||
|
|
const { getUrlParams } = vi.hoisted(() => ({ getUrlParams: vi.fn() }));
|
||
|
|
vi.mock("../UrlParams", () => ({ getUrlParams }));
|
||
|
|
|
||
|
|
vi.mock("livekit-client", async (importOriginal) => ({
|
||
|
|
...(await importOriginal()),
|
||
|
|
Room: vi.fn().mockImplementation(function (this: LivekitRoom) {
|
||
|
|
const emitter = new EventEmitter();
|
||
|
|
return {
|
||
|
|
on: emitter.on.bind(emitter),
|
||
|
|
off: emitter.off.bind(emitter),
|
||
|
|
emit: emitter.emit.bind(emitter),
|
||
|
|
disconnect: vi.fn(),
|
||
|
|
remoteParticipants: new Map(),
|
||
|
|
} as unknown as LivekitRoom;
|
||
|
|
}),
|
||
|
|
}));
|
||
|
|
|
||
|
|
let testScope: ObservableScope;
|
||
|
|
const mockClient: OpenIDClientParts = {
|
||
|
|
getOpenIdToken: vi.fn().mockReturnValue(""),
|
||
|
|
getDeviceId: vi.fn().mockReturnValue("DEV000"),
|
||
|
|
};
|
||
|
|
|
||
|
|
function createRoom(): void {
|
||
|
|
new ECConnectionFactory(
|
||
|
|
mockClient,
|
||
|
|
"!roomid:example.org",
|
||
|
|
mockMediaDevices({}),
|
||
|
|
new BehaviorSubject<ProcessorState>({
|
||
|
|
supported: true,
|
||
|
|
processor: undefined,
|
||
|
|
}),
|
||
|
|
undefined,
|
||
|
|
false,
|
||
|
|
).createConnection(testScope, exampleTransport, ownMemberMock, logger);
|
||
|
|
}
|
||
|
|
|
||
|
|
function capturedAudioDefaults(): Record<string, unknown> {
|
||
|
|
const RoomConstructor = vi.mocked(LivekitRoom);
|
||
|
|
const options = RoomConstructor.mock.calls.at(-1)?.[0];
|
||
|
|
return (options?.audioCaptureDefaults ?? {}) as Record<string, unknown>;
|
||
|
|
}
|
||
|
|
|
||
|
|
beforeEach(() => {
|
||
|
|
testScope = new ObservableScope();
|
||
|
|
echoCancellationSetting.setValue(true);
|
||
|
|
noiseSuppressionSetting.setValue(true);
|
||
|
|
autoGainControlSetting.setValue(true);
|
||
|
|
});
|
||
|
|
|
||
|
|
afterEach(() => {
|
||
|
|
testScope.end();
|
||
|
|
vi.mocked(LivekitRoom).mockClear();
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("[lotus] audio-capture URL param overrides", () => {
|
||
|
|
test("with params defaulted to true, the Settings govern (upstream behaviour)", () => {
|
||
|
|
getUrlParams.mockReturnValue({
|
||
|
|
echoCancellation: true,
|
||
|
|
noiseSuppression: true,
|
||
|
|
autoGainControl: true,
|
||
|
|
});
|
||
|
|
autoGainControlSetting.setValue(false);
|
||
|
|
createRoom();
|
||
|
|
expect(capturedAudioDefaults()).toMatchObject({
|
||
|
|
echoCancellation: true,
|
||
|
|
noiseSuppression: true,
|
||
|
|
autoGainControl: false,
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test("host-passed noiseSuppression=false / autoGainControl=false force OFF regardless of Settings", () => {
|
||
|
|
getUrlParams.mockReturnValue({
|
||
|
|
echoCancellation: true,
|
||
|
|
noiseSuppression: false,
|
||
|
|
autoGainControl: false,
|
||
|
|
});
|
||
|
|
createRoom();
|
||
|
|
expect(capturedAudioDefaults()).toMatchObject({
|
||
|
|
echoCancellation: true,
|
||
|
|
noiseSuppression: false,
|
||
|
|
autoGainControl: false,
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
test("a URL param cannot force a constraint ON when the Setting is off", () => {
|
||
|
|
getUrlParams.mockReturnValue({
|
||
|
|
echoCancellation: true,
|
||
|
|
noiseSuppression: true,
|
||
|
|
autoGainControl: true,
|
||
|
|
});
|
||
|
|
echoCancellationSetting.setValue(false);
|
||
|
|
createRoom();
|
||
|
|
expect(capturedAudioDefaults()).toMatchObject({ echoCancellation: false });
|
||
|
|
});
|
||
|
|
});
|