@webex/plugin-meetings 3.7.0-next.2 → 3.7.0-next.20
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/{webinar-registration-error.js → join-webinar-error.js} +12 -12
- package/dist/common/errors/join-webinar-error.js.map +1 -0
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js +27 -6
- package/dist/constants.js.map +1 -1
- package/dist/index.js +8 -15
- package/dist/index.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/meeting/in-meeting-actions.js +11 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +84 -131
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/util.js +0 -8
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +29 -17
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meetings/index.js +6 -3
- package/dist/meetings/index.js.map +1 -1
- package/dist/members/util.js +4 -2
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/constants.js +3 -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/reachability/clusterReachability.js +12 -11
- package/dist/reachability/clusterReachability.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/types/common/errors/{webinar-registration-error.d.ts → join-webinar-error.d.ts} +2 -2
- package/dist/types/constants.d.ts +19 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/meeting/in-meeting-actions.d.ts +10 -0
- package/dist/types/meeting/index.d.ts +1 -10
- package/dist/types/meeting/util.d.ts +0 -1
- package/dist/types/meeting-info/meeting-info-v2.d.ts +4 -4
- package/dist/types/meetings/index.d.ts +3 -0
- package/dist/types/members/util.d.ts +2 -0
- package/dist/types/metrics/constants.d.ts +3 -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/webinar/index.js +357 -7
- package/dist/webinar/index.js.map +1 -1
- package/package.json +22 -22
- package/src/common/errors/join-webinar-error.ts +24 -0
- package/src/config.ts +1 -1
- package/src/constants.ts +24 -3
- package/src/index.ts +2 -3
- package/src/meeting/in-meeting-actions.ts +21 -0
- package/src/meeting/index.ts +54 -48
- package/src/meeting/util.ts +0 -9
- package/src/meeting-info/meeting-info-v2.ts +23 -11
- package/src/meetings/index.ts +8 -2
- package/src/members/util.ts +1 -0
- package/src/metrics/constants.ts +3 -1
- package/src/multistream/remoteMedia.ts +28 -15
- package/src/reachability/clusterReachability.ts +4 -1
- package/src/recording-controller/enums.ts +5 -2
- package/src/recording-controller/index.ts +17 -4
- package/src/recording-controller/util.ts +20 -5
- package/src/webinar/index.ts +201 -9
- package/test/unit/spec/meeting/in-meeting-actions.ts +13 -1
- package/test/unit/spec/meeting/index.js +106 -77
- package/test/unit/spec/meeting/utils.js +0 -15
- package/test/unit/spec/meeting-info/meetinginfov2.js +9 -4
- package/test/unit/spec/meetings/index.js +9 -5
- package/test/unit/spec/members/utils.js +95 -0
- package/test/unit/spec/multistream/remoteMedia.ts +11 -7
- package/test/unit/spec/reachability/clusterReachability.ts +7 -0
- package/test/unit/spec/recording-controller/index.js +61 -5
- package/test/unit/spec/recording-controller/util.js +39 -3
- package/test/unit/spec/webinar/index.ts +363 -0
- package/dist/common/errors/webinar-registration-error.js.map +0 -1
- package/src/common/errors/webinar-registration-error.ts +0 -27
package/src/webinar/index.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* Copyright (c) 2015-2023 Cisco Systems, Inc. See LICENSE file.
|
|
3
3
|
*/
|
|
4
|
-
import {WebexPlugin} from '@webex/webex-core';
|
|
4
|
+
import {WebexPlugin, config} from '@webex/webex-core';
|
|
5
|
+
import uuid from 'uuid';
|
|
5
6
|
import {get} from 'lodash';
|
|
6
|
-
import {MEETINGS, SELF_ROLES} from '../constants';
|
|
7
|
+
import {HEADERS, HTTP_VERBS, MEETINGS, SELF_ROLES} from '../constants';
|
|
7
8
|
|
|
8
9
|
import WebinarCollection from './collection';
|
|
10
|
+
import LoggerProxy from '../common/logs/logger-proxy';
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* @class Webinar
|
|
@@ -22,6 +24,7 @@ const Webinar = WebexPlugin.extend({
|
|
|
22
24
|
canManageWebcast: 'boolean', // appears the ability to manage webcast
|
|
23
25
|
selfIsPanelist: 'boolean', // self is panelist
|
|
24
26
|
selfIsAttendee: 'boolean', // self is attendee
|
|
27
|
+
practiceSessionEnabled: 'boolean', // practice session enabled
|
|
25
28
|
},
|
|
26
29
|
|
|
27
30
|
/**
|
|
@@ -59,18 +62,207 @@ const Webinar = WebexPlugin.extend({
|
|
|
59
62
|
* @returns {{isPromoted: boolean, isDemoted: boolean}} Role transition states
|
|
60
63
|
*/
|
|
61
64
|
updateRoleChanged(payload) {
|
|
65
|
+
const oldRoles = get(payload, 'oldRoles', []);
|
|
66
|
+
const newRoles = get(payload, 'newRoles', []);
|
|
67
|
+
|
|
62
68
|
const isPromoted =
|
|
63
|
-
|
|
64
|
-
get(payload, 'newRoles', []).includes(SELF_ROLES.PANELIST);
|
|
69
|
+
oldRoles.includes(SELF_ROLES.ATTENDEE) && newRoles.includes(SELF_ROLES.PANELIST);
|
|
65
70
|
const isDemoted =
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
this.set('
|
|
69
|
-
this.
|
|
70
|
-
this.updateCanManageWebcast(get(payload, 'newRoles', []).includes(SELF_ROLES.MODERATOR));
|
|
71
|
+
oldRoles.includes(SELF_ROLES.PANELIST) && newRoles.includes(SELF_ROLES.ATTENDEE);
|
|
72
|
+
this.set('selfIsPanelist', newRoles.includes(SELF_ROLES.PANELIST));
|
|
73
|
+
this.set('selfIsAttendee', newRoles.includes(SELF_ROLES.ATTENDEE));
|
|
74
|
+
this.updateCanManageWebcast(newRoles.includes(SELF_ROLES.MODERATOR));
|
|
71
75
|
|
|
72
76
|
return {isPromoted, isDemoted};
|
|
73
77
|
},
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* start or stop practice session for webinar
|
|
81
|
+
* @param {boolean} enabled
|
|
82
|
+
* @returns {Promise}
|
|
83
|
+
*/
|
|
84
|
+
setPracticeSessionState(enabled) {
|
|
85
|
+
return this.request({
|
|
86
|
+
method: HTTP_VERBS.PATCH,
|
|
87
|
+
uri: `${this.locusUrl}/controls`,
|
|
88
|
+
body: {
|
|
89
|
+
practiceSession: {
|
|
90
|
+
enabled,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
}).catch((error) => {
|
|
94
|
+
LoggerProxy.logger.error('Meeting:webinar#setPracticeSessionState failed', error);
|
|
95
|
+
throw error;
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* update practice session status
|
|
101
|
+
* @param {object} payload
|
|
102
|
+
* @returns {void}
|
|
103
|
+
*/
|
|
104
|
+
updatePracticeSessionStatus(payload) {
|
|
105
|
+
this.set('practiceSessionEnabled', payload.enabled);
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* start webcast mode for webinar
|
|
110
|
+
* @param {object} meeting
|
|
111
|
+
* @param {object} layout
|
|
112
|
+
* @returns {Promise}
|
|
113
|
+
*/
|
|
114
|
+
async startWebcast(meeting, layout) {
|
|
115
|
+
if (!meeting) {
|
|
116
|
+
LoggerProxy.logger.error(
|
|
117
|
+
`Meeting:webinar#startWebcast failed --> meeting parameter : ${meeting}`
|
|
118
|
+
);
|
|
119
|
+
throw new Error('Meeting parameter does not meet expectations');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return this.request({
|
|
123
|
+
method: HTTP_VERBS.PUT,
|
|
124
|
+
uri: `${this.webcastInstanceUrl}/streaming`,
|
|
125
|
+
headers: {
|
|
126
|
+
authorization: await this.webex.credentials.getUserToken(),
|
|
127
|
+
trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,
|
|
128
|
+
[HEADERS.CONTENT_TYPE]: HEADERS.CONTENT_TYPE_VALUE.APPLICATION_JSON,
|
|
129
|
+
},
|
|
130
|
+
body: {
|
|
131
|
+
action: 'start',
|
|
132
|
+
meetingInfo: {
|
|
133
|
+
locusId: meeting.locusId,
|
|
134
|
+
correlationId: meeting.correlationId,
|
|
135
|
+
},
|
|
136
|
+
layout,
|
|
137
|
+
},
|
|
138
|
+
}).catch((error) => {
|
|
139
|
+
LoggerProxy.logger.error('Meeting:webinar#startWebcast failed', error);
|
|
140
|
+
throw error;
|
|
141
|
+
});
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* stop webcast mode for webinar
|
|
146
|
+
* @returns {Promise}
|
|
147
|
+
*/
|
|
148
|
+
async stopWebcast() {
|
|
149
|
+
return this.request({
|
|
150
|
+
method: HTTP_VERBS.PUT,
|
|
151
|
+
uri: `${this.webcastInstanceUrl}/streaming`,
|
|
152
|
+
headers: {
|
|
153
|
+
authorization: await this.webex.credentials.getUserToken(),
|
|
154
|
+
trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,
|
|
155
|
+
[HEADERS.CONTENT_TYPE]: HEADERS.CONTENT_TYPE_VALUE.APPLICATION_JSON,
|
|
156
|
+
},
|
|
157
|
+
body: {
|
|
158
|
+
action: 'stop',
|
|
159
|
+
},
|
|
160
|
+
}).catch((error) => {
|
|
161
|
+
LoggerProxy.logger.error('Meeting:webinar#stopWebcast failed', error);
|
|
162
|
+
throw error;
|
|
163
|
+
});
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* query webcast layout for webinar
|
|
168
|
+
* @returns {Promise}
|
|
169
|
+
*/
|
|
170
|
+
async queryWebcastLayout() {
|
|
171
|
+
return this.request({
|
|
172
|
+
method: HTTP_VERBS.GET,
|
|
173
|
+
uri: `${this.webcastInstanceUrl}/layout`,
|
|
174
|
+
headers: {
|
|
175
|
+
authorization: await this.webex.credentials.getUserToken(),
|
|
176
|
+
trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,
|
|
177
|
+
},
|
|
178
|
+
}).catch((error) => {
|
|
179
|
+
LoggerProxy.logger.error('Meeting:webinar#queryWebcastLayout failed', error);
|
|
180
|
+
throw error;
|
|
181
|
+
});
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* update webcast layout for webinar
|
|
186
|
+
* @param {object} layout
|
|
187
|
+
* @returns {Promise}
|
|
188
|
+
*/
|
|
189
|
+
async updateWebcastLayout(layout) {
|
|
190
|
+
return this.request({
|
|
191
|
+
method: HTTP_VERBS.PUT,
|
|
192
|
+
uri: `${this.webcastInstanceUrl}/layout`,
|
|
193
|
+
headers: {
|
|
194
|
+
authorization: await this.webex.credentials.getUserToken(),
|
|
195
|
+
trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,
|
|
196
|
+
[HEADERS.CONTENT_TYPE]: HEADERS.CONTENT_TYPE_VALUE.APPLICATION_JSON,
|
|
197
|
+
},
|
|
198
|
+
body: {
|
|
199
|
+
videoLayout: layout.videoLayout,
|
|
200
|
+
contentLayout: layout.contentLayout,
|
|
201
|
+
syncStageLayout: layout.syncStageLayout,
|
|
202
|
+
syncStageInMeeting: layout.syncStageInMeeting,
|
|
203
|
+
},
|
|
204
|
+
}).catch((error) => {
|
|
205
|
+
LoggerProxy.logger.error('Meeting:webinar#updateWebcastLayout failed', error);
|
|
206
|
+
throw error;
|
|
207
|
+
});
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* view all webcast attendees
|
|
212
|
+
* @param {string} queryString
|
|
213
|
+
* @returns {Promise}
|
|
214
|
+
*/
|
|
215
|
+
async viewAllWebcastAttendees() {
|
|
216
|
+
return this.request({
|
|
217
|
+
method: HTTP_VERBS.GET,
|
|
218
|
+
uri: `${this.webcastInstanceUrl}/attendees`,
|
|
219
|
+
headers: {
|
|
220
|
+
authorization: await this.webex.credentials.getUserToken(),
|
|
221
|
+
trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,
|
|
222
|
+
},
|
|
223
|
+
}).catch((error) => {
|
|
224
|
+
LoggerProxy.logger.error('Meeting:webinar#viewAllWebcastAttendees failed', error);
|
|
225
|
+
throw error;
|
|
226
|
+
});
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* search webcast attendees by query string
|
|
231
|
+
* @param {string} queryString
|
|
232
|
+
* @returns {Promise}
|
|
233
|
+
*/
|
|
234
|
+
async searchWebcastAttendees(queryString = '') {
|
|
235
|
+
return this.request({
|
|
236
|
+
method: HTTP_VERBS.GET,
|
|
237
|
+
uri: `${this.webcastInstanceUrl}/attendees?keyword=${encodeURIComponent(queryString)}`,
|
|
238
|
+
headers: {
|
|
239
|
+
authorization: await this.webex.credentials.getUserToken(),
|
|
240
|
+
trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,
|
|
241
|
+
},
|
|
242
|
+
}).catch((error) => {
|
|
243
|
+
LoggerProxy.logger.error('Meeting:webinar#searchWebcastAttendees failed', error);
|
|
244
|
+
throw error;
|
|
245
|
+
});
|
|
246
|
+
},
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* expel webcast attendee by participantId
|
|
250
|
+
* @param {string} participantId
|
|
251
|
+
* @returns {Promise}
|
|
252
|
+
*/
|
|
253
|
+
async expelWebcastAttendee(participantId) {
|
|
254
|
+
return this.request({
|
|
255
|
+
method: HTTP_VERBS.DELETE,
|
|
256
|
+
uri: `${this.webcastInstanceUrl}/attendees/${participantId}`,
|
|
257
|
+
headers: {
|
|
258
|
+
authorization: await this.webex.credentials.getUserToken(),
|
|
259
|
+
trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,
|
|
260
|
+
},
|
|
261
|
+
}).catch((error) => {
|
|
262
|
+
LoggerProxy.logger.error('Meeting:webinar#expelWebcastAttendee failed', error);
|
|
263
|
+
throw error;
|
|
264
|
+
});
|
|
265
|
+
},
|
|
74
266
|
});
|
|
75
267
|
|
|
76
268
|
export default Webinar;
|
|
@@ -33,6 +33,7 @@ describe('plugin-meetings', () => {
|
|
|
33
33
|
canStartManualCaption: null,
|
|
34
34
|
canStopManualCaption: null,
|
|
35
35
|
isManualCaptionActive: null,
|
|
36
|
+
isPremiseRecordingEnabled: null,
|
|
36
37
|
isSaveTranscriptsEnabled: null,
|
|
37
38
|
isWebexAssistantActive: null,
|
|
38
39
|
canViewCaptionPanel: null,
|
|
@@ -88,6 +89,11 @@ describe('plugin-meetings', () => {
|
|
|
88
89
|
canShowStageView: null,
|
|
89
90
|
canEnableStageView: null,
|
|
90
91
|
canDisableStageView: null,
|
|
92
|
+
isPracticeSessionOn : null,
|
|
93
|
+
isPracticeSessionOff : null,
|
|
94
|
+
canStartPracticeSession: null,
|
|
95
|
+
canStopPracticeSession: null,
|
|
96
|
+
|
|
91
97
|
...expected,
|
|
92
98
|
};
|
|
93
99
|
|
|
@@ -126,6 +132,7 @@ describe('plugin-meetings', () => {
|
|
|
126
132
|
'canStartManualCaption',
|
|
127
133
|
'canStopManualCaption',
|
|
128
134
|
'isManualCaptionActive',
|
|
135
|
+
'isPremiseRecordingEnabled',
|
|
129
136
|
'isSaveTranscriptsEnabled',
|
|
130
137
|
'isWebexAssistantActive',
|
|
131
138
|
'canViewCaptionPanel',
|
|
@@ -181,7 +188,12 @@ describe('plugin-meetings', () => {
|
|
|
181
188
|
'canShowStageView',
|
|
182
189
|
'canEnableStageView',
|
|
183
190
|
'canDisableStageView',
|
|
184
|
-
|
|
191
|
+
'isPracticeSessionOn',
|
|
192
|
+
'isPracticeSessionOff',
|
|
193
|
+
'canStartPracticeSession',
|
|
194
|
+
'canStopPracticeSession',
|
|
195
|
+
|
|
196
|
+
].forEach((key) => {
|
|
185
197
|
it(`get and set for ${key} work as expected`, () => {
|
|
186
198
|
const inMeetingActions = new InMeetingActions();
|
|
187
199
|
|
|
@@ -91,14 +91,14 @@ import ParameterError from '../../../../src/common/errors/parameter';
|
|
|
91
91
|
import PasswordError from '../../../../src/common/errors/password-error';
|
|
92
92
|
import CaptchaError from '../../../../src/common/errors/captcha-error';
|
|
93
93
|
import PermissionError from '../../../../src/common/errors/permission';
|
|
94
|
-
import
|
|
94
|
+
import JoinWebinarError from '../../../../src/common/errors/join-webinar-error';
|
|
95
95
|
import IntentToJoinError from '../../../../src/common/errors/intent-to-join';
|
|
96
96
|
import testUtils from '../../../utils/testUtils';
|
|
97
97
|
import {
|
|
98
98
|
MeetingInfoV2CaptchaError,
|
|
99
99
|
MeetingInfoV2PasswordError,
|
|
100
100
|
MeetingInfoV2PolicyError,
|
|
101
|
-
|
|
101
|
+
MeetingInfoV2JoinWebinarError,
|
|
102
102
|
} from '../../../../src/meeting-info/meeting-info-v2';
|
|
103
103
|
import {
|
|
104
104
|
DTLS_HANDSHAKE_FAILED_CLIENT_CODE,
|
|
@@ -1705,6 +1705,12 @@ describe('plugin-meetings', () => {
|
|
|
1705
1705
|
sinon.assert.called(setCorrelationIdSpy);
|
|
1706
1706
|
assert.equal(meeting.correlationId, '123');
|
|
1707
1707
|
});
|
|
1708
|
+
|
|
1709
|
+
it('should not send client.call.initiated if told not to', async () => {
|
|
1710
|
+
await meeting.join({sendCallInitiated: false});
|
|
1711
|
+
|
|
1712
|
+
sinon.assert.notCalled(webex.internal.newMetrics.submitClientEvent);
|
|
1713
|
+
});
|
|
1708
1714
|
});
|
|
1709
1715
|
|
|
1710
1716
|
describe('failure', () => {
|
|
@@ -2492,9 +2498,11 @@ describe('plugin-meetings', () => {
|
|
|
2492
2498
|
mediaSettings: {},
|
|
2493
2499
|
});
|
|
2494
2500
|
|
|
2495
|
-
const checkLogCounter = (
|
|
2501
|
+
const checkLogCounter = (delayInMinutes, expectedCounter) => {
|
|
2502
|
+
const delayInMilliseconds = delayInMinutes * 60 * 1000;
|
|
2503
|
+
|
|
2496
2504
|
// first check that the counter is not increased just before the delay
|
|
2497
|
-
clock.tick(
|
|
2505
|
+
clock.tick(delayInMilliseconds - 50);
|
|
2498
2506
|
assert.equal(logUploadCounter, expectedCounter - 1);
|
|
2499
2507
|
|
|
2500
2508
|
// and now check that it has reached expected value after the delay
|
|
@@ -2502,22 +2510,18 @@ describe('plugin-meetings', () => {
|
|
|
2502
2510
|
assert.equal(logUploadCounter, expectedCounter);
|
|
2503
2511
|
};
|
|
2504
2512
|
|
|
2505
|
-
checkLogCounter(
|
|
2506
|
-
checkLogCounter(
|
|
2507
|
-
checkLogCounter(
|
|
2508
|
-
checkLogCounter(
|
|
2509
|
-
checkLogCounter(
|
|
2510
|
-
checkLogCounter(30000, 6);
|
|
2511
|
-
checkLogCounter(30000, 7);
|
|
2512
|
-
checkLogCounter(60000, 8);
|
|
2513
|
-
checkLogCounter(60000, 9);
|
|
2514
|
-
checkLogCounter(60000, 10);
|
|
2513
|
+
checkLogCounter(0.1, 1);
|
|
2514
|
+
checkLogCounter(15, 2);
|
|
2515
|
+
checkLogCounter(30, 3);
|
|
2516
|
+
checkLogCounter(60, 4);
|
|
2517
|
+
checkLogCounter(60, 5);
|
|
2515
2518
|
|
|
2516
|
-
// simulate media connection being removed ->
|
|
2519
|
+
// simulate media connection being removed -> 1 more upload should happen, but nothing more afterwards
|
|
2517
2520
|
meeting.mediaProperties.webrtcMediaConnection = undefined;
|
|
2521
|
+
checkLogCounter(60, 6);
|
|
2518
2522
|
|
|
2519
|
-
clock.tick(
|
|
2520
|
-
assert.equal(logUploadCounter,
|
|
2523
|
+
clock.tick(120 * 1000 * 60);
|
|
2524
|
+
assert.equal(logUploadCounter, 6);
|
|
2521
2525
|
|
|
2522
2526
|
clock.restore();
|
|
2523
2527
|
});
|
|
@@ -3552,14 +3556,6 @@ describe('plugin-meetings', () => {
|
|
|
3552
3556
|
});
|
|
3553
3557
|
});
|
|
3554
3558
|
|
|
3555
|
-
it('succeeds even if getDevices() throws', async () => {
|
|
3556
|
-
meeting.meetingState = 'ACTIVE';
|
|
3557
|
-
|
|
3558
|
-
sinon.stub(InternalMediaCoreModule, 'getDevices').rejects(new Error('fake error'));
|
|
3559
|
-
|
|
3560
|
-
await meeting.addMedia();
|
|
3561
|
-
});
|
|
3562
|
-
|
|
3563
3559
|
describe('CA ice failures checks', () => {
|
|
3564
3560
|
[
|
|
3565
3561
|
{
|
|
@@ -3743,8 +3739,12 @@ describe('plugin-meetings', () => {
|
|
|
3743
3739
|
meeting.setMercuryListener = sinon.stub();
|
|
3744
3740
|
meeting.locusInfo.onFullLocus = sinon.stub();
|
|
3745
3741
|
meeting.webex.meetings.geoHintInfo = {regionCode: 'EU', countryCode: 'UK'};
|
|
3746
|
-
meeting.webex.meetings.reachability.getReachabilityReportToAttachToRoap = sinon
|
|
3747
|
-
|
|
3742
|
+
meeting.webex.meetings.reachability.getReachabilityReportToAttachToRoap = sinon
|
|
3743
|
+
.stub()
|
|
3744
|
+
.resolves({id: 'fake reachability'});
|
|
3745
|
+
meeting.webex.meetings.reachability.getClientMediaPreferences = sinon
|
|
3746
|
+
.stub()
|
|
3747
|
+
.resolves({id: 'fake clientMediaPreferences'});
|
|
3748
3748
|
meeting.roap.doTurnDiscovery = sinon.stub().resolves({
|
|
3749
3749
|
turnServerInfo: {
|
|
3750
3750
|
url: 'turns:turn-server-url:443?transport=tcp',
|
|
@@ -3930,8 +3930,14 @@ describe('plugin-meetings', () => {
|
|
|
3930
3930
|
const checkSdpOfferSent = ({audioMuted, videoMuted}) => {
|
|
3931
3931
|
const {sdp, seq, tieBreaker} = roapOfferMessage;
|
|
3932
3932
|
|
|
3933
|
-
assert.calledWith(
|
|
3934
|
-
|
|
3933
|
+
assert.calledWith(
|
|
3934
|
+
meeting.webex.meetings.reachability.getClientMediaPreferences,
|
|
3935
|
+
meeting.isMultistream,
|
|
3936
|
+
0
|
|
3937
|
+
);
|
|
3938
|
+
assert.calledWith(
|
|
3939
|
+
meeting.webex.meetings.reachability.getReachabilityReportToAttachToRoap
|
|
3940
|
+
);
|
|
3935
3941
|
|
|
3936
3942
|
assert.calledWith(locusMediaRequestStub, {
|
|
3937
3943
|
method: 'PUT',
|
|
@@ -4176,7 +4182,6 @@ describe('plugin-meetings', () => {
|
|
|
4176
4182
|
});
|
|
4177
4183
|
|
|
4178
4184
|
it('addMedia() works correctly when media is enabled with streams to publish', async () => {
|
|
4179
|
-
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
|
|
4180
4185
|
await meeting.addMedia({localStreams: {microphone: fakeMicrophoneStream}});
|
|
4181
4186
|
await simulateRoapOffer();
|
|
4182
4187
|
await simulateRoapOk();
|
|
@@ -4207,12 +4212,9 @@ describe('plugin-meetings', () => {
|
|
|
4207
4212
|
|
|
4208
4213
|
// and that these were the only /media requests that were sent
|
|
4209
4214
|
assert.calledTwice(locusMediaRequestStub);
|
|
4210
|
-
|
|
4211
|
-
assert.calledOnce(handleDeviceLoggingSpy);
|
|
4212
4215
|
});
|
|
4213
4216
|
|
|
4214
4217
|
it('addMedia() works correctly when media is enabled with streams to publish and stream is user muted', async () => {
|
|
4215
|
-
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
|
|
4216
4218
|
fakeMicrophoneStream.userMuted = true;
|
|
4217
4219
|
|
|
4218
4220
|
await meeting.addMedia({localStreams: {microphone: fakeMicrophoneStream}});
|
|
@@ -4244,7 +4246,6 @@ describe('plugin-meetings', () => {
|
|
|
4244
4246
|
|
|
4245
4247
|
// and that these were the only /media requests that were sent
|
|
4246
4248
|
assert.calledTwice(locusMediaRequestStub);
|
|
4247
|
-
assert.calledOnce(handleDeviceLoggingSpy);
|
|
4248
4249
|
});
|
|
4249
4250
|
|
|
4250
4251
|
it('addMedia() works correctly when media is enabled with tracks to publish and track is ended', async () => {
|
|
@@ -4316,7 +4317,6 @@ describe('plugin-meetings', () => {
|
|
|
4316
4317
|
});
|
|
4317
4318
|
|
|
4318
4319
|
it('addMedia() works correctly when media is disabled with streams to publish', async () => {
|
|
4319
|
-
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
|
|
4320
4320
|
await meeting.addMedia({
|
|
4321
4321
|
localStreams: {microphone: fakeMicrophoneStream},
|
|
4322
4322
|
audioEnabled: false,
|
|
@@ -4350,20 +4350,6 @@ describe('plugin-meetings', () => {
|
|
|
4350
4350
|
|
|
4351
4351
|
// and that these were the only /media requests that were sent
|
|
4352
4352
|
assert.calledTwice(locusMediaRequestStub);
|
|
4353
|
-
assert.calledOnce(handleDeviceLoggingSpy);
|
|
4354
|
-
});
|
|
4355
|
-
|
|
4356
|
-
it('handleDeviceLogging not called when media is disabled', async () => {
|
|
4357
|
-
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
|
|
4358
|
-
await meeting.addMedia({
|
|
4359
|
-
localStreams: {microphone: fakeMicrophoneStream},
|
|
4360
|
-
audioEnabled: false,
|
|
4361
|
-
videoEnabled: false,
|
|
4362
|
-
});
|
|
4363
|
-
await simulateRoapOffer();
|
|
4364
|
-
await simulateRoapOk();
|
|
4365
|
-
|
|
4366
|
-
assert.notCalled(handleDeviceLoggingSpy);
|
|
4367
4353
|
});
|
|
4368
4354
|
|
|
4369
4355
|
it('addMedia() works correctly when media is disabled with no streams to publish', async () => {
|
|
@@ -4399,20 +4385,6 @@ describe('plugin-meetings', () => {
|
|
|
4399
4385
|
assert.calledTwice(locusMediaRequestStub);
|
|
4400
4386
|
});
|
|
4401
4387
|
|
|
4402
|
-
it('addMedia() works correctly when media is disabled with no streams to publish', async () => {
|
|
4403
|
-
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
|
|
4404
|
-
await meeting.addMedia({audioEnabled: false});
|
|
4405
|
-
//calling handleDeviceLogging with audioEnaled as true adn videoEnabled as false
|
|
4406
|
-
assert.calledWith(handleDeviceLoggingSpy, false, true);
|
|
4407
|
-
});
|
|
4408
|
-
|
|
4409
|
-
it('addMedia() works correctly when video is disabled with no streams to publish', async () => {
|
|
4410
|
-
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
|
|
4411
|
-
await meeting.addMedia({videoEnabled: false});
|
|
4412
|
-
//calling handleDeviceLogging audioEnabled as true videoEnabled as false
|
|
4413
|
-
assert.calledWith(handleDeviceLoggingSpy, true, false);
|
|
4414
|
-
});
|
|
4415
|
-
|
|
4416
4388
|
it('addMedia() works correctly when video is disabled with no streams to publish', async () => {
|
|
4417
4389
|
await meeting.addMedia({videoEnabled: false});
|
|
4418
4390
|
await simulateRoapOffer();
|
|
@@ -4479,13 +4451,6 @@ describe('plugin-meetings', () => {
|
|
|
4479
4451
|
assert.calledTwice(locusMediaRequestStub);
|
|
4480
4452
|
});
|
|
4481
4453
|
|
|
4482
|
-
it('addMedia() works correctly when both shareAudio and shareVideo is disabled with no streams publish', async () => {
|
|
4483
|
-
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
|
|
4484
|
-
await meeting.addMedia({shareAudioEnabled: false, shareVideoEnabled: false});
|
|
4485
|
-
//calling handleDeviceLogging with audioEnabled true and videoEnabled as true
|
|
4486
|
-
assert.calledWith(handleDeviceLoggingSpy, true, true);
|
|
4487
|
-
});
|
|
4488
|
-
|
|
4489
4454
|
describe('publishStreams()/unpublishStreams() calls', () => {
|
|
4490
4455
|
[
|
|
4491
4456
|
{mediaEnabled: true, expected: {direction: 'sendrecv', localMuteSentValue: false}},
|
|
@@ -6332,29 +6297,74 @@ describe('plugin-meetings', () => {
|
|
|
6332
6297
|
assert.equal(meeting.fetchMeetingInfoTimeoutId, undefined);
|
|
6333
6298
|
});
|
|
6334
6299
|
|
|
6335
|
-
it('handles
|
|
6300
|
+
it('handles MeetingInfoV2JoinWebinarError webinar need registration', async () => {
|
|
6336
6301
|
meeting.destination = FAKE_DESTINATION;
|
|
6337
6302
|
meeting.destinationType = FAKE_TYPE;
|
|
6338
6303
|
meeting.attrs.meetingInfoProvider = {
|
|
6339
6304
|
fetchMeetingInfo: sinon
|
|
6340
6305
|
.stub()
|
|
6341
6306
|
.throws(
|
|
6342
|
-
new
|
|
6307
|
+
new MeetingInfoV2JoinWebinarError(403021, FAKE_MEETING_INFO, 'a message')
|
|
6343
6308
|
),
|
|
6344
6309
|
};
|
|
6345
6310
|
|
|
6346
6311
|
await assert.isRejected(
|
|
6347
6312
|
meeting.fetchMeetingInfo({sendCAevents: true}),
|
|
6348
|
-
|
|
6313
|
+
JoinWebinarError
|
|
6349
6314
|
);
|
|
6350
6315
|
|
|
6351
6316
|
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
|
6352
|
-
assert.equal(meeting.meetingInfoFailureCode, 403021);
|
|
6353
6317
|
assert.equal(
|
|
6354
6318
|
meeting.meetingInfoFailureReason,
|
|
6355
6319
|
MEETING_INFO_FAILURE_REASON.WEBINAR_REGISTRATION
|
|
6356
6320
|
);
|
|
6357
6321
|
});
|
|
6322
|
+
|
|
6323
|
+
it('handles MeetingInfoV2JoinWebinarError webinar need join with webcast', async () => {
|
|
6324
|
+
meeting.destination = FAKE_DESTINATION;
|
|
6325
|
+
meeting.destinationType = FAKE_TYPE;
|
|
6326
|
+
meeting.attrs.meetingInfoProvider = {
|
|
6327
|
+
fetchMeetingInfo: sinon
|
|
6328
|
+
.stub()
|
|
6329
|
+
.throws(
|
|
6330
|
+
new MeetingInfoV2JoinWebinarError(403026, FAKE_MEETING_INFO, 'a message')
|
|
6331
|
+
),
|
|
6332
|
+
};
|
|
6333
|
+
|
|
6334
|
+
await assert.isRejected(
|
|
6335
|
+
meeting.fetchMeetingInfo({sendCAevents: true}),
|
|
6336
|
+
JoinWebinarError
|
|
6337
|
+
);
|
|
6338
|
+
|
|
6339
|
+
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
|
6340
|
+
assert.equal(
|
|
6341
|
+
meeting.meetingInfoFailureReason,
|
|
6342
|
+
MEETING_INFO_FAILURE_REASON.NEED_JOIN_WITH_WEBCAST
|
|
6343
|
+
);
|
|
6344
|
+
});
|
|
6345
|
+
|
|
6346
|
+
it('handles MeetingInfoV2JoinWebinarError webinar need registrationId', async () => {
|
|
6347
|
+
meeting.destination = FAKE_DESTINATION;
|
|
6348
|
+
meeting.destinationType = FAKE_TYPE;
|
|
6349
|
+
meeting.attrs.meetingInfoProvider = {
|
|
6350
|
+
fetchMeetingInfo: sinon
|
|
6351
|
+
.stub()
|
|
6352
|
+
.throws(
|
|
6353
|
+
new MeetingInfoV2JoinWebinarError(403037, FAKE_MEETING_INFO, 'a message')
|
|
6354
|
+
),
|
|
6355
|
+
};
|
|
6356
|
+
|
|
6357
|
+
await assert.isRejected(
|
|
6358
|
+
meeting.fetchMeetingInfo({sendCAevents: true}),
|
|
6359
|
+
JoinWebinarError
|
|
6360
|
+
);
|
|
6361
|
+
|
|
6362
|
+
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
|
6363
|
+
assert.equal(
|
|
6364
|
+
meeting.meetingInfoFailureReason,
|
|
6365
|
+
MEETING_INFO_FAILURE_REASON.WEBINAR_NEED_REGISTRATIONID
|
|
6366
|
+
);
|
|
6367
|
+
});
|
|
6358
6368
|
});
|
|
6359
6369
|
|
|
6360
6370
|
describe('#refreshPermissionToken', () => {
|
|
@@ -7817,7 +7827,9 @@ describe('plugin-meetings', () => {
|
|
|
7817
7827
|
});
|
|
7818
7828
|
|
|
7819
7829
|
it('should collect ice candidates', () => {
|
|
7820
|
-
eventListeners[MediaConnectionEventNames.ICE_CANDIDATE]({
|
|
7830
|
+
eventListeners[MediaConnectionEventNames.ICE_CANDIDATE]({
|
|
7831
|
+
candidate: {candidate: 'candidate'},
|
|
7832
|
+
});
|
|
7821
7833
|
|
|
7822
7834
|
assert.equal(meeting.iceCandidatesCount, 1);
|
|
7823
7835
|
});
|
|
@@ -8123,10 +8135,10 @@ describe('plugin-meetings', () => {
|
|
|
8123
8135
|
meeting.statsAnalyzer.stopAnalyzer = sinon.stub().resolves();
|
|
8124
8136
|
meeting.reconnectionManager = {
|
|
8125
8137
|
reconnect: sinon.stub().resolves(),
|
|
8126
|
-
resetReconnectionTimer: () => {}
|
|
8138
|
+
resetReconnectionTimer: () => {},
|
|
8127
8139
|
};
|
|
8128
8140
|
meeting.currentMediaStatus = {
|
|
8129
|
-
video: true
|
|
8141
|
+
video: true,
|
|
8130
8142
|
};
|
|
8131
8143
|
|
|
8132
8144
|
await mockFailedEvent();
|
|
@@ -8677,6 +8689,13 @@ describe('plugin-meetings', () => {
|
|
|
8677
8689
|
{payload: test1}
|
|
8678
8690
|
);
|
|
8679
8691
|
assert.calledOnce(meeting.updateLLMConnection);
|
|
8692
|
+
assert.calledOnceWithExactly(
|
|
8693
|
+
Metrics.sendBehavioralMetric,
|
|
8694
|
+
BEHAVIORAL_METRICS.GUEST_ENTERED_LOBBY,
|
|
8695
|
+
{
|
|
8696
|
+
correlation_id: meeting.correlationId,
|
|
8697
|
+
}
|
|
8698
|
+
);
|
|
8680
8699
|
done();
|
|
8681
8700
|
});
|
|
8682
8701
|
it('listens to the self admitted guest event', (done) => {
|
|
@@ -8698,6 +8717,13 @@ describe('plugin-meetings', () => {
|
|
|
8698
8717
|
assert.calledOnce(meeting.updateLLMConnection);
|
|
8699
8718
|
assert.calledOnceWithExactly(meeting.rtcMetrics.sendNextMetrics);
|
|
8700
8719
|
|
|
8720
|
+
assert.calledOnceWithExactly(
|
|
8721
|
+
Metrics.sendBehavioralMetric,
|
|
8722
|
+
BEHAVIORAL_METRICS.GUEST_EXITED_LOBBY,
|
|
8723
|
+
{
|
|
8724
|
+
correlation_id: meeting.correlationId,
|
|
8725
|
+
}
|
|
8726
|
+
);
|
|
8701
8727
|
done();
|
|
8702
8728
|
});
|
|
8703
8729
|
|
|
@@ -9030,6 +9056,8 @@ describe('plugin-meetings', () => {
|
|
|
9030
9056
|
});
|
|
9031
9057
|
|
|
9032
9058
|
it('listens to MEETING_CONTROLS_PRACTICE_SESSION_STATUS_UPDATED', async () => {
|
|
9059
|
+
meeting.webinar.updatePracticeSessionStatus = sinon.stub();
|
|
9060
|
+
|
|
9033
9061
|
const state = {example: 'value'};
|
|
9034
9062
|
|
|
9035
9063
|
await meeting.locusInfo.emitScoped(
|
|
@@ -9038,6 +9066,7 @@ describe('plugin-meetings', () => {
|
|
|
9038
9066
|
{state}
|
|
9039
9067
|
);
|
|
9040
9068
|
|
|
9069
|
+
assert.calledOnceWithExactly(meeting.webinar.updatePracticeSessionStatus, state);
|
|
9041
9070
|
assert.calledWith(
|
|
9042
9071
|
TriggerProxy.trigger,
|
|
9043
9072
|
meeting,
|
|
@@ -165,21 +165,6 @@ describe('plugin-meetings', () => {
|
|
|
165
165
|
assert(LoggerProxy.logger.log.called, 'log called');
|
|
166
166
|
});
|
|
167
167
|
});
|
|
168
|
-
|
|
169
|
-
describe('#handleDeviceLogging', () => {
|
|
170
|
-
it('should not log if called without devices', () => {
|
|
171
|
-
MeetingUtil.handleDeviceLogging();
|
|
172
|
-
assert(!LoggerProxy.logger.log.called, 'log not called');
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
it('should log device settings', () => {
|
|
176
|
-
const mockDevices = [{deviceId: 'device-1'}, {deviceId: 'device-2'}];
|
|
177
|
-
|
|
178
|
-
assert(MeetingUtil.handleDeviceLogging, 'is defined');
|
|
179
|
-
MeetingUtil.handleDeviceLogging(mockDevices);
|
|
180
|
-
assert(LoggerProxy.logger.log.called, 'log called');
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
168
|
});
|
|
184
169
|
|
|
185
170
|
describe('addSequence', () => {
|