mockrtc 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +29 -0
- package/LICENSE +201 -0
- package/README.md +290 -0
- package/dist/admin-bin.d.ts +2 -0
- package/dist/admin-bin.js +67 -0
- package/dist/admin-bin.js.map +1 -0
- package/dist/client/mockrtc-client.d.ts +12 -0
- package/dist/client/mockrtc-client.js +67 -0
- package/dist/client/mockrtc-client.js.map +1 -0
- package/dist/client/mockrtc-remote-peer.d.ts +15 -0
- package/dist/client/mockrtc-remote-peer.js +246 -0
- package/dist/client/mockrtc-remote-peer.js.map +1 -0
- package/dist/control-channel.d.ts +8 -0
- package/dist/control-channel.js +11 -0
- package/dist/control-channel.js.map +1 -0
- package/dist/handling/handler-builder.d.ts +138 -0
- package/dist/handling/handler-builder.js +164 -0
- package/dist/handling/handler-builder.js.map +1 -0
- package/dist/handling/handler-step-definitions.d.ts +63 -0
- package/dist/handling/handler-step-definitions.js +123 -0
- package/dist/handling/handler-step-definitions.js.map +1 -0
- package/dist/handling/handler-steps.d.ts +48 -0
- package/dist/handling/handler-steps.js +218 -0
- package/dist/handling/handler-steps.js.map +1 -0
- package/dist/main-browser.d.ts +9 -0
- package/dist/main-browser.js +26 -0
- package/dist/main-browser.js.map +1 -0
- package/dist/main.d.ts +58 -0
- package/dist/main.js +67 -0
- package/dist/main.js.map +1 -0
- package/dist/mockrtc-admin-plugin.d.ts +56 -0
- package/dist/mockrtc-admin-plugin.js +151 -0
- package/dist/mockrtc-admin-plugin.js.map +1 -0
- package/dist/mockrtc-admin-server.d.ts +7 -0
- package/dist/mockrtc-admin-server.js +18 -0
- package/dist/mockrtc-admin-server.js.map +1 -0
- package/dist/mockrtc-client.d.ts +12 -0
- package/dist/mockrtc-client.js +64 -0
- package/dist/mockrtc-client.js.map +1 -0
- package/dist/mockrtc-handler-builder.d.ts +15 -0
- package/dist/mockrtc-handler-builder.js +24 -0
- package/dist/mockrtc-handler-builder.js.map +1 -0
- package/dist/mockrtc-peer.d.ts +147 -0
- package/dist/mockrtc-peer.js +7 -0
- package/dist/mockrtc-peer.js.map +1 -0
- package/dist/mockrtc-remote-peer.d.ts +15 -0
- package/dist/mockrtc-remote-peer.js +234 -0
- package/dist/mockrtc-remote-peer.js.map +1 -0
- package/dist/mockrtc-server-peer.d.ts +29 -0
- package/dist/mockrtc-server-peer.js +145 -0
- package/dist/mockrtc-server-peer.js.map +1 -0
- package/dist/mockrtc-server.d.ts +14 -0
- package/dist/mockrtc-server.js +53 -0
- package/dist/mockrtc-server.js.map +1 -0
- package/dist/mockrtc.d.ts +25 -0
- package/dist/mockrtc.js +7 -0
- package/dist/mockrtc.js.map +1 -0
- package/dist/package.json +52 -0
- package/dist/server/mockrtc-admin-plugin.d.ts +17 -0
- package/dist/server/mockrtc-admin-plugin.js +163 -0
- package/dist/server/mockrtc-admin-plugin.js.map +1 -0
- package/dist/server/mockrtc-admin-server.d.ts +7 -0
- package/dist/server/mockrtc-admin-server.js +18 -0
- package/dist/server/mockrtc-admin-server.js.map +1 -0
- package/dist/server/mockrtc-server-peer.d.ts +24 -0
- package/dist/server/mockrtc-server-peer.js +141 -0
- package/dist/server/mockrtc-server-peer.js.map +1 -0
- package/dist/server/mockrtc-server.d.ts +14 -0
- package/dist/server/mockrtc-server.js +53 -0
- package/dist/server/mockrtc-server.js.map +1 -0
- package/dist/src/main.d.ts +1 -0
- package/dist/src/main.js +24 -0
- package/dist/src/main.js.map +1 -0
- package/dist/src/mockrtc-peer.d.ts +0 -0
- package/dist/src/mockrtc-peer.js +2 -0
- package/dist/src/mockrtc-peer.js.map +1 -0
- package/dist/src/mockrtc.d.ts +0 -0
- package/dist/src/mockrtc.js +65 -0
- package/dist/src/mockrtc.js.map +1 -0
- package/dist/webrtc/control-channel.d.ts +8 -0
- package/dist/webrtc/control-channel.js +11 -0
- package/dist/webrtc/control-channel.js.map +1 -0
- package/dist/webrtc/datachannel-stream.d.ts +25 -0
- package/dist/webrtc/datachannel-stream.js +86 -0
- package/dist/webrtc/datachannel-stream.js.map +1 -0
- package/dist/webrtc/mediatrack-stream.d.ts +29 -0
- package/dist/webrtc/mediatrack-stream.js +109 -0
- package/dist/webrtc/mediatrack-stream.js.map +1 -0
- package/dist/webrtc/mockrtc-connection.d.ts +14 -0
- package/dist/webrtc/mockrtc-connection.js +147 -0
- package/dist/webrtc/mockrtc-connection.js.map +1 -0
- package/dist/webrtc/peer-connection.d.ts +16 -0
- package/dist/webrtc/peer-connection.js +81 -0
- package/dist/webrtc/peer-connection.js.map +1 -0
- package/dist/webrtc/rtc-connection.d.ts +47 -0
- package/dist/webrtc/rtc-connection.js +370 -0
- package/dist/webrtc/rtc-connection.js.map +1 -0
- package/dist/webrtc-hooks.d.ts +30 -0
- package/dist/webrtc-hooks.js +224 -0
- package/dist/webrtc-hooks.js.map +1 -0
- package/karma.conf.ts +89 -0
- package/ngi-eu-footer.png +0 -0
- package/package.json +86 -0
- package/src/admin-bin.ts +57 -0
- package/src/client/mockrtc-client.ts +79 -0
- package/src/client/mockrtc-remote-peer.ts +286 -0
- package/src/handling/handler-builder.ts +215 -0
- package/src/handling/handler-step-definitions.ts +142 -0
- package/src/handling/handler-steps.ts +254 -0
- package/src/main-browser.ts +44 -0
- package/src/main.ts +109 -0
- package/src/mockrtc-peer.ts +176 -0
- package/src/mockrtc.ts +36 -0
- package/src/server/mockrtc-admin-plugin.ts +196 -0
- package/src/server/mockrtc-admin-server.ts +17 -0
- package/src/server/mockrtc-server-peer.ts +159 -0
- package/src/server/mockrtc-server.ts +53 -0
- package/src/webrtc/control-channel.ts +13 -0
- package/src/webrtc/datachannel-stream.ts +102 -0
- package/src/webrtc/mediatrack-stream.ts +135 -0
- package/src/webrtc/mockrtc-connection.ts +164 -0
- package/src/webrtc/rtc-connection.ts +420 -0
- package/src/webrtc-hooks.ts +245 -0
- package/test/integration/close-steps.spec.ts +39 -0
- package/test/integration/connection-setup.spec.ts +230 -0
- package/test/integration/echo-steps.spec.ts +88 -0
- package/test/integration/proxy.spec.ts +526 -0
- package/test/integration/send-steps.spec.ts +76 -0
- package/test/integration/smoke-test.spec.ts +100 -0
- package/test/integration/wait-steps.spec.ts +225 -0
- package/test/start-test-admin-server.ts +12 -0
- package/test/test-setup.ts +136 -0
- package/test/tsconfig.json +11 -0
- package/tsconfig.json +14 -0
- package/typedoc.json +19 -0
- package/wallaby.js +41 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-FileCopyrightText: 2022 Tim Perry <tim@httptoolkit.tech>
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
MockRTC,
|
|
8
|
+
waitForState,
|
|
9
|
+
} from '../test-setup';
|
|
10
|
+
|
|
11
|
+
describe("Close steps", function () {
|
|
12
|
+
|
|
13
|
+
this.timeout(10000); // Closing can take ~5 seconds to be recognized on the client
|
|
14
|
+
|
|
15
|
+
const mockRTC = MockRTC.getRemote();
|
|
16
|
+
|
|
17
|
+
beforeEach(() => mockRTC.start());
|
|
18
|
+
afterEach(() => mockRTC.stop());
|
|
19
|
+
|
|
20
|
+
it("should be able to close a connection immediately", async () => {
|
|
21
|
+
const mockPeer = await mockRTC.buildPeer()
|
|
22
|
+
.thenClose();
|
|
23
|
+
|
|
24
|
+
const localConnection = new RTCPeerConnection();
|
|
25
|
+
|
|
26
|
+
const receivedMessages: string[] = [];
|
|
27
|
+
const testChannel = localConnection.createDataChannel('data-channel');
|
|
28
|
+
testChannel.addEventListener('message', (event) => { receivedMessages.push(event.data) });
|
|
29
|
+
|
|
30
|
+
const localOffer = await localConnection.createOffer();
|
|
31
|
+
await localConnection.setLocalDescription(localOffer);
|
|
32
|
+
const { answer } = await mockPeer.answerOffer(localOffer);
|
|
33
|
+
await localConnection.setRemoteDescription(answer);
|
|
34
|
+
|
|
35
|
+
await waitForState(localConnection, 'connected');
|
|
36
|
+
await waitForState(localConnection, 'disconnected');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
});
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-FileCopyrightText: 2022 Tim Perry <tim@httptoolkit.tech>
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { expect } from 'chai';
|
|
7
|
+
import * as SDP from 'sdp-transform';
|
|
8
|
+
import { MockRTC, waitForState } from '../test-setup';
|
|
9
|
+
|
|
10
|
+
describe("When connecting, MockRTC", function () {
|
|
11
|
+
|
|
12
|
+
const mockRTC = MockRTC.getRemote();
|
|
13
|
+
|
|
14
|
+
beforeEach(() => mockRTC.start());
|
|
15
|
+
afterEach(() => mockRTC.stop());
|
|
16
|
+
|
|
17
|
+
it("should be able create an offer and accept an answer", async () => {
|
|
18
|
+
const mockPeer = await mockRTC.buildPeer().waitForNextMessage().thenSend('Goodbye');
|
|
19
|
+
|
|
20
|
+
const localConnection = new RTCPeerConnection();
|
|
21
|
+
|
|
22
|
+
const { offer, setAnswer } = await mockPeer.createOffer();
|
|
23
|
+
await localConnection.setRemoteDescription(offer);
|
|
24
|
+
|
|
25
|
+
const localAnswer = await localConnection.createAnswer();
|
|
26
|
+
await localConnection.setLocalDescription(localAnswer);
|
|
27
|
+
await setAnswer(localAnswer);
|
|
28
|
+
|
|
29
|
+
// Wait until the connection opens successfully:
|
|
30
|
+
await waitForState(localConnection, 'connected');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should be able to answer a real local offer", async () => {
|
|
34
|
+
const mockPeer = await mockRTC.buildPeer().waitForNextMessage().thenSend('Goodbye');
|
|
35
|
+
|
|
36
|
+
const localConnection = new RTCPeerConnection();
|
|
37
|
+
localConnection.createDataChannel("dataChannel");
|
|
38
|
+
|
|
39
|
+
const localOffer = await localConnection.createOffer();
|
|
40
|
+
await localConnection.setLocalDescription(localOffer);
|
|
41
|
+
|
|
42
|
+
const { answer } = await mockPeer.answerOffer(localOffer);
|
|
43
|
+
await localConnection.setRemoteDescription(answer);
|
|
44
|
+
|
|
45
|
+
// Wait until the connection opens successfully:
|
|
46
|
+
await waitForState(localConnection, 'connected');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should be able to renegotiate after a mock offer was accepted", async () => {
|
|
50
|
+
const mockPeer = await mockRTC.buildPeer().waitForNextMessage().thenSend('Goodbye');
|
|
51
|
+
|
|
52
|
+
const localConnection = new RTCPeerConnection();
|
|
53
|
+
|
|
54
|
+
const { offer, setAnswer, session } = await mockPeer.createOffer();
|
|
55
|
+
await localConnection.setRemoteDescription(offer);
|
|
56
|
+
|
|
57
|
+
const localAnswer = await localConnection.createAnswer();
|
|
58
|
+
await localConnection.setLocalDescription(localAnswer);
|
|
59
|
+
await setAnswer(localAnswer);
|
|
60
|
+
|
|
61
|
+
// Wait until the connection opens successfully:
|
|
62
|
+
await waitForState(localConnection, 'connected');
|
|
63
|
+
|
|
64
|
+
// Renegotiate:
|
|
65
|
+
const updatedOffer = await localConnection.createOffer({ offerToReceiveAudio: true });
|
|
66
|
+
localConnection.setLocalDescription(updatedOffer);
|
|
67
|
+
|
|
68
|
+
const updatedAnswer = await session.answerOffer(updatedOffer);
|
|
69
|
+
await localConnection.setRemoteDescription(updatedAnswer);
|
|
70
|
+
|
|
71
|
+
const updatedDescription = localConnection.currentLocalDescription;
|
|
72
|
+
const updatedMedia = SDP.parse(updatedDescription!.sdp).media;
|
|
73
|
+
expect(updatedMedia.map(m => m.type)).to.include('audio');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("should be able to renegotiate after answering a local offer", async () => {
|
|
77
|
+
const mockPeer = await mockRTC.buildPeer().waitForNextMessage().thenSend('Goodbye');
|
|
78
|
+
|
|
79
|
+
const localConnection = new RTCPeerConnection();
|
|
80
|
+
localConnection.createDataChannel("dataChannel");
|
|
81
|
+
|
|
82
|
+
const localOffer = await localConnection.createOffer();
|
|
83
|
+
await localConnection.setLocalDescription(localOffer);
|
|
84
|
+
const { answer, session } = await mockPeer.answerOffer(localOffer);
|
|
85
|
+
await localConnection.setRemoteDescription(answer);
|
|
86
|
+
|
|
87
|
+
// Wait until the connection opens successfully:
|
|
88
|
+
await waitForState(localConnection, 'connected');
|
|
89
|
+
|
|
90
|
+
// Renegotiate:
|
|
91
|
+
const updatedOffer = await localConnection.createOffer({ offerToReceiveAudio: true });
|
|
92
|
+
localConnection.setLocalDescription(updatedOffer);
|
|
93
|
+
const updatedAnswer = await session.answerOffer(updatedOffer);
|
|
94
|
+
await localConnection.setRemoteDescription(updatedAnswer);
|
|
95
|
+
|
|
96
|
+
const updatedDescription = localConnection.currentLocalDescription;
|
|
97
|
+
const updatedMedia = SDP.parse(updatedDescription!.sdp).media;
|
|
98
|
+
expect(updatedMedia.map(m => m.type)).to.include('audio');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("should be able to create a mock offer that mirrors an existing SDP", async () => {
|
|
102
|
+
const mockPeer = await mockRTC.buildPeer().waitForNextMessage().thenSend('Goodbye');
|
|
103
|
+
|
|
104
|
+
const rawSdpToMirror = await (async () => {
|
|
105
|
+
// Wrapped in a function for clarity that this is separate, just for SDP setup:
|
|
106
|
+
const demoConn = new RTCPeerConnection();
|
|
107
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
108
|
+
const tracks = stream.getTracks();
|
|
109
|
+
demoConn.addTrack(tracks[0]);
|
|
110
|
+
return (await demoConn.createOffer({ offerToReceiveVideo: true })).sdp!;
|
|
111
|
+
})();
|
|
112
|
+
const originalSdp = SDP.parse(rawSdpToMirror);
|
|
113
|
+
|
|
114
|
+
const { offer, setAnswer } = await mockPeer.createOffer({ mirrorSDP: rawSdpToMirror });
|
|
115
|
+
|
|
116
|
+
const localConnection = new RTCPeerConnection();
|
|
117
|
+
await localConnection.setRemoteDescription(offer);
|
|
118
|
+
const localAnswer = await localConnection.createAnswer();
|
|
119
|
+
await localConnection.setLocalDescription(localAnswer);
|
|
120
|
+
await setAnswer(localAnswer);
|
|
121
|
+
|
|
122
|
+
// Wait until the connection opens successfully:
|
|
123
|
+
await waitForState(localConnection, 'connected');
|
|
124
|
+
|
|
125
|
+
// The remote description we accepted should match the originally mirrored SDP:
|
|
126
|
+
const remoteDescription = localConnection.currentRemoteDescription;
|
|
127
|
+
const remoteMedia = SDP.parse(remoteDescription!.sdp).media;
|
|
128
|
+
|
|
129
|
+
expect(remoteMedia.map(m => [
|
|
130
|
+
m.mid, m.type, m.protocol, m.direction
|
|
131
|
+
])).to.deep.equal([
|
|
132
|
+
[0, 'audio', 'UDP/TLS/RTP/SAVPF', 'sendrecv'],
|
|
133
|
+
[1, 'video', 'UDP/TLS/RTP/SAVPF', 'recvonly']
|
|
134
|
+
]);
|
|
135
|
+
|
|
136
|
+
// Check each individual media field. These are the fields that will be passed through when
|
|
137
|
+
// proxying (i.e. they're not linked to a specific peer, unlike the fingerprint etc).
|
|
138
|
+
['video', 'audio'].forEach((media) => {
|
|
139
|
+
const originalMedia = originalSdp.media.find(({ type }) => type === media)!;
|
|
140
|
+
const remoteAgreedMedia = remoteMedia.find(({ type }) => type === media)!;
|
|
141
|
+
([
|
|
142
|
+
'msid',
|
|
143
|
+
'protocol',
|
|
144
|
+
'ext',
|
|
145
|
+
'payloads',
|
|
146
|
+
'ssrcs', // <-- Especially important, SSRC->mid is how both peers map RTP to track
|
|
147
|
+
'ssrcGroups',
|
|
148
|
+
'rtp',
|
|
149
|
+
'fmtp',
|
|
150
|
+
'rtcp',
|
|
151
|
+
'rtcpFb'
|
|
152
|
+
] as const).forEach((field) => {
|
|
153
|
+
expect(remoteAgreedMedia[field]).to.deep.equal(originalMedia[field],
|
|
154
|
+
`Failed to mirror ${media} ${field}`
|
|
155
|
+
);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// The local description is the locally-generated answer, so flips the media direction:
|
|
160
|
+
const localDescription = localConnection.currentLocalDescription;
|
|
161
|
+
const localMedia = SDP.parse(localDescription!.sdp).media;
|
|
162
|
+
expect(localMedia.map(m => [
|
|
163
|
+
m.mid, m.type, m.protocol, m.direction
|
|
164
|
+
])).to.deep.equal([
|
|
165
|
+
[0, 'audio', 'UDP/TLS/RTP/SAVPF', 'recvonly'], // Only receive - no local audio to send
|
|
166
|
+
[1, 'video', 'UDP/TLS/RTP/SAVPF', 'inactive'] // Inactive - no video to send & remote is recvonly
|
|
167
|
+
]);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("should be able to create a mock answer that mirrors an existing SDP", async () => {
|
|
171
|
+
const mockPeer = await mockRTC.buildPeer().waitForNextMessage().thenSend('Goodbye');
|
|
172
|
+
|
|
173
|
+
const localConnection = new RTCPeerConnection();
|
|
174
|
+
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
|
|
175
|
+
const tracks = stream.getTracks();
|
|
176
|
+
localConnection.addTrack(tracks[0]);
|
|
177
|
+
|
|
178
|
+
const localOffer = await localConnection.createOffer();
|
|
179
|
+
await localConnection.setLocalDescription(localOffer);
|
|
180
|
+
|
|
181
|
+
const rawSdpToMirror = await (async () => {
|
|
182
|
+
// Wrapped in a function for clarity that this is separate, just for SDP setup:
|
|
183
|
+
const demoConn = new RTCPeerConnection();
|
|
184
|
+
demoConn.setRemoteDescription(localOffer);
|
|
185
|
+
demoConn.addTrack(tracks[0]);
|
|
186
|
+
return (await demoConn.createAnswer()).sdp!;
|
|
187
|
+
})();
|
|
188
|
+
const originalSdp = SDP.parse(rawSdpToMirror);
|
|
189
|
+
|
|
190
|
+
const { answer } = await mockPeer.answerOffer(localOffer, {
|
|
191
|
+
mirrorSDP: rawSdpToMirror
|
|
192
|
+
});
|
|
193
|
+
localConnection.setRemoteDescription(answer);
|
|
194
|
+
|
|
195
|
+
// Wait until the connection opens successfully:
|
|
196
|
+
await waitForState(localConnection, 'connected');
|
|
197
|
+
|
|
198
|
+
// The remote description we accepted should match the originally mirrored SDP:
|
|
199
|
+
const remoteDescription = localConnection.currentRemoteDescription;
|
|
200
|
+
const remoteMedia = SDP.parse(remoteDescription!.sdp).media;
|
|
201
|
+
|
|
202
|
+
expect(remoteMedia.map(m => [
|
|
203
|
+
m.mid, m.type, m.protocol, m.direction
|
|
204
|
+
])).to.deep.equal([
|
|
205
|
+
[0, 'video', 'UDP/TLS/RTP/SAVPF', 'sendrecv']
|
|
206
|
+
]);
|
|
207
|
+
|
|
208
|
+
// Check that the video has correct data in the fields that will be passed through when
|
|
209
|
+
// proxying (i.e. they're not linked to a specific peer, unlike the fingerprint etc).
|
|
210
|
+
const originalVideo = originalSdp.media[0];
|
|
211
|
+
const remoteAgreedVideo = remoteMedia[0];
|
|
212
|
+
([
|
|
213
|
+
'msid',
|
|
214
|
+
'protocol',
|
|
215
|
+
'ext',
|
|
216
|
+
'payloads',
|
|
217
|
+
'ssrcs', // <-- Especially important, SSRC->mid is how both peers map RTP to track
|
|
218
|
+
'ssrcGroups',
|
|
219
|
+
'rtp',
|
|
220
|
+
'fmtp',
|
|
221
|
+
'rtcp',
|
|
222
|
+
'rtcpFb'
|
|
223
|
+
] as const).forEach((field) => {
|
|
224
|
+
expect(remoteAgreedVideo[field]).to.deep.equal(originalVideo[field],
|
|
225
|
+
`Failed to mirror video ${field}`
|
|
226
|
+
);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-FileCopyrightText: 2022 Tim Perry <tim@httptoolkit.tech>
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
MockRTC,
|
|
8
|
+
expect,
|
|
9
|
+
delay,
|
|
10
|
+
waitForChannelOpen
|
|
11
|
+
} from '../test-setup';
|
|
12
|
+
|
|
13
|
+
describe("Echo steps", function () {
|
|
14
|
+
|
|
15
|
+
const mockRTC = MockRTC.getRemote();
|
|
16
|
+
|
|
17
|
+
beforeEach(() => mockRTC.start());
|
|
18
|
+
afterEach(() => mockRTC.stop());
|
|
19
|
+
|
|
20
|
+
it("should be able to echo messages across multiple data channels", async () => {
|
|
21
|
+
const mockPeer = await mockRTC.buildPeer()
|
|
22
|
+
.thenEcho();
|
|
23
|
+
|
|
24
|
+
const localConnection = new RTCPeerConnection();
|
|
25
|
+
const dataChannel1 = localConnection.createDataChannel("dataChannel1");
|
|
26
|
+
|
|
27
|
+
const localOffer = await localConnection.createOffer();
|
|
28
|
+
await localConnection.setLocalDescription(localOffer);
|
|
29
|
+
const { answer } = await mockPeer.answerOffer(localOffer);
|
|
30
|
+
await localConnection.setRemoteDescription(answer);
|
|
31
|
+
|
|
32
|
+
let messages: Array<any> = [];
|
|
33
|
+
dataChannel1.addEventListener('message', (event) => messages.push("1: " + event.data));
|
|
34
|
+
|
|
35
|
+
await waitForChannelOpen(dataChannel1);
|
|
36
|
+
dataChannel1.send('Test message 1');
|
|
37
|
+
|
|
38
|
+
const dataChannel2 = localConnection.createDataChannel("dataChannel2");
|
|
39
|
+
dataChannel2.addEventListener('message', (event) => messages.push("2: " + event.data));
|
|
40
|
+
await waitForChannelOpen(dataChannel2);
|
|
41
|
+
await delay(10); // Delay to guarantee ordering
|
|
42
|
+
dataChannel2.send('Test message 2');
|
|
43
|
+
|
|
44
|
+
await delay(10); // Delay to guarantee ordering
|
|
45
|
+
dataChannel1.send('Test message 3');
|
|
46
|
+
|
|
47
|
+
await delay(50); // Delay to guarantee delivery
|
|
48
|
+
|
|
49
|
+
expect(messages).to.deep.equal([
|
|
50
|
+
'1: Test message 1',
|
|
51
|
+
'2: Test message 2',
|
|
52
|
+
'1: Test message 3',
|
|
53
|
+
]);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("should be able to echo media", async () => {
|
|
57
|
+
const mockPeer = await mockRTC.buildPeer()
|
|
58
|
+
.thenEcho();
|
|
59
|
+
|
|
60
|
+
// Create a connection to send & receive video:
|
|
61
|
+
const localConn = new RTCPeerConnection();
|
|
62
|
+
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
|
|
63
|
+
localConn.addTrack(stream.getTracks()[0], stream);
|
|
64
|
+
|
|
65
|
+
// Turn incoming tracks into readable streams of frames:
|
|
66
|
+
const mediaStreamPromise = new Promise<ReadableStream<VideoFrame>>((resolve) => {
|
|
67
|
+
localConn.addEventListener('track', ({ track }) => {
|
|
68
|
+
const streamProcessor = new MediaStreamTrackProcessor({
|
|
69
|
+
track: track as MediaStreamVideoTrack
|
|
70
|
+
});
|
|
71
|
+
resolve(streamProcessor.readable);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Complete the connection with the mock peer:
|
|
76
|
+
const localOffer = await localConn.createOffer({ offerToReceiveVideo: true });
|
|
77
|
+
await localConn.setLocalDescription(localOffer);
|
|
78
|
+
const { answer } = await mockPeer.answerOffer(localOffer);
|
|
79
|
+
await localConn.setRemoteDescription(answer);
|
|
80
|
+
|
|
81
|
+
// Check we receive the expected echoed video:
|
|
82
|
+
const localMedia = await mediaStreamPromise;
|
|
83
|
+
const { value: localFrame } = await localMedia!.getReader().read();
|
|
84
|
+
expect(localFrame!.displayHeight).to.be.greaterThanOrEqual(240);
|
|
85
|
+
expect(localFrame!.displayWidth).to.be.greaterThanOrEqual(320);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
});
|