@webex/plugin-meetings 3.0.0-beta.16 → 3.0.0-beta.18
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 +116 -0
- package/dist/breakouts/breakout.js.map +1 -0
- package/dist/breakouts/collection.js +23 -0
- package/dist/breakouts/collection.js.map +1 -0
- package/dist/breakouts/index.js +226 -0
- package/dist/breakouts/index.js.map +1 -0
- package/dist/config.js +4 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js +43 -6
- package/dist/constants.js.map +1 -1
- package/dist/locus-info/controlsUtils.js +2 -1
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +48 -0
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/parser.js +1 -0
- package/dist/locus-info/parser.js.map +1 -1
- package/dist/locus-info/selfUtils.js +19 -11
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/index.js +3 -3
- package/dist/media/index.js.map +1 -1
- package/dist/media/properties.js +4 -4
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +5 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +652 -459
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/request.js +25 -44
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meeting/util.js +22 -57
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +2 -0
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meetings/index.js +28 -18
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/request.js +14 -12
- package/dist/meetings/request.js.map +1 -1
- package/dist/member/index.js +9 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/util.js +14 -1
- package/dist/member/util.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 -1
- package/dist/members/request.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +46 -6
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/multistreamMedia.js +4 -0
- package/dist/multistream/multistreamMedia.js.map +1 -1
- package/dist/multistream/receiveSlot.js +3 -3
- package/dist/multistream/receiveSlot.js.map +1 -1
- package/dist/multistream/receiveSlotManager.js +8 -6
- package/dist/multistream/receiveSlotManager.js.map +1 -1
- package/dist/multistream/remoteMedia.js.map +1 -1
- package/dist/multistream/remoteMediaGroup.js.map +1 -1
- package/dist/multistream/remoteMediaManager.js +168 -63
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/reachability/index.js +63 -51
- package/dist/reachability/index.js.map +1 -1
- package/dist/reactions/constants.js +13 -0
- package/dist/reactions/constants.js.map +1 -0
- package/dist/reactions/reactions.type.js.map +1 -1
- package/dist/reconnection-manager/index.js +25 -12
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/recording-controller/enums.js +17 -0
- package/dist/recording-controller/enums.js.map +1 -0
- package/dist/recording-controller/index.js +343 -0
- package/dist/recording-controller/index.js.map +1 -0
- package/dist/recording-controller/util.js +63 -0
- package/dist/recording-controller/util.js.map +1 -0
- package/dist/roap/request.js +88 -68
- package/dist/roap/request.js.map +1 -1
- package/dist/roap/turnDiscovery.js +72 -47
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/statsAnalyzer/index.js +3 -3
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/statsAnalyzer/mqaUtil.js +18 -6
- package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
- package/package.json +24 -19
- package/src/breakouts/README.md +190 -0
- package/src/breakouts/breakout.ts +110 -0
- package/src/breakouts/collection.ts +19 -0
- package/src/breakouts/index.ts +225 -0
- package/src/config.ts +4 -1
- package/src/constants.ts +39 -1
- package/src/locus-info/controlsUtils.ts +2 -0
- package/src/locus-info/index.ts +59 -1
- package/src/locus-info/parser.ts +1 -0
- package/src/locus-info/selfUtils.ts +8 -0
- package/src/media/index.ts +1 -2
- package/src/media/properties.ts +6 -9
- package/src/meeting/in-meeting-actions.ts +8 -0
- package/src/meeting/index.ts +360 -111
- package/src/meeting/request.ts +9 -31
- package/src/meeting/request.type.ts +2 -0
- package/src/meeting/util.ts +25 -60
- package/src/meeting-info/meeting-info-v2.ts +2 -0
- package/src/meetings/index.ts +10 -5
- package/src/meetings/request.ts +1 -1
- package/src/member/index.ts +9 -0
- package/src/member/util.ts +14 -1
- package/src/members/index.ts +1 -0
- package/src/members/request.ts +1 -0
- package/src/multistream/mediaRequestManager.ts +79 -15
- package/src/multistream/multistreamMedia.ts +4 -0
- package/src/multistream/receiveSlot.ts +17 -12
- package/src/multistream/receiveSlotManager.ts +22 -21
- package/src/multistream/remoteMedia.ts +1 -1
- package/src/multistream/remoteMediaGroup.ts +2 -2
- package/src/multistream/remoteMediaManager.ts +150 -37
- package/src/reachability/index.ts +16 -13
- package/src/reactions/constants.ts +4 -0
- package/src/reactions/reactions.type.ts +25 -0
- package/src/reconnection-manager/index.ts +18 -9
- package/src/recording-controller/enums.ts +8 -0
- package/src/recording-controller/index.ts +315 -0
- package/src/recording-controller/util.ts +58 -0
- package/src/roap/request.ts +78 -73
- package/src/roap/turnDiscovery.ts +8 -6
- package/src/statsAnalyzer/index.ts +4 -4
- package/src/statsAnalyzer/mqaUtil.ts +6 -0
- package/test/unit/spec/breakouts/breakout.ts +119 -0
- package/test/unit/spec/breakouts/collection.ts +15 -0
- package/test/unit/spec/breakouts/index.ts +293 -0
- package/test/unit/spec/locus-info/controlsUtils.js +20 -0
- package/test/unit/spec/locus-info/index.js +103 -0
- package/test/unit/spec/locus-info/selfConstant.js +25 -0
- package/test/unit/spec/locus-info/selfUtils.js +84 -0
- package/test/unit/spec/media/index.ts +1 -1
- package/test/unit/spec/media/properties.ts +9 -9
- package/test/unit/spec/meeting/effectsState.js +5 -1
- package/test/unit/spec/meeting/in-meeting-actions.ts +5 -1
- package/test/unit/spec/meeting/index.js +241 -50
- package/test/unit/spec/meeting/request.js +17 -0
- package/test/unit/spec/meeting/utils.js +28 -122
- package/test/unit/spec/meetings/index.js +1 -0
- package/test/unit/spec/member/util.js +26 -1
- package/test/unit/spec/multistream/mediaRequestManager.ts +312 -50
- package/test/unit/spec/multistream/receiveSlot.ts +6 -6
- package/test/unit/spec/multistream/receiveSlotManager.ts +13 -13
- package/test/unit/spec/multistream/remoteMedia.ts +2 -2
- package/test/unit/spec/multistream/remoteMediaGroup.ts +5 -5
- package/test/unit/spec/multistream/remoteMediaManager.ts +354 -65
- package/test/unit/spec/reachability/index.ts +58 -24
- package/test/unit/spec/reconnection-manager/index.js +42 -13
- package/test/unit/spec/recording-controller/index.js +231 -0
- package/test/unit/spec/recording-controller/util.js +102 -0
- package/test/unit/spec/roap/index.ts +2 -1
- package/test/unit/spec/roap/request.ts +114 -0
- package/test/unit/spec/roap/turnDiscovery.ts +45 -29
- package/test/unit/spec/stats-analyzer/index.js +2 -2
- package/test/utils/webex-test-users.js +1 -0
- package/tsconfig.json +6 -0
- package/dist/media/internal-media-core-wrapper.js +0 -18
- package/dist/media/internal-media-core-wrapper.js.map +0 -1
- package/src/media/internal-media-core-wrapper.ts +0 -9
|
@@ -1,50 +1,84 @@
|
|
|
1
1
|
import {assert} from '@webex/test-helper-chai';
|
|
2
|
+
import MockWebex from '@webex/test-helper-mock-webex';
|
|
3
|
+
import sinon from 'sinon';
|
|
2
4
|
import Reachability from '@webex/plugin-meetings/src/reachability/';
|
|
3
5
|
|
|
4
6
|
describe('isAnyClusterReachable', () => {
|
|
5
|
-
|
|
6
|
-
this.jsdom = require('jsdom-global')('', {url: 'http://localhost'});
|
|
7
|
-
});
|
|
8
|
-
after(function () {
|
|
9
|
-
this.jsdom();
|
|
10
|
-
});
|
|
7
|
+
let webex;
|
|
11
8
|
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
webex = new MockWebex();
|
|
14
11
|
});
|
|
15
12
|
|
|
16
|
-
const checkIsClusterReachable = (mockStorage: any, expectedValue: boolean) => {
|
|
13
|
+
const checkIsClusterReachable = async (mockStorage: any, expectedValue: boolean) => {
|
|
17
14
|
if (mockStorage) {
|
|
18
|
-
|
|
15
|
+
await webex.boundedStorage.put(
|
|
16
|
+
'Reachability',
|
|
17
|
+
'reachability.result',
|
|
18
|
+
JSON.stringify(mockStorage)
|
|
19
|
+
);
|
|
19
20
|
}
|
|
20
|
-
const reachability = new Reachability(
|
|
21
|
+
const reachability = new Reachability(webex);
|
|
21
22
|
|
|
22
|
-
const result = reachability.isAnyClusterReachable();
|
|
23
|
+
const result = await reachability.isAnyClusterReachable();
|
|
23
24
|
|
|
24
25
|
assert.equal(result, expectedValue);
|
|
25
26
|
};
|
|
26
27
|
|
|
27
|
-
it('returns true when udp is reachable', () => {
|
|
28
|
-
checkIsClusterReachable({x: {udp: {reachable: 'true'}, tcp: {reachable: 'false'}}}, true);
|
|
28
|
+
it('returns true when udp is reachable', async () => {
|
|
29
|
+
await checkIsClusterReachable({x: {udp: {reachable: 'true'}, tcp: {reachable: 'false'}}}, true);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('returns true when tcp is reachable', async () => {
|
|
33
|
+
await checkIsClusterReachable({x: {udp: {reachable: 'false'}, tcp: {reachable: 'true'}}}, true);
|
|
29
34
|
});
|
|
30
35
|
|
|
31
|
-
it('returns true when tcp
|
|
32
|
-
checkIsClusterReachable({x: {udp: {reachable: '
|
|
36
|
+
it('returns true when both tcp and udp are reachable', async () => {
|
|
37
|
+
await checkIsClusterReachable({x: {udp: {reachable: 'true'}, tcp: {reachable: 'true'}}}, true);
|
|
33
38
|
});
|
|
34
39
|
|
|
35
|
-
it('returns
|
|
36
|
-
checkIsClusterReachable({x: {udp: {reachable: '
|
|
40
|
+
it('returns false when both tcp and udp are unreachable', async () => {
|
|
41
|
+
await checkIsClusterReachable({x: {udp: {reachable: 'false'}, tcp: {reachable: 'false'}}}, false);
|
|
37
42
|
});
|
|
38
43
|
|
|
39
|
-
it('returns false when
|
|
40
|
-
checkIsClusterReachable({x: {
|
|
44
|
+
it('returns false when reachability result is empty', async () => {
|
|
45
|
+
await checkIsClusterReachable({x: {}}, false);
|
|
41
46
|
});
|
|
42
47
|
|
|
43
|
-
it('returns false when reachability
|
|
44
|
-
checkIsClusterReachable(
|
|
48
|
+
it('returns false when reachability.result item is not there', async () => {
|
|
49
|
+
await checkIsClusterReachable(undefined, false);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('gatherReachability', () => {
|
|
54
|
+
let webex;
|
|
55
|
+
|
|
56
|
+
beforeEach(async () => {
|
|
57
|
+
webex = new MockWebex();
|
|
58
|
+
|
|
59
|
+
await webex.boundedStorage.put(
|
|
60
|
+
'Reachability',
|
|
61
|
+
'reachability.result',
|
|
62
|
+
JSON.stringify({old: 'results'})
|
|
63
|
+
);
|
|
45
64
|
});
|
|
46
65
|
|
|
47
|
-
it('
|
|
48
|
-
|
|
66
|
+
it('stores the reachability', async () => {
|
|
67
|
+
const reachability = new Reachability(webex);
|
|
68
|
+
|
|
69
|
+
const clusters = {some: 'clusters'};
|
|
70
|
+
const reachabilityResults = {some: 'results'};
|
|
71
|
+
|
|
72
|
+
reachability.reachabilityRequest.getClusters = sinon.stub().returns(clusters);
|
|
73
|
+
(reachability as any).performReachabilityCheck = sinon.stub().returns(reachabilityResults)
|
|
74
|
+
|
|
75
|
+
const result = await reachability.gatherReachability();
|
|
76
|
+
|
|
77
|
+
assert.equal(result, reachabilityResults);
|
|
78
|
+
|
|
79
|
+
const storedResult = await webex.boundedStorage.get('Reachability', 'reachability.result');
|
|
80
|
+
|
|
81
|
+
assert.equal(JSON.stringify(result), storedResult);
|
|
49
82
|
});
|
|
83
|
+
|
|
50
84
|
});
|
|
@@ -12,13 +12,16 @@ sinon.assert.expose(chai.assert, {prefix: ''});
|
|
|
12
12
|
|
|
13
13
|
describe('plugin-meetings', () => {
|
|
14
14
|
describe('ReconnectionManager.reconnect', () => {
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
let fakeMediaConnection;
|
|
16
|
+
let fakeMeeting;
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
Metrics.postEvent = sinon.stub();
|
|
20
|
+
fakeMediaConnection = {
|
|
19
21
|
initiateOffer: sinon.stub().resolves({}),
|
|
22
|
+
reconnect: sinon.stub().resolves({}),
|
|
20
23
|
};
|
|
21
|
-
|
|
24
|
+
fakeMeeting = {
|
|
22
25
|
closePeerConnections: sinon.stub().resolves({}),
|
|
23
26
|
createMediaConnection: sinon.stub().returns(fakeMediaConnection),
|
|
24
27
|
config: {
|
|
@@ -37,6 +40,11 @@ describe('plugin-meetings', () => {
|
|
|
37
40
|
},
|
|
38
41
|
mediaProperties: {
|
|
39
42
|
unsetPeerConnection: sinon.stub(),
|
|
43
|
+
webrtcMediaConnection: fakeMediaConnection,
|
|
44
|
+
},
|
|
45
|
+
mediaRequestManagers: {
|
|
46
|
+
audio: {commit: sinon.stub()},
|
|
47
|
+
video: {commit: sinon.stub()},
|
|
40
48
|
},
|
|
41
49
|
roap: {
|
|
42
50
|
doTurnDiscovery: sinon.stub().resolves({
|
|
@@ -58,21 +66,42 @@ describe('plugin-meetings', () => {
|
|
|
58
66
|
},
|
|
59
67
|
},
|
|
60
68
|
};
|
|
69
|
+
});
|
|
61
70
|
|
|
71
|
+
it('uses correct TURN TLS information on the reconnection', async () => {
|
|
62
72
|
const rm = new ReconnectionManager(fakeMeeting);
|
|
63
73
|
|
|
64
74
|
await rm.reconnect();
|
|
65
75
|
|
|
66
|
-
assert.calledOnce(fakeMeeting.closePeerConnections);
|
|
67
|
-
assert.calledOnce(fakeMeeting.mediaProperties.unsetPeerConnection);
|
|
68
76
|
assert.calledOnce(fakeMeeting.roap.doTurnDiscovery);
|
|
69
|
-
assert.calledOnce(
|
|
70
|
-
assert.calledWith(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
assert.calledOnce(fakeMediaConnection.reconnect);
|
|
78
|
+
assert.calledWith(fakeMediaConnection.reconnect, [
|
|
79
|
+
{
|
|
80
|
+
urls: 'fake_turn_url',
|
|
81
|
+
username: 'fake_turn_username',
|
|
82
|
+
credential: 'fake_turn_password',
|
|
83
|
+
},
|
|
84
|
+
]);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('does not re-request media for non-multistream meetings', async () => {
|
|
88
|
+
fakeMeeting.isMultistream = false;
|
|
89
|
+
const rm = new ReconnectionManager(fakeMeeting);
|
|
90
|
+
|
|
91
|
+
await rm.reconnect();
|
|
92
|
+
|
|
93
|
+
assert.notCalled(fakeMeeting.mediaRequestManagers.audio.commit);
|
|
94
|
+
assert.notCalled(fakeMeeting.mediaRequestManagers.video.commit);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('does re-request media for multistream meetings', async () => {
|
|
98
|
+
fakeMeeting.isMultistream = true;
|
|
99
|
+
const rm = new ReconnectionManager(fakeMeeting);
|
|
100
|
+
|
|
101
|
+
await rm.reconnect();
|
|
102
|
+
|
|
103
|
+
assert.calledOnce(fakeMeeting.mediaRequestManagers.audio.commit);
|
|
104
|
+
assert.calledOnce(fakeMeeting.mediaRequestManagers.video.commit);
|
|
76
105
|
});
|
|
77
106
|
});
|
|
78
107
|
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import RecordingController from '@webex/plugin-meetings/src/recording-controller';
|
|
2
|
+
import sinon from 'sinon';
|
|
3
|
+
import {assert} from '@webex/test-helper-chai';
|
|
4
|
+
import { HTTP_VERBS } from '@webex/plugin-meetings/src/constants';
|
|
5
|
+
|
|
6
|
+
describe('plugin-meetings', () => {
|
|
7
|
+
describe('recording-controller tests', () => {
|
|
8
|
+
describe('index', () => {
|
|
9
|
+
let request;
|
|
10
|
+
|
|
11
|
+
describe('class tests', () => {
|
|
12
|
+
it('can set and extract new values later on', () => {
|
|
13
|
+
const controller = new RecordingController({});
|
|
14
|
+
assert.isUndefined(controller.getServiceUrl());
|
|
15
|
+
assert.isUndefined(controller.getSessionId());
|
|
16
|
+
assert.isUndefined(controller.getLocusUrl());
|
|
17
|
+
assert.isUndefined(controller.getLocusId());
|
|
18
|
+
controller.set({
|
|
19
|
+
serviceUrl: 'test',
|
|
20
|
+
sessionId: 'testId',
|
|
21
|
+
locusUrl: 'test/id',
|
|
22
|
+
displayHints: [],
|
|
23
|
+
})
|
|
24
|
+
assert(controller.getServiceUrl(), 'test');
|
|
25
|
+
assert(controller.getSessionId(), 'testId');
|
|
26
|
+
assert(controller.getLocusUrl(), 'test/id');
|
|
27
|
+
assert(controller.getLocusId(), 'id');
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
describe('legacy locus style recording', () => {
|
|
33
|
+
const locusUrl = 'locusUrl';
|
|
34
|
+
let controller;
|
|
35
|
+
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
request = {
|
|
38
|
+
request: sinon.stub().returns(Promise.resolve()),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
controller = new RecordingController(request);
|
|
42
|
+
|
|
43
|
+
controller.set({
|
|
44
|
+
locusUrl,
|
|
45
|
+
displayHints: [],
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('startRecording', () => {
|
|
51
|
+
it('rejects when correct display hint is not present', () => {
|
|
52
|
+
const result = controller.startRecording();
|
|
53
|
+
|
|
54
|
+
assert.notCalled(request.request);
|
|
55
|
+
|
|
56
|
+
assert.isRejected(result);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('can start recording when the correct display hint is present', () => {
|
|
60
|
+
controller.setDisplayHints(['RECORDING_CONTROL_START']);
|
|
61
|
+
|
|
62
|
+
const result = controller.startRecording();
|
|
63
|
+
|
|
64
|
+
assert.calledWith(request.request, {uri: `${locusUrl}/controls`, body: {record: {recording: true, paused: false}}, method: HTTP_VERBS.PATCH});
|
|
65
|
+
|
|
66
|
+
assert.deepEqual(result, request.request.firstCall.returnValue);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('stopRecording', () => {
|
|
71
|
+
it('rejects when correct display hint is not present', () => {
|
|
72
|
+
const result = controller.stopRecording();
|
|
73
|
+
|
|
74
|
+
assert.notCalled(request.request);
|
|
75
|
+
|
|
76
|
+
assert.isRejected(result);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('can stop recording when the correct display hint is present', () => {
|
|
80
|
+
controller.setDisplayHints(['RECORDING_CONTROL_STOP']);
|
|
81
|
+
|
|
82
|
+
const result = controller.stopRecording();
|
|
83
|
+
|
|
84
|
+
assert.calledWith(request.request, {uri: `${locusUrl}/controls`, body: {record: {recording: false, paused: false}}, method: HTTP_VERBS.PATCH});
|
|
85
|
+
|
|
86
|
+
assert.deepEqual(result, request.request.firstCall.returnValue);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('pauseRecording', () => {
|
|
91
|
+
it('rejects when correct display hint is not present', () => {
|
|
92
|
+
const result = controller.pauseRecording();
|
|
93
|
+
|
|
94
|
+
assert.notCalled(request.request);
|
|
95
|
+
|
|
96
|
+
assert.isRejected(result);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('can pause recording when the correct display hint is present', () => {
|
|
100
|
+
controller.setDisplayHints(['RECORDING_CONTROL_PAUSE']);
|
|
101
|
+
|
|
102
|
+
const result = controller.pauseRecording();
|
|
103
|
+
|
|
104
|
+
assert.calledWith(request.request, {uri: `${locusUrl}/controls`, body: {record: {recording: true, paused: true}}, method: HTTP_VERBS.PATCH});
|
|
105
|
+
|
|
106
|
+
assert.deepEqual(result, request.request.firstCall.returnValue);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('resumeRecording', () => {
|
|
111
|
+
it('rejects when correct display hint is not present', () => {
|
|
112
|
+
const result = controller.pauseRecording();
|
|
113
|
+
|
|
114
|
+
assert.notCalled(request.request);
|
|
115
|
+
|
|
116
|
+
assert.isRejected(result);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('can resume recording when the correct display hint is present', () => {
|
|
120
|
+
controller.setDisplayHints(['RECORDING_CONTROL_RESUME']);
|
|
121
|
+
|
|
122
|
+
const result = controller.resumeRecording();
|
|
123
|
+
|
|
124
|
+
assert.calledWith(request.request, {uri: `${locusUrl}/controls`, body: {record: {recording: true, paused: false}}, method: HTTP_VERBS.PATCH});
|
|
125
|
+
|
|
126
|
+
assert.deepEqual(result, request.request.firstCall.returnValue);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('recording streaming service style tests', () => {
|
|
132
|
+
let controller;
|
|
133
|
+
|
|
134
|
+
beforeEach(() => {
|
|
135
|
+
request = {
|
|
136
|
+
request: sinon.stub().returns(Promise.resolve()),
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
controller = new RecordingController(request);
|
|
140
|
+
|
|
141
|
+
controller.set({
|
|
142
|
+
serviceUrl: 'test',
|
|
143
|
+
sessionId: 'testId',
|
|
144
|
+
locusUrl: 'test/id',
|
|
145
|
+
displayHints: [],
|
|
146
|
+
})
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe('startRecording', () => {
|
|
150
|
+
it('rejects when correct display hint is not present', () => {
|
|
151
|
+
const result = controller.startRecording();
|
|
152
|
+
|
|
153
|
+
assert.notCalled(request.request);
|
|
154
|
+
|
|
155
|
+
assert.isRejected(result);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('can start recording when the correct display hint is present', () => {
|
|
159
|
+
controller.setDisplayHints(['RECORDING_CONTROL_START']);
|
|
160
|
+
|
|
161
|
+
const result = controller.startRecording();
|
|
162
|
+
|
|
163
|
+
assert.calledWith(request.request, {uri: `test/loci/id/recording`, body: {meetingInfo: {locusSessionId: 'testId'}, recording: {action: 'start'}}, method: HTTP_VERBS.PUT});
|
|
164
|
+
|
|
165
|
+
assert.deepEqual(result, request.request.firstCall.returnValue);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe('stopRecording', () => {
|
|
170
|
+
it('rejects when correct display hint is not present', () => {
|
|
171
|
+
const result = controller.pauseRecording();
|
|
172
|
+
|
|
173
|
+
assert.notCalled(request.request);
|
|
174
|
+
|
|
175
|
+
assert.isRejected(result);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('can start recording when the correct display hint is present', () => {
|
|
179
|
+
controller.setDisplayHints(['RECORDING_CONTROL_STOP']);
|
|
180
|
+
|
|
181
|
+
const result = controller.stopRecording();
|
|
182
|
+
|
|
183
|
+
assert.calledWith(request.request, {uri: `test/loci/id/recording`, body: {meetingInfo: {locusSessionId: 'testId'}, recording: {action: 'stop'}}, method: HTTP_VERBS.PUT});
|
|
184
|
+
|
|
185
|
+
assert.deepEqual(result, request.request.firstCall.returnValue);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe('pauseRecording', () => {
|
|
190
|
+
it('rejects when correct display hint is not present', () => {
|
|
191
|
+
const result = controller.pauseRecording();
|
|
192
|
+
|
|
193
|
+
assert.notCalled(request.request);
|
|
194
|
+
|
|
195
|
+
assert.isRejected(result);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('can pause recording when the correct display hint is present', () => {
|
|
199
|
+
controller.setDisplayHints(['RECORDING_CONTROL_PAUSE']);
|
|
200
|
+
|
|
201
|
+
const result = controller.pauseRecording();
|
|
202
|
+
|
|
203
|
+
assert.calledWith(request.request, {uri: `test/loci/id/recording`, body: {meetingInfo: {locusSessionId: 'testId'}, recording: {action: 'pause'}}, method: HTTP_VERBS.PUT});
|
|
204
|
+
|
|
205
|
+
assert.deepEqual(result, request.request.firstCall.returnValue);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
describe('resumeRecording', () => {
|
|
210
|
+
it('rejects when correct display hint is not present', () => {
|
|
211
|
+
const result = controller.resumeRecording();
|
|
212
|
+
|
|
213
|
+
assert.notCalled(request.request);
|
|
214
|
+
|
|
215
|
+
assert.isRejected(result);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('can resume recording when the correct display hint is present', () => {
|
|
219
|
+
controller.setDisplayHints(['RECORDING_CONTROL_RESUME']);
|
|
220
|
+
|
|
221
|
+
const result = controller.resumeRecording();
|
|
222
|
+
|
|
223
|
+
assert.calledWith(request.request, {uri: `test/loci/id/recording`, body: {meetingInfo: {locusSessionId: 'testId'}, recording: {action: 'resume'}}, method: HTTP_VERBS.PUT});
|
|
224
|
+
|
|
225
|
+
assert.deepEqual(result, request.request.firstCall.returnValue);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import RecordingUtil from '@webex/plugin-meetings/src/recording-controller/util';
|
|
2
|
+
import RecordingAction from '@webex/plugin-meetings/src/recording-controller/enums';
|
|
3
|
+
import { assert } from 'chai';
|
|
4
|
+
|
|
5
|
+
describe('plugin-meetings', () => {
|
|
6
|
+
describe('recording-controller tests', () => {
|
|
7
|
+
describe('recording util tests', () => {
|
|
8
|
+
|
|
9
|
+
let locusInfo;
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
locusInfo = {
|
|
13
|
+
parsedLocus: {
|
|
14
|
+
info: {
|
|
15
|
+
userDisplayHints: [],
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('canUserStart', () => {
|
|
22
|
+
it('can start recording when the correct display hint is present', () => {
|
|
23
|
+
locusInfo.parsedLocus.info.userDisplayHints.push('RECORDING_CONTROL_START');
|
|
24
|
+
|
|
25
|
+
assert.equal(RecordingUtil.canUserStart(locusInfo.parsedLocus.info.userDisplayHints), true);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('rejects when correct display hint is not present', () => {
|
|
29
|
+
assert.equal(RecordingUtil.canUserStart(locusInfo.parsedLocus.info.userDisplayHints), false);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('canUserPause', () => {
|
|
34
|
+
it('can pause recording when the correct display hint is present', () => {
|
|
35
|
+
locusInfo.parsedLocus.info.userDisplayHints.push('RECORDING_CONTROL_PAUSE');
|
|
36
|
+
|
|
37
|
+
assert.equal(RecordingUtil.canUserPause(locusInfo.parsedLocus.info.userDisplayHints), true);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('rejects when correct display hint is not present', () => {
|
|
41
|
+
assert.equal(RecordingUtil.canUserPause(locusInfo.parsedLocus.info.userDisplayHints), false);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('canUserStop', () => {
|
|
46
|
+
it('can stop recording when the correct display hint is present', () => {
|
|
47
|
+
locusInfo.parsedLocus.info.userDisplayHints.push('RECORDING_CONTROL_STOP');
|
|
48
|
+
|
|
49
|
+
assert.equal(RecordingUtil.canUserStop(locusInfo.parsedLocus.info.userDisplayHints), true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('rejects when correct display hint is not present', () => {
|
|
53
|
+
assert.equal(RecordingUtil.canUserStop(locusInfo.parsedLocus.info.userDisplayHints), false);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('canUserResume', () => {
|
|
58
|
+
it('can start recording when the correct display hint is present', () => {
|
|
59
|
+
locusInfo.parsedLocus.info.userDisplayHints.push('RECORDING_CONTROL_RESUME');
|
|
60
|
+
|
|
61
|
+
assert.equal(RecordingUtil.canUserResume(locusInfo.parsedLocus.info.userDisplayHints), true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('rejects when correct display hint is not present', () => {
|
|
65
|
+
assert.equal(RecordingUtil.canUserResume(locusInfo.parsedLocus.info.userDisplayHints), false);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe('deriveRecordingStates', () => {
|
|
70
|
+
it('gets the correct values for a start recording action', () => {
|
|
71
|
+
assert.deepEqual(RecordingUtil.deriveRecordingStates(RecordingAction.Start), {recording: true, paused: false});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('gets the correct values for a stop recording action', () => {
|
|
75
|
+
assert.deepEqual(RecordingUtil.deriveRecordingStates(RecordingAction.Stop), {recording: false, paused: false});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('gets the correct values for a resume recording action', () => {
|
|
79
|
+
assert.deepEqual(RecordingUtil.deriveRecordingStates(RecordingAction.Resume), {recording: true, paused: false});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('gets the correct values for a paused recording action', () => {
|
|
83
|
+
assert.deepEqual(RecordingUtil.deriveRecordingStates(RecordingAction.Pause), {recording: true, paused: true});
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('extractLocusId', () => {
|
|
88
|
+
it('gets the correct id from the url param', () => {
|
|
89
|
+
assert.equal(RecordingUtil.extractLocusId('test/id'), 'id');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('works with empty string parameters passed', () => {
|
|
93
|
+
assert.equal(RecordingUtil.extractLocusId(''), '');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('works with no parameters passed', () => {
|
|
97
|
+
assert.isUndefined(RecordingUtil.extractLocusId(undefined));
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -4,12 +4,13 @@ import TurnDiscovery from '@webex/plugin-meetings/src/roap/turnDiscovery';
|
|
|
4
4
|
|
|
5
5
|
import RoapRequest from '@webex/plugin-meetings/src/roap/request';
|
|
6
6
|
import Roap from '@webex/plugin-meetings/src/roap/';
|
|
7
|
+
import Meeting from '@webex/plugin-meetings/src/meeting';
|
|
7
8
|
|
|
8
9
|
describe('Roap', () => {
|
|
9
10
|
describe('doTurnDiscovery', () => {
|
|
10
11
|
it('calls this.turnDiscovery.doTurnDiscovery() and forwards all the arguments', async () => {
|
|
11
12
|
const RESULT = {something: 'some value'};
|
|
12
|
-
const meeting = {id: 'some meeting id'};
|
|
13
|
+
const meeting = {id: 'some meeting id'} as Meeting;
|
|
13
14
|
|
|
14
15
|
const doTurnDiscoveryStub = sinon
|
|
15
16
|
.stub(TurnDiscovery.prototype, 'doTurnDiscovery')
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import {assert} from '@webex/test-helper-chai';
|
|
2
|
+
import sinon from 'sinon';
|
|
3
|
+
import MockWebex from '@webex/test-helper-mock-webex';
|
|
4
|
+
import Metrics from '@webex/plugin-meetings/src/metrics';
|
|
5
|
+
|
|
6
|
+
import RoapRequest from '@webex/plugin-meetings/src/roap/request';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
describe('RoapRequest', () => {
|
|
10
|
+
describe('attachRechabilityData', () => {
|
|
11
|
+
let webex;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
webex = new MockWebex();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('attaches the reachability data when it exists', async () => {
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
const roapRequest = new RoapRequest({}, {parent: webex});
|
|
20
|
+
|
|
21
|
+
const sdp = {some: 'attribute'};
|
|
22
|
+
|
|
23
|
+
const reachabilitData = {reachability: 'data'};
|
|
24
|
+
|
|
25
|
+
await webex.boundedStorage.put(
|
|
26
|
+
'Reachability',
|
|
27
|
+
'reachability.result',
|
|
28
|
+
JSON.stringify(reachabilitData)
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const newSdp = await roapRequest.attachRechabilityData(sdp);
|
|
32
|
+
|
|
33
|
+
assert.deepEqual(newSdp, {
|
|
34
|
+
some: 'attribute',
|
|
35
|
+
reachability: reachabilitData
|
|
36
|
+
})
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('handles the case when realiability data does not exist', async () => {
|
|
40
|
+
// @ts-ignore
|
|
41
|
+
const roapRequest = new RoapRequest({}, {parent: webex});
|
|
42
|
+
|
|
43
|
+
const sdp = {some: 'attribute'};
|
|
44
|
+
|
|
45
|
+
const newSdp = await roapRequest.attachRechabilityData(sdp);
|
|
46
|
+
|
|
47
|
+
assert.deepEqual(newSdp, sdp);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('sendRoap', () => {
|
|
52
|
+
let webex;
|
|
53
|
+
|
|
54
|
+
beforeEach(() => {
|
|
55
|
+
webex = new MockWebex();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('calls attachReliabilityData', async () => {
|
|
59
|
+
Metrics.postEvent = sinon.stub();
|
|
60
|
+
|
|
61
|
+
// @ts-ignore
|
|
62
|
+
const roapRequest = new RoapRequest({}, {parent: webex});
|
|
63
|
+
|
|
64
|
+
const newSdp = {new: 'sdp'}
|
|
65
|
+
|
|
66
|
+
roapRequest.attachRechabilityData = sinon.stub().returns(Promise.resolve(newSdp));
|
|
67
|
+
webex.request.returns(Promise.resolve({
|
|
68
|
+
body: {
|
|
69
|
+
locus: {}
|
|
70
|
+
}
|
|
71
|
+
}))
|
|
72
|
+
|
|
73
|
+
const result = await roapRequest.sendRoap({
|
|
74
|
+
roapMessage: {seq: 1},
|
|
75
|
+
locusSelfUrl: 'locusSelfUrl',
|
|
76
|
+
mediaId: 'mediaId',
|
|
77
|
+
correlationId: 'correlationId',
|
|
78
|
+
audioMuted: true,
|
|
79
|
+
videoMuted: true,
|
|
80
|
+
meetingId: 'meetingId',
|
|
81
|
+
preferTranscoding: true
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
assert.calledOnceWithExactly(webex.request, {
|
|
85
|
+
uri: 'locusSelfUrl/media',
|
|
86
|
+
method: 'PUT',
|
|
87
|
+
body: {
|
|
88
|
+
device: {
|
|
89
|
+
url: undefined,
|
|
90
|
+
deviceType: undefined,
|
|
91
|
+
},
|
|
92
|
+
correlationId: 'correlationId',
|
|
93
|
+
localMedias: [{
|
|
94
|
+
localSdp: JSON.stringify(newSdp),
|
|
95
|
+
mediaId: 'mediaId'
|
|
96
|
+
}],
|
|
97
|
+
clientMediaPreferences: {preferTranscoding: true}
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
assert.calledOnceWithExactly(roapRequest.attachRechabilityData, {
|
|
102
|
+
roapMessage: {seq: 1},
|
|
103
|
+
audioMuted: true,
|
|
104
|
+
videoMuted: true
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
assert.deepEqual(result, {
|
|
108
|
+
locus: {
|
|
109
|
+
roapSeq: 1
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
})
|
|
114
|
+
});
|