@webex/plugin-meetings 3.11.0-next.4 → 3.11.0-next.41
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/aiEnableRequest/index.js +184 -0
- package/dist/aiEnableRequest/index.js.map +1 -0
- package/dist/aiEnableRequest/utils.js +36 -0
- package/dist/aiEnableRequest/utils.js.map +1 -0
- package/dist/annotation/index.js +3 -3
- package/dist/annotation/index.js.map +1 -1
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/config.js +5 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js +26 -6
- package/dist/constants.js.map +1 -1
- package/dist/hashTree/constants.js +3 -1
- package/dist/hashTree/constants.js.map +1 -1
- package/dist/hashTree/hashTree.js +18 -0
- package/dist/hashTree/hashTree.js.map +1 -1
- package/dist/hashTree/hashTreeParser.js +709 -380
- package/dist/hashTree/hashTreeParser.js.map +1 -1
- package/dist/hashTree/types.js +4 -2
- package/dist/hashTree/types.js.map +1 -1
- package/dist/hashTree/utils.js +10 -0
- package/dist/hashTree/utils.js.map +1 -1
- package/dist/index.js +11 -2
- package/dist/index.js.map +1 -1
- package/dist/interceptors/constant.js +12 -0
- package/dist/interceptors/constant.js.map +1 -0
- package/dist/interceptors/dataChannelAuthToken.js +233 -0
- package/dist/interceptors/dataChannelAuthToken.js.map +1 -0
- package/dist/interceptors/index.js +7 -0
- package/dist/interceptors/index.js.map +1 -1
- package/dist/interpretation/index.js +2 -2
- package/dist/interpretation/index.js.map +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/controlsUtils.js +5 -3
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +125 -68
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/selfUtils.js +1 -0
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/locus-info/types.js.map +1 -1
- package/dist/media/MediaConnectionAwaiter.js +57 -1
- package/dist/media/MediaConnectionAwaiter.js.map +1 -1
- package/dist/media/properties.js +4 -2
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +7 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +209 -90
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/request.js +50 -0
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meeting/util.js +128 -2
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +78 -36
- package/dist/meetings/index.js.map +1 -1
- package/dist/member/index.js +10 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/util.js +10 -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/multistream/mediaRequestManager.js +1 -1
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/remoteMediaManager.js +11 -0
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/reactions/reactions.type.js.map +1 -1
- package/dist/types/aiEnableRequest/index.d.ts +5 -0
- package/dist/types/aiEnableRequest/utils.d.ts +2 -0
- package/dist/types/config.d.ts +3 -0
- package/dist/types/constants.d.ts +21 -1
- package/dist/types/hashTree/constants.d.ts +1 -0
- package/dist/types/hashTree/hashTree.d.ts +7 -0
- package/dist/types/hashTree/hashTreeParser.d.ts +99 -14
- package/dist/types/hashTree/types.d.ts +3 -0
- package/dist/types/hashTree/utils.d.ts +6 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/interceptors/constant.d.ts +5 -0
- package/dist/types/interceptors/dataChannelAuthToken.d.ts +35 -0
- package/dist/types/interceptors/index.d.ts +2 -1
- package/dist/types/locus-info/index.d.ts +9 -2
- package/dist/types/locus-info/types.d.ts +1 -0
- package/dist/types/media/MediaConnectionAwaiter.d.ts +10 -1
- package/dist/types/media/properties.d.ts +2 -1
- package/dist/types/meeting/in-meeting-actions.d.ts +6 -0
- package/dist/types/meeting/index.d.ts +24 -2
- package/dist/types/meeting/request.d.ts +16 -1
- package/dist/types/meeting/request.type.d.ts +5 -0
- package/dist/types/meeting/util.d.ts +31 -0
- package/dist/types/meetings/index.d.ts +4 -2
- package/dist/types/member/index.d.ts +1 -0
- package/dist/types/member/util.d.ts +5 -0
- package/dist/types/metrics/constants.d.ts +1 -0
- package/dist/types/reactions/reactions.type.d.ts +1 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +22 -22
- package/src/aiEnableRequest/README.md +84 -0
- package/src/aiEnableRequest/index.ts +170 -0
- package/src/aiEnableRequest/utils.ts +25 -0
- package/src/annotation/index.ts +7 -4
- package/src/config.ts +3 -0
- package/src/constants.ts +26 -1
- package/src/hashTree/constants.ts +1 -0
- package/src/hashTree/hashTree.ts +17 -0
- package/src/hashTree/hashTreeParser.ts +627 -249
- package/src/hashTree/types.ts +4 -0
- package/src/hashTree/utils.ts +9 -0
- package/src/index.ts +8 -1
- package/src/interceptors/constant.ts +6 -0
- package/src/interceptors/dataChannelAuthToken.ts +142 -0
- package/src/interceptors/index.ts +2 -1
- package/src/interpretation/index.ts +2 -2
- package/src/locus-info/controlsUtils.ts +11 -0
- package/src/locus-info/index.ts +146 -58
- package/src/locus-info/selfUtils.ts +1 -0
- package/src/locus-info/types.ts +1 -0
- package/src/media/MediaConnectionAwaiter.ts +41 -1
- package/src/media/properties.ts +3 -1
- package/src/meeting/in-meeting-actions.ts +12 -0
- package/src/meeting/index.ts +127 -17
- package/src/meeting/request.ts +42 -0
- package/src/meeting/request.type.ts +6 -0
- package/src/meeting/util.ts +156 -1
- package/src/meetings/index.ts +94 -9
- package/src/member/index.ts +10 -0
- package/src/member/util.ts +12 -0
- package/src/metrics/constants.ts +1 -0
- package/src/multistream/mediaRequestManager.ts +1 -1
- package/src/multistream/remoteMediaManager.ts +13 -0
- package/src/reactions/reactions.type.ts +1 -0
- package/test/unit/spec/aiEnableRequest/index.ts +981 -0
- package/test/unit/spec/aiEnableRequest/utils.ts +130 -0
- package/test/unit/spec/hashTree/hashTree.ts +66 -0
- package/test/unit/spec/hashTree/hashTreeParser.ts +1869 -189
- package/test/unit/spec/interceptors/dataChannelAuthToken.ts +141 -0
- package/test/unit/spec/locus-info/controlsUtils.js +29 -0
- package/test/unit/spec/locus-info/index.js +201 -45
- package/test/unit/spec/media/MediaConnectionAwaiter.ts +41 -1
- package/test/unit/spec/media/properties.ts +12 -3
- package/test/unit/spec/meeting/in-meeting-actions.ts +8 -2
- package/test/unit/spec/meeting/index.js +441 -75
- package/test/unit/spec/meeting/request.js +64 -0
- package/test/unit/spec/meeting/utils.js +433 -22
- package/test/unit/spec/meetings/index.js +550 -10
- package/test/unit/spec/member/index.js +28 -4
- package/test/unit/spec/member/util.js +65 -27
- package/test/unit/spec/multistream/remoteMediaManager.ts +30 -0
|
@@ -82,6 +82,7 @@ import Mercury from '@webex/internal-plugin-mercury';
|
|
|
82
82
|
import Breakouts from '@webex/plugin-meetings/src/breakouts';
|
|
83
83
|
import SimultaneousInterpretation from '@webex/plugin-meetings/src/interpretation';
|
|
84
84
|
import Webinar from '@webex/plugin-meetings/src/webinar';
|
|
85
|
+
import AIEnableRequest from '@webex/plugin-meetings/src/aiEnableRequest';
|
|
85
86
|
import {REACTION_RELAY_TYPES} from '../../../../src/reactions/constants';
|
|
86
87
|
import locus from '../fixture/locus';
|
|
87
88
|
import {
|
|
@@ -123,7 +124,6 @@ import {EVENT_TRIGGERS as VOICEAEVENTS} from '@webex/internal-plugin-voicea';
|
|
|
123
124
|
import {createBrbState} from '@webex/plugin-meetings/src/meeting/brbState';
|
|
124
125
|
import JoinForbiddenError from '../../../../src/common/errors/join-forbidden-error';
|
|
125
126
|
import {EventEmitter} from 'stream';
|
|
126
|
-
|
|
127
127
|
describe('plugin-meetings', () => {
|
|
128
128
|
const logger = {
|
|
129
129
|
info: () => {},
|
|
@@ -265,6 +265,7 @@ describe('plugin-meetings', () => {
|
|
|
265
265
|
stopReachability: sinon.stub(),
|
|
266
266
|
isSubnetReachable: sinon.stub().returns(true),
|
|
267
267
|
};
|
|
268
|
+
webex.internal.llm.isDataChannelTokenEnabled = sinon.stub().resolves(false);
|
|
268
269
|
webex.internal.llm.on = sinon.stub();
|
|
269
270
|
webex.internal.newMetrics.callDiagnosticLatencies = new CallDiagnosticLatencies(
|
|
270
271
|
{},
|
|
@@ -376,6 +377,7 @@ describe('plugin-meetings', () => {
|
|
|
376
377
|
assert.instanceOf(meeting.breakouts, Breakouts);
|
|
377
378
|
assert.instanceOf(meeting.simultaneousInterpretation, SimultaneousInterpretation);
|
|
378
379
|
assert.instanceOf(meeting.webinar, Webinar);
|
|
380
|
+
assert.instanceOf(meeting.aiEnableRequest, AIEnableRequest);
|
|
379
381
|
});
|
|
380
382
|
|
|
381
383
|
it('should call the callback with the meeting that has id already set', () => {
|
|
@@ -738,7 +740,9 @@ describe('plugin-meetings', () => {
|
|
|
738
740
|
let supportsRTCPeerConnectionStub;
|
|
739
741
|
|
|
740
742
|
beforeEach(() => {
|
|
741
|
-
supportsRTCPeerConnectionStub = sinon
|
|
743
|
+
supportsRTCPeerConnectionStub = sinon
|
|
744
|
+
.stub(WebCapabilities, 'supportsRTCPeerConnection')
|
|
745
|
+
.returns(CapabilityState.CAPABLE);
|
|
742
746
|
|
|
743
747
|
meeting.join = sinon.stub().callsFake((joinOptions) => {
|
|
744
748
|
meeting.isMultistream = joinOptions.enableMultistream;
|
|
@@ -1011,33 +1015,53 @@ describe('plugin-meetings', () => {
|
|
|
1011
1015
|
);
|
|
1012
1016
|
});
|
|
1013
1017
|
|
|
1014
|
-
it('should call leave() if addMediaInternal() fails ', async () => {
|
|
1018
|
+
it('should call leave() if addMediaInternal() fails with a browser media error (TypeError)', async () => {
|
|
1015
1019
|
const addMediaError = new Error('fake addMedia error');
|
|
1016
|
-
addMediaError.name = 'TypeError';
|
|
1020
|
+
addMediaError.name = 'TypeError'; // This makes it a browser media error
|
|
1017
1021
|
|
|
1018
|
-
const
|
|
1019
|
-
|
|
1020
|
-
body: {
|
|
1021
|
-
errorCode: 2729,
|
|
1022
|
-
message: 'fake addMedia error',
|
|
1023
|
-
name: 'TypeError'
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
};
|
|
1027
|
-
meeting.addMediaInternal.rejects(addMediaError);
|
|
1028
|
-
sinon.stub(meeting, 'leave').resolves();
|
|
1022
|
+
const leaveStub = sinon.stub(meeting, 'leave').resolves();
|
|
1023
|
+
meeting.addMediaInternal = sinon.stub().rejects(addMediaError);
|
|
1029
1024
|
|
|
1030
|
-
|
|
1025
|
+
// When a browser media error occurs, it gets transformed into a special structure
|
|
1026
|
+
const rejectedError = await assert.isRejected(
|
|
1031
1027
|
meeting.joinWithMedia({
|
|
1032
1028
|
joinOptions,
|
|
1033
1029
|
mediaOptions,
|
|
1034
|
-
})
|
|
1035
|
-
rejectError
|
|
1030
|
+
})
|
|
1036
1031
|
);
|
|
1037
1032
|
|
|
1033
|
+
// Verify the error was transformed with errorCode 2729
|
|
1034
|
+
assert.equal(rejectedError.error.body.errorCode, 2729);
|
|
1035
|
+
assert.equal(rejectedError.error.body.message, 'fake addMedia error');
|
|
1036
|
+
assert.equal(rejectedError.error.body.name, 'TypeError');
|
|
1037
|
+
|
|
1038
1038
|
assert.calledOnce(meeting.join);
|
|
1039
1039
|
assert.calledOnce(meeting.addMediaInternal);
|
|
1040
|
+
assert.calledOnce(leaveStub);
|
|
1041
|
+
assert.calledOnceWithExactly(leaveStub, {
|
|
1042
|
+
resourceId: undefined,
|
|
1043
|
+
reason: 'joinWithMedia failure',
|
|
1044
|
+
});
|
|
1045
|
+
|
|
1046
|
+
// Browser media errors don't retry, so behavioral metric is sent only once
|
|
1047
|
+
// NOTE: The error gets transformed, so the metric receives undefined for message/stack/name
|
|
1048
|
+
// because they're now nested in error.body instead of at the top level
|
|
1040
1049
|
assert.calledOnce(Metrics.sendBehavioralMetric);
|
|
1050
|
+
assert.calledWith(
|
|
1051
|
+
Metrics.sendBehavioralMetric,
|
|
1052
|
+
BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
|
|
1053
|
+
{
|
|
1054
|
+
correlation_id: meeting.correlationId,
|
|
1055
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
|
1056
|
+
reason: undefined, // transformed error doesn't have .message at top level
|
|
1057
|
+
stack: undefined, // transformed error doesn't have .stack at top level
|
|
1058
|
+
leaveErrorReason: undefined,
|
|
1059
|
+
isRetry: false,
|
|
1060
|
+
},
|
|
1061
|
+
{
|
|
1062
|
+
type: undefined, // transformed error doesn't have .name at top level
|
|
1063
|
+
}
|
|
1064
|
+
);
|
|
1041
1065
|
});
|
|
1042
1066
|
|
|
1043
1067
|
it('should not call leave() if addMediaInternal() fails the first time and succeeds the second time and should only call join() once', async () => {
|
|
@@ -1249,8 +1273,14 @@ describe('plugin-meetings', () => {
|
|
|
1249
1273
|
});
|
|
1250
1274
|
|
|
1251
1275
|
[
|
|
1252
|
-
{
|
|
1253
|
-
|
|
1276
|
+
{
|
|
1277
|
+
errorName: 'SdpOfferCreationError',
|
|
1278
|
+
description: 'if we fail to create the offer on first attempt',
|
|
1279
|
+
},
|
|
1280
|
+
{
|
|
1281
|
+
errorName: 'WebrtcApiNotAvailableError',
|
|
1282
|
+
description: 'if RTCPeerConnection is not available',
|
|
1283
|
+
},
|
|
1254
1284
|
].forEach(({errorName, description}) => {
|
|
1255
1285
|
it(`should not attempt a retry ${description}`, async () => {
|
|
1256
1286
|
const addMediaError = new Error('fake addMedia error');
|
|
@@ -1290,7 +1320,7 @@ describe('plugin-meetings', () => {
|
|
|
1290
1320
|
resourceId: undefined,
|
|
1291
1321
|
reason: 'joinWithMedia failure',
|
|
1292
1322
|
});
|
|
1293
|
-
})
|
|
1323
|
+
});
|
|
1294
1324
|
});
|
|
1295
1325
|
|
|
1296
1326
|
it('should ignore sendVideo/receiveVideo when videoEnabled is false', async () => {
|
|
@@ -1882,6 +1912,53 @@ describe('plugin-meetings', () => {
|
|
|
1882
1912
|
fakeProcessedReaction
|
|
1883
1913
|
);
|
|
1884
1914
|
});
|
|
1915
|
+
|
|
1916
|
+
it('should process if participantId does not exist in membersCollection but has displayName in Webinar', () => {
|
|
1917
|
+
LoggerProxy.logger.warn = sinon.stub();
|
|
1918
|
+
meeting.isReactionsSupported = sinon.stub().returns(true);
|
|
1919
|
+
meeting.config.receiveReactions = true;
|
|
1920
|
+
meeting.locusInfo.info = {isWebinar: true};
|
|
1921
|
+
const fakeSendersName = 'Fake reactors name';
|
|
1922
|
+
const fakeReactionPayload = {
|
|
1923
|
+
type: 'fake_type',
|
|
1924
|
+
codepoints: 'fake_codepoints',
|
|
1925
|
+
shortcodes: 'fake_shortcodes',
|
|
1926
|
+
tone: {
|
|
1927
|
+
type: 'fake_tone_type',
|
|
1928
|
+
codepoints: 'fake_tone_codepoints',
|
|
1929
|
+
shortcodes: 'fake_tone_shortcodes',
|
|
1930
|
+
},
|
|
1931
|
+
};
|
|
1932
|
+
const fakeSenderPayload = {
|
|
1933
|
+
displayName: 'Fake reactors name',
|
|
1934
|
+
participantId: 'fake_participant_id',
|
|
1935
|
+
};
|
|
1936
|
+
const fakeProcessedReaction = {
|
|
1937
|
+
reaction: fakeReactionPayload,
|
|
1938
|
+
sender: {
|
|
1939
|
+
id: fakeSenderPayload.participantId,
|
|
1940
|
+
name: fakeSendersName,
|
|
1941
|
+
},
|
|
1942
|
+
};
|
|
1943
|
+
const fakeRelayEvent = {
|
|
1944
|
+
data: {
|
|
1945
|
+
relayType: REACTION_RELAY_TYPES.REACTION,
|
|
1946
|
+
reaction: fakeReactionPayload,
|
|
1947
|
+
sender: fakeSenderPayload,
|
|
1948
|
+
},
|
|
1949
|
+
};
|
|
1950
|
+
meeting.processRelayEvent(fakeRelayEvent);
|
|
1951
|
+
assert.calledWith(
|
|
1952
|
+
TriggerProxy.trigger,
|
|
1953
|
+
sinon.match.instanceOf(Meeting),
|
|
1954
|
+
{
|
|
1955
|
+
file: 'meeting/index',
|
|
1956
|
+
function: 'join',
|
|
1957
|
+
},
|
|
1958
|
+
EVENT_TRIGGERS.MEETING_RECEIVE_REACTIONS,
|
|
1959
|
+
fakeProcessedReaction
|
|
1960
|
+
);
|
|
1961
|
+
});
|
|
1885
1962
|
});
|
|
1886
1963
|
|
|
1887
1964
|
describe('#handleLLMOnline', () => {
|
|
@@ -9254,7 +9331,10 @@ describe('plugin-meetings', () => {
|
|
|
9254
9331
|
|
|
9255
9332
|
// check that the right things were called by the callback
|
|
9256
9333
|
assert.calledOnceWithExactly(meeting.waitForRemoteSDPAnswer);
|
|
9257
|
-
assert.calledOnceWithExactly(
|
|
9334
|
+
assert.calledOnceWithExactly(
|
|
9335
|
+
meeting.mediaProperties.waitForMediaConnectionConnected,
|
|
9336
|
+
meeting.correlationId
|
|
9337
|
+
);
|
|
9258
9338
|
});
|
|
9259
9339
|
});
|
|
9260
9340
|
|
|
@@ -10428,6 +10508,21 @@ describe('plugin-meetings', () => {
|
|
|
10428
10508
|
EVENT_TRIGGERS.MEETING_INTERPRETATION_UPDATE
|
|
10429
10509
|
);
|
|
10430
10510
|
});
|
|
10511
|
+
|
|
10512
|
+
it('listens to the self id changed event and updates aiEnableRequest', () => {
|
|
10513
|
+
meeting.aiEnableRequest = {
|
|
10514
|
+
selfParticipantIdUpdate: sinon.stub(),
|
|
10515
|
+
};
|
|
10516
|
+
|
|
10517
|
+
const payload = {selfId: 'participant-test-123'};
|
|
10518
|
+
|
|
10519
|
+
meeting.locusInfo.emit({function: 'test', file: 'test'}, 'SELF_ID_CHANGED', payload);
|
|
10520
|
+
|
|
10521
|
+
assert.calledOnceWithExactly(
|
|
10522
|
+
meeting.aiEnableRequest.selfParticipantIdUpdate,
|
|
10523
|
+
payload.selfId
|
|
10524
|
+
);
|
|
10525
|
+
});
|
|
10431
10526
|
});
|
|
10432
10527
|
|
|
10433
10528
|
describe('#setUpBreakoutsListener', () => {
|
|
@@ -10675,6 +10770,24 @@ describe('plugin-meetings', () => {
|
|
|
10675
10770
|
);
|
|
10676
10771
|
});
|
|
10677
10772
|
|
|
10773
|
+
it('listens to MEETING_CONTROLS_AI_SUMMARY_NOTIFICATION_UPDATED', async () => {
|
|
10774
|
+
const aiSummaryNotification = {example: 'value'};
|
|
10775
|
+
|
|
10776
|
+
await meeting.locusInfo.emitScoped(
|
|
10777
|
+
{function: 'test', file: 'test'},
|
|
10778
|
+
LOCUSINFO.EVENTS.CONTROLS_AI_SUMMARY_NOTIFICATION_UPDATED,
|
|
10779
|
+
{aiSummaryNotification}
|
|
10780
|
+
);
|
|
10781
|
+
|
|
10782
|
+
assert.calledWith(
|
|
10783
|
+
TriggerProxy.trigger,
|
|
10784
|
+
meeting,
|
|
10785
|
+
{file: 'meeting/index', function: 'setupLocusControlsListener'},
|
|
10786
|
+
EVENT_TRIGGERS.MEETING_CONTROLS_AI_SUMMARY_NOTIFICATION_UPDATED,
|
|
10787
|
+
{aiSummaryNotification}
|
|
10788
|
+
);
|
|
10789
|
+
});
|
|
10790
|
+
|
|
10678
10791
|
it('listens to MEETING_CONTROLS_MEETING_FULL_UPDATED', async () => {
|
|
10679
10792
|
const state = {example: 'value'};
|
|
10680
10793
|
|
|
@@ -10947,6 +11060,9 @@ describe('plugin-meetings', () => {
|
|
|
10947
11060
|
meeting.simultaneousInterpretation = {
|
|
10948
11061
|
approvalUrlUpdate: sinon.stub().returns(undefined),
|
|
10949
11062
|
};
|
|
11063
|
+
meeting.aiEnableRequest = {
|
|
11064
|
+
approvalUrlUpdate: sinon.stub().returns(undefined),
|
|
11065
|
+
};
|
|
10950
11066
|
|
|
10951
11067
|
meeting.locusInfo.emit(
|
|
10952
11068
|
{function: 'test', file: 'test'},
|
|
@@ -10966,6 +11082,10 @@ describe('plugin-meetings', () => {
|
|
|
10966
11082
|
meeting.simultaneousInterpretation.approvalUrlUpdate,
|
|
10967
11083
|
newLocusServices.services.approval.url
|
|
10968
11084
|
);
|
|
11085
|
+
assert.calledWith(
|
|
11086
|
+
meeting.aiEnableRequest.approvalUrlUpdate,
|
|
11087
|
+
newLocusServices.services.approval.url
|
|
11088
|
+
);
|
|
10969
11089
|
assert.calledOnce(meeting.recordingController.setSessionId);
|
|
10970
11090
|
done();
|
|
10971
11091
|
});
|
|
@@ -11371,6 +11491,41 @@ describe('plugin-meetings', () => {
|
|
|
11371
11491
|
});
|
|
11372
11492
|
});
|
|
11373
11493
|
|
|
11494
|
+
describe('localConstraintsChangeHandler', () => {
|
|
11495
|
+
it('calls updatePreferredBitrateKbps when not multistream', () => {
|
|
11496
|
+
meeting.isMultistream = false;
|
|
11497
|
+
meeting.mediaProperties.webrtcMediaConnection = {
|
|
11498
|
+
updatePreferredBitrateKbps: sinon.stub(),
|
|
11499
|
+
};
|
|
11500
|
+
|
|
11501
|
+
meeting.localConstraintsChangeHandler();
|
|
11502
|
+
|
|
11503
|
+
assert.calledOnce(
|
|
11504
|
+
meeting.mediaProperties.webrtcMediaConnection.updatePreferredBitrateKbps
|
|
11505
|
+
);
|
|
11506
|
+
});
|
|
11507
|
+
|
|
11508
|
+
it('does not call updatePreferredBitrateKbps when multistream', () => {
|
|
11509
|
+
meeting.isMultistream = true;
|
|
11510
|
+
meeting.mediaProperties.webrtcMediaConnection = {
|
|
11511
|
+
updatePreferredBitrateKbps: sinon.stub(),
|
|
11512
|
+
};
|
|
11513
|
+
|
|
11514
|
+
meeting.localConstraintsChangeHandler();
|
|
11515
|
+
|
|
11516
|
+
assert.notCalled(
|
|
11517
|
+
meeting.mediaProperties.webrtcMediaConnection.updatePreferredBitrateKbps
|
|
11518
|
+
);
|
|
11519
|
+
});
|
|
11520
|
+
|
|
11521
|
+
it('does not throw when webrtcMediaConnection is undefined', () => {
|
|
11522
|
+
meeting.isMultistream = false;
|
|
11523
|
+
meeting.mediaProperties.webrtcMediaConnection = undefined;
|
|
11524
|
+
|
|
11525
|
+
assert.doesNotThrow(() => meeting.localConstraintsChangeHandler());
|
|
11526
|
+
});
|
|
11527
|
+
});
|
|
11528
|
+
|
|
11374
11529
|
describe('#parseMeetingInfo', () => {
|
|
11375
11530
|
const checkParseMeetingInfo = (expectedInfoToParse) => {
|
|
11376
11531
|
assert.equal(meeting.conversationUrl, expectedInfoToParse.conversationUrl);
|
|
@@ -11750,6 +11905,7 @@ describe('plugin-meetings', () => {
|
|
|
11750
11905
|
let canUnsetDisallowUnmuteSpy;
|
|
11751
11906
|
let canUserRaiseHandSpy;
|
|
11752
11907
|
let bothLeaveAndEndMeetingAvailableSpy;
|
|
11908
|
+
let requireHostEndMeetingBeforeLeaveSpy;
|
|
11753
11909
|
let canUserLowerAllHandsSpy;
|
|
11754
11910
|
let canUserLowerSomeoneElsesHandSpy;
|
|
11755
11911
|
let waitingForOthersToJoinSpy;
|
|
@@ -11761,6 +11917,8 @@ describe('plugin-meetings', () => {
|
|
|
11761
11917
|
let canMoveToLobbySpy;
|
|
11762
11918
|
let isSpokenLanguageAutoDetectionEnabledSpy;
|
|
11763
11919
|
let showAutoEndMeetingWarningSpy;
|
|
11920
|
+
let canAttendeeRequestAiAssistantEnabledSpy;
|
|
11921
|
+
let attendeeRequestAiAssistantDeclinedAllSpy;
|
|
11764
11922
|
// Due to import tree issues, hasHints must be stubed within the scope of the `it`.
|
|
11765
11923
|
|
|
11766
11924
|
beforeEach(() => {
|
|
@@ -11781,6 +11939,10 @@ describe('plugin-meetings', () => {
|
|
|
11781
11939
|
MeetingUtil,
|
|
11782
11940
|
'bothLeaveAndEndMeetingAvailable'
|
|
11783
11941
|
);
|
|
11942
|
+
requireHostEndMeetingBeforeLeaveSpy = sinon.spy(
|
|
11943
|
+
MeetingUtil,
|
|
11944
|
+
'requireHostEndMeetingBeforeLeave'
|
|
11945
|
+
);
|
|
11784
11946
|
canUserLowerSomeoneElsesHandSpy = sinon.spy(MeetingUtil, 'canUserLowerSomeoneElsesHand');
|
|
11785
11947
|
waitingForOthersToJoinSpy = sinon.spy(MeetingUtil, 'waitingForOthersToJoin');
|
|
11786
11948
|
canSendReactionsSpy = sinon.spy(MeetingUtil, 'canSendReactions');
|
|
@@ -11797,12 +11959,22 @@ describe('plugin-meetings', () => {
|
|
|
11797
11959
|
MeetingUtil,
|
|
11798
11960
|
'isSpokenLanguageAutoDetectionEnabled'
|
|
11799
11961
|
);
|
|
11962
|
+
canAttendeeRequestAiAssistantEnabledSpy = sinon.spy(
|
|
11963
|
+
MeetingUtil,
|
|
11964
|
+
'canAttendeeRequestAiAssistantEnabled'
|
|
11965
|
+
);
|
|
11966
|
+
attendeeRequestAiAssistantDeclinedAllSpy = sinon.spy(
|
|
11967
|
+
MeetingUtil,
|
|
11968
|
+
'attendeeRequestAiAssistantDeclinedAll'
|
|
11969
|
+
);
|
|
11800
11970
|
});
|
|
11801
11971
|
|
|
11802
11972
|
afterEach(() => {
|
|
11803
11973
|
inMeetingActionsSetSpy.restore();
|
|
11804
11974
|
waitingForOthersToJoinSpy.restore();
|
|
11805
11975
|
showAutoEndMeetingWarningSpy.restore();
|
|
11976
|
+
canAttendeeRequestAiAssistantEnabledSpy.restore();
|
|
11977
|
+
attendeeRequestAiAssistantDeclinedAllSpy.restore();
|
|
11806
11978
|
});
|
|
11807
11979
|
|
|
11808
11980
|
forEach(
|
|
@@ -12326,6 +12498,7 @@ describe('plugin-meetings', () => {
|
|
|
12326
12498
|
const userDisplayHints = ['LOCK_CONTROL_UNLOCK'];
|
|
12327
12499
|
meeting.userDisplayHints = ['LOCK_CONTROL_UNLOCK'];
|
|
12328
12500
|
meeting.meetingInfo.supportVoIP = true;
|
|
12501
|
+
meeting.roles = [];
|
|
12329
12502
|
|
|
12330
12503
|
meeting.updateMeetingActions();
|
|
12331
12504
|
|
|
@@ -12341,6 +12514,7 @@ describe('plugin-meetings', () => {
|
|
|
12341
12514
|
assert.calledWith(canUnsetDisallowUnmuteSpy, userDisplayHints);
|
|
12342
12515
|
assert.calledWith(canUserRaiseHandSpy, userDisplayHints);
|
|
12343
12516
|
assert.calledWith(bothLeaveAndEndMeetingAvailableSpy, userDisplayHints);
|
|
12517
|
+
assert.calledWith(requireHostEndMeetingBeforeLeaveSpy, userDisplayHints);
|
|
12344
12518
|
assert.calledWith(canUserLowerAllHandsSpy, userDisplayHints);
|
|
12345
12519
|
assert.calledWith(canUserLowerSomeoneElsesHandSpy, userDisplayHints);
|
|
12346
12520
|
assert.calledWith(waitingForOthersToJoinSpy, userDisplayHints);
|
|
@@ -12352,6 +12526,12 @@ describe('plugin-meetings', () => {
|
|
|
12352
12526
|
assert.calledWith(canMoveToLobbySpy, userDisplayHints);
|
|
12353
12527
|
assert.calledWith(showAutoEndMeetingWarningSpy, userDisplayHints);
|
|
12354
12528
|
assert.calledWith(isSpokenLanguageAutoDetectionEnabledSpy, userDisplayHints);
|
|
12529
|
+
assert.calledWith(
|
|
12530
|
+
canAttendeeRequestAiAssistantEnabledSpy,
|
|
12531
|
+
userDisplayHints,
|
|
12532
|
+
meeting.roles
|
|
12533
|
+
);
|
|
12534
|
+
assert.calledWith(attendeeRequestAiAssistantDeclinedAllSpy, userDisplayHints);
|
|
12355
12535
|
|
|
12356
12536
|
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
|
12357
12537
|
requiredHints: [DISPLAY_HINTS.MUTE_ALL],
|
|
@@ -12529,16 +12709,20 @@ describe('plugin-meetings', () => {
|
|
|
12529
12709
|
webex.internal.llm.isConnected = sinon.stub().returns(false);
|
|
12530
12710
|
webex.internal.llm.getLocusUrl = sinon.stub();
|
|
12531
12711
|
webex.internal.llm.getDatachannelUrl = sinon.stub();
|
|
12532
|
-
webex.internal.llm.registerAndConnect = sinon
|
|
12533
|
-
|
|
12534
|
-
|
|
12535
|
-
webex.internal.llm.
|
|
12536
|
-
|
|
12537
|
-
|
|
12712
|
+
webex.internal.llm.registerAndConnect = sinon.stub().resolves('something');
|
|
12713
|
+
webex.internal.llm.disconnectLLM = sinon.stub().resolves();
|
|
12714
|
+
webex.internal.llm.on = sinon.stub();
|
|
12715
|
+
webex.internal.llm.off = sinon.stub();
|
|
12716
|
+
webex.internal.llm.getDatachannelToken = sinon.stub().returns(undefined);
|
|
12717
|
+
webex.internal.llm.setDatachannelToken = sinon.stub();
|
|
12718
|
+
|
|
12538
12719
|
meeting.processRelayEvent = sinon.stub();
|
|
12720
|
+
meeting.processLocusLLMEvent = sinon.stub();
|
|
12721
|
+
meeting.clearLLMHealthCheckTimer = sinon.stub();
|
|
12722
|
+
meeting.startLLMHealthCheckTimer = sinon.stub();
|
|
12723
|
+
|
|
12539
12724
|
meeting.webinar.isJoinPracticeSessionDataChannel = sinon.stub().returns(false);
|
|
12540
12725
|
});
|
|
12541
|
-
|
|
12542
12726
|
it('does not connect if the call is not joined yet', async () => {
|
|
12543
12727
|
meeting.joinedWith = {state: 'any other state'};
|
|
12544
12728
|
webex.internal.llm.getLocusUrl.returns('a url');
|
|
@@ -12552,31 +12736,21 @@ describe('plugin-meetings', () => {
|
|
|
12552
12736
|
assert.equal(result, undefined);
|
|
12553
12737
|
assert.notCalled(meeting.webex.internal.llm.on);
|
|
12554
12738
|
});
|
|
12555
|
-
|
|
12556
12739
|
it('returns undefined if llm is already connected and the locus url is unchanged', async () => {
|
|
12557
12740
|
meeting.joinedWith = {state: 'JOINED'};
|
|
12558
|
-
|
|
12559
|
-
|
|
12560
|
-
|
|
12561
|
-
|
|
12562
|
-
meeting.locusInfo = {url: 'a url', info: {datachannelUrl: 'a datachannel url'}};
|
|
12563
|
-
|
|
12564
|
-
const result = await meeting.updateLLMConnection();
|
|
12565
|
-
|
|
12566
|
-
assert.notCalled(webex.internal.llm.registerAndConnect);
|
|
12567
|
-
assert.notCalled(webex.internal.llm.disconnectLLM);
|
|
12568
|
-
assert.equal(result, undefined);
|
|
12569
|
-
assert.notCalled(meeting.webex.internal.llm.on);
|
|
12570
|
-
});
|
|
12571
|
-
|
|
12572
|
-
it('connects if not already connected', async () => {
|
|
12573
|
-
meeting.joinedWith = {state: 'JOINED'};
|
|
12574
|
-
meeting.locusInfo = {url: 'a url', info: {datachannelUrl: 'a datachannel url'}};
|
|
12741
|
+
meeting.locusInfo = {
|
|
12742
|
+
url: 'a url',
|
|
12743
|
+
info: {datachannelUrl: 'a datachannel url'},
|
|
12744
|
+
};
|
|
12575
12745
|
|
|
12576
12746
|
const result = await meeting.updateLLMConnection();
|
|
12577
|
-
|
|
12578
12747
|
assert.notCalled(webex.internal.llm.disconnectLLM);
|
|
12579
|
-
assert.
|
|
12748
|
+
assert.calledWithExactly(
|
|
12749
|
+
webex.internal.llm.registerAndConnect,
|
|
12750
|
+
'a url',
|
|
12751
|
+
'a datachannel url',
|
|
12752
|
+
undefined
|
|
12753
|
+
);
|
|
12580
12754
|
assert.equal(result, 'something');
|
|
12581
12755
|
assert.calledWithExactly(
|
|
12582
12756
|
meeting.webex.internal.llm.off,
|
|
@@ -12599,27 +12773,49 @@ describe('plugin-meetings', () => {
|
|
|
12599
12773
|
meeting.processLocusLLMEvent
|
|
12600
12774
|
);
|
|
12601
12775
|
});
|
|
12776
|
+
it('connects if not already connected', async () => {
|
|
12777
|
+
meeting.joinedWith = {state: 'JOINED'};
|
|
12778
|
+
meeting.locusInfo = {url: 'a url', info: {datachannelUrl: 'a datachannel url'}};
|
|
12602
12779
|
|
|
12603
|
-
|
|
12780
|
+
const result = await meeting.updateLLMConnection();
|
|
12781
|
+
|
|
12782
|
+
assert.notCalled(webex.internal.llm.disconnectLLM);
|
|
12783
|
+
assert.calledWithExactly(
|
|
12784
|
+
webex.internal.llm.registerAndConnect,
|
|
12785
|
+
'a url',
|
|
12786
|
+
'a datachannel url',
|
|
12787
|
+
undefined
|
|
12788
|
+
);
|
|
12789
|
+
assert.equal(result, 'something');
|
|
12790
|
+
});
|
|
12791
|
+
it('disconnects if the locus url has changed', async () => {
|
|
12604
12792
|
meeting.joinedWith = {state: 'JOINED'};
|
|
12793
|
+
|
|
12605
12794
|
webex.internal.llm.isConnected.returns(true);
|
|
12606
12795
|
webex.internal.llm.getLocusUrl.returns('a url');
|
|
12607
|
-
webex.internal.llm.getDatachannelUrl.returns('a datachannel url');
|
|
12608
12796
|
|
|
12609
|
-
meeting.locusInfo = {
|
|
12797
|
+
meeting.locusInfo = {
|
|
12798
|
+
url: 'a different url',
|
|
12799
|
+
info: {datachannelUrl: 'a datachannel url'},
|
|
12800
|
+
self: {},
|
|
12801
|
+
};
|
|
12610
12802
|
|
|
12611
12803
|
const result = await meeting.updateLLMConnection();
|
|
12612
12804
|
|
|
12613
|
-
assert.
|
|
12805
|
+
assert.calledWithExactly(webex.internal.llm.disconnectLLM, {
|
|
12614
12806
|
code: 3050,
|
|
12615
12807
|
reason: 'done (permanent)',
|
|
12616
12808
|
});
|
|
12617
|
-
|
|
12809
|
+
|
|
12810
|
+
assert.calledWithExactly(
|
|
12618
12811
|
webex.internal.llm.registerAndConnect,
|
|
12619
12812
|
'a different url',
|
|
12620
|
-
'a datachannel url'
|
|
12813
|
+
'a datachannel url',
|
|
12814
|
+
undefined
|
|
12621
12815
|
);
|
|
12816
|
+
|
|
12622
12817
|
assert.equal(result, 'something');
|
|
12818
|
+
|
|
12623
12819
|
assert.calledWithExactly(
|
|
12624
12820
|
meeting.webex.internal.llm.off,
|
|
12625
12821
|
'event:relay.event',
|
|
@@ -12631,6 +12827,7 @@ describe('plugin-meetings', () => {
|
|
|
12631
12827
|
meeting.processLocusLLMEvent
|
|
12632
12828
|
);
|
|
12633
12829
|
assert.callCount(meeting.webex.internal.llm.off, 4);
|
|
12830
|
+
|
|
12634
12831
|
assert.calledWithExactly(
|
|
12635
12832
|
meeting.webex.internal.llm.on,
|
|
12636
12833
|
'event:relay.event',
|
|
@@ -12642,27 +12839,33 @@ describe('plugin-meetings', () => {
|
|
|
12642
12839
|
meeting.processLocusLLMEvent
|
|
12643
12840
|
);
|
|
12644
12841
|
});
|
|
12645
|
-
|
|
12646
|
-
it('disconnects it first if the data channel url has changed', async () => {
|
|
12842
|
+
it('disconnects if the data channel url has changed', async () => {
|
|
12647
12843
|
meeting.joinedWith = {state: 'JOINED'};
|
|
12648
12844
|
webex.internal.llm.isConnected.returns(true);
|
|
12649
12845
|
webex.internal.llm.getLocusUrl.returns('a url');
|
|
12650
|
-
webex.internal.llm.getDatachannelUrl.returns('a datachannel url');
|
|
12651
12846
|
|
|
12652
|
-
meeting.locusInfo = {
|
|
12847
|
+
meeting.locusInfo = {
|
|
12848
|
+
url: 'a url',
|
|
12849
|
+
info: {datachannelUrl: 'a different datachannel url'},
|
|
12850
|
+
self: {},
|
|
12851
|
+
};
|
|
12653
12852
|
|
|
12654
12853
|
const result = await meeting.updateLLMConnection();
|
|
12655
12854
|
|
|
12656
|
-
assert.
|
|
12855
|
+
assert.calledWithExactly(webex.internal.llm.disconnectLLM, {
|
|
12657
12856
|
code: 3050,
|
|
12658
12857
|
reason: 'done (permanent)',
|
|
12659
12858
|
});
|
|
12660
|
-
|
|
12859
|
+
|
|
12860
|
+
assert.calledWithExactly(
|
|
12661
12861
|
webex.internal.llm.registerAndConnect,
|
|
12662
12862
|
'a url',
|
|
12663
|
-
'a different datachannel url'
|
|
12863
|
+
'a different datachannel url',
|
|
12864
|
+
undefined
|
|
12664
12865
|
);
|
|
12866
|
+
|
|
12665
12867
|
assert.equal(result, 'something');
|
|
12868
|
+
|
|
12666
12869
|
assert.calledWithExactly(
|
|
12667
12870
|
meeting.webex.internal.llm.off,
|
|
12668
12871
|
'event:relay.event',
|
|
@@ -12673,6 +12876,7 @@ describe('plugin-meetings', () => {
|
|
|
12673
12876
|
'event:locus.state_message',
|
|
12674
12877
|
meeting.processLocusLLMEvent
|
|
12675
12878
|
);
|
|
12879
|
+
|
|
12676
12880
|
assert.calledWithExactly(
|
|
12677
12881
|
meeting.webex.internal.llm.on,
|
|
12678
12882
|
'event:relay.event',
|
|
@@ -12684,7 +12888,6 @@ describe('plugin-meetings', () => {
|
|
|
12684
12888
|
meeting.processLocusLLMEvent
|
|
12685
12889
|
);
|
|
12686
12890
|
});
|
|
12687
|
-
|
|
12688
12891
|
it('disconnects when the state is not JOINED', async () => {
|
|
12689
12892
|
meeting.joinedWith = {state: 'any other state'};
|
|
12690
12893
|
webex.internal.llm.isConnected.returns(true);
|
|
@@ -12694,35 +12897,132 @@ describe('plugin-meetings', () => {
|
|
|
12694
12897
|
|
|
12695
12898
|
const result = await meeting.updateLLMConnection();
|
|
12696
12899
|
|
|
12697
|
-
assert.calledWith(webex.internal.llm.disconnectLLM,
|
|
12900
|
+
assert.calledWith(webex.internal.llm.disconnectLLM, {
|
|
12901
|
+
code: 3050,
|
|
12902
|
+
reason: 'done (permanent)',
|
|
12903
|
+
});
|
|
12698
12904
|
assert.notCalled(webex.internal.llm.registerAndConnect);
|
|
12699
12905
|
assert.equal(result, undefined);
|
|
12906
|
+
});
|
|
12907
|
+
it('connects practice session data channel when PS started', async () => {
|
|
12908
|
+
meeting.joinedWith = {state: 'JOINED'};
|
|
12909
|
+
meeting.locusInfo = {
|
|
12910
|
+
url: 'a url',
|
|
12911
|
+
info: {
|
|
12912
|
+
datachannelUrl: 'a datachannel url',
|
|
12913
|
+
practiceSessionDatachannelUrl: 'ps-url',
|
|
12914
|
+
},
|
|
12915
|
+
};
|
|
12916
|
+
meeting.webinar.isJoinPracticeSessionDataChannel.returns(true);
|
|
12917
|
+
|
|
12918
|
+
await meeting.updateLLMConnection();
|
|
12919
|
+
|
|
12700
12920
|
assert.calledWithExactly(
|
|
12701
|
-
|
|
12702
|
-
'
|
|
12703
|
-
|
|
12921
|
+
webex.internal.llm.registerAndConnect,
|
|
12922
|
+
'a url',
|
|
12923
|
+
'ps-url',
|
|
12924
|
+
undefined
|
|
12704
12925
|
);
|
|
12926
|
+
});
|
|
12927
|
+
it('passes dataChannelToken to registerAndConnect', async () => {
|
|
12928
|
+
meeting.joinedWith = {state: 'JOINED'};
|
|
12929
|
+
meeting.locusInfo = {
|
|
12930
|
+
url: 'a url',
|
|
12931
|
+
info: {datachannelUrl: 'a datachannel url'},
|
|
12932
|
+
self: {datachannelToken: 'token-123'},
|
|
12933
|
+
};
|
|
12934
|
+
|
|
12935
|
+
webex.internal.llm.getDatachannelToken.returns(undefined);
|
|
12936
|
+
|
|
12937
|
+
await meeting.updateLLMConnection();
|
|
12938
|
+
|
|
12705
12939
|
assert.calledWithExactly(
|
|
12706
|
-
|
|
12707
|
-
'
|
|
12708
|
-
|
|
12940
|
+
webex.internal.llm.registerAndConnect,
|
|
12941
|
+
'a url',
|
|
12942
|
+
'a datachannel url',
|
|
12943
|
+
'token-123'
|
|
12709
12944
|
);
|
|
12945
|
+
assert.calledWithExactly(webex.internal.llm.setDatachannelToken, 'token-123', 'default');
|
|
12710
12946
|
});
|
|
12947
|
+
it('prefers refreshed token over locus self token', async () => {
|
|
12948
|
+
meeting.joinedWith = {state: 'JOINED'};
|
|
12949
|
+
meeting.locusInfo = {
|
|
12950
|
+
url: 'a url',
|
|
12951
|
+
info: {datachannelUrl: 'a datachannel url'},
|
|
12952
|
+
self: {datachannelToken: 'locus-token'},
|
|
12953
|
+
};
|
|
12954
|
+
|
|
12955
|
+
webex.internal.llm.getDatachannelToken.withArgs('default').returns('refreshed-token');
|
|
12711
12956
|
|
|
12712
|
-
|
|
12957
|
+
await meeting.updateLLMConnection();
|
|
12958
|
+
|
|
12959
|
+
assert.calledWithExactly(
|
|
12960
|
+
webex.internal.llm.registerAndConnect,
|
|
12961
|
+
'a url',
|
|
12962
|
+
'a datachannel url',
|
|
12963
|
+
'refreshed-token'
|
|
12964
|
+
);
|
|
12965
|
+
|
|
12966
|
+
assert.notCalled(webex.internal.llm.setDatachannelToken);
|
|
12967
|
+
});
|
|
12968
|
+
it('uses practice session token when in PS even if refreshed token exists', async () => {
|
|
12713
12969
|
meeting.joinedWith = {state: 'JOINED'};
|
|
12970
|
+
|
|
12714
12971
|
meeting.locusInfo = {
|
|
12715
12972
|
url: 'a url',
|
|
12716
12973
|
info: {
|
|
12717
12974
|
datachannelUrl: 'a datachannel url',
|
|
12718
|
-
practiceSessionDatachannelUrl: '
|
|
12975
|
+
practiceSessionDatachannelUrl: 'ps-url',
|
|
12976
|
+
},
|
|
12977
|
+
self: {
|
|
12978
|
+
datachannelToken: 'locus-token',
|
|
12979
|
+
practiceSessionDatachannelToken: 'ps-token',
|
|
12719
12980
|
},
|
|
12720
12981
|
};
|
|
12721
|
-
|
|
12982
|
+
|
|
12983
|
+
meeting.webinar.isJoinPracticeSessionDataChannel.returns(true);
|
|
12984
|
+
|
|
12985
|
+
webex.internal.llm.getDatachannelToken
|
|
12986
|
+
.withArgs(true)
|
|
12987
|
+
.returns('refreshed-ps-token') // refreshed practice token
|
|
12988
|
+
.withArgs(false)
|
|
12989
|
+
.returns('refreshed-normal-token'); // refreshed normal token
|
|
12990
|
+
|
|
12722
12991
|
await meeting.updateLLMConnection();
|
|
12723
12992
|
|
|
12724
|
-
assert.
|
|
12725
|
-
|
|
12993
|
+
assert.calledWithExactly(
|
|
12994
|
+
webex.internal.llm.registerAndConnect,
|
|
12995
|
+
'a url',
|
|
12996
|
+
'ps-url',
|
|
12997
|
+
'ps-token'
|
|
12998
|
+
);
|
|
12999
|
+
assert.calledWithExactly(
|
|
13000
|
+
webex.internal.llm.setDatachannelToken,
|
|
13001
|
+
'ps-token',
|
|
13002
|
+
'practiceSession'
|
|
13003
|
+
);
|
|
13004
|
+
});
|
|
13005
|
+
|
|
13006
|
+
it('does not pass token when data channel with jwt token is disabled', async () => {
|
|
13007
|
+
meeting.joinedWith = {state: 'JOINED'};
|
|
13008
|
+
meeting.locusInfo = {
|
|
13009
|
+
url: 'a url',
|
|
13010
|
+
info: {datachannelUrl: 'a datachannel url'},
|
|
13011
|
+
self: {datachannelToken: 'token-123'},
|
|
13012
|
+
};
|
|
13013
|
+
|
|
13014
|
+
webex.internal.llm.getDatachannelToken.returns(undefined);
|
|
13015
|
+
webex.internal.llm.isDataChannelTokenEnabled = sinon.stub().resolves(false);
|
|
13016
|
+
|
|
13017
|
+
await meeting.updateLLMConnection();
|
|
13018
|
+
|
|
13019
|
+
assert.calledWithExactly(
|
|
13020
|
+
webex.internal.llm.registerAndConnect,
|
|
13021
|
+
'a url',
|
|
13022
|
+
'a datachannel url',
|
|
13023
|
+
'token-123'
|
|
13024
|
+
);
|
|
13025
|
+
assert.calledWithExactly(webex.internal.llm.setDatachannelToken, 'token-123', 'default');
|
|
12726
13026
|
});
|
|
12727
13027
|
});
|
|
12728
13028
|
|
|
@@ -12733,6 +13033,7 @@ describe('plugin-meetings', () => {
|
|
|
12733
13033
|
|
|
12734
13034
|
it('should read the locus object, set on the meeting and return null', () => {
|
|
12735
13035
|
const dataSets = {someFakeStuff: 'dataSet'};
|
|
13036
|
+
const metadata = {some: 'metadata'};
|
|
12736
13037
|
|
|
12737
13038
|
meeting.setLocus({
|
|
12738
13039
|
mediaConnections: [test1],
|
|
@@ -12742,12 +13043,14 @@ describe('plugin-meetings', () => {
|
|
|
12742
13043
|
mediaId: uuid3,
|
|
12743
13044
|
locus: {host: {id: uuid4}},
|
|
12744
13045
|
dataSets,
|
|
13046
|
+
metadata,
|
|
12745
13047
|
});
|
|
12746
13048
|
assert.calledOnce(meeting.locusInfo.initialSetup);
|
|
12747
13049
|
assert.calledWith(meeting.locusInfo.initialSetup, {
|
|
12748
13050
|
trigger: 'join-response',
|
|
12749
13051
|
locus: {host: {id: uuid4}},
|
|
12750
13052
|
dataSets,
|
|
13053
|
+
metadata,
|
|
12751
13054
|
});
|
|
12752
13055
|
assert.equal(meeting.mediaConnections, test1);
|
|
12753
13056
|
assert.equal(meeting.locusUrl, url1);
|
|
@@ -14289,6 +14592,69 @@ describe('plugin-meetings', () => {
|
|
|
14289
14592
|
assert.calledOnce(meeting.meetingRequest.keepAlive);
|
|
14290
14593
|
});
|
|
14291
14594
|
});
|
|
14595
|
+
describe('#refreshDataChannelToken()', () => {
|
|
14596
|
+
let meeting;
|
|
14597
|
+
|
|
14598
|
+
beforeEach(() => {
|
|
14599
|
+
meeting = Object.create(Meeting.prototype);
|
|
14600
|
+
meeting.locusUrl = 'https://locus.example.com';
|
|
14601
|
+
meeting.meetingRequest = {
|
|
14602
|
+
fetchDatachannelToken: sinon.stub().resolves({
|
|
14603
|
+
body: {datachannelToken: 'mock-token'},
|
|
14604
|
+
}),
|
|
14605
|
+
};
|
|
14606
|
+
meeting.members = {
|
|
14607
|
+
selfId: 'self-123',
|
|
14608
|
+
};
|
|
14609
|
+
meeting.webinar = {
|
|
14610
|
+
isJoinPracticeSessionDataChannel: sinon.stub().returns(true),
|
|
14611
|
+
};
|
|
14612
|
+
});
|
|
14613
|
+
|
|
14614
|
+
it('calls fetchDatachannelToken with correct parameters', async () => {
|
|
14615
|
+
await meeting.refreshDataChannelToken();
|
|
14616
|
+
|
|
14617
|
+
sinon.assert.calledOnce(meeting.meetingRequest.fetchDatachannelToken);
|
|
14618
|
+
|
|
14619
|
+
sinon.assert.calledWith(meeting.meetingRequest.fetchDatachannelToken, {
|
|
14620
|
+
locusUrl: 'https://locus.example.com',
|
|
14621
|
+
requestingParticipantId: 'self-123',
|
|
14622
|
+
isPracticeSession: true,
|
|
14623
|
+
});
|
|
14624
|
+
});
|
|
14625
|
+
|
|
14626
|
+
it('returns the correct structured result', async () => {
|
|
14627
|
+
const result = await meeting.refreshDataChannelToken();
|
|
14628
|
+
|
|
14629
|
+
expect(result).to.deep.equal({
|
|
14630
|
+
body: {
|
|
14631
|
+
datachannelToken: 'mock-token',
|
|
14632
|
+
dataChannelTokenType: 'practiceSession',
|
|
14633
|
+
},
|
|
14634
|
+
});
|
|
14635
|
+
});
|
|
14636
|
+
});
|
|
14637
|
+
describe('#getDataChannelTokenType', () => {
|
|
14638
|
+
it('returns PracticeSession when webinar is in practice session mode', () => {
|
|
14639
|
+
meeting.webinar = {
|
|
14640
|
+
isJoinPracticeSessionDataChannel: sinon.stub().returns(true),
|
|
14641
|
+
};
|
|
14642
|
+
|
|
14643
|
+
const result = meeting.getDataChannelTokenType();
|
|
14644
|
+
|
|
14645
|
+
expect(result).to.equal('practiceSession');
|
|
14646
|
+
});
|
|
14647
|
+
|
|
14648
|
+
it('returns Default when not in practice session mode', () => {
|
|
14649
|
+
meeting.webinar = {
|
|
14650
|
+
isJoinPracticeSessionDataChannel: sinon.stub().returns(false),
|
|
14651
|
+
};
|
|
14652
|
+
|
|
14653
|
+
const result = meeting.getDataChannelTokenType();
|
|
14654
|
+
|
|
14655
|
+
expect(result).to.equal('default');
|
|
14656
|
+
});
|
|
14657
|
+
});
|
|
14292
14658
|
describe('#stopKeepAlive', () => {
|
|
14293
14659
|
let clock;
|
|
14294
14660
|
const defaultKeepAliveUrl = 'keep.alive.url';
|