From 586a313c8d2fd5e8982459b6e31d27c09d5066b8 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 14 Sep 2022 09:42:57 +0100 Subject: [PATCH] Add tests for call answering / candidate sending (#2666) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add tests for call answering / candidate sending * Remopve unused stuff * Capitalise Co-authored-by: Šimon Brandner * Capitalisation * Capitalise * Fix typescript strict error * Actually fix TS strict error(?) * TS strict mode try 3 Co-authored-by: Šimon Brandner --- spec/test-utils/webrtc.ts | 7 +++- spec/unit/webrtc/call.spec.ts | 71 +++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/spec/test-utils/webrtc.ts b/spec/test-utils/webrtc.ts index a0bdba354ca..5d994a4545b 100644 --- a/spec/test-utils/webrtc.ts +++ b/spec/test-utils/webrtc.ts @@ -99,6 +99,7 @@ export class MockRTCPeerConnection { private static instances: MockRTCPeerConnection[] = []; private negotiationNeededListener: () => void; + public iceCandidateListener?: (e: RTCPeerConnectionIceEvent) => void; private needsNegotiation = false; public readyToNegotiate: Promise; private onReadyToNegotiate: () => void; @@ -135,7 +136,11 @@ export class MockRTCPeerConnection { } addEventListener(type: string, listener: () => void) { - if (type === 'negotiationneeded') this.negotiationNeededListener = listener; + if (type === 'negotiationneeded') { + this.negotiationNeededListener = listener; + } else if (type == 'icecandidate') { + this.iceCandidateListener = listener; + } } createDataChannel(label: string, opts: RTCDataChannelInit) { return { label, ...opts }; } createOffer() { diff --git a/spec/unit/webrtc/call.spec.ts b/spec/unit/webrtc/call.spec.ts index 156efbfed40..da80bc2c19b 100644 --- a/spec/unit/webrtc/call.spec.ts +++ b/spec/unit/webrtc/call.spec.ts @@ -887,6 +887,77 @@ describe('Call', function() { }); }); + describe("answering calls", () => { + beforeEach(async () => { + await fakeIncomingCall(client, call, "1"); + }); + + const untilEventSent = async (...args) => { + const maxTries = 20; + + for (let tries = 0; tries < maxTries; ++tries) { + if (tries) { + await new Promise(resolve => { + setTimeout(resolve, 100); + }); + } + try { + expect(client.client.sendEvent).toHaveBeenCalledWith(...args); + return; + } catch (e) { + if (tries == maxTries - 1) { + throw e; + } + } + } + }; + + it("sends an answer event", async () => { + await call.answer(); + await untilEventSent( + FAKE_ROOM_ID, + EventType.CallAnswer, + expect.objectContaining({ + call_id: call.callId, + answer: expect.objectContaining({ + type: 'offer', + }), + }), + ); + }); + + it("sends ICE candidates as separate events if they arrive after the answer", async () => { + const fakeCandidateString = "here is a fake candidate!"; + + await call.answer(); + await untilEventSent( + FAKE_ROOM_ID, + EventType.CallAnswer, + expect.objectContaining({}), + ); + + const mockPeerConn = call.peerConn as unknown as MockRTCPeerConnection; + mockPeerConn.iceCandidateListener!({ + candidate: { + candidate: fakeCandidateString, + sdpMLineIndex: 0, + sdpMid: '0', + toJSON: jest.fn().mockReturnValue(fakeCandidateString), + }, + } as unknown as RTCPeerConnectionIceEvent); + + await untilEventSent( + FAKE_ROOM_ID, + EventType.CallCandidates, + expect.objectContaining({ + candidates: [ + fakeCandidateString, + ], + }), + ); + }); + }); + it("times out an incoming call", async () => { jest.useFakeTimers(); await fakeIncomingCall(client, call, "1");