mirror of
https://github.com/vector-im/element-call.git
synced 2026-09-01 21:25:18 +00:00
add tests
This commit is contained in:
@@ -32,7 +32,11 @@ import {
|
|||||||
} from "matrix-js-sdk/lib/matrixrtc";
|
} from "matrix-js-sdk/lib/matrixrtc";
|
||||||
import { deepCompare } from "matrix-js-sdk/lib/utils";
|
import { deepCompare } from "matrix-js-sdk/lib/utils";
|
||||||
|
|
||||||
import { CallViewModel, type Layout } from "./CallViewModel";
|
import {
|
||||||
|
CallViewModel,
|
||||||
|
type CallViewModelOptions,
|
||||||
|
type Layout,
|
||||||
|
} from "./CallViewModel";
|
||||||
import {
|
import {
|
||||||
mockLivekitRoom,
|
mockLivekitRoom,
|
||||||
mockLocalParticipant,
|
mockLocalParticipant,
|
||||||
@@ -231,6 +235,10 @@ function withCallViewModel(
|
|||||||
vm: CallViewModel,
|
vm: CallViewModel,
|
||||||
subjects: { raisedHands$: BehaviorSubject<Record<string, RaisedHandInfo>> },
|
subjects: { raisedHands$: BehaviorSubject<Record<string, RaisedHandInfo>> },
|
||||||
) => void,
|
) => void,
|
||||||
|
options: CallViewModelOptions = {
|
||||||
|
encryptionSystem: { kind: E2eeType.PER_PARTICIPANT },
|
||||||
|
autoLeaveWhenOthersLeft: false,
|
||||||
|
},
|
||||||
): void {
|
): void {
|
||||||
const room = mockMatrixRoom({
|
const room = mockMatrixRoom({
|
||||||
client: {
|
client: {
|
||||||
@@ -281,9 +289,7 @@ function withCallViewModel(
|
|||||||
rtcSession as unknown as MatrixRTCSession,
|
rtcSession as unknown as MatrixRTCSession,
|
||||||
liveKitRoom,
|
liveKitRoom,
|
||||||
mediaDevices,
|
mediaDevices,
|
||||||
{
|
options,
|
||||||
encryptionSystem: { kind: E2eeType.PER_PARTICIPANT },
|
|
||||||
},
|
|
||||||
connectionState$,
|
connectionState$,
|
||||||
raisedHands$,
|
raisedHands$,
|
||||||
new BehaviorSubject({}),
|
new BehaviorSubject({}),
|
||||||
@@ -1037,6 +1043,137 @@ it("should rank raised hands above video feeds and below speakers and presenters
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function nooneEverThere$<T>(
|
||||||
|
hot: (marbles: string, values: Record<string, T[]>) => Observable<T[]>,
|
||||||
|
): Observable<T[]> {
|
||||||
|
return hot("a-b-c-d", {
|
||||||
|
a: [], // Start empty
|
||||||
|
b: [], // Alice joins
|
||||||
|
c: [], // Alice still there
|
||||||
|
d: [], // Alice leaves
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function participantJoinLeave$(
|
||||||
|
hot: (
|
||||||
|
marbles: string,
|
||||||
|
values: Record<string, RemoteParticipant[]>,
|
||||||
|
) => Observable<RemoteParticipant[]>,
|
||||||
|
): Observable<RemoteParticipant[]> {
|
||||||
|
return hot("a-b-c-d", {
|
||||||
|
a: [], // Start empty
|
||||||
|
b: [aliceParticipant], // Alice joins
|
||||||
|
c: [aliceParticipant], // Alice still there
|
||||||
|
d: [], // Alice leaves
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function rtcMemberJoinLeave$(
|
||||||
|
hot: (
|
||||||
|
marbles: string,
|
||||||
|
values: Record<string, CallMembership[]>,
|
||||||
|
) => Observable<CallMembership[]>,
|
||||||
|
): Observable<CallMembership[]> {
|
||||||
|
return hot("a-b-c-d", {
|
||||||
|
a: [], // Start empty
|
||||||
|
b: [aliceRtcMember], // Alice joins
|
||||||
|
c: [aliceRtcMember], // Alice still there
|
||||||
|
d: [], // Alice leaves
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
test("allOthersLeft$ emits only when someone joined and then all others left", () => {
|
||||||
|
withTestScheduler(({ hot, expectObservable }) => {
|
||||||
|
// Test scenario 1: No one ever joins - should only emit initial false and never emit again
|
||||||
|
withCallViewModel(
|
||||||
|
nooneEverThere$(hot),
|
||||||
|
nooneEverThere$(hot),
|
||||||
|
of(ConnectionState.Connected),
|
||||||
|
new Map(),
|
||||||
|
mockMediaDevices({}),
|
||||||
|
(vm) => {
|
||||||
|
expectObservable(vm.allOthersLeft$).toBe("n------", { n: false });
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("allOthersLeft$ emits true when someone joined and then all others left", () => {
|
||||||
|
withTestScheduler(({ hot, expectObservable }) => {
|
||||||
|
withCallViewModel(
|
||||||
|
participantJoinLeave$(hot),
|
||||||
|
rtcMemberJoinLeave$(hot),
|
||||||
|
of(ConnectionState.Connected),
|
||||||
|
new Map(),
|
||||||
|
mockMediaDevices({}),
|
||||||
|
(vm) => {
|
||||||
|
expectObservable(vm.allOthersLeft$).toBe(
|
||||||
|
"n-----u", // false initially, then at frame 6: true then false emissions in same frame
|
||||||
|
{ n: false, u: true }, // map(() => {})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("autoLeaveWhenOthersLeft$ emits only when autoLeaveWhenOthersLeft option is enabled", () => {
|
||||||
|
withTestScheduler(({ hot, expectObservable }) => {
|
||||||
|
withCallViewModel(
|
||||||
|
participantJoinLeave$(hot),
|
||||||
|
rtcMemberJoinLeave$(hot),
|
||||||
|
of(ConnectionState.Connected),
|
||||||
|
new Map(),
|
||||||
|
mockMediaDevices({}),
|
||||||
|
(vm) => {
|
||||||
|
expectObservable(vm.autoLeaveWhenOthersLeft$).toBe(
|
||||||
|
"------e", // false initially, then at frame 6: true then false emissions in same frame
|
||||||
|
{ e: undefined },
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
autoLeaveWhenOthersLeft: true,
|
||||||
|
encryptionSystem: { kind: E2eeType.PER_PARTICIPANT },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("autoLeaveWhenOthersLeft$ never emits autoLeaveWhenOthersLeft option is enabled but noone is there", () => {
|
||||||
|
withTestScheduler(({ hot, expectObservable }) => {
|
||||||
|
withCallViewModel(
|
||||||
|
nooneEverThere$(hot),
|
||||||
|
nooneEverThere$(hot),
|
||||||
|
of(ConnectionState.Connected),
|
||||||
|
new Map(),
|
||||||
|
mockMediaDevices({}),
|
||||||
|
(vm) => {
|
||||||
|
expectObservable(vm.autoLeaveWhenOthersLeft$).toBe(
|
||||||
|
"-------", // false initially, then at frame 6: true then false emissions in same frame
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
autoLeaveWhenOthersLeft: true,
|
||||||
|
encryptionSystem: { kind: E2eeType.PER_PARTICIPANT },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("autoLeaveWhenOthersLeft$ emits when autoLeaveWhenOthersLeft option is enabled and all others left", () => {
|
||||||
|
withTestScheduler(({ hot, expectObservable }) => {
|
||||||
|
withCallViewModel(
|
||||||
|
participantJoinLeave$(hot),
|
||||||
|
rtcMemberJoinLeave$(hot),
|
||||||
|
of(ConnectionState.Connected),
|
||||||
|
new Map(),
|
||||||
|
mockMediaDevices({}),
|
||||||
|
(vm) => {
|
||||||
|
expectObservable(vm.autoLeaveWhenOthersLeft$).toBe("-------");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test("audio output changes when toggling earpiece mode", () => {
|
test("audio output changes when toggling earpiece mode", () => {
|
||||||
withTestScheduler(({ schedule, expectObservable }) => {
|
withTestScheduler(({ schedule, expectObservable }) => {
|
||||||
getUrlParams.mockReturnValue({ controlledAudioDevices: true });
|
getUrlParams.mockReturnValue({ controlledAudioDevices: true });
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ import { calculateDisplayName, shouldDisambiguate } from "../utils/displayname";
|
|||||||
import { type MediaDevices } from "./MediaDevices";
|
import { type MediaDevices } from "./MediaDevices";
|
||||||
import { type Behavior } from "./Behavior";
|
import { type Behavior } from "./Behavior";
|
||||||
|
|
||||||
interface CallViewModelOptions {
|
export interface CallViewModelOptions {
|
||||||
encryptionSystem: EncryptionSystem;
|
encryptionSystem: EncryptionSystem;
|
||||||
autoLeaveWhenOthersLeft?: boolean;
|
autoLeaveWhenOthersLeft?: boolean;
|
||||||
}
|
}
|
||||||
@@ -741,7 +741,10 @@ export class CallViewModel extends ViewModel {
|
|||||||
);
|
);
|
||||||
|
|
||||||
public readonly allOthersLeft$ = this.memberChanges$.pipe(
|
public readonly allOthersLeft$ = this.memberChanges$.pipe(
|
||||||
map(({ ids, left }) => ids.length === 0 && left.length > 0),
|
map(
|
||||||
|
({ ids, left }) =>
|
||||||
|
ids.length === 1 && ids.includes("local:0") && left.length > 0,
|
||||||
|
),
|
||||||
startWith(false),
|
startWith(false),
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user