@webex/plugin-meetings 3.1.0-next.1 → 3.1.0-next.11

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 (66) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/common/errors/{reconnection-in-progress.js → reconnection-not-started.js} +27 -15
  4. package/dist/common/errors/reconnection-not-started.js.map +1 -0
  5. package/dist/constants.js +10 -2
  6. package/dist/constants.js.map +1 -1
  7. package/dist/index.js +15 -3
  8. package/dist/index.js.map +1 -1
  9. package/dist/interpretation/index.js +1 -1
  10. package/dist/interpretation/siLanguage.js +1 -1
  11. package/dist/locus-info/controlsUtils.js +7 -1
  12. package/dist/locus-info/controlsUtils.js.map +1 -1
  13. package/dist/locus-info/index.js +10 -0
  14. package/dist/locus-info/index.js.map +1 -1
  15. package/dist/media/properties.js +102 -57
  16. package/dist/media/properties.js.map +1 -1
  17. package/dist/meeting/in-meeting-actions.js +6 -0
  18. package/dist/meeting/in-meeting-actions.js.map +1 -1
  19. package/dist/meeting/index.js +474 -433
  20. package/dist/meeting/index.js.map +1 -1
  21. package/dist/meeting/util.js +9 -0
  22. package/dist/meeting/util.js.map +1 -1
  23. package/dist/meetings/index.js +1 -23
  24. package/dist/meetings/index.js.map +1 -1
  25. package/dist/reconnection-manager/index.js +138 -109
  26. package/dist/reconnection-manager/index.js.map +1 -1
  27. package/dist/statsAnalyzer/index.js +4 -0
  28. package/dist/statsAnalyzer/index.js.map +1 -1
  29. package/dist/statsAnalyzer/mqaUtil.js +3 -0
  30. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  31. package/dist/types/common/errors/reconnection-not-started.d.ts +13 -0
  32. package/dist/types/constants.d.ts +10 -2
  33. package/dist/types/index.d.ts +3 -2
  34. package/dist/types/media/properties.d.ts +26 -2
  35. package/dist/types/meeting/in-meeting-actions.d.ts +6 -0
  36. package/dist/types/meeting/index.d.ts +3 -5
  37. package/dist/types/meeting/util.d.ts +3 -0
  38. package/dist/types/meetings/index.d.ts +1 -16
  39. package/dist/types/reconnection-manager/index.d.ts +4 -14
  40. package/dist/webinar/index.js +1 -1
  41. package/package.json +21 -21
  42. package/src/common/errors/reconnection-not-started.ts +25 -0
  43. package/src/constants.ts +11 -3
  44. package/src/index.ts +4 -1
  45. package/src/locus-info/controlsUtils.ts +11 -0
  46. package/src/locus-info/index.ts +16 -0
  47. package/src/media/properties.ts +67 -15
  48. package/src/meeting/in-meeting-actions.ts +12 -0
  49. package/src/meeting/index.ts +71 -77
  50. package/src/meeting/util.ts +8 -0
  51. package/src/meetings/index.ts +2 -24
  52. package/src/reconnection-manager/index.ts +128 -105
  53. package/src/statsAnalyzer/index.ts +4 -0
  54. package/src/statsAnalyzer/mqaUtil.ts +5 -0
  55. package/test/unit/spec/locus-info/controlsUtils.js +20 -0
  56. package/test/unit/spec/locus-info/index.js +21 -0
  57. package/test/unit/spec/media/properties.ts +145 -140
  58. package/test/unit/spec/meeting/in-meeting-actions.ts +6 -0
  59. package/test/unit/spec/meeting/index.js +201 -72
  60. package/test/unit/spec/meeting/utils.js +3 -0
  61. package/test/unit/spec/meetings/index.js +1 -6
  62. package/test/unit/spec/reconnection-manager/index.js +127 -39
  63. package/test/unit/spec/stats-analyzer/index.js +11 -0
  64. package/dist/common/errors/reconnection-in-progress.js.map +0 -1
  65. package/dist/types/common/errors/reconnection-in-progress.d.ts +0 -9
  66. package/src/common/errors/reconnection-in-progress.ts +0 -8
@@ -12,6 +12,7 @@ import {Credentials, Token, WebexPlugin} from '@webex/webex-core';
12
12
  import Support from '@webex/internal-plugin-support';
13
13
  import MockWebex from '@webex/test-helper-mock-webex';
14
14
  import StaticConfig from '@webex/plugin-meetings/src/common/config';
15
+ import ReconnectionNotStartedError from '@webex/plugin-meetings/src/common/errors/reconnection-not-started';
15
16
  import {Defer} from '@webex/common';
16
17
  import {
17
18
  FLOOR_ACTION,
@@ -658,6 +659,9 @@ describe('plugin-meetings', () => {
658
659
  assert.calledOnceWithExactly(meeting.addMedia, mediaOptions, fakeTurnServerInfo);
659
660
 
660
661
  assert.deepEqual(result, {join: fakeJoinResult, media: test4});
662
+
663
+ // resets joinWithMediaRetryInfo
664
+ assert.deepEqual(meeting.joinWithMediaRetryInfo, {isRetry: false, prevJoinResponse: undefined});
661
665
  });
662
666
 
663
667
  it("should not call handleTurnDiscoveryHttpResponse if we don't send a TURN discovery request with join", async () => {
@@ -719,8 +723,24 @@ describe('plugin-meetings', () => {
719
723
 
720
724
  await assert.isRejected(meeting.joinWithMedia({mediaOptions: {allowMediaInLobby: true}}));
721
725
 
722
- assert.calledOnceWithExactly(abortTurnDiscoveryStub);
726
+ assert.calledTwice(abortTurnDiscoveryStub);
723
727
 
728
+ assert.calledTwice(Metrics.sendBehavioralMetric);
729
+ assert.calledWith(
730
+ Metrics.sendBehavioralMetric,
731
+ BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
732
+ {
733
+ correlation_id: meeting.correlationId,
734
+ locus_id: undefined,
735
+ reason: error.message,
736
+ stack: error.stack,
737
+ leaveErrorReason: undefined,
738
+ isRetry: false,
739
+ },
740
+ {
741
+ type: error.name,
742
+ }
743
+ );
724
744
  assert.calledWith(
725
745
  Metrics.sendBehavioralMetric,
726
746
  BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
@@ -730,11 +750,52 @@ describe('plugin-meetings', () => {
730
750
  reason: error.message,
731
751
  stack: error.stack,
732
752
  leaveErrorReason: undefined,
753
+ isRetry: true,
754
+ },
755
+ {
756
+ type: error.name,
757
+ }
758
+ );
759
+
760
+ // resets joinWithMediaRetryInfo
761
+ assert.deepEqual(meeting.joinWithMediaRetryInfo, {isRetry: false, prevJoinResponse: undefined});
762
+ });
763
+
764
+ it('should resolve if join() fails the first time but succeeds the second time', async () => {
765
+ const error = new Error('fake');
766
+ meeting.join = sinon.stub().onFirstCall().returns(Promise.reject(error)).onSecondCall().returns(Promise.resolve(fakeJoinResult));
767
+ const leaveStub = sinon.stub(meeting, 'leave').resolves();
768
+
769
+ const result = await meeting.joinWithMedia({
770
+ joinOptions,
771
+ mediaOptions,
772
+ });
773
+
774
+ assert.calledOnce(abortTurnDiscoveryStub);
775
+ assert.calledTwice(meeting.join);
776
+ assert.notCalled(leaveStub);
777
+
778
+ assert.calledOnce(Metrics.sendBehavioralMetric);
779
+ assert.calledWith(
780
+ Metrics.sendBehavioralMetric.firstCall,
781
+ BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
782
+ {
783
+ correlation_id: meeting.correlationId,
784
+ locus_id: meeting.locusUrl.split('/').pop(),
785
+ reason: error.message,
786
+ stack: error.stack,
787
+ leaveErrorReason: undefined,
788
+ isRetry: false,
733
789
  },
734
790
  {
735
791
  type: error.name,
736
792
  }
737
793
  );
794
+
795
+ assert.deepEqual(result, {join: fakeJoinResult, media: test4});
796
+
797
+ // resets joinWithMediaRetryInfo
798
+ assert.deepEqual(meeting.joinWithMediaRetryInfo, {isRetry: false, prevJoinResponse: undefined});
738
799
  });
739
800
 
740
801
  it('should fail if called with allowMediaInLobby:false', async () => {
@@ -767,8 +828,26 @@ describe('plugin-meetings', () => {
767
828
  reason: 'joinWithMedia failure',
768
829
  });
769
830
 
831
+
832
+ // Behavioral metric is sent on both calls of joinWithMedia
833
+ assert.calledTwice(Metrics.sendBehavioralMetric);
770
834
  assert.calledWith(
771
- Metrics.sendBehavioralMetric,
835
+ Metrics.sendBehavioralMetric.firstCall,
836
+ BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
837
+ {
838
+ correlation_id: meeting.correlationId,
839
+ locus_id: meeting.locusUrl.split('/').pop(),
840
+ reason: addMediaError.message,
841
+ stack: addMediaError.stack,
842
+ leaveErrorReason: undefined,
843
+ isRetry: false,
844
+ },
845
+ {
846
+ type: addMediaError.name,
847
+ }
848
+ );
849
+ assert.calledWith(
850
+ Metrics.sendBehavioralMetric.secondCall,
772
851
  BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
773
852
  {
774
853
  correlation_id: meeting.correlationId,
@@ -776,6 +855,47 @@ describe('plugin-meetings', () => {
776
855
  reason: addMediaError.message,
777
856
  stack: addMediaError.stack,
778
857
  leaveErrorReason: leaveError.message,
858
+ isRetry: true,
859
+ },
860
+ {
861
+ type: addMediaError.name,
862
+ }
863
+ );
864
+ });
865
+
866
+ it('should not call leave() if addMedia fails the first time and succeeds the second time and should only call join() once', async () => {
867
+ const addMediaError = new Error('fake addMedia error');
868
+ const leaveError = new Error('leave error');
869
+ const leaveStub = sinon.stub(meeting, 'leave').rejects(leaveError);
870
+
871
+ meeting.addMedia = sinon
872
+ .stub()
873
+ .onFirstCall()
874
+ .rejects(addMediaError)
875
+ .onSecondCall()
876
+ .resolves(test4);
877
+
878
+ const result = await meeting.joinWithMedia({
879
+ joinOptions,
880
+ mediaOptions,
881
+ });
882
+
883
+ assert.deepEqual(result, {join: fakeJoinResult, media: test4});
884
+
885
+ assert.calledOnce(meeting.join);
886
+ assert.notCalled(leaveStub);
887
+
888
+ assert.calledOnce(Metrics.sendBehavioralMetric);
889
+ assert.calledWith(
890
+ Metrics.sendBehavioralMetric.firstCall,
891
+ BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
892
+ {
893
+ correlation_id: meeting.correlationId,
894
+ locus_id: meeting.locusUrl.split('/').pop(),
895
+ reason: addMediaError.message,
896
+ stack: addMediaError.stack,
897
+ leaveErrorReason: undefined,
898
+ isRetry: false,
779
899
  },
780
900
  {
781
901
  type: addMediaError.name,
@@ -802,7 +922,7 @@ describe('plugin-meetings', () => {
802
922
  webex.internal.voicea.on = sinon.stub();
803
923
  webex.internal.voicea.off = sinon.stub();
804
924
  webex.internal.voicea.listenToEvents = sinon.stub();
805
- webex.internal.voicea.toggleTranscribing = sinon.stub();
925
+ webex.internal.voicea.turnOnCaptions = sinon.stub();
806
926
  });
807
927
 
808
928
  it('should subscribe to events for the first time and avoid subscribing for future transcription starts', async () => {
@@ -817,17 +937,16 @@ describe('plugin-meetings', () => {
817
937
  assert.equal(webex.internal.voicea.on.callCount, 4);
818
938
  assert.equal(meeting.areVoiceaEventsSetup, true);
819
939
  assert.equal(webex.internal.voicea.listenToEvents.callCount, 1);
820
- assert.calledWith(webex.internal.voicea.toggleTranscribing, true);
940
+ assert.called(webex.internal.voicea.turnOnCaptions);
821
941
 
822
942
  await meeting.startTranscription();
823
943
  assert.equal(webex.internal.voicea.on.callCount, 4);
824
944
  assert.equal(meeting.areVoiceaEventsSetup, true);
825
945
  assert.equal(webex.internal.voicea.listenToEvents.callCount, 1);
826
- assert.calledTwice(webex.internal.voicea.toggleTranscribing);
827
- assert.calledWith(webex.internal.voicea.toggleTranscribing, true);
946
+ assert.calledTwice(webex.internal.voicea.turnOnCaptions);
828
947
  });
829
948
 
830
- it('should listen to events and not toggleTranscribing if the user is not a host', async () => {
949
+ it('should listen to events and not turnOnCaptions if the user is not a host', async () => {
831
950
  meeting.joinedWith = {
832
951
  state: 'JOINED',
833
952
  };
@@ -839,7 +958,7 @@ describe('plugin-meetings', () => {
839
958
  assert.equal(webex.internal.voicea.on.callCount, 4);
840
959
  assert.equal(meeting.areVoiceaEventsSetup, true);
841
960
  assert.equal(webex.internal.voicea.listenToEvents.callCount, 1);
842
- assert.notCalled(webex.internal.voicea.toggleTranscribing);
961
+ assert.notCalled(webex.internal.voicea.turnOnCaptions);
843
962
  });
844
963
 
845
964
  it("should throw error if request doesn't work", async () => {
@@ -858,7 +977,7 @@ describe('plugin-meetings', () => {
858
977
  webex.internal.voicea.on = sinon.stub();
859
978
  webex.internal.voicea.off = sinon.stub();
860
979
  webex.internal.voicea.listenToEvents = sinon.stub();
861
- webex.internal.voicea.toggleTranscribing = sinon.stub();
980
+ webex.internal.voicea.turnOnCaptions = sinon.stub();
862
981
  });
863
982
 
864
983
  it('should stop listening to voicea events and also trigger a stop event', () => {
@@ -1531,7 +1650,7 @@ describe('plugin-meetings', () => {
1531
1650
  };
1532
1651
  meeting.mediaProperties.setMediaDirection = sinon.stub().returns(true);
1533
1652
  meeting.mediaProperties.waitForMediaConnectionConnected = sinon.stub().resolves();
1534
- meeting.mediaProperties.getCurrentConnectionType = sinon.stub().resolves('udp');
1653
+ meeting.mediaProperties.getCurrentConnectionInfo = sinon.stub().resolves({connectionType: 'udp', selectedCandidatePairChanges: 2, numTransports: 1});
1535
1654
  meeting.audio = muteStateStub;
1536
1655
  meeting.video = muteStateStub;
1537
1656
  sinon.stub(Media, 'createMediaConnection').returns(fakeMediaConnection);
@@ -1631,11 +1750,14 @@ describe('plugin-meetings', () => {
1631
1750
  turnServerUsed: true,
1632
1751
  retriedWithTurnServer: false,
1633
1752
  isMultistream: false,
1753
+ isJoinWithMediaRetry: false,
1634
1754
  signalingState: 'unknown',
1635
1755
  connectionState: 'unknown',
1636
1756
  iceConnectionState: 'unknown',
1637
1757
  someReachabilityMetric1: 'some value1',
1638
1758
  someReachabilityMetric2: 'some value2',
1759
+ selectedCandidatePairChanges: 2,
1760
+ numTransports: 1,
1639
1761
  }
1640
1762
  );
1641
1763
  });
@@ -1735,11 +1857,14 @@ describe('plugin-meetings', () => {
1735
1857
  turnServerUsed: true,
1736
1858
  retriedWithTurnServer: false,
1737
1859
  isMultistream: false,
1860
+ isJoinWithMediaRetry: false,
1738
1861
  signalingState: 'unknown',
1739
1862
  connectionState: 'unknown',
1740
1863
  iceConnectionState: 'unknown',
1741
1864
  someReachabilityMetric1: 'some value1',
1742
1865
  someReachabilityMetric2: 'some value2',
1866
+ selectedCandidatePairChanges: 2,
1867
+ numTransports: 1,
1743
1868
  }
1744
1869
  );
1745
1870
  });
@@ -2213,9 +2338,12 @@ describe('plugin-meetings', () => {
2213
2338
  turnServerUsed: true,
2214
2339
  retriedWithTurnServer: true,
2215
2340
  isMultistream: false,
2341
+ isJoinWithMediaRetry: false,
2216
2342
  signalingState: 'unknown',
2217
2343
  connectionState: 'unknown',
2218
2344
  iceConnectionState: 'unknown',
2345
+ selectedCandidatePairChanges: 2,
2346
+ numTransports: 1,
2219
2347
  },
2220
2348
  ]);
2221
2349
 
@@ -2394,8 +2522,11 @@ describe('plugin-meetings', () => {
2394
2522
  correlation_id: meeting.correlationId,
2395
2523
  locus_id: meeting.locusUrl.split('/').pop(),
2396
2524
  connectionType: 'udp',
2525
+ selectedCandidatePairChanges: 2,
2526
+ numTransports: 1,
2397
2527
  isMultistream: false,
2398
2528
  retriedWithTurnServer: true,
2529
+ isJoinWithMediaRetry: false,
2399
2530
  },
2400
2531
  ]);
2401
2532
  meeting.roap.doTurnDiscovery;
@@ -2536,8 +2667,11 @@ describe('plugin-meetings', () => {
2536
2667
  correlation_id: meeting.correlationId,
2537
2668
  locus_id: meeting.locusUrl.split('/').pop(),
2538
2669
  connectionType: 'udp',
2670
+ selectedCandidatePairChanges: 2,
2671
+ numTransports: 1,
2539
2672
  isMultistream: false,
2540
2673
  retriedWithTurnServer: false,
2674
+ isJoinWithMediaRetry: false,
2541
2675
  someReachabilityMetric1: 'some value1',
2542
2676
  someReachabilityMetric2: 'some value2',
2543
2677
  }
@@ -2596,9 +2730,12 @@ describe('plugin-meetings', () => {
2596
2730
  turnServerUsed: true,
2597
2731
  retriedWithTurnServer: false,
2598
2732
  isMultistream: false,
2733
+ isJoinWithMediaRetry: false,
2599
2734
  signalingState: 'unknown',
2600
2735
  connectionState: 'unknown',
2601
2736
  iceConnectionState: 'unknown',
2737
+ selectedCandidatePairChanges: 2,
2738
+ numTransports: 1,
2602
2739
  }
2603
2740
  );
2604
2741
 
@@ -3017,7 +3154,7 @@ describe('plugin-meetings', () => {
3017
3154
  meeting.mediaId = 'fake media id';
3018
3155
  meeting.selfUrl = 'selfUrl';
3019
3156
  meeting.mediaProperties.waitForMediaConnectionConnected = sinon.stub().resolves();
3020
- meeting.mediaProperties.getCurrentConnectionType = sinon.stub().resolves('udp');
3157
+ meeting.mediaProperties.getCurrentConnectionInfo = sinon.stub().resolves({connectionType: 'udp', selectedCandidatePairChanges: 2, numTransports: 1});
3021
3158
  meeting.setMercuryListener = sinon.stub();
3022
3159
  meeting.locusInfo.onFullLocus = sinon.stub();
3023
3160
  meeting.webex.meetings.geoHintInfo = {regionCode: 'EU', countryCode: 'UK'};
@@ -6778,21 +6915,13 @@ describe('plugin-meetings', () => {
6778
6915
  }),
6779
6916
  };
6780
6917
  meeting.setupMediaConnectionListeners();
6781
- meeting.deferSDPAnswer = {
6782
- resolve: sinon.stub(),
6783
- reject: sinon.stub(),
6784
- };
6785
6918
  meeting.sdpResponseTimer = '1234';
6786
- meeting.mediaProperties.waitForMediaConnectionConnected = sinon.stub().resolves();
6919
+ sinon.stub(meeting.mediaProperties, 'waitForMediaConnectionConnected').resolves();
6787
6920
 
6788
- eventListeners[Event.REMOTE_SDP_ANSWER_PROCESSED]();
6789
6921
  meeting.config.reconnection.enabled = true;
6790
6922
  meeting.currentMediaStatus = {audio: true};
6791
6923
  meeting.reconnectionManager = new ReconnectionManager(meeting);
6792
- meeting.reconnectionManager.reconnect = sinon.stub().returns(Promise.resolve());
6793
- meeting.reconnectionManager.reset = sinon.stub().returns(true);
6794
- meeting.reconnectionManager.cleanup = sinon.stub().returns(true);
6795
- meeting.reconnectionManager.setStatus = sinon.stub();
6924
+ sinon.stub(meeting.reconnectionManager, 'reconnect').returns(Promise.resolve());
6796
6925
  });
6797
6926
 
6798
6927
  it('should throw error if media not established before trying reconnect', async () => {
@@ -6812,87 +6941,72 @@ describe('plugin-meetings', () => {
6812
6941
  }
6813
6942
  });
6814
6943
 
6815
- it('should trigger reconnection success and send CA metric', async () => {
6816
- await meeting.reconnect();
6944
+ it('should call the right functions', async () => {
6945
+ const options = {id: 'fake options'};
6946
+ await meeting.reconnect(options);
6947
+
6948
+ sinon.stub(meeting, 'waitForRemoteSDPAnswer').resolves();
6817
6949
 
6818
- assert.calledWith(
6819
- TriggerProxy.trigger,
6820
- sinon.match.instanceOf(Meeting),
6821
- {file: 'meeting/index', function: 'reconnect'},
6822
- 'meeting:reconnectionSuccess'
6823
- );
6824
- assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
6825
- name: 'client.media.recovered',
6826
- payload: {
6827
- recoveredBy: 'new',
6828
- },
6829
- options: {
6830
- meetingId: meeting.id,
6831
- },
6832
- });
6833
6950
  assert.calledOnceWithExactly(
6834
- meeting.reconnectionManager.setStatus,
6835
- RECONNECTION.STATE.COMPLETE
6951
+ meeting.reconnectionManager.reconnect,
6952
+ options,
6953
+ sinon.match.any
6836
6954
  );
6837
- });
6955
+ const callback = meeting.reconnectionManager.reconnect.getCalls()[0].args[1];
6838
6956
 
6839
- it('should reset after reconnection success', async () => {
6840
- await meeting.reconnect();
6841
- assert.calledOnce(meeting.reconnectionManager.reset);
6957
+ // call the completion callback
6958
+ assert.isFunction(callback);
6959
+ await callback();
6960
+
6961
+ // check that the right things were called by the callback
6962
+ assert.calledOnceWithExactly(meeting.waitForRemoteSDPAnswer);
6963
+ assert.calledOnceWithExactly(meeting.mediaProperties.waitForMediaConnectionConnected);
6842
6964
  });
6843
6965
  });
6844
6966
 
6845
6967
  describe('unsuccessful reconnect', () => {
6968
+ let logUploadSpy;
6969
+
6846
6970
  beforeEach(() => {
6847
- meeting.config.reconnection.enabled = true;
6971
+ logUploadSpy = sinon.spy(meeting, 'uploadLogs');
6848
6972
  meeting.currentMediaStatus = {audio: true};
6849
6973
  meeting.reconnectionManager = new ReconnectionManager(meeting);
6850
6974
  meeting.reconnectionManager.reconnect = sinon
6851
6975
  .stub()
6852
6976
  .returns(Promise.reject(new Error()));
6853
- meeting.reconnectionManager.reset = sinon.stub().returns(true);
6854
6977
  });
6855
6978
 
6856
- it('should trigger an unsuccessful reconnection', async () => {
6979
+ it('should upload logs on reconnect failure', async () => {
6857
6980
  await assert.isRejected(meeting.reconnect());
6858
6981
  assert.calledWith(
6859
6982
  TriggerProxy.trigger,
6860
6983
  sinon.match.instanceOf(Meeting),
6861
6984
  {file: 'meeting/index', function: 'reconnect'},
6862
- 'meeting:reconnectionFailure',
6863
- {error: sinon.match.any}
6985
+ EVENTS.REQUEST_UPLOAD_LOGS,
6986
+ sinon.match.instanceOf(Meeting)
6864
6987
  );
6865
6988
  });
6866
6989
 
6867
- it('should send metrics on reconnect failure', async () => {
6990
+ it('should fail without uploading logs if there is no reconnectionManager', async () => {
6991
+ meeting.reconnectionManager = null;
6868
6992
  await assert.isRejected(meeting.reconnect());
6869
- assert(Metrics.sendBehavioralMetric.calledOnce);
6870
- assert.calledWith(
6871
- Metrics.sendBehavioralMetric,
6872
- BEHAVIORAL_METRICS.MEETING_RECONNECT_FAILURE,
6873
- {
6874
- correlation_id: meeting.correlationId,
6875
- locus_id: meeting.locusUrl.split('/').pop(),
6876
- reason: sinon.match.any,
6877
- stack: sinon.match.any,
6878
- }
6879
- );
6993
+ assert.notCalled(logUploadSpy);
6880
6994
  });
6881
6995
 
6882
- it('should upload logs on reconnect failure', async () => {
6996
+ it('should fail without uploading logs if there is no media established', async () => {
6997
+ meeting.currentMediaStatus = null;
6883
6998
  await assert.isRejected(meeting.reconnect());
6884
- assert.calledWith(
6885
- TriggerProxy.trigger,
6886
- sinon.match.instanceOf(Meeting),
6887
- {file: 'meeting/index', function: 'reconnect'},
6888
- EVENTS.REQUEST_UPLOAD_LOGS,
6889
- sinon.match.instanceOf(Meeting)
6890
- );
6999
+ assert.notCalled(logUploadSpy);
6891
7000
  });
6892
7001
 
6893
- it('should reset after an unsuccessful reconnection', async () => {
6894
- await assert.isRejected(meeting.reconnect());
6895
- assert.calledOnce(meeting.reconnectionManager.reset);
7002
+ it('should resolve if the error is ReconnectionNotStartedError', async () => {
7003
+ meeting.reconnectionManager.reconnect.returns(
7004
+ Promise.reject(new ReconnectionNotStartedError())
7005
+ );
7006
+ await meeting.reconnect();
7007
+
7008
+ // logs shouldn't be uploaded
7009
+ assert.notCalled(logUploadSpy);
6896
7010
  });
6897
7011
  });
6898
7012
  });
@@ -8100,6 +8214,21 @@ describe('plugin-meetings', () => {
8100
8214
  EVENT_TRIGGERS.MEETING_INTERPRETATION_UPDATE
8101
8215
  );
8102
8216
  });
8217
+
8218
+ it('listens to the locus manual caption update event', () => {
8219
+ meeting.locusInfo.emit(
8220
+ {function: 'test', file: 'test'},
8221
+ 'CONTROLS_MEETING_MANUAL_CAPTION_UPDATED',
8222
+ {enable: true}
8223
+ );
8224
+
8225
+ assert.calledWith(
8226
+ TriggerProxy.trigger,
8227
+ meeting,
8228
+ {file: 'meeting/index', function: 'setupLocusControlsListener'},
8229
+ EVENT_TRIGGERS.MEETING_MANUAL_CAPTION_UPDATED
8230
+ );
8231
+ });
8103
8232
  });
8104
8233
 
8105
8234
  describe('#setUpLocusUrlListener', () => {
@@ -765,6 +765,9 @@ describe('plugin-meetings', () => {
765
765
  {functionName: 'canStartTranscribing', displayHint: 'TRANSCRIPTION_CONTROL_START'},
766
766
  {functionName: 'canStopTranscribing', displayHint: 'TRANSCRIPTION_CONTROL_STOP'},
767
767
  {functionName: 'isClosedCaptionActive', displayHint: 'CAPTION_STATUS_ACTIVE'},
768
+ {functionName: 'canStartManualCaption', displayHint: 'MANUAL_CAPTION_START'},
769
+ {functionName: 'canStopManualCaption', displayHint: 'MANUAL_CAPTION_STOP'},
770
+ {functionName: 'isManualCaptionActive', displayHint: 'MANUAL_CAPTION_STATUS_ACTIVE'},
768
771
  {functionName: 'isWebexAssistantActive', displayHint: 'WEBEX_ASSISTANT_STATUS_ACTIVE'},
769
772
  {functionName: 'canViewCaptionPanel', displayHint: 'ENABLE_CAPTION_PANEL'},
770
773
  {functionName: 'isRealTimeTranslationEnabled', displayHint: 'DISPLAY_REAL_TIME_TRANSLATION'},
@@ -513,13 +513,8 @@ describe('plugin-meetings', () => {
513
513
  });
514
514
  describe('#getAllMeetings', () => {
515
515
  it('calls MeetingCollection to get all meetings with supplied options', () => {
516
- webex.meetings.getAllMeetings({
517
- test: test1,
518
- });
516
+ webex.meetings.getAllMeetings();
519
517
  assert.calledOnce(webex.meetings.meetingCollection.getAll);
520
- assert.calledWith(webex.meetings.meetingCollection.getAll, {
521
- test: test1,
522
- });
523
518
  });
524
519
  });
525
520
  });