@webex/plugin-meetings 3.12.0-next.5 → 3.12.0-next.51
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/AGENTS.md +9 -0
- package/dist/aiEnableRequest/index.js +15 -2
- package/dist/aiEnableRequest/index.js.map +1 -1
- package/dist/breakouts/breakout.js +6 -2
- package/dist/breakouts/breakout.js.map +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/constants.js +6 -3
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/constants.js +11 -1
- package/dist/controls-options-manager/constants.js.map +1 -1
- package/dist/controls-options-manager/index.js +38 -24
- package/dist/controls-options-manager/index.js.map +1 -1
- package/dist/controls-options-manager/util.js +91 -0
- package/dist/controls-options-manager/util.js.map +1 -1
- package/dist/hashTree/constants.js +10 -1
- package/dist/hashTree/constants.js.map +1 -1
- package/dist/hashTree/hashTreeParser.js +646 -371
- package/dist/hashTree/hashTreeParser.js.map +1 -1
- package/dist/hashTree/utils.js +22 -0
- package/dist/hashTree/utils.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/interceptors/locusRetry.js +23 -8
- package/dist/interceptors/locusRetry.js.map +1 -1
- package/dist/interpretation/index.js +10 -1
- package/dist/interpretation/index.js.map +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/controlsUtils.js +4 -1
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +289 -86
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/types.js +19 -0
- package/dist/locus-info/types.js.map +1 -1
- package/dist/media/properties.js +1 -0
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +3 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +842 -521
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/util.js +19 -2
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +205 -77
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/meetings.types.js +6 -1
- package/dist/meetings/meetings.types.js.map +1 -1
- package/dist/meetings/request.js +39 -0
- package/dist/meetings/request.js.map +1 -1
- package/dist/meetings/util.js +67 -5
- package/dist/meetings/util.js.map +1 -1
- package/dist/member/index.js +10 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/types.js.map +1 -1
- package/dist/member/util.js +3 -0
- package/dist/member/util.js.map +1 -1
- package/dist/metrics/constants.js +2 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/recording-controller/index.js +1 -3
- package/dist/recording-controller/index.js.map +1 -1
- package/dist/types/config.d.ts +1 -0
- package/dist/types/constants.d.ts +2 -0
- package/dist/types/controls-options-manager/constants.d.ts +6 -1
- package/dist/types/controls-options-manager/index.d.ts +10 -0
- package/dist/types/hashTree/constants.d.ts +1 -0
- package/dist/types/hashTree/hashTreeParser.d.ts +83 -16
- package/dist/types/hashTree/utils.d.ts +11 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/interceptors/locusRetry.d.ts +4 -4
- package/dist/types/locus-info/index.d.ts +46 -6
- package/dist/types/locus-info/types.d.ts +21 -1
- package/dist/types/media/properties.d.ts +1 -0
- package/dist/types/meeting/in-meeting-actions.d.ts +2 -0
- package/dist/types/meeting/index.d.ts +70 -1
- package/dist/types/meeting/util.d.ts +8 -0
- package/dist/types/meetings/index.d.ts +20 -2
- package/dist/types/meetings/meetings.types.d.ts +15 -0
- package/dist/types/meetings/request.d.ts +14 -0
- package/dist/types/member/index.d.ts +1 -0
- package/dist/types/member/types.d.ts +1 -0
- package/dist/types/member/util.d.ts +1 -0
- package/dist/types/metrics/constants.d.ts +1 -0
- package/dist/webinar/index.js +361 -235
- package/dist/webinar/index.js.map +1 -1
- package/package.json +22 -22
- package/src/aiEnableRequest/index.ts +16 -0
- package/src/breakouts/breakout.ts +2 -1
- package/src/config.ts +1 -0
- package/src/constants.ts +5 -1
- package/src/controls-options-manager/constants.ts +14 -1
- package/src/controls-options-manager/index.ts +47 -24
- package/src/controls-options-manager/util.ts +81 -1
- package/src/hashTree/constants.ts +9 -0
- package/src/hashTree/hashTreeParser.ts +362 -174
- package/src/hashTree/utils.ts +17 -0
- package/src/index.ts +5 -0
- package/src/interceptors/locusRetry.ts +25 -4
- package/src/interpretation/index.ts +25 -8
- package/src/locus-info/controlsUtils.ts +3 -1
- package/src/locus-info/index.ts +291 -93
- package/src/locus-info/types.ts +25 -1
- package/src/media/properties.ts +1 -0
- package/src/meeting/in-meeting-actions.ts +4 -0
- package/src/meeting/index.ts +315 -26
- package/src/meeting/util.ts +20 -2
- package/src/meetings/index.ts +109 -43
- package/src/meetings/meetings.types.ts +19 -0
- package/src/meetings/request.ts +43 -0
- package/src/meetings/util.ts +80 -1
- package/src/member/index.ts +10 -0
- package/src/member/types.ts +1 -0
- package/src/member/util.ts +3 -0
- package/src/metrics/constants.ts +1 -0
- package/src/recording-controller/index.ts +1 -2
- package/src/webinar/index.ts +162 -21
- package/test/unit/spec/aiEnableRequest/index.ts +86 -0
- package/test/unit/spec/breakouts/breakout.ts +7 -3
- package/test/unit/spec/controls-options-manager/index.js +140 -29
- package/test/unit/spec/controls-options-manager/util.js +165 -0
- package/test/unit/spec/hashTree/hashTreeParser.ts +1341 -140
- package/test/unit/spec/hashTree/utils.ts +88 -1
- package/test/unit/spec/interceptors/locusRetry.ts +205 -4
- package/test/unit/spec/interpretation/index.ts +26 -4
- package/test/unit/spec/locus-info/controlsUtils.js +172 -57
- package/test/unit/spec/locus-info/index.js +475 -81
- package/test/unit/spec/meeting/in-meeting-actions.ts +2 -0
- package/test/unit/spec/meeting/index.js +836 -41
- package/test/unit/spec/meeting/muteState.js +3 -0
- package/test/unit/spec/meeting/utils.js +33 -0
- package/test/unit/spec/meetings/index.js +309 -10
- package/test/unit/spec/meetings/request.js +141 -0
- package/test/unit/spec/meetings/utils.js +161 -0
- package/test/unit/spec/member/index.js +7 -0
- package/test/unit/spec/member/util.js +24 -0
- package/test/unit/spec/recording-controller/index.js +9 -8
- package/test/unit/spec/webinar/index.ts +141 -16
|
@@ -11,6 +11,7 @@ describe('plugin-meetings', () => {
|
|
|
11
11
|
let audio;
|
|
12
12
|
let video;
|
|
13
13
|
let originalRemoteUpdateAudioVideo;
|
|
14
|
+
let originalUpdateLocusFromApiResponse;
|
|
14
15
|
|
|
15
16
|
const fakeLocusResponse = {body: {locus: {info: 'this is a fake locus'}}};
|
|
16
17
|
|
|
@@ -45,6 +46,7 @@ describe('plugin-meetings', () => {
|
|
|
45
46
|
};
|
|
46
47
|
|
|
47
48
|
originalRemoteUpdateAudioVideo = MeetingUtil.remoteUpdateAudioVideo;
|
|
49
|
+
originalUpdateLocusFromApiResponse = MeetingUtil.updateLocusFromApiResponse;
|
|
48
50
|
|
|
49
51
|
MeetingUtil.remoteUpdateAudioVideo = sinon.stub().resolves(fakeLocusResponse);
|
|
50
52
|
MeetingUtil.updateLocusFromApiResponse = sinon.stub();
|
|
@@ -57,6 +59,7 @@ describe('plugin-meetings', () => {
|
|
|
57
59
|
|
|
58
60
|
afterEach(() => {
|
|
59
61
|
MeetingUtil.remoteUpdateAudioVideo = originalRemoteUpdateAudioVideo;
|
|
62
|
+
MeetingUtil.updateLocusFromApiResponse = originalUpdateLocusFromApiResponse;
|
|
60
63
|
});
|
|
61
64
|
|
|
62
65
|
describe('mute state library', () => {
|
|
@@ -60,6 +60,7 @@ describe('plugin-meetings', () => {
|
|
|
60
60
|
meeting.annotaion = {cleanUp: sinon.stub()};
|
|
61
61
|
meeting.getWebexObject = sinon.stub().returns(webex);
|
|
62
62
|
meeting.simultaneousInterpretation = {cleanUp: sinon.stub()};
|
|
63
|
+
meeting.locusInfo = {cleanUp: sinon.stub()};
|
|
63
64
|
meeting.trigger = sinon.stub();
|
|
64
65
|
meeting.webex = webex;
|
|
65
66
|
meeting.webex.internal.newMetrics.callDiagnosticMetrics =
|
|
@@ -89,6 +90,7 @@ describe('plugin-meetings', () => {
|
|
|
89
90
|
assert.calledOnceWithExactly(meeting.cleanupLLMConneciton, {throwOnError: false});
|
|
90
91
|
assert.calledOnce(meeting.breakouts.cleanUp);
|
|
91
92
|
assert.calledOnce(meeting.simultaneousInterpretation.cleanUp);
|
|
93
|
+
assert.calledOnce(meeting.locusInfo.cleanUp);
|
|
92
94
|
assert.calledOnce(webex.internal.device.meetingEnded);
|
|
93
95
|
assert.calledOnceWithExactly(
|
|
94
96
|
meeting.webex.internal.newMetrics.callDiagnosticMetrics.clearEventLimitsForCorrelationId,
|
|
@@ -110,6 +112,7 @@ describe('plugin-meetings', () => {
|
|
|
110
112
|
assert.notCalled(meeting.cleanupLLMConneciton);
|
|
111
113
|
assert.calledOnce(meeting.breakouts.cleanUp);
|
|
112
114
|
assert.calledOnce(meeting.simultaneousInterpretation.cleanUp);
|
|
115
|
+
assert.calledOnce(meeting.locusInfo.cleanUp);
|
|
113
116
|
assert.calledOnce(webex.internal.device.meetingEnded);
|
|
114
117
|
assert.calledOnceWithExactly(
|
|
115
118
|
meeting.webex.internal.newMetrics.callDiagnosticMetrics.clearEventLimitsForCorrelationId,
|
|
@@ -130,6 +133,7 @@ describe('plugin-meetings', () => {
|
|
|
130
133
|
assert.notCalled(meeting.cleanupLLMConneciton);
|
|
131
134
|
assert.calledOnce(meeting.breakouts.cleanUp);
|
|
132
135
|
assert.calledOnce(meeting.simultaneousInterpretation.cleanUp);
|
|
136
|
+
assert.calledOnce(meeting.locusInfo.cleanUp);
|
|
133
137
|
assert.calledOnce(webex.internal.device.meetingEnded);
|
|
134
138
|
assert.calledOnceWithExactly(
|
|
135
139
|
meeting.webex.internal.newMetrics.callDiagnosticMetrics.clearEventLimitsForCorrelationId,
|
|
@@ -272,6 +276,31 @@ describe('plugin-meetings', () => {
|
|
|
272
276
|
assert.notCalled(meeting.locusInfo.handleLocusAPIResponse);
|
|
273
277
|
});
|
|
274
278
|
|
|
279
|
+
it('should call handleLocusAPIResponse when response body is an unwrapped LocusDTO', () => {
|
|
280
|
+
const meeting = {
|
|
281
|
+
locusInfo: {
|
|
282
|
+
handleLocusAPIResponse: sinon.stub(),
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const originalResponse = {
|
|
287
|
+
body: {
|
|
288
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/some-id',
|
|
289
|
+
participants: [],
|
|
290
|
+
self: {},
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const response = MeetingUtil.updateLocusFromApiResponse(meeting, originalResponse);
|
|
295
|
+
|
|
296
|
+
assert.deepEqual(response, originalResponse);
|
|
297
|
+
assert.calledOnceWithExactly(
|
|
298
|
+
meeting.locusInfo.handleLocusAPIResponse,
|
|
299
|
+
meeting,
|
|
300
|
+
originalResponse.body
|
|
301
|
+
);
|
|
302
|
+
});
|
|
303
|
+
|
|
275
304
|
it('should work with an undefined meeting', () => {
|
|
276
305
|
const originalResponse = {
|
|
277
306
|
body: {
|
|
@@ -1146,6 +1175,10 @@ describe('plugin-meetings', () => {
|
|
|
1146
1175
|
{functionName: 'canSelectSpokenLanguages', displayHint: 'DISPLAY_NON_ENGLISH_ASR'},
|
|
1147
1176
|
{functionName: 'waitingForOthersToJoin', displayHint: 'WAITING_FOR_OTHERS'},
|
|
1148
1177
|
{functionName: 'showAutoEndMeetingWarning', displayHint: 'SHOW_AUTO_END_MEETING_WARNING'},
|
|
1178
|
+
{
|
|
1179
|
+
functionName: 'isAnonymizeDisplayNamesEnabled',
|
|
1180
|
+
displayHint: 'ANONYMOUS_DISPLAY_NAMES_ENABLED',
|
|
1181
|
+
},
|
|
1149
1182
|
].forEach(({functionName, displayHint}) => {
|
|
1150
1183
|
describe(functionName, () => {
|
|
1151
1184
|
it('works as expected', () => {
|
|
@@ -14,12 +14,14 @@ import StaticConfig from '@webex/plugin-meetings/src/common/config';
|
|
|
14
14
|
import TriggerProxy from '@webex/plugin-meetings/src/common/events/trigger-proxy';
|
|
15
15
|
import LoggerProxy from '@webex/plugin-meetings/src/common/logs/logger-proxy';
|
|
16
16
|
import LoggerConfig from '@webex/plugin-meetings/src/common/logs/logger-config';
|
|
17
|
+
import ParameterError from '@webex/plugin-meetings/src/common/errors/parameter';
|
|
17
18
|
import Meeting, {CallStateForMetrics} from '@webex/plugin-meetings/src/meeting';
|
|
18
19
|
import {Services} from '@webex/webex-core';
|
|
19
20
|
import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
|
|
20
21
|
import Meetings from '@webex/plugin-meetings/src/meetings';
|
|
21
22
|
import MeetingCollection from '@webex/plugin-meetings/src/meetings/collection';
|
|
22
23
|
import MeetingsUtil from '@webex/plugin-meetings/src/meetings/util';
|
|
24
|
+
import {SitePreferenceSelectOption} from '@webex/plugin-meetings/src/meetings/meetings.types';
|
|
23
25
|
import PersonalMeetingRoom from '@webex/plugin-meetings/src/personal-meeting-room';
|
|
24
26
|
import Reachability from '@webex/plugin-meetings/src/reachability';
|
|
25
27
|
import Metrics from '@webex/plugin-meetings/src/metrics';
|
|
@@ -91,6 +93,7 @@ describe('plugin-meetings', () => {
|
|
|
91
93
|
locusInfo = {
|
|
92
94
|
parse: sinon.stub().returns(true),
|
|
93
95
|
updateMainSessionLocusCache: sinon.stub(),
|
|
96
|
+
syncAllHashTreeDatasets: sinon.stub(),
|
|
94
97
|
};
|
|
95
98
|
webex = new MockWebex({
|
|
96
99
|
children: {
|
|
@@ -1355,6 +1358,87 @@ describe('plugin-meetings', () => {
|
|
|
1355
1358
|
);
|
|
1356
1359
|
});
|
|
1357
1360
|
});
|
|
1361
|
+
describe('#fetchSitePreferencesMeViaSite', () => {
|
|
1362
|
+
const sitePreferencesResponse = {
|
|
1363
|
+
scheduling: {
|
|
1364
|
+
supportScheduleWebinar: true,
|
|
1365
|
+
webinarWebLink: 'https://go.webex.com/webappng/sites/go/webinar/scheduler',
|
|
1366
|
+
},
|
|
1367
|
+
};
|
|
1368
|
+
|
|
1369
|
+
beforeEach(() => {
|
|
1370
|
+
webex.meetings.request.fetchSitePreferencesMeViaSite = sinon
|
|
1371
|
+
.stub()
|
|
1372
|
+
.resolves(sitePreferencesResponse);
|
|
1373
|
+
});
|
|
1374
|
+
|
|
1375
|
+
it('should have #fetchSitePreferencesMeViaSite', () => {
|
|
1376
|
+
assert.exists(webex.meetings.fetchSitePreferencesMeViaSite);
|
|
1377
|
+
});
|
|
1378
|
+
|
|
1379
|
+
it('fetches scheduling preferences for the preferred Webex site by default', async () => {
|
|
1380
|
+
webex.meetings.preferredWebexSite = 'go.webex.com';
|
|
1381
|
+
|
|
1382
|
+
const result = await webex.meetings.fetchSitePreferencesMeViaSite();
|
|
1383
|
+
|
|
1384
|
+
assert.deepEqual(result, sitePreferencesResponse);
|
|
1385
|
+
assert.calledOnceWithExactly(
|
|
1386
|
+
webex.meetings.request.fetchSitePreferencesMeViaSite,
|
|
1387
|
+
{
|
|
1388
|
+
siteUrl: 'go.webex.com',
|
|
1389
|
+
}
|
|
1390
|
+
);
|
|
1391
|
+
});
|
|
1392
|
+
|
|
1393
|
+
it('uses the provided Webex site instead of the preferred Webex site', async () => {
|
|
1394
|
+
webex.meetings.preferredWebexSite = 'preferred.webex.com';
|
|
1395
|
+
|
|
1396
|
+
await webex.meetings.fetchSitePreferencesMeViaSite({siteUrl: 'go.webex.com'});
|
|
1397
|
+
|
|
1398
|
+
assert.calledOnceWithExactly(
|
|
1399
|
+
webex.meetings.request.fetchSitePreferencesMeViaSite,
|
|
1400
|
+
{
|
|
1401
|
+
siteUrl: 'go.webex.com',
|
|
1402
|
+
}
|
|
1403
|
+
);
|
|
1404
|
+
});
|
|
1405
|
+
|
|
1406
|
+
it('forwards custom site name and preference sections to the request helper', async () => {
|
|
1407
|
+
webex.meetings.preferredWebexSite = 'go.webex.com';
|
|
1408
|
+
|
|
1409
|
+
await webex.meetings.fetchSitePreferencesMeViaSite({
|
|
1410
|
+
siteName: 'custom-site',
|
|
1411
|
+
selectOptions: [SitePreferenceSelectOption.SCHEDULING],
|
|
1412
|
+
});
|
|
1413
|
+
|
|
1414
|
+
assert.calledOnceWithExactly(
|
|
1415
|
+
webex.meetings.request.fetchSitePreferencesMeViaSite,
|
|
1416
|
+
{
|
|
1417
|
+
siteUrl: 'go.webex.com',
|
|
1418
|
+
siteName: 'custom-site',
|
|
1419
|
+
selectOptions: [SitePreferenceSelectOption.SCHEDULING],
|
|
1420
|
+
}
|
|
1421
|
+
);
|
|
1422
|
+
});
|
|
1423
|
+
|
|
1424
|
+
it('throws when no Webex site is available', () => {
|
|
1425
|
+
webex.meetings.preferredWebexSite = '';
|
|
1426
|
+
webex.meetings.request.fetchSitePreferencesMeViaSite.throws(
|
|
1427
|
+
new ParameterError(
|
|
1428
|
+
'No siteUrl available. Call register() before fetching site preferences or provide options.siteUrl.'
|
|
1429
|
+
)
|
|
1430
|
+
);
|
|
1431
|
+
|
|
1432
|
+
assert.throws(
|
|
1433
|
+
() => webex.meetings.fetchSitePreferencesMeViaSite(),
|
|
1434
|
+
ParameterError,
|
|
1435
|
+
'No siteUrl available. Call register() before fetching site preferences or provide options.siteUrl.'
|
|
1436
|
+
);
|
|
1437
|
+
assert.calledOnceWithExactly(webex.meetings.request.fetchSitePreferencesMeViaSite, {
|
|
1438
|
+
siteUrl: '',
|
|
1439
|
+
});
|
|
1440
|
+
});
|
|
1441
|
+
});
|
|
1358
1442
|
describe('Static shortcut proxy methods', () => {
|
|
1359
1443
|
describe('MeetingCollection getByKey proxies', () => {
|
|
1360
1444
|
beforeEach(() => {
|
|
@@ -1391,7 +1475,7 @@ describe('plugin-meetings', () => {
|
|
|
1391
1475
|
it('should have #syncMeetings', () => {
|
|
1392
1476
|
assert.exists(webex.meetings.syncMeetings);
|
|
1393
1477
|
});
|
|
1394
|
-
it('should
|
|
1478
|
+
it('should skip getActiveMeetings but still call syncAllHashTreeDatasets if unverified guest', async () => {
|
|
1395
1479
|
webex.meetings.request.getActiveMeetings = sinon.stub().returns(
|
|
1396
1480
|
Promise.resolve({
|
|
1397
1481
|
loci: [
|
|
@@ -1404,13 +1488,23 @@ describe('plugin-meetings', () => {
|
|
|
1404
1488
|
webex.credentials.isUnverifiedGuest = true;
|
|
1405
1489
|
LoggerProxy.logger.info = sinon.stub();
|
|
1406
1490
|
|
|
1491
|
+
const mockLocusInfo = {
|
|
1492
|
+
syncAllHashTreeDatasets: sinon.stub().resolves(),
|
|
1493
|
+
};
|
|
1494
|
+
webex.meetings.meetingCollection.getAll = sinon.stub().returns({
|
|
1495
|
+
meeting1: {locusInfo: mockLocusInfo},
|
|
1496
|
+
meeting2: {locusInfo: undefined},
|
|
1497
|
+
meeting3: {},
|
|
1498
|
+
});
|
|
1499
|
+
|
|
1407
1500
|
await webex.meetings.syncMeetings();
|
|
1408
1501
|
|
|
1409
1502
|
assert.notCalled(webex.meetings.request.getActiveMeetings);
|
|
1410
1503
|
assert.calledWith(
|
|
1411
1504
|
LoggerProxy.logger.info,
|
|
1412
|
-
'Meetings:index#syncMeetings --> skipping
|
|
1505
|
+
'Meetings:index#syncMeetings --> user is unverified guest, skipping calling Locus for meeting sync'
|
|
1413
1506
|
);
|
|
1507
|
+
assert.calledOnce(mockLocusInfo.syncAllHashTreeDatasets);
|
|
1414
1508
|
});
|
|
1415
1509
|
describe('succesful requests', () => {
|
|
1416
1510
|
beforeEach(() => {
|
|
@@ -1429,6 +1523,9 @@ describe('plugin-meetings', () => {
|
|
|
1429
1523
|
webex.meetings.meetingCollection.getByKey = sinon.stub().returns({
|
|
1430
1524
|
locusInfo,
|
|
1431
1525
|
});
|
|
1526
|
+
webex.meetings.meetingCollection.getAll = sinon.stub().returns({
|
|
1527
|
+
meeting1: {locusInfo, locusUrl: url1},
|
|
1528
|
+
});
|
|
1432
1529
|
});
|
|
1433
1530
|
it('tests the sync meeting calls for existing meeting', async () => {
|
|
1434
1531
|
await webex.meetings.syncMeetings();
|
|
@@ -1436,6 +1533,7 @@ describe('plugin-meetings', () => {
|
|
|
1436
1533
|
assert.calledOnce(webex.meetings.meetingCollection.getByKey);
|
|
1437
1534
|
assert.calledOnce(locusInfo.parse);
|
|
1438
1535
|
assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
|
|
1536
|
+
assert.calledOnce(locusInfo.syncAllHashTreeDatasets);
|
|
1439
1537
|
});
|
|
1440
1538
|
});
|
|
1441
1539
|
describe('when meeting is not returned', () => {
|
|
@@ -1474,7 +1572,7 @@ describe('plugin-meetings', () => {
|
|
|
1474
1572
|
url: url1,
|
|
1475
1573
|
},
|
|
1476
1574
|
hashTreeMessage: undefined,
|
|
1477
|
-
});
|
|
1575
|
+
}, sinon.match.func);
|
|
1478
1576
|
});
|
|
1479
1577
|
});
|
|
1480
1578
|
describe('when destroying meeting is needed', () => {
|
|
@@ -1520,7 +1618,7 @@ describe('plugin-meetings', () => {
|
|
|
1520
1618
|
it('destroy any meeting that has no active locus url if keepOnlyLocusMeetings is not defined', async () => {
|
|
1521
1619
|
await webex.meetings.syncMeetings();
|
|
1522
1620
|
assert.calledOnce(webex.meetings.request.getActiveMeetings);
|
|
1523
|
-
assert.
|
|
1621
|
+
assert.calledTwice(webex.meetings.meetingCollection.getAll);
|
|
1524
1622
|
assert.calledWith(destroySpy, meetingCollectionMeetings.noLongerValidLocusMeeting);
|
|
1525
1623
|
assert.calledWith(destroySpy, meetingCollectionMeetings.otherNonLocusMeeting1);
|
|
1526
1624
|
assert.calledWith(destroySpy, meetingCollectionMeetings.otherNonLocusMeeting2);
|
|
@@ -1532,7 +1630,7 @@ describe('plugin-meetings', () => {
|
|
|
1532
1630
|
it('destroy any meeting that has no active locus url if keepOnlyLocusMeetings === true', async () => {
|
|
1533
1631
|
await webex.meetings.syncMeetings({keepOnlyLocusMeetings: true});
|
|
1534
1632
|
assert.calledOnce(webex.meetings.request.getActiveMeetings);
|
|
1535
|
-
assert.
|
|
1633
|
+
assert.calledTwice(webex.meetings.meetingCollection.getAll);
|
|
1536
1634
|
assert.calledWith(destroySpy, meetingCollectionMeetings.noLongerValidLocusMeeting);
|
|
1537
1635
|
assert.calledWith(destroySpy, meetingCollectionMeetings.otherNonLocusMeeting1);
|
|
1538
1636
|
assert.calledWith(destroySpy, meetingCollectionMeetings.otherNonLocusMeeting2);
|
|
@@ -1544,7 +1642,7 @@ describe('plugin-meetings', () => {
|
|
|
1544
1642
|
it('destroy any LOCUS meetings that have no active locus url if keepOnlyLocusMeetings === false', async () => {
|
|
1545
1643
|
await webex.meetings.syncMeetings({keepOnlyLocusMeetings: false});
|
|
1546
1644
|
assert.calledOnce(webex.meetings.request.getActiveMeetings);
|
|
1547
|
-
assert.
|
|
1645
|
+
assert.calledTwice(webex.meetings.meetingCollection.getAll);
|
|
1548
1646
|
assert.calledWith(destroySpy, meetingCollectionMeetings.noLongerValidLocusMeeting);
|
|
1549
1647
|
assert.callCount(destroySpy, 1);
|
|
1550
1648
|
|
|
@@ -1552,6 +1650,147 @@ describe('plugin-meetings', () => {
|
|
|
1552
1650
|
});
|
|
1553
1651
|
});
|
|
1554
1652
|
});
|
|
1653
|
+
|
|
1654
|
+
describe('when globalMeetingId preserves breakout meetings', () => {
|
|
1655
|
+
let destroySpy;
|
|
1656
|
+
let cleanUpSpy;
|
|
1657
|
+
|
|
1658
|
+
beforeEach(() => {
|
|
1659
|
+
destroySpy = sinon.spy(webex.meetings, 'destroy');
|
|
1660
|
+
cleanUpSpy = sinon.stub(MeetingUtil, 'cleanUp').returns(Promise.resolve());
|
|
1661
|
+
});
|
|
1662
|
+
|
|
1663
|
+
afterEach(() => {
|
|
1664
|
+
cleanUpSpy.restore();
|
|
1665
|
+
});
|
|
1666
|
+
|
|
1667
|
+
it('should not destroy a meeting whose globalMeetingId matches an active locus', async () => {
|
|
1668
|
+
const meetingCollectionMeetings = {
|
|
1669
|
+
breakoutMeeting: {
|
|
1670
|
+
locusUrl: 'breakout-url',
|
|
1671
|
+
locusInfo: {
|
|
1672
|
+
info: {globalMeetingId: 'gmid-123'},
|
|
1673
|
+
syncAllHashTreeDatasets: sinon.stub().resolves(),
|
|
1674
|
+
},
|
|
1675
|
+
sendCallAnalyzerMetrics: sinon.stub(),
|
|
1676
|
+
},
|
|
1677
|
+
};
|
|
1678
|
+
|
|
1679
|
+
webex.meetings.meetingCollection.getAll = sinon
|
|
1680
|
+
.stub()
|
|
1681
|
+
.returns(meetingCollectionMeetings);
|
|
1682
|
+
webex.meetings.request.getActiveMeetings = sinon.stub().resolves({
|
|
1683
|
+
loci: [{url: 'main-url', info: {globalMeetingId: 'gmid-123'}}],
|
|
1684
|
+
});
|
|
1685
|
+
|
|
1686
|
+
await webex.meetings.syncMeetings();
|
|
1687
|
+
|
|
1688
|
+
assert.notCalled(destroySpy);
|
|
1689
|
+
});
|
|
1690
|
+
|
|
1691
|
+
it('should destroy a meeting whose globalMeetingId does NOT match any active locus', async () => {
|
|
1692
|
+
const meetingCollectionMeetings = {
|
|
1693
|
+
breakoutMeeting: {
|
|
1694
|
+
locusUrl: 'breakout-url',
|
|
1695
|
+
locusInfo: {
|
|
1696
|
+
info: {globalMeetingId: 'gmid-other'},
|
|
1697
|
+
syncAllHashTreeDatasets: sinon.stub().resolves(),
|
|
1698
|
+
},
|
|
1699
|
+
sendCallAnalyzerMetrics: sinon.stub(),
|
|
1700
|
+
},
|
|
1701
|
+
};
|
|
1702
|
+
|
|
1703
|
+
webex.meetings.meetingCollection.getAll = sinon
|
|
1704
|
+
.stub()
|
|
1705
|
+
.returns(meetingCollectionMeetings);
|
|
1706
|
+
webex.meetings.request.getActiveMeetings = sinon.stub().resolves({
|
|
1707
|
+
loci: [{url: 'main-url', info: {globalMeetingId: 'gmid-123'}}],
|
|
1708
|
+
});
|
|
1709
|
+
|
|
1710
|
+
await webex.meetings.syncMeetings();
|
|
1711
|
+
|
|
1712
|
+
assert.calledOnce(destroySpy);
|
|
1713
|
+
assert.calledWith(destroySpy, meetingCollectionMeetings.breakoutMeeting);
|
|
1714
|
+
});
|
|
1715
|
+
});
|
|
1716
|
+
|
|
1717
|
+
describe('skipHashTreeSync parameter', () => {
|
|
1718
|
+
it('should skip syncAllHashTreeDatasets when skipHashTreeSync is true', async () => {
|
|
1719
|
+
const mockLocusInfo = {
|
|
1720
|
+
syncAllHashTreeDatasets: sinon.stub().resolves(),
|
|
1721
|
+
};
|
|
1722
|
+
|
|
1723
|
+
webex.meetings.request.getActiveMeetings = sinon.stub().resolves({loci: []});
|
|
1724
|
+
webex.meetings.meetingCollection.getAll = sinon.stub().returns({
|
|
1725
|
+
meeting1: {locusInfo: mockLocusInfo},
|
|
1726
|
+
});
|
|
1727
|
+
|
|
1728
|
+
await webex.meetings.syncMeetings({keepOnlyLocusMeetings: false, skipHashTreeSync: true});
|
|
1729
|
+
|
|
1730
|
+
assert.calledOnce(webex.meetings.request.getActiveMeetings);
|
|
1731
|
+
assert.notCalled(mockLocusInfo.syncAllHashTreeDatasets);
|
|
1732
|
+
});
|
|
1733
|
+
|
|
1734
|
+
it('should call syncAllHashTreeDatasets when skipHashTreeSync is false (default)', async () => {
|
|
1735
|
+
const mockLocusInfo = {
|
|
1736
|
+
syncAllHashTreeDatasets: sinon.stub().resolves(),
|
|
1737
|
+
};
|
|
1738
|
+
|
|
1739
|
+
webex.meetings.request.getActiveMeetings = sinon.stub().resolves({loci: []});
|
|
1740
|
+
webex.meetings.meetingCollection.getAll = sinon.stub().returns({
|
|
1741
|
+
meeting1: {locusInfo: mockLocusInfo},
|
|
1742
|
+
});
|
|
1743
|
+
|
|
1744
|
+
await webex.meetings.syncMeetings({keepOnlyLocusMeetings: false, skipHashTreeSync: false});
|
|
1745
|
+
|
|
1746
|
+
assert.calledOnce(webex.meetings.request.getActiveMeetings);
|
|
1747
|
+
assert.calledOnce(mockLocusInfo.syncAllHashTreeDatasets);
|
|
1748
|
+
});
|
|
1749
|
+
});
|
|
1750
|
+
|
|
1751
|
+
describe('syncAllHashTreeDatasets in syncMeetings', () => {
|
|
1752
|
+
it('should call syncAllHashTreeDatasets for multiple meetings, skipping those without locusInfo', async () => {
|
|
1753
|
+
const mockLocusInfo1 = {
|
|
1754
|
+
syncAllHashTreeDatasets: sinon.stub().resolves(),
|
|
1755
|
+
};
|
|
1756
|
+
const mockLocusInfo2 = {
|
|
1757
|
+
syncAllHashTreeDatasets: sinon.stub().resolves(),
|
|
1758
|
+
};
|
|
1759
|
+
|
|
1760
|
+
webex.meetings.request.getActiveMeetings = sinon.stub().resolves({loci: []});
|
|
1761
|
+
webex.meetings.meetingCollection.getAll = sinon.stub().returns({
|
|
1762
|
+
meeting1: {locusInfo: mockLocusInfo1},
|
|
1763
|
+
meeting2: {locusInfo: undefined},
|
|
1764
|
+
meeting3: {locusInfo: mockLocusInfo2},
|
|
1765
|
+
meeting4: {},
|
|
1766
|
+
});
|
|
1767
|
+
|
|
1768
|
+
await webex.meetings.syncMeetings({keepOnlyLocusMeetings: false});
|
|
1769
|
+
|
|
1770
|
+
assert.calledOnce(mockLocusInfo1.syncAllHashTreeDatasets);
|
|
1771
|
+
assert.calledOnce(mockLocusInfo2.syncAllHashTreeDatasets);
|
|
1772
|
+
});
|
|
1773
|
+
|
|
1774
|
+
it('should not call syncAllHashTreeDatasets when getActiveMeetings throws an error', async () => {
|
|
1775
|
+
const mockLocusInfo = {
|
|
1776
|
+
syncAllHashTreeDatasets: sinon.stub().resolves(),
|
|
1777
|
+
};
|
|
1778
|
+
|
|
1779
|
+
webex.meetings.request.getActiveMeetings = sinon.stub().rejects(new Error('network error'));
|
|
1780
|
+
webex.meetings.meetingCollection.getAll = sinon.stub().returns({
|
|
1781
|
+
meeting1: {locusInfo: mockLocusInfo},
|
|
1782
|
+
});
|
|
1783
|
+
|
|
1784
|
+
try {
|
|
1785
|
+
await webex.meetings.syncMeetings();
|
|
1786
|
+
assert.fail('should have thrown');
|
|
1787
|
+
} catch (err) {
|
|
1788
|
+
assert.equal(err.message, 'network error');
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
assert.notCalled(mockLocusInfo.syncAllHashTreeDatasets);
|
|
1792
|
+
});
|
|
1793
|
+
});
|
|
1555
1794
|
});
|
|
1556
1795
|
describe('#fetchStaticMeetingLink', () => {
|
|
1557
1796
|
const conversationUrl = 'conv.fakeconversationurl.com';
|
|
@@ -2015,7 +2254,7 @@ describe('plugin-meetings', () => {
|
|
|
2015
2254
|
},
|
|
2016
2255
|
},
|
|
2017
2256
|
hashTreeMessage: undefined,
|
|
2018
|
-
});
|
|
2257
|
+
}, sinon.match.func);
|
|
2019
2258
|
});
|
|
2020
2259
|
it('should setup the meeting from a hash tree event', async () => {
|
|
2021
2260
|
const selfData = {};
|
|
@@ -2049,7 +2288,7 @@ describe('plugin-meetings', () => {
|
|
|
2049
2288
|
info: infoData,
|
|
2050
2289
|
},
|
|
2051
2290
|
hashTreeMessage,
|
|
2052
|
-
});
|
|
2291
|
+
}, sinon.match.func);
|
|
2053
2292
|
});
|
|
2054
2293
|
|
|
2055
2294
|
it('should ignore hash tree event when created locus has INACTIVE fullState', async () => {
|
|
@@ -2129,7 +2368,7 @@ describe('plugin-meetings', () => {
|
|
|
2129
2368
|
},
|
|
2130
2369
|
},
|
|
2131
2370
|
hashTreeMessage: undefined,
|
|
2132
|
-
});
|
|
2371
|
+
}, sinon.match.func);
|
|
2133
2372
|
});
|
|
2134
2373
|
|
|
2135
2374
|
it('sends client event correctly on finally', async () => {
|
|
@@ -2205,7 +2444,7 @@ describe('plugin-meetings', () => {
|
|
|
2205
2444
|
},
|
|
2206
2445
|
},
|
|
2207
2446
|
hashTreeMessage: undefined,
|
|
2208
|
-
});
|
|
2447
|
+
}, sinon.match.func);
|
|
2209
2448
|
});
|
|
2210
2449
|
|
|
2211
2450
|
const generateFakeLocusData = (isUnifiedSpaceMeeting) => ({
|
|
@@ -2833,6 +3072,39 @@ describe('plugin-meetings', () => {
|
|
|
2833
3072
|
checkCreateMeetingWithNoMeetingInfo(true, true);
|
|
2834
3073
|
});
|
|
2835
3074
|
|
|
3075
|
+
it('does not emit meeting:added when meeting is destroyed due to missing meeting info', async () => {
|
|
3076
|
+
// Make destroy actually remove the meeting from the collection
|
|
3077
|
+
// so that getMeetingByType returns null in the finally block
|
|
3078
|
+
webex.meetings.destroy = sinon.stub().callsFake((meeting) => {
|
|
3079
|
+
webex.meetings.meetingCollection.delete(meeting.id);
|
|
3080
|
+
});
|
|
3081
|
+
|
|
3082
|
+
try {
|
|
3083
|
+
await webex.meetings.createMeeting(
|
|
3084
|
+
'test destination',
|
|
3085
|
+
'test type',
|
|
3086
|
+
undefined,
|
|
3087
|
+
undefined,
|
|
3088
|
+
undefined,
|
|
3089
|
+
true
|
|
3090
|
+
);
|
|
3091
|
+
assert.fail('should have thrown NoMeetingInfoError');
|
|
3092
|
+
} catch (err) {
|
|
3093
|
+
assert.instanceOf(err, NoMeetingInfoError);
|
|
3094
|
+
}
|
|
3095
|
+
|
|
3096
|
+
assert.calledOnce(webex.meetings.destroy);
|
|
3097
|
+
|
|
3098
|
+
// meeting:added should NOT have been triggered since the meeting was destroyed
|
|
3099
|
+
assert.neverCalledWith(
|
|
3100
|
+
TriggerProxy.trigger,
|
|
3101
|
+
sinon.match.any,
|
|
3102
|
+
sinon.match({function: 'createMeeting'}),
|
|
3103
|
+
'meeting:added',
|
|
3104
|
+
sinon.match.any
|
|
3105
|
+
);
|
|
3106
|
+
});
|
|
3107
|
+
|
|
2836
3108
|
it('creates the meeting avoiding meeting info fetch by passing type as DESTINATION_TYPE.ONE_ON_ONE_CALL', async () => {
|
|
2837
3109
|
const meeting = await webex.meetings.createMeeting(
|
|
2838
3110
|
'test destination',
|
|
@@ -3426,6 +3698,21 @@ describe('plugin-meetings', () => {
|
|
|
3426
3698
|
'Meetings:index#isNeedHandleMainLocus --> self device left&moved in main locus with self joined status, not need to handle'
|
|
3427
3699
|
);
|
|
3428
3700
|
});
|
|
3701
|
+
|
|
3702
|
+
it('check breakout ended with self removed, return false', () => {
|
|
3703
|
+
webex.meetings.meetingCollection.getActiveBreakoutLocus = sinon.stub().returns(null);
|
|
3704
|
+
newLocus.self.state = 'LEFT';
|
|
3705
|
+
newLocus.self.reason = 'OTHER';
|
|
3706
|
+
newLocus.self.removed = true;
|
|
3707
|
+
newLocus.fullState = {state: 'INACTIVE', endMeetingReason: 'BREAKOUT_ENDED'};
|
|
3708
|
+
LoggerProxy.logger.log = sinon.stub();
|
|
3709
|
+
const result = webex.meetings.isNeedHandleMainLocus(meeting, newLocus);
|
|
3710
|
+
assert.equal(result, false);
|
|
3711
|
+
assert.calledWith(
|
|
3712
|
+
LoggerProxy.logger.log,
|
|
3713
|
+
'Meetings:index#isNeedHandleMainLocus --> self moved main locus with self removed status or with device resource moved, not need to handle'
|
|
3714
|
+
);
|
|
3715
|
+
});
|
|
3429
3716
|
});
|
|
3430
3717
|
|
|
3431
3718
|
describe('#isNeedHandleLocusDTO', () => {
|
|
@@ -3486,6 +3773,18 @@ describe('plugin-meetings', () => {
|
|
|
3486
3773
|
const result = webex.meetings.isNeedHandleLocusDTO(meeting, newLocus);
|
|
3487
3774
|
assert.equal(result, false);
|
|
3488
3775
|
});
|
|
3776
|
+
it('breakout session with breakout ended, return false', () => {
|
|
3777
|
+
newLocus.controls.breakout = {
|
|
3778
|
+
sessionType: 'BREAKOUT',
|
|
3779
|
+
};
|
|
3780
|
+
newLocus.self.state = 'LEFT';
|
|
3781
|
+
newLocus.self.reason = 'OTHER';
|
|
3782
|
+
newLocus.self.devices = [];
|
|
3783
|
+
newLocus.fullState = {state: 'INACTIVE', endMeetingReason: 'BREAKOUT_ENDED'};
|
|
3784
|
+
LoggerProxy.logger.log = sinon.stub();
|
|
3785
|
+
const result = webex.meetings.isNeedHandleLocusDTO(meeting, newLocus);
|
|
3786
|
+
assert.equal(result, false);
|
|
3787
|
+
});
|
|
3489
3788
|
it('moved to lobby, return true', () => {
|
|
3490
3789
|
newLocus.controls.breakout = {
|
|
3491
3790
|
sessionType: 'MAIN',
|