@webex/plugin-meetings 3.0.0-beta.35 → 3.0.0-beta.37

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.
Files changed (55) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/edit-lock-error.js +52 -0
  3. package/dist/breakouts/edit-lock-error.js.map +1 -0
  4. package/dist/breakouts/index.js +86 -2
  5. package/dist/breakouts/index.js.map +1 -1
  6. package/dist/constants.js +12 -1
  7. package/dist/constants.js.map +1 -1
  8. package/dist/media/index.js +4 -18
  9. package/dist/media/index.js.map +1 -1
  10. package/dist/media/properties.js +3 -3
  11. package/dist/media/properties.js.map +1 -1
  12. package/dist/meeting/index.js +194 -306
  13. package/dist/meeting/index.js.map +1 -1
  14. package/dist/meeting/muteState.js +7 -2
  15. package/dist/meeting/muteState.js.map +1 -1
  16. package/dist/meeting/util.js +2 -2
  17. package/dist/meeting/util.js.map +1 -1
  18. package/dist/metrics/constants.js +0 -4
  19. package/dist/metrics/constants.js.map +1 -1
  20. package/dist/reconnection-manager/index.js +1 -2
  21. package/dist/reconnection-manager/index.js.map +1 -1
  22. package/dist/statsAnalyzer/index.js +8 -4
  23. package/dist/statsAnalyzer/index.js.map +1 -1
  24. package/dist/types/breakouts/edit-lock-error.d.ts +15 -0
  25. package/dist/types/constants.d.ts +11 -0
  26. package/dist/types/media/properties.d.ts +7 -6
  27. package/dist/types/meeting/index.d.ts +11 -36
  28. package/dist/types/metrics/constants.d.ts +0 -4
  29. package/package.json +19 -19
  30. package/src/breakouts/README.md +8 -2
  31. package/src/breakouts/edit-lock-error.ts +25 -0
  32. package/src/breakouts/index.ts +73 -0
  33. package/src/constants.ts +11 -0
  34. package/src/media/index.ts +14 -24
  35. package/src/media/properties.ts +16 -10
  36. package/src/meeting/index.ts +122 -204
  37. package/src/meeting/muteState.ts +5 -5
  38. package/src/meeting/util.ts +5 -4
  39. package/src/metrics/constants.ts +0 -4
  40. package/src/reconnection-manager/index.ts +1 -1
  41. package/src/statsAnalyzer/index.ts +4 -4
  42. package/test/integration/spec/converged-space-meetings.js +3 -3
  43. package/test/integration/spec/journey.js +3 -3
  44. package/test/unit/spec/breakouts/edit-lock-error.ts +30 -0
  45. package/test/unit/spec/breakouts/index.ts +92 -1
  46. package/test/unit/spec/media/index.ts +8 -6
  47. package/test/unit/spec/meeting/index.js +87 -114
  48. package/test/unit/spec/meeting/muteState.js +21 -22
  49. package/test/unit/spec/meeting/utils.js +3 -1
  50. package/test/utils/testUtils.js +30 -25
  51. package/dist/meeting/effectsState.js +0 -262
  52. package/dist/meeting/effectsState.js.map +0 -1
  53. package/dist/types/meeting/effectsState.d.ts +0 -42
  54. package/src/meeting/effectsState.ts +0 -211
  55. package/test/unit/spec/meeting/effectsState.js +0 -285
@@ -1,211 +0,0 @@
1
- /* eslint-disable no-param-reassign */
2
- import {Media as WebRTCMedia} from '@webex/internal-media-core';
3
-
4
- import BEHAVIORAL_METRICS from '../metrics/constants';
5
- import Metrics from '../metrics';
6
- import MediaUtil from '../media/util';
7
- import LoggerProxy from '../common/logs/logger-proxy';
8
- import {BNR_STATUS} from '../constants';
9
-
10
- const createEffectsState = (type: any) => {
11
- LoggerProxy.logger.info(
12
- `Meeting:effectState#createEffectsState --> creating effectsState for effect ${type}`
13
- );
14
-
15
- return new EffectsState(type);
16
- };
17
-
18
- /* The purpose of this class is to manage the effects state(for eg., BNR).
19
- */
20
- class EffectsState {
21
- effectType: any;
22
- pendingPromiseReject: any;
23
- pendingPromiseResolve: any;
24
- state: any;
25
-
26
- constructor(type: any) {
27
- this.effectType = type;
28
- this.state = {
29
- bnr: {
30
- enabled: BNR_STATUS.NOT_ENABLED,
31
- },
32
- callToWebrtcBNRInProgress: false,
33
- };
34
- // these 2 hold the resolve, reject methods for the promise we returned to the client in last handleClientRequest() call
35
- this.pendingPromiseResolve = null;
36
- this.pendingPromiseReject = null;
37
- }
38
-
39
- /**
40
- * @memberof EffectsState
41
- * @param {Boolean} [isEnable] true for enableBNR, false for disableBNR request
42
- * @param {Object} [meeting] the meeting object
43
- * @returns {Promise}
44
- */
45
- async handleClientRequest(isEnable?: boolean, meeting?: object) {
46
- return new Promise((resolve, reject) => {
47
- if (this.pendingPromiseResolve) {
48
- // resolve the last promise we returned to the client as the client has issued a new request that has superseded the previous one
49
- this.pendingPromiseResolve();
50
- }
51
- this.pendingPromiseResolve = resolve;
52
- this.pendingPromiseReject = reject;
53
-
54
- if (isEnable) this.enableBNR(meeting);
55
- else this.disableBNR(meeting);
56
- });
57
- }
58
-
59
- /**
60
- * Internal API to return status of BNR
61
- * @memberof EffectsState
62
- * @returns {Boolean}
63
- * @public
64
- * @memberof Meeting
65
- */
66
- public isBnrEnabled() {
67
- return this.state.bnr.enabled === BNR_STATUS.ENABLED;
68
- }
69
-
70
- resolvePromise() {
71
- if (this.pendingPromiseResolve) {
72
- this.pendingPromiseResolve(true);
73
- }
74
- this.pendingPromiseResolve = null;
75
- this.pendingPromiseReject = null;
76
- }
77
-
78
- rejectPromise(e) {
79
- if (this.pendingPromiseReject) {
80
- this.pendingPromiseReject(e);
81
- }
82
- this.pendingPromiseResolve = null;
83
- this.pendingPromiseReject = null;
84
- }
85
-
86
- /**
87
- * enableBNR API
88
- * @param {Object} meeting the meeting object
89
- * @returns {Promise<Boolean>}
90
- * @public
91
- * @memberof EffectsState
92
- */
93
- public async enableBNR(meeting: any) {
94
- LoggerProxy.logger.info('Meeting:effectState#enableBNR. Enable BNR called');
95
-
96
- if (this.isBnrEnabled()) {
97
- LoggerProxy.logger.warn('Meeting:index#enableBNR. BNR is already enabled');
98
-
99
- return this.resolvePromise();
100
- }
101
-
102
- if (this.state.callToWebrtcBNRInProgress) {
103
- LoggerProxy.logger.warn(
104
- 'Meeting:effectState#enableBNR. Call to WebRTC in progress, we need to wait for it to complete'
105
- );
106
-
107
- return this.resolvePromise();
108
- }
109
-
110
- const {bnr} = this.state;
111
-
112
- try {
113
- bnr.enabled = BNR_STATUS.SHOULD_ENABLE;
114
- this.state.callToWebrtcBNRInProgress = true;
115
- const audioStream = MediaUtil.createMediaStream([meeting.mediaProperties.audioTrack]);
116
-
117
- LoggerProxy.logger.info(
118
- 'Meeting:effectState#enableBNR. MediaStream created from meeting & sent to updateAudio'
119
- );
120
- await meeting.updateAudio({
121
- sendAudio: true,
122
- receiveAudio: meeting.mediaProperties.mediaDirection.receiveAudio,
123
- stream: audioStream,
124
- });
125
-
126
- LoggerProxy.logger.info(
127
- 'Meeting:effectState#enableBNR. Updated meeting audio with bnr enabled track'
128
- );
129
- bnr.enabled = BNR_STATUS.ENABLED;
130
- this.state.callToWebrtcBNRInProgress = false;
131
- Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ENABLE_BNR_SUCCESS);
132
- } catch (error) {
133
- bnr.enabled = BNR_STATUS.NOT_ENABLED;
134
- this.state.callToWebrtcBNRInProgress = false;
135
- LoggerProxy.logger.error('Meeting:index#enableBNR.', error);
136
-
137
- Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ENABLE_BNR_FAILURE, {
138
- reason: error.message,
139
- stack: error.stack,
140
- });
141
- this.rejectPromise(error);
142
-
143
- throw error;
144
- }
145
-
146
- return this.resolvePromise();
147
- }
148
-
149
- /**
150
- * disableBNR API
151
- * @param {Object} meeting the meeting object
152
- * @returns {Promise<Boolean>}
153
- * @public
154
- * @memberof EffectsState
155
- */
156
- public async disableBNR(meeting: any) {
157
- LoggerProxy.logger.info('Meeting:effectState#disableBNR. Disable BNR called');
158
-
159
- const {bnr} = this.state;
160
-
161
- try {
162
- if (this.state.callToWebrtcBNRInProgress) {
163
- LoggerProxy.logger.info(
164
- 'Meeting:effectState#disableBNR. Call to WebRTC in progress, we need to wait for it to complete'
165
- );
166
-
167
- return this.resolvePromise();
168
- }
169
-
170
- bnr.enabled = BNR_STATUS.SHOULD_DISABLE;
171
- this.state.callToWebrtcBNRInProgress = true;
172
-
173
- // @ts-ignore - disableBNR does not expect an argument
174
- const audioTrack = WebRTCMedia.Effects.BNR.disableBNR(meeting.mediaProperties.audioTrack);
175
-
176
- const audioStream = MediaUtil.createMediaStream([audioTrack]);
177
-
178
- LoggerProxy.logger.info(
179
- 'Meeting:effectState#disableBNR. Raw media track obtained from WebRTC & sent to updateAudio'
180
- );
181
-
182
- await meeting.updateAudio({
183
- sendAudio: true,
184
- receiveAudio: meeting.mediaProperties.mediaDirection.receiveAudio,
185
- stream: audioStream,
186
- });
187
-
188
- bnr.enabled = BNR_STATUS.NOT_ENABLED;
189
-
190
- this.state.callToWebrtcBNRInProgress = false;
191
-
192
- Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.DISABLE_BNR_SUCCESS);
193
- } catch (error) {
194
- bnr.enabled = BNR_STATUS.ENABLED;
195
- this.state.callToWebrtcBNRInProgress = false;
196
- LoggerProxy.logger.error(`Meeting:index#disableBNR. ${error}`);
197
-
198
- Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.DISABLE_BNR_FAILURE, {
199
- reason: error.message,
200
- stack: error.stack,
201
- });
202
- this.rejectPromise(error);
203
-
204
- throw error;
205
- }
206
-
207
- return this.resolvePromise();
208
- }
209
- }
210
-
211
- export default createEffectsState;
@@ -1,285 +0,0 @@
1
- /* eslint-disable camelcase */
2
- import {assert} from '@webex/test-helper-chai';
3
- import sinon from 'sinon';
4
- import MockWebex from '@webex/test-helper-mock-webex';
5
- import {BNR_STATUS} from '@webex/plugin-meetings/src/constants';
6
- import BEHAVIORAL_METRICS from '@webex/plugin-meetings/src/metrics/constants';
7
- import Meeting from '@webex/plugin-meetings/src/meeting';
8
- import Meetings from '@webex/plugin-meetings';
9
- import Metrics from '@webex/plugin-meetings/src/metrics';
10
- import MediaUtil from '@webex/plugin-meetings/src/media/util';
11
- import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
12
- import createEffectsState from '@webex/plugin-meetings/src/meeting/effectsState';
13
- import LoggerProxy from '@webex/plugin-meetings/src/common/logs/logger-proxy';
14
- import LoggerConfig from '@webex/plugin-meetings/src/common/logs/logger-config';
15
- import LLM from '@webex/internal-plugin-llm';
16
- import Mercury from '@webex/internal-plugin-mercury';
17
-
18
- describe('plugin-meetings', () => {
19
- const logger = {
20
- info: () => {},
21
- log: () => {},
22
- error: () => {},
23
- warn: () => {},
24
- trace: () => {},
25
- debug: () => {},
26
- };
27
-
28
- beforeEach(() => {
29
- sinon.stub(Metrics, 'sendBehavioralMetric');
30
- });
31
- afterEach(() => {
32
- sinon.restore();
33
- });
34
-
35
- Object.defineProperty(global.window.navigator, 'mediaDevices', {
36
- writable: true,
37
- value: {
38
- getSupportedConstraints: sinon.stub().returns({
39
- sampleRate: true,
40
- }),
41
- },
42
- });
43
- LoggerConfig.set({verboseEvents: true, enable: false});
44
- LoggerProxy.set(logger);
45
-
46
- let webex;
47
- let meeting;
48
- let uuid1;
49
-
50
- const fakeMediaTrack = () => ({
51
- id: Date.now().toString(),
52
- stop: () => {},
53
- readyState: 'live',
54
- enabled: true,
55
- getSettings: () => ({
56
- sampleRate: 48000,
57
- }),
58
- });
59
-
60
- class FakeMediaStream {
61
- constructor(tracks) {
62
- this.active = false;
63
- this.id = '5146425f-c240-48cc-b86b-27d422988fb7';
64
- this.tracks = tracks;
65
- }
66
-
67
- addTrack = () => undefined;
68
-
69
- getAudioTracks = () => this.tracks;
70
- }
71
-
72
- class FakeAudioContext {
73
- constructor() {
74
- this.state = 'running';
75
- this.baseLatency = 0.005333333333333333;
76
- this.currentTime = 2.7946666666666666;
77
- this.sampleRate = 48000;
78
- this.audioWorklet = {
79
- addModule: async () => undefined,
80
- };
81
- }
82
-
83
- onstatechange = null;
84
-
85
- createMediaStreamSource() {
86
- return {
87
- connect: () => undefined,
88
- mediaStream: {
89
- getAudioTracks() {
90
- // eslint-disable-next-line no-undef
91
- return [new MediaStreamTrack()];
92
- },
93
- },
94
- };
95
- }
96
-
97
- createMediaStreamDestination() {
98
- return {
99
- stream: {
100
- getAudioTracks() {
101
- // eslint-disable-next-line no-undef
102
- return [new MediaStreamTrack()];
103
- },
104
- },
105
- };
106
- }
107
- }
108
-
109
- class FakeAudioWorkletNode {
110
- constructor() {
111
- this.port = {
112
- postMessage: () => undefined,
113
- };
114
- }
115
-
116
- connect() {
117
- /* placeholder method */
118
- }
119
- }
120
-
121
- class FakeMediaStreamTrack {
122
- constructor() {
123
- this.kind = 'audio';
124
- this.enabled = true;
125
- this.label = 'Default - MacBook Pro Microphone (Built-in)';
126
- this.muted = false;
127
- this.readyState = 'live';
128
- this.contentHint = '';
129
- }
130
-
131
- getSettings() {
132
- return {
133
- sampleRate: 48000,
134
- };
135
- }
136
- }
137
- Object.defineProperty(global, 'MediaStream', {
138
- writable: true,
139
- value: FakeMediaStream,
140
- });
141
-
142
- Object.defineProperty(global, 'AudioContext', {
143
- writable: true,
144
- value: FakeAudioContext,
145
- });
146
-
147
- Object.defineProperty(global, 'AudioWorkletNode', {
148
- writable: true,
149
- value: FakeAudioWorkletNode,
150
- });
151
-
152
- Object.defineProperty(global, 'MediaStreamTrack', {
153
- writable: true,
154
- value: FakeMediaStreamTrack,
155
- });
156
-
157
- let effects;
158
-
159
- beforeEach(() => {
160
- webex = new MockWebex({
161
- children: {
162
- meetings: Meetings,
163
- llm: LLM,
164
- mercury: Mercury,
165
- }
166
- });
167
- meeting = new Meeting(
168
- {
169
- userId: uuid1,
170
- },
171
- {
172
- parent: webex,
173
- }
174
- );
175
-
176
- effects = createEffectsState('BNR');
177
- meeting.canUpdateMedia = sinon.stub().returns(true);
178
- MeetingUtil.validateOptions = sinon.stub().returns(Promise.resolve());
179
-
180
- meeting.addMedia = sinon.stub().returns(Promise.resolve());
181
- meeting.getMediaStreams = sinon.stub().returns(Promise.resolve());
182
- sinon.stub(meeting, 'effects').value(effects);
183
- sinon.replace(meeting, 'addMedia', () => {
184
- sinon.stub(meeting.mediaProperties, 'audioTrack').value(fakeMediaTrack());
185
- sinon.stub(meeting.mediaProperties, 'mediaDirection').value({
186
- receiveAudio: true,
187
- });
188
- sinon
189
- .stub(meeting.mediaProperties, 'webrtcMediaConnection')
190
- .value({updateSendReceiveOptions: sinon.stub()});
191
- });
192
- });
193
-
194
- describe('bnr effect library', () => {
195
- beforeEach(async () => {
196
- await meeting.getMediaStreams();
197
- await meeting.addMedia();
198
- });
199
- describe('#enableBNR', () => {
200
- it('should have #enableBnr', () => {
201
- assert.exists(effects.enableBNR);
202
- });
203
-
204
- it('does bnr effect enable on audio track', async () => {
205
- assert.isTrue(await effects.handleClientRequest(true, meeting));
206
- assert.equal(effects.state.bnr.enabled, BNR_STATUS.ENABLED);
207
-
208
- assert(Metrics.sendBehavioralMetric.calledOnce);
209
- assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.ENABLE_BNR_SUCCESS);
210
- });
211
-
212
- it('does resolve request if bnr is already enabled', async () => {
213
- effects.state.bnr.enabled = BNR_STATUS.ENABLED;
214
- assert.isTrue(await effects.handleClientRequest(true, meeting));
215
- assert.equal(effects.state.bnr.enabled, BNR_STATUS.ENABLED);
216
- });
217
-
218
- it('if called twice, does bnr effect enable on audio track for the first request and resolves second', async () => {
219
- Promise.all([
220
- effects.handleClientRequest(true, meeting),
221
- effects.handleClientRequest(true, meeting),
222
- ]).then((resolveFirst, resolveSecond) => {
223
- assert.isTrue(resolveFirst);
224
- assert.isTrue(resolveSecond);
225
- assert.calledOnce(MediaUtil.createMediaStream);
226
- });
227
- });
228
-
229
- it('should throw error for inappropriate sample rate and send error metrics', async () => {
230
- const fakeMediaTrack1 = () => ({
231
- id: Date.now().toString(),
232
- stop: () => {},
233
- readyState: 'live',
234
- getSettings: () => ({
235
- sampleRate: 49000,
236
- }),
237
- });
238
-
239
- sinon.stub(meeting.mediaProperties, 'audioTrack').value(fakeMediaTrack1());
240
-
241
- // eslint-disable-next-line no-undef
242
- MediaUtil.createMediaStream = sinon.stub().returns(new MediaStream([fakeMediaTrack1()]));
243
- try {
244
- await effects.handleClientRequest(true, meeting);
245
- } catch (err) {
246
- assert(Metrics.sendBehavioralMetric.calledOnce);
247
- assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.ENABLE_BNR_FAILURE, {
248
- reason: err.message,
249
- stack: err.stack,
250
- });
251
- assert.equal(err.message, 'Sample rate of 49000 is not supported.');
252
- }
253
- });
254
- });
255
-
256
- describe('#disableBNR', () => {
257
- beforeEach(() => {
258
- effects.state.bnr.enabled = BNR_STATUS.ENABLED;
259
- });
260
- it('should have #disableBnr', () => {
261
- assert.exists(effects.disableBNR);
262
- });
263
-
264
- it('does bnr disable on audio track', async () => {
265
- assert.isTrue(await effects.handleClientRequest(false, meeting));
266
- assert.equal(effects.state.bnr.enabled, BNR_STATUS.NOT_ENABLED);
267
-
268
- assert(Metrics.sendBehavioralMetric.calledOnce);
269
- assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.DISABLE_BNR_SUCCESS);
270
- });
271
-
272
- it('reject request for disable bnr if not enabled', async () => {
273
- try {
274
- await effects.handleClientRequest(false, meeting);
275
- } catch (e) {
276
- assert.equal(e.message, 'Can not disable as BNR is not enabled');
277
- assert.equal(effects.state.bnr.enabled, BNR_STATUS.ENABLED);
278
-
279
- assert(Metrics.sendBehavioralMetric.calledOnce);
280
- assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.DISABLE_BNR_FAILURE);
281
- }
282
- });
283
- });
284
- });
285
- });