@webex/plugin-meetings 3.12.0-next.35 → 3.12.0-next.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.
@@ -221,6 +221,47 @@ describe('plugin-meetings', () => {
221
221
  assert.isTrue(locusInfo.emitChange);
222
222
  });
223
223
 
224
+ it('calls onLocusSynced callback passed as second argument with full locus from join response', async () => {
225
+ const syncedLocus = {url: 'http://locus-url.com', participants: []};
226
+ const onLocusSynced = sinon.stub();
227
+
228
+ await locusInfo.initialSetup(
229
+ {
230
+ trigger: 'join-response',
231
+ locus: syncedLocus,
232
+ },
233
+ onLocusSynced
234
+ );
235
+
236
+ assert.calledOnceWithExactly(onLocusSynced, syncedLocus);
237
+ });
238
+
239
+ it('swallows onLocusSynced callback errors and logs warn', async () => {
240
+ const syncedLocus = {url: 'http://locus-url.com', participants: []};
241
+ const callbackError = new Error('onLocusSynced failed');
242
+ const onLocusSynced = sinon.stub().throws(callbackError);
243
+ const loggerWarnStub = LoggerProxy.logger.warn?.isSinonProxy
244
+ ? LoggerProxy.logger.warn
245
+ : sinon.stub(LoggerProxy.logger, 'warn');
246
+
247
+ loggerWarnStub.resetHistory();
248
+
249
+ await locusInfo.initialSetup(
250
+ {
251
+ trigger: 'join-response',
252
+ locus: syncedLocus,
253
+ },
254
+ onLocusSynced
255
+ );
256
+
257
+ assert.calledOnceWithExactly(onLocusSynced, syncedLocus);
258
+ assert.calledOnce(loggerWarnStub);
259
+ assert.match(
260
+ loggerWarnStub.firstCall.args[0],
261
+ /Locus-info:index#initialSetup --> onLocusSynced callback failed/
262
+ );
263
+ });
264
+
224
265
  it('should initialize the hash tree parser correctly when triggered from a get loci response containing visible datasets', async () => {
225
266
  const visibleDataSets = ['dataset1', 'dataset2'];
226
267
  const locus = createLocusWithVisibleDataSets(visibleDataSets);
@@ -4677,6 +4718,9 @@ describe('plugin-meetings', () => {
4677
4718
  });
4678
4719
 
4679
4720
  describe('#isMeetingActive', () => {
4721
+ beforeEach(() => {
4722
+ webex.internal.newMetrics.submitClientEvent.resetHistory();
4723
+ });
4680
4724
  forEach([_CALL_, _SIP_BRIDGE_, _SPACE_SHARE_], (type) => {
4681
4725
  describe(`type = ${type}`, () => {
4682
4726
  it('sends client event correctly for state = inactive', () => {
@@ -4743,7 +4787,7 @@ describe('plugin-meetings', () => {
4743
4787
  });
4744
4788
  });
4745
4789
 
4746
- it('sends client event correctly for state = MEETING_INACTIVE_TERMINATING', () => {
4790
+ it('sends client event correctly for state = MEETING_INACTIVE', () => {
4747
4791
  locusInfo.getLocusPartner = sinon.stub().returns({state: MEETING_STATE.STATES.LEFT});
4748
4792
  locusInfo.parsedLocus = {
4749
4793
  fullState: {
@@ -4765,7 +4809,7 @@ describe('plugin-meetings', () => {
4765
4809
  });
4766
4810
  });
4767
4811
 
4768
- it('sends client event correctly for state = FULLSTATE_REMOVED', () => {
4812
+ it('does not send client event when state = INACTIVE and endMeetingReason = BREAKOUT_ENDED', () => {
4769
4813
  locusInfo.getLocusPartner = sinon.stub().returns({state: MEETING_STATE.STATES.LEFT});
4770
4814
  locusInfo.parsedLocus = {
4771
4815
  fullState: {
@@ -4774,17 +4818,41 @@ describe('plugin-meetings', () => {
4774
4818
  };
4775
4819
 
4776
4820
  locusInfo.fullState = {
4777
- removed: true,
4821
+ state: LOCUS.STATE.INACTIVE,
4822
+ endMeetingReason: 'BREAKOUT_ENDED',
4778
4823
  };
4779
4824
 
4780
4825
  locusInfo.isMeetingActive();
4781
4826
 
4782
- assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
4783
- name: 'client.call.remote-ended',
4784
- options: {
4785
- meetingId: locusInfo.meetingId,
4827
+ assert.notCalled(webex.internal.newMetrics.submitClientEvent);
4828
+ });
4829
+
4830
+ it('sends client event correctly for state self removed', () => {
4831
+ locusInfo.emitScoped = sinon.stub();
4832
+ locusInfo.parsedLocus = {
4833
+ fullState: {
4834
+ type: _MEETING_,
4786
4835
  },
4787
- });
4836
+ self: {
4837
+ removed: true,
4838
+ }
4839
+ };
4840
+
4841
+ locusInfo.isMeetingActive();
4842
+
4843
+ assert.notCalled(webex.internal.newMetrics.submitClientEvent);
4844
+ assert.calledOnceWithExactly(
4845
+ locusInfo.emitScoped,
4846
+ {
4847
+ file: 'locus-info',
4848
+ function: 'isMeetingActive',
4849
+ },
4850
+ EVENTS.DESTROY_MEETING,
4851
+ {
4852
+ reason: MEETING_REMOVED_REASON.SELF_REMOVED,
4853
+ shouldLeave: false,
4854
+ }
4855
+ );
4788
4856
  });
4789
4857
  });
4790
4858
 
@@ -11353,6 +11353,93 @@ describe('plugin-meetings', () => {
11353
11353
  });
11354
11354
  });
11355
11355
 
11356
+ describe('#finalizeMeetingAfterInitialLocusSetup', () => {
11357
+ it('refreshes destination from synced locus when destination type is LOCUS_ID', () => {
11358
+ const syncedLocus = {url: 'https://locus.example.com/locus/123', info: {topic: 'x'}};
11359
+
11360
+ meeting.destinationType = DESTINATION_TYPE.LOCUS_ID;
11361
+ meeting.destination = {info: {topic: 'old'}};
11362
+
11363
+ meeting.finalizeMeetingAfterInitialLocusSetup(syncedLocus);
11364
+
11365
+ assert.equal(meeting.destination, syncedLocus);
11366
+ });
11367
+
11368
+ it('does not refresh destination when destination type is not LOCUS_ID', () => {
11369
+ const syncedLocus = {url: 'https://locus.example.com/locus/123', info: {topic: 'x'}};
11370
+ const originalDestination = {destination: 'original-destination'};
11371
+
11372
+ meeting.destinationType = DESTINATION_TYPE.CONVERSATION_URL;
11373
+ meeting.destination = originalDestination;
11374
+
11375
+ meeting.finalizeMeetingAfterInitialLocusSetup(syncedLocus);
11376
+
11377
+ assert.equal(meeting.destination, originalDestination);
11378
+ });
11379
+
11380
+ it('fetches meeting info when meetingInfo is empty and destination has info', () => {
11381
+ const fetchMeetingInfoStub = sinon.stub(meeting, 'fetchMeetingInfo').resolves();
11382
+
11383
+ meeting.meetingInfo = {};
11384
+ meeting.destination = {url: 'https://locus.example.com/locus/123', info: {topic: 'x'}};
11385
+
11386
+ meeting.finalizeMeetingAfterInitialLocusSetup({});
11387
+
11388
+ assert.calledOnceWithExactly(fetchMeetingInfoStub, {});
11389
+ });
11390
+
11391
+ it('does not fetch meeting info when destination has no info', () => {
11392
+ const fetchMeetingInfoStub = sinon.stub(meeting, 'fetchMeetingInfo').resolves();
11393
+
11394
+ meeting.meetingInfo = {};
11395
+ meeting.destination = {url: 'https://locus.example.com/locus/123'};
11396
+
11397
+ meeting.finalizeMeetingAfterInitialLocusSetup({});
11398
+
11399
+ assert.notCalled(fetchMeetingInfoStub);
11400
+ });
11401
+
11402
+ it('does not fetch meeting info when meetingInfo is already populated', () => {
11403
+ const fetchMeetingInfoStub = sinon.stub(meeting, 'fetchMeetingInfo').resolves();
11404
+
11405
+ meeting.meetingInfo = {meetingJoinUrl: 'https://example.com/join/abc'};
11406
+ meeting.destination = {url: 'https://locus.example.com/locus/123', info: {topic: 'x'}};
11407
+
11408
+ meeting.finalizeMeetingAfterInitialLocusSetup({});
11409
+
11410
+ assert.notCalled(fetchMeetingInfoStub);
11411
+ });
11412
+
11413
+ it('does not fetch meeting info when delayed fetch timer is already scheduled', () => {
11414
+ const fetchMeetingInfoStub = sinon.stub(meeting, 'fetchMeetingInfo').resolves();
11415
+
11416
+ meeting.meetingInfo = {};
11417
+ meeting.destination = {url: 'https://locus.example.com/locus/123', info: {topic: 'x'}};
11418
+ meeting.fetchMeetingInfoTimeoutId = 42;
11419
+
11420
+ meeting.finalizeMeetingAfterInitialLocusSetup({});
11421
+
11422
+ assert.notCalled(fetchMeetingInfoStub);
11423
+ });
11424
+
11425
+ it('swallows async fetchMeetingInfo errors and logs info', async () => {
11426
+ const error = new Error('fetch failed');
11427
+
11428
+ meeting.meetingInfo = {};
11429
+ meeting.destination = {url: 'https://locus.example.com/locus/123', info: {topic: 'x'}};
11430
+ sinon.stub(meeting, 'fetchMeetingInfo').returns(Promise.reject(error));
11431
+ const loggerInfoStub = sinon.stub(LoggerProxy.logger, 'info');
11432
+
11433
+ await meeting.finalizeMeetingAfterInitialLocusSetup({});
11434
+
11435
+ assert.calledOnce(loggerInfoStub);
11436
+ assert.match(
11437
+ loggerInfoStub.firstCall.args[0],
11438
+ /Meeting:index#finalizeMeetingAfterInitialLocusSetup --> deferred fetchMeetingInfo failed: fetch failed/
11439
+ );
11440
+ });
11441
+ });
11442
+
11356
11443
  describe('#emailInput', () => {
11357
11444
  it('should set the email input', () => {
11358
11445
  assert.notOk(meeting.emailInput);
@@ -1489,7 +1489,7 @@ describe('plugin-meetings', () => {
1489
1489
  url: url1,
1490
1490
  },
1491
1491
  hashTreeMessage: undefined,
1492
- });
1492
+ }, sinon.match.func);
1493
1493
  });
1494
1494
  });
1495
1495
  describe('when destroying meeting is needed', () => {
@@ -2137,7 +2137,7 @@ describe('plugin-meetings', () => {
2137
2137
  },
2138
2138
  },
2139
2139
  hashTreeMessage: undefined,
2140
- });
2140
+ }, sinon.match.func);
2141
2141
  });
2142
2142
  it('should setup the meeting from a hash tree event', async () => {
2143
2143
  const selfData = {};
@@ -2171,7 +2171,7 @@ describe('plugin-meetings', () => {
2171
2171
  info: infoData,
2172
2172
  },
2173
2173
  hashTreeMessage,
2174
- });
2174
+ }, sinon.match.func);
2175
2175
  });
2176
2176
 
2177
2177
  it('should ignore hash tree event when created locus has INACTIVE fullState', async () => {
@@ -2251,7 +2251,7 @@ describe('plugin-meetings', () => {
2251
2251
  },
2252
2252
  },
2253
2253
  hashTreeMessage: undefined,
2254
- });
2254
+ }, sinon.match.func);
2255
2255
  });
2256
2256
 
2257
2257
  it('sends client event correctly on finally', async () => {
@@ -2327,7 +2327,7 @@ describe('plugin-meetings', () => {
2327
2327
  },
2328
2328
  },
2329
2329
  hashTreeMessage: undefined,
2330
- });
2330
+ }, sinon.match.func);
2331
2331
  });
2332
2332
 
2333
2333
  const generateFakeLocusData = (isUnifiedSpaceMeeting) => ({
@@ -238,6 +238,19 @@ describe('plugin-meetings', () => {
238
238
  });
239
239
  });
240
240
 
241
+ describe('#isWholeMeetingEnded', () => {
242
+ [
243
+ {description: 'state is INACTIVE with no endMeetingReason', fullState: {state: 'INACTIVE'}, expected: true},
244
+ {description: 'state is INACTIVE with endMeetingReason OTHER', fullState: {state: 'INACTIVE', endMeetingReason: 'SOME_OTHER_REASON'}, expected: true},
245
+ {description: 'state is INACTIVE with endMeetingReason BREAKOUT_ENDED', fullState: {state: 'INACTIVE', endMeetingReason: 'BREAKOUT_ENDED'}, expected: false},
246
+ {description: 'state is not INACTIVE', fullState: {state: 'ACTIVE', endMeetingReason: 'SOME_OTHER_REASON'}, expected: false},
247
+ ].forEach(({description, fullState, expected}) => {
248
+ it(`returns ${expected} when ${description}`, () => {
249
+ assert.equal(MeetingsUtil.isWholeMeetingEnded(fullState), expected);
250
+ });
251
+ });
252
+ });
253
+
241
254
  describe('#isSelfMovedOrBreakoutEnded', () => {
242
255
  [
243
256
  {description: 'locus is undefined', locus: undefined, expected: false},