@webex/plugin-meetings 3.11.0-next.3 → 3.11.0-next.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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/hashTree/hashTree.js +18 -0
- package/dist/hashTree/hashTree.js.map +1 -1
- package/dist/hashTree/hashTreeParser.js +603 -266
- 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 +2 -1
- 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 +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +80 -44
- package/dist/locus-info/index.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/index.js +134 -40
- 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 +108 -2
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +76 -34
- package/dist/meetings/index.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/config.d.ts +3 -0
- package/dist/types/hashTree/hashTree.d.ts +7 -0
- package/dist/types/hashTree/hashTreeParser.d.ts +83 -12
- package/dist/types/hashTree/types.d.ts +3 -0
- package/dist/types/hashTree/utils.d.ts +6 -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/index.d.ts +27 -5
- 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 +28 -0
- package/dist/types/meetings/index.d.ts +3 -1
- 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/config.ts +3 -0
- package/src/hashTree/hashTree.ts +17 -0
- package/src/hashTree/hashTreeParser.ts +525 -188
- package/src/hashTree/types.ts +4 -0
- package/src/hashTree/utils.ts +9 -0
- package/src/index.ts +6 -1
- package/src/interceptors/constant.ts +6 -0
- package/src/interceptors/dataChannelAuthToken.ts +142 -0
- package/src/interceptors/index.ts +2 -1
- package/src/locus-info/index.ts +110 -35
- 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/index.ts +101 -22
- package/src/meeting/request.ts +42 -0
- package/src/meeting/request.type.ts +6 -0
- package/src/meeting/util.ts +132 -1
- package/src/meetings/index.ts +88 -7
- 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/hashTree/hashTree.ts +66 -0
- package/test/unit/spec/hashTree/hashTreeParser.ts +1594 -162
- package/test/unit/spec/interceptors/dataChannelAuthToken.ts +141 -0
- package/test/unit/spec/locus-info/index.js +173 -45
- package/test/unit/spec/media/MediaConnectionAwaiter.ts +41 -1
- package/test/unit/spec/media/properties.ts +12 -3
- package/test/unit/spec/meeting/index.js +414 -62
- package/test/unit/spec/meeting/request.js +64 -0
- package/test/unit/spec/meeting/utils.js +294 -22
- package/test/unit/spec/meetings/index.js +550 -10
- package/test/unit/spec/multistream/remoteMediaManager.ts +30 -0
|
@@ -41,14 +41,23 @@ describe('MediaProperties', () => {
|
|
|
41
41
|
describe('waitForMediaConnectionConnected', () => {
|
|
42
42
|
it('resolves if media connection is connected', async () => {
|
|
43
43
|
const waitForMediaConnectionConnectedResult = new Defer();
|
|
44
|
+
const correlationId = 'aaaa-bbbb-cccc-dddd';
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
let capturedInstance;
|
|
47
|
+
const stub = sinon
|
|
46
48
|
.stub(MediaConnectionAwaiter.prototype, 'waitForMediaConnectionConnected')
|
|
47
|
-
.
|
|
49
|
+
.callsFake(function () {
|
|
50
|
+
capturedInstance = this;
|
|
51
|
+
return waitForMediaConnectionConnectedResult.promise;
|
|
52
|
+
});
|
|
48
53
|
|
|
49
54
|
waitForMediaConnectionConnectedResult.resolve();
|
|
50
55
|
|
|
51
|
-
await mediaProperties.waitForMediaConnectionConnected();
|
|
56
|
+
await mediaProperties.waitForMediaConnectionConnected(correlationId);
|
|
57
|
+
|
|
58
|
+
assert.calledOnce(stub);
|
|
59
|
+
assert.equal(capturedInstance.correlationId, correlationId);
|
|
60
|
+
assert.equal(capturedInstance.webrtcMediaConnection, mockMC);
|
|
52
61
|
});
|
|
53
62
|
it('rejects if media connection is not connected', async () => {
|
|
54
63
|
const waitForMediaConnectionConnectedResult = new Defer();
|
|
@@ -123,7 +123,6 @@ import {EVENT_TRIGGERS as VOICEAEVENTS} from '@webex/internal-plugin-voicea';
|
|
|
123
123
|
import {createBrbState} from '@webex/plugin-meetings/src/meeting/brbState';
|
|
124
124
|
import JoinForbiddenError from '../../../../src/common/errors/join-forbidden-error';
|
|
125
125
|
import {EventEmitter} from 'stream';
|
|
126
|
-
|
|
127
126
|
describe('plugin-meetings', () => {
|
|
128
127
|
const logger = {
|
|
129
128
|
info: () => {},
|
|
@@ -265,6 +264,7 @@ describe('plugin-meetings', () => {
|
|
|
265
264
|
stopReachability: sinon.stub(),
|
|
266
265
|
isSubnetReachable: sinon.stub().returns(true),
|
|
267
266
|
};
|
|
267
|
+
webex.internal.llm.isDataChannelTokenEnabled = sinon.stub().resolves(false)
|
|
268
268
|
webex.internal.llm.on = sinon.stub();
|
|
269
269
|
webex.internal.newMetrics.callDiagnosticLatencies = new CallDiagnosticLatencies(
|
|
270
270
|
{},
|
|
@@ -1249,7 +1249,7 @@ describe('plugin-meetings', () => {
|
|
|
1249
1249
|
});
|
|
1250
1250
|
|
|
1251
1251
|
[
|
|
1252
|
-
{errorName: 'SdpOfferCreationError', description: 'if we fail to create the offer on first attempt'},
|
|
1252
|
+
{errorName: 'SdpOfferCreationError', description: 'if we fail to create the offer on first attempt'},
|
|
1253
1253
|
{errorName: 'WebrtcApiNotAvailableError', description: 'if RTCPeerConnection is not available'},
|
|
1254
1254
|
].forEach(({errorName, description}) => {
|
|
1255
1255
|
it(`should not attempt a retry ${description}`, async () => {
|
|
@@ -1882,6 +1882,53 @@ describe('plugin-meetings', () => {
|
|
|
1882
1882
|
fakeProcessedReaction
|
|
1883
1883
|
);
|
|
1884
1884
|
});
|
|
1885
|
+
|
|
1886
|
+
it('should process if participantId does not exist in membersCollection but has displayName in Webinar', () => {
|
|
1887
|
+
LoggerProxy.logger.warn = sinon.stub();
|
|
1888
|
+
meeting.isReactionsSupported = sinon.stub().returns(true);
|
|
1889
|
+
meeting.config.receiveReactions = true;
|
|
1890
|
+
meeting.locusInfo.info = {isWebinar: true};
|
|
1891
|
+
const fakeSendersName = 'Fake reactors name';
|
|
1892
|
+
const fakeReactionPayload = {
|
|
1893
|
+
type: 'fake_type',
|
|
1894
|
+
codepoints: 'fake_codepoints',
|
|
1895
|
+
shortcodes: 'fake_shortcodes',
|
|
1896
|
+
tone: {
|
|
1897
|
+
type: 'fake_tone_type',
|
|
1898
|
+
codepoints: 'fake_tone_codepoints',
|
|
1899
|
+
shortcodes: 'fake_tone_shortcodes',
|
|
1900
|
+
},
|
|
1901
|
+
};
|
|
1902
|
+
const fakeSenderPayload = {
|
|
1903
|
+
displayName: 'Fake reactors name',
|
|
1904
|
+
participantId: 'fake_participant_id',
|
|
1905
|
+
};
|
|
1906
|
+
const fakeProcessedReaction = {
|
|
1907
|
+
reaction: fakeReactionPayload,
|
|
1908
|
+
sender: {
|
|
1909
|
+
id: fakeSenderPayload.participantId,
|
|
1910
|
+
name: fakeSendersName,
|
|
1911
|
+
},
|
|
1912
|
+
};
|
|
1913
|
+
const fakeRelayEvent = {
|
|
1914
|
+
data: {
|
|
1915
|
+
relayType: REACTION_RELAY_TYPES.REACTION,
|
|
1916
|
+
reaction: fakeReactionPayload,
|
|
1917
|
+
sender: fakeSenderPayload,
|
|
1918
|
+
},
|
|
1919
|
+
};
|
|
1920
|
+
meeting.processRelayEvent(fakeRelayEvent);
|
|
1921
|
+
assert.calledWith(
|
|
1922
|
+
TriggerProxy.trigger,
|
|
1923
|
+
sinon.match.instanceOf(Meeting),
|
|
1924
|
+
{
|
|
1925
|
+
file: 'meeting/index',
|
|
1926
|
+
function: 'join',
|
|
1927
|
+
},
|
|
1928
|
+
EVENT_TRIGGERS.MEETING_RECEIVE_REACTIONS,
|
|
1929
|
+
fakeProcessedReaction
|
|
1930
|
+
);
|
|
1931
|
+
});
|
|
1885
1932
|
});
|
|
1886
1933
|
|
|
1887
1934
|
describe('#handleLLMOnline', () => {
|
|
@@ -3028,6 +3075,111 @@ describe('plugin-meetings', () => {
|
|
|
3028
3075
|
checkWorking({allowMediaInLobby: true});
|
|
3029
3076
|
});
|
|
3030
3077
|
|
|
3078
|
+
const setupLobbyTest = () => {
|
|
3079
|
+
meeting.roap.doTurnDiscovery = sinon
|
|
3080
|
+
.stub()
|
|
3081
|
+
.resolves({turnServerInfo: undefined, turnDiscoverySkippedReason: undefined});
|
|
3082
|
+
|
|
3083
|
+
meeting.meetingState = 'ACTIVE';
|
|
3084
|
+
meeting.locusInfo.parsedLocus = {self: {state: 'IDLE'}};
|
|
3085
|
+
meeting.isUserUnadmitted = true;
|
|
3086
|
+
|
|
3087
|
+
// Mock locusMediaRequest
|
|
3088
|
+
meeting.locusMediaRequest = {
|
|
3089
|
+
send: sinon.stub().resolves(),
|
|
3090
|
+
isConfluenceCreated: sinon.stub().returns(false),
|
|
3091
|
+
};
|
|
3092
|
+
|
|
3093
|
+
sinon.stub(RemoteMediaManagerModule, 'RemoteMediaManager').returns({
|
|
3094
|
+
start: sinon.stub().resolves(),
|
|
3095
|
+
on: sinon.stub(),
|
|
3096
|
+
logAllReceiveSlots: sinon.stub(),
|
|
3097
|
+
});
|
|
3098
|
+
|
|
3099
|
+
meeting.isMultistream = true;
|
|
3100
|
+
|
|
3101
|
+
const createFakeStream = (id) => ({
|
|
3102
|
+
on: sinon.stub(),
|
|
3103
|
+
off: sinon.stub(),
|
|
3104
|
+
userMuted: false,
|
|
3105
|
+
systemMuted: false,
|
|
3106
|
+
get muted() {
|
|
3107
|
+
return this.userMuted || this.systemMuted;
|
|
3108
|
+
},
|
|
3109
|
+
setUnmuteAllowed: sinon.stub(),
|
|
3110
|
+
setUserMuted: sinon.stub(),
|
|
3111
|
+
outputStream: {
|
|
3112
|
+
getTracks: () => [{id}],
|
|
3113
|
+
},
|
|
3114
|
+
getSettings: sinon.stub().returns({}),
|
|
3115
|
+
});
|
|
3116
|
+
|
|
3117
|
+
return {
|
|
3118
|
+
fakeMicrophoneStream: createFakeStream('fake mic'),
|
|
3119
|
+
fakeCameraStream: createFakeStream('fake camera'),
|
|
3120
|
+
};
|
|
3121
|
+
};
|
|
3122
|
+
|
|
3123
|
+
it('should not publish any local streams when in the lobby and allowPublishMediaInLobby is false', async () => {
|
|
3124
|
+
const {fakeMicrophoneStream, fakeCameraStream} = setupLobbyTest();
|
|
3125
|
+
|
|
3126
|
+
const publishStreamStub = sinon.stub();
|
|
3127
|
+
fakeMediaConnection.createSendSlot = sinon.stub().returns({
|
|
3128
|
+
publishStream: publishStreamStub,
|
|
3129
|
+
unpublishStream: sinon.stub(),
|
|
3130
|
+
setNamedMediaGroups: sinon.stub(),
|
|
3131
|
+
});
|
|
3132
|
+
|
|
3133
|
+
await meeting.addMedia({
|
|
3134
|
+
allowMediaInLobby: true,
|
|
3135
|
+
allowPublishMediaInLobby: false,
|
|
3136
|
+
audioEnabled: true,
|
|
3137
|
+
videoEnabled: true,
|
|
3138
|
+
localStreams: {
|
|
3139
|
+
microphone: fakeMicrophoneStream,
|
|
3140
|
+
camera: fakeCameraStream,
|
|
3141
|
+
},
|
|
3142
|
+
});
|
|
3143
|
+
|
|
3144
|
+
assert.notCalled(publishStreamStub);
|
|
3145
|
+
});
|
|
3146
|
+
|
|
3147
|
+
it('should publish local streams when in the lobby and allowPublishMediaInLobby is true', async () => {
|
|
3148
|
+
const {fakeMicrophoneStream, fakeCameraStream} = setupLobbyTest();
|
|
3149
|
+
|
|
3150
|
+
const audioSlot = {
|
|
3151
|
+
publishStream: sinon.stub(),
|
|
3152
|
+
unpublishStream: sinon.stub(),
|
|
3153
|
+
setNamedMediaGroups: sinon.stub(),
|
|
3154
|
+
};
|
|
3155
|
+
const videoSlot = {
|
|
3156
|
+
publishStream: sinon.stub(),
|
|
3157
|
+
unpublishStream: sinon.stub(),
|
|
3158
|
+
setNamedMediaGroups: sinon.stub(),
|
|
3159
|
+
};
|
|
3160
|
+
|
|
3161
|
+
fakeMediaConnection.createSendSlot = sinon.stub().callsFake((mediaType) => {
|
|
3162
|
+
if (mediaType === 'AUDIO-MAIN') {
|
|
3163
|
+
return audioSlot;
|
|
3164
|
+
}
|
|
3165
|
+
return videoSlot;
|
|
3166
|
+
});
|
|
3167
|
+
|
|
3168
|
+
await meeting.addMedia({
|
|
3169
|
+
allowMediaInLobby: true,
|
|
3170
|
+
allowPublishMediaInLobby: true,
|
|
3171
|
+
audioEnabled: true,
|
|
3172
|
+
videoEnabled: true,
|
|
3173
|
+
localStreams: {
|
|
3174
|
+
microphone: fakeMicrophoneStream,
|
|
3175
|
+
camera: fakeCameraStream,
|
|
3176
|
+
},
|
|
3177
|
+
});
|
|
3178
|
+
|
|
3179
|
+
assert.calledOnceWithExactly(audioSlot.publishStream, fakeMicrophoneStream);
|
|
3180
|
+
assert.calledOnceWithExactly(videoSlot.publishStream, fakeCameraStream);
|
|
3181
|
+
});
|
|
3182
|
+
|
|
3031
3183
|
it('should create rtcMetrics and pass them to Media.createMediaConnection()', async () => {
|
|
3032
3184
|
const setIntervalOriginal = window.setInterval;
|
|
3033
3185
|
window.setInterval = sinon.stub().returns(1);
|
|
@@ -9149,7 +9301,10 @@ describe('plugin-meetings', () => {
|
|
|
9149
9301
|
|
|
9150
9302
|
// check that the right things were called by the callback
|
|
9151
9303
|
assert.calledOnceWithExactly(meeting.waitForRemoteSDPAnswer);
|
|
9152
|
-
assert.calledOnceWithExactly(
|
|
9304
|
+
assert.calledOnceWithExactly(
|
|
9305
|
+
meeting.mediaProperties.waitForMediaConnectionConnected,
|
|
9306
|
+
meeting.correlationId
|
|
9307
|
+
);
|
|
9153
9308
|
});
|
|
9154
9309
|
});
|
|
9155
9310
|
|
|
@@ -12424,16 +12579,20 @@ describe('plugin-meetings', () => {
|
|
|
12424
12579
|
webex.internal.llm.isConnected = sinon.stub().returns(false);
|
|
12425
12580
|
webex.internal.llm.getLocusUrl = sinon.stub();
|
|
12426
12581
|
webex.internal.llm.getDatachannelUrl = sinon.stub();
|
|
12427
|
-
webex.internal.llm.registerAndConnect = sinon
|
|
12428
|
-
|
|
12429
|
-
|
|
12430
|
-
webex.internal.llm.
|
|
12431
|
-
|
|
12432
|
-
|
|
12582
|
+
webex.internal.llm.registerAndConnect = sinon.stub().resolves('something');
|
|
12583
|
+
webex.internal.llm.disconnectLLM = sinon.stub().resolves();
|
|
12584
|
+
webex.internal.llm.on = sinon.stub();
|
|
12585
|
+
webex.internal.llm.off = sinon.stub();
|
|
12586
|
+
webex.internal.llm.getDatachannelToken = sinon.stub().returns(undefined);
|
|
12587
|
+
webex.internal.llm.setDatachannelToken = sinon.stub();
|
|
12588
|
+
|
|
12433
12589
|
meeting.processRelayEvent = sinon.stub();
|
|
12590
|
+
meeting.processLocusLLMEvent = sinon.stub();
|
|
12591
|
+
meeting.clearLLMHealthCheckTimer = sinon.stub();
|
|
12592
|
+
meeting.startLLMHealthCheckTimer = sinon.stub();
|
|
12593
|
+
|
|
12434
12594
|
meeting.webinar.isJoinPracticeSessionDataChannel = sinon.stub().returns(false);
|
|
12435
12595
|
});
|
|
12436
|
-
|
|
12437
12596
|
it('does not connect if the call is not joined yet', async () => {
|
|
12438
12597
|
meeting.joinedWith = {state: 'any other state'};
|
|
12439
12598
|
webex.internal.llm.getLocusUrl.returns('a url');
|
|
@@ -12447,31 +12606,21 @@ describe('plugin-meetings', () => {
|
|
|
12447
12606
|
assert.equal(result, undefined);
|
|
12448
12607
|
assert.notCalled(meeting.webex.internal.llm.on);
|
|
12449
12608
|
});
|
|
12450
|
-
|
|
12451
12609
|
it('returns undefined if llm is already connected and the locus url is unchanged', async () => {
|
|
12452
12610
|
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'}};
|
|
12611
|
+
meeting.locusInfo = {
|
|
12612
|
+
url: 'a url',
|
|
12613
|
+
info: {datachannelUrl: 'a datachannel url'}
|
|
12614
|
+
};
|
|
12470
12615
|
|
|
12471
12616
|
const result = await meeting.updateLLMConnection();
|
|
12472
|
-
|
|
12473
12617
|
assert.notCalled(webex.internal.llm.disconnectLLM);
|
|
12474
|
-
assert.
|
|
12618
|
+
assert.calledWithExactly(
|
|
12619
|
+
webex.internal.llm.registerAndConnect,
|
|
12620
|
+
'a url',
|
|
12621
|
+
'a datachannel url',
|
|
12622
|
+
undefined
|
|
12623
|
+
);
|
|
12475
12624
|
assert.equal(result, 'something');
|
|
12476
12625
|
assert.calledWithExactly(
|
|
12477
12626
|
meeting.webex.internal.llm.off,
|
|
@@ -12494,27 +12643,49 @@ describe('plugin-meetings', () => {
|
|
|
12494
12643
|
meeting.processLocusLLMEvent
|
|
12495
12644
|
);
|
|
12496
12645
|
});
|
|
12646
|
+
it('connects if not already connected', async () => {
|
|
12647
|
+
meeting.joinedWith = {state: 'JOINED'};
|
|
12648
|
+
meeting.locusInfo = {url: 'a url', info: {datachannelUrl: 'a datachannel url'}};
|
|
12497
12649
|
|
|
12498
|
-
|
|
12650
|
+
const result = await meeting.updateLLMConnection();
|
|
12651
|
+
|
|
12652
|
+
assert.notCalled(webex.internal.llm.disconnectLLM);
|
|
12653
|
+
assert.calledWithExactly(
|
|
12654
|
+
webex.internal.llm.registerAndConnect,
|
|
12655
|
+
'a url',
|
|
12656
|
+
'a datachannel url',
|
|
12657
|
+
undefined
|
|
12658
|
+
);
|
|
12659
|
+
assert.equal(result, 'something');
|
|
12660
|
+
});
|
|
12661
|
+
it('disconnects if the locus url has changed', async () => {
|
|
12499
12662
|
meeting.joinedWith = {state: 'JOINED'};
|
|
12663
|
+
|
|
12500
12664
|
webex.internal.llm.isConnected.returns(true);
|
|
12501
12665
|
webex.internal.llm.getLocusUrl.returns('a url');
|
|
12502
|
-
webex.internal.llm.getDatachannelUrl.returns('a datachannel url');
|
|
12503
12666
|
|
|
12504
|
-
meeting.locusInfo = {
|
|
12667
|
+
meeting.locusInfo = {
|
|
12668
|
+
url: 'a different url',
|
|
12669
|
+
info: {datachannelUrl: 'a datachannel url'},
|
|
12670
|
+
self: {}
|
|
12671
|
+
};
|
|
12505
12672
|
|
|
12506
12673
|
const result = await meeting.updateLLMConnection();
|
|
12507
12674
|
|
|
12508
|
-
assert.
|
|
12509
|
-
|
|
12510
|
-
reason: 'done (permanent)'
|
|
12511
|
-
|
|
12512
|
-
|
|
12675
|
+
assert.calledWithExactly(
|
|
12676
|
+
webex.internal.llm.disconnectLLM,
|
|
12677
|
+
{code: 3050, reason: 'done (permanent)'}
|
|
12678
|
+
);
|
|
12679
|
+
|
|
12680
|
+
assert.calledWithExactly(
|
|
12513
12681
|
webex.internal.llm.registerAndConnect,
|
|
12514
12682
|
'a different url',
|
|
12515
|
-
'a datachannel url'
|
|
12683
|
+
'a datachannel url',
|
|
12684
|
+
undefined
|
|
12516
12685
|
);
|
|
12686
|
+
|
|
12517
12687
|
assert.equal(result, 'something');
|
|
12688
|
+
|
|
12518
12689
|
assert.calledWithExactly(
|
|
12519
12690
|
meeting.webex.internal.llm.off,
|
|
12520
12691
|
'event:relay.event',
|
|
@@ -12526,6 +12697,7 @@ describe('plugin-meetings', () => {
|
|
|
12526
12697
|
meeting.processLocusLLMEvent
|
|
12527
12698
|
);
|
|
12528
12699
|
assert.callCount(meeting.webex.internal.llm.off, 4);
|
|
12700
|
+
|
|
12529
12701
|
assert.calledWithExactly(
|
|
12530
12702
|
meeting.webex.internal.llm.on,
|
|
12531
12703
|
'event:relay.event',
|
|
@@ -12537,27 +12709,33 @@ describe('plugin-meetings', () => {
|
|
|
12537
12709
|
meeting.processLocusLLMEvent
|
|
12538
12710
|
);
|
|
12539
12711
|
});
|
|
12540
|
-
|
|
12541
|
-
it('disconnects it first if the data channel url has changed', async () => {
|
|
12712
|
+
it('disconnects if the data channel url has changed', async () => {
|
|
12542
12713
|
meeting.joinedWith = {state: 'JOINED'};
|
|
12543
12714
|
webex.internal.llm.isConnected.returns(true);
|
|
12544
12715
|
webex.internal.llm.getLocusUrl.returns('a url');
|
|
12545
|
-
webex.internal.llm.getDatachannelUrl.returns('a datachannel url');
|
|
12546
12716
|
|
|
12547
|
-
meeting.locusInfo = {
|
|
12717
|
+
meeting.locusInfo = {
|
|
12718
|
+
url: 'a url',
|
|
12719
|
+
info: {datachannelUrl: 'a different datachannel url'},
|
|
12720
|
+
self: {}
|
|
12721
|
+
};
|
|
12548
12722
|
|
|
12549
12723
|
const result = await meeting.updateLLMConnection();
|
|
12550
12724
|
|
|
12551
|
-
assert.
|
|
12552
|
-
|
|
12553
|
-
reason: 'done (permanent)'
|
|
12554
|
-
|
|
12555
|
-
|
|
12725
|
+
assert.calledWithExactly(
|
|
12726
|
+
webex.internal.llm.disconnectLLM,
|
|
12727
|
+
{code: 3050, reason: 'done (permanent)'}
|
|
12728
|
+
);
|
|
12729
|
+
|
|
12730
|
+
assert.calledWithExactly(
|
|
12556
12731
|
webex.internal.llm.registerAndConnect,
|
|
12557
12732
|
'a url',
|
|
12558
|
-
'a different datachannel url'
|
|
12733
|
+
'a different datachannel url',
|
|
12734
|
+
undefined
|
|
12559
12735
|
);
|
|
12736
|
+
|
|
12560
12737
|
assert.equal(result, 'something');
|
|
12738
|
+
|
|
12561
12739
|
assert.calledWithExactly(
|
|
12562
12740
|
meeting.webex.internal.llm.off,
|
|
12563
12741
|
'event:relay.event',
|
|
@@ -12568,6 +12746,7 @@ describe('plugin-meetings', () => {
|
|
|
12568
12746
|
'event:locus.state_message',
|
|
12569
12747
|
meeting.processLocusLLMEvent
|
|
12570
12748
|
);
|
|
12749
|
+
|
|
12571
12750
|
assert.calledWithExactly(
|
|
12572
12751
|
meeting.webex.internal.llm.on,
|
|
12573
12752
|
'event:relay.event',
|
|
@@ -12579,7 +12758,6 @@ describe('plugin-meetings', () => {
|
|
|
12579
12758
|
meeting.processLocusLLMEvent
|
|
12580
12759
|
);
|
|
12581
12760
|
});
|
|
12582
|
-
|
|
12583
12761
|
it('disconnects when the state is not JOINED', async () => {
|
|
12584
12762
|
meeting.joinedWith = {state: 'any other state'};
|
|
12585
12763
|
webex.internal.llm.isConnected.returns(true);
|
|
@@ -12589,35 +12767,140 @@ describe('plugin-meetings', () => {
|
|
|
12589
12767
|
|
|
12590
12768
|
const result = await meeting.updateLLMConnection();
|
|
12591
12769
|
|
|
12592
|
-
assert.calledWith(webex.internal.llm.disconnectLLM,
|
|
12770
|
+
assert.calledWith(webex.internal.llm.disconnectLLM, {
|
|
12771
|
+
code: 3050,
|
|
12772
|
+
reason: 'done (permanent)',
|
|
12773
|
+
});
|
|
12593
12774
|
assert.notCalled(webex.internal.llm.registerAndConnect);
|
|
12594
12775
|
assert.equal(result, undefined);
|
|
12776
|
+
});
|
|
12777
|
+
it('connects practice session data channel when PS started', async () => {
|
|
12778
|
+
meeting.joinedWith = {state: 'JOINED'};
|
|
12779
|
+
meeting.locusInfo = {
|
|
12780
|
+
url: 'a url',
|
|
12781
|
+
info: {
|
|
12782
|
+
datachannelUrl: 'a datachannel url',
|
|
12783
|
+
practiceSessionDatachannelUrl: 'ps-url',
|
|
12784
|
+
},
|
|
12785
|
+
};
|
|
12786
|
+
meeting.webinar.isJoinPracticeSessionDataChannel.returns(true);
|
|
12787
|
+
|
|
12788
|
+
await meeting.updateLLMConnection();
|
|
12789
|
+
|
|
12595
12790
|
assert.calledWithExactly(
|
|
12596
|
-
|
|
12597
|
-
'
|
|
12598
|
-
|
|
12791
|
+
webex.internal.llm.registerAndConnect,
|
|
12792
|
+
'a url',
|
|
12793
|
+
'ps-url',
|
|
12794
|
+
undefined
|
|
12795
|
+
);
|
|
12796
|
+
});
|
|
12797
|
+
it('passes dataChannelToken to registerAndConnect', async () => {
|
|
12798
|
+
meeting.joinedWith = {state: 'JOINED'};
|
|
12799
|
+
meeting.locusInfo = {
|
|
12800
|
+
url: 'a url',
|
|
12801
|
+
info: {datachannelUrl: 'a datachannel url'},
|
|
12802
|
+
self: {datachannelToken: 'token-123'},
|
|
12803
|
+
};
|
|
12804
|
+
|
|
12805
|
+
webex.internal.llm.getDatachannelToken.returns(undefined);
|
|
12806
|
+
|
|
12807
|
+
await meeting.updateLLMConnection();
|
|
12808
|
+
|
|
12809
|
+
assert.calledWithExactly(
|
|
12810
|
+
webex.internal.llm.registerAndConnect,
|
|
12811
|
+
'a url',
|
|
12812
|
+
'a datachannel url',
|
|
12813
|
+
'token-123'
|
|
12599
12814
|
);
|
|
12600
12815
|
assert.calledWithExactly(
|
|
12601
|
-
|
|
12602
|
-
'
|
|
12603
|
-
|
|
12816
|
+
webex.internal.llm.setDatachannelToken,
|
|
12817
|
+
'token-123',
|
|
12818
|
+
'default'
|
|
12604
12819
|
);
|
|
12605
12820
|
});
|
|
12821
|
+
it('prefers refreshed token over locus self token', async () => {
|
|
12822
|
+
meeting.joinedWith = {state: 'JOINED'};
|
|
12823
|
+
meeting.locusInfo = {
|
|
12824
|
+
url: 'a url',
|
|
12825
|
+
info: {datachannelUrl: 'a datachannel url'},
|
|
12826
|
+
self: {datachannelToken: 'locus-token'},
|
|
12827
|
+
};
|
|
12828
|
+
|
|
12829
|
+
webex.internal.llm.getDatachannelToken
|
|
12830
|
+
.withArgs('default')
|
|
12831
|
+
.returns('refreshed-token');
|
|
12832
|
+
|
|
12833
|
+
await meeting.updateLLMConnection();
|
|
12606
12834
|
|
|
12607
|
-
|
|
12835
|
+
assert.calledWithExactly(
|
|
12836
|
+
webex.internal.llm.registerAndConnect,
|
|
12837
|
+
'a url',
|
|
12838
|
+
'a datachannel url',
|
|
12839
|
+
'refreshed-token'
|
|
12840
|
+
);
|
|
12841
|
+
|
|
12842
|
+
assert.notCalled(webex.internal.llm.setDatachannelToken);
|
|
12843
|
+
});
|
|
12844
|
+
it('uses practice session token when in PS even if refreshed token exists', async () => {
|
|
12608
12845
|
meeting.joinedWith = {state: 'JOINED'};
|
|
12846
|
+
|
|
12609
12847
|
meeting.locusInfo = {
|
|
12610
12848
|
url: 'a url',
|
|
12611
12849
|
info: {
|
|
12612
12850
|
datachannelUrl: 'a datachannel url',
|
|
12613
|
-
practiceSessionDatachannelUrl: '
|
|
12851
|
+
practiceSessionDatachannelUrl: 'ps-url',
|
|
12852
|
+
},
|
|
12853
|
+
self: {
|
|
12854
|
+
datachannelToken: 'locus-token',
|
|
12855
|
+
practiceSessionDatachannelToken: 'ps-token',
|
|
12614
12856
|
},
|
|
12615
12857
|
};
|
|
12616
|
-
|
|
12858
|
+
|
|
12859
|
+
meeting.webinar.isJoinPracticeSessionDataChannel.returns(true);
|
|
12860
|
+
|
|
12861
|
+
webex.internal.llm.getDatachannelToken
|
|
12862
|
+
.withArgs(true).returns('refreshed-ps-token') // refreshed practice token
|
|
12863
|
+
.withArgs(false).returns('refreshed-normal-token'); // refreshed normal token
|
|
12864
|
+
|
|
12617
12865
|
await meeting.updateLLMConnection();
|
|
12618
12866
|
|
|
12619
|
-
assert.
|
|
12620
|
-
|
|
12867
|
+
assert.calledWithExactly(
|
|
12868
|
+
webex.internal.llm.registerAndConnect,
|
|
12869
|
+
'a url',
|
|
12870
|
+
'ps-url',
|
|
12871
|
+
'ps-token'
|
|
12872
|
+
);
|
|
12873
|
+
assert.calledWithExactly(
|
|
12874
|
+
webex.internal.llm.setDatachannelToken,
|
|
12875
|
+
'ps-token',
|
|
12876
|
+
'practiceSession'
|
|
12877
|
+
);
|
|
12878
|
+
});
|
|
12879
|
+
|
|
12880
|
+
it('does not pass token when data channel with jwt token is disabled', async () => {
|
|
12881
|
+
meeting.joinedWith = {state: 'JOINED'};
|
|
12882
|
+
meeting.locusInfo = {
|
|
12883
|
+
url: 'a url',
|
|
12884
|
+
info: {datachannelUrl: 'a datachannel url'},
|
|
12885
|
+
self: {datachannelToken: 'token-123'}
|
|
12886
|
+
};
|
|
12887
|
+
|
|
12888
|
+
webex.internal.llm.getDatachannelToken.returns(undefined);
|
|
12889
|
+
webex.internal.llm.isDataChannelTokenEnabled = sinon.stub().resolves(false);
|
|
12890
|
+
|
|
12891
|
+
await meeting.updateLLMConnection();
|
|
12892
|
+
|
|
12893
|
+
assert.calledWithExactly(
|
|
12894
|
+
webex.internal.llm.registerAndConnect,
|
|
12895
|
+
'a url',
|
|
12896
|
+
'a datachannel url',
|
|
12897
|
+
'token-123'
|
|
12898
|
+
);
|
|
12899
|
+
assert.calledWithExactly(
|
|
12900
|
+
webex.internal.llm.setDatachannelToken,
|
|
12901
|
+
'token-123',
|
|
12902
|
+
'default'
|
|
12903
|
+
);
|
|
12621
12904
|
});
|
|
12622
12905
|
});
|
|
12623
12906
|
|
|
@@ -12628,6 +12911,7 @@ describe('plugin-meetings', () => {
|
|
|
12628
12911
|
|
|
12629
12912
|
it('should read the locus object, set on the meeting and return null', () => {
|
|
12630
12913
|
const dataSets = {someFakeStuff: 'dataSet'};
|
|
12914
|
+
const metadata = {some: 'metadata'};
|
|
12631
12915
|
|
|
12632
12916
|
meeting.setLocus({
|
|
12633
12917
|
mediaConnections: [test1],
|
|
@@ -12637,12 +12921,14 @@ describe('plugin-meetings', () => {
|
|
|
12637
12921
|
mediaId: uuid3,
|
|
12638
12922
|
locus: {host: {id: uuid4}},
|
|
12639
12923
|
dataSets,
|
|
12924
|
+
metadata,
|
|
12640
12925
|
});
|
|
12641
12926
|
assert.calledOnce(meeting.locusInfo.initialSetup);
|
|
12642
12927
|
assert.calledWith(meeting.locusInfo.initialSetup, {
|
|
12643
12928
|
trigger: 'join-response',
|
|
12644
12929
|
locus: {host: {id: uuid4}},
|
|
12645
12930
|
dataSets,
|
|
12931
|
+
metadata,
|
|
12646
12932
|
});
|
|
12647
12933
|
assert.equal(meeting.mediaConnections, test1);
|
|
12648
12934
|
assert.equal(meeting.locusUrl, url1);
|
|
@@ -14184,6 +14470,72 @@ describe('plugin-meetings', () => {
|
|
|
14184
14470
|
assert.calledOnce(meeting.meetingRequest.keepAlive);
|
|
14185
14471
|
});
|
|
14186
14472
|
});
|
|
14473
|
+
describe('#refreshDataChannelToken()', () => {
|
|
14474
|
+
let meeting;
|
|
14475
|
+
|
|
14476
|
+
beforeEach(() => {
|
|
14477
|
+
meeting = Object.create(Meeting.prototype);
|
|
14478
|
+
meeting.locusUrl = 'https://locus.example.com';
|
|
14479
|
+
meeting.meetingRequest = {
|
|
14480
|
+
fetchDatachannelToken: sinon.stub().resolves({
|
|
14481
|
+
body: { datachannelToken: 'mock-token' },
|
|
14482
|
+
}),
|
|
14483
|
+
};
|
|
14484
|
+
meeting.members = {
|
|
14485
|
+
selfId: 'self-123',
|
|
14486
|
+
};
|
|
14487
|
+
meeting.webinar = {
|
|
14488
|
+
isJoinPracticeSessionDataChannel: sinon.stub().returns(true),
|
|
14489
|
+
};
|
|
14490
|
+
});
|
|
14491
|
+
|
|
14492
|
+
it('calls fetchDatachannelToken with correct parameters', async () => {
|
|
14493
|
+
await meeting.refreshDataChannelToken();
|
|
14494
|
+
|
|
14495
|
+
sinon.assert.calledOnce(meeting.meetingRequest.fetchDatachannelToken);
|
|
14496
|
+
|
|
14497
|
+
sinon.assert.calledWith(
|
|
14498
|
+
meeting.meetingRequest.fetchDatachannelToken,
|
|
14499
|
+
{
|
|
14500
|
+
locusUrl: 'https://locus.example.com',
|
|
14501
|
+
requestingParticipantId: 'self-123',
|
|
14502
|
+
isPracticeSession: true,
|
|
14503
|
+
}
|
|
14504
|
+
);
|
|
14505
|
+
});
|
|
14506
|
+
|
|
14507
|
+
it('returns the correct structured result', async () => {
|
|
14508
|
+
const result = await meeting.refreshDataChannelToken();
|
|
14509
|
+
|
|
14510
|
+
expect(result).to.deep.equal({
|
|
14511
|
+
body: {
|
|
14512
|
+
datachannelToken: 'mock-token',
|
|
14513
|
+
dataChannelTokenType: 'practiceSession',
|
|
14514
|
+
},
|
|
14515
|
+
});
|
|
14516
|
+
});
|
|
14517
|
+
});
|
|
14518
|
+
describe('#getDataChannelTokenType', () => {
|
|
14519
|
+
it('returns PracticeSession when webinar is in practice session mode', () => {
|
|
14520
|
+
meeting.webinar = {
|
|
14521
|
+
isJoinPracticeSessionDataChannel: sinon.stub().returns(true),
|
|
14522
|
+
};
|
|
14523
|
+
|
|
14524
|
+
const result = meeting.getDataChannelTokenType();
|
|
14525
|
+
|
|
14526
|
+
expect(result).to.equal('practiceSession');
|
|
14527
|
+
});
|
|
14528
|
+
|
|
14529
|
+
it('returns Default when not in practice session mode', () => {
|
|
14530
|
+
meeting.webinar = {
|
|
14531
|
+
isJoinPracticeSessionDataChannel: sinon.stub().returns(false),
|
|
14532
|
+
};
|
|
14533
|
+
|
|
14534
|
+
const result = meeting.getDataChannelTokenType();
|
|
14535
|
+
|
|
14536
|
+
expect(result).to.equal('default');
|
|
14537
|
+
});
|
|
14538
|
+
});
|
|
14187
14539
|
describe('#stopKeepAlive', () => {
|
|
14188
14540
|
let clock;
|
|
14189
14541
|
const defaultKeepAliveUrl = 'keep.alive.url';
|