@webex/plugin-meetings 3.0.0-beta.186 → 3.0.0-beta.188

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.
@@ -3,6 +3,7 @@ import sinon from 'sinon';
3
3
  import {cloneDeep} from 'lodash';
4
4
  import {assert} from '@webex/test-helper-chai';
5
5
  import MockWebex from '@webex/test-helper-mock-webex';
6
+ import testUtils from '../../../utils/testUtils';
6
7
  import Meetings from '@webex/plugin-meetings';
7
8
  import LocusInfo from '@webex/plugin-meetings/src/locus-info';
8
9
  import SelfUtils from '@webex/plugin-meetings/src/locus-info/selfUtils';
@@ -1716,7 +1717,7 @@ describe('plugin-meetings', () => {
1716
1717
  getLocusDTO: sandbox.stub().resolves({body: fakeDeltaLocus}),
1717
1718
  },
1718
1719
  locusInfo: {
1719
- onDeltaLocus: sandbox.stub(),
1720
+ handleLocusDelta: sandbox.stub(),
1720
1721
  },
1721
1722
  locusUrl: 'oldLocusUrl',
1722
1723
  };
@@ -1727,14 +1728,14 @@ describe('plugin-meetings', () => {
1727
1728
 
1728
1729
  // Since we have a promise inside a function we want to test that's not returned,
1729
1730
  // we will wait and stub it's last function to resolve this waiting promise.
1730
- // Also ensures .onDeltaLocus() is called before .resume()
1731
+ // Also ensures .handleLocusDelta() is called before .resume()
1731
1732
  return new Promise((resolve) => {
1732
1733
  locusInfo.locusParser.resume = sandbox.stub().callsFake(() => resolve());
1733
1734
  locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
1734
1735
  }).then(() => {
1735
1736
  assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, { url: 'oldSyncUrl' });
1736
1737
 
1737
- assert.calledOnceWithExactly(meeting.locusInfo.onDeltaLocus, fakeDeltaLocus);
1738
+ assert.calledOnceWithExactly(meeting.locusInfo.handleLocusDelta, fakeDeltaLocus, meeting);
1738
1739
  assert.calledOnce(locusInfo.locusParser.resume);
1739
1740
  });
1740
1741
  });
@@ -1746,7 +1747,7 @@ describe('plugin-meetings', () => {
1746
1747
  getLocusDTO: sandbox.stub().resolves({body: {}}),
1747
1748
  },
1748
1749
  locusInfo: {
1749
- onDeltaLocus: sandbox.stub(),
1750
+ handleLocusDelta: sandbox.stub(),
1750
1751
  onFullLocus: sandbox.stub(),
1751
1752
  },
1752
1753
  locusUrl: 'oldLocusUrl',
@@ -1758,14 +1759,13 @@ describe('plugin-meetings', () => {
1758
1759
 
1759
1760
  // Since we have a promise inside a function we want to test that's not returned,
1760
1761
  // we will wait and stub it's last function to resolve this waiting promise.
1761
- // Also ensures .onDeltaLocus() is called before .resume()
1762
1762
  return new Promise((resolve) => {
1763
1763
  locusInfo.locusParser.resume = sandbox.stub().callsFake(() => resolve());
1764
1764
  locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
1765
1765
  }).then(() => {
1766
1766
  assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, { url: 'oldSyncUrl' });
1767
1767
 
1768
- assert.notCalled(meeting.locusInfo.onDeltaLocus);
1768
+ assert.notCalled(meeting.locusInfo.handleLocusDelta);
1769
1769
  assert.notCalled(meeting.locusInfo.onFullLocus);
1770
1770
  assert.calledOnce(locusInfo.locusParser.resume);
1771
1771
  });
@@ -2130,5 +2130,295 @@ describe('plugin-meetings', () => {
2130
2130
  });
2131
2131
  });
2132
2132
  });
2133
+
2134
+ // semi-integration tests that use real LocusInfo with real Parser
2135
+ // and test various scenarios related to handling out-of-order Locus delta events
2136
+ describe('handling of out-of-order Locus delta events', () => {
2137
+ let clock;
2138
+
2139
+ const generateDeltaEvent = (base, sequence) => {
2140
+ return {
2141
+ baseSequence: {
2142
+ rangeStart: 0,
2143
+ rangeEnd: 0,
2144
+ entries: [base]
2145
+ },
2146
+ sequence: {
2147
+ rangeStart: 0,
2148
+ rangeEnd: 0,
2149
+ entries: [sequence]
2150
+ },
2151
+ syncUrl: `fake sync url for sequence ${sequence}`,
2152
+ self: {
2153
+ person: {
2154
+ id: 'test person id'
2155
+ }
2156
+ },
2157
+ }
2158
+ };
2159
+
2160
+ // a list of example delta events, sorted by time and each event is based on the previous one
2161
+ const deltaEvents = [
2162
+ generateDeltaEvent(10, 20), // 0
2163
+ generateDeltaEvent(20, 30), // 1
2164
+ generateDeltaEvent(30, 40), // 2
2165
+ generateDeltaEvent(40, 50), // 3
2166
+ generateDeltaEvent(50, 60), // 4
2167
+ generateDeltaEvent(60, 70), // 5
2168
+ generateDeltaEvent(70, 80), // 6
2169
+ generateDeltaEvent(80, 90), // 7
2170
+ generateDeltaEvent(90, 100), // 8
2171
+ ];
2172
+
2173
+ let updateLocusInfoStub; // we use this stub to verify that an event has been fully processed
2174
+ let syncRequestStub;
2175
+
2176
+ beforeEach(() => {
2177
+ clock = sinon.useFakeTimers();
2178
+
2179
+ sinon.stub(locusInfo, 'updateParticipantDeltas');
2180
+ sinon.stub(locusInfo, 'updateParticipants');
2181
+ sinon.stub(locusInfo, 'isMeetingActive'),
2182
+ sinon.stub(locusInfo, 'handleOneOnOneEvent'),
2183
+
2184
+ updateLocusInfoStub = sinon.stub(locusInfo, 'updateLocusInfo');
2185
+ syncRequestStub = sinon.stub().resolves({body: {}});
2186
+
2187
+ mockMeeting.locusInfo = locusInfo;
2188
+ mockMeeting.locusUrl = 'fake locus url';
2189
+ mockMeeting.meetingRequest = {
2190
+ getLocusDTO: syncRequestStub,
2191
+ };
2192
+
2193
+ locusInfo.onFullLocus({
2194
+ sequence: {
2195
+ rangeStart: 0,
2196
+ rangeEnd: 0,
2197
+ entries: [10]
2198
+ },
2199
+ self: {
2200
+ person: {
2201
+ id: 'test person id'
2202
+ }
2203
+ },
2204
+ });
2205
+
2206
+ updateLocusInfoStub.resetHistory();
2207
+ });
2208
+
2209
+ afterEach(() => {
2210
+ clock.restore();
2211
+ });
2212
+
2213
+ it('queues out-of-order deltas until it receives a correct delta', () => {
2214
+ // send some out-of-order deltas
2215
+ locusInfo.handleLocusDelta(deltaEvents[1], mockMeeting);
2216
+ locusInfo.handleLocusDelta(deltaEvents[4], mockMeeting);
2217
+
2218
+ // they should be queued and not processed
2219
+ assert.notCalled(updateLocusInfoStub);
2220
+
2221
+ // now one of the missing ones, but not the one SDK is really waiting for
2222
+ locusInfo.handleLocusDelta(deltaEvents[2], mockMeeting);
2223
+
2224
+ // still nothing should be processed
2225
+ assert.notCalled(updateLocusInfoStub);
2226
+
2227
+ // now send the one SDK is waiting for
2228
+ locusInfo.handleLocusDelta(deltaEvents[0], mockMeeting);
2229
+
2230
+ // so deltaEvents with indexes 1,2,3 can be processed, but 5 still not, because 4 is missing
2231
+ assert.callCount(updateLocusInfoStub, 3);
2232
+ assert.calledWith(updateLocusInfoStub.getCall(0), deltaEvents[0]);
2233
+ assert.calledWith(updateLocusInfoStub.getCall(1), deltaEvents[1]);
2234
+ assert.calledWith(updateLocusInfoStub.getCall(2), deltaEvents[2]);
2235
+
2236
+ updateLocusInfoStub.resetHistory();
2237
+
2238
+ // now send deltaEvents[4]
2239
+ locusInfo.handleLocusDelta(deltaEvents[3], mockMeeting);
2240
+
2241
+ // and verify deltaEvents[4] and deltaEvents[5] have been processed
2242
+ assert.callCount(updateLocusInfoStub, 2);
2243
+ assert.calledWith(updateLocusInfoStub.getCall(0), deltaEvents[3]);
2244
+ assert.calledWith(updateLocusInfoStub.getCall(1), deltaEvents[4]);
2245
+ });
2246
+
2247
+ it('handles out-of-order deltas correctly even if all arrive in reverse order', () => {
2248
+ // send a bunch deltas in reverse order
2249
+ for(let i = 4; i >= 0; i--) {
2250
+ locusInfo.handleLocusDelta(deltaEvents[i], mockMeeting);
2251
+ }
2252
+
2253
+ // they should be queued and then processed in correct order
2254
+ assert.callCount(updateLocusInfoStub, 5);
2255
+ assert.calledWith(updateLocusInfoStub.getCall(0), deltaEvents[0]);
2256
+ assert.calledWith(updateLocusInfoStub.getCall(1), deltaEvents[1]);
2257
+ assert.calledWith(updateLocusInfoStub.getCall(2), deltaEvents[2]);
2258
+ assert.calledWith(updateLocusInfoStub.getCall(3), deltaEvents[3]);
2259
+ assert.calledWith(updateLocusInfoStub.getCall(4), deltaEvents[4]);
2260
+ });
2261
+
2262
+ it('sends a sync request using syncUrl if it receives at least 1 delta event and processes later deltas after sync correctly', async () => {
2263
+ // the test first sends an initial "good" delta
2264
+ const initialDeltaIdx = 0;
2265
+ const initialDelta = deltaEvents[initialDeltaIdx];
2266
+
2267
+ // then it sends a bunch of out-of-order deltas (at least 6 to trigger a sync), last one being lastOooDelta
2268
+ const firstOooDeltaIdx = 2;
2269
+ const lastOooDeltaIdx = 7;
2270
+ const lastOooDelta = deltaEvents[lastOooDeltaIdx];
2271
+
2272
+ // and finally, after the sync it sends another "good" delta
2273
+ const goodDeltaAfterSync = deltaEvents[8];
2274
+
2275
+ const deltaLocusFromSyncResponse = {
2276
+ baseSequence: {
2277
+ rangeStart: 0,
2278
+ rangeEnd: 0,
2279
+ entries: [initialDelta.sequence.entries[0]]
2280
+ },
2281
+ sequence: {
2282
+ rangeStart: 0,
2283
+ rangeEnd: 0,
2284
+ entries: [lastOooDelta.sequence.entries[0]]
2285
+ },
2286
+ syncUrl: `fake sync url for sequence ${lastOooDelta.sequence.entries[0]}`,
2287
+ self: {
2288
+ person: {
2289
+ id: 'test person id'
2290
+ }
2291
+ },
2292
+ };
2293
+
2294
+ syncRequestStub.resolves({
2295
+ body: deltaLocusFromSyncResponse
2296
+ });
2297
+
2298
+ // send one correct delta so that SDK has the syncUrl
2299
+ locusInfo.handleLocusDelta(initialDelta, mockMeeting);
2300
+
2301
+ updateLocusInfoStub.resetHistory();
2302
+
2303
+ // send 6 out-of-order deltas to trigger a sync (we're skipping deltaEvents[1])
2304
+ for(let i = firstOooDeltaIdx; i <= lastOooDeltaIdx; i++) {
2305
+ locusInfo.handleLocusDelta(deltaEvents[i], mockMeeting);
2306
+ }
2307
+
2308
+ await testUtils.flushPromises();
2309
+
2310
+ // check that sync was done using the correct syncUrl
2311
+ assert.calledOnceWithExactly(syncRequestStub, {url: initialDelta.syncUrl});
2312
+ assert.calledOnceWithExactly(updateLocusInfoStub, deltaLocusFromSyncResponse);
2313
+
2314
+ updateLocusInfoStub.resetHistory();
2315
+
2316
+ // now send another delta - a good one, it should be processed as normal
2317
+ locusInfo.handleLocusDelta(goodDeltaAfterSync, mockMeeting);
2318
+
2319
+ assert.calledOnceWithExactly(updateLocusInfoStub, goodDeltaAfterSync);
2320
+ });
2321
+
2322
+ it('does a sync if blocked on out-of-order deltas for too long', async () => {
2323
+ // stub random so that the timer fires after 12500 ms
2324
+ sinon.stub(Math, 'random').returns(0.5);
2325
+
2326
+ const oooDelta = deltaEvents[3];
2327
+
2328
+ // setup the stubs so that the sync request receives a full DTO with the sequence equal to the out-of-order delta we simulate
2329
+ const fullLocus = {
2330
+ sequence: oooDelta.sequence
2331
+ };
2332
+ syncRequestStub.resolves({
2333
+ body: fullLocus
2334
+ });
2335
+
2336
+ // send an out-of-order delta
2337
+ locusInfo.handleLocusDelta(oooDelta, mockMeeting);
2338
+
2339
+ await clock.tickAsync(12499);
2340
+ await testUtils.flushPromises();
2341
+ assert.notCalled(syncRequestStub);
2342
+ assert.notCalled(updateLocusInfoStub);
2343
+
2344
+ await clock.tickAsync(1);
2345
+ await testUtils.flushPromises();
2346
+
2347
+ assert.calledOnceWithExactly(syncRequestStub, {url: mockMeeting.locusUrl});
2348
+ assert.calledOnceWithExactly(updateLocusInfoStub, fullLocus);
2349
+ });
2350
+
2351
+ it('does a sync if out-of-order deltas queue becomes too big', async () => {
2352
+ // setup the stubs so that the sync request receives a full DTO with the sequence equal to the out-of-order delta we simulate
2353
+ const fullLocus = {
2354
+ sequence: deltaEvents[6].sequence
2355
+ };
2356
+ syncRequestStub.resolves({
2357
+ body: fullLocus
2358
+ });
2359
+
2360
+ // send 5 deltas, starting from deltaEvents[1] so that SDK is blocked waiting for deltaEvents[0]
2361
+ for(let i = 0; i < 5; i++) {
2362
+ locusInfo.handleLocusDelta(deltaEvents[i + 1], mockMeeting);
2363
+ }
2364
+
2365
+ // nothing should happen, SDK should still be waiting for deltaEvents[0]
2366
+ assert.notCalled(syncRequestStub);
2367
+ assert.notCalled(updateLocusInfoStub);
2368
+
2369
+ // now send one more out-of-order delta to trigger a sync request
2370
+ locusInfo.handleLocusDelta(deltaEvents[6], mockMeeting);
2371
+
2372
+ await testUtils.flushPromises();
2373
+
2374
+ // check sync was done
2375
+ assert.calledOnceWithExactly(syncRequestStub, {url: mockMeeting.locusUrl});
2376
+ assert.calledOnceWithExactly(updateLocusInfoStub, fullLocus);
2377
+ });
2378
+
2379
+ it('processes delta events that are not included in sync response', async () => {
2380
+ // this test sends a bunch of out-of-order deltas, this triggers a sync
2381
+ // but the full locus response doesn't include the last 2 deltas received, so
2382
+ // we check that these 2 deltas are also processed after sync response
2383
+ const fullLocusFromSyncResponse = {
2384
+ baseSequence: {
2385
+ rangeStart: 0,
2386
+ rangeEnd: 0,
2387
+ entries: [deltaEvents[0].sequence.entries[0]]
2388
+ },
2389
+ sequence: {
2390
+ rangeStart: 0,
2391
+ rangeEnd: 0,
2392
+ entries: [deltaEvents[5].sequence.entries[0]]
2393
+ },
2394
+ syncUrl: `fake sync url for sequence ${deltaEvents[5].sequence.entries[0]}`,
2395
+ self: {
2396
+ person: {
2397
+ id: 'test person id'
2398
+ }
2399
+ },
2400
+ };
2401
+
2402
+ syncRequestStub.resolves({
2403
+ body: fullLocusFromSyncResponse
2404
+ });
2405
+
2406
+ // send at least 6 out-of-order deltas to trigger a sync (we're skipping deltaEvents[0])
2407
+ for(let i = 1; i <= 7; i++) {
2408
+ locusInfo.handleLocusDelta(deltaEvents[i], mockMeeting);
2409
+ }
2410
+
2411
+ await testUtils.flushPromises();
2412
+
2413
+ // check that sync was done
2414
+ assert.calledOnceWithExactly(syncRequestStub, {url: mockMeeting.locusUrl});
2415
+
2416
+ // and that remaining deltas from the queue that were not included in full Locus were also processed
2417
+ assert.callCount(updateLocusInfoStub, 3);
2418
+ assert.calledWith(updateLocusInfoStub.getCall(0), fullLocusFromSyncResponse);
2419
+ assert.calledWith(updateLocusInfoStub.getCall(1), deltaEvents[6]);
2420
+ assert.calledWith(updateLocusInfoStub.getCall(2), deltaEvents[7]);
2421
+ });
2422
+ });
2133
2423
  });
2134
2424
  });
@@ -334,27 +334,5 @@ describe('locus-info/parser', () => {
334
334
 
335
335
  assert.isFalse(result);
336
336
  });
337
-
338
- it('sets parser status to IDLE if workingCopy is invalid', () => {
339
- const {IDLE, WORKING} = LocusDeltaParser.status;
340
-
341
- parser.workingCopy = null;
342
- parser.status = WORKING;
343
-
344
- parser.isValidLocus(loci);
345
-
346
- assert.equal(parser.status, IDLE);
347
- });
348
-
349
- it('sets parser status to IDLE if new loci is invalid', () => {
350
- const {IDLE, WORKING} = LocusDeltaParser.status;
351
-
352
- parser.workingCopy = loci;
353
- parser.status = WORKING;
354
-
355
- parser.isValidLocus(null);
356
-
357
- assert.equal(parser.status, IDLE);
358
- });
359
337
  });
360
338
  });
@@ -66,6 +66,7 @@ describe('plugin-meetings', () => {
66
66
  canShareCamera: null,
67
67
  canShareDesktop: null,
68
68
  canShareContent: null,
69
+ canTransferFile: null,
69
70
  ...expected,
70
71
  };
71
72
 
@@ -137,6 +138,7 @@ describe('plugin-meetings', () => {
137
138
  'canShareCamera',
138
139
  'canShareDesktop',
139
140
  'canShareContent',
141
+ 'canTransferFile',
140
142
  ].forEach((key) => {
141
143
  it(`get and set for ${key} work as expected`, () => {
142
144
  const inMeetingActions = new InMeetingActions();
@@ -5684,11 +5684,21 @@ describe('plugin-meetings', () => {
5684
5684
  requiredDisplayHints: [DISPLAY_HINTS.SHARE_CAMERA],
5685
5685
  requiredPolicies: [SELF_POLICY.SUPPORT_CAMERA_SHARE],
5686
5686
  },
5687
+ {
5688
+ actionName: 'canBroadcastMessageToBreakout',
5689
+ requiredDisplayHints: [DISPLAY_HINTS.BROADCAST_MESSAGE_TO_BREAKOUT],
5690
+ requiredPolicies: [SELF_POLICY.SUPPORT_BROADCAST_MESSAGE],
5691
+ },
5687
5692
  {
5688
5693
  actionName: 'canShareDesktop',
5689
5694
  requiredDisplayHints: [DISPLAY_HINTS.SHARE_DESKTOP],
5690
5695
  requiredPolicies: [SELF_POLICY.SUPPORT_DESKTOP_SHARE],
5691
5696
  },
5697
+ {
5698
+ actionName: 'canTransferFile',
5699
+ requiredDisplayHints: [],
5700
+ requiredPolicies: [SELF_POLICY.SUPPORT_FILE_TRANSFER],
5701
+ },
5692
5702
  ],
5693
5703
  ({actionName, requiredDisplayHints, requiredPolicies}) => {
5694
5704
  it(`${actionName} is enabled when the conditions are met`, () => {
@@ -5713,27 +5723,32 @@ describe('plugin-meetings', () => {
5713
5723
  assert.isTrue(meeting.inMeetingActions.get()[actionName]);
5714
5724
  });
5715
5725
 
5716
- it(`${actionName} is disabled when the required display hints are missing`, () => {
5717
- meeting.selfUserPolicies = {};
5726
+ if (requiredDisplayHints.length !== 0) {
5727
+
5728
+ it(`${actionName} is disabled when the required display hints are missing`, () => {
5729
+ meeting.selfUserPolicies = {};
5718
5730
 
5719
- forEach(requiredPolicies, (policy) => {
5720
- meeting.selfUserPolicies[policy] = true;
5721
- });
5731
+ forEach(requiredPolicies, (policy) => {
5732
+ meeting.selfUserPolicies[policy] = true;
5733
+ });
5722
5734
 
5723
- meeting.setUpLocusInfoMeetingInfoListener();
5735
+ meeting.setUpLocusInfoMeetingInfoListener();
5724
5736
 
5725
- const callback = locusInfoOnSpy.thirdCall.args[1];
5737
+ const callback = locusInfoOnSpy.thirdCall.args[1];
5726
5738
 
5727
- const payload = {
5728
- info: {
5729
- userDisplayHints: [],
5730
- },
5731
- };
5739
+ const payload = {
5740
+ info: {
5741
+ userDisplayHints: [],
5742
+ },
5743
+ };
5732
5744
 
5733
- callback(payload);
5745
+ callback(payload);
5746
+
5747
+ assert.isFalse(meeting.inMeetingActions.get()[actionName]);
5748
+ });
5749
+
5750
+ }
5734
5751
 
5735
- assert.isFalse(meeting.inMeetingActions.get()[actionName]);
5736
- });
5737
5752
 
5738
5753
  it(`${actionName} is disabled when the required policies are missing`, () => {
5739
5754
  meeting.selfUserPolicies = {};
@@ -704,8 +704,13 @@ describe('plugin-meetings', () => {
704
704
 
705
705
  describe('canBroadcastMessageToBreakout', () => {
706
706
  it('works as expected', () => {
707
- assert.deepEqual(MeetingUtil.canBroadcastMessageToBreakout(['BROADCAST_MESSAGE_TO_BREAKOUT']), true);
708
- assert.deepEqual(MeetingUtil.canBroadcastMessageToBreakout([]), false);
707
+ assert.deepEqual(MeetingUtil.canBroadcastMessageToBreakout(['BROADCAST_MESSAGE_TO_BREAKOUT'], {
708
+ [SELF_POLICY.SUPPORT_BROADCAST_MESSAGE]: true
709
+ }), true);
710
+ assert.deepEqual(MeetingUtil.canBroadcastMessageToBreakout([], {[SELF_POLICY.SUPPORT_BROADCAST_MESSAGE]: true}), false);
711
+ assert.deepEqual(MeetingUtil.canBroadcastMessageToBreakout(['BROADCAST_MESSAGE_TO_BREAKOUT'], {[SELF_POLICY.SUPPORT_BROADCAST_MESSAGE]: false}), false);
712
+ assert.deepEqual(MeetingUtil.canBroadcastMessageToBreakout(['BROADCAST_MESSAGE_TO_BREAKOUT'], undefined), false);
713
+
709
714
  });
710
715
  });
711
716