@webex/plugin-meetings 3.7.0-next.3 → 3.7.0-next.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +6 -1
- package/dist/constants.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 +9 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +17 -0
- package/dist/meeting/index.js.map +1 -1
- package/dist/members/util.js +4 -2
- package/dist/members/util.js.map +1 -1
- package/dist/reachability/clusterReachability.js +12 -11
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/types/constants.d.ts +4 -0
- package/dist/types/meeting/in-meeting-actions.d.ts +8 -0
- package/dist/types/meeting/index.d.ts +1 -0
- package/dist/types/members/util.d.ts +2 -0
- package/dist/webinar/index.js +39 -7
- package/dist/webinar/index.js.map +1 -1
- package/package.json +3 -3
- package/src/constants.ts +6 -0
- package/src/meeting/in-meeting-actions.ts +17 -0
- package/src/meeting/index.ts +18 -0
- package/src/members/util.ts +1 -0
- package/src/reachability/clusterReachability.ts +4 -1
- package/src/webinar/index.ts +40 -8
- package/test/unit/spec/meeting/in-meeting-actions.ts +11 -1
- package/test/unit/spec/meeting/index.js +3 -0
- package/test/unit/spec/members/utils.js +95 -0
- package/test/unit/spec/reachability/clusterReachability.ts +7 -0
- package/test/unit/spec/webinar/index.ts +47 -0
package/src/webinar/index.ts
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import {WebexPlugin} from '@webex/webex-core';
|
|
5
5
|
import {get} from 'lodash';
|
|
6
|
-
import {MEETINGS, SELF_ROLES} from '../constants';
|
|
6
|
+
import {HTTP_VERBS, MEETINGS, SELF_ROLES} from '../constants';
|
|
7
7
|
|
|
8
8
|
import WebinarCollection from './collection';
|
|
9
|
+
import LoggerProxy from '../common/logs/logger-proxy';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* @class Webinar
|
|
@@ -22,6 +23,7 @@ const Webinar = WebexPlugin.extend({
|
|
|
22
23
|
canManageWebcast: 'boolean', // appears the ability to manage webcast
|
|
23
24
|
selfIsPanelist: 'boolean', // self is panelist
|
|
24
25
|
selfIsAttendee: 'boolean', // self is attendee
|
|
26
|
+
practiceSessionEnabled: 'boolean', // practice session enabled
|
|
25
27
|
},
|
|
26
28
|
|
|
27
29
|
/**
|
|
@@ -59,18 +61,48 @@ const Webinar = WebexPlugin.extend({
|
|
|
59
61
|
* @returns {{isPromoted: boolean, isDemoted: boolean}} Role transition states
|
|
60
62
|
*/
|
|
61
63
|
updateRoleChanged(payload) {
|
|
64
|
+
const oldRoles = get(payload, 'oldRoles', []);
|
|
65
|
+
const newRoles = get(payload, 'newRoles', []);
|
|
66
|
+
|
|
62
67
|
const isPromoted =
|
|
63
|
-
|
|
64
|
-
get(payload, 'newRoles', []).includes(SELF_ROLES.PANELIST);
|
|
68
|
+
oldRoles.includes(SELF_ROLES.ATTENDEE) && newRoles.includes(SELF_ROLES.PANELIST);
|
|
65
69
|
const isDemoted =
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
this.set('
|
|
69
|
-
this.
|
|
70
|
-
this.updateCanManageWebcast(get(payload, 'newRoles', []).includes(SELF_ROLES.MODERATOR));
|
|
70
|
+
oldRoles.includes(SELF_ROLES.PANELIST) && newRoles.includes(SELF_ROLES.ATTENDEE);
|
|
71
|
+
this.set('selfIsPanelist', newRoles.includes(SELF_ROLES.PANELIST));
|
|
72
|
+
this.set('selfIsAttendee', newRoles.includes(SELF_ROLES.ATTENDEE));
|
|
73
|
+
this.updateCanManageWebcast(newRoles.includes(SELF_ROLES.MODERATOR));
|
|
71
74
|
|
|
72
75
|
return {isPromoted, isDemoted};
|
|
73
76
|
},
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* start or stop practice session for webinar
|
|
80
|
+
* @param {boolean} enabled
|
|
81
|
+
* @returns {Promise}
|
|
82
|
+
*/
|
|
83
|
+
setPracticeSessionState(enabled) {
|
|
84
|
+
return this.request({
|
|
85
|
+
method: HTTP_VERBS.PATCH,
|
|
86
|
+
uri: `${this.locusUrl}/controls`,
|
|
87
|
+
body: {
|
|
88
|
+
practiceSession: {
|
|
89
|
+
enabled,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
}).catch((error) => {
|
|
93
|
+
LoggerProxy.logger.error('Meeting:webinar#setPracticeSessionState failed', error);
|
|
94
|
+
throw error;
|
|
95
|
+
});
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* update practice session status
|
|
100
|
+
* @param {object} payload
|
|
101
|
+
* @returns {void}
|
|
102
|
+
*/
|
|
103
|
+
updatePracticeSessionStatus(payload) {
|
|
104
|
+
this.set('practiceSessionEnabled', payload.enabled);
|
|
105
|
+
},
|
|
74
106
|
});
|
|
75
107
|
|
|
76
108
|
export default Webinar;
|
|
@@ -88,6 +88,11 @@ describe('plugin-meetings', () => {
|
|
|
88
88
|
canShowStageView: null,
|
|
89
89
|
canEnableStageView: null,
|
|
90
90
|
canDisableStageView: null,
|
|
91
|
+
isPracticeSessionOn : null,
|
|
92
|
+
isPracticeSessionOff : null,
|
|
93
|
+
canStartPracticeSession: null,
|
|
94
|
+
canStopPracticeSession: null,
|
|
95
|
+
|
|
91
96
|
...expected,
|
|
92
97
|
};
|
|
93
98
|
|
|
@@ -181,7 +186,12 @@ describe('plugin-meetings', () => {
|
|
|
181
186
|
'canShowStageView',
|
|
182
187
|
'canEnableStageView',
|
|
183
188
|
'canDisableStageView',
|
|
184
|
-
|
|
189
|
+
'isPracticeSessionOn',
|
|
190
|
+
'isPracticeSessionOff',
|
|
191
|
+
'canStartPracticeSession',
|
|
192
|
+
'canStopPracticeSession',
|
|
193
|
+
|
|
194
|
+
].forEach((key) => {
|
|
185
195
|
it(`get and set for ${key} work as expected`, () => {
|
|
186
196
|
const inMeetingActions = new InMeetingActions();
|
|
187
197
|
|
|
@@ -9044,6 +9044,8 @@ describe('plugin-meetings', () => {
|
|
|
9044
9044
|
});
|
|
9045
9045
|
|
|
9046
9046
|
it('listens to MEETING_CONTROLS_PRACTICE_SESSION_STATUS_UPDATED', async () => {
|
|
9047
|
+
meeting.webinar.updatePracticeSessionStatus = sinon.stub();
|
|
9048
|
+
|
|
9047
9049
|
const state = {example: 'value'};
|
|
9048
9050
|
|
|
9049
9051
|
await meeting.locusInfo.emitScoped(
|
|
@@ -9052,6 +9054,7 @@ describe('plugin-meetings', () => {
|
|
|
9052
9054
|
{state}
|
|
9053
9055
|
);
|
|
9054
9056
|
|
|
9057
|
+
assert.calledOnceWithExactly( meeting.webinar.updatePracticeSessionStatus, state);
|
|
9055
9058
|
assert.calledWith(
|
|
9056
9059
|
TriggerProxy.trigger,
|
|
9057
9060
|
meeting,
|
|
@@ -262,5 +262,100 @@ describe('plugin-meetings', () => {
|
|
|
262
262
|
testParams(false);
|
|
263
263
|
});
|
|
264
264
|
});
|
|
265
|
+
|
|
266
|
+
describe('#getAddMemberBody', () => {
|
|
267
|
+
it('returns the correct body with email address and roles', () => {
|
|
268
|
+
const options = {
|
|
269
|
+
invitee: {
|
|
270
|
+
emailAddress: 'test@example.com',
|
|
271
|
+
roles: ['role1', 'role2'],
|
|
272
|
+
},
|
|
273
|
+
alertIfActive: true,
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
assert.deepEqual(MembersUtil.getAddMemberBody(options), {
|
|
277
|
+
invitees: [
|
|
278
|
+
{
|
|
279
|
+
address: 'test@example.com',
|
|
280
|
+
roles: ['role1', 'role2'],
|
|
281
|
+
},
|
|
282
|
+
],
|
|
283
|
+
alertIfActive: true,
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it('returns the correct body with phone number and no roles', () => {
|
|
288
|
+
const options = {
|
|
289
|
+
invitee: {
|
|
290
|
+
phoneNumber: '1234567890',
|
|
291
|
+
},
|
|
292
|
+
alertIfActive: false,
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
assert.deepEqual(MembersUtil.getAddMemberBody(options), {
|
|
296
|
+
invitees: [
|
|
297
|
+
{
|
|
298
|
+
address: '1234567890',
|
|
299
|
+
},
|
|
300
|
+
],
|
|
301
|
+
alertIfActive: false,
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('returns the correct body with fallback to email', () => {
|
|
306
|
+
const options = {
|
|
307
|
+
invitee: {
|
|
308
|
+
email: 'fallback@example.com',
|
|
309
|
+
},
|
|
310
|
+
alertIfActive: true,
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
assert.deepEqual(MembersUtil.getAddMemberBody(options), {
|
|
314
|
+
invitees: [
|
|
315
|
+
{
|
|
316
|
+
address: 'fallback@example.com',
|
|
317
|
+
},
|
|
318
|
+
],
|
|
319
|
+
alertIfActive: true,
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it('handles missing `alertIfActive` gracefully', () => {
|
|
324
|
+
const options = {
|
|
325
|
+
invitee: {
|
|
326
|
+
emailAddress: 'test@example.com',
|
|
327
|
+
roles: ['role1'],
|
|
328
|
+
},
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
assert.deepEqual(MembersUtil.getAddMemberBody(options), {
|
|
332
|
+
invitees: [
|
|
333
|
+
{
|
|
334
|
+
address: 'test@example.com',
|
|
335
|
+
roles: ['role1'],
|
|
336
|
+
},
|
|
337
|
+
],
|
|
338
|
+
alertIfActive: undefined,
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('ignores roles if not provided', () => {
|
|
343
|
+
const options = {
|
|
344
|
+
invitee: {
|
|
345
|
+
emailAddress: 'test@example.com',
|
|
346
|
+
},
|
|
347
|
+
alertIfActive: false,
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
assert.deepEqual(MembersUtil.getAddMemberBody(options), {
|
|
351
|
+
invitees: [
|
|
352
|
+
{
|
|
353
|
+
address: 'test@example.com',
|
|
354
|
+
},
|
|
355
|
+
],
|
|
356
|
+
alertIfActive: false,
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
});
|
|
265
360
|
});
|
|
266
361
|
});
|
|
@@ -15,6 +15,7 @@ describe('ClusterReachability', () => {
|
|
|
15
15
|
let previousRTCPeerConnection;
|
|
16
16
|
let clusterReachability;
|
|
17
17
|
let fakePeerConnection;
|
|
18
|
+
let gatherIceCandidatesSpy;
|
|
18
19
|
|
|
19
20
|
const emittedEvents: Record<Events, (ResultEventData | ClientMediaIpsUpdatedEventData)[]> = {
|
|
20
21
|
[Events.resultReady]: [],
|
|
@@ -44,6 +45,8 @@ describe('ClusterReachability', () => {
|
|
|
44
45
|
xtls: ['stun:xtls1.webex.com', 'stun:xtls2.webex.com:443'],
|
|
45
46
|
});
|
|
46
47
|
|
|
48
|
+
gatherIceCandidatesSpy = sinon.spy(clusterReachability, 'gatherIceCandidates');
|
|
49
|
+
|
|
47
50
|
resetEmittedEvents();
|
|
48
51
|
|
|
49
52
|
clusterReachability.on(Events.resultReady, (data: ResultEventData) => {
|
|
@@ -151,6 +154,10 @@ describe('ClusterReachability', () => {
|
|
|
151
154
|
assert.calledOnceWithExactly(fakePeerConnection.createOffer, {offerToReceiveAudio: true});
|
|
152
155
|
assert.calledOnce(fakePeerConnection.setLocalDescription);
|
|
153
156
|
|
|
157
|
+
// Make sure that gatherIceCandidates is called before setLocalDescription
|
|
158
|
+
// as setLocalDescription triggers the ICE gathering process
|
|
159
|
+
assert.isTrue(gatherIceCandidatesSpy.calledBefore(fakePeerConnection.setLocalDescription));
|
|
160
|
+
|
|
154
161
|
clusterReachability.abort();
|
|
155
162
|
await promise;
|
|
156
163
|
|
|
@@ -122,5 +122,52 @@ describe('plugin-meetings', () => {
|
|
|
122
122
|
});
|
|
123
123
|
});
|
|
124
124
|
|
|
125
|
+
describe("#setPracticeSessionState", () => {
|
|
126
|
+
[true, false].forEach((enabled) => {
|
|
127
|
+
it(`sends a PATCH request to ${enabled ? "enable" : "disable"} the practice session`, async () => {
|
|
128
|
+
const result = await webinar.setPracticeSessionState(enabled);
|
|
129
|
+
assert.calledOnce(webex.request);
|
|
130
|
+
assert.calledWith(webex.request, {
|
|
131
|
+
method: "PATCH",
|
|
132
|
+
uri: `${webinar.locusUrl}/controls`,
|
|
133
|
+
body: {
|
|
134
|
+
practiceSession: { enabled }
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
assert.equal(result, "REQUEST_RETURN_VALUE", "should return the resolved value from the request");
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('handles API call failures gracefully', async () => {
|
|
142
|
+
webex.request.rejects(new Error('API_ERROR'));
|
|
143
|
+
const errorLogger = sinon.stub(LoggerProxy.logger, 'error');
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
await webinar.setPracticeSessionState(true);
|
|
147
|
+
assert.fail('setPracticeSessionState should throw an error');
|
|
148
|
+
} catch (error) {
|
|
149
|
+
assert.equal(error.message, 'API_ERROR', 'should throw the correct error');
|
|
150
|
+
assert.calledOnce(errorLogger);
|
|
151
|
+
assert.calledWith(errorLogger, 'Meeting:webinar#setPracticeSessionState failed', sinon.match.instanceOf(Error));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
errorLogger.restore();
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe('#updatePracticeSessionStatus', () => {
|
|
159
|
+
it('sets PS state true', () => {
|
|
160
|
+
webinar.updatePracticeSessionStatus({enabled: true});
|
|
161
|
+
|
|
162
|
+
assert.equal(webinar.practiceSessionEnabled, true);
|
|
163
|
+
});
|
|
164
|
+
it('sets PS state true', () => {
|
|
165
|
+
webinar.updatePracticeSessionStatus({enabled: false});
|
|
166
|
+
|
|
167
|
+
assert.equal(webinar.practiceSessionEnabled, false);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
|
|
125
172
|
})
|
|
126
173
|
})
|