@webex/plugin-meetings 3.0.0-beta.134 → 3.0.0-beta.136

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.
@@ -41,10 +41,10 @@ const Breakouts = WebexPlugin.extend({
41
41
  groups: 'array', // appears when create breakouts
42
42
  manageGroups: 'array', // appears when manage breakouts
43
43
  preAssignments: 'array', // appears when getPreAssignments info hasBreakoutPreAssignments = true
44
- shouldFetchPreassignments: 'boolean', // Controlling the lifecycle of the pre-assign API
45
44
  editLock: 'object', // appears when getBreakout info editlock = true
46
45
  intervalID: 'number',
47
46
  meetingId: 'string',
47
+ canManageBreakouts: 'boolean', // appear the ability to manage breakouts
48
48
  },
49
49
  children: {
50
50
  currentBreakoutSession: Breakout,
@@ -80,7 +80,7 @@ const Breakouts = WebexPlugin.extend({
80
80
  cache: false,
81
81
  deps: ['manageGroups'],
82
82
  /**
83
- * Returns the actived group id
83
+ * Returns the active group id
84
84
  * @returns {boolean}
85
85
  */
86
86
  fn() {
@@ -104,6 +104,21 @@ const Breakouts = WebexPlugin.extend({
104
104
  return this.isInMainSession ? this.groups?.[0]?.status : this.status;
105
105
  },
106
106
  },
107
+ shouldQueryPreAssignments: {
108
+ cache: false,
109
+ deps: ['canManageBreakouts', 'enableBreakoutSession', 'hasBreakoutPreAssignments'],
110
+ /**
111
+ * Returns should query preAssignments or not
112
+ * @returns {boolean}
113
+ */
114
+ fn() {
115
+ return !!(
116
+ this.canManageBreakouts &&
117
+ this.enableBreakoutSession &&
118
+ this.hasBreakoutPreAssignments
119
+ );
120
+ },
121
+ },
107
122
  },
108
123
 
109
124
  /**
@@ -116,6 +131,11 @@ const Breakouts = WebexPlugin.extend({
116
131
  this.trigger(BREAKOUTS.EVENTS.BREAKOUTS_CLOSING);
117
132
  }
118
133
  });
134
+ this.listenTo(this, 'change:shouldQueryPreAssignments', () => {
135
+ if (this.shouldQueryPreAssignments && !this.preAssignments) {
136
+ this.queryPreAssignments();
137
+ }
138
+ });
119
139
  this.debouncedQueryRosters = debounce(this.queryRosters, 10, {
120
140
  leading: true,
121
141
  trailing: false,
@@ -156,6 +176,15 @@ const Breakouts = WebexPlugin.extend({
156
176
  }
157
177
  },
158
178
 
179
+ /**
180
+ * Update whether self is moderator/cohost or not
181
+ * @param {boolean} canManageBreakouts
182
+ * @returns {void}
183
+ */
184
+ updateCanManageBreakouts(canManageBreakouts) {
185
+ this.set('canManageBreakouts', canManageBreakouts);
186
+ },
187
+
159
188
  /**
160
189
  * Update the current breakout resource url
161
190
  * @param {string} breakoutServiceUrl
@@ -273,7 +302,6 @@ const Breakouts = WebexPlugin.extend({
273
302
  * @returns {void}
274
303
  */
275
304
  updateBreakout(params) {
276
- const preEnableBreakoutSession = this.get('enableBreakoutSession');
277
305
  this.set(params);
278
306
 
279
307
  // These values are set manually so they are unset when they are not included in params
@@ -295,11 +323,6 @@ const Breakouts = WebexPlugin.extend({
295
323
  [BREAKOUTS.SESSION_STATES.REQUESTED]: false,
296
324
  });
297
325
 
298
- // We need to call queryPreAssignments when enableBreakoutSession become true
299
- if (preEnableBreakoutSession !== params.enableBreakoutSession) {
300
- this.queryPreAssignments(params);
301
- }
302
-
303
326
  if (
304
327
  this.currentBreakoutSession.previous('sessionId') !== this.currentBreakoutSession.sessionId ||
305
328
  this.currentBreakoutSession.previous('groupId') !== this.currentBreakoutSession.groupId
@@ -529,7 +552,6 @@ const Breakouts = WebexPlugin.extend({
529
552
  });
530
553
 
531
554
  this._setManageGroups(breakoutInfo);
532
- this.shouldFetchPreassignments = false;
533
555
 
534
556
  return breakoutInfo;
535
557
  },
@@ -788,27 +810,21 @@ const Breakouts = WebexPlugin.extend({
788
810
  },
789
811
 
790
812
  /**
791
- * The pre-assignments need to be queried when "hasBreakoutPreAssignments" is true
792
- * @param {Object} params
813
+ * query preAssignments
793
814
  * @returns {void}
794
815
  */
795
- queryPreAssignments(params) {
796
- if (!params || !params.enableBreakoutSession || !params.hasBreakoutPreAssignments) {
797
- return;
798
- }
799
- if (!this.shouldFetchPreassignments) {
800
- this.webex
801
- .request({uri: `${this.url}/preassignments`, qs: {locusUrl: btoa(this.locusUrl)}})
802
- .then((result) => {
803
- if (result.body?.groups) {
804
- this.set('preAssignments', result.body.groups);
805
- }
806
- })
807
- .catch((error) => {
808
- LoggerProxy.logger.error('Meeting:breakouts#queryPreAssignments failed', error);
809
- });
810
- this.shouldFetchPreassignments = true;
811
- }
816
+ queryPreAssignments() {
817
+ this.webex
818
+ .request({uri: `${this.url}/preassignments`, qs: {locusUrl: btoa(this.locusUrl)}})
819
+ .then((result) => {
820
+ if (result.body?.groups) {
821
+ this.set('preAssignments', result.body.groups);
822
+ this.trigger(BREAKOUTS.EVENTS.PRE_ASSIGNMENTS_UPDATE);
823
+ }
824
+ })
825
+ .catch((error) => {
826
+ LoggerProxy.logger.error('Meeting:breakouts#queryPreAssignments failed', error);
827
+ });
812
828
  },
813
829
  /**
814
830
  * assign participants dynamically after breakout sessions started,
package/src/constants.ts CHANGED
@@ -318,6 +318,7 @@ export const EVENT_TRIGGERS = {
318
318
  MEETING_BREAKOUTS_ASK_RETURN_TO_MAIN: 'meeting:breakouts:askReturnToMain',
319
319
  MEETING_BREAKOUTS_LEAVE: 'meeting:breakouts:leave',
320
320
  MEETING_BREAKOUTS_ASK_FOR_HELP: 'meeting:breakouts:askForHelp',
321
+ MEETING_BREAKOUTS_PRE_ASSIGNMENTS_UPDATE: 'meeting:breakouts:preAssignmentsUpdate',
321
322
  MEMBERS_UPDATE: 'members:update',
322
323
  MEMBERS_CLEAR: 'members:clear',
323
324
  MEMBERS_CONTENT_UPDATE: 'members:content:update',
@@ -563,6 +564,7 @@ export const BREAKOUTS = {
563
564
  ASK_RETURN_TO_MAIN: 'ASK_RETURN_TO_MAIN',
564
565
  LEAVE_BREAKOUT: 'LEAVE_BREAKOUT',
565
566
  ASK_FOR_HELP: 'ASK_FOR_HELP',
567
+ PRE_ASSIGNMENTS_UPDATE: 'PRE_ASSIGNMENTS_UPDATE',
566
568
  },
567
569
  SESSION_TYPES: {
568
570
  MAIN: 'MAIN',
@@ -633,7 +635,6 @@ export const LOCUSINFO = {
633
635
  SELF_MEETING_BREAKOUTS_CHANGED: 'SELF_MEETING_BREAKOUTS_CHANGED',
634
636
  MEDIA_INACTIVITY: 'MEDIA_INACTIVITY',
635
637
  LINKS_SERVICES: 'LINKS_SERVICES',
636
- SELF_MODERATOR_OR_COHOST_UPGRADE: 'SELF_MODERATOR_OR_COHOST_UPGRADE',
637
638
  },
638
639
  };
639
640
 
@@ -1237,18 +1237,7 @@ export default class LocusInfo extends EventsScope {
1237
1237
  {oldRoles: parsedSelves.previous?.roles, newRoles: parsedSelves.current?.roles}
1238
1238
  );
1239
1239
  }
1240
- // When the user upgrades to moderator or cohost
1241
- if (parsedSelves.updates.isUpgradeToModeratorOrCohost) {
1242
- this.emitScoped(
1243
- {
1244
- file: 'locus-info',
1245
- function: 'updateSelf',
1246
- },
1247
- LOCUSINFO.EVENTS.SELF_MODERATOR_OR_COHOST_UPGRADE,
1248
- self
1249
- );
1250
- }
1251
- //
1240
+
1252
1241
  if (parsedSelves.updates.isVideoMutedByOthersChanged) {
1253
1242
  this.emitScoped(
1254
1243
  {
@@ -14,7 +14,6 @@ import {
14
14
  AUDIO,
15
15
  VIDEO,
16
16
  MediaContent,
17
- SELF_ROLES,
18
17
  } from '../constants';
19
18
  import ParameterError from '../common/errors/parameter';
20
19
 
@@ -109,7 +108,6 @@ SelfUtils.getSelves = (oldSelf, newSelf, deviceId) => {
109
108
  );
110
109
  updates.moderatorChanged = SelfUtils.moderatorChanged(previous, current);
111
110
  updates.isRolesChanged = SelfUtils.isRolesChanged(previous, current);
112
- updates.isUpgradeToModeratorOrCohost = SelfUtils.isUpgradeToModeratorOrCohost(previous, current);
113
111
  updates.isMediaInactiveOrReleased = SelfUtils.wasMediaInactiveOrReleased(previous, current);
114
112
  updates.isUserObserving = SelfUtils.isDeviceObserving(previous, current);
115
113
  updates.layoutChanged = SelfUtils.layoutChanged(previous, current);
@@ -340,38 +338,20 @@ SelfUtils.moderatorChanged = (oldSelf, changedSelf) => {
340
338
  return oldSelf.moderator !== changedSelf.moderator;
341
339
  };
342
340
 
343
- SelfUtils.isRolesChanged = (oldSelf, changedSelf) => {
344
- if (!oldSelf || !changedSelf) {
345
- return false;
346
- }
347
-
348
- return !isEqual(oldSelf.roles, changedSelf.roles);
349
- };
350
341
  /**
342
+ * determine whether the roles of self is changed or not
351
343
  * @param {Object} oldSelf
352
344
  * @param {Object} changedSelf
353
345
  * @returns {Boolean}
354
- * @throws {Error} if changed self was undefined
355
346
  */
356
- SelfUtils.isUpgradeToModeratorOrCohost = (oldSelf, changedSelf) => {
357
- if (!oldSelf) {
358
- return false;
359
- }
347
+ SelfUtils.isRolesChanged = (oldSelf, changedSelf) => {
360
348
  if (!changedSelf) {
361
- throw new ParameterError(
362
- 'New self must be defined to determine if self transitioned moderator or cohost status.'
363
- );
349
+ // no new self means no change
350
+ return false;
364
351
  }
365
- const isAttendeeOnly =
366
- oldSelf.roles.includes(SELF_ROLES.ATTENDEE) &&
367
- !oldSelf.roles.includes(SELF_ROLES.COHOST) &&
368
- !oldSelf.roles.includes(SELF_ROLES.MODERATOR);
369
- const isCohost = changedSelf.roles.includes(SELF_ROLES.COHOST);
370
- const isModerator = changedSelf.roles.includes(SELF_ROLES.MODERATOR);
371
-
372
- return isAttendeeOnly && (isCohost || isModerator);
373
- };
374
352
 
353
+ return !isEqual(oldSelf?.roles, changedSelf?.roles);
354
+ };
375
355
  /**
376
356
  * @param {Object} oldSelf
377
357
  * @param {Object} changedSelf
@@ -1523,6 +1523,17 @@ export default class Meeting extends StatelessWebexPlugin {
1523
1523
  helpEvent
1524
1524
  );
1525
1525
  });
1526
+
1527
+ this.breakouts.on(BREAKOUTS.EVENTS.PRE_ASSIGNMENTS_UPDATE, () => {
1528
+ Trigger.trigger(
1529
+ this,
1530
+ {
1531
+ file: 'meeting/index',
1532
+ function: 'setUpBreakoutsListener',
1533
+ },
1534
+ EVENT_TRIGGERS.MEETING_BREAKOUTS_PRE_ASSIGNMENTS_UPDATE
1535
+ );
1536
+ });
1526
1537
  }
1527
1538
 
1528
1539
  /**
@@ -2922,6 +2933,11 @@ export default class Meeting extends StatelessWebexPlugin {
2922
2933
  });
2923
2934
 
2924
2935
  this.locusInfo.on(LOCUSINFO.EVENTS.SELF_ROLES_CHANGED, (payload) => {
2936
+ const isModeratorOrCohost =
2937
+ payload.newRoles?.includes(SELF_ROLES.MODERATOR) ||
2938
+ payload.newRoles?.includes(SELF_ROLES.COHOST);
2939
+ this.breakouts.updateCanManageBreakouts(isModeratorOrCohost);
2940
+
2925
2941
  Trigger.trigger(
2926
2942
  this,
2927
2943
  {
@@ -2935,12 +2951,6 @@ export default class Meeting extends StatelessWebexPlugin {
2935
2951
  );
2936
2952
  });
2937
2953
 
2938
- // We need to reinitialize when user upgrades to host or cohost
2939
- this.locusInfo.on(LOCUSINFO.EVENTS.SELF_MODERATOR_OR_COHOST_UPGRADE, (payload) => {
2940
- this.breakouts.queryPreAssignments(payload);
2941
- // ...
2942
- });
2943
-
2944
2954
  this.locusInfo.on(LOCUSINFO.EVENTS.SELF_IS_SHARING_BLOCKED_CHANGE, (payload) => {
2945
2955
  Trigger.trigger(
2946
2956
  this,
@@ -151,6 +151,13 @@ export class RemoteMediaGroup {
151
151
  throw new Error(`remote media object ${remoteMedia.id} not found in the group`);
152
152
  }
153
153
 
154
+ public setPreferLiveVideo(preferLiveVideo: boolean, commit: boolean) {
155
+ if (this.options.preferLiveVideo !== preferLiveVideo) {
156
+ this.options.preferLiveVideo = preferLiveVideo;
157
+ this.sendActiveSpeakerMediaRequest(commit);
158
+ }
159
+ }
160
+
154
161
  private sendActiveSpeakerMediaRequest(commit: boolean) {
155
162
  this.cancelActiveSpeakerMediaRequest(false);
156
163
 
@@ -491,6 +491,20 @@ export class RemoteMediaManager extends EventsScope {
491
491
  return this.currentLayoutId;
492
492
  }
493
493
 
494
+ /**
495
+ * sets the preferLiveVideo
496
+ */
497
+ public setPreferLiveVideo(preferLiveVideo: boolean) {
498
+ LoggerProxy.logger.log(
499
+ `RemoteMediaManager#setPreferLiveVideo --> setPreferLiveVideo is called to set preferLiveVideo to ${preferLiveVideo}`
500
+ );
501
+ this.config.video.preferLiveVideo = preferLiveVideo;
502
+ Object.values(this.media.video.activeSpeakerGroups).forEach((activeSpeakerGroup) => {
503
+ activeSpeakerGroup.setPreferLiveVideo(preferLiveVideo, false);
504
+ });
505
+ this.mediaRequestManagers.video.commit();
506
+ }
507
+
494
508
  /**
495
509
  * Creates the audio slots
496
510
  */
@@ -6,9 +6,7 @@ import sinon from 'sinon';
6
6
  import MockWebex from '@webex/test-helper-mock-webex';
7
7
  import testUtils from '../../../utils/testUtils';
8
8
  import BreakoutEditLockedError from '@webex/plugin-meetings/src/breakouts/edit-lock-error';
9
- import breakoutEvent from "../../../../src/breakouts/events";
10
- import SelfUtils from "../../../../src/locus-info/selfUtils";
11
- import { self } from "../locus-info/selfConstant";
9
+ import breakoutEvent from '../../../../src/breakouts/events';
12
10
 
13
11
  const getBOResponse = (status: string) => {
14
12
  return {
@@ -150,6 +148,16 @@ describe('plugin-meetings', () => {
150
148
  breakouts.breakouts.get('session1').set({requestedLastModifiedTime: "2023-05-09T17:16:01.000Z"});
151
149
  assert.calledOnceWithExactly(breakouts.triggerReturnToMainEvent, breakouts.breakouts.get('session1'));
152
150
  });
151
+
152
+ it('call queryPreAssignments correctly when should query preAssignments is true', () => {
153
+ breakouts.queryPreAssignments = sinon.stub();
154
+ breakouts.set({
155
+ canManageBreakouts: true,
156
+ enableBreakoutSession: true,
157
+ hasBreakoutPreAssignments: true,
158
+ });
159
+ assert.calledThrice(breakouts.queryPreAssignments);
160
+ });
153
161
  });
154
162
 
155
163
  describe('#listenToCurrentSessionTypeChange', () => {
@@ -448,6 +456,18 @@ describe('plugin-meetings', () => {
448
456
  });
449
457
  });
450
458
 
459
+ describe('#updateCanManageBreakouts', () => {
460
+ it('update canManageBreakouts', () => {
461
+ breakouts.updateCanManageBreakouts(true);
462
+
463
+ assert.equal(breakouts.canManageBreakouts, true);
464
+
465
+ breakouts.updateCanManageBreakouts(false);
466
+
467
+ assert.equal(breakouts.canManageBreakouts, false);
468
+ });
469
+ });
470
+
451
471
  describe('#cleanUp', () => {
452
472
  it('stops listening', () => {
453
473
  breakouts.stopListening = sinon.stub();
@@ -504,6 +524,18 @@ describe('plugin-meetings', () => {
504
524
  });
505
525
  });
506
526
 
527
+ describe('#shouldQueryPreAssignments', () => {
528
+ it('returns should query preAssignments depends on status', () => {
529
+ assert.equal(breakouts.shouldQueryPreAssignments, false);
530
+ breakouts.set('canManageBreakouts', true);
531
+ assert.equal(breakouts.shouldQueryPreAssignments, false);
532
+ breakouts.set('enableBreakoutSession', true);
533
+ assert.equal(breakouts.shouldQueryPreAssignments, false);
534
+ breakouts.set('hasBreakoutPreAssignments', true);
535
+ assert.equal(breakouts.shouldQueryPreAssignments, true);
536
+ });
537
+ });
538
+
507
539
  describe('#breakoutStatus', () => {
508
540
  it('return status from groups with session type', () => {
509
541
  breakouts.set('groups', [{status: "OPEN"}]);
@@ -1453,58 +1485,50 @@ describe('plugin-meetings', () => {
1453
1485
  });
1454
1486
  });
1455
1487
 
1456
- describe('queryPreAssignments', () => {
1488
+ describe('#queryPreAssignments', () => {
1457
1489
  it('makes the expected query', async () => {
1458
- webex.request.returns(
1459
- Promise.resolve({
1460
- body: {
1461
- groups: [
1490
+ const mockPreAssignments = [
1491
+ {
1492
+ sessions: [
1462
1493
  {
1463
- sessions: [
1464
- {
1465
- name: 'Breakout session 1',
1466
- assignedEmails: ['a@a.com', 'b@b.com', 'jial2@cisco.com'],
1467
- anyoneCanJoin: false,
1468
- },
1469
- {
1470
- name: 'Breakout session 2',
1471
- anyoneCanJoin: false,
1472
- },
1473
- {
1474
- name: 'Breakout session 3',
1475
- assignedEmails: ['c@c.com'],
1476
- anyoneCanJoin: false,
1477
- },
1478
- ],
1479
- unassignedInvitees: {
1480
- emails: ['d@d.com'],
1481
- },
1482
- type: 'BREAKOUT',
1494
+ name: 'Breakout session 1',
1495
+ assignedEmails: ['aa@aa.com', 'bb@bb.com', 'cc@cc.com'],
1496
+ anyoneCanJoin: false,
1497
+ },
1498
+ {
1499
+ name: 'Breakout session 2',
1500
+ anyoneCanJoin: false,
1501
+ },
1502
+ {
1503
+ name: 'Breakout session 3',
1504
+ assignedEmails: ['cc@cc.com'],
1505
+ anyoneCanJoin: false,
1483
1506
  },
1484
1507
  ],
1508
+ unassignedInvitees: {
1509
+ emails: ['dd@dd.com'],
1510
+ },
1511
+ type: 'BREAKOUT',
1512
+ },
1513
+ ];
1514
+ webex.request.returns(
1515
+ Promise.resolve({
1516
+ body: {
1517
+ groups: mockPreAssignments,
1485
1518
  },
1486
1519
  })
1487
1520
  );
1488
- breakouts.shouldFetchPreassignments = false;
1489
- const result = await breakouts.queryPreAssignments({enableBreakoutSession: true, hasBreakoutPreAssignments: true});
1490
- const arg = webex.request.getCall(0).args[0];
1491
- assert.equal(arg.uri, 'url/preassignments');
1492
- assert.equal(breakouts.preAssignments[0].unassignedInvitees.emails[0], 'd@d.com');
1493
- assert.equal(breakouts.preAssignments[0].sessions[0].name, 'Breakout session 1');
1494
- assert.equal(breakouts.preAssignments[0].sessions[0].anyoneCanJoin, false);
1495
- assert.equal(
1496
- breakouts.preAssignments[0].sessions[0].assignedEmails.toString(),
1497
- ['a@a.com', 'b@b.com', 'jial2@cisco.com'].toString()
1498
- );
1499
- assert.equal(breakouts.preAssignments[0].sessions[1].name, 'Breakout session 2');
1500
- assert.equal(breakouts.preAssignments[0].sessions[1].anyoneCanJoin, false);
1501
- assert.equal(breakouts.preAssignments[0].sessions[1].assignedEmails, undefined);
1502
- assert.equal(breakouts.preAssignments[0].sessions[2].name, 'Breakout session 3');
1503
- assert.equal(breakouts.preAssignments[0].sessions[2].anyoneCanJoin, false);
1504
- assert.equal(breakouts.preAssignments[0].sessions[2].assignedEmails[0], 'c@c.com');
1505
- assert.equal(breakouts.preAssignments[0].unassignedInvitees.emails[0], 'd@d.com');
1506
- assert.equal(breakouts.preAssignments[0].type, 'BREAKOUT');
1507
- assert.equal(breakouts.shouldFetchPreassignments, true);
1521
+ breakouts.set('locusUrl', 'test');
1522
+
1523
+ await breakouts.queryPreAssignments();
1524
+ assert.calledOnceWithExactly(webex.request, {
1525
+ uri: 'url/preassignments',
1526
+ qs: {
1527
+ locusUrl: 'dGVzdA==',
1528
+ }
1529
+ });
1530
+
1531
+ assert.deepEqual(breakouts.preAssignments, mockPreAssignments);
1508
1532
  });
1509
1533
 
1510
1534
  it('rejects when no pre-assignments created for this meeting', async () => {
@@ -971,27 +971,6 @@ describe('plugin-meetings', () => {
971
971
  );
972
972
  });
973
973
 
974
- it('should trigger upgradeToModeratorOrCohost for breakouts', () => {
975
-
976
- locusInfo.self = self;
977
- const upgradeToModeratorOrCohost = cloneDeep(self);
978
- upgradeToModeratorOrCohost.roles = ['ATTENDEE','COHOST'];
979
-
980
- locusInfo.webex.internal.device.url = self.deviceUrl;
981
- locusInfo.emitScoped = sinon.stub();
982
- locusInfo.updateSelf(upgradeToModeratorOrCohost, []);
983
-
984
- assert.neverCalledWith(
985
- locusInfo.emitScoped,
986
- {
987
- file: 'locus-info',
988
- function: 'updateSelf',
989
- },
990
- LOCUSINFO.EVENTS.SELF_MODERATOR_OR_COHOST_UPGRADE,
991
- self
992
- );
993
- });
994
-
995
974
  it('should trigger SELF_REMOTE_MUTE_STATUS_UPDATED if muted and disallowUnmute changed', () => {
996
975
  locusInfo.self = self;
997
976
  const selfWithMutedByOthersAndDissalowUnmute = cloneDeep(self);
@@ -316,43 +316,6 @@ describe('plugin-meetings', () => {
316
316
  });
317
317
  });
318
318
 
319
- describe('isUpgradeToModeratorOrCohost', () => {
320
- it('returns true if changed', () => {
321
- assert.equal(
322
- SelfUtils.isUpgradeToModeratorOrCohost({roles: ['ATTENDEE']}, {roles: ['ATTENDEE','MODERATOR']}),
323
- true
324
- );
325
- });
326
-
327
- it('returns true if changed', () => {
328
- assert.equal(
329
- SelfUtils.isUpgradeToModeratorOrCohost({roles: ['ATTENDEE']}, {roles: ['ATTENDEE','COHOST']}),
330
- true
331
- );
332
- });
333
-
334
- it('returns false if changed', () => {
335
- assert.equal(
336
- SelfUtils.isUpgradeToModeratorOrCohost({roles: ['ATTENDEE','MODERATOR']}, {roles: ['ATTENDEE']}),
337
- false
338
- );
339
- });
340
-
341
- it('returns false if changed', () => {
342
- assert.equal(
343
- SelfUtils.isUpgradeToModeratorOrCohost({roles: ['ATTENDEE','COHOST']}, {roles: ['ATTENDEE']}),
344
- false
345
- );
346
- });
347
-
348
- it('returns false if changed', () => {
349
- assert.equal(
350
- SelfUtils.isUpgradeToModeratorOrCohost({roles: ['ATTENDEE','HOST','MODERATOR']}, {roles: ['ATTENDEE']}),
351
- false
352
- );
353
- });
354
- });
355
-
356
319
  describe('getReplacedBreakoutMoveId', () => {
357
320
  const deviceId = 'https://wdm-a.wbx2.com/wdm/api/v1/devices/20eabde3-4254-48da-9a24';
358
321
  const breakoutMoveId = 'e5caeb2c-ffcc-4e06-a08a-1122e7710398';
@@ -395,6 +358,12 @@ describe('plugin-meetings', () => {
395
358
  });
396
359
 
397
360
  describe('isRolesChanged', () => {
361
+ it('should return false if new self is null', () => {
362
+ const parsedSelf = SelfUtils.parse(self);
363
+
364
+ assert.deepEqual(SelfUtils.isRolesChanged(parsedSelf, null), false);
365
+ });
366
+
398
367
  it('should return true if self roles has changed', () => {
399
368
  const parsedSelf = SelfUtils.parse(self);
400
369
  const clonedSelf = cloneDeep(parsedSelf);
@@ -4867,6 +4867,7 @@ describe('plugin-meetings', () => {
4867
4867
 
4868
4868
  it('listens to the self roles changed event', () => {
4869
4869
  const payload = {oldRoles: [], newRoles: ['COHOST']};
4870
+ meeting.breakouts.updateCanManageBreakouts = sinon.stub();
4870
4871
 
4871
4872
  meeting.locusInfo.emit(
4872
4873
  {function: 'test', file: 'test'},
@@ -4874,6 +4875,7 @@ describe('plugin-meetings', () => {
4874
4875
  payload
4875
4876
  );
4876
4877
 
4878
+ assert.calledOnceWithExactly(meeting.breakouts.updateCanManageBreakouts, true);
4877
4879
  assert.calledWith(
4878
4880
  TriggerProxy.trigger,
4879
4881
  meeting,
@@ -4884,19 +4886,6 @@ describe('plugin-meetings', () => {
4884
4886
  });
4885
4887
  });
4886
4888
 
4887
- describe('#setUpBreakoutsPreAssignmentsListener', () => {
4888
- it('listens to the self moderator or cohost upgrade event', () => {
4889
- meeting.breakouts.queryPreAssignments = sinon.stub();
4890
- const payload = 'payload';
4891
- meeting.locusInfo.emit(
4892
- {function: 'test', file: 'test'},
4893
- 'SELF_MODERATOR_OR_COHOST_UPGRADE',
4894
- payload,
4895
- );
4896
- assert.calledOnceWithExactly(meeting.breakouts.queryPreAssignments, payload);
4897
- });
4898
- });
4899
-
4900
4889
  describe('#setUpBreakoutsListener', () => {
4901
4890
  it('listens to the closing event from breakouts and triggers the closing event', () => {
4902
4891
  TriggerProxy.trigger.reset();
@@ -4972,6 +4961,18 @@ describe('plugin-meetings', () => {
4972
4961
  helpEvent
4973
4962
  );
4974
4963
  });
4964
+
4965
+ it('listens to the preAssignments update event from breakouts and triggers the update event', () => {
4966
+ TriggerProxy.trigger.reset();
4967
+ meeting.breakouts.trigger('PRE_ASSIGNMENTS_UPDATE');
4968
+
4969
+ assert.calledWith(
4970
+ TriggerProxy.trigger,
4971
+ meeting,
4972
+ {file: 'meeting/index', function: 'setUpBreakoutsListener'},
4973
+ EVENT_TRIGGERS.MEETING_BREAKOUTS_PRE_ASSIGNMENTS_UPDATE
4974
+ );
4975
+ });
4975
4976
  });
4976
4977
 
4977
4978
  describe('#setupLocusControlsListener', () => {
@@ -5881,7 +5882,7 @@ describe('plugin-meetings', () => {
5881
5882
  it('connects if not already connected', async () => {
5882
5883
  meeting.joinedWith = {state: 'JOINED'};
5883
5884
  meeting.locusInfo = {url: 'a url', info: {datachannelUrl: 'a datachannel url'}};
5884
-
5885
+
5885
5886
 
5886
5887
  const result = await meeting.updateLLMConnection();
5887
5888