@webex/plugin-meetings 2.38.1 → 2.39.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webex/plugin-meetings",
3
- "version": "2.38.1",
3
+ "version": "2.39.0",
4
4
  "description": "",
5
5
  "license": "Cisco EULA (https://www.cisco.com/c/en/us/products/end-user-license-agreement.html)",
6
6
  "contributors": [
@@ -32,12 +32,12 @@
32
32
  "build": "yarn run -T tsc --declaration true --declarationDir ./types"
33
33
  },
34
34
  "devDependencies": {
35
- "@webex/plugin-meetings": "2.38.1",
36
- "@webex/test-helper-chai": "2.38.1",
37
- "@webex/test-helper-mocha": "2.38.1",
38
- "@webex/test-helper-mock-webex": "2.38.1",
39
- "@webex/test-helper-retry": "2.38.1",
40
- "@webex/test-helper-test-users": "2.38.1",
35
+ "@webex/plugin-meetings": "2.39.0",
36
+ "@webex/test-helper-chai": "2.39.0",
37
+ "@webex/test-helper-mocha": "2.39.0",
38
+ "@webex/test-helper-mock-webex": "2.39.0",
39
+ "@webex/test-helper-retry": "2.39.0",
40
+ "@webex/test-helper-test-users": "2.39.0",
41
41
  "chai": "^4.3.4",
42
42
  "chai-as-promised": "^7.1.1",
43
43
  "jsdom-global": "3.0.2",
@@ -45,18 +45,18 @@
45
45
  "typescript": "^4.7.4"
46
46
  },
47
47
  "dependencies": {
48
- "@webex/common": "2.38.1",
48
+ "@webex/common": "2.39.0",
49
49
  "@webex/internal-media-core": "0.0.7-beta",
50
- "@webex/internal-plugin-conversation": "2.38.1",
51
- "@webex/internal-plugin-device": "2.38.1",
52
- "@webex/internal-plugin-mercury": "2.38.1",
53
- "@webex/internal-plugin-metrics": "2.38.1",
54
- "@webex/internal-plugin-support": "2.38.1",
55
- "@webex/internal-plugin-user": "2.38.1",
56
- "@webex/plugin-people": "2.38.1",
57
- "@webex/plugin-rooms": "2.38.1",
50
+ "@webex/internal-plugin-conversation": "2.39.0",
51
+ "@webex/internal-plugin-device": "2.39.0",
52
+ "@webex/internal-plugin-mercury": "2.39.0",
53
+ "@webex/internal-plugin-metrics": "2.39.0",
54
+ "@webex/internal-plugin-support": "2.39.0",
55
+ "@webex/internal-plugin-user": "2.39.0",
56
+ "@webex/plugin-people": "2.39.0",
57
+ "@webex/plugin-rooms": "2.39.0",
58
58
  "@webex/ts-sdp": "1.0.1",
59
- "@webex/webex-core": "2.38.1",
59
+ "@webex/webex-core": "2.39.0",
60
60
  "bowser": "^2.11.0",
61
61
  "btoa": "^1.2.1",
62
62
  "dotenv": "^4.0.0",
package/src/constants.ts CHANGED
@@ -724,6 +724,10 @@ export const DISPLAY_HINTS = {
724
724
  TRANSCRIPTION_CONTROL_STOP: 'TRANSCRIPTION_CONTROL_STOP',
725
725
  WEBEX_ASSISTANT_STATUS_ACTIVE: 'WEBEX_ASSISTANT_STATUS_ACTIVE',
726
726
  WAITING_FOR_OTHERS: 'WAITING_FOR_OTHERS',
727
+ ENABLE_MUTE_ON_ENTRY: 'ENABLE_MUTE_ON_ENTRY',
728
+ DISABLE_MUTE_ON_ENTRY: 'DISABLE_MUTE_ON_ENTRY',
729
+ ENABLE_HARD_MUTE: 'ENABLE_HARD_MUTE',
730
+ DISABLE_HARD_MUTE: 'DISABLE_HARD_MUTE',
727
731
  };
728
732
 
729
733
  export const SELF_ROLES = {
@@ -0,0 +1,5 @@
1
+ const ENABLED = 'enabled';
2
+ const CAN_SET = 'canSet';
3
+ const CAN_UNSET = 'canUnset';
4
+
5
+ export {ENABLED, CAN_SET, CAN_UNSET};
@@ -0,0 +1,6 @@
1
+ enum Setting {
2
+ disallowUnmute = 'DisallowUnmute',
3
+ muteOnEntry = 'MuteOnEntry',
4
+ }
5
+
6
+ export default Setting;
@@ -0,0 +1,183 @@
1
+ import {camelCase} from 'lodash';
2
+ import PermissionError from '../common/errors/permission';
3
+ import {CONTROLS, HTTP_VERBS} from '../constants';
4
+ import MeetingRequest from '../meeting/request';
5
+ import LoggerProxy from '../common/logs/logger-proxy';
6
+ import Setting from './enums';
7
+ import Util from './util';
8
+ import {CAN_SET, CAN_UNSET, ENABLED} from './constants';
9
+
10
+ /**
11
+ * docs
12
+ * https://sqbu-github.cisco.com/pages/WebExSquared/locus/guides/mute.html
13
+ * https://confluence-eng-gpk2.cisco.com/conf/display/LOCUS/Hard+Mute+and+Audio+Privacy#HardMuteandAudioPrivacy-SelfMuteonEntry
14
+ * https://confluence-eng-gpk2.cisco.com/conf/pages/viewpage.action?spaceKey=UC&title=WEBEX-124454%3A+UCF%3A+Hard+mute+support+for+Teams+joining+Webex+meeting
15
+ * https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-180867
16
+ * https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-393351
17
+ */
18
+
19
+ /**
20
+ * @description ControlsOptionsManager is responsible for handling the behavior of participant controls when somebody joins a meeting
21
+ * @export
22
+ * @private
23
+ * @class Recording
24
+ */
25
+ export default class ControlsOptionsManager {
26
+ /**
27
+ * @instance
28
+ * @type {MeetingRequest}
29
+ * @private
30
+ * @memberof ControlsOptionsManager
31
+ */
32
+ private request: MeetingRequest;
33
+
34
+ /**
35
+ * @instance
36
+ * @type {Array}
37
+ * @private
38
+ * @memberof ControlsOptionsManager
39
+ */
40
+ private displayHints: Array<string> = [];
41
+
42
+ /**
43
+ * @instance
44
+ * @type {string}
45
+ * @private
46
+ * @memberof ControlsOptionsManager
47
+ */
48
+ private locusUrl: string;
49
+
50
+ /**
51
+ * @param {MeetingRequest} request
52
+ * @param {Object} options
53
+ * @constructor
54
+ * @memberof ControlsOptionsManager
55
+ */
56
+ constructor(
57
+ request: MeetingRequest,
58
+ options?: {
59
+ locusUrl: string;
60
+ displayHints?: Array<string>;
61
+ }
62
+ ) {
63
+ this.initialize(request);
64
+ this.set(options);
65
+ }
66
+
67
+ /**
68
+ * @param {MeetingRequest} request
69
+ * @returns {void}
70
+ * @private
71
+ * @memberof ControlsOptionsManager
72
+ */
73
+ private initialize(request: MeetingRequest) {
74
+ this.request = request;
75
+ }
76
+
77
+ /**
78
+ * @param {Object} options
79
+ * @returns {void}
80
+ * @public
81
+ * @memberof ControlsOptionsManager
82
+ */
83
+ public set(options?: {locusUrl: string; displayHints?: Array<string>}) {
84
+ this.extract(options);
85
+ }
86
+
87
+ /**
88
+ * @param {string} url
89
+ * @returns {void}
90
+ * @public
91
+ * @memberof ControlsOptionsManager
92
+ */
93
+ public setLocusUrl(url: string) {
94
+ this.locusUrl = url;
95
+ }
96
+
97
+ /**
98
+ * @param {Array} hints
99
+ * @returns {void}
100
+ * @public
101
+ * @memberof ControlsOptionsManager
102
+ */
103
+ public setDisplayHints(hints: Array<string>) {
104
+ this.displayHints = hints;
105
+ }
106
+
107
+ /**
108
+ * @returns {string}
109
+ * @public
110
+ * @memberof ControlsOptionsManager
111
+ */
112
+ public getLocusUrl() {
113
+ return this.locusUrl;
114
+ }
115
+
116
+ /**
117
+ * @returns {Array}
118
+ * @public
119
+ * @memberof ControlsOptionsManager
120
+ */
121
+ public getDisplayHints() {
122
+ return this.displayHints;
123
+ }
124
+
125
+ /**
126
+ * @param {Object} options
127
+ * @returns {void}
128
+ * @private
129
+ * @memberof ControlsOptionsManager
130
+ */
131
+ private extract(options?: {locusUrl: string; displayHints?: Array<string>}) {
132
+ this.setDisplayHints(options?.displayHints);
133
+ this.setLocusUrl(options?.locusUrl);
134
+ }
135
+
136
+ /**
137
+ * @param {Setting} setting
138
+ * @param {boolean} enabled
139
+ * @private
140
+ * @memberof ControlsOptionsManager
141
+ * @returns {Promise}
142
+ */
143
+ private setControls(setting: Setting, enabled: boolean): Promise<any> {
144
+ LoggerProxy.logger.log(`ControlsOptionsManager:index#setControls --> ${setting} [${enabled}]`);
145
+
146
+ if (Util?.[`${enabled ? CAN_SET : CAN_UNSET}${setting}`](this.displayHints)) {
147
+ // @ts-ignore
148
+ return this.request.request({
149
+ uri: `${this.locusUrl}/${CONTROLS}`,
150
+ body: {
151
+ [camelCase(setting)]: {
152
+ [ENABLED]: enabled,
153
+ },
154
+ },
155
+ method: HTTP_VERBS.PATCH,
156
+ });
157
+ }
158
+
159
+ return Promise.reject(
160
+ new PermissionError(`${setting} [${enabled}] not allowed, due to moderator property.`)
161
+ );
162
+ }
163
+
164
+ /**
165
+ * @public
166
+ * @param {boolean} enabled
167
+ * @memberof ControlsOptionsManager
168
+ * @returns {Promise}
169
+ */
170
+ public setMuteOnEntry(enabled: boolean): Promise<any> {
171
+ return this.setControls(Setting.muteOnEntry, enabled);
172
+ }
173
+
174
+ /**
175
+ * @public
176
+ * @param {boolean} enabled
177
+ * @memberof ControlsOptionsManager
178
+ * @returns {Promise}
179
+ */
180
+ public setDisallowUnmute(enabled: boolean): Promise<any> {
181
+ return this.setControls(Setting.disallowUnmute, enabled);
182
+ }
183
+ }
@@ -0,0 +1,20 @@
1
+ import {DISPLAY_HINTS} from '../constants';
2
+
3
+ const canSetMuteOnEntry = (displayHints: Array<string>): boolean =>
4
+ displayHints.includes(DISPLAY_HINTS.ENABLE_MUTE_ON_ENTRY);
5
+
6
+ const canSetDisallowUnmute = (displayHints: Array<string>): boolean =>
7
+ displayHints.includes(DISPLAY_HINTS.ENABLE_HARD_MUTE);
8
+
9
+ const canUnsetMuteOnEntry = (displayHints: Array<string>): boolean =>
10
+ displayHints.includes(DISPLAY_HINTS.DISABLE_MUTE_ON_ENTRY);
11
+
12
+ const canUnsetDisallowUnmute = (displayHints: Array<string>): boolean =>
13
+ displayHints.includes(DISPLAY_HINTS.DISABLE_HARD_MUTE);
14
+
15
+ export default {
16
+ canSetMuteOnEntry,
17
+ canSetDisallowUnmute,
18
+ canUnsetMuteOnEntry,
19
+ canUnsetDisallowUnmute,
20
+ };
@@ -13,6 +13,10 @@ interface IInMeetingActions {
13
13
  canAdmitParticipant?: boolean;
14
14
  canLock?: boolean;
15
15
  canUnlock?: boolean;
16
+ canSetMuteOnEntry?: boolean;
17
+ canUnsetMuteOnEntry?: boolean;
18
+ canSetDisallowUnmute?: boolean;
19
+ canUnsetDisallowUnmute?: boolean;
16
20
  canAssignHost?: boolean;
17
21
  canStartRecording?: boolean;
18
22
  canPauseRecording?: boolean;
@@ -57,6 +61,14 @@ export default class InMeetingActions implements IInMeetingActions {
57
61
 
58
62
  canStopRecording = null;
59
63
 
64
+ canSetMuteOnEntry = null;
65
+
66
+ canUnsetMuteOnEntry = null;
67
+
68
+ canSetDisallowUnmute = null;
69
+
70
+ canUnsetDisallowUnmute = null;
71
+
60
72
  canRaiseHand = null;
61
73
 
62
74
  canLowerAllHands = null;
@@ -93,6 +105,10 @@ export default class InMeetingActions implements IInMeetingActions {
93
105
  canLock: this.canLock,
94
106
  canUnlock: this.canUnlock,
95
107
  canAssignHost: this.canAssignHost,
108
+ canSetMuteOnEntry: this.canSetMuteOnEntry,
109
+ canUnsetMuteOnEntry: this.canUnsetMuteOnEntry,
110
+ canSetDisallowUnmute: this.canSetDisallowUnmute,
111
+ canUnsetDisallowUnmute: this.canUnsetDisallowUnmute,
96
112
  canStartRecording: this.canStartRecording,
97
113
  canPauseRecording: this.canPauseRecording,
98
114
  canResumeRecording: this.canResumeRecording,
@@ -31,6 +31,7 @@ import MeetingRequest from './request';
31
31
  import Members from '../members/index';
32
32
  import MeetingUtil from './util';
33
33
  import RecordingUtil from '../recording-controller/util';
34
+ import ControlsOptionsUtil from '../controls-options-manager/util';
34
35
  import MediaUtil from '../media/util';
35
36
  import Transcription from '../transcription';
36
37
  import PasswordError from '../common/errors/password-error';
@@ -90,6 +91,7 @@ import {Reaction, ReactionType, SkinToneType} from '../reactions/reactions.type'
90
91
 
91
92
  import InMeetingActions from './in-meeting-actions';
92
93
  import RecordingController from '../recording-controller';
94
+ import ControlsOptionsManager from '../controls-options-manager';
93
95
 
94
96
  const {isBrowser} = BrowserDetection();
95
97
 
@@ -455,6 +457,7 @@ export default class Meeting extends StatelessWebexPlugin {
455
457
  queuedMediaUpdates: any[];
456
458
  recording: any;
457
459
  recordingController: RecordingController;
460
+ controlsOptionsManager: ControlsOptionsManager;
458
461
  requiredCaptcha: any;
459
462
  shareStatus: string;
460
463
  statsAnalyzer: StatsAnalyzer;
@@ -1002,6 +1005,18 @@ export default class Meeting extends StatelessWebexPlugin {
1002
1005
  displayHints: [],
1003
1006
  });
1004
1007
 
1008
+ /**
1009
+ * The class that helps to control recording functions: start, stop, pause, resume, etc
1010
+ * @instance
1011
+ * @type {ControlsOptionsManager}
1012
+ * @public
1013
+ * @memberof Meeting
1014
+ */
1015
+ this.controlsOptionsManager = new ControlsOptionsManager(this.meetingRequest, {
1016
+ locusUrl: this.locusInfo?.url,
1017
+ displayHints: [],
1018
+ });
1019
+
1005
1020
  this.setUpLocusInfoListeners();
1006
1021
  this.locusInfo.init(attrs.locus ? attrs.locus : {});
1007
1022
  this.hasJoinedOnce = false;
@@ -2020,6 +2035,7 @@ export default class Meeting extends StatelessWebexPlugin {
2020
2035
  this.locusUrl = payload;
2021
2036
  this.locusId = this.locusUrl?.split('/').pop();
2022
2037
  this.recordingController.setLocusUrl(this.locusUrl);
2038
+ this.controlsOptionsManager.setLocusUrl(this.locusUrl);
2023
2039
  });
2024
2040
  }
2025
2041
 
@@ -2085,6 +2101,16 @@ export default class Meeting extends StatelessWebexPlugin {
2085
2101
  canAdmitParticipant: MeetingUtil.canAdmitParticipant(payload.info.userDisplayHints),
2086
2102
  canLock: MeetingUtil.canUserLock(payload.info.userDisplayHints),
2087
2103
  canUnlock: MeetingUtil.canUserUnlock(payload.info.userDisplayHints),
2104
+ canSetDisallowUnmute: ControlsOptionsUtil.canSetDisallowUnmute(
2105
+ payload.info.userDisplayHints
2106
+ ),
2107
+ canUnsetDisallowUnmute: ControlsOptionsUtil.canUnsetDisallowUnmute(
2108
+ payload.info.userDisplayHints
2109
+ ),
2110
+ canSetMuteOnEntry: ControlsOptionsUtil.canSetMuteOnEntry(payload.info.userDisplayHints),
2111
+ canUnsetMuteOnEntry: ControlsOptionsUtil.canUnsetMuteOnEntry(
2112
+ payload.info.userDisplayHints
2113
+ ),
2088
2114
  canStartRecording: RecordingUtil.canUserStart(payload.info.userDisplayHints),
2089
2115
  canStopRecording: RecordingUtil.canUserStop(payload.info.userDisplayHints),
2090
2116
  canPauseRecording: RecordingUtil.canUserPause(payload.info.userDisplayHints),
@@ -2113,6 +2139,7 @@ export default class Meeting extends StatelessWebexPlugin {
2113
2139
  });
2114
2140
 
2115
2141
  this.recordingController.setDisplayHints(payload.info.userDisplayHints);
2142
+ this.controlsOptionsManager.setDisplayHints(payload.info.userDisplayHints);
2116
2143
 
2117
2144
  if (changed) {
2118
2145
  Trigger.trigger(
@@ -4900,32 +4927,6 @@ export default class Meeting extends StatelessWebexPlugin {
4900
4927
  )
4901
4928
  )
4902
4929
  .then(() => this.checkForStopShare(mediaSettings.sendShare, previousSendShareStatus))
4903
- .then((startShare) => {
4904
- // This is a special case if we do an /floor grant followed by /media
4905
- // we actually get a OFFER from the server and a GLAR condition happens
4906
- if (startShare) {
4907
- // We are assuming that the clients are connected when doing an update
4908
- // @ts-ignore
4909
- return this.share();
4910
- }
4911
-
4912
- return Promise.resolve();
4913
- })
4914
- .then(() =>
4915
- logRequest(
4916
- this.roap.sendRoapMediaRequest({
4917
- sdp: this.mediaProperties.peerConnection.sdp,
4918
- roapSeq: this.roapSeq,
4919
- meeting: this, // or can pass meeting ID
4920
- }),
4921
- {
4922
- header: `${LOG_HEADER} sendRoapMediaRequest being sent`,
4923
- success: `${LOG_HEADER} sendRoadMediaRequest successful`,
4924
- failure: `${LOG_HEADER} Error updateMedia on send roap media request, `,
4925
- }
4926
- )
4927
- )
4928
- .then(() => this.checkForStopShare(mediaSettings.sendShare, previousSendShareStatus))
4929
4930
  .then((startShare) => {
4930
4931
  // This is a special case if we do an /floor grant followed by /media
4931
4932
  // we actually get a OFFER from the server and a GLAR condition happens
@@ -5552,6 +5553,28 @@ export default class Meeting extends StatelessWebexPlugin {
5552
5553
  return this.recordingController.startRecording();
5553
5554
  }
5554
5555
 
5556
+ /**
5557
+ * set the mute on entry flag for participants if you're the host
5558
+ * @returns {Promise}
5559
+ * @param {boolean} enabled
5560
+ * @public
5561
+ * @memberof Meeting
5562
+ */
5563
+ public setMuteOnEntry(enabled: boolean) {
5564
+ return this.controlsOptionsManager.setMuteOnEntry(enabled);
5565
+ }
5566
+
5567
+ /**
5568
+ * set the disallow unmute flag for participants if you're the host
5569
+ * @returns {Promise}
5570
+ * @param {boolean} enabled
5571
+ * @public
5572
+ * @memberof Meeting
5573
+ */
5574
+ public setDisallowUnmute(enabled: boolean) {
5575
+ return this.controlsOptionsManager.setDisallowUnmute(enabled);
5576
+ }
5577
+
5555
5578
  /**
5556
5579
  * End the recording of this meeting
5557
5580
  * @returns {Promise}
@@ -0,0 +1,124 @@
1
+ import ControlsOptionsManager from '@webex/plugin-meetings/src/controls-options-manager';
2
+ import sinon from 'sinon';
3
+ import {assert} from '@webex/test-helper-chai';
4
+ import { HTTP_VERBS } from '@webex/plugin-meetings/src/constants';
5
+
6
+ describe('plugin-meetings', () => {
7
+ describe('controls-options-manager tests', () => {
8
+ describe('index', () => {
9
+ let request;
10
+
11
+ describe('class tests', () => {
12
+ it('can set and extract new values later on', () => {
13
+ const manager = new ControlsOptionsManager({});
14
+ assert.isUndefined(manager.getLocusUrl());
15
+ manager.set({
16
+ locusUrl: 'test/id',
17
+ })
18
+ assert(manager.getLocusUrl(), 'test/id');
19
+ });
20
+ });
21
+
22
+ describe('Mute On Entry', () => {
23
+ let manager;
24
+
25
+ beforeEach(() => {
26
+ request = {
27
+ request: sinon.stub().returns(Promise.resolve()),
28
+ };
29
+
30
+ manager = new ControlsOptionsManager(request);
31
+
32
+ manager.set({
33
+ locusUrl: 'test/id',
34
+ displayHints: [],
35
+ })
36
+ });
37
+
38
+ describe('setMuteOnEntry', () => {
39
+ it('rejects when correct display hint is not present enabled=false', () => {
40
+ const result = manager.setMuteOnEntry(false);
41
+
42
+ assert.notCalled(request.request);
43
+
44
+ assert.isRejected(result);
45
+ });
46
+
47
+ it('rejects when correct display hint is not present enabled=true', () => {
48
+ const result = manager.setMuteOnEntry(true);
49
+
50
+ assert.notCalled(request.request);
51
+
52
+ assert.isRejected(result);
53
+ });
54
+
55
+ it('can set mute on entry when the display hint is available enabled=true', () => {
56
+ manager.setDisplayHints(['ENABLE_MUTE_ON_ENTRY']);
57
+
58
+ const result = manager.setMuteOnEntry(true);
59
+
60
+ assert.calledWith(request.request, { uri: 'test/id/controls',
61
+ body: { muteOnEntry: { enabled: true } },
62
+ method: HTTP_VERBS.PATCH});
63
+
64
+ assert.deepEqual(result, request.request.firstCall.returnValue);
65
+ });
66
+
67
+ it('can set mute on entry when the display hint is available enabled=false', () => {
68
+ manager.setDisplayHints(['DISABLE_MUTE_ON_ENTRY']);
69
+
70
+ const result = manager.setMuteOnEntry(false);
71
+
72
+ assert.calledWith(request.request, { uri: 'test/id/controls',
73
+ body: { muteOnEntry: { enabled: false } },
74
+ method: HTTP_VERBS.PATCH});
75
+
76
+ assert.deepEqual(result, request.request.firstCall.returnValue);
77
+ });
78
+ });
79
+
80
+ describe('setDisallowUnmute', () => {
81
+ it('rejects when correct display hint is not present enabled=false', () => {
82
+ const result = manager.setDisallowUnmute(false);
83
+
84
+ assert.notCalled(request.request);
85
+
86
+ assert.isRejected(result);
87
+ });
88
+
89
+ it('rejects when correct display hint is not present enabled=true', () => {
90
+ const result = manager.setDisallowUnmute(true);
91
+
92
+ assert.notCalled(request.request);
93
+
94
+ assert.isRejected(result);
95
+ });
96
+
97
+ it('can set mute on entry when the display hint is available enabled=true', () => {
98
+ manager.setDisplayHints(['ENABLE_HARD_MUTE']);
99
+
100
+ const result = manager.setDisallowUnmute(true);
101
+
102
+ assert.calledWith(request.request, { uri: 'test/id/controls',
103
+ body: { disallowUnmute: { enabled: true } },
104
+ method: HTTP_VERBS.PATCH});
105
+
106
+ assert.deepEqual(result, request.request.firstCall.returnValue);
107
+ });
108
+
109
+ it('can set mute on entry when the display hint is available enabled=false', () => {
110
+ manager.setDisplayHints(['DISABLE_HARD_MUTE']);
111
+
112
+ const result = manager.setDisallowUnmute(false);
113
+
114
+ assert.calledWith(request.request, { uri: 'test/id/controls',
115
+ body: { disallowUnmute: { enabled: false } },
116
+ method: HTTP_VERBS.PATCH});
117
+
118
+ assert.deepEqual(result, request.request.firstCall.returnValue);
119
+ });
120
+ });
121
+ });
122
+ });
123
+ });
124
+ });
@@ -0,0 +1,66 @@
1
+ import ControlsOptionsUtil from '@webex/plugin-meetings/src/controls-options-manager/util';
2
+ import { assert } from 'chai';
3
+
4
+ describe('plugin-meetings', () => {
5
+ describe('controls-option-manager tests', () => {
6
+ describe('util tests', () => {
7
+
8
+ let locusInfo;
9
+
10
+ beforeEach(() => {
11
+ locusInfo = {
12
+ parsedLocus: {
13
+ info: {
14
+ userDisplayHints: [],
15
+ },
16
+ },
17
+ };
18
+ });
19
+
20
+ describe('canUserSetMuteOnEntry', () => {
21
+ it('can set mute on entry enable', () => {
22
+ locusInfo.parsedLocus.info.userDisplayHints.push('ENABLE_MUTE_ON_ENTRY');
23
+
24
+ assert.equal(ControlsOptionsUtil.canSetMuteOnEntry(locusInfo.parsedLocus.info.userDisplayHints), true);
25
+ });
26
+
27
+ it('can set mute on entry disable', () => {
28
+ locusInfo.parsedLocus.info.userDisplayHints.push('DISABLE_MUTE_ON_ENTRY');
29
+
30
+ assert.equal(ControlsOptionsUtil.canUnsetMuteOnEntry(locusInfo.parsedLocus.info.userDisplayHints), true);
31
+ });
32
+
33
+ it('rejects when correct display hint is not present for setting mute on entry', () => {
34
+ assert.equal(ControlsOptionsUtil.canSetMuteOnEntry(locusInfo.parsedLocus.info.userDisplayHints), false);
35
+ });
36
+
37
+ it('rejects when correct display hint is not present for unsetting mute on entry', () => {
38
+ assert.equal(ControlsOptionsUtil.canUnsetMuteOnEntry(locusInfo.parsedLocus.info.userDisplayHints), false);
39
+ });
40
+ });
41
+
42
+ describe('canSetDisallowUnmute', () => {
43
+ it('can set disallow unmute enable', () => {
44
+ locusInfo.parsedLocus.info.userDisplayHints.push('ENABLE_HARD_MUTE');
45
+
46
+ assert.equal(ControlsOptionsUtil.canSetDisallowUnmute(locusInfo.parsedLocus.info.userDisplayHints), true);
47
+ });
48
+
49
+ it('can set disallow unmute disable', () => {
50
+ locusInfo.parsedLocus.info.userDisplayHints.push('DISABLE_HARD_MUTE');
51
+
52
+ assert.equal(ControlsOptionsUtil.canUnsetDisallowUnmute(locusInfo.parsedLocus.info.userDisplayHints), true);
53
+ });
54
+
55
+ it('rejects when correct display hint is not present', () => {
56
+ assert.equal(ControlsOptionsUtil.canSetDisallowUnmute(locusInfo.parsedLocus.info.userDisplayHints), false);
57
+ });
58
+
59
+ it('rejects when correct display hint is not present', () => {
60
+ assert.equal(ControlsOptionsUtil.canUnsetDisallowUnmute(locusInfo.parsedLocus.info.userDisplayHints), false);
61
+ });
62
+ });
63
+
64
+ });
65
+ });
66
+ });
@@ -13,6 +13,10 @@ describe('plugin-meetings', () => {
13
13
  canStartRecording: null,
14
14
  canPauseRecording: null,
15
15
  canResumeRecording: null,
16
+ canSetMuteOnEntry: null,
17
+ canUnsetMuteOnEntry: null,
18
+ canSetDisallowUnmute: null,
19
+ canUnsetDisallowUnmute: null,
16
20
  canStopRecording: null,
17
21
  canRaiseHand: null,
18
22
  canLowerAllHands: null,
@@ -49,6 +53,10 @@ describe('plugin-meetings', () => {
49
53
  'canPauseRecording',
50
54
  'canResumeRecording',
51
55
  'canStopRecording',
56
+ 'canSetMuteOnEntry',
57
+ 'canUnsetMuteOnEntry',
58
+ 'canSetDisallowUnmute',
59
+ 'canUnsetDisallowUnmute',
52
60
  'canRaiseHand',
53
61
  'canLowerAllHands',
54
62
  'canLowerSomeoneElsesHand',