You've already forked matrix-react-sdk
mirror of
https://github.com/matrix-org/matrix-react-sdk.git
synced 2025-08-07 21:23:00 +03:00
Use native js-sdk group call support (#9625)
* Use native js-sdk group call support Now that the js-sdk supports group calls natively, our group call implementation can be simplified a bit. Switching to the js-sdk implementation also brings the react-sdk up to date with recent MSC3401 changes, and adds support for joining calls from multiple devices. (So, the previous logic which sent to-device messages to prevent multi-device sessions is no longer necessary.) * Fix strings * Fix strict type errors
This commit is contained in:
@@ -121,7 +121,7 @@ describe("CallEvent", () => {
|
||||
|
||||
it("shows call details and connection controls if the call is loaded", async () => {
|
||||
jest.advanceTimersByTime(90000);
|
||||
call.participants = new Set([alice, bob]);
|
||||
call.participants = new Map([[alice, new Set(["a"])], [bob, new Set(["b"])]]);
|
||||
renderEvent();
|
||||
|
||||
screen.getByText("@alice:example.org started a video call");
|
||||
|
@@ -22,6 +22,7 @@ import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
|
||||
import { Widget } from "matrix-widget-api";
|
||||
|
||||
import type { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||
import type { ClientWidgetApi } from "matrix-widget-api";
|
||||
import {
|
||||
stubClient,
|
||||
@@ -74,7 +75,9 @@ describe("RoomTile", () => {
|
||||
setupAsyncStoreWithClient(WidgetMessagingStore.instance, client);
|
||||
|
||||
MockedCall.create(room, "1");
|
||||
call = CallStore.instance.getCall(room.roomId) as MockedCall;
|
||||
const maybeCall = CallStore.instance.getCall(room.roomId);
|
||||
if (!(maybeCall instanceof MockedCall)) throw new Error("Failed to create call");
|
||||
call = maybeCall;
|
||||
|
||||
widget = new Widget(call.widget);
|
||||
WidgetMessagingStore.instance.storeMessaging(widget, room.roomId, {
|
||||
@@ -123,19 +126,25 @@ describe("RoomTile", () => {
|
||||
});
|
||||
|
||||
it("tracks participants", () => {
|
||||
const alice = mkRoomMember(room.roomId, "@alice:example.org");
|
||||
const bob = mkRoomMember(room.roomId, "@bob:example.org");
|
||||
const carol = mkRoomMember(room.roomId, "@carol:example.org");
|
||||
const alice: [RoomMember, Set<string>] = [
|
||||
mkRoomMember(room.roomId, "@alice:example.org"), new Set(["a"]),
|
||||
];
|
||||
const bob: [RoomMember, Set<string>] = [
|
||||
mkRoomMember(room.roomId, "@bob:example.org"), new Set(["b1", "b2"]),
|
||||
];
|
||||
const carol: [RoomMember, Set<string>] = [
|
||||
mkRoomMember(room.roomId, "@carol:example.org"), new Set(["c"]),
|
||||
];
|
||||
|
||||
expect(screen.queryByLabelText(/participant/)).toBe(null);
|
||||
|
||||
act(() => { call.participants = new Set([alice]); });
|
||||
act(() => { call.participants = new Map([alice]); });
|
||||
expect(screen.getByLabelText("1 participant").textContent).toBe("1");
|
||||
|
||||
act(() => { call.participants = new Set([alice, bob, carol]); });
|
||||
expect(screen.getByLabelText("3 participants").textContent).toBe("3");
|
||||
act(() => { call.participants = new Map([alice, bob, carol]); });
|
||||
expect(screen.getByLabelText("4 participants").textContent).toBe("4");
|
||||
|
||||
act(() => { call.participants = new Set(); });
|
||||
act(() => { call.participants = new Map(); });
|
||||
expect(screen.queryByLabelText(/participant/)).toBe(null);
|
||||
});
|
||||
});
|
||||
|
@@ -131,7 +131,7 @@ describe("CallLobby", () => {
|
||||
|
||||
for (const [userId, avatar] of zip(userIds, avatars)) {
|
||||
fireEvent.focus(avatar!);
|
||||
screen.getByRole("tooltip", { name: userId });
|
||||
screen.getAllByRole("tooltip", { name: userId });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -139,15 +139,21 @@ describe("CallLobby", () => {
|
||||
expect(screen.queryByLabelText(/joined/)).toBe(null);
|
||||
expectAvatars([]);
|
||||
|
||||
act(() => { call.participants = new Set([alice]); });
|
||||
act(() => { call.participants = new Map([[alice, new Set(["a"])]]); });
|
||||
screen.getByText("1 person joined");
|
||||
expectAvatars([alice.userId]);
|
||||
|
||||
act(() => { call.participants = new Set([alice, bob, carol]); });
|
||||
screen.getByText("3 people joined");
|
||||
expectAvatars([alice.userId, bob.userId, carol.userId]);
|
||||
act(() => {
|
||||
call.participants = new Map([
|
||||
[alice, new Set(["a"])],
|
||||
[bob, new Set(["b1", "b2"])],
|
||||
[carol, new Set(["c"])],
|
||||
]);
|
||||
});
|
||||
screen.getByText("4 people joined");
|
||||
expectAvatars([alice.userId, bob.userId, bob.userId, carol.userId]);
|
||||
|
||||
act(() => { call.participants = new Set(); });
|
||||
act(() => { call.participants = new Map(); });
|
||||
expect(screen.queryByLabelText(/joined/)).toBe(null);
|
||||
expectAvatars([]);
|
||||
});
|
||||
@@ -166,7 +172,7 @@ describe("CallLobby", () => {
|
||||
SdkConfig.put({
|
||||
"element_call": { participant_limit: 2, url: "", use_exclusively: false, brand: "Element Call" },
|
||||
});
|
||||
call.participants = new Set([bob, carol]);
|
||||
call.participants = new Map([[bob, new Set("b")], [carol, new Set("c")]]);
|
||||
|
||||
await renderView();
|
||||
const connectSpy = jest.spyOn(call, "connect");
|
||||
|
@@ -68,7 +68,7 @@ describe("createRoom", () => {
|
||||
// widget should be immutable for admins
|
||||
expect(widgetPower).toBeGreaterThan(100);
|
||||
// and we should have been reset back to admin
|
||||
expect(client.setPowerLevel).toHaveBeenCalledWith(roomId, userId, 100, undefined);
|
||||
expect(client.setPowerLevel).toHaveBeenCalledWith(roomId, userId, 100, null);
|
||||
});
|
||||
|
||||
it("sets up Element video rooms correctly", async () => {
|
||||
@@ -98,7 +98,7 @@ describe("createRoom", () => {
|
||||
// call should be immutable for admins
|
||||
expect(callPower).toBeGreaterThan(100);
|
||||
// and we should have been reset back to admin
|
||||
expect(client.setPowerLevel).toHaveBeenCalledWith(roomId, userId, 100, undefined);
|
||||
expect(client.setPowerLevel).toHaveBeenCalledWith(roomId, userId, 100, null);
|
||||
});
|
||||
|
||||
it("doesn't create calls in non-video-rooms", async () => {
|
||||
|
@@ -18,17 +18,17 @@ import EventEmitter from "events";
|
||||
import { mocked } from "jest-mock";
|
||||
import { waitFor } from "@testing-library/react";
|
||||
import { RoomType } from "matrix-js-sdk/src/@types/event";
|
||||
import { ClientEvent, PendingEventOrdering } from "matrix-js-sdk/src/client";
|
||||
import { PendingEventOrdering } from "matrix-js-sdk/src/client";
|
||||
import { Room, RoomEvent } from "matrix-js-sdk/src/models/room";
|
||||
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
|
||||
import { Widget } from "matrix-widget-api";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { GroupCallIntent } from "matrix-js-sdk/src/webrtc/groupCall";
|
||||
|
||||
import type { Mocked } from "jest-mock";
|
||||
import type { MatrixClient, IMyDevice } from "matrix-js-sdk/src/client";
|
||||
import type { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||
import type { ClientWidgetApi } from "matrix-widget-api";
|
||||
import { JitsiCallMemberContent, ElementCallMemberContent, Layout } from "../../src/models/Call";
|
||||
import { JitsiCallMemberContent, Layout } from "../../src/models/Call";
|
||||
import { stubClient, mkEvent, mkRoomMember, setupAsyncStoreWithClient, mockPlatformPeg } from "../test-utils";
|
||||
import MediaDeviceHandler, { MediaDeviceKindEnum } from "../../src/MediaDeviceHandler";
|
||||
import { MatrixClientPeg } from "../../src/MatrixClientPeg";
|
||||
@@ -341,7 +341,7 @@ describe("JitsiCall", () => {
|
||||
});
|
||||
|
||||
it("tracks participants in room state", async () => {
|
||||
expect([...call.participants]).toEqual([]);
|
||||
expect(call.participants).toEqual(new Map());
|
||||
|
||||
// A participant with multiple devices (should only show up once)
|
||||
await client.sendStateEvent(
|
||||
@@ -361,10 +361,13 @@ describe("JitsiCall", () => {
|
||||
// Now, stub out client.sendStateEvent so we can test our local echo
|
||||
client.sendStateEvent.mockReset();
|
||||
await call.connect();
|
||||
expect([...call.participants]).toEqual([bob, alice]);
|
||||
expect(call.participants).toEqual(new Map([
|
||||
[alice, new Set(["alices_device"])],
|
||||
[bob, new Set(["bobweb", "bobdesktop"])],
|
||||
]));
|
||||
|
||||
await call.disconnect();
|
||||
expect([...call.participants]).toEqual([bob]);
|
||||
expect(call.participants).toEqual(new Map([[bob, new Set(["bobweb", "bobdesktop"])]]));
|
||||
});
|
||||
|
||||
it("updates room state when connecting and disconnecting", async () => {
|
||||
@@ -429,10 +432,10 @@ describe("JitsiCall", () => {
|
||||
await call.connect();
|
||||
await call.disconnect();
|
||||
expect(onParticipants.mock.calls).toEqual([
|
||||
[new Set([alice]), new Set()],
|
||||
[new Set([alice]), new Set([alice])],
|
||||
[new Set(), new Set([alice])],
|
||||
[new Set(), new Set()],
|
||||
[new Map([[alice, new Set(["alices_device"])]]), new Map()],
|
||||
[new Map([[alice, new Set(["alices_device"])]]), new Map([[alice, new Set(["alices_device"])]])],
|
||||
[new Map(), new Map([[alice, new Set(["alices_device"])]])],
|
||||
[new Map(), new Map()],
|
||||
]);
|
||||
|
||||
call.off(CallEvent.Participants, onParticipants);
|
||||
@@ -568,11 +571,11 @@ describe("ElementCall", () => {
|
||||
|
||||
it("ignores terminated calls", async () => {
|
||||
await ElementCall.create(room);
|
||||
const call = Call.get(room);
|
||||
if (!(call instanceof ElementCall)) throw new Error("Failed to create call");
|
||||
|
||||
// Terminate the call
|
||||
const [event] = room.currentState.getStateEvents(ElementCall.CALL_EVENT_TYPE.name);
|
||||
const content = { ...event.getContent(), "m.terminated": "Call ended" };
|
||||
await client.sendStateEvent(room.roomId, ElementCall.CALL_EVENT_TYPE.name, content, event.getStateKey()!);
|
||||
await call.groupCall.terminate();
|
||||
|
||||
expect(Call.get(room)).toBeNull();
|
||||
});
|
||||
@@ -599,8 +602,8 @@ describe("ElementCall", () => {
|
||||
|
||||
afterEach(() => cleanUpCallAndWidget(call, widget, audioMutedSpy, videoMutedSpy));
|
||||
|
||||
it("has intent m.prompt", () => {
|
||||
expect(call.groupCall.getContent()["m.intent"]).toBe("m.prompt");
|
||||
it("has prompt intent", () => {
|
||||
expect(call.groupCall.intent).toBe(GroupCallIntent.Prompt);
|
||||
});
|
||||
|
||||
it("connects muted", async () => {
|
||||
@@ -690,19 +693,18 @@ describe("ElementCall", () => {
|
||||
});
|
||||
|
||||
it("tracks participants in room state", async () => {
|
||||
expect([...call.participants]).toEqual([]);
|
||||
expect(call.participants).toEqual(new Map());
|
||||
|
||||
// A participant with multiple devices (should only show up once)
|
||||
await client.sendStateEvent(
|
||||
room.roomId,
|
||||
ElementCall.MEMBER_EVENT_TYPE.name,
|
||||
{
|
||||
"m.expires_ts": 1000 * 60 * 10,
|
||||
"m.calls": [{
|
||||
"m.call_id": call.groupCall.getStateKey()!,
|
||||
"m.call_id": call.groupCall.groupCallId,
|
||||
"m.devices": [
|
||||
{ device_id: "bobweb", session_id: "1", feeds: [] },
|
||||
{ device_id: "bobdesktop", session_id: "1", feeds: [] },
|
||||
{ device_id: "bobweb", session_id: "1", feeds: [], expires_ts: 1000 * 60 * 10 },
|
||||
{ device_id: "bobdesktop", session_id: "1", feeds: [], expires_ts: 1000 * 60 * 10 },
|
||||
],
|
||||
}],
|
||||
},
|
||||
@@ -713,11 +715,10 @@ describe("ElementCall", () => {
|
||||
room.roomId,
|
||||
ElementCall.MEMBER_EVENT_TYPE.name,
|
||||
{
|
||||
"m.expires_ts": -1000 * 60,
|
||||
"m.calls": [{
|
||||
"m.call_id": call.groupCall.getStateKey()!,
|
||||
"m.call_id": call.groupCall.groupCallId,
|
||||
"m.devices": [
|
||||
{ device_id: "carolandroid", session_id: "1", feeds: [] },
|
||||
{ device_id: "carolandroid", session_id: "1", feeds: [], expires_ts: -1000 * 60 },
|
||||
],
|
||||
}],
|
||||
},
|
||||
@@ -727,10 +728,13 @@ describe("ElementCall", () => {
|
||||
// Now, stub out client.sendStateEvent so we can test our local echo
|
||||
client.sendStateEvent.mockReset();
|
||||
await call.connect();
|
||||
expect([...call.participants]).toEqual([bob, alice]);
|
||||
expect(call.participants).toEqual(new Map([
|
||||
[alice, new Set(["alices_device"])],
|
||||
[bob, new Set(["bobweb", "bobdesktop"])],
|
||||
]));
|
||||
|
||||
await call.disconnect();
|
||||
expect([...call.participants]).toEqual([bob]);
|
||||
expect(call.participants).toEqual(new Map([[bob, new Set(["bobweb", "bobdesktop"])]]));
|
||||
});
|
||||
|
||||
it("tracks layout", async () => {
|
||||
@@ -783,9 +787,8 @@ describe("ElementCall", () => {
|
||||
await call.connect();
|
||||
await call.disconnect();
|
||||
expect(onParticipants.mock.calls).toEqual([
|
||||
[new Set([alice]), new Set()],
|
||||
[new Set(), new Set()],
|
||||
[new Set(), new Set([alice])],
|
||||
[new Map([[alice, new Set(["alices_device"])]]), new Map()],
|
||||
[new Map(), new Map([[alice, new Set(["alices_device"])]])],
|
||||
]);
|
||||
|
||||
call.off(CallEvent.Participants, onParticipants);
|
||||
@@ -893,87 +896,17 @@ describe("ElementCall", () => {
|
||||
call.off(CallEvent.Destroy, onDestroy);
|
||||
});
|
||||
|
||||
describe("being kicked out by another device", () => {
|
||||
const onDestroy = jest.fn();
|
||||
|
||||
beforeEach(async () => {
|
||||
await call.connect();
|
||||
call.on(CallEvent.Destroy, onDestroy);
|
||||
|
||||
jest.advanceTimersByTime(100);
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
call.off(CallEvent.Destroy, onDestroy);
|
||||
});
|
||||
|
||||
it("does not terminate the call if we are the last", async () => {
|
||||
client.emit(ClientEvent.ToDeviceEvent, {
|
||||
getType: () => (ElementCall.DUPLICATE_CALL_DEVICE_EVENT_TYPE),
|
||||
getContent: () => ({ device_id: "random_device_id", timestamp: Date.now() }),
|
||||
getSender: () => (client.getUserId()),
|
||||
} as MatrixEvent);
|
||||
|
||||
expect(client.sendStateEvent).not.toHaveBeenCalled();
|
||||
expect(
|
||||
[ConnectionState.Disconnecting, ConnectionState.Disconnected].includes(call.connectionState),
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it("ignores messages from our device", async () => {
|
||||
client.emit(ClientEvent.ToDeviceEvent, {
|
||||
getSender: () => (client.getUserId()),
|
||||
getType: () => (ElementCall.DUPLICATE_CALL_DEVICE_EVENT_TYPE),
|
||||
getContent: () => ({ device_id: client.getDeviceId(), timestamp: Date.now() }),
|
||||
} as MatrixEvent);
|
||||
|
||||
expect(client.sendStateEvent).not.toHaveBeenCalled();
|
||||
expect(
|
||||
[ConnectionState.Disconnecting, ConnectionState.Disconnected].includes(call.connectionState),
|
||||
).toBeFalsy();
|
||||
expect(onDestroy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("ignores messages from other users", async () => {
|
||||
client.emit(ClientEvent.ToDeviceEvent, {
|
||||
getSender: () => (bob.userId),
|
||||
getType: () => (ElementCall.DUPLICATE_CALL_DEVICE_EVENT_TYPE),
|
||||
getContent: () => ({ device_id: "random_device_id", timestamp: Date.now() }),
|
||||
} as MatrixEvent);
|
||||
|
||||
expect(client.sendStateEvent).not.toHaveBeenCalled();
|
||||
expect(
|
||||
[ConnectionState.Disconnecting, ConnectionState.Disconnected].includes(call.connectionState),
|
||||
).toBeFalsy();
|
||||
expect(onDestroy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("ignores messages from the past", async () => {
|
||||
client.emit(ClientEvent.ToDeviceEvent, {
|
||||
getSender: () => (client.getUserId()),
|
||||
getType: () => (ElementCall.DUPLICATE_CALL_DEVICE_EVENT_TYPE),
|
||||
getContent: () => ({ device_id: "random_device_id", timestamp: 0 }),
|
||||
} as MatrixEvent);
|
||||
|
||||
expect(client.sendStateEvent).not.toHaveBeenCalled();
|
||||
expect(
|
||||
[ConnectionState.Disconnecting, ConnectionState.Disconnected].includes(call.connectionState),
|
||||
).toBeFalsy();
|
||||
expect(onDestroy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("ends the call after a random delay if the last participant leaves without ending it", async () => {
|
||||
// Bob connects
|
||||
await client.sendStateEvent(
|
||||
room.roomId,
|
||||
ElementCall.MEMBER_EVENT_TYPE.name,
|
||||
{
|
||||
"m.expires_ts": 1000 * 60 * 10,
|
||||
"m.calls": [{
|
||||
"m.call_id": call.groupCall.getStateKey()!,
|
||||
"m.devices": [{ device_id: "bobweb", session_id: "1", feeds: [] }],
|
||||
"m.call_id": call.groupCall.groupCallId,
|
||||
"m.devices": [
|
||||
{ device_id: "bobweb", session_id: "1", feeds: [], expires_ts: 1000 * 60 * 10 },
|
||||
],
|
||||
}],
|
||||
},
|
||||
bob.userId,
|
||||
@@ -987,9 +920,8 @@ describe("ElementCall", () => {
|
||||
room.roomId,
|
||||
ElementCall.MEMBER_EVENT_TYPE.name,
|
||||
{
|
||||
"m.expires_ts": 1000 * 60 * 10,
|
||||
"m.calls": [{
|
||||
"m.call_id": call.groupCall.getStateKey()!,
|
||||
"m.call_id": call.groupCall.groupCallId,
|
||||
"m.devices": [],
|
||||
}],
|
||||
},
|
||||
@@ -1025,20 +957,22 @@ describe("ElementCall", () => {
|
||||
device_id: "alicedesktopneveronline",
|
||||
};
|
||||
|
||||
const mkContent = (devices: IMyDevice[]): ElementCallMemberContent => ({
|
||||
"m.expires_ts": 1000 * 60 * 10,
|
||||
const mkContent = (devices: IMyDevice[]) => ({
|
||||
"m.calls": [{
|
||||
"m.call_id": call.groupCall.getStateKey()!,
|
||||
"m.devices": devices.map(d => ({ device_id: d.device_id, session_id: "1", feeds: [] })),
|
||||
"m.call_id": call.groupCall.groupCallId,
|
||||
"m.devices": devices.map(d => ({
|
||||
device_id: d.device_id, session_id: "1", feeds: [], expires_ts: 1000 * 60 * 10,
|
||||
})),
|
||||
}],
|
||||
});
|
||||
const expectDevices = (devices: IMyDevice[]) => expect(
|
||||
room.currentState.getStateEvents(ElementCall.MEMBER_EVENT_TYPE.name, alice.userId).getContent(),
|
||||
room.currentState.getStateEvents(ElementCall.MEMBER_EVENT_TYPE.name, alice.userId)?.getContent(),
|
||||
).toEqual({
|
||||
"m.expires_ts": expect.any(Number),
|
||||
"m.calls": [{
|
||||
"m.call_id": call.groupCall.getStateKey()!,
|
||||
"m.devices": devices.map(d => ({ device_id: d.device_id, session_id: "1", feeds: [] })),
|
||||
"m.call_id": call.groupCall.groupCallId,
|
||||
"m.devices": devices.map(d => ({
|
||||
device_id: d.device_id, session_id: "1", feeds: [], expires_ts: expect.any(Number),
|
||||
})),
|
||||
}],
|
||||
});
|
||||
|
||||
@@ -1055,23 +989,10 @@ describe("ElementCall", () => {
|
||||
});
|
||||
|
||||
it("doesn't clean up valid devices", async () => {
|
||||
await call.connect();
|
||||
await client.sendStateEvent(
|
||||
room.roomId,
|
||||
ElementCall.MEMBER_EVENT_TYPE.name,
|
||||
mkContent([aliceWeb, aliceDesktop]),
|
||||
alice.userId,
|
||||
);
|
||||
|
||||
await call.clean();
|
||||
expectDevices([aliceWeb, aliceDesktop]);
|
||||
});
|
||||
|
||||
it("cleans up our own device if we're disconnected", async () => {
|
||||
await client.sendStateEvent(
|
||||
room.roomId,
|
||||
ElementCall.MEMBER_EVENT_TYPE.name,
|
||||
mkContent([aliceWeb, aliceDesktop]),
|
||||
mkContent([aliceDesktop]),
|
||||
alice.userId,
|
||||
);
|
||||
|
||||
@@ -1079,11 +1000,11 @@ describe("ElementCall", () => {
|
||||
expectDevices([aliceDesktop]);
|
||||
});
|
||||
|
||||
it("cleans up devices that have been offline for too long", async () => {
|
||||
it("cleans up our own device if we're disconnected", async () => {
|
||||
await client.sendStateEvent(
|
||||
room.roomId,
|
||||
ElementCall.MEMBER_EVENT_TYPE.name,
|
||||
mkContent([aliceDesktop, aliceDesktopOffline]),
|
||||
mkContent([aliceWeb, aliceDesktop]),
|
||||
alice.userId,
|
||||
);
|
||||
|
||||
@@ -1132,8 +1053,8 @@ describe("ElementCall", () => {
|
||||
|
||||
afterEach(() => cleanUpCallAndWidget(call, widget, audioMutedSpy, videoMutedSpy));
|
||||
|
||||
it("has intent m.room", () => {
|
||||
expect(call.groupCall.getContent()["m.intent"]).toBe("m.room");
|
||||
it("has room intent", () => {
|
||||
expect(call.groupCall.intent).toBe(GroupCallIntent.Room);
|
||||
});
|
||||
|
||||
it("doesn't end the call when the last participant leaves", async () => {
|
||||
|
@@ -16,11 +16,13 @@ limitations under the License.
|
||||
|
||||
import { MatrixWidgetType } from "matrix-widget-api";
|
||||
|
||||
import type { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall";
|
||||
import type { Room } from "matrix-js-sdk/src/models/room";
|
||||
import type { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||
import type { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { mkEvent } from "./test-utils";
|
||||
import { Call, ConnectionState, ElementCall, JitsiCall } from "../../src/models/Call";
|
||||
import { CallStore } from "../../src/stores/CallStore";
|
||||
|
||||
export class MockedCall extends Call {
|
||||
public static readonly EVENT_TYPE = "org.example.mocked_call";
|
||||
@@ -49,7 +51,6 @@ export class MockedCall extends Call {
|
||||
}
|
||||
|
||||
public static create(room: Room, id: string) {
|
||||
// Update room state to let CallStore know that a call might now exist
|
||||
room.addLiveEvents([mkEvent({
|
||||
event: true,
|
||||
type: this.EVENT_TYPE,
|
||||
@@ -59,16 +60,17 @@ export class MockedCall extends Call {
|
||||
skey: id,
|
||||
ts: Date.now(),
|
||||
})]);
|
||||
// @ts-ignore deliberately calling a private method
|
||||
// Let CallStore know that a call might now exist
|
||||
CallStore.instance.updateRoom(room);
|
||||
}
|
||||
|
||||
public get groupCall(): MatrixEvent {
|
||||
return this.event;
|
||||
}
|
||||
public readonly groupCall = { creationTs: this.event.getTs() } as unknown as GroupCall;
|
||||
|
||||
public get participants(): Set<RoomMember> {
|
||||
public get participants(): Map<RoomMember, Set<string>> {
|
||||
return super.participants;
|
||||
}
|
||||
public set participants(value: Set<RoomMember>) {
|
||||
public set participants(value: Map<RoomMember, Set<string>>) {
|
||||
super.participants = value;
|
||||
}
|
||||
|
||||
@@ -77,8 +79,7 @@ export class MockedCall extends Call {
|
||||
}
|
||||
|
||||
// No action needed for any of the following methods since this is just a mock
|
||||
protected getDevices(): string[] { return []; }
|
||||
protected async setDevices(): Promise<void> { }
|
||||
public async clean(): Promise<void> {}
|
||||
// Public to allow spying
|
||||
public async performConnection(): Promise<void> {}
|
||||
public async performDisconnection(): Promise<void> {}
|
||||
|
@@ -39,6 +39,7 @@ import { ReEmitter } from "matrix-js-sdk/src/ReEmitter";
|
||||
import { MediaHandler } from "matrix-js-sdk/src/webrtc/mediaHandler";
|
||||
import { Feature, ServerSupport } from "matrix-js-sdk/src/feature";
|
||||
|
||||
import type { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall";
|
||||
import { MatrixClientPeg as peg } from '../../src/MatrixClientPeg';
|
||||
import { makeType } from "../../src/utils/TypeUtils";
|
||||
import { ValidatedServerConfig } from "../../src/utils/ValidatedServerConfig";
|
||||
@@ -190,6 +191,7 @@ export function createTestClient(): MatrixClient {
|
||||
setVideoInput: jest.fn(),
|
||||
setAudioInput: jest.fn(),
|
||||
setAudioSettings: jest.fn(),
|
||||
stopAllStreams: jest.fn(),
|
||||
} as unknown as MediaHandler),
|
||||
uploadContent: jest.fn(),
|
||||
getEventMapper: () => (opts) => new MatrixEvent(opts),
|
||||
@@ -197,6 +199,7 @@ export function createTestClient(): MatrixClient {
|
||||
doesServerSupportLogoutDevices: jest.fn().mockReturnValue(true),
|
||||
requestPasswordEmailToken: jest.fn().mockRejectedValue({}),
|
||||
setPassword: jest.fn().mockRejectedValue({}),
|
||||
groupCallEventHandler: { groupCalls: new Map<string, GroupCall>() },
|
||||
} as unknown as MatrixClient;
|
||||
|
||||
client.reEmitter = new ReEmitter(client);
|
||||
@@ -453,7 +456,7 @@ export function mkStubRoom(roomId: string = null, name: string, client: MatrixCl
|
||||
getMyMembership: jest.fn().mockReturnValue("join"),
|
||||
maySendMessage: jest.fn().mockReturnValue(true),
|
||||
currentState: {
|
||||
getStateEvents: jest.fn(),
|
||||
getStateEvents: jest.fn((_type, key) => key === undefined ? [] : null),
|
||||
getMember: jest.fn(),
|
||||
mayClientSendStateEvent: jest.fn().mockReturnValue(true),
|
||||
maySendStateEvent: jest.fn().mockReturnValue(true),
|
||||
|
@@ -99,12 +99,15 @@ describe("IncomingCallEvent", () => {
|
||||
const renderToast = () => { render(<IncomingCallToast callEvent={call.event} />); };
|
||||
|
||||
it("correctly shows all the information", () => {
|
||||
call.participants = new Set([alice, bob]);
|
||||
call.participants = new Map([
|
||||
[alice, new Set("a")],
|
||||
[bob, new Set(["b1", "b2"])],
|
||||
]);
|
||||
renderToast();
|
||||
|
||||
screen.getByText("Video call started");
|
||||
screen.getByText("Video");
|
||||
screen.getByLabelText("2 participants");
|
||||
screen.getByLabelText("3 participants");
|
||||
|
||||
screen.getByRole("button", { name: "Join" });
|
||||
screen.getByRole("button", { name: "Close" });
|
||||
|
Reference in New Issue
Block a user