diff --git a/src/room/CallEventAudioRenderer.test.tsx b/src/room/CallEventAudioRenderer.test.tsx
index 281bbafd..ff9fa6d4 100644
--- a/src/room/CallEventAudioRenderer.test.tsx
+++ b/src/room/CallEventAudioRenderer.test.tsx
@@ -31,6 +31,7 @@ import {
aliceRtcMember,
bobRtcMember,
local,
+ localRtcMember,
} from "../utils/test-fixtures";
vitest.mock("../useAudioContext");
@@ -66,7 +67,7 @@ beforeEach(() => {
* a noise every time.
*/
test("plays one sound when entering a call", () => {
- const { vm, remoteRtcMemberships$ } = getBasicCallViewModelEnvironment([
+ const { vm, rtcMemberships$ } = getBasicCallViewModelEnvironment([
local,
alice,
]);
@@ -74,47 +75,47 @@ test("plays one sound when entering a call", () => {
// Joining a call usually means remote participants are added later.
act(() => {
- remoteRtcMemberships$.next([aliceRtcMember, bobRtcMember]);
+ rtcMemberships$.next([localRtcMember, aliceRtcMember, bobRtcMember]);
});
expect(playSound).toHaveBeenCalledOnce();
});
test("plays a sound when a user joins", () => {
- const { vm, remoteRtcMemberships$ } = getBasicCallViewModelEnvironment([
+ const { vm, rtcMemberships$ } = getBasicCallViewModelEnvironment([
local,
alice,
]);
render();
act(() => {
- remoteRtcMemberships$.next([aliceRtcMember, bobRtcMember]);
+ rtcMemberships$.next([localRtcMember, aliceRtcMember, bobRtcMember]);
});
// Play a sound when joining a call.
expect(playSound).toBeCalledWith("join");
});
test("plays a sound when a user leaves", () => {
- const { vm, remoteRtcMemberships$ } = getBasicCallViewModelEnvironment([
+ const { vm, rtcMemberships$ } = getBasicCallViewModelEnvironment([
local,
alice,
]);
render();
act(() => {
- remoteRtcMemberships$.next([]);
+ rtcMemberships$.next([localRtcMember]);
});
expect(playSound).toBeCalledWith("left");
});
test("plays no sound when the participant list is more than the maximum size", () => {
- const mockRtcMemberships: CallMembership[] = [];
+ const mockRtcMemberships: CallMembership[] = [localRtcMember];
for (let i = 0; i < MAX_PARTICIPANT_COUNT_FOR_SOUND; i++) {
mockRtcMemberships.push(
mockRtcMembership(`@user${i}:example.org`, `DEVICE${i}`),
);
}
- const { vm, remoteRtcMemberships$ } = getBasicCallViewModelEnvironment(
+ const { vm, rtcMemberships$ } = getBasicCallViewModelEnvironment(
[local, alice],
mockRtcMemberships,
);
@@ -122,8 +123,8 @@ test("plays no sound when the participant list is more than the maximum size", (
render();
expect(playSound).not.toBeCalled();
act(() => {
- remoteRtcMemberships$.next(
- mockRtcMemberships.slice(0, MAX_PARTICIPANT_COUNT_FOR_SOUND - 1),
+ rtcMemberships$.next(
+ mockRtcMemberships.slice(0, MAX_PARTICIPANT_COUNT_FOR_SOUND),
);
});
expect(playSound).toBeCalledWith("left");
diff --git a/src/room/GroupCallView.test.tsx b/src/room/GroupCallView.test.tsx
index 4eb32af0..12dfdf61 100644
--- a/src/room/GroupCallView.test.tsx
+++ b/src/room/GroupCallView.test.tsx
@@ -137,11 +137,9 @@ function createGroupCallView(
getJoinRule: () => JoinRule.Invite,
} as Partial as RoomState,
});
- const rtcSession = new MockRTCSession(
- room,
- localRtcMember,
- [],
- ).withMemberships(constant([]));
+ const rtcSession = new MockRTCSession(room, []).withMemberships(
+ constant([localRtcMember]),
+ );
rtcSession.joined = joined;
const muteState = {
audio: { enabled: false },
diff --git a/src/state/CallViewModel.test.ts b/src/state/CallViewModel.test.ts
index c46b37c6..6dacb370 100644
--- a/src/state/CallViewModel.test.ts
+++ b/src/state/CallViewModel.test.ts
@@ -248,11 +248,7 @@ function withCallViewModel(
} as Partial as MatrixClient,
getMember: (userId) => roomMembers.get(userId) ?? null,
});
- const rtcSession = new MockRTCSession(
- room,
- localRtcMember,
- [],
- ).withMemberships(rtcMembers$);
+ const rtcSession = new MockRTCSession(room, []).withMemberships(rtcMembers$);
const participantsSpy = vi
.spyOn(ComponentsCore, "connectedParticipantsObserver")
.mockReturnValue(remoteParticipants$);
@@ -322,7 +318,7 @@ test("participants are retained during a focus switch", () => {
a: [aliceParticipant, bobParticipant],
b: [],
}),
- constant([aliceRtcMember, bobRtcMember]),
+ constant([localRtcMember, aliceRtcMember, bobRtcMember]),
behavior(connectionInputMarbles, {
c: ConnectionState.Connected,
s: ECAddonConnectionState.ECSwitchingFocus,
@@ -365,7 +361,7 @@ test("screen sharing activates spotlight layout", () => {
c: [aliceSharingScreen, bobSharingScreen],
d: [aliceParticipant, bobSharingScreen],
}),
- constant([aliceRtcMember, bobRtcMember]),
+ constant([localRtcMember, aliceRtcMember, bobRtcMember]),
of(ConnectionState.Connected),
new Map(),
mockMediaDevices({}),
@@ -445,7 +441,7 @@ test("participants stay in the same order unless to appear/disappear", () => {
withCallViewModel(
constant([aliceParticipant, bobParticipant, daveParticipant]),
- constant([aliceRtcMember, bobRtcMember, daveRtcMember]),
+ constant([localRtcMember, aliceRtcMember, bobRtcMember, daveRtcMember]),
of(ConnectionState.Connected),
new Map([
[
@@ -512,7 +508,7 @@ test("participants adjust order when space becomes constrained", () => {
withCallViewModel(
constant([aliceParticipant, bobParticipant, daveParticipant]),
- constant([aliceRtcMember, bobRtcMember, daveRtcMember]),
+ constant([localRtcMember, aliceRtcMember, bobRtcMember, daveRtcMember]),
of(ConnectionState.Connected),
new Map([
[
@@ -571,7 +567,7 @@ test("spotlight speakers swap places", () => {
withCallViewModel(
constant([aliceParticipant, bobParticipant, daveParticipant]),
- constant([aliceRtcMember, bobRtcMember, daveRtcMember]),
+ constant([localRtcMember, aliceRtcMember, bobRtcMember, daveRtcMember]),
of(ConnectionState.Connected),
new Map([
[
@@ -630,7 +626,7 @@ test("layout enters picture-in-picture mode when requested", () => {
withCallViewModel(
constant([aliceParticipant, bobParticipant]),
- constant([aliceRtcMember, bobRtcMember]),
+ constant([localRtcMember, aliceRtcMember, bobRtcMember]),
of(ConnectionState.Connected),
new Map(),
mockMediaDevices({}),
@@ -672,7 +668,7 @@ test("spotlight remembers whether it's expanded", () => {
withCallViewModel(
constant([aliceParticipant, bobParticipant]),
- constant([aliceRtcMember, bobRtcMember]),
+ constant([localRtcMember, aliceRtcMember, bobRtcMember]),
of(ConnectionState.Connected),
new Map(),
mockMediaDevices({}),
@@ -736,11 +732,11 @@ test("participants must have a MatrixRTCSession to be visible", () => {
e: [aliceParticipant, daveParticipant, bobSharingScreen],
}),
behavior(scenarioInputMarbles, {
- a: [],
- b: [],
- c: [aliceRtcMember],
- d: [aliceRtcMember, daveRtcMember],
- e: [aliceRtcMember, daveRtcMember],
+ a: [localRtcMember],
+ b: [localRtcMember],
+ c: [localRtcMember, aliceRtcMember],
+ d: [localRtcMember, aliceRtcMember, daveRtcMember],
+ e: [localRtcMember, aliceRtcMember, daveRtcMember],
}),
of(ConnectionState.Connected),
new Map(),
@@ -786,7 +782,7 @@ test("shows participants without MatrixRTCSession when enabled in settings", ()
b: [aliceParticipant],
c: [aliceParticipant, bobParticipant],
}),
- constant([]), // No one joins the MatrixRTC session
+ constant([localRtcMember]), // No one else joins the MatrixRTC session
of(ConnectionState.Connected),
new Map(),
mockMediaDevices({}),
@@ -830,10 +826,10 @@ it("should show at least one tile per MatrixRTCSession", () => {
withCallViewModel(
constant([]),
behavior(scenarioInputMarbles, {
- a: [],
- b: [aliceRtcMember],
- c: [aliceRtcMember, daveRtcMember],
- d: [daveRtcMember],
+ a: [localRtcMember],
+ b: [localRtcMember, aliceRtcMember],
+ c: [localRtcMember, aliceRtcMember, daveRtcMember],
+ d: [localRtcMember, daveRtcMember],
}),
of(ConnectionState.Connected),
new Map(),
@@ -878,11 +874,16 @@ test("should disambiguate users with the same displayname", () => {
withCallViewModel(
constant([]),
behavior(scenarioInputMarbles, {
- a: [],
- b: [aliceRtcMember],
- c: [aliceRtcMember, aliceDoppelgangerRtcMember],
- d: [aliceRtcMember, aliceDoppelgangerRtcMember, bobRtcMember],
- e: [aliceDoppelgangerRtcMember, bobRtcMember],
+ a: [localRtcMember],
+ b: [localRtcMember, aliceRtcMember],
+ c: [localRtcMember, aliceRtcMember, aliceDoppelgangerRtcMember],
+ d: [
+ localRtcMember,
+ aliceRtcMember,
+ aliceDoppelgangerRtcMember,
+ bobRtcMember,
+ ],
+ e: [localRtcMember, aliceDoppelgangerRtcMember, bobRtcMember],
}),
of(ConnectionState.Connected),
new Map(),
@@ -928,8 +929,8 @@ test("should disambiguate users with invisible characters", () => {
withCallViewModel(
constant([]),
behavior(scenarioInputMarbles, {
- a: [],
- b: [bobRtcMember, bobZeroWidthSpaceRtcMember],
+ a: [localRtcMember],
+ b: [localRtcMember, bobRtcMember, bobZeroWidthSpaceRtcMember],
}),
of(ConnectionState.Connected),
new Map(),
@@ -961,8 +962,8 @@ test("should strip RTL characters from displayname", () => {
withCallViewModel(
constant([]),
behavior(scenarioInputMarbles, {
- a: [],
- b: [daveRtcMember, daveRTLRtcMember],
+ a: [localRtcMember],
+ b: [localRtcMember, daveRtcMember, daveRTLRtcMember],
}),
of(ConnectionState.Connected),
new Map(),
@@ -992,7 +993,7 @@ it("should rank raised hands above video feeds and below speakers and presenters
withCallViewModel(
constant([aliceParticipant, bobParticipant]),
- constant([aliceRtcMember, bobRtcMember]),
+ constant([localRtcMember, aliceRtcMember, bobRtcMember]),
of(ConnectionState.Connected),
new Map(),
mockMediaDevices({}),
@@ -1077,10 +1078,10 @@ function rtcMemberJoinLeave$(
) => Observable,
): Observable {
return hot("a-b-c-d", {
- a: [], // Start empty
- b: [aliceRtcMember], // Alice joins
- c: [aliceRtcMember], // Alice still there
- d: [], // Alice leaves
+ a: [localRtcMember], // Start empty
+ b: [localRtcMember, aliceRtcMember], // Alice joins
+ c: [localRtcMember, aliceRtcMember], // Alice still there
+ d: [localRtcMember], // Alice leaves
});
}
@@ -1089,7 +1090,7 @@ test("allOthersLeft$ emits only when someone joined and then all others left", (
// Test scenario 1: No one ever joins - should only emit initial false and never emit again
withCallViewModel(
scope.behavior(nooneEverThere$(hot), []),
- scope.behavior(nooneEverThere$(hot), []),
+ constant([localRtcMember]),
of(ConnectionState.Connected),
new Map(),
mockMediaDevices({}),
@@ -1237,7 +1238,7 @@ test("audio output changes when toggling earpiece mode", () => {
withCallViewModel(
constant([]),
- constant([]),
+ constant([localRtcMember]),
of(ConnectionState.Connected),
new Map(),
devices,
diff --git a/src/utils/test-viewmodel.ts b/src/utils/test-viewmodel.ts
index 7978bd96..ce7082c1 100644
--- a/src/utils/test-viewmodel.ts
+++ b/src/utils/test-viewmodel.ts
@@ -34,11 +34,11 @@ import { type RaisedHandInfo, type ReactionInfo } from "../reactions";
export function getBasicRTCSession(
members: RoomMember[],
- initialRemoteRtcMemberships: CallMembership[] = [aliceRtcMember],
+ initialRtcMemberships: CallMembership[] = [localRtcMember, aliceRtcMember],
): {
rtcSession: MockRTCSession;
matrixRoom: Room;
- remoteRtcMemberships$: BehaviorSubject;
+ rtcMemberships$: BehaviorSubject;
} {
const matrixRoomId = "!myRoomId:example.com";
const matrixRoomMembers = new Map(members.map((p) => [p.userId, p]));
@@ -92,41 +92,40 @@ export function getBasicRTCSession(
),
});
- const remoteRtcMemberships$ = new BehaviorSubject(
- initialRemoteRtcMemberships,
+ const rtcMemberships$ = new BehaviorSubject(
+ initialRtcMemberships,
);
- const rtcSession = new MockRTCSession(
- matrixRoom,
- localRtcMember,
- ).withMemberships(remoteRtcMemberships$);
+ const rtcSession = new MockRTCSession(matrixRoom).withMemberships(
+ rtcMemberships$,
+ );
return {
rtcSession,
matrixRoom,
- remoteRtcMemberships$,
+ rtcMemberships$,
};
}
/**
* Construct a basic CallViewModel to test components that make use of it.
* @param members
- * @param initialRemoteRtcMemberships
+ * @param initialRtcMemberships
* @returns
*/
export function getBasicCallViewModelEnvironment(
members: RoomMember[],
- initialRemoteRtcMemberships: CallMembership[] = [aliceRtcMember],
+ initialRtcMemberships: CallMembership[] = [localRtcMember, aliceRtcMember],
): {
vm: CallViewModel;
- remoteRtcMemberships$: BehaviorSubject;
+ rtcMemberships$: BehaviorSubject;
rtcSession: MockRTCSession;
handRaisedSubject$: BehaviorSubject>;
reactionsSubject$: BehaviorSubject>;
} {
- const { rtcSession, matrixRoom, remoteRtcMemberships$ } = getBasicRTCSession(
+ const { rtcSession, matrixRoom, rtcMemberships$ } = getBasicRTCSession(
members,
- initialRemoteRtcMemberships,
+ initialRtcMemberships,
);
const handRaisedSubject$ = new BehaviorSubject({});
const reactionsSubject$ = new BehaviorSubject({});
@@ -150,7 +149,7 @@ export function getBasicCallViewModelEnvironment(
);
return {
vm,
- remoteRtcMemberships$,
+ rtcMemberships$,
rtcSession,
handRaisedSubject$: handRaisedSubject$,
reactionsSubject$: reactionsSubject$,
diff --git a/src/utils/test.ts b/src/utils/test.ts
index b49b5931..dc4f1922 100644
--- a/src/utils/test.ts
+++ b/src/utils/test.ts
@@ -326,7 +326,6 @@ export class MockRTCSession extends TypedEventEmitter<
public constructor(
public readonly room: Room,
- private localMembership: CallMembership,
public memberships: CallMembership[] = [],
) {
super();
@@ -342,10 +341,12 @@ export class MockRTCSession extends TypedEventEmitter<
): MockRTCSession {
rtcMembers$.subscribe((m) => {
const old = this.memberships;
- // always prepend the local participant
- const updated = [this.localMembership, ...(m as CallMembership[])];
- this.memberships = updated;
- this.emit(MatrixRTCSessionEvent.MembershipsChanged, old, updated);
+ this.memberships = m as CallMembership[];
+ this.emit(
+ MatrixRTCSessionEvent.MembershipsChanged,
+ old,
+ this.memberships,
+ );
});
return this;