@webex/plugin-meetings 3.11.0-webex-services-ready.1 → 3.12.0
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 +14 -5
- 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 +28 -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 +290 -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/interceptors/utils.js +27 -0
- package/dist/interceptors/utils.js.map +1 -0
- 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 +217 -79
- 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 +1071 -862
- 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 +133 -3
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +100 -45
- 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 +9 -60
- 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/reconnection-manager/index.js +0 -1
- package/dist/reconnection-manager/index.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 +23 -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 +43 -0
- package/dist/types/interceptors/index.d.ts +2 -1
- package/dist/types/interceptors/utils.d.ts +1 -0
- package/dist/types/locus-info/index.d.ts +21 -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 +38 -6
- 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/multistream/mediaRequestManager.d.ts +0 -23
- package/dist/types/reactions/reactions.type.d.ts +1 -0
- package/dist/types/webinar/utils.d.ts +6 -0
- package/dist/webinar/index.js +260 -90
- package/dist/webinar/index.js.map +1 -1
- package/dist/webinar/utils.js +25 -0
- package/dist/webinar/utils.js.map +1 -0
- package/package.json +24 -23
- 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 +27 -7
- package/src/config.ts +3 -0
- package/src/constants.ts +29 -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 +170 -0
- package/src/interceptors/index.ts +2 -1
- package/src/interceptors/utils.ts +16 -0
- package/src/interpretation/index.ts +2 -2
- package/src/locus-info/controlsUtils.ts +11 -0
- package/src/locus-info/index.ts +231 -61
- 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 +188 -43
- package/src/meeting/request.ts +42 -0
- package/src/meeting/request.type.ts +6 -0
- package/src/meeting/util.ts +160 -2
- package/src/meetings/index.ts +135 -41
- 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 +4 -54
- package/src/multistream/remoteMediaManager.ts +13 -0
- package/src/reactions/reactions.type.ts +1 -0
- package/src/reconnection-manager/index.ts +0 -1
- package/src/webinar/index.ts +162 -5
- package/src/webinar/utils.ts +16 -0
- package/test/unit/spec/aiEnableRequest/index.ts +981 -0
- package/test/unit/spec/aiEnableRequest/utils.ts +130 -0
- package/test/unit/spec/annotation/index.ts +69 -7
- 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 +210 -0
- package/test/unit/spec/interceptors/utils.ts +75 -0
- package/test/unit/spec/locus-info/controlsUtils.js +29 -0
- package/test/unit/spec/locus-info/index.js +383 -46
- 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 +662 -85
- package/test/unit/spec/meeting/request.js +70 -0
- package/test/unit/spec/meeting/utils.js +438 -26
- package/test/unit/spec/meetings/index.js +652 -31
- package/test/unit/spec/member/index.js +28 -4
- package/test/unit/spec/member/util.js +65 -27
- package/test/unit/spec/multistream/mediaRequestManager.ts +2 -85
- package/test/unit/spec/multistream/remoteMediaManager.ts +30 -0
- package/test/unit/spec/reconnection-manager/index.js +4 -8
- package/test/unit/spec/webinar/index.ts +348 -36
- package/test/unit/spec/webinar/utils.ts +39 -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,7 +265,9 @@ 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();
|
|
270
|
+
webex.internal.voicea.announce = sinon.stub();
|
|
269
271
|
webex.internal.newMetrics.callDiagnosticLatencies = new CallDiagnosticLatencies(
|
|
270
272
|
{},
|
|
271
273
|
{parent: webex}
|
|
@@ -376,6 +378,7 @@ describe('plugin-meetings', () => {
|
|
|
376
378
|
assert.instanceOf(meeting.breakouts, Breakouts);
|
|
377
379
|
assert.instanceOf(meeting.simultaneousInterpretation, SimultaneousInterpretation);
|
|
378
380
|
assert.instanceOf(meeting.webinar, Webinar);
|
|
381
|
+
assert.instanceOf(meeting.aiEnableRequest, AIEnableRequest);
|
|
379
382
|
});
|
|
380
383
|
|
|
381
384
|
it('should call the callback with the meeting that has id already set', () => {
|
|
@@ -738,7 +741,9 @@ describe('plugin-meetings', () => {
|
|
|
738
741
|
let supportsRTCPeerConnectionStub;
|
|
739
742
|
|
|
740
743
|
beforeEach(() => {
|
|
741
|
-
supportsRTCPeerConnectionStub = sinon
|
|
744
|
+
supportsRTCPeerConnectionStub = sinon
|
|
745
|
+
.stub(WebCapabilities, 'supportsRTCPeerConnection')
|
|
746
|
+
.returns(CapabilityState.CAPABLE);
|
|
742
747
|
|
|
743
748
|
meeting.join = sinon.stub().callsFake((joinOptions) => {
|
|
744
749
|
meeting.isMultistream = joinOptions.enableMultistream;
|
|
@@ -1011,33 +1016,53 @@ describe('plugin-meetings', () => {
|
|
|
1011
1016
|
);
|
|
1012
1017
|
});
|
|
1013
1018
|
|
|
1014
|
-
it('should call leave() if addMediaInternal() fails ', async () => {
|
|
1019
|
+
it('should call leave() if addMediaInternal() fails with a browser media error (TypeError)', async () => {
|
|
1015
1020
|
const addMediaError = new Error('fake addMedia error');
|
|
1016
|
-
addMediaError.name = 'TypeError';
|
|
1021
|
+
addMediaError.name = 'TypeError'; // This makes it a browser media error
|
|
1017
1022
|
|
|
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();
|
|
1023
|
+
const leaveStub = sinon.stub(meeting, 'leave').resolves();
|
|
1024
|
+
meeting.addMediaInternal = sinon.stub().rejects(addMediaError);
|
|
1029
1025
|
|
|
1030
|
-
|
|
1026
|
+
// When a browser media error occurs, it gets transformed into a special structure
|
|
1027
|
+
const rejectedError = await assert.isRejected(
|
|
1031
1028
|
meeting.joinWithMedia({
|
|
1032
1029
|
joinOptions,
|
|
1033
1030
|
mediaOptions,
|
|
1034
|
-
})
|
|
1035
|
-
rejectError
|
|
1031
|
+
})
|
|
1036
1032
|
);
|
|
1037
1033
|
|
|
1034
|
+
// Verify the error was transformed with errorCode 2729
|
|
1035
|
+
assert.equal(rejectedError.error.body.errorCode, 2729);
|
|
1036
|
+
assert.equal(rejectedError.error.body.message, 'fake addMedia error');
|
|
1037
|
+
assert.equal(rejectedError.error.body.name, 'TypeError');
|
|
1038
|
+
|
|
1038
1039
|
assert.calledOnce(meeting.join);
|
|
1039
1040
|
assert.calledOnce(meeting.addMediaInternal);
|
|
1041
|
+
assert.calledOnce(leaveStub);
|
|
1042
|
+
assert.calledOnceWithExactly(leaveStub, {
|
|
1043
|
+
resourceId: undefined,
|
|
1044
|
+
reason: 'joinWithMedia failure',
|
|
1045
|
+
});
|
|
1046
|
+
|
|
1047
|
+
// Browser media errors don't retry, so behavioral metric is sent only once
|
|
1048
|
+
// NOTE: The error gets transformed, so the metric receives undefined for message/stack/name
|
|
1049
|
+
// because they're now nested in error.body instead of at the top level
|
|
1040
1050
|
assert.calledOnce(Metrics.sendBehavioralMetric);
|
|
1051
|
+
assert.calledWith(
|
|
1052
|
+
Metrics.sendBehavioralMetric,
|
|
1053
|
+
BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
|
|
1054
|
+
{
|
|
1055
|
+
correlation_id: meeting.correlationId,
|
|
1056
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
|
1057
|
+
reason: undefined, // transformed error doesn't have .message at top level
|
|
1058
|
+
stack: undefined, // transformed error doesn't have .stack at top level
|
|
1059
|
+
leaveErrorReason: undefined,
|
|
1060
|
+
isRetry: false,
|
|
1061
|
+
},
|
|
1062
|
+
{
|
|
1063
|
+
type: undefined, // transformed error doesn't have .name at top level
|
|
1064
|
+
}
|
|
1065
|
+
);
|
|
1041
1066
|
});
|
|
1042
1067
|
|
|
1043
1068
|
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 +1274,14 @@ describe('plugin-meetings', () => {
|
|
|
1249
1274
|
});
|
|
1250
1275
|
|
|
1251
1276
|
[
|
|
1252
|
-
{
|
|
1253
|
-
|
|
1277
|
+
{
|
|
1278
|
+
errorName: 'SdpOfferCreationError',
|
|
1279
|
+
description: 'if we fail to create the offer on first attempt',
|
|
1280
|
+
},
|
|
1281
|
+
{
|
|
1282
|
+
errorName: 'WebrtcApiNotAvailableError',
|
|
1283
|
+
description: 'if RTCPeerConnection is not available',
|
|
1284
|
+
},
|
|
1254
1285
|
].forEach(({errorName, description}) => {
|
|
1255
1286
|
it(`should not attempt a retry ${description}`, async () => {
|
|
1256
1287
|
const addMediaError = new Error('fake addMedia error');
|
|
@@ -1290,7 +1321,7 @@ describe('plugin-meetings', () => {
|
|
|
1290
1321
|
resourceId: undefined,
|
|
1291
1322
|
reason: 'joinWithMedia failure',
|
|
1292
1323
|
});
|
|
1293
|
-
})
|
|
1324
|
+
});
|
|
1294
1325
|
});
|
|
1295
1326
|
|
|
1296
1327
|
it('should ignore sendVideo/receiveVideo when videoEnabled is false', async () => {
|
|
@@ -1882,6 +1913,53 @@ describe('plugin-meetings', () => {
|
|
|
1882
1913
|
fakeProcessedReaction
|
|
1883
1914
|
);
|
|
1884
1915
|
});
|
|
1916
|
+
|
|
1917
|
+
it('should process if participantId does not exist in membersCollection but has displayName in Webinar', () => {
|
|
1918
|
+
LoggerProxy.logger.warn = sinon.stub();
|
|
1919
|
+
meeting.isReactionsSupported = sinon.stub().returns(true);
|
|
1920
|
+
meeting.config.receiveReactions = true;
|
|
1921
|
+
meeting.locusInfo.info = {isWebinar: true};
|
|
1922
|
+
const fakeSendersName = 'Fake reactors name';
|
|
1923
|
+
const fakeReactionPayload = {
|
|
1924
|
+
type: 'fake_type',
|
|
1925
|
+
codepoints: 'fake_codepoints',
|
|
1926
|
+
shortcodes: 'fake_shortcodes',
|
|
1927
|
+
tone: {
|
|
1928
|
+
type: 'fake_tone_type',
|
|
1929
|
+
codepoints: 'fake_tone_codepoints',
|
|
1930
|
+
shortcodes: 'fake_tone_shortcodes',
|
|
1931
|
+
},
|
|
1932
|
+
};
|
|
1933
|
+
const fakeSenderPayload = {
|
|
1934
|
+
displayName: 'Fake reactors name',
|
|
1935
|
+
participantId: 'fake_participant_id',
|
|
1936
|
+
};
|
|
1937
|
+
const fakeProcessedReaction = {
|
|
1938
|
+
reaction: fakeReactionPayload,
|
|
1939
|
+
sender: {
|
|
1940
|
+
id: fakeSenderPayload.participantId,
|
|
1941
|
+
name: fakeSendersName,
|
|
1942
|
+
},
|
|
1943
|
+
};
|
|
1944
|
+
const fakeRelayEvent = {
|
|
1945
|
+
data: {
|
|
1946
|
+
relayType: REACTION_RELAY_TYPES.REACTION,
|
|
1947
|
+
reaction: fakeReactionPayload,
|
|
1948
|
+
sender: fakeSenderPayload,
|
|
1949
|
+
},
|
|
1950
|
+
};
|
|
1951
|
+
meeting.processRelayEvent(fakeRelayEvent);
|
|
1952
|
+
assert.calledWith(
|
|
1953
|
+
TriggerProxy.trigger,
|
|
1954
|
+
sinon.match.instanceOf(Meeting),
|
|
1955
|
+
{
|
|
1956
|
+
file: 'meeting/index',
|
|
1957
|
+
function: 'join',
|
|
1958
|
+
},
|
|
1959
|
+
EVENT_TRIGGERS.MEETING_RECEIVE_REACTIONS,
|
|
1960
|
+
fakeProcessedReaction
|
|
1961
|
+
);
|
|
1962
|
+
});
|
|
1885
1963
|
});
|
|
1886
1964
|
|
|
1887
1965
|
describe('#handleLLMOnline', () => {
|
|
@@ -3028,6 +3106,111 @@ describe('plugin-meetings', () => {
|
|
|
3028
3106
|
checkWorking({allowMediaInLobby: true});
|
|
3029
3107
|
});
|
|
3030
3108
|
|
|
3109
|
+
const setupLobbyTest = () => {
|
|
3110
|
+
meeting.roap.doTurnDiscovery = sinon
|
|
3111
|
+
.stub()
|
|
3112
|
+
.resolves({turnServerInfo: undefined, turnDiscoverySkippedReason: undefined});
|
|
3113
|
+
|
|
3114
|
+
meeting.meetingState = 'ACTIVE';
|
|
3115
|
+
meeting.locusInfo.parsedLocus = {self: {state: 'IDLE'}};
|
|
3116
|
+
meeting.isUserUnadmitted = true;
|
|
3117
|
+
|
|
3118
|
+
// Mock locusMediaRequest
|
|
3119
|
+
meeting.locusMediaRequest = {
|
|
3120
|
+
send: sinon.stub().resolves(),
|
|
3121
|
+
isConfluenceCreated: sinon.stub().returns(false),
|
|
3122
|
+
};
|
|
3123
|
+
|
|
3124
|
+
sinon.stub(RemoteMediaManagerModule, 'RemoteMediaManager').returns({
|
|
3125
|
+
start: sinon.stub().resolves(),
|
|
3126
|
+
on: sinon.stub(),
|
|
3127
|
+
logAllReceiveSlots: sinon.stub(),
|
|
3128
|
+
});
|
|
3129
|
+
|
|
3130
|
+
meeting.isMultistream = true;
|
|
3131
|
+
|
|
3132
|
+
const createFakeStream = (id) => ({
|
|
3133
|
+
on: sinon.stub(),
|
|
3134
|
+
off: sinon.stub(),
|
|
3135
|
+
userMuted: false,
|
|
3136
|
+
systemMuted: false,
|
|
3137
|
+
get muted() {
|
|
3138
|
+
return this.userMuted || this.systemMuted;
|
|
3139
|
+
},
|
|
3140
|
+
setUnmuteAllowed: sinon.stub(),
|
|
3141
|
+
setUserMuted: sinon.stub(),
|
|
3142
|
+
outputStream: {
|
|
3143
|
+
getTracks: () => [{id}],
|
|
3144
|
+
},
|
|
3145
|
+
getSettings: sinon.stub().returns({}),
|
|
3146
|
+
});
|
|
3147
|
+
|
|
3148
|
+
return {
|
|
3149
|
+
fakeMicrophoneStream: createFakeStream('fake mic'),
|
|
3150
|
+
fakeCameraStream: createFakeStream('fake camera'),
|
|
3151
|
+
};
|
|
3152
|
+
};
|
|
3153
|
+
|
|
3154
|
+
it('should not publish any local streams when in the lobby and allowPublishMediaInLobby is false', async () => {
|
|
3155
|
+
const {fakeMicrophoneStream, fakeCameraStream} = setupLobbyTest();
|
|
3156
|
+
|
|
3157
|
+
const publishStreamStub = sinon.stub();
|
|
3158
|
+
fakeMediaConnection.createSendSlot = sinon.stub().returns({
|
|
3159
|
+
publishStream: publishStreamStub,
|
|
3160
|
+
unpublishStream: sinon.stub(),
|
|
3161
|
+
setNamedMediaGroups: sinon.stub(),
|
|
3162
|
+
});
|
|
3163
|
+
|
|
3164
|
+
await meeting.addMedia({
|
|
3165
|
+
allowMediaInLobby: true,
|
|
3166
|
+
allowPublishMediaInLobby: false,
|
|
3167
|
+
audioEnabled: true,
|
|
3168
|
+
videoEnabled: true,
|
|
3169
|
+
localStreams: {
|
|
3170
|
+
microphone: fakeMicrophoneStream,
|
|
3171
|
+
camera: fakeCameraStream,
|
|
3172
|
+
},
|
|
3173
|
+
});
|
|
3174
|
+
|
|
3175
|
+
assert.notCalled(publishStreamStub);
|
|
3176
|
+
});
|
|
3177
|
+
|
|
3178
|
+
it('should publish local streams when in the lobby and allowPublishMediaInLobby is true', async () => {
|
|
3179
|
+
const {fakeMicrophoneStream, fakeCameraStream} = setupLobbyTest();
|
|
3180
|
+
|
|
3181
|
+
const audioSlot = {
|
|
3182
|
+
publishStream: sinon.stub(),
|
|
3183
|
+
unpublishStream: sinon.stub(),
|
|
3184
|
+
setNamedMediaGroups: sinon.stub(),
|
|
3185
|
+
};
|
|
3186
|
+
const videoSlot = {
|
|
3187
|
+
publishStream: sinon.stub(),
|
|
3188
|
+
unpublishStream: sinon.stub(),
|
|
3189
|
+
setNamedMediaGroups: sinon.stub(),
|
|
3190
|
+
};
|
|
3191
|
+
|
|
3192
|
+
fakeMediaConnection.createSendSlot = sinon.stub().callsFake((mediaType) => {
|
|
3193
|
+
if (mediaType === 'AUDIO-MAIN') {
|
|
3194
|
+
return audioSlot;
|
|
3195
|
+
}
|
|
3196
|
+
return videoSlot;
|
|
3197
|
+
});
|
|
3198
|
+
|
|
3199
|
+
await meeting.addMedia({
|
|
3200
|
+
allowMediaInLobby: true,
|
|
3201
|
+
allowPublishMediaInLobby: true,
|
|
3202
|
+
audioEnabled: true,
|
|
3203
|
+
videoEnabled: true,
|
|
3204
|
+
localStreams: {
|
|
3205
|
+
microphone: fakeMicrophoneStream,
|
|
3206
|
+
camera: fakeCameraStream,
|
|
3207
|
+
},
|
|
3208
|
+
});
|
|
3209
|
+
|
|
3210
|
+
assert.calledOnceWithExactly(audioSlot.publishStream, fakeMicrophoneStream);
|
|
3211
|
+
assert.calledOnceWithExactly(videoSlot.publishStream, fakeCameraStream);
|
|
3212
|
+
});
|
|
3213
|
+
|
|
3031
3214
|
it('should create rtcMetrics and pass them to Media.createMediaConnection()', async () => {
|
|
3032
3215
|
const setIntervalOriginal = window.setInterval;
|
|
3033
3216
|
window.setInterval = sinon.stub().returns(1);
|
|
@@ -6218,7 +6401,10 @@ describe('plugin-meetings', () => {
|
|
|
6218
6401
|
meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
|
|
6219
6402
|
meeting.unsetPeerConnections = sinon.stub().returns(true);
|
|
6220
6403
|
meeting.logger.error = sinon.stub().returns(true);
|
|
6221
|
-
meeting.
|
|
6404
|
+
meeting.clearMeetingData = sinon.stub().callsFake(async () => {
|
|
6405
|
+
meeting.audio = null;
|
|
6406
|
+
meeting.video = null;
|
|
6407
|
+
});
|
|
6222
6408
|
webex.internal.voicea.off = sinon.stub().returns(true);
|
|
6223
6409
|
meeting.stopTranscription = sinon.stub();
|
|
6224
6410
|
meeting.transcription = {};
|
|
@@ -6245,9 +6431,7 @@ describe('plugin-meetings', () => {
|
|
|
6245
6431
|
assert.calledOnce(meeting.closePeerConnections);
|
|
6246
6432
|
assert.calledOnce(meeting.unsetRemoteStreams);
|
|
6247
6433
|
assert.calledOnce(meeting.unsetPeerConnections);
|
|
6248
|
-
assert.calledOnce(meeting.
|
|
6249
|
-
assert.calledOnce(meeting.annotation.deregisterEvents);
|
|
6250
|
-
assert.calledWith(webex.internal.llm.off, 'event:relay.event', meeting.processRelayEvent);
|
|
6434
|
+
assert.calledOnce(meeting.clearMeetingData);
|
|
6251
6435
|
});
|
|
6252
6436
|
|
|
6253
6437
|
it('should reset call diagnostic latencies correctly', async () => {
|
|
@@ -8248,7 +8432,10 @@ describe('plugin-meetings', () => {
|
|
|
8248
8432
|
meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
|
|
8249
8433
|
meeting.unsetPeerConnections = sinon.stub().returns(true);
|
|
8250
8434
|
meeting.logger.error = sinon.stub().returns(true);
|
|
8251
|
-
meeting.
|
|
8435
|
+
meeting.clearMeetingData = sinon.stub().callsFake(async () => {
|
|
8436
|
+
meeting.audio = null;
|
|
8437
|
+
meeting.video = null;
|
|
8438
|
+
});
|
|
8252
8439
|
meeting.transcription = {};
|
|
8253
8440
|
meeting.stopTranscription = sinon.stub();
|
|
8254
8441
|
|
|
@@ -8274,10 +8461,7 @@ describe('plugin-meetings', () => {
|
|
|
8274
8461
|
assert.calledOnce(meeting?.closePeerConnections);
|
|
8275
8462
|
assert.calledOnce(meeting?.unsetRemoteStreams);
|
|
8276
8463
|
assert.calledOnce(meeting?.unsetPeerConnections);
|
|
8277
|
-
assert.calledOnce(meeting?.
|
|
8278
|
-
|
|
8279
|
-
assert.called(meeting.annotation.deregisterEvents);
|
|
8280
|
-
assert.calledWith(webex.internal.llm.off, 'event:relay.event', meeting.processRelayEvent);
|
|
8464
|
+
assert.calledOnce(meeting?.clearMeetingData);
|
|
8281
8465
|
});
|
|
8282
8466
|
});
|
|
8283
8467
|
|
|
@@ -9149,7 +9333,10 @@ describe('plugin-meetings', () => {
|
|
|
9149
9333
|
|
|
9150
9334
|
// check that the right things were called by the callback
|
|
9151
9335
|
assert.calledOnceWithExactly(meeting.waitForRemoteSDPAnswer);
|
|
9152
|
-
assert.calledOnceWithExactly(
|
|
9336
|
+
assert.calledOnceWithExactly(
|
|
9337
|
+
meeting.mediaProperties.waitForMediaConnectionConnected,
|
|
9338
|
+
meeting.correlationId
|
|
9339
|
+
);
|
|
9153
9340
|
});
|
|
9154
9341
|
});
|
|
9155
9342
|
|
|
@@ -10323,6 +10510,21 @@ describe('plugin-meetings', () => {
|
|
|
10323
10510
|
EVENT_TRIGGERS.MEETING_INTERPRETATION_UPDATE
|
|
10324
10511
|
);
|
|
10325
10512
|
});
|
|
10513
|
+
|
|
10514
|
+
it('listens to the self id changed event and updates aiEnableRequest', () => {
|
|
10515
|
+
meeting.aiEnableRequest = {
|
|
10516
|
+
selfParticipantIdUpdate: sinon.stub(),
|
|
10517
|
+
};
|
|
10518
|
+
|
|
10519
|
+
const payload = {selfId: 'participant-test-123'};
|
|
10520
|
+
|
|
10521
|
+
meeting.locusInfo.emit({function: 'test', file: 'test'}, 'SELF_ID_CHANGED', payload);
|
|
10522
|
+
|
|
10523
|
+
assert.calledOnceWithExactly(
|
|
10524
|
+
meeting.aiEnableRequest.selfParticipantIdUpdate,
|
|
10525
|
+
payload.selfId
|
|
10526
|
+
);
|
|
10527
|
+
});
|
|
10326
10528
|
});
|
|
10327
10529
|
|
|
10328
10530
|
describe('#setUpBreakoutsListener', () => {
|
|
@@ -10570,6 +10772,24 @@ describe('plugin-meetings', () => {
|
|
|
10570
10772
|
);
|
|
10571
10773
|
});
|
|
10572
10774
|
|
|
10775
|
+
it('listens to MEETING_CONTROLS_AI_SUMMARY_NOTIFICATION_UPDATED', async () => {
|
|
10776
|
+
const aiSummaryNotification = {example: 'value'};
|
|
10777
|
+
|
|
10778
|
+
await meeting.locusInfo.emitScoped(
|
|
10779
|
+
{function: 'test', file: 'test'},
|
|
10780
|
+
LOCUSINFO.EVENTS.CONTROLS_AI_SUMMARY_NOTIFICATION_UPDATED,
|
|
10781
|
+
{aiSummaryNotification}
|
|
10782
|
+
);
|
|
10783
|
+
|
|
10784
|
+
assert.calledWith(
|
|
10785
|
+
TriggerProxy.trigger,
|
|
10786
|
+
meeting,
|
|
10787
|
+
{file: 'meeting/index', function: 'setupLocusControlsListener'},
|
|
10788
|
+
EVENT_TRIGGERS.MEETING_CONTROLS_AI_SUMMARY_NOTIFICATION_UPDATED,
|
|
10789
|
+
{aiSummaryNotification}
|
|
10790
|
+
);
|
|
10791
|
+
});
|
|
10792
|
+
|
|
10573
10793
|
it('listens to MEETING_CONTROLS_MEETING_FULL_UPDATED', async () => {
|
|
10574
10794
|
const state = {example: 'value'};
|
|
10575
10795
|
|
|
@@ -10842,6 +11062,9 @@ describe('plugin-meetings', () => {
|
|
|
10842
11062
|
meeting.simultaneousInterpretation = {
|
|
10843
11063
|
approvalUrlUpdate: sinon.stub().returns(undefined),
|
|
10844
11064
|
};
|
|
11065
|
+
meeting.aiEnableRequest = {
|
|
11066
|
+
approvalUrlUpdate: sinon.stub().returns(undefined),
|
|
11067
|
+
};
|
|
10845
11068
|
|
|
10846
11069
|
meeting.locusInfo.emit(
|
|
10847
11070
|
{function: 'test', file: 'test'},
|
|
@@ -10861,6 +11084,10 @@ describe('plugin-meetings', () => {
|
|
|
10861
11084
|
meeting.simultaneousInterpretation.approvalUrlUpdate,
|
|
10862
11085
|
newLocusServices.services.approval.url
|
|
10863
11086
|
);
|
|
11087
|
+
assert.calledWith(
|
|
11088
|
+
meeting.aiEnableRequest.approvalUrlUpdate,
|
|
11089
|
+
newLocusServices.services.approval.url
|
|
11090
|
+
);
|
|
10864
11091
|
assert.calledOnce(meeting.recordingController.setSessionId);
|
|
10865
11092
|
done();
|
|
10866
11093
|
});
|
|
@@ -11266,6 +11493,41 @@ describe('plugin-meetings', () => {
|
|
|
11266
11493
|
});
|
|
11267
11494
|
});
|
|
11268
11495
|
|
|
11496
|
+
describe('localConstraintsChangeHandler', () => {
|
|
11497
|
+
it('calls updatePreferredBitrateKbps when not multistream', () => {
|
|
11498
|
+
meeting.isMultistream = false;
|
|
11499
|
+
meeting.mediaProperties.webrtcMediaConnection = {
|
|
11500
|
+
updatePreferredBitrateKbps: sinon.stub(),
|
|
11501
|
+
};
|
|
11502
|
+
|
|
11503
|
+
meeting.localConstraintsChangeHandler();
|
|
11504
|
+
|
|
11505
|
+
assert.calledOnce(
|
|
11506
|
+
meeting.mediaProperties.webrtcMediaConnection.updatePreferredBitrateKbps
|
|
11507
|
+
);
|
|
11508
|
+
});
|
|
11509
|
+
|
|
11510
|
+
it('does not call updatePreferredBitrateKbps when multistream', () => {
|
|
11511
|
+
meeting.isMultistream = true;
|
|
11512
|
+
meeting.mediaProperties.webrtcMediaConnection = {
|
|
11513
|
+
updatePreferredBitrateKbps: sinon.stub(),
|
|
11514
|
+
};
|
|
11515
|
+
|
|
11516
|
+
meeting.localConstraintsChangeHandler();
|
|
11517
|
+
|
|
11518
|
+
assert.notCalled(
|
|
11519
|
+
meeting.mediaProperties.webrtcMediaConnection.updatePreferredBitrateKbps
|
|
11520
|
+
);
|
|
11521
|
+
});
|
|
11522
|
+
|
|
11523
|
+
it('does not throw when webrtcMediaConnection is undefined', () => {
|
|
11524
|
+
meeting.isMultistream = false;
|
|
11525
|
+
meeting.mediaProperties.webrtcMediaConnection = undefined;
|
|
11526
|
+
|
|
11527
|
+
assert.doesNotThrow(() => meeting.localConstraintsChangeHandler());
|
|
11528
|
+
});
|
|
11529
|
+
});
|
|
11530
|
+
|
|
11269
11531
|
describe('#parseMeetingInfo', () => {
|
|
11270
11532
|
const checkParseMeetingInfo = (expectedInfoToParse) => {
|
|
11271
11533
|
assert.equal(meeting.conversationUrl, expectedInfoToParse.conversationUrl);
|
|
@@ -11645,6 +11907,7 @@ describe('plugin-meetings', () => {
|
|
|
11645
11907
|
let canUnsetDisallowUnmuteSpy;
|
|
11646
11908
|
let canUserRaiseHandSpy;
|
|
11647
11909
|
let bothLeaveAndEndMeetingAvailableSpy;
|
|
11910
|
+
let requireHostEndMeetingBeforeLeaveSpy;
|
|
11648
11911
|
let canUserLowerAllHandsSpy;
|
|
11649
11912
|
let canUserLowerSomeoneElsesHandSpy;
|
|
11650
11913
|
let waitingForOthersToJoinSpy;
|
|
@@ -11656,6 +11919,8 @@ describe('plugin-meetings', () => {
|
|
|
11656
11919
|
let canMoveToLobbySpy;
|
|
11657
11920
|
let isSpokenLanguageAutoDetectionEnabledSpy;
|
|
11658
11921
|
let showAutoEndMeetingWarningSpy;
|
|
11922
|
+
let canAttendeeRequestAiAssistantEnabledSpy;
|
|
11923
|
+
let attendeeRequestAiAssistantDeclinedAllSpy;
|
|
11659
11924
|
// Due to import tree issues, hasHints must be stubed within the scope of the `it`.
|
|
11660
11925
|
|
|
11661
11926
|
beforeEach(() => {
|
|
@@ -11676,6 +11941,10 @@ describe('plugin-meetings', () => {
|
|
|
11676
11941
|
MeetingUtil,
|
|
11677
11942
|
'bothLeaveAndEndMeetingAvailable'
|
|
11678
11943
|
);
|
|
11944
|
+
requireHostEndMeetingBeforeLeaveSpy = sinon.spy(
|
|
11945
|
+
MeetingUtil,
|
|
11946
|
+
'requireHostEndMeetingBeforeLeave'
|
|
11947
|
+
);
|
|
11679
11948
|
canUserLowerSomeoneElsesHandSpy = sinon.spy(MeetingUtil, 'canUserLowerSomeoneElsesHand');
|
|
11680
11949
|
waitingForOthersToJoinSpy = sinon.spy(MeetingUtil, 'waitingForOthersToJoin');
|
|
11681
11950
|
canSendReactionsSpy = sinon.spy(MeetingUtil, 'canSendReactions');
|
|
@@ -11692,12 +11961,22 @@ describe('plugin-meetings', () => {
|
|
|
11692
11961
|
MeetingUtil,
|
|
11693
11962
|
'isSpokenLanguageAutoDetectionEnabled'
|
|
11694
11963
|
);
|
|
11964
|
+
canAttendeeRequestAiAssistantEnabledSpy = sinon.spy(
|
|
11965
|
+
MeetingUtil,
|
|
11966
|
+
'canAttendeeRequestAiAssistantEnabled'
|
|
11967
|
+
);
|
|
11968
|
+
attendeeRequestAiAssistantDeclinedAllSpy = sinon.spy(
|
|
11969
|
+
MeetingUtil,
|
|
11970
|
+
'attendeeRequestAiAssistantDeclinedAll'
|
|
11971
|
+
);
|
|
11695
11972
|
});
|
|
11696
11973
|
|
|
11697
11974
|
afterEach(() => {
|
|
11698
11975
|
inMeetingActionsSetSpy.restore();
|
|
11699
11976
|
waitingForOthersToJoinSpy.restore();
|
|
11700
11977
|
showAutoEndMeetingWarningSpy.restore();
|
|
11978
|
+
canAttendeeRequestAiAssistantEnabledSpy.restore();
|
|
11979
|
+
attendeeRequestAiAssistantDeclinedAllSpy.restore();
|
|
11701
11980
|
});
|
|
11702
11981
|
|
|
11703
11982
|
forEach(
|
|
@@ -12221,6 +12500,7 @@ describe('plugin-meetings', () => {
|
|
|
12221
12500
|
const userDisplayHints = ['LOCK_CONTROL_UNLOCK'];
|
|
12222
12501
|
meeting.userDisplayHints = ['LOCK_CONTROL_UNLOCK'];
|
|
12223
12502
|
meeting.meetingInfo.supportVoIP = true;
|
|
12503
|
+
meeting.roles = [];
|
|
12224
12504
|
|
|
12225
12505
|
meeting.updateMeetingActions();
|
|
12226
12506
|
|
|
@@ -12236,6 +12516,7 @@ describe('plugin-meetings', () => {
|
|
|
12236
12516
|
assert.calledWith(canUnsetDisallowUnmuteSpy, userDisplayHints);
|
|
12237
12517
|
assert.calledWith(canUserRaiseHandSpy, userDisplayHints);
|
|
12238
12518
|
assert.calledWith(bothLeaveAndEndMeetingAvailableSpy, userDisplayHints);
|
|
12519
|
+
assert.calledWith(requireHostEndMeetingBeforeLeaveSpy, userDisplayHints);
|
|
12239
12520
|
assert.calledWith(canUserLowerAllHandsSpy, userDisplayHints);
|
|
12240
12521
|
assert.calledWith(canUserLowerSomeoneElsesHandSpy, userDisplayHints);
|
|
12241
12522
|
assert.calledWith(waitingForOthersToJoinSpy, userDisplayHints);
|
|
@@ -12247,6 +12528,12 @@ describe('plugin-meetings', () => {
|
|
|
12247
12528
|
assert.calledWith(canMoveToLobbySpy, userDisplayHints);
|
|
12248
12529
|
assert.calledWith(showAutoEndMeetingWarningSpy, userDisplayHints);
|
|
12249
12530
|
assert.calledWith(isSpokenLanguageAutoDetectionEnabledSpy, userDisplayHints);
|
|
12531
|
+
assert.calledWith(
|
|
12532
|
+
canAttendeeRequestAiAssistantEnabledSpy,
|
|
12533
|
+
userDisplayHints,
|
|
12534
|
+
meeting.roles
|
|
12535
|
+
);
|
|
12536
|
+
assert.calledWith(attendeeRequestAiAssistantDeclinedAllSpy, userDisplayHints);
|
|
12250
12537
|
|
|
12251
12538
|
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
|
12252
12539
|
requiredHints: [DISPLAY_HINTS.MUTE_ALL],
|
|
@@ -12389,33 +12676,72 @@ describe('plugin-meetings', () => {
|
|
|
12389
12676
|
|
|
12390
12677
|
describe('#handleDataChannelUrlChange', () => {
|
|
12391
12678
|
let updateLLMConnectionSpy;
|
|
12679
|
+
let updatePSDataChannelSpy;
|
|
12392
12680
|
|
|
12393
12681
|
beforeEach(() => {
|
|
12394
12682
|
updateLLMConnectionSpy = sinon.spy(meeting, 'updateLLMConnection');
|
|
12683
|
+
updatePSDataChannelSpy = sinon.stub(meeting.webinar, 'updatePSDataChannel').resolves();
|
|
12684
|
+
meeting.webinar.isJoinPracticeSessionDataChannel = sinon.stub().returns(false);
|
|
12395
12685
|
});
|
|
12396
12686
|
|
|
12397
|
-
const check = (
|
|
12398
|
-
|
|
12687
|
+
const check = (
|
|
12688
|
+
url,
|
|
12689
|
+
practiceSessionDatachannelUrl,
|
|
12690
|
+
{expectedMainCalled, expectedPracticeCalled}
|
|
12691
|
+
) => {
|
|
12692
|
+
meeting.handleDataChannelUrlChange(url, practiceSessionDatachannelUrl);
|
|
12399
12693
|
|
|
12400
|
-
if (
|
|
12694
|
+
if (expectedMainCalled) {
|
|
12401
12695
|
assert.calledWith(updateLLMConnectionSpy);
|
|
12402
12696
|
} else {
|
|
12403
12697
|
assert.notCalled(updateLLMConnectionSpy);
|
|
12404
12698
|
}
|
|
12699
|
+
|
|
12700
|
+
if (expectedPracticeCalled) {
|
|
12701
|
+
assert.calledWith(updatePSDataChannelSpy);
|
|
12702
|
+
} else {
|
|
12703
|
+
assert.notCalled(updatePSDataChannelSpy);
|
|
12704
|
+
}
|
|
12405
12705
|
};
|
|
12406
12706
|
|
|
12407
12707
|
it('calls deferred updateLLMConnection if datachannelURL is set and the enableAutomaticLLM is true', () => {
|
|
12408
12708
|
meeting.config.enableAutomaticLLM = true;
|
|
12409
|
-
check('some url', true);
|
|
12709
|
+
check('some url', undefined, {expectedMainCalled: true, expectedPracticeCalled: false});
|
|
12410
12710
|
});
|
|
12411
12711
|
|
|
12412
12712
|
it('does not call updateLLMConnection if datachannelURL is undefined', () => {
|
|
12413
12713
|
meeting.config.enableAutomaticLLM = true;
|
|
12414
|
-
check(undefined,
|
|
12714
|
+
check(undefined, undefined, {
|
|
12715
|
+
expectedMainCalled: false,
|
|
12716
|
+
expectedPracticeCalled: false,
|
|
12717
|
+
});
|
|
12415
12718
|
});
|
|
12416
12719
|
|
|
12417
12720
|
it('does not call updateLLMConnection if enableAutomaticLLM is false', () => {
|
|
12418
|
-
check('some url',
|
|
12721
|
+
check('some url', 'some practice url', {
|
|
12722
|
+
expectedMainCalled: false,
|
|
12723
|
+
expectedPracticeCalled: false,
|
|
12724
|
+
});
|
|
12725
|
+
});
|
|
12726
|
+
|
|
12727
|
+
it('calls updatePSDataChannel when practice-session routing is active', () => {
|
|
12728
|
+
meeting.config.enableAutomaticLLM = true;
|
|
12729
|
+
meeting.webinar.isJoinPracticeSessionDataChannel.returns(true);
|
|
12730
|
+
|
|
12731
|
+
check('some url', 'some practice url', {
|
|
12732
|
+
expectedMainCalled: true,
|
|
12733
|
+
expectedPracticeCalled: true,
|
|
12734
|
+
});
|
|
12735
|
+
});
|
|
12736
|
+
|
|
12737
|
+
it('does not call updatePSDataChannel when the main datachannelURL is undefined', () => {
|
|
12738
|
+
meeting.config.enableAutomaticLLM = true;
|
|
12739
|
+
meeting.webinar.isJoinPracticeSessionDataChannel.returns(true);
|
|
12740
|
+
|
|
12741
|
+
check(undefined, 'some practice url', {
|
|
12742
|
+
expectedMainCalled: false,
|
|
12743
|
+
expectedPracticeCalled: false,
|
|
12744
|
+
});
|
|
12419
12745
|
});
|
|
12420
12746
|
});
|
|
12421
12747
|
|
|
@@ -12424,16 +12750,20 @@ describe('plugin-meetings', () => {
|
|
|
12424
12750
|
webex.internal.llm.isConnected = sinon.stub().returns(false);
|
|
12425
12751
|
webex.internal.llm.getLocusUrl = sinon.stub();
|
|
12426
12752
|
webex.internal.llm.getDatachannelUrl = sinon.stub();
|
|
12427
|
-
webex.internal.llm.registerAndConnect = sinon
|
|
12428
|
-
|
|
12429
|
-
|
|
12430
|
-
webex.internal.llm.
|
|
12431
|
-
|
|
12432
|
-
|
|
12753
|
+
webex.internal.llm.registerAndConnect = sinon.stub().resolves('something');
|
|
12754
|
+
webex.internal.llm.disconnectLLM = sinon.stub().resolves();
|
|
12755
|
+
webex.internal.llm.on = sinon.stub();
|
|
12756
|
+
webex.internal.llm.off = sinon.stub();
|
|
12757
|
+
webex.internal.llm.getDatachannelToken = sinon.stub().returns(undefined);
|
|
12758
|
+
webex.internal.llm.setDatachannelToken = sinon.stub();
|
|
12759
|
+
|
|
12433
12760
|
meeting.processRelayEvent = sinon.stub();
|
|
12761
|
+
meeting.processLocusLLMEvent = sinon.stub();
|
|
12762
|
+
meeting.clearLLMHealthCheckTimer = sinon.stub();
|
|
12763
|
+
meeting.startLLMHealthCheckTimer = sinon.stub();
|
|
12764
|
+
|
|
12434
12765
|
meeting.webinar.isJoinPracticeSessionDataChannel = sinon.stub().returns(false);
|
|
12435
12766
|
});
|
|
12436
|
-
|
|
12437
12767
|
it('does not connect if the call is not joined yet', async () => {
|
|
12438
12768
|
meeting.joinedWith = {state: 'any other state'};
|
|
12439
12769
|
webex.internal.llm.getLocusUrl.returns('a url');
|
|
@@ -12447,31 +12777,21 @@ describe('plugin-meetings', () => {
|
|
|
12447
12777
|
assert.equal(result, undefined);
|
|
12448
12778
|
assert.notCalled(meeting.webex.internal.llm.on);
|
|
12449
12779
|
});
|
|
12450
|
-
|
|
12451
12780
|
it('returns undefined if llm is already connected and the locus url is unchanged', async () => {
|
|
12452
12781
|
meeting.joinedWith = {state: 'JOINED'};
|
|
12453
|
-
|
|
12454
|
-
|
|
12455
|
-
|
|
12456
|
-
|
|
12457
|
-
meeting.locusInfo = {url: 'a url', info: {datachannelUrl: 'a datachannel url'}};
|
|
12458
|
-
|
|
12459
|
-
const result = await meeting.updateLLMConnection();
|
|
12460
|
-
|
|
12461
|
-
assert.notCalled(webex.internal.llm.registerAndConnect);
|
|
12462
|
-
assert.notCalled(webex.internal.llm.disconnectLLM);
|
|
12463
|
-
assert.equal(result, undefined);
|
|
12464
|
-
assert.notCalled(meeting.webex.internal.llm.on);
|
|
12465
|
-
});
|
|
12466
|
-
|
|
12467
|
-
it('connects if not already connected', async () => {
|
|
12468
|
-
meeting.joinedWith = {state: 'JOINED'};
|
|
12469
|
-
meeting.locusInfo = {url: 'a url', info: {datachannelUrl: 'a datachannel url'}};
|
|
12782
|
+
meeting.locusInfo = {
|
|
12783
|
+
url: 'a url',
|
|
12784
|
+
info: {datachannelUrl: 'a datachannel url'},
|
|
12785
|
+
};
|
|
12470
12786
|
|
|
12471
12787
|
const result = await meeting.updateLLMConnection();
|
|
12472
|
-
|
|
12473
12788
|
assert.notCalled(webex.internal.llm.disconnectLLM);
|
|
12474
|
-
assert.
|
|
12789
|
+
assert.calledWithExactly(
|
|
12790
|
+
webex.internal.llm.registerAndConnect,
|
|
12791
|
+
'a url',
|
|
12792
|
+
'a datachannel url',
|
|
12793
|
+
undefined
|
|
12794
|
+
);
|
|
12475
12795
|
assert.equal(result, 'something');
|
|
12476
12796
|
assert.calledWithExactly(
|
|
12477
12797
|
meeting.webex.internal.llm.off,
|
|
@@ -12494,27 +12814,49 @@ describe('plugin-meetings', () => {
|
|
|
12494
12814
|
meeting.processLocusLLMEvent
|
|
12495
12815
|
);
|
|
12496
12816
|
});
|
|
12817
|
+
it('connects if not already connected', async () => {
|
|
12818
|
+
meeting.joinedWith = {state: 'JOINED'};
|
|
12819
|
+
meeting.locusInfo = {url: 'a url', info: {datachannelUrl: 'a datachannel url'}};
|
|
12820
|
+
|
|
12821
|
+
const result = await meeting.updateLLMConnection();
|
|
12497
12822
|
|
|
12498
|
-
|
|
12823
|
+
assert.notCalled(webex.internal.llm.disconnectLLM);
|
|
12824
|
+
assert.calledWithExactly(
|
|
12825
|
+
webex.internal.llm.registerAndConnect,
|
|
12826
|
+
'a url',
|
|
12827
|
+
'a datachannel url',
|
|
12828
|
+
undefined
|
|
12829
|
+
);
|
|
12830
|
+
assert.equal(result, 'something');
|
|
12831
|
+
});
|
|
12832
|
+
it('disconnects if the locus url has changed', async () => {
|
|
12499
12833
|
meeting.joinedWith = {state: 'JOINED'};
|
|
12834
|
+
|
|
12500
12835
|
webex.internal.llm.isConnected.returns(true);
|
|
12501
12836
|
webex.internal.llm.getLocusUrl.returns('a url');
|
|
12502
|
-
webex.internal.llm.getDatachannelUrl.returns('a datachannel url');
|
|
12503
12837
|
|
|
12504
|
-
meeting.locusInfo = {
|
|
12838
|
+
meeting.locusInfo = {
|
|
12839
|
+
url: 'a different url',
|
|
12840
|
+
info: {datachannelUrl: 'a datachannel url'},
|
|
12841
|
+
self: {},
|
|
12842
|
+
};
|
|
12505
12843
|
|
|
12506
12844
|
const result = await meeting.updateLLMConnection();
|
|
12507
12845
|
|
|
12508
|
-
assert.
|
|
12846
|
+
assert.calledWithExactly(webex.internal.llm.disconnectLLM, {
|
|
12509
12847
|
code: 3050,
|
|
12510
12848
|
reason: 'done (permanent)',
|
|
12511
12849
|
});
|
|
12512
|
-
|
|
12850
|
+
|
|
12851
|
+
assert.calledWithExactly(
|
|
12513
12852
|
webex.internal.llm.registerAndConnect,
|
|
12514
12853
|
'a different url',
|
|
12515
|
-
'a datachannel url'
|
|
12854
|
+
'a datachannel url',
|
|
12855
|
+
undefined
|
|
12516
12856
|
);
|
|
12857
|
+
|
|
12517
12858
|
assert.equal(result, 'something');
|
|
12859
|
+
|
|
12518
12860
|
assert.calledWithExactly(
|
|
12519
12861
|
meeting.webex.internal.llm.off,
|
|
12520
12862
|
'event:relay.event',
|
|
@@ -12526,6 +12868,7 @@ describe('plugin-meetings', () => {
|
|
|
12526
12868
|
meeting.processLocusLLMEvent
|
|
12527
12869
|
);
|
|
12528
12870
|
assert.callCount(meeting.webex.internal.llm.off, 4);
|
|
12871
|
+
|
|
12529
12872
|
assert.calledWithExactly(
|
|
12530
12873
|
meeting.webex.internal.llm.on,
|
|
12531
12874
|
'event:relay.event',
|
|
@@ -12536,28 +12879,37 @@ describe('plugin-meetings', () => {
|
|
|
12536
12879
|
'event:locus.state_message',
|
|
12537
12880
|
meeting.processLocusLLMEvent
|
|
12538
12881
|
);
|
|
12882
|
+
assert.isFalse(
|
|
12883
|
+
meeting.webex.internal.llm.off.calledWithExactly('online', meeting.handleLLMOnline)
|
|
12884
|
+
);
|
|
12539
12885
|
});
|
|
12540
|
-
|
|
12541
|
-
it('disconnects it first if the data channel url has changed', async () => {
|
|
12886
|
+
it('disconnects if the data channel url has changed', async () => {
|
|
12542
12887
|
meeting.joinedWith = {state: 'JOINED'};
|
|
12543
12888
|
webex.internal.llm.isConnected.returns(true);
|
|
12544
12889
|
webex.internal.llm.getLocusUrl.returns('a url');
|
|
12545
|
-
webex.internal.llm.getDatachannelUrl.returns('a datachannel url');
|
|
12546
12890
|
|
|
12547
|
-
meeting.locusInfo = {
|
|
12891
|
+
meeting.locusInfo = {
|
|
12892
|
+
url: 'a url',
|
|
12893
|
+
info: {datachannelUrl: 'a different datachannel url'},
|
|
12894
|
+
self: {},
|
|
12895
|
+
};
|
|
12548
12896
|
|
|
12549
12897
|
const result = await meeting.updateLLMConnection();
|
|
12550
12898
|
|
|
12551
|
-
assert.
|
|
12899
|
+
assert.calledWithExactly(webex.internal.llm.disconnectLLM, {
|
|
12552
12900
|
code: 3050,
|
|
12553
12901
|
reason: 'done (permanent)',
|
|
12554
12902
|
});
|
|
12555
|
-
|
|
12903
|
+
|
|
12904
|
+
assert.calledWithExactly(
|
|
12556
12905
|
webex.internal.llm.registerAndConnect,
|
|
12557
12906
|
'a url',
|
|
12558
|
-
'a different datachannel url'
|
|
12907
|
+
'a different datachannel url',
|
|
12908
|
+
undefined
|
|
12559
12909
|
);
|
|
12910
|
+
|
|
12560
12911
|
assert.equal(result, 'something');
|
|
12912
|
+
|
|
12561
12913
|
assert.calledWithExactly(
|
|
12562
12914
|
meeting.webex.internal.llm.off,
|
|
12563
12915
|
'event:relay.event',
|
|
@@ -12568,6 +12920,7 @@ describe('plugin-meetings', () => {
|
|
|
12568
12920
|
'event:locus.state_message',
|
|
12569
12921
|
meeting.processLocusLLMEvent
|
|
12570
12922
|
);
|
|
12923
|
+
|
|
12571
12924
|
assert.calledWithExactly(
|
|
12572
12925
|
meeting.webex.internal.llm.on,
|
|
12573
12926
|
'event:relay.event',
|
|
@@ -12578,8 +12931,10 @@ describe('plugin-meetings', () => {
|
|
|
12578
12931
|
'event:locus.state_message',
|
|
12579
12932
|
meeting.processLocusLLMEvent
|
|
12580
12933
|
);
|
|
12934
|
+
assert.isFalse(
|
|
12935
|
+
meeting.webex.internal.llm.off.calledWithExactly('online', meeting.handleLLMOnline)
|
|
12936
|
+
);
|
|
12581
12937
|
});
|
|
12582
|
-
|
|
12583
12938
|
it('disconnects when the state is not JOINED', async () => {
|
|
12584
12939
|
meeting.joinedWith = {state: 'any other state'};
|
|
12585
12940
|
webex.internal.llm.isConnected.returns(true);
|
|
@@ -12589,9 +12944,38 @@ describe('plugin-meetings', () => {
|
|
|
12589
12944
|
|
|
12590
12945
|
const result = await meeting.updateLLMConnection();
|
|
12591
12946
|
|
|
12592
|
-
assert.calledWith(webex.internal.llm.disconnectLLM,
|
|
12947
|
+
assert.calledWith(webex.internal.llm.disconnectLLM, {
|
|
12948
|
+
code: 3050,
|
|
12949
|
+
reason: 'done (permanent)',
|
|
12950
|
+
});
|
|
12593
12951
|
assert.notCalled(webex.internal.llm.registerAndConnect);
|
|
12594
12952
|
assert.equal(result, undefined);
|
|
12953
|
+
assert.isFalse(
|
|
12954
|
+
meeting.webex.internal.llm.off.calledWithExactly('online', meeting.handleLLMOnline)
|
|
12955
|
+
);
|
|
12956
|
+
});
|
|
12957
|
+
it('rethrows disconnect errors during reconnect cleanup after removing relay listeners and timer', async () => {
|
|
12958
|
+
const disconnectError = new Error('disconnect failed');
|
|
12959
|
+
|
|
12960
|
+
meeting.joinedWith = {state: 'JOINED'};
|
|
12961
|
+
webex.internal.llm.isConnected.returns(true);
|
|
12962
|
+
webex.internal.llm.getLocusUrl.returns('a url');
|
|
12963
|
+
webex.internal.llm.disconnectLLM.rejects(disconnectError);
|
|
12964
|
+
|
|
12965
|
+
meeting.locusInfo = {
|
|
12966
|
+
url: 'a different url',
|
|
12967
|
+
info: {datachannelUrl: 'a datachannel url'},
|
|
12968
|
+
self: {},
|
|
12969
|
+
};
|
|
12970
|
+
|
|
12971
|
+
try {
|
|
12972
|
+
await meeting.updateLLMConnection();
|
|
12973
|
+
assert.fail('Expected updateLLMConnection to reject when disconnectLLM fails');
|
|
12974
|
+
} catch (error) {
|
|
12975
|
+
assert.equal(error, disconnectError);
|
|
12976
|
+
}
|
|
12977
|
+
|
|
12978
|
+
assert.notCalled(webex.internal.llm.registerAndConnect);
|
|
12595
12979
|
assert.calledWithExactly(
|
|
12596
12980
|
meeting.webex.internal.llm.off,
|
|
12597
12981
|
'event:relay.event',
|
|
@@ -12602,22 +12986,149 @@ describe('plugin-meetings', () => {
|
|
|
12602
12986
|
'event:locus.state_message',
|
|
12603
12987
|
meeting.processLocusLLMEvent
|
|
12604
12988
|
);
|
|
12989
|
+
assert.isFalse(
|
|
12990
|
+
meeting.webex.internal.llm.off.calledWithExactly('online', meeting.handleLLMOnline)
|
|
12991
|
+
);
|
|
12992
|
+
assert.calledOnce(meeting.clearLLMHealthCheckTimer);
|
|
12605
12993
|
});
|
|
12606
|
-
|
|
12607
|
-
it('connect ps data channel if ps started in webinar', async () => {
|
|
12994
|
+
it('still need connect main session data channel when PS started', async () => {
|
|
12608
12995
|
meeting.joinedWith = {state: 'JOINED'};
|
|
12609
12996
|
meeting.locusInfo = {
|
|
12610
12997
|
url: 'a url',
|
|
12611
12998
|
info: {
|
|
12612
12999
|
datachannelUrl: 'a datachannel url',
|
|
12613
|
-
practiceSessionDatachannelUrl: '
|
|
13000
|
+
practiceSessionDatachannelUrl: 'ps-url',
|
|
12614
13001
|
},
|
|
12615
13002
|
};
|
|
12616
|
-
meeting.webinar.isJoinPracticeSessionDataChannel
|
|
13003
|
+
meeting.webinar.isJoinPracticeSessionDataChannel.returns(true);
|
|
13004
|
+
|
|
12617
13005
|
await meeting.updateLLMConnection();
|
|
12618
13006
|
|
|
12619
|
-
assert.
|
|
12620
|
-
|
|
13007
|
+
assert.calledWithExactly(
|
|
13008
|
+
webex.internal.llm.registerAndConnect,
|
|
13009
|
+
'a url',
|
|
13010
|
+
'a datachannel url',
|
|
13011
|
+
undefined
|
|
13012
|
+
);
|
|
13013
|
+
});
|
|
13014
|
+
it('passes dataChannelToken to registerAndConnect', async () => {
|
|
13015
|
+
meeting.joinedWith = {state: 'JOINED'};
|
|
13016
|
+
meeting.locusInfo = {
|
|
13017
|
+
url: 'a url',
|
|
13018
|
+
info: {datachannelUrl: 'a datachannel url'},
|
|
13019
|
+
self: {datachannelToken: 'token-123'},
|
|
13020
|
+
};
|
|
13021
|
+
|
|
13022
|
+
webex.internal.llm.getDatachannelToken.returns(undefined);
|
|
13023
|
+
|
|
13024
|
+
await meeting.updateLLMConnection();
|
|
13025
|
+
|
|
13026
|
+
assert.calledWithExactly(
|
|
13027
|
+
webex.internal.llm.registerAndConnect,
|
|
13028
|
+
'a url',
|
|
13029
|
+
'a datachannel url',
|
|
13030
|
+
'token-123'
|
|
13031
|
+
);
|
|
13032
|
+
assert.calledWithExactly(webex.internal.llm.setDatachannelToken, 'token-123', 'llm-default-session');
|
|
13033
|
+
});
|
|
13034
|
+
it('prefers refreshed token over locus self token', async () => {
|
|
13035
|
+
meeting.joinedWith = {state: 'JOINED'};
|
|
13036
|
+
meeting.locusInfo = {
|
|
13037
|
+
url: 'a url',
|
|
13038
|
+
info: {datachannelUrl: 'a datachannel url'},
|
|
13039
|
+
self: {datachannelToken: 'locus-token'},
|
|
13040
|
+
};
|
|
13041
|
+
|
|
13042
|
+
webex.internal.llm.getDatachannelToken.withArgs('llm-default-session').returns('refreshed-token');
|
|
13043
|
+
|
|
13044
|
+
await meeting.updateLLMConnection();
|
|
13045
|
+
|
|
13046
|
+
assert.calledWithExactly(
|
|
13047
|
+
webex.internal.llm.registerAndConnect,
|
|
13048
|
+
'a url',
|
|
13049
|
+
'a datachannel url',
|
|
13050
|
+
'refreshed-token'
|
|
13051
|
+
);
|
|
13052
|
+
|
|
13053
|
+
assert.notCalled(webex.internal.llm.setDatachannelToken);
|
|
13054
|
+
});
|
|
13055
|
+
|
|
13056
|
+
it('does not pass token when data channel with jwt token is disabled', async () => {
|
|
13057
|
+
meeting.joinedWith = {state: 'JOINED'};
|
|
13058
|
+
meeting.locusInfo = {
|
|
13059
|
+
url: 'a url',
|
|
13060
|
+
info: {datachannelUrl: 'a datachannel url'},
|
|
13061
|
+
self: {datachannelToken: 'token-123'},
|
|
13062
|
+
};
|
|
13063
|
+
|
|
13064
|
+
webex.internal.llm.getDatachannelToken.returns(undefined);
|
|
13065
|
+
webex.internal.llm.isDataChannelTokenEnabled = sinon.stub().resolves(false);
|
|
13066
|
+
|
|
13067
|
+
await meeting.updateLLMConnection();
|
|
13068
|
+
|
|
13069
|
+
assert.calledWithExactly(
|
|
13070
|
+
webex.internal.llm.registerAndConnect,
|
|
13071
|
+
'a url',
|
|
13072
|
+
'a datachannel url',
|
|
13073
|
+
'token-123'
|
|
13074
|
+
);
|
|
13075
|
+
assert.calledWithExactly(webex.internal.llm.setDatachannelToken, 'token-123', 'llm-default-session');
|
|
13076
|
+
});
|
|
13077
|
+
|
|
13078
|
+
describe('#clearMeetingData', () => {
|
|
13079
|
+
beforeEach(() => {
|
|
13080
|
+
webex.internal.llm.isConnected = sinon.stub().returns(true);
|
|
13081
|
+
webex.internal.llm.disconnectLLM = sinon.stub().resolves();
|
|
13082
|
+
webex.internal.llm.off = sinon.stub();
|
|
13083
|
+
meeting.annotation.deregisterEvents = sinon.stub();
|
|
13084
|
+
meeting.clearLLMHealthCheckTimer = sinon.stub();
|
|
13085
|
+
meeting.stopTranscription = sinon.stub();
|
|
13086
|
+
meeting.transcription = {};
|
|
13087
|
+
meeting.shareStatus = 'no-share';
|
|
13088
|
+
});
|
|
13089
|
+
|
|
13090
|
+
it('disconnects llm and removes online and relay listeners during meeting data cleanup', async () => {
|
|
13091
|
+
await meeting.clearMeetingData();
|
|
13092
|
+
|
|
13093
|
+
assert.calledOnceWithExactly(webex.internal.llm.disconnectLLM, {
|
|
13094
|
+
code: 3050,
|
|
13095
|
+
reason: 'done (permanent)',
|
|
13096
|
+
});
|
|
13097
|
+
assert.calledWithExactly(webex.internal.llm.off, 'online', meeting.handleLLMOnline);
|
|
13098
|
+
assert.calledWithExactly(
|
|
13099
|
+
webex.internal.llm.off,
|
|
13100
|
+
'event:relay.event',
|
|
13101
|
+
meeting.processRelayEvent
|
|
13102
|
+
);
|
|
13103
|
+
assert.calledWithExactly(
|
|
13104
|
+
webex.internal.llm.off,
|
|
13105
|
+
'event:locus.state_message',
|
|
13106
|
+
meeting.processLocusLLMEvent
|
|
13107
|
+
);
|
|
13108
|
+
assert.calledOnce(meeting.clearLLMHealthCheckTimer);
|
|
13109
|
+
assert.calledOnce(meeting.stopTranscription);
|
|
13110
|
+
assert.calledOnce(meeting.annotation.deregisterEvents);
|
|
13111
|
+
});
|
|
13112
|
+
it('continues cleanup when disconnectLLM fails during meeting data cleanup', async () => {
|
|
13113
|
+
webex.internal.llm.disconnectLLM.rejects(new Error('disconnect failed'));
|
|
13114
|
+
|
|
13115
|
+
await meeting.clearMeetingData();
|
|
13116
|
+
|
|
13117
|
+
assert.calledWithExactly(webex.internal.llm.off, 'online', meeting.handleLLMOnline);
|
|
13118
|
+
assert.calledWithExactly(
|
|
13119
|
+
webex.internal.llm.off,
|
|
13120
|
+
'event:relay.event',
|
|
13121
|
+
meeting.processRelayEvent
|
|
13122
|
+
);
|
|
13123
|
+
assert.calledWithExactly(
|
|
13124
|
+
webex.internal.llm.off,
|
|
13125
|
+
'event:locus.state_message',
|
|
13126
|
+
meeting.processLocusLLMEvent
|
|
13127
|
+
);
|
|
13128
|
+
assert.calledOnce(meeting.clearLLMHealthCheckTimer);
|
|
13129
|
+
assert.calledOnce(meeting.stopTranscription);
|
|
13130
|
+
assert.calledOnce(meeting.annotation.deregisterEvents);
|
|
13131
|
+
});
|
|
12621
13132
|
});
|
|
12622
13133
|
});
|
|
12623
13134
|
|
|
@@ -12628,6 +13139,7 @@ describe('plugin-meetings', () => {
|
|
|
12628
13139
|
|
|
12629
13140
|
it('should read the locus object, set on the meeting and return null', () => {
|
|
12630
13141
|
const dataSets = {someFakeStuff: 'dataSet'};
|
|
13142
|
+
const metadata = {some: 'metadata'};
|
|
12631
13143
|
|
|
12632
13144
|
meeting.setLocus({
|
|
12633
13145
|
mediaConnections: [test1],
|
|
@@ -12637,12 +13149,14 @@ describe('plugin-meetings', () => {
|
|
|
12637
13149
|
mediaId: uuid3,
|
|
12638
13150
|
locus: {host: {id: uuid4}},
|
|
12639
13151
|
dataSets,
|
|
13152
|
+
metadata,
|
|
12640
13153
|
});
|
|
12641
13154
|
assert.calledOnce(meeting.locusInfo.initialSetup);
|
|
12642
13155
|
assert.calledWith(meeting.locusInfo.initialSetup, {
|
|
12643
13156
|
trigger: 'join-response',
|
|
12644
13157
|
locus: {host: {id: uuid4}},
|
|
12645
13158
|
dataSets,
|
|
13159
|
+
metadata,
|
|
12646
13160
|
});
|
|
12647
13161
|
assert.equal(meeting.mediaConnections, test1);
|
|
12648
13162
|
assert.equal(meeting.locusUrl, url1);
|
|
@@ -14184,6 +14698,69 @@ describe('plugin-meetings', () => {
|
|
|
14184
14698
|
assert.calledOnce(meeting.meetingRequest.keepAlive);
|
|
14185
14699
|
});
|
|
14186
14700
|
});
|
|
14701
|
+
describe('#refreshDataChannelToken()', () => {
|
|
14702
|
+
let meeting;
|
|
14703
|
+
|
|
14704
|
+
beforeEach(() => {
|
|
14705
|
+
meeting = Object.create(Meeting.prototype);
|
|
14706
|
+
meeting.locusUrl = 'https://locus.example.com';
|
|
14707
|
+
meeting.meetingRequest = {
|
|
14708
|
+
fetchDatachannelToken: sinon.stub().resolves({
|
|
14709
|
+
body: {datachannelToken: 'mock-token'},
|
|
14710
|
+
}),
|
|
14711
|
+
};
|
|
14712
|
+
meeting.members = {
|
|
14713
|
+
selfId: 'self-123',
|
|
14714
|
+
};
|
|
14715
|
+
meeting.webinar = {
|
|
14716
|
+
isJoinPracticeSessionDataChannel: sinon.stub().returns(true),
|
|
14717
|
+
};
|
|
14718
|
+
});
|
|
14719
|
+
|
|
14720
|
+
it('calls fetchDatachannelToken with correct parameters', async () => {
|
|
14721
|
+
await meeting.refreshDataChannelToken();
|
|
14722
|
+
|
|
14723
|
+
sinon.assert.calledOnce(meeting.meetingRequest.fetchDatachannelToken);
|
|
14724
|
+
|
|
14725
|
+
sinon.assert.calledWith(meeting.meetingRequest.fetchDatachannelToken, {
|
|
14726
|
+
locusUrl: 'https://locus.example.com',
|
|
14727
|
+
requestingParticipantId: 'self-123',
|
|
14728
|
+
isPracticeSession: true,
|
|
14729
|
+
});
|
|
14730
|
+
});
|
|
14731
|
+
|
|
14732
|
+
it('returns the correct structured result', async () => {
|
|
14733
|
+
const result = await meeting.refreshDataChannelToken();
|
|
14734
|
+
|
|
14735
|
+
expect(result).to.deep.equal({
|
|
14736
|
+
body: {
|
|
14737
|
+
datachannelToken: 'mock-token',
|
|
14738
|
+
dataChannelTokenType: 'llm-practice-session',
|
|
14739
|
+
},
|
|
14740
|
+
});
|
|
14741
|
+
});
|
|
14742
|
+
});
|
|
14743
|
+
describe('#getDataChannelTokenType', () => {
|
|
14744
|
+
it('returns PracticeSession when webinar is in practice session mode', () => {
|
|
14745
|
+
meeting.webinar = {
|
|
14746
|
+
isJoinPracticeSessionDataChannel: sinon.stub().returns(true),
|
|
14747
|
+
};
|
|
14748
|
+
|
|
14749
|
+
const result = meeting.getDataChannelTokenType();
|
|
14750
|
+
|
|
14751
|
+
expect(result).to.equal('llm-practice-session');
|
|
14752
|
+
});
|
|
14753
|
+
|
|
14754
|
+
it('returns Default when not in practice session mode', () => {
|
|
14755
|
+
meeting.webinar = {
|
|
14756
|
+
isJoinPracticeSessionDataChannel: sinon.stub().returns(false),
|
|
14757
|
+
};
|
|
14758
|
+
|
|
14759
|
+
const result = meeting.getDataChannelTokenType();
|
|
14760
|
+
|
|
14761
|
+
expect(result).to.equal('llm-default-session');
|
|
14762
|
+
});
|
|
14763
|
+
});
|
|
14187
14764
|
describe('#stopKeepAlive', () => {
|
|
14188
14765
|
let clock;
|
|
14189
14766
|
const defaultKeepAliveUrl = 'keep.alive.url';
|