@webex/plugin-meetings 3.7.0 → 3.8.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/annotation/index.js +17 -0
- package/dist/annotation/index.js.map +1 -1
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/common/errors/join-forbidden-error.js +52 -0
- package/dist/common/errors/join-forbidden-error.js.map +1 -0
- package/dist/common/errors/{webinar-registration-error.js → join-webinar-error.js} +12 -12
- package/dist/common/errors/join-webinar-error.js.map +1 -0
- package/dist/common/errors/multistream-not-supported-error.js +53 -0
- package/dist/common/errors/multistream-not-supported-error.js.map +1 -0
- package/dist/config.js +3 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js +69 -6
- package/dist/constants.js.map +1 -1
- package/dist/index.js +16 -11
- package/dist/index.js.map +1 -1
- package/dist/interpretation/index.js +4 -4
- package/dist/interpretation/index.js.map +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +14 -3
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/selfUtils.js +35 -17
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/MediaConnectionAwaiter.js +1 -0
- package/dist/media/MediaConnectionAwaiter.js.map +1 -1
- package/dist/media/properties.js +30 -16
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/brbState.js +167 -0
- package/dist/meeting/brbState.js.map +1 -0
- package/dist/meeting/in-meeting-actions.js +13 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +1373 -1052
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +32 -11
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/meeting/muteState.js +1 -6
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +51 -29
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meeting/util.js +103 -67
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +115 -45
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meeting-info/utilv2.js +6 -2
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/index.js +107 -55
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/meetings.types.js +2 -0
- package/dist/meetings/meetings.types.js.map +1 -1
- package/dist/meetings/util.js +1 -1
- package/dist/meetings/util.js.map +1 -1
- package/dist/member/index.js +9 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/types.js.map +1 -1
- package/dist/member/util.js +39 -28
- package/dist/member/util.js.map +1 -1
- package/dist/members/util.js +4 -2
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/constants.js +6 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/multistream/remoteMedia.js +30 -15
- package/dist/multistream/remoteMedia.js.map +1 -1
- package/dist/multistream/remoteMediaManager.js +40 -8
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/multistream/sendSlotManager.js +24 -0
- package/dist/multistream/sendSlotManager.js.map +1 -1
- package/dist/reachability/clusterReachability.js +12 -15
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +471 -140
- package/dist/reachability/index.js.map +1 -1
- package/dist/{rtcMetrics/constants.js → reachability/reachability.types.js} +1 -5
- package/dist/reachability/reachability.types.js.map +1 -0
- package/dist/reachability/request.js +21 -8
- package/dist/reachability/request.js.map +1 -1
- package/dist/recording-controller/enums.js +8 -4
- package/dist/recording-controller/enums.js.map +1 -1
- package/dist/recording-controller/index.js +18 -9
- package/dist/recording-controller/index.js.map +1 -1
- package/dist/recording-controller/util.js +13 -9
- package/dist/recording-controller/util.js.map +1 -1
- package/dist/roap/index.js +15 -15
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/request.js +45 -79
- package/dist/roap/request.js.map +1 -1
- package/dist/roap/turnDiscovery.js +3 -6
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/types/annotation/index.d.ts +5 -0
- package/dist/types/common/errors/join-forbidden-error.d.ts +15 -0
- package/dist/types/common/errors/{webinar-registration-error.d.ts → join-webinar-error.d.ts} +2 -2
- package/dist/types/common/errors/multistream-not-supported-error.d.ts +17 -0
- package/dist/types/config.d.ts +2 -0
- package/dist/types/constants.d.ts +54 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/locus-info/index.d.ts +2 -1
- package/dist/types/meeting/brbState.d.ts +54 -0
- package/dist/types/meeting/in-meeting-actions.d.ts +12 -0
- package/dist/types/meeting/index.d.ts +86 -14
- package/dist/types/meeting/locusMediaRequest.d.ts +6 -3
- package/dist/types/meeting/request.d.ts +14 -3
- package/dist/types/meeting/request.type.d.ts +6 -0
- package/dist/types/meeting/util.d.ts +3 -3
- package/dist/types/meeting-info/meeting-info-v2.d.ts +30 -5
- package/dist/types/meetings/index.d.ts +20 -2
- package/dist/types/meetings/meetings.types.d.ts +8 -0
- package/dist/types/member/index.d.ts +1 -0
- package/dist/types/member/types.d.ts +7 -0
- package/dist/types/members/util.d.ts +2 -0
- package/dist/types/metrics/constants.d.ts +6 -1
- package/dist/types/multistream/remoteMediaManager.d.ts +10 -1
- package/dist/types/multistream/sendSlotManager.d.ts +8 -1
- package/dist/types/reachability/clusterReachability.d.ts +1 -10
- package/dist/types/reachability/index.d.ts +83 -36
- package/dist/types/reachability/reachability.types.d.ts +64 -0
- package/dist/types/reachability/request.d.ts +5 -1
- package/dist/types/recording-controller/enums.d.ts +5 -2
- package/dist/types/recording-controller/index.d.ts +1 -0
- package/dist/types/recording-controller/util.d.ts +2 -1
- package/dist/types/roap/request.d.ts +1 -13
- package/dist/webinar/index.js +390 -7
- package/dist/webinar/index.js.map +1 -1
- package/package.json +23 -22
- package/src/annotation/index.ts +16 -0
- package/src/common/errors/join-forbidden-error.ts +26 -0
- package/src/common/errors/join-webinar-error.ts +24 -0
- package/src/common/errors/multistream-not-supported-error.ts +30 -0
- package/src/config.ts +2 -0
- package/src/constants.ts +62 -3
- package/src/index.ts +5 -3
- package/src/interpretation/index.ts +3 -3
- package/src/locus-info/index.ts +20 -3
- package/src/locus-info/selfUtils.ts +24 -6
- package/src/media/MediaConnectionAwaiter.ts +2 -0
- package/src/media/properties.ts +34 -13
- package/src/meeting/brbState.ts +169 -0
- package/src/meeting/in-meeting-actions.ts +25 -0
- package/src/meeting/index.ts +485 -88
- package/src/meeting/locusMediaRequest.ts +38 -12
- package/src/meeting/muteState.ts +1 -6
- package/src/meeting/request.ts +30 -12
- package/src/meeting/request.type.ts +7 -0
- package/src/meeting/util.ts +32 -13
- package/src/meeting-info/meeting-info-v2.ts +83 -12
- package/src/meeting-info/utilv2.ts +17 -3
- package/src/meetings/index.ts +79 -20
- package/src/meetings/meetings.types.ts +10 -0
- package/src/meetings/util.ts +2 -1
- package/src/member/index.ts +9 -0
- package/src/member/types.ts +8 -0
- package/src/member/util.ts +34 -24
- package/src/members/util.ts +1 -0
- package/src/metrics/constants.ts +6 -1
- package/src/multistream/remoteMedia.ts +28 -15
- package/src/multistream/remoteMediaManager.ts +32 -10
- package/src/multistream/sendSlotManager.ts +31 -0
- package/src/reachability/clusterReachability.ts +5 -15
- package/src/reachability/index.ts +315 -75
- package/src/reachability/reachability.types.ts +85 -0
- package/src/reachability/request.ts +55 -31
- package/src/recording-controller/enums.ts +5 -2
- package/src/recording-controller/index.ts +17 -4
- package/src/recording-controller/util.ts +28 -9
- package/src/roap/index.ts +14 -13
- package/src/roap/request.ts +30 -44
- package/src/roap/turnDiscovery.ts +2 -4
- package/src/webinar/index.ts +235 -9
- package/test/unit/spec/annotation/index.ts +46 -1
- package/test/unit/spec/interpretation/index.ts +39 -1
- package/test/unit/spec/locus-info/index.js +292 -60
- package/test/unit/spec/locus-info/selfConstant.js +7 -0
- package/test/unit/spec/locus-info/selfUtils.js +101 -1
- package/test/unit/spec/media/properties.ts +15 -0
- package/test/unit/spec/meeting/brbState.ts +114 -0
- package/test/unit/spec/meeting/in-meeting-actions.ts +15 -1
- package/test/unit/spec/meeting/index.js +908 -124
- package/test/unit/spec/meeting/locusMediaRequest.ts +111 -66
- package/test/unit/spec/meeting/muteState.js +0 -24
- package/test/unit/spec/meeting/request.js +3 -26
- package/test/unit/spec/meeting/utils.js +73 -28
- package/test/unit/spec/meeting-info/meetinginfov2.js +46 -4
- package/test/unit/spec/meeting-info/utilv2.js +26 -0
- package/test/unit/spec/meetings/index.js +172 -18
- package/test/unit/spec/meetings/utils.js +10 -0
- package/test/unit/spec/member/util.js +52 -11
- package/test/unit/spec/members/utils.js +95 -0
- package/test/unit/spec/multistream/remoteMedia.ts +11 -7
- package/test/unit/spec/multistream/remoteMediaManager.ts +397 -118
- package/test/unit/spec/reachability/clusterReachability.ts +7 -0
- package/test/unit/spec/reachability/index.ts +391 -9
- package/test/unit/spec/reachability/request.js +48 -12
- package/test/unit/spec/recording-controller/index.js +61 -5
- package/test/unit/spec/recording-controller/util.js +39 -3
- package/test/unit/spec/roap/index.ts +48 -1
- package/test/unit/spec/roap/request.ts +51 -109
- package/test/unit/spec/roap/turnDiscovery.ts +202 -147
- package/test/unit/spec/webinar/index.ts +509 -0
- package/dist/common/errors/webinar-registration-error.js.map +0 -1
- package/dist/networkQualityMonitor/index.js +0 -227
- package/dist/networkQualityMonitor/index.js.map +0 -1
- package/dist/rtcMetrics/constants.js.map +0 -1
- package/dist/rtcMetrics/index.js +0 -197
- package/dist/rtcMetrics/index.js.map +0 -1
- package/dist/types/networkQualityMonitor/index.d.ts +0 -70
- package/dist/types/rtcMetrics/constants.d.ts +0 -4
- package/dist/types/rtcMetrics/index.d.ts +0 -71
- package/src/common/errors/webinar-registration-error.ts +0 -27
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import 'jsdom-global/register';
|
|
2
2
|
import sinon from 'sinon';
|
|
3
3
|
import {assert} from '@webex/test-helper-chai';
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import {cloneDeep} from 'lodash';
|
|
5
|
+
import {EventEmitter} from 'events';
|
|
6
6
|
import MockWebex from '@webex/test-helper-mock-webex';
|
|
7
7
|
import Meetings from '@webex/plugin-meetings';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
LocalMuteRequest,
|
|
10
|
+
LocusMediaRequest,
|
|
11
|
+
RoapRequest,
|
|
12
|
+
} from '@webex/plugin-meetings/src/meeting/locusMediaRequest';
|
|
9
13
|
import testUtils from '../../../utils/testUtils';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
14
|
+
import {Defer} from '@webex/common';
|
|
15
|
+
import {IP_VERSION} from '../../../../src/constants';
|
|
12
16
|
|
|
13
17
|
describe('LocusMediaRequest.send()', () => {
|
|
14
18
|
let locusMediaRequest: LocusMediaRequest;
|
|
@@ -16,10 +20,10 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
16
20
|
let mockWebex;
|
|
17
21
|
|
|
18
22
|
const fakeLocusResponse = {
|
|
19
|
-
locus: {
|
|
23
|
+
locus: {something: 'whatever'},
|
|
20
24
|
};
|
|
21
25
|
|
|
22
|
-
const exampleRoapRequestBody:RoapRequest = {
|
|
26
|
+
const exampleRoapRequestBody: RoapRequest = {
|
|
23
27
|
type: 'RoapMessage',
|
|
24
28
|
mediaId: 'mediaId',
|
|
25
29
|
selfUrl: 'fakeMeetingSelfUrl',
|
|
@@ -31,47 +35,67 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
31
35
|
tieBreaker: 0xfffffffe,
|
|
32
36
|
},
|
|
33
37
|
reachability: {
|
|
34
|
-
'wjfkm.wjfkm.*': {udp:{reachable: true}, tcp:{reachable:false}},
|
|
35
|
-
'1eb65fdf-9643-417f-9974-ad72cae0e10f.59268c12-7a04-4b23-a1a1-4c74be03019a.*': {
|
|
38
|
+
'wjfkm.wjfkm.*': {udp: {reachable: true}, tcp: {reachable: false}},
|
|
39
|
+
'1eb65fdf-9643-417f-9974-ad72cae0e10f.59268c12-7a04-4b23-a1a1-4c74be03019a.*': {
|
|
40
|
+
udp: {reachable: false},
|
|
41
|
+
tcp: {reachable: true},
|
|
42
|
+
},
|
|
36
43
|
},
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
clientMediaPreferences: {
|
|
45
|
+
preferTranscoding: false,
|
|
46
|
+
joinCookie: {
|
|
47
|
+
anycastEntryPoint: 'aws-eu-west-1',
|
|
48
|
+
clientIpAddress: 'some ip',
|
|
49
|
+
timeShot: '2023-05-23T08:03:49Z',
|
|
50
|
+
},
|
|
51
|
+
ipver: IP_VERSION.only_ipv4,
|
|
52
|
+
reachability: {
|
|
53
|
+
version: '1',
|
|
54
|
+
result: 'some fake reachability result',
|
|
55
|
+
},
|
|
41
56
|
},
|
|
42
|
-
ipVersion: IP_VERSION.only_ipv4,
|
|
43
57
|
};
|
|
44
58
|
|
|
45
|
-
const createExpectedRoapBody = (
|
|
59
|
+
const createExpectedRoapBody = (
|
|
60
|
+
expectedMessageType,
|
|
61
|
+
expectedMute: {audioMuted: boolean; videoMuted: boolean}
|
|
62
|
+
) => {
|
|
46
63
|
return {
|
|
47
|
-
device: {
|
|
64
|
+
device: {url: 'deviceUrl', deviceType: 'deviceType', regionCode: 'regionCode'},
|
|
48
65
|
correlationId: 'correlationId',
|
|
49
66
|
localMedias: [
|
|
50
67
|
{
|
|
51
68
|
localSdp: `{"audioMuted":${expectedMute.audioMuted},"videoMuted":${expectedMute.videoMuted},"roapMessage":{"messageType":"${expectedMessageType}","sdps":["sdp"],"version":"2","seq":1,"tieBreaker":4294967294},"reachability":{"wjfkm.wjfkm.*":{"udp":{"reachable":true},"tcp":{"reachable":false}},"1eb65fdf-9643-417f-9974-ad72cae0e10f.59268c12-7a04-4b23-a1a1-4c74be03019a.*":{"udp":{"reachable":false},"tcp":{"reachable":true}}}}`,
|
|
52
|
-
mediaId: 'mediaId'
|
|
53
|
-
}
|
|
69
|
+
mediaId: 'mediaId',
|
|
70
|
+
},
|
|
54
71
|
],
|
|
55
72
|
clientMediaPreferences: {
|
|
56
|
-
preferTranscoding:
|
|
73
|
+
preferTranscoding: false,
|
|
57
74
|
ipver: 4,
|
|
58
75
|
joinCookie: {
|
|
59
76
|
anycastEntryPoint: 'aws-eu-west-1',
|
|
60
77
|
clientIpAddress: 'some ip',
|
|
61
|
-
timeShot: '2023-05-23T08:03:49Z'
|
|
62
|
-
}
|
|
63
|
-
|
|
78
|
+
timeShot: '2023-05-23T08:03:49Z',
|
|
79
|
+
},
|
|
80
|
+
reachability: {
|
|
81
|
+
version: '1',
|
|
82
|
+
result: 'some fake reachability result',
|
|
83
|
+
},
|
|
84
|
+
},
|
|
64
85
|
};
|
|
65
86
|
};
|
|
66
87
|
|
|
67
|
-
const exampleLocalMuteRequestBody:LocalMuteRequest = {
|
|
88
|
+
const exampleLocalMuteRequestBody: LocalMuteRequest = {
|
|
68
89
|
type: 'LocalMute',
|
|
69
90
|
mediaId: 'mediaId',
|
|
70
91
|
selfUrl: 'fakeMeetingSelfUrl',
|
|
71
92
|
muteOptions: {},
|
|
72
93
|
};
|
|
73
94
|
|
|
74
|
-
const createExpectedLocalMuteBody = (
|
|
95
|
+
const createExpectedLocalMuteBody = (
|
|
96
|
+
expectedMute: {audioMuted: boolean; videoMuted: boolean},
|
|
97
|
+
sequence = undefined
|
|
98
|
+
) => {
|
|
75
99
|
const body: any = {
|
|
76
100
|
device: {
|
|
77
101
|
url: 'deviceUrl',
|
|
@@ -87,10 +111,6 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
87
111
|
localSdp: `{"audioMuted":${expectedMute.audioMuted},"videoMuted":${expectedMute.videoMuted}}`,
|
|
88
112
|
},
|
|
89
113
|
],
|
|
90
|
-
clientMediaPreferences: {
|
|
91
|
-
preferTranscoding: true,
|
|
92
|
-
ipver: undefined,
|
|
93
|
-
},
|
|
94
114
|
};
|
|
95
115
|
|
|
96
116
|
if (sequence) {
|
|
@@ -109,33 +129,37 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
109
129
|
|
|
110
130
|
mockWebex.internal = {
|
|
111
131
|
newMetrics: {
|
|
112
|
-
submitClientEvent: sinon.stub()
|
|
132
|
+
submitClientEvent: sinon.stub(),
|
|
113
133
|
},
|
|
114
134
|
};
|
|
115
135
|
|
|
116
|
-
locusMediaRequest = new LocusMediaRequest(
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
136
|
+
locusMediaRequest = new LocusMediaRequest(
|
|
137
|
+
{
|
|
138
|
+
device: {
|
|
139
|
+
url: 'deviceUrl',
|
|
140
|
+
deviceType: 'deviceType',
|
|
141
|
+
regionCode: 'regionCode',
|
|
142
|
+
},
|
|
143
|
+
correlationId: 'correlationId',
|
|
144
|
+
meetingId: 'meetingId',
|
|
145
|
+
preferTranscoding: true,
|
|
121
146
|
},
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
parent: mockWebex,
|
|
127
|
-
});
|
|
147
|
+
{
|
|
148
|
+
parent: mockWebex,
|
|
149
|
+
}
|
|
150
|
+
);
|
|
128
151
|
webexRequestStub = sinon.stub(locusMediaRequest, 'request').resolves(fakeLocusResponse);
|
|
129
|
-
})
|
|
152
|
+
});
|
|
130
153
|
|
|
131
|
-
const sendLocalMute = (muteOptions, overrides={}) =>
|
|
154
|
+
const sendLocalMute = (muteOptions, overrides = {}) =>
|
|
155
|
+
locusMediaRequest.send({...exampleLocalMuteRequestBody, ...overrides, muteOptions});
|
|
132
156
|
|
|
133
157
|
const sendRoapMessage = (messageType) => {
|
|
134
158
|
const request = cloneDeep(exampleRoapRequestBody);
|
|
135
159
|
|
|
136
160
|
request.roapMessage.messageType = messageType;
|
|
137
161
|
return locusMediaRequest.send(request);
|
|
138
|
-
}
|
|
162
|
+
};
|
|
139
163
|
|
|
140
164
|
/** Helper function that makes sure the LocusMediaRequest.confluenceState is 'created' */
|
|
141
165
|
const ensureConfluenceCreated = async () => {
|
|
@@ -143,7 +167,7 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
143
167
|
|
|
144
168
|
webexRequestStub.resetHistory();
|
|
145
169
|
mockWebex.internal.newMetrics.submitClientEvent.resetHistory();
|
|
146
|
-
}
|
|
170
|
+
};
|
|
147
171
|
|
|
148
172
|
const checkMetrics = (expectedMetrics: boolean = true) => {
|
|
149
173
|
if (expectedMetrics) {
|
|
@@ -163,7 +187,7 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
163
187
|
} else {
|
|
164
188
|
assert.notCalled(mockWebex.internal.newMetrics.submitClientEvent);
|
|
165
189
|
}
|
|
166
|
-
}
|
|
190
|
+
};
|
|
167
191
|
|
|
168
192
|
it('sends a roap message', async () => {
|
|
169
193
|
const result = await sendRoapMessage('OFFER');
|
|
@@ -174,6 +198,8 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
174
198
|
method: 'PUT',
|
|
175
199
|
uri: 'fakeMeetingSelfUrl/media',
|
|
176
200
|
body: createExpectedRoapBody('OFFER', {audioMuted: true, videoMuted: true}),
|
|
201
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
|
202
|
+
download: sinon.match.instanceOf(EventEmitter),
|
|
177
203
|
});
|
|
178
204
|
|
|
179
205
|
checkMetrics();
|
|
@@ -203,6 +229,8 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
203
229
|
method: 'PUT',
|
|
204
230
|
uri: 'fakeMeetingSelfUrl/media',
|
|
205
231
|
body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: false}),
|
|
232
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
|
233
|
+
download: sinon.match.instanceOf(EventEmitter),
|
|
206
234
|
});
|
|
207
235
|
|
|
208
236
|
checkMetrics(false);
|
|
@@ -221,6 +249,8 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
221
249
|
method: 'PUT',
|
|
222
250
|
uri: 'fakeMeetingSelfUrl/media',
|
|
223
251
|
body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: false}, sequence),
|
|
252
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
|
253
|
+
download: sinon.match.instanceOf(EventEmitter),
|
|
224
254
|
});
|
|
225
255
|
});
|
|
226
256
|
|
|
@@ -230,15 +260,13 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
230
260
|
let result1;
|
|
231
261
|
let result2;
|
|
232
262
|
|
|
233
|
-
const promise1 = sendLocalMute({audioMuted: true, videoMuted: false})
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
});
|
|
263
|
+
const promise1 = sendLocalMute({audioMuted: true, videoMuted: false}).then((result) => {
|
|
264
|
+
result1 = result;
|
|
265
|
+
});
|
|
237
266
|
|
|
238
|
-
const promise2 = sendLocalMute({audioMuted: false, videoMuted: true})
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
});
|
|
267
|
+
const promise2 = sendLocalMute({audioMuted: false, videoMuted: true}).then((result) => {
|
|
268
|
+
result2 = result;
|
|
269
|
+
});
|
|
242
270
|
|
|
243
271
|
await testUtils.flushPromises();
|
|
244
272
|
|
|
@@ -251,6 +279,8 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
251
279
|
method: 'PUT',
|
|
252
280
|
uri: 'fakeMeetingSelfUrl/media',
|
|
253
281
|
body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: true}),
|
|
282
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
|
283
|
+
download: sinon.match.instanceOf(EventEmitter),
|
|
254
284
|
});
|
|
255
285
|
|
|
256
286
|
checkMetrics(false);
|
|
@@ -270,6 +300,8 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
270
300
|
method: 'PUT',
|
|
271
301
|
uri: 'fakeMeetingSelfUrl/media',
|
|
272
302
|
body: createExpectedLocalMuteBody({audioMuted: true, videoMuted: false}),
|
|
303
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
|
304
|
+
download: sinon.match.instanceOf(EventEmitter),
|
|
273
305
|
});
|
|
274
306
|
|
|
275
307
|
checkMetrics(false);
|
|
@@ -289,6 +321,8 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
289
321
|
method: 'PUT',
|
|
290
322
|
uri: 'fakeMeetingSelfUrl/media',
|
|
291
323
|
body: createExpectedRoapBody('OFFER', {audioMuted: true, videoMuted: false}),
|
|
324
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
|
325
|
+
download: sinon.match.instanceOf(EventEmitter),
|
|
292
326
|
});
|
|
293
327
|
|
|
294
328
|
checkMetrics();
|
|
@@ -321,10 +355,10 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
321
355
|
* after the processing cycle from which it was called is finished.
|
|
322
356
|
* This helper function waits for this to happen - it's needed, because we're using
|
|
323
357
|
* fake timers in these tests
|
|
324
|
-
|
|
358
|
+
*/
|
|
325
359
|
const ensureQueueProcessingIsStarted = () => {
|
|
326
360
|
clock.tick(1);
|
|
327
|
-
}
|
|
361
|
+
};
|
|
328
362
|
it('queues requests if there is one already in progress', async () => {
|
|
329
363
|
results.push(sendRoapMessage('OFFER'));
|
|
330
364
|
|
|
@@ -335,6 +369,8 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
335
369
|
method: 'PUT',
|
|
336
370
|
uri: 'fakeMeetingSelfUrl/media',
|
|
337
371
|
body: createExpectedRoapBody('OFFER', {audioMuted: true, videoMuted: true}),
|
|
372
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
|
373
|
+
download: sinon.match.instanceOf(EventEmitter),
|
|
338
374
|
});
|
|
339
375
|
|
|
340
376
|
webexRequestStub.resetHistory();
|
|
@@ -357,6 +393,8 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
357
393
|
method: 'PUT',
|
|
358
394
|
uri: 'fakeMeetingSelfUrl/media',
|
|
359
395
|
body: createExpectedRoapBody('OK', {audioMuted: true, videoMuted: true}),
|
|
396
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
|
397
|
+
download: sinon.match.instanceOf(EventEmitter),
|
|
360
398
|
});
|
|
361
399
|
|
|
362
400
|
// promise returned by the first call to send OFFER should be resolved by now
|
|
@@ -379,6 +417,8 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
379
417
|
method: 'PUT',
|
|
380
418
|
uri: 'fakeMeetingSelfUrl/media',
|
|
381
419
|
body: createExpectedRoapBody('OFFER', {audioMuted: false, videoMuted: false}),
|
|
420
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
|
421
|
+
download: sinon.match.instanceOf(EventEmitter),
|
|
382
422
|
});
|
|
383
423
|
|
|
384
424
|
webexRequestStub.resetHistory();
|
|
@@ -403,6 +443,8 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
403
443
|
method: 'PUT',
|
|
404
444
|
uri: 'fakeMeetingSelfUrl/media',
|
|
405
445
|
body: createExpectedRoapBody('OK', {audioMuted: false, videoMuted: true}),
|
|
446
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
|
447
|
+
download: sinon.match.instanceOf(EventEmitter),
|
|
406
448
|
});
|
|
407
449
|
|
|
408
450
|
// promise returned by the first call to send OFFER should be resolved by now
|
|
@@ -431,16 +473,17 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
431
473
|
|
|
432
474
|
ensureQueueProcessingIsStarted();
|
|
433
475
|
|
|
434
|
-
sendLocalMute({audioMuted: false, videoMuted: true})
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
});
|
|
476
|
+
sendLocalMute({audioMuted: false, videoMuted: true}).then((response) => {
|
|
477
|
+
result = response;
|
|
478
|
+
});
|
|
438
479
|
|
|
439
480
|
// only roap offer should have been sent so far
|
|
440
481
|
assert.calledOnceWithExactly(webexRequestStub, {
|
|
441
482
|
method: 'PUT',
|
|
442
483
|
uri: 'fakeMeetingSelfUrl/media',
|
|
443
484
|
body: createExpectedRoapBody('OFFER', {audioMuted: true, videoMuted: true}),
|
|
485
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
|
486
|
+
download: sinon.match.instanceOf(EventEmitter),
|
|
444
487
|
});
|
|
445
488
|
assert.equal(result, undefined); // sendLocalMute shouldn't resolve yet, as the request should be queued
|
|
446
489
|
assert.equal(locusMediaRequest.isConfluenceCreated(), false);
|
|
@@ -457,10 +500,12 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
457
500
|
method: 'PUT',
|
|
458
501
|
uri: 'fakeMeetingSelfUrl/media',
|
|
459
502
|
body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: true}),
|
|
503
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
|
504
|
+
download: sinon.match.instanceOf(EventEmitter),
|
|
460
505
|
});
|
|
461
506
|
|
|
462
507
|
// check also the result once Locus replies to local mute
|
|
463
|
-
const fakeLocusResponse = {
|
|
508
|
+
const fakeLocusResponse = {response: 'ok'};
|
|
464
509
|
requestsToLocus[1].resolve(fakeLocusResponse);
|
|
465
510
|
await testUtils.flushPromises();
|
|
466
511
|
assert.deepEqual(result, fakeLocusResponse);
|
|
@@ -480,10 +525,9 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
480
525
|
assert.equal(locusMediaRequest.isConfluenceCreated(), true);
|
|
481
526
|
|
|
482
527
|
// now send local mute
|
|
483
|
-
sendLocalMute({audioMuted: false, videoMuted: true})
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
});
|
|
528
|
+
sendLocalMute({audioMuted: false, videoMuted: true}).then((response) => {
|
|
529
|
+
result = response;
|
|
530
|
+
});
|
|
487
531
|
|
|
488
532
|
ensureQueueProcessingIsStarted();
|
|
489
533
|
|
|
@@ -492,8 +536,9 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
492
536
|
method: 'PUT',
|
|
493
537
|
uri: 'fakeMeetingSelfUrl/media',
|
|
494
538
|
body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: true}),
|
|
539
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
|
540
|
+
download: sinon.match.instanceOf(EventEmitter),
|
|
495
541
|
});
|
|
496
542
|
});
|
|
497
|
-
|
|
498
543
|
});
|
|
499
|
-
})
|
|
544
|
+
});
|
|
@@ -113,30 +113,6 @@ describe('plugin-meetings', () => {
|
|
|
113
113
|
assert.isTrue(audio.isRemotelyMuted());
|
|
114
114
|
});
|
|
115
115
|
|
|
116
|
-
it('does not locally unmute on a server unmute', async () => {
|
|
117
|
-
const setServerMutedSpy = meeting.mediaProperties.audioStream.setServerMuted;
|
|
118
|
-
|
|
119
|
-
// simulate remote mute
|
|
120
|
-
audio.handleServerRemoteMuteUpdate(meeting, true, true);
|
|
121
|
-
|
|
122
|
-
assert.isTrue(audio.isRemotelyMuted());
|
|
123
|
-
assert.isTrue(audio.isLocallyMuted());
|
|
124
|
-
|
|
125
|
-
// mutes local
|
|
126
|
-
assert.calledOnceWithExactly(setServerMutedSpy, true, 'remotelyMuted');
|
|
127
|
-
|
|
128
|
-
setServerMutedSpy.resetHistory();
|
|
129
|
-
|
|
130
|
-
// simulate remote unmute
|
|
131
|
-
audio.handleServerRemoteMuteUpdate(meeting, false, true);
|
|
132
|
-
|
|
133
|
-
assert.isFalse(audio.isRemotelyMuted());
|
|
134
|
-
assert.isTrue(audio.isLocallyMuted());
|
|
135
|
-
|
|
136
|
-
// does not unmute local
|
|
137
|
-
assert.notCalled(setServerMutedSpy);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
116
|
it('does local audio unmute if localAudioUnmuteRequired is received', async () => {
|
|
141
117
|
// first we need to have the local stream user muted
|
|
142
118
|
meeting.mediaProperties.audioStream.userMuted = true;
|
|
@@ -196,6 +196,7 @@ describe('plugin-meetings', () => {
|
|
|
196
196
|
const permissionToken = 'permission-token';
|
|
197
197
|
const installationId = 'installationId';
|
|
198
198
|
const reachability = 'reachability';
|
|
199
|
+
const clientMediaPreferences = 'clientMediaPreferences';
|
|
199
200
|
|
|
200
201
|
await meetingsRequest.joinMeeting({
|
|
201
202
|
locusUrl,
|
|
@@ -204,6 +205,7 @@ describe('plugin-meetings', () => {
|
|
|
204
205
|
roapMessage,
|
|
205
206
|
reachability,
|
|
206
207
|
permissionToken,
|
|
208
|
+
clientMediaPreferences
|
|
207
209
|
});
|
|
208
210
|
const requestParams = meetingsRequest.request.getCall(0).args[0];
|
|
209
211
|
|
|
@@ -214,6 +216,7 @@ describe('plugin-meetings', () => {
|
|
|
214
216
|
assert.equal(requestParams.body.device.countryCode, 'US');
|
|
215
217
|
assert.equal(requestParams.body.permissionToken, 'permission-token');
|
|
216
218
|
assert.equal(requestParams.body.device.regionCode, 'WEST-COAST');
|
|
219
|
+
assert.equal(requestParams.body.clientMediaPreferences, 'clientMediaPreferences');
|
|
217
220
|
assert.include(requestParams.body.device.localIp, '127.0.0');
|
|
218
221
|
assert.deepEqual(requestParams.body.localMedias, [
|
|
219
222
|
{localSdp: '{"roapMessage":"roap-message","reachability":"reachability"}'},
|
|
@@ -386,32 +389,6 @@ describe('plugin-meetings', () => {
|
|
|
386
389
|
|
|
387
390
|
assert.deepEqual(requestParams.body.alias, undefined);
|
|
388
391
|
});
|
|
389
|
-
|
|
390
|
-
it('includes joinCookie and ipver correctly', async () => {
|
|
391
|
-
const locusUrl = 'locusURL';
|
|
392
|
-
const deviceUrl = 'deviceUrl';
|
|
393
|
-
const correlationId = 'random-uuid';
|
|
394
|
-
const roapMessage = 'roap-message';
|
|
395
|
-
const permissionToken = 'permission-token';
|
|
396
|
-
|
|
397
|
-
await meetingsRequest.joinMeeting({
|
|
398
|
-
locusUrl,
|
|
399
|
-
deviceUrl,
|
|
400
|
-
correlationId,
|
|
401
|
-
roapMessage,
|
|
402
|
-
permissionToken,
|
|
403
|
-
ipVersion: IP_VERSION.ipv4_and_ipv6,
|
|
404
|
-
});
|
|
405
|
-
const requestParams = meetingsRequest.request.getCall(0).args[0];
|
|
406
|
-
|
|
407
|
-
assert.equal(requestParams.method, 'POST');
|
|
408
|
-
assert.equal(requestParams.uri, `${locusUrl}/participant?alternateRedirect=true`);
|
|
409
|
-
assert.deepEqual(requestParams.body.clientMediaPreferences, {
|
|
410
|
-
joinCookie: {anycastEntryPoint: 'aws-eu-west-1'},
|
|
411
|
-
preferTranscoding: true,
|
|
412
|
-
ipver: 1,
|
|
413
|
-
});
|
|
414
|
-
});
|
|
415
392
|
});
|
|
416
393
|
|
|
417
394
|
describe('#pstn', () => {
|
|
@@ -22,6 +22,12 @@ describe('plugin-meetings', () => {
|
|
|
22
22
|
meetings: Meetings,
|
|
23
23
|
},
|
|
24
24
|
});
|
|
25
|
+
|
|
26
|
+
webex.meetings.reachability = {
|
|
27
|
+
getReachabilityReportToAttachToRoap: sinon.stub().resolves({}),
|
|
28
|
+
getClientMediaPreferences: sinon.stub().resolves({}),
|
|
29
|
+
};
|
|
30
|
+
|
|
25
31
|
const logger = {
|
|
26
32
|
info: sandbox.stub(),
|
|
27
33
|
log: sandbox.stub(),
|
|
@@ -39,6 +45,7 @@ describe('plugin-meetings', () => {
|
|
|
39
45
|
meeting.cleanupLocalStreams = sinon.stub().returns(Promise.resolve());
|
|
40
46
|
meeting.closeRemoteStreams = sinon.stub().returns(Promise.resolve());
|
|
41
47
|
meeting.closePeerConnections = sinon.stub().returns(Promise.resolve());
|
|
48
|
+
meeting.stopPeriodicLogUpload = sinon.stub();
|
|
42
49
|
|
|
43
50
|
meeting.unsetRemoteStreams = sinon.stub();
|
|
44
51
|
meeting.unsetPeerConnections = sinon.stub();
|
|
@@ -64,6 +71,7 @@ describe('plugin-meetings', () => {
|
|
|
64
71
|
assert.calledOnce(meeting.cleanupLocalStreams);
|
|
65
72
|
assert.calledOnce(meeting.closeRemoteStreams);
|
|
66
73
|
assert.calledOnce(meeting.closePeerConnections);
|
|
74
|
+
assert.calledOnce(meeting.stopPeriodicLogUpload);
|
|
67
75
|
|
|
68
76
|
assert.calledOnce(meeting.unsetRemoteStreams);
|
|
69
77
|
assert.calledOnce(meeting.unsetPeerConnections);
|
|
@@ -157,21 +165,6 @@ describe('plugin-meetings', () => {
|
|
|
157
165
|
assert(LoggerProxy.logger.log.called, 'log called');
|
|
158
166
|
});
|
|
159
167
|
});
|
|
160
|
-
|
|
161
|
-
describe('#handleDeviceLogging', () => {
|
|
162
|
-
it('should not log if called without devices', () => {
|
|
163
|
-
MeetingUtil.handleDeviceLogging();
|
|
164
|
-
assert(!LoggerProxy.logger.log.called, 'log not called');
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it('should log device settings', () => {
|
|
168
|
-
const mockDevices = [{deviceId: 'device-1'}, {deviceId: 'device-2'}];
|
|
169
|
-
|
|
170
|
-
assert(MeetingUtil.handleDeviceLogging, 'is defined');
|
|
171
|
-
MeetingUtil.handleDeviceLogging(mockDevices);
|
|
172
|
-
assert(LoggerProxy.logger.log.called, 'log called');
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
168
|
});
|
|
176
169
|
|
|
177
170
|
describe('addSequence', () => {
|
|
@@ -408,17 +401,39 @@ describe('plugin-meetings', () => {
|
|
|
408
401
|
});
|
|
409
402
|
|
|
410
403
|
it('#Should call `meetingRequest.joinMeeting', async () => {
|
|
404
|
+
meeting.isMultistream = true;
|
|
405
|
+
|
|
406
|
+
const FAKE_REACHABILITY_REPORT = {
|
|
407
|
+
id: 'fake reachability report',
|
|
408
|
+
};
|
|
409
|
+
const FAKE_CLIENT_MEDIA_PREFERENCES = {
|
|
410
|
+
id: 'fake client media preferences',
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
webex.meetings.reachability.getReachabilityReportToAttachToRoap.resolves(FAKE_REACHABILITY_REPORT);
|
|
414
|
+
webex.meetings.reachability.getClientMediaPreferences.resolves(FAKE_CLIENT_MEDIA_PREFERENCES);
|
|
415
|
+
|
|
416
|
+
sinon
|
|
417
|
+
.stub(webex.internal.device.ipNetworkDetector, 'supportsIpV4')
|
|
418
|
+
.get(() => true);
|
|
419
|
+
sinon
|
|
420
|
+
.stub(webex.internal.device.ipNetworkDetector, 'supportsIpV6')
|
|
421
|
+
.get(() => true);
|
|
422
|
+
|
|
411
423
|
await MeetingUtil.joinMeeting(meeting, {
|
|
412
424
|
reachability: 'reachability',
|
|
413
425
|
roapMessage: 'roapMessage',
|
|
414
426
|
});
|
|
415
427
|
|
|
428
|
+
assert.calledOnceWithExactly(webex.meetings.reachability.getReachabilityReportToAttachToRoap);
|
|
429
|
+
assert.calledOnceWithExactly(webex.meetings.reachability.getClientMediaPreferences, meeting.isMultistream, IP_VERSION.ipv4_and_ipv6);
|
|
430
|
+
|
|
416
431
|
assert.calledOnce(meeting.meetingRequest.joinMeeting);
|
|
417
432
|
const parameter = meeting.meetingRequest.joinMeeting.getCall(0).args[0];
|
|
418
433
|
|
|
419
434
|
assert.equal(parameter.inviteeAddress, 'meetingJoinUrl');
|
|
420
|
-
assert.equal(parameter.
|
|
421
|
-
assert.equal(parameter.
|
|
435
|
+
assert.equal(parameter.reachability, FAKE_REACHABILITY_REPORT);
|
|
436
|
+
assert.equal(parameter.clientMediaPreferences, FAKE_CLIENT_MEDIA_PREFERENCES);
|
|
422
437
|
assert.equal(parameter.roapMessage, 'roapMessage');
|
|
423
438
|
|
|
424
439
|
assert.calledOnce(meeting.setLocus)
|
|
@@ -445,6 +460,40 @@ describe('plugin-meetings', () => {
|
|
|
445
460
|
});
|
|
446
461
|
});
|
|
447
462
|
|
|
463
|
+
it('should handle failed reachability report retrieval', async () => {
|
|
464
|
+
webex.meetings.reachability.getReachabilityReportToAttachToRoap.rejects(
|
|
465
|
+
new Error('fake error')
|
|
466
|
+
);
|
|
467
|
+
await MeetingUtil.joinMeeting(meeting, {});
|
|
468
|
+
// Verify meeting join still proceeds
|
|
469
|
+
assert.calledOnce(meeting.meetingRequest.joinMeeting);
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
it('should not attach reachability if there is no roap message', async () => {
|
|
473
|
+
await MeetingUtil.joinMeeting(meeting, {});
|
|
474
|
+
|
|
475
|
+
assert.notCalled(webex.meetings.reachability.getReachabilityReportToAttachToRoap);
|
|
476
|
+
assert.calledOnce(meeting.meetingRequest.joinMeeting);
|
|
477
|
+
|
|
478
|
+
const parameter = meeting.meetingRequest.joinMeeting.getCall(0).args[0];
|
|
479
|
+
assert.isUndefined(parameter.reachability);
|
|
480
|
+
assert.isUndefined(parameter.roapMessage);
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it('should handle failed clientMediaPreferences retrieval', async () => {
|
|
484
|
+
webex.meetings.reachability.getClientMediaPreferences.rejects(new Error('fake error'));
|
|
485
|
+
meeting.isMultistream = true;
|
|
486
|
+
await MeetingUtil.joinMeeting(meeting, {});
|
|
487
|
+
// Verify meeting join still proceeds
|
|
488
|
+
assert.calledOnce(meeting.meetingRequest.joinMeeting);
|
|
489
|
+
const parameter = meeting.meetingRequest.joinMeeting.getCall(0).args[0];
|
|
490
|
+
assert.deepEqual(parameter.clientMediaPreferences, {
|
|
491
|
+
preferTranscoding: false,
|
|
492
|
+
ipver: 0,
|
|
493
|
+
joinCookie: undefined,
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
|
|
448
497
|
it('#Should call meetingRequest.joinMeeting with breakoutsSupported=true when passed in as true', async () => {
|
|
449
498
|
await MeetingUtil.joinMeeting(meeting, {
|
|
450
499
|
breakoutsSupported: true,
|
|
@@ -480,17 +529,6 @@ describe('plugin-meetings', () => {
|
|
|
480
529
|
assert.deepEqual(parameter.deviceCapabilities, ['TEST']);
|
|
481
530
|
});
|
|
482
531
|
|
|
483
|
-
it('#Should call meetingRequest.joinMeeting with preferTranscoding=false when multistream is enabled', async () => {
|
|
484
|
-
meeting.isMultistream = true;
|
|
485
|
-
await MeetingUtil.joinMeeting(meeting, {});
|
|
486
|
-
|
|
487
|
-
assert.calledOnce(meeting.meetingRequest.joinMeeting);
|
|
488
|
-
const parameter = meeting.meetingRequest.joinMeeting.getCall(0).args[0];
|
|
489
|
-
|
|
490
|
-
assert.equal(parameter.inviteeAddress, 'meetingJoinUrl');
|
|
491
|
-
assert.equal(parameter.preferTranscoding, false);
|
|
492
|
-
});
|
|
493
|
-
|
|
494
532
|
it('#Should fallback sipUrl if meetingJoinUrl does not exists', async () => {
|
|
495
533
|
meeting.meetingJoinUrl = undefined;
|
|
496
534
|
meeting.sipUri = 'sipUri';
|
|
@@ -733,6 +771,13 @@ describe('plugin-meetings', () => {
|
|
|
733
771
|
});
|
|
734
772
|
});
|
|
735
773
|
|
|
774
|
+
describe('canStartBreakout', () => {
|
|
775
|
+
it('works as expected', () => {
|
|
776
|
+
assert.deepEqual(MeetingUtil.canStartBreakout(['DISABLE_BREAKOUT_START']), false);
|
|
777
|
+
assert.deepEqual(MeetingUtil.canStartBreakout([]), true);
|
|
778
|
+
});
|
|
779
|
+
});
|
|
780
|
+
|
|
736
781
|
describe('canBroadcastMessageToBreakout', () => {
|
|
737
782
|
it('works as expected', () => {
|
|
738
783
|
assert.deepEqual(
|