@webex/plugin-meetings 3.8.1-next.3 → 3.8.1-next.30
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/README.md +26 -13
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +21 -2
- package/dist/constants.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +36 -17
- package/dist/locus-info/index.js.map +1 -1
- package/dist/media/index.js +2 -2
- package/dist/media/index.js.map +1 -1
- package/dist/meeting/brbState.js +14 -12
- package/dist/meeting/brbState.js.map +1 -1
- package/dist/meeting/index.js +169 -66
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/request.js +19 -0
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meetings/index.js +35 -33
- package/dist/meetings/index.js.map +1 -1
- package/dist/members/index.js +8 -6
- package/dist/members/index.js.map +1 -1
- package/dist/members/request.js +3 -3
- package/dist/members/request.js.map +1 -1
- package/dist/members/util.js +18 -6
- package/dist/members/util.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +1 -1
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/remoteMedia.js +34 -5
- package/dist/multistream/remoteMedia.js.map +1 -1
- package/dist/multistream/remoteMediaGroup.js +42 -2
- package/dist/multistream/remoteMediaGroup.js.map +1 -1
- package/dist/multistream/sendSlotManager.js +32 -2
- package/dist/multistream/sendSlotManager.js.map +1 -1
- package/dist/reachability/index.js +5 -10
- package/dist/reachability/index.js.map +1 -1
- package/dist/types/constants.d.ts +19 -0
- package/dist/types/meeting/brbState.d.ts +0 -1
- package/dist/types/meeting/index.d.ts +28 -4
- package/dist/types/meeting/request.d.ts +9 -1
- package/dist/types/meeting/request.type.d.ts +74 -0
- package/dist/types/members/index.d.ts +8 -3
- package/dist/types/members/request.d.ts +1 -1
- package/dist/types/members/util.d.ts +5 -2
- package/dist/types/multistream/remoteMedia.d.ts +20 -1
- package/dist/types/multistream/remoteMediaGroup.d.ts +11 -0
- package/dist/types/multistream/sendSlotManager.d.ts +16 -0
- package/dist/types/reachability/index.d.ts +2 -2
- package/dist/webinar/index.js +1 -1
- package/package.json +24 -24
- package/src/constants.ts +20 -0
- package/src/locus-info/index.ts +47 -20
- package/src/media/index.ts +2 -2
- package/src/meeting/brbState.ts +9 -7
- package/src/meeting/index.ts +126 -23
- package/src/meeting/request.ts +16 -0
- package/src/meeting/request.type.ts +64 -0
- package/src/meetings/index.ts +3 -2
- package/src/members/index.ts +7 -5
- package/src/members/request.ts +2 -2
- package/src/members/util.ts +14 -3
- package/src/multistream/mediaRequestManager.ts +7 -7
- package/src/multistream/remoteMedia.ts +34 -4
- package/src/multistream/remoteMediaGroup.ts +37 -2
- package/src/multistream/sendSlotManager.ts +34 -2
- package/src/reachability/index.ts +5 -13
- package/test/unit/spec/locus-info/index.js +177 -47
- package/test/unit/spec/media/index.ts +107 -0
- package/test/unit/spec/meeting/brbState.ts +9 -9
- package/test/unit/spec/meeting/index.js +606 -55
- package/test/unit/spec/meeting/request.js +71 -0
- package/test/unit/spec/meetings/index.js +1 -0
- package/test/unit/spec/members/index.js +32 -9
- package/test/unit/spec/members/request.js +2 -2
- package/test/unit/spec/members/utils.js +27 -7
- package/test/unit/spec/multistream/mediaRequestManager.ts +19 -6
- package/test/unit/spec/multistream/remoteMedia.ts +66 -2
- package/test/unit/spec/multistream/sendSlotManager.ts +59 -0
- package/test/unit/spec/reachability/index.ts +2 -6
@@ -7,10 +7,20 @@ import {
|
|
7
7
|
StreamState,
|
8
8
|
} from '@webex/internal-media-core';
|
9
9
|
|
10
|
+
/**
|
11
|
+
* This class is used to manage the sendSlots for the given media types.
|
12
|
+
*/
|
10
13
|
export default class SendSlotManager {
|
11
14
|
private readonly slots: Map<MediaType, SendSlot> = new Map();
|
12
15
|
private readonly LoggerProxy: any;
|
16
|
+
private readonly sourceStateOverrides: Map<MediaType, StreamState> = new Map();
|
13
17
|
|
18
|
+
/**
|
19
|
+
* Constructor for SendSlotManager
|
20
|
+
*
|
21
|
+
* @param {any} LoggerProxy is used to log the messages
|
22
|
+
* @constructor
|
23
|
+
*/
|
14
24
|
constructor(LoggerProxy: any) {
|
15
25
|
this.LoggerProxy = LoggerProxy;
|
16
26
|
}
|
@@ -93,7 +103,7 @@ export default class SendSlotManager {
|
|
93
103
|
public setSourceStateOverride(mediaType: MediaType, state: StreamState | null) {
|
94
104
|
if (mediaType !== MediaType.VideoMain) {
|
95
105
|
throw new Error(
|
96
|
-
`
|
106
|
+
`Invalid media type '${mediaType}'. Source state overrides are only applicable to ${MediaType.VideoMain}.`
|
97
107
|
);
|
98
108
|
}
|
99
109
|
|
@@ -103,17 +113,39 @@ export default class SendSlotManager {
|
|
103
113
|
throw new Error(`Slot for ${mediaType} does not exist`);
|
104
114
|
}
|
105
115
|
|
116
|
+
const currentStateOverride = this.getSourceStateOverride(mediaType);
|
117
|
+
if (currentStateOverride === state) {
|
118
|
+
return;
|
119
|
+
}
|
120
|
+
|
106
121
|
if (state) {
|
107
122
|
slot.setSourceStateOverride(state);
|
123
|
+
this.sourceStateOverrides.set(mediaType, state);
|
108
124
|
} else {
|
109
125
|
slot.clearSourceStateOverride();
|
126
|
+
this.sourceStateOverrides.delete(mediaType);
|
110
127
|
}
|
111
128
|
|
112
129
|
this.LoggerProxy.logger.info(
|
113
|
-
`
|
130
|
+
`SendSlotManager->setSourceStateOverride#set source state override for ${mediaType} to ${state}`
|
114
131
|
);
|
115
132
|
}
|
116
133
|
|
134
|
+
/**
|
135
|
+
* Gets the source state override for the given media type.
|
136
|
+
* @param {MediaType} mediaType - The type of media to get the source state override for.
|
137
|
+
* @returns {StreamState | null} - The current source state override or null if not set.
|
138
|
+
*/
|
139
|
+
private getSourceStateOverride(mediaType: MediaType): StreamState | null {
|
140
|
+
if (mediaType !== MediaType.VideoMain) {
|
141
|
+
throw new Error(
|
142
|
+
`Invalid media type '${mediaType}'. Source state overrides are only applicable to ${MediaType.VideoMain}.`
|
143
|
+
);
|
144
|
+
}
|
145
|
+
|
146
|
+
return this.sourceStateOverrides.get(mediaType) || null;
|
147
|
+
}
|
148
|
+
|
117
149
|
/**
|
118
150
|
* This method publishes the given stream to the sendSlot for the given mediaType
|
119
151
|
* @param {MediaType} mediaType MediaType of the sendSlot to which a stream needs to be published (AUDIO_MAIN/VIDEO_MAIN/AUDIO_SLIDES/VIDEO_SLIDES)
|
@@ -140,22 +140,14 @@ export default class Reachability extends EventsScope {
|
|
140
140
|
|
141
141
|
/**
|
142
142
|
* Checks if the given subnet is reachable
|
143
|
-
* @param {string}
|
143
|
+
* @param {string} selectedSubnetFirstOctet - selected subnet first octet, e.g. "10" for "10.X.X.X"
|
144
144
|
* @returns {boolean | null} true if reachable, false if not reachable, null if mediaServerIp is not provided
|
145
145
|
* @public
|
146
146
|
* @memberof Reachability
|
147
147
|
*/
|
148
|
-
public isSubnetReachable(
|
149
|
-
if (!mediaServerIp) {
|
150
|
-
LoggerProxy.logger.error(`Reachability:index#isSubnetReachable --> mediaServerIp is null`);
|
151
|
-
|
152
|
-
return null;
|
153
|
-
}
|
154
|
-
|
155
|
-
const subnetFirstOctet = mediaServerIp.split('.')[0];
|
156
|
-
|
148
|
+
public isSubnetReachable(selectedSubnetFirstOctet: string): boolean | null {
|
157
149
|
LoggerProxy.logger.info(
|
158
|
-
`Reachability:index#isSubnetReachable --> Looking for subnet: ${
|
150
|
+
`Reachability:index#isSubnetReachable --> Looking for subnet: ${selectedSubnetFirstOctet}.X.X.X`
|
159
151
|
);
|
160
152
|
|
161
153
|
const matchingReachedClusters = Object.values(this.clusterReachability).reduce(
|
@@ -167,7 +159,7 @@ export default class Reachability extends EventsScope {
|
|
167
159
|
const subnet = reachedSubnetsArray[i];
|
168
160
|
const reachedSubnetFirstOctet = subnet.split('.')[0];
|
169
161
|
|
170
|
-
if (
|
162
|
+
if (selectedSubnetFirstOctet === reachedSubnetFirstOctet) {
|
171
163
|
acc.add(cluster.name);
|
172
164
|
}
|
173
165
|
|
@@ -186,7 +178,7 @@ export default class Reachability extends EventsScope {
|
|
186
178
|
);
|
187
179
|
|
188
180
|
LoggerProxy.logger.info(
|
189
|
-
`Reachability:index#isSubnetReachable --> Found ${matchingReachedClusters.size} clusters that use the subnet ${
|
181
|
+
`Reachability:index#isSubnetReachable --> Found ${matchingReachedClusters.size} clusters that use the subnet ${selectedSubnetFirstOctet}.X.X.X`
|
190
182
|
);
|
191
183
|
|
192
184
|
return matchingReachedClusters.size > 0;
|
@@ -305,7 +305,7 @@ describe('plugin-meetings', () => {
|
|
305
305
|
{state: newControls.rdcControl}
|
306
306
|
);
|
307
307
|
});
|
308
|
-
|
308
|
+
|
309
309
|
it('should trigger the CONTROLS_POLLING_QA_CHANGED event when necessary', () => {
|
310
310
|
locusInfo.controls = {};
|
311
311
|
locusInfo.emitScoped = sinon.stub();
|
@@ -2108,6 +2108,38 @@ describe('plugin-meetings', () => {
|
|
2108
2108
|
assert.isFunction(locusParser.onDeltaAction);
|
2109
2109
|
});
|
2110
2110
|
|
2111
|
+
it("#updateLocusInfo invokes updateLocusUrl before updateMeetingInfo", () => {
|
2112
|
+
const callOrder = [];
|
2113
|
+
sinon.stub(locusInfo, "updateControls");
|
2114
|
+
sinon.stub(locusInfo, "updateConversationUrl");
|
2115
|
+
sinon.stub(locusInfo, "updateCreated");
|
2116
|
+
sinon.stub(locusInfo, "updateFullState");
|
2117
|
+
sinon.stub(locusInfo, "updateHostInfo");
|
2118
|
+
sinon.stub(locusInfo, "updateMeetingInfo").callsFake(() => {
|
2119
|
+
callOrder.push("updateMeetingInfo");
|
2120
|
+
});
|
2121
|
+
sinon.stub(locusInfo, "updateMediaShares");
|
2122
|
+
sinon.stub(locusInfo, "updateParticipantsUrl");
|
2123
|
+
sinon.stub(locusInfo, "updateReplace");
|
2124
|
+
sinon.stub(locusInfo, "updateSelf");
|
2125
|
+
sinon.stub(locusInfo, "updateLocusUrl").callsFake(() => {
|
2126
|
+
callOrder.push("updateLocusUrl");
|
2127
|
+
});
|
2128
|
+
sinon.stub(locusInfo, "updateAclUrl");
|
2129
|
+
sinon.stub(locusInfo, "updateBasequence");
|
2130
|
+
sinon.stub(locusInfo, "updateSequence");
|
2131
|
+
sinon.stub(locusInfo, "updateMemberShip");
|
2132
|
+
sinon.stub(locusInfo, "updateIdentifiers");
|
2133
|
+
sinon.stub(locusInfo, "updateEmbeddedApps");
|
2134
|
+
sinon.stub(locusInfo, "updateResources");
|
2135
|
+
sinon.stub(locusInfo, "compareAndUpdate");
|
2136
|
+
|
2137
|
+
locusInfo.updateLocusInfo(locus);
|
2138
|
+
|
2139
|
+
// Ensure updateLocusUrl is called before updateMeetingInfo if both are called
|
2140
|
+
assert.deepEqual(callOrder, ['updateLocusUrl', 'updateMeetingInfo']);
|
2141
|
+
});
|
2142
|
+
|
2111
2143
|
it('#updateLocusInfo ignores breakout LEFT message', () => {
|
2112
2144
|
const newLocus = {
|
2113
2145
|
self: {
|
@@ -2159,6 +2191,8 @@ describe('plugin-meetings', () => {
|
|
2159
2191
|
assert.notCalled(locusInfo.compareAndUpdate);
|
2160
2192
|
});
|
2161
2193
|
|
2194
|
+
|
2195
|
+
|
2162
2196
|
it('onFullLocus() updates the working-copy of locus parser', () => {
|
2163
2197
|
const eventType = 'fakeEvent';
|
2164
2198
|
|
@@ -2257,7 +2291,7 @@ describe('plugin-meetings', () => {
|
|
2257
2291
|
|
2258
2292
|
it('applyLocusDeltaData gets delta locus on DESYNC action if we have a syncUrl', () => {
|
2259
2293
|
const {DESYNC} = LocusDeltaParser.loci;
|
2260
|
-
const fakeDeltaLocus = {id: 'fake delta locus'};
|
2294
|
+
const fakeDeltaLocus = {baseSequence: {}, id: 'fake delta locus'};
|
2261
2295
|
const meeting = {
|
2262
2296
|
meetingRequest: {
|
2263
2297
|
getLocusDTO: sandbox.stub().resolves({body: fakeDeltaLocus}),
|
@@ -2392,25 +2426,22 @@ describe('plugin-meetings', () => {
|
|
2392
2426
|
};
|
2393
2427
|
});
|
2394
2428
|
|
2395
|
-
it('applyLocusDeltaData gets full locus on DESYNC action if we do not have a syncUrl and destroys the meeting if that fails', () => {
|
2429
|
+
it('applyLocusDeltaData gets full locus on DESYNC action if we do not have a syncUrl and destroys the meeting if that fails', async () => {
|
2396
2430
|
meeting.meetingRequest.getLocusDTO.rejects(new Error('fake error'));
|
2397
2431
|
|
2398
2432
|
locusInfo.locusParser.workingCopy = {}; // no syncUrl
|
2399
2433
|
|
2400
|
-
|
2401
|
-
// we will wait and stub it's last function to resolve this waiting promise.
|
2402
|
-
return new Promise((resolve) => {
|
2403
|
-
webex.meetings.destroy.callsFake(() => resolve());
|
2404
|
-
locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
|
2405
|
-
}).then(() => {
|
2406
|
-
assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'fullSyncUrl'});
|
2434
|
+
locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
|
2407
2435
|
|
2408
|
-
|
2409
|
-
assert.notCalled(meeting.locusInfo.onFullLocus);
|
2410
|
-
assert.notCalled(locusInfo.locusParser.resume);
|
2436
|
+
await testUtils.flushPromises();
|
2411
2437
|
|
2412
|
-
|
2413
|
-
|
2438
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'fullSyncUrl'});
|
2439
|
+
|
2440
|
+
assert.notCalled(meeting.locusInfo.handleLocusDelta);
|
2441
|
+
assert.notCalled(meeting.locusInfo.onFullLocus);
|
2442
|
+
assert.notCalled(locusInfo.locusParser.resume);
|
2443
|
+
|
2444
|
+
assert.calledOnceWithExactly(webex.meetings.destroy, meeting, 'LOCUS_DTO_SYNC_FAILED');
|
2414
2445
|
});
|
2415
2446
|
|
2416
2447
|
it('applyLocusDeltaData first tries a delta sync on DESYNC action and if that fails, does a full locus sync', () => {
|
@@ -2447,39 +2478,62 @@ describe('plugin-meetings', () => {
|
|
2447
2478
|
});
|
2448
2479
|
});
|
2449
2480
|
|
2450
|
-
it('applyLocusDeltaData
|
2481
|
+
it('applyLocusDeltaData first tries a delta sync on DESYNC action and if that fails with 403, it does not do a full locus sync', async () => {
|
2482
|
+
const fake403Error = new Error('fake error');
|
2483
|
+
fake403Error.statusCode = 403;
|
2484
|
+
|
2485
|
+
meeting.meetingRequest.getLocusDTO.onCall(0).rejects(fake403Error);
|
2486
|
+
|
2487
|
+
locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
|
2488
|
+
|
2489
|
+
await testUtils.flushPromises();
|
2490
|
+
|
2491
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'deltaSyncUrl'});
|
2492
|
+
|
2493
|
+
assert.calledWith(sendBehavioralMetricStub, 'js_sdk_locus_delta_sync_failed', {
|
2494
|
+
correlationId: meeting.correlationId,
|
2495
|
+
url: 'deltaSyncUrl',
|
2496
|
+
reason: 'fake error',
|
2497
|
+
errorName: 'Error',
|
2498
|
+
stack: sinon.match.any,
|
2499
|
+
code: sinon.match.any,
|
2500
|
+
});
|
2501
|
+
|
2502
|
+
assert.notCalled(meeting.locusInfo.handleLocusDelta);
|
2503
|
+
assert.notCalled(meeting.locusInfo.onFullLocus);
|
2504
|
+
assert.notCalled(locusInfo.locusParser.resume);
|
2505
|
+
});
|
2506
|
+
|
2507
|
+
it('applyLocusDeltaData destroys the meeting if both delta sync and full sync fail', async () => {
|
2451
2508
|
meeting.meetingRequest.getLocusDTO.rejects(new Error('fake error'));
|
2452
2509
|
|
2453
|
-
|
2454
|
-
// we will wait and stub it's last function to resolve this waiting promise.
|
2455
|
-
return new Promise((resolve) => {
|
2456
|
-
webex.meetings.destroy.callsFake(() => resolve());
|
2457
|
-
locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
|
2458
|
-
}).then(() => {
|
2459
|
-
assert.calledTwice(meeting.meetingRequest.getLocusDTO);
|
2510
|
+
locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
|
2460
2511
|
|
2461
|
-
|
2462
|
-
{url: 'deltaSyncUrl'},
|
2463
|
-
]);
|
2464
|
-
assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[1].args, [
|
2465
|
-
{url: 'fullSyncUrl'},
|
2466
|
-
]);
|
2512
|
+
await testUtils.flushPromises();
|
2467
2513
|
|
2468
|
-
|
2469
|
-
correlationId: meeting.correlationId,
|
2470
|
-
url: 'deltaSyncUrl',
|
2471
|
-
reason: 'fake error',
|
2472
|
-
errorName: 'Error',
|
2473
|
-
stack: sinon.match.any,
|
2474
|
-
code: sinon.match.any,
|
2475
|
-
});
|
2514
|
+
assert.calledTwice(meeting.meetingRequest.getLocusDTO);
|
2476
2515
|
|
2477
|
-
|
2478
|
-
|
2479
|
-
|
2516
|
+
assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[0].args, [
|
2517
|
+
{url: 'deltaSyncUrl'},
|
2518
|
+
]);
|
2519
|
+
assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[1].args, [
|
2520
|
+
{url: 'fullSyncUrl'},
|
2521
|
+
]);
|
2480
2522
|
|
2481
|
-
|
2523
|
+
assert.calledWith(sendBehavioralMetricStub, 'js_sdk_locus_delta_sync_failed', {
|
2524
|
+
correlationId: meeting.correlationId,
|
2525
|
+
url: 'deltaSyncUrl',
|
2526
|
+
reason: 'fake error',
|
2527
|
+
errorName: 'Error',
|
2528
|
+
stack: sinon.match.any,
|
2529
|
+
code: sinon.match.any,
|
2482
2530
|
});
|
2531
|
+
|
2532
|
+
assert.notCalled(meeting.locusInfo.handleLocusDelta);
|
2533
|
+
assert.notCalled(meeting.locusInfo.onFullLocus);
|
2534
|
+
assert.notCalled(locusInfo.locusParser.resume);
|
2535
|
+
|
2536
|
+
assert.calledOnceWithExactly(webex.meetings.destroy, meeting, 'LOCUS_DTO_SYNC_FAILED');
|
2483
2537
|
});
|
2484
2538
|
});
|
2485
2539
|
|
@@ -2509,9 +2563,7 @@ describe('plugin-meetings', () => {
|
|
2509
2563
|
});
|
2510
2564
|
|
2511
2565
|
it('onDeltaLocus merges delta participants with existing participants', () => {
|
2512
|
-
const FAKE_DELTA_PARTICIPANTS = [
|
2513
|
-
{id: '1111'}, {id: '2222'}
|
2514
|
-
]
|
2566
|
+
const FAKE_DELTA_PARTICIPANTS = [{id: '1111'}, {id: '2222'}];
|
2515
2567
|
fakeLocus.participants = FAKE_DELTA_PARTICIPANTS;
|
2516
2568
|
|
2517
2569
|
sinon.spy(locusInfo, 'mergeParticipants');
|
@@ -2519,9 +2571,87 @@ describe('plugin-meetings', () => {
|
|
2519
2571
|
const existingParticipants = locusInfo.participants;
|
2520
2572
|
|
2521
2573
|
locusInfo.onDeltaLocus(fakeLocus);
|
2522
|
-
assert.calledOnceWithExactly(
|
2574
|
+
assert.calledOnceWithExactly(
|
2575
|
+
locusInfo.mergeParticipants,
|
2576
|
+
existingParticipants,
|
2577
|
+
FAKE_DELTA_PARTICIPANTS
|
2578
|
+
);
|
2523
2579
|
assert.calledWith(locusInfo.updateParticipants, FAKE_DELTA_PARTICIPANTS, false);
|
2524
2580
|
});
|
2581
|
+
|
2582
|
+
[true, false].forEach((isDelta) =>
|
2583
|
+
it(`applyLocusDeltaData - handles empty ${
|
2584
|
+
isDelta ? 'delta' : 'full'
|
2585
|
+
} DTO in response`, async () => {
|
2586
|
+
const {DESYNC} = LocusDeltaParser.loci;
|
2587
|
+
const fakeFullLocusDto = {};
|
2588
|
+
const meeting = {
|
2589
|
+
meetingRequest: {
|
2590
|
+
getLocusDTO: sandbox.stub().resolves({body: fakeFullLocusDto}),
|
2591
|
+
},
|
2592
|
+
locusInfo: {
|
2593
|
+
onFullLocus: sandbox.stub(),
|
2594
|
+
handleLocusDelta: sandbox.stub(),
|
2595
|
+
},
|
2596
|
+
locusUrl: 'fake locus FULL url',
|
2597
|
+
};
|
2598
|
+
|
2599
|
+
sinon.stub(locusInfo.locusParser, 'resume').resolves();
|
2600
|
+
|
2601
|
+
if (isDelta) {
|
2602
|
+
locusInfo.locusParser.workingCopy = {syncUrl: 'fake locus DELTA url'};
|
2603
|
+
} else {
|
2604
|
+
locusInfo.locusParser.workingCopy = {}; // no syncUrl (to trigger FULL DTO request)
|
2605
|
+
}
|
2606
|
+
|
2607
|
+
await locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
|
2608
|
+
|
2609
|
+
await testUtils.flushPromises();
|
2610
|
+
|
2611
|
+
if (isDelta) {
|
2612
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {
|
2613
|
+
url: 'fake locus DELTA url',
|
2614
|
+
});
|
2615
|
+
} else {
|
2616
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {
|
2617
|
+
url: 'fake locus FULL url',
|
2618
|
+
});
|
2619
|
+
}
|
2620
|
+
assert.notCalled(meeting.locusInfo.handleLocusDelta);
|
2621
|
+
assert.notCalled(meeting.locusInfo.onFullLocus);
|
2622
|
+
assert.calledOnce(locusInfo.locusParser.resume);
|
2623
|
+
})
|
2624
|
+
);
|
2625
|
+
|
2626
|
+
it(`applyLocusDeltaData - handles the case when we get FULL DTO when we asked for DELTA DTO`, async () => {
|
2627
|
+
const {DESYNC} = LocusDeltaParser.loci;
|
2628
|
+
const fakeFullLocusDto = {someStuff: 'data'}; // non-empty DTO, without baseSequence
|
2629
|
+
const meeting = {
|
2630
|
+
meetingRequest: {
|
2631
|
+
getLocusDTO: sandbox.stub().resolves({body: fakeFullLocusDto}),
|
2632
|
+
},
|
2633
|
+
locusInfo: {
|
2634
|
+
onFullLocus: sandbox.stub(),
|
2635
|
+
handleLocusDelta: sandbox.stub(),
|
2636
|
+
},
|
2637
|
+
locusUrl: 'fake locus FULL url',
|
2638
|
+
};
|
2639
|
+
|
2640
|
+
sinon.stub(locusInfo.locusParser, 'resume').resolves();
|
2641
|
+
|
2642
|
+
locusInfo.locusParser.workingCopy = {syncUrl: 'fake locus DELTA url'};
|
2643
|
+
|
2644
|
+
await locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
|
2645
|
+
|
2646
|
+
await testUtils.flushPromises();
|
2647
|
+
|
2648
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {
|
2649
|
+
url: 'fake locus DELTA url',
|
2650
|
+
});
|
2651
|
+
assert.notCalled(meeting.locusInfo.handleLocusDelta);
|
2652
|
+
assert.calledOnceWithExactly(meeting.locusInfo.onFullLocus, fakeFullLocusDto);
|
2653
|
+
assert.calledOnce(locusInfo.locusParser.resume);
|
2654
|
+
});
|
2525
2655
|
});
|
2526
2656
|
|
2527
2657
|
describe('#updateLocusCache', () => {
|
@@ -2936,8 +3066,8 @@ describe('plugin-meetings', () => {
|
|
2936
3066
|
|
2937
3067
|
sinon.stub(locusInfo, 'updateParticipantDeltas');
|
2938
3068
|
sinon.stub(locusInfo, 'updateParticipants');
|
2939
|
-
sinon.stub(locusInfo, 'isMeetingActive')
|
2940
|
-
sinon.stub(locusInfo, 'handleOneOnOneEvent')
|
3069
|
+
sinon.stub(locusInfo, 'isMeetingActive');
|
3070
|
+
sinon.stub(locusInfo, 'handleOneOnOneEvent');
|
2941
3071
|
(updateLocusInfoStub = sinon.stub(locusInfo, 'updateLocusInfo'));
|
2942
3072
|
syncRequestStub = sinon.stub().resolves({body: {}});
|
2943
3073
|
|
@@ -137,6 +137,113 @@ describe('createMediaConnection', () => {
|
|
137
137
|
);
|
138
138
|
});
|
139
139
|
|
140
|
+
it('should set direction to sendonly for both audio and video when only send flags are true', () => {
|
141
|
+
const roapMediaConnectionConstructorStub = sinon
|
142
|
+
.stub(InternalMediaCoreModule, 'RoapMediaConnection')
|
143
|
+
.returns(fakeRoapMediaConnection);
|
144
|
+
|
145
|
+
StaticConfig.set({bandwidth: {audio: 123, video: 456, startBitrate: 999}});
|
146
|
+
|
147
|
+
const ENABLE_EXTMAP = false;
|
148
|
+
const ENABLE_RTX = true;
|
149
|
+
|
150
|
+
Media.createMediaConnection(false, 'sendonly-debug-id', 'meetingId', {
|
151
|
+
mediaProperties: {
|
152
|
+
mediaDirection: {
|
153
|
+
sendAudio: true,
|
154
|
+
receiveAudio: false,
|
155
|
+
sendVideo: true,
|
156
|
+
receiveVideo: false,
|
157
|
+
sendShare: false,
|
158
|
+
receiveShare: false,
|
159
|
+
},
|
160
|
+
audioStream: fakeAudioStream,
|
161
|
+
videoStream: fakeVideoStream,
|
162
|
+
shareVideoTrack: null,
|
163
|
+
shareAudioTrack: null,
|
164
|
+
},
|
165
|
+
remoteQualityLevel: 'HIGH',
|
166
|
+
enableRtx: ENABLE_RTX,
|
167
|
+
enableExtmap: ENABLE_EXTMAP,
|
168
|
+
turnServerInfo: undefined,
|
169
|
+
iceCandidatesTimeout: undefined,
|
170
|
+
});
|
171
|
+
|
172
|
+
assert.calledWith(
|
173
|
+
roapMediaConnectionConstructorStub,
|
174
|
+
sinon.match.any,
|
175
|
+
{
|
176
|
+
localTracks: {
|
177
|
+
audio: fakeTrack,
|
178
|
+
video: fakeTrack,
|
179
|
+
screenShareVideo: undefined,
|
180
|
+
screenShareAudio: undefined,
|
181
|
+
},
|
182
|
+
direction: {
|
183
|
+
audio: 'sendonly',
|
184
|
+
video: 'sendonly',
|
185
|
+
screenShareVideo: 'inactive',
|
186
|
+
},
|
187
|
+
remoteQualityLevel: 'HIGH',
|
188
|
+
},
|
189
|
+
'sendonly-debug-id'
|
190
|
+
);
|
191
|
+
});
|
192
|
+
|
193
|
+
it('should set direction to recvonly for both audio and video when only receive flags are true', () => {
|
194
|
+
const roapMediaConnectionConstructorStub = sinon
|
195
|
+
.stub(InternalMediaCoreModule, 'RoapMediaConnection')
|
196
|
+
.returns(fakeRoapMediaConnection);
|
197
|
+
|
198
|
+
StaticConfig.set({bandwidth: {audio: 123, video: 456, startBitrate: 999}});
|
199
|
+
|
200
|
+
const ENABLE_EXTMAP = true;
|
201
|
+
const ENABLE_RTX = false;
|
202
|
+
|
203
|
+
Media.createMediaConnection(false, 'recvonly-debug-id', 'meetingId', {
|
204
|
+
mediaProperties: {
|
205
|
+
mediaDirection: {
|
206
|
+
sendAudio: false,
|
207
|
+
receiveAudio: true,
|
208
|
+
sendVideo: false,
|
209
|
+
receiveVideo: true,
|
210
|
+
sendShare: false,
|
211
|
+
receiveShare: false,
|
212
|
+
},
|
213
|
+
audioStream: fakeAudioStream,
|
214
|
+
videoStream: fakeVideoStream,
|
215
|
+
shareVideoTrack: null,
|
216
|
+
shareAudioTrack: null,
|
217
|
+
},
|
218
|
+
remoteQualityLevel: 'HIGH',
|
219
|
+
enableRtx: ENABLE_RTX,
|
220
|
+
enableExtmap: ENABLE_EXTMAP,
|
221
|
+
turnServerInfo: undefined,
|
222
|
+
iceCandidatesTimeout: undefined,
|
223
|
+
});
|
224
|
+
|
225
|
+
assert.calledWith(
|
226
|
+
roapMediaConnectionConstructorStub,
|
227
|
+
sinon.match.any,
|
228
|
+
{
|
229
|
+
localTracks: {
|
230
|
+
audio: fakeTrack,
|
231
|
+
video: fakeTrack,
|
232
|
+
screenShareVideo: undefined,
|
233
|
+
screenShareAudio: undefined,
|
234
|
+
},
|
235
|
+
direction: {
|
236
|
+
audio: 'recvonly',
|
237
|
+
video: 'recvonly',
|
238
|
+
screenShareVideo: 'inactive',
|
239
|
+
},
|
240
|
+
remoteQualityLevel: 'HIGH',
|
241
|
+
},
|
242
|
+
'recvonly-debug-id'
|
243
|
+
);
|
244
|
+
});
|
245
|
+
|
246
|
+
|
140
247
|
it('creates a MultistreamRoapMediaConnection when multistream is enabled', () => {
|
141
248
|
const multistreamRoapMediaConnectionConstructorStub = sinon
|
142
249
|
.stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
|
@@ -3,12 +3,12 @@ import {assert, expect} from '@webex/test-helper-chai';
|
|
3
3
|
|
4
4
|
import testUtils from '../../../utils/testUtils';
|
5
5
|
import {BrbState, createBrbState} from '@webex/plugin-meetings/src/meeting/brbState';
|
6
|
-
import
|
6
|
+
import {MediaType} from '@webex/internal-media-core';
|
7
7
|
|
8
8
|
describe('plugin-meetings', () => {
|
9
9
|
let meeting: any;
|
10
10
|
let brbState: BrbState;
|
11
|
-
let setBrbStub: sinon.SinonStub;
|
11
|
+
let setBrbStub: sinon.SinonStub;
|
12
12
|
|
13
13
|
beforeEach(async () => {
|
14
14
|
meeting = {
|
@@ -23,7 +23,7 @@ describe('plugin-meetings', () => {
|
|
23
23
|
setSourceStateOverride: sinon.stub(),
|
24
24
|
},
|
25
25
|
meetingRequest: {
|
26
|
-
setBrb: () => {}
|
26
|
+
setBrb: () => {},
|
27
27
|
},
|
28
28
|
};
|
29
29
|
|
@@ -104,12 +104,12 @@ describe('plugin-meetings', () => {
|
|
104
104
|
assert.isTrue(meeting.meetingRequest.setBrb.calledOnce);
|
105
105
|
});
|
106
106
|
|
107
|
-
it('
|
107
|
+
it('updates source state override', async () => {
|
108
108
|
brbState.enable(true, meeting.sendSlotManager);
|
109
109
|
brbState.handleServerBrbUpdate(true);
|
110
110
|
await testUtils.flushPromises();
|
111
111
|
|
112
|
-
assert.isTrue(meeting.sendSlotManager.setSourceStateOverride.
|
112
|
+
assert.isTrue(meeting.sendSlotManager.setSourceStateOverride.called);
|
113
113
|
});
|
114
114
|
|
115
115
|
it('handles server update', async () => {
|
@@ -141,12 +141,12 @@ describe('plugin-meetings', () => {
|
|
141
141
|
it('should reject when sendLocalBrbStateToServer fails', async () => {
|
142
142
|
const error = new Error('send failed');
|
143
143
|
setBrbStub.rejects(error);
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
).to.be.rejectedWith(error);
|
144
|
+
|
145
|
+
const enablePromise = brbState.enable(true, meeting.sendSlotManager);
|
146
|
+
await expect(enablePromise).to.be.rejectedWith(error);
|
148
147
|
|
149
148
|
assert.isFalse(brbState.state.syncToServerInProgress);
|
149
|
+
expect(meeting.sendSlotManager.setSourceStateOverride.called).to.be.false;
|
150
150
|
});
|
151
151
|
});
|
152
152
|
});
|