mediasoup-client 3.6.90 → 3.6.92

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.
Files changed (125) hide show
  1. package/lib/Consumer.d.ts +115 -0
  2. package/lib/Consumer.d.ts.map +1 -0
  3. package/lib/Consumer.js +190 -0
  4. package/lib/DataConsumer.d.ts +93 -0
  5. package/lib/DataConsumer.d.ts.map +1 -0
  6. package/lib/DataConsumer.js +162 -0
  7. package/lib/DataProducer.d.ts +97 -0
  8. package/lib/DataProducer.d.ts.map +1 -0
  9. package/lib/DataProducer.js +180 -0
  10. package/lib/Device.d.ts +92 -0
  11. package/lib/Device.d.ts.map +1 -0
  12. package/lib/Device.js +464 -0
  13. package/lib/EnhancedEventEmitter.d.ts +24 -0
  14. package/lib/EnhancedEventEmitter.d.ts.map +1 -0
  15. package/lib/EnhancedEventEmitter.js +70 -0
  16. package/lib/Logger.d.ts +11 -0
  17. package/lib/Logger.d.ts.map +1 -0
  18. package/lib/Logger.js +37 -0
  19. package/lib/Producer.d.ts +169 -0
  20. package/lib/Producer.d.ts.map +1 -0
  21. package/lib/Producer.js +287 -0
  22. package/lib/RtpParameters.d.ts +306 -0
  23. package/lib/RtpParameters.d.ts.map +1 -0
  24. package/lib/RtpParameters.js +6 -0
  25. package/lib/SctpParameters.d.ts +68 -0
  26. package/lib/SctpParameters.d.ts.map +1 -0
  27. package/lib/SctpParameters.js +2 -0
  28. package/lib/Transport.d.ts +243 -0
  29. package/lib/Transport.d.ts.map +1 -0
  30. package/lib/Transport.js +824 -0
  31. package/lib/errors.d.ts +13 -0
  32. package/lib/errors.d.ts.map +1 -0
  33. package/lib/errors.js +39 -0
  34. package/lib/handlers/Chrome111.d.ts +49 -0
  35. package/lib/handlers/Chrome111.d.ts.map +1 -0
  36. package/lib/handlers/Chrome111.js +658 -0
  37. package/lib/handlers/Chrome55.d.ts +51 -0
  38. package/lib/handlers/Chrome55.d.ts.map +1 -0
  39. package/lib/handlers/Chrome55.js +542 -0
  40. package/lib/handlers/Chrome67.d.ts +51 -0
  41. package/lib/handlers/Chrome67.d.ts.map +1 -0
  42. package/lib/handlers/Chrome67.js +598 -0
  43. package/lib/handlers/Chrome70.d.ts +49 -0
  44. package/lib/handlers/Chrome70.d.ts.map +1 -0
  45. package/lib/handlers/Chrome70.js +627 -0
  46. package/lib/handlers/Chrome74.d.ts +49 -0
  47. package/lib/handlers/Chrome74.d.ts.map +1 -0
  48. package/lib/handlers/Chrome74.js +675 -0
  49. package/lib/handlers/Edge11.d.ts +55 -0
  50. package/lib/handlers/Edge11.d.ts.map +1 -0
  51. package/lib/handlers/Edge11.js +447 -0
  52. package/lib/handlers/FakeHandler.d.ts +49 -0
  53. package/lib/handlers/FakeHandler.d.ts.map +1 -0
  54. package/lib/handlers/FakeHandler.js +276 -0
  55. package/lib/handlers/Firefox60.d.ts +48 -0
  56. package/lib/handlers/Firefox60.d.ts.map +1 -0
  57. package/lib/handlers/Firefox60.js +681 -0
  58. package/lib/handlers/HandlerInterface.d.ts +97 -0
  59. package/lib/handlers/HandlerInterface.d.ts.map +1 -0
  60. package/lib/handlers/HandlerInterface.js +10 -0
  61. package/lib/handlers/ReactNative.d.ts +51 -0
  62. package/lib/handlers/ReactNative.d.ts.map +1 -0
  63. package/lib/handlers/ReactNative.js +558 -0
  64. package/lib/handlers/ReactNativeUnifiedPlan.d.ts +49 -0
  65. package/lib/handlers/ReactNativeUnifiedPlan.d.ts.map +1 -0
  66. package/lib/handlers/ReactNativeUnifiedPlan.js +697 -0
  67. package/lib/handlers/Safari11.d.ts +51 -0
  68. package/lib/handlers/Safari11.d.ts.map +1 -0
  69. package/lib/handlers/Safari11.js +592 -0
  70. package/lib/handlers/Safari12.d.ts +49 -0
  71. package/lib/handlers/Safari12.d.ts.map +1 -0
  72. package/lib/handlers/Safari12.js +648 -0
  73. package/lib/handlers/ortc/edgeUtils.d.ts +11 -0
  74. package/lib/handlers/ortc/edgeUtils.d.ts.map +1 -0
  75. package/lib/handlers/ortc/edgeUtils.js +86 -0
  76. package/lib/handlers/ortc/utils.d.ts +6 -0
  77. package/lib/handlers/ortc/utils.d.ts.map +1 -0
  78. package/lib/handlers/ortc/utils.js +20 -0
  79. package/lib/handlers/sdp/MediaSection.d.ts +68 -0
  80. package/lib/handlers/sdp/MediaSection.d.ts.map +1 -0
  81. package/lib/handlers/sdp/MediaSection.js +589 -0
  82. package/lib/handlers/sdp/RemoteSdp.d.ts +75 -0
  83. package/lib/handlers/sdp/RemoteSdp.d.ts.map +1 -0
  84. package/lib/handlers/sdp/RemoteSdp.js +303 -0
  85. package/lib/handlers/sdp/commonUtils.d.ts +24 -0
  86. package/lib/handlers/sdp/commonUtils.d.ts.map +1 -0
  87. package/lib/handlers/sdp/commonUtils.js +224 -0
  88. package/lib/handlers/sdp/plainRtpUtils.d.ts +14 -0
  89. package/lib/handlers/sdp/plainRtpUtils.d.ts.map +1 -0
  90. package/lib/handlers/sdp/plainRtpUtils.js +33 -0
  91. package/lib/handlers/sdp/planBUtils.d.ts +14 -0
  92. package/lib/handlers/sdp/planBUtils.d.ts.map +1 -0
  93. package/lib/handlers/sdp/planBUtils.js +155 -0
  94. package/lib/handlers/sdp/unifiedPlanUtils.d.ts +12 -0
  95. package/lib/handlers/sdp/unifiedPlanUtils.d.ts.map +1 -0
  96. package/lib/handlers/sdp/unifiedPlanUtils.js +131 -0
  97. package/lib/index.d.ts +14 -1435
  98. package/lib/index.d.ts.map +1 -0
  99. package/lib/index.js +43 -11884
  100. package/lib/ortc.d.ts +123 -0
  101. package/lib/ortc.d.ts.map +1 -0
  102. package/lib/ortc.js +943 -0
  103. package/lib/scalabilityModes.d.ts +6 -0
  104. package/lib/scalabilityModes.d.ts.map +1 -0
  105. package/lib/scalabilityModes.js +20 -0
  106. package/lib/tests/fakeParameters.d.ts +20 -0
  107. package/lib/tests/fakeParameters.d.ts.map +1 -0
  108. package/lib/tests/fakeParameters.js +659 -0
  109. package/lib/tests/test.d.ts +6 -0
  110. package/lib/tests/test.d.ts.map +1 -0
  111. package/lib/tests/test.js +1297 -0
  112. package/lib/tests/uaTestCases.d.ts +9 -0
  113. package/lib/tests/uaTestCases.d.ts.map +1 -0
  114. package/lib/tests/uaTestCases.js +85 -0
  115. package/lib/types.d.ts +15 -0
  116. package/lib/types.d.ts.map +1 -0
  117. package/lib/types.js +26 -0
  118. package/lib/utils.d.ts +9 -0
  119. package/lib/utils.d.ts.map +1 -0
  120. package/lib/utils.js +20 -0
  121. package/npm-scripts.mjs +2 -3
  122. package/package.json +4 -9
  123. package/lib/index.js.map +0 -1
  124. package/lib/index.mjs +0 -11860
  125. package/lib/index.mjs.map +0 -1
@@ -0,0 +1,1297 @@
1
+ "use strict";
2
+ /**
3
+ * This test runs in Node so no browser auto-detection is done. Instead, a
4
+ * FakeHandler device is used.
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || function (mod) {
23
+ if (mod && mod.__esModule) return mod;
24
+ var result = {};
25
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
26
+ __setModuleDefault(result, mod);
27
+ return result;
28
+ };
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ const sdpTransform = __importStar(require("sdp-transform"));
31
+ const fake_mediastreamtrack_1 = require("fake-mediastreamtrack");
32
+ const mediasoupClient = __importStar(require("../"));
33
+ const errors_1 = require("../errors");
34
+ const utils = __importStar(require("../utils"));
35
+ const RemoteSdp_1 = require("../handlers/sdp/RemoteSdp");
36
+ const FakeHandler_1 = require("../handlers/FakeHandler");
37
+ const fakeParameters = __importStar(require("./fakeParameters"));
38
+ const uaTestCases_1 = require("./uaTestCases");
39
+ const { Device, detectDevice, parseScalabilityMode, debug } = mediasoupClient;
40
+ let device;
41
+ let sendTransport;
42
+ let recvTransport;
43
+ let audioProducer;
44
+ let videoProducer;
45
+ let audioConsumer;
46
+ let videoConsumer;
47
+ let dataProducer;
48
+ let dataConsumer;
49
+ test('mediasoup-client exposes debug dependency', () => {
50
+ expect(typeof debug).toBe('function');
51
+ }, 500);
52
+ test('detectDevice() returns nothing in Node', () => {
53
+ expect(detectDevice()).toBe(undefined);
54
+ });
55
+ test('create a Device in Node without custom handlerName/handlerFactory throws UnsupportedError', () => {
56
+ expect(() => new Device())
57
+ .toThrow(errors_1.UnsupportedError);
58
+ });
59
+ test('create a Device with an unknown handlerName string throws TypeError', () => {
60
+ // @ts-ignore
61
+ expect(() => new Device({ handlerName: 'FooBrowser666' }))
62
+ .toThrow(TypeError);
63
+ });
64
+ test('create a Device in Node with a valid handlerFactory succeeds', () => {
65
+ device = new Device({ handlerFactory: FakeHandler_1.FakeHandler.createFactory(fakeParameters) });
66
+ expect(typeof device).toBe('object');
67
+ expect(device.handlerName).toBe('FakeHandler');
68
+ expect(device.loaded).toBe(false);
69
+ });
70
+ test('device.rtpCapabilities getter throws InvalidStateError if not loaded', () => {
71
+ expect(() => device.rtpCapabilities)
72
+ .toThrow(errors_1.InvalidStateError);
73
+ });
74
+ test('device.sctpCapabilities getter throws InvalidStateError if not loaded', () => {
75
+ expect(() => device.sctpCapabilities)
76
+ .toThrow(errors_1.InvalidStateError);
77
+ });
78
+ test('device.canProduce() throws InvalidStateError if not loaded', () => {
79
+ expect(() => device.canProduce('audio'))
80
+ .toThrow(errors_1.InvalidStateError);
81
+ });
82
+ test('device.createSendTransport() throws InvalidStateError if not loaded', () => {
83
+ const { id, iceParameters, iceCandidates, dtlsParameters, sctpParameters } = fakeParameters.generateTransportRemoteParameters();
84
+ expect(() => device.createSendTransport({
85
+ id,
86
+ iceParameters,
87
+ iceCandidates,
88
+ dtlsParameters,
89
+ sctpParameters
90
+ }))
91
+ .toThrow(errors_1.InvalidStateError);
92
+ });
93
+ test('device.load() without routerRtpCapabilities rejects with TypeError', async () => {
94
+ // @ts-ignore
95
+ await expect(device.load({}))
96
+ .rejects
97
+ .toThrow(TypeError);
98
+ expect(device.loaded).toBe(false);
99
+ }, 500);
100
+ test('device.load() with invalid routerRtpCapabilities rejects with TypeError', async () => {
101
+ // Clonse fake router RTP capabilities to make them invalid.
102
+ const routerRtpCapabilities = utils.clone(fakeParameters.generateRouterRtpCapabilities(), {});
103
+ for (const codec of routerRtpCapabilities.codecs) {
104
+ delete codec.mimeType;
105
+ }
106
+ await expect(device.load({ routerRtpCapabilities }))
107
+ .rejects
108
+ .toThrow(TypeError);
109
+ expect(device.loaded).toBe(false);
110
+ }, 500);
111
+ test('device.load() succeeds', async () => {
112
+ // Assume we get the router RTP capabilities.
113
+ const routerRtpCapabilities = fakeParameters.generateRouterRtpCapabilities();
114
+ await expect(device.load({ routerRtpCapabilities }))
115
+ .resolves
116
+ .toBe(undefined);
117
+ expect(device.loaded).toBe(true);
118
+ }, 500);
119
+ test('device.load() rejects with InvalidStateError if already loaded', async () => {
120
+ // @ts-ignore
121
+ await expect(device.load({}))
122
+ .rejects
123
+ .toThrow(errors_1.InvalidStateError);
124
+ expect(device.loaded).toBe(true);
125
+ }, 500);
126
+ test('device.rtpCapabilities getter succeeds', () => {
127
+ expect(typeof device.rtpCapabilities).toBe('object');
128
+ });
129
+ test('device.sctpCapabilities getter succeeds', () => {
130
+ expect(typeof device.sctpCapabilities).toBe('object');
131
+ });
132
+ test('device.canProduce() with "audio"/"video" kind returns true', () => {
133
+ expect(device.canProduce('audio')).toBe(true);
134
+ expect(device.canProduce('video')).toBe(true);
135
+ });
136
+ test('device.canProduce() with invalid kind throws TypeError', () => {
137
+ // @ts-ignore
138
+ expect(() => device.canProduce('chicken'))
139
+ .toThrow(TypeError);
140
+ });
141
+ test('device.createSendTransport() for sending media succeeds', () => {
142
+ // Assume we create a transport in the server and get its remote parameters.
143
+ const { id, iceParameters, iceCandidates, dtlsParameters, sctpParameters } = fakeParameters.generateTransportRemoteParameters();
144
+ sendTransport = device.createSendTransport({
145
+ id,
146
+ iceParameters,
147
+ iceCandidates,
148
+ dtlsParameters,
149
+ sctpParameters,
150
+ appData: { foo: 123 }
151
+ });
152
+ expect(typeof sendTransport).toBe('object');
153
+ expect(sendTransport.id).toBe(id);
154
+ expect(sendTransport.closed).toBe(false);
155
+ expect(sendTransport.direction).toBe('send');
156
+ expect(typeof sendTransport.handler).toBe('object');
157
+ expect(sendTransport.handler instanceof FakeHandler_1.FakeHandler).toBe(true);
158
+ expect(sendTransport.connectionState).toBe('new');
159
+ expect(sendTransport.appData).toEqual({ foo: 123 });
160
+ });
161
+ test('device.createRecvTransport() for receiving media succeeds', () => {
162
+ // Assume we create a transport in the server and get its remote parameters.
163
+ const { id, iceParameters, iceCandidates, dtlsParameters, sctpParameters } = fakeParameters.generateTransportRemoteParameters();
164
+ recvTransport = device.createRecvTransport({
165
+ id,
166
+ iceParameters,
167
+ iceCandidates,
168
+ dtlsParameters,
169
+ sctpParameters
170
+ });
171
+ expect(typeof recvTransport).toBe('object');
172
+ expect(recvTransport.id).toBe(id);
173
+ expect(recvTransport.closed).toBe(false);
174
+ expect(recvTransport.direction).toBe('recv');
175
+ expect(typeof recvTransport.handler).toBe('object');
176
+ expect(recvTransport.handler instanceof FakeHandler_1.FakeHandler).toBe(true);
177
+ expect(recvTransport.connectionState).toBe('new');
178
+ expect(recvTransport.appData).toEqual({});
179
+ });
180
+ test('device.createSendTransport() with missing remote Transport parameters throws TypeError', () => {
181
+ // @ts-ignore
182
+ expect(() => device.createSendTransport({ id: '1234' }))
183
+ .toThrow(TypeError);
184
+ // @ts-ignore
185
+ expect(() => device.createSendTransport({ id: '1234', iceParameters: {} }))
186
+ .toThrow(TypeError);
187
+ expect(() => device.createSendTransport({
188
+ id: '1234',
189
+ // @ts-ignore
190
+ iceParameters: {},
191
+ iceCandidates: []
192
+ }))
193
+ .toThrow(TypeError);
194
+ });
195
+ test('device.createRecvTransport() with a non object appData throws TypeError', () => {
196
+ const { id, iceParameters, iceCandidates, dtlsParameters, sctpParameters } = fakeParameters.generateTransportRemoteParameters();
197
+ expect(() => device.createRecvTransport({
198
+ id,
199
+ iceParameters,
200
+ iceCandidates,
201
+ dtlsParameters,
202
+ sctpParameters,
203
+ // @ts-ignore
204
+ appData: 1234
205
+ }))
206
+ .toThrow(TypeError);
207
+ });
208
+ test('transport.produce() without "connect" listener rejects', async () => {
209
+ const audioTrack = new fake_mediastreamtrack_1.FakeMediaStreamTrack({ kind: 'audio' });
210
+ await expect(sendTransport.produce({ track: audioTrack }))
211
+ .rejects
212
+ .toThrow(Error);
213
+ }, 500);
214
+ test('transport.produce() succeeds', async () => {
215
+ var _a, _b;
216
+ const audioTrack = new fake_mediastreamtrack_1.FakeMediaStreamTrack({ kind: 'audio' });
217
+ const videoTrack = new fake_mediastreamtrack_1.FakeMediaStreamTrack({ kind: 'video' });
218
+ let audioProducerId;
219
+ let videoProducerId;
220
+ let connectEventNumTimesCalled = 0;
221
+ let produceEventNumTimesCalled = 0;
222
+ // eslint-disable-next-line no-unused-vars
223
+ sendTransport.on('connect', ({ dtlsParameters }, callback /* errback */) => {
224
+ connectEventNumTimesCalled++;
225
+ expect(typeof dtlsParameters).toBe('object');
226
+ // Emulate communication with the server and success response (no response
227
+ // data needed).
228
+ setTimeout(callback);
229
+ });
230
+ // eslint-disable-next-line no-unused-vars
231
+ sendTransport.on('produce', ({ kind, rtpParameters, appData }, callback /* errback */) => {
232
+ produceEventNumTimesCalled++;
233
+ expect(typeof kind).toBe('string');
234
+ expect(typeof rtpParameters).toBe('object');
235
+ let id;
236
+ switch (kind) {
237
+ case 'audio':
238
+ {
239
+ expect(appData).toEqual({ foo: 'FOO' });
240
+ id = fakeParameters.generateProducerRemoteParameters().id;
241
+ audioProducerId = id;
242
+ break;
243
+ }
244
+ case 'video':
245
+ {
246
+ expect(appData).toEqual({});
247
+ id = fakeParameters.generateProducerRemoteParameters().id;
248
+ videoProducerId = id;
249
+ break;
250
+ }
251
+ default:
252
+ {
253
+ throw new Error('unknown kind');
254
+ }
255
+ }
256
+ // Emulate communication with the server and success response with Producer
257
+ // remote parameters.
258
+ setTimeout(() => callback({ id }));
259
+ });
260
+ let codecs;
261
+ let headerExtensions;
262
+ let encodings;
263
+ let rtcp;
264
+ // Pause the audio track before creating its Producer.
265
+ audioTrack.enabled = false;
266
+ // Use stopTracks: false.
267
+ audioProducer = await sendTransport.produce({ track: audioTrack, stopTracks: false, appData: { foo: 'FOO' } });
268
+ expect(connectEventNumTimesCalled).toBe(1);
269
+ expect(produceEventNumTimesCalled).toBe(1);
270
+ expect(typeof audioProducer).toBe('object');
271
+ expect(audioProducer.id).toBe(audioProducerId);
272
+ expect(audioProducer.closed).toBe(false);
273
+ expect(audioProducer.kind).toBe('audio');
274
+ expect(audioProducer.track).toBe(audioTrack);
275
+ expect(typeof audioProducer.rtpParameters).toBe('object');
276
+ expect(typeof audioProducer.rtpParameters.mid).toBe('string');
277
+ expect(audioProducer.rtpParameters.codecs.length).toBe(1);
278
+ codecs = audioProducer.rtpParameters.codecs;
279
+ expect(codecs[0]).toEqual({
280
+ mimeType: 'audio/opus',
281
+ payloadType: 111,
282
+ clockRate: 48000,
283
+ channels: 2,
284
+ rtcpFeedback: [
285
+ { type: 'transport-cc', parameter: '' }
286
+ ],
287
+ parameters: {
288
+ minptime: 10,
289
+ useinbandfec: 1
290
+ }
291
+ });
292
+ headerExtensions = audioProducer.rtpParameters.headerExtensions;
293
+ expect(headerExtensions).toEqual([
294
+ {
295
+ uri: 'urn:ietf:params:rtp-hdrext:sdes:mid',
296
+ id: 1,
297
+ encrypt: false,
298
+ parameters: {}
299
+ },
300
+ {
301
+ uri: 'urn:ietf:params:rtp-hdrext:ssrc-audio-level',
302
+ id: 10,
303
+ encrypt: false,
304
+ parameters: {}
305
+ }
306
+ ]);
307
+ encodings = audioProducer.rtpParameters.encodings;
308
+ expect(Array.isArray(encodings)).toBe(true);
309
+ expect(encodings.length).toBe(1);
310
+ expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[0])).toBe('object');
311
+ expect(Object.keys(encodings[0])).toEqual(['ssrc', 'dtx']);
312
+ expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[0].ssrc)).toBe('number');
313
+ rtcp = audioProducer.rtpParameters.rtcp;
314
+ expect(typeof rtcp).toBe('object');
315
+ expect(typeof (rtcp === null || rtcp === void 0 ? void 0 : rtcp.cname)).toBe('string');
316
+ expect(audioProducer.paused).toBe(true);
317
+ expect(audioProducer.maxSpatialLayer).toBe(undefined);
318
+ expect(audioProducer.appData).toEqual({ foo: 'FOO' });
319
+ // Reset the audio paused state.
320
+ audioProducer.resume();
321
+ const videoEncodings = [
322
+ { maxBitrate: 100000 },
323
+ { maxBitrate: 500000 }
324
+ ];
325
+ // Note that stopTracks is not give so it's true by default.
326
+ // Use disableTrackOnPause: false and zeroRtpOnPause: true
327
+ videoProducer = await sendTransport.produce({
328
+ track: videoTrack,
329
+ encodings: videoEncodings,
330
+ disableTrackOnPause: false,
331
+ zeroRtpOnPause: true
332
+ });
333
+ expect(connectEventNumTimesCalled).toBe(1);
334
+ expect(produceEventNumTimesCalled).toBe(2);
335
+ expect(typeof videoProducer).toBe('object');
336
+ expect(videoProducer.id).toBe(videoProducerId);
337
+ expect(videoProducer.closed).toBe(false);
338
+ expect(videoProducer.kind).toBe('video');
339
+ expect(videoProducer.track).toBe(videoTrack);
340
+ expect(typeof videoProducer.rtpParameters).toBe('object');
341
+ expect(typeof videoProducer.rtpParameters.mid).toBe('string');
342
+ expect(videoProducer.rtpParameters.codecs.length).toBe(2);
343
+ codecs = videoProducer.rtpParameters.codecs;
344
+ expect(codecs[0]).toEqual({
345
+ mimeType: 'video/VP8',
346
+ payloadType: 96,
347
+ clockRate: 90000,
348
+ rtcpFeedback: [
349
+ { type: 'goog-remb', parameter: '' },
350
+ { type: 'transport-cc', parameter: '' },
351
+ { type: 'ccm', parameter: 'fir' },
352
+ { type: 'nack', parameter: '' },
353
+ { type: 'nack', parameter: 'pli' }
354
+ ],
355
+ parameters: {
356
+ baz: '1234abcd'
357
+ }
358
+ });
359
+ expect(codecs[1]).toEqual({
360
+ mimeType: 'video/rtx',
361
+ payloadType: 97,
362
+ clockRate: 90000,
363
+ rtcpFeedback: [],
364
+ parameters: {
365
+ apt: 96
366
+ }
367
+ });
368
+ headerExtensions = videoProducer.rtpParameters.headerExtensions;
369
+ expect(headerExtensions).toEqual([
370
+ {
371
+ uri: 'urn:ietf:params:rtp-hdrext:sdes:mid',
372
+ id: 1,
373
+ encrypt: false,
374
+ parameters: {}
375
+ },
376
+ {
377
+ uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time',
378
+ id: 3,
379
+ encrypt: false,
380
+ parameters: {}
381
+ },
382
+ {
383
+ uri: 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01',
384
+ id: 5,
385
+ encrypt: false,
386
+ parameters: {}
387
+ },
388
+ {
389
+ uri: 'urn:3gpp:video-orientation',
390
+ id: 4,
391
+ encrypt: false,
392
+ parameters: {}
393
+ },
394
+ {
395
+ uri: 'urn:ietf:params:rtp-hdrext:toffset',
396
+ id: 2,
397
+ encrypt: false,
398
+ parameters: {}
399
+ }
400
+ ]);
401
+ encodings = videoProducer.rtpParameters.encodings;
402
+ expect(Array.isArray(encodings)).toBe(true);
403
+ expect(encodings.length).toBe(2);
404
+ expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[0])).toBe('object');
405
+ expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[0].ssrc)).toBe('number');
406
+ expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[0].rtx)).toBe('object');
407
+ expect(Object.keys(encodings[0].rtx)).toEqual(['ssrc']);
408
+ expect(typeof ((_a = encodings === null || encodings === void 0 ? void 0 : encodings[0].rtx) === null || _a === void 0 ? void 0 : _a.ssrc)).toBe('number');
409
+ expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[1])).toBe('object');
410
+ expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[1].ssrc)).toBe('number');
411
+ expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[1].rtx)).toBe('object');
412
+ expect(Object.keys(encodings[1].rtx)).toEqual(['ssrc']);
413
+ expect(typeof ((_b = encodings === null || encodings === void 0 ? void 0 : encodings[1].rtx) === null || _b === void 0 ? void 0 : _b.ssrc)).toBe('number');
414
+ rtcp = videoProducer.rtpParameters.rtcp;
415
+ expect(typeof rtcp).toBe('object');
416
+ expect(typeof (rtcp === null || rtcp === void 0 ? void 0 : rtcp.cname)).toBe('string');
417
+ expect(videoProducer.paused).toBe(false);
418
+ expect(videoProducer.maxSpatialLayer).toBe(undefined);
419
+ expect(videoProducer.appData).toEqual({});
420
+ sendTransport.removeAllListeners('connect');
421
+ sendTransport.removeAllListeners('produce');
422
+ }, 500);
423
+ test('transport.produce() without track rejects with TypeError', async () => {
424
+ await expect(sendTransport.produce({}))
425
+ .rejects
426
+ .toThrow(TypeError);
427
+ }, 500);
428
+ test('transport.produce() in a receiving Transport rejects with UnsupportedError', async () => {
429
+ const track = new fake_mediastreamtrack_1.FakeMediaStreamTrack({ kind: 'audio' });
430
+ await expect(recvTransport.produce({ track }))
431
+ .rejects
432
+ .toThrow(errors_1.UnsupportedError);
433
+ }, 500);
434
+ test('transport.produce() with an ended track rejects with InvalidStateError', async () => {
435
+ const track = new fake_mediastreamtrack_1.FakeMediaStreamTrack({ kind: 'audio' });
436
+ track.stop();
437
+ await expect(sendTransport.produce({ track }))
438
+ .rejects
439
+ .toThrow(errors_1.InvalidStateError);
440
+ }, 500);
441
+ test('transport.produce() with a non object appData rejects with TypeError', async () => {
442
+ const track = new fake_mediastreamtrack_1.FakeMediaStreamTrack({ kind: 'audio' });
443
+ // @ts-ignore
444
+ await expect(sendTransport.produce({ track, appData: true }))
445
+ .rejects
446
+ .toThrow(TypeError);
447
+ }, 500);
448
+ test('transport.consume() succeeds', async () => {
449
+ var _a;
450
+ const audioConsumerRemoteParameters = fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'audio/opus' });
451
+ const videoConsumerRemoteParameters = fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'video/VP8' });
452
+ let connectEventNumTimesCalled = 0;
453
+ // eslint-disable-next-line no-unused-vars
454
+ recvTransport.on('connect', ({ dtlsParameters }, callback /* errback */) => {
455
+ connectEventNumTimesCalled++;
456
+ expect(typeof dtlsParameters).toBe('object');
457
+ // Emulate communication with the server and success response (no response
458
+ // data needed).
459
+ setTimeout(callback);
460
+ });
461
+ let codecs;
462
+ let headerExtensions;
463
+ let encodings;
464
+ let rtcp;
465
+ audioConsumer = await recvTransport.consume({
466
+ id: audioConsumerRemoteParameters.id,
467
+ producerId: audioConsumerRemoteParameters.producerId,
468
+ kind: audioConsumerRemoteParameters.kind,
469
+ rtpParameters: audioConsumerRemoteParameters.rtpParameters,
470
+ appData: { bar: 'BAR' }
471
+ });
472
+ expect(connectEventNumTimesCalled).toBe(1);
473
+ expect(typeof audioConsumer).toBe('object');
474
+ expect(audioConsumer.id).toBe(audioConsumerRemoteParameters.id);
475
+ expect(audioConsumer.producerId).toBe(audioConsumerRemoteParameters.producerId);
476
+ expect(audioConsumer.closed).toBe(false);
477
+ expect(audioConsumer.kind).toBe('audio');
478
+ expect(typeof audioConsumer.track).toBe('object');
479
+ expect(typeof audioConsumer.rtpParameters).toBe('object');
480
+ expect(audioConsumer.rtpParameters.mid).toBe(undefined);
481
+ expect(audioConsumer.rtpParameters.codecs.length).toBe(1);
482
+ codecs = audioConsumer.rtpParameters.codecs;
483
+ expect(codecs[0]).toEqual({
484
+ mimeType: 'audio/opus',
485
+ payloadType: 100,
486
+ clockRate: 48000,
487
+ channels: 2,
488
+ rtcpFeedback: [
489
+ { type: 'transport-cc', parameter: '' }
490
+ ],
491
+ parameters: {
492
+ useinbandfec: 1,
493
+ foo: 'bar'
494
+ }
495
+ });
496
+ headerExtensions = audioConsumer.rtpParameters.headerExtensions;
497
+ expect(headerExtensions).toEqual([
498
+ {
499
+ uri: 'urn:ietf:params:rtp-hdrext:sdes:mid',
500
+ id: 1,
501
+ encrypt: false,
502
+ parameters: {}
503
+ },
504
+ {
505
+ uri: 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01',
506
+ id: 5,
507
+ encrypt: false,
508
+ parameters: {}
509
+ },
510
+ {
511
+ uri: 'urn:ietf:params:rtp-hdrext:ssrc-audio-level',
512
+ id: 10,
513
+ encrypt: false,
514
+ parameters: {}
515
+ }
516
+ ]);
517
+ encodings = audioConsumer.rtpParameters.encodings;
518
+ expect(Array.isArray(encodings)).toBe(true);
519
+ expect(encodings.length).toBe(1);
520
+ expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[0])).toBe('object');
521
+ expect(Object.keys(encodings[0])).toEqual(['ssrc', 'dtx']);
522
+ expect(typeof encodings[0].ssrc).toBe('number');
523
+ rtcp = audioProducer.rtpParameters.rtcp;
524
+ expect(typeof rtcp).toBe('object');
525
+ expect(typeof (rtcp === null || rtcp === void 0 ? void 0 : rtcp.cname)).toBe('string');
526
+ expect(audioConsumer.paused).toBe(false);
527
+ expect(audioConsumer.appData).toEqual({ bar: 'BAR' });
528
+ videoConsumer = await recvTransport.consume({
529
+ id: videoConsumerRemoteParameters.id,
530
+ producerId: videoConsumerRemoteParameters.producerId,
531
+ kind: videoConsumerRemoteParameters.kind,
532
+ rtpParameters: videoConsumerRemoteParameters.rtpParameters
533
+ });
534
+ expect(connectEventNumTimesCalled).toBe(1);
535
+ expect(typeof videoConsumer).toBe('object');
536
+ expect(videoConsumer.id).toBe(videoConsumerRemoteParameters.id);
537
+ expect(videoConsumer.producerId).toBe(videoConsumerRemoteParameters.producerId);
538
+ expect(videoConsumer.closed).toBe(false);
539
+ expect(videoConsumer.kind).toBe('video');
540
+ expect(typeof videoConsumer.track).toBe('object');
541
+ expect(typeof videoConsumer.rtpParameters).toBe('object');
542
+ expect(videoConsumer.rtpParameters.mid).toBe(undefined);
543
+ expect(videoConsumer.rtpParameters.codecs.length).toBe(2);
544
+ codecs = videoConsumer.rtpParameters.codecs;
545
+ expect(codecs[0]).toEqual({
546
+ mimeType: 'video/VP8',
547
+ payloadType: 101,
548
+ clockRate: 90000,
549
+ rtcpFeedback: [
550
+ { type: 'nack', parameter: '' },
551
+ { type: 'nack', parameter: 'pli' },
552
+ { type: 'ccm', parameter: 'fir' },
553
+ { type: 'goog-remb', parameter: '' },
554
+ { type: 'transport-cc', parameter: '' }
555
+ ],
556
+ parameters: {
557
+ 'x-google-start-bitrate': 1500
558
+ }
559
+ });
560
+ expect(codecs[1]).toEqual({
561
+ mimeType: 'video/rtx',
562
+ payloadType: 102,
563
+ clockRate: 90000,
564
+ rtcpFeedback: [],
565
+ parameters: {
566
+ apt: 101
567
+ }
568
+ });
569
+ headerExtensions = videoConsumer.rtpParameters.headerExtensions;
570
+ expect(headerExtensions).toEqual([
571
+ {
572
+ uri: 'urn:ietf:params:rtp-hdrext:sdes:mid',
573
+ id: 1,
574
+ encrypt: false,
575
+ parameters: {}
576
+ },
577
+ {
578
+ uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time',
579
+ id: 4,
580
+ encrypt: false,
581
+ parameters: {}
582
+ },
583
+ {
584
+ uri: 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01',
585
+ id: 5,
586
+ encrypt: false,
587
+ parameters: {}
588
+ },
589
+ {
590
+ uri: 'urn:3gpp:video-orientation',
591
+ id: 11,
592
+ encrypt: false,
593
+ parameters: {}
594
+ },
595
+ {
596
+ uri: 'urn:ietf:params:rtp-hdrext:toffset',
597
+ id: 12,
598
+ encrypt: false,
599
+ parameters: {}
600
+ }
601
+ ]);
602
+ encodings = videoConsumer.rtpParameters.encodings;
603
+ expect(Array.isArray(encodings)).toBe(true);
604
+ expect(encodings.length).toBe(1);
605
+ expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[0])).toBe('object');
606
+ expect(Object.keys(encodings[0])).toEqual(['ssrc', 'rtx', 'dtx']);
607
+ expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[0].ssrc)).toBe('number');
608
+ expect(typeof (encodings === null || encodings === void 0 ? void 0 : encodings[0].rtx)).toBe('object');
609
+ expect(Object.keys(encodings[0].rtx)).toEqual(['ssrc']);
610
+ expect(typeof ((_a = encodings === null || encodings === void 0 ? void 0 : encodings[0].rtx) === null || _a === void 0 ? void 0 : _a.ssrc)).toBe('number');
611
+ rtcp = videoConsumer.rtpParameters.rtcp;
612
+ expect(typeof rtcp).toBe('object');
613
+ expect(typeof (rtcp === null || rtcp === void 0 ? void 0 : rtcp.cname)).toBe('string');
614
+ expect(videoConsumer.paused).toBe(false);
615
+ expect(videoConsumer.appData).toEqual({});
616
+ recvTransport.removeAllListeners('connect');
617
+ }, 500);
618
+ test('transport.consume() batches consumers created in same macrotask into the same task', async () => {
619
+ const videoConsumerRemoteParameters1 = fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'video/VP8' });
620
+ const videoConsumerRemoteParameters2 = fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'video/VP8' });
621
+ const pushSpy = jest.spyOn(recvTransport._awaitQueue, 'push');
622
+ const waitForConsumer = (id) => {
623
+ return new Promise((resolve) => {
624
+ recvTransport.observer.on('newconsumer', (consumer) => {
625
+ if (consumer.id === id) {
626
+ resolve();
627
+ }
628
+ });
629
+ });
630
+ };
631
+ const allConsumersCreated = Promise.all([
632
+ waitForConsumer(videoConsumerRemoteParameters1.id),
633
+ waitForConsumer(videoConsumerRemoteParameters2.id)
634
+ ]);
635
+ await Promise.all([
636
+ recvTransport.consume({
637
+ id: videoConsumerRemoteParameters1.id,
638
+ producerId: videoConsumerRemoteParameters1.producerId,
639
+ kind: videoConsumerRemoteParameters1.kind,
640
+ rtpParameters: videoConsumerRemoteParameters1.rtpParameters
641
+ }),
642
+ recvTransport.consume({
643
+ id: videoConsumerRemoteParameters2.id,
644
+ producerId: videoConsumerRemoteParameters2.producerId,
645
+ kind: videoConsumerRemoteParameters2.kind,
646
+ rtpParameters: videoConsumerRemoteParameters2.rtpParameters
647
+ })
648
+ ]);
649
+ await allConsumersCreated;
650
+ expect(pushSpy).toBeCalledTimes(1);
651
+ }, 500);
652
+ test('transport.consume() without remote Consumer parameters rejects with TypeError', async () => {
653
+ // @ts-ignore
654
+ await expect(recvTransport.consume({}))
655
+ .rejects
656
+ .toThrow(TypeError);
657
+ }, 500);
658
+ test('transport.consume() with missing remote Consumer parameters rejects with TypeError', async () => {
659
+ // @ts-ignore
660
+ await expect(recvTransport.consume({ id: '1234' }))
661
+ .rejects
662
+ .toThrow(TypeError);
663
+ // @ts-ignore
664
+ await expect(recvTransport.consume({ id: '1234', producerId: '4444' }))
665
+ .rejects
666
+ .toThrow(TypeError);
667
+ await expect(recvTransport.consume(
668
+ // @ts-ignore
669
+ {
670
+ id: '1234',
671
+ producerId: '4444',
672
+ kind: 'audio'
673
+ }))
674
+ .rejects
675
+ .toThrow(TypeError);
676
+ await expect(recvTransport.consume(
677
+ // @ts-ignore
678
+ {
679
+ id: '1234',
680
+ producerId: '4444',
681
+ kind: 'audio'
682
+ }))
683
+ .rejects
684
+ .toThrow(TypeError);
685
+ }, 500);
686
+ test('transport.consume() in a sending Transport rejects with UnsupportedError', async () => {
687
+ const { id, producerId, kind, rtpParameters } = fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'audio/opus' });
688
+ await expect(sendTransport.consume({
689
+ id,
690
+ producerId,
691
+ kind,
692
+ rtpParameters
693
+ }))
694
+ .rejects
695
+ .toThrow(errors_1.UnsupportedError);
696
+ }, 500);
697
+ test('transport.consume() with unsupported rtpParameters rejects with UnsupportedError', async () => {
698
+ const { id, producerId, kind, rtpParameters } = fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'audio/ISAC' });
699
+ await expect(sendTransport.consume({
700
+ id,
701
+ producerId,
702
+ kind,
703
+ rtpParameters
704
+ }))
705
+ .rejects
706
+ .toThrow(errors_1.UnsupportedError);
707
+ }, 500);
708
+ test('transport.consume() with a non object appData rejects with TypeError', async () => {
709
+ const consumerRemoteParameters = fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'audio/opus' });
710
+ // @ts-ignore
711
+ await expect(recvTransport.consume({ consumerRemoteParameters, appData: true }))
712
+ .rejects
713
+ .toThrow(TypeError);
714
+ }, 500);
715
+ test('transport.produceData() succeeds', async () => {
716
+ let dataProducerId;
717
+ let produceDataEventNumTimesCalled = 0;
718
+ // eslint-disable-next-line no-unused-vars
719
+ sendTransport.on('producedata', ({ sctpStreamParameters, label, protocol, appData }, callback /* errback */) => {
720
+ produceDataEventNumTimesCalled++;
721
+ expect(typeof sctpStreamParameters).toBe('object');
722
+ expect(label).toBe('FOO');
723
+ expect(protocol).toBe('BAR');
724
+ expect(appData).toEqual({ foo: 'FOO' });
725
+ const id = fakeParameters.generateDataProducerRemoteParameters().id;
726
+ dataProducerId = id;
727
+ // Emulate communication with the server and success response with Producer
728
+ // remote parameters.
729
+ setTimeout(() => callback({ id }));
730
+ });
731
+ dataProducer = await sendTransport.produceData({
732
+ ordered: false,
733
+ maxPacketLifeTime: 5555,
734
+ label: 'FOO',
735
+ protocol: 'BAR',
736
+ appData: { foo: 'FOO' }
737
+ });
738
+ expect(produceDataEventNumTimesCalled).toBe(1);
739
+ expect(typeof dataProducer).toBe('object');
740
+ expect(dataProducer.id).toBe(dataProducerId);
741
+ expect(dataProducer.closed).toBe(false);
742
+ expect(typeof dataProducer.sctpStreamParameters).toBe('object');
743
+ expect(typeof dataProducer.sctpStreamParameters.streamId).toBe('number');
744
+ expect(dataProducer.sctpStreamParameters.ordered).toBe(false);
745
+ expect(dataProducer.sctpStreamParameters.maxPacketLifeTime).toBe(5555);
746
+ expect(dataProducer.sctpStreamParameters.maxRetransmits).toBe(undefined);
747
+ expect(dataProducer.label).toBe('FOO');
748
+ expect(dataProducer.protocol).toBe('BAR');
749
+ sendTransport.removeAllListeners('producedata');
750
+ }, 500);
751
+ test('transport.produceData() in a receiving Transport rejects with UnsupportedError', async () => {
752
+ await expect(recvTransport.produceData({}))
753
+ .rejects
754
+ .toThrow(errors_1.UnsupportedError);
755
+ }, 500);
756
+ test('transport.produceData() with a non object appData rejects with TypeError', async () => {
757
+ // @ts-ignore
758
+ await expect(sendTransport.produceData({ appData: true }))
759
+ .rejects
760
+ .toThrow(TypeError);
761
+ }, 500);
762
+ test('transport.consumeData() succeeds', async () => {
763
+ const dataConsumerRemoteParameters = fakeParameters.generateDataConsumerRemoteParameters();
764
+ dataConsumer = await recvTransport.consumeData({
765
+ id: dataConsumerRemoteParameters.id,
766
+ dataProducerId: dataConsumerRemoteParameters.dataProducerId,
767
+ sctpStreamParameters: dataConsumerRemoteParameters.sctpStreamParameters,
768
+ label: 'FOO',
769
+ protocol: 'BAR',
770
+ appData: { bar: 'BAR' }
771
+ });
772
+ expect(typeof dataConsumer).toBe('object');
773
+ expect(dataConsumer.id).toBe(dataConsumerRemoteParameters.id);
774
+ expect(dataConsumer.dataProducerId).toBe(dataConsumerRemoteParameters.dataProducerId);
775
+ expect(dataConsumer.closed).toBe(false);
776
+ expect(typeof dataConsumer.sctpStreamParameters).toBe('object');
777
+ expect(typeof dataConsumer.sctpStreamParameters.streamId).toBe('number');
778
+ expect(dataConsumer.label).toBe('FOO');
779
+ expect(dataConsumer.protocol).toBe('BAR');
780
+ }, 500);
781
+ test('transport.consumeData() without remote DataConsumer parameters rejects with TypeError', async () => {
782
+ // @ts-ignore
783
+ await expect(recvTransport.consumeData({}))
784
+ .rejects
785
+ .toThrow(TypeError);
786
+ }, 500);
787
+ test('transport.consumeData() with missing remote DataConsumer parameters rejects with TypeError', async () => {
788
+ // @ts-ignore
789
+ await expect(recvTransport.consumeData({ id: '1234' }))
790
+ .rejects
791
+ .toThrow(TypeError);
792
+ // @ts-ignore
793
+ await expect(recvTransport.consumeData({ id: '1234', dataProducerId: '4444' }))
794
+ .rejects
795
+ .toThrow(TypeError);
796
+ }, 500);
797
+ test('transport.consumeData() in a sending Transport rejects with UnsupportedError', async () => {
798
+ const { id, dataProducerId, sctpStreamParameters } = fakeParameters.generateDataConsumerRemoteParameters();
799
+ await expect(sendTransport.consumeData({
800
+ id,
801
+ dataProducerId,
802
+ sctpStreamParameters
803
+ }))
804
+ .rejects
805
+ .toThrow(errors_1.UnsupportedError);
806
+ }, 500);
807
+ test('transport.consumeData() with a non object appData rejects with TypeError', async () => {
808
+ const dataConsumerRemoteParameters = fakeParameters.generateDataConsumerRemoteParameters();
809
+ // @ts-ignore
810
+ await expect(recvTransport.consumeData({ dataConsumerRemoteParameters, appData: true }))
811
+ .rejects
812
+ .toThrow(TypeError);
813
+ }, 500);
814
+ test('transport.getStats() succeeds', async () => {
815
+ const stats = await sendTransport.getStats();
816
+ expect(typeof stats).toBe('object');
817
+ }, 500);
818
+ test('transport.restartIce() succeeds', async () => {
819
+ await expect(sendTransport.restartIce({
820
+ iceParameters: {
821
+ usernameFragment: 'foo',
822
+ password: 'xxx'
823
+ }
824
+ }))
825
+ .resolves
826
+ .toBe(undefined);
827
+ }, 500);
828
+ test('transport.restartIce() without remote iceParameters rejects with TypeError', async () => {
829
+ // @ts-ignore
830
+ await expect(sendTransport.restartIce({}))
831
+ .rejects
832
+ .toThrow(TypeError);
833
+ }, 500);
834
+ test('transport.updateIceServers() succeeds', async () => {
835
+ await expect(sendTransport.updateIceServers({ iceServers: [] }))
836
+ .resolves
837
+ .toBe(undefined);
838
+ }, 500);
839
+ test('transport.updateIceServers() without iceServers rejects with TypeError', async () => {
840
+ await expect(sendTransport.updateIceServers({}))
841
+ .rejects
842
+ .toThrow(TypeError);
843
+ }, 500);
844
+ test('connection state change fires "connectionstatechange" in live Transport', () => {
845
+ let connectionStateChangeEventNumTimesCalled = 0;
846
+ sendTransport.on('connectionstatechange', (connectionState) => {
847
+ connectionStateChangeEventNumTimesCalled++;
848
+ expect(connectionState).toBe('completed');
849
+ });
850
+ // @ts-ignore
851
+ sendTransport.handler.setConnectionState('completed');
852
+ expect(connectionStateChangeEventNumTimesCalled).toBe(1);
853
+ expect(sendTransport.connectionState).toBe('completed');
854
+ sendTransport.removeAllListeners('connectionstatechange');
855
+ });
856
+ test('producer.pause() succeeds', () => {
857
+ var _a;
858
+ videoProducer.pause();
859
+ expect(videoProducer.paused).toBe(true);
860
+ // Track will be still enabled due to disableTrackOnPause: false.
861
+ expect((_a = videoProducer.track) === null || _a === void 0 ? void 0 : _a.enabled).toBe(true);
862
+ });
863
+ test('producer.resume() succeeds', () => {
864
+ var _a;
865
+ videoProducer.resume();
866
+ expect(videoProducer.paused).toBe(false);
867
+ expect((_a = videoProducer.track) === null || _a === void 0 ? void 0 : _a.enabled).toBe(true);
868
+ });
869
+ test('producer.replaceTrack() with a new track succeeds', async () => {
870
+ var _a;
871
+ // Have the audio Producer paused.
872
+ audioProducer.pause();
873
+ const audioProducerPreviousTrack = audioProducer.track;
874
+ const newAudioTrack = new fake_mediastreamtrack_1.FakeMediaStreamTrack({ kind: 'audio' });
875
+ await expect(audioProducer.replaceTrack({ track: newAudioTrack }))
876
+ .resolves
877
+ .toBe(undefined);
878
+ // Previous track must be 'live' due to stopTracks: false.
879
+ expect(audioProducerPreviousTrack === null || audioProducerPreviousTrack === void 0 ? void 0 : audioProducerPreviousTrack.readyState).toBe('live');
880
+ expect((_a = audioProducer.track) === null || _a === void 0 ? void 0 : _a.readyState).toBe('live');
881
+ expect(audioProducer.track).not.toBe(audioProducerPreviousTrack);
882
+ expect(audioProducer.track).toBe(newAudioTrack);
883
+ // Producer was already paused.
884
+ expect(audioProducer.paused).toBe(true);
885
+ // Reset the audio paused state.
886
+ audioProducer.resume();
887
+ const videoProducerPreviousTrack = videoProducer.track;
888
+ const newVideoTrack = new fake_mediastreamtrack_1.FakeMediaStreamTrack({ kind: 'video' });
889
+ await expect(videoProducer.replaceTrack({ track: newVideoTrack }))
890
+ .resolves
891
+ .toBe(undefined);
892
+ // Previous track must be 'ended' due to stopTracks: true.
893
+ expect(videoProducerPreviousTrack === null || videoProducerPreviousTrack === void 0 ? void 0 : videoProducerPreviousTrack.readyState).toBe('ended');
894
+ expect(videoProducer.track).not.toBe(videoProducerPreviousTrack);
895
+ expect(videoProducer.track).toBe(newVideoTrack);
896
+ expect(videoProducer.paused).toBe(false);
897
+ }, 500);
898
+ test('producer.replaceTrack() with null succeeds', async () => {
899
+ // Have the audio Producer paused.
900
+ audioProducer.pause();
901
+ const audioProducerPreviousTrack = audioProducer.track;
902
+ await expect(audioProducer.replaceTrack({ track: null }))
903
+ .resolves
904
+ .toBe(undefined);
905
+ // Previous track must be 'live' due to stopTracks: false.
906
+ expect(audioProducerPreviousTrack === null || audioProducerPreviousTrack === void 0 ? void 0 : audioProducerPreviousTrack.readyState).toBe('live');
907
+ expect(audioProducer.track).toBeNull();
908
+ // Producer was already paused.
909
+ expect(audioProducer.paused).toBe(true);
910
+ // Reset the audio paused state.
911
+ audioProducer.resume();
912
+ expect(audioProducer.paused).toBe(false);
913
+ // Manually "mute" the original audio track.
914
+ audioProducerPreviousTrack.enabled = false;
915
+ // Set the original audio track back.
916
+ await expect(audioProducer.replaceTrack({ track: audioProducerPreviousTrack }))
917
+ .resolves
918
+ .toBe(undefined);
919
+ // The given audio track was muted but the Producer was not, so the track
920
+ // must not be muted now.
921
+ expect(audioProducer.paused).toBe(false);
922
+ expect(audioProducerPreviousTrack === null || audioProducerPreviousTrack === void 0 ? void 0 : audioProducerPreviousTrack.enabled).toBe(true);
923
+ // Reset the audio paused state.
924
+ audioProducer.resume();
925
+ }, 500);
926
+ test('producer.replaceTrack() with an ended track rejects with InvalidStateError', async () => {
927
+ var _a;
928
+ const track = new fake_mediastreamtrack_1.FakeMediaStreamTrack({ kind: 'audio' });
929
+ track.stop();
930
+ await expect(videoProducer.replaceTrack({ track }))
931
+ .rejects
932
+ .toThrow(errors_1.InvalidStateError);
933
+ expect(track.readyState).toBe('ended');
934
+ expect((_a = videoProducer.track) === null || _a === void 0 ? void 0 : _a.readyState).toBe('live');
935
+ }, 500);
936
+ test('producer.replaceTrack() with the same track succeeds', async () => {
937
+ var _a;
938
+ await expect(audioProducer.replaceTrack({ track: audioProducer.track }))
939
+ .resolves
940
+ .toBe(undefined);
941
+ expect((_a = audioProducer.track) === null || _a === void 0 ? void 0 : _a.readyState).toBe('live');
942
+ }, 500);
943
+ test('producer.setMaxSpatialLayer() succeeds', async () => {
944
+ await expect(videoProducer.setMaxSpatialLayer(0))
945
+ .resolves
946
+ .toBe(undefined);
947
+ expect(videoProducer.maxSpatialLayer).toBe(0);
948
+ }, 500);
949
+ test('producer.setMaxSpatialLayer() in an audio Producer rejects with UnsupportedError', async () => {
950
+ await expect(audioProducer.setMaxSpatialLayer(1))
951
+ .rejects
952
+ .toThrow(errors_1.UnsupportedError);
953
+ expect(audioProducer.maxSpatialLayer).toBe(undefined);
954
+ }, 500);
955
+ test('producer.setMaxSpatialLayer() with invalid spatialLayer rejects with TypeError', async () => {
956
+ // @ts-ignore
957
+ await expect(videoProducer.setMaxSpatialLayer('chicken'))
958
+ .rejects
959
+ .toThrow(TypeError);
960
+ }, 500);
961
+ test('producer.setMaxSpatialLayer() without spatialLayer rejects with TypeError', async () => {
962
+ // @ts-ignore
963
+ await expect(videoProducer.setMaxSpatialLayer())
964
+ .rejects
965
+ .toThrow(TypeError);
966
+ }, 500);
967
+ test('producer.setRtpEncodingParameters() succeeds', async () => {
968
+ await expect(videoProducer.setRtpEncodingParameters({ scaleResolutionDownBy: 2 }))
969
+ .resolves
970
+ .toBe(undefined);
971
+ expect(videoProducer.maxSpatialLayer).toBe(0);
972
+ }, 500);
973
+ test('producer.getStats() succeeds', async () => {
974
+ const stats = await videoProducer.getStats();
975
+ expect(typeof stats).toBe('object');
976
+ }, 500);
977
+ test('consumer.resume() succeeds', () => {
978
+ videoConsumer.resume();
979
+ expect(videoConsumer.paused).toBe(false);
980
+ });
981
+ test('consumer.pause() succeeds', () => {
982
+ videoConsumer.pause();
983
+ expect(videoConsumer.paused).toBe(true);
984
+ });
985
+ test('consumer.getStats() succeeds', async () => {
986
+ const stats = await videoConsumer.getStats();
987
+ expect(typeof stats).toBe('object');
988
+ }, 500);
989
+ test('producer.close() succeed', () => {
990
+ var _a;
991
+ audioProducer.close();
992
+ expect(audioProducer.closed).toBe(true);
993
+ // Track will be still 'live' due to stopTracks: false.
994
+ expect((_a = audioProducer.track) === null || _a === void 0 ? void 0 : _a.readyState).toBe('live');
995
+ });
996
+ test('producer.replaceTrack() rejects with InvalidStateError if closed', async () => {
997
+ const audioTrack = new fake_mediastreamtrack_1.FakeMediaStreamTrack({ kind: 'audio' });
998
+ await expect(audioProducer.replaceTrack({ track: audioTrack }))
999
+ .rejects
1000
+ .toThrow(errors_1.InvalidStateError);
1001
+ expect(audioTrack.readyState).toBe('live');
1002
+ }, 500);
1003
+ test('producer.getStats() rejects with InvalidStateError if closed', async () => {
1004
+ await expect(audioProducer.getStats())
1005
+ .rejects
1006
+ .toThrow(errors_1.InvalidStateError);
1007
+ }, 500);
1008
+ test('consumer.close() succeed', () => {
1009
+ audioConsumer.close();
1010
+ expect(audioConsumer.closed).toBe(true);
1011
+ expect(audioConsumer.track.readyState).toBe('ended');
1012
+ });
1013
+ test('consumer.getStats() rejects with InvalidStateError if closed', async () => {
1014
+ await expect(audioConsumer.getStats())
1015
+ .rejects
1016
+ .toThrow(errors_1.InvalidStateError);
1017
+ }, 500);
1018
+ test('dataProducer.close() succeed', () => {
1019
+ dataProducer.close();
1020
+ expect(dataProducer.closed).toBe(true);
1021
+ });
1022
+ test('dataConsumer.close() succeed', () => {
1023
+ dataConsumer.close();
1024
+ expect(dataConsumer.closed).toBe(true);
1025
+ });
1026
+ test('remotetely stopped track fires "trackended" in live Producers/Consumers', () => {
1027
+ let audioProducerTrackendedEventCalled = false;
1028
+ let videoProducerTrackendedEventCalled = false;
1029
+ let audiosConsumerTrackendedEventCalled = false;
1030
+ let videoConsumerTrackendedEventCalled = false;
1031
+ audioProducer.on('trackended', () => {
1032
+ audioProducerTrackendedEventCalled = true;
1033
+ });
1034
+ videoProducer.on('trackended', () => {
1035
+ videoProducerTrackendedEventCalled = true;
1036
+ });
1037
+ audioConsumer.on('trackended', () => {
1038
+ audiosConsumerTrackendedEventCalled = true;
1039
+ });
1040
+ videoConsumer.on('trackended', () => {
1041
+ videoConsumerTrackendedEventCalled = true;
1042
+ });
1043
+ // @ts-ignore
1044
+ audioProducer.track.remoteStop();
1045
+ // Audio Producer was already closed.
1046
+ expect(audioProducerTrackendedEventCalled).toBe(false);
1047
+ // @ts-ignore
1048
+ videoProducer.track.remoteStop();
1049
+ expect(videoProducerTrackendedEventCalled).toBe(true);
1050
+ // @ts-ignore
1051
+ audioConsumer.track.remoteStop();
1052
+ // Audio Consumer was already closed.
1053
+ expect(audiosConsumerTrackendedEventCalled).toBe(false);
1054
+ // @ts-ignore
1055
+ videoConsumer.track.remoteStop();
1056
+ expect(videoConsumerTrackendedEventCalled).toBe(true);
1057
+ audioProducer.removeAllListeners();
1058
+ videoProducer.removeAllListeners();
1059
+ audioConsumer.removeAllListeners();
1060
+ videoConsumer.removeAllListeners();
1061
+ });
1062
+ test('transport.close() fires "transportclose" in live Producers/Consumers', () => {
1063
+ let audioProducerTransportcloseEventCalled = false;
1064
+ let videoProducerTransportcloseEventCalled = false;
1065
+ let audioConsumerTransportcloseEventCalled = false;
1066
+ let videoConsumerTransportcloseEventCalled = false;
1067
+ audioProducer.on('transportclose', () => {
1068
+ audioProducerTransportcloseEventCalled = true;
1069
+ });
1070
+ videoProducer.on('transportclose', () => {
1071
+ videoProducerTransportcloseEventCalled = true;
1072
+ });
1073
+ audioConsumer.on('transportclose', () => {
1074
+ audioConsumerTransportcloseEventCalled = true;
1075
+ });
1076
+ videoConsumer.on('transportclose', () => {
1077
+ videoConsumerTransportcloseEventCalled = true;
1078
+ });
1079
+ // Audio Producer was already closed.
1080
+ expect(audioProducer.closed).toBe(true);
1081
+ expect(videoProducer.closed).toBe(false);
1082
+ sendTransport.close();
1083
+ expect(sendTransport.closed).toBe(true);
1084
+ expect(videoProducer.closed).toBe(true);
1085
+ // Audio Producer was already closed.
1086
+ expect(audioProducerTransportcloseEventCalled).toBe(false);
1087
+ expect(videoProducerTransportcloseEventCalled).toBe(true);
1088
+ // Audio Consumer was already closed.
1089
+ expect(audioConsumer.closed).toBe(true);
1090
+ expect(videoConsumer.closed).toBe(false);
1091
+ recvTransport.close();
1092
+ expect(recvTransport.closed).toBe(true);
1093
+ expect(videoConsumer.closed).toBe(true);
1094
+ // Audio Consumer was already closed.
1095
+ expect(audioConsumerTransportcloseEventCalled).toBe(false);
1096
+ expect(videoConsumerTransportcloseEventCalled).toBe(true);
1097
+ audioProducer.removeAllListeners();
1098
+ videoProducer.removeAllListeners();
1099
+ audioConsumer.removeAllListeners();
1100
+ videoConsumer.removeAllListeners();
1101
+ });
1102
+ test('transport.produce() rejects with InvalidStateError if closed', async () => {
1103
+ const track = new fake_mediastreamtrack_1.FakeMediaStreamTrack({ kind: 'audio' });
1104
+ // Add noop listener to avoid the method fail.
1105
+ sendTransport.on('produce', () => { });
1106
+ await expect(sendTransport.produce({ track, stopTracks: false }))
1107
+ .rejects
1108
+ .toThrow(errors_1.InvalidStateError);
1109
+ // The track must be 'live' due to stopTracks: false.
1110
+ expect(track.readyState).toBe('live');
1111
+ sendTransport.removeAllListeners('produce');
1112
+ }, 500);
1113
+ test('transport.consume() rejects with InvalidStateError if closed', async () => {
1114
+ // @ts-ignore
1115
+ await expect(recvTransport.consume({}))
1116
+ .rejects
1117
+ .toThrow(errors_1.InvalidStateError);
1118
+ recvTransport.removeAllListeners();
1119
+ }, 500);
1120
+ test('transport.produceData() rejects with InvalidStateError if closed', async () => {
1121
+ // Add noop listener to avoid the method fail.
1122
+ sendTransport.on('producedata', () => { });
1123
+ await expect(sendTransport.produceData({}))
1124
+ .rejects
1125
+ .toThrow(errors_1.InvalidStateError);
1126
+ sendTransport.removeAllListeners('producedata');
1127
+ }, 500);
1128
+ test('transport.consumeData() rejects with InvalidStateError if closed', async () => {
1129
+ // @ts-ignore
1130
+ await expect(recvTransport.consumeData({}))
1131
+ .rejects
1132
+ .toThrow(errors_1.InvalidStateError);
1133
+ }, 500);
1134
+ test('transport.getStats() rejects with InvalidStateError if closed', async () => {
1135
+ await expect(sendTransport.getStats())
1136
+ .rejects
1137
+ .toThrow(errors_1.InvalidStateError);
1138
+ }, 500);
1139
+ test('transport.restartIce() rejects with InvalidStateError if closed', async () => {
1140
+ // @ts-ignore
1141
+ await expect(sendTransport.restartIce({ ieParameters: {} }))
1142
+ .rejects
1143
+ .toThrow(errors_1.InvalidStateError);
1144
+ }, 500);
1145
+ test('transport.updateIceServers() rejects with InvalidStateError if closed', async () => {
1146
+ await expect(sendTransport.updateIceServers({ iceServers: [] }))
1147
+ .rejects
1148
+ .toThrow(errors_1.InvalidStateError);
1149
+ }, 500);
1150
+ test('connection state change does not fire "connectionstatechange" in closed Transport', () => {
1151
+ let connectionStateChangeEventNumTimesCalled = 0;
1152
+ // eslint-disable-next-line no-unused-vars
1153
+ sendTransport.on('connectionstatechange', ( /* connectionState */) => {
1154
+ connectionStateChangeEventNumTimesCalled++;
1155
+ });
1156
+ // @ts-ignore
1157
+ sendTransport.handler.setConnectionState('disconnected');
1158
+ expect(connectionStateChangeEventNumTimesCalled).toBe(0);
1159
+ expect(sendTransport.connectionState).toBe('disconnected');
1160
+ sendTransport.removeAllListeners('connectionstatechange');
1161
+ });
1162
+ test('RemoteSdp properly handles multiple streams of the same type in planB', async () => {
1163
+ var _a, _b, _c;
1164
+ let sdp = undefined;
1165
+ let sdpObject = undefined;
1166
+ const remoteSdp = new RemoteSdp_1.RemoteSdp({ planB: true });
1167
+ await remoteSdp.receive({
1168
+ mid: 'video',
1169
+ kind: 'video',
1170
+ offerRtpParameters: fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'video/VP8' }).rtpParameters,
1171
+ streamId: 'streamId-1',
1172
+ trackId: 'trackId-1'
1173
+ });
1174
+ sdp = remoteSdp.getSdp();
1175
+ sdpObject = sdpTransform.parse(sdp);
1176
+ expect(sdpObject.media.length).toBe(1);
1177
+ expect(sdpObject.media[0].payloads).toBe('101 102');
1178
+ expect(sdpObject.media[0].rtp.length).toBe(2);
1179
+ expect(sdpObject.media[0].rtp[0].payload).toBe(101);
1180
+ expect(sdpObject.media[0].rtp[0].codec).toBe('VP8');
1181
+ expect(sdpObject.media[0].rtp[1].payload).toBe(102);
1182
+ expect(sdpObject.media[0].rtp[1].codec).toBe('rtx');
1183
+ expect((_a = sdpObject.media[0].ssrcs) === null || _a === void 0 ? void 0 : _a.length).toBe(4);
1184
+ await remoteSdp.receive({
1185
+ mid: 'video',
1186
+ kind: 'video',
1187
+ offerRtpParameters: fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'video/H264' }).rtpParameters,
1188
+ streamId: 'streamId-2',
1189
+ trackId: 'trackId-2'
1190
+ });
1191
+ sdp = remoteSdp.getSdp();
1192
+ sdpObject = sdpTransform.parse(sdp);
1193
+ expect(sdpObject.media.length).toBe(1);
1194
+ expect(sdpObject.media[0].payloads).toBe('101 102 103 104');
1195
+ expect(sdpObject.media[0].rtp.length).toBe(4);
1196
+ expect(sdpObject.media[0].rtp[0].payload).toBe(101);
1197
+ expect(sdpObject.media[0].rtp[0].codec).toBe('VP8');
1198
+ expect(sdpObject.media[0].rtp[1].payload).toBe(102);
1199
+ expect(sdpObject.media[0].rtp[1].codec).toBe('rtx');
1200
+ expect(sdpObject.media[0].rtp[2].payload).toBe(103);
1201
+ expect(sdpObject.media[0].rtp[2].codec).toBe('H264');
1202
+ expect(sdpObject.media[0].rtp[3].payload).toBe(104);
1203
+ expect(sdpObject.media[0].rtp[3].codec).toBe('rtx');
1204
+ expect((_b = sdpObject.media[0].ssrcs) === null || _b === void 0 ? void 0 : _b.length).toBe(8);
1205
+ await remoteSdp.planBStopReceiving({
1206
+ mid: 'video',
1207
+ offerRtpParameters: fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'video/H264' }).rtpParameters
1208
+ });
1209
+ sdp = remoteSdp.getSdp();
1210
+ sdpObject = sdpTransform.parse(sdp);
1211
+ expect(sdpObject.media.length).toBe(1);
1212
+ expect(sdpObject.media[0].payloads).toBe('101 102 103 104');
1213
+ expect(sdpObject.media[0].rtp.length).toBe(4);
1214
+ expect(sdpObject.media[0].rtp[0].payload).toBe(101);
1215
+ expect(sdpObject.media[0].rtp[0].codec).toBe('VP8');
1216
+ expect(sdpObject.media[0].rtp[1].payload).toBe(102);
1217
+ expect(sdpObject.media[0].rtp[1].codec).toBe('rtx');
1218
+ expect(sdpObject.media[0].rtp[2].payload).toBe(103);
1219
+ expect(sdpObject.media[0].rtp[2].codec).toBe('H264');
1220
+ expect(sdpObject.media[0].rtp[3].payload).toBe(104);
1221
+ expect(sdpObject.media[0].rtp[3].codec).toBe('rtx');
1222
+ expect((_c = sdpObject.media[0].ssrcs) === null || _c === void 0 ? void 0 : _c.length).toBe(4);
1223
+ }, 500);
1224
+ test('RemoteSdp does not duplicate codec descriptions', async () => {
1225
+ var _a, _b;
1226
+ let sdp = undefined;
1227
+ let sdpObject = undefined;
1228
+ const remoteSdp = new RemoteSdp_1.RemoteSdp({ planB: true });
1229
+ await remoteSdp.receive({
1230
+ mid: 'video',
1231
+ kind: 'video',
1232
+ offerRtpParameters: fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'video/VP8' }).rtpParameters,
1233
+ streamId: 'streamId-1',
1234
+ trackId: 'trackId-1'
1235
+ });
1236
+ sdp = remoteSdp.getSdp();
1237
+ sdpObject = sdpTransform.parse(sdp);
1238
+ expect(sdpObject.media.length).toBe(1);
1239
+ expect(sdpObject.media[0].payloads).toBe('101 102');
1240
+ expect(sdpObject.media[0].rtp.length).toBe(2);
1241
+ expect(sdpObject.media[0].rtp[0].payload).toBe(101);
1242
+ expect(sdpObject.media[0].rtp[0].codec).toBe('VP8');
1243
+ expect(sdpObject.media[0].rtp[1].payload).toBe(102);
1244
+ expect(sdpObject.media[0].rtp[1].codec).toBe('rtx');
1245
+ expect((_a = sdpObject.media[0].ssrcs) === null || _a === void 0 ? void 0 : _a.length).toBe(4);
1246
+ await remoteSdp.receive({
1247
+ mid: 'video',
1248
+ kind: 'video',
1249
+ offerRtpParameters: fakeParameters.generateConsumerRemoteParameters({ codecMimeType: 'video/VP8' }).rtpParameters,
1250
+ streamId: 'streamId-1',
1251
+ trackId: 'trackId-1'
1252
+ });
1253
+ sdp = remoteSdp.getSdp();
1254
+ sdpObject = sdpTransform.parse(sdp);
1255
+ expect(sdpObject.media.length).toBe(1);
1256
+ expect(sdpObject.media[0].payloads).toBe('101 102');
1257
+ expect(sdpObject.media[0].rtp.length).toBe(2);
1258
+ expect(sdpObject.media[0].rtp[0].payload).toBe(101);
1259
+ expect(sdpObject.media[0].rtp[0].codec).toBe('VP8');
1260
+ expect(sdpObject.media[0].rtp[1].payload).toBe(102);
1261
+ expect(sdpObject.media[0].rtp[1].codec).toBe('rtx');
1262
+ expect((_b = sdpObject.media[0].ssrcs) === null || _b === void 0 ? void 0 : _b.length).toBe(8);
1263
+ }, 500);
1264
+ test('parseScalabilityMode() works', () => {
1265
+ expect(parseScalabilityMode('L1T3')).toEqual({ spatialLayers: 1, temporalLayers: 3 });
1266
+ expect(parseScalabilityMode('L3T2_KEY')).toEqual({ spatialLayers: 3, temporalLayers: 2 });
1267
+ expect(parseScalabilityMode('S2T3')).toEqual({ spatialLayers: 2, temporalLayers: 3 });
1268
+ expect(parseScalabilityMode('foo')).toEqual({ spatialLayers: 1, temporalLayers: 1 });
1269
+ expect(parseScalabilityMode()).toEqual({ spatialLayers: 1, temporalLayers: 1 });
1270
+ expect(parseScalabilityMode('S0T3')).toEqual({ spatialLayers: 1, temporalLayers: 1 });
1271
+ expect(parseScalabilityMode('S1T0')).toEqual({ spatialLayers: 1, temporalLayers: 1 });
1272
+ expect(parseScalabilityMode('L20T3')).toEqual({ spatialLayers: 20, temporalLayers: 3 });
1273
+ expect(parseScalabilityMode('S200T3')).toEqual({ spatialLayers: 1, temporalLayers: 1 });
1274
+ });
1275
+ describe('detectDevice() assigns proper handler based on UserAgent', () => {
1276
+ const originalNavigator = global.navigator;
1277
+ for (const uaTestCase of uaTestCases_1.uaTestCases) {
1278
+ test(uaTestCase.desc, () => {
1279
+ // @ts-ignore
1280
+ global.navigator =
1281
+ {
1282
+ userAgent: uaTestCase.ua
1283
+ };
1284
+ const originalRTCRtpTransceiver = global.RTCRtpTransceiver;
1285
+ if (uaTestCase.expect === 'Safari12') {
1286
+ global.RTCRtpTransceiver = class Dummy {
1287
+ currentDirection() { }
1288
+ };
1289
+ }
1290
+ expect(detectDevice()).toBe(uaTestCase.expect);
1291
+ // Cleanup.
1292
+ global.RTCRtpTransceiver = originalRTCRtpTransceiver;
1293
+ }, 100);
1294
+ }
1295
+ // Cleanup.
1296
+ global.navigator = originalNavigator;
1297
+ });