diff --git a/spec/test-utils/webrtc.ts b/spec/test-utils/webrtc.ts index 9ed142fcc00..80ccbef2668 100644 --- a/spec/test-utils/webrtc.ts +++ b/spec/test-utils/webrtc.ts @@ -109,6 +109,12 @@ export class MockRTCPeerConnection { sdp: DUMMY_SDP, }); } + createAnswer() { + return Promise.resolve({ + type: 'answer', + sdp: DUMMY_SDP, + }); + } setRemoteDescription() { return Promise.resolve(); } diff --git a/spec/unit/webrtc/call.spec.ts b/spec/unit/webrtc/call.spec.ts index 338e968b190..1cedd5c7cc4 100644 --- a/spec/unit/webrtc/call.spec.ts +++ b/spec/unit/webrtc/call.spec.ts @@ -25,7 +25,9 @@ import { installWebRTCMocks, } from "../../test-utils/webrtc"; import { CallFeed } from "../../../src/webrtc/callFeed"; -import { EventType } from "../../../src"; +import { EventType, MatrixEvent } from "../../../src"; + +const FAKE_ROOM_ID = "!foo:bar"; const startVoiceCall = async (client: TestClient, call: MatrixCall): Promise => { const callPromise = call.placeVoiceCall(); @@ -43,6 +45,31 @@ const startVideoCall = async (client: TestClient, call: MatrixCall): Promise { + const callPromise = call.initWithInvite({ + getContent: jest.fn().mockReturnValue({ + version, + call_id: "call_id", + party_id: "remote_party_id", + offer: { + sdp: DUMMY_SDP, + }, + }), + getSender: () => "@test:foo", + getLocalAge: () => null, + } as unknown as MatrixEvent); + call.getFeeds().push(new CallFeed({ + client: client.client, + userId: "remote_user_id", + // @ts-ignore Mock + stream: new MockMediaStream("remote_stream_id", [new MockMediaStreamTrack("remote_tack_id")]), + id: "remote_feed_id", + purpose: SDPStreamMetadataPurpose.Usermedia, + })); + await client.httpBackend.flush(""); + await callPromise; +}; + describe('Call', function() { let client; let call; @@ -60,7 +87,7 @@ describe('Call', function() { client = new TestClient("@alice:foo", "somedevice", "token", undefined, {}); // We just stub out sendEvent: we're not interested in testing the client's // event sending code here - client.client.sendEvent = () => {}; + client.client.sendEvent = jest.fn(); client.client.mediaHandler = new MockMediaHandler; client.client.getMediaHandler = () => client.client.mediaHandler; client.httpBackend.when("GET", "/voip/turnServer").respond(200, {}); @@ -74,7 +101,7 @@ describe('Call', function() { call = new MatrixCall({ client: client.client, - roomId: '!foo:bar', + roomId: FAKE_ROOM_ID, }); // call checks one of these is wired up call.on('error', () => {}); @@ -594,28 +621,7 @@ describe('Call', function() { }); it("should end call after receiving a select event with a different party id", async () => { - const callPromise = call.initWithInvite({ - getContent: () => ({ - version: 1, - call_id: "call_id", - party_id: "remote_party_id", - offer: { - sdp: DUMMY_SDP, - }, - }), - getSender: () => "@test:foo", - getLocalAge: () => null, - }); - call.feeds.push(new CallFeed({ - client, - userId: "remote_user_id", - // @ts-ignore Mock - stream: new MockMediaStream("remote_stream_id", [new MockMediaStreamTrack("remote_tack_id")]), - id: "remote_feed_id", - purpose: SDPStreamMetadataPurpose.Usermedia, - })); - await client.httpBackend.flush(); - await callPromise; + await fakeIncomingCall(client, call); const callHangupCallback = jest.fn(); call.on(CallEvent.Hangup, callHangupCallback); @@ -845,4 +851,72 @@ describe('Call', function() { }); }); }); + + describe("rejecting calls", () => { + it("sends hangup event when rejecting v0 calls", async () => { + await fakeIncomingCall(client, call, 0); + + call.reject(); + + expect(client.client.sendEvent).toHaveBeenCalledWith( + FAKE_ROOM_ID, + EventType.CallHangup, + expect.objectContaining({ + call_id: call.callId, + }), + ); + }); + + it("sends reject event when rejecting v1 calls", async () => { + await fakeIncomingCall(client, call, "1"); + + call.reject(); + + expect(client.client.sendEvent).toHaveBeenCalledWith( + FAKE_ROOM_ID, + EventType.CallReject, + expect.objectContaining({ + call_id: call.callId, + }), + ); + }); + + it("does not reject a call that has already been answered", async () => { + await fakeIncomingCall(client, call, "1"); + + await call.answer(); + + client.client.sendEvent.mockReset(); + + let caught = false; + try { + call.reject(); + } catch (e) { + caught = true; + } + + expect(caught).toEqual(true); + expect(client.client.sendEvent).not.toHaveBeenCalled(); + + call.hangup(); + }); + + it("hangs up a call", async () => { + await fakeIncomingCall(client, call, "1"); + + await call.answer(); + + client.client.sendEvent.mockReset(); + + call.hangup(); + + expect(client.client.sendEvent).toHaveBeenCalledWith( + FAKE_ROOM_ID, + EventType.CallHangup, + expect.objectContaining({ + call_id: call.callId, + }), + ); + }); + }); }); diff --git a/src/webrtc/call.ts b/src/webrtc/call.ts index 43ef311882f..8e14e06a880 100644 --- a/src/webrtc/call.ts +++ b/src/webrtc/call.ts @@ -1569,6 +1569,10 @@ export class MatrixCall extends TypedEventEmitter