You've already forked matrix-react-sdk
mirror of
https://github.com/matrix-org/matrix-react-sdk.git
synced 2025-07-28 15:22:05 +03:00
New group call experience: Starting and ending calls (#9318)
* Create m.room calls in video rooms, and m.prompt calls otherwise * Terminate a call when the last person leaves * Hook up the room header button to a unified CallView component * Write more tests
This commit is contained in:
@ -51,11 +51,12 @@ jest.spyOn(MediaDeviceHandler, "getDevices").mockResolvedValue({
|
||||
jest.spyOn(MediaDeviceHandler, "getAudioInput").mockReturnValue("1");
|
||||
jest.spyOn(MediaDeviceHandler, "getVideoInput").mockReturnValue("2");
|
||||
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation(settingName =>
|
||||
settingName === "feature_video_rooms" || settingName === "feature_element_call_video_rooms" ? true : undefined,
|
||||
const enabledSettings = new Set(["feature_group_calls", "feature_video_rooms", "feature_element_call_video_rooms"]);
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation(
|
||||
settingName => enabledSettings.has(settingName) || undefined,
|
||||
);
|
||||
|
||||
const setUpClientRoomAndStores = (roomType: RoomType): {
|
||||
const setUpClientRoomAndStores = (): {
|
||||
client: Mocked<MatrixClient>;
|
||||
room: Room;
|
||||
alice: RoomMember;
|
||||
@ -68,7 +69,6 @@ const setUpClientRoomAndStores = (roomType: RoomType): {
|
||||
const room = new Room("!1:example.org", client, "@alice:example.org", {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
jest.spyOn(room, "getType").mockReturnValue(roomType);
|
||||
|
||||
const alice = mkRoomMember(room.roomId, "@alice:example.org");
|
||||
const bob = mkRoomMember(room.roomId, "@bob:example.org");
|
||||
@ -165,7 +165,8 @@ describe("JitsiCall", () => {
|
||||
let carol: RoomMember;
|
||||
|
||||
beforeEach(() => {
|
||||
({ client, room, alice, bob, carol } = setUpClientRoomAndStores(RoomType.ElementVideo));
|
||||
({ client, room, alice, bob, carol } = setUpClientRoomAndStores());
|
||||
jest.spyOn(room, "getType").mockReturnValue(RoomType.ElementVideo);
|
||||
});
|
||||
|
||||
afterEach(() => cleanUpClientRoomAndStores(client, room));
|
||||
@ -191,7 +192,7 @@ describe("JitsiCall", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("instance", () => {
|
||||
describe("instance in a video room", () => {
|
||||
let call: JitsiCall;
|
||||
let widget: Widget;
|
||||
let messaging: Mocked<ClientWidgetApi>;
|
||||
@ -542,7 +543,7 @@ describe("ElementCall", () => {
|
||||
let carol: RoomMember;
|
||||
|
||||
beforeEach(() => {
|
||||
({ client, room, alice, bob, carol } = setUpClientRoomAndStores(RoomType.UnstableCall));
|
||||
({ client, room, alice, bob, carol } = setUpClientRoomAndStores());
|
||||
});
|
||||
|
||||
afterEach(() => cleanUpClientRoomAndStores(client, room));
|
||||
@ -569,7 +570,7 @@ describe("ElementCall", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("instance", () => {
|
||||
describe("instance in a non-video room", () => {
|
||||
let call: ElementCall;
|
||||
let widget: Widget;
|
||||
let messaging: Mocked<ClientWidgetApi>;
|
||||
@ -590,6 +591,10 @@ describe("ElementCall", () => {
|
||||
|
||||
afterEach(() => cleanUpCallAndWidget(call, widget, audioMutedSpy, videoMutedSpy));
|
||||
|
||||
it("has intent m.prompt", () => {
|
||||
expect(call.groupCall.getContent()["m.intent"]).toBe("m.prompt");
|
||||
});
|
||||
|
||||
it("connects muted", async () => {
|
||||
expect(call.connectionState).toBe(ConnectionState.Disconnected);
|
||||
audioMutedSpy.mockReturnValue(true);
|
||||
@ -747,6 +752,59 @@ describe("ElementCall", () => {
|
||||
expect(events).toEqual([new Set([alice]), new Set()]);
|
||||
});
|
||||
|
||||
it("ends the call immediately if we're the last participant to leave", async () => {
|
||||
await call.connect();
|
||||
const onDestroy = jest.fn();
|
||||
call.on(CallEvent.Destroy, onDestroy);
|
||||
await call.disconnect();
|
||||
expect(onDestroy).toHaveBeenCalled();
|
||||
call.off(CallEvent.Destroy, onDestroy);
|
||||
});
|
||||
|
||||
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: [] }],
|
||||
}],
|
||||
},
|
||||
bob.userId,
|
||||
);
|
||||
|
||||
const onDestroy = jest.fn();
|
||||
call.on(CallEvent.Destroy, onDestroy);
|
||||
|
||||
// Bob disconnects
|
||||
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": [],
|
||||
}],
|
||||
},
|
||||
bob.userId,
|
||||
);
|
||||
|
||||
// Nothing should happen for at least a second, to give Bob a chance
|
||||
// to end the call on his own
|
||||
jest.advanceTimersByTime(1000);
|
||||
expect(onDestroy).not.toHaveBeenCalled();
|
||||
|
||||
// Within 10 seconds, our client should end the call on behalf of Bob
|
||||
jest.advanceTimersByTime(9000);
|
||||
expect(onDestroy).toHaveBeenCalled();
|
||||
|
||||
call.off(CallEvent.Destroy, onDestroy);
|
||||
});
|
||||
|
||||
describe("clean", () => {
|
||||
const aliceWeb: IMyDevice = {
|
||||
device_id: "aliceweb",
|
||||
@ -848,4 +906,40 @@ describe("ElementCall", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("instance in a video room", () => {
|
||||
let call: ElementCall;
|
||||
let widget: Widget;
|
||||
let audioMutedSpy: jest.SpyInstance<boolean, []>;
|
||||
let videoMutedSpy: jest.SpyInstance<boolean, []>;
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.useFakeTimers();
|
||||
jest.setSystemTime(0);
|
||||
|
||||
jest.spyOn(room, "getType").mockReturnValue(RoomType.UnstableCall);
|
||||
|
||||
await ElementCall.create(room);
|
||||
const maybeCall = ElementCall.get(room);
|
||||
if (maybeCall === null) throw new Error("Failed to create call");
|
||||
call = maybeCall;
|
||||
|
||||
({ widget, audioMutedSpy, videoMutedSpy } = setUpWidget(call));
|
||||
});
|
||||
|
||||
afterEach(() => cleanUpCallAndWidget(call, widget, audioMutedSpy, videoMutedSpy));
|
||||
|
||||
it("has intent m.room", () => {
|
||||
expect(call.groupCall.getContent()["m.intent"]).toBe("m.room");
|
||||
});
|
||||
|
||||
it("doesn't end the call when the last participant leaves", async () => {
|
||||
await call.connect();
|
||||
const onDestroy = jest.fn();
|
||||
call.on(CallEvent.Destroy, onDestroy);
|
||||
await call.disconnect();
|
||||
expect(onDestroy).not.toHaveBeenCalled();
|
||||
call.off(CallEvent.Destroy, onDestroy);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Reference in New Issue
Block a user