@webex/plugin-meetings 3.0.0-beta.250 → 3.0.0-beta.252
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/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +3 -1
- package/dist/constants.js.map +1 -1
- package/dist/index.js +30 -24
- package/dist/index.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/media/index.js +18 -19
- package/dist/media/index.js.map +1 -1
- package/dist/media/properties.js +52 -52
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/index.js +417 -372
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/muteState.js +39 -38
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/util.js +9 -9
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/index.js +45 -23
- package/dist/meeting-info/index.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +22 -4
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meetings/index.js +4 -2
- package/dist/meetings/index.js.map +1 -1
- package/dist/multistream/sendSlotManager.js +233 -0
- package/dist/multistream/sendSlotManager.js.map +1 -0
- package/dist/reconnection-manager/index.js +10 -10
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/types/constants.d.ts +2 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/media/index.d.ts +2 -2
- package/dist/types/media/properties.d.ts +24 -24
- package/dist/types/meeting/index.d.ts +61 -51
- package/dist/types/meeting/muteState.d.ts +16 -16
- package/dist/types/meeting/util.d.ts +2 -2
- package/dist/types/meeting-info/index.d.ts +7 -0
- package/dist/types/meeting-info/meeting-info-v2.d.ts +1 -0
- package/dist/types/multistream/sendSlotManager.d.ts +61 -0
- package/dist/types/reconnection-manager/index.d.ts +2 -2
- package/package.json +20 -20
- package/src/constants.ts +2 -0
- package/src/index.ts +14 -13
- package/src/media/index.ts +32 -34
- package/src/media/properties.ts +47 -46
- package/src/meeting/index.ts +402 -331
- package/src/meeting/muteState.ts +35 -34
- package/src/meeting/util.ts +11 -10
- package/src/meeting-info/index.ts +45 -20
- package/src/meeting-info/meeting-info-v2.ts +25 -5
- package/src/meetings/index.ts +9 -2
- package/src/multistream/sendSlotManager.ts +170 -0
- package/src/reconnection-manager/index.ts +8 -8
- package/test/integration/spec/converged-space-meetings.js +7 -7
- package/test/integration/spec/journey.js +85 -103
- package/test/integration/spec/space-meeting.js +9 -9
- package/test/unit/spec/media/index.ts +23 -66
- package/test/unit/spec/meeting/index.js +786 -848
- package/test/unit/spec/meeting/muteState.js +113 -75
- package/test/unit/spec/meeting/utils.js +14 -16
- package/test/unit/spec/meeting-info/index.js +173 -61
- package/test/unit/spec/meeting-info/meetinginfov2.js +186 -52
- package/test/unit/spec/meetings/index.js +6 -5
- package/test/unit/spec/multistream/sendSlotManager.ts +242 -0
- package/test/unit/spec/reconnection-manager/index.js +4 -3
- package/test/utils/integrationTestUtils.js +4 -4
|
@@ -38,7 +38,9 @@ import {
|
|
|
38
38
|
RemoteTrackType,
|
|
39
39
|
MediaType,
|
|
40
40
|
} from '@webex/internal-media-core';
|
|
41
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
StreamEventNames,
|
|
43
|
+
} from '@webex/media-helpers';
|
|
42
44
|
import * as StatsAnalyzerModule from '@webex/plugin-meetings/src/statsAnalyzer';
|
|
43
45
|
import * as MuteStateModule from '@webex/plugin-meetings/src/meeting/muteState';
|
|
44
46
|
import EventsScope from '@webex/plugin-meetings/src/common/events/events-scope';
|
|
@@ -65,6 +67,7 @@ import Metrics from '@webex/plugin-meetings/src/metrics';
|
|
|
65
67
|
import BEHAVIORAL_METRICS from '@webex/plugin-meetings/src/metrics/constants';
|
|
66
68
|
import {MediaRequestManager} from '@webex/plugin-meetings/src/multistream/mediaRequestManager';
|
|
67
69
|
import * as ReceiveSlotManagerModule from '@webex/plugin-meetings/src/multistream/receiveSlotManager';
|
|
70
|
+
import * as SendSlotManagerModule from '@webex/plugin-meetings/src/multistream/sendSlotManager';
|
|
68
71
|
|
|
69
72
|
import LLM from '@webex/internal-plugin-llm';
|
|
70
73
|
import Mercury from '@webex/internal-plugin-mercury';
|
|
@@ -433,6 +436,35 @@ describe('plugin-meetings', () => {
|
|
|
433
436
|
assert.equal(memberId, undefined);
|
|
434
437
|
});
|
|
435
438
|
});
|
|
439
|
+
|
|
440
|
+
describe('creates SendSlot manager instance', () => {
|
|
441
|
+
let mockSendSlotManagerCtor;
|
|
442
|
+
|
|
443
|
+
beforeEach(() => {
|
|
444
|
+
mockSendSlotManagerCtor = sinon
|
|
445
|
+
.stub(SendSlotManagerModule,'default');
|
|
446
|
+
|
|
447
|
+
meeting = new Meeting(
|
|
448
|
+
{
|
|
449
|
+
userId: uuid1,
|
|
450
|
+
resource: uuid2,
|
|
451
|
+
deviceUrl: uuid3,
|
|
452
|
+
locus: {url: url1},
|
|
453
|
+
destination: testDestination,
|
|
454
|
+
destinationType: _MEETING_ID_,
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
parent: webex,
|
|
458
|
+
}
|
|
459
|
+
);
|
|
460
|
+
|
|
461
|
+
meeting.mediaProperties.webrtcMediaConnection = {createSendSlot: sinon.stub()};
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
it('calls SendSlotManager constructor', () => {
|
|
465
|
+
assert.calledOnce(mockSendSlotManagerCtor);
|
|
466
|
+
});
|
|
467
|
+
});
|
|
436
468
|
});
|
|
437
469
|
|
|
438
470
|
describe('#isLocusCall', () => {
|
|
@@ -824,75 +856,8 @@ describe('plugin-meetings', () => {
|
|
|
824
856
|
sinon.assert.called(setCorrelationIdSpy);
|
|
825
857
|
assert.equal(meeting.correlationId, '123');
|
|
826
858
|
});
|
|
827
|
-
|
|
828
|
-
it('should send Meeting Info CA events if meetingInfo is not empty', async () => {
|
|
829
|
-
meeting.meetingInfo = {info: 'info', meetingLookupUrl: 'url'};
|
|
830
|
-
|
|
831
|
-
const join = meeting.join();
|
|
832
|
-
|
|
833
|
-
assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
|
|
834
|
-
name: 'client.call.initiated',
|
|
835
|
-
payload: {trigger: 'user-interaction', isRoapCallEnabled: true},
|
|
836
|
-
options: {meetingId: meeting.id},
|
|
837
|
-
});
|
|
838
|
-
|
|
839
|
-
assert.exists(join.then);
|
|
840
|
-
const result = await join;
|
|
841
|
-
|
|
842
|
-
assert.calledOnce(MeetingUtil.joinMeeting);
|
|
843
|
-
assert.calledOnce(meeting.setLocus);
|
|
844
|
-
assert.equal(result, joinMeetingResult);
|
|
845
|
-
|
|
846
|
-
assert.calledThrice(webex.internal.newMetrics.submitClientEvent);
|
|
847
|
-
|
|
848
|
-
assert.deepEqual(webex.internal.newMetrics.submitClientEvent.getCall(0).args[0], {
|
|
849
|
-
name: 'client.call.initiated',
|
|
850
|
-
payload: {
|
|
851
|
-
trigger: 'user-interaction',
|
|
852
|
-
isRoapCallEnabled: true,
|
|
853
|
-
},
|
|
854
|
-
options: {meetingId: meeting.id},
|
|
855
|
-
});
|
|
856
|
-
|
|
857
|
-
assert.deepEqual(webex.internal.newMetrics.submitClientEvent.getCall(1).args[0], {
|
|
858
|
-
name: 'client.meetinginfo.request',
|
|
859
|
-
options: {meetingId: meeting.id},
|
|
860
|
-
});
|
|
861
|
-
assert.deepEqual(webex.internal.newMetrics.submitClientEvent.getCall(2).args[0], {
|
|
862
|
-
name: 'client.meetinginfo.response',
|
|
863
|
-
payload: {
|
|
864
|
-
identifiers: {meetingLookupUrl: 'url'},
|
|
865
|
-
},
|
|
866
|
-
options: {meetingId: meeting.id},
|
|
867
|
-
});
|
|
868
|
-
});
|
|
869
|
-
|
|
870
|
-
it('should not send Meeting Info CA events if meetingInfo is empty', async () => {
|
|
871
|
-
meeting.meetingInfo = {};
|
|
872
|
-
|
|
873
|
-
const join = meeting.join();
|
|
874
|
-
|
|
875
|
-
assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
|
|
876
|
-
name: 'client.call.initiated',
|
|
877
|
-
payload: {trigger: 'user-interaction', isRoapCallEnabled: true},
|
|
878
|
-
options: {meetingId: meeting.id},
|
|
879
|
-
});
|
|
880
|
-
|
|
881
|
-
assert.exists(join.then);
|
|
882
|
-
const result = await join;
|
|
883
|
-
|
|
884
|
-
assert.calledOnce(MeetingUtil.joinMeeting);
|
|
885
|
-
assert.calledOnce(meeting.setLocus);
|
|
886
|
-
assert.equal(result, joinMeetingResult);
|
|
887
|
-
|
|
888
|
-
assert.calledOnce(webex.internal.newMetrics.submitClientEvent);
|
|
889
|
-
|
|
890
|
-
assert.equal(
|
|
891
|
-
webex.internal.newMetrics.submitClientEvent.getCall(0).args[0].name,
|
|
892
|
-
'client.call.initiated'
|
|
893
|
-
);
|
|
894
|
-
});
|
|
895
859
|
});
|
|
860
|
+
|
|
896
861
|
describe('failure', () => {
|
|
897
862
|
beforeEach(() => {
|
|
898
863
|
sandbox.stub(MeetingUtil, 'joinMeeting').returns(Promise.reject());
|
|
@@ -1624,440 +1589,424 @@ describe('plugin-meetings', () => {
|
|
|
1624
1589
|
to @webex/internal-media-core when addMedia, updateMedia, publishTracks, unpublishTracks are called
|
|
1625
1590
|
in various combinations.
|
|
1626
1591
|
*/
|
|
1627
|
-
[true,
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
let fakeMicrophoneTrack;
|
|
1637
|
-
let fakeRoapMediaConnection;
|
|
1638
|
-
let fakeMultistreamRoapMediaConnection;
|
|
1639
|
-
let roapMediaConnectionConstructorStub;
|
|
1640
|
-
let multistreamRoapMediaConnectionConstructorStub;
|
|
1641
|
-
let locusMediaRequestStub; // stub for /media requests to Locus
|
|
1592
|
+
[true,false].forEach((isMultistream) =>
|
|
1593
|
+
describe(`addMedia/updateMedia semi-integration tests (${isMultistream ? 'multistream' : 'transcoded'})`, () => {
|
|
1594
|
+
let fakeMicrophoneStream;
|
|
1595
|
+
let fakeRoapMediaConnection;
|
|
1596
|
+
let fakeMultistreamRoapMediaConnection;
|
|
1597
|
+
let roapMediaConnectionConstructorStub;
|
|
1598
|
+
let multistreamRoapMediaConnectionConstructorStub;
|
|
1599
|
+
let locusMediaRequestStub; // stub for /media requests to Locus
|
|
1642
1600
|
|
|
1643
|
-
|
|
1601
|
+
const roapOfferMessage = {messageType: 'OFFER', sdp: 'sdp', seq: '1', tieBreaker: '123'};
|
|
1644
1602
|
|
|
1645
|
-
|
|
1646
|
-
|
|
1603
|
+
let expectedMediaConnectionConfig;
|
|
1604
|
+
let expectedDebugId;
|
|
1647
1605
|
|
|
1648
|
-
|
|
1606
|
+
let clock;
|
|
1649
1607
|
|
|
1650
|
-
|
|
1651
|
-
|
|
1608
|
+
beforeEach(() => {
|
|
1609
|
+
clock = sinon.useFakeTimers();
|
|
1652
1610
|
|
|
1653
|
-
|
|
1611
|
+
sinon.stub(MeetingUtil, 'getIpVersion').returns(IP_VERSION.unknown);
|
|
1654
1612
|
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
},
|
|
1688
|
-
startBitrate: StaticConfig.meetings.bandwidth.startBitrate,
|
|
1689
|
-
periodicKeyframes: 20,
|
|
1690
|
-
disableExtmap: !meeting.config.enableExtmap,
|
|
1691
|
-
disableRtx: !meeting.config.enableRtx,
|
|
1613
|
+
meeting.deviceUrl = 'deviceUrl';
|
|
1614
|
+
meeting.config.deviceType = 'web';
|
|
1615
|
+
meeting.isMultistream = isMultistream;
|
|
1616
|
+
meeting.meetingState = 'ACTIVE';
|
|
1617
|
+
meeting.mediaId = 'fake media id';
|
|
1618
|
+
meeting.selfUrl = 'selfUrl';
|
|
1619
|
+
meeting.mediaProperties.waitForMediaConnectionConnected = sinon.stub().resolves();
|
|
1620
|
+
meeting.mediaProperties.getCurrentConnectionType = sinon.stub().resolves('udp');
|
|
1621
|
+
meeting.setMercuryListener = sinon.stub();
|
|
1622
|
+
meeting.locusInfo.onFullLocus = sinon.stub();
|
|
1623
|
+
meeting.webex.meetings.geoHintInfo = {regionCode: 'EU', countryCode: 'UK'};
|
|
1624
|
+
meeting.webex.meetings.reachability = {
|
|
1625
|
+
isAnyClusterReachable: sinon.stub().resolves(true),
|
|
1626
|
+
};
|
|
1627
|
+
meeting.roap.doTurnDiscovery = sinon
|
|
1628
|
+
.stub()
|
|
1629
|
+
.resolves({turnServerInfo: {}, turnDiscoverySkippedReason: 'reachability'});
|
|
1630
|
+
|
|
1631
|
+
StaticConfig.set({bandwidth: {audio: 1234, video: 5678, startBitrate: 9876}});
|
|
1632
|
+
|
|
1633
|
+
// setup things that are expected to be the same across all the tests and are actually irrelevant for these tests
|
|
1634
|
+
expectedDebugId = `MC-${meeting.id.substring(0, 4)}`;
|
|
1635
|
+
expectedMediaConnectionConfig = {
|
|
1636
|
+
iceServers: [{urls: undefined, username: '', credential: ''}],
|
|
1637
|
+
skipInactiveTransceivers: false,
|
|
1638
|
+
requireH264: true,
|
|
1639
|
+
sdpMunging: {
|
|
1640
|
+
convertPort9to0: false,
|
|
1641
|
+
addContentSlides: true,
|
|
1642
|
+
bandwidthLimits: {
|
|
1643
|
+
audio: StaticConfig.meetings.bandwidth.audio,
|
|
1644
|
+
video: StaticConfig.meetings.bandwidth.video,
|
|
1692
1645
|
},
|
|
1693
|
-
|
|
1646
|
+
startBitrate: StaticConfig.meetings.bandwidth.startBitrate,
|
|
1647
|
+
periodicKeyframes: 20,
|
|
1648
|
+
disableExtmap: !meeting.config.enableExtmap,
|
|
1649
|
+
disableRtx: !meeting.config.enableRtx,
|
|
1650
|
+
},
|
|
1651
|
+
};
|
|
1694
1652
|
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1653
|
+
// setup stubs
|
|
1654
|
+
fakeMicrophoneStream = {
|
|
1655
|
+
on: sinon.stub(),
|
|
1656
|
+
off: sinon.stub(),
|
|
1657
|
+
getSettings: sinon.stub().returns({
|
|
1658
|
+
deviceId: 'some device id'
|
|
1659
|
+
}),
|
|
1660
|
+
muted: false,
|
|
1661
|
+
setUnmuteAllowed: sinon.stub(),
|
|
1662
|
+
setMuted: sinon.stub(),
|
|
1663
|
+
setServerMuted: sinon.stub(),
|
|
1664
|
+
outputTrack: {
|
|
1665
|
+
id: 'fake mic'
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1706
1668
|
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1669
|
+
fakeRoapMediaConnection = {
|
|
1670
|
+
id: 'roap media connection',
|
|
1671
|
+
close: sinon.stub(),
|
|
1672
|
+
getConnectionState: sinon.stub().returns(ConnectionState.Connected),
|
|
1673
|
+
initiateOffer: sinon.stub().resolves({}),
|
|
1674
|
+
update: sinon.stub().resolves({}),
|
|
1675
|
+
on: sinon.stub(),
|
|
1676
|
+
};
|
|
1715
1677
|
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
}
|
|
1678
|
+
fakeMultistreamRoapMediaConnection = {
|
|
1679
|
+
id: 'multistream roap media connection',
|
|
1680
|
+
close: sinon.stub(),
|
|
1681
|
+
getConnectionState: sinon.stub().returns(ConnectionState.Connected),
|
|
1682
|
+
initiateOffer: sinon.stub().resolves({}),
|
|
1683
|
+
on: sinon.stub(),
|
|
1684
|
+
requestMedia: sinon.stub(),
|
|
1685
|
+
createReceiveSlot: sinon.stub().resolves({on: sinon.stub()}),
|
|
1686
|
+
createSendSlot: sinon.stub().returns({
|
|
1687
|
+
publishStream: sinon.stub(),
|
|
1688
|
+
unpublishStream: sinon.stub(),
|
|
1689
|
+
}),
|
|
1690
|
+
enableMultistreamAudio: sinon.stub(),
|
|
1691
|
+
};
|
|
1728
1692
|
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1693
|
+
roapMediaConnectionConstructorStub = sinon
|
|
1694
|
+
.stub(internalMediaModule, 'RoapMediaConnection')
|
|
1695
|
+
.returns(fakeRoapMediaConnection);
|
|
1732
1696
|
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1697
|
+
multistreamRoapMediaConnectionConstructorStub = sinon
|
|
1698
|
+
.stub(internalMediaModule, 'MultistreamRoapMediaConnection')
|
|
1699
|
+
.returns(fakeMultistreamRoapMediaConnection);
|
|
1736
1700
|
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
.resolves({body: {locus: {fullState: {}}}});
|
|
1740
|
-
});
|
|
1701
|
+
locusMediaRequestStub = sinon.stub(WebexPlugin.prototype, 'request').resolves({body: {locus: { fullState: {}}}});
|
|
1702
|
+
});
|
|
1741
1703
|
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1704
|
+
afterEach(() => {
|
|
1705
|
+
clock.restore();
|
|
1706
|
+
sinon.restore();
|
|
1707
|
+
});
|
|
1746
1708
|
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1709
|
+
// helper function that waits until all promises are resolved and any queued up /media requests to Locus are sent out
|
|
1710
|
+
const stableState = async () => {
|
|
1711
|
+
await testUtils.flushPromises();
|
|
1712
|
+
clock.tick(1); // needed because LocusMediaRequest uses Lodash.defer()
|
|
1713
|
+
}
|
|
1752
1714
|
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
}
|
|
1715
|
+
const resetHistory = () => {
|
|
1716
|
+
locusMediaRequestStub.resetHistory();
|
|
1717
|
+
fakeRoapMediaConnection.update.resetHistory();
|
|
1718
|
+
try{
|
|
1719
|
+
meeting.sendSlotManager.getSlot(MediaType.AudioMain).publishStream.resetHistory();
|
|
1720
|
+
}
|
|
1721
|
+
catch(e){}
|
|
1722
|
+
};
|
|
1759
1723
|
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
? fakeMultistreamRoapMediaConnection
|
|
1763
|
-
: fakeRoapMediaConnection;
|
|
1724
|
+
const getRoapListener = () => {
|
|
1725
|
+
const roapMediaConnectionToCheck = isMultistream ? fakeMultistreamRoapMediaConnection : fakeRoapMediaConnection;
|
|
1764
1726
|
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
) {
|
|
1769
|
-
return roapMediaConnectionToCheck.on.getCall(idx).args[1];
|
|
1770
|
-
}
|
|
1727
|
+
for(let idx = 0; idx < roapMediaConnectionToCheck.on.callCount; idx+= 1) {
|
|
1728
|
+
if (roapMediaConnectionToCheck.on.getCall(idx).args[0] === Event.ROAP_MESSAGE_TO_SEND) {
|
|
1729
|
+
return roapMediaConnectionToCheck.on.getCall(idx).args[1];
|
|
1771
1730
|
}
|
|
1772
|
-
assert.fail(
|
|
1773
|
-
'listener for "roap:messageToSend" (Event.ROAP_MESSAGE_TO_SEND) was not registered'
|
|
1774
|
-
);
|
|
1775
1731
|
};
|
|
1732
|
+
assert.fail(
|
|
1733
|
+
'listener for "roap:messageToSend" (Event.ROAP_MESSAGE_TO_SEND) was not registered'
|
|
1734
|
+
);
|
|
1735
|
+
};
|
|
1776
1736
|
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1737
|
+
// simulates a Roap offer being generated by the RoapMediaConnection
|
|
1738
|
+
const simulateRoapOffer = async () => {
|
|
1739
|
+
const roapListener = getRoapListener();
|
|
1780
1740
|
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1741
|
+
await roapListener({roapMessage: roapOfferMessage});
|
|
1742
|
+
await stableState();
|
|
1743
|
+
};
|
|
1784
1744
|
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
assert.calledWith(locusMediaRequestStub, {
|
|
1789
|
-
method: 'PUT',
|
|
1790
|
-
uri: `${meeting.selfUrl}/media`,
|
|
1791
|
-
body: {
|
|
1792
|
-
device: {
|
|
1793
|
-
url: meeting.deviceUrl,
|
|
1794
|
-
deviceType: meeting.config.deviceType,
|
|
1795
|
-
regionCode: 'EU',
|
|
1796
|
-
countryCode: 'UK',
|
|
1797
|
-
},
|
|
1798
|
-
correlationId: meeting.correlationId,
|
|
1799
|
-
localMedias: [
|
|
1800
|
-
{
|
|
1801
|
-
localSdp: `{"audioMuted":${audioMuted},"videoMuted":${videoMuted},"roapMessage":{"messageType":"OFFER","sdps":["${sdp}"],"version":"2","seq":"${seq}","tieBreaker":"${tieBreaker}"}}`,
|
|
1802
|
-
mediaId: 'fake media id',
|
|
1803
|
-
},
|
|
1804
|
-
],
|
|
1805
|
-
clientMediaPreferences: {
|
|
1806
|
-
preferTranscoding: !meeting.isMultistream,
|
|
1807
|
-
joinCookie: undefined,
|
|
1808
|
-
ipver: 0,
|
|
1809
|
-
},
|
|
1810
|
-
},
|
|
1811
|
-
});
|
|
1812
|
-
};
|
|
1745
|
+
const checkSdpOfferSent = ({audioMuted, videoMuted}) => {
|
|
1746
|
+
const {sdp, seq, tieBreaker} = roapOfferMessage;
|
|
1813
1747
|
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
{
|
|
1828
|
-
|
|
1829
|
-
mediaId: 'fake media id',
|
|
1830
|
-
},
|
|
1831
|
-
],
|
|
1832
|
-
clientMediaPreferences: {
|
|
1833
|
-
preferTranscoding: !meeting.isMultistream,
|
|
1834
|
-
ipver: undefined
|
|
1748
|
+
assert.calledWith(locusMediaRequestStub, {
|
|
1749
|
+
method: 'PUT',
|
|
1750
|
+
uri: `${meeting.selfUrl}/media`,
|
|
1751
|
+
body: {
|
|
1752
|
+
device: {
|
|
1753
|
+
url: meeting.deviceUrl,
|
|
1754
|
+
deviceType: meeting.config.deviceType,
|
|
1755
|
+
regionCode: 'EU',
|
|
1756
|
+
countryCode: 'UK',
|
|
1757
|
+
},
|
|
1758
|
+
correlationId: meeting.correlationId,
|
|
1759
|
+
localMedias: [
|
|
1760
|
+
{
|
|
1761
|
+
localSdp: `{"audioMuted":${audioMuted},"videoMuted":${videoMuted},"roapMessage":{"messageType":"OFFER","sdps":["${sdp}"],"version":"2","seq":"${seq}","tieBreaker":"${tieBreaker}"}}`,
|
|
1762
|
+
mediaId: 'fake media id',
|
|
1835
1763
|
},
|
|
1836
|
-
|
|
1837
|
-
|
|
1764
|
+
],
|
|
1765
|
+
clientMediaPreferences: {
|
|
1766
|
+
preferTranscoding: !meeting.isMultistream,
|
|
1767
|
+
joinCookie: undefined,
|
|
1768
|
+
ipver: 0,
|
|
1838
1769
|
},
|
|
1839
|
-
}
|
|
1840
|
-
};
|
|
1841
|
-
|
|
1842
|
-
const checkMediaConnectionCreated = ({
|
|
1843
|
-
mediaConnectionConfig,
|
|
1844
|
-
localTracks,
|
|
1845
|
-
direction,
|
|
1846
|
-
remoteQualityLevel,
|
|
1847
|
-
expectedDebugId,
|
|
1848
|
-
meetingId,
|
|
1849
|
-
}) => {
|
|
1850
|
-
if (isMultistream) {
|
|
1851
|
-
const {iceServers} = mediaConnectionConfig;
|
|
1770
|
+
},
|
|
1771
|
+
});
|
|
1772
|
+
};
|
|
1852
1773
|
|
|
1853
|
-
|
|
1854
|
-
|
|
1774
|
+
const checkLocalMuteSentToLocus = ({audioMuted, videoMuted}) => {
|
|
1775
|
+
assert.calledWith(locusMediaRequestStub, {
|
|
1776
|
+
method: 'PUT',
|
|
1777
|
+
uri: `${meeting.selfUrl}/media`,
|
|
1778
|
+
body: {
|
|
1779
|
+
device: {
|
|
1780
|
+
url: meeting.deviceUrl,
|
|
1781
|
+
deviceType: meeting.config.deviceType,
|
|
1782
|
+
regionCode: 'EU',
|
|
1783
|
+
countryCode: 'UK',
|
|
1784
|
+
},
|
|
1785
|
+
correlationId: meeting.correlationId,
|
|
1786
|
+
localMedias: [
|
|
1855
1787
|
{
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
enableMainVideo: true,
|
|
1788
|
+
localSdp: `{"audioMuted":${audioMuted},"videoMuted":${videoMuted}}`,
|
|
1789
|
+
mediaId: 'fake media id',
|
|
1859
1790
|
},
|
|
1860
|
-
|
|
1861
|
-
|
|
1791
|
+
],
|
|
1792
|
+
clientMediaPreferences: {
|
|
1793
|
+
preferTranscoding: !meeting.isMultistream,
|
|
1794
|
+
ipver: undefined
|
|
1795
|
+
},
|
|
1796
|
+
respOnlySdp: true,
|
|
1797
|
+
usingResource: null,
|
|
1798
|
+
},
|
|
1799
|
+
});
|
|
1800
|
+
};
|
|
1862
1801
|
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1802
|
+
const checkMediaConnectionCreated = ({mediaConnectionConfig, localStreams, direction, remoteQualityLevel, expectedDebugId, meetingId}) => {
|
|
1803
|
+
if (isMultistream) {
|
|
1804
|
+
const {iceServers} = mediaConnectionConfig;
|
|
1805
|
+
|
|
1806
|
+
assert.calledOnceWithMatch(multistreamRoapMediaConnectionConstructorStub, {
|
|
1807
|
+
iceServers,
|
|
1808
|
+
}, meetingId);
|
|
1809
|
+
|
|
1810
|
+
for(let type in localStreams){
|
|
1811
|
+
const stream = localStreams[type];
|
|
1812
|
+
if(stream !== undefined){
|
|
1813
|
+
switch(type){
|
|
1814
|
+
case 'audio':
|
|
1815
|
+
assert.calledOnceWithExactly(meeting.sendSlotManager.getSlot(MediaType.AudioMain).publishStream, stream);
|
|
1816
|
+
break;
|
|
1817
|
+
case 'video':
|
|
1818
|
+
assert.calledOnceWithExactly(meeting.sendSlotManager.getSlot(MediaType.VideoMain).publishStream, stream);
|
|
1819
|
+
break;
|
|
1820
|
+
case 'screenShareAudio':
|
|
1821
|
+
assert.calledOnceWithExactly(meeting.sendSlotManager.getSlot(MediaType.AudioSlides).publishStream, stream);
|
|
1822
|
+
break;
|
|
1823
|
+
case 'screenShareVideo':
|
|
1824
|
+
assert.calledOnceWithExactly(meeting.sendSlotManager.getSlot(MediaType.VideoSlides).publishStream, stream);
|
|
1825
|
+
break;
|
|
1869
1826
|
}
|
|
1870
|
-
}
|
|
1871
|
-
} else {
|
|
1872
|
-
assert.calledOnceWithExactly(
|
|
1873
|
-
roapMediaConnectionConstructorStub,
|
|
1874
|
-
mediaConnectionConfig,
|
|
1875
|
-
{
|
|
1876
|
-
localTracks: {
|
|
1877
|
-
audio: localTracks.audio?.underlyingTrack,
|
|
1878
|
-
video: localTracks.video?.underlyingTrack,
|
|
1879
|
-
screenShareVideo: localTracks.screenShareVideo?.underlyingTrack,
|
|
1880
|
-
},
|
|
1881
|
-
direction,
|
|
1882
|
-
remoteQualityLevel,
|
|
1883
|
-
},
|
|
1884
|
-
expectedDebugId
|
|
1885
|
-
);
|
|
1827
|
+
}
|
|
1886
1828
|
}
|
|
1887
|
-
}
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
video: undefined,
|
|
1899
|
-
screenShareVideo: undefined,
|
|
1900
|
-
screenShareAudio: undefined,
|
|
1901
|
-
},
|
|
1902
|
-
direction: {
|
|
1903
|
-
audio: 'sendrecv',
|
|
1904
|
-
video: 'sendrecv',
|
|
1905
|
-
screenShareVideo: 'recvonly',
|
|
1829
|
+
} else {
|
|
1830
|
+
assert.calledOnceWithExactly(roapMediaConnectionConstructorStub, mediaConnectionConfig,
|
|
1831
|
+
{
|
|
1832
|
+
localTracks: {
|
|
1833
|
+
audio: localStreams.audio?.outputTrack,
|
|
1834
|
+
video: localStreams.video?.outputTrack,
|
|
1835
|
+
screenShareVideo: localStreams.screenShareVideo?.outputTrack,
|
|
1836
|
+
screenShareAudio: localStreams.screenShareAudio?.outputTrack,
|
|
1837
|
+
},
|
|
1838
|
+
direction,
|
|
1839
|
+
remoteQualityLevel,
|
|
1906
1840
|
},
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
// and SDP offer was sent with the right audioMuted/videoMuted values
|
|
1913
|
-
checkSdpOfferSent({audioMuted: true, videoMuted: true});
|
|
1841
|
+
expectedDebugId
|
|
1842
|
+
);
|
|
1843
|
+
}
|
|
1844
|
+
};
|
|
1914
1845
|
|
|
1915
|
-
|
|
1916
|
-
|
|
1846
|
+
it('addMedia() works correctly when media is enabled without tracks to publish', async () => {
|
|
1847
|
+
await meeting.addMedia();
|
|
1848
|
+
await simulateRoapOffer();
|
|
1849
|
+
|
|
1850
|
+
// check RoapMediaConnection was created correctly
|
|
1851
|
+
checkMediaConnectionCreated({
|
|
1852
|
+
mediaConnectionConfig: expectedMediaConnectionConfig,
|
|
1853
|
+
localStreams: {
|
|
1854
|
+
audio: undefined,
|
|
1855
|
+
video: undefined,
|
|
1856
|
+
screenShareVideo: undefined,
|
|
1857
|
+
screenShareAudio: undefined,
|
|
1858
|
+
},
|
|
1859
|
+
direction: {
|
|
1860
|
+
audio: 'sendrecv',
|
|
1861
|
+
video: 'sendrecv',
|
|
1862
|
+
screenShareVideo: 'recvonly',
|
|
1863
|
+
},
|
|
1864
|
+
remoteQualityLevel: 'HIGH',
|
|
1865
|
+
expectedDebugId,
|
|
1866
|
+
meetingId: meeting.id
|
|
1917
1867
|
});
|
|
1918
1868
|
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
await simulateRoapOffer();
|
|
1869
|
+
// and SDP offer was sent with the right audioMuted/videoMuted values
|
|
1870
|
+
checkSdpOfferSent({audioMuted: true, videoMuted: true});
|
|
1922
1871
|
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
localTracks: {
|
|
1927
|
-
audio: fakeMicrophoneTrack,
|
|
1928
|
-
video: undefined,
|
|
1929
|
-
screenShareVideo: undefined,
|
|
1930
|
-
screenShareAudio: undefined,
|
|
1931
|
-
},
|
|
1932
|
-
direction: {
|
|
1933
|
-
audio: 'sendrecv',
|
|
1934
|
-
video: 'sendrecv',
|
|
1935
|
-
screenShareVideo: 'recvonly',
|
|
1936
|
-
},
|
|
1937
|
-
remoteQualityLevel: 'HIGH',
|
|
1938
|
-
expectedDebugId,
|
|
1939
|
-
meetingId: meeting.id,
|
|
1940
|
-
});
|
|
1872
|
+
// and that it was the only /media request that was sent
|
|
1873
|
+
assert.calledOnce(locusMediaRequestStub);
|
|
1874
|
+
});
|
|
1941
1875
|
|
|
1942
|
-
|
|
1943
|
-
|
|
1876
|
+
it('addMedia() works correctly when media is enabled with streams to publish', async () => {
|
|
1877
|
+
await meeting.addMedia({localStreams: {microphone: fakeMicrophoneStream}});
|
|
1878
|
+
await simulateRoapOffer();
|
|
1944
1879
|
|
|
1945
|
-
|
|
1946
|
-
|
|
1880
|
+
// check RoapMediaConnection was created correctly
|
|
1881
|
+
checkMediaConnectionCreated({
|
|
1882
|
+
mediaConnectionConfig: expectedMediaConnectionConfig,
|
|
1883
|
+
localStreams: {
|
|
1884
|
+
audio: fakeMicrophoneStream,
|
|
1885
|
+
video: undefined,
|
|
1886
|
+
screenShareVideo: undefined,
|
|
1887
|
+
screenShareAudio: undefined,
|
|
1888
|
+
},
|
|
1889
|
+
direction: {
|
|
1890
|
+
audio: 'sendrecv',
|
|
1891
|
+
video: 'sendrecv',
|
|
1892
|
+
screenShareVideo: 'recvonly',
|
|
1893
|
+
},
|
|
1894
|
+
remoteQualityLevel: 'HIGH',
|
|
1895
|
+
expectedDebugId,
|
|
1896
|
+
meetingId: meeting.id
|
|
1947
1897
|
});
|
|
1948
1898
|
|
|
1949
|
-
|
|
1950
|
-
|
|
1899
|
+
// and SDP offer was sent with the right audioMuted/videoMuted values
|
|
1900
|
+
checkSdpOfferSent({audioMuted: false, videoMuted: true});
|
|
1951
1901
|
|
|
1952
|
-
|
|
1953
|
-
|
|
1902
|
+
// and no other local mute requests were sent to Locus
|
|
1903
|
+
assert.calledOnce(locusMediaRequestStub);
|
|
1904
|
+
});
|
|
1954
1905
|
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
mediaConnectionConfig: expectedMediaConnectionConfig,
|
|
1958
|
-
localTracks: {
|
|
1959
|
-
audio: fakeMicrophoneTrack,
|
|
1960
|
-
video: undefined,
|
|
1961
|
-
screenShareVideo: undefined,
|
|
1962
|
-
screenShareAudio: undefined,
|
|
1963
|
-
},
|
|
1964
|
-
direction: {
|
|
1965
|
-
audio: 'sendrecv',
|
|
1966
|
-
video: 'sendrecv',
|
|
1967
|
-
screenShareVideo: 'recvonly',
|
|
1968
|
-
},
|
|
1969
|
-
remoteQualityLevel: 'HIGH',
|
|
1970
|
-
expectedDebugId,
|
|
1971
|
-
meetingId: meeting.id,
|
|
1972
|
-
});
|
|
1906
|
+
it('addMedia() works correctly when media is enabled with tracks to publish and track is muted', async () => {
|
|
1907
|
+
fakeMicrophoneStream.muted = true;
|
|
1973
1908
|
|
|
1974
|
-
|
|
1975
|
-
|
|
1909
|
+
await meeting.addMedia({localStreams: {microphone: fakeMicrophoneStream}});
|
|
1910
|
+
await simulateRoapOffer();
|
|
1976
1911
|
|
|
1977
|
-
|
|
1978
|
-
|
|
1912
|
+
// check RoapMediaConnection was created correctly
|
|
1913
|
+
checkMediaConnectionCreated({
|
|
1914
|
+
mediaConnectionConfig: expectedMediaConnectionConfig,
|
|
1915
|
+
localStreams: {
|
|
1916
|
+
audio: fakeMicrophoneStream,
|
|
1917
|
+
video: undefined,
|
|
1918
|
+
screenShareVideo: undefined,
|
|
1919
|
+
screenShareAudio: undefined,
|
|
1920
|
+
},
|
|
1921
|
+
direction: {
|
|
1922
|
+
audio: 'sendrecv',
|
|
1923
|
+
video: 'sendrecv',
|
|
1924
|
+
screenShareVideo: 'recvonly',
|
|
1925
|
+
},
|
|
1926
|
+
remoteQualityLevel: 'HIGH',
|
|
1927
|
+
expectedDebugId,
|
|
1928
|
+
meetingId: meeting.id
|
|
1979
1929
|
});
|
|
1930
|
+
// and SDP offer was sent with the right audioMuted/videoMuted values
|
|
1931
|
+
checkSdpOfferSent({audioMuted: true, videoMuted: true});
|
|
1980
1932
|
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
audioEnabled: false,
|
|
1985
|
-
});
|
|
1986
|
-
await simulateRoapOffer();
|
|
1987
|
-
|
|
1988
|
-
// check RoapMediaConnection was created correctly
|
|
1989
|
-
checkMediaConnectionCreated({
|
|
1990
|
-
mediaConnectionConfig: expectedMediaConnectionConfig,
|
|
1991
|
-
localTracks: {
|
|
1992
|
-
audio: fakeMicrophoneTrack,
|
|
1993
|
-
video: undefined,
|
|
1994
|
-
screenShareVideo: undefined,
|
|
1995
|
-
screenShareAudio: undefined,
|
|
1996
|
-
},
|
|
1997
|
-
direction: {
|
|
1998
|
-
audio: 'inactive',
|
|
1999
|
-
video: 'sendrecv',
|
|
2000
|
-
screenShareVideo: 'recvonly',
|
|
2001
|
-
},
|
|
2002
|
-
remoteQualityLevel: 'HIGH',
|
|
2003
|
-
expectedDebugId,
|
|
2004
|
-
meetingId: meeting.id,
|
|
2005
|
-
});
|
|
1933
|
+
// and no other local mute requests were sent to Locus
|
|
1934
|
+
assert.calledOnce(locusMediaRequestStub);
|
|
1935
|
+
});
|
|
2006
1936
|
|
|
2007
|
-
|
|
2008
|
-
|
|
1937
|
+
it('addMedia() works correctly when media is disabled with tracks to publish', async () => {
|
|
1938
|
+
await meeting.addMedia({localStreams: {microphone: fakeMicrophoneStream}, audioEnabled: false});
|
|
1939
|
+
await simulateRoapOffer();
|
|
2009
1940
|
|
|
2010
|
-
|
|
2011
|
-
|
|
1941
|
+
// check RoapMediaConnection was created correctly
|
|
1942
|
+
checkMediaConnectionCreated({
|
|
1943
|
+
mediaConnectionConfig: expectedMediaConnectionConfig,
|
|
1944
|
+
localStreams: {
|
|
1945
|
+
audio: fakeMicrophoneStream,
|
|
1946
|
+
video: undefined,
|
|
1947
|
+
screenShareVideo: undefined,
|
|
1948
|
+
screenShareAudio: undefined,
|
|
1949
|
+
},
|
|
1950
|
+
direction: {
|
|
1951
|
+
audio: 'inactive',
|
|
1952
|
+
video: 'sendrecv',
|
|
1953
|
+
screenShareVideo: 'recvonly',
|
|
1954
|
+
},
|
|
1955
|
+
remoteQualityLevel: 'HIGH',
|
|
1956
|
+
expectedDebugId,
|
|
1957
|
+
meetingId: meeting.id
|
|
2012
1958
|
});
|
|
2013
1959
|
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
await simulateRoapOffer();
|
|
1960
|
+
// and SDP offer was sent with the right audioMuted/videoMuted values
|
|
1961
|
+
checkSdpOfferSent({audioMuted: true, videoMuted: true});
|
|
2017
1962
|
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
localTracks: {
|
|
2022
|
-
audio: undefined,
|
|
2023
|
-
video: undefined,
|
|
2024
|
-
screenShareVideo: undefined,
|
|
2025
|
-
screenShareAudio: undefined,
|
|
2026
|
-
},
|
|
2027
|
-
direction: {
|
|
2028
|
-
audio: 'inactive',
|
|
2029
|
-
video: 'sendrecv',
|
|
2030
|
-
screenShareVideo: 'recvonly',
|
|
2031
|
-
},
|
|
2032
|
-
remoteQualityLevel: 'HIGH',
|
|
2033
|
-
expectedDebugId,
|
|
2034
|
-
meetingId: meeting.id,
|
|
2035
|
-
});
|
|
1963
|
+
// and no other local mute requests were sent to Locus
|
|
1964
|
+
assert.calledOnce(locusMediaRequestStub);
|
|
1965
|
+
});
|
|
2036
1966
|
|
|
2037
|
-
|
|
2038
|
-
|
|
1967
|
+
it('addMedia() works correctly when media is disabled with no tracks to publish', async () => {
|
|
1968
|
+
await meeting.addMedia({audioEnabled: false});
|
|
1969
|
+
await simulateRoapOffer();
|
|
2039
1970
|
|
|
2040
|
-
|
|
2041
|
-
|
|
1971
|
+
// check RoapMediaConnection was created correctly
|
|
1972
|
+
checkMediaConnectionCreated({
|
|
1973
|
+
mediaConnectionConfig: expectedMediaConnectionConfig,
|
|
1974
|
+
localStreams: {
|
|
1975
|
+
audio: undefined,
|
|
1976
|
+
video: undefined,
|
|
1977
|
+
screenShareVideo: undefined,
|
|
1978
|
+
screenShareAudio: undefined,
|
|
1979
|
+
},
|
|
1980
|
+
direction: {
|
|
1981
|
+
audio: 'inactive',
|
|
1982
|
+
video: 'sendrecv',
|
|
1983
|
+
screenShareVideo: 'recvonly',
|
|
1984
|
+
},
|
|
1985
|
+
remoteQualityLevel: 'HIGH',
|
|
1986
|
+
expectedDebugId,
|
|
1987
|
+
meetingId: meeting.id
|
|
2042
1988
|
});
|
|
2043
1989
|
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
1990
|
+
// and SDP offer was sent with the right audioMuted/videoMuted values
|
|
1991
|
+
checkSdpOfferSent({audioMuted: true, videoMuted: true});
|
|
1992
|
+
|
|
1993
|
+
// and no other local mute requests were sent to Locus
|
|
1994
|
+
assert.calledOnce(locusMediaRequestStub);
|
|
1995
|
+
});
|
|
1996
|
+
|
|
1997
|
+
describe('publishStreams()/unpublishStreams() calls', () => {
|
|
1998
|
+
[
|
|
1999
|
+
{mediaEnabled: true, expected: {direction: 'sendrecv', localMuteSentValue: false}},
|
|
2000
|
+
{mediaEnabled: false, expected: {direction: 'inactive', localMuteSentValue: undefined}}
|
|
2001
|
+
]
|
|
2002
|
+
.forEach(({mediaEnabled, expected}) => {
|
|
2003
|
+
it(`first publishStreams() call while media is ${mediaEnabled ? 'enabled' : 'disabled'}`, async () => {
|
|
2055
2004
|
await meeting.addMedia({audioEnabled: mediaEnabled});
|
|
2056
2005
|
await simulateRoapOffer();
|
|
2057
2006
|
|
|
2058
2007
|
resetHistory();
|
|
2059
2008
|
|
|
2060
|
-
await meeting.
|
|
2009
|
+
await meeting.publishStreams({microphone: fakeMicrophoneStream});
|
|
2061
2010
|
await stableState();
|
|
2062
2011
|
|
|
2063
2012
|
if (expected.localMuteSentValue !== undefined) {
|
|
@@ -2070,14 +2019,12 @@ describe('plugin-meetings', () => {
|
|
|
2070
2019
|
} else {
|
|
2071
2020
|
assert.notCalled(locusMediaRequestStub);
|
|
2072
2021
|
}
|
|
2022
|
+
|
|
2073
2023
|
if (isMultistream) {
|
|
2074
|
-
assert.calledOnceWithExactly(
|
|
2075
|
-
fakeMultistreamRoapMediaConnection.publishTrack,
|
|
2076
|
-
fakeMicrophoneTrack
|
|
2077
|
-
);
|
|
2024
|
+
assert.calledOnceWithExactly(meeting.sendSlotManager.getSlot(MediaType.AudioMain).publishStream, fakeMicrophoneStream);
|
|
2078
2025
|
} else {
|
|
2079
2026
|
assert.calledOnceWithExactly(fakeRoapMediaConnection.update, {
|
|
2080
|
-
localTracks: {audio:
|
|
2027
|
+
localTracks: { audio: fakeMicrophoneStream.outputTrack, video: null, screenShareVideo: null, screenShareAudio: null },
|
|
2081
2028
|
direction: {
|
|
2082
2029
|
audio: expected.direction,
|
|
2083
2030
|
video: 'sendrecv',
|
|
@@ -2088,40 +2035,34 @@ describe('plugin-meetings', () => {
|
|
|
2088
2035
|
}
|
|
2089
2036
|
});
|
|
2090
2037
|
|
|
2091
|
-
it(`second
|
|
2092
|
-
mediaEnabled ? 'enabled' : 'disabled'
|
|
2093
|
-
}`, async () => {
|
|
2038
|
+
it(`second publishStreams() call while media is ${mediaEnabled ? 'enabled' : 'disabled'}`, async () => {
|
|
2094
2039
|
await meeting.addMedia({audioEnabled: mediaEnabled});
|
|
2095
2040
|
await simulateRoapOffer();
|
|
2096
|
-
await meeting.
|
|
2041
|
+
await meeting.publishStreams({microphone: fakeMicrophoneStream});
|
|
2097
2042
|
await stableState();
|
|
2098
2043
|
|
|
2099
2044
|
resetHistory();
|
|
2100
2045
|
|
|
2101
|
-
const
|
|
2102
|
-
const fakeMicrophoneTrack2 = {
|
|
2103
|
-
id: 'fake mic 2',
|
|
2046
|
+
const fakeMicrophoneStream2 = {
|
|
2104
2047
|
on: sinon.stub(),
|
|
2105
2048
|
off: sinon.stub(),
|
|
2049
|
+
muted: false,
|
|
2106
2050
|
setUnmuteAllowed: sinon.stub(),
|
|
2107
2051
|
setMuted: sinon.stub(),
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
}
|
|
2052
|
+
outputTrack:{
|
|
2053
|
+
id: 'fake mic 2',
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2112
2056
|
|
|
2113
|
-
await meeting.
|
|
2057
|
+
await meeting.publishStreams({microphone: fakeMicrophoneStream2});
|
|
2114
2058
|
await stableState();
|
|
2115
2059
|
|
|
2116
2060
|
// only the roap media connection should be updated
|
|
2117
2061
|
if (isMultistream) {
|
|
2118
|
-
assert.calledOnceWithExactly(
|
|
2119
|
-
fakeMultistreamRoapMediaConnection.publishTrack,
|
|
2120
|
-
fakeMicrophoneTrack2
|
|
2121
|
-
);
|
|
2062
|
+
assert.calledOnceWithExactly(meeting.sendSlotManager.getSlot(MediaType.AudioMain).publishStream, fakeMicrophoneStream2);
|
|
2122
2063
|
} else {
|
|
2123
2064
|
assert.calledOnceWithExactly(fakeRoapMediaConnection.update, {
|
|
2124
|
-
localTracks: {audio:
|
|
2065
|
+
localTracks: { audio: fakeMicrophoneStream2.outputTrack, video: null, screenShareVideo: null, screenShareAudio: null },
|
|
2125
2066
|
direction: {
|
|
2126
2067
|
audio: expected.direction,
|
|
2127
2068
|
video: 'sendrecv',
|
|
@@ -2135,28 +2076,23 @@ describe('plugin-meetings', () => {
|
|
|
2135
2076
|
assert.notCalled(locusMediaRequestStub);
|
|
2136
2077
|
});
|
|
2137
2078
|
|
|
2138
|
-
it(`
|
|
2139
|
-
mediaEnabled ? 'enabled' : 'disabled'
|
|
2140
|
-
}`, async () => {
|
|
2079
|
+
it(`unpublishStreams() call while media is ${mediaEnabled ? 'enabled' : 'disabled'}`, async () => {
|
|
2141
2080
|
await meeting.addMedia({audioEnabled: mediaEnabled});
|
|
2142
2081
|
await simulateRoapOffer();
|
|
2143
|
-
await meeting.
|
|
2082
|
+
await meeting.publishStreams({microphone: fakeMicrophoneStream});
|
|
2144
2083
|
await stableState();
|
|
2145
2084
|
|
|
2146
2085
|
resetHistory();
|
|
2147
2086
|
|
|
2148
|
-
await meeting.
|
|
2087
|
+
await meeting.unpublishStreams([fakeMicrophoneStream]);
|
|
2149
2088
|
await stableState();
|
|
2150
2089
|
|
|
2151
2090
|
// the roap media connection should be updated
|
|
2152
2091
|
if (isMultistream) {
|
|
2153
|
-
assert.
|
|
2154
|
-
fakeMultistreamRoapMediaConnection.unpublishTrack,
|
|
2155
|
-
fakeMicrophoneTrack
|
|
2156
|
-
);
|
|
2092
|
+
assert.calledOnce(meeting.sendSlotManager.getSlot(MediaType.AudioMain).unpublishStream);
|
|
2157
2093
|
} else {
|
|
2158
2094
|
assert.calledOnceWithExactly(fakeRoapMediaConnection.update, {
|
|
2159
|
-
localTracks: {audio: null, video: null, screenShareVideo: null},
|
|
2095
|
+
localTracks: { audio: null, video: null, screenShareVideo: null, screenShareAudio: null },
|
|
2160
2096
|
direction: {
|
|
2161
2097
|
audio: expected.direction,
|
|
2162
2098
|
video: 'sendrecv',
|
|
@@ -2179,155 +2115,152 @@ describe('plugin-meetings', () => {
|
|
|
2179
2115
|
}
|
|
2180
2116
|
});
|
|
2181
2117
|
});
|
|
2182
|
-
|
|
2118
|
+
});
|
|
2183
2119
|
|
|
2184
|
-
|
|
2185
|
-
const addMedia = async (enableMedia, track) => {
|
|
2186
|
-
await meeting.addMedia({audioEnabled: enableMedia, localTracks: {microphone: track}});
|
|
2187
|
-
await simulateRoapOffer();
|
|
2120
|
+
describe('updateMedia()', () => {
|
|
2188
2121
|
|
|
2189
|
-
|
|
2190
|
-
};
|
|
2122
|
+
const addMedia = async (enableMedia, stream) => {
|
|
2123
|
+
await meeting.addMedia({audioEnabled: enableMedia, localStreams: {microphone: stream}});
|
|
2124
|
+
await simulateRoapOffer();
|
|
2191
2125
|
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2126
|
+
resetHistory();
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2129
|
+
const checkAudioEnabled = (expectedStream, expectedDirection) => {
|
|
2130
|
+
if (isMultistream) {
|
|
2131
|
+
assert.equal(meeting.sendSlotManager.getSlot(MediaType.AudioMain).active, expectedDirection !== 'inactive');
|
|
2132
|
+
} else {
|
|
2133
|
+
assert.calledOnceWithExactly(fakeRoapMediaConnection.update, {
|
|
2134
|
+
localTracks: { audio: expectedStream?.outputTrack ?? null, video: null, screenShareVideo: null, screenShareAudio: null },
|
|
2135
|
+
direction: {
|
|
2136
|
+
audio: expectedDirection,
|
|
2137
|
+
video: 'sendrecv',
|
|
2138
|
+
screenShareVideo: 'recvonly',
|
|
2139
|
+
},
|
|
2140
|
+
remoteQualityLevel: 'HIGH'
|
|
2141
|
+
});
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2210
2144
|
|
|
2211
|
-
|
|
2212
|
-
|
|
2145
|
+
it('updateMedia() disables media when nothing is published', async () => {
|
|
2146
|
+
await addMedia(true);
|
|
2213
2147
|
|
|
2214
|
-
|
|
2148
|
+
await meeting.updateMedia({audioEnabled: false});
|
|
2215
2149
|
|
|
2216
|
-
|
|
2217
|
-
|
|
2150
|
+
// the roap media connection should be updated
|
|
2151
|
+
checkAudioEnabled(null, 'inactive');
|
|
2218
2152
|
|
|
2219
|
-
|
|
2220
|
-
|
|
2153
|
+
// and that would trigger a new offer so we simulate it happening
|
|
2154
|
+
await simulateRoapOffer();
|
|
2221
2155
|
|
|
2222
|
-
|
|
2223
|
-
|
|
2156
|
+
// check SDP offer was sent with the right audioMuted/videoMuted values
|
|
2157
|
+
checkSdpOfferSent({audioMuted: true, videoMuted: true});
|
|
2224
2158
|
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2159
|
+
// and no other local mute requests were sent to Locus
|
|
2160
|
+
assert.calledOnce(locusMediaRequestStub);
|
|
2161
|
+
});
|
|
2228
2162
|
|
|
2229
|
-
|
|
2230
|
-
|
|
2163
|
+
it('updateMedia() enables media when nothing is published', async () => {
|
|
2164
|
+
await addMedia(false);
|
|
2231
2165
|
|
|
2232
|
-
|
|
2166
|
+
await meeting.updateMedia({audioEnabled: true});
|
|
2233
2167
|
|
|
2234
|
-
|
|
2235
|
-
|
|
2168
|
+
// the roap media connection should be updated
|
|
2169
|
+
checkAudioEnabled(null, 'sendrecv');
|
|
2236
2170
|
|
|
2237
|
-
|
|
2238
|
-
|
|
2171
|
+
// and that would trigger a new offer so we simulate it happening
|
|
2172
|
+
await simulateRoapOffer();
|
|
2239
2173
|
|
|
2240
|
-
|
|
2241
|
-
|
|
2174
|
+
// check SDP offer was sent with the right audioMuted/videoMuted values
|
|
2175
|
+
checkSdpOfferSent({audioMuted: true, videoMuted: true});
|
|
2242
2176
|
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2177
|
+
// and no other local mute requests were sent to Locus
|
|
2178
|
+
assert.calledOnce(locusMediaRequestStub);
|
|
2179
|
+
});
|
|
2246
2180
|
|
|
2247
|
-
|
|
2248
|
-
|
|
2181
|
+
it('updateMedia() disables media when stream is published', async () => {
|
|
2182
|
+
await addMedia(true, fakeMicrophoneStream);
|
|
2249
2183
|
|
|
2250
|
-
|
|
2251
|
-
|
|
2184
|
+
await meeting.updateMedia({audioEnabled: false});
|
|
2185
|
+
await stableState();
|
|
2252
2186
|
|
|
2253
|
-
|
|
2254
|
-
|
|
2187
|
+
// the roap media connection should be updated
|
|
2188
|
+
checkAudioEnabled(fakeMicrophoneStream, 'inactive');
|
|
2255
2189
|
|
|
2256
|
-
|
|
2190
|
+
checkLocalMuteSentToLocus({audioMuted: true, videoMuted: true});
|
|
2257
2191
|
|
|
2258
|
-
|
|
2192
|
+
locusMediaRequestStub.resetHistory();
|
|
2259
2193
|
|
|
2260
|
-
|
|
2261
|
-
|
|
2194
|
+
// and that would trigger a new offer so we simulate it happening
|
|
2195
|
+
await simulateRoapOffer();
|
|
2262
2196
|
|
|
2263
|
-
|
|
2264
|
-
|
|
2197
|
+
// check SDP offer was sent with the right audioMuted/videoMuted values
|
|
2198
|
+
checkSdpOfferSent({audioMuted: true, videoMuted: true});
|
|
2265
2199
|
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2200
|
+
// and no other local mute requests were sent to Locus
|
|
2201
|
+
assert.calledOnce(locusMediaRequestStub);
|
|
2202
|
+
});
|
|
2269
2203
|
|
|
2270
|
-
|
|
2271
|
-
|
|
2204
|
+
it('updateMedia() enables media when stream is published', async () => {
|
|
2205
|
+
await addMedia(false, fakeMicrophoneStream);
|
|
2272
2206
|
|
|
2273
|
-
|
|
2274
|
-
|
|
2207
|
+
await meeting.updateMedia({audioEnabled: true});
|
|
2208
|
+
await stableState();
|
|
2275
2209
|
|
|
2276
|
-
|
|
2277
|
-
|
|
2210
|
+
// the roap media connection should be updated
|
|
2211
|
+
checkAudioEnabled(fakeMicrophoneStream, 'sendrecv');
|
|
2278
2212
|
|
|
2279
|
-
|
|
2213
|
+
checkLocalMuteSentToLocus({audioMuted: false, videoMuted: true});
|
|
2280
2214
|
|
|
2281
|
-
|
|
2215
|
+
locusMediaRequestStub.resetHistory();
|
|
2282
2216
|
|
|
2283
|
-
|
|
2284
|
-
|
|
2217
|
+
// and that would trigger a new offer so we simulate it happening
|
|
2218
|
+
await simulateRoapOffer();
|
|
2285
2219
|
|
|
2286
|
-
|
|
2287
|
-
|
|
2220
|
+
// check SDP offer was sent with the right audioMuted/videoMuted values
|
|
2221
|
+
checkSdpOfferSent({audioMuted: false, videoMuted: true});
|
|
2288
2222
|
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
});
|
|
2223
|
+
// and no other local mute requests were sent to Locus
|
|
2224
|
+
assert.calledOnce(locusMediaRequestStub);
|
|
2292
2225
|
});
|
|
2226
|
+
});
|
|
2293
2227
|
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2228
|
+
[
|
|
2229
|
+
{mute: true, title: 'muting a track before confluence is created'},
|
|
2230
|
+
{mute: false, title: 'unmuting a track before confluence is created'}
|
|
2231
|
+
].forEach(({mute, title}) =>
|
|
2232
|
+
it(title, async () => {
|
|
2233
|
+
// initialize the microphone mute state to opposite of what we do in the test
|
|
2234
|
+
fakeMicrophoneStream.muted = !mute;
|
|
2301
2235
|
|
|
2302
|
-
|
|
2303
|
-
|
|
2236
|
+
await meeting.addMedia({localStreams: {microphone: fakeMicrophoneStream}});
|
|
2237
|
+
await stableState();
|
|
2304
2238
|
|
|
2305
|
-
|
|
2239
|
+
resetHistory();
|
|
2306
2240
|
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2241
|
+
assert.equal(fakeMicrophoneStream.on.getCall(0).args[0], StreamEventNames.MuteStateChange);
|
|
2242
|
+
const mutedListener = fakeMicrophoneStream.on.getCall(0).args[1];
|
|
2243
|
+
// simulate track being muted
|
|
2244
|
+
mutedListener(mute);
|
|
2311
2245
|
|
|
2312
|
-
|
|
2246
|
+
await stableState();
|
|
2313
2247
|
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2248
|
+
// nothing should happen
|
|
2249
|
+
assert.notCalled(locusMediaRequestStub);
|
|
2250
|
+
assert.notCalled(fakeRoapMediaConnection.update);
|
|
2317
2251
|
|
|
2318
|
-
|
|
2319
|
-
|
|
2252
|
+
// now simulate roap offer
|
|
2253
|
+
await simulateRoapOffer();
|
|
2320
2254
|
|
|
2321
|
-
|
|
2322
|
-
|
|
2255
|
+
// it should be sent with the right mute status
|
|
2256
|
+
checkSdpOfferSent({audioMuted: mute, videoMuted: true});
|
|
2323
2257
|
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
);
|
|
2258
|
+
// nothing else should happen
|
|
2259
|
+
assert.calledOnce(locusMediaRequestStub);
|
|
2260
|
+
assert.notCalled(fakeRoapMediaConnection.update);
|
|
2261
|
+
})
|
|
2262
|
+
);
|
|
2263
|
+
}));
|
|
2331
2264
|
|
|
2332
2265
|
describe('#acknowledge', () => {
|
|
2333
2266
|
it('should have #acknowledge', () => {
|
|
@@ -2393,11 +2326,11 @@ describe('plugin-meetings', () => {
|
|
|
2393
2326
|
.stub()
|
|
2394
2327
|
.returns(Promise.resolve({body: 'test'}));
|
|
2395
2328
|
meeting.locusInfo.onFullLocus = sinon.stub().returns(true);
|
|
2396
|
-
meeting.
|
|
2329
|
+
meeting.cleanupLocalStreams = sinon.stub().returns(Promise.resolve());
|
|
2397
2330
|
meeting.closeRemoteStream = sinon.stub().returns(Promise.resolve());
|
|
2398
|
-
sandbox.stub(meeting, '
|
|
2331
|
+
sandbox.stub(meeting, 'closeRemoteStreams').returns(Promise.resolve());
|
|
2399
2332
|
meeting.closePeerConnections = sinon.stub().returns(Promise.resolve());
|
|
2400
|
-
meeting.
|
|
2333
|
+
meeting.unsetRemoteStreams = sinon.stub();
|
|
2401
2334
|
meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
|
|
2402
2335
|
meeting.unsetPeerConnections = sinon.stub().returns(true);
|
|
2403
2336
|
meeting.logger.error = sinon.stub().returns(true);
|
|
@@ -2417,10 +2350,10 @@ describe('plugin-meetings', () => {
|
|
|
2417
2350
|
assert.exists(leave.then);
|
|
2418
2351
|
await leave;
|
|
2419
2352
|
assert.calledOnce(meeting.meetingRequest.leaveMeeting);
|
|
2420
|
-
assert.calledOnce(meeting.
|
|
2421
|
-
assert.calledOnce(meeting.
|
|
2353
|
+
assert.calledOnce(meeting.cleanupLocalStreams);
|
|
2354
|
+
assert.calledOnce(meeting.closeRemoteStreams);
|
|
2422
2355
|
assert.calledOnce(meeting.closePeerConnections);
|
|
2423
|
-
assert.calledOnce(meeting.
|
|
2356
|
+
assert.calledOnce(meeting.unsetRemoteStreams);
|
|
2424
2357
|
assert.calledOnce(meeting.unsetPeerConnections);
|
|
2425
2358
|
});
|
|
2426
2359
|
|
|
@@ -2562,7 +2495,7 @@ describe('plugin-meetings', () => {
|
|
|
2562
2495
|
meeting.locusInfo.mediaShares = [{name: 'content', url: url1}];
|
|
2563
2496
|
meeting.locusInfo.self = {url: url1};
|
|
2564
2497
|
meeting.meetingRequest.changeMeetingFloor = sinon.stub().returns(Promise.resolve());
|
|
2565
|
-
meeting.mediaProperties.
|
|
2498
|
+
meeting.mediaProperties.shareVideoStream = {};
|
|
2566
2499
|
meeting.mediaProperties.mediaDirection.sendShare = true;
|
|
2567
2500
|
meeting.state = 'JOINED';
|
|
2568
2501
|
});
|
|
@@ -2620,16 +2553,17 @@ describe('plugin-meetings', () => {
|
|
|
2620
2553
|
describe('#updateMedia', () => {
|
|
2621
2554
|
let sandbox;
|
|
2622
2555
|
|
|
2623
|
-
const
|
|
2624
|
-
|
|
2556
|
+
const createFakeLocalStream = () => ({
|
|
2557
|
+
outputTrack: {id: 'fake underlying track'},
|
|
2625
2558
|
});
|
|
2626
2559
|
beforeEach(() => {
|
|
2627
2560
|
sandbox = sinon.createSandbox();
|
|
2628
|
-
meeting.audio = {enable: sinon.stub()};
|
|
2629
|
-
meeting.video = {enable: sinon.stub()};
|
|
2630
|
-
meeting.mediaProperties.
|
|
2631
|
-
meeting.mediaProperties.
|
|
2632
|
-
meeting.mediaProperties.
|
|
2561
|
+
meeting.audio = { enable: sinon.stub()};
|
|
2562
|
+
meeting.video = { enable: sinon.stub()};
|
|
2563
|
+
meeting.mediaProperties.audioStream = createFakeLocalStream();
|
|
2564
|
+
meeting.mediaProperties.videoStream = createFakeLocalStream();
|
|
2565
|
+
meeting.mediaProperties.shareVideoStream = createFakeLocalStream();
|
|
2566
|
+
meeting.mediaProperties.shareAudioStream = createFakeLocalStream();
|
|
2633
2567
|
meeting.mediaProperties.mediaDirection = {
|
|
2634
2568
|
sendAudio: true,
|
|
2635
2569
|
sendVideo: true,
|
|
@@ -2637,12 +2571,18 @@ describe('plugin-meetings', () => {
|
|
|
2637
2571
|
receiveAudio: true,
|
|
2638
2572
|
receiveVideo: true,
|
|
2639
2573
|
receiveShare: true,
|
|
2574
|
+
}
|
|
2575
|
+
const fakeMultistreamRoapMediaConnection = {
|
|
2576
|
+
createSendSlot: () => {}
|
|
2640
2577
|
};
|
|
2578
|
+
sinon.stub(fakeMultistreamRoapMediaConnection,'createSendSlot').returns({active: true});
|
|
2579
|
+
meeting.sendSlotManager.createSlot(fakeMultistreamRoapMediaConnection,MediaType.AudioMain);
|
|
2641
2580
|
});
|
|
2642
2581
|
|
|
2643
2582
|
afterEach(() => {
|
|
2644
2583
|
sandbox.restore();
|
|
2645
2584
|
sandbox = null;
|
|
2585
|
+
sinon.restore();
|
|
2646
2586
|
});
|
|
2647
2587
|
|
|
2648
2588
|
forEach(
|
|
@@ -2659,8 +2599,8 @@ describe('plugin-meetings', () => {
|
|
|
2659
2599
|
|
|
2660
2600
|
await meeting.updateMedia({audioEnabled});
|
|
2661
2601
|
|
|
2662
|
-
assert.
|
|
2663
|
-
meeting.
|
|
2602
|
+
assert.equal(
|
|
2603
|
+
meeting.sendSlotManager.getSlot(MediaType.AudioMain).active,
|
|
2664
2604
|
enableMultistreamAudio
|
|
2665
2605
|
);
|
|
2666
2606
|
assert.calledOnceWithExactly(meeting.audio.enable, meeting, enableMultistreamAudio);
|
|
@@ -2692,19 +2632,24 @@ describe('plugin-meetings', () => {
|
|
|
2692
2632
|
|
|
2693
2633
|
// and check that update is called with the original args
|
|
2694
2634
|
assert.calledOnce(meeting.mediaProperties.webrtcMediaConnection.update);
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2635
|
+
|
|
2636
|
+
assert.calledWith(
|
|
2637
|
+
meeting.mediaProperties.webrtcMediaConnection.update,
|
|
2638
|
+
{
|
|
2639
|
+
localTracks: {
|
|
2640
|
+
audio: meeting.mediaProperties.audioStream.outputTrack,
|
|
2641
|
+
video: meeting.mediaProperties.videoStream.outputTrack,
|
|
2642
|
+
screenShareVideo: meeting.mediaProperties.shareVideoStream.outputTrack,
|
|
2643
|
+
screenShareAudio: meeting.mediaProperties.shareVideoStream.outputTrack,
|
|
2644
|
+
},
|
|
2645
|
+
direction: {
|
|
2646
|
+
audio: 'inactive',
|
|
2647
|
+
video: 'inactive',
|
|
2648
|
+
screenShareVideo: 'sendrecv',
|
|
2649
|
+
},
|
|
2650
|
+
remoteQualityLevel: 'HIGH',
|
|
2651
|
+
}
|
|
2652
|
+
);
|
|
2708
2653
|
assert.isTrue(myPromiseResolved);
|
|
2709
2654
|
});
|
|
2710
2655
|
});
|
|
@@ -2722,9 +2667,9 @@ describe('plugin-meetings', () => {
|
|
|
2722
2667
|
receiveVideo: true,
|
|
2723
2668
|
};
|
|
2724
2669
|
meeting.mediaProperties.mediaDirection = mediaDirection;
|
|
2725
|
-
meeting.mediaProperties.
|
|
2670
|
+
meeting.mediaProperties.remoteVideoStream = sinon
|
|
2726
2671
|
.stub()
|
|
2727
|
-
.returns({
|
|
2672
|
+
.returns({outputTrack: {id: 'some mock id'}});
|
|
2728
2673
|
|
|
2729
2674
|
meeting.meetingRequest.changeVideoLayoutDebounced = sinon
|
|
2730
2675
|
.stub()
|
|
@@ -2746,7 +2691,7 @@ describe('plugin-meetings', () => {
|
|
|
2746
2691
|
|
|
2747
2692
|
it('should have receiveVideo true and remote video track should exist', () => {
|
|
2748
2693
|
assert.equal(meeting.mediaProperties.mediaDirection.receiveVideo, true);
|
|
2749
|
-
assert.exists(meeting.mediaProperties.
|
|
2694
|
+
assert.exists(meeting.mediaProperties.remoteVideoStream);
|
|
2750
2695
|
});
|
|
2751
2696
|
|
|
2752
2697
|
it('has layoutType which exists in the list of allowed layoutTypes and should call meetingRequest changeVideoLayoutDebounced method', async () => {
|
|
@@ -2801,7 +2746,7 @@ describe('plugin-meetings', () => {
|
|
|
2801
2746
|
});
|
|
2802
2747
|
|
|
2803
2748
|
meeting.mediaProperties.mediaDirection.receiveShare = true;
|
|
2804
|
-
meeting.mediaProperties.
|
|
2749
|
+
meeting.mediaProperties.remoteShareStream = sinon.stub().returns({mockTrack: 'mockTrack'});
|
|
2805
2750
|
|
|
2806
2751
|
// now call it again with just content
|
|
2807
2752
|
await meeting.changeVideoLayout(layoutTypeSingle, {content: {width: 500, height: 600}});
|
|
@@ -2870,7 +2815,7 @@ describe('plugin-meetings', () => {
|
|
|
2870
2815
|
|
|
2871
2816
|
it('does not call changeVideoLayoutDebounced if renderInfo content changes only very slightly', async () => {
|
|
2872
2817
|
meeting.mediaProperties.mediaDirection.receiveShare = true;
|
|
2873
|
-
meeting.mediaProperties.
|
|
2818
|
+
meeting.mediaProperties.remoteShareStream = sinon.stub().returns({mockTrack: 'mockTrack'});
|
|
2874
2819
|
|
|
2875
2820
|
await meeting.changeVideoLayout(layoutTypeSingle, {
|
|
2876
2821
|
main: {width: 500, height: 510},
|
|
@@ -2910,7 +2855,7 @@ describe('plugin-meetings', () => {
|
|
|
2910
2855
|
|
|
2911
2856
|
it('rounds the width and height values to nearest integers', async () => {
|
|
2912
2857
|
meeting.mediaProperties.mediaDirection.receiveShare = true;
|
|
2913
|
-
meeting.mediaProperties.
|
|
2858
|
+
meeting.mediaProperties.remoteShareStream = sinon.stub().returns({mockTrack: 'mockTrack'});
|
|
2914
2859
|
|
|
2915
2860
|
await meeting.changeVideoLayout(layoutTypeSingle, {
|
|
2916
2861
|
main: {width: 500.5, height: 510.09},
|
|
@@ -3141,7 +3086,7 @@ describe('plugin-meetings', () => {
|
|
|
3141
3086
|
beforeEach(() => {
|
|
3142
3087
|
meeting.locusId = 'locus-id';
|
|
3143
3088
|
meeting.id = 'meeting-id';
|
|
3144
|
-
FAKE_OPTIONS = {meetingId: meeting.id};
|
|
3089
|
+
FAKE_OPTIONS = {meetingId: meeting.id, sendCAevents: true};
|
|
3145
3090
|
});
|
|
3146
3091
|
|
|
3147
3092
|
it('calls meetingInfoProvider with all the right parameters and parses the result', async () => {
|
|
@@ -3161,6 +3106,7 @@ describe('plugin-meetings', () => {
|
|
|
3161
3106
|
password: FAKE_PASSWORD,
|
|
3162
3107
|
captchaCode: FAKE_CAPTCHA_CODE,
|
|
3163
3108
|
extraParams: FAKE_EXTRA_PARAMS,
|
|
3109
|
+
sendCAevents: true,
|
|
3164
3110
|
});
|
|
3165
3111
|
|
|
3166
3112
|
assert.calledWith(
|
|
@@ -3212,7 +3158,7 @@ describe('plugin-meetings', () => {
|
|
|
3212
3158
|
const clock = sinon.useFakeTimers();
|
|
3213
3159
|
const clearTimeoutSpy = sinon.spy(clock, 'clearTimeout');
|
|
3214
3160
|
|
|
3215
|
-
await meeting.fetchMeetingInfo({});
|
|
3161
|
+
await meeting.fetchMeetingInfo({sendCAevents: false});
|
|
3216
3162
|
|
|
3217
3163
|
// clear timer
|
|
3218
3164
|
assert.calledWith(clearTimeoutSpy, FAKE_TIMEOUT_FETCHMEETINGINFO_ID);
|
|
@@ -3229,7 +3175,7 @@ describe('plugin-meetings', () => {
|
|
|
3229
3175
|
undefined,
|
|
3230
3176
|
meeting.locusId,
|
|
3231
3177
|
{},
|
|
3232
|
-
{meetingId: meeting.id}
|
|
3178
|
+
{meetingId: meeting.id, sendCAevents: false}
|
|
3233
3179
|
);
|
|
3234
3180
|
|
|
3235
3181
|
// parseMeeting info
|
|
@@ -3308,7 +3254,7 @@ describe('plugin-meetings', () => {
|
|
|
3308
3254
|
.throws(new MeetingInfoV2PasswordError(403004, FAKE_MEETING_INFO)),
|
|
3309
3255
|
};
|
|
3310
3256
|
|
|
3311
|
-
await assert.isRejected(meeting.fetchMeetingInfo({}), PasswordError);
|
|
3257
|
+
await assert.isRejected(meeting.fetchMeetingInfo({sendCAevents: true}), PasswordError);
|
|
3312
3258
|
|
|
3313
3259
|
assert.calledWith(
|
|
3314
3260
|
meeting.attrs.meetingInfoProvider.fetchMeetingInfo,
|
|
@@ -3319,7 +3265,7 @@ describe('plugin-meetings', () => {
|
|
|
3319
3265
|
undefined,
|
|
3320
3266
|
'locus-id',
|
|
3321
3267
|
{},
|
|
3322
|
-
{meetingId: meeting.id}
|
|
3268
|
+
{meetingId: meeting.id, sendCAevents: true}
|
|
3323
3269
|
);
|
|
3324
3270
|
|
|
3325
3271
|
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
|
@@ -3341,7 +3287,7 @@ describe('plugin-meetings', () => {
|
|
|
3341
3287
|
.throws(new MeetingInfoV2PolicyError(123456, FAKE_MEETING_INFO, 'a message')),
|
|
3342
3288
|
};
|
|
3343
3289
|
|
|
3344
|
-
await assert.isRejected(meeting.fetchMeetingInfo({}), PermissionError);
|
|
3290
|
+
await assert.isRejected(meeting.fetchMeetingInfo({sendCAevents: true}), PermissionError);
|
|
3345
3291
|
|
|
3346
3292
|
assert.calledWith(
|
|
3347
3293
|
meeting.attrs.meetingInfoProvider.fetchMeetingInfo,
|
|
@@ -3352,7 +3298,7 @@ describe('plugin-meetings', () => {
|
|
|
3352
3298
|
undefined,
|
|
3353
3299
|
'locus-id',
|
|
3354
3300
|
{},
|
|
3355
|
-
{meetingId: meeting.id}
|
|
3301
|
+
{meetingId: meeting.id, sendCAevents: true}
|
|
3356
3302
|
);
|
|
3357
3303
|
|
|
3358
3304
|
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
|
@@ -3373,6 +3319,7 @@ describe('plugin-meetings', () => {
|
|
|
3373
3319
|
await assert.isRejected(
|
|
3374
3320
|
meeting.fetchMeetingInfo({
|
|
3375
3321
|
password: 'aaa',
|
|
3322
|
+
sendCAevents: true
|
|
3376
3323
|
}),
|
|
3377
3324
|
CaptchaError
|
|
3378
3325
|
);
|
|
@@ -3386,7 +3333,7 @@ describe('plugin-meetings', () => {
|
|
|
3386
3333
|
undefined,
|
|
3387
3334
|
'locus-id',
|
|
3388
3335
|
{},
|
|
3389
|
-
{meetingId: meeting.id}
|
|
3336
|
+
{meetingId: meeting.id, sendCAevents: true}
|
|
3390
3337
|
);
|
|
3391
3338
|
|
|
3392
3339
|
assert.deepEqual(meeting.meetingInfo, {});
|
|
@@ -3418,6 +3365,7 @@ describe('plugin-meetings', () => {
|
|
|
3418
3365
|
meeting.fetchMeetingInfo({
|
|
3419
3366
|
password: 'aaa',
|
|
3420
3367
|
captchaCode: 'bbb',
|
|
3368
|
+
sendCAevents: true,
|
|
3421
3369
|
}),
|
|
3422
3370
|
CaptchaError
|
|
3423
3371
|
);
|
|
@@ -3431,7 +3379,7 @@ describe('plugin-meetings', () => {
|
|
|
3431
3379
|
undefined,
|
|
3432
3380
|
'locus-id',
|
|
3433
3381
|
{},
|
|
3434
|
-
{meetingId: meeting.id}
|
|
3382
|
+
{meetingId: meeting.id, sendCAevents: true}
|
|
3435
3383
|
);
|
|
3436
3384
|
|
|
3437
3385
|
assert.deepEqual(meeting.meetingInfo, {});
|
|
@@ -3453,6 +3401,7 @@ describe('plugin-meetings', () => {
|
|
|
3453
3401
|
|
|
3454
3402
|
await meeting.fetchMeetingInfo({
|
|
3455
3403
|
password: 'aaa',
|
|
3404
|
+
sendCAevents: true,
|
|
3456
3405
|
});
|
|
3457
3406
|
|
|
3458
3407
|
assert.calledWith(
|
|
@@ -3464,7 +3413,7 @@ describe('plugin-meetings', () => {
|
|
|
3464
3413
|
undefined,
|
|
3465
3414
|
'locus-id',
|
|
3466
3415
|
{},
|
|
3467
|
-
{meetingId: meeting.id}
|
|
3416
|
+
{meetingId: meeting.id, sendCAevents: true}
|
|
3468
3417
|
);
|
|
3469
3418
|
|
|
3470
3419
|
assert.deepEqual(meeting.meetingInfo, {
|
|
@@ -3504,6 +3453,7 @@ describe('plugin-meetings', () => {
|
|
|
3504
3453
|
meeting.fetchMeetingInfo({
|
|
3505
3454
|
password: 'aaa',
|
|
3506
3455
|
captchaCode: 'bbb',
|
|
3456
|
+
sendCAevents: true,
|
|
3507
3457
|
})
|
|
3508
3458
|
);
|
|
3509
3459
|
|
|
@@ -3516,7 +3466,7 @@ describe('plugin-meetings', () => {
|
|
|
3516
3466
|
undefined,
|
|
3517
3467
|
'locus-id',
|
|
3518
3468
|
{},
|
|
3519
|
-
{meetingId: meeting.id}
|
|
3469
|
+
{meetingId: meeting.id, sendCAevents: true}
|
|
3520
3470
|
);
|
|
3521
3471
|
|
|
3522
3472
|
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
|
@@ -3610,6 +3560,7 @@ describe('plugin-meetings', () => {
|
|
|
3610
3560
|
assert.calledWith(meeting.fetchMeetingInfo, {
|
|
3611
3561
|
password: 'password',
|
|
3612
3562
|
captchaCode: 'captcha id',
|
|
3563
|
+
sendCAevents: false,
|
|
3613
3564
|
});
|
|
3614
3565
|
});
|
|
3615
3566
|
it('handles PasswordError returned by fetchMeetingInfo', async () => {
|
|
@@ -3696,11 +3647,11 @@ describe('plugin-meetings', () => {
|
|
|
3696
3647
|
.stub()
|
|
3697
3648
|
.returns(Promise.resolve({body: 'test'}));
|
|
3698
3649
|
meeting.locusInfo.onFullLocus = sinon.stub().returns(true);
|
|
3699
|
-
meeting.
|
|
3650
|
+
meeting.cleanupLocalStreams = sinon.stub().returns(Promise.resolve());
|
|
3700
3651
|
meeting.closeRemoteStream = sinon.stub().returns(Promise.resolve());
|
|
3701
|
-
sandbox.stub(meeting, '
|
|
3652
|
+
sandbox.stub(meeting, 'closeRemoteStreams').returns(Promise.resolve());
|
|
3702
3653
|
meeting.closePeerConnections = sinon.stub().returns(Promise.resolve());
|
|
3703
|
-
meeting.
|
|
3654
|
+
meeting.unsetRemoteStreams = sinon.stub();
|
|
3704
3655
|
meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
|
|
3705
3656
|
meeting.unsetPeerConnections = sinon.stub().returns(true);
|
|
3706
3657
|
meeting.logger.error = sinon.stub().returns(true);
|
|
@@ -3720,10 +3671,10 @@ describe('plugin-meetings', () => {
|
|
|
3720
3671
|
assert.exists(endMeetingForAll.then);
|
|
3721
3672
|
await endMeetingForAll;
|
|
3722
3673
|
assert.calledOnce(meeting?.meetingRequest?.endMeetingForAll);
|
|
3723
|
-
assert.calledOnce(meeting?.
|
|
3724
|
-
assert.calledOnce(meeting?.
|
|
3674
|
+
assert.calledOnce(meeting?.cleanupLocalStreams);
|
|
3675
|
+
assert.calledOnce(meeting?.closeRemoteStreams);
|
|
3725
3676
|
assert.calledOnce(meeting?.closePeerConnections);
|
|
3726
|
-
assert.calledOnce(meeting?.
|
|
3677
|
+
assert.calledOnce(meeting?.unsetRemoteStreams);
|
|
3727
3678
|
assert.calledOnce(meeting?.unsetPeerConnections);
|
|
3728
3679
|
});
|
|
3729
3680
|
});
|
|
@@ -3733,7 +3684,7 @@ describe('plugin-meetings', () => {
|
|
|
3733
3684
|
|
|
3734
3685
|
beforeEach(() => {
|
|
3735
3686
|
sandbox = sinon.createSandbox();
|
|
3736
|
-
sandbox.stub(meeting, '
|
|
3687
|
+
sandbox.stub(meeting, 'cleanupLocalStreams');
|
|
3737
3688
|
|
|
3738
3689
|
sandbox.stub(meeting.mediaProperties, 'setMediaDirection');
|
|
3739
3690
|
|
|
@@ -3812,7 +3763,7 @@ describe('plugin-meetings', () => {
|
|
|
3812
3763
|
|
|
3813
3764
|
// beacuse we are calling callback so we need to wait
|
|
3814
3765
|
|
|
3815
|
-
assert.called(meeting.
|
|
3766
|
+
assert.called(meeting.cleanupLocalStreams);
|
|
3816
3767
|
|
|
3817
3768
|
// give queued Promise callbacks a chance to run
|
|
3818
3769
|
await Promise.resolve();
|
|
@@ -3936,43 +3887,45 @@ describe('plugin-meetings', () => {
|
|
|
3936
3887
|
});
|
|
3937
3888
|
});
|
|
3938
3889
|
describe('Local tracks publishing', () => {
|
|
3939
|
-
let
|
|
3940
|
-
let
|
|
3941
|
-
let
|
|
3942
|
-
let
|
|
3943
|
-
let
|
|
3944
|
-
let LocalDisplayTrackConstructorStub;
|
|
3945
|
-
let LocalMicrophoneTrackConstructorStub;
|
|
3946
|
-
let LocalCameraTrackConstructorStub;
|
|
3947
|
-
let fakeLocalDisplayTrack;
|
|
3948
|
-
let fakeLocalMicrophoneTrack;
|
|
3949
|
-
let fakeLocalCameraTrack;
|
|
3890
|
+
let audioStream;
|
|
3891
|
+
let videoStream;
|
|
3892
|
+
let audioShareStream;
|
|
3893
|
+
let videoShareStream;
|
|
3894
|
+
let fakeMultistreamRoapMediaConnection;
|
|
3950
3895
|
|
|
3951
3896
|
beforeEach(() => {
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3897
|
+
audioStream = {
|
|
3898
|
+
getSettings: sinon.stub().returns({
|
|
3899
|
+
deviceId: 'some device id'
|
|
3900
|
+
}),
|
|
3955
3901
|
on: sinon.stub(),
|
|
3956
3902
|
off: sinon.stub(),
|
|
3957
|
-
}
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
getSettings: sinon.stub().returns({
|
|
3903
|
+
}
|
|
3904
|
+
|
|
3905
|
+
videoStream = {
|
|
3906
|
+
getSettings: sinon.stub().returns({
|
|
3907
|
+
deviceId: 'some device id'
|
|
3908
|
+
}),
|
|
3961
3909
|
on: sinon.stub(),
|
|
3962
3910
|
off: sinon.stub(),
|
|
3963
|
-
}
|
|
3964
|
-
|
|
3965
|
-
|
|
3911
|
+
}
|
|
3912
|
+
|
|
3913
|
+
audioShareStream = {
|
|
3966
3914
|
on: sinon.stub(),
|
|
3967
3915
|
off: sinon.stub(),
|
|
3968
|
-
getSettings: sinon.stub()
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3916
|
+
getSettings: sinon.stub().returns({
|
|
3917
|
+
deviceId: 'some device id'
|
|
3918
|
+
}),
|
|
3919
|
+
}
|
|
3920
|
+
|
|
3921
|
+
videoShareStream = {
|
|
3972
3922
|
on: sinon.stub(),
|
|
3973
3923
|
off: sinon.stub(),
|
|
3974
|
-
getSettings: sinon.stub().returns({
|
|
3975
|
-
|
|
3924
|
+
getSettings: sinon.stub().returns({
|
|
3925
|
+
deviceId: 'some device id'
|
|
3926
|
+
}),
|
|
3927
|
+
}
|
|
3928
|
+
|
|
3976
3929
|
meeting.requestScreenShareFloor = sinon.stub().resolves({});
|
|
3977
3930
|
meeting.releaseScreenShareFloor = sinon.stub().resolves({});
|
|
3978
3931
|
meeting.mediaProperties.mediaDirection = {
|
|
@@ -3981,266 +3934,239 @@ describe('plugin-meetings', () => {
|
|
|
3981
3934
|
sendShare: false,
|
|
3982
3935
|
};
|
|
3983
3936
|
meeting.isMultistream = true;
|
|
3984
|
-
meeting.mediaProperties.webrtcMediaConnection = {
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
// that they return the original track correctly (this is needed for unpublish() API tests)
|
|
4000
|
-
LocalDisplayTrackConstructorStub = sinon
|
|
4001
|
-
.stub(InternalMediaCoreModule, 'LocalDisplayTrack')
|
|
4002
|
-
.callsFake((stream) => {
|
|
4003
|
-
fakeLocalDisplayTrack = createFakeLocalTrack(stream.getTracks()[0]);
|
|
4004
|
-
return fakeLocalDisplayTrack;
|
|
4005
|
-
});
|
|
4006
|
-
LocalMicrophoneTrackConstructorStub = sinon
|
|
4007
|
-
.stub(InternalMediaCoreModule, 'LocalMicrophoneTrack')
|
|
4008
|
-
.callsFake((stream) => {
|
|
4009
|
-
fakeLocalMicrophoneTrack = createFakeLocalTrack(stream.getTracks()[0]);
|
|
4010
|
-
return fakeLocalMicrophoneTrack;
|
|
4011
|
-
});
|
|
4012
|
-
LocalCameraTrackConstructorStub = sinon
|
|
4013
|
-
.stub(InternalMediaCoreModule, 'LocalCameraTrack')
|
|
4014
|
-
.callsFake((stream) => {
|
|
4015
|
-
fakeLocalCameraTrack = createFakeLocalTrack(stream.getTracks()[0]);
|
|
4016
|
-
return fakeLocalCameraTrack;
|
|
4017
|
-
});
|
|
4018
|
-
|
|
4019
|
-
createMuteStateStub = sinon
|
|
4020
|
-
.stub(MuteStateModule, 'createMuteState')
|
|
4021
|
-
.returns({id: 'fake mute state instance'});
|
|
3937
|
+
meeting.mediaProperties.webrtcMediaConnection = {};
|
|
3938
|
+
meeting.audio = { handleLocalStreamChange: sinon.stub()};
|
|
3939
|
+
meeting.video = { handleLocalStreamChange: sinon.stub()};
|
|
3940
|
+
fakeMultistreamRoapMediaConnection = {
|
|
3941
|
+
createSendSlot: () => {
|
|
3942
|
+
return {
|
|
3943
|
+
publishStream: sinon.stub(),
|
|
3944
|
+
unpublishStream: sinon.stub(),
|
|
3945
|
+
}
|
|
3946
|
+
}
|
|
3947
|
+
}
|
|
3948
|
+
meeting.sendSlotManager.createSlot(fakeMultistreamRoapMediaConnection, MediaType.VideoSlides);
|
|
3949
|
+
meeting.sendSlotManager.createSlot(fakeMultistreamRoapMediaConnection, MediaType.AudioSlides);
|
|
3950
|
+
meeting.sendSlotManager.createSlot(fakeMultistreamRoapMediaConnection, MediaType.AudioMain);
|
|
3951
|
+
meeting.sendSlotManager.createSlot(fakeMultistreamRoapMediaConnection, MediaType.VideoMain);
|
|
4022
3952
|
});
|
|
4023
|
-
|
|
3953
|
+
afterEach(() => {
|
|
3954
|
+
sinon.restore();
|
|
3955
|
+
});
|
|
3956
|
+
describe('#publishStreams', () => {
|
|
4024
3957
|
it('fails if there is no media connection', async () => {
|
|
4025
3958
|
meeting.mediaProperties.webrtcMediaConnection = undefined;
|
|
4026
|
-
await assert.isRejected(meeting.
|
|
3959
|
+
await assert.isRejected(meeting.publishStreams({audio: {id: 'some audio track'}}));
|
|
4027
3960
|
});
|
|
4028
3961
|
|
|
4029
|
-
const checkAudioPublished = (
|
|
4030
|
-
assert.calledOnceWithExactly(meeting.audio.
|
|
4031
|
-
assert.calledWith(
|
|
4032
|
-
|
|
3962
|
+
const checkAudioPublished = (stream) => {
|
|
3963
|
+
assert.calledOnceWithExactly(meeting.audio.handleLocalStreamChange, meeting);
|
|
3964
|
+
assert.calledWith(
|
|
3965
|
+
meeting.sendSlotManager.getSlot(MediaType.AudioMain).publishStream,
|
|
3966
|
+
stream
|
|
3967
|
+
);
|
|
3968
|
+
assert.equal(meeting.mediaProperties.audioStream, stream);
|
|
4033
3969
|
// check that sendAudio hasn't been touched
|
|
4034
3970
|
assert.equal(meeting.mediaProperties.mediaDirection.sendAudio, 'fake value');
|
|
4035
3971
|
};
|
|
4036
3972
|
|
|
4037
|
-
const checkVideoPublished = (
|
|
4038
|
-
assert.calledOnceWithExactly(meeting.video.
|
|
4039
|
-
assert.calledWith(
|
|
4040
|
-
|
|
3973
|
+
const checkVideoPublished = (stream) => {
|
|
3974
|
+
assert.calledOnceWithExactly(meeting.video.handleLocalStreamChange, meeting);
|
|
3975
|
+
assert.calledWith(
|
|
3976
|
+
meeting.sendSlotManager.getSlot(MediaType.VideoMain).publishStream,
|
|
3977
|
+
stream
|
|
3978
|
+
);
|
|
3979
|
+
assert.equal(meeting.mediaProperties.videoStream, stream);
|
|
4041
3980
|
// check that sendVideo hasn't been touched
|
|
4042
3981
|
assert.equal(meeting.mediaProperties.mediaDirection.sendVideo, 'fake value');
|
|
4043
3982
|
};
|
|
4044
3983
|
|
|
4045
|
-
const checkScreenShareVideoPublished = (
|
|
3984
|
+
const checkScreenShareVideoPublished = (stream) => {
|
|
4046
3985
|
assert.calledOnce(meeting.requestScreenShareFloor);
|
|
4047
3986
|
|
|
4048
|
-
assert.calledWith(
|
|
4049
|
-
|
|
3987
|
+
assert.calledWith(
|
|
3988
|
+
meeting.sendSlotManager.getSlot(MediaType.VideoSlides).publishStream,
|
|
3989
|
+
stream
|
|
3990
|
+
);
|
|
3991
|
+
assert.equal(meeting.mediaProperties.shareVideoStream, stream);
|
|
4050
3992
|
assert.equal(meeting.mediaProperties.mediaDirection.sendShare, true);
|
|
4051
3993
|
};
|
|
4052
3994
|
|
|
4053
|
-
const checkScreenShareAudioPublished = (
|
|
3995
|
+
const checkScreenShareAudioPublished = (stream) => {
|
|
4054
3996
|
assert.calledOnce(meeting.requestScreenShareFloor);
|
|
4055
3997
|
|
|
4056
|
-
assert.calledWith(
|
|
4057
|
-
|
|
3998
|
+
assert.calledWith(
|
|
3999
|
+
meeting.sendSlotManager.getSlot(MediaType.AudioSlides).publishStream,
|
|
4000
|
+
stream
|
|
4001
|
+
);
|
|
4002
|
+
assert.equal(meeting.mediaProperties.shareAudioStream, stream);
|
|
4058
4003
|
assert.equal(meeting.mediaProperties.mediaDirection.sendShare, true);
|
|
4059
4004
|
};
|
|
4060
4005
|
|
|
4061
|
-
it('requests screen share floor and publishes the screen share video
|
|
4062
|
-
await meeting.
|
|
4006
|
+
it('requests screen share floor and publishes the screen share video stream', async () => {
|
|
4007
|
+
await meeting.publishStreams({screenShare: {video: videoShareStream}});
|
|
4063
4008
|
|
|
4064
|
-
|
|
4065
|
-
checkScreenShareVideoPublished(videoShareTrack);
|
|
4009
|
+
checkScreenShareVideoPublished(videoShareStream);
|
|
4066
4010
|
});
|
|
4067
4011
|
|
|
4068
|
-
it('requests screen share floor and publishes the screen share audio
|
|
4069
|
-
await meeting.
|
|
4012
|
+
it('requests screen share floor and publishes the screen share audio stream', async () => {
|
|
4013
|
+
await meeting.publishStreams({screenShare: {audio: audioShareStream}});
|
|
4070
4014
|
|
|
4071
|
-
|
|
4072
|
-
checkScreenShareAudioPublished(audioShareTrack);
|
|
4015
|
+
checkScreenShareAudioPublished(audioShareStream);
|
|
4073
4016
|
});
|
|
4074
4017
|
|
|
4075
|
-
it('does not request screen share floor when publishing video share
|
|
4076
|
-
await meeting.
|
|
4018
|
+
it('does not request screen share floor when publishing video share stream if already sharing audio', async () => {
|
|
4019
|
+
await meeting.publishStreams({screenShare: {audio: audioShareStream}});
|
|
4077
4020
|
assert.calledOnce(meeting.requestScreenShareFloor);
|
|
4078
4021
|
|
|
4079
4022
|
meeting.requestScreenShareFloor.reset();
|
|
4080
|
-
await meeting.
|
|
4023
|
+
await meeting.publishStreams({screenShare: {video: videoShareStream}});
|
|
4081
4024
|
assert.notCalled(meeting.requestScreenShareFloor);
|
|
4082
4025
|
});
|
|
4083
4026
|
|
|
4084
|
-
it('does not request screen share floor when publishing audio share
|
|
4085
|
-
await meeting.
|
|
4027
|
+
it('does not request screen share floor when publishing audio share stream if already sharing video', async () => {
|
|
4028
|
+
await meeting.publishStreams({screenShare: {video: videoShareStream}});
|
|
4086
4029
|
assert.calledOnce(meeting.requestScreenShareFloor);
|
|
4087
4030
|
|
|
4088
4031
|
meeting.requestScreenShareFloor.reset();
|
|
4089
|
-
await meeting.
|
|
4032
|
+
await meeting.publishStreams({screenShare: {audio: audioShareStream}});
|
|
4090
4033
|
assert.notCalled(meeting.requestScreenShareFloor);
|
|
4091
4034
|
});
|
|
4092
4035
|
|
|
4093
|
-
it('updates MuteState instance and publishes the
|
|
4094
|
-
await meeting.
|
|
4036
|
+
it('updates MuteState instance and publishes the stream for main audio', async () => {
|
|
4037
|
+
await meeting.publishStreams({microphone: audioStream});
|
|
4095
4038
|
|
|
4096
|
-
|
|
4097
|
-
checkAudioPublished(audioTrack);
|
|
4039
|
+
checkAudioPublished(audioStream);
|
|
4098
4040
|
});
|
|
4099
4041
|
|
|
4100
|
-
it('updates MuteState instance and publishes the
|
|
4101
|
-
await meeting.
|
|
4042
|
+
it('updates MuteState instance and publishes the stream for main video', async () => {
|
|
4043
|
+
await meeting.publishStreams({camera: videoStream});
|
|
4102
4044
|
|
|
4103
|
-
|
|
4104
|
-
checkVideoPublished(videoTrack);
|
|
4045
|
+
checkVideoPublished(videoStream);
|
|
4105
4046
|
});
|
|
4106
4047
|
|
|
4107
4048
|
it('publishes audio, video and screen share together', async () => {
|
|
4108
|
-
await meeting.
|
|
4109
|
-
microphone:
|
|
4110
|
-
camera:
|
|
4049
|
+
await meeting.publishStreams({
|
|
4050
|
+
microphone: audioStream,
|
|
4051
|
+
camera: videoStream,
|
|
4111
4052
|
screenShare: {
|
|
4112
|
-
video:
|
|
4113
|
-
audio:
|
|
4053
|
+
video: videoShareStream,
|
|
4054
|
+
audio: audioShareStream,
|
|
4114
4055
|
},
|
|
4115
4056
|
});
|
|
4116
4057
|
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
checkScreenShareAudioPublished(audioShareTrack);
|
|
4058
|
+
checkAudioPublished(audioStream);
|
|
4059
|
+
checkVideoPublished(videoStream);
|
|
4060
|
+
checkScreenShareVideoPublished(videoShareStream);
|
|
4061
|
+
checkScreenShareAudioPublished(audioShareStream);
|
|
4122
4062
|
});
|
|
4123
4063
|
});
|
|
4124
4064
|
|
|
4125
|
-
describe('
|
|
4065
|
+
describe('unpublishStreams', () => {
|
|
4126
4066
|
beforeEach(async () => {
|
|
4127
|
-
await meeting.
|
|
4128
|
-
microphone:
|
|
4129
|
-
camera:
|
|
4130
|
-
screenShare: {video:
|
|
4067
|
+
await meeting.publishStreams({
|
|
4068
|
+
microphone: audioStream,
|
|
4069
|
+
camera: videoStream,
|
|
4070
|
+
screenShare: {video: videoShareStream, audio: audioShareStream},
|
|
4131
4071
|
});
|
|
4132
4072
|
});
|
|
4133
4073
|
|
|
4134
4074
|
const checkAudioUnpublished = () => {
|
|
4135
|
-
assert.
|
|
4136
|
-
meeting.
|
|
4137
|
-
audioTrack
|
|
4075
|
+
assert.calledOnce(
|
|
4076
|
+
meeting.sendSlotManager.getSlot(MediaType.AudioMain).unpublishStream
|
|
4138
4077
|
);
|
|
4139
4078
|
|
|
4140
|
-
assert.equal(meeting.mediaProperties.
|
|
4079
|
+
assert.equal(meeting.mediaProperties.audioStream, null);
|
|
4141
4080
|
assert.equal(meeting.mediaProperties.mediaDirection.sendAudio, 'fake value');
|
|
4142
4081
|
};
|
|
4143
4082
|
|
|
4144
4083
|
const checkVideoUnpublished = () => {
|
|
4145
|
-
assert.
|
|
4146
|
-
meeting.
|
|
4147
|
-
videoTrack
|
|
4084
|
+
assert.calledOnce(
|
|
4085
|
+
meeting.sendSlotManager.getSlot(MediaType.VideoMain).unpublishStream
|
|
4148
4086
|
);
|
|
4149
4087
|
|
|
4150
|
-
assert.equal(meeting.mediaProperties.
|
|
4088
|
+
assert.equal(meeting.mediaProperties.videoStream, null);
|
|
4151
4089
|
assert.equal(meeting.mediaProperties.mediaDirection.sendVideo, 'fake value');
|
|
4152
4090
|
};
|
|
4153
4091
|
|
|
4154
|
-
// share direction will remain true if only one of the two share
|
|
4092
|
+
// share direction will remain true if only one of the two share streams are unpublished
|
|
4155
4093
|
const checkScreenShareVideoUnpublished = (shareDirection = true) => {
|
|
4156
|
-
assert.
|
|
4157
|
-
meeting.
|
|
4158
|
-
videoShareTrack
|
|
4094
|
+
assert.calledOnce(
|
|
4095
|
+
meeting.sendSlotManager.getSlot(MediaType.VideoSlides).unpublishStream
|
|
4159
4096
|
);
|
|
4160
4097
|
|
|
4161
4098
|
assert.calledOnce(meeting.requestScreenShareFloor);
|
|
4162
4099
|
|
|
4163
|
-
assert.equal(meeting.mediaProperties.
|
|
4100
|
+
assert.equal(meeting.mediaProperties.shareVideoStream, null);
|
|
4164
4101
|
assert.equal(meeting.mediaProperties.mediaDirection.sendShare, shareDirection);
|
|
4165
4102
|
};
|
|
4166
4103
|
|
|
4167
|
-
// share direction will remain true if only one of the two share
|
|
4104
|
+
// share direction will remain true if only one of the two share streams are unpublished
|
|
4168
4105
|
const checkScreenShareAudioUnpublished = (shareDirection = true) => {
|
|
4169
|
-
assert.
|
|
4170
|
-
meeting.
|
|
4171
|
-
audioShareTrack
|
|
4106
|
+
assert.calledOnce(
|
|
4107
|
+
meeting.sendSlotManager.getSlot(MediaType.AudioSlides).unpublishStream
|
|
4172
4108
|
);
|
|
4173
4109
|
|
|
4174
4110
|
assert.calledOnce(meeting.requestScreenShareFloor);
|
|
4175
4111
|
|
|
4176
|
-
assert.equal(meeting.mediaProperties.
|
|
4112
|
+
assert.equal(meeting.mediaProperties.shareAudioStream, null);
|
|
4177
4113
|
assert.equal(meeting.mediaProperties.mediaDirection.sendShare, shareDirection);
|
|
4178
4114
|
};
|
|
4179
4115
|
|
|
4180
4116
|
it('fails if there is no media connection', async () => {
|
|
4181
4117
|
meeting.mediaProperties.webrtcMediaConnection = undefined;
|
|
4182
4118
|
await assert.isRejected(
|
|
4183
|
-
meeting.
|
|
4119
|
+
meeting.unpublishStreams([audioStream, videoStream, videoShareStream, audioShareStream])
|
|
4184
4120
|
);
|
|
4185
4121
|
});
|
|
4186
4122
|
|
|
4187
|
-
it('un-publishes the
|
|
4188
|
-
await meeting.
|
|
4189
|
-
audioTrack,
|
|
4190
|
-
videoTrack,
|
|
4191
|
-
videoShareTrack,
|
|
4192
|
-
audioShareTrack,
|
|
4193
|
-
]);
|
|
4123
|
+
it('un-publishes the streams correctly (all 4 together)', async () => {
|
|
4124
|
+
await meeting.unpublishStreams([audioStream, videoStream, videoShareStream, audioShareStream]);
|
|
4194
4125
|
|
|
4195
|
-
assert.equal(meeting.mediaProperties.webrtcMediaConnection.unpublishTrack.callCount, 4);
|
|
4196
4126
|
checkAudioUnpublished();
|
|
4197
4127
|
checkVideoUnpublished();
|
|
4198
4128
|
checkScreenShareVideoUnpublished(false);
|
|
4199
4129
|
checkScreenShareAudioUnpublished(false);
|
|
4200
4130
|
});
|
|
4201
4131
|
|
|
4202
|
-
it('un-publishes the audio
|
|
4203
|
-
await meeting.
|
|
4132
|
+
it('un-publishes the audio stream correctly', async () => {
|
|
4133
|
+
await meeting.unpublishStreams([audioStream]);
|
|
4204
4134
|
|
|
4205
|
-
assert.calledOnce(meeting.mediaProperties.webrtcMediaConnection.unpublishTrack);
|
|
4206
4135
|
checkAudioUnpublished();
|
|
4207
4136
|
});
|
|
4208
4137
|
|
|
4209
|
-
it('un-publishes the video
|
|
4210
|
-
await meeting.
|
|
4138
|
+
it('un-publishes the video stream correctly', async () => {
|
|
4139
|
+
await meeting.unpublishStreams([videoStream]);
|
|
4211
4140
|
|
|
4212
|
-
assert.calledOnce(meeting.mediaProperties.webrtcMediaConnection.unpublishTrack);
|
|
4213
4141
|
checkVideoUnpublished();
|
|
4214
4142
|
});
|
|
4215
4143
|
|
|
4216
|
-
it('un-publishes the screen share video
|
|
4217
|
-
await meeting.
|
|
4144
|
+
it('un-publishes the screen share video stream correctly', async () => {
|
|
4145
|
+
await meeting.unpublishStreams([videoShareStream]);
|
|
4218
4146
|
|
|
4219
|
-
assert.calledOnce(meeting.mediaProperties.webrtcMediaConnection.unpublishTrack);
|
|
4220
4147
|
checkScreenShareVideoUnpublished();
|
|
4221
4148
|
});
|
|
4222
4149
|
|
|
4223
|
-
it('un-publishes the screen share audio
|
|
4224
|
-
await meeting.
|
|
4150
|
+
it('un-publishes the screen share audio stream correctly', async () => {
|
|
4151
|
+
await meeting.unpublishStreams([audioShareStream]);
|
|
4225
4152
|
|
|
4226
|
-
assert.calledOnce(meeting.mediaProperties.webrtcMediaConnection.unpublishTrack);
|
|
4227
4153
|
checkScreenShareAudioUnpublished();
|
|
4228
4154
|
});
|
|
4229
4155
|
|
|
4230
|
-
it('releases share floor and sets send direction to false when both screen share
|
|
4231
|
-
await meeting.
|
|
4156
|
+
it('releases share floor and sets send direction to false when both screen share streams are undefined', async () => {
|
|
4157
|
+
await meeting.unpublishStreams([videoShareStream, audioShareStream]);
|
|
4232
4158
|
|
|
4233
4159
|
assert.calledOnce(meeting.releaseScreenShareFloor);
|
|
4234
4160
|
assert.equal(meeting.mediaProperties.mediaDirection.sendShare, false);
|
|
4235
4161
|
});
|
|
4236
4162
|
|
|
4237
4163
|
it('does not release share floor when audio is released and video still exists', async () => {
|
|
4238
|
-
await meeting.
|
|
4164
|
+
await meeting.unpublishStreams([audioShareStream]);
|
|
4239
4165
|
assert.notCalled(meeting.releaseScreenShareFloor);
|
|
4240
4166
|
});
|
|
4241
4167
|
|
|
4242
4168
|
it('does not release share floor when video is released and audio still exists', async () => {
|
|
4243
|
-
await meeting.
|
|
4169
|
+
await meeting.unpublishStreams([videoShareStream]);
|
|
4244
4170
|
assert.notCalled(meeting.releaseScreenShareFloor);
|
|
4245
4171
|
});
|
|
4246
4172
|
});
|
|
@@ -4250,60 +4176,68 @@ describe('plugin-meetings', () => {
|
|
|
4250
4176
|
describe('#enableMusicMode', () => {
|
|
4251
4177
|
beforeEach(() => {
|
|
4252
4178
|
meeting.isMultistream = true;
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4179
|
+
const fakeMultistreamRoapMediaConnection = {
|
|
4180
|
+
createSendSlot: () => {
|
|
4181
|
+
return {
|
|
4182
|
+
setCodecParameters: sinon.stub().resolves(),
|
|
4183
|
+
deleteCodecParameters: sinon.stub().resolves(),
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4256
4186
|
};
|
|
4187
|
+
meeting.sendSlotManager.createSlot(fakeMultistreamRoapMediaConnection, MediaType.AudioMain, false);
|
|
4188
|
+
meeting.mediaProperties.webrtcMediaConnection = {};
|
|
4189
|
+
});
|
|
4190
|
+
afterEach(() => {
|
|
4191
|
+
sinon.restore();
|
|
4192
|
+
});
|
|
4193
|
+
[
|
|
4194
|
+
{shouldEnableMusicMode: true},
|
|
4195
|
+
{shouldEnableMusicMode: false},
|
|
4196
|
+
].forEach(({shouldEnableMusicMode}) => {
|
|
4197
|
+
it(`fails if there is no media connection for shouldEnableMusicMode: ${shouldEnableMusicMode}`, async () => {
|
|
4198
|
+
meeting.mediaProperties.webrtcMediaConnection = undefined;
|
|
4199
|
+
await assert.isRejected(meeting.enableMusicMode(shouldEnableMusicMode));
|
|
4200
|
+
});
|
|
4257
4201
|
});
|
|
4258
|
-
[{shouldEnableMusicMode: true}, {shouldEnableMusicMode: false}].forEach(
|
|
4259
|
-
({shouldEnableMusicMode}) => {
|
|
4260
|
-
it(`fails if there is no media connection for shouldEnableMusicMode: ${shouldEnableMusicMode}`, async () => {
|
|
4261
|
-
meeting.mediaProperties.webrtcMediaConnection = undefined;
|
|
4262
|
-
await assert.isRejected(meeting.enableMusicMode(shouldEnableMusicMode));
|
|
4263
|
-
});
|
|
4264
|
-
}
|
|
4265
|
-
);
|
|
4266
4202
|
|
|
4267
4203
|
it('should set the codec parameters when shouldEnableMusicMode is true', async () => {
|
|
4268
4204
|
await meeting.enableMusicMode(true);
|
|
4269
|
-
assert.calledOnceWithExactly(
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
maxplaybackrate: '48000',
|
|
4275
|
-
}
|
|
4276
|
-
);
|
|
4277
|
-
assert.notCalled(meeting.mediaProperties.webrtcMediaConnection.deleteCodecParameters);
|
|
4205
|
+
assert.calledOnceWithExactly(meeting.sendSlotManager.getSlot(MediaType.AudioMain).setCodecParameters, {
|
|
4206
|
+
maxaveragebitrate: '64000',
|
|
4207
|
+
maxplaybackrate: '48000',
|
|
4208
|
+
});
|
|
4209
|
+
assert.notCalled(meeting.sendSlotManager.getSlot(MediaType.AudioMain).deleteCodecParameters);
|
|
4278
4210
|
});
|
|
4279
4211
|
|
|
4280
4212
|
it('should set the codec parameters when shouldEnableMusicMode is false', async () => {
|
|
4281
4213
|
await meeting.enableMusicMode(false);
|
|
4282
|
-
assert.calledOnceWithExactly(
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
);
|
|
4287
|
-
assert.notCalled(meeting.mediaProperties.webrtcMediaConnection.setCodecParameters);
|
|
4214
|
+
assert.calledOnceWithExactly(meeting.sendSlotManager.getSlot(MediaType.AudioMain).deleteCodecParameters, [
|
|
4215
|
+
'maxaveragebitrate',
|
|
4216
|
+
'maxplaybackrate',
|
|
4217
|
+
]);
|
|
4218
|
+
assert.notCalled(meeting.sendSlotManager.getSlot(MediaType.AudioMain).setCodecParameters);
|
|
4288
4219
|
});
|
|
4289
4220
|
});
|
|
4290
4221
|
|
|
4291
4222
|
describe('Public Event Triggers', () => {
|
|
4292
4223
|
let sandbox;
|
|
4293
|
-
const {ENDED} = CONSTANTS;
|
|
4294
4224
|
|
|
4295
4225
|
beforeEach(() => {
|
|
4296
|
-
const
|
|
4226
|
+
const fakeMediaStream = () => {
|
|
4227
|
+
return {
|
|
4228
|
+
id: 'fake stream'
|
|
4229
|
+
}
|
|
4230
|
+
};
|
|
4297
4231
|
|
|
4298
4232
|
sandbox = sinon.createSandbox();
|
|
4299
|
-
sandbox.stub(Media, '
|
|
4300
|
-
sandbox.stub(meeting.mediaProperties, '
|
|
4301
|
-
sandbox.stub(meeting.mediaProperties, '
|
|
4302
|
-
sandbox.stub(meeting.mediaProperties, '
|
|
4303
|
-
sandbox.stub(meeting.mediaProperties, '
|
|
4304
|
-
sandbox.stub(meeting.mediaProperties, '
|
|
4305
|
-
sandbox.stub(meeting.mediaProperties, '
|
|
4306
|
-
sandbox.stub(meeting.mediaProperties, '
|
|
4233
|
+
sandbox.stub(Media, 'stopStream').returns(Promise.resolve());
|
|
4234
|
+
sandbox.stub(meeting.mediaProperties, 'audioStream').value(fakeMediaStream());
|
|
4235
|
+
sandbox.stub(meeting.mediaProperties, 'videoStream').value(fakeMediaStream());
|
|
4236
|
+
sandbox.stub(meeting.mediaProperties, 'shareVideoStream').value(fakeMediaStream());
|
|
4237
|
+
sandbox.stub(meeting.mediaProperties, 'shareAudioStream').value(fakeMediaStream());
|
|
4238
|
+
sandbox.stub(meeting.mediaProperties, 'remoteAudioStream').value(fakeMediaStream());
|
|
4239
|
+
sandbox.stub(meeting.mediaProperties, 'remoteVideoStream').value(fakeMediaStream());
|
|
4240
|
+
sandbox.stub(meeting.mediaProperties, 'remoteShareStream').value(fakeMediaStream());
|
|
4307
4241
|
});
|
|
4308
4242
|
afterEach(() => {
|
|
4309
4243
|
sandbox.restore();
|
|
@@ -4403,27 +4337,27 @@ describe('plugin-meetings', () => {
|
|
|
4403
4337
|
});
|
|
4404
4338
|
describe('#closeRemoteStream', () => {
|
|
4405
4339
|
it('should stop remote tracks, and trigger a media:stopped event when the remote tracks are stopped', async () => {
|
|
4406
|
-
await meeting.
|
|
4340
|
+
await meeting.closeRemoteStreams();
|
|
4407
4341
|
|
|
4408
|
-
assert.equal(TriggerProxy.trigger.callCount,
|
|
4342
|
+
assert.equal(TriggerProxy.trigger.callCount, 6);
|
|
4409
4343
|
assert.calledWith(
|
|
4410
4344
|
TriggerProxy.trigger,
|
|
4411
4345
|
sinon.match.instanceOf(Meeting),
|
|
4412
|
-
{file: 'meeting/index', function: '
|
|
4346
|
+
{file: 'meeting/index', function: 'closeRemoteStreams'},
|
|
4413
4347
|
'media:stopped',
|
|
4414
4348
|
{type: 'remoteAudio'}
|
|
4415
4349
|
);
|
|
4416
4350
|
assert.calledWith(
|
|
4417
4351
|
TriggerProxy.trigger,
|
|
4418
4352
|
sinon.match.instanceOf(Meeting),
|
|
4419
|
-
{file: 'meeting/index', function: '
|
|
4353
|
+
{file: 'meeting/index', function: 'closeRemoteStreams'},
|
|
4420
4354
|
'media:stopped',
|
|
4421
4355
|
{type: 'remoteVideo'}
|
|
4422
4356
|
);
|
|
4423
4357
|
assert.calledWith(
|
|
4424
4358
|
TriggerProxy.trigger,
|
|
4425
4359
|
sinon.match.instanceOf(Meeting),
|
|
4426
|
-
{file: 'meeting/index', function: '
|
|
4360
|
+
{file: 'meeting/index', function: 'closeRemoteStreams'},
|
|
4427
4361
|
'media:stopped',
|
|
4428
4362
|
{type: 'remoteShare'}
|
|
4429
4363
|
);
|
|
@@ -4431,6 +4365,10 @@ describe('plugin-meetings', () => {
|
|
|
4431
4365
|
});
|
|
4432
4366
|
describe('#setupMediaConnectionListeners', () => {
|
|
4433
4367
|
let eventListeners;
|
|
4368
|
+
const fakeStream = {
|
|
4369
|
+
id: 'stream',
|
|
4370
|
+
getTracks: () => [{ id: 'track', addEventListener: sinon.stub() }]
|
|
4371
|
+
};
|
|
4434
4372
|
|
|
4435
4373
|
beforeEach(() => {
|
|
4436
4374
|
eventListeners = {};
|
|
@@ -4441,7 +4379,7 @@ describe('plugin-meetings', () => {
|
|
|
4441
4379
|
eventListeners[event] = listener;
|
|
4442
4380
|
}),
|
|
4443
4381
|
};
|
|
4444
|
-
MediaUtil.createMediaStream.returns(
|
|
4382
|
+
MediaUtil.createMediaStream.returns(fakeStream);
|
|
4445
4383
|
});
|
|
4446
4384
|
|
|
4447
4385
|
it('should register for all the correct RoapMediaConnection events', () => {
|
|
@@ -4463,7 +4401,7 @@ describe('plugin-meetings', () => {
|
|
|
4463
4401
|
assert.equal(TriggerProxy.trigger.getCall(2).args[2], 'media:ready');
|
|
4464
4402
|
assert.deepEqual(TriggerProxy.trigger.getCall(2).args[3], {
|
|
4465
4403
|
type: 'remoteAudio',
|
|
4466
|
-
stream:
|
|
4404
|
+
stream: fakeStream,
|
|
4467
4405
|
});
|
|
4468
4406
|
|
|
4469
4407
|
eventListeners[Event.REMOTE_TRACK_ADDED]({
|
|
@@ -4473,7 +4411,7 @@ describe('plugin-meetings', () => {
|
|
|
4473
4411
|
assert.equal(TriggerProxy.trigger.getCall(3).args[2], 'media:ready');
|
|
4474
4412
|
assert.deepEqual(TriggerProxy.trigger.getCall(3).args[3], {
|
|
4475
4413
|
type: 'remoteVideo',
|
|
4476
|
-
stream:
|
|
4414
|
+
stream: fakeStream,
|
|
4477
4415
|
});
|
|
4478
4416
|
|
|
4479
4417
|
eventListeners[Event.REMOTE_TRACK_ADDED]({
|
|
@@ -4483,7 +4421,7 @@ describe('plugin-meetings', () => {
|
|
|
4483
4421
|
assert.equal(TriggerProxy.trigger.getCall(4).args[2], 'media:ready');
|
|
4484
4422
|
assert.deepEqual(TriggerProxy.trigger.getCall(4).args[3], {
|
|
4485
4423
|
type: 'remoteShare',
|
|
4486
|
-
stream:
|
|
4424
|
+
stream: fakeStream,
|
|
4487
4425
|
});
|
|
4488
4426
|
});
|
|
4489
4427
|
|
|
@@ -5514,7 +5452,7 @@ describe('plugin-meetings', () => {
|
|
|
5514
5452
|
|
|
5515
5453
|
beforeEach(() => {
|
|
5516
5454
|
sandbox = sinon.createSandbox();
|
|
5517
|
-
sandbox.stub(meeting.mediaProperties, '
|
|
5455
|
+
sandbox.stub(meeting.mediaProperties, 'unsetRemoteStreams').returns(Promise.resolve());
|
|
5518
5456
|
});
|
|
5519
5457
|
|
|
5520
5458
|
afterEach(() => {
|
|
@@ -5621,11 +5559,11 @@ describe('plugin-meetings', () => {
|
|
|
5621
5559
|
});
|
|
5622
5560
|
});
|
|
5623
5561
|
|
|
5624
|
-
describe('#
|
|
5625
|
-
it('should unset the remote
|
|
5626
|
-
meeting.mediaProperties.
|
|
5627
|
-
meeting.
|
|
5628
|
-
assert.calledOnce(meeting.mediaProperties.
|
|
5562
|
+
describe('#unsetRemoteStreams', () => {
|
|
5563
|
+
it('should unset the remote streams and return null', () => {
|
|
5564
|
+
meeting.mediaProperties.unsetRemoteStreams = sinon.stub().returns(true);
|
|
5565
|
+
meeting.unsetRemoteStreams();
|
|
5566
|
+
assert.calledOnce(meeting.mediaProperties.unsetRemoteStreams);
|
|
5629
5567
|
});
|
|
5630
5568
|
});
|
|
5631
5569
|
// TODO: remove
|