@webex/plugin-meetings 3.0.0-stream-classes.3 → 3.0.0-stream-classes.5
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/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +5 -1
- 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 +25 -1
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/parser.js +5 -0
- package/dist/locus-info/parser.js.map +1 -1
- package/dist/meeting/index.js +72 -15
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +1 -1
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/meeting/request.js +24 -14
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/util.js +33 -3
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +3 -4
- package/dist/meetings/index.js.map +1 -1
- package/dist/metrics/constants.js +4 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/multistream/remoteMediaManager.js +46 -9
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/reachability/index.js +2 -11
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/request.js.map +1 -1
- package/dist/reconnection-manager/index.js +26 -22
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/roap/index.js +5 -7
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/request.js +1 -0
- package/dist/roap/request.js.map +1 -1
- package/dist/roap/turnDiscovery.js +3 -4
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/types/constants.d.ts +1 -0
- package/dist/types/meeting/index.d.ts +16 -2
- package/dist/types/meeting/locusMediaRequest.d.ts +1 -2
- package/dist/types/meeting/request.d.ts +2 -1
- package/dist/types/meeting/util.d.ts +9 -1
- package/dist/types/metrics/constants.d.ts +3 -0
- package/dist/types/multistream/remoteMediaManager.d.ts +9 -1
- package/dist/types/reachability/index.d.ts +0 -6
- package/dist/types/reachability/request.d.ts +1 -1
- package/dist/types/roap/request.d.ts +2 -1
- package/package.json +1 -1
- package/src/constants.ts +3 -2
- package/src/locus-info/index.ts +33 -2
- package/src/locus-info/parser.ts +7 -0
- package/src/meeting/index.ts +59 -16
- package/src/meeting/locusMediaRequest.ts +2 -3
- package/src/meeting/request.ts +16 -6
- package/src/meeting/util.ts +36 -2
- package/src/meetings/index.ts +2 -2
- package/src/metrics/constants.ts +3 -0
- package/src/multistream/remoteMediaManager.ts +41 -4
- package/src/reachability/index.ts +4 -10
- package/src/reachability/request.ts +1 -1
- package/src/reconnection-manager/index.ts +13 -11
- package/src/roap/index.ts +2 -4
- package/src/roap/request.ts +2 -1
- package/src/roap/turnDiscovery.ts +2 -3
- package/test/integration/spec/converged-space-meetings.js +7 -7
- package/test/integration/spec/journey.js +85 -103
- package/test/integration/spec/space-meeting.js +9 -9
- package/test/unit/spec/locus-info/index.js +118 -1
- package/test/unit/spec/meeting/index.js +95 -30
- package/test/unit/spec/meeting/locusMediaRequest.ts +1 -2
- package/test/unit/spec/meeting/request.js +23 -0
- package/test/unit/spec/meeting/utils.js +73 -4
- package/test/unit/spec/meetings/index.js +68 -3
- package/test/unit/spec/multistream/remoteMediaManager.ts +10 -2
- package/test/unit/spec/reachability/index.ts +8 -8
- package/test/unit/spec/reconnection-manager/index.js +21 -0
- package/test/unit/spec/roap/index.ts +5 -1
- package/test/unit/spec/roap/turnDiscovery.ts +11 -4
- package/test/utils/integrationTestUtils.js +4 -4
|
@@ -10,6 +10,7 @@ import SelfUtils from '@webex/plugin-meetings/src/locus-info/selfUtils';
|
|
|
10
10
|
import InfoUtils from '@webex/plugin-meetings/src/locus-info/infoUtils';
|
|
11
11
|
import EmbeddedAppsUtils from '@webex/plugin-meetings/src/locus-info/embeddedAppsUtils';
|
|
12
12
|
import LocusDeltaParser from '@webex/plugin-meetings/src/locus-info/parser';
|
|
13
|
+
import Metrics from '@webex/plugin-meetings/src/metrics';
|
|
13
14
|
|
|
14
15
|
import {
|
|
15
16
|
LOCUSINFO,
|
|
@@ -24,7 +25,6 @@ import {
|
|
|
24
25
|
} from '../../../../src/constants';
|
|
25
26
|
|
|
26
27
|
import {self, selfWithInactivity} from './selfConstant';
|
|
27
|
-
import uuid from 'uuid';
|
|
28
28
|
|
|
29
29
|
describe('plugin-meetings', () => {
|
|
30
30
|
describe('LocusInfo index', () => {
|
|
@@ -39,6 +39,7 @@ describe('plugin-meetings', () => {
|
|
|
39
39
|
const locus = {};
|
|
40
40
|
const meetingId = 'meetingId';
|
|
41
41
|
let locusInfo;
|
|
42
|
+
let sendBehavioralMetricStub;
|
|
42
43
|
|
|
43
44
|
const webex = new MockWebex({
|
|
44
45
|
children: {
|
|
@@ -65,6 +66,12 @@ describe('plugin-meetings', () => {
|
|
|
65
66
|
},
|
|
66
67
|
},
|
|
67
68
|
};
|
|
69
|
+
|
|
70
|
+
sendBehavioralMetricStub = sinon.stub(Metrics, 'sendBehavioralMetric');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
afterEach(() => {
|
|
74
|
+
sinon.restore();
|
|
68
75
|
});
|
|
69
76
|
|
|
70
77
|
describe('#updateControls', () => {
|
|
@@ -1877,6 +1884,114 @@ describe('plugin-meetings', () => {
|
|
|
1877
1884
|
});
|
|
1878
1885
|
});
|
|
1879
1886
|
|
|
1887
|
+
describe('edge cases for sync failing', () => {
|
|
1888
|
+
const {DESYNC} = LocusDeltaParser.loci;
|
|
1889
|
+
const fakeFullLocusDto = {id: 'fake full locus dto'};
|
|
1890
|
+
let meeting;
|
|
1891
|
+
|
|
1892
|
+
beforeEach(() => {
|
|
1893
|
+
sinon.stub(locusInfo.locusParser, 'resume');
|
|
1894
|
+
sinon.stub(webex.meetings, 'destroy');
|
|
1895
|
+
|
|
1896
|
+
meeting = {
|
|
1897
|
+
meetingRequest: {
|
|
1898
|
+
getLocusDTO: sandbox.stub(),
|
|
1899
|
+
},
|
|
1900
|
+
locusInfo: {
|
|
1901
|
+
handleLocusDelta: sandbox.stub(),
|
|
1902
|
+
onFullLocus: sandbox.stub(),
|
|
1903
|
+
},
|
|
1904
|
+
locusUrl: 'fullSyncUrl',
|
|
1905
|
+
};
|
|
1906
|
+
|
|
1907
|
+
locusInfo.locusParser.workingCopy = {
|
|
1908
|
+
syncUrl: 'deltaSyncUrl',
|
|
1909
|
+
};
|
|
1910
|
+
});
|
|
1911
|
+
|
|
1912
|
+
it('applyLocusDeltaData gets full locus on DESYNC action if we do not have a syncUrl and destroys the meeting if that fails', () => {
|
|
1913
|
+
meeting.meetingRequest.getLocusDTO.rejects(new Error('fake error'));
|
|
1914
|
+
|
|
1915
|
+
locusInfo.locusParser.workingCopy = {}; // no syncUrl
|
|
1916
|
+
|
|
1917
|
+
// Since we have a promise inside a function we want to test that's not returned,
|
|
1918
|
+
// we will wait and stub it's last function to resolve this waiting promise.
|
|
1919
|
+
return new Promise((resolve) => {
|
|
1920
|
+
webex.meetings.destroy.callsFake(() => resolve());
|
|
1921
|
+
locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
|
|
1922
|
+
}).then(() => {
|
|
1923
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'fullSyncUrl'});
|
|
1924
|
+
|
|
1925
|
+
assert.notCalled(meeting.locusInfo.handleLocusDelta);
|
|
1926
|
+
assert.notCalled(meeting.locusInfo.onFullLocus);
|
|
1927
|
+
assert.notCalled(locusInfo.locusParser.resume);
|
|
1928
|
+
|
|
1929
|
+
assert.calledOnceWithExactly(webex.meetings.destroy, meeting, 'LOCUS_DTO_SYNC_FAILED');
|
|
1930
|
+
});
|
|
1931
|
+
});
|
|
1932
|
+
|
|
1933
|
+
it('applyLocusDeltaData first tries a delta sync on DESYNC action and if that fails, does a full locus sync', () => {
|
|
1934
|
+
meeting.meetingRequest.getLocusDTO.onCall(0).rejects(new Error('fake error'));
|
|
1935
|
+
meeting.meetingRequest.getLocusDTO.onCall(1).resolves({body: fakeFullLocusDto});
|
|
1936
|
+
|
|
1937
|
+
// Since we have a promise inside a function we want to test that's not returned,
|
|
1938
|
+
// we will wait and stub it's last function to resolve this waiting promise.
|
|
1939
|
+
return new Promise((resolve) => {
|
|
1940
|
+
locusInfo.locusParser.resume.callsFake(() => resolve());
|
|
1941
|
+
locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
|
|
1942
|
+
}).then(() => {
|
|
1943
|
+
assert.calledTwice(meeting.meetingRequest.getLocusDTO);
|
|
1944
|
+
|
|
1945
|
+
assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[0].args, [{url: 'deltaSyncUrl'}]);
|
|
1946
|
+
assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[1].args, [{url: 'fullSyncUrl'}]);
|
|
1947
|
+
|
|
1948
|
+
assert.calledWith(sendBehavioralMetricStub, 'js_sdk_locus_delta_sync_failed', {
|
|
1949
|
+
correlationId: meeting.correlationId,
|
|
1950
|
+
url: 'deltaSyncUrl',
|
|
1951
|
+
reason: 'fake error',
|
|
1952
|
+
errorName: 'Error',
|
|
1953
|
+
stack: sinon.match.any,
|
|
1954
|
+
code: sinon.match.any,
|
|
1955
|
+
});
|
|
1956
|
+
|
|
1957
|
+
assert.notCalled(meeting.locusInfo.handleLocusDelta);
|
|
1958
|
+
assert.calledOnceWithExactly(meeting.locusInfo.onFullLocus, fakeFullLocusDto);
|
|
1959
|
+
assert.calledOnce(locusInfo.locusParser.resume);
|
|
1960
|
+
});
|
|
1961
|
+
});
|
|
1962
|
+
|
|
1963
|
+
it('applyLocusDeltaData destroys the meeting if both delta sync and full sync fail', () => {
|
|
1964
|
+
meeting.meetingRequest.getLocusDTO.rejects(new Error('fake error'));
|
|
1965
|
+
|
|
1966
|
+
// Since we have a promise inside a function we want to test that's not returned,
|
|
1967
|
+
// we will wait and stub it's last function to resolve this waiting promise.
|
|
1968
|
+
return new Promise((resolve) => {
|
|
1969
|
+
webex.meetings.destroy.callsFake(() => resolve());
|
|
1970
|
+
locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
|
|
1971
|
+
}).then(() => {
|
|
1972
|
+
assert.calledTwice(meeting.meetingRequest.getLocusDTO);
|
|
1973
|
+
|
|
1974
|
+
assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[0].args, [{url: 'deltaSyncUrl'}]);
|
|
1975
|
+
assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[1].args, [{url: 'fullSyncUrl'}]);
|
|
1976
|
+
|
|
1977
|
+
assert.calledWith(sendBehavioralMetricStub, 'js_sdk_locus_delta_sync_failed', {
|
|
1978
|
+
correlationId: meeting.correlationId,
|
|
1979
|
+
url: 'deltaSyncUrl',
|
|
1980
|
+
reason: 'fake error',
|
|
1981
|
+
errorName: 'Error',
|
|
1982
|
+
stack: sinon.match.any,
|
|
1983
|
+
code: sinon.match.any,
|
|
1984
|
+
});
|
|
1985
|
+
|
|
1986
|
+
assert.notCalled(meeting.locusInfo.handleLocusDelta);
|
|
1987
|
+
assert.notCalled(meeting.locusInfo.onFullLocus);
|
|
1988
|
+
assert.notCalled(locusInfo.locusParser.resume);
|
|
1989
|
+
|
|
1990
|
+
assert.calledOnceWithExactly(webex.meetings.destroy, meeting, 'LOCUS_DTO_SYNC_FAILED');
|
|
1991
|
+
});
|
|
1992
|
+
});
|
|
1993
|
+
});
|
|
1994
|
+
|
|
1880
1995
|
it('onDeltaLocus handle delta data', () => {
|
|
1881
1996
|
fakeLocus.participants = {};
|
|
1882
1997
|
const fakeBreakout = {
|
|
@@ -2420,6 +2535,8 @@ describe('plugin-meetings', () => {
|
|
|
2420
2535
|
// send an out-of-order delta
|
|
2421
2536
|
locusInfo.handleLocusDelta(oooDelta, mockMeeting);
|
|
2422
2537
|
|
|
2538
|
+
assert.calledOnceWithExactly(sendBehavioralMetricStub, 'js_sdk_locus_delta_ooo', { stack: sinon.match.any})
|
|
2539
|
+
|
|
2423
2540
|
await clock.tickAsync(12499);
|
|
2424
2541
|
await testUtils.flushPromises();
|
|
2425
2542
|
assert.notCalled(syncRequestStub);
|
|
@@ -1406,9 +1406,10 @@ describe('plugin-meetings', () => {
|
|
|
1406
1406
|
});
|
|
1407
1407
|
|
|
1408
1408
|
it('should reject if waitForMediaConnectionConnected() rejects', async () => {
|
|
1409
|
-
|
|
1409
|
+
const FAKE_ERROR = {fatal: true};
|
|
1410
|
+
const getErrorPayloadForClientErrorCodeStub = webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode = sinon
|
|
1410
1411
|
.stub()
|
|
1411
|
-
.returns(
|
|
1412
|
+
.returns(FAKE_ERROR);
|
|
1412
1413
|
meeting.meetingState = 'ACTIVE';
|
|
1413
1414
|
meeting.mediaProperties.waitForMediaConnectionConnected.rejects(new Error('fake error'));
|
|
1414
1415
|
|
|
@@ -1422,14 +1423,14 @@ describe('plugin-meetings', () => {
|
|
|
1422
1423
|
errorThrown = true;
|
|
1423
1424
|
});
|
|
1424
1425
|
|
|
1426
|
+
assert.calledOnceWithExactly(getErrorPayloadForClientErrorCodeStub, {clientErrorCode: 2004});
|
|
1425
1427
|
assert.calledTwice(webex.internal.newMetrics.submitClientEvent);
|
|
1426
|
-
|
|
1427
1428
|
assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
|
|
1428
1429
|
name: 'client.ice.end',
|
|
1429
1430
|
payload: {
|
|
1430
1431
|
canProceed: false,
|
|
1431
1432
|
icePhase: 'JOIN_MEETING_FINAL',
|
|
1432
|
-
errors: [
|
|
1433
|
+
errors: [FAKE_ERROR],
|
|
1433
1434
|
},
|
|
1434
1435
|
options: {
|
|
1435
1436
|
meetingId: meeting.id,
|
|
@@ -1674,6 +1675,8 @@ describe('plugin-meetings', () => {
|
|
|
1674
1675
|
beforeEach(() => {
|
|
1675
1676
|
clock = sinon.useFakeTimers();
|
|
1676
1677
|
|
|
1678
|
+
sinon.stub(MeetingUtil, 'getIpVersion').returns(IP_VERSION.unknown);
|
|
1679
|
+
|
|
1677
1680
|
meeting.deviceUrl = 'deviceUrl';
|
|
1678
1681
|
meeting.config.deviceType = 'web';
|
|
1679
1682
|
meeting.isMultistream = isMultistream;
|
|
@@ -1687,12 +1690,11 @@ describe('plugin-meetings', () => {
|
|
|
1687
1690
|
meeting.webex.meetings.geoHintInfo = {regionCode: 'EU', countryCode: 'UK'};
|
|
1688
1691
|
meeting.webex.meetings.reachability = {
|
|
1689
1692
|
isAnyClusterReachable: sinon.stub().resolves(true),
|
|
1690
|
-
getIpVersion: () => IP_VERSION.unknown,
|
|
1691
1693
|
};
|
|
1692
1694
|
meeting.roap.doTurnDiscovery = sinon
|
|
1693
1695
|
.stub()
|
|
1694
1696
|
.resolves({turnServerInfo: {}, turnDiscoverySkippedReason: 'reachability'});
|
|
1695
|
-
|
|
1697
|
+
|
|
1696
1698
|
StaticConfig.set({bandwidth: {audio: 1234, video: 5678, startBitrate: 9876}});
|
|
1697
1699
|
|
|
1698
1700
|
// setup things that are expected to be the same across all the tests and are actually irrelevant for these tests
|
|
@@ -1768,6 +1770,7 @@ describe('plugin-meetings', () => {
|
|
|
1768
1770
|
|
|
1769
1771
|
afterEach(() => {
|
|
1770
1772
|
clock.restore();
|
|
1773
|
+
sinon.restore();
|
|
1771
1774
|
});
|
|
1772
1775
|
|
|
1773
1776
|
// helper function that waits until all promises are resolved and any queued up /media requests to Locus are sent out
|
|
@@ -1855,7 +1858,7 @@ describe('plugin-meetings', () => {
|
|
|
1855
1858
|
],
|
|
1856
1859
|
clientMediaPreferences: {
|
|
1857
1860
|
preferTranscoding: !meeting.isMultistream,
|
|
1858
|
-
ipver:
|
|
1861
|
+
ipver: undefined
|
|
1859
1862
|
},
|
|
1860
1863
|
respOnlySdp: true,
|
|
1861
1864
|
usingResource: null,
|
|
@@ -1936,7 +1939,7 @@ describe('plugin-meetings', () => {
|
|
|
1936
1939
|
// and that it was the only /media request that was sent
|
|
1937
1940
|
assert.calledOnce(locusMediaRequestStub);
|
|
1938
1941
|
});
|
|
1939
|
-
|
|
1942
|
+
|
|
1940
1943
|
it('addMedia() works correctly when media is enabled with streams to publish', async () => {
|
|
1941
1944
|
await meeting.addMedia({localStreams: {microphone: fakeMicrophoneStream}});
|
|
1942
1945
|
await simulateRoapOffer();
|
|
@@ -2083,7 +2086,7 @@ describe('plugin-meetings', () => {
|
|
|
2083
2086
|
} else {
|
|
2084
2087
|
assert.notCalled(locusMediaRequestStub);
|
|
2085
2088
|
}
|
|
2086
|
-
|
|
2089
|
+
|
|
2087
2090
|
if (isMultistream) {
|
|
2088
2091
|
assert.calledOnceWithExactly(meeting.sendSlotManager.getSlot(MediaType.AudioMain).publishStream, fakeMicrophoneStream);
|
|
2089
2092
|
} else {
|
|
@@ -4485,20 +4488,22 @@ describe('plugin-meetings', () => {
|
|
|
4485
4488
|
|
|
4486
4489
|
describe('submitClientEvent on connectionFailed', () => {
|
|
4487
4490
|
it('sends client.ice.end when connectionFailed on CONNECTION_STATE_CHANGED event', () => {
|
|
4488
|
-
|
|
4489
|
-
|
|
4491
|
+
const FAKE_ERROR = {fatal: true};
|
|
4492
|
+
const getErrorPayloadForClientErrorCodeStub = webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode = sinon
|
|
4493
|
+
.stub()
|
|
4494
|
+
.returns(FAKE_ERROR);
|
|
4490
4495
|
meeting.setupMediaConnectionListeners();
|
|
4491
4496
|
eventListeners[Event.CONNECTION_STATE_CHANGED]({
|
|
4492
4497
|
state: 'Failed',
|
|
4493
4498
|
});
|
|
4499
|
+
assert.calledOnceWithExactly(getErrorPayloadForClientErrorCodeStub, {clientErrorCode: 2004});
|
|
4494
4500
|
assert.calledOnce(webex.internal.newMetrics.submitClientEvent);
|
|
4495
|
-
|
|
4496
4501
|
assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
|
|
4497
4502
|
name: 'client.ice.end',
|
|
4498
4503
|
payload: {
|
|
4499
4504
|
canProceed: false,
|
|
4500
4505
|
icePhase: 'IN_MEETING',
|
|
4501
|
-
errors: [
|
|
4506
|
+
errors: [FAKE_ERROR],
|
|
4502
4507
|
},
|
|
4503
4508
|
options: {
|
|
4504
4509
|
meetingId: meeting.id,
|
|
@@ -5557,29 +5562,40 @@ describe('plugin-meetings', () => {
|
|
|
5557
5562
|
});
|
|
5558
5563
|
});
|
|
5559
5564
|
|
|
5565
|
+
describe('#setPermissionTokenPayload', () => {
|
|
5566
|
+
it('sets correctly', () => {
|
|
5567
|
+
assert.notOk(meeting.permissionTokenPayload);
|
|
5568
|
+
|
|
5569
|
+
const permissionTokenPayloadData = {permission: {userPolicies: {a: true}}, exp: '1234'};
|
|
5570
|
+
|
|
5571
|
+
const jwtDecodeStub = sinon.stub(jwt, 'decode').returns(permissionTokenPayloadData);
|
|
5572
|
+
|
|
5573
|
+
meeting.setPermissionTokenPayload();
|
|
5574
|
+
|
|
5575
|
+
assert.calledOnce(jwtDecodeStub);
|
|
5576
|
+
assert.deepEqual(meeting.permissionTokenPayload, permissionTokenPayloadData);
|
|
5577
|
+
});
|
|
5578
|
+
});
|
|
5579
|
+
|
|
5560
5580
|
describe('#setSelfUserPolicies', () => {
|
|
5561
5581
|
it('sets correctly when policy data is present in token', () => {
|
|
5562
5582
|
assert.notOk(meeting.selfUserPolicies);
|
|
5563
5583
|
|
|
5564
|
-
const dummyToken = 'some data';
|
|
5565
5584
|
const policyData = {permission: {userPolicies: {a: true}}};
|
|
5585
|
+
meeting.permissionTokenPayload = policyData;
|
|
5566
5586
|
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
meeting.setSelfUserPolicies(dummyToken);
|
|
5587
|
+
meeting.setSelfUserPolicies();
|
|
5570
5588
|
|
|
5571
|
-
assert.deepEqual(meeting.selfUserPolicies,
|
|
5589
|
+
assert.deepEqual(meeting.selfUserPolicies, policyData.permission.userPolicies);
|
|
5572
5590
|
});
|
|
5573
5591
|
|
|
5574
5592
|
it('handles missing permission data', () => {
|
|
5575
5593
|
assert.notOk(meeting.selfUserPolicies);
|
|
5576
5594
|
|
|
5577
|
-
const dummyToken = 'some data';
|
|
5578
5595
|
const policyData = {};
|
|
5596
|
+
meeting.permissionTokenPayload = policyData;
|
|
5579
5597
|
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
meeting.setSelfUserPolicies(dummyToken);
|
|
5598
|
+
meeting.setSelfUserPolicies();
|
|
5583
5599
|
|
|
5584
5600
|
assert.deepEqual(meeting.selfUserPolicies, undefined);
|
|
5585
5601
|
});
|
|
@@ -5587,12 +5603,10 @@ describe('plugin-meetings', () => {
|
|
|
5587
5603
|
it('handles missing policy data', () => {
|
|
5588
5604
|
assert.notOk(meeting.selfUserPolicies);
|
|
5589
5605
|
|
|
5590
|
-
const dummyToken = 'some data';
|
|
5591
5606
|
const policyData = {permission: {}};
|
|
5607
|
+
meeting.permissionTokenPayload = policyData;
|
|
5592
5608
|
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
meeting.setSelfUserPolicies(dummyToken);
|
|
5609
|
+
meeting.setSelfUserPolicies();
|
|
5596
5610
|
|
|
5597
5611
|
assert.deepEqual(meeting.selfUserPolicies, undefined);
|
|
5598
5612
|
});
|
|
@@ -5652,19 +5666,37 @@ describe('plugin-meetings', () => {
|
|
|
5652
5666
|
assert.equal(meeting.owner, expectedInfoToParse.owner);
|
|
5653
5667
|
assert.equal(meeting.permissionToken, expectedInfoToParse.permissionToken);
|
|
5654
5668
|
assert.deepEqual(meeting.selfUserPolicies, expectedInfoToParse.selfUserPolicies);
|
|
5669
|
+
|
|
5670
|
+
if(expectedInfoToParse.permissionTokenPayload) {
|
|
5671
|
+
assert.deepEqual(meeting.permissionTokenPayload, expectedInfoToParse.permissionTokenPayload);
|
|
5672
|
+
}
|
|
5655
5673
|
};
|
|
5656
5674
|
|
|
5657
5675
|
it('should parse meeting info from api return when locus meeting object is not available, set values, and return null', () => {
|
|
5658
5676
|
meeting.config.experimental = {enableMediaNegotiatedEvent: true};
|
|
5659
5677
|
meeting.config.experimental.enableUnifiedMeetings = true;
|
|
5678
|
+
|
|
5679
|
+
const expectedPermissionTokenPayload = {
|
|
5680
|
+
exp: "123456",
|
|
5681
|
+
permission: {
|
|
5682
|
+
userPolicies: {
|
|
5683
|
+
a: true
|
|
5684
|
+
}
|
|
5685
|
+
}
|
|
5686
|
+
};
|
|
5687
|
+
|
|
5688
|
+
// generated permissionToken with secret `secret` and
|
|
5689
|
+
// value `JSON.stringify(expectedPermissionTokenPayload)`
|
|
5690
|
+
const permissionToken =
|
|
5691
|
+
'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0';
|
|
5692
|
+
|
|
5660
5693
|
const FAKE_MEETING_INFO = {
|
|
5661
5694
|
body: {
|
|
5662
5695
|
conversationUrl: uuid1,
|
|
5663
5696
|
locusUrl: url1,
|
|
5664
5697
|
meetingJoinUrl: url2,
|
|
5665
5698
|
meetingNumber: '12345',
|
|
5666
|
-
permissionToken
|
|
5667
|
-
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX0sImlhdCI6MTY4OTE2NDEwMn0.9uL_U7QUdYyMerrgHC_gCKOax2j_bz04u8Ikbv9KiXU',
|
|
5699
|
+
permissionToken,
|
|
5668
5700
|
sipMeetingUri: test1,
|
|
5669
5701
|
sipUrl: test1,
|
|
5670
5702
|
owner: test2,
|
|
@@ -5672,6 +5704,7 @@ describe('plugin-meetings', () => {
|
|
|
5672
5704
|
};
|
|
5673
5705
|
|
|
5674
5706
|
meeting.parseMeetingInfo(FAKE_MEETING_INFO);
|
|
5707
|
+
|
|
5675
5708
|
const expectedInfoToParse = {
|
|
5676
5709
|
conversationUrl: uuid1,
|
|
5677
5710
|
locusUrl: url1,
|
|
@@ -5680,8 +5713,8 @@ describe('plugin-meetings', () => {
|
|
|
5680
5713
|
meetingJoinUrl: url2,
|
|
5681
5714
|
owner: test2,
|
|
5682
5715
|
selfUserPolicies: {a: true},
|
|
5683
|
-
permissionToken
|
|
5684
|
-
|
|
5716
|
+
permissionToken,
|
|
5717
|
+
permissionTokenPayload: expectedPermissionTokenPayload
|
|
5685
5718
|
};
|
|
5686
5719
|
|
|
5687
5720
|
checkParseMeetingInfo(expectedInfoToParse);
|
|
@@ -7991,4 +8024,36 @@ describe('plugin-meetings', () => {
|
|
|
7991
8024
|
});
|
|
7992
8025
|
});
|
|
7993
8026
|
});
|
|
8027
|
+
|
|
8028
|
+
describe('#getPermissionTokenTimeLeftInSec', () => {
|
|
8029
|
+
let now;
|
|
8030
|
+
let clock;
|
|
8031
|
+
|
|
8032
|
+
beforeEach(() => {
|
|
8033
|
+
now = Date.now();
|
|
8034
|
+
|
|
8035
|
+
// mock `new Date()` with constant `now`
|
|
8036
|
+
clock = sinon.useFakeTimers(now);
|
|
8037
|
+
});
|
|
8038
|
+
|
|
8039
|
+
afterEach(() => {
|
|
8040
|
+
clock.restore();
|
|
8041
|
+
})
|
|
8042
|
+
|
|
8043
|
+
it('should return undefined if exp is undefined', () => {
|
|
8044
|
+
assert.equal(meeting.getPermissionTokenTimeLeftInSec(), undefined)
|
|
8045
|
+
});
|
|
8046
|
+
|
|
8047
|
+
it('should return the expected positive exp', () => {
|
|
8048
|
+
// set permission token as now + 1 sec
|
|
8049
|
+
meeting.permissionTokenPayload = {exp: (now + 1000).toString()};
|
|
8050
|
+
assert.equal(meeting.getPermissionTokenTimeLeftInSec(), 1);
|
|
8051
|
+
});
|
|
8052
|
+
|
|
8053
|
+
it('should return the expected negative exp', () => {
|
|
8054
|
+
// set permission token as now - 1 sec
|
|
8055
|
+
meeting.permissionTokenPayload = {exp: (now - 1000).toString()};
|
|
8056
|
+
assert.equal(meeting.getPermissionTokenTimeLeftInSec(), -1);
|
|
8057
|
+
});
|
|
8058
|
+
});
|
|
7994
8059
|
});
|
|
@@ -68,7 +68,6 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
68
68
|
mediaId: 'mediaId',
|
|
69
69
|
selfUrl: 'fakeMeetingSelfUrl',
|
|
70
70
|
muteOptions: {},
|
|
71
|
-
ipVersion: IP_VERSION.only_ipv6
|
|
72
71
|
};
|
|
73
72
|
|
|
74
73
|
const createExpectedLocalMuteBody = (expectedMute:{audioMuted: boolean, videoMuted: boolean}, sequence = undefined) => {
|
|
@@ -89,7 +88,7 @@ describe('LocusMediaRequest.send()', () => {
|
|
|
89
88
|
],
|
|
90
89
|
clientMediaPreferences: {
|
|
91
90
|
preferTranscoding: true,
|
|
92
|
-
ipver:
|
|
91
|
+
ipver: undefined,
|
|
93
92
|
},
|
|
94
93
|
};
|
|
95
94
|
|
|
@@ -237,6 +237,29 @@ describe('plugin-meetings', () => {
|
|
|
237
237
|
assert.equal(requestParams.body.invitee.address, 'sipUrl');
|
|
238
238
|
});
|
|
239
239
|
|
|
240
|
+
it('sends uses the locusClusterUrl if available', async () => {
|
|
241
|
+
const deviceUrl = 'deviceUrl';
|
|
242
|
+
const correlationId = 'random-uuid';
|
|
243
|
+
const roapMessage = 'roap-message';
|
|
244
|
+
const inviteeAddress = 'sipUrl';
|
|
245
|
+
const locusClusterUrl = 'locusClusterUrl';
|
|
246
|
+
|
|
247
|
+
await meetingsRequest.joinMeeting({
|
|
248
|
+
deviceUrl,
|
|
249
|
+
correlationId,
|
|
250
|
+
roapMessage,
|
|
251
|
+
locusClusterUrl,
|
|
252
|
+
inviteeAddress,
|
|
253
|
+
});
|
|
254
|
+
const requestParams = meetingsRequest.request.getCall(0).args[0];
|
|
255
|
+
|
|
256
|
+
assert.equal(requestParams.method, 'POST');
|
|
257
|
+
assert.equal(
|
|
258
|
+
requestParams.uri,
|
|
259
|
+
'https://locusClusterUrl/locus/api/v1/loci/call?alternateRedirect=true'
|
|
260
|
+
);
|
|
261
|
+
});
|
|
262
|
+
|
|
240
263
|
it('adds deviceCapabilities to request when breakouts are supported', async () => {
|
|
241
264
|
await meetingsRequest.joinMeeting({
|
|
242
265
|
breakoutsSupported: true,
|
|
@@ -4,10 +4,9 @@ import Meetings from '@webex/plugin-meetings';
|
|
|
4
4
|
import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
|
|
5
5
|
import LoggerProxy from '@webex/plugin-meetings/src/common/logs/logger-proxy';
|
|
6
6
|
import LoggerConfig from '@webex/plugin-meetings/src/common/logs/logger-config';
|
|
7
|
-
import
|
|
8
|
-
import {SELF_POLICY} from '@webex/plugin-meetings/src/constants';
|
|
9
|
-
import {DISPLAY_HINTS} from '@webex/plugin-meetings/src/constants';
|
|
7
|
+
import {SELF_POLICY, IP_VERSION} from '@webex/plugin-meetings/src/constants';
|
|
10
8
|
import MockWebex from '@webex/test-helper-mock-webex';
|
|
9
|
+
import * as BrowserDetectionModule from '@webex/plugin-meetings/src/common/browser-detection';
|
|
11
10
|
|
|
12
11
|
describe('plugin-meetings', () => {
|
|
13
12
|
let webex;
|
|
@@ -337,7 +336,6 @@ describe('plugin-meetings', () => {
|
|
|
337
336
|
selfUrl: 'self url',
|
|
338
337
|
sequence: {},
|
|
339
338
|
type: 'LocalMute',
|
|
340
|
-
ipVersion: 0,
|
|
341
339
|
});
|
|
342
340
|
|
|
343
341
|
assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
|
|
@@ -517,6 +515,26 @@ describe('plugin-meetings', () => {
|
|
|
517
515
|
assert.isUndefined(parameter.inviteeAddress);
|
|
518
516
|
assert.equal(parameter.meetingNumber, 'meetingNumber');
|
|
519
517
|
});
|
|
518
|
+
|
|
519
|
+
it('should pass in the locusClusterUrl from meetingInfo', async () => {
|
|
520
|
+
const meeting = {
|
|
521
|
+
meetingInfo: {
|
|
522
|
+
locusClusterUrl: 'locusClusterUrl',
|
|
523
|
+
},
|
|
524
|
+
meetingRequest: {
|
|
525
|
+
joinMeeting: sinon.stub().returns(Promise.resolve({body: {}, headers: {}})),
|
|
526
|
+
},
|
|
527
|
+
getWebexObject: sinon.stub().returns(webex),
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
MeetingUtil.parseLocusJoin = sinon.stub();
|
|
531
|
+
await MeetingUtil.joinMeeting(meeting, {});
|
|
532
|
+
|
|
533
|
+
assert.calledOnce(meeting.meetingRequest.joinMeeting);
|
|
534
|
+
const parameter = meeting.meetingRequest.joinMeeting.getCall(0).args[0];
|
|
535
|
+
|
|
536
|
+
assert.equal(parameter.locusClusterUrl, 'locusClusterUrl');
|
|
537
|
+
});
|
|
520
538
|
});
|
|
521
539
|
|
|
522
540
|
describe('joinMeetingOptions', () => {
|
|
@@ -963,5 +981,56 @@ describe('plugin-meetings', () => {
|
|
|
963
981
|
assert.equal(result, input);
|
|
964
982
|
});
|
|
965
983
|
});
|
|
984
|
+
|
|
985
|
+
describe('getIpVersion', () => {
|
|
986
|
+
let isBrowserStub;
|
|
987
|
+
beforeEach(() => {
|
|
988
|
+
isBrowserStub = sinon.stub().returns(false);
|
|
989
|
+
|
|
990
|
+
sinon.stub(BrowserDetectionModule, 'default').returns({
|
|
991
|
+
isBrowser: isBrowserStub,
|
|
992
|
+
});
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
afterEach(() => {
|
|
996
|
+
sinon.restore();
|
|
997
|
+
});
|
|
998
|
+
|
|
999
|
+
[
|
|
1000
|
+
{supportsIpV4: undefined, supportsIpV6: undefined, expectedOutput: IP_VERSION.unknown},
|
|
1001
|
+
{supportsIpV4: undefined, supportsIpV6: true, expectedOutput: IP_VERSION.only_ipv6},
|
|
1002
|
+
{supportsIpV4: undefined, supportsIpV6: false, expectedOutput: IP_VERSION.unknown},
|
|
1003
|
+
{supportsIpV4: true, supportsIpV6: undefined, expectedOutput: IP_VERSION.only_ipv4},
|
|
1004
|
+
{supportsIpV4: true, supportsIpV6: true, expectedOutput: IP_VERSION.ipv4_and_ipv6},
|
|
1005
|
+
{supportsIpV4: true, supportsIpV6: false, expectedOutput: IP_VERSION.only_ipv4},
|
|
1006
|
+
{supportsIpV4: false, supportsIpV6: undefined, expectedOutput: IP_VERSION.unknown},
|
|
1007
|
+
{supportsIpV4: false, supportsIpV6: true, expectedOutput: IP_VERSION.only_ipv6},
|
|
1008
|
+
{supportsIpV4: false, supportsIpV6: false, expectedOutput: IP_VERSION.unknown},
|
|
1009
|
+
].forEach(({supportsIpV4, supportsIpV6, expectedOutput}) => {
|
|
1010
|
+
it(`returns ${expectedOutput} when supportsIpV4=${supportsIpV4} and supportsIpV6=${supportsIpV6}`, () => {
|
|
1011
|
+
sinon
|
|
1012
|
+
.stub(webex.internal.device.ipNetworkDetector, 'supportsIpV4')
|
|
1013
|
+
.get(() => supportsIpV4);
|
|
1014
|
+
sinon
|
|
1015
|
+
.stub(webex.internal.device.ipNetworkDetector, 'supportsIpV6')
|
|
1016
|
+
.get(() => supportsIpV6);
|
|
1017
|
+
|
|
1018
|
+
assert.equal(MeetingUtil.getIpVersion(webex), expectedOutput);
|
|
1019
|
+
});
|
|
1020
|
+
|
|
1021
|
+
it(`returns undefined when supportsIpV4=${supportsIpV4} and supportsIpV6=${supportsIpV6} and browser is firefox`, () => {
|
|
1022
|
+
sinon
|
|
1023
|
+
.stub(webex.internal.device.ipNetworkDetector, 'supportsIpV4')
|
|
1024
|
+
.get(() => supportsIpV4);
|
|
1025
|
+
sinon
|
|
1026
|
+
.stub(webex.internal.device.ipNetworkDetector, 'supportsIpV6')
|
|
1027
|
+
.get(() => supportsIpV6);
|
|
1028
|
+
|
|
1029
|
+
isBrowserStub.callsFake((name) => name === 'firefox');
|
|
1030
|
+
|
|
1031
|
+
assert.equal(MeetingUtil.getIpVersion(webex), undefined);
|
|
1032
|
+
});
|
|
1033
|
+
});
|
|
1034
|
+
});
|
|
966
1035
|
});
|
|
967
1036
|
});
|