Skip to content

Commit 9e1b126

Browse files
dbkrrobintown
andauthored
1:1 screenshare tests (#2617)
* 1:1 screenshare tests Fixes element-hq/element-call#548 * Always hang up calls after tests to prevent hanging tests Also fix a null dereference as we may not have an invitee or opponent member when sending voip events if not using to-device messages. * use mockImplementationOnce Co-authored-by: Robin <[email protected]> * use mockImplementationOnce Co-authored-by: Robin <[email protected]> * Add type on mock * Add corresponding call.off * Merge enable & disable screenshare tests Co-authored-by: Robin <[email protected]>
1 parent c527f85 commit 9e1b126

File tree

3 files changed

+85
-11
lines changed

3 files changed

+85
-11
lines changed

spec/test-utils/webrtc.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ export const DUMMY_SDP = (
5656
"a=ssrc:3619738545 cname:2RWtmqhXLdoF4sOi\r\n"
5757
);
5858

59+
export const USERMEDIA_STREAM_ID = "mock_stream_from_media_handler";
60+
export const SCREENSHARE_STREAM_ID = "mock_screen_stream_from_media_handler";
61+
5962
class MockMediaStreamAudioSourceNode {
6063
connect() {}
6164
}
@@ -128,6 +131,10 @@ export class MockRTCPeerConnection {
128131
return new MockRTCRtpSender(track);
129132
}
130133

134+
removeTrack() {
135+
this.needsNegotiation = true;
136+
}
137+
131138
doNegotiation() {
132139
if (this.needsNegotiation && this.negotiationNeededListener) {
133140
this.needsNegotiation = false;
@@ -222,7 +229,7 @@ export class MockMediaHandler {
222229
if (audio) tracks.push(new MockMediaStreamTrack("audio_track", "audio"));
223230
if (video) tracks.push(new MockMediaStreamTrack("video_track", "video"));
224231

225-
const stream = new MockMediaStream("mock_stream_from_media_handler", tracks);
232+
const stream = new MockMediaStream(USERMEDIA_STREAM_ID, tracks);
226233
this.userMediaStreams.push(stream);
227234
return stream;
228235
}
@@ -233,7 +240,7 @@ export class MockMediaHandler {
233240
const tracks = [new MockMediaStreamTrack("video_track", "video")];
234241
if (opts?.audio) tracks.push(new MockMediaStreamTrack("audio_track", "audio"));
235242

236-
const stream = new MockMediaStream("mock_screen_stream_from_media_handler", tracks);
243+
const stream = new MockMediaStream(SCREENSHARE_STREAM_ID, tracks);
237244
this.screensharingStreams.push(stream);
238245
return stream;
239246
}

spec/unit/webrtc/call.spec.ts

+75-8
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import {
3030
MockMediaStream,
3131
MockMediaStreamTrack,
3232
installWebRTCMocks,
33+
MockRTCPeerConnection,
34+
SCREENSHARE_STREAM_ID,
3335
} from "../../test-utils/webrtc";
3436
import { CallFeed } from "../../../src/webrtc/callFeed";
3537
import { EventType, MatrixEvent } from "../../../src";
@@ -117,6 +119,9 @@ describe('Call', function() {
117119
});
118120

119121
afterEach(function() {
122+
// Hangup to stop timers
123+
call.hangup(CallErrorCode.UserHangup, true);
124+
120125
client.stop();
121126
global.navigator = prevNavigator;
122127
global.window = prevWindow;
@@ -178,9 +183,6 @@ describe('Call', function() {
178183
getSender: () => "@test:foo",
179184
});
180185
expect(call.peerConn.addIceCandidate.mock.calls.length).toBe(1);
181-
182-
// Hangup to stop timers
183-
call.hangup(CallErrorCode.UserHangup, true);
184186
});
185187

186188
it('should add candidates received before answer if party ID is correct', async function() {
@@ -283,9 +285,6 @@ describe('Call', function() {
283285
const ident = call.getRemoteAssertedIdentity();
284286
expect(ident.id).toEqual("@steve:example.com");
285287
expect(ident.displayName).toEqual("Steve Gibbons");
286-
287-
// Hangup to stop timers
288-
call.hangup(CallErrorCode.UserHangup, true);
289288
});
290289

291290
it("should map SDPStreamMetadata to feeds", async () => {
@@ -734,16 +733,18 @@ describe('Call', function() {
734733

735734
describe("ignoring streams with ids for which we already have a feed", () => {
736735
const STREAM_ID = "stream_id";
737-
const FEEDS_CHANGED_CALLBACK = jest.fn();
736+
let FEEDS_CHANGED_CALLBACK: jest.Mock<void, []>;
738737

739738
beforeEach(async () => {
739+
FEEDS_CHANGED_CALLBACK = jest.fn();
740+
740741
await startVoiceCall(client, call);
741742
call.on(CallEvent.FeedsChanged, FEEDS_CHANGED_CALLBACK);
742743
jest.spyOn(call, "pushLocalFeed");
743744
});
744745

745746
afterEach(() => {
746-
FEEDS_CHANGED_CALLBACK.mockReset();
747+
call.off(CallEvent.FeedsChanged, FEEDS_CHANGED_CALLBACK);
747748
});
748749

749750
it("should ignore stream passed to pushRemoteFeed()", async () => {
@@ -941,4 +942,70 @@ describe('Call', function() {
941942

942943
expect(call.state).toEqual(CallState.Ended);
943944
});
945+
946+
describe("Screen sharing", () => {
947+
beforeEach(async () => {
948+
await startVoiceCall(client, call);
949+
950+
await call.onAnswerReceived({
951+
getContent: () => {
952+
return {
953+
"version": 1,
954+
"call_id": call.callId,
955+
"party_id": 'party_id',
956+
"answer": {
957+
sdp: DUMMY_SDP,
958+
},
959+
"org.matrix.msc3077.sdp_stream_metadata": {
960+
"foo": {
961+
"purpose": "m.usermedia",
962+
"audio_muted": false,
963+
"video_muted": false,
964+
},
965+
},
966+
};
967+
},
968+
getSender: () => "@test:foo",
969+
});
970+
});
971+
972+
afterEach(() => {
973+
// Hangup to stop timers
974+
call.hangup(CallErrorCode.UserHangup, true);
975+
});
976+
977+
it("enables and disables screensharing", async () => {
978+
await call.setScreensharingEnabled(true);
979+
980+
expect(call.feeds.filter(f => f.purpose == SDPStreamMetadataPurpose.Screenshare).length).toEqual(1);
981+
982+
client.client.sendEvent.mockReset();
983+
const sendNegotiatePromise = new Promise<void>(resolve => {
984+
client.client.sendEvent.mockImplementationOnce(() => {
985+
resolve();
986+
});
987+
});
988+
989+
MockRTCPeerConnection.triggerAllNegotiations();
990+
await sendNegotiatePromise;
991+
992+
expect(client.client.sendEvent).toHaveBeenCalledWith(
993+
FAKE_ROOM_ID,
994+
EventType.CallNegotiate,
995+
expect.objectContaining({
996+
"version": "1",
997+
"call_id": call.callId,
998+
"org.matrix.msc3077.sdp_stream_metadata": expect.objectContaining({
999+
[SCREENSHARE_STREAM_ID]: expect.objectContaining({
1000+
purpose: SDPStreamMetadataPurpose.Screenshare,
1001+
}),
1002+
}),
1003+
}),
1004+
);
1005+
1006+
await call.setScreensharingEnabled(false);
1007+
1008+
expect(call.feeds.filter(f => f.purpose == SDPStreamMetadataPurpose.Screenshare).length).toEqual(0);
1009+
});
1010+
});
9441011
});

src/webrtc/call.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2240,7 +2240,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
22402240
eventType,
22412241
roomId: this.roomId,
22422242
content: realContent,
2243-
userId: this.invitee || this.getOpponentMember().userId,
2243+
userId: this.invitee || this.getOpponentMember()?.userId,
22442244
});
22452245

22462246
await this.client.sendEvent(this.roomId, eventType, realContent);

0 commit comments

Comments
 (0)