Skip to content

Commit 685cab3

Browse files
Improve VoIP integrations testing (#2495)
1 parent 85a96c6 commit 685cab3

File tree

5 files changed

+571
-143
lines changed

5 files changed

+571
-143
lines changed

spec/TestClient.ts

+4
Original file line numberDiff line numberDiff line change
@@ -236,4 +236,8 @@ export class TestClient {
236236
public isFallbackICEServerAllowed(): boolean {
237237
return true;
238238
}
239+
240+
public getUserId(): string {
241+
return this.userId;
242+
}
239243
}

spec/test-utils/webrtc.ts

+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*
2+
Copyright 2022 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
export const DUMMY_SDP = (
18+
"v=0\r\n" +
19+
"o=- 5022425983810148698 2 IN IP4 127.0.0.1\r\n" +
20+
"s=-\r\nt=0 0\r\na=group:BUNDLE 0\r\n" +
21+
"a=msid-semantic: WMS h3wAi7s8QpiQMH14WG3BnDbmlOqo9I5ezGZA\r\n" +
22+
"m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126\r\n" +
23+
"c=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=ice-ufrag:hLDR\r\n" +
24+
"a=ice-pwd:bMGD9aOldHWiI+6nAq/IIlRw\r\n" +
25+
"a=ice-options:trickle\r\n" +
26+
"a=fingerprint:sha-256 E4:94:84:F9:4A:98:8A:56:F5:5F:FD:AF:72:B9:32:89:49:5C:4B:9A:" +
27+
"4A:15:8E:41:8A:F3:69:E4:39:52:DC:D6\r\n" +
28+
"a=setup:active\r\n" +
29+
"a=mid:0\r\n" +
30+
"a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n" +
31+
"a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n" +
32+
"a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n" +
33+
"a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\n" +
34+
"a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\n" +
35+
"a=extmap:6 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\n" +
36+
"a=sendrecv\r\n" +
37+
"a=msid:h3wAi7s8QpiQMH14WG3BnDbmlOqo9I5ezGZA 4357098f-3795-4131-bff4-9ba9c0348c49\r\n" +
38+
"a=rtcp-mux\r\n" +
39+
"a=rtpmap:111 opus/48000/2\r\n" +
40+
"a=rtcp-fb:111 transport-cc\r\n" +
41+
"a=fmtp:111 minptime=10;useinbandfec=1\r\n" +
42+
"a=rtpmap:103 ISAC/16000\r\n" +
43+
"a=rtpmap:104 ISAC/32000\r\n" +
44+
"a=rtpmap:9 G722/8000\r\n" +
45+
"a=rtpmap:0 PCMU/8000\r\n" +
46+
"a=rtpmap:8 PCMA/8000\r\n" +
47+
"a=rtpmap:106 CN/32000\r\n" +
48+
"a=rtpmap:105 CN/16000\r\n" +
49+
"a=rtpmap:13 CN/8000\r\n" +
50+
"a=rtpmap:110 telephone-event/48000\r\n" +
51+
"a=rtpmap:112 telephone-event/32000\r\n" +
52+
"a=rtpmap:113 telephone-event/16000\r\n" +
53+
"a=rtpmap:126 telephone-event/8000\r\n" +
54+
"a=ssrc:3619738545 cname:2RWtmqhXLdoF4sOi\r\n"
55+
);
56+
57+
export class MockRTCPeerConnection {
58+
localDescription: RTCSessionDescription;
59+
60+
constructor() {
61+
this.localDescription = {
62+
sdp: DUMMY_SDP,
63+
type: 'offer',
64+
toJSON: function() { },
65+
};
66+
}
67+
68+
addEventListener() { }
69+
createDataChannel(label: string, opts: RTCDataChannelInit) { return { label, ...opts }; }
70+
createOffer() {
71+
return Promise.resolve({});
72+
}
73+
setRemoteDescription() {
74+
return Promise.resolve();
75+
}
76+
setLocalDescription() {
77+
return Promise.resolve();
78+
}
79+
close() { }
80+
getStats() { return []; }
81+
addTrack(track: MockMediaStreamTrack) { return new MockRTCRtpSender(track); }
82+
}
83+
84+
export class MockRTCRtpSender {
85+
constructor(public track: MockMediaStreamTrack) { }
86+
87+
replaceTrack(track: MockMediaStreamTrack) { this.track = track; }
88+
}
89+
90+
export class MockMediaStreamTrack {
91+
constructor(public readonly id: string, public readonly kind: "audio" | "video", public enabled = true) { }
92+
93+
stop() { }
94+
}
95+
96+
// XXX: Using EventTarget in jest doesn't seem to work, so we write our own
97+
// implementation
98+
export class MockMediaStream {
99+
constructor(
100+
public id: string,
101+
private tracks: MockMediaStreamTrack[] = [],
102+
) {}
103+
104+
listeners: [string, (...args: any[]) => any][] = [];
105+
106+
dispatchEvent(eventType: string) {
107+
this.listeners.forEach(([t, c]) => {
108+
if (t !== eventType) return;
109+
c();
110+
});
111+
}
112+
getTracks() { return this.tracks; }
113+
getAudioTracks() { return this.tracks.filter((track) => track.kind === "audio"); }
114+
getVideoTracks() { return this.tracks.filter((track) => track.kind === "video"); }
115+
addEventListener(eventType: string, callback: (...args: any[]) => any) {
116+
this.listeners.push([eventType, callback]);
117+
}
118+
removeEventListener(eventType: string, callback: (...args: any[]) => any) {
119+
this.listeners.filter(([t, c]) => {
120+
return t !== eventType || c !== callback;
121+
});
122+
}
123+
addTrack(track: MockMediaStreamTrack) {
124+
this.tracks.push(track);
125+
this.dispatchEvent("addtrack");
126+
}
127+
removeTrack(track: MockMediaStreamTrack) { this.tracks.splice(this.tracks.indexOf(track), 1); }
128+
}
129+
130+
export class MockMediaDeviceInfo {
131+
constructor(
132+
public kind: "audio" | "video",
133+
) { }
134+
}
135+
136+
export class MockMediaHandler {
137+
getUserMediaStream(audio: boolean, video: boolean) {
138+
const tracks = [];
139+
if (audio) tracks.push(new MockMediaStreamTrack("audio_track", "audio"));
140+
if (video) tracks.push(new MockMediaStreamTrack("video_track", "video"));
141+
142+
return new MockMediaStream("mock_stream_from_media_handler", tracks);
143+
}
144+
stopUserMediaStream() { }
145+
hasAudioDevice() { return true; }
146+
}

0 commit comments

Comments
 (0)