@webex/plugin-meetings 2.13.0 → 2.14.2
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/constants.js +1 -0
- package/dist/constants.js.map +1 -1
- package/dist/locus-info/controlsUtils.js +12 -2
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +14 -0
- package/dist/locus-info/index.js.map +1 -1
- package/dist/meeting/effectsState.js +328 -0
- package/dist/meeting/effectsState.js.map +1 -0
- package/dist/meeting/index.js +102 -195
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +6 -10
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/package.json +6 -6
- package/src/constants.js +1 -0
- package/src/locus-info/controlsUtils.js +11 -0
- package/src/locus-info/index.js +16 -0
- package/src/meeting/effectsState.js +206 -0
- package/src/meeting/index.js +83 -98
- package/src/meeting-info/meeting-info-v2.js +1 -4
- package/test/unit/spec/locus-info/index.js +34 -0
- package/test/unit/spec/meeting/effectsState.js +292 -0
- package/test/unit/spec/meeting/index.js +42 -200
- package/test/unit/spec/meeting-info/meetinginfov2.js +13 -2
|
@@ -466,108 +466,7 @@ describe('plugin-meetings', () => {
|
|
|
466
466
|
})
|
|
467
467
|
});
|
|
468
468
|
|
|
469
|
-
class FakeMediaStream {
|
|
470
|
-
constructor(tracks) {
|
|
471
|
-
this.active = false;
|
|
472
|
-
this.id = '5146425f-c240-48cc-b86b-27d422988fb7';
|
|
473
|
-
this.tracks = tracks;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
addTrack = () => undefined;
|
|
477
|
-
|
|
478
|
-
getAudioTracks = () => this.tracks;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
class FakeAudioContext {
|
|
482
|
-
constructor() {
|
|
483
|
-
this.state = 'running';
|
|
484
|
-
this.baseLatency = 0.005333333333333333;
|
|
485
|
-
this.currentTime = 2.7946666666666666;
|
|
486
|
-
this.sampleRate = 48000;
|
|
487
|
-
this.audioWorklet = {
|
|
488
|
-
addModule: async () => undefined,
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
onstatechange = null;
|
|
493
|
-
|
|
494
|
-
createMediaStreamSource() {
|
|
495
|
-
return {
|
|
496
|
-
connect: () => undefined,
|
|
497
|
-
mediaStream: {
|
|
498
|
-
getAudioTracks() {
|
|
499
|
-
// eslint-disable-next-line no-undef
|
|
500
|
-
return [new MediaStreamTrack()];
|
|
501
|
-
},
|
|
502
|
-
},
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
createMediaStreamDestination() {
|
|
507
|
-
return {
|
|
508
|
-
stream: {
|
|
509
|
-
getAudioTracks() {
|
|
510
|
-
// eslint-disable-next-line no-undef
|
|
511
|
-
return [new MediaStreamTrack()];
|
|
512
|
-
},
|
|
513
|
-
},
|
|
514
|
-
};
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
class FakeAudioWorkletNode {
|
|
519
|
-
constructor() {
|
|
520
|
-
this.port = {
|
|
521
|
-
postMessage: () => undefined,
|
|
522
|
-
};
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
connect() {
|
|
526
|
-
/* placeholder method */
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
class FakeMediaStreamTrack {
|
|
531
|
-
constructor() {
|
|
532
|
-
this.kind = 'audio';
|
|
533
|
-
this.enabled = true;
|
|
534
|
-
this.label = 'Default - MacBook Pro Microphone (Built-in)';
|
|
535
|
-
this.muted = false;
|
|
536
|
-
this.readyState = 'live';
|
|
537
|
-
this.contentHint = '';
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
getSettings() {
|
|
541
|
-
return {
|
|
542
|
-
sampleRate: 48000
|
|
543
|
-
};
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
Object.defineProperty(global, 'MediaStream', {
|
|
547
|
-
writable: true,
|
|
548
|
-
value: FakeMediaStream,
|
|
549
|
-
});
|
|
550
|
-
|
|
551
|
-
Object.defineProperty(global, 'AudioContext', {
|
|
552
|
-
writable: true,
|
|
553
|
-
value: FakeAudioContext,
|
|
554
|
-
});
|
|
555
|
-
|
|
556
|
-
Object.defineProperty(global, 'AudioWorkletNode', {
|
|
557
|
-
writable: true,
|
|
558
|
-
value: FakeAudioWorkletNode,
|
|
559
|
-
});
|
|
560
|
-
|
|
561
|
-
Object.defineProperty(global, 'MediaStreamTrack', {
|
|
562
|
-
writable: true,
|
|
563
|
-
value: FakeMediaStreamTrack,
|
|
564
|
-
});
|
|
565
|
-
|
|
566
469
|
beforeEach(() => {
|
|
567
|
-
meeting.canUpdateMedia = sinon.stub().returns(true);
|
|
568
|
-
MeetingUtil.validateOptions = sinon.stub().returns(Promise.resolve());
|
|
569
|
-
MeetingUtil.updateTransceiver = sinon.stub();
|
|
570
|
-
|
|
571
470
|
meeting.getMediaStreams = sinon.stub().returns(Promise.resolve());
|
|
572
471
|
sinon.replace(meeting, 'addMedia', () => {
|
|
573
472
|
sinon.stub(meeting.mediaProperties, 'audioTrack').value(fakeMediaTrack());
|
|
@@ -575,11 +474,7 @@ describe('plugin-meetings', () => {
|
|
|
575
474
|
receiveAudio: true
|
|
576
475
|
});
|
|
577
476
|
});
|
|
578
|
-
|
|
579
|
-
// eslint-disable-next-line no-undef
|
|
580
|
-
MediaUtil.createMediaStream = sinon.stub().returns(new MediaStream([fakeMediaTrack()]));
|
|
581
477
|
});
|
|
582
|
-
|
|
583
478
|
describe('#enableBNR', () => {
|
|
584
479
|
it('should have #enableBnr', () => {
|
|
585
480
|
assert.exists(meeting.enableBNR);
|
|
@@ -594,26 +489,14 @@ describe('plugin-meetings', () => {
|
|
|
594
489
|
});
|
|
595
490
|
|
|
596
491
|
describe('after audio attached to meeting', () => {
|
|
492
|
+
let handleClientRequest;
|
|
493
|
+
|
|
597
494
|
beforeEach(async () => {
|
|
598
495
|
await meeting.getMediaStreams();
|
|
599
496
|
await meeting.addMedia();
|
|
600
497
|
});
|
|
601
498
|
|
|
602
|
-
it('should return true for appropriate sample rate', async () => {
|
|
603
|
-
const response = await meeting.enableBNR();
|
|
604
|
-
|
|
605
|
-
assert(Metrics.sendBehavioralMetric.calledOnce);
|
|
606
|
-
assert.calledWith(
|
|
607
|
-
Metrics.sendBehavioralMetric,
|
|
608
|
-
BEHAVIORAL_METRICS.ENABLE_BNR_SUCCESS,
|
|
609
|
-
);
|
|
610
|
-
|
|
611
|
-
assert.equal(response, true);
|
|
612
|
-
});
|
|
613
|
-
|
|
614
499
|
it('should throw error if meeting audio is muted', async () => {
|
|
615
|
-
await meeting.getMediaStreams();
|
|
616
|
-
await meeting.addMedia();
|
|
617
500
|
const handleClientRequest = (meeting, mute) => {
|
|
618
501
|
meeting.mediaProperties.audioTrack.enabled = !mute;
|
|
619
502
|
|
|
@@ -621,82 +504,53 @@ describe('plugin-meetings', () => {
|
|
|
621
504
|
};
|
|
622
505
|
const isMuted = () => !meeting.mediaProperties.audioTrack.enabled;
|
|
623
506
|
|
|
507
|
+
meeting.locusInfo.parsedLocus = {self: {state: 'JOINED'}};
|
|
624
508
|
meeting.mediaId = 'mediaId';
|
|
625
509
|
meeting.audio = {handleClientRequest, isMuted};
|
|
626
|
-
meeting.locusInfo.parsedLocus = {self: {state: 'JOINED'}};
|
|
627
510
|
await meeting.muteAudio();
|
|
628
511
|
await meeting.enableBNR().catch((err) => {
|
|
629
|
-
assert(Metrics.sendBehavioralMetric.calledOnce);
|
|
630
|
-
assert.calledWith(
|
|
631
|
-
Metrics.sendBehavioralMetric,
|
|
632
|
-
BEHAVIORAL_METRICS.ENABLE_BNR_FAILURE, {
|
|
633
|
-
reason: err.message,
|
|
634
|
-
stack: err.stack
|
|
635
|
-
}
|
|
636
|
-
);
|
|
637
512
|
assert.equal(err.message, 'Cannot enable BNR while meeting is muted');
|
|
638
513
|
});
|
|
639
514
|
});
|
|
640
515
|
|
|
641
|
-
it('should
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
readyState: 'live',
|
|
646
|
-
getSettings: () => ({
|
|
647
|
-
sampleRate: 49000
|
|
648
|
-
})
|
|
649
|
-
});
|
|
516
|
+
it('should return true on enable bnr success', async () => {
|
|
517
|
+
handleClientRequest = sinon.stub().returns(Promise.resolve(true));
|
|
518
|
+
meeting.effects = {handleClientRequest};
|
|
519
|
+
const response = await meeting.enableBNR();
|
|
650
520
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
// eslint-disable-next-line no-undef
|
|
654
|
-
MediaUtil.createMediaStream = sinon.stub().returns(new MediaStream([fakeMediaTrack()]));
|
|
655
|
-
|
|
656
|
-
await meeting.enableBNR()
|
|
657
|
-
.then(() => {
|
|
658
|
-
assert.fail('The expected Error was not thrown.');
|
|
659
|
-
})
|
|
660
|
-
.catch((err) => {
|
|
661
|
-
assert(Metrics.sendBehavioralMetric.calledOnce);
|
|
662
|
-
assert.calledWith(
|
|
663
|
-
Metrics.sendBehavioralMetric,
|
|
664
|
-
BEHAVIORAL_METRICS.ENABLE_BNR_FAILURE, {
|
|
665
|
-
reason: err.message,
|
|
666
|
-
stack: err.stack
|
|
667
|
-
}
|
|
668
|
-
);
|
|
669
|
-
assert.equal(err.message, 'Sample rate of 49000 is not supported.');
|
|
670
|
-
});
|
|
521
|
+
assert.equal(response, true);
|
|
671
522
|
});
|
|
672
523
|
});
|
|
673
524
|
});
|
|
674
525
|
|
|
675
526
|
describe('#disableBNR', () => {
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
527
|
+
describe('before audio attached to meeting', () => {
|
|
528
|
+
it('should have #disableBnr', () => {
|
|
529
|
+
assert.exists(meeting.disableBNR);
|
|
530
|
+
});
|
|
680
531
|
|
|
681
|
-
|
|
682
|
-
|
|
532
|
+
it('should throw no audio error', async () => {
|
|
533
|
+
await meeting.disableBNR().catch((err) => {
|
|
534
|
+
assert.equal(err.toString(), 'Error: Meeting doesn\'t have an audioTrack attached');
|
|
535
|
+
});
|
|
536
|
+
});
|
|
683
537
|
});
|
|
538
|
+
describe('after audio attached to meeting', () => {
|
|
539
|
+
beforeEach(async () => {
|
|
540
|
+
await meeting.getMediaStreams();
|
|
541
|
+
await meeting.addMedia();
|
|
542
|
+
});
|
|
684
543
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
const response = await meeting.disableBNR();
|
|
544
|
+
let handleClientRequest;
|
|
545
|
+
let isBnrEnabled;
|
|
688
546
|
|
|
689
|
-
|
|
690
|
-
|
|
547
|
+
it('should return true on disable bnr success', async () => {
|
|
548
|
+
handleClientRequest = sinon.stub().returns(Promise.resolve(true));
|
|
549
|
+
isBnrEnabled = sinon.stub().returns(Promise.resolve(true));
|
|
550
|
+
meeting.effects = {handleClientRequest, isBnrEnabled};
|
|
551
|
+
const response = await meeting.disableBNR();
|
|
691
552
|
|
|
692
|
-
|
|
693
|
-
await meeting.disableBNR().catch((err) => {
|
|
694
|
-
assert(Metrics.sendBehavioralMetric.calledOnce);
|
|
695
|
-
assert.calledWith(
|
|
696
|
-
Metrics.sendBehavioralMetric,
|
|
697
|
-
BEHAVIORAL_METRICS.DISABLE_BNR_FAILURE,
|
|
698
|
-
);
|
|
699
|
-
assert.equal(err.message, 'Can not disable as BNR is not enabled');
|
|
553
|
+
assert.equal(response, true);
|
|
700
554
|
});
|
|
701
555
|
});
|
|
702
556
|
});
|
|
@@ -870,16 +724,12 @@ describe('plugin-meetings', () => {
|
|
|
870
724
|
});
|
|
871
725
|
describe('#isTranscriptionSupported', () => {
|
|
872
726
|
it('should return false if the feature is not supported for the meeting', () => {
|
|
873
|
-
meeting.
|
|
874
|
-
WEBEX_ASSISTANT_STATUS_INACTIVE: true
|
|
875
|
-
};
|
|
727
|
+
meeting.locusInfo.controls = {transcribe: {transcribing: false}};
|
|
876
728
|
|
|
877
729
|
assert.equal(meeting.isTranscriptionSupported(), false);
|
|
878
730
|
});
|
|
879
731
|
it('should return true if webex assitant is enabled', () => {
|
|
880
|
-
meeting.
|
|
881
|
-
WEBEX_ASSISTANT_STATUS_ACTIVE: true
|
|
882
|
-
};
|
|
732
|
+
meeting.locusInfo.controls = {transcribe: {transcribing: true}};
|
|
883
733
|
|
|
884
734
|
assert.equal(meeting.isTranscriptionSupported(), true);
|
|
885
735
|
});
|
|
@@ -1478,7 +1328,7 @@ describe('plugin-meetings', () => {
|
|
|
1478
1328
|
|
|
1479
1329
|
sandbox.stub(meeting.mediaProperties, 'peerConnection').value({shareTransceiver: true});
|
|
1480
1330
|
sandbox.stub(MeetingUtil, 'getTrack').returns({videoTrack: true});
|
|
1481
|
-
|
|
1331
|
+
MeetingUtil.validateOptions = sinon.stub().returns(Promise.resolve(true));
|
|
1482
1332
|
sandbox.stub(meeting, 'canUpdateMedia').returns(true);
|
|
1483
1333
|
sandbox.stub(meeting, 'setLocalShareTrack');
|
|
1484
1334
|
|
|
@@ -1547,13 +1397,14 @@ describe('plugin-meetings', () => {
|
|
|
1547
1397
|
const SENDRECV = 'sendrecv';
|
|
1548
1398
|
const delay = 1e3;
|
|
1549
1399
|
|
|
1400
|
+
MeetingUtil.validateOptions = sinon.stub().returns(Promise.resolve(true));
|
|
1401
|
+
MeetingUtil.updateTransceiver = sinon.stub().returns(Promise.resolve(true));
|
|
1550
1402
|
sandbox.stub(meeting, 'canUpdateMedia').returns(true);
|
|
1551
1403
|
sandbox.stub(MeetingUtil, 'getTrack').returns({videoTrack: null});
|
|
1552
1404
|
sandbox.stub(meeting, 'setLocalShareTrack');
|
|
1553
1405
|
sandbox.stub(meeting, 'unsetLocalShareTrack');
|
|
1554
|
-
sandbox.stub(MeetingUtil, 'validateOptions').resolves(true);
|
|
1555
1406
|
sandbox.stub(meeting, 'checkForStopShare').returns(false);
|
|
1556
|
-
|
|
1407
|
+
|
|
1557
1408
|
sandbox.stub(meeting, 'isLocalShareLive').value(false);
|
|
1558
1409
|
sandbox.stub(meeting, 'handleShareTrackEnded');
|
|
1559
1410
|
sandbox.stub(meeting.mediaProperties, 'peerConnection').value({
|
|
@@ -2424,11 +2275,6 @@ describe('plugin-meetings', () => {
|
|
|
2424
2275
|
assert.equal(meeting.requiredCaptcha, null);
|
|
2425
2276
|
assert.calledTwice(TriggerProxy.trigger);
|
|
2426
2277
|
assert.calledWith(TriggerProxy.trigger, meeting, {file: 'meetings', function: 'fetchMeetingInfo'}, 'meeting:meetingInfoAvailable');
|
|
2427
|
-
assert(Metrics.sendBehavioralMetric.calledOnce);
|
|
2428
|
-
assert.calledWith(
|
|
2429
|
-
Metrics.sendBehavioralMetric,
|
|
2430
|
-
BEHAVIORAL_METRICS.VERIFY_PASSWORD_SUCCESS,
|
|
2431
|
-
);
|
|
2432
2278
|
});
|
|
2433
2279
|
|
|
2434
2280
|
it('calls meetingInfoProvider with all the right parameters and parses the result when random delay is applied', async () => {
|
|
@@ -2500,11 +2346,6 @@ describe('plugin-meetings', () => {
|
|
|
2500
2346
|
|
|
2501
2347
|
assert.calledWith(meeting.attrs.meetingInfoProvider.fetchMeetingInfo, FAKE_DESTINATION, FAKE_TYPE, null, null);
|
|
2502
2348
|
|
|
2503
|
-
assert(Metrics.sendBehavioralMetric.calledOnce);
|
|
2504
|
-
assert.calledWith(
|
|
2505
|
-
Metrics.sendBehavioralMetric,
|
|
2506
|
-
BEHAVIORAL_METRICS.VERIFY_PASSWORD_ERROR,
|
|
2507
|
-
);
|
|
2508
2349
|
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
|
2509
2350
|
assert.equal(meeting.meetingInfoFailureReason, MEETING_INFO_FAILURE_REASON.WRONG_PASSWORD);
|
|
2510
2351
|
assert.equal(meeting.requiredCaptcha, null);
|
|
@@ -2525,11 +2366,7 @@ describe('plugin-meetings', () => {
|
|
|
2525
2366
|
|
|
2526
2367
|
assert.calledWith(meeting.attrs.meetingInfoProvider.fetchMeetingInfo, FAKE_DESTINATION, FAKE_TYPE, 'aaa', null);
|
|
2527
2368
|
|
|
2528
|
-
|
|
2529
|
-
assert.calledWith(
|
|
2530
|
-
Metrics.sendBehavioralMetric,
|
|
2531
|
-
BEHAVIORAL_METRICS.VERIFY_CAPTCHA_ERROR,
|
|
2532
|
-
);
|
|
2369
|
+
|
|
2533
2370
|
assert.deepEqual(meeting.meetingInfo, {});
|
|
2534
2371
|
assert.equal(meeting.meetingInfoFailureReason, MEETING_INFO_FAILURE_REASON.WRONG_PASSWORD);
|
|
2535
2372
|
assert.equal(meeting.passwordStatus, PASSWORD_STATUS.REQUIRED);
|
|
@@ -2683,6 +2520,11 @@ describe('plugin-meetings', () => {
|
|
|
2683
2520
|
meeting.fetchMeetingInfo = sinon.stub().resolves();
|
|
2684
2521
|
const result = await meeting.verifyPassword('password', 'captcha id');
|
|
2685
2522
|
|
|
2523
|
+
assert(Metrics.sendBehavioralMetric.calledOnce);
|
|
2524
|
+
assert.calledWith(
|
|
2525
|
+
Metrics.sendBehavioralMetric,
|
|
2526
|
+
BEHAVIORAL_METRICS.VERIFY_PASSWORD_SUCCESS,
|
|
2527
|
+
);
|
|
2686
2528
|
assert.equal(result.isPasswordValid, true);
|
|
2687
2529
|
assert.equal(result.requiredCaptcha, null);
|
|
2688
2530
|
assert.equal(result.failureReason, MEETING_INFO_FAILURE_REASON.NONE);
|
|
@@ -177,16 +177,17 @@ describe('plugin-meetings', () => {
|
|
|
177
177
|
meetingInfo.createAdhocSpaceMeeting.restore();
|
|
178
178
|
});
|
|
179
179
|
|
|
180
|
+
|
|
180
181
|
it('should throw an error MeetingInfoV2AdhocMeetingError if not able to start adhoc meeting for a conversation', async () => {
|
|
181
182
|
webex.config.meetings.experimental.enableAdhocMeetings = true;
|
|
182
183
|
|
|
183
|
-
webex.request = sinon.stub().rejects({statusCode: 403, body: {code: 400000
|
|
184
|
+
webex.request = sinon.stub().rejects({statusCode: 403, body: {code: 400000}});
|
|
184
185
|
try {
|
|
185
186
|
await meetingInfo.createAdhocSpaceMeeting('conversationUrl');
|
|
186
187
|
}
|
|
187
188
|
catch (err) {
|
|
188
189
|
assert.instanceOf(err, MeetingInfoV2AdhocMeetingError);
|
|
189
|
-
assert.deepEqual(err.message, '
|
|
190
|
+
assert.deepEqual(err.message, 'Failed starting the adhoc meeting, Please contact support team , code=400000');
|
|
190
191
|
assert.equal(err.wbxAppApiCode, 400000);
|
|
191
192
|
}
|
|
192
193
|
});
|
|
@@ -201,6 +202,11 @@ describe('plugin-meetings', () => {
|
|
|
201
202
|
assert.fail('fetchMeetingInfo should have thrown, but has not done that');
|
|
202
203
|
}
|
|
203
204
|
catch (err) {
|
|
205
|
+
assert(Metrics.sendBehavioralMetric.calledOnce);
|
|
206
|
+
assert.calledWith(
|
|
207
|
+
Metrics.sendBehavioralMetric,
|
|
208
|
+
BEHAVIORAL_METRICS.VERIFY_PASSWORD_ERROR,
|
|
209
|
+
);
|
|
204
210
|
assert.instanceOf(err, MeetingInfoV2PasswordError);
|
|
205
211
|
assert.deepEqual(err.meetingInfo, FAKE_MEETING_INFO);
|
|
206
212
|
assert.equal(err.wbxAppApiCode, 403000);
|
|
@@ -226,6 +232,11 @@ describe('plugin-meetings', () => {
|
|
|
226
232
|
assert.fail('fetchMeetingInfo should have thrown, but has not done that');
|
|
227
233
|
}
|
|
228
234
|
catch (err) {
|
|
235
|
+
assert(Metrics.sendBehavioralMetric.calledOnce);
|
|
236
|
+
assert.calledWith(
|
|
237
|
+
Metrics.sendBehavioralMetric,
|
|
238
|
+
BEHAVIORAL_METRICS.VERIFY_CAPTCHA_ERROR,
|
|
239
|
+
);
|
|
229
240
|
assert.instanceOf(err, MeetingInfoV2CaptchaError);
|
|
230
241
|
assert.deepEqual(err.captchaInfo, {
|
|
231
242
|
captchaId: 'fake_captcha_id',
|