Test CallViewModel in all MatrixRTC modes

This commit is contained in:
Robin
2025-12-08 22:42:57 -05:00
parent 2986f90a5f
commit 5a9a62039c
3 changed files with 165 additions and 140 deletions

View File

@@ -60,7 +60,8 @@ import {
import { MediaDevices } from "../MediaDevices.ts"; import { MediaDevices } from "../MediaDevices.ts";
import { getValue } from "../../utils/observable.ts"; import { getValue } from "../../utils/observable.ts";
import { type Behavior, constant } from "../Behavior.ts"; import { type Behavior, constant } from "../Behavior.ts";
import { withCallViewModel } from "./CallViewModelTestUtils.ts"; import { withCallViewModel as withCallViewModelInMode } from "./CallViewModelTestUtils.ts";
import { MatrixRTCMode } from "../../settings/settings.ts";
vi.mock("rxjs", async (importOriginal) => ({ vi.mock("rxjs", async (importOriginal) => ({
...(await importOriginal()), ...(await importOriginal()),
@@ -229,7 +230,13 @@ function mockRingEvent(
// need a value to fill in for them when emitting notifications // need a value to fill in for them when emitting notifications
const mockLegacyRingEvent = {} as { event_id: string } & ICallNotifyContent; const mockLegacyRingEvent = {} as { event_id: string } & ICallNotifyContent;
describe("CallViewModel", () => { describe.each([
[MatrixRTCMode.Legacy],
[MatrixRTCMode.Compatibil],
[MatrixRTCMode.Matrix_2_0],
])("CallViewModel (%s mode)", (mode) => {
const withCallViewModel = withCallViewModelInMode(mode);
test("participants are retained during a focus switch", () => { test("participants are retained during a focus switch", () => {
withTestScheduler(({ behavior, expectObservable }) => { withTestScheduler(({ behavior, expectObservable }) => {
// Participants disappear on frame 2 and come back on frame 3 // Participants disappear on frame 2 and come back on frame 3

View File

@@ -53,6 +53,7 @@ import {
import { type Behavior, constant } from "../Behavior"; import { type Behavior, constant } from "../Behavior";
import { type ProcessorState } from "../../livekit/TrackProcessorContext"; import { type ProcessorState } from "../../livekit/TrackProcessorContext";
import { type MediaDevices } from "../MediaDevices"; import { type MediaDevices } from "../MediaDevices";
import { type MatrixRTCMode } from "../../settings/settings";
mockConfig({ mockConfig({
livekit: { livekit_service_url: "http://my-default-service-url.com" }, livekit: { livekit_service_url: "http://my-default-service-url.com" },
@@ -80,7 +81,8 @@ export interface CallViewModelInputs {
const localParticipant = mockLocalParticipant({ identity: "" }); const localParticipant = mockLocalParticipant({ identity: "" });
export function withCallViewModel( export function withCallViewModel(mode: MatrixRTCMode) {
return (
{ {
remoteParticipants$ = constant([]), remoteParticipants$ = constant([]),
rtcMembers$ = constant([localRtcMember]), rtcMembers$ = constant([localRtcMember]),
@@ -95,14 +97,16 @@ export function withCallViewModel(
continuation: ( continuation: (
vm: CallViewModel, vm: CallViewModel,
rtcSession: MockRTCSession, rtcSession: MockRTCSession,
subjects: { raisedHands$: BehaviorSubject<Record<string, RaisedHandInfo>> }, subjects: {
raisedHands$: BehaviorSubject<Record<string, RaisedHandInfo>>;
},
setSyncState: (value: SyncState) => void, setSyncState: (value: SyncState) => void,
) => void, ) => void,
options: CallViewModelOptions = { options: CallViewModelOptions = {
encryptionSystem: { kind: E2eeType.PER_PARTICIPANT }, encryptionSystem: { kind: E2eeType.PER_PARTICIPANT },
autoLeaveWhenOthersLeft: false, autoLeaveWhenOthersLeft: false,
}, },
): void { ): void => {
let syncState = initialSyncState; let syncState = initialSyncState;
const setSyncState = (value: SyncState): void => { const setSyncState = (value: SyncState): void => {
const prev = syncState; const prev = syncState;
@@ -130,7 +134,9 @@ export function withCallViewModel(
getMembers: () => Array.from(roomMembers.values()), getMembers: () => Array.from(roomMembers.values()),
getMembersWithMembership: () => Array.from(roomMembers.values()), getMembersWithMembership: () => Array.from(roomMembers.values()),
}); });
const rtcSession = new MockRTCSession(room, []).withMemberships(rtcMembers$); const rtcSession = new MockRTCSession(room, []).withMemberships(
rtcMembers$,
);
const participantsSpy = vi const participantsSpy = vi
.spyOn(ComponentsCore, "connectedParticipantsObserver") .spyOn(ComponentsCore, "connectedParticipantsObserver")
.mockReturnValue(remoteParticipants$); .mockReturnValue(remoteParticipants$);
@@ -157,7 +163,9 @@ export function withCallViewModel(
.spyOn(ComponentsCore, "roomEventSelector") .spyOn(ComponentsCore, "roomEventSelector")
.mockImplementation((_room, _eventType) => of()); .mockImplementation((_room, _eventType) => of());
const muteStates = mockMuteStates(); const muteStates = mockMuteStates();
const raisedHands$ = new BehaviorSubject<Record<string, RaisedHandInfo>>({}); const raisedHands$ = new BehaviorSubject<Record<string, RaisedHandInfo>>(
{},
);
const reactions$ = new BehaviorSubject<Record<string, ReactionInfo>>({}); const reactions$ = new BehaviorSubject<Record<string, ReactionInfo>>({});
const vm = createCallViewModel$( const vm = createCallViewModel$(
@@ -176,6 +184,7 @@ export function withCallViewModel(
}), }),
connectionState$, connectionState$,
windowSize$, windowSize$,
matrixRTCMode$: constant(mode),
}, },
raisedHands$, raisedHands$,
reactions$, reactions$,
@@ -193,4 +202,5 @@ export function withCallViewModel(
}); });
continuation(vm, rtcSession, { raisedHands$: raisedHands$ }, setSyncState); continuation(vm, rtcSession, { raisedHands$: raisedHands$ }, setSyncState);
};
} }

View File

@@ -15,6 +15,7 @@ import { constant } from "./Behavior.ts";
import { aliceParticipant, localRtcMember } from "../utils/test-fixtures.ts"; import { aliceParticipant, localRtcMember } from "../utils/test-fixtures.ts";
import { ElementWidgetActions, widget } from "../widget.ts"; import { ElementWidgetActions, widget } from "../widget.ts";
import { E2eeType } from "../e2ee/e2eeType.ts"; import { E2eeType } from "../e2ee/e2eeType.ts";
import { MatrixRTCMode } from "../settings/settings.ts";
vi.mock("@livekit/components-core", { spy: true }); vi.mock("@livekit/components-core", { spy: true });
@@ -34,9 +35,15 @@ vi.mock("../widget", () => ({
}, },
})); }));
it("expect leave when ElementWidgetActions.HangupCall is called", async () => { it.each([
[MatrixRTCMode.Legacy],
[MatrixRTCMode.Compatibil],
[MatrixRTCMode.Matrix_2_0],
])(
"expect leave when ElementWidgetActions.HangupCall is called (%s mode)",
async (mode) => {
const pr = Promise.withResolvers<string>(); const pr = Promise.withResolvers<string>();
withCallViewModel( withCallViewModel(mode)(
{ {
remoteParticipants$: constant([aliceParticipant]), remoteParticipants$: constant([aliceParticipant]),
rtcMembers$: constant([localRtcMember]), rtcMembers$: constant([localRtcMember]),
@@ -66,4 +73,5 @@ it("expect leave when ElementWidgetActions.HangupCall is called", async () => {
const source = await pr.promise; const source = await pr.promise;
expect(source).toBe("user"); expect(source).toBe("user");
}); },
);