@webex/plugin-meetings 3.0.0 → 3.1.0-next.10
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/common/errors/{reconnection-in-progress.js → reconnection-not-started.js} +27 -15
- package/dist/common/errors/reconnection-not-started.js.map +1 -0
- package/dist/config.js +2 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js +18 -6
- package/dist/constants.js.map +1 -1
- package/dist/index.js +86 -0
- package/dist/index.js.map +1 -1
- package/dist/interpretation/index.js +16 -2
- package/dist/interpretation/index.js.map +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/controlsUtils.js +7 -1
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +10 -0
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/mediaSharesUtils.js +15 -1
- package/dist/locus-info/mediaSharesUtils.js.map +1 -1
- package/dist/locus-info/selfUtils.js +5 -0
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/MediaConnectionAwaiter.js +163 -0
- package/dist/media/MediaConnectionAwaiter.js.map +1 -0
- package/dist/media/index.js +4 -1
- package/dist/media/index.js.map +1 -1
- package/dist/media/properties.js +106 -81
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +6 -0
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +1010 -753
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/muteState.js +37 -25
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +32 -23
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/util.js +10 -0
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/util.js +304 -267
- package/dist/meeting-info/util.js.map +1 -1
- package/dist/meeting-info/utilv2.js +334 -295
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/index.js +21 -23
- package/dist/meetings/index.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +1 -1
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/remoteMediaGroup.js +16 -2
- package/dist/multistream/remoteMediaGroup.js.map +1 -1
- package/dist/multistream/remoteMediaManager.js +179 -65
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/multistream/sendSlotManager.js +22 -0
- package/dist/multistream/sendSlotManager.js.map +1 -1
- package/dist/reachability/clusterReachability.js +29 -15
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +18 -2
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/request.js +12 -10
- package/dist/reachability/request.js.map +1 -1
- package/dist/reachability/util.js +19 -0
- package/dist/reachability/util.js.map +1 -1
- package/dist/reconnection-manager/index.js +140 -110
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/roap/index.js +15 -0
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/request.js +3 -3
- package/dist/roap/request.js.map +1 -1
- package/dist/roap/turnDiscovery.js +307 -126
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/statsAnalyzer/index.js +57 -30
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/statsAnalyzer/mqaUtil.js +3 -0
- package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
- package/dist/types/common/errors/reconnection-not-started.d.ts +13 -0
- package/dist/{config.d.ts → types/config.d.ts} +1 -0
- package/dist/{constants.d.ts → types/constants.d.ts} +15 -6
- package/dist/types/index.d.ts +19 -0
- package/dist/types/media/MediaConnectionAwaiter.d.ts +61 -0
- package/dist/{media → types/media}/properties.d.ts +26 -2
- package/dist/{meeting → types/meeting}/in-meeting-actions.d.ts +6 -0
- package/dist/{meeting → types/meeting}/index.d.ts +29 -12
- package/dist/{meeting → types/meeting}/muteState.d.ts +2 -8
- package/dist/{meeting → types/meeting}/request.d.ts +3 -0
- package/dist/{meeting → types/meeting}/util.d.ts +3 -0
- package/dist/{meeting-info → types/meeting-info}/index.d.ts +1 -1
- package/dist/{meeting-info → types/meeting-info}/meeting-info-v2.d.ts +1 -1
- package/dist/types/meeting-info/util.d.ts +49 -0
- package/dist/types/meeting-info/utilv2.d.ts +65 -0
- package/dist/{meetings → types/meetings}/index.d.ts +9 -16
- package/dist/{multistream → types/multistream}/mediaRequestManager.d.ts +2 -1
- package/dist/{multistream → types/multistream}/remoteMediaGroup.d.ts +2 -0
- package/dist/{multistream → types/multistream}/remoteMediaManager.d.ts +15 -0
- package/dist/{multistream → types/multistream}/sendSlotManager.d.ts +9 -1
- package/dist/{reachability → types/reachability}/clusterReachability.d.ts +1 -0
- package/dist/{reachability → types/reachability}/index.d.ts +4 -0
- package/dist/{reachability → types/reachability}/util.d.ts +7 -0
- package/dist/{reconnection-manager → types/reconnection-manager}/index.d.ts +4 -14
- package/dist/{roap → types/roap}/index.d.ts +10 -2
- package/dist/{roap → types/roap}/turnDiscovery.d.ts +64 -17
- package/dist/webinar/index.js +1 -1
- package/package.json +23 -23
- package/src/common/errors/reconnection-not-started.ts +25 -0
- package/src/config.ts +1 -0
- package/src/constants.ts +18 -6
- package/src/index.ts +31 -0
- package/src/interpretation/index.ts +18 -1
- package/src/locus-info/controlsUtils.ts +11 -0
- package/src/locus-info/index.ts +16 -0
- package/src/locus-info/mediaSharesUtils.ts +16 -0
- package/src/locus-info/selfUtils.ts +5 -0
- package/src/media/MediaConnectionAwaiter.ts +174 -0
- package/src/media/index.ts +3 -1
- package/src/media/properties.ts +73 -46
- package/src/meeting/in-meeting-actions.ts +12 -0
- package/src/meeting/index.ts +389 -180
- package/src/meeting/muteState.ts +34 -20
- package/src/meeting/request.ts +18 -2
- package/src/meeting/util.ts +9 -0
- package/src/meeting-info/util.ts +241 -233
- package/src/meeting-info/utilv2.ts +250 -243
- package/src/meetings/index.ts +20 -24
- package/src/multistream/mediaRequestManager.ts +4 -1
- package/src/multistream/remoteMediaGroup.ts +19 -0
- package/src/multistream/remoteMediaManager.ts +101 -16
- package/src/multistream/sendSlotManager.ts +28 -0
- package/src/reachability/clusterReachability.ts +20 -5
- package/src/reachability/index.ts +24 -1
- package/src/reachability/request.ts +15 -11
- package/src/reachability/util.ts +21 -0
- package/src/reconnection-manager/index.ts +129 -106
- package/src/roap/index.ts +25 -3
- package/src/roap/request.ts +3 -3
- package/src/roap/turnDiscovery.ts +244 -78
- package/src/statsAnalyzer/index.ts +67 -27
- package/src/statsAnalyzer/mqaUtil.ts +5 -0
- package/test/integration/spec/journey.js +14 -14
- package/test/integration/spec/space-meeting.js +1 -1
- package/test/unit/spec/interpretation/index.ts +39 -3
- package/test/unit/spec/locus-info/controlsUtils.js +20 -0
- package/test/unit/spec/locus-info/index.js +49 -19
- package/test/unit/spec/locus-info/mediaSharesUtils.ts +9 -0
- package/test/unit/spec/locus-info/selfUtils.js +42 -12
- package/test/unit/spec/media/MediaConnectionAwaiter.ts +344 -0
- package/test/unit/spec/media/index.ts +89 -78
- package/test/unit/spec/media/properties.ts +160 -209
- package/test/unit/spec/meeting/in-meeting-actions.ts +6 -0
- package/test/unit/spec/meeting/index.js +833 -205
- package/test/unit/spec/meeting/muteState.js +219 -67
- package/test/unit/spec/meeting/request.js +21 -0
- package/test/unit/spec/meeting/utils.js +9 -1
- package/test/unit/spec/meeting-info/utilv2.js +6 -0
- package/test/unit/spec/meetings/index.js +41 -26
- package/test/unit/spec/multistream/mediaRequestManager.ts +20 -2
- package/test/unit/spec/multistream/remoteMediaGroup.ts +79 -1
- package/test/unit/spec/multistream/remoteMediaManager.ts +199 -1
- package/test/unit/spec/multistream/sendSlotManager.ts +50 -18
- package/test/unit/spec/reachability/clusterReachability.ts +86 -22
- package/test/unit/spec/reachability/index.ts +197 -60
- package/test/unit/spec/reachability/request.js +15 -7
- package/test/unit/spec/reachability/util.ts +32 -2
- package/test/unit/spec/reconnection-manager/index.js +155 -39
- package/test/unit/spec/roap/index.ts +61 -6
- package/test/unit/spec/roap/turnDiscovery.ts +298 -16
- package/test/unit/spec/stats-analyzer/index.js +190 -0
- package/dist/common/errors/reconnection-in-progress.d.ts +0 -9
- package/dist/common/errors/reconnection-in-progress.js.map +0 -1
- package/dist/index.d.ts +0 -7
- package/dist/meeting-info/util.d.ts +0 -2
- package/dist/meeting-info/utilv2.d.ts +0 -2
- package/dist/member/member.types.d.ts +0 -11
- package/dist/member/member.types.js +0 -17
- package/dist/member/member.types.js.map +0 -1
- package/src/common/errors/reconnection-in-progress.ts +0 -8
- package/src/member/member.types.ts +0 -13
- /package/dist/{annotation → types/annotation}/annotation.types.d.ts +0 -0
- /package/dist/{annotation → types/annotation}/constants.d.ts +0 -0
- /package/dist/{annotation → types/annotation}/index.d.ts +0 -0
- /package/dist/{breakouts → types/breakouts}/breakout.d.ts +0 -0
- /package/dist/{breakouts → types/breakouts}/collection.d.ts +0 -0
- /package/dist/{breakouts → types/breakouts}/edit-lock-error.d.ts +0 -0
- /package/dist/{breakouts → types/breakouts}/events.d.ts +0 -0
- /package/dist/{breakouts → types/breakouts}/index.d.ts +0 -0
- /package/dist/{breakouts → types/breakouts}/request.d.ts +0 -0
- /package/dist/{breakouts → types/breakouts}/utils.d.ts +0 -0
- /package/dist/{common → types/common}/browser-detection.d.ts +0 -0
- /package/dist/{common → types/common}/collection.d.ts +0 -0
- /package/dist/{common → types/common}/config.d.ts +0 -0
- /package/dist/{common → types/common}/errors/captcha-error.d.ts +0 -0
- /package/dist/{common → types/common}/errors/intent-to-join.d.ts +0 -0
- /package/dist/{common → types/common}/errors/join-meeting.d.ts +0 -0
- /package/dist/{common → types/common}/errors/media.d.ts +0 -0
- /package/dist/{common → types/common}/errors/no-meeting-info.d.ts +0 -0
- /package/dist/{common → types/common}/errors/parameter.d.ts +0 -0
- /package/dist/{common → types/common}/errors/password-error.d.ts +0 -0
- /package/dist/{common → types/common}/errors/permission.d.ts +0 -0
- /package/dist/{common → types/common}/errors/reclaim-host-role-errors.d.ts +0 -0
- /package/dist/{common → types/common}/errors/reconnection.d.ts +0 -0
- /package/dist/{common → types/common}/errors/stats.d.ts +0 -0
- /package/dist/{common → types/common}/errors/webex-errors.d.ts +0 -0
- /package/dist/{common → types/common}/errors/webex-meetings-error.d.ts +0 -0
- /package/dist/{common → types/common}/events/events-scope.d.ts +0 -0
- /package/dist/{common → types/common}/events/events.d.ts +0 -0
- /package/dist/{common → types/common}/events/trigger-proxy.d.ts +0 -0
- /package/dist/{common → types/common}/events/util.d.ts +0 -0
- /package/dist/{common → types/common}/logs/logger-config.d.ts +0 -0
- /package/dist/{common → types/common}/logs/logger-proxy.d.ts +0 -0
- /package/dist/{common → types/common}/logs/request.d.ts +0 -0
- /package/dist/{common → types/common}/queue.d.ts +0 -0
- /package/dist/{controls-options-manager → types/controls-options-manager}/constants.d.ts +0 -0
- /package/dist/{controls-options-manager → types/controls-options-manager}/enums.d.ts +0 -0
- /package/dist/{controls-options-manager → types/controls-options-manager}/index.d.ts +0 -0
- /package/dist/{controls-options-manager → types/controls-options-manager}/types.d.ts +0 -0
- /package/dist/{controls-options-manager → types/controls-options-manager}/util.d.ts +0 -0
- /package/dist/{interceptors → types/interceptors}/index.d.ts +0 -0
- /package/dist/{interceptors → types/interceptors}/locusRetry.d.ts +0 -0
- /package/dist/{interpretation → types/interpretation}/collection.d.ts +0 -0
- /package/dist/{interpretation → types/interpretation}/index.d.ts +0 -0
- /package/dist/{interpretation → types/interpretation}/siLanguage.d.ts +0 -0
- /package/dist/{locus-info → types/locus-info}/controlsUtils.d.ts +0 -0
- /package/dist/{locus-info → types/locus-info}/embeddedAppsUtils.d.ts +0 -0
- /package/dist/{locus-info → types/locus-info}/fullState.d.ts +0 -0
- /package/dist/{locus-info → types/locus-info}/hostUtils.d.ts +0 -0
- /package/dist/{locus-info → types/locus-info}/index.d.ts +0 -0
- /package/dist/{locus-info → types/locus-info}/infoUtils.d.ts +0 -0
- /package/dist/{locus-info → types/locus-info}/mediaSharesUtils.d.ts +0 -0
- /package/dist/{locus-info → types/locus-info}/parser.d.ts +0 -0
- /package/dist/{locus-info → types/locus-info}/selfUtils.d.ts +0 -0
- /package/dist/{media → types/media}/index.d.ts +0 -0
- /package/dist/{media → types/media}/util.d.ts +0 -0
- /package/dist/{mediaQualityMetrics → types/mediaQualityMetrics}/config.d.ts +0 -0
- /package/dist/{meeting → types/meeting}/locusMediaRequest.d.ts +0 -0
- /package/dist/{meeting → types/meeting}/request.type.d.ts +0 -0
- /package/dist/{meeting → types/meeting}/state.d.ts +0 -0
- /package/dist/{meeting → types/meeting}/voicea-meeting.d.ts +0 -0
- /package/dist/{meeting-info → types/meeting-info}/collection.d.ts +0 -0
- /package/dist/{meeting-info → types/meeting-info}/request.d.ts +0 -0
- /package/dist/{meetings → types/meetings}/collection.d.ts +0 -0
- /package/dist/{meetings → types/meetings}/meetings.types.d.ts +0 -0
- /package/dist/{meetings → types/meetings}/request.d.ts +0 -0
- /package/dist/{meetings → types/meetings}/util.d.ts +0 -0
- /package/dist/{member → types/member}/index.d.ts +0 -0
- /package/dist/{member → types/member}/types.d.ts +0 -0
- /package/dist/{member → types/member}/util.d.ts +0 -0
- /package/dist/{members → types/members}/collection.d.ts +0 -0
- /package/dist/{members → types/members}/index.d.ts +0 -0
- /package/dist/{members → types/members}/request.d.ts +0 -0
- /package/dist/{members → types/members}/types.d.ts +0 -0
- /package/dist/{members → types/members}/util.d.ts +0 -0
- /package/dist/{metrics → types/metrics}/constants.d.ts +0 -0
- /package/dist/{metrics → types/metrics}/index.d.ts +0 -0
- /package/dist/{multistream → types/multistream}/receiveSlot.d.ts +0 -0
- /package/dist/{multistream → types/multistream}/receiveSlotManager.d.ts +0 -0
- /package/dist/{multistream → types/multistream}/remoteMedia.d.ts +0 -0
- /package/dist/{networkQualityMonitor → types/networkQualityMonitor}/index.d.ts +0 -0
- /package/dist/{personal-meeting-room → types/personal-meeting-room}/index.d.ts +0 -0
- /package/dist/{personal-meeting-room → types/personal-meeting-room}/request.d.ts +0 -0
- /package/dist/{personal-meeting-room → types/personal-meeting-room}/util.d.ts +0 -0
- /package/dist/{reachability → types/reachability}/request.d.ts +0 -0
- /package/dist/{reactions → types/reactions}/constants.d.ts +0 -0
- /package/dist/{reactions → types/reactions}/reactions.d.ts +0 -0
- /package/dist/{reactions → types/reactions}/reactions.type.d.ts +0 -0
- /package/dist/{recording-controller → types/recording-controller}/enums.d.ts +0 -0
- /package/dist/{recording-controller → types/recording-controller}/index.d.ts +0 -0
- /package/dist/{recording-controller → types/recording-controller}/util.d.ts +0 -0
- /package/dist/{roap → types/roap}/request.d.ts +0 -0
- /package/dist/{rtcMetrics → types/rtcMetrics}/constants.d.ts +0 -0
- /package/dist/{rtcMetrics → types/rtcMetrics}/index.d.ts +0 -0
- /package/dist/{statsAnalyzer → types/statsAnalyzer}/global.d.ts +0 -0
- /package/dist/{statsAnalyzer → types/statsAnalyzer}/index.d.ts +0 -0
- /package/dist/{statsAnalyzer → types/statsAnalyzer}/mqaUtil.d.ts +0 -0
- /package/dist/{transcription → types/transcription}/index.d.ts +0 -0
- /package/dist/{webinar → types/webinar}/collection.d.ts +0 -0
- /package/dist/{webinar → types/webinar}/index.d.ts +0 -0
- /package/test/unit/spec/locus-info/{lib/selfConstant.js → selfConstant.js} +0 -0
|
@@ -7,11 +7,12 @@ import sinon from 'sinon';
|
|
|
7
7
|
import * as internalMediaModule from '@webex/internal-media-core';
|
|
8
8
|
import StateMachine from 'javascript-state-machine';
|
|
9
9
|
import uuid from 'uuid';
|
|
10
|
-
import {assert} from '@webex/test-helper-chai';
|
|
10
|
+
import {assert, expect} from '@webex/test-helper-chai';
|
|
11
11
|
import {Credentials, Token, WebexPlugin} from '@webex/webex-core';
|
|
12
12
|
import Support from '@webex/internal-plugin-support';
|
|
13
13
|
import MockWebex from '@webex/test-helper-mock-webex';
|
|
14
14
|
import StaticConfig from '@webex/plugin-meetings/src/common/config';
|
|
15
|
+
import ReconnectionNotStartedError from '@webex/plugin-meetings/src/common/errors/reconnection-not-started';
|
|
15
16
|
import {Defer} from '@webex/common';
|
|
16
17
|
import {
|
|
17
18
|
FLOOR_ACTION,
|
|
@@ -43,7 +44,7 @@ import {
|
|
|
43
44
|
RemoteTrackType,
|
|
44
45
|
MediaType,
|
|
45
46
|
} from '@webex/internal-media-core';
|
|
46
|
-
import {
|
|
47
|
+
import {LocalStreamEventNames} from '@webex/media-helpers';
|
|
47
48
|
import * as StatsAnalyzerModule from '@webex/plugin-meetings/src/statsAnalyzer';
|
|
48
49
|
import EventsScope from '@webex/plugin-meetings/src/common/events/events-scope';
|
|
49
50
|
import Meetings, {CONSTANTS} from '@webex/plugin-meetings';
|
|
@@ -99,7 +100,6 @@ import {
|
|
|
99
100
|
MeetingInfoV2PolicyError,
|
|
100
101
|
} from '../../../../src/meeting-info/meeting-info-v2';
|
|
101
102
|
import {
|
|
102
|
-
CLIENT_ERROR_CODE_TO_ERROR_PAYLOAD,
|
|
103
103
|
DTLS_HANDSHAKE_FAILED_CLIENT_CODE,
|
|
104
104
|
ICE_FAILED_WITHOUT_TURN_TLS_CLIENT_CODE,
|
|
105
105
|
ICE_FAILED_WITH_TURN_TLS_CLIENT_CODE,
|
|
@@ -110,9 +110,7 @@ import CallDiagnosticMetrics from '@webex/internal-plugin-metrics/src/call-diagn
|
|
|
110
110
|
import {ERROR_DESCRIPTIONS} from '@webex/internal-plugin-metrics/src/call-diagnostic/config';
|
|
111
111
|
import MeetingCollection from '@webex/plugin-meetings/src/meetings/collection';
|
|
112
112
|
|
|
113
|
-
import {
|
|
114
|
-
EVENT_TRIGGERS as VOICEAEVENTS,
|
|
115
|
-
} from '@webex/internal-plugin-voicea';
|
|
113
|
+
import {EVENT_TRIGGERS as VOICEAEVENTS} from '@webex/internal-plugin-voicea';
|
|
116
114
|
|
|
117
115
|
describe('plugin-meetings', () => {
|
|
118
116
|
const logger = {
|
|
@@ -613,36 +611,296 @@ describe('plugin-meetings', () => {
|
|
|
613
611
|
assert.exists(meeting.joinWithMedia);
|
|
614
612
|
});
|
|
615
613
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
614
|
+
const fakeRoapMessage = {id: 'fake TURN discovery message'};
|
|
615
|
+
const fakeReachabilityResults = {id: 'fake reachability'};
|
|
616
|
+
const fakeTurnServerInfo = {id: 'fake turn info'};
|
|
617
|
+
const fakeJoinResult = {id: 'join result'};
|
|
620
618
|
|
|
621
|
-
|
|
622
|
-
|
|
619
|
+
const joinOptions = {correlationId: '12345'};
|
|
620
|
+
const mediaOptions = {audioEnabled: true, allowMediaInLobby: true};
|
|
623
621
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
622
|
+
let generateTurnDiscoveryRequestMessageStub;
|
|
623
|
+
let handleTurnDiscoveryHttpResponseStub;
|
|
624
|
+
let abortTurnDiscoveryStub;
|
|
625
|
+
|
|
626
|
+
beforeEach(() => {
|
|
627
|
+
meeting.join = sinon.stub().returns(Promise.resolve(fakeJoinResult));
|
|
628
|
+
meeting.addMedia = sinon.stub().returns(Promise.resolve(test4));
|
|
629
|
+
|
|
630
|
+
webex.meetings.reachability.getReachabilityResults.resolves(fakeReachabilityResults);
|
|
631
|
+
|
|
632
|
+
generateTurnDiscoveryRequestMessageStub = sinon
|
|
633
|
+
.stub(meeting.roap, 'generateTurnDiscoveryRequestMessage')
|
|
634
|
+
.resolves({roapMessage: fakeRoapMessage});
|
|
635
|
+
handleTurnDiscoveryHttpResponseStub = sinon
|
|
636
|
+
.stub(meeting.roap, 'handleTurnDiscoveryHttpResponse')
|
|
637
|
+
.resolves({turnServerInfo: fakeTurnServerInfo, turnDiscoverySkippedReason: undefined});
|
|
638
|
+
abortTurnDiscoveryStub = sinon.stub(meeting.roap, 'abortTurnDiscovery');
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
it('should work as expected', async () => {
|
|
642
|
+
const result = await meeting.joinWithMedia({
|
|
643
|
+
joinOptions,
|
|
644
|
+
mediaOptions,
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
// check that TURN discovery is done with join and addMedia called
|
|
648
|
+
assert.calledOnceWithExactly(meeting.join, {
|
|
649
|
+
...joinOptions,
|
|
650
|
+
roapMessage: fakeRoapMessage,
|
|
651
|
+
reachability: fakeReachabilityResults,
|
|
652
|
+
});
|
|
653
|
+
assert.calledOnceWithExactly(generateTurnDiscoveryRequestMessageStub, meeting, true);
|
|
654
|
+
assert.calledOnceWithExactly(
|
|
655
|
+
handleTurnDiscoveryHttpResponseStub,
|
|
656
|
+
meeting,
|
|
657
|
+
fakeJoinResult
|
|
658
|
+
);
|
|
659
|
+
assert.calledOnceWithExactly(meeting.addMedia, mediaOptions, fakeTurnServerInfo);
|
|
660
|
+
|
|
661
|
+
assert.deepEqual(result, {join: fakeJoinResult, media: test4});
|
|
662
|
+
|
|
663
|
+
// resets joinWithMediaRetryInfo
|
|
664
|
+
assert.deepEqual(meeting.joinWithMediaRetryInfo, {isRetry: false, prevJoinResponse: undefined});
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
it("should not call handleTurnDiscoveryHttpResponse if we don't send a TURN discovery request with join", async () => {
|
|
668
|
+
generateTurnDiscoveryRequestMessageStub.resolves({roapMessage: undefined});
|
|
669
|
+
|
|
670
|
+
const result = await meeting.joinWithMedia({
|
|
671
|
+
joinOptions,
|
|
672
|
+
mediaOptions,
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
// check that TURN discovery is done with join and addMedia called
|
|
676
|
+
assert.calledOnceWithExactly(meeting.join, {
|
|
677
|
+
...joinOptions,
|
|
678
|
+
roapMessage: undefined,
|
|
679
|
+
reachability: fakeReachabilityResults,
|
|
680
|
+
});
|
|
681
|
+
assert.calledOnceWithExactly(generateTurnDiscoveryRequestMessageStub, meeting, true);
|
|
682
|
+
assert.notCalled(handleTurnDiscoveryHttpResponseStub);
|
|
683
|
+
assert.notCalled(abortTurnDiscoveryStub);
|
|
684
|
+
assert.calledOnceWithExactly(meeting.addMedia, mediaOptions, undefined);
|
|
685
|
+
|
|
686
|
+
assert.deepEqual(result, {join: fakeJoinResult, media: test4});
|
|
687
|
+
assert.equal(meeting.turnServerUsed, false);
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
it('should call abortTurnDiscovery() if we do not get a TURN server info', async () => {
|
|
691
|
+
handleTurnDiscoveryHttpResponseStub.resolves({
|
|
692
|
+
turnServerInfo: undefined,
|
|
693
|
+
turnDiscoverySkippedReason: 'missing http response',
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
const result = await meeting.joinWithMedia({
|
|
697
|
+
joinOptions,
|
|
698
|
+
mediaOptions,
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
// check that TURN discovery is done with join and addMedia called
|
|
702
|
+
assert.calledOnceWithExactly(meeting.join, {
|
|
703
|
+
...joinOptions,
|
|
704
|
+
roapMessage: fakeRoapMessage,
|
|
705
|
+
reachability: fakeReachabilityResults,
|
|
706
|
+
});
|
|
707
|
+
assert.calledOnceWithExactly(generateTurnDiscoveryRequestMessageStub, meeting, true);
|
|
708
|
+
assert.calledOnceWithExactly(
|
|
709
|
+
handleTurnDiscoveryHttpResponseStub,
|
|
710
|
+
meeting,
|
|
711
|
+
fakeJoinResult
|
|
712
|
+
);
|
|
713
|
+
assert.calledOnceWithExactly(abortTurnDiscoveryStub);
|
|
714
|
+
assert.calledOnceWithExactly(meeting.addMedia, mediaOptions, undefined);
|
|
715
|
+
|
|
716
|
+
assert.deepEqual(result, {join: fakeJoinResult, media: test4});
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
it('should reject if join() fails', async () => {
|
|
720
|
+
const error = new Error('fake');
|
|
721
|
+
meeting.join = sinon.stub().returns(Promise.reject(error));
|
|
722
|
+
meeting.locusUrl = null; // when join fails, we end up with null locusUrl
|
|
723
|
+
|
|
724
|
+
await assert.isRejected(meeting.joinWithMedia({mediaOptions: {allowMediaInLobby: true}}));
|
|
725
|
+
|
|
726
|
+
assert.calledTwice(abortTurnDiscoveryStub);
|
|
727
|
+
|
|
728
|
+
assert.calledTwice(Metrics.sendBehavioralMetric);
|
|
729
|
+
assert.calledWith(
|
|
730
|
+
Metrics.sendBehavioralMetric,
|
|
731
|
+
BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
|
|
732
|
+
{
|
|
733
|
+
correlation_id: meeting.correlationId,
|
|
734
|
+
locus_id: undefined,
|
|
735
|
+
reason: error.message,
|
|
736
|
+
stack: error.stack,
|
|
737
|
+
leaveErrorReason: undefined,
|
|
738
|
+
isRetry: false,
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
type: error.name,
|
|
742
|
+
}
|
|
743
|
+
);
|
|
744
|
+
assert.calledWith(
|
|
745
|
+
Metrics.sendBehavioralMetric,
|
|
746
|
+
BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
|
|
747
|
+
{
|
|
748
|
+
correlation_id: meeting.correlationId,
|
|
749
|
+
locus_id: undefined,
|
|
750
|
+
reason: error.message,
|
|
751
|
+
stack: error.stack,
|
|
752
|
+
leaveErrorReason: undefined,
|
|
753
|
+
isRetry: true,
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
type: error.name,
|
|
757
|
+
}
|
|
758
|
+
);
|
|
759
|
+
|
|
760
|
+
// resets joinWithMediaRetryInfo
|
|
761
|
+
assert.deepEqual(meeting.joinWithMediaRetryInfo, {isRetry: false, prevJoinResponse: undefined});
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
it('should resolve if join() fails the first time but succeeds the second time', async () => {
|
|
765
|
+
const error = new Error('fake');
|
|
766
|
+
meeting.join = sinon.stub().onFirstCall().returns(Promise.reject(error)).onSecondCall().returns(Promise.resolve(fakeJoinResult));
|
|
767
|
+
const leaveStub = sinon.stub(meeting, 'leave').resolves();
|
|
768
|
+
|
|
769
|
+
const result = await meeting.joinWithMedia({
|
|
770
|
+
joinOptions,
|
|
771
|
+
mediaOptions,
|
|
631
772
|
});
|
|
773
|
+
|
|
774
|
+
assert.calledOnce(abortTurnDiscoveryStub);
|
|
775
|
+
assert.calledTwice(meeting.join);
|
|
776
|
+
assert.notCalled(leaveStub);
|
|
777
|
+
|
|
778
|
+
assert.calledOnce(Metrics.sendBehavioralMetric);
|
|
779
|
+
assert.calledWith(
|
|
780
|
+
Metrics.sendBehavioralMetric.firstCall,
|
|
781
|
+
BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
|
|
782
|
+
{
|
|
783
|
+
correlation_id: meeting.correlationId,
|
|
784
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
|
785
|
+
reason: error.message,
|
|
786
|
+
stack: error.stack,
|
|
787
|
+
leaveErrorReason: undefined,
|
|
788
|
+
isRetry: false,
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
type: error.name,
|
|
792
|
+
}
|
|
793
|
+
);
|
|
794
|
+
|
|
795
|
+
assert.deepEqual(result, {join: fakeJoinResult, media: test4});
|
|
796
|
+
|
|
797
|
+
// resets joinWithMediaRetryInfo
|
|
798
|
+
assert.deepEqual(meeting.joinWithMediaRetryInfo, {isRetry: false, prevJoinResponse: undefined});
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
it('should fail if called with allowMediaInLobby:false', async () => {
|
|
802
|
+
meeting.join = sinon.stub().returns(Promise.resolve(test1));
|
|
803
|
+
meeting.addMedia = sinon.stub().returns(Promise.resolve(test4));
|
|
804
|
+
|
|
805
|
+
await assert.isRejected(
|
|
806
|
+
meeting.joinWithMedia({mediaOptions: {allowMediaInLobby: false}})
|
|
807
|
+
);
|
|
632
808
|
});
|
|
633
809
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
810
|
+
it('should call leave() if addMedia fails and ignore leave() failure', async () => {
|
|
811
|
+
const leaveError = new Error('leave error');
|
|
812
|
+
const addMediaError = new Error('fake addMedia error');
|
|
813
|
+
|
|
814
|
+
const leaveStub = sinon.stub(meeting, 'leave').rejects(leaveError);
|
|
815
|
+
meeting.addMedia = sinon.stub().rejects(addMediaError);
|
|
816
|
+
|
|
817
|
+
await assert.isRejected(
|
|
818
|
+
meeting.joinWithMedia({
|
|
819
|
+
joinOptions: {resourceId: 'some resource'},
|
|
820
|
+
mediaOptions: {allowMediaInLobby: true},
|
|
821
|
+
}),
|
|
822
|
+
addMediaError
|
|
823
|
+
);
|
|
824
|
+
|
|
825
|
+
assert.calledOnce(leaveStub);
|
|
826
|
+
assert.calledOnceWithExactly(leaveStub, {
|
|
827
|
+
resourceId: 'some resource',
|
|
828
|
+
reason: 'joinWithMedia failure',
|
|
638
829
|
});
|
|
639
830
|
|
|
640
|
-
it('should fail if called with allowMediaInLobby:false', async () => {
|
|
641
|
-
meeting.join = sinon.stub().returns(Promise.resolve(test1));
|
|
642
|
-
meeting.addMedia = sinon.stub().returns(Promise.resolve(test4));
|
|
643
831
|
|
|
644
|
-
|
|
832
|
+
// Behavioral metric is sent on both calls of joinWithMedia
|
|
833
|
+
assert.calledTwice(Metrics.sendBehavioralMetric);
|
|
834
|
+
assert.calledWith(
|
|
835
|
+
Metrics.sendBehavioralMetric.firstCall,
|
|
836
|
+
BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
|
|
837
|
+
{
|
|
838
|
+
correlation_id: meeting.correlationId,
|
|
839
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
|
840
|
+
reason: addMediaError.message,
|
|
841
|
+
stack: addMediaError.stack,
|
|
842
|
+
leaveErrorReason: undefined,
|
|
843
|
+
isRetry: false,
|
|
844
|
+
},
|
|
845
|
+
{
|
|
846
|
+
type: addMediaError.name,
|
|
847
|
+
}
|
|
848
|
+
);
|
|
849
|
+
assert.calledWith(
|
|
850
|
+
Metrics.sendBehavioralMetric.secondCall,
|
|
851
|
+
BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
|
|
852
|
+
{
|
|
853
|
+
correlation_id: meeting.correlationId,
|
|
854
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
|
855
|
+
reason: addMediaError.message,
|
|
856
|
+
stack: addMediaError.stack,
|
|
857
|
+
leaveErrorReason: leaveError.message,
|
|
858
|
+
isRetry: true,
|
|
859
|
+
},
|
|
860
|
+
{
|
|
861
|
+
type: addMediaError.name,
|
|
862
|
+
}
|
|
863
|
+
);
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
it('should not call leave() if addMedia fails the first time and succeeds the second time and should only call join() once', async () => {
|
|
867
|
+
const addMediaError = new Error('fake addMedia error');
|
|
868
|
+
const leaveError = new Error('leave error');
|
|
869
|
+
const leaveStub = sinon.stub(meeting, 'leave').rejects(leaveError);
|
|
870
|
+
|
|
871
|
+
meeting.addMedia = sinon
|
|
872
|
+
.stub()
|
|
873
|
+
.onFirstCall()
|
|
874
|
+
.rejects(addMediaError)
|
|
875
|
+
.onSecondCall()
|
|
876
|
+
.resolves(test4);
|
|
877
|
+
|
|
878
|
+
const result = await meeting.joinWithMedia({
|
|
879
|
+
joinOptions,
|
|
880
|
+
mediaOptions,
|
|
645
881
|
});
|
|
882
|
+
|
|
883
|
+
assert.deepEqual(result, {join: fakeJoinResult, media: test4});
|
|
884
|
+
|
|
885
|
+
assert.calledOnce(meeting.join);
|
|
886
|
+
assert.notCalled(leaveStub);
|
|
887
|
+
|
|
888
|
+
assert.calledOnce(Metrics.sendBehavioralMetric);
|
|
889
|
+
assert.calledWith(
|
|
890
|
+
Metrics.sendBehavioralMetric.firstCall,
|
|
891
|
+
BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
|
|
892
|
+
{
|
|
893
|
+
correlation_id: meeting.correlationId,
|
|
894
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
|
895
|
+
reason: addMediaError.message,
|
|
896
|
+
stack: addMediaError.stack,
|
|
897
|
+
leaveErrorReason: undefined,
|
|
898
|
+
isRetry: false,
|
|
899
|
+
},
|
|
900
|
+
{
|
|
901
|
+
type: addMediaError.name,
|
|
902
|
+
}
|
|
903
|
+
);
|
|
646
904
|
});
|
|
647
905
|
});
|
|
648
906
|
|
|
@@ -664,12 +922,12 @@ describe('plugin-meetings', () => {
|
|
|
664
922
|
webex.internal.voicea.on = sinon.stub();
|
|
665
923
|
webex.internal.voicea.off = sinon.stub();
|
|
666
924
|
webex.internal.voicea.listenToEvents = sinon.stub();
|
|
667
|
-
webex.internal.voicea.
|
|
925
|
+
webex.internal.voicea.turnOnCaptions = sinon.stub();
|
|
668
926
|
});
|
|
669
927
|
|
|
670
928
|
it('should subscribe to events for the first time and avoid subscribing for future transcription starts', async () => {
|
|
671
929
|
meeting.joinedWith = {
|
|
672
|
-
state: 'JOINED'
|
|
930
|
+
state: 'JOINED',
|
|
673
931
|
};
|
|
674
932
|
meeting.areVoiceaEventsSetup = false;
|
|
675
933
|
meeting.roles = ['MODERATOR'];
|
|
@@ -679,27 +937,18 @@ describe('plugin-meetings', () => {
|
|
|
679
937
|
assert.equal(webex.internal.voicea.on.callCount, 4);
|
|
680
938
|
assert.equal(meeting.areVoiceaEventsSetup, true);
|
|
681
939
|
assert.equal(webex.internal.voicea.listenToEvents.callCount, 1);
|
|
682
|
-
assert.
|
|
683
|
-
webex.internal.voicea.toggleTranscribing,
|
|
684
|
-
true,
|
|
685
|
-
);
|
|
940
|
+
assert.called(webex.internal.voicea.turnOnCaptions);
|
|
686
941
|
|
|
687
942
|
await meeting.startTranscription();
|
|
688
943
|
assert.equal(webex.internal.voicea.on.callCount, 4);
|
|
689
944
|
assert.equal(meeting.areVoiceaEventsSetup, true);
|
|
690
945
|
assert.equal(webex.internal.voicea.listenToEvents.callCount, 1);
|
|
691
|
-
assert.calledTwice(
|
|
692
|
-
webex.internal.voicea.toggleTranscribing,
|
|
693
|
-
);
|
|
694
|
-
assert.calledWith(
|
|
695
|
-
webex.internal.voicea.toggleTranscribing,
|
|
696
|
-
true,
|
|
697
|
-
);
|
|
946
|
+
assert.calledTwice(webex.internal.voicea.turnOnCaptions);
|
|
698
947
|
});
|
|
699
948
|
|
|
700
|
-
it('should listen to events and not
|
|
949
|
+
it('should listen to events and not turnOnCaptions if the user is not a host', async () => {
|
|
701
950
|
meeting.joinedWith = {
|
|
702
|
-
state: 'JOINED'
|
|
951
|
+
state: 'JOINED',
|
|
703
952
|
};
|
|
704
953
|
meeting.areVoiceaEventsSetup = false;
|
|
705
954
|
meeting.roles = ['COHOST'];
|
|
@@ -709,9 +958,7 @@ describe('plugin-meetings', () => {
|
|
|
709
958
|
assert.equal(webex.internal.voicea.on.callCount, 4);
|
|
710
959
|
assert.equal(meeting.areVoiceaEventsSetup, true);
|
|
711
960
|
assert.equal(webex.internal.voicea.listenToEvents.callCount, 1);
|
|
712
|
-
assert.notCalled(
|
|
713
|
-
webex.internal.voicea.toggleTranscribing
|
|
714
|
-
);
|
|
961
|
+
assert.notCalled(webex.internal.voicea.turnOnCaptions);
|
|
715
962
|
});
|
|
716
963
|
|
|
717
964
|
it("should throw error if request doesn't work", async () => {
|
|
@@ -730,7 +977,7 @@ describe('plugin-meetings', () => {
|
|
|
730
977
|
webex.internal.voicea.on = sinon.stub();
|
|
731
978
|
webex.internal.voicea.off = sinon.stub();
|
|
732
979
|
webex.internal.voicea.listenToEvents = sinon.stub();
|
|
733
|
-
webex.internal.voicea.
|
|
980
|
+
webex.internal.voicea.turnOnCaptions = sinon.stub();
|
|
734
981
|
});
|
|
735
982
|
|
|
736
983
|
it('should stop listening to voicea events and also trigger a stop event', () => {
|
|
@@ -752,7 +999,7 @@ describe('plugin-meetings', () => {
|
|
|
752
999
|
describe('#setCaptionLanguage', () => {
|
|
753
1000
|
beforeEach(() => {
|
|
754
1001
|
meeting.isTranscriptionSupported = sinon.stub();
|
|
755
|
-
meeting.transcription = {
|
|
1002
|
+
meeting.transcription = {languageOptions: {}};
|
|
756
1003
|
webex.internal.voicea.on = sinon.stub();
|
|
757
1004
|
webex.internal.voicea.off = sinon.stub();
|
|
758
1005
|
webex.internal.voicea.setCaptionLanguage = sinon.stub();
|
|
@@ -778,23 +1025,23 @@ describe('plugin-meetings', () => {
|
|
|
778
1025
|
const languageCode = 'fr';
|
|
779
1026
|
|
|
780
1027
|
meeting.setCaptionLanguage(languageCode).then((resolvedLanguageCode) => {
|
|
781
|
-
assert.calledWith(
|
|
782
|
-
|
|
1028
|
+
assert.calledWith(webex.internal.voicea.requestLanguage, languageCode);
|
|
1029
|
+
assert.equal(resolvedLanguageCode, languageCode);
|
|
1030
|
+
assert.equal(
|
|
1031
|
+
meeting.transcription.languageOptions.currentCaptionLanguage,
|
|
783
1032
|
languageCode
|
|
784
1033
|
);
|
|
785
|
-
assert.equal(resolvedLanguageCode, languageCode);
|
|
786
|
-
assert.equal(meeting.transcription.languageOptions.currentCaptionLanguage, languageCode);
|
|
787
1034
|
done();
|
|
788
1035
|
});
|
|
789
1036
|
|
|
790
1037
|
assert.calledOnceWithMatch(
|
|
791
1038
|
webex.internal.voicea.on,
|
|
792
|
-
VOICEAEVENTS.CAPTION_LANGUAGE_UPDATE
|
|
1039
|
+
VOICEAEVENTS.CAPTION_LANGUAGE_UPDATE
|
|
793
1040
|
);
|
|
794
1041
|
|
|
795
1042
|
// Trigger the event
|
|
796
1043
|
const voiceaListenerLangugeUpdate = webex.internal.voicea.on.getCall(0).args[1];
|
|
797
|
-
voiceaListenerLangugeUpdate({
|
|
1044
|
+
voiceaListenerLangugeUpdate({statusCode: 200, languageCode});
|
|
798
1045
|
});
|
|
799
1046
|
|
|
800
1047
|
it('should reject if the statusCode in payload is not 200', (done) => {
|
|
@@ -802,8 +1049,8 @@ describe('plugin-meetings', () => {
|
|
|
802
1049
|
const languageCode = 'fr';
|
|
803
1050
|
const rejectPayload = {
|
|
804
1051
|
statusCode: 400,
|
|
805
|
-
message: 'some error message'
|
|
806
|
-
}
|
|
1052
|
+
message: 'some error message',
|
|
1053
|
+
};
|
|
807
1054
|
|
|
808
1055
|
meeting.setCaptionLanguage(languageCode).catch((payload) => {
|
|
809
1056
|
assert.equal(payload, rejectPayload);
|
|
@@ -812,20 +1059,19 @@ describe('plugin-meetings', () => {
|
|
|
812
1059
|
|
|
813
1060
|
assert.calledOnceWithMatch(
|
|
814
1061
|
webex.internal.voicea.on,
|
|
815
|
-
VOICEAEVENTS.CAPTION_LANGUAGE_UPDATE
|
|
1062
|
+
VOICEAEVENTS.CAPTION_LANGUAGE_UPDATE
|
|
816
1063
|
);
|
|
817
1064
|
|
|
818
1065
|
// Trigger the event
|
|
819
1066
|
const voiceaListenerLangugeUpdate = webex.internal.voicea.on.getCall(0).args[1];
|
|
820
1067
|
voiceaListenerLangugeUpdate(rejectPayload);
|
|
821
1068
|
});
|
|
822
|
-
|
|
823
1069
|
});
|
|
824
1070
|
|
|
825
1071
|
describe('#setSpokenLanguage', () => {
|
|
826
1072
|
beforeEach(() => {
|
|
827
1073
|
meeting.isTranscriptionSupported = sinon.stub();
|
|
828
|
-
meeting.transcription = {
|
|
1074
|
+
meeting.transcription = {languageOptions: {}};
|
|
829
1075
|
webex.internal.voicea.on = sinon.stub();
|
|
830
1076
|
webex.internal.voicea.off = sinon.stub();
|
|
831
1077
|
webex.internal.voicea.setSpokenLanguage = sinon.stub();
|
|
@@ -850,59 +1096,48 @@ describe('plugin-meetings', () => {
|
|
|
850
1096
|
const languageCode = 'fr';
|
|
851
1097
|
|
|
852
1098
|
meeting.setSpokenLanguage(languageCode).then((resolvedLanguageCode) => {
|
|
853
|
-
assert.calledWith(
|
|
854
|
-
webex.internal.voicea.setSpokenLanguage,
|
|
855
|
-
languageCode
|
|
856
|
-
);
|
|
1099
|
+
assert.calledWith(webex.internal.voicea.setSpokenLanguage, languageCode);
|
|
857
1100
|
assert.equal(resolvedLanguageCode, languageCode);
|
|
858
1101
|
assert.equal(meeting.transcription.languageOptions.currentSpokenLanguage, languageCode);
|
|
859
1102
|
done();
|
|
860
1103
|
});
|
|
861
1104
|
|
|
862
|
-
assert.calledOnceWithMatch(
|
|
863
|
-
webex.internal.voicea.on,
|
|
864
|
-
VOICEAEVENTS.SPOKEN_LANGUAGE_UPDATE,
|
|
865
|
-
);
|
|
1105
|
+
assert.calledOnceWithMatch(webex.internal.voicea.on, VOICEAEVENTS.SPOKEN_LANGUAGE_UPDATE);
|
|
866
1106
|
|
|
867
1107
|
// Trigger the event
|
|
868
1108
|
const voiceaListenerLangugeUpdate = webex.internal.voicea.on.getCall(0).args[1];
|
|
869
|
-
voiceaListenerLangugeUpdate({
|
|
1109
|
+
voiceaListenerLangugeUpdate({languageCode});
|
|
870
1110
|
});
|
|
871
1111
|
|
|
872
1112
|
it('should reject if the language code does not exist in payload', (done) => {
|
|
873
1113
|
meeting.isTranscriptionSupported.returns(true);
|
|
874
1114
|
const languageCode = 'fr';
|
|
875
1115
|
const rejectPayload = {
|
|
876
|
-
|
|
877
|
-
}
|
|
1116
|
+
message: 'some error message',
|
|
1117
|
+
};
|
|
878
1118
|
|
|
879
1119
|
meeting.setSpokenLanguage(languageCode).catch((payload) => {
|
|
880
1120
|
assert.equal(payload, rejectPayload);
|
|
881
1121
|
done();
|
|
882
1122
|
});
|
|
883
1123
|
|
|
884
|
-
assert.calledOnceWithMatch(
|
|
885
|
-
webex.internal.voicea.on,
|
|
886
|
-
VOICEAEVENTS.SPOKEN_LANGUAGE_UPDATE,
|
|
887
|
-
);
|
|
1124
|
+
assert.calledOnceWithMatch(webex.internal.voicea.on, VOICEAEVENTS.SPOKEN_LANGUAGE_UPDATE);
|
|
888
1125
|
|
|
889
1126
|
// Trigger the event
|
|
890
1127
|
const voiceaListenerLangugeUpdate = webex.internal.voicea.on.getCall(0).args[1];
|
|
891
1128
|
voiceaListenerLangugeUpdate(rejectPayload);
|
|
892
1129
|
});
|
|
893
|
-
|
|
894
1130
|
});
|
|
895
1131
|
|
|
896
1132
|
describe('transcription events', () => {
|
|
1133
|
+
beforeEach(() => {
|
|
1134
|
+
meeting.trigger = sinon.stub();
|
|
1135
|
+
});
|
|
1136
|
+
|
|
897
1137
|
it('should trigger meeting:caption-received event', () => {
|
|
898
1138
|
meeting.voiceaListenerCallbacks[VOICEAEVENTS.NEW_CAPTION]({});
|
|
899
1139
|
assert.calledWith(
|
|
900
|
-
|
|
901
|
-
sinon.match.instanceOf(Meeting),
|
|
902
|
-
{
|
|
903
|
-
file: 'meeting/index',
|
|
904
|
-
function: 'setUpVoiceaListeners',
|
|
905
|
-
},
|
|
1140
|
+
meeting.trigger,
|
|
906
1141
|
EVENT_TRIGGERS.MEETING_CAPTION_RECEIVED
|
|
907
1142
|
);
|
|
908
1143
|
});
|
|
@@ -910,12 +1145,7 @@ describe('plugin-meetings', () => {
|
|
|
910
1145
|
it('should trigger meeting:receiveTranscription:started event', () => {
|
|
911
1146
|
meeting.voiceaListenerCallbacks[VOICEAEVENTS.VOICEA_ANNOUNCEMENT]({});
|
|
912
1147
|
assert.calledWith(
|
|
913
|
-
|
|
914
|
-
sinon.match.instanceOf(Meeting),
|
|
915
|
-
{
|
|
916
|
-
file: 'meeting/index',
|
|
917
|
-
function: 'setUpVoiceaListeners',
|
|
918
|
-
},
|
|
1148
|
+
meeting.trigger,
|
|
919
1149
|
EVENT_TRIGGERS.MEETING_STARTED_RECEIVING_TRANSCRIPTION
|
|
920
1150
|
);
|
|
921
1151
|
});
|
|
@@ -923,12 +1153,7 @@ describe('plugin-meetings', () => {
|
|
|
923
1153
|
it('should trigger meeting:caption-received event', () => {
|
|
924
1154
|
meeting.voiceaListenerCallbacks[VOICEAEVENTS.NEW_CAPTION]({});
|
|
925
1155
|
assert.calledWith(
|
|
926
|
-
|
|
927
|
-
sinon.match.instanceOf(Meeting),
|
|
928
|
-
{
|
|
929
|
-
file: 'meeting/index',
|
|
930
|
-
function: 'setUpVoiceaListeners',
|
|
931
|
-
},
|
|
1156
|
+
meeting.trigger,
|
|
932
1157
|
EVENT_TRIGGERS.MEETING_CAPTION_RECEIVED
|
|
933
1158
|
);
|
|
934
1159
|
});
|
|
@@ -1134,7 +1359,7 @@ describe('plugin-meetings', () => {
|
|
|
1134
1359
|
file: 'meeting/index',
|
|
1135
1360
|
function: 'join',
|
|
1136
1361
|
},
|
|
1137
|
-
EVENT_TRIGGERS.MEETING_TRANSCRIPTION_CONNECTED
|
|
1362
|
+
EVENT_TRIGGERS.MEETING_TRANSCRIPTION_CONNECTED
|
|
1138
1363
|
);
|
|
1139
1364
|
});
|
|
1140
1365
|
|
|
@@ -1412,7 +1637,6 @@ describe('plugin-meetings', () => {
|
|
|
1412
1637
|
describe('#addMedia', () => {
|
|
1413
1638
|
const muteStateStub = {
|
|
1414
1639
|
handleClientRequest: sinon.stub().returns(Promise.resolve(true)),
|
|
1415
|
-
applyClientStateLocally: sinon.stub().returns(Promise.resolve(true)),
|
|
1416
1640
|
};
|
|
1417
1641
|
|
|
1418
1642
|
let fakeMediaConnection;
|
|
@@ -1426,7 +1650,7 @@ describe('plugin-meetings', () => {
|
|
|
1426
1650
|
};
|
|
1427
1651
|
meeting.mediaProperties.setMediaDirection = sinon.stub().returns(true);
|
|
1428
1652
|
meeting.mediaProperties.waitForMediaConnectionConnected = sinon.stub().resolves();
|
|
1429
|
-
meeting.mediaProperties.
|
|
1653
|
+
meeting.mediaProperties.getCurrentConnectionInfo = sinon.stub().resolves({connectionType: 'udp', selectedCandidatePairChanges: 2, numTransports: 1});
|
|
1430
1654
|
meeting.audio = muteStateStub;
|
|
1431
1655
|
meeting.video = muteStateStub;
|
|
1432
1656
|
sinon.stub(Media, 'createMediaConnection').returns(fakeMediaConnection);
|
|
@@ -1526,11 +1750,14 @@ describe('plugin-meetings', () => {
|
|
|
1526
1750
|
turnServerUsed: true,
|
|
1527
1751
|
retriedWithTurnServer: false,
|
|
1528
1752
|
isMultistream: false,
|
|
1753
|
+
isJoinWithMediaRetry: false,
|
|
1529
1754
|
signalingState: 'unknown',
|
|
1530
1755
|
connectionState: 'unknown',
|
|
1531
1756
|
iceConnectionState: 'unknown',
|
|
1532
1757
|
someReachabilityMetric1: 'some value1',
|
|
1533
1758
|
someReachabilityMetric2: 'some value2',
|
|
1759
|
+
selectedCandidatePairChanges: 2,
|
|
1760
|
+
numTransports: 1,
|
|
1534
1761
|
}
|
|
1535
1762
|
);
|
|
1536
1763
|
});
|
|
@@ -1630,11 +1857,14 @@ describe('plugin-meetings', () => {
|
|
|
1630
1857
|
turnServerUsed: true,
|
|
1631
1858
|
retriedWithTurnServer: false,
|
|
1632
1859
|
isMultistream: false,
|
|
1860
|
+
isJoinWithMediaRetry: false,
|
|
1633
1861
|
signalingState: 'unknown',
|
|
1634
1862
|
connectionState: 'unknown',
|
|
1635
1863
|
iceConnectionState: 'unknown',
|
|
1636
1864
|
someReachabilityMetric1: 'some value1',
|
|
1637
1865
|
someReachabilityMetric2: 'some value2',
|
|
1866
|
+
selectedCandidatePairChanges: 2,
|
|
1867
|
+
numTransports: 1,
|
|
1638
1868
|
}
|
|
1639
1869
|
);
|
|
1640
1870
|
});
|
|
@@ -2108,9 +2338,12 @@ describe('plugin-meetings', () => {
|
|
|
2108
2338
|
turnServerUsed: true,
|
|
2109
2339
|
retriedWithTurnServer: true,
|
|
2110
2340
|
isMultistream: false,
|
|
2341
|
+
isJoinWithMediaRetry: false,
|
|
2111
2342
|
signalingState: 'unknown',
|
|
2112
2343
|
connectionState: 'unknown',
|
|
2113
2344
|
iceConnectionState: 'unknown',
|
|
2345
|
+
selectedCandidatePairChanges: 2,
|
|
2346
|
+
numTransports: 1,
|
|
2114
2347
|
},
|
|
2115
2348
|
]);
|
|
2116
2349
|
|
|
@@ -2289,8 +2522,11 @@ describe('plugin-meetings', () => {
|
|
|
2289
2522
|
correlation_id: meeting.correlationId,
|
|
2290
2523
|
locus_id: meeting.locusUrl.split('/').pop(),
|
|
2291
2524
|
connectionType: 'udp',
|
|
2525
|
+
selectedCandidatePairChanges: 2,
|
|
2526
|
+
numTransports: 1,
|
|
2292
2527
|
isMultistream: false,
|
|
2293
2528
|
retriedWithTurnServer: true,
|
|
2529
|
+
isJoinWithMediaRetry: false,
|
|
2294
2530
|
},
|
|
2295
2531
|
]);
|
|
2296
2532
|
meeting.roap.doTurnDiscovery;
|
|
@@ -2431,8 +2667,11 @@ describe('plugin-meetings', () => {
|
|
|
2431
2667
|
correlation_id: meeting.correlationId,
|
|
2432
2668
|
locus_id: meeting.locusUrl.split('/').pop(),
|
|
2433
2669
|
connectionType: 'udp',
|
|
2670
|
+
selectedCandidatePairChanges: 2,
|
|
2671
|
+
numTransports: 1,
|
|
2434
2672
|
isMultistream: false,
|
|
2435
2673
|
retriedWithTurnServer: false,
|
|
2674
|
+
isJoinWithMediaRetry: false,
|
|
2436
2675
|
someReachabilityMetric1: 'some value1',
|
|
2437
2676
|
someReachabilityMetric2: 'some value2',
|
|
2438
2677
|
}
|
|
@@ -2491,9 +2730,12 @@ describe('plugin-meetings', () => {
|
|
|
2491
2730
|
turnServerUsed: true,
|
|
2492
2731
|
retriedWithTurnServer: false,
|
|
2493
2732
|
isMultistream: false,
|
|
2733
|
+
isJoinWithMediaRetry: false,
|
|
2494
2734
|
signalingState: 'unknown',
|
|
2495
2735
|
connectionState: 'unknown',
|
|
2496
2736
|
iceConnectionState: 'unknown',
|
|
2737
|
+
selectedCandidatePairChanges: 2,
|
|
2738
|
+
numTransports: 1,
|
|
2497
2739
|
}
|
|
2498
2740
|
);
|
|
2499
2741
|
|
|
@@ -2506,6 +2748,7 @@ describe('plugin-meetings', () => {
|
|
|
2506
2748
|
|
|
2507
2749
|
beforeEach(async () => {
|
|
2508
2750
|
meeting.meetingState = 'ACTIVE';
|
|
2751
|
+
meeting.remoteShareInstanceId = '1234';
|
|
2509
2752
|
prevConfigValue = meeting.config.stats.enableStatsAnalyzer;
|
|
2510
2753
|
|
|
2511
2754
|
meeting.config.stats.enableStatsAnalyzer = true;
|
|
@@ -2611,6 +2854,66 @@ describe('plugin-meetings', () => {
|
|
|
2611
2854
|
});
|
|
2612
2855
|
});
|
|
2613
2856
|
|
|
2857
|
+
it('REMOTE_MEDIA_STARTED triggers "meeting:media:remote:start" event and sends metrics for share', async () => {
|
|
2858
|
+
statsAnalyzerStub.emit(
|
|
2859
|
+
{file: 'test', function: 'test'},
|
|
2860
|
+
StatsAnalyzerModule.EVENTS.REMOTE_MEDIA_STARTED,
|
|
2861
|
+
{type: 'share'}
|
|
2862
|
+
);
|
|
2863
|
+
|
|
2864
|
+
assert.calledWith(
|
|
2865
|
+
TriggerProxy.trigger,
|
|
2866
|
+
sinon.match.instanceOf(Meeting),
|
|
2867
|
+
{
|
|
2868
|
+
file: 'meeting/index',
|
|
2869
|
+
function: 'addMedia',
|
|
2870
|
+
},
|
|
2871
|
+
EVENT_TRIGGERS.MEETING_MEDIA_REMOTE_STARTED,
|
|
2872
|
+
{
|
|
2873
|
+
type: 'share',
|
|
2874
|
+
}
|
|
2875
|
+
);
|
|
2876
|
+
assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
|
|
2877
|
+
name: 'client.media.rx.start',
|
|
2878
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
|
2879
|
+
options: {
|
|
2880
|
+
meetingId: meeting.id,
|
|
2881
|
+
},
|
|
2882
|
+
});
|
|
2883
|
+
|
|
2884
|
+
assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
|
|
2885
|
+
name: 'client.media.render.start',
|
|
2886
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
|
2887
|
+
options: {
|
|
2888
|
+
meetingId: meeting.id,
|
|
2889
|
+
},
|
|
2890
|
+
});
|
|
2891
|
+
});
|
|
2892
|
+
|
|
2893
|
+
it('REMOTE_MEDIA_STOPPED triggers the right metrics for share', async () => {
|
|
2894
|
+
statsAnalyzerStub.emit(
|
|
2895
|
+
{file: 'test', function: 'test'},
|
|
2896
|
+
StatsAnalyzerModule.EVENTS.REMOTE_MEDIA_STOPPED,
|
|
2897
|
+
{type: 'share'}
|
|
2898
|
+
);
|
|
2899
|
+
|
|
2900
|
+
assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
|
|
2901
|
+
name: 'client.media.rx.stop',
|
|
2902
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
|
2903
|
+
options: {
|
|
2904
|
+
meetingId: meeting.id,
|
|
2905
|
+
},
|
|
2906
|
+
});
|
|
2907
|
+
|
|
2908
|
+
assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
|
|
2909
|
+
name: 'client.media.render.stop',
|
|
2910
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
|
2911
|
+
options: {
|
|
2912
|
+
meetingId: meeting.id,
|
|
2913
|
+
},
|
|
2914
|
+
});
|
|
2915
|
+
});
|
|
2916
|
+
|
|
2614
2917
|
it('calls submitMQE correctly', async () => {
|
|
2615
2918
|
const fakeData = {intervalMetadata: {bla: 'bla'}};
|
|
2616
2919
|
|
|
@@ -2851,13 +3154,14 @@ describe('plugin-meetings', () => {
|
|
|
2851
3154
|
meeting.mediaId = 'fake media id';
|
|
2852
3155
|
meeting.selfUrl = 'selfUrl';
|
|
2853
3156
|
meeting.mediaProperties.waitForMediaConnectionConnected = sinon.stub().resolves();
|
|
2854
|
-
meeting.mediaProperties.
|
|
3157
|
+
meeting.mediaProperties.getCurrentConnectionInfo = sinon.stub().resolves({connectionType: 'udp', selectedCandidatePairChanges: 2, numTransports: 1});
|
|
2855
3158
|
meeting.setMercuryListener = sinon.stub();
|
|
2856
3159
|
meeting.locusInfo.onFullLocus = sinon.stub();
|
|
2857
3160
|
meeting.webex.meetings.geoHintInfo = {regionCode: 'EU', countryCode: 'UK'};
|
|
2858
|
-
meeting.roap.doTurnDiscovery = sinon
|
|
2859
|
-
|
|
2860
|
-
|
|
3161
|
+
meeting.roap.doTurnDiscovery = sinon.stub().resolves({
|
|
3162
|
+
turnServerInfo: {url: 'turn-url', username: 'turn user', password: 'turn password'},
|
|
3163
|
+
turnDiscoverySkippedReason: 'reachability',
|
|
3164
|
+
});
|
|
2861
3165
|
meeting.deferSDPAnswer = new Defer();
|
|
2862
3166
|
meeting.deferSDPAnswer.resolve();
|
|
2863
3167
|
meeting.webex.meetings.meetingCollection = new MeetingCollection();
|
|
@@ -2868,7 +3172,7 @@ describe('plugin-meetings', () => {
|
|
|
2868
3172
|
// setup things that are expected to be the same across all the tests and are actually irrelevant for these tests
|
|
2869
3173
|
expectedDebugId = `MC-${meeting.id.substring(0, 4)}`;
|
|
2870
3174
|
expectedMediaConnectionConfig = {
|
|
2871
|
-
iceServers: [{urls:
|
|
3175
|
+
iceServers: [{urls: 'turn-url', username: 'turn user', credential: 'turn password'}],
|
|
2872
3176
|
skipInactiveTransceivers: false,
|
|
2873
3177
|
requireH264: true,
|
|
2874
3178
|
sdpMunging: {
|
|
@@ -2892,9 +3196,13 @@ describe('plugin-meetings', () => {
|
|
|
2892
3196
|
getSettings: sinon.stub().returns({
|
|
2893
3197
|
deviceId: 'some device id',
|
|
2894
3198
|
}),
|
|
2895
|
-
|
|
3199
|
+
userMuted: false,
|
|
3200
|
+
systemMuted: false,
|
|
3201
|
+
get muted() {
|
|
3202
|
+
return this.userMuted || this.systemMuted;
|
|
3203
|
+
},
|
|
2896
3204
|
setUnmuteAllowed: sinon.stub(),
|
|
2897
|
-
|
|
3205
|
+
setUserMuted: sinon.stub(),
|
|
2898
3206
|
setServerMuted: sinon.stub(),
|
|
2899
3207
|
outputStream: {
|
|
2900
3208
|
getTracks: () => {
|
|
@@ -2927,6 +3235,7 @@ describe('plugin-meetings', () => {
|
|
|
2927
3235
|
createSendSlot: sinon.stub().returns({
|
|
2928
3236
|
publishStream: sinon.stub(),
|
|
2929
3237
|
unpublishStream: sinon.stub(),
|
|
3238
|
+
setNamedMediaGroups: sinon.stub(),
|
|
2930
3239
|
}),
|
|
2931
3240
|
enableMultistreamAudio: sinon.stub(),
|
|
2932
3241
|
};
|
|
@@ -3128,28 +3437,52 @@ describe('plugin-meetings', () => {
|
|
|
3128
3437
|
if (stream !== undefined) {
|
|
3129
3438
|
switch (type) {
|
|
3130
3439
|
case 'audio':
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3440
|
+
if (stream?.readyState === 'ended') {
|
|
3441
|
+
assert.notCalled(
|
|
3442
|
+
meeting.sendSlotManager.getSlot(MediaType.AudioMain).publishStream
|
|
3443
|
+
);
|
|
3444
|
+
} else {
|
|
3445
|
+
assert.calledOnceWithExactly(
|
|
3446
|
+
meeting.sendSlotManager.getSlot(MediaType.AudioMain).publishStream,
|
|
3447
|
+
stream
|
|
3448
|
+
);
|
|
3449
|
+
}
|
|
3135
3450
|
break;
|
|
3136
3451
|
case 'video':
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3452
|
+
if (stream?.readyState === 'ended') {
|
|
3453
|
+
assert.notCalled(
|
|
3454
|
+
meeting.sendSlotManager.getSlot(MediaType.VideoMain).publishStream
|
|
3455
|
+
);
|
|
3456
|
+
} else {
|
|
3457
|
+
assert.calledOnceWithExactly(
|
|
3458
|
+
meeting.sendSlotManager.getSlot(MediaType.VideoMain).publishStream,
|
|
3459
|
+
stream
|
|
3460
|
+
);
|
|
3461
|
+
}
|
|
3141
3462
|
break;
|
|
3142
3463
|
case 'screenShareAudio':
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3464
|
+
if (stream?.readyState === 'ended') {
|
|
3465
|
+
assert.notCalled(
|
|
3466
|
+
meeting.sendSlotManager.getSlot(MediaType.AudioSlides).publishStream
|
|
3467
|
+
);
|
|
3468
|
+
} else {
|
|
3469
|
+
assert.calledOnceWithExactly(
|
|
3470
|
+
meeting.sendSlotManager.getSlot(MediaType.AudioSlides).publishStream,
|
|
3471
|
+
stream
|
|
3472
|
+
);
|
|
3473
|
+
}
|
|
3147
3474
|
break;
|
|
3148
3475
|
case 'screenShareVideo':
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3476
|
+
if (stream?.readyState === 'ended') {
|
|
3477
|
+
assert.notCalled(
|
|
3478
|
+
meeting.sendSlotManager.getSlot(MediaType.VideoSlides).publishStream
|
|
3479
|
+
);
|
|
3480
|
+
} else {
|
|
3481
|
+
assert.calledOnceWithExactly(
|
|
3482
|
+
meeting.sendSlotManager.getSlot(MediaType.VideoSlides).publishStream,
|
|
3483
|
+
stream
|
|
3484
|
+
);
|
|
3485
|
+
}
|
|
3153
3486
|
break;
|
|
3154
3487
|
}
|
|
3155
3488
|
}
|
|
@@ -3177,7 +3510,7 @@ describe('plugin-meetings', () => {
|
|
|
3177
3510
|
}
|
|
3178
3511
|
};
|
|
3179
3512
|
|
|
3180
|
-
it('addMedia() works correctly when media is enabled without
|
|
3513
|
+
it('addMedia() works correctly when media is enabled without streams to publish', async () => {
|
|
3181
3514
|
await meeting.addMedia();
|
|
3182
3515
|
await simulateRoapOffer();
|
|
3183
3516
|
await simulateRoapOk();
|
|
@@ -3211,6 +3544,7 @@ describe('plugin-meetings', () => {
|
|
|
3211
3544
|
});
|
|
3212
3545
|
|
|
3213
3546
|
it('addMedia() works correctly when media is enabled with streams to publish', async () => {
|
|
3547
|
+
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
|
|
3214
3548
|
await meeting.addMedia({localStreams: {microphone: fakeMicrophoneStream}});
|
|
3215
3549
|
await simulateRoapOffer();
|
|
3216
3550
|
await simulateRoapOk();
|
|
@@ -3241,10 +3575,82 @@ describe('plugin-meetings', () => {
|
|
|
3241
3575
|
|
|
3242
3576
|
// and that these were the only /media requests that were sent
|
|
3243
3577
|
assert.calledTwice(locusMediaRequestStub);
|
|
3578
|
+
|
|
3579
|
+
assert.calledOnce(handleDeviceLoggingSpy);
|
|
3580
|
+
});
|
|
3581
|
+
|
|
3582
|
+
it('addMedia() works correctly when media is enabled with streams to publish and stream is user muted', async () => {
|
|
3583
|
+
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
|
|
3584
|
+
fakeMicrophoneStream.userMuted = true;
|
|
3585
|
+
|
|
3586
|
+
await meeting.addMedia({localStreams: {microphone: fakeMicrophoneStream}});
|
|
3587
|
+
await simulateRoapOffer();
|
|
3588
|
+
await simulateRoapOk();
|
|
3589
|
+
|
|
3590
|
+
// check RoapMediaConnection was created correctly
|
|
3591
|
+
checkMediaConnectionCreated({
|
|
3592
|
+
mediaConnectionConfig: expectedMediaConnectionConfig,
|
|
3593
|
+
localStreams: {
|
|
3594
|
+
audio: fakeMicrophoneStream,
|
|
3595
|
+
video: undefined,
|
|
3596
|
+
screenShareVideo: undefined,
|
|
3597
|
+
screenShareAudio: undefined,
|
|
3598
|
+
},
|
|
3599
|
+
direction: {
|
|
3600
|
+
audio: 'sendrecv',
|
|
3601
|
+
video: 'sendrecv',
|
|
3602
|
+
screenShare: 'recvonly',
|
|
3603
|
+
},
|
|
3604
|
+
remoteQualityLevel: 'HIGH',
|
|
3605
|
+
expectedDebugId,
|
|
3606
|
+
meetingId: meeting.id,
|
|
3607
|
+
});
|
|
3608
|
+
// and SDP offer was sent with the right audioMuted/videoMuted values
|
|
3609
|
+
checkSdpOfferSent({audioMuted: true, videoMuted: true});
|
|
3610
|
+
// check OK was sent with the right audioMuted/videoMuted values
|
|
3611
|
+
checkOkSent({audioMuted: true, videoMuted: true});
|
|
3612
|
+
|
|
3613
|
+
// and that these were the only /media requests that were sent
|
|
3614
|
+
assert.calledTwice(locusMediaRequestStub);
|
|
3615
|
+
assert.calledOnce(handleDeviceLoggingSpy);
|
|
3616
|
+
});
|
|
3617
|
+
|
|
3618
|
+
it('addMedia() works correctly when media is enabled with tracks to publish and track is ended', async () => {
|
|
3619
|
+
fakeMicrophoneStream.readyState = 'ended';
|
|
3620
|
+
|
|
3621
|
+
await meeting.addMedia({localStreams: {microphone: fakeMicrophoneStream}});
|
|
3622
|
+
await simulateRoapOffer();
|
|
3623
|
+
await simulateRoapOk();
|
|
3624
|
+
|
|
3625
|
+
// check RoapMediaConnection was created correctly
|
|
3626
|
+
checkMediaConnectionCreated({
|
|
3627
|
+
mediaConnectionConfig: expectedMediaConnectionConfig,
|
|
3628
|
+
localStreams: {
|
|
3629
|
+
audio: undefined,
|
|
3630
|
+
video: undefined,
|
|
3631
|
+
screenShareVideo: undefined,
|
|
3632
|
+
screenShareAudio: undefined,
|
|
3633
|
+
},
|
|
3634
|
+
direction: {
|
|
3635
|
+
audio: 'sendrecv',
|
|
3636
|
+
video: 'sendrecv',
|
|
3637
|
+
screenShare: 'recvonly',
|
|
3638
|
+
},
|
|
3639
|
+
remoteQualityLevel: 'HIGH',
|
|
3640
|
+
expectedDebugId,
|
|
3641
|
+
meetingId: meeting.id,
|
|
3642
|
+
});
|
|
3643
|
+
// and SDP offer was sent with the right audioMuted/videoMuted values
|
|
3644
|
+
checkSdpOfferSent({audioMuted: true, videoMuted: true});
|
|
3645
|
+
// check OK was sent with the right audioMuted/videoMuted values
|
|
3646
|
+
checkOkSent({audioMuted: true, videoMuted: true});
|
|
3647
|
+
|
|
3648
|
+
// and that these were the only /media requests that were sent
|
|
3649
|
+
assert.calledTwice(locusMediaRequestStub);
|
|
3244
3650
|
});
|
|
3245
3651
|
|
|
3246
|
-
it('addMedia() works correctly when media is enabled with
|
|
3247
|
-
fakeMicrophoneStream.
|
|
3652
|
+
it('addMedia() works correctly when media is enabled with streams to publish and stream is system muted', async () => {
|
|
3653
|
+
fakeMicrophoneStream.systemMuted = true;
|
|
3248
3654
|
|
|
3249
3655
|
await meeting.addMedia({localStreams: {microphone: fakeMicrophoneStream}});
|
|
3250
3656
|
await simulateRoapOffer();
|
|
@@ -3277,7 +3683,8 @@ describe('plugin-meetings', () => {
|
|
|
3277
3683
|
assert.calledTwice(locusMediaRequestStub);
|
|
3278
3684
|
});
|
|
3279
3685
|
|
|
3280
|
-
it('addMedia() works correctly when media is disabled with
|
|
3686
|
+
it('addMedia() works correctly when media is disabled with streams to publish', async () => {
|
|
3687
|
+
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
|
|
3281
3688
|
await meeting.addMedia({
|
|
3282
3689
|
localStreams: {microphone: fakeMicrophoneStream},
|
|
3283
3690
|
audioEnabled: false,
|
|
@@ -3311,9 +3718,23 @@ describe('plugin-meetings', () => {
|
|
|
3311
3718
|
|
|
3312
3719
|
// and that these were the only /media requests that were sent
|
|
3313
3720
|
assert.calledTwice(locusMediaRequestStub);
|
|
3721
|
+
assert.calledOnce(handleDeviceLoggingSpy);
|
|
3314
3722
|
});
|
|
3315
3723
|
|
|
3316
|
-
it('
|
|
3724
|
+
it('handleDeviceLogging not called when media is disabled', async () => {
|
|
3725
|
+
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
|
|
3726
|
+
await meeting.addMedia({
|
|
3727
|
+
localStreams: {microphone: fakeMicrophoneStream},
|
|
3728
|
+
audioEnabled: false,
|
|
3729
|
+
videoEnabled: false
|
|
3730
|
+
});
|
|
3731
|
+
await simulateRoapOffer();
|
|
3732
|
+
await simulateRoapOk();
|
|
3733
|
+
|
|
3734
|
+
assert.notCalled(handleDeviceLoggingSpy);
|
|
3735
|
+
})
|
|
3736
|
+
|
|
3737
|
+
it('addMedia() works correctly when media is disabled with no streams to publish', async () => {
|
|
3317
3738
|
await meeting.addMedia({audioEnabled: false});
|
|
3318
3739
|
await simulateRoapOffer();
|
|
3319
3740
|
await simulateRoapOk();
|
|
@@ -3346,7 +3767,7 @@ describe('plugin-meetings', () => {
|
|
|
3346
3767
|
assert.calledTwice(locusMediaRequestStub);
|
|
3347
3768
|
});
|
|
3348
3769
|
|
|
3349
|
-
it('addMedia() works correctly when video is disabled with no
|
|
3770
|
+
it('addMedia() works correctly when video is disabled with no streams to publish', async () => {
|
|
3350
3771
|
await meeting.addMedia({videoEnabled: false});
|
|
3351
3772
|
await simulateRoapOffer();
|
|
3352
3773
|
await simulateRoapOk();
|
|
@@ -3379,7 +3800,7 @@ describe('plugin-meetings', () => {
|
|
|
3379
3800
|
assert.calledTwice(locusMediaRequestStub);
|
|
3380
3801
|
});
|
|
3381
3802
|
|
|
3382
|
-
it('addMedia() works correctly when screen share is disabled with no
|
|
3803
|
+
it('addMedia() works correctly when screen share is disabled with no streams to publish', async () => {
|
|
3383
3804
|
await meeting.addMedia({shareAudioEnabled: false, shareVideoEnabled: false});
|
|
3384
3805
|
await simulateRoapOffer();
|
|
3385
3806
|
await simulateRoapOk();
|
|
@@ -3480,9 +3901,13 @@ describe('plugin-meetings', () => {
|
|
|
3480
3901
|
const fakeMicrophoneStream2 = {
|
|
3481
3902
|
on: sinon.stub(),
|
|
3482
3903
|
off: sinon.stub(),
|
|
3483
|
-
|
|
3904
|
+
userMuted: false,
|
|
3905
|
+
systemMuted: false,
|
|
3906
|
+
get muted() {
|
|
3907
|
+
return this.userMuted || this.systemMuted;
|
|
3908
|
+
},
|
|
3484
3909
|
setUnmuteAllowed: sinon.stub(),
|
|
3485
|
-
|
|
3910
|
+
setUserMuted: sinon.stub(),
|
|
3486
3911
|
outputStream: {
|
|
3487
3912
|
getTracks: () => {
|
|
3488
3913
|
return [
|
|
@@ -3719,12 +4144,55 @@ describe('plugin-meetings', () => {
|
|
|
3719
4144
|
});
|
|
3720
4145
|
|
|
3721
4146
|
[
|
|
3722
|
-
{mute: true, title: 'muting a track before confluence is created'},
|
|
3723
|
-
{mute: false, title: 'unmuting a track before confluence is created'},
|
|
4147
|
+
{mute: true, title: 'user muting a track before confluence is created'},
|
|
4148
|
+
{mute: false, title: 'user unmuting a track before confluence is created'},
|
|
4149
|
+
].forEach(({mute, title}) =>
|
|
4150
|
+
it(title, async () => {
|
|
4151
|
+
// initialize the microphone mute state to opposite of what we do in the test
|
|
4152
|
+
fakeMicrophoneStream.userMuted = !mute;
|
|
4153
|
+
|
|
4154
|
+
await meeting.addMedia({localStreams: {microphone: fakeMicrophoneStream}});
|
|
4155
|
+
await stableState();
|
|
4156
|
+
|
|
4157
|
+
resetHistory();
|
|
4158
|
+
|
|
4159
|
+
assert.equal(
|
|
4160
|
+
fakeMicrophoneStream.on.getCall(0).args[0],
|
|
4161
|
+
LocalStreamEventNames.UserMuteStateChange
|
|
4162
|
+
);
|
|
4163
|
+
const mutedListener = fakeMicrophoneStream.on.getCall(0).args[1];
|
|
4164
|
+
// simulate track being muted
|
|
4165
|
+
fakeMicrophoneStream.userMuted = mute;
|
|
4166
|
+
mutedListener(mute);
|
|
4167
|
+
|
|
4168
|
+
await stableState();
|
|
4169
|
+
|
|
4170
|
+
// nothing should happen
|
|
4171
|
+
assert.notCalled(locusMediaRequestStub);
|
|
4172
|
+
assert.notCalled(fakeRoapMediaConnection.update);
|
|
4173
|
+
|
|
4174
|
+
// now simulate roap offer and ok
|
|
4175
|
+
await simulateRoapOffer();
|
|
4176
|
+
await simulateRoapOk();
|
|
4177
|
+
|
|
4178
|
+
// it should be sent with the right mute status
|
|
4179
|
+
checkSdpOfferSent({audioMuted: mute, videoMuted: true});
|
|
4180
|
+
// check OK was sent with the right audioMuted/videoMuted values
|
|
4181
|
+
checkOkSent({audioMuted: mute, videoMuted: true});
|
|
4182
|
+
|
|
4183
|
+
// nothing else should happen
|
|
4184
|
+
assert.calledTwice(locusMediaRequestStub);
|
|
4185
|
+
assert.notCalled(fakeRoapMediaConnection.update);
|
|
4186
|
+
})
|
|
4187
|
+
);
|
|
4188
|
+
|
|
4189
|
+
[
|
|
4190
|
+
{mute: true, title: 'system muting a track before confluence is created'},
|
|
4191
|
+
{mute: false, title: 'system unmuting a track before confluence is created'},
|
|
3724
4192
|
].forEach(({mute, title}) =>
|
|
3725
4193
|
it(title, async () => {
|
|
3726
4194
|
// initialize the microphone mute state to opposite of what we do in the test
|
|
3727
|
-
fakeMicrophoneStream.
|
|
4195
|
+
fakeMicrophoneStream.systemMuted = !mute;
|
|
3728
4196
|
|
|
3729
4197
|
await meeting.addMedia({localStreams: {microphone: fakeMicrophoneStream}});
|
|
3730
4198
|
await stableState();
|
|
@@ -3733,10 +4201,11 @@ describe('plugin-meetings', () => {
|
|
|
3733
4201
|
|
|
3734
4202
|
assert.equal(
|
|
3735
4203
|
fakeMicrophoneStream.on.getCall(0).args[0],
|
|
3736
|
-
|
|
4204
|
+
LocalStreamEventNames.UserMuteStateChange
|
|
3737
4205
|
);
|
|
3738
4206
|
const mutedListener = fakeMicrophoneStream.on.getCall(0).args[1];
|
|
3739
4207
|
// simulate track being muted
|
|
4208
|
+
fakeMicrophoneStream.systemMuted = mute;
|
|
3740
4209
|
mutedListener(mute);
|
|
3741
4210
|
|
|
3742
4211
|
await stableState();
|
|
@@ -6137,6 +6606,65 @@ describe('plugin-meetings', () => {
|
|
|
6137
6606
|
checkScreenShareVideoPublished(videoShareStream);
|
|
6138
6607
|
checkScreenShareAudioPublished(audioShareStream);
|
|
6139
6608
|
});
|
|
6609
|
+
|
|
6610
|
+
[
|
|
6611
|
+
{
|
|
6612
|
+
endedStream: 'microphone',
|
|
6613
|
+
streams: {
|
|
6614
|
+
microphone: {
|
|
6615
|
+
readyState: 'ended',
|
|
6616
|
+
},
|
|
6617
|
+
camera: undefined,
|
|
6618
|
+
screenShare: {
|
|
6619
|
+
audio: undefined,
|
|
6620
|
+
video: undefined,
|
|
6621
|
+
},
|
|
6622
|
+
},
|
|
6623
|
+
},
|
|
6624
|
+
{
|
|
6625
|
+
endedStream: 'camera',
|
|
6626
|
+
streams: {
|
|
6627
|
+
microphone: undefined,
|
|
6628
|
+
camera: {
|
|
6629
|
+
readyState: 'ended',
|
|
6630
|
+
},
|
|
6631
|
+
screenShare: {
|
|
6632
|
+
audio: undefined,
|
|
6633
|
+
video: undefined,
|
|
6634
|
+
},
|
|
6635
|
+
},
|
|
6636
|
+
},
|
|
6637
|
+
{
|
|
6638
|
+
endedStream: 'screenShare audio',
|
|
6639
|
+
streams: {
|
|
6640
|
+
microphone: undefined,
|
|
6641
|
+
camera: undefined,
|
|
6642
|
+
screenShare: {
|
|
6643
|
+
audio: {
|
|
6644
|
+
readyState: 'ended',
|
|
6645
|
+
},
|
|
6646
|
+
video: undefined,
|
|
6647
|
+
},
|
|
6648
|
+
},
|
|
6649
|
+
},
|
|
6650
|
+
{
|
|
6651
|
+
endedStream: 'screenShare video',
|
|
6652
|
+
streams: {
|
|
6653
|
+
microphone: undefined,
|
|
6654
|
+
camera: undefined,
|
|
6655
|
+
screenShare: {
|
|
6656
|
+
audio: undefined,
|
|
6657
|
+
video: {
|
|
6658
|
+
readyState: 'ended',
|
|
6659
|
+
},
|
|
6660
|
+
},
|
|
6661
|
+
},
|
|
6662
|
+
},
|
|
6663
|
+
].forEach(({endedStream, streams}) => {
|
|
6664
|
+
it(`throws error if readyState of ${endedStream} is ended`, async () => {
|
|
6665
|
+
assert.isRejected(meeting.publishStreams(streams));
|
|
6666
|
+
});
|
|
6667
|
+
});
|
|
6140
6668
|
});
|
|
6141
6669
|
|
|
6142
6670
|
describe('unpublishStreams', () => {
|
|
@@ -6266,6 +6794,31 @@ describe('plugin-meetings', () => {
|
|
|
6266
6794
|
});
|
|
6267
6795
|
});
|
|
6268
6796
|
|
|
6797
|
+
describe('#setSendNamedMediaGroup', () => {
|
|
6798
|
+
beforeEach(() => {
|
|
6799
|
+
meeting.sendSlotManager.setNamedMediaGroups = sinon.stub().returns(undefined);
|
|
6800
|
+
});
|
|
6801
|
+
it('should throw error if not audio type', () => {
|
|
6802
|
+
expect(() => meeting.setSendNamedMediaGroup(MediaType.VideoMain, 20)).to.throw(
|
|
6803
|
+
`cannot set send named media group which media type is ${MediaType.VideoMain}`
|
|
6804
|
+
);
|
|
6805
|
+
});
|
|
6806
|
+
it('fails if there is no media connection', () => {
|
|
6807
|
+
meeting.mediaProperties.webrtcMediaConnection = undefined;
|
|
6808
|
+
meeting.setSendNamedMediaGroup('AUDIO-MAIN', 20);
|
|
6809
|
+
assert.notCalled(meeting.sendSlotManager.setNamedMediaGroups);
|
|
6810
|
+
});
|
|
6811
|
+
|
|
6812
|
+
it('success if there is media connection', () => {
|
|
6813
|
+
meeting.isMultistream = true;
|
|
6814
|
+
meeting.mediaProperties.webrtcMediaConnection = true;
|
|
6815
|
+
meeting.setSendNamedMediaGroup('AUDIO-MAIN', 20);
|
|
6816
|
+
assert.calledOnceWithExactly(meeting.sendSlotManager.setNamedMediaGroups, 'AUDIO-MAIN', [
|
|
6817
|
+
{type: 1, value: 20},
|
|
6818
|
+
]);
|
|
6819
|
+
});
|
|
6820
|
+
});
|
|
6821
|
+
|
|
6269
6822
|
describe('#enableMusicMode', () => {
|
|
6270
6823
|
beforeEach(() => {
|
|
6271
6824
|
meeting.isMultistream = true;
|
|
@@ -6362,21 +6915,13 @@ describe('plugin-meetings', () => {
|
|
|
6362
6915
|
}),
|
|
6363
6916
|
};
|
|
6364
6917
|
meeting.setupMediaConnectionListeners();
|
|
6365
|
-
meeting.deferSDPAnswer = {
|
|
6366
|
-
resolve: sinon.stub(),
|
|
6367
|
-
reject: sinon.stub(),
|
|
6368
|
-
};
|
|
6369
6918
|
meeting.sdpResponseTimer = '1234';
|
|
6370
|
-
meeting.mediaProperties
|
|
6919
|
+
sinon.stub(meeting.mediaProperties, 'waitForMediaConnectionConnected').resolves();
|
|
6371
6920
|
|
|
6372
|
-
eventListeners[Event.REMOTE_SDP_ANSWER_PROCESSED]();
|
|
6373
6921
|
meeting.config.reconnection.enabled = true;
|
|
6374
6922
|
meeting.currentMediaStatus = {audio: true};
|
|
6375
6923
|
meeting.reconnectionManager = new ReconnectionManager(meeting);
|
|
6376
|
-
meeting.reconnectionManager
|
|
6377
|
-
meeting.reconnectionManager.reset = sinon.stub().returns(true);
|
|
6378
|
-
meeting.reconnectionManager.cleanup = sinon.stub().returns(true);
|
|
6379
|
-
meeting.reconnectionManager.setStatus = sinon.stub();
|
|
6924
|
+
sinon.stub(meeting.reconnectionManager, 'reconnect').returns(Promise.resolve());
|
|
6380
6925
|
});
|
|
6381
6926
|
|
|
6382
6927
|
it('should throw error if media not established before trying reconnect', async () => {
|
|
@@ -6396,87 +6941,72 @@ describe('plugin-meetings', () => {
|
|
|
6396
6941
|
}
|
|
6397
6942
|
});
|
|
6398
6943
|
|
|
6399
|
-
it('should
|
|
6400
|
-
|
|
6944
|
+
it('should call the right functions', async () => {
|
|
6945
|
+
const options = {id: 'fake options'};
|
|
6946
|
+
await meeting.reconnect(options);
|
|
6947
|
+
|
|
6948
|
+
sinon.stub(meeting, 'waitForRemoteSDPAnswer').resolves();
|
|
6401
6949
|
|
|
6402
|
-
assert.calledWith(
|
|
6403
|
-
TriggerProxy.trigger,
|
|
6404
|
-
sinon.match.instanceOf(Meeting),
|
|
6405
|
-
{file: 'meeting/index', function: 'reconnect'},
|
|
6406
|
-
'meeting:reconnectionSuccess'
|
|
6407
|
-
);
|
|
6408
|
-
assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
|
|
6409
|
-
name: 'client.media.recovered',
|
|
6410
|
-
payload: {
|
|
6411
|
-
recoveredBy: 'new',
|
|
6412
|
-
},
|
|
6413
|
-
options: {
|
|
6414
|
-
meetingId: meeting.id,
|
|
6415
|
-
},
|
|
6416
|
-
});
|
|
6417
6950
|
assert.calledOnceWithExactly(
|
|
6418
|
-
meeting.reconnectionManager.
|
|
6419
|
-
|
|
6951
|
+
meeting.reconnectionManager.reconnect,
|
|
6952
|
+
options,
|
|
6953
|
+
sinon.match.any
|
|
6420
6954
|
);
|
|
6421
|
-
|
|
6955
|
+
const callback = meeting.reconnectionManager.reconnect.getCalls()[0].args[1];
|
|
6422
6956
|
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6957
|
+
// call the completion callback
|
|
6958
|
+
assert.isFunction(callback);
|
|
6959
|
+
await callback();
|
|
6960
|
+
|
|
6961
|
+
// check that the right things were called by the callback
|
|
6962
|
+
assert.calledOnceWithExactly(meeting.waitForRemoteSDPAnswer);
|
|
6963
|
+
assert.calledOnceWithExactly(meeting.mediaProperties.waitForMediaConnectionConnected);
|
|
6426
6964
|
});
|
|
6427
6965
|
});
|
|
6428
6966
|
|
|
6429
6967
|
describe('unsuccessful reconnect', () => {
|
|
6968
|
+
let logUploadSpy;
|
|
6969
|
+
|
|
6430
6970
|
beforeEach(() => {
|
|
6431
|
-
|
|
6971
|
+
logUploadSpy = sinon.spy(meeting, 'uploadLogs');
|
|
6432
6972
|
meeting.currentMediaStatus = {audio: true};
|
|
6433
6973
|
meeting.reconnectionManager = new ReconnectionManager(meeting);
|
|
6434
6974
|
meeting.reconnectionManager.reconnect = sinon
|
|
6435
6975
|
.stub()
|
|
6436
6976
|
.returns(Promise.reject(new Error()));
|
|
6437
|
-
meeting.reconnectionManager.reset = sinon.stub().returns(true);
|
|
6438
6977
|
});
|
|
6439
6978
|
|
|
6440
|
-
it('should
|
|
6979
|
+
it('should upload logs on reconnect failure', async () => {
|
|
6441
6980
|
await assert.isRejected(meeting.reconnect());
|
|
6442
6981
|
assert.calledWith(
|
|
6443
6982
|
TriggerProxy.trigger,
|
|
6444
6983
|
sinon.match.instanceOf(Meeting),
|
|
6445
6984
|
{file: 'meeting/index', function: 'reconnect'},
|
|
6446
|
-
|
|
6447
|
-
|
|
6985
|
+
EVENTS.REQUEST_UPLOAD_LOGS,
|
|
6986
|
+
sinon.match.instanceOf(Meeting)
|
|
6448
6987
|
);
|
|
6449
6988
|
});
|
|
6450
6989
|
|
|
6451
|
-
it('should
|
|
6990
|
+
it('should fail without uploading logs if there is no reconnectionManager', async () => {
|
|
6991
|
+
meeting.reconnectionManager = null;
|
|
6452
6992
|
await assert.isRejected(meeting.reconnect());
|
|
6453
|
-
assert(
|
|
6454
|
-
assert.calledWith(
|
|
6455
|
-
Metrics.sendBehavioralMetric,
|
|
6456
|
-
BEHAVIORAL_METRICS.MEETING_RECONNECT_FAILURE,
|
|
6457
|
-
{
|
|
6458
|
-
correlation_id: meeting.correlationId,
|
|
6459
|
-
locus_id: meeting.locusUrl.split('/').pop(),
|
|
6460
|
-
reason: sinon.match.any,
|
|
6461
|
-
stack: sinon.match.any,
|
|
6462
|
-
}
|
|
6463
|
-
);
|
|
6993
|
+
assert.notCalled(logUploadSpy);
|
|
6464
6994
|
});
|
|
6465
6995
|
|
|
6466
|
-
it('should
|
|
6996
|
+
it('should fail without uploading logs if there is no media established', async () => {
|
|
6997
|
+
meeting.currentMediaStatus = null;
|
|
6467
6998
|
await assert.isRejected(meeting.reconnect());
|
|
6468
|
-
assert.
|
|
6469
|
-
TriggerProxy.trigger,
|
|
6470
|
-
sinon.match.instanceOf(Meeting),
|
|
6471
|
-
{file: 'meeting/index', function: 'reconnect'},
|
|
6472
|
-
EVENTS.REQUEST_UPLOAD_LOGS,
|
|
6473
|
-
sinon.match.instanceOf(Meeting)
|
|
6474
|
-
);
|
|
6999
|
+
assert.notCalled(logUploadSpy);
|
|
6475
7000
|
});
|
|
6476
7001
|
|
|
6477
|
-
it('should
|
|
6478
|
-
|
|
6479
|
-
|
|
7002
|
+
it('should resolve if the error is ReconnectionNotStartedError', async () => {
|
|
7003
|
+
meeting.reconnectionManager.reconnect.returns(
|
|
7004
|
+
Promise.reject(new ReconnectionNotStartedError())
|
|
7005
|
+
);
|
|
7006
|
+
await meeting.reconnect();
|
|
7007
|
+
|
|
7008
|
+
// logs shouldn't be uploaded
|
|
7009
|
+
assert.notCalled(logUploadSpy);
|
|
6480
7010
|
});
|
|
6481
7011
|
});
|
|
6482
7012
|
});
|
|
@@ -7336,6 +7866,7 @@ describe('plugin-meetings', () => {
|
|
|
7336
7866
|
});
|
|
7337
7867
|
it('listens to the self admitted guest event', (done) => {
|
|
7338
7868
|
meeting.stopKeepAlive = sinon.stub();
|
|
7869
|
+
meeting.updateLLMConnection = sinon.stub();
|
|
7339
7870
|
meeting.locusInfo.emit({function: 'test', file: 'test'}, 'SELF_ADMITTED_GUEST', test1);
|
|
7340
7871
|
assert.calledOnceWithExactly(meeting.stopKeepAlive);
|
|
7341
7872
|
assert.calledThrice(TriggerProxy.trigger);
|
|
@@ -7346,6 +7877,7 @@ describe('plugin-meetings', () => {
|
|
|
7346
7877
|
'meeting:self:guestAdmitted',
|
|
7347
7878
|
{payload: test1}
|
|
7348
7879
|
);
|
|
7880
|
+
assert.calledOnce(meeting.updateLLMConnection);
|
|
7349
7881
|
done();
|
|
7350
7882
|
});
|
|
7351
7883
|
|
|
@@ -7682,6 +8214,21 @@ describe('plugin-meetings', () => {
|
|
|
7682
8214
|
EVENT_TRIGGERS.MEETING_INTERPRETATION_UPDATE
|
|
7683
8215
|
);
|
|
7684
8216
|
});
|
|
8217
|
+
|
|
8218
|
+
it('listens to the locus manual caption update event', () => {
|
|
8219
|
+
meeting.locusInfo.emit(
|
|
8220
|
+
{function: 'test', file: 'test'},
|
|
8221
|
+
'CONTROLS_MEETING_MANUAL_CAPTION_UPDATED',
|
|
8222
|
+
{enable: true}
|
|
8223
|
+
);
|
|
8224
|
+
|
|
8225
|
+
assert.calledWith(
|
|
8226
|
+
TriggerProxy.trigger,
|
|
8227
|
+
meeting,
|
|
8228
|
+
{file: 'meeting/index', function: 'setupLocusControlsListener'},
|
|
8229
|
+
EVENT_TRIGGERS.MEETING_MANUAL_CAPTION_UPDATED
|
|
8230
|
+
);
|
|
8231
|
+
});
|
|
7685
8232
|
});
|
|
7686
8233
|
|
|
7687
8234
|
describe('#setUpLocusUrlListener', () => {
|
|
@@ -8267,6 +8814,34 @@ describe('plugin-meetings', () => {
|
|
|
8267
8814
|
|
|
8268
8815
|
checkParseMeetingInfo(expectedInfoToParse);
|
|
8269
8816
|
});
|
|
8817
|
+
|
|
8818
|
+
it('should parse meeting info, set values, and return null when permissionToken is not present', () => {
|
|
8819
|
+
meeting.config.experimental = {enableMediaNegotiatedEvent: true};
|
|
8820
|
+
meeting.config.experimental.enableUnifiedMeetings = true;
|
|
8821
|
+
const FAKE_STRING_DESTINATION = 'sipUrl';
|
|
8822
|
+
const FAKE_MEETING_INFO = {
|
|
8823
|
+
conversationUrl: uuid1,
|
|
8824
|
+
locusUrl: url1,
|
|
8825
|
+
meetingJoinUrl: url2,
|
|
8826
|
+
meetingNumber: '12345',
|
|
8827
|
+
sipMeetingUri: test1,
|
|
8828
|
+
sipUrl: test1,
|
|
8829
|
+
owner: test2,
|
|
8830
|
+
};
|
|
8831
|
+
|
|
8832
|
+
meeting.parseMeetingInfo(FAKE_MEETING_INFO, FAKE_STRING_DESTINATION);
|
|
8833
|
+
const expectedInfoToParse = {
|
|
8834
|
+
conversationUrl: uuid1,
|
|
8835
|
+
locusUrl: url1,
|
|
8836
|
+
sipUri: test1,
|
|
8837
|
+
meetingNumber: '12345',
|
|
8838
|
+
meetingJoinUrl: url2,
|
|
8839
|
+
owner: test2,
|
|
8840
|
+
};
|
|
8841
|
+
|
|
8842
|
+
checkParseMeetingInfo(expectedInfoToParse);
|
|
8843
|
+
});
|
|
8844
|
+
|
|
8270
8845
|
it('should parse interpretation info correctly', () => {
|
|
8271
8846
|
const parseInterpretationInfo = sinon.spy(MeetingUtil, 'parseInterpretationInfo');
|
|
8272
8847
|
const mockToggleOnData = {
|
|
@@ -9403,9 +9978,16 @@ describe('plugin-meetings', () => {
|
|
|
9403
9978
|
it('check triggerAnnotationInfoEvent event', () => {
|
|
9404
9979
|
TriggerProxy.trigger.reset();
|
|
9405
9980
|
const annotationInfo = {version: '1', policy: 'Approval'};
|
|
9406
|
-
const expectAnnotationInfo = {
|
|
9981
|
+
const expectAnnotationInfo = {
|
|
9982
|
+
annotationInfo,
|
|
9983
|
+
meetingId: meeting.id,
|
|
9984
|
+
resourceType: 'FILE',
|
|
9985
|
+
};
|
|
9407
9986
|
meeting.webex.meetings = {};
|
|
9408
|
-
meeting.triggerAnnotationInfoEvent(
|
|
9987
|
+
meeting.triggerAnnotationInfoEvent(
|
|
9988
|
+
{annotation: annotationInfo, resourceType: 'FILE'},
|
|
9989
|
+
{}
|
|
9990
|
+
);
|
|
9409
9991
|
assert.calledWith(
|
|
9410
9992
|
TriggerProxy.trigger,
|
|
9411
9993
|
{},
|
|
@@ -9419,8 +10001,8 @@ describe('plugin-meetings', () => {
|
|
|
9419
10001
|
|
|
9420
10002
|
TriggerProxy.trigger.reset();
|
|
9421
10003
|
meeting.triggerAnnotationInfoEvent(
|
|
9422
|
-
{annotation: annotationInfo},
|
|
9423
|
-
{annotation: annotationInfo}
|
|
10004
|
+
{annotation: annotationInfo, resourceType: 'FILE'},
|
|
10005
|
+
{annotation: annotationInfo, resourceType: 'FILE'}
|
|
9424
10006
|
);
|
|
9425
10007
|
assert.notCalled(TriggerProxy.trigger);
|
|
9426
10008
|
|
|
@@ -9429,10 +10011,11 @@ describe('plugin-meetings', () => {
|
|
|
9429
10011
|
const expectAnnotationInfoUpdated = {
|
|
9430
10012
|
annotationInfo: annotationInfoUpdate,
|
|
9431
10013
|
meetingId: meeting.id,
|
|
10014
|
+
resourceType: 'FILE',
|
|
9432
10015
|
};
|
|
9433
10016
|
meeting.triggerAnnotationInfoEvent(
|
|
9434
|
-
{annotation: annotationInfoUpdate},
|
|
9435
|
-
{annotation: annotationInfo}
|
|
10017
|
+
{annotation: annotationInfoUpdate, resourceType: 'FILE'},
|
|
10018
|
+
{annotation: annotationInfo, resourceType: 'FILE'}
|
|
9436
10019
|
);
|
|
9437
10020
|
assert.calledWith(
|
|
9438
10021
|
TriggerProxy.trigger,
|
|
@@ -9446,7 +10029,10 @@ describe('plugin-meetings', () => {
|
|
|
9446
10029
|
);
|
|
9447
10030
|
|
|
9448
10031
|
TriggerProxy.trigger.reset();
|
|
9449
|
-
meeting.triggerAnnotationInfoEvent(null, {
|
|
10032
|
+
meeting.triggerAnnotationInfoEvent(null, {
|
|
10033
|
+
annotation: annotationInfoUpdate,
|
|
10034
|
+
resourceType: 'FILE',
|
|
10035
|
+
});
|
|
9450
10036
|
assert.notCalled(TriggerProxy.trigger);
|
|
9451
10037
|
});
|
|
9452
10038
|
});
|
|
@@ -9470,6 +10056,11 @@ describe('plugin-meetings', () => {
|
|
|
9470
10056
|
'https://board-a.wbx2.com/board/api/v1/channels/977a7330-54f4-11eb-b1ef-91f5eefc7bf3',
|
|
9471
10057
|
};
|
|
9472
10058
|
|
|
10059
|
+
const SHARE_TYPE = {
|
|
10060
|
+
FILE: 'FILE',
|
|
10061
|
+
DESKTOP: 'DESKTOP',
|
|
10062
|
+
};
|
|
10063
|
+
|
|
9473
10064
|
const DEVICE_URL = {
|
|
9474
10065
|
LOCAL_WEB: 'my-web-url',
|
|
9475
10066
|
LOCAL_MAC: 'my-mac-url',
|
|
@@ -9481,11 +10072,14 @@ describe('plugin-meetings', () => {
|
|
|
9481
10072
|
beneficiaryId = null,
|
|
9482
10073
|
disposition = null,
|
|
9483
10074
|
deviceUrlSharing = null,
|
|
9484
|
-
annotation = undefined
|
|
10075
|
+
annotation = undefined,
|
|
10076
|
+
resourceType = undefined
|
|
9485
10077
|
) => ({
|
|
9486
10078
|
beneficiaryId,
|
|
9487
10079
|
disposition,
|
|
9488
10080
|
deviceUrlSharing,
|
|
10081
|
+
annotation,
|
|
10082
|
+
resourceType,
|
|
9489
10083
|
});
|
|
9490
10084
|
const generateWhiteboard = (
|
|
9491
10085
|
beneficiaryId = null,
|
|
@@ -9504,7 +10098,8 @@ describe('plugin-meetings', () => {
|
|
|
9504
10098
|
annotation,
|
|
9505
10099
|
url,
|
|
9506
10100
|
shareInstanceId,
|
|
9507
|
-
deviceUrlSharing
|
|
10101
|
+
deviceUrlSharing,
|
|
10102
|
+
resourceType
|
|
9508
10103
|
) => {
|
|
9509
10104
|
const newPayload = cloneDeep(payload);
|
|
9510
10105
|
|
|
@@ -9538,7 +10133,8 @@ describe('plugin-meetings', () => {
|
|
|
9538
10133
|
beneficiaryId,
|
|
9539
10134
|
FLOOR_ACTION.GRANTED,
|
|
9540
10135
|
deviceUrlSharing,
|
|
9541
|
-
annotation
|
|
10136
|
+
annotation,
|
|
10137
|
+
resourceType
|
|
9542
10138
|
);
|
|
9543
10139
|
|
|
9544
10140
|
if (isEqual(newPayload.current, newPayload.previous)) {
|
|
@@ -9599,6 +10195,7 @@ describe('plugin-meetings', () => {
|
|
|
9599
10195
|
url,
|
|
9600
10196
|
shareInstanceId,
|
|
9601
10197
|
annotationInfo: undefined,
|
|
10198
|
+
resourceType: undefined,
|
|
9602
10199
|
},
|
|
9603
10200
|
});
|
|
9604
10201
|
}
|
|
@@ -10440,7 +11037,8 @@ describe('plugin-meetings', () => {
|
|
|
10440
11037
|
undefined,
|
|
10441
11038
|
undefined,
|
|
10442
11039
|
undefined,
|
|
10443
|
-
DEVICE_URL.REMOTE_A
|
|
11040
|
+
DEVICE_URL.REMOTE_A,
|
|
11041
|
+
undefined
|
|
10444
11042
|
);
|
|
10445
11043
|
const data2 = generateData(
|
|
10446
11044
|
data1.payload,
|
|
@@ -10453,9 +11051,39 @@ describe('plugin-meetings', () => {
|
|
|
10453
11051
|
undefined,
|
|
10454
11052
|
undefined,
|
|
10455
11053
|
undefined,
|
|
10456
|
-
DEVICE_URL.REMOTE_B
|
|
11054
|
+
DEVICE_URL.REMOTE_B,
|
|
11055
|
+
undefined
|
|
11056
|
+
);
|
|
11057
|
+
const data3 = generateData(data2.payload, false, true, USER_IDS.REMOTE_B, undefined);
|
|
11058
|
+
|
|
11059
|
+
payloadTestHelper([data1, data2, data3]);
|
|
11060
|
+
});
|
|
11061
|
+
});
|
|
11062
|
+
|
|
11063
|
+
describe('File Share --> Desktop Share', () => {
|
|
11064
|
+
it('Scenario #1: remote person A shares file then share desktop', () => {
|
|
11065
|
+
const data1 = generateData(
|
|
11066
|
+
blankPayload,
|
|
11067
|
+
true,
|
|
11068
|
+
true,
|
|
11069
|
+
USER_IDS.ME,
|
|
11070
|
+
undefined,
|
|
11071
|
+
false,
|
|
11072
|
+
undefined,
|
|
11073
|
+
undefined,
|
|
11074
|
+
undefined,
|
|
11075
|
+
undefined,
|
|
11076
|
+
DEVICE_URL.LOCAL_WEB,
|
|
11077
|
+
SHARE_TYPE.FILE
|
|
11078
|
+
);
|
|
11079
|
+
const data2 = generateData(
|
|
11080
|
+
data1.payload,
|
|
11081
|
+
true,
|
|
11082
|
+
false,
|
|
11083
|
+
USER_IDS.ME,
|
|
11084
|
+
SHARE_TYPE.DESKTOP
|
|
10457
11085
|
);
|
|
10458
|
-
const data3 = generateData(data2.payload,
|
|
11086
|
+
const data3 = generateData(data2.payload, true, true, USER_IDS.ME);
|
|
10459
11087
|
|
|
10460
11088
|
payloadTestHelper([data1, data2, data3]);
|
|
10461
11089
|
});
|