From 00629e6dc9fecc4154c0f7f8dbea0d2cb1c910c2 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 9 Sep 2022 21:15:34 +0100 Subject: [PATCH] Test fallback screensharing (#2659) * Test fallback screensharing * Test replacetrack is called * Unused import * Return type * Fix other test after new track IDs --- spec/test-utils/webrtc.ts | 23 +++++++++---- spec/unit/webrtc/call.spec.ts | 62 ++++++++++++++++++++++++++++++----- 2 files changed, 70 insertions(+), 15 deletions(-) diff --git a/spec/test-utils/webrtc.ts b/spec/test-utils/webrtc.ts index 0d3ec6fb7..fec018b7c 100644 --- a/spec/test-utils/webrtc.ts +++ b/spec/test-utils/webrtc.ts @@ -104,13 +104,18 @@ export class MockRTCPeerConnection { private onReadyToNegotiate: () => void; localDescription: RTCSessionDescription; signalingState: RTCSignalingState = "stable"; + public senders: MockRTCRtpSender[] = []; - public static triggerAllNegotiations() { + public static triggerAllNegotiations(): void { for (const inst of this.instances) { inst.doNegotiation(); } } + public static hasAnyPendingNegotiations(): boolean { + return this.instances.some(i => i.needsNegotiation); + } + public static resetInstances() { this.instances = []; } @@ -153,10 +158,12 @@ export class MockRTCPeerConnection { } close() { } getStats() { return []; } - addTrack(track: MockMediaStreamTrack) { + addTrack(track: MockMediaStreamTrack): MockRTCRtpSender { this.needsNegotiation = true; this.onReadyToNegotiate(); - return new MockRTCRtpSender(track); + const newSender = new MockRTCRtpSender(track); + this.senders.push(newSender); + return newSender; } removeTrack() { @@ -164,6 +171,8 @@ export class MockRTCPeerConnection { this.onReadyToNegotiate(); } + getSenders(): MockRTCRtpSender[] { return this.senders; } + doNegotiation() { if (this.needsNegotiation && this.negotiationNeededListener) { this.needsNegotiation = false; @@ -271,8 +280,8 @@ export class MockMediaHandler { getUserMediaStream(audio: boolean, video: boolean) { const tracks = []; - if (audio) tracks.push(new MockMediaStreamTrack("audio_track", "audio")); - if (video) tracks.push(new MockMediaStreamTrack("video_track", "video")); + if (audio) tracks.push(new MockMediaStreamTrack("usermedia_audio_track", "audio")); + if (video) tracks.push(new MockMediaStreamTrack("usermedia_video_track", "video")); const stream = new MockMediaStream(USERMEDIA_STREAM_ID, tracks); this.userMediaStreams.push(stream); @@ -282,8 +291,8 @@ export class MockMediaHandler { stream.isStopped = true; } getScreensharingStream = jest.fn((opts?: IScreensharingOpts) => { - const tracks = [new MockMediaStreamTrack("video_track", "video")]; - if (opts?.audio) tracks.push(new MockMediaStreamTrack("audio_track", "audio")); + const tracks = [new MockMediaStreamTrack("screenshare_video_track", "video")]; + if (opts?.audio) tracks.push(new MockMediaStreamTrack("screenshare_audio_track", "audio")); const stream = new MockMediaStream(SCREENSHARE_STREAM_ID, tracks); this.screensharingStreams.push(stream); diff --git a/spec/unit/webrtc/call.spec.ts b/spec/unit/webrtc/call.spec.ts index 55926e28e..fc1a5120d 100644 --- a/spec/unit/webrtc/call.spec.ts +++ b/spec/unit/webrtc/call.spec.ts @@ -389,14 +389,14 @@ describe('Call', function() { const usermediaSenders: Array = (call as any).usermediaSenders; - expect(call.localUsermediaStream.getAudioTracks()[0].id).toBe("audio_track"); - expect(call.localUsermediaStream.getVideoTracks()[0].id).toBe("video_track"); + expect(call.localUsermediaStream.getAudioTracks()[0].id).toBe("usermedia_audio_track"); + expect(call.localUsermediaStream.getVideoTracks()[0].id).toBe("usermedia_video_track"); expect(usermediaSenders.find((sender) => { return sender?.track?.kind === "audio"; - }).track.id).toBe("audio_track"); + }).track.id).toBe("usermedia_audio_track"); expect(usermediaSenders.find((sender) => { return sender?.track?.kind === "video"; - }).track.id).toBe("video_track"); + }).track.id).toBe("usermedia_video_track"); }); it("should handle SDPStreamMetadata changes", async () => { @@ -925,8 +925,8 @@ describe('Call', function() { await call.setScreensharingEnabled(true); expect( - call.getLocalFeeds().filter(f => f.purpose == SDPStreamMetadataPurpose.Screenshare).length, - ).toEqual(1); + call.getLocalFeeds().filter(f => f.purpose == SDPStreamMetadataPurpose.Screenshare), + ).toHaveLength(1); mockSendEvent.mockReset(); const sendNegotiatePromise = new Promise(resolve => { @@ -955,8 +955,54 @@ describe('Call', function() { await call.setScreensharingEnabled(false); expect( - call.getLocalFeeds().filter(f => f.purpose == SDPStreamMetadataPurpose.Screenshare).length, - ).toEqual(0); + call.getLocalFeeds().filter(f => f.purpose == SDPStreamMetadataPurpose.Screenshare), + ).toHaveLength(0); }); }); + + it("falls back to replaceTrack for opponents that don't support stream metadata", async () => { + await startVideoCall(client, call); + + await call.onAnswerReceived(makeMockEvent("@test:foo", { + "version": 1, + "call_id": call.callId, + "party_id": 'party_id', + "answer": { + sdp: DUMMY_SDP, + }, + })); + + MockRTCPeerConnection.triggerAllNegotiations(); + + const mockVideoSender = call.peerConn.getSenders().find(s => s.track.kind === "video"); + const mockReplaceTrack = mockVideoSender.replaceTrack = jest.fn(); + + await call.setScreensharingEnabled(true); + + // our local feed should still reflect the purpose of the feed (ie. screenshare) + expect( + call.getLocalFeeds().filter(f => f.purpose == SDPStreamMetadataPurpose.Screenshare).length, + ).toEqual(1); + + // but we should not have re-negotiated + expect(MockRTCPeerConnection.hasAnyPendingNegotiations()).toEqual(false); + + expect(mockReplaceTrack).toHaveBeenCalledWith(expect.objectContaining({ + id: "screenshare_video_track", + })); + mockReplaceTrack.mockClear(); + + await call.setScreensharingEnabled(false); + + expect( + call.getLocalFeeds().filter(f => f.purpose == SDPStreamMetadataPurpose.Screenshare), + ).toHaveLength(0); + expect(call.getLocalFeeds()).toHaveLength(1); + + expect(MockRTCPeerConnection.hasAnyPendingNegotiations()).toEqual(false); + + expect(mockReplaceTrack).toHaveBeenCalledWith(expect.objectContaining({ + id: "usermedia_video_track", + })); + }); });